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 'package:kernel/ast.dart' as ir; | 5 import 'package:kernel/ast.dart' as ir; |
6 | 6 |
7 import '../closure.dart'; | 7 import '../closure.dart'; |
8 import '../common.dart'; | 8 import '../common.dart'; |
9 import '../common/tasks.dart'; | 9 import '../common/tasks.dart'; |
10 import '../elements/elements.dart'; | 10 import '../elements/elements.dart'; |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
54 | 54 |
55 /// The combined steps of generating our intermediate representation of | 55 /// The combined steps of generating our intermediate representation of |
56 /// closures that need to be rewritten and generating the element model. | 56 /// closures that need to be rewritten and generating the element model. |
57 /// Ultimately these two steps will be split apart with the second step | 57 /// Ultimately these two steps will be split apart with the second step |
58 /// happening later in compilation just before codegen. These steps are | 58 /// happening later in compilation just before codegen. These steps are |
59 /// combined here currently to provide a consistent interface to the rest of | 59 /// combined here currently to provide a consistent interface to the rest of |
60 /// the compiler until we are ready to separate these phases. | 60 /// the compiler until we are ready to separate these phases. |
61 @override | 61 @override |
62 void convertClosures(Iterable<MemberEntity> processedEntities, | 62 void convertClosures(Iterable<MemberEntity> processedEntities, |
63 ClosedWorldRefiner closedWorldRefiner) { | 63 ClosedWorldRefiner closedWorldRefiner) { |
64 var closuresToGenerate = <MemberEntity, Map<ir.TreeNode, ScopeInfo>>{}; | 64 Map<MemberEntity, ClosureModel> closureModels = |
65 _computeClosureModels(processedEntities); | |
66 _createClosureEntities(closureModels, closedWorldRefiner); | |
67 } | |
68 | |
69 // TODO(johnniwinther,efortuna): Compute this during resolution. | |
Emily Fortuna
2017/07/26 18:04:23
see also the existing documentation on convertClos
Johnni Winther
2017/08/02 08:03:13
Done.
| |
70 Map<MemberEntity, ClosureModel> _computeClosureModels( | |
71 Iterable<MemberEntity> processedEntities) { | |
72 Map<MemberEntity, ClosureModel> closureModels = | |
73 <MemberEntity, ClosureModel>{}; | |
65 | 74 |
66 processedEntities.forEach((MemberEntity kEntity) { | 75 processedEntities.forEach((MemberEntity kEntity) { |
67 MemberEntity entity = _kToJElementMap.toBackendMember(kEntity); | 76 MemberEntity entity = _kToJElementMap.toBackendMember(kEntity); |
68 if (entity.isAbstract) return; | 77 if (entity.isAbstract) return; |
69 if (entity.isField && !entity.isInstanceMember) { | 78 if (entity.isField && !entity.isInstanceMember) { |
70 MemberDefinition definition = _elementMap.getMemberDefinition(entity); | 79 MemberDefinition definition = _elementMap.getMemberDefinition(entity); |
71 assert(definition.kind == MemberKind.regular, | 80 assert(definition.kind == MemberKind.regular, |
72 failedAt(entity, "Unexpected member definition $definition")); | 81 failedAt(entity, "Unexpected member definition $definition")); |
73 ir.Field field = definition.node; | 82 ir.Field field = definition.node; |
74 // Skip top-level/static fields without an initializer. | 83 // Skip top-level/static fields without an initializer. |
75 if (field.initializer == null) return; | 84 if (field.initializer == null) return; |
76 } | 85 } |
77 closuresToGenerate[entity] = | 86 closureModels[entity] = _buildClosureModel(entity); |
78 _buildClosureModel(entity, closedWorldRefiner); | |
79 }); | 87 }); |
88 return closureModels; | |
89 } | |
80 | 90 |
81 closuresToGenerate.forEach( | 91 void _createClosureEntities(Map<MemberEntity, ClosureModel> closureModels, |
82 (MemberEntity member, Map<ir.TreeNode, ScopeInfo> closuresToGenerate) { | 92 ClosedWorldRefiner closedWorldRefiner) { |
93 closureModels.forEach((MemberEntity member, ClosureModel model) { | |
94 KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member); | |
95 if (model.scopeInfo != null) { | |
96 _scopeMap[member] = new JsScopeInfo.from(model.scopeInfo, localsMap); | |
97 } | |
98 | |
99 model.capturedScopesMap | |
100 .forEach((ir.Node node, KernelCapturedScope scope) { | |
101 if (scope is KernelCapturedLoopScope) { | |
102 _capturedScopesMap[node] = | |
103 new JsCapturedLoopScope.from(scope, localsMap); | |
104 } else { | |
105 _capturedScopesMap[node] = new JsCapturedScope.from(scope, localsMap); | |
106 } | |
107 }); | |
108 | |
109 Map<ir.TreeNode, KernelScopeInfo> closuresToGenerate = | |
110 model.closuresToGenerate; | |
83 for (ir.TreeNode node in closuresToGenerate.keys) { | 111 for (ir.TreeNode node in closuresToGenerate.keys) { |
84 _produceSyntheticElements( | 112 _produceSyntheticElements( |
85 member, node, closuresToGenerate[node], closedWorldRefiner); | 113 member, node, closuresToGenerate[node], closedWorldRefiner); |
86 } | 114 } |
87 }); | 115 }); |
88 } | 116 } |
89 | 117 |
90 /// Inspect members and mark if those members capture any state that needs to | 118 /// Inspect members and mark if those members capture any state that needs to |
91 /// be marked as free variables. | 119 /// be marked as free variables. |
92 Map<ir.TreeNode, ScopeInfo> _buildClosureModel( | 120 ClosureModel _buildClosureModel(MemberEntity entity) { |
93 MemberEntity entity, ClosedWorldRefiner closedWorldRefiner) { | 121 ClosureModel model = new ClosureModel(); |
94 assert(!_scopeMap.containsKey(entity), | |
95 failedAt(entity, "ScopeInfo already computed for $entity.")); | |
96 Map<ir.TreeNode, ScopeInfo> closuresToGenerate = <ir.TreeNode, ScopeInfo>{}; | |
97 MemberDefinition definition = _elementMap.getMemberDefinition(entity); | 122 MemberDefinition definition = _elementMap.getMemberDefinition(entity); |
98 switch (definition.kind) { | 123 switch (definition.kind) { |
99 case MemberKind.regular: | 124 case MemberKind.regular: |
100 case MemberKind.constructor: | 125 case MemberKind.constructor: |
101 break; | 126 break; |
102 default: | 127 default: |
103 failedAt(entity, "Unexpected member definition $definition"); | 128 failedAt(entity, "Unexpected member definition $definition"); |
104 } | 129 } |
105 ir.Node node = definition.node; | 130 ir.Node node = definition.node; |
106 assert(!_scopeMap.containsKey(entity), | 131 CapturedScopeBuilder translator = new CapturedScopeBuilder(model, |
107 failedAt(entity, "CaptureScope already computed for $node.")); | 132 hasThisLocal: entity.isInstanceMember || entity.isConstructor); |
108 CapturedScopeBuilder translator = new CapturedScopeBuilder( | |
109 entity, | |
110 _capturedScopesMap, | |
111 _scopeMap, | |
112 closuresToGenerate, | |
113 _globalLocalsMap.getLocalsMap(entity)); | |
114 if (entity.isField) { | 133 if (entity.isField) { |
115 if (node is ir.Field && node.initializer != null) { | 134 if (node is ir.Field && node.initializer != null) { |
116 translator.translateLazyInitializer(node); | 135 translator.translateLazyInitializer(node); |
117 } | 136 } |
118 } else { | 137 } else { |
119 assert(node is ir.Procedure || node is ir.Constructor); | 138 assert(node is ir.Procedure || node is ir.Constructor); |
120 translator.translateConstructorOrProcedure(node); | 139 translator.translateConstructorOrProcedure(node); |
121 } | 140 } |
122 return closuresToGenerate; | 141 return model; |
123 } | 142 } |
124 | 143 |
125 /// Given what variables are captured at each point, construct closure classes | 144 /// Given what variables are captured at each point, construct closure classes |
126 /// with fields containing the captured variables to replicate the Dart | 145 /// with fields containing the captured variables to replicate the Dart |
127 /// closure semantics in JS. If this closure captures any variables (meaning | 146 /// closure semantics in JS. If this closure captures any variables (meaning |
128 /// the closure accesses a variable that gets accessed at some point), then | 147 /// the closure accesses a variable that gets accessed at some point), then |
129 /// boxForCapturedVariables stores the local context for those variables. | 148 /// boxForCapturedVariables stores the local context for those variables. |
130 /// If no variables are captured, this parameter is null. | 149 /// If no variables are captured, this parameter is null. |
131 void _produceSyntheticElements( | 150 void _produceSyntheticElements( |
132 MemberEntity member, | 151 MemberEntity member, |
133 ir.TreeNode /* ir.Member | ir.FunctionNode */ node, | 152 ir.TreeNode /* ir.Member | ir.FunctionNode */ node, |
134 ScopeInfo info, | 153 KernelScopeInfo info, |
135 ClosedWorldRefiner closedWorldRefiner) { | 154 ClosedWorldRefiner closedWorldRefiner) { |
136 String name = _computeClosureName(node); | 155 String name = _computeClosureName(node); |
137 KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member); | 156 KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member); |
138 KernelClosureClass closureClass = new KernelClosureClass.fromScopeInfo( | 157 KernelClosureClass closureClass = new KernelClosureClass.fromScopeInfo( |
139 name, member.library, info, node.location, localsMap); | 158 name, member.library, info, node.location, localsMap); |
140 | 159 |
141 Entity entity; | 160 Entity entity; |
142 if (node is ir.Member) { | 161 if (node is ir.Member) { |
143 entity = member; | 162 entity = member; |
144 } else { | 163 } else { |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
233 | 252 |
234 @override | 253 @override |
235 // TODO(efortuna): Eventually closureRepresentationMap[node] should always be | 254 // TODO(efortuna): Eventually closureRepresentationMap[node] should always be |
236 // non-null, and we should just test that with an assert. | 255 // non-null, and we should just test that with an assert. |
237 ClosureRepresentationInfo getClosureRepresentationInfo(Entity entity) { | 256 ClosureRepresentationInfo getClosureRepresentationInfo(Entity entity) { |
238 return _closureRepresentationMap[entity] ?? | 257 return _closureRepresentationMap[entity] ?? |
239 const ClosureRepresentationInfo(); | 258 const ClosureRepresentationInfo(); |
240 } | 259 } |
241 } | 260 } |
242 | 261 |
243 class KernelScopeInfo extends ScopeInfo { | 262 class KernelScopeInfo { |
263 final Set<ir.VariableDeclaration> localsUsedInTryOrSync; | |
264 final bool hasThisLocal; | |
265 final Set<ir.VariableDeclaration> boxedVariables; | |
266 | |
267 /// The set of variables that were defined in another scope, but are used in | |
268 /// this scope. | |
269 Set<ir.VariableDeclaration> freeVariables = new Set<ir.VariableDeclaration>(); | |
270 | |
271 KernelScopeInfo(this.hasThisLocal) | |
272 : localsUsedInTryOrSync = new Set<ir.VariableDeclaration>(), | |
273 boxedVariables = new Set<ir.VariableDeclaration>(); | |
274 | |
275 KernelScopeInfo.from(this.hasThisLocal, KernelScopeInfo info) | |
276 : localsUsedInTryOrSync = info.localsUsedInTryOrSync, | |
277 boxedVariables = info.boxedVariables; | |
278 | |
279 KernelScopeInfo.withBoxedVariables(this.boxedVariables, | |
280 this.localsUsedInTryOrSync, this.freeVariables, this.hasThisLocal); | |
281 | |
282 String toString() { | |
283 StringBuffer sb = new StringBuffer(); | |
284 sb.write('this=$hasThisLocal,'); | |
285 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}'); | |
286 return sb.toString(); | |
287 } | |
288 } | |
289 | |
290 class JsScopeInfo extends ScopeInfo { | |
244 final Set<Local> localsUsedInTryOrSync; | 291 final Set<Local> localsUsedInTryOrSync; |
245 final Local thisLocal; | 292 final Local thisLocal; |
246 final Set<Local> boxedVariables; | 293 final Set<Local> boxedVariables; |
247 | 294 |
248 /// The set of variables that were defined in another scope, but are used in | 295 /// The set of variables that were defined in another scope, but are used in |
249 /// this scope. | 296 /// this scope. |
250 Set<ir.VariableDeclaration> freeVariables = new Set<ir.VariableDeclaration>(); | 297 final Set<Local> freeVariables; |
251 | 298 |
252 KernelScopeInfo(this.thisLocal) | 299 JsScopeInfo(this.thisLocal, this.localsUsedInTryOrSync, this.boxedVariables, |
253 : localsUsedInTryOrSync = new Set<Local>(), | 300 this.freeVariables); |
254 boxedVariables = new Set<Local>(); | |
255 | 301 |
256 KernelScopeInfo.from(this.thisLocal, KernelScopeInfo info) | 302 JsScopeInfo.from(KernelScopeInfo info, KernelToLocalsMap localsMap) |
257 : localsUsedInTryOrSync = info.localsUsedInTryOrSync, | 303 : this.thisLocal = |
258 boxedVariables = info.boxedVariables; | 304 info.hasThisLocal ? new ThisLocal(localsMap.currentMember) : null, |
259 | 305 this.localsUsedInTryOrSync = |
260 KernelScopeInfo.withBoxedVariables(this.boxedVariables, | 306 info.localsUsedInTryOrSync.map(localsMap.getLocalVariable).toSet(), |
261 this.localsUsedInTryOrSync, this.freeVariables, this.thisLocal); | 307 this.boxedVariables = |
308 info.boxedVariables.map(localsMap.getLocalVariable).toSet(), | |
309 this.freeVariables = | |
310 info.freeVariables.map(localsMap.getLocalVariable).toSet(); | |
262 | 311 |
263 void forEachBoxedVariable(f(Local local, FieldEntity field)) { | 312 void forEachBoxedVariable(f(Local local, FieldEntity field)) { |
264 boxedVariables.forEach((Local l) { | 313 boxedVariables.forEach((Local l) { |
265 // TODO(efortuna): add FieldEntities as created. | 314 // TODO(efortuna): add FieldEntities as created. |
266 f(l, null); | 315 f(l, null); |
267 }); | 316 }); |
268 } | 317 } |
269 | 318 |
270 bool localIsUsedInTryOrSync(Local variable) => | 319 bool localIsUsedInTryOrSync(Local variable) => |
271 localsUsedInTryOrSync.contains(variable); | 320 localsUsedInTryOrSync.contains(variable); |
272 | 321 |
273 String toString() { | 322 String toString() { |
274 StringBuffer sb = new StringBuffer(); | 323 StringBuffer sb = new StringBuffer(); |
275 sb.write('this=$thisLocal,'); | 324 sb.write('this=$thisLocal,'); |
276 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}'); | 325 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}'); |
277 return sb.toString(); | 326 return sb.toString(); |
278 } | 327 } |
279 | 328 |
280 bool isBoxed(Local variable) => boxedVariables.contains(variable); | 329 bool isBoxed(Local variable) => boxedVariables.contains(variable); |
281 } | 330 } |
282 | 331 |
283 class KernelCapturedScope extends KernelScopeInfo implements CapturedScope { | 332 class KernelCapturedScope extends KernelScopeInfo { |
284 final Local context; | 333 final ir.TreeNode context; |
285 | 334 |
286 KernelCapturedScope( | 335 KernelCapturedScope( |
287 Set<Local> boxedVariables, | 336 Set<ir.VariableDeclaration> boxedVariables, |
288 this.context, | 337 this.context, |
289 Set<Local> localsUsedInTryOrSync, | 338 Set<ir.VariableDeclaration> localsUsedInTryOrSync, |
290 Set<ir.VariableDeclaration> freeVariables, | 339 Set<ir.VariableDeclaration> freeVariables, |
291 Local thisLocal) | 340 bool hasThisLocal) |
292 : super.withBoxedVariables( | 341 : super.withBoxedVariables( |
293 boxedVariables, localsUsedInTryOrSync, freeVariables, thisLocal); | 342 boxedVariables, localsUsedInTryOrSync, freeVariables, hasThisLocal); |
294 | 343 |
295 bool get requiresContextBox => boxedVariables.isNotEmpty; | 344 bool get requiresContextBox => boxedVariables.isNotEmpty; |
296 } | 345 } |
297 | 346 |
298 class KernelCapturedLoopScope extends KernelCapturedScope | 347 class JsCapturedScope extends JsScopeInfo implements CapturedScope { |
299 implements CapturedLoopScope { | 348 final Local context; |
349 | |
350 JsCapturedScope.from( | |
351 KernelCapturedScope capturedScope, KernelToLocalsMap localsMap) | |
352 : this.context = localsMap.getLocalVariable(capturedScope.context), | |
353 super.from(capturedScope, localsMap); | |
354 | |
355 bool get requiresContextBox => boxedVariables.isNotEmpty; | |
356 } | |
357 | |
358 class KernelCapturedLoopScope extends KernelCapturedScope { | |
359 final List<ir.VariableDeclaration> boxedLoopVariables; | |
360 | |
361 KernelCapturedLoopScope( | |
362 Set<ir.VariableDeclaration> boxedVariables, | |
363 this.boxedLoopVariables, | |
364 ir.TreeNode context, | |
365 Set<ir.VariableDeclaration> localsUsedInTryOrSync, | |
366 Set<ir.VariableDeclaration> freeVariables, | |
367 bool hasThisLocal) | |
368 : super(boxedVariables, context, localsUsedInTryOrSync, freeVariables, | |
369 hasThisLocal); | |
370 | |
371 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; | |
372 } | |
373 | |
374 class JsCapturedLoopScope extends JsCapturedScope implements CapturedLoopScope { | |
300 final List<Local> boxedLoopVariables; | 375 final List<Local> boxedLoopVariables; |
301 | 376 |
302 KernelCapturedLoopScope( | 377 JsCapturedLoopScope.from( |
303 Set<Local> boxedVariables, | 378 KernelCapturedLoopScope capturedScope, KernelToLocalsMap localsMap) |
304 this.boxedLoopVariables, | 379 : this.boxedLoopVariables = capturedScope.boxedLoopVariables |
305 Local context, | 380 .map(localsMap.getLocalVariable) |
306 Set<Local> localsUsedInTryOrSync, | 381 .toList(), |
307 Set<ir.VariableDeclaration> freeVariables, | 382 super.from(capturedScope, localsMap); |
308 Local thisLocal) | |
309 : super(boxedVariables, context, localsUsedInTryOrSync, freeVariables, | |
310 thisLocal); | |
311 | 383 |
312 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; | 384 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; |
313 } | 385 } |
314 | 386 |
315 // TODO(johnniwinther): Add unittest for the computed [ClosureClass]. | 387 // TODO(johnniwinther): Add unittest for the computed [ClosureClass]. |
316 class KernelClosureClass extends KernelScopeInfo | 388 class KernelClosureClass extends JsScopeInfo |
317 implements ClosureRepresentationInfo, JClass { | 389 implements ClosureRepresentationInfo, JClass { |
318 final ir.Location location; | 390 final ir.Location location; |
319 | 391 |
320 final String name; | 392 final String name; |
321 final JLibrary library; | 393 final JLibrary library; |
322 | 394 |
323 /// Index into the classData, classList and classEnvironment lists where this | 395 /// Index into the classData, classList and classEnvironment lists where this |
324 /// entity is stored in [JsToFrontendMapImpl]. | 396 /// entity is stored in [JsToFrontendMapImpl]. |
325 int classIndex; | 397 int classIndex; |
326 | 398 |
327 final Map<Local, JField> localToFieldMap = new Map<Local, JField>(); | 399 final Map<Local, JField> localToFieldMap = new Map<Local, JField>(); |
328 | 400 |
329 KernelClosureClass.fromScopeInfo(this.name, this.library, | 401 KernelClosureClass.fromScopeInfo(this.name, this.library, |
330 KernelScopeInfo info, this.location, KernelToLocalsMap localsMap) | 402 KernelScopeInfo info, this.location, KernelToLocalsMap localsMap) |
331 : super.from(info.thisLocal, info) { | 403 : super.from(info, localsMap) { |
332 // Make a corresponding field entity in this closure class for every single | 404 // Make a corresponding field entity in this closure class for every single |
333 // freeVariable in the KernelScopeInfo.freeVariable. | 405 // freeVariable in the KernelScopeInfo.freeVariable. |
334 int i = 0; | 406 int i = 0; |
335 for (ir.VariableDeclaration variable in info.freeVariables) { | 407 for (ir.VariableDeclaration variable in info.freeVariables) { |
336 // NOTE: This construction order may be slightly different than the | 408 // NOTE: This construction order may be slightly different than the |
337 // old Element version. The old version did all the boxed items and then | 409 // old Element version. The old version did all the boxed items and then |
338 // all the others. | 410 // all the others. |
339 Local capturedLocal = localsMap.getLocalVariable(variable); | 411 Local capturedLocal = localsMap.getLocalVariable(variable); |
340 if (info.isBoxed(capturedLocal)) { | 412 if (isBoxed(capturedLocal)) { |
341 // TODO(efortuna): Coming soon. | 413 // TODO(efortuna): Coming soon. |
342 } else { | 414 } else { |
343 localToFieldMap[capturedLocal] = new ClosureField( | 415 localToFieldMap[capturedLocal] = new ClosureField( |
344 _getClosureVariableName(capturedLocal.name, i), | 416 _getClosureVariableName(capturedLocal.name, i), |
345 this, | 417 this, |
346 variable.isConst, | 418 variable.isConst, |
347 variable.isFinal || variable.isConst); | 419 variable.isFinal || variable.isConst); |
348 // TODO(efortuna): These probably need to get registered somewhere. | 420 // TODO(efortuna): These probably need to get registered somewhere. |
349 } | 421 } |
350 i++; | 422 i++; |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
415 ClosureClassDefinition(this.cls, this.location); | 487 ClosureClassDefinition(this.cls, this.location); |
416 | 488 |
417 ClassKind get kind => ClassKind.closure; | 489 ClassKind get kind => ClassKind.closure; |
418 | 490 |
419 ir.Node get node => | 491 ir.Node get node => |
420 throw new UnsupportedError('ClosureClassDefinition.node for $cls'); | 492 throw new UnsupportedError('ClosureClassDefinition.node for $cls'); |
421 | 493 |
422 String toString() => | 494 String toString() => |
423 'ClosureClassDefinition(kind:$kind,cls:$cls,location:$location)'; | 495 'ClosureClassDefinition(kind:$kind,cls:$cls,location:$location)'; |
424 } | 496 } |
497 | |
498 /// Collection of closure data collected for a single member. | |
499 class ClosureModel { | |
500 /// Collection [ScopeInfo] data for the member, if any. | |
Emily Fortuna
2017/07/26 18:04:23
please add clarification in this comment for the "
Johnni Winther
2017/08/02 08:03:13
The 'not any' case seems to be an inconsistency in
| |
501 KernelScopeInfo scopeInfo; | |
502 | |
503 /// Collected [CapturedScope] data for nodes. | |
504 Map<ir.Node, KernelCapturedScope> capturedScopesMap = | |
505 <ir.Node, KernelCapturedScope>{}; | |
506 | |
507 /// Collected [ScopeInof] data for nodes. | |
Emily Fortuna
2017/07/26 18:04:22
ScopeInof->ScopeInfo
Johnni Winther
2017/08/02 08:03:13
Done.
| |
508 Map<ir.TreeNode, KernelScopeInfo> closuresToGenerate = | |
509 <ir.TreeNode, KernelScopeInfo>{}; | |
510 } | |
OLD | NEW |