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 |