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.*;
024 import org.jetbrains.jet.lang.resolve.BindingContext;
025 import org.jetbrains.k2js.config.Config;
026 import org.jetbrains.k2js.facade.MainCallParameters;
027 import org.jetbrains.k2js.facade.exceptions.MainFunctionNotFoundException;
028 import org.jetbrains.k2js.facade.exceptions.TranslationException;
029 import org.jetbrains.k2js.facade.exceptions.TranslationInternalException;
030 import org.jetbrains.k2js.facade.exceptions.UnsupportedFeatureException;
031 import org.jetbrains.k2js.translate.context.Namer;
032 import org.jetbrains.k2js.translate.context.StaticContext;
033 import org.jetbrains.k2js.translate.context.TranslationContext;
034 import org.jetbrains.k2js.translate.declaration.ClassAliasingMap;
035 import org.jetbrains.k2js.translate.declaration.ClassTranslator;
036 import org.jetbrains.k2js.translate.declaration.NamespaceDeclarationTranslator;
037 import org.jetbrains.k2js.translate.expression.ExpressionVisitor;
038 import org.jetbrains.k2js.translate.expression.FunctionTranslator;
039 import org.jetbrains.k2js.translate.expression.PatternTranslator;
040 import org.jetbrains.k2js.translate.expression.WhenTranslator;
041 import org.jetbrains.k2js.translate.initializer.ClassInitializerTranslator;
042 import org.jetbrains.k2js.translate.reference.CallBuilder;
043 import org.jetbrains.k2js.translate.test.JSTestGenerator;
044 import org.jetbrains.k2js.translate.test.JSTester;
045 import org.jetbrains.k2js.translate.utils.JsAstUtils;
046 import org.jetbrains.k2js.translate.utils.dangerous.DangerousData;
047 import org.jetbrains.k2js.translate.utils.dangerous.DangerousTranslator;
048
049 import java.util.Collection;
050 import java.util.Collections;
051 import java.util.List;
052
053 import static org.jetbrains.jet.plugin.JetMainDetector.getMainFunction;
054 import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptor;
055 import static org.jetbrains.k2js.translate.utils.JsAstUtils.*;
056 import static org.jetbrains.k2js.translate.utils.dangerous.DangerousData.collect;
057
058 /**
059 * This class provides a interface which all translators use to interact with each other.
060 * Goal is to simplify interaction between translators.
061 */
062 public final class Translation {
063
064 private Translation() {
065 }
066
067 @NotNull
068 public static FunctionTranslator functionTranslator(@NotNull JetDeclarationWithBody function,
069 @NotNull TranslationContext context) {
070 return FunctionTranslator.newInstance(function, context);
071 }
072
073 @NotNull
074 private static List<JsStatement> translateFiles(@NotNull Collection<JetFile> files, @NotNull TranslationContext context) {
075 return NamespaceDeclarationTranslator.translateFiles(files, context);
076 }
077
078 @NotNull
079 public static JsExpression translateClassDeclaration(@NotNull JetClass classDeclaration,
080 @NotNull ClassAliasingMap classAliasingMap,
081 @NotNull TranslationContext context) {
082 return ClassTranslator.generateClassCreation(classDeclaration, classAliasingMap, context);
083 }
084
085 @NotNull
086 public static PatternTranslator patternTranslator(@NotNull TranslationContext context) {
087 return PatternTranslator.newInstance(context);
088 }
089
090 @NotNull
091 public static JsNode translateExpression(@NotNull JetExpression expression, @NotNull TranslationContext context) {
092 JsName aliasForExpression = context.aliasingContext().getAliasForExpression(expression);
093 if (aliasForExpression != null) {
094 return aliasForExpression.makeRef();
095 }
096 DangerousData data = collect(expression, context);
097 if (data.shouldBeTranslated()) {
098 return DangerousTranslator.translate(data, context);
099 }
100 return doTranslateExpression(expression, context);
101 }
102
103 //NOTE: use with care
104 @NotNull
105 public static JsNode doTranslateExpression(JetExpression expression, TranslationContext context) {
106 return expression.accept(new ExpressionVisitor(), context);
107 }
108
109 @NotNull
110 public static JsExpression translateAsExpression(@NotNull JetExpression expression,
111 @NotNull TranslationContext context) {
112 return convertToExpression(translateExpression(expression, context));
113 }
114
115 @NotNull
116 public static JsStatement translateAsStatement(@NotNull JetExpression expression,
117 @NotNull TranslationContext context) {
118 return convertToStatement(translateExpression(expression, context));
119 }
120
121 @Nullable
122 public static JsNode translateWhenExpression(@NotNull JetWhenExpression expression,
123 @NotNull TranslationContext context) {
124 return WhenTranslator.translate(expression, context);
125 }
126
127 //TODO: see if generate*Initializer methods fit somewhere else
128 @NotNull
129 public static JsFunction generateClassInitializerMethod(@NotNull JetClassOrObject classDeclaration,
130 @NotNull TranslationContext context) {
131 ClassInitializerTranslator classInitializerTranslator = new ClassInitializerTranslator(classDeclaration, context);
132 return classInitializerTranslator.generateInitializeMethod();
133 }
134
135 @NotNull
136 public static JsProgram generateAst(@NotNull BindingContext bindingContext,
137 @NotNull Collection<JetFile> files, @NotNull MainCallParameters mainCallParameters,
138 @NotNull Config config)
139 throws TranslationException {
140 try {
141 return doGenerateAst(bindingContext, files, mainCallParameters, config);
142 }
143 catch (UnsupportedOperationException e) {
144 throw new UnsupportedFeatureException("Unsupported feature used.", e);
145 }
146 catch (Throwable e) {
147 throw new TranslationInternalException(e);
148 }
149 }
150
151 @NotNull
152 private static JsProgram doGenerateAst(@NotNull BindingContext bindingContext, @NotNull Collection<JetFile> files,
153 @NotNull MainCallParameters mainCallParameters,
154 @NotNull Config config) throws MainFunctionNotFoundException {
155 //TODO: move some of the code somewhere
156 StaticContext staticContext = StaticContext.generateStaticContext(bindingContext, config.getTarget());
157 JsProgram program = staticContext.getProgram();
158 JsBlock block = program.getGlobalBlock();
159
160 JsFunction rootFunction = JsAstUtils.createPackage(block.getStatements(), program.getScope());
161 JsBlock rootBlock = rootFunction.getBody();
162 List<JsStatement> statements = rootBlock.getStatements();
163 statements.add(program.getStringLiteral("use strict").makeStmt());
164
165 TranslationContext context = TranslationContext.rootContext(staticContext);
166 staticContext.getLiteralFunctionTranslator().setRootContext(context);
167 statements.addAll(translateFiles(files, context));
168 defineModule(context, statements, config.getModuleId());
169
170 if (mainCallParameters.shouldBeGenerated()) {
171 JsStatement statement = generateCallToMain(context, files, mainCallParameters.arguments());
172 if (statement != null) {
173 statements.add(statement);
174 }
175 }
176 mayBeGenerateTests(files, config, rootBlock, context);
177 return context.program();
178 }
179
180 private static void defineModule(@NotNull TranslationContext context, @NotNull List<JsStatement> statements, @NotNull String moduleId) {
181 JsName rootNamespaceName = context.scope().findName(Namer.getRootNamespaceName());
182 if (rootNamespaceName != null) {
183 statements.add(new JsInvocation(context.namer().kotlin("defineModule"), context.program().getStringLiteral(moduleId),
184 rootNamespaceName.makeRef()).makeStmt());
185 }
186 }
187
188 private static void mayBeGenerateTests(@NotNull Collection<JetFile> files, @NotNull Config config,
189 @NotNull JsBlock rootBlock, @NotNull TranslationContext context) {
190 JSTester tester = config.getTester();
191 if (tester != null) {
192 tester.initialize(context, rootBlock);
193 JSTestGenerator.generateTestCalls(context, files, tester);
194 tester.deinitialize();
195 }
196 }
197
198 //TODO: determine whether should throw exception
199 @Nullable
200 private static JsStatement generateCallToMain(@NotNull TranslationContext context, @NotNull Collection<JetFile> files,
201 @NotNull List<String> arguments) throws MainFunctionNotFoundException {
202 JetNamedFunction mainFunction = getMainFunction(files);
203 if (mainFunction == null) {
204 return null;
205 }
206 JsInvocation translatedCall = generateInvocation(context, mainFunction);
207 setArguments(context, arguments, translatedCall);
208 return translatedCall.makeStmt();
209 }
210
211
212 @NotNull
213 private static JsInvocation generateInvocation(@NotNull TranslationContext context, @NotNull JetNamedFunction mainFunction) {
214 FunctionDescriptor functionDescriptor = getFunctionDescriptor(context.bindingContext(), mainFunction);
215 JsExpression translatedCall = CallBuilder.build(context).descriptor(functionDescriptor).translate();
216 assert translatedCall instanceof JsInvocation;
217 return (JsInvocation) translatedCall;
218 }
219
220 private static void setArguments(@NotNull TranslationContext context, @NotNull List<String> arguments,
221 @NotNull JsInvocation translatedCall) {
222 JsArrayLiteral arrayLiteral = new JsArrayLiteral(toStringLiteralList(arguments, context.program()));
223 JsAstUtils.setArguments(translatedCall, Collections.<JsExpression>singletonList(arrayLiteral));
224 }
225 }