mirror of
https://github.com/GTNewHorizons/ForgeMultipart.git
synced 2026-11-12 16:37:38 +00:00
Completed ASM Mixin compiler and factory.
This commit is contained in:
1 parent
0bfc73c5f7
commit
ffa0918f4b
7 files changed
+1723
-3
No files matched your search
@@ -13,7 +13,7 @@ import scala.collection.mutable.ListBuffer
|
||||
import scala.collection.mutable.ListBuffer
|
||||
import codechicken.multipart.asm.IMultipartFactory
|
||||
import codechicken.multipart.asm.ScalaCompilerFactory
|
||||
import codechicken.multipart.asm.ASMExtensionFactory
|
||||
import codechicken.multipart.asm.ASMMixinFactory
|
||||
|
||||
object MultipartGenerator
|
||||
{
|
||||
@@ -23,7 +23,7 @@ object MultipartGenerator
|
||||
private var partTraitMap_c:Map[Class[_], Seq[String]] = Map()
|
||||
private var partTraitMap_s:Map[Class[_], Seq[String]] = Map()
|
||||
|
||||
var factory:IMultipartFactory = ASMExtensionFactory
|
||||
var factory:IMultipartFactory = ASMMixinFactory
|
||||
|
||||
def partTraitMap(client:Boolean) = if(client) partTraitMap_c else partTraitMap_s
|
||||
|
||||
|
||||
@@ -51,7 +51,11 @@ class TileMultipart extends TileEntity
|
||||
loadFrom(that)
|
||||
}
|
||||
|
||||
def copyFrom(that:TileMultipart)//Potentially auto gen this
|
||||
/**
|
||||
* This method should be used for copying all the data from the fields in that container tile.
|
||||
* This method will be automatically generated on java tile traits with fields if it is not overridden.
|
||||
*/
|
||||
def copyFrom(that:TileMultipart)
|
||||
{
|
||||
partList = that.partList
|
||||
doesTick = that.doesTick
|
||||
|
||||
@@ -0,0 +1,551 @@
|
||||
package codechicken.multipart.asm
|
||||
|
||||
import scala.collection.mutable.{Map => MMap, ListBuffer => MList, Set => MSet}
|
||||
import scala.collection.JavaConversions._
|
||||
import scala.collection.JavaConverters._
|
||||
import cpw.mods.fml.relauncher.RelaunchClassLoader
|
||||
import org.objectweb.asm.tree._
|
||||
import org.objectweb.asm.Opcodes._
|
||||
import org.objectweb.asm.ClassReader
|
||||
import org.objectweb.asm.util.TraceClassVisitor
|
||||
import org.objectweb.asm.util.Textifier
|
||||
import org.objectweb.asm.Type
|
||||
import org.objectweb.asm.MethodVisitor
|
||||
import Type._
|
||||
import codechicken.core.asm.ASMHelper._
|
||||
import codechicken.core.asm.ObfuscationMappings._
|
||||
import java.io.File
|
||||
import java.io.PrintWriter
|
||||
import ScalaSignature._
|
||||
import java.lang.reflect.Method
|
||||
import java.lang.reflect.Modifier
|
||||
|
||||
object DebugPrinter
|
||||
{
|
||||
val dir = new File("asm/multipart")
|
||||
if(!dir.exists)
|
||||
dir.mkdirs()
|
||||
for(file <- dir.listFiles)
|
||||
file.delete
|
||||
|
||||
def defined(name:String, bytes:Array[Byte])
|
||||
{
|
||||
val fileout = new File(dir, name.replace('/', '#')+".txt")
|
||||
val pout = new PrintWriter(fileout)
|
||||
|
||||
new ClassReader(bytes).accept(new TraceClassVisitor(null, new Textifier(), pout), 0)
|
||||
pout.close()
|
||||
}
|
||||
}
|
||||
|
||||
object ASMMixinCompiler
|
||||
{
|
||||
val cl = getClass.getClassLoader.asInstanceOf[RelaunchClassLoader]
|
||||
val m_defineClass = classOf[ClassLoader].getDeclaredMethod("defineClass", classOf[Array[Byte]], Integer.TYPE, Integer.TYPE)
|
||||
m_defineClass.setAccessible(true)
|
||||
|
||||
private val traitByteMap = MMap[String, Array[Byte]]()
|
||||
private val mixinMap = MMap[String, MixinInfo]()
|
||||
|
||||
def define(name:String, bytes:Array[Byte]) =
|
||||
{
|
||||
internalDefine(name, bytes)
|
||||
m_defineClass.invoke(cl, bytes, 0:Integer, bytes.length:Integer).asInstanceOf[Class[_]]
|
||||
}
|
||||
|
||||
def internalDefine(name:String, bytes:Array[Byte])
|
||||
{
|
||||
traitByteMap.put(name.replace('.', '/'), bytes)
|
||||
BaseNodeInfo.clear(name)
|
||||
DebugPrinter.defined(name, bytes)
|
||||
}
|
||||
|
||||
def classNode(name:String) = traitByteMap.getOrElseUpdate(name.replace('.', '/'), cl.getClassBytes(name.replace('/', '.'))) match {
|
||||
case null => null
|
||||
case v => createClassNode(v, ClassReader.EXPAND_FRAMES)
|
||||
}
|
||||
|
||||
def isScala(cnode:ClassNode) = ScalaSigReader.ann(cnode).isDefined
|
||||
|
||||
def isTrait(cnode:ClassNode) = ScalaSigReader.read(ScalaSigReader.ann(cnode).get).evalT[ClassSymbol](0).isTrait
|
||||
|
||||
def getMixinInfo(name:String) = mixinMap.get(name)
|
||||
|
||||
case class FieldMixin(name:String, desc:String, access:Int)
|
||||
{
|
||||
def accessName(owner:String) = if((access & ACC_PRIVATE) != 0)
|
||||
owner.replace('/', '$')+"$$"+name
|
||||
else
|
||||
name
|
||||
}
|
||||
|
||||
case class MixinInfo(name:String, parent:String, fields:Seq[FieldMixin],
|
||||
methods:Seq[MethodNode], supers:Map[String, String])
|
||||
{
|
||||
def tname = name+"$class"
|
||||
}
|
||||
|
||||
object BaseNodeInfo
|
||||
{
|
||||
private val baseNodeMap = MMap[String, BaseNodeInfo]()
|
||||
|
||||
def clear(name:String) = baseNodeMap.remove(name)
|
||||
|
||||
def getNodeInfo(name:String) = baseNodeMap.getOrElseUpdate(name, new BaseNodeInfo(name))
|
||||
|
||||
private def getNodeInfo(clazz:ClassInfoSource) = clazz match {
|
||||
case null => null
|
||||
case v => baseNodeMap.getOrElseUpdate(v.name, new BaseNodeInfo(v))
|
||||
}
|
||||
|
||||
case class MethodNodeInfo(owner:String, m:MethodInfoSource)
|
||||
{
|
||||
def name = m.name
|
||||
def desc = m.desc
|
||||
}
|
||||
|
||||
trait MethodInfoSource
|
||||
{
|
||||
def name:String
|
||||
def desc:String
|
||||
def isPrivate:Boolean
|
||||
def isAbstract:Boolean
|
||||
}
|
||||
|
||||
trait ClassInfoSource
|
||||
{
|
||||
def name:String
|
||||
def superClass:Option[BaseNodeInfo]
|
||||
def interfaces:Seq[BaseNodeInfo]
|
||||
def methods:Seq[MethodInfoSource]
|
||||
}
|
||||
|
||||
implicit def JClassInfoSource(clazz:Class[_]) = if(clazz == null) null else new JClassInfoSource(clazz)
|
||||
class JClassInfoSource(clazz:Class[_]) extends ClassInfoSource
|
||||
{
|
||||
case class JMethodInfoSource(method:Method) extends MethodInfoSource
|
||||
{
|
||||
def name = method.getName
|
||||
def desc = getType(method).getDescriptor()
|
||||
def isPrivate = Modifier.isPrivate(method.getModifiers)
|
||||
def isAbstract = Modifier.isAbstract(method.getModifiers)
|
||||
}
|
||||
|
||||
def name = clazz.getName.replace('.', '/')
|
||||
def superClass = Option(getNodeInfo(clazz.getSuperclass))
|
||||
def interfaces = clazz.getInterfaces.map(getNodeInfo(_))
|
||||
def methods = clazz.getMethods.map(JMethodInfoSource(_))
|
||||
}
|
||||
|
||||
implicit def ClassNodeInfoSource(cnode:ClassNode) = if(cnode == null) null else new ClassNodeInfoSource(cnode)
|
||||
class ClassNodeInfoSource(cnode:ClassNode) extends ClassInfoSource
|
||||
{
|
||||
case class MethodNodeInfoSource(mnode:MethodNode) extends MethodInfoSource
|
||||
{
|
||||
def name = mnode.name
|
||||
def desc = mnode.desc
|
||||
def isPrivate = (mnode.access & ACC_PRIVATE) != 0
|
||||
def isAbstract = (mnode.access & ACC_ABSTRACT) != 0
|
||||
}
|
||||
|
||||
def name = cnode.name
|
||||
def superClass = Option(getNodeInfo(cnode.superName))
|
||||
def interfaces = cnode.interfaces match {
|
||||
case null => Seq()
|
||||
case v => v.map(getNodeInfo(_))
|
||||
}
|
||||
def methods = cnode.methods.map(MethodNodeInfoSource(_))
|
||||
}
|
||||
|
||||
class ScalaNodeInfoSource(cnode:ClassNode) extends ClassNodeInfoSource(cnode)
|
||||
{
|
||||
val sig = ScalaSigReader.read(ScalaSigReader.ann(cnode).get)
|
||||
val csym = sig.evalT(0):ClassSymbol
|
||||
|
||||
override def superClass = Option(getNodeInfo(csym.jParent(sig)))
|
||||
override def interfaces = csym.jInterfaces(sig).map(getNodeInfo(_))
|
||||
}
|
||||
|
||||
def classInfo(name:String):ClassInfoSource = name match {
|
||||
case null => null
|
||||
case s => classNode(s) match {
|
||||
case null => cl.findClass(s.replace('/', '.'))
|
||||
case v if isScala(v) => new ScalaNodeInfoSource(v)
|
||||
case v => v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import BaseNodeInfo._
|
||||
class BaseNodeInfo(val clazz:ClassInfoSource)
|
||||
{
|
||||
def this(name:String) = this(classInfo(name))
|
||||
|
||||
val publicmethods = exportedMethods(clazz)//map of nameDesc to owner+method
|
||||
|
||||
def exportedMethods(o:ClassInfoSource):Map[String, MethodNodeInfo] =
|
||||
{
|
||||
val eMethods = o.methods.filter(!_.isPrivate)//defined accessable methods
|
||||
.map(m => (m.name+m.desc, MethodNodeInfo(o.name, m))).toMap
|
||||
|
||||
eMethods ++ (o.superClass++o.interfaces).flatMap(_.publicmethods)
|
||||
}
|
||||
}
|
||||
|
||||
def finishBridgeCall(mv:MethodVisitor, mvdesc:String, opcode:Int, owner:String, name:String, desc:String)
|
||||
{
|
||||
val args = getArgumentTypes(mvdesc)
|
||||
for(i <- 0 until args.length)
|
||||
mv.visitVarInsn(args(i).getOpcode(ILOAD), i+1)
|
||||
mv.visitMethodInsn(opcode, owner, name, desc)
|
||||
mv.visitInsn(getReturnType(mvdesc).getOpcode(IRETURN))
|
||||
mv.visitMaxs(args.length+1, args.length+1)
|
||||
}
|
||||
|
||||
def writeBridge(mv:MethodVisitor, mvdesc:String, opcode:Int, owner:String, name:String, desc:String)
|
||||
{
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
finishBridgeCall(mv, mvdesc, opcode, owner, name, desc)
|
||||
}
|
||||
|
||||
def writeStaticBridge(mv:MethodNode, mname:String, t:MixinInfo) =
|
||||
writeBridge(mv, mv.desc, INVOKESTATIC, t.tname, mname, staticDesc(t.name, mv.desc))
|
||||
|
||||
def mixinClasses(name:String, superClass:String, traits:Seq[String]) =
|
||||
{
|
||||
val traitInfos = traits.map(t => mixinMap(t.replace('.', '/')))
|
||||
|
||||
val cnode = new ClassNode()
|
||||
cnode.visit(V1_6, ACC_PUBLIC, name, null, superClass.replace('.', '/'), traitInfos.map(_.name).toArray[String])
|
||||
|
||||
val minit = cnode.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null)
|
||||
minit.visitVarInsn(ALOAD, 0)
|
||||
minit.visitMethodInsn(INVOKESPECIAL, cnode.superName, "<init>", "()V")
|
||||
|
||||
val prevInfos = MList[MixinInfo]()
|
||||
|
||||
traitInfos.foreach{ t =>
|
||||
minit.visitVarInsn(ALOAD, 0)
|
||||
minit.visitMethodInsn(INVOKESTATIC, t.tname, "$init$", "(L"+t.name+";)V")
|
||||
|
||||
t.fields.foreach{ f =>
|
||||
val fv = cnode.visitField(ACC_PRIVATE, f.accessName(t.name), f.desc, null, null).asInstanceOf[FieldNode]
|
||||
|
||||
var mv = cnode.visitMethod(ACC_PUBLIC, fv.name, "()"+f.desc, null, null)
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitFieldInsn(GETFIELD, name, fv.name, fv.desc)
|
||||
mv.visitInsn(getType(fv.desc).getOpcode(IRETURN))
|
||||
mv.visitMaxs(1, 1)
|
||||
|
||||
mv = cnode.visitMethod(ACC_PUBLIC, fv.name+"_$eq", "("+f.desc+")V", null, null)
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitVarInsn(getType(fv.desc).getOpcode(ILOAD), 1)
|
||||
mv.visitFieldInsn(PUTFIELD, name, fv.name, fv.desc)
|
||||
mv.visitInsn(RETURN)
|
||||
mv.visitMaxs(2, 2)
|
||||
}
|
||||
|
||||
t.supers.foreach{ s =>
|
||||
val (name, desc) = seperateDesc(s._1)
|
||||
val mv = cnode.visitMethod(ACC_PUBLIC, t.name.replace('/', '$')+"$$super$"+name, desc, null, null).asInstanceOf[MethodNode]
|
||||
|
||||
prevInfos.reverse.find(t => t.supers.contains(s._1)) match {//each super goes to the one before
|
||||
case Some(st) => writeStaticBridge(mv, name, st)
|
||||
case None => writeBridge(mv, desc, INVOKESPECIAL, s._2, name, desc)
|
||||
}
|
||||
}
|
||||
|
||||
prevInfos+=t
|
||||
}
|
||||
|
||||
val methodSigs = MSet[String]()
|
||||
traitInfos.reverse.foreach{ t => //last trait gets first pick on methods
|
||||
t.methods.foreach{ m =>
|
||||
if(!methodSigs(m.name+m.desc))
|
||||
{
|
||||
val mv = cnode.visitMethod(ACC_PUBLIC, m.name, m.desc, null, Array(m.exceptions:_*)).asInstanceOf[MethodNode]
|
||||
copy(m, mv)
|
||||
mv.instructions = new InsnList()
|
||||
mv.tryCatchBlocks.clear()
|
||||
|
||||
writeStaticBridge(mv, m.name, t)
|
||||
methodSigs+=m.name+m.desc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
minit.visitInsn(RETURN)
|
||||
minit.visitMaxs(1, 1)
|
||||
|
||||
define(cnode.name, createBytes(cnode, 0))
|
||||
}
|
||||
|
||||
def seperateDesc(nameDesc:String) = {
|
||||
val n = nameDesc.indexOf('(')
|
||||
(nameDesc.substring(0, n), nameDesc.substring(n))
|
||||
}
|
||||
|
||||
def staticDesc(owner:String, desc:String) =
|
||||
{
|
||||
val descT = getMethodType(desc)
|
||||
getMethodDescriptor(descT.getReturnType, (getType("L"+owner+";")+:descT.getArgumentTypes):_*)
|
||||
}
|
||||
|
||||
def getSuper(minsn:MethodInsnNode, stack:StackAnalyser):Option[String] =
|
||||
{
|
||||
import StackAnalyser._
|
||||
|
||||
if(minsn.getOpcode != INVOKESPECIAL) return None
|
||||
val oname = stack.owner.getInternalName
|
||||
|
||||
if(minsn.owner.equals(oname)) return None//private this
|
||||
|
||||
stack.peek(Type.getType(minsn.desc).getArgumentTypes.length) match {
|
||||
case This(o) =>
|
||||
case _ => return None//have to be invoked on this
|
||||
}
|
||||
|
||||
getSuper(minsn.name+minsn.desc, oname)
|
||||
}
|
||||
|
||||
def getSuper(nameDesc:String, cname:String) =
|
||||
getNodeInfo(cname).clazz.superClass match {
|
||||
case None => None//no super class
|
||||
case Some(o) => o.publicmethods.get(nameDesc) match {
|
||||
case None => None//no method with sig in super
|
||||
case Some(m) => if(m.m.isAbstract)
|
||||
None //abstract can't be called for supers
|
||||
else
|
||||
Some(m.owner)//congrats, that's a super
|
||||
}
|
||||
}
|
||||
|
||||
def registerJavaTrait(cnode:ClassNode)
|
||||
{
|
||||
if((cnode.access & ACC_INTERFACE) != 0)
|
||||
throw new IllegalArgumentException("Cannot register java interface "+cnode.name+" as a multipart trait. Try register passThroughInterface")
|
||||
if((cnode.access & ACC_ABSTRACT) != 0)
|
||||
throw new IllegalArgumentException("Cannot register abstract class "+cnode.name+" as a multipart trait")
|
||||
if(!cnode.innerClasses.isEmpty)
|
||||
throw new IllegalArgumentException("Inner classes are not permitted for "+cnode.name+" as a multipart trait")
|
||||
|
||||
val inode = new ClassNode()//impl node
|
||||
inode.visit(V1_6, ACC_ABSTRACT|ACC_PUBLIC, cnode.name+"$class", null, "java/lang/Object", null)
|
||||
inode.sourceFile = cnode.sourceFile
|
||||
|
||||
val fields = cnode.fields.map(f => (f.name, FieldMixin(f.name, f.desc, f.access))).toMap
|
||||
val superInfo = getNodeInfo(cnode.superName)
|
||||
val supers = MMap[String, String]()//nameDesc to super owner
|
||||
val methods = MList[MethodNode]()
|
||||
|
||||
val tnode = new ClassNode()//trait node (interface)
|
||||
tnode.visit(V1_6, ACC_INTERFACE|ACC_ABSTRACT|ACC_PUBLIC, cnode.name, null, "java/lang/Object", Array(cnode.interfaces:_*))
|
||||
|
||||
def fname(name:String) = fields(name).accessName(cnode.name)
|
||||
|
||||
fields.values.foreach{ fnode =>
|
||||
tnode.visitMethod(ACC_PUBLIC|ACC_ABSTRACT, fname(fnode.name), "()"+fnode.desc, null, null)
|
||||
tnode.visitMethod(ACC_PUBLIC|ACC_ABSTRACT, fname(fnode.name)+"_$eq", "("+fnode.desc+")V", null, null)
|
||||
}
|
||||
|
||||
def superInsn(minsn:MethodInsnNode) =
|
||||
{
|
||||
val bridgeName = cnode.name.replace('/', '$')+"$$super$"+minsn.name
|
||||
if(!supers.contains(minsn.name+minsn.desc))
|
||||
{
|
||||
tnode.visitMethod(ACC_PUBLIC|ACC_ABSTRACT, bridgeName, minsn.desc, null, null)
|
||||
supers.put(minsn.name+minsn.desc, minsn.owner)
|
||||
}
|
||||
new MethodInsnNode(INVOKEINTERFACE, cnode.name, bridgeName, minsn.desc)
|
||||
}
|
||||
|
||||
def staticClone(mnode:MethodNode, name:String, access:Int) =
|
||||
{
|
||||
val mv = inode.visitMethod(access|ACC_STATIC, name,
|
||||
staticDesc(cnode.name, mnode.desc),
|
||||
null, Array(mnode.exceptions:_*)).asInstanceOf[MethodNode]
|
||||
copy(mnode, mv)
|
||||
mv
|
||||
}
|
||||
|
||||
def staticTransform(mnode:MethodNode, base:MethodNode)
|
||||
{
|
||||
val stack = new StackAnalyser(getType(cnode.name), base)
|
||||
val insnList = mnode.instructions
|
||||
var insn = insnList.getFirst
|
||||
|
||||
def replace(newinsn:AbstractInsnNode)
|
||||
{
|
||||
insnList.insert(insn, newinsn)
|
||||
insnList.remove(insn)
|
||||
insn = newinsn
|
||||
}
|
||||
|
||||
//transform
|
||||
while(insn != null)
|
||||
{
|
||||
insn match {
|
||||
case finsn:FieldInsnNode => insn.getOpcode match
|
||||
{
|
||||
case GETFIELD => replace(new MethodInsnNode(INVOKEINTERFACE, cnode.name,
|
||||
fname(finsn.name), "()"+finsn.desc))
|
||||
case PUTFIELD => replace(new MethodInsnNode(INVOKEINTERFACE, cnode.name,
|
||||
fname(finsn.name)+"_$eq", "("+finsn.desc+")V"))
|
||||
case _ =>
|
||||
}
|
||||
case minsn:MethodInsnNode => insn.getOpcode match
|
||||
{
|
||||
case INVOKESPECIAL =>
|
||||
if(getSuper(minsn, stack).isDefined)
|
||||
replace(superInsn(minsn))
|
||||
case INVOKEVIRTUAL =>
|
||||
if(minsn.owner.equals(cnode.name))
|
||||
minsn.setOpcode(INVOKEINTERFACE)
|
||||
case _ =>
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
stack.visitInsn(insn)
|
||||
insn = insn.getNext
|
||||
}
|
||||
}
|
||||
|
||||
def convertMethod(mnode:MethodNode)
|
||||
{
|
||||
if(mnode.name.equals("<clinit>"))
|
||||
throw new IllegalArgumentException("Static initialisers are not permitted "+cnode.name+" as a multipart trait")
|
||||
|
||||
if(mnode.name.equals("<init>"))
|
||||
{
|
||||
if(!mnode.desc.equals("()V"))
|
||||
throw new IllegalArgumentException("Constructor arguments are not permitted "+cnode.name+" as a multipart trait")
|
||||
|
||||
var mv = staticClone(mnode, "$init$", ACC_PUBLIC)
|
||||
def removeSuperConstructor()
|
||||
{
|
||||
def throwI = throw new IllegalArgumentException("Invalid constructor insn sequence "+cnode.name)
|
||||
|
||||
val insnList = mv.instructions
|
||||
var insn = insnList.getFirst
|
||||
var state = 0
|
||||
while(insn != null && state < 2)
|
||||
{
|
||||
var next = insn.getNext
|
||||
insn match {
|
||||
case linsn:LabelNode =>
|
||||
case linsn:LineNumberNode =>
|
||||
case vinsn:VarInsnNode =>
|
||||
if(state == 0)
|
||||
{
|
||||
if(vinsn.getOpcode == ALOAD && vinsn.`var` == 0)
|
||||
{
|
||||
state = 1
|
||||
insnList.remove(insn)
|
||||
}
|
||||
else
|
||||
throwI
|
||||
}
|
||||
else
|
||||
throwI
|
||||
case minsn:MethodInsnNode =>
|
||||
if(state == 1)
|
||||
{
|
||||
if(minsn.getOpcode == INVOKESPECIAL && minsn.name.equals("<init>") &&
|
||||
minsn.owner.equals(cnode.superName))
|
||||
{
|
||||
insnList.remove(insn)
|
||||
state = 2
|
||||
}
|
||||
else
|
||||
throwI
|
||||
}
|
||||
else
|
||||
throwI
|
||||
case _ => throwI
|
||||
}
|
||||
insn = next
|
||||
}
|
||||
if(state != 2)
|
||||
throwI
|
||||
}
|
||||
removeSuperConstructor()
|
||||
staticTransform(mv, mnode)
|
||||
return
|
||||
}
|
||||
|
||||
if((mnode.access & ACC_PRIVATE) == 0)
|
||||
{
|
||||
val mv = tnode.visitMethod(ACC_PUBLIC|ACC_ABSTRACT, mnode.name, mnode.desc, null, Array(mnode.exceptions:_*))
|
||||
methods+=mv.asInstanceOf[MethodNode]
|
||||
}
|
||||
|
||||
//convert that method!
|
||||
val access = if((mnode.access & ACC_PRIVATE) == 0) ACC_PUBLIC else ACC_PRIVATE
|
||||
var mv = staticClone(mnode, mnode.name, access)
|
||||
staticTransform(mv, mnode)
|
||||
}
|
||||
|
||||
cnode.methods.foreach(convertMethod)
|
||||
|
||||
define(inode.name, createBytes(inode, 0))
|
||||
define(tnode.name, createBytes(tnode, 0))
|
||||
|
||||
mixinMap.put(tnode.name, MixinInfo(tnode.name, cnode.superName,
|
||||
fields.values.toSeq,
|
||||
methods.toSeq, supers.toMap))
|
||||
}
|
||||
|
||||
def registerScalaTrait(cnode:ClassNode)
|
||||
{
|
||||
for(i <- cnode.interfaces)
|
||||
if(classNode(i+"$class") != null)
|
||||
throw new IllegalArgumentException("Multipart trait "+cnode.name+" cannot extend other traits")
|
||||
|
||||
val fieldAccessors = MMap[String, MethodSymbol]()
|
||||
val fields = MList[MethodSymbol]()
|
||||
val methods = MList[MethodNode]()
|
||||
val supers = MMap[String, String]()
|
||||
|
||||
val sig = ScalaSigReader.read(ScalaSigReader.ann(cnode).get)
|
||||
val csym:ClassSymbol = sig.evalT(0)
|
||||
for(i <- 0 until sig.table.length)
|
||||
{
|
||||
import ScalaSignature._
|
||||
|
||||
val e = sig.table(i)
|
||||
if(e.id == 8)//method
|
||||
{
|
||||
val sym:MethodSymbol = sig.evalT(i)
|
||||
if(sym.isParam || !sym.owner.equals(csym)){}
|
||||
else if(sym.isAccessor)
|
||||
{
|
||||
fieldAccessors.put(sym.name, sym)
|
||||
}
|
||||
else if(sym.isMethod)
|
||||
{
|
||||
val desc = sym.jDesc(sig)
|
||||
if(sym.name.contains("super$"))
|
||||
{
|
||||
val name = sym.name.substring(6)
|
||||
supers.put(name+desc, getSuper(name+desc, cnode.name).get)
|
||||
}
|
||||
else if(!sym.name.equals("$init$") && !sym.isPrivate)
|
||||
{
|
||||
methods+=findMethod(new DescriptorMapping(cnode.name, sym.name, desc), cnode)
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fields+=sym
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mixinMap.put(cnode.name, MixinInfo(cnode.name, csym.jParent(sig),
|
||||
fields.map(sym => FieldMixin(sym.name.trim, getReturnType(sym.jDesc(sig)).getDescriptor,
|
||||
if(fieldAccessors(sym.name.trim).isPrivate) ACC_PRIVATE else ACC_PUBLIC)),
|
||||
methods, supers.toMap))
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
package codechicken.multipart.asm
|
||||
|
||||
import scala.collection.Seq
|
||||
import codechicken.multipart.TileMultipart
|
||||
import codechicken.multipart.TileMultipartClient
|
||||
import scala.collection.JavaConversions._
|
||||
import scala.collection.JavaConverters._
|
||||
import org.objectweb.asm.Opcodes._
|
||||
import org.objectweb.asm.ClassWriter
|
||||
import codechicken.core.asm.CC_ClassWriter
|
||||
import org.objectweb.asm.MethodVisitor
|
||||
import org.objectweb.asm.FieldVisitor
|
||||
import org.objectweb.asm.Label
|
||||
import cpw.mods.fml.relauncher.ReflectionHelper
|
||||
import org.objectweb.asm.commons.RemappingClassAdapter
|
||||
import org.objectweb.asm.commons.SimpleRemapper
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import org.objectweb.asm.tree._
|
||||
import org.objectweb.asm.Type
|
||||
import Type._
|
||||
import scala.collection.immutable.Range
|
||||
import codechicken.core.asm.ASMHelper._
|
||||
import codechicken.core.asm.ObfuscationMappings._
|
||||
import codechicken.multipart.MultipartGenerator
|
||||
import ASMMixinCompiler._
|
||||
|
||||
object ASMMixinFactory extends IMultipartFactory
|
||||
{
|
||||
type TMClass = Class[_ <: TileMultipart]
|
||||
|
||||
private var ugenid = 0
|
||||
private var generatorMap = Map[SuperSet, Constructor]()
|
||||
|
||||
def uniqueName(prefix:String):String = {
|
||||
val ret = prefix+"$$"+ugenid
|
||||
ugenid += 1
|
||||
return ret
|
||||
}
|
||||
|
||||
def simpleName(name:String) = name.substring(name.replace('/', '.').lastIndexOf('.')+1)
|
||||
|
||||
abstract class Constructor
|
||||
{
|
||||
def generate():TileMultipart
|
||||
}
|
||||
|
||||
object SuperSet
|
||||
{
|
||||
val TileMultipartType = classOf[TileMultipart].getName
|
||||
val TileMultipartClientType = classOf[TileMultipartClient].getName
|
||||
def apply(types:Seq[String], client:Boolean) = new SuperSet(types, client)
|
||||
}
|
||||
|
||||
class SuperSet(types:Seq[String], client:Boolean)
|
||||
{
|
||||
import SuperSet._
|
||||
val set = baseType+:types.sorted
|
||||
|
||||
def baseType = if(client) TileMultipartClientType else TileMultipartType
|
||||
|
||||
override def equals(obj:Any) = obj match
|
||||
{
|
||||
case x:SuperSet => set == x.set
|
||||
case _ => false
|
||||
}
|
||||
|
||||
override def hashCode() = set.hashCode
|
||||
|
||||
def generate():TileMultipart = get.generate
|
||||
|
||||
def get() = generatorMap.getOrElse(this, gen_sync)
|
||||
|
||||
def gen_sync():Constructor = ASMMixinFactory.synchronized
|
||||
{
|
||||
return generatorMap.getOrElse(this, {
|
||||
var gen = generator
|
||||
generatorMap = generatorMap+(this->gen)
|
||||
gen
|
||||
})
|
||||
}
|
||||
|
||||
def generator():Constructor =
|
||||
{
|
||||
val cmpClass = if(!types.isEmpty)
|
||||
mixinClasses(uniqueName("TileMultipart_cmp"), set.head, set.drop(1))
|
||||
else
|
||||
cl.loadClass(baseType)
|
||||
|
||||
MultipartGenerator.registerTileClass(cmpClass.asInstanceOf[TMClass], types.toSet)
|
||||
return constructor(cmpClass)
|
||||
}
|
||||
}
|
||||
|
||||
def constructor(clazz:Class[_]):Constructor =
|
||||
{
|
||||
val name = uniqueName("TileMultipart_gen")
|
||||
val cw = new CC_ClassWriter(ASM4)
|
||||
val superName = classOf[Constructor].getName.replace('.', '/')
|
||||
val className = clazz.getName.replace('.', '/')
|
||||
var mv:MethodVisitor = null
|
||||
|
||||
cw.visit(V1_6, ACC_PUBLIC|ACC_SUPER, name, null, superName, null)
|
||||
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null)
|
||||
mv.visitCode()
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitMethodInsn(INVOKESPECIAL, superName, "<init>", "()V")
|
||||
mv.visitInsn(RETURN)
|
||||
mv.visitMaxs(1, 1)
|
||||
mv.visitEnd()
|
||||
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "generate", "()Lcodechicken/multipart/TileMultipart;", null, null)
|
||||
mv.visitCode()
|
||||
mv.visitTypeInsn(NEW, className)
|
||||
mv.visitInsn(DUP)
|
||||
mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "()V")
|
||||
mv.visitInsn(ARETURN)
|
||||
mv.visitMaxs(2, 1)
|
||||
mv.visitEnd()
|
||||
|
||||
cw.visitEnd()
|
||||
|
||||
return define(name, cw.toByteArray).newInstance.asInstanceOf[Constructor]
|
||||
}
|
||||
|
||||
private def autoCompleteJavaTrait(cnode:ClassNode)
|
||||
{
|
||||
if(!cnode.fields.isEmpty && findMethod(new DescriptorMapping(cnode.name, "copyFrom", "(Lcodechicken/multipart/TileMultipart;)V"), cnode) == null)
|
||||
{
|
||||
val mv = cnode.visitMethod(ACC_PUBLIC, "copyFrom", "(Lcodechicken/multipart/TileMultipart;)V", null, null)
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "codechicken/multipart/TileMultipart", "copyFrom", "(Lcodechicken/multipart/TileMultipart;)V");
|
||||
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitTypeInsn(INSTANCEOF, cnode.name)
|
||||
val end = new Label()
|
||||
mv.visitJumpInsn(IFEQ, end)
|
||||
|
||||
cnode.fields.foreach{ f =>
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitFieldInsn(GETFIELD, cnode.name, f.name, f.desc)
|
||||
mv.visitFieldInsn(PUTFIELD, cnode.name, f.name, f.desc)
|
||||
}
|
||||
|
||||
mv.visitLabel(end)
|
||||
mv.visitInsn(RETURN)
|
||||
mv.visitMaxs(2, 2)
|
||||
}
|
||||
}
|
||||
|
||||
def registerTrait(s_interface:String, s_trait:String, client:Boolean)
|
||||
{
|
||||
val cnode = classNode(s_trait)
|
||||
if(cnode == null)
|
||||
throw new ClassNotFoundException(s_trait)
|
||||
|
||||
def superClass = getMixinInfo(cnode.name) match {
|
||||
case Some(m) => m.parent
|
||||
case None => BaseNodeInfo.getNodeInfo(cnode.name).clazz.superClass.get.clazz.name
|
||||
}
|
||||
|
||||
superClass match {
|
||||
case "codechicken/multipart/TileMultipartClient" => if(!client)
|
||||
throw new IllegalArgumentException("Multipart trait "+s_trait+" cannot implement TileMultipartClient on the server")
|
||||
case "codechicken/multipart/TileMultipart" =>
|
||||
case _ => throw new IllegalArgumentException("Multipart trait "+s_trait+" must implement TileMultipart or TileMultipartClient")
|
||||
}
|
||||
|
||||
if(getMixinInfo(cnode.name).isDefined)
|
||||
return
|
||||
|
||||
if(isScala(cnode) && isTrait(cnode))
|
||||
{
|
||||
registerScalaTrait(cnode)
|
||||
}
|
||||
else
|
||||
{
|
||||
autoCompleteJavaTrait(cnode)
|
||||
registerJavaTrait(cnode)
|
||||
}
|
||||
}
|
||||
|
||||
def generatePassThroughTrait(s_interface:String):String =
|
||||
{
|
||||
def passThroughTraitName(iName:String) =
|
||||
"T" + (if(iName.startsWith("I")) iName.substring(1) else iName)
|
||||
|
||||
val tname = uniqueName(passThroughTraitName(simpleName(s_interface)))
|
||||
val vname = "impl"
|
||||
val iname = s_interface.replace('.', '/')
|
||||
val idesc = "L"+iname+";"
|
||||
|
||||
val inode = classNode(s_interface)
|
||||
if((inode.access&ACC_INTERFACE) == 0) throw new IllegalArgumentException(s_interface+" is not an interface.")
|
||||
|
||||
val cw = new CC_ClassWriter(ASM4)
|
||||
var mv:MethodVisitor = null
|
||||
var fv:FieldVisitor = null
|
||||
|
||||
cw.visit(V1_6, ACC_PUBLIC|ACC_SUPER, tname, null, "codechicken/multipart/TileMultipart", Array(iname))
|
||||
|
||||
{
|
||||
fv = cw.visitField(ACC_PRIVATE, vname, idesc, null, null)
|
||||
fv.visitEnd()
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null)
|
||||
mv.visitCode()
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "codechicken/multipart/TileMultipart", "<init>", "()V")
|
||||
mv.visitInsn(RETURN)
|
||||
mv.visitMaxs(1, 1)
|
||||
mv.visitEnd()
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "bindPart", "(Lcodechicken/multipart/TMultiPart;)V", null, null)
|
||||
mv.visitCode()
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "codechicken/multipart/TileMultipart", "bindPart", "(Lcodechicken/multipart/TMultiPart;)V")
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitTypeInsn(INSTANCEOF, iname)
|
||||
val l2 = new Label()
|
||||
mv.visitJumpInsn(IFEQ, l2)
|
||||
val l3 = new Label()
|
||||
mv.visitLabel(l3)
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitTypeInsn(CHECKCAST, iname)
|
||||
mv.visitFieldInsn(PUTFIELD, tname, vname, idesc)
|
||||
mv.visitLabel(l2)
|
||||
mv.visitFrame(F_SAME, 0, null, 0, null)
|
||||
mv.visitInsn(RETURN)
|
||||
mv.visitMaxs(2, 2)
|
||||
mv.visitEnd()
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "partRemoved", "(Lcodechicken/multipart/TMultiPart;I)V", null, null)
|
||||
mv.visitCode()
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitVarInsn(ILOAD, 2)
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "codechicken/multipart/TileMultipart", "partRemoved", "(Lcodechicken/multipart/TMultiPart;I)V")
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitFieldInsn(GETFIELD, tname, vname, idesc)
|
||||
val l2 = new Label()
|
||||
mv.visitJumpInsn(IF_ACMPNE, l2)
|
||||
val l3 = new Label()
|
||||
mv.visitLabel(l3)
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitInsn(ACONST_NULL)
|
||||
mv.visitFieldInsn(PUTFIELD, tname, vname, idesc)
|
||||
mv.visitLabel(l2)
|
||||
mv.visitFrame(F_SAME, 0, null, 0, null)
|
||||
mv.visitInsn(RETURN)
|
||||
mv.visitMaxs(3, 3)
|
||||
mv.visitEnd()
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "canAddPart", "(Lcodechicken/multipart/TMultiPart;)Z", null, null)
|
||||
mv.visitCode()
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitFieldInsn(GETFIELD, tname, vname, idesc)
|
||||
val l1 = new Label()
|
||||
mv.visitJumpInsn(IFNULL, l1)
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitTypeInsn(INSTANCEOF, iname)
|
||||
mv.visitJumpInsn(IFEQ, l1)
|
||||
val l2 = new Label()
|
||||
mv.visitLabel(l2)
|
||||
mv.visitInsn(ICONST_0)
|
||||
mv.visitInsn(IRETURN)
|
||||
mv.visitLabel(l1)
|
||||
mv.visitFrame(F_SAME, 0, null, 0, null)
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "codechicken/multipart/TileMultipart", "canAddPart", "(Lcodechicken/multipart/TMultiPart;)Z")
|
||||
mv.visitInsn(IRETURN)
|
||||
mv.visitMaxs(2, 2)
|
||||
mv.visitEnd()
|
||||
}
|
||||
|
||||
def methods(cnode:ClassNode):Seq[MethodNode] =
|
||||
{
|
||||
val m = cnode.methods
|
||||
if(cnode.interfaces != null)
|
||||
m++=cnode.interfaces.flatMap(i => methods(classNode(i)))
|
||||
m
|
||||
}
|
||||
|
||||
def generatePassThroughMethod(m:MethodNode)
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC, m.name, m.desc, m.signature, Array(m.exceptions:_*))
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitFieldInsn(GETFIELD, tname, vname, idesc)
|
||||
finishBridgeCall(mv, m.desc, INVOKEINTERFACE, iname, m.name, m.desc)
|
||||
}
|
||||
|
||||
methods(inode).foreach(generatePassThroughMethod(_))
|
||||
|
||||
cw.visitEnd()
|
||||
internalDefine(tname, cw.toByteArray)
|
||||
registerTrait(s_interface, tname, false)
|
||||
return tname
|
||||
}
|
||||
|
||||
def generateTile(types:Seq[String], client:Boolean) = SuperSet(types, client).generate
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
/* __ *\
|
||||
** ________ ___ / / ___ Scala API **
|
||||
** / __/ __// _ | / / / _ | (c) 2007-2013, LAMP/EPFL **
|
||||
** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
|
||||
** /____/\___/_/ |_/____/_/ | | **
|
||||
** |/ **
|
||||
\* */
|
||||
package codechicken.multipart.asm
|
||||
|
||||
object ByteCodecs {
|
||||
|
||||
def avoidZero(src: Array[Byte]): Array[Byte] = {
|
||||
var i = 0
|
||||
val srclen = src.length
|
||||
var count = 0
|
||||
while (i < srclen) {
|
||||
if (src(i) == 0x7f) count += 1
|
||||
i += 1
|
||||
}
|
||||
val dst = new Array[Byte](srclen + count)
|
||||
i = 0
|
||||
var j = 0
|
||||
while (i < srclen) {
|
||||
val in = src(i)
|
||||
if (in == 0x7f) {
|
||||
dst(j) = (0xc0).toByte
|
||||
dst(j + 1) = (0x80).toByte
|
||||
j += 2
|
||||
} else {
|
||||
dst(j) = (in + 1).toByte
|
||||
j += 1
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
dst
|
||||
}
|
||||
|
||||
def regenerateZero(src: Array[Byte]): Int = {
|
||||
var i = 0
|
||||
val srclen = src.length
|
||||
var j = 0
|
||||
while (i < srclen) {
|
||||
val in: Int = src(i) & 0xff
|
||||
if (in == 0xc0 && (src(i + 1) & 0xff) == 0x80) {
|
||||
src(j) = 0x7f
|
||||
i += 2
|
||||
} else if (in == 0) {
|
||||
src(j) = 0x7f
|
||||
i += 1
|
||||
} else {
|
||||
src(j) = (in - 1).toByte
|
||||
i += 1
|
||||
}
|
||||
j += 1
|
||||
}
|
||||
j
|
||||
}
|
||||
|
||||
def encode8to7(src: Array[Byte]): Array[Byte] = {
|
||||
val srclen = src.length
|
||||
val dstlen = (srclen * 8 + 6) / 7
|
||||
val dst = new Array[Byte](dstlen)
|
||||
var i = 0
|
||||
var j = 0
|
||||
while (i + 6 < srclen) {
|
||||
var in: Int = src(i) & 0xff
|
||||
dst(j) = (in & 0x7f).toByte
|
||||
var out: Int = in >>> 7
|
||||
in = src(i + 1) & 0xff
|
||||
dst(j + 1) = (out | (in << 1) & 0x7f).toByte
|
||||
out = in >>> 6
|
||||
in = src(i + 2) & 0xff
|
||||
dst(j + 2) = (out | (in << 2) & 0x7f).toByte
|
||||
out = in >>> 5
|
||||
in = src(i + 3) & 0xff
|
||||
dst(j + 3) = (out | (in << 3) & 0x7f).toByte
|
||||
out = in >>> 4
|
||||
in = src(i + 4) & 0xff
|
||||
dst(j + 4) = (out | (in << 4) & 0x7f).toByte
|
||||
out = in >>> 3
|
||||
in = src(i + 5) & 0xff
|
||||
dst(j + 5) = (out | (in << 5) & 0x7f).toByte
|
||||
out = in >>> 2
|
||||
in = src(i + 6) & 0xff
|
||||
dst(j + 6) = (out | (in << 6) & 0x7f).toByte
|
||||
out = in >>> 1
|
||||
dst(j + 7) = out.toByte
|
||||
i += 7
|
||||
j += 8
|
||||
}
|
||||
if (i < srclen) {
|
||||
var in: Int = src(i) & 0xff
|
||||
dst(j) = (in & 0x7f).toByte; j += 1
|
||||
var out: Int = in >>> 7
|
||||
if (i + 1 < srclen) {
|
||||
in = src(i + 1) & 0xff
|
||||
dst(j) = (out | (in << 1) & 0x7f).toByte; j += 1
|
||||
out = in >>> 6
|
||||
if (i + 2 < srclen) {
|
||||
in = src(i + 2) & 0xff
|
||||
dst(j) = (out | (in << 2) & 0x7f).toByte; j += 1
|
||||
out = in >>> 5
|
||||
if (i + 3 < srclen) {
|
||||
in = src(i + 3) & 0xff
|
||||
dst(j) = (out | (in << 3) & 0x7f).toByte; j += 1
|
||||
out = in >>> 4
|
||||
if (i + 4 < srclen) {
|
||||
in = src(i + 4) & 0xff
|
||||
dst(j) = (out | (in << 4) & 0x7f).toByte; j += 1
|
||||
out = in >>> 3
|
||||
if (i + 5 < srclen) {
|
||||
in = src(i + 5) & 0xff
|
||||
dst(j) = (out | (in << 5) & 0x7f).toByte; j += 1
|
||||
out = in >>> 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (j < dstlen) dst(j) = out.toByte
|
||||
}
|
||||
dst
|
||||
}
|
||||
|
||||
def decode7to8(src: Array[Byte], srclen: Int): Int = {
|
||||
var i = 0
|
||||
var j = 0
|
||||
val dstlen = (srclen * 7 + 7) / 8
|
||||
while (i + 7 < srclen) {
|
||||
var out: Int = src(i).toInt
|
||||
var in: Byte = src(i + 1)
|
||||
src(j) = (out | (in & 0x01) << 7).toByte
|
||||
out = in >>> 1
|
||||
in = src(i + 2)
|
||||
src(j + 1) = (out | (in & 0x03) << 6).toByte
|
||||
out = in >>> 2
|
||||
in = src(i + 3)
|
||||
src(j + 2) = (out | (in & 0x07) << 5).toByte
|
||||
out = in >>> 3
|
||||
in = src(i + 4)
|
||||
src(j + 3) = (out | (in & 0x0f) << 4).toByte
|
||||
out = in >>> 4
|
||||
in = src(i + 5)
|
||||
src(j + 4) = (out | (in & 0x1f) << 3).toByte
|
||||
out = in >>> 5
|
||||
in = src(i + 6)
|
||||
src(j + 5) = (out | (in & 0x3f) << 2).toByte
|
||||
out = in >>> 6
|
||||
in = src(i + 7)
|
||||
src(j + 6) = (out | in << 1).toByte
|
||||
i += 8
|
||||
j += 7
|
||||
}
|
||||
if (i < srclen) {
|
||||
var out: Int = src(i).toInt
|
||||
if (i + 1 < srclen) {
|
||||
var in: Byte = src(i + 1)
|
||||
src(j) = (out | (in & 0x01) << 7).toByte; j += 1
|
||||
out = in >>> 1
|
||||
if (i + 2 < srclen) {
|
||||
in = src(i + 2)
|
||||
src(j) = (out | (in & 0x03) << 6).toByte; j += 1
|
||||
out = in >>> 2
|
||||
if (i + 3 < srclen) {
|
||||
in = src(i + 3)
|
||||
src(j) = (out | (in & 0x07) << 5).toByte; j += 1
|
||||
out = in >>> 3
|
||||
if (i + 4 < srclen) {
|
||||
in = src(i + 4)
|
||||
src(j) = (out | (in & 0x0f) << 4).toByte; j += 1
|
||||
out = in >>> 4
|
||||
if (i + 5 < srclen) {
|
||||
in = src(i + 5)
|
||||
src(j) = (out | (in & 0x1f) << 3).toByte; j += 1
|
||||
out = in >>> 5
|
||||
if (i + 6 < srclen) {
|
||||
in = src(i + 6)
|
||||
src(j) = (out | (in & 0x3f) << 2).toByte; j += 1
|
||||
out = in >>> 6
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (j < dstlen) src(j) = out.toByte
|
||||
}
|
||||
dstlen
|
||||
}
|
||||
|
||||
def encode(xs: Array[Byte]): Array[Byte] = avoidZero(encode8to7(xs))
|
||||
|
||||
/**
|
||||
* Destructively decodes array xs and returns the length of the decoded array.
|
||||
*
|
||||
* Sometimes returns (length+1) of the decoded array. Example:
|
||||
*
|
||||
* scala> val enc = scala.reflect.generic.ByteCodecs.encode(Array(1,2,3))
|
||||
* enc: Array[Byte] = Array(2, 5, 13, 1)
|
||||
*
|
||||
* scala> scala.reflect.generic.ByteCodecs.decode(enc)
|
||||
* res43: Int = 4
|
||||
*
|
||||
* scala> enc
|
||||
* res44: Array[Byte] = Array(1, 2, 3, 0)
|
||||
*
|
||||
* However, this does not always happen.
|
||||
*/
|
||||
def decode(xs: Array[Byte]): Int = {
|
||||
val len = regenerateZero(xs)
|
||||
decode7to8(xs, len)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
package codechicken.multipart.asm
|
||||
|
||||
import org.objectweb.asm.tree.ClassNode
|
||||
import org.objectweb.asm.tree.AnnotationNode
|
||||
import org.objectweb.asm.tree.FieldNode
|
||||
import org.objectweb.asm.tree.MethodNode
|
||||
import java.util.{List => JList}
|
||||
import scala.collection.mutable.{ListBuffer => MList}
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.collection.JavaConversions._
|
||||
|
||||
import ScalaSignature._
|
||||
|
||||
object ScalaSignature
|
||||
{
|
||||
object Bytes
|
||||
{
|
||||
implicit def reader(bc:Bytes) = new ByteCodeReader(bc)
|
||||
|
||||
def apply(bytes:Array[Byte], pos:Int, len:Int) = new Bytes(bytes, pos, len)
|
||||
def apply(bytes:Array[Byte]):Bytes = apply(bytes, 0, bytes.length)
|
||||
}
|
||||
|
||||
class Bytes(val bytes:Array[Byte], val pos:Int, val len:Int)
|
||||
{
|
||||
def section() = bytes take pos drop len
|
||||
}
|
||||
|
||||
trait Flags
|
||||
{
|
||||
def hasFlag(flag:Int):Boolean
|
||||
|
||||
def isProtected = hasFlag(0x00000001)
|
||||
def isPrivate = hasFlag(0x00000004)
|
||||
def isAbstract = hasFlag(0x00000008)
|
||||
def isInterface = hasFlag(0x00000080)
|
||||
def isMethod = hasFlag(0x00000200)
|
||||
def isParam = hasFlag(0x00002000)
|
||||
def isStatic = hasFlag(0x00800000)
|
||||
def isTrait = hasFlag(0x02000000)
|
||||
def isAccessor = hasFlag(0x08000000)
|
||||
}
|
||||
|
||||
trait SymbolRef extends Flags
|
||||
{
|
||||
def full:String
|
||||
def flags:Int
|
||||
def hasFlag(flag:Int) = (flags&flag) != 0
|
||||
}
|
||||
|
||||
case class ClassSymbol(name:String, owner:SymbolRef, flags:Int, info:Int) extends SymbolRef
|
||||
{
|
||||
override def toString = "ClassSymbol("+name+","+owner+","+flags.toHexString+","+info+")"
|
||||
def full = owner.full+"."+name
|
||||
|
||||
def info(sig:ScalaSignature):ClassType = sig.evalT(info)
|
||||
def jParent(sig:ScalaSignature) = info(sig).parent.jName
|
||||
def jInterfaces(sig:ScalaSignature) = info(sig).interfaces.map(_.jName)
|
||||
}
|
||||
|
||||
case class MethodSymbol(name:String, owner:SymbolRef, flags:Int, info:Int) extends SymbolRef
|
||||
{
|
||||
override def toString = "MethodSymbol("+name+","+owner+","+flags.toHexString+","+info+")"
|
||||
def full = owner.full+"."+name
|
||||
|
||||
def info(sig:ScalaSignature):SMethodType = sig.evalT(info)
|
||||
def jDesc(sig:ScalaSignature):String = info(sig).jDesc(sig)
|
||||
}
|
||||
|
||||
case class ExternalSymbol(name:String) extends SymbolRef
|
||||
{
|
||||
override def toString = name
|
||||
def full = name
|
||||
def flags = 0
|
||||
}
|
||||
|
||||
case object NoSymbol extends SymbolRef
|
||||
{
|
||||
def full = "<no symbol>"
|
||||
def flags = 0
|
||||
}
|
||||
|
||||
trait SMethodType
|
||||
{
|
||||
def jDesc(sig:ScalaSignature):String = "("+params.map(m => m.info(sig).returnType.jDesc).mkString+")"+returnType.jDesc
|
||||
def returnType:TypeRef
|
||||
def params:List[MethodSymbol]
|
||||
}
|
||||
|
||||
case class ClassType(owner:SymbolRef, parents:List[TypeRef])
|
||||
{
|
||||
def parent = parents.head
|
||||
def interfaces = parents.drop(1)
|
||||
}
|
||||
|
||||
case class MethodType(returnType:TypeRef, params:List[MethodSymbol]) extends SMethodType
|
||||
|
||||
case class ParameterlessType(returnType:TypeRef) extends SMethodType
|
||||
{
|
||||
def params = List()
|
||||
}
|
||||
|
||||
trait TypeRef
|
||||
{
|
||||
def name:String
|
||||
def jName = name.replace('.', '/') match {
|
||||
case "scala/AnyRef" => "java/lang/Object"
|
||||
case s => s
|
||||
}
|
||||
def jDesc:String = name match
|
||||
{
|
||||
case "scala.Array" => null
|
||||
case "scala.Long" => "J"
|
||||
case "scala.Int" => "I"
|
||||
case "scala.Short" => "S"
|
||||
case "scala.Byte" => "B"
|
||||
case "scala.Double" => "D"
|
||||
case "scala.Float" => "F"
|
||||
case "scala.Boolean" => "Z"
|
||||
case "scala.Unit" => "V"
|
||||
case _ => "L"+jName+";"
|
||||
}
|
||||
}
|
||||
|
||||
case class TypeRefType(owner:TypeRef, sym:SymbolRef, typArgs:List[TypeRef]) extends TypeRef with SMethodType
|
||||
{
|
||||
def params = List()
|
||||
|
||||
def returnType = this
|
||||
|
||||
def name = sym.full
|
||||
|
||||
override def jDesc = name match
|
||||
{
|
||||
case "scala.Array" => "["+typArgs(0).jDesc
|
||||
case _ => super.jDesc
|
||||
}
|
||||
}
|
||||
|
||||
case class ThisType(sym:SymbolRef) extends TypeRef
|
||||
{
|
||||
def name = sym.full
|
||||
}
|
||||
|
||||
case class SingleType(owner:TypeRef, sym:SymbolRef) extends TypeRef
|
||||
{
|
||||
def name = sym.full
|
||||
}
|
||||
|
||||
case object NoType extends TypeRef
|
||||
{
|
||||
def name = "<no type>"
|
||||
}
|
||||
|
||||
class SigEntry(val start:Int, val bytes:Bytes)
|
||||
{
|
||||
def id() = bytes.bytes(start)
|
||||
|
||||
def delete()
|
||||
{
|
||||
bytes.bytes(start) = 3
|
||||
}
|
||||
}
|
||||
}
|
||||
case class ScalaSignature(major:Int, minor:Int, table:Array[SigEntry], bytes:Bytes)
|
||||
{
|
||||
def evalS(i:Int):String = {
|
||||
val e = table(i)
|
||||
val bc = e.bytes
|
||||
val bcr = bc:ByteCodeReader
|
||||
return e.id match
|
||||
{
|
||||
case 1|2 => bcr.readString(bc.len)
|
||||
case 3 => NoSymbol.full
|
||||
case 9|10 => {
|
||||
var s = evalS(bcr.readNat)
|
||||
if(bc.pos+bc.len > bcr.pos)
|
||||
s = evalS(bcr.readNat)+"."+s
|
||||
s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def evalT[T](i:Int):T = eval(i).asInstanceOf[T]
|
||||
|
||||
def evalList[T](bcr:ByteCodeReader) =
|
||||
{
|
||||
var l = MList[T]()
|
||||
while(bcr.more)
|
||||
l+=evalT(bcr.readNat)
|
||||
l.toList
|
||||
}
|
||||
|
||||
def eval(i:Int):Any = {//we only parse the ones we actually care about
|
||||
val e = table(i)
|
||||
val bc = e.bytes
|
||||
val bcr = bc:ByteCodeReader
|
||||
return e.id match
|
||||
{
|
||||
case 1|2 => evalS(i)
|
||||
case 6 => ClassSymbol(evalS(bcr.readNat), evalT(bcr.readNat), bcr.readNat, bcr.readNat)
|
||||
case 8 => MethodSymbol(evalS(bcr.readNat), evalT(bcr.readNat), bcr.readNat, bcr.readNat)
|
||||
case 9|10 => ExternalSymbol(evalS(i))
|
||||
case 11|12 => NoType //12 is actually NoPrefixType (no lower bound)
|
||||
case 13 => ThisType(evalT(bcr.readNat))
|
||||
case 14 => SingleType(evalT(bcr.readNat), evalT(bcr.readNat))
|
||||
case 16 => TypeRefType(evalT(bcr.readNat), evalT(bcr.readNat), evalList(bcr))
|
||||
case 19 => ClassType(evalT(bcr.readNat), evalList(bcr))
|
||||
case 20 => MethodType(evalT(bcr.readNat), evalList(bcr))
|
||||
case 21|48 => ParameterlessType(evalT(bcr.readNat))//48 is actually a bounded super type, but it should work fine for our purposes
|
||||
case _ => NoSymbol
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ByteCodeReader(val bc:Bytes)
|
||||
{
|
||||
var pos = bc.pos
|
||||
|
||||
def more() = pos < bc.pos+bc.len
|
||||
|
||||
def readString(len:Int) = advance(len)(new String(bc.bytes drop pos take len))
|
||||
|
||||
def readByte() = advance(1)(bc.bytes(pos))
|
||||
|
||||
def readNat():Int =
|
||||
{
|
||||
var r = 0
|
||||
var b = 0
|
||||
do
|
||||
{
|
||||
b = readByte
|
||||
r = r<<7|b&0x7F
|
||||
}
|
||||
while((b&0x80) != 0)
|
||||
return r
|
||||
}
|
||||
|
||||
def advance[A](len:Int)(r:A):A =
|
||||
{
|
||||
if(pos+len > bc.pos+bc.len)
|
||||
throw new IllegalArgumentException("Ran off the end of bytecode")
|
||||
pos+=len
|
||||
return r
|
||||
}
|
||||
|
||||
def readEntry() =
|
||||
{
|
||||
val p = pos
|
||||
val tpe:Int = readByte
|
||||
val len = readNat
|
||||
advance(len)(new SigEntry(p, new Bytes(bc.bytes, pos, len)))
|
||||
}
|
||||
|
||||
def readSig() =
|
||||
{
|
||||
val major = readByte
|
||||
val minor = readByte
|
||||
val table = new Array[SigEntry](readNat)
|
||||
for(i <- 0 until table.size)
|
||||
table(i) = readEntry
|
||||
ScalaSignature(major, minor, table, bc)
|
||||
}
|
||||
}
|
||||
|
||||
object ScalaSigReader
|
||||
{
|
||||
def decode(s:String):Array[Byte] =
|
||||
{
|
||||
val bytes = s.getBytes
|
||||
return bytes take ByteCodecs.decode(bytes)
|
||||
}
|
||||
|
||||
def encode(b:Array[Byte]):String =
|
||||
{
|
||||
val bytes = ByteCodecs.encode8to7(b)
|
||||
var i = 0
|
||||
while(i < bytes.length)
|
||||
{
|
||||
bytes(i) = ((bytes(i)+1)&0x7F).toByte
|
||||
i+=1
|
||||
}
|
||||
return new String(bytes.take(bytes.length-1), "UTF-8")
|
||||
}
|
||||
|
||||
def read(ann:AnnotationNode):ScalaSignature = Bytes(decode(ann.values.get(1).asInstanceOf[String])).readSig
|
||||
|
||||
def write(sig:ScalaSignature, ann:AnnotationNode) = ann.values.set(1, encode(sig.bytes.bytes))
|
||||
|
||||
def ann(cnode:ClassNode):Option[AnnotationNode] = cnode.visibleAnnotations match {
|
||||
case null => None
|
||||
case a => a.find(ann => ann.desc.equals("Lscala/reflect/ScalaSignature;"))
|
||||
}
|
||||
}
|
||||
|
||||
class ScalaSigSideTransformer
|
||||
{
|
||||
def transform(ann:AnnotationNode, cnode:ClassNode, removedFields:JList[FieldNode], removedMethods:JList[MethodNode])
|
||||
{
|
||||
if(removedFields.isEmpty && removedMethods.isEmpty)
|
||||
return
|
||||
|
||||
val remFields = removedFields.asScala.map(f => (f.name, f.desc.replace('$', '/')))
|
||||
val remMethods = removedMethods.asScala.map(f => (f.name, f.desc.replace('$', '/')))
|
||||
|
||||
val sig = ScalaSigReader.read(ann)
|
||||
for(i <- 0 until sig.table.length)
|
||||
{
|
||||
val e = sig.table(i)
|
||||
if(e.id == 8)//check and remove
|
||||
{
|
||||
val sym:MethodSymbol = sig.evalT(i)
|
||||
if(sym.isAccessor)
|
||||
{
|
||||
val fName = if(sym.name.endsWith("_$eq")) sym.name.substring(0, sym.name.length-4) else sym.name
|
||||
if(remFields.find(t => t._1 == sym.name.trim).nonEmpty)
|
||||
{
|
||||
e.delete()
|
||||
val it = cnode.methods.iterator
|
||||
while(it.hasNext)
|
||||
{
|
||||
val m = it.next
|
||||
if(m.name == sym.name && m.desc == sym.jDesc(sig))
|
||||
it.remove
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(sym.isMethod)
|
||||
{
|
||||
if(remMethods.find(t => t._1 == sym.name && t._2 == sym.jDesc(sig)).nonEmpty)
|
||||
e.delete()
|
||||
}
|
||||
else//field
|
||||
{
|
||||
if(remFields.find(t => t._1 == sym.name.trim).nonEmpty)
|
||||
e.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
ScalaSigReader.write(sig, ann)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
package codechicken.multipart.asm
|
||||
|
||||
import scala.collection.mutable.{Map => MMap, ListBuffer => MList}
|
||||
import org.objectweb.asm.tree._
|
||||
import org.objectweb.asm.Opcodes._
|
||||
import org.objectweb.asm.Type
|
||||
import org.objectweb.asm.Type._
|
||||
import scala.collection.JavaConversions._
|
||||
|
||||
object StackAnalyser
|
||||
{
|
||||
trait StackEntry
|
||||
{
|
||||
def getType:Type
|
||||
}
|
||||
|
||||
case class This(owner:Type) extends StackEntry
|
||||
{
|
||||
def getType = owner
|
||||
}
|
||||
case class Param(i:Int, t:Type) extends StackEntry
|
||||
{
|
||||
def getType = t
|
||||
}
|
||||
case class Const(c:Any) extends StackEntry
|
||||
{
|
||||
def getType = c match {
|
||||
case o:Byte => BYTE_TYPE
|
||||
case o:Short => SHORT_TYPE
|
||||
case o:Int => INT_TYPE
|
||||
case o:Long => LONG_TYPE
|
||||
case o:Float => FLOAT_TYPE
|
||||
case o:Double => DOUBLE_TYPE
|
||||
case o:Char => CHAR_TYPE
|
||||
case o:Boolean => BOOLEAN_TYPE
|
||||
case o:String => getObjectType("java/lang/String")
|
||||
case null => getObjectType("java/lang/Object")
|
||||
case _ => throw new IllegalArgumentException("Unknown const "+c)
|
||||
}
|
||||
}
|
||||
case class UnaryOp(op:Int, e:StackEntry) extends StackEntry
|
||||
{
|
||||
def getType = e.getType
|
||||
}
|
||||
case class BinaryOp(op:Int, e2:StackEntry, e1:StackEntry) extends StackEntry
|
||||
{
|
||||
def getType = e1.getType
|
||||
}
|
||||
case class PrimitiveCast(e:StackEntry, t:Type) extends StackEntry
|
||||
{
|
||||
def getType = t
|
||||
}
|
||||
case class ReturnAddress() extends StackEntry
|
||||
{
|
||||
def getType = INT_TYPE
|
||||
}
|
||||
case class GetField(obj:StackEntry, field:FieldInsnNode) extends StackEntry
|
||||
{
|
||||
def getType = Type.getType(field.desc)
|
||||
}
|
||||
case class Invoke(op:Int, params:Array[StackEntry], obj:StackEntry, method:MethodInsnNode) extends StackEntry
|
||||
{
|
||||
def getType = Type.getMethodType(method.desc).getReturnType
|
||||
}
|
||||
case class New(t:Type) extends StackEntry
|
||||
{
|
||||
def getType = t
|
||||
}
|
||||
case class NewArray(len:StackEntry, t:Type) extends StackEntry
|
||||
{
|
||||
def getType = t
|
||||
}
|
||||
case class ArrayLength(array:StackEntry) extends StackEntry
|
||||
{
|
||||
def getType = INT_TYPE
|
||||
}
|
||||
case class ArrayLoad(index:StackEntry, e:StackEntry) extends StackEntry
|
||||
{
|
||||
def getType = e.getType.getElementType
|
||||
}
|
||||
case class Cast(obj:StackEntry, t:Type) extends StackEntry
|
||||
{
|
||||
def getType = t
|
||||
}
|
||||
case class NewMultiArray(sizes:Array[StackEntry], t:Type) extends StackEntry
|
||||
{
|
||||
def getType = t
|
||||
}
|
||||
case class CaughtException(t:Type) extends StackEntry
|
||||
{
|
||||
def getType = t
|
||||
}
|
||||
}
|
||||
|
||||
class StackAnalyser(val owner:Type, val m:MethodNode)
|
||||
{
|
||||
import StackAnalyser._
|
||||
|
||||
val stack = MList[StackEntry]()
|
||||
val locals = MList[StackEntry]()
|
||||
private val catchHandlers = MMap[LabelNode, TryCatchBlockNode]()
|
||||
|
||||
{
|
||||
if((m.access & ACC_STATIC) == 0)
|
||||
pushL(This(owner))
|
||||
|
||||
val ptypes = getArgumentTypes(m.desc)
|
||||
for(i <- 0 until ptypes.length)
|
||||
pushL(Param(i, ptypes(i)))
|
||||
|
||||
m.tryCatchBlocks.foreach(b => catchHandlers.put(b.handler, b))
|
||||
}
|
||||
|
||||
def pushL(entry:StackEntry) = setL(locals.size, entry)
|
||||
|
||||
def setL(i:Int, entry:StackEntry) =
|
||||
{
|
||||
while(i+entry.getType.getSize > locals.size) locals += null
|
||||
locals(i) = (entry)
|
||||
if(entry.getType.getSize == 2)
|
||||
locals(i+1) = entry
|
||||
}
|
||||
|
||||
def push(entry:StackEntry) = insert(0, entry)
|
||||
|
||||
def pop(i:Int = 0) =
|
||||
{
|
||||
val e = stack.remove(stack.size-i-1)
|
||||
if(e.getType.getSize == 2)
|
||||
{
|
||||
if(peek(i) != e) throw new IllegalStateException("Wide stack entry elems don't match ("+e+","+peek(i))
|
||||
stack.remove(i)
|
||||
}
|
||||
e
|
||||
}
|
||||
|
||||
def peek(i:Int = 0) = stack(stack.size-i-1)
|
||||
|
||||
def insert(i:Int, entry:StackEntry)
|
||||
{
|
||||
if(entry.getType.getSize == 0)
|
||||
return
|
||||
stack.insert(stack.size-i, entry)
|
||||
if(entry.getType.getSize == 2)
|
||||
stack.insert(stack.size-i, entry)
|
||||
}
|
||||
|
||||
def popArgs(desc:String) =
|
||||
{
|
||||
val t = getType(desc)
|
||||
val args = new Array[StackEntry](t.getArgumentTypes.length)
|
||||
for(i <- 0 until args.length)
|
||||
args(args.length-i-1) = pop()
|
||||
args
|
||||
}
|
||||
|
||||
def visitInsn(ainsn:AbstractInsnNode)
|
||||
{
|
||||
ainsn match {
|
||||
case insn:InsnNode => ainsn.getOpcode match
|
||||
{
|
||||
case ACONST_NULL => push(Const(null))
|
||||
case ICONST_M1 => push(Const(-1))
|
||||
case ICONST_0 => push(Const(0))
|
||||
case ICONST_1 => push(Const(1))
|
||||
case ICONST_2 => push(Const(2))
|
||||
case ICONST_3 => push(Const(3))
|
||||
case ICONST_4 => push(Const(4))
|
||||
case ICONST_5 => push(Const(5))
|
||||
case LCONST_0 => push(Const(0L))
|
||||
case LCONST_1 => push(Const(1L))
|
||||
case FCONST_0 => push(Const(0F))
|
||||
case FCONST_1 => push(Const(1F))
|
||||
case FCONST_2 => push(Const(2F))
|
||||
case DCONST_0 => push(Const(0D))
|
||||
case DCONST_1 => push(Const(1D))
|
||||
|
||||
case i if(i >= IALOAD && i <= SALOAD) =>
|
||||
push(ArrayLoad(pop(), pop()))
|
||||
case i if(i >= IASTORE && i <= SASTORE) =>
|
||||
pop(); pop(); pop()
|
||||
|
||||
case POP => pop()
|
||||
case POP2 => pop(); pop()
|
||||
case DUP => push(peek())
|
||||
case DUP_X1 => insert(2, peek())
|
||||
case DUP_X2 => insert(3, peek())
|
||||
case DUP2 => push(peek(1)); push(peek(1))
|
||||
case DUP2_X1 => insert(3, peek(1)); insert(3, peek())
|
||||
case DUP2_X2 => insert(4, peek(1)); insert(4, peek())
|
||||
case SWAP => push(pop(1))
|
||||
|
||||
case i if(i >= IADD && i <= DREM) =>
|
||||
push(BinaryOp(i, pop(), pop()))
|
||||
case i if(i >= INEG && i <= DNEG) =>
|
||||
push(UnaryOp(i, pop()))
|
||||
case i if(i >= ISHL && i <= LXOR) =>
|
||||
push(BinaryOp(i, pop(), pop()))
|
||||
|
||||
case L2I|F2I|D2I => push(PrimitiveCast(pop(), DOUBLE_TYPE))
|
||||
case I2L|F2L|D2L => push(PrimitiveCast(pop(), LONG_TYPE))
|
||||
case I2F|L2F|D2F => push(PrimitiveCast(pop(), FLOAT_TYPE))
|
||||
case I2D|L2D|F2D => push(PrimitiveCast(pop(), DOUBLE_TYPE))
|
||||
case I2B => push(PrimitiveCast(pop(), BYTE_TYPE))
|
||||
case I2C => push(PrimitiveCast(pop(), CHAR_TYPE))
|
||||
case I2S => push(PrimitiveCast(pop(), SHORT_TYPE))
|
||||
|
||||
case i if(i >= LCMP && i <= DCMPG) =>
|
||||
push(BinaryOp(i, pop(), pop()))
|
||||
|
||||
case i if(i >= IRETURN && i <= ARETURN) => pop()
|
||||
|
||||
case ARRAYLENGTH => push(ArrayLength(pop()))
|
||||
case ATHROW => pop()
|
||||
case MONITORENTER|MONITOREXIT => pop()
|
||||
|
||||
case _ =>
|
||||
|
||||
}
|
||||
case iinsn:IntInsnNode => ainsn.getOpcode match
|
||||
{
|
||||
case BIPUSH => push(Const(iinsn.operand.toByte))
|
||||
case SIPUSH => push(Const(iinsn.operand.toShort))
|
||||
}
|
||||
case ldcinsn:LdcInsnNode => ainsn.getOpcode match
|
||||
{
|
||||
case LDC => push(Const(ldcinsn.cst))
|
||||
}
|
||||
case vinsn:VarInsnNode => ainsn.getOpcode match
|
||||
{
|
||||
case i if(i >= ILOAD && i <= ALOAD) =>
|
||||
push(locals(vinsn.`var`))
|
||||
case i if(i >= ISTORE && i <= ASTORE) =>
|
||||
setL(vinsn.`var`, pop())
|
||||
}
|
||||
case incinsn:IincInsnNode => ainsn.getOpcode match
|
||||
{
|
||||
case IINC => setL(incinsn.`var`, BinaryOp(IINC, Const(incinsn.incr), locals(incinsn.`var`)))
|
||||
}
|
||||
case jinsn:JumpInsnNode => ainsn.getOpcode match
|
||||
{
|
||||
case i if(i >= IFEQ && i <= IFLE) => pop()
|
||||
case i if(i >= IF_ICMPEQ && i <= IF_ACMPNE) => pop(); pop()
|
||||
case JSR => push(ReturnAddress())
|
||||
case IFNULL|IFNONNULL => pop()
|
||||
case GOTO =>
|
||||
}
|
||||
case sinsn:TableSwitchInsnNode => pop()
|
||||
case linsn:LookupSwitchInsnNode => pop()
|
||||
case finsn:FieldInsnNode => ainsn.getOpcode match
|
||||
{
|
||||
case GETSTATIC => push(GetField(null, finsn))
|
||||
case PUTSTATIC => pop()
|
||||
case GETFIELD => push(GetField(pop(), finsn))
|
||||
case PUTFIELD => pop(); pop()
|
||||
}
|
||||
case minsn:MethodInsnNode => ainsn.getOpcode match
|
||||
{
|
||||
case INVOKEVIRTUAL|INVOKESPECIAL|INVOKEINTERFACE =>
|
||||
push(Invoke(ainsn.getOpcode, popArgs(minsn.desc), pop(), minsn))
|
||||
case INVOKESTATIC =>
|
||||
push(Invoke(ainsn.getOpcode, popArgs(minsn.desc), null, minsn))
|
||||
}
|
||||
case tinsn:TypeInsnNode => ainsn.getOpcode match
|
||||
{
|
||||
case NEW => push(New(getType(tinsn.desc)))
|
||||
case NEWARRAY => push(NewArray(pop(), getType(tinsn.desc)))
|
||||
case ANEWARRAY => push(NewArray(pop(), getType("["+tinsn.desc)))
|
||||
case CHECKCAST => push(Cast(pop(), getType(tinsn.desc)))
|
||||
case INSTANCEOF => push(UnaryOp(INSTANCEOF, pop()))
|
||||
}
|
||||
case mainsn:MultiANewArrayInsnNode =>
|
||||
{
|
||||
val sizes = new Array[StackEntry](mainsn.dims)
|
||||
for(i <- 0 until sizes.length)
|
||||
sizes(i) = pop()
|
||||
push(NewMultiArray(sizes, getType(mainsn.desc)))
|
||||
}
|
||||
/*case fnode:FrameNode => fnode.`type` match
|
||||
{
|
||||
case F_NEW|F_FULL => println("reset stacks/locals")
|
||||
case F_APPEND => println("add local")
|
||||
case F_CHOP => println("rem locals")
|
||||
case F_SAME => println("reset")
|
||||
case F_SAME1 => println("reset locals and all but bottom stack")
|
||||
}*/
|
||||
case lnode:LabelNode =>
|
||||
{
|
||||
catchHandlers.get(lnode) match
|
||||
{
|
||||
case Some(tcblock) => push(CaughtException(Type.getType(tcblock.`type`)))
|
||||
case None =>
|
||||
}
|
||||
}
|
||||
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user