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 com.google.common.collect.Lists;
020    import com.intellij.psi.PsiElement;
021    import com.intellij.util.ArrayUtil;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.asm4.MethodVisitor;
025    import org.jetbrains.asm4.Type;
026    import org.jetbrains.asm4.commons.InstructionAdapter;
027    import org.jetbrains.asm4.commons.Method;
028    import org.jetbrains.jet.codegen.binding.CalculatedClosure;
029    import org.jetbrains.jet.codegen.context.CodegenContext;
030    import org.jetbrains.jet.codegen.context.LocalLookup;
031    import org.jetbrains.jet.codegen.signature.BothSignatureWriter;
032    import org.jetbrains.jet.codegen.signature.JvmMethodSignature;
033    import org.jetbrains.jet.codegen.state.GenerationState;
034    import org.jetbrains.jet.codegen.state.GenerationStateAware;
035    import org.jetbrains.jet.codegen.state.JetTypeMapper;
036    import org.jetbrains.jet.codegen.state.JetTypeMapperMode;
037    import org.jetbrains.jet.lang.descriptors.*;
038    import org.jetbrains.jet.lang.resolve.BindingContext;
039    import org.jetbrains.jet.lang.resolve.java.JvmAbi;
040    import org.jetbrains.jet.lang.resolve.java.JvmClassName;
041    import org.jetbrains.jet.lang.resolve.java.sam.SingleAbstractMethodUtils;
042    import org.jetbrains.jet.lang.resolve.name.Name;
043    import org.jetbrains.jet.lang.types.JetType;
044    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
045    
046    import java.util.Collection;
047    import java.util.Collections;
048    import java.util.List;
049    
050    import static org.jetbrains.asm4.Opcodes.*;
051    import static org.jetbrains.jet.codegen.AsmUtil.*;
052    import static org.jetbrains.jet.codegen.CodegenUtil.isConst;
053    import static org.jetbrains.jet.codegen.binding.CodegenBinding.*;
054    
055    public class ClosureCodegen extends GenerationStateAware {
056        private final PsiElement fun;
057        private final FunctionDescriptor funDescriptor;
058        private final ClassDescriptor samInterface;
059        private final JvmClassName superClass;
060        private final CodegenContext context;
061        private final FunctionGenerationStrategy strategy;
062        private final CalculatedClosure closure;
063        private final JvmClassName name;
064    
065        private Method constructor;
066    
067        public ClosureCodegen(
068                @NotNull GenerationState state,
069                @NotNull PsiElement fun,
070                @NotNull FunctionDescriptor funDescriptor,
071                @Nullable ClassDescriptor samInterface,
072                @NotNull JvmClassName closureSuperClass,
073                @NotNull CodegenContext context,
074                @NotNull LocalLookup localLookup,
075                @NotNull FunctionGenerationStrategy strategy
076        ) {
077            super(state);
078    
079            this.fun = fun;
080            this.funDescriptor = funDescriptor;
081            this.samInterface = samInterface;
082            this.superClass = closureSuperClass;
083            this.context = context.intoClosure(funDescriptor, localLookup, typeMapper);
084            this.strategy = strategy;
085    
086            ClassDescriptor classDescriptor = anonymousClassForFunction(bindingContext, funDescriptor);
087            this.closure = bindingContext.get(CLOSURE, classDescriptor);
088            assert closure != null : "Closure must be calculated for class: " + classDescriptor;
089    
090            this.name = classNameForAnonymousClass(bindingContext, funDescriptor);
091        }
092    
093    
094        public void gen() {
095            ClassBuilder cv = state.getFactory().newVisitor(name, fun.getContainingFile());
096    
097            FunctionDescriptor interfaceFunction;
098            String[] superInterfaces;
099    
100            if (samInterface == null) {
101                interfaceFunction = getInvokeFunction(funDescriptor);
102                superInterfaces = ArrayUtil.EMPTY_STRING_ARRAY;
103            }
104            else {
105                interfaceFunction = SingleAbstractMethodUtils.getAbstractMethodOfSamInterface(samInterface);
106                superInterfaces = new String[] {JvmClassName.byClassDescriptor(samInterface).getInternalName()};
107            }
108    
109            cv.defineClass(fun,
110                           V1_6,
111                           ACC_FINAL | ACC_SUPER,
112                           name.getInternalName(),
113                           getGenericSignature(),
114                           superClass.getInternalName(),
115                           superInterfaces
116            );
117            cv.visitSource(fun.getContainingFile().getName(), null);
118    
119    
120            generateBridge(interfaceFunction, cv);
121    
122            JvmMethodSignature jvmMethodSignature = typeMapper.mapSignature(interfaceFunction.getName(), funDescriptor);
123    
124            FunctionCodegen fc = new FunctionCodegen(context, cv, state);
125            fc.generateMethod(fun, jvmMethodSignature, funDescriptor, strategy);
126    
127            this.constructor = generateConstructor(cv);
128    
129            if (isConst(closure)) {
130                generateConstInstance(cv);
131            }
132    
133            genClosureFields(closure, cv, typeMapper);
134    
135            cv.done();
136        }
137    
138        @NotNull
139        public StackValue putInstanceOnStack(@NotNull InstructionAdapter v, @NotNull ExpressionCodegen codegen) {
140            Type asmType = name.getAsmType();
141            if (isConst(closure)) {
142                v.getstatic(name.getInternalName(), JvmAbi.INSTANCE_FIELD, name.getDescriptor());
143            }
144            else {
145                v.anew(asmType);
146                v.dup();
147    
148                codegen.pushClosureOnStack(closure, false);
149                v.invokespecial(name.getInternalName(), "<init>", constructor.getDescriptor());
150            }
151            return StackValue.onStack(asmType);
152        }
153    
154    
155        private void generateConstInstance(@NotNull ClassBuilder cv) {
156            MethodVisitor mv = cv.newMethod(fun, ACC_STATIC | ACC_SYNTHETIC, "<clinit>", "()V", null, ArrayUtil.EMPTY_STRING_ARRAY);
157            InstructionAdapter iv = new InstructionAdapter(mv);
158    
159            cv.newField(fun, ACC_STATIC | ACC_FINAL, JvmAbi.INSTANCE_FIELD, name.getDescriptor(), null, null);
160    
161            if (state.getClassBuilderMode() == ClassBuilderMode.STUBS) {
162                genStubCode(mv);
163            }
164            else if (state.getClassBuilderMode() == ClassBuilderMode.FULL) {
165                mv.visitCode();
166                genInitSingletonField(name.getAsmType(), iv);
167                mv.visitInsn(RETURN);
168                FunctionCodegen.endVisit(mv, "<clinit>", fun);
169            }
170        }
171    
172        private void generateBridge(@NotNull FunctionDescriptor interfaceFunction, @NotNull ClassBuilder cv) {
173            Method bridge = typeMapper.mapSignature(interfaceFunction).getAsmMethod();
174    
175            Method delegate = typeMapper.mapSignature(interfaceFunction.getName(), funDescriptor).getAsmMethod();
176    
177            if (bridge.getDescriptor().equals(delegate.getDescriptor())) {
178                return;
179            }
180    
181            MethodVisitor mv = cv.newMethod(fun, ACC_PUBLIC | ACC_BRIDGE, interfaceFunction.getName().asString(),
182                                            bridge.getDescriptor(), null, ArrayUtil.EMPTY_STRING_ARRAY);
183            if (state.getClassBuilderMode() == ClassBuilderMode.STUBS) {
184                genStubCode(mv);
185            }
186            if (state.getClassBuilderMode() == ClassBuilderMode.FULL) {
187                mv.visitCode();
188    
189                InstructionAdapter iv = new InstructionAdapter(mv);
190    
191                iv.load(0, name.getAsmType());
192    
193                ReceiverParameterDescriptor receiver = funDescriptor.getReceiverParameter();
194                int count = 1;
195                if (receiver != null) {
196                    StackValue.local(count, bridge.getArgumentTypes()[count - 1]).put(typeMapper.mapType(receiver.getType()), iv);
197                    count++;
198                }
199    
200                List<ValueParameterDescriptor> params = funDescriptor.getValueParameters();
201                for (ValueParameterDescriptor param : params) {
202                    StackValue.local(count, bridge.getArgumentTypes()[count - 1]).put(typeMapper.mapType(param.getType()), iv);
203                    count++;
204                }
205    
206                iv.invokevirtual(name.getInternalName(), interfaceFunction.getName().asString(), delegate.getDescriptor());
207                StackValue.onStack(delegate.getReturnType()).put(bridge.getReturnType(), iv);
208    
209                iv.areturn(bridge.getReturnType());
210    
211                FunctionCodegen.endVisit(mv, "bridge", fun);
212            }
213        }
214    
215        @NotNull
216        private Method generateConstructor(@NotNull ClassBuilder cv) {
217            List<FieldInfo> args = calculateConstructorParameters(typeMapper, closure, name.getAsmType());
218    
219            Type[] argTypes = fieldListToTypeArray(args);
220    
221            Method constructor = new Method("<init>", Type.VOID_TYPE, argTypes);
222            MethodVisitor mv = cv.newMethod(fun, NO_FLAG_PACKAGE_PRIVATE, "<init>", constructor.getDescriptor(), null,
223                                            ArrayUtil.EMPTY_STRING_ARRAY);
224            if (state.getClassBuilderMode() == ClassBuilderMode.STUBS) {
225                genStubCode(mv);
226            }
227            else if (state.getClassBuilderMode() == ClassBuilderMode.FULL) {
228                mv.visitCode();
229                InstructionAdapter iv = new InstructionAdapter(mv);
230    
231                iv.load(0, superClass.getAsmType());
232                iv.invokespecial(superClass.getInternalName(), "<init>", "()V");
233    
234                int k = 1;
235                for (FieldInfo fieldInfo : args) {
236                    k = AsmUtil.genAssignInstanceFieldFromParam(fieldInfo, k, iv);
237                }
238    
239                iv.visitInsn(RETURN);
240    
241                FunctionCodegen.endVisit(iv, "constructor", fun);
242            }
243            return constructor;
244        }
245    
246        @NotNull
247        public static List<FieldInfo> calculateConstructorParameters(
248                @NotNull JetTypeMapper typeMapper,
249                @NotNull CalculatedClosure closure,
250                @NotNull Type ownerType
251        ) {
252            BindingContext bindingContext = typeMapper.getBindingContext();
253            List<FieldInfo> args = Lists.newArrayList();
254            ClassDescriptor captureThis = closure.getCaptureThis();
255            if (captureThis != null) {
256                Type type = typeMapper.mapType(captureThis);
257                args.add(FieldInfo.createForHiddenField(ownerType, type, CAPTURED_THIS_FIELD));
258            }
259            ClassifierDescriptor captureReceiver = closure.getCaptureReceiver();
260            if (captureReceiver != null) {
261                args.add(FieldInfo.createForHiddenField(ownerType, typeMapper.mapType(captureReceiver), CAPTURED_RECEIVER_FIELD));
262            }
263    
264            for (DeclarationDescriptor descriptor : closure.getCaptureVariables().keySet()) {
265                if (descriptor instanceof VariableDescriptor && !(descriptor instanceof PropertyDescriptor)) {
266                    Type sharedVarType = typeMapper.getSharedVarType(descriptor);
267    
268                    Type type = sharedVarType != null
269                                      ? sharedVarType
270                                      : typeMapper.mapType((VariableDescriptor) descriptor);
271                    args.add(FieldInfo.createForHiddenField(ownerType, type, "$" + descriptor.getName().asString()));
272                }
273                else if (isLocalNamedFun(descriptor)) {
274                    JvmClassName className = classNameForAnonymousClass(bindingContext, (FunctionDescriptor) descriptor);
275                    args.add(FieldInfo.createForHiddenField(ownerType, className.getAsmType(), "$" + descriptor.getName().asString()));
276                }
277                else if (descriptor instanceof FunctionDescriptor) {
278                    assert captureReceiver != null;
279                }
280            }
281            return args;
282        }
283    
284        private static Type[] fieldListToTypeArray(List<FieldInfo> args) {
285            Type[] argTypes = new Type[args.size()];
286            for (int i = 0; i != argTypes.length; ++i) {
287                argTypes[i] = args.get(i).getFieldType();
288            }
289            return argTypes;
290        }
291    
292        @NotNull
293        private String getGenericSignature() {
294            ClassDescriptor classDescriptor = anonymousClassForFunction(bindingContext, funDescriptor);
295            Collection<JetType> supertypes = classDescriptor.getTypeConstructor().getSupertypes();
296            assert supertypes.size() == 1 : "Closure must have exactly one supertype: " + funDescriptor;
297            JetType supertype = supertypes.iterator().next();
298    
299            BothSignatureWriter sw = new BothSignatureWriter(BothSignatureWriter.Mode.CLASS, true);
300            typeMapper.writeFormalTypeParameters(Collections.<TypeParameterDescriptor>emptyList(), sw);
301            sw.writeSuperclass();
302            typeMapper.mapType(supertype, sw, JetTypeMapperMode.TYPE_PARAMETER);
303            sw.writeSuperclassEnd();
304    
305            String signature = sw.makeJavaGenericSignature();
306            assert signature != null : "Closure superclass must have a generic signature: " + funDescriptor;
307            return signature;
308        }
309    
310        private static FunctionDescriptor getInvokeFunction(FunctionDescriptor funDescriptor) {
311            int paramCount = funDescriptor.getValueParameters().size();
312            KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance();
313            ClassDescriptor funClass = funDescriptor.getReceiverParameter() == null
314                                       ? builtIns.getFunction(paramCount)
315                                       : builtIns.getExtensionFunction(paramCount);
316            return funClass.getDefaultType().getMemberScope().getFunctions(Name.identifier("invoke")).iterator().next();
317        }
318    }