Refact stdlib generator, add support for different backends
[JS IR BE] Runtime fixes * Do not generate external declarations for IR BE * Move `arrayToString` helper function out of shared JS stdlib * Fix arrays type check for IR BE
This commit is contained in:
@@ -46,14 +46,16 @@ fun main(args: Array<String>) {
|
||||
val commonDir = baseDir.resolveExistingDir("libraries/stdlib/common/src/generated")
|
||||
val jvmDir = baseDir.resolveExistingDir("libraries/stdlib/jvm/src/generated")
|
||||
val jsDir = baseDir.resolveExistingDir("libraries/stdlib/js/src/generated")
|
||||
val jsIrDir = baseDir.resolveExistingDir("libraries/stdlib/js/irRuntime/generated")
|
||||
|
||||
templateGroups.groupByFileAndWrite { (platform, source) ->
|
||||
templateGroups.groupByFileAndWrite { (target, source) ->
|
||||
// File("build/out/$platform/$source.kt")
|
||||
when (platform) {
|
||||
Platform.Common -> commonDir.resolve("_${source.name.capitalize()}.kt")
|
||||
Platform.JVM -> jvmDir.resolve("_${source.name.capitalize()}Jvm.kt")
|
||||
Platform.JS -> jsDir.resolve("_${source.name.capitalize()}Js.kt")
|
||||
Platform.Native -> error("Native is unsupported yet")
|
||||
when (target) {
|
||||
KotlinTarget.Common -> commonDir.resolve("_${source.name.capitalize()}.kt")
|
||||
KotlinTarget.JVM -> jvmDir.resolve("_${source.name.capitalize()}Jvm.kt")
|
||||
KotlinTarget.JS -> jsDir.resolve("_${source.name.capitalize()}Js.kt")
|
||||
KotlinTarget.JS_IR -> jsIrDir.resolve("_${source.name.capitalize()}Js.kt")
|
||||
KotlinTarget.Native -> error("Native is unsupported yet")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,8 +83,14 @@ object ArrayOps : TemplateGroupBase() {
|
||||
}
|
||||
|
||||
on(Platform.JS) {
|
||||
annotation("""@library("arrayEquals")""")
|
||||
body { "definedExternally" }
|
||||
on(Backend.Legacy) {
|
||||
annotation("""@library("arrayEquals")""")
|
||||
body { "definedExternally" }
|
||||
}
|
||||
|
||||
on(Backend.IR) {
|
||||
body { "return contentEqualsInternal(other)" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,8 +122,14 @@ object ArrayOps : TemplateGroupBase() {
|
||||
}
|
||||
}
|
||||
on(Platform.JS) {
|
||||
annotation("""@library("arrayDeepEquals")""")
|
||||
body { "definedExternally" }
|
||||
on(Backend.Legacy) {
|
||||
annotation("""@library("arrayDeepEquals")""")
|
||||
body { "definedExternally" }
|
||||
}
|
||||
|
||||
on(Backend.IR) {
|
||||
body { "return contentDeepEqualsImpl(other)" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,8 +153,13 @@ object ArrayOps : TemplateGroupBase() {
|
||||
body { "return java.util.Arrays.toString(this)" }
|
||||
}
|
||||
on(Platform.JS) {
|
||||
annotation("""@library("arrayToString")""")
|
||||
body { "definedExternally" }
|
||||
on(Backend.Legacy) {
|
||||
annotation("""@library("arrayToString")""")
|
||||
body { "definedExternally" }
|
||||
}
|
||||
on(Backend.IR) {
|
||||
body { "return arrayToString(this as Array<*>)" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,8 +191,13 @@ object ArrayOps : TemplateGroupBase() {
|
||||
}
|
||||
}
|
||||
on(Platform.JS) {
|
||||
annotation("""@library("arrayDeepToString")""")
|
||||
body { "definedExternally" }
|
||||
on(Backend.Legacy) {
|
||||
annotation("""@library("arrayDeepToString")""")
|
||||
body { "definedExternally" }
|
||||
}
|
||||
on(Backend.IR) {
|
||||
body { "return contentDeepToStringImpl()" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,8 +218,13 @@ object ArrayOps : TemplateGroupBase() {
|
||||
body { "return java.util.Arrays.hashCode(this)" }
|
||||
}
|
||||
on(Platform.JS) {
|
||||
annotation("""@library("arrayHashCode")""")
|
||||
body { "definedExternally" }
|
||||
on(Backend.Legacy) {
|
||||
annotation("""@library("arrayHashCode")""")
|
||||
body { "definedExternally" }
|
||||
}
|
||||
on(Backend.IR) {
|
||||
body { "return contentHashCodeInternal()" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,8 +254,13 @@ object ArrayOps : TemplateGroupBase() {
|
||||
}
|
||||
}
|
||||
on(Platform.JS) {
|
||||
annotation("""@library("arrayDeepHashCode")""")
|
||||
body { "definedExternally" }
|
||||
on(Backend.Legacy) {
|
||||
annotation("""@library("arrayDeepHashCode")""")
|
||||
body { "definedExternally" }
|
||||
}
|
||||
on(Backend.IR) {
|
||||
body { "return contentDeepHashCodeInternal()" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -756,8 +788,13 @@ object ArrayOps : TemplateGroupBase() {
|
||||
}
|
||||
specialFor(ArraysOfPrimitives) {
|
||||
if (primitive != PrimitiveType.Long) {
|
||||
annotation("""@library("primitiveArraySort")""")
|
||||
body { "definedExternally" }
|
||||
on(Backend.Legacy) {
|
||||
annotation("""@library("primitiveArraySort")""")
|
||||
body { "definedExternally" }
|
||||
}
|
||||
on(Backend.IR) {
|
||||
body { "this.asDynamic().sort()" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,12 +86,26 @@ enum class Platform {
|
||||
Common,
|
||||
JVM,
|
||||
JS,
|
||||
Native;
|
||||
Native
|
||||
}
|
||||
|
||||
enum class Backend {
|
||||
Any,
|
||||
Legacy,
|
||||
IR
|
||||
}
|
||||
|
||||
enum class KotlinTarget(val platform: Platform, val backend: Backend) {
|
||||
Common(Platform.Common, Backend.Any),
|
||||
JVM(Platform.JVM, Backend.Any),
|
||||
JS(Platform.JS, Backend.Legacy),
|
||||
JS_IR(Platform.JS, Backend.IR),
|
||||
Native(Platform.Native, Backend.IR);
|
||||
|
||||
val fullName get() = "Kotlin/$name"
|
||||
|
||||
companion object {
|
||||
val values = values().toList()
|
||||
val values = KotlinTarget.values().toList()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ private fun getDefaultSourceFile(f: Family): SourceFile = when (f) {
|
||||
@TemplateDsl
|
||||
class MemberBuilder(
|
||||
val allowedPlatforms: Set<Platform>,
|
||||
val platform: Platform,
|
||||
val target: KotlinTarget,
|
||||
var family: Family,
|
||||
var sourceFile: SourceFile = getDefaultSourceFile(family),
|
||||
var primitive: PrimitiveType? = null
|
||||
@@ -143,13 +143,18 @@ class MemberBuilder(
|
||||
|
||||
fun on(platform: Platform, action: () -> Unit) {
|
||||
require(platform in allowedPlatforms) { "Platform $platform is not in the list of allowed platforms $allowedPlatforms" }
|
||||
if (this.platform == platform)
|
||||
if (target.platform == platform)
|
||||
action()
|
||||
else {
|
||||
hasPlatformSpecializations = true
|
||||
}
|
||||
}
|
||||
|
||||
fun on(backend: Backend, action: () -> Unit) {
|
||||
require(target.platform == Platform.JS)
|
||||
if (target.backend == backend) action()
|
||||
}
|
||||
|
||||
fun specialFor(f: Family, action: () -> Unit) {
|
||||
if (family == f)
|
||||
action()
|
||||
@@ -165,14 +170,14 @@ class MemberBuilder(
|
||||
val headerOnly: Boolean
|
||||
val isImpl: Boolean
|
||||
if (!legacyMode) {
|
||||
headerOnly = platform == Platform.Common && hasPlatformSpecializations
|
||||
isImpl = platform != Platform.Common && Platform.Common in allowedPlatforms
|
||||
headerOnly = target.platform == Platform.Common && hasPlatformSpecializations
|
||||
isImpl = target.platform != Platform.Common && Platform.Common in allowedPlatforms
|
||||
}
|
||||
else {
|
||||
// legacy mode when all is headerOnly + no_impl
|
||||
// except functions with optional parameters - they are common + no_impl
|
||||
val hasOptionalParams = signature.contains("=")
|
||||
headerOnly = platform == Platform.Common && !hasOptionalParams
|
||||
headerOnly = target.platform == Platform.Common && !hasOptionalParams
|
||||
isImpl = false
|
||||
}
|
||||
|
||||
@@ -376,7 +381,7 @@ class MemberBuilder(
|
||||
|
||||
val body = (body ?:
|
||||
deprecate?.replaceWith?.let { "return $it" } ?:
|
||||
throw RuntimeException("$signature for ${platform.fullName}: no body specified for ${family to primitive}")
|
||||
throw RuntimeException("$signature for ${target.fullName}: no body specified for ${family to primitive}")
|
||||
).trim('\n')
|
||||
val indent: Int = body.takeWhile { it == ' ' }.length
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ interface MemberTemplate {
|
||||
/** Specifies which platforms this member template should be generated for */
|
||||
fun platforms(vararg platforms: Platform)
|
||||
|
||||
fun instantiate(platforms: List<Platform> = Platform.values): Sequence<MemberBuilder>
|
||||
fun instantiate(targets: List<KotlinTarget> = KotlinTarget.values): Sequence<MemberBuilder>
|
||||
|
||||
/** Registers parameterless member builder function */
|
||||
fun builder(b: MemberBuildAction)
|
||||
@@ -78,9 +78,9 @@ abstract class MemberTemplateDefinition<TParam> : MemberTemplate {
|
||||
|
||||
private val buildActions = mutableListOf<BuildAction>()
|
||||
|
||||
private var targetPlatforms = setOf(*Platform.values())
|
||||
private var allowedPlatforms = setOf(*Platform.values())
|
||||
override fun platforms(vararg platforms: Platform) {
|
||||
targetPlatforms = setOf(*platforms)
|
||||
allowedPlatforms = setOf(*platforms)
|
||||
}
|
||||
|
||||
|
||||
@@ -105,23 +105,24 @@ abstract class MemberTemplateDefinition<TParam> : MemberTemplate {
|
||||
} ?: this
|
||||
|
||||
|
||||
override fun instantiate(platforms: List<Platform>): Sequence<MemberBuilder> {
|
||||
val resultingPlatforms = platforms.intersect(targetPlatforms)
|
||||
val specificPlatforms by lazy { resultingPlatforms - Platform.Common }
|
||||
override fun instantiate(targets: List<KotlinTarget>): Sequence<MemberBuilder> {
|
||||
val resultingTargets = targets.filter { it.platform in allowedPlatforms }
|
||||
val resultingPlatforms = resultingTargets.map { it.platform }.distinct()
|
||||
val specificTargets by lazy { resultingTargets - KotlinTarget.Common }
|
||||
|
||||
fun platformMemberBuilders(family: Family, p: TParam) =
|
||||
if (Platform.Common in targetPlatforms) {
|
||||
val commonMemberBuilder = createMemberBuilder(Platform.Common, family, p)
|
||||
if (Platform.Common in allowedPlatforms) {
|
||||
val commonMemberBuilder = createMemberBuilder(KotlinTarget.Common, family, p)
|
||||
mutableListOf<MemberBuilder>().also { builders ->
|
||||
if (Platform.Common in resultingPlatforms) builders.add(commonMemberBuilder)
|
||||
if (commonMemberBuilder.hasPlatformSpecializations) {
|
||||
specificPlatforms.mapTo(builders) {
|
||||
specificTargets.mapTo(builders) {
|
||||
createMemberBuilder(it, family, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
resultingPlatforms.map { createMemberBuilder(it, family, p) }
|
||||
resultingTargets.map { createMemberBuilder(it, family, p) }
|
||||
}
|
||||
|
||||
return parametrize()
|
||||
@@ -130,8 +131,8 @@ abstract class MemberTemplateDefinition<TParam> : MemberTemplate {
|
||||
.flatten()
|
||||
}
|
||||
|
||||
private fun createMemberBuilder(platform: Platform, family: Family, p: TParam): MemberBuilder {
|
||||
return MemberBuilder(targetPlatforms, platform, family).also { builder ->
|
||||
private fun createMemberBuilder(target: KotlinTarget, family: Family, p: TParam): MemberBuilder {
|
||||
return MemberBuilder(allowedPlatforms, target, family).also { builder ->
|
||||
for (action in buildActions) {
|
||||
when (action) {
|
||||
is BuildAction.Generic -> action(builder)
|
||||
|
||||
@@ -29,37 +29,37 @@ fun readCopyrightNoticeFromProfile(copyrightProfile: File): String {
|
||||
}
|
||||
|
||||
|
||||
data class PlatformSourceFile(
|
||||
val platform: Platform,
|
||||
val sourceFile: SourceFile
|
||||
data class TargetedSourceFile(
|
||||
val target: KotlinTarget,
|
||||
val sourceFile: SourceFile
|
||||
)
|
||||
|
||||
private val platformsToGenerate = Platform.values - Platform.Native
|
||||
private val targetsToGenerate = KotlinTarget.values - KotlinTarget.Native
|
||||
|
||||
@JvmName("groupByFileAndWriteGroups")
|
||||
fun Sequence<TemplateGroup>.groupByFileAndWrite(
|
||||
fileNameBuilder: (PlatformSourceFile) -> File
|
||||
fileNameBuilder: (TargetedSourceFile) -> File
|
||||
) {
|
||||
flatMap { group ->
|
||||
group.invoke()
|
||||
.flatMap { it.instantiate(platformsToGenerate) }
|
||||
.flatMap { it.instantiate(targetsToGenerate) }
|
||||
.sortedBy { it.sortingSignature }
|
||||
}.groupByFileAndWrite(fileNameBuilder)
|
||||
}
|
||||
|
||||
@JvmName("groupByFileAndWriteTemplates")
|
||||
fun Sequence<MemberTemplate>.groupByFileAndWrite(
|
||||
fileNameBuilder: (PlatformSourceFile) -> File
|
||||
fileNameBuilder: (TargetedSourceFile) -> File
|
||||
) {
|
||||
flatMap { it.instantiate(platformsToGenerate) }
|
||||
flatMap { it.instantiate(targetsToGenerate) }
|
||||
.groupByFileAndWrite(fileNameBuilder)
|
||||
}
|
||||
|
||||
@JvmName("groupByFileAndWriteMembers")
|
||||
fun Sequence<MemberBuilder>.groupByFileAndWrite(
|
||||
fileNameBuilder: (PlatformSourceFile) -> File
|
||||
fileNameBuilder: (TargetedSourceFile) -> File
|
||||
) {
|
||||
val groupedMembers = groupBy { PlatformSourceFile(it.platform, it.sourceFile) }
|
||||
val groupedMembers = groupBy { TargetedSourceFile(it.target, it.sourceFile) }
|
||||
|
||||
for ((psf, members) in groupedMembers) {
|
||||
val file = fileNameBuilder(psf)
|
||||
@@ -67,14 +67,14 @@ fun Sequence<MemberBuilder>.groupByFileAndWrite(
|
||||
}
|
||||
}
|
||||
|
||||
fun List<MemberBuilder>.writeTo(file: File, platformSource: PlatformSourceFile) {
|
||||
val (platform, sourceFile) = platformSource
|
||||
fun List<MemberBuilder>.writeTo(file: File, targetedSource: TargetedSourceFile) {
|
||||
val (target, sourceFile) = targetedSource
|
||||
println("Generating file: $file")
|
||||
file.parentFile.mkdirs()
|
||||
FileWriter(file).use { writer ->
|
||||
writer.appendln(COPYRIGHT_NOTICE)
|
||||
|
||||
when (platform) {
|
||||
when (target.platform) {
|
||||
Platform.Common, Platform.JVM -> {
|
||||
if (sourceFile.multifile) {
|
||||
writer.appendln("@file:kotlin.jvm.JvmMultifileClass")
|
||||
@@ -87,14 +87,14 @@ fun List<MemberBuilder>.writeTo(file: File, platformSource: PlatformSourceFile)
|
||||
|
||||
writer.append("package ${sourceFile.packageName ?: "kotlin"}\n\n")
|
||||
writer.append("${COMMON_AUTOGENERATED_WARNING}\n\n")
|
||||
if (platform == Platform.JS) {
|
||||
if (target.platform == Platform.JS) {
|
||||
writer.appendln("import kotlin.js.*")
|
||||
if (sourceFile == SourceFile.Arrays) {
|
||||
writer.appendln("import primitiveArrayConcat")
|
||||
writer.appendln("import withType")
|
||||
}
|
||||
}
|
||||
if (platform == Platform.Common) {
|
||||
if (target.platform == Platform.Common) {
|
||||
writer.appendln("import kotlin.random.*")
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user