From bc273ddd756db2f7870da0704d502400785b31be Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Mon, 3 Feb 2014 23:06:25 +0400 Subject: [PATCH] JS backend: fixed using the stable mangling for extra cases. --- .../k2js/test/semantics/FunctionTest.java | 4 + .../translate/utils/JsDescriptorUtils.java | 5 +- .../translate/utils/TranslationUtils.java | 34 ++- .../expression/function/cases/mangling.kt | 195 ++++++++++++++++++ 4 files changed, 232 insertions(+), 6 deletions(-) create mode 100644 js/js.translator/testFiles/expression/function/cases/mangling.kt diff --git a/js/js.tests/test/org/jetbrains/k2js/test/semantics/FunctionTest.java b/js/js.tests/test/org/jetbrains/k2js/test/semantics/FunctionTest.java index bb6fb554f4f..a363ce71b1f 100644 --- a/js/js.tests/test/org/jetbrains/k2js/test/semantics/FunctionTest.java +++ b/js/js.tests/test/org/jetbrains/k2js/test/semantics/FunctionTest.java @@ -113,6 +113,10 @@ public class FunctionTest extends AbstractExpressionTest { fooBoxTest(); } + public void testMangling() throws Exception { + checkFooBoxIsOk(); + } + public void testManglingStability() throws Exception { checkFooBoxIsOk(); } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/JsDescriptorUtils.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/JsDescriptorUtils.java index 8d89fc5ea27..8d178667eb4 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/JsDescriptorUtils.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/JsDescriptorUtils.java @@ -36,7 +36,6 @@ import java.util.List; import java.util.Set; import static org.jetbrains.jet.lang.resolve.DescriptorUtils.*; -import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getFqName; public final class JsDescriptorUtils { // TODO: maybe we should use external annotations or something else. @@ -98,6 +97,10 @@ public final class JsDescriptorUtils { return (functionDescriptor.getReceiverParameter() != null); } + public static boolean isOverride(@NotNull CallableMemberDescriptor descriptor) { + return !descriptor.getOverriddenDescriptors().isEmpty(); + } + //TODO: why callable descriptor @Nullable public static DeclarationDescriptor getExpectedThisDescriptor(@NotNull CallableDescriptor callableDescriptor) { diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java index 71511c69b5c..f0969d9d742 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java @@ -148,22 +148,44 @@ public final class TranslationUtils { return getSimpleMangledName(descriptor); } + //TODO extend logic for nested/inner declarations private static boolean needsStableMangling(FunctionDescriptor descriptor) { - if (descriptor.getVisibility() == Visibilities.PUBLIC || !descriptor.getOverriddenDescriptors().isEmpty()) { + // Use stable mangling for overrides because we use stable mangling a overridable declaration. + if (JsDescriptorUtils.isOverride(descriptor)) { return true; } DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration(); if (containingDeclaration instanceof PackageFragmentDescriptor) { + return descriptor.getVisibility().isPublicAPI(); + } + else if (containingDeclaration instanceof ClassDescriptor) { + ClassDescriptor classDescriptor = (ClassDescriptor) containingDeclaration; + // Use stable mangling when it inside a overridable declaration for avoid clashing names when inheritance. + if (classDescriptor.getModality().isOverridable()) { + return true; + } + + // Don't use stable mangling when it inside a non-public API declaration. + if (!classDescriptor.getVisibility().isPublicAPI()) { + return false; + } + + // Ignore the `protected` visibility because it can be use outside a containing declaration + // only when the containing declaration is overridable. + if (descriptor.getVisibility() == Visibilities.PUBLIC) { + return true; + } + return false; } - if (containingDeclaration instanceof MemberDescriptor) { - return ((MemberDescriptor) containingDeclaration).getModality().isOverridable(); - } + assert containingDeclaration instanceof CallableMemberDescriptor : + "containingDeclaration for descriptor have unsupported type for mangling, " + + "descriptor: " + descriptor + ", containingDeclaration: " + containingDeclaration; - return true; + return false; } @NotNull @@ -413,6 +435,8 @@ public final class TranslationUtils { } private static boolean isNativeOrOverrideNative(FunctionDescriptor descriptor) { + if (AnnotationsUtils.isNativeObject(descriptor)) return true; + Set declarations = BindingContextUtils.getAllOverriddenDeclarations(descriptor); for (FunctionDescriptor memberDescriptor : declarations) { if (AnnotationsUtils.isNativeObject(memberDescriptor)) return true; diff --git a/js/js.translator/testFiles/expression/function/cases/mangling.kt b/js/js.translator/testFiles/expression/function/cases/mangling.kt new file mode 100644 index 00000000000..74fb71a7da6 --- /dev/null +++ b/js/js.translator/testFiles/expression/function/cases/mangling.kt @@ -0,0 +1,195 @@ +package foo + +public fun public_baz(i: Int) {} +native public fun public_baz(a: String) {} + +fun internal_baz(i: Int) {} +native fun internal_baz(a: String) {} + +private fun private_baz(i: Int) {} +native private fun private_baz(a: String) {} + +public class PublicClass { + public fun public_baz(i: Int) {} + native public fun public_baz(a: String) {} + + fun internal_baz(i: Int) {} + native fun internal_baz(a: String) {} + + private fun private_baz(i: Int) {} + native private fun private_baz(a: String) {} + + val call_private_baz = { private_baz(0)} + val call_private_native_baz = { private_baz("native")} +} + +class InternalClass { + public fun public_baz(i: Int) {} + native public fun public_baz(a: String) {} + + fun internal_baz(i: Int) {} + native fun internal_baz(a: String) {} + + private fun private_baz(i: Int) {} + native private fun private_baz(a: String) {} + + val call_private_baz = { private_baz(0)} + val call_private_native_baz = { private_baz("native")} +} + +private class PrivateClass { + public fun public_baz(i: Int) {} + native public fun public_baz(a: String) {} + + fun internal_baz(i: Int) {} + native fun internal_baz(a: String) {} + + private fun private_baz(i: Int) {} + native private fun private_baz(a: String) {} + + val call_private_baz = { private_baz(0)} + val call_private_native_baz = { private_baz("native")} +} + +open public class OpenPublicClass { + public fun public_baz(i: Int) {} + native public fun public_baz(a: String) {} + + fun internal_baz(i: Int) {} + native fun internal_baz(a: String) {} + + private fun private_baz(i: Int) {} + native private fun private_baz(a: String) {} + + val call_private_baz = { private_baz(0)} + val call_private_native_baz = { private_baz("native")} +} + +open class OpenInternalClass { + public fun public_baz(i: Int) {} + native public fun public_baz(a: String) {} + + fun internal_baz(i: Int) {} + native fun internal_baz(a: String) {} + + private fun private_baz(i: Int) {} + native private fun private_baz(a: String) {} + + val call_private_baz = { private_baz(0)} + val call_private_native_baz = { private_baz("native")} +} + +open private class OpenPrivateClass { + public fun public_baz(i: Int) {} + native public fun public_baz(a: String) {} + + fun internal_baz(i: Int) {} + native fun internal_baz(a: String) {} + + private fun private_baz(i: Int) {} + native private fun private_baz(a: String) {} + + val call_private_baz = { private_baz(0)} + val call_private_native_baz = { private_baz("native")} +} + +// Helpers + +native +fun String.search(regexp: RegExp): Int = noImpl + +native +class RegExp(regexp: String, flags: String = "") { + fun exec(s: String): Array? = noImpl +} + +val CALEE_NAME = RegExp("""\b\w*(baz[^(]*)""") + +fun Function0.extractNames(): Array { + val names = CALEE_NAME.exec(this.toString()) + + if (names == null || names.size != 2) { + throw Exception("Cannot extract function name, $names for actual = \"$this\"") + } + + return names +} + +// Testing + +var testGroup = "" + +fun test(expected: String, f: ()->Unit){ + val actual = f.extractNames() + + if (expected != actual[1]) { + throw Exception("Failed on '$testGroup' group: expected = \"$expected\", actual[1] = \"${actual[1]}\"\n actual = $actual") + } +} + +public fun stable_mangled_baz(i: Int) {} + +val SIMPLE = "baz" +val SIMPLE1 = "${SIMPLE}_1" +val NATIVE = SIMPLE +val STABLE = { stable_mangled_baz(0) }.extractNames()[1] + +fun box(): String { + testGroup = "Top Level" + test(STABLE) { public_baz(0) } + test(NATIVE) { public_baz("native") } + test(SIMPLE1) { internal_baz(0) } + test(NATIVE) { internal_baz("native") } + test(SIMPLE1) { private_baz(0) } + test(NATIVE) { private_baz("native") } + + testGroup = "Public Class" + test(STABLE) { PublicClass().public_baz(0) } + test(NATIVE) { PublicClass().public_baz("native") } + test(SIMPLE1) { PublicClass().internal_baz(0) } + test(NATIVE) { PublicClass().internal_baz("native") } + test(SIMPLE1, PublicClass().call_private_baz) + test(NATIVE, PublicClass().call_private_native_baz) + + testGroup = "Internal Class" + test(SIMPLE1) { InternalClass().public_baz(0) } + test(NATIVE) { InternalClass().public_baz("native") } + test(SIMPLE1) { InternalClass().internal_baz(0) } + test(NATIVE) { InternalClass().internal_baz("native") } + test(SIMPLE1, InternalClass().call_private_baz) + test(NATIVE, InternalClass().call_private_native_baz) + + testGroup = "Private Class" + test(SIMPLE1) { PrivateClass().public_baz(0) } + test(NATIVE) { PrivateClass().public_baz("native") } + test(SIMPLE1) { PrivateClass().internal_baz(0) } + test(NATIVE) { PrivateClass().internal_baz("native") } + test(SIMPLE1, PrivateClass().call_private_baz) + test(NATIVE, PrivateClass().call_private_native_baz) + + testGroup = "Open Public Class" + test(STABLE) { OpenPublicClass().public_baz(0) } + test(NATIVE) { OpenPublicClass().public_baz("native") } + test(STABLE) { OpenPublicClass().internal_baz(0) } + test(NATIVE) { OpenPublicClass().internal_baz("native") } + test(STABLE, OpenPublicClass().call_private_baz) + test(NATIVE, OpenPublicClass().call_private_native_baz) + + testGroup = "Open Internal Class" + test(STABLE) { OpenInternalClass().public_baz(0) } + test(NATIVE) { OpenInternalClass().public_baz("native") } + test(STABLE) { OpenInternalClass().internal_baz(0) } + test(NATIVE) { OpenInternalClass().internal_baz("native") } + test(STABLE, OpenInternalClass().call_private_baz) + test(NATIVE, OpenInternalClass().call_private_native_baz) + + testGroup = "Open Private Class" + test(STABLE) { OpenPrivateClass().public_baz(0) } + test(NATIVE) { OpenPrivateClass().public_baz("native") } + test(STABLE) { OpenPrivateClass().internal_baz(0) } + test(NATIVE) { OpenPrivateClass().internal_baz("native") } + test(STABLE, OpenPrivateClass().call_private_baz) + test(NATIVE, OpenPrivateClass().call_private_native_baz) + + return "OK" +}