| 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/entities.dart'; | 10 import '../elements/entities.dart'; |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 147 | 147 |
| 148 // TODO(efortuna): Eventually capturedScopesMap[node] should always | 148 // TODO(efortuna): Eventually capturedScopesMap[node] should always |
| 149 // be non-null, and we should just test that with an assert. | 149 // be non-null, and we should just test that with an assert. |
| 150 @override | 150 @override |
| 151 CapturedScope getCapturedScope(MemberEntity entity) { | 151 CapturedScope getCapturedScope(MemberEntity entity) { |
| 152 MemberDefinition definition = _elementMap.getMemberDefinition(entity); | 152 MemberDefinition definition = _elementMap.getMemberDefinition(entity); |
| 153 switch (definition.kind) { | 153 switch (definition.kind) { |
| 154 case MemberKind.regular: | 154 case MemberKind.regular: |
| 155 case MemberKind.constructor: | 155 case MemberKind.constructor: |
| 156 case MemberKind.constructorBody: | 156 case MemberKind.constructorBody: |
| 157 case MemberKind.closureCall: |
| 157 return _capturedScopesMap[definition.node] ?? const CapturedScope(); | 158 return _capturedScopesMap[definition.node] ?? const CapturedScope(); |
| 158 default: | 159 default: |
| 159 throw failedAt(entity, "Unexpected member definition $definition"); | 160 throw failedAt(entity, "Unexpected member definition $definition"); |
| 160 } | 161 } |
| 161 } | 162 } |
| 162 | 163 |
| 163 @override | 164 @override |
| 164 // TODO(efortuna): Eventually capturedScopesMap[node] should always | 165 // TODO(efortuna): Eventually capturedScopesMap[node] should always |
| 165 // be non-null, and we should just test that with an assert. | 166 // be non-null, and we should just test that with an assert. |
| 166 CapturedLoopScope getCapturedLoopScope(ir.Node loopNode) => | 167 CapturedLoopScope getCapturedLoopScope(ir.Node loopNode) => |
| 167 _capturedScopesMap[loopNode] ?? const CapturedLoopScope(); | 168 _capturedScopesMap[loopNode] ?? const CapturedLoopScope(); |
| 168 | 169 |
| 169 @override | 170 @override |
| 170 // TODO(efortuna): Eventually closureRepresentationMap[node] should always be | 171 // TODO(efortuna): Eventually closureRepresentationMap[node] should always be |
| 171 // non-null, and we should just test that with an assert. | 172 // non-null, and we should just test that with an assert. |
| 172 ClosureRepresentationInfo getClosureRepresentationInfo(Entity entity) { | 173 ClosureRepresentationInfo getClosureRepresentationInfo(Entity entity) { |
| 173 return _closureRepresentationMap[entity] ?? | 174 return _closureRepresentationMap[entity] ?? |
| 174 const ClosureRepresentationInfo(); | 175 const ClosureRepresentationInfo(); |
| 175 } | 176 } |
| 176 } | 177 } |
| 177 | 178 |
| 178 class KernelScopeInfo { | 179 class KernelScopeInfo { |
| 179 final Set<ir.VariableDeclaration> localsUsedInTryOrSync; | 180 final Set<ir.VariableDeclaration> localsUsedInTryOrSync; |
| 180 final bool hasThisLocal; | 181 final bool hasThisLocal; |
| 181 final Set<ir.VariableDeclaration> boxedVariables; | 182 final Set<ir.VariableDeclaration> boxedVariables; |
| 183 // If boxedVariables is empty, this will be null, because no variables will |
| 184 // need to be boxed. |
| 185 final NodeBox capturedVariablesAccessor; |
| 182 | 186 |
| 183 /// The set of variables that were defined in another scope, but are used in | 187 /// The set of variables that were defined in another scope, but are used in |
| 184 /// this scope. | 188 /// this scope. |
| 185 Set<ir.VariableDeclaration> freeVariables = new Set<ir.VariableDeclaration>(); | 189 Set<ir.VariableDeclaration> freeVariables = new Set<ir.VariableDeclaration>(); |
| 186 | 190 |
| 187 KernelScopeInfo(this.hasThisLocal) | 191 KernelScopeInfo(this.hasThisLocal) |
| 188 : localsUsedInTryOrSync = new Set<ir.VariableDeclaration>(), | 192 : localsUsedInTryOrSync = new Set<ir.VariableDeclaration>(), |
| 189 boxedVariables = new Set<ir.VariableDeclaration>(); | 193 boxedVariables = new Set<ir.VariableDeclaration>(), |
| 194 capturedVariablesAccessor = null; |
| 190 | 195 |
| 191 KernelScopeInfo.from(this.hasThisLocal, KernelScopeInfo info) | 196 KernelScopeInfo.from(this.hasThisLocal, KernelScopeInfo info) |
| 192 : localsUsedInTryOrSync = info.localsUsedInTryOrSync, | 197 : localsUsedInTryOrSync = info.localsUsedInTryOrSync, |
| 193 boxedVariables = info.boxedVariables; | 198 boxedVariables = info.boxedVariables, |
| 199 capturedVariablesAccessor = null; |
| 194 | 200 |
| 195 KernelScopeInfo.withBoxedVariables(this.boxedVariables, | 201 KernelScopeInfo.withBoxedVariables( |
| 196 this.localsUsedInTryOrSync, this.freeVariables, this.hasThisLocal); | 202 this.boxedVariables, |
| 203 this.capturedVariablesAccessor, |
| 204 this.localsUsedInTryOrSync, |
| 205 this.freeVariables, |
| 206 this.hasThisLocal); |
| 197 | 207 |
| 198 String toString() { | 208 String toString() { |
| 199 StringBuffer sb = new StringBuffer(); | 209 StringBuffer sb = new StringBuffer(); |
| 200 sb.write('this=$hasThisLocal,'); | 210 sb.write('this=$hasThisLocal,'); |
| 201 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}'); | 211 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}'); |
| 202 return sb.toString(); | 212 return sb.toString(); |
| 203 } | 213 } |
| 204 } | 214 } |
| 205 | 215 |
| 206 class JsScopeInfo extends ScopeInfo { | 216 class JsScopeInfo extends ScopeInfo { |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 243 } | 253 } |
| 244 | 254 |
| 245 bool isBoxed(Local variable) => boxedVariables.contains(variable); | 255 bool isBoxed(Local variable) => boxedVariables.contains(variable); |
| 246 } | 256 } |
| 247 | 257 |
| 248 class KernelCapturedScope extends KernelScopeInfo { | 258 class KernelCapturedScope extends KernelScopeInfo { |
| 249 final ir.TreeNode context; | 259 final ir.TreeNode context; |
| 250 | 260 |
| 251 KernelCapturedScope( | 261 KernelCapturedScope( |
| 252 Set<ir.VariableDeclaration> boxedVariables, | 262 Set<ir.VariableDeclaration> boxedVariables, |
| 263 NodeBox capturedVariablesAccessor, |
| 253 this.context, | 264 this.context, |
| 254 Set<ir.VariableDeclaration> localsUsedInTryOrSync, | 265 Set<ir.VariableDeclaration> localsUsedInTryOrSync, |
| 255 Set<ir.VariableDeclaration> freeVariables, | 266 Set<ir.VariableDeclaration> freeVariables, |
| 256 bool hasThisLocal) | 267 bool hasThisLocal) |
| 257 : super.withBoxedVariables( | 268 : super.withBoxedVariables(boxedVariables, capturedVariablesAccessor, |
| 258 boxedVariables, localsUsedInTryOrSync, freeVariables, hasThisLocal); | 269 localsUsedInTryOrSync, freeVariables, hasThisLocal); |
| 259 | 270 |
| 260 bool get requiresContextBox => boxedVariables.isNotEmpty; | 271 bool get requiresContextBox => boxedVariables.isNotEmpty; |
| 261 } | 272 } |
| 262 | 273 |
| 263 class JsCapturedScope extends JsScopeInfo implements CapturedScope { | 274 class JsCapturedScope extends JsScopeInfo implements CapturedScope { |
| 264 final Local context; | 275 final Local context; |
| 265 | 276 |
| 266 JsCapturedScope.from( | 277 JsCapturedScope.from( |
| 267 KernelCapturedScope capturedScope, KernelToLocalsMap localsMap) | 278 KernelCapturedScope capturedScope, KernelToLocalsMap localsMap) |
| 268 : this.context = localsMap.getLocalVariable(capturedScope.context), | 279 : this.context = localsMap.getLocalVariable(capturedScope.context), |
| 269 super.from(capturedScope, localsMap); | 280 super.from(capturedScope, localsMap); |
| 270 | 281 |
| 271 bool get requiresContextBox => boxedVariables.isNotEmpty; | 282 bool get requiresContextBox => boxedVariables.isNotEmpty; |
| 272 } | 283 } |
| 273 | 284 |
| 274 class KernelCapturedLoopScope extends KernelCapturedScope { | 285 class KernelCapturedLoopScope extends KernelCapturedScope { |
| 275 final List<ir.VariableDeclaration> boxedLoopVariables; | 286 final List<ir.VariableDeclaration> boxedLoopVariables; |
| 276 | 287 |
| 277 KernelCapturedLoopScope( | 288 KernelCapturedLoopScope( |
| 278 Set<ir.VariableDeclaration> boxedVariables, | 289 Set<ir.VariableDeclaration> boxedVariables, |
| 290 NodeBox capturedVariablesAccessor, |
| 279 this.boxedLoopVariables, | 291 this.boxedLoopVariables, |
| 280 ir.TreeNode context, | 292 ir.TreeNode context, |
| 281 Set<ir.VariableDeclaration> localsUsedInTryOrSync, | 293 Set<ir.VariableDeclaration> localsUsedInTryOrSync, |
| 282 Set<ir.VariableDeclaration> freeVariables, | 294 Set<ir.VariableDeclaration> freeVariables, |
| 283 bool hasThisLocal) | 295 bool hasThisLocal) |
| 284 : super(boxedVariables, context, localsUsedInTryOrSync, freeVariables, | 296 : super(boxedVariables, capturedVariablesAccessor, context, |
| 285 hasThisLocal); | 297 localsUsedInTryOrSync, freeVariables, hasThisLocal); |
| 286 | 298 |
| 287 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; | 299 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; |
| 288 } | 300 } |
| 289 | 301 |
| 290 class JsCapturedLoopScope extends JsCapturedScope implements CapturedLoopScope { | 302 class JsCapturedLoopScope extends JsCapturedScope implements CapturedLoopScope { |
| 291 final List<Local> boxedLoopVariables; | 303 final List<Local> boxedLoopVariables; |
| 292 | 304 |
| 293 JsCapturedLoopScope.from( | 305 JsCapturedLoopScope.from( |
| 294 KernelCapturedLoopScope capturedScope, KernelToLocalsMap localsMap) | 306 KernelCapturedLoopScope capturedScope, KernelToLocalsMap localsMap) |
| 295 : this.boxedLoopVariables = capturedScope.boxedLoopVariables | 307 : this.boxedLoopVariables = capturedScope.boxedLoopVariables |
| 296 .map(localsMap.getLocalVariable) | 308 .map(localsMap.getLocalVariable) |
| 297 .toList(), | 309 .toList(), |
| 298 super.from(capturedScope, localsMap); | 310 super.from(capturedScope, localsMap); |
| 299 | 311 |
| 300 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; | 312 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; |
| 301 } | 313 } |
| 302 | 314 |
| 303 // TODO(johnniwinther): Add unittest for the computed [ClosureClass]. | 315 // TODO(johnniwinther): Add unittest for the computed [ClosureClass]. |
| 304 class KernelClosureClass extends JsScopeInfo | 316 class KernelClosureClass extends JsScopeInfo |
| 305 implements ClosureRepresentationInfo, JClass { | 317 implements ClosureRepresentationInfo, JClass { |
| 306 final ir.Location location; | 318 final ir.Location location; |
| 307 | 319 |
| 308 final String name; | 320 final String name; |
| 309 final JLibrary library; | 321 final JLibrary library; |
| 310 JFunction callMethod; | 322 JFunction callMethod; |
| 323 final Local closureEntity; |
| 311 | 324 |
| 312 /// Index into the classData, classList and classEnvironment lists where this | 325 /// Index into the classData, classList and classEnvironment lists where this |
| 313 /// entity is stored in [JsToFrontendMapImpl]. | 326 /// entity is stored in [JsToFrontendMapImpl]. |
| 314 final int classIndex; | 327 final int classIndex; |
| 315 | 328 |
| 316 final Map<Local, JField> localToFieldMap = new Map<Local, JField>(); | 329 final Map<Local, JField> localToFieldMap = new Map<Local, JField>(); |
| 317 | 330 |
| 318 KernelClosureClass.fromScopeInfo(this.name, this.classIndex, this.library, | 331 KernelClosureClass.fromScopeInfo( |
| 319 KernelScopeInfo info, this.location, KernelToLocalsMap localsMap) | 332 ir.FunctionNode closureSourceNode, |
| 320 : super.from(info, localsMap); | 333 this.name, |
| 321 | 334 this.classIndex, |
| 322 // TODO(efortuna): Implement. | 335 this.library, |
| 323 Local get closureEntity => null; | 336 KernelScopeInfo info, |
| 337 this.location, |
| 338 KernelToLocalsMap localsMap) |
| 339 : super.from(info, localsMap), |
| 340 closureEntity = localsMap.getLocalFunction(closureSourceNode.parent); |
| 324 | 341 |
| 325 ClassEntity get closureClassEntity => this; | 342 ClassEntity get closureClassEntity => this; |
| 326 | 343 |
| 327 List<Local> get createdFieldEntities => localToFieldMap.keys.toList(); | 344 List<Local> get createdFieldEntities => localToFieldMap.keys.toList(); |
| 328 | 345 |
| 329 // TODO(efortuna): Implement. | 346 // TODO(efortuna): Implement. |
| 330 FieldEntity get thisFieldEntity => null; | 347 FieldEntity get thisFieldEntity => null; |
| 331 | 348 |
| 332 void forEachCapturedVariable(f(Local from, JField to)) { | 349 void forEachCapturedVariable(f(Local from, JField to)) { |
| 333 localToFieldMap.forEach(f); | 350 localToFieldMap.forEach(f); |
| 334 } | 351 } |
| 335 | 352 |
| 336 // TODO(efortuna): Implement. | |
| 337 @override | 353 @override |
| 338 void forEachBoxedVariable(f(Local local, JField field)) {} | 354 void forEachBoxedVariable(f(Local local, JField field)) { |
| 355 for (Local l in localToFieldMap.keys) { |
| 356 if (localToFieldMap[l] is JBoxedField) f(l, localToFieldMap[l]); |
| 357 } |
| 358 } |
| 339 | 359 |
| 340 // TODO(efortuna): Implement. | 360 void forEachFreeVariable(f(Local variable, JField field)) { |
| 341 void forEachFreeVariable(f(Local variable, JField field)) {} | 361 for (Local l in localToFieldMap.keys) { |
| 362 var jField = localToFieldMap[l]; |
| 363 if (jField is! JBoxedField && jField is! BoxLocal) f(l, jField); |
| 364 } |
| 365 } |
| 342 | 366 |
| 343 // TODO(efortuna): Implement. | 367 bool isVariableBoxed(Local variable) => |
| 344 bool isVariableBoxed(Local variable) => false; | 368 localToFieldMap.keys.contains(variable); |
| 345 | 369 |
| 346 bool get isClosure => true; | 370 bool get isClosure => true; |
| 347 | 371 |
| 348 bool get isAbstract => false; | 372 bool get isAbstract => false; |
| 349 | 373 |
| 350 String toString() => '${jsElementPrefix}class($name)'; | 374 String toString() => '${jsElementPrefix}class($name)'; |
| 351 } | 375 } |
| 352 | 376 |
| 377 /// A local variable to disambiguate between a variable that has been captured |
| 378 /// from one scope to another. This is the ir.Node version that corresponds to |
| 379 /// [BoxLocal]. |
| 380 class NodeBox { |
| 381 final String name; |
| 382 final ir.TreeNode executableContext; |
| 383 final ir.Member memberContext; |
| 384 NodeBox(this.name, this.executableContext, this.memberContext); |
| 385 } |
| 386 |
| 353 class JClosureField extends JField { | 387 class JClosureField extends JField { |
| 354 JClosureField(String name, int memberIndex, | 388 JClosureField(String name, int memberIndex, |
| 355 KernelClosureClass containingClass, bool isConst, bool isAssignable) | 389 KernelClosureClass containingClass, bool isConst, bool isAssignable) |
| 356 : super(memberIndex, containingClass.library, containingClass, | 390 : super(memberIndex, containingClass.library, containingClass, |
| 357 new Name(name, containingClass.library), | 391 new Name(name, containingClass.library), |
| 358 isAssignable: isAssignable, isConst: isConst); | 392 isAssignable: isAssignable, isConst: isConst); |
| 359 } | 393 } |
| 360 | 394 |
| 395 /// A ClosureField that has been "boxed" to prevent name shadowing with the |
| 396 /// original variable and ensure that this variable is updated/read with the |
| 397 /// most recent value. |
| 398 /// This corresponds to BoxFieldElement; we reuse BoxLocal from the original |
| 399 /// algorithm to correspond to the actual name of the variable. |
| 400 class JBoxedField extends JField { |
| 401 final BoxLocal box; |
| 402 JBoxedField(String name, int memberIndex, this.box, |
| 403 KernelClosureClass containingClass, bool isConst, bool isAssignable) |
| 404 : super(memberIndex, containingClass.library, containingClass, |
| 405 new Name(name, containingClass.library), |
| 406 isAssignable: isAssignable, isConst: isConst); |
| 407 } |
| 408 |
| 361 class ClosureClassDefinition implements ClassDefinition { | 409 class ClosureClassDefinition implements ClassDefinition { |
| 362 final ClassEntity cls; | 410 final ClassEntity cls; |
| 363 final ir.Location location; | 411 final ir.Location location; |
| 364 | 412 |
| 365 ClosureClassDefinition(this.cls, this.location); | 413 ClosureClassDefinition(this.cls, this.location); |
| 366 | 414 |
| 367 ClassKind get kind => ClassKind.closure; | 415 ClassKind get kind => ClassKind.closure; |
| 368 | 416 |
| 369 ir.Node get node => | 417 ir.Node get node => |
| 370 throw new UnsupportedError('ClosureClassDefinition.node for $cls'); | 418 throw new UnsupportedError('ClosureClassDefinition.node for $cls'); |
| (...skipping 23 matching lines...) Expand all Loading... |
| 394 KernelScopeInfo scopeInfo; | 442 KernelScopeInfo scopeInfo; |
| 395 | 443 |
| 396 /// Collected [CapturedScope] data for nodes. | 444 /// Collected [CapturedScope] data for nodes. |
| 397 Map<ir.Node, KernelCapturedScope> capturedScopesMap = | 445 Map<ir.Node, KernelCapturedScope> capturedScopesMap = |
| 398 <ir.Node, KernelCapturedScope>{}; | 446 <ir.Node, KernelCapturedScope>{}; |
| 399 | 447 |
| 400 /// Collected [ScopeInfo] data for nodes. | 448 /// Collected [ScopeInfo] data for nodes. |
| 401 Map<ir.FunctionNode, KernelScopeInfo> closuresToGenerate = | 449 Map<ir.FunctionNode, KernelScopeInfo> closuresToGenerate = |
| 402 <ir.FunctionNode, KernelScopeInfo>{}; | 450 <ir.FunctionNode, KernelScopeInfo>{}; |
| 403 } | 451 } |
| OLD | NEW |