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