I started working with Asm this week. After playing with it for a bit, it seemed obvious that JRuby would be a sweet spot for Asm code; very often, you need to implement only a small fraction of the interfaces that Asm uses.
Here’s my first JRuby effort with Asm. It’s straightforward and doesn’t do much yet – just outputs the contents of the .class files given on the command line. Here’s the JRuby:
require 'java'
require 'asm-3.3.1'
require 'pp'
# This lets me type org.objectweb.asm::... instead of Java::OrgObjectwebAsm::...
def org
Java::Org
end
# This lets me use ClassReader without typing out org.objectweb.asm::ClassReader
java_import org.objectweb.asm::ClassReader
# Ruby supports mixins. Both visitor classes (class and method) use this method_missing
# call.
#
# The generic visitor just prints information for any call to a visitor* method.
module GenericVisitor
def method_missing *args
if args.first.to_s =~ /visit.*/
puts "In type: #{self.class} call to " + args.to_s
else
super
end
end
end
class SampleMethodVisitor
include org.objectweb.asm::MethodVisitor
include GenericVisitor
end
class SampleClassVisitor
include org.objectweb.asm::ClassVisitor
include GenericVisitor
# we need to keep a copy of our method visitor
def initialize args
@method_visitor = args[:method_visitor]
end
def visit_method *args
# The output has methods seperated with an easy-to-see string
puts "visit method " + args.to_s + "================================"
# Asm wants ClassVisitor#visitMethod to return a method visitor
@method_visitor
end
end
# Loop through each .class file given on the command line
ARGV.each do |classfile|
puts " ---------------- Reading file #{classfile}"
f = java.io.File.new classfile
fis = java.io.FileInputStream.new f
class_reader = ClassReader.new fis
v = SampleClassVisitor.new method_visitor: SampleMethodVisitor.new
class_reader.accept v, 0
puts
end
And here’s the output:
---------------- Reading file /Users/james/experimements/AsmSample/bin/com/restphone/classSignature/SampleOne.class
In type: SampleClassVisitor call to [:visit, 50, 33, "com/restphone/classSignature/SampleOne", nil, "java/lang/Object", #<#<Class:0x12690ed81>:0x28b53b32>]
In type: SampleClassVisitor call to [:visitSource, "SampleOne.java", nil]
In type: SampleClassVisitor call to [:visitField, 9, "x", "Ljava/lang/Integer;", nil, nil]
visit method [8, "<clinit>", "()V", nil, nil]================================
In type: SampleMethodVisitor call to [:visitCode]
In type: SampleMethodVisitor call to [:visitLabel, #<Java::OrgObjectwebAsm::Label:0x236954e1>]
In type: SampleMethodVisitor call to [:visitLineNumber, 4, #<Java::OrgObjectwebAsm::Label:0x236954e1>]
In type: SampleMethodVisitor call to [:visitFieldInsn, 178, "com/restphone/classSignature/SampleTwo", "y", "Ljava/lang/Integer;"]
In type: SampleMethodVisitor call to [:visitFieldInsn, 179, "com/restphone/classSignature/SampleOne", "x", "Ljava/lang/Integer;"]
In type: SampleMethodVisitor call to [:visitLabel, #<Java::OrgObjectwebAsm::Label:0x643f58bb>]
In type: SampleMethodVisitor call to [:visitLineNumber, 3, #<Java::OrgObjectwebAsm::Label:0x643f58bb>]
In type: SampleMethodVisitor call to [:visitInsn, 177]
In type: SampleMethodVisitor call to [:visitMaxs, 1, 0]
In type: SampleMethodVisitor call to [:visitEnd]
visit method [1, "<init>", "()V", nil, nil]================================
In type: SampleMethodVisitor call to [:visitCode]
In type: SampleMethodVisitor call to [:visitLabel, #<Java::OrgObjectwebAsm::Label:0x1d7aaa0e>]
In type: SampleMethodVisitor call to [:visitLineNumber, 3, #<Java::OrgObjectwebAsm::Label:0x1d7aaa0e>]
In type: SampleMethodVisitor call to [:visitVarInsn, 25, 0]
In type: SampleMethodVisitor call to [:visitMethodInsn, 183, "java/lang/Object", "<init>", "()V"]
In type: SampleMethodVisitor call to [:visitInsn, 177]
In type: SampleMethodVisitor call to [:visitLabel, #<Java::OrgObjectwebAsm::Label:0x13ad9b0f>]
In type: SampleMethodVisitor call to [:visitLocalVariable, "this", "Lcom/restphone/classSignature/SampleOne;", nil, #<Java::OrgObjectwebAsm::Label:0x1d7aaa0e>, #<Java::OrgObjectwebAsm::Label:0x13ad9b0f>, 0]
In type: SampleMethodVisitor call to [:visitMaxs, 1, 1]
In type: SampleMethodVisitor call to [:visitEnd]
visit method [1, "doubleTheValue", "(Ljava/lang/Integer;)Ljava/lang/Integer;", nil, nil]================================
In type: SampleMethodVisitor call to [:visitCode]
In type: SampleMethodVisitor call to [:visitLabel, #<Java::OrgObjectwebAsm::Label:0x9eae15f>]
In type: SampleMethodVisitor call to [:visitLineNumber, 7, #<Java::OrgObjectwebAsm::Label:0x9eae15f>]
In type: SampleMethodVisitor call to [:visitVarInsn, 25, 1]
In type: SampleMethodVisitor call to [:visitMethodInsn, 182, "java/lang/Integer", "intValue", "()I"]
In type: SampleMethodVisitor call to [:visitInsn, 5]
In type: SampleMethodVisitor call to [:visitInsn, 104]
In type: SampleMethodVisitor call to [:visitMethodInsn, 184, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;"]
In type: SampleMethodVisitor call to [:visitInsn, 176]
In type: SampleMethodVisitor call to [:visitLabel, #<Java::OrgObjectwebAsm::Label:0x2569a1c5>]
In type: SampleMethodVisitor call to [:visitLocalVariable, "this", "Lcom/restphone/classSignature/SampleOne;", nil, #<Java::OrgObjectwebAsm::Label:0x9eae15f>, #<Java::OrgObjectwebAsm::Label:0x2569a1c5>, 0]
In type: SampleMethodVisitor call to [:visitLocalVariable, "x", "Ljava/lang/Integer;", nil, #<Java::OrgObjectwebAsm::Label:0x9eae15f>, #<Java::OrgObjectwebAsm::Label:0x2569a1c5>, 1]
In type: SampleMethodVisitor call to [:visitMaxs, 2, 2]
In type: SampleMethodVisitor call to [:visitEnd]
In type: SampleClassVisitor call to [:visitEnd]
Incidentally, I used my new Eclipse plugin to send the current selection through a filter to html-escape the text. Check it out on github.