import time as python_time import json import sys class PrimitiveFinished(Exception): """Exception to indicate the result value of a primitive, as a return cannot be used.""" def __init__(self, value): Exception.__init__(self) self.result = value 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 # the kernel's: returns are handled by throwing a PrimitiveFinished exception; the caller's # 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']])] raise PrimitiveFinished({'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']])] raise PrimitiveFinished({'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']])] raise PrimitiveFinished({'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']])] raise PrimitiveFinished({'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']])] raise PrimitiveFinished({'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']])] raise PrimitiveFinished({'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']])] raise PrimitiveFinished({'value': a['value'] or b['value']}) def bool_not(a, **remainder): if 'value' not in a: a['value'], = yield [("RV", [a['id']])] raise PrimitiveFinished({'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']])] raise PrimitiveFinished({'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']])] raise PrimitiveFinished({'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']])] raise PrimitiveFinished({'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']])] raise PrimitiveFinished({'value': float(a['value']) / float(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']])] raise PrimitiveFinished({'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']])] raise PrimitiveFinished({'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:])] raise PrimitiveFinished({'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']])] raise PrimitiveFinished({'value': a['value'][b['value']]}) def string_len(a, **remainder): if 'value' not in a: a['value'], = yield [("RV", [a['id']])] raise PrimitiveFinished({'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']])] raise PrimitiveFinished({'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']])] raise PrimitiveFinished({'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): raise PrimitiveFinished({'value': str(a['value']['value'])}) else: raise PrimitiveFinished({'value': str(a['value'])}) def cast_float(a, **remainder): if 'value' not in a: a['value'], = yield [("RV", [a['id']])] raise PrimitiveFinished({'value': float(a['value'])}) def cast_boolean(a, **remainder): if 'value' not in a: a['value'], = yield [("RV", [a['id']])] raise PrimitiveFinished({'value': bool(a['value'])}) def cast_integer(a, **remainder): if 'value' not in a: a['value'], = yield [("RV", [a['id']])] raise PrimitiveFinished({'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): raise PrimitiveFinished({'value': str(a['value']['value'])}) else: raise PrimitiveFinished({'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']])] raise PrimitiveFinished({'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']])] raise PrimitiveFinished(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])] raise PrimitiveFinished(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])] raise PrimitiveFinished(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']])] raise PrimitiveFinished({'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']])] raise PrimitiveFinished({'id': result}) def dict_read_node(a, b, **remainder): result, = yield [("RDN", [a['id'], b['id']])] raise PrimitiveFinished({'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']])] raise PrimitiveFinished({'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 raise PrimitiveFinished({'value': False}) value, = yield [("RDN", [a['id'], b['id']])] raise PrimitiveFinished({'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)] raise PrimitiveFinished({'id': result}) def is_physical_int(a, **remainder): if "value" not in a: a['value'], = yield [("RV", [a['id']])] try: raise PrimitiveFinished({'value': isinstance(a['value'], int) or isinstance(a['value'], long)}) except NameError: raise PrimitiveFinished({'value': isinstance(a['value'], int)}) def is_physical_string(a, **remainder): if "value" not in a: a['value'], = yield [("RV", [a['id']])] try: raise PrimitiveFinished({'value': isinstance(a['value'], str) or isinstance(a['value'], unicode)}) except NameError: raise PrimitiveFinished({'value': isinstance(a['value'], str)}) def is_physical_float(a, **remainder): if "value" not in a: a['value'], = yield [("RV", [a['id']])] raise PrimitiveFinished({'value': isinstance(a['value'], float)}) def is_physical_boolean(a, **remainder): if "value" not in a: a['value'], = yield [("RV", [a['id']])] raise PrimitiveFinished({'value': isinstance(a['value'], bool)}) def is_physical_action(a, **remainder): if "value" not in a: a['value'], = yield [("RV", [a['id']])] raise PrimitiveFinished({'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']])] raise PrimitiveFinished({'value': isinstance(a['value'], dict) and a['value']["value"] == "none"}) def create_node(**remainder): result, = yield [("CN", [])] raise PrimitiveFinished({'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']])] raise PrimitiveFinished({'id': result}) def create_value(a, **remainder): if "value" not in a: a['value'], = yield [("RV", [a['id']])] raise PrimitiveFinished({'value': a['value']}) def read_nr_out(a, **remainder): if "id" not in a: a['id'], = yield [("CNV", [a['value']])] outgoing, = yield [("RO", [a['id']])] raise PrimitiveFinished({'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']])] raise PrimitiveFinished({'id': sorted(outgoing)[b['value']] if len(outgoing) > b['value'] else root}) def read_nr_in(a, **remainder): if "id" not in a: a['id'], = yield [("CNV", [a['value']])] incoming, = yield [("RI", [a['id']])] raise PrimitiveFinished({'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']])] raise PrimitiveFinished({'id': sorted(incoming)[b['value']] if len(incoming) > b['value'] else root}) def read_edge_src(a, **remainder): result, = yield [("RE", [a['id']])] raise PrimitiveFinished({'id': result[0]}) def read_edge_dst(a, **remainder): result, = yield [("RE", [a['id']])] raise PrimitiveFinished({'id': result[1]}) def delete_element(a, **remainder): if "id" not in a: raise PrimitiveFinished({'value': False}) edge, = yield [("RE", [a['id']])] if edge[0] is None: # Not an edge: yield [("DN", [a['id']])] raise PrimitiveFinished({'value': False}) else: yield [("DE", [a['id']])] raise PrimitiveFinished({'value': True}) def read_root(root, **remainder): raise PrimitiveFinished({'id': root}) def is_edge(a, **remainder): if "id" not in a: raise PrimitiveFinished({'value': False}) edge, = yield [("RE", [a['id']])] raise PrimitiveFinished({'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'])) raise PrimitiveFinished(a) def read_taskroot(task_root, **remainder): raise PrimitiveFinished({'id': task_root}) def time(**remainder): raise PrimitiveFinished({'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() raise PrimitiveFinished({'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])] raise PrimitiveFinished(a) def is_error(a, **remainder): if a['id'] is None: raise PrimitiveFinished({'value': True}) else: raise PrimitiveFinished({'value': False})