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