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

com.redhat.ceylon.compiler.java.loader.CeylonEnter Maven / Gradle / Ivy

There is a newer version: 1.3.3
Show newest version
/*
 * Copyright Red Hat Inc. and/or its affiliates and other contributors
 * as indicated by the authors tag. All rights reserved.
 *
 * This copyrighted material is made available to anyone wishing to use,
 * modify, copy, or redistribute it subject to the terms and conditions
 * of the GNU General Public License version 2.
 * 
 * This particular file is subject to the "Classpath" exception as provided in the 
 * LICENSE file that accompanied this code.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT A
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License,
 * along with this distribution; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA  02110-1301, USA.
 */

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

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import org.antlr.runtime.Token;

import com.redhat.ceylon.cmr.api.ArtifactContext;
import com.redhat.ceylon.cmr.api.RepositoryManager;
import com.redhat.ceylon.cmr.impl.InvalidArchiveException;
import com.redhat.ceylon.cmr.util.JarUtils;
import com.redhat.ceylon.common.Backend;
import com.redhat.ceylon.common.ModuleSpec;
import com.redhat.ceylon.common.StatusPrinter;
import com.redhat.ceylon.compiler.java.codegen.AnnotationDeclarationVisitor;
import com.redhat.ceylon.compiler.java.codegen.AnnotationModelVisitor;
import com.redhat.ceylon.compiler.java.codegen.BoxingDeclarationVisitor;
import com.redhat.ceylon.compiler.java.codegen.BoxingVisitor;
import com.redhat.ceylon.compiler.java.codegen.CeylonCompilationUnit;
import com.redhat.ceylon.compiler.java.codegen.CeylonTransformer;
import com.redhat.ceylon.compiler.java.codegen.CodeGenError;
import com.redhat.ceylon.compiler.java.codegen.CompilerBoxingDeclarationVisitor;
import com.redhat.ceylon.compiler.java.codegen.CompilerBoxingVisitor;
import com.redhat.ceylon.compiler.java.codegen.DeferredVisitor;
import com.redhat.ceylon.compiler.java.codegen.DefiniteAssignmentVisitor;
import com.redhat.ceylon.compiler.java.codegen.EeVisitor;
import com.redhat.ceylon.compiler.java.codegen.InterfaceVisitor;
import com.redhat.ceylon.compiler.java.codegen.JvmMissingNativeVisitor;
import com.redhat.ceylon.compiler.java.codegen.SmallDeclarationVisitor;
import com.redhat.ceylon.compiler.java.codegen.SmallVisitor;
import com.redhat.ceylon.compiler.java.codegen.TypeParameterCaptureVisitor;
import com.redhat.ceylon.compiler.java.codegen.UnsupportedVisitor;
import com.redhat.ceylon.compiler.java.tools.CeylonLog;
import com.redhat.ceylon.compiler.java.tools.CeylonPhasedUnit;
import com.redhat.ceylon.compiler.java.tools.CeyloncFileManager;
import com.redhat.ceylon.compiler.java.tools.LanguageCompiler;
import com.redhat.ceylon.compiler.java.tools.LanguageCompiler.CompilerDelegate;
import com.redhat.ceylon.compiler.java.util.Util;
import com.redhat.ceylon.compiler.typechecker.analyzer.AnalysisError;
import com.redhat.ceylon.compiler.typechecker.analyzer.UnsupportedError;
import com.redhat.ceylon.compiler.typechecker.analyzer.UsageWarning;
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.parser.LexError;
import com.redhat.ceylon.compiler.typechecker.parser.ParseError;
import com.redhat.ceylon.compiler.typechecker.tree.Message;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.CompilationUnit;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.CompilerAnnotation;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.Statement;
import com.redhat.ceylon.compiler.typechecker.tree.TreeUtil;
import com.redhat.ceylon.compiler.typechecker.tree.UnexpectedError;
import com.redhat.ceylon.compiler.typechecker.util.AssertionVisitor;
import com.redhat.ceylon.compiler.typechecker.util.WarningSuppressionVisitor;
import com.redhat.ceylon.javax.tools.JavaFileManager;
import com.redhat.ceylon.javax.tools.JavaFileObject.Kind;
import com.redhat.ceylon.javax.tools.StandardLocation;
import com.redhat.ceylon.langtools.source.util.TaskEvent;
import com.redhat.ceylon.langtools.source.util.TaskListener;
import com.redhat.ceylon.langtools.tools.javac.code.Kinds;
import com.redhat.ceylon.langtools.tools.javac.code.Symbol;
import com.redhat.ceylon.langtools.tools.javac.code.Symbol.ClassSymbol;
import com.redhat.ceylon.langtools.tools.javac.code.Symtab;
import com.redhat.ceylon.langtools.tools.javac.code.Type.ClassType;
import com.redhat.ceylon.langtools.tools.javac.code.TypeTag;
import com.redhat.ceylon.langtools.tools.javac.code.Types;
import com.redhat.ceylon.langtools.tools.javac.comp.Annotate;
import com.redhat.ceylon.langtools.tools.javac.comp.AttrContext;
import com.redhat.ceylon.langtools.tools.javac.comp.Check;
import com.redhat.ceylon.langtools.tools.javac.comp.Enter;
import com.redhat.ceylon.langtools.tools.javac.comp.Env;
import com.redhat.ceylon.langtools.tools.javac.comp.Todo;
import com.redhat.ceylon.langtools.tools.javac.main.Option;
import com.redhat.ceylon.langtools.tools.javac.tree.EndPosTable;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCCompilationUnit;
import com.redhat.ceylon.langtools.tools.javac.util.Abort;
import com.redhat.ceylon.langtools.tools.javac.util.Context;
import com.redhat.ceylon.langtools.tools.javac.util.JCDiagnostic.DiagnosticPosition;
import com.redhat.ceylon.langtools.tools.javac.util.JCDiagnostic.SimpleDiagnosticPosition;
import com.redhat.ceylon.langtools.tools.javac.util.List;
import com.redhat.ceylon.langtools.tools.javac.util.Log;
import com.redhat.ceylon.langtools.tools.javac.util.Log.WriterKind;
import com.redhat.ceylon.langtools.tools.javac.util.Options;
import com.redhat.ceylon.langtools.tools.javac.util.Position;
import com.redhat.ceylon.langtools.tools.javac.util.SourceLanguage;
import com.redhat.ceylon.langtools.tools.javac.util.SourceLanguage.Language;
import com.redhat.ceylon.model.cmr.ArtifactResult;
import com.redhat.ceylon.model.loader.AbstractModelLoader;
import com.redhat.ceylon.model.loader.Timer;
import com.redhat.ceylon.model.loader.model.LazyModule;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.model.typechecker.model.ModelUtil;
import com.redhat.ceylon.model.typechecker.model.Module;
import com.redhat.ceylon.model.typechecker.model.Package;
import com.redhat.ceylon.model.typechecker.model.Setter;
import com.redhat.ceylon.model.typechecker.model.Type;
import com.redhat.ceylon.model.typechecker.model.TypedDeclaration;
import com.redhat.ceylon.model.typechecker.model.Unit;

