KT-11030 Refactoring code to properly implement secondary constructors in local classes

This commit is contained in:
Alexey Andreev
2016-03-24 16:20:57 +03:00
parent b39c16cb6c
commit c1c57a06bb
5 changed files with 83 additions and 53 deletions
@@ -28,7 +28,6 @@ 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;
import org.jetbrains.kotlin.js.config.LibrarySourcesConfig;
import org.jetbrains.kotlin.js.translate.context.generator.Generator;
import org.jetbrains.kotlin.js.translate.context.generator.Rule;
@@ -39,8 +38,6 @@ import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.BindingTrace;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -106,9 +103,6 @@ public final class StaticContext {
@NotNull
private final Config config;
@NotNull
private final EcmaVersion ecmaVersion;
//TODO: too many parameters in constructor
private StaticContext(@NotNull JsProgram program, @NotNull BindingTrace bindingTrace,
@NotNull Namer namer, @NotNull Intrinsics intrinsics,
@@ -120,14 +114,9 @@ public final class StaticContext {
this.rootScope = rootScope;
this.standardClasses = standardClasses;
this.config = config;
this.ecmaVersion = config.getTarget();
this.reflectionTypes = new ReflectionTypes(moduleDescriptor);
}
public boolean isEcma5() {
return ecmaVersion == EcmaVersion.v5;
}
@NotNull
public JsProgram getProgram() {
return program;
@@ -272,21 +261,15 @@ public final class StaticContext {
return null;
}
List<String> parts = new ArrayList<String>();
String suggested = getSuggestedName(descriptor);
do {
parts.add(descriptor.getName().isSpecial() ? "f" : descriptor.getName().getIdentifier());
descriptor = descriptor.getContainingDeclaration();
} while (descriptor != null && !(descriptor instanceof ClassOrPackageFragmentDescriptor));
assert descriptor != null;
Collections.reverse(parts);
StringBuilder suggestedName = new StringBuilder(parts.get(0));
for (int i = 1; i < parts.size(); ++i) {
suggestedName.append('$').append(parts.get(i));
}
JsScope scope = getScopeForDescriptor(descriptor);
return scope.declareFreshName(suggestedName.toString());
return scope.declareFreshName(suggested);
}
};
@@ -31,7 +31,7 @@ public class Generator<V> {
@NotNull
private final List<Rule<V>> rules = Lists.newArrayList();
public void addRule(@NotNull Rule<V> rule) {
protected final void addRule(@NotNull Rule<V> rule) {
rules.add(rule);
}
@@ -295,6 +295,7 @@ class ClassTranslator private constructor(
}
private fun generateSecondaryConstructor(constructor: KtSecondaryConstructor, context: TranslationContext): JsPropertyInitializer {
// Prepare
val constructorDescriptor = BindingUtils.getDescriptorForElement(context.bindingContext(), constructor) as ConstructorDescriptor
val classDescriptor = constructorDescriptor.containingDeclaration
@@ -309,6 +310,9 @@ class ClassTranslator private constructor(
translationContext = translationContext.newDeclaration(constructorDescriptor, translationContext.definitionPlace)
translationContext = translationContext.innerContextWithAliased(receiverDescriptor, thisNameRef)
val closure = translationContext.getLocalClassClosure(classDescriptor)
val usageTracker = context.usageTracker()
val outerName = translationContext.getOuterClassReference(classDescriptor);
val outerClass = DescriptorUtils.getContainingClass(classDescriptor)
if (outerClass != null && outerName != null) {
@@ -316,43 +320,59 @@ class ClassTranslator private constructor(
translationContext = translationContext.innerContextWithAliased(outerClassRef, outerName.makeRef())
}
// Translate constructor body
val constructorInitializer = FunctionTranslator.newInstance(constructor, translationContext).translateAsMethod()
val constructorFunction = constructorInitializer.valueExpr as JsFunction
val leadingArgs = mutableListOf<JsExpression>()
if (outerName != null) {
constructorFunction.parameters.add(0, JsParameter(outerName))
leadingArgs.add(outerName.makeRef())
}
constructorFunction.parameters.add(JsParameter(thisName))
// Translate super/this call
val forAddToBeginning = arrayListOf<JsStatement>()
val referenceToClass = translationContext.getQualifiedReference(classDescriptor)
val forAddToBeginning: List<JsStatement> =
with(arrayListOf<JsStatement>()) {
addAll(FunctionBodyTranslator.setDefaultValueForArguments(constructorDescriptor, translationContext))
forAddToBeginning.addAll(FunctionBodyTranslator.setDefaultValueForArguments(constructorDescriptor, translationContext))
val createInstance = Namer.createObjectWithPrototypeFrom(referenceToClass)
val instanceVar = JsAstUtils.assignment(thisNameRef, JsAstUtils.or(thisNameRef, createInstance)).makeStmt()
add(instanceVar)
val createInstance = Namer.createObjectWithPrototypeFrom(referenceToClass)
val instanceVar = JsAstUtils.assignment(thisNameRef, JsAstUtils.or(thisNameRef, createInstance)).makeStmt()
forAddToBeginning.add(instanceVar)
val resolvedCall = BindingContextUtils.getDelegationConstructorCall(translationContext.bindingContext(),
constructorDescriptor)
val delegationClassDescriptor = resolvedCall?.resultingDescriptor?.containingDeclaration
val resolvedCall = BindingContextUtils.getDelegationConstructorCall(translationContext.bindingContext(),
constructorDescriptor)
val delegationClassDescriptor = resolvedCall?.resultingDescriptor?.containingDeclaration
val superCall = if (resolvedCall != null && !KotlinBuiltIns.isAny(delegationClassDescriptor!!)) {
CallTranslator.translate(translationContext, resolvedCall)
}
else {
null
}
if (resolvedCall != null && !KotlinBuiltIns.isAny(delegationClassDescriptor!!)) {
val superCall = CallTranslator.translate(translationContext, resolvedCall)
add(superCall.toInvocationWith(leadingArgs, thisNameRef).makeStmt())
}
// Add parameters for closure and outer instance
val leadingArgs = mutableListOf<JsExpression>()
val delegationCtorInTheSameClass = delegationClassDescriptor == classDescriptor
if (!delegationCtorInTheSameClass && !classDescriptor.hasPrimaryConstructor()) {
add(JsInvocation(Namer.getFunctionCallRef(referenceToClass), listOf(thisNameRef) + leadingArgs).makeStmt())
}
var additionalParameterIndex = 0
if (closure != null && usageTracker != null) {
for (capturedValue in closure) {
val capturedName = usageTracker.capturedDescriptorToJsName[capturedValue]!!
constructorFunction.parameters.add(additionalParameterIndex++, JsParameter(capturedName))
leadingArgs.add(capturedName.makeRef())
}
}
this
}
if (outerName != null) {
constructorFunction.parameters.add(additionalParameterIndex, JsParameter(outerName))
leadingArgs.add(outerName.makeRef())
}
constructorFunction.parameters.add(JsParameter(thisName))
// Insert super/this call to beginning of function
if (superCall != null) {
forAddToBeginning.add(superCall.toInvocationWith(leadingArgs, thisNameRef).makeStmt())
}
val delegationCtorInTheSameClass = delegationClassDescriptor == classDescriptor
if (!delegationCtorInTheSameClass && !classDescriptor.hasPrimaryConstructor()) {
forAddToBeginning.add(JsInvocation(Namer.getFunctionCallRef(referenceToClass), listOf(thisNameRef) + leadingArgs).makeStmt())
}
with(constructorFunction.body.statements) {
addAll(0, forAddToBeginning)
@@ -572,9 +572,8 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
JsObjectScope scope = new JsObjectScope(context.scope(), descriptor.toString(), "");
TranslationContext classContext = context.innerWithUsageTracker(scope, descriptor);
JsInvocation jsClass = ClassTranslator.generateClassCreation(klass, classContext);
context.getDefinitionPlace().getProperties().addAll(ClassTranslator.Companion.translate(klass, classContext));
JsName name = context.getNameForDescriptor(descriptor);
return context.define(name, jsClass);
return JsEmpty.INSTANCE;
}
}
@@ -40,7 +40,7 @@ import static org.jetbrains.kotlin.resolve.DescriptorUtils.getFqName;
public class ManglingUtils {
private ManglingUtils() {}
public static final Comparator<CallableDescriptor> CALLABLE_COMPARATOR = new CallableComparator();
private static final Comparator<CallableDescriptor> CALLABLE_COMPARATOR = new CallableComparator();
@NotNull
public static String getMangledName(@NotNull PropertyDescriptor descriptor, @NotNull String suggestedName) {
@@ -49,7 +49,7 @@ public class ManglingUtils {
@NotNull
public static String getSuggestedName(@NotNull DeclarationDescriptor descriptor) {
String suggestedName = descriptor.getName().asString();
String suggestedName = descriptor.getName().isSpecial() ? "f" : descriptor.getName().getIdentifier();
if (descriptor instanceof FunctionDescriptor ||
descriptor instanceof PropertyDescriptor && DescriptorUtils.isExtension((PropertyDescriptor) descriptor)
@@ -57,9 +57,37 @@ public class ManglingUtils {
suggestedName = getMangledName((CallableMemberDescriptor) descriptor);
}
ClassDescriptor localClass = null;
if (descriptor instanceof ConstructorDescriptor) {
ConstructorDescriptor constructor = (ConstructorDescriptor) descriptor;
localClass = constructor.getContainingDeclaration();
}
else if (descriptor instanceof ClassDescriptor) {
localClass = (ClassDescriptor) descriptor;
}
if (DescriptorUtils.isDescriptorWithLocalVisibility(localClass)) {
suggestedName = getSuggestedLocalPrefix(localClass) + suggestedName;
}
return suggestedName;
}
@NotNull
private static String getSuggestedLocalPrefix(@NotNull DeclarationDescriptor descriptor) {
List<String> parts = new ArrayList<String>();
while (true) {
descriptor = descriptor.getContainingDeclaration();
if (descriptor == null || descriptor instanceof ClassOrPackageFragmentDescriptor) {
break;
}
parts.add(descriptor.getName().isSpecial() ? "f" : descriptor.getName().getIdentifier());
}
Collections.reverse(parts);
return StringUtil.join(parts, "$") + "$";
}
@NotNull
private static String getMangledName(@NotNull CallableMemberDescriptor descriptor) {
if (needsStableMangling(descriptor)) {
@@ -142,7 +170,7 @@ public class ManglingUtils {
@NotNull
private static String getSuggestedName(@NotNull CallableDescriptor descriptor) {
if (descriptor instanceof ConstructorDescriptor && !((ConstructorDescriptor) descriptor).isPrimary()) {
return descriptor.getContainingDeclaration().getName().asString();
return descriptor.getContainingDeclaration().getName().asString() + "_init";
}
else {
return descriptor.getName().asString();