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 /// Represents an entry's position in one of the global metadata arrays. |
| 8 /// |
| 9 /// [_rc] is used to count the number of references of the token in the |
| 10 /// ast for a program. |
| 11 /// [value] is the actual position, once they have been finalized. |
| 12 abstract class _MetadataEntry extends jsAst.DeferredNumber |
| 13 implements Comparable { |
| 14 jsAst.Expression get entry; |
| 15 int get value; |
| 16 int get _rc; |
| 17 |
| 18 // Mark this entry as seen. On the first time this is seen, the visitor |
| 19 // will be applied to the [entry] to also mark potential [_MetadataEntry] |
| 20 // instances in the [entry] as seen. |
| 21 markSeen(jsAst.BaseVisitor visitor); |
| 22 } |
| 23 |
| 24 class _BoundMetadataEntry extends _MetadataEntry { |
| 25 int _value = -1; |
| 26 int _rc = 0; |
| 27 final jsAst.Expression entry; |
| 28 |
| 29 _BoundMetadataEntry(this.entry); |
| 30 |
| 31 bool get isFinalized => _value != -1; |
| 32 |
| 33 finalize(int value) { |
| 34 assert(!isFinalized); |
| 35 _value = value; |
| 36 } |
| 37 |
| 38 int get value { |
| 39 assert(isFinalized); |
| 40 return _value; |
| 41 } |
| 42 |
| 43 bool get isUsed => _rc > 0; |
| 44 |
| 45 markSeen(jsAst.BaseVisitor visitor) { |
| 46 _rc++; |
| 47 if (_rc == 1) entry.accept(visitor); |
| 48 } |
| 49 |
| 50 int compareTo(_MetadataEntry other) => other._rc - this._rc; |
| 51 } |
| 52 |
| 53 abstract class Placeholder implements jsAst.DeferredNumber { |
| 54 bind(_MetadataEntry entry); |
| 55 } |
| 56 |
| 57 class _ForwardingMetadataEntry extends _MetadataEntry implements Placeholder { |
| 58 _MetadataEntry _forwardTo; |
| 59 var debug; |
| 60 |
| 61 bool get isBound => _forwardTo != null; |
| 62 |
| 63 _ForwardingMetadataEntry([this.debug]); |
| 64 |
| 65 _MetadataEntry get forwardTo { |
| 66 assert(isBound); |
| 67 return _forwardTo; |
| 68 } |
| 69 |
| 70 jsAst.Expression get entry { |
| 71 assert(isBound); |
| 72 return forwardTo.entry; |
| 73 } |
| 74 |
| 75 int get value { |
| 76 assert(isBound); |
| 77 return forwardTo.value; |
| 78 } |
| 79 |
| 80 int get _rc => forwardTo._rc; |
| 81 |
| 82 markSeen(jsAst.BaseVisitor visitor) => forwardTo.markSeen(visitor); |
| 83 |
| 84 int compareTo(other) => forwardTo.compareTo(other); |
| 85 |
| 86 bind(_MetadataEntry entry) { |
| 87 assert(!isBound); |
| 88 _forwardTo = entry; |
| 89 } |
| 90 } |
| 91 |
| 92 class _MetadataList extends jsAst.DeferredExpression { |
| 93 jsAst.Expression _value; |
| 94 |
| 95 void setExpression(jsAst.Expression value) { |
| 96 // TODO(herhut): Enable the below assertion once incremental mode is gone. |
| 97 // assert(_value == null); |
| 98 assert(value.precedenceLevel == this.precedenceLevel); |
| 99 _value = value; |
| 100 } |
| 101 |
| 102 jsAst.Expression get value { |
| 103 assert(_value != null); |
| 104 return _value; |
| 105 } |
| 106 |
| 107 int get precedenceLevel => js_precedence.PRIMARY; |
| 108 } |
| 109 |
7 class MetadataCollector { | 110 class MetadataCollector { |
8 final Compiler _compiler; | 111 final Compiler _compiler; |
9 final Emitter _emitter; | 112 final Emitter _emitter; |
10 | 113 |
11 /// A list of JS expressions that represent metadata, parameter names and | 114 /// A token for a list of expressions that represent metadata, parameter names |
12 /// type variable types. | 115 /// and type variable types. |
13 final List<jsAst.Expression> globalMetadata = <jsAst.Expression>[]; | 116 final _MetadataList _globalMetadata = new _MetadataList(); |
| 117 jsAst.Expression get globalMetadata => _globalMetadata; |
14 | 118 |
15 /// A map used to canonicalize the entries of globalMetadata. | 119 /// A map used to canonicalize the entries of globalMetadata. |
16 final Map<String, int> _globalMetadataMap = <String, int>{}; | 120 Map<String, _BoundMetadataEntry> _globalMetadataMap; |
17 | 121 |
18 /// A map with lists of JS expressions, one list for each output unit. The | 122 /// A map with a token for a lists of JS expressions, one token for each |
19 /// entries represent types including function types and typedefs. | 123 /// output unit. Once finalized, the entries represent types including |
20 final Map<OutputUnit, List<jsAst.Expression>> types = | 124 /// function types and typedefs. |
21 <OutputUnit, List<jsAst.Expression>>{}; | 125 Map<OutputUnit, _MetadataList> _typesTokens = |
| 126 new Map<OutputUnit, _MetadataList>(); |
| 127 |
| 128 jsAst.Expression getTypesForOutputUnit(OutputUnit outputUnit) { |
| 129 return _typesTokens.putIfAbsent(outputUnit, () => new _MetadataList()); |
| 130 } |
22 | 131 |
23 /// A map used to canonicalize the entries of types. | 132 /// A map used to canonicalize the entries of types. |
24 final Map<OutputUnit, Map<String, int>> _typesMap = | 133 Map<OutputUnit, Map<DartType, _BoundMetadataEntry>> _typesMap = |
25 <OutputUnit, Map<String, int>>{}; | 134 <OutputUnit, Map<DartType, _BoundMetadataEntry>>{}; |
26 | 135 |
27 MetadataCollector(this._compiler, this._emitter); | 136 // To support incremental compilation, we have to be able to eagerly emit |
| 137 // metadata and add metadata later on. We use the below two counters for |
| 138 // this. |
| 139 int _globalMetadataCounter = 0; |
| 140 int _globalTypesCounter = 0; |
| 141 |
| 142 MetadataCollector(this._compiler, this._emitter) { |
| 143 _globalMetadataMap = new Map<String, _BoundMetadataEntry>(); |
| 144 } |
28 | 145 |
29 JavaScriptBackend get _backend => _compiler.backend; | 146 JavaScriptBackend get _backend => _compiler.backend; |
30 TypeVariableHandler get _typeVariableHandler => _backend.typeVariableHandler; | 147 TypeVariableHandler get _typeVariableHandler => _backend.typeVariableHandler; |
31 | 148 |
32 bool _mustEmitMetadataFor(Element element) { | 149 bool _mustEmitMetadataFor(Element element) { |
33 return _backend.mustRetainMetadata && | 150 return _backend.mustRetainMetadata && |
34 _backend.referencedFromMirrorSystem(element); | 151 _backend.referencedFromMirrorSystem(element); |
35 } | 152 } |
36 | 153 |
37 /// The metadata function returns the metadata associated with | 154 /// The metadata function returns the metadata associated with |
(...skipping 19 matching lines...) Expand all Loading... |
57 metadata.add(_emitter.constantReference(constant)); | 174 metadata.add(_emitter.constantReference(constant)); |
58 } | 175 } |
59 } | 176 } |
60 } | 177 } |
61 if (metadata.isEmpty) return null; | 178 if (metadata.isEmpty) return null; |
62 return js('function() { return # }', | 179 return js('function() { return # }', |
63 new jsAst.ArrayInitializer(metadata)); | 180 new jsAst.ArrayInitializer(metadata)); |
64 }); | 181 }); |
65 } | 182 } |
66 | 183 |
67 List<int> reifyDefaultArguments(FunctionElement function) { | 184 List<jsAst.DeferredNumber> reifyDefaultArguments(FunctionElement function) { |
68 FunctionSignature signature = function.functionSignature; | 185 FunctionSignature signature = function.functionSignature; |
69 if (signature.optionalParameterCount == 0) return const []; | 186 if (signature.optionalParameterCount == 0) return const []; |
70 List<int> defaultValues = <int>[]; | 187 List<jsAst.DeferredNumber> defaultValues = <jsAst.DeferredNumber>[]; |
71 for (ParameterElement element in signature.optionalParameters) { | 188 for (ParameterElement element in signature.optionalParameters) { |
72 ConstantValue constant = | 189 ConstantValue constant = |
73 _backend.constants.getConstantValueForVariable(element); | 190 _backend.constants.getConstantValueForVariable(element); |
74 jsAst.Expression expression = (constant == null) | 191 jsAst.Expression expression = (constant == null) |
75 ? null | 192 ? new jsAst.LiteralNull() |
76 : _emitter.constantReference(constant); | 193 : _emitter.constantReference(constant); |
77 defaultValues.add(addGlobalMetadata(expression)); | 194 defaultValues.add(_addGlobalMetadata(expression)); |
78 } | 195 } |
79 return defaultValues; | 196 return defaultValues; |
80 } | 197 } |
81 | 198 |
82 int reifyMetadata(MetadataAnnotation annotation) { | 199 jsAst.Expression reifyMetadata(MetadataAnnotation annotation) { |
83 ConstantValue constant = | 200 ConstantValue constant = |
84 _backend.constants.getConstantValueForMetadata(annotation); | 201 _backend.constants.getConstantValueForMetadata(annotation); |
85 if (constant == null) { | 202 if (constant == null) { |
86 _compiler.internalError(annotation, 'Annotation value is null.'); | 203 _compiler.internalError(annotation, 'Annotation value is null.'); |
87 return -1; | 204 return null; |
88 } | 205 } |
89 return addGlobalMetadata(_emitter.constantReference(constant)); | 206 return _addGlobalMetadata(_emitter.constantReference(constant)); |
90 } | 207 } |
91 | 208 |
92 int reifyType(DartType type, {bool ignoreTypeVariables: false}) { | 209 jsAst.Expression reifyType(DartType type, {ignoreTypeVariables: false}) { |
93 return reifyTypeForOutputUnit(type, | 210 return reifyTypeForOutputUnit(type, |
94 _compiler.deferredLoadTask.mainOutputUnit, | 211 _compiler.deferredLoadTask.mainOutputUnit, |
95 ignoreTypeVariables: ignoreTypeVariables); | 212 ignoreTypeVariables: ignoreTypeVariables); |
96 } | 213 } |
97 | 214 |
98 int reifyTypeForOutputUnit(DartType type, OutputUnit outputUnit, | 215 jsAst.Expression reifyTypeForOutputUnit(DartType type, |
99 {bool ignoreTypeVariables: false}) { | 216 OutputUnit outputUnit, |
100 jsAst.Expression representation = | 217 {ignoreTypeVariables: false}) { |
101 _backend.rti.getTypeRepresentation( | 218 return addTypeInOutputUnit(type, outputUnit, |
102 type, | 219 ignoreTypeVariables: ignoreTypeVariables); |
103 (variable) { | 220 } |
104 if (ignoreTypeVariables) return new jsAst.LiteralNull(); | 221 |
105 return js.number( | 222 jsAst.Expression reifyName(String name) { |
106 _typeVariableHandler.reifyTypeVariable( | 223 return _addGlobalMetadata(js.string(name)); |
107 variable.element)); | 224 } |
108 }, | 225 |
109 (TypedefType typedef) { | 226 jsAst.Expression reifyExpression(jsAst.Expression expression) { |
110 return _backend.isAccessibleByReflection(typedef.element); | 227 return _addGlobalMetadata(expression); |
111 }); | 228 } |
| 229 |
| 230 Placeholder getMetadataPlaceholder([debug]) { |
| 231 return new _ForwardingMetadataEntry(debug); |
| 232 } |
| 233 |
| 234 _MetadataEntry _addGlobalMetadata(jsAst.Node node) { |
| 235 String printed = jsAst.prettyPrint(node, _compiler).getText(); |
| 236 return _globalMetadataMap.putIfAbsent(printed, () { |
| 237 _BoundMetadataEntry result = new _BoundMetadataEntry(node); |
| 238 if (_compiler.hasIncrementalSupport) { |
| 239 result.finalize(_globalMetadataCounter++); |
| 240 } |
| 241 return result; |
| 242 }); |
| 243 } |
| 244 |
| 245 jsAst.Expression _computeTypeRepresentation(DartType type, |
| 246 {ignoreTypeVariables: false}) { |
| 247 jsAst.Expression representation = _backend.rti.getTypeRepresentation( |
| 248 type, |
| 249 (variable) { |
| 250 if (ignoreTypeVariables) return new jsAst.LiteralNull(); |
| 251 return _typeVariableHandler.reifyTypeVariable(variable.element); |
| 252 }, |
| 253 (TypedefType typedef) { |
| 254 return _backend.isAccessibleByReflection(typedef.element); |
| 255 }); |
112 | 256 |
113 if (representation is jsAst.LiteralString) { | 257 if (representation is jsAst.LiteralString) { |
114 // We don't want the representation to be a string, since we use | 258 // 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. | 259 // strings as indicator for non-initialized types in the lazy emitter. |
116 _compiler.internalError( | 260 _compiler.internalError( |
117 NO_LOCATION_SPANNABLE, 'reified types should not be strings.'); | 261 NO_LOCATION_SPANNABLE, 'reified types should not be strings.'); |
118 } | 262 } |
119 | 263 |
120 return addTypeInOutputUnit(representation, outputUnit); | 264 return representation; |
| 265 |
121 } | 266 } |
122 | 267 |
123 int reifyName(String name) { | 268 jsAst.Expression addTypeInOutputUnit(DartType type, |
124 return addGlobalMetadata(js('"$name"')); | 269 OutputUnit outputUnit, |
125 } | 270 {ignoreTypeVariables: false}) { |
126 | 271 if (_typesMap[outputUnit] == null) { |
127 int addGlobalMetadata(jsAst.Expression expression) { | 272 _typesMap[outputUnit] = new Map<DartType, _BoundMetadataEntry>(); |
128 // TODO(sigmund): consider adding an effient way to compare expressions | 273 } |
129 String string = jsAst.prettyPrint(expression, _compiler).getText(); | 274 return _typesMap[outputUnit].putIfAbsent(type, () { |
130 return _globalMetadataMap.putIfAbsent(string, () { | 275 _BoundMetadataEntry result = new _BoundMetadataEntry( |
131 globalMetadata.add(expression); | 276 _computeTypeRepresentation(type, |
132 return globalMetadata.length - 1; | 277 ignoreTypeVariables: ignoreTypeVariables)); |
| 278 if (_compiler.hasIncrementalSupport) { |
| 279 result.finalize(_globalTypesCounter++); |
| 280 } |
| 281 return result; |
133 }); | 282 }); |
134 } | 283 } |
135 | 284 |
136 int addTypeInOutputUnit(jsAst.Expression type, OutputUnit outputUnit) { | 285 List<jsAst.DeferredNumber> 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, () { | 286 return _compiler.withCurrentElement(element, () { |
154 if (!_mustEmitMetadataFor(element)) return const <int>[]; | 287 if (!_mustEmitMetadataFor(element)) return const <jsAst.DeferredNumber>[]; |
155 List<int> metadata = <int>[]; | 288 List<jsAst.DeferredNumber> metadata = <jsAst.DeferredNumber>[]; |
156 Link link = element.metadata; | 289 Link link = element.metadata; |
157 // TODO(ahe): Why is metadata sometimes null? | 290 // TODO(ahe): Why is metadata sometimes null? |
158 if (link != null) { | 291 if (link != null) { |
159 for (; !link.isEmpty; link = link.tail) { | 292 for (; !link.isEmpty; link = link.tail) { |
160 metadata.add(reifyMetadata(link.head)); | 293 metadata.add(reifyMetadata(link.head)); |
161 } | 294 } |
162 } | 295 } |
163 return metadata; | 296 return metadata; |
164 }); | 297 }); |
165 } | 298 } |
| 299 |
| 300 void countTokensInProgram(jsAst.Program program) { |
| 301 TokenCounter visitor = new TokenCounter(); |
| 302 visitor.countTokens(program); |
| 303 } |
| 304 |
| 305 void finalizeTokens() { |
| 306 bool checkTokensInTypes(OutputUnit outputUnit, entries) { |
| 307 UnBoundDebugger debugger = new UnBoundDebugger(outputUnit); |
| 308 for (_BoundMetadataEntry entry in entries) { |
| 309 if (!entry.isUsed) continue; |
| 310 if (debugger.findUnboundPlaceholders(entry.entry)) { |
| 311 return false; |
| 312 } |
| 313 } |
| 314 return true; |
| 315 } |
| 316 void countTokensInTypes(Iterable<_BoundMetadataEntry> entries) { |
| 317 TokenCounter counter = new TokenCounter(); |
| 318 entries.where((_BoundMetadataEntry e) => e._rc > 0) |
| 319 .map((_BoundMetadataEntry e) => e.entry) |
| 320 .forEach(counter.countTokens); |
| 321 } |
| 322 |
| 323 jsAst.ArrayInitializer finalizeMap(Map<dynamic, _BoundMetadataEntry> map) { |
| 324 // When in incremental mode, we allocate entries eagerly. |
| 325 if (_compiler.hasIncrementalSupport) { |
| 326 return new jsAst.ArrayInitializer(map.values.toList()); |
| 327 } |
| 328 |
| 329 bool isUsed(_BoundMetadataEntry entry) => entry.isUsed; |
| 330 List<_BoundMetadataEntry> entries = map.values.where(isUsed).toList(); |
| 331 entries.sort(); |
| 332 |
| 333 // TODO(herhut): Bucket entries by index length and use a stable |
| 334 // distribution within buckets. |
| 335 int count = 0; |
| 336 for (_BoundMetadataEntry entry in entries) { |
| 337 entry.finalize(count++); |
| 338 } |
| 339 |
| 340 List<jsAst.Node> values = |
| 341 entries.map((_BoundMetadataEntry e) => e.entry).toList(); |
| 342 |
| 343 return new jsAst.ArrayInitializer(values); |
| 344 } |
| 345 |
| 346 _globalMetadata.setExpression(finalizeMap(_globalMetadataMap)); |
| 347 |
| 348 _typesTokens.forEach((OutputUnit outputUnit, _MetadataList token) { |
| 349 Map typesMap = _typesMap[outputUnit]; |
| 350 if (typesMap != null) { |
| 351 assert(checkTokensInTypes(outputUnit, typesMap.values)); |
| 352 countTokensInTypes(typesMap.values); |
| 353 token.setExpression(finalizeMap(typesMap)); |
| 354 } else { |
| 355 token.setExpression(new jsAst.ArrayInitializer([])); |
| 356 } |
| 357 }); |
| 358 } |
166 } | 359 } |
| 360 |
| 361 class TokenCounter extends jsAst.BaseVisitor { |
| 362 @override |
| 363 visitDeferredNumber(jsAst.DeferredNumber token) { |
| 364 if (token is _MetadataEntry) { |
| 365 token.markSeen(this); |
| 366 } |
| 367 } |
| 368 |
| 369 void countTokens(jsAst.Node node) => node.accept(this); |
| 370 } |
| 371 |
| 372 class UnBoundDebugger extends jsAst.BaseVisitor { |
| 373 OutputUnit outputUnit; |
| 374 bool _foundUnboundToken = false; |
| 375 |
| 376 UnBoundDebugger(this.outputUnit); |
| 377 |
| 378 @override |
| 379 visitDeferredNumber(jsAst.DeferredNumber token) { |
| 380 if (token is _ForwardingMetadataEntry && !token.isBound) { |
| 381 _foundUnboundToken = true; |
| 382 } |
| 383 } |
| 384 |
| 385 bool findUnboundPlaceholders(jsAst.Node node) { |
| 386 node.accept(this); |
| 387 return _foundUnboundToken; |
| 388 } |
| 389 } |
OLD | NEW |