| 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 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 256 StringBuffer sb = new StringBuffer(); | 272 StringBuffer sb = new StringBuffer(); |
| 257 sb.write('this=$hasThisLocal,'); | 273 sb.write('this=$hasThisLocal,'); |
| 258 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}'); | 274 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}'); |
| 259 return sb.toString(); | 275 return sb.toString(); |
| 260 } | 276 } |
| 261 } | 277 } |
| 262 | 278 |
| 263 class JsScopeInfo extends ScopeInfo { | 279 class JsScopeInfo extends ScopeInfo { |
| 264 final Set<Local> localsUsedInTryOrSync; | 280 final Set<Local> localsUsedInTryOrSync; |
| 265 final Local thisLocal; | 281 final Local thisLocal; |
| 266 final Set<Local> boxedVariables; | 282 final Map<Local, JRecordField> boxedVariables; |
| 267 | 283 |
| 268 /// The set of variables that were defined in another scope, but are used in | 284 /// The set of variables that were defined in another scope, but are used in |
| 269 /// this scope. | 285 /// this scope. |
| 270 final Set<Local> freeVariables; | 286 final Set<Local> freeVariables; |
| 271 | 287 |
| 272 JsScopeInfo(this.thisLocal, this.localsUsedInTryOrSync, this.boxedVariables, | 288 JsScopeInfo.from( |
| 273 this.freeVariables); | 289 this.boxedVariables, KernelScopeInfo info, KernelToLocalsMap localsMap) |
| 274 | |
| 275 JsScopeInfo.from(KernelScopeInfo info, KernelToLocalsMap localsMap) | |
| 276 : this.thisLocal = | 290 : this.thisLocal = |
| 277 info.hasThisLocal ? new ThisLocal(localsMap.currentMember) : null, | 291 info.hasThisLocal ? new ThisLocal(localsMap.currentMember) : null, |
| 278 this.localsUsedInTryOrSync = | 292 this.localsUsedInTryOrSync = |
| 279 info.localsUsedInTryOrSync.map(localsMap.getLocalVariable).toSet(), | 293 info.localsUsedInTryOrSync.map(localsMap.getLocalVariable).toSet(), |
| 280 this.boxedVariables = | |
| 281 info.boxedVariables.map(localsMap.getLocalVariable).toSet(), | |
| 282 this.freeVariables = | 294 this.freeVariables = |
| 283 info.freeVariables.map(localsMap.getLocalVariable).toSet(); | 295 info.freeVariables.map(localsMap.getLocalVariable).toSet(); |
| 284 | 296 |
| 285 void forEachBoxedVariable(f(Local local, FieldEntity field)) { | 297 void forEachBoxedVariable(f(Local local, FieldEntity field)) { |
| 286 boxedVariables.forEach((Local l) { | 298 boxedVariables.forEach((Local l, JRecordField box) { |
| 287 // TODO(efortuna): add FieldEntities as created. | 299 f(l, box); |
| 288 f(l, null); | |
| 289 }); | 300 }); |
| 290 } | 301 } |
| 291 | 302 |
| 292 bool localIsUsedInTryOrSync(Local variable) => | 303 bool localIsUsedInTryOrSync(Local variable) => |
| 293 localsUsedInTryOrSync.contains(variable); | 304 localsUsedInTryOrSync.contains(variable); |
| 294 | 305 |
| 295 String toString() { | 306 String toString() { |
| 296 StringBuffer sb = new StringBuffer(); | 307 StringBuffer sb = new StringBuffer(); |
| 297 sb.write('this=$thisLocal,'); | 308 sb.write('this=$thisLocal,'); |
| 298 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}'); | 309 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}'); |
| 299 return sb.toString(); | 310 return sb.toString(); |
| 300 } | 311 } |
| 301 | 312 |
| 302 bool isBoxed(Local variable) => boxedVariables.contains(variable); | 313 bool isBoxed(Local variable) => boxedVariables.containsKey(variable); |
| 303 } | 314 } |
| 304 | 315 |
| 305 class KernelCapturedScope extends KernelScopeInfo { | 316 class KernelCapturedScope extends KernelScopeInfo { |
| 306 final ir.TreeNode context; | |
| 307 | |
| 308 KernelCapturedScope( | 317 KernelCapturedScope( |
| 309 Set<ir.VariableDeclaration> boxedVariables, | 318 Set<ir.VariableDeclaration> boxedVariables, |
| 310 NodeBox capturedVariablesAccessor, | 319 NodeBox capturedVariablesAccessor, |
| 311 this.context, | |
| 312 Set<ir.VariableDeclaration> localsUsedInTryOrSync, | 320 Set<ir.VariableDeclaration> localsUsedInTryOrSync, |
| 313 Set<ir.VariableDeclaration> freeVariables, | 321 Set<ir.VariableDeclaration> freeVariables, |
| 314 bool hasThisLocal) | 322 bool hasThisLocal) |
| 315 : super.withBoxedVariables(boxedVariables, capturedVariablesAccessor, | 323 : super.withBoxedVariables(boxedVariables, capturedVariablesAccessor, |
| 316 localsUsedInTryOrSync, freeVariables, hasThisLocal); | 324 localsUsedInTryOrSync, freeVariables, hasThisLocal); |
| 317 | 325 |
| 318 bool get requiresContextBox => boxedVariables.isNotEmpty; | 326 bool get requiresContextBox => boxedVariables.isNotEmpty; |
| 319 } | 327 } |
| 320 | 328 |
| 321 class JsCapturedScope extends JsScopeInfo implements CapturedScope { | 329 class JsCapturedScope extends JsScopeInfo implements CapturedScope { |
| 322 final Local context; | 330 final Local context; |
| 323 | 331 |
| 324 JsCapturedScope.from( | 332 JsCapturedScope.from(Map<Local, JRecordField> boxedVariables, |
| 325 KernelCapturedScope capturedScope, KernelToLocalsMap localsMap) | 333 KernelCapturedScope capturedScope, KernelToLocalsMap localsMap) |
| 326 : this.context = localsMap.getLocalVariable(capturedScope.context), | 334 : this.context = |
| 327 super.from(capturedScope, localsMap); | 335 boxedVariables.isNotEmpty ? boxedVariables.values.first.box : null, |
| 336 super.from(boxedVariables, capturedScope, localsMap); |
| 328 | 337 |
| 329 bool get requiresContextBox => boxedVariables.isNotEmpty; | 338 bool get requiresContextBox => boxedVariables.isNotEmpty; |
| 330 } | 339 } |
| 331 | 340 |
| 332 class KernelCapturedLoopScope extends KernelCapturedScope { | 341 class KernelCapturedLoopScope extends KernelCapturedScope { |
| 333 final List<ir.VariableDeclaration> boxedLoopVariables; | 342 final List<ir.VariableDeclaration> boxedLoopVariables; |
| 334 | 343 |
| 335 KernelCapturedLoopScope( | 344 KernelCapturedLoopScope( |
| 336 Set<ir.VariableDeclaration> boxedVariables, | 345 Set<ir.VariableDeclaration> boxedVariables, |
| 337 NodeBox capturedVariablesAccessor, | 346 NodeBox capturedVariablesAccessor, |
| 338 this.boxedLoopVariables, | 347 this.boxedLoopVariables, |
| 339 ir.TreeNode context, | |
| 340 Set<ir.VariableDeclaration> localsUsedInTryOrSync, | 348 Set<ir.VariableDeclaration> localsUsedInTryOrSync, |
| 341 Set<ir.VariableDeclaration> freeVariables, | 349 Set<ir.VariableDeclaration> freeVariables, |
| 342 bool hasThisLocal) | 350 bool hasThisLocal) |
| 343 : super(boxedVariables, capturedVariablesAccessor, context, | 351 : super(boxedVariables, capturedVariablesAccessor, localsUsedInTryOrSync, |
| 344 localsUsedInTryOrSync, freeVariables, hasThisLocal); | 352 freeVariables, hasThisLocal); |
| 345 | 353 |
| 346 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; | 354 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; |
| 347 } | 355 } |
| 348 | 356 |
| 349 class JsCapturedLoopScope extends JsCapturedScope implements CapturedLoopScope { | 357 class JsCapturedLoopScope extends JsCapturedScope implements CapturedLoopScope { |
| 350 final List<Local> boxedLoopVariables; | 358 final List<Local> boxedLoopVariables; |
| 351 | 359 |
| 352 JsCapturedLoopScope.from( | 360 JsCapturedLoopScope.from(Map<Local, JRecordField> boxedVariables, |
| 353 KernelCapturedLoopScope capturedScope, KernelToLocalsMap localsMap) | 361 KernelCapturedLoopScope capturedScope, KernelToLocalsMap localsMap) |
| 354 : this.boxedLoopVariables = capturedScope.boxedLoopVariables | 362 : this.boxedLoopVariables = capturedScope.boxedLoopVariables |
| 355 .map(localsMap.getLocalVariable) | 363 .map(localsMap.getLocalVariable) |
| 356 .toList(), | 364 .toList(), |
| 357 super.from(capturedScope, localsMap); | 365 super.from(boxedVariables, capturedScope, localsMap); |
| 358 | 366 |
| 359 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; | 367 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; |
| 360 } | 368 } |
| 361 | 369 |
| 362 // TODO(johnniwinther): Add unittest for the computed [ClosureClass]. | 370 // TODO(johnniwinther): Add unittest for the computed [ClosureClass]. |
| 363 class KernelClosureClass extends JsScopeInfo | 371 class KernelClosureClass extends JsScopeInfo |
| 364 implements ClosureRepresentationInfo { | 372 implements ClosureRepresentationInfo { |
| 365 JFunction callMethod; | 373 JFunction callMethod; |
| 366 final Local closureEntity; | 374 final Local closureEntity; |
| 367 final Local thisLocal; | 375 final Local thisLocal; |
| 368 final JClass closureClassEntity; | 376 final JClass closureClassEntity; |
| 369 | 377 |
| 370 final Map<Local, JField> localToFieldMap = new Map<Local, JField>(); | 378 final Map<Local, JField> localToFieldMap = new Map<Local, JField>(); |
| 371 | 379 |
| 372 KernelClosureClass.fromScopeInfo( | 380 KernelClosureClass.fromScopeInfo( |
| 373 this.closureClassEntity, | 381 this.closureClassEntity, |
| 374 ir.FunctionNode closureSourceNode, | 382 ir.FunctionNode closureSourceNode, |
| 383 Map<Local, JRecordField> boxedVariables, |
| 375 KernelScopeInfo info, | 384 KernelScopeInfo info, |
| 376 KernelToLocalsMap localsMap, | 385 KernelToLocalsMap localsMap, |
| 377 this.closureEntity, | 386 this.closureEntity, |
| 378 this.thisLocal) | 387 this.thisLocal) |
| 379 : super.from(info, localsMap); | 388 : super.from(boxedVariables, info, localsMap); |
| 380 | 389 |
| 381 List<Local> get createdFieldEntities => localToFieldMap.keys.toList(); | 390 List<Local> get createdFieldEntities => localToFieldMap.keys.toList(); |
| 382 | 391 |
| 383 FieldEntity get thisFieldEntity => localToFieldMap[thisLocal]; | 392 FieldEntity get thisFieldEntity => localToFieldMap[thisLocal]; |
| 384 | 393 |
| 385 void forEachCapturedVariable(f(Local from, JField to)) { | 394 void forEachCapturedVariable(f(Local from, JField to)) { |
| 386 localToFieldMap.forEach(f); | 395 for (Local l in localToFieldMap.keys) { |
| 396 var jField = localToFieldMap[l]; |
| 397 if (l is! BoxLocal) f(l, jField); |
| 398 } |
| 387 } | 399 } |
| 388 | 400 |
| 389 @override | 401 @override |
| 390 void forEachBoxedVariable(f(Local local, JField field)) { | 402 void forEachBoxedVariable(f(Local local, JField field)) { |
| 391 for (Local l in localToFieldMap.keys) { | 403 boxedVariables.forEach(f); |
| 392 if (localToFieldMap[l] is JRecordField) f(l, localToFieldMap[l]); | |
| 393 } | |
| 394 } | 404 } |
| 395 | 405 |
| 396 void forEachFreeVariable(f(Local variable, JField field)) { | 406 void forEachFreeVariable(f(Local variable, JField field)) { |
| 397 for (Local l in localToFieldMap.keys) { | 407 localToFieldMap.forEach(f); |
| 398 var jField = localToFieldMap[l]; | 408 boxedVariables.forEach(f); |
| 399 if (jField is! JRecordField && jField is! BoxLocal) f(l, jField); | |
| 400 } | |
| 401 } | 409 } |
| 402 | 410 |
| 403 bool isVariableBoxed(Local variable) => | 411 bool isVariableBoxed(Local variable) => |
| 404 localToFieldMap.keys.contains(variable); | 412 localToFieldMap.keys.contains(variable); |
| 405 | 413 |
| 406 bool get isClosure => true; | 414 bool get isClosure => true; |
| 407 } | 415 } |
| 408 | 416 |
| 409 /// A local variable to disambiguate between a variable that has been captured | 417 /// A local variable to disambiguate between a variable that has been captured |
| 410 /// from one scope to another. This is the ir.Node version that corresponds to | 418 /// from one scope to another. This is the ir.Node version that corresponds to |
| (...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 591 KernelScopeInfo scopeInfo; | 599 KernelScopeInfo scopeInfo; |
| 592 | 600 |
| 593 /// Collected [CapturedScope] data for nodes. | 601 /// Collected [CapturedScope] data for nodes. |
| 594 Map<ir.Node, KernelCapturedScope> capturedScopesMap = | 602 Map<ir.Node, KernelCapturedScope> capturedScopesMap = |
| 595 <ir.Node, KernelCapturedScope>{}; | 603 <ir.Node, KernelCapturedScope>{}; |
| 596 | 604 |
| 597 /// Collected [ScopeInfo] data for nodes. | 605 /// Collected [ScopeInfo] data for nodes. |
| 598 Map<ir.TreeNode, KernelScopeInfo> closuresToGenerate = | 606 Map<ir.TreeNode, KernelScopeInfo> closuresToGenerate = |
| 599 <ir.TreeNode, KernelScopeInfo>{}; | 607 <ir.TreeNode, KernelScopeInfo>{}; |
| 600 } | 608 } |
| OLD | NEW |