blob: 7b9314eed8fd308f64452b8e6201b6ac04b8f471 [file] [log] [blame]
// 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/elements.dart' show Entity;
import '../elements/entities.dart';
import '../js/js.dart' as jsAst;
import '../js_backend/js_backend.dart' show JavaScriptBackend, Namer;
import '../world.dart' show ClosedWorld;
import 'full_emitter/emitter.dart' as full_js_emitter;
import 'lazy_emitter/emitter.dart' as lazy_js_emitter;
import 'program_builder/program_builder.dart';
import 'startup_emitter/emitter.dart' as startup_js_emitter;
import 'metadata_collector.dart' show MetadataCollector;
import 'native_emitter.dart' show NativeEmitter;
import 'type_test_registry.dart' show TypeTestRegistry;
const USE_LAZY_EMITTER = const bool.fromEnvironment("dart2js.use.lazy.emitter");
/**
* 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;
EmitterFactory _emitterFactory;
Emitter _emitter;
final Compiler compiler;
/// Records if a type variable is read dynamically for type tests.
final Set<TypeVariableEntity> readTypeVariables =
new Set<TypeVariableEntity>();
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,
super(compiler.measurer) {
nativeEmitter = new NativeEmitter(this);
if (USE_LAZY_EMITTER) {
_emitterFactory = new lazy_js_emitter.EmitterFactory();
} else if (useStartupEmitter) {
_emitterFactory = new startup_js_emitter.EmitterFactory(
generateSourceMap: generateSourceMap);
} else {
_emitterFactory = new full_js_emitter.EmitterFactory(
generateSourceMap: generateSourceMap);
}
}
Emitter get emitter {
assert(invariant(NO_LOCATION_SPANNABLE, _emitter != null,
message: "Emitter has not been created yet."));
return _emitter;
}
String get name => 'Code emitter';
/// Returns the string that is used to find library patches that are
/// specialized for the emitter.
String get patchVersion => _emitterFactory.patchVersion;
/// 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 registerReadTypeVariable(TypeVariableEntity element) {
readTypeVariables.add(element);
}
Set<ClassEntity> _finalizeRti() {
// Compute the required type checks to know which classes need a
// 'is$' method.
typeTestRegistry.computeRequiredTypeChecks();
// Compute the classes needed by RTI.
return typeTestRegistry.computeRtiNeededClasses();
}
/// Creates the [Emitter] for this task.
void createEmitter(Namer namer, ClosedWorld closedWorld) {
measure(() {
_emitter = _emitterFactory.createEmitter(this, namer, closedWorld);
metadataCollector = new MetadataCollector(compiler, _emitter);
typeTestRegistry = new TypeTestRegistry(compiler, closedWorld);
});
}
int assembleProgram(Namer namer, ClosedWorld closedWorld) {
return measure(() {
emitter.invalidateCaches();
Set<ClassEntity> rtiNeededClasses = _finalizeRti();
ProgramBuilder programBuilder = new ProgramBuilder(
compiler, namer, this, emitter, closedWorld, rtiNeededClasses);
int size = emitter.emitProgram(programBuilder);
// TODO(floitsch): we shouldn't need the `neededClasses` anymore.
neededClasses = programBuilder.collector.neededClasses;
return size;
});
}
}
abstract class EmitterFactory {
/// Returns the string that is used to find library patches that are
/// specialized for this emitter.
String get patchVersion;
/// 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);
}
abstract class Emitter {
/// 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(FieldEntity element);
/// Returns the closure expression of a static function.
jsAst.Expression isolateStaticClosureAccess(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(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);
void invalidateCaches();
}