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.JetDeclarationWithBody; 047 import org.jetbrains.kotlin.psi.JetExpression; 048 import org.jetbrains.kotlin.psi.JetFile; 049 import org.jetbrains.kotlin.psi.JetNamedFunction; 050 import org.jetbrains.kotlin.resolve.BindingContext; 051 import org.jetbrains.kotlin.resolve.BindingTrace; 052 import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilPackage; 053 054 import java.util.Collection; 055 import java.util.Collections; 056 import java.util.List; 057 058 import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getFunctionDescriptor; 059 import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.convertToStatement; 060 import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.toStringLiteralList; 061 import static org.jetbrains.kotlin.js.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 BindingTrace bindingTrace, 166 @NotNull Collection<JetFile> files, @NotNull MainCallParameters mainCallParameters, 167 @NotNull ModuleDescriptor moduleDescriptor, 168 @NotNull Config config) 169 throws TranslationException { 170 try { 171 return doGenerateAst(bindingTrace, files, mainCallParameters, moduleDescriptor, config); 172 } 173 catch (UnsupportedOperationException e) { 174 throw new UnsupportedFeatureException("Unsupported feature used.", e); 175 } 176 catch (Throwable e) { 177 throw new TranslationInternalException(e); 178 } 179 } 180 181 @NotNull 182 private static JsProgram doGenerateAst(@NotNull BindingTrace bindingTrace, @NotNull Collection<JetFile> files, 183 @NotNull MainCallParameters mainCallParameters, 184 @NotNull ModuleDescriptor moduleDescriptor, 185 @NotNull Config config) throws MainFunctionNotFoundException { 186 StaticContext staticContext = StaticContext.generateStaticContext(bindingTrace, config, moduleDescriptor); 187 JsProgram program = staticContext.getProgram(); 188 JsBlock block = program.getGlobalBlock(); 189 190 JsFunction rootFunction = JsAstUtils.createPackage(block.getStatements(), program.getScope()); 191 JsBlock rootBlock = rootFunction.getBody(); 192 List<JsStatement> statements = rootBlock.getStatements(); 193 statements.add(program.getStringLiteral("use strict").makeStmt()); 194 195 TranslationContext context = TranslationContext.rootContext(staticContext, rootFunction); 196 statements.addAll(PackageDeclarationTranslator.translateFiles(files, context)); 197 defineModule(context, statements, config.getModuleId()); 198 199 if (mainCallParameters.shouldBeGenerated()) { 200 JsStatement statement = generateCallToMain(context, files, mainCallParameters.arguments()); 201 if (statement != null) { 202 statements.add(statement); 203 } 204 } 205 mayBeGenerateTests(files, config, rootBlock, context); 206 return context.program(); 207 } 208 209 private static void defineModule(@NotNull TranslationContext context, @NotNull List<JsStatement> statements, @NotNull String moduleId) { 210 JsName rootPackageName = context.scope().findName(Namer.getRootPackageName()); 211 if (rootPackageName != null) { 212 statements.add(new JsInvocation(context.namer().kotlin("defineModule"), context.program().getStringLiteral(moduleId), 213 rootPackageName.makeRef()).makeStmt()); 214 } 215 } 216 217 private static void mayBeGenerateTests(@NotNull Collection<JetFile> files, @NotNull Config config, 218 @NotNull JsBlock rootBlock, @NotNull TranslationContext context) { 219 JSTester tester = config.isTestConfig() ? new JSRhinoUnitTester() : new QUnitTester(); 220 tester.initialize(context, rootBlock); 221 JSTestGenerator.generateTestCalls(context, files, tester); 222 tester.deinitialize(); 223 } 224 225 //TODO: determine whether should throw exception 226 @Nullable 227 private static JsStatement generateCallToMain(@NotNull TranslationContext context, @NotNull Collection<JetFile> files, 228 @NotNull List<String> arguments) throws MainFunctionNotFoundException { 229 MainFunctionDetector mainFunctionDetector = new MainFunctionDetector(context.bindingContext()); 230 JetNamedFunction mainFunction = mainFunctionDetector.getMainFunction(files); 231 if (mainFunction == null) { 232 return null; 233 } 234 FunctionDescriptor functionDescriptor = getFunctionDescriptor(context.bindingContext(), mainFunction); 235 JsArrayLiteral argument = new JsArrayLiteral(toStringLiteralList(arguments, context.program())); 236 return CallTranslator.INSTANCE$.buildCall(context, functionDescriptor, Collections.singletonList(argument), null).makeStmt(); 237 } 238 }