| 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 83aa3f00697f8b1b0e2927f16784ac98c50c239f..f8afee062c78c0fb3d16f7e13fcb31a4779647de 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 ||
|
| + // 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,
|
| @@ -2672,12 +2544,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);
|
| }
|
| }
|
|
|
| @@ -3153,11 +3027,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,
|
| @@ -3166,7 +3042,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);
|
| @@ -3185,7 +3061,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();
|
| }
|
| }
|
|
|