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 017package org.jetbrains.k2js.translate.declaration; 018 019import com.google.dart.compiler.backend.js.ast.*; 020import com.intellij.util.SmartList; 021import org.jetbrains.annotations.NotNull; 022import org.jetbrains.annotations.Nullable; 023import org.jetbrains.jet.lang.descriptors.ClassDescriptor; 024import org.jetbrains.jet.lang.descriptors.ClassKind; 025import org.jetbrains.jet.lang.descriptors.PropertyDescriptor; 026import org.jetbrains.jet.lang.psi.JetClassOrObject; 027import org.jetbrains.jet.lang.psi.JetObjectLiteralExpression; 028import org.jetbrains.jet.lang.psi.JetParameter; 029import org.jetbrains.jet.lang.types.JetType; 030import org.jetbrains.k2js.translate.context.Namer; 031import org.jetbrains.k2js.translate.context.TranslationContext; 032import org.jetbrains.k2js.translate.general.AbstractTranslator; 033import org.jetbrains.k2js.translate.general.Translation; 034import org.jetbrains.k2js.translate.utils.AnnotationsUtils; 035 036import java.util.ArrayList; 037import java.util.Collection; 038import java.util.Collections; 039import java.util.List; 040 041import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getClassDescriptorForType; 042import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isNotAny; 043import static org.jetbrains.k2js.translate.utils.BindingUtils.getClassDescriptor; 044import static org.jetbrains.k2js.translate.utils.BindingUtils.getPropertyDescriptorForConstructorParameter; 045import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getContainingClass; 046import static org.jetbrains.k2js.translate.utils.PsiUtils.getPrimaryConstructorParameters; 047import static org.jetbrains.k2js.translate.utils.TranslationUtils.getQualifiedReference; 048 049/** 050 * Generates a definition of a single class. 051 */ 052public 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}