Package com.redhat.ceylon.compiler.java.tools

Source Code of com.redhat.ceylon.compiler.java.tools.LanguageCompiler$CompilerDelegate

/*
* Copyright (c) 1999, 2006, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.  Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

/*
* Copyright Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the authors tag. All rights reserved.
*/

package com.redhat.ceylon.compiler.java.tools;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Set;

import javax.annotation.processing.Processor;
import javax.tools.FileObject;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;
import javax.tools.StandardLocation;

import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CommonTokenStream;

import com.redhat.ceylon.cmr.util.JarUtils;
import com.redhat.ceylon.common.FileUtil;
import com.redhat.ceylon.compiler.java.codegen.CeylonClassWriter;
import com.redhat.ceylon.compiler.java.codegen.CeylonCompilationUnit;
import com.redhat.ceylon.compiler.java.codegen.CeylonFileObject;
import com.redhat.ceylon.compiler.java.codegen.CeylonTransformer;
import com.redhat.ceylon.compiler.java.loader.CeylonEnter;
import com.redhat.ceylon.compiler.java.loader.CeylonModelLoader;
import com.redhat.ceylon.compiler.java.util.Timer;
import com.redhat.ceylon.compiler.java.util.Util;
import com.redhat.ceylon.compiler.loader.AbstractModelLoader;
import com.redhat.ceylon.compiler.typechecker.analyzer.ModuleManager;
import com.redhat.ceylon.compiler.typechecker.analyzer.Warning;
import com.redhat.ceylon.compiler.typechecker.context.PhasedUnit;
import com.redhat.ceylon.compiler.typechecker.context.PhasedUnits;
import com.redhat.ceylon.compiler.typechecker.io.VFS;
import com.redhat.ceylon.compiler.typechecker.io.VirtualFile;
import com.redhat.ceylon.compiler.typechecker.model.Module;
import com.redhat.ceylon.compiler.typechecker.model.Modules;
import com.redhat.ceylon.compiler.typechecker.model.Package;
import com.redhat.ceylon.compiler.typechecker.parser.CeylonLexer;
import com.redhat.ceylon.compiler.typechecker.parser.CeylonParser;
import com.redhat.ceylon.compiler.typechecker.parser.LexError;
import com.redhat.ceylon.compiler.typechecker.parser.ParseError;
import com.redhat.ceylon.compiler.typechecker.parser.RecognitionError;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.CompilationUnit;
import com.redhat.ceylon.compiler.typechecker.util.ModuleManagerFactory;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.code.Symbol.CompletionFailure;
import com.sun.tools.javac.comp.AttrContext;
import com.sun.tools.javac.comp.Env;
import com.sun.tools.javac.file.JavacFileManager;
import com.sun.tools.javac.jvm.ClassWriter;
import com.sun.tools.javac.main.JavaCompiler;
import com.sun.tools.javac.main.OptionName;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.TreeInfo;
import com.sun.tools.javac.util.Abort;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Convert;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Log;
import com.sun.tools.javac.util.Options;
import com.sun.tools.javac.util.Pair;
import com.sun.tools.javac.util.Position;
import com.sun.tools.javac.util.Position.LineMap;
import com.sun.tools.javac.util.SourceLanguage;
import com.sun.tools.javac.util.SourceLanguage.Language;

public class LanguageCompiler extends JavaCompiler {

    /** The context key for the phasedUnits. */
    protected static final Context.Key<PhasedUnits> phasedUnitsKey = new Context.Key<PhasedUnits>();
    public static final Context.Key<CompilerDelegate> compilerDelegateKey = new Context.Key<CompilerDelegate>();

    /** The context key for the ceylon context. */
    public static final Context.Key<com.redhat.ceylon.compiler.typechecker.context.Context> ceylonContextKey = new Context.Key<com.redhat.ceylon.compiler.typechecker.context.Context>();

    private final CeylonTransformer gen;
    private final PhasedUnits phasedUnits;
    private final CompilerDelegate compilerDelegate;
    private final com.redhat.ceylon.compiler.typechecker.context.Context ceylonContext;
    private final VFS vfs;

