| 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 name of the property that stores the tear-off getter on a static |
| 8 /// function. |
| 9 /// |
| 10 /// This property is only used when isolates are used. |
| 11 /// |
| 12 /// When serializing static functions we transmit the |
| 13 /// name of the static function, but not the name of the function's getter. We |
| 14 /// store the getter-function on the static function itself, which allows us to |
| 15 /// find it easily. |
| 16 const String tearOffPropertyName = r'$tearOff'; |
| 17 |
| 18 /// The name of the property that stores the list of fields on a constructor. |
| 19 /// |
| 20 /// This property is only used when isolates are used. |
| 21 /// |
| 22 /// When serializing objects we extract all fields from any given object. |
| 23 /// We extract the names of all fields from a fresh empty object. This list |
| 24 /// is cached on the constructor in this property to to avoid too many |
| 25 /// allocations. |
| 26 const String cachedClassFieldNames = r'$cachedFieldNames'; |
| 27 |
| 7 /// The fast startup emitter's goal is to minimize the amount of work that the | 28 /// 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. | 29 /// JavaScript engine has to do before it can start running user code. |
| 9 /// | 30 /// |
| 10 /// Whenever possible, the emitter uses object literals instead of updating | 31 /// Whenever possible, the emitter uses object literals instead of updating |
| 11 /// objects. | 32 /// objects. |
| 12 /// | 33 /// |
| 13 /// Example: | 34 /// Example: |
| 14 /// | 35 /// |
| 15 /// // Holders are initialized directly with the classes and static | 36 /// // Holders are initialized directly with the classes and static |
| 16 /// // functions. | 37 /// // functions. |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 135 // The argument [funsOrNames] is an array of strings or functions. If it is a | 156 // 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 | 157 // name, then the function should be fetched from the container. The first |
| 137 // entry in that array *must* be a string. | 158 // entry in that array *must* be a string. |
| 138 // | 159 // |
| 139 // TODO(floitsch): Change tearOffCode to accept the data directly, or create a | 160 // TODO(floitsch): Change tearOffCode to accept the data directly, or create a |
| 140 // different tearOffCode? | 161 // different tearOffCode? |
| 141 function installTearOff(container, getterName, | 162 function installTearOff(container, getterName, |
| 142 isStatic, isIntercepted, requiredParameterCount, | 163 isStatic, isIntercepted, requiredParameterCount, |
| 143 optionalParameterDefaultValues, | 164 optionalParameterDefaultValues, |
| 144 callNames, funsOrNames, funType) { | 165 callNames, funsOrNames, funType) { |
| 166 // A function can have several stubs (for example to fill in optional |
| 167 // arguments). We collect these functions in the `funs` array. |
| 145 var funs = []; | 168 var funs = []; |
| 146 for (var i = 0; i < funsOrNames.length; i++) { | 169 for (var i = 0; i < funsOrNames.length; i++) { |
| 147 var fun = funsOrNames[i]; | 170 var fun = funsOrNames[i]; |
| 148 if ((typeof fun) == 'string') fun = container[fun]; | 171 if ((typeof fun) == 'string') fun = container[fun]; |
| 149 fun.#callName = callNames[i]; | 172 fun.#callName = callNames[i]; |
| 150 funs.push(fun); | 173 funs.push(fun); |
| 151 } | 174 } |
| 152 | 175 |
| 153 funs[0][#argumentCount] = requiredParameterCount; | 176 // The main function to which all stubs redirect. |
| 154 funs[0][#defaultArgumentValues] = optionalParameterDefaultValues; | 177 var fun = funs[0]; |
| 178 |
| 179 fun[#argumentCount] = requiredParameterCount; |
| 180 fun[#defaultArgumentValues] = optionalParameterDefaultValues; |
| 155 var reflectionInfo = funType; | 181 var reflectionInfo = funType; |
| 156 if (typeof reflectionInfo == "number") { | 182 if (typeof reflectionInfo == "number") { |
| 157 // The reflectionInfo can either be a function, or a pointer into the types | 183 // The reflectionInfo can either be a function, or a pointer into the types |
| 158 // table. If it points into the types-table we need to update the index, | 184 // table. If it points into the types-table we need to update the index, |
| 159 // in case the tear-off is part of a deferred hunk. | 185 // in case the tear-off is part of a deferred hunk. |
| 160 reflectionInfo = reflectionInfo + typesOffset; | 186 reflectionInfo = reflectionInfo + typesOffset; |
| 161 } | 187 } |
| 162 var name = funsOrNames[0]; | 188 var name = funsOrNames[0]; |
| 163 container[getterName] = | 189 var getterFunction = |
| 164 tearOff(funs, reflectionInfo, isStatic, name, isIntercepted); | 190 tearOff(funs, reflectionInfo, isStatic, name, isIntercepted); |
| 191 container[getterName] = getterFunction; |
| 192 if (isStatic) { |
| 193 fun.$tearOffPropertyName = getterFunction; |
| 194 } |
| 165 } | 195 } |
| 166 | 196 |
| 167 // Instead of setting the interceptor tags directly we use this update | 197 // Instead of setting the interceptor tags directly we use this update |
| 168 // function. This makes it easier for deferred fragments to contribute to the | 198 // function. This makes it easier for deferred fragments to contribute to the |
| 169 // embedded global. | 199 // embedded global. |
| 170 function setOrUpdateInterceptorsByTag(newTags) { | 200 function setOrUpdateInterceptorsByTag(newTags) { |
| 171 var tags = #embeddedInterceptorTags; | 201 var tags = #embeddedInterceptorTags; |
| 172 if (!tags) { | 202 if (!tags) { |
| 173 #embeddedInterceptorTags = newTags; | 203 #embeddedInterceptorTags = newTags; |
| 174 return; | 204 return; |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 214 // Update the typesOffset for the next deferred library. | 244 // Update the typesOffset for the next deferred library. |
| 215 typesOffset = #embeddedTypes.length; | 245 typesOffset = #embeddedTypes.length; |
| 216 | 246 |
| 217 // TODO(floitsch): extend natives. | 247 // TODO(floitsch): extend natives. |
| 218 hunk(inherit, mixin, lazy, makeConstList, installTearOff, | 248 hunk(inherit, mixin, lazy, makeConstList, installTearOff, |
| 219 updateHolder, updateTypes, setOrUpdateInterceptorsByTag, | 249 updateHolder, updateTypes, setOrUpdateInterceptorsByTag, |
| 220 setOrUpdateLeafTags, | 250 setOrUpdateLeafTags, |
| 221 #embeddedGlobalsObject, #holdersList, #staticState); | 251 #embeddedGlobalsObject, #holdersList, #staticState); |
| 222 } | 252 } |
| 223 | 253 |
| 254 // Returns the global with the given [name]. |
| 255 function getGlobalFromName(name) { |
| 256 // TODO(floitsch): we are running through all holders. Since negative |
| 257 // lookups are expensive we might need to improve this. |
| 258 // Relies on the fact that all names are unique across all holders. |
| 259 for (var i = 0; i < holders.length; i++) { |
| 260 // The constant holder reuses the same names. Therefore we must skip it. |
| 261 if (holders[i] == #constantHolderReference) continue; |
| 262 // Relies on the fact that all variables are unique. |
| 263 if (holders[i][name]) return holders[i][name]; |
| 264 } |
| 265 } |
| 266 |
| 224 // Creates the holders. | 267 // Creates the holders. |
| 225 #holders; | 268 #holders; |
| 226 // TODO(floitsch): if name is not set (for example in IE), run through all | 269 // TODO(floitsch): if name is not set (for example in IE), run through all |
| 227 // functions and set the name. | 270 // functions and set the name. |
| 228 | 271 |
| 229 // TODO(floitsch): we should build this object as a literal. | 272 // TODO(floitsch): we should build this object as a literal. |
| 230 var #staticStateDeclaration = {}; | 273 var #staticStateDeclaration = {}; |
| 231 | 274 |
| 232 // Sets the prototypes of classes. | 275 // Sets the prototypes of classes. |
| 233 #prototypes; | 276 #prototypes; |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 343 'embeddedInterceptorTags': | 386 'embeddedInterceptorTags': |
| 344 generateEmbeddedGlobalAccess(INTERCEPTORS_BY_TAG), | 387 generateEmbeddedGlobalAccess(INTERCEPTORS_BY_TAG), |
| 345 'embeddedLeafTags': generateEmbeddedGlobalAccess(LEAF_TAGS), | 388 'embeddedLeafTags': generateEmbeddedGlobalAccess(LEAF_TAGS), |
| 346 'embeddedGlobalsObject': js.js("init"), | 389 'embeddedGlobalsObject': js.js("init"), |
| 347 'holdersList': new js.ArrayInitializer(program.holders.map((holder) { | 390 'holdersList': new js.ArrayInitializer(program.holders.map((holder) { |
| 348 return js.js("#", holder.name); | 391 return js.js("#", holder.name); |
| 349 }).toList()), | 392 }).toList()), |
| 350 'staticStateDeclaration': new js.VariableDeclaration( | 393 'staticStateDeclaration': new js.VariableDeclaration( |
| 351 namer.staticStateHolder, allowRename: false), | 394 namer.staticStateHolder, allowRename: false), |
| 352 'staticState': js.js('#', namer.staticStateHolder), | 395 'staticState': js.js('#', namer.staticStateHolder), |
| 396 'constantHolderReference': buildConstantHolderReference(program), |
| 353 'holders': emitHolders(program.holders, fragment), | 397 'holders': emitHolders(program.holders, fragment), |
| 354 'callName': js.string(namer.callNameField), | 398 'callName': js.string(namer.callNameField), |
| 355 'argumentCount': js.string(namer.requiredParameterField), | 399 'argumentCount': js.string(namer.requiredParameterField), |
| 356 'defaultArgumentValues': js.string(namer.defaultValuesField), | 400 'defaultArgumentValues': js.string(namer.defaultValuesField), |
| 357 'prototypes': emitPrototypes(fragment), | 401 'prototypes': emitPrototypes(fragment), |
| 358 'inheritance': emitInheritance(fragment), | 402 'inheritance': emitInheritance(fragment), |
| 359 'aliases': emitInstanceMethodAliases(fragment), | 403 'aliases': emitInstanceMethodAliases(fragment), |
| 360 'tearOffs': emitInstallTearOffs(fragment), | 404 'tearOffs': emitInstallTearOffs(fragment), |
| 361 'constants': emitConstants(fragment), | 405 'constants': emitConstants(fragment), |
| 362 'staticNonFinalFields': emitStaticNonFinalFields(fragment), | 406 'staticNonFinalFields': emitStaticNonFinalFields(fragment), |
| (...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 462 new js.VariableDeclarationList(holders | 506 new js.VariableDeclarationList(holders |
| 463 .map(emitHolderInitialization) | 507 .map(emitHolderInitialization) |
| 464 .toList())), | 508 .toList())), |
| 465 js.js.statement('var holders = #', new js.ArrayInitializer( | 509 js.js.statement('var holders = #', new js.ArrayInitializer( |
| 466 holders | 510 holders |
| 467 .map((holder) => new js.VariableUse(holder.name)) | 511 .map((holder) => new js.VariableUse(holder.name)) |
| 468 .toList(growable: false)))]; | 512 .toList(growable: false)))]; |
| 469 return new js.Block(statements); | 513 return new js.Block(statements); |
| 470 } | 514 } |
| 471 | 515 |
| 516 /// Returns a reference to the constant holder, or the JS-literal `null`. |
| 517 js.Expression buildConstantHolderReference(Program program) { |
| 518 Holder constantHolder = program.holders |
| 519 .firstWhere((Holder holder) => holder.isConstantsHolder, |
| 520 orElse: () => null); |
| 521 if (constantHolder == null) return new js.LiteralNull(); |
| 522 return new js.VariableUse(constantHolder.name); |
| 523 } |
| 524 |
| 472 /// Emits the given [method]. | 525 /// Emits the given [method]. |
| 473 /// | 526 /// |
| 474 /// A Dart method might result in several JavaScript functions, if it | 527 /// A Dart method might result in several JavaScript functions, if it |
| 475 /// requires stubs. The returned map contains the original method and all | 528 /// requires stubs. The returned map contains the original method and all |
| 476 /// the stubs it needs. | 529 /// the stubs it needs. |
| 477 Map<js.Name, js.Expression> emitStaticMethod(StaticMethod method) { | 530 Map<js.Name, js.Expression> emitStaticMethod(StaticMethod method) { |
| 478 Map<js.Name, js.Expression> jsMethods = <js.Name, js.Expression>{}; | 531 Map<js.Name, js.Expression> jsMethods = <js.Name, js.Expression>{}; |
| 479 | 532 |
| 480 // We don't need to install stub-methods. They can only be used when there | 533 // We don't need to install stub-methods. They can only be used when there |
| 481 // are tear-offs, in which case they are emitted there. | 534 // are tear-offs, in which case they are emitted there. |
| (...skipping 466 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 948 | 1001 |
| 949 return new js.Property(js.string(MANGLED_GLOBAL_NAMES), | 1002 return new js.Property(js.string(MANGLED_GLOBAL_NAMES), |
| 950 new js.ObjectInitializer(names)); | 1003 new js.ObjectInitializer(names)); |
| 951 } | 1004 } |
| 952 | 1005 |
| 953 /// Emits the [GET_TYPE_FROM_NAME] embedded global. | 1006 /// Emits the [GET_TYPE_FROM_NAME] embedded global. |
| 954 /// | 1007 /// |
| 955 /// This embedded global provides a way to go from a class name (which is | 1008 /// This embedded global provides a way to go from a class name (which is |
| 956 /// also the constructor's name) to the constructor itself. | 1009 /// also the constructor's name) to the constructor itself. |
| 957 js.Property emitGetTypeFromName() { | 1010 js.Property emitGetTypeFromName() { |
| 958 // TODO(floitsch): Fix getTypeFromName. It's too inefficient. | 1011 js.Expression function = js.js("getGlobalFromName"); |
| 959 // The current implementation relies on the fact that the names in holders | |
| 960 // are unique across all holders. | |
| 961 // TODO(floitsch): constants and other globals may share the same name. | |
| 962 // If a global happens to have the same (minified) name this code breaks. | |
| 963 // A follow-up CL has a fix for this. | |
| 964 js.Expression function = | |
| 965 js.js( """function(name) { | |
| 966 for (var i = 0; i < holders.length; i++) { | |
| 967 // Relies on the fact that all variables are unique. | |
| 968 if (holders[i][name]) return holders[i][name]; | |
| 969 } | |
| 970 }"""); | |
| 971 return new js.Property(js.string(GET_TYPE_FROM_NAME), function); | 1012 return new js.Property(js.string(GET_TYPE_FROM_NAME), function); |
| 972 } | 1013 } |
| 973 | 1014 |
| 974 /// Emits the [METADATA] embedded global. | 1015 /// Emits the [METADATA] embedded global. |
| 975 /// | 1016 /// |
| 976 /// The metadata itself has already been computed earlier and is stored in | 1017 /// The metadata itself has already been computed earlier and is stored in |
| 977 /// the [program]. | 1018 /// the [program]. |
| 978 List<js.Property> emitMetadata(Program program) { | 1019 List<js.Property> emitMetadata(Program program) { |
| 979 List<js.Property> metadataGlobals = <js.Property>[]; | 1020 List<js.Property> metadataGlobals = <js.Property>[]; |
| 980 | 1021 |
| (...skipping 26 matching lines...) Expand all Loading... |
| 1007 } | 1048 } |
| 1008 | 1049 |
| 1009 if (program.hasIsolateSupport) { | 1050 if (program.hasIsolateSupport) { |
| 1010 String staticStateName = namer.staticStateHolder; | 1051 String staticStateName = namer.staticStateHolder; |
| 1011 // TODO(floitsch): this doesn't create a new isolate, but just reuses | 1052 // TODO(floitsch): this doesn't create a new isolate, but just reuses |
| 1012 // the current static state. Since we don't run multiple isolates in the | 1053 // the current static state. Since we don't run multiple isolates in the |
| 1013 // same JavaScript context (except for testing) this shouldn't have any | 1054 // same JavaScript context (except for testing) this shouldn't have any |
| 1014 // impact on real-world programs, though. | 1055 // impact on real-world programs, though. |
| 1015 globals.add( | 1056 globals.add( |
| 1016 new js.Property(js.string(CREATE_NEW_ISOLATE), | 1057 new js.Property(js.string(CREATE_NEW_ISOLATE), |
| 1017 js.js('function () { return $staticStateName; }'))); | 1058 js.js('function () { return $staticStateName; }'))); |
| 1018 // TODO(floitsch): add remaining isolate functions. | 1059 |
| 1060 js.Expression nameToClosureFunction = js.js(''' |
| 1061 // First fetch the static function. From there we can execute its |
| 1062 // getter function which builds a Dart closure. |
| 1063 function(name) { |
| 1064 var staticFunction = getGlobalFromName(name); |
| 1065 var getterFunction = staticFunction.$tearOffPropertyName; |
| 1066 return getterFunction(); |
| 1067 }'''); |
| 1068 globals.add(new js.Property(js.string(STATIC_FUNCTION_NAME_TO_CLOSURE), |
| 1069 nameToClosureFunction)); |
| 1070 |
| 1071 globals.add( |
| 1072 new js.Property(js.string(CLASS_ID_EXTRACTOR), |
| 1073 js.js('function(o) { return o.constructor.name; }'))); |
| 1074 |
| 1075 js.Expression extractFieldsFunction = js.js(''' |
| 1076 function(o) { |
| 1077 var constructor = o.constructor; |
| 1078 var fieldNames = constructor.$cachedClassFieldNames; |
| 1079 if (!fieldNames) { |
| 1080 // Extract the fields from an empty unmodified object. |
| 1081 var empty = new constructor(); |
| 1082 // This gives us the keys that the constructor sets. |
| 1083 fieldNames = constructor.$cachedClassFieldNames = Object.keys(empty); |
| 1084 } |
| 1085 var result = new Array(fieldNames.length); |
| 1086 for (var i = 0; i < fieldNames.length; i++) { |
| 1087 result[i] = o[fieldNames[i]]; |
| 1088 } |
| 1089 return result; |
| 1090 }'''); |
| 1091 globals.add(new js.Property(js.string(CLASS_FIELDS_EXTRACTOR), |
| 1092 extractFieldsFunction)); |
| 1093 |
| 1094 js.Expression createInstanceFromClassIdFunction = js.js(''' |
| 1095 function(name) { |
| 1096 var constructor = getGlobalFromName(name); |
| 1097 return new constructor(); |
| 1098 } |
| 1099 '''); |
| 1100 globals.add(new js.Property(js.string(INSTANCE_FROM_CLASS_ID), |
| 1101 createInstanceFromClassIdFunction)); |
| 1102 |
| 1103 js.Expression initializeEmptyInstanceFunction = js.js(''' |
| 1104 function(name, o, fields) { |
| 1105 var constructor = o.constructor; |
| 1106 // By construction the object `o` is an empty object with the same |
| 1107 // keys as the one we used in the extract-fields function. |
| 1108 var fieldNames = Object.keys(o); |
| 1109 if (fieldNames.length != fields.length) { |
| 1110 throw new Error("Mismatch during deserialization."); |
| 1111 } |
| 1112 for (var i = 0; i < fields.length; i++) { |
| 1113 o[fieldNames[i]] = fields[i]; |
| 1114 } |
| 1115 return o; |
| 1116 }'''); |
| 1117 globals.add(new js.Property(js.string(INITIALIZE_EMPTY_INSTANCE), |
| 1118 initializeEmptyInstanceFunction)); |
| 1019 } | 1119 } |
| 1020 | 1120 |
| 1021 globals.add(emitMangledGlobalNames()); | 1121 globals.add(emitMangledGlobalNames()); |
| 1022 | 1122 |
| 1023 // The [MANGLED_NAMES] table is only relevant for reflection. | 1123 // The [MANGLED_NAMES] table is only relevant for reflection. |
| 1024 // TODO(floitsch): verify that this is correct. | 1124 // TODO(floitsch): verify that this is correct. |
| 1025 globals.add(new js.Property(js.string(MANGLED_NAMES), | 1125 globals.add(new js.Property(js.string(MANGLED_NAMES), |
| 1026 new js.ObjectInitializer([]))); | 1126 new js.ObjectInitializer([]))); |
| 1027 | 1127 |
| 1028 globals.add(emitGetTypeFromName()); | 1128 globals.add(emitGetTypeFromName()); |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1099 } | 1199 } |
| 1100 } | 1200 } |
| 1101 statements.add(js.js.statement("setOrUpdateInterceptorsByTag(#);", | 1201 statements.add(js.js.statement("setOrUpdateInterceptorsByTag(#);", |
| 1102 js.objectLiteral(interceptorsByTag))); | 1202 js.objectLiteral(interceptorsByTag))); |
| 1103 statements.add(js.js.statement("setOrUpdateLeafTags(#);", | 1203 statements.add(js.js.statement("setOrUpdateLeafTags(#);", |
| 1104 js.objectLiteral(leafTags))); | 1204 js.objectLiteral(leafTags))); |
| 1105 statements.add(subclassAssignment); | 1205 statements.add(subclassAssignment); |
| 1106 | 1206 |
| 1107 return new js.Block(statements); | 1207 return new js.Block(statements); |
| 1108 } | 1208 } |
| 1109 } | 1209 } |
| OLD | NEW |