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 int allParamsSize; 033 private final Parameters params; 034 private final int actualParamsSize; 035 036 private final StackValue[] remapValues; 037 038 private final int additionalShift; 039 040 public LocalVarRemapper(Parameters params, int additionalShift) { 041 this.additionalShift = additionalShift; 042 this.allParamsSize = params.totalSize(); 043 this.params = params; 044 045 int realSize = 0; 046 remapValues = new StackValue [params.totalSize()]; 047 048 int index = 0; 049 for (ParameterInfo info : params) { 050 if (!info.isSkippedOrRemapped()) { 051 remapValues[index] = StackValue.local(realSize, AsmTypes.OBJECT_TYPE); 052 realSize += info.getType().getSize(); 053 } else { 054 remapValues[index] = info.isRemapped() ? info.getRemapValue() : null; 055 } 056 index++; 057 } 058 059 actualParamsSize = realSize; 060 } 061 062 public RemapInfo doRemap(int index) { 063 int remappedIndex; 064 065 if (index < allParamsSize) { 066 ParameterInfo info = params.get(index); 067 StackValue remapped = remapValues[index]; 068 if (info.isSkipped || remapped == null) { 069 return new RemapInfo(info); 070 } 071 if (info.isRemapped()) { 072 return new RemapInfo(remapped, info, REMAPPED); 073 } else { 074 remappedIndex = ((StackValue.Local)remapped).index; 075 } 076 } else { 077 remappedIndex = actualParamsSize - params.totalSize() + index; //captured params not used directly in this inlined method, they used in closure 078 } 079 080 return new RemapInfo(StackValue.local(remappedIndex + additionalShift, AsmTypes.OBJECT_TYPE), null, SHIFT); 081 } 082 083 public RemapInfo remap(int index) { 084 RemapInfo info = doRemap(index); 085 if (FAIL == info.status) { 086 assert info.parameterInfo != null : "Parameter info should be not null"; 087 throw new RuntimeException("Trying to access skipped parameter: " + info.parameterInfo.type + " at " +index); 088 } 089 return info; 090 } 091 092 public void visitIincInsn(int var, int increment, MethodVisitor mv) { 093 RemapInfo remap = remap(var); 094 assert remap.value instanceof StackValue.Local; 095 mv.visitIincInsn(((StackValue.Local) remap.value).index, increment); 096 } 097 098 public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index, MethodVisitor mv) { 099 RemapInfo info = doRemap(index); 100 if (SHIFT == info.status) { 101 //add entries only for shifted vars 102 mv.visitLocalVariable(name, desc, signature, start, end, ((StackValue.Local) info.value).index); 103 } 104 } 105 106 107 public void visitVarInsn(int opcode, int var, InstructionAdapter mv) { 108 RemapInfo remapInfo = remap(var); 109 StackValue value = remapInfo.value; 110 if (value instanceof StackValue.Local) { 111 if (remapInfo.parameterInfo != null) { 112 opcode = value.type.getOpcode(Opcodes.ILOAD); 113 } 114 mv.visitVarInsn(opcode, ((StackValue.Local) value).index); 115 if (remapInfo.parameterInfo != null) { 116 StackValue.coerce(value.type, remapInfo.parameterInfo.type, mv); 117 } 118 } else { 119 assert remapInfo.parameterInfo != null : "Non local value should have parameter info"; 120 value.put(remapInfo.parameterInfo.type, mv); 121 } 122 } 123 124 public enum RemapStatus { 125 SHIFT, 126 REMAPPED, 127 FAIL 128 } 129 130 private static class RemapInfo { 131 public final StackValue value; 132 public final ParameterInfo parameterInfo; 133 public final RemapStatus status; 134 135 public RemapInfo(@NotNull StackValue value, @Nullable ParameterInfo info, RemapStatus remapStatus) { 136 this.value = value; 137 parameterInfo = info; 138 this.status = remapStatus; 139 } 140 141 public RemapInfo(@NotNull ParameterInfo info) { 142 this.value = null; 143 parameterInfo = info; 144 this.status = FAIL; 145 } 146 } 147 }