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 com.intellij.openapi.util.NotNullLazyValue;
021    import com.intellij.openapi.util.Trinity;
022    import com.intellij.util.containers.Stack;
023    import org.jetbrains.annotations.NotNull;
024    import org.jetbrains.annotations.Nullable;
025    import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
026    import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor;
027    import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
028    import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
029    import org.jetbrains.jet.lang.psi.JetClassBody;
030    import org.jetbrains.jet.lang.psi.JetClassOrObject;
031    import org.jetbrains.jet.lang.psi.JetDeclarationWithBody;
032    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
033    import org.jetbrains.k2js.translate.LabelGenerator;
034    import org.jetbrains.k2js.translate.context.AliasingContext;
035    import org.jetbrains.k2js.translate.context.Namer;
036    import org.jetbrains.k2js.translate.context.TranslationContext;
037    import org.jetbrains.k2js.translate.context.UsageTracker;
038    import org.jetbrains.k2js.translate.declaration.ClassTranslator;
039    import org.jetbrains.k2js.translate.general.AbstractTranslator;
040    
041    import java.util.List;
042    
043    import static org.jetbrains.k2js.translate.utils.FunctionBodyTranslator.translateFunctionBody;
044    import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getExpectedReceiverDescriptor;
045    
046    public class LiteralFunctionTranslator extends AbstractTranslator {
047        private final Stack<NotNullLazyValue<Trinity<List<JsPropertyInitializer>, LabelGenerator, JsExpression>>> definitionPlaces =
048                new Stack<NotNullLazyValue<Trinity<List<JsPropertyInitializer>, LabelGenerator, JsExpression>>>();
049        private NotNullLazyValue<Trinity<List<JsPropertyInitializer>, LabelGenerator, JsExpression>> definitionPlace;
050    
051        public LiteralFunctionTranslator(@NotNull TranslationContext context) {
052            super(context);
053        }
054    
055        public static Trinity<List<JsPropertyInitializer>, LabelGenerator, JsExpression> createPlace(@NotNull List<JsPropertyInitializer> list,
056                @NotNull JsExpression reference) {
057            return Trinity.create(list, new LabelGenerator('f'), reference);
058        }
059    
060        public void setDefinitionPlace(@Nullable NotNullLazyValue<Trinity<List<JsPropertyInitializer>, LabelGenerator, JsExpression>> place) {
061            if (place == null) {
062                definitionPlaces.pop();
063                definitionPlace = definitionPlaces.isEmpty() ? null : definitionPlaces.peek();
064            }
065            else {
066                definitionPlaces.push(place);
067                definitionPlace = place;
068            }
069        }
070    
071        public JsExpression translate(@NotNull JetDeclarationWithBody declaration, @NotNull FunctionDescriptor descriptor, @NotNull TranslationContext outerContext) {
072            JsFunction fun = createFunction();
073            TranslationContext funContext;
074            boolean asInner;
075            ClassDescriptor outerClass;
076            AliasingContext aliasingContext;
077            DeclarationDescriptor receiverDescriptor = getExpectedReceiverDescriptor(descriptor);
078            JsName receiverName;
079            if (receiverDescriptor == null) {
080                receiverName = null;
081                aliasingContext = null;
082            }
083            else {
084                receiverName = fun.getScope().declareName(Namer.getReceiverParameterName());
085                aliasingContext = outerContext.aliasingContext().inner(receiverDescriptor, receiverName.makeRef());
086            }
087    
088            if (descriptor.getContainingDeclaration() instanceof ConstructorDescriptor) {
089                // KT-2388
090                asInner = true;
091                fun.setName(fun.getScope().declareName(Namer.CALLEE_NAME));
092                outerClass = (ClassDescriptor) descriptor.getContainingDeclaration().getContainingDeclaration();
093                assert outerClass != null;
094    
095                if (receiverDescriptor == null) {
096                    aliasingContext = outerContext.aliasingContext().notShareableThisAliased(outerClass, new JsNameRef("o", fun.getName().makeRef()));
097                }
098            }
099            else {
100                outerClass = null;
101                asInner = DescriptorUtils.isTopLevelDeclaration(descriptor);
102            }
103    
104            funContext = outerContext.newFunctionBody(fun, aliasingContext,
105                                                      new UsageTracker(descriptor, outerContext.usageTracker(), outerClass));
106    
107            fun.getBody().getStatements().addAll(translateFunctionBody(descriptor, declaration, funContext).getStatements());
108    
109            InnerFunctionTranslator translator = null;
110            if (!asInner) {
111                translator = new InnerFunctionTranslator(descriptor, funContext, fun);
112            }
113    
114            if (asInner) {
115                addRegularParameters(descriptor, fun, funContext, receiverName);
116                if (outerClass != null) {
117                    UsageTracker usageTracker = funContext.usageTracker();
118                    assert usageTracker != null;
119                    if (usageTracker.isUsed()) {
120                        return new JsInvocation(context().namer().kotlin("assignOwner"), fun, JsLiteral.THIS);
121                    }
122                    else {
123                        fun.setName(null);
124                    }
125                }
126    
127                return fun;
128            }
129    
130            JsExpression result = translator.translate(createReference(fun), outerContext);
131            addRegularParameters(descriptor, fun, funContext, receiverName);
132            return result;
133        }
134    
135        private JsNameRef createReference(JsFunction fun) {
136            Trinity<List<JsPropertyInitializer>, LabelGenerator, JsExpression> place = definitionPlace.getValue();
137            JsNameRef nameRef = new JsNameRef(place.second.generate(), place.third);
138            place.first.add(new JsPropertyInitializer(nameRef, fun));
139            return nameRef;
140        }
141    
142        private static void addRegularParameters(
143                @NotNull FunctionDescriptor descriptor,
144                @NotNull JsFunction fun,
145                @NotNull TranslationContext funContext,
146                @Nullable JsName receiverName
147        ) {
148            if (receiverName != null) {
149                fun.getParameters().add(new JsParameter(receiverName));
150            }
151            FunctionTranslator.addParameters(fun.getParameters(), descriptor, funContext);
152        }
153    
154        private JsFunction createFunction() {
155            return new JsFunction(context().scope(), new JsBlock());
156        }
157    
158        public JsExpression translate(
159                @NotNull ClassDescriptor outerClass,
160                @NotNull TranslationContext outerClassContext,
161                @NotNull JetClassOrObject declaration,
162                @NotNull ClassDescriptor descriptor,
163                @NotNull ClassTranslator classTranslator
164        ) {
165            JsFunction fun = createFunction();
166            JsNameRef outerClassRef = fun.getScope().declareName(Namer.OUTER_CLASS_NAME).makeRef();
167            UsageTracker usageTracker = new UsageTracker(descriptor, outerClassContext.usageTracker(), outerClass);
168            AliasingContext aliasingContext = outerClassContext.aliasingContext().inner(outerClass, outerClassRef);
169            TranslationContext funContext = outerClassContext.newFunctionBody(fun, aliasingContext, usageTracker);
170    
171            fun.getBody().getStatements().add(new JsReturn(classTranslator.translate(funContext)));
172            JetClassBody body = declaration.getBody();
173            assert body != null;
174            return new InnerObjectTranslator(funContext, fun).translate(createReference(fun), usageTracker.isUsed() ? outerClassRef : null);
175        }
176    }