OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 } |
OLD | NEW |