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.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 }