|
| 1 | +package onion.compiler.environment |
| 2 | + |
| 3 | +import onion.compiler.{IRT, Modifier, OnionTypeConversion, MultiTable, OrderedTable, ClassTable} |
| 4 | +import org.objectweb.asm.{ClassReader, Opcodes} |
| 5 | +import org.objectweb.asm.tree.{ClassNode, MethodNode, FieldNode} |
| 6 | +import org.apache.bcel.generic.{Type => BType} |
| 7 | + |
| 8 | +object AsmRefs { |
| 9 | + private def toOnionModifier(access: Int): Int = { |
| 10 | + var mod = 0 |
| 11 | + if ((access & Opcodes.ACC_PRIVATE) != 0) mod |= Modifier.PRIVATE |
| 12 | + if ((access & Opcodes.ACC_PROTECTED) != 0) mod |= Modifier.PROTECTED |
| 13 | + if ((access & Opcodes.ACC_PUBLIC) != 0) mod |= Modifier.PUBLIC |
| 14 | + if ((access & Opcodes.ACC_STATIC) != 0) mod |= Modifier.STATIC |
| 15 | + if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) mod |= Modifier.SYNCHRONIZED |
| 16 | + if ((access & Opcodes.ACC_ABSTRACT) != 0) mod |= Modifier.ABSTRACT |
| 17 | + if ((access & Opcodes.ACC_FINAL) != 0) mod |= Modifier.FINAL |
| 18 | + mod |
| 19 | + } |
| 20 | + |
| 21 | + final val CONSTRUCTOR_NAME = "<init>" |
| 22 | + |
| 23 | + class AsmMethodRef(method: MethodNode, override val affiliation: IRT.ClassType, bridge: OnionTypeConversion) extends IRT.Method { |
| 24 | + override val modifier: Int = toOnionModifier(method.access) |
| 25 | + override val name: String = method.name |
| 26 | + private val argTypes = org.objectweb.asm.Type.getArgumentTypes(method.desc).map(t => bridge.toOnionType(BType.getType(t.getDescriptor))) |
| 27 | + override def arguments: Array[IRT.Type] = argTypes.clone() |
| 28 | + override val returnType: IRT.Type = bridge.toOnionType(BType.getType(org.objectweb.asm.Type.getReturnType(method.desc).getDescriptor)) |
| 29 | + val underlying: MethodNode = method |
| 30 | + } |
| 31 | + |
| 32 | + class AsmFieldRef(field: FieldNode, override val affiliation: IRT.ClassType, bridge: OnionTypeConversion) extends IRT.FieldRef { |
| 33 | + override val modifier: Int = toOnionModifier(field.access) |
| 34 | + override val name: String = field.name |
| 35 | + override val `type`: IRT.Type = bridge.toOnionType(BType.getType(field.desc)) |
| 36 | + val underlying: FieldNode = field |
| 37 | + } |
| 38 | + |
| 39 | + class AsmConstructorRef(method: MethodNode, override val affiliation: IRT.ClassType, bridge: OnionTypeConversion) extends IRT.ConstructorRef { |
| 40 | + override val modifier: Int = toOnionModifier(method.access) |
| 41 | + override val name: String = CONSTRUCTOR_NAME |
| 42 | + private val args0 = org.objectweb.asm.Type.getArgumentTypes(method.desc).map(t => bridge.toOnionType(BType.getType(t.getDescriptor))) |
| 43 | + override def getArgs: Array[IRT.Type] = args0.clone() |
| 44 | + val underlying: MethodNode = method |
| 45 | + } |
| 46 | + |
| 47 | + class AsmClassType(classBytes: Array[Byte], table: ClassTable) extends IRT.AbstractClassType { |
| 48 | + private val node = { |
| 49 | + val cr = new ClassReader(classBytes) |
| 50 | + val n = new ClassNode() |
| 51 | + cr.accept(n, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES) |
| 52 | + n |
| 53 | + } |
| 54 | + |
| 55 | + private val bridge = new OnionTypeConversion(table) |
| 56 | + private val modifier_ = toOnionModifier(node.access) |
| 57 | + |
| 58 | + private lazy val methods_ : MultiTable[IRT.Method] = { |
| 59 | + val m = new MultiTable[IRT.Method] |
| 60 | + import scala.jdk.CollectionConverters._ |
| 61 | + for (method <- node.methods.asInstanceOf[java.util.List[MethodNode]].asScala if method.name != CONSTRUCTOR_NAME) { |
| 62 | + m.add(new AsmMethodRef(method, this, bridge)) |
| 63 | + } |
| 64 | + m |
| 65 | + } |
| 66 | + |
| 67 | + private lazy val fields_ : OrderedTable[IRT.FieldRef] = { |
| 68 | + val f = new OrderedTable[IRT.FieldRef] |
| 69 | + import scala.jdk.CollectionConverters._ |
| 70 | + for (field <- node.fields.asInstanceOf[java.util.List[FieldNode]].asScala) { |
| 71 | + f.add(new AsmFieldRef(field, this, bridge)) |
| 72 | + } |
| 73 | + f |
| 74 | + } |
| 75 | + |
| 76 | + private lazy val constructors_ : Seq[IRT.ConstructorRef] = { |
| 77 | + import scala.jdk.CollectionConverters._ |
| 78 | + node.methods.asInstanceOf[java.util.List[MethodNode]].asScala.collect { |
| 79 | + case m if m.name == CONSTRUCTOR_NAME => new AsmConstructorRef(m, this, bridge) |
| 80 | + }.toSeq |
| 81 | + } |
| 82 | + |
| 83 | + def isInterface: Boolean = (node.access & Opcodes.ACC_INTERFACE) != 0 |
| 84 | + def modifier: Int = modifier_ |
| 85 | + def name: String = node.name.replace('/', '.') |
| 86 | + def superClass: IRT.ClassType = { |
| 87 | + if (node.superName == null) null else table.load(node.superName.replace('/', '.')) |
| 88 | + } |
| 89 | + def interfaces: Seq[IRT.ClassType] = { |
| 90 | + import scala.jdk.CollectionConverters._ |
| 91 | + node.interfaces.asInstanceOf[java.util.List[String]].asScala.map(n => table.load(n.replace('/', '.'))).toIndexedSeq |
| 92 | + } |
| 93 | + |
| 94 | + def methods: Seq[IRT.Method] = methods_.values |
| 95 | + def methods(name: String): Array[IRT.Method] = methods_.get(name).toArray |
| 96 | + def fields: Array[IRT.FieldRef] = fields_.values.toArray |
| 97 | + def field(name: String): IRT.FieldRef = fields_.get(name).orNull |
| 98 | + def constructors: Array[IRT.ConstructorRef] = constructors_.toArray |
| 99 | + } |
| 100 | +} |
| 101 | + |
0 commit comments