001/*
002 * Copyright 2010-2013 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.jetbrains.jet.codegen;
018
019import org.jetbrains.annotations.NotNull;
020import org.jetbrains.annotations.Nullable;
021import org.jetbrains.asm4.Type;
022import org.jetbrains.asm4.commons.InstructionAdapter;
023import org.jetbrains.asm4.util.Printer;
024import org.jetbrains.jet.codegen.signature.JvmMethodSignature;
025import org.jetbrains.jet.codegen.state.GenerationState;
026import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
027import org.jetbrains.jet.lang.resolve.java.JvmAbi;
028import org.jetbrains.jet.lang.resolve.java.JvmClassName;
029
030import java.util.List;
031
032import static org.jetbrains.asm4.Opcodes.INVOKESPECIAL;
033import static org.jetbrains.asm4.Opcodes.INVOKESTATIC;
034
035public class CallableMethod implements Callable {
036    @NotNull
037    private final JvmClassName owner;
038    @Nullable
039    private final JvmClassName defaultImplOwner;
040    @Nullable
041    private final JvmClassName defaultImplParam;
042    private final JvmMethodSignature signature;
043    private final int invokeOpcode;
044    @Nullable
045    private final JvmClassName thisClass;
046    @Nullable
047    private final Type receiverParameterType;
048    @Nullable
049    private final Type generateCalleeType;
050
051    public CallableMethod(
052            @NotNull JvmClassName owner, @Nullable JvmClassName defaultImplOwner, @Nullable JvmClassName defaultImplParam,
053            JvmMethodSignature signature, int invokeOpcode,
054            @Nullable JvmClassName thisClass, @Nullable Type receiverParameterType, @Nullable Type generateCalleeType
055    ) {
056        this.owner = owner;
057        this.defaultImplOwner = defaultImplOwner;
058        this.defaultImplParam = defaultImplParam;
059        this.signature = signature;
060        this.invokeOpcode = invokeOpcode;
061        this.thisClass = thisClass;
062        this.receiverParameterType = receiverParameterType;
063        this.generateCalleeType = generateCalleeType;
064    }
065
066    @NotNull
067    public JvmClassName getOwner() {
068        return owner;
069    }
070
071    @Nullable
072    public JvmClassName getDefaultImplParam() {
073        return defaultImplParam;
074    }
075
076    public JvmMethodSignature getSignature() {
077        return signature;
078    }
079
080    public int getInvokeOpcode() {
081        return invokeOpcode;
082    }
083
084    public List<Type> getValueParameterTypes() {
085        return signature.getValueParameterTypes();
086    }
087
088    @Nullable
089    public JvmClassName getThisType() {
090        return thisClass;
091    }
092
093    @Nullable
094    public Type getReceiverClass() {
095        return receiverParameterType;
096    }
097
098    private void invoke(InstructionAdapter v) {
099        v.visitMethodInsn(getInvokeOpcode(), owner.getInternalName(), getSignature().getAsmMethod().getName(),
100                          getSignature().getAsmMethod().getDescriptor());
101    }
102
103    public void invokeWithNotNullAssertion(
104            @NotNull InstructionAdapter v,
105            @NotNull GenerationState state,
106            @NotNull ResolvedCall resolvedCall
107    ) {
108        invokeWithoutAssertions(v);
109        AsmUtil.genNotNullAssertionForMethod(v, state, resolvedCall);
110    }
111
112    public void invokeWithoutAssertions(@NotNull InstructionAdapter v) {
113        invoke(v);
114    }
115
116    @Nullable
117    public Type getGenerateCalleeType() {
118        return generateCalleeType;
119    }
120
121    private void invokeDefault(InstructionAdapter v, int mask) {
122        if (defaultImplOwner == null || defaultImplParam == null) {
123            throw new IllegalStateException();
124        }
125
126        v.iconst(mask);
127        String desc = getSignature().getAsmMethod().getDescriptor().replace(")", "I)");
128        if ("<init>".equals(getSignature().getAsmMethod().getName())) {
129            v.visitMethodInsn(INVOKESPECIAL, defaultImplOwner.getInternalName(), "<init>", desc);
130        }
131        else {
132            if (getInvokeOpcode() != INVOKESTATIC) {
133                desc = desc.replace("(", "(" + defaultImplParam.getDescriptor());
134            }
135            v.visitMethodInsn(INVOKESTATIC, defaultImplOwner.getInternalName(),
136                              getSignature().getAsmMethod().getName() + JvmAbi.DEFAULT_PARAMS_IMPL_SUFFIX, desc);
137        }
138    }
139
140    public void invokeDefaultWithNotNullAssertion(
141            @NotNull InstructionAdapter v,
142            @NotNull GenerationState state,
143            @NotNull ResolvedCall resolvedCall,
144            int mask
145    ) {
146        invokeDefault(v, mask);
147        AsmUtil.genNotNullAssertionForMethod(v, state, resolvedCall);
148    }
149
150    public boolean isNeedsThis() {
151        return thisClass != null && generateCalleeType == null;
152    }
153
154    public Type getReturnType() {
155        return signature.getAsmMethod().getReturnType();
156    }
157
158    @Override
159    public String toString() {
160        StringBuilder sb = new StringBuilder();
161        sb.append(Printer.OPCODES[invokeOpcode]);
162        sb.append(" ");
163        sb.append(owner);
164        sb.append(".");
165        sb.append(signature);
166        return sb.toString();
167    }
168}