diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/callTranslator/CallInfo.kt b/js/js.translator/src/org/jetbrains/k2js/translate/callTranslator/CallInfo.kt index 18f590baacd..c13cfcf0724 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/callTranslator/CallInfo.kt +++ b/js/js.translator/src/org/jetbrains/k2js/translate/callTranslator/CallInfo.kt @@ -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, explicitReceivers: ExplicitReceivers): CallInfo { diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/context/TranslationContext.java b/js/js.translator/src/org/jetbrains/k2js/translate/context/TranslationContext.java index 5774683c278..0d66dca48c1 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/context/TranslationContext.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/context/TranslationContext.java @@ -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; } } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/context/UsageTracker.java b/js/js.translator/src/org/jetbrains/k2js/translate/context/UsageTracker.java deleted file mode 100644 index 425389725d2..00000000000 --- a/js/js.translator/src/org/jetbrains/k2js/translate/context/UsageTracker.java +++ /dev/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 children; - - private boolean used; - @Nullable - private Set 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(); - } - children.add(child); - } - - private void addCapturedMember(CallableDescriptor descriptor) { - if (capturedVariables == null) { - capturedVariables = new OrderedSet(); - } - 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 consumer) { - forEachCaptured(consumer, memberDescriptor, children == null ? null : new THashSet()); - } - - private void forEachCaptured(Consumer consumer, MemberDescriptor requestorDescriptor, @Nullable THashSet 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; - } -} diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/context/UsageTracker.kt b/js/js.translator/src/org/jetbrains/k2js/translate/context/UsageTracker.kt new file mode 100644 index 00000000000..b5dc6d4b416 --- /dev/null +++ b/js/js.translator/src/org/jetbrains/k2js/translate/context/UsageTracker.kt @@ -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() + + // For readonly access from external places. + val capturedDescriptorToJsName: Map + 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() +} \ No newline at end of file diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/declaration/ClassTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/declaration/ClassTranslator.java index b938b543529..aedb7a0eb30 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/declaration/ClassTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/declaration/ClassTranslator.java @@ -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); } } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java b/js/js.translator/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java index 42a6baee971..a1afebc969e 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java @@ -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 { @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 { 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 diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/expression/InnerDeclarationTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/expression/InnerDeclarationTranslator.java deleted file mode 100644 index 1b6cd8b691f..00000000000 --- a/js/js.translator/src/org/jetbrains/k2js/translate/expression/InnerDeclarationTranslator.java +++ /dev/null @@ -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 invocationArguments = invocation.getArguments(); - usageTracker.forEachCaptured(new Consumer() { - @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); -} diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/expression/InnerFunctionTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/expression/InnerFunctionTranslator.java deleted file mode 100644 index 95ac97b687b..00000000000 --- a/js/js.translator/src/org/jetbrains/k2js/translate/expression/InnerFunctionTranslator.java +++ /dev/null @@ -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(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; - } -} diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/expression/InnerObjectTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/expression/InnerObjectTranslator.java deleted file mode 100644 index 1f27582c3ca..00000000000 --- a/js/js.translator/src/org/jetbrains/k2js/translate/expression/InnerObjectTranslator.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2010-2013 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jetbrains.k2js.translate.expression; - -import com.google.dart.compiler.backend.js.ast.*; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.k2js.translate.context.TranslationContext; - -public class InnerObjectTranslator extends InnerDeclarationTranslator { - public InnerObjectTranslator( - @NotNull TranslationContext context, - @NotNull JsFunction fun - ) { - super(context, fun); - } - - @Override - @NotNull - protected JsExpression createExpression(@NotNull JsNameRef nameRef, @Nullable JsExpression self) { - return createInvocation(nameRef, self); - } - - @Override - @NotNull - protected JsInvocation createInvocation(@NotNull JsNameRef nameRef, @Nullable JsExpression self) { - JsInvocation invocation = new JsInvocation(nameRef); - if (self != null) { - fun.getParameters().add(new JsParameter(((JsNameRef) self).getName())); - invocation.getArguments().add(JsLiteral.THIS); - } - return invocation; - } -} diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/expression/LiteralFunctionTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/expression/LiteralFunctionTranslator.java deleted file mode 100644 index ed9aaf69234..00000000000 --- a/js/js.translator/src/org/jetbrains/k2js/translate/expression/LiteralFunctionTranslator.java +++ /dev/null @@ -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 vars = new SmartList(); - - 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(); - } -} diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/expression/LiteralFunctionTranslator.kt b/js/js.translator/src/org/jetbrains/k2js/translate/expression/LiteralFunctionTranslator.kt new file mode 100644 index 00000000000..c0784c0d7c6 --- /dev/null +++ b/js/js.translator/src/org/jetbrains/k2js/translate/expression/LiteralFunctionTranslator.kt @@ -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 +} \ No newline at end of file diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/JsDescriptorUtils.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/JsDescriptorUtils.java index 56f95cb52c7..37d172abaf5 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/JsDescriptorUtils.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/JsDescriptorUtils.java @@ -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 diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java index bbbfb2132bf..34c53ca772a 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java @@ -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();