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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+11
-4
@@ -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
|
||||
|
||||
-80
@@ -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);
|
||||
}
|
||||
-82
@@ -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;
|
||||
}
|
||||
}
|
||||
-48
@@ -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;
|
||||
}
|
||||
}
|
||||
-204
@@ -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();
|
||||
}
|
||||
}
|
||||
+95
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user