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 }