001    /*
002    /*
003     * Copyright 2010-2013 JetBrains s.r.o.
004     *
005     * Licensed under the Apache License, Version 2.0 (the "License");
006     * you may not use this file except in compliance with the License.
007     * You may obtain a copy of the License at
008     *
009     * http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.jetbrains.jet.codegen;
019    
020    import com.intellij.openapi.progress.ProcessCanceledException;
021    import com.intellij.psi.PsiElement;
022    import com.intellij.util.ArrayUtil;
023    import com.intellij.util.Function;
024    import com.intellij.util.containers.ContainerUtil;
025    import kotlin.Function1;
026    import org.jetbrains.annotations.NotNull;
027    import org.jetbrains.annotations.Nullable;
028    import org.jetbrains.jet.codegen.binding.CodegenBinding;
029    import org.jetbrains.jet.codegen.bridges.Bridge;
030    import org.jetbrains.jet.codegen.bridges.BridgesPackage;
031    import org.jetbrains.jet.codegen.context.CodegenContext;
032    import org.jetbrains.jet.codegen.context.MethodContext;
033    import org.jetbrains.jet.codegen.context.PackageFacadeContext;
034    import org.jetbrains.jet.codegen.optimization.OptimizationMethodVisitor;
035    import org.jetbrains.jet.codegen.state.GenerationState;
036    import org.jetbrains.jet.codegen.state.JetTypeMapper;
037    import org.jetbrains.jet.lang.descriptors.*;
038    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
039    import org.jetbrains.jet.lang.psi.JetNamedFunction;
040    import org.jetbrains.jet.lang.resolve.BindingContext;
041    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
042    import org.jetbrains.jet.lang.resolve.calls.CallResolverUtil;
043    import org.jetbrains.jet.lang.resolve.constants.ArrayValue;
044    import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
045    import org.jetbrains.jet.lang.resolve.constants.JavaClassValue;
046    import org.jetbrains.jet.lang.resolve.java.diagnostics.JvmDeclarationOrigin;
047    import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodParameterKind;
048    import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodParameterSignature;
049    import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodSignature;
050    import org.jetbrains.jet.lang.resolve.name.FqName;
051    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
052    import org.jetbrains.org.objectweb.asm.AnnotationVisitor;
053    import org.jetbrains.org.objectweb.asm.Label;
054    import org.jetbrains.org.objectweb.asm.MethodVisitor;
055    import org.jetbrains.org.objectweb.asm.Type;
056    import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
057    import org.jetbrains.org.objectweb.asm.commons.Method;
058    import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor;
059    
060    import java.io.PrintWriter;
061    import java.io.StringWriter;
062    import java.util.Collection;
063    import java.util.Iterator;
064    import java.util.List;
065    import java.util.Set;
066    
067    import static org.jetbrains.jet.codegen.AsmUtil.*;
068    import static org.jetbrains.jet.codegen.JvmSerializationBindings.*;
069    import static org.jetbrains.jet.codegen.binding.CodegenBinding.isLocalNamedFun;
070    import static org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor.Kind.DECLARATION;
071    import static org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils.callableDescriptorToDeclaration;
072    import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isFunctionLiteral;
073    import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isTrait;
074    import static org.jetbrains.jet.lang.resolve.java.AsmTypeConstants.OBJECT_TYPE;
075    import static org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.OLD_JET_VALUE_PARAMETER_ANNOTATION;
076    import static org.jetbrains.jet.lang.resolve.java.diagnostics.DiagnosticsPackage.OtherOrigin;
077    import static org.jetbrains.org.objectweb.asm.Opcodes.*;
078    
079    public class FunctionCodegen extends ParentCodegenAware {
080        private final CodegenContext owner;
081    
082        private final ClassBuilder v;
083    
084        public FunctionCodegen(
085                @NotNull CodegenContext owner,
086                @NotNull ClassBuilder v,
087                @NotNull GenerationState state,
088                MemberCodegen<?> parentCodegen
089        ) {
090            super(state, parentCodegen);
091            this.owner = owner;
092            this.v = v;
093        }
094    
095        public void gen(@NotNull JetNamedFunction function) {
096            SimpleFunctionDescriptor functionDescriptor = bindingContext.get(BindingContext.FUNCTION, function);
097            assert functionDescriptor != null : "No descriptor for function " + function.getText() + "\n" +
098                                                "in " + function.getContainingFile().getVirtualFile();
099    
100            OwnerKind kind = owner.getContextKind();
101            JvmMethodSignature method = typeMapper.mapSignature(functionDescriptor, kind);
102    
103            if (kind != OwnerKind.TRAIT_IMPL || function.hasBody()) {
104                generateMethod(OtherOrigin(function, functionDescriptor),
105                               method, functionDescriptor,
106                               new FunctionGenerationStrategy.FunctionDefault(state, functionDescriptor, function));
107            }
108    
109            generateDefaultIfNeeded(owner.intoFunction(functionDescriptor), method, functionDescriptor, kind,
110                                    DefaultParameterValueLoader.DEFAULT, function);
111        }
112    
113        public void generateMethod(
114                @NotNull JvmDeclarationOrigin origin,
115                @NotNull JvmMethodSignature jvmSignature,
116                @NotNull FunctionDescriptor functionDescriptor,
117                @NotNull FunctionGenerationStrategy strategy
118        ) {
119            generateMethod(origin, jvmSignature, functionDescriptor, owner.intoFunction(functionDescriptor), strategy);
120        }
121    
122        public void generateMethod(
123                @NotNull JvmDeclarationOrigin origin,
124                @NotNull JvmMethodSignature jvmSignature,
125                @NotNull FunctionDescriptor functionDescriptor,
126                @NotNull MethodContext methodContext,
127                @NotNull FunctionGenerationStrategy strategy
128        ) {
129            OwnerKind methodContextKind = methodContext.getContextKind();
130            Method asmMethod = jvmSignature.getAsmMethod();
131    
132            MethodVisitor mv = v.newMethod(origin,
133                                           getMethodAsmFlags(functionDescriptor, methodContextKind),
134                                           asmMethod.getName(),
135                                           asmMethod.getDescriptor(),
136                                           jvmSignature.getGenericsSignature(),
137                                           getThrownExceptions(functionDescriptor, typeMapper));
138    
139            if (owner instanceof PackageFacadeContext) {
140                Type ownerType = ((PackageFacadeContext) owner).getDelegateToClassType();
141                v.getSerializationBindings().put(IMPL_CLASS_NAME_FOR_CALLABLE, functionDescriptor, shortNameByAsmType(ownerType));
142            }
143            else {
144                v.getSerializationBindings().put(METHOD_FOR_FUNCTION, functionDescriptor, asmMethod);
145            }
146    
147            AnnotationCodegen.forMethod(mv, typeMapper).genAnnotations(functionDescriptor, asmMethod.getReturnType());
148    
149            generateParameterAnnotations(functionDescriptor, mv, jvmSignature);
150    
151            generateJetValueParameterAnnotations(mv, functionDescriptor, jvmSignature);
152    
153            generateBridges(functionDescriptor);
154    
155            if (state.getClassBuilderMode() == ClassBuilderMode.LIGHT_CLASSES || isAbstractMethod(functionDescriptor, methodContextKind)) {
156                generateLocalVariableTable(
157                        mv,
158                        jvmSignature,
159                        functionDescriptor,
160                        getThisTypeForFunction(functionDescriptor, methodContext, typeMapper),
161                        new Label(),
162                        new Label(),
163                        methodContextKind
164                );
165    
166                mv.visitEnd();
167                return;
168            }
169    
170            generateMethodBody(mv, functionDescriptor, methodContext, jvmSignature, strategy, getParentCodegen());
171    
172            endVisit(mv, null, origin.getElement());
173    
174            methodContext.recordSyntheticAccessorIfNeeded(functionDescriptor, bindingContext);
175        }
176    
177        private void generateParameterAnnotations(
178                @NotNull FunctionDescriptor functionDescriptor,
179                @NotNull MethodVisitor mv,
180                @NotNull JvmMethodSignature jvmSignature
181        ) {
182            Iterator<ValueParameterDescriptor> iterator = functionDescriptor.getValueParameters().iterator();
183            List<JvmMethodParameterSignature> kotlinParameterTypes = jvmSignature.getValueParameters();
184    
185            for (int i = 0; i < kotlinParameterTypes.size(); i++) {
186                JvmMethodParameterSignature parameterSignature = kotlinParameterTypes.get(i);
187                JvmMethodParameterKind kind = parameterSignature.getKind();
188                if (kind.isSkippedInGenericSignature()) {
189                    markEnumOrInnerConstructorParameterAsSynthetic(mv, i);
190                    continue;
191                }
192    
193                if (kind == JvmMethodParameterKind.VALUE) {
194                    ValueParameterDescriptor parameter = iterator.next();
195                    v.getSerializationBindings().put(INDEX_FOR_VALUE_PARAMETER, parameter, i);
196                    AnnotationCodegen.forParameter(i, mv, typeMapper).genAnnotations(parameter, parameterSignature.getAsmType());
197                }
198            }
199        }
200    
201        @SuppressWarnings("deprecation")
202        private static void generateJetValueParameterAnnotations(
203                @NotNull MethodVisitor mv,
204                @NotNull FunctionDescriptor functionDescriptor,
205                @NotNull JvmMethodSignature jvmSignature
206        ) {
207            Iterator<ValueParameterDescriptor> descriptors = functionDescriptor.getValueParameters().iterator();
208            List<JvmMethodParameterSignature> kotlinParameterTypes = jvmSignature.getValueParameters();
209    
210            for (int i = 0; i < kotlinParameterTypes.size(); i++) {
211                JvmMethodParameterKind kind = kotlinParameterTypes.get(i).getKind();
212                if (kind.isSkippedInGenericSignature()) continue;
213    
214                String name;
215                boolean nullableType;
216                if (kind == JvmMethodParameterKind.VALUE) {
217                    ValueParameterDescriptor descriptor = descriptors.next();
218                    name = descriptor.getName().asString();
219                    nullableType = descriptor.getType().isNullable();
220                }
221                else {
222                    String lowercaseKind = kind.name().toLowerCase();
223                    if (needIndexForVar(kind)) {
224                        name = "$" + lowercaseKind + "$" + i;
225                    }
226                    else {
227                        name = "$" + lowercaseKind;
228                    }
229    
230                    if (kind == JvmMethodParameterKind.RECEIVER) {
231                        ReceiverParameterDescriptor receiver = functionDescriptor.getReceiverParameter();
232                        nullableType = receiver == null || receiver.getType().isNullable();
233                    }
234                    else {
235                        nullableType = true;
236                    }
237                }
238    
239                AnnotationVisitor av =
240                        mv.visitParameterAnnotation(i, asmDescByFqNameWithoutInnerClasses(OLD_JET_VALUE_PARAMETER_ANNOTATION), true);
241                if (av != null) {
242                    av.visit("name", name);
243                    if (nullableType) {
244                        av.visit("type", "?");
245                    }
246                    av.visitEnd();
247                }
248            }
249        }
250    
251        private void markEnumOrInnerConstructorParameterAsSynthetic(MethodVisitor mv, int i) {
252            // IDEA's ClsPsi builder fails to annotate synthetic parameters
253            if (state.getClassBuilderMode() == ClassBuilderMode.LIGHT_CLASSES) return;
254    
255            // This is needed to avoid RuntimeInvisibleParameterAnnotations error in javac:
256            // see MethodWriter.visitParameterAnnotation()
257    
258            AnnotationVisitor av = mv.visitParameterAnnotation(i, "Ljava/lang/Synthetic;", true);
259            if (av != null) {
260                av.visitEnd();
261            }
262        }
263    
264        @Nullable
265        private static Type getThisTypeForFunction(@NotNull FunctionDescriptor functionDescriptor, @NotNull MethodContext context, @NotNull JetTypeMapper typeMapper) {
266            ReceiverParameterDescriptor expectedThisObject = functionDescriptor.getExpectedThisObject();
267            if (functionDescriptor instanceof ConstructorDescriptor) {
268                return typeMapper.mapType(functionDescriptor);
269            }
270            else if (expectedThisObject != null) {
271                return typeMapper.mapType(expectedThisObject.getType());
272            }
273            else if (isFunctionLiteral(functionDescriptor) || isLocalNamedFun(functionDescriptor)) {
274                return typeMapper.mapType(context.getThisDescriptor());
275            }
276            else {
277                return null;
278            }
279        }
280    
281        public static void generateMethodBody(
282                @NotNull MethodVisitor mv,
283                @NotNull FunctionDescriptor functionDescriptor,
284                @NotNull MethodContext context,
285                @NotNull JvmMethodSignature signature,
286                @NotNull FunctionGenerationStrategy strategy,
287                @NotNull MemberCodegen<?> parentCodegen
288        ) {
289            mv.visitCode();
290    
291            Label methodBegin = new Label();
292            mv.visitLabel(methodBegin);
293    
294            JetTypeMapper typeMapper = parentCodegen.typeMapper;
295    
296            if (context.getParentContext() instanceof PackageFacadeContext) {
297                generateStaticDelegateMethodBody(mv, signature.getAsmMethod(), (PackageFacadeContext) context.getParentContext());
298            }
299            else {
300                FrameMap frameMap = createFrameMap(parentCodegen.state, functionDescriptor, signature, isStatic(context.getContextKind()));
301    
302                Label methodEntry = new Label();
303                mv.visitLabel(methodEntry);
304                context.setMethodStartLabel(methodEntry);
305    
306                if (!JetTypeMapper.isAccessor(functionDescriptor)) {
307                    genNotNullAssertionsForParameters(new InstructionAdapter(mv), parentCodegen.state, functionDescriptor, frameMap);
308                }
309    
310                strategy.generateBody(mv, frameMap, signature, context, parentCodegen);
311            }
312    
313            Label methodEnd = new Label();
314            mv.visitLabel(methodEnd);
315    
316            Type thisType = getThisTypeForFunction(functionDescriptor, context, typeMapper);
317            generateLocalVariableTable(mv, signature, functionDescriptor, thisType, methodBegin, methodEnd, context.getContextKind());
318        }
319    
320        private static void generateLocalVariableTable(
321                @NotNull MethodVisitor mv,
322                @NotNull JvmMethodSignature jvmMethodSignature,
323                @NotNull FunctionDescriptor functionDescriptor,
324                @Nullable Type thisType,
325                @NotNull Label methodBegin,
326                @NotNull Label methodEnd,
327                @NotNull OwnerKind ownerKind
328        ) {
329            Iterator<ValueParameterDescriptor> valueParameters = functionDescriptor.getValueParameters().iterator();
330            List<JvmMethodParameterSignature> params = jvmMethodSignature.getValueParameters();
331            int shift = 0;
332    
333            boolean isStatic = AsmUtil.isStaticMethod(ownerKind, functionDescriptor);
334            if (!isStatic) {
335                //add this
336                if (thisType != null) {
337                    mv.visitLocalVariable("this", thisType.getDescriptor(), null, methodBegin, methodEnd, shift);
338                } else {
339                    //TODO: provide thisType for callable reference
340                }
341                shift++;
342            }
343    
344            for (int i = 0; i < params.size(); i++) {
345                JvmMethodParameterSignature param =  params.get(i);
346                JvmMethodParameterKind kind = param.getKind();
347                String parameterName;
348    
349                if (kind == JvmMethodParameterKind.VALUE) {
350                    ValueParameterDescriptor parameter = valueParameters.next();
351                    parameterName = parameter.getName().asString();
352                }
353                else {
354                    String lowercaseKind = kind.name().toLowerCase();
355                    parameterName = needIndexForVar(kind)
356                                    ? "$" + lowercaseKind + "$" + i
357                                    : "$" + lowercaseKind;
358                }
359    
360                Type type = param.getAsmType();
361                mv.visitLocalVariable(parameterName, type.getDescriptor(), null, methodBegin, methodEnd, shift);
362                shift += type.getSize();
363            }
364        }
365    
366        private static void generateStaticDelegateMethodBody(
367                @NotNull MethodVisitor mv,
368                @NotNull Method asmMethod,
369                @NotNull PackageFacadeContext context
370        ) {
371            InstructionAdapter iv = new InstructionAdapter(mv);
372            Type[] argTypes = asmMethod.getArgumentTypes();
373    
374            // The first line of some package file is written to the line number attribute of a static delegate to allow to 'step into' it
375            // This is similar to what javac does with bridge methods
376            Label label = new Label();
377            iv.visitLabel(label);
378            iv.visitLineNumber(1, label);
379    
380            int k = 0;
381            for (Type argType : argTypes) {
382                iv.load(k, argType);
383                k += argType.getSize();
384            }
385            iv.invokestatic(context.getDelegateToClassType().getInternalName(), asmMethod.getName(), asmMethod.getDescriptor(), false);
386            iv.areturn(asmMethod.getReturnType());
387        }
388    
389        private static boolean needIndexForVar(JvmMethodParameterKind kind) {
390            return kind == JvmMethodParameterKind.CAPTURED_LOCAL_VARIABLE ||
391                   kind == JvmMethodParameterKind.ENUM_NAME_OR_ORDINAL ||
392                   kind == JvmMethodParameterKind.SUPER_CALL_PARAM;
393        }
394    
395        public static void endVisit(MethodVisitor mv, @Nullable String description, @Nullable PsiElement method) {
396            try {
397                mv.visitMaxs(-1, -1);
398                mv.visitEnd();
399            }
400            catch (ProcessCanceledException e) {
401                throw e;
402            }
403            catch (Throwable t) {
404                String bytecode = renderByteCodeIfAvailable(mv);
405                throw new CompilationException(
406                        "wrong code generated" +
407                        (description != null ? " for " + description : "") +
408                        t.getClass().getName() +
409                        " " +
410                        t.getMessage() +
411                        (bytecode != null ? "\nbytecode:\n" + bytecode : ""),
412                        t, method);
413            }
414        }
415    
416        private static String renderByteCodeIfAvailable(MethodVisitor mv) {
417            String bytecode = null;
418    
419            if (mv instanceof OptimizationMethodVisitor) {
420                mv = ((OptimizationMethodVisitor) mv).getTraceMethodVisitorIfPossible();
421            }
422    
423            if (mv instanceof TraceMethodVisitor) {
424                TraceMethodVisitor traceMethodVisitor = (TraceMethodVisitor) mv;
425                StringWriter sw = new StringWriter();
426                PrintWriter pw = new PrintWriter(sw);
427                traceMethodVisitor.p.print(pw);
428                pw.close();
429                bytecode = sw.toString();
430            }
431            return bytecode;
432        }
433    
434        public void generateBridges(@NotNull FunctionDescriptor descriptor) {
435            if (descriptor instanceof ConstructorDescriptor) return;
436            if (owner.getContextKind() == OwnerKind.TRAIT_IMPL) return;
437            if (isTrait(descriptor.getContainingDeclaration())) return;
438    
439            // equals(Any?), hashCode(), toString() never need bridges
440            if (isMethodOfAny(descriptor)) return;
441    
442            // If the function doesn't have a physical declaration among super-functions, it's a SAM adapter or alike and doesn't need bridges
443            if (CallResolverUtil.isOrOverridesSynthesized(descriptor)) return;
444    
445            Set<Bridge<Method>> bridgesToGenerate = BridgesPackage.generateBridgesForFunctionDescriptor(
446                    descriptor,
447                    new Function1<FunctionDescriptor, Method>() {
448                        @Override
449                        public Method invoke(FunctionDescriptor descriptor) {
450                            return typeMapper.mapSignature(descriptor).getAsmMethod();
451                        }
452                    }
453            );
454    
455            if (!bridgesToGenerate.isEmpty()) {
456                PsiElement origin = descriptor.getKind() == DECLARATION ? callableDescriptorToDeclaration(descriptor) : null;
457                for (Bridge<Method> bridge : bridgesToGenerate) {
458                    generateBridge(origin, descriptor, bridge.getFrom(), bridge.getTo());
459                }
460            }
461        }
462    
463        private static boolean isMethodOfAny(@NotNull FunctionDescriptor descriptor) {
464            String name = descriptor.getName().asString();
465            List<ValueParameterDescriptor> parameters = descriptor.getValueParameters();
466            if (parameters.isEmpty()) {
467                return name.equals("hashCode") || name.equals("toString");
468            }
469            else if (parameters.size() == 1 && name.equals("equals")) {
470                ValueParameterDescriptor parameter = parameters.get(0);
471                return parameter.getType().equals(KotlinBuiltIns.getInstance().getNullableAnyType());
472            }
473            return false;
474        }
475    
476        @NotNull
477        private static String[] getThrownExceptions(@NotNull FunctionDescriptor function, @NotNull final JetTypeMapper mapper) {
478            AnnotationDescriptor annotation = function.getAnnotations().findAnnotation(new FqName("kotlin.throws"));
479            if (annotation == null) return ArrayUtil.EMPTY_STRING_ARRAY;
480    
481            Collection<CompileTimeConstant<?>> values = annotation.getAllValueArguments().values();
482            if (values.isEmpty()) return ArrayUtil.EMPTY_STRING_ARRAY;
483    
484            Object value = values.iterator().next();
485            if (!(value instanceof ArrayValue)) return ArrayUtil.EMPTY_STRING_ARRAY;
486            ArrayValue arrayValue = (ArrayValue) value;
487    
488            List<String> strings = ContainerUtil.mapNotNull(
489                    arrayValue.getValue(),
490                    new Function<CompileTimeConstant<?>, String>() {
491                        @Override
492                        public String fun(CompileTimeConstant<?> constant) {
493                            if (constant instanceof JavaClassValue) {
494                                JavaClassValue classValue = (JavaClassValue) constant;
495                                ClassDescriptor classDescriptor = DescriptorUtils.getClassDescriptorForType(classValue.getValue());
496                                return mapper.mapClass(classDescriptor).getInternalName();
497                            }
498                            return null;
499                        }
500                    }
501            );
502            return ArrayUtil.toStringArray(strings);
503        }
504    
505        static void generateConstructorWithoutParametersIfNeeded(
506                @NotNull GenerationState state,
507                @NotNull CallableMethod method,
508                @NotNull ConstructorDescriptor constructorDescriptor,
509                @NotNull ClassBuilder classBuilder
510        ) {
511            if (!isDefaultConstructorNeeded(state.getBindingContext(), constructorDescriptor)) {
512                return;
513            }
514            int flags = getVisibilityAccessFlag(constructorDescriptor);
515            MethodVisitor mv = classBuilder.newMethod(OtherOrigin(constructorDescriptor), flags, "<init>", "()V", null,
516                                                      getThrownExceptions(constructorDescriptor, state.getTypeMapper()));
517    
518            if (state.getClassBuilderMode() == ClassBuilderMode.LIGHT_CLASSES) return;
519    
520            InstructionAdapter v = new InstructionAdapter(mv);
521            mv.visitCode();
522    
523            Type methodOwner = method.getOwner();
524            v.load(0, methodOwner); // Load this on stack
525    
526            int mask = 0;
527            for (ValueParameterDescriptor parameterDescriptor : constructorDescriptor.getValueParameters()) {
528                Type paramType = state.getTypeMapper().mapType(parameterDescriptor.getType());
529                pushDefaultValueOnStack(paramType, v);
530                mask |= (1 << parameterDescriptor.getIndex());
531            }
532            v.iconst(mask);
533            String desc = method.getAsmMethod().getDescriptor().replace(")", "I)");
534            v.invokespecial(methodOwner.getInternalName(), "<init>", desc, false);
535            v.areturn(Type.VOID_TYPE);
536            endVisit(mv, "default constructor for " + methodOwner.getInternalName(), null);
537        }
538    
539        void generateDefaultIfNeeded(
540                @NotNull MethodContext owner,
541                @NotNull JvmMethodSignature signature,
542                @NotNull FunctionDescriptor functionDescriptor,
543                @NotNull OwnerKind kind,
544                @NotNull DefaultParameterValueLoader loadStrategy,
545                @Nullable JetNamedFunction function
546        ) {
547            DeclarationDescriptor contextClass = owner.getContextDescriptor().getContainingDeclaration();
548    
549            if (kind != OwnerKind.TRAIT_IMPL &&
550                contextClass instanceof ClassDescriptor &&
551                ((ClassDescriptor) contextClass).getKind() == ClassKind.TRAIT) {
552                return;
553            }
554    
555            if (!isDefaultNeeded(functionDescriptor)) {
556                return;
557            }
558    
559            Method jvmSignature = signature.getAsmMethod();
560    
561            int flags = getVisibilityAccessFlag(functionDescriptor) | getDeprecatedAccessFlag(functionDescriptor);
562    
563            boolean isConstructor = "<init>".equals(jvmSignature.getName());
564    
565            Method defaultMethod = typeMapper.mapDefaultMethod(functionDescriptor, kind, owner);
566    
567            MethodVisitor mv = v.newMethod(OtherOrigin(functionDescriptor), flags | (isConstructor ? 0 : ACC_STATIC),
568                                           defaultMethod.getName(),
569                                           defaultMethod.getDescriptor(), null,
570                                           getThrownExceptions(functionDescriptor, typeMapper));
571    
572            if (state.getClassBuilderMode() == ClassBuilderMode.FULL) {
573                if (this.owner instanceof PackageFacadeContext) {
574                    mv.visitCode();
575                    generateStaticDelegateMethodBody(mv, defaultMethod, (PackageFacadeContext) this.owner);
576                    endVisit(mv, "default method delegation", callableDescriptorToDeclaration(functionDescriptor));
577                }
578                else {
579                    generateDefaultImpl(owner, signature, functionDescriptor, isStatic(kind), mv, loadStrategy, function);
580                }
581            }
582        }
583    
584        private void generateDefaultImpl(
585                @NotNull MethodContext methodContext,
586                @NotNull JvmMethodSignature signature,
587                @NotNull FunctionDescriptor functionDescriptor,
588                boolean isStatic,
589                @NotNull MethodVisitor mv,
590                @NotNull DefaultParameterValueLoader loadStrategy,
591                @Nullable JetNamedFunction function
592        ) {
593            mv.visitCode();
594            generateDefaultImplBody(methodContext, signature, functionDescriptor, isStatic, mv, loadStrategy, function, getParentCodegen(), state);
595            endVisit(mv, "default method", callableDescriptorToDeclaration(functionDescriptor));
596        }
597    
598        public static void generateDefaultImplBody(
599                @NotNull MethodContext methodContext,
600                @NotNull JvmMethodSignature signature,
601                @NotNull FunctionDescriptor functionDescriptor,
602                boolean isStatic,
603                @NotNull MethodVisitor mv,
604                @NotNull DefaultParameterValueLoader loadStrategy,
605                @Nullable JetNamedFunction function,
606                @NotNull MemberCodegen<?> parentCodegen,
607                @NotNull GenerationState state
608        ) {
609            FrameMap frameMap = createFrameMap(state, functionDescriptor, signature, isStatic);
610    
611            ExpressionCodegen codegen = new ExpressionCodegen(mv, frameMap, signature.getReturnType(), methodContext, state, parentCodegen);
612    
613            int maskIndex = frameMap.enterTemp(Type.INT_TYPE);
614    
615            CallGenerator generator = codegen.getOrCreateCallGenerator(functionDescriptor, function);
616    
617            InstructionAdapter iv = new InstructionAdapter(mv);
618            loadExplicitArgumentsOnStack(iv, OBJECT_TYPE, isStatic, signature);
619            generator.putHiddenParams();
620    
621            List<JvmMethodParameterSignature> mappedParameters = signature.getValueParameters();
622            int capturedArgumentsCount = 0;
623            while (capturedArgumentsCount < mappedParameters.size() &&
624                   mappedParameters.get(capturedArgumentsCount).getKind() != JvmMethodParameterKind.VALUE) {
625                capturedArgumentsCount++;
626            }
627    
628            List<ValueParameterDescriptor> valueParameters = functionDescriptor.getValueParameters();
629            for (int index = 0; index < valueParameters.size(); index++) {
630                ValueParameterDescriptor parameterDescriptor = valueParameters.get(index);
631                Type type = mappedParameters.get(capturedArgumentsCount + index).getAsmType();
632    
633                int parameterIndex = frameMap.getIndex(parameterDescriptor);
634                if (parameterDescriptor.declaresDefaultValue()) {
635                    iv.load(maskIndex, Type.INT_TYPE);
636                    iv.iconst(1 << index);
637                    iv.and(Type.INT_TYPE);
638                    Label loadArg = new Label();
639                    iv.ifeq(loadArg);
640    
641                    loadStrategy.putValueOnStack(parameterDescriptor, codegen);
642    
643                    iv.store(parameterIndex, type);
644    
645                    iv.mark(loadArg);
646                }
647    
648                generator.putValueIfNeeded(parameterDescriptor, type, StackValue.local(parameterIndex, type));
649            }
650    
651            CallableMethod method;
652            if (functionDescriptor instanceof ConstructorDescriptor) {
653                method = state.getTypeMapper().mapToCallableMethod((ConstructorDescriptor) functionDescriptor);
654            }
655            else {
656                method = state.getTypeMapper().mapToCallableMethod(functionDescriptor, false, methodContext);
657            }
658    
659            generator.genCallWithoutAssertions(method, codegen);
660    
661            iv.areturn(signature.getReturnType());
662        }
663    
664        @NotNull
665        private static FrameMap createFrameMap(
666                @NotNull GenerationState state,
667                @NotNull FunctionDescriptor function,
668                @NotNull JvmMethodSignature signature,
669                boolean isStatic
670        ) {
671            FrameMap frameMap = new FrameMap();
672            if (!isStatic) {
673                frameMap.enterTemp(OBJECT_TYPE);
674            }
675    
676            for (JvmMethodParameterSignature parameter : signature.getValueParameters()) {
677                if (parameter.getKind() != JvmMethodParameterKind.VALUE) {
678                    frameMap.enterTemp(parameter.getAsmType());
679                }
680            }
681    
682            for (ValueParameterDescriptor parameter : function.getValueParameters()) {
683                frameMap.enter(parameter, state.getTypeMapper().mapType(parameter));
684            }
685    
686            return frameMap;
687        }
688    
689        private static void loadExplicitArgumentsOnStack(
690                @NotNull InstructionAdapter iv,
691                @NotNull Type ownerType,
692                boolean isStatic,
693                @NotNull JvmMethodSignature signature
694        ) {
695            int var = 0;
696            if (!isStatic) {
697                iv.load(var, ownerType);
698                var += ownerType.getSize();
699            }
700    
701            for (JvmMethodParameterSignature parameterSignature : signature.getValueParameters()) {
702                if (parameterSignature.getKind() != JvmMethodParameterKind.VALUE) {
703                    Type type = parameterSignature.getAsmType();
704                    iv.load(var, type);
705                    var += type.getSize();
706                }
707            }
708        }
709    
710        private static boolean isDefaultNeeded(FunctionDescriptor functionDescriptor) {
711            boolean needed = false;
712            if (functionDescriptor != null) {
713                for (ValueParameterDescriptor parameterDescriptor : functionDescriptor.getValueParameters()) {
714                    if (parameterDescriptor.declaresDefaultValue()) {
715                        needed = true;
716                        break;
717                    }
718                }
719            }
720            return needed;
721        }
722    
723        private static boolean isDefaultConstructorNeeded(@NotNull BindingContext context, @NotNull ConstructorDescriptor constructorDescriptor) {
724            ClassDescriptor classDescriptor = constructorDescriptor.getContainingDeclaration();
725    
726            if (CodegenBinding.canHaveOuter(context, classDescriptor)) return false;
727    
728            if (classDescriptor.getVisibility() == Visibilities.PRIVATE ||
729                constructorDescriptor.getVisibility() == Visibilities.PRIVATE) return false;
730    
731            if (constructorDescriptor.getValueParameters().isEmpty()) return false;
732    
733            for (ValueParameterDescriptor parameterDescriptor : constructorDescriptor.getValueParameters()) {
734                if (!parameterDescriptor.declaresDefaultValue()) {
735                    return false;
736                }
737            }
738            return true;
739        }
740    
741        private void generateBridge(
742                @Nullable PsiElement origin,
743                @NotNull FunctionDescriptor descriptor,
744                @NotNull Method bridge,
745                @NotNull Method delegateTo
746        ) {
747            int flags = ACC_PUBLIC | ACC_BRIDGE | ACC_SYNTHETIC; // TODO.
748    
749            MethodVisitor mv = v.newMethod(OtherOrigin(descriptor), flags, delegateTo.getName(), bridge.getDescriptor(), null, null);
750            if (state.getClassBuilderMode() != ClassBuilderMode.FULL) return;
751    
752            mv.visitCode();
753    
754            Type[] argTypes = bridge.getArgumentTypes();
755            Type[] originalArgTypes = delegateTo.getArgumentTypes();
756    
757            InstructionAdapter iv = new InstructionAdapter(mv);
758            iv.load(0, OBJECT_TYPE);
759            for (int i = 0, reg = 1; i < argTypes.length; i++) {
760                StackValue.local(reg, argTypes[i]).put(originalArgTypes[i], iv);
761                //noinspection AssignmentToForLoopParameter
762                reg += argTypes[i].getSize();
763            }
764    
765            iv.invokevirtual(v.getThisName(), delegateTo.getName(), delegateTo.getDescriptor());
766    
767            StackValue.coerce(delegateTo.getReturnType(), bridge.getReturnType(), iv);
768            iv.areturn(bridge.getReturnType());
769    
770            endVisit(mv, "bridge method", origin);
771        }
772    
773        public void genDelegate(FunctionDescriptor functionDescriptor, FunctionDescriptor overriddenDescriptor, StackValue field) {
774            genDelegate(functionDescriptor, (ClassDescriptor) overriddenDescriptor.getContainingDeclaration(), field,
775                        typeMapper.mapSignature(functionDescriptor),
776                        typeMapper.mapSignature(overriddenDescriptor.getOriginal())
777            );
778        }
779    
780        public void genDelegate(
781                FunctionDescriptor functionDescriptor,
782                final ClassDescriptor toClass,
783                final StackValue field,
784                final JvmMethodSignature jvmDelegateMethodSignature,
785                final JvmMethodSignature jvmOverriddenMethodSignature
786        ) {
787            generateMethod(
788                    OtherOrigin(functionDescriptor), jvmDelegateMethodSignature, functionDescriptor,
789                    new FunctionGenerationStrategy() {
790                        @Override
791                        public void generateBody(
792                                @NotNull MethodVisitor mv,
793                                @NotNull FrameMap frameMap,
794                                @NotNull JvmMethodSignature signature,
795                                @NotNull MethodContext context,
796                                @NotNull MemberCodegen<?> parentCodegen
797                        ) {
798                            Method overriddenMethod = jvmOverriddenMethodSignature.getAsmMethod();
799                            Method delegateMethod = jvmDelegateMethodSignature.getAsmMethod();
800    
801                            Type[] argTypes = delegateMethod.getArgumentTypes();
802                            Type[] originalArgTypes = overriddenMethod.getArgumentTypes();
803    
804                            InstructionAdapter iv = new InstructionAdapter(mv);
805                            iv.load(0, OBJECT_TYPE);
806                            field.put(field.type, iv);
807                            for (int i = 0, reg = 1; i < argTypes.length; i++) {
808                                StackValue.local(reg, argTypes[i]).put(originalArgTypes[i], iv);
809                                //noinspection AssignmentToForLoopParameter
810                                reg += argTypes[i].getSize();
811                            }
812    
813                            String internalName = typeMapper.mapType(toClass).getInternalName();
814                            if (toClass.getKind() == ClassKind.TRAIT) {
815                                iv.invokeinterface(internalName, overriddenMethod.getName(), overriddenMethod.getDescriptor());
816                            }
817                            else {
818                                iv.invokevirtual(internalName, overriddenMethod.getName(), overriddenMethod.getDescriptor());
819                            }
820    
821                            StackValue.onStack(overriddenMethod.getReturnType()).put(delegateMethod.getReturnType(), iv);
822    
823                            iv.areturn(delegateMethod.getReturnType());
824                        }
825                    }
826            );
827        }
828    }