All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.jet.codegen.ClassFileFactory Maven / Gradle / Ivy

/*
 * Copyright 2010-2013 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jetbrains.jet.codegen;

import com.google.common.collect.Lists;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.OutputFile;
import org.jetbrains.jet.OutputFileCollection;
import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.org.objectweb.asm.Type;

import java.io.File;
import java.util.*;

import static org.jetbrains.jet.codegen.AsmUtil.asmTypeByFqNameWithoutInnerClasses;
import static org.jetbrains.jet.codegen.AsmUtil.isPrimitive;
import static org.jetbrains.jet.lang.resolve.java.PackageClassUtils.getPackageClassFqName;

public class ClassFileFactory implements OutputFileCollection {
    private final GenerationState state;
    private final ClassBuilderFactory builderFactory;
    private final Map package2codegen = new HashMap();
    private final Map generators = new LinkedHashMap();

    private boolean isDone = false;

    public ClassFileFactory(@NotNull GenerationState state, @NotNull ClassBuilderFactory builderFactory) {
        this.state = state;
        this.builderFactory = builderFactory;
    }

    @NotNull
    ClassBuilder newVisitor(@NotNull Type asmType, @NotNull PsiFile sourceFile) {
        return newVisitor(asmType, Collections.singletonList(sourceFile));
    }

    @NotNull
    private ClassBuilder newVisitor(@NotNull Type asmType, @NotNull Collection sourceFiles) {
        String outputFilePath = asmType.getInternalName() + ".class";
        state.getProgress().reportOutput(toIoFilesIgnoringNonPhysical(sourceFiles), new File(outputFilePath));
        ClassBuilder answer = builderFactory.newClassBuilder();
        generators.put(outputFilePath, new ClassBuilderAndSourceFileList(answer, sourceFiles));
        return answer;
    }

    void done() {
        if (!isDone) {
            isDone = true;
            for (PackageCodegen codegen : package2codegen.values()) {
                codegen.done();
            }
        }
    }

    @NotNull
    @Override
    public List asList() {
        done();
        return ContainerUtil.map(generators.keySet(), new Function() {
            @Override
            public OutputFile fun(String relativeClassFilePath) {
                return new OutputClassFile(relativeClassFilePath);
            }
        });
    }

    @Override
    @Nullable
    public OutputFile get(@NotNull String relativePath) {
        if (generators.containsKey(relativePath)) return new OutputClassFile(relativePath);

        return null;
    }

    public String createText() {
        StringBuilder answer = new StringBuilder();

        for (OutputFile file : asList()) {
            answer.append("@").append(file.getRelativePath()).append('\n');
            answer.append(file.asText());
        }

        return answer.toString();
    }

    public PackageCodegen forPackage(final FqName fqName, final Collection files) {
        assert !isDone : "Already done!";
        PackageCodegen codegen = package2codegen.get(fqName);
        if (codegen == null) {
            ClassBuilderOnDemand onDemand = new ClassBuilderOnDemand() {
                @NotNull
                @Override
                protected ClassBuilder createClassBuilder() {
                    return newVisitor(asmTypeByFqNameWithoutInnerClasses(getPackageClassFqName(fqName)), files);
                }
            };
            codegen = new PackageCodegen(onDemand, fqName, state, files);
            package2codegen.put(fqName, codegen);
        }

        return codegen;
    }

    public ClassBuilder forClassImplementation(ClassDescriptor aClass, PsiFile sourceFile) {
        Type type = state.getTypeMapper().mapClass(aClass);
        if (isPrimitive(type)) {
            throw new IllegalStateException("Codegen for primitive type is not possible: " + aClass);
        }
        return newVisitor(type, sourceFile);
    }

    public ClassBuilder forLambdaInlining(Type lambdaType, PsiFile sourceFile) {
        if (isPrimitive(lambdaType)) {
            throw new IllegalStateException("Codegen for primitive type is not possible: " + lambdaType);
        }
        return newVisitor(lambdaType, sourceFile);
    }

    @NotNull
    public ClassBuilder forPackagePart(@NotNull Type asmType, @NotNull PsiFile sourceFile) {
        return newVisitor(asmType, sourceFile);
    }

    @NotNull
    public ClassBuilder forTraitImplementation(@NotNull ClassDescriptor aClass, @NotNull GenerationState state, @NotNull PsiFile file) {
        return newVisitor(state.getTypeMapper().mapTraitImpl(aClass), file);
    }

    private static Collection toIoFilesIgnoringNonPhysical(Collection psiFiles) {
        List result = Lists.newArrayList();
        for (PsiFile psiFile : psiFiles) {
            VirtualFile virtualFile = psiFile.getVirtualFile();
            // We ignore non-physical files here, because this code is needed to tell the make what inputs affect which outputs
            // a non-physical file cannot be processed by make
            if (virtualFile != null) {
                result.add(new File(virtualFile.getPath()));
            }
        }
        return result;
    }

    private final class OutputClassFile implements OutputFile {
        final String relativeClassFilePath;

        OutputClassFile(String relativeClassFilePath) {
            this.relativeClassFilePath = relativeClassFilePath;
        }

        @NotNull
        @Override
        public String getRelativePath() {
            return relativeClassFilePath;
        }

        @NotNull
        @Override
        public List getSourceFiles() {
            ClassBuilderAndSourceFileList pair = generators.get(relativeClassFilePath);
            if (pair == null) {
                throw new IllegalStateException("No record for binary file " + relativeClassFilePath);
            }

            return ContainerUtil.mapNotNull(
                    pair.sourceFiles,
                    new Function() {
                        @Override
                        public File fun(PsiFile file) {
                            VirtualFile virtualFile = file.getVirtualFile();
                            if (virtualFile == null) return null;

                            return VfsUtilCore.virtualToIoFile(virtualFile);
                        }
                    }
            );
        }

        @NotNull
        @Override
        public byte[] asByteArray() {
            return builderFactory.asBytes(generators.get(relativeClassFilePath).classBuilder);
        }

        @NotNull
        @Override
        public String asText() {
            return builderFactory.asText(generators.get(relativeClassFilePath).classBuilder);
        }

        @NotNull
        @Override
        public String toString() {
            return getRelativePath() + " (compiled from " + getSourceFiles() + ")";
        }
    }

    private static final class ClassBuilderAndSourceFileList {
        private final ClassBuilder classBuilder;
        private final Collection sourceFiles;

        private ClassBuilderAndSourceFileList(ClassBuilder classBuilder, Collection sourceFiles) {
            this.classBuilder = classBuilder;
            this.sourceFiles = sourceFiles;
        }
    }

    public void removeInlinedClasses(Set classNamesToRemove) {
        for (String classInternalName : classNamesToRemove) {
            generators.remove(classInternalName + ".class");
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy