Skip to content
Browse files

Update Duby jvm_compiler to the point it can compile a simple fib scr…

…ipt. Additional changes to compiler.rb and c_compiler.rb to support the modified signatures and compiler flow.

git-svn-id: http://svn.codehaus.org/jruby/trunk/jruby@7565 961051c9-f516-0410-bf72-c9f7e237a7b7
  • Loading branch information...
1 parent 5f8365a commit 0d5f7a0ac5a82198909332ecef209a21cdf764a6 @headius headius committed Aug 28, 2008
View
45 lib/ruby/site_ruby/1.8/compiler/builder.rb
@@ -110,10 +110,15 @@ def method?
class ClassBuilder
include Util
include TypeNamespace
+
+ begin
+ import "jruby.objectweb.asm.Opcodes"
+ import "jruby.objectweb.asm.ClassWriter"
+ rescue
+ import "org.objectweb.asm.Opcodes"
+ import "org.objectweb.asm.ClassWriter"
+ end
- import "jruby.objectweb.asm.Opcodes"
- import "jruby.objectweb.asm.ClassWriter"
- include Opcodes
import java.lang.Object
import java.lang.Void
include Signature
@@ -135,7 +140,7 @@ def initialize(file_builder, class_name, file_name, superclass = Object, *interf
interface_paths = []
interfaces.each {|interface| interface_paths << path(interface)}
- @class_writer.visit(V1_4, ACC_PUBLIC | ACC_SUPER, class_name, nil, path(superclass), interface_paths.to_java(:string))
+ @class_writer.visit(Opcodes::V1_4, Opcodes::ACC_PUBLIC | Opcodes::ACC_SUPER, class_name, nil, path(superclass), interface_paths.to_java(:string))
@class_writer.visit_source(file_name, nil)
@constructor = nil
@@ -158,7 +163,7 @@ def generate
if @constructor
@constructor.generate_constructor(@superclass)
else
- method = MethodBuilder.new(self, ACC_PUBLIC, "<init>", [])
+ method = MethodBuilder.new(self, Opcodes::ACC_PUBLIC, "<init>", [])
method.start
method.aload 0
method.invokespecial @superclass, "<init>", Void::TYPE
@@ -176,7 +181,7 @@ def generate
# generate fields
@fields.each do |name, field|
- @class_writer.visit_field(ACC_PROTECTED, field.name, class_id(field.type), nil, nil)
+ @class_writer.visit_field(Opcodes::ACC_PROTECTED, field.name, class_id(field.type), nil, nil)
end
String.from_java_bytes(@class_writer.to_byte_array)
@@ -218,20 +223,30 @@ def method(name, *signature, &block)
raise "Overloading not yet supported"
end
- mb = MethodBuilder.new(self, ACC_PUBLIC, name, signature)
+ mb = MethodBuilder.new(self, Opcodes::ACC_PUBLIC, name, signature)
deferred_builder = DeferredMethodBuilder.new(name, mb, signature, block)
if name == "<init>"
@constructor = deferred_builder
else
@instance_methods[name] = deferred_builder
end
+
+ mb
end
# New version does not instance_eval, to allow for easier embedding
def static_method(name, *signature, &block)
- mb = MethodBuilder.new(self, ACC_PUBLIC | ACC_STATIC, name, signature)
- deferred_builder = DeferredMethodBuilder.new(name, mb, signature, block)
- @static_methods[name] = deferred_builder
+ mb = MethodBuilder.new(self, Opcodes::ACC_PUBLIC | Opcodes::ACC_STATIC, name, signature)
+# deferred_builder = DeferredMethodBuilder.new(name, mb, signature, block)
+# @static_methods[name] = deferred_builder
+
+ if block
+ mb.start
+ block.yield(mb)
+ mb.stop
+ else
+ mb
+ end
end
# name for signature generation using the class being generated
@@ -318,8 +333,12 @@ def method?
end
class MethodBuilder
- import "jruby.objectweb.asm.Opcodes"
- include Opcodes
+ begin
+ import "jruby.objectweb.asm.Opcodes"
+ rescue
+ import "org.objectweb.asm.Opcodes"
+ end
+
include Compiler::Bytecode
attr_reader :method_visitor
@@ -335,7 +354,7 @@ def initialize(class_builder, modifiers, name, signature)
@locals = {}
- @static = (modifiers & ACC_STATIC) != 0
+ @static = (modifiers & Opcodes::ACC_STATIC) != 0
@locals['this'] = TypedVariable.new("this", @class_builder, 0) unless @static
end
View
47 lib/ruby/site_ruby/1.8/compiler/bytecode.rb
@@ -5,9 +5,14 @@ module Compiler
# JVM assembly code. Included classes must just provide a method_visitor accessor
module Bytecode
include Signature
-
- import "jruby.objectweb.asm.Opcodes"
- import "jruby.objectweb.asm.Label"
+
+ begin
+ import "jruby.objectweb.asm.Opcodes"
+ import "jruby.objectweb.asm.Label"
+ rescue Exception
+ import "org.objectweb.asm.Opcodes"
+ import "org.objectweb.asm.Label"
+ end
import java.lang.Object
import java.lang.System
@@ -44,6 +49,13 @@ def #{const_down}(value)
method_visitor.visit_ldc_insn(value)
end
", b, __FILE__, __LINE__
+
+ when "BIPUSH", "SIPUSH"
+ eval "
+ def #{const_down}(value)
+ method_visitor.visit_int_insn(Opcodes::#{const_name}, value)
+ end
+ "
when "INVOKESTATIC", "INVOKEVIRTUAL", "INVOKEINTERFACE", "INVOKESPECIAL"
# method instructions
@@ -64,7 +76,7 @@ def returnvoid()
"ARRAYLENGTH",
"ARETURN", "ATHROW", "ACONST_NULL", "AALOAD",
"BALOAD", "BASTORE",
- "ICONST_0", "ICONST_1", "ICONST_2", "ICONST_3", "IRETURN", "IALOAD",
+ "ICONST_M1", "ICONST_0", "ICONST_1", "ICONST_2", "ICONST_3", "ICONST_4", "ICONST_5", "IRETURN", "IALOAD",
"IADD", "IINC", "ISUB", "IDIV", "IMUL", "INEG", "IAND", "IOR", "IXOR",
"LCONST_0", "LCONST_1", "LRETURN", "LALOAD",
"LADD", "LINC", "LSUB", "LDIV", "LMUL", "LNEG", "LAND", "LOR", "LXOR",
@@ -162,5 +174,32 @@ def swap2
def line(num)
method_visitor.visit_line_number num, Label.new
end
+
+ def push_int(num)
+ if (num <= Java::java.lang.Byte::MAX_VALUE && num >= Java::java.lang.Byte::MIN_VALUE)
+ case num
+ when -1
+ iconst_m1
+ when 0
+ iconst_0
+ when 1
+ iconst_1
+ when 2
+ iconst_2
+ when 3
+ iconst_3
+ when 4
+ iconst_4
+ when 5
+ iconst_5
+ else
+ bipush(num)
+ end
+ elsif (num <= Java::java.lang.Short::MAX_VALUE && num >= Java::java.lang.Short::MIN_VALUE)
+ sipush(num)
+ else
+ ldc(num)
+ end
+ end
end
end
View
48 lib/ruby/site_ruby/1.8/duby/c_compiler.rb
@@ -5,12 +5,12 @@ module Duby
module Compiler
class C
class MathCompiler
- def call(compiler, name, recv, args)
- recv.compile(compiler)
+ def call(compiler, call)
+ call.target.compile(compiler)
- compiler.src << " #{name} "
+ compiler.src << " #{call.name} "
- args.each {|arg| arg.compile(compiler)}
+ call.parameters.each {|param| param.compile(compiler)}
end
end
@@ -28,58 +28,70 @@ def initialize(filename)
def compile(ast)
ast.compile(self)
end
+
+ def define_main(body)
+ old_src, @src = @src, "int main(int argc, char **argv)\n{\n"
+
+ body.compile(self)
+
+ @src << "}\n\n"
+
+ @src = old_src + @src
+ end
def define_method(name, signature, args, body)
- @src << "#{type_mapper[signature[:return]]} #{name}("
+ old_src, @src = @src, "#{type_mapper[signature[:return]]} #{name}("
args.compile(self)
- @src << ") {"
+ @src << ")\n{\n"
body.compile(self)
- @src << "}\n\n"
+ @src << "\n}\n\n"
+
+ @src = old_src + @src
end
def declare_argument(name, type)
@src << "#{type_mapper[type]} #{name}"
end
- def branch(condition, body, els)
+ def branch(iff)
@src << "if ("
- condition.compile(self)
+ iff.condition.compile(self)
@src << ") {"
- body.compile(self)
+ iff.body.compile(self)
- if els
+ if iff.else
@src << "} else {"
- els.compile(self)
+ iff.else.compile(self)
end
@src << "}"
end
- def call(name, recv, args)
- call_compilers[recv.inferred_type].call(self, name, recv, args)
+ def call(call)
+ call_compilers[call.target.inferred_type].call(self, call)
end
def call_compilers
@call_compilers ||= {}
end
- def self_call(name, args)
- @src << "#{name}("
+ def self_call(fcall)
+ @src << "#{fcall.name}("
- args.each {|arg| arg.compile(self)}
+ fcall.parameters.each {|param| param.compile(self)}
@src << ")"
end
- def local(name)
+ def local(name, type)
@src << name
end
View
12 lib/ruby/site_ruby/1.8/duby/compiler.rb
@@ -34,21 +34,21 @@ def compile(compiler)
class Local
def compile(compiler)
- compiler.local(name)
+ compiler.local(name, inferred_type)
end
end
class LocalAssignment
def compile(compiler)
- compiler.local_assign(name) {
+ compiler.local_assign(name, inferred_type) {
value.compile(compiler)
}
end
end
class Script
def compile(compiler)
- body.compile(compiler)
+ compiler.define_main(body)
end
end
@@ -72,7 +72,7 @@ def compile(compiler)
class If
def compile(compiler)
- compiler.branch(condition, body, self.else)
+ compiler.branch(self)
end
end
@@ -84,13 +84,13 @@ def compile(compiler)
class FunctionalCall
def compile(compiler)
- compiler.self_call(name, parameters)
+ compiler.self_call(self)
end
end
class Call
def compile(compiler)
- compiler.call(name, target, parameters)
+ compiler.call(self)
end
end
end
View
257 lib/ruby/site_ruby/1.8/duby/jvm_compiler.rb
@@ -1,194 +1,179 @@
require 'duby'
+require 'compiler/builder'
module Duby
module Compiler
- class C
+ class JVM
class MathCompiler
- def call(compiler, name, recv, args)
- recv.call
-
- compiler.src << " #{name} "
+ def call(compiler, call)
+ call.target.compile(compiler)
+ call.parameters.each {|param| param.compile(compiler)}
- args.call
+ target_type = call.target.inferred_type
+ case target_type
+ when AST.type(:fixnum)
+ case call.name
+ when '-'
+ compiler.method.isub
+ when '+'
+ compiler.method.iadd
+ else
+ compiler.method.invokevirtual(
+ compiler.type_mapper[call.target.inferred_type],
+ call.name,
+ [compiler.type_mapper[call.inferred_type], *call.parameters.map {|param| compiler.type_mapper[param.inferred_type]}])
+ end
+ else
+ compiler.method.invokevirtual(
+ compiler.type_mapper[call.target.inferred_type],
+ call.name,
+ [compiler.type_mapper[call.inferred_type], *call.parameters.map {|param| compiler.type_mapper[param.inferred_type]}])
+ end
end
end
- attr_accessor :filename, :src
+ attr_accessor :filename, :src, :method
def initialize(filename)
@filename = filename
@src = ""
- self.type_mapper[AST::TypeReference.new(:fixnum)] = "int"
+ self.type_mapper[AST.type(:fixnum)] = Java::int
+ self.type_mapper[AST.type(:string, true)] = Java::java.lang.String[]
- self.call_compilers[AST::TypeReference.new(:fixnum)] = MathCompiler.new
+ self.call_compilers[AST.type(:fixnum)] = MathCompiler.new
+
+ @file = ::Compiler::FileBuilder.new(filename)
+ @class = @file.public_class(filename.split('.')[0])
end
def compile(ast)
ast.compile(this)
end
+
+ def define_main(body)
+ oldmethod, @method = @method, @class.static_method("main", type_mapper[AST.type(:void)], type_mapper[AST.type(:string, true)])
+
+ @method.start
+
+ body.compile(self)
+
+ @method.returnvoid
+ @method.stop
+
+ @method = oldmethod
+ end
def define_method(name, signature, args, body)
- @src << "#{type_mapper[signature[:return]]} #{name}("
-
- args.call
+ oldmethod, @method = @method, @class.static_method(name.to_s, type_mapper[signature[:return]], *args.args.map {|arg| type_mapper[arg.inferred_type]})
+
+ @method.start
- @src << ") {"
+ #args.call
- body.call
+ body.compile(self)
+
+ case signature[:return]
+ when AST.type(:fixnum)
+ @method.ireturn
+ else
+ @method.aload 0
+ @method.areturn
+ end
- @src << "}\n\n"
+ @method.stop
+
+ @method = oldmethod
end
def declare_argument(name, type)
- @src << "#{type_mapper[type]} #{name}"
+ # declare local vars for arguments here
end
- def branch(condition, body_proc, else_proc)
- @src << "if ("
-
- condition.call
+ def branch(iff)
+ elselabel = @method.label
+ donelabel = @method.label
- @src << ") {"
-
- body_proc.call
-
- if else_proc
- @src << "} else {"
-
- else_proc.call
+ # this is ugly...need a better way to abstract the idea of compiling a
+ # conditional branch while still fitting into JVM opcodes
+ predicate = iff.condition.predicate
+ case iff.condition.predicate
+ when AST::Call
+ case predicate.target.inferred_type
+ when AST.type(:fixnum)
+ # fixnum conditional, so we need to use JVM opcodes
+ case predicate.parameters[0].inferred_type
+ when AST.type(:fixnum)
+ # fixnum on fixnum, easy
+ case predicate.name
+ when '<'
+ predicate.target.compile(self)
+ predicate.parameters[0].compile(self)
+ @method.if_icmpge(elselabel)
+ else
+ raise "Unknown :fixnum on :fixnum predicate operation: " + predicate.name
+ end
+ else
+ raise "Unknown :fixnum on " + predicate.parameters[0].inferred_type + " predicate operations: " + predicate.name
+ end
+ else
+ raise "Unknown " + predicate.target.inferred_type + " on " + predicate.parameters[0].inferred_type + " predicate operations: " + predicate.name
+ end
end
-
- @src << "}"
+
+ iff.body.compile(self)
+
+ @method.goto(donelabel)
+
+ elselabel.set!
+
+ iff.else.compile(self) if iff.else
+
+ donelabel.set!
end
- def call(name, recv_type, recv, args)
- call_compilers[recv_type].call(self, name, recv, args)
+ def call(call)
+ call_compilers[call.target.inferred_type].call(self, call)
end
def call_compilers
@call_compilers ||= {}
end
- def self_call(name, args)
- @src << "#{name}("
-
- args.call
-
- @src << ")"
+ def self_call(fcall)
+ fcall.parameters.each {|param| param.compile(self)}
+ @method.invokestatic(
+ @method.this,
+ fcall.name,
+ [type_mapper[fcall.inferred_type], *fcall.parameters.map {|param| type_mapper[param.inferred_type]}])
end
- def local(name)
- @src << name
+ def local(name, type)
+ case type
+ when AST.type(:fixnum)
+ @method.iload(@method.local(name))
+ else
+ @method.aload(@method.local(name))
+ end
end
def fixnum(value)
- @src << value.to_s
+ @method.push_int(value)
end
def newline
- @src << ";\n"
- end
-
- def ret
- @src << "return "
-
- yield
+ # TODO: line numbering
end
def generate
- @src
+ @file.generate{|filename, builder| File.open(filename, 'w') {|f| f.write(builder.generate)}}
end
def type_mapper
@type_mapper ||= {}
end
end
end
-
- module AST
- class Script
- def compile(compiler)
- # preparations for the .c file would go here
- body.compile(compiler)
- end
- end
-
- class Body
- def compile(compiler)
- last = children[-1]
- children.each do |child|
- child.compile(compiler)
- compiler.newline
- end
- end
- end
-
- class MethodDefinition
- def compile(compiler)
- args_callback = proc {arguments.compile(compiler)}
- body_callback = proc {body.compile(compiler)}
- compiler.define_method(name, signature, args_callback, body_callback)
- end
- end
-
- class Arguments
- def compile(compiler)
- args.each {|arg| compiler.declare_argument(arg.name, arg.inferred_type)} if args
- end
- end
-
- class Noop
- def compile(compiler)
- # nothing
- end
- end
-
- class Fixnum
- def compile(compiler)
- compiler.fixnum(literal)
- end
- end
-
- class If
- def compile(compiler)
- cond_callback = proc { condition.compile(compiler) }
- body_callback = proc { body.compile(compiler) }
- else_callback = proc { self.else.compile(compiler)}
-
- compiler.branch(cond_callback, body_callback, else_callback)
- end
- end
-
- class Condition
- def compile(compiler)
- predicate.compile(compiler)
- end
- end
-
- class FunctionalCall
- def compile(compiler)
- args_callback = proc { parameters.each {|param| param.compile(compiler)}}
-
- compiler.self_call(name, args_callback)
- end
- end
-
- class Call
- def compile(compiler)
- recv_callback = proc { target.compile(compiler) }
- args_callback = proc { parameters.each {|param| param.compile(compiler)}}
-
- compiler.call(name, target.inferred_type, recv_callback, args_callback)
- end
- end
-
- class Local
- def compile(compiler)
- compiler.local(name)
- end
- end
- end
end
if __FILE__ == $0
@@ -198,10 +183,10 @@ def compile(compiler)
ast.infer(typer)
typer.resolve(true)
- compiler = Duby::Compiler::JVM.new("#{ARGV[0].split('.')[0]}.class")
+ compiler = Duby::Compiler::JVM.new(ARGV[0])
ast.compile(compiler)
- File.open(compiler.filename, "w") {|file| file.write(compiler.generate)}
+ compiler.generate
end
__END__
require 'duby'
@@ -250,7 +235,7 @@ def initialize(filename)
@filename = filename
@file_builder = FileBuilder.new(filename)
- fixnum_type = AST::TypeReference.new(:fixnum)
+ fixnum_type = AST.type(:fixnum)
self.type_mapper[fixnum_type] = Java::int.java_class
View
4 test/compiler/duby/test_compilation.rb
@@ -63,7 +63,7 @@ def test_local
new_ast.compile(@compiler)
- assert_equal([[:local_assign, nil, "a"], [:fixnum, 1], [:local, nil, "a"]], @compiler.calls)
+ assert_equal([[:local_assign, "a", nil], [:fixnum, 1], [:local, "a", nil]], @compiler.calls)
end
def test_local_typed
@@ -72,7 +72,7 @@ def test_local_typed
new_ast.infer(typer)
new_ast.compile(@compiler)
- assert_equal([[:local_assign, AST.type(:fixnum), "a"], [:fixnum, 1], [:local, AST.type(:fixnum), "a"]], @compiler.calls)
+ assert_equal([[:local_assign, "a", AST.type(:fixnum)], [:fixnum, 1], [:local, "a", AST.type(:fixnum)]], @compiler.calls)
end
=begin
def test_args
View
27 test/compiler/test_bytecode.rb
@@ -3,8 +3,13 @@
class TestBytecode < Test::Unit::TestCase
include Compiler::Bytecode
+
+ begin
+ import "jruby.objectweb.asm.Opcodes"
+ rescue
+ import "org.objectweb.asm.Opcodes"
+ end
- import "jruby.objectweb.asm.Opcodes"
import java.lang.System
import java.lang.Integer
import java.lang.Void
@@ -44,6 +49,26 @@ def test_ldc
assert_equal([:visit_ldc_insn, "a"], (ldc "a"))
assert_equal([:visit_ldc_insn, java.lang.Integer.new(1)], (ldc_int 1))
end
+
+ def test_int_insns
+ assert_equal([:visit_int_insn, Opcodes::BIPUSH, 1], (bipush 1))
+ assert_equal([:visit_int_insn, Opcodes::SIPUSH, 1], (sipush 1))
+
+ assert_equal([:visit_int_insn, Opcodes::BIPUSH, -2], (push_int(-2)))
+ assert_equal([:visit_insn, Opcodes::ICONST_M1], (push_int(-1)))
+ assert_equal([:visit_insn, Opcodes::ICONST_0], (push_int(0)))
+ assert_equal([:visit_insn, Opcodes::ICONST_1], (push_int(1)))
+ assert_equal([:visit_insn, Opcodes::ICONST_2], (push_int(2)))
+ assert_equal([:visit_insn, Opcodes::ICONST_3], (push_int(3)))
+ assert_equal([:visit_insn, Opcodes::ICONST_4], (push_int(4)))
+ assert_equal([:visit_insn, Opcodes::ICONST_5], (push_int(5)))
+ assert_equal([:visit_int_insn, Opcodes::BIPUSH, 6], (push_int(6)))
+
+ assert_equal([:visit_int_insn, Opcodes::SIPUSH, -129], (push_int(-129)))
+ assert_equal([:visit_int_insn, Opcodes::SIPUSH, 128], (push_int(128)))
+ assert_equal([:visit_ldc_insn, -65537], (push_int(-65537)))
+ assert_equal([:visit_ldc_insn, 65536], (push_int(65536)))
+ end
def test_method_insns
assert_equal(

0 comments on commit 0d5f7a0

Please sign in to comment.
Something went wrong with that request. Please try again.