package com.redhat.ceylon.compiler.js;
import com.redhat.ceylon.compiler.typechecker.model.Declaration;
import com.redhat.ceylon.compiler.typechecker.model.MethodOrValue;
import com.redhat.ceylon.compiler.typechecker.model.Setter;
import com.redhat.ceylon.compiler.typechecker.model.TypedDeclaration;
import com.redhat.ceylon.compiler.typechecker.model.Value;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Visitor;
/**
* Determines if a value is "captured" by
* block nested in the same containing scope.
*
* For example, a captured value in a class
* body is an attribute. A captured value in
* a method body can outlive the execution of
* the method.
*
* @author Gavin King
*
*/
public class ValueVisitor extends Visitor {
private final TypedDeclaration declaration;
private boolean inCapturingScope = false;
private int sameScope;
public ValueVisitor(TypedDeclaration declaration) {
this.declaration = declaration;
}
private boolean enterCapturingScope() {
boolean cs = inCapturingScope;
inCapturingScope = true;
return cs;
}
private void exitCapturingScope(boolean cs) {
inCapturingScope = cs;
}
@Override public void visit(Tree.BaseMemberExpression that) {
visitReference(that);
/*if (that.getIdentifier()!=null) {
TypedDeclaration d = (TypedDeclaration) getDeclaration(that.getScope(), that.getUnit(), that.getIdentifier(), context);
visitReference(that, d);
}*/
}
private void visitReference(Tree.Primary that) {
if (inCapturingScope) {
capture(that);
}
}
private void capture(Tree.Primary that) {
if (that instanceof Tree.MemberOrTypeExpression) {
TypedDeclaration d = (TypedDeclaration) ((Tree.MemberOrTypeExpression) that).getDeclaration();
if (d==declaration) {
if (d.isParameter()) {
if (!d.getContainer().equals(that.getScope()) || sameScope>0) { //a reference from a default argument
//expression of the same parameter
//list does not capture a parameter
((MethodOrValue) d).setCaptured(true);
}
} else if (d instanceof Value) {
((Value) d).setCaptured(true);
}
/*if (d.isVariable() && !d.isClassMember() && !d.isToplevel()) {
that.addError("access to variable local from capturing scope: " + declaration.getName());
}*/
}
}
}
@Override
public void visit(Tree.QualifiedMemberExpression that) {
super.visit(that);
if (isSelfReference(that.getPrimary())) {
visitReference(that);
}
else {
capture(that);
}
}
private boolean isSelfReference(Tree.Primary that) {
return that instanceof Tree.This || that instanceof Tree.Outer;
}
@Override public void visit(Tree.Declaration that) {
Declaration dm = that.getDeclarationModel();
if (dm==declaration.getContainer()
|| dm==declaration
|| (dm instanceof Setter && ((Setter) dm).getGetter()==declaration)) {
inCapturingScope = false;
}
super.visit(that);
}
@Override public void visit(Tree.ClassDefinition that) {
boolean cs = enterCapturingScope();
super.visit(that);
exitCapturingScope(cs);
}
@Override public void visit(Tree.ObjectDefinition that) {
boolean cs = enterCapturingScope();
super.visit(that);
exitCapturingScope(cs);
}
@Override public void visit(Tree.MethodDefinition that) {
boolean cs = enterCapturingScope();
super.visit(that);
exitCapturingScope(cs);
}
@Override public void visit(Tree.AttributeGetterDefinition that) {
boolean cs = enterCapturingScope();
super.visit(that);
exitCapturingScope(cs);
}
@Override public void visit(Tree.AttributeSetterDefinition that) {
boolean cs = enterCapturingScope();
super.visit(that);
exitCapturingScope(cs);
}
@Override public void visit(Tree.TypedArgument that) {
boolean cs = enterCapturingScope();
super.visit(that);
exitCapturingScope(cs);
}
@Override public void visit(Tree.FunctionArgument that) {
boolean cs = enterCapturingScope();
super.visit(that);
exitCapturingScope(cs);
}
@Override
public void visit(Tree.LazySpecifierExpression that) {
if(that.getExpression() == null)return;
boolean cs = enterCapturingScope();
sameScope++;
that.getExpression().visit(this);
sameScope--;
exitCapturingScope(cs);
}
@Override
public void visit(Tree.Parameter that) {
//Mark all class initializer parameters as captured
if (that.getParameterModel().getDeclaration() instanceof com.redhat.ceylon.compiler.typechecker.model.Class) {
that.getParameterModel().getModel().setCaptured(true);
}
super.visit(that);
}
}