Chromium Code Reviews| 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 |
| index 44ae3c5c99322b3cd85fc5fd95fda8417479ab7c..e6cf9977c438fc09459efd6cf6fb8916209ff3ba 100644 |
| --- a/compiler/java/com/google/dart/compiler/backend/js/RuntimeTypeInjector.java |
| +++ b/compiler/java/com/google/dart/compiler/backend/js/RuntimeTypeInjector.java |
| @@ -46,9 +46,12 @@ 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; |
| @@ -66,16 +69,27 @@ 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.Elements; |
| +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; |
| @@ -96,11 +110,15 @@ public class RuntimeTypeInjector { |
| private CoreTypeProvider typeProvider; |
| private final TranslationContext translationContext; |
| private final Types types; |
| + private final DartMangler mangler; |
| + private final LibraryElement unitLibrary; |
| RuntimeTypeInjector( |
| TraversalContextProvider context, |
| CoreTypeProvider typeProvider, |
| - TranslationContext translationContext, boolean emitTypeChecks) { |
| + TranslationContext translationContext, |
| + boolean emitTypeChecks, DartMangler mangler, |
| + LibraryElement unitLibrary) { |
| this.context = context; |
| this.translationContext = translationContext; |
| JsProgram program = translationContext.getProgram(); |
| @@ -109,6 +127,8 @@ public class RuntimeTypeInjector { |
| this.builtInTypeChecks = makeBuiltinTypes(typeProvider); |
| this.typeProvider = typeProvider; |
| this.emitTypeChecks = emitTypeChecks; |
| + this.mangler = mangler; |
| + this.unitLibrary = unitLibrary; |
| types = Types.getInstance(typeProvider); |
| } |
| @@ -133,6 +153,29 @@ public class RuntimeTypeInjector { |
| } |
| } |
| + /** |
| + * Generate the code necessary to allow for runtime type checks of dart typedefs |
| + */ |
| + void generateRuntimeTypeInfo(DartFunctionTypeAlias x) { |
| + // 1) create static type information lookup function |
|
zundel
2011/11/29 21:53:46
this comment is kind of useless.
codefu
2011/11/29 22:42:13
Done.
|
| + generateRTTLookupMethod(x); |
| + } |
| + |
| + /** |
| + * Generate the code necessary to allow for runtime type checks of dart class methods |
| + */ |
| + void generateRuntimeTypeInfo(DartMethodDefinition x) { |
| + // 1) create static type information lookup function |
|
zundel
2011/11/29 21:53:46
this comment is kind of useless.
codefu
2011/11/29 22:42:13
Done.
|
| + 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); |
| @@ -352,6 +395,12 @@ public class RuntimeTypeInjector { |
| for (InterfaceType interfaceType : classElement.getInterfaces() ) { |
| ClassElement interfaceElement = interfaceType.getElement(); |
| + // Codefu: Random addition to keep 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()) { |
| @@ -373,6 +422,281 @@ public class RuntimeTypeInjector { |
| 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<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)])); |
|
zundel
2011/11/29 21:53:46
there is a common sub-expression that is called tw
codefu
2011/11/29 22:42:13
I can update the example, the index can be differe
|
| + * } |
| + */ |
| + private void generateRTTLookupMethod(MethodElement methodElement, String overrideName) { |
| + boolean hasTypes = false; |
| + |
| + if (methodElement instanceof ConstructorElement |
| + || Elements.isNonFactoryConstructor(methodElement) |
|
zundel
2011/11/29 21:53:46
This will never get here because methodElement ins
codefu
2011/11/29 22:42:13
I believe factory is set if the factory keyword is
|
| + || methodElement.getModifiers().isFactory() || methodElement.getModifiers().isNative()) { |
| + /* No type lookups for factories or constructors */ |
| + return; |
| + } |
| + ClassElement classElement = getEnclosingClassElement(methodElement.getEnclosingElement()); |
| + hasTypes = classElement != null ? hasTypeParameters(classElement) : false; |
| + |
| + JsProgram program = translationContext.getProgram(); |
| + JsExpression typeArgContextExpr = hasTypes ? buildTypeArgsReference(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); |
| + if (arr == null) { |
| + return; // Error, Do not generate a lookup function |
| + } |
| + |
| + 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) { |
| + if (methodElement.getEnclosingElement().getKind().equals(ElementKind.CLASS)) { |
| + JsNameRef classJsNameRef = getJsName(methodElement.getEnclosingElement()).makeRef(); |
| + String getterName = mangler.createGetterSyntax(methodElement, unitLibrary); |
| + String methodName = methodElement.getName(); |
| + JsName getterJsName = globalScope.declareName(getterName, getterName, methodName); |
| + String mangledMethodName = mangler.mangleNamedMethod(methodElement, unitLibrary) |
| + + ".$lookupRTT"; |
| + JsNameRef methodToCall; |
| + JsNameRef getterJsNameRef; |
| + if (methodElement.getModifiers().isStatic()) { |
| + getterJsNameRef = AstUtil.newNameRef(classJsNameRef, getterJsName); |
| + methodToCall = AstUtil.newNameRef(classJsNameRef, mangledMethodName); |
| + } else { |
| + JsNameRef prototypeRef = AstUtil.newPrototypeNameRef(classJsNameRef); |
| + getterJsNameRef = AstUtil.newNameRef(prototypeRef, getterJsName); |
| + methodToCall = AstUtil.newNameRef(prototypeRef, mangledMethodName); |
| + } |
| + fnDecl = assign(null, methodToCall, lookupFn); |
| + } else { |
| + // Top level method |
| + String mangledMethodName = mangler.mangleNamedMethod(methodElement, unitLibrary) |
| + + ".$lookupRTT"; |
| + fnDecl = assign(null, AstUtil.newNameRef(null, mangledMethodName), lookupFn); |
| + } |
| + } else { |
| + // Special care for hoisted functions. |
| + JsNameRef funcName = new JsNameRef(overrideName + "$lookupRTT"); |
| + fnDecl = assign(null, funcName, 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 JsExpression generateRTTCreate(VariableElement element, ClassElement classElement) { |
| + boolean hasTypes = false; |
| + FunctionType type = (FunctionType) element.getType(); |
| + |
| + if (element instanceof ConstructorElement || Elements.isNonFactoryConstructor(element) |
|
zundel
2011/11/29 21:53:46
same comment as above.
|
| + || element.getModifiers().isFactory() || element.getModifiers().isNative()) { |
| + // No type lookups for factories or constructors |
| + return AstUtil.newNameRef(null, "RTT.dynamicType"); |
| + } |
| + 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(type.getParameterTypes(), classElement, |
| + typeArgContextExpr); |
| + if (arr == null) { |
| + return AstUtil.newNameRef(null, "RTT.dynamicType"); |
| + } |
| + |
| + JsExpression returnExpr = generateRTTLookupForType(element, type.getReturnType(), classElement, |
| + typeArgContextExpr); |
| + |
| + callLookup.getArguments().add(arr.getExpressions().isEmpty() ? program.getNullLiteral() : arr); |
| + callLookup.getArguments().add(returnExpr); |
| + return callLookup; |
| + } |
| + |
| + private JsArrayLiteral generateTypeArrayFromTypes(List<? extends Type> parameterTypes, |
| + ClassElement classElement, JsExpression typeArgContextExpr) { |
| + JsArrayLiteral jsTypeArray = new JsArrayLiteral(); |
| + for (Type param : parameterTypes) { |
| + JsExpression elementExpr; |
| + elementExpr = generateRTTLookupForType(param.getElement(), param, classElement, |
| + typeArgContextExpr); |
| + if (elementExpr == null) { |
| + return null; |
| + } |
| + jsTypeArray.getExpressions().add(elementExpr); |
| + } |
| + return jsTypeArray; |
| + } |
| + |
| + private JsArrayLiteral generateTypeArrayFromParameters(List<DartParameter> params, |
| + ClassElement classElement, JsExpression typeArgContextExpr) { |
| + JsArrayLiteral jsTypeArray = new JsArrayLiteral(); |
| + for (DartParameter param : params) { |
| + JsExpression elementExpr; |
| + if (param.getTypeNode() == null) { |
| + elementExpr = AstUtil.newNameRef(null, "RTT.dynamicType"); |
| + } else { |
| + elementExpr = generateRTTLookupForType(param.getSymbol(), param.getTypeNode().getType(), |
| + classElement, typeArgContextExpr); |
| + if (elementExpr == null) { |
|
zundel
2011/11/29 21:53:46
This looks odd - Is this a normal occurrence? Does
codefu
2011/11/29 22:42:13
This is probably an artifact from when generateRTT
|
| + return null; |
| + } |
| + } |
| + jsTypeArray.getExpressions().add(elementExpr); |
| + } |
| + 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); |
| + if (rttTypeExpression == null) { |
| + return null; |
| + } |
| + jsTypeArray.getExpressions().add(rttTypeExpression); |
| + } |
| + return jsTypeArray; |
| + } |
| + |
| + private JsExpression generateRTTLookupForType(Element element, Type elementType, |
| + ClassElement classElement, JsExpression typeArgContextExpr) { |
| + JsExpression elementExpr = null; |
| + switch (TypeKind.of(elementType)) { |
| + case VARIABLE: |
| + elementExpr = buildTypeLookupExpression(elementType, classElement.getTypeParameters(), |
| + typeArgContextExpr); |
| + break; |
| + case INTERFACE: |
| + elementExpr = generateRTTLookup((ClassElement) elementType.getElement(), |
| + (InterfaceType) elementType, classElement); |
| + break; |
| + case FUNCTION: |
| + elementExpr = generateRTTCreate((VariableElement) element, classElement); |
| + break; |
| + case FUNCTION_ALIAS: |
| + elementExpr = buildTypeLookupExpression(elementType, |
| + classElement != null ? classElement.getTypeParameters() : null, typeArgContextExpr); |
| + break; |
| + case DYNAMIC: |
| + elementExpr = AstUtil.newNameRef(null, "RTT.dynamicType"); |
| + break; |
| + case VOID: |
| + case NONE: |
| + elementExpr = translationContext.getProgram().getNullLiteral(); |
| + break; |
| + default: |
| + // Do not generate a lookup method |
| + elementExpr = buildTypeLookupExpression(elementType, classElement.getTypeParameters(), |
| + typeArgContextExpr); |
| + break; |
| + } |
| + return elementExpr; |
| + } |
| + |
| + 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(); |
| + boolean hasTypeParams = hasTypeParameters(classElement); |
| + |
| + // 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); |
| + if (arr == null) { |
| + return; // Error. Do not generate a lookup function |
| + } |
| + callArgs.add(arr.getExpressions().isEmpty() ? program.getNullLiteral() : arr); |
| + |
| + JsExpression returnExpr = generateRTTLookupForType(funcType.getElement(), |
| + funcType.getReturnType(), classElement, typeArgsExpr); |
| + callArgs.add(returnExpr); |
| + |
| + body.add(new JsReturn(invokeCreate)); |
| + |
| + // Finally, Add the function to the global block of statements. |
| + JsExpression fnDecl = assign(null, getRTTLookupMethodName(classElement), lookupFn); |
| + globalBlock.getStatements().add(fnDecl.makeStmt()); |
| + } |
| + |
| static JsExpression getRTTClassId(TranslationContext translationContext, |
| ClassElement classElement) { |
| JsName classJsName = translationContext.getNames().getName(classElement); |
| @@ -510,7 +834,13 @@ public class RuntimeTypeInjector { |
| ((FunctionType)contextElement.getType()).getTypeVariables(), |
| buildFactoryTypeInfoReference()); |
| } else { |
| - typeArgs = buildTypeArgs(instanceType, null, null); |
| + if( contextClassElement instanceof FunctionAliasElementImplementation) { |
|
zundel
2011/11/29 21:53:46
better to use ElementKind.of()
codefu
2011/11/29 22:42:13
Done.
|
| + // 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: |
| @@ -578,6 +908,7 @@ public class RuntimeTypeInjector { |
| Type type, List<? extends Type> list, JsExpression contextTypeArgs) { |
| switch (TypeKind.of(type)) { |
| case INTERFACE: |
| + case FUNCTION_ALIAS: |
| InterfaceType interfaceType = (InterfaceType) type; |
| JsInvocation callLookup = call(null, |
| getRTTLookupMethodName(interfaceType.getElement())); |
| @@ -590,9 +921,18 @@ public class RuntimeTypeInjector { |
| } |
| return callLookup; |
| - case FUNCTION_ALIAS: |
| - // TODO(johnlenz): implement this |
| - return newQualifiedNameRef("RTT.placeholderType"); |
| + case FUNCTION: |
| + FunctionType functionType = (FunctionType) type; |
|
codefu
2011/11/29 22:42:13
Hey hey, check out that tab.
|
| + JsInvocation functionTypeCallLookup = call(null, |
| + getRTTLookupMethodName(functionType.getElement())); |
| + if (hasTypeParameters(functionType.getElement())) { |
| + JsArrayLiteral typeArgs = new JsArrayLiteral(); |
| + for (Type arg : functionType.getTypeVariables()) { |
| + typeArgs.getExpressions().add(buildTypeLookupExpression(arg, list, contextTypeArgs)); |
| + } |
| + functionTypeCallLookup.getArguments().add(typeArgs); |
| + } |
| + return functionTypeCallLookup; |
| case VARIABLE: |
| TypeVariable var = (TypeVariable)type; |
| @@ -748,6 +1088,9 @@ public class RuntimeTypeInjector { |
| 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"); |
| } |