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