JS backend: add the support secondary constructors

This commit is contained in:
Zalim Bashorov
2015-05-28 11:00:14 +03:00
parent 016534b487
commit 68eca8bc00
16 changed files with 273 additions and 99 deletions
@@ -16,16 +16,13 @@
package org.jetbrains.kotlin.js.descriptorUtils
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.TypeProjection
import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.types.JetType
public val JetType.nameIfStandardType: Name?
get() {
@@ -56,4 +53,6 @@ public fun JetType.getJetTypeFqName(printTypeArguments: Boolean): String {
}
return DescriptorUtils.getFqName(declaration).asString() + typeArgumentsAsString
}
}
public fun ClassDescriptor.hasPrimaryConstructor(): Boolean = getUnsubstitutedPrimaryConstructor() != null
@@ -211,7 +211,12 @@ object ConstructorCallCase : FunctionCallCase {
val functionRef = if (isNative()) fqName else context.aliasOrValue(callableDescriptor) { fqName }
return JsNew(functionRef, argumentsInfo.translateArguments)
if((callableDescriptor as ConstructorDescriptor).isPrimary()) {
return JsNew(functionRef, argumentsInfo.translateArguments)
}
else {
return JsInvocation(functionRef, listOf(JsLiteral.UNDEFINED) + argumentsInfo.translateArguments)
}
}
}
@@ -86,6 +86,7 @@ public final class Namer {
private static final String ROOT_PACKAGE = "_";
private static final String RECEIVER_PARAMETER_NAME = "$receiver";
public static final String ANOTHER_THIS_PARAMETER_NAME = "$this";
private static final String THROW_NPE_FUN_NAME = "throwNPE";
private static final String COMPANION_OBJECT_GETTER = "object";
@@ -98,6 +99,9 @@ public final class Namer {
@NotNull
public static final JsExpression UNDEFINED_EXPRESSION = new JsPrefixOperation(JsUnaryOperator.VOID, JsNumberLiteral.ZERO);
private static final JsNameRef JS_OBJECT = new JsNameRef("Object");
private static final JsNameRef JS_OBJECT_CREATE_FUNCTION = new JsNameRef("create", JS_OBJECT);
public static boolean isUndefined(@NotNull JsExpression expr) {
if (expr instanceof JsPrefixOperation) {
JsUnaryOperator op = ((JsPrefixOperation) expr).getOperator();
@@ -216,6 +220,11 @@ public final class Namer {
return new JsNameRef(APPLY_FUNCTION, functionExpression);
}
@NotNull
public static JsInvocation createObjectWithPrototypeFrom(JsNameRef referenceToClass) {
return new JsInvocation(JS_OBJECT_CREATE_FUNCTION, getRefToPrototype(referenceToClass));
}
@NotNull
public static JsNameRef getCapturedVarAccessor(@NotNull JsExpression ref) {
return new JsNameRef(CAPTURED_VAR_FIELD, ref);
@@ -22,6 +22,7 @@ import com.intellij.openapi.util.Factory;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.ReflectionTypes;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.js.config.Config;
import org.jetbrains.kotlin.js.config.EcmaVersion;
@@ -35,7 +36,6 @@ import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.BindingTrace;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.calls.tasks.TasksPackage;
import org.jetbrains.kotlin.builtins.ReflectionTypes;
import java.util.Map;
@@ -267,7 +267,9 @@ public final class StaticContext {
Rule<JsName> constructorOrCompanionObjectHasTheSameNameAsTheClass = new Rule<JsName>() {
@Override
public JsName apply(@NotNull DeclarationDescriptor descriptor) {
if (descriptor instanceof ConstructorDescriptor || (DescriptorUtils.isCompanionObject(descriptor))) {
if (descriptor instanceof ConstructorDescriptor && ((ConstructorDescriptor) descriptor).isPrimary() ||
DescriptorUtils.isCompanionObject(descriptor)
) {
//noinspection ConstantConditions
return getNameForDescriptor(descriptor.getContainingDeclaration());
}
@@ -20,11 +20,11 @@ import com.google.dart.compiler.backend.js.ast.*;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.ReflectionTypes;
import org.jetbrains.kotlin.descriptors.CallableDescriptor;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.MemberDescriptor;
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor;
import org.jetbrains.kotlin.diagnostics.DiagnosticSink;
import org.jetbrains.kotlin.js.config.Config;
import org.jetbrains.kotlin.js.translate.intrinsic.Intrinsics;
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
@@ -32,7 +32,6 @@ import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.JetExpression;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.BindingTrace;
import org.jetbrains.kotlin.builtins.ReflectionTypes;
import java.util.HashMap;
import java.util.Map;
@@ -130,6 +129,12 @@ public class TranslationContext {
return new TranslationContext(this, staticContext, dynamicContext, aliasingContext, usageTracker, place);
}
@NotNull
public TranslationContext newDeclarationWithScope(@NotNull DeclarationDescriptor descriptor, @NotNull JsScope scope) {
DynamicContext dynamicContext = DynamicContext.newContext(scope, getBlockForDescriptor(descriptor));
return new TranslationContext(this, staticContext, dynamicContext, aliasingContext, usageTracker, this.getDefinitionPlace());
}
@NotNull
private TranslationContext innerWithAliasingContext(AliasingContext aliasingContext) {
return new TranslationContext(this, this.staticContext, this.dynamicContext, aliasingContext, this.usageTracker, null);
@@ -22,30 +22,32 @@ import org.jetbrains.kotlin.backend.common.CodegenUtil
import org.jetbrains.kotlin.backend.common.bridges.Bridge
import org.jetbrains.kotlin.backend.common.bridges.generateBridgesForFunctionDescriptor
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.js.descriptorUtils.hasPrimaryConstructor
import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator
import org.jetbrains.kotlin.js.translate.context.DefinitionPlace
import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.declaration.propertyTranslator.translateAccessors
import org.jetbrains.kotlin.js.translate.expression.FunctionTranslator
import org.jetbrains.kotlin.js.translate.expression.withCapturedParameters
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator
import org.jetbrains.kotlin.js.translate.initializer.ClassInitializerTranslator
import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator.translateAsFQReference
import org.jetbrains.kotlin.js.translate.utils.*
import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getClassDescriptor
import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getPropertyDescriptorForConstructorParameter
import org.jetbrains.kotlin.js.translate.utils.ID
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getReceiverParameterForDeclaration
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getSupertypesWithoutFakes
import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getPrimaryConstructorParameters
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.simpleReturnFunction
import org.jetbrains.kotlin.js.translate.utils.generateDelegateCall
import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.toInvocationWith
import org.jetbrains.kotlin.psi.JetClass
import org.jetbrains.kotlin.psi.JetClass
import org.jetbrains.kotlin.psi.JetClassOrObject
import org.jetbrains.kotlin.psi.JetObjectDeclaration
import org.jetbrains.kotlin.psi.JetSecondaryConstructor
import org.jetbrains.kotlin.resolve.BindingContextUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils.*
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.TypeConstructor
@@ -71,7 +73,7 @@ public class ClassTranslator private constructor(
return translateObjectInsideClass(context())
}
public fun translate(declarationContext: TranslationContext = context()): JsInvocation {
private fun translate(declarationContext: TranslationContext = context()): JsInvocation {
return JsInvocation(context().namer().classCreateInvocation(descriptor), getClassCreateInvocationArguments(declarationContext))
}
@@ -279,6 +281,10 @@ public class ClassTranslator private constructor(
result.add(JsPropertyInitializer(classNameRef, classCreation))
classDeclaration.getSecondaryConstructors().forEach {
result.add(generateSecondaryConstructor(it, context))
}
return result
}
@@ -291,5 +297,54 @@ public class ClassTranslator private constructor(
public fun generateObjectLiteral(objectDeclaration: JetObjectDeclaration, context: TranslationContext): JsExpression {
return ClassTranslator(objectDeclaration, context).translateObjectLiteralExpression()
}
private fun generateSecondaryConstructor(constructor: JetSecondaryConstructor, context: TranslationContext): JsPropertyInitializer {
val constructorDescriptor = BindingUtils.getDescriptorForElement(context.bindingContext(), constructor) as ConstructorDescriptor
val classDescriptor = constructorDescriptor.getContainingDeclaration()
val constructorScope = context.getScopeForDescriptor(constructorDescriptor)
val thisName = constructorScope.declareName(Namer.ANOTHER_THIS_PARAMETER_NAME)
val thisNameRef = thisName.makeRef()
val receiverDescriptor = JsDescriptorUtils.getReceiverParameterForDeclaration(classDescriptor)
val translationContext = context.innerContextWithAliased(receiverDescriptor, thisNameRef)
val constructorInitializer = FunctionTranslator.newInstance(constructor, translationContext).translateAsMethod()
val constructorFunction = constructorInitializer.getValueExpr() as JsFunction
constructorFunction.getParameters().add(0, JsParameter(thisName))
val referenceToClass = context.getQualifiedReference(classDescriptor)
val forAddToBeginning: List<JsStatement> =
with(arrayListOf<JsStatement>()) {
addAll(FunctionBodyTranslator.setDefaultValueForArguments(constructorDescriptor, context))
val createInstance = Namer.createObjectWithPrototypeFrom(referenceToClass)
val instanceVar = JsAstUtils.assignment(thisNameRef, JsAstUtils.or(thisNameRef, createInstance)).makeStmt()
add(instanceVar)
val resolvedCall = BindingContextUtils.getDelegationConstructorCall(context.bindingContext(), constructorDescriptor)
val delegationClassDescriptor = resolvedCall?.getResultingDescriptor()?.getContainingDeclaration()
if (resolvedCall != null && !KotlinBuiltIns.isAny(delegationClassDescriptor!!)) {
val superCall = CallTranslator.translate(context, resolvedCall)
add(superCall.toInvocationWith(thisNameRef).makeStmt())
}
val delegationCtorInTheSameClass = delegationClassDescriptor == classDescriptor
if (!delegationCtorInTheSameClass && !classDescriptor.hasPrimaryConstructor()) {
add(JsInvocation(Namer.getFunctionCallRef(referenceToClass), thisNameRef).makeStmt())
}
this
}
with(constructorFunction.getBody().getStatements()) {
addAll(0, forAddToBeginning)
add(JsReturn(thisNameRef))
}
return constructorInitializer
}
}
}
@@ -126,4 +126,9 @@ public class DeclarationBodyVisitor extends TranslatorVisitor<Void> {
// parsed it in initializer visitor => no additional actions are needed
return null;
}
@Override
public Void visitSecondaryConstructor(@NotNull JetSecondaryConstructor constructor, TranslationContext data) {
return null;
}
}
@@ -18,11 +18,13 @@ package org.jetbrains.kotlin.js.translate.expression;
import com.google.dart.compiler.backend.js.ast.*;
import com.google.dart.compiler.backend.js.ast.metadata.MetadataPackage;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
import org.jetbrains.kotlin.descriptors.VariableDescriptor;
import org.jetbrains.kotlin.js.translate.context.Namer;
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.declaration.ClassTranslator;
import org.jetbrains.kotlin.js.translate.expression.loopTranslator.LoopTranslatorPackage;
@@ -135,6 +137,12 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
public JsNode visitReturnExpression(@NotNull JetReturnExpression jetReturnExpression,
@NotNull TranslationContext context) {
JetExpression returned = jetReturnExpression.getReturnedExpression();
// TODO: add related descriptor to context and use it here
JetDeclarationWithBody parent = PsiTreeUtil.getParentOfType(jetReturnExpression, JetDeclarationWithBody.class);
if (parent instanceof JetSecondaryConstructor) {
return new JsReturn(new JsNameRef(Namer.ANOTHER_THIS_PARAMETER_NAME)).source(jetReturnExpression);
}
if (returned == null) {
return new JsReturn(null).source(jetReturnExpression);
}
@@ -32,7 +32,6 @@ import org.jetbrains.kotlin.psi.JetDeclarationWithBody;
import org.jetbrains.kotlin.psi.JetFunctionLiteralExpression;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -115,7 +114,7 @@ public final class FunctionTranslator extends AbstractTranslator {
private void translateBody() {
if (!functionDeclaration.hasBody()) {
assert descriptor.getModality().equals(Modality.ABSTRACT);
assert descriptor instanceof ConstructorDescriptor || descriptor.getModality().equals(Modality.ABSTRACT);
return;
}
functionObject.getBody().getStatements().addAll(translateFunctionBody(descriptor, functionDeclaration, functionBodyContext).getStatements());
@@ -20,19 +20,15 @@ import com.google.dart.compiler.backend.js.ast.*;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.PropertyDescriptor;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator;
import org.jetbrains.kotlin.js.translate.context.Namer;
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.declaration.DelegationTranslator;
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator;
import org.jetbrains.kotlin.js.translate.reference.CallArgumentTranslator;
import org.jetbrains.kotlin.lexer.JetTokens;
import org.jetbrains.kotlin.psi.JetClassOrObject;
import org.jetbrains.kotlin.psi.JetDelegationSpecifier;
import org.jetbrains.kotlin.psi.JetDelegatorToSuperCall;
import org.jetbrains.kotlin.psi.JetParameter;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.types.JetType;
@@ -44,6 +40,7 @@ import java.util.List;
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.*;
import static org.jetbrains.kotlin.js.translate.utils.FunctionBodyTranslator.setDefaultValueForArguments;
import static org.jetbrains.kotlin.js.translate.utils.PsiUtils.getPrimaryConstructorParameters;
import static org.jetbrains.kotlin.js.translate.utils.jsAstUtils.JsAstUtilsPackage.toInvocationWith;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.getClassDescriptorForType;
public final class ClassInitializerTranslator extends AbstractTranslator {
@@ -56,26 +53,39 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
@NotNull JetClassOrObject classDeclaration,
@NotNull TranslationContext context
) {
// Note: it's important we use scope for class descriptor because anonymous function used in property initializers
// belong to the properties themselves
super(context.newDeclaration(getConstructor(context.bindingContext(), classDeclaration), null));
super(context.newDeclarationWithScope(
getClassDescriptor(context.bindingContext(), classDeclaration),
new JsFunctionScope(context.scope(), "scope for primary/default constructor")));
this.classDeclaration = classDeclaration;
}
@NotNull
public JsFunction generateInitializeMethod(DelegationTranslator delegationTranslator) {
//TODO: it's inconsistent that we have scope for class and function for constructor, currently have problems implementing better way
ConstructorDescriptor primaryConstructor = getConstructor(bindingContext(), classDeclaration);
JsFunction result = context().getFunctionObject(primaryConstructor);
//NOTE: while we translate constructor parameters we also add property initializer statements
// for properties declared as constructor parameters
result.getParameters().addAll(translatePrimaryConstructorParameters());
mayBeAddCallToSuperMethod(result);
ClassDescriptor classDescriptor = getClassDescriptor(bindingContext(), classDeclaration);
ConstructorDescriptor primaryConstructor = classDescriptor.getUnsubstitutedPrimaryConstructor();
JsFunction result;
if (primaryConstructor != null) {
result = context().getFunctionObject(primaryConstructor);
result.getBody().getStatements().addAll(setDefaultValueForArguments(primaryConstructor, context()));
//NOTE: while we translate constructor parameters we also add property initializer statements
// for properties declared as constructor parameters
result.getParameters().addAll(translatePrimaryConstructorParameters());
mayBeAddCallToSuperMethod(result);
}
else {
result = new JsFunction(context().scope(), new JsBlock(), "fake constructor for " + classDescriptor.getName().asString());
}
delegationTranslator.addInitCode(initializerStatements);
new InitializerVisitor(initializerStatements).traverseContainer(classDeclaration, context());
List<JsStatement> statements = result.getBody().getStatements();
statements.addAll(setDefaultValueForArguments(primaryConstructor, context()));
for (JsStatement statement : initializerStatements) {
if (statement instanceof JsBlock) {
statements.addAll(((JsBlock) statement).getStatements());
@@ -90,15 +100,15 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
@NotNull
public JsExpression generateEnumEntryInstanceCreation(@NotNull JetType enumClassType) {
JetDelegatorToSuperCall superCall = getSuperCall();
List<JsExpression> arguments;
if (superCall != null) {
arguments = translateArguments(superCall);
} else {
arguments = Collections.emptyList();
ResolvedCall<FunctionDescriptor> superCall = getSuperCall();
if (superCall == null) {
ClassDescriptor classDescriptor = getClassDescriptorForType(enumClassType);
JsNameRef reference = context().getQualifiedReference(classDescriptor);
return new JsNew(reference);
}
JsNameRef reference = context().getQualifiedReference(getClassDescriptorForType(enumClassType));
return new JsNew(reference, arguments);
return CallTranslator.translate(context(), superCall);
}
private void mayBeAddCallToSuperMethod(JsFunction initializer) {
@@ -107,11 +117,18 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
return;
}
if (hasAncestorClass(bindingContext(), classDeclaration)) {
JetDelegatorToSuperCall superCall = getSuperCall();
if (superCall == null) {
return;
ResolvedCall<FunctionDescriptor> superCall = getSuperCall();
if (superCall == null) return;
if (classDeclaration instanceof JetEnumEntry) {
JsExpression expression = CallTranslator.translate(context(), superCall, null);
JsExpression fixedInvocation = toInvocationWith(expression, JsLiteral.THIS);
initializerStatements.add(0, fixedInvocation.makeStmt());
}
else {
List<JsExpression> arguments = CallArgumentTranslator.translate(superCall, null, context()).getTranslateArguments();
addCallToSuperMethod(arguments, initializer);
}
addCallToSuperMethod(translateArguments(superCall), initializer);
}
}
@@ -124,21 +141,16 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
initializerStatements.add(0, call.makeStmt());
}
@NotNull
private List<JsExpression> translateArguments(@NotNull JetDelegatorToSuperCall superCall) {
ResolvedCall<?> call = CallUtilPackage.getResolvedCallWithAssert(superCall, context().bindingContext());
return CallArgumentTranslator.translate(call, null, context()).getTranslateArguments();
}
@Nullable
private JetDelegatorToSuperCall getSuperCall() {
JetDelegatorToSuperCall result = null;
private ResolvedCall<FunctionDescriptor> getSuperCall() {
for (JetDelegationSpecifier specifier : classDeclaration.getDelegationSpecifiers()) {
if (specifier instanceof JetDelegatorToSuperCall) {
result = (JetDelegatorToSuperCall) specifier;
JetDelegatorToSuperCall superCall = (JetDelegatorToSuperCall) specifier;
//noinspection unchecked
return (ResolvedCall<FunctionDescriptor>) CallUtilPackage.getResolvedCallWithAssert(superCall, bindingContext());
}
}
return result;
return null;
}
@NotNull
@@ -78,4 +78,9 @@ public final class InitializerVisitor extends TranslatorVisitor<Void> {
}
return null;
}
@Override
public Void visitSecondaryConstructor(@NotNull JetSecondaryConstructor constructor, TranslationContext data) {
return null;
}
}
@@ -220,17 +220,4 @@ public final class BindingUtils {
boolean isGet) {
return BindingContextUtils.getNotNull(context, isGet ? INDEXED_LVALUE_GET : INDEXED_LVALUE_SET, arrayAccessExpression);
}
public static ConstructorDescriptor getConstructor(@NotNull BindingContext bindingContext,
@NotNull JetClassOrObject declaration) {
ConstructorDescriptor primaryConstructor = getClassDescriptor(bindingContext, declaration).getUnsubstitutedPrimaryConstructor();
assert primaryConstructor != null : message(declaration, "Traits do not have initialize methods");
return primaryConstructor;
}
@Nullable
public static SimpleFunctionDescriptor getNullableDescriptorForFunction(@NotNull BindingContext bindingContext,
@NotNull JetNamedFunction function) {
return bindingContext.get(BindingContext.FUNCTION, function);
}
}
@@ -118,6 +118,7 @@ public final class JsDescriptorUtils {
", receiverParameter = " + receiverParameter);
}
@NotNull
public static ReceiverParameterDescriptor getReceiverParameterForDeclaration(DeclarationDescriptor declarationDescriptor) {
if (declarationDescriptor instanceof ClassDescriptor) {
return ((ClassDescriptor) declarationDescriptor).getThisAsReceiverParameter();
@@ -19,9 +19,13 @@ package org.jetbrains.kotlin.js.translate.utils;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import kotlin.KotlinPackage;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.backend.common.CodegenUtil;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
import org.jetbrains.kotlin.descriptors.impl.ConstructorDescriptorImpl;
import org.jetbrains.kotlin.name.FqNameUnsafe;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
@@ -31,12 +35,13 @@ import org.jetbrains.kotlin.resolve.scopes.JetScope;
import java.util.*;
import static org.jetbrains.kotlin.js.descriptorUtils.DescriptorUtilsPackage.getJetTypeFqName;
import static org.jetbrains.kotlin.js.descriptorUtils.DescriptorUtilsPackage.hasPrimaryConstructor;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.getFqName;
public class ManglingUtils {
private ManglingUtils() {}
public static final Comparator<CallableMemberDescriptor> OVERLOADED_MEMBER_COMPARATOR = new OverloadedMemberComparator();
public static final Comparator<CallableDescriptor> CALLABLE_COMPARATOR = new CallableComparator();
@NotNull
public static String getMangledName(@NotNull PropertyDescriptor descriptor, @NotNull String suggestedName) {
@@ -131,30 +136,66 @@ public class ManglingUtils {
@NotNull
private static String getStableMangledName(@NotNull CallableDescriptor descriptor) {
return getStableMangledName(descriptor.getName().asString(), getArgumentTypesAsString(descriptor));
String suggestedName = getSuggestedName(descriptor);
return getStableMangledName(suggestedName, getArgumentTypesAsString(descriptor));
}
@NotNull
private static String getSimpleMangledName(@NotNull final CallableMemberDescriptor descriptor) {
DeclarationDescriptor declaration = descriptor.getContainingDeclaration();
private static String getSuggestedName(@NotNull CallableDescriptor descriptor) {
if (descriptor instanceof ConstructorDescriptor && !((ConstructorDescriptor) descriptor).isPrimary()) {
DeclarationDescriptor classDescriptor = descriptor.getContainingDeclaration();
assert classDescriptor != null;
return classDescriptor.getName().asString();
}
else {
return descriptor.getName().asString();
}
}
@NotNull
private static String getSimpleMangledName(@NotNull CallableMemberDescriptor descriptor) {
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
JetScope jetScope = null;
if (declaration instanceof PackageFragmentDescriptor) {
jetScope = ((PackageFragmentDescriptor) declaration).getMemberScope();
String nameToCompare = descriptor.getName().asString();
if (containingDeclaration != null && descriptor instanceof ConstructorDescriptor) {
nameToCompare = containingDeclaration.getName().asString();
containingDeclaration = containingDeclaration.getContainingDeclaration();
}
else if (declaration instanceof ClassDescriptor) {
jetScope = ((ClassDescriptor) declaration).getDefaultType().getMemberScope();
if (containingDeclaration instanceof PackageFragmentDescriptor) {
jetScope = ((PackageFragmentDescriptor) containingDeclaration).getMemberScope();
}
else if (containingDeclaration instanceof ClassDescriptor) {
jetScope = ((ClassDescriptor) containingDeclaration).getDefaultType().getMemberScope();
}
int counter = 0;
if (jetScope != null) {
final String finalNameToCompare = nameToCompare;
Collection<DeclarationDescriptor> declarations = jetScope.getDescriptors(DescriptorKindFilter.CALLABLES, JetScope.ALL_NAME_FILTER);
List<CallableMemberDescriptor>
overloadedFunctions = ContainerUtil.mapNotNull(declarations, new Function<DeclarationDescriptor, CallableMemberDescriptor>() {
List<CallableDescriptor> overloadedFunctions =
KotlinPackage.flatMap(declarations, new Function1<DeclarationDescriptor, Iterable<? extends CallableDescriptor>>() {
@Override
public CallableMemberDescriptor fun(DeclarationDescriptor declarationDescriptor) {
if (!(declarationDescriptor instanceof CallableMemberDescriptor)) return null;
public Iterable<? extends CallableDescriptor> invoke(DeclarationDescriptor declarationDescriptor) {
if (declarationDescriptor instanceof ClassDescriptor && finalNameToCompare.equals(declarationDescriptor.getName().asString())) {
ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
Collection<ConstructorDescriptor> constructors = classDescriptor.getConstructors();
if (!hasPrimaryConstructor(classDescriptor)) {
ConstructorDescriptorImpl fakePrimaryConstructor =
ConstructorDescriptorImpl.create(classDescriptor, Annotations.EMPTY, true, SourceElement.NO_SOURCE);
return KotlinPackage.plus(constructors, fakePrimaryConstructor);
}
return constructors;
}
if (!(declarationDescriptor instanceof CallableMemberDescriptor)) return Collections.emptyList();
CallableMemberDescriptor callableMemberDescriptor = (CallableMemberDescriptor) declarationDescriptor;
@@ -163,24 +204,26 @@ public class ManglingUtils {
// when name == null it's mean that it's not native.
if (name == null) {
// skip functions without arguments, because we don't use mangling for them
if (needsStableMangling(callableMemberDescriptor) && !callableMemberDescriptor.getValueParameters().isEmpty()) return null;
if (needsStableMangling(callableMemberDescriptor) && !callableMemberDescriptor.getValueParameters().isEmpty()) return Collections.emptyList();
// TODO add prefix for property: get_$name and set_$name
name = callableMemberDescriptor.getName().asString();
}
return descriptor.getName().asString().equals(name) ? callableMemberDescriptor : null;
if (finalNameToCompare.equals(name)) return Collections.singletonList(callableMemberDescriptor);
return Collections.emptyList();
}
});
if (overloadedFunctions.size() > 1) {
Collections.sort(overloadedFunctions, OVERLOADED_MEMBER_COMPARATOR);
Collections.sort(overloadedFunctions, CALLABLE_COMPARATOR);
counter = ContainerUtil.indexOfIdentity(overloadedFunctions, descriptor);
assert counter >= 0;
}
}
String name = descriptor.getName().asString();
String name = getSuggestedName(descriptor);
return counter == 0 ? name : name + '_' + counter;
}
@@ -206,13 +249,21 @@ public class ManglingUtils {
public static String getStableMangledNameForDescriptor(@NotNull ClassDescriptor descriptor, @NotNull String functionName) {
Collection<FunctionDescriptor> functions = descriptor.getDefaultType().getMemberScope().getFunctions(Name.identifier(functionName));
assert functions.size() == 1 : "Can't select a single function: " + functionName + " in " + descriptor;
return getSuggestedName(functions.iterator().next());
return getSuggestedName((DeclarationDescriptor) functions.iterator().next());
}
private static class OverloadedMemberComparator implements Comparator<CallableMemberDescriptor> {
private static class CallableComparator implements Comparator<CallableDescriptor> {
@Override
public int compare(@NotNull CallableMemberDescriptor a, @NotNull CallableMemberDescriptor b) {
// native functions first
public int compare(@NotNull CallableDescriptor a, @NotNull CallableDescriptor b) {
// primary constructors
if (a instanceof ConstructorDescriptor && ((ConstructorDescriptor) a).isPrimary()) {
if (!(b instanceof ConstructorDescriptor) || !((ConstructorDescriptor) b).isPrimary()) return -1;
}
else if (b instanceof ConstructorDescriptor && ((ConstructorDescriptor) b).isPrimary()) {
return 1;
}
// native functions
if (isNativeOrOverrideNative(a)) {
if (!isNativeOrOverrideNative(b)) return -1;
}
@@ -238,14 +289,16 @@ public class ManglingUtils {
return aArguments.compareTo(bArguments);
}
private static int arity(CallableMemberDescriptor descriptor) {
private static int arity(CallableDescriptor descriptor) {
return descriptor.getValueParameters().size() + (descriptor.getExtensionReceiverParameter() == null ? 0 : 1);
}
private static boolean isNativeOrOverrideNative(CallableMemberDescriptor descriptor) {
private static boolean isNativeOrOverrideNative(CallableDescriptor descriptor) {
if (!(descriptor instanceof CallableMemberDescriptor)) return false;
if (AnnotationsUtils.isNativeObject(descriptor)) return true;
Set<CallableMemberDescriptor> declarations = DescriptorUtils.getAllOverriddenDeclarations(descriptor);
Set<CallableMemberDescriptor> declarations = DescriptorUtils.getAllOverriddenDeclarations((CallableMemberDescriptor) descriptor);
for (CallableMemberDescriptor memberDescriptor : declarations) {
if (AnnotationsUtils.isNativeObject(memberDescriptor)) return true;
}
@@ -146,7 +146,17 @@ public final class TranslationUtils {
}
backingFieldName = context.declarePropertyOrPropertyAccessorName(descriptor, backingFieldMangledName, false);
}
return new JsNameRef(backingFieldName, JsLiteral.THIS);
DeclarationDescriptor containingDescriptor = descriptor.getContainingDeclaration();
JsExpression receiver;
if (containingDescriptor instanceof PackageFragmentDescriptor) {
// used inside package initializer
receiver = JsLiteral.THIS;
}
else {
receiver = context.getDispatchReceiver(JsDescriptorUtils.getReceiverParameterForDeclaration(containingDescriptor));
}
return new JsNameRef(backingFieldName, receiver);
}
@NotNull
@@ -17,7 +17,7 @@
package org.jetbrains.kotlin.js.translate.utils.jsAstUtils
import com.google.dart.compiler.backend.js.ast.*
import org.jetbrains.kotlin.js.inline.util.IdentitySet
import org.jetbrains.kotlin.js.translate.context.Namer
public fun JsFunction.addStatement(stmt: JsStatement) {
getBody().getStatements().add(stmt)
@@ -56,6 +56,25 @@ public fun JsNode.any(predicate: (JsNode) -> Boolean): Boolean {
return visitor.matched
}
fun JsExpression.toInvocationWith(thisExpr: JsExpression): JsExpression {
val qualifier: JsExpression
val arguments: MutableList<JsExpression>
when (this) {
is JsNew -> {
qualifier = Namer.getFunctionCallRef(getConstructorExpression())
arguments = getArguments()
}
is JsInvocation -> {
qualifier = getQualifier()
arguments = with(getArguments()) { subList(1, size()) }
}
else -> throw IllegalStateException("Unexpected node type: " + javaClass)
}
return JsInvocation(qualifier, listOf(thisExpr) + arguments)
}
public var JsNameRef.qualifier: JsExpression?
get() = getQualifier()
set(value) = setQualifier(value)