Index: pkg/dev_compiler/lib/src/compiler/code_generator.dart |
diff --git a/pkg/dev_compiler/lib/src/compiler/code_generator.dart b/pkg/dev_compiler/lib/src/compiler/code_generator.dart |
index b7f3d5a048c61f3eb83779eb8979cf64a025c04e..361f5ec0198127d03ab3f0f684ebeaa356228e5d 100644 |
--- a/pkg/dev_compiler/lib/src/compiler/code_generator.dart |
+++ b/pkg/dev_compiler/lib/src/compiler/code_generator.dart |
@@ -308,7 +308,8 @@ class CodeGenerator extends GeneralizingAstVisitor |
} |
List<String> _getJSName(Element e) { |
- if (findAnnotation(e.library, isPublicJSAnnotation) == null) { |
+ if (e.library == null || |
+ findAnnotation(e.library, isPublicJSAnnotation) == null) { |
return null; |
} |
@@ -322,6 +323,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
if (findAnnotation(e, isPublicJSAnnotation) != null) { |
elementJSName = getAnnotationName(e, isPublicJSAnnotation) ?? ''; |
} |
+ |
if (e is TopLevelVariableElement && |
e.getter != null && |
(e.getter.isExternal || |
@@ -2705,11 +2707,8 @@ class CodeGenerator extends GeneralizingAstVisitor |
var type = element.enclosingElement.type; |
var member = _emitMemberName(name, isStatic: isStatic, type: type); |
- // For static methods, we add the raw type name, without generics or |
- // library prefix. We don't need those because static calls can't use |
- // the generic type. |
if (isStatic) { |
- var dynType = _emitType(fillDynamicTypeArgs(type)); |
+ var dynType = _emitStaticAccess(type); |
return new JS.PropertyAccess(dynType, member); |
} |
@@ -2908,6 +2907,40 @@ class CodeGenerator extends GeneralizingAstVisitor |
return typeParts; |
} |
+ /// Emits an expression that lets you access statics on a [type] from code. |
+ /// |
+ /// If [nameType] is true, then the type will be named. In addition, |
+ /// if [hoistType] is true, then the named type will be hoisted. |
+ JS.Expression _emitConstructorAccess(DartType type, |
+ {bool nameType: true, bool hoistType: true}) { |
+ return _emitJSInterop(type.element) ?? |
+ _emitType(type, nameType: nameType, hoistType: hoistType); |
+ } |
+ |
+ /// Emits an expression that lets you access statics on a [type] from code. |
+ JS.Expression _emitStaticAccess(DartType type) { |
+ // Make sure we aren't attempting to emit a static access path to a type |
+ // that does not have a valid static access path. |
+ assert(!type.isVoid && |
+ !type.isDynamic && |
+ !type.isBottom && |
+ type is! TypeParameterType); |
+ |
+ // For statics, we add the raw type name, without generics or |
+ // library prefix. We don't need those because static calls can't use |
+ // the generic type. |
+ type = fillDynamicTypeArgs(type); |
+ var element = type.element; |
+ _declareBeforeUse(element); |
+ |
+ var interop = _emitJSInterop(element); |
+ if (interop != null) return interop; |
+ |
+ assert(type.name != '' && type.name != null); |
+ |
+ return _emitTopLevelNameNoInterop(element); |
+ } |
+ |
/// Emits a Dart [type] into code. |
/// |
/// If [lowerTypedef] is set, a typedef will be expanded as if it were a |
@@ -2937,13 +2970,37 @@ class CodeGenerator extends GeneralizingAstVisitor |
return _callHelper('bottom'); |
} |
- _declareBeforeUse(type.element); |
+ var element = type.element; |
+ _declareBeforeUse(element); |
+ |
+ var interop = _emitJSInterop(element); |
+ // Type parameters don't matter as JS interop types cannot be reified. |
+ // We have to use lazy JS types because until we have proper module |
+ // loading for JS libraries bundled with Dart libraries, we will sometimes |
+ // need to load Dart libraries before the corresponding JS libraries are |
+ // actually loaded. |
+ // Given a JS type such as: |
+ // @JS('google.maps.Location') |
+ // class Location { ... } |
+ // We can't emit a reference to MyType because the JS library that defines |
+ // it may be loaded after our code. So for now, we use a special lazy type |
+ // object to represent MyType. |
+ // Anonymous JS types do not have a corresponding concrete JS type so we |
+ // have to use a helper to define them. |
+ if (interop != null) { |
+ if (_isObjectLiteral(element)) { |
+ return _callHelper( |
+ 'lazyAnonymousJSType(#)', js.string(element.displayName)); |
+ } else { |
+ return _callHelper('lazyJSType(() => #, #)', |
+ [interop, js.string(_getJSName(element).join('.'))]); |
+ } |
+ } |
// TODO(jmesserly): like constants, should we hoist function types out of |
// methods? Similar issue with generic types. For all of these, we may want |
// to canonicalize them too, at least when inside the same library. |
var name = type.name; |
- var element = type.element; |
if (name == '' || name == null || lowerTypedef) { |
// TODO(jmesserly): should we change how typedefs work? They currently |
// go through use similar logic as generic classes. This makes them |
@@ -2957,9 +3014,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
return new JS.Identifier(name); |
} |
- if (type == subClass?.type) { |
- return className; |
- } |
+ if (type == subClass?.type) return className; |
if (type is ParameterizedType) { |
var args = type.typeArguments; |
@@ -2982,12 +3037,16 @@ class CodeGenerator extends GeneralizingAstVisitor |
} |
} |
- return _emitTopLevelName(element); |
+ return _emitTopLevelNameNoInterop(element); |
} |
JS.PropertyAccess _emitTopLevelName(Element e, {String suffix: ''}) { |
var interop = _emitJSInterop(e); |
if (interop != null) return interop; |
+ return _emitTopLevelNameNoInterop(e, suffix: suffix); |
+ } |
+ |
+ JS.PropertyAccess _emitTopLevelNameNoInterop(Element e, {String suffix: ''}) { |
String name = getJSExportName(e) + suffix; |
return new JS.PropertyAccess( |
emitLibraryName(e.library), _propertyName(name)); |
@@ -3211,7 +3270,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
// the generic type. |
ClassElement classElement = element.enclosingElement; |
var type = classElement.type; |
- var dynType = _emitType(fillDynamicTypeArgs(type)); |
+ var dynType = _emitStaticAccess(type); |
var member = _emitMemberName(element.name, isStatic: true, type: type); |
return _visit(rhs).toAssignExpression( |
annotate(new JS.PropertyAccess(dynType, member), lhs)); |
@@ -3362,6 +3421,19 @@ class CodeGenerator extends GeneralizingAstVisitor |
return helperMethodName; |
} |
+ JS.Expression _emitTarget(Expression target, Element element, bool isStatic) { |
+ if (isStatic) { |
+ if (element is ConstructorElement) { |
+ return _emitConstructorAccess(element.enclosingElement.type); |
+ } |
+ if (element is ExecutableElement) { |
+ return _emitStaticAccess( |
+ (element.enclosingElement as ClassElement).type); |
+ } |
+ } |
+ return _visit(target); |
+ } |
+ |
/// Emits a (possibly generic) instance method call. |
JS.Expression _emitMethodCallInternal( |
Expression target, |
@@ -3374,7 +3446,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
bool isStatic = element is ExecutableElement && element.isStatic; |
var memberName = _emitMemberName(name, type: type, isStatic: isStatic); |
- JS.Expression jsTarget = _visit(target); |
+ JS.Expression jsTarget = _emitTarget(target, element, isStatic); |
if (isDynamicInvoke(target) || isDynamicInvoke(node.methodName)) { |
if (_inWhitelistCode(target)) { |
var vars = <JS.MetaLetVariable, JS.Expression>{}; |
@@ -3899,7 +3971,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
var classElem = element.enclosingElement; |
var interop = _emitJSInterop(classElem); |
if (interop != null) return interop; |
- var typeName = _emitType(type); |
+ var typeName = _emitConstructorAccess(type); |
if (name != null || element.isFactory) { |
var namedCtor = _constructorName(element); |
return new JS.PropertyAccess(typeName, namedCtor); |
@@ -3925,7 +3997,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
if (element == null) { |
// TODO(jmesserly): this only happens if we had a static error. |
// Should we generate a throw instead? |
- ctor = _emitType(type, |
+ ctor = _emitConstructorAccess(type, |
nameType: options.hoistInstanceCreation, |
hoistType: options.hoistInstanceCreation); |
if (name != null) { |
@@ -4749,7 +4821,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
[_emitDynamicOperationName('dload'), _visit(target), name]); |
} |
- var jsTarget = _visit(target); |
+ var jsTarget = _emitTarget(target, member, isStatic); |
bool isSuper = jsTarget is JS.Super; |
if (isSuper && member is FieldElement && !member.isSynthetic) { |
@@ -5123,7 +5195,8 @@ class CodeGenerator extends GeneralizingAstVisitor |
// TODO(vsm): When we canonicalize, we need to treat private symbols |
// correctly. |
var name = js.string(node.components.join('.'), "'"); |
- return js.call('#.new(#)', [_emitType(types.symbolType), name]); |
+ return js |
+ .call('#.new(#)', [_emitConstructorAccess(types.symbolType), name]); |
} |
return _emitConst(emitSymbol); |