From 5e3aa33b13e0a2f87b926a830fd803001dcd2bcf Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Mon, 30 May 2016 12:35:41 +0300 Subject: [PATCH] KT-2752: add draft implementation of JsName annotation. Fix some tests using the annotation --- .../kotlin/js/PredefinedAnnotation.kt | 3 +- .../kotlin/js/naming/FQNGenerator.kt | 25 ++++++---- .../js/translate/utils/AnnotationsUtils.java | 13 +++++ js/js.libraries/src/core/annotations.kt | 6 ++- js/js.libraries/src/dom/Dom.kt | 4 ++ .../src/internal/primitiveCompanionObjects.kt | 48 +++++++++---------- .../expression/function/manglingStability.kt | 29 ++++------- .../cases/Traffic light.kt | 2 +- 8 files changed, 75 insertions(+), 55 deletions(-) diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/PredefinedAnnotation.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/PredefinedAnnotation.kt index 112df9a087b..4bb71fbb188 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/PredefinedAnnotation.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/PredefinedAnnotation.kt @@ -23,7 +23,8 @@ enum class PredefinedAnnotation(fqName: String) { NATIVE("kotlin.js.native"), NATIVE_INVOKE("kotlin.js.nativeInvoke"), NATIVE_GETTER("kotlin.js.nativeGetter"), - NATIVE_SETTER("kotlin.js.nativeSetter"); + NATIVE_SETTER("kotlin.js.nativeSetter"), + JS_NAME("kotlin.js.JsName"); val fqName: FqName = FqName(fqName) diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/naming/FQNGenerator.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/naming/FQNGenerator.kt index 2ce0d8297c9..c52814ff75b 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/naming/FQNGenerator.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/naming/FQNGenerator.kt @@ -135,11 +135,12 @@ class FQNGenerator { } } - return if (needsStableMangling(resolvedDescriptor)) { - Pair(getStableMangledName(baseName, getArgumentTypesAsString(resolvedDescriptor)), true) - } - else { - Pair(baseName, false) + val explicitName = getJsName(descriptor) + return when { + explicitName != null -> Pair(explicitName, true) + needsStableMangling(resolvedDescriptor) -> + Pair(getStableMangledName(baseName, getArgumentTypesAsString(resolvedDescriptor)), true) + else -> Pair(baseName, false) } } @@ -167,8 +168,7 @@ class FQNGenerator { } private fun getStableMangledName(suggestedName: String, forCalculateId: String): String { - val absHashCode = Math.abs(forCalculateId.hashCode()) - val suffix = if (absHashCode == 0) "" else "_" + Integer.toString(absHashCode, Character.MAX_RADIX) + "$" + val suffix = if (forCalculateId.isEmpty()) "" else "_${mangledId(forCalculateId)}\$" return suggestedName + suffix } @@ -187,7 +187,9 @@ class FQNGenerator { return when (containingDeclaration) { is PackageFragmentDescriptor -> descriptor.visibility.isPublicAPI is ClassDescriptor -> { - if (descriptor.modality == Modality.OPEN || descriptor.modality == Modality.ABSTRACT) return true + if (descriptor.modality == Modality.OPEN || descriptor.modality == Modality.ABSTRACT) { + return descriptor.visibility.isPublicAPI + } // valueOf() is created in the library with a mangled name for every enum class if (descriptor is FunctionDescriptor && descriptor.isEnumValueOfMethod()) return true @@ -210,4 +212,11 @@ class FQNGenerator { } } } + + companion object { + @JvmStatic fun mangledId(forCalculateId: String): String { + val absHashCode = Math.abs(forCalculateId.hashCode()) + return if (absHashCode != 0) Integer.toString(absHashCode, Character.MAX_RADIX) else "" + } + } } diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/translate/utils/AnnotationsUtils.java b/js/js.frontend/src/org/jetbrains/kotlin/js/translate/utils/AnnotationsUtils.java index 88217baddd6..76aa0c7cf0b 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/translate/utils/AnnotationsUtils.java +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/translate/utils/AnnotationsUtils.java @@ -126,6 +126,19 @@ public final class AnnotationsUtils { return hasAnnotationOrInsideAnnotatedClass(descriptor, PredefinedAnnotation.LIBRARY); } + @Nullable + public static String getJsName(@NotNull DeclarationDescriptor descriptor) { + AnnotationDescriptor annotation = getAnnotationByName(descriptor, PredefinedAnnotation.JS_NAME.getFqName()); + if (annotation == null) return null; + + ConstantValue value = annotation.getAllValueArguments().values().iterator().next(); + assert value != null : "JsName annotation should always declare string parameter"; + + Object result = value.getValue(); + assert result instanceof String : "Parameter of JsName annotation should be string"; + return (String) result; + } + public static boolean isPredefinedObject(@NotNull DeclarationDescriptor descriptor) { for (PredefinedAnnotation annotation : PredefinedAnnotation.values()) { if (hasAnnotationOrInsideAnnotatedClass(descriptor, annotation)) { diff --git a/js/js.libraries/src/core/annotations.kt b/js/js.libraries/src/core/annotations.kt index a4610dc3077..449649fc343 100644 --- a/js/js.libraries/src/core/annotations.kt +++ b/js/js.libraries/src/core/annotations.kt @@ -46,4 +46,8 @@ public annotation class enumerable() @native @Target(CLASS) @Deprecated("Do not use this annotation: it is for internal use only") -public annotation class marker \ No newline at end of file +public annotation class marker + +@Retention(AnnotationRetention.BINARY) +@Target(CLASS, FUNCTION, PROPERTY, CONSTRUCTOR) +annotation class JsName(val name: String) \ No newline at end of file diff --git a/js/js.libraries/src/dom/Dom.kt b/js/js.libraries/src/dom/Dom.kt index 7d47f2c34c5..44e26ccd47a 100644 --- a/js/js.libraries/src/dom/Dom.kt +++ b/js/js.libraries/src/dom/Dom.kt @@ -45,6 +45,7 @@ val Element?.elements: List get() = this?.elements() ?: emptyList() @Deprecated("Use non-nullable receiver version elements()", ReplaceWith("this?.elements(localName) ?: emptyList()")) +@JsName("deprecated_elements") fun Element?.elements(localName: String): List = this?.elements(localName) ?: emptyList() /** Returns all the descendant elements given the local element name */ @@ -53,11 +54,13 @@ fun Element.elements(localName: String = "*"): List { } /** Returns all the descendant elements given the local element name */ +@JsName("deprecated_document_elements") fun Document?.elements(localName: String = "*"): List { return this?.getElementsByTagName(localName)?.asElementList() ?: emptyList() } @Deprecated("Use non-nullable elements function instead", ReplaceWith("this?.elements(namespaceUri, localName) ?: emptyList()")) +@JsName("deprecated_elements_2") fun Element?.elements(namespaceUri: String, localName: String): List = this?.elements(namespaceUri, localName) ?: emptyList() /** Returns all the descendant elements given the namespace URI and local element name */ @@ -71,6 +74,7 @@ fun Document?.elements(namespaceUri: String, localName: String): List { } @Deprecated("Use non-null function instead with elvis", ReplaceWith("this?.asList() ?: emptyList()")) +@JsName("deprecated_asList") fun NodeList?.asList(): List = this?.asList() ?: emptyList() fun NodeList.asList(): List = NodeListAsList(this) diff --git a/js/js.libraries/src/internal/primitiveCompanionObjects.kt b/js/js.libraries/src/internal/primitiveCompanionObjects.kt index 6592d33c623..4ad6e8855b3 100644 --- a/js/js.libraries/src/internal/primitiveCompanionObjects.kt +++ b/js/js.libraries/src/internal/primitiveCompanionObjects.kt @@ -17,48 +17,48 @@ package kotlin.js.internal private object DoubleCompanionObject { - val MIN_VALUE: Double = js("Number.MIN_VALUE") - val MAX_VALUE: Double = js("Number.MAX_VALUE") - val POSITIVE_INFINITY: Double = js("Number.POSITIVE_INFINITY") - val NEGATIVE_INFINITY: Double = js("Number.NEGATIVE_INFINITY") - val NaN: Double = js("Number.NaN") + @JsName("MIN_VALUE") val MIN_VALUE: Double = js("Number.MIN_VALUE") + @JsName("MAX_VALUE") val MAX_VALUE: Double = js("Number.MAX_VALUE") + @JsName("POSITIVE_INFINITY") val POSITIVE_INFINITY: Double = js("Number.POSITIVE_INFINITY") + @JsName("NEGATIVE_INFINITY") val NEGATIVE_INFINITY: Double = js("Number.NEGATIVE_INFINITY") + @JsName("NaN") val NaN: Double = js("Number.NaN") } private object FloatCompanionObject { - val MIN_VALUE: Float = js("Number.MIN_VALUE") - val MAX_VALUE: Float = js("Number.MAX_VALUE") - val POSITIVE_INFINITY: Float = js("Number.POSITIVE_INFINITY") - val NEGATIVE_INFINITY: Float = js("Number.NEGATIVE_INFINITY") - val NaN: Float = js("Number.NaN") + @JsName("MIN_VALUE") val MIN_VALUE: Float = js("Number.MIN_VALUE") + @JsName("MAX_VALUE") val MAX_VALUE: Float = js("Number.MAX_VALUE") + @JsName("POSITIVE_INFINITY") val POSITIVE_INFINITY: Float = js("Number.POSITIVE_INFINITY") + @JsName("NEGATIVE_INFINITY") val NEGATIVE_INFINITY: Float = js("Number.NEGATIVE_INFINITY") + @JsName("NaN") val NaN: Float = js("Number.NaN") } private object IntCompanionObject { - val MIN_VALUE: Int = -2147483647 - 1 - val MAX_VALUE: Int = 2147483647 + @JsName("MIN_VALUE") val MIN_VALUE: Int = -2147483647 - 1 + @JsName("MAX_VALUE") val MAX_VALUE: Int = 2147483647 } private object LongCompanionObject { - val MIN_VALUE: Long = js("Kotlin.Long.MIN_VALUE") - val MAX_VALUE: Long = js("Kotlin.Long.MAX_VALUE") + @JsName("MIN_VALUE") val MIN_VALUE: Long = js("Kotlin.Long.MIN_VALUE") + @JsName("MAX_VALUE") val MAX_VALUE: Long = js("Kotlin.Long.MAX_VALUE") } private object ShortCompanionObject { - val MIN_VALUE: Short = -32768 - val MAX_VALUE: Short = 32767 + @JsName("MIN_VALUE") val MIN_VALUE: Short = -32768 + @JsName("MAX_VALUE") val MAX_VALUE: Short = 32767 } private object ByteCompanionObject { - val MIN_VALUE: Byte = -128 - val MAX_VALUE: Byte = 127 + @JsName("MIN_VALUE") val MIN_VALUE: Byte = -128 + @JsName("MAX_VALUE") val MAX_VALUE: Byte = 127 } private object CharCompanionObject { - public const val MIN_HIGH_SURROGATE: Char = '\uD800' - public const val MAX_HIGH_SURROGATE: Char = '\uDBFF' - public const val MIN_LOW_SURROGATE: Char = '\uDC00' - public const val MAX_LOW_SURROGATE: Char = '\uDFFF' - public const val MIN_SURROGATE: Char = MIN_HIGH_SURROGATE - public const val MAX_SURROGATE: Char = MAX_LOW_SURROGATE + @JsName("MIN_HIGH_SURROGATE") public const val MIN_HIGH_SURROGATE: Char = '\uD800' + @JsName("MAX_HIGH_SURROGATE") public const val MAX_HIGH_SURROGATE: Char = '\uDBFF' + @JsName("MIN_LOW_SURROGATE") public const val MIN_LOW_SURROGATE: Char = '\uDC00' + @JsName("MAX_LOW_SURROGATE") public const val MAX_LOW_SURROGATE: Char = '\uDFFF' + @JsName("MIN_SURROGATE") public const val MIN_SURROGATE: Char = MIN_HIGH_SURROGATE + @JsName("MAX_SURROGATE") public const val MAX_SURROGATE: Char = MAX_LOW_SURROGATE } private object StringCompanionObject {} diff --git a/js/js.translator/testData/box/expression/function/manglingStability.kt b/js/js.translator/testData/box/expression/function/manglingStability.kt index 5726b15239e..11aba29b43f 100644 --- a/js/js.translator/testData/box/expression/function/manglingStability.kt +++ b/js/js.translator/testData/box/expression/function/manglingStability.kt @@ -56,12 +56,12 @@ val mixed_b = { mixed_boo() + mixed_boo(1) + mixed_boo("str") + mixed_boo("str", class TestInternal { - fun foo(): Int = 1 - fun foo(i: Int): Int = 2 + @JsName("foo") internal fun foo(): Int = 1 + @JsName("foo_2") internal fun foo(i: Int): Int = 2 - fun boo(i: Int): Int = 2 - fun boo(s: String): Int = 3 - fun boo(): Int = 1 + @JsName("boo_2") internal fun boo(i: Int): Int = 2 + @JsName("boo_3") internal fun boo(s: String): Int = 3 + @JsName("boo") internal fun boo(): Int = 1 } val internal_in_class_f = { TestInternal().foo() + TestInternal().foo(1) } @@ -128,25 +128,15 @@ val public_ext_f: A.() -> Int = { -> this.foo() + this.foo } val public_ext_b: A.() -> Int = { -> this.boo() + this.boo } interface TestPublicInTrait { - public fun foo(): Int = 2 - public val foo: Int - public val boo: Int - public fun boo(): Int = 2 + @JsName("foo") fun foo(): Int = 2 + @JsName("fooProp") val foo: Int + @JsName("booProp") val boo: Int + @JsName("boo") fun boo(): Int = 2 } val public_in_trait_f = { obj: TestPublicInTrait -> obj.foo() + obj.foo } val public_in_trait_b = { obj: TestPublicInTrait -> obj.boo() + obj.boo } -interface TestInternalInTrait { - fun foo(): Int = 2 - val foo: Int - val boo: Int - fun boo(): Int = 2 -} - -val internal_in_trait_f = { obj: TestInternalInTrait -> obj.foo() + obj.foo } -val internal_in_trait_b = { obj: TestInternalInTrait -> obj.boo() + obj.boo } - //Testing fun test(testName: String, ff: Any, fb: Any) { @@ -169,7 +159,6 @@ fun box(): String { test("public_ext_prop", public_ext_f, public_ext_b) test("public_in_trait", public_in_trait_f, public_in_trait_b) - test("internal_in_trait", internal_in_trait_f, internal_in_trait_b) return "OK" } diff --git a/js/js.translator/testData/webDemoCanvasExamples/cases/Traffic light.kt b/js/js.translator/testData/webDemoCanvasExamples/cases/Traffic light.kt index 16fb88b6794..15bfac35bad 100644 --- a/js/js.translator/testData/webDemoCanvasExamples/cases/Traffic light.kt +++ b/js/js.translator/testData/webDemoCanvasExamples/cases/Traffic light.kt @@ -173,7 +173,7 @@ class TrafficLight(override var pos: Vector, val direction: String, val startCol var timer = Timer(Vector(pos.x + 6, pos.y + 12)) var currentColor = startColor; var isForceColorChange = false - var changeColorForward = (startColor == "red") + private var changeColorForward = (startColor == "red") init { list.add(TrafficLightItem(v(pos.x, pos.y), PATH_TO_IMAGES + "red_color.png"))