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