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.FieldInsnNode;
024    import org.jetbrains.jet.codegen.StackValue;
025    
026    import java.util.Collection;
027    import java.util.Map;
028    
029    public class RegeneratedLambdaFieldRemapper extends FieldRemapper {
030    
031        private final String oldOwnerType;
032    
033        private final String newOwnerType;
034    
035        private final Parameters parameters;
036    
037        private final Map<String, LambdaInfo> recapturedLambdas;
038    
039        public RegeneratedLambdaFieldRemapper(
040                String oldOwnerType,
041                String newOwnerType,
042                Parameters parameters,
043                Map<String, LambdaInfo> recapturedLambdas,
044                FieldRemapper remapper
045        ) {
046            super(oldOwnerType, remapper, parameters);
047            this.oldOwnerType = oldOwnerType;
048            this.newOwnerType = newOwnerType;
049            this.parameters = parameters;
050            this.recapturedLambdas = recapturedLambdas;
051        }
052    
053        @Override
054        public boolean canProcess(@NotNull String fieldOwner, boolean isFolding) {
055            return super.canProcess(fieldOwner, isFolding) || isRecapturedLambdaType(fieldOwner);
056        }
057    
058        private boolean isRecapturedLambdaType(String owner) {
059            return recapturedLambdas.containsKey(owner);
060        }
061    
062        @Nullable
063        @Override
064        public CapturedParamInfo findField(@NotNull FieldInsnNode fieldInsnNode, @NotNull Collection<CapturedParamInfo> captured) {
065            boolean searchInParent = !canProcess(fieldInsnNode.owner, false);
066            if (searchInParent) {
067                return parent.findField(fieldInsnNode);
068            } else {
069                return findFieldInMyCaptured(fieldInsnNode);
070            }
071        }
072    
073        @Nullable
074        public CapturedParamInfo findFieldInMyCaptured(@NotNull FieldInsnNode fieldInsnNode) {
075            return super.findField(fieldInsnNode, parameters.getCaptured());
076        }
077    
078        @Nullable
079        @Override
080        public StackValue getFieldForInline(@NotNull FieldInsnNode node, @Nullable StackValue prefix) {
081            assert node.name.startsWith("$$$") : "Captured field template should start with $$$ prefix";
082            FieldInsnNode fin = new FieldInsnNode(node.getOpcode(), node.owner, node.name.substring(3), node.desc);
083            CapturedParamInfo field = findFieldInMyCaptured(fin);
084    
085            boolean searchInParent = false;
086            if (field == null) {
087                field = findFieldInMyCaptured(new FieldInsnNode(Opcodes.GETSTATIC, oldOwnerType, "this$0", Type.getObjectType(parent.getLambdaInternalName()).getDescriptor()));
088                searchInParent = true;
089                if (field == null) {
090                    throw new IllegalStateException("Could find captured this " + getLambdaInternalName());
091                }
092            }
093    
094            StackValue result =
095                    StackValue.composed(prefix == null ? StackValue.local(0, Type.getObjectType(getLambdaInternalName())) : prefix,
096                                        StackValue.field(field.getType(),
097                                                         Type.getObjectType(newOwnerType), /*TODO owner type*/
098                                                         field.getNewFieldName(), false)
099            );
100    
101            return searchInParent ? parent.getFieldForInline(node, result) : result;
102        }
103    }