From fbbf1e90ec0d48d0c6eaeffc194ddfbfdb07e8cd Mon Sep 17 00:00:00 2001 From: Johnni Winther Date: Wed, 15 Mar 2017 12:58:55 +0100 Subject: [PATCH] Split js interop registration into library/class/member elements R=sigmund@google.com Review-Url: https://codereview.chromium.org/2731163002 . --- .../src/js_backend/backend_serialization.dart | 36 ++- .../src/js_backend/field_naming_mixin.dart | 2 +- .../lib/src/js_backend/frequency_namer.dart | 2 +- .../src/js_backend/js_interop_analysis.dart | 55 ++-- .../lib/src/js_backend/minify_namer.dart | 2 +- pkg/compiler/lib/src/js_backend/namer.dart | 30 --- .../lib/src/js_backend/native_data.dart | 243 +++++++++++------- .../lib/src/js_emitter/native_emitter.dart | 3 +- .../program_builder/program_builder.dart | 2 +- pkg/compiler/lib/src/ssa/builder.dart | 25 +- pkg/compiler/lib/src/types/types.dart | 9 +- .../serialization/native_data_test.dart | 16 +- 12 files changed, 264 insertions(+), 161 deletions(-) diff --git a/pkg/compiler/lib/src/js_backend/backend_serialization.dart b/pkg/compiler/lib/src/js_backend/backend_serialization.dart index f1ea95b75ad..eacdf1bcdf3 100644 --- a/pkg/compiler/lib/src/js_backend/backend_serialization.dart +++ b/pkg/compiler/lib/src/js_backend/backend_serialization.dart @@ -39,7 +39,9 @@ class JavaScriptBackendSerialization implements BackendSerialization { deserializer = new JavaScriptBackendDeserializer(nativeData); } -const Key JS_INTEROP_NAME = const Key('jsInteropName'); +const Key JS_INTEROP_LIBRARY_NAME = const Key('jsInteropLibraryName'); +const Key JS_INTEROP_CLASS_NAME = const Key('jsInteropClassName'); +const Key JS_INTEROP_MEMBER_NAME = const Key('jsInteropMemberName'); const Key NATIVE_MEMBER_NAME = const Key('nativeMemberName'); const Key NATIVE_CLASS_TAG_INFO = const Key('nativeClassTagInfo'); const Key NATIVE_METHOD_BEHAVIOR = const Key('nativeMethodBehavior'); @@ -58,9 +60,17 @@ class JavaScriptBackendSerializer implements SerializerPlugin { return encoder ??= createEncoder(_BACKEND_DATA_TAG); } - String jsInteropName = nativeData.jsInteropNames[element]; - if (jsInteropName != null) { - getEncoder().setString(JS_INTEROP_NAME, jsInteropName); + String jsInteropLibraryName = nativeData.jsInteropLibraryNames[element]; + if (jsInteropLibraryName != null) { + getEncoder().setString(JS_INTEROP_LIBRARY_NAME, jsInteropLibraryName); + } + String jsInteropClassName = nativeData.jsInteropClassNames[element]; + if (jsInteropClassName != null) { + getEncoder().setString(JS_INTEROP_CLASS_NAME, jsInteropClassName); + } + String jsInteropMemberName = nativeData.jsInteropMemberNames[element]; + if (jsInteropMemberName != null) { + getEncoder().setString(JS_INTEROP_MEMBER_NAME, jsInteropMemberName); } String nativeMemberName = nativeData.nativeMemberName[element]; if (nativeMemberName != null) { @@ -107,10 +117,20 @@ class JavaScriptBackendDeserializer implements DeserializerPlugin { void onElement(Element element, ObjectDecoder getDecoder(String tag)) { ObjectDecoder decoder = getDecoder(_BACKEND_DATA_TAG); if (decoder != null) { - String jsInteropName = - decoder.getString(JS_INTEROP_NAME, isOptional: true); - if (jsInteropName != null) { - nativeData.jsInteropNames[element] = jsInteropName; + String jsInteropLibraryName = + decoder.getString(JS_INTEROP_LIBRARY_NAME, isOptional: true); + if (jsInteropLibraryName != null) { + nativeData.jsInteropLibraryNames[element] = jsInteropLibraryName; + } + String jsInteropClassName = + decoder.getString(JS_INTEROP_CLASS_NAME, isOptional: true); + if (jsInteropClassName != null) { + nativeData.jsInteropClassNames[element] = jsInteropClassName; + } + String jsInteropMemberName = + decoder.getString(JS_INTEROP_MEMBER_NAME, isOptional: true); + if (jsInteropMemberName != null) { + nativeData.jsInteropMemberNames[element] = jsInteropMemberName; } String nativeMemberName = decoder.getString(NATIVE_MEMBER_NAME, isOptional: true); diff --git a/pkg/compiler/lib/src/js_backend/field_naming_mixin.dart b/pkg/compiler/lib/src/js_backend/field_naming_mixin.dart index 04a9fdab3b1..275f25edd42 100644 --- a/pkg/compiler/lib/src/js_backend/field_naming_mixin.dart +++ b/pkg/compiler/lib/src/js_backend/field_naming_mixin.dart @@ -13,7 +13,7 @@ abstract class _MinifiedFieldNamer implements Namer { // The inheritance scope based naming might not yield a name. For instance, // this could be because the field belongs to a mixin. In such a case this // will return `null` and a normal field name has to be used. - jsAst.Name _minifiedInstanceFieldPropertyName(Element element) { + jsAst.Name _minifiedInstanceFieldPropertyName(FieldElement element) { if (_nativeData.hasFixedBackendName(element)) { return new StringBackedName(_nativeData.getFixedBackendName(element)); } diff --git a/pkg/compiler/lib/src/js_backend/frequency_namer.dart b/pkg/compiler/lib/src/js_backend/frequency_namer.dart index 651c99f1f00..7ea25d6270a 100644 --- a/pkg/compiler/lib/src/js_backend/frequency_namer.dart +++ b/pkg/compiler/lib/src/js_backend/frequency_namer.dart @@ -64,7 +64,7 @@ class FrequencyBasedNamer extends Namer } @override - jsAst.Name instanceFieldPropertyName(Element element) { + jsAst.Name instanceFieldPropertyName(FieldElement element) { jsAst.Name proposed = _minifiedInstanceFieldPropertyName(element); if (proposed != null) { return proposed; diff --git a/pkg/compiler/lib/src/js_backend/js_interop_analysis.dart b/pkg/compiler/lib/src/js_backend/js_interop_analysis.dart index 1b4aeb9cef0..d760c7c2c07 100644 --- a/pkg/compiler/lib/src/js_backend/js_interop_analysis.dart +++ b/pkg/compiler/lib/src/js_backend/js_interop_analysis.dart @@ -16,9 +16,9 @@ import '../elements/elements.dart' ClassElement, Element, FieldElement, - FunctionElement, LibraryElement, ParameterElement, + MemberElement, MethodElement, MetadataAnnotation; import '../js/js.dart' as jsAst; @@ -57,8 +57,10 @@ class JsInteropAnalysis { _inCodegen = true; } - void processJsInteropAnnotation(Element e) { - for (MetadataAnnotation annotation in e.implementation.metadata) { + /// Resolves the metadata of [element] and returns the name of the `JS(...)` + /// annotation for js interop, if found. + String processJsInteropAnnotation(Element element) { + for (MetadataAnnotation annotation in element.implementation.metadata) { // TODO(johnniwinther): Avoid processing unresolved elements. if (annotation.constant == null) continue; ConstantValue constant = @@ -67,18 +69,19 @@ class JsInteropAnalysis { ConstructedConstantValue constructedConstant = constant; if (constructedConstant.type.element == helpers.jsAnnotationClass) { ConstantValue value = constructedConstant.fields[nameField]; + String name; if (value.isString) { StringConstantValue stringValue = value; - backend.nativeDataBuilder - .setJsInteropName(e, stringValue.primitiveValue.slowToString()); + name = stringValue.primitiveValue.slowToString(); } else { // TODO(jacobr): report a warning if the value is not a String. - backend.nativeDataBuilder.setJsInteropName(e, ''); + name = ''; } enabledJsInterop = true; - return; + return name; } } + return null; } bool hasAnonymousAnnotation(Element element) { @@ -94,7 +97,7 @@ class JsInteropAnalysis { }); } - void _checkFunctionParameters(FunctionElement fn) { + void _checkFunctionParameters(MethodElement fn) { if (fn.hasFunctionSignature && fn.functionSignature.optionalParametersAreNamed) { backend.reporter.reportErrorMessage( @@ -105,17 +108,31 @@ class JsInteropAnalysis { } void processJsInteropAnnotationsInLibrary(LibraryElement library) { - processJsInteropAnnotation(library); + String libraryName = processJsInteropAnnotation(library); + if (libraryName != null) { + backend.nativeDataBuilder.setJsInteropLibraryName(library, libraryName); + } library.implementation.forEachLocalMember((Element element) { - processJsInteropAnnotation(element); - if (element is MethodElement) { - if (!backend.nativeData.isJsInteropMember(element)) return; - _checkFunctionParameters(element); + if (element is MemberElement) { + String memberName = processJsInteropAnnotation(element); + if (memberName != null) { + backend.nativeDataBuilder.setJsInteropMemberName(element, memberName); + } + + if (element is MethodElement) { + if (!backend.nativeData.isJsInteropMember(element)) return; + _checkFunctionParameters(element); + } } if (!element.isClass) return; ClassElement classElement = element; + String className = processJsInteropAnnotation(classElement); + if (className != null) { + backend.nativeDataBuilder + .setJsInteropClassName(classElement, className); + } if (!backend.nativeData.isJsInteropClass(classElement)) return; // Skip classes that are completely unreachable. This should only happen @@ -133,13 +150,17 @@ class JsInteropAnalysis { }); } - classElement.forEachMember((ClassElement classElement, Element member) { - processJsInteropAnnotation(member); + classElement + .forEachMember((ClassElement classElement, MemberElement member) { + String memberName = processJsInteropAnnotation(member); + if (memberName != null) { + backend.nativeDataBuilder.setJsInteropMemberName(element, memberName); + } if (!member.isSynthesized && backend.nativeData.isJsInteropClass(classElement) && - member is FunctionElement) { - FunctionElement fn = member; + member is MethodElement) { + MethodElement fn = member; if (!fn.isExternal && !fn.isAbstract && !fn.isConstructor && diff --git a/pkg/compiler/lib/src/js_backend/minify_namer.dart b/pkg/compiler/lib/src/js_backend/minify_namer.dart index ebf5691dc69..198f7cda9d5 100644 --- a/pkg/compiler/lib/src/js_backend/minify_namer.dart +++ b/pkg/compiler/lib/src/js_backend/minify_namer.dart @@ -292,7 +292,7 @@ class MinifyNamer extends Namer } @override - jsAst.Name instanceFieldPropertyName(Element element) { + jsAst.Name instanceFieldPropertyName(FieldElement element) { jsAst.Name proposed = _minifiedInstanceFieldPropertyName(element); if (proposed != null) { return proposed; diff --git a/pkg/compiler/lib/src/js_backend/namer.dart b/pkg/compiler/lib/src/js_backend/namer.dart index 9c7e0dae357..8f74dba7d1b 100644 --- a/pkg/compiler/lib/src/js_backend/namer.dart +++ b/pkg/compiler/lib/src/js_backend/namer.dart @@ -778,36 +778,6 @@ class Namer { return invocationName(new Selector.fromElement(method)); } - String _jsNameHelper(Element e) { - String jsInteropName = _nativeData.getJsInteropName(e); - if (jsInteropName != null && jsInteropName.isNotEmpty) return jsInteropName; - return e.isLibrary ? 'self' : _nativeData.getUnescapedJSInteropName(e.name); - } - - /// Returns a JavaScript path specifying the context in which - /// [element.fixedBackendName] should be evaluated. Only applicable for - /// elements using typed JavaScript interop. - /// For example: fixedBackendPath for the static method createMap in the - /// Map class of the goog.map JavaScript library would have path - /// "goog.maps.Map". - String fixedBackendMethodPath(MethodElement element) { - return _fixedBackendPath(element); - } - - String _fixedBackendPath(Element element) { - if (!_nativeData.isJsInterop(element)) return null; - if (element.isInstanceMember) return 'this'; - if (element.isConstructor) return _fixedBackendPath(element.enclosingClass); - if (element.isLibrary) return 'self'; - var sb = new StringBuffer(); - sb..write(_jsNameHelper(element.library)); - - if (element.enclosingClass != null && element.enclosingClass != element) { - sb..write('.')..write(_jsNameHelper(element.enclosingClass)); - } - return sb.toString(); - } - /// Returns the annotated name for a variant of `call`. /// The result has the form: /// diff --git a/pkg/compiler/lib/src/js_backend/native_data.dart b/pkg/compiler/lib/src/js_backend/native_data.dart index 0d6b5ea5d38..8401fb7e1c5 100644 --- a/pkg/compiler/lib/src/js_backend/native_data.dart +++ b/pkg/compiler/lib/src/js_backend/native_data.dart @@ -10,7 +10,6 @@ import '../elements/elements.dart' ClassElement, Element, FieldElement, - FunctionElement, LibraryElement, MemberElement, MethodElement; @@ -63,11 +62,17 @@ abstract class NativeData extends NativeClassData { /// Returns `true` if the name of [element] is fixed for the generated /// JavaScript. - bool hasFixedBackendName(Element element); + bool hasFixedBackendName(MemberElement element); /// Computes the name for [element] to use in the generated JavaScript. This /// is either given through a native annotation or a js interop annotation. - String getFixedBackendName(Entity entity); + String getFixedBackendName(MemberEntity element); + + /// Computes the name prefix for [element] to use in the generated JavaScript. + /// + /// For static and top-level members and constructors this is based on the + /// JavaScript names for the library and/or the enclosing class. + String getFixedBackendMethodPath(MethodElement element); /// Returns the list of non-directive native tag words for [cls]. List getNativeTagsOfClass(ClassElement cls); @@ -75,22 +80,21 @@ abstract class NativeData extends NativeClassData { /// Returns `true` if [cls] has a `!nonleaf` tag word. bool hasNativeTagsForcedNonLeaf(ClassElement cls); - /// Returns `true` if [element] is part of JsInterop. - /// - /// Deprecated: Use [isJsInteropLibrary], [isJsInteropClass] or - /// [isJsInteropMember] instead. - @deprecated - bool isJsInterop(Element element); - /// Returns `true` if [element] is a JsInterop method. - bool isJsInteropMember(MethodElement element); + bool isJsInteropMember(MemberEntity element); - /// Returns the explicit js interop name for [element]. - String getJsInteropName(Element element); + /// Returns the explicit js interop name for library [element]. + String getJsInteropLibraryName(LibraryElement element); + + /// Returns the explicit js interop name for class [element]. + String getJsInteropClassName(ClassElement element); + + /// Returns the explicit js interop name for member [element]. + String getJsInteropMemberName(MemberElement element); /// Apply JS$ escaping scheme to convert possible escaped Dart names into /// JS names. - String getUnescapedJSInteropName(String name); + String computeUnescapedJSInteropName(String name); } abstract class NativeClassDataBuilder { @@ -131,15 +135,24 @@ abstract class NativeDataBuilder { /// [element] in the generated JavaScript. void setNativeMemberName(MemberElement element, String name); - /// Sets the explicit js interop [name] for [element]. - void setJsInteropName(Element element, String name); + /// Sets the explicit js interop [name] for the library [element]. + void setJsInteropLibraryName(LibraryElement element, String name); + + /// Sets the explicit js interop [name] for the class [element]. + void setJsInteropClassName(ClassElement element, String name); + + /// Sets the explicit js interop [name] for the member [element]. + void setJsInteropMemberName(MemberElement element, String name); } class NativeDataImpl implements NativeData, NativeDataBuilder, NativeClassDataBuilder { /// The JavaScript names for elements implemented via typed JavaScript /// interop. - Map jsInteropNames = {}; + Map jsInteropLibraryNames = + {}; + Map jsInteropClassNames = {}; + Map jsInteropMemberNames = {}; /// The JavaScript names for native JavaScript elements implemented. Map nativeMemberName = {}; @@ -165,125 +178,183 @@ class NativeDataImpl static const String _jsInteropEscapePrefix = r'JS$'; /// Returns `true` if [element] is explicitly marked as part of JsInterop. - bool _isJsInterop(Element element) { - return jsInteropNames.containsKey(element.declaration); + bool _isJsInteropLibrary(LibraryElement element) { + return jsInteropLibraryNames.containsKey(element); } - /// Marks [element] as an explicit part of JsInterop. The js interop name is - /// expected to be computed later. - void markAsJsInterop(Element element) { - jsInteropNames[element.declaration] = null; + /// Returns `true` if [element] is explicitly marked as part of JsInterop. + bool _isJsInteropClass(ClassElement element) { + return jsInteropClassNames.containsKey(element); + } + + /// Returns `true` if [element] is explicitly marked as part of JsInterop. + bool _isJsInteropMember(MemberElement element) { + return jsInteropMemberNames.containsKey(element); } @override void markAsJsInteropLibrary(LibraryElement element) { - markAsJsInterop(element); + jsInteropLibraryNames[element] = null; } @override void markAsJsInteropClass(ClassElement element) { - markAsJsInterop(element); + jsInteropClassNames[element] = null; } @override void markAsJsInteropMember(MemberElement element) { - markAsJsInterop(element); + jsInteropMemberNames[element] = null; } - /// Sets the explicit js interop [name] for [element]. - void setJsInteropName(Element element, String name) { - assert(invariant(element, isJsInterop(element), + /// Sets the explicit js interop [name] for the library [element]. + void setJsInteropLibraryName(LibraryElement element, String name) { + assert(invariant(element, _isJsInteropLibrary(element), message: - 'Element $element is not js interop but given a js interop name.')); - jsInteropNames[element.declaration] = name; + 'Library $element is not js interop but given a js interop name.')); + jsInteropLibraryNames[element] = name; } - /// Returns the explicit js interop name for [element]. - String getJsInteropName(Element element) { - return jsInteropNames[element.declaration]; + /// Sets the explicit js interop [name] for the class [element]. + void setJsInteropClassName(ClassElement element, String name) { + assert(invariant(element, _isJsInteropClass(element), + message: + 'Class $element is not js interop but given a js interop name.')); + jsInteropClassNames[element] = name; } - /// Returns `true` if [element] is part of JsInterop. - bool isJsInterop(Element element) { - // An function is part of JsInterop in the following cases: - // * It has a jsInteropName annotation - // * It is external member of a class or library tagged as JsInterop. - if (element.isFunction || element.isConstructor || element.isAccessor) { - FunctionElement function = element; - if (!function.isExternal) return false; + /// Sets the explicit js interop [name] for the member [element]. + void setJsInteropMemberName(MemberElement element, String name) { + assert(invariant(element, _isJsInteropMember(element), + message: + 'Member $element is not js interop but given a js interop name.')); + jsInteropMemberNames[element] = name; + } - if (_isJsInterop(function)) return true; - if (function.isClassMember) return isJsInterop(function.contextClass); - if (function.isTopLevel) return isJsInterop(function.library); - return false; - } else { - return _isJsInterop(element); - } + /// Returns the explicit js interop name for library [element]. + String getJsInteropLibraryName(LibraryElement element) { + return jsInteropLibraryNames[element]; + } + + /// Returns the explicit js interop name for class [element]. + String getJsInteropClassName(ClassElement element) { + return jsInteropClassNames[element]; + } + + /// Returns the explicit js interop name for member [element]. + String getJsInteropMemberName(MemberElement element) { + return jsInteropMemberNames[element]; } /// Returns `true` if [element] is a JsInterop library. - bool isJsInteropLibrary(LibraryElement element) => isJsInterop(element); + bool isJsInteropLibrary(LibraryElement element) => + _isJsInteropLibrary(element); /// Returns `true` if [element] is a JsInterop class. - bool isJsInteropClass(ClassElement element) => isJsInterop(element); + bool isJsInteropClass(ClassElement element) => _isJsInteropClass(element); /// Returns `true` if [element] is a JsInterop method. - bool isJsInteropMember(MethodElement element) => isJsInterop(element); + bool isJsInteropMember(MemberElement element) { + if (element.isFunction || element.isConstructor || element.isAccessor) { + MethodElement function = element; + if (!function.isExternal) return false; + + if (_isJsInteropMember(function)) return true; + if (function.isClassMember) { + return _isJsInteropClass(function.enclosingClass); + } + if (function.isTopLevel) { + return _isJsInteropLibrary(function.library); + } + return false; + } else { + return _isJsInteropMember(element); + } + } /// Returns `true` if the name of [element] is fixed for the generated /// JavaScript. - bool hasFixedBackendName(Element element) { - return isJsInterop(element) || + bool hasFixedBackendName(MemberElement element) { + return isJsInteropMember(element) || nativeMemberName.containsKey(element.declaration); } - String _jsNameHelper(Element element) { - String jsInteropName = jsInteropNames[element.declaration]; - assert(invariant(element, !(_isJsInterop(element) && jsInteropName == null), - message: - 'Element $element is js interop but js interop name has not yet ' - 'been computed.')); - if (jsInteropName != null && jsInteropName.isNotEmpty) { - return jsInteropName; - } - return element.isLibrary ? 'self' : getUnescapedJSInteropName(element.name); - } - /// Computes the name for [element] to use in the generated JavaScript. This /// is either given through a native annotation or a js interop annotation. - String getFixedBackendName(Entity entity) { - // TODO(johnniwinther): Remove this assignment from [Entity] to [Element] - // when `.declaration` is no longer needed. - Element element = entity; + String getFixedBackendName(MemberElement element) { String name = nativeMemberName[element.declaration]; - if (name == null && isJsInterop(element)) { + if (name == null && isJsInteropMember(element)) { // If an element isJsInterop but _isJsInterop is false that means it is // considered interop as the parent class is interop. - name = _jsNameHelper( - element.isConstructor ? element.enclosingClass : element); + name = element.isConstructor + ? _jsClassNameHelper(element.enclosingClass) + : _jsMemberNameHelper(element); nativeMemberName[element.declaration] = name; } return name; } - /// Whether [element] corresponds to a native JavaScript construct either - /// through the native mechanism (`@Native(...)` or the `native` pseudo - /// keyword) which is only allowed for internal libraries or via the typed - /// JavaScriptInterop mechanism which is allowed for user libraries. - bool isNative(Element element) { - if (isJsInterop(element)) return true; - if (element.isClass) { - return nativeClassTagInfo.containsKey(element.declaration); - } else { - return nativeMemberName.containsKey(element.declaration); + String _jsLibraryNameHelper(LibraryElement element) { + String jsInteropName = getJsInteropLibraryName(element); + if (jsInteropName != null && jsInteropName.isNotEmpty) return jsInteropName; + return 'self'; + } + + String _jsClassNameHelper(ClassElement element) { + String jsInteropName = getJsInteropClassName(element); + if (jsInteropName != null && jsInteropName.isNotEmpty) return jsInteropName; + return computeUnescapedJSInteropName(element.name); + } + + String _jsMemberNameHelper(MemberElement element) { + String jsInteropName = jsInteropMemberNames[element]; + assert(invariant(element, + !(jsInteropMemberNames.containsKey(element) && jsInteropName == null), + message: + 'Member $element is js interop but js interop name has not yet ' + 'been computed.')); + if (jsInteropName != null && jsInteropName.isNotEmpty) { + return jsInteropName; } + return computeUnescapedJSInteropName(element.name); + } + + /// Returns a JavaScript path specifying the context in which + /// [element.fixedBackendName] should be evaluated. Only applicable for + /// elements using typed JavaScript interop. + /// For example: fixedBackendPath for the static method createMap in the + /// Map class of the goog.map JavaScript library would have path + /// "goog.maps.Map". + String getFixedBackendMethodPath(MethodElement element) { + if (!isJsInteropMember(element)) return null; + if (element.isInstanceMember) return 'this'; + if (element.isConstructor) { + return _fixedBackendClassPath(element.enclosingClass); + } + StringBuffer sb = new StringBuffer(); + sb.write(_jsLibraryNameHelper(element.library)); + if (element.enclosingClass != null) { + sb..write('.')..write(_jsClassNameHelper(element.enclosingClass)); + } + return sb.toString(); + } + + String _fixedBackendClassPath(ClassElement element) { + if (!isJsInteropClass(element)) return null; + return _jsLibraryNameHelper(element.library); } /// Returns `true` if [cls] is a native class. - bool isNativeClass(ClassElement element) => isNative(element); + bool isNativeClass(ClassElement element) { + if (isJsInteropClass(element)) return true; + return nativeClassTagInfo.containsKey(element); + } /// Returns `true` if [element] is a native member of a native class. - bool isNativeMember(MemberElement element) => isNative(element); + bool isNativeMember(MemberElement element) { + if (isJsInteropMember(element)) return true; + return nativeMemberName.containsKey(element); + } /// Returns `true` if [element] or any of its superclasses is native. bool isNativeOrExtendsNative(ClassElement element) { @@ -390,7 +461,7 @@ class NativeDataImpl /// Apply JS$ escaping scheme to convert possible escaped Dart names into /// JS names. - String getUnescapedJSInteropName(String name) { + String computeUnescapedJSInteropName(String name) { return name.startsWith(_jsInteropEscapePrefix) ? name.substring(_jsInteropEscapePrefix.length) : name; diff --git a/pkg/compiler/lib/src/js_emitter/native_emitter.dart b/pkg/compiler/lib/src/js_emitter/native_emitter.dart index 573943103de..ee23ef276aa 100644 --- a/pkg/compiler/lib/src/js_emitter/native_emitter.dart +++ b/pkg/compiler/lib/src/js_emitter/native_emitter.dart @@ -339,7 +339,8 @@ class NativeEmitter { // and library that uses typed JavaScript interop will create only 1 // unique template. receiver = js - .uncachedExpressionTemplate(namer.fixedBackendMethodPath(member)) + .uncachedExpressionTemplate( + nativeData.getFixedBackendMethodPath(member)) .instantiate([]); } else { receiver = js('this'); diff --git a/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart b/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart index beea7d26b99..3f60ffa6998 100644 --- a/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart +++ b/pkg/compiler/lib/src/js_emitter/program_builder/program_builder.dart @@ -354,7 +354,7 @@ class ProgramBuilder { if (e is ClassElement && backend.nativeData.isJsInteropClass(e)) { e.declaration.forEachMember((_, Element member) { var jsName = - backend.nativeData.getUnescapedJSInteropName(member.name); + backend.nativeData.computeUnescapedJSInteropName(member.name); if (!member.isInstanceMember) return; if (member.isGetter || member.isField || member.isFunction) { var selectors = worldBuilder.getterInvocationsByName(member.name); diff --git a/pkg/compiler/lib/src/ssa/builder.dart b/pkg/compiler/lib/src/ssa/builder.dart index 6d3436375b1..907d0e2cfb6 100644 --- a/pkg/compiler/lib/src/ssa/builder.dart +++ b/pkg/compiler/lib/src/ssa/builder.dart @@ -1067,7 +1067,7 @@ class SsaBuilder extends ast.Visitor ast.Send call = link.head; assert(ast.Initializers.isSuperConstructorCall(call) || ast.Initializers.isConstructorRedirect(call)); - FunctionElement target = elements[call].implementation; + ConstructorElement target = elements[call]; CallStructure callStructure = elements.getSelector(call).callStructure; Link arguments = call.arguments; @@ -2490,8 +2490,8 @@ class SsaBuilder extends ast.Visitor * Invariant: [element] must be an implementation element. */ List makeStaticArgumentList(CallStructure callStructure, - Link arguments, FunctionElement element) { - assert(invariant(element, element.isImplementation)); + Link arguments, MethodElement element) { + assert(invariant(element, element.isDeclaration)); HInstruction compileArgument(ast.Node argument) { visit(argument); @@ -2501,7 +2501,7 @@ class SsaBuilder extends ast.Visitor return Elements.makeArgumentsList( callStructure, arguments, - element, + element.implementation, compileArgument, backend.nativeData.isJsInteropMember(element) ? handleConstantForOptionalParameterJsInterop @@ -3069,8 +3069,8 @@ class SsaBuilder extends ast.Visitor Selector selector = elements.getSelector(node); assert(invariant(node, selector.applies(method.implementation), message: "$selector does not apply to ${method.implementation}")); - List inputs = makeStaticArgumentList( - selector.callStructure, node.arguments, method.implementation); + List inputs = + makeStaticArgumentList(selector.callStructure, node.arguments, method); push(buildInvokeSuper(selector, method, inputs, sourceInformation)); } @@ -3402,7 +3402,7 @@ class SsaBuilder extends ast.Visitor inputs.add(graph.addConstantNull(closedWorld)); } inputs.addAll(makeStaticArgumentList( - callStructure, send.arguments, constructorImplementation)); + callStructure, send.arguments, constructorImplementation.declaration)); TypeMask elementType = computeType(constructor); if (isFixedListConstructorCall) { @@ -3562,9 +3562,9 @@ class SsaBuilder extends ast.Visitor /// Generate an invocation to the static or top level [function]. void generateStaticFunctionInvoke( - ast.Send node, FunctionElement function, CallStructure callStructure) { - List inputs = makeStaticArgumentList( - callStructure, node.arguments, function.implementation); + ast.Send node, MethodElement function, CallStructure callStructure) { + List inputs = + makeStaticArgumentList(callStructure, node.arguments, function); pushInvokeStatic(node, function, inputs, sourceInformation: @@ -4026,7 +4026,7 @@ class SsaBuilder extends ast.Visitor if (argument != null) { filteredArguments.add(argument); var jsName = - backend.nativeData.getUnescapedJSInteropName(parameter.name); + backend.nativeData.computeUnescapedJSInteropName(parameter.name); parameterNameMap[jsName] = new js.InterpolatedExpression(positions++); } i++; @@ -4045,7 +4045,8 @@ class SsaBuilder extends ast.Visitor ..sourceInformation = sourceInformation; } var target = new HForeignCode( - js.js.parseForeignJS("${backend.namer.fixedBackendMethodPath(element)}." + js.js.parseForeignJS( + "${backend.nativeData.getFixedBackendMethodPath(element)}." "${backend.nativeData.getFixedBackendName(element)}"), commonMasks.dynamicType, []); diff --git a/pkg/compiler/lib/src/types/types.dart b/pkg/compiler/lib/src/types/types.dart index a11c61de646..d4565d5fadd 100644 --- a/pkg/compiler/lib/src/types/types.dart +++ b/pkg/compiler/lib/src/types/types.dart @@ -206,13 +206,20 @@ class GlobalTypeInferenceResults { var key = (element is SynthesizedCallMethodElementX) ? element.memberContext : element; + bool isJsInterop = false; + if (element is MemberElement) { + isJsInterop = _compiler.backend.nativeData.isJsInteropMember(element); + } else if (element is ClassElement) { + // TODO(johnniwinther): Can we meet classes here? + isJsInterop = _compiler.backend.nativeData.isJsInteropClass(element); + } return _elementResults.putIfAbsent( element, () => new GlobalTypeInferenceElementResultImpl( element, _inferrer.inferrer.inTreeData[key], _inferrer, - _compiler.backend.nativeData.isJsInterop(element), + isJsInterop, dynamicType)); } diff --git a/tests/compiler/dart2js/serialization/native_data_test.dart b/tests/compiler/dart2js/serialization/native_data_test.dart index e9d116c69fd..e723e3fbf4c 100644 --- a/tests/compiler/dart2js/serialization/native_data_test.dart +++ b/tests/compiler/dart2js/serialization/native_data_test.dart @@ -50,8 +50,20 @@ Future checkNativeData(Uri uri, {bool verbose: false}) async { NativeDataImpl nativeData1 = backend1.nativeData; NativeDataImpl nativeData2 = backend2.nativeData; - checkMaps(nativeData1.jsInteropNames, nativeData2.jsInteropNames, - "NativeData.jsInteropNames", areElementsEquivalent, equality, + checkMaps( + nativeData1.jsInteropLibraryNames, + nativeData2.jsInteropLibraryNames, + "NativeData.jsInteropLibraryNames", + areElementsEquivalent, + equality, + verbose: verbose); + + checkMaps(nativeData1.jsInteropClassNames, nativeData2.jsInteropClassNames, + "NativeData.jsInteropClassNames", areElementsEquivalent, equality, + verbose: verbose); + + checkMaps(nativeData1.jsInteropMemberNames, nativeData2.jsInteropMemberNames, + "NativeData.jsInteropMemberNames", areElementsEquivalent, equality, verbose: verbose); checkMaps(nativeData1.nativeMemberName, nativeData2.nativeMemberName,