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'; |
+} |