| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| library dart2js.js_emitter.code_emitter_task; |
| |
| import 'package:js_runtime/shared/embedded_names.dart' show JsBuiltin; |
| |
| import '../common.dart'; |
| import '../common/tasks.dart' show CompilerTask; |
| import '../compiler.dart' show Compiler; |
| import '../constants/values.dart'; |
| import '../deferred_load.dart' show OutputUnit; |
| import '../elements/entities.dart'; |
| import '../js/js.dart' as jsAst; |
| import '../js_backend/js_backend.dart' show JavaScriptBackend, Namer; |
| import '../universe/world_builder.dart' show CodegenWorldBuilder; |
| import '../world.dart' show ClosedWorld; |
| import 'full_emitter/emitter.dart' as full_js_emitter; |
| import 'program_builder/program_builder.dart'; |
| import 'startup_emitter/emitter.dart' as startup_js_emitter; |
| |
| import 'metadata_collector.dart' show MetadataCollector; |
| import 'model.dart'; |
| import 'native_emitter.dart' show NativeEmitter; |
| import 'type_test_registry.dart' show TypeTestRegistry; |
| import 'sorter.dart'; |
| |
| /** |
| * Generates the code for all used classes in the program. Static fields (even |
| * in classes) are ignored, since they can be treated as non-class elements. |
| * |
| * The code for the containing (used) methods must exist in the `universe`. |
| */ |
| class CodeEmitterTask extends CompilerTask { |
| TypeTestRegistry typeTestRegistry; |
| NativeEmitter _nativeEmitter; |
| MetadataCollector metadataCollector; |
| final EmitterFactory _emitterFactory; |
| Emitter _emitter; |
| final Compiler compiler; |
| |
| JavaScriptBackend get backend => compiler.backend; |
| |
| @deprecated |
| // This field should be removed. It's currently only needed for dump-info and |
| // tests. |
| // The field is set after the program has been emitted. |
| /// Contains a list of all classes that are emitted. |
| Set<ClassEntity> neededClasses; |
| |
| CodeEmitterTask( |
| Compiler compiler, bool generateSourceMap, bool useStartupEmitter) |
| : compiler = compiler, |
| _emitterFactory = useStartupEmitter |
| ? new startup_js_emitter.EmitterFactory( |
| generateSourceMap: generateSourceMap) |
| : new full_js_emitter.EmitterFactory( |
| generateSourceMap: generateSourceMap), |
| super(compiler.measurer); |
| |
| NativeEmitter get nativeEmitter { |
| assert( |
| _nativeEmitter != null, |
| failedAt( |
| NO_LOCATION_SPANNABLE, "NativeEmitter has not been created yet.")); |
| return _nativeEmitter; |
| } |
| |
| Emitter get emitter { |
| assert(_emitter != null, |
| failedAt(NO_LOCATION_SPANNABLE, "Emitter has not been created yet.")); |
| return _emitter; |
| } |
| |
| String get name => 'Code emitter'; |
| |
| /// Returns true, if the emitter supports reflection. |
| bool get supportsReflection => _emitterFactory.supportsReflection; |
| |
| /// Returns the closure expression of a static function. |
| jsAst.Expression isolateStaticClosureAccess(FunctionEntity element) { |
| return emitter.isolateStaticClosureAccess(element); |
| } |
| |
| /// Returns the JS function that must be invoked to get the value of the |
| /// lazily initialized static. |
| jsAst.Expression isolateLazyInitializerAccess(FieldEntity element) { |
| return emitter.isolateLazyInitializerAccess(element); |
| } |
| |
| /// Returns the JS code for accessing the embedded [global]. |
| jsAst.Expression generateEmbeddedGlobalAccess(String global) { |
| return emitter.generateEmbeddedGlobalAccess(global); |
| } |
| |
| /// Returns the JS code for accessing the given [constant]. |
| jsAst.Expression constantReference(ConstantValue constant) { |
| return emitter.constantReference(constant); |
| } |
| |
| jsAst.Expression staticFieldAccess(FieldEntity e) { |
| return emitter.staticFieldAccess(e); |
| } |
| |
| /// Returns the JS function representing the given function. |
| /// |
| /// The function must be invoked and can not be used as closure. |
| jsAst.Expression staticFunctionAccess(FunctionEntity e) { |
| return emitter.staticFunctionAccess(e); |
| } |
| |
| /// Returns the JS constructor of the given element. |
| /// |
| /// The returned expression must only be used in a JS `new` expression. |
| jsAst.Expression constructorAccess(ClassEntity e) { |
| return emitter.constructorAccess(e); |
| } |
| |
| /// Returns the JS prototype of the given class [e]. |
| jsAst.Expression prototypeAccess(ClassEntity e, |
| {bool hasBeenInstantiated: false}) { |
| return emitter.prototypeAccess(e, hasBeenInstantiated); |
| } |
| |
| /// Returns the JS prototype of the given interceptor class [e]. |
| jsAst.Expression interceptorPrototypeAccess(ClassEntity e) { |
| return jsAst.js('#.prototype', interceptorClassAccess(e)); |
| } |
| |
| /// Returns the JS constructor of the given interceptor class [e]. |
| jsAst.Expression interceptorClassAccess(ClassEntity e) { |
| return emitter.interceptorClassAccess(e); |
| } |
| |
| /// Returns the JS expression representing the type [e]. |
| /// |
| /// The given type [e] might be a Typedef. |
| jsAst.Expression typeAccess(Entity e) { |
| return emitter.typeAccess(e); |
| } |
| |
| /// Returns the JS template for the given [builtin]. |
| jsAst.Template builtinTemplateFor(JsBuiltin builtin) { |
| return emitter.templateForBuiltin(builtin); |
| } |
| |
| void _finalizeRti() { |
| // Compute the required type checks to know which classes need a |
| // 'is$' method. |
| typeTestRegistry.computeRequiredTypeChecks(backend.rtiChecksBuilder); |
| // Compute the classes needed by RTI. |
| typeTestRegistry.computeRtiNeededClasses( |
| backend.rtiSubstitutions, backend.generatedCode.keys); |
| } |
| |
| /// Creates the [Emitter] for this task. |
| void createEmitter(Namer namer, ClosedWorld closedWorld, |
| CodegenWorldBuilder codegenWorldBuilder, Sorter sorter) { |
| measure(() { |
| _nativeEmitter = new NativeEmitter(this, closedWorld, codegenWorldBuilder, |
| backend.nativeCodegenEnqueuer); |
| _emitter = |
| _emitterFactory.createEmitter(this, namer, closedWorld, sorter); |
| metadataCollector = new MetadataCollector(compiler.options, |
| compiler.reporter, _emitter, backend.rtiEncoder, codegenWorldBuilder); |
| typeTestRegistry = new TypeTestRegistry(compiler.options, |
| codegenWorldBuilder, closedWorld.elementEnvironment); |
| }); |
| } |
| |
| int assembleProgram(Namer namer, ClosedWorld closedWorld) { |
| return measure(() { |
| _finalizeRti(); |
| ProgramBuilder programBuilder = new ProgramBuilder( |
| compiler.options, |
| compiler.reporter, |
| closedWorld.elementEnvironment, |
| closedWorld.commonElements, |
| closedWorld.dartTypes, |
| compiler.deferredLoadTask, |
| backend.outputUnitData, |
| compiler.backendStrategy.closureDataLookup, |
| compiler.codegenWorldBuilder, |
| backend.nativeCodegenEnqueuer, |
| closedWorld.backendUsage, |
| backend.constants, |
| closedWorld.nativeData, |
| closedWorld.rtiNeed, |
| closedWorld.interceptorData, |
| backend.superMemberData, |
| typeTestRegistry.rtiChecks, |
| backend.rtiEncoder, |
| backend.jsInteropAnalysis, |
| backend.oneShotInterceptorData, |
| backend.customElementsCodegenAnalysis, |
| backend.generatedCode, |
| namer, |
| this, |
| closedWorld, |
| backend.sourceInformationStrategy, |
| compiler.backendStrategy.sorter, |
| typeTestRegistry.rtiNeededClasses, |
| closedWorld.elementEnvironment.mainFunction); |
| int size = emitter.emitProgram(programBuilder); |
| // TODO(floitsch): we shouldn't need the `neededClasses` anymore. |
| neededClasses = programBuilder.collector.neededClasses; |
| return size; |
| }); |
| } |
| } |
| |
| abstract class EmitterFactory { |
| /// Returns true, if the emitter supports reflection. |
| bool get supportsReflection; |
| |
| /// Create the [Emitter] for the emitter [task] that uses the given [namer]. |
| Emitter createEmitter(CodeEmitterTask task, Namer namer, |
| ClosedWorld closedWorld, Sorter sorter); |
| } |
| |
| abstract class Emitter { |
| Program get programForTesting; |
| |
| /// Uses the [programBuilder] to generate a model of the program, emits |
| /// the program, and returns the size of the generated output. |
| int emitProgram(ProgramBuilder programBuilder); |
| |
| /// Returns the JS function that must be invoked to get the value of the |
| /// lazily initialized static. |
| jsAst.Expression isolateLazyInitializerAccess(covariant FieldEntity element); |
| |
| /// Returns the closure expression of a static function. |
| jsAst.Expression isolateStaticClosureAccess(covariant FunctionEntity element); |
| |
| /// Returns the JS code for accessing the embedded [global]. |
| jsAst.Expression generateEmbeddedGlobalAccess(String global); |
| |
| /// Returns the JS function representing the given function. |
| /// |
| /// The function must be invoked and can not be used as closure. |
| jsAst.Expression staticFunctionAccess(FunctionEntity element); |
| |
| jsAst.Expression staticFieldAccess(FieldEntity element); |
| |
| /// Returns the JS constructor of the given element. |
| /// |
| /// The returned expression must only be used in a JS `new` expression. |
| jsAst.Expression constructorAccess(ClassEntity e); |
| |
| /// Returns the JS prototype of the given class [e]. |
| jsAst.Expression prototypeAccess( |
| covariant ClassEntity e, bool hasBeenInstantiated); |
| |
| /// Returns the JS constructor of the given interceptor class [e]. |
| jsAst.Expression interceptorClassAccess(ClassEntity e); |
| |
| /// Returns the JS expression representing the type [e]. |
| jsAst.Expression typeAccess(Entity e); |
| |
| /// Returns the JS expression representing a function that returns 'null' |
| jsAst.Expression generateFunctionThatReturnsNull(); |
| |
| int compareConstants(ConstantValue a, ConstantValue b); |
| bool isConstantInlinedOrAlreadyEmitted(ConstantValue constant); |
| |
| /// Returns the JS code for accessing the given [constant]. |
| jsAst.Expression constantReference(ConstantValue constant); |
| |
| /// Returns the JS template for the given [builtin]. |
| jsAst.Template templateForBuiltin(JsBuiltin builtin); |
| |
| /// Returns the size of the code generated for a given output [unit]. |
| int generatedSize(OutputUnit unit); |
| } |
| |
| abstract class EmitterBase implements Emitter { |
| Program programForTesting; |
| Namer get namer; |
| |
| jsAst.PropertyAccess globalPropertyAccessForMember(MemberEntity element) { |
| jsAst.Name name = namer.globalPropertyNameForMember(element); |
| jsAst.PropertyAccess pa = new jsAst.PropertyAccess( |
| new jsAst.VariableUse(namer.globalObjectForMember(element)), name); |
| return pa; |
| } |
| |
| jsAst.PropertyAccess globalPropertyAccessForClass(ClassEntity element) { |
| jsAst.Name name = namer.globalPropertyNameForClass(element); |
| jsAst.PropertyAccess pa = new jsAst.PropertyAccess( |
| new jsAst.VariableUse(namer.globalObjectForClass(element)), name); |
| return pa; |
| } |
| |
| jsAst.PropertyAccess globalPropertyAccessForType(Entity element) { |
| jsAst.Name name = namer.globalPropertyNameForType(element); |
| jsAst.PropertyAccess pa = new jsAst.PropertyAccess( |
| new jsAst.VariableUse(namer.globalObjectForType(element)), name); |
| return pa; |
| } |
| |
| @override |
| jsAst.PropertyAccess staticFieldAccess(FieldEntity element) { |
| return globalPropertyAccessForMember(element); |
| } |
| |
| @override |
| jsAst.PropertyAccess staticFunctionAccess(FunctionEntity element) { |
| return globalPropertyAccessForMember(element); |
| } |
| |
| @override |
| jsAst.PropertyAccess constructorAccess(ClassEntity element) { |
| return globalPropertyAccessForClass(element); |
| } |
| |
| @override |
| jsAst.Expression interceptorClassAccess(ClassEntity element) { |
| return globalPropertyAccessForClass(element); |
| } |
| |
| @override |
| jsAst.Expression typeAccess(Entity element) { |
| return globalPropertyAccessForType(element); |
| } |
| } |