001    /*
002     * Copyright 2010-2013 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.jet.codegen.inline;
018    
019    import org.jetbrains.annotations.NotNull;
020    import org.jetbrains.annotations.Nullable;
021    import org.jetbrains.asm4.Opcodes;
022    import org.jetbrains.asm4.tree.AbstractInsnNode;
023    import org.jetbrains.asm4.tree.FieldInsnNode;
024    import org.jetbrains.asm4.tree.MethodNode;
025    import org.jetbrains.jet.codegen.StackValue;
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        public void addCapturedFields(LambdaInfo lambdaInfo, ParametersBuilder builder) {
045            for (CapturedParamInfo info : lambdaInfo.getCapturedVars()) {
046                builder.addCapturedParam(info, info);
047            }
048        }
049    
050        public boolean canProcess(@NotNull String fieldOwner) {
051            return fieldOwner.equals(getLambdaInternalName());
052        }
053    
054        @Nullable
055        public AbstractInsnNode transformIfNeeded(
056                @NotNull List<AbstractInsnNode> capturedFieldAccess,
057                @NotNull MethodNode node
058        ) {
059            if (capturedFieldAccess.size() == 1) {
060                //just aload
061                return null;
062            }
063    
064            return transformIfNeeded(capturedFieldAccess, 1, node);
065        }
066    
067        @Nullable
068        private AbstractInsnNode transformIfNeeded(
069                @NotNull List<AbstractInsnNode> capturedFieldAccess,
070                int currentInstruction,
071                @NotNull MethodNode node
072        ) {
073            AbstractInsnNode transformed = null;
074            boolean checkParent = !isRoot() && currentInstruction < capturedFieldAccess.size() - 1;
075            if (checkParent) {
076                transformed = parent.transformIfNeeded(capturedFieldAccess, currentInstruction + 1, node);
077            }
078    
079            if (transformed == null) {
080                //if parent couldn't transform
081                FieldInsnNode insnNode = (FieldInsnNode) capturedFieldAccess.get(currentInstruction);
082                if (canProcess(insnNode.owner)) {
083                    insnNode.name = "$$$" + insnNode.name;
084                    insnNode.setOpcode(Opcodes.GETSTATIC);
085    
086                    AbstractInsnNode next = capturedFieldAccess.get(0);
087                    while (next != insnNode) {
088                        AbstractInsnNode toDelete = next;
089                        next = next.getNext();
090                        node.instructions.remove(toDelete);
091                    }
092    
093                    transformed = capturedFieldAccess.get(capturedFieldAccess.size() - 1);
094                }
095            }
096    
097            return transformed;
098        }
099    
100        public CapturedParamInfo findField(@NotNull FieldInsnNode fieldInsnNode) {
101            return findField(fieldInsnNode, params.getCaptured());
102        }
103    
104        @Nullable
105        public CapturedParamInfo findField(@NotNull FieldInsnNode fieldInsnNode, @NotNull Collection<CapturedParamInfo> captured) {
106            for (CapturedParamInfo valueDescriptor : captured) {
107                if (valueDescriptor.getFieldName().equals(fieldInsnNode.name) && fieldInsnNode.owner.equals(valueDescriptor.getContainingLambdaName())) {
108                    return valueDescriptor;
109                }
110            }
111            return null;
112        }
113    
114        public FieldRemapper getParent() {
115            return parent;
116        }
117    
118        public String getLambdaInternalName() {
119            return lambdaInternalName;
120        }
121    
122        public boolean isRoot() {
123            return parent == null;
124        }
125    
126        @Nullable
127        public StackValue getFieldForInline(@NotNull FieldInsnNode node, @Nullable StackValue prefix) {
128            CapturedParamInfo field = MethodInliner.findCapturedField(node, this);
129            return field.getRemapValue();
130        }
131    }