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.List; 028 import java.util.Map; 029 030 public class RegeneratedLambdaFieldRemapper extends FieldRemapper { 031 032 private final String oldOwnerType; 033 034 private final String newOwnerType; 035 036 private final Parameters parameters; 037 038 private final Map<String, LambdaInfo> recapturedLambdas; 039 040 public RegeneratedLambdaFieldRemapper( 041 String oldOwnerType, 042 String newOwnerType, 043 Parameters parameters, 044 Map<String, LambdaInfo> recapturedLambdas, 045 FieldRemapper remapper 046 ) { 047 super(oldOwnerType, remapper, parameters); 048 this.oldOwnerType = oldOwnerType; 049 this.newOwnerType = newOwnerType; 050 this.parameters = parameters; 051 this.recapturedLambdas = recapturedLambdas; 052 } 053 054 @Override 055 public void addCapturedFields(LambdaInfo lambdaInfo, ParametersBuilder builder) { 056 if (canProcess(lambdaInfo.getLambdaClassType().getInternalName())) { 057 List<CapturedParamInfo> captured = parameters.getCaptured(); 058 for (CapturedParamInfo originalField : lambdaInfo.getCapturedVars()) { 059 CapturedParamInfo foundField = null; 060 for (CapturedParamInfo capturedParamInfo : captured) { 061 if (capturedParamInfo.getContainingLambdaName().equals(originalField.getContainingLambdaName())) { 062 if (capturedParamInfo.getFieldName().equals(LambdaTransformer.getNewFieldName(originalField.getFieldName()))) { 063 foundField = originalField; 064 break; 065 } 066 } 067 } 068 069 if (foundField == null) { 070 throw new IllegalStateException("Captured parameter should exists in outer context: " + originalField.getFieldName()); 071 } 072 073 builder.addCapturedParam(foundField, foundField); 074 } 075 } else { 076 //in case when inlining lambda into another one inside inline function 077 parent.addCapturedFields(lambdaInfo, builder); 078 } 079 } 080 081 @Override 082 public boolean canProcess(@NotNull String fieldOwner) { 083 return super.canProcess(fieldOwner) || isRecapturedLambdaType(fieldOwner); 084 } 085 086 private boolean isRecapturedLambdaType(String owner) { 087 return recapturedLambdas.containsKey(owner); 088 } 089 090 @Nullable 091 @Override 092 public CapturedParamInfo findField(@NotNull FieldInsnNode fieldInsnNode, @NotNull Collection<CapturedParamInfo> captured) { 093 boolean searchInParent = !canProcess(fieldInsnNode.owner); 094 if (searchInParent) { 095 return parent.findField(fieldInsnNode); 096 } else if (isRecapturedLambdaType(fieldInsnNode.owner)) { 097 LambdaInfo info = recapturedLambdas.get(fieldInsnNode.owner); 098 return super.findField(fieldInsnNode, info.getCapturedVars()); 099 } 100 else { 101 return super.findField(fieldInsnNode, captured); 102 } 103 } 104 105 @Nullable 106 public CapturedParamInfo findFieldInMyCaptured(@NotNull FieldInsnNode fieldInsnNode) { 107 if (isRecapturedLambdaType(fieldInsnNode.owner)) { 108 LambdaInfo info = recapturedLambdas.get(fieldInsnNode.owner); 109 return super.findField(fieldInsnNode, info.getCapturedVars()); 110 } 111 else { 112 return super.findField(fieldInsnNode, parameters.getCaptured()); 113 } 114 } 115 116 @Nullable 117 @Override 118 public StackValue getFieldForInline(@NotNull FieldInsnNode node, @Nullable StackValue prefix) { 119 assert node.name.startsWith("$$$") : "Captured field template should start with $$$ prefix"; 120 FieldInsnNode fin = new FieldInsnNode(node.getOpcode(), node.owner, node.name.substring(3), node.desc); 121 CapturedParamInfo field = findFieldInMyCaptured(fin); 122 123 boolean searchInParent = false; 124 if (field == null) { 125 field = findFieldInMyCaptured(new FieldInsnNode(Opcodes.GETSTATIC, oldOwnerType, "this$0", Type.getObjectType(parent.getLambdaInternalName()).getDescriptor())); 126 searchInParent = true; 127 if (field == null) { 128 throw new IllegalStateException("Could find captured this " + getLambdaInternalName()); 129 } 130 } 131 132 String newName = field.getContainingLambdaName().equals(getLambdaInternalName()) 133 ? field.getFieldName() 134 : LambdaTransformer.getNewFieldName(field.getFieldName()); 135 StackValue result = 136 StackValue.composed(prefix == null ? StackValue.local(0, Type.getObjectType(getLambdaInternalName())) : prefix, 137 StackValue.field(field.getType(), 138 Type.getObjectType(newOwnerType), /*TODO owner type*/ 139 newName, false) 140 ); 141 142 return searchInParent ? parent.getFieldForInline(node, result) : result; 143 } 144 }