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.Type;
024 import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode;
025
026 import java.util.Collection;
027 import java.util.Map;
028
029 public class RegeneratedLambdaFieldRemapper extends FieldRemapper {
030 private final String oldOwnerType;
031 private final String newOwnerType;
032 private final Parameters parameters;
033 private final Map<String, LambdaInfo> recapturedLambdas;
034 private final boolean isConstructor;
035
036 public RegeneratedLambdaFieldRemapper(
037 @NotNull String oldOwnerType,
038 @NotNull String newOwnerType,
039 @NotNull Parameters parameters,
040 @NotNull Map<String, LambdaInfo> recapturedLambdas,
041 @NotNull FieldRemapper remapper,
042 boolean isConstructor
043 ) {
044 super(oldOwnerType, remapper, parameters);
045 this.oldOwnerType = oldOwnerType;
046 this.newOwnerType = newOwnerType;
047 this.parameters = parameters;
048 this.recapturedLambdas = recapturedLambdas;
049 this.isConstructor = isConstructor;
050 }
051
052 @Override
053 public boolean canProcess(@NotNull String fieldOwner, @NotNull String fieldName, boolean isFolding) {
054 return super.canProcess(fieldOwner, fieldName, isFolding) || isRecapturedLambdaType(fieldOwner, isFolding);
055 }
056
057 private boolean isRecapturedLambdaType(@NotNull String owner, boolean isFolding) {
058 return recapturedLambdas.containsKey(owner) && (isFolding || !(parent instanceof InlinedLambdaRemapper));
059 }
060
061 @Nullable
062 @Override
063 public CapturedParamInfo findField(@NotNull FieldInsnNode fieldInsnNode, @NotNull Collection<CapturedParamInfo> captured) {
064 boolean searchInParent = !canProcess(fieldInsnNode.owner, fieldInsnNode.name, false);
065 if (searchInParent) {
066 return parent.findField(fieldInsnNode);
067 }
068 return findFieldInMyCaptured(fieldInsnNode);
069 }
070
071 @Override
072 public boolean processNonAload0FieldAccessChains(boolean isInlinedLambda) {
073 return isInlinedLambda && isConstructor;
074 }
075
076 @Nullable
077 private CapturedParamInfo findFieldInMyCaptured(@NotNull FieldInsnNode fieldInsnNode) {
078 return super.findField(fieldInsnNode, parameters.getCaptured());
079 }
080
081 @NotNull
082 @Override
083 public String getNewLambdaInternalName() {
084 return newOwnerType;
085 }
086
087 @Nullable
088 @Override
089 public StackValue getFieldForInline(@NotNull FieldInsnNode node, @Nullable StackValue prefix) {
090 assert node.name.startsWith("$$$") : "Captured field template should start with $$$ prefix";
091 if (node.name.equals("$$$" + InlineCodegenUtil.THIS)) {
092 assert oldOwnerType.equals(node.owner) : "Can't unfold '$$$THIS' parameter";
093 return StackValue.LOCAL_0;
094 }
095
096 FieldInsnNode fin = new FieldInsnNode(node.getOpcode(), node.owner, node.name.substring(3), node.desc);
097 CapturedParamInfo field = findFieldInMyCaptured(fin);
098
099 boolean searchInParent = false;
100 if (field == null) {
101 field = findFieldInMyCaptured(new FieldInsnNode(
102 Opcodes.GETSTATIC, oldOwnerType, InlineCodegenUtil.THIS$0,
103 Type.getObjectType(parent.getLambdaInternalName()).getDescriptor()
104 ));
105 searchInParent = true;
106 if (field == null) {
107 throw new IllegalStateException("Couldn't find captured this " + getLambdaInternalName() + " for " + node.name);
108 }
109 }
110
111 StackValue result = StackValue.field(
112 field.isSkipped ?
113 Type.getObjectType(parent.parent.getNewLambdaInternalName()) : field.getType(),
114 Type.getObjectType(getNewLambdaInternalName()), /*TODO owner type*/
115 field.getNewFieldName(), false,
116 prefix == null ? StackValue.LOCAL_0 : prefix
117 );
118
119 return searchInParent ? parent.getFieldForInline(node, result) : result;
120 }
121 }