Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(175)

Side by Side Diff: pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart

Issue 1229923005: dart2js: support tear-offs in the startup emitter. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Fix bad assert. Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 part of dart2js.js_emitter.startup_emitter.model_emitter; 5 part of dart2js.js_emitter.startup_emitter.model_emitter;
6 6
7 /// The fast startup emitter's goal is to minimize the amount of work that the 7 /// The fast startup emitter's goal is to minimize the amount of work that the
8 /// JavaScript engine has to do before it can start running user code. 8 /// JavaScript engine has to do before it can start running user code.
9 /// 9 ///
10 /// Whenever possible, the emitter uses object literals instead of updating 10 /// Whenever possible, the emitter uses object literals instead of updating
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
105 // The runtime ensures that const-lists cannot be modified. 105 // The runtime ensures that const-lists cannot be modified.
106 function makeConstList(list) { 106 function makeConstList(list) {
107 // By assigning a function to the properties they become part of the 107 // By assigning a function to the properties they become part of the
108 // hidden class. The actual values of the fields don't matter, since we 108 // hidden class. The actual values of the fields don't matter, since we
109 // only check if they exist. 109 // only check if they exist.
110 list.immutable\$list = Array; 110 list.immutable\$list = Array;
111 list.fixed\$length = Array; 111 list.fixed\$length = Array;
112 return list; 112 return list;
113 } 113 }
114 114
115 // TODO(floitsch): provide code for tear-offs. 115 // This variable is used by the tearOffCode to guarantee unique functions per
116 // tear-offs.
117 var functionCounter = 0;
118 #tearOffCode;
119
120 // Each deferred hunk comes with its own types which are added to the end
121 // of the types-array.
122 // The `funTypes` passed to the `installTearOff` function below is relative to
123 // the hunk the function comes from. The `typesOffset` variable encodes the
124 // offset at which the new types will be added.
125 var typesOffset = 0;
126
127 // Adapts the stored data, so it's suitable for a tearOff call.
128 //
129 // Stores the tear-off getter-function in the [container]'s [getterName]
130 // property.
131 //
132 // The [container] is either a class (that is, its prototype), or the holder for
133 // static functions.
134 //
135 // The argument [funsOrNames] is an array of strings or functions. If it is a
136 // name, then the function should be fetched from the container. The first
137 // entry in that array *must* be a string.
138 //
139 // TODO(floitsch): Change tearOffCode to accept the data directly, or create a
140 // different tearOffCode?
141 function installTearOff(
142 container, getterName, isStatic, isIntercepted, requiredParameterCount,
143 optionalParameterDefaultValues, callNames, funsOrNames, funType) {
144 var funs = [];
145 for (var i = 0; i < funsOrNames.length; i++) {
146 var fun = funsOrNames[i];
147 if ((typeof fun) == 'string') fun = container[fun];
148 fun.#callName = callNames[i];
149 funs.push(fun);
150 }
151
152 funs[0][#argumentCount] = requiredParameterCount;
153 funs[0][#defaultArgumentValues] = optionalParameterDefaultValues;
154 var reflectionInfo = funType;
155 if (typeof reflectionInfo == "number") {
156 // The reflectionInfo can either be a function, or a pointer into the types
157 // table. If it points into the types-table we need to update the index,
158 // in case the tear-off is part of a deferred hunk.
159 reflectionInfo = reflectionInfo + typesOffset;
160 }
161 var name = funsOrNames[0];
162 container[getterName] =
163 tearOff(funs, reflectionInfo, isStatic, name, isIntercepted);
164 }
116 165
117 // Instead of setting the interceptor tags directly we use this update 166 // Instead of setting the interceptor tags directly we use this update
118 // function. This makes it easier for deferred fragments to contribute to the 167 // function. This makes it easier for deferred fragments to contribute to the
119 // embedded global. 168 // embedded global.
120 function setOrUpdateInterceptorsByTag(newTags) { 169 function setOrUpdateInterceptorsByTag(newTags) {
121 var tags = #embeddedInterceptorTags; 170 var tags = #embeddedInterceptorTags;
122 if (!tags) { 171 if (!tags) {
123 #embeddedInterceptorTags = newTags; 172 #embeddedInterceptorTags = newTags;
124 return; 173 return;
125 } 174 }
126 copyProperties(newTags, tags); 175 copyProperties(newTags, tags);
127 } 176 }
128 177
129 // Instead of setting the leaf tags directly we use this update 178 // Instead of setting the leaf tags directly we use this update
130 // function. This makes it easier for deferred fragments to contribute to the 179 // function. This makes it easier for deferred fragments to contribute to the
131 // embedded global. 180 // embedded global.
132 function setOrUpdateLeafTags(newTags) { 181 function setOrUpdateLeafTags(newTags) {
133 var tags = #embeddedLeafTags; 182 var tags = #embeddedLeafTags;
134 if (!tags) { 183 if (!tags) {
135 #embeddedLeafTags = newTags; 184 #embeddedLeafTags = newTags;
136 return; 185 return;
137 } 186 }
138 copyProperties(newTags, tags); 187 copyProperties(newTags, tags);
139 } 188 }
140 189
141 // Updates the types embedded global. 190 // Updates the types embedded global.
142 function updateTypes(newTypes) { 191 function updateTypes(newTypes) {
143 var types = #embeddedTypes; 192 var types = #embeddedTypes;
193 // This relies on the fact that types are added *after* the tear-offs have
194 // been installed. The tear-off function uses the types-length to figure
195 // out at which offset its types are located. If the types were added earlier
196 // the offset would be wrong.
144 types.push.apply(types, newTypes); 197 types.push.apply(types, newTypes);
145 } 198 }
146 199
147 // Updates the given holder with the properties of the [newHolder]. 200 // Updates the given holder with the properties of the [newHolder].
148 // This function is used when a deferred fragment is initialized. 201 // This function is used when a deferred fragment is initialized.
149 function updateHolder(holder, newHolder) { 202 function updateHolder(holder, newHolder) {
150 // TODO(floitsch): updating the prototype (instead of copying) is 203 // TODO(floitsch): updating the prototype (instead of copying) is
151 // *horribly* inefficient in Firefox. There we should just copy the 204 // *horribly* inefficient in Firefox. There we should just copy the
152 // properties. 205 // properties.
153 var oldPrototype = holder.__proto__; 206 var oldPrototype = holder.__proto__;
154 newHolder.__proto__ = oldPrototype; 207 newHolder.__proto__ = oldPrototype;
155 holder.__proto__ = newHolder; 208 holder.__proto__ = newHolder;
156 return holder; 209 return holder;
157 } 210 }
158 211
159 // Every deferred hunk (i.e. fragment) is a function that we can invoke to 212 // Every deferred hunk (i.e. fragment) is a function that we can invoke to
160 // initialize it. At this moment it contributes its data to the main hunk. 213 // initialize it. At this moment it contributes its data to the main hunk.
161 function initializeDeferredHunk(hunk) { 214 function initializeDeferredHunk(hunk) {
215 // Update the typesOffset for the next deferred library.
216 typesOffset = #embeddedTypes.length;
217
162 // TODO(floitsch): extend natives. 218 // TODO(floitsch): extend natives.
163 hunk(inherit, mixin, lazy, makeConstList, installTearOff, 219 hunk(inherit, mixin, lazy, makeConstList, installTearOff,
164 updateHolder, updateTypes, updateInterceptorsByTag, updateLeafTags, 220 updateHolder, updateTypes, updateInterceptorsByTag, updateLeafTags,
165 #embeddedGlobalsObject, #holdersList, #staticState); 221 #embeddedGlobalsObject, #holdersList, #staticState);
166 } 222 }
167 223
168 // Creates the holders. 224 // Creates the holders.
169 #holders; 225 #holders;
170 // TODO(floitsch): if name is not set (for example in IE), run through all 226 // TODO(floitsch): if name is not set (for example in IE), run through all
171 // functions and set the name. 227 // functions and set the name.
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
279 335
280 js.Statement emitMainFragment(Program program) { 336 js.Statement emitMainFragment(Program program) {
281 MainFragment fragment = program.fragments.first; 337 MainFragment fragment = program.fragments.first;
282 338
283 return js.js.statement(mainBoilerplate, 339 return js.js.statement(mainBoilerplate,
284 {'deferredInitializer': emitDeferredInitializerGlobal(program.loadMap), 340 {'deferredInitializer': emitDeferredInitializerGlobal(program.loadMap),
285 'typeNameProperty': js.string(ModelEmitter.typeNameProperty), 341 'typeNameProperty': js.string(ModelEmitter.typeNameProperty),
286 'cyclicThrow': backend.emitter.staticFunctionAccess( 342 'cyclicThrow': backend.emitter.staticFunctionAccess(
287 backend.getCyclicThrowHelper()), 343 backend.getCyclicThrowHelper()),
288 'operatorIsPrefix': js.string(namer.operatorIsPrefix), 344 'operatorIsPrefix': js.string(namer.operatorIsPrefix),
345 'tearOffCode': new js.Block(buildTearOffCode(backend)),
289 'embeddedTypes': generateEmbeddedGlobalAccess(TYPES), 346 'embeddedTypes': generateEmbeddedGlobalAccess(TYPES),
290 'embeddedInterceptorTags': 347 'embeddedInterceptorTags':
291 generateEmbeddedGlobalAccess(INTERCEPTORS_BY_TAG), 348 generateEmbeddedGlobalAccess(INTERCEPTORS_BY_TAG),
292 'embeddedLeafTags': generateEmbeddedGlobalAccess(LEAF_TAGS), 349 'embeddedLeafTags': generateEmbeddedGlobalAccess(LEAF_TAGS),
293 'embeddedGlobalsObject': js.js("init"), 350 'embeddedGlobalsObject': js.js("init"),
294 'holdersList': new js.ArrayInitializer(program.holders.map((holder) { 351 'holdersList': new js.ArrayInitializer(program.holders.map((holder) {
295 return js.js("#", holder.name); 352 return js.js("#", holder.name);
296 }).toList()), 353 }).toList()),
297 'staticStateDeclaration': new js.VariableDeclaration( 354 'staticStateDeclaration': new js.VariableDeclaration(
298 namer.staticStateHolder, allowRename: false), 355 namer.staticStateHolder, allowRename: false),
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after
420 } 477 }
421 478
422 /// Emits the given [method]. 479 /// Emits the given [method].
423 /// 480 ///
424 /// A Dart method might result in several JavaScript functions, if it 481 /// A Dart method might result in several JavaScript functions, if it
425 /// requires stubs. The returned map contains the original method and all 482 /// requires stubs. The returned map contains the original method and all
426 /// the stubs it needs. 483 /// the stubs it needs.
427 Map<js.Name, js.Expression> emitStaticMethod(StaticMethod method) { 484 Map<js.Name, js.Expression> emitStaticMethod(StaticMethod method) {
428 Map<js.Name, js.Expression> jsMethods = <js.Name, js.Expression>{}; 485 Map<js.Name, js.Expression> jsMethods = <js.Name, js.Expression>{};
429 486
487 // We don't need to install stub-methods. They can only be used when there
488 // are tear-offs, in which case they are emitted there.
489 assert(() {
490 if (method is StaticDartMethod) {
491 return method.needsTearOff || method.parameterStubs.isEmpty;
492 }
493 return true;
494 });
430 jsMethods[method.name] = method.code; 495 jsMethods[method.name] = method.code;
431 // TODO(floitsch): can there be anything else than a StaticDartMethod?
432 if (method is StaticDartMethod) {
433 for (ParameterStubMethod stubMethod in method.parameterStubs) {
434 jsMethods[stubMethod.name] = stubMethod.code;
435 }
436 }
437 496
438 return jsMethods; 497 return jsMethods;
439 } 498 }
440 499
441 /// Emits a constructor for the given class [cls]. 500 /// Emits a constructor for the given class [cls].
442 /// 501 ///
443 /// The constructor is statically built. 502 /// The constructor is statically built.
444 js.Expression emitConstructor(Class cls) { 503 js.Expression emitConstructor(Class cls) {
445 List<js.Name> fieldNames = const <js.Name>[]; 504 List<js.Name> fieldNames = const <js.Name>[];
446 505
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
481 // TODO(floitsch): we could avoid that property if we knew that it wasn't 540 // TODO(floitsch): we could avoid that property if we knew that it wasn't
482 // needed. 541 // needed.
483 js.Expression emitPrototype(Class cls) { 542 js.Expression emitPrototype(Class cls) {
484 Iterable<Method> methods = cls.methods; 543 Iterable<Method> methods = cls.methods;
485 Iterable<Method> isChecks = cls.isChecks; 544 Iterable<Method> isChecks = cls.isChecks;
486 Iterable<Method> callStubs = cls.callStubs; 545 Iterable<Method> callStubs = cls.callStubs;
487 Iterable<Method> typeVariableReaderStubs = cls.typeVariableReaderStubs; 546 Iterable<Method> typeVariableReaderStubs = cls.typeVariableReaderStubs;
488 Iterable<Method> noSuchMethodStubs = cls.noSuchMethodStubs; 547 Iterable<Method> noSuchMethodStubs = cls.noSuchMethodStubs;
489 Iterable<Method> gettersSetters = generateGettersSetters(cls); 548 Iterable<Method> gettersSetters = generateGettersSetters(cls);
490 Iterable<Method> allMethods = 549 Iterable<Method> allMethods =
491 [methods, isChecks, callStubs, typeVariableReaderStubs, 550 [methods, isChecks, callStubs, typeVariableReaderStubs,
492 noSuchMethodStubs, gettersSetters].expand((x) => x); 551 noSuchMethodStubs, gettersSetters].expand((x) => x);
493 552
494 List<js.Property> properties = <js.Property>[]; 553 List<js.Property> properties = <js.Property>[];
495 554
496 if (cls.superclass == null) { 555 if (cls.superclass == null) {
497 properties.add(new js.Property(js.string("constructor"), 556 properties.add(new js.Property(js.string("constructor"),
498 classReference(cls))); 557 classReference(cls)));
499 properties.add(new js.Property(namer.operatorIs(cls.element), 558 properties.add(new js.Property(namer.operatorIs(cls.element),
500 js.number(1))); 559 js.number(1)));
501 } 560 }
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after
627 [classReference(cls), js.quoteName(method.aliasName), 686 [classReference(cls), js.quoteName(method.aliasName),
628 classReference(cls), js.quoteName(method.name)])); 687 classReference(cls), js.quoteName(method.name)]));
629 688
630 } 689 }
631 } 690 }
632 } 691 }
633 } 692 }
634 return new js.Block(assignments); 693 return new js.Block(assignments);
635 } 694 }
636 695
696 /// Encodes the optional default values so that the runtime Function.apply
697 /// can use them.
698 js.Expression _encodeOptionalParameterDefaultValues(DartMethod method) {
699 // TODO(herhut): Replace [js.LiteralNull] with [js.ArrayHole].
700 if (method.optionalParameterDefaultValues is List) {
701 List<ConstantValue> defaultValues = method.optionalParameterDefaultValues;
702 Iterable<js.Expression> elements =
703 defaultValues.map(generateConstantReference);
704 return new js.ArrayInitializer(elements.toList());
705 } else {
706 Map<String, ConstantValue> defaultValues =
707 method.optionalParameterDefaultValues;
708 List<js.Property> properties = <js.Property>[];
709 defaultValues.forEach((String name, ConstantValue value) {
710 properties.add(new js.Property(js.string(name),
711 generateConstantReference(value)));
712 });
713 return new js.ObjectInitializer(properties);
714 }
715 }
716
717 /// Emits the statement that installs a tear off for a method.
718 ///
719 /// Tear-offs might be passed to `Function.apply` which means that all
720 /// calling-conventions (with or without optional positional/named arguments)
721 /// are possible. As such, the tear-off needs enough information to fill in
722 /// missing parameters.
723 js.Statement emitInstallTearOff(js.Expression container, DartMethod method) {
724 List<js.Name> callNames = <js.Name>[];
725 List<js.Expression> funsOrNames = <js.Expression>[];
726
727 /// Adds the stub-method's code or name to the [funsOrNames] array.
728 ///
729 /// Static methods don't need stub-methods except for tear-offs. As such,
730 /// they are not emitted in the prototype, but directly passed here.
731 ///
732 /// Instance-methods install the stub-methods in their prototype, and we
733 /// use string-based redirections to find them there.
734 void addFunOrName(StubMethod stubMethod) {
735 if (method.isStatic) {
736 funsOrNames.add(stubMethod.code);
737 } else {
738 funsOrNames.add(js.quoteName(stubMethod.name));
739 }
740 }
741
742 callNames.add(method.callName);
743 // The first entry in the funsOrNames-array must be a string.
744 funsOrNames.add(js.quoteName(method.name));
745 for (ParameterStubMethod stubMethod in method.parameterStubs) {
746 callNames.add(stubMethod.callName);
747 addFunOrName(stubMethod);
748 }
749
750 js.ArrayInitializer callNameArray =
751 new js.ArrayInitializer(callNames.map(js.quoteName).toList());
752 js.ArrayInitializer funsOrNamesArray = new js.ArrayInitializer(funsOrNames);
753
754 bool isIntercepted = false;
755 if (method is InstanceMethod) {
756 isIntercepted = backend.isInterceptedMethod(method.element);
757 }
758 int requiredParameterCount = 0;
759 js.Expression optionalParameterDefaultValues = new js.LiteralNull();
760 if (method.canBeApplied) {
761 requiredParameterCount = method.requiredParameterCount;
762 optionalParameterDefaultValues =
763 _encodeOptionalParameterDefaultValues(method);
764 }
765
766 return js.js.statement('''
767 installTearOff(#container, #getterName, #isStatic, #isIntercepted,
768 #requiredParameterCount, #optionalParameterDefaultValues,
769 #callNames, #funsOrNames, #funType)''',
770 {
771 "container": container,
772 "getterName": js.quoteName(method.tearOffName),
773 "isStatic": new js.LiteralBool(method.isStatic),
774 "isIntercepted": new js.LiteralBool(isIntercepted),
775 "requiredParameterCount": js.number(requiredParameterCount),
776 "optionalParameterDefaultValues": optionalParameterDefaultValues,
777 "callNames": callNameArray,
778 "funsOrNames": funsOrNamesArray,
779 "funType": method.functionType,
780 });
781 }
782
637 /// Emits the section that installs tear-off getters. 783 /// Emits the section that installs tear-off getters.
638 js.Statement emitInstallTearOffs(fragment) { 784 js.Statement emitInstallTearOffs(Fragment fragment) {
639 throw new UnimplementedError('emitInstallTearOffs'); 785 List<js.Statement> inits = <js.Statement>[];
786
787 for (Library library in fragment.libraries) {
788 for (StaticMethod method in library.statics) {
789 // TODO(floitsch): can there be anything else than a StaticDartMethod?
790 if (method is StaticDartMethod) {
791 if (method.needsTearOff) {
792 Holder holder = method.holder;
793 inits.add(
794 emitInstallTearOff(new js.VariableUse(holder.name), method));
795 }
796 }
797 }
798 for (Class cls in library.classes) {
799 for (InstanceMethod method in cls.methods) {
800 if (method.needsTearOff) {
801 js.Expression container = js.js("#.prototype", classReference(cls));
802 inits.add(emitInstallTearOff(container, method));
803 }
804 }
805 }
806 }
807 return new js.Block(inits);
640 } 808 }
641 809
642 /// Emits the constants section. 810 /// Emits the constants section.
643 js.Statement emitConstants(Fragment fragment) { 811 js.Statement emitConstants(Fragment fragment) {
644 List<js.Statement> assignments = <js.Statement>[]; 812 List<js.Statement> assignments = <js.Statement>[];
645 for (Constant constant in fragment.constants) { 813 for (Constant constant in fragment.constants) {
646 // TODO(floitsch): instead of just updating the constant holder, we should 814 // TODO(floitsch): instead of just updating the constant holder, we should
647 // find the constants that don't have any dependency on other constants 815 // find the constants that don't have any dependency on other constants
648 // and create an object-literal with them (and assign it to the 816 // and create an object-literal with them (and assign it to the
649 // constant-holder variable). 817 // constant-holder variable).
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
694 } 862 }
695 863
696 emitEmbeddedGlobals(program) { 864 emitEmbeddedGlobals(program) {
697 throw new UnimplementedError('emitEmbeddedGlobals'); 865 throw new UnimplementedError('emitEmbeddedGlobals');
698 } 866 }
699 867
700 emitNativeSupport(fragment) { 868 emitNativeSupport(fragment) {
701 throw new UnimplementedError('emitNativeSupport'); 869 throw new UnimplementedError('emitNativeSupport');
702 } 870 }
703 } 871 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/js_emitter/startup_emitter/emitter.dart ('k') | pkg/compiler/lib/src/patch_parser.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698