Chromium Code Reviews| Index: lib/compiler/implementation/js_backend/backend.dart |
| diff --git a/lib/compiler/implementation/js_backend/backend.dart b/lib/compiler/implementation/js_backend/backend.dart |
| index 9006953008a06a8e154a994fd8e3534c36f98079..2994708845817a8f9e37c49fdc72b2b462d2e16b 100644 |
| --- a/lib/compiler/implementation/js_backend/backend.dart |
| +++ b/lib/compiler/implementation/js_backend/backend.dart |
| @@ -223,6 +223,114 @@ class HTypeList { |
| allUnknown ? "HTypeList.ALL_UNKNOWN" : "HTypeList $types"; |
| } |
| +class FieldTypesRegistry { |
| + final JavaScriptBackend backend; |
| + final Map<Element, HType> fieldInitializerTypeMap; |
| + final Map<Element, HType> fieldConstructorTypeMap; |
| + final Map<Element, HType> fieldTypeMap; |
|
ngeoffray
2012/09/20 14:43:01
Why do you need three maps and not one?
Søren Gjesse
2012/09/21 13:49:44
I need to separate the types set by the initialize
|
| + final Map<Element, FunctionSet> optimizedFunctions; |
| + |
| + FieldTypesRegistry(JavaScriptBackend backend) |
| + : fieldInitializerTypeMap = new Map<Element, HType>(), |
| + fieldConstructorTypeMap = new Map<Element, HType>(), |
| + fieldTypeMap = new Map<Element, HType>(), |
| + optimizedFunctions = new Map<Element, FunctionSet>(), |
| + this.backend = backend; |
| + |
| + Compiler get compiler => backend.compiler; |
| + |
| + void scheduleRecompilation(Element field) { |
| + FunctionSet optimized = optimizedFunctions[field]; |
| + if (optimized != null) { |
| + optimized.forEach(backend.scheduleForRecompilation); |
| + } |
| + optimizedFunctions.remove(field); |
| + } |
| + |
| + void registerFieldType(Map<Element, HType> typeMap, |
| + Element field, |
| + HType type) { |
| + assert(field.isField()); |
| + HType oldType = typeMap[field]; |
| + HType newType; |
| + |
| + if (oldType != null) { |
| + newType = oldType.union(type); |
| + } else { |
| + newType = type; |
| + } |
| + typeMap[field] = newType; |
| + if (oldType != newType) { |
| + scheduleRecompilation(field); |
| + } |
| + } |
| + |
| + void registerFieldInitializer(Element field, HType type) { |
| + registerFieldType(fieldInitializerTypeMap, field, type); |
| + } |
| + |
| + void registerFieldConstructor(Element field, HType type) { |
| + registerFieldType(fieldConstructorTypeMap, field, type); |
| + } |
| + |
| + void registerFieldSetter(HFieldSet node, HTypeMap types) { |
| + Element field = node.element; |
| + HType initializerType = fieldInitializerTypeMap[field]; |
| + HType constructorType = fieldConstructorTypeMap[field]; |
| + HType setterType = fieldTypeMap[field]; |
| + HType type = types[node.value]; |
| + if (type == HType.UNKNOWN && |
| + initializerType == null && |
| + constructorType == null && |
| + setterType == null) { |
|
ngeoffray
2012/09/20 14:43:01
Not sure what all these checks really mean. If the
Søren Gjesse
2012/09/21 13:49:44
This checks whether there is any type information
|
| + return; |
| + } |
| + registerFieldType(fieldTypeMap, field, type); |
| + } |
| + |
| + void addedDynamicSetter(Selector setter) { |
| + optimizedFunctions.getKeys().forEach((Element field) { |
|
ngeoffray
2012/09/20 14:43:01
I would do optimizedFunctions.forEach((Element fie
Søren Gjesse
2012/09/21 13:49:44
Done.
|
| + if (field.name == setter.name) { |
| + if (compiler.codegenWorld.hasInvokedSetter(field, compiler)) { |
| + scheduleRecompilation(field); |
| + } |
| + } |
| + }); |
| + } |
| + |
| + HType optimisticFieldType(Element field) { |
| + assert(field.isField()); |
| + assert(field.isMember()); |
| + if (compiler.codegenWorld.hasInvokedSetter(field, compiler)) { |
| + return HType.UNKNOWN; |
| + } |
| + HType initializerType = fieldInitializerTypeMap[field]; |
| + HType constructorType = fieldConstructorTypeMap[field]; |
| + if (initializerType === null && constructorType === null) { |
| + return HType.UNKNOWN; |
| + } |
| + HType result = constructorType != null ? constructorType : initializerType; |
| + HType type = fieldTypeMap[field]; |
| + if (type !== null) result = result.union(type); |
| + return result; |
| + } |
| + |
| + void registerOptimization(FunctionElement element, |
|
ngeoffray
2012/09/20 14:43:01
registerOptimization -> registerOptimizedFunction
Søren Gjesse
2012/09/21 13:49:44
Done.
|
| + Element field, |
| + HType type) { |
| + assert(field.isField()); |
| + optimizedFunctions.putIfAbsent( |
|
ngeoffray
2012/09/20 14:43:01
FunctionSet set = optimizedFunctions...
set.add(el
Søren Gjesse
2012/09/21 13:49:44
This pattern is used in many places. Is it the per
|
| + field, () => new FunctionSet(backend.compiler)); |
| + optimizedFunctions[field].add(element); |
| + } |
| + |
| + void dump() { |
| + optimizedFunctions.getKeys().forEach((Element field) { |
| + print("Inferred $field has type ${optimisticFieldType(field)}"); |
| + }); |
| + } |
| +} |
| + |
| class ArgumentTypesRegistry { |
| final JavaScriptBackend backend; |
| final Map<Element, HTypeList> staticTypeMap; |
| @@ -421,24 +529,18 @@ class JavaScriptBackend extends Backend { |
| */ |
| ClassElement jsIndexingBehaviorInterface; |
| - final Map<Element, Map<Element, HType>> fieldInitializers; |
| - final Map<Element, Map<Element, HType>> fieldConstructorSetters; |
| - final Map<Element, Map<Element, HType>> fieldSettersType; |
| - |
| final Map<Element, ReturnInfo> returnInfo; |
| final List<Element> invalidateAfterCodegen; |
| ArgumentTypesRegistry argumentTypes; |
| + FieldTypesRegistry fieldTypes; |
| List<CompilerTask> get tasks { |
| return <CompilerTask>[builder, optimizer, generator, emitter]; |
| } |
| JavaScriptBackend(Compiler compiler, bool generateSourceMap) |
| - : fieldInitializers = new Map<Element, Map<Element, HType>>(), |
| - fieldConstructorSetters = new Map<Element, Map<Element, HType>>(), |
| - fieldSettersType = new Map<Element, Map<Element, HType>>(), |
| - namer = new Namer(compiler), |
| + : namer = new Namer(compiler), |
| returnInfo = new Map<Element, ReturnInfo>(), |
| invalidateAfterCodegen = new List<Element>(), |
| super(compiler, constantSystem: JAVA_SCRIPT_CONSTANT_SYSTEM) { |
| @@ -447,6 +549,7 @@ class JavaScriptBackend extends Backend { |
| optimizer = new SsaOptimizerTask(this); |
| generator = new SsaCodeGeneratorTask(this); |
| argumentTypes = new ArgumentTypesRegistry(this); |
| + fieldTypes = new FieldTypesRegistry(this); |
| } |
| Element get cyclicThrowHelper { |
| @@ -512,106 +615,6 @@ class JavaScriptBackend extends Backend { |
| emitter.assembleProgram(); |
| } |
| - void updateFieldInitializers(Element field, HType propagatedType) { |
| - assert(field.isField()); |
| - assert(field.isMember()); |
| - Map<Element, HType> fields = |
| - fieldInitializers.putIfAbsent( |
| - field.getEnclosingClass(), () => new Map<Element, HType>()); |
| - if (!fields.containsKey(field)) { |
| - fields[field] = propagatedType; |
| - } else { |
| - fields[field] = fields[field].union(propagatedType); |
| - } |
| - } |
| - |
| - HType typeFromInitializersSoFar(Element field) { |
| - assert(field.isField()); |
| - assert(field.isMember()); |
| - if (!fieldInitializers.containsKey(field.getEnclosingClass())) { |
| - return HType.CONFLICTING; |
| - } |
| - Map<Element, HType> fields = fieldInitializers[field.getEnclosingClass()]; |
| - return fields[field]; |
| - } |
| - |
| - void updateFieldConstructorSetters(Element field, HType type) { |
| - assert(field.isField()); |
| - assert(field.isMember()); |
| - Map<Element, HType> fields = |
| - fieldConstructorSetters.putIfAbsent( |
| - field.getEnclosingClass(), () => new Map<Element, HType>()); |
| - if (!fields.containsKey(field)) { |
| - fields[field] = type; |
| - } else { |
| - fields[field] = fields[field].union(type); |
| - } |
| - } |
| - |
| - // Check if this field is set in the constructor body. |
| - bool hasConstructorBodyFieldSetter(Element field) { |
| - ClassElement enclosingClass = field.getEnclosingClass(); |
| - if (!fieldConstructorSetters.containsKey(enclosingClass)) { |
| - return false; |
| - } |
| - return fieldConstructorSetters[enclosingClass][field] != null; |
| - } |
| - |
| - // Provide an optimistic estimate of the type of a field after construction. |
| - // If the constructor body has setters for fields returns HType.UNKNOWN. |
| - // This only takes the initializer lists and field assignments in the |
| - // constructor body into account. The constructor body might have method calls |
| - // that could alter the field. |
| - HType optimisticFieldTypeAfterConstruction(Element field) { |
| - assert(field.isField()); |
| - assert(field.isMember()); |
| - |
| - ClassElement classElement = field.getEnclosingClass(); |
| - if (hasConstructorBodyFieldSetter(field)) { |
| - // If there are field setters but there is only constructor then the type |
| - // of the field is determined by the assignments in the constructor |
| - // body. |
| - var constructors = classElement.constructors; |
| - if (constructors.head !== null && constructors.tail.isEmpty()) { |
| - return fieldConstructorSetters[classElement][field]; |
| - } else { |
| - return HType.UNKNOWN; |
| - } |
| - } else if (fieldInitializers.containsKey(classElement)) { |
| - HType type = fieldInitializers[classElement][field]; |
| - return type == null ? HType.CONFLICTING : type; |
| - } else { |
| - return HType.CONFLICTING; |
| - } |
| - } |
| - |
| - void updateFieldSetters(Element field, HType type) { |
| - assert(field.isField()); |
| - assert(field.isMember()); |
| - Map<Element, HType> fields = |
| - fieldSettersType.putIfAbsent( |
| - field.getEnclosingClass(), () => new Map<Element, HType>()); |
| - if (!fields.containsKey(field)) { |
| - fields[field] = type; |
| - } else { |
| - fields[field] = fields[field].union(type); |
| - } |
| - } |
| - |
| - // Returns the type that field setters are setting the field to based on what |
| - // have been seen during compilation so far. |
| - HType fieldSettersTypeSoFar(Element field) { |
| - assert(field.isField()); |
| - assert(field.isMember()); |
| - ClassElement enclosingClass = field.getEnclosingClass(); |
| - if (!fieldSettersType.containsKey(enclosingClass)) { |
| - return HType.CONFLICTING; |
| - } |
| - Map<Element, HType> fields = fieldSettersType[enclosingClass]; |
| - if (!fields.containsKey(field)) return HType.CONFLICTING; |
| - return fields[field]; |
| - } |
| - |
| void scheduleForRecompilation(Element element) { |
| if (compiler.phase == Compiler.PHASE_COMPILING) { |
| invalidateAfterCodegen.add(element); |
| @@ -672,6 +675,12 @@ class JavaScriptBackend extends Backend { |
| element, parameterTypes, defaultValueTypes); |
| } |
| + registerFieldTypesOptimization(FunctionElement element, |
| + Element field, |
| + HType type) { |
| + fieldTypes.registerOptimization(element, field, type); |
| + } |
| + |
| void registerReturnType(FunctionElement element, HType returnType) { |
| ReturnInfo info = returnInfo[element]; |
| if (info != null) { |
| @@ -706,6 +715,26 @@ class JavaScriptBackend extends Backend { |
| }); |
| } |
| + void registerFieldInitializer(Element field, HType type) { |
| + fieldTypes.registerFieldInitializer(field, type); |
| + } |
| + |
| + void registerFieldConstructor(Element field, HType type) { |
| + fieldTypes.registerFieldConstructor(field, type); |
| + } |
| + |
| + void registerFieldSetter(HFieldSet node, HTypeMap types) { |
| + fieldTypes.registerFieldSetter(node, types); |
| + } |
| + |
| + void addedDynamicSetter(Selector setter) { |
| + fieldTypes.addedDynamicSetter(setter); |
| + } |
| + |
| + HType optimisticFieldType(Element element) { |
| + return fieldTypes.optimisticFieldType(element); |
| + } |
| + |
| SourceString getCheckedModeHelper(DartType type) { |
| Element element = type.element; |
| bool nativeCheck = |