implement CallType logic

This commit is contained in:
Pavel Talanov
2012-02-10 22:34:34 +04:00
parent 893d983861
commit 254ed6de14
16 changed files with 173 additions and 142 deletions
@@ -126,6 +126,14 @@ public class StaticContext {
@Nullable @Nullable
public JsNameRef getQualifierForDescriptor(@NotNull DeclarationDescriptor descriptor) { public JsNameRef getQualifierForDescriptor(@NotNull DeclarationDescriptor descriptor) {
//TODO: refactor
if (descriptor instanceof PropertyDescriptor) {
return null;
}
if (isVariableAsFunction(descriptor)) {
return null;
}
//TODO: hack! //TODO: hack!
if (AnnotationsUtils.isNativeObject(descriptor)) { if (AnnotationsUtils.isNativeObject(descriptor)) {
return null; return null;
@@ -17,10 +17,7 @@ import org.jetbrains.k2js.translate.general.TranslatorVisitor;
import org.jetbrains.k2js.translate.operation.BinaryOperationTranslator; import org.jetbrains.k2js.translate.operation.BinaryOperationTranslator;
import org.jetbrains.k2js.translate.operation.IncrementTranslator; import org.jetbrains.k2js.translate.operation.IncrementTranslator;
import org.jetbrains.k2js.translate.operation.UnaryOperationTranslator; import org.jetbrains.k2js.translate.operation.UnaryOperationTranslator;
import org.jetbrains.k2js.translate.reference.AccessTranslator; import org.jetbrains.k2js.translate.reference.*;
import org.jetbrains.k2js.translate.reference.CallTranslator;
import org.jetbrains.k2js.translate.reference.QualifiedExpressionTranslator;
import org.jetbrains.k2js.translate.reference.ReferenceTranslator;
import org.jetbrains.k2js.translate.utils.BindingUtils; import org.jetbrains.k2js.translate.utils.BindingUtils;
import org.jetbrains.k2js.translate.utils.TranslationUtils; import org.jetbrains.k2js.translate.utils.TranslationUtils;
@@ -135,7 +132,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
@NotNull @NotNull
public JsNode visitCallExpression(@NotNull JetCallExpression expression, public JsNode visitCallExpression(@NotNull JetCallExpression expression,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
return CallTranslator.translate(expression, null, context); return CallTranslator.translate(expression, null, CallType.NORMAL, context);
} }
@Override @Override
@@ -260,7 +257,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
@NotNull @NotNull
public JsNode visitDotQualifiedExpression(@NotNull JetDotQualifiedExpression expression, public JsNode visitDotQualifiedExpression(@NotNull JetDotQualifiedExpression expression,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
return QualifiedExpressionTranslator.translateDotQualifiedExpression(expression, context); return QualifiedExpressionTranslator.translateQualifiedExpression(expression, context);
} }
@Override @Override
@@ -289,7 +286,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
@NotNull @NotNull
public JsNode visitSafeQualifiedExpression(@NotNull JetSafeQualifiedExpression expression, public JsNode visitSafeQualifiedExpression(@NotNull JetSafeQualifiedExpression expression,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
return QualifiedExpressionTranslator.translateSafeQualifiedExpression(expression, context); return QualifiedExpressionTranslator.translateQualifiedExpression(expression, context);
} }
@Override @Override
@@ -13,6 +13,7 @@ import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.AbstractTranslator; import org.jetbrains.k2js.translate.general.AbstractTranslator;
import org.jetbrains.k2js.translate.general.Translation; import org.jetbrains.k2js.translate.general.Translation;
import org.jetbrains.k2js.translate.reference.CallTranslator; import org.jetbrains.k2js.translate.reference.CallTranslator;
import org.jetbrains.k2js.translate.reference.CallType;
import static org.jetbrains.k2js.translate.utils.BindingUtils.*; import static org.jetbrains.k2js.translate.utils.BindingUtils.*;
import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopBody; import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopBody;
@@ -66,13 +67,13 @@ public final class ForTranslator extends AbstractTranslator {
@NotNull @NotNull
private JsExpression nextMethodInvocation(@NotNull TemporaryVariable iterator) { private JsExpression nextMethodInvocation(@NotNull TemporaryVariable iterator) {
FunctionDescriptor nextFunction = getNextFunction(context().bindingContext(), getLoopRange()); FunctionDescriptor nextFunction = getNextFunction(context().bindingContext(), getLoopRange());
return CallTranslator.translate(iterator.nameReference(), nextFunction, context()); return CallTranslator.translate(iterator.nameReference(), nextFunction, CallType.NORMAL, context());
} }
@NotNull @NotNull
private JsExpression hasNextMethodInvocation(@NotNull TemporaryVariable iterator) { private JsExpression hasNextMethodInvocation(@NotNull TemporaryVariable iterator) {
CallableDescriptor hasNextFunction = getHasNextCallable(context().bindingContext(), getLoopRange()); CallableDescriptor hasNextFunction = getHasNextCallable(context().bindingContext(), getLoopRange());
return CallTranslator.translate(iterator.nameReference(), hasNextFunction, context()); return CallTranslator.translate(iterator.nameReference(), hasNextFunction, CallType.NORMAL, context());
} }
@NotNull @NotNull
@@ -80,7 +81,7 @@ public final class ForTranslator extends AbstractTranslator {
JetExpression rangeExpression = getLoopRange(); JetExpression rangeExpression = getLoopRange();
JsExpression range = Translation.translateAsExpression(rangeExpression, context()); JsExpression range = Translation.translateAsExpression(rangeExpression, context());
FunctionDescriptor iteratorFunction = getIteratorFunction(context().bindingContext(), rangeExpression); FunctionDescriptor iteratorFunction = getIteratorFunction(context().bindingContext(), rangeExpression);
return CallTranslator.translate(range, iteratorFunction, context()); return CallTranslator.translate(range, iteratorFunction, CallType.NORMAL, context());
} }
@NotNull @NotNull
@@ -24,7 +24,8 @@ public abstract class AccessTranslator extends AbstractTranslator {
return QualifiedExpressionTranslator.getAccessTranslator((JetQualifiedExpression) referenceExpression, context); return QualifiedExpressionTranslator.getAccessTranslator((JetQualifiedExpression) referenceExpression, context);
} }
assert referenceExpression instanceof JetSimpleNameExpression; assert referenceExpression instanceof JetSimpleNameExpression;
return PropertyAccessTranslator.newInstance((JetSimpleNameExpression) referenceExpression, null, context); return PropertyAccessTranslator.newInstance((JetSimpleNameExpression) referenceExpression,
null, CallType.NORMAL, context);
} }
if (referenceExpression instanceof JetArrayAccessExpression) { if (referenceExpression instanceof JetArrayAccessExpression) {
return ArrayAccessTranslator.newInstance((JetArrayAccessExpression) referenceExpression, context); return ArrayAccessTranslator.newInstance((JetArrayAccessExpression) referenceExpression, context);
@@ -44,7 +44,7 @@ public final class ArrayAccessTranslator extends AccessTranslator {
ResolvedCall<?> resolvedCall = BindingUtils.getResolvedCall(context().bindingContext(), expression); ResolvedCall<?> resolvedCall = BindingUtils.getResolvedCall(context().bindingContext(), expression);
List<JsExpression> arguments = translateIndexExpressions(); List<JsExpression> arguments = translateIndexExpressions();
return CallTranslator.translate(translateArrayExpression(), arguments, resolvedCall, return CallTranslator.translate(translateArrayExpression(), arguments, resolvedCall,
methodDescriptor, context()); methodDescriptor, CallType.NORMAL, context());
} }
@Override @Override
@@ -54,7 +54,7 @@ public final class ArrayAccessTranslator extends AccessTranslator {
List<JsExpression> arguments = translateIndexExpressions(); List<JsExpression> arguments = translateIndexExpressions();
arguments.add(expression); arguments.add(expression);
return CallTranslator.translate(translateArrayExpression(), arguments, resolvedCall, return CallTranslator.translate(translateArrayExpression(), arguments, resolvedCall,
methodDescriptor, context()); methodDescriptor, CallType.NORMAL, context());
} }
@NotNull @NotNull
@@ -33,6 +33,19 @@ import static org.jetbrains.k2js.translate.utils.TranslationUtils.*;
//TODO: reorder methods properly //TODO: reorder methods properly
public final class CallTranslator extends AbstractTranslator { public final class CallTranslator extends AbstractTranslator {
private static class CallParameters {
public CallParameters(@Nullable JsExpression receiver, @NotNull JsExpression functionReference) {
this.receiver = receiver;
this.functionReference = functionReference;
}
@Nullable
public /*var*/ JsExpression receiver;
@NotNull
public /*var*/ JsExpression functionReference;
}
private static final class Builder { private static final class Builder {
@NotNull @NotNull
@@ -48,7 +61,7 @@ public final class CallTranslator extends AbstractTranslator {
List<JsExpression> arguments = Collections.emptyList(); List<JsExpression> arguments = Collections.emptyList();
ResolvedCall<?> resolvedCall = ResolvedCall<?> resolvedCall =
getResolvedCall(context.bindingContext(), unaryExpression.getOperationReference()); getResolvedCall(context.bindingContext(), unaryExpression.getOperationReference());
return new CallTranslator(receiver, null, arguments, resolvedCall, null, context); return new CallTranslator(receiver, null, arguments, resolvedCall, null, CallType.NORMAL, context);
} }
//TODO: method too long //TODO: method too long
@@ -71,19 +84,20 @@ public final class CallTranslator extends AbstractTranslator {
ResolvedCall<?> resolvedCall = ResolvedCall<?> resolvedCall =
getResolvedCall(context.bindingContext(), binaryExpression.getOperationReference()); getResolvedCall(context.bindingContext(), binaryExpression.getOperationReference());
return new CallTranslator(receiver, null, arguments, resolvedCall, null, context); return new CallTranslator(receiver, null, arguments, resolvedCall, null, CallType.NORMAL, context);
} }
@NotNull @NotNull
private CallTranslator buildFromCallExpression(@NotNull JetCallExpression callExpression, private CallTranslator buildFromCallExpression(@NotNull JetCallExpression callExpression,
@Nullable JsExpression receiver) { @Nullable JsExpression receiver,
@NotNull CallType callType) {
ResolvedCall<?> resolvedCall = getResolvedCallForCallExpression(context.bindingContext(), callExpression); ResolvedCall<?> resolvedCall = getResolvedCallForCallExpression(context.bindingContext(), callExpression);
List<JsExpression> arguments = translateArgumentsForCallExpression(callExpression, context); List<JsExpression> arguments = translateArgumentsForCallExpression(callExpression, context);
JsExpression callee = null; JsExpression callee = null;
if (resolvedCall.getCandidateDescriptor() instanceof ExpressionAsFunctionDescriptor) { if (resolvedCall.getCandidateDescriptor() instanceof ExpressionAsFunctionDescriptor) {
callee = Translation.translateAsExpression(getCallee(callExpression), context); callee = Translation.translateAsExpression(getCallee(callExpression), context);
} }
return new CallTranslator(receiver, callee, arguments, resolvedCall, null, context); return new CallTranslator(receiver, callee, arguments, resolvedCall, null, callType, context);
} }
@NotNull @NotNull
@@ -130,25 +144,29 @@ public final class CallTranslator extends AbstractTranslator {
@NotNull @NotNull
public static JsExpression translate(@Nullable JsExpression receiver, public static JsExpression translate(@Nullable JsExpression receiver,
@NotNull CallableDescriptor descriptor, @NotNull CallableDescriptor descriptor,
@NotNull CallType callType,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
return translate(receiver, Collections.<JsExpression>emptyList(), return translate(receiver, Collections.<JsExpression>emptyList(),
ResolvedCallImpl.create(descriptor), null, context); ResolvedCallImpl.create(descriptor), null, callType, context);
} }
@NotNull @NotNull
public static JsExpression translate(@Nullable JsExpression receiver, public static JsExpression translate(@Nullable JsExpression receiver,
@NotNull ResolvedCall<?> resolvedCall, @NotNull ResolvedCall<?> resolvedCall,
@Nullable CallableDescriptor descriptorToCall, @Nullable CallableDescriptor descriptorToCall,
@NotNull CallType callType,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
return translate(receiver, Collections.<JsExpression>emptyList(), resolvedCall, descriptorToCall, context); return (new CallTranslator(receiver, null, Collections.<JsExpression>emptyList(), resolvedCall,
descriptorToCall, CallType.SAFE, context)).translate();
} }
@NotNull @NotNull
public static JsExpression translate(@Nullable JsExpression receiver, @NotNull List<JsExpression> arguments, public static JsExpression translate(@Nullable JsExpression receiver, @NotNull List<JsExpression> arguments,
@NotNull ResolvedCall<?> resolvedCall, @NotNull ResolvedCall<?> resolvedCall,
@Nullable CallableDescriptor descriptorToCall, @Nullable CallableDescriptor descriptorToCall,
@NotNull CallType callType,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
return (new CallTranslator(receiver, null, arguments, resolvedCall, descriptorToCall, context)).translate(); return (new CallTranslator(receiver, null, arguments, resolvedCall, descriptorToCall, callType, context)).translate();
} }
@NotNull @NotNull
@@ -160,8 +178,9 @@ public final class CallTranslator extends AbstractTranslator {
@NotNull @NotNull
public static JsExpression translate(@NotNull JetCallExpression callExpression, public static JsExpression translate(@NotNull JetCallExpression callExpression,
@Nullable JsExpression receiver, @Nullable JsExpression receiver,
@NotNull CallType callType,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
return (new Builder(context).buildFromCallExpression(callExpression, receiver)).translate(); return (new Builder(context).buildFromCallExpression(callExpression, receiver, callType)).translate();
} }
@NotNull @NotNull
@@ -200,15 +219,20 @@ public final class CallTranslator extends AbstractTranslator {
@NotNull @NotNull
private final CallableDescriptor descriptor; private final CallableDescriptor descriptor;
@NotNull
private final CallType callType;
private CallTranslator(@Nullable JsExpression receiver, @Nullable JsExpression callee, private CallTranslator(@Nullable JsExpression receiver, @Nullable JsExpression callee,
@NotNull List<JsExpression> arguments, @NotNull List<JsExpression> arguments,
@NotNull ResolvedCall<? extends CallableDescriptor> resolvedCall, @NotNull ResolvedCall<? extends CallableDescriptor> resolvedCall,
@Nullable CallableDescriptor descriptorToCall, @Nullable CallableDescriptor descriptorToCall,
@NotNull CallType callType,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
super(context); super(context);
this.receiver = receiver; this.receiver = receiver;
this.arguments = arguments; this.arguments = arguments;
this.resolvedCall = resolvedCall; this.resolvedCall = resolvedCall;
this.callType = callType;
if (descriptorToCall != null) { if (descriptorToCall != null) {
this.descriptor = descriptorToCall; this.descriptor = descriptorToCall;
} else { } else {
@@ -234,8 +258,16 @@ public final class CallTranslator extends AbstractTranslator {
return methodCall(); return methodCall();
} }
private boolean isExtensionFunctionLiteral() {
boolean isLiteral = descriptor instanceof VariableAsFunctionDescriptor
|| descriptor instanceof ExpressionAsFunctionDescriptor;
return isExtensionFunction() && isLiteral;
}
@NotNull @NotNull
private JsExpression extensionFunctionLiteralCall() { private JsExpression extensionFunctionLiteralCall() {
//TODO: call type
receiver = getExtensionFunctionCallReceiver(); receiver = getExtensionFunctionCallReceiver();
List<JsExpression> callArguments = generateExtensionCallArgumentList(); List<JsExpression> callArguments = generateExtensionCallArgumentList();
JsInvocation callMethodInvocation = generateCallMethodInvocation(); JsInvocation callMethodInvocation = generateCallMethodInvocation();
@@ -247,21 +279,17 @@ public final class CallTranslator extends AbstractTranslator {
JsNameRef callMethodNameRef = AstUtil.newQualifiedNameRef("call"); JsNameRef callMethodNameRef = AstUtil.newQualifiedNameRef("call");
JsInvocation callMethodInvocation = new JsInvocation(); JsInvocation callMethodInvocation = new JsInvocation();
callMethodInvocation.setQualifier(callMethodNameRef); callMethodInvocation.setQualifier(callMethodNameRef);
AstUtil.setQualifier(callMethodInvocation, calleeReference()); AstUtil.setQualifier(callMethodInvocation, callParameters().functionReference);
return callMethodInvocation; return callMethodInvocation;
} }
private boolean isExtensionFunctionLiteral() {
boolean isLiteral = descriptor instanceof VariableAsFunctionDescriptor
|| descriptor instanceof ExpressionAsFunctionDescriptor;
return isExtensionFunction() && isLiteral;
}
@NotNull @NotNull
private JsExpression extensionFunctionCall() { private JsExpression extensionFunctionCall() {
receiver = getExtensionFunctionCallReceiver(); receiver = getExtensionFunctionCallReceiver();
List<JsExpression> argumentList = generateExtensionCallArgumentList(); List<JsExpression> argumentList = generateExtensionCallArgumentList();
return AstUtil.newInvocation(calleeReference(), argumentList); JsExpression functionReference = callParameters().functionReference;
AstUtil.setQualifier(functionReference, callParameters().receiver);
return AstUtil.newInvocation(functionReference, argumentList);
} }
@NotNull @NotNull
@@ -295,13 +323,24 @@ public final class CallTranslator extends AbstractTranslator {
assert descriptor instanceof FunctionDescriptor; assert descriptor instanceof FunctionDescriptor;
FunctionIntrinsic functionIntrinsic = FunctionIntrinsic functionIntrinsic =
context().intrinsics().getFunctionIntrinsic((FunctionDescriptor) descriptor); context().intrinsics().getFunctionIntrinsic((FunctionDescriptor) descriptor);
JsExpression receiverExpression = thisObject(); JsExpression receiverExpression = resolveThisObject(/*do not get qualifier*/false);
return functionIntrinsic.apply(receiverExpression, arguments, context()); return functionIntrinsic.apply(receiverExpression, arguments, context());
} }
@NotNull @NotNull
private JsInvocation methodCall() { private JsExpression methodCall() {
return AstUtil.newInvocation(calleeReference(), arguments); final CallParameters callParameters = callParameters();
return callType.constructCall(callParameters.receiver, new CallType.CallConstructor() {
@NotNull
@Override
public JsExpression construct(@Nullable JsExpression receiver) {
JsExpression functionReference = callParameters.functionReference;
if (receiver != null) {
AstUtil.setQualifier(functionReference, receiver);
}
return AstUtil.newInvocation(functionReference, arguments);
}
}, context());
} }
private boolean isConstructor() { private boolean isConstructor() {
@@ -313,54 +352,59 @@ public final class CallTranslator extends AbstractTranslator {
} }
@NotNull @NotNull
private JsExpression calleeReference() { private CallParameters callParameters() {
if (callee != null) { if (callee != null) {
return callee; return new CallParameters(null, callee);
} }
if (isVariableDescriptor(descriptor)) { JsExpression thisObject = resolveThisObject(/*just get qualifier if null*/ true);
//TODO: write tests on this cases JsExpression functionReference = functionReference();
VariableDescriptor variableDescriptor = return new CallParameters(thisObject, functionReference);
getVariableDescriptorForVariableAsFunction((VariableAsFunctionDescriptor) descriptor); }
if (variableDescriptor instanceof PropertyDescriptor) {
return getterCall((PropertyDescriptor) variableDescriptor); @NotNull
} private JsExpression functionReference() {
return qualifiedMethodReference(variableDescriptor); if (!isVariableAsFunction(descriptor)) {
return ReferenceTranslator.translateAsLocalNameReference(descriptor, context());
} }
return qualifiedMethodReference(descriptor); VariableDescriptor variableDescriptor =
getVariableDescriptorForVariableAsFunction((VariableAsFunctionDescriptor) descriptor);
if (variableDescriptor instanceof PropertyDescriptor) {
return getterCall((PropertyDescriptor) variableDescriptor);
}
return ReferenceTranslator.translateAsLocalNameReference(variableDescriptor, context());
} }
@NotNull @NotNull
private JsExpression getterCall(@NotNull PropertyDescriptor variableDescriptor) { private JsExpression getterCall(@NotNull PropertyDescriptor variableDescriptor) {
//TODO: call type?
return PropertyAccessTranslator.translateAsPropertyGetterCall(variableDescriptor, resolvedCall, context()); return PropertyAccessTranslator.translateAsPropertyGetterCall(variableDescriptor, resolvedCall, context());
} }
@NotNull @NotNull
private JsExpression qualifiedMethodReference(@NotNull DeclarationDescriptor descriptor) { private JsExpression translateAsFunctionWithNoThisObject(@NotNull DeclarationDescriptor descriptor) {
JsExpression thisObject = thisObject(); return ReferenceTranslator.translateAsFQReference(descriptor, context());
if (thisObject == null) {
return ReferenceTranslator.translateAsFQReference(descriptor, context());
}
JsExpression methodReference = ReferenceTranslator.translateAsLocalNameReference(descriptor, context());
AstUtil.setQualifier(methodReference, thisObject);
return methodReference;
} }
//TODO: refactor
@Nullable @Nullable
private JsExpression thisObject() { private JsExpression resolveThisObject(boolean getQualifierIfNull) {
if (receiver != null) { if (receiver != null) {
return receiver; return receiver;
} }
ReceiverDescriptor thisObject = resolvedCall.getThisObject(); ReceiverDescriptor thisObject = resolvedCall.getThisObject();
if (!thisObject.exists()) { if (thisObject.exists()) {
return null; DeclarationDescriptor expectedThisDescriptor = getDeclarationDescriptorForReceiver(thisObject);
return TranslationUtils.getThisObject(context(), expectedThisDescriptor);
} }
DeclarationDescriptor expectedThisDescriptor = getDeclarationDescriptorForReceiver(thisObject); if (getQualifierIfNull) {
return TranslationUtils.getThisObject(context(), expectedThisDescriptor); return context().getQualifierForDescriptor(descriptor);
}
return null;
} }
@NotNull @NotNull
private JsExpression constructorCall() { private JsExpression constructorCall() {
JsExpression constructorReference = ReferenceTranslator.translateAsFQReference(descriptor, context()); JsExpression constructorReference = translateAsFunctionWithNoThisObject(descriptor);
JsNew constructorCall = new JsNew(constructorReference); JsNew constructorCall = new JsNew(constructorReference);
constructorCall.setArguments(arguments); constructorCall.setArguments(arguments);
return constructorCall; return constructorCall;
@@ -6,6 +6,7 @@ import com.google.dart.compiler.backend.js.ast.JsExpression;
import com.google.dart.compiler.backend.js.ast.JsNullLiteral; import com.google.dart.compiler.backend.js.ast.JsNullLiteral;
import com.google.dart.compiler.util.AstUtil; import com.google.dart.compiler.util.AstUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.psi.JetDotQualifiedExpression; import org.jetbrains.jet.lang.psi.JetDotQualifiedExpression;
import org.jetbrains.jet.lang.psi.JetQualifiedExpression; import org.jetbrains.jet.lang.psi.JetQualifiedExpression;
import org.jetbrains.jet.lang.psi.JetSafeQualifiedExpression; import org.jetbrains.jet.lang.psi.JetSafeQualifiedExpression;
@@ -21,20 +22,16 @@ public enum CallType {
SAFE { SAFE {
@NotNull @NotNull
@Override @Override
JsExpression mutateSelector(@NotNull JsExpression selector, @NotNull TranslationContext context) { JsExpression constructCall(@Nullable JsExpression receiver, @NotNull CallConstructor constructor,
TemporaryVariable temporaryVariable = context.declareTemporary(selector); @NotNull TranslationContext context) {
return newSequence(temporaryVariable.assignmentExpression(), temporaryVariable.nameReference()); assert receiver != null;
} TemporaryVariable temporaryVariable = context.declareTemporary(receiver);
@NotNull
@Override
JsExpression mutateExpression(@NotNull JsExpression mutatedSelector,
@NotNull JsExpression expressionWithMutatedSelector,
@NotNull TranslationContext context) {
JsNullLiteral nullLiteral = context.program().getNullLiteral(); JsNullLiteral nullLiteral = context.program().getNullLiteral();
//TODO: find similar not null checks //TODO: find similar not null checks
JsBinaryOperation notNullCheck = AstUtil.notEqual(expressionWithMutatedSelector, nullLiteral); JsBinaryOperation notNullCheck = AstUtil.notEqual(temporaryVariable.nameReference(), nullLiteral);
return new JsConditional(notNullCheck, expressionWithMutatedSelector, nullLiteral); JsConditional callMethodIfNotNullElseNull =
new JsConditional(notNullCheck, constructor.construct(temporaryVariable.nameReference()), nullLiteral);
return newSequence(temporaryVariable.assignmentExpression(), callMethodIfNotNullElseNull);
} }
}, },
//TODO: bang qualifier is not implemented in frontend for now //TODO: bang qualifier is not implemented in frontend for now
@@ -42,31 +39,18 @@ public enum CallType {
NORMAL { NORMAL {
@NotNull @NotNull
@Override @Override
JsExpression mutateSelector(@NotNull JsExpression selector, @NotNull TranslationContext context) { JsExpression constructCall(@Nullable JsExpression receiver, @NotNull CallConstructor constructor,
// do not mutate @NotNull TranslationContext context) {
return selector; return constructor.construct(receiver);
}
@NotNull
@Override
JsExpression mutateExpression(@NotNull JsExpression mutatedSelector,
@NotNull JsExpression expression,
@NotNull TranslationContext context) {
// do not mutate
return expression;
} }
}; };
@NotNull @NotNull
abstract JsExpression mutateSelector(@NotNull JsExpression selector, @NotNull TranslationContext context); abstract JsExpression constructCall(@Nullable JsExpression receiver, @NotNull CallConstructor constructor,
@NotNull TranslationContext context);
@NotNull @NotNull
abstract JsExpression mutateExpression(@NotNull JsExpression mutatedSelector, public static CallType getCallTypeForQualifiedExpression(@NotNull JetQualifiedExpression expression) {
@NotNull JsExpression expression,
@NotNull TranslationContext context);
@NotNull
public static CallType getCallTypeForQualifierExpression(@NotNull JetQualifiedExpression expression) {
if (expression instanceof JetSafeQualifiedExpression) { if (expression instanceof JetSafeQualifiedExpression) {
return SAFE; return SAFE;
} }
@@ -74,4 +58,9 @@ public enum CallType {
return NORMAL; return NORMAL;
} }
public interface CallConstructor {
@NotNull
JsExpression construct(@Nullable JsExpression receiver);
}
} }
@@ -37,24 +37,15 @@ public final class KotlinPropertyAccessTranslator extends PropertyAccessTranslat
@Override @Override
@NotNull @NotNull
public JsExpression translateAsGet() { public JsExpression translateAsGet() {
return getterCall(); return CallTranslator.translate(qualifier, resolvedCall, propertyDescriptor.getGetter(), getCallType(), context());
}
@NotNull
private JsExpression getterCall() {
return CallTranslator.translate(qualifier, resolvedCall, propertyDescriptor.getGetter(), context());
} }
@Override @Override
@NotNull @NotNull
public JsExpression translateAsSet(@NotNull JsExpression toSetTo) { public JsExpression translateAsSet(@NotNull JsExpression toSetTo) {
return setterCall(toSetTo); return CallTranslator.translate(qualifier, Arrays.asList(toSetTo),
resolvedCall, propertyDescriptor.getSetter(), getCallType(), context());
} }
@NotNull
private JsExpression setterCall(@NotNull JsExpression toSetTo) {
return CallTranslator.translate(qualifier, Arrays.asList(toSetTo),
resolvedCall, propertyDescriptor.getSetter(), context());
}
} }
@@ -36,13 +36,18 @@ public abstract class PropertyAccessTranslator extends AccessTranslator {
@NotNull @NotNull
public static PropertyAccessTranslator newInstance(@NotNull JetSimpleNameExpression expression, public static PropertyAccessTranslator newInstance(@NotNull JetSimpleNameExpression expression,
@Nullable JsExpression qualifier, @Nullable JsExpression qualifier,
@NotNull CallType callType,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
PropertyAccessTranslator result;
PropertyDescriptor propertyDescriptor = getPropertyDescriptor(expression, context); PropertyDescriptor propertyDescriptor = getPropertyDescriptor(expression, context);
if (isNativeObject(propertyDescriptor) || isBackingFieldReference(expression)) { if (isNativeObject(propertyDescriptor) || isBackingFieldReference(expression)) {
return new NativePropertyAccessTranslator(propertyDescriptor, qualifier, context); result = new NativePropertyAccessTranslator(propertyDescriptor, qualifier, context);
} else {
ResolvedCall<?> resolvedCall = getResolvedCall(context.bindingContext(), expression);
result = new KotlinPropertyAccessTranslator(propertyDescriptor, qualifier, resolvedCall, context);
} }
ResolvedCall<?> resolvedCall = getResolvedCall(context.bindingContext(), expression); result.setCallType(callType);
return new KotlinPropertyAccessTranslator(propertyDescriptor, qualifier, resolvedCall, context); return result;
} }
@NotNull @NotNull
@@ -67,8 +72,9 @@ public abstract class PropertyAccessTranslator extends AccessTranslator {
@NotNull @NotNull
public static JsExpression translateAsPropertyGetterCall(@NotNull JetSimpleNameExpression expression, public static JsExpression translateAsPropertyGetterCall(@NotNull JetSimpleNameExpression expression,
@Nullable JsExpression qualifier, @Nullable JsExpression qualifier,
@NotNull CallType callType,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
return (newInstance(expression, qualifier, context)) return (newInstance(expression, qualifier, callType, context))
.translateAsGet(); .translateAsGet();
} }
@@ -1,18 +1,16 @@
package org.jetbrains.k2js.translate.reference; package org.jetbrains.k2js.translate.reference;
import com.google.dart.compiler.backend.js.ast.JsConditional;
import com.google.dart.compiler.backend.js.ast.JsExpression; import com.google.dart.compiler.backend.js.ast.JsExpression;
import com.google.dart.compiler.backend.js.ast.JsNullLiteral;
import com.google.dart.compiler.util.AstUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.psi.JetCallExpression;
import org.jetbrains.k2js.translate.context.TemporaryVariable; import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetQualifiedExpression;
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
import org.jetbrains.k2js.translate.context.TranslationContext; import org.jetbrains.k2js.translate.context.TranslationContext;
import static org.jetbrains.k2js.translate.general.Translation.translateAsExpression; import static org.jetbrains.k2js.translate.general.Translation.translateAsExpression;
import static org.jetbrains.k2js.translate.utils.PsiUtils.getNotNullSimpleNameSelector; import static org.jetbrains.k2js.translate.utils.PsiUtils.getNotNullSimpleNameSelector;
import static org.jetbrains.k2js.translate.utils.PsiUtils.getSelector; import static org.jetbrains.k2js.translate.utils.PsiUtils.getSelector;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.notNullCheck;
/** /**
* @author Pavel Talanov * @author Pavel Talanov
@@ -28,44 +26,38 @@ public final class QualifiedExpressionTranslator {
JsExpression receiver = translateReceiver(expression, context); JsExpression receiver = translateReceiver(expression, context);
PropertyAccessTranslator result = PropertyAccessTranslator result =
PropertyAccessTranslator.newInstance(getNotNullSimpleNameSelector(expression), receiver, context); PropertyAccessTranslator.newInstance(getNotNullSimpleNameSelector(expression), receiver,
result.setCallType(CallType.getCallTypeForQualifierExpression(expression)); CallType.getCallTypeForQualifiedExpression(expression), context);
result.setCallType(CallType.getCallTypeForQualifiedExpression(expression));
return result; return result;
} }
@NotNull @NotNull
public static JsExpression translateSafeQualifiedExpression(@NotNull JetSafeQualifiedExpression expression, public static JsExpression translateQualifiedExpression(@NotNull JetQualifiedExpression expression,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
TemporaryVariable receiver = context.declareTemporary(translateReceiver(expression, context));
JsNullLiteral nullLiteral = context.program().getNullLiteral();
JetExpression selector = getSelector(expression);
JsExpression thenExpression = translate(receiver.nameReference(), selector, context);
JsConditional callMethodIfNotNullConditional
= new JsConditional(notNullCheck(context, receiver.nameReference()), thenExpression, nullLiteral);
return AstUtil.newSequence(receiver.assignmentExpression(), callMethodIfNotNullConditional);
}
@NotNull
public static JsExpression translateDotQualifiedExpression(@NotNull JetDotQualifiedExpression expression,
@NotNull TranslationContext context) {
JsExpression receiver = translateReceiver(expression, context); JsExpression receiver = translateReceiver(expression, context);
JetExpression selector = getSelector(expression); JetExpression selector = getSelector(expression);
return translate(receiver, selector, context); CallType callType = CallType.getCallTypeForQualifiedExpression(expression);
return dispatchToCorrectTranslator(receiver, selector, callType, context);
} }
@NotNull @NotNull
private static JsExpression translate(@NotNull JsExpression receiver, @NotNull JetExpression selector, private static JsExpression dispatchToCorrectTranslator(@NotNull JsExpression receiver,
@NotNull TranslationContext context) { @NotNull JetExpression selector,
@NotNull CallType callType,
@NotNull TranslationContext context) {
if (PropertyAccessTranslator.canBePropertyGetterCall(selector, context)) { if (PropertyAccessTranslator.canBePropertyGetterCall(selector, context)) {
assert selector instanceof JetSimpleNameExpression : "Selectors for properties must be simple names.";
return PropertyAccessTranslator.translateAsPropertyGetterCall return PropertyAccessTranslator.translateAsPropertyGetterCall
((JetSimpleNameExpression) selector, receiver, context); ((JetSimpleNameExpression) selector, receiver, callType, context);
} }
if (selector instanceof JetCallExpression) { if (selector instanceof JetCallExpression) {
return CallTranslator.translate((JetCallExpression) selector, receiver, context); return CallTranslator.translate((JetCallExpression) selector, receiver, callType, context);
} }
throw new AssertionError("Unexpected qualified expression"); throw new AssertionError("Unexpected qualified expression");
} }
//TODO: if has duplications
@NotNull @NotNull
private static JsExpression translateReceiver(@NotNull JetQualifiedExpression expression, private static JsExpression translateReceiver(@NotNull JetQualifiedExpression expression,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
@@ -19,7 +19,7 @@ public final class ReferenceTranslator {
public static JsExpression translateSimpleName(@NotNull JetSimpleNameExpression expression, public static JsExpression translateSimpleName(@NotNull JetSimpleNameExpression expression,
@NotNull TranslationContext context) { @NotNull TranslationContext context) {
if (PropertyAccessTranslator.canBePropertyGetterCall(expression, context)) { if (PropertyAccessTranslator.canBePropertyGetterCall(expression, context)) {
return PropertyAccessTranslator.translateAsPropertyGetterCall(expression, null, context); return PropertyAccessTranslator.translateAsPropertyGetterCall(expression, null, CallType.NORMAL, context);
} }
DeclarationDescriptor referencedDescriptor = DeclarationDescriptor referencedDescriptor =
getDescriptorForReferenceExpression(context.bindingContext(), expression); getDescriptorForReferenceExpression(context.bindingContext(), expression);
@@ -16,7 +16,7 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getVariableDescriptorForVariableAsFunction; import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getVariableDescriptorForVariableAsFunction;
import static org.jetbrains.k2js.translate.utils.DescriptorUtils.isVariableDescriptor; import static org.jetbrains.k2js.translate.utils.DescriptorUtils.isVariableAsFunction;
/** /**
* @author Pavel Talanov * @author Pavel Talanov
@@ -191,7 +191,7 @@ public final class BindingUtils {
private static DeclarationDescriptor getNullableDescriptorForReferenceExpression(@NotNull BindingContext context, private static DeclarationDescriptor getNullableDescriptorForReferenceExpression(@NotNull BindingContext context,
@NotNull JetReferenceExpression reference) { @NotNull JetReferenceExpression reference) {
DeclarationDescriptor referencedDescriptor = context.get(BindingContext.REFERENCE_TARGET, reference); DeclarationDescriptor referencedDescriptor = context.get(BindingContext.REFERENCE_TARGET, reference);
if (isVariableDescriptor(referencedDescriptor)) { if (isVariableAsFunction(referencedDescriptor)) {
assert referencedDescriptor != null; assert referencedDescriptor != null;
return getVariableDescriptorForVariableAsFunction((VariableAsFunctionDescriptor) referencedDescriptor); return getVariableDescriptorForVariableAsFunction((VariableAsFunctionDescriptor) referencedDescriptor);
} }
@@ -111,7 +111,7 @@ public final class DescriptorUtils {
} }
public static boolean isVariableDescriptor(@Nullable DeclarationDescriptor referencedDescriptor) { public static boolean isVariableAsFunction(@Nullable DeclarationDescriptor referencedDescriptor) {
return referencedDescriptor instanceof VariableAsFunctionDescriptor; return referencedDescriptor instanceof VariableAsFunctionDescriptor;
} }
@@ -4,12 +4,15 @@ class A() {
var c = 3 var c = 3
} }
fun box() { fun box() : Boolean {
var a1 : A? = A() var a1 : A? = A()
var a2 : A? = null var a2 : A? = null
a1?.c++ a1?.c = 4
a2?.c++ a2?.c = 5
if (a1?.c != 4) {
return false;
}
a2 = a1 a2 = a1
a2?.c++ a2?.c = 5
return (a2?.c == 5) return (a2?.c == 5) && (a1?.c == 5)
} }
@@ -2,7 +2,6 @@ package foo
class A() { class A() {
fun doSomething() { fun doSomething() {
} }
} }
@@ -14,7 +14,7 @@ public final class MiscTest extends AbstractExpressionTest {
return MAIN; return MAIN;
} }
public void testNamespaceProperties() throws Exception { public void testLocalPropertys() throws Exception {
testFunctionOutput("localProperty.jet", "foo", "box", 50); testFunctionOutput("localProperty.jet", "foo", "box", 50);
} }