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