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.jet.lang.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.k2js.translate.context.TranslationContext
|
import org.jetbrains.k2js.translate.context.TranslationContext
|
||||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue
|
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.jet.lang.resolve.calls.tasks.ExplicitReceiverKind.*
|
||||||
import org.jetbrains.k2js.translate.utils.AnnotationsUtils
|
import org.jetbrains.k2js.translate.utils.AnnotationsUtils
|
||||||
import org.jetbrains.jet.lang.psi.JetSuperExpression
|
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 {
|
private fun TranslationContext.getThisObject(receiverValue: ReceiverValue): JsExpression {
|
||||||
assert(receiverValue.exists(), "receiverValue must be exist here")
|
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 {
|
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 com.intellij.psi.PsiElement;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
|
import org.jetbrains.jet.lang.descriptors.*;
|
||||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
|
||||||
import org.jetbrains.jet.lang.psi.JetExpression;
|
import org.jetbrains.jet.lang.psi.JetExpression;
|
||||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||||
import org.jetbrains.jet.lang.resolve.name.FqName;
|
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.HashMap;
|
||||||
import java.util.Map;
|
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.BindingUtils.getDescriptorForElement;
|
||||||
import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getExpectedReceiverDescriptor;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All the info about the state of the translation process.
|
* All the info about the state of the translation process.
|
||||||
@@ -108,13 +107,15 @@ public class TranslationContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public TranslationContext newFunctionBody(
|
public TranslationContext newFunctionBody(@NotNull JsFunction fun, @Nullable AliasingContext aliasingContext) {
|
||||||
@NotNull JsFunction fun,
|
return new TranslationContext(this, fun, aliasingContext == null ? this.aliasingContext.inner() : aliasingContext, null);
|
||||||
@Nullable AliasingContext aliasingContext,
|
}
|
||||||
@Nullable UsageTracker usageTracker
|
|
||||||
) {
|
@NotNull
|
||||||
return new TranslationContext(this, fun, aliasingContext == null ? new AliasingContext(this.aliasingContext) : aliasingContext,
|
public TranslationContext newFunctionBodyWithUsageTracker(@NotNull JsFunction fun, @NotNull MemberDescriptor descriptor) {
|
||||||
usageTracker);
|
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
|
@NotNull
|
||||||
@@ -146,7 +147,7 @@ public class TranslationContext {
|
|||||||
@NotNull
|
@NotNull
|
||||||
public JsBlock getBlockForDescriptor(@NotNull DeclarationDescriptor descriptor) {
|
public JsBlock getBlockForDescriptor(@NotNull DeclarationDescriptor descriptor) {
|
||||||
if (descriptor instanceof CallableDescriptor) {
|
if (descriptor instanceof CallableDescriptor) {
|
||||||
return getFunctionObject((CallableDescriptor)descriptor).getBody();
|
return getFunctionObject((CallableDescriptor) descriptor).getBody();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return new JsBlock();
|
return new JsBlock();
|
||||||
@@ -261,24 +262,17 @@ public class TranslationContext {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public JsExpression getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor) {
|
public JsExpression getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor) {
|
||||||
descriptorUsedInThisContext(descriptor);
|
JsNameRef nameRef = captureIfNeedAndGetCapturedName(descriptor);
|
||||||
|
if (nameRef != null) {
|
||||||
|
return nameRef;
|
||||||
|
}
|
||||||
|
|
||||||
return aliasingContext.getAliasForDescriptor(descriptor);
|
return aliasingContext.getAliasForDescriptor(descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public JsExpression getThisObject(@NotNull DeclarationDescriptor descriptor) {
|
public JsExpression getThisObject(@NotNull ReceiverParameterDescriptor descriptor) {
|
||||||
DeclarationDescriptor effectiveDescriptor;
|
JsExpression alias = getAliasForDescriptor(descriptor);
|
||||||
if (descriptor instanceof CallableDescriptor) {
|
|
||||||
effectiveDescriptor = getExpectedReceiverDescriptor((CallableDescriptor) descriptor);
|
|
||||||
assert effectiveDescriptor != null;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
effectiveDescriptor = descriptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
descriptorUsedInThisContext(effectiveDescriptor);
|
|
||||||
|
|
||||||
JsExpression alias = aliasingContext.getAliasForDescriptor(effectiveDescriptor);
|
|
||||||
return alias == null ? JsLiteral.THIS : alias;
|
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)");
|
throw new AssertionError("Can not find definition place from rootContext(definitionPlace and parent is null)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
public JsNameRef define(String name, JsExpression expression) {
|
public JsNameRef define(String name, JsExpression expression) {
|
||||||
return getDefinitionPlace().define(name, expression);
|
return getDefinitionPlace().define(name, expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void descriptorUsedInThisContext(DeclarationDescriptor effectiveDescriptor) {
|
@Nullable
|
||||||
if (usageTracker != null) {
|
private JsNameRef captureIfNeedAndGetCapturedName(DeclarationDescriptor descriptor) {
|
||||||
usageTracker.triggerUsed(effectiveDescriptor);
|
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.ClassDescriptor;
|
||||||
import org.jetbrains.jet.lang.descriptors.ClassKind;
|
import org.jetbrains.jet.lang.descriptors.ClassKind;
|
||||||
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
|
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.JetClassOrObject;
|
||||||
import org.jetbrains.jet.lang.psi.JetObjectDeclaration;
|
import org.jetbrains.jet.lang.psi.JetObjectDeclaration;
|
||||||
import org.jetbrains.jet.lang.psi.JetParameter;
|
import org.jetbrains.jet.lang.psi.JetParameter;
|
||||||
import org.jetbrains.jet.lang.types.JetType;
|
import org.jetbrains.jet.lang.types.JetType;
|
||||||
import org.jetbrains.jet.lang.types.TypeConstructor;
|
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.general.AbstractTranslator;
|
||||||
import org.jetbrains.k2js.translate.initializer.ClassInitializerTranslator;
|
import org.jetbrains.k2js.translate.initializer.ClassInitializerTranslator;
|
||||||
import org.jetbrains.k2js.translate.utils.JsAstUtils;
|
import org.jetbrains.k2js.translate.utils.JsAstUtils;
|
||||||
|
import org.jetbrains.k2js.translate.utils.TranslationUtils;
|
||||||
|
|
||||||
import java.util.*;
|
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.getContainingClass;
|
||||||
import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getSupertypesWithoutFakes;
|
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.PsiUtils.getPrimaryConstructorParameters;
|
||||||
import static org.jetbrains.k2js.translate.utils.TranslationUtils.getSuggestedName;
|
|
||||||
import static org.jetbrains.k2js.translate.utils.TranslationUtils.simpleReturnFunction;
|
import static org.jetbrains.k2js.translate.utils.TranslationUtils.simpleReturnFunction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -79,7 +80,7 @@ public final class ClassTranslator extends AbstractTranslator {
|
|||||||
return translate(context());
|
return translate(context());
|
||||||
}
|
}
|
||||||
|
|
||||||
return translateObjectInsideClass(containingClass, context());
|
return translateObjectInsideClass(context());
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -217,19 +218,13 @@ public final class ClassTranslator extends AbstractTranslator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private JsExpression translateObjectInsideClass(@NotNull ClassDescriptor outerClass, @NotNull TranslationContext outerClassContext) {
|
private JsExpression translateObjectInsideClass(@NotNull TranslationContext outerClassContext) {
|
||||||
JsFunction fun = new JsFunction(outerClassContext.scope(), new JsBlock());
|
JsFunction fun = new JsFunction(outerClassContext.scope(), new JsBlock());
|
||||||
JsNameRef outerClassRef = fun.getScope().declareName(Namer.OUTER_CLASS_NAME).makeRef();
|
TranslationContext funContext = outerClassContext.newFunctionBodyWithUsageTracker(fun, descriptor);
|
||||||
UsageTracker usageTracker = new UsageTracker(descriptor, outerClassContext.usageTracker(), outerClass);
|
|
||||||
AliasingContext aliasingContext = outerClassContext.aliasingContext().inner(outerClass, outerClassRef);
|
|
||||||
TranslationContext funContext = outerClassContext.newFunctionBody(fun, aliasingContext, usageTracker);
|
|
||||||
|
|
||||||
fun.getBody().getStatements().add(new JsReturn(translate(funContext)));
|
fun.getBody().getStatements().add(new JsReturn(translate(funContext)));
|
||||||
|
|
||||||
JetClassBody body = classDeclaration.getBody();
|
String suggestedName = TranslationUtils.getSuggestedName(funContext, descriptor);
|
||||||
assert body != null;
|
return ExpressionPackage.withCapturedParameters(fun, funContext, outerClassContext, suggestedName);
|
||||||
|
|
||||||
JsNameRef define = funContext.define(getSuggestedName(funContext, descriptor), fun);
|
|
||||||
return new InnerObjectTranslator(funContext, fun).translate(define, usageTracker.isUsed() ? outerClassRef : null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-4
@@ -21,8 +21,8 @@ import com.intellij.psi.tree.IElementType;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.jet.JetNodeTypes;
|
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.DeclarationDescriptor;
|
||||||
|
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
|
||||||
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
|
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
|
||||||
import org.jetbrains.jet.lang.psi.*;
|
import org.jetbrains.jet.lang.psi.*;
|
||||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
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.BindingUtils.*;
|
||||||
import static org.jetbrains.k2js.translate.utils.ErrorReportingUtils.message;
|
import static org.jetbrains.k2js.translate.utils.ErrorReportingUtils.message;
|
||||||
import static org.jetbrains.k2js.translate.utils.JsAstUtils.*;
|
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.TranslationUtils.translateInitializerForProperty;
|
||||||
import static org.jetbrains.k2js.translate.utils.mutator.LastExpressionMutator.mutateLastExpression;
|
import static org.jetbrains.k2js.translate.utils.mutator.LastExpressionMutator.mutateLastExpression;
|
||||||
|
|
||||||
@@ -403,13 +404,18 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
|
|||||||
@Override
|
@Override
|
||||||
@NotNull
|
@NotNull
|
||||||
public JsNode visitFunctionLiteralExpression(@NotNull JetFunctionLiteralExpression expression, @NotNull TranslationContext context) {
|
public JsNode visitFunctionLiteralExpression(@NotNull JetFunctionLiteralExpression expression, @NotNull TranslationContext context) {
|
||||||
return LiteralFunctionTranslator.translate(expression.getFunctionLiteral(), context);
|
return new LiteralFunctionTranslator(context).translate(expression.getFunctionLiteral());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@NotNull
|
@NotNull
|
||||||
public JsNode visitNamedFunction(@NotNull JetNamedFunction expression, @NotNull TranslationContext context) {
|
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
|
@Override
|
||||||
@@ -418,7 +424,8 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
|
|||||||
DeclarationDescriptor thisExpression =
|
DeclarationDescriptor thisExpression =
|
||||||
getDescriptorForReferenceExpression(context.bindingContext(), expression.getInstanceReference());
|
getDescriptorForReferenceExpression(context.bindingContext(), expression.getInstanceReference());
|
||||||
assert thisExpression != null : "This expression must reference a descriptor: " + expression.getText();
|
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
|
@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
|
@NotNull
|
||||||
public static DeclarationDescriptor getDeclarationDescriptorForReceiver(@NotNull ReceiverValue receiverParameter) {
|
public static ReceiverParameterDescriptor getReceiverParameterForReceiver(@NotNull ReceiverValue receiverParameter) {
|
||||||
DeclarationDescriptor declarationDescriptor;
|
DeclarationDescriptor declarationDescriptor = getDeclarationDescriptorForReceiver(receiverParameter);
|
||||||
|
return getReceiverParameterForDeclaration(declarationDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static DeclarationDescriptor getDeclarationDescriptorForReceiver(@NotNull ReceiverValue receiverParameter) {
|
||||||
if (receiverParameter instanceof ThisReceiver) {
|
if (receiverParameter instanceof ThisReceiver) {
|
||||||
declarationDescriptor = ((ThisReceiver) receiverParameter).getDeclarationDescriptor();
|
DeclarationDescriptor declarationDescriptor = ((ThisReceiver) receiverParameter).getDeclarationDescriptor();
|
||||||
}
|
return declarationDescriptor.getOriginal();
|
||||||
else {
|
|
||||||
throw new UnsupportedOperationException("Unsupported receiver type: " + receiverParameter.getClass() +
|
|
||||||
", receiverParameter = " + receiverParameter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
//TODO: maybe we have similar routine
|
||||||
|
|||||||
@@ -411,6 +411,7 @@ public final class TranslationUtils {
|
|||||||
return ensureNotNull;
|
return ensureNotNull;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
public static String getSuggestedName(TranslationContext context, DeclarationDescriptor descriptor) {
|
public static String getSuggestedName(TranslationContext context, DeclarationDescriptor descriptor) {
|
||||||
String suggestedName = "";
|
String suggestedName = "";
|
||||||
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
|
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
|
||||||
|
|||||||
Reference in New Issue
Block a user