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 }