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

com.redhat.ceylon.compiler.js.JsCompiler Maven / Gradle / Ivy

There is a newer version: 1.3.3
Show newest version
package com.redhat.ceylon.compiler.js;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import com.redhat.ceylon.cmr.api.ArtifactContext;
import com.redhat.ceylon.cmr.api.ArtifactCreator;
import com.redhat.ceylon.cmr.api.RepositoryManager;
import com.redhat.ceylon.cmr.ceylon.CeylonUtils;
import com.redhat.ceylon.cmr.impl.ShaSigner;
import com.redhat.ceylon.common.Backend;
import com.redhat.ceylon.common.FileUtil;
import com.redhat.ceylon.common.Versions;
import com.redhat.ceylon.common.log.Logger;
import com.redhat.ceylon.compiler.js.loader.JsModuleSourceMapper;
import com.redhat.ceylon.compiler.js.loader.NpmAware;
import com.redhat.ceylon.compiler.js.util.JsIdentifierNames;
import com.redhat.ceylon.compiler.js.util.JsLogger;
import com.redhat.ceylon.compiler.js.util.JsOutput;
import com.redhat.ceylon.compiler.js.util.NpmDescriptorGenerator;
import com.redhat.ceylon.compiler.js.util.Options;
import com.redhat.ceylon.compiler.typechecker.TypeChecker;
import com.redhat.ceylon.compiler.typechecker.analyzer.MissingNativeVisitor;
import com.redhat.ceylon.compiler.typechecker.analyzer.Warning;
import com.redhat.ceylon.compiler.typechecker.context.PhasedUnit;
import com.redhat.ceylon.compiler.typechecker.io.VirtualFile;
import com.redhat.ceylon.compiler.typechecker.tree.Message;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Visitor;
import com.redhat.ceylon.compiler.typechecker.util.WarningSuppressionVisitor;
import com.redhat.ceylon.model.cmr.ArtifactResult;
import com.redhat.ceylon.model.typechecker.model.Constructor;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.model.typechecker.model.ImportableScope;
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.TypedDeclaration;
import com.redhat.ceylon.model.typechecker.model.Unit;

public class JsCompiler {

    protected final TypeChecker tc;
    protected final Options opts;
    protected final RepositoryManager outRepo;
    private final ErrorCollectingVisitor errorVisitor;

    private boolean stopOnErrors = true;
    private int errCount = 0;

    protected final List srcDirectories;
    protected List srcFiles;
    protected List resFiles;
    private final Map output = new HashMap();
    //You have to manually set this when compiling the language module
    private boolean compilingLanguageModule;
    private int exitCode = 0;
    private Logger logger;
    private JsIdentifierNames names;

    private class JsMissingNativeVisitor extends MissingNativeVisitor {
        
        public JsMissingNativeVisitor(File cwd) {
            super(Backend.JavaScript);
        }
        
        protected boolean checkNative(Node node, Declaration model) {
            if (model.getUnit() != node.getUnit()
                    && ModelUtil.isForBackend(
                            model.getUnit().getPackage().getModule().getNativeBackends(),
                            Backend.JavaScript)) {
                // If the model doesn't come from the current unit we're
                // compiling we check if it comes from a module that
                // supports JS and if so we assume everything is okay.
                // This is not actually correct! But it's HACK for the
                // IDE that doesn't really know about JS modules
                return true;
            }
            return canStitchNative(model);
        }
    }
    
    public int getExitCode() {
        return exitCode;
    }

    public JsCompiler(TypeChecker tc, Options options) {
        this(tc, options, false);
    }
    
    public JsCompiler(TypeChecker tc, Options options, boolean compilingLanguageModule) {
        this.tc = tc;
        this.opts = options;
        this.compilingLanguageModule = compilingLanguageModule;
        outRepo = CeylonUtils.repoManager()
                .cwd(options.getCwd())
                .outRepo(options.getOutRepo())
                .user(options.getUser())
                .password(options.getPass())
                .buildOutputManager();
        logger = opts.getLogger();
        if(logger == null) {
            logger = new JsLogger(opts);
        }
        errorVisitor = new ErrorCollectingVisitor(tc);
        srcDirectories = initSourceDirectories(opts);
    }

