import time as python_time import json import sys class InterpretedFunctionFinished(Exception): """Exception to indicate the result value of an interpreted function, as a return cannot be used.""" def __init__(self, value): Exception.__init__(self) self.result = value class SleepKernel(Exception): """Exception to indicate the kernel to sleep for some time.""" def __init__(self, timeout, interruptable): Exception.__init__(self) self.timeout = timeout self.interruptable = interruptable # Functions annotated with __exception_return use the JIT's calling convention instead of # returnvalue is not modified. # # ### Rationale for __exception_return # # __exception_return is a useful mechanism because it allows us to have a __call_function # implementation that has O(1) state read overhead. A previous implementation of # __call_function checked if the caller's frame had been popped whenever # ModelverseKernel.execute_yield threw a StopIteration exception. However, that incurs O(n) overhead # _per call,_ where n is the number of StopIteration exceptions that are thrown during the call. # O(n) is pretty bad, but this actually becomes O(n * m) when m calls to __call_function are # nested. And that's just not acceptable. # __exception_return requires kernel support, but I think the complexity gains are well worth it; # I reckon JIT-to-interpreter switches aren't going to get a whole lot cheaper than this. EXCEPTION_RETURN_KEY = "__exception_return" """A dictionary key for functions which request that the kernel throw a InterpretedFunctionFinished exception with the return value instead of injecting the return value in the caller's frame.""" def integer_subtraction(a, b, **remainder): if 'value' not in a: a['value'], = yield [("RV", [a['id']])] if 'value' not in b: b['value'], = yield [("RV", [b['id']])] yield [("RETURN", [{'value': a['value'] - b['value']}])] def integer_addition(a, b, **remainder): if 'value' not in a: a['value'], = yield [("RV", [a['id']])] if 'value' not in b: b['value'], = yield [("RV", [b['id']])] yield [("RETURN", [{'value': a['value'] + b['value']}])] def integer_multiplication(a, b, **remainder): if 'value' not in a: a['value'], = yield [("RV", [a['id']])] if 'value' not in b: b['value'], = yield [("RV", [b['id']])] yield [("RETURN", [{'value': a['value'] * b['value']}])] def integer_division(a, b, **remainder): if 'value' not in a: a['value'], = yield [("RV", [a['id']])] if 'value' not in b: b['value'], = yield [("RV", [b['id']])] yield [("RETURN", [{'value': int(a['value']) // b['value']}])] def integer_lt(a, b, **remainder): if 'value' not in a: a['value'], = yield [("RV", [a['id']])] if 'value' not in b: b['value'], = yield [("RV", [b['id']])] yield [("RETURN", [{'value': a['value'] < b['value']}])] def bool_and(a, b, **remainder): if 'value' not in a: a['value'], = yield [("RV", [a['id']])] if 'value' not in b: b['value'], = yield [("RV", [b['id']])] yield [("RETURN", [{'value': a['value'] and b['value']}])] def bool_or(a, b, **remainder): if 'value' not in a: a['value'], = yield [("RV", [a['id']])] if 'value' not in b: b['value'], = yield [("RV", [b['id']])] yield [("RETURN", [{'value': a['value'] or b['value']}])] def bool_not(a, **remainder): if 'value' not in a: a['value'], = yield [("RV", [a['id']])] yield [("RETURN", [{'value': not a['value']}])] def float_subtraction(a, b, **remainder): if 'value' not in a: a['value'], = yield [("RV", [a['id']])] if 'value' not in b: b['value'], = yield [("RV", [b['id']])] yield [("RETURN", [{'value': a['value'] - b['value']}])] def float_addition(a, b, **remainder): if 'value' not in a: a['value'], = yield [("RV", [a['id']])] if 'value' not in b: b['value'], = yield [("RV", [b['id']])] yield [("RETURN", [{'value': a['value'] + b['value']}])] def float_multiplication(a, b, **remainder): if 'value' not in a: a['value'], = yield [("RV", [a['id']])] if 'value' not in b: b['value'], = yield [("RV", [b['id']])] yield [("RETURN", [{'value': a['value'] * b['value']}])] def float_division(a, b, **remainder): if 'value' not in a: a['value'], = yield [("RV", [a['id']])] if 'value' not in b: b['value'], = yield [("RV", [b['id']])] yield [("RETURN", [{'value': float(a['value']) / b['value']}])] def float_lt(a, b, **remainder): if 'value' not in a: a['value'], = yield [("RV", [a['id']])] if 'value' not in b: b['value'], = yield [("RV", [b['id']])] yield [("RETURN", [{'value': a['value'] < b['value']}])] def string_join(a, b, **remainder): if 'value' not in a: a['value'], = yield [("RV", [a['id']])] if 'value' not in b: b['value'], = yield [("RV", [b['id']])] yield [("RETURN", [{'value': str(a['value']) + str(b['value'])}])] def string_split(a, b, **remainder): # TODO make non-primitive, though compiled if 'value' not in a: a['value'], = yield [("RV", [a['id']])] if 'value' not in b: b['value'], = yield [("RV", [b['id']])] result = a['value'].split(b['value']) elems = yield [("CN", [])] + [("CNV", [v]) for v in result] new_val = elems[0] yield [("CD", [new_val, i, v]) for i, v in enumerate(elems[1:])] yield [("RETURN", [{'id': new_val}])] def string_get(a, b, **remainder): if 'value' not in a: a['value'], = yield [("RV", [a['id']])] if 'value' not in b: b['value'], = yield [("RV", [b['id']])] yield [("RETURN", [{'value': a['value'][b['value']]}])] def string_len(a, **remainder): if 'value' not in a: a['value'], = yield [("RV", [a['id']])] yield [("RETURN", [{'value': len(a['value'])}])] def value_eq(a, b, **remainder): if 'value' not in a: a['value'], = yield [("RV", [a['id']])] if 'value' not in b: b['value'], = yield [("RV", [b['id']])] yield [("RETURN", [{'value': a['value'] == b['value']}])] def element_eq(a, b, **remainder): if "id" not in a: #print("MATERIALIZING A element_eq") a['id'], = yield [("CNV", [a['value']])] if "id" not in b: #print("MATERIALIZING B element_eq") b['id'], = yield [("CNV", [b['value']])] yield [("RETURN", [{'value': a['id'] == b['id']}])] def cast_string(a, **remainder): if 'value' not in a: a['value'], = yield [("RV", [a['id']])] if isinstance(a['value'], dict): yield [("RETURN", [{'value': str(a['value']['value'])}])] else: yield [("RETURN", [{'value': str(a['value'])}])] def cast_float(a, **remainder): if 'value' not in a: a['value'], = yield [("RV", [a['id']])] yield [("RETURN", [{'value': float(a['value'])}])] def cast_boolean(a, **remainder): if 'value' not in a: a['value'], = yield [("RV", [a['id']])] yield [("RETURN", [{'value': bool(a['value'])}])] def cast_integer(a, **remainder): if 'value' not in a: a['value'], = yield [("RV", [a['id']])] yield [("RETURN", [{'value': int(a['value'])}])] def cast_value(a, **remainder): if 'value' not in a: a['value'], = yield [("RV", [a['id']])] if isinstance(a['value'], dict): yield [("RETURN", [{'value': str(a['value']['value'])}])] else: yield [("RETURN", [{'value': json.dumps(a['value'])}])] def cast_id(a, **remainder): if "id" not in a: #print("MATERIALIZING A cast_id") a['id'], = yield [("CNV", [a['value']])] yield [("RETURN", [{'value': str(a['id'])}])] def dict_add_fast(a, b, c, **remainder): # TODO deprecate, as dict_add is now also efficient if "value" not in b: b['value'], = yield [("RV", [b['id']])] if "id" not in c: #print("MATERIALIZING C dict_add_fast") c['id'], = yield [("CNV", [c['value']])] yield [("CD", [a['id'], b['value'], c['id']])] yield [("RETURN", [a])] def dict_delete(a, b, **remainder): if "value" not in b: b['value'], = yield [("RV", [b['id']])] edge, = yield [("RDE", [a['id'], b['value']])] if edge is None: print("Failed dict_delete for value '%s'!" % b['value']) keys, = yield [("RDK", [a['id']])] keys = yield [("RV", [i]) for i in keys] print("Keys: " + str(keys)) raise Exception() yield [("DE", [edge])] yield [("RETURN", [a])] def dict_delete_node(a, b, **remainder): edge, = yield [("RDNE", [a['id'], b['id']])] if edge is None: print("Failed dict_delete_node!") yield [("DE", [edge])] yield [("RETURN", [a])] def dict_read(a, b, **remainder): if "value" not in b: b['value'], = yield [("RV", [b['id']])] result, = yield [("RD", [a['id'], b['value']])] yield [("RETURN", [{'id': result}])] def dict_read_edge(a, b, **remainder): if "value" not in b: b['value'], = yield [("RV", [b['id']])] result, = yield [("RDE", [a['id'], b['value']])] yield [("RETURN", [{'id': result}])] def dict_read_node(a, b, **remainder): result, = yield [("RDN", [a['id'], b['id']])] yield [("RETURN", [{'id': result}])] def dict_in(a, b, **remainder): if "value" not in b: b['value'], = yield [("RV", [b['id']])] value, = yield [("RD", [a['id'], b['value']])] yield [("RETURN", [{'value': value is not None}])] def dict_in_node(a, b, **remainder): if "id" not in b: # Not even allocated the node, so it is certain not to be in the dictionary yield [("RETURN", [{'value': False}])] value, = yield [("RDN", [a['id'], b['id']])] yield [("RETURN", [{'value': value is not None}])] def dict_keys(a, **remainder): keys, result = yield [("RDK", [a['id']]), ("CN", [])] edges = yield [("CE", [result, result]) for _ in keys] _ = yield [("CE", [edge, key]) for edge, key in zip(edges, keys)] yield [("RETURN", [{'id': result}])] def is_physical_int(a, **remainder): if "value" not in a: a['value'], = yield [("RV", [a['id']])] try: yield [("RETURN", [{'value': isinstance(a['value'], int) or isinstance(a['value'], long)}])] except NameError: yield [("RETURN", [{'value': isinstance(a['value'], int)}])] def is_physical_string(a, **remainder): if "value" not in a: a['value'], = yield [("RV", [a['id']])] try: yield [("RETURN", [{'value': isinstance(a['value'], str) or isinstance(a['value'], unicode)}])] except NameError: yield [("RETURN", [{'value': isinstance(a['value'], str)}])] def is_physical_float(a, **remainder): if "value" not in a: a['value'], = yield [("RV", [a['id']])] yield [("RETURN", [{'value': isinstance(a['value'], float)}])] def is_physical_boolean(a, **remainder): if "value" not in a: a['value'], = yield [("RV", [a['id']])] yield [("RETURN", [{'value': isinstance(a['value'], bool)}])] def is_physical_action(a, **remainder): if "value" not in a: a['value'], = yield [("RV", [a['id']])] yield [("RETURN", [{'value': isinstance(a['value'], dict) and a['value']["value"] in ["if", "while", "assign", "call", "break", "continue", "return", "resolve", "access", "constant", "global", "declare"]}])] def is_physical_none(a, **remainder): if "value" not in a: a['value'], = yield [("RV", [a['id']])] yield [("RETURN", [{'value': isinstance(a['value'], dict) and a['value']["value"] == "none"}])] def create_node(**remainder): result, = yield [("CN", [])] yield [("RETURN", [{'id': result}])] def create_edge(a, b, **remainder): if "id" not in a: #print("MATERIALIZING A create_edge") a['id'], = yield [("CNV", [a['value']])] if "id" not in b: #print("MATERIALIZING B create_edge") b['id'], = yield [("CNV", [b['value']])] result, = yield [("CE", [a['id'], b['id']])] yield [("RETURN", [{'id': result}])] def create_value(a, **remainder): if "value" not in a: a['value'], = yield [("RV", [a['id']])] yield [("RETURN", [{'value': a['value']}])] def read_nr_out(a, **remainder): if "id" not in a: yield [("RETURN", [{'value': 0}])] else: outgoing, = yield [("RO", [a['id']])] yield [("RETURN", [{'value': len(outgoing)}])] def read_out(a, b, root, **remainder): if "id" not in a: a['id'], = yield [("CNV", [a['value']])] if "value" not in b: b['value'], = yield [("RV", [b['id']])] outgoing, = yield [("RO", [a['id']])] yield [("RETURN", [{'id': sorted(outgoing)[b['value']] if len(outgoing) > b['value'] else root}])] def read_nr_in(a, **remainder): if "id" not in a: yield [("RETURN", [{'value': 0}])] else: incoming, = yield [("RI", [a['id']])] yield [("RETURN", [{'value': len(incoming)}])] def read_in(a, b, root, **remainder): if "id" not in a: a['id'], = yield [("CNV", [a['value']])] if "value" not in b: b['value'], = yield [("RV", [b['id']])] incoming, = yield [("RI", [a['id']])] yield [("RETURN", [{'id': sorted(incoming)[b['value']] if len(incoming) > b['value'] else root}])] def read_edge_src(a, **remainder): result, = yield [("RE", [a['id']])] yield [("RETURN", [{'id': result[0]}])] def read_edge_dst(a, **remainder): result, = yield [("RE", [a['id']])] yield [("RETURN", [{'id': result[1]}])] def delete_element(a, **remainder): if "id" not in a: yield [("RETURN", [{'value': False}])] edge, = yield [("RE", [a['id']])] if edge[0] is None: # Not an edge: yield [("DN", [a['id']])] yield [("RETURN", [{'value': False}])] else: yield [("DE", [a['id']])] yield [("RETURN", [{'value': True}])] def read_root(root, **remainder): yield [("RETURN", [{'id': root}])] def is_edge(a, **remainder): if "id" not in a: yield [("RETURN", [{'value': False}])] edge, = yield [("RE", [a['id']])] yield [("RETURN", [{'value': edge[0] is not None}])] def log(a, **remainder): if "value" not in a: a['value'], = yield [("RV", [a['id']])] print("== LOG == " + str(a['value'])) yield [("RETURN", [a])] def read_taskroot(task_root, **remainder): yield [("RETURN", [{'id': task_root}])] def time(**remainder): yield [("RETURN", [{'value': python_time.time()}])] def hash(a, **remainder): if "value" not in a: a['value'], = yield [("RV", [a['id']])] import hashlib try: value = hashlib.sha512(a['value']).hexdigest() except TypeError: value = hashlib.sha512(a['value'].encode()).hexdigest() yield [("RETURN", [{'value': value}])] def __sleep(a, b, **remainder): if "value" not in a: a['value'], = yield [("RV", [a['id']])] if "value" not in b: b['value'], = yield [("RV", [b['id']])] timeout = a['value'] interruptable = b['value'] yield [("SLEEP", [timeout, interruptable])] yield [("RETURN", [a])] def is_error(a, **remainder): if a['id'] is None: yield [("RETURN", [{'value': True}])] else: yield [("RETURN", [{'value': False}])] def list_sort(a, **remainder): elements, = yield [("RO", [a['id']])] values = yield [("RD", [a['id'], i]) for i in range(len(elements))] values = yield [("RV", [i]) for i in values] new_list, = yield [("CN", [])] values = yield [("CNV", [v]) for v in sorted(values)] yield [("CD", [new_list, i, v]) for i, v in enumerate(values)] yield [("RETURN", [{'id': new_list}])]