| 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.js_emitter.program_builder; | |
| 6 | |
| 7 import 'js_emitter.dart' show computeMixinClass; | |
| 8 import 'model.dart'; | |
| 9 | |
| 10 import '../common.dart'; | |
| 11 import '../js/js.dart' as js; | |
| 12 | |
| 13 import '../js_backend/js_backend.dart' show | |
| 14 Namer, | |
| 15 JavaScriptBackend, | |
| 16 JavaScriptConstantCompiler; | |
| 17 | |
| 18 import 'js_emitter.dart' show | |
| 19 ClassStubGenerator, | |
| 20 CodeEmitterTask, | |
| 21 InterceptorStubGenerator, | |
| 22 MainCallStubGenerator, | |
| 23 ParameterStubGenerator, | |
| 24 RuntimeTypeGenerator, | |
| 25 TypeTestProperties; | |
| 26 | |
| 27 import '../elements/elements.dart' show ParameterElement, MethodElement; | |
| 28 | |
| 29 import '../universe/universe.dart' show Universe, TypeMaskSet; | |
| 30 import '../deferred_load.dart' show DeferredLoadTask, OutputUnit; | |
| 31 | |
| 32 part 'registry.dart'; | |
| 33 | |
| 34 class ProgramBuilder { | |
| 35 final Compiler _compiler; | |
| 36 final Namer namer; | |
| 37 final CodeEmitterTask _task; | |
| 38 | |
| 39 final Registry _registry; | |
| 40 | |
| 41 /// True if the program should store function types in the metadata. | |
| 42 bool _storeFunctionTypesInMetadata = false; | |
| 43 | |
| 44 ProgramBuilder(Compiler compiler, | |
| 45 this.namer, | |
| 46 this._task) | |
| 47 : this._compiler = compiler, | |
| 48 this._registry = new Registry(compiler); | |
| 49 | |
| 50 JavaScriptBackend get backend => _compiler.backend; | |
| 51 Universe get universe => _compiler.codegenWorld; | |
| 52 | |
| 53 /// Mapping from [ClassElement] to constructed [Class]. We need this to | |
| 54 /// update the superclass in the [Class]. | |
| 55 final Map<ClassElement, Class> _classes = <ClassElement, Class>{}; | |
| 56 | |
| 57 /// Mapping from [OutputUnit] to constructed [Fragment]. We need this to | |
| 58 /// generate the deferredLoadingMap (to know which hunks to load). | |
| 59 final Map<OutputUnit, Fragment> _outputs = <OutputUnit, Fragment>{}; | |
| 60 | |
| 61 /// Mapping from [ConstantValue] to constructed [Constant]. We need this to | |
| 62 /// update field-initializers to point to the ConstantModel. | |
| 63 final Map<ConstantValue, Constant> _constants = <ConstantValue, Constant>{}; | |
| 64 | |
| 65 Set<Class> _unneededNativeClasses; | |
| 66 | |
| 67 Program buildProgram({bool storeFunctionTypesInMetadata: false}) { | |
| 68 this._storeFunctionTypesInMetadata = storeFunctionTypesInMetadata; | |
| 69 // Note: In rare cases (mostly tests) output units can be empty. This | |
| 70 // happens when the deferred code is dead-code eliminated but we still need | |
| 71 // to check that the library has been loaded. | |
| 72 _compiler.deferredLoadTask.allOutputUnits.forEach( | |
| 73 _registry.registerOutputUnit); | |
| 74 _task.outputClassLists.forEach(_registry.registerElements); | |
| 75 _task.outputStaticLists.forEach(_registry.registerElements); | |
| 76 _task.outputConstantLists.forEach(_registerConstants); | |
| 77 _task.outputStaticNonFinalFieldLists.forEach(_registry.registerElements); | |
| 78 | |
| 79 // TODO(kasperl): There's code that implicitly needs access to the special | |
| 80 // $ holder so we have to register that. Can we track if we have to? | |
| 81 _registry.registerHolder(r'$'); | |
| 82 | |
| 83 // We need to run the native-preparation before we build the output. The | |
| 84 // preparation code, in turn needs the classes to be set up. | |
| 85 // We thus build the classes before building their containers. | |
| 86 _task.outputClassLists.forEach((OutputUnit _, List<ClassElement> classes) { | |
| 87 classes.forEach(_buildClass); | |
| 88 }); | |
| 89 | |
| 90 // Resolve the superclass references after we've processed all the classes. | |
| 91 _classes.forEach((ClassElement element, Class c) { | |
| 92 if (element.superclass != null) { | |
| 93 c.setSuperclass(_classes[element.superclass]); | |
| 94 assert(c.superclass != null); | |
| 95 } | |
| 96 if (c is MixinApplication) { | |
| 97 c.setMixinClass(_classes[computeMixinClass(element)]); | |
| 98 assert(c.mixinClass != null); | |
| 99 } | |
| 100 }); | |
| 101 | |
| 102 List<Class> nativeClasses = _task.nativeClassesAndSubclasses | |
| 103 .map((ClassElement classElement) => _classes[classElement]) | |
| 104 .toList(); | |
| 105 | |
| 106 _unneededNativeClasses = | |
| 107 _task.nativeEmitter.prepareNativeClasses(nativeClasses); | |
| 108 | |
| 109 MainFragment mainFragment = _buildMainFragment(_registry.mainLibrariesMap); | |
| 110 Iterable<Fragment> deferredFragments = | |
| 111 _registry.deferredLibrariesMap.map(_buildDeferredFragment); | |
| 112 | |
| 113 List<Fragment> fragments = new List<Fragment>(_registry.librariesMapCount); | |
| 114 fragments[0] = mainFragment; | |
| 115 fragments.setAll(1, deferredFragments); | |
| 116 | |
| 117 _markEagerClasses(); | |
| 118 | |
| 119 List<Holder> holders = _registry.holders.toList(growable: false); | |
| 120 | |
| 121 bool needsNativeSupport = _compiler.enqueuer.codegen.nativeEnqueuer | |
| 122 .hasInstantiatedNativeClasses(); | |
| 123 | |
| 124 assert(!needsNativeSupport || nativeClasses.isNotEmpty); | |
| 125 | |
| 126 List<js.TokenFinalizer> finalizers = [_task.metadataCollector]; | |
| 127 if (backend.namer is js.TokenFinalizer) { | |
| 128 var namingFinalizer = backend.namer; | |
| 129 finalizers.add(namingFinalizer); | |
| 130 } | |
| 131 | |
| 132 return new Program( | |
| 133 fragments, | |
| 134 holders, | |
| 135 _buildLoadMap(), | |
| 136 _buildTypeToInterceptorMap(), | |
| 137 _task.metadataCollector, | |
| 138 finalizers, | |
| 139 needsNativeSupport: needsNativeSupport, | |
| 140 outputContainsConstantList: _task.outputContainsConstantList, | |
| 141 hasIsolateSupport: _compiler.hasIsolateSupport); | |
| 142 } | |
| 143 | |
| 144 void _markEagerClasses() { | |
| 145 _markEagerInterceptorClasses(); | |
| 146 } | |
| 147 | |
| 148 /// Builds a map from loadId to outputs-to-load. | |
| 149 Map<String, List<Fragment>> _buildLoadMap() { | |
| 150 Map<String, List<Fragment>> loadMap = <String, List<Fragment>>{}; | |
| 151 _compiler.deferredLoadTask.hunksToLoad | |
| 152 .forEach((String loadId, List<OutputUnit> outputUnits) { | |
| 153 loadMap[loadId] = outputUnits | |
| 154 .map((OutputUnit unit) => _outputs[unit]) | |
| 155 .toList(growable: false); | |
| 156 }); | |
| 157 return loadMap; | |
| 158 } | |
| 159 | |
| 160 js.Expression _buildTypeToInterceptorMap() { | |
| 161 InterceptorStubGenerator stubGenerator = | |
| 162 new InterceptorStubGenerator(_compiler, namer, backend); | |
| 163 return stubGenerator.generateTypeToInterceptorMap(); | |
| 164 } | |
| 165 | |
| 166 MainFragment _buildMainFragment(LibrariesMap librariesMap) { | |
| 167 // Construct the main output from the libraries and the registered holders. | |
| 168 MainFragment result = new MainFragment( | |
| 169 librariesMap.outputUnit, | |
| 170 "", // The empty string is the name for the main output file. | |
| 171 _buildInvokeMain(), | |
| 172 _buildLibraries(librariesMap), | |
| 173 _buildStaticNonFinalFields(librariesMap), | |
| 174 _buildStaticLazilyInitializedFields(librariesMap), | |
| 175 _buildConstants(librariesMap)); | |
| 176 _outputs[librariesMap.outputUnit] = result; | |
| 177 return result; | |
| 178 } | |
| 179 | |
| 180 js.Statement _buildInvokeMain() { | |
| 181 MainCallStubGenerator generator = | |
| 182 new MainCallStubGenerator(_compiler, backend, backend.emitter); | |
| 183 return generator.generateInvokeMain(); | |
| 184 } | |
| 185 | |
| 186 DeferredFragment _buildDeferredFragment(LibrariesMap librariesMap) { | |
| 187 DeferredFragment result = new DeferredFragment( | |
| 188 librariesMap.outputUnit, | |
| 189 backend.deferredPartFileName(librariesMap.name, addExtension: false), | |
| 190 librariesMap.name, | |
| 191 _buildLibraries(librariesMap), | |
| 192 _buildStaticNonFinalFields(librariesMap), | |
| 193 _buildStaticLazilyInitializedFields(librariesMap), | |
| 194 _buildConstants(librariesMap)); | |
| 195 _outputs[librariesMap.outputUnit] = result; | |
| 196 return result; | |
| 197 } | |
| 198 | |
| 199 List<Constant> _buildConstants(LibrariesMap librariesMap) { | |
| 200 List<ConstantValue> constantValues = | |
| 201 _task.outputConstantLists[librariesMap.outputUnit]; | |
| 202 if (constantValues == null) return const <Constant>[]; | |
| 203 return constantValues.map((ConstantValue value) => _constants[value]) | |
| 204 .toList(growable: false); | |
| 205 } | |
| 206 | |
| 207 List<StaticField> _buildStaticNonFinalFields(LibrariesMap librariesMap) { | |
| 208 List<VariableElement> staticNonFinalFields = | |
| 209 _task.outputStaticNonFinalFieldLists[librariesMap.outputUnit]; | |
| 210 if (staticNonFinalFields == null) return const <StaticField>[]; | |
| 211 | |
| 212 return staticNonFinalFields | |
| 213 .map(_buildStaticField) | |
| 214 .toList(growable: false); | |
| 215 } | |
| 216 | |
| 217 StaticField _buildStaticField(Element element) { | |
| 218 JavaScriptConstantCompiler handler = backend.constants; | |
| 219 ConstantValue initialValue = handler.getInitialValueFor(element); | |
| 220 // TODO(zarah): The holder should not be registered during building of | |
| 221 // a static field. | |
| 222 _registry.registerHolder(namer.globalObjectForConstant(initialValue)); | |
| 223 js.Expression code = _task.emitter.constantReference(initialValue); | |
| 224 js.Name name = namer.globalPropertyName(element); | |
| 225 bool isFinal = false; | |
| 226 bool isLazy = false; | |
| 227 | |
| 228 // TODO(floitsch): we shouldn't update the registry in the middle of | |
| 229 // building a static field. (Note that the $ holder is already registered | |
| 230 // earlier). | |
| 231 return new StaticField(element, | |
| 232 name, _registry.registerHolder(r'$'), code, | |
| 233 isFinal, isLazy); | |
| 234 } | |
| 235 | |
| 236 List<StaticField> _buildStaticLazilyInitializedFields( | |
| 237 LibrariesMap librariesMap) { | |
| 238 // TODO(floitsch): lazy fields should just be in their respective | |
| 239 // libraries. | |
| 240 if (librariesMap != _registry.mainLibrariesMap) { | |
| 241 return const <StaticField>[]; | |
| 242 } | |
| 243 | |
| 244 JavaScriptConstantCompiler handler = backend.constants; | |
| 245 List<VariableElement> lazyFields = | |
| 246 handler.getLazilyInitializedFieldsForEmission(); | |
| 247 return Elements.sortedByPosition(lazyFields) | |
| 248 .map(_buildLazyField) | |
| 249 .where((field) => field != null) // Happens when the field was unused. | |
| 250 .toList(growable: false); | |
| 251 } | |
| 252 | |
| 253 StaticField _buildLazyField(Element element) { | |
| 254 js.Expression code = backend.generatedCode[element]; | |
| 255 // The code is null if we ended up not needing the lazily | |
| 256 // initialized field after all because of constant folding | |
| 257 // before code generation. | |
| 258 if (code == null) return null; | |
| 259 | |
| 260 js.Name name = namer.globalPropertyName(element); | |
| 261 bool isFinal = element.isFinal; | |
| 262 bool isLazy = true; | |
| 263 // TODO(floitsch): we shouldn't update the registry in the middle of | |
| 264 // building a static field. (Note that the $ holder is already registered | |
| 265 // earlier). | |
| 266 return new StaticField(element, | |
| 267 name, _registry.registerHolder(r'$'), code, | |
| 268 isFinal, isLazy); | |
| 269 } | |
| 270 | |
| 271 List<Library> _buildLibraries(LibrariesMap librariesMap) { | |
| 272 List<Library> libraries = new List<Library>(librariesMap.length); | |
| 273 int count = 0; | |
| 274 librariesMap.forEach((LibraryElement library, List<Element> elements) { | |
| 275 libraries[count++] = _buildLibrary(library, elements); | |
| 276 }); | |
| 277 return libraries; | |
| 278 } | |
| 279 | |
| 280 // Note that a library-element may have multiple [Library]s, if it is split | |
| 281 // into multiple output units. | |
| 282 Library _buildLibrary(LibraryElement library, List<Element> elements) { | |
| 283 String uri = library.canonicalUri.toString(); | |
| 284 | |
| 285 List<StaticMethod> statics = elements | |
| 286 .where((e) => e is FunctionElement) | |
| 287 .map(_buildStaticMethod) | |
| 288 .toList(); | |
| 289 | |
| 290 if (library == backend.interceptorsLibrary) { | |
| 291 statics.addAll(_generateGetInterceptorMethods()); | |
| 292 statics.addAll(_generateOneShotInterceptors()); | |
| 293 } | |
| 294 | |
| 295 List<Class> classes = elements | |
| 296 .where((e) => e is ClassElement) | |
| 297 .map((ClassElement classElement) => _classes[classElement]) | |
| 298 .where((Class cls) => | |
| 299 !cls.isNative || !_unneededNativeClasses.contains(cls)) | |
| 300 .toList(growable: false); | |
| 301 | |
| 302 bool visitStatics = true; | |
| 303 List<Field> staticFieldsForReflection = _buildFields(library, visitStatics); | |
| 304 | |
| 305 return new Library(library, uri, statics, classes, | |
| 306 staticFieldsForReflection); | |
| 307 } | |
| 308 | |
| 309 /// HACK for Incremental Compilation. | |
| 310 /// | |
| 311 /// Returns a class that contains the fields of a class. | |
| 312 Class buildFieldsHackForIncrementalCompilation(ClassElement element) { | |
| 313 assert(_compiler.hasIncrementalSupport); | |
| 314 | |
| 315 List<Field> instanceFields = _buildFields(element, false); | |
| 316 js.Name name = namer.className(element); | |
| 317 | |
| 318 return new Class( | |
| 319 element, name, null, [], instanceFields, [], [], [], [], [], null, | |
| 320 isDirectlyInstantiated: true, | |
| 321 onlyForRti: false, | |
| 322 isNative: element.isNative); | |
| 323 } | |
| 324 | |
| 325 Class _buildClass(ClassElement element) { | |
| 326 bool onlyForRti = _task.typeTestRegistry.rtiNeededClasses.contains(element); | |
| 327 | |
| 328 List<Method> methods = []; | |
| 329 List<StubMethod> callStubs = <StubMethod>[]; | |
| 330 | |
| 331 ClassStubGenerator classStubGenerator = | |
| 332 new ClassStubGenerator(_compiler, namer, backend); | |
| 333 RuntimeTypeGenerator runtimeTypeGenerator = | |
| 334 new RuntimeTypeGenerator(_compiler, _task, namer); | |
| 335 | |
| 336 void visitMember(ClassElement enclosing, Element member) { | |
| 337 assert(invariant(element, member.isDeclaration)); | |
| 338 assert(invariant(element, element == enclosing)); | |
| 339 | |
| 340 if (Elements.isNonAbstractInstanceMember(member)) { | |
| 341 // TODO(herhut): Remove once _buildMethod can no longer return null. | |
| 342 Method method = _buildMethod(member); | |
| 343 if (method != null) methods.add(method); | |
| 344 } | |
| 345 if (member.isGetter || member.isField) { | |
| 346 Map<Selector, TypeMaskSet> selectors = | |
| 347 _compiler.codegenWorld.invocationsByName(member.name); | |
| 348 if (selectors != null && !selectors.isEmpty) { | |
| 349 | |
| 350 Map<js.Name, js.Expression> callStubsForMember = | |
| 351 classStubGenerator.generateCallStubsForGetter(member, selectors); | |
| 352 callStubsForMember.forEach((js.Name name, js.Expression code) { | |
| 353 callStubs.add(_buildStubMethod(name, code, element: member)); | |
| 354 }); | |
| 355 } | |
| 356 } | |
| 357 } | |
| 358 | |
| 359 List<StubMethod> typeVariableReaderStubs = | |
| 360 runtimeTypeGenerator.generateTypeVariableReaderStubs(element); | |
| 361 | |
| 362 List<StubMethod> noSuchMethodStubs = <StubMethod>[]; | |
| 363 if (backend.enabledNoSuchMethod && element == _compiler.objectClass) { | |
| 364 Map<js.Name, Selector> selectors = | |
| 365 classStubGenerator.computeSelectorsForNsmHandlers(); | |
| 366 selectors.forEach((js.Name name, Selector selector) { | |
| 367 noSuchMethodStubs | |
| 368 .add(classStubGenerator.generateStubForNoSuchMethod(name, | |
| 369 selector)); | |
| 370 }); | |
| 371 } | |
| 372 | |
| 373 if (element == backend.closureClass) { | |
| 374 // We add a special getter here to allow for tearing off a closure from | |
| 375 // itself. | |
| 376 js.Name name = namer.getterForMember(Selector.CALL_NAME); | |
| 377 js.Fun function = js.js('function() { return this; }'); | |
| 378 callStubs.add(_buildStubMethod(name, function)); | |
| 379 } | |
| 380 | |
| 381 ClassElement implementation = element.implementation; | |
| 382 | |
| 383 // MixinApplications run through the members of their mixin. Here, we are | |
| 384 // only interested in direct members. | |
| 385 if (!onlyForRti && !element.isMixinApplication) { | |
| 386 implementation.forEachMember(visitMember, includeBackendMembers: true); | |
| 387 } | |
| 388 | |
| 389 List<Field> instanceFields = | |
| 390 onlyForRti ? const <Field>[] : _buildFields(element, false); | |
| 391 List<Field> staticFieldsForReflection = | |
| 392 onlyForRti ? const <Field>[] : _buildFields(element, true); | |
| 393 | |
| 394 TypeTestProperties typeTests = | |
| 395 runtimeTypeGenerator.generateIsTests( | |
| 396 element, | |
| 397 storeFunctionTypeInMetadata: _storeFunctionTypesInMetadata); | |
| 398 | |
| 399 List<StubMethod> isChecks = <StubMethod>[]; | |
| 400 typeTests.properties.forEach((js.Name name, js.Node code) { | |
| 401 isChecks.add(_buildStubMethod(name, code)); | |
| 402 }); | |
| 403 | |
| 404 js.Name name = namer.className(element); | |
| 405 String holderName = namer.globalObjectFor(element); | |
| 406 // TODO(floitsch): we shouldn't update the registry in the middle of | |
| 407 // building a class. | |
| 408 Holder holder = _registry.registerHolder(holderName); | |
| 409 bool isInstantiated = | |
| 410 _compiler.codegenWorld.directlyInstantiatedClasses.contains(element); | |
| 411 | |
| 412 Class result; | |
| 413 if (element.isMixinApplication && !onlyForRti) { | |
| 414 assert(!element.isNative); | |
| 415 assert(methods.isEmpty); | |
| 416 | |
| 417 result = new MixinApplication(element, | |
| 418 name, holder, | |
| 419 instanceFields, | |
| 420 staticFieldsForReflection, | |
| 421 callStubs, | |
| 422 typeVariableReaderStubs, | |
| 423 isChecks, | |
| 424 typeTests.functionTypeIndex, | |
| 425 isDirectlyInstantiated: isInstantiated, | |
| 426 onlyForRti: onlyForRti); | |
| 427 } else { | |
| 428 result = new Class(element, | |
| 429 name, holder, methods, instanceFields, | |
| 430 staticFieldsForReflection, | |
| 431 callStubs, | |
| 432 typeVariableReaderStubs, | |
| 433 noSuchMethodStubs, | |
| 434 isChecks, | |
| 435 typeTests.functionTypeIndex, | |
| 436 isDirectlyInstantiated: isInstantiated, | |
| 437 onlyForRti: onlyForRti, | |
| 438 isNative: element.isNative); | |
| 439 } | |
| 440 _classes[element] = result; | |
| 441 return result; | |
| 442 } | |
| 443 | |
| 444 bool _methodNeedsStubs(FunctionElement method) { | |
| 445 return !method.functionSignature.optionalParameters.isEmpty; | |
| 446 } | |
| 447 | |
| 448 bool _methodCanBeReflected(FunctionElement method) { | |
| 449 return backend.isAccessibleByReflection(method) || | |
| 450 // During incremental compilation, we have to assume that reflection | |
| 451 // *might* get enabled. | |
| 452 _compiler.hasIncrementalSupport; | |
| 453 } | |
| 454 | |
| 455 bool _methodCanBeApplied(FunctionElement method) { | |
| 456 return _compiler.enabledFunctionApply && | |
| 457 _compiler.world.getMightBePassedToApply(method); | |
| 458 } | |
| 459 | |
| 460 // TODO(herhut): Refactor incremental compilation and remove method. | |
| 461 Method buildMethodHackForIncrementalCompilation(FunctionElement element) { | |
| 462 assert(_compiler.hasIncrementalSupport); | |
| 463 if (element.isInstanceMember) { | |
| 464 return _buildMethod(element); | |
| 465 } else { | |
| 466 return _buildStaticMethod(element); | |
| 467 } | |
| 468 } | |
| 469 | |
| 470 /* Map | List */ _computeParameterDefaultValues(FunctionSignature signature) { | |
| 471 var /* Map | List */ optionalParameterDefaultValues; | |
| 472 if (signature.optionalParametersAreNamed) { | |
| 473 optionalParameterDefaultValues = new Map<String, ConstantValue>(); | |
| 474 signature.forEachOptionalParameter((ParameterElement parameter) { | |
| 475 ConstantValue def = | |
| 476 backend.constants.getConstantValueForVariable(parameter); | |
| 477 optionalParameterDefaultValues[parameter.name] = def; | |
| 478 }); | |
| 479 } else { | |
| 480 optionalParameterDefaultValues = <ConstantValue>[]; | |
| 481 signature.forEachOptionalParameter((ParameterElement parameter) { | |
| 482 ConstantValue def = | |
| 483 backend.constants.getConstantValueForVariable(parameter); | |
| 484 optionalParameterDefaultValues.add(def); | |
| 485 }); | |
| 486 } | |
| 487 return optionalParameterDefaultValues; | |
| 488 } | |
| 489 | |
| 490 DartMethod _buildMethod(MethodElement element) { | |
| 491 js.Name name = namer.methodPropertyName(element); | |
| 492 js.Expression code = backend.generatedCode[element]; | |
| 493 | |
| 494 // TODO(kasperl): Figure out under which conditions code is null. | |
| 495 if (code == null) return null; | |
| 496 | |
| 497 bool canTearOff = false; | |
| 498 js.Name tearOffName; | |
| 499 bool isClosure = false; | |
| 500 bool isNotApplyTarget = !element.isFunction || element.isAccessor; | |
| 501 | |
| 502 bool canBeReflected = _methodCanBeReflected(element); | |
| 503 bool canBeApplied = _methodCanBeApplied(element); | |
| 504 | |
| 505 js.Name aliasName = backend.isAliasedSuperMember(element) | |
| 506 ? namer.aliasedSuperMemberPropertyName(element) | |
| 507 : null; | |
| 508 | |
| 509 if (isNotApplyTarget) { | |
| 510 canTearOff = false; | |
| 511 } else { | |
| 512 if (element.enclosingClass.isClosure) { | |
| 513 canTearOff = false; | |
| 514 isClosure = true; | |
| 515 } else { | |
| 516 // Careful with operators. | |
| 517 canTearOff = universe.hasInvokedGetter(element, _compiler.world) || | |
| 518 (canBeReflected && !element.isOperator); | |
| 519 assert(canTearOff || | |
| 520 !universe.methodsNeedingSuperGetter.contains(element)); | |
| 521 tearOffName = namer.getterForElement(element); | |
| 522 } | |
| 523 } | |
| 524 | |
| 525 if (canTearOff) { | |
| 526 assert(invariant(element, !element.isGenerativeConstructor)); | |
| 527 assert(invariant(element, !element.isGenerativeConstructorBody)); | |
| 528 assert(invariant(element, !element.isConstructor)); | |
| 529 } | |
| 530 | |
| 531 js.Name callName = null; | |
| 532 if (canTearOff) { | |
| 533 Selector callSelector = | |
| 534 new Selector.fromElement(element).toCallSelector(); | |
| 535 callName = namer.invocationName(callSelector); | |
| 536 } | |
| 537 | |
| 538 DartType memberType; | |
| 539 if (element.isGenerativeConstructorBody) { | |
| 540 // TODO(herhut): Why does this need to be normalized away? We never need | |
| 541 // this information anyway as they cannot be torn off or | |
| 542 // reflected. | |
| 543 var body = element; | |
| 544 memberType = body.constructor.type; | |
| 545 } else { | |
| 546 memberType = element.type; | |
| 547 } | |
| 548 | |
| 549 js.Expression functionType; | |
| 550 if (canTearOff || canBeReflected) { | |
| 551 OutputUnit outputUnit = | |
| 552 _compiler.deferredLoadTask.outputUnitForElement(element); | |
| 553 functionType = _generateFunctionType(memberType, outputUnit); | |
| 554 } | |
| 555 | |
| 556 int requiredParameterCount; | |
| 557 var /* List | Map */ optionalParameterDefaultValues; | |
| 558 if (canBeApplied || canBeReflected) { | |
| 559 FunctionSignature signature = element.functionSignature; | |
| 560 requiredParameterCount = signature.requiredParameterCount; | |
| 561 optionalParameterDefaultValues = | |
| 562 _computeParameterDefaultValues(signature); | |
| 563 } | |
| 564 | |
| 565 return new InstanceMethod(element, name, code, | |
| 566 _generateParameterStubs(element, canTearOff), callName, | |
| 567 needsTearOff: canTearOff, tearOffName: tearOffName, | |
| 568 isClosure: isClosure, aliasName: aliasName, | |
| 569 canBeApplied: canBeApplied, canBeReflected: canBeReflected, | |
| 570 requiredParameterCount: requiredParameterCount, | |
| 571 optionalParameterDefaultValues: optionalParameterDefaultValues, | |
| 572 functionType: functionType); | |
| 573 } | |
| 574 | |
| 575 js.Expression _generateFunctionType(DartType type, OutputUnit outputUnit) { | |
| 576 if (type.containsTypeVariables) { | |
| 577 js.Expression thisAccess = js.js(r'this.$receiver'); | |
| 578 return backend.rti.getSignatureEncoding(type, thisAccess); | |
| 579 } else { | |
| 580 return backend.emitter.metadataCollector | |
| 581 .reifyTypeForOutputUnit(type, outputUnit); | |
| 582 } | |
| 583 } | |
| 584 | |
| 585 List<ParameterStubMethod> _generateParameterStubs(MethodElement element, | |
| 586 bool canTearOff) { | |
| 587 | |
| 588 if (!_methodNeedsStubs(element)) return const <ParameterStubMethod>[]; | |
| 589 | |
| 590 ParameterStubGenerator generator = | |
| 591 new ParameterStubGenerator(_compiler, namer, backend); | |
| 592 return generator.generateParameterStubs(element, canTearOff: canTearOff); | |
| 593 } | |
| 594 | |
| 595 /// Builds a stub method. | |
| 596 /// | |
| 597 /// Stub methods may have an element that can be used for code-size | |
| 598 /// attribution. | |
| 599 Method _buildStubMethod(js.Name name, js.Expression code, | |
| 600 {Element element}) { | |
| 601 return new StubMethod(name, code, element: element); | |
| 602 } | |
| 603 | |
| 604 // The getInterceptor methods directly access the prototype of classes. | |
| 605 // We must evaluate these classes eagerly so that the prototype is | |
| 606 // accessible. | |
| 607 void _markEagerInterceptorClasses() { | |
| 608 Map<js.Name, Set<ClassElement>> specializedGetInterceptors = | |
| 609 backend.specializedGetInterceptors; | |
| 610 for (Set<ClassElement> classes in specializedGetInterceptors.values) { | |
| 611 for (ClassElement element in classes) { | |
| 612 Class cls = _classes[element]; | |
| 613 if (cls != null) cls.isEager = true; | |
| 614 } | |
| 615 } | |
| 616 } | |
| 617 | |
| 618 Iterable<StaticStubMethod> _generateGetInterceptorMethods() { | |
| 619 InterceptorStubGenerator stubGenerator = | |
| 620 new InterceptorStubGenerator(_compiler, namer, backend); | |
| 621 | |
| 622 String holderName = namer.globalObjectFor(backend.interceptorsLibrary); | |
| 623 // TODO(floitsch): we shouldn't update the registry in the middle of | |
| 624 // generating the interceptor methods. | |
| 625 Holder holder = _registry.registerHolder(holderName); | |
| 626 | |
| 627 Map<js.Name, Set<ClassElement>> specializedGetInterceptors = | |
| 628 backend.specializedGetInterceptors; | |
| 629 List<js.Name> names = specializedGetInterceptors.keys.toList()..sort(); | |
| 630 return names.map((js.Name name) { | |
| 631 Set<ClassElement> classes = specializedGetInterceptors[name]; | |
| 632 js.Expression code = stubGenerator.generateGetInterceptorMethod(classes); | |
| 633 return new StaticStubMethod(name, holder, code); | |
| 634 }); | |
| 635 } | |
| 636 | |
| 637 List<Field> _buildFields(Element holder, bool visitStatics) { | |
| 638 List<Field> fields = <Field>[]; | |
| 639 _task.oldEmitter.classEmitter.visitFields( | |
| 640 holder, visitStatics, (VariableElement field, | |
| 641 js.Name name, | |
| 642 js.Name accessorName, | |
| 643 bool needsGetter, | |
| 644 bool needsSetter, | |
| 645 bool needsCheckedSetter) { | |
| 646 assert(invariant(field, field.isDeclaration)); | |
| 647 | |
| 648 int getterFlags = 0; | |
| 649 if (needsGetter) { | |
| 650 if (visitStatics || !backend.fieldHasInterceptedGetter(field)) { | |
| 651 getterFlags = 1; | |
| 652 } else { | |
| 653 getterFlags += 2; | |
| 654 // TODO(sra): 'isInterceptorClass' might not be the correct test | |
| 655 // for methods forced to use the interceptor convention because | |
| 656 // the method's class was elsewhere mixed-in to an interceptor. | |
| 657 if (!backend.isInterceptorClass(holder)) { | |
| 658 getterFlags += 1; | |
| 659 } | |
| 660 } | |
| 661 } | |
| 662 | |
| 663 int setterFlags = 0; | |
| 664 if (needsSetter) { | |
| 665 if (visitStatics || !backend.fieldHasInterceptedSetter(field)) { | |
| 666 setterFlags = 1; | |
| 667 } else { | |
| 668 setterFlags += 2; | |
| 669 if (!backend.isInterceptorClass(holder)) { | |
| 670 setterFlags += 1; | |
| 671 } | |
| 672 } | |
| 673 } | |
| 674 | |
| 675 fields.add(new Field(field, name, accessorName, | |
| 676 getterFlags, setterFlags, | |
| 677 needsCheckedSetter)); | |
| 678 }); | |
| 679 | |
| 680 return fields; | |
| 681 } | |
| 682 | |
| 683 Iterable<StaticStubMethod> _generateOneShotInterceptors() { | |
| 684 InterceptorStubGenerator stubGenerator = | |
| 685 new InterceptorStubGenerator(_compiler, namer, backend); | |
| 686 | |
| 687 String holderName = namer.globalObjectFor(backend.interceptorsLibrary); | |
| 688 // TODO(floitsch): we shouldn't update the registry in the middle of | |
| 689 // generating the interceptor methods. | |
| 690 Holder holder = _registry.registerHolder(holderName); | |
| 691 | |
| 692 List<js.Name> names = backend.oneShotInterceptors.keys.toList()..sort(); | |
| 693 return names.map((js.Name name) { | |
| 694 js.Expression code = stubGenerator.generateOneShotInterceptor(name); | |
| 695 return new StaticStubMethod(name, holder, code); | |
| 696 }); | |
| 697 } | |
| 698 | |
| 699 StaticDartMethod _buildStaticMethod(FunctionElement element) { | |
| 700 js.Name name = namer.methodPropertyName(element); | |
| 701 String holder = namer.globalObjectFor(element); | |
| 702 js.Expression code = backend.generatedCode[element]; | |
| 703 | |
| 704 bool isApplyTarget = !element.isConstructor && !element.isAccessor; | |
| 705 bool canBeApplied = _methodCanBeApplied(element); | |
| 706 bool canBeReflected = _methodCanBeReflected(element); | |
| 707 | |
| 708 bool needsTearOff = isApplyTarget && | |
| 709 (canBeReflected || | |
| 710 universe.staticFunctionsNeedingGetter.contains(element)); | |
| 711 | |
| 712 js.Name tearOffName = | |
| 713 needsTearOff ? namer.staticClosureName(element) : null; | |
| 714 | |
| 715 | |
| 716 js.Name callName = null; | |
| 717 if (needsTearOff) { | |
| 718 Selector callSelector = | |
| 719 new Selector.fromElement(element).toCallSelector(); | |
| 720 callName = namer.invocationName(callSelector); | |
| 721 } | |
| 722 js.Expression functionType; | |
| 723 DartType type = element.type; | |
| 724 if (needsTearOff || canBeReflected) { | |
| 725 OutputUnit outputUnit = | |
| 726 _compiler.deferredLoadTask.outputUnitForElement(element); | |
| 727 functionType = _generateFunctionType(type, outputUnit); | |
| 728 } | |
| 729 | |
| 730 int requiredParameterCount; | |
| 731 var /* List | Map */ optionalParameterDefaultValues; | |
| 732 if (canBeApplied || canBeReflected) { | |
| 733 FunctionSignature signature = element.functionSignature; | |
| 734 requiredParameterCount = signature.requiredParameterCount; | |
| 735 optionalParameterDefaultValues = | |
| 736 _computeParameterDefaultValues(signature); | |
| 737 } | |
| 738 | |
| 739 // TODO(floitsch): we shouldn't update the registry in the middle of | |
| 740 // building a static method. | |
| 741 return new StaticDartMethod(element, | |
| 742 name, _registry.registerHolder(holder), code, | |
| 743 _generateParameterStubs(element, needsTearOff), | |
| 744 callName, | |
| 745 needsTearOff: needsTearOff, | |
| 746 tearOffName: tearOffName, | |
| 747 canBeApplied: canBeApplied, | |
| 748 canBeReflected: canBeReflected, | |
| 749 requiredParameterCount: requiredParameterCount, | |
| 750 optionalParameterDefaultValues: | |
| 751 optionalParameterDefaultValues, | |
| 752 functionType: functionType); | |
| 753 } | |
| 754 | |
| 755 void _registerConstants(OutputUnit outputUnit, | |
| 756 Iterable<ConstantValue> constantValues) { | |
| 757 // `constantValues` is null if an outputUnit doesn't contain any constants. | |
| 758 if (constantValues == null) return; | |
| 759 for (ConstantValue constantValue in constantValues) { | |
| 760 _registry.registerConstant(outputUnit, constantValue); | |
| 761 assert(!_constants.containsKey(constantValue)); | |
| 762 js.Name name = namer.constantName(constantValue); | |
| 763 String constantObject = namer.globalObjectForConstant(constantValue); | |
| 764 Holder holder = _registry.registerHolder(constantObject); | |
| 765 Constant constant = new Constant(name, holder, constantValue); | |
| 766 _constants[constantValue] = constant; | |
| 767 } | |
| 768 } | |
| 769 } | |
| OLD | NEW |