001 /* 002 * Copyright 2010-2016 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 com.intellij.util.ArrayUtil; 020 import kotlin.jvm.functions.Function0; 021 import org.jetbrains.annotations.NotNull; 022 import org.jetbrains.kotlin.codegen.AsmUtil; 023 import org.jetbrains.kotlin.codegen.ClassBuilder; 024 import org.jetbrains.kotlin.codegen.FieldInfo; 025 import org.jetbrains.kotlin.codegen.StackValue; 026 import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin; 027 import org.jetbrains.org.objectweb.asm.*; 028 import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter; 029 import org.jetbrains.org.objectweb.asm.tree.*; 030 031 import java.util.*; 032 033 import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.isThis0; 034 import static org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.NO_ORIGIN; 035 036 public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjectTransformationInfo> { 037 private final InliningContext inliningContext; 038 private final Type oldObjectType; 039 private final boolean isSameModule; 040 private final Map<String, List<String>> fieldNames = new HashMap<String, List<String>>(); 041 042 private MethodNode constructor; 043 private String sourceInfo; 044 private String debugInfo; 045 private SourceMapper sourceMapper; 046 047 public AnonymousObjectTransformer( 048 @NotNull AnonymousObjectTransformationInfo transformationInfo, 049 @NotNull InliningContext inliningContext, 050 boolean isSameModule 051 ) { 052 super(transformationInfo, inliningContext.state); 053 this.isSameModule = isSameModule; 054 this.inliningContext = inliningContext; 055 this.oldObjectType = Type.getObjectType(transformationInfo.getOldClassName()); 056 } 057 058 @Override 059 @NotNull 060 public InlineResult doTransform(@NotNull FieldRemapper parentRemapper) { 061 final List<InnerClassNode> innerClassNodes = new ArrayList<InnerClassNode>(); 062 final ClassBuilder classBuilder = createRemappingClassBuilderViaFactory(inliningContext); 063 final List<MethodNode> methodsToTransform = new ArrayList<MethodNode>(); 064 065 createClassReader().accept(new ClassVisitor(InlineCodegenUtil.API, classBuilder.getVisitor()) { 066 @Override 067 public void visit(int version, int access, @NotNull String name, String signature, String superName, String[] interfaces) { 068 InlineCodegenUtil.assertVersionNotGreaterThanJava6(version, name); 069 classBuilder.defineClass(null, version, access, name, signature, superName, interfaces); 070 } 071 072 @Override 073 public void visitInnerClass(@NotNull String name, String outerName, String innerName, int access) { 074 innerClassNodes.add(new InnerClassNode(name, outerName, innerName, access)); 075 } 076 077 @Override 078 public MethodVisitor visitMethod( 079 int access, @NotNull String name, @NotNull String desc, String signature, String[] exceptions 080 ) { 081 MethodNode node = new MethodNode(access, name, desc, signature, exceptions); 082 if (name.equals("<init>")) { 083 if (constructor != null) { 084 throw new RuntimeException("Lambda, SAM or anonymous object should have only one constructor"); 085 } 086 constructor = node; 087 } 088 else { 089 methodsToTransform.add(node); 090 } 091 return node; 092 } 093 094 @Override 095 public FieldVisitor visitField(int access, @NotNull String name, @NotNull String desc, String signature, Object value) { 096 addUniqueField(name); 097 if (InlineCodegenUtil.isCapturedFieldName(name)) { 098 return null; 099 } 100 else { 101 return classBuilder.newField(JvmDeclarationOrigin.NO_ORIGIN, access, name, desc, signature, value); 102 } 103 } 104 105 @Override 106 public void visitSource(String source, String debug) { 107 sourceInfo = source; 108 debugInfo = debug; 109 } 110 111 @Override 112 public void visitEnd() { 113 } 114 }, ClassReader.SKIP_FRAMES); 115 116 if (!inliningContext.isInliningLambda) { 117 if (debugInfo != null && !debugInfo.isEmpty()) { 118 sourceMapper = SourceMapper.Companion.createFromSmap(SMAPParser.parse(debugInfo)); 119 } 120 else { 121 //seems we can't do any clever mapping cause we don't know any about original class name 122 sourceMapper = IdenticalSourceMapper.INSTANCE; 123 } 124 if (sourceInfo != null && !InlineCodegenUtil.GENERATE_SMAP) { 125 classBuilder.visitSource(sourceInfo, debugInfo); 126 } 127 } 128 else { 129 if (sourceInfo != null) { 130 classBuilder.visitSource(sourceInfo, debugInfo); 131 } 132 sourceMapper = IdenticalSourceMapper.INSTANCE; 133 } 134 135 ParametersBuilder allCapturedParamBuilder = ParametersBuilder.newBuilder(); 136 ParametersBuilder constructorParamBuilder = ParametersBuilder.newBuilder(); 137 List<CapturedParamInfo> additionalFakeParams = 138 extractParametersMappingAndPatchConstructor(constructor, allCapturedParamBuilder, constructorParamBuilder, 139 transformationInfo, parentRemapper); 140 List<MethodVisitor> deferringMethods = new ArrayList<MethodVisitor>(); 141 142 for (MethodNode next : methodsToTransform) { 143 MethodVisitor deferringVisitor = newMethod(classBuilder, next); 144 InlineResult funResult = 145 inlineMethodAndUpdateGlobalResult(parentRemapper, deferringVisitor, next, allCapturedParamBuilder, false); 146 147 Type returnType = Type.getReturnType(next.desc); 148 if (!AsmUtil.isPrimitive(returnType)) { 149 String oldFunReturnType = returnType.getInternalName(); 150 String newFunReturnType = funResult.getChangedTypes().get(oldFunReturnType); 151 if (newFunReturnType != null) { 152 inliningContext.typeRemapper.addAdditionalMappings(oldFunReturnType, newFunReturnType); 153 } 154 } 155 deferringMethods.add(deferringVisitor); 156 } 157 158 for (MethodVisitor method : deferringMethods) { 159 method.visitEnd(); 160 } 161 162 generateConstructorAndFields(classBuilder, allCapturedParamBuilder, constructorParamBuilder, parentRemapper, additionalFakeParams); 163 164 SourceMapper.Companion.flushToClassBuilder(sourceMapper, classBuilder); 165 166 ClassVisitor visitor = classBuilder.getVisitor(); 167 for (InnerClassNode node : innerClassNodes) { 168 visitor.visitInnerClass(node.name, node.outerName, node.innerName, node.access); 169 } 170 171 writeOuterInfo(visitor); 172 173 classBuilder.done(); 174 175 return transformationResult; 176 } 177 178 private void writeOuterInfo(@NotNull ClassVisitor visitor) { 179 InlineCallSiteInfo info = inliningContext.getCallSiteInfo(); 180 visitor.visitOuterClass(info.getOwnerClassName(), info.getFunctionName(), info.getFunctionDesc()); 181 } 182 183 @NotNull 184 private InlineResult inlineMethodAndUpdateGlobalResult( 185 @NotNull FieldRemapper parentRemapper, 186 @NotNull MethodVisitor deferringVisitor, 187 @NotNull MethodNode next, 188 @NotNull ParametersBuilder allCapturedParamBuilder, 189 boolean isConstructor 190 ) { 191 InlineResult funResult = inlineMethod(parentRemapper, deferringVisitor, next, allCapturedParamBuilder, isConstructor); 192 transformationResult.addAllClassesToRemove(funResult); 193 transformationResult.getReifiedTypeParametersUsages().mergeAll(funResult.getReifiedTypeParametersUsages()); 194 return funResult; 195 } 196 197 @NotNull 198 private InlineResult inlineMethod( 199 @NotNull FieldRemapper parentRemapper, 200 @NotNull MethodVisitor deferringVisitor, 201 @NotNull MethodNode sourceNode, 202 @NotNull ParametersBuilder capturedBuilder, 203 boolean isConstructor 204 ) { 205 ReifiedTypeParametersUsages typeParametersToReify = inliningContext.reifiedTypeInliner.reifyInstructions(sourceNode); 206 Parameters parameters = 207 isConstructor ? capturedBuilder.buildParameters() : getMethodParametersWithCaptured(capturedBuilder, sourceNode); 208 209 RegeneratedLambdaFieldRemapper remapper = new RegeneratedLambdaFieldRemapper( 210 oldObjectType.getInternalName(), transformationInfo.getNewClassName(), parameters, 211 transformationInfo.getCapturedLambdasToInline(), parentRemapper, isConstructor 212 ); 213 214 MethodInliner inliner = new MethodInliner( 215 sourceNode, 216 parameters, 217 inliningContext.subInline(inliningContext.nameGenerator.subGenerator("lambda")), 218 remapper, 219 isSameModule, 220 "Transformer for " + transformationInfo.getOldClassName(), 221 sourceMapper, 222 new InlineCallSiteInfo( 223 transformationInfo.getOldClassName(), 224 sourceNode.name, 225 isConstructor ? transformationInfo.getNewConstructorDescriptor() : sourceNode.desc 226 ), 227 null 228 ); 229 230 InlineResult result = inliner.doInline(deferringVisitor, new LocalVarRemapper(parameters, 0), false, LabelOwner.NOT_APPLICABLE); 231 result.getReifiedTypeParametersUsages().mergeAll(typeParametersToReify); 232 deferringVisitor.visitMaxs(-1, -1); 233 return result; 234 } 235 236 private void generateConstructorAndFields( 237 @NotNull ClassBuilder classBuilder, 238 @NotNull ParametersBuilder allCapturedBuilder, 239 @NotNull ParametersBuilder constructorInlineBuilder, 240 @NotNull FieldRemapper parentRemapper, 241 @NotNull List<CapturedParamInfo> constructorAdditionalFakeParams 242 ) { 243 List<Type> descTypes = new ArrayList<Type>(); 244 245 Parameters constructorParams = constructorInlineBuilder.buildParameters(); 246 int[] capturedIndexes = new int[constructorParams.getReal().size() + constructorParams.getCaptured().size()]; 247 int index = 0; 248 int size = 0; 249 250 //complex processing cause it could have super constructor call params 251 for (ParameterInfo info : constructorParams) { 252 if (!info.isSkipped) { //not inlined 253 if (info.isCaptured() || info instanceof CapturedParamInfo) { 254 capturedIndexes[index] = size; 255 index++; 256 } 257 258 if (size != 0) { //skip this 259 descTypes.add(info.getType()); 260 } 261 size += info.getType().getSize(); 262 } 263 } 264 265 String constructorDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE, descTypes.toArray(new Type[descTypes.size()])); 266 //TODO for inline method make public class 267 transformationInfo.setNewConstructorDescriptor(constructorDescriptor); 268 MethodVisitor constructorVisitor = classBuilder.newMethod( 269 NO_ORIGIN, AsmUtil.NO_FLAG_PACKAGE_PRIVATE, "<init>", constructorDescriptor, null, ArrayUtil.EMPTY_STRING_ARRAY 270 ); 271 272 final Label newBodyStartLabel = new Label(); 273 constructorVisitor.visitLabel(newBodyStartLabel); 274 //initialize captured fields 275 List<NewJavaField> newFieldsWithSkipped = TransformationUtilsKt.getNewFieldsToGenerate(allCapturedBuilder.listCaptured()); 276 List<FieldInfo> fieldInfoWithSkipped = 277 TransformationUtilsKt.transformToFieldInfo(Type.getObjectType(transformationInfo.getNewClassName()), newFieldsWithSkipped); 278 279 int paramIndex = 0; 280 InstructionAdapter capturedFieldInitializer = new InstructionAdapter(constructorVisitor); 281 for (int i = 0; i < fieldInfoWithSkipped.size(); i++) { 282 FieldInfo fieldInfo = fieldInfoWithSkipped.get(i); 283 if (!newFieldsWithSkipped.get(i).getSkip()) { 284 AsmUtil.genAssignInstanceFieldFromParam(fieldInfo, capturedIndexes[paramIndex], capturedFieldInitializer); 285 } 286 paramIndex++; 287 } 288 289 //then transform constructor 290 //HACK: in inlinining into constructor we access original captured fields with field access not local var 291 //but this fields added to general params (this assumes local var access) not captured one, 292 //so we need to add them to captured params 293 for (CapturedParamInfo info : constructorAdditionalFakeParams) { 294 CapturedParamInfo fake = constructorInlineBuilder.addCapturedParamCopy(info); 295 296 if (fake.getLambda() != null) { 297 //set remap value to skip this fake (captured with lambda already skipped) 298 StackValue composed = StackValue.field( 299 fake.getType(), 300 oldObjectType, 301 fake.getNewFieldName(), 302 false, 303 StackValue.LOCAL_0 304 ); 305 fake.setRemapValue(composed); 306 } 307 } 308 309 MethodNode intermediateMethodNode = 310 new MethodNode(AsmUtil.NO_FLAG_PACKAGE_PRIVATE, "<init>", constructorDescriptor, null, ArrayUtil.EMPTY_STRING_ARRAY); 311 inlineMethodAndUpdateGlobalResult(parentRemapper, intermediateMethodNode, constructor, constructorInlineBuilder, true); 312 313 AbstractInsnNode first = intermediateMethodNode.instructions.getFirst(); 314 final Label oldStartLabel = first instanceof LabelNode ? ((LabelNode) first).getLabel() : null; 315 intermediateMethodNode.accept(new MethodBodyVisitor(capturedFieldInitializer) { 316 @Override 317 public void visitLocalVariable( 318 @NotNull String name, @NotNull String desc, String signature, @NotNull Label start, @NotNull Label end, int index 319 ) { 320 if (oldStartLabel == start) { 321 start = newBodyStartLabel;//patch for jack&jill 322 } 323 super.visitLocalVariable(name, desc, signature, start, end, index); 324 } 325 }); 326 constructorVisitor.visitEnd(); 327 AsmUtil.genClosureFields( 328 TransformationUtilsKt.toNameTypePair(TransformationUtilsKt.filterSkipped(newFieldsWithSkipped)), classBuilder 329 ); 330 } 331 332 @NotNull 333 private Parameters getMethodParametersWithCaptured(@NotNull ParametersBuilder capturedBuilder, @NotNull MethodNode sourceNode) { 334 ParametersBuilder builder = ParametersBuilder.initializeBuilderFrom(oldObjectType, sourceNode.desc); 335 for (CapturedParamInfo param : capturedBuilder.listCaptured()) { 336 builder.addCapturedParamCopy(param); 337 } 338 return builder.buildParameters(); 339 } 340 341 @NotNull 342 private static DeferredMethodVisitor newMethod(@NotNull final ClassBuilder builder, @NotNull final MethodNode original) { 343 return new DeferredMethodVisitor( 344 new MethodNode( 345 original.access, original.name, original.desc, original.signature, 346 ArrayUtil.toStringArray(original.exceptions) 347 ), 348 new Function0<MethodVisitor>() { 349 @Override 350 public MethodVisitor invoke() { 351 return builder.newMethod( 352 NO_ORIGIN, original.access, original.name, original.desc, original.signature, 353 ArrayUtil.toStringArray(original.exceptions) 354 ); 355 } 356 } 357 ); 358 } 359 360 @NotNull 361 private List<CapturedParamInfo> extractParametersMappingAndPatchConstructor( 362 @NotNull MethodNode constructor, 363 @NotNull ParametersBuilder capturedParamBuilder, 364 @NotNull ParametersBuilder constructorParamBuilder, 365 @NotNull final AnonymousObjectTransformationInfo transformationInfo, 366 @NotNull FieldRemapper parentFieldRemapper 367 ) { 368 Set<LambdaInfo> capturedLambdas = new LinkedHashSet<LambdaInfo>(); //captured var of inlined parameter 369 List<CapturedParamInfo> constructorAdditionalFakeParams = new ArrayList<CapturedParamInfo>(); 370 Map<Integer, LambdaInfo> indexToLambda = transformationInfo.getLambdasToInline(); 371 Set<Integer> capturedParams = new HashSet<Integer>(); 372 373 //load captured parameters and patch instruction list (NB: there is also could be object fields) 374 AbstractInsnNode cur = constructor.instructions.getFirst(); 375 while (cur != null) { 376 if (cur instanceof FieldInsnNode) { 377 FieldInsnNode fieldNode = (FieldInsnNode) cur; 378 String fieldName = fieldNode.name; 379 if (fieldNode.getOpcode() == Opcodes.PUTFIELD && InlineCodegenUtil.isCapturedFieldName(fieldName)) { 380 boolean isPrevVarNode = fieldNode.getPrevious() instanceof VarInsnNode; 381 boolean isPrevPrevVarNode = isPrevVarNode && fieldNode.getPrevious().getPrevious() instanceof VarInsnNode; 382 383 if (isPrevPrevVarNode) { 384 VarInsnNode node = (VarInsnNode) fieldNode.getPrevious().getPrevious(); 385 if (node.var == 0) { 386 VarInsnNode previous = (VarInsnNode) fieldNode.getPrevious(); 387 int varIndex = previous.var; 388 LambdaInfo lambdaInfo = indexToLambda.get(varIndex); 389 String newFieldName = 390 isThis0(fieldName) && shouldRenameThis0(parentFieldRemapper, indexToLambda.values()) 391 ? getNewFieldName(fieldName, true) 392 : fieldName; 393 CapturedParamInfo info = capturedParamBuilder.addCapturedParam( 394 Type.getObjectType(transformationInfo.getOldClassName()), fieldName, newFieldName, 395 Type.getType(fieldNode.desc), lambdaInfo != null, null 396 ); 397 if (lambdaInfo != null) { 398 info.setLambda(lambdaInfo); 399 capturedLambdas.add(lambdaInfo); 400 } 401 constructorAdditionalFakeParams.add(info); 402 capturedParams.add(varIndex); 403 404 constructor.instructions.remove(previous.getPrevious()); 405 constructor.instructions.remove(previous); 406 AbstractInsnNode temp = cur; 407 cur = cur.getNext(); 408 constructor.instructions.remove(temp); 409 continue; 410 } 411 } 412 } 413 } 414 cur = cur.getNext(); 415 } 416 417 constructorParamBuilder.addThis(oldObjectType, false); 418 String constructorDesc = transformationInfo.getConstructorDesc(); 419 420 if (constructorDesc == null) { 421 // in case of anonymous object with empty closure 422 constructorDesc = Type.getMethodDescriptor(Type.VOID_TYPE); 423 } 424 425 Type[] types = Type.getArgumentTypes(constructorDesc); 426 for (Type type : types) { 427 LambdaInfo info = indexToLambda.get(constructorParamBuilder.getNextValueParameterIndex()); 428 ParameterInfo parameterInfo = constructorParamBuilder.addNextParameter(type, info != null); 429 parameterInfo.setLambda(info); 430 if (capturedParams.contains(parameterInfo.getIndex())) { 431 parameterInfo.setCaptured(true); 432 } 433 else { 434 //otherwise it's super constructor parameter 435 } 436 } 437 438 //For all inlined lambdas add their captured parameters 439 //TODO: some of such parameters could be skipped - we should perform additional analysis 440 Map<String, LambdaInfo> capturedLambdasToInline = new HashMap<String, LambdaInfo>(); //captured var of inlined parameter 441 List<CapturedParamDesc> allRecapturedParameters = new ArrayList<CapturedParamDesc>(); 442 boolean addCapturedNotAddOuter = 443 parentFieldRemapper.isRoot() || 444 (parentFieldRemapper instanceof InlinedLambdaRemapper && parentFieldRemapper.getParent().isRoot()); 445 Map<String, CapturedParamInfo> alreadyAdded = new HashMap<String, CapturedParamInfo>(); 446 for (LambdaInfo info : capturedLambdas) { 447 if (addCapturedNotAddOuter) { 448 for (CapturedParamDesc desc : info.getCapturedVars()) { 449 String key = desc.getFieldName() + "$$$" + desc.getType().getClassName(); 450 CapturedParamInfo alreadyAddedParam = alreadyAdded.get(key); 451 452 CapturedParamInfo recapturedParamInfo = capturedParamBuilder.addCapturedParam( 453 desc, 454 alreadyAddedParam != null ? alreadyAddedParam.getNewFieldName() : getNewFieldName(desc.getFieldName(), false), 455 alreadyAddedParam != null 456 ); 457 StackValue composed = StackValue.field( 458 desc.getType(), 459 oldObjectType, /*TODO owner type*/ 460 recapturedParamInfo.getNewFieldName(), 461 false, 462 StackValue.LOCAL_0 463 ); 464 recapturedParamInfo.setRemapValue(composed); 465 allRecapturedParameters.add(desc); 466 467 constructorParamBuilder.addCapturedParam(recapturedParamInfo, recapturedParamInfo.getNewFieldName()) 468 .setRemapValue(composed); 469 470 if (isThis0(desc.getFieldName())) { 471 alreadyAdded.put(key, recapturedParamInfo); 472 } 473 } 474 } 475 capturedLambdasToInline.put(info.getLambdaClassType().getInternalName(), info); 476 } 477 478 if (parentFieldRemapper instanceof InlinedLambdaRemapper && !capturedLambdas.isEmpty() && !addCapturedNotAddOuter) { 479 //lambda with non InlinedLambdaRemapper already have outer 480 FieldRemapper parent = parentFieldRemapper.getParent(); 481 assert parent instanceof RegeneratedLambdaFieldRemapper; 482 Type ownerType = Type.getObjectType(parent.getLambdaInternalName()); 483 CapturedParamDesc desc = new CapturedParamDesc(ownerType, InlineCodegenUtil.THIS, ownerType); 484 CapturedParamInfo recapturedParamInfo = 485 capturedParamBuilder.addCapturedParam(desc, InlineCodegenUtil.THIS$0/*outer lambda/object*/, false); 486 StackValue composed = StackValue.LOCAL_0; 487 recapturedParamInfo.setRemapValue(composed); 488 allRecapturedParameters.add(desc); 489 490 constructorParamBuilder.addCapturedParam(recapturedParamInfo, recapturedParamInfo.getNewFieldName()).setRemapValue(composed); 491 } 492 493 transformationInfo.setAllRecapturedParameters(allRecapturedParameters); 494 transformationInfo.setCapturedLambdasToInline(capturedLambdasToInline); 495 496 return constructorAdditionalFakeParams; 497 } 498 499 private static boolean shouldRenameThis0(@NotNull FieldRemapper parentFieldRemapper, @NotNull Collection<LambdaInfo> values) { 500 if (isFirstDeclSiteLambdaFieldRemapper(parentFieldRemapper)) { 501 for (LambdaInfo value : values) { 502 for (CapturedParamDesc desc : value.getCapturedVars()) { 503 if (isThis0(desc.getFieldName())) { 504 return true; 505 } 506 } 507 } 508 } 509 return false; 510 } 511 512 @NotNull 513 private String getNewFieldName(@NotNull String oldName, boolean originalField) { 514 if (InlineCodegenUtil.THIS$0.equals(oldName)) { 515 if (!originalField) { 516 return oldName; 517 } 518 else { 519 //rename original 'this$0' in declaration site lambda (inside inline function) to use this$0 only for outer lambda/object access on call site 520 return addUniqueField(oldName + InlineCodegenUtil.INLINE_FUN_THIS_0_SUFFIX); 521 } 522 } 523 return addUniqueField(oldName + InlineCodegenUtil.INLINE_TRANSFORMATION_SUFFIX); 524 } 525 526 @NotNull 527 private String addUniqueField(@NotNull String name) { 528 List<String> existNames = fieldNames.get(name); 529 if (existNames == null) { 530 existNames = new LinkedList<String>(); 531 fieldNames.put(name, existNames); 532 } 533 String suffix = existNames.isEmpty() ? "" : "$" + existNames.size(); 534 String newName = name + suffix; 535 existNames.add(newName); 536 return newName; 537 } 538 539 private static boolean isFirstDeclSiteLambdaFieldRemapper(@NotNull FieldRemapper parentRemapper) { 540 return !(parentRemapper instanceof RegeneratedLambdaFieldRemapper) && !(parentRemapper instanceof InlinedLambdaRemapper); 541 } 542 }