| 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 '../constants/expressions.dart'; | 10 import '../constants/expressions.dart'; |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 93 @override | 93 @override |
| 94 void convertClosures(Iterable<MemberEntity> processedEntities, | 94 void convertClosures(Iterable<MemberEntity> processedEntities, |
| 95 ClosedWorldRefiner closedWorldRefiner) { | 95 ClosedWorldRefiner closedWorldRefiner) { |
| 96 _createClosureEntities(_closureModels, closedWorldRefiner); | 96 _createClosureEntities(_closureModels, closedWorldRefiner); |
| 97 } | 97 } |
| 98 | 98 |
| 99 void _createClosureEntities(Map<MemberEntity, ScopeModel> closureModels, | 99 void _createClosureEntities(Map<MemberEntity, ScopeModel> closureModels, |
| 100 JsClosedWorld closedWorldRefiner) { | 100 JsClosedWorld closedWorldRefiner) { |
| 101 closureModels.forEach((MemberEntity member, ScopeModel model) { | 101 closureModels.forEach((MemberEntity member, ScopeModel model) { |
| 102 KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member); | 102 KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member); |
| 103 if (model.scopeInfo != null) { | 103 Map<Local, JRecordField> allBoxedVariables = |
| 104 _scopeMap[member] = new JsScopeInfo.from(model.scopeInfo, localsMap); | 104 _elementMap.makeRecordContainer(model.scopeInfo, member, localsMap); |
| 105 } | 105 _scopeMap[member] = |
| 106 new JsScopeInfo.from(allBoxedVariables, model.scopeInfo, localsMap); |
| 106 | 107 |
| 107 model.capturedScopesMap | 108 model.capturedScopesMap |
| 108 .forEach((ir.Node node, KernelCapturedScope scope) { | 109 .forEach((ir.Node node, KernelCapturedScope scope) { |
| 110 Map<Local, JRecordField> boxedVariables = |
| 111 _elementMap.makeRecordContainer(scope, member, localsMap); |
| 109 if (scope is KernelCapturedLoopScope) { | 112 if (scope is KernelCapturedLoopScope) { |
| 110 _capturedScopesMap[node] = | 113 _capturedScopesMap[node] = |
| 111 new JsCapturedLoopScope.from(scope, localsMap); | 114 new JsCapturedLoopScope.from(boxedVariables, scope, localsMap); |
| 112 } else { | 115 } else { |
| 113 _capturedScopesMap[node] = new JsCapturedScope.from(scope, localsMap); | 116 _capturedScopesMap[node] = |
| 117 new JsCapturedScope.from(boxedVariables, scope, localsMap); |
| 114 } | 118 } |
| 119 allBoxedVariables.addAll(boxedVariables); |
| 115 }); | 120 }); |
| 116 | 121 |
| 117 Map<ir.TreeNode, KernelScopeInfo> closuresToGenerate = | 122 Map<ir.TreeNode, KernelScopeInfo> closuresToGenerate = |
| 118 model.closuresToGenerate; | 123 model.closuresToGenerate; |
| 119 for (ir.TreeNode node in closuresToGenerate.keys) { | 124 for (ir.TreeNode node in closuresToGenerate.keys) { |
| 120 ir.FunctionNode functionNode; | 125 ir.FunctionNode functionNode; |
| 121 if (node is ir.FunctionDeclaration) { | 126 if (node is ir.FunctionDeclaration) { |
| 122 functionNode = node.function; | 127 functionNode = node.function; |
| 123 } else if (node is ir.FunctionExpression) { | 128 } else if (node is ir.FunctionExpression) { |
| 124 functionNode = node.function; | 129 functionNode = node.function; |
| 125 } else { | 130 } else { |
| 126 failedAt(member, "Unexpected closure node ${node}"); | 131 failedAt(member, "Unexpected closure node ${node}"); |
| 127 } | 132 } |
| 128 KernelClosureClass closureClass = _produceSyntheticElements( | 133 KernelClosureClass closureClass = _produceSyntheticElements( |
| 129 member, functionNode, closuresToGenerate[node], closedWorldRefiner); | 134 member, |
| 135 functionNode, |
| 136 closuresToGenerate[node], |
| 137 allBoxedVariables, |
| 138 closedWorldRefiner); |
| 130 // Add also for the call method. | 139 // Add also for the call method. |
| 131 _scopeMap[closureClass.callMethod] = closureClass; | 140 _scopeMap[closureClass.callMethod] = closureClass; |
| 132 } | 141 } |
| 133 }); | 142 }); |
| 134 } | 143 } |
| 135 | 144 |
| 136 /// Given what variables are captured at each point, construct closure classes | 145 /// Given what variables are captured at each point, construct closure classes |
| 137 /// with fields containing the captured variables to replicate the Dart | 146 /// with fields containing the captured variables to replicate the Dart |
| 138 /// closure semantics in JS. If this closure captures any variables (meaning | 147 /// closure semantics in JS. If this closure captures any variables (meaning |
| 139 /// the closure accesses a variable that gets accessed at some point), then | 148 /// the closure accesses a variable that gets accessed at some point), then |
| 140 /// boxForCapturedVariables stores the local context for those variables. | 149 /// boxForCapturedVariables stores the local context for those variables. |
| 141 /// If no variables are captured, this parameter is null. | 150 /// If no variables are captured, this parameter is null. |
| 142 KernelClosureClass _produceSyntheticElements( | 151 KernelClosureClass _produceSyntheticElements( |
| 143 MemberEntity member, | 152 MemberEntity member, |
| 144 ir.FunctionNode node, | 153 ir.FunctionNode node, |
| 145 KernelScopeInfo info, | 154 KernelScopeInfo info, |
| 155 Map<Local, JRecordField> boxedVariables, |
| 146 JsClosedWorld closedWorldRefiner) { | 156 JsClosedWorld closedWorldRefiner) { |
| 147 KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member); | 157 KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member); |
| 148 KernelClosureClass closureClass = closedWorldRefiner.buildClosureClass( | 158 KernelClosureClass closureClass = closedWorldRefiner.buildClosureClass( |
| 149 member, node, member.library, info, node.location, localsMap); | 159 member, |
| 160 node, |
| 161 member.library, |
| 162 boxedVariables, |
| 163 info, |
| 164 node.location, |
| 165 localsMap); |
| 150 | 166 |
| 151 // We want the original declaration where that function is used to point | 167 // We want the original declaration where that function is used to point |
| 152 // to the correct closure class. | 168 // to the correct closure class. |
| 153 _memberClosureRepresentationMap[closureClass.callMethod] = closureClass; | 169 _memberClosureRepresentationMap[closureClass.callMethod] = closureClass; |
| 154 _globalLocalsMap.setLocalsMap(closureClass.callMethod, localsMap); | 170 _globalLocalsMap.setLocalsMap(closureClass.callMethod, localsMap); |
| 155 if (node.parent is ir.Member) { | 171 if (node.parent is ir.Member) { |
| 156 assert(_elementMap.getMember(node.parent) == member); | 172 assert(_elementMap.getMember(node.parent) == member); |
| 157 _memberClosureRepresentationMap[member] = closureClass; | 173 _memberClosureRepresentationMap[member] = closureClass; |
| 158 } else { | 174 } else { |
| 159 assert(node.parent is ir.FunctionExpression || | 175 assert(node.parent is ir.FunctionExpression || |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 258 StringBuffer sb = new StringBuffer(); | 274 StringBuffer sb = new StringBuffer(); |
| 259 sb.write('this=$hasThisLocal,'); | 275 sb.write('this=$hasThisLocal,'); |
| 260 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}'); | 276 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}'); |
| 261 return sb.toString(); | 277 return sb.toString(); |
| 262 } | 278 } |
| 263 } | 279 } |
| 264 | 280 |
| 265 class JsScopeInfo extends ScopeInfo { | 281 class JsScopeInfo extends ScopeInfo { |
| 266 final Set<Local> localsUsedInTryOrSync; | 282 final Set<Local> localsUsedInTryOrSync; |
| 267 final Local thisLocal; | 283 final Local thisLocal; |
| 268 final Set<Local> boxedVariables; | 284 final Map<Local, JRecordField> boxedVariables; |
| 269 | 285 |
| 270 /// The set of variables that were defined in another scope, but are used in | 286 /// The set of variables that were defined in another scope, but are used in |
| 271 /// this scope. | 287 /// this scope. |
| 272 final Set<Local> freeVariables; | 288 final Set<Local> freeVariables; |
| 273 | 289 |
| 274 JsScopeInfo(this.thisLocal, this.localsUsedInTryOrSync, this.boxedVariables, | 290 JsScopeInfo.from( |
| 275 this.freeVariables); | 291 this.boxedVariables, KernelScopeInfo info, KernelToLocalsMap localsMap) |
| 276 | |
| 277 JsScopeInfo.from(KernelScopeInfo info, KernelToLocalsMap localsMap) | |
| 278 : this.thisLocal = | 292 : this.thisLocal = |
| 279 info.hasThisLocal ? new ThisLocal(localsMap.currentMember) : null, | 293 info.hasThisLocal ? new ThisLocal(localsMap.currentMember) : null, |
| 280 this.localsUsedInTryOrSync = | 294 this.localsUsedInTryOrSync = |
| 281 info.localsUsedInTryOrSync.map(localsMap.getLocalVariable).toSet(), | 295 info.localsUsedInTryOrSync.map(localsMap.getLocalVariable).toSet(), |
| 282 this.boxedVariables = | |
| 283 info.boxedVariables.map(localsMap.getLocalVariable).toSet(), | |
| 284 this.freeVariables = | 296 this.freeVariables = |
| 285 info.freeVariables.map(localsMap.getLocalVariable).toSet(); | 297 info.freeVariables.map(localsMap.getLocalVariable).toSet(); |
| 286 | 298 |
| 287 void forEachBoxedVariable(f(Local local, FieldEntity field)) { | 299 void forEachBoxedVariable(f(Local local, FieldEntity field)) { |
| 288 boxedVariables.forEach((Local l) { | 300 boxedVariables.forEach((Local l, JRecordField box) { |
| 289 // TODO(efortuna): add FieldEntities as created. | 301 f(l, box); |
| 290 f(l, null); | |
| 291 }); | 302 }); |
| 292 } | 303 } |
| 293 | 304 |
| 294 bool localIsUsedInTryOrSync(Local variable) => | 305 bool localIsUsedInTryOrSync(Local variable) => |
| 295 localsUsedInTryOrSync.contains(variable); | 306 localsUsedInTryOrSync.contains(variable); |
| 296 | 307 |
| 297 String toString() { | 308 String toString() { |
| 298 StringBuffer sb = new StringBuffer(); | 309 StringBuffer sb = new StringBuffer(); |
| 299 sb.write('this=$thisLocal,'); | 310 sb.write('this=$thisLocal,'); |
| 300 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}'); | 311 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}'); |
| 301 return sb.toString(); | 312 return sb.toString(); |
| 302 } | 313 } |
| 303 | 314 |
| 304 bool isBoxed(Local variable) => boxedVariables.contains(variable); | 315 bool isBoxed(Local variable) => boxedVariables.containsKey(variable); |
| 305 } | 316 } |
| 306 | 317 |
| 307 class KernelCapturedScope extends KernelScopeInfo { | 318 class KernelCapturedScope extends KernelScopeInfo { |
| 308 final ir.TreeNode context; | |
| 309 | |
| 310 KernelCapturedScope( | 319 KernelCapturedScope( |
| 311 Set<ir.VariableDeclaration> boxedVariables, | 320 Set<ir.VariableDeclaration> boxedVariables, |
| 312 NodeBox capturedVariablesAccessor, | 321 NodeBox capturedVariablesAccessor, |
| 313 this.context, | |
| 314 Set<ir.VariableDeclaration> localsUsedInTryOrSync, | 322 Set<ir.VariableDeclaration> localsUsedInTryOrSync, |
| 315 Set<ir.VariableDeclaration> freeVariables, | 323 Set<ir.VariableDeclaration> freeVariables, |
| 316 bool hasThisLocal) | 324 bool hasThisLocal) |
| 317 : super.withBoxedVariables(boxedVariables, capturedVariablesAccessor, | 325 : super.withBoxedVariables(boxedVariables, capturedVariablesAccessor, |
| 318 localsUsedInTryOrSync, freeVariables, hasThisLocal); | 326 localsUsedInTryOrSync, freeVariables, hasThisLocal); |
| 319 | 327 |
| 320 bool get requiresContextBox => boxedVariables.isNotEmpty; | 328 bool get requiresContextBox => boxedVariables.isNotEmpty; |
| 321 } | 329 } |
| 322 | 330 |
| 323 class JsCapturedScope extends JsScopeInfo implements CapturedScope { | 331 class JsCapturedScope extends JsScopeInfo implements CapturedScope { |
| 324 final Local context; | 332 final Local context; |
| 325 | 333 |
| 326 JsCapturedScope.from( | 334 JsCapturedScope.from(Map<Local, JRecordField> boxedVariables, |
| 327 KernelCapturedScope capturedScope, KernelToLocalsMap localsMap) | 335 KernelCapturedScope capturedScope, KernelToLocalsMap localsMap) |
| 328 : this.context = localsMap.getLocalVariable(capturedScope.context), | 336 : this.context = |
| 329 super.from(capturedScope, localsMap); | 337 boxedVariables.isNotEmpty ? boxedVariables.values.first.box : null, |
| 338 super.from(boxedVariables, capturedScope, localsMap); |
| 330 | 339 |
| 331 bool get requiresContextBox => boxedVariables.isNotEmpty; | 340 bool get requiresContextBox => boxedVariables.isNotEmpty; |
| 332 } | 341 } |
| 333 | 342 |
| 334 class KernelCapturedLoopScope extends KernelCapturedScope { | 343 class KernelCapturedLoopScope extends KernelCapturedScope { |
| 335 final List<ir.VariableDeclaration> boxedLoopVariables; | 344 final List<ir.VariableDeclaration> boxedLoopVariables; |
| 336 | 345 |
| 337 KernelCapturedLoopScope( | 346 KernelCapturedLoopScope( |
| 338 Set<ir.VariableDeclaration> boxedVariables, | 347 Set<ir.VariableDeclaration> boxedVariables, |
| 339 NodeBox capturedVariablesAccessor, | 348 NodeBox capturedVariablesAccessor, |
| 340 this.boxedLoopVariables, | 349 this.boxedLoopVariables, |
| 341 ir.TreeNode context, | |
| 342 Set<ir.VariableDeclaration> localsUsedInTryOrSync, | 350 Set<ir.VariableDeclaration> localsUsedInTryOrSync, |
| 343 Set<ir.VariableDeclaration> freeVariables, | 351 Set<ir.VariableDeclaration> freeVariables, |
| 344 bool hasThisLocal) | 352 bool hasThisLocal) |
| 345 : super(boxedVariables, capturedVariablesAccessor, context, | 353 : super(boxedVariables, capturedVariablesAccessor, localsUsedInTryOrSync, |
| 346 localsUsedInTryOrSync, freeVariables, hasThisLocal); | 354 freeVariables, hasThisLocal); |
| 347 | 355 |
| 348 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; | 356 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; |
| 349 } | 357 } |
| 350 | 358 |
| 351 class JsCapturedLoopScope extends JsCapturedScope implements CapturedLoopScope { | 359 class JsCapturedLoopScope extends JsCapturedScope implements CapturedLoopScope { |
| 352 final List<Local> boxedLoopVariables; | 360 final List<Local> boxedLoopVariables; |
| 353 | 361 |
| 354 JsCapturedLoopScope.from( | 362 JsCapturedLoopScope.from(Map<Local, JRecordField> boxedVariables, |
| 355 KernelCapturedLoopScope capturedScope, KernelToLocalsMap localsMap) | 363 KernelCapturedLoopScope capturedScope, KernelToLocalsMap localsMap) |
| 356 : this.boxedLoopVariables = capturedScope.boxedLoopVariables | 364 : this.boxedLoopVariables = capturedScope.boxedLoopVariables |
| 357 .map(localsMap.getLocalVariable) | 365 .map(localsMap.getLocalVariable) |
| 358 .toList(), | 366 .toList(), |
| 359 super.from(capturedScope, localsMap); | 367 super.from(boxedVariables, capturedScope, localsMap); |
| 360 | 368 |
| 361 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; | 369 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; |
| 362 } | 370 } |
| 363 | 371 |
| 364 // TODO(johnniwinther): Add unittest for the computed [ClosureClass]. | 372 // TODO(johnniwinther): Add unittest for the computed [ClosureClass]. |
| 365 class KernelClosureClass extends JsScopeInfo | 373 class KernelClosureClass extends JsScopeInfo |
| 366 implements ClosureRepresentationInfo { | 374 implements ClosureRepresentationInfo { |
| 367 JFunction callMethod; | 375 JFunction callMethod; |
| 368 final Local closureEntity; | 376 final Local closureEntity; |
| 369 final Local thisLocal; | 377 final Local thisLocal; |
| 370 final JClass closureClassEntity; | 378 final JClass closureClassEntity; |
| 371 | 379 |
| 372 final Map<Local, JField> localToFieldMap = new Map<Local, JField>(); | 380 final Map<Local, JField> localToFieldMap = new Map<Local, JField>(); |
| 373 | 381 |
| 374 KernelClosureClass.fromScopeInfo( | 382 KernelClosureClass.fromScopeInfo( |
| 375 this.closureClassEntity, | 383 this.closureClassEntity, |
| 376 ir.FunctionNode closureSourceNode, | 384 ir.FunctionNode closureSourceNode, |
| 385 Map<Local, JRecordField> boxedVariables, |
| 377 KernelScopeInfo info, | 386 KernelScopeInfo info, |
| 378 KernelToLocalsMap localsMap, | 387 KernelToLocalsMap localsMap, |
| 379 this.closureEntity, | 388 this.closureEntity, |
| 380 this.thisLocal) | 389 this.thisLocal) |
| 381 : super.from(info, localsMap); | 390 : super.from(boxedVariables, info, localsMap); |
| 382 | 391 |
| 383 List<Local> get createdFieldEntities => localToFieldMap.keys.toList(); | 392 List<Local> get createdFieldEntities => localToFieldMap.keys.toList(); |
| 384 | 393 |
| 385 FieldEntity get thisFieldEntity => localToFieldMap[thisLocal]; | 394 FieldEntity get thisFieldEntity => localToFieldMap[thisLocal]; |
| 386 | 395 |
| 387 void forEachCapturedVariable(f(Local from, JField to)) { | 396 void forEachCapturedVariable(f(Local from, JField to)) { |
| 388 localToFieldMap.forEach(f); | 397 for (Local l in localToFieldMap.keys) { |
| 398 var jField = localToFieldMap[l]; |
| 399 if (l is! BoxLocal) f(l, jField); |
| 400 } |
| 389 } | 401 } |
| 390 | 402 |
| 391 @override | 403 @override |
| 392 void forEachBoxedVariable(f(Local local, JField field)) { | 404 void forEachBoxedVariable(f(Local local, JField field)) { |
| 393 for (Local l in localToFieldMap.keys) { | 405 boxedVariables.forEach(f); |
| 394 if (localToFieldMap[l] is JRecordField) f(l, localToFieldMap[l]); | |
| 395 } | |
| 396 } | 406 } |
| 397 | 407 |
| 398 void forEachFreeVariable(f(Local variable, JField field)) { | 408 void forEachFreeVariable(f(Local variable, JField field)) { |
| 399 for (Local l in localToFieldMap.keys) { | 409 localToFieldMap.forEach(f); |
| 400 var jField = localToFieldMap[l]; | 410 boxedVariables.forEach(f); |
| 401 if (jField is! JRecordField && jField is! BoxLocal) f(l, jField); | |
| 402 } | |
| 403 } | 411 } |
| 404 | 412 |
| 405 bool isVariableBoxed(Local variable) => | 413 bool isVariableBoxed(Local variable) => |
| 406 localToFieldMap.keys.contains(variable); | 414 localToFieldMap.keys.contains(variable); |
| 407 | 415 |
| 408 bool get isClosure => true; | 416 bool get isClosure => true; |
| 409 } | 417 } |
| 410 | 418 |
| 411 /// A local variable to disambiguate between a variable that has been captured | 419 /// A local variable to disambiguate between a variable that has been captured |
| 412 /// from one scope to another. This is the ir.Node version that corresponds to | 420 /// from one scope to another. This is the ir.Node version that corresponds to |
| (...skipping 198 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 611 KernelScopeInfo scopeInfo; | 619 KernelScopeInfo scopeInfo; |
| 612 | 620 |
| 613 /// Collected [CapturedScope] data for nodes. | 621 /// Collected [CapturedScope] data for nodes. |
| 614 Map<ir.Node, KernelCapturedScope> capturedScopesMap = | 622 Map<ir.Node, KernelCapturedScope> capturedScopesMap = |
| 615 <ir.Node, KernelCapturedScope>{}; | 623 <ir.Node, KernelCapturedScope>{}; |
| 616 | 624 |
| 617 /// Collected [ScopeInfo] data for nodes. | 625 /// Collected [ScopeInfo] data for nodes. |
| 618 Map<ir.TreeNode, KernelScopeInfo> closuresToGenerate = | 626 Map<ir.TreeNode, KernelScopeInfo> closuresToGenerate = |
| 619 <ir.TreeNode, KernelScopeInfo>{}; | 627 <ir.TreeNode, KernelScopeInfo>{}; |
| 620 } | 628 } |
| OLD | NEW |