    protected List initSourceDirectories(Options opts) {
        ArrayList srcdirs = new ArrayList(opts.getSrcDirs().size());
        for (File srcdir : opts.getSrcDirs()) {
            srcdirs.add(sourceDirVirtual(srcdir));
        }
        return srcdirs;
    }
    
    protected VirtualFile sourceDirVirtual(File f) {
        return tc.getContext().getVfs().getFromFile(f);
    }
    
    private boolean isURL(String path) {
        try {
            new URL(path);
            return true;
        } catch (MalformedURLException e) {
            return false;
        }
    }

    /** Specifies whether the compiler should stop when errors are found in a compilation unit (default true). */
    public JsCompiler stopOnErrors(boolean flag) {
        stopOnErrors = flag;
        return this;
    }

    /** Sets the names of the files to compile. By default this is null, which means all units from the typechecker
     * will be compiled. */
    public JsCompiler setSourceFiles(List files) {
        if (files != null && !files.isEmpty()) {
            this.srcFiles = files;
        }
        return this;
    }

    /** Sets the names of the resources to pack with the compiler output. */
    public JsCompiler setResourceFiles(List files) {
        this.resFiles = files;
        return this;
    }

    /** Compile one phased unit. */
    private int compileUnit(PhasedUnit pu, JsIdentifierNames names) throws IOException {
        if (opts.isVerbose()) {
            logger.debug("Compiling "+pu.getUnitFile().getPath()+" to JS");
        }
        JsOutput jsout = getOutput(pu);
        MissingNativeVisitor mnv = new JsMissingNativeVisitor(opts.getCwd());
        pu.getCompilationUnit().visit(mnv);
        GenerateJsVisitor jsv = new GenerateJsVisitor(this, jsout, opts, names, pu.getTokens());
        pu.getCompilationUnit().visit(jsv);
        pu.getCompilationUnit().visit(errorVisitor);
        return jsv.getExitCode();
    }

    /** Indicates if compilation should stop, based on whether there were errors
     * in the last compilation unit and the stopOnErrors flag is set. */
    protected boolean stopOnError() {
        errCount += errorVisitor.getErrorCount();
        return stopOnErrors && errCount > 0;
    }