public class CeylonEnter extends Enter {

    public static CeylonEnter instance(Context context) {
        CeylonEnter instance = (CeylonEnter)context.get(enterKey);
        if (instance == null){
            instance = new CeylonEnter(context);
            context.put(enterKey, instance);
        }
        return instance;
    }

    private CeylonTransformer gen;
    private boolean hasRun = false;
    private PhasedUnits phasedUnits;
    private CompilerDelegate compilerDelegate;
    private Log log;
    private AbstractModelLoader modelLoader;
    private Options options;
    private Timer timer;
    private CeyloncFileManager fileManager;
    private boolean verbose;
    private Check chk;
    private Types types;
    private Symtab symtab;
    private Todo todo;
    private boolean isBootstrap;
    private Annotate annotate;
    private Set modulesAddedToClassPath = new HashSet();
    private Set modulesAddedToAptClassPath = new HashSet();
    private TaskListener taskListener;
    private SourceLanguage sourceLanguage;
    private StatusPrinter sp;
    private boolean hasJavaAndCeylonSources;
    private LanguageCompiler compiler;

    
    protected CeylonEnter(Context context) {
        super(context);
        // make sure it's loaded first
        CeylonClassReader.instance(context);
        try {
            gen = CeylonTransformer.getInstance(context);
        } catch (Exception e) {
            // FIXME
            e.printStackTrace();
        }
        
        phasedUnits = LanguageCompiler.getPhasedUnitsInstance(context);
        compilerDelegate = LanguageCompiler.getCompilerDelegate(context);
        log = CeylonLog.instance(context);
        modelLoader = CeylonModelLoader.instance(context);
        options = Options.instance(context);
        timer = com.redhat.ceylon.compiler.java.util.Timer.instance(context);
        fileManager = (CeyloncFileManager) context.get(JavaFileManager.class);
        verbose = options.get(Option.VERBOSE) != null;
        isBootstrap = options.get(Option.BOOTSTRAPCEYLON) != null;
        chk = Check.instance(context);
        types = Types.instance(context);
        symtab = Symtab.instance(context);
        todo = Todo.instance(context);
        annotate = Annotate.instance(context);
        taskListener = context.get(TaskListener.class);
        sourceLanguage = SourceLanguage.instance(context);
        compiler = (LanguageCompiler) LanguageCompiler.instance(context);

        // now superclass init
        init(context);
        
        boolean isProgressPrinted = options.get(Option.CEYLONPROGRESS) != null && StatusPrinter.canPrint();
        if(isProgressPrinted && taskListener == null){
            sp = LanguageCompiler.getStatusPrinterInstance(context);
        }else{
            sp = null;
        }
    }

