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;
018    
019    import com.google.common.collect.Lists;
020    import com.google.common.collect.Ordering;
021    import com.intellij.openapi.application.ApplicationManager;
022    import com.intellij.openapi.progress.ProcessCanceledException;
023    import com.intellij.openapi.vfs.VirtualFile;
024    import com.intellij.util.ArrayUtil;
025    import com.intellij.util.SmartList;
026    import com.intellij.util.containers.ContainerUtil;
027    import kotlin.jvm.functions.Function0;
028    import org.jetbrains.annotations.Mutable;
029    import org.jetbrains.annotations.NotNull;
030    import org.jetbrains.annotations.Nullable;
031    import org.jetbrains.kotlin.codegen.context.CodegenContext;
032    import org.jetbrains.kotlin.codegen.context.FieldOwnerContext;
033    import org.jetbrains.kotlin.codegen.context.MethodContext;
034    import org.jetbrains.kotlin.codegen.context.PackageContext;
035    import org.jetbrains.kotlin.codegen.state.GenerationState;
036    import org.jetbrains.kotlin.config.IncrementalCompilation;
037    import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor;
038    import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
039    import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor;
040    import org.jetbrains.kotlin.diagnostics.DiagnosticUtils;
041    import org.jetbrains.kotlin.fileClasses.FileClassesPackage;
042    import org.jetbrains.kotlin.fileClasses.JvmFileClassInfo;
043    import org.jetbrains.kotlin.load.java.JvmAbi;
044    import org.jetbrains.kotlin.load.java.JvmAnnotationNames;
045    import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils;
046    import org.jetbrains.kotlin.load.kotlin.PackageParts;
047    import org.jetbrains.kotlin.load.kotlin.incremental.IncrementalPackageFragmentProvider;
048    import org.jetbrains.kotlin.name.FqName;
049    import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus;
050    import org.jetbrains.kotlin.psi.*;
051    import org.jetbrains.kotlin.resolve.BindingContext;
052    import org.jetbrains.kotlin.resolve.MemberComparator;
053    import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
054    import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter;
055    import org.jetbrains.kotlin.resolve.scopes.JetScope;
056    import org.jetbrains.kotlin.serialization.DescriptorSerializer;
057    import org.jetbrains.kotlin.serialization.ProtoBuf;
058    import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor;
059    import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor;
060    import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedSimpleFunctionDescriptor;
061    import org.jetbrains.kotlin.serialization.jvm.BitEncoding;
062    import org.jetbrains.org.objectweb.asm.AnnotationVisitor;
063    import org.jetbrains.org.objectweb.asm.MethodVisitor;
064    import org.jetbrains.org.objectweb.asm.Type;
065    import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
066    import org.jetbrains.org.objectweb.asm.commons.Method;
067    
068    import java.util.*;
069    
070    import static org.jetbrains.kotlin.codegen.AsmUtil.asmDescByFqNameWithoutInnerClasses;
071    import static org.jetbrains.kotlin.codegen.AsmUtil.method;
072    import static org.jetbrains.kotlin.load.kotlin.PackageClassUtils.getPackageClassFqName;
073    import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.K_PACKAGE_TYPE;
074    import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.getType;
075    import static org.jetbrains.kotlin.resolve.jvm.diagnostics.DiagnosticsPackage.*;
076    import static org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.NO_ORIGIN;
077    import static org.jetbrains.org.objectweb.asm.Opcodes.*;
078    
079    public class PackageCodegen {
080        private final ClassBuilderOnDemand v;
081        private final GenerationState state;
082        private final Collection<JetFile> files;
083        private final Type packageClassType;
084        private final PackageFragmentDescriptor packageFragment;
085        private final PackageFragmentDescriptor compiledPackageFragment;
086        private final List<DeserializedCallableMemberDescriptor> previouslyCompiledCallables;
087    
088        private final PackageParts packageParts;
089    
090        public PackageCodegen(
091                @NotNull GenerationState state,
092                @NotNull Collection<JetFile> files,
093                @NotNull FqName fqName
094        ) {
095            this.state = state;
096            this.files = files;
097            this.packageFragment = getOnlyPackageFragment(fqName);
098            this.packageClassType = AsmUtil.asmTypeByFqNameWithoutInnerClasses(getPackageClassFqName(fqName));
099            this.compiledPackageFragment = getCompiledPackageFragment(fqName);
100            this.previouslyCompiledCallables = filterDeserializedCallables(compiledPackageFragment);
101    
102            assert packageFragment != null || compiledPackageFragment != null : fqName.asString() + " " + files;
103    
104            this.v = new ClassBuilderOnDemand(new Function0<ClassBuilder>() {
105                @Override
106                public ClassBuilder invoke() {
107                    Collection<JetFile> files = PackageCodegen.this.files;
108                    JetFile sourceFile = getRepresentativePackageFile(files);
109    
110                    ClassBuilder v = PackageCodegen.this.state.getFactory().newVisitor(
111                            PackageFacade(packageFragment == null ? compiledPackageFragment : packageFragment),
112                            packageClassType, PackagePartClassUtils.getFilesWithCallables(files)
113                    );
114                    v.defineClass(sourceFile, V1_6,
115                                  ACC_PUBLIC | ACC_FINAL | ACC_DEPRECATED,
116                                  packageClassType.getInternalName(),
117                                  null,
118                                  "java/lang/Object",
119                                  ArrayUtil.EMPTY_STRING_ARRAY
120                    );
121                    //We don't generate any source information for package with multiple files
122                    if (sourceFile != null) {
123                        v.visitSource(sourceFile.getName(), null);
124                    }
125                    return v;
126                }
127            });
128            packageParts = new PackageParts(fqName.asString());
129        }
130    
131        // Returns null if file has callables in several files
132        @Nullable
133        private JetFile getRepresentativePackageFile(@NotNull Collection<JetFile> packageFiles) {
134            if (!previouslyCompiledCallables.isEmpty()) {
135                return null;
136            }
137    
138            List<JetFile> packageFilesWithCallables = PackagePartClassUtils.getFilesWithCallables(packageFiles);
139            return packageFilesWithCallables.size() == 1 ? packageFilesWithCallables.get(0) : null;
140        }
141    
142        @Nullable
143        private PackageFragmentDescriptor getCompiledPackageFragment(@NotNull FqName fqName) {
144            if (!IncrementalCompilation.isEnabled()) {
145                return null;
146            }
147    
148            // TODO rewrite it to something more robust when module system is implemented
149            for (PackageFragmentDescriptor fragment : state.getModule().getPackage(fqName).getFragments()) {
150                if (fragment instanceof IncrementalPackageFragmentProvider.IncrementalPackageFragment &&
151                    ((IncrementalPackageFragmentProvider.IncrementalPackageFragment) fragment).getTarget().equals(state.getTargetId())) {
152                    return fragment;
153                }
154            }
155            return null;
156        }
157    
158        @NotNull
159        private static List<DeserializedCallableMemberDescriptor> filterDeserializedCallables(@Nullable PackageFragmentDescriptor packageFragment) {
160            if (packageFragment == null) {
161                return Collections.emptyList();
162            }
163            List<DeserializedCallableMemberDescriptor> callables = Lists.newArrayList();
164            for (DeclarationDescriptor member : packageFragment.getMemberScope().getDescriptors(DescriptorKindFilter.CALLABLES, JetScope.ALL_NAME_FILTER)) {
165                if (member instanceof DeserializedCallableMemberDescriptor) {
166                    callables.add((DeserializedCallableMemberDescriptor) member);
167                }
168            }
169            return callables;
170        }
171    
172        private void generateDelegationsToPreviouslyCompiled(@NotNull @Mutable Map<CallableMemberDescriptor, Runnable> generateCallableMemberTasks) {
173            for (final DeserializedCallableMemberDescriptor member : previouslyCompiledCallables) {
174                generateCallableMemberTasks.put(member, new Runnable() {
175                    @Override
176                    public void run() {
177                        FieldOwnerContext context = state.getRootContext().intoPackageFacade(
178                                AsmUtil.asmTypeByFqNameWithoutInnerClasses(PackagePartClassUtils.getPackagePartFqName(member)),
179                                compiledPackageFragment
180                        );
181    
182                        MemberCodegen<?> memberCodegen = createCodegenForPartOfPackageFacade(context);
183    
184                        if (member instanceof DeserializedSimpleFunctionDescriptor) {
185                            DeserializedSimpleFunctionDescriptor function = (DeserializedSimpleFunctionDescriptor) member;
186                            memberCodegen.functionCodegen.generateMethod(OtherOrigin(function), function,
187                                                                         new FunctionGenerationStrategy() {
188                                                                             @Override
189                                                                             public void generateBody(
190                                                                                     @NotNull MethodVisitor mv,
191                                                                                     @NotNull FrameMap frameMap,
192                                                                                     @NotNull JvmMethodSignature signature,
193                                                                                     @NotNull MethodContext context,
194                                                                                     @NotNull MemberCodegen<?> parentCodegen
195                                                                             ) {
196                                                                                 throw new IllegalStateException("shouldn't be called");
197                                                                             }
198                                                                         }
199                            );
200    
201                            memberCodegen.functionCodegen.generateDefaultIfNeeded(
202                                    context.intoFunction(function), function, OwnerKind.PACKAGE, DefaultParameterValueLoader.DEFAULT, null
203                            );
204    
205                            memberCodegen.functionCodegen.generateOverloadsWithDefaultValues(null, function, function);
206                        }
207                        else if (member instanceof DeserializedPropertyDescriptor) {
208                            memberCodegen.propertyCodegen.generateInPackageFacade((DeserializedPropertyDescriptor) member);
209                        }
210                        else {
211                            throw new IllegalStateException("Unexpected member: " + member);
212                        }
213                    }
214                });
215            }
216        }
217    
218        public void generate(@NotNull CompilationErrorHandler errorHandler) {
219            List<JvmSerializationBindings> bindings = new ArrayList<JvmSerializationBindings>(files.size() + 1);
220    
221            Map<CallableMemberDescriptor, Runnable> generateCallableMemberTasks = new HashMap<CallableMemberDescriptor, Runnable>();
222    
223            for (JetFile file : files) {
224                ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
225                try {
226                    ClassBuilder builder = generateFile(file, generateCallableMemberTasks);
227                    if (builder != null) {
228                        bindings.add(builder.getSerializationBindings());
229                    }
230                }
231                catch (ProcessCanceledException e) {
232                    throw e;
233                }
234                catch (Throwable e) {
235                    VirtualFile vFile = file.getVirtualFile();
236                    errorHandler.reportException(e, vFile == null ? "no file" : vFile.getUrl());
237                    DiagnosticUtils.throwIfRunningOnServer(e);
238                    if (ApplicationManager.getApplication().isInternal()) {
239                        //noinspection CallToPrintStackTrace
240                        e.printStackTrace();
241                    }
242                }
243            }
244    
245            generateDelegationsToPreviouslyCompiled(generateCallableMemberTasks);
246    
247            if (!generateCallableMemberTasks.isEmpty()) {
248                generatePackageFacadeClass(generateCallableMemberTasks, bindings);
249            }
250        }
251    
252        private void generatePackageFacadeClass(
253                @NotNull Map<CallableMemberDescriptor, Runnable> tasks,
254                @NotNull List<JvmSerializationBindings> bindings
255        ) {
256            generateKotlinPackageReflectionField();
257            MemberCodegen.generateModuleNameField(state, v);
258    
259            for (CallableMemberDescriptor member : Ordering.from(MemberComparator.INSTANCE).sortedCopy(tasks.keySet())) {
260                tasks.get(member).run();
261            }
262    
263            bindings.add(v.getSerializationBindings());
264            writeDeprecatedAnnotation();
265            writeKotlinPackageAnnotationIfNeeded(JvmSerializationBindings.union(bindings));
266        }
267    
268        private void generateKotlinPackageReflectionField() {
269            MethodVisitor mv = v.newMethod(NO_ORIGIN, ACC_STATIC, "<clinit>", "()V", null, null);
270            Method method = method("createKotlinPackage", K_PACKAGE_TYPE, getType(Class.class), getType(String.class));
271            InstructionAdapter iv = new InstructionAdapter(mv);
272            MemberCodegen.generateReflectionObjectField(state, packageClassType, v, method, JvmAbi.KOTLIN_PACKAGE_FIELD_NAME, iv);
273            iv.areturn(Type.VOID_TYPE);
274            FunctionCodegen.endVisit(mv, "package facade static initializer", null);
275        }
276    
277        private void writeKotlinPackageAnnotationIfNeeded(@NotNull JvmSerializationBindings bindings) {
278            if (state.getClassBuilderMode() != ClassBuilderMode.FULL) {
279                return;
280            }
281    
282            // SCRIPT: Do not write KotlinPackage annotation for scripts (if any is??)
283            for (JetFile file : files) {
284                if (file.isScript()) return;
285            }
286    
287            DescriptorSerializer serializer = DescriptorSerializer.createTopLevel(new JvmSerializerExtension(bindings, state.getTypeMapper()));
288            Collection<PackageFragmentDescriptor> packageFragments = Lists.newArrayList();
289            ContainerUtil.addIfNotNull(packageFragments, packageFragment);
290            ContainerUtil.addIfNotNull(packageFragments, compiledPackageFragment);
291            ProtoBuf.Package packageProto = serializer.packageProtoWithoutDescriptors().build();
292    
293            AnnotationVisitor av = v.newAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_PACKAGE), true);
294            JvmCodegenUtil.writeAbiVersion(av);
295            AnnotationVisitor array = av.visitArray(JvmAnnotationNames.DATA_FIELD_NAME);
296            for (String string : BitEncoding.encodeBytes(serializer.serialize(packageProto))) {
297                array.visit(null, string);
298            }
299            array.visitEnd();
300            av.visitEnd();
301        }
302    
303        private void writeDeprecatedAnnotation() {
304            AnnotationVisitor av = v.newAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.JAVA_LANG_DEPRECATED), true);
305            av.visitEnd();
306        }
307    
308        @Nullable
309        private ClassBuilder generateFile(@NotNull JetFile file, @NotNull Map<CallableMemberDescriptor, Runnable> generateCallableMemberTasks) {
310            JvmFileClassInfo fileClassInfo = state.getFileClassesProvider().getFileClassInfo(file);
311    
312            if (fileClassInfo.getIsMultifileClass()) {
313                Type fileFacadeType = AsmUtil.asmTypeByFqNameWithoutInnerClasses(fileClassInfo.getFacadeClassFqName());
314                addDelegateToFileClassMemberTasks(file, generateCallableMemberTasks, fileFacadeType);
315                return null;
316            }
317    
318            Type fileClassType = AsmUtil.asmTypeByFqNameWithoutInnerClasses(fileClassInfo.getFileClassFqName());
319            PackageContext packagePartContext = state.getRootContext().intoPackagePart(packageFragment, fileClassType);
320    
321            boolean generatePackagePart = false;
322    
323            for (JetDeclaration declaration : file.getDeclarations()) {
324                if (declaration instanceof JetProperty || declaration instanceof JetNamedFunction) {
325                    generatePackagePart = true;
326                }
327                else if (declaration instanceof JetClassOrObject) {
328                    JetClassOrObject classOrObject = (JetClassOrObject) declaration;
329                    if (state.getGenerateDeclaredClassFilter().shouldGenerateClass(classOrObject)) {
330                        generateClassOrObject(classOrObject);
331                    }
332                }
333                else if (declaration instanceof JetScript) {
334                    JetScript script = (JetScript) declaration;
335    
336                    // SCRIPT: generate script code, should be separate execution branch
337                    if (state.getGenerateDeclaredClassFilter().shouldGenerateScript(script)) {
338                        ScriptCodegen.createScriptCodegen(script, state, packagePartContext).generate();
339                    }
340                }
341            }
342    
343            if (!generatePackagePart || !state.getGenerateDeclaredClassFilter().shouldGeneratePackagePart(file)) return null;
344    
345            String name = fileClassType.getInternalName();
346            packageParts.getParts().add(name.substring(name.lastIndexOf('/') + 1));
347    
348            ClassBuilder builder = state.getFactory().newVisitor(PackagePart(file, packageFragment), fileClassType, file);
349    
350            new PackagePartCodegen(builder, file, fileClassType, packagePartContext, state).generate();
351    
352            addDelegateToFileClassMemberTasks(file, generateCallableMemberTasks, fileClassType);
353    
354            return builder;
355        }
356    
357        private void addDelegateToFileClassMemberTasks(
358                @NotNull JetFile file,
359                @NotNull Map<CallableMemberDescriptor, Runnable> generateCallableMemberTasks,
360                @NotNull Type fileClassType
361        ) {
362            FieldOwnerContext packageFacade = state.getRootContext().intoPackageFacade(fileClassType, packageFragment);
363            final MemberCodegen<?> memberCodegen = createCodegenForPartOfPackageFacade(packageFacade);
364    
365            for (final JetDeclaration declaration : file.getDeclarations()) {
366                if (declaration instanceof JetNamedFunction || declaration instanceof JetProperty) {
367                    DeclarationDescriptor descriptor = state.getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration);
368                    assert descriptor instanceof CallableMemberDescriptor :
369                            "Expected callable member, was " + descriptor + " for " + declaration.getText();
370                    generateCallableMemberTasks.put(
371                            (CallableMemberDescriptor) descriptor,
372                            new Runnable() {
373                                @Override
374                                public void run() {
375                                    memberCodegen.genFunctionOrProperty(declaration);
376                                }
377                            }
378                    );
379                }
380            }
381        }
382    
383        private MemberCodegen<?> createCodegenForPartOfPackageFacade(@NotNull FieldOwnerContext packageFacade) {
384            return new MemberCodegen<JetFile>(state, null, packageFacade, null, v) {
385                @Override
386                protected void generateDeclaration() {
387                    throw new UnsupportedOperationException();
388                }
389    
390                @Override
391                protected void generateBody() {
392                    throw new UnsupportedOperationException();
393                }
394    
395                @Override
396                protected void generateKotlinAnnotation() {
397                    throw new UnsupportedOperationException();
398                }
399            };
400        }
401    
402        @Nullable
403        private PackageFragmentDescriptor getOnlyPackageFragment(@NotNull FqName expectedFqName) {
404            SmartList<PackageFragmentDescriptor> fragments = new SmartList<PackageFragmentDescriptor>();
405            for (JetFile file : files) {
406                PackageFragmentDescriptor fragment = state.getBindingContext().get(BindingContext.FILE_TO_PACKAGE_FRAGMENT, file);
407                assert fragment != null : "package fragment is null for " + file + "\n" + file.getText();
408    
409                assert expectedFqName.equals(fragment.getFqName()) :
410                        "expected package fq name: " + expectedFqName + ", actual: " + fragment.getFqName();
411    
412                if (!fragments.contains(fragment)) {
413                    fragments.add(fragment);
414                }
415            }
416            if (fragments.size() > 1) {
417                throw new IllegalStateException("More than one package fragment, files: " + files + " | fragments: " + fragments);
418            }
419    
420            if (fragments.isEmpty()) {
421                return null;
422            }
423            return fragments.get(0);
424        }
425    
426        public void generateClassOrObject(@NotNull JetClassOrObject classOrObject) {
427            JetFile file = classOrObject.getContainingJetFile();
428            Type packagePartType = FileClassesPackage.getFileClassType(state.getFileClassesProvider(), file);
429            CodegenContext context = state.getRootContext().intoPackagePart(packageFragment, packagePartType);
430            MemberCodegen.genClassOrObject(context, classOrObject, state, null);
431        }
432    
433        public void done() {
434            v.done();
435        }
436    
437        public PackageParts getPackageParts() {
438            return packageParts;
439        }
440    
441        public Collection<JetFile> getFiles() {
442            return files;
443        }
444    }