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