Rework Closure* for FunctionLiteralTranslator.
Adapted from https://github.com/develar/kotlin/commit/a9e0a42fb1347fa8e21c86b5a073ef8a7c873da0.
This commit is contained in:
+84
-32
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+17
-3
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user