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.org.objectweb.asm.Opcodes; 023 import org.jetbrains.org.objectweb.asm.Type; 024 import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode; 025 import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode; 026 import org.jetbrains.org.objectweb.asm.tree.MethodNode; 027 028 import java.util.Collection; 029 import java.util.List; 030 031 public class FieldRemapper { 032 033 private final String lambdaInternalName; 034 035 protected FieldRemapper parent; 036 037 private final Parameters params; 038 039 public FieldRemapper(@Nullable String lambdaInternalName, @Nullable FieldRemapper parent, @NotNull Parameters methodParams) { 040 this.lambdaInternalName = lambdaInternalName; 041 this.parent = parent; 042 params = methodParams; 043 } 044 045 protected boolean canProcess(@NotNull String fieldOwner, String fieldName, boolean isFolding) { 046 return fieldOwner.equals(getLambdaInternalName()) && 047 //don't process general field of anonymous objects 048 InlineCodegenUtil.isCapturedFieldName(fieldName); 049 } 050 051 @Nullable 052 public AbstractInsnNode foldFieldAccessChainIfNeeded( 053 @NotNull List<AbstractInsnNode> capturedFieldAccess, 054 @NotNull MethodNode node 055 ) { 056 if (capturedFieldAccess.size() == 1) { 057 //just aload 058 return null; 059 } 060 061 return foldFieldAccessChainIfNeeded(capturedFieldAccess, 1, node); 062 } 063 064 //TODO: seems that this method is redundant but it added from safety purposes before new milestone 065 public boolean processNonAload0FieldAccessChains(boolean isInlinedLambda) { 066 return false; 067 } 068 069 @Nullable 070 private AbstractInsnNode foldFieldAccessChainIfNeeded( 071 @NotNull List<AbstractInsnNode> capturedFieldAccess, 072 int currentInstruction, 073 @NotNull MethodNode node 074 ) { 075 AbstractInsnNode transformed = null; 076 boolean checkParent = !isRoot() && currentInstruction < capturedFieldAccess.size() - 1; 077 if (checkParent) { 078 transformed = parent.foldFieldAccessChainIfNeeded(capturedFieldAccess, currentInstruction + 1, node); 079 } 080 081 if (transformed == null) { 082 //if parent couldn't transform 083 FieldInsnNode insnNode = (FieldInsnNode) capturedFieldAccess.get(currentInstruction); 084 if (canProcess(insnNode.owner, insnNode.name, true)) { 085 insnNode.name = "$$$" + insnNode.name; 086 insnNode.setOpcode(Opcodes.GETSTATIC); 087 088 AbstractInsnNode next = capturedFieldAccess.get(0); 089 while (next != insnNode) { 090 AbstractInsnNode toDelete = next; 091 next = next.getNext(); 092 node.instructions.remove(toDelete); 093 } 094 095 transformed = capturedFieldAccess.get(capturedFieldAccess.size() - 1); 096 } 097 } 098 099 return transformed; 100 } 101 102 public CapturedParamInfo findField(@NotNull FieldInsnNode fieldInsnNode) { 103 return findField(fieldInsnNode, params.getCaptured()); 104 } 105 106 @Nullable 107 protected CapturedParamInfo findField(@NotNull FieldInsnNode fieldInsnNode, @NotNull Collection<CapturedParamInfo> captured) { 108 for (CapturedParamInfo valueDescriptor : captured) { 109 if (valueDescriptor.getOriginalFieldName().equals(fieldInsnNode.name) && fieldInsnNode.owner.equals(valueDescriptor.getContainingLambdaName())) { 110 return valueDescriptor; 111 } 112 } 113 return null; 114 } 115 116 public FieldRemapper getParent() { 117 return parent; 118 } 119 120 public String getLambdaInternalName() { 121 return lambdaInternalName; 122 } 123 124 public String getNewLambdaInternalName() { 125 return lambdaInternalName; 126 } 127 128 public boolean isRoot() { 129 return parent == null; 130 } 131 132 @Nullable 133 public StackValue getFieldForInline(@NotNull FieldInsnNode node, @Nullable StackValue prefix) { 134 CapturedParamInfo field = MethodInliner.findCapturedField(node, this); 135 return field.getRemapValue(); 136 } 137 138 public boolean isInsideInliningLambda() { 139 return !isRoot() && parent.isInsideInliningLambda(); 140 } 141 }