001    /*
002     * Copyright 2010-2014 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.jet.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.util.io.FileUtil;
024    import com.intellij.openapi.vfs.VirtualFile;
025    import com.intellij.psi.PsiFile;
026    import com.intellij.util.ArrayUtil;
027    import com.intellij.util.PathUtil;
028    import com.intellij.util.SmartList;
029    import org.jetbrains.annotations.NotNull;
030    import org.jetbrains.annotations.Nullable;
031    import org.jetbrains.jet.codegen.context.CodegenContext;
032    import org.jetbrains.jet.codegen.context.FieldOwnerContext;
033    import org.jetbrains.jet.codegen.context.MethodContext;
034    import org.jetbrains.jet.codegen.context.PackageContext;
035    import org.jetbrains.jet.codegen.signature.JvmMethodSignature;
036    import org.jetbrains.jet.codegen.state.GenerationState;
037    import org.jetbrains.jet.config.IncrementalCompilation;
038    import org.jetbrains.jet.descriptors.serialization.BitEncoding;
039    import org.jetbrains.jet.descriptors.serialization.DescriptorSerializer;
040    import org.jetbrains.jet.descriptors.serialization.PackageData;
041    import org.jetbrains.jet.descriptors.serialization.ProtoBuf;
042    import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedCallableMemberDescriptor;
043    import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedPropertyDescriptor;
044    import org.jetbrains.jet.descriptors.serialization.descriptors.DeserializedSimpleFunctionDescriptor;
045    import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor;
046    import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
047    import org.jetbrains.jet.lang.descriptors.PackageFragmentDescriptor;
048    import org.jetbrains.jet.lang.diagnostics.DiagnosticUtils;
049    import org.jetbrains.jet.lang.psi.*;
050    import org.jetbrains.jet.lang.resolve.BindingContext;
051    import org.jetbrains.jet.lang.resolve.MemberComparator;
052    import org.jetbrains.jet.lang.resolve.java.JvmAbi;
053    import org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames;
054    import org.jetbrains.jet.lang.resolve.java.JvmClassName;
055    import org.jetbrains.jet.lang.resolve.java.lazy.descriptors.LazyJavaPackageFragmentScope;
056    import org.jetbrains.jet.lang.resolve.kotlin.BaseDescriptorDeserializer;
057    import org.jetbrains.jet.lang.resolve.name.FqName;
058    import org.jetbrains.jet.lang.resolve.name.Name;
059    import org.jetbrains.org.objectweb.asm.AnnotationVisitor;
060    import org.jetbrains.org.objectweb.asm.MethodVisitor;
061    import org.jetbrains.org.objectweb.asm.Type;
062    
063    import java.util.*;
064    
065    import static org.jetbrains.jet.codegen.AsmUtil.asmDescByFqNameWithoutInnerClasses;
066    import static org.jetbrains.jet.codegen.AsmUtil.asmTypeByFqNameWithoutInnerClasses;
067    import static org.jetbrains.jet.descriptors.serialization.NameSerializationUtil.createNameResolver;
068    import static org.jetbrains.jet.lang.resolve.java.PackageClassUtils.getPackageClassFqName;
069    import static org.jetbrains.org.objectweb.asm.Opcodes.*;
070    
071    public class PackageCodegen {
072        private final GenerationState state;
073        private final ClassBuilderOnDemand v;
074        private final Collection<JetFile> files;
075        private final PackageFragmentDescriptor packageFragment;
076        private final PackageFragmentDescriptor compiledPackageFragment;
077    
078        public PackageCodegen(
079                @NotNull ClassBuilderOnDemand v,
080                @NotNull final FqName fqName,
081                @NotNull GenerationState state,
082                @NotNull Collection<JetFile> packageFiles
083        ) {
084            this.state = state;
085            this.v = v;
086            this.files = packageFiles;
087            this.packageFragment = getOnlyPackageFragment();
088            this.compiledPackageFragment = getCompiledPackageFragment();
089    
090            final PsiFile sourceFile = packageFiles.size() == 1 && getPreviouslyCompiledCallables().isEmpty()
091                                       ? packageFiles.iterator().next().getContainingFile() : null;
092    
093            v.addOptionalDeclaration(new ClassBuilderOnDemand.ClassBuilderCallback() {
094                @Override
095                public void doSomething(@NotNull ClassBuilder v) {
096                    v.defineClass(sourceFile, V1_6,
097                                  ACC_PUBLIC | ACC_FINAL,
098                                  JvmClassName.byFqNameWithoutInnerClasses(getPackageClassFqName(fqName)).getInternalName(),
099                                  null,
100                                  "java/lang/Object",
101                                  ArrayUtil.EMPTY_STRING_ARRAY
102                    );
103                    //We don't generate any source information for package with multiple files
104                    if (sourceFile != null) {
105                        v.visitSource(sourceFile.getName(), null);
106                    }
107                }
108            });
109        }
110    
111        @Nullable
112        private PackageFragmentDescriptor getCompiledPackageFragment() {
113            if (!IncrementalCompilation.ENABLED) {
114                return null;
115            }
116    
117            // TODO rewrite it to something more robust when module system is implemented
118            for (PackageFragmentDescriptor anotherFragment : packageFragment.getContainingDeclaration().getPackageFragmentProvider()
119                    .getPackageFragments(packageFragment.getFqName())) {
120                if (anotherFragment.getMemberScope() instanceof LazyJavaPackageFragmentScope) {
121                    return anotherFragment;
122                }
123            }
124            return null;
125        }
126    
127        @NotNull
128        private List<DeserializedCallableMemberDescriptor> getPreviouslyCompiledCallables() {
129            List<DeserializedCallableMemberDescriptor> callables = Lists.newArrayList();
130            if (compiledPackageFragment != null) {
131                for (DeclarationDescriptor member : compiledPackageFragment.getMemberScope().getAllDescriptors()) {
132                    if (member instanceof DeserializedCallableMemberDescriptor) {
133                        callables.add((DeserializedCallableMemberDescriptor) member);
134                    }
135                }
136            }
137            return callables;
138        }
139    
140        private void generateDelegationsToPreviouslyCompiled(Map<CallableMemberDescriptor, Runnable> generateCallableMemberTasks) {
141            for (final DeserializedCallableMemberDescriptor member : getPreviouslyCompiledCallables()) {
142                generateCallableMemberTasks.put(member, new Runnable() {
143                    @Override
144                    public void run() {
145                        FieldOwnerContext context = CodegenContext.STATIC.intoPackageFacade(
146                                Type.getObjectType(getPackagePartInternalName(member)),
147                                compiledPackageFragment);
148    
149                        FunctionCodegen functionCodegen = new FunctionCodegen(
150                                context,
151                                v.getClassBuilder(),
152                                state,
153                                getMemberCodegen(context)
154                        );
155    
156                        if (member instanceof DeserializedSimpleFunctionDescriptor) {
157                            DeserializedSimpleFunctionDescriptor function = (DeserializedSimpleFunctionDescriptor) member;
158                            JvmMethodSignature signature = state.getTypeMapper().mapSignature(function, OwnerKind.PACKAGE);
159                            functionCodegen.generateMethod(null, signature, function,
160                                                           new FunctionGenerationStrategy() {
161                                                               @Override
162                                                               public void generateBody(
163                                                                       @NotNull MethodVisitor mv,
164                                                                       @NotNull JvmMethodSignature signature,
165                                                                       @NotNull MethodContext context,
166                                                                       @Nullable MemberCodegen<?> parentCodegen
167                                                               ) {
168                                                                   throw new IllegalStateException("shouldn't be called");
169                                                               }
170                                                           }
171                            );
172                        }
173                        else if (member instanceof DeserializedPropertyDescriptor) {
174                            PropertyCodegen propertyCodegen = new PropertyCodegen(
175                                    context, v.getClassBuilder(), functionCodegen, getMemberCodegen(context));
176                            propertyCodegen.generateInPackageFacade((DeserializedPropertyDescriptor) member);
177                        }
178                        else {
179                            throw new IllegalStateException("Unexpected member: " + member);
180                        }
181                    }
182                });
183            }
184        }
185    
186        public void generate(@NotNull CompilationErrorHandler errorHandler) {
187            List<JvmSerializationBindings> bindings = new ArrayList<JvmSerializationBindings>(files.size() + 1);
188            boolean shouldGeneratePackageClass = shouldGeneratePackageClass(files);
189            if (shouldGeneratePackageClass) {
190                bindings.add(v.getClassBuilder().getSerializationBindings());
191            }
192    
193            Map<CallableMemberDescriptor, Runnable> generateCallableMemberTasks = new HashMap<CallableMemberDescriptor, Runnable>();
194    
195            for (JetFile file : files) {
196                try {
197                    ClassBuilder builder = generate(file, generateCallableMemberTasks);
198                    if (builder != null) {
199                        bindings.add(builder.getSerializationBindings());
200                    }
201                }
202                catch (ProcessCanceledException e) {
203                    throw e;
204                }
205                catch (Throwable e) {
206                    VirtualFile vFile = file.getVirtualFile();
207                    errorHandler.reportException(e, vFile == null ? "no file" : vFile.getUrl());
208                    DiagnosticUtils.throwIfRunningOnServer(e);
209                    if (ApplicationManager.getApplication().isInternal()) {
210                        //noinspection CallToPrintStackTrace
211                        e.printStackTrace();
212                    }
213                }
214            }
215    
216            if (shouldGeneratePackageClass) {
217                // Shouldn't generate delegations to previously compiled if we compile only "classes" part of a package.
218                generateDelegationsToPreviouslyCompiled(generateCallableMemberTasks);
219            }
220    
221            for (CallableMemberDescriptor member : Ordering.from(MemberComparator.INSTANCE).sortedCopy(generateCallableMemberTasks.keySet())) {
222                generateCallableMemberTasks.get(member).run();
223            }
224    
225            if (shouldGeneratePackageClass) {
226                writeKotlinPackageAnnotationIfNeeded(JvmSerializationBindings.union(bindings));
227            }
228    
229            assert v.isActivated() == shouldGeneratePackageClass :
230                    "Different algorithms for generating package class and for heuristics for: " + packageFragment;
231        }
232    
233        private void writeKotlinPackageAnnotationIfNeeded(@NotNull JvmSerializationBindings bindings) {
234            if (state.getClassBuilderMode() != ClassBuilderMode.FULL) {
235                return;
236            }
237    
238            // SCRIPT: Do not write annotations for scripts (if any is??)
239            for (JetFile file : files) {
240                if (file.isScript()) return;
241            }
242    
243            DescriptorSerializer serializer = new DescriptorSerializer(new JavaSerializerExtension(bindings));
244            Collection<PackageFragmentDescriptor> packageFragments = compiledPackageFragment == null
245                                                                     ? Collections.singleton(packageFragment)
246                                                                     : Arrays.asList(packageFragment, compiledPackageFragment);
247            ProtoBuf.Package packageProto = serializer.packageProto(packageFragments).build();
248    
249            if (packageProto.getMemberCount() == 0) return;
250    
251            PackageData data = new PackageData(createNameResolver(serializer.getNameTable()), packageProto);
252    
253            AnnotationVisitor av =
254                    v.getClassBuilder().newAnnotation(asmDescByFqNameWithoutInnerClasses(JvmAnnotationNames.KOTLIN_PACKAGE), true);
255            av.visit(JvmAnnotationNames.ABI_VERSION_FIELD_NAME, JvmAbi.VERSION);
256            AnnotationVisitor array = av.visitArray(JvmAnnotationNames.DATA_FIELD_NAME);
257            for (String string : BitEncoding.encodeBytes(data.toBytes())) {
258                array.visit(null, string);
259            }
260            array.visitEnd();
261            av.visitEnd();
262        }
263    
264        @Nullable
265        private ClassBuilder generate(@NotNull JetFile file, @NotNull Map<CallableMemberDescriptor, Runnable> generateCallableMemberTasks) {
266            boolean generateSrcClass = false;
267            Type packagePartType = getPackagePartType(getPackageClassFqName(packageFragment.getFqName()), file.getVirtualFile());
268            PackageContext packagePartContext = CodegenContext.STATIC.intoPackagePart(packageFragment, packagePartType);
269    
270            for (JetDeclaration declaration : file.getDeclarations()) {
271                if (declaration instanceof JetProperty || declaration instanceof JetNamedFunction) {
272                    generateSrcClass = true;
273                }
274                else if (declaration instanceof JetClassOrObject) {
275                    JetClassOrObject classOrObject = (JetClassOrObject) declaration;
276                    if (state.getGenerateDeclaredClassFilter().shouldProcess(classOrObject)) {
277                        generateClassOrObject(classOrObject);
278                    }
279                }
280                else if (declaration instanceof JetScript) {
281                   // SCRIPT: generate script code, should be separate execution branch
282                   ScriptCodegen.createScriptCodegen((JetScript) declaration, state, packagePartContext).generate();
283                }
284            }
285    
286            if (!generateSrcClass) return null;
287    
288            ClassBuilder builder = state.getFactory().forPackagePart(packagePartType, file);
289    
290            new PackagePartCodegen(builder, file, packagePartType, packagePartContext, state).generate();
291    
292            FieldOwnerContext packageFacade = CodegenContext.STATIC.intoPackageFacade(packagePartType, packageFragment);
293    
294            final MemberCodegen<?> memberCodegen = getMemberCodegen(packageFacade);
295    
296            for (final JetDeclaration declaration : file.getDeclarations()) {
297                if (declaration instanceof JetNamedFunction || declaration instanceof JetProperty) {
298                    DeclarationDescriptor descriptor = state.getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration);
299                    assert descriptor instanceof CallableMemberDescriptor :
300                            "Expected callable member, was " + descriptor + " for " + declaration.getText();
301                    generateCallableMemberTasks.put(
302                            (CallableMemberDescriptor) descriptor,
303                            new Runnable() {
304                                @Override
305                                public void run() {
306                                    memberCodegen.genFunctionOrProperty((JetTypeParameterListOwner) declaration, v.getClassBuilder());
307                                }
308                            }
309                    );
310                }
311            }
312    
313            return builder;
314        }
315    
316        //TODO: FIX: Default method generated at facade without delegation
317        private MemberCodegen<?> getMemberCodegen(@NotNull FieldOwnerContext packageFacade) {
318            return new MemberCodegen<JetFile>(state, null, packageFacade, null, null) {
319                @Override
320                protected void generateDeclaration() {
321                    throw new UnsupportedOperationException();
322                }
323    
324                @Override
325                protected void generateBody() {
326                    throw new UnsupportedOperationException();
327                }
328    
329                @Override
330                protected void generateKotlinAnnotation() {
331                    throw new UnsupportedOperationException();
332                }
333            };
334        }
335    
336        @NotNull
337        private PackageFragmentDescriptor getOnlyPackageFragment() {
338            SmartList<PackageFragmentDescriptor> fragments = new SmartList<PackageFragmentDescriptor>();
339            for (JetFile file : files) {
340                PackageFragmentDescriptor fragment = state.getBindingContext().get(BindingContext.FILE_TO_PACKAGE_FRAGMENT, file);
341                assert fragment != null : "package fragment is null for " + file;
342    
343                if (!fragments.contains(fragment)) {
344                    fragments.add(fragment);
345                }
346            }
347            if (fragments.size() != 1) {
348                throw new IllegalStateException("More than one package fragment, files: " + files + " | fragments: " + fragments);
349            }
350            return fragments.get(0);
351        }
352    
353        public void generateClassOrObject(@NotNull JetClassOrObject classOrObject) {
354            JetFile file = classOrObject.getContainingJetFile();
355            Type packagePartType = getPackagePartType(getPackageClassFqName(packageFragment.getFqName()), file.getVirtualFile());
356            CodegenContext context = CodegenContext.STATIC.intoPackagePart(packageFragment, packagePartType);
357            MemberCodegen.genClassOrObject(context, classOrObject, state, null);
358        }
359    
360        /**
361         * @param packageFiles all files should have same package name
362         * @return
363         */
364        public static boolean shouldGeneratePackageClass(@NotNull Collection<JetFile> packageFiles) {
365            for (JetFile file : packageFiles) {
366                for (JetDeclaration declaration : file.getDeclarations()) {
367                    if (declaration instanceof JetProperty || declaration instanceof JetNamedFunction) {
368                        return true;
369                    }
370                }
371            }
372    
373            return false;
374        }
375    
376        public void done() {
377            v.done();
378        }
379    
380        @NotNull
381        public static Type getPackagePartType(@NotNull FqName facadeFqName, @NotNull VirtualFile file) {
382            String fileName = FileUtil.getNameWithoutExtension(PathUtil.getFileName(file.getName()));
383    
384            // path hashCode to prevent same name / different path collision
385            String srcName = facadeFqName.shortName().asString() + "-" + replaceSpecialSymbols(fileName) + "-" + Integer.toHexString(
386                    JvmCodegenUtil.getPathHashCode(file));
387    
388            return asmTypeByFqNameWithoutInnerClasses(facadeFqName.parent().child(Name.identifier(srcName)));
389        }
390    
391        @NotNull
392        private static String replaceSpecialSymbols(@NotNull String str) {
393            return str.replace('.', '_');
394        }
395    
396        @NotNull
397        public static String getPackagePartInternalName(@NotNull JetFile file) {
398            FqName packageFqName = file.getPackageFqName();
399            return getPackagePartType(getPackageClassFqName(packageFqName), file.getVirtualFile()).getInternalName();
400        }
401    
402        @NotNull
403        public static String getPackagePartInternalName(@NotNull DeserializedCallableMemberDescriptor callable) {
404            FqName packageFqName = ((PackageFragmentDescriptor) callable.getContainingDeclaration()).getFqName();
405            FqName packagePartFqName = packageFqName.child(BaseDescriptorDeserializer.getPackagePartClassName(callable));
406            return JvmClassName.byFqNameWithoutInnerClasses(packagePartFqName).getInternalName();
407        }
408    }