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