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.JsNode;
021 import com.google.dart.compiler.backend.js.ast.JsProgram;
022 import com.google.dart.compiler.util.TextOutputImpl;
023 import com.intellij.openapi.project.Project;
024 import com.intellij.openapi.util.text.StringUtil;
025 import com.intellij.openapi.vfs.VfsUtilCore;
026 import com.intellij.openapi.vfs.VirtualFile;
027 import com.intellij.psi.PsiFile;
028 import com.intellij.util.Consumer;
029 import com.intellij.util.Function;
030 import com.intellij.util.SmartList;
031 import com.intellij.util.containers.ContainerUtil;
032 import org.jetbrains.annotations.NotNull;
033 import org.jetbrains.annotations.Nullable;
034 import org.jetbrains.jet.OutputFileCollection;
035 import org.jetbrains.jet.SimpleOutputFile;
036 import org.jetbrains.jet.SimpleOutputFileCollection;
037 import org.jetbrains.jet.analyzer.AnalyzeExhaust;
038 import org.jetbrains.jet.lang.descriptors.ModuleDescriptor;
039 import org.jetbrains.jet.lang.psi.JetFile;
040 import org.jetbrains.jet.lang.resolve.BindingContext;
041 import org.jetbrains.jet.utils.fileUtils.FileUtilsPackage;
042 import org.jetbrains.js.compiler.JsSourceGenerationVisitor;
043 import org.jetbrains.js.compiler.sourcemap.SourceMap3Builder;
044 import org.jetbrains.js.compiler.sourcemap.SourceMapBuilder;
045 import org.jetbrains.k2js.analyze.TopDownAnalyzerFacadeForJS;
046 import org.jetbrains.k2js.config.Config;
047 import org.jetbrains.k2js.facade.exceptions.TranslationException;
048 import org.jetbrains.k2js.translate.general.Translation;
049
050 import java.io.File;
051 import java.io.IOException;
052 import java.util.Collections;
053 import java.util.List;
054
055 import static org.jetbrains.jet.lang.psi.PsiPackage.JetPsiFactory;
056 import static org.jetbrains.k2js.facade.FacadeUtils.parseString;
057
058 /**
059 * An entry point of translator.
060 */
061 public final class K2JSTranslator {
062
063 public static final String FLUSH_SYSTEM_OUT = "Kotlin.out.flush();\n";
064 public static final String GET_SYSTEM_OUT = "Kotlin.out.buffer;\n";
065
066 public static OutputFileCollection translateWithMainCallParameters(
067 @NotNull MainCallParameters mainCall,
068 @NotNull List<JetFile> files,
069 @NotNull File outputFile,
070 @Nullable File outputPrefixFile,
071 @Nullable File outputPostfixFile,
072 @NotNull Config config,
073 @NotNull Consumer<JsNode> astConsumer // hack for tests
074 ) throws TranslationException, IOException {
075 K2JSTranslator translator = new K2JSTranslator(config);
076 TextOutputImpl output = new TextOutputImpl();
077 SourceMapBuilder sourceMapBuilder = config.isSourcemap() ? new SourceMap3Builder(outputFile, output, new SourceMapBuilderConsumer()) : null;
078 String programCode = translator.generateProgramCode(files, mainCall, output, sourceMapBuilder, astConsumer);
079
080 String prefix = FileUtilsPackage.readTextOrEmpty(outputPrefixFile);
081 String postfix = FileUtilsPackage.readTextOrEmpty(outputPostfixFile);
082
083 StringBuilder outBuilder = new StringBuilder(programCode.length() + prefix.length() + postfix.length());
084 outBuilder.append(prefix).append(programCode).append(postfix);
085
086 List<File> sourceFiles = ContainerUtil.map(files, new Function<JetFile, File>() {
087 @Override
088 public File fun(JetFile file) {
089 VirtualFile virtualFile = file.getOriginalFile().getVirtualFile();
090 if (virtualFile == null) return new File(file.getName());
091 return VfsUtilCore.virtualToIoFile(virtualFile);
092 }
093 });
094
095 SimpleOutputFile jsFile = new SimpleOutputFile(sourceFiles, outputFile.getName(), outBuilder.toString());
096 List<SimpleOutputFile> outputFiles = new SmartList<SimpleOutputFile>(jsFile);
097
098 if (sourceMapBuilder != null) {
099 sourceMapBuilder.skipLinesAtBeginning(StringUtil.getLineBreakCount(prefix));
100 SimpleOutputFile sourcemapFile = new SimpleOutputFile(sourceFiles, sourceMapBuilder.getOutFile().getName(), sourceMapBuilder.build());
101 outputFiles.add(sourcemapFile);
102 }
103
104 return new SimpleOutputFileCollection(outputFiles);
105 }
106
107 @NotNull
108 private final Config config;
109
110 public K2JSTranslator(@NotNull Config config) {
111 this.config = config;
112 }
113
114 //NOTE: web demo related method
115 @SuppressWarnings("UnusedDeclaration")
116 @NotNull
117 public String translateStringWithCallToMain(@NotNull String programText, @NotNull String argumentsString) throws TranslationException {
118 JetFile file = JetPsiFactory(getProject()).createFile("test", programText);
119 String programCode = generateProgramCode(file, MainCallParameters.mainWithArguments(parseString(argumentsString))) + "\n";
120 return FLUSH_SYSTEM_OUT + programCode + GET_SYSTEM_OUT;
121 }
122
123 @NotNull
124 public String generateProgramCode(@NotNull JetFile file, @NotNull MainCallParameters mainCallParameters) throws TranslationException {
125 return generateProgramCode(Collections.singletonList(file), mainCallParameters);
126 }
127
128 @NotNull
129 public String generateProgramCode(@NotNull List<JetFile> files, @NotNull MainCallParameters mainCallParameters)
130 throws TranslationException {
131 //noinspection unchecked
132 return generateProgramCode(files, mainCallParameters, new TextOutputImpl(), null, Consumer.EMPTY_CONSUMER);
133 }
134
135 @NotNull
136 public String generateProgramCode(
137 @NotNull List<JetFile> files,
138 @NotNull MainCallParameters mainCallParameters,
139 @NotNull TextOutputImpl output,
140 @Nullable SourceMapBuilder sourceMapBuilder,
141 @NotNull Consumer<JsNode> astConsumer
142 ) throws TranslationException {
143 JsProgram program = generateProgram(files, mainCallParameters);
144
145 JsSourceGenerationVisitor sourceGenerator = new JsSourceGenerationVisitor(output, sourceMapBuilder);
146 program.accept(sourceGenerator);
147
148 astConsumer.consume(program);
149
150 return output.toString();
151 }
152
153 @NotNull
154 public JsProgram generateProgram(@NotNull List<JetFile> filesToTranslate,
155 @NotNull MainCallParameters mainCallParameters)
156 throws TranslationException {
157 AnalyzeExhaust analyzeExhaust = TopDownAnalyzerFacadeForJS.analyzeFiles(filesToTranslate, Predicates.<PsiFile>alwaysTrue(), config);
158 BindingContext bindingContext = analyzeExhaust.getBindingContext();
159 TopDownAnalyzerFacadeForJS.checkForErrors(Config.withJsLibAdded(filesToTranslate, config), bindingContext);
160 ModuleDescriptor moduleDescriptor = analyzeExhaust.getModuleDescriptor();
161 return Translation.generateAst(bindingContext, filesToTranslate, mainCallParameters, moduleDescriptor, config);
162 }
163
164 @NotNull
165 private Project getProject() {
166 return config.getProject();
167 }
168 }