Skip to content

Commit

Permalink
(tmp) PreLaunch: Replace most uses of Asm's Type with custom Descript…
Browse files Browse the repository at this point in the history
…or class
  • Loading branch information
mattco98 committed Jun 17, 2023
1 parent 9729087 commit 9003e59
Show file tree
Hide file tree
Showing 19 changed files with 650 additions and 343 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ dependencies {
include(modImplementation("net.fabricmc.fabric-api:fabric-api:${property("fabric_version")}")!!)

include(modImplementation("net.fabricmc:fabric-language-kotlin:1.9.4+kotlin.1.8.21")!!)
include(modImplementation("com.github.ChatTriggers:rhino:8f320341a8")!!)
include(modImplementation("com.github.ChatTriggers:rhino:273b924b64")!!)
include(modImplementation("com.fasterxml.jackson.core:jackson-core:2.13.2")!!)
include(modImplementation("com.fifesoft:rsyntaxtextarea:3.2.0")!!)
include(modImplementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")!!)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package com.chattriggers.ctjs.engine.langs.js

import com.chattriggers.ctjs.CTJS
import com.chattriggers.ctjs.launch.CTJavaObjectMappingProvider
import com.chattriggers.ctjs.launch.Mappings
import net.fabricmc.loader.api.FabricLoader
import org.mozilla.javascript.Context
import org.mozilla.javascript.Context.EMIT_DEBUG_OUTPUT
import org.mozilla.javascript.Context.FEATURE_LOCATION_INFORMATION_IN_ERROR
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ object JSLoader : ILoader {
}

@JvmStatic
fun invokeMixin(func: Callable, args: Array<Any?>): Any {
fun invokeMixin(func: Callable, args: Array<Any?>): Any? {
return wrapInContext {
func.call(Context.getCurrentContext(), scope, scope, args)
}
Expand Down
360 changes: 360 additions & 0 deletions src/main/kotlin/com/chattriggers/ctjs/launch/Descriptor.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,360 @@
package com.chattriggers.ctjs.launch

import com.chattriggers.ctjs.launch.generation.Utils
import org.objectweb.asm.Type

sealed interface Descriptor {
val isType get() = true

fun originalDescriptor(): String
fun mappedDescriptor(): String
fun toMappedType(): Type

enum class Primitive(private val type: Type) : Descriptor {
VOID(Type.VOID_TYPE),
BOOLEAN(Type.BOOLEAN_TYPE),
CHAR(Type.CHAR_TYPE),
BYTE(Type.BYTE_TYPE),
SHORT(Type.SHORT_TYPE),
INT(Type.INT_TYPE),
FLOAT(Type.FLOAT_TYPE),
LONG(Type.LONG_TYPE),
DOUBLE(Type.DOUBLE_TYPE);

override fun originalDescriptor(): String = type.descriptor
override fun mappedDescriptor() = originalDescriptor()
override fun toMappedType() = type

override fun toString() = originalDescriptor()

companion object {
val IDENTIFIERS = values().map { it.toString() }
}
}

class Object(private val descriptor: String): Descriptor {
init {
require(descriptor !in Primitive.IDENTIFIERS) {
"Cannot pass a primitive type to Descriptor.Object"
}
require(!descriptor.startsWith('[')) {
"Cannot pass an array type to Descriptor.Object"
}
require('(' !in descriptor && ')' !in descriptor) {
"Cannot pass a method descriptor to Descriptor.Object"
}
require(':' !in descriptor) {
"Cannot pass a field descriptor to Descriptor.Object"
}
}

override fun originalDescriptor() = descriptor

override fun mappedDescriptor(): String {
// Do not map this class if it is not in a mapped package
for (mappedPackage in Mappings.mappedPackages) {
if (descriptor.startsWith(mappedPackage)) {
return Mappings.getMappedClassName(descriptor)?.let {
"L$it;"
} ?: error("Unknown class \"$descriptor\"")
}
}

return descriptor
}

override fun toMappedType(): Type = Type.getType(mappedDescriptor())

override fun toString() = originalDescriptor()
}

data class Array(val base: Descriptor, val dimensions: Int) : Descriptor {
init {
require(base.isType) {
"Cannot pass a non-type object base to Descriptor.Array"
}
require(dimensions > 0) {
"Cannot pass a dimensions count less than 1 to Descritor.Array"
}
}

override fun originalDescriptor() = "[".repeat(dimensions) + base.originalDescriptor()

override fun mappedDescriptor() = "[".repeat(dimensions) + base.mappedDescriptor()

override fun toMappedType(): Type = Type.getType(mappedDescriptor())

override fun toString() = originalDescriptor()
}

data class Field(val owner: Object?, val name: String, val type: Descriptor?) : Descriptor {
override val isType get() = false
private val mappedName by lazy {
Mappings.getMappedClass(owner!!.originalDescriptor())?.fields?.get(name)?.name?.value
?: error("Unknown field $name in class $owner")
}

init {
require(type?.isType != false) {
"Cannot use non-type descriptor $type as the field type"
}
}

override fun originalDescriptor() = buildString {
if (owner != null)
append(owner.originalDescriptor())
append(name)
append(':')
if (type != null)
append(type.originalDescriptor())
}

override fun mappedDescriptor() = buildString {
require(owner != null) {
"Cannot build mapped field descriptor from incomplete field descriptor"
}
append(owner.mappedDescriptor())
append(mappedName)
append(':')
if (type != null)
append(type.mappedDescriptor())
}

override fun toMappedType() = error("Cannot convert Field descriptor to Type")

override fun toString() = originalDescriptor()
}

data class Method(
val owner: Object?,
val name: String,
val parameters: List<Descriptor>?,
val returnType: Descriptor?,
) : Descriptor {
override val isType get() = false
private val mappedName by lazy {
Utils.findMethod(Mappings.getMappedClass(owner!!.originalDescriptor())!!, this).first.name.value
}

init {
parameters?.forEach {
require(it.isType) {
"Cannot use non-type descriptor $it as a method parameter"
}
}
require(returnType?.isType != false) {
"Cannot use non-type descriptor $returnType as the method return type"
}

require((parameters == null) == (returnType == null)) {
"Parameters and return type must both be specified or omitted in a method descriptor"
}
}

override fun originalDescriptor() = buildString {
if (owner != null)
append(owner.originalDescriptor())
append(name)
if (parameters != null) {
append('(')
parameters.forEach { append(it.originalDescriptor()) }
append(')')
append(returnType!!.originalDescriptor())
}
}

override fun mappedDescriptor() = buildString {
require(owner != null) {
"Cannot build mapped method descriptor from incomplete method descriptor"
}
append(owner.mappedDescriptor())
append(mappedName)
if (parameters != null) {
append('(')
parameters.forEach { append(it.mappedDescriptor()) }
append(')')
append(returnType!!.mappedDescriptor())
}
}

override fun toMappedType(): Type = Type.getMethodType(mappedDescriptor())

override fun toString() = originalDescriptor()
}

data class New(val type: Descriptor, val parameters: List<Descriptor>?) : Descriptor {
override val isType get() = false

init {
// TODO: What would `new int[]{...}` look like here?
require(type is Object) {
"New descriptor cannot use non-object descriptor as its return type"
}
parameters?.forEach {
require(it.isType) {
"Cannot use non-type descriptor $it as a new descriptor parameter"
}
}
}

override fun originalDescriptor() = buildString {
if (parameters != null) {
append('(')
parameters.forEach { append(it.originalDescriptor()) }
append(')')
}
append(type.originalDescriptor())
}

override fun mappedDescriptor() = buildString {
if (parameters != null) {
append('(')
parameters.forEach { append(it.mappedDescriptor()) }
append(')')
}
append(type.mappedDescriptor())
}

override fun toMappedType() = error("Cannot convert New descriptor to Type")

override fun toString() = originalDescriptor()
}

class Parser(private val text: String) {
private var cursor = 0
private val ch get() = text[cursor]
private val done get() = cursor > text.lastIndex

init {
require(text.isNotEmpty()) { "Invalid descriptor \"$text\"" }
}

private fun parseJavaIdentifier(): String? {
if (done || !ch.isJavaIdentifierStart())
return null

val start = cursor
cursor++
while (!done && ch.isJavaIdentifierPart())
cursor++
return text.substring(start, cursor)
}

fun parseType(full: Boolean = false): Descriptor {
if (full)
check(cursor == 0)

val descriptor = if (ch == 'L') {
val semicolonIndex = text.indexOf(';', cursor)
if (semicolonIndex == -1)
error("Invalid descriptor \"$text\": expected ';' after position $cursor")
val objectType = text.substring(cursor, semicolonIndex + 1)
cursor += objectType.length
Object(objectType)
} else if (ch in "VZCBSIFJD") {
val primitive = Primitive.values().first { it.toString() == ch.toString() }
cursor++
primitive
} else if (ch == '[') {
cursor++
val base = parseType()
if (base is Array) {
Array(base.base, base.dimensions + 1)
} else Array(base, 1)
} else {
throw IllegalArgumentException("Invalid descriptor \"$text\": unexpected character at position $cursor")
}

if (full)
require(done) { "Invalid descriptor: \"$text\""}

return descriptor
}

fun parseField(full: Boolean): Field {
check(cursor == 0)

val owner = try {
val type = parseType()
require(type is Object) {
"Cannot create field descriptor with a non-object owner"
}
type
} catch (e: IllegalArgumentException) {
if (full)
throw e
null
}

val name = parseJavaIdentifier()
requireNotNull(name) { "Invalid field descriptor: \"$text\"" }

val type = if (!done && ch == ':') {
cursor++
parseType()
} else null

if (full)
requireNotNull(type) { "Invalid field descriptor: \"$text\"" }

require(done) { "Invalid field descriptor: \"$text\"" }
return Field(owner, name, type)
}

fun parseMethod(full: Boolean): Method {
check(cursor == 0)

val owner = try {
val type = parseType()
require(type is Object) {
"Cannot create method descriptor with a non-object owner"
}
type
} catch (e: IllegalArgumentException) {
if (full)
throw e
null
}

val name = parseJavaIdentifier()
requireNotNull(name) { "Invalid method descriptor: \"$text\"" }

val parameters = parseParameters()
if (parameters == null && full)
throw IllegalArgumentException("Expected full method descriptor, found \"$text\"")

val returnType = if (parameters != null) {
parseType()
} else null

require(done) { "Invalid method descriptor: \"$text\"" }

return Method(owner, name, parameters, returnType)
}

fun parseNew(full: Boolean): New {
check(cursor == 0)

val parameters = parseParameters()
if (parameters == null && full)
throw IllegalArgumentException("Expected full new descriptor, found \"$text\"")

val type = parseType()

require(done) { "Invalid new descriptor: \"$text\"" }
return New(type, parameters)
}

private fun parseParameters(): List<Descriptor>? {
return if (!done && ch == '(') {
cursor++
val parameters = mutableListOf<Descriptor>()
while (!done && ch != ')')
parameters.add(parseType())
require(!done && ch == ')') { "Invalid method descriptor: \"$text\"" }
cursor++
parameters
} else null
}
}
}
Loading

0 comments on commit 9003e59

Please sign in to comment.