    @Override
    public void main(List trees) {
        // complete the javac AST with a completed ceylon model
        timer.startTask("prepareForTypeChecking");
        prepareForTypeChecking(trees);
        timer.endTask();
        List javaTrees = List.nil();
        List ceylonTrees = List.nil();
        // split them in two sets: java and ceylon
        for(JCCompilationUnit tree : trees){
            if(tree instanceof CeylonCompilationUnit)
                ceylonTrees = ceylonTrees.prepend(tree);
            else
                javaTrees = javaTrees.prepend(tree);
        }
        timer.startTask("Enter on Java trees");
        boolean needsModelReset = isBootstrap;
        // enter java trees first to set up their ClassSymbol objects for ceylon trees to use during type-checking
        if(!javaTrees.isEmpty()){
            setupImportedPackagesForJavaTrees(javaTrees);
            hasJavaAndCeylonSources = true;
            needsModelReset = true;
        }
        // this is false if we're in an APT round where we did not generate the trees
        if(!compiler.isAddModuleTrees()){
            setupImportedPackagesForJavaTrees(ceylonTrees);
        }
        if(isBootstrap || hasJavaAndCeylonSources){
            super.main(trees);
        }
        // now we can type-check the Ceylon code
        List packageInfo = completeCeylonTrees(trees);
        trees = trees.prependList(packageInfo);
        ceylonTrees = ceylonTrees.prependList(packageInfo);
        
        if(compiler.isHadRunTwiceException()){
            needsModelReset = true;
        }
        if(needsModelReset){
            // bootstrapping the language module is a bit more complex
            resetAndRunEnterAgain(trees);
        }else{
            timer.startTask("Enter on Ceylon trees");
            // and complete their new trees
            try {
                sourceLanguage.push(Language.CEYLON);
                super.main(ceylonTrees);
            } finally {
                sourceLanguage.pop();
            }
            timer.endTask();
        }
    }

    private void setupImportedPackagesForJavaTrees(List javaTrees) {
        JCTree.Visitor visitor = new ImportScanner(modelLoader);
        for(JCCompilationUnit unit : javaTrees){
            unit.accept(visitor);
        }
    }

    private void resetAndRunEnterAgain(List trees) {
        timer.startTask("Resetting all trees for bootstrap");
        
        // get rid of some caches and state
        chk.compiled.clear();
        types.reset();
        annotate.reset();
        super.reset();
        
        // reset all class symbols
        for(ClassSymbol classSymbol : symtab.classes.values()){
            if(Util.isLoadedFromSource(classSymbol) 
                    || (classSymbol.sourcefile != null && classSymbol.sourcefile.getKind() == Kind.SOURCE)){
                resetClassSymbol(classSymbol);
            }
        }
        
        // reset the trees
        JCTypeResetter jcTypeResetter = new JCTypeResetter();
        for(JCCompilationUnit tree : trees){
            tree.accept(jcTypeResetter);
        }
        
        // and reset the list of things to compile, because we got rid of the Env key we used to look them up
        // so they'd appear as extra things to compile when we do Enter
        todo.reset();
        timer.endTask();
        
        timer.startTask("Enter on Java+Ceylon trees");
        // now do Enter on all the java+ceylon code
        try {
            sourceLanguage.push(Language.CEYLON);
            super.main(trees);
        } finally {
            sourceLanguage.pop();
        }
        timer.endTask();
    }