  private AbstractModelLoader modelLoader;

    private CeylonEnter ceylonEnter;

    private Options options;
   
    private Timer timer;
    private boolean isBootstrap;
    private boolean addedDefaultModuleToClassPath;
    private boolean treatLikelyBugsAsErrors = false;
    private Set<Module> modulesLoadedFromSource = new HashSet<Module>();
    private List<JavaFileObject> resourceFileObjects;
    private Map<String,CeylonFileObject> moduleNamesToFileObjects = new HashMap<String,CeylonFileObject>();
    private SourceLanguage sourceLanguage;

    /** Get the PhasedUnits instance for this context. */
    public static PhasedUnits getPhasedUnitsInstance(final Context context) {
        PhasedUnits phasedUnits = context.get(phasedUnitsKey);
        if (phasedUnits == null) {
            com.redhat.ceylon.compiler.typechecker.context.Context ceylonContext = getCeylonContextInstance(context);
            phasedUnits = new PhasedUnits(ceylonContext, new ModuleManagerFactory(){
                @Override
                public ModuleManager createModuleManager(com.redhat.ceylon.compiler.typechecker.context.Context ceylonContext) {
                    CompilerDelegate phasedUnitsManager = getCompilerDelegate(context);
                    return phasedUnitsManager.getModuleManager();
                }
            });
            context.put(phasedUnitsKey, phasedUnits);
        }
        return phasedUnits;
    }

    public static CompilerDelegate getCompilerDelegate(final Context context) {
        CompilerDelegate compilerDelegate = context.get(compilerDelegateKey);
        if (compilerDelegate == null) {
            return new CeyloncCompilerDelegate(context);
        }
        return compilerDelegate;
    }
   
    /** Get the Ceylon context instance for this context. */
    public static com.redhat.ceylon.compiler.typechecker.context.Context getCeylonContextInstance(Context context) {
        com.redhat.ceylon.compiler.typechecker.context.Context ceylonContext = context.get(ceylonContextKey);
        if (ceylonContext == null) {
            CeyloncFileManager fileManager = (CeyloncFileManager) context.get(JavaFileManager.class);
            VFS vfs = new VFS();
            ceylonContext = new com.redhat.ceylon.compiler.typechecker.context.Context(fileManager.getRepositoryManager(), vfs);
            context.put(ceylonContextKey, ceylonContext);
        }
        return ceylonContext;
    }

    /** Get the JavaCompiler instance for this context. */
    public static JavaCompiler instance(Context context) {
        Options options = Options.instance(context);
        options.put("-Xprefer", "source");
        // make sure it's registered
        CeylonLog.instance(context);
        CeylonEnter.instance(context);
        CeylonClassWriter.instance(context);
        JavaCompiler instance = context.get(compilerKey);
        if (instance == null)
            instance = new LanguageCompiler(context);
        return instance;
    }

