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.psi.*;
024    import org.jetbrains.jet.lang.resolve.BindingContext;
025    import org.jetbrains.k2js.config.Config;
026    import org.jetbrains.k2js.facade.MainCallParameters;
027    import org.jetbrains.k2js.facade.exceptions.MainFunctionNotFoundException;
028    import org.jetbrains.k2js.facade.exceptions.TranslationException;
029    import org.jetbrains.k2js.facade.exceptions.TranslationInternalException;
030    import org.jetbrains.k2js.facade.exceptions.UnsupportedFeatureException;
031    import org.jetbrains.k2js.translate.context.Namer;
032    import org.jetbrains.k2js.translate.context.StaticContext;
033    import org.jetbrains.k2js.translate.context.TranslationContext;
034    import org.jetbrains.k2js.translate.declaration.ClassAliasingMap;
035    import org.jetbrains.k2js.translate.declaration.ClassTranslator;
036    import org.jetbrains.k2js.translate.declaration.NamespaceDeclarationTranslator;
037    import org.jetbrains.k2js.translate.expression.ExpressionVisitor;
038    import org.jetbrains.k2js.translate.expression.FunctionTranslator;
039    import org.jetbrains.k2js.translate.expression.PatternTranslator;
040    import org.jetbrains.k2js.translate.expression.WhenTranslator;
041    import org.jetbrains.k2js.translate.initializer.ClassInitializerTranslator;
042    import org.jetbrains.k2js.translate.reference.CallBuilder;
043    import org.jetbrains.k2js.translate.test.JSTestGenerator;
044    import org.jetbrains.k2js.translate.test.JSTester;
045    import org.jetbrains.k2js.translate.utils.JsAstUtils;
046    import org.jetbrains.k2js.translate.utils.dangerous.DangerousData;
047    import org.jetbrains.k2js.translate.utils.dangerous.DangerousTranslator;
048    
049    import java.util.Collection;
050    import java.util.Collections;
051    import java.util.List;
052    
053    import static org.jetbrains.jet.plugin.JetMainDetector.getMainFunction;
054    import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptor;
055    import static org.jetbrains.k2js.translate.utils.JsAstUtils.*;
056    import 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     */
062    public 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    }