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 }