blob: b4e2ffab96cb1171565aea7f51a34291fc18442d [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.
part of js_backend;
typedef void Recompile(Element element);
class ReturnInfo {
HType returnType;
List<Element> compiledFunctions;
ReturnInfo(HType this.returnType)
: compiledFunctions = new List<Element>();
ReturnInfo.unknownType() : this(null);
void update(HType type, Recompile recompile, Compiler compiler) {
HType newType =
returnType != null ? returnType.union(type, compiler) : type;
if (newType != returnType) {
if (returnType == null && identical(newType, HType.UNKNOWN)) {
// If the first actual piece of information is not providing any type
// information there is no need to recompile callers.
compiledFunctions.clear();
}
returnType = newType;
if (recompile != null) {
compiledFunctions.forEach(recompile);
}
compiledFunctions.clear();
}
}
// Note that lazy initializers are treated like functions (but are not
// of type [FunctionElement].
addCompiledFunction(Element function) => compiledFunctions.add(function);
}
class OptionalParameterTypes {
final List<SourceString> names;
final List<HType> types;
OptionalParameterTypes(int optionalArgumentsCount)
: names = new List<SourceString>(optionalArgumentsCount),
types = new List<HType>(optionalArgumentsCount);
int get length => names.length;
SourceString name(int index) => names[index];
HType type(int index) => types[index];
int indexOf(SourceString name) => names.indexOf(name);
HType typeFor(SourceString name) {
int index = indexOf(name);
if (index == -1) return null;
return type(index);
}
void update(int index, SourceString name, HType type) {
names[index] = name;
types[index] = type;
}
String toString() => "OptionalParameterTypes($names, $types)";
}
class HTypeList {
final List<HType> types;
final List<SourceString> namedArguments;
HTypeList(int length)
: types = new List<HType>(length),
namedArguments = null;
HTypeList.withNamedArguments(int length, this.namedArguments)
: types = new List<HType>(length);
const HTypeList.withAllUnknown()
: types = null,
namedArguments = null;
factory HTypeList.fromStaticInvocation(HInvokeStatic node) {
bool allUnknown = true;
for (int i = 1; i < node.inputs.length; i++) {
if (node.inputs[i].instructionType != HType.UNKNOWN) {
allUnknown = false;
break;
}
}
if (allUnknown) return HTypeList.ALL_UNKNOWN;
HTypeList result = new HTypeList(node.inputs.length - 1);
for (int i = 0; i < result.types.length; i++) {
result.types[i] = node.inputs[i + 1].instructionType;
assert(!result.types[i].isConflicting());
}
return result;
}
factory HTypeList.fromDynamicInvocation(HInvokeDynamic node,
Selector selector) {
HTypeList result;
int argumentsCount = node.inputs.length - 1;
int startInvokeIndex = HInvoke.ARGUMENTS_OFFSET;
if (node.isInterceptorCall) {
argumentsCount--;
startInvokeIndex++;
}
if (selector.namedArgumentCount > 0) {
result =
new HTypeList.withNamedArguments(
argumentsCount, selector.namedArguments);
} else {
result = new HTypeList(argumentsCount);
}
for (int i = 0; i < result.types.length; i++) {
result.types[i] = node.inputs[i + startInvokeIndex].instructionType;
assert(!result.types[i].isConflicting());
}
return result;
}
static const HTypeList ALL_UNKNOWN = const HTypeList.withAllUnknown();
bool get allUnknown => types == null;
bool get hasNamedArguments => namedArguments != null;
int get length => types.length;
HType operator[](int index) => types[index];
void operator[]=(int index, HType type) { types[index] = type; }
HTypeList union(HTypeList other, Compiler compiler) {
if (allUnknown) return this;
if (other.allUnknown) return other;
if (length != other.length) return HTypeList.ALL_UNKNOWN;
bool onlyUnknown = true;
HTypeList result = this;
for (int i = 0; i < length; i++) {
HType newType = this[i].union(other[i], compiler);
if (result == this && newType != this[i]) {
// Create a new argument types object with the matching types copied.
result = new HTypeList(length);
result.types.setRange(0, i, this.types);
}
if (result != this) {
result.types[i] = newType;
}
if (result[i] != HType.UNKNOWN) onlyUnknown = false;
}
return onlyUnknown ? HTypeList.ALL_UNKNOWN : result;
}
HTypeList unionWithOptionalParameters(
Selector selector,
FunctionSignature signature,
OptionalParameterTypes defaultValueTypes) {
assert(allUnknown || selector.argumentCount == this.length);
// Create a new HTypeList for holding types for all parameters.
HTypeList result = new HTypeList(signature.parameterCount);
// First fill in the type of the positional arguments.
int nextTypeIndex = -1;
if (allUnknown) {
for (int i = 0; i < selector.positionalArgumentCount; i++) {
result.types[i] = HType.UNKNOWN;
}
} else {
result.types.setRange(0, selector.positionalArgumentCount, this.types);
nextTypeIndex = selector.positionalArgumentCount;
}
// Next fill the type of the optional arguments.
// As the selector can pass optional arguments positionally some of the
// optional arguments might already have a type set. We only need to look
// at the optional arguments not passed positionally.
// The variable 'index' is counting the signatures optional arguments, the
// variable 'next' is set to the next optional arguments to look at and
// is used to skip some optional arguments.
int next = selector.positionalArgumentCount;
int index = signature.requiredParameterCount;
signature.forEachOptionalParameter((Element element) {
// If some optional parameters were passed positionally these have
// already been filled.
if (index == next) {
assert(result.types[index] == null);
HType type = null;
if (hasNamedArguments &&
selector.namedArguments.indexOf(element.name) >= 0) {
type = types[nextTypeIndex++];
} else {
type = defaultValueTypes.typeFor(element.name);
}
result.types[index] = type;
next++;
}
index++;
});
return result;
}
String toString() =>
allUnknown ? "HTypeList.ALL_UNKNOWN" : "HTypeList $types";
}
class FieldTypesRegistry {
final JavaScriptBackend backend;
/**
* For each class, [constructors] holds the set of constructors. If there is
* more than one constructor for a class it is currently not possible to
* infer the field types from construction, as the information collected does
* not correlate the generative constructors and generative constructor
* body/bodies.
*/
final Map<ClassElement, Set<Element>> constructors;
/**
* The collected type information is stored in three maps. One for types
* assigned in the initializer list(s) [fieldInitializerTypeMap], one for
* types assigned in the constructor(s) [fieldConstructorTypeMap], and one
* for types assigned in the rest of the code, where the field can be
* resolved [fieldTypeMap].
*
* If a field has a type both from constructors and from the initializer
* list(s), then the type from the constructor(s) will owerride the one from
* the initializer list(s).
*
* Because the order in which generative constructors, generative constructor
* bodies and normal method/function bodies are compiled is undefined, and
* because they can all be recompiled, it is not possible to combine this
* information into one map at the moment.
*/
final Map<Element, HType> fieldInitializerTypeMap;
final Map<Element, HType> fieldConstructorTypeMap;
final Map<Element, HType> fieldTypeMap;
/**
* The set of current names setter selectors used. If a named selector is
* used it is currently not possible to infer the type of the field.
*/
final Set<SourceString> setterSelectorsUsed;
final Map<Element, Set<Element>> optimizedStaticFunctions;
final Map<Element, FunctionSet> optimizedFunctions;
FieldTypesRegistry(JavaScriptBackend backend)
: constructors = new Map<ClassElement, Set<Element>>(),
fieldInitializerTypeMap = new Map<Element, HType>(),
fieldConstructorTypeMap = new Map<Element, HType>(),
fieldTypeMap = new Map<Element, HType>(),
setterSelectorsUsed = new Set<SourceString>(),
optimizedStaticFunctions = new Map<Element, Set<Element>>(),
optimizedFunctions = new Map<Element, FunctionSet>(),
this.backend = backend;
Compiler get compiler => backend.compiler;
void scheduleRecompilation(Element field) {
Set optimizedStatics = optimizedStaticFunctions[field];
if (optimizedStatics != null) {
optimizedStatics.forEach(backend.scheduleForRecompilation);
optimizedStaticFunctions.remove(field);
}
FunctionSet optimized = optimizedFunctions[field];
if (optimized != null) {
optimized.forEach(backend.scheduleForRecompilation);
optimizedFunctions.remove(field);
}
}
int constructorCount(Element element) {
assert(element.isClass());
Set<Element> ctors = constructors[element];
return ctors == null ? 0 : ctors.length;
}
void registerFieldType(Map<Element, HType> typeMap,
Element field,
HType type) {
assert(field.isField());
assert(!type.isConflicting());
HType before = optimisticFieldType(field);
HType oldType = typeMap[field];
HType newType;
if (oldType != null) {
newType = oldType.union(type, compiler);
} else {
newType = type;
}
typeMap[field] = newType;
if (oldType != newType) {
scheduleRecompilation(field);
}
}
void registerConstructor(Element element) {
assert(element.isGenerativeConstructor());
Element cls = element.getEnclosingClass();
constructors.putIfAbsent(cls, () => new Set<Element>());
Set<Element> ctors = constructors[cls];
if (ctors.contains(element)) return;
ctors.add(element);
// We cannot infer field types for classes with more than one constructor.
// When the second constructor is seen, recompile all functions relying on
// optimistic field types for that class.
// TODO(sgjesse): Handle field types for classes with more than one
// constructor.
if (ctors.length == 2) {
optimizedFunctions.keys.toList().forEach((Element field) {
if (identical(field.enclosingElement, cls)) {
scheduleRecompilation(field);
}
});
}
}
void registerFieldInitializer(Element field, HType type) {
registerFieldType(fieldInitializerTypeMap, field, type);
}
void registerFieldConstructor(Element field, HType type) {
registerFieldType(fieldConstructorTypeMap, field, type);
}
void registerFieldSetter(FunctionElement element, Element field, HType type) {
HType initializerType = fieldInitializerTypeMap[field];
HType constructorType = fieldConstructorTypeMap[field];
HType setterType = fieldTypeMap[field];
if (type == HType.UNKNOWN
&& initializerType == null
&& constructorType == null
&& setterType == null) {
// Don't register UNKNOWN if there is currently no type information
// present for the field. Instead register the function holding the
// setter for recompilation if better type information for the field
// becomes available.
registerOptimizedFunction(element, field, type);
return;
}
registerFieldType(fieldTypeMap, field, type);
}
void addedDynamicSetter(Selector setter, HType type) {
// Field type optimizations are disabled for all fields matching a
// setter selector.
assert(setter.isSetter());
// TODO(sgjesse): Take the type of the setter into account.
if (setterSelectorsUsed.contains(setter.name)) return;
setterSelectorsUsed.add(setter.name);
optimizedStaticFunctions.keys.toList().forEach((Element field) {
if (field.name == setter.name) {
scheduleRecompilation(field);
}
});
optimizedFunctions.keys.toList().forEach((Element field) {
if (field.name == setter.name) {
scheduleRecompilation(field);
}
});
}
HType optimisticFieldType(Element field) {
assert(field.isField());
if (constructorCount(field.getEnclosingClass()) > 1) {
return HType.UNKNOWN;
}
if (setterSelectorsUsed.contains(field.name)) {
return HType.UNKNOWN;
}
HType initializerType = fieldInitializerTypeMap[field];
HType constructorType = fieldConstructorTypeMap[field];
if (initializerType == null && constructorType == null) {
// If there are no constructor type information return UNKNOWN. This
// ensures that the function will be recompiled if useful constructor
// type information becomes available.
return HType.UNKNOWN;
}
// A type set through the constructor overrides the type from the
// initializer list.
HType result = constructorType != null ? constructorType : initializerType;
HType type = fieldTypeMap[field];
if (type != null) result = result.union(type, compiler);
return result;
}
void registerOptimizedFunction(FunctionElement element,
Element field,
HType type) {
assert(field.isField());
if (Elements.isStaticOrTopLevel(element)) {
optimizedStaticFunctions.putIfAbsent(
field, () => new Set<Element>());
optimizedStaticFunctions[field].add(element);
} else {
optimizedFunctions.putIfAbsent(
field, () => new FunctionSet(backend.compiler));
optimizedFunctions[field].add(element);
}
}
void dump() {
Set<Element> allFields = new Set<Element>();
fieldInitializerTypeMap.keys.forEach(allFields.add);
fieldConstructorTypeMap.keys.forEach(allFields.add);
fieldTypeMap.keys.forEach(allFields.add);
allFields.forEach((Element field) {
print("Inferred $field has type ${optimisticFieldType(field)}");
});
}
}
class ArgumentTypesRegistry {
final JavaScriptBackend backend;
/**
* Documentation wanted -- johnniwinther
*
* Invariant: Keys must be declaration elements.
*/
final Map<Element, HTypeList> staticTypeMap;
/**
* Documentation wanted -- johnniwinther
*
* Invariant: Elements must be declaration elements.
*/
final Set<Element> optimizedStaticFunctions;
final SelectorMap<HTypeList> selectorTypeMap;
final FunctionSet optimizedFunctions;
/**
* Documentation wanted -- johnniwinther
*
* Invariant: Keys must be declaration elements.
*/
final Map<Element, HTypeList> optimizedTypes;
final Map<Element, OptionalParameterTypes> optimizedDefaultValueTypes;
ArgumentTypesRegistry(JavaScriptBackend backend)
: staticTypeMap = new Map<Element, HTypeList>(),
optimizedStaticFunctions = new Set<Element>(),
selectorTypeMap = new SelectorMap<HTypeList>(backend.compiler),
optimizedFunctions = new FunctionSet(backend.compiler),
optimizedTypes = new Map<Element, HTypeList>(),
optimizedDefaultValueTypes =
new Map<Element, OptionalParameterTypes>(),
this.backend = backend;
Compiler get compiler => backend.compiler;
bool updateTypes(HTypeList oldTypes, HTypeList newTypes, var key, var map) {
if (oldTypes.allUnknown) return false;
newTypes = oldTypes.union(newTypes, backend.compiler);
if (identical(newTypes, oldTypes)) return false;
map[key] = newTypes;
return true;
}
void registerStaticInvocation(HInvokeStatic node) {
Element element = node.element;
assert(invariant(node, element.isDeclaration));
HTypeList oldTypes = staticTypeMap[element];
HTypeList newTypes = new HTypeList.fromStaticInvocation(node);
if (oldTypes == null) {
staticTypeMap[element] = newTypes;
} else if (updateTypes(oldTypes, newTypes, element, staticTypeMap)) {
if (optimizedStaticFunctions.contains(element)) {
backend.scheduleForRecompilation(element);
}
}
}
void registerNonCallStaticUse(HStatic node) {
// When a static is used for anything else than a call target we cannot
// infer anything about its parameter types.
Element element = node.element;
assert(invariant(node, element.isDeclaration));
if (optimizedStaticFunctions.contains(element)) {
backend.scheduleForRecompilation(element);
}
staticTypeMap[element] = HTypeList.ALL_UNKNOWN;
}
void registerDynamicInvocation(HTypeList providedTypes, Selector selector) {
if (selector.isClosureCall()) {
// We cannot use the current framework to do optimizations based
// on the 'call' selector because we are also generating closure
// calls during the emitter phase, which at this point, does not
// track parameter types, nor invalidates optimized methods.
return;
}
if (!selectorTypeMap.containsKey(selector)) {
selectorTypeMap[selector] = providedTypes;
} else {
HTypeList oldTypes = selectorTypeMap[selector];
updateTypes(oldTypes, providedTypes, selector, selectorTypeMap);
}
// If we're not compiling, we don't have to do anything.
if (compiler.phase != Compiler.PHASE_COMPILING) return;
// Run through all optimized functions and figure out if they need
// to be recompiled because of this new invocation.
for (Element element in optimizedFunctions.filter(selector)) {
// TODO(kasperl): Maybe check if the element is already marked for
// recompilation? Could be pretty cheap compared to computing
// union types.
HTypeList newTypes =
parameterTypes(element, optimizedDefaultValueTypes[element]);
bool recompile = false;
if (newTypes.allUnknown) {
recompile = true;
} else {
HTypeList oldTypes = optimizedTypes[element];
assert(newTypes.length == oldTypes.length);
for (int i = 0; i < oldTypes.length; i++) {
if (newTypes[i] != oldTypes[i]) {
recompile = true;
break;
}
}
}
if (recompile) backend.scheduleForRecompilation(element);
}
}
HTypeList parameterTypes(FunctionElement element,
OptionalParameterTypes defaultValueTypes) {
assert(invariant(element, element.isDeclaration));
// Handle static functions separately.
if (Elements.isStaticOrTopLevelFunction(element) ||
element.kind == ElementKind.GENERATIVE_CONSTRUCTOR) {
HTypeList types = staticTypeMap[element];
if (types != null) {
if (!optimizedStaticFunctions.contains(element)) {
optimizedStaticFunctions.add(element);
}
return types;
} else {
return HTypeList.ALL_UNKNOWN;
}
}
// Getters have no parameters.
if (element.isGetter()) return HTypeList.ALL_UNKNOWN;
// TODO(kasperl): What kind of non-members do we get here?
if (!element.isMember()) return HTypeList.ALL_UNKNOWN;
// If there are any getters for this method we cannot know anything about
// the types of the provided parameters. Use resolverWorld for now as that
// information does not change during compilation.
// TODO(ngeoffray): These checks should use the codegenWorld and keep track
// of changes to this information.
if (compiler.resolverWorld.hasInvokedGetter(element, compiler)) {
return HTypeList.ALL_UNKNOWN;
}
FunctionSignature signature = element.computeSignature(compiler);
HTypeList found = null;
selectorTypeMap.visitMatching(element,
(Selector selector, HTypeList types) {
if (selector.argumentCount != signature.parameterCount ||
selector.namedArgumentCount > 0) {
types = types.unionWithOptionalParameters(selector,
signature,
defaultValueTypes);
}
assert(types.allUnknown || types.length == signature.parameterCount);
found = (found == null) ? types : found.union(types, compiler);
return !found.allUnknown;
});
return found != null ? found : HTypeList.ALL_UNKNOWN;
}
void registerOptimizedFunction(Element element,
HTypeList parameterTypes,
OptionalParameterTypes defaultValueTypes) {
if (Elements.isStaticOrTopLevelFunction(element)) {
if (parameterTypes.allUnknown) {
optimizedStaticFunctions.remove(element);
} else {
optimizedStaticFunctions.add(element);
}
}
// TODO(kasperl): What kind of non-members do we get here?
if (!element.isInstanceMember()) return;
if (parameterTypes.allUnknown) {
optimizedFunctions.remove(element);
optimizedTypes.remove(element);
optimizedDefaultValueTypes.remove(element);
} else {
optimizedFunctions.add(element);
optimizedTypes[element] = parameterTypes;
optimizedDefaultValueTypes[element] = defaultValueTypes;
}
}
void dump() {
optimizedFunctions.forEach((Element element) {
HTypeList types = optimizedTypes[element];
print("Inferred $element has argument types ${types.types}");
});
}
}
class JavaScriptItemCompilationContext extends ItemCompilationContext {
final Set<HInstruction> boundsChecked;
JavaScriptItemCompilationContext()
: boundsChecked = new Set<HInstruction>();
}
class JavaScriptBackend extends Backend {
SsaBuilderTask builder;
SsaOptimizerTask optimizer;
SsaCodeGeneratorTask generator;
CodeEmitterTask emitter;
/**
* The generated code as a js AST for compiled methods.
*/
Map<Element, jsAst.Expression> get generatedCode {
return compiler.enqueuer.codegen.generatedCode;
}
/**
* The generated code as a js AST for compiled bailout methods.
*/
final Map<Element, jsAst.Expression> generatedBailoutCode =
new Map<Element, jsAst.Expression>();
ClassElement jsStringClass;
ClassElement jsArrayClass;
ClassElement jsNumberClass;
ClassElement jsIntClass;
ClassElement jsDoubleClass;
ClassElement jsFunctionClass;
ClassElement jsNullClass;
ClassElement jsBoolClass;
ClassElement jsIndexableClass;
ClassElement jsMutableArrayClass;
ClassElement jsFixedArrayClass;
ClassElement jsExtendableArrayClass;
Element jsArrayLength;
Element jsStringLength;
Element jsArrayRemoveLast;
Element jsArrayAdd;
Element jsStringSplit;
Element jsStringConcat;
Element jsStringToString;
ClassElement typeLiteralClass;
ClassElement mapLiteralClass;
ClassElement constMapLiteralClass;
Element getInterceptorMethod;
Element interceptedNames;
bool seenAnyClass = false;
final Namer namer;
/**
* Interface used to determine if an object has the JavaScript
* indexing behavior. The interface is only visible to specific
* libraries.
*/
ClassElement jsIndexingBehaviorInterface;
final Map<Element, ReturnInfo> returnInfo;
/**
* Documentation wanted -- johnniwinther
*
* Invariant: Elements must be declaration elements.
*/
final List<Element> invalidateAfterCodegen;
ArgumentTypesRegistry argumentTypes;
FieldTypesRegistry fieldTypes;
/**
* A collection of selectors of intercepted method calls. The
* emitter uses this set to generate the [:ObjectInterceptor:] class
* whose members just forward the call to the intercepted receiver.
*/
final Set<Selector> usedInterceptors;
/**
* A collection of selectors that must have a one shot interceptor
* generated.
*/
final Map<String, Selector> oneShotInterceptors;
/**
* The members of instantiated interceptor classes: maps a member name to the
* list of members that have that name. This map is used by the codegen to
* know whether a send must be intercepted or not.
*/
final Map<SourceString, Set<Element>> interceptedElements;
// TODO(sra): Not all methods in the Set always require an interceptor. A
// method may be mixed into a true interceptor *and* a plain class. For the
// method to work on the interceptor class it needs to use the explicit
// receiver. This constrains the call on a known plain receiver to pass the
// explicit receiver. https://code.google.com/p/dart/issues/detail?id=8942
/**
* A map of specialized versions of the [getInterceptorMethod].
* Since [getInterceptorMethod] is a hot method at runtime, we're
* always specializing it based on the incoming type. The keys in
* the map are the names of these specialized versions. Note that
* the generic version that contains all possible type checks is
* also stored in this map.
*/
final Map<String, Collection<ClassElement>> specializedGetInterceptors;
/**
* Set of classes whose methods are intercepted.
*/
final Set<ClassElement> interceptedClasses;
/**
* Set of classes whose `operator ==` methods handle `null` themselves.
*/
final Set<ClassElement> specialOperatorEqClasses = new Set<ClassElement>();
/**
* Set of selectors that are used from within loops. Used by the
* builder to allow speculative optimizations for functions without
* loops themselves.
*/
final Map<SourceString, Set<Selector>> selectorsCalledInLoop =
new Map<SourceString, Set<Selector>>();
List<CompilerTask> get tasks {
return <CompilerTask>[builder, optimizer, generator, emitter];
}
final RuntimeTypes rti;
JavaScriptBackend(Compiler compiler, bool generateSourceMap, bool disableEval)
: namer = determineNamer(compiler),
returnInfo = new Map<Element, ReturnInfo>(),
invalidateAfterCodegen = new List<Element>(),
usedInterceptors = new Set<Selector>(),
oneShotInterceptors = new Map<String, Selector>(),
interceptedElements = new Map<SourceString, Set<Element>>(),
rti = new RuntimeTypes(compiler),
specializedGetInterceptors =
new Map<String, Collection<ClassElement>>(),
interceptedClasses = new Set<ClassElement>(),
super(compiler, JAVA_SCRIPT_CONSTANT_SYSTEM) {
emitter = disableEval
? new CodeEmitterNoEvalTask(compiler, namer, generateSourceMap)
: new CodeEmitterTask(compiler, namer, generateSourceMap);
builder = new SsaBuilderTask(this);
optimizer = new SsaOptimizerTask(this);
generator = new SsaCodeGeneratorTask(this);
argumentTypes = new ArgumentTypesRegistry(this);
fieldTypes = new FieldTypesRegistry(this);
}
static Namer determineNamer(Compiler compiler) {
return compiler.enableMinification ?
new MinifyNamer(compiler) :
new Namer(compiler);
}
bool isInterceptorClass(ClassElement element) {
if (element == null) return false;
if (element.isNative()) return true;
return interceptedClasses.contains(element);
}
void addInterceptedSelector(Selector selector) {
usedInterceptors.add(selector);
}
String registerOneShotInterceptor(Selector selector) {
Set<ClassElement> classes = getInterceptedClassesOn(selector.name);
String name = namer.getOneShotInterceptorName(selector, classes);
if (!oneShotInterceptors.containsKey(name)) {
registerSpecializedGetInterceptor(classes);
oneShotInterceptors[name] = selector;
}
return name;
}
bool isInterceptedMethod(Element element) {
return element.isInstanceMember()
&& !element.isGenerativeConstructorBody()
&& interceptedElements[element.name] != null;
}
bool fieldHasInterceptedGetter(Element element) {
assert(element.isField());
return interceptedElements[element.name] != null;
}
bool fieldHasInterceptedSetter(Element element) {
assert(element.isField());
return interceptedElements[element.name] != null;
}
bool isInterceptedName(SourceString name) {
return interceptedElements[name] != null;
}
final Map<SourceString, Set<ClassElement>> interceptedClassesCache =
new Map<SourceString, Set<ClassElement>>();
/**
* Returns a set of interceptor classes that contain a member named
* [name]. Returns [:null:] if there is no class.
*/
Set<ClassElement> getInterceptedClassesOn(SourceString name) {
Set<Element> intercepted = interceptedElements[name];
if (intercepted == null) return null;
return interceptedClassesCache.putIfAbsent(name, () {
// Populate the cache by running through all the elements and
// determine if the given selector applies to them.
Set<ClassElement> result = new Set<ClassElement>();
for (Element element in intercepted) {
ClassElement classElement = element.getEnclosingClass();
result.add(classElement);
}
return result;
});
}
bool operatorEqHandlesNullArgument(FunctionElement operatorEqfunction) {
return specialOperatorEqClasses.contains(
operatorEqfunction.getEnclosingClass());
}
void initializeHelperClasses() {
getInterceptorMethod =
compiler.findInterceptor(const SourceString('getInterceptor'));
interceptedNames =
compiler.findInterceptor(const SourceString('interceptedNames'));
List<ClassElement> classes = [
compiler.objectClass,
jsStringClass = compiler.findInterceptor(const SourceString('JSString')),
jsArrayClass = compiler.findInterceptor(const SourceString('JSArray')),
// The int class must be before the double class, because the
// emitter relies on this list for the order of type checks.
jsIntClass = compiler.findInterceptor(const SourceString('JSInt')),
jsDoubleClass = compiler.findInterceptor(const SourceString('JSDouble')),
jsNumberClass = compiler.findInterceptor(const SourceString('JSNumber')),
jsNullClass = compiler.findInterceptor(const SourceString('JSNull')),
jsFunctionClass =
compiler.findInterceptor(const SourceString('JSFunction')),
jsBoolClass = compiler.findInterceptor(const SourceString('JSBool')),
jsMutableArrayClass =
compiler.findInterceptor(const SourceString('JSMutableArray')),
jsFixedArrayClass =
compiler.findInterceptor(const SourceString('JSFixedArray')),
jsExtendableArrayClass =
compiler.findInterceptor(const SourceString('JSExtendableArray'))];
jsIndexableClass =
compiler.findInterceptor(const SourceString('JSIndexable'));
// TODO(kasperl): Some tests do not define the special JSArray
// subclasses, so we check to see if they are defined before
// trying to resolve them.
if (jsFixedArrayClass != null) {
jsFixedArrayClass.ensureResolved(compiler);
}
if (jsExtendableArrayClass != null) {
jsExtendableArrayClass.ensureResolved(compiler);
}
jsArrayClass.ensureResolved(compiler);
jsArrayLength = compiler.lookupElementIn(
jsArrayClass, const SourceString('length'));
jsArrayRemoveLast = compiler.lookupElementIn(
jsArrayClass, const SourceString('removeLast'));
jsArrayAdd = compiler.lookupElementIn(
jsArrayClass, const SourceString('add'));
jsStringClass.ensureResolved(compiler);
jsStringLength = compiler.lookupElementIn(
jsStringClass, const SourceString('length'));
jsStringSplit = compiler.lookupElementIn(
jsStringClass, const SourceString('split'));
jsStringConcat = compiler.lookupElementIn(
jsStringClass, const SourceString('concat'));
jsStringToString = compiler.lookupElementIn(
jsStringClass, const SourceString('toString'));
for (ClassElement cls in classes) {
if (cls != null) interceptedClasses.add(cls);
}
typeLiteralClass = compiler.findHelper(const SourceString('TypeImpl'));
mapLiteralClass =
compiler.coreLibrary.find(const SourceString('LinkedHashMap'));
constMapLiteralClass =
compiler.findHelper(const SourceString('ConstantMap'));
specialOperatorEqClasses
..add(jsNullClass)
..add(jsNumberClass);
}
void addInterceptorsForNativeClassMembers(
ClassElement cls, Enqueuer enqueuer) {
if (enqueuer.isResolutionQueue) {
cls.ensureResolved(compiler);
cls.forEachMember((ClassElement classElement, Element member) {
Set<Element> set = interceptedElements.putIfAbsent(
member.name, () => new Set<Element>());
set.add(member);
},
includeSuperMembers: true);
}
}
void addInterceptors(ClassElement cls,
Enqueuer enqueuer,
TreeElements elements) {
if (enqueuer.isResolutionQueue) {
cls.ensureResolved(compiler);
cls.forEachMember((ClassElement classElement, Element member) {
Set<Element> set = interceptedElements.putIfAbsent(
member.name, () => new Set<Element>());
set.add(member);
},
includeSuperMembers: true);
}
enqueuer.registerInstantiatedClass(cls, elements);
}
void registerSpecializedGetInterceptor(Set<ClassElement> classes) {
String name = namer.getInterceptorName(getInterceptorMethod, classes);
if (classes.contains(compiler.objectClass)) {
// We can't use a specialized [getInterceptorMethod], so we make
// sure we emit the one with all checks.
specializedGetInterceptors[name] = interceptedClasses;
} else {
specializedGetInterceptors[name] = classes;
}
}
void initializeNoSuchMethod() {
// In case the emitter generates noSuchMethod calls, we need to
// make sure all [noSuchMethod] methods know they might take a
// [JsInvocationMirror] as parameter.
HTypeList types = new HTypeList(1);
types[0] = new HType.nonNullExact(
compiler.jsInvocationMirrorClass.computeType(compiler),
compiler);
argumentTypes.registerDynamicInvocation(types, new Selector.noSuchMethod());
}
void registerInstantiatedClass(ClassElement cls,
Enqueuer enqueuer,
TreeElements elements) {
if (!seenAnyClass) {
initializeNoSuchMethod();
seenAnyClass = true;
}
// Register any helper that will be needed by the backend.
if (enqueuer.isResolutionQueue) {
if (cls == compiler.intClass
|| cls == compiler.doubleClass
|| cls == compiler.numClass) {
// The backend will try to optimize number operations and use the
// `iae` helper directly.
enqueuer.registerStaticUse(
compiler.findHelper(const SourceString('iae')));
} else if (cls == compiler.listClass
|| cls == compiler.stringClass) {
// The backend will try to optimize array and string access and use the
// `ioore` and `iae` helpers directly.
enqueuer.registerStaticUse(
compiler.findHelper(const SourceString('ioore')));
enqueuer.registerStaticUse(
compiler.findHelper(const SourceString('iae')));
} else if (cls == compiler.functionClass) {
enqueuer.registerInstantiatedClass(compiler.closureClass, elements);
} else if (cls == compiler.mapClass) {
// The backend will use a literal list to initialize the entries
// of the map.
enqueuer.registerInstantiatedClass(compiler.listClass, elements);
enqueuer.registerInstantiatedClass(mapLiteralClass, elements);
enqueueInResolution(getMapMaker(), elements);
}
}
ClassElement result = null;
if (cls == compiler.stringClass) {
addInterceptors(jsStringClass, enqueuer, elements);
} else if (cls == compiler.listClass) {
addInterceptors(jsArrayClass, enqueuer, elements);
enqueuer.registerInstantiatedClass(jsFixedArrayClass, elements);
enqueuer.registerInstantiatedClass(jsExtendableArrayClass, elements);
} else if (cls == compiler.intClass) {
addInterceptors(jsIntClass, enqueuer, elements);
addInterceptors(jsNumberClass, enqueuer, elements);
} else if (cls == compiler.doubleClass) {
addInterceptors(jsDoubleClass, enqueuer, elements);
addInterceptors(jsNumberClass, enqueuer, elements);
} else if (cls == compiler.functionClass) {
addInterceptors(jsFunctionClass, enqueuer, elements);
} else if (cls == compiler.boolClass) {
addInterceptors(jsBoolClass, enqueuer, elements);
} else if (cls == compiler.nullClass) {
addInterceptors(jsNullClass, enqueuer, elements);
} else if (cls == compiler.numClass) {
addInterceptors(jsIntClass, enqueuer, elements);
addInterceptors(jsDoubleClass, enqueuer, elements);
addInterceptors(jsNumberClass, enqueuer, elements);
} else if (cls == compiler.mapClass) {
} else if (cls.isNative()) {
addInterceptorsForNativeClassMembers(cls, enqueuer);
}
if (compiler.enableTypeAssertions) {
// We need to register is checks for assignments to fields.
cls.forEachLocalMember((Element member) {
if (!member.isInstanceMember() || !member.isField()) return;
DartType type = member.computeType(compiler);
enqueuer.registerIsCheck(type, elements);
});
}
}
JavaScriptItemCompilationContext createItemCompilationContext() {
return new JavaScriptItemCompilationContext();
}
void enqueueHelpers(ResolutionEnqueuer world, TreeElements elements) {
jsIndexingBehaviorInterface =
compiler.findHelper(const SourceString('JavaScriptIndexingBehavior'));
if (jsIndexingBehaviorInterface != null) {
world.registerIsCheck(jsIndexingBehaviorInterface.computeType(compiler),
elements);
}
if (compiler.enableTypeAssertions) {
// Unconditionally register the helper that checks if the
// expression in an if/while/for is a boolean.
// TODO(ngeoffray): Should we have the resolver register those instead?
Element e =
compiler.findHelper(const SourceString('boolConversionCheck'));
if (e != null) world.addToWorkList(e);
}
}
onResolutionComplete() => rti.computeClassesNeedingRti();
void registerStringInterpolation(TreeElements elements) {
enqueueInResolution(getStringInterpolationHelper(), elements);
}
void registerCatchStatement(TreeElements elements) {
enqueueInResolution(getExceptionUnwrapper(), elements);
}
void registerThrow(TreeElements elements) {
enqueueInResolution(getThrowHelper(), elements);
}
void registerLazyField(TreeElements elements) {
enqueueInResolution(getCyclicThrowHelper(), elements);
}
void registerTypeLiteral(TreeElements elements) {
enqueueInResolution(getCreateRuntimeType(), elements);
}
void registerStackTraceInCatch(TreeElements elements) {
enqueueInResolution(getTraceFromException(), elements);
}
void registerSetRuntimeType(TreeElements elements) {
enqueueInResolution(getSetRuntimeTypeInfo(), elements);
}
void registerGetRuntimeTypeArgument(TreeElements elements) {
enqueueInResolution(getGetRuntimeTypeArgument(), elements);
}
void registerRuntimeType(TreeElements elements) {
enqueueInResolution(getSetRuntimeTypeInfo(), elements);
enqueueInResolution(getGetRuntimeTypeInfo(), elements);
enqueueInResolution(getGetRuntimeTypeArgument(), elements);
compiler.enqueuer.resolution.registerInstantiatedClass(
compiler.listClass, elements);
}
void registerTypeVariableExpression(TreeElements elements) {
registerRuntimeType(elements);
enqueueInResolution(getRuntimeTypeToString(), elements);
enqueueInResolution(getCreateRuntimeType(), elements);
}
void registerIsCheck(DartType type, Enqueuer world, TreeElements elements) {
world.registerInstantiatedClass(compiler.boolClass, elements);
bool isTypeVariable = type.kind == TypeKind.TYPE_VARIABLE;
if (!type.isRaw || isTypeVariable) {
enqueueInResolution(getSetRuntimeTypeInfo(), elements);
enqueueInResolution(getGetRuntimeTypeInfo(), elements);
enqueueInResolution(getGetRuntimeTypeArgument(), elements);
enqueueInResolution(getCheckSubtype(), elements);
if (isTypeVariable) {
enqueueInResolution(getGetObjectIsSubtype(), elements);
}
world.registerInstantiatedClass(compiler.listClass, elements);
}
// [registerIsCheck] is also called for checked mode checks, so we
// need to register checked mode helpers.
if (compiler.enableTypeAssertions) {
Element e = getCheckedModeHelper(type, typeCast: false);
if (e != null) world.addToWorkList(e);
// We also need the native variant of the check (for DOM types).
e = getNativeCheckedModeHelper(type, typeCast: false);
if (e != null) world.addToWorkList(e);
}
if (type.element.isNative()) {
// We will neeed to add the "$is" and "$as" properties on the
// JavaScript object prototype, so we make sure
// [:defineProperty:] is compiled.
world.addToWorkList(
compiler.findHelper(const SourceString('defineProperty')));
}
}
void registerAsCheck(DartType type, TreeElements elements) {
Element e = getCheckedModeHelper(type, typeCast: true);
enqueueInResolution(e, elements);
// We also need the native variant of the check (for DOM types).
e = getNativeCheckedModeHelper(type, typeCast: true);
enqueueInResolution(e, elements);
}
void registerThrowNoSuchMethod(TreeElements elements) {
enqueueInResolution(getThrowNoSuchMethod(), elements);
}
void registerThrowRuntimeError(TreeElements elements) {
enqueueInResolution(getThrowRuntimeError(), elements);
}
void registerAbstractClassInstantiation(TreeElements elements) {
enqueueInResolution(getThrowAbstractClassInstantiationError(), elements);
}
void registerFallThroughError(TreeElements elements) {
enqueueInResolution(getFallThroughError(), elements);
}
void registerSuperNoSuchMethod(TreeElements elements) {
enqueueInResolution(getCreateInvocationMirror(), elements);
enqueueInResolution(
compiler.objectClass.lookupLocalMember(Compiler.NO_SUCH_METHOD),
elements);
compiler.enqueuer.resolution.registerInstantiatedClass(
compiler.listClass, elements);
}
void registerRequiredType(DartType type, Element enclosingElement) {
/**
* If [argument] has type variables or is a type variable, this
* method registers a RTI dependency between the class where the
* type variable is defined (that is the enclosing class of the
* current element being resolved) and the class of [annotation].
* If the class of [annotation] requires RTI, then the class of
* the type variable does too.
*/
void analyzeTypeArgument(DartType annotation, DartType argument) {
if (argument == null) return;
if (argument.element.isTypeVariable()) {
ClassElement enclosing = argument.element.getEnclosingClass();
assert(enclosing == enclosingElement.getEnclosingClass());
rti.registerRtiDependency(annotation.element, enclosing);
} else if (argument is InterfaceType) {
InterfaceType type = argument;
type.typeArguments.forEach((DartType argument) {
analyzeTypeArgument(annotation, argument);
});
}
}
if (type is InterfaceType) {
InterfaceType itf = type;
itf.typeArguments.forEach((DartType argument) {
analyzeTypeArgument(type, argument);
});
}
// TODO(ngeoffray): Also handle T a (in checked mode).
}
void registerClassUsingVariableExpression(ClassElement cls) {
rti.classesUsingTypeVariableExpression.add(cls);
}
bool needsRti(ClassElement cls) {
return rti.classesNeedingRti.contains(cls) || compiler.enabledRuntimeType;
}
void enqueueInResolution(Element e, TreeElements elements) {
if (e == null) return;
ResolutionEnqueuer enqueuer = compiler.enqueuer.resolution;
enqueuer.addToWorkList(e);
elements.registerDependency(e);
}
void registerConstantMap(TreeElements elements) {
Element e = compiler.findHelper(const SourceString('ConstantMap'));
if (e != null) {
compiler.enqueuer.resolution.registerInstantiatedClass(e, elements);
}
e = compiler.findHelper(const SourceString('ConstantProtoMap'));
if (e != null) {
compiler.enqueuer.resolution.registerInstantiatedClass(e, elements);
}
}
void codegen(CodegenWorkItem work) {
Element element = work.element;
if (element.kind.category == ElementCategory.VARIABLE) {
Constant initialValue = compiler.constantHandler.compileWorkItem(work);
if (initialValue != null) {
return;
} else {
// If the constant-handler was not able to produce a result we have to
// go through the builder (below) to generate the lazy initializer for
// the static variable.
// We also need to register the use of the cyclic-error helper.
compiler.enqueuer.codegen.registerStaticUse(getCyclicThrowHelper());
}
}
HGraph graph = builder.build(work);
optimizer.optimize(work, graph, false);
if (work.allowSpeculativeOptimization
&& optimizer.trySpeculativeOptimizations(work, graph)) {
jsAst.Expression code = generator.generateBailoutMethod(work, graph);
generatedBailoutCode[element] = code;
optimizer.prepareForSpeculativeOptimizations(work, graph);
optimizer.optimize(work, graph, true);
}
jsAst.Expression code = generator.generateCode(work, graph);
generatedCode[element] = code;
invalidateAfterCodegen.forEach(eagerRecompile);
invalidateAfterCodegen.clear();
}
native.NativeEnqueuer nativeResolutionEnqueuer(Enqueuer world) {
return new native.NativeResolutionEnqueuer(world, compiler);
}
native.NativeEnqueuer nativeCodegenEnqueuer(Enqueuer world) {
return new native.NativeCodegenEnqueuer(world, compiler, emitter);
}
/**
* Unit test hook that returns code of an element as a String.
*
* Invariant: [element] must be a declaration element.
*/
String assembleCode(Element element) {
assert(invariant(element, element.isDeclaration));
return jsAst.prettyPrint(generatedCode[element], compiler).getText();
}
void assembleProgram() {
emitter.assembleProgram();
}
/**
* Documentation wanted -- johnniwinther
*
* Invariant: [element] must be a declaration element.
*/
void scheduleForRecompilation(Element element) {
assert(invariant(element, element.isDeclaration));
if (compiler.phase == Compiler.PHASE_COMPILING) {
invalidateAfterCodegen.add(element);
}
}
/**
* Register a dynamic invocation and collect the provided types for the
* named selector.
*/
void registerDynamicInvocation(HInvokeDynamic node, Selector selector) {
HTypeList providedTypes =
new HTypeList.fromDynamicInvocation(node, selector);
argumentTypes.registerDynamicInvocation(providedTypes, selector);
}
/**
* Register a static invocation and collect the provided types for the
* named selector.
*/
void registerStaticInvocation(HInvokeStatic node) {
argumentTypes.registerStaticInvocation(node);
}
/**
* Register that a static is used for something else than a direct call
* target.
*/
void registerNonCallStaticUse(HStatic node) {
argumentTypes.registerNonCallStaticUse(node);
}
/**
* Retrieve the types of the parameters used for calling the [element]
* function. The types are optimistic in the sense as they are based on the
* possible invocations of the function seen so far.
*
* Invariant: [element] must be a declaration element.
*/
HTypeList optimisticParameterTypes(
FunctionElement element,
OptionalParameterTypes defaultValueTypes) {
assert(invariant(element, element.isDeclaration));
if (element.parameterCount(compiler) == 0) return HTypeList.ALL_UNKNOWN;
return argumentTypes.parameterTypes(element, defaultValueTypes);
}
/**
* Register that the function [element] has been optimized under the
* assumptions that the types [parameterType] will be used for calling it.
* The passed [defaultValueTypes] holds the types of default values for
* the optional parameters. If this assumption fail the function will be
* scheduled for recompilation.
*
* Invariant: [element] must be a declaration element.
*/
registerParameterTypesOptimization(
FunctionElement element,
HTypeList parameterTypes,
OptionalParameterTypes defaultValueTypes) {
assert(invariant(element, element.isDeclaration));
if (element.parameterCount(compiler) == 0) return;
argumentTypes.registerOptimizedFunction(
element, parameterTypes, defaultValueTypes);
}
registerFieldTypesOptimization(FunctionElement element,
Element field,
HType type) {
fieldTypes.registerOptimizedFunction(element, field, type);
}
/**
* Documentation wanted -- johnniwinther
*
* Invariant: [element] must be a declaration element.
*/
void registerReturnType(FunctionElement element, HType returnType) {
assert(invariant(element, element.isDeclaration));
ReturnInfo info = returnInfo[element];
if (info != null) {
info.update(returnType, scheduleForRecompilation, compiler);
} else {
returnInfo[element] = new ReturnInfo(returnType);
}
}
/**
* Retrieve the return type of the function [callee]. The type is optimistic
* in the sense that is is based on the compilation of [callee]. If [callee]
* is recompiled the return type might change to someting broader. For that
* reason [caller] is registered for recompilation if this happens. If the
* function [callee] has not yet been compiled the returned type is [null].
*
* Invariant: Both [caller] and [callee] must be declaration elements.
*/
HType optimisticReturnTypesWithRecompilationOnTypeChange(
Element caller, FunctionElement callee) {
assert(invariant(callee, callee.isDeclaration));
returnInfo.putIfAbsent(callee, () => new ReturnInfo.unknownType());
ReturnInfo info = returnInfo[callee];
HType returnType = info.returnType;
if (returnType != HType.UNKNOWN && returnType != null && caller != null) {
assert(invariant(caller, caller.isDeclaration));
info.addCompiledFunction(caller);
}
return info.returnType;
}
void dumpReturnTypes() {
returnInfo.forEach((Element element, ReturnInfo info) {
if (info.returnType != HType.UNKNOWN) {
print("Inferred $element has return type ${info.returnType}");
}
});
}
void registerConstructor(Element element) {
fieldTypes.registerConstructor(element);
}
void registerFieldInitializer(Element field, HType type) {
fieldTypes.registerFieldInitializer(field, type);
}
void registerFieldConstructor(Element field, HType type) {
fieldTypes.registerFieldConstructor(field, type);
}
void registerFieldSetter(FunctionElement element, Element field, HType type) {
fieldTypes.registerFieldSetter(element, field, type);
}
void addedDynamicSetter(Selector setter, HType type) {
fieldTypes.addedDynamicSetter(setter, type);
}
HType optimisticFieldType(Element element) {
return fieldTypes.optimisticFieldType(element);
}
/**
* Returns the checked mode helper that will be needed to do a type check/type
* cast on [type] at runtime. Note that this method is being called both by
* the resolver with interface types (int, String, ...), and by the SSA
* backend with implementation types (JSInt, JSString, ...).
*/
Element getCheckedModeHelper(DartType type, {bool typeCast}) {
return compiler.findHelper(getCheckedModeHelperName(
type, typeCast: typeCast, nativeCheckOnly: false));
}
/**
* Returns the native checked mode helper that will be needed to do a type
* check/type cast on [type] at runtime. If no native helper exists for
* [type], [:null:] is returned.
*/
Element getNativeCheckedModeHelper(DartType type, {bool typeCast}) {
SourceString sourceName = getCheckedModeHelperName(
type, typeCast: typeCast, nativeCheckOnly: true);
if (sourceName == null) return null;
return compiler.findHelper(sourceName);
}
/**
* Returns the name of the type check/type cast helper method for [type]. If
* [nativeCheckOnly] is [:true:], only names for native helpers are returned.
*/
SourceString getCheckedModeHelperName(DartType type,
{bool typeCast,
bool nativeCheckOnly}) {
Element element = type.element;
bool nativeCheck = nativeCheckOnly ||
emitter.nativeEmitter.requiresNativeIsCheck(element);
if (type.isMalformed) {
// Check for malformed types first, because the type may be a list type
// with a malformed argument type.
if (nativeCheckOnly) return null;
return typeCast
? const SourceString('malformedTypeCast')
: const SourceString('malformedTypeCheck');
} else if (type == compiler.types.voidType) {
assert(!typeCast); // Cannot cast to void.
if (nativeCheckOnly) return null;
return const SourceString('voidTypeCheck');
} else if (element == jsStringClass || element == compiler.stringClass) {
if (nativeCheckOnly) return null;
return typeCast
? const SourceString("stringTypeCast")
: const SourceString('stringTypeCheck');
} else if (element == jsDoubleClass || element == compiler.doubleClass) {
if (nativeCheckOnly) return null;
return typeCast
? const SourceString("doubleTypeCast")
: const SourceString('doubleTypeCheck');
} else if (element == jsNumberClass || element == compiler.numClass) {
if (nativeCheckOnly) return null;
return typeCast
? const SourceString("numTypeCast")
: const SourceString('numTypeCheck');
} else if (element == jsBoolClass || element == compiler.boolClass) {
if (nativeCheckOnly) return null;
return typeCast
? const SourceString("boolTypeCast")
: const SourceString('boolTypeCheck');
} else if (element == jsFunctionClass ||
element == compiler.functionClass) {
if (nativeCheckOnly) return null;
return typeCast
? const SourceString("functionTypeCast")
: const SourceString('functionTypeCheck');
} else if (element == jsIntClass || element == compiler.intClass) {
if (nativeCheckOnly) return null;
return typeCast ?
const SourceString("intTypeCast") :
const SourceString('intTypeCheck');
} else if (Elements.isNumberOrStringSupertype(element, compiler)) {
if (nativeCheck) {
return typeCast
? const SourceString("numberOrStringSuperNativeTypeCast")
: const SourceString('numberOrStringSuperNativeTypeCheck');
} else {
return typeCast
? const SourceString("numberOrStringSuperTypeCast")
: const SourceString('numberOrStringSuperTypeCheck');
}
} else if (Elements.isStringOnlySupertype(element, compiler)) {
if (nativeCheck) {
return typeCast
? const SourceString("stringSuperNativeTypeCast")
: const SourceString('stringSuperNativeTypeCheck');
} else {
return typeCast
? const SourceString("stringSuperTypeCast")
: const SourceString('stringSuperTypeCheck');
}
} else if (element == compiler.listClass || element == jsArrayClass) {
if (nativeCheckOnly) return null;
return typeCast
? const SourceString("listTypeCast")
: const SourceString('listTypeCheck');
} else {
if (Elements.isListSupertype(element, compiler)) {
if (nativeCheck) {
return typeCast
? const SourceString("listSuperNativeTypeCast")
: const SourceString('listSuperNativeTypeCheck');
} else {
return typeCast
? const SourceString("listSuperTypeCast")
: const SourceString('listSuperTypeCheck');
}
} else {
if (nativeCheck) {
return typeCast
? const SourceString("callTypeCast")
: const SourceString('callTypeCheck');
} else {
return typeCast
? const SourceString("propertyTypeCast")
: const SourceString('propertyTypeCheck');
}
}
}
}
void dumpInferredTypes() {
print("Inferred argument types:");
print("------------------------");
argumentTypes.dump();
print("");
print("Inferred return types:");
print("----------------------");
dumpReturnTypes();
print("");
print("Inferred field types:");
print("------------------------");
fieldTypes.dump();
print("");
}
Element getExceptionUnwrapper() {
return compiler.findHelper(const SourceString('unwrapException'));
}
Element getThrowRuntimeError() {
return compiler.findHelper(const SourceString('throwRuntimeError'));
}
Element getThrowMalformedSubtypeError() {
return compiler.findHelper(
const SourceString('throwMalformedSubtypeError'));
}
Element getThrowAbstractClassInstantiationError() {
return compiler.findHelper(
const SourceString('throwAbstractClassInstantiationError'));
}
Element getStringInterpolationHelper() {
return compiler.findHelper(const SourceString('S'));
}
Element getThrowHelper() {
return compiler.findHelper(const SourceString(r'$throw'));
}
Element getClosureConverter() {
return compiler.findHelper(const SourceString('convertDartClosureToJS'));
}
Element getTraceFromException() {
return compiler.findHelper(const SourceString('getTraceFromException'));
}
Element getMapMaker() {
return compiler.findHelper(const SourceString('makeLiteralMap'));
}
Element getSetRuntimeTypeInfo() {
return compiler.findHelper(const SourceString('setRuntimeTypeInfo'));
}
Element getGetRuntimeTypeInfo() {
return compiler.findHelper(const SourceString('getRuntimeTypeInfo'));
}
Element getGetRuntimeTypeArgument() {
return compiler.findHelper(const SourceString('getRuntimeTypeArgument'));
}
Element getRuntimeTypeToString() {
return compiler.findHelper(const SourceString('runtimeTypeToString'));
}
Element getCheckSubtype() {
return compiler.findHelper(const SourceString('checkSubtype'));
}
Element getGetObjectIsSubtype() {
return compiler.findHelper(const SourceString('objectIsSubtype'));
}
Element getThrowNoSuchMethod() {
return compiler.findHelper(const SourceString('throwNoSuchMethod'));
}
Element getCreateRuntimeType() {
return compiler.findHelper(const SourceString('createRuntimeType'));
}
Element getFallThroughError() {
return compiler.findHelper(const SourceString("getFallThroughError"));
}
Element getCreateInvocationMirror() {
return compiler.findHelper(Compiler.CREATE_INVOCATION_MIRROR);
}
Element getCyclicThrowHelper() {
return compiler.findHelper(const SourceString("throwCyclicInit"));
}
/**
* Remove [element] from the set of generated code, and put it back
* into the worklist.
*
* Invariant: [element] must be a declaration element.
*/
void eagerRecompile(Element element) {
assert(invariant(element, element.isDeclaration));
generatedCode.remove(element);
generatedBailoutCode.remove(element);
compiler.enqueuer.codegen.addToWorkList(element);
}
bool isNullImplementation(ClassElement cls) {
return cls == jsNullClass;
}
ClassElement get intImplementation => jsIntClass;
ClassElement get doubleImplementation => jsDoubleClass;
ClassElement get numImplementation => jsNumberClass;
ClassElement get stringImplementation => jsStringClass;
ClassElement get listImplementation => jsArrayClass;
ClassElement get constListImplementation => jsArrayClass;
ClassElement get fixedListImplementation => jsFixedArrayClass;
ClassElement get growableListImplementation => jsExtendableArrayClass;
ClassElement get mapImplementation => mapLiteralClass;
ClassElement get constMapImplementation => constMapLiteralClass;
ClassElement get functionImplementation => jsFunctionClass;
ClassElement get typeImplementation => typeLiteralClass;
ClassElement get boolImplementation => jsBoolClass;
ClassElement get nullImplementation => jsNullClass;
}