Skip to content

Commit

Permalink
rolling along with gizmo
Browse files Browse the repository at this point in the history
  • Loading branch information
evanchooly committed Aug 19, 2024
1 parent 7276f6a commit cadc9fc
Show file tree
Hide file tree
Showing 19 changed files with 381 additions and 27 deletions.
4 changes: 2 additions & 2 deletions .mvn/wrapper/maven-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/4.0.0-alpha-13/apache-maven-4.0.0-alpha-13-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/4.0.0-beta-3/apache-maven-4.0.0-beta-3-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar
5 changes: 5 additions & 0 deletions critter/core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@
<version>2.29.0.Final</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.quarkus.gizmo</groupId>
<artifactId>gizmo</artifactId>
<version>1.8.0</version>
</dependency>
</dependencies>

<build>
Expand Down
4 changes: 4 additions & 0 deletions critter/core/src/main/kotlin/dev/morphia/critter/Critter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import java.io.File
class Critter(val root: File) {
companion object {
var outputDirectory = File("target/generated-sources/morphia")

fun critterPackage(entity: Class<*>): String {
return "${entity.packageName}.__morphia.${entity.simpleName.lowercase()}"
}
}

private val outputDir = File(root, "target")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package dev.morphia.critter.parser

import dev.morphia.critter.CritterEntityModel
import dev.morphia.critter.parser.gizmo.GizmoEntityModelGenerator
import dev.morphia.critter.parser.java.CritterClassLoader
import dev.morphia.critter.parser.java.CritterParser.critterClassLoader
import dev.morphia.mapping.Mapper
import org.objectweb.asm.ClassReader
import org.objectweb.asm.tree.ClassNode

class CritterGizmoGenerator(val classLoader: CritterClassLoader, val mapper: Mapper) {
val propertyFinder = PropertyFinder(mapper, classLoader)

fun generate(type: Class<*>): CritterEntityModel? {
val classNode = ClassNode()
ClassReader(type.name).accept(classNode, 0)
val propertyNames = propertyFinder.find(type, classNode)

return null // entityModel(type, propertyNames)
}

private fun entityModel(type: Class<*>, propertyNames: List<String>): CritterEntityModel {
val entityModelGenerator = GizmoEntityModelGenerator(type, propertyNames)
return critterClassLoader
.loadClass(entityModelGenerator.generatedType)
.getConstructor(Mapper::class.java)
.newInstance(mapper) as CritterEntityModel
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package dev.morphia.critter.parser

import dev.morphia.config.PropertyAnnotationProvider
import dev.morphia.critter.parser.asm.AddFieldAccessorMethods
import dev.morphia.critter.parser.asm.CritterPropertyModelGenerator
import dev.morphia.critter.parser.asm.EntityAccessorGenerator
import dev.morphia.critter.parser.gizmo.GizmoPropertyAccessorGenerator
import dev.morphia.critter.parser.gizmo.GizmoPropertyModelGenerator
import dev.morphia.critter.parser.java.CritterClassLoader
import dev.morphia.critter.parser.java.CritterParser
import dev.morphia.mapping.Mapper
Expand All @@ -28,16 +28,11 @@ class PropertyFinder(mapper: Mapper, val classLoader: CritterClassLoader) {
AddFieldAccessorMethods(entityType, fields).emit()
)
fields.forEach { field ->
val accessorGenerator = EntityAccessorGenerator(entityType, field)
classLoader.register(
accessorGenerator.generatedType.className,
accessorGenerator.emit()
)

val propertyModelGenerator = CritterPropertyModelGenerator(entityType, field)
val className = propertyModelGenerator.generatedType.className
classLoader.register(className, propertyModelGenerator.emit())
models += className
val accessorGenerator = GizmoPropertyAccessorGenerator(entityType, field)
accessorGenerator.emit()
val propertyModelGenerator = GizmoPropertyModelGenerator(entityType, field)
propertyModelGenerator.emit()
models += propertyModelGenerator.generatedType
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package dev.morphia.critter.parser.asm

import dev.morphia.annotations.Entity
import dev.morphia.critter.Critter.Companion.critterPackage
import dev.morphia.critter.CritterEntityModel
import dev.morphia.critter.parser.asm.Generators.critterPackage
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
import java.lang.reflect.Modifier
Expand All @@ -19,7 +19,7 @@ class CritterEntityModelGenerator(val entity: Class<*>, val models: List<String>
}

init {
val generatorName = "${critterPackage(entity)}${entity.simpleName}EntityModel"
val generatorName = "${critterPackage(entity)}.${entity.simpleName}EntityModel"
generatedType = Type.getType("L${generatorName};")
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package dev.morphia.critter.parser.asm

import dev.morphia.critter.Critter.Companion.critterPackage
import dev.morphia.critter.identifierCase
import dev.morphia.critter.parser.asm.Generators.critterPackage
import dev.morphia.critter.titleCase
import dev.morphia.mapping.codec.pojo.critter.CritterPropertyModel
import org.objectweb.asm.FieldVisitor
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package dev.morphia.critter.parser.asm

import dev.morphia.critter.parser.asm.Generators.critterPackage
import dev.morphia.critter.Critter.Companion.critterPackage
import dev.morphia.critter.parser.asm.Generators.wrap
import dev.morphia.critter.titleCase
import org.objectweb.asm.Label
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@ object Generators {
var config = MorphiaConfig.load()
var convention = MorphiaDefaultsConvention()

fun critterPackage(entity: Class<*>): String {
val name = "${entity.packageName}/__morphia/${entity.simpleName.lowercase()}/"
return name.replace('.', '/')
}

fun wrap(fieldType: Type): Type {
return when (fieldType) {
Type.VOID_TYPE -> Type.getType(Void::class.java)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package dev.morphia.critter.parser.gizmo

import dev.morphia.critter.Critter.Companion.critterPackage

open class BaseGizmoGenerator(val entity: Class<*>) {
val baseName: String
lateinit var generatedType: String

init {
val `package` = critterPackage(entity)
baseName = `package`
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package dev.morphia.critter.parser.gizmo

class GizmoEntityModelGenerator(type: Class<*>, propertyNames: List<String>) :
BaseGizmoGenerator(type) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package dev.morphia.critter.parser.gizmo

import dev.morphia.critter.parser.java.CritterParser.critterClassLoader
import dev.morphia.critter.titleCase
import io.quarkus.gizmo.ClassCreator
import io.quarkus.gizmo.MethodDescriptor.ofConstructor
import io.quarkus.gizmo.MethodDescriptor.ofMethod
import io.quarkus.gizmo.SignatureBuilder.*
import io.quarkus.gizmo.Type.*
import org.bson.codecs.pojo.PropertyAccessor
import org.objectweb.asm.Type.getReturnType
import org.objectweb.asm.Type.getType
import org.objectweb.asm.tree.FieldNode
import org.objectweb.asm.tree.MethodNode

class GizmoPropertyAccessorGenerator : BaseGizmoGenerator {
constructor(entity: Class<*>, field: FieldNode) : super(entity) {
propertyName = field.name
propertyType = getType(field.desc).className
generatedType = "${baseName}.${propertyName.titleCase()}Accessor"
}

constructor(entity: Class<*>, method: MethodNode) : super(entity) {
propertyName = method.name
propertyType = getReturnType(method.signature).className
generatedType = "${baseName}.${propertyName.titleCase()}Accessor"
}

lateinit var creator: ClassCreator
val propertyName: String
val propertyType: String

fun emit() {
creator =
ClassCreator.builder()
.signature(
forClass()
.addInterface(
parameterizedType(
classType(PropertyAccessor::class.java),
classType(propertyType)
)
)
)
.classOutput { name, data ->
critterClassLoader.register(name.replace('/', '.'), data)
}
.className(generatedType)
.build()

ctor()
get()
set()
creator.close()
}

private fun get() {
val method =
creator.getMethodCreator(ofMethod(generatedType, "get", propertyType, entity.name))
method.signature =
forMethod()
.addTypeParameter(typeVariable("S"))
.setReturnType(classType(propertyType))
.addParameterType(classType(entity))
.build()
method.setParameterNames(arrayOf("model"))
val toInvoke = ofMethod(entity, "__read${propertyName.titleCase()}", propertyType)
method.returnValue(method.invokeVirtualMethod(toInvoke, method.getMethodParam(0)))
}

private fun set() {
val method =
creator.getMethodCreator(
ofMethod(generatedType, "set", "void", entity.name, propertyType)
)
method.signature =
forMethod()
.addTypeParameter(typeVariable("S"))
.setReturnType(voidType())
.addParameterType(classType(entity))
.addParameterType(classType(propertyType))
.build()
method.setParameterNames(arrayOf("model", "value"))

val toInvoke = ofMethod(entity, "__write${propertyName.titleCase()}", "void", propertyType)
method.invokeVirtualMethod(toInvoke, method.getMethodParam(0), method.getMethodParam(1))
method.returnValue(null)
}

private fun ctor() {
val constructor = creator.getConstructorCreator(*arrayOf<String>())
constructor.invokeSpecialMethod(ofConstructor(Object::class.java), constructor.`this`)
constructor.returnVoid()

constructor.close()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package dev.morphia.critter.parser.gizmo

import dev.morphia.critter.parser.java.CritterParser.critterClassLoader
import dev.morphia.critter.parser.ksp.extensions.methodCase
import dev.morphia.critter.titleCase
import dev.morphia.mapping.codec.pojo.EntityModel
import dev.morphia.mapping.codec.pojo.PropertyModel
import dev.morphia.mapping.codec.pojo.critter.CritterPropertyModel
import io.quarkus.gizmo.ClassCreator
import io.quarkus.gizmo.MethodCreator
import io.quarkus.gizmo.MethodDescriptor
import io.quarkus.gizmo.ResultHandle
import org.bson.codecs.pojo.PropertyAccessor
import org.objectweb.asm.Type
import org.objectweb.asm.Type.getType
import org.objectweb.asm.tree.AnnotationNode
import org.objectweb.asm.tree.FieldNode

class GizmoPropertyModelGenerator : BaseGizmoGenerator {

constructor(entity: Class<*>, field: FieldNode) : super(entity) {
propertyName = field.name.titleCase()
generatedType = "${baseName}.${propertyName}Model"
accessorType = "${baseName}.${propertyName}Accessor"
annotations = field.visibleAnnotations ?: listOf()
}

val propertyName: String
val accessorType: String
lateinit var creator: ClassCreator
lateinit var annotations: List<AnnotationNode>

fun emit() {
creator =
ClassCreator.builder()
.classOutput { name, data ->
critterClassLoader.register(name.replace('/', '.'), data)
}
.className(generatedType)
.superClass(CritterPropertyModel::class.java)
.build()

ctor()
getAccessor()

creator.close()
}

private fun ctor() {
val constructor = creator.getConstructorCreator(EntityModel::class.java)
constructor.invokeSpecialMethod(
MethodDescriptor.ofConstructor(PropertyModel::class.java, EntityModel::class.java),
constructor.`this`,
constructor.getMethodParam(0)
)
constructor.setParameterNames(arrayOf("model"))

registerAnnotations(constructor)

constructor.close()
}

private fun registerAnnotations(constructor: MethodCreator) {
val annotationMethod =
MethodDescriptor.ofMethod(
PropertyModel::class.java.name,
"annotation",
PropertyModel::class.java.name,
Annotation::class.java
)
annotations.forEach { annotation ->
constructor.invokeVirtualMethod(
annotationMethod,
constructor.`this`,
annotationBuilder(constructor, annotation)
)
}
}

private fun annotationBuilder(
constructor: MethodCreator,
annotation: AnnotationNode
): ResultHandle {
val type = getType(annotation.desc)
val classType = type.className.substringAfterLast('.')
val builderType = Type.getType("L${type.className}Builder;")
val builder =
MethodDescriptor.ofMethod(
builderType.className,
"${classType.methodCase()}Builder",
builderType.className
)

var local = constructor.invokeStaticMethod(builder)
val values = annotation.values?.windowed(2, 2) ?: emptyList()
values.forEach { value ->
val method =
MethodDescriptor.ofMethod(
builderType.className,
value[0] as String,
builderType.className,
value[1].javaClass
)
constructor.invokeVirtualMethod(method, local, load(constructor, value[1]))
}

return local
}

private fun load(constructor: MethodCreator, value: Any): ResultHandle {
return when (value) {
is String -> constructor.load(value)
is Int -> constructor.load(value)
is Long -> constructor.load(value)
is Boolean -> constructor.load(value)
is List<*> -> {
val toTypedArray = value.map { load(constructor, it!!) }.toTypedArray()
constructor.marshalAsArray(value[0]!!.javaClass, *toTypedArray)
}
else -> TODO("${value.javaClass} is not yet supported")
}
}

private fun getAccessor() {
val field = creator.getFieldCreator("accessor", accessorType)
val method =
creator.getMethodCreator(
MethodDescriptor.ofMethod(
creator.className,
"getAccessor",
PropertyAccessor::class.java.name
)
)

method.returnValue(method.readInstanceField(field.fieldDescriptor, method.`this`))

method.close()
}
}
Loading

0 comments on commit cadc9fc

Please sign in to comment.