First stab at inline functions.

This commit is contained in:
Pavel V. Talanov
2012-03-28 15:08:46 +04:00
parent 0a42abc7c9
commit 8843bac989
8 changed files with 305 additions and 63 deletions
@@ -0,0 +1,50 @@
/*
* 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.test.semantics;
import com.intellij.openapi.util.io.FileUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.k2js.test.SingleFileTranslationTest;
import java.io.File;
/**
* @author Pavel Talanov
*/
public final class InlineTest extends SingleFileTranslationTest {
public InlineTest() {
super("inline/");
}
public void testFunctionWithoutParameters() throws Exception {
String filename = "functionWithoutParameters.kt";
checkFooBoxIsTrue(filename);
String generatedJSFilePath = getOutputFilePath(filename);
String outputFileText = FileUtil.loadFile(new File(generatedJSFilePath));
assertTrue(countOccurrences(outputFileText, "myInlineFun") == 1);
}
private static int countOccurrences(@NotNull String str, @NotNull String subStr) {
int count = 0;
String s = str;
while (s.contains(subStr)) {
s = s.replaceFirst(subStr, "");
count++;
}
return count;
}
}
@@ -79,6 +79,13 @@ public class AliasingContext {
return newContext;
}
@NotNull
public AliasingContext withDescriptorsAliased(@NotNull Map<DeclarationDescriptor, JsName> aliases) {
AliasingContext newContext = new AliasingContext(this);
newContext.aliasesForDescriptors.putAll(aliases);
return newContext;
}
@NotNull
private AliasingContext getParent() {
assert parent != null;
@@ -93,6 +93,11 @@ public final class TranslationContext {
return new TranslationContext(staticContext, dynamicContext, aliasingContext.withAliasesForExpressions(aliases));
}
@NotNull
public TranslationContext innerContextWithDescriptorsAliased(@NotNull Map<DeclarationDescriptor, JsName> aliases) {
return new TranslationContext(staticContext, dynamicContext, aliasingContext.withDescriptorsAliased(aliases));
}
@NotNull
public JsBlock getBlockForDescriptor(@NotNull DeclarationDescriptor descriptor) {
if (descriptor instanceof CallableDescriptor) {
@@ -0,0 +1,103 @@
/*
* 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.reference;
import com.google.common.collect.Lists;
import com.google.dart.compiler.backend.js.ast.JsArrayLiteral;
import com.google.dart.compiler.backend.js.ast.JsExpression;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.psi.JetCallExpression;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.ValueArgument;
import org.jetbrains.jet.lang.resolve.calls.*;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.AbstractTranslator;
import org.jetbrains.k2js.translate.general.Translation;
import java.util.Arrays;
import java.util.List;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getDefaultArgument;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getResolvedCallForCallExpression;
/**
* @author Pavel Talanov
*/
public abstract class AbstractCallExpressionTranslator extends AbstractTranslator {
@NotNull
protected final JetCallExpression expression;
@NotNull
protected final ResolvedCall<?> resolvedCall;
@Nullable
protected final JsExpression receiver;
@NotNull
protected final CallType callType;
protected AbstractCallExpressionTranslator(@NotNull JetCallExpression expression,
@Nullable JsExpression receiver,
@NotNull CallType type, @NotNull TranslationContext context) {
super(context);
this.expression = expression;
this.resolvedCall = getResolvedCallForCallExpression(bindingContext(), expression);
this.receiver = receiver;
callType = type;
}
abstract public boolean shouldWrapVarargInArray();
@NotNull
protected List<JsExpression> translateSingleArgument(@NotNull ResolvedValueArgument actualArgument,
@NotNull ValueParameterDescriptor parameterDescriptor) {
List<ValueArgument> valueArguments = actualArgument.getArguments();
if (actualArgument instanceof VarargValueArgument) {
return translateVarargArgument(valueArguments);
}
if (actualArgument instanceof DefaultValueArgument) {
JetExpression defaultArgument = getDefaultArgument(bindingContext(), parameterDescriptor);
return Arrays.asList(Translation.translateAsExpression(defaultArgument, context()));
}
assert actualArgument instanceof ExpressionValueArgument;
assert valueArguments.size() == 1;
JetExpression argumentExpression = valueArguments.get(0).getArgumentExpression();
assert argumentExpression != null;
return Arrays.asList(Translation.translateAsExpression(argumentExpression, context()));
}
@NotNull
private List<JsExpression> translateVarargArgument(@NotNull List<ValueArgument> arguments) {
List<JsExpression> translatedArgs = Lists.newArrayList();
for (ValueArgument argument : arguments) {
JetExpression argumentExpression = argument.getArgumentExpression();
assert argumentExpression != null;
translatedArgs.add(Translation.translateAsExpression(argumentExpression, context()));
}
if (shouldWrapVarargInArray()) {
return wrapInArrayLiteral(translatedArgs);
}
return translatedArgs;
}
@NotNull
private static List<JsExpression> wrapInArrayLiteral(@NotNull List<JsExpression> translatedArgs) {
JsArrayLiteral argsWrappedInArray = new JsArrayLiteral();
argsWrappedInArray.getExpressions().addAll(translatedArgs);
return Arrays.<JsExpression>asList(argsWrappedInArray);
}
}
@@ -16,8 +16,6 @@
package org.jetbrains.k2js.translate.reference;
import com.google.common.collect.Lists;
import com.google.dart.compiler.backend.js.ast.JsArrayLiteral;
import com.google.dart.compiler.backend.js.ast.JsExpression;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -27,61 +25,51 @@ import org.jetbrains.jet.lang.descriptors.VariableAsFunctionDescriptor;
import org.jetbrains.jet.lang.psi.JetCallExpression;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
import org.jetbrains.jet.lang.psi.ValueArgument;
import org.jetbrains.jet.lang.resolve.calls.*;
import org.jetbrains.jet.lang.resolve.calls.ExpressionAsFunctionDescriptor;
import org.jetbrains.jet.lang.resolve.calls.ResolvedValueArgument;
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.AnnotationsUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getDefaultArgument;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getResolvedCallForCallExpression;
import static org.jetbrains.k2js.translate.utils.PsiUtils.getCallee;
/**
* @author Pavel Talanov
*/
public final class CallExpressionTranslator extends AbstractTranslator {
public final class CallExpressionTranslator extends AbstractCallExpressionTranslator {
@NotNull
public static JsExpression translate(@NotNull JetCallExpression expression,
@Nullable JsExpression receiver,
@NotNull CallType callType,
@NotNull TranslationContext context) {
return (new CallExpressionTranslator(expression, receiver, context)).translate(callType);
if (InlinedCallExpressionTranslator.shouldBeInlined(expression, context)) {
return InlinedCallExpressionTranslator.translate(expression, receiver, callType, context);
}
return (new CallExpressionTranslator(expression, receiver, callType, context)).translate();
}
@NotNull
private final JetCallExpression expression;
@NotNull
private final ResolvedCall<?> resolvedCall;
private final boolean isNativeFunctionCall;
@Nullable
private final JsExpression receiver;
private CallExpressionTranslator(@NotNull JetCallExpression expression,
@Nullable JsExpression receiver,
@NotNull TranslationContext context) {
super(context);
this.expression = expression;
this.resolvedCall = getResolvedCallForCallExpression(bindingContext(), expression);
this.receiver = receiver;
@NotNull CallType callType, @NotNull TranslationContext context) {
super(expression, receiver, callType, context);
this.isNativeFunctionCall = AnnotationsUtils.isNativeObject(resolvedCall.getCandidateDescriptor());
}
@NotNull
private JsExpression translate(@NotNull CallType callType) {
private JsExpression translate() {
return CallBuilder.build(context())
.receiver(receiver)
.callee(getCalleeExpression())
.args(translateArguments())
.resolvedCall(resolvedCall)
.type(callType)
.translate();
.receiver(receiver)
.callee(getCalleeExpression())
.args(translateArguments())
.resolvedCall(resolvedCall)
.type(callType)
.translate();
}
@Nullable
@@ -100,7 +88,7 @@ public final class CallExpressionTranslator extends AbstractTranslator {
private JsExpression translateVariableAsFunction() {
JetExpression callee = getCallee(expression);
assert callee instanceof JetSimpleNameExpression;
return ReferenceTranslator.getAccessTranslator((JetSimpleNameExpression) callee, receiver, context()).translateAsGet();
return ReferenceTranslator.getAccessTranslator((JetSimpleNameExpression)callee, receiver, context()).translateAsGet();
}
@NotNull
@@ -111,7 +99,6 @@ public final class CallExpressionTranslator extends AbstractTranslator {
@NotNull
private List<JsExpression> translateArguments() {
List<JsExpression> result = new ArrayList<JsExpression>();
ResolvedCall<?> resolvedCall = getResolvedCallForCallExpression(bindingContext(), expression);
for (ValueParameterDescriptor parameterDescriptor : resolvedCall.getResultingDescriptor().getValueParameters()) {
ResolvedValueArgument actualArgument = resolvedCall.getValueArgumentsByIndex().get(parameterDescriptor.getIndex());
result.addAll(translateSingleArgument(actualArgument, parameterDescriptor));
@@ -119,38 +106,8 @@ public final class CallExpressionTranslator extends AbstractTranslator {
return result;
}
@NotNull
private List<JsExpression> translateSingleArgument(@NotNull ResolvedValueArgument actualArgument,
@NotNull ValueParameterDescriptor parameterDescriptor) {
List<ValueArgument> valueArguments = actualArgument.getArguments();
if (actualArgument instanceof VarargValueArgument) {
return translateVarargArgument(valueArguments);
}
if (actualArgument instanceof DefaultValueArgument) {
JetExpression defaultArgument = getDefaultArgument(bindingContext(), parameterDescriptor);
return Arrays.asList(Translation.translateAsExpression(defaultArgument, context()));
}
assert actualArgument instanceof ExpressionValueArgument;
assert valueArguments.size() == 1;
return Arrays.asList(Translation.translateAsExpression(valueArguments.get(0).getArgumentExpression(), context()));
}
@NotNull
private List<JsExpression> translateVarargArgument(@NotNull List<ValueArgument> arguments) {
List<JsExpression> translatedArgs = Lists.newArrayList();
for (ValueArgument argument : arguments) {
translatedArgs.add(Translation.translateAsExpression(argument.getArgumentExpression(), context()));
}
if (isNativeFunctionCall) {
return translatedArgs;
}
return wrapInArrayLiteral(translatedArgs);
}
@NotNull
private static List<JsExpression> wrapInArrayLiteral(@NotNull List<JsExpression> translatedArgs) {
JsArrayLiteral argsWrappedInArray = new JsArrayLiteral();
argsWrappedInArray.getExpressions().addAll(translatedArgs);
return Arrays.<JsExpression>asList(argsWrappedInArray);
@Override
public boolean shouldWrapVarargInArray() {
return !isNativeFunctionCall;
}
}
@@ -0,0 +1,107 @@
/*
* 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.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 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.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 java.util.List;
import java.util.Map;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getResolvedCallForCallExpression;
/**
* @author Pavel Talanov
*/
public final class InlinedCallExpressionTranslator extends AbstractCallExpressionTranslator {
public static boolean shouldBeInlined(@NotNull JetCallExpression expression, @NotNull TranslationContext context) {
ResolvedCall<?> resolvedCall = getResolvedCallForCallExpression(context.bindingContext(), expression);
CallableDescriptor descriptor = resolvedCall.getCandidateDescriptor();
if (descriptor instanceof SimpleFunctionDescriptor) {
return ((SimpleFunctionDescriptor)descriptor).isInline();
}
return false;
}
@NotNull
public static JsExpression translate(@NotNull JetCallExpression expression,
@Nullable JsExpression receiver,
@NotNull CallType callType,
@NotNull TranslationContext context) {
return (new InlinedCallExpressionTranslator(expression, receiver, callType, context)).translate();
}
private InlinedCallExpressionTranslator(@NotNull JetCallExpression expression, @Nullable JsExpression receiver,
@NotNull CallType callType, @NotNull TranslationContext context) {
super(expression, receiver, callType, context);
}
@NotNull
private JsExpression translate() {
TranslationContext contextWithAllParametersAliased = createContextWithAllParametersAliased();
JetFunction function = BindingUtils.getFunctionForDescriptor(bindingContext(), getFunctionDescriptor());
return Translation.translateAsExpression(function.getBodyExpression(), contextWithAllParametersAliased);
}
@NotNull
private SimpleFunctionDescriptor getFunctionDescriptor() {
CallableDescriptor descriptor = resolvedCall.getCandidateDescriptor().getOriginal();
assert descriptor instanceof SimpleFunctionDescriptor : "Inlined functions should have descriptor of type SimpleFunctionDescriptor";
return (SimpleFunctionDescriptor)descriptor;
}
private TranslationContext createContextWithAllParametersAliased() {
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);
aliases.put(parameterDescriptor, aliasForArgument.name());
context().addStatementToCurrentBlock(aliasForArgument.assignmentExpression().makeStmt());
}
return context().innerContextWithDescriptorsAliased(aliases);
}
@NotNull
private JsExpression translateArgument(@NotNull ValueParameterDescriptor parameterDescriptor,
@NotNull ResolvedValueArgument actualArgument) {
List<JsExpression> translatedSingleArgument = translateSingleArgument(actualArgument, parameterDescriptor);
assert translatedSingleArgument.size() == 1 : "We always wrap varargs in kotlin calls.";
return translatedSingleArgument.get(0);
}
@Override
public boolean shouldWrapVarargInArray() {
return true;
}
}
@@ -93,6 +93,14 @@ public final class BindingUtils {
return (JetClass)result;
}
@NotNull
public static JetFunction getFunctionForDescriptor(@NotNull BindingContext context,
@NotNull SimpleFunctionDescriptor descriptor) {
PsiElement result = context.get(BindingContext.DESCRIPTOR_TO_DECLARATION, descriptor);
assert result instanceof JetFunction : "SimpleFunctionDescriptor should have declaration of type JetFunction";
return (JetFunction)result;
}
@NotNull
public static List<JetDeclaration> getDeclarationsForNamespace(@NotNull BindingContext bindingContext,
@NotNull NamespaceDescriptor namespace) {
@@ -0,0 +1,5 @@
package foo
fun box() = myInlineFun()
inline fun myInlineFun() = true