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