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.translate.general;
018
019 import com.google.dart.compiler.backend.js.ast.*;
020 import org.jetbrains.annotations.NotNull;
021 import org.jetbrains.annotations.Nullable;
022 import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
023 import org.jetbrains.jet.lang.psi.JetDeclarationWithBody;
024 import org.jetbrains.jet.lang.psi.JetExpression;
025 import org.jetbrains.jet.lang.psi.JetFile;
026 import org.jetbrains.jet.lang.psi.JetNamedFunction;
027 import org.jetbrains.jet.lang.resolve.BindingContext;
028 import org.jetbrains.k2js.config.Config;
029 import org.jetbrains.k2js.facade.MainCallParameters;
030 import org.jetbrains.k2js.facade.exceptions.MainFunctionNotFoundException;
031 import org.jetbrains.k2js.facade.exceptions.TranslationException;
032 import org.jetbrains.k2js.facade.exceptions.TranslationInternalException;
033 import org.jetbrains.k2js.facade.exceptions.UnsupportedFeatureException;
034 import org.jetbrains.k2js.translate.context.Namer;
035 import org.jetbrains.k2js.translate.context.StaticContext;
036 import org.jetbrains.k2js.translate.context.TranslationContext;
037 import org.jetbrains.k2js.translate.declaration.NamespaceDeclarationTranslator;
038 import org.jetbrains.k2js.translate.expression.ExpressionVisitor;
039 import org.jetbrains.k2js.translate.expression.FunctionTranslator;
040 import org.jetbrains.k2js.translate.expression.PatternTranslator;
041 import org.jetbrains.k2js.translate.reference.CallBuilder;
042 import org.jetbrains.k2js.translate.test.JSTestGenerator;
043 import org.jetbrains.k2js.translate.test.JSTester;
044 import org.jetbrains.k2js.translate.utils.JsAstUtils;
045 import org.jetbrains.k2js.translate.utils.dangerous.DangerousData;
046 import org.jetbrains.k2js.translate.utils.dangerous.DangerousTranslator;
047
048 import java.util.Collection;
049 import java.util.List;
050
051 import static org.jetbrains.jet.plugin.JetMainDetector.getMainFunction;
052 import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptor;
053 import static org.jetbrains.k2js.translate.utils.JsAstUtils.*;
054 import static org.jetbrains.k2js.translate.utils.dangerous.DangerousData.collect;
055
056 /**
057 * This class provides a interface which all translators use to interact with each other.
058 * Goal is to simplify interaction between translators.
059 */
060 public final class Translation {
061
062 private Translation() {
063 }
064
065 @NotNull
066 public static FunctionTranslator functionTranslator(@NotNull JetDeclarationWithBody function,
067 @NotNull TranslationContext context) {
068 return FunctionTranslator.newInstance(function, context);
069 }
070
071 @NotNull
072 public static PatternTranslator patternTranslator(@NotNull TranslationContext context) {
073 return PatternTranslator.newInstance(context);
074 }
075
076 @NotNull
077 public static JsNode translateExpression(@NotNull JetExpression expression, @NotNull TranslationContext context) {
078 JsName aliasForExpression = context.aliasingContext().getAliasForExpression(expression);
079 if (aliasForExpression != null) {
080 return aliasForExpression.makeRef();
081 }
082 DangerousData data = collect(expression, context);
083 if (data.shouldBeTranslated()) {
084 return DangerousTranslator.translate(data, context);
085 }
086 return doTranslateExpression(expression, context);
087 }
088
089 //NOTE: use with care
090 @NotNull
091 public static JsNode doTranslateExpression(JetExpression expression, TranslationContext context) {
092 return expression.accept(new ExpressionVisitor(), context);
093 }
094
095 @NotNull
096 public static JsExpression translateAsExpression(@NotNull JetExpression expression,
097 @NotNull TranslationContext context) {
098 return convertToExpression(translateExpression(expression, context));
099 }
100
101 @NotNull
102 public static JsStatement translateAsStatement(@NotNull JetExpression expression,
103 @NotNull TranslationContext context) {
104 return convertToStatement(translateExpression(expression, context));
105 }
106
107 @NotNull
108 public static JsProgram generateAst(@NotNull BindingContext bindingContext,
109 @NotNull Collection<JetFile> files, @NotNull MainCallParameters mainCallParameters,
110 @NotNull Config config)
111 throws TranslationException {
112 try {
113 return doGenerateAst(bindingContext, files, mainCallParameters, config);
114 }
115 catch (UnsupportedOperationException e) {
116 throw new UnsupportedFeatureException("Unsupported feature used.", e);
117 }
118 catch (Throwable e) {
119 throw new TranslationInternalException(e);
120 }
121 }
122
123 @NotNull
124 private static JsProgram doGenerateAst(@NotNull BindingContext bindingContext, @NotNull Collection<JetFile> files,
125 @NotNull MainCallParameters mainCallParameters,
126 @NotNull Config config) throws MainFunctionNotFoundException {
127 StaticContext staticContext = StaticContext.generateStaticContext(bindingContext, config.getTarget());
128 JsProgram program = staticContext.getProgram();
129 JsBlock block = program.getGlobalBlock();
130
131 JsFunction rootFunction = JsAstUtils.createPackage(block.getStatements(), program.getScope());
132 JsBlock rootBlock = rootFunction.getBody();
133 List<JsStatement> statements = rootBlock.getStatements();
134 statements.add(program.getStringLiteral("use strict").makeStmt());
135
136 TranslationContext context = TranslationContext.rootContext(staticContext, rootFunction);
137 staticContext.initTranslators(context);
138 statements.addAll(NamespaceDeclarationTranslator.translateFiles(files, context));
139 defineModule(context, statements, config.getModuleId());
140
141 if (mainCallParameters.shouldBeGenerated()) {
142 JsStatement statement = generateCallToMain(context, files, mainCallParameters.arguments());
143 if (statement != null) {
144 statements.add(statement);
145 }
146 }
147 mayBeGenerateTests(files, config, rootBlock, context);
148 return context.program();
149 }
150
151 private static void defineModule(@NotNull TranslationContext context, @NotNull List<JsStatement> statements, @NotNull String moduleId) {
152 JsName rootNamespaceName = context.scope().findName(Namer.getRootNamespaceName());
153 if (rootNamespaceName != null) {
154 statements.add(new JsInvocation(context.namer().kotlin("defineModule"), context.program().getStringLiteral(moduleId),
155 rootNamespaceName.makeRef()).makeStmt());
156 }
157 }
158
159 private static void mayBeGenerateTests(@NotNull Collection<JetFile> files, @NotNull Config config,
160 @NotNull JsBlock rootBlock, @NotNull TranslationContext context) {
161 JSTester tester = config.getTester();
162 if (tester != null) {
163 tester.initialize(context, rootBlock);
164 JSTestGenerator.generateTestCalls(context, files, tester);
165 tester.deinitialize();
166 }
167 }
168
169 //TODO: determine whether should throw exception
170 @Nullable
171 private static JsStatement generateCallToMain(@NotNull TranslationContext context, @NotNull Collection<JetFile> files,
172 @NotNull List<String> arguments) throws MainFunctionNotFoundException {
173 JetNamedFunction mainFunction = getMainFunction(files);
174 if (mainFunction == null) {
175 return null;
176 }
177 FunctionDescriptor functionDescriptor = getFunctionDescriptor(context.bindingContext(), mainFunction);
178 return CallBuilder.build(context)
179 .args(new JsArrayLiteral(toStringLiteralList(arguments, context.program())))
180 .descriptor(functionDescriptor)
181 .translate().makeStmt();
182 }
183 }