    /**
     * This resets a ClassSymbol recursively, for bootstrap
     */
    private void resetClassSymbol(ClassSymbol classSymbol) {
        // look for inner classes
        if(classSymbol.members_field != null){
            for(Symbol member : classSymbol.getEnclosedElements()){
                if(member instanceof ClassSymbol){
                    resetClassSymbol((ClassSymbol)member);
                }
            }
        }
        
        // reset its type, we need to keep it
        com.redhat.ceylon.langtools.tools.javac.code.Type.ClassType classType = (ClassType) classSymbol.type;
        classType.all_interfaces_field = null;
        classType.interfaces_field = null;
        classType.supertype_field = null;
        classType.typarams_field = null;
        // make sure we give erroneous types a second chance
        if(classType.getTag() == TypeTag.ERROR){
            // This is how ClassSymbol's constructor builds them, so make'em brand new
            classSymbol.type = new ClassType(com.redhat.ceylon.langtools.tools.javac.code.Type.noType, null, null);
            classSymbol.type.tsym = classSymbol;
        }
        
        // reset its members and completer
        classSymbol.members_field = null;
        classSymbol.completer = null;
        // make sure we revert the class symbol kind to a new state by removing ERR kinds
        classSymbol.kind = Kinds.TYP;
        classSymbol.flags_field = 0;
    }

    @Override
    protected com.redhat.ceylon.langtools.tools.javac.code.Type classEnter(JCTree tree, Env env) {
        if(tree instanceof CeylonCompilationUnit){
            sourceLanguage.push(Language.CEYLON);
            try{
                return super.classEnter(tree, env);
            }finally{
                sourceLanguage.pop();
            }
        }else
            return super.classEnter(tree, env);
    }
    
    public void prepareForTypeChecking(List trees) {
        if (hasRun)
            throw new RuntimeException("Waaaaa, running twice!!!");
        //By now le language module version should be known (as local)
        //or we should use the default one.
        int numParserErrors = log.nerrors;
        // load the standard modules
        timer.startTask("loadStandardModules");
        compilerDelegate.loadStandardModules(modelLoader);
        timer.endTask();

        // load the modules we are compiling first
        hasRun = true;
        
        // make sure we don't load the files we are compiling from their class files
        timer.startTask("setupSourceFileObjects");
        compilerDelegate.setupSourceFileObjects(trees, modelLoader);
        timer.endTask();
       
        // resolve module dependencies
        timer.startTask("verifyModuleDependencyTree");
        compilerDelegate.resolveModuleDependencies(phasedUnits);
        timer.endTask();
        
        // now load package descriptors
        timer.startTask("loadPackageDescriptors");
        compilerDelegate.loadPackageDescriptors(modelLoader);
        timer.endTask();
        
        // at this point, abort if we had any errors logged due to module descriptors
        timer.startTask("collectTreeErrors");
        collectTreeErrors(false, false);
        timer.endTask();
        // check if we abort on errors or not
        if (options.get(Option.CEYLONCONTINUE) == null) {
            // if we didn't have any errors on module descriptors, 
            // we can go on, none were logged so
            // they can't be re-logged and duplicated later on
            if(log.nerrors - numParserErrors > 0)
                throw new Abort();
        }
    }

    
    public List completeCeylonTrees(List trees){
        // run the type checker
        timer.startTask("Ceylon type checking");
        typeCheck();
        // some debugging
        //printModules();
        timer.startTask("Ceylon code generation");
        /*
         * Here we convert the ceylon tree to its javac AST, after the typechecker has run
         */
        Timer nested = timer.nestedTimer();
        if(sp != null){
            sp.clearLine();
            sp.log("Generating AST");
        }
        int i=1;
        int size = countCeylonFiles(trees);
        List packageInfos = List.nil();
        for (JCCompilationUnit tree : trees) {
            if (tree instanceof CeylonCompilationUnit) {
                CeylonCompilationUnit ceylonTree = (CeylonCompilationUnit) tree;
                gen.setMap(ceylonTree.lineMap);
                CeylonPhasedUnit phasedUnit = (CeylonPhasedUnit)ceylonTree.phasedUnit;

                if(sp != null){
                    sp.clearLine();
                    sp.log("Generating ["+(i++)+"/"+size+"] ");
                    sp.log(phasedUnit.getPathRelativeToSrcDir());
                }

                gen.setFileObject(phasedUnit.getFileObject());
                nested.startTask("Ceylon code generation for " + phasedUnit.getUnitFile().getName());
                TaskEvent event = new TaskEvent(TaskEvent.Kind.PARSE, tree);
                if (taskListener != null) {
                    taskListener.started(event);
                }
                ceylonTree.defs = gen.transformAfterTypeChecking(ceylonTree.ceylonTree).toList();
                if (taskListener != null) {
                    taskListener.finished(event);
                }
                packageInfos = packageInfos.prependList(gen.transformPackageInfo(ceylonTree));
                nested.endTask();
                if(isVerbose("ast")){
                    log.printRawLines(WriterKind.ERROR, "Model tree for "+tree.getSourceFile());
                    log.printRawLines(WriterKind.ERROR, ceylonTree.ceylonTree.toString());
                }
                if(isVerbose("code")){
                    log.printRawLines(WriterKind.ERROR, "Java code generated for "+tree.getSourceFile());
                    log.printRawLines(WriterKind.ERROR, ceylonTree.toString());
                }
            }
        }
        if(isVerbose("code")){
            for (JCCompilationUnit packageInfo : packageInfos) {
                log.printRawLines(WriterKind.ERROR, packageInfo.toString());
            }
        }
        
        if(sp != null){
            sp.clearLine();
        }
        timer.startTask("Ceylon error generation");
        printGeneratorErrors();
        timer.endTask();
        // write some stats
        if(verbose)
            modelLoader.printStats();
        return packageInfos;
    }

