Chromium Code Reviews| Index: pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart |
| diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart |
| index 55f9f290c18e6b14e4b93ec3457b5213a293f1f8..977c4841bd5c1dbd24659161dfd68c732a375e76 100644 |
| --- a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart |
| +++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart |
| @@ -297,7 +297,7 @@ function initializeDeferredHunk(hunk) { |
| hunk(inherit, mixin, lazy, makeConstList, convertToFastObject, installTearOff, |
| setFunctionNamesIfNecessary, updateHolder, updateTypes, |
| setOrUpdateInterceptorsByTag, setOrUpdateLeafTags, |
| - #embeddedGlobalsObject, #holdersList, #staticState); |
| + #embeddedGlobalsObject, holders, #staticState); |
| } |
| // Returns the global with the given [name]. |
| @@ -313,11 +313,27 @@ function getGlobalFromName(name) { |
| } |
| } |
| +if (#hasSoftDeferredClasses) { |
| + // Loads the soft-deferred classes and initializes them. |
| + // Updates the prototype of the given object. |
| + function softDef(o) { |
| + softDef = function(o) {}; // Replace ourselves. |
| + #deferredGlobal[#softId]( |
| + holders, #embeddedGlobalsObject, #staticState, inherit, mixin, |
| + installTearOff); |
| + o.__proto__ = o.constructor.prototype; |
|
sra1
2017/05/02 20:40:13
This will make Firefox angry.
floitsch
2017/05/06 19:25:43
Yes. I added a comment.
|
| + } |
| +} |
| + |
| +if (#isTrackingAllocations) { |
| + var allocations = #deferredGlobal['allocations'] = {}; |
| +} |
| + |
| // Creates the holders. |
| #holders; |
| // If the name is not set on the functions, do it now. |
| -setFunctionNamesIfNecessary(#holdersList); |
| +setFunctionNamesIfNecessary(holders); |
| // TODO(floitsch): we should build this object as a literal. |
| var #staticStateDeclaration = {}; |
| @@ -429,6 +445,28 @@ updateTypes(#types); |
| #nativeSupport; |
| }'''; |
| +/// Soft-deferred fragments are built similarly to the main fragment. |
| +/// |
| +/// However, they don't contribute anything to global namespace, but just |
| +/// initialize existing classes. For example, they update the inheritance |
| +/// hierarchy, and add methods the prototypes. |
| +const String softDeferredBoilerplate = ''' |
| +#deferredGlobal[#softId] = |
| + function(holdersList, #embeddedGlobalsObject, #staticState, inherit, mixin, |
| + installTearOff) { |
| + |
| +// Installs the holders as local variables. |
| +#installHoldersAsLocals; |
| +// Sets the prototypes of the new classes. |
| +#prototypes; |
| +// Sets aliases of methods (on the prototypes of classes). |
| +#aliases; |
| +// Installs the tear-offs of functions. |
| +#tearOffs; |
| +// Builds the inheritance structure. |
| +#inheritance; |
| +}'''; |
| + |
| /** |
| * This class builds a JavaScript tree for a given fragment. |
| * |
| @@ -464,48 +502,80 @@ class FragmentEmitter { |
| Iterable<Holder> nonStaticStateHolders = |
| program.holders.where((Holder holder) => !holder.isStaticStateHolder); |
| - return js.js.statement(mainBoilerplate, { |
| - 'directAccessTestExpression': js.js(directAccessTestExpression), |
| - 'typeNameProperty': js.string(ModelEmitter.typeNameProperty), |
| - 'cyclicThrow': backend.emitter |
| - .staticFunctionAccess(compiler.commonElements.cyclicThrowHelper), |
| - 'operatorIsPrefix': js.string(namer.operatorIsPrefix), |
| - 'tearOffCode': new js.Block(buildTearOffCode(compiler.options, |
| - backend.emitter.emitter, backend.namer, compiler.commonElements)), |
| - 'embeddedTypes': generateEmbeddedGlobalAccess(TYPES), |
| - 'embeddedInterceptorTags': |
| - generateEmbeddedGlobalAccess(INTERCEPTORS_BY_TAG), |
| - 'embeddedLeafTags': generateEmbeddedGlobalAccess(LEAF_TAGS), |
| - 'embeddedGlobalsObject': js.js("init"), |
| - 'holdersList': new js.ArrayInitializer(nonStaticStateHolders |
| - .map((holder) => js.js("#", holder.name)) |
| - .toList(growable: false)), |
| - 'staticStateDeclaration': new js.VariableDeclaration( |
| - namer.staticStateHolder, |
| - allowRename: false), |
| - 'staticState': js.js('#', namer.staticStateHolder), |
| - 'constantHolderReference': buildConstantHolderReference(program), |
| - 'holders': emitHolders(program.holders, fragment), |
| - 'callName': js.string(namer.callNameField), |
| - 'stubName': js.string(namer.stubNameField), |
| - 'argumentCount': js.string(namer.requiredParameterField), |
| - 'defaultArgumentValues': js.string(namer.defaultValuesField), |
| - 'prototypes': emitPrototypes(fragment), |
| - 'inheritance': emitInheritance(fragment), |
| - 'aliases': emitInstanceMethodAliases(fragment), |
| - 'tearOffs': emitInstallTearOffs(fragment), |
| - 'constants': emitConstants(fragment), |
| - 'staticNonFinalFields': emitStaticNonFinalFields(fragment), |
| - 'lazyStatics': emitLazilyInitializedStatics(fragment), |
| - 'embeddedGlobals': emitEmbeddedGlobals(program, deferredLoadHashes), |
| - 'nativeSupport': program.needsNativeSupport |
| - ? emitNativeSupport(fragment) |
| - : new js.EmptyStatement(), |
| - 'jsInteropSupport': backend.jsInteropAnalysis.enabledJsInterop |
| - ? backend.jsInteropAnalysis.buildJsInteropBootstrap() |
| + String softDeferredId = "softDeferred${new Random().nextInt(0x7FFFFFFF)}"; |
| + |
| + return new js.Block([ |
| + program.hasSoftDeferredClasses |
| + ? js.js.statement(softDeferredBoilerplate, { |
| + 'deferredGlobal': ModelEmitter.deferredInitializersGlobal, |
| + 'softId': js.string(softDeferredId), |
| + // TODO(floitsch): don't just reference 'init'. |
| + 'embeddedGlobalsObject': new js.Parameter('init'), |
| + 'staticState': new js.Parameter(namer.staticStateHolder), |
| + 'installHoldersAsLocals': |
| + emitInstallHoldersAsLocals(nonStaticStateHolders), |
| + 'prototypes': emitPrototypes(fragment, softDeferred: true), |
| + 'aliases': |
| + emitInstanceMethodAliases(fragment, softDeferred: true), |
| + 'tearOffs': emitInstallTearOffs(fragment, softDeferred: true), |
| + 'inheritance': emitInheritance(fragment, softDeferred: true), |
| + }) |
| : new js.EmptyStatement(), |
| - 'invokeMain': fragment.invokeMain, |
| - }); |
| + js.js.statement(mainBoilerplate, { |
| + 'directAccessTestExpression': js.js(directAccessTestExpression), |
| + 'typeNameProperty': js.string(ModelEmitter.typeNameProperty), |
| + 'cyclicThrow': backend.emitter |
| + .staticFunctionAccess(compiler.commonElements.cyclicThrowHelper), |
| + 'operatorIsPrefix': js.string(namer.operatorIsPrefix), |
| + 'tearOffCode': new js.Block(buildTearOffCode(compiler.options, |
| + backend.emitter.emitter, backend.namer, compiler.commonElements)), |
| + 'embeddedTypes': generateEmbeddedGlobalAccess(TYPES), |
| + 'embeddedInterceptorTags': |
| + generateEmbeddedGlobalAccess(INTERCEPTORS_BY_TAG), |
| + 'embeddedLeafTags': generateEmbeddedGlobalAccess(LEAF_TAGS), |
| + 'embeddedGlobalsObject': js.js("init"), |
| + 'staticStateDeclaration': new js.VariableDeclaration( |
| + namer.staticStateHolder, |
| + allowRename: false), |
| + 'staticState': js.js('#', namer.staticStateHolder), |
| + 'constantHolderReference': buildConstantHolderReference(program), |
| + 'holders': emitHolders(program.holders, fragment), |
| + 'callName': js.string(namer.callNameField), |
| + 'stubName': js.string(namer.stubNameField), |
| + 'argumentCount': js.string(namer.requiredParameterField), |
| + 'defaultArgumentValues': js.string(namer.defaultValuesField), |
| + 'deferredGlobal': ModelEmitter.deferredInitializersGlobal, |
| + 'hasSoftDeferredClasses': program.hasSoftDeferredClasses, |
| + 'softId': js.string(softDeferredId), |
| + 'isTrackingAllocations': compiler.options.trackAllocations, |
| + 'prototypes': emitPrototypes(fragment), |
| + 'inheritance': emitInheritance(fragment), |
| + 'aliases': emitInstanceMethodAliases(fragment), |
| + 'tearOffs': emitInstallTearOffs(fragment), |
| + 'constants': emitConstants(fragment), |
| + 'staticNonFinalFields': emitStaticNonFinalFields(fragment), |
| + 'lazyStatics': emitLazilyInitializedStatics(fragment), |
| + 'embeddedGlobals': emitEmbeddedGlobals(program, deferredLoadHashes), |
| + 'nativeSupport': program.needsNativeSupport |
| + ? emitNativeSupport(fragment) |
| + : new js.EmptyStatement(), |
| + 'jsInteropSupport': backend.jsInteropAnalysis.enabledJsInterop |
| + ? backend.jsInteropAnalysis.buildJsInteropBootstrap() |
| + : new js.EmptyStatement(), |
| + 'invokeMain': fragment.invokeMain, |
| + }), |
| + ]); |
| + } |
| + |
| + js.Statement emitInstallHoldersAsLocals(Iterable<Holder> holders) { |
| + List<js.Statement> holderInits = <js.Statement>[]; |
| + int counter = 0; |
| + for (Holder holder in holders) { |
| + holderInits.add(new js.ExpressionStatement(new js.VariableInitialization( |
| + new js.VariableDeclaration(holder.name, allowRename: false), |
| + js.js("holdersList[#]", js.number(counter++))))); |
| + } |
| + return new js.Block(holderInits); |
| } |
| js.Expression emitDeferredFragment(DeferredFragment fragment, |
| @@ -657,6 +727,14 @@ class FragmentEmitter { |
| var parameters = <js.Name>[]; |
| var thisRef; |
| + if (cls.isSoftDeferred) { |
| + statements.add(js.js.statement('softDef(this)')); |
| + } else if (compiler.options.trackAllocations) { |
| + String qualifiedName = |
| + "${cls.element.library.canonicalUri}:${cls.element.name}"; |
| + statements.add(js.js.statement('allocations["$qualifiedName"] = true')); |
| + } |
| + |
| // If there are many references to `this`, cache it in a local. |
| if (cls.fields.length + (cls.hasRtiField ? 1 : 0) >= 4) { |
| // TODO(29455): Fix js_ast printer and minifier to avoid conflicts between |
| @@ -697,9 +775,10 @@ class FragmentEmitter { |
| /// |
| /// This section updates the prototype-property of all constructors in the |
| /// global holders. |
| - js.Statement emitPrototypes(Fragment fragment) { |
| + js.Statement emitPrototypes(Fragment fragment, {softDeferred = false}) { |
| List<js.Statement> assignments = fragment.libraries |
| .expand((Library library) => library.classes) |
| + .where((Class cls) => cls.isSoftDeferred == softDeferred) |
| .map((Class cls) { |
| var proto = js.js.statement( |
| '#.prototype = #;', [classReference(cls), emitPrototype(cls)]); |
| @@ -864,13 +943,14 @@ class FragmentEmitter { |
| /// |
| /// In this section prototype chains are updated and mixin functions are |
| /// copied. |
| - js.Statement emitInheritance(Fragment fragment) { |
| + js.Statement emitInheritance(Fragment fragment, {bool softDeferred = false}) { |
| List<js.Statement> inheritCalls = <js.Statement>[]; |
| List<js.Statement> mixinCalls = <js.Statement>[]; |
| Set<Class> classesInFragment = new Set<Class>(); |
| for (Library library in fragment.libraries) { |
| - classesInFragment.addAll(library.classes); |
| + classesInFragment.addAll(library.classes |
| + .where(((Class cls) => cls.isSoftDeferred == softDeferred))); |
| } |
| Map<Class, List<Class>> subclasses = <Class, List<Class>>{}; |
| @@ -891,6 +971,7 @@ class FragmentEmitter { |
| for (Library library in fragment.libraries) { |
| for (Class cls in library.classes) { |
| + if (cls.isSoftDeferred != softDeferred) continue; |
| collect(cls); |
| if (cls.isMixinApplication) { |
| @@ -937,11 +1018,13 @@ class FragmentEmitter { |
| /// |
| /// This step consists of simply copying JavaScript functions to their |
| /// aliased names so they point to the same function. |
| - js.Statement emitInstanceMethodAliases(Fragment fragment) { |
| + js.Statement emitInstanceMethodAliases(Fragment fragment, |
| + {bool softDeferred = false}) { |
| List<js.Statement> assignments = <js.Statement>[]; |
| for (Library library in fragment.libraries) { |
| for (Class cls in library.classes) { |
| + if (cls.isSoftDeferred != softDeferred) continue; |
| for (InstanceMethod method in cls.methods) { |
| if (method.aliasName != null) { |
| assignments.add(js.js.statement('#.prototype.# = #.prototype.#', [ |
| @@ -1066,7 +1149,8 @@ class FragmentEmitter { |
| } |
| /// Emits the section that installs tear-off getters. |
| - js.Statement emitInstallTearOffs(Fragment fragment) { |
| + js.Statement emitInstallTearOffs(Fragment fragment, |
| + {bool softDeferred = false}) { |
| List<js.Statement> inits = <js.Statement>[]; |
| js.Expression temp; |
| @@ -1082,6 +1166,7 @@ class FragmentEmitter { |
| } |
| } |
| for (Class cls in library.classes) { |
| + if (cls.isSoftDeferred != softDeferred) continue; |
| var methods = cls.methods.where((m) => m.needsTearOff).toList(); |
| js.Expression container = js.js("#.prototype", classReference(cls)); |
| js.Expression reference = container; |