    /** Compile all the phased units in the typechecker.
     * @return true is compilation was successful (0 errors/warnings), false otherwise. */
    public boolean generate() throws IOException {
        errorVisitor.clear();
        errCount = 0;
        output.clear();
        try {
            if (opts.isVerbose()) {
                logger.debug("Generating metamodel...");
            }
            List typecheckerPhasedUnits = tc.getPhasedUnits().getPhasedUnits();
            List phasedUnits = new ArrayList<>(typecheckerPhasedUnits.size());
            for (PhasedUnit pu : typecheckerPhasedUnits) {
                if (srcFiles == null) {
                    phasedUnits.add(pu);
                } else {
                    File path = getFullPath(pu);
                    if (srcFiles.contains(path)) {
                        phasedUnits.add(pu);
                    }
                }
            }
            boolean generatedCode = false;
            
            //First generate the metamodel
            final Module defmod = tc.getContext().getModules().getDefaultModule();
            for (PhasedUnit pu: phasedUnits) {
                //#416 default module with packages
                Module mod = pu.getPackage().getModule();
                if (mod.getVersion() == null && !mod.isDefaultModule()) {
                    //Switch with the default module
                    for (com.redhat.ceylon.model.typechecker.model.Package pkg : mod.getPackages()) {
                        defmod.getPackages().add(pkg);
                        pkg.setModule(defmod);
                    }
                }
                EnumSet suppressedWarnings = opts.getSuppressWarnings();
                if (suppressedWarnings == null)
                    suppressedWarnings = EnumSet.noneOf(Warning.class);
                pu.getCompilationUnit().visit(
                            new WarningSuppressionVisitor<>(Warning.class, suppressedWarnings));
                //Perform capture analysis
                for (com.redhat.ceylon.model.typechecker.model.Declaration d : pu.getDeclarations()) {
                    if (d instanceof TypedDeclaration && d instanceof com.redhat.ceylon.model.typechecker.model.Setter == false) {
                        pu.getCompilationUnit().visit(new ValueVisitor((TypedDeclaration)d));
                    }
                }
                pu.getCompilationUnit().visit(getOutput(pu).mmg);
                if (opts.hasVerboseFlag("ast")) {
                    if (opts.getOutWriter() == null) {
                        logger.debug(pu.getCompilationUnit().toString());
                    } else {
                        opts.getOutWriter().write(pu.getCompilationUnit().toString());
                        opts.getOutWriter().write('\n');
                    }
                }
            }
            //Then write it out and output the reference in the module file
            names = new JsIdentifierNames(this);
            if (!compilingLanguageModule) {
                for (Map.Entry e : output.entrySet()) {
                    e.getValue().encodeModel(names);
                }
            }
            
            //Output all the require calls for any imports
            final Visitor importVisitor = new Visitor() {
                public void visit(Tree.Import that) {
                    ImportableScope scope =
                            that.getImportMemberOrTypeList().getImportList().getImportedScope();
                    Module _m = that.getUnit().getPackage().getModule();
                    if (scope instanceof Package) {
                        Package pkg = (Package)scope;
                        Module om = pkg.getModule();
                        if (!om.equals(_m) && (!om.isNative() ||
                                om.getNativeBackends().supports(Backend.JavaScript))) {
                            Module impmod = ((Package) scope).getModule();
                            if (impmod instanceof NpmAware && ((NpmAware)impmod).getNpmPath() != null) {
                                output.get(_m).requireFromNpm(impmod, names);
                            } else {
                                output.get(_m).require(impmod, names);
                            }
                        }
                    }
                }
                public void visit(Tree.ImportModule that) {
                    if (that.getImportPath() != null && that.getImportPath().getModel() instanceof Module) {
                        Module m = (Module)that.getImportPath().getModel();
                        //Binary version check goes here now
                        int binMajorVersion = m.getJsMajor();
                        int binMinorVersion = m.getJsMinor();
                        if (m.getJsMajor() == 0) {
                            //Check if it's something we're compiling
                            for (PhasedUnit pu : tc.getPhasedUnits().getPhasedUnits()) {
                                if (pu.getPackage() != null && pu.getPackage().getModule() == m) {
                                    m.setJsMajor(Versions.JS_BINARY_MAJOR_VERSION);
                                    m.setJsMinor(Versions.JS_BINARY_MINOR_VERSION);
                                    binMajorVersion = Versions.JS_BINARY_MAJOR_VERSION;
                                    binMinorVersion = Versions.JS_BINARY_MINOR_VERSION;
                                    break;
                                }
                            }
                            if (m.getJsMajor() == 0) {
                                //Load the module (most likely we're in the IDE if we need to do this)
                                ArtifactContext ac = new ArtifactContext(null, m.getNameAsString(),
                                        m.getVersion(), ArtifactContext.JS_MODEL);
                                ac.setIgnoreDependencies(true);
                                ac.setThrowErrorIfMissing(false);
                                ArtifactResult ar = tc.getContext().getRepositoryManager().getArtifactResult(ac);
                                if (ar == null) {
                                    return;
                                }
                                File js = ar.artifact();
                                if (js != null) {
                                    Map json = JsModuleSourceMapper.loadJsonModel(js);
                                    String binVersion = json.get("$mod-bin").toString();
                                    int p = binVersion.indexOf('.');
                                    binMajorVersion = Integer.valueOf(binVersion.substring(0, p));
                                    binMinorVersion = Integer.valueOf(binVersion.substring(p + 1));
                                }
                            }
                        }
                        if (!Versions.isJsBinaryVersionSupported(binMajorVersion, binMinorVersion)) {
                            that.addError(
                                    "version '"+ m.getVersion() + "' of module '" + m.getNameAsString() + 
                                    "' was compiled by an incompatible version of the compiler (binary version " +
                                    binMajorVersion + "." + binMinorVersion + " of module is not compatible with binary version " + 
                                    Versions.JS_BINARY_MAJOR_VERSION + "." + Versions.JS_BINARY_MINOR_VERSION + " of this compiler)");
                        }
                    }
                }
            };

            for (PhasedUnit pu: phasedUnits) {
                pu.getCompilationUnit().visit(importVisitor);
            }

            //Then generate the JS code
            List pkgs = new ArrayList<>(4);
            if (srcFiles == null && !phasedUnits.isEmpty()) {
                for (PhasedUnit pu: phasedUnits) {
                    if ("module.ceylon".equals(pu.getUnitFile().getName())) {
                        final int t = compileUnit(pu);
                        generatedCode = true;
                        if (t != 0) {
                            return false;
                        }
                    }
                }
                for (PhasedUnit pu: phasedUnits) {
                    if ("package.ceylon".equals(pu.getUnitFile().getName())) {
                        pkgs.add(pu);
                        continue;
                    } else if ("module.ceylon".equals(pu.getUnitFile().getName())) {
                        continue;
                    }
                    final int t = compileUnit(pu);
                    generatedCode = true;
                    if (t == 1) {
                        return false;
                    } else if (t == 2) {
                        break;
                    }
                }
            } else if(srcFiles != null && !srcFiles.isEmpty()
                         // For the specific case of the Stitcher
                         && !typecheckerPhasedUnits.isEmpty() ){
                for (PhasedUnit pu: phasedUnits) {
                    if ("module.ceylon".equals(pu.getUnitFile().getName())) {
                        final int t = compileUnit(pu);
                        generatedCode = true;
                        if (t != 0) {
                            return false;
                        }
                    }
                }

                for (File path : srcFiles) {
                    if (path.getPath().endsWith(ArtifactContext.JS)) {
                        //Just output the file
                        File dir = path.getParentFile();
                        PhasedUnit lastUnit = phasedUnits.isEmpty() ? typecheckerPhasedUnits.get(0) : phasedUnits.get(0);
                        for (PhasedUnit pu : phasedUnits) {
                            if (pu.getUnitFile().getPath().startsWith(dir.getPath())) {
                                lastUnit = pu;
                                break;
                            }
                        }
                        final JsOutput lastOut = getOutput(lastUnit);
                        VirtualFile vpath = findFile(path);
                        try (BufferedReader reader = new BufferedReader(new InputStreamReader(vpath.getInputStream(), opts.getEncoding()))) {
                            String line = null;
                            while ((line = reader.readLine()) != null) {
                                if (opts.isMinify()) {
                                    line = line.trim();
                                    if (!opts.isComment() && line.startsWith("//") && !line.contains("*/")) {
                                        continue;
                                    }
                                }
                                if (line.length()==0) {
                                    continue;
                                }
                                lastOut.getWriter().write(line);
                                lastOut.getWriter().write('\n');
                            }
                        } finally {
                            lastOut.addSource(path);
                        }
                        generatedCode = true;
                    } else {
                        //Find the corresponding compilation unit
                        for (PhasedUnit pu : phasedUnits) {
                            File unitFile = getFullPath(pu);
                            if (path.equals(unitFile)) {
                                if (path.getName().equals("package.ceylon")) {
                                    pkgs.add(pu);
                                    continue;
                                } else if (path.getName().equals("module.ceylon")) {
                                    continue;
                                }
                                final int t = compileUnit(pu);
                                generatedCode = true;
                                if (t == 1) {
                                    return false;
                                } else if (t == 2) {
                                    break;
                                }
                            }
                        }
                    }
                }
            }
            for (PhasedUnit pu: pkgs) {
                final int t = compileUnit(pu);
                generatedCode = true;
                if (t == 1) {
                    return false;
                } else if (t == 2) {
                    break;
                }
            }
            if(!generatedCode){
                logger.error("No source units found to compile");
                exitCode = 2;
            }
        } finally {
            if (exitCode==0) {
                exitCode = finish();
            }
        }
        return errCount == 0 && exitCode == 0;
    }

