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