From 5013abe8cd9cd39f988cba053dca76b68510338b Mon Sep 17 00:00:00 2001 From: Michael Nedzelsky Date: Fri, 19 Dec 2014 04:00:43 +0300 Subject: [PATCH] JS backend: fix KT-6401 JS: wrong result for code with many spread operator #KT-6401 Fixed --- .../reference/CallArgumentTranslator.java | 152 ++++++++++++++---- 1 file changed, 121 insertions(+), 31 deletions(-) diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallArgumentTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallArgumentTranslator.java index d7e3acfde1a..36ebf281ed2 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallArgumentTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallArgumentTranslator.java @@ -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"); * 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 } @NotNull - public static ArgumentsKind translateSingleArgument( + private static ArgumentsKind translateSingleArgument( @NotNull ResolvedValueArgument actualArgument, @NotNull List result, - @NotNull TranslationContext context, - boolean shouldWrapVarargInArray + @NotNull TranslationContext context ) { List valueArguments = actualArgument.getArguments(); - if (actualArgument instanceof VarargValueArgument) { - return translateVarargArgument(valueArguments, result, context, shouldWrapVarargInArray); - } - else if (actualArgument instanceof DefaultValueArgument) { + + if (actualArgument instanceof DefaultValueArgument) { result.add(context.namer().getUndefinedExpression()); 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 { - 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 { - return ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT; - } + return ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT; } } @@ -128,7 +127,6 @@ public class CallArgumentTranslator extends AbstractTranslator { @NotNull TranslationContext context, boolean shouldWrapVarargInArray ) { - ArgumentsKind resultKind = ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT; if (arguments.isEmpty()) { if (shouldWrapVarargInArray) { result.add(new JsArrayLiteral(Collections.emptyList())); @@ -139,11 +137,28 @@ public class CallArgumentTranslator extends AbstractTranslator { List list; if (shouldWrapVarargInArray) { list = arguments.size() == 1 ? new SmartList() : new ArrayList(arguments.size()); - result.add(new JsArrayLiteral(list)); } else { list = result; } + + ArgumentsKind resultKind = translateValueArguments(arguments, list, context); + + if (shouldWrapVarargInArray) { + List concatArguments = prepareConcatArguments(arguments, list); + JsExpression concatExpression = concatArgumentsIfNeeded(concatArguments); + result.add(concatExpression); + } + + return resultKind; + } + + private static ArgumentsKind translateValueArguments( + @NotNull List arguments, + @NotNull List list, + @NotNull TranslationContext context + ) { + ArgumentsKind resultKind = ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT; List argContexts = new SmartList(); boolean argumentsShouldBeExtractedToTmpVars = false; for (ValueArgument argument : arguments) { @@ -166,6 +181,53 @@ public class CallArgumentTranslator extends AbstractTranslator { return resultKind; } + @NotNull + private static JsExpression concatArgumentsIfNeeded(@NotNull List 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 prepareConcatArguments(@NotNull List arguments, @NotNull List 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 concatArguments = new SmartList(); + List lastArrayContent = new SmartList(); + + 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(); + } + else { + concatArguments.add(expressionArgument); + } + } + else { + lastArrayContent.add(expressionArgument); + } + } + if (lastArrayContent.size() > 0) { + concatArguments.add(new JsArrayLiteral(lastArrayContent)); + } + + return concatArguments; + } + private static void extractArguments( @NotNull List argExpressions, @NotNull List argContexts, @@ -234,23 +296,45 @@ public class CallArgumentTranslator extends AbstractTranslator { boolean argumentsShouldBeExtractedToTmpVars = false; List argContexts = new SmartList(); ArgumentsKind kind = ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT; + List concatArguments = null; for (ValueParameterDescriptor parameterDescriptor : valueParameters) { ResolvedValueArgument actualArgument = valueArgumentsByIndex.get(parameterDescriptor.getIndex()); + TranslationContext argContext = context().innerBlock(); + if (actualArgument instanceof VarargValueArgument) { - assert !hasSpreadOperator; List arguments = actualArgument.getArguments(); - hasSpreadOperator = arguments.size() == 1 && arguments.get(0).getSpreadElement() != null; - if (isNativeFunctionCall && hasSpreadOperator) { - argsBeforeVararg = result; - result = new SmartList(); + int size = arguments.size(); + for (int i = 0; i != size; ++i) { + if (arguments.get(i).getSpreadElement() != null) { + hasSpreadOperator = true; + break; + } + } + + if (hasSpreadOperator) { + if (isNativeFunctionCall) { + argsBeforeVararg = result; + result = new SmartList(); + List list = new SmartList(); + 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(); - kind = translateSingleArgument(actualArgument, result, argContext, !isNativeFunctionCall && !hasSpreadOperator); + else { + kind = translateSingleArgument(actualArgument, result, argContext); + } + context().moveVarsFrom(argContext); argContexts.add(argContext); argumentsShouldBeExtractedToTmpVars = argumentsShouldBeExtractedToTmpVars || !argContext.currentBlockIsEmpty(); @@ -263,11 +347,17 @@ public class CallArgumentTranslator extends AbstractTranslator { } 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()) { - JsInvocation concatArguments = new JsInvocation(new JsNameRef("concat", new JsArrayLiteral(argsBeforeVararg)), result); - result = new SmartList(concatArguments); + concatArguments.add(0, new JsArrayLiteral(argsBeforeVararg)); } + result = new SmartList(concatArgumentsIfNeeded(concatArguments)); + if (receiver != null) { cachedReceiver = context().getOrDeclareTemporaryConstVariable(receiver); result.add(0, cachedReceiver.reference());