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