JS backend: fixed the variable capturing in deeply nested functions.
(cherry picked from commit cee29a6)
This commit is contained in:
@@ -35,4 +35,8 @@ public final class ClosureTest extends SingleFileTranslationTest {
|
||||
public void testClosureReferencingMember() throws Exception {
|
||||
fooBoxTest();
|
||||
}
|
||||
|
||||
public void testLocalParameterInLocalNamedFunction() throws Exception {
|
||||
fooBoxTest();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.Named;
|
||||
import org.jetbrains.jet.lang.psi.JetExpression;
|
||||
@@ -237,7 +236,7 @@ public class TranslationContext {
|
||||
}
|
||||
|
||||
public JsExpression getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor) {
|
||||
if (usageTracker != null && descriptor instanceof ClassDescriptor) {
|
||||
if (usageTracker != null) {
|
||||
usageTracker.triggerUsed(descriptor);
|
||||
}
|
||||
return aliasingContext.getAliasForDescriptor(descriptor);
|
||||
|
||||
@@ -16,23 +16,189 @@
|
||||
|
||||
package org.jetbrains.k2js.translate.context;
|
||||
|
||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.containers.OrderedSet;
|
||||
import gnu.trove.THashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.descriptors.*;
|
||||
import org.jetbrains.k2js.translate.utils.JsDescriptorUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public final class UsageTracker {
|
||||
@Nullable
|
||||
private final ClassDescriptor trackedClassDescriptor;
|
||||
@NotNull
|
||||
private final MemberDescriptor memberDescriptor;
|
||||
|
||||
@Nullable
|
||||
private final UsageTracker parent;
|
||||
@Nullable
|
||||
private List<UsageTracker> children;
|
||||
|
||||
public class UsageTracker {
|
||||
private final DeclarationDescriptor trackedDescriptor;
|
||||
private boolean used;
|
||||
@Nullable
|
||||
private Set<CallableDescriptor> capturedVariables;
|
||||
private ClassDescriptor outerClassDescriptor;
|
||||
|
||||
public UsageTracker(DeclarationDescriptor trackedDescriptor) {
|
||||
this.trackedDescriptor = trackedDescriptor;
|
||||
public UsageTracker(@NotNull MemberDescriptor memberDescriptor, @Nullable UsageTracker parent, @Nullable ClassDescriptor trackedClassDescriptor) {
|
||||
this.memberDescriptor = memberDescriptor;
|
||||
this.trackedClassDescriptor = trackedClassDescriptor;
|
||||
this.parent = parent;
|
||||
if (parent != null) {
|
||||
parent.addChild(this);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isUsed() {
|
||||
return used;
|
||||
}
|
||||
|
||||
private void addChild(UsageTracker child) {
|
||||
if (children == null) {
|
||||
children = new SmartList<UsageTracker>();
|
||||
}
|
||||
children.add(child);
|
||||
}
|
||||
|
||||
private void addCapturedMember(CallableDescriptor descriptor) {
|
||||
if (capturedVariables == null) {
|
||||
capturedVariables = new OrderedSet<CallableDescriptor>();
|
||||
}
|
||||
capturedVariables.add(descriptor);
|
||||
}
|
||||
|
||||
public void triggerUsed(DeclarationDescriptor descriptor) {
|
||||
if (trackedDescriptor == descriptor) {
|
||||
used = true;
|
||||
if ((descriptor instanceof PropertyDescriptor || descriptor instanceof PropertyAccessorDescriptor)) {
|
||||
checkOuterClass(descriptor);
|
||||
}
|
||||
else if (descriptor instanceof VariableDescriptor) {
|
||||
VariableDescriptor variableDescriptor = (VariableDescriptor) descriptor;
|
||||
if ((capturedVariables == null || !capturedVariables.contains(variableDescriptor)) &&
|
||||
!isAncestor(memberDescriptor, variableDescriptor)) {
|
||||
addCapturedMember(variableDescriptor);
|
||||
}
|
||||
}
|
||||
else if (descriptor instanceof SimpleFunctionDescriptor) {
|
||||
checkOuterClass(descriptor);
|
||||
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
|
||||
// local named function
|
||||
CallableDescriptor callableDescriptor = (CallableDescriptor) descriptor;
|
||||
if (!JsDescriptorUtils.isExtension(callableDescriptor) &&
|
||||
!(containingDeclaration instanceof ClassOrNamespaceDescriptor) &&
|
||||
!isAncestor(memberDescriptor, descriptor)) {
|
||||
addCapturedMember(callableDescriptor);
|
||||
}
|
||||
}
|
||||
else if (descriptor instanceof ClassDescriptor) {
|
||||
if (trackedClassDescriptor == descriptor) {
|
||||
used = true;
|
||||
}
|
||||
else if (parent != null) {
|
||||
UsageTracker p = parent;
|
||||
do {
|
||||
if (p.trackedClassDescriptor == descriptor) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
while ((p = p.parent) != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkOuterClass(DeclarationDescriptor descriptor) {
|
||||
if (outerClassDescriptor == null) {
|
||||
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
|
||||
if (containingDeclaration instanceof ClassDescriptor) {
|
||||
outerClassDescriptor = (ClassDescriptor) containingDeclaration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ClassDescriptor getOuterClassDescriptor() {
|
||||
if (outerClassDescriptor == null && parent != null) {
|
||||
UsageTracker p = parent;
|
||||
do {
|
||||
if (p.outerClassDescriptor != null) {
|
||||
return p.outerClassDescriptor;
|
||||
}
|
||||
}
|
||||
while ((p = p.parent) != null);
|
||||
}
|
||||
return outerClassDescriptor;
|
||||
}
|
||||
|
||||
public void forEachCaptured(Consumer<CallableDescriptor> consumer) {
|
||||
forEachCaptured(consumer, this, children == null ? null : new THashSet<CallableDescriptor>());
|
||||
}
|
||||
|
||||
private boolean isOneOfTheMyParentsIsAncestor(VariableDescriptor parameterDescriptor, UsageTracker requestor) {
|
||||
if (requestor == this) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FunctionDescriptor paramOwner = (FunctionDescriptor) parameterDescriptor.getContainingDeclaration();
|
||||
UsageTracker p = parent;
|
||||
do {
|
||||
if (p.memberDescriptor == paramOwner) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
while ((p = p.parent) != null && p != requestor);
|
||||
return false;
|
||||
}
|
||||
|
||||
private void forEachCaptured(Consumer<CallableDescriptor> consumer, UsageTracker requestor, @Nullable THashSet<CallableDescriptor> visited) {
|
||||
if (capturedVariables != null) {
|
||||
for (CallableDescriptor variable : capturedVariables) {
|
||||
if (variable instanceof VariableDescriptor && isOneOfTheMyParentsIsAncestor((VariableDescriptor) variable, requestor)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (visited == null || visited.add(variable)) {
|
||||
consumer.consume(variable);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (children != null) {
|
||||
for (UsageTracker child : children) {
|
||||
child.forEachCaptured(consumer, requestor, visited);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasCaptured() {
|
||||
if (capturedVariables != null) {
|
||||
assert !capturedVariables.isEmpty();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (children != null) {
|
||||
for (UsageTracker child : children) {
|
||||
if (child.hasCaptured()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// differs from DescriptorUtils - fails if reach NamespaceDescriptor
|
||||
private 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;
|
||||
}
|
||||
}
|
||||
|
||||
+26
-40
@@ -17,70 +17,56 @@
|
||||
package org.jetbrains.k2js.translate.expression;
|
||||
|
||||
import com.google.dart.compiler.backend.js.ast.*;
|
||||
import com.intellij.util.Consumer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
|
||||
import org.jetbrains.jet.lang.psi.JetElement;
|
||||
import org.jetbrains.k2js.translate.context.TranslationContext;
|
||||
import org.jetbrains.k2js.translate.utils.closure.ClosureContext;
|
||||
import org.jetbrains.k2js.translate.utils.closure.ClosureUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
abstract class InnerDeclarationTranslator {
|
||||
protected final ClosureContext closureContext;
|
||||
protected final TranslationContext context;
|
||||
protected final JsFunction fun;
|
||||
|
||||
public InnerDeclarationTranslator(@NotNull JetElement element,
|
||||
@NotNull DeclarationDescriptor descriptor,
|
||||
public InnerDeclarationTranslator(
|
||||
@NotNull TranslationContext context,
|
||||
@NotNull JsFunction fun) {
|
||||
@NotNull JsFunction fun
|
||||
) {
|
||||
this.context = context;
|
||||
closureContext = ClosureUtils.captureClosure(context.bindingContext(), element, descriptor);
|
||||
this.fun = fun;
|
||||
}
|
||||
|
||||
protected List<ValueParameterDescriptor> getValueParameters() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public JsExpression translate(@NotNull JsNameRef nameRef, @Nullable JsExpression self) {
|
||||
if (closureContext.getDescriptors().isEmpty() && self == JsLiteral.NULL) {
|
||||
boolean hasCaptured = context.usageTracker().hasCaptured();
|
||||
if (!hasCaptured && self == JsLiteral.NULL) {
|
||||
return createExpression(nameRef, self);
|
||||
}
|
||||
else {
|
||||
JsInvocation invocation = createInvocation(nameRef, self);
|
||||
addCapturedValueParameters(invocation);
|
||||
return invocation;
|
||||
|
||||
JsInvocation invocation = createInvocation(nameRef, self);
|
||||
if (hasCaptured) {
|
||||
final List<JsExpression> expressions = invocation.getArguments();
|
||||
context.usageTracker().forEachCaptured(new Consumer<CallableDescriptor>() {
|
||||
@Override
|
||||
public void consume(CallableDescriptor descriptor) {
|
||||
JsName name;
|
||||
if (descriptor instanceof VariableDescriptor) {
|
||||
name = context.getNameForDescriptor(descriptor);
|
||||
}
|
||||
else {
|
||||
name = ((JsNameRef) context.getAliasForDescriptor(descriptor)).getName();
|
||||
assert name != null;
|
||||
}
|
||||
fun.getParameters().add(new JsParameter(name));
|
||||
expressions.add(name.makeRef());
|
||||
}
|
||||
});
|
||||
}
|
||||
return invocation;
|
||||
}
|
||||
|
||||
protected abstract JsExpression createExpression(@NotNull JsNameRef nameRef, @Nullable JsExpression self);
|
||||
|
||||
protected abstract JsInvocation createInvocation(@NotNull JsNameRef nameRef, @Nullable JsExpression self);
|
||||
|
||||
private void addCapturedValueParameters(JsInvocation bind) {
|
||||
if (closureContext.getDescriptors().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<JsExpression> expressions = bind.getArguments();
|
||||
for (CallableDescriptor descriptor : closureContext.getDescriptors()) {
|
||||
JsName name;
|
||||
if (descriptor instanceof VariableDescriptor) {
|
||||
name = context.getNameForDescriptor(descriptor);
|
||||
}
|
||||
else {
|
||||
name = ((JsNameRef) context.getAliasForDescriptor(descriptor)).getName();
|
||||
assert name != null;
|
||||
}
|
||||
fun.getParameters().add(new JsParameter(name));
|
||||
expressions.add(name.makeRef());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-13
@@ -22,28 +22,20 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
|
||||
import org.jetbrains.jet.lang.psi.JetElement;
|
||||
import org.jetbrains.k2js.translate.context.TranslationContext;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class InnerFunctionTranslator extends InnerDeclarationTranslator {
|
||||
private final FunctionDescriptor descriptor;
|
||||
|
||||
public InnerFunctionTranslator(@NotNull JetElement element,
|
||||
public InnerFunctionTranslator(
|
||||
@NotNull FunctionDescriptor descriptor,
|
||||
@NotNull TranslationContext context,
|
||||
@NotNull JsFunction fun) {
|
||||
super(element, descriptor, context, fun);
|
||||
@NotNull JsFunction fun
|
||||
) {
|
||||
super(context, fun);
|
||||
this.descriptor = descriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ValueParameterDescriptor> getValueParameters() {
|
||||
return descriptor.getValueParameters();
|
||||
}
|
||||
|
||||
@SuppressWarnings("MethodOverloadsMethodOfSuperclass")
|
||||
@NotNull
|
||||
public JsExpression translate(@NotNull JsNameRef nameRef, @NotNull TranslationContext outerContext) {
|
||||
@@ -62,7 +54,8 @@ class InnerFunctionTranslator extends InnerDeclarationTranslator {
|
||||
|
||||
@NotNull
|
||||
private JsExpression getThis(TranslationContext outerContext) {
|
||||
ClassDescriptor outerClassDescriptor = closureContext.outerClassDescriptor;
|
||||
//noinspection ConstantConditions
|
||||
ClassDescriptor outerClassDescriptor = context.usageTracker().getOuterClassDescriptor();
|
||||
if (outerClassDescriptor != null && descriptor.getReceiverParameter() == null) {
|
||||
return outerContext.getThisObject(outerClassDescriptor);
|
||||
}
|
||||
|
||||
+5
-4
@@ -19,13 +19,14 @@ package org.jetbrains.k2js.translate.expression;
|
||||
import com.google.dart.compiler.backend.js.ast.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
|
||||
import org.jetbrains.jet.lang.psi.JetElement;
|
||||
import org.jetbrains.k2js.translate.context.TranslationContext;
|
||||
|
||||
class InnerObjectTranslator extends InnerDeclarationTranslator {
|
||||
public InnerObjectTranslator(@NotNull JetElement element, @NotNull ClassDescriptor descriptor, @NotNull TranslationContext context, @NotNull JsFunction fun) {
|
||||
super(element, descriptor, context, fun);
|
||||
public InnerObjectTranslator(
|
||||
@NotNull TranslationContext context,
|
||||
@NotNull JsFunction fun
|
||||
) {
|
||||
super(context, fun);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+7
-7
@@ -97,21 +97,20 @@ public class LiteralFunctionTranslator {
|
||||
if (receiverDescriptor == null) {
|
||||
aliasingContext = aliasingContext.inner(outerClass, new JsNameRef("o", fun.getName().makeRef()));
|
||||
}
|
||||
|
||||
funContext = rootContext.contextWithScope(fun, aliasingContext, new UsageTracker(outerClass));
|
||||
}
|
||||
else {
|
||||
outerClass = null;
|
||||
asInner = DescriptorUtils.isTopLevelDeclaration(descriptor);
|
||||
|
||||
funContext = rootContext.contextWithScope(fun, aliasingContext, outerContext.usageTracker());
|
||||
}
|
||||
|
||||
funContext = rootContext.contextWithScope(fun, aliasingContext,
|
||||
new UsageTracker(descriptor, outerContext.usageTracker(), outerClass));
|
||||
|
||||
fun.getBody().getStatements().addAll(translateFunctionBody(descriptor, declaration, funContext).getStatements());
|
||||
|
||||
InnerFunctionTranslator translator = null;
|
||||
if (!asInner) {
|
||||
translator = new InnerFunctionTranslator(declaration.asElement(), descriptor, funContext, fun);
|
||||
translator = new InnerFunctionTranslator(descriptor, funContext, fun);
|
||||
}
|
||||
|
||||
if (asInner) {
|
||||
@@ -161,12 +160,13 @@ public class LiteralFunctionTranslator {
|
||||
JsFunction fun = createFunction();
|
||||
JsNameRef outerClassRef = fun.getScope().declareName("$this").makeRef();
|
||||
TranslationContext funContext = rootContext
|
||||
.contextWithScope(fun, rootContext.aliasingContext().inner(outerClass, outerClassRef), new UsageTracker(outerClass));
|
||||
.contextWithScope(fun, rootContext.aliasingContext().inner(outerClass, outerClassRef), new UsageTracker(descriptor, null,
|
||||
outerClass));
|
||||
|
||||
fun.getBody().getStatements().add(new JsReturn(classTranslator.translate(funContext)));
|
||||
JetClassBody body = declaration.getBody();
|
||||
assert body != null;
|
||||
InnerObjectTranslator translator = new InnerObjectTranslator(body, descriptor, funContext, fun);
|
||||
InnerObjectTranslator translator = new InnerObjectTranslator(funContext, fun);
|
||||
return translator.translate(createReference(fun), funContext.usageTracker().isUsed() ? outerClassRef : null);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -51,15 +51,15 @@ public final class ReferenceTranslator {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JsExpression translateAsLocalNameReference(@NotNull DeclarationDescriptor referencedDescriptor,
|
||||
public static JsExpression translateAsLocalNameReference(@NotNull DeclarationDescriptor descriptor,
|
||||
@NotNull TranslationContext context) {
|
||||
if (referencedDescriptor instanceof FunctionDescriptor || referencedDescriptor instanceof VariableDescriptor) {
|
||||
JsExpression alias = context.aliasingContext().getAliasForDescriptor(referencedDescriptor);
|
||||
if (descriptor instanceof FunctionDescriptor || descriptor instanceof VariableDescriptor) {
|
||||
JsExpression alias = context.getAliasForDescriptor(descriptor);
|
||||
if (alias != null) {
|
||||
return alias;
|
||||
}
|
||||
}
|
||||
return context.getNameForDescriptor(referencedDescriptor).makeRef();
|
||||
return context.getNameForDescriptor(descriptor).makeRef();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
-144
@@ -1,144 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2013 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.k2js.translate.utils.closure;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.JetNodeTypes;
|
||||
import org.jetbrains.jet.lang.descriptors.*;
|
||||
import org.jetbrains.jet.lang.descriptors.impl.LocalVariableDescriptor;
|
||||
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;
|
||||
import org.jetbrains.k2js.translate.utils.JsDescriptorUtils;
|
||||
|
||||
class CaptureClosureVisitor extends JetTreeVisitor<ClosureContext> {
|
||||
@NotNull
|
||||
private final BindingContext bindingContext;
|
||||
@NotNull
|
||||
private final DeclarationDescriptor functionDescriptor;
|
||||
|
||||
/*package*/ CaptureClosureVisitor(@NotNull DeclarationDescriptor descriptor, @NotNull BindingContext bindingContext) {
|
||||
this.bindingContext = bindingContext;
|
||||
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) {
|
||||
if (expression.getNode().getElementType() == JetNodeTypes.OPERATION_REFERENCE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
DeclarationDescriptor descriptor = BindingUtils.getNullableDescriptorForReferenceExpression(bindingContext, expression);
|
||||
if (!(descriptor instanceof VariableDescriptor)) {
|
||||
if (descriptor instanceof SimpleFunctionDescriptor) {
|
||||
checkSimpleFunction(context, (SimpleFunctionDescriptor) descriptor);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
VariableDescriptor variableDescriptor = (VariableDescriptor) descriptor;
|
||||
if (captured(variableDescriptor, context)) {
|
||||
context.put(variableDescriptor);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void checkSimpleFunction(ClosureContext context, @Nullable SimpleFunctionDescriptor descriptor) {
|
||||
if (descriptor == null || descriptor.getName().isSpecial()) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkOuterClassDescriptor(descriptor, context);
|
||||
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
|
||||
// local named function
|
||||
if (!JsDescriptorUtils.isExtension(descriptor) && !(containingDeclaration instanceof ClassOrNamespaceDescriptor) && !isAncestor(functionDescriptor, descriptor)) {
|
||||
context.put(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
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 instanceof ValueParameterDescriptor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
PsiElement variableDeclaration = BindingContextUtils.descriptorToDeclaration(bindingContext, descriptor);
|
||||
if (variableDeclaration == null) {
|
||||
// "it" doesn't have declaration
|
||||
return false;
|
||||
}
|
||||
|
||||
return (!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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2013 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.k2js.translate.utils.closure;
|
||||
|
||||
import com.intellij.util.containers.OrderedSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
public final class ClosureContext {
|
||||
@NotNull
|
||||
private final Set<CallableDescriptor> descriptors = new OrderedSet<CallableDescriptor>();
|
||||
|
||||
@Nullable
|
||||
public ClassDescriptor outerClassDescriptor;
|
||||
|
||||
/*package*/ void put(@NotNull CallableDescriptor descriptor) {
|
||||
descriptors.add(descriptor);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Collection<CallableDescriptor> getDescriptors() {
|
||||
return descriptors;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2013 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
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.jet.lang.resolve.BindingContext;
|
||||
|
||||
public final class ClosureUtils {
|
||||
private ClosureUtils() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static ClosureContext captureClosure(@NotNull BindingContext bindingContext, @NotNull JetElement element, @NotNull DeclarationDescriptor descriptor) {
|
||||
CaptureClosureVisitor captureClosureVisitor = new CaptureClosureVisitor(descriptor, bindingContext);
|
||||
ClosureContext closureContext = new ClosureContext();
|
||||
element.acceptChildren(captureClosureVisitor, closureContext);
|
||||
return closureContext;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package foo
|
||||
|
||||
fun getLastFocused(callback:(window:Any?)->Unit) {
|
||||
callback(null)
|
||||
}
|
||||
|
||||
fun box(): Boolean {
|
||||
var result = "no"
|
||||
createTab(true) {
|
||||
result = it
|
||||
}
|
||||
return result == "yes"
|
||||
}
|
||||
|
||||
fun createTab(focusWindow:Boolean, callback:((String)->Unit)?) {
|
||||
getLastFocused {
|
||||
fun createTab() {
|
||||
if (focusWindow && callback != null) {
|
||||
callback("yes")
|
||||
}
|
||||
}
|
||||
|
||||
if (it == null) {
|
||||
createTab()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user