    public LanguageCompiler(Context context) {
        super(context);
        ceylonContext = getCeylonContextInstance(context);
        vfs = ceylonContext.getVfs();
        compilerDelegate = getCompilerDelegate(context);
        phasedUnits = getPhasedUnitsInstance(context);
        try {
            gen = CeylonTransformer.getInstance(context);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        modelLoader = CeylonModelLoader.instance(context);
        ceylonEnter = CeylonEnter.instance(context);
        options = Options.instance(context);
        isBootstrap = options.get(OptionName.BOOTSTRAPCEYLON) != null;
        timer = Timer.instance(context);
        sourceLanguage = SourceLanguage.instance(context);
    }

    @Override
    public void compile(List<JavaFileObject> fileObjects,
            List<String> classnames,
            Iterable<? extends Processor> processors)
    {
        // Now we first split the files into sources/modules and resources
        List<JavaFileObject> sourceFiles = List.nil();
        List<JavaFileObject> resourceFiles = List.nil();
        for (JavaFileObject fo : fileObjects) {
            if (isResource(fo)) {
                resourceFiles = resourceFiles.append(fo);
            } else {
                sourceFiles = sourceFiles.append(fo);
            }
        }
        this.resourceFileObjects = resourceFiles;
        // Add any module files for the resources (if needed)
        sourceFiles = addModuleDescriptors(sourceFiles, resourceFiles);
        // And then continue to the compilation of the source files
        super.compile(sourceFiles, classnames, processors);
    }

    private boolean isResource(JavaFileObject fo) {
        // make sure we get a proper normalized abslute path
        String fileName = FileUtil.absoluteFile(new File(fo.toUri().getPath())).getPath();
        // now see if it's in any of the resource paths
        JavacFileManager dfm = (JavacFileManager) fileManager;
        for (File dir : dfm.getLocation(CeylonLocation.RESOURCE_PATH)) {
            String prefix = FileUtil.absoluteFile(dir).getPath();
            if (fileName.startsWith(prefix)) {
                return true;
            }
        }
        return false;
    }
   
    // This is a bit of a hack, but if we got passed a list of resources
    // without any accompaning source files we'll not be able to determine
    // the module to which the resource files belong. So to try to fix that
    // we see if a module file exists in the source folders and add it to
    // the list of source files
    private List<JavaFileObject> addModuleDescriptors(List<JavaFileObject> sourceFiles, List<JavaFileObject> resourceFiles) {
        List<JavaFileObject> result = sourceFiles;
        JavacFileManager dfm = (JavacFileManager) fileManager;
        for (JavaFileObject fo : resourceFiles) {
            String resName = JarUtils.toPlatformIndependentPath(dfm.getLocation(CeylonLocation.RESOURCE_PATH), fo.getName());
            JavaFileObject moduleFile = findModuleDescriptorForFile(new File(resName));
            if (moduleFile != null && !result.contains(moduleFile)) {
                result = result.append(moduleFile);
            }
        }
        return result;
    }

    private JavaFileObject findModuleDescriptorForFile(File file) {
        JavacFileManager dfm = (JavacFileManager) fileManager;
        File dir = file.getParentFile();
        while (dir != null) {
            try {
                String name = dir.getPath() + "/module";
                JavaFileObject fo = dfm.getJavaFileForInput(StandardLocation.SOURCE_PATH, name, Kind.SOURCE);
                if (fo != null) {
                    return fo;
                }
            } catch (IOException e) {
                // Ignore
            }
            dir = dir.getParentFile();
        }
        return null;
    }

    @Override
    public void close(boolean disposeNames) {
        if (resourceFileObjects != null) {
            addResources();
            resourceFileObjects = null;
        }
        super.close(disposeNames);
    }

    private void addResources() throws Abort {
       HashSet<String> written = new HashSet<String>();
        try {
            for (JavaFileObject fo : resourceFileObjects) {
                CeyloncFileManager dfm = (CeyloncFileManager) fileManager;
                String jarFileName = JarUtils.toPlatformIndependentPath(dfm.getLocation(CeylonLocation.RESOURCE_PATH), fo.getName());
                if (!written.contains(jarFileName)) {
                    dfm.setModule(modelLoader.findModuleForFile(new File(jarFileName)));
                    FileObject outFile = dfm.getFileForOutput(StandardLocation.CLASS_OUTPUT, "", jarFileName, null);
                    OutputStream out = outFile.openOutputStream();
                    try {
                        InputStream in = new FileInputStream(new File(fo.getName()));
                        try {
                            JarUtils.copy(in, out);
                        } finally {
                            in.close();
                        }
                    } finally {
                        out.close();
                    }
                    written.add(jarFileName);
                }
            }
        } catch (IOException ex) {
            throw new Abort(ex);
        }
    }
   
    /**
     * Parse contents of file.
     * @param filename The name of the file to be parsed.
     */
    public JCTree.JCCompilationUnit parse(JavaFileObject filename) {
        JavaFileObject prev = log.useSource(filename);
        try {
            JCTree.JCCompilationUnit t;
            if (filename.getName().endsWith(".java")) {

                t = parse(filename, readSource(filename));
            } else {
                t = ceylonParse(filename, readSource(filename));
            }
            if (t.endPositions != null)
                log.setEndPosTable(filename, t.endPositions);
            return t;
        } finally {
            log.useSource(prev);
        }
    }

    protected JCCompilationUnit parse(JavaFileObject filename, CharSequence readSource) {
        // FIXME
        if (filename instanceof CeylonFileObject)
            return ceylonParse(filename, readSource);
        else
            return super.parse(filename, readSource);
    }


    public static interface CompilerDelegate {
       
        ModuleManager getModuleManager();
        PhasedUnit getExternalSourcePhasedUnit(VirtualFile srcDir, VirtualFile file);
        void typeCheck(java.util.List<PhasedUnit> listOfUnits);
        void visitModules(PhasedUnits phasedUnits);
        void loadStandardModules(AbstractModelLoader modelLoader);
        void setupSourceFileObjects(List<JCCompilationUnit> trees, AbstractModelLoader modelLoader);
        void resolveModuleDependencies(PhasedUnits phasedUnits);
        void loadPackageDescriptors(AbstractModelLoader modelLoader);
    }
   
    private static class RunTwiceException extends RuntimeException {

        public RunTwiceException(String string) {
            super(string);
        }
       
    }
   
    private JCCompilationUnit ceylonParse(JavaFileObject filename, CharSequence readSource) {
        if(ceylonEnter.hasRun())
            throw new RunTwiceException("Trying to load new source file after CeylonEnter has been called: "+filename);
        try {
            ModuleManager moduleManager = phasedUnits.getModuleManager();
            File sourceFile = new File(filename.getName());
            // FIXME: temporary solution
            VirtualFile file = vfs.getFromFile(sourceFile);
            VirtualFile srcDir = vfs.getFromFile(getSrcDir(sourceFile));
           
            String source = readSource.toString();
            char[] chars = source.toCharArray();
            LineMap map = Position.makeLineMap(chars, chars.length, false);
           
            PhasedUnit phasedUnit = null;
           
            PhasedUnit externalPhasedUnit = compilerDelegate.getExternalSourcePhasedUnit(srcDir, file);
           
            String suppressWarnings = options.get(OptionName.CEYLONSUPPRESSWARNINGS);
            final EnumSet<Warning> suppressedWarnings;
            if (suppressWarnings != null) {
                if (suppressWarnings.trim().isEmpty()) {
                    suppressedWarnings = EnumSet.allOf(Warning.class);
                } else {
                    suppressedWarnings = EnumSet.noneOf(Warning.class);
                    for (String name : suppressWarnings.trim().split(" *, *")) {
                        suppressedWarnings.add(Warning.valueOf(name));
                    }
                }
            } else {
                suppressedWarnings = EnumSet.noneOf(Warning.class);
            }
           
            if (externalPhasedUnit != null) {
                phasedUnit = new CeylonPhasedUnit(externalPhasedUnit, filename, map);
                phasedUnit.setSuppressedWarnings(suppressedWarnings);
                phasedUnits.addPhasedUnit(externalPhasedUnit.getUnitFile(), phasedUnit);
                gen.setMap(map);
               
                String pkgName = phasedUnit.getPackage().getQualifiedNameString();
                if ("".equals(pkgName)) {
                    pkgName = null;
                }
                return gen.makeJCCompilationUnitPlaceholder(phasedUnit.getCompilationUnit(), filename, pkgName, phasedUnit);
            }
            if (phasedUnit == null) {
                ANTLRStringStream input = new NewlineFixingStringStream(source);
                CeylonLexer lexer = new CeylonLexer(input);

                CommonTokenStream tokens = new CommonTokenStream(lexer);

                CeylonParser parser = new CeylonParser(tokens);
                CompilationUnit cu = parser.compilationUnit();

                java.util.List<LexError> lexerErrors = lexer.getErrors();
                for (LexError le : lexerErrors) {
                    printError(le, le.getMessage(), "ceylon.lexer", map);
                }

                java.util.List<ParseError> parserErrors = parser.getErrors();
                for (ParseError pe : parserErrors) {
                    printError(pe, pe.getMessage(), "ceylon.parser", map);
                }

                // if we continue and it's not a descriptor, we don't care about errors
                if ((options.get(OptionName.CEYLONCONTINUE) != null
                        && !ModuleManager.MODULE_FILE.equals(sourceFile.getName())
                        && !ModuleManager.PACKAGE_FILE.equals(sourceFile.getName()))
                        // otherwise we care about errors
                        || (lexerErrors.size() == 0
                            && parserErrors.size() == 0)) {
                    // FIXME: this is bad in many ways
                    String pkgName = getPackage(filename);
                    // make a Package with no module yet, we will resolve them later
                    /*
                     * Stef: see javadoc for findOrCreateModulelessPackage() for why this is here.
                     */
                    com.redhat.ceylon.compiler.typechecker.model.Package p = modelLoader.findOrCreateModulelessPackage(pkgName == null ? "" : pkgName);
                    phasedUnit = new CeylonPhasedUnit(file, srcDir, cu, p, moduleManager, ceylonContext, filename, map);
                    phasedUnit.setSuppressedWarnings(suppressedWarnings);
                    phasedUnits.addPhasedUnit(file, phasedUnit);
                    gen.setMap(map);

                    return gen.makeJCCompilationUnitPlaceholder(cu, filename, pkgName, phasedUnit);
                }
            }
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        JCCompilationUnit result = make.TopLevel(List.<JCAnnotation> nil(), null, List.<JCTree> of(make.Erroneous()));
        result.sourcefile = filename;
        return result;
    }

    @Override
    public List<JCCompilationUnit> parseFiles(Iterable<JavaFileObject> fileObjects) {
        timer.startTask("parse");
        /*
         * Stef: see javadoc for fixDefaultPackage() for why this is here.
         */
        modelLoader.fixDefaultPackage();
        List<JCCompilationUnit> trees = super.parseFiles(fileObjects);
        timer.startTask("loadCompiledModules");
        LinkedList<JCCompilationUnit> moduleTrees = new LinkedList<JCCompilationUnit>();
        // now load modules and associate their moduleless packages with the corresponding modules
        loadCompiledModules(trees, moduleTrees);
        for (JCCompilationUnit moduleTree : moduleTrees) {
            trees = trees.append(moduleTree);
        }
        /*
         * Stef: see javadoc for cacheModulelessPackages() for why this is here.
         */
        modelLoader.cacheModulelessPackages();
        timer.endTask();
        return trees;
    }

    private void loadCompiledModules(List<JCCompilationUnit> trees, LinkedList<JCCompilationUnit> moduleTrees) {
        compilerDelegate.visitModules(phasedUnits);
        Modules modules = ceylonContext.getModules();
        // now make sure the phase units have their modules and packages set correctly
        for (PhasedUnit pu : phasedUnits.getPhasedUnits()) {
            Package pkg = pu.getPackage();
            loadModuleFromSource(pkg, modules, moduleTrees, trees);
        }
        // also make sure we have packages and modules set up for every Java file we compile
        for(JCCompilationUnit cu : trees){
            // skip Ceylon CUs
            if(cu instanceof CeylonCompilationUnit)
                continue;
            String packageName = "";
            if(cu.pid != null)
                packageName = TreeInfo.fullName(cu.pid).toString();
            /*
             * Stef: see javadoc for findOrCreateModulelessPackage() for why this is here.
             */
            Package pkg = modelLoader.findOrCreateModulelessPackage(packageName);
            loadModuleFromSource(pkg, modules, moduleTrees, trees);
        }
        for(PhasedUnit phasedUnit : phasedUnits.getPhasedUnits()){
            for(Tree.ModuleDescriptor modDescr : phasedUnit.getCompilationUnit().getModuleDescriptors()){
                String name = phasedUnit.getPackage().getNameAsString();
                CeylonPhasedUnit cpu = (CeylonPhasedUnit) phasedUnit;
                CeylonFileObject cfo = (CeylonFileObject) cpu.getFileObject();
                moduleNamesToFileObjects .put(name, cfo);
            }
        }
    }

    private void loadModuleFromSource(Package pkg, Modules modules, LinkedList<JCCompilationUnit> moduleTrees, List<JCCompilationUnit> parsedTrees) {
        // skip it if we already resolved the package
        if(pkg.getModule() != null){
            // make sure the default module is always added to the classpath, it will be the only one to have a module
            if(!addedDefaultModuleToClassPath && pkg.getModule().isDefault()){
                addedDefaultModuleToClassPath = true;
                ceylonEnter.addOutputModuleToClassPath(pkg.getModule());
            }
            return;
        }
        String pkgName = pkg.getQualifiedNameString();
        Module module = null;
        // do we have a module for this package?
        // FIXME: is this true? what if we have a module.ceylon at toplevel?
        if(pkgName.isEmpty())
            module = modules.getDefaultModule();
        else{
            for(Module m : modulesLoadedFromSource){
                if(Util.isSubPackage(m.getNameAsString(), pkgName)){
                    module = m;
                    break;
                }
            }
            if(module == null){
                module = loadModuleFromSource(pkgName, moduleTrees, parsedTrees);
            }
            else if (! module.isAvailable()) {
                loadModuleFromSource(pkgName, moduleTrees, parsedTrees);
            }

            if(module == null){
                // no declaration for it, must be the default module, unless we're bootstrapping the language module,
                // because we have some com.redhat.ceylon packages that must go in the language module
                if(isBootstrap)
                    module = modules.getLanguageModule();
                else
                    module = modules.getDefaultModule();
            }
        }
        // bind module and package together
        pkg.setModule(module);
        if (!module.getPackages().contains(pkg)) {
            module.getPackages().add(pkg);
        }
        // automatically add this module's jar to the classpath if it exists
        ceylonEnter.addOutputModuleToClassPath(module);
    }

    private Module loadModuleFromSource(String pkgName, LinkedList<JCCompilationUnit> moduleTrees, List<JCCompilationUnit> parsedTrees) {
        if(pkgName.isEmpty())
            return null;
        String moduleClassName = pkgName + ".module";
        JavaFileObject fileObject;
        try {
            if(options.get(OptionName.VERBOSE) != null){
                Log.printLines(log.noticeWriter, "[Trying to load module "+moduleClassName+"]");
            }
            fileObject = fileManager.getJavaFileForInput(StandardLocation.SOURCE_PATH, moduleClassName, Kind.SOURCE);
            if(options.get(OptionName.VERBOSE) != null){
                Log.printLines(log.noticeWriter, "[Got file object: "+fileObject+"]");
            }
        } catch (IOException e) {
            e.printStackTrace();
            return loadModuleFromSource(getParentPackage(pkgName), moduleTrees, parsedTrees);
        }
        if(fileObject != null){
            // first make sure we're not already compiling it: this can happen if we have several versions of the
            // same module already loaded: we will get one which isn't the one we compile, but that's not the one
            // we really want to compile.
            for(JCCompilationUnit parsedTree : parsedTrees){
                if(parsedTree.sourcefile.equals(fileObject)
                        && parsedTree instanceof CeylonCompilationUnit){
                    // same file! we already parsed it, let's return this one's module
                    PhasedUnit phasedUnit = ((CeylonCompilationUnit)parsedTree).phasedUnit;
                    // the module visitor does load the module but does not set the unit's package module
                    if(phasedUnit.getPackage().getModule() == null){
                        // so find the module it created
                        for(Module mod : ceylonContext.getModules().getListOfModules()){
                            // we recognise it with the unit
                            if(mod.getUnit() == phasedUnit.getUnit()){
                                // set the package's module
                                Package pkg = phasedUnit.getPackage();
                                pkg.setModule(mod);
                                mod.getPackages().add(pkg);
                                modulesLoadedFromSource.add(mod);
                                break;
                            }
                        }
                    }
                    // now return it
                    return phasedUnit.getPackage().getModule();
                }
            }
            JCCompilationUnit javaCompilationUnit = parse(fileObject);
            Module module;
            if(javaCompilationUnit instanceof CeylonCompilationUnit){
                CeylonCompilationUnit ceylonCompilationUnit = (CeylonCompilationUnit) javaCompilationUnit;
                moduleTrees.add(ceylonCompilationUnit);
                // parse the module info from there
                module = ceylonCompilationUnit.phasedUnit.visitSrcModulePhase();
                ceylonCompilationUnit.phasedUnit.visitRemainingModulePhase();
                // now set the module
                if(module != null){
                    ceylonCompilationUnit.phasedUnit.getPackage().setModule(module);
                }
            }else{
                // there was a syntax error in the module descriptor, make a pretend module so that we can
                // correctly mark all declarations as part of that module, but we won't generate any code
                // for it
                ModuleManager moduleManager = phasedUnits.getModuleManager();
                module = moduleManager.getOrCreateModule(Arrays.asList(pkgName.split("\\.")), "bogus");
            }
            // now remember it
            if(module != null){
                modulesLoadedFromSource.add(module);
                return module;
            }
        }
        return loadModuleFromSource(getParentPackage(pkgName), moduleTrees, parsedTrees);
    }

    private String getParentPackage(String pkgName) {
        int lastDot = pkgName.lastIndexOf(".");
        if(lastDot == -1)
            return "";
        return pkgName.substring(0, lastDot);
    }

    // FIXME: this function is terrible, possibly refactor it with getPackage?
    private File getSrcDir(File sourceFile) throws IOException {
        Iterable<? extends File> prefixes = ((JavacFileManager)fileManager).getLocation(StandardLocation.SOURCE_PATH);
        File srcDirFile = FileUtil.selectPath(prefixes, sourceFile.getPath());
        if (srcDirFile != null) {
            return srcDirFile;
        } else {
            // This error should have been caught by the tool chain
            throw new RuntimeException(sourceFile.getPath() + " is not in the current source path");
        }
    }

    private String getPackage(JavaFileObject file) throws IOException{
        Iterable<? extends File> prefixes = ((JavacFileManager)fileManager).getLocation(StandardLocation.SOURCE_PATH);

      // Figure out the package name by stripping the "-src" prefix and
      // extracting
      // the package part of the fullname.
       
        String filePath = file.toUri().getPath();
        // go absolute
        filePath = new File(filePath).getCanonicalPath();
       
        int srcDirLength = 0;
        for (File prefixFile : prefixes) {
            String prefix = prefixFile.getCanonicalPath();
            if (filePath.startsWith(prefix) && prefix.length() > srcDirLength) {
                srcDirLength = prefix.length();
            }
      }
       
        if (srcDirLength > 0) {
            String fullname = filePath.substring(srcDirLength);
            assert fullname.endsWith(".ceylon");
            fullname = fullname.substring(0, fullname.length() - ".ceylon".length());
            fullname = fullname.replace(File.separator, ".");
            if(fullname.startsWith("."))
                fullname = fullname.substring(1);
            String packageName = Convert.packagePart(fullname);
            if (!packageName.equals(""))
                return packageName;
        }
      return null;
    }
   
    private void printError(RecognitionError le, String message, String key, LineMap map) {
        int pos = -1;
        if (le.getLine() > 0) {
            /* does not seem to be a way to determine the max line number so we do an ugly try-catch */
            try {
                pos = map.getStartPosition(le.getLine()) + le.getCharacterInLine();
            } catch (Exception e) { }
        }
        log.error(pos, key, message);
    }

    public Env<AttrContext> attribute(Env<AttrContext> env) {
        if (env.toplevel.sourcefile instanceof CeylonFileObject || isBootstrap) {
            try {
                sourceLanguage.push(Language.CEYLON);
                return super.attribute(env);
            } finally {
                sourceLanguage.pop();
            }
        }
        return super.attribute(env);
    }

    @Override
    protected JavaFileObject genCode(Env<AttrContext> env, JCClassDecl cdef) throws IOException {
        if (env.toplevel.sourcefile instanceof CeylonFileObject) {
            try {
                sourceLanguage.push(Language.CEYLON);
                // call our own genCode
                return genCodeUnlessError(env, cdef);
            } finally {
                sourceLanguage.pop();
            }
        }
        return super.genCode(env, cdef);
    }

    @Override
    protected boolean shouldStop(CompileState cs) {
        // we override this to make sure we don't stop because of errors, because we want to generate
        // code for classes with no errors
        boolean result;
        if (shouldStopPolicy == null)
            result = false;
        else
            result = cs.ordinal() > shouldStopPolicy.ordinal();
        if (!result && super.shouldStop(cs)) {
            treatLikelyBugsAsErrors = true;
        }
        return result;
    }
   
    public boolean getTreatLikelyBugsAsErrors() {
        return treatLikelyBugsAsErrors;
    }
   
    private JavaFileObject genCodeUnlessError(Env<AttrContext> env, JCClassDecl cdef) throws IOException {
        CeylonFileObject sourcefile = (CeylonFileObject) env.toplevel.sourcefile;
        try {
            // do not look at the global number of errors but only those for this file
            if (super.gen.genClass(env, cdef)){
                String packageName = cdef.sym.packge().getQualifiedName().toString();
                Package pkg = modelLoader.findPackage(packageName);
                if(pkg == null)
                    throw new RuntimeException("Failed to find package: "+packageName);
                Module module = pkg.getModule();
                if(!module.isDefault()){
                    String moduleName = module.getNameAsString();
                    CeylonFileObject moduleFileObject = moduleNamesToFileObjects.get(moduleName);
                    // if there's no module source file object it means the module descriptor had parse errors
                    if(moduleFileObject == null || moduleFileObject.hasError()){
                        // we do not produce any class files for modules with errors
                        if(options.get(OptionName.VERBOSE) != null){
                            Log.printLines(log.noticeWriter, "[Not writing class "+cdef.sym.className()
                                    +" because its module has errors: "+moduleName+"]");
                        }
                        return null;
                    }
                }
                return writer.writeClass(cdef.sym);
            }
        } catch (ClassWriter.PoolOverflow ex) {
            log.error(cdef.pos(), "limit.pool");
        } catch (ClassWriter.StringOverflow ex) {
            log.error(cdef.pos(), "limit.string.overflow",
                    ex.value.substring(0, 20));
        } catch (CompletionFailure ex) {
            chk.completionError(cdef.pos(), ex);
        } catch (AssertionError e) {
            throw new RuntimeException("Error generating bytecode for " + sourcefile.getName(), e);
        }
        return null;
    }

    protected void desugar(final Env<AttrContext> env, Queue<Pair<Env<AttrContext>, JCClassDecl>> results) {
        if (env.toplevel.sourcefile instanceof CeylonFileObject) {
            try {
                sourceLanguage.push(Language.CEYLON);
                super.desugar(env, results);
                return;
            } finally {
                sourceLanguage.pop();
            }
        }
        super.desugar(env, results);
    }
   
    protected void flow(Env<AttrContext> env, Queue<Env<AttrContext>> results) {
        if (env.toplevel.sourcefile instanceof CeylonFileObject) {
            try {
                sourceLanguage.push(Language.CEYLON);
                super.flow(env, results);
                return;
            } finally {
                sourceLanguage.pop();
            }
        }
        super.flow(env, results);  
    }

    @Override
    public void initProcessAnnotations(Iterable<? extends Processor> processors) {
        // don't do anything, which will leave the "processAnnotations" field to false
    }

    @Override
    public void complete(ClassSymbol c) throws CompletionFailure {
        try {
            sourceLanguage.push(Language.JAVA);
            super.complete(c);
        } catch (RunTwiceException e) {
            throw new CompletionFailure(c, e.getLocalizedMessage());
        } finally {
            sourceLanguage.pop();
        }
    }
   
    @Override
    public void generate(Queue<Pair<Env<AttrContext>, JCClassDecl>> queue, Queue<JavaFileObject> results) {
        timer.startTask("Generate");
        super.generate(queue, results);
        timer.endTask();
    }
}
TOP

Related Classes of com.redhat.ceylon.compiler.java.tools.LanguageCompiler$CompilerDelegate

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.