JS backend : change the capturing variables and rewrite some classes.

#KT-4236 Fixed
 #KT-4600 Fixed
 #KT-4237 Fixed
 #KT-4207 Fixed
 #KT-4218 Fixed
This commit is contained in:
Zalim Bashorov
2014-03-07 13:58:30 +04:00
parent 434a6ed6e2
commit a3cd1bbfda
13 changed files with 289 additions and 667 deletions
@@ -22,7 +22,7 @@ import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
import org.jetbrains.jet.lang.descriptors.CallableDescriptor
import org.jetbrains.k2js.translate.context.TranslationContext
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getDeclarationDescriptorForReceiver
import org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getReceiverParameterForReceiver
import org.jetbrains.jet.lang.resolve.calls.tasks.ExplicitReceiverKind.*
import org.jetbrains.k2js.translate.utils.AnnotationsUtils
import org.jetbrains.jet.lang.psi.JetSuperExpression
@@ -86,7 +86,7 @@ fun TranslationContext.getCallInfo(resolvedCall: ResolvedCall<out FunctionDescri
private fun TranslationContext.getThisObject(receiverValue: ReceiverValue): JsExpression {
assert(receiverValue.exists(), "receiverValue must be exist here")
return getThisObject(getDeclarationDescriptorForReceiver(receiverValue))
return getThisObject(getReceiverParameterForReceiver(receiverValue))
}
private fun TranslationContext.createCallInfo(resolvedCall: ResolvedCall<out CallableDescriptor>, explicitReceivers: ExplicitReceivers): CallInfo {
@@ -20,8 +20,7 @@ import com.google.dart.compiler.backend.js.ast.*;
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.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.name.FqName;
@@ -30,8 +29,8 @@ import org.jetbrains.k2js.translate.intrinsic.Intrinsics;
import java.util.HashMap;
import java.util.Map;
import static org.jetbrains.k2js.translate.context.ContextPackage.getNameForCapturedDescriptor;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForElement;
import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getExpectedReceiverDescriptor;
/**
* All the info about the state of the translation process.
@@ -108,13 +107,15 @@ public class TranslationContext {
}
@NotNull
public TranslationContext newFunctionBody(
@NotNull JsFunction fun,
@Nullable AliasingContext aliasingContext,
@Nullable UsageTracker usageTracker
) {
return new TranslationContext(this, fun, aliasingContext == null ? new AliasingContext(this.aliasingContext) : aliasingContext,
usageTracker);
public TranslationContext newFunctionBody(@NotNull JsFunction fun, @Nullable AliasingContext aliasingContext) {
return new TranslationContext(this, fun, aliasingContext == null ? this.aliasingContext.inner() : aliasingContext, null);
}
@NotNull
public TranslationContext newFunctionBodyWithUsageTracker(@NotNull JsFunction fun, @NotNull MemberDescriptor descriptor) {
DynamicContext dynamicContext = DynamicContext.newContext(fun.getScope(), fun.getBody());
UsageTracker usageTracker = new UsageTracker(this.usageTracker, descriptor, fun.getScope(), this.staticContext);
return new TranslationContext(this, this.staticContext, dynamicContext, this.aliasingContext.inner(), usageTracker, this.definitionPlace);
}
@NotNull
@@ -146,7 +147,7 @@ public class TranslationContext {
@NotNull
public JsBlock getBlockForDescriptor(@NotNull DeclarationDescriptor descriptor) {
if (descriptor instanceof CallableDescriptor) {
return getFunctionObject((CallableDescriptor)descriptor).getBody();
return getFunctionObject((CallableDescriptor) descriptor).getBody();
}
else {
return new JsBlock();
@@ -261,24 +262,17 @@ public class TranslationContext {
@Nullable
public JsExpression getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor) {
descriptorUsedInThisContext(descriptor);
JsNameRef nameRef = captureIfNeedAndGetCapturedName(descriptor);
if (nameRef != null) {
return nameRef;
}
return aliasingContext.getAliasForDescriptor(descriptor);
}
@NotNull
public JsExpression getThisObject(@NotNull DeclarationDescriptor descriptor) {
DeclarationDescriptor effectiveDescriptor;
if (descriptor instanceof CallableDescriptor) {
effectiveDescriptor = getExpectedReceiverDescriptor((CallableDescriptor) descriptor);
assert effectiveDescriptor != null;
}
else {
effectiveDescriptor = descriptor;
}
descriptorUsedInThisContext(effectiveDescriptor);
JsExpression alias = aliasingContext.getAliasForDescriptor(effectiveDescriptor);
public JsExpression getThisObject(@NotNull ReceiverParameterDescriptor descriptor) {
JsExpression alias = getAliasForDescriptor(descriptor);
return alias == null ? JsLiteral.THIS : alias;
}
@@ -290,13 +284,22 @@ public class TranslationContext {
throw new AssertionError("Can not find definition place from rootContext(definitionPlace and parent is null)");
}
@NotNull
public JsNameRef define(String name, JsExpression expression) {
return getDefinitionPlace().define(name, expression);
}
private void descriptorUsedInThisContext(DeclarationDescriptor effectiveDescriptor) {
if (usageTracker != null) {
usageTracker.triggerUsed(effectiveDescriptor);
@Nullable
private JsNameRef captureIfNeedAndGetCapturedName(DeclarationDescriptor descriptor) {
if (usageTracker != null && descriptor instanceof CallableDescriptor) {
CallableDescriptor callableDescriptor = (CallableDescriptor) descriptor;
usageTracker.used(callableDescriptor);
JsName name = getNameForCapturedDescriptor(usageTracker, callableDescriptor);
if (name != null) return name.makeRef();
}
return null;
}
}
@@ -1,197 +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.context;
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 List<UsageTracker> children;
private boolean used;
@Nullable
private Set<CallableDescriptor> capturedVariables;
private ClassDescriptor outerClassDescriptor;
public UsageTracker(@NotNull MemberDescriptor memberDescriptor, @Nullable UsageTracker parent, @Nullable ClassDescriptor trackedClassDescriptor) {
this.memberDescriptor = memberDescriptor;
this.trackedClassDescriptor = trackedClassDescriptor;
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(@NotNull DeclarationDescriptor descriptor) {
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) {
CallableDescriptor callableDescriptor = (CallableDescriptor) descriptor;
if (JsDescriptorUtils.isExtension(callableDescriptor)) {
return;
}
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
if (containingDeclaration instanceof ClassDescriptor) {
// skip methods defined in class, for example Int::plus
if (outerClassDescriptor == null && (callableDescriptor.getExpectedThisObject() == null || isAncestor(containingDeclaration, memberDescriptor))) {
outerClassDescriptor = (ClassDescriptor) containingDeclaration;
}
return;
}
// local named function
if (!(containingDeclaration instanceof ClassOrPackageFragmentDescriptor) &&
!isAncestor(memberDescriptor, descriptor)) {
addCapturedMember(callableDescriptor);
}
}
else if (descriptor instanceof ClassDescriptor && trackedClassDescriptor == descriptor) {
used = true;
}
}
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 || children == null) {
return outerClassDescriptor;
}
for (UsageTracker child : children) {
ClassDescriptor childOuterClassDescriptor = child.getOuterClassDescriptor();
if (childOuterClassDescriptor != null) {
return childOuterClassDescriptor;
}
}
return null;
}
public void forEachCaptured(Consumer<CallableDescriptor> consumer) {
forEachCaptured(consumer, memberDescriptor, children == null ? null : new THashSet<CallableDescriptor>());
}
private void forEachCaptured(Consumer<CallableDescriptor> consumer, MemberDescriptor requestorDescriptor, @Nullable THashSet<CallableDescriptor> visited) {
if (capturedVariables != null) {
for (CallableDescriptor callableDescriptor : capturedVariables) {
if (!isAncestor(requestorDescriptor, callableDescriptor) && (visited == null || visited.add(callableDescriptor))) {
consumer.consume(callableDescriptor);
}
}
}
if (children != null) {
for (UsageTracker child : children) {
child.forEachCaptured(consumer, requestorDescriptor, 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;
}
public boolean isCaptured(@NotNull CallableDescriptor descriptor) {
if (capturedVariables != null && capturedVariables.contains(descriptor)) {
return true;
}
if (children != null) {
for (UsageTracker child : children) {
if (child.isCaptured(descriptor)) {
return true;
}
}
}
return false;
}
// differs from DescriptorUtils - fails if reach PackageFragmentDescriptor
private static boolean isAncestor(
@NotNull DeclarationDescriptor ancestor,
@NotNull DeclarationDescriptor declarationDescriptor
) {
DeclarationDescriptor descriptor = declarationDescriptor.getContainingDeclaration();
while (descriptor != null && !(descriptor instanceof PackageFragmentDescriptor)) {
if (ancestor == descriptor) {
return true;
}
descriptor = descriptor.getContainingDeclaration();
}
return false;
}
}
@@ -0,0 +1,116 @@
/*
* Copyright 2010-2014 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.context
import org.jetbrains.jet.lang.descriptors.*
import org.jetbrains.jet.lang.resolve.DescriptorUtils.isAncestor
import com.google.dart.compiler.backend.js.ast.JsName
import com.google.dart.compiler.backend.js.ast.JsScope
private val CAPTURED_RECEIVER_NAME_PREFIX : String = "this$"
class UsageTracker(
private val parent: UsageTracker?,
val containingDescriptor: MemberDescriptor,
private val scope: JsScope,
private val staticContext: StaticContext
) {
private val captured = hashMapOf<CallableDescriptor, JsName>()
// For readonly access from external places.
val capturedDescriptorToJsName: Map<CallableDescriptor, JsName>
get() = captured
public fun used(descriptor: CallableDescriptor) {
if (isCaptured(descriptor)) return
// local named function
if (descriptor is FunctionDescriptor && descriptor.getVisibility() == Visibilities.LOCAL) {
assert(!descriptor.getName().isSpecial()) { "Function with special name can not be captured, descriptor: $descriptor" }
captureIfNeed(descriptor)
}
// local variable
else if (descriptor is VariableDescriptor && descriptor !is PropertyDescriptor) {
captureIfNeed(descriptor)
}
// this or receiver
else if (descriptor is ReceiverParameterDescriptor) {
captureIfNeed(descriptor)
}
}
private fun captureIfNeed(descriptor: CallableDescriptor?) {
if (descriptor == null || isCaptured(descriptor) || isAncestor(containingDescriptor, descriptor, strict = true)) return
parent?.captureIfNeed(descriptor)
captured[descriptor] = descriptor.getJsNameForCapturedDescriptor()
}
private fun CallableDescriptor.getJsNameForCapturedDescriptor(): JsName {
val suggestedName =
when (this) {
is ReceiverParameterDescriptor -> {
this.getNameForCapturedReceiver()
}
else -> {
// TODO: drop temporary HACK or add description
val name = staticContext.getNameForDescriptor(this)
name.getIdent()
}
}
return scope.declareFreshName(suggestedName)
}
}
public fun UsageTracker.getNameForCapturedDescriptor(descriptor: CallableDescriptor): JsName? = capturedDescriptorToJsName.get(descriptor)
public fun UsageTracker.hasCapturedExceptContaining(): Boolean {
val hasNotCaptured =
capturedDescriptorToJsName.isEmpty() ||
(capturedDescriptorToJsName.size == 1 && capturedDescriptorToJsName.containsKey(containingDescriptor))
return !hasNotCaptured
}
public fun UsageTracker.isCaptured(descriptor: CallableDescriptor): Boolean = capturedDescriptorToJsName.containsKey(descriptor)
// NOTE: don't use from other places to avoid name clashes! So, it is not in Namer.
private fun ReceiverParameterDescriptor.getNameForCapturedReceiver(): String {
fun DeclarationDescriptor.getNameForCapturedDescriptor(namePostfix: String = ""): String {
val name = this.getName()
val nameAsString = if (name.isSpecial()) "" else name.asString()
return CAPTURED_RECEIVER_NAME_PREFIX + nameAsString + namePostfix
}
val containingDeclaration = this.getContainingDeclaration()
assert(containingDeclaration is MemberDescriptor) {
"Unsupported descriptor type: ${containingDeclaration.getClass()}, " +
"receiverDescriptor = $this, " +"containingDeclaration = $containingDeclaration"
}
if (containingDeclaration is ClassDescriptor && containingDeclaration.getKind() == ClassKind.CLASS_OBJECT) {
return containingDeclaration.getContainingDeclaration().getNameForCapturedDescriptor(namePostfix = "$")
}
return containingDeclaration.getNameForCapturedDescriptor()
}
@@ -22,16 +22,18 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.ClassKind;
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
import org.jetbrains.jet.lang.psi.JetClassBody;
import org.jetbrains.jet.lang.psi.JetClassOrObject;
import org.jetbrains.jet.lang.psi.JetObjectDeclaration;
import org.jetbrains.jet.lang.psi.JetParameter;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypeConstructor;
import org.jetbrains.k2js.translate.context.*;
import org.jetbrains.k2js.translate.context.DefinitionPlace;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.expression.ExpressionPackage;
import org.jetbrains.k2js.translate.general.AbstractTranslator;
import org.jetbrains.k2js.translate.initializer.ClassInitializerTranslator;
import org.jetbrains.k2js.translate.utils.JsAstUtils;
import org.jetbrains.k2js.translate.utils.TranslationUtils;
import java.util.*;
@@ -43,7 +45,6 @@ import static org.jetbrains.k2js.translate.utils.BindingUtils.getPropertyDescrip
import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getContainingClass;
import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getSupertypesWithoutFakes;
import static org.jetbrains.k2js.translate.utils.PsiUtils.getPrimaryConstructorParameters;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.getSuggestedName;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.simpleReturnFunction;
/**
@@ -79,7 +80,7 @@ public final class ClassTranslator extends AbstractTranslator {
return translate(context());
}
return translateObjectInsideClass(containingClass, context());
return translateObjectInsideClass(context());
}
@NotNull
@@ -217,19 +218,13 @@ public final class ClassTranslator extends AbstractTranslator {
}
@NotNull
private JsExpression translateObjectInsideClass(@NotNull ClassDescriptor outerClass, @NotNull TranslationContext outerClassContext) {
private JsExpression translateObjectInsideClass(@NotNull TranslationContext outerClassContext) {
JsFunction fun = new JsFunction(outerClassContext.scope(), new JsBlock());
JsNameRef outerClassRef = fun.getScope().declareName(Namer.OUTER_CLASS_NAME).makeRef();
UsageTracker usageTracker = new UsageTracker(descriptor, outerClassContext.usageTracker(), outerClass);
AliasingContext aliasingContext = outerClassContext.aliasingContext().inner(outerClass, outerClassRef);
TranslationContext funContext = outerClassContext.newFunctionBody(fun, aliasingContext, usageTracker);
TranslationContext funContext = outerClassContext.newFunctionBodyWithUsageTracker(fun, descriptor);
fun.getBody().getStatements().add(new JsReturn(translate(funContext)));
JetClassBody body = classDeclaration.getBody();
assert body != null;
JsNameRef define = funContext.define(getSuggestedName(funContext, descriptor), fun);
return new InnerObjectTranslator(funContext, fun).translate(define, usageTracker.isUsed() ? outerClassRef : null);
String suggestedName = TranslationUtils.getSuggestedName(funContext, descriptor);
return ExpressionPackage.withCapturedParameters(fun, funContext, outerClassContext, suggestedName);
}
}
@@ -21,8 +21,8 @@ import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.JetNodeTypes;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
@@ -56,6 +56,7 @@ import static org.jetbrains.k2js.translate.reference.ReferenceTranslator.transla
import static org.jetbrains.k2js.translate.utils.BindingUtils.*;
import static org.jetbrains.k2js.translate.utils.ErrorReportingUtils.message;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.*;
import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getReceiverParameterForDeclaration;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateInitializerForProperty;
import static org.jetbrains.k2js.translate.utils.mutator.LastExpressionMutator.mutateLastExpression;
@@ -403,13 +404,18 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
@Override
@NotNull
public JsNode visitFunctionLiteralExpression(@NotNull JetFunctionLiteralExpression expression, @NotNull TranslationContext context) {
return LiteralFunctionTranslator.translate(expression.getFunctionLiteral(), context);
return new LiteralFunctionTranslator(context).translate(expression.getFunctionLiteral());
}
@Override
@NotNull
public JsNode visitNamedFunction(@NotNull JetNamedFunction expression, @NotNull TranslationContext context) {
return LiteralFunctionTranslator.translateLocalNamedFunction(expression, context).source(expression);
JsExpression alias = new LiteralFunctionTranslator(context).translate(expression);
FunctionDescriptor descriptor = getFunctionDescriptor(context.bindingContext(), expression);
JsName name = context.getNameForDescriptor(descriptor);
context.aliasingContext().registerAlias(descriptor, name.makeRef());
return new JsVars(new JsVars.JsVar(name, alias)).source(expression);
}
@Override
@@ -418,7 +424,8 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
DeclarationDescriptor thisExpression =
getDescriptorForReferenceExpression(context.bindingContext(), expression.getInstanceReference());
assert thisExpression != null : "This expression must reference a descriptor: " + expression.getText();
return context.getThisObject(thisExpression).source(expression);
return context.getThisObject(getReceiverParameterForDeclaration(thisExpression)).source(expression);
}
@Override
@@ -1,80 +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.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.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.context.UsageTracker;
import java.util.List;
abstract class InnerDeclarationTranslator {
protected final TranslationContext context;
protected final JsFunction fun;
public InnerDeclarationTranslator(
@NotNull TranslationContext context,
@NotNull JsFunction fun
) {
this.context = context;
this.fun = fun;
}
@NotNull
public JsExpression translate(@NotNull JsNameRef nameRef, @Nullable JsExpression self) {
UsageTracker usageTracker = context.usageTracker();
assert usageTracker != null : "Usage tracker should not be null for InnerDeclarationTranslator";
boolean hasCaptured = usageTracker.hasCaptured();
if (!hasCaptured && self == JsLiteral.NULL) {
return createExpression(nameRef, self);
}
JsInvocation invocation = createInvocation(nameRef, self);
if (hasCaptured) {
final List<JsExpression> invocationArguments = invocation.getArguments();
usageTracker.forEachCaptured(new Consumer<CallableDescriptor>() {
@Override
public void consume(CallableDescriptor descriptor) {
fun.getParameters().add(new JsParameter(getParameterNameFor(descriptor)));
invocationArguments.add(getParameterNameRefFor(descriptor));
}
});
}
return invocation;
}
@NotNull
protected JsName getParameterNameFor(@NotNull CallableDescriptor descriptor) {
return context.getNameForDescriptor(descriptor);
}
@NotNull
protected JsNameRef getParameterNameRefFor(@NotNull CallableDescriptor descriptor) {
return getParameterNameFor(descriptor).makeRef();
}
@NotNull
protected abstract JsExpression createExpression(@NotNull JsNameRef nameRef, @Nullable JsExpression self);
@NotNull
protected abstract JsInvocation createInvocation(@NotNull JsNameRef nameRef, @Nullable JsExpression self);
}
@@ -1,82 +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.expression;
import com.google.dart.compiler.backend.js.ast.*;
import com.intellij.util.SmartList;
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.FunctionDescriptor;
import org.jetbrains.k2js.translate.context.TranslationContext;
class InnerFunctionTranslator extends InnerDeclarationTranslator {
@NotNull private final FunctionDescriptor descriptor;
@Nullable private final JsNameRef funRef;
public InnerFunctionTranslator(
@NotNull FunctionDescriptor descriptor,
@NotNull TranslationContext context,
@NotNull JsFunction fun,
@Nullable JsNameRef funRef
) {
super(context, fun);
this.descriptor = descriptor;
this.funRef = funRef;
}
@SuppressWarnings("MethodOverloadsMethodOfSuperclass")
@NotNull
public JsExpression translate(@NotNull JsNameRef nameRef, @NotNull TranslationContext outerContext) {
return translate(nameRef, getThis(outerContext));
}
@Override
@NotNull
protected JsNameRef getParameterNameRefFor(@NotNull CallableDescriptor descriptor) {
if (descriptor == this.descriptor) {
assert funRef != null;
return funRef;
}
return super.getParameterNameRefFor(descriptor);
}
@Override
@NotNull
protected JsExpression createExpression(@NotNull JsNameRef nameRef, @Nullable JsExpression self) {
return nameRef;
}
@Override
@NotNull
protected JsInvocation createInvocation(@NotNull JsNameRef nameRef, @Nullable JsExpression self) {
return new JsInvocation(new JsNameRef("bind", nameRef), new SmartList<JsExpression>(self));
}
@NotNull
private JsExpression getThis(TranslationContext outerContext) {
//noinspection ConstantConditions
ClassDescriptor outerClassDescriptor = context.usageTracker().getOuterClassDescriptor();
if (outerClassDescriptor != null && descriptor.getReceiverParameter() == null) {
return outerContext.getThisObject(outerClassDescriptor);
}
return JsLiteral.NULL;
}
}
@@ -1,48 +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.expression;
import com.google.dart.compiler.backend.js.ast.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.k2js.translate.context.TranslationContext;
public class InnerObjectTranslator extends InnerDeclarationTranslator {
public InnerObjectTranslator(
@NotNull TranslationContext context,
@NotNull JsFunction fun
) {
super(context, fun);
}
@Override
@NotNull
protected JsExpression createExpression(@NotNull JsNameRef nameRef, @Nullable JsExpression self) {
return createInvocation(nameRef, self);
}
@Override
@NotNull
protected JsInvocation createInvocation(@NotNull JsNameRef nameRef, @Nullable JsExpression self) {
JsInvocation invocation = new JsInvocation(nameRef);
if (self != null) {
fun.getParameters().add(new JsParameter(((JsNameRef) self).getName()));
invocation.getArguments().add(JsLiteral.THIS);
}
return invocation;
}
}
@@ -1,204 +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.expression;
import com.google.dart.compiler.backend.js.ast.*;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.psi.JetDeclarationWithBody;
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.k2js.translate.context.AliasingContext;
import org.jetbrains.k2js.translate.context.Namer;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.context.UsageTracker;
import org.jetbrains.k2js.translate.general.AbstractTranslator;
import org.jetbrains.k2js.translate.utils.JsAstUtils;
import java.util.List;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptor;
import static org.jetbrains.k2js.translate.utils.FunctionBodyTranslator.translateFunctionBody;
import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getExpectedReceiverDescriptor;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.getSuggestedName;
public class LiteralFunctionTranslator extends AbstractTranslator {
private static final String CAPTURED_VALUE_FIELD = "v";
private final JetDeclarationWithBody declaration;
private final FunctionDescriptor descriptor;
private final JsFunction jsFunction;
private final TranslationContext functionContext;
private final boolean inConstructorOrTopLevel;
private final ClassDescriptor outerClass;
private final JsName receiverName;
private JsNameRef tempRef = null;
private LiteralFunctionTranslator(@NotNull JetDeclarationWithBody declaration, @NotNull TranslationContext context) {
super(context);
this.declaration = declaration;
this.descriptor = getFunctionDescriptor(context().bindingContext(), declaration);
DeclarationDescriptor receiverDescriptor = getExpectedReceiverDescriptor(descriptor);
jsFunction = new JsFunction(context().scope(), new JsBlock());
AliasingContext aliasingContext;
if (receiverDescriptor == null) {
receiverName = null;
aliasingContext = null;
}
else {
receiverName = jsFunction.getScope().declareName(Namer.getReceiverParameterName());
aliasingContext = context().aliasingContext().inner(receiverDescriptor, receiverName.makeRef());
}
if (descriptor.getContainingDeclaration() instanceof ConstructorDescriptor) {
// KT-2388
inConstructorOrTopLevel = true;
jsFunction.setName(jsFunction.getScope().declareName(Namer.CALLEE_NAME));
outerClass = (ClassDescriptor) descriptor.getContainingDeclaration().getContainingDeclaration();
assert outerClass != null;
if (aliasingContext == null) {
aliasingContext = context().aliasingContext();
}
aliasingContext = aliasingContext.notShareableThisAliased(outerClass, new JsNameRef("o", jsFunction.getName().makeRef()));
}
else {
outerClass = null;
inConstructorOrTopLevel = DescriptorUtils.isTopLevelDeclaration(descriptor);
}
UsageTracker funTracker = new UsageTracker(descriptor, context().usageTracker(), outerClass);
functionContext = context().newFunctionBody(jsFunction, aliasingContext, funTracker);
}
private void translateBody() {
JsBlock functionBody = translateFunctionBody(descriptor, declaration, functionContext);
jsFunction.getBody().getStatements().addAll(functionBody.getStatements());
}
@NotNull
private JsExpression finish() {
JsExpression result;
if (inConstructorOrTopLevel) {
result = jsFunction;
if (outerClass != null) {
UsageTracker usageTracker = functionContext.usageTracker();
assert usageTracker != null;
if (usageTracker.isUsed()) {
result = new JsInvocation(context().namer().kotlin("assignOwner"), jsFunction, JsLiteral.THIS);
}
else {
jsFunction.setName(null);
}
}
}
else {
JsNameRef funReference = context().define(getSuggestedName(functionContext, descriptor), jsFunction);
InnerFunctionTranslator innerTranslator = new InnerFunctionTranslator(descriptor, functionContext, jsFunction, tempRef);
result = innerTranslator.translate(funReference, context());
}
addRegularParameters(descriptor, jsFunction, functionContext, receiverName);
return result;
}
@NotNull
private JsExpression translate() {
translateBody();
return finish();
}
@NotNull
public JsVars translateLocalNamedFunction() {
// Add ability to capture this named function.
// Will be available like `foo.v` (for function `foo`)
// Can not generate direct call because function may have some closures.
JsName funName = functionContext.getNameForDescriptor(descriptor);
JsNameRef alias = new JsNameRef(CAPTURED_VALUE_FIELD, funName.makeRef());
functionContext.aliasingContext().registerAlias(descriptor, alias);
translateBody();
UsageTracker funTracker = functionContext.usageTracker();
assert funTracker != null;
boolean funIsCaptured = funTracker.isCaptured(descriptor);
// Create temporary variable name which will be contain reference to the function.
JsName temp;
if (funIsCaptured) {
assert !inConstructorOrTopLevel : "A recursive closure in constructor is unsupported.";
// Use `context()` because it should be created in the scope which contain call.
temp = context().scope().declareTemporary();
tempRef = temp.makeRef();
}
else {
temp = null;
}
JsExpression result = finish();
List<JsVars.JsVar> vars = new SmartList<JsVars.JsVar>();
if (funIsCaptured) {
JsVars.JsVar tempVar = new JsVars.JsVar(temp, new JsObjectLiteral());
vars.add(tempVar);
// Save `result` to the field of temporary variable if the function is captured.
result = JsAstUtils.assignment(new JsNameRef(CAPTURED_VALUE_FIELD, temp.makeRef()), result);
}
JsVars.JsVar fun = new JsVars.JsVar(funName, result);
vars.add(fun);
return new JsVars(vars, /*mulitline =*/ false);
}
private static void addRegularParameters(
@NotNull FunctionDescriptor descriptor,
@NotNull JsFunction fun,
@NotNull TranslationContext funContext,
@Nullable JsName receiverName
) {
if (receiverName != null) {
fun.getParameters().add(new JsParameter(receiverName));
}
FunctionTranslator.addParameters(fun.getParameters(), descriptor, funContext);
}
@NotNull
public static JsVars translateLocalNamedFunction(@NotNull JetDeclarationWithBody declaration, @NotNull TranslationContext outerContext) {
return new LiteralFunctionTranslator(declaration, outerContext).translateLocalNamedFunction();
}
@NotNull
public static JsExpression translate(@NotNull JetDeclarationWithBody declaration, @NotNull TranslationContext outerContext) {
return new LiteralFunctionTranslator(declaration, outerContext).translate();
}
}
@@ -0,0 +1,95 @@
/*
* Copyright 2010-2014 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.expression
import com.google.dart.compiler.backend.js.ast.*
import org.jetbrains.jet.lang.descriptors.CallableDescriptor
import org.jetbrains.jet.lang.descriptors.ReceiverParameterDescriptor
import org.jetbrains.jet.lang.psi.JetDeclarationWithBody
import org.jetbrains.k2js.translate.context.*
import org.jetbrains.k2js.translate.general.AbstractTranslator
import org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptor
import org.jetbrains.k2js.translate.utils.FunctionBodyTranslator.translateFunctionBody
import org.jetbrains.k2js.translate.utils.TranslationUtils.getSuggestedName
import org.jetbrains.k2js.translate.utils.TranslationUtils.simpleReturnFunction
class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslator(context) {
fun translate(declaration: JetDeclarationWithBody): JsExpression {
val invokingContext = context()
val descriptor = getFunctionDescriptor(invokingContext.bindingContext(), declaration)
val lambda = invokingContext.getFunctionObject(descriptor)
val functionContext = invokingContext.newFunctionBodyWithUsageTracker(lambda, descriptor)
val receiverDescriptor = descriptor.getReceiverParameter()
if (receiverDescriptor != null) {
val receiverName = lambda.getScope()?.declareName(Namer.getReceiverParameterName())
lambda.getParameters()?.add(JsParameter(receiverName))
functionContext.aliasingContext().registerAlias(receiverDescriptor, receiverName!!.makeRef())
}
FunctionTranslator.addParameters(lambda.getParameters(), descriptor, functionContext)
val functionBody = translateFunctionBody(descriptor, declaration, functionContext)
lambda.getBody()?.getStatements()?.addAll(functionBody.getStatements()!!)
val tracker = functionContext.usageTracker()!!
val isRecursive = tracker.isCaptured(descriptor)
if (isRecursive) {
lambda.setName(tracker.getNameForCapturedDescriptor(descriptor))
}
val suggestedName = getSuggestedName(functionContext, descriptor)
if (tracker.hasCapturedExceptContaining()) {
val lambdaCreator = simpleReturnFunction(invokingContext.scope(), lambda)
return lambdaCreator.withCapturedParameters(functionContext, invokingContext, suggestedName)
}
return invokingContext.define(suggestedName, lambda)
}
}
fun JsFunction.withCapturedParameters(context: TranslationContext, invokingContext: TranslationContext, suggestedName: String): JsExpression {
fun getParameterNameRefForInvocation(callableDescriptor: CallableDescriptor): JsExpression {
val alias = invokingContext.getAliasForDescriptor(callableDescriptor)
if (alias != null) return alias
if (callableDescriptor is ReceiverParameterDescriptor) return JsLiteral.THIS
return invokingContext.getNameForDescriptor(callableDescriptor).makeRef()
}
val ref = invokingContext.define(suggestedName, this)
val invocation = JsInvocation(ref)
val invocationArguments = invocation.getArguments()!!
val functionParameters = this.getParameters()!!
val tracker = context.usageTracker()!!
for ((descriptor, name) in tracker.capturedDescriptorToJsName) {
if (descriptor == tracker.containingDescriptor) continue
functionParameters.add(JsParameter(name))
invocationArguments.add(getParameterNameRefForInvocation(descriptor))
}
return invocation
}
@@ -103,18 +103,34 @@ public final class JsDescriptorUtils {
}
@NotNull
public static DeclarationDescriptor getDeclarationDescriptorForReceiver(@NotNull ReceiverValue receiverParameter) {
DeclarationDescriptor declarationDescriptor;
public static ReceiverParameterDescriptor getReceiverParameterForReceiver(@NotNull ReceiverValue receiverParameter) {
DeclarationDescriptor declarationDescriptor = getDeclarationDescriptorForReceiver(receiverParameter);
return getReceiverParameterForDeclaration(declarationDescriptor);
}
@NotNull
private static DeclarationDescriptor getDeclarationDescriptorForReceiver(@NotNull ReceiverValue receiverParameter) {
if (receiverParameter instanceof ThisReceiver) {
declarationDescriptor = ((ThisReceiver) receiverParameter).getDeclarationDescriptor();
}
else {
throw new UnsupportedOperationException("Unsupported receiver type: " + receiverParameter.getClass() +
", receiverParameter = " + receiverParameter);
DeclarationDescriptor declarationDescriptor = ((ThisReceiver) receiverParameter).getDeclarationDescriptor();
return declarationDescriptor.getOriginal();
}
return declarationDescriptor.getOriginal();
throw new UnsupportedOperationException("Unsupported receiver type: " + receiverParameter.getClass() +
", receiverParameter = " + receiverParameter);
}
public static ReceiverParameterDescriptor getReceiverParameterForDeclaration(DeclarationDescriptor declarationDescriptor) {
if (declarationDescriptor instanceof ClassDescriptor) {
return ((ClassDescriptor) declarationDescriptor).getThisAsReceiverParameter();
}
else if (declarationDescriptor instanceof CallableMemberDescriptor) {
ReceiverParameterDescriptor receiverDescriptor = ((CallableMemberDescriptor) declarationDescriptor).getReceiverParameter();
assert receiverDescriptor != null;
return receiverDescriptor;
}
throw new UnsupportedOperationException("Unsupported declaration type: " + declarationDescriptor.getClass() +
", declarationDescriptor = " + declarationDescriptor);
}
//TODO: maybe we have similar routine
@@ -411,6 +411,7 @@ public final class TranslationUtils {
return ensureNotNull;
}
@NotNull
public static String getSuggestedName(TranslationContext context, DeclarationDescriptor descriptor) {
String suggestedName = "";
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();