| 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 58207b3d3209e65b7eb8274f040009a5f4b64a9e..d82122ae14da61339cf16b76fe1904cd67da448d 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
|
| @@ -4,6 +4,27 @@
|
|
|
| part of dart2js.js_emitter.startup_emitter.model_emitter;
|
|
|
| +/// The name of the property that stores the tear-off getter on a static
|
| +/// function.
|
| +///
|
| +/// This property is only used when isolates are used.
|
| +///
|
| +/// When serializing static functions we transmit the
|
| +/// name of the static function, but not the name of the function's getter. We
|
| +/// store the getter-function on the static function itself, which allows us to
|
| +/// find it easily.
|
| +const String tearOffPropertyName = r'$tearOff';
|
| +
|
| +/// The name of the property that stores the list of fields on a constructor.
|
| +///
|
| +/// This property is only used when isolates are used.
|
| +///
|
| +/// When serializing objects we extract all fields from any given object.
|
| +/// We extract the names of all fields from a fresh empty object. This list
|
| +/// is cached on the constructor in this property to to avoid too many
|
| +/// allocations.
|
| +const String cachedClassFieldNames = r'$cachedFieldNames';
|
| +
|
| /// The fast startup emitter's goal is to minimize the amount of work that the
|
| /// JavaScript engine has to do before it can start running user code.
|
| ///
|
| @@ -142,6 +163,8 @@ function installTearOff(container, getterName,
|
| isStatic, isIntercepted, requiredParameterCount,
|
| optionalParameterDefaultValues,
|
| callNames, funsOrNames, funType) {
|
| + // A function can have several stubs (for example to fill in optional
|
| + // arguments). We collect these functions in the `funs` array.
|
| var funs = [];
|
| for (var i = 0; i < funsOrNames.length; i++) {
|
| var fun = funsOrNames[i];
|
| @@ -150,8 +173,11 @@ function installTearOff(container, getterName,
|
| funs.push(fun);
|
| }
|
|
|
| - funs[0][#argumentCount] = requiredParameterCount;
|
| - funs[0][#defaultArgumentValues] = optionalParameterDefaultValues;
|
| + // The main function to which all stubs redirect.
|
| + var fun = funs[0];
|
| +
|
| + fun[#argumentCount] = requiredParameterCount;
|
| + fun[#defaultArgumentValues] = optionalParameterDefaultValues;
|
| var reflectionInfo = funType;
|
| if (typeof reflectionInfo == "number") {
|
| // The reflectionInfo can either be a function, or a pointer into the types
|
| @@ -160,8 +186,12 @@ function installTearOff(container, getterName,
|
| reflectionInfo = reflectionInfo + typesOffset;
|
| }
|
| var name = funsOrNames[0];
|
| - container[getterName] =
|
| + var getterFunction =
|
| tearOff(funs, reflectionInfo, isStatic, name, isIntercepted);
|
| + container[getterName] = getterFunction;
|
| + if (isStatic) {
|
| + fun.$tearOffPropertyName = getterFunction;
|
| + }
|
| }
|
|
|
| // Instead of setting the interceptor tags directly we use this update
|
| @@ -221,6 +251,19 @@ function initializeDeferredHunk(hunk) {
|
| #embeddedGlobalsObject, #holdersList, #staticState);
|
| }
|
|
|
| +// Returns the global with the given [name].
|
| +function getGlobalFromName(name) {
|
| + // TODO(floitsch): we are running through all holders. Since negative
|
| + // lookups are expensive we might need to improve this.
|
| + // Relies on the fact that all names are unique across all holders.
|
| + for (var i = 0; i < holders.length; i++) {
|
| + // The constant holder reuses the same names. Therefore we must skip it.
|
| + if (holders[i] == #constantHolderReference) continue;
|
| + // Relies on the fact that all variables are unique.
|
| + if (holders[i][name]) return holders[i][name];
|
| + }
|
| +}
|
| +
|
| // Creates the holders.
|
| #holders;
|
| // TODO(floitsch): if name is not set (for example in IE), run through all
|
| @@ -350,6 +393,7 @@ class FragmentEmitter {
|
| '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),
|
| 'argumentCount': js.string(namer.requiredParameterField),
|
| @@ -469,6 +513,15 @@ class FragmentEmitter {
|
| return new js.Block(statements);
|
| }
|
|
|
| + /// Returns a reference to the constant holder, or the JS-literal `null`.
|
| + js.Expression buildConstantHolderReference(Program program) {
|
| + Holder constantHolder = program.holders
|
| + .firstWhere((Holder holder) => holder.isConstantsHolder,
|
| + orElse: () => null);
|
| + if (constantHolder == null) return new js.LiteralNull();
|
| + return new js.VariableUse(constantHolder.name);
|
| + }
|
| +
|
| /// Emits the given [method].
|
| ///
|
| /// A Dart method might result in several JavaScript functions, if it
|
| @@ -955,19 +1008,7 @@ class FragmentEmitter {
|
| /// This embedded global provides a way to go from a class name (which is
|
| /// also the constructor's name) to the constructor itself.
|
| js.Property emitGetTypeFromName() {
|
| - // TODO(floitsch): Fix getTypeFromName. It's too inefficient.
|
| - // The current implementation relies on the fact that the names in holders
|
| - // are unique across all holders.
|
| - // TODO(floitsch): constants and other globals may share the same name.
|
| - // If a global happens to have the same (minified) name this code breaks.
|
| - // A follow-up CL has a fix for this.
|
| - js.Expression function =
|
| - js.js( """function(name) {
|
| - for (var i = 0; i < holders.length; i++) {
|
| - // Relies on the fact that all variables are unique.
|
| - if (holders[i][name]) return holders[i][name];
|
| - }
|
| - }""");
|
| + js.Expression function = js.js("getGlobalFromName");
|
| return new js.Property(js.string(GET_TYPE_FROM_NAME), function);
|
| }
|
|
|
| @@ -1014,8 +1055,67 @@ class FragmentEmitter {
|
| // impact on real-world programs, though.
|
| globals.add(
|
| new js.Property(js.string(CREATE_NEW_ISOLATE),
|
| - js.js('function () { return $staticStateName; }')));
|
| - // TODO(floitsch): add remaining isolate functions.
|
| + js.js('function () { return $staticStateName; }')));
|
| +
|
| + js.Expression nameToClosureFunction = js.js('''
|
| + // First fetch the static function. From there we can execute its
|
| + // getter function which builds a Dart closure.
|
| + function(name) {
|
| + var staticFunction = getGlobalFromName(name);
|
| + var getterFunction = staticFunction.$tearOffPropertyName;
|
| + return getterFunction();
|
| + }''');
|
| + globals.add(new js.Property(js.string(STATIC_FUNCTION_NAME_TO_CLOSURE),
|
| + nameToClosureFunction));
|
| +
|
| + globals.add(
|
| + new js.Property(js.string(CLASS_ID_EXTRACTOR),
|
| + js.js('function(o) { return o.constructor.name; }')));
|
| +
|
| + js.Expression extractFieldsFunction = js.js('''
|
| + function(o) {
|
| + var constructor = o.constructor;
|
| + var fieldNames = constructor.$cachedClassFieldNames;
|
| + if (!fieldNames) {
|
| + // Extract the fields from an empty unmodified object.
|
| + var empty = new constructor();
|
| + // This gives us the keys that the constructor sets.
|
| + fieldNames = constructor.$cachedClassFieldNames = Object.keys(empty);
|
| + }
|
| + var result = new Array(fieldNames.length);
|
| + for (var i = 0; i < fieldNames.length; i++) {
|
| + result[i] = o[fieldNames[i]];
|
| + }
|
| + return result;
|
| + }''');
|
| + globals.add(new js.Property(js.string(CLASS_FIELDS_EXTRACTOR),
|
| + extractFieldsFunction));
|
| +
|
| + js.Expression createInstanceFromClassIdFunction = js.js('''
|
| + function(name) {
|
| + var constructor = getGlobalFromName(name);
|
| + return new constructor();
|
| + }
|
| + ''');
|
| + globals.add(new js.Property(js.string(INSTANCE_FROM_CLASS_ID),
|
| + createInstanceFromClassIdFunction));
|
| +
|
| + js.Expression initializeEmptyInstanceFunction = js.js('''
|
| + function(name, o, fields) {
|
| + var constructor = o.constructor;
|
| + // By construction the object `o` is an empty object with the same
|
| + // keys as the one we used in the extract-fields function.
|
| + var fieldNames = Object.keys(o);
|
| + if (fieldNames.length != fields.length) {
|
| + throw new Error("Mismatch during deserialization.");
|
| + }
|
| + for (var i = 0; i < fields.length; i++) {
|
| + o[fieldNames[i]] = fields[i];
|
| + }
|
| + return o;
|
| + }''');
|
| + globals.add(new js.Property(js.string(INITIALIZE_EMPTY_INSTANCE),
|
| + initializeEmptyInstanceFunction));
|
| }
|
|
|
| globals.add(emitMangledGlobalNames());
|
| @@ -1106,4 +1206,4 @@ class FragmentEmitter {
|
|
|
| return new js.Block(statements);
|
| }
|
| -}
|
| +}
|
|
|