def resolve_to_signature( self, node: parser.Node, scope: "emitter.Scopes", generic_type_context: Optional[GenericTypeContext] ) -> "emitter.MethodSignature": if util.get_flattened( node ) is not None and self.program.get_method_signature_optional( util.nonnull(util.get_flattened(node))) is not None: return self.program.get_method_signature( util.nonnull(util.get_flattened(node))) if node.i("ident"): node.compile_error( "Attempt to call an identifier that doesn't resolve to a method" ) # Unreachable return None # type: ignore elif node.i("."): typ = self.decide_type(node[0], scope, generic_type_context) if not node[1].i("ident"): raise ValueError("This is a compiler bug") if not isinstance(typ, AbstractCallableType): node.compile_error( "Attempt to call a member of something that isn't callable" ) return typ.method_signature_strict(node[1].data_strict, node) else: node.compile_error( "Attempt to call something that can't be called") # Unreachable return None # type: ignore
def resolve(self, node: parser.Node, generic_type_context: Optional[GenericTypeContext], fail_silent: bool = False, allow_raw: bool = False) -> Optional[AbstractType]: for typ in self.types: if typ.resolves(node, self.program): if not allow_raw and isinstance( typ, ClazzType) and typ.signature.is_raw_type: node.compile_error("Cannot use raw types directly") return typ if node.i("["): element_type = self.resolve(node[0], generic_type_context, fail_silent=fail_silent) if element_type is None: if not fail_silent: # TypeSystem.resolve will never return None if fail_silent == True raise ValueError("This is a compiler bug") return None return self.get_array_type(element_type) elif node.i("<>"): # We've got to create a new specialization of the class signature # because evidently the required one doesn't exist. generic_class_type = self.resolve(node[0], generic_type_context, fail_silent=fail_silent, allow_raw=True) if generic_class_type is None: # We know fail_silent == True return None if not isinstance(generic_class_type, ClazzType): # Yes, I really mean isinstance, not .is_class() node.compile_error( "Cannot provide generic type arguments to a type that isn't a class" ) type_arguments: List[AbstractType] = [] for type_argument_node in node.children[1:]: argument = self.resolve(type_argument_node, generic_type_context, fail_silent=fail_silent) if argument is None: # We know fail_silent == True return None type_arguments.append(argument) return generic_class_type.specialize(type_arguments, node) if generic_type_context is not None: ret = generic_type_context.resolve_optional(node, self.program) if ret is not None: return ret if not fail_silent: raise TypingError( node, "Could not resolve type: '%s' ('%s')" % (node, util.get_flattened(node))) else: return None
def resolves(self, node: parser.Node, program: "emitter.Program") -> bool: def flatten_qualified(nod: parser.Node) -> str: if nod.i("ident"): return nod.data_strict else: return "%s.%s" % (flatten_qualified( nod[0]), flatten_qualified(nod[1])) if not node.i("ident") and not node.i("."): return False name = flatten_qualified(node) for search_path in program.search_paths: if "%s%s" % (search_path, name) == self.name: return True return False
def resolves(self, node: parser.Node, program: "emitter.Program") -> bool: if node.i("ident") or node.i("."): return any([ self.name == path + util.nonnull(util.get_flattened(node)) for path in program.search_paths ]) if self.signature.generic_type_context is None: return False if not node.i("<>"): return False if not any([ self.name == path + util.nonnull(util.get_flattened(node[0])) for path in program.search_paths ]): return False for i in range(len(node) - 1): if not self.signature.generic_type_context.arguments[i].resolves( node[i + 1], program): return False return True
def eval_expr(self, node: parser.Node) -> InterpreterValueAny: # TODO: Support referencing other static variables # Bitmask for 64-bit computation bitmask = 0xffffffffffffffff if node.i("number"): return self.create_int_value(int(node.data_strict)) elif node.i("true"): return self.true elif node.i("false"): return self.false elif node.i("null"): return self.null elif node.of("+", "*", "^", "&", "|"): lhs = self.eval_expr(node[0]) rhs = self.eval_expr(node[1]) if not isinstance(lhs, InterpreterValueInteger) or not isinstance( rhs, InterpreterValueInteger): raise typesys.TypingError( node, "Attempt to perform arithmetic on something that isn't an integers" ) if node.i("+"): ret = lhs.value + rhs.value elif node.i("*"): ret = lhs.value * rhs.value elif node.i("^"): ret = lhs.value ^ rhs.value elif node.i("&"): ret = lhs.value & rhs.value elif node.i("|"): ret = lhs.value | rhs.value return self.create_int_value(ret & bitmask) elif node.of("and", "or"): lhs = self.eval_expr(node[0]) rhs = self.eval_expr(node[1]) if not isinstance(lhs, InterpreterValueBoolean) or not isinstance( rhs, InterpreterValueBoolean): raise typesys.TypingError( node, "Attempt to perform logical operation on something that isn't an integers" ) if node.i("and"): ret = lhs.value and rhs.value elif node.i("or"): ret = lhs.value or rhs.value return self.true if ret else self.false elif node.i("not"): val = self.eval_expr(node[0]) if not isinstance(val, InterpreterValueBoolean): raise typesys.TypingError( node, "Attempt to perform logical operation on something that isn't an integers" ) return self.false if val.value else self.true elif node.of(">=", "<=", "<", ">"): lhs = self.eval_expr(node[0]) rhs = self.eval_expr(node[1]) if not isinstance(lhs, InterpreterValueInteger) or not isinstance( rhs, InterpreterValueInteger): raise typesys.TypingError( node, "Attempt to perform arithmetic on something that isn't an integers" ) lhs_value = self.normalize_negative(lhs.value) rhs_value = self.normalize_negative(rhs.value) if node.i(">="): ret = lhs_value >= rhs_value elif node.i("<="): ret = lhs_value <= rhs_value elif node.i("<"): ret = lhs_value < rhs_value elif node.i(">"): ret = lhs_value > rhs_value else: raise ValueError("This is a compiler bug") return self.true if ret else self.false elif node.of("==", "!="): lhs = self.eval_expr(node[0]) rhs = self.eval_expr(node[1]) if not lhs.type.is_assignable_to( rhs.type) and not rhs.type.is_assignable_to(lhs.type): raise typesys.TypingError( node, "Incomparable types: '%s' and '%s'" % (lhs.type, rhs.type)) return self.true if lhs.equals(rhs) ^ ( True if node.i("!=") else False) else self.false elif node.i("-") and len(node) == 2: lhs = self.eval_expr(node[0]) rhs = self.eval_expr(node[1]) if not isinstance(lhs, InterpreterValueInteger) or not isinstance( rhs, InterpreterValueInteger): raise typesys.TypingError( node, "Attempt to perform arithmetic on something that isn't an integers" ) return self.create_int_value((lhs.value - rhs.value) & bitmask) elif (node.i("-") and len(node) == 1) or node.i("~"): val = self.eval_expr(node[0]) if not isinstance(val, InterpreterValueInteger): raise typesys.TypingError( node, "Attempt to negate something that isn't an integer") return self.create_int_value( (-val.value if node.i("-") else ~val.value) & bitmask) elif node.i("/"): lhs = self.eval_expr(node[0]) rhs = self.eval_expr(node[1]) if not isinstance(lhs, InterpreterValueInteger) or not isinstance( rhs, InterpreterValueInteger): raise typesys.TypingError( node, "Attempt to perform arithmetic on something that isn't an integers" ) lhs_value = lhs.value rhs_value = rhs.value # Make sure, if our value is negative, we're dividing by a # negative rather than a very large value (due to two's # complement) lhs_value = self.normalize_negative(lhs_value) rhs_value = self.normalize_negative(rhs_value) res = lhs_value // rhs_value if res * rhs.value != lhs.value: # Python rounds toward negative infinity, whereas C # (and thus our language) rounds toward 0. if res < 0: res += 1 return self.create_int_value(res & bitmask) elif node.i("["): typ = self.program.types.decide_type(node, emitter.Scopes(), None) if not isinstance(typ, typesys.ArrayType): raise ValueError("This is a compiler bug.") # All of our typechecking has already been taken care of in # the logic in typesys there result_values: List[AbstractInterpreterValue] = [] for child in node: result_values.append(self.eval_expr(child)) return self.create_array_value(typ, result_values) elif node.i("arrinst"): typ = self.program.types.decide_type(node, emitter.Scopes(), None) if not isinstance(typ, typesys.ArrayType): raise ValueError("This is a compiler bug.") # Same thing, all of our typechecking is delegated to typesys array_len = self.eval_expr(node[1]) if not isinstance(array_len, InterpreterValueInteger): raise typesys.TypingError( node[1], "Type of array length must be an integer") if array_len.value > 1024: node.warn( "Statically creating a very large array, this will make your bytecode file very large: %d" % array_len.value) arrinst_result_values: List[AbstractInterpreterValue] if isinstance(typ.parent_type, typesys.IntType): arrinst_result_values = [self.create_int_value(0) ] * array_len.value elif isinstance(typ.parent_type, typesys.BoolType): arrinst_result_values = [self.false] * array_len.value else: arrinst_result_values = [self.null] * array_len.value return self.create_array_value(typ, arrinst_result_values) elif node.i("#"): val = self.eval_expr(node[0]) if not isinstance(val, InterpreterValueArray): raise typesys.TypingError( node[0], "Cannot find length of something that isn't an array") return self.create_int_value(len(val.value)) elif node.i("access"): lhs = self.eval_expr(node[0]) rhs = self.eval_expr(node[1]) if not isinstance(lhs, InterpreterValueArray): raise typesys.TypingError( node[0], "Can't access an element of something that isn't an array") if not isinstance(rhs, InterpreterValueInteger): raise typesys.TypingError(node[1], "Array indices must be integers") return lhs.value[rhs.value] else: node.compile_error("Cannot evaluate expression at compile-time")
def decide_type( self, expr: parser.Node, scope: "emitter.Scopes", generic_type_context: Optional[GenericTypeContext], suppress_coercing_void_warning: bool = False) -> AbstractType: if expr.i("as"): return self.resolve_strict(expr[1], generic_type_context) elif expr.i("instanceof"): return self.bool_type elif expr.of("+", "*", "-", "^", "&", "|", "%", "/") and len(expr) == 2: lhs_type = self.decide_type(expr[0], scope, generic_type_context) rhs_type = self.decide_type(expr[1], scope, generic_type_context) if not lhs_type.is_numerical(): raise TypingError(expr[0], "Type %s is not numerical" % lhs_type) if not rhs_type.is_numerical(): raise TypingError(expr[1], "Type %s is not numerical" % rhs_type) if not lhs_type.is_assignable_from( rhs_type) or not lhs_type.is_assignable_to(rhs_type): raise TypingError( expr, "Types %s and %s are incompatible for arithmetic operation" % (lhs_type, rhs_type)) return lhs_type elif expr.of(">=", "<=", ">", "<"): lhs_type = self.decide_type(expr[0], scope, generic_type_context) rhs_type = self.decide_type(expr[1], scope, generic_type_context) if not lhs_type.is_numerical(): raise TypingError(expr[0], "Type %s is not numerical" % lhs_type) if not rhs_type.is_numerical(): raise TypingError(expr[1], "Type %s is not numerical" % rhs_type) if not lhs_type.is_assignable_from( rhs_type) or not lhs_type.is_assignable_to(rhs_type): raise TypingError( expr, "Types %s and %s are incompatible for arithmetic comparison" % (lhs_type, rhs_type)) return self.bool_type elif expr.of("==", "!="): lhs_type = self.decide_type(expr[0], scope, generic_type_context) rhs_type = self.decide_type(expr[1], scope, generic_type_context) if not lhs_type.is_assignable_to( rhs_type) and not rhs_type.is_assignable_to(lhs_type): raise TypingError( expr, "Incomparable types: '%s' and '%s'" % (lhs_type, rhs_type)) return self.bool_type elif expr.i("number"): return self.int_type elif expr.of("and", "or"): lhs_type = self.decide_type(expr[0], scope, generic_type_context) rhs_type = self.decide_type(expr[1], scope, generic_type_context) if not lhs_type.is_boolean(): raise TypingError(expr[0], "Type %s is not boolean" % lhs_type) if not rhs_type.is_boolean(): raise TypingError(expr[0], "Type %s is not boolean" % rhs_type) return self.bool_type elif expr.i("not"): type = self.decide_type(expr[0], scope, generic_type_context) if not type.is_boolean(): raise TypingError(expr[0], "Type %s is not boolean" % type) return self.bool_type elif expr.of("~", "-") and len(expr) == 1: type = self.decide_type(expr[0], scope, generic_type_context) if not type.is_numerical(): raise TypingError(expr[0], "Type %s is not numerical" % type) return self.int_type elif expr.of("ident", ".") \ and util.get_flattened(expr) is not None \ and self.program.static_variables.has_variable(util.nonnull(util.get_flattened(expr))): return self.program.static_variables.resolve_variable( util.nonnull(util.get_flattened(expr))).type elif expr.i("ident"): return scope.resolve(expr.data_strict, expr).type elif expr.of("true", "false"): return self.bool_type elif expr.i("null"): return self.void_type elif expr.i("["): # For now, everything must be the same type, except for nulls # interspersed. Later this will change. current_type = None if len(expr) == 0: expr.compile_error( "Zero-length arrays must be instantiated with the arbitrary-length instantiation syntax" ) for child in expr: current_child_type: AbstractType = self.decide_type( child, scope, generic_type_context) if current_type is None: if not current_child_type.is_void(): current_type = current_child_type else: continue # This is to make the typechecker happy # We know this is safe because of the previous if-statement current_type_not_none: AbstractType = current_type while current_type_not_none.get_supertype( ) is not None and not current_child_type.is_assignable_to( current_type_not_none): current_type = current_type_not_none.get_supertype() if not current_child_type.is_assignable_to( current_type_not_none): raise TypingError( child, "Could not reconcile type %s" % (current_child_type)) if current_type is None: expr.compile_error( "Cannot have an array literal comprising only null values, use arbitrary-length instantiation syntax instead" ) # Unreachable return None # type: ignore else: return self.get_array_type(current_type) elif expr.i("arrinst"): return self.get_array_type( self.resolve_strict(expr[0], generic_type_context)) elif expr.i("#"): if not self.decide_type(expr[0], scope, generic_type_context).is_array(): raise TypingError( expr[0], "Can't decide length of something that isn't an array") return self.int_type elif expr.i("access"): lhs_type = self.decide_type(expr[0], scope, generic_type_context) if not lhs_type.is_array(): raise TypingError( expr, "Attempt to access element of something that isn't an array" ) assert isinstance(lhs_type, ArrayType) return lhs_type.parent_type elif expr.i("call"): # TODO: Move method call typechecking in here from emitter.py. signature = self.resolve_to_signature(expr[0], scope, generic_type_context) if len(expr["typeargs"]) != ( len(signature.generic_type_context.arguments) if signature.generic_type_context is not None else 0): expr.compile_error( "Wrong number of type arguments (expected %s, got %s)" % (len(signature.generic_type_context.arguments) if signature.generic_type_context is not None else 0, len(expr["typeargs"]))) signature = signature.specialize([ self.resolve_strict(typ, generic_type_context) for typ in expr["typeargs"] ], self.program, expr["typeargs"]) if signature is not None: if (not suppress_coercing_void_warning) and isinstance( signature.returntype, VoidType): expr.warn("Coercing void") return signature.returntype if expr[0].i("."): dot_node = expr[0] lhs_type = self.decide_type(dot_node[0], scope) if not lhs_type.is_clazz(): raise TypingError( dot_node[1], "Attempt to call a method on non-class type '%s'" % lhs_type) signature = lhs_type.method_signature(dot_node[1].data) if signature is None: expr.compile_error("Unknown method name '%s'" % expr[0].data) if (not suppress_coercing_void_warning) and isinstance( signature.returntype, VoidType): expr.warn("Coercing void") return signature.returntype elif expr.i("new"): ret = self.resolve_strict(expr[0], generic_type_context, allow_raw=True) # Why not .is_class()? Because we can't instantiate generic type parameters. if not isinstance(ret, ClazzType): raise TypingError(expr[0], "Type %s is not a class" % ret) type_arguments: List[AbstractType] = [] for argument in expr["typeargs"]: type_arguments.append( self.resolve_strict(argument, generic_type_context)) ret = ret.specialize(type_arguments, expr) return ret elif expr.i("."): # Ternary operator to satisfy typechecker (if-else statement would effectively require phi node which is ugly) lhs_typ: AbstractType = \ (scope.resolve(expr[0].data_strict, expr[0]).type) if expr[0].i("ident") \ else self.decide_type(expr[0], scope, generic_type_context) if not lhs_typ.is_clazz(): expr.compile_error( "Attempt to access an attribute of something that isn't a class" ) assert isinstance(lhs_typ, ClazzType) return lhs_typ.type_of_property(expr[1].data_strict, expr) elif expr.i("string"): string_type = self.get_string_type() if string_type is None: expr.compile_error( "Cannot use string literal without an implementation of stdlib.String" ) return string_type elif expr.i("super"): # TODO: This is horribly ugly typ = scope.resolve("this", expr).type if not isinstance(typ, ClazzType): expr.compile_error("Variable `this` isn't a class type") parent_type = typ.get_supertype() if parent_type is None: expr.compile_error( "Attempt to reference superclass in a class without a superclass" ) return parent_type else: raise ValueError( "Expression not accounted for in typesys. This is a compiler bug." )
def flatten_qualified(nod: parser.Node) -> str: if nod.i("ident"): return nod.data_strict else: return "%s.%s" % (flatten_qualified( nod[0]), flatten_qualified(nod[1]))
def resolves(self, node: parser.Node, program: "emitter.Program") -> bool: return node.i("[]") and self.parent_type.resolves(node[0], program)