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