001    /*
002     * Copyright 2010-2015 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.kotlin.codegen;
018    
019    import org.jetbrains.annotations.NotNull;
020    import org.jetbrains.annotations.Nullable;
021    import org.jetbrains.kotlin.codegen.state.GenerationState;
022    import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
023    import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
024    import org.jetbrains.kotlin.load.java.JvmAbi;
025    import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind;
026    import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature;
027    import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
028    import org.jetbrains.org.objectweb.asm.Opcodes;
029    import org.jetbrains.org.objectweb.asm.Type;
030    import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
031    import org.jetbrains.org.objectweb.asm.commons.Method;
032    import org.jetbrains.org.objectweb.asm.util.Printer;
033    
034    import java.util.ArrayList;
035    import java.util.List;
036    
037    import static org.jetbrains.org.objectweb.asm.Opcodes.INVOKESPECIAL;
038    import static org.jetbrains.org.objectweb.asm.Opcodes.INVOKESTATIC;
039    
040    public class CallableMethod implements Callable {
041        private final Type owner;
042        private final Type defaultImplOwner;
043        private final Type defaultImplParam;
044        private final JvmMethodSignature signature;
045        private final int invokeOpcode;
046        private final Type thisClass;
047        private final Type receiverParameterType;
048        private final Type generateCalleeType;
049    
050        public CallableMethod(
051                @NotNull Type owner,
052                @Nullable Type defaultImplOwner,
053                @Nullable Type defaultImplParam,
054                @NotNull JvmMethodSignature signature,
055                int invokeOpcode,
056                @Nullable Type thisClass,
057                @Nullable Type receiverParameterType,
058                @Nullable Type generateCalleeType
059        ) {
060            this.owner = owner;
061            this.defaultImplOwner = defaultImplOwner;
062            this.defaultImplParam = defaultImplParam;
063            this.signature = signature;
064            this.invokeOpcode = invokeOpcode;
065            this.thisClass = thisClass;
066            this.receiverParameterType = receiverParameterType;
067            this.generateCalleeType = generateCalleeType;
068        }
069    
070        @NotNull
071        public Type getOwner() {
072            return owner;
073        }
074    
075        @NotNull
076        public List<JvmMethodParameterSignature> getValueParameters() {
077            return signature.getValueParameters();
078        }
079    
080        @NotNull
081        public List<Type> getValueParameterTypes() {
082            List<JvmMethodParameterSignature> valueParameters = signature.getValueParameters();
083            List<Type> result = new ArrayList<Type>(valueParameters.size());
084            for (JvmMethodParameterSignature parameter : valueParameters) {
085                if (parameter.getKind() == JvmMethodParameterKind.VALUE) {
086                    result.add(parameter.getAsmType());
087                }
088            }
089            return result;
090        }
091    
092        @NotNull
093        public Method getAsmMethod() {
094            return signature.getAsmMethod();
095        }
096    
097        public int getInvokeOpcode() {
098            return invokeOpcode;
099        }
100    
101        @Nullable
102        public Type getThisType() {
103            return thisClass;
104        }
105    
106        @Nullable
107        public Type getReceiverClass() {
108            return receiverParameterType;
109        }
110    
111        private void invoke(InstructionAdapter v) {
112            v.visitMethodInsn(getInvokeOpcode(), owner.getInternalName(), getAsmMethod().getName(), getAsmMethod().getDescriptor());
113        }
114    
115        public void invokeWithNotNullAssertion(
116                @NotNull InstructionAdapter v,
117                @NotNull GenerationState state,
118                @NotNull ResolvedCall resolvedCall
119        ) {
120            invokeWithoutAssertions(v);
121            AsmUtil.genNotNullAssertionForMethod(v, state, resolvedCall);
122        }
123    
124        public void invokeWithoutAssertions(@NotNull InstructionAdapter v) {
125            invoke(v);
126        }
127    
128        @Nullable
129        public Type getGenerateCalleeType() {
130            return generateCalleeType;
131        }
132    
133        private void invokeDefault(InstructionAdapter v) {
134            if (defaultImplOwner == null || defaultImplParam == null) {
135                throw new IllegalStateException();
136            }
137    
138            Method method = getAsmMethod();
139            String desc = JetTypeMapper.getDefaultDescriptor(method, receiverParameterType != null);
140            if ("<init>".equals(method.getName())) {
141                v.aconst(null);
142                v.visitMethodInsn(INVOKESPECIAL, defaultImplOwner.getInternalName(), "<init>", desc, false);
143            }
144            else {
145                if (getInvokeOpcode() != INVOKESTATIC) {
146                    desc = desc.replace("(", "(" + defaultImplParam.getDescriptor());
147                }
148                v.visitMethodInsn(INVOKESTATIC, defaultImplOwner.getInternalName(),
149                                  method.getName() + JvmAbi.DEFAULT_PARAMS_IMPL_SUFFIX, desc, false);
150            }
151        }
152    
153        public void invokeDefaultWithNotNullAssertion(
154                @NotNull InstructionAdapter v,
155                @NotNull GenerationState state,
156                @NotNull ResolvedCall resolvedCall
157        ) {
158            invokeDefault(v);
159            AsmUtil.genNotNullAssertionForMethod(v, state, resolvedCall);
160        }
161    
162        public boolean isNeedsThis() {
163            return thisClass != null && generateCalleeType == null;
164        }
165    
166        @NotNull
167        public Type getReturnType() {
168            return signature.getReturnType();
169        }
170    
171        @Override
172        public String toString() {
173            return Printer.OPCODES[invokeOpcode] + " " + owner.getInternalName() + "." + signature;
174        }
175    
176        public boolean isStaticCall() {
177            return getInvokeOpcode() == Opcodes.INVOKESTATIC;
178        }
179    }