Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

r43@vatu: evan | 2006-08-24 16:34:21 -0700

 MASSIVE update.
 
 ironed a number of bugs, added more primitives (including the beginning
 of the platform specific code).
 
 AND... it runs code!!
  • Loading branch information...
commit 8dd2b6b8f549f8a47e26f8c02302c0bb016446ad 1 parent fdda5d1
@evanphx evanphx authored
Showing with 2,576 additions and 251 deletions.
  1. +32 −0 Rakefile
  2. +66 −0 bin/rcc
  3. +13 −0 bin/rubinius
  4. +82 −0 kernel/array.rb
  5. +99 −0 kernel/auto_fields.rb
  6. +63 −0 kernel/class.rb
  7. +95 −0 kernel/context.rb
  8. +106 −0 kernel/core.rb
  9. +16 −0 kernel/exception.rb
  10. +45 −0 kernel/fixnum.rb
  11. +76 −0 kernel/hash.rb
  12. +20 −0 kernel/io.rb
  13. +10 −0 kernel/method_missing.rb
  14. +51 −0 kernel/module.rb
  15. +19 −0 kernel/nil.rb
  16. +17 −0 kernel/string.rb
  17. +27 −0 kernel/symbol.rb
  18. +41 −0 kernel/tuple.rb
  19. +13 −2 lib/bytecode/assembler.rb
  20. +242 −47 lib/bytecode/compiler.rb
  21. +34 −10 lib/bytecode/constructor.rb
  22. +16 −5 lib/bytecode/encoder.rb
  23. +20 −15 lib/cpu/bootstrap.rb
  24. +182 −61 lib/cpu/instructions.rb
  25. +198 −9 lib/cpu/primitives.rb
  26. +32 −8 lib/cpu/runtime.rb
  27. +14 −8 lib/cpu/simple_marshal.rb
  28. +15 −0 lib/log.rb
  29. +94 −0 lib/machine.rb
  30. +51 −0 lib/object.rb
  31. +1 −1  lib/object_memory.rb
  32. +32 −0 lib/platform.rb
  33. +17 −10 lib/translation/local_scoping.rb
  34. +111 −35 lib/translation/normalize.rb
  35. +19 −0 lib/translation/states.rb
  36. +2 −1  lib/types.rb
  37. +4 −0 lib/types/bytearray.rb
  38. +2 −1  lib/types/compiled_method.rb
  39. +11 −1 lib/types/hash.rb
  40. +22 −0 lib/types/io.rb
  41. +4 −4 lib/types/method_context.rb
  42. +6 −0 lib/types/module.rb
  43. +6 −0 lib/types/object.rb
  44. +36 −0 lib/types/string.rb
  45. +6 −0 lib/types/symbol.rb
  46. +13 −2 samples/ivar.rb
  47. +5 −2 samples/lvar.rb
  48. +5 −0 test/bytecode/test_assembler.rb
  49. +115 −11 test/bytecode/test_compiler.rb
  50. +20 −0 test/bytecode/test_encoder.rb
  51. +4 −0 test/cpu/test_cpu.rb
  52. +83 −0 test/cpu/test_instructions.rb
  53. +183 −17 test/cpu/test_primitives.rb
  54. +1 −1  test/cpu/test_simple_marshal.rb
  55. +79 −0 test/test_machine.rb
