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    }