| 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 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 89 @override | 89 @override |
| 90 void convertClosures(Iterable<MemberEntity> processedEntities, | 90 void convertClosures(Iterable<MemberEntity> processedEntities, |
| 91 ClosedWorldRefiner closedWorldRefiner) { | 91 ClosedWorldRefiner closedWorldRefiner) { |
| 92 _createClosureEntities(_closureModels, closedWorldRefiner); | 92 _createClosureEntities(_closureModels, closedWorldRefiner); |
| 93 } | 93 } |
| 94 | 94 |
| 95 void _createClosureEntities(Map<MemberEntity, ScopeModel> closureModels, | 95 void _createClosureEntities(Map<MemberEntity, ScopeModel> closureModels, |
| 96 JsClosedWorld closedWorldRefiner) { | 96 JsClosedWorld closedWorldRefiner) { |
| 97 closureModels.forEach((MemberEntity member, ScopeModel model) { | 97 closureModels.forEach((MemberEntity member, ScopeModel model) { |
| 98 KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member); | 98 KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member); |
| 99 if (model.scopeInfo != null) { | 99 assert(model.scopeInfo != null); |
| 100 _scopeMap[member] = new JsScopeInfo.from(model.scopeInfo, localsMap); | 100 var boxedVariables = |
| 101 } | 101 _elementMap.makeRecordContainer(model.scopeInfo, member, localsMap); |
| 102 _scopeMap[member] = |
| 103 new JsScopeInfo.from(boxedVariables, model.scopeInfo, localsMap); |
| 104 print('created the following scope info $member ${_scopeMap[member]}'); |
| 102 | 105 |
| 106 Set<JsCapturedScope> capturedScopes = new Set<JsCaptuerdScope>(); |
| 103 model.capturedScopesMap | 107 model.capturedScopesMap |
| 104 .forEach((ir.Node node, KernelCapturedScope scope) { | 108 .forEach((ir.Node node, KernelCapturedScope scope) { |
| 109 boxedVariables = _elementMap.makeRecordContainer( |
| 110 model.scopeInfo, |
| 111 _elementMap.getMember(node), |
| 112 localsMap); // TODO: (maybe don't need to call this again.) |
| 105 if (scope is KernelCapturedLoopScope) { | 113 if (scope is KernelCapturedLoopScope) { |
| 106 _capturedScopesMap[node] = | 114 _capturedScopesMap[node] = |
| 107 new JsCapturedLoopScope.from(scope, localsMap); | 115 new JsCapturedLoopScope.from(boxedVariables, scope, localsMap); |
| 108 } else { | 116 } else { |
| 109 _capturedScopesMap[node] = new JsCapturedScope.from(scope, localsMap); | 117 _capturedScopesMap[node] = |
| 118 new JsCapturedScope.from(boxedVariables, scope, localsMap); |
| 110 } | 119 } |
| 120 capturedScopes.add(_capturedScopesMap[node]); |
| 121 print('captured scope info $node ${_capturedScopesMap[node]}'); |
| 111 }); | 122 }); |
| 112 | 123 |
| 113 Map<ir.FunctionNode, KernelScopeInfo> closuresToGenerate = | 124 Map<ir.FunctionNode, KernelScopeInfo> closuresToGenerate = |
| 114 model.closuresToGenerate; | 125 model.closuresToGenerate; |
| 115 for (ir.FunctionNode node in closuresToGenerate.keys) { | 126 for (ir.FunctionNode node in closuresToGenerate.keys) { |
| 116 _produceSyntheticElements( | 127 KernelClosureClass closureClass = _produceSyntheticElements(member, |
| 117 member, node, closuresToGenerate[node], closedWorldRefiner); | 128 node, closuresToGenerate[node], capturedScopes, closedWorldRefiner); |
| 129 // Add one for each call method. |
| 130 KernelToLocalsMap localsMap = |
| 131 _globalLocalsMap.getLocalsMap(closureClass.callMethod); |
| 132 _scopeMap[closureClass.callMethod] = closureClass; |
| 133 print('then we made $closureClass starting from ${node.parent}'); |
| 118 } | 134 } |
| 119 }); | 135 }); |
| 120 } | 136 } |
| 121 | 137 |
| 122 /// Given what variables are captured at each point, construct closure classes | 138 /// Given what variables are captured at each point, construct closure classes |
| 123 /// with fields containing the captured variables to replicate the Dart | 139 /// with fields containing the captured variables to replicate the Dart |
| 124 /// closure semantics in JS. If this closure captures any variables (meaning | 140 /// closure semantics in JS. If this closure captures any variables (meaning |
| 125 /// the closure accesses a variable that gets accessed at some point), then | 141 /// the closure accesses a variable that gets accessed at some point), then |
| 126 /// boxForCapturedVariables stores the local context for those variables. | 142 /// boxForCapturedVariables stores the local context for those variables. |
| 127 /// If no variables are captured, this parameter is null. | 143 /// If no variables are captured, this parameter is null. |
| 128 void _produceSyntheticElements(MemberEntity member, ir.FunctionNode node, | 144 KernelClosureClass _produceSyntheticElements( |
| 129 KernelScopeInfo info, JsClosedWorld closedWorldRefiner) { | 145 MemberEntity member, |
| 146 ir.FunctionNode node, |
| 147 KernelScopeInfo info, |
| 148 Set<JsCaptuerdScope> capturedScopes, |
| 149 JsClosedWorld closedWorldRefiner) { |
| 130 KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member); | 150 KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member); |
| 151 |
| 131 KernelClosureClass closureClass = closedWorldRefiner.buildClosureClass( | 152 KernelClosureClass closureClass = closedWorldRefiner.buildClosureClass( |
| 132 member, node, member.library, info, node.location, localsMap); | 153 member, |
| 154 node, |
| 155 member.library, |
| 156 capturedScopes, |
| 157 info, |
| 158 node.location, |
| 159 localsMap); |
| 133 | 160 |
| 134 // We want the original declaration where that function is used to point | 161 // We want the original declaration where that function is used to point |
| 135 // to the correct closure class. | 162 // to the correct closure class. |
| 136 _closureRepresentationMap[closureClass.callMethod] = closureClass; | 163 _closureRepresentationMap[closureClass.callMethod] = closureClass; |
| 137 Entity entity; | 164 Entity entity; |
| 138 if (node.parent is ir.Member) { | 165 if (node.parent is ir.Member) { |
| 139 entity = _elementMap.getMember(node.parent); | 166 entity = _elementMap.getMember(node.parent); |
| 140 } else { | 167 } else { |
| 141 entity = localsMap.getLocalFunction(node.parent); | 168 entity = localsMap.getLocalFunction(node.parent); |
| 142 } | 169 } |
| 143 assert(entity != null); | 170 assert(entity != null); |
| 144 _closureRepresentationMap[entity] = closureClass; | 171 _closureRepresentationMap[entity] = closureClass; |
| 172 return closureClass; |
| 145 } | 173 } |
| 146 | 174 |
| 147 @override | 175 @override |
| 148 ScopeInfo getScopeInfo(Entity entity) { | 176 ScopeInfo getScopeInfo(Entity entity) { |
| 149 // TODO(johnniwinther): Remove this check when constructor bodies a created | 177 // TODO(johnniwinther): Remove this check when constructor bodies a created |
| 150 // eagerly with the J-model; a constructor body should have it's own | 178 // eagerly with the J-model; a constructor body should have it's own |
| 151 // [ClosureRepresentationInfo]. | 179 // [ClosureRepresentationInfo]. |
| 152 if (entity is ConstructorBodyEntity) { | 180 if (entity is ConstructorBodyEntity) { |
| 153 ConstructorBodyEntity constructorBody = entity; | 181 ConstructorBodyEntity constructorBody = entity; |
| 154 entity = constructorBody.constructor; | 182 entity = constructorBody.constructor; |
| 155 } | 183 } |
| 156 | 184 |
| 157 return _scopeMap[entity] ?? getClosureRepresentationInfo(entity); | 185 return _scopeMap[entity] ?? getClosureRepresentationInfo(entity); |
| 158 } | 186 } |
| 159 | 187 |
| 160 // TODO(efortuna): Eventually capturedScopesMap[node] should always | |
| 161 // be non-null, and we should just test that with an assert. | |
| 162 @override | 188 @override |
| 163 CapturedScope getCapturedScope(MemberEntity entity) { | 189 CapturedScope getCapturedScope(MemberEntity entity) { |
| 164 MemberDefinition definition = _elementMap.getMemberDefinition(entity); | 190 MemberDefinition definition = _elementMap.getMemberDefinition(entity); |
| 165 switch (definition.kind) { | 191 switch (definition.kind) { |
| 166 case MemberKind.regular: | 192 case MemberKind.regular: |
| 167 case MemberKind.constructor: | 193 case MemberKind.constructor: |
| 168 case MemberKind.constructorBody: | 194 case MemberKind.constructorBody: |
| 169 case MemberKind.closureCall: | 195 case MemberKind.closureCall: |
| 170 return _capturedScopesMap[definition.node] ?? const CapturedScope(); | 196 return _capturedScopesMap[definition.node] ?? const CapturedScope(); |
| 171 default: | 197 default: |
| 172 throw failedAt(entity, "Unexpected member definition $definition"); | 198 throw failedAt(entity, "Unexpected member definition $definition"); |
| 173 } | 199 } |
| 174 } | 200 } |
| 175 | 201 |
| 176 @override | 202 @override |
| 177 // TODO(efortuna): Eventually capturedScopesMap[node] should always | |
| 178 // be non-null, and we should just test that with an assert. | |
| 179 CapturedLoopScope getCapturedLoopScope(ir.Node loopNode) => | 203 CapturedLoopScope getCapturedLoopScope(ir.Node loopNode) => |
| 180 _capturedScopesMap[loopNode] ?? const CapturedLoopScope(); | 204 _capturedScopesMap[loopNode] ?? const CapturedLoopScope(); |
| 181 | 205 |
| 182 @override | 206 @override |
| 183 ClosureRepresentationInfo getClosureRepresentationInfo(Entity entity) { | 207 ClosureRepresentationInfo getClosureRepresentationInfo(Entity entity) { |
| 184 var closure = _closureRepresentationMap[entity]; | 208 var closure = _closureRepresentationMap[entity]; |
| 185 assert( | 209 assert( |
| 186 closure != null, | 210 closure != null, |
| 187 "Corresponding closure class not found for $entity. " | 211 "Corresponding closure class not found for $entity. " |
| 188 "Closures found for ${_closureRepresentationMap.keys}"); | 212 "Closures found for ${_closureRepresentationMap.keys}"); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 201 final bool hasThisLocal; | 225 final bool hasThisLocal; |
| 202 final Set<ir.VariableDeclaration> boxedVariables; | 226 final Set<ir.VariableDeclaration> boxedVariables; |
| 203 // If boxedVariables is empty, this will be null, because no variables will | 227 // If boxedVariables is empty, this will be null, because no variables will |
| 204 // need to be boxed. | 228 // need to be boxed. |
| 205 final NodeBox capturedVariablesAccessor; | 229 final NodeBox capturedVariablesAccessor; |
| 206 | 230 |
| 207 /// The set of variables that were defined in another scope, but are used in | 231 /// The set of variables that were defined in another scope, but are used in |
| 208 /// this scope. | 232 /// this scope. |
| 209 Set<ir.VariableDeclaration> freeVariables = new Set<ir.VariableDeclaration>(); | 233 Set<ir.VariableDeclaration> freeVariables = new Set<ir.VariableDeclaration>(); |
| 210 | 234 |
| 235 /// The set of scopes that this scope captures. In practice we only populate |
| 236 /// it if this scope uses a particular variable that is defined in another |
| 237 /// scope, and this information is only really useful for closures. |
| 238 final Set<ir.Node> capturedScopes = new Set<ir.Node>(); |
| 239 |
| 211 KernelScopeInfo(this.hasThisLocal) | 240 KernelScopeInfo(this.hasThisLocal) |
| 212 : localsUsedInTryOrSync = new Set<ir.VariableDeclaration>(), | 241 : localsUsedInTryOrSync = new Set<ir.VariableDeclaration>(), |
| 213 boxedVariables = new Set<ir.VariableDeclaration>(), | 242 boxedVariables = new Set<ir.VariableDeclaration>(), |
| 214 capturedVariablesAccessor = null; | 243 capturedVariablesAccessor = null; |
| 215 | 244 |
| 216 KernelScopeInfo.from(this.hasThisLocal, KernelScopeInfo info) | 245 KernelScopeInfo.from(this.hasThisLocal, KernelScopeInfo info) |
| 217 : localsUsedInTryOrSync = info.localsUsedInTryOrSync, | 246 : localsUsedInTryOrSync = info.localsUsedInTryOrSync, |
| 218 boxedVariables = info.boxedVariables, | 247 boxedVariables = info.boxedVariables, |
| 219 capturedVariablesAccessor = null; | 248 capturedVariablesAccessor = null; |
| 220 | 249 |
| 221 KernelScopeInfo.withBoxedVariables( | 250 KernelScopeInfo.withBoxedVariables( |
| 222 this.boxedVariables, | 251 this.boxedVariables, |
| 223 this.capturedVariablesAccessor, | 252 this.capturedVariablesAccessor, |
| 224 this.localsUsedInTryOrSync, | 253 this.localsUsedInTryOrSync, |
| 225 this.freeVariables, | 254 this.freeVariables, |
| 226 this.hasThisLocal); | 255 this.hasThisLocal); |
| 227 | 256 |
| 228 String toString() { | 257 String toString() { |
| 229 StringBuffer sb = new StringBuffer(); | 258 StringBuffer sb = new StringBuffer(); |
| 230 sb.write('this=$hasThisLocal,'); | 259 sb.write('this=$hasThisLocal,'); |
| 231 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}'); | 260 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}},'); |
| 261 sb.write('freeVariables={${freeVariables.join(', ')}},'); |
| 262 sb.write('boxedVariables={${boxedVariables.join(', ')}},'); |
| 263 sb.write('capturedVariablesAccessor=$capturedVariablesAccessor'); |
| 232 return sb.toString(); | 264 return sb.toString(); |
| 233 } | 265 } |
| 234 } | 266 } |
| 235 | 267 |
| 236 class JsScopeInfo extends ScopeInfo { | 268 class JsScopeInfo extends ScopeInfo { |
| 237 final Set<Local> localsUsedInTryOrSync; | 269 final Set<Local> localsUsedInTryOrSync; |
| 238 final Local thisLocal; | 270 final Local thisLocal; |
| 239 final Set<Local> boxedVariables; | 271 final Map<Local, JRecord> boxedVariables; |
| 240 | 272 |
| 241 /// The set of variables that were defined in another scope, but are used in | 273 /// The set of variables that were defined in another scope, but are used in |
| 242 /// this scope. | 274 /// this scope. |
| 243 final Set<Local> freeVariables; | 275 final Set<Local> freeVariables; |
| 244 | 276 |
| 245 JsScopeInfo(this.thisLocal, this.localsUsedInTryOrSync, this.boxedVariables, | 277 JsScopeInfo.from( |
| 246 this.freeVariables); | 278 this.boxedVariables, KernelScopeInfo info, KernelToLocalsMap localsMap) |
| 247 | |
| 248 JsScopeInfo.from(KernelScopeInfo info, KernelToLocalsMap localsMap) | |
| 249 : this.thisLocal = | 279 : this.thisLocal = |
| 250 info.hasThisLocal ? new ThisLocal(localsMap.currentMember) : null, | 280 info.hasThisLocal ? new ThisLocal(localsMap.currentMember) : null, |
| 251 this.localsUsedInTryOrSync = | 281 this.localsUsedInTryOrSync = |
| 252 info.localsUsedInTryOrSync.map(localsMap.getLocalVariable).toSet(), | 282 info.localsUsedInTryOrSync.map(localsMap.getLocalVariable).toSet(), |
| 253 this.boxedVariables = | |
| 254 info.boxedVariables.map(localsMap.getLocalVariable).toSet(), | |
| 255 this.freeVariables = | 283 this.freeVariables = |
| 256 info.freeVariables.map(localsMap.getLocalVariable).toSet(); | 284 info.freeVariables.map(localsMap.getLocalVariable).toSet(); |
| 257 | 285 |
| 258 void forEachBoxedVariable(f(Local local, FieldEntity field)) { | 286 void forEachBoxedVariable(f(Local local, FieldEntity field)) { |
| 259 boxedVariables.forEach((Local l) { | 287 boxedVariables.forEach((Local l, JRecord box) { |
| 260 // TODO(efortuna): add FieldEntities as created. | 288 f(l, box); |
| 261 f(l, null); | |
| 262 }); | 289 }); |
| 263 } | 290 } |
| 264 | 291 |
| 265 bool localIsUsedInTryOrSync(Local variable) => | 292 bool localIsUsedInTryOrSync(Local variable) => |
| 266 localsUsedInTryOrSync.contains(variable); | 293 localsUsedInTryOrSync.contains(variable); |
| 267 | 294 |
| 268 String toString() { | 295 String toString() { |
| 269 StringBuffer sb = new StringBuffer(); | 296 StringBuffer sb = new StringBuffer(); |
| 297 sb.write('JsScopeInfo: '); |
| 270 sb.write('this=$thisLocal,'); | 298 sb.write('this=$thisLocal,'); |
| 271 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}'); | 299 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}'); |
| 300 sb.write('freeVariables={${freeVariables.join(', ')}}'); |
| 301 sb.write('boxedVariables={$boxedVariables}'); |
| 272 return sb.toString(); | 302 return sb.toString(); |
| 273 } | 303 } |
| 274 | 304 |
| 275 bool isBoxed(Local variable) => boxedVariables.contains(variable); | 305 bool isBoxed(Local variable) => boxedVariables.containsKey(variable); |
| 276 } | 306 } |
| 277 | 307 |
| 278 class KernelCapturedScope extends KernelScopeInfo { | 308 class KernelCapturedScope extends KernelScopeInfo { |
| 279 final ir.TreeNode context; | |
| 280 | |
| 281 KernelCapturedScope( | 309 KernelCapturedScope( |
| 282 Set<ir.VariableDeclaration> boxedVariables, | 310 Set<ir.VariableDeclaration> boxedVariables, |
| 283 NodeBox capturedVariablesAccessor, | 311 NodeBox capturedVariablesAccessor, |
| 284 this.context, | |
| 285 Set<ir.VariableDeclaration> localsUsedInTryOrSync, | 312 Set<ir.VariableDeclaration> localsUsedInTryOrSync, |
| 286 Set<ir.VariableDeclaration> freeVariables, | 313 Set<ir.VariableDeclaration> freeVariables, |
| 287 bool hasThisLocal) | 314 bool hasThisLocal) |
| 288 : super.withBoxedVariables(boxedVariables, capturedVariablesAccessor, | 315 : super.withBoxedVariables(boxedVariables, capturedVariablesAccessor, |
| 289 localsUsedInTryOrSync, freeVariables, hasThisLocal); | 316 localsUsedInTryOrSync, freeVariables, hasThisLocal); |
| 290 | 317 |
| 291 bool get requiresContextBox => boxedVariables.isNotEmpty; | 318 bool get requiresContextBox => boxedVariables.isNotEmpty; |
| 292 } | 319 } |
| 293 | 320 |
| 294 class JsCapturedScope extends JsScopeInfo implements CapturedScope { | 321 class JsCapturedScope extends JsScopeInfo implements CapturedScope { |
| 295 final Local context; | 322 final BoxLocal context; |
| 296 | 323 |
| 297 JsCapturedScope.from( | 324 JsCapturedScope.from(Map<Local, JRecord> boxedVariables, |
| 298 KernelCapturedScope capturedScope, KernelToLocalsMap localsMap) | 325 KernelCapturedScope capturedScope, KernelToLocalsMap localsMap) |
| 299 : this.context = localsMap.getLocalVariable(capturedScope.context), | 326 : this.context = |
| 300 super.from(capturedScope, localsMap); | 327 boxedVariables.isNotEmpty ? boxedVariables.values.first.box : null, |
| 328 super.from(boxedVariables, capturedScope, localsMap); |
| 301 | 329 |
| 302 bool get requiresContextBox => boxedVariables.isNotEmpty; | 330 bool get requiresContextBox => boxedVariables.isNotEmpty; |
| 331 |
| 332 String toString() { |
| 333 StringBuffer sb = new StringBuffer(); |
| 334 sb.write('this=$thisLocal,'); |
| 335 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}'); |
| 336 sb.write('freeVariables={${freeVariables.join(', ')}}'); |
| 337 sb.write('boxedVariables={$boxedVariables}'); |
| 338 sb.write('context={$context}'); |
| 339 return sb.toString(); |
| 340 } |
| 303 } | 341 } |
| 304 | 342 |
| 305 class KernelCapturedLoopScope extends KernelCapturedScope { | 343 class KernelCapturedLoopScope extends KernelCapturedScope { |
| 306 final List<ir.VariableDeclaration> boxedLoopVariables; | 344 final List<ir.VariableDeclaration> boxedLoopVariables; |
| 307 | 345 |
| 308 KernelCapturedLoopScope( | 346 KernelCapturedLoopScope( |
| 309 Set<ir.VariableDeclaration> boxedVariables, | 347 Set<ir.VariableDeclaration> boxedVariables, |
| 310 NodeBox capturedVariablesAccessor, | 348 NodeBox capturedVariablesAccessor, |
| 311 this.boxedLoopVariables, | 349 this.boxedLoopVariables, |
| 312 ir.TreeNode context, | |
| 313 Set<ir.VariableDeclaration> localsUsedInTryOrSync, | 350 Set<ir.VariableDeclaration> localsUsedInTryOrSync, |
| 314 Set<ir.VariableDeclaration> freeVariables, | 351 Set<ir.VariableDeclaration> freeVariables, |
| 315 bool hasThisLocal) | 352 bool hasThisLocal) |
| 316 : super(boxedVariables, capturedVariablesAccessor, context, | 353 : super(boxedVariables, capturedVariablesAccessor, localsUsedInTryOrSync, |
| 317 localsUsedInTryOrSync, freeVariables, hasThisLocal); | 354 freeVariables, hasThisLocal); |
| 318 | 355 |
| 319 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; | 356 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; |
| 320 } | 357 } |
| 321 | 358 |
| 322 class JsCapturedLoopScope extends JsCapturedScope implements CapturedLoopScope { | 359 class JsCapturedLoopScope extends JsCapturedScope implements CapturedLoopScope { |
| 323 final List<Local> boxedLoopVariables; | 360 final List<Local> boxedLoopVariables; |
| 324 | 361 |
| 325 JsCapturedLoopScope.from( | 362 JsCapturedLoopScope.from(Map<Local, JRecord> boxedVariables, |
| 326 KernelCapturedLoopScope capturedScope, KernelToLocalsMap localsMap) | 363 KernelCapturedLoopScope capturedScope, KernelToLocalsMap localsMap) |
| 327 : this.boxedLoopVariables = capturedScope.boxedLoopVariables | 364 : this.boxedLoopVariables = capturedScope.boxedLoopVariables |
| 328 .map(localsMap.getLocalVariable) | 365 .map(localsMap.getLocalVariable) |
| 329 .toList(), | 366 .toList(), |
| 330 super.from(capturedScope, localsMap); | 367 super.from(boxedVariables, capturedScope, localsMap); |
| 331 | 368 |
| 332 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; | 369 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; |
| 333 } | 370 } |
| 334 | 371 |
| 335 // TODO(johnniwinther): Add unittest for the computed [ClosureClass]. | 372 // TODO(johnniwinther): Add unittest for the computed [ClosureClass]. |
| 336 class KernelClosureClass extends JsScopeInfo | 373 class KernelClosureClass extends JsScopeInfo |
| 337 implements ClosureRepresentationInfo, JClass { | 374 implements ClosureRepresentationInfo { |
| 338 final String name; | |
| 339 final JLibrary library; | |
| 340 JFunction callMethod; | 375 JFunction callMethod; |
| 341 final Local closureEntity; | 376 final Local closureEntity; |
| 342 final Local thisLocal; | 377 final Local thisLocal; |
| 343 | 378 final ClassEntity closureClassEntity; |
| 344 /// Index into the classData, classList and classEnvironment lists where this | |
| 345 /// entity is stored in [JsToFrontendMapImpl]. | |
| 346 final int classIndex; | |
| 347 | 379 |
| 348 final Map<Local, JField> localToFieldMap = new Map<Local, JField>(); | 380 final Map<Local, JField> localToFieldMap = new Map<Local, JField>(); |
| 349 | 381 |
| 382 /// The set of scopes that this scope captures. In practice we only populate |
| 383 /// it if this scope uses a particular variable that is defined in another |
| 384 /// scope, and this information is only really useful for closures. |
| 385 final Set<JsCapturedScope> capturedScopes; |
| 386 |
| 350 KernelClosureClass.fromScopeInfo( | 387 KernelClosureClass.fromScopeInfo( |
| 388 this.closureClassEntity, |
| 351 ir.FunctionNode closureSourceNode, | 389 ir.FunctionNode closureSourceNode, |
| 352 this.name, | 390 Map<Local, JRecord> boxedVariables, |
| 353 this.classIndex, | 391 this.capturedScopes, |
| 354 this.library, | |
| 355 KernelScopeInfo info, | 392 KernelScopeInfo info, |
| 356 KernelToLocalsMap localsMap) | 393 KernelToLocalsMap localsMap) |
| 357 : closureEntity = closureSourceNode.parent is ir.Member | 394 : closureEntity = closureSourceNode.parent is ir.Member |
| 358 ? null | 395 ? null |
| 359 : localsMap.getLocalFunction(closureSourceNode.parent), | 396 : localsMap.getLocalFunction(closureSourceNode.parent), |
| 360 thisLocal = | 397 thisLocal = |
| 361 info.hasThisLocal ? new ThisLocal(localsMap.currentMember) : null, | 398 info.hasThisLocal ? new ThisLocal(localsMap.currentMember) : null, |
| 362 super.from(info, localsMap); | 399 super.from(boxedVariables, info, localsMap); |
| 363 | |
| 364 ClassEntity get closureClassEntity => this; | |
| 365 | 400 |
| 366 List<Local> get createdFieldEntities => localToFieldMap.keys.toList(); | 401 List<Local> get createdFieldEntities => localToFieldMap.keys.toList(); |
| 367 | 402 |
| 368 FieldEntity get thisFieldEntity => localToFieldMap[thisLocal]; | 403 FieldEntity get thisFieldEntity => localToFieldMap[thisLocal]; |
| 369 | 404 |
| 370 void forEachCapturedVariable(f(Local from, JField to)) { | 405 void forEachCapturedVariable(f(Local from, JField to)) { |
| 371 localToFieldMap.forEach(f); | 406 localToFieldMap.forEach(f); |
| 372 } | 407 } |
| 373 | 408 |
| 374 @override | 409 @override |
| 375 void forEachBoxedVariable(f(Local local, JField field)) { | 410 void forEachBoxedVariable(f(Local local, JField field)) { |
| 376 for (Local l in localToFieldMap.keys) { | 411 for (Local l in localToFieldMap.keys) { |
| 377 if (localToFieldMap[l] is JBoxedField) f(l, localToFieldMap[l]); | 412 if (localToFieldMap[l] is JRecord) f(l, localToFieldMap[l]); |
| 413 } |
| 414 for (JsCapturedScope scope in capturedScopes) { |
| 415 scope.forEachBoxedVariable(f); |
| 378 } | 416 } |
| 379 } | 417 } |
| 380 | 418 |
| 381 void forEachFreeVariable(f(Local variable, JField field)) { | 419 void forEachFreeVariable(f(Local variable, JField field)) { |
| 382 for (Local l in localToFieldMap.keys) { | 420 for (Local l in localToFieldMap.keys) { |
| 383 var jField = localToFieldMap[l]; | 421 var jField = localToFieldMap[l]; |
| 384 if (jField is! JBoxedField && jField is! BoxLocal) f(l, jField); | 422 if (jField is! BoxLocal) f(l, jField); |
| 385 } | 423 } |
| 386 } | 424 } |
| 387 | 425 |
| 388 bool isVariableBoxed(Local variable) => | 426 bool isVariableBoxed(Local variable) { |
| 389 localToFieldMap.keys.contains(variable); | 427 print( |
| 428 'RRRRRRRRRRR ${localToFieldMap.keys.contains(variable)} ${capturedScopes
}'); |
| 429 return localToFieldMap.keys.contains(variable) || |
| 430 capturedScopes.any((JsCapturedScope scope) => scope.isBoxed(variable)); |
| 431 } |
| 432 |
| 433 @override |
| 434 bool isBoxed(Local variable) { |
| 435 print( |
| 436 'RRRRRRRRRRR ${localToFieldMap.keys.contains(variable)} ${capturedScopes
}'); |
| 437 return localToFieldMap.keys.contains(variable) || |
| 438 capturedScopes.any((JsCapturedScope scope) => scope.isBoxed(variable)); |
| 439 } |
| 390 | 440 |
| 391 bool get isClosure => true; | 441 bool get isClosure => true; |
| 392 | |
| 393 bool get isAbstract => false; | |
| 394 | |
| 395 String toString() => '${jsElementPrefix}class($name)'; | |
| 396 } | 442 } |
| 397 | 443 |
| 398 /// A local variable to disambiguate between a variable that has been captured | 444 /// A local variable to disambiguate between a variable that has been captured |
| 399 /// from one scope to another. This is the ir.Node version that corresponds to | 445 /// from one scope to another. This is the ir.Node version that corresponds to |
| 400 /// [BoxLocal]. | 446 /// [BoxLocal]. |
| 401 class NodeBox { | 447 class NodeBox { |
| 402 final String name; | 448 final String name; |
| 403 final ir.TreeNode executableContext; | 449 final ir.TreeNode executableContext; |
| 404 NodeBox(this.name, this.executableContext); | 450 NodeBox(this.name, this.executableContext); |
| 405 } | 451 } |
| 406 | 452 |
| 453 class JClosureClass extends JClass { |
| 454 // TODO(efortuna): omg this is so horrible. |
| 455 final KernelToLocalsMap localsMap; |
| 456 |
| 457 JClosureClass(this.localsMap, JLibrary library, int classIndex, String name) |
| 458 : super(library, classIndex, name, isAbstract: false); |
| 459 |
| 460 @override |
| 461 bool get isClosure => true; |
| 462 |
| 463 String toString() => '${jsElementPrefix}closure_class($name)'; |
| 464 } |
| 465 |
| 407 class JClosureField extends JField { | 466 class JClosureField extends JField { |
| 408 JClosureField(String name, int memberIndex, | 467 JClosureField(String name, int memberIndex, |
| 409 KernelClosureClass containingClass, bool isConst, bool isAssignable) | 468 KernelClosureClass containingClass, bool isConst, bool isAssignable) |
| 410 : super(memberIndex, containingClass.library, containingClass, | 469 : super( |
| 411 new Name(name, containingClass.library), | 470 memberIndex, |
| 412 isAssignable: isAssignable, isConst: isConst, isStatic: false); | 471 containingClass.closureClassEntity.library, |
| 472 containingClass.closureClassEntity, |
| 473 new Name(name, containingClass.closureClassEntity.library), |
| 474 isAssignable: isAssignable, |
| 475 isConst: isConst, |
| 476 isStatic: false); |
| 413 } | 477 } |
| 414 | 478 |
| 415 /// A ClosureField that has been "boxed" to prevent name shadowing with the | 479 /// A container for variables declared in a particular scope that are accessed |
| 480 /// elsewhere. |
| 481 // TODO(efortuna, johnniwinther): Don't implement JClass. This isn't actually a |
| 482 // class. |
| 483 class JRecordContainer implements JClass { |
| 484 final JLibrary library; |
| 485 final String name; |
| 486 |
| 487 /// Index into the classData, classList and classEnvironment lists where this |
| 488 /// entity is stored in [JsToFrontendMapImpl]. |
| 489 final int classIndex; |
| 490 |
| 491 JRecordContainer(this.library, this.classIndex, this.name); |
| 492 |
| 493 bool get isAbstract => false; |
| 494 |
| 495 bool get isClosure => false; |
| 496 |
| 497 String toString() => '${jsElementPrefix}record_container($name)'; |
| 498 } |
| 499 |
| 500 /// A variable that has been "boxed" to prevent name shadowing with the |
| 416 /// original variable and ensure that this variable is updated/read with the | 501 /// original variable and ensure that this variable is updated/read with the |
| 417 /// most recent value. | 502 /// most recent value. |
| 418 /// This corresponds to BoxFieldElement; we reuse BoxLocal from the original | 503 /// This corresponds to BoxFieldElement; we reuse BoxLocal from the original |
| 419 /// algorithm to correspond to the actual name of the variable. | 504 /// algorithm to correspond to the actual name of the variable. |
| 420 class JBoxedField extends JField { | 505 class JRecord extends JField { |
| 421 final BoxLocal box; | 506 final BoxLocal box; |
| 422 JBoxedField(String name, int memberIndex, this.box, | 507 JRecord(String name, int memberIndex, this.box, JClass containingClass, |
| 423 KernelClosureClass containingClass, bool isConst, bool isAssignable) | 508 bool isConst, bool isAssignable) |
| 424 : super(memberIndex, containingClass.library, containingClass, | 509 : super(memberIndex, containingClass.library, containingClass, |
| 425 new Name(name, containingClass.library), | 510 new Name(name, containingClass.library), |
| 426 isAssignable: isAssignable, isConst: isConst); | 511 isAssignable: isAssignable, isConst: isConst); |
| 427 } | 512 } |
| 428 | 513 |
| 429 class ClosureClassDefinition implements ClassDefinition { | 514 class ClosureClassDefinition implements ClassDefinition { |
| 430 final ClassEntity cls; | 515 final ClassEntity cls; |
| 431 final SourceSpan location; | 516 final SourceSpan location; |
| 432 | 517 |
| 433 ClosureClassDefinition(this.cls, this.location); | 518 ClosureClassDefinition(this.cls, this.location); |
| 434 | 519 |
| 435 ClassKind get kind => ClassKind.closure; | 520 ClassKind get kind => ClassKind.closure; |
| 436 | 521 |
| 437 ir.Node get node => | 522 ir.Node get node => throw new UnsupportedError('JRecord.node for $cls'); |
| 438 throw new UnsupportedError('ClosureClassDefinition.node for $cls'); | |
| 439 | 523 |
| 440 String toString() => | 524 String toString() => 'JRecord(kind:$kind,cls:$cls,location:$location)'; |
| 441 'ClosureClassDefinition(kind:$kind,cls:$cls,location:$location)'; | |
| 442 } | 525 } |
| 443 | 526 |
| 444 class ClosureMemberData implements MemberData { | 527 class ClosureMemberData implements MemberData { |
| 445 final MemberDefinition definition; | 528 final MemberDefinition definition; |
| 446 | 529 |
| 447 ClosureMemberData(this.definition); | 530 ClosureMemberData(this.definition); |
| 448 | 531 |
| 449 @override | 532 @override |
| 450 Iterable<ConstantValue> getMetadata(KernelToElementMap elementMap) { | 533 Iterable<ConstantValue> getMetadata(KernelToElementMap elementMap) { |
| 451 return const <ConstantValue>[]; | 534 return const <ConstantValue>[]; |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 510 final SourceSpan location; | 593 final SourceSpan location; |
| 511 final MemberKind kind; | 594 final MemberKind kind; |
| 512 final ir.Node node; | 595 final ir.Node node; |
| 513 | 596 |
| 514 ClosureMemberDefinition(this.member, this.location, this.kind, this.node); | 597 ClosureMemberDefinition(this.member, this.location, this.kind, this.node); |
| 515 | 598 |
| 516 String toString() => | 599 String toString() => |
| 517 'ClosureMemberDefinition(kind:$kind,member:$member,location:$location)'; | 600 'ClosureMemberDefinition(kind:$kind,member:$member,location:$location)'; |
| 518 } | 601 } |
| 519 | 602 |
| 603 class RecordContainerDefinition implements ClassDefinition { |
| 604 final ClassEntity cls; |
| 605 final SourceSpan location; |
| 606 |
| 607 RecordContainerDefinition(this.cls, this.location); |
| 608 |
| 609 ClassKind get kind => ClassKind.container; |
| 610 |
| 611 ir.Node get node => |
| 612 throw new UnsupportedError('RecordContainerDefinition.node for $cls'); |
| 613 |
| 614 String toString() => |
| 615 'RecordContainerDefinition(kind:$kind,cls:$cls,location:$location)'; |
| 616 } |
| 617 |
| 520 /// Collection of scope data collected for a single member. | 618 /// Collection of scope data collected for a single member. |
| 521 class ScopeModel { | 619 class ScopeModel { |
| 522 /// Collection [ScopeInfo] data for the member. | 620 /// Collection [ScopeInfo] data for the member. |
| 523 KernelScopeInfo scopeInfo; | 621 KernelScopeInfo scopeInfo; |
| 524 | 622 |
| 525 /// Collected [CapturedScope] data for nodes. | 623 /// Collected [CapturedScope] data for nodes. |
| 526 Map<ir.Node, KernelCapturedScope> capturedScopesMap = | 624 Map<ir.Node, KernelCapturedScope> capturedScopesMap = |
| 527 <ir.Node, KernelCapturedScope>{}; | 625 <ir.Node, KernelCapturedScope>{}; |
| 528 | 626 |
| 529 /// Collected [ScopeInfo] data for nodes. | 627 /// Collected [ScopeInfo] data for nodes. |
| 530 Map<ir.FunctionNode, KernelScopeInfo> closuresToGenerate = | 628 Map<ir.FunctionNode, KernelScopeInfo> closuresToGenerate = |
| 531 <ir.FunctionNode, KernelScopeInfo>{}; | 629 <ir.FunctionNode, KernelScopeInfo>{}; |
| 532 } | 630 } |
| OLD | NEW |