| 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 |