| 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 library dart2js.new_js_emitter.model_emitter; | |
| 6 | |
| 7 import '../../constants/values.dart' show ConstantValue, FunctionConstantValue; | |
| 8 import '../../dart2jslib.dart' show Compiler; | |
| 9 import '../../elements/elements.dart' show ClassElement, FunctionElement; | |
| 10 import '../../js/js.dart' as js; | |
| 11 import '../../js_backend/js_backend.dart' show | |
| 12 JavaScriptBackend, | |
| 13 Namer, | |
| 14 ConstantEmitter; | |
| 15 | |
| 16 import '../js_emitter.dart' show AstContainer, NativeEmitter; | |
| 17 | |
| 18 import 'package:js_runtime/shared/embedded_names.dart' show | |
| 19 CREATE_NEW_ISOLATE, | |
| 20 DEFERRED_LIBRARY_URIS, | |
| 21 DEFERRED_LIBRARY_HASHES, | |
| 22 GET_TYPE_FROM_NAME, | |
| 23 INITIALIZE_LOADED_HUNK, | |
| 24 INTERCEPTORS_BY_TAG, | |
| 25 IS_HUNK_INITIALIZED, | |
| 26 IS_HUNK_LOADED, | |
| 27 LEAF_TAGS, | |
| 28 MANGLED_GLOBAL_NAMES, | |
| 29 METADATA, | |
| 30 TYPE_TO_INTERCEPTOR_MAP, | |
| 31 TYPES; | |
| 32 | |
| 33 import '../js_emitter.dart' show NativeGenerator, buildTearOffCode; | |
| 34 import '../model.dart'; | |
| 35 | |
| 36 class ModelEmitter { | |
| 37 final Compiler compiler; | |
| 38 final Namer namer; | |
| 39 ConstantEmitter constantEmitter; | |
| 40 final NativeEmitter nativeEmitter; | |
| 41 | |
| 42 JavaScriptBackend get backend => compiler.backend; | |
| 43 | |
| 44 /// For deferred loading we communicate the initializers via this global var. | |
| 45 static const String deferredInitializersGlobal = | |
| 46 r"$__dart_deferred_initializers__"; | |
| 47 | |
| 48 static const String deferredExtension = "part.js"; | |
| 49 | |
| 50 static const String typeNameProperty = r"builtin$cls"; | |
| 51 | |
| 52 ModelEmitter(Compiler compiler, Namer namer, this.nativeEmitter) | |
| 53 : this.compiler = compiler, | |
| 54 this.namer = namer { | |
| 55 | |
| 56 this.constantEmitter = new ConstantEmitter( | |
| 57 compiler, namer, this.generateConstantReference, | |
| 58 constantListGenerator); | |
| 59 } | |
| 60 | |
| 61 js.Expression constantListGenerator(js.Expression array) { | |
| 62 // TODO(floitsch): remove hard-coded name. | |
| 63 return js.js('makeConstList(#)', [array]); | |
| 64 } | |
| 65 | |
| 66 js.Expression generateEmbeddedGlobalAccess(String global) { | |
| 67 return js.js(generateEmbeddedGlobalAccessString(global)); | |
| 68 } | |
| 69 | |
| 70 String generateEmbeddedGlobalAccessString(String global) { | |
| 71 // TODO(floitsch): don't use 'init' as global embedder storage. | |
| 72 return 'init.$global'; | |
| 73 } | |
| 74 | |
| 75 bool isConstantInlinedOrAlreadyEmitted(ConstantValue constant) { | |
| 76 if (constant.isFunction) return true; // Already emitted. | |
| 77 if (constant.isPrimitive) return true; // Inlined. | |
| 78 if (constant.isDummy) return true; // Inlined. | |
| 79 // The name is null when the constant is already a JS constant. | |
| 80 // TODO(floitsch): every constant should be registered, so that we can | |
| 81 // share the ones that take up too much space (like some strings). | |
| 82 if (namer.constantName(constant) == null) return true; | |
| 83 return false; | |
| 84 } | |
| 85 | |
| 86 // TODO(floitsch): copied from OldEmitter. Adjust or share. | |
| 87 int compareConstants(ConstantValue a, ConstantValue b) { | |
| 88 // Inlined constants don't affect the order and sometimes don't even have | |
| 89 // names. | |
| 90 int cmp1 = isConstantInlinedOrAlreadyEmitted(a) ? 0 : 1; | |
| 91 int cmp2 = isConstantInlinedOrAlreadyEmitted(b) ? 0 : 1; | |
| 92 if (cmp1 + cmp2 < 2) return cmp1 - cmp2; | |
| 93 | |
| 94 // Emit constant interceptors first. Constant interceptors for primitives | |
| 95 // might be used by code that builds other constants. See Issue 18173. | |
| 96 if (a.isInterceptor != b.isInterceptor) { | |
| 97 return a.isInterceptor ? -1 : 1; | |
| 98 } | |
| 99 | |
| 100 // Sorting by the long name clusters constants with the same constructor | |
| 101 // which compresses a tiny bit better. | |
| 102 int r = namer.constantLongName(a).compareTo(namer.constantLongName(b)); | |
| 103 if (r != 0) return r; | |
| 104 // Resolve collisions in the long name by using the constant name (i.e. JS | |
| 105 // name) which is unique. | |
| 106 return namer.constantName(a).compareTo(namer.constantName(b)); | |
| 107 } | |
| 108 | |
| 109 js.Expression generateStaticClosureAccess(FunctionElement element) { | |
| 110 return js.js('#.#()', | |
| 111 [namer.globalObjectFor(element), namer.staticClosureName(element)]); | |
| 112 } | |
| 113 | |
| 114 js.Expression generateConstantReference(ConstantValue value) { | |
| 115 if (value.isFunction) { | |
| 116 FunctionConstantValue functionConstant = value; | |
| 117 return generateStaticClosureAccess(functionConstant.element); | |
| 118 } | |
| 119 | |
| 120 // We are only interested in the "isInlined" part, but it does not hurt to | |
| 121 // test for the other predicates. | |
| 122 if (isConstantInlinedOrAlreadyEmitted(value)) { | |
| 123 return constantEmitter.generate(value); | |
| 124 } | |
| 125 return js.js('#.#()', [namer.globalObjectForConstant(value), | |
| 126 namer.constantName(value)]); | |
| 127 } | |
| 128 | |
| 129 int emitProgram(Program program) { | |
| 130 List<Fragment> fragments = program.fragments; | |
| 131 MainFragment mainFragment = fragments.first; | |
| 132 | |
| 133 int totalSize = 0; | |
| 134 | |
| 135 // We have to emit the deferred fragments first, since we need their | |
| 136 // deferred hash (which depends on the output) when emitting the main | |
| 137 // fragment. | |
| 138 List<js.Expression> fragmentsCode = fragments.skip(1).map( | |
| 139 (DeferredFragment deferredUnit) { | |
| 140 js.Expression types = | |
| 141 program.metadataTypesForOutputUnit(deferredUnit.outputUnit); | |
| 142 return emitDeferredFragment(types, deferredUnit, program.holders); | |
| 143 }).toList(); | |
| 144 | |
| 145 js.Statement mainAst = emitMainFragment(program); | |
| 146 | |
| 147 js.TokenCounter counter = new js.TokenCounter(); | |
| 148 fragmentsCode.forEach(counter.countTokens); | |
| 149 counter.countTokens(mainAst); | |
| 150 | |
| 151 program.finalizers.forEach((js.TokenFinalizer f) => f.finalizeTokens()); | |
| 152 | |
| 153 for (int i = 0; i < fragmentsCode.length; ++i) { | |
| 154 String code = js.prettyPrint(fragmentsCode[i], compiler).getText(); | |
| 155 totalSize += code.length; | |
| 156 compiler.outputProvider(fragments[i+1].outputFileName, deferredExtension) | |
| 157 ..add(code) | |
| 158 ..close(); | |
| 159 } | |
| 160 | |
| 161 String mainCode = js.prettyPrint(mainAst, compiler).getText(); | |
| 162 compiler.outputProvider(mainFragment.outputFileName, 'js') | |
| 163 ..add(buildGeneratedBy(compiler)) | |
| 164 ..add(mainCode) | |
| 165 ..close(); | |
| 166 totalSize += mainCode.length; | |
| 167 | |
| 168 return totalSize; | |
| 169 } | |
| 170 | |
| 171 /// Returns a [js.Literal] that represents the string result of unparsing | |
| 172 /// [value]. | |
| 173 /// | |
| 174 /// The returned node will, when its string value is requested, pretty-print | |
| 175 /// the given [value] and, if [protectForEval] is true, wrap the resulting | |
| 176 /// string in parenthesis. The result is also escaped. | |
| 177 /// | |
| 178 /// See [_UnparsedNode] for details. | |
| 179 js.Literal unparse(Compiler compiler, js.Node value, | |
| 180 {bool protectForEval: true}) { | |
| 181 return new js.UnparsedNode(value, compiler, protectForEval); | |
| 182 } | |
| 183 | |
| 184 String buildGeneratedBy(compiler) { | |
| 185 var suffix = ''; | |
| 186 if (compiler.hasBuildId) suffix = ' version: ${compiler.buildId}'; | |
| 187 return '// Generated by dart2js, the Dart to JavaScript compiler$suffix.\n'; | |
| 188 } | |
| 189 | |
| 190 js.Statement emitMainFragment(Program program) { | |
| 191 MainFragment fragment = program.fragments.first; | |
| 192 List<js.Expression> elements = fragment.libraries.map(emitLibrary).toList(); | |
| 193 elements.add( | |
| 194 emitLazilyInitializedStatics(fragment.staticLazilyInitializedFields)); | |
| 195 elements.add(emitConstants(fragment.constants)); | |
| 196 | |
| 197 js.Expression code = new js.ArrayInitializer(elements); | |
| 198 | |
| 199 Map<String, dynamic> holes = | |
| 200 {'deferredInitializer': emitDeferredInitializerGlobal(program.loadMap), | |
| 201 'holders': emitHolders(program.holders), | |
| 202 'tearOff': buildTearOffCode(backend), | |
| 203 'parseFunctionDescriptor': | |
| 204 js.js.statement(parseFunctionDescriptorBoilerplate, | |
| 205 {'argumentCount': js.string(namer.requiredParameterField), | |
| 206 'defaultArgumentValues': js.string(namer.defaultValuesField), | |
| 207 'callName': js.string(namer.callNameField)}), | |
| 208 | |
| 209 'cyclicThrow': | |
| 210 backend.emitter.staticFunctionAccess(backend.getCyclicThrowHelper()), | |
| 211 'outputContainsConstantList': program.outputContainsConstantList, | |
| 212 'embeddedGlobals': emitEmbeddedGlobals(program), | |
| 213 'readMetadataTypeFunction': readMetadataTypeFunction, | |
| 214 'staticNonFinals': | |
| 215 emitStaticNonFinalFields(fragment.staticNonFinalFields), | |
| 216 'operatorIsPrefix': js.string(namer.operatorIsPrefix), | |
| 217 'callName': js.string(namer.callNameField), | |
| 218 'argumentCount': js.string(namer.requiredParameterField), | |
| 219 'defaultArgumentValues': js.string(namer.defaultValuesField), | |
| 220 'eagerClasses': emitEagerClassInitializations(fragment.libraries), | |
| 221 'invokeMain': fragment.invokeMain, | |
| 222 'code': code}; | |
| 223 | |
| 224 holes.addAll(nativeHoles(program)); | |
| 225 | |
| 226 return js.js.statement(boilerplate, holes); | |
| 227 } | |
| 228 | |
| 229 Map<String, dynamic> nativeHoles(Program program) { | |
| 230 Map<String, dynamic> nativeHoles = <String, dynamic>{}; | |
| 231 | |
| 232 js.Statement nativeIsolateAffinityTagInitialization; | |
| 233 if (NativeGenerator.needsIsolateAffinityTagInitialization(backend)) { | |
| 234 nativeIsolateAffinityTagInitialization = | |
| 235 NativeGenerator.generateIsolateAffinityTagInitialization( | |
| 236 backend, | |
| 237 generateEmbeddedGlobalAccess, | |
| 238 // TODO(floitsch): convertToFastObject. | |
| 239 js.js("(function(x) { return x; })", [])); | |
| 240 } else { | |
| 241 nativeIsolateAffinityTagInitialization = js.js.statement(";"); | |
| 242 } | |
| 243 nativeHoles['nativeIsolateAffinityTagInitialization'] = | |
| 244 nativeIsolateAffinityTagInitialization; | |
| 245 | |
| 246 | |
| 247 js.Expression nativeInfoAccess = js.js('nativeInfo', []); | |
| 248 js.Expression constructorAccess = js.js('constructor', []); | |
| 249 Function subclassReadGenerator = (js.Expression subclass) { | |
| 250 return js.js('holdersMap[#][#].ensureResolved()', [subclass, subclass]); | |
| 251 }; | |
| 252 js.Expression interceptorsByTagAccess = | |
| 253 generateEmbeddedGlobalAccess(INTERCEPTORS_BY_TAG); | |
| 254 js.Expression leafTagsAccess = | |
| 255 generateEmbeddedGlobalAccess(LEAF_TAGS); | |
| 256 js.Statement nativeInfoHandler = NativeGenerator.buildNativeInfoHandler( | |
| 257 nativeInfoAccess, | |
| 258 constructorAccess, | |
| 259 subclassReadGenerator, | |
| 260 interceptorsByTagAccess, | |
| 261 leafTagsAccess); | |
| 262 | |
| 263 nativeHoles['needsNativeSupport'] = program.needsNativeSupport; | |
| 264 nativeHoles['needsNoNativeSupport'] = !program.needsNativeSupport; | |
| 265 nativeHoles['nativeInfoHandler'] = nativeInfoHandler; | |
| 266 | |
| 267 return nativeHoles; | |
| 268 } | |
| 269 | |
| 270 js.Block emitHolders(List<Holder> holders) { | |
| 271 // The top-level variables for holders must *not* be renamed by the | |
| 272 // JavaScript pretty printer because a lot of code already uses the | |
| 273 // non-renamed names. The generated code looks like this: | |
| 274 // | |
| 275 // var H = {}, ..., G = {}; | |
| 276 // var holders = [ H, ..., G ]; | |
| 277 // | |
| 278 // and it is inserted at the top of the top-level function expression | |
| 279 // that covers the entire program. | |
| 280 | |
| 281 List<js.Statement> statements = [ | |
| 282 new js.ExpressionStatement( | |
| 283 new js.VariableDeclarationList(holders.map((e) => | |
| 284 new js.VariableInitialization( | |
| 285 new js.VariableDeclaration(e.name, allowRename: false), | |
| 286 new js.ObjectInitializer(const []))).toList())), | |
| 287 js.js.statement('var holders = #', new js.ArrayInitializer( | |
| 288 holders.map((e) => new js.VariableUse(e.name)) | |
| 289 .toList(growable: false))), | |
| 290 js.js.statement('var holdersMap = Object.create(null)') | |
| 291 ]; | |
| 292 return new js.Block(statements); | |
| 293 } | |
| 294 | |
| 295 js.Block emitEmbeddedGlobals(Program program) { | |
| 296 List<js.Property> globals = <js.Property>[]; | |
| 297 | |
| 298 if (program.loadMap.isNotEmpty) { | |
| 299 globals.addAll(emitEmbeddedGlobalsForDeferredLoading(program.loadMap)); | |
| 300 } | |
| 301 | |
| 302 if (program.typeToInterceptorMap != null) { | |
| 303 globals.add(new js.Property(js.string(TYPE_TO_INTERCEPTOR_MAP), | |
| 304 program.typeToInterceptorMap)); | |
| 305 } | |
| 306 | |
| 307 if (program.hasIsolateSupport) { | |
| 308 String isolateName = namer.currentIsolate; | |
| 309 globals.add( | |
| 310 new js.Property(js.string(CREATE_NEW_ISOLATE), | |
| 311 js.js('function () { return $isolateName; }'))); | |
| 312 // TODO(floitsch): add remaining isolate functions. | |
| 313 } | |
| 314 | |
| 315 globals.add(emitMangledGlobalNames()); | |
| 316 | |
| 317 globals.add(emitGetTypeFromName()); | |
| 318 | |
| 319 globals.addAll(emitMetadata(program)); | |
| 320 | |
| 321 if (program.needsNativeSupport) { | |
| 322 globals.add(new js.Property(js.string(INTERCEPTORS_BY_TAG), | |
| 323 js.js('Object.create(null)', []))); | |
| 324 globals.add(new js.Property(js.string(LEAF_TAGS), | |
| 325 js.js('Object.create(null)', []))); | |
| 326 } | |
| 327 | |
| 328 js.ObjectInitializer globalsObject = new js.ObjectInitializer(globals); | |
| 329 | |
| 330 List<js.Statement> statements = | |
| 331 [new js.ExpressionStatement( | |
| 332 new js.VariableDeclarationList( | |
| 333 [new js.VariableInitialization( | |
| 334 new js.VariableDeclaration("init", allowRename: false), | |
| 335 globalsObject)]))]; | |
| 336 return new js.Block(statements); | |
| 337 } | |
| 338 | |
| 339 js.Property emitMangledGlobalNames() { | |
| 340 List<js.Property> names = <js.Property>[]; | |
| 341 | |
| 342 // We want to keep the original names for the most common core classes when | |
| 343 // calling toString on them. | |
| 344 List<ClassElement> nativeClassesNeedingUnmangledName = | |
| 345 [compiler.intClass, compiler.doubleClass, compiler.numClass, | |
| 346 compiler.stringClass, compiler.boolClass, compiler.nullClass, | |
| 347 compiler.listClass]; | |
| 348 nativeClassesNeedingUnmangledName.forEach((element) { | |
| 349 names.add(new js.Property(js.quoteName(namer.className(element)), | |
| 350 js.string(element.name))); | |
| 351 }); | |
| 352 | |
| 353 return new js.Property(js.string(MANGLED_GLOBAL_NAMES), | |
| 354 new js.ObjectInitializer(names)); | |
| 355 } | |
| 356 | |
| 357 js.Statement emitDeferredInitializerGlobal(Map loadMap) { | |
| 358 if (loadMap.isEmpty) return new js.Block.empty(); | |
| 359 | |
| 360 return js.js.statement(""" | |
| 361 if (typeof($deferredInitializersGlobal) === 'undefined') | |
| 362 var $deferredInitializersGlobal = Object.create(null);"""); | |
| 363 } | |
| 364 | |
| 365 Iterable<js.Property> emitEmbeddedGlobalsForDeferredLoading( | |
| 366 Map<String, List<Fragment>> loadMap) { | |
| 367 | |
| 368 List<js.Property> globals = <js.Property>[]; | |
| 369 | |
| 370 js.ArrayInitializer fragmentUris(List<Fragment> fragments) { | |
| 371 return js.stringArray(fragments.map((DeferredFragment fragment) => | |
| 372 "${fragment.outputFileName}.$deferredExtension")); | |
| 373 } | |
| 374 js.ArrayInitializer fragmentHashes(List<Fragment> fragments) { | |
| 375 // TODO(floitsch): the hash must depend on the generated code. | |
| 376 return js.numArray( | |
| 377 fragments.map((DeferredFragment fragment) => fragment.hashCode)); | |
| 378 } | |
| 379 | |
| 380 List<js.Property> uris = new List<js.Property>(loadMap.length); | |
| 381 List<js.Property> hashes = new List<js.Property>(loadMap.length); | |
| 382 int count = 0; | |
| 383 loadMap.forEach((String loadId, List<Fragment> fragmentList) { | |
| 384 uris[count] = | |
| 385 new js.Property(js.string(loadId), fragmentUris(fragmentList)); | |
| 386 hashes[count] = | |
| 387 new js.Property(js.string(loadId), fragmentHashes(fragmentList)); | |
| 388 count++; | |
| 389 }); | |
| 390 | |
| 391 globals.add(new js.Property(js.string(DEFERRED_LIBRARY_URIS), | |
| 392 new js.ObjectInitializer(uris))); | |
| 393 globals.add(new js.Property(js.string(DEFERRED_LIBRARY_HASHES), | |
| 394 new js.ObjectInitializer(hashes))); | |
| 395 | |
| 396 js.Expression isHunkLoadedFunction = | |
| 397 js.js("function(hash) { return !!$deferredInitializersGlobal[hash]; }"); | |
| 398 globals.add(new js.Property(js.string(IS_HUNK_LOADED), | |
| 399 isHunkLoadedFunction)); | |
| 400 | |
| 401 js.Expression isHunkInitializedFunction = | |
| 402 js.js("function(hash) { return false; }"); | |
| 403 globals.add(new js.Property(js.string(IS_HUNK_INITIALIZED), | |
| 404 isHunkInitializedFunction)); | |
| 405 | |
| 406 js.Expression typesAccess = generateEmbeddedGlobalAccess(TYPES); | |
| 407 | |
| 408 /// See [emitEmbeddedGlobalsForDeferredLoading] for the format of the | |
| 409 /// deferred hunk. | |
| 410 js.Expression initializeLoadedHunkFunction = | |
| 411 js.js(""" | |
| 412 function(hash) { | |
| 413 var hunk = $deferredInitializersGlobal[hash]; | |
| 414 $setupProgramName(hunk[0], #typesAccess.length); | |
| 415 eval(hunk[1]); | |
| 416 var deferredTypes = eval(hunk[2]); | |
| 417 #typesAccess.push.apply(#typesAccess, deferredTypes); | |
| 418 }""", {'typesAccess': typesAccess}); | |
| 419 | |
| 420 globals.add(new js.Property(js.string(INITIALIZE_LOADED_HUNK), | |
| 421 initializeLoadedHunkFunction)); | |
| 422 | |
| 423 return globals; | |
| 424 } | |
| 425 | |
| 426 js.Property emitGetTypeFromName() { | |
| 427 js.Expression function = | |
| 428 js.js( """function(name) { | |
| 429 return holdersMap[name][name].ensureResolved(); | |
| 430 }"""); | |
| 431 return new js.Property(js.string(GET_TYPE_FROM_NAME), function); | |
| 432 } | |
| 433 | |
| 434 static final String readMetadataTypeName = "readMetadataType"; | |
| 435 | |
| 436 js.Statement get readMetadataTypeFunction { | |
| 437 // Types are non-evaluated and must be compiled at first use. | |
| 438 // Compiled strings are guaranteed not to be strings, and it's thus safe | |
| 439 // to use a type-test to determine if a type has already been compiled. | |
| 440 return js.js.statement('''function $readMetadataTypeName(index) { | |
| 441 var type = #typesAccess[index]; | |
| 442 if (typeof type == 'string') { | |
| 443 type = expressionCompile(type); | |
| 444 #typesAccess[index] = type; | |
| 445 } | |
| 446 return type; | |
| 447 }''', {"typesAccess": generateEmbeddedGlobalAccess(TYPES)}); | |
| 448 } | |
| 449 | |
| 450 js.Template get templateForReadType { | |
| 451 // TODO(floitsch): make sure that no local variable shadows the access to | |
| 452 // the readMetadataType function. | |
| 453 return js.js.expressionTemplateFor('$readMetadataTypeName(#)'); | |
| 454 } | |
| 455 | |
| 456 static final String readMetadataName = "readLazyMetadata"; | |
| 457 static final String lazyMetadataName = "lazy_$METADATA"; | |
| 458 | |
| 459 js.Statement get readMetadataFunction { | |
| 460 // Types are non-evaluated and must be compiled at first use. | |
| 461 // Compiled strings are guaranteed not to be strings, and it's thus safe | |
| 462 // to use a type-test to determine if a type has already been compiled. | |
| 463 return js.js.statement('''function $readMetadataName(index) { | |
| 464 var lazyMetadata = #lazyMetadataAccess[index]; | |
| 465 if (typeof lazyMetadata == 'string') { | |
| 466 #metadataAccess[index] = expressionCompile(lazyMetadata); | |
| 467 #lazyMetadataAccess[index] = null; | |
| 468 } | |
| 469 return #metadataAccess[index]; | |
| 470 }''', { | |
| 471 "lazyMetadataAccess": generateEmbeddedGlobalAccess(lazyMetadataName), | |
| 472 "metadataAccess": generateEmbeddedGlobalAccess(METADATA) | |
| 473 }); | |
| 474 } | |
| 475 | |
| 476 js.Template get templateForReadMetadata { | |
| 477 // TODO(floitsch): make sure that no local variable shadows the access to | |
| 478 // the readMetadata function. | |
| 479 return js.js.expressionTemplateFor('$readMetadataName(#)'); | |
| 480 } | |
| 481 | |
| 482 List<js.Property> emitMetadata(Program program) { | |
| 483 List<js.Property> metadataGlobals = <js.Property>[]; | |
| 484 | |
| 485 js.Property createGlobal(js.Expression metadata, String global) { | |
| 486 return new js.Property(js.string(global), metadata); | |
| 487 } | |
| 488 | |
| 489 metadataGlobals.add(createGlobal(program.metadata, METADATA)); | |
| 490 js.Expression types = | |
| 491 program.metadataTypesForOutputUnit(program.mainFragment.outputUnit); | |
| 492 metadataGlobals.add(createGlobal(types, TYPES)); | |
| 493 | |
| 494 return metadataGlobals; | |
| 495 } | |
| 496 | |
| 497 js.Expression emitDeferredFragment(js.Expression deferredTypes, | |
| 498 DeferredFragment fragment, | |
| 499 List<Holder> holders) { | |
| 500 // TODO(floitsch): initialize eager classes. | |
| 501 // TODO(floitsch): the hash must depend on the output. | |
| 502 int hash = fragment.hashCode; | |
| 503 | |
| 504 List<js.Expression> deferredCode = | |
| 505 fragment.libraries.map(emitLibrary).toList(); | |
| 506 | |
| 507 deferredCode.add( | |
| 508 emitLazilyInitializedStatics(fragment.staticLazilyInitializedFields)); | |
| 509 deferredCode.add(emitConstants(fragment.constants)); | |
| 510 | |
| 511 js.ArrayInitializer deferredArray = new js.ArrayInitializer(deferredCode); | |
| 512 | |
| 513 // This is the code that must be evaluated after all deferred classes have | |
| 514 // been setup. | |
| 515 js.Statement immediateCode = new js.Block([ | |
| 516 emitStaticNonFinalFields(fragment.staticNonFinalFields), | |
| 517 emitEagerClassInitializations(fragment.libraries)]); | |
| 518 | |
| 519 | |
| 520 js.Literal immediateString = unparse(compiler, immediateCode); | |
| 521 | |
| 522 js.ArrayInitializer hunk = | |
| 523 new js.ArrayInitializer([deferredArray, immediateString, | |
| 524 deferredTypes]); | |
| 525 | |
| 526 return js.js("$deferredInitializersGlobal[$hash] = #", hunk); | |
| 527 } | |
| 528 | |
| 529 // This string should be referenced wherever JavaScript code makes assumptions | |
| 530 // on the constants format. | |
| 531 static final String constantsDescription = | |
| 532 "The constants are encoded as a follows:" | |
| 533 " [constantsHolderIndex, name, code, name2, code2, ...]." | |
| 534 "The array is completely empty if there is no constant at all."; | |
| 535 | |
| 536 js.ArrayInitializer emitConstants(List<Constant> constants) { | |
| 537 List<js.Expression> data = <js.Expression>[]; | |
| 538 if (constants.isNotEmpty) { | |
| 539 int holderIndex = constants.first.holder.index; | |
| 540 data.add(js.number(holderIndex)); | |
| 541 data.addAll(constants.expand((Constant constant) { | |
| 542 assert(constant.holder.index == holderIndex); | |
| 543 js.Expression code = constantEmitter.generate(constant.value); | |
| 544 return [js.quoteName(constant.name), unparse(compiler, code)]; | |
| 545 })); | |
| 546 } | |
| 547 return new js.ArrayInitializer(data); | |
| 548 } | |
| 549 | |
| 550 js.Block emitStaticNonFinalFields(List<StaticField> fields) { | |
| 551 Iterable<js.Statement> statements = fields.map((StaticField field) { | |
| 552 return js.js.statement("#.# = #;", | |
| 553 [field.holder.name, field.name, field.code]); | |
| 554 }); | |
| 555 return new js.Block(statements.toList()); | |
| 556 } | |
| 557 | |
| 558 js.Expression emitLazilyInitializedStatics(List<StaticField> fields) { | |
| 559 Iterable fieldDescriptors = fields.expand((field) => | |
| 560 [ js.quoteName(field.name), | |
| 561 js.quoteName(namer.deriveLazyInitializerName(field.name)), | |
| 562 js.number(field.holder.index), | |
| 563 emitLazyInitializer(field) ]); | |
| 564 return new js.ArrayInitializer(fieldDescriptors.toList(growable: false)); | |
| 565 } | |
| 566 | |
| 567 js.Block emitEagerClassInitializations(List<Library> libraries) { | |
| 568 js.Statement createInstantiation(Class cls) { | |
| 569 return js.js.statement('new #.#()', [cls.holder.name, cls.name]); | |
| 570 } | |
| 571 | |
| 572 List<js.Statement> instantiations = | |
| 573 libraries.expand((Library library) => library.classes) | |
| 574 .where((Class cls) => cls.isEager) | |
| 575 .map(createInstantiation) | |
| 576 .toList(growable: false); | |
| 577 return new js.Block(instantiations); | |
| 578 } | |
| 579 | |
| 580 // This string should be referenced wherever JavaScript code makes assumptions | |
| 581 // on the mixin format. | |
| 582 static final String nativeInfoDescription = | |
| 583 "A class is encoded as follows:" | |
| 584 " [name, class-code, holder-index], or " | |
| 585 " [name, class-code, native-info, holder-index]."; | |
| 586 | |
| 587 js.Expression emitLibrary(Library library) { | |
| 588 Iterable staticDescriptors = library.statics.expand(emitStaticMethod); | |
| 589 | |
| 590 Iterable classDescriptors = library.classes.expand((Class cls) { | |
| 591 js.Literal name = js.quoteName(cls.name); | |
| 592 js.LiteralNumber holderIndex = js.number(cls.holder.index); | |
| 593 js.Expression emittedClass = emitClass(cls); | |
| 594 js.Expression nativeInfo = NativeGenerator.encodeNativeInfo(cls); | |
| 595 if (nativeInfo == null) { | |
| 596 return [name, emittedClass, holderIndex]; | |
| 597 } else { | |
| 598 return [name, emittedClass, nativeInfo, holderIndex]; | |
| 599 } | |
| 600 }); | |
| 601 | |
| 602 js.Expression staticArray = | |
| 603 new js.ArrayInitializer(staticDescriptors.toList(growable: false)); | |
| 604 js.Expression classArray = | |
| 605 new js.ArrayInitializer(classDescriptors.toList(growable: false)); | |
| 606 | |
| 607 return new js.ArrayInitializer([staticArray, classArray]); | |
| 608 } | |
| 609 | |
| 610 js.Expression _generateConstructor(Class cls) { | |
| 611 List<js.Name> fieldNames = const <js.Name>[]; | |
| 612 | |
| 613 // If the class is not directly instantiated we only need it for inheritance | |
| 614 // or RTI. In either case we don't need its fields. | |
| 615 if (cls.isDirectlyInstantiated && !cls.isNative) { | |
| 616 fieldNames = cls.fields.map((Field field) => field.name).toList(); | |
| 617 } | |
| 618 js.Name name = cls.name; | |
| 619 | |
| 620 Iterable<js.Name> assignments = fieldNames.map((js.Name field) { | |
| 621 return js.js("this.#field = #field", {"field": field}); | |
| 622 }); | |
| 623 | |
| 624 return js.js('function #(#) { # }', [name, fieldNames, assignments]); | |
| 625 } | |
| 626 | |
| 627 Method _generateGetter(Field field) { | |
| 628 String getterTemplateFor(int flags) { | |
| 629 switch (flags) { | |
| 630 case 1: return "function() { return this[#]; }"; | |
| 631 case 2: return "function(receiver) { return receiver[#]; }"; | |
| 632 case 3: return "function(receiver) { return this[#]; }"; | |
| 633 } | |
| 634 return null; | |
| 635 } | |
| 636 | |
| 637 js.Expression fieldName = js.quoteName(field.name); | |
| 638 js.Expression code = js.js(getterTemplateFor(field.getterFlags), fieldName); | |
| 639 js.Name getterName = namer.deriveGetterName(field.accessorName); | |
| 640 return new StubMethod(getterName, code); | |
| 641 } | |
| 642 | |
| 643 Method _generateSetter(Field field) { | |
| 644 String setterTemplateFor(int flags) { | |
| 645 switch (flags) { | |
| 646 case 1: return "function(val) { return this[#] = val; }"; | |
| 647 case 2: return "function(receiver, val) { return receiver[#] = val; }"; | |
| 648 case 3: return "function(receiver, val) { return this[#] = val; }"; | |
| 649 } | |
| 650 return null; | |
| 651 } | |
| 652 js.Expression fieldName = js.quoteName(field.name); | |
| 653 js.Expression code = js.js(setterTemplateFor(field.setterFlags), fieldName); | |
| 654 js.Name setterName = namer.deriveSetterName(field.accessorName); | |
| 655 return new StubMethod(setterName, code); | |
| 656 } | |
| 657 | |
| 658 Iterable<Method> _generateGettersSetters(Class cls) { | |
| 659 Iterable<Method> getters = cls.fields | |
| 660 .where((Field field) => field.needsGetter) | |
| 661 .map(_generateGetter); | |
| 662 | |
| 663 Iterable<Method> setters = cls.fields | |
| 664 .where((Field field) => field.needsUncheckedSetter) | |
| 665 .map(_generateSetter); | |
| 666 | |
| 667 return [getters, setters].expand((x) => x); | |
| 668 } | |
| 669 | |
| 670 // This string should be referenced wherever JavaScript code makes assumptions | |
| 671 // on the mixin format. | |
| 672 static final String mixinFormatDescription = | |
| 673 "Mixins have a reference to their mixin class at the place of the usual" | |
| 674 "constructor. If they are instantiated the constructor follows the" | |
| 675 "reference."; | |
| 676 | |
| 677 js.Expression emitClass(Class cls) { | |
| 678 List elements = [js.quoteName(cls.superclassName, allowNull: true), | |
| 679 js.number(cls.superclassHolderIndex)]; | |
| 680 | |
| 681 if (cls.isMixinApplication) { | |
| 682 MixinApplication mixin = cls; | |
| 683 elements.add(js.quoteName(mixin.mixinClass.name)); | |
| 684 elements.add(js.number(mixin.mixinClass.holder.index)); | |
| 685 if (cls.isDirectlyInstantiated) { | |
| 686 elements.add(_generateConstructor(cls)); | |
| 687 } | |
| 688 } else { | |
| 689 elements.add(_generateConstructor(cls)); | |
| 690 } | |
| 691 Iterable<Method> methods = cls.methods; | |
| 692 Iterable<Method> isChecks = cls.isChecks; | |
| 693 Iterable<Method> callStubs = cls.callStubs; | |
| 694 Iterable<Method> typeVariableReaderStubs = cls.typeVariableReaderStubs; | |
| 695 Iterable<Method> noSuchMethodStubs = cls.noSuchMethodStubs; | |
| 696 Iterable<Method> gettersSetters = _generateGettersSetters(cls); | |
| 697 Iterable<Method> allMethods = | |
| 698 [methods, isChecks, callStubs, typeVariableReaderStubs, | |
| 699 noSuchMethodStubs, gettersSetters].expand((x) => x); | |
| 700 elements.addAll(allMethods.expand(emitInstanceMethod)); | |
| 701 | |
| 702 return unparse(compiler, new js.ArrayInitializer(elements)); | |
| 703 } | |
| 704 | |
| 705 js.Expression emitLazyInitializer(StaticField field) { | |
| 706 assert(field.isLazy); | |
| 707 return unparse(compiler, field.code); | |
| 708 } | |
| 709 | |
| 710 /// JavaScript code template that implements parsing of a function descriptor. | |
| 711 /// Descriptors are used in place of the actual JavaScript function | |
| 712 /// definition in the output if additional information needs to be passed to | |
| 713 /// facilitate the generation of tearOffs at runtime. The format is an array | |
| 714 /// with the following fields: | |
| 715 /// | |
| 716 /// * [InstanceMethod.aliasName] (optional). | |
| 717 /// * [Method.code] | |
| 718 /// * [DartMethod.callName] | |
| 719 /// * isInterceptedMethod (optional, present if [DartMethod.needsTearOff]). | |
| 720 /// * [DartMethod.tearOffName] (optional, present if | |
| 721 /// [DartMethod.needsTearOff]). | |
| 722 /// * functionType (optional, present if [DartMethod.needsTearOff]). | |
| 723 /// | |
| 724 /// followed by | |
| 725 /// | |
| 726 /// * [ParameterStubMethod.name] | |
| 727 /// * [ParameterStubMethod.callName] | |
| 728 /// * [ParameterStubMethod.code] | |
| 729 /// | |
| 730 /// for each stub in [DartMethod.parameterStubs]. | |
| 731 /// | |
| 732 /// If the closure could be used in `Function.apply` (i.e. | |
| 733 /// [DartMethod.canBeApplied] is true) then the following fields are appended: | |
| 734 /// | |
| 735 /// * [DartMethod.requiredParameterCount] | |
| 736 /// * [DartMethod.optionalParameterDefaultValues] | |
| 737 | |
| 738 static final String parseFunctionDescriptorBoilerplate = r""" | |
| 739 function parseFunctionDescriptor(proto, name, descriptor, typesOffset) { | |
| 740 if (descriptor instanceof Array) { | |
| 741 // 'pos' points to the last read entry. | |
| 742 var f, pos = -1; | |
| 743 var aliasOrFunction = descriptor[++pos]; | |
| 744 if (typeof aliasOrFunction == "string") { | |
| 745 // Install the alias for super calls on the prototype chain. | |
| 746 proto[aliasOrFunction] = f = descriptor[++pos]; | |
| 747 } else { | |
| 748 f = aliasOrFunction; | |
| 749 } | |
| 750 | |
| 751 proto[name] = f; | |
| 752 var funs = [f]; | |
| 753 f[#callName] = descriptor[++pos]; | |
| 754 | |
| 755 var isInterceptedOrParameterStubName = descriptor[pos + 1]; | |
| 756 var isIntercepted, tearOffName, reflectionInfo; | |
| 757 if (typeof isInterceptedOrParameterStubName == "boolean") { | |
| 758 isIntercepted = descriptor[++pos]; | |
| 759 tearOffName = descriptor[++pos]; | |
| 760 reflectionInfo = descriptor[++pos]; | |
| 761 if (typeof reflectionInfo == "number") { | |
| 762 reflectionInfo = reflectionInfo + typesOffset; | |
| 763 } | |
| 764 } | |
| 765 | |
| 766 // We iterate in blocks of 3 but have to stop before we reach the (optional) | |
| 767 // two trailing items. To accomplish this, we only iterate until we reach | |
| 768 // length - 2. | |
| 769 for (++pos; pos < descriptor.length - 2; pos += 3) { | |
| 770 var stub = descriptor[pos + 2]; | |
| 771 stub[#callName] = descriptor[pos + 1]; | |
| 772 proto[descriptor[pos]] = stub; | |
| 773 funs.push(stub); | |
| 774 } | |
| 775 | |
| 776 if (tearOffName) { | |
| 777 proto[tearOffName] = | |
| 778 tearOff(funs, reflectionInfo, false, name, isIntercepted); | |
| 779 } | |
| 780 if (pos < descriptor.length) { | |
| 781 f[#argumentCount] = descriptor[pos]; | |
| 782 f[#defaultArgumentValues] = descriptor[pos + 1]; | |
| 783 } | |
| 784 } else { | |
| 785 proto[name] = descriptor; | |
| 786 } | |
| 787 } | |
| 788 """; | |
| 789 | |
| 790 js.Expression _encodeOptionalParameterDefaultValues(DartMethod method) { | |
| 791 // TODO(herhut): Replace [js.LiteralNull] with [js.ArrayHole]. | |
| 792 if (method.optionalParameterDefaultValues is List) { | |
| 793 List<ConstantValue> defaultValues = method.optionalParameterDefaultValues; | |
| 794 Iterable<js.Expression> elements = | |
| 795 defaultValues.map(generateConstantReference); | |
| 796 return new js.ArrayInitializer(elements.toList()); | |
| 797 } else { | |
| 798 Map<String, ConstantValue> defaultValues = | |
| 799 method.optionalParameterDefaultValues; | |
| 800 List<js.Property> properties = <js.Property>[]; | |
| 801 defaultValues.forEach((String name, ConstantValue value) { | |
| 802 properties.add(new js.Property(js.string(name), | |
| 803 generateConstantReference(value))); | |
| 804 }); | |
| 805 return new js.ObjectInitializer(properties); | |
| 806 } | |
| 807 } | |
| 808 | |
| 809 Iterable<js.Expression> emitInstanceMethod(Method method) { | |
| 810 | |
| 811 List<js.Expression> makeNameCodePair(Method method) { | |
| 812 return [js.quoteName(method.name), method.code]; | |
| 813 } | |
| 814 | |
| 815 List<js.Expression> makeNameCallNameCodeTriplet(ParameterStubMethod stub) { | |
| 816 js.Expression callName = stub.callName == null | |
| 817 ? new js.LiteralNull() | |
| 818 : js.quoteName(stub.callName); | |
| 819 return [js.quoteName(stub.name), callName, stub.code]; | |
| 820 } | |
| 821 | |
| 822 if (method is InstanceMethod) { | |
| 823 if (method.needsTearOff || method.aliasName != null) { | |
| 824 /// See [parseFunctionDescriptorBoilerplate] for a full description of | |
| 825 /// the format. | |
| 826 // [name, [aliasName, function, callName, isIntercepted, tearOffName, | |
| 827 // functionType, stub1_name, stub1_callName, stub1_code, ...] | |
| 828 var data = []; | |
| 829 if (method.aliasName != null) { | |
| 830 data.add(js.quoteName(method.aliasName)); | |
| 831 } | |
| 832 data.add(method.code); | |
| 833 data.add(js.quoteName(method.callName, allowNull: true)); | |
| 834 | |
| 835 if (method.needsTearOff) { | |
| 836 bool isIntercepted = backend.isInterceptedMethod(method.element); | |
| 837 data.add(new js.LiteralBool(isIntercepted)); | |
| 838 data.add(js.quoteName(method.tearOffName)); | |
| 839 data.add((method.functionType)); | |
| 840 } | |
| 841 | |
| 842 data.addAll(method.parameterStubs.expand(makeNameCallNameCodeTriplet)); | |
| 843 if (method.canBeApplied) { | |
| 844 data.add(js.number(method.requiredParameterCount)); | |
| 845 data.add(_encodeOptionalParameterDefaultValues(method)); | |
| 846 } | |
| 847 return [js.quoteName(method.name), new js.ArrayInitializer(data)]; | |
| 848 } else { | |
| 849 // TODO(floitsch): not the most efficient way... | |
| 850 return ([method]..addAll(method.parameterStubs)) | |
| 851 .expand(makeNameCodePair); | |
| 852 } | |
| 853 } else { | |
| 854 return makeNameCodePair(method); | |
| 855 } | |
| 856 } | |
| 857 | |
| 858 Iterable<js.Expression> emitStaticMethod(StaticMethod method) { | |
| 859 js.Expression holderIndex = js.number(method.holder.index); | |
| 860 List<js.Expression> output = <js.Expression>[]; | |
| 861 | |
| 862 void _addMethod(Method method) { | |
| 863 js.Expression unparsed = unparse(compiler, method.code); | |
| 864 output.add(js.quoteName(method.name)); | |
| 865 output.add(holderIndex); | |
| 866 output.add(unparsed); | |
| 867 } | |
| 868 | |
| 869 List<js.Expression> makeNameCallNameCodeTriplet(ParameterStubMethod stub) { | |
| 870 js.Expression callName = stub.callName == null | |
| 871 ? new js.LiteralNull() | |
| 872 : js.quoteName(stub.callName); | |
| 873 return [js.quoteName(stub.name), callName, unparse(compiler, stub.code)]; | |
| 874 } | |
| 875 | |
| 876 _addMethod(method); | |
| 877 // TODO(floitsch): can there be anything else than a StaticDartMethod? | |
| 878 if (method is StaticDartMethod) { | |
| 879 if (method.needsTearOff) { | |
| 880 /// The format emitted is the same as for the parser specified at | |
| 881 /// [parseFunctionDescriptorBoilerplate] except for the missing | |
| 882 /// field whether the method is intercepted. | |
| 883 // [name, [function, callName, tearOffName, functionType, | |
| 884 // stub1_name, stub1_callName, stub1_code, ...] | |
| 885 var data = [unparse(compiler, method.code)]; | |
| 886 data.add(js.quoteName(method.callName)); | |
| 887 data.add(js.quoteName(method.tearOffName)); | |
| 888 data.add(method.functionType); | |
| 889 data.addAll(method.parameterStubs.expand(makeNameCallNameCodeTriplet)); | |
| 890 if (method.canBeApplied) { | |
| 891 data.add(js.number(method.requiredParameterCount)); | |
| 892 data.add(_encodeOptionalParameterDefaultValues(method)); | |
| 893 } | |
| 894 return [js.quoteName(method.name), holderIndex, | |
| 895 new js.ArrayInitializer(data)]; | |
| 896 } else { | |
| 897 method.parameterStubs.forEach(_addMethod); | |
| 898 } | |
| 899 } | |
| 900 return output; | |
| 901 } | |
| 902 | |
| 903 static final String setupProgramName = "setupProgram"; | |
| 904 | |
| 905 static final String boilerplate = """ | |
| 906 { | |
| 907 // Declare deferred-initializer global. | |
| 908 #deferredInitializer; | |
| 909 | |
| 910 (function(start, program) { | |
| 911 // Initialize holder objects. | |
| 912 #holders; | |
| 913 var nativeInfos = Object.create(null); | |
| 914 | |
| 915 // Counter to generate unique names for tear offs. | |
| 916 var functionCounter = 0; | |
| 917 | |
| 918 function $setupProgramName(program, typesOffset) { | |
| 919 for (var i = 0; i < program.length - 2; i++) { | |
| 920 setupLibrary(program[i], typesOffset); | |
| 921 } | |
| 922 setupLazyStatics(program[i]); | |
| 923 setupConstants(program[i + 1]); | |
| 924 } | |
| 925 | |
| 926 function setupLibrary(library, typesOffset) { | |
| 927 var statics = library[0]; | |
| 928 for (var i = 0; i < statics.length; i += 3) { | |
| 929 var holderIndex = statics[i + 1]; | |
| 930 setupStatic(statics[i], holders[holderIndex], statics[i + 2], | |
| 931 typesOffset); | |
| 932 } | |
| 933 | |
| 934 var classes = library[1]; | |
| 935 for (var i = 0; i < classes.length; i += 3) { | |
| 936 var name = classes[i]; | |
| 937 var cls = classes[i + 1]; | |
| 938 | |
| 939 if (#needsNativeSupport) { | |
| 940 // $nativeInfoDescription. | |
| 941 var indexOrNativeInfo = classes[i + 2]; | |
| 942 if (typeof indexOrNativeInfo == "number") { | |
| 943 var holderIndex = classes[i + 2]; | |
| 944 } else { | |
| 945 nativeInfos[name] = indexOrNativeInfo; | |
| 946 holderIndex = classes[i + 3]; | |
| 947 i++; | |
| 948 } | |
| 949 } | |
| 950 | |
| 951 if (#needsNoNativeSupport) { | |
| 952 var holderIndex = classes[i + 2]; | |
| 953 } | |
| 954 | |
| 955 holdersMap[name] = holders[holderIndex]; | |
| 956 setupClass(name, holders[holderIndex], cls, typesOffset); | |
| 957 } | |
| 958 } | |
| 959 | |
| 960 function setupLazyStatics(statics) { | |
| 961 for (var i = 0; i < statics.length; i += 4) { | |
| 962 var name = statics[i]; | |
| 963 var getterName = statics[i + 1]; | |
| 964 var holderIndex = statics[i + 2]; | |
| 965 var initializer = statics[i + 3]; | |
| 966 setupLazyStatic(name, getterName, holders[holderIndex], initializer); | |
| 967 } | |
| 968 } | |
| 969 | |
| 970 function setupConstants(constants) { | |
| 971 // $constantsDescription. | |
| 972 if (constants.length == 0) return; | |
| 973 // We assume that all constants are in the same holder. | |
| 974 var holder = holders[constants[0]]; | |
| 975 for (var i = 1; i < constants.length; i += 2) { | |
| 976 var name = constants[i]; | |
| 977 var initializer = constants[i + 1]; | |
| 978 setupConstant(name, holder, initializer); | |
| 979 } | |
| 980 } | |
| 981 | |
| 982 function setupStatic(name, holder, descriptor, typesOffset) { | |
| 983 if (typeof descriptor == 'string') { | |
| 984 holder[name] = function() { | |
| 985 if (descriptor == null) { | |
| 986 // Already compiled. This happens when we have calls to the static as | |
| 987 // arguments to the static: `foo(foo(499))`; | |
| 988 return holder[name].apply(this, arguments); | |
| 989 } | |
| 990 var method = compile(name, descriptor); | |
| 991 holder[name] = method; | |
| 992 descriptor = null; // GC the descriptor. | |
| 993 return method.apply(this, arguments); | |
| 994 }; | |
| 995 } else { | |
| 996 // Parse the tear off information and generate compile handlers. | |
| 997 // TODO(herhut): Share parser with instance methods. | |
| 998 function compileAllStubs(typesOffset) { | |
| 999 var funs; | |
| 1000 var fun = compile(name, descriptor[0]); | |
| 1001 fun[#callName] = descriptor[1]; | |
| 1002 holder[name] = fun; | |
| 1003 funs = [fun]; | |
| 1004 // We iterate in blocks of 3 but have to stop before we reach the | |
| 1005 // (optional) two trailing items. To accomplish this, we only iterate | |
| 1006 // until we reach length - 2. | |
| 1007 for (var pos = 4; pos < descriptor.length - 2; pos += 3) { | |
| 1008 var stubName = descriptor[pos]; | |
| 1009 fun = compile(stubName, descriptor[pos + 2]); | |
| 1010 fun[#callName] = descriptor[pos + 1]; | |
| 1011 holder[stubName] = fun; | |
| 1012 funs.push(fun); | |
| 1013 } | |
| 1014 if (descriptor[2] != null) { // tear-off name. | |
| 1015 // functions, reflectionInfo, isStatic, name, isIntercepted. | |
| 1016 var reflectionInfo = descriptor[3]; | |
| 1017 if (typeof reflectionInfo == "number") { | |
| 1018 reflectionInfo = reflectionInfo + typesOffset; | |
| 1019 } | |
| 1020 holder[descriptor[2]] = | |
| 1021 tearOff(funs, reflectionInfo, true, name, false); | |
| 1022 } | |
| 1023 if (pos < descriptor.length) { | |
| 1024 fun[#argumentCount] = descriptor[pos]; | |
| 1025 fun[#defaultArgumentValues] = descriptor[pos + 1]; | |
| 1026 } | |
| 1027 } | |
| 1028 | |
| 1029 function setupCompileAllAndDelegateStub(name, typesOffset) { | |
| 1030 holder[name] = function() { | |
| 1031 // The descriptor is null if we already compiled this function. This | |
| 1032 // happens when we have calls to the static as arguments to the | |
| 1033 // static: `foo(foo(499))`; | |
| 1034 if (descriptor != null) { | |
| 1035 compileAllStubs(typesOffset); | |
| 1036 descriptor = null; // GC the descriptor. | |
| 1037 } | |
| 1038 return holder[name].apply(this, arguments); | |
| 1039 }; | |
| 1040 } | |
| 1041 | |
| 1042 setupCompileAllAndDelegateStub(name, typesOffset); | |
| 1043 for (var pos = 4; pos < descriptor.length; pos += 3) { | |
| 1044 setupCompileAllAndDelegateStub(descriptor[pos], typesOffset); | |
| 1045 } | |
| 1046 if (descriptor[2] != null) { // tear-off name. | |
| 1047 setupCompileAllAndDelegateStub(descriptor[2], typesOffset) | |
| 1048 } | |
| 1049 } | |
| 1050 } | |
| 1051 | |
| 1052 function setupLazyStatic(name, getterName, holder, descriptor) { | |
| 1053 holder[name] = null; | |
| 1054 holder[getterName] = function() { | |
| 1055 var initializer = compile(name, descriptor); | |
| 1056 holder[getterName] = function() { #cyclicThrow(name) }; | |
| 1057 var result; | |
| 1058 var sentinelInProgress = descriptor; | |
| 1059 try { | |
| 1060 result = holder[name] = sentinelInProgress; | |
| 1061 result = holder[name] = initializer(); | |
| 1062 } finally { | |
| 1063 // Use try-finally, not try-catch/throw as it destroys the stack trace. | |
| 1064 if (result === sentinelInProgress) { | |
| 1065 // The lazy static (holder[name]) might have been set to a different | |
| 1066 // value. According to spec we still have to reset it to null, if the | |
| 1067 // initialization failed. | |
| 1068 holder[name] = null; | |
| 1069 } | |
| 1070 // TODO(floitsch): the function should probably be unique for each | |
| 1071 // static. | |
| 1072 holder[getterName] = function() { return this[name]; }; | |
| 1073 } | |
| 1074 return result; | |
| 1075 }; | |
| 1076 } | |
| 1077 | |
| 1078 function setupConstant(name, holder, descriptor) { | |
| 1079 var c; | |
| 1080 holder[name] = function() { | |
| 1081 if (descriptor !== null) { | |
| 1082 c = compile(name, descriptor); | |
| 1083 name = null; | |
| 1084 descriptor = null; | |
| 1085 } | |
| 1086 return c; | |
| 1087 }; | |
| 1088 } | |
| 1089 | |
| 1090 function setupClass(name, holder, descriptor, typesOffset) { | |
| 1091 var patch = function() { | |
| 1092 if (patch.ensureResolved == patch) { | |
| 1093 // We have not yet been compiled. | |
| 1094 var constructor = compileConstructor(name, descriptor, typesOffset); | |
| 1095 holder[name] = constructor; | |
| 1096 name = holder = descriptor = null; // GC the captured arguments. | |
| 1097 // Make sure we can invoke 'ensureResolved' multiple times on the patch | |
| 1098 // function. | |
| 1099 patch.ensureResolved = function() { return constructor; }; | |
| 1100 constructor.ensureResolved = function() { return this; }; | |
| 1101 } else { | |
| 1102 // This can happen when arguments to the constructor are of the same | |
| 1103 // class, like in `new A(new A(null))`. | |
| 1104 constructor = patch.ensureResolved(); | |
| 1105 } | |
| 1106 // If the patch has been called as "ensureResolved" return. | |
| 1107 if (this === patch) return constructor; | |
| 1108 var object = new constructor(); | |
| 1109 constructor.apply(object, arguments); | |
| 1110 return object; | |
| 1111 }; | |
| 1112 | |
| 1113 // We store the patch function on itself to make it | |
| 1114 // possible to resolve superclass references without constructing instances. | |
| 1115 patch.ensureResolved = patch; | |
| 1116 holder[name] = patch; | |
| 1117 } | |
| 1118 | |
| 1119 #tearOff; | |
| 1120 | |
| 1121 #parseFunctionDescriptor; | |
| 1122 | |
| 1123 function compileConstructor(name, descriptor, typesOffset) { | |
| 1124 descriptor = compile(name, descriptor); | |
| 1125 var prototype = determinePrototype(descriptor); | |
| 1126 var constructor; | |
| 1127 var functionsIndex; | |
| 1128 // $mixinFormatDescription. | |
| 1129 if (typeof descriptor[2] !== 'function') { | |
| 1130 fillPrototypeWithMixedIn(descriptor[2], descriptor[3], prototype); | |
| 1131 // descriptor[4] contains the constructor if the mixin application is | |
| 1132 // directly instantiated. | |
| 1133 if (typeof descriptor[4] === 'function') { | |
| 1134 constructor = descriptor[4]; | |
| 1135 functionsIndex = 5; | |
| 1136 } else { | |
| 1137 constructor = function() {}; | |
| 1138 functionsIndex = 4; | |
| 1139 } | |
| 1140 } else { | |
| 1141 constructor = descriptor[2]; | |
| 1142 functionsIndex = 3; | |
| 1143 } | |
| 1144 | |
| 1145 for (var i = functionsIndex; i < descriptor.length; i += 2) { | |
| 1146 parseFunctionDescriptor(prototype, descriptor[i], descriptor[i + 1], | |
| 1147 typesOffset); | |
| 1148 } | |
| 1149 | |
| 1150 constructor.$typeNameProperty = name; // Needed for RTI. | |
| 1151 constructor.prototype = prototype; | |
| 1152 prototype[#operatorIsPrefix + name] = constructor; | |
| 1153 prototype.constructor = constructor; | |
| 1154 return constructor; | |
| 1155 } | |
| 1156 | |
| 1157 function fillPrototypeWithMixedIn(mixinName, mixinHolderIndex, prototype) { | |
| 1158 var mixin = holders[mixinHolderIndex][mixinName].ensureResolved(); | |
| 1159 var mixinPrototype = mixin.prototype; | |
| 1160 | |
| 1161 // Fill the prototype with the mixin's properties. | |
| 1162 var mixinProperties = Object.keys(mixinPrototype); | |
| 1163 for (var i = 0; i < mixinProperties.length; i++) { | |
| 1164 var p = mixinProperties[i]; | |
| 1165 prototype[p] = mixinPrototype[p]; | |
| 1166 } | |
| 1167 } | |
| 1168 | |
| 1169 function determinePrototype(descriptor) { | |
| 1170 var superclassName = descriptor[0]; | |
| 1171 if (!superclassName) return { }; | |
| 1172 | |
| 1173 // Look up the superclass constructor function in the right holder. | |
| 1174 var holderIndex = descriptor[1]; | |
| 1175 var superclass = holders[holderIndex][superclassName].ensureResolved(); | |
| 1176 | |
| 1177 // Create a new prototype object chained to the superclass prototype. | |
| 1178 var intermediate = function() { }; | |
| 1179 intermediate.prototype = superclass.prototype; | |
| 1180 return new intermediate(); | |
| 1181 } | |
| 1182 | |
| 1183 function compile(__name__, __s__) { | |
| 1184 'use strict'; | |
| 1185 // TODO(floitsch): evaluate the performance impact of the string | |
| 1186 // concatenations. | |
| 1187 return eval(__s__ + "\\n//# sourceURL=" + __name__ + ".js"); | |
| 1188 } | |
| 1189 | |
| 1190 if (#outputContainsConstantList) { | |
| 1191 function makeConstList(list) { | |
| 1192 // By assigning a function to the properties they become part of the | |
| 1193 // hidden class. The actual values of the fields don't matter, since we | |
| 1194 // only check if they exist. | |
| 1195 list.immutable\$list = Array; | |
| 1196 list.fixed\$length = Array; | |
| 1197 return list; | |
| 1198 } | |
| 1199 } | |
| 1200 | |
| 1201 if (#needsNativeSupport) { | |
| 1202 function handleNativeClassInfos() { | |
| 1203 for (var nativeClass in nativeInfos) { | |
| 1204 var constructor = holdersMap[nativeClass][nativeClass].ensureResolved(); | |
| 1205 var nativeInfo = nativeInfos[nativeClass]; | |
| 1206 #nativeInfoHandler; | |
| 1207 } | |
| 1208 } | |
| 1209 } | |
| 1210 | |
| 1211 $setupProgramName(program, 0); | |
| 1212 | |
| 1213 // Initialize globals. | |
| 1214 #embeddedGlobals; | |
| 1215 | |
| 1216 function expressionCompile(__s__) { | |
| 1217 'use strict'; | |
| 1218 return eval('(' + __s__ + ')'); | |
| 1219 } | |
| 1220 | |
| 1221 #readMetadataTypeFunction; | |
| 1222 | |
| 1223 // TODO(floitsch): this order means that native classes may not be | |
| 1224 // referenced from constants. I'm mostly afraid of things like using them as | |
| 1225 // generic arguments (which should be fine, but maybe there are other | |
| 1226 // similar things). | |
| 1227 // Initialize natives. | |
| 1228 if (#needsNativeSupport) handleNativeClassInfos(); | |
| 1229 | |
| 1230 // Initialize static non-final fields. | |
| 1231 #staticNonFinals; | |
| 1232 | |
| 1233 // Add native boilerplate code. | |
| 1234 #nativeIsolateAffinityTagInitialization; | |
| 1235 | |
| 1236 // Initialize eager classes. | |
| 1237 #eagerClasses; | |
| 1238 | |
| 1239 var end = Date.now(); | |
| 1240 // print('Setup: ' + (end - start) + ' ms.'); | |
| 1241 | |
| 1242 #invokeMain; // Start main. | |
| 1243 | |
| 1244 })(Date.now(), #code) | |
| 1245 }"""; | |
| 1246 | |
| 1247 } | |
| OLD | NEW |