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.inline;
018
019 import com.intellij.openapi.vfs.VirtualFile;
020 import com.intellij.psi.PsiElement;
021 import org.jetbrains.annotations.NotNull;
022 import org.jetbrains.annotations.Nullable;
023 import org.jetbrains.asm4.MethodVisitor;
024 import org.jetbrains.asm4.Opcodes;
025 import org.jetbrains.asm4.Type;
026 import org.jetbrains.asm4.commons.Method;
027 import org.jetbrains.asm4.tree.MethodNode;
028 import org.jetbrains.asm4.util.Textifier;
029 import org.jetbrains.asm4.util.TraceMethodVisitor;
030 import org.jetbrains.jet.codegen.*;
031 import org.jetbrains.jet.codegen.context.CodegenContext;
032 import org.jetbrains.jet.codegen.context.MethodContext;
033 import org.jetbrains.jet.codegen.context.PackageContext;
034 import org.jetbrains.jet.codegen.signature.JvmMethodParameterKind;
035 import org.jetbrains.jet.codegen.signature.JvmMethodParameterSignature;
036 import org.jetbrains.jet.codegen.signature.JvmMethodSignature;
037 import org.jetbrains.jet.codegen.state.GenerationState;
038 import org.jetbrains.jet.codegen.state.JetTypeMapper;
039 import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedSimpleFunctionDescriptor;
040 import org.jetbrains.jet.lang.descriptors.*;
041 import org.jetbrains.jet.lang.descriptors.impl.AnonymousFunctionDescriptor;
042 import org.jetbrains.jet.lang.psi.*;
043 import org.jetbrains.jet.lang.resolve.BindingContext;
044 import org.jetbrains.jet.lang.resolve.BindingContextUtils;
045 import org.jetbrains.jet.lang.resolve.DescriptorUtils;
046 import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
047 import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants;
048 import org.jetbrains.jet.lang.types.lang.InlineStrategy;
049 import org.jetbrains.jet.lang.types.lang.InlineUtil;
050 import org.jetbrains.jet.renderer.DescriptorRenderer;
051
052 import java.io.IOException;
053 import java.io.PrintWriter;
054 import java.io.StringWriter;
055 import java.util.*;
056
057 import static org.jetbrains.jet.codegen.AsmUtil.getMethodAsmFlags;
058 import static org.jetbrains.jet.codegen.AsmUtil.isPrimitive;
059
060 public class InlineCodegen implements ParentCodegenAware, CallGenerator {
061
062 private final JetTypeMapper typeMapper;
063
064 private final ExpressionCodegen codegen;
065
066 private final boolean asFunctionInline;
067
068 private final GenerationState state;
069
070 private final Call call;
071
072 private final SimpleFunctionDescriptor functionDescriptor;
073
074 private final BindingContext bindingContext;
075
076 private final MethodContext context;
077
078 private final FrameMap originalFunctionFrame;
079
080 private final int initialFrameSize;
081
082 private final JvmMethodSignature jvmSignature;
083
084 private final boolean isSameModule;
085
086 private LambdaInfo activeLambda;
087
088 protected final List<ParameterInfo> actualParameters = new ArrayList<ParameterInfo>();
089
090 protected final Map<Integer, LambdaInfo> expressionMap = new HashMap<Integer, LambdaInfo>();
091
092 public InlineCodegen(
093 @NotNull ExpressionCodegen codegen,
094 @NotNull GenerationState state,
095 @NotNull SimpleFunctionDescriptor functionDescriptor,
096 @NotNull Call call
097 ) {
098 assert functionDescriptor.getInlineStrategy().isInline() : "InlineCodegen could inline only inline function but " + functionDescriptor;
099
100 this.state = state;
101 this.typeMapper = state.getTypeMapper();
102 this.codegen = codegen;
103 this.call = call;
104 this.functionDescriptor = functionDescriptor.getOriginal();
105 bindingContext = codegen.getBindingContext();
106 initialFrameSize = codegen.getFrameMap().getCurrentSize();
107
108 context = (MethodContext) getContext(functionDescriptor, state);
109 originalFunctionFrame = context.prepareFrame(typeMapper);
110 jvmSignature = typeMapper.mapSignature(functionDescriptor, context.getContextKind());
111
112 InlineStrategy inlineStrategy =
113 codegen.getContext().isInlineFunction() ? InlineStrategy.IN_PLACE : functionDescriptor.getInlineStrategy();
114 this.asFunctionInline = false;
115
116 isSameModule = !(functionDescriptor instanceof DeserializedSimpleFunctionDescriptor) /*not compiled library*/ &&
117 CodegenUtil.isCallInsideSameModuleAsDeclared(functionDescriptor, codegen.getContext());
118 }
119
120
121 @Override
122 public void genCall(CallableMethod callableMethod, ResolvedCall<?> resolvedCall, int mask, ExpressionCodegen codegen) {
123 assert mask == 0 : "Default method invocation couldn't be inlined " + resolvedCall;
124
125 MethodNode node = null;
126
127 try {
128 node = createMethodNode(callableMethod);
129 inlineCall(node);
130 }
131 catch (CompilationException e) {
132 throw e;
133 }
134 catch (Exception e) {
135 String text = getNodeText(node);
136 PsiElement element = BindingContextUtils.descriptorToDeclaration(bindingContext, this.codegen.getContext().getContextDescriptor());
137 throw new CompilationException("Couldn't inline method call '" +
138 functionDescriptor.getName() +
139 "' into \n" + (element != null ? element.getText() : "null psi element " + this.codegen.getContext().getContextDescriptor()) +
140 "\ncause: " +
141 text, e, call.getCallElement());
142 }
143
144 leaveTemps();
145 }
146
147 @NotNull
148 private MethodNode createMethodNode(CallableMethod callableMethod)
149 throws ClassNotFoundException, IOException {
150 MethodNode node;
151 if (functionDescriptor instanceof DeserializedSimpleFunctionDescriptor) {
152 VirtualFile file = InlineCodegenUtil.getVirtualFileForCallable((DeserializedSimpleFunctionDescriptor) functionDescriptor, state);
153 String methodDesc = callableMethod.getAsmMethod().getDescriptor();
154 DeclarationDescriptor parentDescriptor = functionDescriptor.getContainingDeclaration();
155 if (DescriptorUtils.isTrait(parentDescriptor)) {
156 methodDesc = "(" + typeMapper.mapType((ClassDescriptor) parentDescriptor).getDescriptor() + methodDesc.substring(1);
157 }
158 node = InlineCodegenUtil.getMethodNode(file.getInputStream(), functionDescriptor.getName().asString(), methodDesc);
159
160 if (node == null) {
161 throw new RuntimeException("Couldn't obtain compiled function body for " + descriptorName(functionDescriptor));
162 }
163 }
164 else {
165 PsiElement element = BindingContextUtils.descriptorToDeclaration(bindingContext, functionDescriptor);
166
167 if (element == null) {
168 throw new RuntimeException("Couldn't find declaration for function " + descriptorName(functionDescriptor));
169 }
170
171 JvmMethodSignature jvmSignature = typeMapper.mapSignature(functionDescriptor, context.getContextKind());
172 Method asmMethod = jvmSignature.getAsmMethod();
173 node = new MethodNode(Opcodes.ASM4,
174 getMethodAsmFlags(functionDescriptor, context.getContextKind()),
175 asmMethod.getName(),
176 asmMethod.getDescriptor(),
177 jvmSignature.getGenericsSignature(),
178 null);
179
180 //for maxLocals calculation
181 MethodVisitor adapter = InlineCodegenUtil.wrapWithMaxLocalCalc(node);
182 FunctionCodegen.generateMethodBody(adapter, functionDescriptor, context.getParentContext().intoFunction(functionDescriptor),
183 jvmSignature,
184 new FunctionGenerationStrategy.FunctionDefault(state,
185 functionDescriptor,
186 (JetDeclarationWithBody) element),
187 getParentCodegen());
188 adapter.visitMaxs(-1, -1);
189 adapter.visitEnd();
190 }
191 return node;
192 }
193
194 private void inlineCall(MethodNode node) {
195 generateClosuresBodies();
196
197 List<ParameterInfo> realParams = new ArrayList<ParameterInfo>(actualParameters);
198
199 putClosureParametersOnStack();
200
201 List<CapturedParamInfo> captured = getAllCaptured();
202
203 Parameters parameters = new Parameters(realParams, Parameters.shiftAndAddStubs(captured, realParams.size()));
204
205 InliningContext info =
206 new InliningContext(expressionMap, null, null, null, state,
207 codegen.getInlineNameGenerator().subGenerator(functionDescriptor.getName().asString()),
208 codegen.getContext(), call, Collections.<String, String>emptyMap());
209
210 MethodInliner inliner = new MethodInliner(node, parameters, info, null, new LambdaFieldRemapper(), isSameModule); //with captured
211
212 VarRemapper.ParamRemapper remapper = new VarRemapper.ParamRemapper(parameters, initialFrameSize);
213
214 inliner.doInline(codegen.v, remapper);
215 }
216
217
218 private void generateClosuresBodies() {
219 for (LambdaInfo info : expressionMap.values()) {
220 info.setNode(generateLambdaBody(info));
221 }
222 }
223
224 private MethodNode generateLambdaBody(LambdaInfo info) {
225 JetFunctionLiteral declaration = info.getFunctionLiteral();
226 FunctionDescriptor descriptor = info.getFunctionDescriptor();
227
228 MethodContext parentContext = codegen.getContext();
229
230 MethodContext context = parentContext.intoClosure(descriptor, codegen, typeMapper).intoInlinedLambda(descriptor);
231
232 JvmMethodSignature jvmMethodSignature = typeMapper.mapSignature(descriptor);
233 Method asmMethod = jvmMethodSignature.getAsmMethod();
234 MethodNode methodNode = new MethodNode(Opcodes.ASM4, getMethodAsmFlags(descriptor, context.getContextKind()), asmMethod.getName(), asmMethod.getDescriptor(), jvmMethodSignature.getGenericsSignature(), null);
235
236 MethodVisitor adapter = InlineCodegenUtil.wrapWithMaxLocalCalc(methodNode);
237
238 FunctionCodegen.generateMethodBody(adapter, descriptor, context, jvmMethodSignature, new FunctionGenerationStrategy.FunctionDefault(state, descriptor, declaration) {
239 @Override
240 public boolean generateLocalVarTable() {
241 return false;
242 }
243 }, codegen.getParentCodegen());
244 adapter.visitMaxs(-1, -1);
245
246 return methodNode;
247 }
248
249
250
251 @Override
252 public void afterParameterPut(@NotNull Type type, @Nullable StackValue stackValue, ValueParameterDescriptor valueParameterDescriptor) {
253 putCapturedInLocal(type, stackValue, valueParameterDescriptor, -1);
254 }
255
256 public void putCapturedInLocal(
257 @NotNull Type type, @Nullable StackValue stackValue, @Nullable ValueParameterDescriptor valueParameterDescriptor, int capturedParamIndex
258 ) {
259 if (!asFunctionInline && Type.VOID_TYPE != type) {
260 //TODO remap only inlinable closure => otherwise we could get a lot of problem
261 boolean couldBeRemapped = !shouldPutValue(type, stackValue, valueParameterDescriptor);
262 StackValue remappedIndex = couldBeRemapped ? stackValue : null;
263
264 ParameterInfo info = new ParameterInfo(type, false, couldBeRemapped ? -1 : codegen.getFrameMap().enterTemp(type), remappedIndex);
265
266 if (capturedParamIndex >= 0 && couldBeRemapped) {
267 CapturedParamInfo capturedParamInfo = activeLambda.getCapturedVars().get(capturedParamIndex);
268 capturedParamInfo.setRemapValue(remappedIndex != null ? remappedIndex : StackValue.local(info.getIndex(), info.getType()));
269 }
270
271 doWithParameter(info);
272 }
273 }
274
275 /*descriptor is null for captured vars*/
276 public boolean shouldPutValue(
277 @NotNull Type type,
278 @Nullable StackValue stackValue,
279 @Nullable ValueParameterDescriptor descriptor
280 ) {
281
282 if (stackValue == null) {
283 //default or vararg
284 return true;
285 }
286
287 //remap only inline functions (and maybe non primitives)
288 //TODO - clean asserion and remapping logic
289 if (isPrimitive(type) != isPrimitive(stackValue.type)) {
290 //don't remap boxing/unboxing primitives - lost identity and perfomance
291 return true;
292 }
293
294 if (stackValue instanceof StackValue.Local) {
295 return false;
296 }
297
298 if (stackValue instanceof StackValue.Composed) {
299 //see: Method.isSpecialStackValue: go through aload 0
300 if (codegen.getContext().isInliningLambda() && codegen.getContext().getContextDescriptor() instanceof AnonymousFunctionDescriptor) {
301 if (descriptor != null && !InlineUtil.hasNoinlineAnnotation(descriptor)) {
302 //TODO: check type of context
303 return false;
304 }
305 }
306 }
307 return true;
308 }
309
310 private void doWithParameter(ParameterInfo info) {
311 recordParamInfo(info, true);
312 putParameterOnStack(info);
313 }
314
315 private int recordParamInfo(ParameterInfo info, boolean addToFrame) {
316 Type type = info.type;
317 actualParameters.add(info);
318 if (info.getType().getSize() == 2) {
319 actualParameters.add(ParameterInfo.STUB);
320 }
321 if (addToFrame) {
322 return originalFunctionFrame.enterTemp(type);
323 }
324 return -1;
325 }
326
327 private void putParameterOnStack(ParameterInfo info) {
328 if (!info.isSkippedOrRemapped()) {
329 int index = info.getIndex();
330 Type type = info.type;
331 StackValue.local(index, type).store(type, codegen.v);
332 }
333 }
334
335 @Override
336 public void putHiddenParams() {
337 List<JvmMethodParameterSignature> types = jvmSignature.getValueParameters();
338
339 if (!isStaticMethod(functionDescriptor, context)) {
340 Type type = AsmTypeConstants.OBJECT_TYPE;
341 ParameterInfo info = new ParameterInfo(type, false, codegen.getFrameMap().enterTemp(type), -1);
342 recordParamInfo(info, false);
343 }
344
345 for (JvmMethodParameterSignature param : types) {
346 if (param.getKind() == JvmMethodParameterKind.VALUE) {
347 break;
348 }
349 Type type = param.getAsmType();
350 ParameterInfo info = new ParameterInfo(type, false, codegen.getFrameMap().enterTemp(type), -1);
351 recordParamInfo(info, false);
352 }
353
354 for (ListIterator<? extends ParameterInfo> iterator = actualParameters.listIterator(actualParameters.size()); iterator.hasPrevious(); ) {
355 ParameterInfo param = iterator.previous();
356 putParameterOnStack(param);
357 }
358 }
359
360 public void leaveTemps() {
361 FrameMap frameMap = codegen.getFrameMap();
362 for (ListIterator<? extends ParameterInfo> iterator = actualParameters.listIterator(actualParameters.size()); iterator.hasPrevious(); ) {
363 ParameterInfo param = iterator.previous();
364 if (!param.isSkippedOrRemapped()) {
365 frameMap.leaveTemp(param.type);
366 }
367 }
368 }
369
370 public static boolean isInliningClosure(JetExpression expression, ValueParameterDescriptor valueParameterDescriptora) {
371 //TODO deparenthisise
372 return expression instanceof JetFunctionLiteralExpression &&
373 !InlineUtil.hasNoinlineAnnotation(valueParameterDescriptora);
374 }
375
376 public void rememberClosure(JetFunctionLiteralExpression expression, Type type) {
377 ParameterInfo closureInfo = new ParameterInfo(type, true, -1, -1);
378 int index = recordParamInfo(closureInfo, true);
379
380 LambdaInfo info = new LambdaInfo(expression, typeMapper);
381 expressionMap.put(index, info);
382
383 closureInfo.setLambda(info);
384 }
385
386 private void putClosureParametersOnStack() {
387 //TODO: SORT
388 int currentSize = actualParameters.size();
389 for (LambdaInfo next : expressionMap.values()) {
390 if (next.closure != null) {
391 activeLambda = next;
392 next.setParamOffset(currentSize);
393 codegen.pushClosureOnStack(next.closure, false, this);
394 currentSize += next.getCapturedVarsSize();
395 }
396 }
397 activeLambda = null;
398 }
399
400 private List<CapturedParamInfo> getAllCaptured() {
401 //TODO: SORT
402 List<CapturedParamInfo> result = new ArrayList<CapturedParamInfo>();
403 for (LambdaInfo next : expressionMap.values()) {
404 if (next.closure != null) {
405 result.addAll(next.getCapturedVars());
406 }
407 }
408 return result;
409 }
410
411 @Nullable
412 @Override
413 public MemberCodegen getParentCodegen() {
414 return codegen.getParentCodegen();
415 }
416
417 public static CodegenContext getContext(DeclarationDescriptor descriptor, GenerationState state) {
418 if (descriptor instanceof PackageFragmentDescriptor) {
419 return new PackageContext((PackageFragmentDescriptor) descriptor, null, null);
420 }
421
422 CodegenContext parent = getContext(descriptor.getContainingDeclaration(), state);
423
424 if (descriptor instanceof ClassDescriptor) {
425 OwnerKind kind = DescriptorUtils.isTrait(descriptor) ? OwnerKind.TRAIT_IMPL : OwnerKind.IMPLEMENTATION;
426 return parent.intoClass((ClassDescriptor) descriptor, kind, state);
427 }
428 else if (descriptor instanceof FunctionDescriptor) {
429 return parent.intoFunction((FunctionDescriptor) descriptor);
430 }
431
432 throw new IllegalStateException("Couldn't build context for " + descriptorName(descriptor));
433 }
434
435 private static boolean isStaticMethod(FunctionDescriptor functionDescriptor, MethodContext context) {
436 return (getMethodAsmFlags(functionDescriptor, context.getContextKind()) & Opcodes.ACC_STATIC) != 0;
437 }
438
439 @NotNull
440 public static String getNodeText(@Nullable MethodNode node) {
441 if (node == null) {
442 return "Not generated";
443 }
444 Textifier p = new Textifier();
445 node.accept(new TraceMethodVisitor(p));
446 StringWriter sw = new StringWriter();
447 p.print(new PrintWriter(sw));
448 sw.flush();
449 return node.name + ": \n " + sw.getBuffer().toString();
450 }
451
452 private static String descriptorName(DeclarationDescriptor descriptor) {
453 return DescriptorRenderer.SHORT_NAMES_IN_TYPES.render(descriptor);
454 }
455
456 @Override
457 public void genValueAndPut(
458 @NotNull ValueParameterDescriptor valueParameterDescriptor,
459 @NotNull JetExpression argumentExpression,
460 @NotNull Type parameterType
461 ) {
462 //TODO deparenthisise
463 if (isInliningClosure(argumentExpression, valueParameterDescriptor)) {
464 rememberClosure((JetFunctionLiteralExpression) argumentExpression, parameterType);
465 } else {
466 StackValue value = codegen.gen(argumentExpression);
467 if (shouldPutValue(parameterType, value, valueParameterDescriptor)) {
468 value.put(parameterType, codegen.v);
469 }
470 afterParameterPut(parameterType, value, valueParameterDescriptor);
471 }
472 }
473
474 @Override
475 public void putCapturedValueOnStack(
476 @NotNull StackValue stackValue, @NotNull Type valueType, int paramIndex
477 ) {
478 if (shouldPutValue(stackValue.type, stackValue, null)) {
479 stackValue.put(stackValue.type, codegen.v);
480 }
481 putCapturedInLocal(stackValue.type, stackValue, null, paramIndex);
482 }
483 }