    private int compileUnit(PhasedUnit pu) throws IOException {
        exitCode = compileUnit(pu, names);
        if (exitCode != 0) {
            return 1;
        }
        if (stopOnError()) {
            logger.error("Errors found. Compilation stopped.");
            return 2;
        }
        getOutput(pu).addSource(getFullPath(pu));
        return 0;
    }

    VirtualFile findFile(File path) {
        for (VirtualFile root : srcDirectories) {
            String p = path.getPath().replace(File.separator, "/");
            String r = root.getPath();
            if (p.startsWith(r)) {
                File relPath = new File(p.substring(r.length() + 1));
                VirtualFile f = findFile(root, relPath);
                if (f != null) {
                    return f;
                }
            }
        }
        return null;
    }

    private VirtualFile findFile(VirtualFile root, File path) {
        VirtualFile result = root;
        String parts[] = path.getPath().split(Pattern.quote(File.separator));
        outer:
        for (String p : parts) {
            if (p.equals(".")) continue;
            for (VirtualFile f : result.getChildren()) {
                if (f.getName().equals(p)) {
                    result = f;
                    continue outer;
                }
            }
            return null;
        }
        return result;
    }
    
    public File getFullPath(PhasedUnit pu) {
        return new File(pu.getUnit().getFullPath());
    }
    