    private int countCeylonFiles(List trees) {
        int cnt = 0;
        for (JCCompilationUnit tree : trees) {
            if (tree instanceof CeylonCompilationUnit) {
                cnt++;
            }
        }
        return cnt;
    }
    
    private boolean isVerbose(String key) {
        return verbose || options.get(Option.VERBOSE.text + ":" + key) != null;
    }

    public void addOutputModuleToClassPath(Module module){
        RepositoryManager repositoryManager = fileManager.getOutputRepositoryManager();
        ArtifactResult artifact = null;
        try {
            ArtifactContext ctx = new ArtifactContext(null, module.getNameAsString(), module.getVersion(), ArtifactContext.CAR, ArtifactContext.JAR);
            artifact = repositoryManager.getArtifactResult(ctx);
        } catch (InvalidArchiveException e) {
            log.warning("ceylon", "Module car " + e.getPath()
                    +" obtained from repository " + e.getRepository()
                    +" has an invalid SHA1 signature:"
                    +" it will be overwritten but if the problem"
                    +" persists you need to remove it and rebuild the module, since it may be corrupted.");
        } catch (Exception e) {
            String moduleName = module.getNameAsString();
            if(!module.isDefaultModule())
                moduleName += "/" + module.getVersion();
            log.error("ceylon", "Exception occured while trying to resolve module "+moduleName);
            e.printStackTrace();
        }
        if (artifact == null || JarUtils.isValidJar(artifact.artifact())) {
            addModuleToClassPath(module, false, artifact);
        } else {
            log.warning("ceylon", "Module car " + artifact.artifact()
            +" obtained from repository " + artifact.repository()
            +" could not be read:"
            +" it will be overwritten but if the problem"
            +" persists you need to remove it and rebuild the module, since it may be corrupted.");
        }
    }
    
    public boolean isModuleInClassPath(Module module){
        return modulesAddedToClassPath.contains(module);
    }
    
    public void addModuleToClassPath(Module module, boolean errorIfMissing, ArtifactResult result) {
        if(verbose)
            log.printRawLines(WriterKind.NOTICE, "[Adding module to classpath: "+module.getNameAsString()+"/"+module.getVersion()+"]");        
        
        Collection classPath = fileManager.getLocations().getLocation(StandardLocation.CLASS_PATH);
        
        File artifact = null;
        try {
            artifact = result != null ? result.artifact() : null;
        } catch (Exception e) {
            String moduleName = module.getNameAsString();
            if(!module.isDefaultModule())
                moduleName += "/" + module.getVersion();
            log.error("ceylon", "Exception occured while trying to resolve module "+moduleName);
            e.printStackTrace();
        }
        
        if(verbose){
            if(artifact != null)
                log.printRawLines(WriterKind.NOTICE, "[Found module at : "+artifact.getPath()+"]");
            else
                log.printRawLines(WriterKind.NOTICE, "[Could not find module]");
        }

        if(modulesAddedToClassPath.add(module)){
            if(artifact != null && artifact.exists()){
                ArrayList newClassPath = new ArrayList(classPath);
                newClassPath.add(artifact);
                try {
                    fileManager.getLocations().setLocation(StandardLocation.CLASS_PATH, newClassPath);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                ((LazyModule)module).loadPackageList(result);
            }else if(errorIfMissing){
                log.error("ceylon", "Failed to find module "+module.getNameAsString()+"/"+module.getVersion()+" in repositories");
            }
        }else if(verbose){
            log.printRawLines(WriterKind.NOTICE, "[Module already added to classpath]");
        }
    }

    public void addModuleToAptPath(ModuleSpec module, ArtifactResult result) {
        if(verbose)
            log.printRawLines(WriterKind.NOTICE, "[Adding APT module to APT classpath: "+module+"]");        
        
        Collection classPath = fileManager.getLocations().getLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH);
        
        File artifact = null;
        try {
            artifact = result != null ? result.artifact() : null;
        } catch (Exception e) {
            log.error("ceylon", "Exception occured while trying to resolve APT module "+module);
            e.printStackTrace();
        }
        
        if(verbose){
            if(artifact != null)
                log.printRawLines(WriterKind.NOTICE, "[Found APT module at : "+artifact.getPath()+"]");
            else
                log.printRawLines(WriterKind.NOTICE, "[Could not find APT module]");
        }

        if(modulesAddedToAptClassPath.add(module)){
            if(artifact != null && artifact.exists()){
                ArrayList newClassPath = classPath == null
                        ? new ArrayList(1)
                        : new ArrayList(classPath);
                newClassPath.add(artifact);
                try {
                    fileManager.getLocations().setLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH, newClassPath);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }else{
                log.error("ceylon", "Failed to find APT module "+module+" in repositories");
            }
        }else if(verbose){
            log.printRawLines(WriterKind.NOTICE, "[APT Module already added to APT classpath]");
        }
    }

