JS backend: fix KT-6401 JS: wrong result for code with many spread operator
#KT-6401 Fixed
This commit is contained in:
+121
-31
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2013 JetBrains s.r.o.
|
* Copyright 2010-2014 JetBrains s.r.o.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -91,33 +91,32 @@ public class CallArgumentTranslator extends AbstractTranslator {
|
|||||||
private static enum ArgumentsKind { HAS_EMPTY_EXPRESSION_ARGUMENT, HAS_NOT_EMPTY_EXPRESSION_ARGUMENT }
|
private static enum ArgumentsKind { HAS_EMPTY_EXPRESSION_ARGUMENT, HAS_NOT_EMPTY_EXPRESSION_ARGUMENT }
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static ArgumentsKind translateSingleArgument(
|
private static ArgumentsKind translateSingleArgument(
|
||||||
@NotNull ResolvedValueArgument actualArgument,
|
@NotNull ResolvedValueArgument actualArgument,
|
||||||
@NotNull List<JsExpression> result,
|
@NotNull List<JsExpression> result,
|
||||||
@NotNull TranslationContext context,
|
@NotNull TranslationContext context
|
||||||
boolean shouldWrapVarargInArray
|
|
||||||
) {
|
) {
|
||||||
List<ValueArgument> valueArguments = actualArgument.getArguments();
|
List<ValueArgument> valueArguments = actualArgument.getArguments();
|
||||||
if (actualArgument instanceof VarargValueArgument) {
|
|
||||||
return translateVarargArgument(valueArguments, result, context, shouldWrapVarargInArray);
|
if (actualArgument instanceof DefaultValueArgument) {
|
||||||
}
|
|
||||||
else if (actualArgument instanceof DefaultValueArgument) {
|
|
||||||
result.add(context.namer().getUndefinedExpression());
|
result.add(context.namer().getUndefinedExpression());
|
||||||
return ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT;
|
return ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert actualArgument instanceof ExpressionValueArgument;
|
||||||
|
assert valueArguments.size() == 1;
|
||||||
|
|
||||||
|
JetExpression argumentExpression = valueArguments.get(0).getArgumentExpression();
|
||||||
|
assert argumentExpression != null;
|
||||||
|
|
||||||
|
JsExpression jsExpression = Translation.translateAsExpression(argumentExpression, context);
|
||||||
|
result.add(jsExpression);
|
||||||
|
|
||||||
|
if (JsAstUtils.isEmptyExpression(jsExpression)) {
|
||||||
|
return ArgumentsKind.HAS_EMPTY_EXPRESSION_ARGUMENT;
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
assert actualArgument instanceof ExpressionValueArgument;
|
return ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT;
|
||||||
assert valueArguments.size() == 1;
|
|
||||||
JetExpression argumentExpression = valueArguments.get(0).getArgumentExpression();
|
|
||||||
assert argumentExpression != null;
|
|
||||||
JsExpression jsExpression = Translation.translateAsExpression(argumentExpression, context);
|
|
||||||
result.add(jsExpression);
|
|
||||||
if (JsAstUtils.isEmptyExpression(jsExpression)) {
|
|
||||||
return ArgumentsKind.HAS_EMPTY_EXPRESSION_ARGUMENT;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,7 +127,6 @@ public class CallArgumentTranslator extends AbstractTranslator {
|
|||||||
@NotNull TranslationContext context,
|
@NotNull TranslationContext context,
|
||||||
boolean shouldWrapVarargInArray
|
boolean shouldWrapVarargInArray
|
||||||
) {
|
) {
|
||||||
ArgumentsKind resultKind = ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT;
|
|
||||||
if (arguments.isEmpty()) {
|
if (arguments.isEmpty()) {
|
||||||
if (shouldWrapVarargInArray) {
|
if (shouldWrapVarargInArray) {
|
||||||
result.add(new JsArrayLiteral(Collections.<JsExpression>emptyList()));
|
result.add(new JsArrayLiteral(Collections.<JsExpression>emptyList()));
|
||||||
@@ -139,11 +137,28 @@ public class CallArgumentTranslator extends AbstractTranslator {
|
|||||||
List<JsExpression> list;
|
List<JsExpression> list;
|
||||||
if (shouldWrapVarargInArray) {
|
if (shouldWrapVarargInArray) {
|
||||||
list = arguments.size() == 1 ? new SmartList<JsExpression>() : new ArrayList<JsExpression>(arguments.size());
|
list = arguments.size() == 1 ? new SmartList<JsExpression>() : new ArrayList<JsExpression>(arguments.size());
|
||||||
result.add(new JsArrayLiteral(list));
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
list = result;
|
list = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ArgumentsKind resultKind = translateValueArguments(arguments, list, context);
|
||||||
|
|
||||||
|
if (shouldWrapVarargInArray) {
|
||||||
|
List<JsExpression> concatArguments = prepareConcatArguments(arguments, list);
|
||||||
|
JsExpression concatExpression = concatArgumentsIfNeeded(concatArguments);
|
||||||
|
result.add(concatExpression);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultKind;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ArgumentsKind translateValueArguments(
|
||||||
|
@NotNull List<ValueArgument> arguments,
|
||||||
|
@NotNull List<JsExpression> list,
|
||||||
|
@NotNull TranslationContext context
|
||||||
|
) {
|
||||||
|
ArgumentsKind resultKind = ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT;
|
||||||
List<TranslationContext> argContexts = new SmartList<TranslationContext>();
|
List<TranslationContext> argContexts = new SmartList<TranslationContext>();
|
||||||
boolean argumentsShouldBeExtractedToTmpVars = false;
|
boolean argumentsShouldBeExtractedToTmpVars = false;
|
||||||
for (ValueArgument argument : arguments) {
|
for (ValueArgument argument : arguments) {
|
||||||
@@ -166,6 +181,53 @@ public class CallArgumentTranslator extends AbstractTranslator {
|
|||||||
return resultKind;
|
return resultKind;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static JsExpression concatArgumentsIfNeeded(@NotNull List<JsExpression> concatArguments) {
|
||||||
|
assert concatArguments.size() > 0 : "concatArguments.size should not be 0";
|
||||||
|
|
||||||
|
if (concatArguments.size() > 1) {
|
||||||
|
return new JsInvocation(new JsNameRef("concat", concatArguments.get(0)), concatArguments.subList(1, concatArguments.size()));
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return concatArguments.get(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static List<JsExpression> prepareConcatArguments(@NotNull List<ValueArgument> arguments, @NotNull List<JsExpression> list) {
|
||||||
|
assert arguments.size() != 0 : "arguments.size should not be 0";
|
||||||
|
assert arguments.size() == list.size() : "arguments.size: " + arguments.size() + " != list.size: " + list.size();
|
||||||
|
|
||||||
|
List<JsExpression> concatArguments = new SmartList<JsExpression>();
|
||||||
|
List<JsExpression> lastArrayContent = new SmartList<JsExpression>();
|
||||||
|
|
||||||
|
int size = arguments.size();
|
||||||
|
for(int index = 0; index < size; index++) {
|
||||||
|
ValueArgument valueArgument = arguments.get(index);
|
||||||
|
JsExpression expressionArgument = list.get(index);
|
||||||
|
|
||||||
|
if (valueArgument.getSpreadElement() != null) {
|
||||||
|
if (lastArrayContent.size() > 0) {
|
||||||
|
concatArguments.add(new JsArrayLiteral(lastArrayContent));
|
||||||
|
concatArguments.add(expressionArgument);
|
||||||
|
lastArrayContent = new SmartList<JsExpression>();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
concatArguments.add(expressionArgument);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lastArrayContent.add(expressionArgument);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lastArrayContent.size() > 0) {
|
||||||
|
concatArguments.add(new JsArrayLiteral(lastArrayContent));
|
||||||
|
}
|
||||||
|
|
||||||
|
return concatArguments;
|
||||||
|
}
|
||||||
|
|
||||||
private static void extractArguments(
|
private static void extractArguments(
|
||||||
@NotNull List<JsExpression> argExpressions,
|
@NotNull List<JsExpression> argExpressions,
|
||||||
@NotNull List<TranslationContext> argContexts,
|
@NotNull List<TranslationContext> argContexts,
|
||||||
@@ -234,23 +296,45 @@ public class CallArgumentTranslator extends AbstractTranslator {
|
|||||||
boolean argumentsShouldBeExtractedToTmpVars = false;
|
boolean argumentsShouldBeExtractedToTmpVars = false;
|
||||||
List<TranslationContext> argContexts = new SmartList<TranslationContext>();
|
List<TranslationContext> argContexts = new SmartList<TranslationContext>();
|
||||||
ArgumentsKind kind = ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT;
|
ArgumentsKind kind = ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT;
|
||||||
|
List<JsExpression> concatArguments = null;
|
||||||
|
|
||||||
for (ValueParameterDescriptor parameterDescriptor : valueParameters) {
|
for (ValueParameterDescriptor parameterDescriptor : valueParameters) {
|
||||||
ResolvedValueArgument actualArgument = valueArgumentsByIndex.get(parameterDescriptor.getIndex());
|
ResolvedValueArgument actualArgument = valueArgumentsByIndex.get(parameterDescriptor.getIndex());
|
||||||
|
|
||||||
|
TranslationContext argContext = context().innerBlock();
|
||||||
|
|
||||||
if (actualArgument instanceof VarargValueArgument) {
|
if (actualArgument instanceof VarargValueArgument) {
|
||||||
assert !hasSpreadOperator;
|
|
||||||
|
|
||||||
List<ValueArgument> arguments = actualArgument.getArguments();
|
List<ValueArgument> arguments = actualArgument.getArguments();
|
||||||
hasSpreadOperator = arguments.size() == 1 && arguments.get(0).getSpreadElement() != null;
|
|
||||||
|
|
||||||
if (isNativeFunctionCall && hasSpreadOperator) {
|
int size = arguments.size();
|
||||||
argsBeforeVararg = result;
|
for (int i = 0; i != size; ++i) {
|
||||||
result = new SmartList<JsExpression>();
|
if (arguments.get(i).getSpreadElement() != null) {
|
||||||
|
hasSpreadOperator = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasSpreadOperator) {
|
||||||
|
if (isNativeFunctionCall) {
|
||||||
|
argsBeforeVararg = result;
|
||||||
|
result = new SmartList<JsExpression>();
|
||||||
|
List<JsExpression> list = new SmartList<JsExpression>();
|
||||||
|
kind = translateValueArguments(arguments, list, argContext);
|
||||||
|
concatArguments = prepareConcatArguments(arguments, list);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
kind = translateVarargArgument(arguments, result, argContext, size > 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
kind = translateVarargArgument(arguments, result, argContext, !isNativeFunctionCall);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TranslationContext argContext = context().innerBlock();
|
else {
|
||||||
kind = translateSingleArgument(actualArgument, result, argContext, !isNativeFunctionCall && !hasSpreadOperator);
|
kind = translateSingleArgument(actualArgument, result, argContext);
|
||||||
|
}
|
||||||
|
|
||||||
context().moveVarsFrom(argContext);
|
context().moveVarsFrom(argContext);
|
||||||
argContexts.add(argContext);
|
argContexts.add(argContext);
|
||||||
argumentsShouldBeExtractedToTmpVars = argumentsShouldBeExtractedToTmpVars || !argContext.currentBlockIsEmpty();
|
argumentsShouldBeExtractedToTmpVars = argumentsShouldBeExtractedToTmpVars || !argContext.currentBlockIsEmpty();
|
||||||
@@ -263,11 +347,17 @@ public class CallArgumentTranslator extends AbstractTranslator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isNativeFunctionCall && hasSpreadOperator) {
|
if (isNativeFunctionCall && hasSpreadOperator) {
|
||||||
|
assert argsBeforeVararg != null : "argsBeforeVararg should not be null";
|
||||||
|
assert concatArguments != null : "concatArguments should not be null";
|
||||||
|
|
||||||
|
concatArguments.addAll(result);
|
||||||
|
|
||||||
if (!argsBeforeVararg.isEmpty()) {
|
if (!argsBeforeVararg.isEmpty()) {
|
||||||
JsInvocation concatArguments = new JsInvocation(new JsNameRef("concat", new JsArrayLiteral(argsBeforeVararg)), result);
|
concatArguments.add(0, new JsArrayLiteral(argsBeforeVararg));
|
||||||
result = new SmartList<JsExpression>(concatArguments);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result = new SmartList<JsExpression>(concatArgumentsIfNeeded(concatArguments));
|
||||||
|
|
||||||
if (receiver != null) {
|
if (receiver != null) {
|
||||||
cachedReceiver = context().getOrDeclareTemporaryConstVariable(receiver);
|
cachedReceiver = context().getOrDeclareTemporaryConstVariable(receiver);
|
||||||
result.add(0, cachedReceiver.reference());
|
result.add(0, cachedReceiver.reference());
|
||||||
|
|||||||
Reference in New Issue
Block a user