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