More cases supported for inline functions: namespace level functions with parameters (not blocks)
This commit is contained in:
@@ -31,11 +31,34 @@ public final class InlineTest extends SingleFileTranslationTest {
|
||||
}
|
||||
|
||||
public void testFunctionWithoutParameters() throws Exception {
|
||||
String filename = "functionWithoutParameters.kt";
|
||||
checkFooBoxIsTrueAndFunctionNameIsNotReferenced("functionWithoutParameters.kt", "myInlineFun");
|
||||
}
|
||||
|
||||
public void testFunctionWithBlockBody() throws Exception {
|
||||
checkFooBoxIsTrueAndFunctionNameIsNotReferenced("functionWithBlockBody.kt", "myInlineFun");
|
||||
}
|
||||
|
||||
public void testFunctionWithOneParameter() throws Exception {
|
||||
checkFooBoxIsTrueAndFunctionNameIsNotReferenced("functionWithOneParameter.kt", "myInlineFun");
|
||||
}
|
||||
|
||||
public void testFunctionWithTwoParameters() throws Exception {
|
||||
checkFooBoxIsTrueAndFunctionNameIsNotReferenced("functionWithTwoParameters.kt", "myInlineFun");
|
||||
}
|
||||
|
||||
public void testMethod() throws Exception {
|
||||
checkFooBoxIsTrueAndFunctionNameIsNotReferenced("method.kt", "myInlineMethod");
|
||||
}
|
||||
|
||||
public void testMethodWithReferenceToThis() throws Exception {
|
||||
checkFooBoxIsTrueAndFunctionNameIsNotReferenced("methodWithReferenceToThis.kt", "myInlineMethod");
|
||||
}
|
||||
|
||||
private void checkFooBoxIsTrueAndFunctionNameIsNotReferenced(@NotNull String filename, String funName) throws Exception {
|
||||
checkFooBoxIsTrue(filename);
|
||||
String generatedJSFilePath = getOutputFilePath(filename);
|
||||
String outputFileText = FileUtil.loadFile(new File(generatedJSFilePath));
|
||||
assertTrue(countOccurrences(outputFileText, "myInlineFun") == 1);
|
||||
assertTrue(countOccurrences(outputFileText, funName) == 1);
|
||||
}
|
||||
|
||||
private static int countOccurrences(@NotNull String str, @NotNull String subStr) {
|
||||
|
||||
@@ -131,6 +131,10 @@ public final class TranslationContext {
|
||||
|
||||
@NotNull
|
||||
public JsName getNameForDescriptor(@NotNull DeclarationDescriptor descriptor) {
|
||||
JsName alias = aliasingContext.getAliasForDescriptor(descriptor);
|
||||
if (alias != null) {
|
||||
return alias;
|
||||
}
|
||||
return staticContext.getNameForDescriptor(descriptor);
|
||||
}
|
||||
|
||||
|
||||
+8
-34
@@ -35,6 +35,7 @@ import org.jetbrains.k2js.translate.general.AbstractTranslator;
|
||||
import org.jetbrains.k2js.translate.general.Translation;
|
||||
import org.jetbrains.k2js.translate.reference.ReferenceTranslator;
|
||||
import org.jetbrains.k2js.translate.utils.DescriptorUtils;
|
||||
import org.jetbrains.k2js.translate.utils.FunctionBodyTranslator;
|
||||
import org.jetbrains.k2js.translate.utils.TranslationUtils;
|
||||
import org.jetbrains.k2js.translate.utils.closure.ClosureContext;
|
||||
import org.jetbrains.k2js.translate.utils.closure.ClosureUtils;
|
||||
@@ -45,6 +46,7 @@ import java.util.List;
|
||||
|
||||
import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptor;
|
||||
import static org.jetbrains.k2js.translate.utils.DescriptorUtils.*;
|
||||
import static org.jetbrains.k2js.translate.utils.FunctionBodyTranslator.translateFunctionBody;
|
||||
import static org.jetbrains.k2js.translate.utils.JsAstUtils.convertToBlock;
|
||||
import static org.jetbrains.k2js.translate.utils.JsAstUtils.setParameters;
|
||||
import static org.jetbrains.k2js.translate.utils.mutator.LastExpressionMutator.mutateLastExpression;
|
||||
@@ -64,7 +66,7 @@ public final class FunctionTranslator extends AbstractTranslator {
|
||||
@NotNull
|
||||
private final TranslationContext functionBodyContext;
|
||||
@Nullable
|
||||
private TemporaryVariable aliasForContaingClassThis = null;
|
||||
private TemporaryVariable aliasForContainingClassThis = null;
|
||||
@NotNull
|
||||
private final JetDeclarationWithBody functionDeclaration;
|
||||
@Nullable
|
||||
@@ -106,8 +108,8 @@ public final class FunctionTranslator extends AbstractTranslator {
|
||||
return getContextWithFunctionBodyBlock();
|
||||
}
|
||||
JsExpression thisQualifier = TranslationUtils.getThisObject(context(), containingClass);
|
||||
aliasForContaingClassThis = context().declareTemporary(thisQualifier);
|
||||
return getContextWithFunctionBodyBlock().innerContextWithThisAliased(containingClass, aliasForContaingClassThis.name());
|
||||
aliasForContainingClassThis = context().declareTemporary(thisQualifier);
|
||||
return getContextWithFunctionBodyBlock().innerContextWithThisAliased(containingClass, aliasForContainingClassThis.name());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -152,8 +154,8 @@ public final class FunctionTranslator extends AbstractTranslator {
|
||||
if (!shouldAliasThisObject()) {
|
||||
return functionObject;
|
||||
}
|
||||
assert aliasForContaingClassThis != null;
|
||||
return AstUtil.newSequence(aliasForContaingClassThis.assignmentExpression(), functionObject);
|
||||
assert aliasForContainingClassThis != null;
|
||||
return AstUtil.newSequence(aliasForContainingClassThis.assignmentExpression(), functionObject);
|
||||
}
|
||||
|
||||
private boolean shouldAliasThisObject() {
|
||||
@@ -197,37 +199,9 @@ public final class FunctionTranslator extends AbstractTranslator {
|
||||
assert descriptor.getModality().equals(Modality.ABSTRACT);
|
||||
return;
|
||||
}
|
||||
JsNode realBody = Translation.translateExpression(jetBodyExpression, functionBodyContext);
|
||||
functionBody.getStatements().add(mayBeWrapWithReturn(realBody));
|
||||
functionBody.getStatements().add(translateFunctionBody(descriptor, functionDeclaration, functionBodyContext));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private JsBlock mayBeWrapWithReturn(@NotNull JsNode body) {
|
||||
if (!mustAddReturnToGeneratedFunctionBody()) {
|
||||
return convertToBlock(body);
|
||||
}
|
||||
return convertToBlock(lastExpressionReturned(body));
|
||||
}
|
||||
|
||||
private boolean mustAddReturnToGeneratedFunctionBody() {
|
||||
JetType functionReturnType = descriptor.getReturnType();
|
||||
assert functionReturnType != null : "Function return typed type must be resolved.";
|
||||
return (!functionDeclaration.hasBlockBody()) && (!JetStandardClasses.isUnit(functionReturnType));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static JsNode lastExpressionReturned(@NotNull JsNode body) {
|
||||
return mutateLastExpression(body, new Mutator() {
|
||||
@Override
|
||||
@NotNull
|
||||
public JsNode mutate(@NotNull JsNode node) {
|
||||
if (!(node instanceof JsExpression)) {
|
||||
return node;
|
||||
}
|
||||
return new JsReturn((JsExpression)node);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<JsParameter> translateParameters() {
|
||||
|
||||
+75
-15
@@ -19,25 +19,28 @@ package org.jetbrains.k2js.translate.reference;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.dart.compiler.backend.js.ast.JsExpression;
|
||||
import com.google.dart.compiler.backend.js.ast.JsName;
|
||||
import com.google.dart.compiler.backend.js.ast.JsNode;
|
||||
import com.google.dart.compiler.backend.js.ast.JsReturn;
|
||||
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.SimpleFunctionDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.*;
|
||||
import org.jetbrains.jet.lang.psi.JetCallExpression;
|
||||
import org.jetbrains.jet.lang.psi.JetFunction;
|
||||
import org.jetbrains.jet.lang.resolve.calls.ResolvedCall;
|
||||
import org.jetbrains.jet.lang.resolve.calls.ResolvedValueArgument;
|
||||
import org.jetbrains.k2js.translate.context.TemporaryVariable;
|
||||
import org.jetbrains.k2js.translate.context.TranslationContext;
|
||||
import org.jetbrains.k2js.translate.general.Translation;
|
||||
import org.jetbrains.k2js.translate.utils.BindingUtils;
|
||||
import org.jetbrains.k2js.translate.utils.DescriptorUtils;
|
||||
import org.jetbrains.k2js.translate.utils.JsAstUtils;
|
||||
import org.jetbrains.k2js.translate.utils.mutator.LastExpressionMutator;
|
||||
import org.jetbrains.k2js.translate.utils.mutator.Mutator;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionForDescriptor;
|
||||
import static org.jetbrains.k2js.translate.utils.BindingUtils.getResolvedCallForCallExpression;
|
||||
import static org.jetbrains.k2js.translate.utils.FunctionBodyTranslator.translateFunctionBody;
|
||||
|
||||
/**
|
||||
* @author Pavel Talanov
|
||||
@@ -68,9 +71,18 @@ public final class InlinedCallExpressionTranslator extends AbstractCallExpressio
|
||||
|
||||
@NotNull
|
||||
private JsExpression translate() {
|
||||
TranslationContext contextWithAllParametersAliased = createContextWithAllParametersAliased();
|
||||
JetFunction function = BindingUtils.getFunctionForDescriptor(bindingContext(), getFunctionDescriptor());
|
||||
return Translation.translateAsExpression(function.getBodyExpression(), contextWithAllParametersAliased);
|
||||
TranslationContext contextWithAllParametersAliased = createContextForInlining();
|
||||
JsNode translatedBody = translateFunctionBody(getFunctionDescriptor(), getFunctionBody(), contextWithAllParametersAliased);
|
||||
//TODO: declare uninitialized temporary
|
||||
TemporaryVariable temporaryVariable = contextWithAllParametersAliased.declareTemporary(program().getNullLiteral());
|
||||
JsNode mutatedBody = LastExpressionMutator.mutateLastExpression(translatedBody, new InlineFunctionMutator(temporaryVariable));
|
||||
context().addStatementToCurrentBlock(JsAstUtils.convertToBlock(mutatedBody));
|
||||
return temporaryVariable.reference();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private JetFunction getFunctionBody() {
|
||||
return getFunctionForDescriptor(bindingContext(), getFunctionDescriptor());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -80,16 +92,44 @@ public final class InlinedCallExpressionTranslator extends AbstractCallExpressio
|
||||
return (SimpleFunctionDescriptor)descriptor;
|
||||
}
|
||||
|
||||
private TranslationContext createContextWithAllParametersAliased() {
|
||||
@NotNull
|
||||
private TranslationContext createContextForInlining() {
|
||||
TranslationContext contextForInlining = context();
|
||||
contextForInlining = createContextWithAliasForThisExpression(contextForInlining);
|
||||
return createContextWithAliasesForParameters(contextForInlining);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private TranslationContext createContextWithAliasesForParameters(@NotNull TranslationContext contextForInlining) {
|
||||
Map<DeclarationDescriptor, JsName> aliases = Maps.newHashMap();
|
||||
for (ValueParameterDescriptor parameterDescriptor : resolvedCall.getResultingDescriptor().getValueParameters()) {
|
||||
ResolvedValueArgument actualArgument = resolvedCall.getValueArgumentsByIndex().get(parameterDescriptor.getIndex());
|
||||
JsExpression translatedArgument = translateArgument(parameterDescriptor, actualArgument);
|
||||
TemporaryVariable aliasForArgument = context().declareTemporary(translatedArgument);
|
||||
TemporaryVariable aliasForArgument = createAliasForArgument(parameterDescriptor);
|
||||
aliases.put(parameterDescriptor, aliasForArgument.name());
|
||||
context().addStatementToCurrentBlock(aliasForArgument.assignmentExpression().makeStmt());
|
||||
}
|
||||
return context().innerContextWithDescriptorsAliased(aliases);
|
||||
return contextForInlining.innerContextWithDescriptorsAliased(aliases);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private TranslationContext createContextWithAliasForThisExpression(@NotNull TranslationContext contextForInlining) {
|
||||
ClassDescriptor classDescriptorForMethod = DescriptorUtils.getClassDescriptorForMethod(getFunctionDescriptor());
|
||||
if (classDescriptorForMethod == null) {
|
||||
return contextForInlining;
|
||||
}
|
||||
//TODO
|
||||
TranslationContext contextWithAliasForThisExpression = contextForInlining;
|
||||
TemporaryVariable aliasForThis = contextForInlining.declareTemporary(receiver);
|
||||
contextWithAliasForThisExpression = contextForInlining.innerContextWithThisAliased(classDescriptorForMethod, aliasForThis.name());
|
||||
contextWithAliasForThisExpression.addStatementToCurrentBlock(aliasForThis.assignmentExpression().makeStmt());
|
||||
return contextWithAliasForThisExpression;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private TemporaryVariable createAliasForArgument(@NotNull ValueParameterDescriptor parameterDescriptor) {
|
||||
ResolvedValueArgument actualArgument = resolvedCall.getValueArgumentsByIndex().get(parameterDescriptor.getIndex());
|
||||
JsExpression translatedArgument = translateArgument(parameterDescriptor, actualArgument);
|
||||
TemporaryVariable aliasForArgument = context().declareTemporary(translatedArgument);
|
||||
context().addStatementToCurrentBlock(aliasForArgument.assignmentExpression().makeStmt());
|
||||
return aliasForArgument;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -104,4 +144,24 @@ public final class InlinedCallExpressionTranslator extends AbstractCallExpressio
|
||||
public boolean shouldWrapVarargInArray() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static final class InlineFunctionMutator implements Mutator {
|
||||
|
||||
@NotNull
|
||||
private final TemporaryVariable toAssignTo;
|
||||
|
||||
private InlineFunctionMutator(@NotNull TemporaryVariable to) {
|
||||
toAssignTo = to;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public JsNode mutate(@NotNull JsNode node) {
|
||||
if (node instanceof JsReturn) {
|
||||
JsExpression returnedExpression = ((JsReturn)node).getExpr();
|
||||
return JsAstUtils.assignment(toAssignTo.name().makeRef(), returnedExpression);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,4 +288,17 @@ public final class DescriptorUtils {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static ClassDescriptor getClassDescriptorForMethod(@NotNull SimpleFunctionDescriptor functionDescriptor) {
|
||||
if (!functionDescriptor.getExpectedThisObject().exists()) {
|
||||
return null;
|
||||
}
|
||||
ClassifierDescriptor descriptor =
|
||||
functionDescriptor.getExpectedThisObject().getType().getConstructor().getDeclarationDescriptor();
|
||||
if (descriptor instanceof ClassDescriptor) {
|
||||
return (ClassDescriptor)descriptor.getOriginal();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2010-2012 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.k2js.translate.utils;
|
||||
|
||||
import com.google.dart.compiler.backend.js.ast.JsBlock;
|
||||
import com.google.dart.compiler.backend.js.ast.JsExpression;
|
||||
import com.google.dart.compiler.backend.js.ast.JsNode;
|
||||
import com.google.dart.compiler.backend.js.ast.JsReturn;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
|
||||
import org.jetbrains.jet.lang.psi.JetDeclarationWithBody;
|
||||
import org.jetbrains.jet.lang.psi.JetExpression;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.lang.types.lang.JetStandardClasses;
|
||||
import org.jetbrains.k2js.translate.context.TranslationContext;
|
||||
import org.jetbrains.k2js.translate.general.AbstractTranslator;
|
||||
import org.jetbrains.k2js.translate.general.Translation;
|
||||
import org.jetbrains.k2js.translate.utils.mutator.Mutator;
|
||||
|
||||
import static org.jetbrains.k2js.translate.utils.JsAstUtils.convertToBlock;
|
||||
import static org.jetbrains.k2js.translate.utils.mutator.LastExpressionMutator.mutateLastExpression;
|
||||
|
||||
/**
|
||||
* @author Pavel Talanov
|
||||
*/
|
||||
public final class FunctionBodyTranslator extends AbstractTranslator {
|
||||
|
||||
@NotNull
|
||||
public static JsBlock translateFunctionBody(@NotNull FunctionDescriptor descriptor,
|
||||
@NotNull JetDeclarationWithBody declarationWithBody,
|
||||
@NotNull TranslationContext functionBodyContext) {
|
||||
return (new FunctionBodyTranslator(descriptor, declarationWithBody, functionBodyContext)).translate();
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
private final FunctionDescriptor descriptor;
|
||||
@NotNull
|
||||
private final JetDeclarationWithBody declaration;
|
||||
|
||||
private FunctionBodyTranslator(@NotNull FunctionDescriptor descriptor,
|
||||
@NotNull JetDeclarationWithBody declaration,
|
||||
@NotNull TranslationContext context) {
|
||||
super(context);
|
||||
this.descriptor = descriptor;
|
||||
this.declaration = declaration;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private JsBlock translate() {
|
||||
JetExpression jetBodyExpression = declaration.getBodyExpression();
|
||||
assert jetBodyExpression != null : "Cannot translate a body of an abstract function.";
|
||||
return mayBeWrapWithReturn(Translation.translateExpression(jetBodyExpression, context()));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private JsBlock mayBeWrapWithReturn(@NotNull JsNode body) {
|
||||
if (!mustAddReturnToGeneratedFunctionBody()) {
|
||||
return convertToBlock(body);
|
||||
}
|
||||
return convertToBlock(lastExpressionReturned(body));
|
||||
}
|
||||
|
||||
private boolean mustAddReturnToGeneratedFunctionBody() {
|
||||
JetType functionReturnType = descriptor.getReturnType();
|
||||
assert functionReturnType != null : "Function return typed type must be resolved.";
|
||||
return (!declaration.hasBlockBody()) && (!JetStandardClasses.isUnit(functionReturnType));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static JsNode lastExpressionReturned(@NotNull JsNode body) {
|
||||
return mutateLastExpression(body, new Mutator() {
|
||||
@Override
|
||||
@NotNull
|
||||
public JsNode mutate(@NotNull JsNode node) {
|
||||
if (!(node instanceof JsExpression)) {
|
||||
return node;
|
||||
}
|
||||
return new JsReturn((JsExpression)node);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
+2
@@ -39,6 +39,8 @@ public final class LastExpressionMutator {
|
||||
this.mutator = mutator;
|
||||
}
|
||||
|
||||
//TODO: visitor?
|
||||
//TODO: when expression?
|
||||
@NotNull
|
||||
private JsNode apply(@NotNull JsNode node) {
|
||||
if (node instanceof JsBlock) {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package foo
|
||||
|
||||
fun box() = !myInlineFun()
|
||||
|
||||
inline fun myInlineFun() : Boolean {
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package foo
|
||||
|
||||
fun box() : Boolean {
|
||||
val t = myInlineFun(1 == 2)
|
||||
val f = myInlineFun(1 == 2)
|
||||
return t and f
|
||||
}
|
||||
|
||||
inline fun myInlineFun(t : Boolean) = !t
|
||||
@@ -0,0 +1,13 @@
|
||||
package foo
|
||||
|
||||
fun box() : Boolean {
|
||||
if (myInlineFun(3, 2) != 1) {
|
||||
return false;
|
||||
}
|
||||
if (myInlineFun(100, -100) != 200) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline fun myInlineFun(l : Int, r : Int) = l - r
|
||||
@@ -0,0 +1,11 @@
|
||||
package foo
|
||||
|
||||
fun box() : Boolean {
|
||||
return (!(A().myInlineMethod()))
|
||||
}
|
||||
|
||||
class A() {
|
||||
inline fun myInlineMethod() : Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package foo
|
||||
|
||||
fun box() : Boolean {
|
||||
return !(A(false).myInlineMethod()) and A(true).myInlineMethod()
|
||||
}
|
||||
|
||||
class A(val a : Boolean) {
|
||||
inline fun myInlineMethod() : Boolean {
|
||||
return a
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user