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