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.expression;
018    
019    import com.google.dart.compiler.backend.js.ast.*;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
022    import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor;
023    import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
024    import org.jetbrains.jet.lang.psi.JetClassOrObject;
025    import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression;
026    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
027    import org.jetbrains.k2js.translate.LabelGenerator;
028    import org.jetbrains.k2js.translate.context.Namer;
029    import org.jetbrains.k2js.translate.context.TraceableThisAliasProvider;
030    import org.jetbrains.k2js.translate.context.TranslationContext;
031    import org.jetbrains.k2js.translate.declaration.ClassTranslator;
032    
033    import java.util.ArrayList;
034    import java.util.List;
035    
036    import static com.google.dart.compiler.backend.js.ast.JsVars.JsVar;
037    import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptor;
038    import static org.jetbrains.k2js.translate.utils.FunctionBodyTranslator.translateFunctionBody;
039    
040    // todo easy incremental compiler implementation - generated functions should be inside corresponding class/namespace definition
041    public class LiteralFunctionTranslator {
042        private final List<JsPropertyInitializer> properties = new ArrayList<JsPropertyInitializer>();
043        private final LabelGenerator labelGenerator = new LabelGenerator('f');
044        private final JsNameRef containingVarRef = new JsNameRef("_f");
045    
046        private TranslationContext rootContext;
047    
048        public void setRootContext(@NotNull TranslationContext rootContext) {
049            assert this.rootContext == null;
050            this.rootContext = rootContext;
051            JsName containingVarName = rootContext.scope().declareName(containingVarRef.getIdent());
052            containingVarRef.resolve(containingVarName);
053        }
054    
055        public JsVar getDeclaration() {
056            return new JsVar(containingVarRef.getName(), properties.isEmpty() ? null : new JsObjectLiteral(properties, true));
057        }
058    
059        public JsExpression translate(@NotNull JetFunctionLiteralExpression declaration) {
060            FunctionDescriptor descriptor = getFunctionDescriptor(rootContext.bindingContext(), declaration.getFunctionLiteral());
061    
062            JsFunction fun = createFunction();
063            TranslationContext funContext;
064            boolean asInner;
065            if (descriptor.getContainingDeclaration() instanceof ConstructorDescriptor) {
066                // KT-2388
067                asInner = true;
068                fun.setName(fun.getScope().declareName(Namer.CALLEE_NAME));
069                ClassDescriptor classDescriptor = (ClassDescriptor) descriptor.getContainingDeclaration().getContainingDeclaration();
070                assert classDescriptor != null;
071                funContext = createThisTraceableContext(classDescriptor, fun, new JsNameRef("o", fun.getName().makeRef()));
072            }
073            else {
074                asInner = DescriptorUtils.isTopLevelDeclaration(descriptor);
075                funContext = rootContext.contextWithScope(fun);
076            }
077    
078            fun.getBody().getStatements().addAll(translateFunctionBody(descriptor, declaration.getFunctionLiteral(), funContext).getStatements());
079    
080            InnerFunctionTranslator translator = null;
081            if (!asInner) {
082                translator = new InnerFunctionTranslator(declaration, descriptor, funContext, fun);
083                if (translator.isLocalVariablesAffected()) {
084                    asInner = true;
085                }
086            }
087    
088            if (asInner) {
089                FunctionTranslator.addParameters(fun.getParameters(), descriptor, funContext);
090                if (funContext.thisAliasProvider() instanceof TraceableThisAliasProvider) {
091                    TraceableThisAliasProvider provider = (TraceableThisAliasProvider) funContext.thisAliasProvider();
092                    if (provider.wasThisCaptured()) {
093                        return new JsInvocation(rootContext.namer().kotlin("assignOwner"), fun, JsLiteral.THIS);
094                    }
095                    else {
096                        fun.setName(null);
097                    }
098                }
099    
100    
101                return fun;
102            }
103    
104            return translate(translator, fun);
105        }
106    
107        private JsFunction createFunction() {
108            return new JsFunction(rootContext.scope(), new JsBlock());
109        }
110    
111        public JsExpression translate(@NotNull ClassDescriptor containingClass,
112                @NotNull JetClassOrObject declaration,
113                @NotNull ClassDescriptor descriptor,
114                @NotNull ClassTranslator classTranslator) {
115            JsFunction fun = createFunction();
116            JsName outerThisName = fun.getScope().declareName("$this");
117            TranslationContext funContext = createThisTraceableContext(containingClass, fun, outerThisName.makeRef());
118    
119            fun.getBody().getStatements().add(new JsReturn(classTranslator.translateClassOrObjectCreation(funContext)));
120            return translate(new InnerObjectTranslator(declaration, descriptor, funContext, fun), fun);
121        }
122    
123        private JsExpression translate(@NotNull InnerDeclarationTranslator translator, @NotNull JsFunction fun) {
124            JsNameRef nameRef = new JsNameRef(labelGenerator.generate(), containingVarRef);
125            properties.add(new JsPropertyInitializer(nameRef, fun));
126            return translator.translate(nameRef);
127        }
128    
129        private TranslationContext createThisTraceableContext(@NotNull ClassDescriptor containingClass,
130                @NotNull JsFunction fun,
131                @NotNull JsNameRef thisRef) {
132            return rootContext.contextWithScope(fun, rootContext.aliasingContext().inner(
133                    new TraceableThisAliasProvider(containingClass, thisRef)));
134        }
135    }
136