Index: compiler/java/com/google/dart/compiler/backend/js/RuntimeTypeInjector.java |
diff --git a/compiler/java/com/google/dart/compiler/backend/js/RuntimeTypeInjector.java b/compiler/java/com/google/dart/compiler/backend/js/RuntimeTypeInjector.java |
deleted file mode 100644 |
index 3f3cafb7207353e386f2956ccd03355475744efa..0000000000000000000000000000000000000000 |
--- a/compiler/java/com/google/dart/compiler/backend/js/RuntimeTypeInjector.java |
+++ /dev/null |
@@ -1,1277 +0,0 @@ |
-// Copyright 2012, the Dart project authors. All rights reserved. |
-// Redistribution and use in source and binary forms, with or without |
-// modification, are permitted provided that the following conditions are |
-// met: |
-// |
-// * Redistributions of source code must retain the above copyright |
-// notice, this list of conditions and the following disclaimer. |
-// * Redistributions in binary form must reproduce the above |
-// copyright notice, this list of conditions and the following |
-// disclaimer in the documentation and/or other materials provided |
-// with the distribution. |
-// * Neither the name of Google Inc. nor the names of its |
-// contributors may be used to endorse or promote products derived |
-// from this software without specific prior written permission. |
-// |
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
- |
-package com.google.dart.compiler.backend.js; |
- |
-import static com.google.dart.compiler.util.AstUtil.and; |
-import static com.google.dart.compiler.util.AstUtil.assign; |
-import static com.google.dart.compiler.util.AstUtil.call; |
-import static com.google.dart.compiler.util.AstUtil.comma; |
-import static com.google.dart.compiler.util.AstUtil.nameref; |
-import static com.google.dart.compiler.util.AstUtil.neq; |
-import static com.google.dart.compiler.util.AstUtil.newAssignment; |
-import static com.google.dart.compiler.util.AstUtil.newInvocation; |
-import static com.google.dart.compiler.util.AstUtil.newNameRef; |
-import static com.google.dart.compiler.util.AstUtil.newQualifiedNameRef; |
-import static com.google.dart.compiler.util.AstUtil.newVar; |
-import static com.google.dart.compiler.util.AstUtil.not; |
- |
-import com.google.common.collect.Maps; |
-import com.google.common.collect.Sets; |
-import com.google.dart.compiler.InternalCompilerException; |
-import com.google.dart.compiler.ast.DartArrayLiteral; |
-import com.google.dart.compiler.ast.DartClass; |
-import com.google.dart.compiler.ast.DartClassMember; |
-import com.google.dart.compiler.ast.DartFunctionExpression; |
-import com.google.dart.compiler.ast.DartFunctionTypeAlias; |
-import com.google.dart.compiler.ast.DartMapLiteral; |
-import com.google.dart.compiler.ast.DartMethodDefinition; |
-import com.google.dart.compiler.ast.DartNewExpression; |
-import com.google.dart.compiler.ast.DartParameter; |
-import com.google.dart.compiler.ast.DartTypeNode; |
-import com.google.dart.compiler.backend.js.ast.JsArrayAccess; |
-import com.google.dart.compiler.backend.js.ast.JsArrayLiteral; |
-import com.google.dart.compiler.backend.js.ast.JsBlock; |
-import com.google.dart.compiler.backend.js.ast.JsExpression; |
-import com.google.dart.compiler.backend.js.ast.JsFunction; |
-import com.google.dart.compiler.backend.js.ast.JsInvocation; |
-import com.google.dart.compiler.backend.js.ast.JsName; |
-import com.google.dart.compiler.backend.js.ast.JsNameRef; |
-import com.google.dart.compiler.backend.js.ast.JsParameter; |
-import com.google.dart.compiler.backend.js.ast.JsProgram; |
-import com.google.dart.compiler.backend.js.ast.JsReturn; |
-import com.google.dart.compiler.backend.js.ast.JsScope; |
-import com.google.dart.compiler.backend.js.ast.JsStatement; |
-import com.google.dart.compiler.backend.js.ast.JsStringLiteral; |
-import com.google.dart.compiler.backend.js.ast.JsThisRef; |
-import com.google.dart.compiler.common.SourceInfo; |
-import com.google.dart.compiler.common.Symbol; |
-import com.google.dart.compiler.resolver.ClassElement; |
-import com.google.dart.compiler.resolver.ConstructorElement; |
-import com.google.dart.compiler.resolver.CoreTypeProvider; |
-import com.google.dart.compiler.resolver.DynamicElement; |
-import com.google.dart.compiler.resolver.Element; |
-import com.google.dart.compiler.resolver.ElementKind; |
-import com.google.dart.compiler.resolver.EnclosingElement; |
-import com.google.dart.compiler.resolver.FunctionAliasElementImplementation; |
-import com.google.dart.compiler.resolver.LibraryElement; |
-import com.google.dart.compiler.resolver.MethodElement; |
-import com.google.dart.compiler.resolver.VariableElement; |
-import com.google.dart.compiler.type.FunctionAliasType; |
-import com.google.dart.compiler.type.FunctionType; |
-import com.google.dart.compiler.type.InterfaceType; |
-import com.google.dart.compiler.type.Type; |
-import com.google.dart.compiler.type.TypeKind; |
-import com.google.dart.compiler.type.TypeVariable; |
-import com.google.dart.compiler.type.Types; |
-import com.google.dart.compiler.util.AstUtil; |
- |
-import java.util.List; |
-import java.util.Map; |
-import java.util.Set; |
- |
-/** |
- * A helper class that contains the logic necessary for injecting runtime type references. |
- * |
- * @author johnlenz@google.com (John Lenz) |
- */ |
-public class RuntimeTypeInjector { |
- private final boolean emitTypeChecks; |
- private final TraversalContextProvider context; |
- private final JsBlock globalBlock; |
- private final JsScope globalScope; |
- // Maps builtin types to Javascript types implementations. |
- private final Map<ClassElement, String> builtInTypeChecks; |
- private CoreTypeProvider typeProvider; |
- private final TranslationContext translationContext; |
- private final Types types; |
- private final DartMangler mangler; |
- private final LibraryElement unitLibrary; |
- private static final String RTT_DYNAMIC_LOOKUP = "RTT.dynamicType.$lookupRTT"; |
- private static final String RTT_NAMED_PARAMETER = "named"; |
- |
- RuntimeTypeInjector( |
- TraversalContextProvider context, |
- CoreTypeProvider typeProvider, |
- TranslationContext translationContext, |
- boolean emitTypeChecks, DartMangler mangler, |
- LibraryElement unitLibrary) { |
- this.context = context; |
- this.translationContext = translationContext; |
- JsProgram program = translationContext.getProgram(); |
- this.globalBlock = program.getGlobalBlock(); |
- this.globalScope = program.getScope(); |
- this.builtInTypeChecks = makeBuiltinTypes(typeProvider); |
- this.typeProvider = typeProvider; |
- this.emitTypeChecks = emitTypeChecks; |
- this.mangler = mangler; |
- this.unitLibrary = unitLibrary; |
- types = Types.getInstance(typeProvider); |
- } |
- |
- private Map<ClassElement, String> makeBuiltinTypes(CoreTypeProvider typeProvider) { |
- Map<ClassElement, String> builtinTypes = Maps.newHashMap(); |
- builtinTypes.put(typeProvider.getBoolType().getElement(), "$isBool"); |
- builtinTypes.put(typeProvider.getIntType().getElement(), "$isNum"); |
- builtinTypes.put(typeProvider.getDoubleType().getElement(), "$isNum"); |
- builtinTypes.put(typeProvider.getStringType().getElement(), "$isString"); |
- return builtinTypes; |
- } |
- |
- /** |
- * Generate the code necessary to allow for runtime type checks |
- */ |
- void generateRuntimeTypeInfo(DartClass x) { |
- generateRuntimeTypeInfoMethods(x); |
- |
- ClassElement classElement = x.getSymbol(); |
- if (!classElement.isInterface()) { |
- injectInterfaceMarkers(classElement, x); |
- } |
- } |
- |
- /** |
- * Generate the code necessary to allow for runtime type checks of dart typedefs |
- */ |
- void generateRuntimeTypeInfo(DartFunctionTypeAlias x) { |
- generateRTTLookupMethod(x); |
- } |
- |
- /** |
- * Generate the code necessary to allow for runtime type checks of dart class methods |
- */ |
- void generateRuntimeTypeInfo(DartMethodDefinition x) { |
- generateRTTLookupMethod(x); |
- } |
- |
- /** |
- * Generate the code necessary to allow for runtime type checks of dart function expressions |
- */ |
- void generateRuntimeTypeInfo(DartFunctionExpression x, String lookupName) { |
- generateRTTLookupMethod(x, lookupName); |
- } |
- |
- private void injectInterfaceMarkers(ClassElement classElement, SourceInfo srcRef) { |
- JsProgram program = translationContext.getProgram(); |
- JsName classJsName = translationContext.getNames().getName(classElement); |
- for (InterfaceType iface : getAllInterfaces(classElement)) { |
- JsStatement assignment = (JsStatement) newAssignment( |
- newNameRef( |
- newNameRef(new JsNameRef(classJsName), "prototype"), |
- "$implements$" + translationContext.getMangler().mangleClassName(iface.getElement())), |
- program.getNumberLiteral(1)).makeStmt().setSourceRef(srcRef); |
- globalBlock.getStatements().add(assignment); |
- } |
- } |
- |
- private Set<InterfaceType> getAllInterfaces(ClassElement classElement) { |
- // TODO(johnlenz): All interfaces here should not include the super class implemented interfaces |
- // those are handled by the super-class definition. |
- Set<InterfaceType> interfaces = Sets.newLinkedHashSet(); |
- if (classElement.getType() == null) { |
- throw new InternalCompilerException("type is null on ClassElement " + classElement); |
- } |
- // A class needs to implement its own implied interface so the "is" |
- // implementation works properly. |
- interfaces.add(classElement.getType()); |
- |
- InterfaceType current = classElement.getType(); |
- if (current != null) { |
- addAllInterfaces(interfaces, current); |
- } |
- return interfaces; |
- } |
- |
- private void addAllInterfaces(Set<InterfaceType> interfaces, InterfaceType t) { |
- interfaces.add(t); |
- for (InterfaceType current : t.getElement().getInterfaces()) { |
- addAllInterfaces(interfaces, current); |
- } |
- } |
- |
- /** |
- * Insert the function or method necessary to implement runtime type information |
- * for the provided class. |
- */ |
- private void generateRuntimeTypeInfoMethods(DartClass x) { |
- // 1) create static type information lookup function |
- generateRTTLookupMethod(x); |
- |
- // 2) create a method to fill in the type information for the class |
- ClassElement classElement = x.getSymbol(); |
- if (hasRTTImplements(classElement)) { |
- generateRTTImplementsMethod(x); |
- } |
- |
- // 3) create "addTo" method for use by classes or interfaces that inherit from this class |
- generateRTTAddToMethod(x); |
- } |
- |
- private void generateRTTLookupMethod(DartClass x) { |
- ClassElement classElement = x.getSymbol(); |
- boolean hasTypeParams = hasTypeParameters(classElement); |
- |
- // 1) create static type information construction function |
- // function Foo$lookupOrCreateRTT(typeargs) { |
- // return $createRTT(name, Foo$RTTimplements, typeargs) ; |
- // } |
- |
- // Build the function |
- JsFunction lookupFn = new JsFunction(globalScope); |
- lookupFn.setBody(new JsBlock()); |
- List<JsStatement> body = lookupFn.getBody().getStatements(); |
- JsScope scope = new JsScope(globalScope, "temp"); |
- |
- JsProgram program = translationContext.getProgram(); |
- |
- JsInvocation invokeCreate = call(null, |
- newQualifiedNameRef("RTT.create"), getRTTClassId(classElement)); |
- List<JsExpression> callArgs = invokeCreate.getArguments(); |
- if (hasRTTImplements(classElement)) { |
- callArgs.add(getRTTImplementsMethodName(classElement)); |
- } else { |
- // need a placeholder param if the typeArgs are needed. |
- callArgs.add(program.getNullLiteral()); |
- } |
- |
- JsName typeArgs = scope.declareName("typeArgs"); |
- lookupFn.getParameters().add(new JsParameter(typeArgs)); |
- if (hasTypeParams) { |
- callArgs.add(typeArgs.makeRef()); |
- } else { |
- callArgs.add(program.getNullLiteral()); |
- } |
- |
- JsName named = scope.declareName(RTT_NAMED_PARAMETER); |
- lookupFn.getParameters().add(new JsParameter(named)); |
- callArgs.add(named.makeRef()); |
- |
- body.add(new JsReturn(invokeCreate)); |
- |
- // Finally, Add the function |
- JsExpression fnDecl = assign(null, |
- getRTTLookupMethodNameRef(classElement), lookupFn); |
- globalBlock.getStatements().add(fnDecl.makeStmt()); |
- } |
- |
- private void generateRTTImplementsMethod(DartClass x) { |
- ClassElement classElement = x.getSymbol(); |
- |
- // 1) create static type information construction function |
- // function Foo$lookupOrCreateRTT(rtt, typeArgs) { |
- // |
- // // superclass |
- // FooSuper$addTo(rtt, superTypeArg1, ...); |
- // // interfaces |
- // FooInterface1$addTo(rtt, interface1TypeArg1, ...); |
- // |
- // } |
- |
- boolean hasTypeParams = classElement.getTypeParameters().size() > 0; |
- |
- // Build the function |
- JsFunction implementsFn = new JsFunction(globalScope); |
- implementsFn.setBody(new JsBlock()); |
- List<JsStatement> body = implementsFn.getBody().getStatements(); |
- JsScope scope = new JsScope(globalScope, "temp"); |
- |
- JsName rtt = scope.declareName("rtt"); |
- implementsFn.getParameters().add(new JsParameter(rtt)); |
- JsName typeArgs = null; |
- if (hasTypeParams) { |
- typeArgs = scope.declareName("typeArgs"); |
- implementsFn.getParameters().add(new JsParameter(typeArgs)); |
- } |
- |
- JsInvocation callAddTo = newInvocation(getRTTAddToMethodName(classElement), rtt.makeRef()); |
- if (hasTypeParams) { |
- typeArgs = scope.declareName("typeArgs"); |
- callAddTo.getArguments().add(typeArgs.makeRef()); |
- } |
- body.add(callAddTo.makeStmt()); |
- |
- // Finally, Add the function |
- JsExpression fnDecl = assign(null, |
- getRTTImplementsMethodName(classElement), implementsFn); |
- globalBlock.getStatements().add(fnDecl.makeStmt()); |
- } |
- |
- private void generateRTTAddToMethod(DartClass x) { |
- ClassElement classElement = x.getSymbol(); |
- |
- // 2) create "addTo" method |
- // Foo$Type$addTo(target, typeargs) { |
- // var rtt = Foo$lookupOrCreateRTT(typeargs) |
- // target.implementedTypes[rtt.classkey] = rtt; |
- // } |
- |
- // Build the function |
- JsFunction addToFn = new JsFunction(globalScope); |
- addToFn.setBody(new JsBlock()); |
- JsScope scope = new JsScope(globalScope, "temp"); |
- |
- JsName targetType = scope.declareName("target"); |
- addToFn.getParameters().add(new JsParameter(targetType)); |
- |
- // Get the RTT info object |
- JsName rtt = scope.declareName("rtt"); |
- List<JsStatement> body = addToFn.getBody().getStatements(); |
- JsInvocation callLookup = newInvocation( |
- getRTTLookupMethodNameRef(classElement)); |
- |
- if (hasTypeParameters(classElement)) { |
- JsName typeArgs = scope.declareName("typeArgs"); |
- addToFn.getParameters().add(new JsParameter(typeArgs)); |
- callLookup.getArguments().add(new JsNameRef(typeArgs)); |
- } |
- |
- JsStatement rttLookup = newVar((SourceInfo)null, rtt, callLookup); |
- body.add(rttLookup); |
- |
- // store it. |
- JsExpression addToTypes = newAssignment( |
- new JsArrayAccess( |
- newNameRef(targetType.makeRef(), "implementedTypes"), |
- newNameRef(rtt.makeRef(), "classKey")), |
- rtt.makeRef()); |
- body.add(addToTypes.makeStmt()); |
- |
- InterfaceType superType = classElement.getSupertype(); |
- if (superType != null && !superType.getElement().isObject()) { |
- ClassElement interfaceElement = superType.getElement(); |
- JsInvocation callAddTo = newInvocation( |
- getRTTAddToMethodName(interfaceElement), targetType.makeRef()); |
- if (hasTypeParameters(interfaceElement) && !superType.hasDynamicTypeArgs()) { |
- JsArrayLiteral superTypeArgs = new JsArrayLiteral(); |
- List<Type> typeParams = classElement.getTypeParameters(); |
- for (Type arg : superType.getArguments()) { |
- superTypeArgs.getExpressions().add( |
- buildTypeLookupExpression(arg, typeParams, |
- nameref(null, targetType.makeRef(), "typeArgs"))); |
- } |
- callAddTo.getArguments().add(superTypeArgs); |
- } |
- body.add(callAddTo.makeStmt()); |
- } |
- |
- // Add the interfaces |
- |
- for (InterfaceType interfaceType : classElement.getInterfaces() ) { |
- ClassElement interfaceElement = interfaceType.getElement(); |
- // Codefu: Random addition to keep language/BlackListed13 tests failing. |
- // RTT.dynamic could define $addTo(), but this should be treated as a |
- // a compile time error, not a run time error. |
- if (interfaceElement instanceof DynamicElement) { |
- continue; |
- } |
- JsInvocation callAddTo = call(null, |
- getRTTAddToMethodName(interfaceElement), targetType.makeRef()); |
- if (hasTypeParameters(interfaceElement) && !interfaceType.hasDynamicTypeArgs()) { |
- JsArrayLiteral interfaceTypeArgs = new JsArrayLiteral(); |
- List<Type> typeParams = classElement.getTypeParameters(); |
- for (Type arg : interfaceType.getArguments()) { |
- interfaceTypeArgs.getExpressions().add( |
- buildTypeLookupExpression(arg, typeParams, |
- nameref(null, targetType.makeRef(), "typeArgs"))); |
- } |
- callAddTo.getArguments().add(interfaceTypeArgs); |
- } |
- body.add(callAddTo.makeStmt()); |
- } |
- |
- // Add the function statement |
- JsExpression fnDecl = newAssignment( |
- getRTTAddToMethodName(classElement), addToFn); |
- globalBlock.getStatements().add(fnDecl.makeStmt()); |
- } |
- |
- private void generateRTTLookupMethod(DartMethodDefinition x) { |
- generateRTTLookupMethod(x.getSymbol(), null); |
- } |
- |
- private void generateRTTLookupMethod(DartFunctionExpression x, String overrideName) { |
- generateRTTLookupMethod(x.getSymbol(), overrideName); |
- } |
- |
- private ClassElement getEnclosingClassElement(EnclosingElement enclosingElement) { |
- while(enclosingElement != null) { |
- if (enclosingElement.getKind().equals(ElementKind.CLASS)) { |
- return (ClassElement)enclosingElement; |
- } |
- enclosingElement = enclosingElement.getEnclosingElement(); |
- } |
- return null; |
- } |
- |
- /* |
- * This function will create a lookup function that indirectly calls RTT.createFunction with |
- * and array of parameter types and the return type as arguments. Example: |
- * Dart: |
- * typedef List<J> TestAliasType<K,J>(int x, K k); |
- * JS: |
- * <libraryname>$<TestAliasType>$Dart.$lookupRTT = function(typeArgs){ |
- * return RTT.createFunction( |
- * [int$Dart.$lookupRTT(), RTT.getTypeArg(typeArgs, 0)], |
- * List$Dart.$lookupRTT([RTT.getTypeArg(typeArgs, 1)])); |
- * } |
- */ |
- private void generateRTTLookupMethod(MethodElement methodElement, String overrideName) { |
- boolean hasTypeArguments = false; |
- |
- if (ElementKind.of(methodElement).equals(ElementKind.CONSTRUCTOR) |
- || methodElement.getModifiers().isNative()) { |
- // No type lookups for constructors or natives |
- return; |
- } |
- ClassElement classElement = getEnclosingClassElement(methodElement.getEnclosingElement()); |
- hasTypeArguments = classElement != null ? hasTypeParameters(classElement) : false; |
- |
- JsProgram program = translationContext.getProgram(); |
- JsExpression typeArgContextExpr = hasTypeArguments ? |
- buildTypeArgsReferenceFromThis(classElement) : null; |
- |
- // Build the function |
- JsFunction lookupFn = new JsFunction(globalScope); |
- lookupFn.setBody(new JsBlock()); |
- JsScope scope = new JsScope(globalScope, "temp"); |
- |
- List<JsStatement> body = lookupFn.getBody().getStatements(); |
- JsInvocation callLookup; |
- |
- callLookup = newInvocation(newQualifiedNameRef("RTT.createFunction")); |
- |
- JsArrayLiteral arr = generateTypeArrayFromElements(methodElement.getParameters(), classElement, |
- typeArgContextExpr); |
- |
- JsExpression returnExpr = generateRTTLookupForType(methodElement, |
- methodElement.getReturnType(), classElement, typeArgContextExpr); |
- |
- callLookup.getArguments().add(arr.getExpressions().isEmpty() ? program.getNullLiteral() : arr); |
- callLookup.getArguments().add(returnExpr); |
- body.add(new JsReturn(callLookup)); |
- |
- // Finally, Add the lookup function to the global block. |
- JsExpression fnDecl; |
- if (overrideName == null) { |
- JsNameRef methodToCall = getRTTLookupMethodNameRef(methodElement); |
- if (methodElement.getEnclosingElement().getKind().equals(ElementKind.CLASS)) { |
- fnDecl = assign(null, methodToCall, lookupFn); |
- } else { |
- // Top level method |
- lookupFn.setName(scope.declareFreshName(methodToCall.getIdent())); |
- fnDecl = lookupFn; |
- } |
- } else { |
- // Special care for hoisted functions. |
- lookupFn.setName(scope.declareName(overrideName)); |
- fnDecl = lookupFn; |
- } |
- globalBlock.getStatements().add(fnDecl.makeStmt()); |
- } |
- |
- /* |
- * Create a direct lookup of types for the given element. Useful for in-line function types, as |
- * in the example: |
- * int foo( int bar(double x) ); |
- * No lookup method exists for the type "int bar(double x)", hence the in-line creation of: |
- * RTT.createFunction([RTT Parameter Types],RTT ReturnType)) |
- */ |
- private JsInvocation generateRTTCreate(Element element, |
- FunctionType elementType, |
- ClassElement classElement) { |
- boolean hasTypes = false; |
- |
- if (ElementKind.of(element).equals(ElementKind.CONSTRUCTOR) |
- || element.getModifiers().isNative()) { |
- // No type lookups for constructors or natives |
- return newInvocation(newQualifiedNameRef(RTT_DYNAMIC_LOOKUP)); |
- } |
- hasTypes = classElement != null ? hasTypeParameters(classElement) : false; |
- |
- JsProgram program = translationContext.getProgram(); |
- JsExpression typeArgContextExpr = hasTypes ? buildTypeArgsReference(classElement) : null; |
- JsInvocation callLookup; |
- callLookup = newInvocation(newQualifiedNameRef("RTT.createFunction")); |
- |
- JsArrayLiteral arr = generateTypeArrayFromTypes(elementType.getParameterTypes(), classElement, |
- typeArgContextExpr); |
- if (arr == null) { |
- return newInvocation(newQualifiedNameRef(RTT_DYNAMIC_LOOKUP)); |
- } |
- |
- JsExpression returnExpr = generateRTTLookupForType(element, elementType.getReturnType(), |
- classElement, typeArgContextExpr); |
- |
- callLookup.getArguments().add(arr.getExpressions().isEmpty() ? program.getNullLiteral() : arr); |
- callLookup.getArguments().add(returnExpr); |
- return callLookup; |
- } |
- |
- private JsArrayLiteral generateTypeArrayFromTypes(List<Type> parameterTypes, |
- ClassElement classElement, JsExpression typeArgContextExpr) { |
- JsArrayLiteral jsTypeArray = new JsArrayLiteral(); |
- for (Type param : parameterTypes) { |
- JsExpression elementExpr; |
- elementExpr = generateRTTLookupForType(param.getElement(), param, classElement, |
- typeArgContextExpr); |
- jsTypeArray.getExpressions().add(elementExpr); |
- } |
- return jsTypeArray; |
- } |
- |
- private JsArrayLiteral generateTypeArrayFromParameters(List<DartParameter> params, |
- ClassElement classElement, JsExpression typeArgContextExpr) { |
- JsArrayLiteral jsTypeArray = new JsArrayLiteral(); |
- for (DartParameter param : params) { |
- Type paramType = param.getTypeNode() == null ? null : param.getTypeNode().getType(); |
- JsInvocation elementInvoke = generateRTTLookupForType(param.getSymbol(), paramType, classElement, |
- typeArgContextExpr); |
- jsTypeArray.getExpressions().add(elementInvoke); |
- } |
- return jsTypeArray; |
- } |
- |
- private JsArrayLiteral generateTypeArrayFromElements(List<VariableElement> elements, |
- ClassElement classElement, JsExpression typeArgContextExpr) { |
- JsArrayLiteral jsTypeArray = new JsArrayLiteral(); |
- for (VariableElement element : elements) { |
- JsExpression rttTypeExpression = generateRTTLookupForType(element, element.getType(), |
- classElement, typeArgContextExpr); |
- jsTypeArray.getExpressions().add(rttTypeExpression); |
- } |
- return jsTypeArray; |
- } |
- |
- private JsInvocation generateRTTLookupForType(Element element, Type elementType, |
- ClassElement classElement, JsExpression typeArgContextExpr) { |
- return generateRTTLookupForType(element, elementType, classElement, typeArgContextExpr, |
- element.getModifiers().isNamed() ? element.getName() : null); |
- } |
- |
- private JsInvocation generateRTTLookupForType(Element element, Type elementType, |
- ClassElement classElement, JsExpression typeArgContextExpr, String named) { |
- JsInvocation elementInvoke = null; |
- switch (TypeKind.of(elementType)) { |
- case VARIABLE: |
- elementInvoke = buildTypeLookupExpression(elementType, classElement.getTypeParameters(), |
- typeArgContextExpr); |
- break; |
- case INTERFACE: |
- elementInvoke = generateRTTLookup((ClassElement) elementType.getElement(), |
- (InterfaceType) elementType, classElement); |
- break; |
- case FUNCTION: |
- elementInvoke = generateRTTCreate(element, (FunctionType) elementType, classElement); |
- break; |
- case FUNCTION_ALIAS: |
- elementInvoke = buildTypeLookupExpression(elementType, |
- classElement != null ? classElement.getTypeParameters() : null, typeArgContextExpr); |
- break; |
- case DYNAMIC: |
- case VOID: |
- case NONE: |
- elementInvoke = newInvocation(newQualifiedNameRef(RTT_DYNAMIC_LOOKUP)); |
- break; |
- default: |
- elementInvoke = buildTypeLookupExpression(elementType, classElement.getTypeParameters(), |
- typeArgContextExpr); |
- break; |
- } |
- if (named != null) { |
- if (elementInvoke.getArguments().size() == 0) { |
- elementInvoke.getArguments().add(translationContext.getProgram().getNullLiteral()); |
- } |
- elementInvoke.getArguments().add( |
- translationContext.getProgram().getStringLiteral(named)); |
- } |
- assert elementInvoke != null; |
- return elementInvoke; |
- } |
- |
- private JsName getJsName(Symbol symbol) { |
- return translationContext.getNames().getName(symbol); |
- } |
- |
- /* |
- * This function will create a lookup function that indirectly calls RTT.createFunction with |
- * and array of parameter types and the return type as arguments. Example: |
- * Dart: |
- * typedef List<K> TestAliasType<K>(int x, K k); |
- * JS: |
- * <libraryname>$<TestAliasType>$Dart.$lookupRTT = function(typeArgs){ |
- * return RTT.createFunction( |
- * [int$Dart.$lookupRTT(), RTT.getTypeArg(typeArgs, 0)], |
- * List$Dart.$lookupRTT([RTT.getTypeArg(typeArgs, 0)])); |
- * } |
- */ |
- private void generateRTTLookupMethod(DartFunctionTypeAlias x) { |
- FunctionAliasElementImplementation classElement = |
- (FunctionAliasElementImplementation) x.getSymbol(); |
- FunctionType funcType = classElement.getFunctionType(); |
- |
- // Build the function |
- JsFunction lookupFn = new JsFunction(globalScope); |
- lookupFn.setBody(new JsBlock()); |
- List<JsStatement> body = lookupFn.getBody().getStatements(); |
- JsScope scope = new JsScope(globalScope, "temp"); |
- JsProgram program = translationContext.getProgram(); |
- JsInvocation invokeCreate = call(null, newQualifiedNameRef("RTT.createFunction")); |
- List<JsExpression> callArgs = invokeCreate.getArguments(); |
- |
- JsName typeArgs = scope.declareName("typeArgs"); |
- JsExpression typeArgsExpr = new JsNameRef("typeArgs"); |
- lookupFn.getParameters().add(new JsParameter(typeArgs)); |
- |
- JsArrayLiteral arr = generateTypeArrayFromParameters(x.getParameters(), classElement, |
- typeArgsExpr); |
- callArgs.add(arr.getExpressions().isEmpty() ? program.getNullLiteral() : arr); |
- |
- JsExpression returnExpr = generateRTTLookupForType(funcType.getElement(), |
- funcType.getReturnType(), classElement, typeArgsExpr); |
- callArgs.add(returnExpr); |
- |
- JsName named = scope.declareName(RTT_NAMED_PARAMETER); |
- lookupFn.getParameters().add(new JsParameter(named)); |
- callArgs.add(named.makeRef()); |
- |
- body.add(new JsReturn(invokeCreate)); |
- |
- // Finally, Add the function to the global block of statements. |
- JsExpression fnDecl = assign(null, getRTTLookupMethodNameRef(classElement), lookupFn); |
- globalBlock.getStatements().add(fnDecl.makeStmt()); |
- } |
- |
- static JsExpression getRTTClassId(TranslationContext translationContext, |
- ClassElement classElement) { |
- JsName classJsName = translationContext.getNames().getName(classElement); |
- JsProgram program = translationContext.getProgram(); |
- JsStringLiteral clsid = program.getStringLiteral(classJsName.getShortIdent()); |
- return call(null, nameref(null, "$cls"), clsid); |
- } |
- |
- JsExpression getRTTClassId(ClassElement classElement) { |
- return getRTTClassId(translationContext, classElement); |
- } |
- |
- private JsNameRef getRTTLookupMethodNameRef(ClassElement classElement) { |
- return nameref(null, translationContext.getNames().getName(classElement), "$lookupRTT"); |
- } |
- |
- public JsNameRef getRTTLookupMethodNameRef(MethodElement methodElement) { |
- JsNameRef methodToCall; |
- |
- if (ElementKind.of(methodElement.getEnclosingElement()) == ElementKind.CLASS) { |
- JsNameRef classJsNameRef = getJsName(methodElement.getEnclosingElement()).makeRef(); |
- String mangledMethodName = mangler.mangleRttLookupMethod(methodElement, unitLibrary); |
- if (methodElement.getModifiers().isStatic()) { |
- methodToCall = AstUtil.newNameRef(classJsNameRef, mangledMethodName); |
- } else { |
- JsNameRef prototypeRef = AstUtil.newPrototypeNameRef(classJsNameRef); |
- methodToCall = AstUtil.newNameRef(prototypeRef, mangledMethodName); |
- } |
- } else { |
- // Top level method |
- methodToCall = AstUtil.newQualifiedNameRef(mangler.mangleRttLookupMethod(methodElement, |
- unitLibrary)); |
- } |
- return methodToCall; |
- } |
- |
- private JsNameRef getRTTImplementsMethodName(ClassElement classElement) { |
- return nameref(null, translationContext.getNames().getName(classElement), "$RTTimplements"); |
- } |
- |
- private JsNameRef getRTTAddToMethodName(ClassElement classElement) { |
- return nameref(null, translationContext.getNames().getName(classElement), "$addTo"); |
- } |
- |
- /** |
- * Build a class relative type arguments expression |
- * |
- * @param classElement The class whose type arguments to refer to. |
- */ |
- private JsExpression buildTypeArgsReference(ClassElement classElement) { |
- JsExpression typeArgs; |
- if (inFactory()) { |
- if (hasTypeParameters(classElement)) { |
- // There is no inheritence involved with generic factory methods, |
- // so we simply use a hard reference to the type info parameter to the |
- // factory. |
- typeArgs = nameref(null, "$typeArgs"); |
- } else { |
- typeArgs = new JsArrayLiteral(); |
- } |
- } else { |
- typeArgs = buildTypeArgsReferenceFromThis(classElement); |
- } |
- return typeArgs; |
- } |
- |
- private JsExpression buildTypeArgsReferenceFromThis(ClassElement classElement) { |
- // build: $getTypeArgsFor(this, 'class') |
- // Here build a reference to the type parameter for this class instance, this needs |
- // be looked up on a per-class basis. |
- return call(null, |
- newQualifiedNameRef( |
- "RTT.getTypeArgsFor"), new JsThisRef(), getRTTClassId(classElement)); |
- } |
- |
- /** |
- * @return a JsArrayLiteral listing the type arguments for the interface instance. |
- */ |
- private JsExpression buildTypeArgs( |
- InterfaceType instanceType, |
- List<Type> listTypeVars, |
- JsExpression contextTypeArgs) { |
- ClassElement classElement = instanceType.getElement(); |
- if (!hasTypeParameters(classElement)) { |
- return null; |
- } |
- |
- if (instanceType.hasDynamicTypeArgs()) { |
- JsProgram program = translationContext.getProgram(); |
- return program.getNullLiteral(); |
- } |
- |
- JsArrayLiteral arr = new JsArrayLiteral(); |
- assert instanceType.getArguments().size() > 0; |
- for (Type type : instanceType.getArguments()) { |
- JsExpression typeExpr = buildTypeLookupExpression( |
- type, listTypeVars, contextTypeArgs); |
- arr.getExpressions().add(typeExpr); |
- } |
- |
- return arr; |
- } |
- |
- /** |
- * @return a JsArrayLiteral listing the type arguments for the interface instance. |
- */ |
- private JsExpression buildTypeArgsForFactory( |
- FunctionType functionType, |
- InterfaceType instanceType, |
- List<Type> listTypeVars, |
- JsExpression contextTypeArgs) { |
- if (instanceType.getElement().getTypeParameters().size() == 0) { |
- return null; |
- } |
- |
- if (instanceType.hasDynamicTypeArgs()) { |
- return translationContext.getProgram().getNullLiteral(); |
- } |
- |
- JsArrayLiteral arr = new JsArrayLiteral(); |
- for (Type type : instanceType.getArguments()) { |
- JsExpression typeExpr = buildTypeLookupExpression( |
- type, listTypeVars, contextTypeArgs); |
- arr.getExpressions().add(typeExpr); |
- } |
- |
- return arr; |
- } |
- |
- /** |
- * @return an expression for looking up the RTT information for the given RAW type. |
- */ |
- private JsExpression generateRawRTTLookup(ClassElement classElement) { |
- JsInvocation invokeLookup = call(null, getRTTLookupMethodNameRef(classElement)); |
- return invokeLookup; |
- } |
- |
- private JsExpression generateRTTLookup( |
- InterfaceType instanceType, ClassElement contextClassElement) { |
- return generateRTTLookup(instanceType.getElement(), instanceType, contextClassElement); |
- } |
- |
- private JsInvocation generateRTTLookup( |
- ClassElement classElement, InterfaceType instanceType, ClassElement contextClassElement) { |
- JsInvocation invokeLookup = call(null, getRTTLookupMethodNameRef(classElement)); |
- if (hasTypeParameters(instanceType.getElement()) && !instanceType.hasDynamicTypeArgs()) { |
- JsExpression typeArgs = generateTypeArgsArray(instanceType, contextClassElement); |
- assert typeArgs != null; |
- invokeLookup.getArguments().add(typeArgs); |
- } |
- return invokeLookup; |
- } |
- |
- private JsExpression generateTypeArgsArray( |
- InterfaceType instanceType, ClassElement contextClassElement) { |
- JsExpression typeArgs; |
- if (inStaticNotFactory(contextClassElement)) { |
- if( ElementKind.of(contextClassElement) == ElementKind.FUNCTION_TYPE_ALIAS) { |
- // Special case for FunctionAlias as they can have generic types. |
- typeArgs = buildTypeArgs(instanceType, contextClassElement.getTypeParameters(), |
- new JsNameRef("typeArgs")); |
- } else { |
- typeArgs = buildTypeArgs(instanceType, null, null); |
- } |
- } else { |
- // Build type args in a class context: |
- // When building a type list in a class instance, type variables are |
- // resolved from the runtime type information on the instance of the object. |
- JsExpression typeArgContextExpr = buildTypeArgsReference(contextClassElement); |
- typeArgs = buildTypeArgs( |
- instanceType, |
- contextClassElement.getTypeParameters(), |
- typeArgContextExpr); |
- } |
- return typeArgs; |
- } |
- |
- private JsExpression generateTypeArgsArrayForFactory(FunctionType functionType, |
- InterfaceType instanceType, |
- ClassElement contextClassElement) { |
- JsExpression typeArgs; |
- if (inStaticNotFactory(contextClassElement)) { |
- typeArgs = buildTypeArgsForFactory(functionType, instanceType, null, null); |
- } else { |
- // Build type args in a class context: |
- // When building a type list in a class instance, type variables are |
- // resolved from the runtime type information on the instance of the |
- // object. |
- JsExpression typeArgContextExpr = buildTypeArgsReference(contextClassElement); |
- typeArgs = buildTypeArgsForFactory(functionType, instanceType, contextClassElement |
- .getTypeParameters(), typeArgContextExpr); |
- } |
- return typeArgs; |
- } |
- |
- /** |
- * @param classElement |
- * @return Whether the class has type parameters. |
- */ |
- private boolean hasTypeParameters(ClassElement classElement) { |
- return classElement.getTypeParameters().size() > 0; |
- } |
- |
- private boolean hasRTTImplements(ClassElement classElement) { |
- InterfaceType superType = classElement.getSupertype(); |
- return ((superType != null |
- && !superType.getElement().isObject()) |
- || !classElement.getInterfaces().isEmpty()); |
- } |
- |
- /** |
- * @return js The expression used to lookup the RTT for the given type. |
- */ |
- private JsInvocation buildTypeLookupExpression( |
- Type type, List<Type> list, JsExpression contextTypeArgs) { |
- switch (TypeKind.of(type)) { |
- case INTERFACE: |
- case FUNCTION_ALIAS: |
- InterfaceType interfaceType = (InterfaceType) type; |
- JsInvocation callLookup = call(null, |
- getRTTLookupMethodNameRef(interfaceType.getElement())); |
- if (hasTypeParameters(interfaceType.getElement()) && !interfaceType.hasDynamicTypeArgs()) { |
- JsArrayLiteral typeArgs = new JsArrayLiteral(); |
- for (Type arg : interfaceType.getArguments()) { |
- typeArgs.getExpressions().add(buildTypeLookupExpression(arg, list, contextTypeArgs)); |
- } |
- callLookup.getArguments().add(typeArgs); |
- } |
- return callLookup; |
- |
- case FUNCTION: |
- FunctionType functionType = (FunctionType) type; |
- JsInvocation functionTypeCallLookup = call(null, |
- getRTTLookupMethodNameRef(functionType.getElement())); |
- if (hasTypeParameters(functionType.getElement())) { |
- JsArrayLiteral typeArgs = new JsArrayLiteral(); |
- functionTypeCallLookup.getArguments().add(typeArgs); |
- } |
- return functionTypeCallLookup; |
- |
- case VARIABLE: |
- TypeVariable var = (TypeVariable)type; |
- JsProgram program = translationContext.getProgram(); |
- int varIndex = 0; |
- for (Type t : list) { |
- if (t.equals(type)) { |
- return call(null, newQualifiedNameRef("RTT.getTypeArg"), |
- Cloner.clone(contextTypeArgs), |
- program.getNumberLiteral(varIndex)); |
- } |
- varIndex++; |
- } |
- throw new AssertionError("unresolved type variable:" + var); |
- |
- default: |
- throw new AssertionError("unexpected type kind:" + type.getKind()); |
- } |
- } |
- |
- /** |
- * Add the appropriate code to a class's constructing factory, if necessary. |
- */ |
- void maybeAddClassRuntimeTypeToConstructor( |
- ClassElement classElement, JsFunction factory, JsExpression thisRef) { |
- // TODO(johnlenz):in optimized mode, only add this where it is needed. |
- JsScope factoryScope = factory.getScope(); |
- JsExpression typeInfo; |
- if (hasTypeParameters(classElement)) { |
- JsName typeinfoParameter = factoryScope.declareName("$rtt"); |
- factory.getParameters().add(0, new JsParameter(typeinfoParameter)); |
- typeInfo = typeinfoParameter.makeRef(); |
- } else { |
- // TODO(johnlenz): this is a constant value, it only needs to be evaluated once. |
- typeInfo = generateRawRTTLookup(classElement); |
- } |
- JsExpression setTypeInfo = assign(null, |
- nameref(null, Cloner.clone(thisRef), "$typeInfo"), typeInfo); |
- factory.getBody().getStatements().add(0, setTypeInfo.makeStmt()); |
- } |
- |
- /** |
- * Add a type arguments parameter to a factory method, if necessary. |
- */ |
- void maybeAddTypeParameterToFactory(DartMethodDefinition method, JsFunction factory) { |
- // TODO(johnlenz):in optimized mode, only add this where it is needed. |
- if (isParameterizedFactoryMethod(method)) { |
- JsScope scope = factory.getScope(); |
- JsName typeArgs = scope.declareName("$typeArgs"); |
- factory.getParameters().add(0, new JsParameter(typeArgs)); |
- } |
- } |
- |
- private boolean isParameterizedFactoryMethod(DartMethodDefinition method) { |
- assert method.getModifiers().isFactory(); |
- Element enclosingElement = method.getSymbol().getEnclosingElement(); |
- if (ElementKind.of(enclosingElement).equals(ElementKind.CLASS)) { |
- ClassElement enclosingClass = (ClassElement) enclosingElement; |
- return !enclosingClass.getTypeParameters().isEmpty(); |
- } |
- return false; |
- } |
- |
- /** |
- * Add a runtime type information to a array literal, if necessary. |
- */ |
- JsExpression maybeAddRuntimeTypeForArrayLiteral(ClassElement enclosingClass, |
- DartArrayLiteral x, JsArrayLiteral jsArray) { |
- // TODO(johnlenz):in optimized mode, only add this where it is needed. |
- InterfaceType instanceType = typeProvider.getArrayLiteralType( |
- x.getType().getArguments().get(0)); |
- JsExpression rtt = generateRTTLookup(instanceType, enclosingClass); |
- // Bind the runtime type information to the native array expression |
- return call(x, newQualifiedNameRef("RTT.setTypeInfo"), jsArray, rtt); |
- } |
- |
- /** |
- * Add a runtime type information to a map literal, if necessary. |
- */ |
- void maybeAddRuntimeTypeToMapLiteralConstructor(ClassElement enclosingClass, |
- DartMapLiteral x, JsInvocation invoke) { |
- // TODO(johnlenz):in optimized mode, only add this where it is needed. |
- |
- // Fixup the type for map literal type to be the implementing type. |
- List<Type> typeArgs = x.getType().getArguments(); |
- InterfaceType instanceType = typeProvider.getMapLiteralType( |
- typeArgs.get(0), typeArgs.get(1)); |
- JsExpression rtt = generateRTTLookup(instanceType, enclosingClass); |
- invoke.getArguments().add(rtt); |
- } |
- |
- /** |
- * Add runtime type information to a "new" expression, if necessary. |
- */ |
- void mayAddRuntimeTypeToConstrutorOrFactoryCall( |
- ClassElement enclosingClass, DartNewExpression x, JsInvocation invoke) { |
- // TODO(johnlenz):in optimized mode, only add this where it is needed. |
- |
- InterfaceType instanceType = Types.constructorType(x); |
- if (instanceType == null) { |
- // TODO(johnlenz): HackHack. Currently the "new FallThroughError" injected by the |
- // Normalizer does not have the instance type attached. But in this |
- // case we know it does not have any type parameters. |
- // reportError(x.getParent().getParent(), new AssertionError("missing type information")); |
- assert typeProvider.getFallThroughError().getElement().lookupConstructor("").equals( |
- x.getSymbol()); |
- } else if (constructorHasTypeParameters(x)) { |
- ConstructorElement constructor = x.getSymbol(); |
- ClassElement containingClassElement = enclosingClass; |
- if (constructor.getModifiers().isFactory()) { |
- // We are calling a factory, this is either in a class |
- FunctionType functionType = (FunctionType) constructor.getType(); |
- JsExpression typeArgs = generateTypeArgsArrayForFactory( |
- functionType, instanceType, containingClassElement); |
- assert typeArgs != null; |
- invoke.getArguments().add(0, typeArgs); |
- } else { |
- ClassElement constructorClassElement = constructor.getConstructorType(); |
- invoke.getArguments().add(0, |
- generateRTTLookup(constructorClassElement, instanceType, containingClassElement)); |
- } |
- } |
- } |
- |
- private boolean constructorHasTypeParameters(DartNewExpression x) { |
- ConstructorElement element = x.getSymbol(); |
- if (element.getModifiers().isFactory()) { |
- return isParameterizedFactoryMethod((DartMethodDefinition)element.getNode()); |
- } else { |
- InterfaceType instanceType = Types.constructorType(x); |
- return hasTypeParameters(instanceType.getElement()); |
- } |
- } |
- |
- /** |
- * @return an expression that implements a runtime "is" operation |
- */ |
- JsExpression generateInstanceOfComparison( |
- ClassElement enclosingClass, |
- JsExpression lhs, |
- DartTypeNode typeNode, |
- SourceInfo src) { |
- ClassElement currentClass = enclosingClass; |
- Type type = typeNode.getType(); |
- switch (TypeKind.of(type)) { |
- case INTERFACE: |
- InterfaceType interfaceType = (InterfaceType) type; |
- if (hasTypeParameters(interfaceType.getElement()) |
- && !interfaceType.hasDynamicTypeArgs()) { |
- return generateRefiedInterfaceTypeComparison(lhs, interfaceType, currentClass, src); |
- } else { |
- // TODO(johnlenz): special case "is Object"? |
- return generateRawInterfaceTypeComparison(lhs, typeNode, src); |
- } |
- case VARIABLE: |
- TypeVariable typeVar = (TypeVariable) type; |
- return generateRefiedTypeVariableComparison(lhs, typeVar, currentClass, src); |
- case DYNAMIC: |
- JsProgram program = translationContext.getProgram(); |
- return program.getTrueLiteral(); |
- case FUNCTION_ALIAS: |
- FunctionAliasType aliasType = (FunctionAliasType) type; |
- return generateRefiedInterfaceTypeComparison(lhs, aliasType, currentClass, src); |
- default: |
- throw new IllegalStateException("unexpected"); |
- } |
- } |
- |
- private JsExpression generateRefiedInterfaceTypeComparison( |
- JsExpression lhs, InterfaceType type, ClassElement contextClassElement, SourceInfo src) { |
- JsExpression rtt = generateRTTLookup(type, contextClassElement); |
- return call(src, nameref(src, rtt, "implementedBy"), lhs); |
- } |
- |
- private JsExpression getReifiedTypeVariableRTT(TypeVariable type, ClassElement contextClassElement) { |
- JsExpression rttContext; |
- // this.typeinfo.implementedTypes['class'].typeArgs; |
- // or for a factory method: |
- // $typeArgs |
- rttContext = buildTypeArgsReference(contextClassElement); |
- |
- // rtt = rttContext.typeArgs[x] |
- JsExpression rtt = buildTypeLookupExpression(type, contextClassElement.getTypeParameters(), rttContext); |
- return rtt; |
- } |
- |
- private JsExpression generateRefiedTypeVariableComparison( |
- JsExpression lhs, TypeVariable type, ClassElement contextClassElement, SourceInfo src) { |
- JsExpression rtt = getReifiedTypeVariableRTT(type, contextClassElement); |
- return call(src, nameref(src, rtt, "implementedBy"), lhs); |
- } |
- |
- private JsExpression generateRawInterfaceTypeComparison( |
- JsExpression lhs, DartTypeNode typeNode, |
- SourceInfo src) { |
- ClassElement element = (ClassElement) typeNode.getType().getElement(); |
- if (element.equals(typeProvider.getObjectType().getElement())) { |
- // Everything is an object, including null |
- return this.translationContext.getProgram().getTrueLiteral(); |
- } |
- String builtin = builtInTypeChecks.get(element); |
- if (builtin != null) { |
- return call(src, nameref(src, builtin), lhs); |
- } |
- |
- // Due to implementing implied interfaces of classes, we always have to |
- // use $implements$ rather than using JS instanceof for classes. |
- |
- // Inject: !!(tmp = target, tmp && tmp.$implements$type) |
- JsProgram program = translationContext.getProgram(); |
- JsName tmp = context.createTemporary(); |
- String mangledClass = translationContext.getMangler().mangleClassName(element); |
- return not(src, |
- not(src, |
- comma(src, |
- assign(src, tmp.makeRef(), lhs), |
- and(src, |
- neq(src, tmp.makeRef().setSourceRef(src), program.getNullLiteral()), |
- nameref(src, |
- tmp, |
- "$implements$" + mangledClass))))); |
- } |
- |
- private boolean inFactory() { |
- DartClassMember<?> member = context.getCurrentClassMember(); |
- return member != null && member.getModifiers().isFactory(); |
- } |
- |
- private boolean inStaticNotFactory(ClassElement containingClass) { |
- return !inFactory() && inStatic(containingClass); |
- } |
- |
- private boolean inStatic(ClassElement containingClass) { |
- DartClassMember<?> member = context.getCurrentClassMember(); |
- return containingClass == null |
- || containingClass.getKind() != ElementKind.CLASS |
- || member == null |
- || member.getModifiers().isStatic(); |
- } |
- |
- |
- /** |
- * Optionally emit a runtime type check, which is a call to $chk passing the |
- * runtime type object for the required type and an expression. |
- * |
- * @param enclosingClass enclosing class element |
- * @param expr expression to check |
- * @param type {@link Type} expected type to check against, null is unknown |
- * @param exprType {@link Type} statically determined type from analyzing expr |
- * @param src source info to use for the generated code |
- * @return an expression wrapping the type check call |
- */ |
- JsExpression addTypeCheck(ClassElement enclosingClass, JsExpression expr, Type type, |
- Type exprType, SourceInfo src) { |
- if (!emitTypeChecks || isStaticallyGoodAssignment(type, exprType)) { |
- return expr; |
- } |
- if (isStaticallyBadAssignment(type, exprType, expr)) { |
- return injectTypeError(enclosingClass, expr, type, src); |
- } |
- return injectTypeCheck(enclosingClass, expr, type, src); |
- } |
- |
- /** |
- * Check if an assignment is statically known to have a type error. |
- * |
- * @param type type of assignment target |
- * @param exprType type of value being assigned |
- * @param expr |
- * @return true if the assignment is known to be bad statically |
- */ |
- private boolean isStaticallyBadAssignment(Type type, Type exprType, JsExpression expr) { |
- if (!expr.isDefinitelyNotNull() || type == null || exprType == null |
- || type.getKind() == TypeKind.DYNAMIC || exprType.getKind() == TypeKind.DYNAMIC) { |
- // nulls can be assigned to any type, and if either is dynamic we can't |
- // reject it |
- return false; |
- } |
- return !types.isAssignable(type, exprType); |
- } |
- |
- /** |
- * @param type |
- * @param exprType |
- * @return true if the assignment is statically known to not need a check |
- */ |
- private boolean isStaticallyGoodAssignment(Type type, Type exprType) { |
- if (type == null || type.getKind() == TypeKind.DYNAMIC) { |
- // if there is no target type or it is dynamic, it is good |
- return true; |
- } |
- if (exprType == null || exprType.getKind() == TypeKind.DYNAMIC) { |
- // otherwise, if the source type is unknown or dynamic, we need a check |
- return false; |
- } |
- return types.isAssignable(type, exprType); |
- } |
- |
- /** |
- * Optionally emit a type error, used when it is statically known that a |
- * TypeError must be thrown. |
- * |
- * @param enclosingClass enclosing class element |
- * @param expr expression that fails the check |
- * @param type type to check against, null is unknown |
- * @param src source info to use for the generated code |
- * @return an expression wrapping the call to the throw helper |
- */ |
- private JsExpression injectTypeError(ClassElement enclosingClass, JsExpression expr, Type type, |
- SourceInfo src) { |
- JsExpression rtt = getRtt(enclosingClass, expr, type); |
- if (rtt != null) { |
- expr = call(src, nameref(src, "$te"), rtt, expr); |
- } |
- return expr; |
- } |
- |
- /** |
- * Optionally emit a runtime type check, which is a call to $chk passing the |
- * runtime type object for the required type and an expression. |
- * |
- * @param enclosingClass enclosing class element |
- * @param expr expression to check |
- * @param type type to check against, null is unknown |
- * @param src source info to use for the generated code |
- * @return an expression wrapping the type check call |
- */ |
- private JsExpression injectTypeCheck(ClassElement enclosingClass, JsExpression expr, Type type, |
- SourceInfo src) { |
- JsExpression rtt = getRtt(enclosingClass, expr, type); |
- if (rtt != null) { |
- expr = call(src, nameref(src, "$chk"), rtt, expr); |
- } |
- return expr; |
- } |
- |
- /** |
- * Get the runtime type object for a type. |
- * |
- * @param enclosingClass |
- * @param expr |
- * @param type |
- * @return a {@link JsExpression} for the runtime type, or null if no check is required/possible |
- */ |
- private JsExpression getRtt(ClassElement enclosingClass, JsExpression expr, Type type) { |
- if (!emitTypeChecks || type == null) { |
- return null; |
- } |
- JsExpression rtt; |
- switch (TypeKind.of(type)) { |
- case INTERFACE: |
- InterfaceType interfaceType = (InterfaceType) type; |
- // TODO: do we need a special case for raw interfaces? |
- return generateRTTLookup(interfaceType, enclosingClass); |
- case VARIABLE: |
- TypeVariable typeVar = (TypeVariable) type; |
- return getReifiedTypeVariableRTT(typeVar, enclosingClass); |
- case FUNCTION: |
- // TODO: We need a more detailed RTT than just a generic function. |
- return generateRTTLookup(typeProvider.getFunctionType(), enclosingClass); |
- case VOID: |
- case FUNCTION_ALIAS: |
- // TODO: implement, no checks for now |
- case DYNAMIC: |
- // no check required |
- return null; |
- default: |
- throw new IllegalStateException("unexpected type " + type); |
- } |
- } |
-} |