JS backend: refactoring: extract CallArgumentTranslator from AbstractCallExpressionTranslator and CallExpressionTranslator

This commit is contained in:
Erokhin Stanislav
2013-09-20 20:09:31 +04:00
parent 0e6d9e857c
commit dae791d7b1
4 changed files with 206 additions and 137 deletions
@@ -16,25 +16,14 @@
package org.jetbrains.k2js.translate.reference;
import com.google.dart.compiler.backend.js.ast.JsArrayLiteral;
import com.google.dart.compiler.backend.js.ast.JsExpression;
import com.intellij.util.SmartList;
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.model.*;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.AbstractTranslator;
import org.jetbrains.k2js.translate.general.Translation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getDefaultArgument;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getResolvedCallForCallExpression;
public abstract class AbstractCallExpressionTranslator extends AbstractTranslator {
@@ -58,50 +47,4 @@ public abstract class AbstractCallExpressionTranslator extends AbstractTranslato
this.callType = type;
}
protected abstract boolean shouldWrapVarargInArray();
protected void translateSingleArgument(
@NotNull ResolvedValueArgument actualArgument,
@NotNull ValueParameterDescriptor parameterDescriptor,
@NotNull List<JsExpression> result
) {
List<ValueArgument> valueArguments = actualArgument.getArguments();
if (actualArgument instanceof VarargValueArgument) {
translateVarargArgument(valueArguments, result);
}
else if (actualArgument instanceof DefaultValueArgument) {
JetExpression defaultArgument = getDefaultArgument(bindingContext(), parameterDescriptor);
result.add(Translation.translateAsExpression(defaultArgument, context()));
}
else {
assert actualArgument instanceof ExpressionValueArgument;
assert valueArguments.size() == 1;
JetExpression argumentExpression = valueArguments.get(0).getArgumentExpression();
assert argumentExpression != null;
result.add(Translation.translateAsExpression(argumentExpression, context()));
}
}
private void translateVarargArgument(@NotNull List<ValueArgument> arguments, @NotNull List<JsExpression> result) {
if (arguments.isEmpty()) {
if (shouldWrapVarargInArray()) {
result.add(new JsArrayLiteral(Collections.<JsExpression>emptyList()));
}
return;
}
List<JsExpression> list;
if (shouldWrapVarargInArray()) {
list = arguments.size() == 1 ? new SmartList<JsExpression>() : new ArrayList<JsExpression>(arguments.size());
result.add(new JsArrayLiteral(list));
}
else {
list = result;
}
for (ValueArgument argument : arguments) {
JetExpression argumentExpression = argument.getArgumentExpression();
assert argumentExpression != null;
list.add(Translation.translateAsExpression(argumentExpression, context()));
}
}
}
@@ -0,0 +1,193 @@
/*
* 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.reference;
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.ValueParameterDescriptor;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.ValueArgument;
import org.jetbrains.jet.lang.resolve.calls.model.*;
import org.jetbrains.k2js.translate.context.TemporaryConstVariable;
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.Collections;
import java.util.List;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getDefaultArgument;
public class CallArgumentTranslator extends AbstractTranslator {
@NotNull
public static ArgumentsInfo translate(
@NotNull ResolvedCall<?> resolvedCall,
@Nullable JsExpression receiver,
@NotNull TranslationContext context
) {
CallArgumentTranslator argumentTranslator = new CallArgumentTranslator(resolvedCall, receiver, context);
return argumentTranslator.translate();
}
public static class ArgumentsInfo {
private final List<JsExpression> translateArguments;
private final boolean hasSpreadOperator;
private final TemporaryConstVariable cachedReceiver;
public ArgumentsInfo(List<JsExpression> arguments, boolean operator, TemporaryConstVariable receiver) {
translateArguments = arguments;
hasSpreadOperator = operator;
cachedReceiver = receiver;
}
@NotNull
public List<JsExpression> getTranslateArguments() {
return translateArguments;
}
public boolean isHasSpreadOperator() {
return hasSpreadOperator;
}
@Nullable
public TemporaryConstVariable getCachedReceiver() {
return cachedReceiver;
}
}
public static void translateSingleArgument(
@NotNull ResolvedValueArgument actualArgument,
@NotNull ValueParameterDescriptor parameterDescriptor,
@NotNull List<JsExpression> result,
@NotNull TranslationContext context,
boolean shouldWrapVarargInArray
) {
List<ValueArgument> valueArguments = actualArgument.getArguments();
if (actualArgument instanceof VarargValueArgument) {
translateVarargArgument(valueArguments, result, context, shouldWrapVarargInArray);
}
else if (actualArgument instanceof DefaultValueArgument) {
JetExpression defaultArgument = getDefaultArgument(context.bindingContext(), parameterDescriptor);
result.add(Translation.translateAsExpression(defaultArgument, context));
}
else {
assert actualArgument instanceof ExpressionValueArgument;
assert valueArguments.size() == 1;
JetExpression argumentExpression = valueArguments.get(0).getArgumentExpression();
assert argumentExpression != null;
result.add(Translation.translateAsExpression(argumentExpression, context));
}
}
private static void translateVarargArgument(
@NotNull List<ValueArgument> arguments,
@NotNull List<JsExpression> result,
@NotNull TranslationContext context,
boolean shouldWrapVarargInArray
) {
if (arguments.isEmpty()) {
if (shouldWrapVarargInArray) {
result.add(new JsArrayLiteral(Collections.<JsExpression>emptyList()));
}
return;
}
List<JsExpression> list;
if (shouldWrapVarargInArray) {
list = arguments.size() == 1 ? new SmartList<JsExpression>() : new ArrayList<JsExpression>(arguments.size());
result.add(new JsArrayLiteral(list));
}
else {
list = result;
}
for (ValueArgument argument : arguments) {
JetExpression argumentExpression = argument.getArgumentExpression();
assert argumentExpression != null;
list.add(Translation.translateAsExpression(argumentExpression, context));
}
}
@NotNull
private final ResolvedCall<?> resolvedCall;
@Nullable
private final JsExpression receiver;
private final boolean isNativeFunctionCall;
private CallArgumentTranslator(
@NotNull ResolvedCall<?> resolvedCall,
@Nullable JsExpression receiver,
@NotNull TranslationContext context
) {
super(context);
this.resolvedCall = resolvedCall;
this.receiver = receiver;
this.isNativeFunctionCall = AnnotationsUtils.isNativeObject(resolvedCall.getCandidateDescriptor());
}
private ArgumentsInfo translate() {
List<ValueParameterDescriptor> valueParameters = resolvedCall.getResultingDescriptor().getValueParameters();
if (valueParameters.isEmpty()) {
return new ArgumentsInfo(Collections.<JsExpression>emptyList(), false, null);
}
boolean hasSpreadOperator = false;
TemporaryConstVariable cachedReceiver = null;
List<JsExpression> result = new ArrayList<JsExpression>(valueParameters.size());
List<ResolvedValueArgument> valueArgumentsByIndex = resolvedCall.getValueArgumentsByIndex();
List<JsExpression> argsBeforeVararg = null;
for (ValueParameterDescriptor parameterDescriptor : valueParameters) {
ResolvedValueArgument actualArgument = valueArgumentsByIndex.get(parameterDescriptor.getIndex());
if (actualArgument instanceof VarargValueArgument) {
assert !hasSpreadOperator;
List<ValueArgument> arguments = actualArgument.getArguments();
hasSpreadOperator = arguments.size() == 1 && arguments.get(0).getSpreadElement() != null;
if (isNativeFunctionCall && hasSpreadOperator) {
argsBeforeVararg = result;
result = new SmartList<JsExpression>();
}
}
translateSingleArgument(actualArgument, parameterDescriptor, result, context(), !isNativeFunctionCall && !hasSpreadOperator);
}
if (isNativeFunctionCall && hasSpreadOperator) {
if (!argsBeforeVararg.isEmpty()) {
JsInvocation concatArguments = new JsInvocation(new JsNameRef("concat", new JsArrayLiteral(argsBeforeVararg)), result);
result = new SmartList<JsExpression>(concatArguments);
}
if (receiver != null) {
cachedReceiver = context().getOrDeclareTemporaryConstVariable(receiver);
result.add(0, cachedReceiver.reference());
}
else {
result.add(0, JsLiteral.NULL);
}
}
return new ArgumentsInfo(result, hasSpreadOperator, cachedReceiver);
}
}
@@ -16,31 +16,22 @@
package org.jetbrains.k2js.translate.reference;
import com.google.dart.compiler.backend.js.ast.*;
import com.intellij.util.SmartList;
import com.google.dart.compiler.backend.js.ast.JsExpression;
import com.google.dart.compiler.backend.js.ast.JsNameRef;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
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.JetSimpleNameExpression;
import org.jetbrains.jet.lang.psi.ValueArgument;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedValueArgument;
import org.jetbrains.jet.lang.resolve.calls.model.VarargValueArgument;
import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall;
import org.jetbrains.jet.lang.resolve.calls.util.ExpressionAsFunctionDescriptor;
import org.jetbrains.k2js.translate.context.TemporaryConstVariable;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.Translation;
import org.jetbrains.k2js.translate.utils.AnnotationsUtils;
import org.jetbrains.k2js.translate.utils.PsiUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.jetbrains.k2js.translate.utils.PsiUtils.getCallee;
public final class CallExpressionTranslator extends AbstractCallExpressionTranslator {
@@ -57,9 +48,7 @@ public final class CallExpressionTranslator extends AbstractCallExpressionTransl
}
private final boolean isNativeFunctionCall;
private boolean hasSpreadOperator = false;
private TemporaryConstVariable cachedReceiver = null;
private List<JsExpression> translatedArguments = null;
private CallArgumentTranslator.ArgumentsInfo argumentsInfo = null;
private JsExpression translatedReceiver = null;
private JsExpression translatedCallee = null;
@@ -77,14 +66,14 @@ public final class CallExpressionTranslator extends AbstractCallExpressionTransl
return CallBuilder.build(context())
.receiver(translatedReceiver)
.callee(translatedCallee)
.args(translatedArguments)
.args(argumentsInfo.getTranslateArguments())
.resolvedCall(getResolvedCall())
.type(callType)
.translate();
}
private void prepareToBuildCall() {
translatedArguments = translateArguments();
argumentsInfo = CallArgumentTranslator.translate(resolvedCall, receiver, context());
translatedReceiver = getReceiver();
translatedCallee = getCalleeExpression();
}
@@ -99,20 +88,20 @@ public final class CallExpressionTranslator extends AbstractCallExpressionTransl
@Nullable
private JsExpression getReceiver() {
assert translatedArguments != null : "the results of this function depends on the results of translateArguments()";
assert argumentsInfo != null : "the results of this function depends on the argumentsInfo";
if (receiver == null) {
return null;
}
if (cachedReceiver != null) {
return cachedReceiver.assignmentExpression();
if (argumentsInfo.getCachedReceiver() != null) {
return argumentsInfo.getCachedReceiver().assignmentExpression();
}
return receiver;
}
@Nullable
private JsExpression getCalleeExpression() {
assert translatedArguments != null : "the results of this function depends on the results of translateArguments()";
if (isNativeFunctionCall && hasSpreadOperator) {
assert argumentsInfo != null : "the results of this function depends on the argumentsInfo";
if (isNativeFunctionCall && argumentsInfo.isHasSpreadOperator()) {
String functionName = resolvedCall.getCandidateDescriptor().getOriginal().getName().getIdentifier();
return new JsNameRef("apply", functionName);
}
@@ -142,56 +131,4 @@ public final class CallExpressionTranslator extends AbstractCallExpressionTransl
return Translation.translateAsExpression(getCallee(expression), context());
}
@NotNull
private List<JsExpression> translateArguments() {
List<ValueParameterDescriptor> valueParameters = resolvedCall.getResultingDescriptor().getValueParameters();
if (valueParameters.isEmpty()) {
return Collections.emptyList();
}
List<JsExpression> result = new ArrayList<JsExpression>(valueParameters.size());
List<ResolvedValueArgument> valueArgumentsByIndex = resolvedCall.getValueArgumentsByIndex();
List<JsExpression> argsBeforeVararg = null;
for (ValueParameterDescriptor parameterDescriptor : valueParameters) {
ResolvedValueArgument actualArgument = valueArgumentsByIndex.get(parameterDescriptor.getIndex());
if (actualArgument instanceof VarargValueArgument) {
assert !hasSpreadOperator;
List<ValueArgument> arguments = actualArgument.getArguments();
hasSpreadOperator = arguments.size() == 1 && arguments.get(0).getSpreadElement() != null;
if (isNativeFunctionCall && hasSpreadOperator) {
assert argsBeforeVararg == null;
argsBeforeVararg = result;
result = new SmartList<JsExpression>();
}
}
translateSingleArgument(actualArgument, parameterDescriptor, result);
}
if (isNativeFunctionCall && hasSpreadOperator) {
assert argsBeforeVararg != null;
if (!argsBeforeVararg.isEmpty()) {
JsInvocation concatArguments = new JsInvocation(new JsNameRef("concat", new JsArrayLiteral(argsBeforeVararg)), result);
result = new SmartList<JsExpression>(concatArguments);
}
if (receiver != null) {
cachedReceiver = context().getOrDeclareTemporaryConstVariable(receiver);
result.add(0, cachedReceiver.reference());
}
else {
result.add(0, JsLiteral.NULL);
}
}
return result;
}
@Override
public boolean shouldWrapVarargInArray() {
return !isNativeFunctionCall && !hasSpreadOperator;
}
}
@@ -40,6 +40,7 @@ import org.jetbrains.k2js.translate.utils.mutator.Mutator;
import java.util.List;
import java.util.Map;
import static org.jetbrains.k2js.translate.reference.CallArgumentTranslator.translateSingleArgument;
import static org.jetbrains.k2js.translate.reference.CallParametersResolver.resolveCallParameters;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionForDescriptor;
import static org.jetbrains.k2js.translate.utils.FunctionBodyTranslator.translateFunctionBody;
@@ -154,16 +155,11 @@ public final class InlinedCallExpressionTranslator extends AbstractCallExpressio
private JsExpression translateArgument(@NotNull ValueParameterDescriptor parameterDescriptor,
@NotNull ResolvedValueArgument actualArgument) {
List<JsExpression> result = new SmartList<JsExpression>();
translateSingleArgument(actualArgument, parameterDescriptor, result);
translateSingleArgument(actualArgument, parameterDescriptor, result, context(), true);
assert result.size() == 1 : "We always wrap varargs in kotlin calls.";
return result.get(0);
}
@Override
public boolean shouldWrapVarargInArray() {
return true;
}
private static final class InlineFunctionMutator implements Mutator {
@NotNull