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.declaration;
018
019 import com.google.dart.compiler.backend.js.ast.*;
020 import com.intellij.util.SmartList;
021 import org.jetbrains.annotations.NotNull;
022 import org.jetbrains.annotations.Nullable;
023 import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
024 import org.jetbrains.jet.lang.descriptors.ClassKind;
025 import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
026 import org.jetbrains.jet.lang.psi.JetClassOrObject;
027 import org.jetbrains.jet.lang.psi.JetObjectLiteralExpression;
028 import org.jetbrains.jet.lang.psi.JetParameter;
029 import org.jetbrains.jet.lang.types.JetType;
030 import org.jetbrains.k2js.translate.context.Namer;
031 import org.jetbrains.k2js.translate.context.TranslationContext;
032 import org.jetbrains.k2js.translate.general.AbstractTranslator;
033 import org.jetbrains.k2js.translate.general.Translation;
034 import org.jetbrains.k2js.translate.utils.AnnotationsUtils;
035
036 import java.util.ArrayList;
037 import java.util.Collection;
038 import java.util.Collections;
039 import java.util.List;
040
041 import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getClassDescriptorForType;
042 import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isNotAny;
043 import static org.jetbrains.k2js.translate.utils.BindingUtils.getClassDescriptor;
044 import static org.jetbrains.k2js.translate.utils.BindingUtils.getPropertyDescriptorForConstructorParameter;
045 import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getContainingClass;
046 import static org.jetbrains.k2js.translate.utils.PsiUtils.getPrimaryConstructorParameters;
047 import static org.jetbrains.k2js.translate.utils.TranslationUtils.getQualifiedReference;
048
049 /**
050 * Generates a definition of a single class.
051 */
052 public final class ClassTranslator extends AbstractTranslator {
053 @NotNull
054 private final DeclarationBodyVisitor declarationBodyVisitor = new DeclarationBodyVisitor();
055
056 @NotNull
057 private final JetClassOrObject classDeclaration;
058
059 @NotNull
060 private final ClassDescriptor descriptor;
061
062 @Nullable
063 private final ClassAliasingMap aliasingMap;
064
065 @NotNull
066 public static JsExpression generateClassCreation(@NotNull JetClassOrObject classDeclaration,
067 @NotNull ClassAliasingMap aliasingMap,
068 @NotNull TranslationContext context) {
069 return new ClassTranslator(classDeclaration, aliasingMap, context).translateClassOrObjectCreation(context);
070 }
071
072 @NotNull
073 public static JsExpression generateClassCreation(@NotNull JetClassOrObject classDeclaration, @NotNull TranslationContext context) {
074 return new ClassTranslator(classDeclaration, null, context).translateClassOrObjectCreation(context);
075 }
076
077 @NotNull
078 public static JsExpression generateClassCreation(@NotNull JetClassOrObject classDeclaration,
079 @NotNull ClassDescriptor descriptor,
080 @NotNull TranslationContext context) {
081 return new ClassTranslator(classDeclaration, descriptor, null, context).translateClassOrObjectCreation(context);
082 }
083
084 @NotNull
085 public static JsExpression generateObjectLiteral(@NotNull JetObjectLiteralExpression objectLiteralExpression,
086 @NotNull TranslationContext context) {
087 return new ClassTranslator(objectLiteralExpression.getObjectDeclaration(), null, context).translateObjectLiteralExpression();
088 }
089
090 ClassTranslator(@NotNull JetClassOrObject classDeclaration,
091 @Nullable ClassAliasingMap aliasingMap,
092 @NotNull TranslationContext context) {
093 this(classDeclaration, getClassDescriptor(context.bindingContext(), classDeclaration), aliasingMap, context);
094 }
095
096 ClassTranslator(@NotNull JetClassOrObject classDeclaration,
097 @NotNull ClassDescriptor descriptor,
098 @Nullable ClassAliasingMap aliasingMap,
099 @NotNull TranslationContext context) {
100 super(context);
101 this.aliasingMap = aliasingMap;
102 this.descriptor = descriptor;
103 this.classDeclaration = classDeclaration;
104 }
105
106 @NotNull
107 private JsExpression translateObjectLiteralExpression() {
108 ClassDescriptor containingClass = getContainingClass(descriptor);
109 if (containingClass == null) {
110 return translateClassOrObjectCreation(context());
111 }
112 return translateAsObjectCreationExpressionWithEnclosingThisSaved(containingClass);
113 }
114
115 @NotNull
116 private JsExpression translateAsObjectCreationExpressionWithEnclosingThisSaved(@NotNull ClassDescriptor containingClass) {
117 return context().literalFunctionTranslator().translate(containingClass, classDeclaration, descriptor, this);
118 }
119
120 @NotNull
121 public JsExpression translateClassOrObjectCreation(@NotNull TranslationContext declarationContext) {
122 JsInvocation createInvocation = context().namer().classCreateInvocation(descriptor);
123 translateClassOrObjectCreation(createInvocation, declarationContext);
124 return createInvocation;
125 }
126
127 public void translateClassOrObjectCreation(@NotNull JsInvocation createInvocation) {
128 translateClassOrObjectCreation(createInvocation, context());
129 }
130
131 private void translateClassOrObjectCreation(@NotNull JsInvocation createInvocation, @NotNull TranslationContext context) {
132 addSuperclassReferences(createInvocation);
133 addClassOwnDeclarations(createInvocation, context);
134 }
135
136 private boolean isTrait() {
137 return descriptor.getKind().equals(ClassKind.TRAIT);
138 }
139
140 private void addClassOwnDeclarations(@NotNull JsInvocation jsClassDeclaration, @NotNull TranslationContext classDeclarationContext) {
141 JsObjectLiteral properties = new JsObjectLiteral(true);
142 List<JsPropertyInitializer> propertyList = properties.getPropertyInitializers();
143 if (!isTrait()) {
144 JsFunction initializer = Translation.generateClassInitializerMethod(classDeclaration, classDeclarationContext);
145 if (context().isEcma5()) {
146 jsClassDeclaration.getArguments().add(initializer.getBody().getStatements().isEmpty() ? JsLiteral.NULL : initializer);
147 }
148 else {
149 propertyList.add(new JsPropertyInitializer(Namer.initializeMethodReference(), initializer));
150 }
151 }
152
153 propertyList.addAll(translatePropertiesAsConstructorParameters(classDeclarationContext));
154 propertyList.addAll(declarationBodyVisitor.traverseClass(classDeclaration, classDeclarationContext));
155
156 if (!propertyList.isEmpty() || !context().isEcma5()) {
157 jsClassDeclaration.getArguments().add(properties);
158 }
159 }
160
161 private void addSuperclassReferences(@NotNull JsInvocation jsClassDeclaration) {
162 List<JsExpression> superClassReferences = getSupertypesNameReferences();
163 List<JsExpression> expressions = jsClassDeclaration.getArguments();
164 if (context().isEcma5()) {
165 if (superClassReferences.isEmpty()) {
166 jsClassDeclaration.getArguments().add(JsLiteral.NULL);
167 return;
168 }
169 else if (superClassReferences.size() > 1) {
170 JsArrayLiteral arrayLiteral = new JsArrayLiteral();
171 jsClassDeclaration.getArguments().add(arrayLiteral);
172 expressions = arrayLiteral.getExpressions();
173 }
174 }
175
176 for (JsExpression superClassReference : superClassReferences) {
177 expressions.add(superClassReference);
178 }
179 }
180
181 @NotNull
182 private List<JsExpression> getSupertypesNameReferences() {
183 Collection<JetType> supertypes = descriptor.getTypeConstructor().getSupertypes();
184 if (supertypes.isEmpty()) {
185 return Collections.emptyList();
186 }
187
188 JsExpression base = null;
189 List<JsExpression> list = null;
190 for (JetType type : supertypes) {
191 ClassDescriptor result = getClassDescriptorForType(type);
192 if (isNotAny(result) && !AnnotationsUtils.isNativeObject(result)) {
193 switch (result.getKind()) {
194 case CLASS:
195 base = getClassReference(result);
196 break;
197 case TRAIT:
198 if (list == null) {
199 list = new ArrayList<JsExpression>();
200 }
201 list.add(getClassReference(result));
202 break;
203
204 default:
205 throw new UnsupportedOperationException("unsupported super class kind " + result.getKind().name());
206 }
207 }
208 }
209
210 if (list == null) {
211 return base == null ? Collections.<JsExpression>emptyList() : Collections.singletonList(base);
212 }
213 else if (base != null) {
214 list.add(0, base);
215 }
216
217 return list;
218 }
219
220 @NotNull
221 private JsExpression getClassReference(@NotNull ClassDescriptor superClassDescriptor) {
222 // aliasing here is needed for the declaration generation step
223 if (aliasingMap != null) {
224 JsNameRef name = aliasingMap.get(superClassDescriptor, descriptor);
225 if (name != null) {
226 return name;
227 }
228 }
229
230 // from library
231 return getQualifiedReference(context(), superClassDescriptor);
232 }
233
234 @NotNull
235 private List<JsPropertyInitializer> translatePropertiesAsConstructorParameters(@NotNull TranslationContext classDeclarationContext) {
236 List<JetParameter> parameters = getPrimaryConstructorParameters(classDeclaration);
237 if (parameters.isEmpty()) {
238 return Collections.emptyList();
239 }
240
241 List<JsPropertyInitializer> result = new SmartList<JsPropertyInitializer>();
242 for (JetParameter parameter : parameters) {
243 PropertyDescriptor descriptor = getPropertyDescriptorForConstructorParameter(bindingContext(), parameter);
244 if (descriptor != null) {
245 PropertyTranslator.translateAccessors(descriptor, result, classDeclarationContext);
246 }
247 }
248 return result;
249 }
250 }