    private void typeCheck() {
        final java.util.List listOfUnits = phasedUnits.getPhasedUnits();
        
        Module jdk = modelLoader.getJDKBaseModule();
        Package javaLangPackage = jdk.getPackage("java.lang");
        for (PhasedUnit pu : listOfUnits) {
        	pu.getUnit().setJavaLangPackage(javaLangPackage);
        }
        
        // Delegate to an external typechecker (e.g. the IDE build)
        compilerDelegate.typeCheck(listOfUnits);

        if(sp != null){
            sp.clearLine();
            sp.log("Preparation phase");
        }

        int size = listOfUnits.size();
        
        int i=1;
        // This phase is proper to the Java backend 
        ForcedCaptureVisitor fcv = new ForcedCaptureVisitor();
        
        for (PhasedUnit pu : listOfUnits) {
            if(sp != null)
                progressPreparation(1, i++, size, pu);
            Unit unit = pu.getUnit();
            final CompilationUnit compilationUnit = pu.getCompilationUnit();
            compilationUnit.visit(fcv);
            for (Declaration d: unit.getDeclarations()) {
                if (d instanceof TypedDeclaration 
                        && !(d instanceof Setter)
                        // skip already captured members
                        && !d.isCaptured()) {
                    compilationUnit.visit(new MethodOrValueReferenceVisitor((TypedDeclaration) d));
                }
            }
        }
        
        EeVisitor eeVisitor = gen.getEeVisitor();
        UnsupportedVisitor uv = new UnsupportedVisitor(eeVisitor);
        JvmMissingNativeVisitor mnv = new JvmMissingNativeVisitor(modelLoader);
        BoxingDeclarationVisitor boxingDeclarationVisitor = new CompilerBoxingDeclarationVisitor(gen);
        BoxingVisitor boxingVisitor = new CompilerBoxingVisitor(gen);
        SmallDeclarationVisitor smallDeclarationVisitor = new SmallDeclarationVisitor();
        SmallVisitor smallVisitor = new SmallVisitor();
        DeferredVisitor deferredVisitor = new DeferredVisitor();
        AnnotationDeclarationVisitor adv = new AnnotationDeclarationVisitor(gen);
        AnnotationModelVisitor amv = new AnnotationModelVisitor(gen);
        DefiniteAssignmentVisitor dav = new DefiniteAssignmentVisitor();
        TypeParameterCaptureVisitor tpCaptureVisitor = new TypeParameterCaptureVisitor();
        InterfaceVisitor localInterfaceVisitor = new InterfaceVisitor();
        
        // Extra phases for the compiler
        
        // boxing visitor depends on boxing decl
        i=1;
        for (PhasedUnit pu : listOfUnits) {
            if(sp != null)
                progressPreparation(2, i++, size, pu);
            pu.getCompilationUnit().visit(eeVisitor);
            pu.getCompilationUnit().visit(uv);
            pu.getCompilationUnit().visit(adv);
        }
        i=1;
        for (PhasedUnit pu : listOfUnits) {
            if(sp != null)
                progressPreparation(3, i++, size, pu);
            pu.getCompilationUnit().visit(boxingDeclarationVisitor);
            pu.getCompilationUnit().visit(smallDeclarationVisitor);
            pu.getCompilationUnit().visit(amv);
            
        }
        i=1;
        // the others can run at the same time
        for (PhasedUnit pu : listOfUnits) {
            if(sp != null)
                progressPreparation(4, i++, size, pu);
            CompilationUnit compilationUnit = pu.getCompilationUnit();
            compilationUnit.visit(mnv);
            compilationUnit.visit(boxingVisitor);
            compilationUnit.visit(smallVisitor);
            compilationUnit.visit(deferredVisitor);
            compilationUnit.visit(dav);
            compilationUnit.visit(tpCaptureVisitor);
            compilationUnit.visit(localInterfaceVisitor);
        }
        
        i=1;
        for (PhasedUnit pu : listOfUnits) {
            if(sp != null)
                progressPreparation(5, i++, size, pu);
            CompilationUnit compilationUnit = pu.getCompilationUnit();
            compilationUnit.visit(new WarningSuppressionVisitor(Warning.class, pu.getSuppressedWarnings()));
        }
        
        collectTreeErrors(true, true);
    }

