More cases supported for inline functions: namespace level functions with parameters (not blocks)

This commit is contained in:
Pavel V. Talanov
2012-03-28 16:38:59 +04:00
parent 8843bac989
commit 9b2dc6f584
12 changed files with 275 additions and 51 deletions
@@ -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);
}
@@ -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() {
@@ -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);
}
});
}
}
@@ -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
}
}