Permalink
Browse files

Add a mirror api for reading class files.

Based mostly on the Java reflection API, with some inspiration from
javax.lang.model.*.
  • Loading branch information...
1 parent abf4813 commit e58955770192d604370054e45e23c432a81fbfc4 @ribrdb ribrdb committed May 18, 2010
Showing with 367 additions and 0 deletions.
  1. +15 −0 bin/bitep
  2. +1 −0 lib/bitescript.rb
  3. +5 −0 lib/bitescript/asm.rb
  4. +346 −0 lib/bitescript/mirror.rb
View
15 bin/bitep
@@ -0,0 +1,15 @@
+#!/usr/bin/env jruby
+begin
+ require 'bitescript'
+rescue LoadError
+ $: << File.dirname(File.dirname(__FILE__)) + '/lib'
+ require 'bitescript'
+end
+
+file = ARGV[0]
+
+raise "usage: bitep <class_name>" unless file
+
+cb = BiteScript::ASM::ClassMirror::Builder.new
+BiteScript::ASM::ClassReader.new(ARGV[0]).accept(cb, 3)
+puts cb.mirror.inspect
View
1 lib/bitescript.rb
@@ -3,6 +3,7 @@
require 'bitescript/signature'
require 'bitescript/bytecode'
require 'bitescript/builder'
+require 'bitescript/mirror'
module BiteScript
VERSION = '0.0.4'
View
5 lib/bitescript/asm.rb
@@ -13,5 +13,10 @@ module ASM
end
java_import asm_package.Label
java_import asm_package.Type
+ java_import asm_package.AnnotationVisitor
+ java_import asm_package.ClassVisitor
+ java_import asm_package.FieldVisitor
+ java_import asm_package.MethodVisitor
+ java_import asm_package.ClassReader
end
end
View
346 lib/bitescript/mirror.rb
@@ -0,0 +1,346 @@
+module BiteScript::ASM
+ class EnumValue
+ attr_reader :declaring_type, :name
+
+ def initialize(declaring_type, name)
+ @declaring_type = declaring_type
+ @name = name
+ end
+ end
+
+ class AnnotationMirror
+ attr_reader :type, :parent
+ def initialize(type, parent=nil)
+ @type = type
+ @parent = parent
+ @values = {}
+ end
+
+ def value
+ @values['value']
+ end
+
+ def value=(value)
+ @values['value'] = value
+ end
+
+ def [](name)
+ @values[name]
+ end
+
+ def []=(name, value)
+ @values[name] = value
+ end
+
+ def inspect
+ unless @values.empty?
+ values = []
+ @values.each do |k, v|
+ values << "#{k}=#{inspect_value(v)}"
+ end
+ values = "(#{values.join ', '})"
+ end
+ "@#{type.class_name}#{values}\n"
+ end
+
+ def inspect_value(v)
+ case v
+ when Type
+ v.class_name + ".class"
+ when Array
+ "{#{v.map{|x| inspect_value(x)}.join(', ')}}"
+ when EnumValue
+ "#{v.declaring_type.class_name}.#{v.name}"
+ else
+ v.inspect
+ end
+ end
+
+ class Builder
+ class ValueArray
+ attr_reader :parent
+ def initialize(annotation, array)
+ @parent = annotation
+ @array = array
+ end
+
+ def []=(name, value)
+ @array << value
+ end
+ end
+
+ include BiteScript::ASM::AnnotationVisitor
+
+ attr_reader :annotation
+ def initialize(desc, visible)
+ @current = @annotation = AnnotationMirror.new(Type.getType(desc))
+ end
+
+
+ def visit(name, value)
+ case value
+ when ArrayJavaProxy
+ visitArray(name)
+ value.each {|x| visit(name, x)}
+ visitEnd
+ else
+ @current[name] = value
+ end
+ end
+
+ def visitAnnotation(name, desc)
+ @current = AnnotationMirror.new(Type.getType(desc), @current)
+ self
+ end
+
+ def visitArray(name)
+ array = @current[name] = []
+ @current = ValueArray.new(@current, array)
+ end
+
+ def visitEnum(name, desc, value)
+ @current[name] = EnumValue.new(Type.getType(desc), value)
+ end
+
+ def visitEnd
+ @current = @annotation.parent
+ end
+ end
+ end
+
+ module Annotated
+ def annotations
+ @annotations ||= {}
+ end
+
+ def addAnnotation(annotation)
+ annotations[annotation.type] = annotation
+ end
+
+ def getDeclaredAnnotation(name)
+ annotations[name]
+ end
+
+ def declaredAnnotations
+ annotations.values
+ end
+
+ def inspect_annotations
+ declaredAnnotations.map {|a| a.inspect}.join('')
+ end
+ end
+
+ module Modifiers
+ attr_accessor :flags
+ def self.add_modifier(name)
+ class_eval <<-EOF
+ def #{name.downcase}?
+ (flags & Opcodes.ACC_#{name.upcase}) != 0
+ end
+ EOF
+ end
+ %w(annotation bridge deprecated enum interface).each do |name|
+ add_modifier(name)
+ end
+ code = ''
+ %w(Public Private Protected Final Native
+ Static Strict Synchronized Transient Volatile).each do |name|
+ add_modifier(name)
+ code << "modifiers << '#{name.downcase} ' if #{name.downcase}?\n"
+ end
+
+ class_eval <<-EOF
+ def modifier_string
+ modifiers = ''
+ #{code}
+ modifiers
+ end
+ EOF
+ end
+
+ class ClassMirror
+ include Annotated
+ include Modifiers
+
+ attr_reader :type, :interfaces
+ attr_accessor :superclass
+
+ def initialize(type, flags)
+ super()
+ @type = type
+ @flags = flags
+ @methods = Hash.new {|h, k| h[k] = {}}
+ @constructors = {}
+ @fields = {}
+ @interfaces = []
+ end
+
+ def getConstructor(*arg_types)
+ @constructors[arg_types]
+ end
+
+ def getConstructors
+ @constructors.values
+ end
+
+ def addConstructor(constructor)
+ @constructors[constructor.parameters] = constructor
+ end
+
+ def getDeclaredMethod(name, *args)
+ if args[0].kind_of?(Array)
+ args = args[0]
+ end
+ @methods[name][args]
+ end
+
+ def getDeclaredMethods(name=nil)
+ if name
+ @methods[name].values
+ else
+ @methods.values.map {|m| m.values}.flatten
+ end
+ end
+
+ def addMethod(method)
+ if method.name == '<init>'
+ @constructors[method.parameters] = method
+ else
+ @methods[method.name][method.parameters] = method
+ end
+ end
+
+ def getField(name)
+ @fields[name]
+ end
+
+ def getDeclaredFields
+ @fields.values
+ end
+
+ def addField(field)
+ @fields[field.name] = field
+ end
+
+ def inspect
+ if annotation?
+ kind = "@interface"
+ elsif interface?
+ kind = "interface"
+ elsif enum?
+ kind = "enum"
+ else
+ kind = "class"
+ end
+ if superclass && !enum? && !interface?
+ extends = "extends #{superclass.getClassName} "
+ end
+ if self.interfaces && !self.interfaces.empty?
+ interfaces = self.interfaces.map{|i| i.class_name}.join(', ')
+ if interface?
+ extends = "extends #{interfaces} "
+ else
+ implements = "implements #{interfaces} "
+ end
+ end
+ result = "#{inspect_annotations}#{modifier_string}#{kind} "
+ result << "#{type.class_name} #{extends}{\n"
+ (getDeclaredFields + getConstructors + getDeclaredMethods).each do |f|
+ result << f.inspect << "\n"
+ end
+ result << "}"
+ end
+
+ class Builder
+ include BiteScript::ASM::ClassVisitor
+ include BiteScript::ASM::FieldVisitor
+ include BiteScript::ASM::MethodVisitor
+
+ def visit(version, access, name, signature, super_name, interfaces)
+ @current = @class = ClassMirror.new(Type.getObjectType(name), access)
+ @class.superclass = Type.getObjectType(super_name)
+ if interfaces
+ interfaces.each do |i|
+ @class.interfaces << Type.getObjectType(i)
+ end
+ end
+ end
+
+ def mirror
+ @class
+ end
+
+ def visitSource(source, debug); end
+ def visitOuterClass(owner, name, desc); end
+ def visitAttribute(attribute); end
+ def visitInnerClass(name, outer, inner, access); end
+ def visitEnd; end
+
+ def visitAnnotation(desc, visible)
+ builder = AnnotationMirror::Builder.new(desc, visible)
+ @current.addAnnotation(builder.annotation)
+ builder
+ end
+
+ def visitField(flags, name, desc, signature, value)
+ @current = FieldMirror.new(@class, flags, name, Type.getType(desc), value)
+ @class.addField(@current)
+ self
+ end
+
+ def visitMethod(flags, name, desc, signature, exceptions)
+ return_type = Type.getReturnType(desc)
+ parameters = Type.getArgumentTypes(desc).to_a
+ exceptions = (exceptions || []).map {|e| Type.getObjectType(e)}
+ @current = MethodMirror.new(
+ @class, flags, return_type, name, parameters, exceptions)
+ @class.addMethod(@current)
+ # TODO parameter annotations, default value, etc.
+ self
+ end
+ end
+ end
+
+ class FieldMirror
+ include Modifiers
+ include Annotated
+
+ attr_reader :declaring_class, :name, :type, :value
+ def initialize(klass, flags, name, type, value)
+ @declaring_class = klass
+ @flags = flags
+ @name = name
+ @type = type
+ @value = value
+ end
+
+ def inspect
+ inspect_annotations + "#{modifier_string}#{type.getClassName} #{name};"
+ end
+ end
+
+ class MethodMirror
+ include Modifiers
+ include Annotated
+
+ attr_reader :declaring_class, :name, :return_type, :parameters, :exceptions
+ def initialize(klass, flags, return_type, name, parameters, exceptions)
+ @flags = flags
+ @declaring_class = klass
+ @name = name
+ @return_type = return_type
+ @parameters = parameters
+ @exceptions = exceptions
+ end
+
+ def inspect
+ "%s%s%s %s(%s);" % [
+ inspect_annotations,
+ modifier_string,
+ return_type.class_name,
+ name,
+ parameters.map {|x| x.class_name}.join(', '),
+ ]
+ end
+ end
+end

0 comments on commit e589557

Please sign in to comment.