Rework Closure* for FunctionLiteralTranslator.

Adapted from https://github.com/develar/kotlin/commit/a9e0a42fb1347fa8e21c86b5a073ef8a7c873da0.
This commit is contained in:
Pavel V. Talanov
2012-08-10 21:33:41 +04:00
parent 29d1b7e29f
commit a923274e05
3 changed files with 106 additions and 40 deletions
@@ -17,16 +17,10 @@
package org.jetbrains.k2js.translate.utils.closure;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.JetNodeTypes;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
import org.jetbrains.jet.lang.psi.JetElement;
import org.jetbrains.jet.lang.psi.JetProperty;
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
import org.jetbrains.jet.lang.psi.JetTreeVisitor;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
import org.jetbrains.k2js.translate.utils.BindingUtils;
@@ -34,49 +28,107 @@ import org.jetbrains.k2js.translate.utils.BindingUtils;
/**
* @author Pavel Talanov
*/
public class CaptureClosureVisitor extends JetTreeVisitor<ClosureContext> {
class CaptureClosureVisitor extends JetTreeVisitor<ClosureContext> {
@NotNull
private final BindingContext bindingContext;
@NotNull
private final JetElement functionElement;
private final DeclarationDescriptor functionDescriptor;
/*package*/ CaptureClosureVisitor(@NotNull JetElement functionElement, @NotNull BindingContext bindingContext) {
/*package*/ CaptureClosureVisitor(@NotNull DeclarationDescriptor descriptor, @NotNull BindingContext bindingContext) {
this.bindingContext = bindingContext;
this.functionElement = functionElement;
functionDescriptor = descriptor;
}
@Override
public Void visitJetElement(JetElement element, ClosureContext data) {
if (element instanceof JetValueArgument) {
JetExpression expression = ((JetValueArgument) element).getArgumentExpression();
if (expression != null) {
expression.accept(this, data);
}
}
else if (!(element instanceof JetNamedFunction)) {
return super.visitJetElement(element, data);
}
return null;
}
@Override
public Void visitSimpleNameExpression(@NotNull JetSimpleNameExpression expression,
@NotNull ClosureContext context) {
//TODO: this gets as dirty as it can be, should clean up and test more or use different approach
expression.acceptChildren(this, context);
@NotNull ClosureContext context) {
if (expression.getNode().getElementType() == JetNodeTypes.OPERATION_REFERENCE) {
return null;
}
DeclarationDescriptor descriptor = BindingUtils.getNullableDescriptorForReferenceExpression(bindingContext, expression);
if (!(descriptor instanceof VariableDescriptor)) {
if (descriptor instanceof SimpleFunctionDescriptor && !descriptor.getName().isSpecial()) {
checkOuterClassDescriptor(descriptor, context);
}
return null;
}
VariableDescriptor variableDescriptor = (VariableDescriptor) descriptor;
PsiElement variableDeclaration = BindingContextUtils.descriptorToDeclaration(bindingContext, descriptor);
if (variableDeclaration == null) {
return null;
}
if (PsiTreeUtil.isAncestor(functionElement, variableDeclaration, false)) {
return null;
}
boolean isProperty = variableDescriptor instanceof PropertyDescriptor;
if (!isProperty && !variableDescriptor.isVar() && variableDeclaration instanceof JetProperty) {
context.put(variableDescriptor);
return null;
}
boolean isLoopParameter = variableDeclaration.getNode().getElementType().equals(JetNodeTypes.LOOP_PARAMETER);
if (isLoopParameter) {
if (captured(variableDescriptor, context)) {
context.put(variableDescriptor);
}
return null;
}
private boolean captured(VariableDescriptor descriptor, ClosureContext context) {
if (descriptor instanceof PropertyDescriptor) {
checkOuterClassDescriptor(descriptor, context);
return false;
}
// is not working, try test life — init is captured, but this method returns false
//if (bindingContext.get(BindingContext.CAPTURED_IN_CLOSURE, descriptor) != Boolean.TRUE) {
// return false;
//}
if (isAncestor(functionDescriptor, descriptor)) {
return false;
}
if (descriptor instanceof LocalVariableDescriptor && descriptor.isVar()) {
// todo modification of outer local variable
context.setHasLocalVariables();
return true;
}
PsiElement variableDeclaration = BindingContextUtils.descriptorToDeclaration(bindingContext, descriptor);
if (variableDeclaration == null) {
// "it" doesn't have declaration
return false;
}
return descriptor instanceof ValueParameterDescriptor ||
(!descriptor.isVar() && variableDeclaration instanceof JetProperty) ||
variableDeclaration.getNode().getElementType().equals(JetNodeTypes.LOOP_PARAMETER);
}
// differs from DescriptorUtils — fails if reach NamespaceDescriptor
public static boolean isAncestor(@NotNull DeclarationDescriptor ancestor,
@NotNull DeclarationDescriptor declarationDescriptor) {
DeclarationDescriptor descriptor = declarationDescriptor.getContainingDeclaration();
while (descriptor != null && !(descriptor instanceof NamespaceDescriptor)) {
if (ancestor == descriptor) {
return true;
}
descriptor = descriptor.getContainingDeclaration();
}
return false;
}
private static void checkOuterClassDescriptor(DeclarationDescriptor descriptor, ClosureContext context) {
if (context.outerClassDescriptor == null) {
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
if (containingDeclaration instanceof ClassDescriptor) {
context.outerClassDescriptor = (ClassDescriptor) containingDeclaration;
}
}
}
}
@@ -16,8 +16,10 @@
package org.jetbrains.k2js.translate.utils.closure;
import com.google.common.collect.Sets;
import com.intellij.util.containers.OrderedSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
import java.util.Collection;
@@ -27,9 +29,21 @@ import java.util.Set;
* @author Pavel Talanov
*/
public final class ClosureContext {
@NotNull
private final Set<VariableDescriptor> descriptors = Sets.newHashSet();
private final Set<VariableDescriptor> descriptors = new OrderedSet<VariableDescriptor>();
private boolean hasLocalVariables;
@Nullable
public ClassDescriptor outerClassDescriptor;
public boolean isLocalVariablesAffected() {
return hasLocalVariables;
}
void setHasLocalVariables() {
hasLocalVariables = true;
}
/*package*/ void put(@NotNull VariableDescriptor descriptor) {
descriptors.add(descriptor);
@@ -17,22 +17,22 @@
package org.jetbrains.k2js.translate.utils.closure;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.psi.JetElement;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.jet.lang.resolve.BindingContext;
/**
* @author Pavel Talanov
*/
public final class ClosureUtils {
private ClosureUtils() {
}
@NotNull
public static ClosureContext captureClosure(@NotNull TranslationContext context, @NotNull JetElement function) {
CaptureClosureVisitor captureClosureVisitor = new CaptureClosureVisitor(function, context.bindingContext());
public static ClosureContext captureClosure(@NotNull BindingContext bindingContext, @NotNull JetElement element, @NotNull DeclarationDescriptor descriptor) {
CaptureClosureVisitor captureClosureVisitor = new CaptureClosureVisitor(descriptor, bindingContext);
ClosureContext closureContext = new ClosureContext();
function.accept(captureClosureVisitor, closureContext);
element.accept(captureClosureVisitor, closureContext);
return closureContext;
}
}