
com.jtransc.plugin.reflection.MetaReflectionJTranscPlugin.kt Maven / Gradle / Ivy
package com.jtransc.plugin.reflection
import com.jtransc.annotation.JTranscInvisibleExternal
import com.jtransc.annotation.haxe.HaxeRemoveField
import com.jtransc.ast.*
import com.jtransc.plugin.JTranscPlugin
import com.jtransc.reflection.JTranscInternalNames
import j.ClassInfo
import j.MemberInfo
import j.ProgramReflection
/**
* This class aims to create classes to perform reflection on available classes
*/
class MetaReflectionJTranscPlugin : JTranscPlugin() {
override val priority: Int = Int.MAX_VALUE - 1000
fun AstClass.mustReflect(invisibleExternalSet: Set = setOf()): Boolean {
return this.visible && (this.fqname !in invisibleExternalSet) && (this.annotationsList.getNativeNameForTarget(targetName) == null)
}
fun AstMethod.mustReflect(): Boolean = this.visible
fun AstField.mustReflect(): Boolean = this.visible && !this.annotationsList.contains()
override fun processAfterTreeShaking(program: AstProgram) {
// Do not generate if ProgramReflection class is not referenced!
// Also methods are not updated in the case they do not exist!
if (ProgramReflection::class.java.fqname !in program) return
if (ClassInfo::class.java.fqname !in program) return
val types = program.types
val ProgramReflectionClass = program[ProgramReflection::class.java.fqname]
val annotationFqname = "java.lang.annotation.Annotation".fqname
val classesForAnnotations = hashMapOf()
fun getAnnotationProxyClass(annotationType: AstType.REF): AstClass {
return classesForAnnotations.getOrPut(annotationType) {
val annotationClass = program[annotationType]!!
val annotationMethods = annotationClass.methods.filter { it.methodType.argCount == 0 }
val members = annotationMethods.map { it.name to it.methodType.ret }
val proxyClassName = "${annotationType.fqname}\$Impl"
val clazz = program.createDataClass(proxyClassName.fqname, members, interfaces = listOf(annotationType.name, annotationFqname)) {
extraVisible = true
val annotationTypeMR = AstMethodWithoutClassRef("annotationType", AstType.METHOD(AstType.CLASS, listOf()))
val getClassMR = AstMethodWithoutClassRef("getClass", AstType.METHOD(AstType.CLASS, listOf()))
val toStringMR = AstMethodWithoutClassRef("toString", AstType.METHOD(AstType.STRING, listOf()))
for (m in annotationMethods) {
val ref = AstMethodWithoutClassRef(m.name, m.methodType)
if (ref == annotationTypeMR) continue
if (ref == getClassMR) continue
if (ref == toStringMR) continue
createMethod(m.name, m.methodType) {
extraVisible = false
RETURN(THIS[locateField(m.name)!!])
}
}
createMethod(annotationTypeMR.name, annotationTypeMR.type) {
RETURN(annotationType.lit)
}
createMethod(getClassMR.name, getClassMR.type) {
RETURN(annotationType.lit)
}
createMethod(toStringMR.name, toStringMR.type) {
val appendObject = AstMethodRef(StringBuilder::class.java.fqname, "append", AstType.METHOD(AstType.STRINGBUILDER, listOf(AstType.OBJECT)))
val toString = AstMethodRef(java.lang.Object::class.java.fqname, "toString", AstType.METHOD(AstType.STRING, listOf()))
var mod: AstExpr = AstExpr.NEW_WITH_CONSTRUCTOR(AstMethodRef(AstType.STRINGBUILDER.name, "", AstType.METHOD(AstType.VOID, listOf())), listOf())
var buffer = ""
var pos = 0
buffer += "@${annotationType.fqname}("
for (method in annotationMethods) {
if (pos != 0) buffer += ","
pos++
buffer += "${method.name}="
if (buffer.isNotEmpty()) {
mod = mod[appendObject](buffer.lit)
buffer = ""
}
mod = mod[appendObject](THIS[method]().castTo(OBJECT))
}
mod = mod[appendObject](")".lit)
RETURN(mod[toString]())
//RETURN(AstExpr.LITERAL("@${annotationType.fqname}", types))
}
}
//println("Created class: $proxyClassName : ${clazz.visible}")
clazz
}
}
//println("----------")
for (clazz in program.classes.toList().filter { it.extendsOrImplements(annotationFqname) && !it.fqname.endsWith("\$Proxy") }) {
//println("$clazz: ${clazz.extendsOrImplements(annotationFqname)}")
getAnnotationProxyClass(clazz.ref)
}
val invisibleExternalSet = program.allAnnotationsList.getAllTyped().flatMap { it.classes.toList() }.toSet()
val visibleClasses = program.classes.filter { it.mustReflect(invisibleExternalSet) }
val CLASS_INFO = program[ClassInfo::class.java.fqname]
val CLASS_INFO_CREATE = CLASS_INFO.getMethodWithoutOverrides(ClassInfo::create.name)!!.ref
val genInternalClassNames = program.containsMethod(JTranscInternalNames::class.java.fqname, JTranscInternalNames::getInternalClassName.name)
val genInternalMemberNames = program.containsMethod(JTranscInternalNames::class.java.fqname, JTranscInternalNames::getInternalFieldName.name) || program.containsMethod(JTranscInternalNames::class.java.fqname, JTranscInternalNames::getInternalMethodName.name)
fun toAnnotationExpr(data: Any?, temps: TempAstLocalFactory, builder: AstBuilder2): AstExpr {
return builder.run {
when (data) {
null -> null.lit
is AstAnnotation -> {
if (data.runtimeVisible) {
val annotationType = program[data.type]
val annotationProxy = getAnnotationProxyClass(data.type)
val annotationProxyConstructor = annotationProxy.constructors.first()
val args = arrayListOf()
for (m in annotationType!!.methods) {
val value = data.elements[m.name] ?: m.defaultTag
args += value
}
AstExpr.NEW_WITH_CONSTRUCTOR(annotationProxyConstructor.ref, args.map { toAnnotationExpr(it, temps, builder) })
} else {
null.lit
}
}
is AstFieldWithoutTypeRef -> toAnnotationExpr(program[data.containingClass].locateField(data.name)!!.ref, temps, builder)
is AstFieldRef -> AstExpr.FIELD_STATIC_ACCESS(data)
is Pair<*, *> -> toAnnotationExpr(data.second, temps, builder)
is List<*> -> {
val local = temps.create(ARRAY(OBJECT))
SET(local, (local.type.asArray()).newArray(data.size.lit))
for ((index, item) in data.withIndex()) {
SET_ARRAY(local, index.lit, toAnnotationExpr(item, temps, builder))
}
local.expr
}
is com.jtransc.org.objectweb.asm.Type -> {
AstType.REF(data.className.fqname).lit
}
else -> data.lit
}
}
}
val ANNOTATION_ARRAY = ARRAY(AstType.REF("java.lang.annotation.Annotation"))
ProgramReflectionClass.getMethodWithoutOverrides(ProgramReflection::getClassAnnotations.name)?.replaceBodyOptBuild { args ->
val (classIdArg) = args
val temps = TempAstLocalFactory()
val outLocal = AstLocal(0, "out", ANNOTATION_ARRAY)
SWITCH(classIdArg.expr) {
for (clazz in visibleClasses) {
val annotations = clazz.runtimeAnnotations
if (annotations.isNotEmpty() && "java.lang.annotation.Annotation".fqname !in clazz.implementing) {
CASE(clazz.classId) {
SET(outLocal, ANNOTATION_ARRAY.newArray(annotations.size.lit))
for ((index, annotation) in annotations.withIndex()) {
SET_ARRAY(outLocal, index.lit, toAnnotationExpr(annotation, temps, this))
}
RETURN(outLocal)
}
}
}
}
RETURN(NULL)
}
ProgramReflectionClass.getMethodWithoutOverrides(ProgramReflection::getFieldAnnotations.name)?.replaceBodyOptBuild { args ->
val (classIdArg, fieldIdArg) = args
val temps = TempAstLocalFactory()
val outLocal = AstLocal(0, "out", ANNOTATION_ARRAY)
SWITCH(classIdArg.expr) {
for (clazz in visibleClasses) {
val fields = clazz.fields.filter { it.mustReflect() }
if (fields.flatMap { it.runtimeAnnotations }.isNotEmpty()) {
CASE(clazz.classId) {
SWITCH(fieldIdArg.expr) {
for (field in fields) {
val annotations = field.runtimeAnnotations
if (annotations.isNotEmpty()) {
CASE(field.id) {
SET(outLocal, ANNOTATION_ARRAY.newArray(annotations.size.lit))
for ((index, annotation) in annotations.withIndex()) {
SET_ARRAY(outLocal, index.lit, toAnnotationExpr(annotation, temps, this))
}
RETURN(outLocal)
}
}
}
}
}
}
}
}
RETURN(NULL)
}
ProgramReflectionClass.getMethodWithoutOverrides(ProgramReflection::getMethodAnnotations.name)?.replaceBodyOptBuild { args ->
val (classIdArg, methodIdArg) = args
val temps = TempAstLocalFactory()
val outLocal = AstLocal(0, "out", ANNOTATION_ARRAY)
SWITCH(classIdArg.expr) {
for (clazz in visibleClasses) {
val methods = clazz.methods
if (methods.flatMap { it.runtimeAnnotations }.isNotEmpty()) {
CASE(clazz.classId) {
SWITCH(methodIdArg.expr) {
for (method in methods) {
val annotations = method.runtimeAnnotations
if (annotations.isNotEmpty()) {
CASE(method.id) {
SET(outLocal, ANNOTATION_ARRAY.newArray(annotations.size.lit))
for ((index, annotation) in annotations.withIndex()) {
SET_ARRAY(outLocal, index.lit, toAnnotationExpr(annotation, temps, this))
}
RETURN(outLocal)
}
}
}
}
}
}
}
}
RETURN(NULL)
}
ProgramReflectionClass.getMethodWithoutOverrides(ProgramReflection::getMethodArgumentAnnotations.name)?.replaceBodyOptBuild { args ->
val (classIdArg, methodIdArg, argIndexArt) = args
val temps = TempAstLocalFactory()
val outLocal = AstLocal(0, "out", ANNOTATION_ARRAY)
SWITCH(classIdArg.expr) {
for (clazz in visibleClasses) {
val methods = clazz.methods.filter { it.mustReflect() }
if (methods.flatMap { it.parameterAnnotations.flatMap { it } }.isNotEmpty()) {
CASE(clazz.classId) {
SWITCH(methodIdArg.expr) {
for (method in methods) {
val allParameterAnnotations = method.parameterAnnotations.flatMap { it }
if (allParameterAnnotations.isNotEmpty()) {
CASE(method.id) {
SWITCH(argIndexArt.expr) {
for (argIndex in 0 until method.methodType.argCount) {
val annotations = method.parameterAnnotations[argIndex]
if (annotations.isNotEmpty()) {
CASE(argIndex) {
SET(outLocal, ANNOTATION_ARRAY.newArray(annotations.size.lit))
for ((index, annotation) in annotations.withIndex()) {
SET_ARRAY(outLocal, index.lit, toAnnotationExpr(annotation, temps, this))
}
RETURN(outLocal)
}
}
}
}
RETURN(NULL)
}
}
}
}
}
}
}
}
RETURN(NULL)
}
val casesPerMethod: Int = 100
val getAllClassesMethod: AstMethod? =
if (program.contains(ProgramReflection.AllClasses::class.java.fqname))
program[ProgramReflection.AllClasses::class.java.fqname].getMethodWithoutOverrides(ProgramReflection.AllClasses::getAllClasses.name)
else
ProgramReflectionClass.getMethodWithoutOverrides(ProgramReflection::getAllClasses.name)
getAllClassesMethod?.replaceBodyOptBuild {
val out = AstLocal(0, "out", ARRAY(CLASS_INFO))
SET(out, ARRAY(CLASS_INFO).newArray(program.lastClassId.lit))
for (oldClass in visibleClasses.sortedBy { it.classId }) {
val classId = oldClass.classId
val directInterfaces = oldClass.directInterfaces
val relatedTypes = oldClass.getAllRelatedTypesIdsWithout0AtEnd()
//println("CLASS: ${oldClass.fqname}")
SET_ARRAY(out, classId.lit, CLASS_INFO_CREATE(
classId.lit,
if (genInternalClassNames) AstExpr.LITERAL_REFNAME(oldClass.ref) else NULL,
oldClass.fqname.lit,
oldClass.modifiers.acc.lit,
(oldClass.parentClass?.classId ?: -1).lit,
if (directInterfaces.isNotEmpty()) AstExpr.INTARRAY_LITERAL(directInterfaces.map { it.classId }) else null.lit,
if (relatedTypes.isNotEmpty()) AstExpr.INTARRAY_LITERAL(relatedTypes) else null.lit
))
}
RETURN(out.local)
}
// @TODO: We should create a submethod per class to avoid calling ::SI (static initialization) for all the classes
if (program.contains(ProgramReflection.DynamicNewInvoke::class.java.fqname)) {
val dynamicNewInvokeClass: AstClass = program[ProgramReflection.DynamicNewInvoke::class.java.fqname]
val dynamicInvokeMethod: AstMethod? = dynamicNewInvokeClass.getMethodWithoutOverrides(ProgramReflection.DynamicNewInvoke::dynamicInvoke.name)
if (dynamicInvokeMethod != null) {
val additionalMethods: MutableList = mutableListOf()
val classes: List = visibleClasses.flatMap { it.methodsWithoutConstructors.filter { it.mustReflect() } }.sortedBy { it.id }
var methodIndex: Int = 0
while (methodIndex * casesPerMethod < classes.size) {
val mI: Int = methodIndex
val sI: Int = methodIndex * casesPerMethod
val newMethod: AstMethod =
dynamicNewInvokeClass.createMethod(dynamicInvokeMethod.name + mI, dynamicInvokeMethod.methodType, true) {
val (classId, methodId, obj, args) = it
var currentIndex: Int = sI
val finishIndex: Int = if (currentIndex + casesPerMethod < classes.size) currentIndex + casesPerMethod else classes.size
SWITCH(methodId.expr) {
while (currentIndex < finishIndex) {
val method = classes[currentIndex]
val params: List = method.methodType.args.map {
AstExpr.ARRAY_ACCESS(args.expr, it.index.lit).castTo(it.type)
}
val callExprUncasted = if (method.isStatic) {
AstExpr.CALL_STATIC(method.containingClass.ref, method.ref, params)
} else {
AstExpr.CALL_INSTANCE(obj.expr.castTo(method.containingClass.ref), method.ref, params)
}
CASE(method.id) {
if (method.methodType.retVoid) {
STM(callExprUncasted)
RETURN(NULL)
} else {
RETURN(callExprUncasted.castTo(OBJECT))
}
}
currentIndex++
}
}
RETURN(NULL)
}
additionalMethods.add(newMethod)
methodIndex++
}
dynamicInvokeMethod.replaceBodyOptBuild {
val (classId, methodId, obj, args) = it
var id: Int = additionalMethods.size
while (--id >= 0) {
val method: AstMethod = additionalMethods[id]
val constructor: AstMethod = classes[id * casesPerMethod]
IF(AstExpr.BINOP(AstType.BOOL, methodId.expr, AstBinop.GE, constructor.id.lit)) {
val params: List = listOf(classId.expr, methodId.expr, obj.expr, args.expr)
val callExprUncasted = AstExpr.CALL_STATIC(method.ref, params)
RETURN(callExprUncasted.castTo(OBJECT))
}
}
RETURN(NULL)
}
}
}
// @TODO: We should create a submethod per class to avoid calling ::SI (static initialization) for all the classes
if (program.contains(ProgramReflection.DynamicNewInvoke::class.java.fqname)) {
val dynamicNewInvokeClass: AstClass = program[ProgramReflection.DynamicNewInvoke::class.java.fqname]
val dynamicNewMethod: AstMethod? = dynamicNewInvokeClass.getMethodWithoutOverrides(ProgramReflection.DynamicNewInvoke::dynamicNew.name)
if (dynamicNewMethod != null) {
val additionalMethods: MutableList = mutableListOf()
val classes: List = visibleClasses.filter { !it.isAbstract && !it.isInterface }.flatMap { it.constructors.filter { it.mustReflect() } }.sortedBy { it.id }
var methodIndex: Int = 0
while (methodIndex * casesPerMethod < classes.size) {
val mI: Int = methodIndex
val sI: Int = methodIndex * casesPerMethod
val newMethod: AstMethod =
dynamicNewInvokeClass.createMethod(dynamicNewMethod.name + mI, dynamicNewMethod.methodType, true) {
val (classId, methodId, args) = it
var currentIndex: Int = sI
val finishIndex: Int = if (currentIndex + casesPerMethod < classes.size) currentIndex + casesPerMethod else classes.size
SWITCH(methodId.expr) {
while (currentIndex < finishIndex) {
val constructor = classes[currentIndex]
CASE(constructor.id) {
val params = constructor.methodType.args.map {
AstExpr.ARRAY_ACCESS(args.expr, it.index.lit).castTo(it.type)
}
val callExprUncasted = AstExpr.NEW_WITH_CONSTRUCTOR(constructor.ref, params)
RETURN(callExprUncasted.castTo(OBJECT))
}
currentIndex++
}
}
RETURN(NULL)
}
additionalMethods.add(newMethod)
methodIndex++
}
dynamicNewMethod.replaceBodyOptBuild {
val (classId, methodId, args) = it
var id: Int = additionalMethods.size
while (--id >= 0) {
val method: AstMethod = additionalMethods[id]
val constructor: AstMethod = classes[id * casesPerMethod]
IF(AstExpr.BINOP(AstType.BOOL, methodId.expr, AstBinop.GE, constructor.id.lit)) {
val params: List = listOf(classId.expr, methodId.expr, args.expr)
val callExprUncasted = AstExpr.CALL_STATIC(method.ref, params)
RETURN(callExprUncasted.castTo(OBJECT))
}
}
RETURN(NULL)
}
}
}
// Member information (constructors, methods and fields)
if (MemberInfo::class.java.fqname in program) {
val MemberInfoClass = program[MemberInfo::class.java.fqname]
val MemberInfo_create = MemberInfoClass.getMethodWithoutOverrides(MemberInfo::create.name)!!.ref
//val MemberInfo_createList = MemberInfoClass.getMethodWithoutOverrides(MemberInfo::createList.name)!!.ref
data class MemberInfoWithRef(val ref: Any, val mi: MemberInfo)
fun genMemberMethods(list: List>>, clazz: AstClass, method: AstMethod): List {
val additionalMethods: MutableList = mutableListOf()
var methodIndex: Int = 0
while (methodIndex * casesPerMethod < list.size) {
val mI: Int = methodIndex
val sI: Int = methodIndex * casesPerMethod
val parentClass: AstClass = clazz
val parentMethod: AstMethod = method
val newMethod: AstMethod =
parentClass.createMethod(parentMethod.name + mI, parentMethod.methodType, true) {
val (classIdArg) = it
val out = AstLocal(0, "out", ARRAY(MemberInfoClass))
var currentIndex: Int = sI
val finishIndex: Int = if (currentIndex + casesPerMethod < list.size) currentIndex + casesPerMethod else list.size
SWITCH(classIdArg.expr) {
while (currentIndex < finishIndex) {
val (keyClass, members) = list[currentIndex]
if (members.isNotEmpty()) {
CASE(keyClass.classId) {
SET(out, ARRAY(MemberInfoClass).newArray(members.size.lit))
for ((index, memberWithRef) in members.withIndex()) {
val ref = memberWithRef.ref
val member = memberWithRef.mi
SET_ARRAY(out, index.lit, MemberInfo_create(
member.id.lit,
if (genInternalMemberNames) AstExpr.LITERAL_REFNAME(ref) else NULL,
member.name.lit,
member.modifiers.lit,
member.desc.lit,
member.genericDesc.lit
))
}
RETURN(out.local)
}
}
currentIndex++
}
}
RETURN(NULL)
}
additionalMethods.add(newMethod)
methodIndex++
}
return additionalMethods
}
fun AstBuilder2.genMemberList(args: List, list: List>>, additionalMethods: List) {
val (classIdArg) = args
var id: Int = additionalMethods.size
while (--id >= 0) {
val method: AstMethod = additionalMethods[id]
val (keyClass, members) = list[id * casesPerMethod]
IF(AstExpr.BINOP(AstType.BOOL, classIdArg.expr, AstBinop.GE, keyClass.classId.lit)) {
val params = listOf(classIdArg.expr)
val callExprUncasted = AstExpr.CALL_STATIC(method.ref, params)
RETURN(callExprUncasted.castTo(ARRAY(MemberInfoClass)))
}
}
RETURN(NULL)
}
// ProgramReflectionClass.getConstructors
if (program.contains(ProgramReflection.AllConstructors::class.java.fqname)) {
val allConstructorsClass: AstClass = program[ProgramReflection.AllConstructors::class.java.fqname]
val getConstructorsMethod: AstMethod? = allConstructorsClass.getMethodWithoutOverrides(ProgramReflection.AllConstructors::getConstructors.name)
if (getConstructorsMethod != null) {
val list: List>> = visibleClasses.map { clazz ->
clazz to clazz.constructors.filter { it.mustReflect() }.map {
MemberInfoWithRef(it.ref, MemberInfo(it.id, null, it.name, it.modifiers.acc, it.desc, it.genericSignature))
}
}.toList()
val additionalMethods = genMemberMethods(list, allConstructorsClass, getConstructorsMethod)
getConstructorsMethod.replaceBodyOptBuild {
genMemberList(it, list, additionalMethods)
}
}
}
// ProgramReflectionClass.getMethods
if (program.contains(ProgramReflection.AllMethods::class.java.fqname)) {
val allMethodsClass: AstClass = program[ProgramReflection.AllMethods::class.java.fqname]
val getMethodsMethod: AstMethod? = allMethodsClass.getMethodWithoutOverrides(ProgramReflection.AllMethods::getMethods.name)
if (getMethodsMethod != null) {
val list: List>> = visibleClasses.map { clazz ->
clazz to clazz.methodsWithoutConstructors.filter { it.mustReflect() }.map {
MemberInfoWithRef(it.ref, MemberInfo(it.id, null, it.name, it.modifiers.acc, it.desc, it.genericSignature))
}
}.toList()
val additionalMethods = genMemberMethods(list, allMethodsClass, getMethodsMethod)
getMethodsMethod.replaceBodyOptBuild {
genMemberList(it, list, additionalMethods)
}
}
}
// ProgramReflectionClass.getFields
if (program.contains(ProgramReflection.AllFields::class.java.fqname)) {
val allFieldsClass: AstClass = program[ProgramReflection.AllFields::class.java.fqname]
val getFieldsMethod: AstMethod? = allFieldsClass.getMethodWithoutOverrides(ProgramReflection.AllFields::getFields.name)
if (getFieldsMethod != null) {
val list: List>> = visibleClasses.map { clazz ->
clazz to clazz.fields.filter { it.mustReflect() }.map {
MemberInfoWithRef(it.ref, MemberInfo(it.id, null, it.name, it.modifiers.acc, it.desc, it.genericSignature))
}
}.toList()
val additionalMethods = genMemberMethods(list, allFieldsClass, getFieldsMethod)
getFieldsMethod.replaceBodyOptBuild {
genMemberList(it, list, additionalMethods)
}
}
}
// ProgramReflectionClass.dynamicGet
if (program.contains(ProgramReflection.DynamicGetSet::class.java.fqname)) {
val dynamicGetSetClass: AstClass = program[ProgramReflection.DynamicGetSet::class.java.fqname]
val dynamicGetMethod: AstMethod? = dynamicGetSetClass.getMethodWithoutOverrides(ProgramReflection.DynamicGetSet::dynamicGet.name)
if (dynamicGetMethod != null) {
val additionalMethods: MutableList = mutableListOf()
val fields: List = visibleClasses.flatMap { it.fields.filter { it.mustReflect() } }.sortedBy { it.id }
var methodIndex: Int = 0
while (methodIndex * casesPerMethod < fields.size) {
val mI: Int = methodIndex
val sI: Int = methodIndex * casesPerMethod
val newMethod: AstMethod = dynamicGetSetClass.createMethod(dynamicGetMethod.name + mI, dynamicGetMethod.methodType, true) {
val (classId, fieldId, objParam) = it
var currentIndex: Int = sI
val finishIndex: Int = if (currentIndex + casesPerMethod < fields.size) currentIndex + casesPerMethod else fields.size
SWITCH(fieldId.expr) {
while (currentIndex < finishIndex) {
val field = fields[currentIndex]
val expr = if (field.isStatic) {
AstExpr.FIELD_STATIC_ACCESS(field.ref)
} else {
AstExpr.FIELD_INSTANCE_ACCESS(field.ref, objParam.expr.castTo(field.containingClass.astType))
}
CASE(field.id) {
RETURN(expr.castTo(OBJECT))
}
currentIndex++
}
}
RETURN(NULL)
}
additionalMethods.add(newMethod)
methodIndex++
}
dynamicGetMethod.replaceBodyOptBuild {
val (classId, fieldId, objParam) = it
var id: Int = additionalMethods.size
while (--id >= 0) {
val method: AstMethod = additionalMethods[id]
val field: AstField = fields[id * casesPerMethod]
IF(AstExpr.BINOP(AstType.BOOL, fieldId.expr, AstBinop.GE, field.id.lit)) {
val params = listOf(classId.expr, fieldId.expr, objParam.expr)
val callExprUncasted = AstExpr.CALL_STATIC(method.ref, params)
RETURN(callExprUncasted.castTo(OBJECT))
}
}
RETURN(NULL)
}
}
}
// ProgramReflectionClass.dynamicSet
if (program.contains(ProgramReflection.DynamicGetSet::class.java.fqname)) {
val dynamicGetSetClass: AstClass = program[ProgramReflection.DynamicGetSet::class.java.fqname]
val dynamicSetMethod: AstMethod? = dynamicGetSetClass.getMethodWithoutOverrides(ProgramReflection.DynamicGetSet::dynamicSet.name)
if (dynamicSetMethod != null) {
val additionalMethods: MutableList = mutableListOf()
val fields: List = visibleClasses.flatMap { it.fields.filter { it.mustReflect() } }.sortedBy { it.id }
var methodIndex: Int = 0
while (methodIndex * casesPerMethod < fields.size) {
val mI: Int = methodIndex
val sI: Int = methodIndex * casesPerMethod
val newMethod: AstMethod = dynamicGetSetClass.createMethod(dynamicSetMethod.name + mI, dynamicSetMethod.methodType, true) {
val (classIdParam, fieldIdParam, objParam, valueParam) = it
var currentIndex: Int = sI
val finishIndex: Int = if (currentIndex + casesPerMethod < fields.size) currentIndex + casesPerMethod else fields.size
SWITCH(fieldIdParam.expr) {
while (currentIndex < finishIndex) {
val field = fields[currentIndex]
val expr = valueParam.expr.castTo(field.type)
CASE(field.id) {
if (field.isStatic) {
STM(AstStm.SET_FIELD_STATIC(field.ref, expr))
} else {
STM(AstStm.SET_FIELD_INSTANCE(field.ref, objParam.expr.castTo(field.containingClass.astType), expr))
}
AstStm.BREAK()
}
currentIndex++
}
}
}
additionalMethods.add(newMethod)
methodIndex++
}
dynamicSetMethod.replaceBodyOptBuild {
val (classIdParam, fieldIdParam, objParam, valueParam) = it
var id: Int = additionalMethods.size
while (--id >= 0) {
val method: AstMethod = additionalMethods[id]
val field: AstField = fields[id * casesPerMethod]
IF(AstExpr.BINOP(AstType.BOOL, fieldIdParam.expr, AstBinop.GE, field.id.lit)) {
val params = listOf(classIdParam.expr, fieldIdParam.expr, objParam.expr, valueParam.expr)
STM(AstExpr.CALL_STATIC(method.ref, params))
RETURN()
}
}
}
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy