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.asm4.tree.VarInsnNode;
026    
027    import java.util.Collection;
028    import java.util.List;
029    
030    import static org.jetbrains.jet.codegen.inline.MethodInliner.getPreviousNoLabelNoLine;
031    
032    public class LambdaFieldRemapper {
033    
034        private String lambdaInternalName;
035    
036        protected LambdaFieldRemapper parent;
037    
038        private final Parameters params;
039    
040        public LambdaFieldRemapper(@Nullable String lambdaInternalName, @Nullable LambdaFieldRemapper parent, @NotNull Parameters methodParams) {
041            this.lambdaInternalName = lambdaInternalName;
042            this.parent = parent;
043            params = methodParams;
044        }
045    
046        public AbstractInsnNode doTransform(MethodNode node, FieldInsnNode fieldInsnNode, CapturedParamInfo capturedField) {
047            AbstractInsnNode loadThis = getPreviousThis(fieldInsnNode);
048    
049            int opcode = fieldInsnNode.getOpcode() == Opcodes.GETFIELD ? capturedField.getType().getOpcode(Opcodes.ILOAD) : capturedField.getType().getOpcode(Opcodes.ISTORE);
050            VarInsnNode newInstruction = new VarInsnNode(opcode, capturedField.getIndex());
051    
052            node.instructions.remove(loadThis); //remove aload this
053            node.instructions.insertBefore(fieldInsnNode, newInstruction);
054            node.instructions.remove(fieldInsnNode); //remove aload field
055    
056            return newInstruction;
057        }
058    
059        protected static AbstractInsnNode getPreviousThis(FieldInsnNode fieldInsnNode) {
060            AbstractInsnNode loadThis = getPreviousNoLabelNoLine(fieldInsnNode);
061    
062            assert loadThis.getType() == AbstractInsnNode.VAR_INSN || loadThis.getType() == AbstractInsnNode.FIELD_INSN :
063                    "Field access instruction should go after load this but goes after " + loadThis;
064            assert loadThis.getOpcode() == Opcodes.ALOAD || loadThis.getOpcode() == Opcodes.GETSTATIC :
065                    "This should be loaded by ALOAD or GETSTATIC but " + loadThis.getOpcode();
066            return loadThis;
067        }
068    
069        public List<CapturedParamInfo> markRecaptured(List<CapturedParamInfo> originalCaptured, LambdaInfo lambda) {
070            return originalCaptured;
071        }
072    
073    
074        public boolean canProcess(@NotNull String owner, @NotNull String currentLambdaType) {
075            return owner.equals(currentLambdaType);
076        }
077    
078        @Nullable
079        public CapturedParamInfo findField(@NotNull FieldInsnNode fieldInsnNode, @NotNull Collection<CapturedParamInfo> captured) {
080            for (CapturedParamInfo valueDescriptor : captured) {
081                if (valueDescriptor.getFieldName().equals(fieldInsnNode.name)) {
082                    return valueDescriptor;
083                }
084            }
085            return null;
086        }
087    
088        public LambdaFieldRemapper getParent() {
089            return parent;
090        }
091        public String getLambdaInternalName() {
092            return lambdaInternalName;
093        }
094    
095        public boolean isRoot() {
096            return parent == null;
097        }
098    
099        public boolean shouldPatch(@NotNull FieldInsnNode node) {
100            return !isRoot() && parent.shouldPatch(node);
101        }
102    
103        @NotNull
104        public AbstractInsnNode patch(@NotNull FieldInsnNode field, @NotNull MethodNode node) {
105            //parent is inlined so we need patch instruction chain
106            if (!isRoot()){
107                return parent.patch(field, node);
108            }
109            throw new IllegalStateException("Should be invoked");
110        }
111    }