| 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 final InterceptorEmitter interceptorEmitter = new InterceptorEmitter(); | |
| 16 | |
| 17 // TODO(johnniwinther): Wrap these fields in a caching strategy. | |
| 18 final Set<ConstantValue> cachedEmittedConstants; | |
| 19 final List<jsAst.Statement> cachedEmittedConstantsAst = <jsAst.Statement>[]; | |
| 20 final Map<Element, ClassBuilder> cachedClassBuilders; | |
| 21 final Set<Element> cachedElements; | |
| 22 | |
| 23 bool needsClassSupport = false; | |
| 24 bool needsMixinSupport = false; | |
| 25 bool needsLazyInitializer = false; | |
| 26 | |
| 27 /// True if [ContainerBuilder.addMemberMethodFromInfo] used "structured info", | |
| 28 /// that is, some function was needed for reflection, had stubs, or had a | |
| 29 /// super alias. | |
| 30 bool needsStructuredMemberInfo = false; | |
| 31 | |
| 32 final Namer namer; | |
| 33 ConstantEmitter constantEmitter; | |
| 34 NativeEmitter get nativeEmitter => task.nativeEmitter; | |
| 35 TypeTestRegistry get typeTestRegistry => task.typeTestRegistry; | |
| 36 | |
| 37 // The full code that is written to each hunk part-file. | |
| 38 Map<OutputUnit, CodeOutput> outputBuffers = new Map<OutputUnit, CodeOutput>(); | |
| 39 | |
| 40 String classesCollector; | |
| 41 Set<ClassElement> get neededClasses => task.neededClasses; | |
| 42 Map<OutputUnit, List<ClassElement>> get outputClassLists | |
| 43 => task.outputClassLists; | |
| 44 Map<OutputUnit, List<ConstantValue>> get outputConstantLists | |
| 45 => task.outputConstantLists; | |
| 46 final Map<jsAst.Name, String> mangledFieldNames = | |
| 47 new HashMap<jsAst.Name, String>(); | |
| 48 final Map<jsAst.Name, String> mangledGlobalFieldNames = | |
| 49 new HashMap<jsAst.Name, String>(); | |
| 50 final Set<jsAst.Name> recordedMangledNames = new Set<jsAst.Name>(); | |
| 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 /** | |
| 64 * List of expressions and statements that will be included in the | |
| 65 * precompiled function. | |
| 66 * | |
| 67 * To save space, dart2js normally generates constructors and accessors | |
| 68 * dynamically. This doesn't work in CSP mode, so dart2js emits them directly | |
| 69 * when in CSP mode. | |
| 70 */ | |
| 71 Map<OutputUnit, List<jsAst.Node>> _cspPrecompiledFunctions = | |
| 72 new Map<OutputUnit, List<jsAst.Node>>(); | |
| 73 | |
| 74 Map<OutputUnit, List<jsAst.Expression>> _cspPrecompiledConstructorNames = | |
| 75 new Map<OutputUnit, List<jsAst.Expression>>(); | |
| 76 | |
| 77 /** | |
| 78 * Accumulate properties for classes and libraries, describing their | |
| 79 * static/top-level members. | |
| 80 * Later, these members are emitted when the class or library is emitted. | |
| 81 * | |
| 82 * See [getElementDescriptor]. | |
| 83 */ | |
| 84 // TODO(ahe): Generate statics with their class, and store only libraries in | |
| 85 // this map. | |
| 86 final Map<Fragment, Map<Element, ClassBuilder>> elementDescriptors = | |
| 87 new Map<Fragment, Map<Element, ClassBuilder>>(); | |
| 88 | |
| 89 final bool generateSourceMap; | |
| 90 | |
| 91 OldEmitter(Compiler compiler, Namer namer, this.generateSourceMap, this.task) | |
| 92 : this.compiler = compiler, | |
| 93 this.namer = namer, | |
| 94 cachedEmittedConstants = compiler.cacheStrategy.newSet(), | |
| 95 cachedClassBuilders = compiler.cacheStrategy.newMap(), | |
| 96 cachedElements = compiler.cacheStrategy.newSet() { | |
| 97 constantEmitter = new ConstantEmitter( | |
| 98 compiler, namer, this.constantReference, constantListGenerator); | |
| 99 containerBuilder.emitter = this; | |
| 100 classEmitter.emitter = this; | |
| 101 nsmEmitter.emitter = this; | |
| 102 interceptorEmitter.emitter = this; | |
| 103 } | |
| 104 | |
| 105 List<jsAst.Node> cspPrecompiledFunctionFor(OutputUnit outputUnit) { | |
| 106 return _cspPrecompiledFunctions.putIfAbsent( | |
| 107 outputUnit, | |
| 108 () => new List<jsAst.Node>()); | |
| 109 } | |
| 110 | |
| 111 List<jsAst.Expression> cspPrecompiledConstructorNamesFor( | |
| 112 OutputUnit outputUnit) { | |
| 113 return _cspPrecompiledConstructorNames.putIfAbsent( | |
| 114 outputUnit, | |
| 115 () => new List<jsAst.Expression>()); | |
| 116 } | |
| 117 | |
| 118 /// Erases the precompiled information for csp mode for all output units. | |
| 119 /// Used by the incremental compiler. | |
| 120 void clearCspPrecompiledNodes() { | |
| 121 _cspPrecompiledFunctions.clear(); | |
| 122 _cspPrecompiledConstructorNames.clear(); | |
| 123 } | |
| 124 | |
| 125 @override | |
| 126 bool isConstantInlinedOrAlreadyEmitted(ConstantValue constant) { | |
| 127 if (constant.isFunction) return true; // Already emitted. | |
| 128 if (constant.isPrimitive) return true; // Inlined. | |
| 129 if (constant.isDummy) return true; // Inlined. | |
| 130 // The name is null when the constant is already a JS constant. | |
| 131 // TODO(floitsch): every constant should be registered, so that we can | |
| 132 // share the ones that take up too much space (like some strings). | |
| 133 if (namer.constantName(constant) == null) return true; | |
| 134 return false; | |
| 135 } | |
| 136 | |
| 137 @override | |
| 138 int compareConstants(ConstantValue a, ConstantValue b) { | |
| 139 // Inlined constants don't affect the order and sometimes don't even have | |
| 140 // names. | |
| 141 int cmp1 = isConstantInlinedOrAlreadyEmitted(a) ? 0 : 1; | |
| 142 int cmp2 = isConstantInlinedOrAlreadyEmitted(b) ? 0 : 1; | |
| 143 if (cmp1 + cmp2 < 2) return cmp1 - cmp2; | |
| 144 | |
| 145 // Emit constant interceptors first. Constant interceptors for primitives | |
| 146 // might be used by code that builds other constants. See Issue 18173. | |
| 147 if (a.isInterceptor != b.isInterceptor) { | |
| 148 return a.isInterceptor ? -1 : 1; | |
| 149 } | |
| 150 | |
| 151 // Sorting by the long name clusters constants with the same constructor | |
| 152 // which compresses a tiny bit better. | |
| 153 int r = namer.constantLongName(a).compareTo(namer.constantLongName(b)); | |
| 154 if (r != 0) return r; | |
| 155 // Resolve collisions in the long name by using the constant name (i.e. JS | |
| 156 // name) which is unique. | |
| 157 // TODO(herhut): Find a better way to resolve collisions. | |
| 158 return namer.constantName(a).hashCode.compareTo( | |
| 159 namer.constantName(b).hashCode); | |
| 160 } | |
| 161 | |
| 162 @override | |
| 163 jsAst.Expression constantReference(ConstantValue value) { | |
| 164 if (value.isFunction) { | |
| 165 FunctionConstantValue functionConstant = value; | |
| 166 return isolateStaticClosureAccess(functionConstant.element); | |
| 167 } | |
| 168 | |
| 169 // We are only interested in the "isInlined" part, but it does not hurt to | |
| 170 // test for the other predicates. | |
| 171 if (isConstantInlinedOrAlreadyEmitted(value)) { | |
| 172 return constantEmitter.generate(value); | |
| 173 } | |
| 174 return js('#.#', [namer.globalObjectForConstant(value), | |
| 175 namer.constantName(value)]); | |
| 176 } | |
| 177 | |
| 178 jsAst.Expression constantInitializerExpression(ConstantValue value) { | |
| 179 return constantEmitter.generate(value); | |
| 180 } | |
| 181 | |
| 182 String get name => 'CodeEmitter'; | |
| 183 | |
| 184 String get finishIsolateConstructorName | |
| 185 => '${namer.isolateName}.\$finishIsolateConstructor'; | |
| 186 String get isolatePropertiesName | |
| 187 => '${namer.isolateName}.${namer.isolatePropertiesName}'; | |
| 188 String get lazyInitializerProperty | |
| 189 => r'$lazy'; | |
| 190 String get lazyInitializerName | |
| 191 => '${namer.isolateName}.${lazyInitializerProperty}'; | |
| 192 String get initName => 'init'; | |
| 193 | |
| 194 jsAst.Name get makeConstListProperty | |
| 195 => namer.internalGlobal('makeConstantList'); | |
| 196 | |
| 197 /// The name of the property that contains all field names. | |
| 198 /// | |
| 199 /// This property is added to constructors when isolate support is enabled. | |
| 200 static const String FIELD_NAMES_PROPERTY_NAME = r"$__fields__"; | |
| 201 | |
| 202 /// For deferred loading we communicate the initializers via this global var. | |
| 203 final String deferredInitializers = r"$dart_deferred_initializers$"; | |
| 204 | |
| 205 /// Contains the global state that is needed to initialize and load a | |
| 206 /// deferred library. | |
| 207 String get globalsHolder => r"$globals$"; | |
| 208 | |
| 209 @override | |
| 210 jsAst.Expression generateEmbeddedGlobalAccess(String global) { | |
| 211 return js(generateEmbeddedGlobalAccessString(global)); | |
| 212 } | |
| 213 | |
| 214 String generateEmbeddedGlobalAccessString(String global) { | |
| 215 // TODO(floitsch): don't use 'init' as global embedder storage. | |
| 216 return '$initName.$global'; | |
| 217 } | |
| 218 | |
| 219 jsAst.PropertyAccess globalPropertyAccess(Element element) { | |
| 220 jsAst.Name name = namer.globalPropertyName(element); | |
| 221 jsAst.PropertyAccess pa = new jsAst.PropertyAccess( | |
| 222 new jsAst.VariableUse(namer.globalObjectFor(element)), | |
| 223 name); | |
| 224 return pa; | |
| 225 } | |
| 226 | |
| 227 @override | |
| 228 jsAst.Expression isolateLazyInitializerAccess(FieldElement element) { | |
| 229 return jsAst.js('#.#', [namer.globalObjectFor(element), | |
| 230 namer.lazyInitializerName(element)]); | |
| 231 } | |
| 232 | |
| 233 @override | |
| 234 jsAst.Expression isolateStaticClosureAccess(FunctionElement element) { | |
| 235 return jsAst.js('#.#()', | |
| 236 [namer.globalObjectFor(element), namer.staticClosureName(element)]); | |
| 237 } | |
| 238 | |
| 239 @override | |
| 240 jsAst.PropertyAccess staticFieldAccess(FieldElement element) { | |
| 241 return globalPropertyAccess(element); | |
| 242 } | |
| 243 | |
| 244 @override | |
| 245 jsAst.PropertyAccess staticFunctionAccess(FunctionElement element) { | |
| 246 return globalPropertyAccess(element); | |
| 247 } | |
| 248 | |
| 249 @override | |
| 250 jsAst.PropertyAccess constructorAccess(ClassElement element) { | |
| 251 return globalPropertyAccess(element); | |
| 252 } | |
| 253 | |
| 254 @override | |
| 255 jsAst.PropertyAccess prototypeAccess(ClassElement element, | |
| 256 bool hasBeenInstantiated) { | |
| 257 return jsAst.js('#.prototype', constructorAccess(element)); | |
| 258 } | |
| 259 | |
| 260 @override | |
| 261 jsAst.PropertyAccess interceptorClassAccess(ClassElement element) { | |
| 262 return globalPropertyAccess(element); | |
| 263 } | |
| 264 | |
| 265 @override | |
| 266 jsAst.PropertyAccess typeAccess(Element element) { | |
| 267 return globalPropertyAccess(element); | |
| 268 } | |
| 269 | |
| 270 @override | |
| 271 jsAst.Template templateForBuiltin(JsBuiltin builtin) { | |
| 272 switch (builtin) { | |
| 273 case JsBuiltin.dartObjectConstructor: | |
| 274 return jsAst.js.expressionTemplateYielding( | |
| 275 typeAccess(compiler.objectClass)); | |
| 276 | |
| 277 case JsBuiltin.isCheckPropertyToJsConstructorName: | |
| 278 int isPrefixLength = namer.operatorIsPrefix.length; | |
| 279 return jsAst.js.expressionTemplateFor('#.substring($isPrefixLength)'); | |
| 280 | |
| 281 case JsBuiltin.isFunctionType: | |
| 282 return backend.rti.representationGenerator.templateForIsFunctionType; | |
| 283 | |
| 284 case JsBuiltin.rawRtiToJsConstructorName: | |
| 285 return jsAst.js.expressionTemplateFor("#.$typeNameProperty"); | |
| 286 | |
| 287 case JsBuiltin.rawRuntimeType: | |
| 288 return jsAst.js.expressionTemplateFor("#.constructor"); | |
| 289 | |
| 290 case JsBuiltin.createFunctionTypeRti: | |
| 291 return backend.rti.representationGenerator | |
| 292 .templateForCreateFunctionType; | |
| 293 | |
| 294 case JsBuiltin.isSubtype: | |
| 295 // TODO(floitsch): move this closer to where is-check properties are | |
| 296 // built. | |
| 297 String isPrefix = namer.operatorIsPrefix; | |
| 298 return jsAst.js.expressionTemplateFor( | |
| 299 "('$isPrefix' + #) in #.prototype"); | |
| 300 | |
| 301 case JsBuiltin.isGivenTypeRti: | |
| 302 return jsAst.js.expressionTemplateFor('#.$typeNameProperty === #'); | |
| 303 | |
| 304 case JsBuiltin.getMetadata: | |
| 305 String metadataAccess = | |
| 306 generateEmbeddedGlobalAccessString(embeddedNames.METADATA); | |
| 307 return jsAst.js.expressionTemplateFor("$metadataAccess[#]"); | |
| 308 | |
| 309 case JsBuiltin.getType: | |
| 310 String typesAccess = | |
| 311 generateEmbeddedGlobalAccessString(embeddedNames.TYPES); | |
| 312 return jsAst.js.expressionTemplateFor("$typesAccess[#]"); | |
| 313 | |
| 314 default: | |
| 315 compiler.internalError(NO_LOCATION_SPANNABLE, | |
| 316 "Unhandled Builtin: $builtin"); | |
| 317 return null; | |
| 318 } | |
| 319 } | |
| 320 | |
| 321 List<jsAst.Statement> buildTrivialNsmHandlers(){ | |
| 322 return nsmEmitter.buildTrivialNsmHandlers(); | |
| 323 } | |
| 324 | |
| 325 jsAst.Statement buildNativeInfoHandler( | |
| 326 jsAst.Expression infoAccess, | |
| 327 jsAst.Expression constructorAccess, | |
| 328 jsAst.Expression subclassReadGenerator(jsAst.Expression subclass), | |
| 329 jsAst.Expression interceptorsByTagAccess, | |
| 330 jsAst.Expression leafTagsAccess) { | |
| 331 return NativeGenerator.buildNativeInfoHandler(infoAccess, constructorAccess, | |
| 332 subclassReadGenerator, | |
| 333 interceptorsByTagAccess, | |
| 334 leafTagsAccess); | |
| 335 } | |
| 336 | |
| 337 jsAst.ObjectInitializer generateInterceptedNamesSet() { | |
| 338 return interceptorEmitter.generateInterceptedNamesSet(); | |
| 339 } | |
| 340 | |
| 341 /// In minified mode we want to keep the name for the most common core types. | |
| 342 bool _isNativeTypeNeedingReflectionName(Element element) { | |
| 343 if (!element.isClass) return false; | |
| 344 return (element == compiler.intClass || | |
| 345 element == compiler.doubleClass || | |
| 346 element == compiler.numClass || | |
| 347 element == compiler.stringClass || | |
| 348 element == compiler.boolClass || | |
| 349 element == compiler.nullClass || | |
| 350 element == compiler.listClass); | |
| 351 } | |
| 352 | |
| 353 /// Returns the "reflection name" of an [Element] or [Selector]. | |
| 354 /// The reflection name of a getter 'foo' is 'foo'. | |
| 355 /// The reflection name of a setter 'foo' is 'foo='. | |
| 356 /// The reflection name of a method 'foo' is 'foo:N:M:O', where N is the | |
| 357 /// number of required arguments, M is the number of optional arguments, and | |
| 358 /// O is the named arguments. | |
| 359 /// The reflection name of a constructor is similar to a regular method but | |
| 360 /// starts with 'new '. | |
| 361 /// The reflection name of class 'C' is 'C'. | |
| 362 /// An anonymous mixin application has no reflection name. | |
| 363 /// This is used by js_mirrors.dart. | |
| 364 String getReflectionName(elementOrSelector, jsAst.Name mangledName) { | |
| 365 String name = elementOrSelector.name; | |
| 366 if (backend.shouldRetainName(name) || | |
| 367 elementOrSelector is Element && | |
| 368 // Make sure to retain names of unnamed constructors, and | |
| 369 // for common native types. | |
| 370 ((name == '' && | |
| 371 backend.isAccessibleByReflection(elementOrSelector)) || | |
| 372 _isNativeTypeNeedingReflectionName(elementOrSelector))) { | |
| 373 | |
| 374 // TODO(ahe): Enable the next line when I can tell the difference between | |
| 375 // an instance method and a global. They may have the same mangled name. | |
| 376 // if (recordedMangledNames.contains(mangledName)) return null; | |
| 377 recordedMangledNames.add(mangledName); | |
| 378 return getReflectionNameInternal(elementOrSelector, mangledName); | |
| 379 } | |
| 380 return null; | |
| 381 } | |
| 382 | |
| 383 String getReflectionNameInternal(elementOrSelector, | |
| 384 jsAst.Name mangledName) { | |
| 385 String name = namer.privateName(elementOrSelector.memberName); | |
| 386 if (elementOrSelector.isGetter) return name; | |
| 387 if (elementOrSelector.isSetter) { | |
| 388 if (mangledName is! SetterName) return '$name='; | |
| 389 SetterName setterName = mangledName; | |
| 390 jsAst.Name base = setterName.base; | |
| 391 jsAst.Name getter = namer.deriveGetterName(base); | |
| 392 mangledFieldNames.putIfAbsent(getter, () => name); | |
| 393 assert(mangledFieldNames[getter] == name); | |
| 394 recordedMangledNames.add(getter); | |
| 395 // TODO(karlklose,ahe): we do not actually need to store information | |
| 396 // about the name of this setter in the output, but it is needed for | |
| 397 // marking the function as invokable by reflection. | |
| 398 return '$name='; | |
| 399 } | |
| 400 if (elementOrSelector is Element && elementOrSelector.isClosure) { | |
| 401 // Closures are synthesized and their name might conflict with existing | |
| 402 // globals. Assign an illegal name, and make sure they don't clash | |
| 403 // with each other. | |
| 404 return " $name"; | |
| 405 } | |
| 406 if (elementOrSelector is Selector | |
| 407 || elementOrSelector.isFunction | |
| 408 || elementOrSelector.isConstructor) { | |
| 409 int positionalParameterCount; | |
| 410 String namedArguments = ''; | |
| 411 bool isConstructor = false; | |
| 412 if (elementOrSelector is Selector) { | |
| 413 CallStructure callStructure = elementOrSelector.callStructure; | |
| 414 positionalParameterCount = callStructure.positionalArgumentCount; | |
| 415 namedArguments = namedParametersAsReflectionNames(callStructure); | |
| 416 } else { | |
| 417 FunctionElement function = elementOrSelector; | |
| 418 if (function.isConstructor) { | |
| 419 isConstructor = true; | |
| 420 name = Elements.reconstructConstructorName(function); | |
| 421 } | |
| 422 FunctionSignature signature = function.functionSignature; | |
| 423 positionalParameterCount = signature.requiredParameterCount; | |
| 424 if (signature.optionalParametersAreNamed) { | |
| 425 var names = []; | |
| 426 for (Element e in signature.optionalParameters) { | |
| 427 names.add(e.name); | |
| 428 } | |
| 429 CallStructure callStructure = | |
| 430 new CallStructure(positionalParameterCount, names); | |
| 431 namedArguments = namedParametersAsReflectionNames(callStructure); | |
| 432 } else { | |
| 433 // Named parameters are handled differently by mirrors. For unnamed | |
| 434 // parameters, they are actually required if invoked | |
| 435 // reflectively. Also, if you have a method c(x) and c([x]) they both | |
| 436 // get the same mangled name, so they must have the same reflection | |
| 437 // name. | |
| 438 positionalParameterCount += signature.optionalParameterCount; | |
| 439 } | |
| 440 } | |
| 441 String suffix = '$name:$positionalParameterCount$namedArguments'; | |
| 442 return (isConstructor) ? 'new $suffix' : suffix; | |
| 443 } | |
| 444 Element element = elementOrSelector; | |
| 445 if (element.isGenerativeConstructorBody) { | |
| 446 return null; | |
| 447 } else if (element.isClass) { | |
| 448 ClassElement cls = element; | |
| 449 if (cls.isUnnamedMixinApplication) return null; | |
| 450 return cls.name; | |
| 451 } else if (element.isTypedef) { | |
| 452 return element.name; | |
| 453 } | |
| 454 throw compiler.internalError(element, | |
| 455 'Do not know how to reflect on this $element.'); | |
| 456 } | |
| 457 | |
| 458 String namedParametersAsReflectionNames(CallStructure structure) { | |
| 459 if (structure.isUnnamed) return ''; | |
| 460 String names = structure.getOrderedNamedArguments().join(':'); | |
| 461 return ':$names'; | |
| 462 } | |
| 463 | |
| 464 jsAst.Statement buildCspPrecompiledFunctionFor( | |
| 465 OutputUnit outputUnit) { | |
| 466 if (compiler.useContentSecurityPolicy) { | |
| 467 // TODO(ahe): Compute a hash code. | |
| 468 // TODO(sigurdm): Avoid this precompiled function. Generated | |
| 469 // constructor-functions and getter/setter functions can be stored in the | |
| 470 // library-description table. Setting properties on these can be moved to | |
| 471 // finishClasses. | |
| 472 return js.statement(r""" | |
| 473 #precompiled = function ($collectedClasses$) { | |
| 474 #norename; | |
| 475 var $desc; | |
| 476 #functions; | |
| 477 return #result; | |
| 478 };""", | |
| 479 {'norename': new jsAst.Comment("// ::norenaming:: "), | |
| 480 'precompiled': generateEmbeddedGlobalAccess(embeddedNames.PRECOMPILED), | |
| 481 'functions': cspPrecompiledFunctionFor(outputUnit), | |
| 482 'result': new jsAst.ArrayInitializer( | |
| 483 cspPrecompiledConstructorNamesFor(outputUnit))}); | |
| 484 } else { | |
| 485 return js.comment("Constructors are generated at runtime."); | |
| 486 } | |
| 487 } | |
| 488 | |
| 489 void assembleClass(Class cls, ClassBuilder enclosingBuilder, | |
| 490 Fragment fragment) { | |
| 491 ClassElement classElement = cls.element; | |
| 492 compiler.withCurrentElement(classElement, () { | |
| 493 if (compiler.hasIncrementalSupport) { | |
| 494 ClassBuilder cachedBuilder = | |
| 495 cachedClassBuilders.putIfAbsent(classElement, () { | |
| 496 ClassBuilder builder = | |
| 497 new ClassBuilder.forClass(classElement, namer); | |
| 498 classEmitter.emitClass(cls, builder, fragment); | |
| 499 return builder; | |
| 500 }); | |
| 501 invariant(classElement, cachedBuilder.fields.isEmpty); | |
| 502 invariant(classElement, cachedBuilder.superName == null); | |
| 503 invariant(classElement, cachedBuilder.functionType == null); | |
| 504 invariant(classElement, cachedBuilder.fieldMetadata == null); | |
| 505 enclosingBuilder.properties.addAll(cachedBuilder.properties); | |
| 506 } else { | |
| 507 classEmitter.emitClass(cls, enclosingBuilder, fragment); | |
| 508 } | |
| 509 }); | |
| 510 } | |
| 511 | |
| 512 void assembleStaticFunctions(Iterable<Method> staticFunctions, | |
| 513 Fragment fragment) { | |
| 514 if (staticFunctions == null) return; | |
| 515 | |
| 516 for (Method method in staticFunctions) { | |
| 517 Element element = method.element; | |
| 518 // We need to filter out null-elements for the interceptors. | |
| 519 // TODO(floitsch): use the precomputed interceptors here. | |
| 520 if (element == null) continue; | |
| 521 ClassBuilder builder = new ClassBuilder.forStatics(element, namer); | |
| 522 containerBuilder.addMemberMethod(method, builder); | |
| 523 getElementDescriptor(element, fragment).properties | |
| 524 .addAll(builder.properties); | |
| 525 } | |
| 526 } | |
| 527 | |
| 528 jsAst.Statement buildStaticNonFinalFieldInitializations( | |
| 529 OutputUnit outputUnit) { | |
| 530 jsAst.Statement buildInitialization(Element element, | |
| 531 jsAst.Expression initialValue) { | |
| 532 // Note: `namer.currentIsolate` refers to the isolate properties here. | |
| 533 return js.statement('${namer.currentIsolate}.# = #', | |
| 534 [namer.globalPropertyName(element), initialValue]); | |
| 535 } | |
| 536 | |
| 537 bool inMainUnit = (outputUnit == compiler.deferredLoadTask.mainOutputUnit); | |
| 538 JavaScriptConstantCompiler handler = backend.constants; | |
| 539 List<jsAst.Statement> parts = <jsAst.Statement>[]; | |
| 540 | |
| 541 Iterable<Element> fields = task.outputStaticNonFinalFieldLists[outputUnit]; | |
| 542 // If the outputUnit does not contain any static non-final fields, then | |
| 543 // [fields] is `null`. | |
| 544 if (fields != null) { | |
| 545 for (Element element in fields) { | |
| 546 compiler.withCurrentElement(element, () { | |
| 547 ConstantValue constant = handler.getInitialValueFor(element); | |
| 548 parts.add(buildInitialization(element, constantReference(constant))); | |
| 549 }); | |
| 550 } | |
| 551 } | |
| 552 | |
| 553 if (inMainUnit && task.outputStaticNonFinalFieldLists.length > 1) { | |
| 554 // In the main output-unit we output a stub initializer for deferred | |
| 555 // variables, so that `isolateProperties` stays a fast object. | |
| 556 task.outputStaticNonFinalFieldLists.forEach( | |
| 557 (OutputUnit fieldsOutputUnit, Iterable<VariableElement> fields) { | |
| 558 if (fieldsOutputUnit == outputUnit) return; // Skip the main unit. | |
| 559 for (Element element in fields) { | |
| 560 compiler.withCurrentElement(element, () { | |
| 561 parts.add(buildInitialization(element, jsAst.number(0))); | |
| 562 }); | |
| 563 } | |
| 564 }); | |
| 565 } | |
| 566 | |
| 567 return new jsAst.Block(parts); | |
| 568 } | |
| 569 | |
| 570 jsAst.Statement buildLazilyInitializedStaticFields() { | |
| 571 JavaScriptConstantCompiler handler = backend.constants; | |
| 572 List<VariableElement> lazyFields = | |
| 573 handler.getLazilyInitializedFieldsForEmission(); | |
| 574 if (lazyFields.isNotEmpty) { | |
| 575 needsLazyInitializer = true; | |
| 576 List<jsAst.Expression> laziesInfo = buildLaziesInfo(lazyFields); | |
| 577 return js.statement(''' | |
| 578 (function(lazies) { | |
| 579 for (var i = 0; i < lazies.length; ) { | |
| 580 var fieldName = lazies[i++]; | |
| 581 var getterName = lazies[i++]; | |
| 582 if (#notMinified) { | |
| 583 var staticName = lazies[i++]; | |
| 584 } | |
| 585 var lazyValue = lazies[i++]; | |
| 586 | |
| 587 // We build the lazy-check here: | |
| 588 // lazyInitializer(fieldName, getterName, lazyValue, staticName); | |
| 589 // 'staticName' is used for error reporting in non-minified mode. | |
| 590 // 'lazyValue' must be a closure that constructs the initial value. | |
| 591 if (#notMinified) { | |
| 592 #lazy(fieldName, getterName, lazyValue, staticName); | |
| 593 } else { | |
| 594 #lazy(fieldName, getterName, lazyValue); | |
| 595 } | |
| 596 } | |
| 597 })(#laziesInfo) | |
| 598 ''', {'notMinified': !compiler.enableMinification, | |
| 599 'laziesInfo': new jsAst.ArrayInitializer(laziesInfo), | |
| 600 'lazy': js(lazyInitializerName)}); | |
| 601 } else { | |
| 602 return js.comment("No lazy statics."); | |
| 603 } | |
| 604 } | |
| 605 | |
| 606 List<jsAst.Expression> buildLaziesInfo(List<VariableElement> lazies) { | |
| 607 List<jsAst.Expression> laziesInfo = <jsAst.Expression>[]; | |
| 608 for (VariableElement element in Elements.sortedByPosition(lazies)) { | |
| 609 jsAst.Expression code = backend.generatedCode[element]; | |
| 610 // The code is null if we ended up not needing the lazily | |
| 611 // initialized field after all because of constant folding | |
| 612 // before code generation. | |
| 613 if (code == null) continue; | |
| 614 laziesInfo.add(js.quoteName(namer.globalPropertyName(element))); | |
| 615 laziesInfo.add(js.quoteName(namer.lazyInitializerName(element))); | |
| 616 if (!compiler.enableMinification) { | |
| 617 laziesInfo.add(js.string(element.name)); | |
| 618 } | |
| 619 laziesInfo.add(code); | |
| 620 } | |
| 621 return laziesInfo; | |
| 622 } | |
| 623 | |
| 624 // TODO(sra): Remove this unused function. | |
| 625 jsAst.Expression buildLazilyInitializedStaticField( | |
| 626 VariableElement element, {String isolateProperties}) { | |
| 627 jsAst.Expression code = backend.generatedCode[element]; | |
| 628 // The code is null if we ended up not needing the lazily | |
| 629 // initialized field after all because of constant folding | |
| 630 // before code generation. | |
| 631 if (code == null) return null; | |
| 632 // The code only computes the initial value. We build the lazy-check | |
| 633 // here: | |
| 634 // lazyInitializer(fieldName, getterName, initial, name, prototype); | |
| 635 // The name is used for error reporting. The 'initial' must be a | |
| 636 // closure that constructs the initial value. | |
| 637 if (isolateProperties != null) { | |
| 638 // This is currently only used in incremental compilation to patch | |
| 639 // in new lazy values. | |
| 640 return js('#(#,#,#,#,#)', | |
| 641 [js(lazyInitializerName), | |
| 642 js.quoteName(namer.globalPropertyName(element)), | |
| 643 js.quoteName(namer.lazyInitializerName(element)), | |
| 644 code, | |
| 645 js.string(element.name), | |
| 646 isolateProperties]); | |
| 647 } | |
| 648 | |
| 649 if (compiler.enableMinification) { | |
| 650 return js('#(#,#,#)', | |
| 651 [js(lazyInitializerName), | |
| 652 js.quoteName(namer.globalPropertyName(element)), | |
| 653 js.quoteName(namer.lazyInitializerName(element)), | |
| 654 code]); | |
| 655 } else { | |
| 656 return js('#(#,#,#,#)', | |
| 657 [js(lazyInitializerName), | |
| 658 js.quoteName(namer.globalPropertyName(element)), | |
| 659 js.quoteName(namer.lazyInitializerName(element)), | |
| 660 code, | |
| 661 js.string(element.name)]); | |
| 662 } | |
| 663 } | |
| 664 | |
| 665 jsAst.Statement buildMetadata(Program program, OutputUnit outputUnit) { | |
| 666 List<jsAst.Statement> parts = <jsAst.Statement>[]; | |
| 667 | |
| 668 jsAst.Expression types = program.metadataTypesForOutputUnit(outputUnit); | |
| 669 | |
| 670 if (outputUnit == compiler.deferredLoadTask.mainOutputUnit) { | |
| 671 jsAst.Expression metadataAccess = | |
| 672 generateEmbeddedGlobalAccess(embeddedNames.METADATA); | |
| 673 jsAst.Expression typesAccess = | |
| 674 generateEmbeddedGlobalAccess(embeddedNames.TYPES); | |
| 675 | |
| 676 parts..add(js.statement('# = #;', [metadataAccess, program.metadata])) | |
| 677 ..add(js.statement('# = #;', [typesAccess, types])); | |
| 678 } else if (types != null) { | |
| 679 parts.add(js.statement('var ${namer.deferredTypesName} = #;', | |
| 680 types)); | |
| 681 } | |
| 682 return new jsAst.Block(parts); | |
| 683 } | |
| 684 | |
| 685 jsAst.Statement buildCompileTimeConstants(List<Constant> constants, | |
| 686 {bool isMainFragment}) { | |
| 687 assert(isMainFragment != null); | |
| 688 | |
| 689 if (constants.isEmpty) return js.comment("No constants in program."); | |
| 690 List<jsAst.Statement> parts = <jsAst.Statement>[]; | |
| 691 if (compiler.hasIncrementalSupport && isMainFragment) { | |
| 692 parts = cachedEmittedConstantsAst; | |
| 693 } | |
| 694 for (Constant constant in constants) { | |
| 695 ConstantValue constantValue = constant.value; | |
| 696 if (compiler.hasIncrementalSupport && isMainFragment) { | |
| 697 if (cachedEmittedConstants.contains(constantValue)) continue; | |
| 698 cachedEmittedConstants.add(constantValue); | |
| 699 } | |
| 700 parts.add(buildConstantInitializer(constantValue)); | |
| 701 } | |
| 702 | |
| 703 return new jsAst.Block(parts); | |
| 704 } | |
| 705 | |
| 706 jsAst.Statement buildConstantInitializer(ConstantValue constant) { | |
| 707 jsAst.Name name = namer.constantName(constant); | |
| 708 return js.statement('#.# = #', | |
| 709 [namer.globalObjectForConstant(constant), name, | |
| 710 constantInitializerExpression(constant)]); | |
| 711 } | |
| 712 | |
| 713 jsAst.Expression constantListGenerator(jsAst.Expression array) { | |
| 714 // TODO(floitsch): there is no harm in caching the template. | |
| 715 return js('${namer.isolateName}.#(#)', [makeConstListProperty, array]); | |
| 716 } | |
| 717 | |
| 718 jsAst.Statement buildMakeConstantList() { | |
| 719 if (task.outputContainsConstantList) { | |
| 720 return js.statement(r''' | |
| 721 // Functions are stored in the hidden class and not as properties in | |
| 722 // the object. We never actually look at the value, but only want | |
| 723 // to know if the property exists. | |
| 724 #.# = function (list) { | |
| 725 list.immutable$list = Array; | |
| 726 list.fixed$length = Array; | |
| 727 return list; | |
| 728 }''', | |
| 729 [namer.isolateName, makeConstListProperty]); | |
| 730 } else { | |
| 731 return js.comment("Output contains no constant list."); | |
| 732 } | |
| 733 } | |
| 734 | |
| 735 jsAst.Statement buildFunctionThatReturnsNull() { | |
| 736 return js.statement('#.# = function() {}', | |
| 737 [namer.isolateName, | |
| 738 backend.rti.getFunctionThatReturnsNullName]); | |
| 739 } | |
| 740 | |
| 741 jsAst.Expression generateFunctionThatReturnsNull() { | |
| 742 return js("#.#", [namer.isolateName, | |
| 743 backend.rti.getFunctionThatReturnsNullName]); | |
| 744 } | |
| 745 | |
| 746 buildMain(jsAst.Statement invokeMain) { | |
| 747 if (compiler.isMockCompilation) return js.comment("Mock compilation"); | |
| 748 | |
| 749 List<jsAst.Statement> parts = <jsAst.Statement>[]; | |
| 750 | |
| 751 if (NativeGenerator.needsIsolateAffinityTagInitialization(backend)) { | |
| 752 parts.add( | |
| 753 NativeGenerator.generateIsolateAffinityTagInitialization( | |
| 754 backend, | |
| 755 generateEmbeddedGlobalAccess, | |
| 756 js("convertToFastObject", []))); | |
| 757 } | |
| 758 | |
| 759 parts..add(js.comment('BEGIN invoke [main].')) | |
| 760 ..add(invokeMain) | |
| 761 ..add(js.comment('END invoke [main].')); | |
| 762 | |
| 763 return new jsAst.Block(parts); | |
| 764 } | |
| 765 | |
| 766 jsAst.Statement buildInitFunction() { | |
| 767 jsAst.Expression allClassesAccess = | |
| 768 generateEmbeddedGlobalAccess(embeddedNames.ALL_CLASSES); | |
| 769 jsAst.Expression getTypeFromNameAccess = | |
| 770 generateEmbeddedGlobalAccess(embeddedNames.GET_TYPE_FROM_NAME); | |
| 771 jsAst.Expression interceptorsByTagAccess = | |
| 772 generateEmbeddedGlobalAccess(embeddedNames.INTERCEPTORS_BY_TAG); | |
| 773 jsAst.Expression leafTagsAccess = | |
| 774 generateEmbeddedGlobalAccess(embeddedNames.LEAF_TAGS); | |
| 775 jsAst.Expression finishedClassesAccess = | |
| 776 generateEmbeddedGlobalAccess(embeddedNames.FINISHED_CLASSES); | |
| 777 jsAst.Expression cyclicThrow = | |
| 778 staticFunctionAccess(backend.getCyclicThrowHelper()); | |
| 779 jsAst.Expression laziesAccess = | |
| 780 generateEmbeddedGlobalAccess(embeddedNames.LAZIES); | |
| 781 | |
| 782 return js.statement(''' | |
| 783 function init() { | |
| 784 $isolatePropertiesName = Object.create(null); | |
| 785 #allClasses = map(); | |
| 786 #getTypeFromName = function(name) {return #allClasses[name];}; | |
| 787 #interceptorsByTag = map(); | |
| 788 #leafTags = map(); | |
| 789 #finishedClasses = map(); | |
| 790 | |
| 791 if (#needsLazyInitializer) { | |
| 792 // [staticName] is only provided in non-minified mode. If missing, we | |
| 793 // fall back to [fieldName]. Likewise, [prototype] is optional and | |
| 794 // defaults to the isolateProperties object. | |
| 795 $lazyInitializerName = function (fieldName, getterName, lazyValue, | |
| 796 staticName, prototype) { | |
| 797 if (!#lazies) #lazies = Object.create(null); | |
| 798 #lazies[fieldName] = getterName; | |
| 799 | |
| 800 // 'prototype' will be undefined except if we are doing an update | |
| 801 // during incremental compilation. In this case we put the lazy | |
| 802 // field directly on the isolate instead of the isolateProperties. | |
| 803 prototype = prototype || $isolatePropertiesName; | |
| 804 var sentinelUndefined = {}; | |
| 805 var sentinelInProgress = {}; | |
| 806 prototype[fieldName] = sentinelUndefined; | |
| 807 | |
| 808 prototype[getterName] = function () { | |
| 809 var result = this[fieldName]; | |
| 810 try { | |
| 811 if (result === sentinelUndefined) { | |
| 812 this[fieldName] = sentinelInProgress; | |
| 813 | |
| 814 try { | |
| 815 result = this[fieldName] = lazyValue(); | |
| 816 } finally { | |
| 817 // Use try-finally, not try-catch/throw as it destroys the | |
| 818 // stack trace. | |
| 819 if (result === sentinelUndefined) | |
| 820 this[fieldName] = null; | |
| 821 } | |
| 822 } else { | |
| 823 if (result === sentinelInProgress) | |
| 824 // In minified mode, static name is not provided, so fall | |
| 825 // back to the minified fieldName. | |
| 826 #cyclicThrow(staticName || fieldName); | |
| 827 } | |
| 828 | |
| 829 return result; | |
| 830 } finally { | |
| 831 this[getterName] = function() { return this[fieldName]; }; | |
| 832 } | |
| 833 } | |
| 834 } | |
| 835 } | |
| 836 | |
| 837 // We replace the old Isolate function with a new one that initializes | |
| 838 // all its fields with the initial (and often final) value of all | |
| 839 // globals. | |
| 840 // | |
| 841 // We also copy over old values like the prototype, and the | |
| 842 // isolateProperties themselves. | |
| 843 $finishIsolateConstructorName = function (oldIsolate) { | |
| 844 var isolateProperties = oldIsolate.#isolatePropertiesName; | |
| 845 function Isolate() { | |
| 846 | |
| 847 var staticNames = Object.keys(isolateProperties); | |
| 848 for (var i = 0; i < staticNames.length; i++) { | |
| 849 var staticName = staticNames[i]; | |
| 850 this[staticName] = isolateProperties[staticName]; | |
| 851 } | |
| 852 | |
| 853 // Reset lazy initializers to null. | |
| 854 // When forcing the object to fast mode (below) v8 will consider | |
| 855 // functions as part the object's map. Since we will change them | |
| 856 // (after the first call to the getter), we would have a map | |
| 857 // transition. | |
| 858 var lazies = init.lazies; | |
| 859 var lazyInitializers = lazies ? Object.keys(lazies) : []; | |
| 860 for (var i = 0; i < lazyInitializers.length; i++) { | |
| 861 this[lazies[lazyInitializers[i]]] = null; | |
| 862 } | |
| 863 | |
| 864 // Use the newly created object as prototype. In Chrome, | |
| 865 // this creates a hidden class for the object and makes | |
| 866 // sure it is fast to access. | |
| 867 function ForceEfficientMap() {} | |
| 868 ForceEfficientMap.prototype = this; | |
| 869 new ForceEfficientMap(); | |
| 870 | |
| 871 // Now, after being a fast map we can set the lazies again. | |
| 872 for (var i = 0; i < lazyInitializers.length; i++) { | |
| 873 var lazyInitName = lazies[lazyInitializers[i]]; | |
| 874 this[lazyInitName] = isolateProperties[lazyInitName]; | |
| 875 } | |
| 876 } | |
| 877 Isolate.prototype = oldIsolate.prototype; | |
| 878 Isolate.prototype.constructor = Isolate; | |
| 879 Isolate.#isolatePropertiesName = isolateProperties; | |
| 880 if (#outputContainsConstantList) { | |
| 881 Isolate.#makeConstListProperty = oldIsolate.#makeConstListProperty; | |
| 882 } | |
| 883 Isolate.#functionThatReturnsNullProperty = | |
| 884 oldIsolate.#functionThatReturnsNullProperty; | |
| 885 if (#hasIncrementalSupport) { | |
| 886 Isolate.#lazyInitializerProperty = | |
| 887 oldIsolate.#lazyInitializerProperty; | |
| 888 } | |
| 889 return Isolate; | |
| 890 } | |
| 891 | |
| 892 }''', {'allClasses': allClassesAccess, | |
| 893 'getTypeFromName': getTypeFromNameAccess, | |
| 894 'interceptorsByTag': interceptorsByTagAccess, | |
| 895 'leafTags': leafTagsAccess, | |
| 896 'finishedClasses': finishedClassesAccess, | |
| 897 'needsLazyInitializer': needsLazyInitializer, | |
| 898 'lazies': laziesAccess, 'cyclicThrow': cyclicThrow, | |
| 899 'isolatePropertiesName': namer.isolatePropertiesName, | |
| 900 'outputContainsConstantList': task.outputContainsConstantList, | |
| 901 'makeConstListProperty': makeConstListProperty, | |
| 902 'functionThatReturnsNullProperty': | |
| 903 backend.rti.getFunctionThatReturnsNullName, | |
| 904 'hasIncrementalSupport': compiler.hasIncrementalSupport, | |
| 905 'lazyInitializerProperty': lazyInitializerProperty,}); | |
| 906 } | |
| 907 | |
| 908 jsAst.Statement buildConvertToFastObjectFunction() { | |
| 909 List<jsAst.Statement> debugCode = <jsAst.Statement>[]; | |
| 910 if (DEBUG_FAST_OBJECTS) { | |
| 911 debugCode.add(js.statement(r''' | |
| 912 // The following only works on V8 when run with option | |
| 913 // "--allow-natives-syntax". We use'new Function' because the | |
| 914 // miniparser does not understand V8 native syntax. | |
| 915 if (typeof print === "function") { | |
| 916 var HasFastProperties = | |
| 917 new Function("a", "return %HasFastProperties(a)"); | |
| 918 print("Size of global object: " | |
| 919 + String(Object.getOwnPropertyNames(properties).length) | |
| 920 + ", fast properties " + HasFastProperties(properties)); | |
| 921 }''')); | |
| 922 } | |
| 923 | |
| 924 return js.statement(r''' | |
| 925 function convertToFastObject(properties) { | |
| 926 // Create an instance that uses 'properties' as prototype. This should | |
| 927 // make 'properties' a fast object. | |
| 928 function MyClass() {}; | |
| 929 MyClass.prototype = properties; | |
| 930 new MyClass(); | |
| 931 #; | |
| 932 return properties; | |
| 933 }''', [debugCode]); | |
| 934 } | |
| 935 | |
| 936 jsAst.Statement buildConvertToSlowObjectFunction() { | |
| 937 return js.statement(r''' | |
| 938 function convertToSlowObject(properties) { | |
| 939 // Add and remove a property to make the object transition into hashmap | |
| 940 // mode. | |
| 941 properties.__MAGIC_SLOW_PROPERTY = 1; | |
| 942 delete properties.__MAGIC_SLOW_PROPERTY; | |
| 943 return properties; | |
| 944 }'''); | |
| 945 } | |
| 946 | |
| 947 jsAst.Statement buildSupportsDirectProtoAccess() { | |
| 948 jsAst.Statement supportsDirectProtoAccess; | |
| 949 | |
| 950 if (compiler.hasIncrementalSupport) { | |
| 951 supportsDirectProtoAccess = js.statement(r''' | |
| 952 var supportsDirectProtoAccess = false; | |
| 953 '''); | |
| 954 } else { | |
| 955 supportsDirectProtoAccess = js.statement(r''' | |
| 956 var supportsDirectProtoAccess = (function () { | |
| 957 var cls = function () {}; | |
| 958 cls.prototype = {'p': {}}; | |
| 959 var object = new cls(); | |
| 960 return object.__proto__ && | |
| 961 object.__proto__.p === cls.prototype.p; | |
| 962 })(); | |
| 963 '''); | |
| 964 } | |
| 965 | |
| 966 return supportsDirectProtoAccess; | |
| 967 } | |
| 968 | |
| 969 jsAst.Expression generateLibraryDescriptor(LibraryElement library, | |
| 970 Fragment fragment) { | |
| 971 var uri = ""; | |
| 972 if (!compiler.enableMinification || backend.mustPreserveUris) { | |
| 973 uri = library.canonicalUri; | |
| 974 if (uri.scheme == 'file' && compiler.outputUri != null) { | |
| 975 uri = relativize(compiler.outputUri, library.canonicalUri, false); | |
| 976 } | |
| 977 } | |
| 978 | |
| 979 String libraryName = | |
| 980 (!compiler.enableMinification || backend.mustRetainLibraryNames) ? | |
| 981 library.getLibraryName() : | |
| 982 ""; | |
| 983 | |
| 984 jsAst.Fun metadata = task.metadataCollector.buildMetadataFunction(library); | |
| 985 | |
| 986 ClassBuilder descriptor = elementDescriptors[fragment][library]; | |
| 987 | |
| 988 jsAst.ObjectInitializer initializer; | |
| 989 if (descriptor == null) { | |
| 990 // Nothing of the library was emitted. | |
| 991 // TODO(floitsch): this should not happen. We currently have an example | |
| 992 // with language/prefix6_negative_test.dart where we have an instance | |
| 993 // method without its corresponding class. | |
| 994 initializer = new jsAst.ObjectInitializer([]); | |
| 995 } else { | |
| 996 initializer = descriptor.toObjectInitializer(); | |
| 997 } | |
| 998 | |
| 999 compiler.dumpInfoTask.registerElementAst(library, metadata); | |
| 1000 compiler.dumpInfoTask.registerElementAst(library, initializer); | |
| 1001 | |
| 1002 List<jsAst.Expression> parts = <jsAst.Expression>[]; | |
| 1003 parts..add(js.string(libraryName)) | |
| 1004 ..add(js.string(uri.toString())) | |
| 1005 ..add(metadata == null ? new jsAst.ArrayHole() : metadata) | |
| 1006 ..add(js('#', namer.globalObjectFor(library))) | |
| 1007 ..add(initializer); | |
| 1008 if (library == compiler.mainApp) { | |
| 1009 parts.add(js.number(1)); | |
| 1010 } | |
| 1011 | |
| 1012 return new jsAst.ArrayInitializer(parts); | |
| 1013 } | |
| 1014 | |
| 1015 void assemblePrecompiledConstructor(OutputUnit outputUnit, | |
| 1016 jsAst.Name constructorName, | |
| 1017 jsAst.Expression constructorAst, | |
| 1018 List<jsAst.Name> fields) { | |
| 1019 cspPrecompiledFunctionFor(outputUnit).add( | |
| 1020 new jsAst.FunctionDeclaration(constructorName, constructorAst)); | |
| 1021 | |
| 1022 String fieldNamesProperty = FIELD_NAMES_PROPERTY_NAME; | |
| 1023 bool hasIsolateSupport = compiler.hasIsolateSupport; | |
| 1024 jsAst.Node fieldNamesArray; | |
| 1025 if (hasIsolateSupport) { | |
| 1026 fieldNamesArray = | |
| 1027 new jsAst.ArrayInitializer(fields.map(js.quoteName).toList()); | |
| 1028 } else { | |
| 1029 fieldNamesArray = new jsAst.LiteralNull(); | |
| 1030 } | |
| 1031 | |
| 1032 cspPrecompiledFunctionFor(outputUnit).add(js.statement(r''' | |
| 1033 { | |
| 1034 #constructorName.#typeNameProperty = #constructorNameString; | |
| 1035 // IE does not have a name property. | |
| 1036 if (!("name" in #constructorName)) | |
| 1037 #constructorName.name = #constructorNameString; | |
| 1038 $desc = $collectedClasses$.#constructorName[1]; | |
| 1039 #constructorName.prototype = $desc; | |
| 1040 ''' /* next string is not a raw string */ ''' | |
| 1041 if (#hasIsolateSupport) { | |
| 1042 #constructorName.$fieldNamesProperty = #fieldNamesArray; | |
| 1043 } | |
| 1044 }''', | |
| 1045 {"constructorName": constructorName, | |
| 1046 "typeNameProperty": typeNameProperty, | |
| 1047 "constructorNameString": js.quoteName(constructorName), | |
| 1048 "hasIsolateSupport": hasIsolateSupport, | |
| 1049 "fieldNamesArray": fieldNamesArray})); | |
| 1050 | |
| 1051 cspPrecompiledConstructorNamesFor(outputUnit).add(js('#', constructorName)); | |
| 1052 } | |
| 1053 | |
| 1054 void assembleTypedefs(Program program) { | |
| 1055 Fragment mainFragment = program.mainFragment; | |
| 1056 OutputUnit mainOutputUnit = mainFragment.outputUnit; | |
| 1057 | |
| 1058 // Emit all required typedef declarations into the main output unit. | |
| 1059 // TODO(karlklose): unify required classes and typedefs to declarations | |
| 1060 // and have builders for each kind. | |
| 1061 for (TypedefElement typedef in typedefsNeededForReflection) { | |
| 1062 LibraryElement library = typedef.library; | |
| 1063 // TODO(karlklose): add a TypedefBuilder and move this code there. | |
| 1064 DartType type = typedef.alias; | |
| 1065 // TODO(zarah): reify type variables once reflection on type arguments of | |
| 1066 // typedefs is supported. | |
| 1067 jsAst.Expression typeIndex = | |
| 1068 task.metadataCollector.reifyType(type, ignoreTypeVariables: true); | |
| 1069 ClassBuilder builder = new ClassBuilder.forStatics(typedef, namer); | |
| 1070 builder.addPropertyByName(embeddedNames.TYPEDEF_TYPE_PROPERTY_NAME, | |
| 1071 typeIndex); | |
| 1072 builder.addPropertyByName(embeddedNames.TYPEDEF_PREDICATE_PROPERTY_NAME, | |
| 1073 js.boolean(true)); | |
| 1074 | |
| 1075 // We can be pretty sure that the objectClass is initialized, since | |
| 1076 // typedefs are only emitted with reflection, which requires lots of | |
| 1077 // classes. | |
| 1078 assert(compiler.objectClass != null); | |
| 1079 builder.superName = namer.className(compiler.objectClass); | |
| 1080 jsAst.Node declaration = builder.toObjectInitializer(); | |
| 1081 jsAst.Name mangledName = namer.globalPropertyName(typedef); | |
| 1082 String reflectionName = getReflectionName(typedef, mangledName); | |
| 1083 getElementDescriptor(library, mainFragment) | |
| 1084 ..addProperty(mangledName, declaration) | |
| 1085 ..addPropertyByName("+$reflectionName", js.string('')); | |
| 1086 // Also emit a trivial constructor for CSP mode. | |
| 1087 jsAst.Name constructorName = mangledName; | |
| 1088 jsAst.Expression constructorAst = js('function() {}'); | |
| 1089 List<jsAst.Name> fieldNames = []; | |
| 1090 assemblePrecompiledConstructor(mainOutputUnit, | |
| 1091 constructorName, | |
| 1092 constructorAst, | |
| 1093 fieldNames); | |
| 1094 } | |
| 1095 } | |
| 1096 | |
| 1097 jsAst.Statement buildGlobalObjectSetup(bool isProgramSplit) { | |
| 1098 List<jsAst.Statement> parts = <jsAst.Statement>[]; | |
| 1099 | |
| 1100 parts.add(js.comment(""" | |
| 1101 // The global objects start as so-called "slow objects". For V8, this | |
| 1102 // means that it won't try to make map transitions as we add properties | |
| 1103 // to these objects. Later on, we attempt to turn these objects into | |
| 1104 // fast objects by calling "convertToFastObject" (see | |
| 1105 // [emitConvertToFastObjectFunction]). | |
| 1106 """)); | |
| 1107 | |
| 1108 for (String globalObject in Namer.reservedGlobalObjectNames) { | |
| 1109 if (isProgramSplit) { | |
| 1110 String template = | |
| 1111 "var #globalObject = #globalsHolder.#globalObject = map();"; | |
| 1112 parts.add(js.statement(template, {"globalObject": globalObject, | |
| 1113 "globalsHolder": globalsHolder})); | |
| 1114 } else { | |
| 1115 parts.add(js.statement("var #globalObject = map();", | |
| 1116 {"globalObject": globalObject})); | |
| 1117 } | |
| 1118 | |
| 1119 } | |
| 1120 | |
| 1121 return new jsAst.Block(parts); | |
| 1122 } | |
| 1123 | |
| 1124 jsAst.Statement buildConvertGlobalObjectToFastObjects() { | |
| 1125 List<jsAst.Statement> parts = <jsAst.Statement>[]; | |
| 1126 | |
| 1127 for (String globalObject in Namer.reservedGlobalObjectNames) { | |
| 1128 parts.add(js.statement( | |
| 1129 '#globalObject = convertToFastObject(#globalObject);', | |
| 1130 {"globalObject": globalObject})); | |
| 1131 } | |
| 1132 | |
| 1133 return new jsAst.Block(parts); | |
| 1134 } | |
| 1135 | |
| 1136 jsAst.Statement buildDebugFastObjectCode() { | |
| 1137 List<jsAst.Statement> parts = <jsAst.Statement>[]; | |
| 1138 | |
| 1139 if (DEBUG_FAST_OBJECTS) { | |
| 1140 parts.add(js.statement(r''' | |
| 1141 // The following only works on V8 when run with option | |
| 1142 // "--allow-natives-syntax". We use'new Function' because the | |
| 1143 // miniparser does not understand V8 native syntax. | |
| 1144 if (typeof print === "function") { | |
| 1145 var HasFastProperties = | |
| 1146 new Function("a", "return %HasFastProperties(a)"); | |
| 1147 print("Size of global helper object: " | |
| 1148 + String(Object.getOwnPropertyNames(H).length) | |
| 1149 + ", fast properties " + HasFastProperties(H)); | |
| 1150 print("Size of global platform object: " | |
| 1151 + String(Object.getOwnPropertyNames(P).length) | |
| 1152 + ", fast properties " + HasFastProperties(P)); | |
| 1153 print("Size of global dart:html object: " | |
| 1154 + String(Object.getOwnPropertyNames(W).length) | |
| 1155 + ", fast properties " + HasFastProperties(W)); | |
| 1156 print("Size of isolate properties object: " | |
| 1157 + String(Object.getOwnPropertyNames($).length) | |
| 1158 + ", fast properties " + HasFastProperties($)); | |
| 1159 print("Size of constant object: " | |
| 1160 + String(Object.getOwnPropertyNames(C).length) | |
| 1161 + ", fast properties " + HasFastProperties(C)); | |
| 1162 var names = Object.getOwnPropertyNames($); | |
| 1163 for (var i = 0; i < names.length; i++) { | |
| 1164 print("$." + names[i]); | |
| 1165 } | |
| 1166 } | |
| 1167 ''')); | |
| 1168 | |
| 1169 for (String object in Namer.userGlobalObjects) { | |
| 1170 parts.add(js.statement(''' | |
| 1171 if (typeof print === "function") { | |
| 1172 print("Size of " + #objectString + ": " | |
| 1173 + String(Object.getOwnPropertyNames(#object).length) | |
| 1174 + ", fast properties " + HasFastProperties(#object)); | |
| 1175 } | |
| 1176 ''', {"object": object, "objectString": js.string(object)})); | |
| 1177 } | |
| 1178 } | |
| 1179 | |
| 1180 return new jsAst.Block(parts); | |
| 1181 } | |
| 1182 | |
| 1183 jsAst.Statement buildMangledNames() { | |
| 1184 List<jsAst.Statement> parts = <jsAst.Statement>[]; | |
| 1185 | |
| 1186 if (!mangledFieldNames.isEmpty) { | |
| 1187 List<jsAst.Name> keys = mangledFieldNames.keys.toList()..sort(); | |
| 1188 var properties = []; | |
| 1189 for (jsAst.Name key in keys) { | |
| 1190 var value = js.string(mangledFieldNames[key]); | |
| 1191 properties.add(new jsAst.Property(key, value)); | |
| 1192 } | |
| 1193 | |
| 1194 jsAst.Expression mangledNamesAccess = | |
| 1195 generateEmbeddedGlobalAccess(embeddedNames.MANGLED_NAMES); | |
| 1196 var map = new jsAst.ObjectInitializer(properties); | |
| 1197 parts.add(js.statement('# = #', [mangledNamesAccess, map])); | |
| 1198 } | |
| 1199 | |
| 1200 if (!mangledGlobalFieldNames.isEmpty) { | |
| 1201 List<jsAst.Name> keys = mangledGlobalFieldNames.keys.toList() | |
| 1202 ..sort(); | |
| 1203 List<jsAst.Property> properties = <jsAst.Property>[]; | |
| 1204 for (jsAst.Name key in keys) { | |
| 1205 jsAst.Literal value = js.string(mangledGlobalFieldNames[key]); | |
| 1206 properties.add(new jsAst.Property(js.quoteName(key), value)); | |
| 1207 } | |
| 1208 jsAst.Expression mangledGlobalNamesAccess = | |
| 1209 generateEmbeddedGlobalAccess(embeddedNames.MANGLED_GLOBAL_NAMES); | |
| 1210 jsAst.ObjectInitializer map = new jsAst.ObjectInitializer(properties); | |
| 1211 parts.add(js.statement('# = #', [mangledGlobalNamesAccess, map])); | |
| 1212 } | |
| 1213 | |
| 1214 return new jsAst.Block(parts); | |
| 1215 } | |
| 1216 | |
| 1217 void checkEverythingEmitted(Iterable<Element> elements) { | |
| 1218 List<Element> pendingStatics; | |
| 1219 if (!compiler.hasIncrementalSupport) { | |
| 1220 pendingStatics = | |
| 1221 Elements.sortedByPosition(elements.where((e) => !e.isLibrary)); | |
| 1222 | |
| 1223 pendingStatics.forEach((element) => | |
| 1224 compiler.reportInfo( | |
| 1225 element, MessageKind.GENERIC, {'text': 'Pending statics.'})); | |
| 1226 } | |
| 1227 | |
| 1228 if (pendingStatics != null && !pendingStatics.isEmpty) { | |
| 1229 compiler.internalError(pendingStatics.first, | |
| 1230 'Pending statics (see above).'); | |
| 1231 } | |
| 1232 } | |
| 1233 | |
| 1234 void assembleLibrary(Library library, Fragment fragment) { | |
| 1235 LibraryElement libraryElement = library.element; | |
| 1236 | |
| 1237 assembleStaticFunctions(library.statics, fragment); | |
| 1238 | |
| 1239 ClassBuilder libraryBuilder = | |
| 1240 getElementDescriptor(libraryElement, fragment); | |
| 1241 for (Class cls in library.classes) { | |
| 1242 assembleClass(cls, libraryBuilder, fragment); | |
| 1243 } | |
| 1244 | |
| 1245 classEmitter.emitFields(library, libraryBuilder, emitStatics: true); | |
| 1246 } | |
| 1247 | |
| 1248 void assembleProgram(Program program) { | |
| 1249 for (Fragment fragment in program.fragments) { | |
| 1250 for (Library library in fragment.libraries) { | |
| 1251 assembleLibrary(library, fragment); | |
| 1252 } | |
| 1253 } | |
| 1254 assembleTypedefs(program); | |
| 1255 } | |
| 1256 | |
| 1257 jsAst.Program buildOutputAstForMain(Program program, | |
| 1258 Map<OutputUnit, _DeferredOutputUnitHash> deferredLoadHashes) { | |
| 1259 MainFragment mainFragment = program.mainFragment; | |
| 1260 OutputUnit mainOutputUnit = mainFragment.outputUnit; | |
| 1261 bool isProgramSplit = program.isSplit; | |
| 1262 | |
| 1263 List<jsAst.Statement> statements = <jsAst.Statement>[]; | |
| 1264 | |
| 1265 statements..add(buildGeneratedBy()) | |
| 1266 ..add(js.comment(HOOKS_API_USAGE)); | |
| 1267 | |
| 1268 if (isProgramSplit) { | |
| 1269 /// For deferred loading we communicate the initializers via this global | |
| 1270 /// variable. The deferred hunks will add their initialization to this. | |
| 1271 /// The semicolon is important in minified mode, without it the | |
| 1272 /// following parenthesis looks like a call to the object literal. | |
| 1273 statements.add( | |
| 1274 js.statement('self.#deferredInitializers = ' | |
| 1275 'self.#deferredInitializers || Object.create(null);', | |
| 1276 {'deferredInitializers': deferredInitializers})); | |
| 1277 } | |
| 1278 | |
| 1279 // Collect the AST for the decriptors | |
| 1280 Map<Element, ClassBuilder> descriptors = elementDescriptors[mainFragment]; | |
| 1281 if (descriptors == null) descriptors = const {}; | |
| 1282 | |
| 1283 checkEverythingEmitted(descriptors.keys); | |
| 1284 | |
| 1285 Iterable<LibraryElement> libraries = | |
| 1286 task.outputLibraryLists[mainOutputUnit]; | |
| 1287 if (libraries == null) libraries = <LibraryElement>[]; | |
| 1288 | |
| 1289 List<jsAst.Expression> parts = <jsAst.Expression>[]; | |
| 1290 for (LibraryElement library in Elements.sortedByPosition(libraries)) { | |
| 1291 parts.add(generateLibraryDescriptor(library, mainFragment)); | |
| 1292 descriptors.remove(library); | |
| 1293 } | |
| 1294 | |
| 1295 if (descriptors.isNotEmpty) { | |
| 1296 List<Element> remainingLibraries = descriptors.keys | |
| 1297 .where((Element e) => e is LibraryElement) | |
| 1298 .toList(); | |
| 1299 | |
| 1300 // The remaining descriptors are only accessible through reflection. | |
| 1301 // The program builder does not collect libraries that only | |
| 1302 // contain typedefs that are used for reflection. | |
| 1303 for (LibraryElement element in remainingLibraries) { | |
| 1304 assert(element is LibraryElement || compiler.hasIncrementalSupport); | |
| 1305 if (element is LibraryElement) { | |
| 1306 parts.add(generateLibraryDescriptor(element, mainFragment)); | |
| 1307 descriptors.remove(element); | |
| 1308 } | |
| 1309 } | |
| 1310 } | |
| 1311 jsAst.ArrayInitializer descriptorsAst = new jsAst.ArrayInitializer(parts); | |
| 1312 | |
| 1313 // Using a named function here produces easier to read stack traces in | |
| 1314 // Chrome/V8. | |
| 1315 statements.add(js.statement(""" | |
| 1316 (function() { | |
| 1317 // No renaming in the top-level function to save the locals for the | |
| 1318 // nested context where they will be used more. We have to put the | |
| 1319 // comment into a hole as the parser strips out comments right away. | |
| 1320 #disableVariableRenaming; | |
| 1321 #supportsDirectProtoAccess; | |
| 1322 | |
| 1323 if (#hasIncrementalSupport) { | |
| 1324 #helper = #helper || Object.create(null); | |
| 1325 #helper.patch = function(a) { eval(a)}; | |
| 1326 #helper.schemaChange = #schemaChange; | |
| 1327 #helper.addMethod = #addMethod; | |
| 1328 #helper.extractStubs = | |
| 1329 function(array, name, isStatic, originalDescriptor) { | |
| 1330 var descriptor = Object.create(null); | |
| 1331 this.addStubs(descriptor, array, name, isStatic, []); | |
| 1332 return descriptor; | |
| 1333 }; | |
| 1334 } | |
| 1335 | |
| 1336 if (#isProgramSplit) { | |
| 1337 /// We collect all the global state, so it can be passed to the | |
| 1338 /// initializer of deferred files. | |
| 1339 var #globalsHolder = Object.create(null) | |
| 1340 } | |
| 1341 | |
| 1342 // [map] returns an object that V8 shouldn't try to optimize with a | |
| 1343 // hidden class. This prevents a potential performance problem where V8 | |
| 1344 // tries to build a hidden class for an object used as a hashMap. | |
| 1345 // It requires fewer characters to declare a variable as a parameter than | |
| 1346 // with `var`. | |
| 1347 function map(x) { | |
| 1348 x = Object.create(null); | |
| 1349 x.x = 0; | |
| 1350 delete x.x; | |
| 1351 return x; | |
| 1352 } | |
| 1353 | |
| 1354 #globalObjectSetup; | |
| 1355 | |
| 1356 function #isolateName() {} | |
| 1357 | |
| 1358 if (#isProgramSplit) { | |
| 1359 #globalsHolder.#isolateName = #isolateName; | |
| 1360 #globalsHolder.#initName = #initName; | |
| 1361 #globalsHolder.#setupProgramName = #setupProgramName; | |
| 1362 } | |
| 1363 | |
| 1364 init(); | |
| 1365 | |
| 1366 #mangledNames; | |
| 1367 | |
| 1368 #cspPrecompiledFunctions; | |
| 1369 | |
| 1370 #setupProgram; | |
| 1371 | |
| 1372 #functionThatReturnsNull; | |
| 1373 | |
| 1374 // The argument to reflectionDataParser is assigned to a temporary 'dart' | |
| 1375 // so that 'dart.' will appear as the prefix to dart methods in stack | |
| 1376 // traces and profile entries. | |
| 1377 var dart = #descriptors; | |
| 1378 | |
| 1379 #setupProgramName(dart, 0); | |
| 1380 | |
| 1381 #getInterceptorMethods; | |
| 1382 #oneShotInterceptors; | |
| 1383 | |
| 1384 #makeConstantList; | |
| 1385 | |
| 1386 // We abuse the short name used for the isolate here to store | |
| 1387 // the isolate properties. This is safe as long as the real isolate | |
| 1388 // object does not exist yet. | |
| 1389 var ${namer.currentIsolate} = #isolatePropertiesName; | |
| 1390 | |
| 1391 // Constants in checked mode call into RTI code to set type information | |
| 1392 // which may need getInterceptor (and one-shot interceptor) methods, so | |
| 1393 // we have to make sure that [emitGetInterceptorMethods] and | |
| 1394 // [emitOneShotInterceptors] have been called. | |
| 1395 #compileTimeConstants; | |
| 1396 | |
| 1397 // Static field initializations require the classes and compile-time | |
| 1398 // constants to be set up. | |
| 1399 #staticNonFinalInitializers; | |
| 1400 | |
| 1401 ${namer.currentIsolate} = null; | |
| 1402 | |
| 1403 #deferredBoilerPlate; | |
| 1404 | |
| 1405 #typeToInterceptorMap; | |
| 1406 | |
| 1407 #lazyStaticFields; | |
| 1408 | |
| 1409 #isolateName = $finishIsolateConstructorName(#isolateName); | |
| 1410 | |
| 1411 ${namer.currentIsolate} = new #isolateName(); | |
| 1412 | |
| 1413 #metadata; | |
| 1414 | |
| 1415 #convertToFastObject; | |
| 1416 #convertToSlowObject; | |
| 1417 | |
| 1418 #convertGlobalObjectsToFastObjects; | |
| 1419 #debugFastObjects; | |
| 1420 | |
| 1421 #init; | |
| 1422 | |
| 1423 #main; | |
| 1424 })(); | |
| 1425 """, { | |
| 1426 "disableVariableRenaming": js.comment("/* ::norenaming:: */"), | |
| 1427 "hasIncrementalSupport": compiler.hasIncrementalSupport, | |
| 1428 "helper": js('this.#', [namer.incrementalHelperName]), | |
| 1429 "schemaChange": buildSchemaChangeFunction(), | |
| 1430 "addMethod": buildIncrementalAddMethod(), | |
| 1431 "isProgramSplit": isProgramSplit, | |
| 1432 "supportsDirectProtoAccess": buildSupportsDirectProtoAccess(), | |
| 1433 "globalsHolder": globalsHolder, | |
| 1434 "globalObjectSetup": buildGlobalObjectSetup(isProgramSplit), | |
| 1435 "isolateName": namer.isolateName, | |
| 1436 "isolatePropertiesName": js(isolatePropertiesName), | |
| 1437 "initName": initName, | |
| 1438 "functionThatReturnsNull": buildFunctionThatReturnsNull(), | |
| 1439 "mangledNames": buildMangledNames(), | |
| 1440 "setupProgram": buildSetupProgram(program, compiler, backend, namer, this)
, | |
| 1441 "setupProgramName": setupProgramName, | |
| 1442 "descriptors": descriptorsAst, | |
| 1443 "cspPrecompiledFunctions": buildCspPrecompiledFunctionFor(mainOutputUnit), | |
| 1444 "getInterceptorMethods": interceptorEmitter.buildGetInterceptorMethods(), | |
| 1445 "oneShotInterceptors": interceptorEmitter.buildOneShotInterceptors(), | |
| 1446 "makeConstantList": buildMakeConstantList(), | |
| 1447 "compileTimeConstants": buildCompileTimeConstants(mainFragment.constants, | |
| 1448 isMainFragment: true), | |
| 1449 "deferredBoilerPlate": buildDeferredBoilerPlate(deferredLoadHashes), | |
| 1450 "staticNonFinalInitializers": buildStaticNonFinalFieldInitializations( | |
| 1451 mainOutputUnit), | |
| 1452 "typeToInterceptorMap": | |
| 1453 interceptorEmitter.buildTypeToInterceptorMap(program), | |
| 1454 "lazyStaticFields": buildLazilyInitializedStaticFields(), | |
| 1455 "metadata": buildMetadata(program, mainOutputUnit), | |
| 1456 "convertToFastObject": buildConvertToFastObjectFunction(), | |
| 1457 "convertToSlowObject": buildConvertToSlowObjectFunction(), | |
| 1458 "convertGlobalObjectsToFastObjects": | |
| 1459 buildConvertGlobalObjectToFastObjects(), | |
| 1460 "debugFastObjects": buildDebugFastObjectCode(), | |
| 1461 "init": buildInitFunction(), | |
| 1462 "main": buildMain(mainFragment.invokeMain) | |
| 1463 })); | |
| 1464 | |
| 1465 return new jsAst.Program(statements); | |
| 1466 } | |
| 1467 | |
| 1468 void emitMainOutputUnit(OutputUnit mainOutputUnit, jsAst.Program program) { | |
| 1469 LineColumnCollector lineColumnCollector; | |
| 1470 List<CodeOutputListener> codeOutputListeners; | |
| 1471 if (generateSourceMap) { | |
| 1472 lineColumnCollector = new LineColumnCollector(); | |
| 1473 codeOutputListeners = <CodeOutputListener>[lineColumnCollector]; | |
| 1474 } | |
| 1475 | |
| 1476 CodeOutput mainOutput = | |
| 1477 new StreamCodeOutput(compiler.outputProvider('', 'js'), | |
| 1478 codeOutputListeners); | |
| 1479 outputBuffers[mainOutputUnit] = mainOutput; | |
| 1480 | |
| 1481 | |
| 1482 mainOutput.addBuffer(jsAst.prettyPrint(program, | |
| 1483 compiler, | |
| 1484 monitor: compiler.dumpInfoTask)); | |
| 1485 | |
| 1486 if (compiler.deferredMapUri != null) { | |
| 1487 outputDeferredMap(); | |
| 1488 } | |
| 1489 | |
| 1490 if (generateSourceMap) { | |
| 1491 mainOutput.add( | |
| 1492 generateSourceMapTag(compiler.sourceMapUri, compiler.outputUri)); | |
| 1493 } | |
| 1494 | |
| 1495 mainOutput.close(); | |
| 1496 | |
| 1497 if (generateSourceMap) { | |
| 1498 outputSourceMap(mainOutput, lineColumnCollector, '', | |
| 1499 compiler.sourceMapUri, compiler.outputUri); | |
| 1500 } | |
| 1501 } | |
| 1502 | |
| 1503 /// Used by incremental compilation to patch up the prototype of | |
| 1504 /// [oldConstructor] for use as prototype of [newConstructor]. | |
| 1505 jsAst.Fun buildSchemaChangeFunction() { | |
| 1506 if (!compiler.hasIncrementalSupport) return null; | |
| 1507 return js(''' | |
| 1508 function(newConstructor, oldConstructor, superclass) { | |
| 1509 // Invariant: newConstructor.prototype has no interesting properties besides | |
| 1510 // generated accessors. These are copied to oldPrototype which will be | |
| 1511 // updated by other incremental changes. | |
| 1512 if (superclass != null) { | |
| 1513 this.inheritFrom(newConstructor, superclass); | |
| 1514 } | |
| 1515 var oldPrototype = oldConstructor.prototype; | |
| 1516 var newPrototype = newConstructor.prototype; | |
| 1517 var hasOwnProperty = Object.prototype.hasOwnProperty; | |
| 1518 for (var property in newPrototype) { | |
| 1519 if (hasOwnProperty.call(newPrototype, property)) { | |
| 1520 // Copy generated accessors. | |
| 1521 oldPrototype[property] = newPrototype[property]; | |
| 1522 } | |
| 1523 } | |
| 1524 oldPrototype.__proto__ = newConstructor.prototype.__proto__; | |
| 1525 oldPrototype.constructor = newConstructor; | |
| 1526 newConstructor.prototype = oldPrototype; | |
| 1527 return newConstructor; | |
| 1528 }'''); | |
| 1529 } | |
| 1530 | |
| 1531 /// Used by incremental compilation to patch up an object ([holder]) with a | |
| 1532 /// new (or updated) method. [arrayOrFunction] is either the new method, or | |
| 1533 /// an array containing the method (see | |
| 1534 /// [ContainerBuilder.addMemberMethodFromInfo]). [name] is the name of the | |
| 1535 /// new method. [isStatic] tells if method is static (or | |
| 1536 /// top-level). [globalFunctionsAccess] is a reference to | |
| 1537 /// [embeddedNames.GLOBAL_FUNCTIONS]. | |
| 1538 jsAst.Fun buildIncrementalAddMethod() { | |
| 1539 if (!compiler.hasIncrementalSupport) return null; | |
| 1540 return js(r""" | |
| 1541 function(originalDescriptor, name, holder, isStatic, globalFunctionsAccess) { | |
| 1542 var arrayOrFunction = originalDescriptor[name]; | |
| 1543 var method; | |
| 1544 if (arrayOrFunction.constructor === Array) { | |
| 1545 var existing = holder[name]; | |
| 1546 var array = arrayOrFunction; | |
| 1547 | |
| 1548 // Each method may have a number of stubs associated. For example, if an | |
| 1549 // instance method supports multiple arguments, a stub for each matching | |
| 1550 // selector. There is also a getter stub for tear-off getters. For example, | |
| 1551 // an instance method foo([a]) may have the following stubs: foo$0, foo$1, | |
| 1552 // and get$foo (here exemplified using unminified names). | |
| 1553 // [extractStubs] returns a JavaScript object whose own properties | |
| 1554 // corresponds to the stubs. | |
| 1555 var descriptor = | |
| 1556 this.extractStubs(array, name, isStatic, originalDescriptor); | |
| 1557 method = descriptor[name]; | |
| 1558 | |
| 1559 // Iterate through the properties of descriptor and copy the stubs to the | |
| 1560 // existing holder (for instance methods, a prototype). | |
| 1561 for (var property in descriptor) { | |
| 1562 if (!Object.prototype.hasOwnProperty.call(descriptor, property)) continue; | |
| 1563 var stub = descriptor[property]; | |
| 1564 var existingStub = holder[property]; | |
| 1565 if (stub === method || !existingStub || !stub.$getterStub) { | |
| 1566 // Not replacing an existing getter stub. | |
| 1567 holder[property] = stub; | |
| 1568 continue; | |
| 1569 } | |
| 1570 if (!stub.$getterStub) { | |
| 1571 var error = new Error('Unexpected stub.'); | |
| 1572 error.stub = stub; | |
| 1573 throw error; | |
| 1574 } | |
| 1575 | |
| 1576 // Existing getter stubs need special treatment as they may already have | |
| 1577 // been called and produced a closure. | |
| 1578 this.pendingStubs = this.pendingStubs || []; | |
| 1579 // It isn't safe to invoke the stub yet. | |
| 1580 this.pendingStubs.push((function(holder, stub, existingStub, existing, | |
| 1581 method) { | |
| 1582 return function() { | |
| 1583 var receiver = isStatic ? holder : new holder.constructor(); | |
| 1584 // Invoke the existing stub to obtain the tear-off closure. | |
| 1585 existingStub = existingStub.call(receiver); | |
| 1586 // Invoke the new stub to create a tear-off closure we can use as a | |
| 1587 // prototype. | |
| 1588 stub = stub.call(receiver); | |
| 1589 | |
| 1590 // Copy the properties from the new tear-off's prototype to the | |
| 1591 // prototype of the existing tear-off. | |
| 1592 var newProto = stub.constructor.prototype; | |
| 1593 var existingProto = existingStub.constructor.prototype; | |
| 1594 for (var stubProperty in newProto) { | |
| 1595 if (!Object.prototype.hasOwnProperty.call(newProto, stubProperty)) | |
| 1596 continue; | |
| 1597 existingProto[stubProperty] = newProto[stubProperty]; | |
| 1598 } | |
| 1599 | |
| 1600 // Update all the existing stub's references to [existing] to | |
| 1601 // [method]. Instance tear-offs are call-by-name, so this isn't | |
| 1602 // necessary for those. | |
| 1603 if (!isStatic) return; | |
| 1604 for (var reference in existingStub) { | |
| 1605 if (existingStub[reference] === existing) { | |
| 1606 existingStub[reference] = method; | |
| 1607 } | |
| 1608 } | |
| 1609 } | |
| 1610 })(holder, stub, existingStub, existing, method)); | |
| 1611 } | |
| 1612 } else { | |
| 1613 method = arrayOrFunction; | |
| 1614 holder[name] = method; | |
| 1615 } | |
| 1616 if (isStatic) globalFunctionsAccess[name] = method; | |
| 1617 }"""); | |
| 1618 } | |
| 1619 | |
| 1620 Map<OutputUnit, jsAst.Expression> buildDescriptorsForOutputUnits( | |
| 1621 Program program) { | |
| 1622 Map<OutputUnit, jsAst.Expression> outputs = | |
| 1623 new Map<OutputUnit, jsAst.Expression>(); | |
| 1624 | |
| 1625 for (Fragment fragment in program.deferredFragments) { | |
| 1626 OutputUnit outputUnit = fragment.outputUnit; | |
| 1627 | |
| 1628 Map<Element, ClassBuilder> descriptors = elementDescriptors[fragment]; | |
| 1629 | |
| 1630 if (descriptors != null && descriptors.isNotEmpty) { | |
| 1631 Iterable<LibraryElement> libraries = | |
| 1632 task.outputLibraryLists[outputUnit]; | |
| 1633 if (libraries == null) libraries = []; | |
| 1634 | |
| 1635 // TODO(johnniwinther): Avoid creating [CodeBuffer]s. | |
| 1636 List<jsAst.Expression> parts = <jsAst.Expression>[]; | |
| 1637 for (LibraryElement library in Elements.sortedByPosition(libraries)) { | |
| 1638 parts.add(generateLibraryDescriptor(library, fragment)); | |
| 1639 descriptors.remove(library); | |
| 1640 } | |
| 1641 | |
| 1642 outputs[outputUnit] = new jsAst.ArrayInitializer(parts); | |
| 1643 } | |
| 1644 } | |
| 1645 | |
| 1646 return outputs; | |
| 1647 } | |
| 1648 | |
| 1649 void finalizeTokensInAst(jsAst.Program main, | |
| 1650 Iterable<jsAst.Program> deferredParts) { | |
| 1651 jsAst.TokenCounter counter = new jsAst.TokenCounter(); | |
| 1652 counter.countTokens(main); | |
| 1653 deferredParts.forEach(counter.countTokens); | |
| 1654 task.metadataCollector.finalizeTokens(); | |
| 1655 if (backend.namer is jsAst.TokenFinalizer) { | |
| 1656 var finalizer = backend.namer; | |
| 1657 finalizer.finalizeTokens(); | |
| 1658 } | |
| 1659 } | |
| 1660 | |
| 1661 int emitProgram(ProgramBuilder programBuilder) { | |
| 1662 Program program = programBuilder.buildProgram( | |
| 1663 storeFunctionTypesInMetadata: true); | |
| 1664 | |
| 1665 assembleProgram(program); | |
| 1666 | |
| 1667 // Construct the ASTs for all deferred output units. | |
| 1668 Map<OutputUnit, jsAst.Program> deferredParts = | |
| 1669 buildOutputAstForDeferredCode(program); | |
| 1670 | |
| 1671 Map<OutputUnit, _DeferredOutputUnitHash> deferredHashTokens = | |
| 1672 new Map<OutputUnit, _DeferredOutputUnitHash>.fromIterables( | |
| 1673 deferredParts.keys, | |
| 1674 deferredParts.keys.map((OutputUnit unit) { | |
| 1675 return new _DeferredOutputUnitHash(unit); | |
| 1676 }) | |
| 1677 ); | |
| 1678 | |
| 1679 jsAst.Program mainOutput = | |
| 1680 buildOutputAstForMain(program, deferredHashTokens); | |
| 1681 | |
| 1682 finalizeTokensInAst(mainOutput, deferredParts.values); | |
| 1683 | |
| 1684 // Emit deferred units first, so we have their hashes. | |
| 1685 // Map from OutputUnit to a hash of its content. The hash uniquely | |
| 1686 // identifies the code of the output-unit. It does not include | |
| 1687 // boilerplate JS code, like the sourcemap directives or the hash | |
| 1688 // itself. | |
| 1689 Map<OutputUnit, String> deferredLoadHashes = | |
| 1690 emitDeferredOutputUnits(deferredParts); | |
| 1691 | |
| 1692 deferredHashTokens.forEach((OutputUnit key, _DeferredOutputUnitHash token) { | |
| 1693 token.setHash(deferredLoadHashes[key]); | |
| 1694 }); | |
| 1695 emitMainOutputUnit(program.mainFragment.outputUnit, mainOutput); | |
| 1696 | |
| 1697 if (backend.requiresPreamble && | |
| 1698 !backend.htmlLibraryIsLoaded) { | |
| 1699 compiler.reportHint(NO_LOCATION_SPANNABLE, MessageKind.PREAMBLE); | |
| 1700 } | |
| 1701 // Return the total program size. | |
| 1702 return outputBuffers.values.fold(0, (a, b) => a + b.length); | |
| 1703 } | |
| 1704 | |
| 1705 String generateSourceMapTag(Uri sourceMapUri, Uri fileUri) { | |
| 1706 if (sourceMapUri != null && fileUri != null) { | |
| 1707 String sourceMapFileName = relativize(fileUri, sourceMapUri, false); | |
| 1708 return ''' | |
| 1709 | |
| 1710 //# sourceMappingURL=$sourceMapFileName | |
| 1711 '''; | |
| 1712 } | |
| 1713 return ''; | |
| 1714 } | |
| 1715 | |
| 1716 ClassBuilder getElementDescriptor(Element element, Fragment fragment) { | |
| 1717 Element owner = element.library; | |
| 1718 bool isClass = false; | |
| 1719 if (!element.isLibrary && !element.isTopLevel && !element.isNative) { | |
| 1720 // For static (not top level) elements, record their code in a buffer | |
| 1721 // specific to the class. For now, not supported for native classes and | |
| 1722 // native elements. | |
| 1723 ClassElement cls = | |
| 1724 element.enclosingClassOrCompilationUnit.declaration; | |
| 1725 if (compiler.codegenWorld.directlyInstantiatedClasses.contains(cls) && | |
| 1726 !cls.isNative && | |
| 1727 compiler.deferredLoadTask.outputUnitForElement(element) == | |
| 1728 compiler.deferredLoadTask.outputUnitForElement(cls)) { | |
| 1729 owner = cls; | |
| 1730 } | |
| 1731 } | |
| 1732 if (owner == null) { | |
| 1733 compiler.internalError(element, 'Owner is null.'); | |
| 1734 } | |
| 1735 return elementDescriptors | |
| 1736 .putIfAbsent(fragment, () => new Map<Element, ClassBuilder>()) | |
| 1737 .putIfAbsent(owner, () { | |
| 1738 return new ClassBuilder(owner, namer, owner.isClass); | |
| 1739 }); | |
| 1740 } | |
| 1741 | |
| 1742 /// Emits support-code for deferred loading into [output]. | |
| 1743 jsAst.Statement buildDeferredBoilerPlate( | |
| 1744 Map<OutputUnit, _DeferredOutputUnitHash> deferredLoadHashes) { | |
| 1745 List<jsAst.Statement> parts = <jsAst.Statement>[]; | |
| 1746 | |
| 1747 parts.add(js.statement(''' | |
| 1748 { | |
| 1749 // Function for checking if a hunk is loaded given its hash. | |
| 1750 #isHunkLoaded = function(hunkHash) { | |
| 1751 return !!$deferredInitializers[hunkHash]; | |
| 1752 }; | |
| 1753 #deferredInitialized = new Object(null); | |
| 1754 // Function for checking if a hunk is initialized given its hash. | |
| 1755 #isHunkInitialized = function(hunkHash) { | |
| 1756 return #deferredInitialized[hunkHash]; | |
| 1757 }; | |
| 1758 // Function for initializing a loaded hunk, given its hash. | |
| 1759 #initializeLoadedHunk = function(hunkHash) { | |
| 1760 $deferredInitializers[hunkHash]( | |
| 1761 #globalsHolder, ${namer.currentIsolate}); | |
| 1762 #deferredInitialized[hunkHash] = true; | |
| 1763 }; | |
| 1764 } | |
| 1765 ''', {"globalsHolder": globalsHolder, | |
| 1766 "isHunkLoaded": generateEmbeddedGlobalAccess( | |
| 1767 embeddedNames.IS_HUNK_LOADED), | |
| 1768 "isHunkInitialized": generateEmbeddedGlobalAccess( | |
| 1769 embeddedNames.IS_HUNK_INITIALIZED), | |
| 1770 "initializeLoadedHunk": generateEmbeddedGlobalAccess( | |
| 1771 embeddedNames.INITIALIZE_LOADED_HUNK), | |
| 1772 "deferredInitialized": generateEmbeddedGlobalAccess( | |
| 1773 embeddedNames.DEFERRED_INITIALIZED)})); | |
| 1774 | |
| 1775 // Write a javascript mapping from Deferred import load ids (derrived | |
| 1776 // from the import prefix.) to a list of lists of uris of hunks to load, | |
| 1777 // and a corresponding mapping to a list of hashes used by | |
| 1778 // INITIALIZE_LOADED_HUNK and IS_HUNK_LOADED. | |
| 1779 Map<String, List<jsAst.LiteralString>> deferredLibraryUris = | |
| 1780 new Map<String, List<jsAst.LiteralString>>(); | |
| 1781 Map<String, List<_DeferredOutputUnitHash>> deferredLibraryHashes = | |
| 1782 new Map<String, List<_DeferredOutputUnitHash>>(); | |
| 1783 compiler.deferredLoadTask.hunksToLoad.forEach( | |
| 1784 (String loadId, List<OutputUnit>outputUnits) { | |
| 1785 List<jsAst.LiteralString> uris = new List<jsAst.LiteralString>(); | |
| 1786 List<_DeferredOutputUnitHash> hashes = | |
| 1787 new List<_DeferredOutputUnitHash>(); | |
| 1788 deferredLibraryHashes[loadId] = new List<_DeferredOutputUnitHash>(); | |
| 1789 for (OutputUnit outputUnit in outputUnits) { | |
| 1790 uris.add(js.escapedString( | |
| 1791 backend.deferredPartFileName(outputUnit.name))); | |
| 1792 hashes.add(deferredLoadHashes[outputUnit]); | |
| 1793 } | |
| 1794 | |
| 1795 deferredLibraryUris[loadId] = uris; | |
| 1796 deferredLibraryHashes[loadId] = hashes; | |
| 1797 }); | |
| 1798 | |
| 1799 void emitMapping(String name, Map<String, List<jsAst.Expression>> mapping) { | |
| 1800 List<jsAst.Property> properties = new List<jsAst.Property>(); | |
| 1801 mapping.forEach((String key, List<jsAst.Expression> values) { | |
| 1802 properties.add(new jsAst.Property(js.escapedString(key), | |
| 1803 new jsAst.ArrayInitializer(values))); | |
| 1804 }); | |
| 1805 jsAst.Node initializer = | |
| 1806 new jsAst.ObjectInitializer(properties, isOneLiner: true); | |
| 1807 | |
| 1808 jsAst.Node globalName = generateEmbeddedGlobalAccess(name); | |
| 1809 parts.add(js.statement("# = #", [globalName, initializer])); | |
| 1810 } | |
| 1811 | |
| 1812 emitMapping(embeddedNames.DEFERRED_LIBRARY_URIS, deferredLibraryUris); | |
| 1813 emitMapping(embeddedNames.DEFERRED_LIBRARY_HASHES, | |
| 1814 deferredLibraryHashes); | |
| 1815 | |
| 1816 return new jsAst.Block(parts); | |
| 1817 } | |
| 1818 | |
| 1819 Map <OutputUnit, jsAst.Program> buildOutputAstForDeferredCode( | |
| 1820 Program program) { | |
| 1821 if (!program.isSplit) return const <OutputUnit, jsAst.Program>{}; | |
| 1822 | |
| 1823 Map<OutputUnit, jsAst.Program> result = | |
| 1824 new Map<OutputUnit, jsAst.Program>(); | |
| 1825 | |
| 1826 Map<OutputUnit, jsAst.Expression> deferredAsts = | |
| 1827 buildDescriptorsForOutputUnits(program); | |
| 1828 | |
| 1829 for (Fragment fragment in program.deferredFragments) { | |
| 1830 OutputUnit outputUnit = fragment.outputUnit; | |
| 1831 jsAst.Expression libraryDescriptor = deferredAsts[outputUnit]; | |
| 1832 List<jsAst.Statement> body = <jsAst.Statement>[]; | |
| 1833 | |
| 1834 // No renaming in the top-level function to save the locals for the | |
| 1835 // nested context where they will be used more. | |
| 1836 body.add(js.comment("/* ::norenaming:: ")); | |
| 1837 | |
| 1838 for (String globalObject in Namer.reservedGlobalObjectNames) { | |
| 1839 body.add(js.statement('var #object = #globalsHolder.#object;', | |
| 1840 {'globalsHolder': globalsHolder, | |
| 1841 'object': globalObject})); | |
| 1842 } | |
| 1843 body..add(js.statement('var init = #globalsHolder.init;', | |
| 1844 {'globalsHolder': globalsHolder})) | |
| 1845 ..add(js.statement('var $setupProgramName = ' | |
| 1846 '#globalsHolder.$setupProgramName;', | |
| 1847 {'globalsHolder': globalsHolder})) | |
| 1848 ..add(js.statement('var ${namer.isolateName} = ' | |
| 1849 '#globalsHolder.${namer.isolateName};', | |
| 1850 {'globalsHolder': globalsHolder})); | |
| 1851 String typesAccess = | |
| 1852 generateEmbeddedGlobalAccessString(embeddedNames.TYPES); | |
| 1853 if (libraryDescriptor != null) { | |
| 1854 // The argument to reflectionDataParser is assigned to a temporary | |
| 1855 // 'dart' so that 'dart.' will appear as the prefix to dart methods | |
| 1856 // in stack traces and profile entries. | |
| 1857 body.add(js.statement('var dart = #', libraryDescriptor)); | |
| 1858 | |
| 1859 if (compiler.useContentSecurityPolicy) { | |
| 1860 body.add(buildCspPrecompiledFunctionFor(outputUnit)); | |
| 1861 } | |
| 1862 body.add( | |
| 1863 js.statement('$setupProgramName(dart, ${typesAccess}.length);')); | |
| 1864 } | |
| 1865 | |
| 1866 body..add(buildMetadata(program, outputUnit)) | |
| 1867 ..add(js.statement('${typesAccess}.push.apply(${typesAccess}, ' | |
| 1868 '${namer.deferredTypesName});')); | |
| 1869 | |
| 1870 // Set the currentIsolate variable to the current isolate (which is | |
| 1871 // provided as second argument). | |
| 1872 body.add(js.statement("${namer.currentIsolate} = arguments[1];")); | |
| 1873 | |
| 1874 body.add(buildCompileTimeConstants(fragment.constants, | |
| 1875 isMainFragment: false)); | |
| 1876 body.add(buildStaticNonFinalFieldInitializations(outputUnit)); | |
| 1877 | |
| 1878 List<jsAst.Statement> statements = <jsAst.Statement>[]; | |
| 1879 | |
| 1880 statements | |
| 1881 ..add(buildGeneratedBy()) | |
| 1882 ..add(js.statement('${deferredInitializers}.current = ' | |
| 1883 """function (#) { | |
| 1884 # | |
| 1885 } | |
| 1886 """, [globalsHolder, body])); | |
| 1887 | |
| 1888 result[outputUnit] = new jsAst.Program(statements); | |
| 1889 } | |
| 1890 | |
| 1891 return result; | |
| 1892 } | |
| 1893 | |
| 1894 /// Returns a map from OutputUnit to a hash of its content. The hash uniquely | |
| 1895 /// identifies the code of the output-unit. It does not include | |
| 1896 /// boilerplate JS code, like the sourcemap directives or the hash | |
| 1897 /// itself. | |
| 1898 Map<OutputUnit, String> emitDeferredOutputUnits( | |
| 1899 Map<OutputUnit, jsAst.Program> outputAsts) { | |
| 1900 | |
| 1901 Map<OutputUnit, String> hunkHashes = new Map<OutputUnit, String>(); | |
| 1902 | |
| 1903 for (OutputUnit outputUnit in outputAsts.keys) { | |
| 1904 List<CodeOutputListener> outputListeners = <CodeOutputListener>[]; | |
| 1905 Hasher hasher = new Hasher(); | |
| 1906 outputListeners.add(hasher); | |
| 1907 | |
| 1908 LineColumnCollector lineColumnCollector; | |
| 1909 if (generateSourceMap) { | |
| 1910 lineColumnCollector = new LineColumnCollector(); | |
| 1911 outputListeners.add(lineColumnCollector); | |
| 1912 } | |
| 1913 | |
| 1914 String partPrefix = | |
| 1915 backend.deferredPartFileName(outputUnit.name, addExtension: false); | |
| 1916 CodeOutput output = new StreamCodeOutput( | |
| 1917 compiler.outputProvider(partPrefix, 'part.js'), | |
| 1918 outputListeners); | |
| 1919 | |
| 1920 outputBuffers[outputUnit] = output; | |
| 1921 | |
| 1922 output.addBuffer(jsAst.prettyPrint(outputAsts[outputUnit], | |
| 1923 compiler, | |
| 1924 monitor: compiler.dumpInfoTask)); | |
| 1925 | |
| 1926 // Make a unique hash of the code (before the sourcemaps are added) | |
| 1927 // This will be used to retrieve the initializing function from the global | |
| 1928 // variable. | |
| 1929 String hash = hasher.getHash(); | |
| 1930 | |
| 1931 output.add('$N${deferredInitializers}["$hash"]$_=$_' | |
| 1932 '${deferredInitializers}.current$N'); | |
| 1933 | |
| 1934 if (generateSourceMap) { | |
| 1935 Uri mapUri, partUri; | |
| 1936 Uri sourceMapUri = compiler.sourceMapUri; | |
| 1937 Uri outputUri = compiler.outputUri; | |
| 1938 | |
| 1939 String partName = "$partPrefix.part"; | |
| 1940 | |
| 1941 if (sourceMapUri != null) { | |
| 1942 String mapFileName = partName + ".js.map"; | |
| 1943 List<String> mapSegments = sourceMapUri.pathSegments.toList(); | |
| 1944 mapSegments[mapSegments.length - 1] = mapFileName; | |
| 1945 mapUri = compiler.sourceMapUri.replace(pathSegments: mapSegments); | |
| 1946 } | |
| 1947 | |
| 1948 if (outputUri != null) { | |
| 1949 String partFileName = partName + ".js"; | |
| 1950 List<String> partSegments = outputUri.pathSegments.toList(); | |
| 1951 partSegments[partSegments.length - 1] = partFileName; | |
| 1952 partUri = compiler.outputUri.replace(pathSegments: partSegments); | |
| 1953 } | |
| 1954 | |
| 1955 output.add(generateSourceMapTag(mapUri, partUri)); | |
| 1956 output.close(); | |
| 1957 outputSourceMap(output, lineColumnCollector, partName, | |
| 1958 mapUri, partUri); | |
| 1959 } else { | |
| 1960 output.close(); | |
| 1961 } | |
| 1962 | |
| 1963 hunkHashes[outputUnit] = hash; | |
| 1964 } | |
| 1965 return hunkHashes; | |
| 1966 } | |
| 1967 | |
| 1968 jsAst.Comment buildGeneratedBy() { | |
| 1969 String suffix = ''; | |
| 1970 if (compiler.hasBuildId) suffix = ' version: ${compiler.buildId}'; | |
| 1971 String msg = '// Generated by dart2js, the Dart to JavaScript ' | |
| 1972 'compiler$suffix.'; | |
| 1973 return new jsAst.Comment(msg); | |
| 1974 } | |
| 1975 | |
| 1976 void outputSourceMap(CodeOutput output, | |
| 1977 LineColumnProvider lineColumnProvider, | |
| 1978 String name, | |
| 1979 [Uri sourceMapUri, | |
| 1980 Uri fileUri]) { | |
| 1981 if (!generateSourceMap) return; | |
| 1982 // Create a source file for the compilation output. This allows using | |
| 1983 // [:getLine:] to transform offsets to line numbers in [SourceMapBuilder]. | |
| 1984 SourceMapBuilder sourceMapBuilder = | |
| 1985 new SourceMapBuilder(sourceMapUri, fileUri, lineColumnProvider); | |
| 1986 output.forEachSourceLocation(sourceMapBuilder.addMapping); | |
| 1987 String sourceMap = sourceMapBuilder.build(); | |
| 1988 compiler.outputProvider(name, 'js.map') | |
| 1989 ..add(sourceMap) | |
| 1990 ..close(); | |
| 1991 } | |
| 1992 | |
| 1993 void outputDeferredMap() { | |
| 1994 Map<String, dynamic> mapping = new Map<String, dynamic>(); | |
| 1995 // Json does not support comments, so we embed the explanation in the | |
| 1996 // data. | |
| 1997 mapping["_comment"] = "This mapping shows which compiled `.js` files are " | |
| 1998 "needed for a given deferred library import."; | |
| 1999 mapping.addAll(compiler.deferredLoadTask.computeDeferredMap()); | |
| 2000 compiler.outputProvider(compiler.deferredMapUri.path, 'deferred_map') | |
| 2001 ..add(const JsonEncoder.withIndent(" ").convert(mapping)) | |
| 2002 ..close(); | |
| 2003 } | |
| 2004 | |
| 2005 void invalidateCaches() { | |
| 2006 if (!compiler.hasIncrementalSupport) return; | |
| 2007 if (cachedElements.isEmpty) return; | |
| 2008 for (Element element in compiler.enqueuer.codegen.newlyEnqueuedElements) { | |
| 2009 if (element.isInstanceMember) { | |
| 2010 cachedClassBuilders.remove(element.enclosingClass); | |
| 2011 | |
| 2012 nativeEmitter.cachedBuilders.remove(element.enclosingClass); | |
| 2013 | |
| 2014 } | |
| 2015 } | |
| 2016 } | |
| 2017 } | |
| OLD | NEW |