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    }