Chromium Code Reviews| Index: pkg/compiler/lib/src/js_model/closure_visitors.dart |
| diff --git a/pkg/compiler/lib/src/js_model/closure_visitors.dart b/pkg/compiler/lib/src/js_model/closure_visitors.dart |
| index f27c6dd20a3f1e970714019e6ed4eea99b9c1f7e..c9466cf01a225b4ec05a5d9acc54484e862454ee 100644 |
| --- a/pkg/compiler/lib/src/js_model/closure_visitors.dart |
| +++ b/pkg/compiler/lib/src/js_model/closure_visitors.dart |
| @@ -177,6 +177,42 @@ class CapturedScopeBuilder extends ir.Visitor { |
| } |
| } |
| + @override |
| + void visitThisExpression(ir.ThisExpression thisExpression) { |
| + if (_hasThisLocal) _registerNeedsThis(); |
| + } |
| + |
| + @override |
| + void visitTypeParameter(ir.TypeParameter typeParameter) { |
| + ir.TreeNode context = _executableContext; |
| + if (_isInsideClosure && context is ir.Procedure && context.isFactory) { |
| + // This is a closure in a factory constructor. Since there is no |
| + // [:this:], we have to mark the type arguments as free variables to |
| + // capture them in the closure. |
| + // TODO(efortuna): Implement for in the case of RTI. |
| + // useTypeVariableAsLocal(typeParameter.bound); |
| + } |
| + |
| + if (_executableContext is ir.Member && |
| + _executableContext is! ir.Field && |
| + _hasThisLocal) { |
| + // In checked mode, using a type variable in a type annotation may lead |
| + // to a runtime type check that needs to access the type argument and |
| + // therefore the closure needs a this-element, if it is not in a field |
| + // initializer; field initializers are evaluated in a context where |
| + // the type arguments are available in locals. |
| + _registerNeedsThis(); |
| + } |
| + } |
| + |
| + /// Add `this` as a variable that needs to be accessed (and thus may become a |
| + /// free/captured variable. |
| + void _registerNeedsThis() { |
| + if (_isInsideClosure) { |
| + _currentScopeInfo.freeVariables.add(const ThisVariable()); |
|
Johnni Winther
2017/08/31 07:24:38
Add a `needsThis` property on [KernelScopeInfo] in
Emily Fortuna
2017/08/31 17:40:52
Done.
|
| + } |
| + } |
| + |
| @override |
| void visitForStatement(ir.ForStatement node) { |
| List<ir.VariableDeclaration> boxedLoopVariables = |
| @@ -303,3 +339,85 @@ class CapturedScopeBuilder extends ir.Visitor { |
| visitInvokable(functionDeclaration); |
| } |
| } |
| + |
| +/// A fake sentinel "ir" node place holder representing the usage of `this` |
| +/// inside closures that occur inside members. |
| +class ThisVariable implements ir.VariableDeclaration { |
| + const ThisVariable(); |
| + |
| + bool get isConst => true; |
| + set isConst(bool constVal) => |
| + throw new UnsupportedError("ThisVariable.setIsConst"); |
| + bool get isFinal => true; |
| + set isFinal(bool finalValue) => |
| + throw new UnsupportedError("ThisVariable.setIsFinal"); |
| + bool get isCovariant => false; |
| + set isCovariant(bool covariant) => |
| + throw new UnsupportedError("ThisVariable.setIsCovariant"); |
| + bool get isFieldFormal => false; |
| + set isFieldFormal(bool formal) => |
| + throw new UnsupportedError("ThisVariable.setIsFieldFormal"); |
| + String get name => 'this'; |
| + set name(String name) => throw new UnsupportedError("ThisVariable.setName"); |
| + set type(ir.DartType type) => throw new UnsupportedError("ThisVariable.type"); |
| + int get fileOffset => throw new UnsupportedError("ThisVariable.fileOffset"); |
| + set fileOffset(int offset) => |
| + throw new UnsupportedError("ThisVariable.setFileOffset"); |
| + |
| + int get binaryOffsetNoTag => |
| + throw new UnsupportedError("ThisVariable.binaryOffsetNoTag"); |
| + set binaryOffsetNoTag(int offset) => |
| + throw new UnsupportedError("ThisVariable.binaryOffsetNoTag"); |
| + |
| + int get fileEqualsOffset => |
| + throw new UnsupportedError("ThisVariable.fileEqualsOffset"); |
| + set fileEqualsOffset(int offset) => |
| + throw new UnsupportedError("ThisVariable.setFileEqualsOffset"); |
| + |
| + int get flags => throw new UnsupportedError("ThisVariable.flags"); |
| + set flags(int flags) => throw new UnsupportedError("ThisVariable.flags"); |
| + |
| + ir.DartType get type => |
| + throw new UnsupportedError("Sentinel this does not have a type."); |
| + |
| + ir.TreeNode get parent => |
| + throw new UnsupportedError("Sentinel this does not have a parent."); |
| + |
| + set parent(ir.TreeNode parent) => |
| + throw new UnsupportedError("Sentinel this does not have a parent."); |
| + |
| + ir.Expression get initializer => null; |
| + set initializer(ir.Expression expression) => |
| + throw new UnsupportedError("ThisVariable.setInitializer"); |
| + |
| + accept(ir.StatementVisitor v) => throw new UnsupportedError( |
| + "ThisVariable should not be walked in AST traversal"); |
| + accept1(ir.StatementVisitor1 v, arg) => throw new UnsupportedError( |
| + "ThisVariable should not be walked in AST traversal"); |
| + |
| + visitChildren(ir.Visitor v) { |
| + throw new UnsupportedError( |
| + "ThisVariable should not be walked in AST traversal"); |
| + } |
| + |
| + transformChildren(ir.Transformer v) { |
| + throw new UnsupportedError( |
| + "ThisVariable should not be walked in AST traversal"); |
| + } |
| + |
| + void replaceChild(ir.TreeNode child, ir.TreeNode replacement) => |
| + throw new UnsupportedError("ThisVariable.replaceChild"); |
| + |
| + void replaceWith(ir.TreeNode replacement) => |
| + throw new UnsupportedError("ThisVariable.replaceWith"); |
| + |
| + void remove() => throw new UnsupportedError("ThisVariable.remove"); |
| + |
| + ir.Program get enclosingProgram => |
| + throw new UnsupportedError("ThisVariable.enclosingProgram"); |
| + |
| + ir.Location get location => |
| + throw new UnsupportedError("ThisVariable.location"); |
| + |
| + String toString() => 'thisVariable'; |
| +} |