001 /* 002 * Copyright 2010-2015 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.kotlin.codegen.inline; 018 019 import org.jetbrains.annotations.NotNull; 020 import org.jetbrains.annotations.Nullable; 021 import org.jetbrains.kotlin.codegen.StackValue; 022 import org.jetbrains.kotlin.resolve.jvm.AsmTypes; 023 import org.jetbrains.org.objectweb.asm.Label; 024 import org.jetbrains.org.objectweb.asm.MethodVisitor; 025 import org.jetbrains.org.objectweb.asm.Opcodes; 026 import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter; 027 028 import static org.jetbrains.kotlin.codegen.inline.LocalVarRemapper.RemapStatus.*; 029 030 public class LocalVarRemapper { 031 032 private final Parameters params; 033 private final int actualParamsSize; 034 035 private final StackValue[] remapValues; 036 private final int additionalShift; 037 038 public LocalVarRemapper(Parameters params, int additionalShift) { 039 this.additionalShift = additionalShift; 040 this.params = params; 041 042 remapValues = new StackValue [params.getArgsSizeOnStack()]; 043 044 int realSize = 0; 045 for (ParameterInfo info : params) { 046 Integer shift = params.getDeclarationSlot(info); 047 if (!info.isSkippedOrRemapped()) { 048 remapValues[shift] = StackValue.local(realSize, AsmTypes.OBJECT_TYPE); 049 realSize += info.getType().getSize(); 050 } 051 else { 052 remapValues[shift] = info.isRemapped() ? info.getRemapValue() : null; 053 } 054 } 055 056 actualParamsSize = realSize; 057 } 058 059 public RemapInfo doRemap(int index) { 060 int remappedIndex; 061 062 if (index < params.getArgsSizeOnStack()) { 063 ParameterInfo info = params.getParameterByDeclarationSlot(index); 064 StackValue remapped = remapValues[index]; 065 if (info.isSkipped || remapped == null) { 066 return new RemapInfo(info); 067 } 068 if (info.isRemapped()) { 069 return new RemapInfo(remapped, info, REMAPPED); 070 } else { 071 remappedIndex = ((StackValue.Local)remapped).index; 072 } 073 } else { 074 remappedIndex = actualParamsSize - params.getArgsSizeOnStack() + index; //captured params not used directly in this inlined method, they used in closure 075 } 076 077 return new RemapInfo(StackValue.local(remappedIndex + additionalShift, AsmTypes.OBJECT_TYPE), null, SHIFT); 078 } 079 080 public RemapInfo remap(int index) { 081 RemapInfo info = doRemap(index); 082 if (FAIL == info.status) { 083 assert info.parameterInfo != null : "Parameter info should be not null"; 084 throw new RuntimeException("Trying to access skipped parameter: " + info.parameterInfo.type + " at " +index); 085 } 086 return info; 087 } 088 089 public void visitIincInsn(int var, int increment, MethodVisitor mv) { 090 RemapInfo remap = remap(var); 091 assert remap.value instanceof StackValue.Local; 092 mv.visitIincInsn(((StackValue.Local) remap.value).index, increment); 093 } 094 095 public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index, MethodVisitor mv) { 096 RemapInfo info = doRemap(index); 097 //add entries only for shifted vars 098 if (SHIFT == info.status) { 099 int newIndex = ((StackValue.Local) info.value).index; 100 mv.visitLocalVariable(name, desc, signature, start, end, newIndex); 101 } 102 } 103 104 105 public void visitVarInsn(int opcode, int var, InstructionAdapter mv) { 106 RemapInfo remapInfo = remap(var); 107 StackValue value = remapInfo.value; 108 if (value instanceof StackValue.Local) { 109 if (remapInfo.parameterInfo != null) { 110 //All remapped value parameters can't be rewritten except case of default ones. 111 //On remapping default parameter to actual value there is only one instruction that writes to it according to mask value 112 //but if such parameter remapped then it passed and this mask branch code never executed 113 //TODO add assertion about parameter default value: descriptor is required 114 opcode = value.type.getOpcode(InlineCodegenUtil.isStoreInstruction(opcode) ? Opcodes.ISTORE : Opcodes.ILOAD); 115 } 116 mv.visitVarInsn(opcode, ((StackValue.Local) value).index); 117 if (remapInfo.parameterInfo != null) { 118 StackValue.coerce(value.type, remapInfo.parameterInfo.type, mv); 119 } 120 } else { 121 assert remapInfo.parameterInfo != null : "Non local value should have parameter info"; 122 value.put(remapInfo.parameterInfo.type, mv); 123 } 124 } 125 126 public enum RemapStatus { 127 SHIFT, 128 REMAPPED, 129 FAIL 130 } 131 132 public static class RemapInfo { 133 public final StackValue value; 134 public final ParameterInfo parameterInfo; 135 public final RemapStatus status; 136 137 public RemapInfo(@NotNull StackValue value, @Nullable ParameterInfo info, RemapStatus remapStatus) { 138 this.value = value; 139 parameterInfo = info; 140 this.status = remapStatus; 141 } 142 143 public RemapInfo(@NotNull ParameterInfo info) { 144 this.value = null; 145 parameterInfo = info; 146 this.status = FAIL; 147 } 148 } 149 }