Chromium Code Reviews| Index: pkg/compiler/lib/src/js_emitter/metadata_collector.dart |
| diff --git a/pkg/compiler/lib/src/js_emitter/metadata_collector.dart b/pkg/compiler/lib/src/js_emitter/metadata_collector.dart |
| index a2501fd668653c3e292171c825ec0934d74f2d3f..4ec5161055f4126c64055792d5bc35ef53b71a77 100644 |
| --- a/pkg/compiler/lib/src/js_emitter/metadata_collector.dart |
| +++ b/pkg/compiler/lib/src/js_emitter/metadata_collector.dart |
| @@ -4,27 +4,125 @@ |
| part of dart2js.js_emitter; |
| +abstract class _MetadataEntry extends jsAst.TokenNumber implements Comparable { |
| + jsAst.Expression get entry; |
| + int get value; |
| + int get _rc; |
|
karlklose
2015/05/28 09:39:53
Could you add comments for this class and _rc?
herhut
2015/06/01 12:09:42
Done.
|
| + |
| + markSeen(); |
| +} |
| + |
| +class _BoundMetadataEntry extends _MetadataEntry { |
| + int _value = -1; |
| + int _rc = 0; |
| + final jsAst.Expression entry; |
| + |
| + _BoundMetadataEntry(this.entry); |
| + |
| + bool get isFinalized => _value != -1; |
| + |
| + finalize(int value) { |
| + assert(!isFinalized); |
| + _value = value; |
| + } |
| + |
| + int get value { |
| + if (!isFinalized) print ("OHA! I have no value!"); |
|
karlklose
2015/05/28 09:39:53
Remove/replace debug print.
herhut
2015/06/01 12:09:42
Done.
|
| + assert(isFinalized); |
| + return _value; |
| + } |
| + |
| + markSeen() => _rc++; |
| + |
| + int compareTo(_MetadataEntry other) => other._rc - this._rc; |
| +} |
| + |
| +abstract class Placeholder implements jsAst.TokenNumber { |
| + bind(_MetadataEntry entry); |
| +} |
| + |
| +class _ForwardingMetadataEntry extends _MetadataEntry implements Placeholder { |
| + _MetadataEntry _forwardTo; |
| + var debugData; |
| + var trace; |
| + |
| + bool get isBound => _forwardTo != null; |
| + |
| + _ForwardingMetadataEntry([this.debugData, this.trace]); |
| + |
| + _MetadataEntry get forwardTo { |
| + assert(isBound); |
| + return _forwardTo; |
| + } |
| + |
| + jsAst.Expression get entry { |
| + if (isBound) return forwardTo.entry; |
| + return new jsAst.LiteralString('"BOGUS: $debugData"'); |
|
karlklose
2015/05/28 09:39:53
assert(isBound) or throw?
herhut
2015/06/01 12:09:42
Done.
|
| + } |
| + |
| + int get value { |
| + if (isBound) return forwardTo.value; |
| + return -336699; |
|
sra1
2015/05/27 19:38:51
Should this be
assert(isBound);
?
herhut
2015/06/01 12:09:42
Done.
|
| + } |
| + |
| + int get _rc => forwardTo._rc; |
| + markSeen() { |
| + if (isBound) forwardTo.markSeen(); |
| + } |
| + int compareTo(other) => forwardTo.compareTo(other); |
| + |
| + bind(_MetadataEntry entry) { |
| + assert(!isBound); |
| + _forwardTo = entry; |
| + } |
| +} |
| + |
| +class _MetadataList extends jsAst.TokenExpression { |
| + jsAst.Expression _value; |
| + |
| + void setExpression(jsAst.Expression value) { |
| + assert(_value == null); |
| + _value = value; |
| + } |
| + |
| + jsAst.Expression get value { |
| + assert(_value != null); |
| + return _value; |
| + } |
| +} |
| + |
| class MetadataCollector { |
| final Compiler _compiler; |
| final Emitter _emitter; |
| - /// A list of JS expressions that represent metadata, parameter names and |
| - /// type variable types. |
| - final List<jsAst.Expression> globalMetadata = <jsAst.Expression>[]; |
| + /// A token for a list of expressions that represent metadata, parameter names |
| + /// and type variable types. |
| + final _MetadataList _globalMetadata = new _MetadataList(); |
| + |
| + jsAst.Expression get globalMetadata => _globalMetadata; |
| + |
| + final jsAst.Expression _globalMetadataToken = new _MetadataList(); |
| /// A map used to canonicalize the entries of globalMetadata. |
| - final Map<String, int> _globalMetadataMap = <String, int>{}; |
| + Map<String, _BoundMetadataEntry> _globalMetadataMap; |
| - /// A map with lists of JS expressions, one list for each output unit. The |
| - /// entries represent types including function types and typedefs. |
| - final Map<OutputUnit, List<jsAst.Expression>> types = |
| - <OutputUnit, List<jsAst.Expression>>{}; |
| + /// A map with a token for a lists of JS expressions, one token for each |
| + /// output unit. Once finalized, the entries represent types including |
| + /// function types and typedefs. |
| + Map<OutputUnit, _MetadataList> _typesTokens = |
| + new Map<OutputUnit, _MetadataList>(); |
| + |
| + jsAst.Expression getTypesForOutputUnit(OutputUnit outputUnit) { |
| + return _typesTokens.putIfAbsent(outputUnit, () => new _MetadataList()); |
| + } |
| /// A map used to canonicalize the entries of types. |
| - final Map<OutputUnit, Map<String, int>> _typesMap = |
| - <OutputUnit, Map<String, int>>{}; |
| + Map<OutputUnit, Map<DartType, _BoundMetadataEntry>> _typesMap = |
| + <OutputUnit, Map<DartType, _BoundMetadataEntry>>{}; |
| - MetadataCollector(this._compiler, this._emitter); |
| + MetadataCollector(this._compiler, this._emitter) { |
| + _globalMetadataMap = new Map<String, _BoundMetadataEntry>(); |
| + } |
| JavaScriptBackend get _backend => _compiler.backend; |
| TypeVariableHandler get _typeVariableHandler => _backend.typeVariableHandler; |
| @@ -64,51 +162,72 @@ class MetadataCollector { |
| }); |
| } |
| - List<int> reifyDefaultArguments(FunctionElement function) { |
| + List<jsAst.TokenNumber> reifyDefaultArguments(FunctionElement function) { |
| FunctionSignature signature = function.functionSignature; |
| if (signature.optionalParameterCount == 0) return const []; |
| - List<int> defaultValues = <int>[]; |
| + List<jsAst.TokenNumber> defaultValues = <jsAst.TokenNumber>[]; |
| for (ParameterElement element in signature.optionalParameters) { |
| ConstantExpression constant = |
| _backend.constants.getConstantForVariable(element); |
| - jsAst.Expression expression = (constant == null) |
| - ? null |
| - : _emitter.constantReference(constant.value); |
| - defaultValues.add(addGlobalMetadata(expression)); |
| + jsAst.Expression representation = (constant == null) |
| + ? new jsAst.LiteralNull() |
| + : _emitter.constantReference(constant.value);; |
| + defaultValues.add(_addGlobalMetadata(representation)); |
| } |
| return defaultValues; |
| } |
| - int reifyMetadata(MetadataAnnotation annotation) { |
| + jsAst.Expression reifyMetadata(MetadataAnnotation annotation) { |
| ConstantExpression constant = |
| _backend.constants.getConstantForMetadata(annotation); |
| if (constant == null) { |
| _compiler.internalError(annotation, 'Annotation value is null.'); |
| - return -1; |
| + return null; |
| } |
| - return addGlobalMetadata(_emitter.constantReference(constant.value)); |
| + return _addGlobalMetadata(_emitter.constantReference(constant.value)); |
| } |
| - int reifyType(DartType type, {bool ignoreTypeVariables: false}) { |
| + jsAst.Expression reifyType(DartType type, {ignoreTypeVariables: false}) { |
| return reifyTypeForOutputUnit(type, |
| _compiler.deferredLoadTask.mainOutputUnit, |
| ignoreTypeVariables: ignoreTypeVariables); |
| } |
| - int reifyTypeForOutputUnit(DartType type, OutputUnit outputUnit, |
| - {bool ignoreTypeVariables: false}) { |
| - jsAst.Expression representation = |
| - _backend.rti.getTypeRepresentation( |
| - type, |
| - (variable) { |
| - if (ignoreTypeVariables) return new jsAst.LiteralNull(); |
| - return js.number( |
| - _typeVariableHandler.reifyTypeVariable( |
| - variable.element)); |
| - }, |
| - (TypedefType typedef) { |
| - return _backend.isAccessibleByReflection(typedef.element); |
| - }); |
| + jsAst.Expression reifyTypeForOutputUnit(DartType type, |
| + OutputUnit outputUnit, |
| + {ignoreTypeVariables: false}) { |
| + return addTypeInOutputUnit(type, outputUnit, |
| + ignoreTypeVariables: ignoreTypeVariables); |
| + } |
| + |
| + jsAst.Expression reifyName(String name) { |
| + return _addGlobalMetadata(js.string(name)); |
| + } |
| + |
| + jsAst.Expression reifyExpression(jsAst.Expression expression) { |
| + return _addGlobalMetadata(expression); |
| + } |
| + |
| + Placeholder getMetadataPlaceholder([debug]) => new _ForwardingMetadataEntry(debug); |
|
sra1
2015/05/27 19:38:50
line length
herhut
2015/06/01 12:09:41
Done.
|
| + |
| + _MetadataEntry _addGlobalMetadata(jsAst.Node node) { |
| + String printed = jsAst.prettyPrint(node, _compiler).getText(); |
| + return _globalMetadataMap.putIfAbsent(printed, () { |
| + return new _BoundMetadataEntry(node); |
| + }); |
| + } |
| + |
| + jsAst.Expression _computeTypeRepresentation(DartType type, |
| + {ignoreTypeVariables: false}) { |
| + jsAst.Expression representation = _backend.rti.getTypeRepresentation( |
| + type, |
| + (variable) { |
| + if (ignoreTypeVariables) return new jsAst.LiteralNull(); |
| + return _typeVariableHandler.reifyTypeVariable(variable.element); |
| + }, |
| + (TypedefType typedef) { |
| + return _backend.isAccessibleByReflection(typedef.element); |
| + }); |
| if (representation is jsAst.LiteralString) { |
| // We don't want the representation to be a string, since we use |
| @@ -117,42 +236,27 @@ class MetadataCollector { |
| NO_LOCATION_SPANNABLE, 'reified types should not be strings.'); |
| } |
| - return addTypeInOutputUnit(representation, outputUnit); |
| - } |
| + return representation; |
| - int reifyName(String name) { |
| - return addGlobalMetadata(js('"$name"')); |
| } |
| - int addGlobalMetadata(jsAst.Expression expression) { |
| - // TODO(sigmund): consider adding an effient way to compare expressions |
| - String string = jsAst.prettyPrint(expression, _compiler).getText(); |
| - return _globalMetadataMap.putIfAbsent(string, () { |
| - globalMetadata.add(expression); |
| - return globalMetadata.length - 1; |
| - }); |
| - } |
| - |
| - int addTypeInOutputUnit(jsAst.Expression type, OutputUnit outputUnit) { |
| - String string = jsAst.prettyPrint(type, _compiler).getText(); |
| + jsAst.Expression addTypeInOutputUnit(DartType type, |
| + OutputUnit outputUnit, |
| + {ignoreTypeVariables: false}) { |
| if (_typesMap[outputUnit] == null) { |
| - _typesMap[outputUnit] = <String, int>{}; |
| + _typesMap[outputUnit] = new Map<DartType, _BoundMetadataEntry>(); |
| } |
| - return _typesMap[outputUnit].putIfAbsent(string, () { |
| - |
| - if (types[outputUnit] == null) { |
| - types[outputUnit] = <jsAst.Expression>[]; |
| - } |
| - |
| - types[outputUnit].add(type); |
| - return types[outputUnit].length - 1; |
| + return _typesMap[outputUnit].putIfAbsent(type, () { |
| + return new _BoundMetadataEntry( |
| + _computeTypeRepresentation(type, |
| + ignoreTypeVariables: ignoreTypeVariables)); |
| }); |
| } |
| - List<int> computeMetadata(FunctionElement element) { |
| + List<jsAst.TokenNumber> computeMetadata(FunctionElement element) { |
| return _compiler.withCurrentElement(element, () { |
| - if (!_mustEmitMetadataFor(element)) return const <int>[]; |
| - List<int> metadata = <int>[]; |
| + if (!_mustEmitMetadataFor(element)) return const <jsAst.TokenNumber>[]; |
| + List<jsAst.TokenNumber> metadata = <jsAst.TokenNumber>[]; |
| Link link = element.metadata; |
| // TODO(ahe): Why is metadata sometimes null? |
| if (link != null) { |
| @@ -163,4 +267,81 @@ class MetadataCollector { |
| return metadata; |
| }); |
| } |
| + |
| + void countTokensInProgram(jsAst.Program program) { |
| + TokenCounter visitor = new TokenCounter(); |
| + visitor.countTokens(program); |
| + } |
| + |
| + void finalizeTokens() { |
| + void checkTokensInTypes(OutputUnit outputUnit, entries) { |
| + UnBoundDebugger debugger = new UnBoundDebugger(outputUnit); |
| + for (_BoundMetadataEntry entry in entries) { |
| + if (entry._rc == 0) { |
|
karlklose
2015/05/28 09:39:53
Add/use helper in _BoundMetadataEntry?
herhut
2015/06/01 12:09:42
Done.
|
| + print("UNUSED ${entry.hashCode}"); |
|
karlklose
2015/05/28 09:39:53
Remove debug print?
herhut
2015/06/01 12:09:41
Done.
|
| + continue; |
| + } |
| + debugger.findUnboundPlaceholders(entry.entry); |
| + } |
| + } |
| + void countTokensInTypes(Iterable<_BoundMetadataEntry> entries) { |
| + TokenCounter counter = new TokenCounter(); |
| + entries.where((_BoundMetadataEntry e) => e._rc > 0) |
| + .map((_BoundMetadataEntry e) => e.entry) |
| + .forEach(counter.countTokens); |
| + } |
| + |
| + jsAst.ArrayInitializer finalizeMap(Map<dynamic, _BoundMetadataEntry> map) { |
|
sra1
2015/05/27 19:38:50
Indentation
herhut
2015/06/01 12:09:42
Done.
|
| + bool isUsed(_BoundMetadataEntry entry) => entry._rc > 0; |
|
karlklose
2015/05/28 09:39:53
Move this helper to _BoundMetaDataEntry?
herhut
2015/06/01 12:09:42
Done.
|
| + List<_BoundMetadataEntry> entries = map.values.where(isUsed).toList(); |
| + entries.sort(); |
|
sra1
2015/05/27 19:38:50
Sort is not stable.
Equivalence classes by referen
herhut
2015/06/01 12:09:41
True but this is not a regression. The original im
|
| + |
| + int count = 0; |
| + for (_BoundMetadataEntry entry in entries) { |
| + entry.finalize(count++); |
| + } |
|
sra1
2015/05/27 20:30:15
Longer term I think we need to be more sophisticat
herhut
2015/06/01 12:09:42
Acknowledged.
|
| + |
| + List<jsAst.Node> values = |
| + entries.map((_BoundMetadataEntry e) => e.entry).toList(); |
| + |
| + return new jsAst.ArrayInitializer(values); |
| + } |
| + |
| + _globalMetadata.setExpression(finalizeMap(_globalMetadataMap)); |
| + |
| + _typesTokens.forEach((OutputUnit outputUnit, _MetadataList token) { |
| + Map typesMap = _typesMap[outputUnit]; |
| + if (typesMap != null) { |
| + checkTokensInTypes(outputUnit, typesMap.values); |
| + countTokensInTypes(typesMap.values); |
| + token.setExpression(finalizeMap(typesMap)); |
| + } else { |
| + token.setExpression(new jsAst.ArrayInitializer([])); |
| + } |
| + }); |
| + } |
| } |
| + |
| +class TokenCounter extends jsAst.BaseVisitor { |
| + visitTokenNumber(jsAst.TokenNumber token) { |
| + if (token is _MetadataEntry) { |
| + token.markSeen(); |
| + } |
| + } |
| + |
| + void countTokens(jsAst.Node node) => node.accept(this); |
| +} |
| + |
| +class UnBoundDebugger extends jsAst.BaseVisitor { |
| + OutputUnit outputUnit; |
| + |
| + UnBoundDebugger(this.outputUnit); |
| + |
| + visitTokenNumber(jsAst.TokenNumber token) { |
| + if (token is _ForwardingMetadataEntry && !token.isBound) { |
| + print("UNBOUND ${outputUnit.name} ${token.debugData} ${token.debugData.hashCode}"); |
|
karlklose
2015/05/28 09:39:53
Remove debug print.
herhut
2015/06/01 12:09:41
Turned the entire thing into an assertion.
|
| + } |
| + } |
| + |
| + void findUnboundPlaceholders(jsAst.Node node) => node.accept(this); |
| +} |