001    /*
002     * Copyright 2010-2013 JetBrains s.r.o.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.jetbrains.k2js.facade;
018    
019    import com.google.common.base.Predicates;
020    import com.google.dart.compiler.backend.js.ast.JsProgram;
021    import com.google.dart.compiler.util.TextOutputImpl;
022    import com.intellij.openapi.project.Project;
023    import com.intellij.openapi.util.text.StringUtil;
024    import com.intellij.openapi.vfs.VfsUtilCore;
025    import com.intellij.openapi.vfs.VirtualFile;
026    import com.intellij.psi.PsiFile;
027    import com.intellij.util.Function;
028    import com.intellij.util.SmartList;
029    import com.intellij.util.containers.ContainerUtil;
030    import org.jetbrains.annotations.NotNull;
031    import org.jetbrains.annotations.Nullable;
032    import org.jetbrains.jet.OutputFileCollection;
033    import org.jetbrains.jet.SimpleOutputFile;
034    import org.jetbrains.jet.SimpleOutputFileCollection;
035    import org.jetbrains.jet.analyzer.AnalyzeExhaust;
036    import org.jetbrains.jet.lang.descriptors.ModuleDescriptor;
037    import org.jetbrains.jet.lang.psi.JetFile;
038    import org.jetbrains.jet.lang.resolve.BindingContext;
039    import org.jetbrains.jet.utils.fileUtils.FileUtilsPackage;
040    import org.jetbrains.js.compiler.JsSourceGenerationVisitor;
041    import org.jetbrains.js.compiler.sourcemap.SourceMap3Builder;
042    import org.jetbrains.js.compiler.sourcemap.SourceMapBuilder;
043    import org.jetbrains.k2js.analyze.AnalyzerFacadeForJS;
044    import org.jetbrains.k2js.config.Config;
045    import org.jetbrains.k2js.facade.exceptions.TranslationException;
046    import org.jetbrains.k2js.translate.general.Translation;
047    
048    import java.io.File;
049    import java.io.IOException;
050    import java.util.Collections;
051    import java.util.List;
052    
053    import static org.jetbrains.jet.lang.psi.PsiPackage.JetPsiFactory;
054    import static org.jetbrains.k2js.facade.FacadeUtils.parseString;
055    
056    /**
057     * An entry point of translator.
058     */
059    public final class K2JSTranslator {
060    
061        public static final String FLUSH_SYSTEM_OUT = "Kotlin.System.flush();\n";
062        public static final String GET_SYSTEM_OUT = "Kotlin.System.output();\n";
063    
064        public static OutputFileCollection translateWithMainCallParameters(
065                @NotNull MainCallParameters mainCall,
066                @NotNull List<JetFile> files,
067                @NotNull File outputFile,
068                @Nullable File outputPrefixFile,
069                @Nullable File outputPostfixFile,
070                @NotNull Config config
071        ) throws TranslationException, IOException {
072            K2JSTranslator translator = new K2JSTranslator(config);
073            TextOutputImpl output = new TextOutputImpl();
074            SourceMapBuilder sourceMapBuilder = config.isSourcemap() ? new SourceMap3Builder(outputFile, output, new SourceMapBuilderConsumer()) : null;
075            String programCode = translator.generateProgramCode(files, mainCall, output, sourceMapBuilder);
076    
077            String prefix = FileUtilsPackage.readTextOrEmpty(outputPrefixFile);
078            String postfix = FileUtilsPackage.readTextOrEmpty(outputPostfixFile);
079    
080            StringBuilder outBuilder = new StringBuilder(programCode.length() + prefix.length() + postfix.length());
081            outBuilder.append(prefix).append(programCode).append(postfix);
082    
083            List<File> sourceFiles = ContainerUtil.map(files, new Function<JetFile, File>() {
084                @Override
085                public File fun(JetFile file) {
086                    VirtualFile virtualFile = file.getOriginalFile().getVirtualFile();
087                    if (virtualFile == null) return new File(file.getName());
088                    return VfsUtilCore.virtualToIoFile(virtualFile);
089                }
090            });
091    
092            SimpleOutputFile jsFile = new SimpleOutputFile(sourceFiles, outputFile.getName(), outBuilder.toString());
093            List<SimpleOutputFile> outputFiles = new SmartList<SimpleOutputFile>(jsFile);
094    
095            if (sourceMapBuilder != null) {
096                sourceMapBuilder.skipLinesAtBeginning(StringUtil.getLineBreakCount(prefix));
097                SimpleOutputFile sourcemapFile = new SimpleOutputFile(sourceFiles, sourceMapBuilder.getOutFile().getName(), sourceMapBuilder.build());
098                outputFiles.add(sourcemapFile);
099            }
100    
101            return new SimpleOutputFileCollection(outputFiles);
102        }
103    
104        @NotNull
105        private final Config config;
106    
107        public K2JSTranslator(@NotNull Config config) {
108            this.config = config;
109        }
110    
111        //NOTE: web demo related method
112        @SuppressWarnings("UnusedDeclaration")
113        @NotNull
114        public String translateStringWithCallToMain(@NotNull String programText, @NotNull String argumentsString) throws TranslationException {
115            JetFile file = JetPsiFactory(getProject()).createFile("test", programText);
116            String programCode = generateProgramCode(file, MainCallParameters.mainWithArguments(parseString(argumentsString))) + "\n";
117            return FLUSH_SYSTEM_OUT + programCode + GET_SYSTEM_OUT;
118        }
119    
120        @NotNull
121        public String generateProgramCode(@NotNull JetFile file, @NotNull MainCallParameters mainCallParameters) throws TranslationException {
122            return generateProgramCode(Collections.singletonList(file), mainCallParameters);
123        }
124    
125        @NotNull
126        public String generateProgramCode(@NotNull List<JetFile> files, @NotNull MainCallParameters mainCallParameters)
127                throws TranslationException {
128            return generateProgramCode(files, mainCallParameters, new TextOutputImpl(), null);
129        }
130    
131        @NotNull
132        public String generateProgramCode(
133                @NotNull List<JetFile> files,
134                @NotNull MainCallParameters mainCallParameters,
135                @NotNull TextOutputImpl output,
136                @Nullable SourceMapBuilder sourceMapBuilder
137        ) throws TranslationException {
138            JsProgram program = generateProgram(files, mainCallParameters);
139            JsSourceGenerationVisitor sourceGenerator = new JsSourceGenerationVisitor(output, sourceMapBuilder);
140            program.accept(sourceGenerator);
141            return output.toString();
142        }
143    
144        @NotNull
145        public JsProgram generateProgram(@NotNull List<JetFile> filesToTranslate,
146                @NotNull MainCallParameters mainCallParameters)
147                throws TranslationException {
148            AnalyzeExhaust analyzeExhaust = AnalyzerFacadeForJS.analyzeFiles(filesToTranslate, Predicates.<PsiFile>alwaysTrue(), config);
149            BindingContext bindingContext = analyzeExhaust.getBindingContext();
150            AnalyzerFacadeForJS.checkForErrors(Config.withJsLibAdded(filesToTranslate, config), bindingContext);
151            ModuleDescriptor moduleDescriptor = analyzeExhaust.getModuleDescriptor();
152            return Translation.generateAst(bindingContext, filesToTranslate, mainCallParameters, moduleDescriptor, config);
153        }
154    
155        @NotNull
156        private Project getProject() {
157            return config.getProject();
158        }
159    }