    private void progressPreparation(int phase, int i, int size, PhasedUnit pu) {
        sp.clearLine();
        sp.log("Preparing "+phase+"/5 ["+(i++)+"/"+size+"] ");
        sp.log(pu.getPathRelativeToSrcDir());
    }

    private void collectTreeErrors(boolean runAssertions, final boolean reportWarnings) {
        final java.util.List listOfUnits = phasedUnits.getPhasedUnits();

        for (PhasedUnit pu : listOfUnits) {
            pu.getCompilationUnit().visit(new JavacAssertionVisitor((CeylonPhasedUnit) pu, runAssertions){
                @Override
                protected void out(UnexpectedError err) {
                    setSource();
                    logError(getPosition(err.getTreeNode()), "ceylon", err.getMessage());
                }
                @Override
                protected void out(AnalysisError err) {
                    if(err.isWarning()){
                        if (reportWarnings) {
                            setSource();
                            Node node = getIdentifyingNode(err.getTreeNode());
                            logWarning(getPosition(node), "ceylon", err.getMessage());
                        }
                    }else{
                        setSource();
                        Node node = getIdentifyingNode(err.getTreeNode());
                        logError(getPosition(node), "ceylon", err.getMessage());
                    }
                }
                @Override
                protected void out(Node that, LexError err) {
                    setSource();
                    DiagnosticPosition pos = getPosition((err.getLine()), err.getCharacterInLine());
                    logError(pos, "ceylon", err.getMessage());
                }
                @Override
                protected void out(Node that, ParseError err) {
                    setSource();
                    DiagnosticPosition pos = getPosition((err.getLine()), err.getCharacterInLine());
                    logError(pos, "ceylon", err.getMessage());
                }
                @Override
                protected void out(UnsupportedError err) {
                    setSource();
                    Node node = getIdentifyingNode(err.getTreeNode());
                    logError(getPosition(node), "ceylon", err.getMessage());
                }
                @Override
                protected void out(UsageWarning warning) {
                    if (reportWarnings && !warning.isSuppressed()) {
                        setSource();
                        Node node = getIdentifyingNode(warning.getTreeNode());
                        logWarning(getPosition(node), "ceylon", warning.getMessage());
                    }
                }
                @Override
                protected void out(Node that, String message) {
                    setSource();
                    logError(getPosition(that), "ceylon", message);
                }
            });
        }
    }

    /**
     * Visits the nodes of each unit calling 
     * {@link #logError(int, String, String)} for each {@link CodeGenError}
     */
    private void printGeneratorErrors() {
        final java.util.List listOfUnits = phasedUnits.getPhasedUnits();

        for (PhasedUnit pu : listOfUnits) {
            pu.getCompilationUnit().visit(new JavacAssertionVisitor((CeylonPhasedUnit) pu, false){
                @Override
                protected void out(UnexpectedError err) {
                    if(err instanceof CodeGenError){
                        setSource();
                        CodeGenError error = ((CodeGenError)err);
                        String location = locationInfo(error);
                        logError(getPosition(err.getTreeNode()), 
                                "ceylon.codegen.exception", 
                                "compiler bug: "+error.getMessage()+" at " + location);
                    }
                }
                private String locationInfo(CodeGenError error) {
                    if (error.getCause() != null
                            && error.getCause().getStackTrace() != null
                            && error.getCause().getStackTrace().length > 0) {
                        return error.getCause().getStackTrace()[0].toString();
                    } else {
                        return "unknown";
                    }
                }
                // Ignore those
                @Override
                protected void out(AnalysisError err) {}
                @Override
                protected void out(UnsupportedError err) {}
                @Override
                protected void out(UsageWarning warn) {}
                @Override
                protected void out(Node that, ParseError err) {}
                @Override
                protected void out(Node that, LexError err) {}
                @Override
                protected void out(Node that, String message) {}
            });
        }
    }

