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