Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of dart2js.js_emitter; | 5 part of dart2js.js_emitter; |
| 6 | 6 |
| 7 abstract class _MetadataEntry extends jsAst.TokenNumber implements Comparable { | |
| 8 jsAst.Expression get entry; | |
| 9 int get value; | |
| 10 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.
| |
| 11 | |
| 12 markSeen(); | |
| 13 } | |
| 14 | |
| 15 class _BoundMetadataEntry extends _MetadataEntry { | |
| 16 int _value = -1; | |
| 17 int _rc = 0; | |
| 18 final jsAst.Expression entry; | |
| 19 | |
| 20 _BoundMetadataEntry(this.entry); | |
| 21 | |
| 22 bool get isFinalized => _value != -1; | |
| 23 | |
| 24 finalize(int value) { | |
| 25 assert(!isFinalized); | |
| 26 _value = value; | |
| 27 } | |
| 28 | |
| 29 int get value { | |
| 30 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.
| |
| 31 assert(isFinalized); | |
| 32 return _value; | |
| 33 } | |
| 34 | |
| 35 markSeen() => _rc++; | |
| 36 | |
| 37 int compareTo(_MetadataEntry other) => other._rc - this._rc; | |
| 38 } | |
| 39 | |
| 40 abstract class Placeholder implements jsAst.TokenNumber { | |
| 41 bind(_MetadataEntry entry); | |
| 42 } | |
| 43 | |
| 44 class _ForwardingMetadataEntry extends _MetadataEntry implements Placeholder { | |
| 45 _MetadataEntry _forwardTo; | |
| 46 var debugData; | |
| 47 var trace; | |
| 48 | |
| 49 bool get isBound => _forwardTo != null; | |
| 50 | |
| 51 _ForwardingMetadataEntry([this.debugData, this.trace]); | |
| 52 | |
| 53 _MetadataEntry get forwardTo { | |
| 54 assert(isBound); | |
| 55 return _forwardTo; | |
| 56 } | |
| 57 | |
| 58 jsAst.Expression get entry { | |
| 59 if (isBound) return forwardTo.entry; | |
| 60 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.
| |
| 61 } | |
| 62 | |
| 63 int get value { | |
| 64 if (isBound) return forwardTo.value; | |
| 65 return -336699; | |
|
sra1
2015/05/27 19:38:51
Should this be
assert(isBound);
?
herhut
2015/06/01 12:09:42
Done.
| |
| 66 } | |
| 67 | |
| 68 int get _rc => forwardTo._rc; | |
| 69 markSeen() { | |
| 70 if (isBound) forwardTo.markSeen(); | |
| 71 } | |
| 72 int compareTo(other) => forwardTo.compareTo(other); | |
| 73 | |
| 74 bind(_MetadataEntry entry) { | |
| 75 assert(!isBound); | |
| 76 _forwardTo = entry; | |
| 77 } | |
| 78 } | |
| 79 | |
| 80 class _MetadataList extends jsAst.TokenExpression { | |
| 81 jsAst.Expression _value; | |
| 82 | |
| 83 void setExpression(jsAst.Expression value) { | |
| 84 assert(_value == null); | |
| 85 _value = value; | |
| 86 } | |
| 87 | |
| 88 jsAst.Expression get value { | |
| 89 assert(_value != null); | |
| 90 return _value; | |
| 91 } | |
| 92 } | |
| 93 | |
| 7 class MetadataCollector { | 94 class MetadataCollector { |
| 8 final Compiler _compiler; | 95 final Compiler _compiler; |
| 9 final Emitter _emitter; | 96 final Emitter _emitter; |
| 10 | 97 |
| 11 /// A list of JS expressions that represent metadata, parameter names and | 98 /// A token for a list of expressions that represent metadata, parameter names |
| 12 /// type variable types. | 99 /// and type variable types. |
| 13 final List<jsAst.Expression> globalMetadata = <jsAst.Expression>[]; | 100 final _MetadataList _globalMetadata = new _MetadataList(); |
| 101 | |
| 102 jsAst.Expression get globalMetadata => _globalMetadata; | |
| 103 | |
| 104 final jsAst.Expression _globalMetadataToken = new _MetadataList(); | |
| 14 | 105 |
| 15 /// A map used to canonicalize the entries of globalMetadata. | 106 /// A map used to canonicalize the entries of globalMetadata. |
| 16 final Map<String, int> _globalMetadataMap = <String, int>{}; | 107 Map<String, _BoundMetadataEntry> _globalMetadataMap; |
| 17 | 108 |
| 18 /// A map with lists of JS expressions, one list for each output unit. The | 109 /// A map with a token for a lists of JS expressions, one token for each |
| 19 /// entries represent types including function types and typedefs. | 110 /// output unit. Once finalized, the entries represent types including |
| 20 final Map<OutputUnit, List<jsAst.Expression>> types = | 111 /// function types and typedefs. |
| 21 <OutputUnit, List<jsAst.Expression>>{}; | 112 Map<OutputUnit, _MetadataList> _typesTokens = |
| 113 new Map<OutputUnit, _MetadataList>(); | |
| 114 | |
| 115 jsAst.Expression getTypesForOutputUnit(OutputUnit outputUnit) { | |
| 116 return _typesTokens.putIfAbsent(outputUnit, () => new _MetadataList()); | |
| 117 } | |
| 22 | 118 |
| 23 /// A map used to canonicalize the entries of types. | 119 /// A map used to canonicalize the entries of types. |
| 24 final Map<OutputUnit, Map<String, int>> _typesMap = | 120 Map<OutputUnit, Map<DartType, _BoundMetadataEntry>> _typesMap = |
| 25 <OutputUnit, Map<String, int>>{}; | 121 <OutputUnit, Map<DartType, _BoundMetadataEntry>>{}; |
| 26 | 122 |
| 27 MetadataCollector(this._compiler, this._emitter); | 123 MetadataCollector(this._compiler, this._emitter) { |
| 124 _globalMetadataMap = new Map<String, _BoundMetadataEntry>(); | |
| 125 } | |
| 28 | 126 |
| 29 JavaScriptBackend get _backend => _compiler.backend; | 127 JavaScriptBackend get _backend => _compiler.backend; |
| 30 TypeVariableHandler get _typeVariableHandler => _backend.typeVariableHandler; | 128 TypeVariableHandler get _typeVariableHandler => _backend.typeVariableHandler; |
| 31 | 129 |
| 32 bool _mustEmitMetadataFor(Element element) { | 130 bool _mustEmitMetadataFor(Element element) { |
| 33 return _backend.mustRetainMetadata && | 131 return _backend.mustRetainMetadata && |
| 34 _backend.referencedFromMirrorSystem(element); | 132 _backend.referencedFromMirrorSystem(element); |
| 35 } | 133 } |
| 36 | 134 |
| 37 /// The metadata function returns the metadata associated with | 135 /// The metadata function returns the metadata associated with |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 57 metadata.add(_emitter.constantReference(constant.value)); | 155 metadata.add(_emitter.constantReference(constant.value)); |
| 58 } | 156 } |
| 59 } | 157 } |
| 60 } | 158 } |
| 61 if (metadata.isEmpty) return null; | 159 if (metadata.isEmpty) return null; |
| 62 return js('function() { return # }', | 160 return js('function() { return # }', |
| 63 new jsAst.ArrayInitializer(metadata)); | 161 new jsAst.ArrayInitializer(metadata)); |
| 64 }); | 162 }); |
| 65 } | 163 } |
| 66 | 164 |
| 67 List<int> reifyDefaultArguments(FunctionElement function) { | 165 List<jsAst.TokenNumber> reifyDefaultArguments(FunctionElement function) { |
| 68 FunctionSignature signature = function.functionSignature; | 166 FunctionSignature signature = function.functionSignature; |
| 69 if (signature.optionalParameterCount == 0) return const []; | 167 if (signature.optionalParameterCount == 0) return const []; |
| 70 List<int> defaultValues = <int>[]; | 168 List<jsAst.TokenNumber> defaultValues = <jsAst.TokenNumber>[]; |
| 71 for (ParameterElement element in signature.optionalParameters) { | 169 for (ParameterElement element in signature.optionalParameters) { |
| 72 ConstantExpression constant = | 170 ConstantExpression constant = |
| 73 _backend.constants.getConstantForVariable(element); | 171 _backend.constants.getConstantForVariable(element); |
| 74 jsAst.Expression expression = (constant == null) | 172 jsAst.Expression representation = (constant == null) |
| 75 ? null | 173 ? new jsAst.LiteralNull() |
| 76 : _emitter.constantReference(constant.value); | 174 : _emitter.constantReference(constant.value);; |
| 77 defaultValues.add(addGlobalMetadata(expression)); | 175 defaultValues.add(_addGlobalMetadata(representation)); |
| 78 } | 176 } |
| 79 return defaultValues; | 177 return defaultValues; |
| 80 } | 178 } |
| 81 | 179 |
| 82 int reifyMetadata(MetadataAnnotation annotation) { | 180 jsAst.Expression reifyMetadata(MetadataAnnotation annotation) { |
| 83 ConstantExpression constant = | 181 ConstantExpression constant = |
| 84 _backend.constants.getConstantForMetadata(annotation); | 182 _backend.constants.getConstantForMetadata(annotation); |
| 85 if (constant == null) { | 183 if (constant == null) { |
| 86 _compiler.internalError(annotation, 'Annotation value is null.'); | 184 _compiler.internalError(annotation, 'Annotation value is null.'); |
| 87 return -1; | 185 return null; |
| 88 } | 186 } |
| 89 return addGlobalMetadata(_emitter.constantReference(constant.value)); | 187 return _addGlobalMetadata(_emitter.constantReference(constant.value)); |
| 90 } | 188 } |
| 91 | 189 |
| 92 int reifyType(DartType type, {bool ignoreTypeVariables: false}) { | 190 jsAst.Expression reifyType(DartType type, {ignoreTypeVariables: false}) { |
| 93 return reifyTypeForOutputUnit(type, | 191 return reifyTypeForOutputUnit(type, |
| 94 _compiler.deferredLoadTask.mainOutputUnit, | 192 _compiler.deferredLoadTask.mainOutputUnit, |
| 95 ignoreTypeVariables: ignoreTypeVariables); | 193 ignoreTypeVariables: ignoreTypeVariables); |
| 96 } | 194 } |
| 97 | 195 |
| 98 int reifyTypeForOutputUnit(DartType type, OutputUnit outputUnit, | 196 jsAst.Expression reifyTypeForOutputUnit(DartType type, |
| 99 {bool ignoreTypeVariables: false}) { | 197 OutputUnit outputUnit, |
| 100 jsAst.Expression representation = | 198 {ignoreTypeVariables: false}) { |
| 101 _backend.rti.getTypeRepresentation( | 199 return addTypeInOutputUnit(type, outputUnit, |
| 102 type, | 200 ignoreTypeVariables: ignoreTypeVariables); |
| 103 (variable) { | 201 } |
| 104 if (ignoreTypeVariables) return new jsAst.LiteralNull(); | 202 |
| 105 return js.number( | 203 jsAst.Expression reifyName(String name) { |
| 106 _typeVariableHandler.reifyTypeVariable( | 204 return _addGlobalMetadata(js.string(name)); |
| 107 variable.element)); | 205 } |
| 108 }, | 206 |
| 109 (TypedefType typedef) { | 207 jsAst.Expression reifyExpression(jsAst.Expression expression) { |
| 110 return _backend.isAccessibleByReflection(typedef.element); | 208 return _addGlobalMetadata(expression); |
| 111 }); | 209 } |
| 210 | |
| 211 Placeholder getMetadataPlaceholder([debug]) => new _ForwardingMetadataEntry(de bug); | |
|
sra1
2015/05/27 19:38:50
line length
herhut
2015/06/01 12:09:41
Done.
| |
| 212 | |
| 213 _MetadataEntry _addGlobalMetadata(jsAst.Node node) { | |
| 214 String printed = jsAst.prettyPrint(node, _compiler).getText(); | |
| 215 return _globalMetadataMap.putIfAbsent(printed, () { | |
| 216 return new _BoundMetadataEntry(node); | |
| 217 }); | |
| 218 } | |
| 219 | |
| 220 jsAst.Expression _computeTypeRepresentation(DartType type, | |
| 221 {ignoreTypeVariables: false}) { | |
| 222 jsAst.Expression representation = _backend.rti.getTypeRepresentation( | |
| 223 type, | |
| 224 (variable) { | |
| 225 if (ignoreTypeVariables) return new jsAst.LiteralNull(); | |
| 226 return _typeVariableHandler.reifyTypeVariable(variable.element); | |
| 227 }, | |
| 228 (TypedefType typedef) { | |
| 229 return _backend.isAccessibleByReflection(typedef.element); | |
| 230 }); | |
| 112 | 231 |
| 113 if (representation is jsAst.LiteralString) { | 232 if (representation is jsAst.LiteralString) { |
| 114 // We don't want the representation to be a string, since we use | 233 // We don't want the representation to be a string, since we use |
| 115 // strings as indicator for non-initialized types in the lazy emitter. | 234 // strings as indicator for non-initialized types in the lazy emitter. |
| 116 _compiler.internalError( | 235 _compiler.internalError( |
| 117 NO_LOCATION_SPANNABLE, 'reified types should not be strings.'); | 236 NO_LOCATION_SPANNABLE, 'reified types should not be strings.'); |
| 118 } | 237 } |
| 119 | 238 |
| 120 return addTypeInOutputUnit(representation, outputUnit); | 239 return representation; |
| 240 | |
| 121 } | 241 } |
| 122 | 242 |
| 123 int reifyName(String name) { | 243 jsAst.Expression addTypeInOutputUnit(DartType type, |
| 124 return addGlobalMetadata(js('"$name"')); | 244 OutputUnit outputUnit, |
| 125 } | 245 {ignoreTypeVariables: false}) { |
| 126 | 246 if (_typesMap[outputUnit] == null) { |
| 127 int addGlobalMetadata(jsAst.Expression expression) { | 247 _typesMap[outputUnit] = new Map<DartType, _BoundMetadataEntry>(); |
| 128 // TODO(sigmund): consider adding an effient way to compare expressions | 248 } |
| 129 String string = jsAst.prettyPrint(expression, _compiler).getText(); | 249 return _typesMap[outputUnit].putIfAbsent(type, () { |
| 130 return _globalMetadataMap.putIfAbsent(string, () { | 250 return new _BoundMetadataEntry( |
| 131 globalMetadata.add(expression); | 251 _computeTypeRepresentation(type, |
| 132 return globalMetadata.length - 1; | 252 ignoreTypeVariables: ignoreTypeVariables)); |
| 133 }); | 253 }); |
| 134 } | 254 } |
| 135 | 255 |
| 136 int addTypeInOutputUnit(jsAst.Expression type, OutputUnit outputUnit) { | 256 List<jsAst.TokenNumber> computeMetadata(FunctionElement element) { |
| 137 String string = jsAst.prettyPrint(type, _compiler).getText(); | |
| 138 if (_typesMap[outputUnit] == null) { | |
| 139 _typesMap[outputUnit] = <String, int>{}; | |
| 140 } | |
| 141 return _typesMap[outputUnit].putIfAbsent(string, () { | |
| 142 | |
| 143 if (types[outputUnit] == null) { | |
| 144 types[outputUnit] = <jsAst.Expression>[]; | |
| 145 } | |
| 146 | |
| 147 types[outputUnit].add(type); | |
| 148 return types[outputUnit].length - 1; | |
| 149 }); | |
| 150 } | |
| 151 | |
| 152 List<int> computeMetadata(FunctionElement element) { | |
| 153 return _compiler.withCurrentElement(element, () { | 257 return _compiler.withCurrentElement(element, () { |
| 154 if (!_mustEmitMetadataFor(element)) return const <int>[]; | 258 if (!_mustEmitMetadataFor(element)) return const <jsAst.TokenNumber>[]; |
| 155 List<int> metadata = <int>[]; | 259 List<jsAst.TokenNumber> metadata = <jsAst.TokenNumber>[]; |
| 156 Link link = element.metadata; | 260 Link link = element.metadata; |
| 157 // TODO(ahe): Why is metadata sometimes null? | 261 // TODO(ahe): Why is metadata sometimes null? |
| 158 if (link != null) { | 262 if (link != null) { |
| 159 for (; !link.isEmpty; link = link.tail) { | 263 for (; !link.isEmpty; link = link.tail) { |
| 160 metadata.add(reifyMetadata(link.head)); | 264 metadata.add(reifyMetadata(link.head)); |
| 161 } | 265 } |
| 162 } | 266 } |
| 163 return metadata; | 267 return metadata; |
| 164 }); | 268 }); |
| 165 } | 269 } |
| 270 | |
| 271 void countTokensInProgram(jsAst.Program program) { | |
| 272 TokenCounter visitor = new TokenCounter(); | |
| 273 visitor.countTokens(program); | |
| 274 } | |
| 275 | |
| 276 void finalizeTokens() { | |
| 277 void checkTokensInTypes(OutputUnit outputUnit, entries) { | |
| 278 UnBoundDebugger debugger = new UnBoundDebugger(outputUnit); | |
| 279 for (_BoundMetadataEntry entry in entries) { | |
| 280 if (entry._rc == 0) { | |
|
karlklose
2015/05/28 09:39:53
Add/use helper in _BoundMetadataEntry?
herhut
2015/06/01 12:09:42
Done.
| |
| 281 print("UNUSED ${entry.hashCode}"); | |
|
karlklose
2015/05/28 09:39:53
Remove debug print?
herhut
2015/06/01 12:09:41
Done.
| |
| 282 continue; | |
| 283 } | |
| 284 debugger.findUnboundPlaceholders(entry.entry); | |
| 285 } | |
| 286 } | |
| 287 void countTokensInTypes(Iterable<_BoundMetadataEntry> entries) { | |
| 288 TokenCounter counter = new TokenCounter(); | |
| 289 entries.where((_BoundMetadataEntry e) => e._rc > 0) | |
| 290 .map((_BoundMetadataEntry e) => e.entry) | |
| 291 .forEach(counter.countTokens); | |
| 292 } | |
| 293 | |
| 294 jsAst.ArrayInitializer finalizeMap(Map<dynamic, _BoundMetadataEntry> map) { | |
|
sra1
2015/05/27 19:38:50
Indentation
herhut
2015/06/01 12:09:42
Done.
| |
| 295 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.
| |
| 296 List<_BoundMetadataEntry> entries = map.values.where(isUsed).toList(); | |
| 297 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
| |
| 298 | |
| 299 int count = 0; | |
| 300 for (_BoundMetadataEntry entry in entries) { | |
| 301 entry.finalize(count++); | |
| 302 } | |
|
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.
| |
| 303 | |
| 304 List<jsAst.Node> values = | |
| 305 entries.map((_BoundMetadataEntry e) => e.entry).toList(); | |
| 306 | |
| 307 return new jsAst.ArrayInitializer(values); | |
| 308 } | |
| 309 | |
| 310 _globalMetadata.setExpression(finalizeMap(_globalMetadataMap)); | |
| 311 | |
| 312 _typesTokens.forEach((OutputUnit outputUnit, _MetadataList token) { | |
| 313 Map typesMap = _typesMap[outputUnit]; | |
| 314 if (typesMap != null) { | |
| 315 checkTokensInTypes(outputUnit, typesMap.values); | |
| 316 countTokensInTypes(typesMap.values); | |
| 317 token.setExpression(finalizeMap(typesMap)); | |
| 318 } else { | |
| 319 token.setExpression(new jsAst.ArrayInitializer([])); | |
| 320 } | |
| 321 }); | |
| 322 } | |
| 166 } | 323 } |
| 324 | |
| 325 class TokenCounter extends jsAst.BaseVisitor { | |
| 326 visitTokenNumber(jsAst.TokenNumber token) { | |
| 327 if (token is _MetadataEntry) { | |
| 328 token.markSeen(); | |
| 329 } | |
| 330 } | |
| 331 | |
| 332 void countTokens(jsAst.Node node) => node.accept(this); | |
| 333 } | |
| 334 | |
| 335 class UnBoundDebugger extends jsAst.BaseVisitor { | |
| 336 OutputUnit outputUnit; | |
| 337 | |
| 338 UnBoundDebugger(this.outputUnit); | |
| 339 | |
| 340 visitTokenNumber(jsAst.TokenNumber token) { | |
| 341 if (token is _ForwardingMetadataEntry && !token.isBound) { | |
| 342 print("UNBOUND ${outputUnit.name} ${token.debugData} ${token.debugData.has hCode}"); | |
|
karlklose
2015/05/28 09:39:53
Remove debug print.
herhut
2015/06/01 12:09:41
Turned the entire thing into an assertion.
| |
| 343 } | |
| 344 } | |
| 345 | |
| 346 void findUnboundPlaceholders(jsAst.Node node) => node.accept(this); | |
| 347 } | |
| OLD | NEW |