KT-962: Function is called two times

safecall expression now compute receiver only once.
This commit is contained in:
Pavel Talanov
2012-01-17 17:37:40 +04:00
parent b89cb4a9df
commit 0d89e5d01f
9 changed files with 101 additions and 44 deletions
@@ -309,6 +309,28 @@ public class AstUtil {
throw new AssertionError("Set qualifier should be applied only to JsInvocation or JsNameRef instances");
}
public static JsExpression getQualifier(JsExpression expression) {
if (expression instanceof JsInvocation) {
return ((JsInvocation) expression).getQualifier();
}
if (expression instanceof JsNameRef) {
return ((JsNameRef) expression).getQualifier();
}
throw new AssertionError("Get qualifier should be applied only to JsInvocation or JsNameRef instances");
}
public static void substituteTopLevelQualifier(JsExpression expression, JsExpression substitutor) {
assert expression != null;
JsExpression previous = expression;
JsExpression current = getQualifier(expression);
assert current != null : "Cannot be applied to unqualified expression";
while (getQualifier(current) != null) {
previous = current;
current = getQualifier(current);
}
setQualifier(previous, substitutor);
}
//TODO: look for should-be-usages
public static JsNameRef qualified(JsName selector, JsExpression qualifier) {
JsNameRef reference = selector.makeRef();
@@ -5,54 +5,57 @@ import com.google.dart.compiler.util.AstUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
//TODO: track that temporary declarations is in the scope of the block where they are declared
public class DynamicContext {
@NotNull
public static DynamicContext rootContext(@NotNull NamingScope rootScope, @NotNull JsBlock globalBlock) {
return new DynamicContext(rootScope, globalBlock);
return new DynamicContext(rootScope, rootScope, globalBlock);
}
@NotNull
public static DynamicContext contextWithScope(@NotNull NamingScope scope) {
return new DynamicContext(scope, new JsBlock());
return new DynamicContext(scope, scope, new JsBlock());
}
@NotNull
private NamingScope namingScope;
private NamingScope currentScope;
@NotNull
private NamingScope blockScope;
@NotNull
private JsBlock currentBlock;
private DynamicContext(@NotNull NamingScope scope, @NotNull JsBlock block) {
this.namingScope = scope;
private DynamicContext(@NotNull NamingScope scope, @NotNull NamingScope blockScope, @NotNull JsBlock block) {
this.currentScope = scope;
this.currentBlock = block;
this.blockScope = blockScope;
}
@NotNull
public DynamicContext innerScope(@NotNull JsScope scope) {
return new DynamicContext(namingScope.innerScope(scope), currentBlock);
return new DynamicContext(currentScope.innerScope(scope), blockScope, currentBlock);
}
//TODO:
@NotNull
public DynamicContext innerBlock(@NotNull JsBlock block) {
return new DynamicContext(namingScope, block);
return new DynamicContext(currentScope, currentScope, block);
}
@NotNull
public JsName getLocalName(@NotNull DeclarationDescriptor descriptor) {
JsName name = namingScope.getName(descriptor);
JsName name = currentScope.getName(descriptor);
assert name != null : descriptor.getName() + " is not declared. Use isDeclared to check.";
return name;
}
public boolean isDeclared(@NotNull DeclarationDescriptor descriptor) {
return namingScope.isDeclared(descriptor);
return currentScope.isDeclared(descriptor);
}
@NotNull
public TemporaryVariable declareTemporary(@NotNull JsExpression initExpression) {
JsName temporaryName = namingScope.declareTemporary();
JsName temporaryName = blockScope.declareTemporary();
JsVars temporaryDeclaration = AstUtil.newVar(temporaryName, null);
jsBlock().addVarDeclaration(temporaryDeclaration);
return new TemporaryVariable(temporaryName, initExpression);
@@ -60,15 +63,14 @@ public class DynamicContext {
@NotNull
public JsName declareLocalVariable(@NotNull DeclarationDescriptor descriptor) {
return namingScope.declareVariable(descriptor, descriptor.getName());
return currentScope.declareVariable(descriptor, descriptor.getName());
}
@NotNull
public JsScope jsScope() {
return namingScope.jsScope();
return currentScope.jsScope();
}
//TODO:
@NotNull
public JsBlock jsBlock() {
return currentBlock;
@@ -16,6 +16,7 @@ import java.util.List;
* @author Pavel.Talanov
*/
//TODO: rework translator to translate everything in the namespace not only in one file
// TEST IT
public final class NamespaceTranslator extends AbstractTranslator {
@NotNull
@@ -66,8 +67,7 @@ public final class NamespaceTranslator extends AbstractTranslator {
JsInvocation namespaceDeclaration = namespaceCreateMethodInvocation();
addMemberDeclarations(namespaceDeclaration);
addClassesDeclarations(namespaceDeclaration);
return AstUtil.newAssignmentStatement
(namespaceName.makeRef(), namespaceDeclaration);
return AstUtil.newVar(namespaceName, namespaceDeclaration);
}
private void addClassesDeclarations(@NotNull JsInvocation namespaceDeclaration) {
@@ -8,6 +8,7 @@ import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
import org.jetbrains.jet.lang.resolve.constants.NullValue;
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.general.TranslatorVisitor;
@@ -16,7 +17,6 @@ import org.jetbrains.k2js.translate.operation.IncrementTranslator;
import org.jetbrains.k2js.translate.operation.UnaryOperationTranslator;
import org.jetbrains.k2js.translate.reference.AccessTranslator;
import org.jetbrains.k2js.translate.reference.CallTranslator;
import org.jetbrains.k2js.translate.reference.PropertyAccessTranslator;
import org.jetbrains.k2js.translate.reference.ReferenceTranslator;
import org.jetbrains.k2js.translate.utils.BindingUtils;
@@ -247,29 +247,23 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
if (expression.getSelectorExpression() instanceof JetCallExpression) {
return CallTranslator.translate(expression, context);
}
return translateQualifiedExpression(expression, context);
}
@NotNull
private JsExpression translateQualifiedExpression(@NotNull JetQualifiedExpression expression,
@NotNull TranslationContext context) {
if (PropertyAccessTranslator.canBePropertyGetterCall(expression, context)) {
return PropertyAccessTranslator.translateAsPropertyGetterCall(expression, context);
}
return translateAsQualifiedAccess(expression, context);
}
@NotNull
private JsExpression translateAsQualifiedAccess(@NotNull JetQualifiedExpression expression,
@NotNull TranslationContext context) {
JsExpression receiver = translateReceiver(expression, context);
JsExpression selector = translateSelector(expression, context);
assert (selector instanceof JsNameRef || selector instanceof JsInvocation)
return composeQualifiedExpression(receiver, selector);
}
@NotNull
private JsExpression composeQualifiedExpression(@NotNull JsExpression receiver, @NotNull JsExpression selector) {
//TODO: make sure that logic would not break for binary operation. check if there is a way to provide clearer logic
assert (selector instanceof JsNameRef || selector instanceof JsInvocation || selector instanceof JsBinaryOperation)
: "Selector should be a name reference or a method invocation in dot qualified expression.";
if (selector instanceof JsInvocation) {
return translateAsQualifiedInvocation(receiver, (JsInvocation) selector);
} else {
} else if (selector instanceof JsNameRef) {
return translateAsQualifiedNameReference(receiver, (JsNameRef) selector);
} else {
((JsBinaryOperation) selector).setArg1(receiver);
return selector;
}
}
@@ -327,11 +321,13 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
@NotNull
public JsNode visitSafeQualifiedExpression(@NotNull JetSafeQualifiedExpression expression,
@NotNull TranslationContext context) {
JsExpression receiver = translateReceiver(expression, context);
TemporaryVariable receiver = context.declareTemporary(translateReceiver(expression, context));
JsNullLiteral nullLiteral = context.program().getNullLiteral();
JsExpression thenExpression = translateQualifiedExpression(expression, context);
return new JsConditional(notNullCheck(context, receiver),
thenExpression, nullLiteral);
JsExpression selector = translateSelector(expression, context);
JsExpression thenExpression = composeQualifiedExpression(receiver.nameReference(), selector);
JsConditional callMethodIfNotNullConditional
= new JsConditional(notNullCheck(context, receiver.nameReference()), thenExpression, nullLiteral);
return AstUtil.newSequence(receiver.assignmentExpression(), callMethodIfNotNullConditional);
}
@Override
@@ -37,7 +37,6 @@ public final class ForTranslator extends AbstractTranslator {
this.expression = forExpression;
}
@NotNull
private JsBlock translate() {
JsName parameterName = declareParameter();
@@ -55,9 +54,13 @@ public final class ForTranslator extends AbstractTranslator {
@NotNull
private JsBlock generateCycleBody(@NotNull JsName parameterName, @NotNull TemporaryVariable iterator) {
JsStatement parameterAssignment = AstUtil.newAssignmentStatement(parameterName.makeRef(), nextMethodInvocation(iterator));
JsNode originalBody = Translation.translateExpression(getLoopBody(expression), context());
return AstUtil.newBlock(parameterAssignment, AstUtil.convertToBlock(originalBody));
JsBlock cycleBody = new JsBlock();
JsStatement parameterAssignment =
AstUtil.newVar(parameterName, nextMethodInvocation(iterator));
JsNode originalBody = Translation.translateExpression(getLoopBody(expression), context().innerBlock(cycleBody));
cycleBody.addStatement(parameterAssignment);
cycleBody.addStatement(AstUtil.convertToBlock(originalBody));
return cycleBody;
}
@NotNull
@@ -26,4 +26,16 @@ public class MiscTest extends AbstractExpressionTest {
// checkOutput("intRange.kt", " ");
testFooBoxIsTrue("intRange.kt");
}
@Test
public void safecallComputesExpressionOnlyOnce() throws Exception {
testFooBoxIsTrue("safecallComputesExpressionOnlyOnce.kt");
}
@Test
public void KT962() throws Exception {
checkOutput("KT-962.kt", "stdout\n" +
"Hello, world!\n");
}
}
@@ -61,7 +61,7 @@ fun main(args:Array<String>)
v.view(it, {(itemLifetime, item)->
x.add(item)
Dump(x)
itemLifetime.attach { x.remove(item); Dump(x) }
itemLifetime.attach { x.remove(item as Any); Dump(x) }
})
})
}
@@ -0,0 +1,8 @@
fun stdout(): java.io.PrintStream? {
System.out?.println("stdout")
return System.out
}
fun main(args : Array<String>) {
stdout()?.println("Hello, world!")
}
@@ -0,0 +1,14 @@
package foo
var i = 0
fun test() : Int? = i++
fun box() : Boolean {
if (i != 0) return false
test()?.plus(1)
if (i != 1) return false
test()?.minus(2)
if (i != 2) return false
return true
}