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