From 5b49869cac78f7787c90d88c4e77e988f5cd5a58 Mon Sep 17 00:00:00 2001 From: Svetlana Isakova Date: Mon, 30 Apr 2012 15:21:13 +0400 Subject: [PATCH] changed CallTransformer responsible for resolving second call ('invoke') for 'variable as function' case --- .../calls/CallTransformationStrategy.java | 118 --------- .../lang/resolve/calls/CallTransformer.java | 239 ++++++++++++++++++ 2 files changed, 239 insertions(+), 118 deletions(-) delete mode 100644 compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallTransformationStrategy.java create mode 100644 compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallTransformer.java diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallTransformationStrategy.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallTransformationStrategy.java deleted file mode 100644 index 5fc68364ae5..00000000000 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallTransformationStrategy.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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.jet.lang.resolve.calls; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.jet.lang.descriptors.CallableDescriptor; -import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; -import org.jetbrains.jet.lang.descriptors.VariableDescriptor; -import org.jetbrains.jet.lang.psi.*; -import org.jetbrains.jet.lang.resolve.BindingTrace; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -/** - * @author svtk - */ -public interface CallTransformationStrategy { - - @NotNull - CallResolutionContext createCallContext(@NotNull ResolutionCandidate candidate, - @NotNull ResolutionTask task, - @NotNull BindingTrace trace, - @NotNull TracingStrategy tracing); - - @NotNull - Collection> transformResultCall(@NotNull CallResolutionContext callResolutionContext, - @NotNull CallResolver callResolver, - @NotNull ResolutionTask task); - - CallTransformationStrategy - PROPERTY_CALL_TRANSFORMATION_STRATEGY = new CallTransformationStrategy() { - @NotNull - @Override - public CallResolutionContext createCallContext(@NotNull ResolutionCandidate candidate, - @NotNull ResolutionTask task, @NotNull BindingTrace trace, @NotNull TracingStrategy tracing) { - ResolvedCallImpl candidateCall = ResolvedCallImpl.create(candidate); - return new CallResolutionContext(candidateCall, task, trace, tracing); - } - - @NotNull - @Override - public Collection> transformResultCall(@NotNull CallResolutionContext context, - @NotNull CallResolver callResolver, @NotNull ResolutionTask task) { - return Collections.singleton(context.candidateCall); - } - }; - - CallTransformationStrategy - FUNCTION_CALL_TRANSFORMATION_STRATEGY = new CallTransformationStrategy() { - @NotNull - @Override - public CallResolutionContext createCallContext(@NotNull ResolutionCandidate candidate, - @NotNull ResolutionTask task, - @NotNull BindingTrace trace, - @NotNull TracingStrategy tracing) { - if (candidate.getDescriptor() instanceof FunctionDescriptor) { - return new CallResolutionContext(ResolvedCallImpl.create(candidate), task, trace, tracing); - } - assert candidate.getDescriptor() instanceof VariableDescriptor; - Call propertyCall = new DelegatingCall(task.call) { - @Override - public JetValueArgumentList getValueArgumentList() { - return null; - } - - @NotNull - @Override - public List getFunctionLiteralArguments() { - return Collections.emptyList(); - } - - @NotNull - @Override - public List getTypeArguments() { - return Collections.emptyList(); - } - - @Override - public JetTypeArgumentList getTypeArgumentList() { - return null; - } - }; - return new CallResolutionContext(ResolvedCallImpl.create(candidate), task, trace, tracing, propertyCall); - } - - @NotNull - @Override - public Collection> transformResultCall(@NotNull CallResolutionContext context, - @NotNull CallResolver callResolver, @NotNull ResolutionTask task) { - FunctionDescriptor descriptor = context.candidateCall.getCandidateDescriptor(); - if (descriptor instanceof FunctionDescriptor) { - return Collections.singleton(context.candidateCall); - } - assert descriptor instanceof VariableDescriptor; - BasicResolutionContext basicResolutionContext = - BasicResolutionContext.create(context.trace, context.scope, task.call, context.expectedType, context.dataFlowInfo); - OverloadResolutionResults results = - callResolver.resolveCallWithGivenName(basicResolutionContext, task.reference, "invoke"); - return ((OverloadResolutionResultsImpl)results).getResultingCalls(); - } - }; -} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallTransformer.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallTransformer.java new file mode 100644 index 00000000000..adda74ced5e --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallTransformer.java @@ -0,0 +1,239 @@ +/* + * 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.jet.lang.resolve.calls; + +import com.google.common.base.Function; +import com.google.common.collect.Collections2; +import com.google.common.collect.Lists; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.CallableDescriptor; +import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; +import org.jetbrains.jet.lang.descriptors.VariableDescriptor; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lang.resolve.*; +import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver; +import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; +import org.jetbrains.jet.lang.types.JetType; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * CallTransformer treats specially 'variable as function' call case, other cases keeps unchanged (base realization). + * + * For the call 'b.foo(1)' where foo is a variable that has method 'invoke' (for example of function type) + * CallTransformer creates two contexts, two calls in each, and performs second ('invoke') call resolution: + * + * context#1. calls: 'b.foo' 'invoke(1)' + * context#2. calls: 'foo' 'b.invoke(1)' + * + * If success VariableAsFunctionResolvedCall is created. + * + * @author svtk + */ +public class CallTransformer { + private CallTransformer() {} + + /** + * Returns two contexts for 'variable as function' case (in FUNCTION_CALL_TRANSFORMER), one context otherwise + */ + @NotNull + public Collection> createCallContexts(@NotNull ResolutionCandidate candidate, + @NotNull ResolutionTask task, + @NotNull TemporaryBindingTrace temporaryTrace) { + + ResolvedCallImpl candidateCall = ResolvedCallImpl.create(candidate, temporaryTrace); + return Collections.singleton(CallResolutionContext.create(candidateCall, task, temporaryTrace, task.tracing)); + } + + /** + * Returns collection of resolved calls for 'invoke' for 'variable as function' case (in FUNCTION_CALL_TRANSFORMER), + * the resolved call from callResolutionContext otherwise + */ + @NotNull + public Collection> transformCall(@NotNull CallResolutionContext callResolutionContext, + @NotNull CallResolver callResolver, + @NotNull ResolutionTask task) { + + return Collections.singleton((ResolvedCallWithTrace)callResolutionContext.candidateCall); + } + + + public static CallTransformer PROPERTY_CALL_TRANSFORMER = new CallTransformer(); + + public static CallTransformer FUNCTION_CALL_TRANSFORMER = new CallTransformer() { + @NotNull + @Override + public Collection> createCallContexts(@NotNull ResolutionCandidate candidate, + @NotNull ResolutionTask task, @NotNull TemporaryBindingTrace temporaryTrace) { + + if (candidate.getDescriptor() instanceof FunctionDescriptor) { + return super.createCallContexts(candidate, task, temporaryTrace); + } + + assert candidate.getDescriptor() instanceof VariableDescriptor; + + boolean hasReceiver = candidate.getReceiverArgument().exists(); + Call variableCall = stripCallArguments(task); + if (!hasReceiver) { + CallResolutionContext context = CallResolutionContext.create( + ResolvedCallImpl.create(candidate, temporaryTrace), task, temporaryTrace, task.tracing, variableCall); + return Collections.singleton(context); + } + Call variableCallWithoutReceiver = stripReceiver(variableCall); + CallResolutionContext contextWithReceiver = createContextWithChainedTrace( + candidate, variableCall, temporaryTrace, task); + + ResolutionCandidate candidateWithoutReceiver = ResolutionCandidate.create( + candidate.getDescriptor(), candidate.getThisObject(), ReceiverDescriptor.NO_RECEIVER); + + CallResolutionContext contextWithoutReceiver = createContextWithChainedTrace( + candidateWithoutReceiver, variableCallWithoutReceiver, temporaryTrace, task); + + contextWithoutReceiver.delayedReceiverForVariableAsFunctionSecondCall = variableCall.getExplicitReceiver(); + + return Lists.newArrayList(contextWithReceiver, contextWithoutReceiver); + } + + private CallResolutionContext createContextWithChainedTrace(ResolutionCandidate candidate, + Call call, TemporaryBindingTrace temporaryTrace, ResolutionTask task) { + + ChainedTemporaryBindingTrace chainedTrace = ChainedTemporaryBindingTrace.create(temporaryTrace); + ResolvedCallImpl resolvedCall = ResolvedCallImpl.create(candidate, chainedTrace); + return CallResolutionContext.create(resolvedCall, task, chainedTrace, task.tracing, call); + } + + private Call stripCallArguments(@NotNull ResolutionTask task) { + return new DelegatingCall(task.call) { + @Override + public JetValueArgumentList getValueArgumentList() { + return null; + } + + @NotNull + @Override + public List getValueArguments() { + return Collections.emptyList(); + } + + @NotNull + @Override + public List getFunctionLiteralArguments() { + return Collections.emptyList(); + } + + @NotNull + @Override + public List getTypeArguments() { + return Collections.emptyList(); + } + + @Override + public JetTypeArgumentList getTypeArgumentList() { + return null; + } + }; + } + + private Call stripReceiver(@NotNull Call variableCall) { + return new DelegatingCall(variableCall) { + @NotNull + @Override + public ReceiverDescriptor getExplicitReceiver() { + return ReceiverDescriptor.NO_RECEIVER; + } + }; + } + + @NotNull + @Override + public Collection> transformCall(@NotNull final CallResolutionContext context, + @NotNull CallResolver callResolver, @NotNull final ResolutionTask task) { + + final CallableDescriptor descriptor = context.candidateCall.getCandidateDescriptor(); + if (descriptor instanceof FunctionDescriptor) { + return super.transformCall(context, callResolver, task); + } + + assert descriptor instanceof VariableDescriptor; + JetType returnType = descriptor.getReturnType(); + if (returnType == null) { + return Collections.emptyList(); + } + + final ResolvedCallWithTrace variableResolvedCall = (ResolvedCallWithTrace)context.candidateCall; + + Call functionCall = createFunctionCall(context, task, returnType); + + final TemporaryBindingTrace variableCallTrace = context.candidateCall.getTrace(); + BasicResolutionContext basicResolutionContext = BasicResolutionContext.create(variableCallTrace, context.scope, functionCall, context.expectedType, context.dataFlowInfo); + + // 'invoke' call resolve + OverloadResolutionResults results = callResolver.resolveCallWithGivenName(basicResolutionContext, task.reference, "invoke"); + Collection> calls = ((OverloadResolutionResultsImpl)results).getResultingCalls(); + + return Collections2.transform(calls, new Function, ResolvedCallWithTrace>() { + @Override + public ResolvedCallWithTrace apply(ResolvedCallWithTrace functionResolvedCall) { + return new VariableAsFunctionResolvedCall(functionResolvedCall, variableResolvedCall); + } + }); + } + + private Call createFunctionCall(final CallResolutionContext context, + final ResolutionTask task, JetType returnType) { + + final ExpressionReceiver receiverFromVariable = new ExpressionReceiver(task.reference, returnType); + final JetSimpleNameExpression invokeExpression = (JetSimpleNameExpression) JetPsiFactory.createExpression( + task.call.getCallElement().getProject(), "invoke"); + + return new DelegatingCall(task.call) { + @NotNull + @Override + public ReceiverDescriptor getExplicitReceiver() { + return context.delayedReceiverForVariableAsFunctionSecondCall; + } + + @NotNull + @Override + public ReceiverDescriptor getThisObject() { + return receiverFromVariable; + } + + @Override + public JetExpression getCalleeExpression() { + return invokeExpression; + } + + @NotNull + @Override + public PsiElement getCallElement() { + if (task.call.getCallElement() instanceof JetCallElement) { + //to report errors properly + JetValueArgumentList list = ((JetCallElement)task.call.getCallElement()).getValueArgumentList(); + if (list != null) { + return list; + } + } + return invokeExpression; + } + }; + } + }; +}