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