    protected void logError(int position, String key, String message) {
        logError(toDiagnosticPosition(position), key, message);
    }

    protected void logError(DiagnosticPosition position, String key, String message) {
        boolean prev = log.multipleErrors;
        // we want multiple errors for Ceylon
        log.multipleErrors = true;
        try{
            log.error(position, key, message);
        }finally{
            log.multipleErrors = prev;
        }
    }

    protected void logWarning(int position, String key, String message) {
        logWarning(toDiagnosticPosition(position), key, message);
    }

    protected void logWarning(DiagnosticPosition position, String key, String message) {
        boolean prev = log.multipleErrors;
        // we want multiple errors for Ceylon
        log.multipleErrors = true;
        try{
            log.warning(position, key, message);
        }finally{
            log.multipleErrors = prev;
        }
    }
    
    protected void logStackTrace(String key, Throwable t) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        t.printStackTrace(pw);
        pw.close();
        String tracktrace = sw.toString();
        boolean prev = log.multipleErrors;
        // we want multiple errors for Ceylon
        log.multipleErrors = true;
        try{
            log.printLines(WriterKind.ERROR, key, tracktrace);
        }finally{
            log.multipleErrors = prev;
        }
    }

    protected DiagnosticPosition toDiagnosticPosition(int pos) {
        return (pos == Position.NOPOS ? null : new SimpleDiagnosticPosition(pos));
    }

    private class JavacAssertionVisitor extends AssertionVisitor {
        private CeylonPhasedUnit cpu;
        protected final boolean runAssertions;
        JavacAssertionVisitor(CeylonPhasedUnit cpu, boolean runAssertions){
            this.cpu = cpu;
            this.runAssertions = runAssertions;
            this.includeUsageWarnings(true);
        }
        
        @Override
        protected void checkType(Statement that, Type type, Node typedNode) {
            if(runAssertions)
                super.checkType(that, type, typedNode);
        }
        
        @Override
        protected boolean includeError(Message err, int phase) {
            return err.getBackend() == null ||
                    ModelUtil.isForBackend(err.getBackend().asSet(), Backend.Java);
        }

        protected Node getIdentifyingNode(Node node) {
            return TreeUtil.getIdentifyingNode(node);
        }
        
        protected DiagnosticPosition getPosition(int line, int characterInLine) {
            int pos = getPositionAsInt(line, characterInLine);
            return toDiagnosticPosition(pos);
        }
        
        protected int getPositionAsInt(int line, int characterInLine) {
            if (line<1) {
                return -1;
            }
            try {
                return cpu.getLineMap().getPosition(line,characterInLine);
            }
            catch (ArrayIndexOutOfBoundsException aie) {
                if (line<2) {
                    return -1;
                }
                return cpu.getLineMap().getPosition(line-1, 0);
            }
        }
        
        protected DiagnosticPosition getPosition(Node node) {
            if(node != null) {
                Token token = node.getToken();
                if (token != null) {
                    final int startOffset = node.getStartIndex();
                    final int endOffset = node.getStopIndex();
                    final int startLine = token.getLine();
                    final int startCol = token.getCharPositionInLine();
                    
                    if (startOffset < 0 || endOffset < 0) {
                        return getPosition(startLine, 
                                startCol);
                    } else {
                        return new DiagnosticPosition() {
                            @Override
                            public JCTree getTree() {
                                return null;
                            }
                            
                            @Override
                            public int getStartPosition() {
                                return startOffset;
                            }
                            
                            @Override
                            public int getPreferredPosition() {
                                return startOffset;
                            }
                            
                            @Override
                            public int getEndPosition(EndPosTable endPosTable) {
                                return endOffset;
                            }
                        };
                    }
                }
            }
            return null;
        }
        
        protected void setSource() {
            log.useSource(cpu.getFileObject());
        }
        
        @Override
        protected void initExpectingError(java.util.List annotations) {
            if (runAssertions) {
                super.initExpectingError(annotations);
            }
        }
    }
    
    public boolean hasRun(){
        return hasRun;
    }

    public boolean isCompilingJavaAndCeylonSources() {
        return hasJavaAndCeylonSources;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy