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    
017    package org.jetbrains.jet.codegen;
018    
019    import org.jetbrains.annotations.NotNull;
020    import org.jetbrains.annotations.Nullable;
021    import org.jetbrains.asm4.Type;
022    import org.jetbrains.asm4.commons.InstructionAdapter;
023    import org.jetbrains.asm4.util.Printer;
024    import org.jetbrains.jet.codegen.signature.JvmMethodSignature;
025    import org.jetbrains.jet.codegen.state.GenerationState;
026    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
027    import org.jetbrains.jet.lang.resolve.java.JvmAbi;
028    import org.jetbrains.jet.lang.resolve.java.JvmClassName;
029    
030    import java.util.List;
031    
032    import static org.jetbrains.asm4.Opcodes.INVOKESPECIAL;
033    import static org.jetbrains.asm4.Opcodes.INVOKESTATIC;
034    
035    public 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    }