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
017package org.jetbrains.jet.codegen;
018
019import com.google.common.collect.Lists;
020import com.intellij.psi.PsiElement;
021import com.intellij.util.ArrayUtil;
022import org.jetbrains.annotations.NotNull;
023import org.jetbrains.annotations.Nullable;
024import org.jetbrains.asm4.MethodVisitor;
025import org.jetbrains.asm4.Type;
026import org.jetbrains.asm4.commons.InstructionAdapter;
027import org.jetbrains.asm4.commons.Method;
028import org.jetbrains.jet.codegen.binding.CalculatedClosure;
029import org.jetbrains.jet.codegen.context.CodegenContext;
030import org.jetbrains.jet.codegen.context.LocalLookup;
031import org.jetbrains.jet.codegen.signature.BothSignatureWriter;
032import org.jetbrains.jet.codegen.signature.JvmMethodSignature;
033import org.jetbrains.jet.codegen.state.GenerationState;
034import org.jetbrains.jet.codegen.state.GenerationStateAware;
035import org.jetbrains.jet.codegen.state.JetTypeMapper;
036import org.jetbrains.jet.codegen.state.JetTypeMapperMode;
037import org.jetbrains.jet.lang.descriptors.*;
038import org.jetbrains.jet.lang.resolve.BindingContext;
039import org.jetbrains.jet.lang.resolve.java.JvmAbi;
040import org.jetbrains.jet.lang.resolve.java.JvmClassName;
041import org.jetbrains.jet.lang.resolve.java.sam.SingleAbstractMethodUtils;
042import org.jetbrains.jet.lang.resolve.name.Name;
043import org.jetbrains.jet.lang.types.JetType;
044import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
045
046import java.util.Collection;
047import java.util.Collections;
048import java.util.List;
049
050import static org.jetbrains.asm4.Opcodes.*;
051import static org.jetbrains.jet.codegen.AsmUtil.*;
052import static org.jetbrains.jet.codegen.CodegenUtil.isConst;
053import static org.jetbrains.jet.codegen.binding.CodegenBinding.*;
054
055public 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.getInternalName(), 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, false, 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.writeSupersStart();
302        sw.writeSuperclass();
303        typeMapper.mapType(supertype, sw, JetTypeMapperMode.TYPE_PARAMETER);
304        sw.writeSuperclassEnd();
305        sw.writeSupersEnd();
306
307        String signature = sw.makeJavaGenericSignature();
308        assert signature != null : "Closure superclass must have a generic signature: " + funDescriptor;
309        return signature;
310    }
311
312    private static FunctionDescriptor getInvokeFunction(FunctionDescriptor funDescriptor) {
313        int paramCount = funDescriptor.getValueParameters().size();
314        KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance();
315        ClassDescriptor funClass = funDescriptor.getReceiverParameter() == null
316                                   ? builtIns.getFunction(paramCount)
317                                   : builtIns.getExtensionFunction(paramCount);
318        return funClass.getDefaultType().getMemberScope().getFunctions(Name.identifier("invoke")).iterator().next();
319    }
320}