001 /*
002 * Copyright 2010-2015 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.kotlin.js.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.kotlin.descriptors.FunctionDescriptor;
023 import org.jetbrains.kotlin.descriptors.ModuleDescriptor;
024 import org.jetbrains.kotlin.idea.MainFunctionDetector;
025 import org.jetbrains.kotlin.js.config.Config;
026 import org.jetbrains.kotlin.js.facade.MainCallParameters;
027 import org.jetbrains.kotlin.js.facade.exceptions.MainFunctionNotFoundException;
028 import org.jetbrains.kotlin.js.facade.exceptions.TranslationException;
029 import org.jetbrains.kotlin.js.facade.exceptions.TranslationInternalException;
030 import org.jetbrains.kotlin.js.facade.exceptions.UnsupportedFeatureException;
031 import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator;
032 import org.jetbrains.kotlin.js.translate.context.Namer;
033 import org.jetbrains.kotlin.js.translate.context.StaticContext;
034 import org.jetbrains.kotlin.js.translate.context.TemporaryVariable;
035 import org.jetbrains.kotlin.js.translate.context.TranslationContext;
036 import org.jetbrains.kotlin.js.translate.declaration.PackageDeclarationTranslator;
037 import org.jetbrains.kotlin.js.translate.expression.ExpressionVisitor;
038 import org.jetbrains.kotlin.js.translate.expression.FunctionTranslator;
039 import org.jetbrains.kotlin.js.translate.expression.PatternTranslator;
040 import org.jetbrains.kotlin.js.translate.test.JSRhinoUnitTester;
041 import org.jetbrains.kotlin.js.translate.test.JSTestGenerator;
042 import org.jetbrains.kotlin.js.translate.test.JSTester;
043 import org.jetbrains.kotlin.js.translate.test.QUnitTester;
044 import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
045 import org.jetbrains.kotlin.js.translate.utils.mutator.AssignToExpressionMutator;
046 import org.jetbrains.kotlin.psi.KtDeclarationWithBody;
047 import org.jetbrains.kotlin.psi.KtExpression;
048 import org.jetbrains.kotlin.psi.KtFile;
049 import org.jetbrains.kotlin.psi.KtNamedFunction;
050 import org.jetbrains.kotlin.resolve.BindingTrace;
051 import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilsKt;
052
053 import java.util.Collection;
054 import java.util.Collections;
055 import java.util.List;
056
057 import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getFunctionDescriptor;
058 import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.convertToStatement;
059 import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.toStringLiteralList;
060 import static org.jetbrains.kotlin.js.translate.utils.mutator.LastExpressionMutator.mutateLastExpression;
061
062 /**
063 * This class provides a interface which all translators use to interact with each other.
064 * Goal is to simplify interaction between translators.
065 */
066 public final class Translation {
067
068 private Translation() {
069 }
070
071 @NotNull
072 public static FunctionTranslator functionTranslator(@NotNull KtDeclarationWithBody function,
073 @NotNull TranslationContext context) {
074 return FunctionTranslator.newInstance(function, context);
075 }
076
077 @NotNull
078 public static PatternTranslator patternTranslator(@NotNull TranslationContext context) {
079 return PatternTranslator.newInstance(context);
080 }
081
082 @NotNull
083 public static JsNode translateExpression(@NotNull KtExpression expression, @NotNull TranslationContext context) {
084 return translateExpression(expression, context, context.dynamicContext().jsBlock());
085 }
086
087 @NotNull
088 public static JsNode translateExpression(@NotNull KtExpression expression, @NotNull TranslationContext context, @NotNull JsBlock block) {
089 JsExpression aliasForExpression = context.aliasingContext().getAliasForExpression(expression);
090 if (aliasForExpression != null) {
091 return aliasForExpression;
092 }
093
094 TranslationContext innerContext = context.innerBlock();
095 JsNode result = doTranslateExpression(expression, innerContext);
096 context.moveVarsFrom(innerContext);
097 block.getStatements().addAll(innerContext.dynamicContext().jsBlock().getStatements());
098
099 if (BindingContextUtilsKt.isUnreachableCode(expression, context.bindingContext())) {
100 return context.getEmptyExpression();
101 }
102
103 return result;
104 }
105
106 //NOTE: use with care
107 @NotNull
108 public static JsNode doTranslateExpression(KtExpression expression, TranslationContext context) {
109 return expression.accept(new ExpressionVisitor(), context);
110 }
111
112 @NotNull
113 public static JsExpression translateAsExpression(@NotNull KtExpression expression, @NotNull TranslationContext context) {
114 return translateAsExpression(expression, context, context.dynamicContext().jsBlock());
115 }
116
117 @NotNull
118 public static JsExpression translateAsExpression(
119 @NotNull KtExpression expression,
120 @NotNull TranslationContext context,
121 @NotNull JsBlock block
122 ) {
123 JsNode jsNode = translateExpression(expression, context, block);
124 if (jsNode instanceof JsExpression) {
125 return (JsExpression) jsNode;
126 }
127
128 assert jsNode instanceof JsStatement : "Unexpected node of type: " + jsNode.getClass().toString();
129 if (BindingContextUtilsKt.isUsedAsExpression(expression, context.bindingContext())) {
130 TemporaryVariable result = context.declareTemporary(null);
131 AssignToExpressionMutator saveResultToTemporaryMutator = new AssignToExpressionMutator(result.reference());
132 block.getStatements().add(mutateLastExpression(jsNode, saveResultToTemporaryMutator));
133 return result.reference();
134 }
135
136 block.getStatements().add(convertToStatement(jsNode));
137 return context.getEmptyExpression();
138 }
139
140 @NotNull
141 public static JsStatement translateAsStatement(@NotNull KtExpression expression, @NotNull TranslationContext context) {
142 return translateAsStatement(expression, context, context.dynamicContext().jsBlock());
143 }
144
145 @NotNull
146 public static JsStatement translateAsStatement(
147 @NotNull KtExpression expression,
148 @NotNull TranslationContext context,
149 @NotNull JsBlock block) {
150 return convertToStatement(translateExpression(expression, context, block));
151 }
152
153 @NotNull
154 public static JsStatement translateAsStatementAndMergeInBlockIfNeeded(
155 @NotNull KtExpression expression,
156 @NotNull TranslationContext context
157 ) {
158 JsBlock block = new JsBlock();
159 JsNode node = translateExpression(expression, context, block);
160 return JsAstUtils.mergeStatementInBlockIfNeeded(convertToStatement(node), block);
161 }
162
163 @NotNull
164 public static TranslationContext generateAst(@NotNull BindingTrace bindingTrace,
165 @NotNull Collection<KtFile> files, @NotNull MainCallParameters mainCallParameters,
166 @NotNull ModuleDescriptor moduleDescriptor,
167 @NotNull Config config)
168 throws TranslationException {
169 try {
170 return doGenerateAst(bindingTrace, files, mainCallParameters, moduleDescriptor, config);
171 }
172 catch (UnsupportedOperationException e) {
173 throw new UnsupportedFeatureException("Unsupported feature used.", e);
174 }
175 catch (Throwable e) {
176 throw new TranslationInternalException(e);
177 }
178 }
179
180 @NotNull
181 private static TranslationContext doGenerateAst(@NotNull BindingTrace bindingTrace, @NotNull Collection<KtFile> files,
182 @NotNull MainCallParameters mainCallParameters,
183 @NotNull ModuleDescriptor moduleDescriptor,
184 @NotNull Config config) throws MainFunctionNotFoundException {
185 StaticContext staticContext = StaticContext.generateStaticContext(bindingTrace, config, moduleDescriptor);
186 JsProgram program = staticContext.getProgram();
187 JsBlock block = program.getGlobalBlock();
188
189 JsFunction rootFunction = JsAstUtils.createPackage(block.getStatements(), program.getScope());
190 JsBlock rootBlock = rootFunction.getBody();
191 List<JsStatement> statements = rootBlock.getStatements();
192 statements.add(program.getStringLiteral("use strict").makeStmt());
193
194 TranslationContext context = TranslationContext.rootContext(staticContext, rootFunction);
195 statements.addAll(PackageDeclarationTranslator.translateFiles(files, context));
196 defineModule(context, statements, config.getModuleId());
197
198 if (mainCallParameters.shouldBeGenerated()) {
199 JsStatement statement = generateCallToMain(context, files, mainCallParameters.arguments());
200 if (statement != null) {
201 statements.add(statement);
202 }
203 }
204 mayBeGenerateTests(files, config, rootBlock, context);
205 return context;
206 }
207
208 private static void defineModule(@NotNull TranslationContext context, @NotNull List<JsStatement> statements, @NotNull String moduleId) {
209 JsName rootPackageName = context.scope().findName(Namer.getRootPackageName());
210 if (rootPackageName != null) {
211 statements.add(new JsInvocation(context.namer().kotlin("defineModule"), context.program().getStringLiteral(moduleId),
212 rootPackageName.makeRef()).makeStmt());
213 }
214 }
215
216 private static void mayBeGenerateTests(@NotNull Collection<KtFile> files, @NotNull Config config,
217 @NotNull JsBlock rootBlock, @NotNull TranslationContext context) {
218 JSTester tester = config.isTestConfig() ? new JSRhinoUnitTester() : new QUnitTester();
219 tester.initialize(context, rootBlock);
220 JSTestGenerator.generateTestCalls(context, files, tester);
221 tester.deinitialize();
222 }
223
224 //TODO: determine whether should throw exception
225 @Nullable
226 private static JsStatement generateCallToMain(@NotNull TranslationContext context, @NotNull Collection<KtFile> files,
227 @NotNull List<String> arguments) throws MainFunctionNotFoundException {
228 MainFunctionDetector mainFunctionDetector = new MainFunctionDetector(context.bindingContext());
229 KtNamedFunction mainFunction = mainFunctionDetector.getMainFunction(files);
230 if (mainFunction == null) {
231 return null;
232 }
233 FunctionDescriptor functionDescriptor = getFunctionDescriptor(context.bindingContext(), mainFunction);
234 JsArrayLiteral argument = new JsArrayLiteral(toStringLiteralList(arguments, context.program()));
235 return CallTranslator.INSTANCE$.buildCall(context, functionDescriptor, Collections.singletonList(argument), null).makeStmt();
236 }
237 }