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.TopDownAnalyzerFacadeForJS;
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 = TopDownAnalyzerFacadeForJS.analyzeFiles(filesToTranslate, Predicates.<PsiFile>alwaysTrue(), config);
149 BindingContext bindingContext = analyzeExhaust.getBindingContext();
150 TopDownAnalyzerFacadeForJS.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 }