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 017package org.jetbrains.k2js.translate.general; 018 019import com.google.dart.compiler.backend.js.ast.*; 020import org.jetbrains.annotations.NotNull; 021import org.jetbrains.annotations.Nullable; 022import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; 023import org.jetbrains.jet.lang.psi.*; 024import org.jetbrains.jet.lang.resolve.BindingContext; 025import org.jetbrains.k2js.config.Config; 026import org.jetbrains.k2js.facade.MainCallParameters; 027import org.jetbrains.k2js.facade.exceptions.MainFunctionNotFoundException; 028import org.jetbrains.k2js.facade.exceptions.TranslationException; 029import org.jetbrains.k2js.facade.exceptions.TranslationInternalException; 030import org.jetbrains.k2js.facade.exceptions.UnsupportedFeatureException; 031import org.jetbrains.k2js.translate.context.Namer; 032import org.jetbrains.k2js.translate.context.StaticContext; 033import org.jetbrains.k2js.translate.context.TranslationContext; 034import org.jetbrains.k2js.translate.declaration.ClassAliasingMap; 035import org.jetbrains.k2js.translate.declaration.ClassTranslator; 036import org.jetbrains.k2js.translate.declaration.NamespaceDeclarationTranslator; 037import org.jetbrains.k2js.translate.expression.ExpressionVisitor; 038import org.jetbrains.k2js.translate.expression.FunctionTranslator; 039import org.jetbrains.k2js.translate.expression.PatternTranslator; 040import org.jetbrains.k2js.translate.expression.WhenTranslator; 041import org.jetbrains.k2js.translate.initializer.ClassInitializerTranslator; 042import org.jetbrains.k2js.translate.reference.CallBuilder; 043import org.jetbrains.k2js.translate.test.JSTestGenerator; 044import org.jetbrains.k2js.translate.test.JSTester; 045import org.jetbrains.k2js.translate.utils.JsAstUtils; 046import org.jetbrains.k2js.translate.utils.dangerous.DangerousData; 047import org.jetbrains.k2js.translate.utils.dangerous.DangerousTranslator; 048 049import java.util.Collection; 050import java.util.Collections; 051import java.util.List; 052 053import static org.jetbrains.jet.plugin.JetMainDetector.getMainFunction; 054import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptor; 055import static org.jetbrains.k2js.translate.utils.JsAstUtils.*; 056import static org.jetbrains.k2js.translate.utils.dangerous.DangerousData.collect; 057 058/** 059 * This class provides a interface which all translators use to interact with each other. 060 * Goal is to simplify interaction between translators. 061 */ 062public final class Translation { 063 064 private Translation() { 065 } 066 067 @NotNull 068 public static FunctionTranslator functionTranslator(@NotNull JetDeclarationWithBody function, 069 @NotNull TranslationContext context) { 070 return FunctionTranslator.newInstance(function, context); 071 } 072 073 @NotNull 074 private static List<JsStatement> translateFiles(@NotNull Collection<JetFile> files, @NotNull TranslationContext context) { 075 return NamespaceDeclarationTranslator.translateFiles(files, context); 076 } 077 078 @NotNull 079 public static JsExpression translateClassDeclaration(@NotNull JetClass classDeclaration, 080 @NotNull ClassAliasingMap classAliasingMap, 081 @NotNull TranslationContext context) { 082 return ClassTranslator.generateClassCreation(classDeclaration, classAliasingMap, context); 083 } 084 085 @NotNull 086 public static PatternTranslator patternTranslator(@NotNull TranslationContext context) { 087 return PatternTranslator.newInstance(context); 088 } 089 090 @NotNull 091 public static JsNode translateExpression(@NotNull JetExpression expression, @NotNull TranslationContext context) { 092 JsName aliasForExpression = context.aliasingContext().getAliasForExpression(expression); 093 if (aliasForExpression != null) { 094 return aliasForExpression.makeRef(); 095 } 096 DangerousData data = collect(expression, context); 097 if (data.shouldBeTranslated()) { 098 return DangerousTranslator.translate(data, context); 099 } 100 return doTranslateExpression(expression, context); 101 } 102 103 //NOTE: use with care 104 @NotNull 105 public static JsNode doTranslateExpression(JetExpression expression, TranslationContext context) { 106 return expression.accept(new ExpressionVisitor(), context); 107 } 108 109 @NotNull 110 public static JsExpression translateAsExpression(@NotNull JetExpression expression, 111 @NotNull TranslationContext context) { 112 return convertToExpression(translateExpression(expression, context)); 113 } 114 115 @NotNull 116 public static JsStatement translateAsStatement(@NotNull JetExpression expression, 117 @NotNull TranslationContext context) { 118 return convertToStatement(translateExpression(expression, context)); 119 } 120 121 @Nullable 122 public static JsNode translateWhenExpression(@NotNull JetWhenExpression expression, 123 @NotNull TranslationContext context) { 124 return WhenTranslator.translate(expression, context); 125 } 126 127 //TODO: see if generate*Initializer methods fit somewhere else 128 @NotNull 129 public static JsFunction generateClassInitializerMethod(@NotNull JetClassOrObject classDeclaration, 130 @NotNull TranslationContext context) { 131 ClassInitializerTranslator classInitializerTranslator = new ClassInitializerTranslator(classDeclaration, context); 132 return classInitializerTranslator.generateInitializeMethod(); 133 } 134 135 @NotNull 136 public static JsProgram generateAst(@NotNull BindingContext bindingContext, 137 @NotNull Collection<JetFile> files, @NotNull MainCallParameters mainCallParameters, 138 @NotNull Config config) 139 throws TranslationException { 140 try { 141 return doGenerateAst(bindingContext, files, mainCallParameters, config); 142 } 143 catch (UnsupportedOperationException e) { 144 throw new UnsupportedFeatureException("Unsupported feature used.", e); 145 } 146 catch (Throwable e) { 147 throw new TranslationInternalException(e); 148 } 149 } 150 151 @NotNull 152 private static JsProgram doGenerateAst(@NotNull BindingContext bindingContext, @NotNull Collection<JetFile> files, 153 @NotNull MainCallParameters mainCallParameters, 154 @NotNull Config config) throws MainFunctionNotFoundException { 155 //TODO: move some of the code somewhere 156 StaticContext staticContext = StaticContext.generateStaticContext(bindingContext, config.getTarget()); 157 JsProgram program = staticContext.getProgram(); 158 JsBlock block = program.getGlobalBlock(); 159 160 JsFunction rootFunction = JsAstUtils.createPackage(block.getStatements(), program.getScope()); 161 JsBlock rootBlock = rootFunction.getBody(); 162 List<JsStatement> statements = rootBlock.getStatements(); 163 statements.add(program.getStringLiteral("use strict").makeStmt()); 164 165 TranslationContext context = TranslationContext.rootContext(staticContext); 166 staticContext.getLiteralFunctionTranslator().setRootContext(context); 167 statements.addAll(translateFiles(files, context)); 168 defineModule(context, statements, config.getModuleId()); 169 170 if (mainCallParameters.shouldBeGenerated()) { 171 JsStatement statement = generateCallToMain(context, files, mainCallParameters.arguments()); 172 if (statement != null) { 173 statements.add(statement); 174 } 175 } 176 mayBeGenerateTests(files, config, rootBlock, context); 177 return context.program(); 178 } 179 180 private static void defineModule(@NotNull TranslationContext context, @NotNull List<JsStatement> statements, @NotNull String moduleId) { 181 JsName rootNamespaceName = context.scope().findName(Namer.getRootNamespaceName()); 182 if (rootNamespaceName != null) { 183 statements.add(new JsInvocation(context.namer().kotlin("defineModule"), context.program().getStringLiteral(moduleId), 184 rootNamespaceName.makeRef()).makeStmt()); 185 } 186 } 187 188 private static void mayBeGenerateTests(@NotNull Collection<JetFile> files, @NotNull Config config, 189 @NotNull JsBlock rootBlock, @NotNull TranslationContext context) { 190 JSTester tester = config.getTester(); 191 if (tester != null) { 192 tester.initialize(context, rootBlock); 193 JSTestGenerator.generateTestCalls(context, files, tester); 194 tester.deinitialize(); 195 } 196 } 197 198 //TODO: determine whether should throw exception 199 @Nullable 200 private static JsStatement generateCallToMain(@NotNull TranslationContext context, @NotNull Collection<JetFile> files, 201 @NotNull List<String> arguments) throws MainFunctionNotFoundException { 202 JetNamedFunction mainFunction = getMainFunction(files); 203 if (mainFunction == null) { 204 return null; 205 } 206 JsInvocation translatedCall = generateInvocation(context, mainFunction); 207 setArguments(context, arguments, translatedCall); 208 return translatedCall.makeStmt(); 209 } 210 211 212 @NotNull 213 private static JsInvocation generateInvocation(@NotNull TranslationContext context, @NotNull JetNamedFunction mainFunction) { 214 FunctionDescriptor functionDescriptor = getFunctionDescriptor(context.bindingContext(), mainFunction); 215 JsExpression translatedCall = CallBuilder.build(context).descriptor(functionDescriptor).translate(); 216 assert translatedCall instanceof JsInvocation; 217 return (JsInvocation) translatedCall; 218 } 219 220 private static void setArguments(@NotNull TranslationContext context, @NotNull List<String> arguments, 221 @NotNull JsInvocation translatedCall) { 222 JsArrayLiteral arrayLiteral = new JsArrayLiteral(toStringLiteralList(arguments, context.program())); 223 JsAstUtils.setArguments(translatedCall, Collections.<JsExpression>singletonList(arrayLiteral)); 224 } 225}