Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(545)

Side by Side Diff: tests/compiler/dart2js/closure/closure_test.dart

Issue 3003963002: Add more tests for closures and change closure indexing to be by FunctionExpression or FunctionDecl… (Closed)
Patch Set: . Created 3 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698