Added support spread operator in JS-Backend.
This commit is contained in:
@@ -85,7 +85,7 @@ public class FunctionTest extends AbstractExpressionTest {
|
||||
|
||||
|
||||
public void testVararg() throws Exception {
|
||||
fooBoxTest();
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
public void testKT921() throws Exception {
|
||||
|
||||
@@ -50,7 +50,7 @@ public final class NativeInteropTest extends SingleFileTranslationTest {
|
||||
}
|
||||
|
||||
public void testVararg() throws Exception {
|
||||
fooBoxTest();
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
public void testUndefined() throws Exception {
|
||||
|
||||
+67
-9
@@ -16,7 +16,8 @@
|
||||
|
||||
package org.jetbrains.k2js.translate.reference;
|
||||
|
||||
import com.google.dart.compiler.backend.js.ast.JsExpression;
|
||||
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;
|
||||
@@ -24,10 +25,13 @@ 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.resolve.calls.util.ExpressionAsFunctionDescriptor;
|
||||
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.TemporaryVariable;
|
||||
import org.jetbrains.k2js.translate.context.TranslationContext;
|
||||
import org.jetbrains.k2js.translate.general.Translation;
|
||||
import org.jetbrains.k2js.translate.utils.AnnotationsUtils;
|
||||
@@ -52,6 +56,11 @@ public final class CallExpressionTranslator extends AbstractCallExpressionTransl
|
||||
}
|
||||
|
||||
private final boolean isNativeFunctionCall;
|
||||
private boolean hasSpreadOperator = false;
|
||||
private TemporaryVariable cachedReceiver = null;
|
||||
private List<JsExpression> translatedArguments = null;
|
||||
private JsExpression translatedReceiver = null;
|
||||
private JsExpression translatedCallee = null;
|
||||
|
||||
private CallExpressionTranslator(@NotNull JetCallExpression expression,
|
||||
@Nullable JsExpression receiver,
|
||||
@@ -62,15 +71,23 @@ public final class CallExpressionTranslator extends AbstractCallExpressionTransl
|
||||
|
||||
@NotNull
|
||||
private JsExpression translate() {
|
||||
prepareToBuildCall();
|
||||
|
||||
return CallBuilder.build(context())
|
||||
.receiver(getReceiver())
|
||||
.callee(getCalleeExpression())
|
||||
.args(translateArguments())
|
||||
.receiver(translatedReceiver)
|
||||
.callee(translatedCallee)
|
||||
.args(translatedArguments)
|
||||
.resolvedCall(getResolvedCall())
|
||||
.type(callType)
|
||||
.translate();
|
||||
}
|
||||
|
||||
private void prepareToBuildCall() {
|
||||
translatedArguments = translateArguments();
|
||||
translatedReceiver = getReceiver();
|
||||
translatedCallee = getCalleeExpression();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private ResolvedCall<?> getResolvedCall() {
|
||||
if (resolvedCall instanceof VariableAsFunctionResolvedCall) {
|
||||
@@ -81,14 +98,23 @@ public final class CallExpressionTranslator extends AbstractCallExpressionTransl
|
||||
|
||||
@Nullable
|
||||
private JsExpression getReceiver() {
|
||||
if (receiver != null) {
|
||||
return receiver;
|
||||
assert translatedArguments != null : "the results of this function depends on the results of translateArguments()";
|
||||
if (receiver == null) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
if (cachedReceiver != null) {
|
||||
return cachedReceiver.assignmentExpression();
|
||||
}
|
||||
return receiver;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private JsExpression getCalleeExpression() {
|
||||
assert translatedArguments != null : "the results of this function depends on the results of translateArguments()";
|
||||
if (isNativeFunctionCall && hasSpreadOperator) {
|
||||
String functionName = resolvedCall.getCandidateDescriptor().getOriginal().getName().getIdentifier();
|
||||
return new JsNameRef("apply", functionName);
|
||||
}
|
||||
CallableDescriptor candidateDescriptor = resolvedCall.getCandidateDescriptor();
|
||||
if (candidateDescriptor instanceof ExpressionAsFunctionDescriptor) {
|
||||
return translateExpressionAsFunction();
|
||||
@@ -118,15 +144,47 @@ public final class CallExpressionTranslator extends AbstractCallExpressionTransl
|
||||
@NotNull
|
||||
private List<JsExpression> translateArguments() {
|
||||
List<JsExpression> result = new ArrayList<JsExpression>();
|
||||
List<JsExpression> argsBeforeVararg = null;
|
||||
for (ValueParameterDescriptor parameterDescriptor : resolvedCall.getResultingDescriptor().getValueParameters()) {
|
||||
ResolvedValueArgument actualArgument = resolvedCall.getValueArgumentsByIndex().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>();
|
||||
}
|
||||
}
|
||||
|
||||
result.addAll(translateSingleArgument(actualArgument, parameterDescriptor));
|
||||
}
|
||||
|
||||
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().declareTemporary(receiver);
|
||||
result.add(0, cachedReceiver.reference());
|
||||
}
|
||||
else {
|
||||
result.add(0, JsLiteral.NULL);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldWrapVarargInArray() {
|
||||
return !isNativeFunctionCall;
|
||||
return !isNativeFunctionCall && !hasSpreadOperator;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,5 +13,57 @@ fun testSum(expectedSum : Int, vararg i : Int) : Boolean {
|
||||
return (expectedSum == sum)
|
||||
}
|
||||
|
||||
fun box() = testSize(0) && testSum(0) && testSize(3, 1, 1, 1) && testSum(3, 1, 1, 1) && testSize(6, 1, 1, 1, 2, 3, 4) &&
|
||||
testSum(30, 10, 20, 0)
|
||||
fun testSpreadOperator(vararg args : Int): Boolean {
|
||||
var sum = 0;
|
||||
for (a in args) sum += a
|
||||
|
||||
return testSize(args.size, *args) && testSum(sum, *args)
|
||||
}
|
||||
|
||||
class Bar(val size: Int, val sum: Int) {
|
||||
fun test(vararg args : Int) = testSize(size, *args) && testSum(sum, *args)
|
||||
}
|
||||
|
||||
object obj {
|
||||
fun test(size: Int, sum: Int, vararg args: Int) = testSize(size, *args) && testSum(sum, *args)
|
||||
}
|
||||
|
||||
fun spreadInMethodCall(size: Int, sum: Int, vararg args: Int) = Bar(size, sum).test(*args)
|
||||
|
||||
fun spreadInObjectMethodCall(size: Int, sum: Int, vararg args: Int) = obj.test(size, sum, *args)
|
||||
|
||||
fun testVarargWithFunLit(vararg args: Int, f: (a: IntArray) -> Boolean): Boolean = f(args)
|
||||
|
||||
fun box(): String {
|
||||
if (!testSize(0))
|
||||
return "wrong vararg size when call function without args"
|
||||
|
||||
if (!testSum(0))
|
||||
return "wrong vararg sum (arguments) when call function without args"
|
||||
|
||||
if (!testSize(6, 1, 1, 1, 2, 3, 4))
|
||||
return "wrong vararg size when call function with some args (1)"
|
||||
|
||||
if (!testSum(30, 10, 20, 0))
|
||||
return "wrong vararg sum (arguments) when call function with some args (1)"
|
||||
|
||||
if (!testSpreadOperator(30, 10, 20, 0))
|
||||
return "failed when call function using spread operator"
|
||||
|
||||
if (!Bar(3, 30).test(10, 20, 0))
|
||||
return "failed when call method"
|
||||
|
||||
if (!spreadInMethodCall(2, 3, 1, 2))
|
||||
return "failed when call method using spread operator"
|
||||
|
||||
if (!obj.test(5, 15, 1, 2, 3, 4, 5))
|
||||
return "failed when call method of object"
|
||||
|
||||
if (!spreadInObjectMethodCall(2, 3, 1, 2))
|
||||
return "failed when call method of object using spread operator"
|
||||
|
||||
if (!testVarargWithFunLit(1, 2, 3) { args -> args.size == 3 })
|
||||
return "failed when call function with vararg and fun literal"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -5,20 +5,85 @@ import js.*
|
||||
native
|
||||
fun paramCount(vararg a : Int) : Int = js.noImpl
|
||||
|
||||
fun count(vararg a : Int) = a.size
|
||||
// test spread operator
|
||||
fun count(vararg a : Int) = paramCount(*a)
|
||||
|
||||
fun box() : Boolean {
|
||||
if (paramCount(1, 2 ,3) != 3) {
|
||||
return false;
|
||||
}
|
||||
if (paramCount() != 0) {
|
||||
return false;
|
||||
}
|
||||
if (count() != 0) {
|
||||
return false;
|
||||
}
|
||||
if (count(1, 1, 1, 1) != 4) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
native
|
||||
class Bar(val size: Int, order: Int = 0) {
|
||||
fun test(order: Int, dummy: Int, vararg args: Int): Boolean = js.noImpl
|
||||
class object {
|
||||
fun startNewTest(): Boolean = js.noImpl
|
||||
var hasOrderProblem: Boolean = false
|
||||
}
|
||||
}
|
||||
|
||||
native
|
||||
object obj {
|
||||
fun test(size: Int, vararg args: Int): Boolean = js.noImpl
|
||||
}
|
||||
|
||||
fun spreadInMethodCall(size: Int, vararg args: Int) = Bar(size).test(0, 1, *args)
|
||||
|
||||
fun spreadInObjectMethodCall(size: Int, vararg args: Int) = obj.test(size, *args)
|
||||
|
||||
native
|
||||
fun testNativeVarargWithFunLit(vararg args: Int, f: (a: IntArray) -> Boolean): Boolean = js.noImpl
|
||||
|
||||
fun testSpreadOperatorWithSafeCall(a: Bar?, expected: Boolean?, vararg args: Int): Boolean {
|
||||
return a?.test(0, 1, *args) == expected
|
||||
}
|
||||
|
||||
fun testSpreadOperatorWithSureCall(a: Bar?, vararg args: Int): Boolean {
|
||||
return a!!.test(0, 1, *args)
|
||||
}
|
||||
|
||||
fun testCallOrder(vararg args: Int) =
|
||||
Bar.startNewTest() &&
|
||||
Bar(args.size, 0).test(1, 1, *args) && Bar(args.size, 2).test(3, 1, *args) &&
|
||||
!Bar.hasOrderProblem
|
||||
|
||||
fun box(): String {
|
||||
if (paramCount() != 0)
|
||||
return "failed when call native function without args"
|
||||
|
||||
if (paramCount(1, 2 ,3) != 3)
|
||||
return "failed when call native function with some args"
|
||||
|
||||
if (count() != 0)
|
||||
return "failed when call native function without args using spread operator"
|
||||
|
||||
if (count(1, 1, 1, 1) != 4)
|
||||
return "failed when call native function with some args using spread operator"
|
||||
|
||||
if (!Bar(5).test(0, 1, 1, 2, 3, 4, 5))
|
||||
return "failed when call method with some args"
|
||||
|
||||
if (!spreadInMethodCall(2, 1, 2))
|
||||
return "failed when call method using spread operator"
|
||||
|
||||
if (!(obj.test(5, 1, 2, 3, 4, 5)))
|
||||
return "failed when call method of object"
|
||||
|
||||
if (!(spreadInObjectMethodCall(2, 1, 2)))
|
||||
return "failed when call method of object using spread operator"
|
||||
|
||||
if (!(testNativeVarargWithFunLit(1, 2, 3) { args -> args.size == 3 }))
|
||||
return "failed when call native function with vararg and fun literal"
|
||||
|
||||
if (!(testSpreadOperatorWithSafeCall(null, null)))
|
||||
return "failed when test spread operator with SafeCall (?.) using null receiver"
|
||||
|
||||
if (!(testSpreadOperatorWithSafeCall(Bar(3), true, 1, 2, 3)))
|
||||
return "failed when test spread operator with SafeCall (?.)"
|
||||
|
||||
if (!(testSpreadOperatorWithSureCall(Bar(3), 1, 2, 3)))
|
||||
return "failed when test spread operator with SureCall (!!)"
|
||||
|
||||
if (!(testCallOrder()))
|
||||
return "failed when test calling order when using spread operator without args"
|
||||
|
||||
if (!(testCallOrder(1, 2, 3, 4)))
|
||||
return "failed when test calling order when using spread operator without args"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -16,4 +16,40 @@
|
||||
|
||||
function paramCount() {
|
||||
return arguments.length
|
||||
}
|
||||
|
||||
function Bar(size, order) {
|
||||
this.size = size;
|
||||
Bar.checkOrder(order);
|
||||
}
|
||||
|
||||
Bar.order = 0;
|
||||
Bar.hasOrderProblem = false;
|
||||
Bar.checkOrder = function (expectedOrder) {
|
||||
var curOrder = Bar.order++;
|
||||
Bar.hasOrderProblem = Bar.hasOrderProblem || curOrder !== expectedOrder;
|
||||
};
|
||||
|
||||
Bar.startNewTest = function () {
|
||||
Bar.hasOrderProblem = false;
|
||||
Bar.order = 0;
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
Bar.prototype.test = function (order, dummy /*, args */) {
|
||||
Bar.checkOrder(order);
|
||||
return dummy === 1 && (arguments.length - 2) === this.size;
|
||||
};
|
||||
|
||||
var obj = {
|
||||
test : function (size /*, args */) {
|
||||
return (arguments.length - 1) === size;
|
||||
}
|
||||
};
|
||||
|
||||
function testNativeVarargWithFunLit(/* args, f */) {
|
||||
var args = Array.prototype.slice.call(arguments, 0, arguments.length - 1);
|
||||
var f = arguments[arguments.length - 1];
|
||||
return typeof f === "function" && f(args);
|
||||
}
|
||||
Reference in New Issue
Block a user