diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/context/DynamicContext.java b/js/js.translator/src/org/jetbrains/k2js/translate/context/DynamicContext.java index 4245b57f840..0fd7f29bb8f 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/context/DynamicContext.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/context/DynamicContext.java @@ -63,7 +63,7 @@ public final class DynamicContext { JsName temporaryName = currentScope.declareTemporary(); vars.add(new JsVar(temporaryName, null)); - return new TemporaryVariable(temporaryName, initExpression); + return TemporaryVariable.create(temporaryName, initExpression); } @NotNull diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/context/TemporaryVariable.java b/js/js.translator/src/org/jetbrains/k2js/translate/context/TemporaryVariable.java index bd933d3e309..c30ee87d4ab 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/context/TemporaryVariable.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/context/TemporaryVariable.java @@ -24,14 +24,19 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.k2js.translate.utils.JsAstUtils; public class TemporaryVariable { + + /*package*/ static TemporaryVariable create(@NotNull JsName temporaryName, @Nullable JsExpression initExpression) { + return new TemporaryVariable(temporaryName, initExpression == null ? null : JsAstUtils.assignment(temporaryName.makeRef(), initExpression)); + } + @Nullable private final JsExpression assignmentExpression; @NotNull private final JsName variableName; - /*package*/ TemporaryVariable(@NotNull JsName temporaryName, @Nullable JsExpression initExpression) { + protected TemporaryVariable(@NotNull JsName temporaryName, @Nullable JsExpression assignmentExpression) { this.variableName = temporaryName; - this.assignmentExpression = initExpression == null ? null : JsAstUtils.assignment(variableName.makeRef(), initExpression); + this.assignmentExpression = assignmentExpression; } @NotNull diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/context/TranslationContext.java b/js/js.translator/src/org/jetbrains/k2js/translate/context/TranslationContext.java index 374123af50c..3f584075e1f 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/context/TranslationContext.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/context/TranslationContext.java @@ -28,6 +28,7 @@ import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.k2js.translate.expression.LiteralFunctionTranslator; import org.jetbrains.k2js.translate.intrinsic.Intrinsics; +import java.util.HashMap; import java.util.Map; import static org.jetbrains.k2js.translate.utils.BindingUtils.getDescriptorForElement; @@ -53,6 +54,8 @@ public class TranslationContext { rootDynamicContext, rootAliasingContext); } + private final HashMap expressionToTempConstVariableCache = new HashMap(); + public boolean isEcma5() { return staticContext.isEcma5(); } @@ -177,6 +180,27 @@ public class TranslationContext { return dynamicContext.declareTemporary(initExpression); } + @NotNull + public TemporaryConstVariable getOrDeclareTemporaryConstVariable(@NotNull JsExpression expression) { + TemporaryConstVariable tempVar = expressionToTempConstVariableCache.get(expression); + + if (tempVar == null) { + TemporaryVariable tmpVar = declareTemporary(expression); + + tempVar = new TemporaryConstVariable(tmpVar.name(), tmpVar.assignmentExpression()); + + expressionToTempConstVariableCache.put(expression, tempVar); + expressionToTempConstVariableCache.put(tmpVar.assignmentExpression(), tempVar); + } + + return tempVar; + } + + public void associateExpressionToLazyValue(JsExpression expression, TemporaryConstVariable temporaryConstVariable) { + assert expression == temporaryConstVariable.assignmentExpression(); + expressionToTempConstVariableCache.put(expression, temporaryConstVariable); + } + @NotNull public Namer namer() { return staticContext.getNamer(); diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/operation/UnaryOperationTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/operation/UnaryOperationTranslator.java index 48873acabd4..85a82e04267 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/operation/UnaryOperationTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/operation/UnaryOperationTranslator.java @@ -21,7 +21,7 @@ import com.google.dart.compiler.backend.js.ast.JsExpression; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.psi.JetUnaryExpression; import org.jetbrains.jet.lexer.JetTokens; -import org.jetbrains.k2js.translate.context.TemporaryVariable; +import org.jetbrains.k2js.translate.context.TemporaryConstVariable; import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.reference.CallBuilder; import org.jetbrains.k2js.translate.reference.CallType; @@ -58,8 +58,14 @@ public final class UnaryOperationTranslator { @NotNull private static JsExpression translateExclExclOperator(@NotNull JetUnaryExpression expression, @NotNull TranslationContext context) { - TemporaryVariable cachedValue = context.declareTemporary(translateAsExpression(getBaseExpression(expression), context)); - return new JsConditional(isNotNullCheck(cachedValue.assignmentExpression()), cachedValue.reference(), context.namer().throwNPEFunctionCall()); + JsExpression translatedExpression = translateAsExpression(getBaseExpression(expression), context); + TemporaryConstVariable tempVar = context.getOrDeclareTemporaryConstVariable(translatedExpression); + + JsConditional ensureNotNull = new JsConditional(isNotNullCheck(tempVar.value()), tempVar.value(), context.namer().throwNPEFunctionCall()); + + // associate (cache) ensureNotNull expression to new LazyValue with same name. + context.associateExpressionToLazyValue(ensureNotNull, new TemporaryConstVariable(tempVar.name(), ensureNotNull)); + return ensureNotNull; } @NotNull diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallExpressionTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallExpressionTranslator.java index d1c9cb177f6..75940e5604c 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallExpressionTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallExpressionTranslator.java @@ -31,7 +31,7 @@ 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.TemporaryConstVariable; import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.general.Translation; import org.jetbrains.k2js.translate.utils.AnnotationsUtils; @@ -57,7 +57,7 @@ public final class CallExpressionTranslator extends AbstractCallExpressionTransl private final boolean isNativeFunctionCall; private boolean hasSpreadOperator = false; - private TemporaryVariable cachedReceiver = null; + private TemporaryConstVariable cachedReceiver = null; private List translatedArguments = null; private JsExpression translatedReceiver = null; private JsExpression translatedCallee = null; @@ -172,7 +172,7 @@ public final class CallExpressionTranslator extends AbstractCallExpressionTransl } if (receiver != null) { - cachedReceiver = context().declareTemporary(receiver); + cachedReceiver = context().getOrDeclareTemporaryConstVariable(receiver); result.add(0, cachedReceiver.reference()); } else { diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallType.java b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallType.java index 13b1849df95..893455b6d3c 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallType.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallType.java @@ -24,7 +24,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.psi.JetDotQualifiedExpression; import org.jetbrains.jet.lang.psi.JetQualifiedExpression; import org.jetbrains.jet.lang.psi.JetSafeQualifiedExpression; -import org.jetbrains.k2js.translate.context.TemporaryVariable; +import org.jetbrains.k2js.translate.context.TemporaryConstVariable; import org.jetbrains.k2js.translate.context.TranslationContext; import static org.jetbrains.k2js.translate.utils.TranslationUtils.isNotNullCheck; @@ -36,8 +36,9 @@ public enum CallType { JsExpression constructCall(@Nullable JsExpression receiver, @NotNull CallConstructor constructor, @NotNull TranslationContext context) { assert receiver != null; - TemporaryVariable cachedValue = context.declareTemporary(receiver); - return new JsConditional(isNotNullCheck(cachedValue.assignmentExpression()), constructor.construct(cachedValue.reference()), JsLiteral.NULL); + + TemporaryConstVariable tempVar = context.getOrDeclareTemporaryConstVariable(receiver); + return new JsConditional(isNotNullCheck(tempVar.value()), constructor.construct(tempVar.value()), JsLiteral.NULL); } }, //TODO: bang qualifier is not implemented in frontend for now diff --git a/js/js.translator/testFiles/native/cases/vararg.kt b/js/js.translator/testFiles/native/cases/vararg.kt index f81532714e9..1486d25e137 100644 --- a/js/js.translator/testFiles/native/cases/vararg.kt +++ b/js/js.translator/testFiles/native/cases/vararg.kt @@ -85,5 +85,9 @@ fun box(): String { if (!(testCallOrder(1, 2, 3, 4))) return "failed when test calling order when using spread operator without args" + val baz: Bar? = Bar(1) + if (!(baz!!)?.test(0, 1, 1)) + return "failed when combined SureCall and SafeCall, maybe we lost cached expression" + return "OK" } \ No newline at end of file