Chromium Code Reviews| Index: pkg/compiler/lib/src/js_backend/backend.dart |
| diff --git a/pkg/compiler/lib/src/js_backend/backend.dart b/pkg/compiler/lib/src/js_backend/backend.dart |
| index 1d8a35a891c0214d73aded03895d0cb1149c9ef7..889e31978d7bb3d21c8d9c7223f007739d8c902c 100644 |
| --- a/pkg/compiler/lib/src/js_backend/backend.dart |
| +++ b/pkg/compiler/lib/src/js_backend/backend.dart |
| @@ -504,6 +504,10 @@ class JavaScriptBackend extends Backend { |
| SourceInformationStrategy sourceInformationStrategy; |
| + JavaScriptBackendSerialization serialization; |
| + |
| + final NativeData nativeData = new NativeData(); |
| + |
| final BackendHelpers helpers; |
| final BackendImpacts impacts; |
| @@ -542,6 +546,7 @@ class JavaScriptBackend extends Backend { |
| ? new CpsFunctionCompiler( |
| compiler, this, sourceInformationStrategy) |
| : new SsaFunctionCompiler(this, sourceInformationStrategy); |
| + serialization = new JavaScriptBackendSerialization(this); |
| } |
| ConstantSystem get constantSystem => constants.constantSystem; |
| @@ -600,44 +605,50 @@ class JavaScriptBackend extends Backend { |
| // TODO(johnniwinther): Replace this with a more precise modelling; type |
| // inference of these elements is disabled. |
| Element registerBackendUse(Element element) { |
| - if (element != null) { |
| - bool registerUse = false; |
| - if (element == helpers.streamIteratorConstructor || |
| - element == helpers.compiler.symbolConstructor || |
| - element == helpers.compiler.symbolValidatedConstructor || |
| - element == helpers.syncCompleterConstructor || |
| - element == coreClasses.symbolClass || |
| - element == helpers.objectNoSuchMethod) { |
| - // TODO(johnniwinther): These are valid but we could be more precise. |
| - registerUse = true; |
| - } else if (element.implementationLibrary.isPatch || |
| - element.library == helpers.jsHelperLibrary || |
| - element.library == helpers.interceptorsLibrary || |
| - element.library == helpers.isolateHelperLibrary) { |
| - // TODO(johnniwinther): We should be more precise about these. |
| - registerUse = true; |
| - } else if (element == coreClasses.listClass || |
| - element == helpers.mapLiteralClass || |
| - element == coreClasses.functionClass || |
| - element == coreClasses.stringClass) { |
| - // TODO(johnniwinther): Avoid these. |
| - registerUse = true; |
| - } |
| - if (!registerUse) { |
| - assert(invariant(element, false, |
| - message: "Backend use of $element is not allowed.")); |
| - return element; |
| - } |
| - helpersUsed.add(element.declaration); |
| - if (element.isClass && element.isPatched) { |
| - // Both declaration and implementation may declare fields, so we |
| - // add both to the list of helpers. |
| - helpersUsed.add(element.implementation); |
| - } |
| + if (element == null) return null; |
| + assert(invariant(element, _isValidBackendUse(element), |
| + message: "Backend use of $element is not allowed.")); |
| + helpersUsed.add(element.declaration); |
| + if (element.isClass && element.isPatched) { |
| + // Both declaration and implementation may declare fields, so we |
| + // add both to the list of helpers. |
| + helpersUsed.add(element.implementation); |
| } |
| return element; |
| } |
| + bool _isValidBackendUse(Element element) { |
| + assert(invariant(element, element.isDeclaration, |
| + message: "")); |
| + if (element == helpers.streamIteratorConstructor || |
| + element == helpers.compiler.symbolConstructor || |
| + element == helpers.compiler.symbolValidatedConstructor || |
| + element == helpers.syncCompleterConstructor || |
| + element == coreClasses.symbolClass || |
| + element == helpers.objectNoSuchMethod) { |
| + // TODO(johnniwinther): These are valid but we could be more precise. |
| + return true; |
| + } else if (element.implementationLibrary.isPatch || |
|
Siggi Cherem (dart-lang)
2016/03/17 15:33:45
nit, can be done later: any reason why not merge a
Johnni Winther
2016/03/18 08:11:32
They are grouped by the TODOs (the reasons for all
|
| + // Needed to detect deserialized injected elements, that is |
| + // element declared in patch files. |
| + (element.library.isPlatformLibrary && |
| + element.sourcePosition.uri.path.contains( |
| + '_internal/js_runtime/lib/')) || |
| + element.library == helpers.jsHelperLibrary || |
| + element.library == helpers.interceptorsLibrary || |
| + element.library == helpers.isolateHelperLibrary) { |
| + // TODO(johnniwinther): We should be more precise about these. |
| + return true; |
| + } else if (element == coreClasses.listClass || |
| + element == helpers.mapLiteralClass || |
| + element == coreClasses.functionClass || |
| + element == coreClasses.stringClass) { |
| + // TODO(johnniwinther): Avoid these. |
| + return true; |
| + } |
| + return false; |
| + } |
| + |
| bool usedByBackend(Element element) { |
| if (element.isParameter |
| || element.isInitializingFormal |
| @@ -712,161 +723,16 @@ class JavaScriptBackend extends Backend { |
| return aliasedSuperMembers.contains(member); |
| } |
| - /// The JavaScript names for elements implemented via typed JavaScript |
| - /// interop. |
| - Map<Element, String> jsInteropNames = <Element, String>{}; |
| - |
| - /// The JavaScript names for native JavaScript elements implemented. |
| - Map<Element, String> nativeMemberName = <Element, String>{}; |
| - |
| - /// Tag info for native JavaScript classes names. See |
| - /// [setNativeClassTagInfo]. |
| - Map<ClassElement, String> nativeClassTagInfo = <ClassElement, String>{}; |
| - |
| - /// Returns `true` if [element] is explicitly marked as part of JsInterop. |
| - bool _isJsInterop(Element element) { |
| - return jsInteropNames.containsKey(element.declaration); |
| - } |
| - |
| - /// Returns [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; |
| - } |
| - |
| - /// Sets the explicit js interop [name] for [element]. |
| - void setJsInteropName(Element element, String name) { |
| - assert(invariant(element, |
| - isJsInterop(element), |
| - message: |
| - 'Element $element is not js interop but given a js interop name.')); |
| - jsInteropNames[element.declaration] = name; |
| - } |
| - |
| - /// Returns the explicit js interop name for [element]. |
| - String getJsInteropName(Element element) { |
| - return jsInteropNames[element.declaration]; |
| - } |
| - |
| /// Returns `true` if [element] is part of JsInterop. |
| @override |
| - 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; |
| - |
| - 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 `true` if the name of [element] is fixed for the generated |
| - /// JavaScript. |
| - bool hasFixedBackendName(Element element) { |
| - return isJsInterop(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' : 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(Element element) { |
| - String name = nativeMemberName[element.declaration]; |
| - if (name == null && isJsInterop(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); |
| - nativeMemberName[element.declaration] = name; |
| - } |
| - return name; |
| - } |
| + bool isJsInterop(Element element) => nativeData.isJsInterop(element); |
| /// 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. |
| @override |
| - bool isNative(Element element) { |
| - if (isJsInterop(element)) return true; |
| - if (element.isClass) { |
| - return nativeClassTagInfo.containsKey(element.declaration); |
| - } else { |
| - return nativeMemberName.containsKey(element.declaration); |
| - } |
| - } |
| - |
| - /// Sets the native [name] for the member [element]. This name is used for |
| - /// [element] in the generated JavaScript. |
| - void setNativeMemberName(MemberElement element, String name) { |
| - // TODO(johnniwinther): Avoid setting this more than once. The enqueuer |
| - // might enqueue [element] several times (before processing it) and computes |
| - // name on each call to `internalAddToWorkList`. |
| - assert(invariant(element, |
| - nativeMemberName[element.declaration] == null || |
| - nativeMemberName[element.declaration] == name, |
| - message: |
| - "Native member name set inconsistently on $element: " |
| - "Existing name '${nativeMemberName[element.declaration]}', " |
| - "new name '$name'.")); |
| - nativeMemberName[element.declaration] = name; |
| - } |
| - |
| - /// Sets the native tag info for [cls]. |
| - /// |
| - /// The tag info string contains comma-separated 'words' which are either |
| - /// dispatch tags (having JavaScript identifier syntax) and directives that |
| - /// begin with `!`. |
| - void setNativeClassTagInfo(ClassElement cls, String tagInfo) { |
| - // TODO(johnniwinther): Assert that this is only called once. The memory |
| - // compiler copies pre-processed elements into a new compiler through |
| - // [Compiler.onLibraryScanned] and thereby causes multiple calls to this |
| - // method. |
| - assert(invariant(cls, |
| - nativeClassTagInfo[cls.declaration] == null || |
| - nativeClassTagInfo[cls.declaration] == tagInfo, |
| - message: |
| - "Native tag info set inconsistently on $cls: " |
| - "Existing tag info '${nativeClassTagInfo[cls.declaration]}', " |
| - "new tag info '$tagInfo'.")); |
| - nativeClassTagInfo[cls.declaration] = tagInfo; |
| - } |
| - |
| - /// Returns the list of native tag words for [cls]. |
| - List<String> getNativeTagsOfClassRaw(ClassElement cls) { |
| - String quotedName = nativeClassTagInfo[cls.declaration]; |
| - return quotedName.substring(1, quotedName.length - 1).split(','); |
| - } |
| - |
| - /// Returns the list of non-directive native tag words for [cls]. |
| - List<String> getNativeTagsOfClass(ClassElement cls) { |
| - return getNativeTagsOfClassRaw(cls).where( |
| - (s) => !s.startsWith('!')).toList(); |
| - } |
| - |
| - /// Returns `true` if [cls] has a `!nonleaf` tag word. |
| - bool hasNativeTagsForcedNonLeaf(ClassElement cls) { |
| - return getNativeTagsOfClassRaw(cls).contains('!nonleaf'); |
| - } |
| + bool isNative(Element element) => nativeData.isNative(element); |
| bool isNativeOrExtendsNative(ClassElement element) { |
| if (element == null) return false; |
| @@ -1009,7 +875,13 @@ class JavaScriptBackend extends Backend { |
| Element interceptorMember = interceptorClass.lookupMember(member.name); |
| // Interceptors must override all Object methods due to calling convention |
| // differences. |
| - assert(interceptorMember.enclosingClass == interceptorClass); |
| + assert(invariant( |
| + interceptorMember, |
| + interceptorMember.enclosingClass == interceptorClass, |
| + message: |
| + "Member ${member.name} not overridden in ${interceptorClass}. " |
| + "Found $interceptorMember from " |
| + "${interceptorMember.enclosingClass}.")); |
| }); |
| } |
| @@ -1209,7 +1081,7 @@ class JavaScriptBackend extends Backend { |
| Element getFactory(String name, int arity) { |
| // The constructor is on the patch class, but dart2js unit tests don't |
| // have a patch class. |
| - ClassElement implementation = cls.patch != null ? cls.patch : cls; |
| + ClassElement implementation = cls.implementation; |
| ConstructorElement ctor = implementation.lookupConstructor(name); |
| if (ctor == null || |
| (Name.isPrivateName(name) && |
| @@ -1225,7 +1097,7 @@ class JavaScriptBackend extends Backend { |
| Element getMember(String name) { |
| // The constructor is on the patch class, but dart2js unit tests don't |
| // have a patch class. |
| - ClassElement implementation = cls.patch != null ? cls.patch : cls; |
| + ClassElement implementation = cls.implementation; |
| Element element = implementation.lookupLocalMember(name); |
| if (element == null || !element.isFunction || !element.isStatic) { |
| reporter.internalError(helpers.mapLiteralClass, |
| @@ -2673,12 +2545,14 @@ class JavaScriptBackend extends Backend { |
| @override |
| ImpactStrategy createImpactStrategy( |
| {bool supportDeferredLoad: true, |
| - bool supportDumpInfo: true}) { |
| + bool supportDumpInfo: true, |
| + bool supportSerialization: true}) { |
| return new JavaScriptImpactStrategy( |
| resolution, |
| compiler.dumpInfoTask, |
| supportDeferredLoad: supportDeferredLoad, |
| - supportDumpInfo: supportDumpInfo); |
| + supportDumpInfo: supportDumpInfo, |
| + supportSerialization: supportSerialization); |
| } |
| } |
| @@ -3154,11 +3028,13 @@ class JavaScriptImpactStrategy extends ImpactStrategy { |
| final DumpInfoTask dumpInfoTask; |
| final bool supportDeferredLoad; |
| final bool supportDumpInfo; |
| + final bool supportSerialization; |
| JavaScriptImpactStrategy(this.resolution, |
| this.dumpInfoTask, |
| {this.supportDeferredLoad, |
| - this.supportDumpInfo}); |
| + this.supportDumpInfo, |
| + this.supportSerialization}); |
| @override |
| void visitImpact(Element element, |
| @@ -3167,7 +3043,7 @@ class JavaScriptImpactStrategy extends ImpactStrategy { |
| ImpactUseCase impactUse) { |
| // TODO(johnniwinther): Compute the application strategy once for each use. |
| if (impactUse == ResolutionEnqueuer.IMPACT_USE) { |
| - if (supportDeferredLoad) { |
| + if (supportDeferredLoad || supportSerialization) { |
| impact.apply(visitor); |
| } else { |
| impact.apply(visitor); |
| @@ -3186,7 +3062,10 @@ class JavaScriptImpactStrategy extends ImpactStrategy { |
| @override |
| void onImpactUsed(ImpactUseCase impactUse) { |
| - if (impactUse == DeferredLoadTask.IMPACT_USE) { |
| + if (impactUse == DeferredLoadTask.IMPACT_USE && |
| + !supportSerialization) { |
| + // TODO(johnniwinther): Allow emptying when serialization has been |
| + // performed. |
| resolution.emptyCache(); |
| } |
| } |