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.Type;
023    import org.jetbrains.asm4.tree.AbstractInsnNode;
024    import org.jetbrains.asm4.tree.FieldInsnNode;
025    import org.jetbrains.asm4.tree.MethodNode;
026    
027    import java.util.Collection;
028    import java.util.List;
029    import java.util.Map;
030    
031    public class RegeneratedLambdaFieldRemapper extends LambdaFieldRemapper {
032    
033        private final String oldOwnerType;
034    
035        private final String newOwnerType;
036    
037        private final Parameters parameters;
038    
039        private final Map<String, LambdaInfo> recapturedLambdas;
040    
041        public RegeneratedLambdaFieldRemapper(
042                String oldOwnerType,
043                String newOwnerType,
044                Parameters parameters,
045                Map<String, LambdaInfo> recapturedLambdas,
046                LambdaFieldRemapper remapper
047        ) {
048            super(oldOwnerType, remapper, parameters);
049            this.oldOwnerType = oldOwnerType;
050            this.newOwnerType = newOwnerType;
051            this.parameters = parameters;
052            this.recapturedLambdas = recapturedLambdas;
053        }
054    
055        @Override
056        public AbstractInsnNode doTransform(
057                MethodNode node, FieldInsnNode fieldInsnNode, CapturedParamInfo capturedField
058        ) {
059            boolean isRecaptured = isRecapturedLambdaType(fieldInsnNode.owner);
060    
061            if (!isRecaptured && capturedField.getLambda() != null) {
062                //strict inlining
063                return super.doTransform(node, fieldInsnNode, capturedField);
064            }
065    
066            AbstractInsnNode loadThis = getPreviousThis(fieldInsnNode);
067    
068            int opcode = Opcodes.GETSTATIC;
069    
070            String descriptor = Type.getObjectType(newOwnerType).getDescriptor();
071    
072            //HACK: it would be reverted again to ALOAD 0 later
073            FieldInsnNode thisStub = new FieldInsnNode(opcode, newOwnerType, "$$$this", descriptor);
074    
075            node.instructions.insertBefore(loadThis, thisStub);
076            node.instructions.remove(loadThis);
077    
078            fieldInsnNode.owner = newOwnerType;
079            fieldInsnNode.name = isRecaptured || capturedField.getRecapturedFrom() != null ? LambdaTransformer.getNewFieldName(capturedField.getFieldName()) : capturedField.getFieldName();
080    
081            return fieldInsnNode;
082        }
083    
084        @Override
085        public List<CapturedParamInfo> markRecaptured(List<CapturedParamInfo> originalCaptured, LambdaInfo lambda) {
086            List<CapturedParamInfo> captured = parameters.getCaptured();
087            for (CapturedParamInfo originalField : originalCaptured) {
088                for (CapturedParamInfo capturedParamInfo : captured) {
089                    if (capturedParamInfo.getRecapturedFrom() == lambda) {
090                        if (capturedParamInfo.getFieldName().equals(LambdaTransformer.getNewFieldName(originalField.getFieldName()))) {
091                            originalField.setRecapturedFrom(lambda);//just mark recaptured
092                        }
093                    }
094                }
095            }
096            return originalCaptured;
097        }
098    
099        @Override
100        public boolean canProcess(String owner, String currentLambdaType) {
101            return super.canProcess(owner, currentLambdaType) || isRecapturedLambdaType(owner);
102        }
103    
104        private boolean isRecapturedLambdaType(String owner) {
105            return recapturedLambdas.containsKey(owner);
106        }
107    
108        @Nullable
109        @Override
110        public CapturedParamInfo findField(FieldInsnNode fieldInsnNode, Collection<CapturedParamInfo> captured) {
111            if (isRecapturedLambdaType(fieldInsnNode.owner)) {
112                LambdaInfo info = recapturedLambdas.get(fieldInsnNode.owner);
113                return super.findField(fieldInsnNode, info.getCapturedVars());
114            }
115            else {
116                return super.findField(fieldInsnNode, captured);
117            }
118        }
119    
120        @Override
121        public boolean shouldPatch(@NotNull FieldInsnNode node) {
122            //parent is inlined so we need patch instruction chain
123            return shouldPatchByMe(node) || parent.shouldPatch(node);
124        }
125    
126        private boolean shouldPatchByMe(@NotNull FieldInsnNode node) {
127            //parent is inlined so we need patch instruction chain
128            //aloading inlined this
129            return parent.isRoot() && node.owner.equals(getLambdaInternalName()) && node.name.equals("this$0");
130        }
131    
132        @NotNull
133        @Override
134        public AbstractInsnNode patch(@NotNull FieldInsnNode fieldInsnNode, @NotNull MethodNode node) {
135            if (!shouldPatchByMe(fieldInsnNode)) {
136                return parent.patch(fieldInsnNode, node);
137            }
138            //parent is inlined so we need patch instruction chain
139            AbstractInsnNode previous = fieldInsnNode.getPrevious();
140            AbstractInsnNode nextInstruction = fieldInsnNode.getNext();
141            if (!(nextInstruction instanceof FieldInsnNode)) {
142                throw new IllegalStateException(
143                        "Instruction after inlined one should be field access: " + nextInstruction);
144            }
145            if (!(previous instanceof FieldInsnNode)) {
146                throw new IllegalStateException("Instruction before inlined one should be field access: " + previous);
147            }
148            FieldInsnNode next = (FieldInsnNode) nextInstruction;
149            node.instructions.remove(next.getPrevious());
150            next.owner = Type.getType(((FieldInsnNode) previous).desc).getInternalName();
151            next.name = node.name.equals("this$0") ? node.name : LambdaTransformer.getNewFieldName(next.name);
152    
153            return next;
154        }
155    }