    /** Creates a JsOutput if needed, for the PhasedUnit.
     * Right now it's one file per module. */
    private JsOutput getOutput(PhasedUnit pu) throws IOException {
        Module mod = pu.getPackage().getModule();
        JsOutput jsout = output.get(mod);
        if (jsout==null) {
            jsout = newJsOutput(mod);
            output.put(mod, jsout);
            if (opts.isModulify()) {
                jsout.openWrapper();
            }
        }
        return jsout;
    }

    /** This exists solely so that the web IDE can override it and use a different JsOutput */
    protected JsOutput newJsOutput(Module m) throws IOException {
        return new JsOutput(m, isCompilingLanguageModule());
    }

    JsOutput getOutputForModule(Module m) {
        return output.get(m);
    }
    JsIdentifierNames getNames() {
        return names;
    }

    /** Closes all output writers and puts resulting artifacts in the output repo. */
    protected int finish() throws IOException {
        int result = 0;
        String outDir = CeylonUtils.resolveRepoUrl(opts.getOutRepo());
        if(!isURL(outDir)){
            File root = new File(outDir);
            if (root.exists()) {
                if (!(root.isDirectory() && root.canWrite())) {
                    logger.error("Cannot write to "+root+". Stop.");
                    result = 1;
                }
            } else {
                if (!FileUtil.mkdirs(root)) {
                    logger.error("Cannot create "+root+". Stop.");
                    result = 1;
                }
            }
        }
        for (Map.Entry entry: output.entrySet()) {
            JsOutput jsout = entry.getValue();

            if (!compilingLanguageModule) {
                jsout.publishUnsharedDeclarations(names);
            }
            if (opts.isModulify()) {
                jsout.closeWrapper();
            }
            String moduleName = entry.getKey().getNameAsString();
            String moduleVersion = entry.getKey().getVersion();
            
            if(opts.getDiagnosticListener() != null)
                opts.getDiagnosticListener().moduleCompiled(moduleName, moduleVersion);
            
            //Create the JS file
            final File jsart = jsout.close();
            final File modart = jsout.getModelFile();
            if (entry.getKey().isDefaultModule()) {
                logger.info("Created module "+moduleName);
            } else if (!compilingLanguageModule) {
                logger.info("Created module "+moduleName+"/"+moduleVersion);
            }
            if (compilingLanguageModule) {
                ArtifactContext artifact = new ArtifactContext(null, "delete", "me", ArtifactContext.JS);
                artifact.setForceOperation(true);
                outRepo.putArtifact(artifact, jsart);
            } else {
                final ArtifactContext artifact = new ArtifactContext(null, moduleName, moduleVersion, ArtifactContext.JS);
                artifact.setForceOperation(true);
                outRepo.putArtifact(artifact, jsart);
                final ArtifactContext martifact = new ArtifactContext(null, moduleName, moduleVersion, ArtifactContext.JS_MODEL);
                martifact.setForceOperation(true);
                outRepo.putArtifact(martifact, modart);
                //js file signature
                ShaSigner.signArtifact(outRepo, artifact, jsart, logger);
                ShaSigner.signArtifact(outRepo, martifact, modart, logger);
                //Create the src archive
                if (opts.isGenerateSourceArchive()) {
                    ArtifactCreator sac = CeylonUtils.makeSourceArtifactCreator(
                            outRepo,
                            opts.getSrcDirs(),
                            moduleName, moduleVersion,
                            opts.hasVerboseFlag("cmr"), logger);
                    sac.copy(FileUtil.filesToPathList(jsout.getSources()));
                }
                if (resFiles != null && !resFiles.isEmpty()) {
                    ArtifactCreator sac = CeylonUtils.makeResourceArtifactCreator(
                            outRepo,
                            opts.getSrcDirs(),
                            opts.getResourceDirs(),
                            opts.getResourceRootName(),
                            moduleName, moduleVersion,
                            opts.hasVerboseFlag("cmr"), logger);
                    sac.copy(FileUtil.filesToPathList(filterForModule(resFiles, opts.getResourceDirs(), moduleName)));
                }
                if (!entry.getKey().isDefaultModule()) {
                    String npmdesc = new NpmDescriptorGenerator(entry.getKey(),
                            opts.isGenerateSourceArchive(), resFiles != null && !resFiles.isEmpty())
                            .generateDescriptor();
                    File npmfile = File.createTempFile("npm", "json");
                    try{
                        try (FileWriter fw = new FileWriter(npmfile)) {
                            fw.write(npmdesc);
                        }
                        final ArtifactContext npmArtifact = new ArtifactContext(null, moduleName, moduleVersion, ArtifactContext.NPM_DESCRIPTOR);
                        npmArtifact.setForceOperation(true);
                        outRepo.putArtifact(npmArtifact, npmfile);
                    }finally{
                        npmfile.delete();
                    }
                }
            }
            FileUtil.deleteQuietly(jsart);
            if (modart!=null) {
                FileUtil.deleteQuietly(modart);
            }
        }
        return result;
    }

