Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jetbrains.k2js.translate.declaration.ClassTranslator Maven / Gradle / Ivy
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.k2js.translate.declaration;
import com.google.dart.compiler.backend.js.ast.*;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.backend.common.CodegenUtil;
import org.jetbrains.jet.codegen.bridges.Bridge;
import org.jetbrains.jet.codegen.bridges.BridgesPackage;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.JetClassOrObject;
import org.jetbrains.jet.lang.psi.JetObjectDeclaration;
import org.jetbrains.jet.lang.psi.JetParameter;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypeConstructor;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.k2js.translate.context.DefinitionPlace;
import org.jetbrains.k2js.translate.context.Namer;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.declaration.propertyTranslator.PropertyTranslatorPackage;
import org.jetbrains.k2js.translate.expression.ExpressionPackage;
import org.jetbrains.k2js.translate.general.AbstractTranslator;
import org.jetbrains.k2js.translate.initializer.ClassInitializerTranslator;
import org.jetbrains.k2js.translate.utils.JsAstUtils;
import org.jetbrains.k2js.translate.utils.UtilsPackage;
import java.util.*;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.*;
import static org.jetbrains.jet.lang.types.TypeUtils.topologicallySortSuperclassesAndRecordAllInstances;
import static org.jetbrains.k2js.translate.reference.ReferenceTranslator.translateAsFQReference;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getClassDescriptor;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getPropertyDescriptorForConstructorParameter;
import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.*;
import static org.jetbrains.k2js.translate.utils.PsiUtils.getPrimaryConstructorParameters;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.simpleReturnFunction;
import static org.jetbrains.k2js.translate.utils.UtilsPackage.*;
/**
* Generates a definition of a single class.
*/
public final class ClassTranslator extends AbstractTranslator {
@NotNull
private final JetClassOrObject classDeclaration;
@NotNull
private final ClassDescriptor descriptor;
@NotNull
public static JsInvocation generateClassCreation(@NotNull JetClassOrObject classDeclaration, @NotNull TranslationContext context) {
return new ClassTranslator(classDeclaration, context).translate();
}
@NotNull
public static JsExpression generateObjectLiteral(@NotNull JetObjectDeclaration objectDeclaration, @NotNull TranslationContext context) {
return new ClassTranslator(objectDeclaration, context).translateObjectLiteralExpression();
}
private ClassTranslator(@NotNull JetClassOrObject classDeclaration, @NotNull TranslationContext context) {
super(context);
this.classDeclaration = classDeclaration;
this.descriptor = getClassDescriptor(context.bindingContext(), classDeclaration);
}
@NotNull
private JsExpression translateObjectLiteralExpression() {
ClassDescriptor containingClass = getContainingClass(descriptor);
if (containingClass == null) {
return translate(context());
}
return translateObjectInsideClass(context());
}
@NotNull
public JsInvocation translate() {
return translate(context());
}
@NotNull
public JsInvocation translate(@NotNull TranslationContext declarationContext) {
return new JsInvocation(context().namer().classCreateInvocation(descriptor), getClassCreateInvocationArguments(declarationContext));
}
private boolean isTrait() {
return descriptor.getKind().equals(ClassKind.TRAIT);
}
@NotNull
private List getClassCreateInvocationArguments(@NotNull TranslationContext declarationContext) {
List invocationArguments = new ArrayList();
List properties = new SmartList();
List staticProperties = new SmartList();
boolean isTopLevelDeclaration = context() == declarationContext;
JsNameRef qualifiedReference = null;
if (isTopLevelDeclaration) {
DefinitionPlace definitionPlace = null;
if (!descriptor.getKind().isSingleton() && !isAnonymousObject(descriptor)) {
qualifiedReference = declarationContext.getQualifiedReference(descriptor);
JsScope scope = context().getScopeForDescriptor(descriptor);
definitionPlace = new DefinitionPlace((JsObjectScope) scope, qualifiedReference, staticProperties);
}
declarationContext = declarationContext.newDeclaration(descriptor, definitionPlace);
}
declarationContext = fixContextForClassObjectAccessing(declarationContext);
invocationArguments.add(getSuperclassReferences(declarationContext));
DelegationTranslator delegationTranslator = new DelegationTranslator(classDeclaration, context());
if (!isTrait()) {
JsFunction initializer = new ClassInitializerTranslator(classDeclaration, declarationContext).generateInitializeMethod(delegationTranslator);
invocationArguments.add(initializer.getBody().getStatements().isEmpty() ? JsLiteral.NULL : initializer);
}
translatePropertiesAsConstructorParameters(declarationContext, properties);
DeclarationBodyVisitor bodyVisitor = new DeclarationBodyVisitor(properties, staticProperties);
bodyVisitor.traverseContainer(classDeclaration, declarationContext);
delegationTranslator.generateDelegated(properties);
if (KotlinBuiltIns.getInstance().isData(descriptor)) {
new JsDataClassGenerator(classDeclaration, declarationContext, properties).generate();
}
if (isEnumClass(descriptor)) {
JsObjectLiteral enumEntries = new JsObjectLiteral(bodyVisitor.getEnumEntryList(), true);
JsFunction function = simpleReturnFunction(declarationContext.getScopeForDescriptor(descriptor), enumEntries);
invocationArguments.add(function);
}
generatedBridgeMethods(properties);
boolean hasStaticProperties = !staticProperties.isEmpty();
if (!properties.isEmpty() || hasStaticProperties) {
if (properties.isEmpty()) {
invocationArguments.add(JsLiteral.NULL);
}
else {
if (qualifiedReference != null) {
// about "prototype" - see http://code.google.com/p/jsdoc-toolkit/wiki/TagLends
invocationArguments.add(new JsDocComment(JsAstUtils.LENDS_JS_DOC_TAG, new JsNameRef("prototype", qualifiedReference)));
}
invocationArguments.add(new JsObjectLiteral(properties, true));
}
}
if (hasStaticProperties) {
invocationArguments.add(new JsDocComment(JsAstUtils.LENDS_JS_DOC_TAG, qualifiedReference));
invocationArguments.add(new JsObjectLiteral(staticProperties, true));
}
return invocationArguments;
}
private TranslationContext fixContextForClassObjectAccessing(TranslationContext declarationContext) {
// In Kotlin we can access to class object members without qualifier just by name, but we should translate it to access with FQ name.
// So create alias for class object receiver parameter.
ClassDescriptor classObjectDescriptor = descriptor.getClassObjectDescriptor();
if (classObjectDescriptor != null) {
JsExpression referenceToClass = translateAsFQReference(classObjectDescriptor.getContainingDeclaration(), declarationContext);
JsExpression classObjectAccessor = Namer.getClassObjectAccessor(referenceToClass);
ReceiverParameterDescriptor classObjectReceiver = getReceiverParameterForDeclaration(classObjectDescriptor);
declarationContext.aliasingContext().registerAlias(classObjectReceiver, classObjectAccessor);
}
// Overlap alias of class object receiver for accessing from containing class(see previous if block),
// because inside class object we should use simple name for access.
if (descriptor.getKind() == ClassKind.CLASS_OBJECT) {
declarationContext = declarationContext.innerContextWithAliased(descriptor.getThisAsReceiverParameter(), JsLiteral.THIS);
}
return declarationContext;
}
private JsExpression getSuperclassReferences(@NotNull TranslationContext declarationContext) {
List superClassReferences = getSupertypesNameReferences();
if (superClassReferences.isEmpty()) {
return JsLiteral.NULL;
} else {
return simpleReturnFunction(declarationContext.scope(), new JsArrayLiteral(superClassReferences));
}
}
@NotNull
private List getSupertypesNameReferences() {
List supertypes = getSupertypesWithoutFakes(descriptor);
if (supertypes.isEmpty()) {
return Collections.emptyList();
}
if (supertypes.size() == 1) {
JetType type = supertypes.get(0);
ClassDescriptor supertypeDescriptor = getClassDescriptorForType(type);
return Collections.singletonList(getClassReference(supertypeDescriptor));
}
Set supertypeConstructors = new HashSet();
for (JetType type : supertypes) {
supertypeConstructors.add(type.getConstructor());
}
List sortedAllSuperTypes = topologicallySortSuperclassesAndRecordAllInstances(descriptor.getDefaultType(),
new HashMap>(),
new HashSet());
List supertypesRefs = new ArrayList();
for (TypeConstructor typeConstructor : sortedAllSuperTypes) {
if (supertypeConstructors.contains(typeConstructor)) {
ClassDescriptor supertypeDescriptor = getClassDescriptorForTypeConstructor(typeConstructor);
supertypesRefs.add(getClassReference(supertypeDescriptor));
}
}
return supertypesRefs;
}
@NotNull
private JsNameRef getClassReference(@NotNull ClassDescriptor superClassDescriptor) {
return context().getQualifiedReference(superClassDescriptor);
}
private void translatePropertiesAsConstructorParameters(@NotNull TranslationContext classDeclarationContext,
@NotNull List result) {
for (JetParameter parameter : getPrimaryConstructorParameters(classDeclaration)) {
PropertyDescriptor descriptor = getPropertyDescriptorForConstructorParameter(bindingContext(), parameter);
if (descriptor != null) {
PropertyTranslatorPackage.translateAccessors(descriptor, result, classDeclarationContext);
}
}
}
@NotNull
private JsExpression translateObjectInsideClass(@NotNull TranslationContext outerClassContext) {
JsFunction fun = new JsFunction(outerClassContext.scope(), new JsBlock(), "initializer for " + descriptor.getName().asString());
TranslationContext funContext = outerClassContext.newFunctionBodyWithUsageTracker(fun, descriptor);
fun.getBody().getStatements().add(new JsReturn(translate(funContext)));
return ExpressionPackage.withCapturedParameters(fun, funContext, outerClassContext, descriptor);
}
private void generatedBridgeMethods(@NotNull List properties) {
if (isTrait()) return;
generateBridgesToTraitImpl(properties);
generateOtherBridges(properties);
}
private void generateBridgesToTraitImpl(List properties) {
for(Map.Entry entry : CodegenUtil.getTraitMethods(descriptor).entrySet()) {
if (!areNamesEqual(entry.getKey(), entry.getValue())) {
properties.add(generateDelegateCall(entry.getValue(), entry.getKey(), JsLiteral.THIS, context()));
}
}
}
private void generateOtherBridges(List properties) {
for (DeclarationDescriptor memberDescriptor : descriptor.getDefaultType().getMemberScope().getAllDescriptors()) {
if (memberDescriptor instanceof FunctionDescriptor) {
FunctionDescriptor functionDescriptor = (FunctionDescriptor) memberDescriptor;
Set> bridgesToGenerate =
BridgesPackage.generateBridgesForFunctionDescriptor(functionDescriptor, UtilsPackage.getID());
for (Bridge bridge : bridgesToGenerate) {
generateBridge(bridge, properties);
}
}
}
}
private void generateBridge(
@NotNull Bridge bridge,
@NotNull List properties
) {
FunctionDescriptor fromDescriptor = bridge.getFrom();
FunctionDescriptor toDescriptor = bridge.getTo();
if (areNamesEqual(fromDescriptor, toDescriptor)) return;
if (fromDescriptor.getKind().isReal() &&
fromDescriptor.getModality() != Modality.ABSTRACT &&
!toDescriptor.getKind().isReal()) return;
properties.add(generateDelegateCall(fromDescriptor, toDescriptor, JsLiteral.THIS, context()));
}
private boolean areNamesEqual(@NotNull FunctionDescriptor first, @NotNull FunctionDescriptor second) {
JsName firstName = context().getNameForDescriptor(first);
JsName secondName = context().getNameForDescriptor(second);
return firstName.getIdent().equals(secondName.getIdent());
}
}