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 endCall(inlineCall(node));
130 }
131 catch (CompilationException e) {
132 throw e;
133 }
134 catch (Exception e) {
135 boolean generateNodeText = !(e instanceof InlineException);
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 (generateNodeText ? ("\ncause: " + getNodeText(node)) : ""),
141 e, call.getCallElement());
142 }
143
144
145 }
146
147 private void endCall(@NotNull InlineResult result) {
148 leaveTemps();
149
150 state.getFactory().removeInlinedClasses(result.getClassesToRemove());
151 }
152
153 @NotNull
154 private MethodNode createMethodNode(CallableMethod callableMethod)
155 throws ClassNotFoundException, IOException {
156 MethodNode node;
157 if (functionDescriptor instanceof DeserializedSimpleFunctionDescriptor) {
158 VirtualFile file = InlineCodegenUtil.getVirtualFileForCallable((DeserializedSimpleFunctionDescriptor) functionDescriptor, state);
159 String methodDesc = callableMethod.getAsmMethod().getDescriptor();
160 DeclarationDescriptor parentDescriptor = functionDescriptor.getContainingDeclaration();
161 if (DescriptorUtils.isTrait(parentDescriptor)) {
162 methodDesc = "(" + typeMapper.mapType((ClassDescriptor) parentDescriptor).getDescriptor() + methodDesc.substring(1);
163 }
164 node = InlineCodegenUtil.getMethodNode(file.getInputStream(), functionDescriptor.getName().asString(), methodDesc);
165
166 if (node == null) {
167 throw new RuntimeException("Couldn't obtain compiled function body for " + descriptorName(functionDescriptor));
168 }
169 }
170 else {
171 PsiElement element = BindingContextUtils.descriptorToDeclaration(bindingContext, functionDescriptor);
172
173 if (element == null) {
174 throw new RuntimeException("Couldn't find declaration for function " + descriptorName(functionDescriptor));
175 }
176
177 JvmMethodSignature jvmSignature = typeMapper.mapSignature(functionDescriptor, context.getContextKind());
178 Method asmMethod = jvmSignature.getAsmMethod();
179 node = new MethodNode(Opcodes.ASM4,
180 getMethodAsmFlags(functionDescriptor, context.getContextKind()),
181 asmMethod.getName(),
182 asmMethod.getDescriptor(),
183 jvmSignature.getGenericsSignature(),
184 null);
185
186 //for maxLocals calculation
187 MethodVisitor adapter = InlineCodegenUtil.wrapWithMaxLocalCalc(node);
188 FunctionCodegen.generateMethodBody(adapter, functionDescriptor, context.getParentContext().intoFunction(functionDescriptor),
189 jvmSignature,
190 new FunctionGenerationStrategy.FunctionDefault(state,
191 functionDescriptor,
192 (JetDeclarationWithBody) element),
193 getParentCodegen());
194 adapter.visitMaxs(-1, -1);
195 adapter.visitEnd();
196 }
197 return node;
198 }
199
200 private InlineResult inlineCall(MethodNode node) {
201 generateClosuresBodies();
202
203 List<ParameterInfo> realParams = new ArrayList<ParameterInfo>(actualParameters);
204
205 putClosureParametersOnStack();
206
207 List<CapturedParamInfo> captured = getAllCaptured();
208
209 Parameters parameters = new Parameters(realParams, Parameters.shiftAndAddStubs(captured, realParams.size()));
210
211 InliningContext info =
212 new InliningContext(expressionMap, null, null, null, state,
213 codegen.getInlineNameGenerator().subGenerator(functionDescriptor.getName().asString()),
214 codegen.getContext(), call, Collections.<String, String>emptyMap(), false, false);
215
216 MethodInliner inliner = new MethodInliner(node, parameters, info, new FieldRemapper(null, null, parameters), isSameModule, "Method inlining " + call.getCallElement().getText()); //with captured
217
218 LocalVarRemapper remapper = new LocalVarRemapper(parameters, initialFrameSize);
219
220 return inliner.doInline(codegen.v, remapper);
221 }
222
223 private void generateClosuresBodies() {
224 for (LambdaInfo info : expressionMap.values()) {
225 info.setNode(generateLambdaBody(info));
226 }
227 }
228
229 private MethodNode generateLambdaBody(LambdaInfo info) {
230 JetFunctionLiteral declaration = info.getFunctionLiteral();
231 FunctionDescriptor descriptor = info.getFunctionDescriptor();
232
233 MethodContext parentContext = codegen.getContext();
234
235 MethodContext context = parentContext.intoClosure(descriptor, codegen, typeMapper).intoInlinedLambda(descriptor);
236
237 JvmMethodSignature jvmMethodSignature = typeMapper.mapSignature(descriptor);
238 Method asmMethod = jvmMethodSignature.getAsmMethod();
239 MethodNode methodNode = new MethodNode(Opcodes.ASM4, getMethodAsmFlags(descriptor, context.getContextKind()), asmMethod.getName(), asmMethod.getDescriptor(), jvmMethodSignature.getGenericsSignature(), null);
240
241 MethodVisitor adapter = InlineCodegenUtil.wrapWithMaxLocalCalc(methodNode);
242
243 FunctionCodegen.generateMethodBody(adapter, descriptor, context, jvmMethodSignature, new FunctionGenerationStrategy.FunctionDefault(state, descriptor, declaration), 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 + " " + node.desc + ": \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 }