    private Collection filterForModule(List files, List roots, String moduleName) {
        ArrayList result = new ArrayList(files.size());
        for (File f : files) {
            String rel = FileUtil.relativeFile(roots, f.getPath());
            if (rel.startsWith(moduleName + "/") || rel.startsWith(moduleName + "\\")
                    || ("default".equals(moduleName) && !(rel.contains("/") || rel.contains("\\")))) {
                result.add(f);
            }
        }
        return result;
    }

    public int printErrorsAndCount(Writer out) throws IOException {
        DiagnosticListener diagnosticListener = opts.getDiagnosticListener();
        return errorVisitor.printErrors(out, diagnosticListener, true, true);
    }

    /** Print all the errors found during compilation to the specified stream. 
     * @throws IOException */
    public int printErrors(Writer out) throws IOException {
        DiagnosticListener diagnosticListener = opts.getDiagnosticListener();
        return errorVisitor.printErrors(out, diagnosticListener, true, false);
    }
    
    /** Returns the list of errors found during compilation. */
    public Set getErrors() {
        return errorVisitor.getErrors();
    }

    /** Writes the beginning of the wrapper function for a JS module. */
    public static void beginWrapper(Writer writer) throws IOException {
        writer.write("(function(define) { define(function(require, ex$, module) {\n");
    }