View
32 Rakefile
@@ -0,0 +1,32 @@
+
+task :fields do
+ $:.unshift "lib"
+ require 'types'
+ fd = File.open("kernel/auto_fields.rb", "w")
+ Rubinius::Types.each do |name, mod|
+ next if mod::TotalFields.size == 0
+ sname = mod.name.split("::").last
+ fd.puts "class #{sname}"
+ idx = 0
+ mod::TotalFields.each do |fel|
+ fd.puts "def #{fel}; Ruby.asm \"push self\\npush #{idx}\\nfetch_field\"; end"
+ idx += 1
+ end
+ fd.puts "end"
+ end
+ fd.close
+end
+
+task :kernel do
+ fd = File.open("lib/kernel.rb", "w")
+ Dir["kernel/*.rb"].each do |path|
+ puts path
+ cur = File.open(path)
+ fd << cur.read
+ cur.close
+ fd << "\n"
+ end
+ fd.close
+end
+
+# vim: syntax=ruby
View
66 bin/rcc
@@ -0,0 +1,66 @@
+#!/usr/bin/env ruby
+$:.unshift "lib"
+require 'rubygems'
+require 'bytecode/compiler'
+require 'bytecode/constructor'
+
+if ARGV.first.index("-") == 0
+ option = ARGV.shift
+end
+
+
+con = Bytecode::Constructor.new(nil)
+com = Bytecode::Compiler.new
+
+file = ARGV.shift
+fd = File.open(file)
+sx = con.convert_to_sexp(fd)
+fd.close
+
+if option == "-x"
+ require 'pp'
+ begin
+ pp com.fully_normalize(sx)
+ rescue Object => e
+ puts e.backtrace
+ end
+ exit 0
+end
+
+begin
+ meth = com.compile_as_script sx, :__script__
+rescue Object => e
+ puts e.backtrace
+ raise
+end
+
+def iputs(str, indent)
+ print " " * indent
+ puts str
+end
+
+def show_method(m, indent=0)
+ iputs "=== Method Description: #{m.name}, primitive: #{m.primitive.inspect}", indent
+ iputs " From: #{m.file}", indent
+ iputs "Assembly:", indent
+ m.assembly.split("\n").each do |line|
+ iputs line, indent + 2
+ end
+ iputs "Literals:", indent
+ meths = m.literals.select { |mo|
+ Bytecode::MethodDescription === mo
+ }
+ others = m.literals - meths
+ others.each do |lit|
+ iputs lit.inspect, indent + 2
+ end
+
+ meths.each do |lit|
+ show_method(lit, indent + 1)
+ end
+
+
+ iputs "=== done with #{m.name}", indent
+end
+
+show_method(meth)
View
13 bin/rubinius
@@ -0,0 +1,13 @@
+#!/usr/bin/env ruby
+$:.unshift "lib"
+require 'machine'
+
+mach = Machine.new
+
+file = ARGV.shift
+mach.import_args ARGV
+mach.setup_standard_io
+mach.run_file "lib/kernel.rb"
+Log.debug "Kernel loaded properly."
+mach.run_file file
+exit 0
View
82 kernel/array.rb
@@ -0,0 +1,82 @@
+class Array
+ def to_s
+ "#<Array:0x#{object_id.to_s(16)} #{total} elements>"
+ end
+
+ def inspect
+ "[#{join(", ")}]"
+ end
+
+ def each
+ t = self.total
+ i = 0
+ while i < t
+ yield tuple.at(i)
+ i += 1
+ end
+ self
+ end
+
+ def map
+ out = []
+ each do |a|
+ out << yield(a)
+ end
+ return out
+ end
+
+ def map!
+ i = 0
+ each do |a|
+ self[i] = yield(a)
+ i += 1
+ end
+ return self
+ end
+
+ def join(sep)
+ str = ""
+ t = self.total
+ return str if t == 0
+ tuple.join_upto(sep, t)
+ end
+
+ def <<(ent)
+ self[total] = ent
+ end
+
+ def [](idx)
+ if idx >= total
+ return nil
+ end
+
+ tuple.at(idx)
+ end
+
+ def []=(idx, ent)
+ use = tuple
+ if idx >= use.fields
+ nt = Tuple.new(total + 10)
+ nt.copy_from use
+ put 1, nt
+ use = nt
+ end
+
+ use.put idx, ent
+ put(0, idx + 1)
+ return ent
+ end
+
+ def include?(obj)
+ nd = self.total
+ i = 0
+ while i < nd
+ test = self[i]
+ is = (test == obj)
+ return true if is
+ i += 1
+ end
+
+ return false
+ end
+end
View
99 kernel/auto_fields.rb
@@ -0,0 +1,99 @@
+class CompiledMethod
+def instance_variables; Ruby.asm "push self\npush 0\nfetch_field"; end
+def bytecodes; Ruby.asm "push self\npush 1\nfetch_field"; end
+def primitive; Ruby.asm "push self\npush 2\nfetch_field"; end
+def locals; Ruby.asm "push self\npush 3\nfetch_field"; end
+def literals; Ruby.asm "push self\npush 4\nfetch_field"; end
+def required; Ruby.asm "push self\npush 5\nfetch_field"; end
+def arguments; Ruby.asm "push self\npush 6\nfetch_field"; end
+def scope; Ruby.asm "push self\npush 7\nfetch_field"; end
+def exceptions; Ruby.asm "push self\npush 8\nfetch_field"; end
+def lines; Ruby.asm "push self\npush 9\nfetch_field"; end
+def file; Ruby.asm "push self\npush 10\nfetch_field"; end
+end
+class Array
+def total; Ruby.asm "push self\npush 0\nfetch_field"; end
+def tuple; Ruby.asm "push self\npush 1\nfetch_field"; end
+end
+class MetaClass
+def instance_variables; Ruby.asm "push self\npush 0\nfetch_field"; end
+def methods; Ruby.asm "push self\npush 1\nfetch_field"; end
+def name; Ruby.asm "push self\npush 2\nfetch_field"; end
+def constants; Ruby.asm "push self\npush 3\nfetch_field"; end
+def superclass; Ruby.asm "push self\npush 4\nfetch_field"; end
+def instance_fields; Ruby.asm "push self\npush 5\nfetch_field"; end
+def attached_instance; Ruby.asm "push self\npush 6\nfetch_field"; end
+end
+class BlockContext
+def instance_variables; Ruby.asm "push self\npush 0\nfetch_field"; end
+def sender; Ruby.asm "push self\npush 1\nfetch_field"; end
+def ip; Ruby.asm "push self\npush 2\nfetch_field"; end
+def sp; Ruby.asm "push self\npush 3\nfetch_field"; end
+def block; Ruby.asm "push self\npush 4\nfetch_field"; end
+def raiseable; Ruby.asm "push self\npush 5\nfetch_field"; end
+def home; Ruby.asm "push self\npush 6\nfetch_field"; end
+def last_op; Ruby.asm "push self\npush 7\nfetch_field"; end
+def start_op; Ruby.asm "push self\npush 8\nfetch_field"; end
+end
+class Object
+def instance_variables; Ruby.asm "push self\npush 0\nfetch_field"; end
+end
+class Class
+def instance_variables; Ruby.asm "push self\npush 0\nfetch_field"; end
+def methods; Ruby.asm "push self\npush 1\nfetch_field"; end
+def name; Ruby.asm "push self\npush 2\nfetch_field"; end
+def constants; Ruby.asm "push self\npush 3\nfetch_field"; end
+def superclass; Ruby.asm "push self\npush 4\nfetch_field"; end
+def instance_fields; Ruby.asm "push self\npush 5\nfetch_field"; end
+end
+class MethodTable
+def instance_variables; Ruby.asm "push self\npush 0\nfetch_field"; end
+def keys; Ruby.asm "push self\npush 1\nfetch_field"; end
+def values; Ruby.asm "push self\npush 2\nfetch_field"; end
+def bins; Ruby.asm "push self\npush 3\nfetch_field"; end
+def entries; Ruby.asm "push self\npush 4\nfetch_field"; end
+def default; Ruby.asm "push self\npush 5\nfetch_field"; end
+end
+class Hash
+def instance_variables; Ruby.asm "push self\npush 0\nfetch_field"; end
+def keys; Ruby.asm "push self\npush 1\nfetch_field"; end
+def values; Ruby.asm "push self\npush 2\nfetch_field"; end
+def bins; Ruby.asm "push self\npush 3\nfetch_field"; end
+def entries; Ruby.asm "push self\npush 4\nfetch_field"; end
+def default; Ruby.asm "push self\npush 5\nfetch_field"; end
+end
+class IO
+def descriptor; Ruby.asm "push self\npush 0\nfetch_field"; end
+end
+class SymbolTable
+def instance_variables; Ruby.asm "push self\npush 0\nfetch_field"; end
+def symbols; Ruby.asm "push self\npush 1\nfetch_field"; end
+def strings; Ruby.asm "push self\npush 2\nfetch_field"; end
+end
+class String
+def bytes; Ruby.asm "push self\npush 0\nfetch_field"; end
+def characters; Ruby.asm "push self\npush 1\nfetch_field"; end
+def encoding; Ruby.asm "push self\npush 2\nfetch_field"; end
+def data; Ruby.asm "push self\npush 3\nfetch_field"; end
+end
+class MethodContext
+def instance_variables; Ruby.asm "push self\npush 0\nfetch_field"; end
+def sender; Ruby.asm "push self\npush 1\nfetch_field"; end
+def ip; Ruby.asm "push self\npush 2\nfetch_field"; end
+def sp; Ruby.asm "push self\npush 3\nfetch_field"; end
+def block; Ruby.asm "push self\npush 4\nfetch_field"; end
+def raiseable; Ruby.asm "push self\npush 5\nfetch_field"; end
+def method; Ruby.asm "push self\npush 6\nfetch_field"; end
+def bytecodes; Ruby.asm "push self\npush 7\nfetch_field"; end
+def literals; Ruby.asm "push self\npush 8\nfetch_field"; end
+def receiver; Ruby.asm "push self\npush 9\nfetch_field"; end
+def locals; Ruby.asm "push self\npush 10\nfetch_field"; end
+def argcount; Ruby.asm "push self\npush 11\nfetch_field"; end
+def name; Ruby.asm "push self\npush 12\nfetch_field"; end
+end
+class Module
+def instance_variables; Ruby.asm "push self\npush 0\nfetch_field"; end
+def methods; Ruby.asm "push self\npush 1\nfetch_field"; end
+def name; Ruby.asm "push self\npush 2\nfetch_field"; end
+def constants; Ruby.asm "push self\npush 3\nfetch_field"; end
+end
View
63 kernel/class.rb
@@ -0,0 +1,63 @@
+class Class
+ def allocate
+ Ruby.primitive :allocate
+ end
+
+ def new(*arg)
+ obj = self.allocate()
+ obj.initialize(*arg)
+ return obj
+ end
+
+ def ancestors
+ out = [self]
+ sup = direct_superclass()
+ while sup
+ out << sup
+ sup = sup.direct_superclass()
+ end
+ return out
+ end
+
+ def <(other)
+ ancestors.include?(other)
+ end
+
+ def ===(inst)
+ # Could call kind_of?, but the body of kind_of does this exact
+ # thing.
+ inst.class < self
+ end
+
+ def alias_method(nw,cur)
+ meth = methods[cur]
+ unless meth
+ exc = NoMethodError.new("No method by the name of '#{cur}' for #{self}")
+ raise exc
+ end
+
+ methods[nw] = meth
+ return meth
+ end
+
+ def instance_methods
+ methods.keys
+ end
+
+ def superclass
+ cls = direct_superclass()
+ return nil unless cls
+ while cls and cls.class == IncludedModule
+ cls = cls.direct_superclass()
+ end
+ return cls
+ end
+
+ def direct_superclass
+ Ruby.asm "push self\npush 4\nfetch_field"
+ end
+
+ def instance_fields=(num)
+ put 5, num
+ end
+end
View
95 kernel/context.rb
@@ -0,0 +1,95 @@
+
+class MethodContext
+ def self.current
+ cur = nil
+ Ruby.asm "push_context\nset cur\n"
+ return cur.sender
+ end
+
+ def to_s
+ "#<#{self.class}:0x#{self.object_id.to_s(16)} #{receiver}##{name} #{file}:#{line}>"
+ end
+
+ def file
+ method.file
+ end
+
+ def lines
+ method.lines
+ end
+
+ def line
+ i = self.ip
+ self.lines.each do |t|
+ start = t.at(0)
+ nd = t.at(1)
+ op = t.at(2)
+ if i >= start and i < nd
+ return op
+ end
+ end
+ return 0
+ end
+end
+
+class BlockContext
+ def call(*args)
+ if args.total == 1
+ block_call args[0]
+ else
+ block_call args.tuple
+ end
+ end
+ def block_call(args)
+ Ruby.primitive :block_call
+ end
+end
+
+class Proc
+
+ Ruby.asm "push self\npush 2\npush 5\nstore_field"
+
+ def initialize(blk)
+ put 1, blk
+ end
+
+ def block
+ Ruby.asm "push 1\npush self\nfetch_field"
+ end
+
+ def call(*args)
+ obj = block.dup
+ obj.call(*args)
+ end
+end
+
+class Backtrace
+ def initialize
+ put 0, {}
+ @frames = []
+ end
+
+ def show
+ #strs = @frames.map do |ary|
+ # "#{ary[0]}##{ary[1]} at #{ary[2]}:#{ary[3]}"
+ #end
+ strs = @frames.join("\n")
+ return strs
+ strs.join("\n")
+ end
+
+ def fill_from(ctx)
+ while ctx
+ str = "#{ctx.receiver.to_s}##{ctx.name} at #{ctx.file}:#{ctx.line}"
+ @frames << str
+ ctx = ctx.sender
+ end
+ end
+
+ def self.backtrace
+ obj = new()
+ ctx = MethodContext.current.sender
+ obj.fill_from ctx
+ return obj
+ end
+end
View
106 kernel/core.rb
@@ -0,0 +1,106 @@
+
+module Kernel
+ def puts(obj)
+ STDOUT.puts obj.to_s
+ end
+
+ def p(obj)
+ STDOUT.puts obj.inspect
+ end
+
+ def print(obj)
+ STDOUT.write obj.to_s
+ end
+
+ def raise(exc, msg=nil)
+ if msg
+ cls = exc
+ exc = cls.new(msg)
+ end
+ Ruby.asm "push exc\nraise_exc"
+ end
+
+end
+
+class InvalidIndex < Exception
+end
+
+class Object
+ def initialize
+ put 0, {}
+ end
+
+ def at(idx)
+ Ruby.primitive :at
+ exc = InvalidIndex.new("Could not access index #{idx} of #{self}")
+ raise exc
+ end
+
+ def put(idx, val)
+ Ruby.primitive :put
+ exc = InvalidIndex.new("Could not write to index #{idx} of #{self}")
+ raise exc
+ end
+
+ def fields
+ Ruby.primitive :fields
+ end
+
+ def class
+ Ruby.primitive :logical_class
+ end
+
+ def kind_of?(cls)
+ self.class < cls
+ end
+
+ def object_id
+ Ruby.primitive :object_id
+ end
+
+ def hash
+ Ruby.primitive :hash_object
+ end
+
+ def copy_from(other)
+ Ruby.primitive :dup_into
+ end
+
+ def dup
+ nw = self.class.allocate
+ nw.copy_from(self)
+ return nw
+ end
+
+ def ==(other)
+ object_id == other.object_id
+ end
+
+ def to_s
+ "#<#{self.class.name}:0x#{self.object_id.to_s(16)}>"
+ end
+
+ def inspect
+ to_s
+ end
+
+ def nil?
+ false
+ end
+
+ def undef?
+ false
+ end
+end
+
+class TrueClass
+ def to_s
+ "true"
+ end
+end
+
+class FalseClass
+ def to_s
+ "false"
+ end
+end
View
16 kernel/exception.rb
@@ -0,0 +1,16 @@
+class Exception
+ def initialize(message)
+ ctx = MethodContext.current.sender.sender.sender
+ # puts "EXCEPTION: #{ctx}"
+ put 0, message
+ put 1, Backtrace.backtrace
+ end
+
+ def backtrace
+ at(1)
+ end
+
+ def message
+ at(0)
+ end
+end
View
45 kernel/fixnum.rb
@@ -0,0 +1,45 @@
+class Fixnum
+ def ==(o)
+ Ruby.primitive :equal
+ end
+
+ def +(o)
+ Ruby.primitive :add
+ end
+
+ def -(o)
+ Ruby.primitive :sub
+ end
+
+ def <=>(o)
+ Ruby.primitive :compare
+ end
+
+ def <(o)
+ (self <=> o) == -1
+ end
+
+ def <=(o)
+ comp = (self <=> o)
+ return true if comp == 0 or comp == -1
+ return false
+ end
+
+ def >(o)
+ (self <=> o) == 1
+ end
+
+ def >=(o)
+ comp = (self <=> o)
+ return true if comp == 0 or comp == 1
+ return false
+ end
+
+ def to_s(base=10)
+ based_to_s(base)
+ end
+
+ def based_to_s(base)
+ Ruby.primitive :fixnum_to_s
+ end
+end
View
76 kernel/hash.rb
@@ -0,0 +1,76 @@
+class Hash
+ def self.new(default=nil,&block)
+ hsh = {}
+ hsh.put 5, (default or block)
+ return hsh
+ end
+
+ def get_by_hash(hsh, key)
+ Ruby.primitive :hash_get
+ exc = ArgumentError.new("Unable to fetch hash element for '#{key}' (#{hsh})")
+ raise exc
+ end
+
+ def set_by_hash(hsh, key, val)
+ Ruby.primitive :hash_set
+ end
+
+ def default
+ at(5)
+ end
+
+ def [](key)
+ out = get_by_hash key.hash, key
+ if out.undef?
+ dfl = self.default
+ return nil unless dfl
+ if BlockContext === dfl
+ out = dfl.call(self, key)
+ else
+ out = dfl
+ self[key] = out
+ end
+ end
+ return out
+ end
+
+ def []=(key, val)
+ set_by_hash key.hash, key, val
+ end
+
+ def values_data
+ at(2)
+ end
+
+ def keys
+ out = []
+ values_data.each do |tup|
+ while tup
+ out << tup.at(1)
+ tup = tup.at(3)
+ end
+ end
+ return out
+ end
+
+ def values
+ out = []
+ values_data.each do |tup|
+ while tup
+ out << tup.at(2)
+ tup = tup.at(3)
+ end
+ end
+ return out
+ end
+
+ def each
+ values_data.each do |tup|
+ while tup
+ yield tup.at(0), tup.at(1)
+ tup = tup.at(3)
+ end
+ end
+ return self
+ end
+end
View
20 kernel/io.rb
@@ -0,0 +1,20 @@
+
+class IOError < Exception
+end
+
+class IO
+ def puts(str)
+ write str
+ write "\n"
+ end
+
+ def write(str)
+ Ruby.primitive :io_write
+ exc = IOError.new("Unable to write '#{str}' via #{self}")
+ raise exc
+ end
+
+ def read(size)
+ Ruby.primitive :io_read
+ end
+end
View
10 kernel/method_missing.rb
@@ -0,0 +1,10 @@
+def self.to_s
+ "main"
+end
+
+class Object
+ def method_missing(meth, *args)
+ exc = NoMethodError.new "No method '#{meth}' on a #{self.class}."
+ raise exc
+ end
+end
View
51 kernel/module.rb
@@ -0,0 +1,51 @@
+class Module
+ def name
+ at(2)
+ end
+
+ def to_s
+ name.to_s
+ end
+
+ def include(mod)
+ im = IncludedModule.new(mod)
+ im.attach_to self
+ end
+end
+
+class IncludedModule < Module
+ self.instance_fields = 6
+
+ def initialize(mod)
+ put 0, mod.instance_variables
+ put 1, mod.methods
+ put 2, mod.name
+ put 5, mod
+ end
+
+ def old_to_s
+ "#<IM>"
+ end
+
+ def module
+ at(5)
+ end
+
+ def attach_to(cls)
+ put 4, cls.superclass
+ cls.put 4, self
+ end
+
+ def superclass
+ at(4)
+ end
+
+ def direct_superclass
+ Ruby.asm "push self\npush 4\nfetch_field"
+ end
+
+end
+
+class Object
+ include Kernel
+end
View
19 kernel/nil.rb
@@ -0,0 +1,19 @@
+class NilClass
+ def to_s
+ "nil"
+ end
+
+ def nil?
+ true
+ end
+end
+
+class UndefClass
+ def undef?
+ true
+ end
+
+ def to_s
+ "undef"
+ end
+end
View
17 kernel/string.rb
@@ -0,0 +1,17 @@
+class String
+ def to_s
+ self
+ end
+
+ def <<(other)
+ out = nil
+ Ruby.asm "push other\npush self\nstring_append\nset out"
+ return out
+ end
+
+ def dup
+ out = nil
+ Ruby.asm "push self\nstring_dup\nset out"
+ return out
+ end
+end
View
27 kernel/symbol.rb
@@ -0,0 +1,27 @@
+class SymbolTable
+ def symbols
+ at(1)
+ end
+
+ def strings
+ at(2)
+ end
+
+ def symbol_to_string(sym)
+ symbols.at(sym.index)
+ end
+end
+
+class Symbol
+ def index
+ Ruby.primitive :symbol_index
+ end
+
+ def to_s
+ Symbols.symbol_to_string(self)
+ end
+
+ def inspect
+ ":#{to_s}"
+ end
+end
View
41 kernel/tuple.rb
@@ -0,0 +1,41 @@
+class Tuple
+ def self.new(cnt)
+ Ruby.primitive :allocate_count
+ end
+
+ def to_s
+ "#<Tuple:0x#{object_id.to_s(16)} #{fields} elements>"
+ end
+
+ def each
+ i = 0
+ t = fields
+ while i < t
+ yield self.at(i)
+ i += 1
+ end
+ self
+ end
+
+ def inspect
+ "#<Tuple: #{join(", ")}>"
+ end
+
+ def join(sep)
+ join_upto(sep, fields)
+ end
+
+ def join_upto(sep, count)
+ str = ""
+ return str if count == 0
+ count -= 1
+ i = 0
+ while i < count
+ str << at(i).inspect
+ str << sep.dup
+ i += 1
+ end
+ str << at(count).inspect
+ return str
+ end
+end
View
15 lib/bytecode/assembler.rb
@@ -96,6 +96,7 @@ def assemble(str)
end
def exceptions_as_tuple
+ return RObject.nil if @exceptions.empty?
excs = sorted_exceptions()
tuple_of_int_tuples(excs)
end
@@ -165,6 +166,8 @@ def parse_command(kind, args)
case kind
when :line
if ent = @source_lines.last
+ # If we're already tracking this line, don't add anything.
+ return if ent.last == args.to_i
ent[1] = @current_op - 1
else
@source_lines << [0, @current_op, 0]
@@ -286,7 +289,7 @@ def parse_lvar(what)
cnt = find_local(what.to_sym)
return cnt
elsif /(^[a-z_][A-Za-z0-9_]*):(\d+)$/.match(what)
- name = $1
+ name = $1.to_sym
cnt = $2.to_i
@locals[name] = cnt
return cnt
@@ -459,7 +462,15 @@ def parse_operation(*parts)
idx = find_literal(sym)
@current_op += 5
if args = parts.shift
- meth = (op == :send ? :send_stack : :send_stack_with_block)
+ if args.to_i.to_s == args
+ meth = (op == :send ? :send_stack : :send_stack_with_block)
+ elsif args == "+"
+ meth = :send_with_arg_register
+ @output << [meth, idx]
+ return
+ else
+ raise "Unknown send argument type '#{args}'"
+ end
@output << [meth, idx, args.to_i]
@current_op += 4
else
View
289 lib/bytecode/compiler.rb
@@ -4,6 +4,7 @@
require 'translation/local_scoping'
require 'sexp/composite_processor'
require 'bytecode/assembler'
+require 'translation/states'
module Bytecode
@@ -13,6 +14,8 @@ def initialize(name)
@assembly = ""
@literals = []
@primitive = nil
+ @file = nil
+ @required = 0
end
def add_literal(obj)
@@ -21,7 +24,8 @@ def add_literal(obj)
return idx
end
- attr_accessor :name, :assembly, :literals, :primitive
+ attr_accessor :name, :assembly, :literals, :primitive, :file
+ attr_accessor :required
def to_cmethod
asm = Bytecode::Assembler.new(@literals)
@@ -30,6 +34,8 @@ def to_cmethod
bc = enc.encode_stream stream
lcls = asm.number_of_locals
cmeth = Rubinius::CompiledMethod.from_string bc, lcls
+ cmeth.required = RObject.wrap(@required)
+ cmeth.exceptions = asm.exceptions_as_tuple
if @primitive.kind_of? Symbol
idx = CPU::Primitives.name_to_index(@primitive)
@@ -39,6 +45,16 @@ def to_cmethod
end
cmeth.literals = encode_literals
+ if @file
+ # Log.info "Method #{@name} is contained in #{@file}."
+ sym = Rubinius::String.new(@file).to_sym
+ cmeth.file = sym
+ else
+ # Log.info "Method #{@name} is contained in an unknown place."
+ cmeth.file = RObject.nil
+ end
+
+ cmeth.lines = asm.lines_as_tuple
return cmeth
end
@@ -55,6 +71,8 @@ def encode_literals
# puts "Encoded #{lit.inspect} as #{out.inspect}"
when Bytecode::MethodDescription
out = lit.to_cmethod
+ when String
+ out = Rubinius::String.new(lit)
else
raise "Unable to encode literal: #{lit.inspect}"
end
@@ -67,23 +85,33 @@ def encode_literals
end
class Compiler
-
- def compile(sx, name)
+ def compile(sx, name, extra=[])
+ state = RsLocalState.new
+ nx = fully_normalize(sx, extra, state)
meth = MethodDescription.new(name)
- comp = CompositeSexpProcessor.new
- # Convert the block args to the new rules...
- comp << RsLocalScoper.new
- # normalize the rest of the sexp...
- comp << RsNormalizer.new
- # and compile it!
- pro = Processor.new(self, meth)
- comp << pro
- comp.process sx
+
+ pro = Processor.new(self, meth, state)
+ pro.process nx
return pro
end
- def compile_as_method(sx, name)
- pro = compile(sx, name)
+ def fully_normalize(x, extra=[], state=RsLocalState.new)
+ extra.each { |a| state.local(a) }
+ comp = CompositeSexpProcessor.new
+ comp << RsNormalizer.new(state, true)
+ comp << RsLocalScoper.new(state)
+
+ return comp.process(x)
+ end
+
+ def compile_as_method(sx, name, extra=[])
+ begin
+ pro = compile(sx, name, extra)
+ rescue UnknownNodeError => e
+ exc = RuntimeError.new "Unable to compile '#{name}', compiled error detected. '#{e.message}'"
+ exc.set_backtrace e.backtrace
+ raise exc
+ end
pro.finalize("ret")
return pro.method
end
@@ -95,7 +123,7 @@ def compile_as_script(sx, name)
end
class Processor < SexpProcessor
- def initialize(cont, meth)
+ def initialize(cont, meth, state)
super()
self.require_expected = false
self.strict = true
@@ -104,6 +132,7 @@ def initialize(cont, meth)
@method = meth
@output = ""
@unique_id = 0
+ @state = state
end
attr_reader :method
@@ -148,7 +177,9 @@ def process_self(x)
def process_and(x)
process x.shift
lbl = unique_lbl()
+ add "dup"
add "gif #{lbl}"
+ add "pop"
process x.shift
add "#{lbl}:"
end
@@ -156,7 +187,9 @@ def process_and(x)
def process_or(x)
process x.shift
lbl = unique_lbl()
+ add "dup"
add "git #{lbl}"
+ add "pop"
process x.shift
add "#{lbl}:"
end
@@ -235,6 +268,10 @@ def process_block(x)
end
def process_scope(x)
+ if x.first.empty?
+ x.clear
+ return []
+ end
out = process x.shift
x.clear
out
@@ -277,6 +314,10 @@ def process_array(x)
add "make_array #{sz}"
end
+ def process_zarray(x)
+ add "make_array 0"
+ end
+
def process_hash(x)
sz = x.size
y = x.reverse
@@ -296,9 +337,10 @@ def process_argscat(x)
ary = x.shift
itm = x.shift
process itm
- add "cast_array"
- add "push_array"
ary.shift
+
+ add "cast_array_for_args #{ary.size}"
+ add "push_array"
ary.reverse.each do |i|
process i
end
@@ -309,6 +351,15 @@ def process_ivar(x)
add "push #{name}"
end
+ def process_gvar(x)
+ kind = x.shift
+ if kind == :$!
+ add "push_exception"
+ else
+ raise "Unknown gvar #{kind}"
+ end
+ end
+
def process_iasgn(x)
name = x.shift
process x.shift
@@ -380,12 +431,14 @@ def process_class(x)
end
add "dup"
+ Log.debug "Compiling class '#{name}'"
meth = @compiler.compile_as_method body, :__class_init__
idx = @method.add_literal meth
meth.assembly = "push self\nset_encloser\n" + meth.assembly
add "push_literal #{idx}"
add "swap"
add "attach __class_init__"
+ add "pop"
add "send __class_init__"
add "pop"
add "set_encloser"
@@ -419,6 +472,7 @@ def process_module(x)
add "push_literal #{idx}"
add "swap"
add "attach __module_init__"
+ add "pop"
add "send __module_init__"
add "pop"
add "set_encloser"
@@ -440,7 +494,7 @@ def process_rescue(x)
do_resbody res, rr, fin
add "#{rr}:"
add "push_exception"
- add "raise"
+ add "raise_exc"
add "#{fin}:"
# Since this is always the end if the exception has either
# not occured or has been correctly handled, we clear the current
@@ -524,16 +578,18 @@ def process_masgn(x)
rhs = x.shift
splat = x.shift
source = x.shift
-
- # The sexp has 2 nodes that do the same thing. It's annoying.
- if source[0] == :to_ary or source[0] == :splat
- process source.last
- add "cast_tuple"
- elsif source[0] == :array
- handle_array_masgn rhs, splat, source
- return
- else
- process source
+
+ if source
+ # The sexp has 2 nodes that do the same thing. It's annoying.
+ if source[0] == :to_ary or source[0] == :splat
+ process source.last
+ add "cast_tuple"
+ elsif source[0] == :array
+ handle_array_masgn rhs, splat, source
+ return
+ else
+ process source
+ end
end
rhs.shift # get rid of :array
@@ -568,17 +624,37 @@ def handle_array_masgn(rhs, splat, source)
def detect_primitive(body)
cl = body[1][1]
- if cl.kind_of?(Array) and cl[0,3] == [:call, [:const, :Ruby], :primitive]
+ if cl.first == :newline
+ cl = cl.last
+ end
+ found = detect_special(:primitive, cl)
+ if found
+ b = body[1]
+ b.shift
+ b.shift
+ # Detect that a primitive is used with no
+ # handling for if the primitive fails.
+ if b.empty?
+ # puts "Using default primitive fallback for #{found}"
+ msg = [:str, "Primitive #{found} failed."]
+ exc = [:call, [:const, :ArgumentError], :new, [:array, msg]]
+ b.unshift [:call, [:self], :raise, [:array, exc]]
+ end
+ b.unshift :block
+ body[1] = b
+ return found
+ end
+
+ return nil
+ end
+
+ def detect_special(kind, cl)
+ if cl.kind_of?(Array) and cl[0,3] == [:call, [:const, :Ruby], kind]
args = cl.last
args.shift
if args.size == 1
ary = args.last
- if ary.kind_of? Array and ary[0] == :lit
- b = body[1]
- b.shift
- b.shift
- b.unshift :block
- body[1] = b
+ if ary.kind_of? Array and [:lit, :str].include?(ary[0])
return ary.last
end
end
@@ -587,24 +663,40 @@ def detect_primitive(body)
return nil
end
- def process_defn(x)
+ def process_defs(x)
+ s = @output
+ str = ""
+ @output = str
+ process(x.shift)
+ @output = s
+ process_defn x, "attach_method", str
+ end
+
+ def process_defn(x, kind="add_method", recv="push_self")
name = x.shift
args = x.shift
body = x.shift
+ Log.debug " ==> Compiling '#{name}'"
+
prim = detect_primitive(body)
- meth = @compiler.compile_as_method body, name
+ extra = args[1].dup
+ if args[3]
+ extra << args[3].first
+ end
+ meth = @compiler.compile_as_method body, name, extra
meth.primitive = prim if prim
idx = @method.add_literal meth
- add "push_self"
add "push_literal #{idx}"
- add "add_method #{name}"
+ add recv
+ add "#{kind} #{name}"
str = ""
required = args[1].size
args[1].each do |var|
str << "set #{var}\n"
end
+
max = min = args[1].size
defaults = args[4]
@@ -630,6 +722,9 @@ def process_defn(x)
if splat
str << "make_rest #{required}\nset #{splat.first}\n"
max = 0
+ req = -1
+ else
+ req = min
end
if args.last and args.last.first == :block_arg
@@ -639,19 +734,39 @@ def process_defn(x)
str << "push_block\nset #{name}:#{idx}\n"
end
+ meth.required = req
+
str = "check_argcount #{min} #{max}\n" + str
meth.assembly = str + meth.assembly
end
def process_call(x, block=false)
+
+ x.unshift :call
+ if asm = detect_special(:asm, x)
+ add asm
+ x.clear
+ return
+ end
+ x.shift
+
recv = x.shift
meth = x.shift
args = x.shift
if args
- args.shift
- args.reverse.each { |a| process(a) }
+ if args.first == :argscat
+ process(args)
+ sz = "+"
+ add "push nil"
+ else
+ args.shift
+ args.reverse.each { |a| process(a) }
+ sz = args.size
+ end
+ else
+ sz = nil
end
if block
@@ -663,15 +778,13 @@ def process_call(x, block=false)
process recv
- if args
- sz = args.size
- else
- sz = nil
- end
-
add "#{op} #{meth} #{sz}"
end
+ def process_attrasgn(x)
+ process_call x
+ end
+
def process_block_pass(x)
blk = x.shift
cl = x.shift
@@ -700,6 +813,88 @@ def process_block_iter(x)
add "#{one}: soft_return"
set_label two
end
+
+ def process_str(x)
+ str = x.shift
+ cnt = @method.add_literal str
+ add "push_literal #{cnt}"
+ # The string dup is strings work the same as the do in CRuby,
+ # ie that every declaration of them is a new one.
+ add "string_dup"
+ end
+
+ def process_static_str(x)
+ str = x.shift
+ cnt = @method.add_literal str
+ add "push_literal #{cnt}"
+ # We don't dup it, meaning that every call will get the same one.
+ # This gives ruby a decent static string buffer object.
+ end
+
+ def process_evstr(x)
+ process x.shift
+ add "send to_s"
+ end
+
+ def process_dstr(x)
+ str = x.shift
+ cnt = 0
+ while y = x.pop
+ process y
+ cnt += 1
+ end
+ lit = @method.add_literal str
+ add "push_literal #{lit}"
+ add "string_dup"
+ cnt.times { add "string_append" }
+ end
+
+ def process_yield(x)
+ args = x.shift
+
+ if args
+ kind = args.first
+ if kind == :array
+ args.shift
+ args.reverse.each { |a| process(a) }
+ sz = args.size
+ else
+ process(args)
+ sz = 1
+ end
+ else
+ sz = 0
+ end
+
+ x.shift
+
+ add "push_block"
+ add "send call #{sz}"
+ end
+
+ def process_next(x)
+ val = x.shift
+ if val
+ process(val)
+ end
+ add "soft_return"
+ end
+
+ def process_newline(x)
+ line = x.shift
+ @method.file = x.shift
+ add "\#line #{line}"
+ process(x.shift)
+ end
+
+ def process_alias(x)
+ cur = x.shift
+ nw = x.shift
+ add "push :#{cur}"
+ add "push :#{nw}"
+ add "push self"
+ add "send alias_method 2"
+ end
end
end
end
View
44 lib/bytecode/constructor.rb
@@ -13,13 +13,20 @@ def initialize(cpu)
@cpu = cpu
@enc = Bytecode::InstructionEncoder.new
@sm = SimpleMarshal.new
+ @newlines = true
end
+ attr_accessor :newlines
+
def convert_to_sexp(code)
- syd = SydneyParser.load_string(code)
- return syd.sexp
+ if IO === code
+ syd = SydneyParser.load_file code
+ else
+ syd = SydneyParser.load_string(code.to_s)
+ end
+ return syd.sexp(false, @newlines)
end
-
+
def compile(code)
sexp = convert_to_sexp(code)
comp = Bytecode::Compiler.new
@@ -29,8 +36,7 @@ def compile(code)
def compile_file(path)
fd = File.open(path)
- code = fd.read
- meth = compile(code)
+ meth = compile(fd)
fd.close
return meth
end
@@ -59,6 +65,28 @@ def file_newer?(comp, orig)
return false
end
+ def refresh_file(path)
+ cp = compiled_path(path)
+ return if file_newer?(cp, path)
+ compile_and_save(path)
+ end
+
+ def clear_precompiled(dir)
+ Dir["#{dir}/*.rbc"].each do |path|
+ File.unlink path
+ end
+ end
+
+ def compile_and_save(path)
+ cp = compiled_path(path)
+ Log.info "(Compiling #{path}..)"
+ cm = compile_file(path)
+ fd = File.open(cp, "w")
+ fd << @sm.marshal(cm)
+ fd.close
+ return cm
+ end
+
def load_file(path)
cp = compiled_path(path)
if file_newer?(cp, path)
@@ -68,11 +96,7 @@ def load_file(path)
return @sm.unmarshal(str)
end
- cm = compile_file(path)
- fd = File.open(cp, "w")
- fd << @sm.marshal(cm)
- fd.close
- return cm
+ return compile_and_save(path)
end
end
end
View
21 lib/bytecode/encoder.rb
@@ -63,7 +63,13 @@ class InvalidOpCode < RuntimeError
:activate_method,
:push_cpath_top,
:check_argcount,
- :passed_arg
+ :passed_arg,
+ :string_append,
+ :string_dup,
+ :set_args,
+ :get_args,
+ :send_with_arg_register,
+ :cast_array_for_args
]
IntArg = [
@@ -92,7 +98,9 @@ class InvalidOpCode < RuntimeError
:activate_method,
:send_stack_with_block,
:check_argcount,
- :passed_arg
+ :passed_arg,
+ :send_with_arg_register,
+ :cast_array_for_args
]
TwoInt = [
@@ -126,7 +134,10 @@ def encode(kind, *args)
raise InvalidOpCode, "Unknown opcode '#{kind}'"
end
- process_args(kind, opcode, args)
+ orig = args.dup
+ out = process_args(kind, opcode, args)
+ # puts "#{kind} (#{orig.inspect}) encoded as: #{out.inspect}"
+ out
end
def process_args(kind, opcode, args)
@@ -137,7 +148,7 @@ def process_args(kind, opcode, args)
unless Numeric === int
raise "#{kind} expects an integer only."
end
- str << [int].pack("I")
+ str << [int].pack("i")
end
if TwoInt.include?(kind)
@@ -145,7 +156,7 @@ def process_args(kind, opcode, args)
unless Numeric === int
raise "#{kind} expects an integer only."
end
- str << [int].pack("I")
+ str << [int].pack("i")
end
unless args.empty?
View
35 lib/cpu/bootstrap.rb
@@ -26,6 +26,8 @@ def bootstrap
Global.methtbl = Rubinius::MethodTable.class_obj Global.hash
Global.cmethod = Rubinius::CompiledMethod.class_obj Global.object
+ Global.io = Rubinius::IO.class_obj Global.object
+
Global.symbols = Rubinius::SymbolTable.new
Global.object.setup "Object"
@@ -40,15 +42,17 @@ def bootstrap
Global.string.setup "String"
Global.symtbl.setup "SymbolTable"
Global.methtbl.setup "MethodTable"
+ Global.cmethod.setup "CompiledMethod"
+ Global.io.setup "IO"
sym = Rubinius::String.new("Symbols").to_sym
Global.object.const_set sym, Global.symbols
- Rubinius::Class.create_normal "NilClass", Global.object, 0
- Rubinius::Class.create_normal "TrueClass", Global.object, 0
- Rubinius::Class.create_normal "FalseClass", Global.object, 0
- Rubinius::Class.create_normal "Fixnum", Global.object, 0
- Rubinius::Class.create_normal "UndefClass", Global.object, 0
+ Global.nil_class = Rubinius::Class.create_normal "NilClass", Global.object, 0
+ Global.true_class = Rubinius::Class.create_normal "TrueClass", Global.object, 0
+ Global.false_class = Rubinius::Class.create_normal "FalseClass", Global.object, 0
+ Global.fixnum = Rubinius::Class.create_normal "Fixnum", Global.object, 0
+ Global.undef_class = Rubinius::Class.create_normal "UndefClass", Global.object, 0
bootstrap_contexts
initialize_context
@@ -69,16 +73,17 @@ def define_class(name, sup=Global.object, fields=0)
end
def bootstrap_exceptions
- exc = define_class "Exception", Global.object, 1
- fat = define_class "fatal", exc, 1
- std = define_class "StandardError", exc, 1
- arg = define_class "ArgumentError", std, 1
- ner = define_class "NameError", std, 1
- nme = define_class "NoMethodError", ner, 1
- syn = define_class "SyntaxError", exc, 1
- loe = define_class "LoadError", exc, 1
- run = define_class "RuntimeError", std, 1
- sys = define_class "SystemCallError", std, 1
+ exc_fields = 2
+ exc = define_class "Exception", Global.object, exc_fields
+ fat = define_class "fatal", exc, exc_fields
+ std = define_class "StandardError", exc, exc_fields
+ arg = define_class "ArgumentError", std, exc_fields
+ ner = define_class "NameError", std, exc_fields
+ nme = define_class "NoMethodError", ner, exc_fields
+ syn = define_class "SyntaxError", exc, exc_fields
+ loe = define_class "LoadError", exc, exc_fields
+ run = define_class "RuntimeError", std, exc_fields
+ sys = define_class "SystemCallError", std, exc_fields
Global.exc_arg = arg
Global.exc_loe = loe
View
243 lib/cpu/instructions.rb
@@ -24,7 +24,8 @@ def dispatch
meth = Bytecode::InstructionEncoder::OpCodes[op]
raise "Unknown opcode '#{op}'" unless meth
- Log.debug "on #{op} / #{meth} (#{@ip})"
+ # Log.debug "on #{op} / #{meth} (#{@ip})"
+ # Log.debug " =(stack)=> #{print_stack}"
@ip += 1
send(meth)
return true
@@ -33,7 +34,7 @@ def dispatch
IntSize = 4
def next_int
- int = @data[@ip, IntSize].unpack("I").first
+ int = @data[@ip, IntSize].unpack("i").first
@ip += IntSize
return int
end
@@ -41,6 +42,18 @@ def next_int
def noop
end
+ def push_int
+ int = next_int()
+ begin
+ obj = RObject.wrap(int)
+ rescue ArgumentError
+ p int
+ p @data[@ip-IntSize,IntSize]
+ raise
+ end
+ stack_push obj.address
+ end
+
def push_nil
stack_push CPU::NIL
end
@@ -63,6 +76,7 @@ def push_literal
end
def push_self
+ # Log.debug "Pushed self, which is #{@self.address}"
push_object @self
end
@@ -70,6 +84,7 @@ def push_local
idx = next_int
val = @locals.at(idx)
push_object val
+ # Log.debug "Pushed local at #{idx} (is #{val.address})"
end
def push_exception
@@ -86,7 +101,7 @@ def push_block
def push_ivar
sym = @literals.at next_int
- obj = RObject.new stack_pop
+ obj = @self
obj.as :object
val = obj.get_ivar sym
push_object val
@@ -118,8 +133,8 @@ def store_field
end
def fetch_field
- idx = obj_top()
- obj = obj_top()
+ idx = pop_object()
+ obj = pop_object()
out = obj.at(idx.to_int)
@@ -179,8 +194,9 @@ def pop
def set_local
idx = next_int
- val = stack_top
+ val = stack_pop
@locals.put idx, RObject.new(val)
+ # Log.debug "Set local #{idx} to #{val}."
end
def make_array
@@ -214,6 +230,13 @@ def cast_array
obj = ary
end
push_object obj
+ return obj
+ end
+
+ def cast_array_for_args
+ @args = next_int()
+ ary = cast_array()
+ @args += ary.as(:array).total.to_int
end
def cast_tuple
@@ -247,7 +270,7 @@ def make_hash
def set_ivar
sym = @literals.at next_int
val = RObject.new stack_pop
- obj = RObject.new stack_pop
+ obj = @self
obj.as :object
obj.set_ivar sym, val
push_object val
@@ -255,10 +278,29 @@ def set_ivar
def push_const
sym = @literals.at next_int
+ @enclosing_class.as :module
hsh = @enclosing_class.constants
hsh.as :hash
+ sym.as :symbol
val = hsh.find(sym)
- push_object val
+ if val.nil?
+ @paths.each do |cls|
+ cls.as :module
+ hsh = cls.constants
+ hsh.as :hash
+ val = hsh.find(sym)
+ unless val.nil?
+ push_object val
+ return
+ end
+ end
+ else
+ # Log.debug "Pushed #{val.address} as const #{sym.as_string}"
+ push_object val
+ return
+ end
+
+ raise "Unable to find const #{sym.as_string}"
end
def set_const
@@ -299,7 +341,13 @@ def push_cpath_top
end
def set_encloser
- @enclosing_class = pop_object
+ cls = pop_object
+ if @paths.first == cls
+ @paths.shift
+ else
+ @paths.unshift @enclosing_class
+ end
+ @enclosing_class = cls
end
def push_encloser
@@ -324,8 +372,10 @@ def open_class_at(klass, sup)
hsh = klass.constants
hsh.as :hash
+ sym.as :symbol
exist = hsh.find sym
if exist.nil?
+ # Log.debug "Creating class #{sym.as_string}"
if sup.nil?
sup = CPU::Global.object
end
@@ -340,6 +390,7 @@ def open_class_at(klass, sup)
obj.superclass = sup
obj.instance_fields = sup.instance_fields
obj.metaclass.setup_fields
+ obj.setup_fields
hsh.set sym, obj
@@ -351,6 +402,8 @@ def open_class_at(klass, sup)
else
push_object exist
end
+
+ # Log.debug "Re-openning class #{sym.as_string}, #{exist.address}"
end
end
@@ -381,6 +434,7 @@ def open_module_at(klass)
obj.as :module
obj.name = sym
obj.metaclass
+ obj.setup_fields
hsh.set sym, obj
push_object obj
@@ -401,52 +455,72 @@ def attach_method
end
def add_method
- sym = @literals.at next_int
+ sym = @literals.at next_int
target = pop_object
method = pop_object
target.as :class
meths = target.methods
meths.as :hash
meths.set sym, method
+ sym.as :symbol
+ # puts "Added method #{sym.as_string} (#{sym.hash_int}) to #{target.address}."
push_object method
end
def find_method(obj, name)
- klass = obj.rclass
+ klass = obj.logical_class
hsh = klass.methods
hsh.as :hash
+ name.as :symbol
+ klass.as :class
+ # puts "Looking for #{name.as_string} in #{klass.as_string}, #{obj.as_string}"
meth = hsh.find(name)
while meth.nil?
klass = klass.superclass
+ klass.as :class
break if klass.nil?
+ # puts "Looking for #{name.as_string} (#{name.hash_int}) in #{klass.as_string}, #{obj.as_string}"
hsh = klass.methods
hsh.as :hash
meth = hsh.find(name)
end
+ if klass.reference? and klass.name.symbol?
+ # puts "Found #{name.as_string} in #{klass.name.as(:symbol).as_string}."
+ end
return meth
end
def locate_method(recv, sym)
- @method_missing = Rubinius::String.new("method_missing").to_sym
+ sym.as :symbol
+
+ missing = false
mo = find_method(recv, sym)
if mo.nil?
mo = find_method(recv, @method_missing)
+ missing = true
if mo.nil?
- raise "No method_missing found!"
+ raise "No method_missing found! #{sym.as_string} was lost!"
end
end
- return mo
+ mo.as :cmethod
+ return [mo, missing]
end
- def create_context(recv, mo)
+ def create_context(recv, mo, name)
mo.as :cmethod
- ctx = Rubinius::MethodContext.from_method(mo,
- @active_context)
+ if @active_context.nil?
+ sender = RObject.nil
+ else
+ sender = @active_context
+ end
+ ctx = Rubinius::MethodContext.from_method(mo, sender)
ctx.receiver = recv
+ ctx.name = name
+ ctx.sp = RObject.wrap(@sp)
return ctx
end
@@ -454,69 +528,85 @@ def activate_method
recv = pop_object
meth = pop_object
cnt = next_int
- goto_method recv, meth, cnt
+ goto_method recv, meth, cnt, RObject.nil
end
- def goto_method(recv, meth, count)
- ctx = create_context(recv, meth)
+ def goto_method(recv, meth, count, name)
+ ctx = create_context(recv, meth, name)
ctx.argcount = RObject.wrap count
activate_context ctx, ctx
end
-
- def send_method
+
+ def unified_send(recv, idx, args, block)
if @literals.nil?
raise "Literals are nil!"
end
- sym = @literals.at next_int
+ sym = @literals.at idx
- recv = pop_object
- mo = locate_method recv, sym
- ctx = create_context recv, mo
- ctx.argcount = RObject.wrap(0)
+ sym.as :symbol
- activate_context ctx, ctx
- end
-
- def send_stack
- if @literals.nil?
- raise "Literals are nil!"
+ # puts "Sending #{sym.as_string} (#{idx}) to #{recv.address}."
+
+ # Log.debug "stack is #{print_stack} for #{sym.as_string} (#{args} args)"
+
+ mo, missing = locate_method recv, sym
+ prim = mo.primitive.to_int
+ req = mo.required.to_int
+ if prim > -1 and (req < 0 or args == req)
+ push_object recv
+ if @primitives.perform(mo.primitive.to_int)
+ # Log.debug "Primitive response for #{sym.as_string} successful."
+ return
+ else
+ # Log.debug "Pritive response for #{sym.as_string} FAILED."
+ end
end
- sym = @literals.at next_int
- args = next_int()
- recv = pop_object
+ if missing
+ args += 1
+ push_object sym
+ end
- mo = locate_method recv, sym
- ctx = create_context recv, mo
+ ctx = create_context recv, mo, sym
+ # Log.debug "=> Created context #{ctx.address}"
ctx.argcount = RObject.wrap(args)
+ ctx.block = block
activate_context ctx, ctx
end
- def send_stack_with_block
- if @literals.nil?
- raise "Literals are nil!"
- end
- sym = @literals.at next_int
+ def send_method
+ unified_send pop_object, next_int, 0, RObject.nil
+ end
+
+ def send_stack
+ sym = next_int()
args = next_int()
+ unified_send pop_object, sym, args, RObject.nil
+ end
+ def send_stack_with_block
recv = pop_object
-
- mo = locate_method recv, sym
- ctx = create_context recv, mo
- ctx.argcount = RObject.wrap(args)
- ctx.block = pop_object
-
- activate_context ctx, ctx
+ blk = pop_object
+ sym = next_int()
+ args = next_int()
+ unified_send recv, sym, args, blk
end
+ def send_with_arg_register
+ recv = pop_object
+ blk = pop_object
+ sym = next_int()
+ unified_send recv, sym, @args, blk
+ end
+
# Returns directly back to the sender, regardless of the type.
# This is NOT a normal return which causes the home context
# to return. This is used for things like break and next inside
# a block. A block will always have one of these as it's last
# bytecode.
def soft_return
- return_to_sender
+ return_to_sender(false)
end
# caller return is this strange instruction that actually returns
@@ -540,22 +630,32 @@ def ret
def raise_exception(exc)
@exception = exc
+ # STDERR.puts "Exception raised: #{exc.at(0).as(:string).as_string}"
ctx = @active_context
+ # puts "Exception occurred, searching for handler..."
until ctx.nil?
-
+
+ # puts "Looking in #{ctx.address}, #{@exceptions.address}..."
# If the context isn't raisable, we just set the register
# and return. The idea is this allows external code to examine
# the exception and decide what it wants to do.
if ctx.raiseable.false?
+ puts "Hit non-raiseable ctx. stopping."
return
end
table = @exceptions
+
+ # If there is no table, then this method has no handlers.
+ if table.nil?
+ return_to_sender
+ ctx = @active_context
+ next
+ end
+
table.as :tuple
- raise "Invaild table" if table.nil?
-
cur = @ip
total = table.fields
target = 0
@@ -568,7 +668,8 @@ def raise_exception(exc)
end
end
- ctx = ctx.sender
+ return_to_sender(false)
+ ctx = @active_context
end
# TODO implement looking up the context chain for a handler.
@@ -595,6 +696,7 @@ def make_rest
req = next_int()
total = @active_context.argcount.to_int - req
ary = Rubinius::Array.new(total)
+ # Log.debug "Making rest of #{total} items (req=#{req})"
0.upto(total - 1) do |idx|
ary.set idx, pop_object
end
@@ -623,6 +725,30 @@ def passed_arg
end
end
+ def string_append
+ recv = pop_object
+ str = pop_object
+ recv.as :string
+ recv.append str
+ push_object recv
+ end
+
+ def string_dup
+ recv = pop_object
+ recv.as :string
+ out = Rubinius::String.new(recv.as_string)
+ push_object out
+ end
+
+ def set_args
+ int = pop_object.to_int
+ @args = int
+ end
+
+ def get_args
+ push_object RObject.wrap(@args)
+ end
+
private
def new_exception(klass, msg)
@@ -636,10 +762,5 @@ def new_exception(klass, msg)
def obj_top
RObject.new(stack_pop)
end
-
- def push_int
- obj = RObject.wrap(next_int)
- stack_push obj.address
- end
-
+
end
View
207 lib/cpu/primitives.rb
@@ -1,5 +1,6 @@
require 'cpu/