[JS IR BE] Add JsExport annotations
This commit is contained in:
@@ -48,6 +48,7 @@ class JsIrBackendContext(
|
|||||||
override val irBuiltIns: IrBuiltIns,
|
override val irBuiltIns: IrBuiltIns,
|
||||||
val symbolTable: SymbolTable,
|
val symbolTable: SymbolTable,
|
||||||
irModuleFragment: IrModuleFragment,
|
irModuleFragment: IrModuleFragment,
|
||||||
|
val additionalExportedDeclarations: Set<FqName>,
|
||||||
override val configuration: CompilerConfiguration
|
override val configuration: CompilerConfiguration
|
||||||
) : CommonBackendContext {
|
) : CommonBackendContext {
|
||||||
|
|
||||||
@@ -275,6 +276,8 @@ class JsIrBackendContext(
|
|||||||
val throwableConstructors by lazy { throwableClass.owner.declarations.filterIsInstance<IrConstructor>().map { it.symbol } }
|
val throwableConstructors by lazy { throwableClass.owner.declarations.filterIsInstance<IrConstructor>().map { it.symbol } }
|
||||||
val defaultThrowableCtor by lazy { throwableConstructors.single { it.owner.valueParameters.size == 0 } }
|
val defaultThrowableCtor by lazy { throwableConstructors.single { it.owner.valueParameters.size == 0 } }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private fun referenceOperators(): Map<Name, MutableMap<IrClassifierSymbol, IrSimpleFunctionSymbol>> {
|
private fun referenceOperators(): Map<Name, MutableMap<IrClassifierSymbol, IrSimpleFunctionSymbol>> {
|
||||||
val primitiveIrSymbols = irBuiltIns.primitiveIrTypes.map { it.classifierOrFail as IrClassSymbol }
|
val primitiveIrSymbols = irBuiltIns.primitiveIrTypes.map { it.classifierOrFail as IrClassSymbol }
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
|||||||
import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator
|
import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator
|
||||||
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
|
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
|
||||||
import org.jetbrains.kotlin.library.KotlinLibrary
|
import org.jetbrains.kotlin.library.KotlinLibrary
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.utils.DFS
|
import org.jetbrains.kotlin.utils.DFS
|
||||||
|
|
||||||
@@ -34,7 +35,8 @@ fun compile(
|
|||||||
phaseConfig: PhaseConfig,
|
phaseConfig: PhaseConfig,
|
||||||
allDependencies: List<KotlinLibrary>,
|
allDependencies: List<KotlinLibrary>,
|
||||||
friendDependencies: List<KotlinLibrary>,
|
friendDependencies: List<KotlinLibrary>,
|
||||||
mainArguments: List<String>?
|
mainArguments: List<String>?,
|
||||||
|
exportedDeclarations: Set<FqName> = emptySet()
|
||||||
): String {
|
): String {
|
||||||
val (moduleFragment, dependencyModules, irBuiltIns, symbolTable, deserializer) =
|
val (moduleFragment, dependencyModules, irBuiltIns, symbolTable, deserializer) =
|
||||||
loadIr(project, files, configuration, allDependencies, friendDependencies)
|
loadIr(project, files, configuration, allDependencies, friendDependencies)
|
||||||
@@ -43,7 +45,7 @@ fun compile(
|
|||||||
|
|
||||||
val mainFunction = JsMainFunctionDetector.getMainFunctionOrNull(moduleFragment)
|
val mainFunction = JsMainFunctionDetector.getMainFunctionOrNull(moduleFragment)
|
||||||
|
|
||||||
val context = JsIrBackendContext(moduleDescriptor, irBuiltIns, symbolTable, moduleFragment, configuration)
|
val context = JsIrBackendContext(moduleDescriptor, irBuiltIns, symbolTable, moduleFragment, exportedDeclarations, configuration)
|
||||||
|
|
||||||
// Load declarations referenced during `context` initialization
|
// Load declarations referenced during `context` initialization
|
||||||
dependencyModules.forEach {
|
dependencyModules.forEach {
|
||||||
|
|||||||
+28
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
|||||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||||
|
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||||
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
||||||
import org.jetbrains.kotlin.ir.util.isObject
|
import org.jetbrains.kotlin.ir.util.isObject
|
||||||
import org.jetbrains.kotlin.js.backend.ast.*
|
import org.jetbrains.kotlin.js.backend.ast.*
|
||||||
@@ -94,6 +95,9 @@ class IrModuleToJsTransformer(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!declaration.isExported())
|
||||||
|
return null
|
||||||
|
|
||||||
if (declaration.isEffectivelyExternal())
|
if (declaration.isEffectivelyExternal())
|
||||||
return null
|
return null
|
||||||
|
|
||||||
@@ -278,4 +282,28 @@ class IrModuleToJsTransformer(
|
|||||||
declarationHandler
|
declarationHandler
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun IrDeclarationWithName.isExported(): Boolean {
|
||||||
|
if (fqNameWhenAvailable in backendContext.additionalExportedDeclarations)
|
||||||
|
return true
|
||||||
|
|
||||||
|
// Hack to support properties
|
||||||
|
val correspondingProperty = when {
|
||||||
|
this is IrField -> correspondingPropertySymbol
|
||||||
|
this is IrSimpleFunction -> correspondingPropertySymbol
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
correspondingProperty?.let {
|
||||||
|
return it.owner.isExported()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isJsExport())
|
||||||
|
return true
|
||||||
|
|
||||||
|
return when (val parent = parent) {
|
||||||
|
is IrDeclarationWithName -> parent.isExported()
|
||||||
|
is IrAnnotationContainer -> parent.isJsExport()
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+4
-2
@@ -5,8 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.ir.backend.js.utils
|
package org.jetbrains.kotlin.ir.backend.js.utils
|
||||||
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
|
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrConst
|
import org.jetbrains.kotlin.ir.expressions.IrConst
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
|
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
|
||||||
@@ -20,6 +19,7 @@ object JsAnnotations {
|
|||||||
val jsNonModuleFqn = FqName("kotlin.js.JsNonModule")
|
val jsNonModuleFqn = FqName("kotlin.js.JsNonModule")
|
||||||
val jsNameFqn = FqName("kotlin.js.JsName")
|
val jsNameFqn = FqName("kotlin.js.JsName")
|
||||||
val jsQualifierFqn = FqName("kotlin.js.JsQualifier")
|
val jsQualifierFqn = FqName("kotlin.js.JsQualifier")
|
||||||
|
val jsExportFqn = FqName("kotlin.js.JsExport")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
@@ -38,6 +38,8 @@ fun IrAnnotationContainer.getJsQualifier(): String? =
|
|||||||
fun IrAnnotationContainer.getJsName(): String? =
|
fun IrAnnotationContainer.getJsName(): String? =
|
||||||
getAnnotation(JsAnnotations.jsNameFqn)?.getSingleConstStringArgument()
|
getAnnotation(JsAnnotations.jsNameFqn)?.getSingleConstStringArgument()
|
||||||
|
|
||||||
|
fun IrAnnotationContainer.isJsExport(): Boolean =
|
||||||
|
hasAnnotation(JsAnnotations.jsExportFqn)
|
||||||
|
|
||||||
fun IrDeclarationWithName.getJsNameOrKotlinName(): Name =
|
fun IrDeclarationWithName.getJsNameOrKotlinName(): Name =
|
||||||
when (val jsName = getJsName()) {
|
when (val jsName = getJsName()) {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
|||||||
import org.jetbrains.kotlin.js.config.JsConfig
|
import org.jetbrains.kotlin.js.config.JsConfig
|
||||||
import org.jetbrains.kotlin.js.facade.MainCallParameters
|
import org.jetbrains.kotlin.js.facade.MainCallParameters
|
||||||
import org.jetbrains.kotlin.js.facade.TranslationUnit
|
import org.jetbrains.kotlin.js.facade.TranslationUnit
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.test.TargetBackend
|
import org.jetbrains.kotlin.test.TargetBackend
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@@ -108,7 +109,8 @@ abstract class BasicIrBoxTest(
|
|||||||
phaseConfig = phaseConfig,
|
phaseConfig = phaseConfig,
|
||||||
allDependencies = allDependencies,
|
allDependencies = allDependencies,
|
||||||
friendDependencies = emptyList(),
|
friendDependencies = emptyList(),
|
||||||
mainArguments = mainCallParameters.run { if (shouldBeGenerated()) arguments() else null }
|
mainArguments = mainCallParameters.run { if (shouldBeGenerated()) arguments() else null },
|
||||||
|
exportedDeclarations = setOf(FqName.fromSegments(listOfNotNull(testPackage, testFunction)))
|
||||||
)
|
)
|
||||||
|
|
||||||
val wrappedCode = wrapWithModuleEmulationMarkers(jsCode, moduleId = config.moduleId, moduleKind = config.moduleKind)
|
val wrappedCode = wrapWithModuleEmulationMarkers(jsCode, moduleId = config.moduleId, moduleKind = config.moduleKind)
|
||||||
|
|||||||
-5
@@ -1520,11 +1520,6 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
|
|||||||
runTest("js/js.translator/testData/box/expression/cast/checkThrowCCE.kt");
|
runTest("js/js.translator/testData/box/expression/cast/checkThrowCCE.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("explicitUpcast.kt")
|
|
||||||
public void testExplicitUpcast() throws Exception {
|
|
||||||
runTest("js/js.translator/testData/box/expression/cast/explicitUpcast.kt");
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestMetadata("implicitCastToLong.kt")
|
@TestMetadata("implicitCastToLong.kt")
|
||||||
public void testImplicitCastToLong() throws Exception {
|
public void testImplicitCastToLong() throws Exception {
|
||||||
runTest("js/js.translator/testData/box/expression/cast/implicitCastToLong.kt");
|
runTest("js/js.translator/testData/box/expression/cast/implicitCastToLong.kt");
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
// SKIP_MINIFICATION
|
// SKIP_MINIFICATION
|
||||||
@JsName("foo")
|
@JsName("foo")
|
||||||
|
@JsExport
|
||||||
fun foo(): Char = '1'
|
fun foo(): Char = '1'
|
||||||
|
|
||||||
|
@JsExport
|
||||||
val p1: Char = '2'
|
val p1: Char = '2'
|
||||||
|
|
||||||
|
@JsExport
|
||||||
var p2: Char = '3'
|
var p2: Char = '3'
|
||||||
|
|
||||||
|
@JsExport
|
||||||
var p3: Char = '4'
|
var p3: Char = '4'
|
||||||
get() = field + 1
|
get() = field + 1
|
||||||
set(value) {
|
set(value) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
// MODULE: non-identifier-module-name
|
// MODULE: non-identifier-module-name
|
||||||
// FILE: lib.kt
|
// FILE: lib.kt
|
||||||
@JsName("foo")
|
@JsName("foo")
|
||||||
|
@JsExport
|
||||||
public fun foo(k: String): String = "O$k"
|
public fun foo(k: String): String = "O$k"
|
||||||
|
|
||||||
// FILE: test.js
|
// FILE: test.js
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
// MODULE: if
|
// MODULE: if
|
||||||
// FILE: lib.kt
|
// FILE: lib.kt
|
||||||
@JsName("foo")
|
@JsName("foo")
|
||||||
|
@JsExport
|
||||||
public fun foo(k: String): String = "O$k"
|
public fun foo(k: String): String = "O$k"
|
||||||
|
|
||||||
// FILE: test.js
|
// FILE: test.js
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
// SKIP_MINIFICATION
|
// SKIP_MINIFICATION
|
||||||
// This test assumes that external JS code calls Kotlin code directly
|
// This test assumes that external JS code calls Kotlin code directly
|
||||||
|
|
||||||
|
// Legacy export, wrong types
|
||||||
|
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||||
|
|
||||||
open class A
|
open class A
|
||||||
|
|
||||||
class B : A()
|
class B : A()
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ inline fun ok(): I {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@JsName("convolutedOk")
|
@JsName("convolutedOk")
|
||||||
|
@JsExport
|
||||||
inline fun convolutedOk(): I {
|
inline fun convolutedOk(): I {
|
||||||
val fail = object : I {
|
val fail = object : I {
|
||||||
override fun ok() = "fail"
|
override fun ok() = "fail"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// EXPECTED_REACHABLE_NODES: 1290
|
// EXPECTED_REACHABLE_NODES: 1290
|
||||||
|
|
||||||
|
@JsExport
|
||||||
data class A(val value: Int)
|
data class A(val value: Int)
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
+4
-4
@@ -6,10 +6,10 @@ object A {
|
|||||||
@JsName("js_property") val f: String get() = "property"
|
@JsName("js_property") val f: String get() = "property"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun test() = js("""
|
fun test(): dynamic {
|
||||||
var a = JS_TESTS.A;
|
val a = A.asDynamic()
|
||||||
return a.js_method() + ";" + a.js_property;
|
return a.js_method() + ";" + a.js_property
|
||||||
""")
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
val result = test()
|
val result = test()
|
||||||
|
|||||||
@@ -8,10 +8,9 @@ class B : A() {
|
|||||||
override fun f(x: Int) = "B.f($x)"
|
override fun f(x: Int) = "B.f($x)"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun test() = js("""
|
fun test(): dynamic {
|
||||||
var module = JS_TESTS;
|
return A().asDynamic().js_f(23) + ";" + B().asDynamic().js_f(42)
|
||||||
return new (module.A)().js_f(23) + ";" + new (module.B)().js_f(42);
|
}
|
||||||
""")
|
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
val result = test()
|
val result = test()
|
||||||
|
|||||||
@@ -8,10 +8,9 @@ class B : A {
|
|||||||
override fun f(x: Int) = "B.f($x)"
|
override fun f(x: Int) = "B.f($x)"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun test() = js("""
|
fun test(): dynamic {
|
||||||
var module = JS_TESTS;
|
return B().asDynamic().js_f(23)
|
||||||
return new (module.B)().js_f(23);
|
}
|
||||||
""")
|
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
val result = test()
|
val result = test()
|
||||||
|
|||||||
+4
-4
@@ -10,10 +10,10 @@ object A {
|
|||||||
@JsName("js_q") val q: String get() = "q"
|
@JsName("js_q") val q: String get() = "q"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun test() = js("""
|
fun test(): dynamic {
|
||||||
var a = JS_TESTS.A;
|
var a = A.asDynamic()
|
||||||
return a.js_f(23) + ";" + a.js_g(42) + ";" + a.js_p + ";" + a.js_q;
|
return a.js_f(23) + ";" + a.js_g(42) + ";" + a.js_p + ";" + a.js_q
|
||||||
""")
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
val result = test()
|
val result = test()
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// SKIP_MINIFICATION
|
// SKIP_MINIFICATION
|
||||||
|
|
||||||
|
@JsExport
|
||||||
val top = "TOP LEVEL"
|
val top = "TOP LEVEL"
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// SKIP_MINIFICATION
|
// SKIP_MINIFICATION
|
||||||
// Contains calls from external JS code
|
// Contains calls from external JS code
|
||||||
|
|
||||||
|
@JsExport
|
||||||
open class A {
|
open class A {
|
||||||
@JsName("foo")
|
@JsName("foo")
|
||||||
open protected fun foo(n: Int) = 23
|
open protected fun foo(n: Int) = 23
|
||||||
@@ -9,6 +10,7 @@ open class A {
|
|||||||
fun bar(n: Int) = foo(n) + 100
|
fun bar(n: Int) = foo(n) + 100
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsExport
|
||||||
open class B {
|
open class B {
|
||||||
@JsName("foo")
|
@JsName("foo")
|
||||||
protected fun foo(n: Int) = 42
|
protected fun foo(n: Int) = 42
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ class A {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsExport
|
||||||
val A.z: Int
|
val A.z: Int
|
||||||
@JsName("getZ_") get() = 42
|
@JsName("getZ_") get() = 42
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
// EXPECTED_REACHABLE_NODES: 1288
|
// EXPECTED_REACHABLE_NODES: 1288
|
||||||
|
|
||||||
|
@JsExport
|
||||||
val x: Int
|
val x: Int
|
||||||
@JsName("getX_") get() = 23
|
@JsName("getX_") get() = 23
|
||||||
|
|
||||||
|
@JsExport
|
||||||
var y: Int = 0
|
var y: Int = 0
|
||||||
@JsName("getY_") get() = field + 10
|
@JsName("getY_") get() = field + 10
|
||||||
@JsName("setY_") set(value) {
|
@JsName("setY_") set(value) {
|
||||||
|
|||||||
@@ -27,10 +27,13 @@ fun box(): String {
|
|||||||
if (!hasProp(b, "foo")) return "B hasn't foo"
|
if (!hasProp(b, "foo")) return "B hasn't foo"
|
||||||
if (!hasProp(b, "boo")) return "B hasn't boo"
|
if (!hasProp(b, "boo")) return "B hasn't boo"
|
||||||
|
|
||||||
val PREFIX = "_"
|
// Legacy scheme exports interfaces
|
||||||
if (eval("$PREFIX.A") == null) return "$PREFIX.A not found"
|
if (testUtils.isLegacyBackend()) {
|
||||||
if (eval("$PREFIX.B") == null) return "$PREFIX.B not found"
|
val PREFIX = "_"
|
||||||
if (eval("$PREFIX.A === $PREFIX.B") as Boolean) return "A and B refer to the same object"
|
if (eval("$PREFIX.A") == null) return "$PREFIX.A not found"
|
||||||
|
if (eval("$PREFIX.B") == null) return "$PREFIX.B not found"
|
||||||
|
if (eval("$PREFIX.A === $PREFIX.B") as Boolean) return "A and B refer to the same object"
|
||||||
|
}
|
||||||
|
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
@@ -159,4 +159,14 @@ public annotation class JsNonModule
|
|||||||
*/
|
*/
|
||||||
@Retention(AnnotationRetention.BINARY)
|
@Retention(AnnotationRetention.BINARY)
|
||||||
@Target(AnnotationTarget.FILE)
|
@Target(AnnotationTarget.FILE)
|
||||||
public annotation class JsQualifier(val value: String)
|
public annotation class JsQualifier(val value: String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exports top-level declaration.
|
||||||
|
*
|
||||||
|
* Used in future IR-based backend.
|
||||||
|
* Has no effect in current JS backend.
|
||||||
|
*/
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
@Target(CLASS, PROPERTY, FUNCTION, FILE)
|
||||||
|
public annotation class JsExport
|
||||||
|
|||||||
Reference in New Issue
Block a user