| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
| 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. | |
| 4 | |
| 5 part of dart2js.js_emitter; | |
| 6 | |
| 7 | |
| 8 class OldEmitter implements Emitter { | |
| 9 final Compiler compiler; | |
| 10 final CodeEmitterTask task; | |
| 11 | |
| 12 final ContainerBuilder containerBuilder = new ContainerBuilder(); | |
| 13 final ClassEmitter classEmitter = new ClassEmitter(); | |
| 14 final NsmEmitter nsmEmitter = new NsmEmitter(); | |
| 15 TypeTestEmitter get typeTestEmitter => task.typeTestEmitter; | |
| 16 final InterceptorEmitter interceptorEmitter = new InterceptorEmitter(); | |
| 17 final MetadataEmitter metadataEmitter = new MetadataEmitter(); | |
| 18 | |
| 19 final Set<ConstantValue> cachedEmittedConstants; | |
| 20 final CodeBuffer cachedEmittedConstantsBuffer = new CodeBuffer(); | |
| 21 final Map<Element, ClassBuilder> cachedClassBuilders; | |
| 22 final Set<Element> cachedElements; | |
| 23 | |
| 24 bool needsDefineClass = false; | |
| 25 bool needsMixinSupport = false; | |
| 26 bool needsLazyInitializer = false; | |
| 27 final Namer namer; | |
| 28 ConstantEmitter constantEmitter; | |
| 29 NativeEmitter get nativeEmitter => task.nativeEmitter; | |
| 30 | |
| 31 // The full code that is written to each hunk part-file. | |
| 32 Map<OutputUnit, CodeBuffer> outputBuffers = new Map<OutputUnit, CodeBuffer>(); | |
| 33 final CodeBuffer deferredConstants = new CodeBuffer(); | |
| 34 | |
| 35 /** Shorter access to [isolatePropertiesName]. Both here in the code, as | |
| 36 well as in the generated code. */ | |
| 37 String isolateProperties; | |
| 38 String classesCollector; | |
| 39 Set<ClassElement> get neededClasses => task.neededClasses; | |
| 40 Map<OutputUnit, List<ClassElement>> get outputClassLists | |
| 41 => task.outputClassLists; | |
| 42 Map<OutputUnit, List<ConstantValue>> get outputConstantLists | |
| 43 => task.outputConstantLists; | |
| 44 List<ClassElement> get nativeClasses => task.nativeClasses; | |
| 45 final Map<String, String> mangledFieldNames = <String, String>{}; | |
| 46 final Map<String, String> mangledGlobalFieldNames = <String, String>{}; | |
| 47 final Set<String> recordedMangledNames = new Set<String>(); | |
| 48 | |
| 49 final Map<ClassElement, Map<String, jsAst.Expression>> additionalProperties = | |
| 50 new Map<ClassElement, Map<String, jsAst.Expression>>(); | |
| 51 | |
| 52 List<TypedefElement> get typedefsNeededForReflection => | |
| 53 task.typedefsNeededForReflection; | |
| 54 | |
| 55 JavaScriptBackend get backend => compiler.backend; | |
| 56 TypeVariableHandler get typeVariableHandler => backend.typeVariableHandler; | |
| 57 | |
| 58 String get _ => space; | |
| 59 String get space => compiler.enableMinification ? "" : " "; | |
| 60 String get n => compiler.enableMinification ? "" : "\n"; | |
| 61 String get N => compiler.enableMinification ? "\n" : ";\n"; | |
| 62 | |
| 63 CodeBuffer getBuffer(OutputUnit outputUnit) { | |
| 64 return outputBuffers.putIfAbsent(outputUnit, () => new CodeBuffer()); | |
| 65 } | |
| 66 | |
| 67 CodeBuffer get mainBuffer { | |
| 68 return getBuffer(compiler.deferredLoadTask.mainOutputUnit); | |
| 69 } | |
| 70 | |
| 71 /** | |
| 72 * List of expressions and statements that will be included in the | |
| 73 * precompiled function. | |
| 74 * | |
| 75 * To save space, dart2js normally generates constructors and accessors | |
| 76 * dynamically. This doesn't work in CSP mode, and may impact startup time | |
| 77 * negatively. So dart2js will emit these functions to a separate file that | |
| 78 * can be optionally included to support CSP mode or for faster startup. | |
| 79 */ | |
| 80 Map<OutputUnit, List<jsAst.Node>> _cspPrecompiledFunctions = | |
| 81 new Map<OutputUnit, List<jsAst.Node>>(); | |
| 82 | |
| 83 Map<OutputUnit, List<jsAst.Expression>> _cspPrecompiledConstructorNames = | |
| 84 new Map<OutputUnit, List<jsAst.Expression>>(); | |
| 85 | |
| 86 /** | |
| 87 * Accumulate properties for classes and libraries, describing their | |
| 88 * static/top-level members. | |
| 89 * Later, these members are emitted when the class or library is emitted. | |
| 90 * | |
| 91 * See [getElementDescriptor]. | |
| 92 */ | |
| 93 // TODO(ahe): Generate statics with their class, and store only libraries in | |
| 94 // this map. | |
| 95 final Map<Element, ClassBuilder> elementDescriptors = | |
| 96 new Map<Element, ClassBuilder>(); | |
| 97 | |
| 98 final bool generateSourceMap; | |
| 99 | |
| 100 OldEmitter(Compiler compiler, Namer namer, this.generateSourceMap, this.task) | |
| 101 : this.compiler = compiler, | |
| 102 this.namer = namer, | |
| 103 cachedEmittedConstants = compiler.cacheStrategy.newSet(), | |
| 104 cachedClassBuilders = compiler.cacheStrategy.newMap(), | |
| 105 cachedElements = compiler.cacheStrategy.newSet() { | |
| 106 constantEmitter = | |
| 107 new ConstantEmitter(compiler, namer, makeConstantListTemplate); | |
| 108 containerBuilder.emitter = this; | |
| 109 classEmitter.emitter = this; | |
| 110 nsmEmitter.emitter = this; | |
| 111 interceptorEmitter.emitter = this; | |
| 112 metadataEmitter.emitter = this; | |
| 113 } | |
| 114 | |
| 115 List<jsAst.Node> cspPrecompiledFunctionFor(OutputUnit outputUnit) { | |
| 116 return _cspPrecompiledFunctions.putIfAbsent( | |
| 117 outputUnit, | |
| 118 () => new List<jsAst.Node>()); | |
| 119 } | |
| 120 | |
| 121 List<jsAst.Expression> cspPrecompiledConstructorNamesFor( | |
| 122 OutputUnit outputUnit) { | |
| 123 return _cspPrecompiledConstructorNames.putIfAbsent( | |
| 124 outputUnit, | |
| 125 () => new List<jsAst.Expression>()); | |
| 126 } | |
| 127 | |
| 128 /// Erases the precompiled information for csp mode for all output units. | |
| 129 /// Used by the incremental compiler. | |
| 130 void clearCspPrecompiledNodes() { | |
| 131 _cspPrecompiledFunctions.clear(); | |
| 132 _cspPrecompiledConstructorNames.clear(); | |
| 133 } | |
| 134 | |
| 135 void addComment(String comment, CodeBuffer buffer) { | |
| 136 buffer.write(jsAst.prettyPrint(js.comment(comment), compiler)); | |
| 137 } | |
| 138 | |
| 139 jsAst.Expression constantReference(ConstantValue value) { | |
| 140 return constantEmitter.reference(value); | |
| 141 } | |
| 142 | |
| 143 jsAst.Expression constantInitializerExpression(ConstantValue value) { | |
| 144 return constantEmitter.initializationExpression(value); | |
| 145 } | |
| 146 | |
| 147 String get name => 'CodeEmitter'; | |
| 148 | |
| 149 String get currentGenerateAccessorName | |
| 150 => '${namer.currentIsolate}.\$generateAccessor'; | |
| 151 String get generateAccessorHolder | |
| 152 => '$isolatePropertiesName.\$generateAccessor'; | |
| 153 String get finishClassesProperty | |
| 154 => r'$finishClasses'; | |
| 155 String get finishClassesName | |
| 156 => '${namer.isolateName}.$finishClassesProperty'; | |
| 157 String get finishIsolateConstructorName | |
| 158 => '${namer.isolateName}.\$finishIsolateConstructor'; | |
| 159 String get isolatePropertiesName | |
| 160 => '${namer.isolateName}.${namer.isolatePropertiesName}'; | |
| 161 String get lazyInitializerName | |
| 162 => '${namer.isolateName}.\$lazy'; | |
| 163 String get initName => 'init'; | |
| 164 String get makeConstListProperty | |
| 165 => namer.getMappedInstanceName('makeConstantList'); | |
| 166 | |
| 167 /// For deferred loading we communicate the initializers via this global var. | |
| 168 final String deferredInitializers = r"$dart_deferred_initializers"; | |
| 169 | |
| 170 /// All the global state can be passed around with this variable. | |
| 171 String get globalsHolder => namer.getMappedGlobalName("globalsHolder"); | |
| 172 | |
| 173 jsAst.Expression generateEmbeddedGlobalAccess(String global) { | |
| 174 return js(generateEmbeddedGlobalAccessString(global)); | |
| 175 } | |
| 176 | |
| 177 String generateEmbeddedGlobalAccessString(String global) { | |
| 178 // TODO(floitsch): don't use 'init' as global embedder storage. | |
| 179 return '$initName.$global'; | |
| 180 } | |
| 181 | |
| 182 jsAst.FunctionDeclaration get generateAccessorFunction { | |
| 183 const RANGE1_SIZE = RANGE1_LAST - RANGE1_FIRST + 1; | |
| 184 const RANGE2_SIZE = RANGE2_LAST - RANGE2_FIRST + 1; | |
| 185 const RANGE1_ADJUST = - (FIRST_FIELD_CODE - RANGE1_FIRST); | |
| 186 const RANGE2_ADJUST = - (FIRST_FIELD_CODE + RANGE1_SIZE - RANGE2_FIRST); | |
| 187 const RANGE3_ADJUST = | |
| 188 - (FIRST_FIELD_CODE + RANGE1_SIZE + RANGE2_SIZE - RANGE3_FIRST); | |
| 189 | |
| 190 String receiverParamName = compiler.enableMinification ? "r" : "receiver"; | |
| 191 String valueParamName = compiler.enableMinification ? "v" : "value"; | |
| 192 String reflectableField = namer.reflectableField; | |
| 193 | |
| 194 return js.statement(''' | |
| 195 function generateAccessor(fieldDescriptor, accessors, cls) { | |
| 196 var fieldInformation = fieldDescriptor.split("-"); | |
| 197 var field = fieldInformation[0]; | |
| 198 var len = field.length; | |
| 199 var code = field.charCodeAt(len - 1); | |
| 200 var reflectable; | |
| 201 if (fieldInformation.length > 1) reflectable = true; | |
| 202 else reflectable = false; | |
| 203 code = ((code >= $RANGE1_FIRST) && (code <= $RANGE1_LAST)) | |
| 204 ? code - $RANGE1_ADJUST | |
| 205 : ((code >= $RANGE2_FIRST) && (code <= $RANGE2_LAST)) | |
| 206 ? code - $RANGE2_ADJUST | |
| 207 : ((code >= $RANGE3_FIRST) && (code <= $RANGE3_LAST)) | |
| 208 ? code - $RANGE3_ADJUST | |
| 209 : $NO_FIELD_CODE; | |
| 210 | |
| 211 if (code) { // needsAccessor | |
| 212 var getterCode = code & 3; | |
| 213 var setterCode = code >> 2; | |
| 214 var accessorName = field = field.substring(0, len - 1); | |
| 215 | |
| 216 var divider = field.indexOf(":"); | |
| 217 if (divider > 0) { // Colon never in first position. | |
| 218 accessorName = field.substring(0, divider); | |
| 219 field = field.substring(divider + 1); | |
| 220 } | |
| 221 | |
| 222 if (getterCode) { // needsGetter | |
| 223 var args = (getterCode & 2) ? "$receiverParamName" : ""; | |
| 224 var receiver = (getterCode & 1) ? "this" : "$receiverParamName"; | |
| 225 var body = "return " + receiver + "." + field; | |
| 226 var property = | |
| 227 cls + ".prototype.${namer.getterPrefix}" + accessorName + "="; | |
| 228 var fn = "function(" + args + "){" + body + "}"; | |
| 229 if (reflectable) | |
| 230 accessors.push(property + "\$reflectable(" + fn + ");\\n"); | |
| 231 else | |
| 232 accessors.push(property + fn + ";\\n"); | |
| 233 } | |
| 234 | |
| 235 if (setterCode) { // needsSetter | |
| 236 var args = (setterCode & 2) | |
| 237 ? "$receiverParamName,${_}$valueParamName" | |
| 238 : "$valueParamName"; | |
| 239 var receiver = (setterCode & 1) ? "this" : "$receiverParamName"; | |
| 240 var body = receiver + "." + field + "$_=$_$valueParamName"; | |
| 241 var property = | |
| 242 cls + ".prototype.${namer.setterPrefix}" + accessorName + "="; | |
| 243 var fn = "function(" + args + "){" + body + "}"; | |
| 244 if (reflectable) | |
| 245 accessors.push(property + "\$reflectable(" + fn + ");\\n"); | |
| 246 else | |
| 247 accessors.push(property + fn + ";\\n"); | |
| 248 } | |
| 249 } | |
| 250 | |
| 251 return field; | |
| 252 }'''); | |
| 253 } | |
| 254 | |
| 255 List get defineClassFunction { | |
| 256 // First the class name, then the field names in an array and the members | |
| 257 // (inside an Object literal). | |
| 258 // The caller can also pass in the constructor as a function if needed. | |
| 259 // | |
| 260 // Example: | |
| 261 // defineClass("A", ["x", "y"], { | |
| 262 // foo$1: function(y) { | |
| 263 // print(this.x + y); | |
| 264 // }, | |
| 265 // bar$2: function(t, v) { | |
| 266 // this.x = t - v; | |
| 267 // }, | |
| 268 // }); | |
| 269 | |
| 270 var defineClass = js('''function(name, cls, fields) { | |
| 271 var accessors = []; | |
| 272 | |
| 273 var str = "function " + cls + "("; | |
| 274 var body = ""; | |
| 275 | |
| 276 for (var i = 0; i < fields.length; i++) { | |
| 277 if(i != 0) str += ", "; | |
| 278 | |
| 279 var field = generateAccessor(fields[i], accessors, cls); | |
| 280 var parameter = "parameter_" + field; | |
| 281 str += parameter; | |
| 282 body += ("this." + field + " = " + parameter + ";\\n"); | |
| 283 } | |
| 284 str += ") {\\n" + body + "}\\n"; | |
| 285 str += cls + ".builtin\$cls=\\"" + name + "\\";\\n"; | |
| 286 str += "\$desc=\$collectedClasses." + cls + ";\\n"; | |
| 287 str += "if(\$desc instanceof Array) \$desc = \$desc[1];\\n"; | |
| 288 str += cls + ".prototype = \$desc;\\n"; | |
| 289 if (typeof defineClass.name != "string") { | |
| 290 str += cls + ".name=\\"" + cls + "\\";\\n"; | |
| 291 } | |
| 292 str += accessors.join(""); | |
| 293 | |
| 294 return str; | |
| 295 }'''); | |
| 296 // Declare a function called "generateAccessor". This is used in | |
| 297 // defineClassFunction (it's a local declaration in init()). | |
| 298 return [ | |
| 299 generateAccessorFunction, | |
| 300 js('$generateAccessorHolder = generateAccessor'), | |
| 301 new jsAst.FunctionDeclaration( | |
| 302 new jsAst.VariableDeclaration('defineClass'), defineClass) ]; | |
| 303 } | |
| 304 | |
| 305 /** Needs defineClass to be defined. */ | |
| 306 List buildInheritFrom() { | |
| 307 return [js(r''' | |
| 308 var inheritFrom = function() { | |
| 309 function tmp() {} | |
| 310 var hasOwnProperty = Object.prototype.hasOwnProperty; | |
| 311 return function (constructor, superConstructor) { | |
| 312 tmp.prototype = superConstructor.prototype; | |
| 313 var object = new tmp(); | |
| 314 var properties = constructor.prototype; | |
| 315 for (var member in properties) | |
| 316 if (hasOwnProperty.call(properties, member)) | |
| 317 object[member] = properties[member]; | |
| 318 object.constructor = constructor; | |
| 319 constructor.prototype = object; | |
| 320 return object; | |
| 321 }; | |
| 322 }() | |
| 323 ''')]; | |
| 324 } | |
| 325 | |
| 326 jsAst.Fun get finishClassesFunction { | |
| 327 // Class descriptions are collected in a JS object. | |
| 328 // 'finishClasses' takes all collected descriptions and sets up | |
| 329 // the prototype. | |
| 330 // Once set up, the constructors prototype field satisfy: | |
| 331 // - it contains all (local) members. | |
| 332 // - its internal prototype (__proto__) points to the superclass' | |
| 333 // prototype field. | |
| 334 // - the prototype's constructor field points to the JavaScript | |
| 335 // constructor. | |
| 336 // For engines where we have access to the '__proto__' we can manipulate | |
| 337 // the object literal directly. For other engines we have to create a new | |
| 338 // object and copy over the members. | |
| 339 | |
| 340 String reflectableField = namer.reflectableField; | |
| 341 jsAst.Expression allClassesAccess = | |
| 342 generateEmbeddedGlobalAccess(embeddedNames.ALL_CLASSES); | |
| 343 jsAst.Expression metadataAccess = | |
| 344 generateEmbeddedGlobalAccess(embeddedNames.METADATA); | |
| 345 jsAst.Expression interceptorsByTagAccess = | |
| 346 generateEmbeddedGlobalAccess(embeddedNames.INTERCEPTORS_BY_TAG); | |
| 347 jsAst.Expression leafTagsAccess = | |
| 348 generateEmbeddedGlobalAccess(embeddedNames.LEAF_TAGS); | |
| 349 | |
| 350 return js(''' | |
| 351 function(collectedClasses, isolateProperties, existingIsolateProperties) { | |
| 352 var pendingClasses = Object.create(null); | |
| 353 if (!#) # = Object.create(null); // embedded allClasses. | |
| 354 var allClasses = #; // embedded allClasses; | |
| 355 | |
| 356 if (#) // DEBUG_FAST_OBJECTS | |
| 357 print("Number of classes: " + | |
| 358 Object.getOwnPropertyNames(\$\$).length); | |
| 359 | |
| 360 var hasOwnProperty = Object.prototype.hasOwnProperty; | |
| 361 | |
| 362 if (typeof dart_precompiled == "function") { | |
| 363 var constructors = dart_precompiled(collectedClasses); | |
| 364 } else { | |
| 365 var combinedConstructorFunction = | |
| 366 "function \$reflectable(fn){fn.$reflectableField=1;return fn};\\n"+ | |
| 367 "var \$desc;\\n"; | |
| 368 var constructorsList = []; | |
| 369 } | |
| 370 | |
| 371 for (var cls in collectedClasses) { | |
| 372 var desc = collectedClasses[cls]; | |
| 373 if (desc instanceof Array) desc = desc[1]; | |
| 374 | |
| 375 /* The 'fields' are either a constructor function or a | |
| 376 * string encoding fields, constructor and superclass. Get | |
| 377 * the superclass and the fields in the format | |
| 378 * '[name/]Super;field1,field2' | |
| 379 * from the CLASS_DESCRIPTOR_PROPERTY property on the descriptor. | |
| 380 * The 'name/' is optional and contains the name that should be used | |
| 381 * when printing the runtime type string. It is used, for example, | |
| 382 * to print the runtime type JSInt as 'int'. | |
| 383 */ | |
| 384 var classData = desc["${namer.classDescriptorProperty}"], | |
| 385 supr, name = cls, fields = classData; | |
| 386 if (#) // backend.hasRetainedMetadata | |
| 387 if (typeof classData == "object" && | |
| 388 classData instanceof Array) { | |
| 389 classData = fields = classData[0]; | |
| 390 } | |
| 391 if (typeof classData == "string") { | |
| 392 var split = classData.split("/"); | |
| 393 if (split.length == 2) { | |
| 394 name = split[0]; | |
| 395 fields = split[1]; | |
| 396 } | |
| 397 } | |
| 398 | |
| 399 var s = fields.split(";"); | |
| 400 fields = s[1] == "" ? [] : s[1].split(","); | |
| 401 supr = s[0]; | |
| 402 split = supr.split(":"); | |
| 403 if (split.length == 2) { | |
| 404 supr = split[0]; | |
| 405 var functionSignature = split[1]; | |
| 406 if (functionSignature) | |
| 407 desc.\$signature = (function(s) { | |
| 408 return function(){ return #[s]; }; // embedded metadata. | |
| 409 })(functionSignature); | |
| 410 } | |
| 411 | |
| 412 if (#) // needsMixinSupport | |
| 413 if (supr && supr.indexOf("+") > 0) { | |
| 414 s = supr.split("+"); | |
| 415 supr = s[0]; | |
| 416 var mixin = collectedClasses[s[1]]; | |
| 417 if (mixin instanceof Array) mixin = mixin[1]; | |
| 418 for (var d in mixin) { | |
| 419 if (hasOwnProperty.call(mixin, d) && | |
| 420 !hasOwnProperty.call(desc, d)) | |
| 421 desc[d] = mixin[d]; | |
| 422 } | |
| 423 } | |
| 424 | |
| 425 if (typeof dart_precompiled != "function") { | |
| 426 combinedConstructorFunction += defineClass(name, cls, fields); | |
| 427 constructorsList.push(cls); | |
| 428 } | |
| 429 if (supr) pendingClasses[cls] = supr; | |
| 430 } | |
| 431 | |
| 432 if (typeof dart_precompiled != "function") { | |
| 433 combinedConstructorFunction += | |
| 434 "return [\\n " + constructorsList.join(",\\n ") + "\\n]"; | |
| 435 var constructors = | |
| 436 new Function("\$collectedClasses", combinedConstructorFunction) | |
| 437 (collectedClasses); | |
| 438 combinedConstructorFunction = null; | |
| 439 } | |
| 440 | |
| 441 for (var i = 0; i < constructors.length; i++) { | |
| 442 var constructor = constructors[i]; | |
| 443 var cls = constructor.name; | |
| 444 var desc = collectedClasses[cls]; | |
| 445 var globalObject = isolateProperties; | |
| 446 if (desc instanceof Array) { | |
| 447 globalObject = desc[0] || isolateProperties; | |
| 448 desc = desc[1]; | |
| 449 } | |
| 450 if (#) //backend.isTreeShakingDisabled, | |
| 451 constructor["${namer.metadataField}"] = desc; | |
| 452 allClasses[cls] = constructor; | |
| 453 globalObject[cls] = constructor; | |
| 454 } | |
| 455 | |
| 456 constructors = null; | |
| 457 | |
| 458 var finishedClasses = Object.create(null); | |
| 459 # = Object.create(null); // embedded interceptorsByTag. | |
| 460 # = Object.create(null); // embedded leafTags. | |
| 461 | |
| 462 #; // buildFinishClass(), | |
| 463 | |
| 464 #; // buildTrivialNsmHandlers() | |
| 465 | |
| 466 for (var cls in pendingClasses) finishClass(cls); | |
| 467 }''', [ | |
| 468 allClassesAccess, allClassesAccess, | |
| 469 allClassesAccess, | |
| 470 DEBUG_FAST_OBJECTS, | |
| 471 backend.hasRetainedMetadata, | |
| 472 metadataAccess, | |
| 473 needsMixinSupport, | |
| 474 backend.isTreeShakingDisabled, | |
| 475 interceptorsByTagAccess, | |
| 476 leafTagsAccess, | |
| 477 buildFinishClass(), | |
| 478 nsmEmitter.buildTrivialNsmHandlers()]); | |
| 479 } | |
| 480 | |
| 481 jsAst.Node optional(bool condition, jsAst.Node node) { | |
| 482 return condition ? node : new jsAst.EmptyStatement(); | |
| 483 } | |
| 484 | |
| 485 jsAst.FunctionDeclaration buildFinishClass() { | |
| 486 String specProperty = '"${namer.nativeSpecProperty}"'; // "%" | |
| 487 | |
| 488 jsAst.Expression interceptorsByTagAccess = | |
| 489 generateEmbeddedGlobalAccess(embeddedNames.INTERCEPTORS_BY_TAG); | |
| 490 jsAst.Expression leafTagsAccess = | |
| 491 generateEmbeddedGlobalAccess(embeddedNames.LEAF_TAGS); | |
| 492 | |
| 493 return js.statement(''' | |
| 494 function finishClass(cls) { | |
| 495 | |
| 496 if (finishedClasses[cls]) return; | |
| 497 finishedClasses[cls] = true; | |
| 498 | |
| 499 var superclass = pendingClasses[cls]; | |
| 500 | |
| 501 // The superclass is only false (empty string) for the Dart Object | |
| 502 // class. The minifier together with noSuchMethod can put methods on | |
| 503 // the Object.prototype object, and they show through here, so we check | |
| 504 // that we have a string. | |
| 505 if (!superclass || typeof superclass != "string") return; | |
| 506 finishClass(superclass); | |
| 507 var constructor = allClasses[cls]; | |
| 508 var superConstructor = allClasses[superclass]; | |
| 509 | |
| 510 if (!superConstructor) | |
| 511 superConstructor = existingIsolateProperties[superclass]; | |
| 512 | |
| 513 var prototype = inheritFrom(constructor, superConstructor); | |
| 514 | |
| 515 if (#) { // !nativeClasses.isEmpty, | |
| 516 // The property looks like this: | |
| 517 // | |
| 518 // HtmlElement: { | |
| 519 // "%": "HTMLDivElement|HTMLAnchorElement;HTMLElement;FancyButton" | |
| 520 // | |
| 521 // The first two semicolon-separated parts contain dispatch tags, the | |
| 522 // third contains the JavaScript names for classes. | |
| 523 // | |
| 524 // The tags indicate that JavaScript objects with the dispatch tags | |
| 525 // (usually constructor names) HTMLDivElement, HTMLAnchorElement and | |
| 526 // HTMLElement all map to the Dart native class named HtmlElement. | |
| 527 // The first set is for effective leaf nodes in the hierarchy, the | |
| 528 // second set is non-leaf nodes. | |
| 529 // | |
| 530 // The third part contains the JavaScript names of Dart classes that | |
| 531 // extend the native class. Here, FancyButton extends HtmlElement, so | |
| 532 // the runtime needs to know that window.HTMLElement.prototype is the | |
| 533 // prototype that needs to be extended in creating the custom element. | |
| 534 // | |
| 535 // The information is used to build tables referenced by | |
| 536 // getNativeInterceptor and custom element support. | |
| 537 if (Object.prototype.hasOwnProperty.call(prototype, $specProperty)) { | |
| 538 var nativeSpec = prototype[$specProperty].split(";"); | |
| 539 if (nativeSpec[0]) { | |
| 540 var tags = nativeSpec[0].split("|"); | |
| 541 for (var i = 0; i < tags.length; i++) { | |
| 542 #[tags[i]] = constructor; // embedded interceptorsByTag. | |
| 543 #[tags[i]] = true; // embedded leafTags. | |
| 544 } | |
| 545 } | |
| 546 if (nativeSpec[1]) { | |
| 547 tags = nativeSpec[1].split("|"); | |
| 548 if (#) { // User subclassing of native classes? | |
| 549 if (nativeSpec[2]) { | |
| 550 var subclasses = nativeSpec[2].split("|"); | |
| 551 for (var i = 0; i < subclasses.length; i++) { | |
| 552 var subclass = allClasses[subclasses[i]]; | |
| 553 subclass.\$nativeSuperclassTag = tags[0]; | |
| 554 } | |
| 555 } | |
| 556 for (i = 0; i < tags.length; i++) { | |
| 557 #[tags[i]] = constructor; // embedded interceptorsByTag. | |
| 558 #[tags[i]] = false; // embedded leafTags. | |
| 559 } | |
| 560 } | |
| 561 } | |
| 562 } | |
| 563 } | |
| 564 }''', [!nativeClasses.isEmpty, | |
| 565 interceptorsByTagAccess, | |
| 566 leafTagsAccess, | |
| 567 true, | |
| 568 interceptorsByTagAccess, | |
| 569 leafTagsAccess]); | |
| 570 } | |
| 571 | |
| 572 jsAst.Fun get finishIsolateConstructorFunction { | |
| 573 // We replace the old Isolate function with a new one that initializes | |
| 574 // all its fields with the initial (and often final) value of all globals. | |
| 575 // | |
| 576 // We also copy over old values like the prototype, and the | |
| 577 // isolateProperties themselves. | |
| 578 return js(''' | |
| 579 function (oldIsolate) { | |
| 580 var isolateProperties = oldIsolate.#; // isolatePropertiesName | |
| 581 function Isolate() { | |
| 582 var hasOwnProperty = Object.prototype.hasOwnProperty; | |
| 583 for (var staticName in isolateProperties) | |
| 584 if (hasOwnProperty.call(isolateProperties, staticName)) | |
| 585 this[staticName] = isolateProperties[staticName]; | |
| 586 | |
| 587 // Reset lazy initializers to null. | |
| 588 // When forcing the object to fast mode (below) v8 will consider | |
| 589 // functions as part the object's map. Since we will change them | |
| 590 // (after the first call to the getter), we would have a map | |
| 591 // transition. | |
| 592 var lazies = init.lazies; | |
| 593 for (var lazyInit in lazies) { | |
| 594 this[lazies[lazyInit]] = null; | |
| 595 } | |
| 596 | |
| 597 // Use the newly created object as prototype. In Chrome, | |
| 598 // this creates a hidden class for the object and makes | |
| 599 // sure it is fast to access. | |
| 600 function ForceEfficientMap() {} | |
| 601 ForceEfficientMap.prototype = this; | |
| 602 new ForceEfficientMap(); | |
| 603 | |
| 604 // Now, after being a fast map we can set the lazies again. | |
| 605 for (var lazyInit in lazies) { | |
| 606 var lazyInitName = lazies[lazyInit]; | |
| 607 this[lazyInitName] = isolateProperties[lazyInitName]; | |
| 608 } | |
| 609 } | |
| 610 Isolate.prototype = oldIsolate.prototype; | |
| 611 Isolate.prototype.constructor = Isolate; | |
| 612 Isolate.# = isolateProperties; // isolatePropertiesName | |
| 613 if (#) // needsDefineClass. | |
| 614 Isolate.# = oldIsolate.#; // finishClassesProperty * 2 | |
| 615 if (#) // outputContainsConstantList | |
| 616 Isolate.# = oldIsolate.#; // makeConstListProperty * 2 | |
| 617 return Isolate; | |
| 618 }''', | |
| 619 [namer.isolatePropertiesName, namer.isolatePropertiesName, | |
| 620 needsDefineClass, finishClassesProperty, finishClassesProperty, | |
| 621 task.outputContainsConstantList, | |
| 622 makeConstListProperty, makeConstListProperty ]); | |
| 623 } | |
| 624 | |
| 625 jsAst.Fun get lazyInitializerFunction { | |
| 626 String isolate = namer.currentIsolate; | |
| 627 jsAst.Expression cyclicThrow = | |
| 628 namer.elementAccess(backend.getCyclicThrowHelper()); | |
| 629 jsAst.Expression laziesAccess = | |
| 630 generateEmbeddedGlobalAccess(embeddedNames.LAZIES); | |
| 631 | |
| 632 return js(''' | |
| 633 function (prototype, staticName, fieldName, getterName, lazyValue) { | |
| 634 if (!#) # = Object.create(null); | |
| 635 #[fieldName] = getterName; | |
| 636 | |
| 637 var sentinelUndefined = {}; | |
| 638 var sentinelInProgress = {}; | |
| 639 prototype[fieldName] = sentinelUndefined; | |
| 640 | |
| 641 prototype[getterName] = function () { | |
| 642 var result = $isolate[fieldName]; | |
| 643 try { | |
| 644 if (result === sentinelUndefined) { | |
| 645 $isolate[fieldName] = sentinelInProgress; | |
| 646 | |
| 647 try { | |
| 648 result = $isolate[fieldName] = lazyValue(); | |
| 649 } finally { | |
| 650 // Use try-finally, not try-catch/throw as it destroys the | |
| 651 // stack trace. | |
| 652 if (result === sentinelUndefined) | |
| 653 $isolate[fieldName] = null; | |
| 654 } | |
| 655 } else { | |
| 656 if (result === sentinelInProgress) | |
| 657 #(staticName); | |
| 658 } | |
| 659 | |
| 660 return result; | |
| 661 } finally { | |
| 662 $isolate[getterName] = function() { return this[fieldName]; }; | |
| 663 } | |
| 664 } | |
| 665 } | |
| 666 ''', [laziesAccess, laziesAccess, | |
| 667 laziesAccess, | |
| 668 cyclicThrow]); | |
| 669 } | |
| 670 | |
| 671 List buildDefineClassAndFinishClassFunctionsIfNecessary() { | |
| 672 if (!needsDefineClass) return []; | |
| 673 return defineClassFunction | |
| 674 ..addAll(buildInheritFrom()) | |
| 675 ..addAll([ | |
| 676 js('$finishClassesName = #', finishClassesFunction) | |
| 677 ]); | |
| 678 } | |
| 679 | |
| 680 List buildLazyInitializerFunctionIfNecessary() { | |
| 681 if (!needsLazyInitializer) return []; | |
| 682 | |
| 683 return [js('# = #', [js(lazyInitializerName), lazyInitializerFunction])]; | |
| 684 } | |
| 685 | |
| 686 List buildFinishIsolateConstructor() { | |
| 687 return [ | |
| 688 js('$finishIsolateConstructorName = #', finishIsolateConstructorFunction) | |
| 689 ]; | |
| 690 } | |
| 691 | |
| 692 void emitFinishIsolateConstructorInvocation(CodeBuffer buffer) { | |
| 693 String isolate = namer.isolateName; | |
| 694 buffer.write("$isolate = $finishIsolateConstructorName($isolate)$N"); | |
| 695 } | |
| 696 | |
| 697 /// In minified mode we want to keep the name for the most common core types. | |
| 698 bool _isNativeTypeNeedingReflectionName(Element element) { | |
| 699 if (!element.isClass) return false; | |
| 700 return (element == compiler.intClass || | |
| 701 element == compiler.doubleClass || | |
| 702 element == compiler.numClass || | |
| 703 element == compiler.stringClass || | |
| 704 element == compiler.boolClass || | |
| 705 element == compiler.nullClass || | |
| 706 element == compiler.listClass); | |
| 707 } | |
| 708 | |
| 709 /// Returns the "reflection name" of an [Element] or [Selector]. | |
| 710 /// The reflection name of a getter 'foo' is 'foo'. | |
| 711 /// The reflection name of a setter 'foo' is 'foo='. | |
| 712 /// The reflection name of a method 'foo' is 'foo:N:M:O', where N is the | |
| 713 /// number of required arguments, M is the number of optional arguments, and | |
| 714 /// O is the named arguments. | |
| 715 /// The reflection name of a constructor is similar to a regular method but | |
| 716 /// starts with 'new '. | |
| 717 /// The reflection name of class 'C' is 'C'. | |
| 718 /// An anonymous mixin application has no reflection name. | |
| 719 /// This is used by js_mirrors.dart. | |
| 720 String getReflectionName(elementOrSelector, String mangledName) { | |
| 721 String name = elementOrSelector.name; | |
| 722 if (backend.shouldRetainName(name) || | |
| 723 elementOrSelector is Element && | |
| 724 // Make sure to retain names of unnamed constructors, and | |
| 725 // for common native types. | |
| 726 ((name == '' && | |
| 727 backend.isAccessibleByReflection(elementOrSelector)) || | |
| 728 _isNativeTypeNeedingReflectionName(elementOrSelector))) { | |
| 729 | |
| 730 // TODO(ahe): Enable the next line when I can tell the difference between | |
| 731 // an instance method and a global. They may have the same mangled name. | |
| 732 // if (recordedMangledNames.contains(mangledName)) return null; | |
| 733 recordedMangledNames.add(mangledName); | |
| 734 return getReflectionNameInternal(elementOrSelector, mangledName); | |
| 735 } | |
| 736 return null; | |
| 737 } | |
| 738 | |
| 739 String getReflectionNameInternal(elementOrSelector, String mangledName) { | |
| 740 String name = | |
| 741 namer.privateName(elementOrSelector.library, elementOrSelector.name); | |
| 742 if (elementOrSelector.isGetter) return name; | |
| 743 if (elementOrSelector.isSetter) { | |
| 744 if (!mangledName.startsWith(namer.setterPrefix)) return '$name='; | |
| 745 String base = mangledName.substring(namer.setterPrefix.length); | |
| 746 String getter = '${namer.getterPrefix}$base'; | |
| 747 mangledFieldNames.putIfAbsent(getter, () => name); | |
| 748 assert(mangledFieldNames[getter] == name); | |
| 749 recordedMangledNames.add(getter); | |
| 750 // TODO(karlklose,ahe): we do not actually need to store information | |
| 751 // about the name of this setter in the output, but it is needed for | |
| 752 // marking the function as invokable by reflection. | |
| 753 return '$name='; | |
| 754 } | |
| 755 if (elementOrSelector is Element && elementOrSelector.isClosure) { | |
| 756 // Closures are synthesized and their name might conflict with existing | |
| 757 // globals. Assign an illegal name, and make sure they don't clash | |
| 758 // with each other. | |
| 759 return " $mangledName"; | |
| 760 } | |
| 761 if (elementOrSelector is Selector | |
| 762 || elementOrSelector.isFunction | |
| 763 || elementOrSelector.isConstructor) { | |
| 764 int requiredParameterCount; | |
| 765 int optionalParameterCount; | |
| 766 String namedArguments = ''; | |
| 767 bool isConstructor = false; | |
| 768 if (elementOrSelector is Selector) { | |
| 769 Selector selector = elementOrSelector; | |
| 770 requiredParameterCount = selector.argumentCount; | |
| 771 optionalParameterCount = 0; | |
| 772 namedArguments = namedParametersAsReflectionNames(selector); | |
| 773 } else { | |
| 774 FunctionElement function = elementOrSelector; | |
| 775 if (function.isConstructor) { | |
| 776 isConstructor = true; | |
| 777 name = Elements.reconstructConstructorName(function); | |
| 778 } | |
| 779 FunctionSignature signature = function.functionSignature; | |
| 780 requiredParameterCount = signature.requiredParameterCount; | |
| 781 optionalParameterCount = signature.optionalParameterCount; | |
| 782 if (signature.optionalParametersAreNamed) { | |
| 783 var names = []; | |
| 784 for (Element e in signature.optionalParameters) { | |
| 785 names.add(e.name); | |
| 786 } | |
| 787 Selector selector = new Selector.call( | |
| 788 function.name, | |
| 789 function.library, | |
| 790 requiredParameterCount, | |
| 791 names); | |
| 792 namedArguments = namedParametersAsReflectionNames(selector); | |
| 793 } else { | |
| 794 // Named parameters are handled differently by mirrors. For unnamed | |
| 795 // parameters, they are actually required if invoked | |
| 796 // reflectively. Also, if you have a method c(x) and c([x]) they both | |
| 797 // get the same mangled name, so they must have the same reflection | |
| 798 // name. | |
| 799 requiredParameterCount += optionalParameterCount; | |
| 800 optionalParameterCount = 0; | |
| 801 } | |
| 802 } | |
| 803 String suffix = | |
| 804 // TODO(ahe): We probably don't need optionalParameterCount in the | |
| 805 // reflection name. | |
| 806 '$name:$requiredParameterCount:$optionalParameterCount' | |
| 807 '$namedArguments'; | |
| 808 return (isConstructor) ? 'new $suffix' : suffix; | |
| 809 } | |
| 810 Element element = elementOrSelector; | |
| 811 if (element.isGenerativeConstructorBody) { | |
| 812 return null; | |
| 813 } else if (element.isClass) { | |
| 814 ClassElement cls = element; | |
| 815 if (cls.isUnnamedMixinApplication) return null; | |
| 816 return cls.name; | |
| 817 } else if (element.isTypedef) { | |
| 818 return element.name; | |
| 819 } | |
| 820 throw compiler.internalError(element, | |
| 821 'Do not know how to reflect on this $element.'); | |
| 822 } | |
| 823 | |
| 824 String namedParametersAsReflectionNames(Selector selector) { | |
| 825 if (selector.getOrderedNamedArguments().isEmpty) return ''; | |
| 826 String names = selector.getOrderedNamedArguments().join(':'); | |
| 827 return ':$names'; | |
| 828 } | |
| 829 | |
| 830 jsAst.FunctionDeclaration buildCspPrecompiledFunctionFor( | |
| 831 OutputUnit outputUnit) { | |
| 832 // TODO(ahe): Compute a hash code. | |
| 833 return js.statement(''' | |
| 834 function dart_precompiled(\$collectedClasses) { | |
| 835 var \$desc; | |
| 836 #; | |
| 837 return #; | |
| 838 }''', | |
| 839 [cspPrecompiledFunctionFor(outputUnit), | |
| 840 new jsAst.ArrayInitializer.from( | |
| 841 cspPrecompiledConstructorNamesFor(outputUnit))]); | |
| 842 } | |
| 843 | |
| 844 void generateClass(ClassElement classElement, ClassBuilder properties) { | |
| 845 compiler.withCurrentElement(classElement, () { | |
| 846 if (compiler.hasIncrementalSupport) { | |
| 847 ClassBuilder builder = | |
| 848 cachedClassBuilders.putIfAbsent(classElement, () { | |
| 849 ClassBuilder builder = new ClassBuilder(classElement, namer); | |
| 850 classEmitter.generateClass( | |
| 851 classElement, builder, additionalProperties[classElement]); | |
| 852 return builder; | |
| 853 }); | |
| 854 invariant(classElement, builder.fields.isEmpty); | |
| 855 invariant(classElement, builder.superName == null); | |
| 856 invariant(classElement, builder.functionType == null); | |
| 857 invariant(classElement, builder.fieldMetadata == null); | |
| 858 properties.properties.addAll(builder.properties); | |
| 859 } else { | |
| 860 classEmitter.generateClass( | |
| 861 classElement, properties, additionalProperties[classElement]); | |
| 862 } | |
| 863 }); | |
| 864 } | |
| 865 | |
| 866 void emitFinishClassesInvocationIfNecessary(CodeBuffer buffer) { | |
| 867 if (needsDefineClass) { | |
| 868 buffer.write('$finishClassesName($classesCollector,' | |
| 869 '$_$isolateProperties,' | |
| 870 '${_}null)$N'); | |
| 871 | |
| 872 // Reset the map. | |
| 873 buffer.write("$classesCollector$_=${_}null$N$n"); | |
| 874 } | |
| 875 } | |
| 876 | |
| 877 void emitStaticFunctions(List<Element> staticFunctions) { | |
| 878 for (Element element in staticFunctions) { | |
| 879 ClassBuilder builder = new ClassBuilder(element, namer); | |
| 880 containerBuilder.addMember(element, builder); | |
| 881 getElementDescriptor(element).properties.addAll(builder.properties); | |
| 882 } | |
| 883 } | |
| 884 | |
| 885 void emitStaticNonFinalFieldInitializations(CodeBuffer buffer) { | |
| 886 JavaScriptConstantCompiler handler = backend.constants; | |
| 887 Iterable<VariableElement> staticNonFinalFields = | |
| 888 handler.getStaticNonFinalFieldsForEmission(); | |
| 889 for (Element element in Elements.sortedByPosition(staticNonFinalFields)) { | |
| 890 // [:interceptedNames:] is handled in [emitInterceptedNames]. | |
| 891 if (element == backend.interceptedNames) continue; | |
| 892 // `mapTypeToInterceptor` is handled in [emitMapTypeToInterceptor]. | |
| 893 if (element == backend.mapTypeToInterceptor) continue; | |
| 894 compiler.withCurrentElement(element, () { | |
| 895 ConstantValue initialValue = handler.getInitialValueFor(element).value; | |
| 896 jsAst.Expression init = | |
| 897 js('$isolateProperties.# = #', | |
| 898 [namer.getNameOfGlobalField(element), | |
| 899 constantEmitter.referenceInInitializationContext(initialValue)]); | |
| 900 buffer.write(jsAst.prettyPrint(init, compiler, | |
| 901 monitor: compiler.dumpInfoTask)); | |
| 902 buffer.write('$N'); | |
| 903 }); | |
| 904 } | |
| 905 } | |
| 906 | |
| 907 void emitLazilyInitializedStaticFields(CodeBuffer buffer) { | |
| 908 JavaScriptConstantCompiler handler = backend.constants; | |
| 909 List<VariableElement> lazyFields = | |
| 910 handler.getLazilyInitializedFieldsForEmission(); | |
| 911 if (!lazyFields.isEmpty) { | |
| 912 needsLazyInitializer = true; | |
| 913 for (VariableElement element in Elements.sortedByPosition(lazyFields)) { | |
| 914 jsAst.Expression code = backend.generatedCode[element]; | |
| 915 // The code is null if we ended up not needing the lazily | |
| 916 // initialized field after all because of constant folding | |
| 917 // before code generation. | |
| 918 if (code == null) continue; | |
| 919 // The code only computes the initial value. We build the lazy-check | |
| 920 // here: | |
| 921 // lazyInitializer(prototype, 'name', fieldName, getterName, initial); | |
| 922 // The name is used for error reporting. The 'initial' must be a | |
| 923 // closure that constructs the initial value. | |
| 924 jsAst.Expression init = js('#(#,#,#,#,#)', | |
| 925 [js(lazyInitializerName), | |
| 926 js(isolateProperties), | |
| 927 js.string(element.name), | |
| 928 js.string(namer.getNameX(element)), | |
| 929 js.string(namer.getLazyInitializerName(element)), | |
| 930 code]); | |
| 931 buffer.write(jsAst.prettyPrint(init, compiler, | |
| 932 monitor: compiler.dumpInfoTask)); | |
| 933 buffer.write("$N"); | |
| 934 } | |
| 935 } | |
| 936 } | |
| 937 | |
| 938 bool isConstantInlinedOrAlreadyEmitted(ConstantValue constant) { | |
| 939 if (constant.isFunction) return true; // Already emitted. | |
| 940 if (constant.isPrimitive) return true; // Inlined. | |
| 941 if (constant.isDummy) return true; // Inlined. | |
| 942 // The name is null when the constant is already a JS constant. | |
| 943 // TODO(floitsch): every constant should be registered, so that we can | |
| 944 // share the ones that take up too much space (like some strings). | |
| 945 if (namer.constantName(constant) == null) return true; | |
| 946 return false; | |
| 947 } | |
| 948 | |
| 949 int compareConstants(ConstantValue a, ConstantValue b) { | |
| 950 // Inlined constants don't affect the order and sometimes don't even have | |
| 951 // names. | |
| 952 int cmp1 = isConstantInlinedOrAlreadyEmitted(a) ? 0 : 1; | |
| 953 int cmp2 = isConstantInlinedOrAlreadyEmitted(b) ? 0 : 1; | |
| 954 if (cmp1 + cmp2 < 2) return cmp1 - cmp2; | |
| 955 | |
| 956 // Emit constant interceptors first. Constant interceptors for primitives | |
| 957 // might be used by code that builds other constants. See Issue 18173. | |
| 958 if (a.isInterceptor != b.isInterceptor) { | |
| 959 return a.isInterceptor ? -1 : 1; | |
| 960 } | |
| 961 | |
| 962 // Sorting by the long name clusters constants with the same constructor | |
| 963 // which compresses a tiny bit better. | |
| 964 int r = namer.constantLongName(a).compareTo(namer.constantLongName(b)); | |
| 965 if (r != 0) return r; | |
| 966 // Resolve collisions in the long name by using the constant name (i.e. JS | |
| 967 // name) which is unique. | |
| 968 return namer.constantName(a).compareTo(namer.constantName(b)); | |
| 969 } | |
| 970 | |
| 971 void emitCompileTimeConstants(CodeBuffer buffer, OutputUnit outputUnit) { | |
| 972 List<ConstantValue> constants = outputConstantLists[outputUnit]; | |
| 973 if (constants == null) return; | |
| 974 bool isMainBuffer = buffer == mainBuffer; | |
| 975 if (compiler.hasIncrementalSupport && isMainBuffer) { | |
| 976 buffer = cachedEmittedConstantsBuffer; | |
| 977 } | |
| 978 for (ConstantValue constant in constants) { | |
| 979 if (compiler.hasIncrementalSupport && isMainBuffer) { | |
| 980 if (cachedEmittedConstants.contains(constant)) continue; | |
| 981 cachedEmittedConstants.add(constant); | |
| 982 } | |
| 983 String name = namer.constantName(constant); | |
| 984 jsAst.Expression init = js('#.# = #', | |
| 985 [namer.globalObjectForConstant(constant), name, | |
| 986 constantInitializerExpression(constant)]); | |
| 987 buffer.write(jsAst.prettyPrint(init, compiler, | |
| 988 monitor: compiler.dumpInfoTask)); | |
| 989 buffer.write('$N'); | |
| 990 } | |
| 991 if (compiler.hasIncrementalSupport && isMainBuffer) { | |
| 992 mainBuffer.add(cachedEmittedConstantsBuffer); | |
| 993 } | |
| 994 } | |
| 995 | |
| 996 jsAst.Template get makeConstantListTemplate { | |
| 997 // TODO(floitsch): there is no harm in caching the template. | |
| 998 return jsAst.js.uncachedExpressionTemplate( | |
| 999 '${namer.isolateName}.$makeConstListProperty(#)'); | |
| 1000 } | |
| 1001 | |
| 1002 void emitMakeConstantList(CodeBuffer buffer) { | |
| 1003 buffer.write( | |
| 1004 jsAst.prettyPrint( | |
| 1005 // Functions are stored in the hidden class and not as properties in | |
| 1006 // the object. We never actually look at the value, but only want | |
| 1007 // to know if the property exists. | |
| 1008 js.statement(r'''#.# = function(list) { | |
| 1009 list.immutable$list = Array; | |
| 1010 list.fixed$length = Array; | |
| 1011 return list; | |
| 1012 }''', | |
| 1013 [namer.isolateName, makeConstListProperty]), | |
| 1014 compiler, monitor: compiler.dumpInfoTask)); | |
| 1015 buffer.write(N); | |
| 1016 } | |
| 1017 | |
| 1018 /// Returns the code equivalent to: | |
| 1019 /// `function(args) { $.startRootIsolate(X.main$closure(), args); }` | |
| 1020 jsAst.Expression buildIsolateSetupClosure(Element appMain, | |
| 1021 Element isolateMain) { | |
| 1022 jsAst.Expression mainAccess = namer.isolateStaticClosureAccess(appMain); | |
| 1023 // Since we pass the closurized version of the main method to | |
| 1024 // the isolate method, we must make sure that it exists. | |
| 1025 return js('function(a){ #(#, a); }', | |
| 1026 [namer.elementAccess(isolateMain), mainAccess]); | |
| 1027 } | |
| 1028 | |
| 1029 /** | |
| 1030 * Emits code that sets the `isolateTag embedded global to a unique string. | |
| 1031 */ | |
| 1032 jsAst.Expression generateIsolateAffinityTagInitialization() { | |
| 1033 jsAst.Expression getIsolateTagAccess = | |
| 1034 generateEmbeddedGlobalAccess(embeddedNames.GET_ISOLATE_TAG); | |
| 1035 jsAst.Expression isolateTagAccess = | |
| 1036 generateEmbeddedGlobalAccess(embeddedNames.ISOLATE_TAG); | |
| 1037 | |
| 1038 return js(''' | |
| 1039 !function() { | |
| 1040 // On V8, the 'intern' function converts a string to a symbol, which | |
| 1041 // makes property access much faster. | |
| 1042 function intern(s) { | |
| 1043 var o = {}; | |
| 1044 o[s] = 1; | |
| 1045 return Object.keys(convertToFastObject(o))[0]; | |
| 1046 } | |
| 1047 | |
| 1048 # = function(name) { // embedded getIsolateTag | |
| 1049 return intern("___dart_" + name + #); // embedded isolateTag | |
| 1050 }; | |
| 1051 | |
| 1052 // To ensure that different programs loaded into the same context (page) | |
| 1053 // use distinct dispatch properies, we place an object on `Object` to | |
| 1054 // contain the names already in use. | |
| 1055 var tableProperty = "___dart_isolate_tags_"; | |
| 1056 var usedProperties = Object[tableProperty] || | |
| 1057 (Object[tableProperty] = Object.create(null)); | |
| 1058 | |
| 1059 var rootProperty = "_${generateIsolateTagRoot()}"; | |
| 1060 for (var i = 0; ; i++) { | |
| 1061 var property = intern(rootProperty + "_" + i + "_"); | |
| 1062 if (!(property in usedProperties)) { | |
| 1063 usedProperties[property] = 1; | |
| 1064 # = property; // embedded isolateTag | |
| 1065 break; | |
| 1066 } | |
| 1067 } | |
| 1068 }() | |
| 1069 ''', [getIsolateTagAccess, | |
| 1070 isolateTagAccess, | |
| 1071 isolateTagAccess]); | |
| 1072 } | |
| 1073 | |
| 1074 jsAst.Expression generateDispatchPropertyNameInitialization() { | |
| 1075 jsAst.Expression dispatchPropertyNameAccess = | |
| 1076 generateEmbeddedGlobalAccess(embeddedNames.DISPATCH_PROPERTY_NAME); | |
| 1077 jsAst.Expression getIsolateTagAccess = | |
| 1078 generateEmbeddedGlobalAccess(embeddedNames.GET_ISOLATE_TAG); | |
| 1079 return js('# = #("dispatch_record")', | |
| 1080 [dispatchPropertyNameAccess, | |
| 1081 getIsolateTagAccess]); | |
| 1082 } | |
| 1083 | |
| 1084 String generateIsolateTagRoot() { | |
| 1085 // TODO(sra): MD5 of contributing source code or URIs? | |
| 1086 return 'ZxYxX'; | |
| 1087 } | |
| 1088 | |
| 1089 emitMain(CodeBuffer buffer) { | |
| 1090 if (compiler.isMockCompilation) return; | |
| 1091 Element main = compiler.mainFunction; | |
| 1092 jsAst.Expression mainCallClosure = null; | |
| 1093 if (compiler.hasIsolateSupport) { | |
| 1094 Element isolateMain = | |
| 1095 backend.isolateHelperLibrary.find(JavaScriptBackend.START_ROOT_ISOLATE); | |
| 1096 mainCallClosure = buildIsolateSetupClosure(main, isolateMain); | |
| 1097 } else if (compiler.hasIncrementalSupport) { | |
| 1098 mainCallClosure = | |
| 1099 js('function() { return #(); }', namer.elementAccess(main)); | |
| 1100 } else { | |
| 1101 mainCallClosure = namer.elementAccess(main); | |
| 1102 } | |
| 1103 | |
| 1104 if (backend.needToInitializeIsolateAffinityTag) { | |
| 1105 buffer.write( | |
| 1106 jsAst.prettyPrint(generateIsolateAffinityTagInitialization(), | |
| 1107 compiler, monitor: compiler.dumpInfoTask)); | |
| 1108 buffer.write(N); | |
| 1109 } | |
| 1110 if (backend.needToInitializeDispatchProperty) { | |
| 1111 assert(backend.needToInitializeIsolateAffinityTag); | |
| 1112 buffer.write( | |
| 1113 jsAst.prettyPrint(generateDispatchPropertyNameInitialization(), | |
| 1114 compiler, monitor: compiler.dumpInfoTask)); | |
| 1115 buffer.write(N); | |
| 1116 } | |
| 1117 | |
| 1118 jsAst.Expression currentScriptAccess = | |
| 1119 generateEmbeddedGlobalAccess(embeddedNames.CURRENT_SCRIPT); | |
| 1120 | |
| 1121 addComment('BEGIN invoke [main].', buffer); | |
| 1122 // This code finds the currently executing script by listening to the | |
| 1123 // onload event of all script tags and getting the first script which | |
| 1124 // finishes. Since onload is called immediately after execution this should | |
| 1125 // not substantially change execution order. | |
| 1126 jsAst.Statement invokeMain = js.statement(''' | |
| 1127 (function (callback) { | |
| 1128 if (typeof document === "undefined") { | |
| 1129 callback(null); | |
| 1130 return; | |
| 1131 } | |
| 1132 if (document.currentScript) { | |
| 1133 callback(document.currentScript); | |
| 1134 return; | |
| 1135 } | |
| 1136 | |
| 1137 var scripts = document.scripts; | |
| 1138 function onLoad(event) { | |
| 1139 for (var i = 0; i < scripts.length; ++i) { | |
| 1140 scripts[i].removeEventListener("load", onLoad, false); | |
| 1141 } | |
| 1142 callback(event.target); | |
| 1143 } | |
| 1144 for (var i = 0; i < scripts.length; ++i) { | |
| 1145 scripts[i].addEventListener("load", onLoad, false); | |
| 1146 } | |
| 1147 })(function(currentScript) { | |
| 1148 # = currentScript; // embedded currentScript. | |
| 1149 | |
| 1150 if (typeof dartMainRunner === "function") { | |
| 1151 dartMainRunner(#, []); // mainCallClosure. | |
| 1152 } else { | |
| 1153 #([]); // mainCallClosure. | |
| 1154 } | |
| 1155 })$N''', [currentScriptAccess, | |
| 1156 mainCallClosure, | |
| 1157 mainCallClosure]); | |
| 1158 | |
| 1159 buffer.write(';'); | |
| 1160 buffer.write(jsAst.prettyPrint(invokeMain, | |
| 1161 compiler, monitor: compiler.dumpInfoTask)); | |
| 1162 buffer.write(N); | |
| 1163 addComment('END invoke [main].', buffer); | |
| 1164 } | |
| 1165 | |
| 1166 void emitInitFunction(CodeBuffer buffer) { | |
| 1167 jsAst.FunctionDeclaration decl = js.statement(''' | |
| 1168 function init() { | |
| 1169 $isolateProperties = Object.create(null); | |
| 1170 #; #; #; | |
| 1171 }''', [ | |
| 1172 buildDefineClassAndFinishClassFunctionsIfNecessary(), | |
| 1173 buildLazyInitializerFunctionIfNecessary(), | |
| 1174 buildFinishIsolateConstructor()]); | |
| 1175 | |
| 1176 buffer.write(jsAst.prettyPrint(decl, | |
| 1177 compiler, monitor: compiler.dumpInfoTask).getText()); | |
| 1178 if (compiler.enableMinification) buffer.write('\n'); | |
| 1179 } | |
| 1180 | |
| 1181 void emitConvertToFastObjectFunction() { | |
| 1182 List<jsAst.Statement> debugCode = <jsAst.Statement>[]; | |
| 1183 if (DEBUG_FAST_OBJECTS) { | |
| 1184 debugCode.add(js.statement(r''' | |
| 1185 // The following only works on V8 when run with option | |
| 1186 // "--allow-natives-syntax". We use'new Function' because the | |
| 1187 // miniparser does not understand V8 native syntax. | |
| 1188 if (typeof print === "function") { | |
| 1189 var HasFastProperties = | |
| 1190 new Function("a", "return %HasFastProperties(a)"); | |
| 1191 print("Size of global object: " | |
| 1192 + String(Object.getOwnPropertyNames(properties).length) | |
| 1193 + ", fast properties " + HasFastProperties(properties)); | |
| 1194 }''')); | |
| 1195 } | |
| 1196 | |
| 1197 jsAst.Statement convertToFastObject = js.statement(r''' | |
| 1198 function convertToFastObject(properties) { | |
| 1199 // Create an instance that uses 'properties' as prototype. This should | |
| 1200 // make 'properties' a fast object. | |
| 1201 function MyClass() {}; | |
| 1202 MyClass.prototype = properties; | |
| 1203 new MyClass(); | |
| 1204 #; | |
| 1205 return properties; | |
| 1206 }''', [debugCode]); | |
| 1207 | |
| 1208 mainBuffer.add(jsAst.prettyPrint(convertToFastObject, compiler)); | |
| 1209 mainBuffer.add(N); | |
| 1210 } | |
| 1211 | |
| 1212 void writeLibraryDescriptors(CodeBuffer buffer, LibraryElement library) { | |
| 1213 var uri = ""; | |
| 1214 if (!compiler.enableMinification || backend.mustPreserveUris) { | |
| 1215 uri = library.canonicalUri; | |
| 1216 if (uri.scheme == 'file' && compiler.outputUri != null) { | |
| 1217 uri = relativize(compiler.outputUri, library.canonicalUri, false); | |
| 1218 } | |
| 1219 } | |
| 1220 ClassBuilder descriptor = elementDescriptors[library]; | |
| 1221 if (descriptor == null) { | |
| 1222 // Nothing of the library was emitted. | |
| 1223 // TODO(floitsch): this should not happen. We currently have an example | |
| 1224 // with language/prefix6_negative_test.dart where we have an instance | |
| 1225 // method without its corresponding class. | |
| 1226 return; | |
| 1227 } | |
| 1228 | |
| 1229 String libraryName = | |
| 1230 (!compiler.enableMinification || backend.mustRetainLibraryNames) ? | |
| 1231 library.getLibraryName() : | |
| 1232 ""; | |
| 1233 | |
| 1234 jsAst.Fun metadata = metadataEmitter.buildMetadataFunction(library); | |
| 1235 | |
| 1236 jsAst.ObjectInitializer initializers = descriptor.toObjectInitializer(); | |
| 1237 | |
| 1238 compiler.dumpInfoTask.registerElementAst(library, metadata); | |
| 1239 compiler.dumpInfoTask.registerElementAst(library, initializers); | |
| 1240 buffer | |
| 1241 ..write('["$libraryName",$_') | |
| 1242 ..write('"${uri}",$_') | |
| 1243 ..write(metadata == null ? "" : jsAst.prettyPrint(metadata, | |
| 1244 compiler, | |
| 1245 monitor: compiler.dumpInfoTask)) | |
| 1246 ..write(',$_') | |
| 1247 ..write(namer.globalObjectFor(library)) | |
| 1248 ..write(',$_') | |
| 1249 ..write(jsAst.prettyPrint(initializers, | |
| 1250 compiler, | |
| 1251 monitor: compiler.dumpInfoTask)) | |
| 1252 ..write(library == compiler.mainApp ? ',${n}1' : "") | |
| 1253 ..write('],$n'); | |
| 1254 } | |
| 1255 | |
| 1256 void emitPrecompiledConstructor(OutputUnit outputUnit, | |
| 1257 String constructorName, | |
| 1258 jsAst.Expression constructorAst) { | |
| 1259 cspPrecompiledFunctionFor(outputUnit).add( | |
| 1260 new jsAst.FunctionDeclaration( | |
| 1261 new jsAst.VariableDeclaration(constructorName), constructorAst)); | |
| 1262 cspPrecompiledFunctionFor(outputUnit).add( | |
| 1263 js.statement(r'''{ | |
| 1264 #.builtin$cls = #; | |
| 1265 if (!"name" in #) | |
| 1266 #.name = #; | |
| 1267 $desc=$collectedClasses.#; | |
| 1268 if ($desc instanceof Array) $desc = $desc[1]; | |
| 1269 #.prototype = $desc; | |
| 1270 }''', | |
| 1271 [ constructorName, js.string(constructorName), | |
| 1272 constructorName, | |
| 1273 constructorName, js.string(constructorName), | |
| 1274 constructorName, | |
| 1275 constructorName | |
| 1276 ])); | |
| 1277 | |
| 1278 cspPrecompiledConstructorNamesFor(outputUnit).add(js('#', constructorName)); | |
| 1279 } | |
| 1280 | |
| 1281 /// Extracts the output name of the compiler's outputUri. | |
| 1282 String deferredPartFileName(OutputUnit outputUnit, | |
| 1283 {bool addExtension: true}) { | |
| 1284 String outPath = compiler.outputUri != null | |
| 1285 ? compiler.outputUri.path | |
| 1286 : "out"; | |
| 1287 String outName = outPath.substring(outPath.lastIndexOf('/') + 1); | |
| 1288 String extension = addExtension ? ".part.js" : ""; | |
| 1289 if (outputUnit == compiler.deferredLoadTask.mainOutputUnit) { | |
| 1290 return "$outName$extension"; | |
| 1291 } else { | |
| 1292 String name = outputUnit.name; | |
| 1293 return "${outName}_$name$extension"; | |
| 1294 } | |
| 1295 } | |
| 1296 | |
| 1297 void emitLibraries(Iterable<LibraryElement> libraries) { | |
| 1298 if (libraries.isEmpty) return; | |
| 1299 | |
| 1300 // TODO(karlklose): document what kinds of fields this loop adds to the | |
| 1301 // library class builder. | |
| 1302 for (LibraryElement element in libraries) { | |
| 1303 LibraryElement library = element; | |
| 1304 ClassBuilder builder = new ClassBuilder(library, namer); | |
| 1305 if (classEmitter.emitFields(library, builder, null, emitStatics: true)) { | |
| 1306 jsAst.ObjectInitializer initializer = builder.toObjectInitializer(); | |
| 1307 compiler.dumpInfoTask.registerElementAst(builder.element, initializer); | |
| 1308 getElementDescriptor(library).properties.addAll(initializer.properties); | |
| 1309 } | |
| 1310 } | |
| 1311 } | |
| 1312 | |
| 1313 void emitTypedefs() { | |
| 1314 OutputUnit mainOutputUnit = compiler.deferredLoadTask.mainOutputUnit; | |
| 1315 | |
| 1316 // Emit all required typedef declarations into the main output unit. | |
| 1317 // TODO(karlklose): unify required classes and typedefs to declarations | |
| 1318 // and have builders for each kind. | |
| 1319 for (TypedefElement typedef in typedefsNeededForReflection) { | |
| 1320 OutputUnit mainUnit = compiler.deferredLoadTask.mainOutputUnit; | |
| 1321 LibraryElement library = typedef.library; | |
| 1322 // TODO(karlklose): add a TypedefBuilder and move this code there. | |
| 1323 DartType type = typedef.alias; | |
| 1324 int typeIndex = metadataEmitter.reifyType(type); | |
| 1325 String typeReference = | |
| 1326 encoding.encodeTypedefFieldDescriptor(typeIndex); | |
| 1327 jsAst.Property descriptor = new jsAst.Property( | |
| 1328 js.string(namer.classDescriptorProperty), | |
| 1329 js.string(typeReference)); | |
| 1330 jsAst.Node declaration = new jsAst.ObjectInitializer([descriptor]); | |
| 1331 String mangledName = namer.getNameX(typedef); | |
| 1332 String reflectionName = getReflectionName(typedef, mangledName); | |
| 1333 getElementDescriptor(library) | |
| 1334 ..addProperty(mangledName, declaration) | |
| 1335 ..addProperty("+$reflectionName", js.string('')); | |
| 1336 // Also emit a trivial constructor for CSP mode. | |
| 1337 String constructorName = mangledName; | |
| 1338 jsAst.Expression constructorAst = js('function() {}'); | |
| 1339 emitPrecompiledConstructor(mainOutputUnit, | |
| 1340 constructorName, | |
| 1341 constructorAst); | |
| 1342 } | |
| 1343 } | |
| 1344 | |
| 1345 void emitMangledNames() { | |
| 1346 if (!mangledFieldNames.isEmpty) { | |
| 1347 var keys = mangledFieldNames.keys.toList(); | |
| 1348 keys.sort(); | |
| 1349 var properties = []; | |
| 1350 for (String key in keys) { | |
| 1351 var value = js.string('${mangledFieldNames[key]}'); | |
| 1352 properties.add(new jsAst.Property(js.string(key), value)); | |
| 1353 } | |
| 1354 | |
| 1355 jsAst.Expression mangledNamesAccess = | |
| 1356 generateEmbeddedGlobalAccess(embeddedNames.MANGLED_NAMES); | |
| 1357 var map = new jsAst.ObjectInitializer(properties); | |
| 1358 mainBuffer.write( | |
| 1359 jsAst.prettyPrint( | |
| 1360 js.statement('# = #', [mangledNamesAccess, map]), | |
| 1361 compiler, | |
| 1362 monitor: compiler.dumpInfoTask)); | |
| 1363 if (compiler.enableMinification) { | |
| 1364 mainBuffer.write(';'); | |
| 1365 } | |
| 1366 } | |
| 1367 if (!mangledGlobalFieldNames.isEmpty) { | |
| 1368 var keys = mangledGlobalFieldNames.keys.toList(); | |
| 1369 keys.sort(); | |
| 1370 var properties = []; | |
| 1371 for (String key in keys) { | |
| 1372 var value = js.string('${mangledGlobalFieldNames[key]}'); | |
| 1373 properties.add(new jsAst.Property(js.string(key), value)); | |
| 1374 } | |
| 1375 jsAst.Expression mangledGlobalNamesAccess = | |
| 1376 generateEmbeddedGlobalAccess(embeddedNames.MANGLED_GLOBAL_NAMES); | |
| 1377 var map = new jsAst.ObjectInitializer(properties); | |
| 1378 mainBuffer.write( | |
| 1379 jsAst.prettyPrint( | |
| 1380 js.statement('# = #', [mangledGlobalNamesAccess, map]), | |
| 1381 compiler, | |
| 1382 monitor: compiler.dumpInfoTask)); | |
| 1383 if (compiler.enableMinification) { | |
| 1384 mainBuffer.write(';'); | |
| 1385 } | |
| 1386 } | |
| 1387 } | |
| 1388 | |
| 1389 void checkEverythingEmitted(Iterable<Element> elements) { | |
| 1390 List<Element> pendingStatics; | |
| 1391 if (!compiler.hasIncrementalSupport) { | |
| 1392 pendingStatics = | |
| 1393 Elements.sortedByPosition(elements.where((e) => !e.isLibrary)); | |
| 1394 | |
| 1395 pendingStatics.forEach((element) => | |
| 1396 compiler.reportInfo( | |
| 1397 element, MessageKind.GENERIC, {'text': 'Pending statics.'})); | |
| 1398 } | |
| 1399 | |
| 1400 if (pendingStatics != null && !pendingStatics.isEmpty) { | |
| 1401 compiler.internalError(pendingStatics.first, | |
| 1402 'Pending statics (see above).'); | |
| 1403 } | |
| 1404 } | |
| 1405 | |
| 1406 void emitMainOutputUnit(Map<OutputUnit, String> deferredLoadHashes, | |
| 1407 CodeBuffer nativeBuffer) { | |
| 1408 bool isProgramSplit = compiler.deferredLoadTask.isProgramSplit; | |
| 1409 OutputUnit mainOutputUnit = compiler.deferredLoadTask.mainOutputUnit; | |
| 1410 | |
| 1411 mainBuffer.add(buildGeneratedBy()); | |
| 1412 addComment(HOOKS_API_USAGE, mainBuffer); | |
| 1413 | |
| 1414 if (isProgramSplit) { | |
| 1415 /// For deferred loading we communicate the initializers via this global | |
| 1416 /// variable. The deferred hunks will add their initialization to this. | |
| 1417 /// The semicolon is important in minified mode, without it the | |
| 1418 /// following parenthesis looks like a call to the object literal. | |
| 1419 mainBuffer..add( | |
| 1420 'self.${deferredInitializers} = self.${deferredInitializers} || ' | |
| 1421 'Object.create(null);$n'); | |
| 1422 } | |
| 1423 | |
| 1424 // Using a named function here produces easier to read stack traces in | |
| 1425 // Chrome/V8. | |
| 1426 mainBuffer.add('(function(${namer.currentIsolate})$_{\n'); | |
| 1427 if (compiler.hasIncrementalSupport) { | |
| 1428 mainBuffer.add( | |
| 1429 '(this.\$dart_unsafe_eval =' | |
| 1430 ' this.\$dart_unsafe_eval || Object.create(null))' | |
| 1431 '.patch = function(a) { eval(a) }$N'); | |
| 1432 } | |
| 1433 if (isProgramSplit) { | |
| 1434 /// We collect all the global state of the, so it can be passed to the | |
| 1435 /// initializer of deferred files. | |
| 1436 mainBuffer.add('var ${globalsHolder}$_=${_}Object.create(null)$N'); | |
| 1437 } | |
| 1438 mainBuffer.add('function dart()$_{$n' | |
| 1439 '${_}${_}this.x$_=${_}0$N' | |
| 1440 '${_}${_}delete this.x$N' | |
| 1441 '}$n'); | |
| 1442 for (String globalObject in Namer.reservedGlobalObjectNames) { | |
| 1443 // The global objects start as so-called "slow objects". For V8, this | |
| 1444 // means that it won't try to make map transitions as we add properties | |
| 1445 // to these objects. Later on, we attempt to turn these objects into | |
| 1446 // fast objects by calling "convertToFastObject" (see | |
| 1447 // [emitConvertToFastObjectFunction]). | |
| 1448 mainBuffer.write('var ${globalObject}$_=${_}'); | |
| 1449 if(isProgramSplit) { | |
| 1450 mainBuffer.add('${globalsHolder}.$globalObject$_=${_}'); | |
| 1451 } | |
| 1452 mainBuffer.write('new dart$N'); | |
| 1453 } | |
| 1454 | |
| 1455 mainBuffer.add('function ${namer.isolateName}()$_{}\n'); | |
| 1456 if (isProgramSplit) { | |
| 1457 mainBuffer | |
| 1458 .write('${globalsHolder}.${namer.isolateName}$_=$_' | |
| 1459 '${namer.isolateName}$N' | |
| 1460 '${globalsHolder}.$initName$_=${_}$initName$N'); | |
| 1461 } | |
| 1462 mainBuffer.add('init()$N$n'); | |
| 1463 mainBuffer.add('$isolateProperties$_=$_$isolatePropertiesName$N'); | |
| 1464 | |
| 1465 emitStaticFunctions(task.outputStaticLists[mainOutputUnit]); | |
| 1466 | |
| 1467 // Only output the classesCollector if we actually have any classes. | |
| 1468 if (!(nativeClasses.isEmpty && | |
| 1469 compiler.codegenWorld.staticFunctionsNeedingGetter.isEmpty && | |
| 1470 outputClassLists.values.every((classList) => classList.isEmpty) && | |
| 1471 typedefsNeededForReflection.isEmpty)) { | |
| 1472 // Shorten the code by using "$$" as temporary. | |
| 1473 classesCollector = r"$$"; | |
| 1474 mainBuffer.add('var $classesCollector$_=${_}Object.create(null)$N$n'); | |
| 1475 } | |
| 1476 | |
| 1477 if (!nativeClasses.isEmpty) { | |
| 1478 addComment('Native classes', mainBuffer); | |
| 1479 } | |
| 1480 | |
| 1481 List<ClassElement> classes = task.outputClassLists[mainOutputUnit]; | |
| 1482 if (classes != null) { | |
| 1483 for (ClassElement element in classes) { | |
| 1484 generateClass(element, getElementDescriptor(element)); | |
| 1485 } | |
| 1486 } | |
| 1487 | |
| 1488 if (compiler.enableMinification) { | |
| 1489 mainBuffer.write(';'); | |
| 1490 } | |
| 1491 | |
| 1492 if (elementDescriptors.isNotEmpty) { | |
| 1493 Iterable<LibraryElement> libraries = | |
| 1494 task.outputLibraryLists[mainOutputUnit]; | |
| 1495 if (libraries == null) libraries = []; | |
| 1496 emitLibraries(libraries); | |
| 1497 emitTypedefs(); | |
| 1498 emitMangledNames(); | |
| 1499 | |
| 1500 checkEverythingEmitted(elementDescriptors.keys); | |
| 1501 | |
| 1502 CodeBuffer libraryBuffer = new CodeBuffer(); | |
| 1503 for (LibraryElement library in Elements.sortedByPosition(libraries)) { | |
| 1504 writeLibraryDescriptors(libraryBuffer, library); | |
| 1505 elementDescriptors.remove(library); | |
| 1506 } | |
| 1507 | |
| 1508 mainBuffer | |
| 1509 ..write('(') | |
| 1510 ..write( | |
| 1511 jsAst.prettyPrint( | |
| 1512 getReflectionDataParser(classesCollector, backend), | |
| 1513 compiler)) | |
| 1514 ..write(')') | |
| 1515 ..write('([$n') | |
| 1516 ..add(libraryBuffer) | |
| 1517 ..write('])$N'); | |
| 1518 | |
| 1519 emitFinishClassesInvocationIfNecessary(mainBuffer); | |
| 1520 } | |
| 1521 | |
| 1522 typeTestEmitter.emitRuntimeTypeSupport(mainBuffer, mainOutputUnit); | |
| 1523 interceptorEmitter.emitGetInterceptorMethods(mainBuffer); | |
| 1524 interceptorEmitter.emitOneShotInterceptors(mainBuffer); | |
| 1525 | |
| 1526 if (task.outputContainsConstantList) { | |
| 1527 emitMakeConstantList(mainBuffer); | |
| 1528 } | |
| 1529 | |
| 1530 // Constants in checked mode call into RTI code to set type information | |
| 1531 // which may need getInterceptor (and one-shot interceptor) methods, so | |
| 1532 // we have to make sure that [emitGetInterceptorMethods] and | |
| 1533 // [emitOneShotInterceptors] have been called. | |
| 1534 emitCompileTimeConstants(mainBuffer, mainOutputUnit); | |
| 1535 | |
| 1536 emitDeferredBoilerPlate(mainBuffer, deferredLoadHashes); | |
| 1537 | |
| 1538 // Static field initializations require the classes and compile-time | |
| 1539 // constants to be set up. | |
| 1540 emitStaticNonFinalFieldInitializations(mainBuffer); | |
| 1541 interceptorEmitter.emitInterceptedNames(mainBuffer); | |
| 1542 interceptorEmitter.emitMapTypeToInterceptor(mainBuffer); | |
| 1543 emitLazilyInitializedStaticFields(mainBuffer); | |
| 1544 | |
| 1545 mainBuffer.writeln(); | |
| 1546 mainBuffer.add(nativeBuffer); | |
| 1547 | |
| 1548 metadataEmitter.emitMetadata(mainBuffer); | |
| 1549 | |
| 1550 isolateProperties = isolatePropertiesName; | |
| 1551 // The following code should not use the short-hand for the | |
| 1552 // initialStatics. | |
| 1553 mainBuffer.add('${namer.currentIsolate}$_=${_}null$N'); | |
| 1554 | |
| 1555 emitFinishIsolateConstructorInvocation(mainBuffer); | |
| 1556 mainBuffer.add( | |
| 1557 '${namer.currentIsolate}$_=${_}new ${namer.isolateName}()$N'); | |
| 1558 | |
| 1559 emitConvertToFastObjectFunction(); | |
| 1560 for (String globalObject in Namer.reservedGlobalObjectNames) { | |
| 1561 mainBuffer.add('$globalObject = convertToFastObject($globalObject)$N'); | |
| 1562 } | |
| 1563 if (DEBUG_FAST_OBJECTS) { | |
| 1564 mainBuffer.add(r''' | |
| 1565 // The following only works on V8 when run with option | |
| 1566 // "--allow-natives-syntax". We use'new Function' because the | |
| 1567 // miniparser does not understand V8 native syntax. | |
| 1568 if (typeof print === "function") { | |
| 1569 var HasFastProperties = | |
| 1570 new Function("a", "return %HasFastProperties(a)"); | |
| 1571 print("Size of global helper object: " | |
| 1572 + String(Object.getOwnPropertyNames(H).length) | |
| 1573 + ", fast properties " + HasFastProperties(H)); | |
| 1574 print("Size of global platform object: " | |
| 1575 + String(Object.getOwnPropertyNames(P).length) | |
| 1576 + ", fast properties " + HasFastProperties(P)); | |
| 1577 print("Size of global dart:html object: " | |
| 1578 + String(Object.getOwnPropertyNames(W).length) | |
| 1579 + ", fast properties " + HasFastProperties(W)); | |
| 1580 print("Size of isolate properties object: " | |
| 1581 + String(Object.getOwnPropertyNames($).length) | |
| 1582 + ", fast properties " + HasFastProperties($)); | |
| 1583 print("Size of constant object: " | |
| 1584 + String(Object.getOwnPropertyNames(C).length) | |
| 1585 + ", fast properties " + HasFastProperties(C)); | |
| 1586 var names = Object.getOwnPropertyNames($); | |
| 1587 for (var i = 0; i < names.length; i++) { | |
| 1588 print("$." + names[i]); | |
| 1589 } | |
| 1590 } | |
| 1591 '''); | |
| 1592 for (String object in Namer.userGlobalObjects) { | |
| 1593 mainBuffer.add(''' | |
| 1594 if (typeof print === "function") { | |
| 1595 print("Size of $object: " | |
| 1596 + String(Object.getOwnPropertyNames($object).length) | |
| 1597 + ", fast properties " + HasFastProperties($object)); | |
| 1598 } | |
| 1599 '''); | |
| 1600 } | |
| 1601 } | |
| 1602 | |
| 1603 jsAst.FunctionDeclaration precompiledFunctionAst = | |
| 1604 buildCspPrecompiledFunctionFor(mainOutputUnit); | |
| 1605 emitInitFunction(mainBuffer); | |
| 1606 emitMain(mainBuffer); | |
| 1607 mainBuffer.add('})()\n'); | |
| 1608 | |
| 1609 if (compiler.useContentSecurityPolicy) { | |
| 1610 mainBuffer.write( | |
| 1611 jsAst.prettyPrint( | |
| 1612 precompiledFunctionAst, | |
| 1613 compiler, | |
| 1614 monitor: compiler.dumpInfoTask, | |
| 1615 allowVariableMinification: false).getText()); | |
| 1616 } | |
| 1617 | |
| 1618 String assembledCode = mainBuffer.getText(); | |
| 1619 if (generateSourceMap) { | |
| 1620 outputSourceMap(assembledCode, mainBuffer, '', | |
| 1621 compiler.sourceMapUri, compiler.outputUri); | |
| 1622 mainBuffer.add( | |
| 1623 generateSourceMapTag(compiler.sourceMapUri, compiler.outputUri)); | |
| 1624 assembledCode = mainBuffer.getText(); | |
| 1625 } | |
| 1626 | |
| 1627 compiler.outputProvider('', 'js') | |
| 1628 ..add(assembledCode) | |
| 1629 ..close(); | |
| 1630 compiler.assembledCode = assembledCode; | |
| 1631 | |
| 1632 if (!compiler.useContentSecurityPolicy) { | |
| 1633 CodeBuffer cspBuffer = new CodeBuffer(); | |
| 1634 cspBuffer.add(mainBuffer); | |
| 1635 cspBuffer.write(""" | |
| 1636 { | |
| 1637 var message = | |
| 1638 'Deprecation: Automatic generation of output for Content Security\\n' + | |
| 1639 'Policy is deprecated and will be removed with the next development\\n' + | |
| 1640 'release. Use the --csp option to generate CSP restricted output.'; | |
| 1641 if (typeof dartPrint == "function") { | |
| 1642 dartPrint(message); | |
| 1643 } else if (typeof console == "object" && typeof console.log == "function") { | |
| 1644 console.log(message); | |
| 1645 } else if (typeof print == "function") { | |
| 1646 print(message); | |
| 1647 } | |
| 1648 }\n"""); | |
| 1649 | |
| 1650 cspBuffer.write( | |
| 1651 jsAst.prettyPrint( | |
| 1652 precompiledFunctionAst, compiler, | |
| 1653 allowVariableMinification: false).getText()); | |
| 1654 | |
| 1655 compiler.outputProvider('', 'precompiled.js') | |
| 1656 ..add(cspBuffer.getText()) | |
| 1657 ..close(); | |
| 1658 } | |
| 1659 } | |
| 1660 | |
| 1661 /// Returns a map from OutputUnit to a hash of its content. The hash uniquely | |
| 1662 /// identifies the code of the output-unit. It does not include | |
| 1663 /// boilerplate JS code, like the sourcemap directives or the hash | |
| 1664 /// itself. | |
| 1665 Map<OutputUnit, String> emitDeferredOutputUnits() { | |
| 1666 if (!compiler.deferredLoadTask.isProgramSplit) return const {}; | |
| 1667 | |
| 1668 Map<OutputUnit, CodeBuffer> outputBuffers = | |
| 1669 new Map<OutputUnit, CodeBuffer>(); | |
| 1670 | |
| 1671 for (OutputUnit outputUnit in compiler.deferredLoadTask.allOutputUnits) { | |
| 1672 if (outputUnit == compiler.deferredLoadTask.mainOutputUnit) continue; | |
| 1673 | |
| 1674 List<Element> functions = task.outputStaticLists[outputUnit]; | |
| 1675 if (functions != null) { | |
| 1676 emitStaticFunctions(functions); | |
| 1677 } | |
| 1678 | |
| 1679 List<ClassElement> classes = task.outputClassLists[outputUnit]; | |
| 1680 if (classes != null) { | |
| 1681 for (ClassElement element in classes) { | |
| 1682 generateClass(element, getElementDescriptor(element)); | |
| 1683 } | |
| 1684 } | |
| 1685 | |
| 1686 if (elementDescriptors.isNotEmpty) { | |
| 1687 Iterable<LibraryElement> libraries = | |
| 1688 task.outputLibraryLists[outputUnit]; | |
| 1689 if (libraries == null) libraries = []; | |
| 1690 emitLibraries(libraries); | |
| 1691 | |
| 1692 CodeBuffer buffer = new CodeBuffer(); | |
| 1693 outputBuffers[outputUnit] = buffer; | |
| 1694 for (LibraryElement library in Elements.sortedByPosition(libraries)) { | |
| 1695 writeLibraryDescriptors(buffer, library); | |
| 1696 elementDescriptors.remove(library); | |
| 1697 } | |
| 1698 } | |
| 1699 } | |
| 1700 | |
| 1701 return emitDeferredCode(outputBuffers); | |
| 1702 } | |
| 1703 | |
| 1704 CodeBuffer buildNativesBuffer() { | |
| 1705 // Emit native classes on [nativeBuffer]. | |
| 1706 final CodeBuffer nativeBuffer = new CodeBuffer(); | |
| 1707 | |
| 1708 if (nativeClasses.isEmpty) return nativeBuffer; | |
| 1709 | |
| 1710 | |
| 1711 addComment('Native classes', nativeBuffer); | |
| 1712 | |
| 1713 nativeEmitter.generateNativeClasses(nativeClasses, mainBuffer, | |
| 1714 additionalProperties); | |
| 1715 | |
| 1716 nativeEmitter.finishGenerateNativeClasses(); | |
| 1717 nativeEmitter.assembleCode(nativeBuffer); | |
| 1718 | |
| 1719 return nativeBuffer; | |
| 1720 } | |
| 1721 | |
| 1722 void emitProgram(Program program) { | |
| 1723 // Shorten the code by using [namer.currentIsolate] as temporary. | |
| 1724 isolateProperties = namer.currentIsolate; | |
| 1725 | |
| 1726 classesCollector = r"$$"; | |
| 1727 | |
| 1728 // Emit deferred units first, so we have their hashes. | |
| 1729 // Map from OutputUnit to a hash of its content. The hash uniquely | |
| 1730 // identifies the code of the output-unit. It does not include | |
| 1731 // boilerplate JS code, like the sourcemap directives or the hash | |
| 1732 // itself. | |
| 1733 Map<OutputUnit, String> deferredLoadHashes = emitDeferredOutputUnits(); | |
| 1734 CodeBuffer nativeBuffer = buildNativesBuffer(); | |
| 1735 emitMainOutputUnit(deferredLoadHashes, nativeBuffer); | |
| 1736 | |
| 1737 if (backend.requiresPreamble && | |
| 1738 !backend.htmlLibraryIsLoaded) { | |
| 1739 compiler.reportHint(NO_LOCATION_SPANNABLE, MessageKind.PREAMBLE); | |
| 1740 } | |
| 1741 } | |
| 1742 | |
| 1743 String generateSourceMapTag(Uri sourceMapUri, Uri fileUri) { | |
| 1744 if (sourceMapUri != null && fileUri != null) { | |
| 1745 String sourceMapFileName = relativize(fileUri, sourceMapUri, false); | |
| 1746 return ''' | |
| 1747 | |
| 1748 //# sourceMappingURL=$sourceMapFileName | |
| 1749 '''; | |
| 1750 } | |
| 1751 return ''; | |
| 1752 } | |
| 1753 | |
| 1754 ClassBuilder getElementDescriptor(Element element) { | |
| 1755 Element owner = element.library; | |
| 1756 if (!element.isLibrary && !element.isTopLevel && !element.isNative) { | |
| 1757 // For static (not top level) elements, record their code in a buffer | |
| 1758 // specific to the class. For now, not supported for native classes and | |
| 1759 // native elements. | |
| 1760 ClassElement cls = | |
| 1761 element.enclosingClassOrCompilationUnit.declaration; | |
| 1762 if (compiler.codegenWorld.directlyInstantiatedClasses.contains(cls) | |
| 1763 && !cls.isNative) { | |
| 1764 owner = cls; | |
| 1765 } | |
| 1766 } | |
| 1767 if (owner == null) { | |
| 1768 compiler.internalError(element, 'Owner is null.'); | |
| 1769 } | |
| 1770 return elementDescriptors.putIfAbsent( | |
| 1771 owner, | |
| 1772 () => new ClassBuilder(owner, namer)); | |
| 1773 } | |
| 1774 | |
| 1775 /// Emits support-code for deferred loading into [buffer]. | |
| 1776 void emitDeferredBoilerPlate(CodeBuffer buffer, | |
| 1777 Map<OutputUnit, String> deferredLoadHashes) { | |
| 1778 // Function for checking if a hunk is loaded given its hash. | |
| 1779 buffer.write(jsAst.prettyPrint( | |
| 1780 js('# = function(hunkHash) {' | |
| 1781 ' return !!$deferredInitializers[hunkHash];' | |
| 1782 '}', generateEmbeddedGlobalAccess(embeddedNames.IS_HUNK_LOADED)), | |
| 1783 compiler, monitor: compiler.dumpInfoTask)); | |
| 1784 buffer.write('$N'); | |
| 1785 // Function for initializing a loaded hunk, given its hash. | |
| 1786 buffer.write(jsAst.prettyPrint( | |
| 1787 js('# = function(hunkHash) {' | |
| 1788 ' $deferredInitializers[hunkHash](' | |
| 1789 '$globalsHolder, ${namer.currentIsolate})' | |
| 1790 '}', | |
| 1791 generateEmbeddedGlobalAccess( | |
| 1792 embeddedNames.INITIALIZE_LOADED_HUNK)), | |
| 1793 compiler, monitor: compiler.dumpInfoTask)); | |
| 1794 buffer.write('$N'); | |
| 1795 // Write a javascript mapping from Deferred import load ids (derrived | |
| 1796 // from the import prefix.) to a list of lists of uris of hunks to load, | |
| 1797 // and a corresponding mapping to a list of hashes used by | |
| 1798 // INITIALIZE_LOADED_HUNK and IS_HUNK_LOADED. | |
| 1799 Map<String, List<String>> deferredLibraryUris = | |
| 1800 new Map<String, List<String>>(); | |
| 1801 Map<String, List<String>> deferredLibraryHashes = | |
| 1802 new Map<String, List<String>>(); | |
| 1803 compiler.deferredLoadTask.hunksToLoad.forEach( | |
| 1804 (String loadId, List<OutputUnit>outputUnits) { | |
| 1805 List<String> uris = new List<String>(); | |
| 1806 List<String> hashes = new List<String>(); | |
| 1807 deferredLibraryHashes[loadId] = new List<String>(); | |
| 1808 for (OutputUnit outputUnit in outputUnits) { | |
| 1809 uris.add(deferredPartFileName(outputUnit)); | |
| 1810 hashes.add(deferredLoadHashes[outputUnit]); | |
| 1811 } | |
| 1812 | |
| 1813 deferredLibraryUris[loadId] = uris; | |
| 1814 deferredLibraryHashes[loadId] = hashes; | |
| 1815 }); | |
| 1816 | |
| 1817 void emitMapping(String name, Map<String, List<String>> mapping) { | |
| 1818 List<jsAst.Property> properties = new List<jsAst.Property>(); | |
| 1819 mapping.forEach((String key, List<String> values) { | |
| 1820 properties.add(new jsAst.Property(js.escapedString(key), | |
| 1821 new jsAst.ArrayInitializer.from( | |
| 1822 values.map(js.escapedString)))); | |
| 1823 }); | |
| 1824 jsAst.Node initializer = | |
| 1825 new jsAst.ObjectInitializer(properties, isOneLiner: true); | |
| 1826 | |
| 1827 jsAst.Node globalName = generateEmbeddedGlobalAccess(name); | |
| 1828 buffer.write(jsAst.prettyPrint( | |
| 1829 js("# = #", [globalName, initializer]), | |
| 1830 compiler, monitor: compiler.dumpInfoTask)); | |
| 1831 buffer.write('$N'); | |
| 1832 | |
| 1833 } | |
| 1834 | |
| 1835 emitMapping(embeddedNames.DEFERRED_LIBRARY_URIS, deferredLibraryUris); | |
| 1836 emitMapping(embeddedNames.DEFERRED_LIBRARY_HASHES, | |
| 1837 deferredLibraryHashes); | |
| 1838 } | |
| 1839 | |
| 1840 /// Emits code for all output units except the main. | |
| 1841 /// Returns a mapping from outputUnit to a hash of the corresponding hunk that | |
| 1842 /// can be used for calling the initializer. | |
| 1843 Map<OutputUnit, String> emitDeferredCode( | |
| 1844 Map<OutputUnit, CodeBuffer> deferredBuffers) { | |
| 1845 | |
| 1846 Map<OutputUnit, String> hunkHashes = new Map<OutputUnit, String>(); | |
| 1847 | |
| 1848 for (OutputUnit outputUnit in compiler.deferredLoadTask.allOutputUnits) { | |
| 1849 if (outputUnit == compiler.deferredLoadTask.mainOutputUnit) continue; | |
| 1850 | |
| 1851 CodeBuffer libraryDescriptorBuffer = deferredBuffers[outputUnit]; | |
| 1852 | |
| 1853 CodeBuffer outputBuffer = new CodeBuffer(); | |
| 1854 | |
| 1855 outputBuffer..write(buildGeneratedBy()) | |
| 1856 ..write('${deferredInitializers}.current$_=$_' | |
| 1857 'function$_(${globalsHolder}) {$N'); | |
| 1858 for (String globalObject in Namer.reservedGlobalObjectNames) { | |
| 1859 outputBuffer | |
| 1860 .write('var $globalObject$_=$_' | |
| 1861 '${globalsHolder}.$globalObject$N'); | |
| 1862 } | |
| 1863 outputBuffer | |
| 1864 ..write('var init$_=$_${globalsHolder}.init$N') | |
| 1865 ..write('var ${namer.isolateName}$_=$_' | |
| 1866 '${globalsHolder}.${namer.isolateName}$N'); | |
| 1867 if (libraryDescriptorBuffer != null) { | |
| 1868 // TODO(ahe): This defines a lot of properties on the | |
| 1869 // Isolate.prototype object. We know this will turn it into a | |
| 1870 // slow object in V8, so instead we should do something similar | |
| 1871 // to Isolate.$finishIsolateConstructor. | |
| 1872 outputBuffer | |
| 1873 ..write('var ${namer.currentIsolate}$_=$_$isolatePropertiesName$N') | |
| 1874 // The classesCollector object ($$). | |
| 1875 ..write('$classesCollector$_=${_}Object.create(null);$n') | |
| 1876 ..write('(') | |
| 1877 ..write( | |
| 1878 jsAst.prettyPrint( | |
| 1879 getReflectionDataParser(classesCollector, backend), | |
| 1880 compiler, monitor: compiler.dumpInfoTask)) | |
| 1881 ..write(')') | |
| 1882 ..write('([$n') | |
| 1883 ..addBuffer(libraryDescriptorBuffer) | |
| 1884 ..write('])$N'); | |
| 1885 | |
| 1886 if (outputClassLists.containsKey(outputUnit)) { | |
| 1887 outputBuffer.write( | |
| 1888 '$finishClassesName($classesCollector,$_${namer.currentIsolate},' | |
| 1889 '$_$isolatePropertiesName)$N'); | |
| 1890 } | |
| 1891 | |
| 1892 } | |
| 1893 | |
| 1894 // Set the currentIsolate variable to the current isolate (which is | |
| 1895 // provided as second argument). | |
| 1896 // We need to do this, because we use the same variable for setting up | |
| 1897 // the isolate-properties and for storing the current isolate. During | |
| 1898 // the setup (the code above this lines) we must set the variable to | |
| 1899 // the isolate-properties. | |
| 1900 // After we have done the setup (finishing with `finishClasses`) it must | |
| 1901 // point to the current Isolate. Otherwise all methods/functions | |
| 1902 // accessing isolate variables will access the wrong object. | |
| 1903 outputBuffer.write("${namer.currentIsolate}$_=${_}arguments[1]$N"); | |
| 1904 typeTestEmitter.emitRuntimeTypeSupport(outputBuffer, outputUnit); | |
| 1905 | |
| 1906 emitCompileTimeConstants(outputBuffer, outputUnit); | |
| 1907 outputBuffer.write('}$N'); | |
| 1908 | |
| 1909 if (compiler.useContentSecurityPolicy) { | |
| 1910 jsAst.FunctionDeclaration precompiledFunctionAst = | |
| 1911 buildCspPrecompiledFunctionFor(outputUnit); | |
| 1912 | |
| 1913 outputBuffer.write( | |
| 1914 jsAst.prettyPrint( | |
| 1915 precompiledFunctionAst, compiler, | |
| 1916 monitor: compiler.dumpInfoTask, | |
| 1917 allowVariableMinification: false).getText()); | |
| 1918 } | |
| 1919 | |
| 1920 // Make a unique hash of the code (before the sourcemaps are added) | |
| 1921 // This will be used to retrieve the initializing function from the global | |
| 1922 // variable. | |
| 1923 String hash = hashOfString(outputBuffer.getText()); | |
| 1924 | |
| 1925 outputBuffer.add('${deferredInitializers}["$hash"]$_=$_' | |
| 1926 '${deferredInitializers}.current$N'); | |
| 1927 | |
| 1928 String partPrefix = deferredPartFileName(outputUnit, addExtension: false); | |
| 1929 if (generateSourceMap) { | |
| 1930 Uri mapUri, partUri; | |
| 1931 Uri sourceMapUri = compiler.sourceMapUri; | |
| 1932 Uri outputUri = compiler.outputUri; | |
| 1933 | |
| 1934 String partName = "$partPrefix.part"; | |
| 1935 | |
| 1936 if (sourceMapUri != null) { | |
| 1937 String mapFileName = partName + ".js.map"; | |
| 1938 List<String> mapSegments = sourceMapUri.pathSegments.toList(); | |
| 1939 mapSegments[mapSegments.length - 1] = mapFileName; | |
| 1940 mapUri = compiler.sourceMapUri.replace(pathSegments: mapSegments); | |
| 1941 } | |
| 1942 | |
| 1943 if (outputUri != null) { | |
| 1944 String partFileName = partName + ".js"; | |
| 1945 List<String> partSegments = outputUri.pathSegments.toList(); | |
| 1946 partSegments[partSegments.length - 1] = partFileName; | |
| 1947 partUri = compiler.outputUri.replace(pathSegments: partSegments); | |
| 1948 } | |
| 1949 | |
| 1950 outputSourceMap(outputBuffer.getText(), outputBuffer, partName, | |
| 1951 mapUri, partUri); | |
| 1952 outputBuffer.add(generateSourceMapTag(mapUri, partUri)); | |
| 1953 } | |
| 1954 | |
| 1955 outputBuffers[outputUnit] = outputBuffer; | |
| 1956 compiler.outputProvider(partPrefix, 'part.js') | |
| 1957 ..add(outputBuffer.getText()) | |
| 1958 ..close(); | |
| 1959 | |
| 1960 hunkHashes[outputUnit] = hash; | |
| 1961 } | |
| 1962 return hunkHashes; | |
| 1963 } | |
| 1964 | |
| 1965 String buildGeneratedBy() { | |
| 1966 var suffix = ''; | |
| 1967 if (compiler.hasBuildId) suffix = ' version: ${compiler.buildId}'; | |
| 1968 return '// Generated by dart2js, the Dart to JavaScript compiler$suffix.\n'; | |
| 1969 } | |
| 1970 | |
| 1971 void outputSourceMap(String code, CodeBuffer buffer, String name, | |
| 1972 [Uri sourceMapUri, Uri fileUri]) { | |
| 1973 if (!generateSourceMap) return; | |
| 1974 // Create a source file for the compilation output. This allows using | |
| 1975 // [:getLine:] to transform offsets to line numbers in [SourceMapBuilder]. | |
| 1976 SourceFile compiledFile = new StringSourceFile(null, code); | |
| 1977 SourceMapBuilder sourceMapBuilder = | |
| 1978 new SourceMapBuilder(sourceMapUri, fileUri, compiledFile); | |
| 1979 buffer.forEachSourceLocation(sourceMapBuilder.addMapping); | |
| 1980 String sourceMap = sourceMapBuilder.build(); | |
| 1981 compiler.outputProvider(name, 'js.map') | |
| 1982 ..add(sourceMap) | |
| 1983 ..close(); | |
| 1984 } | |
| 1985 | |
| 1986 void invalidateCaches() { | |
| 1987 if (!compiler.hasIncrementalSupport) return; | |
| 1988 if (cachedElements.isEmpty) return; | |
| 1989 for (Element element in compiler.enqueuer.codegen.newlyEnqueuedElements) { | |
| 1990 if (element.isInstanceMember) { | |
| 1991 cachedClassBuilders.remove(element.enclosingClass); | |
| 1992 | |
| 1993 nativeEmitter.cachedBuilders.remove(element.enclosingClass); | |
| 1994 | |
| 1995 } | |
| 1996 } | |
| 1997 } | |
| 1998 } | |
| OLD | NEW |