From d71af0cfd78313cc37aa373b26b94dfcc98cd294 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Sun, 16 Jan 2022 23:48:36 +0000 Subject: [PATCH] Kotlin metadata annotation remapping (#573) Co-authored-by: Juuxel <6596629+Juuxel@users.noreply.github.com> --- .editorconfig | 2 +- build.gradle | 43 ++- gradle.properties | 4 +- .../loom/configuration/mods/ModProcessor.java | 13 +- .../task/service/TinyRemapperService.java | 12 +- ...ClassMetadataRemappingAnnotationVisitor.kt | 124 ++++++ .../KotlinMetadataRemappingClassVisitor.kt | 47 +++ .../KotlinMetadataTinyRemapperExtension.kt | 39 ++ .../kotlin/remapping/RemappingKmVisitors.kt | 359 ++++++++++++++++++ .../loom/test/LoomTestConstants.groovy | 2 +- .../loom/test/integration/KotlinTest.groovy | 6 + .../loom/test/util/ServerRunner.groovy | 1 + ...sMetadataRemappingAnnotationVisitorTest.kt | 82 ++++ src/test/resources/classes/PosInChunk.class | Bin 0 -> 3410 bytes .../resources/mappings/PosInChunk.mappings | 220 +++++++++++ .../projects/kotlin/build.gradle.kts | 7 +- .../language/kotlin/ExampleSerializable.kt | 9 + .../language/kotlin/IdentifierSerializer.kt | 24 ++ .../fabricmc/language/kotlin/PosInChunk.kt | 10 + .../fabricmc/language/kotlin/TestModClass.kt | 10 +- .../kotlin/src/main/resources/fabric.mod.json | 16 +- 21 files changed, 984 insertions(+), 46 deletions(-) create mode 100644 src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinClassMetadataRemappingAnnotationVisitor.kt create mode 100644 src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinMetadataRemappingClassVisitor.kt create mode 100644 src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinMetadataTinyRemapperExtension.kt create mode 100644 src/main/kotlin/net/fabricmc/loom/kotlin/remapping/RemappingKmVisitors.kt create mode 100644 src/test/kotlin/net/fabricmc/loom/test/kotlin/KotlinClassMetadataRemappingAnnotationVisitorTest.kt create mode 100644 src/test/resources/classes/PosInChunk.class create mode 100644 src/test/resources/mappings/PosInChunk.mappings create mode 100644 src/test/resources/projects/kotlin/src/main/kotlin/net/fabricmc/language/kotlin/ExampleSerializable.kt create mode 100644 src/test/resources/projects/kotlin/src/main/kotlin/net/fabricmc/language/kotlin/IdentifierSerializer.kt create mode 100644 src/test/resources/projects/kotlin/src/main/kotlin/net/fabricmc/language/kotlin/PosInChunk.kt diff --git a/.editorconfig b/.editorconfig index e1774508..ef62c9e2 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,4 @@ -[*.{gradle,java}] +[*.{gradle,java,kotlin}] indent_style = tab ij_continuation_indent_size = 8 ij_java_imports_layout = $*,|,java.**,|,javax.**,|,*,|,net.fabricmc.** diff --git a/build.gradle b/build.gradle index 653b0dff..595c97bc 100644 --- a/build.gradle +++ b/build.gradle @@ -8,6 +8,7 @@ plugins { id 'checkstyle' id 'jacoco' id 'codenarc' + id "org.jetbrains.kotlin.jvm" version "1.5.31" // Must match the version included with gradle. id "com.diffplug.spotless" version "5.14.1" } @@ -19,6 +20,11 @@ tasks.withType(JavaCompile).configureEach { it.options.release = 17 } +tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { + kotlinOptions { + jvmTarget = "16" // Change to 17 when updating gradle/kotlin to 1.6.10 + } +} group = 'net.fabricmc' archivesBaseName = project.name @@ -92,6 +98,11 @@ dependencies { // source code remapping implementation ('net.fabricmc:mercury:0.2.4') + // Kotlin + implementation("org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.4.1") { + transitive = false + } + // Kapt integration compileOnly('org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.0') @@ -100,7 +111,10 @@ dependencies { testImplementation('org.spockframework:spock-core:2.0-groovy-3.0') { exclude module: 'groovy-all' } - testImplementation 'io.javalin:javalin:3.13.11' + testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.8.1' + testImplementation ('io.javalin:javalin:3.13.11') { + exclude group: 'org.jetbrains.kotlin' + } testImplementation 'net.fabricmc:fabric-installer:0.9.0' compileOnly 'org.jetbrains:annotations:23.0.0' @@ -127,6 +141,13 @@ spotless { groovy { licenseHeaderFile(rootProject.file("HEADER")).yearSeparator("-") } + + kotlin { + licenseHeaderFile(rootProject.file("HEADER")).yearSeparator("-") + targetExclude("**/build.gradle.kts") + targetExclude("src/test/resources/projects/*/**") + ktlint() + } } checkstyle { @@ -172,18 +193,6 @@ import org.w3c.dom.Document import org.w3c.dom.Element import org.w3c.dom.Node -def patchPom(groovy.util.Node node) { - node.dependencies.first().each { - def groupId = it.get("groupId").first().value().first() - - // Patch all eclipse deps to use a strict version - if (groupId.startsWith("org.eclipse.")) { - def version = it.get("version").first().value().first() - it.get("version").first().value = new groovy.util.NodeList(["[$version]"]) - } - } -} - publishing { publications { plugin(MavenPublication) { publication -> @@ -192,10 +201,6 @@ publishing { version project.version from components.java - - pom.withXml { - patchPom(asNode()) - } } // Also publish a snapshot so people can use the latest version if they wish @@ -205,10 +210,6 @@ publishing { version baseVersion + '-SNAPSHOT' from components.java - - pom.withXml { - patchPom(asNode()) - } } // Manually crate the plugin marker for snapshot versions diff --git a/gradle.properties b/gradle.properties index 69c68a84..31014b15 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,5 @@ name = fabric-loom description = The Gradle plugin for Fabric -url = https://github.com/FabricMC/fabric-loom \ No newline at end of file +url = https://github.com/FabricMC/fabric-loom + +kotlin.stdlib.default.dependency = false \ No newline at end of file diff --git a/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java b/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java index 9ee6e54a..4775599b 100644 --- a/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java @@ -46,6 +46,7 @@ import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.configuration.RemappedConfigurationEntry; import net.fabricmc.loom.configuration.processors.dependency.ModDependencyInfo; import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; +import net.fabricmc.loom.kotlin.remapping.KotlinMetadataTinyRemapperExtension; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.TinyRemapperHelper; import net.fabricmc.loom.util.ZipUtils; @@ -134,16 +135,22 @@ public class ModProcessor { private void remapJars(List remapList) throws IOException { final LoomGradleExtension extension = LoomGradleExtension.get(project); final MappingsProviderImpl mappingsProvider = extension.getMappingsProvider(); + final boolean useKotlinExtension = project.getPluginManager().hasPlugin("org.jetbrains.kotlin.jvm"); Path[] mcDeps = project.getConfigurations().getByName(Constants.Configurations.LOADER_DEPENDENCIES).getFiles() .stream().map(File::toPath).toArray(Path[]::new); project.getLogger().lifecycle(":remapping " + remapList.size() + " mods (TinyRemapper, " + fromM + " -> " + toM + ")"); - final TinyRemapper remapper = TinyRemapper.newRemapper() + TinyRemapper.Builder builder = TinyRemapper.newRemapper() .withMappings(TinyRemapperHelper.create(mappingsProvider.getMappings(), fromM, toM, false)) - .renameInvalidLocals(false) - .build(); + .renameInvalidLocals(false); + + if (useKotlinExtension) { + builder.extension(KotlinMetadataTinyRemapperExtension.INSTANCE); + } + + final TinyRemapper remapper = builder.build(); for (Path minecraftJar : extension.getMinecraftJars(MappingsNamespace.INTERMEDIARY)) { remapper.readClassPathAsync(minecraftJar); diff --git a/src/main/java/net/fabricmc/loom/task/service/TinyRemapperService.java b/src/main/java/net/fabricmc/loom/task/service/TinyRemapperService.java index 47d9e478..9e05d656 100644 --- a/src/main/java/net/fabricmc/loom/task/service/TinyRemapperService.java +++ b/src/main/java/net/fabricmc/loom/task/service/TinyRemapperService.java @@ -37,6 +37,7 @@ import java.util.Objects; import org.gradle.api.Project; import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.kotlin.remapping.KotlinMetadataTinyRemapperExtension; import net.fabricmc.loom.task.AbstractRemapJarTask; import net.fabricmc.loom.util.service.SharedService; import net.fabricmc.loom.util.service.SharedServiceManager; @@ -52,9 +53,10 @@ public class TinyRemapperService implements SharedService { final LoomGradleExtension extension = LoomGradleExtension.get(project); final SharedServiceManager sharedServiceManager = SharedServiceManager.get(project); final boolean legacyMixin = extension.getMixin().getUseLegacyMixinAp().get(); + final boolean useKotlinExtension = project.getPluginManager().hasPlugin("org.jetbrains.kotlin.jvm"); // Generates an id that is used to share the remapper across projects. This tasks in the remap jar task name to handle custom remap jar tasks separately. - final String id = extension.getMappingsProvider().getBuildServiceName("remapJarService", from, to) + ":" + remapJarTask.getName(); + final String id = extension.getMappingsProvider().getBuildServiceName("remapJarService", from, to) + ":" + remapJarTask.getName() + (useKotlinExtension ? ":kotlin" : ""); TinyRemapperService service = sharedServiceManager.getOrCreateService(id, () -> { List mappings = new ArrayList<>(); @@ -64,7 +66,7 @@ public class TinyRemapperService implements SharedService { mappings.add(MixinMappingsService.getService(SharedServiceManager.get(project)).getMappingProvider(from, to)); } - return new TinyRemapperService(mappings, !legacyMixin); + return new TinyRemapperService(mappings, !legacyMixin, useKotlinExtension); }); service.readClasspath(remapJarTask.getClasspath().getFiles().stream().map(File::toPath).toList()); @@ -78,7 +80,7 @@ public class TinyRemapperService implements SharedService { // Set to true once remapping has started, once set no inputs can be read. private boolean isRemapping = false; - public TinyRemapperService(List mappings, boolean useMixinExtension) { + public TinyRemapperService(List mappings, boolean useMixinExtension, boolean useKotlinExtension) { TinyRemapper.Builder builder = TinyRemapper.newRemapper(); for (IMappingProvider provider : mappings) { @@ -89,6 +91,10 @@ public class TinyRemapperService implements SharedService { builder.extension(new net.fabricmc.tinyremapper.extension.mixin.MixinExtension()); } + if (useKotlinExtension) { + builder.extension(KotlinMetadataTinyRemapperExtension.INSTANCE); + } + tinyRemapper = builder.build(); } diff --git a/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinClassMetadataRemappingAnnotationVisitor.kt b/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinClassMetadataRemappingAnnotationVisitor.kt new file mode 100644 index 00000000..f847fc19 --- /dev/null +++ b/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinClassMetadataRemappingAnnotationVisitor.kt @@ -0,0 +1,124 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.kotlin.remapping + +import kotlinx.metadata.jvm.KotlinClassHeader +import kotlinx.metadata.jvm.KotlinClassMetadata +import org.objectweb.asm.AnnotationVisitor +import org.objectweb.asm.Opcodes +import org.objectweb.asm.commons.Remapper +import org.objectweb.asm.tree.AnnotationNode + +class KotlinClassMetadataRemappingAnnotationVisitor(private val remapper: Remapper, val next: AnnotationVisitor) : + AnnotationNode(Opcodes.ASM9, KotlinMetadataRemappingClassVisitor.ANNOTATION_DESCRIPTOR) { + + private var _name: String? = null + + override fun visit(name: String?, value: Any?) { + super.visit(name, value) + this._name = name + } + + override fun visitEnd() { + super.visitEnd() + when (val metadata = readMetadata()) { + is KotlinClassMetadata.Class -> { + val klass = metadata.toKmClass() + val writer = KotlinClassMetadata.Class.Writer() + klass.accept(RemappingKmVisitors(remapper).RemappingKmClassVisitor(writer)) + writeClassHeader(writer.write().header) + } + is KotlinClassMetadata.SyntheticClass -> { + val klambda = metadata.toKmLambda() + + if (klambda != null) { + val writer = KotlinClassMetadata.SyntheticClass.Writer() + klambda.accept(RemappingKmVisitors(remapper).RemappingKmLambdaVisitor(writer)) + writeClassHeader(writer.write().header) + } else { + accept(next) + } + } + // Can only be turned into KmPackage which is useless data + is KotlinClassMetadata.FileFacade, is KotlinClassMetadata.MultiFileClassPart, + // Can't be turned into data + is KotlinClassMetadata.MultiFileClassFacade, is KotlinClassMetadata.Unknown, null -> { + // do nothing + accept(next) + } + } + } + + @Suppress("UNCHECKED_CAST") + private fun readMetadata(): KotlinClassMetadata? { + var kind: Int? = null + var metadataVersion: IntArray? = null + var data1: Array? = null + var data2: Array? = null + var extraString: String? = null + var packageName: String? = null + var extraInt: Int? = null + + if (values == null) { + return null + } + + values.chunked(2).forEach { (name, value) -> + when (name) { + "k" -> kind = value as Int + "mv" -> metadataVersion = (value as List).toIntArray() + "d1" -> data1 = (value as List).toTypedArray() + "d2" -> data2 = (value as List).toTypedArray() + "xs" -> extraString = value as String + "pn" -> packageName = value as String + "xi" -> extraInt = value as Int + } + } + + val header = KotlinClassHeader(kind, metadataVersion, data1, data2, extraString, packageName, extraInt) + return KotlinClassMetadata.read(header) + } + + private fun writeClassHeader(header: KotlinClassHeader) { + val newNode = AnnotationNode(api, desc) + newNode.values = this.values.toMutableList() + + newNode.run { + for (i in values.indices step 2) { + when (values[i]) { + "k" -> values[i + 1] = header.kind + "mv" -> values[i + 1] = header.metadataVersion.toList() + "d1" -> values[i + 1] = header.data1.toList() + "d2" -> values[i + 1] = header.data2.toList() + "xs" -> values[i + 1] = header.extraString + "pn" -> values[i + 1] = header.packageName + "xi" -> values[i + 1] = header.extraInt + } + } + } + + newNode.accept(next) + } +} diff --git a/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinMetadataRemappingClassVisitor.kt b/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinMetadataRemappingClassVisitor.kt new file mode 100644 index 00000000..f6716a89 --- /dev/null +++ b/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinMetadataRemappingClassVisitor.kt @@ -0,0 +1,47 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.kotlin.remapping + +import org.objectweb.asm.AnnotationVisitor +import org.objectweb.asm.ClassVisitor +import org.objectweb.asm.Opcodes +import org.objectweb.asm.Type +import org.objectweb.asm.commons.Remapper + +class KotlinMetadataRemappingClassVisitor(private val remapper: Remapper, next: ClassVisitor?) : ClassVisitor(Opcodes.ASM9, next) { + companion object { + val ANNOTATION_DESCRIPTOR: String = Type.getDescriptor(Metadata::class.java) + } + + override fun visitAnnotation(descriptor: String, visible: Boolean): AnnotationVisitor? { + var result: AnnotationVisitor? = super.visitAnnotation(descriptor, visible) + + if (descriptor == ANNOTATION_DESCRIPTOR && result != null) { + result = KotlinClassMetadataRemappingAnnotationVisitor(remapper, result) + } + + return result + } +} diff --git a/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinMetadataTinyRemapperExtension.kt b/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinMetadataTinyRemapperExtension.kt new file mode 100644 index 00000000..1ba80919 --- /dev/null +++ b/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/KotlinMetadataTinyRemapperExtension.kt @@ -0,0 +1,39 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.kotlin.remapping + +import net.fabricmc.tinyremapper.TinyRemapper +import net.fabricmc.tinyremapper.api.TrClass +import org.objectweb.asm.ClassVisitor + +object KotlinMetadataTinyRemapperExtension : TinyRemapper.ApplyVisitorProvider, TinyRemapper.Extension { + override fun insertApplyVisitor(cls: TrClass, next: ClassVisitor): ClassVisitor { + return KotlinMetadataRemappingClassVisitor(cls.environment.remapper, next) + } + + override fun attach(builder: TinyRemapper.Builder) { + builder.extraPreApplyVisitor(this) + } +} diff --git a/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/RemappingKmVisitors.kt b/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/RemappingKmVisitors.kt new file mode 100644 index 00000000..3acbd24a --- /dev/null +++ b/src/main/kotlin/net/fabricmc/loom/kotlin/remapping/RemappingKmVisitors.kt @@ -0,0 +1,359 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.kotlin.remapping + +import kotlinx.metadata.ClassName +import kotlinx.metadata.Flags +import kotlinx.metadata.KmAnnotation +import kotlinx.metadata.KmClassExtensionVisitor +import kotlinx.metadata.KmClassVisitor +import kotlinx.metadata.KmConstructorExtensionVisitor +import kotlinx.metadata.KmConstructorVisitor +import kotlinx.metadata.KmContractVisitor +import kotlinx.metadata.KmEffectExpressionVisitor +import kotlinx.metadata.KmEffectInvocationKind +import kotlinx.metadata.KmEffectType +import kotlinx.metadata.KmEffectVisitor +import kotlinx.metadata.KmExtensionType +import kotlinx.metadata.KmFunctionExtensionVisitor +import kotlinx.metadata.KmFunctionVisitor +import kotlinx.metadata.KmLambdaVisitor +import kotlinx.metadata.KmPropertyExtensionVisitor +import kotlinx.metadata.KmPropertyVisitor +import kotlinx.metadata.KmTypeAliasVisitor +import kotlinx.metadata.KmTypeExtensionVisitor +import kotlinx.metadata.KmTypeParameterExtensionVisitor +import kotlinx.metadata.KmTypeParameterVisitor +import kotlinx.metadata.KmTypeVisitor +import kotlinx.metadata.KmValueParameterVisitor +import kotlinx.metadata.KmVariance +import kotlinx.metadata.jvm.JvmClassExtensionVisitor +import kotlinx.metadata.jvm.JvmConstructorExtensionVisitor +import kotlinx.metadata.jvm.JvmFieldSignature +import kotlinx.metadata.jvm.JvmFunctionExtensionVisitor +import kotlinx.metadata.jvm.JvmMethodSignature +import kotlinx.metadata.jvm.JvmPropertyExtensionVisitor +import kotlinx.metadata.jvm.JvmTypeExtensionVisitor +import kotlinx.metadata.jvm.JvmTypeParameterExtensionVisitor +import org.objectweb.asm.commons.Remapper + +class RemappingKmVisitors(private val remapper: Remapper) { + private fun remapJvmMethodSignature(signature: JvmMethodSignature?): JvmMethodSignature? { + if (signature != null) { + return JvmMethodSignature(signature.name, remapper.mapMethodDesc(signature.desc)) + } + + return null + } + + private fun remapJvmFieldSignature(signature: JvmFieldSignature?): JvmFieldSignature? { + if (signature != null) { + return JvmFieldSignature(signature.name, remapper.mapDesc(signature.desc)) + } + + return null + } + + inner class RemappingKmClassVisitor(delegate: KmClassVisitor?) : KmClassVisitor(delegate) { + override fun visit(flags: Flags, name: ClassName) { + super.visit(flags, remapper.map(name)) + } + + override fun visitNestedClass(name: String) { + super.visitNestedClass(remapper.map(name)) + } + + override fun visitSealedSubclass(name: ClassName) { + super.visitSealedSubclass(remapper.map(name)) + } + + override fun visitSupertype(flags: Flags): KmTypeVisitor { + return RemappingKmTypeVisitor(super.visitSupertype(flags)) + } + + override fun visitFunction(flags: Flags, name: String): KmFunctionVisitor { + return RemappingKmFunctionVisitor(super.visitFunction(flags, name)) + } + + override fun visitInlineClassUnderlyingType(flags: Flags): KmTypeVisitor { + return RemappingKmTypeVisitor(super.visitInlineClassUnderlyingType(flags)) + } + + override fun visitProperty(flags: Flags, name: String, getterFlags: Flags, setterFlags: Flags): KmPropertyVisitor { + return RemappingKmPropertyVisitor(super.visitProperty(flags, name, getterFlags, setterFlags)) + } + + override fun visitTypeAlias(flags: Flags, name: String): KmTypeAliasVisitor { + return RemappingKmTypeAliasVisitor(super.visitTypeAlias(flags, name)) + } + + override fun visitConstructor(flags: Flags): KmConstructorVisitor { + return RemappingKmConstructorVisitor(super.visitConstructor(flags)) + } + + override fun visitExtensions(type: KmExtensionType): KmClassExtensionVisitor { + return RemappingJvmClassExtensionVisitor(super.visitExtensions(type) as JvmClassExtensionVisitor?) + } + + override fun visitTypeParameter( + flags: Flags, + name: String, + id: Int, + variance: KmVariance + ): KmTypeParameterVisitor { + return RemappingKmTypeParameterVisitor(super.visitTypeParameter(flags, name, id, variance)) + } + } + + inner class RemappingKmLambdaVisitor(delegate: KmLambdaVisitor?) : KmLambdaVisitor(delegate) { + override fun visitFunction(flags: Flags, name: String): KmFunctionVisitor { + return RemappingKmFunctionVisitor(super.visitFunction(flags, name)) + } + } + + inner class RemappingKmTypeVisitor(delegate: KmTypeVisitor?) : KmTypeVisitor(delegate) { + override fun visitClass(name: ClassName) { + super.visitClass(remapper.map(name)) + } + + override fun visitTypeAlias(name: ClassName) { + super.visitTypeAlias(remapper.map(name)) + } + + override fun visitAbbreviatedType(flags: Flags): KmTypeVisitor { + return RemappingKmTypeVisitor(super.visitAbbreviatedType(flags)) + } + + override fun visitArgument(flags: Flags, variance: KmVariance): KmTypeVisitor { + return RemappingKmTypeVisitor(super.visitArgument(flags, variance)) + } + + override fun visitOuterType(flags: Flags): KmTypeVisitor { + return RemappingKmTypeVisitor(super.visitOuterType(flags)) + } + + override fun visitFlexibleTypeUpperBound(flags: Flags, typeFlexibilityId: String?): KmTypeVisitor { + return RemappingKmTypeVisitor(super.visitFlexibleTypeUpperBound(flags, typeFlexibilityId)) + } + + override fun visitExtensions(type: KmExtensionType): KmTypeExtensionVisitor { + return RemappingJvmTypeExtensionVisitor(super.visitExtensions(type) as JvmTypeExtensionVisitor?) + } + } + + inner class RemappingJvmTypeExtensionVisitor(delegate: JvmTypeExtensionVisitor?) : JvmTypeExtensionVisitor(delegate) { + override fun visitAnnotation(annotation: KmAnnotation) { + super.visitAnnotation(KmAnnotation(remapper.map(annotation.className), annotation.arguments)) + } + } + + inner class RemappingKmFunctionVisitor(delegate: KmFunctionVisitor?) : KmFunctionVisitor(delegate) { + override fun visitReceiverParameterType(flags: Flags): KmTypeVisitor { + return RemappingKmTypeVisitor(super.visitReceiverParameterType(flags)) + } + + override fun visitReturnType(flags: Flags): KmTypeVisitor { + return RemappingKmTypeVisitor(super.visitReturnType(flags)) + } + + override fun visitValueParameter(flags: Flags, name: String): KmValueParameterVisitor { + return RemappingKmValueParameterVisitor(super.visitValueParameter(flags, name)) + } + + override fun visitExtensions(type: KmExtensionType): KmFunctionExtensionVisitor { + return RemappingJvmFunctionExtensionVisitor(super.visitExtensions(type) as JvmFunctionExtensionVisitor) + } + + override fun visitContract(): KmContractVisitor { + return RemappingKmContractVisitor(super.visitContract()) + } + } + + inner class RemappingKmContractVisitor(delegate: KmContractVisitor?) : KmContractVisitor(delegate) { + override fun visitEffect(type: KmEffectType, invocationKind: KmEffectInvocationKind?): KmEffectVisitor { + return RemappingKmEffectVisitor(super.visitEffect(type, invocationKind)) + } + } + + inner class RemappingKmEffectVisitor(delegate: KmEffectVisitor?) : KmEffectVisitor(delegate) { + override fun visitConclusionOfConditionalEffect(): KmEffectExpressionVisitor { + return RemappingKmEffectExpressionVisitor(super.visitConclusionOfConditionalEffect()) + } + + override fun visitConstructorArgument(): KmEffectExpressionVisitor { + return RemappingKmEffectExpressionVisitor(super.visitConclusionOfConditionalEffect()) + } + } + + inner class RemappingKmEffectExpressionVisitor(delegate: KmEffectExpressionVisitor?) : KmEffectExpressionVisitor(delegate) { + override fun visitAndArgument(): KmEffectExpressionVisitor { + return RemappingKmEffectExpressionVisitor(super.visitAndArgument()) + } + + override fun visitIsInstanceType(flags: Flags): KmTypeVisitor { + return RemappingKmTypeVisitor(super.visitIsInstanceType(flags)) + } + + override fun visitOrArgument(): KmEffectExpressionVisitor { + return RemappingKmEffectExpressionVisitor(super.visitOrArgument()) + } + } + + inner class RemappingKmPropertyVisitor(delegate: KmPropertyVisitor?) : KmPropertyVisitor(delegate) { + override fun visitReceiverParameterType(flags: Flags): KmTypeVisitor { + return RemappingKmTypeVisitor(super.visitReceiverParameterType(flags)) + } + + override fun visitReturnType(flags: Flags): KmTypeVisitor { + return RemappingKmTypeVisitor(super.visitReturnType(flags)) + } + + override fun visitSetterParameter(flags: Flags, name: String): KmValueParameterVisitor { + return RemappingKmValueParameterVisitor(super.visitSetterParameter(flags, name)) + } + + override fun visitExtensions(type: KmExtensionType): KmPropertyExtensionVisitor { + return RemappingJvmPropertyExtensionVisitor(super.visitExtensions(type) as JvmPropertyExtensionVisitor?) + } + + override fun visitTypeParameter( + flags: Flags, + name: String, + id: Int, + variance: KmVariance + ): KmTypeParameterVisitor { + return RemappingKmTypeParameterVisitor(super.visitTypeParameter(flags, name, id, variance)) + } + } + + inner class RemappingJvmPropertyExtensionVisitor(delegate: JvmPropertyExtensionVisitor?) : JvmPropertyExtensionVisitor(delegate) { + override fun visit( + jvmFlags: Flags, + fieldSignature: JvmFieldSignature?, + getterSignature: JvmMethodSignature?, + setterSignature: JvmMethodSignature? + ) { + super.visit(jvmFlags, remapJvmFieldSignature(fieldSignature), remapJvmMethodSignature(getterSignature), remapJvmMethodSignature(setterSignature)) + } + + override fun visitSyntheticMethodForAnnotations(signature: JvmMethodSignature?) { + super.visitSyntheticMethodForAnnotations(remapJvmMethodSignature(signature)) + } + + override fun visitSyntheticMethodForDelegate(signature: JvmMethodSignature?) { + super.visitSyntheticMethodForDelegate(remapJvmMethodSignature(signature)) + } + } + + inner class RemappingKmTypeParameterVisitor(delegate: KmTypeParameterVisitor?) : KmTypeParameterVisitor(delegate) { + override fun visitExtensions(type: KmExtensionType): KmTypeParameterExtensionVisitor { + return RemappingJvmTypeParameterExtensionVisitor(super.visitExtensions(type) as JvmTypeParameterExtensionVisitor?) + } + + override fun visitUpperBound(flags: Flags): KmTypeVisitor { + return RemappingKmTypeVisitor(super.visitUpperBound(flags)) + } + } + + inner class RemappingJvmTypeParameterExtensionVisitor(delegate: JvmTypeParameterExtensionVisitor?) : JvmTypeParameterExtensionVisitor(delegate) { + override fun visitAnnotation(annotation: KmAnnotation) { + super.visitAnnotation(KmAnnotation(remapper.map(annotation.className), annotation.arguments)) + } + } + + inner class RemappingKmValueParameterVisitor(delegate: KmValueParameterVisitor?) : KmValueParameterVisitor(delegate) { + override fun visitType(flags: Flags): KmTypeVisitor { + return RemappingKmTypeVisitor(super.visitType(flags)) + } + + override fun visitVarargElementType(flags: Flags): KmTypeVisitor { + return RemappingKmTypeVisitor(super.visitVarargElementType(flags)) + } + } + + inner class RemappingKmTypeAliasVisitor(delegate: KmTypeAliasVisitor?) : KmTypeAliasVisitor(delegate) { + override fun visitExpandedType(flags: Flags): KmTypeVisitor { + return RemappingKmTypeVisitor(super.visitExpandedType(flags)) + } + + override fun visitTypeParameter( + flags: Flags, + name: String, + id: Int, + variance: KmVariance + ): KmTypeParameterVisitor { + return RemappingKmTypeParameterVisitor(super.visitTypeParameter(flags, name, id, variance)) + } + + override fun visitUnderlyingType(flags: Flags): KmTypeVisitor { + return RemappingKmTypeVisitor(super.visitUnderlyingType(flags)) + } + + override fun visitAnnotation(annotation: KmAnnotation) { + super.visitAnnotation(KmAnnotation(remapper.map(annotation.className), annotation.arguments)) + } + } + + inner class RemappingJvmFunctionExtensionVisitor(delegate: JvmFunctionExtensionVisitor?) : JvmFunctionExtensionVisitor(delegate) { + override fun visit(signature: JvmMethodSignature?) { + super.visit(remapJvmMethodSignature(signature)) + } + + override fun visitLambdaClassOriginName(internalName: String) { + super.visitLambdaClassOriginName(remapper.map(internalName)) + } + } + + inner class RemappingJvmClassExtensionVisitor(delegate: JvmClassExtensionVisitor?) : JvmClassExtensionVisitor(delegate) { + override fun visitAnonymousObjectOriginName(internalName: String) { + super.visitAnonymousObjectOriginName(remapper.map(internalName)) + } + + override fun visitLocalDelegatedProperty( + flags: Flags, + name: String, + getterFlags: Flags, + setterFlags: Flags + ): KmPropertyVisitor { + return RemappingKmPropertyVisitor(super.visitLocalDelegatedProperty(flags, name, getterFlags, setterFlags)) + } + } + + inner class RemappingKmConstructorVisitor(delegate: KmConstructorVisitor?) : KmConstructorVisitor(delegate) { + override fun visitExtensions(type: KmExtensionType): KmConstructorExtensionVisitor { + return RemappingJvmConstructorExtensionVisitor(super.visitExtensions(type) as JvmConstructorExtensionVisitor?) + } + + override fun visitValueParameter(flags: Flags, name: String): KmValueParameterVisitor { + return RemappingKmValueParameterVisitor(super.visitValueParameter(flags, name)) + } + } + + inner class RemappingJvmConstructorExtensionVisitor(delegate: JvmConstructorExtensionVisitor?) : JvmConstructorExtensionVisitor(delegate) { + override fun visit(signature: JvmMethodSignature?) { + super.visit(remapJvmMethodSignature(signature)) + } + } +} diff --git a/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy b/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy index bae7f567..3b04a3e4 100644 --- a/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy @@ -28,7 +28,7 @@ import org.gradle.util.GradleVersion class LoomTestConstants { public final static String DEFAULT_GRADLE = GradleVersion.current().getVersion() - public final static String PRE_RELEASE_GRADLE = "7.5-20220110230252+0000" + public final static String PRE_RELEASE_GRADLE = "7.5-20220116010338+0000" public final static String[] STANDARD_TEST_VERSIONS = [DEFAULT_GRADLE, PRE_RELEASE_GRADLE] } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/KotlinTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/KotlinTest.groovy index 91cf813d..afe06b03 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/KotlinTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/KotlinTest.groovy @@ -25,6 +25,7 @@ package net.fabricmc.loom.test.integration import net.fabricmc.loom.test.util.GradleProjectTestTrait +import net.fabricmc.loom.test.util.ServerRunner import spock.lang.Specification import spock.lang.Unroll @@ -36,12 +37,17 @@ class KotlinTest extends Specification implements GradleProjectTestTrait { def "kotlin build (gradle #version)"() { setup: def gradle = gradleProject(project: "kotlin", version: version) + def server = ServerRunner.create(gradle.projectDir, "1.16.5") + .withMod(gradle.getOutputFile("fabric-example-mod-0.0.1.jar")) + .downloadMod(ServerRunner.FABRIC_LANG_KOTLIN, "fabric-language-kotlin-1.7.1+kotlin.1.6.10.jar") when: def result = gradle.run(task: "build") + def serverResult = server.run() then: result.task(":build").outcome == SUCCESS + serverResult.successful() where: version << STANDARD_TEST_VERSIONS diff --git a/src/test/groovy/net/fabricmc/loom/test/util/ServerRunner.groovy b/src/test/groovy/net/fabricmc/loom/test/util/ServerRunner.groovy index 82b519d9..0a95fd7d 100644 --- a/src/test/groovy/net/fabricmc/loom/test/util/ServerRunner.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/util/ServerRunner.groovy @@ -34,6 +34,7 @@ class ServerRunner { "1.16.5": "https://github.com/FabricMC/fabric/releases/download/0.37.1%2B1.16/fabric-api-0.37.1+1.16.jar", "1.17.1": "https://github.com/FabricMC/fabric/releases/download/0.37.1%2B1.17/fabric-api-0.37.1+1.17.jar" ] + static final String FABRIC_LANG_KOTLIN = "https://maven.fabricmc.net/net/fabricmc/fabric-language-kotlin/1.7.1%2Bkotlin.1.6.10/fabric-language-kotlin-1.7.1%2Bkotlin.1.6.10.jar" final File serverDir final String minecraftVersion diff --git a/src/test/kotlin/net/fabricmc/loom/test/kotlin/KotlinClassMetadataRemappingAnnotationVisitorTest.kt b/src/test/kotlin/net/fabricmc/loom/test/kotlin/KotlinClassMetadataRemappingAnnotationVisitorTest.kt new file mode 100644 index 00000000..369beaaa --- /dev/null +++ b/src/test/kotlin/net/fabricmc/loom/test/kotlin/KotlinClassMetadataRemappingAnnotationVisitorTest.kt @@ -0,0 +1,82 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.test.kotlin + +import java.io.File +import java.io.PrintWriter +import java.io.StringWriter +import java.nio.file.Paths +import net.fabricmc.loom.kotlin.remapping.KotlinMetadataRemappingClassVisitor +import net.fabricmc.loom.util.TinyRemapperHelper +import net.fabricmc.mappingio.MappingReader +import net.fabricmc.mappingio.tree.MemoryMappingTree +import net.fabricmc.tinyremapper.IMappingProvider +import net.fabricmc.tinyremapper.TinyRemapper +import org.junit.jupiter.api.Test +import org.objectweb.asm.ClassReader +import org.objectweb.asm.Opcodes +import org.objectweb.asm.util.Textifier +import org.objectweb.asm.util.TraceClassVisitor + +// See: https://github.com/JetBrains/kotlin/blob/master/libraries/kotlinx-metadata/jvm/test/kotlinx/metadata/test/MetadataSmokeTest.kt#L67 +class KotlinClassMetadataRemappingAnnotationVisitorTest { + /* + val test = KmClass() + klass.accept(RemappingKmClassVisitor(remapper, test)) + println(GsonBuilder().setPrettyPrinting().create().toJson(test)) + */ + + @Test + fun simpleTest() { + val inputPosInChunk = getClassBytes("PosInChunk") + + val classReader = ClassReader(inputPosInChunk) + + val tinyRemapper = TinyRemapper.newRemapper() + .withMappings(readMappings("PosInChunk")) + .build() + + val stringWriter = StringWriter() + val traceClassVisitor = TraceClassVisitor(null, TextifierImpl(), PrintWriter(stringWriter)) + + classReader.accept(KotlinMetadataRemappingClassVisitor(tinyRemapper.environment.remapper, traceClassVisitor), 0) + + val d2Regex = Regex("(d2=)(.*)") + val asm = stringWriter.toString() + println(d2Regex.find(asm)?.value) + } + + private fun getClassBytes(name: String): ByteArray { + return File("src/test/resources/classes/$name.class").readBytes() + } + + private fun readMappings(name: String): IMappingProvider { + val mappingTree = MemoryMappingTree() + MappingReader.read(Paths.get("src/test/resources/mappings/$name.mappings"), mappingTree) + return TinyRemapperHelper.create(mappingTree, "named", "intermediary", false) + } + + private class TextifierImpl : Textifier(Opcodes.ASM9) +} diff --git a/src/test/resources/classes/PosInChunk.class b/src/test/resources/classes/PosInChunk.class new file mode 100644 index 0000000000000000000000000000000000000000..8d00faefb26a29618a1dd1c9ddfb8884d0634633 GIT binary patch literal 3410 zcmb7G-*Xe!75?r@me$hRUQ4zy{*jPiiYx;%E^X4kj@|pT|sU0zic3bY&SF*)b1MS4YfXM z_YCxhT23M9R~=7ba_N9eo2AQ|?OzY_mP2JZ6gL!(6@oZ*$F(a>>yBS)`A)4=xBO~p zq1LEuu*6we^Ul`JcKs#;I2A9AR}=;-RlBkgMYwD=t-4J^5?@$)k|~ z1Y{m0tX`3(qPdRK`_$DAn#L~`y1A6AzSZ=v8~Byfa|XYT&E7EZW;pvBDSzJr+i|(0 znM$L+*>G*w=R$UsnYhB_$x37MuEMFH#eYU;8sayxYTvP1HD6&pB=7(SZkA`M9BYX1 zz|9`~R2X<0@5ola%eM6!VM8el6eeR)qc&4GwA+l~+(OH#t=i2r&Sy}TL)^1_85Fk9 z>i8WGytTP$yQ>N(h+ZBX^ngqX69_ z;+Y*QEbi}oU7VKy?2Zra0cvs!R?F{}1i&Zvl zFW9`4{b|dp@oE~3m)qVL-Zc~Z-66a{td*M0mg{QxRk;FkF@~ZjT-8!^sa&*uYt`~C zI%xH+1TSnQLrMnl&fMVqwjslBrKtZ?w1pygCUC5g`n&xM8b6_MntftlZAHGOj=rsp9o0v(N zLzIV@Xqb{ozfi=7iAMxq=?|lGX*vrVN$MFh%gG?o?xYb+wu^`?d(7;!Z&IT|CC@$y z&V`5cq`hs`H*5CEdV`G~I|QdTc%GACEA=h$dAZc8uh`9xWRqoY8x^Z|&1yQLN0Sp* zTCVTZ?XtV&cn;?~@4-8}uZ_a!i)PP#?cp6**|=zJMnN-I8?9!=KIcf%_2C0Ek^2qmrAd?m#$(7)9&u1#iRH78CuHrw@^N`@>&_-wwh3=P^ zZ4YPjV9tCD_i)Z1#90U$;rfK24~eCu@u}SScQ{&{+QD(N?>@yO|ECBaW AkN^Mx literal 0 HcmV?d00001 diff --git a/src/test/resources/mappings/PosInChunk.mappings b/src/test/resources/mappings/PosInChunk.mappings new file mode 100644 index 00000000..c6f1d3c4 --- /dev/null +++ b/src/test/resources/mappings/PosInChunk.mappings @@ -0,0 +1,220 @@ +tiny 2 0 intermediary named +c net/minecraft/class_2338 net/minecraft/util/math/BlockPos + c Represents the position of a block in a three-dimensional volume.\n\n

The position is integer-valued.\n\n

A block position may be mutable; hence, when using block positions\nobtained from other places as map keys, etc., you should call {@link\n#toImmutable()} to obtain an immutable block position. + f J field_10976 BITS_X + f Lcom/mojang/serialization/Codec; field_25064 CODEC + f Lorg/apache/logging/log4j/Logger; field_18789 LOGGER + f Lnet/minecraft/class_2338; field_10980 ORIGIN + c The block position which x, y, and z values are all zero. + f I field_10983 BIT_SHIFT_Z + f I field_10975 SIZE_BITS_Y + f J field_10974 BITS_Y + f I field_10978 SIZE_BITS_X + f I field_10981 BIT_SHIFT_X + f J field_10973 BITS_Z + f I field_10977 SIZE_BITS_Z + m ([I)Lnet/minecraft/class_2338; method_29095 method_29095 + p 0 values + m (Ljava/util/Random;IIIIIII)Ljava/lang/Iterable; method_27156 iterateRandomly + c Iterates through {@code count} random block positions in the given area.\n\n

The iterator yields positions in no specific order. The same position\nmay be returned multiple times by the iterator. + p 6 maxY + c the maximum y value for returned positions + p 7 maxZ + c the maximum z value for returned positions + p 4 minZ + c the minimum z value for returned positions + p 5 maxX + c the maximum x value for returned positions + p 2 minX + c the minimum x value for returned positions + p 3 minY + c the minimum y value for returned positions + p 0 random + c the {@link Random} object used to compute new positions + p 1 count + c the number of positions to iterate + m (JIII)J method_10096 add + p 0 value + p 3 y + p 2 x + p 4 z + m ()Lnet/minecraft/class_2338$class_2339; method_25503 mutableCopy + c Returns a mutable copy of this block position.\n\n

If this block position is a mutable one, mutation to this block\nposition won't affect the returned position. + m (I)Lnet/minecraft/class_2338; method_10088 west + p 1 distance + m (I)Lnet/minecraft/class_2338; method_10076 north + p 1 distance + m ()J method_10063 asLong + m ()Lnet/minecraft/class_2338; method_10084 up + m (Lnet/minecraft/class_2382;)V + p 1 pos + m (Lnet/minecraft/class_2338;III)Ljava/util/stream/Stream; method_25998 streamOutwards + p 0 center + p 3 maxZ + p 1 maxX + p 2 maxY + m (Lnet/minecraft/class_2374;)V + p 1 pos + m (Lnet/minecraft/class_2382;)Lnet/minecraft/class_2338; method_10081 add + m (Lnet/minecraft/class_238;)Ljava/util/stream/Stream; method_29715 stream + p 0 box + m ()Lnet/minecraft/class_2338; method_10072 south + m (III)Lnet/minecraft/class_2338; method_10069 add + m (I)Lnet/minecraft/class_2338; method_35830 multiply + m (Ljava/util/Random;ILnet/minecraft/class_2338;I)Ljava/lang/Iterable; method_34848 iterateRandomly + c Iterates through {@code count} random block positions in a given range around the given position.\n\n

The iterator yields positions in no specific order. The same position\nmay be returned multiple times by the iterator. + p 0 random + c the {@link Random} object used to compute new positions + p 1 count + c the number of positions to iterate + p 2 around + c the {@link BlockPos} to iterate around + p 3 range + c the maximum distance from the given pos in any axis + m (I)Lnet/minecraft/class_2338; method_10077 south + p 1 distance + m (I)Lnet/minecraft/class_2338; method_10089 east + p 1 distance + m (J)J method_10091 removeChunkSectionLocalY + p 0 y + m (J)I method_10071 unpackLongY + p 0 packedPos + m (Lnet/minecraft/class_2338;ILnet/minecraft/class_2350;Lnet/minecraft/class_2350;)Ljava/lang/Iterable; method_30512 iterateInSquare + c Iterates block positions around the {@code center} in a square of\n({@code 2 * radius + 1}) by ({@code 2 * radius + 1}). The blocks\nare iterated in a (square) spiral around the center.\n\n

The first block returned is the center, then the iterator moves\na block towards the first direction, followed by moving along\nthe second direction.\n\n@throws IllegalStateException when the 2 directions lie on the same axis + p 2 firstDirection + c the direction the iterator moves first + p 3 secondDirection + c the direction the iterator moves after the first + p 0 center + c the center of iteration + p 1 radius + c the maximum chebychev distance + m (J)I method_10083 unpackLongZ + p 0 packedPos + m (J)Lnet/minecraft/class_2338; method_10092 fromLong + p 0 packedPos + m (I)Lnet/minecraft/class_2338; method_33096 withY + p 1 y + m (Ljava/util/stream/IntStream;)Lcom/mojang/serialization/DataResult; method_29094 method_29094 + p 0 stream + m (Lnet/minecraft/class_2338;Lnet/minecraft/class_2338;)Ljava/util/stream/Stream; method_20437 stream + p 1 end + p 0 start + m (Lnet/minecraft/class_2350;)Lnet/minecraft/class_2338; method_10093 offset + m (I)Lnet/minecraft/class_2338; method_10086 up + p 1 distance + m (III)J method_10064 asLong + p 0 x + p 1 y + p 2 z + m ()Lnet/minecraft/class_2338; method_10074 down + m (IIIIII)Ljava/lang/Iterable; method_10094 iterate + p 1 startY + p 0 startX + p 3 endX + p 2 startZ + p 5 endZ + p 4 endY + m ()Lnet/minecraft/class_2338; method_10062 toImmutable + c Returns an immutable block position with the same x, y, and z as this\nposition.\n\n

This method should be called when a block position is used as map\nkeys as to prevent side effects of mutations of mutable block positions. + m ()Lnet/minecraft/class_2338; method_10078 east + m (Lnet/minecraft/class_3341;)Ljava/util/stream/Stream; method_23627 stream + p 0 box + m (Lnet/minecraft/class_2350;I)Lnet/minecraft/class_2338; method_10079 offset + m (Lnet/minecraft/class_2338;III)Ljava/lang/Iterable; method_25996 iterateOutwards + c Iterates block positions around the {@code center}. The iteration order\nis mainly based on the manhattan distance of the position from the\ncenter.\n\n

For the same manhattan distance, the positions are iterated by y\noffset, from negative to positive. For the same y offset, the positions\nare iterated by x offset, from negative to positive. For the two\npositions with the same x and y offsets and the same manhattan distance,\nthe one with a positive z offset is visited first before the one with a\nnegative z offset. + p 2 rangeY + c the maximum y difference from the center + p 3 rangeZ + c the maximum z difference from the center + p 0 center + c the center of iteration + p 1 rangeX + c the maximum x difference from the center + m (Lnet/minecraft/class_2338;)Ljava/util/stream/IntStream; method_29093 method_29093 + p 0 pos + m (Lnet/minecraft/class_2382;)Lnet/minecraft/class_2338; method_10059 subtract + m (Lnet/minecraft/class_2382;)Lnet/minecraft/class_2338; method_10075 crossProduct + p 1 pos + m (IIIIII)Ljava/util/stream/Stream; method_17962 stream + p 1 startY + p 0 startX + p 3 endX + p 2 startZ + p 5 endZ + p 4 endY + m (Lnet/minecraft/class_2350$class_2351;I)Lnet/minecraft/class_2338; method_30513 offset + m (Lnet/minecraft/class_2338;IILjava/util/function/Predicate;)Ljava/util/Optional; method_25997 findClosest + p 3 condition + p 1 horizontalRange + p 2 verticalRange + p 0 pos + m (I)Lnet/minecraft/class_2338; method_10087 down + m (DDD)Lnet/minecraft/class_2338; method_10080 add + m (Lnet/minecraft/class_2338;Lnet/minecraft/class_2338;)Ljava/lang/Iterable; method_10097 iterate + p 1 end + p 0 start + m ()Lnet/minecraft/class_2338; method_10095 north + m ()Lnet/minecraft/class_2338; method_10067 west + m (J)I method_10061 unpackLongX + p 0 packedPos + m (Lnet/minecraft/class_243;)V + p 1 pos + m (JLnet/minecraft/class_2350;)J method_10060 offset + p 2 direction + p 0 value + m (Lnet/minecraft/class_2470;)Lnet/minecraft/class_2338; method_10070 rotate + p 1 rotationrotate +c net/minecraft/class_1923 net/minecraft/util/math/ChunkPos + f I field_9180 z + f I field_9181 x + f Lnet/minecraft/class_1923; field_35107 ORIGIN + f J field_17348 MARKER + m ()Lnet/minecraft/class_2338; method_8323 getStartPos + m (Lnet/minecraft/class_1923;)I method_24022 getChebyshevDistance + p 1 pos + m ()J method_8324 toLong + m (II)J method_8331 toLong + p 1 chunkZ + p 0 chunkX + m ()I method_17887 getRegionRelativeX + m ()I method_17885 getRegionX + m (J)V + p 1 pos + m ()I method_33940 getCenterX + m (J)I method_8325 getPackedX + p 0 pos + m (Lnet/minecraft/class_2338;)V + p 1 pos + m ()I method_33942 getCenterZ + m ()I method_8326 getStartX + m ()I method_8328 getStartZ + m (III)Lnet/minecraft/class_2338; method_35231 getBlockPos + p 2 y + p 1 offsetX + p 3 offsetZ + m (I)I method_33939 getOffsetX + p 1 offset + m (Lnet/minecraft/class_1923;Lnet/minecraft/class_1923;)Ljava/util/stream/Stream; method_19281 stream + p 0 pos1 + p 1 pos2 + m (Lnet/minecraft/class_2338;)J method_37232 toLong + p 0 pos + m (I)I method_33941 getOffsetZ + p 1 offset + m ()I method_17888 getRegionRelativeZ + m (I)Lnet/minecraft/class_2338; method_33943 getCenterAtY + p 1 y + m ()I method_17886 getRegionZ + m ()I method_8327 getEndX + m (II)V + p 2 z + p 1 x + m ()I method_8329 getEndZ + m (J)I method_8332 getPackedZ + p 0 pos + m (Lnet/minecraft/class_1923;I)Ljava/util/stream/Stream; method_19280 stream + p 0 center + p 1 radius + m (Ljava/lang/Object;)Z equals equals + p 1 o \ No newline at end of file diff --git a/src/test/resources/projects/kotlin/build.gradle.kts b/src/test/resources/projects/kotlin/build.gradle.kts index 50c0c6b6..6e9bcc2b 100644 --- a/src/test/resources/projects/kotlin/build.gradle.kts +++ b/src/test/resources/projects/kotlin/build.gradle.kts @@ -1,7 +1,8 @@ import java.util.Properties plugins { - kotlin("jvm") version "1.5.21" + kotlin("jvm") version "1.6.10" + kotlin("plugin.serialization") version "1.6.10" id("fabric-loom") } @@ -15,6 +16,6 @@ version = "0.0.1" dependencies { minecraft(group = "com.mojang", name = "minecraft", version = "1.16.5") mappings(group = "net.fabricmc", name = "yarn", version = "1.16.5+build.5", classifier = "v2") - modImplementation("net.fabricmc:fabric-loader:0.11.2") - modImplementation(group = "net.fabricmc", name = "fabric-language-kotlin", version = "1.6.3+kotlin.1.5.21") + modImplementation("net.fabricmc:fabric-loader:0.12.12") + modImplementation(group = "net.fabricmc", name = "fabric-language-kotlin", version = "1.7.1+kotlin.1.6.10") } \ No newline at end of file diff --git a/src/test/resources/projects/kotlin/src/main/kotlin/net/fabricmc/language/kotlin/ExampleSerializable.kt b/src/test/resources/projects/kotlin/src/main/kotlin/net/fabricmc/language/kotlin/ExampleSerializable.kt new file mode 100644 index 00000000..deff52c1 --- /dev/null +++ b/src/test/resources/projects/kotlin/src/main/kotlin/net/fabricmc/language/kotlin/ExampleSerializable.kt @@ -0,0 +1,9 @@ +package net.fabricmc.language.kotlin + +import kotlinx.serialization.Serializable + +import net.minecraft.util.Identifier + +@Serializable +data class ExampleSerializable(@Serializable(with = IdentifierSerializer::class) val identifier: Identifier, val test: Double) { +} \ No newline at end of file diff --git a/src/test/resources/projects/kotlin/src/main/kotlin/net/fabricmc/language/kotlin/IdentifierSerializer.kt b/src/test/resources/projects/kotlin/src/main/kotlin/net/fabricmc/language/kotlin/IdentifierSerializer.kt new file mode 100644 index 00000000..07791a5e --- /dev/null +++ b/src/test/resources/projects/kotlin/src/main/kotlin/net/fabricmc/language/kotlin/IdentifierSerializer.kt @@ -0,0 +1,24 @@ +package net.fabricmc.language.kotlin + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor + +import net.minecraft.util.Identifier + +// Based on https://kotlin.github.io/kotlinx.serialization/kotlinx-serialization-core/kotlinx-serialization-core/kotlinx.serialization.descriptors/-primitive-serial-descriptor.html +class IdentifierSerializer : KSerializer { + override val descriptor = + PrimitiveSerialDescriptor("net.fabricmc.language.kotlin.IdentifierSerializer", PrimitiveKind.STRING) + + override fun deserialize(decoder: Decoder): Identifier { + val split = decoder.decodeString().split(':') + return Identifier(split[0], split[1]) + } + + override fun serialize(encoder: Encoder, value: Identifier) { + encoder.encodeString("${value.namespace}:${value.path}") + } +} \ No newline at end of file diff --git a/src/test/resources/projects/kotlin/src/main/kotlin/net/fabricmc/language/kotlin/PosInChunk.kt b/src/test/resources/projects/kotlin/src/main/kotlin/net/fabricmc/language/kotlin/PosInChunk.kt new file mode 100644 index 00000000..78cb8f08 --- /dev/null +++ b/src/test/resources/projects/kotlin/src/main/kotlin/net/fabricmc/language/kotlin/PosInChunk.kt @@ -0,0 +1,10 @@ +package net.fabricmc.language.kotlin + +import net.minecraft.util.math.BlockPos +import net.minecraft.util.math.ChunkPos + +data class PosInChunk(val x: Int, val y: Int, val z: Int) { + constructor(blockPos: BlockPos) : this(blockPos.x and 15, blockPos.y, blockPos.z and 15) + + fun getBlockPos(chunkPos: ChunkPos) = BlockPos(chunkPos.startX + x, y, chunkPos.startZ + z) +} \ No newline at end of file diff --git a/src/test/resources/projects/kotlin/src/main/kotlin/net/fabricmc/language/kotlin/TestModClass.kt b/src/test/resources/projects/kotlin/src/main/kotlin/net/fabricmc/language/kotlin/TestModClass.kt index 5213187f..456ce7a9 100644 --- a/src/test/resources/projects/kotlin/src/main/kotlin/net/fabricmc/language/kotlin/TestModClass.kt +++ b/src/test/resources/projects/kotlin/src/main/kotlin/net/fabricmc/language/kotlin/TestModClass.kt @@ -2,14 +2,22 @@ package net.fabricmc.language.kotlin import net.fabricmc.api.ModInitializer import org.apache.logging.log4j.LogManager +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +import net.minecraft.util.Identifier class TestModClass : ModInitializer { - val logger = LogManager.getFormatterLogger("KotlinLanguageTest") override fun onInitialize() { + val json = Json.encodeToString(ExampleSerializable(Identifier("kotlin:hello"), 12.0)) + val obj = Json.decodeFromString(json) + logger.info("**************************") logger.info("Hello from Kotlin TestModClass") + logger.info(json) + logger.info(obj) logger.info("**************************") } } \ No newline at end of file diff --git a/src/test/resources/projects/kotlin/src/main/resources/fabric.mod.json b/src/test/resources/projects/kotlin/src/main/resources/fabric.mod.json index 5618f96e..95d752c0 100644 --- a/src/test/resources/projects/kotlin/src/main/resources/fabric.mod.json +++ b/src/test/resources/projects/kotlin/src/main/resources/fabric.mod.json @@ -2,32 +2,24 @@ "schemaVersion": 1, "id": "modid", "version": "${version}", - "name": "Example Mod", "description": "This is an example description! Tell everyone what your mod is about!", - "authors": [ - "Me!" - ], "contact": { "homepage": "https://fabricmc.net/", "sources": "https://github.com/FabricMC/fabric-example-mod" }, - "license": "CC0-1.0", - "environment": "*", "entrypoints": { "main": [ - "net.fabricmc.language.kotlin.TestModClass" + { + "adapter": "kotlin", + "value": "net.fabricmc.language.kotlin.TestModClass" + } ] }, - "depends": { "fabricloader": ">=0.7.4", - "fabric": "*", "minecraft": "1.16.x" - }, - "suggests": { - "another-mod": "*" } }