    /** Writes the require wrapper handling for our wrapper */
    public static void requireWrapper(Writer writer, Module mod) throws IOException {
        writer.write("var $$req$ = require; require = (typeof $$ceylon$require == 'undefined') ? $$req$ : function() { return $$ceylon$require('" + JsCompiler.scriptPath(mod) + "', $$req$, Array.prototype.slice.call(arguments)); }\n");
    }

    /** Writes the ending of the wrapper function for a JS module. */
    public static void endWrapper(Writer writer) throws IOException {
        //Finish the wrapper
        writer.write("});\n");
        writer.write("}(typeof define==='function' && define.amd ? define : function (factory) {\n");
        writer.write("if (typeof exports!=='undefined') { factory(require, exports, module);\n");
        writer.write("} else { throw 'no module loader'; }\n");
        writer.write("}));\n");
    }

    protected boolean nonCeylonUnit(Unit u) {
        return (u.getFilename() != null && !(u.getFilename().isEmpty()||u.getFilename().endsWith(".ceylon")))
                || (u.getPackage() != null && u.getPackage().getModule() != null && u.getPackage().getModule().isJava());
    }

    /** Returns true if the compiler is currently compiling the language module. */
    public boolean isCompilingLanguageModule() {
        return compilingLanguageModule;
    }

    /** Create a path for a require call to fetch the specified module. */
    public static String scriptPath(Module mod) {
        StringBuilder path = new StringBuilder(mod.getNameAsString().replace('.', '/')).append('/');
        if (!mod.isDefaultModule()) {
            path.append(mod.getVersion()).append('/');
        }
        path.append(mod.getNameAsString());
        if (!mod.isDefaultModule()) {
            path.append('-').append(mod.getVersion());
        }
        return path.toString();
    }

    VirtualFile getStitchedFile(final Declaration d, final String suffix) {
        String fqn = d.getQualifiedNameString();
        String name = d.getName();
        if (name == null && d instanceof Constructor) {
            name = "default$constructor";
            if (fqn.endsWith(".null")) {
                fqn = fqn.substring(0, fqn.length()-4);
            }
            fqn = fqn + name;
        }
        if (fqn.startsWith("ceylon.language")) {
            fqn = fqn.substring(15);
        }
        if (fqn.startsWith("::")) {
            fqn=fqn.substring(2);
        }
        fqn = fqn.replace('.', '/').replace("::", "/");
        File path;
        if (isCompilingLanguageModule()) {
            path = new File(Stitcher.LANGMOD_JS_SRC, fqn + suffix);
        } else {
            PhasedUnit pu = tc.getPhasedUnitFromRelativePath(d.getUnit().getRelativePath());
            path = new File(getFullPath(pu).getParentFile(), name + suffix);
        }
        return findFile(path);
    }

    VirtualFile getStitchedConstructorFile(final Declaration d, final String suffix) {
        VirtualFile f;
        if (isCompilingLanguageModule()) {
            f = getStitchedFile(d, suffix + ArtifactContext.JS);
        } else {
            f = findFile(new File(new File(d.getUnit().getFullPath()).getParentFile(),
                    String.format("%s%s.js", names.name(d), suffix)));
        }
        return f;
    }
    
    boolean canStitchNative(final Declaration d) {
        if (isCompilingLanguageModule()) {
            switch(d.getName()){
            // in special files
            case "Integer":
            case "Float":
            case "true":
            case "false":
            case "modules":
                // only native on JVM really
            case "Tuple":
            case "Callable":
            case "Throwable":
            case "Exception":
            case "reach":
                return true;
            }
        }
        VirtualFile f = getStitchedFile(d, ArtifactContext.JS);
        if (f != null)
            return true;
        if (isCompilingLanguageModule()) {
            // try folder
            f = getStitchedFile(d, "");
            if (f != null && f.isFolder())
                return true;
        }
        return false;
    }
    
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy