From a923274e05a4610c6a84f66671355d9095c5b469 Mon Sep 17 00:00:00 2001 From: "Pavel V. Talanov" Date: Fri, 10 Aug 2012 21:33:41 +0400 Subject: [PATCH] Rework Closure* for FunctionLiteralTranslator. Adapted from https://github.com/develar/kotlin/commit/a9e0a42fb1347fa8e21c86b5a073ef8a7c873da0. --- .../utils/closure/CaptureClosureVisitor.java | 116 +++++++++++++----- .../utils/closure/ClosureContext.java | 20 ++- .../translate/utils/closure/ClosureUtils.java | 10 +- 3 files changed, 106 insertions(+), 40 deletions(-) diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/closure/CaptureClosureVisitor.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/closure/CaptureClosureVisitor.java index 0efd171d933..c4bb83c570e 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/closure/CaptureClosureVisitor.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/closure/CaptureClosureVisitor.java @@ -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 { - +class CaptureClosureVisitor extends JetTreeVisitor { @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; + } + } + } } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/closure/ClosureContext.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/closure/ClosureContext.java index 324fa038fd6..1b0f233d38b 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/closure/ClosureContext.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/closure/ClosureContext.java @@ -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 descriptors = Sets.newHashSet(); + private final Set descriptors = new OrderedSet(); + + 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); diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/closure/ClosureUtils.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/closure/ClosureUtils.java index 2781f42fbbe..882897dd733 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/closure/ClosureUtils.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/closure/ClosureUtils.java @@ -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; } }