Big refactoring of CallTranslator.

This commit is contained in:
Pavel V. Talanov
2012-03-28 18:36:57 +04:00
parent 9b2dc6f584
commit 003182f499
6 changed files with 253 additions and 102 deletions
@@ -0,0 +1,59 @@
/*
* Copyright 2010-2012 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.k2js.translate.reference;
import com.google.dart.compiler.backend.js.ast.JsExpression;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Pavel Talanov
*/
public final class CallParameters {
@Nullable
private JsExpression receiver;
@NotNull
private JsExpression functionReference;
@Nullable
private JsExpression thisObject;
public CallParameters(@Nullable JsExpression receiver,
@NotNull JsExpression functionReference,
@Nullable JsExpression thisObject) {
this.receiver = receiver;
this.functionReference = functionReference;
this.thisObject = thisObject;
}
@NotNull
public JsExpression getFunctionReference() {
return functionReference;
}
@Nullable
public JsExpression getThisObject() {
return thisObject;
}
@Nullable
public JsExpression getReceiver() {
return receiver;
}
}
@@ -0,0 +1,120 @@
/*
* Copyright 2010-2012 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.k2js.translate.reference;
import com.google.dart.compiler.backend.js.ast.JsExpression;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.resolve.calls.ResolvedCall;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.utils.TranslationUtils;
import static org.jetbrains.k2js.translate.utils.DescriptorUtils.*;
/**
* @author Pavel Talanov
*/
public final class CallParametersResolver {
public static CallParameters resolveCallParameters(@Nullable JsExpression qualifier,
@Nullable JsExpression callee,
@NotNull CallableDescriptor descriptor,
@NotNull ResolvedCall<? extends CallableDescriptor> call,
@NotNull TranslationContext context) {
return (new CallParametersResolver(qualifier, callee, descriptor, call, context)).resolve();
}
// the actual qualifier for the call at the call site
@Nullable
private final JsExpression qualifier;
@Nullable
private final JsExpression callee;
@NotNull
private final CallableDescriptor descriptor;
@NotNull
private final TranslationContext context;
@NotNull
private final ResolvedCall<? extends CallableDescriptor> resolvedCall;
private final boolean isExtensionCall;
public CallParametersResolver(@Nullable JsExpression qualifier,
@Nullable JsExpression callee,
@NotNull CallableDescriptor descriptor,
@NotNull ResolvedCall<? extends CallableDescriptor> call,
@NotNull TranslationContext context) {
this.qualifier = qualifier;
this.callee = callee;
this.descriptor = descriptor;
this.context = context;
this.resolvedCall = call;
this.isExtensionCall = resolvedCall.getReceiverArgument().exists();
}
@NotNull
private CallParameters resolve() {
JsExpression receiver = isExtensionCall ? getExtensionFunctionCallReceiver() : null;
JsExpression functionReference = getFunctionReference();
JsExpression thisObject = getThisObject();
return new CallParameters(receiver, functionReference, thisObject);
}
@NotNull
private JsExpression getFunctionReference() {
if (callee != null) {
return callee;
}
if (!isVariableAsFunction(descriptor)) {
return ReferenceTranslator.translateAsLocalNameReference(descriptor, context);
}
VariableDescriptor variableDescriptor =
getVariableDescriptorForVariableAsFunction((VariableAsFunctionDescriptor)descriptor);
if (variableDescriptor instanceof PropertyDescriptor) {
return getterCall((PropertyDescriptor)variableDescriptor);
}
return ReferenceTranslator.translateAsLocalNameReference(variableDescriptor, context);
}
//TODO: inspect
@NotNull
private JsExpression getterCall(@NotNull PropertyDescriptor variableDescriptor) {
//TODO: call type?
return PropertyAccessTranslator.translateAsPropertyGetterCall(variableDescriptor, resolvedCall, context);
}
@Nullable
private JsExpression getThisObject() {
if (qualifier != null && !isExtensionCall) {
return qualifier;
}
JsExpression result = TranslationUtils.resolveThisObjectForResolvedCall(resolvedCall, context);
if (result != null) {
return result;
}
return null;
}
@NotNull
private JsExpression getExtensionFunctionCallReceiver() {
if (qualifier != null) {
return qualifier;
}
DeclarationDescriptor expectedReceiverDescriptor = getExpectedReceiverDescriptor(descriptor);
assert expectedReceiverDescriptor != null;
return TranslationUtils.getThisObject(context, expectedReceiverDescriptor);
}
}
@@ -23,22 +23,23 @@ import com.google.dart.compiler.backend.js.ast.JsNew;
import com.google.dart.compiler.util.AstUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.VariableAsFunctionDescriptor;
import org.jetbrains.jet.lang.resolve.calls.ExpressionAsFunctionDescriptor;
import org.jetbrains.jet.lang.resolve.calls.ResolvedCall;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.AbstractTranslator;
import org.jetbrains.k2js.translate.intrinsic.Intrinsic;
import org.jetbrains.k2js.translate.utils.AnnotationsUtils;
import org.jetbrains.k2js.translate.utils.TranslationUtils;
import java.util.ArrayList;
import java.util.List;
import static org.jetbrains.k2js.translate.utils.DescriptorUtils.*;
import static org.jetbrains.k2js.translate.reference.CallParametersResolver.resolveCallParameters;
import static org.jetbrains.k2js.translate.utils.DescriptorUtils.isConstructorDescriptor;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.*;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.getThisObject;
/**
* @author Pavel Talanov
@@ -46,24 +47,6 @@ import static org.jetbrains.k2js.translate.utils.TranslationUtils.getThisObject;
//TODO: write tests on calling backing fields as functions
//TODO: think of the refactoring the class
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 final JsExpression receiver;
@NotNull
public final JsExpression functionReference;
}
//NOTE: receiver may mean this object as well
@Nullable
private /*var*/ JsExpression receiver;
@Nullable
private final JsExpression callee;
@@ -79,6 +62,9 @@ public final class CallTranslator extends AbstractTranslator {
@NotNull
private final CallType callType;
@NotNull
private final CallParameters callParameters;
/*package*/ CallTranslator(@Nullable JsExpression receiver, @Nullable JsExpression callee,
@NotNull List<JsExpression> arguments,
@NotNull ResolvedCall<? extends CallableDescriptor> resolvedCall,
@@ -86,12 +72,12 @@ public final class CallTranslator extends AbstractTranslator {
@NotNull CallType callType,
@NotNull TranslationContext context) {
super(context);
this.receiver = receiver;
this.arguments = arguments;
this.resolvedCall = resolvedCall;
this.callType = callType;
this.descriptor = descriptorToCall;
this.callee = callee;
this.callParameters = resolveCallParameters(receiver, callee, descriptor, resolvedCall, context);
}
@NotNull
@@ -114,17 +100,16 @@ public final class CallTranslator extends AbstractTranslator {
if (isExpressionAsFunction()) {
return expressionAsFunctionCall();
}
return methodCall(callParameters(resolveThisObject(/*just get qualifier if null*/ true)));
return methodCall(getThisObjectOrQualifier());
}
private boolean isExpressionAsFunction() {
return callee != null;
}
@NotNull
private JsExpression expressionAsFunctionCall() {
assert callee != null;
CallParameters expressionAsFunctionParameters = new CallParameters(null, callee);
return methodCall(expressionAsFunctionParameters);
return methodCall(null, callee);
}
private boolean isIntrinsic() {
@@ -134,10 +119,8 @@ public final class CallTranslator extends AbstractTranslator {
@NotNull
private JsExpression intrinsicInvocation() {
assert descriptor instanceof FunctionDescriptor;
Intrinsic intrinsic =
context().intrinsics().getFunctionIntrinsic((FunctionDescriptor) descriptor);
JsExpression receiverExpression = resolveThisObject(/*do not get qualifier*/false);
return intrinsic.apply(receiverExpression, arguments, context());
Intrinsic intrinsic = context().intrinsics().getFunctionIntrinsic((FunctionDescriptor)descriptor);
return intrinsic.apply(callParameters.getThisObject(), arguments, context());
}
private boolean isConstructor() {
@@ -163,8 +146,7 @@ public final class CallTranslator extends AbstractTranslator {
@NotNull
private JsExpression nativeExtensionCall() {
receiver = getExtensionFunctionCallReceiver();
return methodCall(callParameters(resolveThisObject(/*just get qualifier if null = */ true)));
return methodCall(callParameters.getReceiver());
}
private boolean isExtensionFunctionLiteral() {
@@ -175,8 +157,7 @@ public final class CallTranslator extends AbstractTranslator {
@NotNull
private JsExpression extensionFunctionLiteralCall() {
JsExpression realReceiver = getExtensionFunctionCallReceiver();
return callType.constructCall(realReceiver, new CallType.CallConstructor() {
return callType.constructCall(callParameters.getReceiver(), new CallType.CallConstructor() {
@NotNull
@Override
public JsExpression construct(@Nullable JsExpression receiver) {
@@ -199,7 +180,7 @@ public final class CallTranslator extends AbstractTranslator {
JsNameRef callMethodNameRef = AstUtil.newQualifiedNameRef("call");
JsInvocation callMethodInvocation = new JsInvocation();
callMethodInvocation.setQualifier(callMethodNameRef);
setQualifier(callMethodInvocation, callParameters(resolveThisObject(/*just get qualifier if null*/ true)).functionReference);
setQualifier(callMethodInvocation, callParameters.getFunctionReference());
return callMethodInvocation;
}
@@ -211,8 +192,7 @@ public final class CallTranslator extends AbstractTranslator {
@NotNull
private JsExpression extensionFunctionCall() {
JsExpression realReceiver = getExtensionFunctionCallReceiver();
return callType.constructCall(realReceiver, new CallType.CallConstructor() {
return callType.constructCall(callParameters.getReceiver(), new CallType.CallConstructor() {
@NotNull
@Override
public JsExpression construct(@Nullable JsExpression receiver) {
@@ -222,92 +202,47 @@ public final class CallTranslator extends AbstractTranslator {
}, context());
}
@NotNull
private JsExpression getExtensionFunctionCallReceiver() {
if (receiver != null) {
JsExpression result = receiver;
//Now the rest of the code can work as if it was simple method invocation
receiver = null;
return result;
}
DeclarationDescriptor expectedReceiverDescriptor = getExpectedReceiverDescriptor(descriptor);
assert expectedReceiverDescriptor != null;
return getThisObject(context(), expectedReceiverDescriptor);
}
@NotNull
private JsExpression constructExtensionFunctionCall(@NotNull JsExpression receiver) {
List<JsExpression> argumentList = generateExtensionCallArgumentList(receiver);
CallParameters callParameters = callParameters(resolveThisObject(/*just get qualifier if null*/ true));
JsExpression functionReference = callParameters.functionReference;
setQualifier(functionReference, callParameters.receiver);
JsExpression functionReference = callParameters.getFunctionReference();
setQualifier(functionReference, getThisObjectOrQualifier());
return newInvocation(functionReference, argumentList);
}
@NotNull
private List<JsExpression> generateExtensionCallArgumentList(@NotNull JsExpression receiver) {
List<JsExpression> argumentList = new ArrayList<JsExpression>();
assert this.receiver == null : "Should be null at that point";
argumentList.add(receiver);
argumentList.addAll(arguments);
return argumentList;
}
@NotNull
private JsExpression methodCall(@NotNull final CallParameters callParameters) {
return callType.constructCall(callParameters.receiver, new CallType.CallConstructor() {
private JsExpression methodCall(@Nullable JsExpression receiver) {
return methodCall(receiver, callParameters.getFunctionReference());
}
@NotNull
private JsExpression methodCall(@Nullable JsExpression receiver, final JsExpression callee) {
return callType.constructCall(receiver, new CallType.CallConstructor() {
@NotNull
@Override
public JsExpression construct(@Nullable JsExpression receiver) {
JsExpression functionReference = callParameters.functionReference;
if (receiver != null) {
setQualifier(functionReference, receiver);
setQualifier(callee, receiver);
}
return newInvocation(functionReference, arguments);
return newInvocation(callee, arguments);
}
}, context());
}
@NotNull
private CallParameters callParameters(@Nullable JsExpression receiver) {
JsExpression functionReference = functionReference();
return new CallParameters(receiver, functionReference);
}
//TODO: inspect
@NotNull
private JsExpression functionReference() {
if (!isVariableAsFunction(descriptor)) {
return ReferenceTranslator.translateAsLocalNameReference(descriptor, context());
}
VariableDescriptor variableDescriptor =
getVariableDescriptorForVariableAsFunction((VariableAsFunctionDescriptor) descriptor);
if (variableDescriptor instanceof PropertyDescriptor) {
return getterCall((PropertyDescriptor) variableDescriptor);
}
return ReferenceTranslator.translateAsLocalNameReference(variableDescriptor, context());
}
@NotNull
private JsExpression getterCall(@NotNull PropertyDescriptor variableDescriptor) {
//TODO: call type?
return PropertyAccessTranslator.translateAsPropertyGetterCall(variableDescriptor, resolvedCall, context());
}
//TODO: refactor
@Nullable
private JsExpression resolveThisObject(boolean getQualifierIfNull) {
if (receiver != null) {
return receiver;
JsExpression getThisObjectOrQualifier() {
JsExpression thisObject = callParameters.getThisObject();
if (thisObject != null) {
return thisObject;
}
ReceiverDescriptor thisObject = resolvedCall.getThisObject();
if (thisObject.exists()) {
DeclarationDescriptor expectedThisDescriptor = getDeclarationDescriptorForReceiver(thisObject);
return TranslationUtils.getThisObject(context(), expectedThisDescriptor);
}
if (getQualifierIfNull) {
return context().getQualifierForDescriptor(descriptor);
}
return null;
return context().getQualifierForDescriptor(descriptor);
}
}
@@ -32,6 +32,7 @@ import org.jetbrains.k2js.translate.context.TemporaryVariable;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.utils.DescriptorUtils;
import org.jetbrains.k2js.translate.utils.JsAstUtils;
import org.jetbrains.k2js.translate.utils.TranslationUtils;
import org.jetbrains.k2js.translate.utils.mutator.LastExpressionMutator;
import org.jetbrains.k2js.translate.utils.mutator.Mutator;
@@ -115,14 +116,23 @@ public final class InlinedCallExpressionTranslator extends AbstractCallExpressio
if (classDescriptorForMethod == null) {
return contextForInlining;
}
//TODO
TranslationContext contextWithAliasForThisExpression = contextForInlining;
TemporaryVariable aliasForThis = contextForInlining.declareTemporary(receiver);
TemporaryVariable aliasForThis = contextWithAliasForThisExpression.declareTemporary(getReceiver());
contextWithAliasForThisExpression = contextForInlining.innerContextWithThisAliased(classDescriptorForMethod, aliasForThis.name());
contextWithAliasForThisExpression.addStatementToCurrentBlock(aliasForThis.assignmentExpression().makeStmt());
return contextWithAliasForThisExpression;
}
@NotNull
private JsExpression getReceiver() {
if (receiver != null) {
return receiver;
}
JsExpression result = TranslationUtils.resolveThisObjectForResolvedCall(resolvedCall, context());
assert result != null;
return result;
}
@NotNull
private TemporaryVariable createAliasForArgument(@NotNull ValueParameterDescriptor parameterDescriptor) {
ResolvedValueArgument actualArgument = resolvedCall.getValueArgumentsByIndex().get(parameterDescriptor.getIndex());
@@ -21,6 +21,8 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.calls.ResolvedCall;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.Translation;
import org.jetbrains.k2js.translate.intrinsic.Intrinsic;
@@ -32,6 +34,7 @@ import java.util.List;
import static com.google.dart.compiler.util.AstUtil.newAssignment;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptorForOperationExpression;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getPropertyDescriptor;
import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getDeclarationDescriptorForReceiver;
import static org.jetbrains.k2js.translate.utils.DescriptorUtils.getExpectedReceiverDescriptor;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.*;
@@ -207,4 +210,15 @@ public final class TranslationUtils {
PropertyDescriptor propertyDescriptor = getPropertyDescriptor(context.bindingContext(), property);
return assignmentToBackingField(context, propertyDescriptor, initExpression);
}
@Nullable
public static JsExpression resolveThisObjectForResolvedCall(@NotNull ResolvedCall<?> call,
@NotNull TranslationContext context) {
ReceiverDescriptor thisObject = call.getThisObject();
if (!thisObject.exists()) {
return null;
}
DeclarationDescriptor expectedThisDescriptor = getDeclarationDescriptorForReceiver(thisObject);
return TranslationUtils.getThisObject(context, expectedThisDescriptor);
}
}
@@ -0,0 +1,13 @@
package foo
fun box() : Boolean {
return !(A(false).wrap()) and A(true).wrap()
}
class A(val a : Boolean) {
inline fun myInlineMethod() : Boolean {
return a
}
fun wrap() = myInlineMethod()
}