OLD | NEW |
---|---|
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2017, 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 import 'dart:io' hide Link; | 5 import 'dart:io' hide Link; |
6 import 'package:async_helper/async_helper.dart'; | 6 import 'package:async_helper/async_helper.dart'; |
7 import 'package:compiler/src/closure.dart'; | 7 import 'package:compiler/src/closure.dart'; |
8 import 'package:compiler/src/commandline_options.dart'; | 8 import 'package:compiler/src/commandline_options.dart'; |
9 import 'package:compiler/src/common.dart'; | 9 import 'package:compiler/src/common.dart'; |
10 import 'package:compiler/src/compiler.dart'; | 10 import 'package:compiler/src/compiler.dart'; |
11 import 'package:compiler/src/diagnostics/diagnostic_listener.dart'; | 11 import 'package:compiler/src/diagnostics/diagnostic_listener.dart'; |
12 import 'package:compiler/src/elements/elements.dart'; | 12 import 'package:compiler/src/elements/elements.dart'; |
13 import 'package:compiler/src/elements/entities.dart'; | 13 import 'package:compiler/src/elements/entities.dart'; |
14 import 'package:compiler/src/kernel/element_map.dart'; | 14 import 'package:compiler/src/kernel/element_map.dart'; |
15 import 'package:compiler/src/kernel/kernel_backend_strategy.dart'; | 15 import 'package:compiler/src/kernel/kernel_backend_strategy.dart'; |
16 import 'package:compiler/src/js_model/locals.dart'; | 16 import 'package:compiler/src/js_model/locals.dart'; |
17 import 'package:compiler/src/tree/nodes.dart' as ast; | 17 import 'package:compiler/src/tree/nodes.dart' as ast; |
18 import 'package:compiler/src/util/util.dart'; | 18 import 'package:compiler/src/util/util.dart'; |
19 import 'package:expect/expect.dart'; | 19 import 'package:expect/expect.dart'; |
20 import '../equivalence/id_equivalence.dart'; | 20 import '../equivalence/id_equivalence.dart'; |
21 import '../equivalence/id_equivalence_helper.dart'; | 21 import '../equivalence/id_equivalence_helper.dart'; |
22 import 'package:kernel/ast.dart' as ir; | 22 import 'package:kernel/ast.dart' as ir; |
23 | 23 |
24 main(List<String> args) { | 24 main(List<String> args) { |
25 bool verbose = args.contains('-v'); | 25 bool verbose = args.contains('-v'); |
26 asyncTest(() async { | 26 asyncTest(() async { |
27 Directory dataDir = new Directory.fromUri(Platform.script.resolve('data')); | 27 Directory dataDir = new Directory.fromUri(Platform.script.resolve('data')); |
28 await checkTests(dataDir, computeClosureData, computeKernelClosureData, | 28 await checkTests(dataDir, computeClosureData, computeKernelClosureData, |
29 // TODO(johnnniwinther,efortuna): Enable these tests for .dill. | 29 // TODO(johnnniwinther,efortuna): Enable these tests for .dill. |
30 skipForKernel: ['captured_variable.dart'], | 30 skipForKernel: [ |
31 'captured_variable.dart', | |
32 'mixed.dart', | |
33 'mutations.dart', | |
34 'nested_closures.dart' | |
35 ], | |
31 options: [Flags.disableTypeInference], | 36 options: [Flags.disableTypeInference], |
32 verbose: verbose); | 37 verbose: verbose); |
33 }); | 38 }); |
34 } | 39 } |
35 | 40 |
36 /// Compute closure data mapping for [_member] as a [MemberElement]. | 41 /// Compute closure data mapping for [_member] as a [MemberElement]. |
37 /// | 42 /// |
38 /// Fills [actualMap] with the data and [sourceSpanMap] with the source spans | 43 /// Fills [actualMap] with the data and [sourceSpanMap] with the source spans |
39 /// for the data origin. | 44 /// for the data origin. |
40 void computeClosureData( | 45 void computeClosureData( |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
77 ClosureAstComputer(DiagnosticReporter reporter, Map<Id, ActualData> actualMap, | 82 ClosureAstComputer(DiagnosticReporter reporter, Map<Id, ActualData> actualMap, |
78 ResolvedAst resolvedAst, this.closureDataLookup, | 83 ResolvedAst resolvedAst, this.closureDataLookup, |
79 {this.verbose: false}) | 84 {this.verbose: false}) |
80 : super(reporter, actualMap, resolvedAst) { | 85 : super(reporter, actualMap, resolvedAst) { |
81 pushMember(resolvedAst.element as MemberElement); | 86 pushMember(resolvedAst.element as MemberElement); |
82 } | 87 } |
83 | 88 |
84 visitFunctionExpression(ast.FunctionExpression node) { | 89 visitFunctionExpression(ast.FunctionExpression node) { |
85 Entity localFunction = resolvedAst.elements.getFunctionDefinition(node); | 90 Entity localFunction = resolvedAst.elements.getFunctionDefinition(node); |
86 if (localFunction is LocalFunctionElement) { | 91 if (localFunction is LocalFunctionElement) { |
92 pushMember(localFunction.callMethod); | |
87 pushLocalFunction(node); | 93 pushLocalFunction(node); |
88 super.visitFunctionExpression(node); | 94 super.visitFunctionExpression(node); |
89 popLocalFunction(); | 95 popLocalFunction(); |
96 popMember(); | |
90 } else { | 97 } else { |
91 super.visitFunctionExpression(node); | 98 super.visitFunctionExpression(node); |
92 } | 99 } |
93 } | 100 } |
94 | 101 |
95 @override | 102 @override |
96 String computeNodeValue(ast.Node node, [AstElement element]) { | 103 String computeNodeValue(ast.Node node, [AstElement element]) { |
97 if (element != null && element.isLocal) { | 104 if (element != null && element.isLocal) { |
98 if (element.isFunction) { | 105 if (element.isFunction) { |
99 return computeObjectValue(element); | 106 LocalFunctionElement localFunction = element; |
107 return computeObjectValue(localFunction.callMethod); | |
100 } else { | 108 } else { |
101 LocalElement local = element; | 109 LocalElement local = element; |
102 return computeLocalValue(local); | 110 return computeLocalValue(local); |
103 } | 111 } |
104 } | 112 } |
105 // TODO(johnniwinther,efortuna): Collect data for other nodes? | 113 // TODO(johnniwinther,efortuna): Collect data for other nodes? |
106 return null; | 114 return null; |
107 } | 115 } |
108 | 116 |
109 @override | 117 @override |
110 String computeElementValue(AstElement element) { | 118 String computeElementValue(covariant MemberElement element) { |
111 // TODO(johnniwinther,efortuna): Collect data for the member | 119 // TODO(johnniwinther,efortuna): Collect data for the member |
112 // (has thisLocal, has box, etc.). | 120 // (has thisLocal, has box, etc.). |
113 return computeObjectValue(element); | 121 return computeObjectValue(element); |
114 } | 122 } |
115 } | 123 } |
116 | 124 |
117 /// Kernel IR visitor for computing closure data. | 125 /// Kernel IR visitor for computing closure data. |
118 class ClosureIrChecker extends IrDataExtractor with ComputeValueMixin<ir.Node> { | 126 class ClosureIrChecker extends IrDataExtractor with ComputeValueMixin<ir.Node> { |
119 final MemberEntity member; | 127 final MemberEntity member; |
120 final ClosureDataLookup<ir.Node> closureDataLookup; | 128 final ClosureDataLookup<ir.Node> closureDataLookup; |
121 final KernelToLocalsMap _localsMap; | 129 final KernelToLocalsMap _localsMap; |
122 final bool verbose; | 130 final bool verbose; |
123 | 131 |
124 ClosureIrChecker( | 132 ClosureIrChecker( |
125 Map<Id, ActualData> actualMap, | 133 Map<Id, ActualData> actualMap, |
126 KernelToElementMapForBuilding elementMap, | 134 KernelToElementMapForBuilding elementMap, |
127 this.member, | 135 this.member, |
128 this._localsMap, | 136 this._localsMap, |
129 this.closureDataLookup, | 137 this.closureDataLookup, |
130 {this.verbose: false}) | 138 {this.verbose: false}) |
131 : super(actualMap) { | 139 : super(actualMap) { |
132 pushMember(member); | 140 pushMember(member); |
133 } | 141 } |
134 | 142 |
135 visitFunctionExpression(ir.FunctionExpression node) { | 143 visitFunctionExpression(ir.FunctionExpression node) { |
144 ClosureRepresentationInfo info = closureDataLookup.getClosureInfo(node); | |
145 pushMember(info.callMethod); | |
136 pushLocalFunction(node); | 146 pushLocalFunction(node); |
137 super.visitFunctionExpression(node); | 147 super.visitFunctionExpression(node); |
138 popLocalFunction(); | 148 popLocalFunction(); |
149 popMember(); | |
139 } | 150 } |
140 | 151 |
141 visitFunctionDeclaration(ir.FunctionDeclaration node) { | 152 visitFunctionDeclaration(ir.FunctionDeclaration node) { |
153 ClosureRepresentationInfo info = closureDataLookup.getClosureInfo(node); | |
154 pushMember(info.callMethod); | |
142 pushLocalFunction(node); | 155 pushLocalFunction(node); |
143 super.visitFunctionDeclaration(node); | 156 super.visitFunctionDeclaration(node); |
144 popLocalFunction(); | 157 popLocalFunction(); |
158 popMember(); | |
145 } | 159 } |
146 | 160 |
147 @override | 161 @override |
148 String computeNodeValue(ir.Node node) { | 162 String computeNodeValue(ir.Node node) { |
149 if (node is ir.VariableDeclaration) { | 163 if (node is ir.VariableDeclaration) { |
150 if (node.parent is ir.FunctionDeclaration) { | 164 if (node.parent is ir.FunctionDeclaration) { |
151 return computeObjectValue(node.parent); | 165 ClosureRepresentationInfo info = |
166 closureDataLookup.getClosureInfo(node.parent); | |
167 return computeObjectValue(info.callMethod); | |
152 } | 168 } |
153 Local local = _localsMap.getLocalVariable(node); | 169 Local local = _localsMap.getLocalVariable(node); |
154 return computeLocalValue(local); | 170 return computeLocalValue(local); |
155 } else if (node is ir.FunctionExpression) { | 171 } else if (node is ir.FunctionExpression) { |
156 return computeObjectValue(node); | 172 ClosureRepresentationInfo info = closureDataLookup.getClosureInfo(node); |
173 return computeObjectValue(info.callMethod); | |
157 } | 174 } |
158 return null; | 175 return null; |
159 } | 176 } |
160 | 177 |
161 @override | 178 @override |
162 String computeMemberValue(ir.Member node) { | 179 String computeMemberValue(ir.Member node) { |
163 return computeObjectValue(member); | 180 return computeObjectValue(member); |
164 } | 181 } |
165 } | 182 } |
166 | 183 |
167 abstract class ComputeValueMixin<T> { | 184 abstract class ComputeValueMixin<T> { |
168 bool get verbose; | 185 bool get verbose; |
186 Map<BoxLocal, String> boxNames = <BoxLocal, String>{}; | |
169 ClosureDataLookup<T> get closureDataLookup; | 187 ClosureDataLookup<T> get closureDataLookup; |
170 Link<ScopeInfo> scopeInfoStack = const Link<ScopeInfo>(); | 188 Link<ScopeInfo> scopeInfoStack = const Link<ScopeInfo>(); |
171 ScopeInfo get scopeInfo => scopeInfoStack.head; | 189 ScopeInfo get scopeInfo => scopeInfoStack.head; |
172 CapturedScope capturedScope; | 190 CapturedScope get capturedScope => capturedScopeStack.head; |
191 Link<CapturedScope> capturedScopeStack = const Link<CapturedScope>(); | |
173 Link<ClosureRepresentationInfo> closureRepresentationInfoStack = | 192 Link<ClosureRepresentationInfo> closureRepresentationInfoStack = |
174 const Link<ClosureRepresentationInfo>(); | 193 const Link<ClosureRepresentationInfo>(); |
175 ClosureRepresentationInfo get closureRepresentationInfo => | 194 ClosureRepresentationInfo get closureRepresentationInfo => |
176 closureRepresentationInfoStack.isNotEmpty | 195 closureRepresentationInfoStack.isNotEmpty |
177 ? closureRepresentationInfoStack.head | 196 ? closureRepresentationInfoStack.head |
178 : null; | 197 : null; |
179 | 198 |
180 void pushMember(MemberEntity member) { | 199 void pushMember(MemberEntity member) { |
181 scopeInfoStack = | 200 scopeInfoStack = |
182 scopeInfoStack.prepend(closureDataLookup.getScopeInfo(member)); | 201 scopeInfoStack.prepend(closureDataLookup.getScopeInfo(member)); |
183 capturedScope = closureDataLookup.getCapturedScope(member); | 202 capturedScopeStack = |
203 capturedScopeStack.prepend(closureDataLookup.getCapturedScope(member)); | |
204 if (capturedScope.requiresContextBox) { | |
205 boxNames[capturedScope.context] = 'box${boxNames.length}'; | |
206 } | |
184 dump(member); | 207 dump(member); |
185 } | 208 } |
186 | 209 |
187 void popMember() { | 210 void popMember() { |
188 scopeInfoStack = scopeInfoStack.tail; | 211 scopeInfoStack = scopeInfoStack.tail; |
212 capturedScopeStack = capturedScopeStack.tail; | |
189 } | 213 } |
190 | 214 |
191 void pushLocalFunction(T node) { | 215 void pushLocalFunction(T node) { |
192 closureRepresentationInfoStack = closureRepresentationInfoStack | 216 closureRepresentationInfoStack = closureRepresentationInfoStack |
193 .prepend(closureDataLookup.getClosureInfo(node)); | 217 .prepend(closureDataLookup.getClosureInfo(node)); |
194 dump(node); | 218 dump(node); |
195 } | 219 } |
196 | 220 |
197 void popLocalFunction() { | 221 void popLocalFunction() { |
198 closureRepresentationInfoStack = closureRepresentationInfoStack.tail; | 222 closureRepresentationInfoStack = closureRepresentationInfoStack.tail; |
199 } | 223 } |
200 | 224 |
201 void dump(Object object) { | 225 void dump(Object object) { |
202 if (!verbose) return; | 226 if (!verbose) return; |
203 | 227 |
204 print('object: $object'); | 228 print('object: $object'); |
205 if (object is MemberEntity) { | 229 if (object is MemberEntity) { |
206 print(' scopeInfo (${scopeInfo.runtimeType})'); | |
207 scopeInfo.forEachBoxedVariable((a, b) => print(' boxed1: $a->$b')); | |
208 print(' capturedScope (${capturedScope.runtimeType})'); | 230 print(' capturedScope (${capturedScope.runtimeType})'); |
209 capturedScope.forEachBoxedVariable((a, b) => print(' boxed2: $a->$b')); | 231 capturedScope.forEachBoxedVariable((a, b) => print(' boxed2: $a->$b')); |
210 } | 232 } |
211 print( | 233 print( |
212 ' closureRepresentationInfo (${closureRepresentationInfo.runtimeType})') ; | 234 ' closureRepresentationInfo (${closureRepresentationInfo.runtimeType})') ; |
213 closureRepresentationInfo | 235 closureRepresentationInfo |
214 ?.forEachCapturedVariable((a, b) => print(' captured: $a->$b')); | 236 ?.forEachCapturedVariable((a, b) => print(' captured: $a->$b')); |
215 closureRepresentationInfo | 237 closureRepresentationInfo |
216 ?.forEachFreeVariable((a, b) => print(' free3: $a->$b')); | 238 ?.forEachFreeVariable((a, b) => print(' free3: $a->$b')); |
217 closureRepresentationInfo | 239 closureRepresentationInfo |
218 ?.forEachBoxedVariable((a, b) => print(' boxed3: $a->$b')); | 240 ?.forEachBoxedVariable((a, b) => print(' boxed3: $a->$b')); |
219 } | 241 } |
220 | 242 |
221 /// Compute a string representation of the data stored for [local] in [info]. | 243 /// Compute a string representation of the data stored for [local] in [info]. |
222 String computeLocalValue(Local local) { | 244 String computeLocalValue(Local local) { |
223 List<String> features = <String>[]; | 245 List<String> features = <String>[]; |
224 if (scopeInfo.localIsUsedInTryOrSync(local)) { | 246 if (scopeInfo.localIsUsedInTryOrSync(local)) { |
225 features.add('inTry'); | 247 features.add('inTry'); |
226 // TODO(johnniwinther,efortuna): Should this be enabled and checked? | 248 // TODO(johnniwinther,efortuna): Should this be enabled and checked? |
227 //Expect.isTrue(capturedScope.localIsUsedInTryOrSync(local)); | 249 //Expect.isTrue(capturedScope.localIsUsedInTryOrSync(local)); |
228 } else { | 250 } else { |
229 //Expect.isFalse(capturedScope.localIsUsedInTryOrSync(local)); | 251 //Expect.isFalse(capturedScope.localIsUsedInTryOrSync(local)); |
230 } | 252 } |
231 if (scopeInfo.isBoxed(local)) { | 253 if (capturedScope.isBoxed(local)) { |
232 features.add('boxed'); | 254 features.add('boxed'); |
233 Expect.isTrue(capturedScope.isBoxed(local)); | |
234 } else { | |
235 Expect.isFalse(capturedScope.isBoxed(local)); | |
236 } | 255 } |
237 if (capturedScope.context == local) { | 256 if (capturedScope.context == local) { |
238 features.add('local'); | 257 // TODO(johnniwinther): This can't happen! |
Siggi Cherem (dart-lang)
2017/08/28 17:33:45
nit: rewrite to explain what this means? not sure
| |
258 features.add('box'); | |
Siggi Cherem (dart-lang)
2017/08/28 17:33:45
maybe use a different string to denote this is an
| |
239 } | 259 } |
240 if (capturedScope is CapturedLoopScope) { | 260 if (capturedScope is CapturedLoopScope) { |
241 CapturedLoopScope loopScope = capturedScope; | 261 CapturedLoopScope loopScope = capturedScope; |
242 if (loopScope.boxedLoopVariables.contains(local)) { | 262 if (loopScope.boxedLoopVariables.contains(local)) { |
243 features.add('loop'); | 263 features.add('loop'); |
244 } | 264 } |
245 } | 265 } |
246 if (closureRepresentationInfo != null) { | 266 if (closureRepresentationInfo != null) { |
247 if (closureRepresentationInfo.createdFieldEntities.contains(local)) { | 267 if (closureRepresentationInfo.createdFieldEntities.contains(local)) { |
248 features.add('field'); | 268 features.add('field'); |
249 } | 269 } |
250 if (closureRepresentationInfo.isVariableBoxed(local)) { | |
251 features.add('variable-boxed'); | |
252 } | |
253 } | 270 } |
254 // TODO(johnniwinther,efortuna): Add more info? | 271 // TODO(johnniwinther,efortuna): Add more info? |
255 return (features.toList()..sort()).join(','); | 272 return (features.toList()..sort()).join(','); |
256 } | 273 } |
257 | 274 |
258 String computeObjectValue(Object object) { | 275 String computeObjectValue(MemberEntity member) { |
259 Map<String, String> features = <String, String>{}; | 276 Map<String, String> features = <String, String>{}; |
260 | 277 |
261 void addLocals(String name, forEach(f(Local local, _))) { | 278 void addLocals(String name, forEach(f(Local local, _))) { |
262 List<String> names = <String>[]; | 279 List<String> names = <String>[]; |
263 forEach((Local local, _) { | 280 forEach((Local local, _) { |
264 if (local is BoxLocal) { | 281 if (local is BoxLocal) { |
265 names.add('box'); | 282 names.add(boxNames[local]); |
266 } else { | 283 } else { |
267 names.add(local.name); | 284 names.add(local.name); |
268 } | 285 } |
269 }); | 286 }); |
270 String value = names.isEmpty ? null : '[${(names..sort()).join(',')}]'; | 287 String value = names.isEmpty ? null : '[${(names..sort()).join(',')}]'; |
271 if (features.containsKey(name)) { | 288 if (features.containsKey(name)) { |
272 Expect.equals( | 289 Expect.equals( |
273 features[name], value, "Inconsistent values for $name on $object."); | 290 features[name], value, "Inconsistent values for $name on $member."); |
274 } | 291 } |
275 features[name] = value; | 292 features[name] = value; |
276 } | 293 } |
277 | 294 |
278 if (object is MemberEntity) { | 295 if (scopeInfo.thisLocal != null) { |
279 if (scopeInfo.thisLocal != null) { | 296 features['hasThis'] = ''; |
280 features['hasThis'] = ''; | |
281 } | |
282 addLocals('boxed', scopeInfo.forEachBoxedVariable); | |
283 | |
284 if (capturedScope.requiresContextBox) { | |
285 features['requiresBox'] = ''; | |
286 } | |
287 addLocals('boxed', capturedScope.forEachBoxedVariable); | |
288 } | 297 } |
298 if (capturedScope.requiresContextBox) { | |
299 //print('------------- $object from $capturedScope'); | |
Siggi Cherem (dart-lang)
2017/08/28 17:33:45
delete
| |
300 features['box'] = boxNames[capturedScope.context]; | |
301 } | |
302 addLocals('boxed', capturedScope.forEachBoxedVariable); | |
Siggi Cherem (dart-lang)
2017/08/28 17:33:45
an idea: since this and requiresContextBox are cor
| |
289 | 303 |
290 if (closureRepresentationInfo != null) { | 304 if (closureRepresentationInfo != null) { |
291 addLocals('boxed', closureRepresentationInfo.forEachBoxedVariable); | |
292 addLocals('captured', closureRepresentationInfo.forEachCapturedVariable); | |
293 addLocals('free', closureRepresentationInfo.forEachFreeVariable); | 305 addLocals('free', closureRepresentationInfo.forEachFreeVariable); |
294 } | 306 } |
295 | 307 |
296 StringBuffer sb = new StringBuffer(); | 308 StringBuffer sb = new StringBuffer(); |
297 bool needsComma = false; | 309 bool needsComma = false; |
298 for (String name in features.keys.toList()..sort()) { | 310 for (String name in features.keys.toList()..sort()) { |
299 String value = features[name]; | 311 String value = features[name]; |
300 if (value != null) { | 312 if (value != null) { |
301 if (needsComma) { | 313 if (needsComma) { |
302 sb.write(','); | 314 sb.write(','); |
303 } | 315 } |
304 sb.write(name); | 316 sb.write(name); |
305 if (value != '') { | 317 if (value != '') { |
306 sb.write('='); | 318 sb.write('='); |
307 sb.write(value); | 319 sb.write(value); |
308 } | 320 } |
309 needsComma = true; | 321 needsComma = true; |
310 } | 322 } |
311 } | 323 } |
312 return sb.toString(); | 324 return sb.toString(); |
313 } | 325 } |
314 } | 326 } |
OLD | NEW |