mirror of
https://github.com/mistergibson/gxg-web-client.git
synced 2026-10-19 07:39:44 -07:00
Initial Commit
This commit is contained in:
parent
356ad3315b
commit
81daf6f387
283 changed files with 130672 additions and 1 deletions
7
gxg/core/alias.rb
Normal file
7
gxg/core/alias.rb
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
module Rubinius
|
||||
class Alias
|
||||
attr_reader :original_exec
|
||||
attr_reader :original_module
|
||||
attr_reader :original_name
|
||||
end
|
||||
end
|
||||
810
gxg/core/alpha.rb
Normal file
810
gxg/core/alpha.rb
Normal file
|
|
@ -0,0 +1,810 @@
|
|||
# This is the beginning of loading Ruby code. At this point, the VM is
|
||||
# bootstrapped and the fundamental data structures, primitive functions and
|
||||
# the basic classes and objects are available.
|
||||
#
|
||||
# The classes, modules, and methods defined here provide basic functionality
|
||||
# needed to load the bootstrap directory. By the end of this file, the
|
||||
# following methods are available:
|
||||
#
|
||||
# attr_reader :sym
|
||||
# attr_writer :sym
|
||||
# attr_accessor :sym
|
||||
#
|
||||
# private :sym
|
||||
# protected :sym
|
||||
#
|
||||
# module_function :sym
|
||||
#
|
||||
# These forms should be used in the rest of the core library. In core/zed.rb,
|
||||
# more complete forms of these methods are provided for user code.
|
||||
#
|
||||
# NOTE: The order of these definitions is important. Do not
|
||||
# change it without consultation.
|
||||
|
||||
class NilClass
|
||||
def nil?
|
||||
true
|
||||
end
|
||||
|
||||
def !
|
||||
true
|
||||
end
|
||||
|
||||
def !=(other)
|
||||
other.nil? ? false : true
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
other.nil? ? true : false
|
||||
end
|
||||
|
||||
# alias_method is not available yet
|
||||
def equal?(other)
|
||||
other.nil? ? true : false
|
||||
end
|
||||
end
|
||||
|
||||
# This class encapsulates primitives that involve the VM itself rather than
|
||||
# something in Ruby-land.
|
||||
#
|
||||
# See core/vm.rb
|
||||
#
|
||||
class Rubinius::VM
|
||||
|
||||
# Write given string to stderr.
|
||||
#
|
||||
# Used to support error reporting where IO is not reliable yet.
|
||||
#
|
||||
def self.write_error(str)
|
||||
Rubinius.primitive :vm_write_error
|
||||
raise PrimitiveFailure, "Rubinius::VM.write_error primitive failed"
|
||||
end
|
||||
|
||||
# Prints Ruby backtrace at point of call.
|
||||
#
|
||||
def self.show_backtrace
|
||||
Rubinius.primitive :vm_show_backtrace
|
||||
raise PrimitiveFailure, "Rubinius::VM.show_backtrace primitive failed"
|
||||
end
|
||||
|
||||
# Reset the method cache globally for given method name.
|
||||
#
|
||||
def self.reset_method_cache(mod, sym)
|
||||
Rubinius.primitive :vm_reset_method_cache
|
||||
raise PrimitiveFailure, "Rubinius::VM.reset_method_cache primitive failed"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class Object
|
||||
# Prints basic information about the object to stdout.
|
||||
#
|
||||
def __show__
|
||||
Rubinius.primitive :object_show
|
||||
"<unknown>"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class Class
|
||||
|
||||
# Allocate memory for an instance of the class without initialization.
|
||||
#
|
||||
# The object returned is valid to use, but its #initialize method has not
|
||||
# been called. In almost all cases, .new is the correct method to use
|
||||
# instead.
|
||||
#
|
||||
# See .new
|
||||
#
|
||||
def allocate
|
||||
Rubinius.primitive :class_allocate
|
||||
raise RuntimeError, "Class#allocate primitive failed on #{self.inspect}"
|
||||
end
|
||||
|
||||
# Allocate and initialize an instance of the class.
|
||||
#
|
||||
# Default implementation: merely allocates the instance, and then calls the
|
||||
# #initialize method on the object with the given arguments and block, if
|
||||
# provided.
|
||||
#
|
||||
# See .allocate
|
||||
#
|
||||
def new(*args)
|
||||
obj = allocate()
|
||||
|
||||
Rubinius.asm(args, obj) do |args, obj|
|
||||
run obj
|
||||
run args
|
||||
push_block
|
||||
send_with_splat :initialize, 0, true
|
||||
# no pop here, as .asm blocks imply a pop as they're not
|
||||
# allowed to leak a stack value
|
||||
end
|
||||
|
||||
obj
|
||||
end
|
||||
end
|
||||
|
||||
module Kernel
|
||||
def nil?
|
||||
false
|
||||
end
|
||||
|
||||
# Return the Class object this object is an instance of.
|
||||
#
|
||||
# Note that this method must always be called with an explicit receiver,
|
||||
# since class by itself is a keyword.
|
||||
#
|
||||
def class
|
||||
Rubinius.primitive :object_class
|
||||
raise PrimitiveFailure, "Kernel#class primitive failed."
|
||||
end
|
||||
|
||||
# Return the class object of self.
|
||||
#
|
||||
# This is the same as #class, but named to always be available.
|
||||
def __class__
|
||||
Rubinius.primitive :object_class
|
||||
raise PrimitiveFailure, "Kernel#class primitive failed."
|
||||
end
|
||||
|
||||
# String representation of an object.
|
||||
#
|
||||
# By default, the representation is the name of the object's class preceded
|
||||
# by a # to indicate the object is an instance thereof.
|
||||
#
|
||||
def to_s
|
||||
"#<#{self.class.name}>"
|
||||
end
|
||||
|
||||
# :internal:
|
||||
#
|
||||
# Lowest-level implementation of raise, used internally by core library code
|
||||
# until a more sophisticated version is loaded.
|
||||
#
|
||||
# Redefined later.
|
||||
#
|
||||
def raise(cls, str="", junk=nil)
|
||||
Rubinius::VM.write_error "Fatal error loading core library:\n "
|
||||
Rubinius::VM.write_error str
|
||||
Rubinius::VM.write_error "\n"
|
||||
Rubinius::VM.show_backtrace
|
||||
Process.exit! 1
|
||||
end
|
||||
|
||||
# Returns true if the given Class is either the class or superclass of the
|
||||
# object or, when given a Module, if the Module has been included in
|
||||
# object's class or one of its superclasses. Returns false otherwise.
|
||||
#
|
||||
# If the argument is not a Class or Module, a TypeError is raised.
|
||||
#
|
||||
def kind_of?(cls)
|
||||
Rubinius.primitive :object_kind_of
|
||||
raise TypeError, "Kernel#kind_of? requires a Class or Module argument"
|
||||
end
|
||||
|
||||
# Hook method invoked when object is sent a message it cannot handle.
|
||||
#
|
||||
# The default implementation will merely raise a NoMethodError with
|
||||
# information about the message.
|
||||
#
|
||||
# This method may be overridden, and is often used to provide dynamic
|
||||
# behaviour. An overriding version should call super if it fails to resolve
|
||||
# the message. This practice ensures that the default version will be called
|
||||
# if all else fails.
|
||||
#
|
||||
def method_missing(meth, *args)
|
||||
raise NoMethodError, "Unable to send '#{meth}' on '#{self}' (#{self.class})"
|
||||
end
|
||||
|
||||
# :internal:
|
||||
#
|
||||
# Backend method for Object#dup and Object#clone.
|
||||
#
|
||||
# Redefined in core/kernel.rb
|
||||
#
|
||||
def initialize_copy(other)
|
||||
end
|
||||
|
||||
# Generic shallow copy of object.
|
||||
#
|
||||
# Copies instance variables, but does not recursively copy the objects they
|
||||
# reference. Copies taintedness.
|
||||
#
|
||||
# In contrast to .clone, .dup can be considered as creating a new object of
|
||||
# the same class and populating it with data from the object.
|
||||
#
|
||||
# If class-specific behaviour is desired, the class should define
|
||||
# #initialize_copy and implement the behaviour there. #initialize_copy will
|
||||
# automatically be called on the new object - the copy - with the original
|
||||
# object as argument if defined.
|
||||
#
|
||||
def dup
|
||||
copy = Rubinius::Type.object_class(self).allocate
|
||||
|
||||
Rubinius.invoke_primitive :object_copy_object, copy, self
|
||||
|
||||
Rubinius::Type.object_initialize_dup self, copy
|
||||
copy
|
||||
end
|
||||
|
||||
# Direct shallow copy of object.
|
||||
#
|
||||
# Copies instance variables, but does not recursively copy the objects they
|
||||
# reference. Copies taintedness and frozenness.
|
||||
#
|
||||
# In contrast to .dup, .clone can be considered to actually clone the
|
||||
# existing object, including its internal state and any singleton methods.
|
||||
#
|
||||
# If class-specific behaviour is desired, the class should define
|
||||
# #initialize_copy and implement the behaviour there. #initialize_copy will
|
||||
# automatically be called on the new object - the copy - with the original
|
||||
# object as argument if defined.
|
||||
#
|
||||
def clone
|
||||
# Do not implement in terms of dup. It breaks rails.
|
||||
#
|
||||
copy = Rubinius::Type.object_class(self).allocate
|
||||
|
||||
Rubinius.invoke_primitive :object_copy_object, copy, self
|
||||
Rubinius.invoke_primitive :object_copy_singleton_class, copy, self
|
||||
|
||||
Rubinius::Type.object_initialize_clone self, copy
|
||||
|
||||
copy.freeze if frozen?
|
||||
copy
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Module for internals.
|
||||
#
|
||||
# See core/rubinius.rb
|
||||
#
|
||||
module Rubinius
|
||||
class Mirror
|
||||
def self.subject=(klass)
|
||||
@subject = klass
|
||||
end
|
||||
|
||||
def self.subject
|
||||
@subject
|
||||
end
|
||||
end
|
||||
|
||||
module Runtime
|
||||
def self.dup_as_array(obj)
|
||||
Rubinius.primitive :array_dup_as_array
|
||||
raise PrimitiveFailure, "Rubinius::Runtime.dup_as_array primitive failed"
|
||||
end
|
||||
end
|
||||
|
||||
# Executable abstraction for accessors.
|
||||
#
|
||||
class AccessVariable
|
||||
|
||||
# Specialised allocation.
|
||||
#
|
||||
def self.allocate
|
||||
Rubinius.primitive :accessvariable_allocate
|
||||
raise PrimitiveFailure, "AccessVariable.allocate primitive failed"
|
||||
end
|
||||
|
||||
# Set up the executable.
|
||||
#
|
||||
# Name of variable provided without leading @, the second parameter should
|
||||
# be true if the attr is writable.
|
||||
#
|
||||
def initialize(variable, write)
|
||||
@primitive = nil
|
||||
@serial = 0
|
||||
@name = "@#{variable}".to_sym
|
||||
@write = write
|
||||
end
|
||||
|
||||
# Get the arity for this reader or writr
|
||||
#
|
||||
def arity
|
||||
@write ? 1 : 0
|
||||
end
|
||||
|
||||
# Create a getter for named instance var, without leading @.
|
||||
#
|
||||
def self.get_ivar(name)
|
||||
new(name, false)
|
||||
end
|
||||
|
||||
# Create a setter for named instance var, without leading @.
|
||||
#
|
||||
def self.set_ivar(name)
|
||||
new(name, true)
|
||||
end
|
||||
end
|
||||
|
||||
# Simplified lookup table.
|
||||
#
|
||||
# See core/lookup_table.rb.
|
||||
#
|
||||
class LookupTable
|
||||
|
||||
# Retrieve value for given key.
|
||||
#
|
||||
def [](key)
|
||||
Rubinius.primitive :lookuptable_aref
|
||||
raise PrimitiveFailure, "LookupTable#[] primitive failed"
|
||||
end
|
||||
|
||||
# Store value under key.
|
||||
#
|
||||
def []=(key, val)
|
||||
Rubinius.primitive :lookuptable_store
|
||||
raise PrimitiveFailure, "LookupTable#[]= primitive failed"
|
||||
end
|
||||
end
|
||||
|
||||
# Constant table for storing methods.
|
||||
#
|
||||
# See core/constant_table.rb
|
||||
#
|
||||
class ConstantTable
|
||||
# Perform lookup for constant name.
|
||||
#
|
||||
def lookup(name)
|
||||
Rubinius.primitive :constant_table_lookup
|
||||
raise PrimitiveFailure, "ConstantTable#lookup primitive failed"
|
||||
end
|
||||
|
||||
# Store Constant under name, with given visibility.
|
||||
#
|
||||
def store(name, constant, visibility)
|
||||
Rubinius.primitive :constant_table_store
|
||||
raise PrimitiveFailure, "ConstantTable#store primitive failed"
|
||||
end
|
||||
end
|
||||
|
||||
# Lookup table for storing methods.
|
||||
#
|
||||
# See core/method_table.rb
|
||||
#
|
||||
class MethodTable
|
||||
# Perform lookup for method name.
|
||||
#
|
||||
def lookup(name)
|
||||
Rubinius.primitive :methodtable_lookup
|
||||
raise PrimitiveFailure, "MethodTable#lookup primitive failed"
|
||||
end
|
||||
|
||||
# Store Executable under name, with given visibility.
|
||||
#
|
||||
def store(name, method_id, exec, scope, serial, visibility)
|
||||
Rubinius.primitive :methodtable_store
|
||||
raise PrimitiveFailure, "MethodTable#store primitive failed"
|
||||
end
|
||||
|
||||
# Make an alias from +original_name+ in +original_mod+ to +name+ with
|
||||
# visibility +vis+
|
||||
def alias(name, visibility, original_name, original_exec, original_mod)
|
||||
Rubinius.primitive :methodtable_alias
|
||||
raise PrimitiveFailure, "MethodTable#alias primitive failed"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Symbol
|
||||
# Produce String representation of this Symbol.
|
||||
#
|
||||
def to_s
|
||||
Rubinius.primitive :symbol_to_s
|
||||
raise PrimitiveFailure, "Symbol#to_s primitive failed"
|
||||
end
|
||||
|
||||
# For completeness, returns self.
|
||||
#
|
||||
def to_sym
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
class String
|
||||
# Returns the <code>Symbol</code> corresponding to <i>self</i>, creating the
|
||||
# symbol if it did not previously exist. See <code>Symbol#id2name</code>.
|
||||
#
|
||||
# "Koala".intern #=> :Koala
|
||||
# s = 'cat'.to_sym #=> :cat
|
||||
# s == :cat #=> true
|
||||
# s = '@cat'.to_sym #=> :@cat
|
||||
# s == :@cat #=> true
|
||||
#
|
||||
# This can also be used to create symbols that cannot be represented using the
|
||||
# <code>:xxx</code> notation.
|
||||
#
|
||||
# 'cat and dog'.to_sym #=> :"cat and dog"
|
||||
#--
|
||||
# TODO: Add taintedness-check
|
||||
#++
|
||||
def to_sym
|
||||
Rubinius.primitive :symbol_lookup
|
||||
raise PrimitiveFailure, "String#to_sym primitive failed: #{self.dump}"
|
||||
end
|
||||
|
||||
# For completeness, returns self.
|
||||
#
|
||||
def to_s
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
module Process
|
||||
# Terminate with given status code.
|
||||
#
|
||||
def self.exit(code=0)
|
||||
case code
|
||||
when true
|
||||
code = 0
|
||||
when false
|
||||
code = 1
|
||||
else
|
||||
code = Rubinius::Type.coerce_to code, Integer, :to_int
|
||||
end
|
||||
|
||||
raise SystemExit.new(code)
|
||||
end
|
||||
|
||||
def self.exit!(code=1)
|
||||
Rubinius.primitive :vm_exit
|
||||
|
||||
case code
|
||||
when true
|
||||
exit! 0
|
||||
when false
|
||||
exit! 1
|
||||
else
|
||||
exit! Rubinius::Type.coerce_to(code, Integer, :to_int)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Module
|
||||
def method_table ; @method_table ; end
|
||||
def constant_table ; @constant_table ; end
|
||||
def name ; @module_name.to_s ; end
|
||||
def origin ; @origin ; end
|
||||
|
||||
# Specialised allocator.
|
||||
#
|
||||
def self.allocate
|
||||
Rubinius.primitive :module_allocate
|
||||
raise PrimitiveFailure, "Module.allocate primitive failed"
|
||||
end
|
||||
|
||||
# :internal:
|
||||
#
|
||||
# Hook called when a constant cannot be located.
|
||||
#
|
||||
# Default implementation 'raises', but we don't use #raise to prevent
|
||||
# infinite recursion.
|
||||
#
|
||||
# Redefined in core/module.rb
|
||||
#
|
||||
def const_missing(name)
|
||||
Rubinius::VM.write_error "Missing or uninitialized constant: #{name.to_s}"
|
||||
end
|
||||
|
||||
# Set Module's direct superclass.
|
||||
#
|
||||
# The corresponding 'getter' #superclass method is defined in class.rb,
|
||||
# because it is more complex than a mere accessor
|
||||
#
|
||||
def superclass=(other)
|
||||
Rubinius.check_frozen
|
||||
@superclass = other
|
||||
end
|
||||
|
||||
# Module's stored superclass.
|
||||
#
|
||||
# This may be either an included Module or an inherited Class.
|
||||
#
|
||||
def direct_superclass
|
||||
@superclass
|
||||
end
|
||||
|
||||
# :internal:
|
||||
#
|
||||
# Perform actual work for including a Module in this one.
|
||||
#
|
||||
# Redefined in core/module.rb.
|
||||
#
|
||||
def append_features(mod)
|
||||
im = Rubinius::IncludedModule.new(self)
|
||||
im.attach_to mod
|
||||
end
|
||||
|
||||
# Hook method called on Module when another Module is .include'd into it.
|
||||
#
|
||||
# Override for module-specific behaviour.
|
||||
#
|
||||
def included(mod); end
|
||||
|
||||
# :internal:
|
||||
#
|
||||
# Basic version of .include used while loading core library code.
|
||||
#
|
||||
# Redefined in core/module.rb.
|
||||
#
|
||||
def include(mod)
|
||||
mod.append_features(self)
|
||||
Rubinius.privately do
|
||||
mod.included self
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
# :internal:
|
||||
#
|
||||
# Basic version used while loading core library code.
|
||||
#
|
||||
# Redefined in core/module.rb.
|
||||
#
|
||||
def attr_reader(name)
|
||||
Rubinius::VM.reset_method_cache self, name
|
||||
meth = Rubinius::AccessVariable.get_ivar name
|
||||
@method_table.store name, nil, meth, nil, 0, :public
|
||||
nil
|
||||
end
|
||||
|
||||
def attr_reader_specific(name, method_name)
|
||||
Rubinius::VM.reset_method_cache self, method_name
|
||||
meth = Rubinius::AccessVariable.get_ivar name
|
||||
@method_table.store method_name, nil, meth, nil, 0, :public
|
||||
nil
|
||||
end
|
||||
|
||||
# :internal:
|
||||
#
|
||||
# Basic version used while loading core library code.
|
||||
#
|
||||
# Redefined in core/module.rb.
|
||||
#
|
||||
def attr_writer(name)
|
||||
meth = Rubinius::AccessVariable.set_ivar name
|
||||
writer_name = "#{name}=".to_sym
|
||||
Rubinius::VM.reset_method_cache self, writer_name
|
||||
@method_table.store writer_name, nil, meth, nil, 0, :public
|
||||
nil
|
||||
end
|
||||
|
||||
# :internal:
|
||||
#
|
||||
# Basic version used while loading core library code.
|
||||
#
|
||||
# Redefined in core/module.rb.
|
||||
#
|
||||
def attr_accessor(name)
|
||||
attr_reader(name)
|
||||
attr_writer(name)
|
||||
nil
|
||||
end
|
||||
|
||||
# :internal:
|
||||
#
|
||||
# Basic version used while loading core library code. Cannot be used as a
|
||||
# toggle, and only takes a single method name.
|
||||
#
|
||||
# Redefined in core/module.rb.
|
||||
#
|
||||
def public(name)
|
||||
if entry = @method_table.lookup(name)
|
||||
entry.visibility = :public
|
||||
end
|
||||
end
|
||||
|
||||
# :internal:
|
||||
#
|
||||
# Basic version used while loading core library code. Cannot be used as a
|
||||
# toggle, and only takes a single method name.
|
||||
#
|
||||
# Redefined in core/module.rb.
|
||||
#
|
||||
def private(name)
|
||||
if entry = @method_table.lookup(name)
|
||||
entry.visibility = :private
|
||||
end
|
||||
end
|
||||
|
||||
# :internal:
|
||||
#
|
||||
# Basic version used while loading core library code. Cannot be used as a
|
||||
# toggle, and only takes a single method name.
|
||||
#
|
||||
# Redefined in core/module.rb.
|
||||
#
|
||||
def protected(name)
|
||||
if entry = @method_table.lookup(name)
|
||||
entry.visibility = :protected
|
||||
end
|
||||
end
|
||||
|
||||
# :internal:
|
||||
#
|
||||
# Basic version used while loading core library code. Creates a copy of
|
||||
# current method and stores it under the new name. The two are independent.
|
||||
#
|
||||
# Redefined in core/module.rb.
|
||||
#
|
||||
def alias_method(new_name, current_name)
|
||||
Rubinius::VM.reset_method_cache self, new_name
|
||||
|
||||
# If we're aliasing a method we contain, just reference it directly, no
|
||||
# need for the alias wrapper
|
||||
if entry = @method_table.lookup(current_name)
|
||||
@method_table.store new_name, entry.method_id, entry.get_method,
|
||||
entry.scope, entry.serial, entry.visibility
|
||||
else
|
||||
mod = direct_superclass()
|
||||
while mod
|
||||
entry = mod.method_table.lookup(current_name)
|
||||
break if entry
|
||||
mod = mod.direct_superclass
|
||||
end
|
||||
|
||||
unless entry
|
||||
raise NoMethodError, "No method '#{current_name}' to alias to '#{new_name}'"
|
||||
end
|
||||
|
||||
@method_table.alias new_name, entry.visibility, current_name,
|
||||
entry.get_method, mod
|
||||
end
|
||||
end
|
||||
|
||||
# :internal:
|
||||
#
|
||||
# Basic version used while loading core library code. Only takes a single
|
||||
# method name.
|
||||
#
|
||||
# Redefined in core/module.rb.
|
||||
#
|
||||
def module_function(name)
|
||||
if entry = @method_table.lookup(name)
|
||||
sc = class << self; self; end
|
||||
Rubinius::VM.reset_method_cache sc, name
|
||||
sc.method_table.store name, entry.method_id, entry.method,
|
||||
entry.scope, entry.serial, :public
|
||||
private name
|
||||
end
|
||||
end
|
||||
|
||||
def custom_call_site(name)
|
||||
if entry = @method_table.lookup(name)
|
||||
entry.get_method.custom_call_site
|
||||
end
|
||||
end
|
||||
|
||||
def undef_method(name)
|
||||
Rubinius::VM.reset_method_cache self, name
|
||||
@method_table.store name, nil, nil, nil, 0, :undef
|
||||
name
|
||||
end
|
||||
end
|
||||
|
||||
module Rubinius
|
||||
class AccessVariable
|
||||
attr_reader :name
|
||||
end
|
||||
|
||||
# Visibility handling for MethodTables.
|
||||
#
|
||||
# See core/method_table.rb
|
||||
#
|
||||
class MethodTable::Bucket
|
||||
attr_accessor :visibility
|
||||
attr_accessor :method_id
|
||||
attr_accessor :method
|
||||
attr_accessor :scope
|
||||
attr_accessor :serial
|
||||
|
||||
def get_method
|
||||
Rubinius.primitive :methodtable_bucket_get_method
|
||||
raise PrimitiveFailure, "MethodTable::Bucket#get_method primitive failed"
|
||||
end
|
||||
|
||||
def public?
|
||||
@visibility == :public
|
||||
end
|
||||
|
||||
def private?
|
||||
@visibility == :private
|
||||
end
|
||||
|
||||
def protected?
|
||||
@visibility == :protected
|
||||
end
|
||||
end
|
||||
|
||||
# :internal:
|
||||
#
|
||||
# Internal representation of a Module's inclusion in another.
|
||||
#
|
||||
# Abstracts the injection of the included Module into the ancestor hierarchy
|
||||
# for method- and constant lookup in a roughly transparent fashion.
|
||||
#
|
||||
# This class is known to the VM.
|
||||
#
|
||||
class IncludedModule < Module
|
||||
attr_reader :superclass
|
||||
attr_reader :module
|
||||
|
||||
# :internal:
|
||||
#
|
||||
# Specialised allocator.
|
||||
#
|
||||
def self.allocate
|
||||
Rubinius.primitive :included_module_allocate
|
||||
raise PrimitiveFailure, "IncludedModule.allocate primitive failed"
|
||||
end
|
||||
|
||||
# :internal:
|
||||
#
|
||||
# Created referencing the Module that is being included.
|
||||
#
|
||||
def initialize(mod)
|
||||
@method_table = mod.method_table
|
||||
@constant_table = mod.constant_table
|
||||
@module = mod
|
||||
end
|
||||
|
||||
def module
|
||||
@module
|
||||
end
|
||||
|
||||
# :internal:
|
||||
#
|
||||
# Inject self inbetween class and its previous direct superclass.
|
||||
#
|
||||
def attach_to(cls)
|
||||
@superclass = cls.direct_superclass
|
||||
cls.superclass = self
|
||||
self
|
||||
end
|
||||
|
||||
# :internal:
|
||||
#
|
||||
# Name of the included Module.
|
||||
#
|
||||
def name
|
||||
@module.name
|
||||
end
|
||||
|
||||
# :internal:
|
||||
#
|
||||
# String representation of the included Module.
|
||||
#
|
||||
def to_s
|
||||
@module.to_s
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<IncludedModule #{@module.to_s}>"
|
||||
end
|
||||
|
||||
# Returns true if +other+ is the same object as self or if +other+ is the
|
||||
# module this IncludedModule is for.
|
||||
#
|
||||
def ==(other)
|
||||
if other.kind_of? IncludedModule
|
||||
super || other.module == @module
|
||||
else
|
||||
super || other == @module
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Object
|
||||
include Kernel
|
||||
end
|
||||
|
||||
module Enumerable; end
|
||||
class Integer; end
|
||||
class IO; end
|
||||
class Numeric; end
|
||||
class String; end
|
||||
573
gxg/core/argf.rb
Normal file
573
gxg/core/argf.rb
Normal file
|
|
@ -0,0 +1,573 @@
|
|||
module Rubinius
|
||||
|
||||
# :internal:
|
||||
#
|
||||
# The virtual concatenation file of the files given on command line (or
|
||||
# from $stdin if no files were given.)
|
||||
#
|
||||
# The only instance hereof is the object referred to by ARGF.
|
||||
#
|
||||
# @see ARGF
|
||||
#
|
||||
class ARGFClass
|
||||
include Enumerable
|
||||
|
||||
# :internal:
|
||||
#
|
||||
# Create a stateless ARGF.
|
||||
#
|
||||
# The actual setup is done on the fly:
|
||||
#
|
||||
# @see #advance!
|
||||
#
|
||||
def initialize
|
||||
@lineno = 0
|
||||
@advance = true
|
||||
@init = false
|
||||
@use_stdin_only = false
|
||||
@encoding_args = nil
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
#
|
||||
# Set stream into binary mode.
|
||||
#
|
||||
# Stream is set into binary mode, i.e. 8-bit ASCII.
|
||||
# Once set, the binary mode cannot be undone. Returns
|
||||
# self.
|
||||
#
|
||||
def binmode
|
||||
@binmode = true
|
||||
@external = Encoding::ASCII_8BIT
|
||||
self
|
||||
end
|
||||
|
||||
def binmode?
|
||||
@binmode
|
||||
end
|
||||
|
||||
#
|
||||
# Close stream.
|
||||
#
|
||||
def close
|
||||
advance!
|
||||
@stream.close
|
||||
@advance = true unless @use_stdin_only
|
||||
@lineno = 0
|
||||
@binmode = false
|
||||
@external = nil
|
||||
self
|
||||
end
|
||||
|
||||
#
|
||||
# True if the stream is closed.
|
||||
#
|
||||
def closed?
|
||||
advance!
|
||||
@stream.closed?
|
||||
end
|
||||
|
||||
def default_value
|
||||
"".encode(encoding)
|
||||
end
|
||||
|
||||
#
|
||||
# Linewise iteration.
|
||||
#
|
||||
# Yields one line from stream at a time, as given by
|
||||
# #gets. An Enumerator is returned if no block is
|
||||
# provided. Returns nil if no content, self otherwise.
|
||||
#
|
||||
# @see #gets.
|
||||
#
|
||||
def each_line(sep=$/)
|
||||
return to_enum :each_line, sep unless block_given?
|
||||
return nil unless advance!
|
||||
|
||||
while line = gets(sep)
|
||||
yield line
|
||||
end
|
||||
self
|
||||
end
|
||||
alias_method :lines, :each_line
|
||||
alias_method :each, :each_line
|
||||
|
||||
#
|
||||
# Bytewise iteration.
|
||||
#
|
||||
# Yields one byte at a time from stream, an Integer
|
||||
# as given by #getc. An Enumerator is returned if no
|
||||
# block is provided. Returns self.
|
||||
#
|
||||
# @see #getc
|
||||
#
|
||||
def each_byte
|
||||
return to_enum :each_byte unless block_given?
|
||||
while ch = getbyte()
|
||||
yield ch
|
||||
end
|
||||
self
|
||||
end
|
||||
alias_method :bytes, :each_byte
|
||||
|
||||
#
|
||||
# Character-wise iteration.
|
||||
#
|
||||
# Yields one character at a time from stream. An
|
||||
# Enumerator is returned if no block is provided.
|
||||
# Returns self.
|
||||
#
|
||||
# The characters yielded are gotten from #getc.
|
||||
#
|
||||
# @see #getc
|
||||
#
|
||||
def each_char
|
||||
return to_enum :each_char unless block_given?
|
||||
while c = getc()
|
||||
yield c.chr
|
||||
end
|
||||
self
|
||||
end
|
||||
alias_method :chars, :each_char
|
||||
|
||||
def each_codepoint
|
||||
return to_enum :each_codepoint unless block_given?
|
||||
|
||||
while c = getc
|
||||
yield c.ord
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
alias_method :codepoints, :each_codepoint
|
||||
|
||||
def encoding
|
||||
@external || Encoding.default_external
|
||||
end
|
||||
|
||||
#
|
||||
# Query whether stream is at end-of-file.
|
||||
#
|
||||
# True if there is a stream and it is in EOF
|
||||
# status.
|
||||
#
|
||||
def eof?
|
||||
@stream and @stream.eof?
|
||||
end
|
||||
alias_method :eof, :eof?
|
||||
|
||||
#
|
||||
# File descriptor number for stream.
|
||||
#
|
||||
# Returns a file descriptor number for the stream being
|
||||
# read out of.
|
||||
#
|
||||
# @todo Check correctness, does this imply there may be
|
||||
# multiple FDs and if so, is this correct? --rue
|
||||
#
|
||||
def fileno
|
||||
raise ArgumentError, "No stream" unless advance!
|
||||
@stream.fileno
|
||||
end
|
||||
alias_method :to_i, :fileno
|
||||
|
||||
#
|
||||
# File path currently in use.
|
||||
#
|
||||
# Path to file from which read currently is
|
||||
# occurring, or an indication that the stream
|
||||
# is STDIN.
|
||||
#
|
||||
def filename
|
||||
advance!
|
||||
@filename
|
||||
end
|
||||
alias_method :path, :filename
|
||||
|
||||
#
|
||||
# Current stream object.
|
||||
#
|
||||
# This may change during the course of execution,
|
||||
# but is the current one!
|
||||
#
|
||||
def file
|
||||
advance!
|
||||
@stream
|
||||
end
|
||||
|
||||
def getbyte
|
||||
while true
|
||||
return nil unless advance!
|
||||
if val = @stream.getbyte
|
||||
return val
|
||||
end
|
||||
|
||||
return nil if @use_stdin_only
|
||||
@stream.close unless @stream.closed?
|
||||
@advance = true
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Return one character from stream.
|
||||
#
|
||||
# If a character cannot be returned and we are
|
||||
# reading from a file, the stream is closed.
|
||||
#
|
||||
def getc
|
||||
while true
|
||||
return nil unless advance!
|
||||
if val = @stream.getc
|
||||
return val
|
||||
end
|
||||
|
||||
return nil if @use_stdin_only
|
||||
@stream.close unless @stream.closed?
|
||||
@advance = true
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Return next line of text from stream.
|
||||
#
|
||||
# If a line cannot be returned and we are
|
||||
# reading from a file, the stream is closed.
|
||||
#
|
||||
# The mechanism does track the line numbers,
|
||||
# and updates $. accordingly.
|
||||
#
|
||||
def gets(sep=$/)
|
||||
while true
|
||||
return nil unless advance!
|
||||
line = @stream.gets(sep)
|
||||
|
||||
unless line
|
||||
return nil if @use_stdin_only
|
||||
@stream.close unless @stream.closed?
|
||||
@advance = true
|
||||
next
|
||||
end
|
||||
|
||||
@lineno += 1
|
||||
$. = @lineno
|
||||
return line
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Return current line number.
|
||||
#
|
||||
# Line numbers are maintained when using the linewise
|
||||
# access methods.
|
||||
#
|
||||
# @see #gets
|
||||
# @see #each_line
|
||||
#
|
||||
attr_reader :lineno
|
||||
|
||||
#
|
||||
# Set current line number.
|
||||
#
|
||||
# Also sets $. accordingly.
|
||||
#
|
||||
# @todo Should this be public? --rue
|
||||
#
|
||||
def lineno=(val)
|
||||
$. = @lineno = val
|
||||
end
|
||||
|
||||
#
|
||||
# Return stream position for seeking etc.
|
||||
#
|
||||
# @see IO#pos.
|
||||
#
|
||||
def pos
|
||||
raise ArgumentError, "no stream" unless advance!
|
||||
@stream.tell
|
||||
end
|
||||
alias_method :tell, :pos
|
||||
|
||||
#
|
||||
# Set stream position to a previously obtained position.
|
||||
#
|
||||
# @see IO#pos=
|
||||
#
|
||||
def pos=(position)
|
||||
raise ArgumentError, "no stream" unless advance!
|
||||
@stream.pos = position
|
||||
end
|
||||
|
||||
#
|
||||
# Read a byte from stream.
|
||||
#
|
||||
# Similar to #getc, but raises an EOFError if
|
||||
# EOF has been reached.
|
||||
#
|
||||
# @see #getc
|
||||
#
|
||||
def readbyte
|
||||
advance!
|
||||
|
||||
if val = getc()
|
||||
return val
|
||||
end
|
||||
|
||||
raise EOFError, "ARGF at end"
|
||||
end
|
||||
alias_method :readchar, :readbyte
|
||||
|
||||
#
|
||||
# Read number of bytes or all, optionally into buffer.
|
||||
#
|
||||
# If number of bytes is not given or is nil, tries to read
|
||||
# all of the stream, which is then closed. If the number is
|
||||
# specified, then at most that many bytes will be read.
|
||||
#
|
||||
# A buffer responding to #<< may be provided as the second
|
||||
# argument. The data read is pushed into it. If no buffer
|
||||
# is provided, as by default, a String with the data is
|
||||
# returned instead.
|
||||
#
|
||||
def read(bytes=nil, output=nil)
|
||||
# The user might try to pass in nil, so we have to check here
|
||||
output ||= default_value
|
||||
output.clear
|
||||
|
||||
if bytes
|
||||
bytes_left = bytes
|
||||
|
||||
until bytes_left == 0
|
||||
return output unless advance!
|
||||
|
||||
if res = @stream.read(bytes_left)
|
||||
output << res
|
||||
bytes_left -= res.size
|
||||
else
|
||||
break if @use_stdin_only
|
||||
@stream.close unless @stream.closed?
|
||||
@advance = true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return output
|
||||
end
|
||||
|
||||
while advance!
|
||||
output << @stream.read
|
||||
|
||||
break if @use_stdin_only
|
||||
@stream.close unless @stream.closed?
|
||||
@advance = true
|
||||
end
|
||||
|
||||
output
|
||||
end
|
||||
|
||||
def read_nonblock(maxlen, output = nil, exception: true)
|
||||
output ||= default_value
|
||||
|
||||
unless advance!
|
||||
output.clear
|
||||
raise EOFError, "ARGF at end"
|
||||
end
|
||||
|
||||
begin
|
||||
out = @stream.read_nonblock(maxlen, output, exception: exception)
|
||||
|
||||
return out if out == :wait_readable
|
||||
rescue EOFError => e
|
||||
raise e if @use_stdin_only
|
||||
|
||||
@stream.close
|
||||
@advance = true
|
||||
advance! or raise e
|
||||
end
|
||||
|
||||
return output
|
||||
end
|
||||
|
||||
#
|
||||
# Read next line of text.
|
||||
#
|
||||
# As #gets, but an EOFError is raised if the stream
|
||||
# is at EOF.
|
||||
#
|
||||
# @see #gets
|
||||
#
|
||||
def readline(sep=$/)
|
||||
raise EOFError, "ARGF at end" unless advance!
|
||||
|
||||
if line = gets(sep)
|
||||
return line
|
||||
end
|
||||
|
||||
raise EOFError, "ARGF at end"
|
||||
end
|
||||
|
||||
#
|
||||
# Read all lines from stream.
|
||||
#
|
||||
# Reads all lines into an Array using #gets and
|
||||
# returns the Array.
|
||||
#
|
||||
# @see #gets
|
||||
#
|
||||
def readlines(sep=$/)
|
||||
return [] unless advance!
|
||||
|
||||
lines = []
|
||||
while line = gets(sep)
|
||||
lines << line
|
||||
end
|
||||
|
||||
lines
|
||||
end
|
||||
|
||||
alias_method :to_a, :readlines
|
||||
|
||||
def readpartial(maxlen, output=nil)
|
||||
output ||= default_value
|
||||
|
||||
unless advance!
|
||||
output.clear
|
||||
raise EOFError, "ARGF at end"
|
||||
end
|
||||
|
||||
begin
|
||||
@stream.readpartial(maxlen, output)
|
||||
rescue EOFError => e
|
||||
raise e if @use_stdin_only
|
||||
|
||||
@stream.close
|
||||
@advance = true
|
||||
advance! or raise e
|
||||
end
|
||||
|
||||
return output
|
||||
end
|
||||
|
||||
#
|
||||
# Rewind the stream to its beginning.
|
||||
#
|
||||
# Line number is updated accordingly.
|
||||
#
|
||||
# @todo Is this correct, only current stream is rewound? --rue
|
||||
#
|
||||
def rewind
|
||||
raise ArgumentError, "no stream to rewind" unless advance!
|
||||
@lineno -= @stream.lineno
|
||||
@stream.rewind
|
||||
end
|
||||
|
||||
#
|
||||
# Seek into a previous position in the stream.
|
||||
#
|
||||
# @see IO#seek.
|
||||
#
|
||||
def seek(*args)
|
||||
raise ArgumentError, "no stream" unless advance!
|
||||
@stream.seek(*args)
|
||||
end
|
||||
|
||||
def set_encoding(*args)
|
||||
@encoding_args = args
|
||||
if @stream and !@stream.closed?
|
||||
@stream.set_encoding *args
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Close file stream and return self.
|
||||
#
|
||||
# STDIN is not closed if being used, otherwise the
|
||||
# stream gets closed. Returns self.
|
||||
#
|
||||
def skip
|
||||
return self if @use_stdin_only
|
||||
@stream.close unless @stream.closed?
|
||||
@advance = true
|
||||
self
|
||||
end
|
||||
|
||||
def stream(file)
|
||||
stream = file == "-" ? STDIN : File.open(file, "r", :external_encoding => encoding)
|
||||
|
||||
if @encoding_args
|
||||
stream.set_encoding *@encoding_args
|
||||
elsif encoding
|
||||
stream.set_encoding encoding
|
||||
end
|
||||
|
||||
stream
|
||||
end
|
||||
|
||||
#
|
||||
# Return IO object for current stream.
|
||||
#
|
||||
# @see IO#to_io
|
||||
#
|
||||
def to_io
|
||||
advance!
|
||||
@stream.to_io
|
||||
end
|
||||
|
||||
#
|
||||
# Returns "ARGF" as the string representation of this object.
|
||||
#
|
||||
def to_s
|
||||
"ARGF"
|
||||
end
|
||||
|
||||
|
||||
# Internals
|
||||
|
||||
#
|
||||
# Main processing.
|
||||
#
|
||||
# If not initialised yet, sets the object up on either
|
||||
# first of provided file names or STDIN.
|
||||
#
|
||||
# Does nothing further or later if using STDIN, but if
|
||||
# there are further file names in ARGV, tries to open
|
||||
# the next one as the current stream.
|
||||
#
|
||||
def advance!
|
||||
return true unless @advance
|
||||
|
||||
unless @init
|
||||
|
||||
if ARGV.empty?
|
||||
@advance = false
|
||||
@stream = STDIN
|
||||
@filename = "-"
|
||||
@use_stdin_only = true
|
||||
return true
|
||||
end
|
||||
@init = true
|
||||
end
|
||||
|
||||
File.unlink(@backup_filename) if @backup_filename && $-i == ""
|
||||
|
||||
return false if @use_stdin_only || ARGV.empty?
|
||||
|
||||
@advance = false
|
||||
|
||||
file = ARGV.shift
|
||||
@stream = stream(file)
|
||||
@filename = file
|
||||
|
||||
if $-i && @stream != STDIN
|
||||
backup_extension = $-i == "" ? ".bak" : $-i
|
||||
@backup_filename = "#{@filename}#{backup_extension}"
|
||||
File.rename(@filename, @backup_filename)
|
||||
@stream = File.open(@backup_filename, "r")
|
||||
$stdout = File.open(@filename, "w")
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
private :advance!
|
||||
end
|
||||
end
|
||||
2324
gxg/core/array.rb
Normal file
2324
gxg/core/array.rb
Normal file
File diff suppressed because it is too large
Load diff
96
gxg/core/array_mirror.rb
Normal file
96
gxg/core/array_mirror.rb
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
module Rubinius
|
||||
class Mirror
|
||||
class Array < Mirror
|
||||
self.subject = ::Array
|
||||
|
||||
def self.reflect(object)
|
||||
m = super(object)
|
||||
|
||||
if Rubinius::Type.object_kind_of? m.object, ::Array
|
||||
m
|
||||
elsif ary = Rubinius::Type.try_convert(m.object, ::Array, :to_ary)
|
||||
super(ary)
|
||||
else
|
||||
message = "expected Array, given #{Rubinius::Type.object_class(object)}"
|
||||
raise TypeError, message
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: implement mirror_attribute :name
|
||||
def total
|
||||
Rubinius.invoke_primitive :object_get_ivar, @object, :@total
|
||||
end
|
||||
|
||||
def total=(value)
|
||||
Rubinius.invoke_primitive :object_set_ivar, @object, :@total, value
|
||||
end
|
||||
|
||||
def tuple
|
||||
Rubinius.invoke_primitive :object_get_ivar, @object, :@tuple
|
||||
end
|
||||
|
||||
def tuple=(value)
|
||||
Rubinius.invoke_primitive :object_set_ivar, @object, :@tuple, value
|
||||
end
|
||||
|
||||
def start
|
||||
Rubinius.invoke_primitive :object_get_ivar, @object, :@start
|
||||
end
|
||||
|
||||
def start=(value)
|
||||
Rubinius.invoke_primitive :object_set_ivar, @object, :@start, value
|
||||
end
|
||||
|
||||
# Implementation based on the binomial_coefficient
|
||||
# implemented on MRI array.c.
|
||||
def binomial_coefficient(comb, size)
|
||||
comb = size-comb if (comb > size-comb)
|
||||
return 0 if comb < 0
|
||||
descending_factorial(size, comb) / descending_factorial(comb, comb)
|
||||
end
|
||||
|
||||
# Implementation based on the rb_ary_combination_size
|
||||
# implemented on MRI array.c.
|
||||
def combination_size(num)
|
||||
binomial_coefficient(num, @object.size)
|
||||
end
|
||||
|
||||
# Implementation based on the descending_factorial
|
||||
# implemented on MRI array.c.
|
||||
# Comment from there:
|
||||
# Returns the product of from, from-1, ..., from - how_many + 1.
|
||||
# http://en.wikipedia.org/wiki/Pochhammer_symbol
|
||||
def descending_factorial(from, how_many)
|
||||
cnt = how_many >= 0 ? 1 : 0
|
||||
while (how_many) > 0
|
||||
cnt = cnt*(from)
|
||||
from -= 1
|
||||
how_many -= 1
|
||||
end
|
||||
cnt
|
||||
end
|
||||
|
||||
# Implementation based on the rb_ary_permutation_size
|
||||
# implemented on MRI array.c.
|
||||
def permutation_size(num)
|
||||
n = @object.size
|
||||
if undefined.equal? num
|
||||
k = @object.size
|
||||
else
|
||||
k = Rubinius::Type.coerce_to_collection_index num
|
||||
end
|
||||
descending_factorial(n, k)
|
||||
end
|
||||
|
||||
def repeated_combination_size(combination_size)
|
||||
return 1 if combination_size == 0
|
||||
return binomial_coefficient(combination_size, @object.size + combination_size - 1)
|
||||
end
|
||||
|
||||
def repeated_permutation_size(combination_size)
|
||||
return 0 if combination_size < 0
|
||||
@object.size ** combination_size
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
43
gxg/core/atomic.rb
Normal file
43
gxg/core/atomic.rb
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
module Rubinius
|
||||
class AtomicReference
|
||||
def initialize(val=nil)
|
||||
set(val) unless val.nil?
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def marshal_dump
|
||||
get
|
||||
end
|
||||
|
||||
def marshal_load(val)
|
||||
set(val)
|
||||
end
|
||||
|
||||
def get
|
||||
Rubinius.primitive :atomic_get
|
||||
raise PrimitiveFailure, "Rubinius::AtomicReference#get primitive failed"
|
||||
end
|
||||
|
||||
alias_method :value, :get
|
||||
|
||||
def set(val)
|
||||
Rubinius.primitive :atomic_set
|
||||
raise PrimitiveFailure, "Rubinius::AtomicReference#set primitive failed"
|
||||
end
|
||||
|
||||
alias_method :value=, :set
|
||||
|
||||
def compare_and_set(old, new)
|
||||
Rubinius.primitive :atomic_compare_and_set
|
||||
raise PrimitiveFailure, "Rubinius::AtomicReference#compare_and_set primitive failed"
|
||||
end
|
||||
|
||||
def get_and_set(new)
|
||||
while true
|
||||
val = get
|
||||
return val if compare_and_set(val, new)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
119
gxg/core/autoload.rb
Normal file
119
gxg/core/autoload.rb
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
##
|
||||
# Used to implement Module#autoload.
|
||||
|
||||
class Autoload
|
||||
attr_reader :name
|
||||
attr_reader :scope
|
||||
attr_reader :path
|
||||
attr_reader :constant
|
||||
|
||||
attr_accessor :loaded
|
||||
attr_accessor :loading
|
||||
|
||||
def self.allocate
|
||||
Rubinius.primitive :autoload_allocate
|
||||
raise PrimtiveFailure, "Autoload.allocate primitive failed"
|
||||
end
|
||||
|
||||
def initialize(name, scope, path)
|
||||
@name = name
|
||||
@scope = scope
|
||||
@path = path
|
||||
@constant = undefined
|
||||
@loading = false
|
||||
@loaded = false
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def inspect
|
||||
"#<#{self.class.name}:0x#{object_id.to_s(16)} name=#{@name} scope=#{@scope}" \
|
||||
" path=#{@path} constant=#{@constant} loading=#{@loading} loaded=#{@loaded}>"
|
||||
end
|
||||
|
||||
alias_method :to_s, :inspect
|
||||
|
||||
def loading?
|
||||
Rubinius.synchronize(self) { @loading }
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Change the file to autoload. Used by Module#autoload
|
||||
def set_path(path)
|
||||
@path = path
|
||||
@loading = false
|
||||
end
|
||||
|
||||
##
|
||||
# When any code that finds a constant sees an instance of Autoload as its match,
|
||||
# it calls this method on us
|
||||
def call(under, honor_require=false)
|
||||
return constant unless undefined.equal? constant
|
||||
|
||||
if resolve or not honor_require
|
||||
find_const under
|
||||
end
|
||||
end
|
||||
|
||||
def resolve
|
||||
# The protocol that MRI defines for resolving an Autoload instance
|
||||
# requires that the constant table entry be removed _during_ the load so
|
||||
# that Module#const_defined? returns false and defined?() returns nil for
|
||||
# the constant that triggered the load.
|
||||
|
||||
Rubinius.synchronize(self) do
|
||||
unless @loaded or @loading
|
||||
@loading = true
|
||||
|
||||
result = Rubinius::CodeLoader.new(@path).require
|
||||
|
||||
@loaded = true if result
|
||||
@loading = false
|
||||
|
||||
result
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def find_const(under)
|
||||
current = under
|
||||
constant = undefined
|
||||
|
||||
while current
|
||||
if entry = current.constant_table.lookup(name)
|
||||
constant = entry.constant
|
||||
if constant.equal? self
|
||||
if undefined.equal?(constant.constant)
|
||||
unless Object.constant_table.lookup(name)
|
||||
return under.const_missing(name)
|
||||
end
|
||||
else
|
||||
entry.constant = constant.constant
|
||||
return constant.constant
|
||||
end
|
||||
end
|
||||
return constant
|
||||
end
|
||||
|
||||
current = current.direct_superclass
|
||||
end
|
||||
|
||||
if instance_of?(Module)
|
||||
if entry = Object.constant_table.lookup(name)
|
||||
constant = entry.constant
|
||||
if constant.equal? self
|
||||
if undefined.equal?(constant.constant)
|
||||
return under.const_missing(name)
|
||||
else
|
||||
entry.constant = constant.constant
|
||||
return constant.constant
|
||||
end
|
||||
end
|
||||
return constant
|
||||
end
|
||||
end
|
||||
|
||||
under.const_missing(name)
|
||||
end
|
||||
end
|
||||
163
gxg/core/backtrace.rb
Normal file
163
gxg/core/backtrace.rb
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
##
|
||||
# Contains all logic for gathering and displaying backtraces.
|
||||
|
||||
class Rubinius::Backtrace
|
||||
include Enumerable
|
||||
|
||||
MAX_WIDTH_PERCENTAGE = 45
|
||||
MIN_WIDTH = 20
|
||||
|
||||
# If passed nil, we assume someone forgot to create a backtrace
|
||||
# in the VM.
|
||||
def self.backtrace(locations)
|
||||
locations ? new(locations) : new([Rubinius::Location::Missing.new])
|
||||
end
|
||||
|
||||
def initialize(locations, width = Rubinius::TERMINAL_WIDTH, min_width = MIN_WIDTH)
|
||||
color_config = Rubinius::Config["rbx.colorize_backtraces"]
|
||||
if ENV['RBX_NOCOLOR'] or color_config == "no" or color_config == "NO"
|
||||
@colorize = false
|
||||
else
|
||||
@colorize = true
|
||||
end
|
||||
|
||||
@locations = locations
|
||||
@bold = "\033[0;1m"
|
||||
@inline_effect = "\033[0;4m"
|
||||
|
||||
@width = width
|
||||
@min_width = min_width
|
||||
|
||||
@mri_backtrace = nil
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def [](index)
|
||||
@locations[index]
|
||||
end
|
||||
|
||||
def show(sep="\n", show_color=true)
|
||||
show_color = false unless @colorize
|
||||
clear = show_color ? "\033[0m" : ""
|
||||
|
||||
max = 0
|
||||
lines = []
|
||||
last_method = nil
|
||||
last_line = nil
|
||||
last_name = nil
|
||||
times = 0
|
||||
|
||||
@locations.reverse_each do |loc|
|
||||
if loc.name == last_name and loc.method == last_method \
|
||||
and loc.line == last_line
|
||||
times += 1
|
||||
else
|
||||
lines.last[-1] = times if lines.size > 0
|
||||
last_method = loc.method
|
||||
last_line = loc.line
|
||||
last_name = loc.name
|
||||
|
||||
receiver_name = loc.describe_receiver
|
||||
|
||||
if loc.is_block
|
||||
method_name = "{ } in #{loc.describe_method}"
|
||||
else
|
||||
method_name = loc.describe_method
|
||||
end
|
||||
|
||||
size = method_name.size + 4 # additional space needed for {#, .}
|
||||
max = size if size > max
|
||||
|
||||
lines << [receiver_name, method_name, loc, 1]
|
||||
times = 0
|
||||
end
|
||||
end
|
||||
|
||||
cwd = Dir.getwd
|
||||
|
||||
str = ""
|
||||
lines.each do |receiver_name, method_name, location, rec_times|
|
||||
next unless location
|
||||
|
||||
full_name = "#{method_name} #{receiver_name[-1]} "
|
||||
|
||||
if show_color
|
||||
receiver_method = "#{@bold}#{full_name}#{receiver_name[0..-2]}#{clear}"
|
||||
else
|
||||
receiver_method = "#{full_name}#{receiver_name[0..-2]}"
|
||||
end
|
||||
|
||||
spaces = max - full_name.size
|
||||
spaces = 0 if spaces < 0
|
||||
|
||||
if show_color and location.inlined?
|
||||
start = " #{' ' * spaces}#{receiver_method} #{@inline_effect}at#{clear}#{color} "
|
||||
else
|
||||
start = " #{' ' * spaces}#{receiver_method} at "
|
||||
end
|
||||
|
||||
# start.size without the escapes
|
||||
start_size = 1 + spaces + receiver_method.size + 4
|
||||
|
||||
pos = location.position cwd
|
||||
|
||||
pos << " (+#{location.ip})"
|
||||
|
||||
if rec_times > 1
|
||||
pos << " (#{rec_times} times)"
|
||||
end
|
||||
|
||||
if location.is_jit and $DEBUG
|
||||
pos << " (jit)"
|
||||
end
|
||||
|
||||
str << " #{start}#{pos}"
|
||||
|
||||
str << sep
|
||||
end
|
||||
|
||||
return str
|
||||
end
|
||||
|
||||
def join(sep)
|
||||
show
|
||||
end
|
||||
|
||||
alias_method :to_s, :show
|
||||
|
||||
def each
|
||||
@locations.each { |f| yield f }
|
||||
self
|
||||
end
|
||||
|
||||
def self.detect_backtrace(obj)
|
||||
if bt = obj.instance_variable_get(:@rbx_backtrace)
|
||||
return bt if bt.same_mri_backtrace?(obj)
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
def to_mri
|
||||
return @mri_backtrace if @mri_backtrace
|
||||
|
||||
@mri_backtrace = @locations.map do |loc|
|
||||
"#{loc.position}:in `#{loc.describe_method}'"
|
||||
end
|
||||
|
||||
# A little extra magic, so we can carry the backtrace along and reuse it
|
||||
|
||||
@mri_backtrace.instance_variable_set(:@rbx_backtrace, self)
|
||||
|
||||
return @mri_backtrace
|
||||
end
|
||||
|
||||
def same_mri_backtrace?(obj)
|
||||
currently = @locations.map do |loc|
|
||||
"#{loc.position}:in `#{loc.describe_method}'"
|
||||
end
|
||||
|
||||
currently == obj
|
||||
end
|
||||
end
|
||||
162
gxg/core/basic_object.rb
Normal file
162
gxg/core/basic_object.rb
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
class BasicObject
|
||||
def initialize
|
||||
# do nothing
|
||||
end
|
||||
private :initialize
|
||||
|
||||
def equal?(other)
|
||||
Rubinius.primitive :object_equal
|
||||
raise ::PrimitiveFailure, "BasicObject#equal? primitive failed"
|
||||
end
|
||||
|
||||
alias_method :==, :equal?
|
||||
|
||||
def !
|
||||
Rubinius.invoke_primitive(:object_equal, self, false) ||
|
||||
Rubinius.invoke_primitive(:object_equal, self, nil) ? true : false
|
||||
end
|
||||
|
||||
def !=(other)
|
||||
self == other ? false : true
|
||||
end
|
||||
|
||||
# Send message to object with given arguments.
|
||||
#
|
||||
# Ignores visibility of method, and may therefore be used to
|
||||
# invoke protected or private methods.
|
||||
#
|
||||
# As denoted by the double-underscore, this method must not
|
||||
# be removed or redefined by user code.
|
||||
#
|
||||
def __send__(message, *args)
|
||||
Rubinius.primitive :object_send
|
||||
raise ::PrimitiveFailure, "BasicObject#__send__ primitive failed"
|
||||
end
|
||||
|
||||
def __id__
|
||||
Rubinius.primitive :object_id
|
||||
raise ::PrimitiveFailure, "BasicObject#__id__ primitive failed"
|
||||
end
|
||||
|
||||
def method_missing(meth, *args)
|
||||
error = ::NoMethodError
|
||||
.new("Unable to send '#{meth}' on instance of BasicObject", receiver: self)
|
||||
|
||||
::Kernel.raise(error)
|
||||
end
|
||||
private :method_missing
|
||||
|
||||
def singleton_method_added(name) end
|
||||
private :singleton_method_added
|
||||
|
||||
def singleton_method_removed(name) end
|
||||
private :singleton_method_removed
|
||||
|
||||
def singleton_method_undefined(name) end
|
||||
private :singleton_method_undefined
|
||||
|
||||
##
|
||||
# :call-seq:
|
||||
# obj.instance_eval(string [, filename [, lineno]] ) => obj
|
||||
# obj.instance_eval { | | block } => obj
|
||||
#
|
||||
# Evaluates a string containing Ruby source code, or the given block, within
|
||||
# the context of the receiver +obj+. In order to set the context, the
|
||||
# variable +self+ is set to +obj+ while the code is executing, giving the
|
||||
# code access to +obj+'s instance variables. In the version of
|
||||
# #instance_eval that takes a +String+, the optional second and third
|
||||
# parameters supply a filename and starting line number that are used when
|
||||
# reporting compilation errors.
|
||||
#
|
||||
# class Klass
|
||||
# def initialize
|
||||
# @secret = 99
|
||||
# end
|
||||
# end
|
||||
# k = Klass.new
|
||||
# k.instance_eval { @secret } #=> 99
|
||||
|
||||
def instance_eval(string=nil, filename="(eval)", line=1, &prc)
|
||||
if ::ImmediateValue === self
|
||||
sc = nil
|
||||
else
|
||||
sc = ::Rubinius::Type.object_singleton_class(self)
|
||||
end
|
||||
|
||||
if prc
|
||||
if string
|
||||
raise ::ArgumentError, 'cannot pass both a block and a string to evaluate'
|
||||
end
|
||||
|
||||
return prc.ruby_method.call(self) if prc.ruby_method
|
||||
|
||||
# Return a copy of the BlockEnvironment with the receiver set to self
|
||||
env = prc.block
|
||||
|
||||
if sc
|
||||
lexical_scope = env.repoint_scope sc
|
||||
else
|
||||
lexical_scope = env.disable_scope!
|
||||
end
|
||||
|
||||
return env.call_under(self, lexical_scope, true, self)
|
||||
elsif string
|
||||
string = ::Kernel.StringValue(string)
|
||||
|
||||
lexical_scope = ::Rubinius::LexicalScope.of_sender
|
||||
|
||||
if sc
|
||||
lexical_scope = ::Rubinius::LexicalScope.new(sc, lexical_scope)
|
||||
else
|
||||
lexical_scope = lexical_scope.using_disabled_scope
|
||||
end
|
||||
|
||||
binding = ::Binding.setup(::Rubinius::VariableScope.of_sender,
|
||||
::Rubinius::CompiledCode.of_sender,
|
||||
lexical_scope)
|
||||
|
||||
c = ::Rubinius::ToolSets::Runtime::Compiler
|
||||
be = c.construct_block string, binding, filename, line
|
||||
|
||||
be.call_on_instance(self)
|
||||
else
|
||||
raise ::ArgumentError, 'block not supplied'
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# :call-seq:
|
||||
# obj.instance_exec(arg, ...) { |var,...| block } => obj
|
||||
#
|
||||
# Executes the given block within the context of the receiver +obj+. In
|
||||
# order to set the context, the variable +self+ is set to +obj+ while the
|
||||
# code is executing, giving the code access to +obj+'s instance variables.
|
||||
#
|
||||
# Arguments are passed as block parameters.
|
||||
#
|
||||
# class Klass
|
||||
# def initialize
|
||||
# @secret = 99
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# k = Klass.new
|
||||
# k.instance_exec(5) { |x| @secret+x } #=> 104
|
||||
|
||||
def instance_exec(*args, &prc)
|
||||
raise ::LocalJumpError, "Missing block" unless block_given?
|
||||
env = prc.block
|
||||
|
||||
return prc.ruby_method.call(*args) if prc.ruby_method
|
||||
|
||||
lexical_scope = env.lexical_scope
|
||||
if ::ImmediateValue === self
|
||||
lexical_scope = lexical_scope.using_disabled_scope
|
||||
else
|
||||
sc = ::Rubinius::Type.object_singleton_class(self)
|
||||
lexical_scope = lexical_scope.using_current_as(sc)
|
||||
end
|
||||
|
||||
return env.call_under(self, lexical_scope, true, *args)
|
||||
end
|
||||
end
|
||||
116
gxg/core/binding.rb
Normal file
116
gxg/core/binding.rb
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
class Binding
|
||||
attr_accessor :variables
|
||||
attr_accessor :compiled_code
|
||||
attr_accessor :lexical_scope
|
||||
attr_accessor :proc_environment
|
||||
attr_accessor :location
|
||||
attr_accessor :receiver
|
||||
|
||||
def from_proc?
|
||||
@proc_environment
|
||||
end
|
||||
|
||||
def self.self_context(recv, variables)
|
||||
Rubinius::Type.object_equal(recv, Kernel) ? recv : variables.self
|
||||
end
|
||||
private :self_context
|
||||
|
||||
# Create a new Binding object. MRI does not allow .new to be called, so
|
||||
# we used .setup(). Any code can use this as they wish, provided they have
|
||||
# all the right arguments.
|
||||
#
|
||||
# +variables+ is a Rubinius::VariableScope object
|
||||
# +code+ is a Rubinius::CompiledCode object
|
||||
# +lexical_scope+ is a Rubinius::LexicalScope object
|
||||
#
|
||||
# See Kernel#binding in core/eval.rb for a simple example of creating a
|
||||
# Binding object.
|
||||
#
|
||||
def self.setup(variables, code, lexical_scope, recv=nil, location=nil)
|
||||
bind = allocate()
|
||||
|
||||
bind.receiver = self_context(recv, variables)
|
||||
bind.variables = variables
|
||||
bind.compiled_code = code
|
||||
bind.lexical_scope = lexical_scope
|
||||
bind.location = location
|
||||
|
||||
return bind
|
||||
end
|
||||
|
||||
def line_number
|
||||
if proc_environment
|
||||
proc_environment.line
|
||||
else
|
||||
location ? location.line : 1
|
||||
end
|
||||
end
|
||||
|
||||
# Evaluates the Ruby expression(s) in string, in the binding‘s context.
|
||||
# If the optional filename and lineno parameters are present,
|
||||
# they will be used when reporting syntax errors.
|
||||
def eval(expr, filename=nil, lineno=nil)
|
||||
lineno ||= filename ? 1 : line_number
|
||||
|
||||
Kernel.eval(expr, self, filename, lineno)
|
||||
end
|
||||
|
||||
def local_variables
|
||||
variables.local_variables
|
||||
end
|
||||
|
||||
def local_variable_set(name, value)
|
||||
unless name.is_a?(Symbol)
|
||||
name = Rubinius::Type.coerce_to(name, String, :to_str).to_sym
|
||||
end
|
||||
|
||||
vars = variables
|
||||
|
||||
# If a local variable is defined in a parent scope we should update the
|
||||
# variable in said scope and all child scopes, instead of _only_ setting it
|
||||
# in the current scope.
|
||||
while vars
|
||||
meth = vars.method
|
||||
|
||||
if meth.local_names.include?(name)
|
||||
return vars.set_local(meth.local_slot(name), value)
|
||||
elsif vars.eval_local_defined?(name)
|
||||
return vars.set_eval_local(name, value)
|
||||
end
|
||||
|
||||
vars = vars.parent
|
||||
end
|
||||
|
||||
variables.set_eval_local(name, value)
|
||||
end
|
||||
|
||||
def local_variable_get(name)
|
||||
unless name.is_a?(Symbol)
|
||||
name = Rubinius::Type.coerce_to(name, String, :to_str).to_sym
|
||||
end
|
||||
|
||||
vars = variables
|
||||
|
||||
while vars
|
||||
meth = vars.method
|
||||
|
||||
if meth.local_names.include?(name)
|
||||
return vars.locals[meth.local_slot(name)]
|
||||
elsif vars.eval_local_defined?(name)
|
||||
return vars.get_eval_local(name)
|
||||
end
|
||||
|
||||
vars = vars.parent
|
||||
end
|
||||
|
||||
raise NameErrror, "local variable #{name.inspect} not defined for #{inspect}"
|
||||
end
|
||||
|
||||
def local_variable_defined?(name)
|
||||
unless name.is_a?(Symbol)
|
||||
name = Rubinius::Type.coerce_to(name, String, :to_str).to_sym
|
||||
end
|
||||
|
||||
variables.local_defined?(name)
|
||||
end
|
||||
end
|
||||
183
gxg/core/block_environment.rb
Normal file
183
gxg/core/block_environment.rb
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
##
|
||||
# Describes the environment a block was created in. BlockEnvironment is used
|
||||
# to create a BlockContext.
|
||||
|
||||
module Rubinius
|
||||
class BlockEnvironment
|
||||
attr_reader :scope
|
||||
attr_reader :top_scope
|
||||
attr_reader :module
|
||||
|
||||
# The CompiledCode that implements the code for the block
|
||||
attr_reader :compiled_code
|
||||
attr_reader :lexical_scope
|
||||
|
||||
attr_accessor :proc_environment
|
||||
|
||||
def self.allocate
|
||||
Rubinius.primitive :blockenvironment_allocate
|
||||
raise PrimitiveFailure, "BlockEnvironment.allocate primitive failed"
|
||||
end
|
||||
|
||||
def call(*args)
|
||||
Rubinius.primitive :block_call
|
||||
raise PrimitiveFailure, "BlockEnvironment#call primitive failed"
|
||||
end
|
||||
|
||||
def call_under(recv, lexical_scope, visibility_scope, *args)
|
||||
Rubinius.primitive :block_call_under
|
||||
raise PrimitiveFailure, "BlockEnvironment#call_under primitive failed"
|
||||
end
|
||||
|
||||
def self.of_sender
|
||||
Rubinius.primitive :block_env_of_sender
|
||||
raise PrimitiveFailure, "BlockEnvironment#of_sender primitive failed"
|
||||
end
|
||||
|
||||
class AsMethod < Executable
|
||||
attr_reader :block_env
|
||||
|
||||
def self.new(block_env)
|
||||
Rubinius.primitive :block_as_method_create
|
||||
raise PrimitiveFailure, "BlockEnvironment::AsMethod.new primitive failed"
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
# Given the following code:
|
||||
# class Foo
|
||||
# p = Proc.new { :cool }
|
||||
# define_method :a, p
|
||||
# define_method :a, p
|
||||
# end
|
||||
# foo = Foo.new
|
||||
# foo.method(:a)
|
||||
# foo.method(:b)
|
||||
#
|
||||
# The Method instances for :a and :b have different
|
||||
# BlockEnvironments as define_method dups the BE
|
||||
# when given a Proc.
|
||||
#
|
||||
# The methods are equal if the BEs are equal.
|
||||
return false unless other.kind_of? AsMethod
|
||||
|
||||
@block_env == other.block_env
|
||||
end
|
||||
|
||||
def parameters
|
||||
@block_env.parameters
|
||||
end
|
||||
|
||||
def arity
|
||||
@block_env.compiled_code.arity
|
||||
end
|
||||
|
||||
def local_names
|
||||
@block_env.compiled_code.local_names
|
||||
end
|
||||
|
||||
def required_args
|
||||
@block_env.compiled_code.required_args
|
||||
end
|
||||
|
||||
def total_args
|
||||
@block_env.compiled_code.total_args
|
||||
end
|
||||
|
||||
def post_args
|
||||
@block_env.compiled_code.post_args
|
||||
end
|
||||
|
||||
def splat
|
||||
@block_env.compiled_code.splat
|
||||
end
|
||||
|
||||
def block_index
|
||||
@block_env.compiled_code.block_index
|
||||
end
|
||||
|
||||
def file
|
||||
@block_env.file
|
||||
end
|
||||
|
||||
def defined_line
|
||||
@block_env.line
|
||||
end
|
||||
|
||||
def scope
|
||||
@block_env.scope
|
||||
end
|
||||
end
|
||||
|
||||
def from_proc?
|
||||
@proc_environment
|
||||
end
|
||||
|
||||
def under_context(scope, code)
|
||||
@top_scope = scope
|
||||
@scope = scope
|
||||
@compiled_code = code
|
||||
@lexical_scope = code.scope
|
||||
@module = code.scope.module
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
def change_name(name)
|
||||
@compiled_code = @compiled_code.change_name name
|
||||
end
|
||||
|
||||
def repoint_scope(where)
|
||||
@lexical_scope.using_current_as where
|
||||
end
|
||||
|
||||
def disable_scope!
|
||||
@lexical_scope.using_disabled_scope
|
||||
end
|
||||
|
||||
def to_binding
|
||||
Binding.setup @scope, @compiled_code, @lexical_scope
|
||||
end
|
||||
|
||||
def make_independent
|
||||
@top_scope = @top_scope.dup
|
||||
@scope = @scope.dup
|
||||
end
|
||||
|
||||
def call_on_instance(obj, *args)
|
||||
call_under obj, @lexical_scope, false, *args
|
||||
end
|
||||
|
||||
def arity
|
||||
@compiled_code.arity
|
||||
end
|
||||
|
||||
def parameters
|
||||
@compiled_code.parameters
|
||||
end
|
||||
|
||||
def file
|
||||
@compiled_code.file
|
||||
end
|
||||
|
||||
def line
|
||||
@compiled_code.defined_line
|
||||
end
|
||||
|
||||
def source_location
|
||||
[file.to_s, line]
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
return false unless other.kind_of? BlockEnvironment
|
||||
|
||||
@top_scope == other.top_scope and
|
||||
@scope == other.scope and
|
||||
@module == other.module and
|
||||
@compiled_code.equivalent_body?(other.compiled_code)
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<#{self.class.name}:0x#{self.object_id.to_s(16)} scope=#{@scope.inspect} top_scope=#{@top_scope.inspect} module=#{@module.inspect} compiled_code=#{@compiled_code.inspect} lexical_scope=#{@lexical_scope.inspect}>"
|
||||
end
|
||||
end
|
||||
end
|
||||
127
gxg/core/byte_array.rb
Normal file
127
gxg/core/byte_array.rb
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
##
|
||||
# An array of bytes, used as a low-level data store for implementing various
|
||||
# other classes.
|
||||
module Rubinius
|
||||
class ByteArray
|
||||
def self.allocate
|
||||
raise TypeError, "ByteArray cannot be created via allocate()"
|
||||
end
|
||||
|
||||
def self.allocate_sized(cnt)
|
||||
Rubinius.primitive :bytearray_allocate
|
||||
raise PrimitiveFailure, "ByteArray#allocate primitive failed"
|
||||
end
|
||||
|
||||
def self.new(cnt)
|
||||
obj = allocate_sized cnt
|
||||
Rubinius.asm(obj) do |obj|
|
||||
run obj
|
||||
send :initialize, 0, true
|
||||
end
|
||||
|
||||
obj
|
||||
end
|
||||
|
||||
def fetch_bytes(start, count)
|
||||
Rubinius.primitive :bytearray_fetch_bytes
|
||||
raise PrimitiveFailure, "ByteArray#fetch_bytes primitive failed"
|
||||
end
|
||||
|
||||
def move_bytes(start, count, dest)
|
||||
Rubinius.primitive :bytearray_move_bytes
|
||||
raise ArgumentError, "ByteArray#move_bytes primitive failed"
|
||||
end
|
||||
|
||||
def get_byte(index)
|
||||
Rubinius.primitive :bytearray_get_byte
|
||||
raise PrimitiveFailure, "ByteArray#get_byte primitive failed"
|
||||
end
|
||||
|
||||
alias_method :[], :get_byte
|
||||
|
||||
def set_byte(index, value)
|
||||
Rubinius.primitive :bytearray_set_byte
|
||||
raise PrimitiveFailure, "ByteArray#set_byte primitive failed"
|
||||
end
|
||||
|
||||
alias_method :[]=, :set_byte
|
||||
|
||||
def compare_bytes(other, a, b)
|
||||
Rubinius.primitive :bytearray_compare_bytes
|
||||
raise PrimitiveFailure, "ByteArray#compare_bytes primitive failed"
|
||||
end
|
||||
|
||||
def size
|
||||
Rubinius.primitive :bytearray_size
|
||||
raise PrimitiveFailure, "ByteArray#size primitive failed"
|
||||
end
|
||||
|
||||
def dup
|
||||
obj = Rubinius::Type.object_class(self).allocate_sized(self.size)
|
||||
|
||||
Rubinius.invoke_primitive :object_copy_object, obj, self
|
||||
|
||||
Rubinius.privately do
|
||||
obj.initialize_copy self
|
||||
end
|
||||
|
||||
return obj
|
||||
end
|
||||
|
||||
##
|
||||
# Searches for +pattern+ in the ByteArray. Returns the number
|
||||
# of characters from the front of the ByteArray to the end
|
||||
# of the pattern if a match is found. Returns Qnil if a match
|
||||
# is not found. Starts searching at index +start+.
|
||||
def locate(pattern, start, max)
|
||||
Rubinius.primitive :bytearray_locate
|
||||
raise PrimitiveFailure, "ByteArray#locate primitive failed"
|
||||
end
|
||||
|
||||
# Return a new ByteArray by taking the bytes from +string+ and +self+
|
||||
# together.
|
||||
def prepend(string)
|
||||
Rubinius.primitive :bytearray_prepend
|
||||
|
||||
if string.kind_of? String
|
||||
raise PrimitiveFailure, "ByteArray#prepend failed"
|
||||
else
|
||||
prepend(StringValue(string))
|
||||
end
|
||||
end
|
||||
|
||||
def reverse(start, total)
|
||||
Rubinius.primitive :bytearray_reverse
|
||||
raise PrimitiveFailure, "ByteArray#reverse primitive failed"
|
||||
end
|
||||
|
||||
# TODO: encoding
|
||||
def character_at_index(index)
|
||||
"" << self[index]
|
||||
end
|
||||
|
||||
def each
|
||||
i = 0
|
||||
max = size()
|
||||
|
||||
while i < max
|
||||
yield get_byte(i)
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<#{self.class}:0x#{object_id.to_s(16)} #{size} bytes>"
|
||||
end
|
||||
|
||||
def <=>(other)
|
||||
compare_bytes other, size, other.size
|
||||
end
|
||||
|
||||
# Sets the first character to be an ASCII capitalize letter
|
||||
# if it's an ASCII lower case letter
|
||||
def first_capitalize!
|
||||
self[0] = self[0].toupper
|
||||
end
|
||||
end
|
||||
end
|
||||
76
gxg/core/call_site.rb
Normal file
76
gxg/core/call_site.rb
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
module Rubinius
|
||||
class CallSite
|
||||
attr_reader :name
|
||||
attr_reader :cache
|
||||
|
||||
def ip
|
||||
Rubinius.primitive :call_site_ip
|
||||
raise PrimitiveFailure, "CallSite#ip primitive failed"
|
||||
end
|
||||
|
||||
def size
|
||||
Rubinius.primitive :call_site_size
|
||||
raise PrimitiveFailure, "CallSite#size primitive failed"
|
||||
end
|
||||
|
||||
def invokes
|
||||
Rubinius.primitive :call_site_invokes
|
||||
raise PrimitiveFailure, "CallSite#invokes primitive failed"
|
||||
end
|
||||
|
||||
def hits
|
||||
Rubinius.primitive :call_site_hits
|
||||
raise PrimitiveFailure, "CallSite#hits primitive failed"
|
||||
end
|
||||
|
||||
def misses
|
||||
Rubinius.primitive :call_site_misses
|
||||
raise PrimitiveFailure, "CallSite#misses primitive failed"
|
||||
end
|
||||
|
||||
def evictions
|
||||
Rubinius.primitive :call_site_evictions
|
||||
raise PrimitiveFailure, "CallSite#evictions primitive failed"
|
||||
end
|
||||
|
||||
def rotations
|
||||
Rubinius.primitive :call_site_rotations
|
||||
raise PrimitiveFailure, "CallSite#rotations primitive failed"
|
||||
end
|
||||
|
||||
def dead_list_size
|
||||
Rubinius.primitive :call_site_dead_list_size
|
||||
raise PrimitiveFailure, "CallSite#dead_list_size primitive failed"
|
||||
end
|
||||
|
||||
def cache_entries
|
||||
Rubinius.primitive :call_site_cache_entries
|
||||
raise PrimitiveFailure, "CallSite#caches primitive failed"
|
||||
end
|
||||
|
||||
def reset
|
||||
Rubinius.primitive :call_site_reset
|
||||
raise PrimitiveFailure, "CallSite#reset primitive failed"
|
||||
end
|
||||
|
||||
def inspect
|
||||
str = "#<#{self.class.name}:0x#{self.object_id.to_s(16)} name=#{@name} ip=#{ip} " \
|
||||
"size=#{size} invokes=#{invokes} hits=#{hits} misses=#{misses} " \
|
||||
"evictions=#{evictions} rotations=#{rotations} dead_list=#{dead_list_size}"
|
||||
|
||||
cache_entries.each do |entry|
|
||||
str << " #<Entry"
|
||||
i = 0
|
||||
while i < entry.size
|
||||
str << " #{entry[i]}=#{entry[i+1]}"
|
||||
i += 2
|
||||
end
|
||||
str << ">"
|
||||
end
|
||||
|
||||
str << ">"
|
||||
end
|
||||
|
||||
alias_method :to_s, :inspect
|
||||
end
|
||||
end
|
||||
166
gxg/core/capi.rb
Normal file
166
gxg/core/capi.rb
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
module Rubinius
|
||||
# CAPI contains utility methods used by capi.
|
||||
module CAPI
|
||||
def self.coerce(x, y, error=false)
|
||||
begin
|
||||
ary = y.coerce x
|
||||
rescue
|
||||
if error
|
||||
raise TypeError, "#{y.inspect} can't be coerced into #{x.class}"
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
unless ary.kind_of? Array and ary.size == 2
|
||||
if error
|
||||
raise TypeError, "coerce must return [x, y]"
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return ary
|
||||
end
|
||||
|
||||
# C-API method corresponding to rb_num_coerce_bin
|
||||
def self.rb_num_coerce_bin(x, y, op)
|
||||
a, b = coerce x, y, true
|
||||
|
||||
a.__send__ op, b
|
||||
end
|
||||
|
||||
# C-API method corresponding to rb_num_coerce_cmp
|
||||
def self.rb_num_coerce_cmp(x, y, op)
|
||||
ary = coerce x, y
|
||||
|
||||
return ary.first.__send__ op, ary.last if ary
|
||||
end
|
||||
|
||||
# C-API method corresponding to rb_num_coerce_relop
|
||||
def self.rb_num_coerce_relop(x, y, op)
|
||||
ary = coerce x, y, false
|
||||
|
||||
if ary
|
||||
rel = ary.first.__send__ op, ary.last
|
||||
return rel unless rel.equal? nil
|
||||
end
|
||||
|
||||
raise ArgumentError, "comparison of #{x.class} with #{y.class} failed"
|
||||
end
|
||||
|
||||
def self.sprintf(format, args)
|
||||
::Rubinius::Sprinter.get(format).call(*args)
|
||||
end
|
||||
|
||||
def self.rb_str_encode(str, enc, flags, opts)
|
||||
# TODO: set flags from opts
|
||||
if str.encoding == enc
|
||||
str.dup
|
||||
else
|
||||
ec = Encoding::Converter.new str.encoding, enc, flags
|
||||
ec.replacement = opts[:replace] if opts
|
||||
|
||||
ec.convert str
|
||||
end
|
||||
end
|
||||
|
||||
def self.rb_str_conv_enc_opts(str, from, to, flags, opts)
|
||||
return str if to.nil?
|
||||
return str if from == to
|
||||
|
||||
if (to.ascii_compatible? and str.ascii_only?) or to == Encoding::ASCII_8BIT
|
||||
if str.encoding != to
|
||||
return str.dup.force_encoding(to)
|
||||
else
|
||||
return str
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: set flags from opts
|
||||
ec = Encoding::Converter.new from, to, flags
|
||||
ec.replacement = opts[:replace] if opts
|
||||
|
||||
result = ""
|
||||
status = ec.primitive_convert str.dup, result, 0, nil, flags
|
||||
|
||||
return status == :finished ? result : str
|
||||
end
|
||||
|
||||
def self.rb_integer_pack(value, words, numwords, wordsize, nails, flags)
|
||||
digits = INTEGER_PACK_DIGITS
|
||||
numbytes = numwords * wordsize
|
||||
nibble_size = numbytes * 2
|
||||
|
||||
nibbles = value.abs.to_s(16)
|
||||
nibbles = nibbles[-nibble_size..-1] if nibble_size < nibbles.size
|
||||
|
||||
complement = value < 0 && (flags & INTEGER_PACK_2COMP) != 0
|
||||
|
||||
if (ENDIAN == :little and (flags & INTEGER_PACK_BIG_ENDIAN) != 0) or
|
||||
(ENDIAN == :big and (flags & INTEGER_PACK_LITTLE_ENDIAN) != 0)
|
||||
c_index = index = numbytes - 1
|
||||
incr = -1
|
||||
else
|
||||
c_index = index = 0
|
||||
incr = 1
|
||||
end
|
||||
|
||||
i = nibbles.size - 1
|
||||
stop = nibbles.size & 1
|
||||
|
||||
while i > stop
|
||||
high = digits[nibbles.getbyte(i - 1)]
|
||||
low = digits[nibbles.getbyte(i)]
|
||||
byte = (high << 4) | low
|
||||
|
||||
byte ^= 0xff if complement
|
||||
words.setbyte index, byte
|
||||
|
||||
index += incr
|
||||
i -= 2
|
||||
end
|
||||
|
||||
if stop == 1
|
||||
byte = digits[nibbles.getbyte(0)]
|
||||
byte ^= 0xff if complement
|
||||
words.setbyte index, byte
|
||||
end
|
||||
|
||||
if complement
|
||||
c_stop = c_index == 0 ? numbytes : -1
|
||||
|
||||
while c_index != c_stop
|
||||
byte = words.getbyte(c_index) + 1
|
||||
words.setbyte(c_index, byte)
|
||||
|
||||
break if byte <= 0xff
|
||||
|
||||
c_index += incr
|
||||
end
|
||||
end
|
||||
|
||||
overflow = 2**(numwords * wordsize * 8)
|
||||
|
||||
if value < 0
|
||||
if complement
|
||||
return -2 if value < -overflow
|
||||
else
|
||||
return -2 if value <= -overflow
|
||||
end
|
||||
return -1
|
||||
else
|
||||
return 2 if value >= overflow
|
||||
return 1
|
||||
end
|
||||
end
|
||||
|
||||
def self.rb_block_proc(env)
|
||||
Rubinius::Mirror::Proc.from_block ::Proc, env
|
||||
end
|
||||
|
||||
def self.rb_obj_method_arity(obj, sym)
|
||||
obj.method(sym).arity
|
||||
end
|
||||
end
|
||||
end
|
||||
40
gxg/core/channel.rb
Normal file
40
gxg/core/channel.rb
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# Channel is a FIFO. If #receive is called and no value is available, the
|
||||
# Thread will sleep until a value is available. If #receive_timeout is called
|
||||
# and no value is availale, the Thread will sleep until a value is available
|
||||
# or duration time has elapsed. If #try_receive is called, it returns
|
||||
# immediately with a value or nil. It's impossible to know whether nil was in
|
||||
# the channel in the case that the return value is nil.
|
||||
module Rubinius
|
||||
class Channel
|
||||
def self.allocate
|
||||
Rubinius.primitive :channel_new
|
||||
raise PrimitiveFailure, "Channel.allocate primitive failed"
|
||||
end
|
||||
|
||||
def send(obj)
|
||||
Rubinius.primitive :channel_send
|
||||
raise PrimitiveFailure, "Channel#send primitive failed"
|
||||
end
|
||||
|
||||
alias_method :<<, :send
|
||||
|
||||
def receive
|
||||
Rubinius.primitive :channel_receive
|
||||
raise PrimitiveFailure, "Channel#receive primitive failed"
|
||||
end
|
||||
|
||||
def receive_timeout(duration)
|
||||
Rubinius.primitive :channel_receive_timeout
|
||||
raise PrimitiveFailure, "Channel#receive_timeout primitive failed"
|
||||
end
|
||||
|
||||
def try_receive
|
||||
Rubinius.primitive :channel_try_receive
|
||||
raise PrimitiveFailure, "Channel#try_receive primitive failed"
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<Rubinius::Channel:0x#{object_id.to_s(16)}>"
|
||||
end
|
||||
end
|
||||
end
|
||||
55
gxg/core/character.rb
Normal file
55
gxg/core/character.rb
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
module Rubinius
|
||||
class Character < ::String
|
||||
def self.__allocate__
|
||||
Rubinius.primitive :character_allocate
|
||||
raise PrimitiveFailure, "Rubinius::Character.allocate primitive failed"
|
||||
end
|
||||
|
||||
def self.allocate
|
||||
chr = __allocate__
|
||||
chr.__data__ = Rubinius::ByteArray.allocate_sized(1)
|
||||
chr.num_bytes = 0
|
||||
chr
|
||||
end
|
||||
|
||||
def alphabetical?
|
||||
Rubinius.primitive :character_alphabetical_p
|
||||
raise PrimitiveFailure, "Rubinius::Character#alphabetical? primitive failed"
|
||||
end
|
||||
|
||||
def ascii?
|
||||
Rubinius.primitive :character_ascii_p
|
||||
raise PrimitiveFailure, "Rubinius::Character#ascii? primitive failed"
|
||||
end
|
||||
|
||||
def digit?
|
||||
Rubinius.primitive :character_digit_p
|
||||
raise PrimitiveFailure, "Rubinius::Character#digit? primitive failed"
|
||||
end
|
||||
|
||||
def lower?
|
||||
Rubinius.primitive :character_lower_p
|
||||
raise PrimitiveFailure, "Rubinius::Character#lower? primitive failed"
|
||||
end
|
||||
|
||||
def printable?
|
||||
Rubinius.primitive :character_printable_p
|
||||
raise PrimitiveFailure, "Rubinius::Character#printable? primitive failed"
|
||||
end
|
||||
|
||||
def punctuation?
|
||||
Rubinius.primitive :character_punctuation_p
|
||||
raise PrimitiveFailure, "Rubinius::Character#punctuation? primitive failed"
|
||||
end
|
||||
|
||||
def space?
|
||||
Rubinius.primitive :character_space_p
|
||||
raise PrimitiveFailure, "Rubinius::Character#space? primitive failed"
|
||||
end
|
||||
|
||||
def upper?
|
||||
Rubinius.primitive :character_upper_p
|
||||
raise PrimitiveFailure, "Rubinius::Character#upper? primitive failed"
|
||||
end
|
||||
end
|
||||
end
|
||||
67
gxg/core/class.rb
Normal file
67
gxg/core/class.rb
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
class Class
|
||||
def self.allocate
|
||||
Rubinius.primitive :class_s_allocate
|
||||
raise PrimitiveFailure, "Class.allocate primitive failed"
|
||||
end
|
||||
|
||||
def set_superclass(sup)
|
||||
Rubinius.primitive :class_set_superclass
|
||||
kind = Rubinius::Type.object_class(sup)
|
||||
raise TypeError, "Class.set_superclass: argument must be a Class (#{kind} given)"
|
||||
end
|
||||
|
||||
private :set_superclass
|
||||
|
||||
def initialize(sclass=Object, name=nil, under=nil)
|
||||
raise TypeError, "already initialized class" if @instance_type
|
||||
raise TypeError, "can't make subclass of Class" if Class.equal?(sclass)
|
||||
|
||||
set_superclass sclass
|
||||
|
||||
# Things (rails) depend on the fact that a normal class is in the constant
|
||||
# table and have a name BEFORE inherited is run.
|
||||
under.const_set name, self if under
|
||||
|
||||
if sclass
|
||||
Rubinius.privately do
|
||||
sclass.inherited self
|
||||
end
|
||||
end
|
||||
super()
|
||||
end
|
||||
private :initialize
|
||||
|
||||
##
|
||||
# Specialized initialize_copy because Class needs additional protection
|
||||
def initialize_copy(other)
|
||||
# The code in super (Module#initialize_copy) will duplicate
|
||||
# the method table. Therefore do this check here to see whether
|
||||
# we've already initialized this class before continuing.
|
||||
unless @method_table == other.method_table
|
||||
raise TypeError, "already initialized class"
|
||||
end
|
||||
|
||||
@module_name = nil
|
||||
super
|
||||
end
|
||||
|
||||
private :initialize_copy
|
||||
|
||||
def inherited(name)
|
||||
end
|
||||
private :inherited
|
||||
|
||||
def superclass
|
||||
cls = direct_superclass
|
||||
unless cls
|
||||
return nil if self == BasicObject
|
||||
raise TypeError, "uninitialized class"
|
||||
end
|
||||
|
||||
while cls and cls.kind_of? Rubinius::IncludedModule
|
||||
cls = cls.direct_superclass
|
||||
end
|
||||
|
||||
return cls
|
||||
end
|
||||
end
|
||||
41
gxg/core/code_db.rb
Normal file
41
gxg/core/code_db.rb
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
module Rubinius
|
||||
class CodeDB
|
||||
attr_accessor :loaded_features
|
||||
attr_accessor :load_path
|
||||
|
||||
class << self
|
||||
# CodeDB.open is the constructor
|
||||
private :new
|
||||
end
|
||||
|
||||
def self.open(path)
|
||||
Rubinius.primitive :code_db_open
|
||||
raise PrimitiveFailure, "Rubinius::CodeDB.open primitive failed"
|
||||
end
|
||||
|
||||
def load_db
|
||||
Rubinius.primitive :code_db_load_db
|
||||
raise PrimitiveFailure, "Rubinius::CodeDB#load_db primitive failed"
|
||||
end
|
||||
|
||||
def close
|
||||
Rubinius.primitive :code_db_close
|
||||
raise PrimitiveFailure, "Rubinius::CodeDB#close primitive failed"
|
||||
end
|
||||
|
||||
def load(id)
|
||||
Rubinius.primitive :code_db_load
|
||||
raise PrimitiveFailure, "Rubinius::CodeDB#load primitive failed"
|
||||
end
|
||||
|
||||
def load_feature(stem, ext, reload, record)
|
||||
Rubinius.primitive :code_db_load_feature
|
||||
raise PrimitiveFailure, "Rubinius::CodeDB#load_feature primitive failed"
|
||||
end
|
||||
|
||||
def store(code, stem, path, feature, record)
|
||||
Rubinius.primitive :code_db_store
|
||||
raise PrimitiveFailure, "Rubinius::CodeDB#store primitive failed"
|
||||
end
|
||||
end
|
||||
end
|
||||
348
gxg/core/code_loader.rb
Normal file
348
gxg/core/code_loader.rb
Normal file
|
|
@ -0,0 +1,348 @@
|
|||
module Rubinius
|
||||
class CodeLoader
|
||||
def self.registry
|
||||
Rubinius.synchronize(self) { @registry ||= [] }
|
||||
end
|
||||
|
||||
def self.loading?(path)
|
||||
feature = nil
|
||||
|
||||
resource = Feature.new path
|
||||
if resource.loadable? or resource.search
|
||||
feature = resource.feature
|
||||
end
|
||||
|
||||
unless feature
|
||||
resource = Library.new path
|
||||
if resource.loadable? or resource.search
|
||||
feature = resource.feature
|
||||
end
|
||||
end
|
||||
|
||||
if feature
|
||||
Rubinius.synchronize(registry) do
|
||||
return true if registry.include? feature
|
||||
end
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
class Resource
|
||||
attr_reader :feature
|
||||
|
||||
def initialize(stem, ext)
|
||||
stem = Type.coerce_to_path stem
|
||||
stem = "#{stem}#{ext}" unless stem.suffix? ext
|
||||
|
||||
@relative = false
|
||||
|
||||
if stem[0] == ?~
|
||||
@prefix = ""
|
||||
stem = File.expand_path stem
|
||||
elsif stem[0] == ?/
|
||||
@prefix = ""
|
||||
elsif stem[0] == ?. and (stem[1] == ?/ or
|
||||
(stem[1] == ?. and stem[2] == ?/))
|
||||
@relative = true
|
||||
@prefix = Dir.pwd
|
||||
else
|
||||
@prefix = nil
|
||||
end
|
||||
|
||||
@stem = stem
|
||||
@feature = nil
|
||||
@record = true
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def resolve
|
||||
return true if loadable?
|
||||
return true if search
|
||||
return false
|
||||
end
|
||||
|
||||
def search
|
||||
return false if @relative
|
||||
|
||||
$LOAD_PATH.each do |dir|
|
||||
if dir[-1] == ?/
|
||||
@prefix = dir[0...-1]
|
||||
else
|
||||
@prefix = dir
|
||||
end
|
||||
|
||||
return true if loadable?
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
def loadable?
|
||||
feature = "#{@prefix}/#{@stem}"
|
||||
|
||||
stat = File::Stat.stat feature
|
||||
|
||||
if stat and stat.file? and stat.readable?
|
||||
@feature = File.expand_path feature
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
def unload_feature
|
||||
Rubinius.synchronize($LOADED_FEATURES) do
|
||||
$LOADED_FEATURES.delete_if { |f| f == @feature }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Source < Resource
|
||||
def initialize(stem, ext)
|
||||
super stem, ext
|
||||
|
||||
@code = nil
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def load
|
||||
if @code
|
||||
@feature = @code.file.to_s
|
||||
else
|
||||
@code = ToolSets::Runtime::Compiler.compile_file @feature
|
||||
CoreDB.store @code, @stem, @prefix, @feature, @record
|
||||
end
|
||||
|
||||
run_script
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
def make_script
|
||||
script = @code.create_script @wrap
|
||||
script.file_path = script.data_path = @code.file.to_s
|
||||
script
|
||||
end
|
||||
|
||||
def run_script
|
||||
make_script
|
||||
Rubinius.run_script @code
|
||||
rescue Object
|
||||
unload_feature
|
||||
raise
|
||||
end
|
||||
end
|
||||
|
||||
class Feature < Source
|
||||
def initialize(stem)
|
||||
super stem, CodeLoader.source_extension
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def resolve
|
||||
@code = CoreDB.load_feature @stem, CodeLoader.source_extension, false, @record
|
||||
|
||||
return true if @code or false.equal? @code
|
||||
return true if loadable?
|
||||
return true if search
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
def load
|
||||
return false if false.equal? @code
|
||||
|
||||
super()
|
||||
end
|
||||
end
|
||||
|
||||
class CodeModule < Source
|
||||
def initialize(stem, wrap)
|
||||
super stem, ""
|
||||
|
||||
@wrap = wrap
|
||||
@record = false
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def resolve
|
||||
@code = CoreDB.load_feature @stem, CodeLoader.source_extension, true, @record
|
||||
|
||||
return true if @code
|
||||
return true if loadable?
|
||||
return true if search
|
||||
|
||||
@prefix = Dir.pwd
|
||||
return true if loadable?
|
||||
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
class Script < Source
|
||||
def initialize(stem)
|
||||
super File.expand_path(stem), ""
|
||||
|
||||
@file = stem
|
||||
@record = false
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def resolve
|
||||
@code = CoreDB.load_feature @stem, "", true, @record
|
||||
|
||||
return true if @code
|
||||
return true if loadable?
|
||||
return false
|
||||
end
|
||||
|
||||
def make_script
|
||||
script = super()
|
||||
script.file_path = @file
|
||||
script.make_main!
|
||||
end
|
||||
end
|
||||
|
||||
class Library < Resource
|
||||
def initialize(stem)
|
||||
if stem.suffix? ".so"
|
||||
super stem[0...-3], CodeLoader.library_extension
|
||||
else
|
||||
super stem, CodeLoader.library_extension
|
||||
end
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def load
|
||||
CoreDB.store nil, @stem, @prefix, @feature, @record
|
||||
|
||||
name = File.basename @feature, CodeLoader.library_extension
|
||||
NativeMethod.load_extension(@feature, name)
|
||||
|
||||
return true
|
||||
rescue Object
|
||||
unload_feature
|
||||
raise
|
||||
end
|
||||
end
|
||||
|
||||
def self.source_extension
|
||||
".rb"
|
||||
end
|
||||
|
||||
def self.library_extension
|
||||
LIBSUFFIX
|
||||
end
|
||||
|
||||
# Loads rubygems using the bootstrap standard library files.
|
||||
def self.load_rubygems
|
||||
require "rubygems"
|
||||
end
|
||||
|
||||
# Loads the pre-compiled bytecode compiler. Sets up paths needed by the
|
||||
# compiler to find dependencies like the parser.
|
||||
def self.load_compiler
|
||||
begin
|
||||
require "rubinius/code/toolset"
|
||||
|
||||
ToolSets.create :runtime do
|
||||
begin
|
||||
require "rubinius/code/melbourne"
|
||||
rescue LoadError
|
||||
STDERR.puts "Melbourne failed to load, Ruby source parsing disabled"
|
||||
end
|
||||
require "rubinius/code/processor"
|
||||
require "rubinius/code/compiler"
|
||||
require "rubinius/code/ast"
|
||||
end
|
||||
rescue Object => e
|
||||
raise LoadError, "Unable to load the bytecode compiler", e
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(stem)
|
||||
@stem = stem
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def load_script
|
||||
resource = Script.new @stem
|
||||
|
||||
load_error unless resource.resolve
|
||||
resource.load
|
||||
end
|
||||
|
||||
def load(wrap)
|
||||
resource = CodeModule.new @stem, wrap
|
||||
|
||||
load_error unless resource.resolve
|
||||
resource.load
|
||||
end
|
||||
|
||||
def require
|
||||
resource = Feature.new @stem
|
||||
if resource.resolve
|
||||
register_file resource.feature
|
||||
return resource.load
|
||||
end
|
||||
|
||||
resource = Library.new @stem
|
||||
if resource.resolve
|
||||
register_file resource.feature
|
||||
return resource.load
|
||||
end
|
||||
|
||||
load_error
|
||||
ensure
|
||||
unregister_file resource.feature if resource
|
||||
end
|
||||
|
||||
def require_relative(scope)
|
||||
script = scope.current_script
|
||||
if script
|
||||
if script.data_path
|
||||
path = File.dirname(File.realdirpath(script.data_path))
|
||||
else
|
||||
path = Dir.pwd
|
||||
end
|
||||
|
||||
@stem = File.expand_path(@stem, path)
|
||||
|
||||
require
|
||||
else
|
||||
raise LoadError.new "unable to get relative path"
|
||||
end
|
||||
end
|
||||
|
||||
def register_file(path)
|
||||
registry = self.class.registry
|
||||
|
||||
Rubinius.synchronize(registry) do
|
||||
registry << path
|
||||
end
|
||||
end
|
||||
|
||||
def unregister_file(path)
|
||||
registry = self.class.registry
|
||||
|
||||
Rubinius.synchronize(registry) do
|
||||
registry.delete_if { |x| x == path }
|
||||
end
|
||||
end
|
||||
|
||||
def load_error
|
||||
# Some versions of Rails override LoadError.new but do NOT override
|
||||
# LoadError.exception, so we must use LoadError.new here to match MRI
|
||||
# behavior.
|
||||
raise LoadError.new("no such file to load -- #{@stem}", path: @stem)
|
||||
end
|
||||
end
|
||||
end
|
||||
33
gxg/core/compact_lookup_table.rb
Normal file
33
gxg/core/compact_lookup_table.rb
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
module Rubinius
|
||||
class CompactLookupTable
|
||||
def self.allocate
|
||||
Rubinius.primitive :compactlookuptable_allocate
|
||||
raise PrimitiveFailure, "CompactLookupTable.allocate primitive failed"
|
||||
end
|
||||
|
||||
def [](key)
|
||||
Rubinius.primitive :compactlookuptable_fetch
|
||||
raise PrimitiveFailure, "CompactLookupTable#[] primitive failed"
|
||||
end
|
||||
|
||||
def []=(key, val)
|
||||
Rubinius.primitive :compactlookuptable_store
|
||||
raise PrimitiveFailure, "CompactLookupTable#[]= primitive failed"
|
||||
end
|
||||
|
||||
def key?(key)
|
||||
Rubinius.primitive :compactlookuptable_has_key
|
||||
raise PrimitiveFailure, "CompactLookupTable#key? primitive failed"
|
||||
end
|
||||
|
||||
def keys
|
||||
Rubinius.primitive :compactlookuptable_keys
|
||||
raise PrimitiveFailure, "CompactLookupTable#keys primitive failed"
|
||||
end
|
||||
|
||||
def values
|
||||
Rubinius.primitive :compactlookuptable_values
|
||||
raise PrimitiveFailure, "CompactLookupTable#keys primitive failed"
|
||||
end
|
||||
end
|
||||
end
|
||||
63
gxg/core/comparable.rb
Normal file
63
gxg/core/comparable.rb
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
module Comparable
|
||||
def ==(other)
|
||||
return true if equal?(other)
|
||||
|
||||
return false if Thread.detect_recursion(self, other) do
|
||||
unless comp = (self <=> other)
|
||||
return false
|
||||
end
|
||||
|
||||
return Comparable.compare_int(comp) == 0
|
||||
end
|
||||
end
|
||||
|
||||
def >(other)
|
||||
unless comp = (self <=> other)
|
||||
raise ArgumentError, "comparison of #{self.class} with #{other.class}"
|
||||
end
|
||||
|
||||
Comparable.compare_int(comp) > 0
|
||||
end
|
||||
|
||||
def >=(other)
|
||||
unless comp = (self <=> other)
|
||||
raise ArgumentError, "comparison of #{self.class} with #{other.class}"
|
||||
end
|
||||
|
||||
Comparable.compare_int(comp) >= 0
|
||||
end
|
||||
|
||||
def <(other)
|
||||
unless comp = (self <=> other)
|
||||
raise ArgumentError, "comparison of #{self.class} with #{other.class}"
|
||||
end
|
||||
|
||||
Comparable.compare_int(comp) < 0
|
||||
end
|
||||
|
||||
def <=(other)
|
||||
unless comp = (self <=> other)
|
||||
raise ArgumentError, "comparison of #{self.class} with #{other.class}"
|
||||
end
|
||||
|
||||
Comparable.compare_int(comp) <= 0
|
||||
end
|
||||
|
||||
def between?(min, max)
|
||||
# This could be more elegant, but we need to use <= and => on self to match
|
||||
# MRI.
|
||||
return false if self < min
|
||||
return false if self > max
|
||||
return true
|
||||
end
|
||||
|
||||
# A version of MRI's rb_cmpint (sort of)
|
||||
def self.compare_int(int)
|
||||
return int if int.kind_of? Fixnum
|
||||
|
||||
return 1 if int > 0
|
||||
return -1 if int < 0
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
726
gxg/core/compiled_code.rb
Normal file
726
gxg/core/compiled_code.rb
Normal file
|
|
@ -0,0 +1,726 @@
|
|||
module Rubinius
|
||||
class CompiledCode < Executable
|
||||
def self.allocate
|
||||
Rubinius.primitive :compiledcode_allocate
|
||||
raise PrimitiveFailure, "CompiledCode.allocate primitive failed"
|
||||
end
|
||||
|
||||
def dup
|
||||
Rubinius.primitive :compiledcode_dup
|
||||
raise PrimitiveFailure, "CompiledCode#dup primitive failed"
|
||||
end
|
||||
|
||||
def call_sites
|
||||
Rubinius.primitive :compiledcode_call_sites
|
||||
raise PrimitiveFailure, "CompiledCode#call_sites primitive failed"
|
||||
end
|
||||
|
||||
def code_id
|
||||
@code_id || stamp_id
|
||||
end
|
||||
|
||||
def constant_caches
|
||||
Rubinius.primitive :compiledcode_constant_caches
|
||||
raise PrimitiveFailure, "CompiledCode#constant_caches primitive failed"
|
||||
end
|
||||
|
||||
# Return the CompiledCode for caller of the method that called
|
||||
# .of_sender.
|
||||
#
|
||||
# For example:
|
||||
#
|
||||
# def g
|
||||
# f_code = Rubinius::CompiledCode.of_sender
|
||||
# end
|
||||
#
|
||||
# def f
|
||||
# g
|
||||
# end
|
||||
#
|
||||
# f_code is the CompiledCode of f as requested by g.
|
||||
#
|
||||
def self.of_sender
|
||||
Rubinius.primitive :compiledcode_of_sender
|
||||
raise PrimitiveFailure, "CompiledCode.of_sender primitive failed"
|
||||
end
|
||||
|
||||
def jitted?
|
||||
Rubinius.primitive :compiledcode_jitted_p
|
||||
raise PrimitiveFailure, "CompiledCode#jitted? primitive failed"
|
||||
end
|
||||
|
||||
def sample_count
|
||||
Rubinius.primitive :compiledcode_sample_count
|
||||
raise PrimitiveFailure, "CompiledCode#sample_count primitive failed"
|
||||
end
|
||||
|
||||
# Returns the CompiledCode object for the currently executing Ruby
|
||||
# method. For example:
|
||||
#
|
||||
# def m
|
||||
# p Rubinius::CompiledCode.current.name
|
||||
# end
|
||||
#
|
||||
def self.current
|
||||
Rubinius.primitive :compiledcode_current
|
||||
raise PrimitiveFailure, "CompiledCode.current primitive failed"
|
||||
end
|
||||
|
||||
##
|
||||
# Any CompiledCode with this value in it's serial slot is expected to be
|
||||
# the default, core library version.
|
||||
KernelMethodSerial = 47
|
||||
|
||||
##
|
||||
# Ivars: instance_variables, primitive, serial, name, iseq, stack_size,
|
||||
# local_count, required_args, total_args, splat, literals, exceptions,
|
||||
# lines, file, compiled, scope
|
||||
##
|
||||
# This is runtime hints, added to the method by the VM to indicate how it's
|
||||
# being used.
|
||||
|
||||
attr_accessor :name
|
||||
attr_accessor :hints # added by the VM to indicate how it's being used.
|
||||
attr_accessor :metadata # [Tuple] extra data
|
||||
attr_accessor :name # [Symbol] name of the method
|
||||
attr_accessor :iseq # [Tuple] instructions to execute
|
||||
attr_accessor :stack_size # [Integer] size of stack at compile time
|
||||
attr_accessor :local_count # [Integer] number of local vars
|
||||
attr_accessor :required_args # [Integer] number of required args
|
||||
attr_accessor :post_args # [Integer] number of args after splat
|
||||
attr_accessor :total_args # [Integer] number of total args
|
||||
attr_accessor :splat # [Integer] POSITION of the splat arg
|
||||
attr_accessor :literals # [Tuple] tuple of the literals
|
||||
attr_accessor :lines # [Tuple] tuple of the lines where its found
|
||||
attr_accessor :file # [Symbol] the file where this comes from
|
||||
attr_accessor :local_names # [Array<Symbol>] names of the local vars
|
||||
attr_accessor :scope # [LexicalScope] scope for looking up constants
|
||||
attr_accessor :keywords # [Tuple] pairs of Symbol name, required flag
|
||||
attr_accessor :arity # [Integer] number of arguments, negative if variadic.
|
||||
attr_accessor :registers # [Integer] number of registers used.
|
||||
|
||||
##
|
||||
# Compare this method with +other+. Instead of bugging out if +other+
|
||||
# isn't a {CompiledCode}, this returns +false+ immediately unless
|
||||
# we're comparing two apples, AKA, {CompiledCode}s. The methods have
|
||||
# to be the exact same in implementation, but their scoping (location)
|
||||
# can differ.
|
||||
#
|
||||
# For instance:
|
||||
#
|
||||
# (module A; def m; 5; end; end) == (def m; 5; end)
|
||||
#
|
||||
# and
|
||||
#
|
||||
# def monkey; 5; end
|
||||
#
|
||||
# and
|
||||
#
|
||||
# module B
|
||||
# def monkey; 5; end
|
||||
# end
|
||||
#
|
||||
# would all be the same, despite their access to different ivars and scopes
|
||||
# (which {CompiledCode}s DO keep track of)
|
||||
#
|
||||
# @todo Make example (in method documentation) match reality
|
||||
# @param [Rubinius::CompiledCode] other the other part to compare
|
||||
# @param [Boolean]
|
||||
#
|
||||
def ==(other)
|
||||
return false unless other.kind_of? CompiledCode
|
||||
equivalent_body?(other) and
|
||||
@lines == other.lines and
|
||||
@name == other.name
|
||||
end
|
||||
|
||||
def equivalent_body?(other)
|
||||
@tags == other.tags and
|
||||
@primitive == other.primitive and
|
||||
@iseq == other.iseq and
|
||||
@stack_size == other.stack_size and
|
||||
@local_count == other.local_count and
|
||||
@required_args == other.required_args and
|
||||
@total_args == other.total_args and
|
||||
@splat == other.splat and
|
||||
block_index == other.block_index and
|
||||
@literals == other.literals and
|
||||
@file == other.file and
|
||||
@local_names == other.local_names
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the index of local +name+ or nil if there is no local.
|
||||
#
|
||||
def local_slot(name)
|
||||
slot = 0
|
||||
while slot < @local_count
|
||||
return slot if @local_names[slot] == name
|
||||
slot += 1
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Stores the index of the block argument in the metadata storage.
|
||||
#
|
||||
# @param [Fixnum] position
|
||||
#
|
||||
def block_index=(position)
|
||||
if position
|
||||
add_metadata(:block_index, position)
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# @return [Fixnum|NilClass]
|
||||
#
|
||||
def block_index
|
||||
return get_metadata(:block_index)
|
||||
end
|
||||
|
||||
##
|
||||
# Return a human readable interpretation of this method.
|
||||
#
|
||||
# @return [String]
|
||||
def inspect
|
||||
"#<#{self.class.name}:0x#{object_id.to_s(16)} name=#{@name} file=#{@file} line=#{defined_line}>"
|
||||
end
|
||||
|
||||
alias_method :to_s, :inspect
|
||||
|
||||
##
|
||||
# Make the method change its scope so that it can act as though
|
||||
# it's from somewhere else. You can pass in a method and +self+
|
||||
# will borrow its scope.
|
||||
#
|
||||
# @param [#scope] other the other method that has a scope we can borrow
|
||||
def inherit_scope(other)
|
||||
@scope = other.scope
|
||||
end
|
||||
|
||||
##
|
||||
# Set a breakpoint on +ip+. +obj+ can be any object. When the breakpoint
|
||||
# is hit +obj+ is sent to the debugger so it can distinguish which breakpoint
|
||||
# this is.
|
||||
#
|
||||
def set_breakpoint(ip, obj)
|
||||
Rubinius.primitive :compiledcode_set_breakpoint
|
||||
raise ArgumentError, "Unable to set breakpoint on #{inspect} at invalid bytecode address #{ip}"
|
||||
end
|
||||
|
||||
##
|
||||
# Erase a breakpoint at +ip+
|
||||
#
|
||||
def clear_breakpoint(ip)
|
||||
Rubinius.primitive :compiledcode_clear_breakpoint
|
||||
raise ArgumentError, "Unable to clear breakpoint on #{inspect} at invalid bytecode address #{ip}"
|
||||
end
|
||||
|
||||
##
|
||||
# Indicate if there is a breakpoint set at +ip+
|
||||
#
|
||||
def breakpoint?(ip)
|
||||
Rubinius.primitive :compiledcode_is_breakpoint
|
||||
raise ArgumentError, "Unable to retrieve breakpoint status on #{inspect} at bytecode address #{ip}"
|
||||
end
|
||||
|
||||
def stamp_id
|
||||
Rubinius.primitive :compiledcode_stamp_id
|
||||
raise ArgumentError, "CompiledCode#stamp_id primitive failed"
|
||||
end
|
||||
|
||||
class Script
|
||||
attr_accessor :compiled_code
|
||||
attr_accessor :file_path
|
||||
attr_accessor :data_path
|
||||
attr_accessor :eval_source
|
||||
|
||||
def initialize(method, path=nil, for_eval=false)
|
||||
@compiled_code = method
|
||||
@file_path = path
|
||||
@for_eval = for_eval
|
||||
@eval_source = nil
|
||||
@main = false
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def eval?
|
||||
@for_eval
|
||||
end
|
||||
|
||||
def make_main!
|
||||
@main = true
|
||||
end
|
||||
|
||||
def main?
|
||||
@main
|
||||
end
|
||||
end
|
||||
|
||||
# Creates the Script instance for a toplevel compiled method.
|
||||
def create_script(wrap=false)
|
||||
script = CompiledCode::Script.new(self)
|
||||
|
||||
# Setup the scoping.
|
||||
cs = LexicalScope.new(Object)
|
||||
cs.script = script
|
||||
|
||||
if wrap
|
||||
@scope = LexicalScope.new(Module.new, cs)
|
||||
else
|
||||
@scope = cs
|
||||
end
|
||||
|
||||
sc = Rubinius::Type.object_singleton_class(MAIN)
|
||||
VM.reset_method_cache sc, :__script__
|
||||
sc.method_table.store :__script__, nil, self, @scope, 0, :public
|
||||
|
||||
script
|
||||
end
|
||||
|
||||
def active_path
|
||||
if @scope and script = @scope.current_script
|
||||
if fp = script.file_path
|
||||
return fp.dup
|
||||
end
|
||||
end
|
||||
|
||||
@file.to_s
|
||||
end
|
||||
|
||||
def eval_source
|
||||
if @scope and script = @scope.current_script
|
||||
return script.eval_source
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
##
|
||||
# Return the line of source code at +ip+.
|
||||
#
|
||||
# @param [Fixnum] ip
|
||||
# @return [Fixnum] line
|
||||
def line_from_ip(ip)
|
||||
return -1 unless @lines
|
||||
return 0 if @lines.size < 2
|
||||
|
||||
low = 0
|
||||
high = @lines.size / 2 - 1
|
||||
|
||||
while low <= high
|
||||
# the chance that we're going from a fixnum to a bignum
|
||||
# here is low, but we still try to prevent that.
|
||||
mid = low + ((high - low) / 2)
|
||||
|
||||
line_index = mid * 2 + 1
|
||||
|
||||
if ip < @lines.at(line_index - 1)
|
||||
high = mid - 1
|
||||
elsif ip >= @lines.at(line_index + 1)
|
||||
low = mid + 1
|
||||
else
|
||||
return @lines.at(line_index)
|
||||
end
|
||||
end
|
||||
|
||||
@lines.at(@lines.size - 2)
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the address (IP) of the first instruction in this CompiledCode
|
||||
# that is on the specified line, or the address of the first instruction on
|
||||
# the next code line after the specified line if there are no instructions
|
||||
# on the requested line.
|
||||
# This method only looks at instructions within the current CompiledCode;
|
||||
# see #locate_line for an alternate method that also searches inside the child
|
||||
# CompiledCodes.
|
||||
#
|
||||
# Optionally only consider ip's greater than +start+
|
||||
#
|
||||
# @return [Fixnum] the address of the first instruction
|
||||
# OR nil if there is no ip for the given line
|
||||
def first_ip_on_line(line, start=nil)
|
||||
i = 1
|
||||
total = @lines.size
|
||||
|
||||
while i < total
|
||||
cur_line = @lines.at(i)
|
||||
if cur_line >= line
|
||||
ip = @lines.at(i-1)
|
||||
|
||||
if !start or ip > start
|
||||
# matched the definition line, return 0
|
||||
return 0 if ip == -1
|
||||
return ip
|
||||
end
|
||||
end
|
||||
|
||||
i += 2
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
##
|
||||
# The first line where instructions are located.
|
||||
#
|
||||
# @return [Fixnum]
|
||||
def first_line
|
||||
line_from_ip(0)
|
||||
end
|
||||
|
||||
##
|
||||
# Indicate the line in the source code that this
|
||||
# was defined on.
|
||||
#
|
||||
def defined_line
|
||||
# Detect a -1 ip, which indicates a definition entry.
|
||||
return @lines[1] if @lines[0] == -1
|
||||
first_line
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Given all CompiledCodes in the system, find one that
|
||||
# was defined in file +file+ and encompasses +line+
|
||||
#
|
||||
def self.locate(file, line=nil)
|
||||
file = StringValue file
|
||||
|
||||
if line
|
||||
line = Integer line
|
||||
elsif m = /\A(.*):(\d+)\Z/.match(file)
|
||||
file = m[1]
|
||||
line = m[2].to_i
|
||||
end
|
||||
|
||||
ary = []
|
||||
ObjectSpace.find_object([:kind_of, Rubinius::CompiledCode], ary)
|
||||
|
||||
methods = ary.find_all do |x|
|
||||
x.scope and path = x.scope.absolute_active_path and \
|
||||
path.suffix?(file)
|
||||
end
|
||||
|
||||
return methods unless line
|
||||
|
||||
methods.find_all { |x| x.first_ip_on_line(line) }
|
||||
end
|
||||
|
||||
##
|
||||
# Is this actually a block of code?
|
||||
#
|
||||
# @return [Boolean]
|
||||
def is_block?
|
||||
get_metadata(:for_block)
|
||||
end
|
||||
|
||||
def for_eval?
|
||||
get_metadata(:for_eval)
|
||||
end
|
||||
|
||||
def for_module_body?
|
||||
get_metadata(:for_module_body)
|
||||
end
|
||||
|
||||
def describe
|
||||
str = "method #{@name}: #{@total_args} arg(s), #{@required_args} required"
|
||||
if @splat
|
||||
str << ", splatted."
|
||||
end
|
||||
str
|
||||
end
|
||||
|
||||
##
|
||||
# Convenience method to return an array of the child CompiledCodes from
|
||||
# this CompiledCode's literals.
|
||||
#
|
||||
# @return [Tuple]
|
||||
def child_methods
|
||||
literals.select { |lit| lit.kind_of? CompiledCode }
|
||||
end
|
||||
|
||||
def change_name(name)
|
||||
code = dup
|
||||
code.name = name
|
||||
|
||||
lits = Tuple.new(code.literals.size)
|
||||
code.literals.each_with_index do |lit, idx|
|
||||
if lit.kind_of? CompiledCode and lit.is_block?
|
||||
lit = lit.change_name name
|
||||
end
|
||||
|
||||
lits[idx] = lit
|
||||
end
|
||||
|
||||
code.literals = lits
|
||||
return code
|
||||
end
|
||||
|
||||
##
|
||||
# Locates the CompiledCode and instruction address (IP) of the first
|
||||
# instruction on the specified line. This method recursively examines child
|
||||
# compiled methods until an exact match for the searched line is found.
|
||||
# It returns both the matching CompiledCode and the IP of the first
|
||||
# instruction on the requested line, or nil if no match for the specified line
|
||||
# is found.
|
||||
#
|
||||
# @return [(Rubinius::CompiledCode, Fixnum), NilClass] returns
|
||||
# nil if nothing is found, else an array of size 2 containing the method
|
||||
# the line was found in and the IP pointing there.
|
||||
def locate_line(line)
|
||||
i = 1
|
||||
total = @lines.size
|
||||
while i < total
|
||||
cur_line = @lines.at(i)
|
||||
if cur_line == line
|
||||
ip = @lines.at(i-1)
|
||||
return nil if ip < 0
|
||||
return [self, ip]
|
||||
elsif cur_line > line
|
||||
break
|
||||
end
|
||||
|
||||
i += 2
|
||||
end
|
||||
|
||||
# Didn't find line in this CM, so check if a contained
|
||||
# CM encompasses the line searched for
|
||||
child_methods.each do |child|
|
||||
if res = child.locate_line(line)
|
||||
return res
|
||||
end
|
||||
end
|
||||
|
||||
# No child method is a match - fail
|
||||
return nil
|
||||
end
|
||||
|
||||
##
|
||||
# Decodes the instruction sequence that is represented by this compileed
|
||||
# method. Delegates to InstructionSequence to do the instruction decoding,
|
||||
# but then converts opcode literal arguments to their actual values by looking
|
||||
# them up in the literals tuple.
|
||||
# Takes an optional bytecodes argument representing the bytecode that is to
|
||||
# be decoded using this CompiledCode's locals and literals. This is provided
|
||||
# for use by the debugger, where the bytecode sequence to be decoded may not
|
||||
# exactly match the bytecode currently held by the CompiledCode, typically
|
||||
# as a result of substituting yield_debugger instructions into the bytecode.
|
||||
def decode(bytecodes = @iseq)
|
||||
decoder = Rubinius::InstructionDecoder.new(bytecodes)
|
||||
stream = decoder.decode(false)
|
||||
ip = 0
|
||||
|
||||
stream.map do |inst|
|
||||
instruct = Instruction.new(inst, self, ip)
|
||||
ip += instruct.size
|
||||
instruct
|
||||
end
|
||||
end
|
||||
|
||||
def add_metadata(key, val)
|
||||
raise TypeError, "key must be a symbol" unless key.kind_of? Symbol
|
||||
|
||||
case val
|
||||
when true, false, Symbol, Fixnum, String
|
||||
# ok
|
||||
else
|
||||
raise TypeError, "invalid type of value"
|
||||
end
|
||||
|
||||
@metadata ||= nil # to deal with MRI seeing @metadata as not set
|
||||
|
||||
unless @metadata
|
||||
@metadata = Tuple.new(2)
|
||||
@metadata[0] = key
|
||||
@metadata[1] = val
|
||||
return val
|
||||
end
|
||||
|
||||
i = 0
|
||||
fin = @metadata.size
|
||||
|
||||
while i < fin
|
||||
if @metadata[i] == key
|
||||
@metadata[i + 1] = val
|
||||
return val
|
||||
end
|
||||
|
||||
i += 2
|
||||
end
|
||||
|
||||
tup = Tuple.new(fin + 2)
|
||||
tup.copy_from @metadata, 0, fin, 0
|
||||
tup[fin] = key
|
||||
tup[fin + 1] = val
|
||||
|
||||
@metadata = tup
|
||||
|
||||
return val
|
||||
end
|
||||
|
||||
def get_metadata(key)
|
||||
return nil unless @metadata.kind_of? Tuple
|
||||
|
||||
i = 0
|
||||
while i < @metadata.size
|
||||
if @metadata[i] == key
|
||||
return @metadata[i + 1]
|
||||
end
|
||||
|
||||
i += 2
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
##
|
||||
# For Method#parameters
|
||||
def parameters
|
||||
params = []
|
||||
|
||||
return params unless respond_to?(:local_names)
|
||||
|
||||
m = required_args - post_args
|
||||
o = m + total_args - required_args
|
||||
p = o + post_args
|
||||
p += 1 if splat
|
||||
|
||||
local_names.each_with_index do |name, i|
|
||||
if i < m
|
||||
params << parameter(:req, name)
|
||||
elsif i < o
|
||||
params << [:opt, name]
|
||||
elsif splat == i
|
||||
params << parameter(:rest, name)
|
||||
elsif i < p
|
||||
params << parameter(:req, name)
|
||||
elsif block_index == i
|
||||
params << [:block, name]
|
||||
end
|
||||
end
|
||||
|
||||
params
|
||||
end
|
||||
|
||||
def parameter(kind, param)
|
||||
array = [kind]
|
||||
array << param unless param == :* or param[0, 2] == "_:"
|
||||
array
|
||||
end
|
||||
private :parameter
|
||||
|
||||
##
|
||||
# Represents virtual machine's CPU instruction.
|
||||
# Instructions are organized into instruction
|
||||
# sequences known as iSeq, forming body
|
||||
# of CompiledCodes.
|
||||
#
|
||||
# To generate VM opcodes documentation
|
||||
# use rake doc:vm task.
|
||||
class Instruction
|
||||
class Association
|
||||
def initialize(index)
|
||||
@index = index
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def inspect
|
||||
"literals[#{@index}]"
|
||||
end
|
||||
end
|
||||
|
||||
class Location
|
||||
FORMAT = "%04d:"
|
||||
|
||||
def initialize(location)
|
||||
@location = location
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def inspect
|
||||
FORMAT % @location
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(inst, code, ip)
|
||||
@instruction = inst[0]
|
||||
@args = inst[1..-1]
|
||||
@comment = nil
|
||||
|
||||
@args.each_index do |i|
|
||||
case @instruction.args[i]
|
||||
when :literal
|
||||
@args[i] = code.literals[@args[i]]
|
||||
when :local
|
||||
# TODO: Blocks should be able to retrieve local names as well,
|
||||
# but need access to method corresponding to home context
|
||||
if code.local_names and !code.is_block?
|
||||
@comment = code.local_names[args[i]].to_s
|
||||
end
|
||||
when :association
|
||||
@args[i] = Association.new(args[i])
|
||||
when :location
|
||||
@args[i] = Location.new(args[i])
|
||||
end
|
||||
end
|
||||
|
||||
@compiled_code = code
|
||||
@ip = ip
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
# Instruction pointer
|
||||
attr_reader :ip
|
||||
|
||||
##
|
||||
# Return the line that this instruction is on in the method
|
||||
#
|
||||
def line
|
||||
@compiled_code.line_from_ip(ip)
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the OpCode object
|
||||
#
|
||||
attr_reader :instruction
|
||||
|
||||
##
|
||||
# Returns the symbol representing the opcode for this instruction.
|
||||
#
|
||||
def opcode
|
||||
@instruction.opcode
|
||||
end
|
||||
|
||||
##
|
||||
# Returns an array of 0 to 2 arguments, depending on the opcode.
|
||||
#
|
||||
attr_reader :args
|
||||
|
||||
##
|
||||
# Returns a Fixnum indicating how wide the instruction takes up
|
||||
# in the instruction stream
|
||||
#
|
||||
def size
|
||||
@args.size + 1
|
||||
end
|
||||
|
||||
##
|
||||
# A nice human readable interpretation of this set of instructions
|
||||
def to_s
|
||||
str = "#{Location::FORMAT} %-27s" % [@ip, opcode]
|
||||
str << @args.map{ |a| a.inspect }.join(', ')
|
||||
if @comment
|
||||
str << " # #{@comment}"
|
||||
end
|
||||
|
||||
return str
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
340
gxg/core/complex.rb
Normal file
340
gxg/core/complex.rb
Normal file
|
|
@ -0,0 +1,340 @@
|
|||
#
|
||||
# complex.rb -
|
||||
# $Release Version: 0.5 $
|
||||
# $Revision: 1.3 $
|
||||
# $Date: 1998/07/08 10:05:28 $
|
||||
# by Keiju ISHITSUKA(SHL Japan Inc.)
|
||||
#
|
||||
|
||||
class Complex < Numeric
|
||||
|
||||
undef_method :%
|
||||
undef_method :<
|
||||
undef_method :<=
|
||||
undef_method :<=>
|
||||
undef_method :>
|
||||
undef_method :>=
|
||||
undef_method :between?
|
||||
undef_method :div
|
||||
undef_method :divmod
|
||||
undef_method :floor
|
||||
undef_method :ceil
|
||||
undef_method :modulo
|
||||
undef_method :remainder
|
||||
undef_method :round
|
||||
undef_method :step
|
||||
undef_method :truncate
|
||||
undef_method :i
|
||||
undef_method :positive?
|
||||
undef_method :negative?
|
||||
|
||||
def self.convert(real, imag = undefined)
|
||||
if real.equal?(nil) || imag.equal?(nil)
|
||||
raise TypeError, "cannot convert nil into Complex"
|
||||
end
|
||||
|
||||
real = real.to_c if real.kind_of?(String)
|
||||
imag = imag.to_c if imag.kind_of?(String)
|
||||
|
||||
if real.kind_of?(Complex) && !real.imag.kind_of?(Float) && real.imag == 0
|
||||
real = real.real
|
||||
end
|
||||
|
||||
if imag.kind_of?(Complex) && !imag.imag.kind_of?(Float) && imag.imag == 0
|
||||
imag = imag.real
|
||||
end
|
||||
|
||||
if real.kind_of?(Complex) && !imag.kind_of?(Float) && imag == 0
|
||||
return real
|
||||
end
|
||||
|
||||
if undefined.equal?(imag)
|
||||
if real.kind_of?(Numeric) && !real.real?
|
||||
return real
|
||||
elsif !real.kind_of?(Numeric)
|
||||
return Rubinius::Type.coerce_to(real, Complex, :to_c)
|
||||
else
|
||||
imag = 0
|
||||
end
|
||||
elsif real.kind_of?(Numeric) && imag.kind_of?(Numeric) && (!real.real? || !imag.real?)
|
||||
return real + imag * Complex.new(0, 1)
|
||||
end
|
||||
|
||||
return real if Rubinius.mathn_loaded? && imag.equal?(0)
|
||||
rect(real, imag)
|
||||
end
|
||||
|
||||
def Complex.generic?(other) # :nodoc:
|
||||
other.kind_of?(Integer) or
|
||||
other.kind_of?(Float) or
|
||||
(defined?(Rational) and other.kind_of?(Rational))
|
||||
end
|
||||
|
||||
def Complex.rect(real, imag=0)
|
||||
raise TypeError, 'not a real' unless check_real?(real) && check_real?(imag)
|
||||
new(real, imag)
|
||||
end
|
||||
|
||||
def Complex.polar(r, theta=0)
|
||||
raise TypeError, 'not a real' unless check_real?(r) && check_real?(theta)
|
||||
|
||||
Complex(r*Math.cos(theta), r*Math.sin(theta))
|
||||
end
|
||||
|
||||
def Complex.check_real?(obj)
|
||||
obj.kind_of?(Numeric) && obj.real?
|
||||
end
|
||||
|
||||
def initialize(a, b = 0)
|
||||
@real = a
|
||||
@imag = b
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def -@
|
||||
Complex(-real, -imag)
|
||||
end
|
||||
|
||||
def +(other)
|
||||
if other.kind_of?(Complex)
|
||||
Complex(real + other.real, imag + other.imag)
|
||||
elsif other.kind_of?(Numeric) && other.real?
|
||||
Complex(real + other, imag)
|
||||
else
|
||||
redo_coerced(:+, other)
|
||||
end
|
||||
end
|
||||
|
||||
def -(other)
|
||||
if other.kind_of?(Complex)
|
||||
Complex(real - other.real, imag - other.imag)
|
||||
elsif other.kind_of?(Numeric) && other.real?
|
||||
Complex(real - other, imag)
|
||||
else
|
||||
redo_coerced(:-, other)
|
||||
end
|
||||
end
|
||||
|
||||
def *(other)
|
||||
if other.kind_of?(Complex)
|
||||
Complex(real * other.real - imag * other.imag,
|
||||
real * other.imag + imag * other.real)
|
||||
elsif other.kind_of?(Numeric) && other.real?
|
||||
Complex(real * other, imag * other)
|
||||
else
|
||||
redo_coerced(:*, other)
|
||||
end
|
||||
end
|
||||
|
||||
def divide(other)
|
||||
if other.kind_of?(Complex)
|
||||
self * other.conjugate / other.abs2
|
||||
elsif other.kind_of?(Numeric) && other.real?
|
||||
Complex(real.quo(other), imag.quo(other))
|
||||
else
|
||||
redo_coerced(:quo, other)
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :/, :divide
|
||||
alias_method :quo, :divide
|
||||
|
||||
def ** (other)
|
||||
if !other.kind_of?(Float) && other == 0
|
||||
return Complex(1)
|
||||
end
|
||||
if other.kind_of?(Complex)
|
||||
r, theta = polar
|
||||
ore = other.real
|
||||
oim = other.imag
|
||||
nr = Math.exp(ore*Math.log(r) - oim * theta)
|
||||
ntheta = theta*ore + oim*Math.log(r)
|
||||
Complex.polar(nr, ntheta)
|
||||
elsif other.kind_of?(Integer)
|
||||
if other > 0
|
||||
x = self
|
||||
z = x
|
||||
n = other - 1
|
||||
while n != 0
|
||||
while (div, mod = n.divmod(2)
|
||||
mod == 0)
|
||||
x = Complex(x.real*x.real - x.imag*x.imag, 2*x.real*x.imag)
|
||||
n = div
|
||||
end
|
||||
z *= x
|
||||
n -= 1
|
||||
end
|
||||
z
|
||||
else
|
||||
if defined? Rational
|
||||
(Rational.new(1, 1) / self) ** -other
|
||||
else
|
||||
self ** Float(other)
|
||||
end
|
||||
end
|
||||
elsif Complex.generic?(other)
|
||||
r, theta = polar
|
||||
Complex.polar(r**other, theta*other)
|
||||
else
|
||||
x, y = other.coerce(self)
|
||||
x**y
|
||||
end
|
||||
end
|
||||
|
||||
def abs
|
||||
Math.hypot(@real, @imag)
|
||||
end
|
||||
|
||||
alias_method :magnitude, :abs
|
||||
|
||||
def abs2
|
||||
@real*@real + @imag*@imag
|
||||
end
|
||||
|
||||
def arg
|
||||
Math.atan2(@imag, @real)
|
||||
end
|
||||
alias_method :angle, :arg
|
||||
alias_method :phase, :arg
|
||||
|
||||
def polar
|
||||
[abs, arg]
|
||||
end
|
||||
|
||||
def conjugate
|
||||
Complex(@real, -@imag)
|
||||
end
|
||||
alias_method :conj, :conjugate
|
||||
|
||||
def ==(other)
|
||||
if other.kind_of?(Complex)
|
||||
real == other.real && imag == other.imag
|
||||
elsif other.kind_of?(Numeric) && other.real?
|
||||
real == other && imag == 0
|
||||
else
|
||||
other == self
|
||||
end
|
||||
end
|
||||
|
||||
def eql?(other)
|
||||
other.kind_of?(Complex) and
|
||||
imag.class == other.imag.class and
|
||||
real.class == other.real.class and
|
||||
self == other
|
||||
end
|
||||
|
||||
def coerce(other)
|
||||
if other.kind_of?(Numeric) && other.real?
|
||||
[Complex.new(other, 0), self]
|
||||
elsif other.kind_of?(Complex)
|
||||
[other, self]
|
||||
else
|
||||
raise TypeError, "#{other.class} can't be coerced into Complex"
|
||||
end
|
||||
end
|
||||
|
||||
def denominator
|
||||
@real.denominator.lcm(@imag.denominator)
|
||||
end
|
||||
|
||||
def numerator
|
||||
cd = denominator
|
||||
Complex(@real.numerator*(cd/@real.denominator),
|
||||
@imag.numerator*(cd/@imag.denominator))
|
||||
end
|
||||
|
||||
def real?
|
||||
false
|
||||
end
|
||||
|
||||
def rect
|
||||
[@real, @imag]
|
||||
end
|
||||
|
||||
alias_method :rectangular, :rect
|
||||
|
||||
def to_f
|
||||
raise RangeError, "can't convert #{self} into Float" unless !imag.kind_of?(Float) && imag == 0
|
||||
real.to_f
|
||||
end
|
||||
|
||||
def to_i
|
||||
raise RangeError, "can't convert #{self} into Integer" unless !imag.kind_of?(Float) && imag == 0
|
||||
real.to_i
|
||||
end
|
||||
|
||||
def to_r
|
||||
raise RangeError, "can't' convert #{self} into Rational" unless !imag.kind_of?(Float) && imag == 0
|
||||
real.to_r
|
||||
end
|
||||
|
||||
def rationalize(eps = nil)
|
||||
raise RangeError, "can't' convert #{self} into Rational" unless !imag.kind_of?(Float) && imag == 0
|
||||
real.rationalize(eps)
|
||||
end
|
||||
|
||||
def to_s
|
||||
result = real.to_s
|
||||
|
||||
if imag.kind_of?(Float) ? !imag.nan? && imag.signbit? : imag < 0
|
||||
result << "-"
|
||||
else
|
||||
result << "+"
|
||||
end
|
||||
|
||||
imag_s = imag.abs.to_s
|
||||
result << imag_s
|
||||
|
||||
unless imag_s[-1] =~ /\d/
|
||||
result << "*"
|
||||
end
|
||||
|
||||
result << "i"
|
||||
result
|
||||
end
|
||||
|
||||
def hash
|
||||
@real.hash ^ @imag.hash
|
||||
end
|
||||
|
||||
def inspect
|
||||
"(#{to_s})"
|
||||
end
|
||||
|
||||
def fdiv(other)
|
||||
raise TypeError, "#{other.class} can't be coerced into Complex" unless other.is_a?(Numeric)
|
||||
|
||||
# FIXME
|
||||
self / other
|
||||
end
|
||||
|
||||
def marshal_dump
|
||||
ary = [real, imag]
|
||||
instance_variables.each do |ivar|
|
||||
ary.instance_variable_set(ivar, instance_variable_get(ivar))
|
||||
end
|
||||
ary
|
||||
end
|
||||
|
||||
private :marshal_dump
|
||||
|
||||
def marshal_load(ary)
|
||||
@real, @imag = ary
|
||||
ary.instance_variables.each do |ivar|
|
||||
instance_variable_set(ivar, ary.instance_variable_get(ivar))
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
class << self
|
||||
private :convert
|
||||
private :check_real?
|
||||
|
||||
alias_method :rectangular, :rect
|
||||
end
|
||||
|
||||
attr_reader :real
|
||||
attr_reader :imag
|
||||
alias_method :imaginary, :imag
|
||||
end
|
||||
59
gxg/core/complexifier.rb
Normal file
59
gxg/core/complexifier.rb
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
class String
|
||||
class Complexifier
|
||||
def initialize(value)
|
||||
@value = value
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def convert
|
||||
if m = PATTERN0.match(@value)
|
||||
sr = m[1]
|
||||
si = m[2]
|
||||
re = m.post_match
|
||||
po = true
|
||||
elsif m = PATTERN1.match(@value)
|
||||
sr = nil
|
||||
si = (m[1] || "") + (m[2] || "1")
|
||||
re = m.post_match
|
||||
po = false
|
||||
elsif m = PATTERN2.match(@value)
|
||||
sr = m[1]
|
||||
si = m[2] ? m[3] + (m[4] || "1") : nil
|
||||
re = m.post_match
|
||||
po = false
|
||||
else
|
||||
return Complex.new(0, 0)
|
||||
end
|
||||
|
||||
r = 0
|
||||
i = 0
|
||||
|
||||
if sr
|
||||
if sr.include?("/")
|
||||
r = sr.to_r
|
||||
elsif sr.match(/[.eE]/)
|
||||
r = sr.to_f
|
||||
else
|
||||
r = sr.to_i
|
||||
end
|
||||
end
|
||||
|
||||
if si
|
||||
if si.include?("/")
|
||||
i = si.to_r
|
||||
elsif si.match(/[.eE]/)
|
||||
i = si.to_f
|
||||
else
|
||||
i = si.to_i
|
||||
end
|
||||
end
|
||||
|
||||
if po
|
||||
Complex.polar(r, i)
|
||||
else
|
||||
Complex.rect(r, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
34
gxg/core/configuration.rb
Normal file
34
gxg/core/configuration.rb
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
module Rubinius
|
||||
class Configuration
|
||||
def initialize
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def get_variable(name)
|
||||
Rubinius.primitive :vm_get_config_item
|
||||
raise PrimitiveFailure, "Rubinius::Configuration#get_variable primitive failed"
|
||||
end
|
||||
|
||||
def get_section(section)
|
||||
Rubinius.primitive :vm_get_config_section
|
||||
raise PrimitiveFailure, "Rubinius::Configuration#get_section primitive failed"
|
||||
end
|
||||
|
||||
def section(section)
|
||||
ary = get_section(section)
|
||||
i = 0
|
||||
while i < ary.size
|
||||
tup = ary[i]
|
||||
yield tup[0], tup[1]
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
|
||||
def [](name)
|
||||
get_variable(name)
|
||||
end
|
||||
|
||||
alias_method :get, :[]
|
||||
end
|
||||
end
|
||||
31
gxg/core/console.rb
Normal file
31
gxg/core/console.rb
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
module Rubinius
|
||||
module Console
|
||||
class Server
|
||||
attr_reader :inbox
|
||||
attr_reader :outbox
|
||||
attr_reader :thread
|
||||
|
||||
def initialize
|
||||
@inbox = Channel.new
|
||||
@outbox = Channel.new
|
||||
|
||||
@thread = Thread.new do
|
||||
source = @inbox.receive
|
||||
break unless source.size
|
||||
|
||||
@outbox.send evaluate(source)
|
||||
end
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def evaluate(source)
|
||||
begin
|
||||
eval(source, TOPLEVEL_BINDING, "(console)", 1).inspect
|
||||
rescue Object => e
|
||||
e.message + e.backtrace.join("\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
28
gxg/core/constant_cache.rb
Normal file
28
gxg/core/constant_cache.rb
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
module Rubinius
|
||||
class ConstantCache
|
||||
attr_reader :name
|
||||
attr_reader :value
|
||||
attr_reader :module
|
||||
attr_reader :lexical_scope
|
||||
attr_reader :executable
|
||||
|
||||
def ip
|
||||
Rubinius.primitive :constant_cache_ip
|
||||
raise PrimitiveFailure, "CallSite#ip primitive failed"
|
||||
end
|
||||
|
||||
def serial
|
||||
Rubinius.primitive :constant_cache_serial
|
||||
raise PrimitiveFailure, "CallSite#serial primitive failed"
|
||||
end
|
||||
|
||||
def location
|
||||
"#{@executable.file}:#{@executable.line_from_ip(ip)}"
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<#{self.class.name}:0x#{self.object_id.to_s(16)} #{location}##{@name} constant=#{@value} module=#{@module} lexical_scope=#{@lexical_scope}>"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
97
gxg/core/constant_table.rb
Normal file
97
gxg/core/constant_table.rb
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
module Rubinius
|
||||
class ConstantTable
|
||||
class Bucket
|
||||
attr_reader :name
|
||||
attr_accessor :constant
|
||||
attr_accessor :visibility
|
||||
attr_reader :next
|
||||
|
||||
def private?
|
||||
@visibility == :private
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<#{self.class}:0x#{object_id.to_s(16)} name=#{@name.inspect} constant=#{@constant.inspect} visibility=#{visibility.inspect}>"
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :bins
|
||||
|
||||
def size
|
||||
@entries
|
||||
end
|
||||
|
||||
alias_method :length, :size
|
||||
|
||||
def self.allocate
|
||||
Rubinius.primitive :constant_table_allocate
|
||||
raise PrimitiveFailure, "ConstantTable.allocate primitive failed"
|
||||
end
|
||||
|
||||
def initialize(hash=nil)
|
||||
return unless hash
|
||||
hash.each do |k, v|
|
||||
self[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def duplicate
|
||||
Rubinius.primitive :constant_table_duplicate
|
||||
raise PrimitiveFailure, "ConstantTable#duplicate primitive failed"
|
||||
end
|
||||
|
||||
def dup
|
||||
copy = duplicate
|
||||
Rubinius.privately do
|
||||
copy.initialize_copy self
|
||||
end
|
||||
copy
|
||||
end
|
||||
|
||||
def name?(name)
|
||||
Rubinius.primitive :constant_table_has_name
|
||||
raise PrimitiveFailure, "ConstantTable#name? primitive failed"
|
||||
end
|
||||
|
||||
alias_method :has_name?, :name?
|
||||
alias_method :include?, :name?
|
||||
alias_method :member?, :name?
|
||||
|
||||
def keys
|
||||
Rubinius.primitive :constant_table_keys
|
||||
raise PrimitiveFailure, "ConstantTable#keys primitive failed"
|
||||
end
|
||||
|
||||
def delete(name)
|
||||
Rubinius.primitive :constant_table_delete
|
||||
raise PrimitiveFailure, "ConstantTable#delete primitive failed"
|
||||
end
|
||||
|
||||
def each
|
||||
raise LocalJumpError, "no block given" unless block_given?
|
||||
|
||||
vals = values
|
||||
|
||||
i = 0
|
||||
max = @bins
|
||||
|
||||
while i < max
|
||||
if entry = vals.at(i)
|
||||
while entry
|
||||
yield entry.name, entry.constant, entry.visibility
|
||||
entry = entry.next
|
||||
end
|
||||
end
|
||||
i += 1
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
def values
|
||||
Rubinius.primitive :constant_table_values
|
||||
raise PrimitiveFailure, "ConstantTable#values primitive failed"
|
||||
end
|
||||
end
|
||||
end
|
||||
43
gxg/core/continuation.rb
Normal file
43
gxg/core/continuation.rb
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
class Continuation
|
||||
# def self.create(task=nil)
|
||||
# cont = self.allocate
|
||||
# cont.setup(task)
|
||||
# cont
|
||||
# end
|
||||
#
|
||||
# def setup(task=nil)
|
||||
# @task = task
|
||||
# @value = nil
|
||||
# end
|
||||
#
|
||||
# def call(*value)
|
||||
# task = @task.dup
|
||||
# if value.empty?
|
||||
# @value = nil
|
||||
# elsif value.size == 1
|
||||
# @value = value.pop
|
||||
# else
|
||||
# @value = value
|
||||
# end
|
||||
# Rubinius::Task.current = task
|
||||
# end
|
||||
#
|
||||
# alias_method :[], :call
|
||||
end
|
||||
|
||||
module Kernel
|
||||
def callcc
|
||||
raise NotImplementedError, "call/cc is not implemented!"
|
||||
# cont = Continuation.create
|
||||
# # Task#dup appears as though it returns nil in the dup'd
|
||||
# # task, kinda like fork().
|
||||
# task = Rubinius::Task.current.dup
|
||||
# if task
|
||||
# cont.task = task
|
||||
# yield cont
|
||||
# else
|
||||
# return cont.value
|
||||
# end
|
||||
end
|
||||
module_function :callcc
|
||||
end
|
||||
90
gxg/core/ctype.rb
Normal file
90
gxg/core/ctype.rb
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
##
|
||||
# Mixin containing byte classification methods.
|
||||
#--
|
||||
# isspace, islower, ... are not in MRI core library.
|
||||
|
||||
module Rubinius::CType
|
||||
# The character literals (?x) are Fixnums in 1.8 and Strings in 1.9
|
||||
# so we use literal values instead so this is 1.8/1.9 compatible.
|
||||
|
||||
# \n \r \t \f \v \a \b \e
|
||||
def self.isctrl(num)
|
||||
(num >= 7 and num <= 13) or num == 27
|
||||
end
|
||||
|
||||
# ' ' \n \t \r \f \v
|
||||
def self.isspace(num)
|
||||
num == 32 or (num >= 9 and num <= 13)
|
||||
end
|
||||
|
||||
def self.isupper(num)
|
||||
num >= 65 and num <= 90
|
||||
end
|
||||
|
||||
def self.islower(num)
|
||||
num >= 97 and num <= 122
|
||||
end
|
||||
|
||||
def self.isdigit(num)
|
||||
num >= 48 and num <= 57
|
||||
end
|
||||
|
||||
def self.isalnum(num)
|
||||
islower(num) or isupper(num) or isdigit(num)
|
||||
end
|
||||
|
||||
def self.toupper!(num)
|
||||
num - 32
|
||||
end
|
||||
|
||||
def self.toupper(num)
|
||||
islower(num) ? toupper!(num) : num
|
||||
end
|
||||
|
||||
def self.tolower!(num)
|
||||
num + 32
|
||||
end
|
||||
|
||||
def self.tolower(num)
|
||||
isupper(num) ? tolower!(num) : num
|
||||
end
|
||||
|
||||
def self.toprint(num)
|
||||
# The character literals (?x) are Fixnums in 1.8 and Strings in 1.9
|
||||
# so we use literal values instead so this is 1.8/1.9 compatible.
|
||||
case num
|
||||
when 7; '\a'
|
||||
when 8; '\b'
|
||||
when 9; '\t'
|
||||
when 10; '\n'
|
||||
when 11; '\v'
|
||||
when 12; '\f'
|
||||
when 13; '\r'
|
||||
when 27; '\e'
|
||||
when 34; '\"'
|
||||
when 35; Rubinius::Tuple['#$', '\#$', '#@', '\#@', '#{', '\#{', '#', '#']
|
||||
when 92; '\\\\'
|
||||
else
|
||||
if num < 32 || num > 126
|
||||
unprintable_chr(num)
|
||||
else
|
||||
num.chr
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.unprintable_chr(num)
|
||||
str = "\\x00"
|
||||
str.modify!
|
||||
m = Rubinius::Mirror.reflect str
|
||||
|
||||
c = num.to_s(16).upcase
|
||||
m.copy_from c, 0, c.size, 4-c.size
|
||||
|
||||
str
|
||||
end
|
||||
|
||||
def toprint
|
||||
Printed[self]
|
||||
end
|
||||
end
|
||||
41
gxg/core/delegated_method.rb
Normal file
41
gxg/core/delegated_method.rb
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
##
|
||||
# DelegatedMethod is used to implement Module#define_method
|
||||
|
||||
module Rubinius
|
||||
class DelegatedMethod < Executable
|
||||
def initialize(name, method, receiver, pass_self=false)
|
||||
@name = name
|
||||
@method = method
|
||||
@receiver = receiver
|
||||
@pass_self = pass_self
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
attr_reader :name
|
||||
attr_reader :receiver
|
||||
|
||||
def call(called_object, called_method, *args, &block)
|
||||
args.unshift called_object if @pass_self
|
||||
@receiver.__send__(@method, *args, &block)
|
||||
end
|
||||
|
||||
alias_method :activate, :call
|
||||
|
||||
def arity
|
||||
@receiver.arity
|
||||
end
|
||||
|
||||
def parameters
|
||||
@receiver.parameters
|
||||
end
|
||||
|
||||
def source_location
|
||||
@receiver.source_location
|
||||
end
|
||||
|
||||
def scope
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
29
gxg/core/deprecations.rb
Normal file
29
gxg/core/deprecations.rb
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
module Rubinius
|
||||
DEPRECATIONS = {
|
||||
# "Description" => "Alternative"
|
||||
"Rubinius::Executable#primitive is deprecated." =>
|
||||
"Use Rubinius::Executable#tags instead.",
|
||||
"Non-UTF-8 encodings for String objects are deprecated." =>
|
||||
"Use String objects encoded as UTF-8.",
|
||||
"Encoding objects other than UTF-8 are deprecated." =>
|
||||
"Use ByteString (when available) to passed text data with non-UTF-8 encodings through a program. Use transcoding facilities at IO boundaries to read from or write to non-UTF-8 encodings. Use File.transcode to transcode a file from one encoding to another.",
|
||||
"Mutating a String object is deprecated." =>
|
||||
"Use ByteString for binary data or MutableString for text (when available).",
|
||||
"Insertion ordering of Hash is deprecated." =>
|
||||
"Use OrderedHash when insertion ordering is desired.",
|
||||
"OpenSSL is deprecated." =>
|
||||
"Use the core cryptography and TLS libraries when available.",
|
||||
"The Ruby stdlib is deprecated." =>
|
||||
"Use core libraries (eg Socket) or alternative gems when available.",
|
||||
"Rubygems and Bundler are deprecated." =>
|
||||
"Use the IPFS library facility when available.",
|
||||
"Use of the identifier 'type' at script scope is deprecated." =>
|
||||
"Use an identifier not named 'type' at script scope.",
|
||||
"Use of the identifier 'import' at script, class/module, method, block, or function scope is deprecated." =>
|
||||
"Use an identifier not named 'import' at script, class/module, method, block, or function scope.",
|
||||
"Defining a new method without removing an existing method definition for that name is deprecated." =>
|
||||
"Defining a new method with the same name as an existing method will define multiple methods. Use `remove_method` followed by `def` to replace a method.",
|
||||
"Fixnum and Bignum classes are deprecated" =>
|
||||
"Use Integer instead of Fixnum or Bignum",
|
||||
}
|
||||
end
|
||||
269
gxg/core/dir.rb
Normal file
269
gxg/core/dir.rb
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
class Dir
|
||||
include Enumerable
|
||||
|
||||
def self.allocate
|
||||
Rubinius.primitive :dir_allocate
|
||||
raise PrimitiveFailure, "Dir.allocate primitive failed"
|
||||
end
|
||||
|
||||
def initialize(path, options=undefined)
|
||||
path = Rubinius::Type.coerce_to_path path
|
||||
|
||||
if options.equal? undefined
|
||||
enc = nil
|
||||
else
|
||||
options = Rubinius::Type.coerce_to options, Hash, :to_hash
|
||||
enc = options[:encoding]
|
||||
enc = Rubinius::Type.coerce_to_encoding enc if enc
|
||||
end
|
||||
|
||||
Rubinius.invoke_primitive :dir_open, self, path, enc
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def close
|
||||
Rubinius.primitive :dir_close
|
||||
raise PrimitiveFailure, "Dir#close primitive failed"
|
||||
end
|
||||
|
||||
def closed?
|
||||
Rubinius.primitive :dir_closed_p
|
||||
raise PrimitiveFailure, "Dir#closed? primitive failed"
|
||||
end
|
||||
|
||||
def read
|
||||
entry = Rubinius.invoke_primitive :dir_read, self
|
||||
return unless entry
|
||||
|
||||
if Encoding.default_external == Encoding::US_ASCII && !entry.valid_encoding?
|
||||
entry.force_encoding Encoding::ASCII_8BIT
|
||||
return entry
|
||||
end
|
||||
|
||||
enc = Encoding.default_internal
|
||||
enc ? entry.encode(enc) : entry
|
||||
end
|
||||
|
||||
def control(kind, pos)
|
||||
Rubinius.primitive :dir_control
|
||||
raise PrimitiveFailure, "Dir#__control__ primitive failed"
|
||||
end
|
||||
|
||||
private :control
|
||||
|
||||
def self.open(path, options=undefined)
|
||||
dir = new path, options
|
||||
if block_given?
|
||||
begin
|
||||
value = yield dir
|
||||
ensure
|
||||
dir.close
|
||||
end
|
||||
|
||||
return value
|
||||
else
|
||||
return dir
|
||||
end
|
||||
end
|
||||
|
||||
def self.entries(path, options=undefined)
|
||||
ret = []
|
||||
|
||||
open(path, options) do |dir|
|
||||
while s = dir.read
|
||||
ret << s
|
||||
end
|
||||
end
|
||||
|
||||
ret
|
||||
end
|
||||
|
||||
def self.exist?(path)
|
||||
PrivateFile.directory?(path)
|
||||
end
|
||||
|
||||
class << self
|
||||
alias_method :exists?, :exist?
|
||||
end
|
||||
|
||||
def self.home(user=nil)
|
||||
PrivateFile.expand_path("~#{user}")
|
||||
end
|
||||
|
||||
def self.[](*patterns)
|
||||
if patterns.size == 1
|
||||
pattern = Rubinius::Type.coerce_to_path(patterns[0])
|
||||
return [] if pattern.empty?
|
||||
patterns = glob_split pattern
|
||||
end
|
||||
|
||||
glob patterns
|
||||
end
|
||||
|
||||
def self.glob(pattern, flags=0, &block)
|
||||
if pattern.kind_of? Array
|
||||
patterns = pattern
|
||||
else
|
||||
pattern = Rubinius::Type.coerce_to_path pattern
|
||||
|
||||
return [] if pattern.empty?
|
||||
|
||||
patterns = glob_split pattern
|
||||
end
|
||||
|
||||
matches = []
|
||||
index = 0
|
||||
|
||||
patterns.each do |pat|
|
||||
pat = Rubinius::Type.coerce_to_path pat
|
||||
enc = Rubinius::Type.ascii_compatible_encoding pat
|
||||
Dir::Glob.glob pat, flags, matches
|
||||
|
||||
total = matches.size
|
||||
while index < total
|
||||
Rubinius::Type.encode_string matches[index], enc
|
||||
index += 1
|
||||
end
|
||||
end
|
||||
|
||||
if block
|
||||
matches.each(&block)
|
||||
return nil
|
||||
end
|
||||
|
||||
return matches
|
||||
end
|
||||
|
||||
def self.glob_split(pattern)
|
||||
result = []
|
||||
start = 0
|
||||
m = Rubinius::Mirror.reflect pattern
|
||||
while idx = m.find_string("\0", start)
|
||||
result << pattern.byteslice(start, idx)
|
||||
start = idx + 1
|
||||
end
|
||||
result << pattern.byteslice(start, pattern.bytesize)
|
||||
end
|
||||
|
||||
def self.foreach(path)
|
||||
return to_enum(:foreach, path) unless block_given?
|
||||
|
||||
open(path) do |dir|
|
||||
while s = dir.read
|
||||
yield s
|
||||
end
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def self.join_path(p1, p2, dirsep)
|
||||
"#{p1}#{dirsep ? '/' : ''}#{p2}"
|
||||
end
|
||||
|
||||
def self.chdir(path = ENV['HOME'])
|
||||
path = Rubinius::Type.coerce_to_path path
|
||||
|
||||
if block_given?
|
||||
original_path = self.getwd
|
||||
error = FFI::Platform::POSIX.chdir path
|
||||
Errno.handle(path) if error != 0
|
||||
|
||||
begin
|
||||
value = yield path
|
||||
ensure
|
||||
error = FFI::Platform::POSIX.chdir original_path
|
||||
Errno.handle(original_path) if error != 0
|
||||
end
|
||||
|
||||
return value
|
||||
else
|
||||
error = FFI::Platform::POSIX.chdir path
|
||||
Errno.handle path if error != 0
|
||||
error
|
||||
end
|
||||
end
|
||||
|
||||
def self.mkdir(path, mode = 0777)
|
||||
error = FFI::Platform::POSIX.mkdir(Rubinius::Type.coerce_to_path(path), mode)
|
||||
Errno.handle path if error != 0
|
||||
error
|
||||
end
|
||||
|
||||
def self.rmdir(path)
|
||||
error = FFI::Platform::POSIX.rmdir(Rubinius::Type.coerce_to_path(path))
|
||||
Errno.handle path if error != 0
|
||||
error
|
||||
end
|
||||
|
||||
def self.getwd
|
||||
buf = String.pattern Rubinius::PATH_MAX, 0
|
||||
wd = FFI::Platform::POSIX.getcwd(buf, buf.length)
|
||||
Errno.handle unless wd
|
||||
Rubinius::Type.external_string wd
|
||||
end
|
||||
|
||||
def self.chroot(path)
|
||||
ret = FFI::Platform::POSIX.chroot Rubinius::Type.coerce_to_path(path)
|
||||
Errno.handle path if ret != 0
|
||||
ret
|
||||
end
|
||||
|
||||
def each
|
||||
return to_enum unless block_given?
|
||||
|
||||
while s = read
|
||||
yield s
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
def fileno
|
||||
Rubinius.primitive :dir_fileno
|
||||
raise PrimitiveFailure, "Dir#fileno primitive failed"
|
||||
end
|
||||
|
||||
attr_reader :path
|
||||
|
||||
alias_method :to_path, :path
|
||||
|
||||
SeekKind = 0
|
||||
RewindKind = 1
|
||||
TellKind = 2
|
||||
|
||||
def pos
|
||||
control TellKind, 0
|
||||
end
|
||||
|
||||
alias_method :tell, :pos
|
||||
|
||||
def pos=(position)
|
||||
seek(position)
|
||||
|
||||
position
|
||||
end
|
||||
|
||||
def seek(position)
|
||||
control SeekKind, position
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
def rewind
|
||||
control RewindKind, 0
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<#{self.class}:#{object_id.to_s(16)} @path=#{@path}>"
|
||||
end
|
||||
|
||||
class << self
|
||||
alias_method :pwd, :getwd
|
||||
alias_method :delete, :rmdir
|
||||
alias_method :unlink, :rmdir
|
||||
end
|
||||
end
|
||||
460
gxg/core/dir_glob.rb
Normal file
460
gxg/core/dir_glob.rb
Normal file
|
|
@ -0,0 +1,460 @@
|
|||
class Dir
|
||||
module Glob
|
||||
class Node
|
||||
def initialize(nxt, flags)
|
||||
@flags = flags
|
||||
@next = nxt
|
||||
@separator = nil
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
attr_writer :separator
|
||||
|
||||
def separator
|
||||
@separator || "/"
|
||||
end
|
||||
|
||||
def path_join(parent, ent)
|
||||
return ent unless parent
|
||||
if parent == "/"
|
||||
"/#{ent}"
|
||||
else
|
||||
"#{parent}#{separator}#{ent}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class ConstantDirectory < Node
|
||||
def initialize(nxt, flags, dir)
|
||||
super nxt, flags
|
||||
@dir = dir
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def call(env, path)
|
||||
full = path_join(path, @dir)
|
||||
|
||||
# Don't check if full exists. It just costs us time
|
||||
# and the downstream node will be able to check properly.
|
||||
@next.call env, full
|
||||
end
|
||||
end
|
||||
|
||||
class ConstantEntry < Node
|
||||
def initialize(nxt, flags, name)
|
||||
super nxt, flags
|
||||
@name = name
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def call(env, parent)
|
||||
path = path_join(parent, @name)
|
||||
|
||||
if File.exist? path
|
||||
env.matches << path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class RootDirectory < Node
|
||||
def call(env, path)
|
||||
@next.call env, "/"
|
||||
end
|
||||
end
|
||||
|
||||
class RecursiveDirectories < Node
|
||||
def call(env, start)
|
||||
return if !start || !File.exist?(start)
|
||||
|
||||
# Even though the recursive entry is zero width
|
||||
# in this case, its left separator is still the
|
||||
# dominant one, so we fix things up to use it.
|
||||
switched = @next.dup
|
||||
switched.separator = @separator
|
||||
switched.call env, start
|
||||
|
||||
stack = [start]
|
||||
|
||||
allow_dots = ((@flags & File::FNM_DOTMATCH) != 0)
|
||||
|
||||
until stack.empty?
|
||||
path = stack.pop
|
||||
begin
|
||||
dir = Dir.new(path)
|
||||
rescue Errno::ENOTDIR
|
||||
next
|
||||
end
|
||||
|
||||
while ent = dir.read
|
||||
next if ent == "." || ent == ".."
|
||||
full = path_join(path, ent)
|
||||
stat = File::Stat.lstat full
|
||||
|
||||
if stat.directory? and (allow_dots or ent.getbyte(0) != 46) # ?.
|
||||
stack << full
|
||||
@next.call env, full
|
||||
end
|
||||
end
|
||||
dir.close
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class StartRecursiveDirectories < Node
|
||||
def call(env, start)
|
||||
raise "invalid usage" if start
|
||||
|
||||
# Even though the recursive entry is zero width
|
||||
# in this case, its left separator is still the
|
||||
# dominant one, so we fix things up to use it.
|
||||
if @separator
|
||||
switched = @next.dup
|
||||
switched.separator = @separator
|
||||
switched.call env, start
|
||||
else
|
||||
@next.call env, start
|
||||
end
|
||||
|
||||
stack = []
|
||||
|
||||
allow_dots = ((@flags & File::FNM_DOTMATCH) != 0)
|
||||
|
||||
dir = Dir.new(".")
|
||||
while ent = dir.read
|
||||
next if ent == "." || ent == ".."
|
||||
stat = File::Stat.lstat ent
|
||||
|
||||
if stat.directory? and (allow_dots or ent.getbyte(0) != 46) # ?.
|
||||
stack << ent
|
||||
@next.call env, ent
|
||||
end
|
||||
end
|
||||
dir.close
|
||||
|
||||
until stack.empty?
|
||||
path = stack.pop
|
||||
dir = Dir.new(path)
|
||||
while ent = dir.read
|
||||
next if ent == "." || ent == ".."
|
||||
full = path_join(path, ent)
|
||||
stat = File::Stat.lstat full
|
||||
|
||||
if stat.directory? and ent.getbyte(0) != 46 # ?.
|
||||
stack << full
|
||||
@next.call env, full
|
||||
end
|
||||
end
|
||||
dir.close
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Match < Node
|
||||
def initialize(nxt, flags, glob)
|
||||
super nxt, flags
|
||||
@glob = glob || ""
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def match?(str)
|
||||
File.fnmatch @glob, str, @flags
|
||||
end
|
||||
end
|
||||
|
||||
class DirectoryMatch < Match
|
||||
def initialize(nxt, flags, glob)
|
||||
super
|
||||
|
||||
@glob.gsub! "**", "*"
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def call(env, path)
|
||||
return if path and !File.exist?("#{path}/.")
|
||||
|
||||
dir = Dir.new(path ? path : ".")
|
||||
while ent = dir.read
|
||||
if match? ent
|
||||
full = path_join(path, ent)
|
||||
|
||||
if File.directory? full
|
||||
@next.call env, full
|
||||
end
|
||||
end
|
||||
end
|
||||
dir.close
|
||||
end
|
||||
end
|
||||
|
||||
class EntryMatch < Match
|
||||
def call(env, path)
|
||||
return if path and !File.exist?("#{path}/.")
|
||||
|
||||
begin
|
||||
dir = Dir.new(path ? path : ".")
|
||||
rescue SystemCallError
|
||||
return
|
||||
end
|
||||
|
||||
while ent = dir.read
|
||||
if match? ent
|
||||
env.matches << path_join(path, ent)
|
||||
end
|
||||
end
|
||||
dir.close
|
||||
end
|
||||
end
|
||||
|
||||
class DirectoriesOnly < Node
|
||||
def call(env, path)
|
||||
if path and File.exist?("#{path}/.")
|
||||
env.matches << "#{path}/"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Environment
|
||||
attr_reader :matches
|
||||
|
||||
def initialize(matches=[])
|
||||
@matches = matches
|
||||
end
|
||||
|
||||
private :initialize
|
||||
end
|
||||
|
||||
def self.path_split(str)
|
||||
start = 0
|
||||
ret = []
|
||||
|
||||
last_match = nil
|
||||
|
||||
while match = %r!/+!.match_from(str, start)
|
||||
cur_start, cur_end = match.full
|
||||
ret << str.byteslice(start, cur_start - start)
|
||||
ret << str.byteslice(cur_start, cur_end - cur_start)
|
||||
|
||||
start = cur_end
|
||||
|
||||
last_match = match
|
||||
end
|
||||
|
||||
if last_match
|
||||
ret << last_match.post_match
|
||||
else
|
||||
ret << str
|
||||
end
|
||||
|
||||
# Trim from end
|
||||
if !ret.empty?
|
||||
while s = ret.last and s.empty?
|
||||
ret.pop
|
||||
end
|
||||
end
|
||||
|
||||
ret
|
||||
end
|
||||
|
||||
def self.single_compile(glob, flags=0)
|
||||
parts = path_split(glob)
|
||||
|
||||
if glob.getbyte(-1) == 47 # ?/
|
||||
last = DirectoriesOnly.new nil, flags
|
||||
else
|
||||
file = parts.pop
|
||||
if /^[a-zA-Z0-9._]+$/.match(file)
|
||||
last = ConstantEntry.new nil, flags, file
|
||||
else
|
||||
last = EntryMatch.new nil, flags, file
|
||||
end
|
||||
end
|
||||
|
||||
until parts.empty?
|
||||
last.separator = parts.pop
|
||||
dir = parts.pop
|
||||
|
||||
if dir == "**"
|
||||
if parts.empty?
|
||||
last = StartRecursiveDirectories.new last, flags
|
||||
else
|
||||
last = RecursiveDirectories.new last, flags
|
||||
end
|
||||
elsif /^[^\*\?\]]+$/.match(dir)
|
||||
while /^[^\*\?\]]+$/.match(parts[-2])
|
||||
next_sep = parts.pop
|
||||
next_sect = parts.pop
|
||||
|
||||
dir = next_sect << next_sep << dir
|
||||
end
|
||||
|
||||
last = ConstantDirectory.new last, flags, dir
|
||||
elsif !dir.empty?
|
||||
last = DirectoryMatch.new last, flags, dir
|
||||
end
|
||||
end
|
||||
|
||||
if glob.getbyte(0) == 47 # ?/
|
||||
last = RootDirectory.new last, flags
|
||||
end
|
||||
|
||||
last
|
||||
end
|
||||
|
||||
def self.run(node, matches=[])
|
||||
env = Environment.new(matches)
|
||||
node.call env, nil
|
||||
env.matches
|
||||
end
|
||||
|
||||
def self.glob_cache
|
||||
@glob_cache
|
||||
end
|
||||
|
||||
def self.glob(pattern, flags, matches=[])
|
||||
# Rubygems uses Dir[] as a glorified File.exist? to check for multiple
|
||||
# extensions. So we went ahead and sped up that specific case.
|
||||
|
||||
if flags == 0 and
|
||||
m = /^([a-zA-Z0-9_.\/\s]*[a-zA-Z0-9_.])(?:\{([^{}\/\*\?]*)\})?$/.match(pattern)
|
||||
# no meta characters, so this is a glorified
|
||||
# File.exist? check. We allow for a brace expansion
|
||||
# only as a suffix.
|
||||
|
||||
if braces = m[2]
|
||||
stem = m[1]
|
||||
|
||||
braces.split(",").each do |s|
|
||||
path = "#{stem}#{s}"
|
||||
if File.exist? path
|
||||
matches << path
|
||||
end
|
||||
end
|
||||
|
||||
# Split strips an empty closing part, so we need to add it back in
|
||||
if braces.getbyte(-1) == 44 # ?,
|
||||
matches << stem if File.exist? stem
|
||||
end
|
||||
else
|
||||
matches << pattern if File.exist?(pattern)
|
||||
end
|
||||
|
||||
return matches
|
||||
end
|
||||
|
||||
ec_key = nil
|
||||
|
||||
if gc = @glob_cache
|
||||
ec_key = [pattern, flags]
|
||||
if patterns = gc.retrieve(ec_key)
|
||||
patterns.each do |node|
|
||||
run node, matches
|
||||
end
|
||||
|
||||
return matches
|
||||
end
|
||||
end
|
||||
|
||||
if pattern.include? "{"
|
||||
patterns = compile(pattern, flags)
|
||||
|
||||
gc.set ec_key, patterns if ec_key
|
||||
|
||||
patterns.each do |node|
|
||||
run node, matches
|
||||
end
|
||||
elsif node = single_compile(pattern, flags)
|
||||
gc.set ec_key, [node] if ec_key
|
||||
|
||||
run node, matches
|
||||
else
|
||||
matches
|
||||
end
|
||||
end
|
||||
|
||||
def self.compile(pattern, flags=0, patterns=[])
|
||||
escape = (flags & File::FNM_NOESCAPE) == 0
|
||||
|
||||
rbrace = nil
|
||||
lbrace = nil
|
||||
|
||||
# Do a quick search for a { to start the search better
|
||||
i = pattern.index("{")
|
||||
|
||||
# If there was a { found, then search
|
||||
if i
|
||||
nest = 0
|
||||
|
||||
while i < pattern.size
|
||||
char = pattern[i]
|
||||
|
||||
if char == "{"
|
||||
lbrace = i if nest == 0
|
||||
nest += 1
|
||||
end
|
||||
|
||||
if char == "}"
|
||||
nest -= 1
|
||||
end
|
||||
|
||||
if nest == 0
|
||||
rbrace = i
|
||||
break
|
||||
end
|
||||
|
||||
if char == "\\" and escape
|
||||
i += 1
|
||||
end
|
||||
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
|
||||
# There was a full {} expression detected, expand each part of it
|
||||
# recursively.
|
||||
if lbrace and rbrace
|
||||
pos = lbrace
|
||||
front = pattern[0...lbrace]
|
||||
back = pattern[(rbrace + 1)..-1]
|
||||
|
||||
while pos < rbrace
|
||||
nest = 0
|
||||
pos += 1
|
||||
last = pos
|
||||
|
||||
while pos < rbrace and not (pattern[pos] == "," and nest == 0)
|
||||
nest += 1 if pattern[pos] == "{"
|
||||
nest -= 1 if pattern[pos] == "}"
|
||||
|
||||
if pattern[pos] == "\\" and escape
|
||||
pos += 1
|
||||
break if pos == rbrace
|
||||
end
|
||||
|
||||
pos += 1
|
||||
end
|
||||
|
||||
brace_pattern = "#{front}#{pattern[last...pos]}#{back}"
|
||||
|
||||
compile brace_pattern, flags, patterns
|
||||
end
|
||||
|
||||
# No braces found, match the pattern normally
|
||||
else
|
||||
# Don't use .glob here because this code can detect properly
|
||||
# if a { is a brace or a just a normal character, but .glob can't.
|
||||
# if .glob is used and there is a { as a normal character, it will
|
||||
# recurse forever.
|
||||
if node = single_compile(pattern, flags)
|
||||
patterns << node
|
||||
end
|
||||
end
|
||||
|
||||
patterns
|
||||
end
|
||||
end
|
||||
end
|
||||
570
gxg/core/encoding.rb
Normal file
570
gxg/core/encoding.rb
Normal file
|
|
@ -0,0 +1,570 @@
|
|||
class EncodingError < StandardError
|
||||
end
|
||||
|
||||
class Encoding
|
||||
class UndefinedConversionError < EncodingError
|
||||
attr_accessor :source_encoding_name
|
||||
attr_accessor :destination_encoding_name
|
||||
attr_accessor :source_encoding
|
||||
attr_accessor :destination_encoding
|
||||
attr_accessor :error_char
|
||||
|
||||
private :source_encoding_name=
|
||||
private :destination_encoding_name=
|
||||
private :source_encoding=
|
||||
private :destination_encoding=
|
||||
private :error_char=
|
||||
end
|
||||
|
||||
class InvalidByteSequenceError < EncodingError
|
||||
attr_accessor :source_encoding_name
|
||||
attr_accessor :destination_encoding_name
|
||||
attr_accessor :source_encoding
|
||||
attr_accessor :destination_encoding
|
||||
attr_accessor :error_bytes
|
||||
attr_accessor :readagain_bytes
|
||||
attr_writer :incomplete_input
|
||||
|
||||
private :source_encoding_name=
|
||||
private :destination_encoding_name=
|
||||
private :source_encoding=
|
||||
private :destination_encoding=
|
||||
private :error_bytes=
|
||||
private :readagain_bytes=
|
||||
private :incomplete_input=
|
||||
|
||||
def initialize(message="")
|
||||
super(message)
|
||||
|
||||
@incomplete_input = nil
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def incomplete_input?
|
||||
@incomplete_input
|
||||
end
|
||||
end
|
||||
|
||||
class ConverterNotFoundError < EncodingError
|
||||
end
|
||||
|
||||
class CompatibilityError < EncodingError
|
||||
end
|
||||
|
||||
class Transcoding
|
||||
attr_accessor :source
|
||||
attr_accessor :target
|
||||
|
||||
def inspect
|
||||
"#<#{super} #{source} to #{target}"
|
||||
end
|
||||
end
|
||||
|
||||
class Converter
|
||||
attr_accessor :source_encoding
|
||||
attr_accessor :destination_encoding
|
||||
attr_reader :replacement
|
||||
attr_reader :options
|
||||
|
||||
def self.allocate
|
||||
Rubinius.primitive :encoding_converter_allocate
|
||||
raise PrimitiveFailure, "Encoding::Converter.allocate primitive failed"
|
||||
end
|
||||
|
||||
def self.asciicompat_encoding(string_or_encoding)
|
||||
encoding = Rubinius::Type.try_convert_to_encoding string_or_encoding
|
||||
|
||||
return if not encoding or undefined.equal? encoding
|
||||
return if encoding.ascii_compatible?
|
||||
|
||||
transcoding = TranscodingMap[encoding.name.upcase]
|
||||
return unless transcoding and transcoding.size == 1
|
||||
|
||||
Encoding.find transcoding.keys.first.to_s
|
||||
end
|
||||
|
||||
def self.search_convpath(from, to, options=undefined)
|
||||
new(from, to, options).convpath
|
||||
end
|
||||
|
||||
def initialize(from, to, options=undefined)
|
||||
@source_encoding = Rubinius::Type.coerce_to_encoding from
|
||||
@destination_encoding = Rubinius::Type.coerce_to_encoding to
|
||||
|
||||
if options.kind_of? Fixnum
|
||||
@options = options
|
||||
elsif !undefined.equal? options
|
||||
options = Rubinius::Type.coerce_to options, Hash, :to_hash
|
||||
|
||||
@options = 0
|
||||
unless options.empty?
|
||||
@options |= INVALID_REPLACE if options[:invalid] == :replace
|
||||
@options |= UNDEF_REPLACE if options[:undef] == :replace
|
||||
|
||||
if options[:newline] == :universal or options[:universal_newline]
|
||||
@options |= UNIVERSAL_NEWLINE_DECORATOR
|
||||
end
|
||||
|
||||
if options[:newline] == :crlf or options[:crlf_newline]
|
||||
@options |= CRLF_NEWLINE_DECORATOR
|
||||
end
|
||||
|
||||
if options[:newline] == :cr or options[:cr_newline]
|
||||
@options |= CR_NEWLINE_DECORATOR
|
||||
end
|
||||
|
||||
@options |= XML_TEXT_DECORATOR if options[:xml] == :text
|
||||
if options[:xml] == :attr
|
||||
@options |= XML_ATTR_CONTENT_DECORATOR
|
||||
@options |= XML_ATTR_QUOTE_DECORATOR
|
||||
end
|
||||
|
||||
replacement = options[:replace]
|
||||
end
|
||||
else
|
||||
@options = 0
|
||||
end
|
||||
|
||||
source_name = @source_encoding.name.upcase.to_sym
|
||||
dest_name = @destination_encoding.name.upcase.to_sym
|
||||
|
||||
unless source_name == dest_name
|
||||
@convpath, @converters = TranscodingPath[source_name, dest_name]
|
||||
end
|
||||
|
||||
unless @convpath
|
||||
conversion = "(#{@source_encoding.name} to #{@destination_encoding.name})"
|
||||
msg = "code converter not found #{conversion}"
|
||||
raise ConverterNotFoundError, msg
|
||||
end
|
||||
|
||||
if @options & (INVALID_REPLACE | UNDEF_REPLACE | UNDEF_HEX_CHARREF)
|
||||
if replacement.nil?
|
||||
if @destination_encoding == Encoding::UTF_8
|
||||
@replacement = "\xef\xbf\xbd".force_encoding(Encoding::UTF_8)
|
||||
else
|
||||
@replacement = "?".force_encoding(Encoding::US_ASCII)
|
||||
end
|
||||
else
|
||||
@replacement = Rubinius::Type.coerce_to replacement, String, :to_str
|
||||
end
|
||||
|
||||
replacement_encoding_name = @replacement.encoding.name.upcase
|
||||
@replacement_converters = []
|
||||
|
||||
@convpath.each do |enc|
|
||||
name = enc.to_s.upcase
|
||||
next if name == replacement_encoding_name
|
||||
|
||||
_, converters = TranscodingPath[replacement_encoding_name, enc]
|
||||
@replacement_converters << name << converters
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def convert(str)
|
||||
str = StringValue(str)
|
||||
|
||||
dest = ""
|
||||
status = primitive_convert str.dup, dest, nil, nil, @options | PARTIAL_INPUT
|
||||
|
||||
if status == :invalid_byte_sequence or
|
||||
status == :undefined_conversion or
|
||||
status == :incomplete_input
|
||||
raise last_error
|
||||
end
|
||||
|
||||
if status == :finished
|
||||
raise ArgumentError, "converter already finished"
|
||||
end
|
||||
|
||||
if status != :source_buffer_empty
|
||||
raise RuntimeError, "unexpected result of Encoding::Converter#primitive_convert: #{status}"
|
||||
end
|
||||
|
||||
dest
|
||||
end
|
||||
|
||||
def primitive_convert(source, target, offset=nil, size=nil, options=0)
|
||||
source = StringValue(source) if source
|
||||
target = StringValue(target)
|
||||
|
||||
if offset.nil?
|
||||
offset = target.bytesize
|
||||
else
|
||||
offset = Rubinius::Type.coerce_to offset, Fixnum, :to_int
|
||||
end
|
||||
|
||||
if size.nil?
|
||||
size = -1
|
||||
else
|
||||
size = Rubinius::Type.coerce_to size, Fixnum, :to_int
|
||||
|
||||
if size < 0
|
||||
raise ArgumentError, "byte size is negative"
|
||||
end
|
||||
end
|
||||
|
||||
if offset < 0
|
||||
raise ArgumentError, "byte offset is negative"
|
||||
end
|
||||
|
||||
if offset > target.bytesize
|
||||
raise ArgumentError, "byte offset is greater than destination buffer size"
|
||||
end
|
||||
|
||||
if !options.kind_of? Fixnum
|
||||
opts = Rubinius::Type.coerce_to options, Hash, :to_hash
|
||||
|
||||
options = 0
|
||||
options |= PARTIAL_INPUT if opts[:partial_input]
|
||||
options |= AFTER_OUTPUT if opts[:after_output]
|
||||
end
|
||||
|
||||
if primitive_errinfo.first == :invalid_byte_sequence
|
||||
source.prepend putback
|
||||
end
|
||||
|
||||
Rubinius.invoke_primitive(:encoding_converter_primitive_convert,
|
||||
self, source, target, offset, size, options)
|
||||
end
|
||||
|
||||
def putback(maxbytes=nil)
|
||||
Rubinius.primitive :encoding_converter_putback
|
||||
|
||||
putback maxbytes
|
||||
end
|
||||
|
||||
def finish
|
||||
dest = ""
|
||||
status = primitive_convert nil, dest
|
||||
|
||||
if status == :invalid_byte_sequence or
|
||||
status == :undefined_conversion or
|
||||
status == :incomplete_input
|
||||
raise last_error
|
||||
end
|
||||
|
||||
if status != :finished
|
||||
raise RuntimeError, "unexpected result of Encoding::Converter#finish: #{status}"
|
||||
end
|
||||
|
||||
dest
|
||||
end
|
||||
|
||||
def last_error
|
||||
error = Rubinius.invoke_primitive :encoding_converter_last_error, self
|
||||
return if error.nil?
|
||||
|
||||
result = error[:result]
|
||||
error_bytes = error[:error_bytes]
|
||||
error_bytes_msg = error_bytes.dump
|
||||
source_encoding_name = error[:source_encoding_name]
|
||||
destination_encoding_name = error[:destination_encoding_name]
|
||||
|
||||
case result
|
||||
when :invalid_byte_sequence
|
||||
read_again_string = error[:read_again_string]
|
||||
if read_again_string
|
||||
msg = "#{error_bytes_msg} followed by #{read_again_string.dump} on #{source_encoding_name}"
|
||||
else
|
||||
msg = "#{error_bytes_msg} on #{source_encoding_name}"
|
||||
end
|
||||
|
||||
exc = InvalidByteSequenceError.new msg
|
||||
when :incomplete_input
|
||||
msg = "incomplete #{error_bytes_msg} on #{source_encoding_name}"
|
||||
|
||||
exc = InvalidByteSequenceError.new msg
|
||||
when :undefined_conversion
|
||||
error_char = error_bytes
|
||||
if codepoint = error[:codepoint]
|
||||
error_bytes_msg = "U+%04X" % codepoint
|
||||
end
|
||||
|
||||
if source_encoding_name.to_sym == @source_encoding.name and
|
||||
destination_encoding_name.to_sym == @destination_encoding.name
|
||||
msg = "#{error_bytes_msg} from #{source_encoding_name} to #{destination_encoding_name}"
|
||||
else
|
||||
msg = "#{error_bytes_msg} to #{destination_encoding_name} in conversion from #{source_encoding_name}"
|
||||
transcoder = @converters.first
|
||||
msg << " to #{transcoder.target}"
|
||||
end
|
||||
|
||||
exc = UndefinedConversionError.new msg
|
||||
end
|
||||
|
||||
Rubinius.privately do
|
||||
exc.source_encoding_name = source_encoding_name
|
||||
src = Rubinius::Type.try_convert_to_encoding source_encoding_name
|
||||
exc.source_encoding = src unless undefined.equal? src
|
||||
|
||||
exc.destination_encoding_name = destination_encoding_name
|
||||
dst = Rubinius::Type.try_convert_to_encoding destination_encoding_name
|
||||
exc.destination_encoding = dst unless undefined.equal? dst
|
||||
|
||||
if error_char
|
||||
error_char.force_encoding src unless undefined.equal? src
|
||||
exc.error_char = error_char
|
||||
end
|
||||
|
||||
if result == :invalid_byte_sequence or result == :incomplete_input
|
||||
exc.error_bytes = error_bytes.force_encoding Encoding::ASCII_8BIT
|
||||
|
||||
if bytes = error[:read_again_bytes]
|
||||
exc.readagain_bytes = bytes.force_encoding Encoding::ASCII_8BIT
|
||||
end
|
||||
end
|
||||
|
||||
if result == :invalid_byte_sequence
|
||||
exc.incomplete_input = false
|
||||
elsif result == :incomplete_input
|
||||
exc.incomplete_input = true
|
||||
end
|
||||
end
|
||||
|
||||
exc
|
||||
end
|
||||
|
||||
def primitive_errinfo
|
||||
Rubinius.primitive :encoding_converter_primitive_errinfo
|
||||
raise PrimitiveFailure, "Encoding::Converter#primitive_errinfo primitive failed"
|
||||
end
|
||||
|
||||
def convpath
|
||||
path = []
|
||||
a = 0
|
||||
b = @convpath.size - 1
|
||||
|
||||
while a < b
|
||||
path << [Encoding.find(@convpath[a].to_s), Encoding.find(@convpath[a + 1].to_s)]
|
||||
a += 1
|
||||
end
|
||||
|
||||
path << "xml_text_escape" if @options & XML_TEXT_DECORATOR != 0
|
||||
path << "xml_attr_content_escape" if @options & XML_ATTR_CONTENT_DECORATOR != 0
|
||||
path << "xml_attr_quote" if @options & XML_ATTR_QUOTE_DECORATOR != 0
|
||||
path << "universal_newline" if @options & UNIVERSAL_NEWLINE_DECORATOR != 0
|
||||
path << "crlf_newline" if @options & CRLF_NEWLINE_DECORATOR != 0
|
||||
path << "cr_newline" if @options & CR_NEWLINE_DECORATOR != 0
|
||||
|
||||
path
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<Encoding::Converter: #{source_encoding.name} to #{destination_encoding.name}>"
|
||||
end
|
||||
|
||||
def replacement=(str)
|
||||
str = StringValue(str)
|
||||
|
||||
@replacement = str.encode(@destination_encoding)
|
||||
end
|
||||
|
||||
class TranscodingPath
|
||||
def self.paths
|
||||
@paths
|
||||
end
|
||||
|
||||
def self.default_transcoders?
|
||||
@transcoders_count == TranscodingMap.size
|
||||
end
|
||||
|
||||
def self.[](source, target)
|
||||
key = "[#{source}, #{target}]"
|
||||
|
||||
path, converters = paths[key]
|
||||
|
||||
unless path
|
||||
return unless path = search(source, target)
|
||||
paths[key] = [path]
|
||||
end
|
||||
|
||||
unless converters
|
||||
converters = get_converters path
|
||||
paths[key][1] = converters
|
||||
end
|
||||
|
||||
return path, converters
|
||||
end
|
||||
|
||||
def self.search(source, target)
|
||||
if entry = TranscodingMap[source]
|
||||
if entry[target]
|
||||
return [source, target]
|
||||
else
|
||||
visited = { source => true }
|
||||
search = { [source] => entry }
|
||||
|
||||
until search.empty?
|
||||
path, table = search.shift
|
||||
|
||||
table.each do |key, _|
|
||||
next if visited.key? key
|
||||
next unless entry = TranscodingMap[key]
|
||||
|
||||
return path << key << target if entry[target]
|
||||
|
||||
unless visited.key? key
|
||||
search[path.dup << key] = entry
|
||||
visited[key] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.get_converters(path)
|
||||
converters = []
|
||||
total = path.size - 1
|
||||
i = 0
|
||||
|
||||
while i < total
|
||||
entry = TranscodingMap[path[i]][path[i + 1]]
|
||||
|
||||
if entry.kind_of? String
|
||||
lib = "#{Rubinius::ENC_PATH}/#{entry}"
|
||||
Rubinius::NativeMethod.load_extension lib, entry
|
||||
|
||||
entry = TranscodingMap[path[i]][path[i + 1]]
|
||||
end
|
||||
|
||||
converters << entry
|
||||
|
||||
i += 1
|
||||
end
|
||||
|
||||
converters
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :name
|
||||
attr_reader :dummy
|
||||
|
||||
alias_method :to_s, :name
|
||||
alias_method :dummy?, :dummy
|
||||
|
||||
def self.compatible?(a, b)
|
||||
Rubinius.primitive :encoding_compatible_p
|
||||
raise PrimitiveFailure, "Encoding.compatible? primitive failed"
|
||||
end
|
||||
|
||||
def replicate(name)
|
||||
Rubinius.primitive :encoding_replicate
|
||||
raise PrimitiveFailure, "Encoding#replicate primitive failed"
|
||||
end
|
||||
|
||||
def ascii_compatible?
|
||||
Rubinius.primitive :encoding_ascii_compatible_p
|
||||
raise PrimitiveFailure, "Encoding#ascii_compatible? primitive failed"
|
||||
end
|
||||
|
||||
def self.aliases
|
||||
aliases = {}
|
||||
EncodingMap.each do |n, r|
|
||||
index = r.last
|
||||
next unless index
|
||||
|
||||
aname = r.first
|
||||
aliases[aname] = EncodingList[index].name if aname
|
||||
end
|
||||
|
||||
aliases
|
||||
end
|
||||
|
||||
def self.set_alias_index(name, obj)
|
||||
key = name.upcase.to_sym
|
||||
|
||||
case obj
|
||||
when Encoding
|
||||
source_name = obj.name
|
||||
when nil
|
||||
EncodingMap[key][1] = nil
|
||||
return
|
||||
else
|
||||
source_name = StringValue(obj)
|
||||
end
|
||||
|
||||
entry = EncodingMap[source_name.upcase.to_sym]
|
||||
raise ArgumentError, "unknown encoding name - #{source_name}" unless entry
|
||||
index = entry.last
|
||||
|
||||
EncodingMap[key][1] = index
|
||||
end
|
||||
|
||||
class << self
|
||||
private :set_alias_index
|
||||
end
|
||||
|
||||
def self.default_external
|
||||
if undefined.equal? @default_external
|
||||
@default_external = find "external"
|
||||
end
|
||||
@default_external
|
||||
end
|
||||
|
||||
def self.default_external=(enc)
|
||||
raise ArgumentError, "default external encoding cannot be nil" if enc.nil?
|
||||
|
||||
set_alias_index "external", enc
|
||||
set_alias_index "filesystem", enc
|
||||
@default_external = undefined
|
||||
end
|
||||
|
||||
def self.default_internal
|
||||
# Rubinius internal encoding is always UTF-8
|
||||
end
|
||||
|
||||
def self.default_internal=(enc)
|
||||
Rubinius::Logger.system.warn "Encoding.default_internal= is deprecated. Rubinius internal encoding is always UTF-8"
|
||||
end
|
||||
|
||||
def self.find(name)
|
||||
enc = Rubinius::Type.try_convert_to_encoding name
|
||||
return enc unless undefined.equal? enc
|
||||
|
||||
raise ArgumentError, "unknown encoding name - #{name}"
|
||||
end
|
||||
|
||||
def self.list
|
||||
EncodingList
|
||||
end
|
||||
|
||||
def self.locale_charmap
|
||||
LocaleCharmap
|
||||
end
|
||||
|
||||
def self.name_list
|
||||
EncodingMap.map do |n, r|
|
||||
index = r.last
|
||||
r.first or (index and EncodingList[index].name)
|
||||
end
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<Encoding:#{name}#{" (dummy)" if dummy?}>"
|
||||
end
|
||||
|
||||
def names
|
||||
entry = EncodingMap[name.upcase.to_sym]
|
||||
names = [name]
|
||||
EncodingMap.each do |k, r|
|
||||
aname = r.first
|
||||
names << aname if aname and r.last == entry.last
|
||||
end
|
||||
names
|
||||
end
|
||||
|
||||
def _dump(depth)
|
||||
name
|
||||
end
|
||||
|
||||
def self._load(name)
|
||||
find name
|
||||
end
|
||||
end
|
||||
864
gxg/core/enumerable.rb
Normal file
864
gxg/core/enumerable.rb
Normal file
|
|
@ -0,0 +1,864 @@
|
|||
# Just to save you 10 seconds, the reason we always use #each to extract
|
||||
# elements instead of something simpler is because Enumerable can not assume
|
||||
# any other methods than #each. If needed, class-specific versions of any of
|
||||
# these methods can be written *in those classes* to override these.
|
||||
|
||||
module Enumerable
|
||||
def chunk
|
||||
raise ArgumentError, "no block given" unless block_given?
|
||||
::Enumerator.new do |yielder|
|
||||
previous = nil
|
||||
accumulate = []
|
||||
each do |element|
|
||||
key = yield(element)
|
||||
if key.nil? || (key.is_a?(Symbol) && key.to_s[0, 1] == "_")
|
||||
yielder.yield [previous, accumulate] unless accumulate.empty?
|
||||
accumulate = []
|
||||
previous = nil
|
||||
case key
|
||||
when nil, :_separator
|
||||
when :_alone
|
||||
yielder.yield [key, [element]]
|
||||
else
|
||||
raise RuntimeError, "symbols beginning with an underscore are reserved"
|
||||
end
|
||||
else
|
||||
if previous.nil? || previous == key
|
||||
accumulate << element
|
||||
else
|
||||
yielder.yield [previous, accumulate] unless accumulate.empty?
|
||||
accumulate = [element]
|
||||
end
|
||||
previous = key
|
||||
end
|
||||
end
|
||||
yielder.yield [previous, accumulate] unless accumulate.empty?
|
||||
end
|
||||
end
|
||||
|
||||
def collect
|
||||
if block_given?
|
||||
ary = []
|
||||
each do |*element|
|
||||
ary << yield(*element)
|
||||
end
|
||||
ary
|
||||
else
|
||||
to_enum(:collect) { enumerator_size }
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :map, :collect
|
||||
|
||||
def count(item = undefined)
|
||||
seq = 0
|
||||
if !undefined.equal?(item)
|
||||
each do
|
||||
element = Rubinius.single_block_arg
|
||||
seq += 1 if item == element
|
||||
end
|
||||
elsif block_given?
|
||||
each { |element| seq += 1 if yield(element) }
|
||||
else
|
||||
each { seq += 1 }
|
||||
end
|
||||
seq
|
||||
end
|
||||
|
||||
def each_entry(*pass)
|
||||
return to_enum(:each_entry, *pass) { enumerator_size } unless block_given?
|
||||
each(*pass) do
|
||||
element = Rubinius.single_block_arg
|
||||
yield element
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
def each_with_object(memo)
|
||||
return to_enum(:each_with_object, memo) { enumerator_size } unless block_given?
|
||||
each do
|
||||
element = Rubinius.single_block_arg
|
||||
yield element, memo
|
||||
end
|
||||
memo
|
||||
end
|
||||
|
||||
alias_method :with_object, :each_with_object
|
||||
|
||||
def flat_map
|
||||
return to_enum(:flat_map) { enumerator_size } unless block_given?
|
||||
|
||||
array = []
|
||||
each do |*element|
|
||||
result = yield(*element)
|
||||
|
||||
value = Rubinius::Type.try_convert(result, Array, :to_ary) || result
|
||||
|
||||
if value.kind_of? Array
|
||||
array.concat value
|
||||
else
|
||||
array.push value
|
||||
end
|
||||
end
|
||||
|
||||
array
|
||||
end
|
||||
|
||||
alias_method :collect_concat, :flat_map
|
||||
|
||||
def lazy
|
||||
Enumerator::Lazy.new(self, enumerator_size) do |yielder, *args|
|
||||
yielder.<<(*args)
|
||||
end
|
||||
end
|
||||
|
||||
def enumerator_size
|
||||
Rubinius::Type.object_respond_to?(self, :size) ? size : nil
|
||||
end
|
||||
private :enumerator_size
|
||||
|
||||
def group_by
|
||||
return to_enum(:group_by) { enumerator_size } unless block_given?
|
||||
|
||||
h = {}
|
||||
each do
|
||||
element = Rubinius.single_block_arg
|
||||
key = yield(element)
|
||||
if h.key?(key)
|
||||
h[key] << element
|
||||
else
|
||||
h[key] = [element]
|
||||
end
|
||||
end
|
||||
Rubinius::Type.infect h, self
|
||||
h
|
||||
end
|
||||
|
||||
def slice_before(pattern = undefined, &block)
|
||||
pattern_given = !(undefined.equal? pattern)
|
||||
|
||||
raise ArgumentError, "cannot pass both pattern and block" if pattern_given && block_given?
|
||||
raise ArgumentError, "a pattern or a block must be provided" if !pattern_given && !block_given?
|
||||
|
||||
block = Proc.new{ |elem| pattern === elem } if pattern_given
|
||||
|
||||
Enumerator.new do |yielder|
|
||||
accumulator = nil
|
||||
each do
|
||||
element = Rubinius.single_block_arg
|
||||
start_new = block.yield(element)
|
||||
if start_new
|
||||
yielder.yield accumulator if accumulator
|
||||
accumulator = [element]
|
||||
else
|
||||
accumulator ||= []
|
||||
accumulator << element
|
||||
end
|
||||
end
|
||||
yielder.yield accumulator if accumulator
|
||||
end
|
||||
end
|
||||
|
||||
def slice_after(pattern = undefined, &block)
|
||||
pattern_given = !undefined.equal?(pattern)
|
||||
|
||||
raise ArgumentError, "cannot pass both pattern and block" if pattern_given && block_given?
|
||||
raise ArgumentError, "a pattern or a block must be provided" if !pattern_given && !block_given?
|
||||
|
||||
block = Proc.new { |elem| pattern === elem } if pattern_given
|
||||
|
||||
Enumerator.new do |yielder|
|
||||
accumulator = nil
|
||||
each do
|
||||
element = Rubinius.single_block_arg
|
||||
end_chunk = block.yield(element)
|
||||
accumulator ||= []
|
||||
if end_chunk
|
||||
accumulator << element
|
||||
yielder.yield accumulator
|
||||
accumulator = nil
|
||||
else
|
||||
accumulator << element
|
||||
end
|
||||
end
|
||||
yielder.yield accumulator if accumulator
|
||||
end
|
||||
end
|
||||
|
||||
def slice_when(&block)
|
||||
raise ArgumentError, "wrong number of arguments (0 for 1)" unless block_given?
|
||||
|
||||
Enumerator.new do |enum|
|
||||
ary = nil
|
||||
last_after = nil
|
||||
each_cons(2) do |before, after|
|
||||
last_after = after
|
||||
match = block.call before, after
|
||||
|
||||
ary ||= []
|
||||
if match
|
||||
ary << before
|
||||
enum.yield ary
|
||||
ary = []
|
||||
else
|
||||
ary << before
|
||||
end
|
||||
end
|
||||
|
||||
unless ary.nil?
|
||||
ary << last_after
|
||||
enum.yield ary
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def chunk_while(&block)
|
||||
raise ArgumentError, "no block given" unless block_given?
|
||||
|
||||
slice_when { |before, after| !(yield before, after) }
|
||||
end
|
||||
|
||||
def to_a(*arg)
|
||||
ary = []
|
||||
each(*arg) do
|
||||
element = Rubinius.single_block_arg
|
||||
ary << element
|
||||
nil
|
||||
end
|
||||
Rubinius::Type.infect ary, self
|
||||
ary
|
||||
end
|
||||
alias_method :entries, :to_a
|
||||
|
||||
def to_h(*arg)
|
||||
h = {}
|
||||
each_with_index(*arg) do |element, i|
|
||||
unless element.respond_to?(:to_ary)
|
||||
raise TypeError, "wrong element type #{element.class} at #{i} (expected array)"
|
||||
end
|
||||
|
||||
ary = element.to_ary
|
||||
if ary.size != 2
|
||||
raise ArgumentError, "wrong array length at #{i} (expected 2, was #{ary.size})"
|
||||
end
|
||||
|
||||
h[ary[0]] = ary[1]
|
||||
end
|
||||
h
|
||||
end
|
||||
|
||||
def zip(*args)
|
||||
args.map! do |a|
|
||||
if a.respond_to? :to_ary
|
||||
a.to_ary
|
||||
else
|
||||
a.to_enum(:each)
|
||||
end
|
||||
end
|
||||
|
||||
results = []
|
||||
i = 0
|
||||
each do
|
||||
element = Rubinius.single_block_arg
|
||||
entry = args.inject([element]) do |ary, a|
|
||||
ary << case a
|
||||
when Array
|
||||
a[i]
|
||||
else
|
||||
begin
|
||||
a.next
|
||||
rescue StopIteration
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
yield entry if block_given?
|
||||
|
||||
results << entry
|
||||
i += 1
|
||||
end
|
||||
|
||||
return nil if block_given?
|
||||
results
|
||||
end
|
||||
|
||||
def each_with_index(*args)
|
||||
return to_enum(:each_with_index, *args) { enumerator_size } unless block_given?
|
||||
|
||||
idx = 0
|
||||
each(*args) do
|
||||
element = Rubinius.single_block_arg
|
||||
yield element, idx
|
||||
idx += 1
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
def grep(pattern)
|
||||
ary = []
|
||||
|
||||
each do
|
||||
element = Rubinius.single_block_arg
|
||||
if pattern === element
|
||||
Regexp.set_block_last_match
|
||||
if block_given?
|
||||
ary << yield(element)
|
||||
else
|
||||
ary << element
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ary
|
||||
end
|
||||
|
||||
def grep_v(pattern)
|
||||
ary = []
|
||||
|
||||
each do
|
||||
element = Rubinius.single_block_arg
|
||||
unless pattern === element
|
||||
if block_given?
|
||||
ary << yield(element)
|
||||
else
|
||||
ary << element
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ary
|
||||
end
|
||||
|
||||
def sort(&prc)
|
||||
ary = to_a
|
||||
ary.frozen? ? ary.sort(&prc) : ary.sort!(&prc)
|
||||
end
|
||||
|
||||
class SortedElement
|
||||
def initialize(val, sort_id)
|
||||
@value, @sort_id = val, sort_id
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
attr_reader :value
|
||||
attr_reader :sort_id
|
||||
|
||||
def <=>(other)
|
||||
@sort_id <=> other.sort_id
|
||||
end
|
||||
end
|
||||
|
||||
def sort_by
|
||||
return to_enum(:sort_by) { enumerator_size } unless block_given?
|
||||
|
||||
# Transform each value to a tuple with the value and it's sort by value
|
||||
sort_values = map do
|
||||
element = Rubinius.single_block_arg
|
||||
SortedElement.new(element, yield(element))
|
||||
end.to_a
|
||||
|
||||
# Now sort the tuple according to the sort by value
|
||||
sort_values.sort!
|
||||
|
||||
# Now strip of the tuple leaving the original value
|
||||
sort_values.map! { |ary| ary.value }
|
||||
end
|
||||
|
||||
def inject(initial=undefined, sym=undefined)
|
||||
if !block_given? or !undefined.equal?(sym)
|
||||
if undefined.equal?(sym)
|
||||
sym = initial
|
||||
initial = undefined
|
||||
end
|
||||
|
||||
# Do the sym version
|
||||
|
||||
sym = sym.to_sym
|
||||
|
||||
each do
|
||||
element = Rubinius.single_block_arg
|
||||
if undefined.equal? initial
|
||||
initial = element
|
||||
else
|
||||
initial = initial.__send__(sym, element)
|
||||
end
|
||||
end
|
||||
|
||||
# Block version
|
||||
else
|
||||
each do
|
||||
element = Rubinius.single_block_arg
|
||||
if undefined.equal? initial
|
||||
initial = element
|
||||
else
|
||||
initial = yield(initial, element)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
undefined.equal?(initial) ? nil : initial
|
||||
end
|
||||
alias_method :reduce, :inject
|
||||
|
||||
def all?
|
||||
if block_given?
|
||||
each { |*element| return false unless yield(*element) }
|
||||
else
|
||||
each { return false unless Rubinius.single_block_arg }
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def any?
|
||||
if block_given?
|
||||
each { |*element| return true if yield(*element) }
|
||||
else
|
||||
each { return true if Rubinius.single_block_arg }
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
def cycle(many=nil)
|
||||
unless block_given?
|
||||
return to_enum(:cycle, many) do
|
||||
Rubinius::EnumerableHelper.cycle_size(enumerator_size, many)
|
||||
end
|
||||
end
|
||||
|
||||
if many
|
||||
many = Rubinius::Type.coerce_to_collection_index many
|
||||
return nil if many <= 0
|
||||
else
|
||||
many = nil
|
||||
end
|
||||
|
||||
cache = []
|
||||
each do
|
||||
element = Rubinius.single_block_arg
|
||||
cache << element
|
||||
yield element
|
||||
end
|
||||
|
||||
return nil if cache.empty?
|
||||
|
||||
if many
|
||||
i = 0
|
||||
many -= 1
|
||||
while i < many
|
||||
cache.each { |element| yield element }
|
||||
i += 1
|
||||
end
|
||||
else
|
||||
while true
|
||||
cache.each { |element| yield element }
|
||||
end
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def drop(n)
|
||||
n = Rubinius::Type.coerce_to_collection_index n
|
||||
raise ArgumentError, "attempt to drop negative size" if n < 0
|
||||
|
||||
ary = to_a
|
||||
return [] if n > ary.size
|
||||
ary[n...ary.size]
|
||||
end
|
||||
|
||||
def drop_while
|
||||
return to_enum(:drop_while) unless block_given?
|
||||
|
||||
ary = []
|
||||
dropping = true
|
||||
each do
|
||||
element = Rubinius.single_block_arg
|
||||
ary << element unless dropping &&= yield(element)
|
||||
end
|
||||
|
||||
ary
|
||||
end
|
||||
|
||||
def each_cons(num)
|
||||
n = Rubinius::Type.coerce_to_collection_index num
|
||||
raise ArgumentError, "invalid size: #{n}" if n <= 0
|
||||
|
||||
unless block_given?
|
||||
return to_enum(:each_cons, num) do
|
||||
enum_size = enumerator_size
|
||||
if enum_size.nil?
|
||||
nil
|
||||
elsif enum_size == 0 || enum_size < n
|
||||
0
|
||||
else
|
||||
enum_size - n + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
array = []
|
||||
each do
|
||||
element = Rubinius.single_block_arg
|
||||
array << element
|
||||
array.shift if array.size > n
|
||||
yield array.dup if array.size == n
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def each_slice(slice_size)
|
||||
n = Rubinius::Type.coerce_to_collection_index slice_size
|
||||
raise ArgumentError, "invalid slice size: #{n}" if n <= 0
|
||||
|
||||
unless block_given?
|
||||
return to_enum(:each_slice, slice_size) do
|
||||
enum_size = enumerator_size
|
||||
enum_size.nil? ? nil : (enum_size.to_f / n).ceil
|
||||
end
|
||||
end
|
||||
|
||||
a = []
|
||||
each do
|
||||
element = Rubinius.single_block_arg
|
||||
a << element
|
||||
if a.length == n
|
||||
yield a
|
||||
a = []
|
||||
end
|
||||
end
|
||||
|
||||
yield a unless a.empty?
|
||||
nil
|
||||
end
|
||||
|
||||
def find(ifnone=nil)
|
||||
return to_enum(:find, ifnone) unless block_given?
|
||||
|
||||
each do
|
||||
element = Rubinius.single_block_arg
|
||||
return element if yield(element)
|
||||
end
|
||||
|
||||
ifnone.call if ifnone
|
||||
end
|
||||
|
||||
alias_method :detect, :find
|
||||
|
||||
def find_all
|
||||
return to_enum(:find_all) { enumerator_size } unless block_given?
|
||||
|
||||
ary = []
|
||||
each do
|
||||
element = Rubinius.single_block_arg
|
||||
ary << element if yield(element)
|
||||
end
|
||||
ary
|
||||
end
|
||||
|
||||
alias_method :select, :find_all
|
||||
|
||||
def find_index(value=undefined)
|
||||
if undefined.equal? value
|
||||
return to_enum(:find_index) unless block_given?
|
||||
|
||||
i = 0
|
||||
each do |*args|
|
||||
return i if yield(*args)
|
||||
i += 1
|
||||
end
|
||||
else
|
||||
i = 0
|
||||
each do
|
||||
e = Rubinius.single_block_arg
|
||||
return i if e == value
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def first(n=undefined)
|
||||
return __take__(n) unless undefined.equal?(n)
|
||||
each do
|
||||
return Rubinius.single_block_arg
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def min(n=nil)
|
||||
if n.nil?
|
||||
min = undefined
|
||||
each do
|
||||
element = Rubinius.single_block_arg
|
||||
if undefined.equal? min
|
||||
min = element
|
||||
else
|
||||
comp = block_given? ? yield(element, min) : element <=> min
|
||||
unless comp
|
||||
raise ArgumentError, "comparison of #{element.class} with #{min} failed"
|
||||
end
|
||||
|
||||
if Comparable.compare_int(comp) < 0
|
||||
min = element
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
undefined.equal?(min) ? nil : min
|
||||
else
|
||||
if block_given?
|
||||
sort { |a, b| yield a, b }.take n
|
||||
else
|
||||
sort.take n
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def max(n=nil)
|
||||
if n.nil?
|
||||
max = undefined
|
||||
each do
|
||||
element = Rubinius.single_block_arg
|
||||
if undefined.equal? max
|
||||
max = element
|
||||
else
|
||||
comp = block_given? ? yield(element, max) : element <=> max
|
||||
unless comp
|
||||
raise ArgumentError, "comparison of #{element.class} with #{max} failed"
|
||||
end
|
||||
|
||||
if Comparable.compare_int(comp) > 0
|
||||
max = element
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
undefined.equal?(max) ? nil : max
|
||||
else
|
||||
if block_given?
|
||||
sort { |a, b| yield a, b }.reverse.take n
|
||||
else
|
||||
sort.reverse.take n
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def max_by(n=nil)
|
||||
return to_enum(:max_by, n) { enumerator_size } unless block_given?
|
||||
|
||||
if n.nil?
|
||||
max_element = nil
|
||||
max_result = undefined
|
||||
|
||||
each do
|
||||
element = Rubinius.single_block_arg
|
||||
result = yield element
|
||||
|
||||
if undefined.equal?(max_result) or \
|
||||
Rubinius::Type.coerce_to_comparison(max_result, result) < 0
|
||||
max_element = element
|
||||
max_result = result
|
||||
end
|
||||
end
|
||||
|
||||
max_element
|
||||
else
|
||||
sort_by { |element| yield element }.reverse.take n
|
||||
end
|
||||
end
|
||||
|
||||
def min_by(n=nil)
|
||||
return to_enum(:min_by, n) { enumerator_size } unless block_given?
|
||||
|
||||
if n.nil?
|
||||
min_element = nil
|
||||
min_result = undefined
|
||||
|
||||
each do
|
||||
element = Rubinius.single_block_arg
|
||||
result = yield element
|
||||
|
||||
if undefined.equal?(min_result) or \
|
||||
Rubinius::Type.coerce_to_comparison(min_result, result) > 0
|
||||
min_element = element
|
||||
min_result = result
|
||||
end
|
||||
end
|
||||
|
||||
min_element
|
||||
else
|
||||
sort_by { |element| yield element }.take n
|
||||
end
|
||||
end
|
||||
|
||||
def self.sort_proc
|
||||
@sort_proc ||= Proc.new do |a, b|
|
||||
unless ret = a <=> b
|
||||
raise ArgumentError, "Improper spaceship value"
|
||||
end
|
||||
ret
|
||||
end
|
||||
end
|
||||
|
||||
def minmax(&block)
|
||||
block = Enumerable.sort_proc unless block
|
||||
first_time = true
|
||||
min, max = nil
|
||||
|
||||
each do
|
||||
element = Rubinius.single_block_arg
|
||||
if first_time
|
||||
min = max = element
|
||||
first_time = false
|
||||
else
|
||||
unless min_cmp = block.call(min, element)
|
||||
raise ArgumentError, "comparison failed"
|
||||
end
|
||||
min = element if min_cmp > 0
|
||||
|
||||
unless max_cmp = block.call(max, element)
|
||||
raise ArgumentError, "comparison failed"
|
||||
end
|
||||
|
||||
max = element if max_cmp < 0
|
||||
end
|
||||
end
|
||||
[min, max]
|
||||
end
|
||||
|
||||
def minmax_by(&block)
|
||||
return to_enum(:minmax_by) { enumerator_size } unless block_given?
|
||||
|
||||
min_element = nil
|
||||
min_result = undefined
|
||||
|
||||
max_element = nil
|
||||
max_result = undefined
|
||||
|
||||
each do
|
||||
element = Rubinius.single_block_arg
|
||||
result = yield element
|
||||
|
||||
if undefined.equal?(min_result) or \
|
||||
Rubinius::Type.coerce_to_comparison(min_result, result) > 0
|
||||
min_element = element
|
||||
min_result = result
|
||||
end
|
||||
|
||||
if undefined.equal?(max_result) or \
|
||||
Rubinius::Type.coerce_to_comparison(max_result, result) < 0
|
||||
max_element = element
|
||||
max_result = result
|
||||
end
|
||||
end
|
||||
|
||||
[min_element, max_element]
|
||||
end
|
||||
|
||||
def none?
|
||||
if block_given?
|
||||
each { |*element| return false if yield(*element) }
|
||||
else
|
||||
each { return false if Rubinius.single_block_arg }
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
def one?
|
||||
found_one = false
|
||||
|
||||
if block_given?
|
||||
each do |*element|
|
||||
if yield(*element)
|
||||
return false if found_one
|
||||
found_one = true
|
||||
end
|
||||
end
|
||||
else
|
||||
each do
|
||||
if Rubinius.single_block_arg
|
||||
return false if found_one
|
||||
found_one = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
found_one
|
||||
end
|
||||
|
||||
def partition
|
||||
return to_enum(:partition) { enumerator_size } unless block_given?
|
||||
|
||||
left = []
|
||||
right = []
|
||||
each do
|
||||
element = Rubinius.single_block_arg
|
||||
yield(element) ? left.push(element) : right.push(element)
|
||||
end
|
||||
|
||||
return [left, right]
|
||||
end
|
||||
|
||||
def reject
|
||||
return to_enum(:reject) { enumerator_size } unless block_given?
|
||||
|
||||
ary = []
|
||||
each do
|
||||
element = Rubinius.single_block_arg
|
||||
ary << element unless yield(element)
|
||||
end
|
||||
|
||||
ary
|
||||
end
|
||||
|
||||
def reverse_each(&block)
|
||||
return to_enum(:reverse_each) { enumerator_size } unless block_given?
|
||||
|
||||
# There is no other way then to convert to an array first... see 1.9's source.
|
||||
to_a.reverse_each(&block)
|
||||
self
|
||||
end
|
||||
|
||||
def take(n)
|
||||
n = Rubinius::Type.coerce_to_collection_index n
|
||||
raise ArgumentError, "attempt to take negative size: #{n}" if n < 0
|
||||
|
||||
array = []
|
||||
|
||||
unless n <= 0
|
||||
each do
|
||||
element = Rubinius.single_block_arg
|
||||
array << element
|
||||
break if array.size >= n
|
||||
end
|
||||
end
|
||||
|
||||
array
|
||||
end
|
||||
alias_method :__take__, :take
|
||||
private :__take__
|
||||
|
||||
def take_while
|
||||
return to_enum(:take_while) unless block_given?
|
||||
|
||||
array = []
|
||||
each do |element|
|
||||
return array unless yield(element)
|
||||
array << element
|
||||
end
|
||||
|
||||
array
|
||||
end
|
||||
|
||||
def include?(obj)
|
||||
each { return true if Rubinius.single_block_arg == obj }
|
||||
false
|
||||
end
|
||||
|
||||
alias_method :member?, :include?
|
||||
|
||||
end
|
||||
13
gxg/core/enumerable_helper.rb
Normal file
13
gxg/core/enumerable_helper.rb
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
module Rubinius
|
||||
module EnumerableHelper
|
||||
def self.cycle_size(enum_size, many)
|
||||
if many
|
||||
many = Rubinius::Type.coerce_to_collection_index many
|
||||
many = 0 if many < 0
|
||||
(_size = enum_size).nil? ? nil : _size * many
|
||||
else
|
||||
enum_size.nil? ? nil : Float::INFINITY
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
500
gxg/core/enumerator.rb
Normal file
500
gxg/core/enumerator.rb
Normal file
|
|
@ -0,0 +1,500 @@
|
|||
class Enumerator
|
||||
include Enumerable
|
||||
|
||||
attr_writer :args
|
||||
private :args=
|
||||
|
||||
attr_writer :size
|
||||
private :size=
|
||||
|
||||
attr_reader :generator
|
||||
protected :generator
|
||||
|
||||
attr_reader :lookahead
|
||||
protected :lookahead
|
||||
|
||||
def initialize_enumerator(receiver, size, method_name, *method_args)
|
||||
@object = receiver
|
||||
@size = size
|
||||
@iter = method_name
|
||||
@args = method_args
|
||||
@generator = nil
|
||||
@lookahead = []
|
||||
@feedvalue = nil
|
||||
|
||||
self
|
||||
end
|
||||
private :initialize_enumerator
|
||||
|
||||
def initialize(receiver_or_size=undefined, method_name=:each, *method_args, &block)
|
||||
size = nil
|
||||
|
||||
if block_given?
|
||||
unless undefined.equal? receiver_or_size
|
||||
size = if receiver_or_size.nil? || receiver_or_size == Float::INFINITY || receiver_or_size.respond_to?(:call)
|
||||
receiver_or_size
|
||||
else
|
||||
Rubinius::Type.coerce_to receiver_or_size, Integer, :to_int
|
||||
end
|
||||
end
|
||||
|
||||
receiver = Generator.new(&block)
|
||||
else
|
||||
if undefined.equal? receiver_or_size
|
||||
raise ArgumentError, "Enumerator#initialize requires a block when called without arguments"
|
||||
end
|
||||
|
||||
receiver = receiver_or_size
|
||||
end
|
||||
|
||||
method_name = Rubinius::Type.coerce_to method_name, Symbol, :to_sym
|
||||
|
||||
initialize_enumerator receiver, size, method_name, *method_args
|
||||
|
||||
self
|
||||
end
|
||||
private :initialize
|
||||
|
||||
def each(*args)
|
||||
enumerator = self
|
||||
new_args = @args
|
||||
|
||||
unless args.empty?
|
||||
enumerator = dup
|
||||
new_args = @args.empty? ? args : (@args + args)
|
||||
end
|
||||
|
||||
Rubinius.privately do
|
||||
enumerator.args = new_args
|
||||
end
|
||||
|
||||
if block_given?
|
||||
Rubinius.privately do
|
||||
enumerator.each_with_block do |*yield_args|
|
||||
yield(*yield_args)
|
||||
end
|
||||
end
|
||||
else
|
||||
enumerator
|
||||
end
|
||||
end
|
||||
|
||||
def each_with_block
|
||||
@object.__send__ @iter, *@args do |*args|
|
||||
ret = yield(*args)
|
||||
unless @feedvalue.nil?
|
||||
ret = @feedvalue
|
||||
@feedvalue = nil
|
||||
end
|
||||
ret
|
||||
end
|
||||
end
|
||||
private :each_with_block
|
||||
|
||||
def each_with_index
|
||||
return to_enum(:each_with_index) { size } unless block_given?
|
||||
|
||||
idx = 0
|
||||
|
||||
each do
|
||||
o = Rubinius.single_block_arg
|
||||
val = yield(o, idx)
|
||||
idx += 1
|
||||
val
|
||||
end
|
||||
end
|
||||
|
||||
def next
|
||||
return @lookahead.shift unless @lookahead.empty?
|
||||
|
||||
@generator ||= Iterator.new self
|
||||
|
||||
begin
|
||||
return @generator.next if @generator.next?
|
||||
rescue StopIteration
|
||||
end
|
||||
|
||||
exception = StopIteration.new "iteration reached end"
|
||||
Rubinius.privately do
|
||||
exception.result = @generator.result
|
||||
end
|
||||
|
||||
raise exception
|
||||
end
|
||||
|
||||
def next_values
|
||||
Array(self.next)
|
||||
end
|
||||
|
||||
def peek
|
||||
return @lookahead.first unless @lookahead.empty?
|
||||
item = self.next
|
||||
@lookahead << item
|
||||
item
|
||||
end
|
||||
|
||||
def peek_values
|
||||
Array(self.peek).dup
|
||||
end
|
||||
|
||||
def rewind
|
||||
@object.rewind if @object.respond_to? :rewind
|
||||
@generator.rewind if @generator
|
||||
@lookahead = []
|
||||
@feedvalue = nil
|
||||
self
|
||||
end
|
||||
|
||||
def size
|
||||
@size.respond_to?(:call) ? @size.call : @size
|
||||
end
|
||||
|
||||
def with_index(offset=0)
|
||||
if offset
|
||||
offset = Rubinius::Type.coerce_to offset, Integer, :to_int
|
||||
else
|
||||
offset = 0
|
||||
end
|
||||
|
||||
return to_enum(:with_index, offset) { size } unless block_given?
|
||||
|
||||
each do
|
||||
o = Rubinius.single_block_arg
|
||||
val = yield(o, offset)
|
||||
offset += 1
|
||||
val
|
||||
end
|
||||
end
|
||||
|
||||
def feed(val)
|
||||
raise TypeError, "Feed value already set" unless @feedvalue.nil?
|
||||
@feedvalue = val
|
||||
nil
|
||||
end
|
||||
|
||||
def dup
|
||||
raise TypeError, "Can't copy iteration state" if @generator&.instance_variable_get(:@fiber)
|
||||
super
|
||||
end
|
||||
|
||||
def initialize_copy(other)
|
||||
@generator = other.generator&.dup
|
||||
@lookahead = other.lookahead.dup
|
||||
super
|
||||
end
|
||||
private :initialize_copy
|
||||
|
||||
class Yielder
|
||||
def initialize(&block)
|
||||
raise LocalJumpError, "Expected a block to be given" unless block_given?
|
||||
|
||||
@proc = block
|
||||
|
||||
self
|
||||
end
|
||||
private :initialize
|
||||
|
||||
def yield(*args)
|
||||
@proc.call *args
|
||||
end
|
||||
|
||||
def <<(*args)
|
||||
self.yield(*args)
|
||||
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
class Generator
|
||||
include Enumerable
|
||||
|
||||
def initialize(&block)
|
||||
raise LocalJumpError, "Expected a block to be given" unless block_given?
|
||||
|
||||
@proc = block
|
||||
|
||||
self
|
||||
end
|
||||
private :initialize
|
||||
|
||||
def each(*args)
|
||||
enclosed_yield = Proc.new { |*enclosed_args| yield *enclosed_args }
|
||||
|
||||
@proc.call Yielder.new(&enclosed_yield), *args
|
||||
end
|
||||
end
|
||||
|
||||
class Lazy < self
|
||||
class StopLazyError < Exception; end
|
||||
|
||||
def initialize(receiver, size=nil)
|
||||
raise ArgumentError, "Lazy#initialize requires a block" unless block_given?
|
||||
Rubinius.check_frozen
|
||||
|
||||
super(size) do |yielder, *each_args|
|
||||
begin
|
||||
receiver.each(*each_args) do |*args|
|
||||
yield yielder, *args
|
||||
end
|
||||
rescue Exception
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
private :initialize
|
||||
|
||||
def to_enum(method_name=:each, *method_args, &block)
|
||||
size = block_given? ? block : nil
|
||||
ret = Lazy.allocate
|
||||
|
||||
Rubinius.privately do
|
||||
ret.initialize_enumerator self, size, method_name, *method_args
|
||||
end
|
||||
|
||||
ret
|
||||
end
|
||||
alias_method :enum_for, :to_enum
|
||||
|
||||
def lazy
|
||||
self
|
||||
end
|
||||
|
||||
alias_method :force, :to_a
|
||||
|
||||
def take(n)
|
||||
n = Rubinius::Type.coerce_to n, Integer, :to_int
|
||||
raise ArgumentError, "attempt to take negative size" if n < 0
|
||||
|
||||
current_size = enumerator_size
|
||||
set_size = if current_size.kind_of?(Numeric)
|
||||
n < current_size ? n : current_size
|
||||
else
|
||||
current_size
|
||||
end
|
||||
|
||||
return to_enum(:cycle, 0).lazy if n.zero?
|
||||
|
||||
taken = 0
|
||||
Lazy.new(self, set_size) do |yielder, *args|
|
||||
if taken < n
|
||||
yielder.yield(*args)
|
||||
taken += 1
|
||||
raise StopLazyError unless taken < n
|
||||
else
|
||||
raise StopLazyError
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def drop(n)
|
||||
n = Rubinius::Type.coerce_to n, Integer, :to_int
|
||||
raise ArgumentError, "attempt to drop negative size" if n < 0
|
||||
|
||||
current_size = enumerator_size
|
||||
set_size = if current_size.kind_of?(Integer)
|
||||
n < current_size ? current_size - n : 0
|
||||
else
|
||||
current_size
|
||||
end
|
||||
|
||||
dropped = 0
|
||||
Lazy.new(self, set_size) do |yielder, *args|
|
||||
if dropped < n
|
||||
dropped += 1
|
||||
else
|
||||
yielder.yield(*args)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def take_while
|
||||
raise ArgumentError, "Lazy#take_while requires a block" unless block_given?
|
||||
|
||||
Lazy.new(self, nil) do |yielder, *args|
|
||||
if yield(*args)
|
||||
yielder.yield(*args)
|
||||
else
|
||||
raise StopLazyError
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def drop_while
|
||||
raise ArgumentError, "Lazy#drop_while requires a block" unless block_given?
|
||||
|
||||
succeeding = true
|
||||
Lazy.new(self, nil) do |yielder, *args|
|
||||
if succeeding
|
||||
unless yield(*args)
|
||||
succeeding = false
|
||||
yielder.yield(*args)
|
||||
end
|
||||
else
|
||||
yielder.yield(*args)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def select
|
||||
raise ArgumentError, 'Lazy#{select,find_all} requires a block' unless block_given?
|
||||
|
||||
Lazy.new(self, nil) do |yielder, *args|
|
||||
val = args.length >= 2 ? args : args.first
|
||||
yielder.yield(*args) if yield(val)
|
||||
end
|
||||
end
|
||||
alias_method :find_all, :select
|
||||
|
||||
def reject
|
||||
raise ArgumentError, "Lazy#reject requires a block" unless block_given?
|
||||
|
||||
Lazy.new(self, nil) do |yielder, *args|
|
||||
val = args.length >= 2 ? args : args.first
|
||||
yielder.yield(*args) unless yield(val)
|
||||
end
|
||||
end
|
||||
|
||||
def grep(pattern)
|
||||
Lazy.new(self, nil) do |yielder, *args|
|
||||
val = args.length >= 2 ? args : args.first
|
||||
|
||||
if pattern === val
|
||||
Regexp.set_block_last_match
|
||||
|
||||
if block_given?
|
||||
yielder.yield yield(val)
|
||||
else
|
||||
yielder.yield val
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def grep_v(pattern)
|
||||
Lazy.new(self, nil) do |yielder, *args|
|
||||
val = args.length >= 2 ? args : args.first
|
||||
|
||||
unless pattern === val
|
||||
if block_given?
|
||||
yielder.yield yield(val)
|
||||
else
|
||||
yielder.yield val
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def map
|
||||
raise ArgumentError, 'Lazy#{map,collect} requires a block' unless block_given?
|
||||
|
||||
Lazy.new(self, enumerator_size) do |yielder, *args|
|
||||
yielder.yield yield(*args)
|
||||
end
|
||||
end
|
||||
alias_method :collect, :map
|
||||
|
||||
def collect_concat
|
||||
raise ArgumentError, 'Lazy#{collect_concat,flat_map} requires a block' unless block_given?
|
||||
|
||||
Lazy.new(self, nil) do |yielder, *args|
|
||||
yield_ret = yield(*args)
|
||||
|
||||
if Rubinius::Type.object_respond_to?(yield_ret, :force) &&
|
||||
Rubinius::Type.object_respond_to?(yield_ret, :each)
|
||||
yield_ret.each do |v|
|
||||
yielder.yield v
|
||||
end
|
||||
else
|
||||
array = Rubinius::Type.check_convert_type yield_ret, Array, :to_ary
|
||||
if array
|
||||
array.each do |v|
|
||||
yielder.yield v
|
||||
end
|
||||
else
|
||||
yielder.yield yield_ret
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
alias_method :flat_map, :collect_concat
|
||||
|
||||
def zip(*lists)
|
||||
return super(*lists) { |entry| yield entry } if block_given?
|
||||
|
||||
lists.map! do |list|
|
||||
array = Rubinius::Type.check_convert_type list, Array, :to_ary
|
||||
|
||||
case
|
||||
when array
|
||||
array
|
||||
when Rubinius::Type.object_respond_to?(list, :each)
|
||||
list.to_enum :each
|
||||
else
|
||||
raise TypeError, "wrong argument type #{list.class} (must respond to :each)"
|
||||
end
|
||||
end
|
||||
|
||||
index = 0
|
||||
Lazy.new(self, enumerator_size) do |yielder, *args|
|
||||
val = args.length >= 2 ? args : args.first
|
||||
rests = lists.map { |list|
|
||||
case list
|
||||
when Array
|
||||
list[index]
|
||||
else
|
||||
begin
|
||||
list.next
|
||||
rescue StopIteration
|
||||
nil
|
||||
end
|
||||
end
|
||||
}
|
||||
yielder.yield [val, *rests]
|
||||
index += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Iterator
|
||||
STACK_SIZE = 1_048_576
|
||||
|
||||
attr_reader :result
|
||||
|
||||
def initialize(obj)
|
||||
@object = obj
|
||||
rewind
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def next?
|
||||
!@done
|
||||
end
|
||||
|
||||
def next
|
||||
reset unless @fiber
|
||||
|
||||
val = @fiber.resume
|
||||
|
||||
raise StopIteration, "iteration has ended" if @done
|
||||
|
||||
return val
|
||||
end
|
||||
|
||||
def rewind
|
||||
@fiber = nil
|
||||
@done = false
|
||||
end
|
||||
|
||||
def reset
|
||||
@done = false
|
||||
@fiber = Fiber.new stack_size: STACK_SIZE do
|
||||
obj = @object
|
||||
@result = obj.each { |*val| Fiber.yield *val }
|
||||
@done = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
294
gxg/core/env.rb
Normal file
294
gxg/core/env.rb
Normal file
|
|
@ -0,0 +1,294 @@
|
|||
module Rubinius
|
||||
class EnvironmentVariables
|
||||
include Enumerable
|
||||
|
||||
def [](key)
|
||||
value = getenv(StringValue(key))
|
||||
if value
|
||||
value = set_encoding value
|
||||
value.freeze
|
||||
end
|
||||
value
|
||||
end
|
||||
|
||||
def []=(key, value)
|
||||
key = StringValue(key)
|
||||
if value.nil?
|
||||
unsetenv(key)
|
||||
else
|
||||
if setenv(key, StringValue(value), 1) != 0
|
||||
Errno.handle("setenv")
|
||||
end
|
||||
end
|
||||
value
|
||||
end
|
||||
|
||||
alias_method :store, :[]=
|
||||
|
||||
def each_key
|
||||
return to_enum(:each_key) { size } unless block_given?
|
||||
|
||||
each { |k, v| yield k }
|
||||
end
|
||||
|
||||
def each_value
|
||||
return to_enum(:each_value) { size } unless block_given?
|
||||
|
||||
each { |k, v| yield v }
|
||||
end
|
||||
|
||||
def each
|
||||
return to_enum(:each) { size } unless block_given?
|
||||
|
||||
env = environ()
|
||||
ptr_size = FFI.type_size FFI.find_type(:pointer)
|
||||
|
||||
offset = 0
|
||||
cur = env + offset
|
||||
|
||||
until cur.read_pointer.null?
|
||||
entry = cur.read_pointer.read_string
|
||||
key, value = entry.split '=', 2
|
||||
value.taint if value
|
||||
key.taint if key
|
||||
|
||||
key = set_encoding key
|
||||
value = set_encoding value
|
||||
|
||||
yield key, value
|
||||
|
||||
offset += ptr_size
|
||||
cur = env + offset
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
alias_method :each_pair, :each
|
||||
|
||||
def delete(key)
|
||||
existing_value = self[key]
|
||||
if existing_value
|
||||
self[key] = nil
|
||||
elsif block_given?
|
||||
yield key
|
||||
end
|
||||
existing_value
|
||||
end
|
||||
|
||||
def delete_if(&block)
|
||||
return to_enum(:delete_if) { size } unless block_given?
|
||||
reject!(&block)
|
||||
self
|
||||
end
|
||||
|
||||
def include?(key)
|
||||
!self[key].nil?
|
||||
end
|
||||
|
||||
alias_method :has_key?, :include?
|
||||
alias_method :key?, :include?
|
||||
|
||||
# More efficient than using the one from Enumerable
|
||||
alias_method :member?, :include?
|
||||
|
||||
def fetch(key, absent=undefined)
|
||||
if block_given? and !undefined.equal?(absent)
|
||||
warn "block supersedes default value argument"
|
||||
end
|
||||
|
||||
if value = self[key]
|
||||
return value
|
||||
end
|
||||
|
||||
if block_given?
|
||||
return yield(key)
|
||||
elsif undefined.equal?(absent)
|
||||
raise KeyError, "key not found"
|
||||
end
|
||||
|
||||
return absent
|
||||
end
|
||||
|
||||
def to_s
|
||||
"ENV"
|
||||
end
|
||||
|
||||
def inspect
|
||||
to_hash.inspect
|
||||
end
|
||||
|
||||
def reject(&block)
|
||||
to_hash.reject(&block)
|
||||
end
|
||||
|
||||
def reject!
|
||||
return to_enum(:reject!) { size } unless block_given?
|
||||
|
||||
# Avoid deleting from environ while iterating because the
|
||||
# OS can handle that in a million different bad ways.
|
||||
|
||||
keys = []
|
||||
each { |k, v| keys << k if yield(k, v) }
|
||||
keys.each { |k| delete k }
|
||||
|
||||
keys.empty? ? nil : self
|
||||
end
|
||||
|
||||
def clear
|
||||
# Avoid deleting from environ while iterating because the
|
||||
# OS can handle that in a million different bad ways.
|
||||
|
||||
keys = []
|
||||
each { |k, v| keys << k }
|
||||
keys.each { |k| delete k }
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
def has_value?(value)
|
||||
each { |k, v| return true if v == value }
|
||||
return false
|
||||
end
|
||||
|
||||
alias_method :value?, :has_value?
|
||||
|
||||
def values_at(*params)
|
||||
params.map{ |k| self[k] }
|
||||
end
|
||||
|
||||
def index(value)
|
||||
each do |k, v|
|
||||
return k if v == value
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def invert
|
||||
to_hash.invert
|
||||
end
|
||||
|
||||
def key(value)
|
||||
index(value)
|
||||
end
|
||||
|
||||
def keys
|
||||
keys = []
|
||||
each { |k, v| keys << k }
|
||||
keys
|
||||
end
|
||||
|
||||
def values
|
||||
vals = []
|
||||
each { |k, v| vals << v }
|
||||
vals
|
||||
end
|
||||
|
||||
def empty?
|
||||
each { return false }
|
||||
return true
|
||||
end
|
||||
|
||||
def length
|
||||
sz = 0
|
||||
each { |k, v| sz += 1 }
|
||||
sz
|
||||
end
|
||||
|
||||
alias_method :size, :length
|
||||
|
||||
def rehash
|
||||
# No need to do anything, our keys are always strings
|
||||
end
|
||||
|
||||
def replace(other)
|
||||
clear
|
||||
other.each { |k, v| self[k] = v }
|
||||
end
|
||||
|
||||
def select(&blk)
|
||||
return to_enum { size } unless block_given?
|
||||
to_hash.select(&blk)
|
||||
end
|
||||
|
||||
def shift
|
||||
env = environ()
|
||||
|
||||
offset = 0
|
||||
cur = env + offset
|
||||
|
||||
ptr = cur.read_pointer
|
||||
return nil unless ptr
|
||||
|
||||
key, value = ptr.read_string.split "=", 2
|
||||
|
||||
return nil unless key
|
||||
|
||||
key.taint if key
|
||||
value.taint if value
|
||||
|
||||
delete key
|
||||
|
||||
key = set_encoding key
|
||||
value = set_encoding value
|
||||
|
||||
return [key, value]
|
||||
end
|
||||
|
||||
def to_a
|
||||
ary = []
|
||||
each { |k, v| ary << [k, v] }
|
||||
ary
|
||||
end
|
||||
|
||||
def to_hash
|
||||
hsh = {}
|
||||
each { |k, v| hsh[k] = v }
|
||||
hsh
|
||||
end
|
||||
|
||||
alias_method :to_h, :to_hash
|
||||
|
||||
def update(other)
|
||||
if block_given?
|
||||
other.each { |k, v| self[k] = yield(k, self[k], v) }
|
||||
else
|
||||
other.each { |k, v| self[k] = v }
|
||||
end
|
||||
end
|
||||
|
||||
def keep_if(&block)
|
||||
return to_enum(:keep_if) { size } unless block_given?
|
||||
select!(&block)
|
||||
self
|
||||
end
|
||||
|
||||
def select!
|
||||
return to_enum(:select!) { size } unless block_given?
|
||||
reject! { |k, v| !yield(k, v) }
|
||||
end
|
||||
|
||||
def assoc(key)
|
||||
key = StringValue(key)
|
||||
value = self[key]
|
||||
value ? [key, value] : nil
|
||||
end
|
||||
|
||||
def rassoc(value)
|
||||
value = StringValue(value)
|
||||
key = index(value)
|
||||
key ? [key, value] : nil
|
||||
end
|
||||
|
||||
def set_encoding(value)
|
||||
return unless value.kind_of? String
|
||||
if Encoding.default_internal && value.ascii_only?
|
||||
value.encode Encoding.default_internal, Encoding.find("locale")
|
||||
else
|
||||
value.force_encoding Encoding.find("locale")
|
||||
end
|
||||
end
|
||||
|
||||
private :set_encoding
|
||||
end
|
||||
end
|
||||
42
gxg/core/errno.rb
Normal file
42
gxg/core/errno.rb
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
##
|
||||
# Interface to the C errno integer.
|
||||
|
||||
module Errno
|
||||
##
|
||||
# Raises the appropriate SystemCallError exception with +additional+ as the
|
||||
# message. Equivalent to MRI's rb_sys_fail().
|
||||
#
|
||||
# Unlike rb_sys_fail(), handle does not raise an exception if errno is 0.
|
||||
|
||||
def self.handle(additional = nil)
|
||||
err = errno
|
||||
return if err == 0
|
||||
|
||||
raise SystemCallError.new(additional, err)
|
||||
end
|
||||
|
||||
def self.errno
|
||||
FFI.errno
|
||||
end
|
||||
|
||||
def self.eql?(code)
|
||||
FFI.errno == code
|
||||
end
|
||||
|
||||
def self.raise_waitreadable(message=nil)
|
||||
raise IO::EAGAINWaitReadable, message
|
||||
end
|
||||
|
||||
def self.raise_waitwritable(message=nil)
|
||||
raise IO::EAGAINWaitWritable, message
|
||||
end
|
||||
|
||||
def self.raise_eagain(message=nil)
|
||||
raise_errno(message, Errno::EAGAIN::Errno)
|
||||
end
|
||||
|
||||
def self.raise_errno(message, errno)
|
||||
raise SystemCallError.new(message, errno)
|
||||
end
|
||||
private :raise_errno
|
||||
end
|
||||
578
gxg/core/exception.rb
Normal file
578
gxg/core/exception.rb
Normal file
|
|
@ -0,0 +1,578 @@
|
|||
class Exception
|
||||
attr_accessor :locations
|
||||
attr_accessor :cause
|
||||
attr_accessor :custom_backtrace
|
||||
|
||||
def initialize(message = nil)
|
||||
@reason_message = message
|
||||
@locations = nil
|
||||
@backtrace = nil
|
||||
@custom_backtrace = nil
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def capture_backtrace!(offset=1)
|
||||
@locations = Rubinius::VM.backtrace offset
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
other.instance_of?(__class__) &&
|
||||
message == other.message &&
|
||||
backtrace == other.backtrace
|
||||
end
|
||||
|
||||
def to_s
|
||||
if @reason_message
|
||||
@reason_message.to_s
|
||||
else
|
||||
self.class.to_s
|
||||
end
|
||||
end
|
||||
|
||||
# This is here rather than in yaml.rb because it contains "private"
|
||||
# information, ie, the list of ivars. Putting it over in the yaml
|
||||
# source means it's easy to forget about.
|
||||
def to_yaml_properties
|
||||
list = super
|
||||
list.delete :@backtrace
|
||||
list.delete :@custom_backtrace
|
||||
return list
|
||||
end
|
||||
|
||||
def message
|
||||
@reason_message
|
||||
end
|
||||
|
||||
# Needed to properly implement #exception, which must clone and call
|
||||
# #initialize again, BUT not a subclasses initialize.
|
||||
alias_method :__initialize__, :initialize
|
||||
|
||||
def backtrace
|
||||
return @custom_backtrace if @custom_backtrace
|
||||
|
||||
if backtrace?
|
||||
awesome_backtrace.to_mri
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
# Indicates if the Exception has a backtrace set
|
||||
def backtrace?
|
||||
(@backtrace || @locations) ? true : false
|
||||
end
|
||||
|
||||
def awesome_backtrace
|
||||
@backtrace ||= Rubinius::Backtrace.backtrace(@locations)
|
||||
end
|
||||
|
||||
def render(header="An exception occurred", io=STDERR, color=true)
|
||||
exception = self
|
||||
exceptions = []
|
||||
|
||||
while exception
|
||||
exceptions.unshift exception
|
||||
exception = exception.cause
|
||||
end
|
||||
|
||||
while exception = exceptions.shift
|
||||
if custom_trace = exception.custom_backtrace
|
||||
io.puts "User-defined backtrace:", "\n"
|
||||
|
||||
custom_trace.reverse_each { |line| io.puts line }
|
||||
|
||||
io.puts "\nBacktrace:", "\n"
|
||||
end
|
||||
|
||||
io.puts exception.awesome_backtrace.show("\n", color)
|
||||
|
||||
io.puts "\n#{exception.message} (#{exception.class})"
|
||||
|
||||
io.puts "\nCausing:", "\n" unless exceptions.empty?
|
||||
end
|
||||
|
||||
message_lines = message.to_s.split("\n")
|
||||
|
||||
io.puts header
|
||||
io.puts
|
||||
io.puts " #{message_lines.shift} (#{self.class})"
|
||||
|
||||
message_lines.each do |line|
|
||||
io.puts " #{line}"
|
||||
end
|
||||
end
|
||||
|
||||
def set_backtrace(bt)
|
||||
if bt.kind_of? Rubinius::Backtrace
|
||||
@backtrace = bt
|
||||
else
|
||||
# See if we stashed a Backtrace object away, and use it.
|
||||
if hidden_bt = Rubinius::Backtrace.detect_backtrace(bt)
|
||||
@backtrace = hidden_bt
|
||||
else
|
||||
type_error = TypeError.new "backtrace must be Array of String"
|
||||
case bt
|
||||
when Array
|
||||
if bt.all? { |s| s.kind_of? String }
|
||||
@custom_backtrace = bt
|
||||
else
|
||||
raise type_error
|
||||
end
|
||||
when String
|
||||
@custom_backtrace = [bt]
|
||||
when nil
|
||||
@custom_backtrace = nil
|
||||
else
|
||||
raise type_error
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# This is important, because I subclass can just override #to_s and calling
|
||||
# #message will call it. Using an alias doesn't achieve that.
|
||||
def message
|
||||
to_s
|
||||
end
|
||||
|
||||
def inspect
|
||||
s = self.to_s
|
||||
if s.empty?
|
||||
self.class.name
|
||||
else
|
||||
"#<#{self.class.name}: #{s}>"
|
||||
end
|
||||
end
|
||||
|
||||
class << self
|
||||
alias_method :exception, :new
|
||||
end
|
||||
|
||||
def exception(message=nil)
|
||||
if message
|
||||
unless message.equal? self
|
||||
# As strange as this might seem, this IS actually the protocol
|
||||
# that MRI implements for this. The explicit call to
|
||||
# Exception#initialize (via __initialize__) is exactly what MRI
|
||||
# does.
|
||||
e = clone
|
||||
Rubinius.privately { e.__initialize__(message) }
|
||||
return e
|
||||
end
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
def location
|
||||
[context.file.to_s, context.line]
|
||||
end
|
||||
end
|
||||
|
||||
class PrimitiveFailure < Exception
|
||||
end
|
||||
|
||||
class ScriptError < Exception
|
||||
end
|
||||
|
||||
class StandardError < Exception
|
||||
end
|
||||
|
||||
class SignalException < Exception
|
||||
end
|
||||
|
||||
class NoMemoryError < Exception
|
||||
end
|
||||
|
||||
class ZeroDivisionError < StandardError
|
||||
end
|
||||
|
||||
class ArgumentError < StandardError
|
||||
def to_s
|
||||
if @given and @expected
|
||||
if @method_name
|
||||
"method '#{@method_name}': given #{@given}, expected #{@expected}"
|
||||
else
|
||||
"given #{@given}, expected #{@expected}"
|
||||
end
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class UncaughtThrowError < ArgumentError
|
||||
attr_accessor :tag
|
||||
end
|
||||
|
||||
class IndexError < StandardError
|
||||
end
|
||||
|
||||
class StopIteration < IndexError
|
||||
end
|
||||
|
||||
class RangeError < StandardError
|
||||
end
|
||||
|
||||
class FloatDomainError < RangeError
|
||||
end
|
||||
|
||||
class LocalJumpError < StandardError
|
||||
end
|
||||
|
||||
class NameError < StandardError
|
||||
attr_reader :name
|
||||
|
||||
def initialize(*args, receiver: nil)
|
||||
super(args.shift)
|
||||
|
||||
@name = args.shift
|
||||
@receiver = receiver
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def receiver
|
||||
if @receiver
|
||||
@receiver
|
||||
else
|
||||
raise ArgumentError, 'no receiver is available'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class NoMethodError < NameError
|
||||
attr_reader :name
|
||||
attr_reader :args
|
||||
|
||||
def initialize(*arguments, **options)
|
||||
super(arguments.shift, **options)
|
||||
@name = arguments.shift
|
||||
@args = arguments.shift
|
||||
end
|
||||
|
||||
private :initialize
|
||||
end
|
||||
|
||||
class RuntimeError < StandardError
|
||||
end
|
||||
|
||||
class SecurityError < StandardError
|
||||
end
|
||||
|
||||
class ThreadError < StandardError
|
||||
end
|
||||
|
||||
class FiberError < StandardError
|
||||
end
|
||||
|
||||
class TypeError < StandardError
|
||||
end
|
||||
|
||||
class FloatDomainError < RangeError
|
||||
end
|
||||
|
||||
class RegexpError < StandardError
|
||||
end
|
||||
|
||||
class LoadError < ScriptError
|
||||
attr_accessor :path
|
||||
|
||||
def initialize(message=nil, path: nil)
|
||||
super message
|
||||
@path = path
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
class InvalidExtensionError < LoadError
|
||||
end
|
||||
|
||||
class MRIExtensionError < InvalidExtensionError
|
||||
end
|
||||
end
|
||||
|
||||
class NotImplementedError < ScriptError
|
||||
end
|
||||
|
||||
class Interrupt < SignalException
|
||||
def initialize(*args)
|
||||
super(args.shift)
|
||||
@name = args.shift
|
||||
end
|
||||
|
||||
private :initialize
|
||||
end
|
||||
|
||||
class IOError < StandardError
|
||||
end
|
||||
|
||||
class EOFError < IOError
|
||||
end
|
||||
|
||||
class LocalJumpError < StandardError
|
||||
end
|
||||
|
||||
class SyntaxError < ScriptError
|
||||
attr_accessor :column
|
||||
attr_accessor :line
|
||||
attr_accessor :file
|
||||
attr_accessor :code
|
||||
|
||||
def self.from(message, column, line, code, file)
|
||||
exc = new message
|
||||
exc.file = file
|
||||
exc.line = line
|
||||
exc.column = column
|
||||
exc.code = code
|
||||
exc
|
||||
end
|
||||
|
||||
def reason
|
||||
@reason_message
|
||||
end
|
||||
end
|
||||
|
||||
class SystemExit < Exception
|
||||
|
||||
##
|
||||
# Process exit status if this exception is raised
|
||||
|
||||
attr_reader :status
|
||||
|
||||
##
|
||||
# Creates a SystemExit exception with optional status and message. If the
|
||||
# status is omitted, Process::EXIT_SUCCESS is used.
|
||||
#--
|
||||
# *args is used to simulate optional prepended argument like MRI
|
||||
|
||||
def initialize(first=nil, *args)
|
||||
if first.kind_of?(Fixnum)
|
||||
status = first
|
||||
super(*args)
|
||||
else
|
||||
status = Process::EXIT_SUCCESS
|
||||
super
|
||||
end
|
||||
|
||||
@status = status
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
##
|
||||
# Returns true is exiting successfully, false if not. A successful exit is
|
||||
# one with a status equal to 0 (zero). Any other status is considered a
|
||||
# unsuccessful exit.
|
||||
|
||||
def success?
|
||||
status == Process::EXIT_SUCCESS
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class SystemCallError < StandardError
|
||||
|
||||
attr_reader :errno
|
||||
|
||||
def self.errno_error(message, errno, location)
|
||||
Rubinius.primitive :exception_errno_error
|
||||
raise PrimitiveFailure, "SystemCallError.errno_error failed"
|
||||
end
|
||||
|
||||
# We use .new here because when errno is set, we attempt to
|
||||
# lookup and return a subclass of SystemCallError, specificly,
|
||||
# one of the Errno subclasses.
|
||||
def self.new(*args)
|
||||
# This method is used 2 completely different ways. One is when it's called
|
||||
# on SystemCallError, in which case it tries to construct a Errno subclass
|
||||
# or makes a generic instead of itself.
|
||||
#
|
||||
# Otherwise it's called on a Errno subclass and just helps setup
|
||||
# a instance of the subclass
|
||||
if self.equal? SystemCallError
|
||||
case args.size
|
||||
when 1
|
||||
if args.first.kind_of?(Fixnum)
|
||||
errno = args.first
|
||||
message = nil
|
||||
else
|
||||
errno = nil
|
||||
message = StringValue(args.first)
|
||||
end
|
||||
location = nil
|
||||
when 2
|
||||
message, errno = args
|
||||
location = nil
|
||||
when 3
|
||||
message, errno, location = args
|
||||
else
|
||||
raise ArgumentError, "wrong number of arguments (#{args.size} for 1..3)"
|
||||
end
|
||||
|
||||
# If it corresponds to a known Errno class, create and return it now
|
||||
if errno && error = SystemCallError.errno_error(message, errno, location)
|
||||
return error
|
||||
else
|
||||
return super(message, errno, location)
|
||||
end
|
||||
else
|
||||
case args.size
|
||||
when 0
|
||||
message = nil
|
||||
location = nil
|
||||
when 1
|
||||
message = StringValue(args.first)
|
||||
location = nil
|
||||
when 2
|
||||
message, location = args
|
||||
else
|
||||
raise ArgumentError, "wrong number of arguments (#{args.size} for 0..2)"
|
||||
end
|
||||
|
||||
if defined?(self::Errno) && self::Errno.kind_of?(Fixnum)
|
||||
errno = self::Errno
|
||||
error = SystemCallError.errno_error(message, self::Errno, location)
|
||||
if error && error.class.equal?(self)
|
||||
return error
|
||||
end
|
||||
end
|
||||
|
||||
error = allocate
|
||||
Rubinius::Unsafe.set_class error, self
|
||||
Rubinius.privately { error.initialize(*args) }
|
||||
return error
|
||||
end
|
||||
end
|
||||
|
||||
# Must do this here because we have a unique new and otherwise .exception will
|
||||
# call Exception.new because of the alias in Exception.
|
||||
class << self
|
||||
alias_method :exception, :new
|
||||
end
|
||||
|
||||
# Use splat args here so that arity returns -1 to match MRI.
|
||||
def initialize(*args)
|
||||
kls = self.class
|
||||
message, errno, location = args
|
||||
@errno = errno
|
||||
|
||||
msg = "unknown error"
|
||||
msg << " @ #{StringValue(location)}" if location
|
||||
msg << " - #{StringValue(message)}" if message
|
||||
super(msg)
|
||||
end
|
||||
|
||||
private :initialize
|
||||
end
|
||||
|
||||
class KeyError < IndexError
|
||||
end
|
||||
|
||||
class SignalException < Exception
|
||||
|
||||
attr_reader :signo
|
||||
attr_reader :signm
|
||||
|
||||
def initialize(signo = nil, signm = nil)
|
||||
# MRI overrides this behavior just for SignalException itself
|
||||
# but not for anything that inherits from it, therefore we
|
||||
# need this ugly check to make sure it works as intented.
|
||||
return super(signo) unless self.class == SignalException
|
||||
if signo.is_a? Integer
|
||||
unless @signm = Signal::Numbers[signo]
|
||||
raise ArgumentError, "invalid signal number #{signo}"
|
||||
end
|
||||
@signo = signo
|
||||
@signm = signm || "SIG#{@signm}"
|
||||
elsif signo
|
||||
if signm
|
||||
raise ArgumentError, "wrong number of arguments (2 for 1)"
|
||||
end
|
||||
signm = signo
|
||||
if signo.kind_of?(Symbol)
|
||||
signm = signm.to_s
|
||||
else
|
||||
signm = StringValue(signm)
|
||||
end
|
||||
signm = signm[3..-1] if signm.prefix? "SIG"
|
||||
unless @signo = Signal::Names[signm]
|
||||
raise ArgumentError, "invalid signal name #{signm}"
|
||||
end
|
||||
@signm = "SIG#{signm}"
|
||||
end
|
||||
super(@signm)
|
||||
end
|
||||
|
||||
private :initialize
|
||||
end
|
||||
|
||||
class StopIteration
|
||||
attr_accessor :result
|
||||
private :result=
|
||||
end
|
||||
|
||||
##
|
||||
# Base class for various exceptions raised in the VM.
|
||||
|
||||
class Rubinius::MachineException < Exception
|
||||
end
|
||||
|
||||
class Rubinius::ConcurrentUpdateError < Rubinius::MachineException
|
||||
end
|
||||
|
||||
class Rubinius::VMException < Exception
|
||||
end
|
||||
|
||||
##
|
||||
# Raised in the VM when an assertion fails.
|
||||
|
||||
class Rubinius::AssertionError < Rubinius::VMException
|
||||
end
|
||||
|
||||
##
|
||||
# Raised in the VM when attempting to read/write outside
|
||||
# the bounds of an object.
|
||||
|
||||
class Rubinius::ObjectBoundsExceededError < Rubinius::VMException
|
||||
end
|
||||
|
||||
# Defined by the VM itself
|
||||
class Rubinius::InvalidBytecode < Rubinius::Internal
|
||||
attr_reader :compiled_code
|
||||
attr_reader :ip
|
||||
|
||||
def message
|
||||
if @compiled_code
|
||||
if @ip and @ip >= 0
|
||||
"#{super} - at #{@compiled_code.name}+#{@ip}"
|
||||
else
|
||||
"#{super} - method #{@compiled_code.name}"
|
||||
end
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class InterpreterError < Exception
|
||||
|
||||
end
|
||||
|
||||
class DeadlockError < Exception
|
||||
|
||||
end
|
||||
|
||||
# MRI has an Exception class named "fatal" that is raised
|
||||
# by the rb_fatal function. The class is not accessible from
|
||||
# ruby because the name is begins with a lower-case letter.
|
||||
# Also, the exception cannot be rescued.
|
||||
#
|
||||
# To support rb_fatal in the C-API, Rubinius provides the
|
||||
# following FatalError class. If it clashes with code in
|
||||
# the wild, we can rename it.
|
||||
|
||||
class FatalError < Exception
|
||||
end
|
||||
44
gxg/core/executable.rb
Normal file
44
gxg/core/executable.rb
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
##
|
||||
# Parent class of everything that can live in a MethodTable.
|
||||
#
|
||||
# When calling an Executable, first the primitive slot is checked and executed
|
||||
# if it exists. If execution of the primitive is successful, the value is put
|
||||
# on the stack, and execution of the method is finished. If unsuccessful or
|
||||
# absent (only in the case of a CompiledCode), the ruby code code
|
||||
# in the CompiledCode is run.
|
||||
module Rubinius
|
||||
class Executable
|
||||
attr_accessor :serial
|
||||
attr_accessor :primitive
|
||||
attr_accessor :tags
|
||||
|
||||
def self.allocate
|
||||
Rubinius.primitive :executable_allocate
|
||||
raise PrimitiveFailure, "Executable.allocate primitive failed"
|
||||
end
|
||||
|
||||
def invoke(name, mod, recv, args, block)
|
||||
Rubinius.primitive :executable_invoke
|
||||
raise PrimitiveFailure, "Executable#invoke primitive failed"
|
||||
end
|
||||
|
||||
def custom_call_site
|
||||
Rubinius.primitive :executable_set_custom_call_site
|
||||
raise PrimitiveFailure, "Executable#set_custom_call_site primitive failed"
|
||||
end
|
||||
|
||||
##
|
||||
# Allows instances of Executable and its subclasses
|
||||
# to duck-type as CompiledCode::Visibility since
|
||||
# they can both be entries in a MethodTable.
|
||||
def public?
|
||||
true
|
||||
end
|
||||
|
||||
def private?
|
||||
false
|
||||
end
|
||||
|
||||
alias_method :protected?, :private?
|
||||
end
|
||||
end
|
||||
23
gxg/core/false.rb
Normal file
23
gxg/core/false.rb
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
class FalseClass
|
||||
def &(other)
|
||||
false
|
||||
end
|
||||
|
||||
def ^(other)
|
||||
!!other
|
||||
end
|
||||
|
||||
def to_s
|
||||
"false"
|
||||
end
|
||||
|
||||
alias_method :|, :^
|
||||
|
||||
def inspect
|
||||
# Call #to_s rather than alias it so that people can change #to_s if they
|
||||
# wish. Yes, sounds dumb.
|
||||
to_s
|
||||
end
|
||||
end
|
||||
|
||||
FALSE = false
|
||||
528
gxg/core/ffi.rb
Normal file
528
gxg/core/ffi.rb
Normal file
|
|
@ -0,0 +1,528 @@
|
|||
##
|
||||
# A Foreign Function Interface used to bind C libraries to ruby.
|
||||
|
||||
module Rubinius
|
||||
module FFI
|
||||
|
||||
# Specialised error classes
|
||||
class TypeError < RuntimeError; end
|
||||
|
||||
class NotFoundError < RuntimeError; end
|
||||
|
||||
class << self
|
||||
def add_typedef(current, add)
|
||||
if current.kind_of? Fixnum
|
||||
code = current
|
||||
else
|
||||
code = FFI::TypeDefs[current]
|
||||
raise TypeError, "Unable to resolve type '#{current}'" unless code
|
||||
end
|
||||
|
||||
FFI::TypeDefs[add] = code
|
||||
end
|
||||
|
||||
def find_type(name)
|
||||
code = FFI::TypeDefs[name]
|
||||
raise TypeError, "Unable to resolve type '#{name}'" unless code
|
||||
return code
|
||||
end
|
||||
|
||||
##
|
||||
# Given a +type+ as a number, indicate how many bytes that type
|
||||
# takes up on this platform.
|
||||
|
||||
def type_size(type)
|
||||
Rubinius.primitive :nativefunction_type_size
|
||||
|
||||
case type
|
||||
when Symbol
|
||||
return type_size(find_type(type))
|
||||
when Rubinius::NativeFunction
|
||||
return type_size(TYPE_PTR)
|
||||
when FFI::Enum
|
||||
return type_size(TYPE_ENUM)
|
||||
end
|
||||
|
||||
raise PrimitiveFailure, "FFI.type_size primitive failed: #{type}"
|
||||
end
|
||||
|
||||
def size_to_type(size)
|
||||
if sz = TypeSizes[size]
|
||||
return sz
|
||||
end
|
||||
|
||||
# Be like C, use int as the default type size.
|
||||
return :int
|
||||
end
|
||||
|
||||
def config(name)
|
||||
Rubinius::Config["rbx.platform.#{name}"]
|
||||
end
|
||||
|
||||
def config_hash(name)
|
||||
vals = {}
|
||||
section = "rbx.platform.#{name}."
|
||||
Rubinius::Config.section section do |key, value|
|
||||
vals[key.substring(section.size, key.length)] = value
|
||||
end
|
||||
vals
|
||||
end
|
||||
|
||||
def errno
|
||||
FFI::Platform::POSIX.errno
|
||||
end
|
||||
|
||||
# Convenience method for determining if a function call succeeded or failed
|
||||
def call_failed?(return_code)
|
||||
return_code == -1
|
||||
end
|
||||
end
|
||||
|
||||
# Represents a C enum.
|
||||
class Enum
|
||||
attr_reader :tag
|
||||
attr_reader :kv_map
|
||||
|
||||
def initialize(info, tag=nil)
|
||||
@tag = tag
|
||||
@kv_map = Hash.new
|
||||
|
||||
unless info.nil?
|
||||
last_cst = nil
|
||||
value = 0
|
||||
info.each do |i|
|
||||
case i
|
||||
when Symbol
|
||||
@kv_map[i] = value
|
||||
last_cst = i
|
||||
value += 1
|
||||
when Integer
|
||||
@kv_map[last_cst] = i
|
||||
value = i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def inspect
|
||||
"#<%s:0x%x %s=>%s>" % [self.class, self.object_id, @tag, @kv_map.inspect]
|
||||
end
|
||||
|
||||
def anonym?
|
||||
@tag.nil?
|
||||
end
|
||||
|
||||
def values
|
||||
@kv_map.values
|
||||
end
|
||||
|
||||
def symbols
|
||||
@kv_map.keys
|
||||
end
|
||||
|
||||
def [](sym)
|
||||
@kv_map[sym]
|
||||
end
|
||||
|
||||
def symbol(value)
|
||||
key, val = @kv_map.detect { |key, val| val == value }
|
||||
key
|
||||
end
|
||||
end
|
||||
|
||||
# It's a class to be compat with the ffi gem.
|
||||
class Type
|
||||
class Array
|
||||
def initialize(element_type, size, impl_class=nil)
|
||||
@element_type = element_type
|
||||
@size = size
|
||||
@implementation = impl_class
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
attr_reader :element_type
|
||||
attr_reader :size
|
||||
attr_reader :implementation
|
||||
end
|
||||
|
||||
class StructByValue
|
||||
def initialize(struct)
|
||||
@implementation = struct
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
attr_reader :implementation
|
||||
end
|
||||
|
||||
Struct = StructByValue
|
||||
|
||||
CHAR = TYPE_CHAR
|
||||
UCHAR = TYPE_UCHAR
|
||||
BOOL = TYPE_BOOL
|
||||
SHORT = TYPE_SHORT
|
||||
USHORT = TYPE_USHORT
|
||||
INT = TYPE_INT
|
||||
UINT = TYPE_UINT
|
||||
LONG = TYPE_LONG
|
||||
ULONG = TYPE_ULONG
|
||||
LL = TYPE_LL
|
||||
ULL = TYPE_ULL
|
||||
FLOAT = TYPE_FLOAT
|
||||
DOUBLE = TYPE_DOUBLE
|
||||
PTR = TYPE_PTR
|
||||
VOID = TYPE_VOID
|
||||
STRING = TYPE_STRING
|
||||
STRPTR = TYPE_STRPTR
|
||||
CHARARR = TYPE_CHARARR
|
||||
ENUM = TYPE_ENUM
|
||||
VARARGS = TYPE_VARARGS
|
||||
end
|
||||
|
||||
##
|
||||
# Namespace for holding platform-specific C constants.
|
||||
|
||||
module Platform
|
||||
def self.windows?
|
||||
Rubinius.windows?
|
||||
end
|
||||
|
||||
def self.mac?
|
||||
Rubinius.darwin?
|
||||
end
|
||||
|
||||
def self.unix?
|
||||
! windows?
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Represents a C struct as ruby class.
|
||||
|
||||
class Struct
|
||||
class InlineArray
|
||||
include Enumerable
|
||||
|
||||
def initialize(type, ptr)
|
||||
@pointer = ptr
|
||||
@type = type.element_type
|
||||
@size = type.size
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
attr_reader :size
|
||||
|
||||
def [](idx)
|
||||
if idx >= @size
|
||||
raise ArgumentError, "index out of range (#{idx} >= #{@size})"
|
||||
end
|
||||
|
||||
@pointer.get_at_offset(idx * @type, @type)
|
||||
end
|
||||
|
||||
def []=(idx, val)
|
||||
if idx >= @size
|
||||
raise ArgumentError, "index out of range (#{idx} >= #{@size})"
|
||||
end
|
||||
|
||||
@pointer.set_at_offset(idx * @type, @type, val)
|
||||
end
|
||||
|
||||
def each
|
||||
@size.times do |ele|
|
||||
yield @pointer.get_at_offset(ele * @type, @type)
|
||||
end
|
||||
end
|
||||
|
||||
def to_a
|
||||
ary = []
|
||||
each { |x| ary << x }
|
||||
ary
|
||||
end
|
||||
|
||||
def to_ptr
|
||||
@pointer
|
||||
end
|
||||
end
|
||||
|
||||
class InlineCharArray < InlineArray
|
||||
def to_s
|
||||
str = @pointer.read_string(@size)
|
||||
|
||||
# NULL clamp this, to be the same as the ffi gem.
|
||||
if idx = str.index("\0")
|
||||
return str[0, idx]
|
||||
end
|
||||
|
||||
return str
|
||||
end
|
||||
|
||||
alias_method :to_str, :to_s
|
||||
alias_method :inspect, :to_s
|
||||
end
|
||||
|
||||
def self.find_nested_parent
|
||||
return nil if self.name.nil?
|
||||
|
||||
path = self.name.split("::")
|
||||
path.pop # remove ourself
|
||||
|
||||
mod = Object
|
||||
|
||||
begin
|
||||
path.each { |c| mod = mod.const_get(c) }
|
||||
rescue NameError
|
||||
# bail.
|
||||
return nil
|
||||
end
|
||||
|
||||
mod.respond_to?(:find_type) ? mod : nil
|
||||
end
|
||||
|
||||
attr_reader :pointer
|
||||
|
||||
def self.layout(*spec)
|
||||
return @layout if spec.size == 0
|
||||
|
||||
# Pick up a enclosing FFI::Library
|
||||
@enclosing_module ||= find_nested_parent
|
||||
|
||||
cspec = Rubinius::LookupTable.new
|
||||
i = 0
|
||||
|
||||
@size = 0
|
||||
@members = []
|
||||
|
||||
while i < spec.size
|
||||
name = spec[i]
|
||||
@members << name
|
||||
|
||||
f = spec[i + 1]
|
||||
|
||||
type_code = nil
|
||||
|
||||
if f.kind_of? Array
|
||||
ary_type = f[0]
|
||||
ary_size = f[1]
|
||||
|
||||
if @enclosing_module
|
||||
type_code = @enclosing_module.find_type(ary_type)
|
||||
end
|
||||
|
||||
type_code ||= FFI.find_type(ary_type)
|
||||
type_size = FFI.type_size(type_code)
|
||||
|
||||
case type_code
|
||||
when TYPE_CHAR, TYPE_UCHAR
|
||||
klass = InlineCharArray
|
||||
else
|
||||
klass = InlineArray
|
||||
end
|
||||
|
||||
type = FFI::Type::Array.new(type_code, ary_size, klass)
|
||||
element_size = type_size * ary_size
|
||||
elsif f.kind_of?(Class) and (f < FFI::Struct || f < FFI::Union)
|
||||
type = FFI::Type::StructByValue.new(f)
|
||||
element_size = type_size = f.size
|
||||
else
|
||||
if @enclosing_module
|
||||
type_code = @enclosing_module.find_type(f) rescue nil
|
||||
end
|
||||
|
||||
type_code ||= FFI.find_type(f)
|
||||
|
||||
type = type_code
|
||||
element_size = type_size = FFI.type_size(type_code)
|
||||
end
|
||||
|
||||
offset = spec[i + 2]
|
||||
|
||||
if offset.kind_of?(Fixnum)
|
||||
i += 3
|
||||
else
|
||||
if self < FFI::Union
|
||||
offset = 0
|
||||
else
|
||||
offset = @size
|
||||
|
||||
mod = offset % type_size
|
||||
unless mod == 0
|
||||
# we need to align it.
|
||||
offset += (type_size - mod)
|
||||
end
|
||||
end
|
||||
|
||||
i += 2
|
||||
end
|
||||
|
||||
cspec[name] = [offset, type]
|
||||
ending = offset + element_size
|
||||
@size = ending if @size < ending
|
||||
end
|
||||
|
||||
@layout = cspec unless self == FFI::Struct
|
||||
|
||||
return cspec
|
||||
end
|
||||
|
||||
def self.config(base, *fields)
|
||||
@size = Rubinius::Config["#{base}.sizeof"]
|
||||
cspec = Rubinius::LookupTable.new
|
||||
|
||||
fields.each do |field|
|
||||
offset = Rubinius::Config["#{base}.#{field}.offset"]
|
||||
element_size = Rubinius::Config["#{base}.#{field}.size"]
|
||||
type = Rubinius::Config["#{base}.#{field}.type"]
|
||||
type = type ? type.to_sym : FFI.size_to_type(size)
|
||||
|
||||
type_code = FFI.find_type type
|
||||
|
||||
if :char_array == type
|
||||
type_code = FFI::Type::Array.new(FFI.find_type(:char), element_size, InlineCharArray)
|
||||
end
|
||||
|
||||
cspec[field] = [offset, type_code]
|
||||
ending = offset + element_size
|
||||
@size = ending if @size < ending
|
||||
end
|
||||
|
||||
@layout = cspec
|
||||
|
||||
return cspec
|
||||
end
|
||||
|
||||
def self.size
|
||||
@size
|
||||
end
|
||||
|
||||
def size
|
||||
self.class.size
|
||||
end
|
||||
|
||||
def self.offset_of(name)
|
||||
@layout[name].first
|
||||
end
|
||||
|
||||
def offset_of(name)
|
||||
@cspec[name].first
|
||||
end
|
||||
|
||||
def self.offsets
|
||||
members.map do |member|
|
||||
[member, @layout[member].first]
|
||||
end
|
||||
end
|
||||
|
||||
def offsets
|
||||
members.map do |member|
|
||||
[member, @cspec[member].first]
|
||||
end
|
||||
end
|
||||
|
||||
def self.members
|
||||
@members
|
||||
end
|
||||
|
||||
def members
|
||||
self.class.members
|
||||
end
|
||||
|
||||
def initialize(pointer=nil, *spec)
|
||||
@cspec = self.class.layout(*spec)
|
||||
|
||||
if pointer
|
||||
@pointer = pointer
|
||||
else
|
||||
@pointer = MemoryPointer.new size
|
||||
end
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def to_ptr
|
||||
@pointer
|
||||
end
|
||||
|
||||
def free
|
||||
@pointer.free
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
return false unless other.is_a?(self.class)
|
||||
@pointer == other.pointer
|
||||
end
|
||||
|
||||
def initialize_copy(ptr)
|
||||
@pointer = ptr.pointer.dup
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def []=(field, val)
|
||||
offset, type = @cspec[field]
|
||||
raise "Unknown field #{field}" unless offset
|
||||
|
||||
case type
|
||||
when Fixnum
|
||||
@pointer.set_at_offset(offset, type, val)
|
||||
when FFI::Type::Array
|
||||
if type.implementation == InlineCharArray
|
||||
(@pointer + offset).write_string StringValue(val), type.size
|
||||
return val
|
||||
end
|
||||
|
||||
raise TypeError, "Unable to set inline array"
|
||||
when Rubinius::NativeFunction
|
||||
@pointer.set_at_offset(offset, FFI::TYPE_PTR, val)
|
||||
else
|
||||
@pointer.set_at_offset(offset, type, val)
|
||||
end
|
||||
|
||||
return val
|
||||
end
|
||||
|
||||
def [](field)
|
||||
offset, type = @cspec[field]
|
||||
raise "Unknown field #{field}" unless offset
|
||||
|
||||
case type
|
||||
when FFI::TYPE_CHARARR
|
||||
(@pointer + offset).read_string
|
||||
when Fixnum
|
||||
@pointer.get_at_offset(offset, type)
|
||||
when FFI::Type::Array
|
||||
type.implementation.new(type, @pointer + offset)
|
||||
when FFI::Type::StructByValue
|
||||
type.implementation.new(@pointer + offset)
|
||||
when Rubinius::NativeFunction
|
||||
ptr = @pointer.get_at_offset(offset, FFI::TYPE_PTR)
|
||||
if ptr
|
||||
FFI::Function.new(type.return_type, type.argument_types, ptr)
|
||||
else
|
||||
nil
|
||||
end
|
||||
else
|
||||
@pointer.get_at_offset(offset, type)
|
||||
end
|
||||
end
|
||||
|
||||
def values
|
||||
members.map { |m| self[m] }
|
||||
end
|
||||
|
||||
def null?
|
||||
@pointer == FFI::Pointer::NULL
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Represents a C union as ruby class.
|
||||
|
||||
class Union < Struct
|
||||
end
|
||||
end
|
||||
end
|
||||
73
gxg/core/fiber.rb
Normal file
73
gxg/core/fiber.rb
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
class Fiber
|
||||
attr_reader :stack_size
|
||||
attr_reader :thread_name
|
||||
attr_reader :fiber_id
|
||||
attr_reader :source
|
||||
|
||||
def self.new(**kw, &block)
|
||||
if block.nil?
|
||||
raise ArgumentError, "Fiber.new requires a block"
|
||||
end
|
||||
|
||||
stack_size = Rubinius::Type.try_convert kw[:stack_size], Fixnum, :to_int
|
||||
|
||||
Rubinius.invoke_primitive :fiber_new, stack_size, block, self
|
||||
end
|
||||
|
||||
def self.current
|
||||
Rubinius.primitive :fiber_s_current
|
||||
raise PrimitiveFailure, "Fiber.current primitive failed"
|
||||
end
|
||||
|
||||
def self.yield(*args)
|
||||
Rubinius.primitive :fiber_s_yield
|
||||
raise PrimitiveFailure, "Fiber.yield primitive failed"
|
||||
end
|
||||
|
||||
def self.list
|
||||
Rubinius.primitive :fiber_s_list
|
||||
raise PrimitiveFailure, "Fiber.list primitive failed"
|
||||
end
|
||||
|
||||
def self.main
|
||||
Rubinius.primitive :fiber_s_main
|
||||
raise PrimitiveFailure, "Fiber.main primitive failed"
|
||||
end
|
||||
|
||||
def self.count
|
||||
Rubinius.primitive :fiber_s_count
|
||||
raise PrimitiveFailure, "Fiber.count primitive failed"
|
||||
end
|
||||
|
||||
def status
|
||||
Rubinius.primitive :fiber_status
|
||||
raise PrimitiveFailure, "Fiber#status primitive failed"
|
||||
end
|
||||
|
||||
def resume(*args)
|
||||
Rubinius.primitive :fiber_resume
|
||||
raise PrimitiveFailure, "Fiber#resume primitive failed"
|
||||
end
|
||||
|
||||
def transfer(*args)
|
||||
Rubinius.primitive :fiber_transfer
|
||||
raise PrimitiveFailure, "Fiber#transfer primitive failed"
|
||||
end
|
||||
|
||||
def dispose
|
||||
Rubinius.primitive :fiber_dispose
|
||||
raise PrimitiveFailure, "Fiber#dispose primitive failed"
|
||||
end
|
||||
|
||||
def alive?
|
||||
status != "dead"
|
||||
end
|
||||
|
||||
def inspect
|
||||
str = "#<#{self.class}:0x#{object_id.to_s(16)} thread_name=#{@thread_name} fiber_id=#{@fiber_id} status=#{status}"
|
||||
str << " source=#{@source}" if @source
|
||||
str << ">"
|
||||
end
|
||||
|
||||
alias_method :to_s, :inspect
|
||||
end
|
||||
1289
gxg/core/file.rb
Normal file
1289
gxg/core/file.rb
Normal file
File diff suppressed because it is too large
Load diff
119
gxg/core/file_test.rb
Normal file
119
gxg/core/file_test.rb
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
module FileTest
|
||||
def blockdev?(path)
|
||||
File.blockdev? path
|
||||
end
|
||||
module_function :blockdev?
|
||||
|
||||
def chardev?(path)
|
||||
File.chardev? path
|
||||
end
|
||||
module_function :chardev?
|
||||
|
||||
def directory?(path)
|
||||
File.directory? path
|
||||
end
|
||||
module_function :directory?
|
||||
|
||||
def executable?(path)
|
||||
File.executable? path
|
||||
end
|
||||
module_function :executable?
|
||||
|
||||
def executable_real?(path)
|
||||
File.executable_real? path
|
||||
end
|
||||
module_function :executable_real?
|
||||
|
||||
def exist?(path)
|
||||
File.exist? path
|
||||
end
|
||||
module_function :exist?
|
||||
|
||||
alias_method :exists?, :exist?
|
||||
module_function :exists?
|
||||
|
||||
def file?(path)
|
||||
File.file? path
|
||||
end
|
||||
module_function :file?
|
||||
|
||||
def grpowned?(path)
|
||||
File.grpowned? path
|
||||
end
|
||||
module_function :grpowned?
|
||||
|
||||
def identical?(a, b)
|
||||
File.identical? a, b
|
||||
end
|
||||
module_function :identical?
|
||||
|
||||
def owned?(path)
|
||||
File.owned? path
|
||||
end
|
||||
module_function :owned?
|
||||
|
||||
def pipe?(path)
|
||||
File.pipe? path
|
||||
end
|
||||
module_function :pipe?
|
||||
|
||||
def readable?(path)
|
||||
File.readable? path
|
||||
end
|
||||
module_function :readable?
|
||||
|
||||
def readable_real?(path)
|
||||
File.readable_real? path
|
||||
end
|
||||
module_function :readable_real?
|
||||
|
||||
def setgid?(path)
|
||||
File.setgid? path
|
||||
end
|
||||
module_function :setgid?
|
||||
|
||||
def setuid?(path)
|
||||
File.setuid? path
|
||||
end
|
||||
module_function :setuid?
|
||||
|
||||
def size(path)
|
||||
File.size path
|
||||
end
|
||||
module_function :size
|
||||
|
||||
def size?(path)
|
||||
File.size? path
|
||||
end
|
||||
module_function :size?
|
||||
|
||||
def socket?(path)
|
||||
File.socket? path
|
||||
end
|
||||
module_function :socket?
|
||||
|
||||
def sticky?(path)
|
||||
File.sticky? path
|
||||
end
|
||||
module_function :sticky?
|
||||
|
||||
def symlink?(path)
|
||||
File.symlink? path
|
||||
end
|
||||
module_function :symlink?
|
||||
|
||||
def writable?(path)
|
||||
File.writable? path
|
||||
end
|
||||
module_function :writable?
|
||||
|
||||
def writable_real?(path)
|
||||
File.writable_real? path
|
||||
end
|
||||
module_function :writable_real?
|
||||
|
||||
def zero?(path)
|
||||
File.zero? path
|
||||
end
|
||||
module_function :zero?
|
||||
end
|
||||
602
gxg/core/float.rb
Normal file
602
gxg/core/float.rb
Normal file
|
|
@ -0,0 +1,602 @@
|
|||
class Float < Numeric
|
||||
def **(other)
|
||||
Rubinius.primitive :float_pow
|
||||
|
||||
if other.is_a?(Float) && self < 0 && other != other.round
|
||||
return Complex.new(self, 0) ** other
|
||||
end
|
||||
|
||||
b, a = math_coerce other
|
||||
a ** b
|
||||
end
|
||||
|
||||
def imaginary
|
||||
0
|
||||
end
|
||||
|
||||
def numerator
|
||||
if nan?
|
||||
NAN
|
||||
elsif infinite? == 1
|
||||
INFINITY
|
||||
elsif infinite? == -1
|
||||
-INFINITY
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def denominator
|
||||
if infinite? || nan?
|
||||
1
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def to_r
|
||||
f, e = Math.frexp self
|
||||
f = Math.ldexp(f, MANT_DIG).to_i
|
||||
e -= MANT_DIG
|
||||
|
||||
(f * (RADIX ** e)).to_r
|
||||
end
|
||||
|
||||
def arg
|
||||
if nan?
|
||||
self
|
||||
elsif signbit?
|
||||
Math::PI
|
||||
else
|
||||
0
|
||||
end
|
||||
end
|
||||
alias_method :angle, :arg
|
||||
alias_method :phase, :arg
|
||||
|
||||
def rationalize(eps=undefined)
|
||||
if undefined.equal?(eps)
|
||||
f, n = Math.frexp self
|
||||
f = Math.ldexp(f, Float::MANT_DIG).to_i
|
||||
n -= Float::MANT_DIG
|
||||
|
||||
Rational.new(2 * f, 1 << (1 - n)).rationalize(Rational.new(1, 1 << (1 - n)))
|
||||
else
|
||||
to_r.rationalize(eps)
|
||||
end
|
||||
end
|
||||
|
||||
def round(ndigits=0)
|
||||
ndigits = Rubinius::Type.coerce_to(ndigits, Integer, :to_int)
|
||||
|
||||
if ndigits == 0
|
||||
return Rubinius.invoke_primitive :float_round, self
|
||||
elsif ndigits < 0
|
||||
return truncate.round ndigits
|
||||
end
|
||||
|
||||
return self if infinite? or nan?
|
||||
|
||||
_, exp = Math.frexp(self)
|
||||
|
||||
if ndigits >= (Float::DIG + 2) - (exp > 0 ? exp / 4 : exp / 3 - 1)
|
||||
return self
|
||||
end
|
||||
|
||||
if ndigits < -(exp > 0 ? exp / 3 + 1 : exp / 4)
|
||||
return 0.0
|
||||
end
|
||||
|
||||
f = 10**ndigits
|
||||
Rubinius.invoke_primitive(:float_round, self * f) / f.to_f
|
||||
end
|
||||
def coerce(other)
|
||||
return [other, self] if other.kind_of? Float
|
||||
[Float(other), self]
|
||||
end
|
||||
|
||||
def -@
|
||||
Rubinius.primitive :float_neg
|
||||
raise PrimitiveFailure, "Float#-@ primitive failed"
|
||||
end
|
||||
|
||||
def abs
|
||||
FFI::Platform::Math.fabs(self)
|
||||
end
|
||||
|
||||
alias_method :magnitude, :abs
|
||||
|
||||
def signbit?
|
||||
Rubinius.primitive :float_signbit_p
|
||||
raise PrimitiveFailure, "Float#signbit? primitive failed"
|
||||
end
|
||||
|
||||
def +(other)
|
||||
Rubinius.asm do
|
||||
flt = new_label
|
||||
done = new_label
|
||||
|
||||
r0 = new_register
|
||||
r1 = new_register
|
||||
r2 = new_register
|
||||
r3 = new_register
|
||||
|
||||
r_load_m_binops r0, r1
|
||||
|
||||
n_promote r2, r0, r1
|
||||
|
||||
r_load_1 r3
|
||||
n_ieq r3, r3, r2
|
||||
b_if r3, flt
|
||||
goto done
|
||||
|
||||
flt.set!
|
||||
n_dadd r0, r0, r1
|
||||
r_store_float r0, r0
|
||||
r_ret r0
|
||||
|
||||
done.set!
|
||||
|
||||
# TODO: teach the bytecode compiler better
|
||||
push_true
|
||||
end
|
||||
|
||||
b, a = math_coerce other
|
||||
a + b
|
||||
end
|
||||
|
||||
def -(other)
|
||||
Rubinius.asm do
|
||||
flt = new_label
|
||||
done = new_label
|
||||
|
||||
r0 = new_register
|
||||
r1 = new_register
|
||||
r2 = new_register
|
||||
r3 = new_register
|
||||
|
||||
r_load_m_binops r0, r1
|
||||
|
||||
n_promote r2, r0, r1
|
||||
|
||||
r_load_1 r3
|
||||
n_ieq r3, r3, r2
|
||||
b_if r3, flt
|
||||
goto done
|
||||
|
||||
flt.set!
|
||||
n_dsub r0, r0, r1
|
||||
r_store_float r0, r0
|
||||
r_ret r0
|
||||
|
||||
done.set!
|
||||
|
||||
# TODO: teach the bytecode compiler better
|
||||
push_true
|
||||
end
|
||||
|
||||
b, a = math_coerce other
|
||||
a - b
|
||||
end
|
||||
|
||||
def *(other)
|
||||
Rubinius.asm do
|
||||
flt = new_label
|
||||
done = new_label
|
||||
|
||||
r0 = new_register
|
||||
r1 = new_register
|
||||
r2 = new_register
|
||||
r3 = new_register
|
||||
|
||||
r_load_m_binops r0, r1
|
||||
|
||||
n_promote r2, r0, r1
|
||||
|
||||
r_load_1 r3
|
||||
n_ieq r3, r3, r2
|
||||
b_if r3, flt
|
||||
goto done
|
||||
|
||||
flt.set!
|
||||
n_dmul r0, r0, r1
|
||||
r_store_float r0, r0
|
||||
r_ret r0
|
||||
|
||||
done.set!
|
||||
|
||||
# TODO: teach the bytecode compiler better
|
||||
push_true
|
||||
end
|
||||
|
||||
b, a = math_coerce other
|
||||
a * b
|
||||
end
|
||||
|
||||
def /(other)
|
||||
Rubinius.asm do
|
||||
flt = new_label
|
||||
done = new_label
|
||||
|
||||
r0 = new_register
|
||||
r1 = new_register
|
||||
r2 = new_register
|
||||
r3 = new_register
|
||||
|
||||
r_load_m_binops r0, r1
|
||||
|
||||
n_promote r2, r0, r1
|
||||
|
||||
r_load_1 r3
|
||||
n_ieq r3, r3, r2
|
||||
b_if r3, flt
|
||||
goto done
|
||||
|
||||
flt.set!
|
||||
n_ddiv r0, r0, r1
|
||||
r_store_float r0, r0
|
||||
r_ret r0
|
||||
|
||||
done.set!
|
||||
|
||||
# TODO: teach the bytecode compiler better
|
||||
push_true
|
||||
end
|
||||
|
||||
redo_coerced :/, other
|
||||
end
|
||||
|
||||
alias_method :divide, :/
|
||||
alias_method :quo, :/
|
||||
alias_method :fdiv, :/
|
||||
|
||||
INFINITY = 1.0 / 0.0
|
||||
NAN = 0.0 / 0.0
|
||||
|
||||
def divmod(other)
|
||||
Rubinius.primitive :float_divmod
|
||||
b, a = math_coerce other
|
||||
a.divmod b
|
||||
end
|
||||
|
||||
def %(other)
|
||||
return 0 / 0.to_f if other == 0
|
||||
Rubinius.primitive :float_mod
|
||||
b, a = math_coerce other
|
||||
a % b
|
||||
end
|
||||
|
||||
alias_method :modulo, :%
|
||||
|
||||
def <(other)
|
||||
Rubinius.asm do
|
||||
done = new_label
|
||||
|
||||
r0 = new_register
|
||||
r1 = new_register
|
||||
r2 = new_register
|
||||
r3 = new_register
|
||||
|
||||
r_load_m_binops r0, r1
|
||||
|
||||
n_promote r2, r0, r1
|
||||
|
||||
r_load_0 r3
|
||||
n_ieq r3, r3, r2
|
||||
b_if r3, done
|
||||
|
||||
n_dlt r0, r0, r1
|
||||
r_load_bool r0, r0
|
||||
r_ret r0
|
||||
|
||||
done.set!
|
||||
|
||||
# TODO: teach the bytecode compiler better
|
||||
push_true
|
||||
end
|
||||
|
||||
b, a = math_coerce other, :compare_error
|
||||
a < b
|
||||
end
|
||||
|
||||
def <=(other)
|
||||
Rubinius.asm do
|
||||
done = new_label
|
||||
|
||||
r0 = new_register
|
||||
r1 = new_register
|
||||
r2 = new_register
|
||||
r3 = new_register
|
||||
|
||||
r_load_m_binops r0, r1
|
||||
|
||||
n_promote r2, r0, r1
|
||||
|
||||
r_load_0 r3
|
||||
n_ieq r3, r3, r2
|
||||
b_if r3, done
|
||||
|
||||
n_dle r0, r0, r1
|
||||
r_load_bool r0, r0
|
||||
r_ret r0
|
||||
|
||||
done.set!
|
||||
|
||||
# TODO: teach the bytecode compiler better
|
||||
push_true
|
||||
end
|
||||
|
||||
b, a = math_coerce other, :compare_error
|
||||
a <= b
|
||||
end
|
||||
|
||||
def >(other)
|
||||
Rubinius.asm do
|
||||
done = new_label
|
||||
|
||||
r0 = new_register
|
||||
r1 = new_register
|
||||
r2 = new_register
|
||||
r3 = new_register
|
||||
|
||||
r_load_m_binops r0, r1
|
||||
|
||||
n_promote r2, r0, r1
|
||||
|
||||
r_load_0 r3
|
||||
n_ieq r3, r3, r2
|
||||
b_if r3, done
|
||||
|
||||
n_dgt r0, r0, r1
|
||||
r_load_bool r0, r0
|
||||
r_ret r0
|
||||
|
||||
done.set!
|
||||
|
||||
# TODO: teach the bytecode compiler better
|
||||
push_true
|
||||
end
|
||||
|
||||
b, a = math_coerce other, :compare_error
|
||||
a > b
|
||||
end
|
||||
|
||||
def >=(other)
|
||||
Rubinius.asm do
|
||||
done = new_label
|
||||
|
||||
r0 = new_register
|
||||
r1 = new_register
|
||||
r2 = new_register
|
||||
r3 = new_register
|
||||
|
||||
r_load_m_binops r0, r1
|
||||
|
||||
n_promote r2, r0, r1
|
||||
|
||||
r_load_0 r3
|
||||
n_ieq r3, r3, r2
|
||||
b_if r3, done
|
||||
|
||||
n_dge r0, r0, r1
|
||||
r_load_bool r0, r0
|
||||
r_ret r0
|
||||
|
||||
done.set!
|
||||
|
||||
# TODO: teach the bytecode compiler better
|
||||
push_true
|
||||
end
|
||||
|
||||
b, a = math_coerce other, :compare_error
|
||||
a >= b
|
||||
end
|
||||
|
||||
def <=>(other)
|
||||
Rubinius.primitive :float_compare
|
||||
b, a = math_coerce other, :compare_error
|
||||
a <=> b
|
||||
rescue ArgumentError
|
||||
nil
|
||||
end
|
||||
|
||||
def !=(other)
|
||||
Rubinius.asm do
|
||||
done = new_label
|
||||
|
||||
r0 = new_register
|
||||
r1 = new_register
|
||||
r2 = new_register
|
||||
r3 = new_register
|
||||
|
||||
r_load_m_binops r0, r1
|
||||
|
||||
n_promote r2, r0, r1
|
||||
|
||||
r_load_0 r3
|
||||
n_ieq r3, r3, r2
|
||||
b_if r3, done
|
||||
|
||||
n_dne r0, r0, r1
|
||||
r_load_bool r0, r0
|
||||
r_ret r0
|
||||
|
||||
done.set!
|
||||
|
||||
# TODO: teach the bytecode compiler better
|
||||
push_true
|
||||
end
|
||||
|
||||
b, a = math_coerce(other)
|
||||
a != b ? true : false
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
Rubinius.asm do
|
||||
done = new_label
|
||||
|
||||
r0 = new_register
|
||||
r1 = new_register
|
||||
r2 = new_register
|
||||
r3 = new_register
|
||||
|
||||
r_load_m_binops r0, r1
|
||||
|
||||
n_promote r2, r0, r1
|
||||
|
||||
r_load_0 r3
|
||||
n_ieq r3, r3, r2
|
||||
b_if r3, done
|
||||
|
||||
n_deq r0, r0, r1
|
||||
r_load_bool r0, r0
|
||||
r_ret r0
|
||||
|
||||
done.set!
|
||||
|
||||
# TODO: teach the bytecode compiler better
|
||||
push_true
|
||||
end
|
||||
|
||||
begin
|
||||
b, a = math_coerce(other)
|
||||
a == b ? true : false
|
||||
rescue TypeError
|
||||
return other == self
|
||||
end
|
||||
end
|
||||
|
||||
def eql?(other)
|
||||
Rubinius.primitive :float_eql
|
||||
false
|
||||
end
|
||||
|
||||
def nan?
|
||||
Rubinius.asm do
|
||||
r0 = new_register
|
||||
r1 = new_register
|
||||
|
||||
r_load_self r0
|
||||
r_load_float r0, r0
|
||||
n_dnan r1, r0
|
||||
r_load_bool r0, r1
|
||||
r_ret r0
|
||||
|
||||
# TODO: teach the bytecode compiler better
|
||||
push_true
|
||||
end
|
||||
end
|
||||
|
||||
def infinite?
|
||||
Rubinius.asm do
|
||||
no = new_label
|
||||
|
||||
r0 = new_register
|
||||
r1 = new_register
|
||||
r2 = new_register
|
||||
|
||||
r_load_self r0
|
||||
r_load_float r0, r0
|
||||
n_dinf r1, r0
|
||||
|
||||
r_load_0 r0
|
||||
n_ieq r2, r1, r0
|
||||
b_if r2, no
|
||||
|
||||
r_store_int r0, r1
|
||||
r_ret r0
|
||||
|
||||
no.set!
|
||||
r_load_nil r0, 0
|
||||
r_ret r0
|
||||
|
||||
# TODO: teach the bytecode compiler better
|
||||
push_true
|
||||
end
|
||||
end
|
||||
|
||||
def finite?
|
||||
not (nan? or infinite?)
|
||||
end
|
||||
|
||||
def to_f
|
||||
self
|
||||
end
|
||||
|
||||
def to_i
|
||||
Rubinius.primitive :float_to_i
|
||||
raise PrimitiveFailure, "Float#to_i primitive failed"
|
||||
end
|
||||
|
||||
alias_method :to_int, :to_i
|
||||
alias_method :truncate, :to_i
|
||||
|
||||
def to_s
|
||||
to_s_minimal
|
||||
end
|
||||
|
||||
alias_method :inspect, :to_s
|
||||
|
||||
def to_s_minimal
|
||||
Rubinius.primitive :float_to_s_minimal
|
||||
raise PrimitiveFailure, "Float#to_s_minimal primitive failed: output exceeds buffer size"
|
||||
end
|
||||
|
||||
def to_s_formatted(fmt)
|
||||
Rubinius.primitive :float_to_s_formatted
|
||||
raise PrimitiveFailure, "Float#to_s_formatted primitive failed: output exceeds buffer size"
|
||||
end
|
||||
private :to_s_formatted
|
||||
|
||||
def dtoa
|
||||
Rubinius.primitive :float_dtoa
|
||||
raise PrimitiveFailure, "Fload#dtoa primitive failed"
|
||||
end
|
||||
|
||||
def to_packed(size)
|
||||
Rubinius.primitive :float_to_packed
|
||||
raise PrimitiveFailure, "Float#to_packed primitive failed"
|
||||
end
|
||||
|
||||
def ceil
|
||||
int = to_i()
|
||||
|
||||
return int if self == int or self < 0
|
||||
return int + 1
|
||||
end
|
||||
|
||||
def floor
|
||||
int = to_i()
|
||||
|
||||
return int if self > 0 or self == int
|
||||
return int - 1
|
||||
end
|
||||
|
||||
def next_float
|
||||
return NAN if self.nan?
|
||||
return -MAX if self == -INFINITY
|
||||
return INFINITY if self == MAX
|
||||
return Math.ldexp(0.5, MIN_EXP - MANT_DIG + 1) if self.zero?
|
||||
|
||||
frac, exp = Math.frexp self
|
||||
|
||||
if frac == -0.5
|
||||
frac *= 2
|
||||
exp -= 1
|
||||
end
|
||||
|
||||
smallest_frac = EPSILON / 2
|
||||
smallest_frac = Math.ldexp(smallest_frac, MIN_EXP - exp) if exp < MIN_EXP
|
||||
|
||||
result_frac = frac + smallest_frac
|
||||
|
||||
return -0.0 if result_frac.zero? && frac < 0
|
||||
return 0.0 if result_frac.zero? && frac > 0
|
||||
|
||||
return Math.ldexp result_frac, exp
|
||||
end
|
||||
|
||||
def prev_float
|
||||
return -(-self).next_float
|
||||
end
|
||||
end
|
||||
33
gxg/core/fsevent.rb
Normal file
33
gxg/core/fsevent.rb
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
module Rubinius
|
||||
class FSEvent
|
||||
attr_accessor :fileno
|
||||
attr_accessor :path
|
||||
|
||||
def self.allocate
|
||||
Rubinius.primitive :fsevent_allocate
|
||||
raise PrimitiveFailure, "Rubinius::FSEvent.allocate failed"
|
||||
end
|
||||
|
||||
def watch_file(fd, path)
|
||||
Rubinius.primitive :fsevent_watch_file
|
||||
raise PrimitiveFailure, "Rubinius::FSEvent#watch_file failed"
|
||||
end
|
||||
|
||||
def wait_for_event
|
||||
Rubinius.primitive :fsevent_wait_for_event
|
||||
raise PrimitiveFailure, "Rubinius::FSEvent#wait_for_event failed"
|
||||
end
|
||||
|
||||
def inspect
|
||||
str = "#<#{self.class}:0x#{self.__id__.to_s(16)}"
|
||||
|
||||
str << " fileno=#{fileno}" if fileno
|
||||
str << " path=#{path}" if path
|
||||
str << ">"
|
||||
|
||||
Rubinius::Type.infect str, self
|
||||
|
||||
str
|
||||
end
|
||||
end
|
||||
end
|
||||
137
gxg/core/gc.rb
Normal file
137
gxg/core/gc.rb
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
module GC
|
||||
def self.start
|
||||
run(false)
|
||||
end
|
||||
|
||||
def self.run(force)
|
||||
Rubinius.primitive :vm_gc_start
|
||||
raise PrimitiveFailure, "GC.run primitive failed"
|
||||
end
|
||||
|
||||
# Totally fake.
|
||||
def self.stress
|
||||
@stress_level ||= false
|
||||
end
|
||||
|
||||
# Totally fake.
|
||||
def self.stress=(flag)
|
||||
@stress_level = !!flag
|
||||
end
|
||||
|
||||
# Totally fake.
|
||||
@enabled = true
|
||||
|
||||
def self.enable
|
||||
# We don't support disable, so sure! enabled!
|
||||
ret = !@enabled
|
||||
@enabled = true
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
# Totally fake.
|
||||
def self.disable
|
||||
# Treat this like a request that we don't honor.
|
||||
ret = !@enabled
|
||||
@enabled = false
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
def garbage_collect
|
||||
GC.start
|
||||
end
|
||||
|
||||
def self.count
|
||||
if data = stat
|
||||
data[:"gc.young.count"] + data[:"gc.immix.count"]
|
||||
else
|
||||
0
|
||||
end
|
||||
end
|
||||
|
||||
def self.time
|
||||
if data = stat
|
||||
data[:"gc.young.ms"] +
|
||||
data[:"gc.immix.stop.ms"] +
|
||||
data[:"gc.large.sweep.us"] * 1_000
|
||||
else
|
||||
0
|
||||
end
|
||||
end
|
||||
|
||||
def self.stat
|
||||
if Rubinius::Metrics.enabled?
|
||||
return Rubinius::Metrics.data.to_hash
|
||||
end
|
||||
end
|
||||
|
||||
module Profiler
|
||||
@enabled = true
|
||||
@since = 0
|
||||
|
||||
def self.clear
|
||||
@since = GC.time
|
||||
nil
|
||||
end
|
||||
|
||||
def self.disable
|
||||
# Treat this like a request that we don't honor.
|
||||
ret = !@enabled
|
||||
@enabled = false
|
||||
ret
|
||||
end
|
||||
|
||||
def self.enable
|
||||
# We don't support disable, so sure! enabled!
|
||||
ret = !@enabled
|
||||
@enabled = true
|
||||
ret
|
||||
end
|
||||
|
||||
def self.enabled?
|
||||
@enabled
|
||||
end
|
||||
|
||||
def self.report(out = $stdout)
|
||||
out.write result
|
||||
end
|
||||
|
||||
def self.result
|
||||
unless Rubinius::Metrics.enabled?
|
||||
return "Rubinius::Metrics is disabled. GC::Profiler results are unavailable"
|
||||
end
|
||||
|
||||
stats = GC.stat
|
||||
|
||||
out = <<-OUT
|
||||
Complete process runtime statistics
|
||||
===================================
|
||||
|
||||
Collections
|
||||
Count Total time / concurrent (ms)
|
||||
Young #{sprintf("% 22d", stats[:'gc.young.count'])} #{sprintf("% 16d ", stats[:'gc.young.ms'])}
|
||||
Full #{sprintf("% 22d", stats[:'gc.immix.count'])} #{sprintf("% 16d / % 10d", stats[:'gc.immix.stop.ms'], stats[:'gc.immix.concurrent.ms'])}
|
||||
|
||||
Allocation
|
||||
Objects allocated Bytes allocated
|
||||
Young #{sprintf("% 22d", stats[:'memory.young.objects'])} #{sprintf("% 22d", stats[:'memory.young.bytes'])}
|
||||
Promoted#{sprintf("% 22d", stats[:'memory.promoted.objects'])} #{sprintf("% 22d", stats[:'memory.promoted.bytes'])}
|
||||
Mature #{sprintf("% 22d", stats[:'memory.immix.objects'])} #{sprintf("% 22d", stats[:'memory.immix.bytes'])}
|
||||
|
||||
|
||||
Usage
|
||||
Bytes used
|
||||
Young #{sprintf("% 22d", stats[:'memory.young.bytes'])}
|
||||
Mature #{sprintf("% 22d", stats[:'memory.immix.bytes'])}
|
||||
Large #{sprintf("% 22d", stats[:'memory.large.bytes'])}
|
||||
Code #{sprintf("% 22d", stats[:'memory.code.bytes'])}
|
||||
Symbols #{sprintf("% 22d", stats[:'memory.symbols.bytes'])}
|
||||
OUT
|
||||
end
|
||||
|
||||
def self.total_time
|
||||
(GC.time - @since) / 1000.0
|
||||
end
|
||||
end
|
||||
end
|
||||
159
gxg/core/global.rb
Normal file
159
gxg/core/global.rb
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
##
|
||||
# Stores global variables and global variable aliases.
|
||||
|
||||
module Rubinius
|
||||
class GlobalVariables
|
||||
def initialize
|
||||
load_path = CoreDB.load_path
|
||||
loaded_features = CoreDB.loaded_features
|
||||
|
||||
@internal = LookupTable.new
|
||||
|
||||
@internal[:$;] = nil
|
||||
@internal[:$/] = "\n" # Input record separator
|
||||
@internal[:$\] = nil # Output record separator
|
||||
@internal[:$<] = ARGF
|
||||
@internal[:$:] = load_path
|
||||
@internal[:$-I] = load_path
|
||||
@internal[:$"] = loaded_features
|
||||
@internal[:$,] = nil # Output field separator
|
||||
@internal[:$.] = 0
|
||||
@internal[:$=] = false # ignore case, whatever that is
|
||||
@internal[:$0] = nil
|
||||
@internal[:$CONSOLE] = STDOUT
|
||||
@internal[:$DEBUG] = false
|
||||
@internal[:$LOADED_FEATURES] = loaded_features
|
||||
@internal[:$LOAD_PATH] = load_path
|
||||
@internal[:$SAFE] = 0
|
||||
|
||||
@internal[:$stderr] = STDERR
|
||||
@internal[:$stdin] = STDIN
|
||||
@internal[:$stdout] = STDOUT
|
||||
|
||||
@alias = LookupTable.new
|
||||
@hooks = LookupTable.new
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def key?(key)
|
||||
@internal.key?(key) || @alias.key?(key)
|
||||
end
|
||||
|
||||
def variables
|
||||
@internal.keys + @alias.keys
|
||||
end
|
||||
|
||||
def [](key)
|
||||
if @internal.key? key
|
||||
@internal[key]
|
||||
elsif @hooks.key? key
|
||||
@hooks[key][0].call(key)
|
||||
elsif @alias.key? key
|
||||
self[@alias[key]]
|
||||
end
|
||||
end
|
||||
|
||||
def []=(key, data)
|
||||
if alias_key = @alias[key]
|
||||
self[alias_key] = data
|
||||
elsif @hooks.key? key
|
||||
hook = @hooks[key]
|
||||
val = hook[1].call(key, data)
|
||||
if hook[2]
|
||||
@internal[key] = val
|
||||
end
|
||||
val
|
||||
else
|
||||
@internal[key] = data
|
||||
end
|
||||
end
|
||||
|
||||
# Respects internal and alias, but not hooks. Useful to bypass
|
||||
# normal read only checks.
|
||||
def set!(key, data)
|
||||
if alias_key = @alias[key]
|
||||
set! alias_key, data
|
||||
else
|
||||
@internal[key] = data
|
||||
end
|
||||
end
|
||||
|
||||
def add_alias(current_name, alias_name)
|
||||
current_name = current_name.to_sym
|
||||
alias_name = alias_name.to_sym
|
||||
|
||||
if @internal.key? alias_name
|
||||
@internal.delete alias_name
|
||||
end
|
||||
|
||||
if @hooks.key? alias_name
|
||||
@hooks.delete alias_name
|
||||
end
|
||||
|
||||
@alias[alias_name] = current_name
|
||||
end
|
||||
|
||||
def illegal_set(*args)
|
||||
raise NameError, "unable to set global"
|
||||
end
|
||||
|
||||
def set_hook(var, getter=nil, setter=nil, &block)
|
||||
if block
|
||||
if getter or setter
|
||||
raise ArgumentError, "duplicate procs provided with block"
|
||||
end
|
||||
|
||||
@hooks[var] = [block, method(:illegal_set)]
|
||||
else
|
||||
set_internal = false
|
||||
set_internal = true if getter == :[]
|
||||
|
||||
if !getter
|
||||
getter = method(:nil_return)
|
||||
elsif getter.kind_of? Symbol
|
||||
getter = method(getter)
|
||||
end
|
||||
|
||||
unless getter.respond_to?(:call)
|
||||
raise ArgumentError, "getter must respond to call"
|
||||
end
|
||||
|
||||
if !setter
|
||||
setter = method(:illegal_set)
|
||||
elsif setter.kind_of? Symbol
|
||||
setter = method(setter)
|
||||
end
|
||||
|
||||
unless setter.respond_to?(:call)
|
||||
raise ArgumentError, "setter must respond to call"
|
||||
end
|
||||
|
||||
@hooks[var] = [getter, setter, set_internal]
|
||||
end
|
||||
end
|
||||
|
||||
def remove_hook(var)
|
||||
@hooks.delete(var)
|
||||
end
|
||||
|
||||
def nil_return
|
||||
nil
|
||||
end
|
||||
|
||||
def set_filter(var, block)
|
||||
@hooks[var] = [method(:nil_return), block, true]
|
||||
end
|
||||
|
||||
def raise_readonly(key, data)
|
||||
raise NameError, "#{key} is a read-only global variable"
|
||||
end
|
||||
|
||||
def read_only(*vars)
|
||||
mo = method(:raise_readonly)
|
||||
vars.each do |v|
|
||||
set_filter v, mo
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
1004
gxg/core/hash.rb
Normal file
1004
gxg/core/hash.rb
Normal file
File diff suppressed because it is too large
Load diff
23
gxg/core/hook.rb
Normal file
23
gxg/core/hook.rb
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
module Rubinius
|
||||
class Hook
|
||||
def initialize
|
||||
@hooks = []
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def add(obj)
|
||||
@hooks << obj
|
||||
end
|
||||
|
||||
def remove(obj)
|
||||
@hooks.delete(obj)
|
||||
end
|
||||
|
||||
def trigger!(*args)
|
||||
@hooks.each do |obj|
|
||||
obj.call(*args)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
307
gxg/core/identity_map.rb
Normal file
307
gxg/core/identity_map.rb
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
module Rubinius
|
||||
|
||||
# IdentityMap is customized for uniquely storing elements from an Array to
|
||||
# implement the following Array methods: #&, #|, #-, #uniq, and #uniq!
|
||||
#
|
||||
# The algorithm used is double hashing with an overflow array. For each
|
||||
# array element, the element, its hash value, and its ordinal in the
|
||||
# sequence of elements added to the map are stored.
|
||||
#
|
||||
# Methods are provided to test an element for inclusion in the map and to
|
||||
# delete an entry from the map. The contents of a map can be returned as an
|
||||
# array in the order the elements were added to the map.
|
||||
|
||||
class IdentityMap
|
||||
attr_reader :size
|
||||
|
||||
Row = Table = Rubinius::Tuple
|
||||
MIN_CAPACITY = 64
|
||||
MIN_ROW = 10
|
||||
ROW_GROWTH = 9
|
||||
|
||||
# Converts one or more Enumerable instances to a single IdentityMap
|
||||
def self.from(*arrays, &block)
|
||||
im = allocate
|
||||
Rubinius.privately { im.load(arrays, &block) }
|
||||
im
|
||||
end
|
||||
|
||||
def initialize
|
||||
capacity = MIN_CAPACITY
|
||||
@table = Table.new capacity
|
||||
@mask = capacity - 4
|
||||
@max = capacity
|
||||
@size = 0
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
# Adds +item+ to the IdentityMap if it does not already exist. May cause
|
||||
# a row to be added or enlarged. Returns +self+.
|
||||
def insert(item, &block)
|
||||
redistribute if @size > @max
|
||||
|
||||
if block_given?
|
||||
item_hash = yield(item).hash
|
||||
else
|
||||
item_hash = item.hash
|
||||
end
|
||||
|
||||
index = item_hash & @mask
|
||||
table = @table
|
||||
|
||||
if num_entries = table[index]
|
||||
index += 1
|
||||
|
||||
if num_entries == 1
|
||||
return self if match?(table, index, item, item_hash, &block)
|
||||
|
||||
table[index-1] = 2
|
||||
table[index] = promote_row table, index, item, item_hash, @size
|
||||
else
|
||||
i = 1
|
||||
row = table[index]
|
||||
total = row[0]
|
||||
|
||||
while i < total
|
||||
return self if match?(row, i, item, item_hash, &block)
|
||||
i += 3
|
||||
end
|
||||
|
||||
if total == row.size
|
||||
table[index] = enlarge_row row, item, item_hash, @size
|
||||
else
|
||||
i = row[0]
|
||||
set_item row, i, item, item_hash, @size
|
||||
row[0] = i + 3
|
||||
end
|
||||
end
|
||||
else
|
||||
table[index] = 1
|
||||
set_item table, index+1, item, item_hash, @size
|
||||
end
|
||||
@size += 1
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
# Returns +true+ if +item+ is in the IdentityMap, +false+ otherwise.
|
||||
def include?(item)
|
||||
item_hash = item.hash
|
||||
|
||||
index = item_hash & @mask
|
||||
table = @table
|
||||
if num_entries = table[index]
|
||||
index += 1
|
||||
|
||||
if num_entries == 1
|
||||
return true if match? table, index, item, item_hash
|
||||
else
|
||||
row = table[index]
|
||||
i = 1
|
||||
total = row[0]
|
||||
while i < total
|
||||
return true if match? row, i, item, item_hash
|
||||
i += 3
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
# If +item+ is in the IdentityMap, removes it and returns +true+.
|
||||
# Otherwise, returns +false+.
|
||||
def delete(item)
|
||||
item_hash = item.hash
|
||||
|
||||
index = item_hash & @mask
|
||||
table = @table
|
||||
|
||||
if num_entries = table[index]
|
||||
index += 1
|
||||
if num_entries == 1
|
||||
if match? table, index, item, item_hash
|
||||
table[index] = nil
|
||||
@size -= 1
|
||||
return true
|
||||
end
|
||||
else
|
||||
row = table[index]
|
||||
i = 1
|
||||
total = row[0]
|
||||
while i < total
|
||||
if match? row, i, item, item_hash
|
||||
row[i] = nil
|
||||
@size -= 1
|
||||
return true
|
||||
end
|
||||
i += 3
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
# Returns an Array containing all items in the IdentityMap in the order
|
||||
# in which they were added to the IdentityMap.
|
||||
def to_array
|
||||
array = Array.new @size
|
||||
|
||||
i = 0
|
||||
table = @table
|
||||
total = table.size
|
||||
|
||||
while i < total
|
||||
if num_entries = table[i]
|
||||
if num_entries == 1
|
||||
array[table[i+3]] = table[i+2] if table[i+1]
|
||||
else
|
||||
row = table[i+1]
|
||||
k = row[0]
|
||||
j = 1
|
||||
while j < k
|
||||
array[row[j+2]] = row[j+1] if row[j]
|
||||
j += 3
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
i += 4
|
||||
end
|
||||
|
||||
array
|
||||
end
|
||||
|
||||
# Private implementation methods
|
||||
|
||||
def resize(total)
|
||||
capacity = MIN_CAPACITY
|
||||
while capacity < total
|
||||
capacity <<= 2
|
||||
end
|
||||
|
||||
@table = Table.new capacity
|
||||
@mask = capacity - 4
|
||||
@max = capacity
|
||||
end
|
||||
private :resize
|
||||
|
||||
def redistribute
|
||||
table = @table
|
||||
resize @size
|
||||
|
||||
i = 0
|
||||
total = table.size
|
||||
|
||||
while i < total
|
||||
if num_entries = table[i]
|
||||
if num_entries == 1
|
||||
if item_hash = table[i+1]
|
||||
add_item table[i+2], item_hash, table[i+3]
|
||||
end
|
||||
else
|
||||
row = table[i+1]
|
||||
k = row[0]
|
||||
j = 1
|
||||
while j < k
|
||||
if item_hash = row[j]
|
||||
add_item row[j+1], item_hash, row[j+2]
|
||||
end
|
||||
j += 3
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
i += 4
|
||||
end
|
||||
end
|
||||
private :redistribute
|
||||
|
||||
def add_item(item, item_hash, ordinal)
|
||||
index = item_hash & @mask
|
||||
table = @table
|
||||
|
||||
if num_entries = table[index]
|
||||
index += 1
|
||||
|
||||
if num_entries == 1
|
||||
table[index-1] = 2
|
||||
table[index] = promote_row table, index, item, item_hash, ordinal
|
||||
else
|
||||
row = table[index]
|
||||
i = row[0]
|
||||
|
||||
if i == row.size
|
||||
table[index] = enlarge_row row, item, item_hash, ordinal
|
||||
else
|
||||
set_item row, i, item, item_hash, ordinal
|
||||
row[0] = i + 3
|
||||
end
|
||||
end
|
||||
else
|
||||
table[index] = 1
|
||||
set_item table, index+1, item, item_hash, ordinal
|
||||
end
|
||||
end
|
||||
private :add_item
|
||||
|
||||
# Given an Array of Enumerable instances, computes a bounding set
|
||||
# to contain them and then adds each item to the IdentityMap.
|
||||
def load(arrays, &block)
|
||||
resize(arrays.inject(0) { |sum, array| sum + array.size })
|
||||
@size = 0
|
||||
|
||||
arrays.each do |array|
|
||||
array.each { |item| insert(item, &block) }
|
||||
end
|
||||
end
|
||||
private :load
|
||||
|
||||
def match?(table, index, item, item_hash)
|
||||
return false unless table[index] == item_hash
|
||||
other = table[index+1]
|
||||
if block_given?
|
||||
item = yield item
|
||||
other = yield other
|
||||
end
|
||||
Rubinius::Type.object_equal(item, other) or item.eql?(other)
|
||||
end
|
||||
private :match?
|
||||
|
||||
def set_item(table, index, item, item_hash, ordinal)
|
||||
table[index] = item_hash
|
||||
table[index+1] = item
|
||||
table[index+2] = ordinal
|
||||
end
|
||||
private :set_item
|
||||
|
||||
def promote_row(row, index, item, item_hash, ordinal)
|
||||
new_row = Row.new MIN_ROW
|
||||
|
||||
new_row[0] = 7
|
||||
new_row[1] = row[index]
|
||||
new_row[2] = row[index+1]
|
||||
new_row[3] = row[index+2]
|
||||
new_row[4] = item_hash
|
||||
new_row[5] = item
|
||||
new_row[6] = ordinal
|
||||
|
||||
new_row
|
||||
end
|
||||
private :promote_row
|
||||
|
||||
def enlarge_row(row, item, item_hash, ordinal)
|
||||
new_row = Row.new row.size + ROW_GROWTH
|
||||
new_row.copy_from row, 1, row.size-1, 1
|
||||
|
||||
index = row[0]
|
||||
new_row[0] = index + 3
|
||||
set_item new_row, index, item, item_hash, ordinal
|
||||
|
||||
new_row
|
||||
end
|
||||
private :enlarge_row
|
||||
end
|
||||
end
|
||||
31
gxg/core/immediate.rb
Normal file
31
gxg/core/immediate.rb
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
##
|
||||
# Mixin used to identify classes which have no memory storage.
|
||||
|
||||
module ImmediateValue
|
||||
def singleton_methods(all=true)
|
||||
[]
|
||||
end
|
||||
|
||||
def public_singleton_methods
|
||||
[]
|
||||
end
|
||||
private :public_singleton_methods
|
||||
|
||||
def private_singleton_methods
|
||||
[]
|
||||
end
|
||||
private :private_singleton_methods
|
||||
|
||||
def protected_singleton_methods
|
||||
[]
|
||||
end
|
||||
private :protected_singleton_methods
|
||||
|
||||
def dup
|
||||
raise TypeError, "can't dup #{self.class.name}"
|
||||
end
|
||||
|
||||
def clone
|
||||
raise TypeError, "can't clone #{self.class.name}"
|
||||
end
|
||||
end
|
||||
1580
gxg/core/integer.rb
Normal file
1580
gxg/core/integer.rb
Normal file
File diff suppressed because it is too large
Load diff
3609
gxg/core/io.rb
Normal file
3609
gxg/core/io.rb
Normal file
File diff suppressed because it is too large
Load diff
36
gxg/core/iseq.rb
Normal file
36
gxg/core/iseq.rb
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
module Rubinius
|
||||
class InstructionSequence
|
||||
def self.allocate
|
||||
Rubinius.primitive :instruction_sequence_allocate
|
||||
raise PrimitiveFailure, "Rubinius::InstructionSequence.allocate primitive failed"
|
||||
end
|
||||
|
||||
def initialize(size)
|
||||
if size.kind_of? Tuple
|
||||
@opcodes = size
|
||||
else
|
||||
@opcodes = Tuple.new(size)
|
||||
end
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
attr_reader :opcodes
|
||||
|
||||
def ==(other)
|
||||
other.kind_of?(InstructionSequence) and @opcodes == other.opcodes
|
||||
end
|
||||
|
||||
def []=(idx, val)
|
||||
@opcodes[idx] = val
|
||||
end
|
||||
|
||||
def [](idx)
|
||||
@opcodes[idx]
|
||||
end
|
||||
|
||||
def size
|
||||
@opcodes.size
|
||||
end
|
||||
end
|
||||
end
|
||||
32
gxg/core/jit.rb
Normal file
32
gxg/core/jit.rb
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
module Rubinius
|
||||
module JIT
|
||||
class << self
|
||||
attr_accessor :properties
|
||||
|
||||
def compile(object, compiled_code, block_environment=nil)
|
||||
Rubinius.invoke_primitive :jit_compile, self, object, compiled_code, block_environment
|
||||
end
|
||||
|
||||
def enabled?
|
||||
Rubinius.primitive :jit_enabled_p
|
||||
raise PrimitiveFailure, "Rubinius::JIT.enabled? primitive failed"
|
||||
end
|
||||
|
||||
# TODO: Fix configuration
|
||||
def compile_threshold
|
||||
Rubinius.primitive :jit_compile_threshold
|
||||
raise PrimitiveFailure, "Rubinius::JIT.compile_threshold primitive failed"
|
||||
end
|
||||
|
||||
def sync=(flag)
|
||||
Rubinius.primitive :jit_sync_set
|
||||
raise PrimitiveFailure, "Rubinius::JIT.sync primitive failed"
|
||||
end
|
||||
|
||||
def sync
|
||||
Rubinius.primitive :jit_sync_get
|
||||
raise PrimitiveFailure, "Rubinius::JIT.sync primitive failed"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
1142
gxg/core/kernel.rb
Normal file
1142
gxg/core/kernel.rb
Normal file
File diff suppressed because it is too large
Load diff
181
gxg/core/lexical_scope.rb
Normal file
181
gxg/core/lexical_scope.rb
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
##
|
||||
# A linked list that details the static, lexical scope the method was created
|
||||
# in.
|
||||
#
|
||||
# TODO: document
|
||||
|
||||
module Rubinius
|
||||
class LexicalScope
|
||||
def self.of_sender
|
||||
Rubinius.primitive :lexical_scope_of_sender
|
||||
raise PrimitiveFailure, "primitive failed"
|
||||
end
|
||||
|
||||
def class_variable_get(sym)
|
||||
Rubinius.primitive :lexical_scope_cvar_get
|
||||
|
||||
raise NameError, "Invalid class variable name '#{sym}'"
|
||||
end
|
||||
|
||||
def class_variable_set(sym, val)
|
||||
Rubinius.primitive :lexical_scope_cvar_set
|
||||
|
||||
raise NameError, "Invalid class variable name '#{sym}'"
|
||||
end
|
||||
|
||||
def class_variable_defined?(sym)
|
||||
Rubinius.primitive :lexical_scope_cvar_defined
|
||||
|
||||
raise NameError, "Invalid class variable name '#{sym}'"
|
||||
end
|
||||
|
||||
def class_variable_get_or_set(sym, val)
|
||||
Rubinius.primitive :lexical_scope_cvar_get_or_set
|
||||
|
||||
raise NameError, "Invalid class variable name '#{sym}'"
|
||||
end
|
||||
|
||||
#
|
||||
# @todo Verify the recursion here does not cause problems. --rue
|
||||
#
|
||||
def initialize(mod, par = nil)
|
||||
@module = mod
|
||||
@parent = par
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
# Source code of this scope.
|
||||
attr_accessor :script
|
||||
|
||||
# The function map for this scope references its top-most parent's map
|
||||
attr_accessor :functions
|
||||
|
||||
# Module or class this lexical scope enclosed into.
|
||||
attr_reader :module
|
||||
|
||||
# Constant scope object this scope enclosed into.
|
||||
attr_reader :parent
|
||||
|
||||
# Module or class representing the 'current class'. MRI manipulates
|
||||
# this outside of the lexical scope and uses it for undef and method
|
||||
# definition.
|
||||
attr_accessor :current_module
|
||||
|
||||
# Set to indicate that no methods may be add to this scope
|
||||
attr_accessor :disabled_for_methods
|
||||
|
||||
# Lazy initialized hash map used for flip-flops
|
||||
attr_accessor :flip_flops
|
||||
|
||||
def const_defined?(name, search_parents=true)
|
||||
scope = self
|
||||
while scope and scope.module != Object
|
||||
return true if scope.module.const_defined?(name)
|
||||
scope = scope.parent
|
||||
end
|
||||
|
||||
return Object.const_defined?(name)
|
||||
end
|
||||
|
||||
def inspect
|
||||
current = " current=#{@current_module}" if @current_module
|
||||
"#<#{self.class.name}:0x#{self.object_id.to_s(16)} parent=#{@parent.inspect} module=#{@module}#{current}>"
|
||||
end
|
||||
|
||||
def to_s
|
||||
inspect
|
||||
end
|
||||
|
||||
# Indicates if this is a toplevel/default scope
|
||||
def top_level?
|
||||
!@parent
|
||||
end
|
||||
|
||||
# Use the same info as the current LexicalScope, but set current_module to
|
||||
# +mod+. Chains off the current LexicalScope.
|
||||
def using_current_as(mod)
|
||||
if top_level?
|
||||
# Don't chain up if this is a toplevel, create a new one.
|
||||
cs = dup
|
||||
else
|
||||
cs = LexicalScope.new @module, self
|
||||
end
|
||||
|
||||
cs.current_module = mod
|
||||
return cs
|
||||
end
|
||||
|
||||
def using_disabled_scope
|
||||
cs = using_current_as(@module)
|
||||
cs.disabled_for_methods = true
|
||||
return cs
|
||||
end
|
||||
|
||||
def for_method_definition
|
||||
if @disabled_for_methods
|
||||
raise TypeError, "unable to create methods in this scope"
|
||||
end
|
||||
|
||||
return @current_module if @current_module
|
||||
return @module
|
||||
end
|
||||
|
||||
def alias_method(name, original)
|
||||
Rubinius.privately do
|
||||
for_method_definition.alias_method name, original
|
||||
end
|
||||
end
|
||||
|
||||
def __undef_method__(name)
|
||||
Rubinius.privately do
|
||||
for_method_definition.undef_method name
|
||||
end
|
||||
end
|
||||
|
||||
def active_path
|
||||
script = current_script
|
||||
if script
|
||||
if path = script.file_path
|
||||
return path.dup
|
||||
end
|
||||
end
|
||||
|
||||
return "__unknown__.rb"
|
||||
end
|
||||
|
||||
def absolute_active_path
|
||||
script = current_script
|
||||
if script
|
||||
if path = script.data_path
|
||||
return path.dup
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
def data_path
|
||||
script = current_script
|
||||
if script and script.main?
|
||||
if path = script.data_path
|
||||
return path.dup
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
def current_script
|
||||
scope = self
|
||||
while scope and !scope.script
|
||||
scope = scope.parent
|
||||
end
|
||||
return scope && scope.script
|
||||
end
|
||||
|
||||
def const_set(name, value)
|
||||
@module.const_set name, value
|
||||
end
|
||||
end
|
||||
end
|
||||
319
gxg/core/library.rb
Normal file
319
gxg/core/library.rb
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
class Rubinius::NativeFunction
|
||||
attr_accessor :return_type
|
||||
attr_accessor :argument_types
|
||||
end
|
||||
|
||||
module Rubinius
|
||||
module FFI
|
||||
def self.generate_function(ptr, name, args, ret)
|
||||
Rubinius.primitive :nativefunction_generate
|
||||
raise PrimitiveFailure, "FFI.generate_function primitive failed"
|
||||
end
|
||||
|
||||
def self.generate_trampoline(obj, name, args, ret)
|
||||
Rubinius.primitive :nativefunction_generate_tramp
|
||||
raise PrimitiveFailure, "FFI.generate_function_tramp primitive failed"
|
||||
end
|
||||
|
||||
module Library
|
||||
LIBC = Rubinius::LIBC
|
||||
|
||||
# Set which library or libraries +attach_function+ should
|
||||
# look in. By default it only searches for the function in
|
||||
# the current process. If you want to specify this as one
|
||||
# of the locations, add FFI::USE_THIS_PROCESS_AS_LIBRARY.
|
||||
# The libraries are tried in the order given.
|
||||
#
|
||||
def ffi_lib(*names)
|
||||
@ffi_lib = names.map do |x|
|
||||
if x == FFI::CURRENT_PROCESS
|
||||
DynamicLibrary::CURRENT_PROCESS
|
||||
|
||||
# When the element is an array, it's an ordered choice,
|
||||
# ie, pick the first library that works.
|
||||
elsif x.kind_of? Array
|
||||
lib = nil
|
||||
|
||||
x.each do |name|
|
||||
begin
|
||||
lib = DynamicLibrary.new(name, @ffi_lib_flags)
|
||||
break
|
||||
rescue LoadError
|
||||
end
|
||||
end
|
||||
|
||||
# If .new worked, then lib is set and we can use it.
|
||||
unless lib
|
||||
raise LoadError, "Unable to find library among: #{x.inspect}"
|
||||
end
|
||||
|
||||
lib
|
||||
else
|
||||
DynamicLibrary.new(x)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def ffi_libraries
|
||||
@ffi_lib or [DynamicLibrary::CURRENT_PROCESS]
|
||||
end
|
||||
private :ffi_libraries
|
||||
|
||||
# Flags used in {#ffi_lib}.
|
||||
#
|
||||
# This map allows you to supply symbols to {#ffi_lib_flags} instead of
|
||||
# the actual constants.
|
||||
def flags_map
|
||||
{
|
||||
:global => DynamicLibrary::RTLD_GLOBAL,
|
||||
:local => DynamicLibrary::RTLD_LOCAL,
|
||||
:lazy => DynamicLibrary::RTLD_LAZY,
|
||||
:now => DynamicLibrary::RTLD_NOW
|
||||
}
|
||||
end
|
||||
|
||||
# Sets library flags for {#ffi_lib}.
|
||||
#
|
||||
# @example
|
||||
# ffi_lib_flags(:lazy, :local) # => 5
|
||||
#
|
||||
# @param [Symbol, …] flags (see {FlagsMap})
|
||||
# @return [Fixnum] the new value
|
||||
def ffi_lib_flags(*flags)
|
||||
@ffi_lib_flags = flags.inject(0) { |result, f| result | flags_map[f] }
|
||||
end
|
||||
|
||||
# Attach a C function to this module. The arguments can have two forms:
|
||||
#
|
||||
# attach_function c_name, [c_arg1, c_arg2], ret
|
||||
# attach_function mod_name, c_name, [c_arg1, c_arg2], ret
|
||||
#
|
||||
# In the first form, +c_name+ will also be used for the name of the module
|
||||
# method. In the second form, the module method name is +mod_name+.
|
||||
#
|
||||
# In either form, an optional options hash will be accepted as an
|
||||
# additional argument, although currently all options are ignored.
|
||||
#
|
||||
# The +c_name+ and +mod_name+ can be given as Strings or Symbols.
|
||||
#
|
||||
# The types of the arguments to the C function, +c_arg1+, +c_arg2+, etc, are
|
||||
# given as an array even if there is only one.
|
||||
#
|
||||
# The final argument, +ret+, is the type of the return value from the C
|
||||
# function.
|
||||
def attach_function(name, a2, a3, a4=nil, a5=nil)
|
||||
if a4 && (a2.kind_of?(String) || a2.kind_of?(Symbol))
|
||||
cname = a2.to_s
|
||||
args = a3
|
||||
ret = a4
|
||||
else
|
||||
cname = name.to_s
|
||||
args = a2
|
||||
ret = a3
|
||||
end
|
||||
|
||||
mname = name.to_sym
|
||||
|
||||
ffi_libraries.each do |lib|
|
||||
if ptr = lib.find_symbol(cname)
|
||||
return pointer_as_function(mname, ptr, args, ret)
|
||||
end
|
||||
end
|
||||
|
||||
ffi_function_missing cname, mname, args, ret
|
||||
end
|
||||
|
||||
# Attach a C variable to this module. The arguments can have two forms:
|
||||
#
|
||||
# attach_variable mod_name, type
|
||||
# attach_variable mod_name, c_name, type
|
||||
#
|
||||
# In the first form, +mod_name+ will also be used for the name of the C variable.
|
||||
# In the second form, the C variable name is +c_name+.
|
||||
#
|
||||
# The +mod_name+ and +c_name+ can be given as Strings or Symbols.
|
||||
#
|
||||
# The final argument, +type+, is the type of the C variable
|
||||
def attach_variable(mname, a1, a2=nil)
|
||||
cname, type = a2 ? [ a1, a2 ] : [ mname.to_s, a1 ]
|
||||
ptr = nil
|
||||
|
||||
ffi_libraries.each do |lib|
|
||||
ptr = lib.find_symbol(cname.to_s)
|
||||
break unless ptr.nil?
|
||||
end
|
||||
|
||||
raise FFI::NotFoundError, "Unable to find '#{cname}'" if ptr.nil? || ptr.null?
|
||||
|
||||
if type.kind_of?(Class) and type.ancestors.include?(FFI::Struct)
|
||||
c = type.new(ptr)
|
||||
|
||||
self.module_eval <<-code, __FILE__, __LINE__
|
||||
@ffi_gvar_#{mname} = c
|
||||
def self.#{mname}
|
||||
@ffi_gvar_#{mname}
|
||||
end
|
||||
code
|
||||
|
||||
else
|
||||
enclosing_module = self
|
||||
|
||||
cs = Class.new(FFI::Struct) do
|
||||
@enclosing_module = enclosing_module
|
||||
end
|
||||
|
||||
cs.layout :gvar, type
|
||||
c = cs.new(ptr)
|
||||
|
||||
self.module_eval <<-code, __FILE__, __LINE__
|
||||
@ffi_gvar_#{mname} = c
|
||||
def self.#{mname}
|
||||
@ffi_gvar_#{mname}[:gvar]
|
||||
end
|
||||
def self.#{mname}=(value)
|
||||
@ffi_gvar_#{mname}[:gvar] = value
|
||||
end
|
||||
code
|
||||
|
||||
end
|
||||
|
||||
return ptr
|
||||
end
|
||||
|
||||
# Generic error method attached in place of missing foreign functions
|
||||
# during loading the core library. See core/zed.rb for a version that
|
||||
# raises immediately if the foreign function is unavaiblable.
|
||||
def ffi_function_not_implemented(*args)
|
||||
raise NotImplementedError, "function not implemented on this platform"
|
||||
end
|
||||
|
||||
# Protocol for attaching foregin functions. If #attach_function fails to
|
||||
# find a foreign function, this method will be called. Client code can
|
||||
# provide an override to customize features.
|
||||
def ffi_function_missing(cname, mname, args, ret)
|
||||
if func = Rubinius.find_method(self, :ffi_function_not_implemented)
|
||||
func = func[0].dup
|
||||
func.name = cname.to_sym
|
||||
add_function mname, func
|
||||
end
|
||||
end
|
||||
|
||||
def add_function(name, func)
|
||||
# Make it available as a method callable directly..
|
||||
sc = Rubinius::Type.object_singleton_class(self)
|
||||
Rubinius::VM.reset_method_cache sc, name
|
||||
|
||||
sc.method_table.store name, nil, func, nil, 0, :public
|
||||
|
||||
# and expose it as a private method for people who
|
||||
# want to include this module.
|
||||
method_table.store name, nil, func, nil, 0, :private
|
||||
end
|
||||
private :add_function
|
||||
|
||||
def pointer_as_function(name, ptr, args, ret)
|
||||
args.map! { |a| find_type a }
|
||||
|
||||
if func = FFI.generate_function(ptr, name.to_sym, args, find_type(ret))
|
||||
add_function name, func
|
||||
return func
|
||||
end
|
||||
|
||||
raise FFI::NotFoundError, "Unable to attach pointer"
|
||||
end
|
||||
|
||||
def callback(a1, a2, a3=nil)
|
||||
if a3
|
||||
name = a1
|
||||
params = a2
|
||||
ret = a3
|
||||
else
|
||||
name = nil
|
||||
params = a1
|
||||
ret = a2
|
||||
end
|
||||
|
||||
args = params.map { |x| find_type(x) }
|
||||
|
||||
func, ptr = FFI.generate_trampoline nil, :ffi_tramp,
|
||||
args, find_type(ret)
|
||||
|
||||
func.argument_types = params
|
||||
func.return_type = ret
|
||||
|
||||
if name
|
||||
@ffi_callbacks ||= {}
|
||||
@ffi_callbacks[name] = func
|
||||
|
||||
typedef func, name
|
||||
end
|
||||
|
||||
return func
|
||||
end
|
||||
|
||||
def typedef(old, add)
|
||||
@typedefs ||= Rubinius::LookupTable.new
|
||||
@typedefs[add] = find_type(old)
|
||||
end
|
||||
|
||||
def find_type(name)
|
||||
@typedefs ||= Rubinius::LookupTable.new
|
||||
|
||||
if name.kind_of? Rubinius::NativeFunction or name.kind_of? FFI::Enum
|
||||
return name
|
||||
end
|
||||
|
||||
if type = @typedefs[name]
|
||||
return type
|
||||
end
|
||||
|
||||
FFI.find_type(name)
|
||||
end
|
||||
|
||||
def enum(*args)
|
||||
@tagged_enums ||= Rubinius::LookupTable.new
|
||||
@anon_enums ||= Array.new
|
||||
|
||||
tag, values = if args[0].kind_of?(Symbol) && args[1].kind_of?(Array)
|
||||
[ args[0], args[1] ]
|
||||
elsif args[0].kind_of?(Array)
|
||||
[ nil, args[0] ]
|
||||
else
|
||||
[ nil, args ]
|
||||
end
|
||||
|
||||
enum = FFI::Enum.new values, tag
|
||||
|
||||
if tag
|
||||
typedef(enum, tag)
|
||||
@tagged_enums[tag] = enum
|
||||
else
|
||||
@anon_enums << enum
|
||||
end
|
||||
|
||||
return enum
|
||||
end
|
||||
|
||||
def enum_type(tag)
|
||||
|
||||
if enum = @tagged_enums[tag]
|
||||
enum
|
||||
else
|
||||
@anon_enums.detect { |enum| enum.symbols.include?(tag) }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def enum_value(value)
|
||||
|
||||
if enum = @anon_enums.detect { |enum| enum.symbols.include?(value) }
|
||||
enum
|
||||
else
|
||||
tag,enum = @tagged_enums.detect { |tag,enum| enum.symbols.include?(value) }
|
||||
return enum
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
130
gxg/core/load_order.txt
Normal file
130
gxg/core/load_order.txt
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
alpha.rb
|
||||
alias.rb
|
||||
argf.rb
|
||||
array.rb
|
||||
array_mirror.rb
|
||||
atomic.rb
|
||||
autoload.rb
|
||||
backtrace.rb
|
||||
basic_object.rb
|
||||
binding.rb
|
||||
block_environment.rb
|
||||
byte_array.rb
|
||||
call_site.rb
|
||||
capi.rb
|
||||
channel.rb
|
||||
character.rb
|
||||
class.rb
|
||||
code_db.rb
|
||||
code_loader.rb
|
||||
compact_lookup_table.rb
|
||||
comparable.rb
|
||||
compiled_code.rb
|
||||
complex.rb
|
||||
complexifier.rb
|
||||
configuration.rb
|
||||
console.rb
|
||||
constant_cache.rb
|
||||
lexical_scope.rb
|
||||
constant_table.rb
|
||||
continuation.rb
|
||||
ctype.rb
|
||||
delegated_method.rb
|
||||
dir.rb
|
||||
dir_glob.rb
|
||||
encoding.rb
|
||||
enumerable.rb
|
||||
enumerable_helper.rb
|
||||
enumerator.rb
|
||||
env.rb
|
||||
errno.rb
|
||||
exception.rb
|
||||
executable.rb
|
||||
false.rb
|
||||
ffi.rb
|
||||
fiber.rb
|
||||
file.rb
|
||||
file_test.rb
|
||||
float.rb
|
||||
fsevent.rb
|
||||
gc.rb
|
||||
global.rb
|
||||
hash.rb
|
||||
hook.rb
|
||||
identity_map.rb
|
||||
immediate.rb
|
||||
integer.rb
|
||||
io.rb
|
||||
iseq.rb
|
||||
jit.rb
|
||||
kernel.rb
|
||||
library.rb
|
||||
location.rb
|
||||
logger.rb
|
||||
lookup_table.rb
|
||||
lru_cache.rb
|
||||
main.rb
|
||||
marshal.rb
|
||||
match_data.rb
|
||||
math.rb
|
||||
method.rb
|
||||
method_equality.rb
|
||||
method_table.rb
|
||||
metrics.rb
|
||||
mirror.rb
|
||||
missing_method.rb
|
||||
module.rb
|
||||
mutex.rb
|
||||
native_method.rb
|
||||
nil.rb
|
||||
numeric.rb
|
||||
numeric_mirror.rb
|
||||
object_space.rb
|
||||
options.rb
|
||||
pack.rb
|
||||
peg.rb
|
||||
pointer.rb
|
||||
pointer_accessors.rb
|
||||
prediction.rb
|
||||
proc.rb
|
||||
proc_mirror.rb
|
||||
process.rb
|
||||
process_mirror.rb
|
||||
random.rb
|
||||
range.rb
|
||||
range_mirror.rb
|
||||
rational.rb
|
||||
rationalizer.rb
|
||||
regexp.rb
|
||||
respond_to_cache.rb
|
||||
rubinius.rb
|
||||
runtime.rb
|
||||
signal.rb
|
||||
splitter.rb
|
||||
sprinter.rb
|
||||
stat.rb
|
||||
stats.rb
|
||||
string.rb
|
||||
string_mirror.rb
|
||||
struct.rb
|
||||
symbol.rb
|
||||
thread.rb
|
||||
thread_group.rb
|
||||
thread_group_mirror.rb
|
||||
thread_mirror.rb
|
||||
throw_catch.rb
|
||||
thunk.rb
|
||||
time.rb
|
||||
true.rb
|
||||
tuple.rb
|
||||
type.rb
|
||||
undefined.rb
|
||||
variable_scope.rb
|
||||
vm.rb
|
||||
weakref.rb
|
||||
zed.rb
|
||||
ruby_constants.rb
|
||||
build_config.rb
|
||||
rbconfig.rb
|
||||
deprecations.rb
|
||||
loader.rb
|
||||
872
gxg/core/loader.rb
Normal file
872
gxg/core/loader.rb
Normal file
|
|
@ -0,0 +1,872 @@
|
|||
TOPLEVEL_BINDING = binding()
|
||||
|
||||
module Rubinius
|
||||
class Loader
|
||||
def initialize
|
||||
@exit_code = 0
|
||||
@process_id = Process.pid
|
||||
@load_paths = []
|
||||
@requires = []
|
||||
@evals = []
|
||||
@script = nil
|
||||
@debugging = false
|
||||
@repl = true
|
||||
@printed_version = false
|
||||
@input_loop = false
|
||||
@input_loop_print = false
|
||||
@input_loop_split = false
|
||||
@simple_options = false
|
||||
@early_option_stop = false
|
||||
@check_syntax = false
|
||||
@load_profiler = false
|
||||
@enable_gems = true
|
||||
@load_gemfile = false
|
||||
@deprecations = false
|
||||
|
||||
version = RUBY_VERSION.split(".").first(2).join(".")
|
||||
@gem_bins = ["#{version}/bin", "bin"].map do |dir|
|
||||
File.join Rubinius::GEMS_PATH, dir
|
||||
end
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def self.debugger
|
||||
@debugger_proc
|
||||
end
|
||||
|
||||
def self.debugger=(prc)
|
||||
@debugger_proc = prc
|
||||
end
|
||||
|
||||
# Finish setting up after loading the core library.
|
||||
def preamble
|
||||
set_program_name "rbx"
|
||||
@stage = "running Loader preamble"
|
||||
|
||||
# Set the default visibility for the top level binding
|
||||
TOPLEVEL_BINDING.variables.method_visibility = :private
|
||||
|
||||
$VERBOSE = false
|
||||
end
|
||||
|
||||
# Set up $LOAD_PATH.
|
||||
def system_load_path
|
||||
@stage = "setting up system load path"
|
||||
|
||||
@lib_bin = "#{Rubinius::CODEDB_PATH}/source/bin"
|
||||
|
||||
$LOAD_PATH.unshift(Rubinius::ARCH_PATH,
|
||||
Rubinius::SITE_PATH,
|
||||
"#{Rubinius::SITE_PATH}/#{RUBY_VERSION}",
|
||||
Rubinius::VENDOR_PATH,
|
||||
"#{Rubinius::CODEDB_PATH}/source")
|
||||
|
||||
if libs = ENV['RUBYLIB']
|
||||
Rubinius::Logger.system.write "RUBYLIB: #{libs}"
|
||||
|
||||
$LOAD_PATH.unshift(*libs.split(File::PATH_SEPARATOR).compact)
|
||||
end
|
||||
end
|
||||
|
||||
# Load customization code:
|
||||
# /etc/rbxrc
|
||||
# $HOME/.rbxrc
|
||||
# $RBX_PRELOAD
|
||||
def preload
|
||||
@stage = "preloading rbxrc code"
|
||||
|
||||
['/etc/rbxrc',"#{ENV['HOME']}/.rbxrc",ENV['RBX_PRELOAD']].each do |file|
|
||||
begin
|
||||
load file if file and File.exist?(file)
|
||||
rescue LoadError
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Register signal handlers.
|
||||
def signals
|
||||
@stage = "registering signal handlers"
|
||||
|
||||
# Set up a handler for SIGINT that raises Interrupt on the main thread
|
||||
Signal.trap("INT") do |sig|
|
||||
raise Interrupt, "Thread has been interrupted"
|
||||
end
|
||||
|
||||
["HUP", "QUIT", "TERM", "ALRM", "USR1", "USR2"].each do |signal|
|
||||
Signal.trap(signal) do |sig|
|
||||
raise SignalException, sig
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def show_syntax_error(e)
|
||||
STDERR.puts "A syntax error has occurred:"
|
||||
STDERR.puts " #{e.message}"
|
||||
STDERR.puts "\nCode:\n#{e.code}"
|
||||
if e.column
|
||||
STDERR.puts((" " * (e.column - 1)) + "^")
|
||||
end
|
||||
end
|
||||
|
||||
def show_syntax_errors(syns)
|
||||
syns.each do |e|
|
||||
STDERR.puts "#{e.file}:#{e.line}: #{e.reason}"
|
||||
end
|
||||
end
|
||||
|
||||
# Checks if a subcommand with basename +base+ exists. Returns the full
|
||||
# path to the subcommand if it does; otherwise, returns nil.
|
||||
def find_subcommand(base)
|
||||
command = File.join @lib_bin, "#{base}.rb"
|
||||
return command if File.exist? command
|
||||
return nil
|
||||
end
|
||||
|
||||
# Checks if a gem wrapper named +base+ exists. Returns the full path to
|
||||
# the gem wrapper if it does; otherwise, returns nil.
|
||||
def find_gem_wrapper(base)
|
||||
@gem_bins.each do |dir|
|
||||
wrapper = File.join dir, base
|
||||
return wrapper if File.exist? wrapper
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
# Detects if the Rubinius executable was aliased to a subcommand or a
|
||||
# rubygems executable. If so, changes ARGV as if Rubinius were invoked
|
||||
# to run the subcommand or rubygems executable.
|
||||
def detect_alias
|
||||
cmd = ARG0.split("/").last
|
||||
|
||||
# ignore the common ones straight away
|
||||
return if cmd == "rbx" or cmd == "ruby"
|
||||
|
||||
# Check if we are aliased to a Rubinius subcommand.
|
||||
if subcommand = find_subcommand(cmd)
|
||||
ARGV.unshift subcommand
|
||||
return
|
||||
end
|
||||
|
||||
# Check if we are aliased to a rubygems executable.
|
||||
if gem_wrapper = find_gem_wrapper(cmd)
|
||||
ARGV.unshift "-S", gem_wrapper
|
||||
end
|
||||
end
|
||||
|
||||
def handle_simple_options(argv)
|
||||
argv.delete_if do |x|
|
||||
if x[0] == ?-
|
||||
if equal = x.index("=")
|
||||
name = x.substring(1, equal-1)
|
||||
|
||||
equal += 1
|
||||
val = x.substring(equal, x.size - equal)
|
||||
else
|
||||
name = x.substring(1, x.size - 1)
|
||||
val = true
|
||||
end
|
||||
|
||||
name.gsub! "-", "_"
|
||||
|
||||
Rubinius::Globals[:"$#{name}"] = val
|
||||
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def define_global_methods
|
||||
Kernel.module_eval do
|
||||
def gsub(pattern, rep=nil, &block)
|
||||
target = $_
|
||||
raise TypeError, "$_ must be a String, but is #{target.inspect}" unless target.kind_of? String
|
||||
$_ = target.gsub(pattern, rep, &block)
|
||||
end
|
||||
module_function :gsub
|
||||
|
||||
def sub(pattern, rep=nil, &block)
|
||||
target = $_
|
||||
raise TypeError, "$_ must be a String, but is #{target.inspect}" unless target.kind_of? String
|
||||
$_ = target.sub(pattern, rep, &block)
|
||||
end
|
||||
module_function :sub
|
||||
|
||||
def chomp(string=$/)
|
||||
raise TypeError, "$_ must be a String" unless $_.kind_of? String
|
||||
$_ = $_.chomp(string)
|
||||
end
|
||||
module_function :chomp
|
||||
|
||||
def chop
|
||||
raise TypeError, "$_ must be a String" unless $_.kind_of? String
|
||||
$_ = $_.chop
|
||||
end
|
||||
module_function :chop
|
||||
end
|
||||
end
|
||||
|
||||
# Process all command line arguments.
|
||||
def options(argv=ARGV)
|
||||
@stage = "processing command line arguments"
|
||||
|
||||
options = Options.new "Usage: rbx [subcommand] [options] [--] [script] [arguments]", 25
|
||||
|
||||
options.left_align
|
||||
options.on_extra do |x|
|
||||
raise Options::ParseError, "Unrecognized option: #{x}" if x[0] == ?-
|
||||
if @script.nil? and @evals.empty?
|
||||
@script = x
|
||||
else
|
||||
argv.unshift x
|
||||
end
|
||||
options.stop_parsing
|
||||
end
|
||||
|
||||
options.doc "Script is any valid Ruby source code file"
|
||||
|
||||
options.doc "\nRuby options"
|
||||
options.on "-", "Read and evaluate code from STDIN" do
|
||||
@repl = false
|
||||
set_program_name "-"
|
||||
stdin = STDIN.dup
|
||||
script = STDIN.read
|
||||
STDIN.reopen(stdin)
|
||||
execute_script script
|
||||
end
|
||||
|
||||
options.on "--", "Stop processing command line arguments" do
|
||||
@early_option_stop = true
|
||||
options.stop_parsing
|
||||
end
|
||||
|
||||
options.on "-a", "Used with -n and -p, splits $_ into $F" do
|
||||
@input_loop_split = true
|
||||
Rubinius::Globals.set!(:$-a, true)
|
||||
end
|
||||
|
||||
options.on "-c", "Only check the syntax" do
|
||||
@repl = false
|
||||
@check_syntax = true
|
||||
end
|
||||
|
||||
options.on "-C", "DIR", "Change directory to DIR before running scripts" do |dir|
|
||||
@directory = dir
|
||||
end
|
||||
|
||||
options.on "-d", "Enable debugging output and set $DEBUG to true" do
|
||||
$VERBOSE = true
|
||||
$DEBUG = true
|
||||
end
|
||||
|
||||
options.on "--disable-gems", "Do not automatically load rubygems on startup" do
|
||||
@enable_gems = false
|
||||
end
|
||||
|
||||
options.on "-e", "CODE", "Compile and execute CODE" do |code|
|
||||
@repl = false
|
||||
set_program_name "(eval)"
|
||||
@evals << code
|
||||
end
|
||||
|
||||
options.on '-F', "PATTERN", "Set $; to PATTERN" do |pattern|
|
||||
Rubinius::Globals.set!(:$;, Regexp.new(pattern))
|
||||
end
|
||||
|
||||
options.on '-G', '--gemfile', 'Respect a Gemfile in the current path' do
|
||||
@load_gemfile = true
|
||||
end
|
||||
|
||||
options.on "-h", "--help", "Display this help" do
|
||||
@repl = false
|
||||
puts options
|
||||
done
|
||||
end
|
||||
|
||||
options.on "-i", "[EXT]", "Edit ARGV files in place, making backup with EXT" do |ext|
|
||||
# in place edit mode
|
||||
ext ||= ''
|
||||
$-i = ext
|
||||
end
|
||||
|
||||
options.on "-I", "DIR1[:DIR2]", "Add directories to $LOAD_PATH" do |dir|
|
||||
@load_paths << dir
|
||||
end
|
||||
|
||||
options.on "-K", "Ignored $KCODE option for compatibility" do
|
||||
msg = "The -K option is deprecated. Rubinius internal encoding is always UTF-8"
|
||||
STDERR.puts msg
|
||||
Rubinius::Logger.system.warn msg
|
||||
end
|
||||
|
||||
options.on "-U", "Set Encoding.default_internal to UTF-8" do
|
||||
msg = "The -U option is deprecated. Rubinius internal encoding is always UTF-8"
|
||||
STDERR.puts msg
|
||||
Rubinius::Logger.system.warn msg
|
||||
end
|
||||
|
||||
options.on "-E", "ENC", "Set external:internal character encoding to ENC" do |enc|
|
||||
external, internal = enc.split(":")
|
||||
Encoding.default_external = external if external and !external.empty?
|
||||
|
||||
if internal and !internal.empty?
|
||||
msg = "The -E option setting internal encoding is deprecated. Rubinius internal encoding is always UTF-8"
|
||||
STDERR.puts msg
|
||||
Rubinius::Logger.system.warn msg
|
||||
end
|
||||
end
|
||||
|
||||
options.on "--main", "PATH", "Load PATH directly from CodeDB" do |path|
|
||||
code = Rubinius::CoreDB.load_feature path, "", false, false
|
||||
if code
|
||||
code.create_script false
|
||||
Rubinius.run_script code
|
||||
exit 0
|
||||
else
|
||||
puts "file not found: #{path}"
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
options.on "-n", "Wrap running code in 'while(gets()) ...'" do
|
||||
@input_loop = true
|
||||
define_global_methods
|
||||
end
|
||||
|
||||
options.on "-p", "Same as -n, but also print $_" do
|
||||
@input_loop = true
|
||||
@input_loop_print = true
|
||||
Rubinius::Globals.set!(:$-p, true)
|
||||
define_global_methods
|
||||
end
|
||||
|
||||
options.on "-r", "LIBRARY", "Require library before execution" do |file|
|
||||
@requires << file
|
||||
end
|
||||
|
||||
options.on("-s", "Process options after the script into globals") do
|
||||
@simple_options = true
|
||||
end
|
||||
|
||||
options.on("-S", "SCRIPT",
|
||||
"Run SCRIPT using PATH environment variable to find it") do |script|
|
||||
options.stop_parsing
|
||||
@repl = false
|
||||
|
||||
# Load a gemfile now if we need to so that -S can see binstubs
|
||||
# internal to the Gemfile
|
||||
gemfile
|
||||
|
||||
# First, check if any existing gem wrappers match.
|
||||
unless file = find_gem_wrapper(script)
|
||||
# Then, check if any Rubinius subcommands match.
|
||||
unless file = find_subcommand(script)
|
||||
# Finally, check if any file on PATH matches.
|
||||
search = ENV['PATH'].split(File::PATH_SEPARATOR)
|
||||
search.each do |d|
|
||||
path = File.join d, script
|
||||
if File.exist? path
|
||||
file = path
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
set_program_name script if file
|
||||
|
||||
# if missing, let it die a natural death
|
||||
@script = file ? file : script
|
||||
end
|
||||
|
||||
options.on "-v", "Display the version and set $VERBOSE to true" do
|
||||
@repl = false
|
||||
$VERBOSE = true
|
||||
|
||||
unless @printed_version
|
||||
puts Rubinius.version
|
||||
@printed_version = true
|
||||
end
|
||||
end
|
||||
|
||||
options.on "-w", "Enable warnings" do
|
||||
$VERBOSE = true
|
||||
end
|
||||
|
||||
options.on("-W", "[level]",
|
||||
"Set warning level: 0=silence, 1=medium, 2=verbose (default)") do |l|
|
||||
case l
|
||||
when "0"
|
||||
$VERBOSE = nil
|
||||
when "1"
|
||||
$VERBOSE = false
|
||||
when nil
|
||||
$VERBOSE = true
|
||||
else
|
||||
# MRI -h says -W2 sets $VERBOSE to true, but behaviorally
|
||||
# any value >= 2 sets $VERBOSE to true.
|
||||
$VERBOSE = true
|
||||
end
|
||||
end
|
||||
|
||||
options.on "--show-nil-location", "Show where nil value originated when NoMethodError on nil is raised" do
|
||||
$rbx_show_nil_location = true
|
||||
end
|
||||
|
||||
options.on "--deprecated", "Display any deprecations" do
|
||||
@deprecations = true
|
||||
end
|
||||
|
||||
options.on "--version", "Display the version" do
|
||||
@repl = false
|
||||
puts Rubinius.version
|
||||
end
|
||||
|
||||
options.doc <<-DOC
|
||||
\nRubinius subcommands
|
||||
|
||||
Rubinius provides subcommands that implement various facilities. Each
|
||||
subcommand provides its own help. For example:
|
||||
|
||||
rbx compile -h
|
||||
|
||||
Available subcommands:
|
||||
|
||||
compile Run the bytecode compiler
|
||||
gem Run RubyGems 'gem' command
|
||||
irb Run IRB
|
||||
erb Run ERb
|
||||
DOC
|
||||
|
||||
options.doc <<-DOC
|
||||
VM Options
|
||||
-X<variable>[=<value>]
|
||||
This option is recognized by the VM before any ruby code is loaded.
|
||||
It is used to set VM configuration options.
|
||||
|
||||
Use -Xhelp to see the list of options the VM recognizes.
|
||||
All variables, even ones that the VM doesn't understand, are available
|
||||
in Rubinius::Config.
|
||||
|
||||
Options are parsed from these sources, in this order:
|
||||
|
||||
1. The file .rbxrc in the current working directory.
|
||||
2. The RBXOPT environment variable.
|
||||
3. The command line options.
|
||||
|
||||
This order permits environment and command line options to override
|
||||
"application" configuration. Likewise, command line options can override
|
||||
environment configuration.
|
||||
|
||||
A number of Rubinius features are driven by setting these variables.
|
||||
DOC
|
||||
|
||||
options.parse ARGV
|
||||
|
||||
handle_rubyopt(options)
|
||||
|
||||
if @early_option_stop and @simple_options
|
||||
handle_simple_options(argv)
|
||||
end
|
||||
|
||||
if Rubinius::Config['help']
|
||||
STDOUT.puts "Rubinius configuration variables:"
|
||||
STDOUT.puts " These variables are normally set via -X and read via Rubinius::Config[var]."
|
||||
STDOUT.puts
|
||||
|
||||
require 'rubinius/configuration'
|
||||
Rubinius::ConfigurationVariables.instance.show_help(STDOUT)
|
||||
exit 0
|
||||
end
|
||||
|
||||
exit 0 if Rubinius::Config["config.print"]
|
||||
|
||||
if str = Rubinius::Config['tool.require']
|
||||
begin
|
||||
require str
|
||||
rescue LoadError
|
||||
STDERR.puts "Unable to require file for tool: '#{str}'"
|
||||
end
|
||||
end
|
||||
|
||||
if Rubinius::Config['profile'] || Rubinius::Config['jit.profile']
|
||||
@load_profiler = true
|
||||
end
|
||||
|
||||
if @check_syntax
|
||||
check_syntax
|
||||
end
|
||||
end
|
||||
|
||||
# Sets $0 ($PROGRAM_NAME) without changing the process title
|
||||
def set_program_name(name)
|
||||
Rubinius::Globals.set! :$0, name
|
||||
end
|
||||
private :set_program_name
|
||||
|
||||
def handle_rubyopt(options)
|
||||
if env_opts = ENV['RUBYOPT']
|
||||
Rubinius::Logger.system.write "RUBYOPT: #{env_opts}"
|
||||
|
||||
options.start_parsing
|
||||
env_opts = env_opts.strip.split(/\s+/)
|
||||
|
||||
until env_opts.empty?
|
||||
entry = env_opts.shift
|
||||
|
||||
unless entry[0] == ?-
|
||||
entry = "-#{entry}"
|
||||
end
|
||||
|
||||
opt, arg, rest = options.split entry, 2
|
||||
|
||||
options.process env_opts, entry, opt, arg
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Update the load paths with any -I arguments.
|
||||
def load_paths
|
||||
@stage = "setting load paths"
|
||||
|
||||
@load_paths.each do |path|
|
||||
path.split(":").reverse_each do |path|
|
||||
# We used to run expand_path on path first, but MRI
|
||||
# doesn't and it breaks some code if we do.
|
||||
$LOAD_PATH.unshift(path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def deprecations
|
||||
@stage = "showing deprecations"
|
||||
|
||||
return unless @deprecations
|
||||
|
||||
if Rubinius::DEPRECATIONS.size > 0
|
||||
Rubinius::DEPRECATIONS.each do |desc, alt|
|
||||
STDERR.puts <<-EOM
|
||||
Deprecation: #{desc}
|
||||
Alternative: #{alt}
|
||||
|
||||
EOM
|
||||
end
|
||||
|
||||
exit 1
|
||||
else
|
||||
STDERR.puts "Rubinius #{Rubinius::VERSION} has no deprecation notices"
|
||||
exit 0
|
||||
end
|
||||
end
|
||||
|
||||
def load_compiler
|
||||
@stage = "loading the compiler"
|
||||
|
||||
begin
|
||||
CodeLoader.load_compiler
|
||||
rescue LoadError => e
|
||||
message = "Unable to load the bytecode compiler."
|
||||
Rubinius::Logger.system.error message
|
||||
Rubinius::Logger.system.error(
|
||||
"Please run 'rake' or 'rake install' to rebuild the compiler.")
|
||||
Rubinius::Logger.log_exception message, e
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
def debugger
|
||||
@stage = "running the debugger"
|
||||
|
||||
if Rubinius::Config['debug']
|
||||
if custom = Loader.debugger
|
||||
custom.call
|
||||
else
|
||||
require 'rubinius/debugger'
|
||||
Rubinius::Debugger.start
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def rubygems
|
||||
@stage = "loading Rubygems"
|
||||
|
||||
CodeLoader.load_rubygems if @enable_gems
|
||||
end
|
||||
|
||||
def gemfile
|
||||
@stage = "loading Gemfile"
|
||||
|
||||
if @load_gemfile
|
||||
CodeLoader.load_rubygems
|
||||
require 'bundler/setup'
|
||||
@load_gemfile = false
|
||||
end
|
||||
end
|
||||
|
||||
def profiler
|
||||
return unless @load_profiler
|
||||
@stage = "loading profiler"
|
||||
|
||||
require 'profile'
|
||||
end
|
||||
|
||||
# Require any -r arguments
|
||||
def requires
|
||||
@stage = "requiring command line files"
|
||||
|
||||
@requires.each { |file| require file }
|
||||
end
|
||||
|
||||
# Evaluate any -e arguments
|
||||
def evals
|
||||
return if @evals.empty?
|
||||
|
||||
@repl = false
|
||||
@stage = "evaluating command line code"
|
||||
|
||||
Dir.chdir(@directory) if @directory
|
||||
|
||||
if @input_loop
|
||||
while gets
|
||||
$F = $_.split if @input_loop_split
|
||||
eval @evals.join("\n"), TOPLEVEL_BINDING, "-e", 1
|
||||
print $_ if @input_loop_print
|
||||
end
|
||||
else
|
||||
eval @evals.join("\n"), TOPLEVEL_BINDING, "-e", 1
|
||||
end
|
||||
end
|
||||
|
||||
# Run the script passed on the command line
|
||||
def script
|
||||
return unless @script and @evals.empty?
|
||||
|
||||
@repl = false
|
||||
|
||||
handle_simple_options(ARGV) if @simple_options
|
||||
|
||||
@stage = "running #{@script}"
|
||||
Dir.chdir @directory if @directory
|
||||
|
||||
if File.exist? @script
|
||||
if IO.read(@script, 6) == "!RBIX\n"
|
||||
raise LoadError, "'#{@script}' is not a Ruby source file"
|
||||
end
|
||||
else
|
||||
if @script.suffix?(".rb")
|
||||
raise LoadError, "unable to find '#{@script}'"
|
||||
else
|
||||
command = File.join @lib_bin, "#{@script}.rb"
|
||||
unless File.exist? command
|
||||
raise LoadError, "unable to find Rubinius command '#{@script}'"
|
||||
else
|
||||
@script = command
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
set_program_name @script
|
||||
CodeLoader.new(@script).load_script
|
||||
end
|
||||
|
||||
#Check Ruby syntax of source
|
||||
def check_syntax
|
||||
parser_class = Rubinius::ToolSets::Runtime::Melbourne
|
||||
|
||||
if @script
|
||||
if File.exist?(@script)
|
||||
parser = parser_class.new @script, 1, []
|
||||
parser.parse_file
|
||||
else
|
||||
puts "rbx: Unable to find file -- #{@script} (LoadError)"
|
||||
exit 1
|
||||
end
|
||||
elsif not @evals.empty?
|
||||
parser = parser_class.new('-e', 1, [])
|
||||
parser.parse_string(@evals.join("\n"))
|
||||
else
|
||||
parser = parser_class.new('-', 1, [])
|
||||
parser.parse_string(STDIN.read)
|
||||
end
|
||||
|
||||
puts "Syntax OK"
|
||||
exit 0
|
||||
rescue SyntaxError => e
|
||||
show_syntax_errors(parser.syntax_errors)
|
||||
exit 1
|
||||
end
|
||||
|
||||
# Run IRB unless we were passed -e, -S arguments or a script to run.
|
||||
def repl
|
||||
return unless @repl
|
||||
|
||||
@stage = "running IRB"
|
||||
|
||||
# Check if a different REPL is set.
|
||||
repl = ENV["RBX_REPL"] || "irb"
|
||||
|
||||
if Terminal
|
||||
load File.join(Rubinius::GEMS_PATH, "bin", repl)
|
||||
else
|
||||
set_program_name "(eval)"
|
||||
execute_script "p #{STDIN.read}"
|
||||
end
|
||||
end
|
||||
|
||||
def execute_script(script)
|
||||
eval script, TOPLEVEL_BINDING
|
||||
end
|
||||
|
||||
def run_at_exits
|
||||
until AtExit.empty?
|
||||
begin
|
||||
AtExit.shift.call
|
||||
rescue SystemExit => e
|
||||
@exit_code = e.status
|
||||
# We recurse down so that future at_exits
|
||||
# see the current exception
|
||||
run_at_exits
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def flush_stdio
|
||||
STDOUT.flush unless STDOUT.closed?
|
||||
STDERR.flush unless STDERR.closed?
|
||||
end
|
||||
|
||||
def exit_with_exception(e)
|
||||
@exit_code = 1
|
||||
Rubinius::Logger.log_exception "An exception occurred #{@stage}", e
|
||||
end
|
||||
|
||||
# Cleanup and at_exit processing.
|
||||
def epilogue
|
||||
@stage = "running at_exit handlers"
|
||||
|
||||
begin
|
||||
Signal.run_handler Signal::Names["EXIT"]
|
||||
rescue SystemExit => e
|
||||
@exit_code = e.status
|
||||
end
|
||||
|
||||
run_at_exits
|
||||
|
||||
flush_stdio
|
||||
|
||||
rescue Object => e
|
||||
exit_with_exception e
|
||||
end
|
||||
|
||||
# Exit.
|
||||
def done
|
||||
# check that this is a valid exit rather than failing to process
|
||||
# unwinding properly.
|
||||
#
|
||||
# TODO: this code is pretty gross, nice object inspectors, please.
|
||||
thread_state = Rubinius.thread_state
|
||||
reason = thread_state[0]
|
||||
unless reason == :none
|
||||
STDERR.puts "\nERROR: the VM is exiting improperly #{@stage}"
|
||||
STDERR.puts "intended operation: #{reason.inspect}"
|
||||
STDERR.puts "associated value: #{thread_state[1].inspect}"
|
||||
|
||||
destination = thread_state[2]
|
||||
|
||||
if destination
|
||||
method = destination.method
|
||||
|
||||
STDERR.puts "destination scope:"
|
||||
STDERR.puts " method: #{method.name} at #{method.file}:#{method.first_line}"
|
||||
STDERR.puts " module: #{destination.module.name}"
|
||||
STDERR.puts " block: #{destination.block}" if destination.block
|
||||
else
|
||||
STDERR.puts "destination scope: unknown"
|
||||
end
|
||||
|
||||
if reason == :catch_throw
|
||||
STDERR.puts "throw destination: #{thread_state[4].inspect}"
|
||||
end
|
||||
|
||||
if exception = thread_state[3]
|
||||
begin
|
||||
exception.render
|
||||
rescue Exception => e
|
||||
STDERR.puts "Unable to render backtrace: #{e.message} (#{e.class})"
|
||||
STDERR.puts "Raw backtrace data:"
|
||||
|
||||
exception.locations.each do |loc|
|
||||
p [loc.file, loc.line]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@exit_code = 1
|
||||
end
|
||||
|
||||
Process.exit! @exit_code
|
||||
end
|
||||
|
||||
def handle_exception(e)
|
||||
case e
|
||||
when SystemExit
|
||||
@exit_code = e.status
|
||||
when SyntaxError
|
||||
@exit_code = 1
|
||||
|
||||
STDERR.puts e.awesome_backtrace.show
|
||||
STDERR.puts
|
||||
|
||||
show_syntax_error(e)
|
||||
when Interrupt
|
||||
exit_with_exception e
|
||||
when SignalException
|
||||
Signal.trap(e.signo, "SIG_DFL")
|
||||
Process.kill e.signo, Process.pid
|
||||
when nil
|
||||
# what?
|
||||
else
|
||||
exit_with_exception e
|
||||
end
|
||||
rescue Object => e
|
||||
exit_with_exception e
|
||||
ensure
|
||||
epilogue
|
||||
end
|
||||
|
||||
# Orchestrate everything.
|
||||
def main
|
||||
preamble
|
||||
system_load_path
|
||||
signals
|
||||
load_compiler
|
||||
preload
|
||||
detect_alias
|
||||
options
|
||||
load_paths
|
||||
deprecations
|
||||
rubygems
|
||||
gemfile
|
||||
debugger
|
||||
profiler
|
||||
requires
|
||||
evals
|
||||
script
|
||||
repl
|
||||
|
||||
rescue Object => e
|
||||
handle_exception e
|
||||
else
|
||||
# We do this, run epilogue both in the rescue blocks and also here,
|
||||
# so that at_exit{} hooks can read $!.
|
||||
epilogue
|
||||
ensure
|
||||
done
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
167
gxg/core/location.rb
Normal file
167
gxg/core/location.rb
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
module Rubinius
|
||||
class Location
|
||||
|
||||
attr_reader :method
|
||||
attr_reader :name
|
||||
|
||||
attr_reader :ip
|
||||
attr_reader :variables
|
||||
attr_reader :lexical_scope
|
||||
|
||||
def self.of_closest_ruby_method
|
||||
Rubinius.primitive :location_of_closest_ruby_method
|
||||
raise PrimitiveFailure, "Location.of_closest_ruby_method primitive failed"
|
||||
end
|
||||
|
||||
def is_block
|
||||
@flags & 1 == 1
|
||||
end
|
||||
|
||||
def is_jit
|
||||
@flags & 2 == 2
|
||||
end
|
||||
|
||||
def ip_on_current?
|
||||
@flags & 4 == 4
|
||||
end
|
||||
|
||||
def inlined?
|
||||
@name.nil?
|
||||
end
|
||||
|
||||
def describe_receiver
|
||||
# We can't call @receiver.class because it might be overriden (or in
|
||||
# the case of Mocha, mocked out) and easily causes stack overflows
|
||||
klass = Rubinius::Type.object_class @receiver
|
||||
|
||||
if @method_module.equal?(Kernel)
|
||||
"Kernel."
|
||||
elsif Rubinius::Type.object_kind_of?(@method_module, Class) and
|
||||
ao = Rubinius::Type.singleton_class_object(@method_module)
|
||||
"#{ao}."
|
||||
elsif @method_module and @method_module != klass
|
||||
"#{@method_module}(#{klass})#"
|
||||
else
|
||||
"#{klass}#"
|
||||
end
|
||||
end
|
||||
|
||||
def describe
|
||||
if is_block
|
||||
"{ } in #{describe_receiver}#{describe_method}"
|
||||
else
|
||||
"#{describe_receiver}#{describe_method}"
|
||||
end
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<Rubinius::Location #{describe}>"
|
||||
end
|
||||
|
||||
def describe_method
|
||||
if is_block or @name == @method.name
|
||||
@name.to_s
|
||||
elsif !@name # inlined methods have no name
|
||||
@method.name.to_s
|
||||
else
|
||||
"#{@name} (#{@method.name})"
|
||||
end
|
||||
end
|
||||
|
||||
# Current line being executed by the VM.
|
||||
def line
|
||||
return 0 unless @method
|
||||
|
||||
ip = @ip
|
||||
ip -= 1 unless ip_on_current?
|
||||
|
||||
return @method.first_line unless ip > 0
|
||||
|
||||
@method.line_from_ip(ip)
|
||||
end
|
||||
|
||||
def file(relative_to=nil)
|
||||
path = @method.active_path
|
||||
|
||||
if relative_to
|
||||
# Be sure we can bail out if something doesn't work and still
|
||||
# show something.
|
||||
begin
|
||||
full = File.expand_path path
|
||||
if full.prefix? relative_to
|
||||
return full[relative_to.size+1..-1]
|
||||
else
|
||||
return path
|
||||
end
|
||||
rescue StandardError
|
||||
return path
|
||||
end
|
||||
end
|
||||
|
||||
path
|
||||
end
|
||||
|
||||
##
|
||||
# Place in the source that this method was created at.
|
||||
def position(relative_to=nil)
|
||||
l = line()
|
||||
|
||||
if l == 0
|
||||
if @ip < 0
|
||||
file(relative_to)
|
||||
else
|
||||
"#{file(relative_to)}+#{@ip}"
|
||||
end
|
||||
else
|
||||
"#{file(relative_to)}:#{l}"
|
||||
end
|
||||
end
|
||||
|
||||
# See Rubinius::Backtrace.backtrace
|
||||
class Missing
|
||||
def describe
|
||||
"*** Missing backtrace! Did the VM not create one? ***"
|
||||
end
|
||||
|
||||
def describe_receiver
|
||||
"missing_backtrace_unknown_receiver"
|
||||
end
|
||||
|
||||
def describe_method
|
||||
"missing_backtrace_unknown_location"
|
||||
end
|
||||
|
||||
def method
|
||||
"missing_backtrace_unknown_location"
|
||||
end
|
||||
|
||||
def line
|
||||
"<no line>"
|
||||
end
|
||||
|
||||
def position(dir=nil)
|
||||
"<no file>"
|
||||
end
|
||||
|
||||
def ip
|
||||
0
|
||||
end
|
||||
|
||||
def name
|
||||
"missing_backtrace_unknown_name"
|
||||
end
|
||||
|
||||
def is_block
|
||||
false
|
||||
end
|
||||
|
||||
def is_jit
|
||||
false
|
||||
end
|
||||
|
||||
def inlined?
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
99
gxg/core/logger.rb
Normal file
99
gxg/core/logger.rb
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
module Rubinius
|
||||
class Logger
|
||||
attr_accessor :name
|
||||
attr_accessor :format
|
||||
|
||||
def self.allocate
|
||||
Rubinius.primitive :logger_allocate
|
||||
raise PrimitiveFailure, "Rubinius::Logger.allocate failed"
|
||||
end
|
||||
|
||||
def self.system
|
||||
@system_logger ||= new "system"
|
||||
end
|
||||
|
||||
def self.log_exception(message, e)
|
||||
log = system
|
||||
|
||||
exception = e
|
||||
exceptions = []
|
||||
|
||||
while exception
|
||||
exceptions.unshift exception
|
||||
exception = exception.cause
|
||||
end
|
||||
|
||||
color = STDERR.tty?
|
||||
|
||||
while exception = exceptions.shift
|
||||
if custom_trace = exception.custom_backtrace
|
||||
msg = "User-defined backtrace:"
|
||||
log.error msg
|
||||
STDERR.puts msg
|
||||
|
||||
custom_trace.reverse_each do |line|
|
||||
log.error line
|
||||
STDERR.puts line
|
||||
end
|
||||
|
||||
msg = "Backtrace: "
|
||||
log.error msg
|
||||
STDERR.puts "\n", msg, "\n"
|
||||
end
|
||||
|
||||
exception.backtrace.reverse_each { |line| log.error line }
|
||||
STDERR.puts exception.awesome_backtrace.show("\n", color)
|
||||
|
||||
msg = "#{exception.message} (#{exception.class})"
|
||||
log.error msg
|
||||
STDERR.puts "\n", msg
|
||||
|
||||
unless exceptions.empty?
|
||||
msg = "Causing:"
|
||||
log.error msg
|
||||
STDERR.puts "\n", msg, "\n"
|
||||
end
|
||||
end
|
||||
|
||||
log.error message
|
||||
STDERR.puts "\n", message
|
||||
end
|
||||
|
||||
def initialize(name)
|
||||
@name = name
|
||||
@format = "#{name}: %s"
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def write(message)
|
||||
Rubinius.primitive :logger_write
|
||||
raise PrimitiveFailure, "Rubinius::Logger#write primitive failed"
|
||||
end
|
||||
|
||||
def fatal(message)
|
||||
Rubinius.primitive :logger_fatal
|
||||
raise PrimitiveFailure, "Rubinius::Logger#fatal primitive failed"
|
||||
end
|
||||
|
||||
def error(message)
|
||||
Rubinius.primitive :logger_error
|
||||
raise PrimitiveFailure, "Rubinius::Logger#error primitive failed"
|
||||
end
|
||||
|
||||
def warn(message)
|
||||
Rubinius.primitive :logger_warn
|
||||
raise PrimitiveFailure, "Rubinius::Logger#warn primitive failed"
|
||||
end
|
||||
|
||||
def info(message)
|
||||
Rubinius.primitive :logger_info
|
||||
raise PrimitiveFailure, "Rubinius::Logger#info primitive failed"
|
||||
end
|
||||
|
||||
def debug(message)
|
||||
Rubinius.primitive :logger_debug
|
||||
raise PrimitiveFailure, "Rubinius::Logger#debug primitive failed"
|
||||
end
|
||||
end
|
||||
end
|
||||
134
gxg/core/lookup_table.rb
Normal file
134
gxg/core/lookup_table.rb
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
##
|
||||
# A LookupTable is similar to a Hash in that keys are used to set and
|
||||
# reference values. However, unlike Hash, whether a key matches an
|
||||
# entry in LookupTable is determined by using the == comparison operator
|
||||
# in C code. In effect, two keys are equal if they are the same pointer.
|
||||
#
|
||||
# NOTE: the value used to determine the bin is the "pointer" value of
|
||||
# key >> 2. This makes it possible to calculate the bin from Ruby with
|
||||
# the same result as in C. For example:
|
||||
#
|
||||
# l = LookupTable.new
|
||||
# loc = :a.hash & (l.bins - 1) # => 12
|
||||
# class LookupTable
|
||||
# def show
|
||||
# @values
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# l[:a] = 1
|
||||
# l.show # => #<Tuple: nil, ..., nil, #<Bucket: @key=>:a, @value=>1,
|
||||
# @next=>nil>, nil, nil, nil>
|
||||
#
|
||||
# where ... is ten "nil, " entries.
|
||||
#
|
||||
# LookupTable is intended to be used with Symbol or Fixnum keys because
|
||||
# it is a strict identity map. Usage of Strings as keys will likely result
|
||||
# in strange behavior.
|
||||
# LookupTable is NOT intended to be used generally like Hash.
|
||||
|
||||
module Rubinius
|
||||
class LookupTable
|
||||
include Enumerable
|
||||
|
||||
class Bucket
|
||||
attr_reader :key
|
||||
attr_reader :value
|
||||
attr_reader :next
|
||||
end
|
||||
|
||||
attr_reader :values
|
||||
attr_reader :bins
|
||||
|
||||
attr_reader_specific :entries, :size
|
||||
alias_method :length, :size
|
||||
|
||||
def self.allocate
|
||||
Rubinius.primitive :lookuptable_allocate
|
||||
raise PrimitiveFailure, "LookupTable.allocate primitive failed"
|
||||
end
|
||||
|
||||
def initialize(hash=nil)
|
||||
return unless hash
|
||||
hash.each do |k, v|
|
||||
self[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def duplicate
|
||||
Rubinius.primitive :lookuptable_duplicate
|
||||
raise PrimitiveFailure, "LookupTable#duplicate primitive failed"
|
||||
end
|
||||
|
||||
def dup
|
||||
copy = duplicate
|
||||
Rubinius.privately do
|
||||
copy.initialize_copy self
|
||||
end
|
||||
copy
|
||||
end
|
||||
|
||||
alias_method :clone, :dup
|
||||
|
||||
def fetch(key, return_on_failure)
|
||||
Rubinius.primitive :lookuptable_fetch
|
||||
raise PrimitiveFailure, "LookupTable#fetch primitive failed"
|
||||
end
|
||||
|
||||
def key?(key)
|
||||
Rubinius.primitive :lookuptable_has_key
|
||||
raise PrimitiveFailure, "LookupTable#key? primitive failed"
|
||||
end
|
||||
|
||||
alias_method :has_key?, :key?
|
||||
alias_method :include?, :key?
|
||||
alias_method :member?, :key?
|
||||
|
||||
def delete(key)
|
||||
Rubinius.primitive :lookuptable_delete
|
||||
raise PrimitiveFailure, "LookupTable#delete primitive failed"
|
||||
end
|
||||
|
||||
def keys
|
||||
Rubinius.primitive :lookuptable_keys
|
||||
raise PrimitiveFailure, "LookupTable#keys primitive failed"
|
||||
end
|
||||
|
||||
def values
|
||||
Rubinius.primitive :lookuptable_values
|
||||
raise PrimitiveFailure, "LookupTable#values primitive failed"
|
||||
end
|
||||
|
||||
def each
|
||||
max = @bins
|
||||
i = 0
|
||||
vals = @values
|
||||
|
||||
while i < max
|
||||
entry = vals.at(i)
|
||||
|
||||
while entry
|
||||
yield entry.key, entry.value
|
||||
entry = entry.next
|
||||
end
|
||||
i += 1
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
def empty?
|
||||
@entries == 0
|
||||
end
|
||||
|
||||
def to_s
|
||||
"#<#{self.class}:0x#{object_id.to_s(16)} #{entries} entries>"
|
||||
end
|
||||
|
||||
def inspect
|
||||
ents = collect { |k, v| " #{k.inspect}=>#{v.inspect}" }.join(",")
|
||||
"#<#{self.class}#{ents}>"
|
||||
end
|
||||
end
|
||||
end
|
||||
147
gxg/core/lru_cache.rb
Normal file
147
gxg/core/lru_cache.rb
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
module Rubinius
|
||||
class LRUCache
|
||||
class Entry
|
||||
attr_reader :hits
|
||||
attr_reader :key
|
||||
attr_accessor :value
|
||||
attr_accessor :next_entry
|
||||
attr_accessor :prev_entry
|
||||
|
||||
def initialize(key, value)
|
||||
@key = key
|
||||
@value = value
|
||||
@hits = 0
|
||||
@next_entry = nil
|
||||
@prev_entry = nil
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def insert_after(entry)
|
||||
nxt = entry.next_entry
|
||||
|
||||
@prev_entry = entry
|
||||
@next_entry = nxt
|
||||
|
||||
entry.next_entry = self
|
||||
nxt.prev_entry = self if nxt
|
||||
end
|
||||
|
||||
def insert_before(entry)
|
||||
prev = entry.prev_entry
|
||||
|
||||
@prev_entry = prev
|
||||
@next_entry = entry
|
||||
|
||||
entry.prev_entry = self
|
||||
prev.next_entry = self if prev
|
||||
end
|
||||
|
||||
def detach!
|
||||
@next_entry.prev_entry = @prev_entry if @next_entry
|
||||
@prev_entry.next_entry = @next_entry if @prev_entry
|
||||
|
||||
@next_entry = nil
|
||||
@prev_entry = nil
|
||||
end
|
||||
|
||||
def become_first!
|
||||
@prev_entry = nil
|
||||
end
|
||||
|
||||
def inc!
|
||||
@hits += 1
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(total)
|
||||
@cache = {}
|
||||
@total = total
|
||||
@current = 0
|
||||
|
||||
@head = Entry.new(nil, nil)
|
||||
@tail = Entry.new(nil, nil)
|
||||
|
||||
@tail.insert_after(@head)
|
||||
|
||||
@misses = 0
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
attr_reader :current
|
||||
attr_reader :misses
|
||||
|
||||
def clear!
|
||||
Rubinius.synchronize(self) do
|
||||
@cache = {}
|
||||
@current = 0
|
||||
|
||||
@head = Entry.new(nil, nil, -1)
|
||||
@tail = Entry.new(nil, nil, -2)
|
||||
|
||||
@tail.insert_after(@head)
|
||||
end
|
||||
end
|
||||
|
||||
def explain
|
||||
entry = @head.next_entry
|
||||
while entry != @tail
|
||||
str, layout = entry.key
|
||||
puts "hits: #{entry.hits}"
|
||||
puts "layout: #{layout.inspect}"
|
||||
puts "<STRING>"
|
||||
puts str
|
||||
puts "</STRING>"
|
||||
|
||||
entry = entry.next_entry
|
||||
end
|
||||
end
|
||||
|
||||
def retrieve(key)
|
||||
Rubinius.synchronize(self) do
|
||||
if entry = @cache[key]
|
||||
entry.inc!
|
||||
|
||||
entry.detach!
|
||||
entry.insert_before @tail
|
||||
|
||||
return entry.value
|
||||
end
|
||||
|
||||
@misses += 1
|
||||
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def set(key, value)
|
||||
Rubinius.synchronize(self) do
|
||||
if entry = @cache[key]
|
||||
entry.value = value
|
||||
|
||||
entry.detach!
|
||||
entry.insert_before @tail
|
||||
|
||||
return value
|
||||
end
|
||||
|
||||
if @current == @total
|
||||
entry = @head.next_entry
|
||||
|
||||
entry.detach!
|
||||
|
||||
@cache.delete entry.key
|
||||
else
|
||||
@current += 1
|
||||
end
|
||||
|
||||
entry = Entry.new(key, value)
|
||||
|
||||
entry.insert_before @tail
|
||||
|
||||
@cache[key] = entry
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
35
gxg/core/main.rb
Normal file
35
gxg/core/main.rb
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
class << MAIN
|
||||
def include(*mods)
|
||||
Rubinius.privately do
|
||||
Object.include(*mods)
|
||||
end
|
||||
end
|
||||
|
||||
def public(*methods)
|
||||
Rubinius.privately do
|
||||
Object.public(*methods)
|
||||
end
|
||||
|
||||
Object
|
||||
end
|
||||
|
||||
def private(*methods)
|
||||
Rubinius.privately do
|
||||
Object.private(*methods)
|
||||
end
|
||||
|
||||
Object
|
||||
end
|
||||
|
||||
def define_method(*args, &block)
|
||||
Rubinius.privately do
|
||||
Object.define_method(*args, &block)
|
||||
end
|
||||
end
|
||||
|
||||
def to_s
|
||||
"main"
|
||||
end
|
||||
|
||||
alias_method :inspect, :to_s
|
||||
end
|
||||
1227
gxg/core/marshal.rb
Normal file
1227
gxg/core/marshal.rb
Normal file
File diff suppressed because it is too large
Load diff
208
gxg/core/match_data.rb
Normal file
208
gxg/core/match_data.rb
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
class MatchData
|
||||
def begin(idx)
|
||||
if idx == 0
|
||||
start = @full.at(0)
|
||||
else
|
||||
start = @region.at(idx - 1).at(0)
|
||||
return nil if start == -1
|
||||
end
|
||||
m = Rubinius::Mirror.reflect @source
|
||||
m.byte_to_character_index start
|
||||
end
|
||||
|
||||
def end(idx)
|
||||
if idx == 0
|
||||
fin = @full.at(1)
|
||||
else
|
||||
fin = @region.at(idx - 1).at(1)
|
||||
return nil if fin == -1
|
||||
end
|
||||
m = Rubinius::Mirror.reflect @source
|
||||
m.byte_to_character_index fin
|
||||
end
|
||||
|
||||
def offset(idx)
|
||||
out = []
|
||||
out << self.begin(idx)
|
||||
out << self.end(idx)
|
||||
return out
|
||||
end
|
||||
|
||||
def [](idx, len = nil)
|
||||
return to_a[idx, len] if len
|
||||
|
||||
case idx
|
||||
when Fixnum
|
||||
if idx <= 0
|
||||
return matched_area() if idx == 0
|
||||
return to_a[idx]
|
||||
elsif idx <= @region.size
|
||||
tup = @region[idx - 1]
|
||||
|
||||
x = tup.at(0)
|
||||
return nil if x == -1
|
||||
|
||||
y = tup.at(1)
|
||||
return @source.byteslice(x, y-x)
|
||||
end
|
||||
when String
|
||||
if @regexp.name_table
|
||||
return self[idx.to_sym]
|
||||
end
|
||||
raise IndexError, "Unknown named group '#{idx}'"
|
||||
when Symbol
|
||||
if @regexp.name_table
|
||||
if nums = @regexp.name_table[idx]
|
||||
nums.reverse_each do |num|
|
||||
val = self[num]
|
||||
return val if val
|
||||
end
|
||||
return nil
|
||||
end
|
||||
end
|
||||
raise IndexError, "Unknown named group '#{idx}'"
|
||||
end
|
||||
|
||||
return to_a[idx]
|
||||
end
|
||||
|
||||
attr_reader :regexp
|
||||
|
||||
def ==(other)
|
||||
other.kind_of?(MatchData) &&
|
||||
string == other.string &&
|
||||
regexp == other.regexp &&
|
||||
captures == other.captures
|
||||
end
|
||||
alias_method :eql?, :==
|
||||
|
||||
def string
|
||||
@source.dup.freeze
|
||||
end
|
||||
|
||||
def source
|
||||
@source
|
||||
end
|
||||
|
||||
def full
|
||||
@full
|
||||
end
|
||||
|
||||
def length
|
||||
@region.fields + 1
|
||||
end
|
||||
|
||||
def captures
|
||||
out = Array.new(@region.fields)
|
||||
|
||||
idx = 0
|
||||
@region.each do |tup|
|
||||
x = tup.at(0)
|
||||
|
||||
if x == -1
|
||||
val = nil
|
||||
else
|
||||
y = tup.at(1)
|
||||
val = @source.byteslice(x, y-x)
|
||||
end
|
||||
|
||||
out[idx] = val
|
||||
idx += 1
|
||||
end
|
||||
|
||||
return out
|
||||
end
|
||||
|
||||
def names
|
||||
@regexp.names
|
||||
end
|
||||
|
||||
def pre_match
|
||||
return @source.byteslice(0, 0) if @full.at(0) == 0
|
||||
nd = @full.at(0) - 1
|
||||
@source.byteslice(0, nd+1)
|
||||
end
|
||||
|
||||
def pre_match_from(idx)
|
||||
return @source.byteslice(0, 0) if @full.at(0) == 0
|
||||
nd = @full.at(0) - 1
|
||||
@source.byteslice(idx, nd-idx+1)
|
||||
end
|
||||
|
||||
def collapsing?
|
||||
@full[0] == @full[1]
|
||||
end
|
||||
|
||||
def post_match
|
||||
nd = @source.bytesize - 1
|
||||
st = @full.at(1)
|
||||
@source.byteslice(st, nd-st+1)
|
||||
end
|
||||
|
||||
def inspect
|
||||
capts = captures
|
||||
if capts.empty?
|
||||
"#<MatchData \"#{matched_area}\">"
|
||||
else
|
||||
idx = 0
|
||||
capts.map! { |capture| "#{idx += 1}:#{capture.inspect}" }
|
||||
"#<MatchData \"#{matched_area}\" #{capts.join(" ")}>"
|
||||
end
|
||||
end
|
||||
|
||||
def select
|
||||
unless block_given?
|
||||
raise LocalJumpError, "no block given"
|
||||
end
|
||||
|
||||
out = []
|
||||
ma = matched_area()
|
||||
out << ma if yield ma
|
||||
|
||||
each_capture do |str|
|
||||
if yield(str)
|
||||
out << str
|
||||
end
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
alias_method :size, :length
|
||||
|
||||
def to_a
|
||||
ary = captures()
|
||||
ary.unshift matched_area()
|
||||
return ary
|
||||
end
|
||||
|
||||
def values_at(*indexes)
|
||||
indexes.map { |i| self[i] }.flatten(1)
|
||||
end
|
||||
|
||||
def matched_area
|
||||
x = @full.at(0)
|
||||
y = @full.at(1)
|
||||
@source.byteslice(x, y-x)
|
||||
end
|
||||
|
||||
alias_method :to_s, :matched_area
|
||||
private :matched_area
|
||||
|
||||
def get_capture(num)
|
||||
x, y = @region[num]
|
||||
return nil if !y or x == -1
|
||||
|
||||
return @source.byteslice(x, y-x)
|
||||
end
|
||||
|
||||
private :get_capture
|
||||
|
||||
def each_capture
|
||||
@region.each do |tup|
|
||||
x, y = *tup
|
||||
yield @source.byteslice(x, y-x)
|
||||
end
|
||||
end
|
||||
|
||||
private :each_capture
|
||||
end
|
||||
250
gxg/core/math.rb
Normal file
250
gxg/core/math.rb
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
module Math
|
||||
def atan2(y, x)
|
||||
y = Rubinius::Type.coerce_to_float y
|
||||
x = Rubinius::Type.coerce_to_float x
|
||||
FFI::Platform::Math.atan2 y, x
|
||||
end
|
||||
module_function :atan2
|
||||
|
||||
def cos(x)
|
||||
FFI::Platform::Math.cos Rubinius::Type.coerce_to_float(x)
|
||||
end
|
||||
module_function :cos
|
||||
|
||||
def sin(x)
|
||||
FFI::Platform::Math.sin Rubinius::Type.coerce_to_float(x)
|
||||
end
|
||||
module_function :sin
|
||||
|
||||
def tan(x)
|
||||
FFI::Platform::Math.tan Rubinius::Type.coerce_to_float(x)
|
||||
end
|
||||
module_function :tan
|
||||
|
||||
def atan(x)
|
||||
FFI::Platform::Math.atan Rubinius::Type.coerce_to_float(x)
|
||||
end
|
||||
module_function :atan
|
||||
|
||||
def cosh(x)
|
||||
FFI::Platform::Math.cosh Rubinius::Type.coerce_to_float(x)
|
||||
end
|
||||
module_function :cosh
|
||||
|
||||
def sinh(x)
|
||||
FFI::Platform::Math.sinh Rubinius::Type.coerce_to_float(x)
|
||||
end
|
||||
module_function :sinh
|
||||
|
||||
def tanh(x)
|
||||
FFI::Platform::Math.tanh Rubinius::Type.coerce_to_float(x)
|
||||
end
|
||||
module_function :tanh
|
||||
|
||||
def asinh(x)
|
||||
FFI::Platform::Math.asinh Rubinius::Type.coerce_to_float(x)
|
||||
end
|
||||
module_function :asinh
|
||||
|
||||
def atanh(x)
|
||||
x = Rubinius::Type.coerce_to_float(x)
|
||||
raise DomainError, 'atanh' unless x.abs <= 1.0
|
||||
|
||||
FFI::Platform::POSIX.errno = 0
|
||||
|
||||
ret = FFI::Platform::Math.atanh x
|
||||
begin
|
||||
Errno.handle
|
||||
|
||||
# Linux sets errno to ERANGE, but it should be EDOM
|
||||
rescue Errno::ERANGE
|
||||
raise Errno::EDOM
|
||||
end
|
||||
|
||||
ret
|
||||
end
|
||||
module_function :atanh
|
||||
|
||||
def exp(x)
|
||||
FFI::Platform::Math.exp Rubinius::Type.coerce_to_float(x)
|
||||
end
|
||||
module_function :exp
|
||||
|
||||
def frexp(x)
|
||||
x = Rubinius::Type.coerce_to_float(x)
|
||||
FFI::MemoryPointer.new :int do |exp|
|
||||
result = FFI::Platform::Math.frexp x, exp
|
||||
[result, exp.read_int]
|
||||
end
|
||||
end
|
||||
module_function :frexp
|
||||
|
||||
def ldexp(x, n)
|
||||
if n.kind_of? Float and n.nan?
|
||||
raise RangeError, "NaN cannot be converted to an Integer"
|
||||
end
|
||||
|
||||
n = Rubinius::Type.coerce_to(n, Integer, :to_int)
|
||||
|
||||
FFI::Platform::Math.ldexp Rubinius::Type.coerce_to_float(x), n
|
||||
end
|
||||
module_function :ldexp
|
||||
|
||||
# Rubinius-specific, used in Marshal
|
||||
def modf(x)
|
||||
FFI::MemoryPointer.new :double do |integral|
|
||||
fractional = FFI::Platform::Math.modf x, integral
|
||||
[fractional, integral.read_double]
|
||||
end
|
||||
end
|
||||
module_function :modf
|
||||
|
||||
def hypot(x, y)
|
||||
x = Rubinius::Type.coerce_to_float x
|
||||
y = Rubinius::Type.coerce_to_float y
|
||||
FFI::Platform::Math.hypot x, y
|
||||
end
|
||||
module_function :hypot
|
||||
|
||||
def erf(x)
|
||||
FFI::Platform::Math.erf Rubinius::Type.coerce_to_float(x)
|
||||
end
|
||||
module_function :erf
|
||||
|
||||
def erfc(x)
|
||||
FFI::Platform::Math.erfc Rubinius::Type.coerce_to_float(x)
|
||||
end
|
||||
module_function :erfc
|
||||
|
||||
def asin(x)
|
||||
return Float::NAN if x.kind_of? Float and x.nan?
|
||||
|
||||
x = Rubinius::Type.coerce_to_float(x)
|
||||
raise DomainError, 'asin' unless x.abs <= 1.0
|
||||
FFI::Platform::Math.asin x
|
||||
end
|
||||
module_function :asin
|
||||
|
||||
def acos(x)
|
||||
return Float::NAN if x.kind_of? Float and x.nan?
|
||||
|
||||
x = Rubinius::Type.coerce_to_float(x)
|
||||
raise DomainError, 'acos' unless x.abs <= 1.0
|
||||
|
||||
FFI::Platform::POSIX.errno = 0
|
||||
|
||||
ret = FFI::Platform::Math.acos x
|
||||
Errno.handle
|
||||
ret
|
||||
end
|
||||
module_function :acos
|
||||
|
||||
def acosh(x)
|
||||
return Float::NAN if x.kind_of? Float and x.nan?
|
||||
|
||||
x = Rubinius::Type.coerce_to_float(x)
|
||||
raise DomainError, 'acosh' unless x >= 1.0
|
||||
FFI::Platform::Math.acosh x
|
||||
end
|
||||
module_function :acosh
|
||||
|
||||
def cbrt(x)
|
||||
x = Rubinius::Type.coerce_to_float x
|
||||
FFI::Platform::Math.cbrt x
|
||||
end
|
||||
module_function :cbrt
|
||||
|
||||
def gamma(x)
|
||||
x = Rubinius::Type.coerce_to_float x
|
||||
|
||||
# if x is negative zero, return -infinity
|
||||
return -Float::INFINITY if (1 / x) == -Float::INFINITY
|
||||
return Float::INFINITY if x == 0.0
|
||||
return Float::NAN if x.nan?
|
||||
|
||||
if sign = x.infinite?
|
||||
raise DomainError, "gamma" if sign == -1
|
||||
|
||||
return Float::INFINITY
|
||||
end
|
||||
|
||||
FFI::MemoryPointer.new :double do |integral|
|
||||
fractional = FFI::Platform::Math.modf x, integral
|
||||
int = integral.read_double.to_i
|
||||
|
||||
if fractional == 0.0
|
||||
raise DomainError, "gamma" if int < 0
|
||||
return FactorialTable[int - 1] if int <= FactorialTable.size
|
||||
end
|
||||
end
|
||||
|
||||
FFI::Platform::Math.tgamma x
|
||||
end
|
||||
module_function :gamma
|
||||
|
||||
def lgamma(x)
|
||||
x = Rubinius::Type.coerce_to_float x
|
||||
|
||||
if sign = x.infinite?
|
||||
raise DomainError, "lgamma" if sign == -1
|
||||
|
||||
return [Float::INFINITY, 1]
|
||||
end
|
||||
|
||||
FFI::MemoryPointer.new :int do |sign|
|
||||
sign.write_int 1
|
||||
result = FFI::Platform::Math.lgamma_r x, sign
|
||||
[result, sign.read_int]
|
||||
end
|
||||
end
|
||||
module_function :lgamma
|
||||
|
||||
def log(x, base=undefined)
|
||||
return Float::NAN if x.kind_of? Float and x.nan?
|
||||
|
||||
x = Rubinius::Type.coerce_to_float x
|
||||
raise DomainError, 'log' unless x >= 0.0
|
||||
return -Float::INFINITY if x == 0.0
|
||||
y = FFI::Platform::Math.log x
|
||||
unless undefined.equal? base
|
||||
base = Rubinius::Type.coerce_to_float base
|
||||
y /= log(base)
|
||||
end
|
||||
y
|
||||
end
|
||||
module_function :log
|
||||
|
||||
def log2(x)
|
||||
return Float::NAN if x.kind_of? Float and x.nan?
|
||||
|
||||
if x.kind_of? Bignum and x >= 0 and Float::MAX_EXP <= (bits = x.bit_length)
|
||||
bits -= Float::MANT_DIG
|
||||
x >>= bits
|
||||
else
|
||||
bits = 0
|
||||
end
|
||||
|
||||
x = Rubinius::Type.coerce_to_float(x)
|
||||
raise DomainError, 'log2' unless x >= 0.0
|
||||
FFI::Platform::Math.log2(x) + bits
|
||||
end
|
||||
module_function :log2
|
||||
|
||||
def log10(x)
|
||||
return Float::NAN if x.kind_of? Float and x.nan?
|
||||
|
||||
x = Rubinius::Type.coerce_to_float(x)
|
||||
raise DomainError, 'log10' unless x >= 0.0
|
||||
FFI::Platform::Math.log10 x
|
||||
end
|
||||
module_function :log10
|
||||
|
||||
def sqrt(x)
|
||||
return Float::NAN if x.kind_of? Float and x.nan?
|
||||
|
||||
x = Rubinius::Type.coerce_to_float(x)
|
||||
raise DomainError, 'sqrt' unless x >= 0.0
|
||||
FFI::Platform::Math.sqrt x
|
||||
end
|
||||
module_function :sqrt
|
||||
end
|
||||
362
gxg/core/method.rb
Normal file
362
gxg/core/method.rb
Normal file
|
|
@ -0,0 +1,362 @@
|
|||
##
|
||||
# Method objects are essentially detached, freely passed-around methods. The
|
||||
# Method is a copy of the method on the object at the time of extraction, so
|
||||
# if the method itself is changed, overridden, aliased or removed from the
|
||||
# object, the Method object still contains the old functionality. In addition,
|
||||
# the call itself is not in any way stored so it will reflect the state of the
|
||||
# object at the time of calling.
|
||||
#
|
||||
# Methods are normally bound to a particular object but it is possible to use
|
||||
# Method#unbind to create an UnboundMethod object for the purpose of
|
||||
# re-binding to a different object.
|
||||
|
||||
class Method
|
||||
|
||||
##
|
||||
# Takes and stores the receiver object, the method's bytecodes and the
|
||||
# Module that the method is defined in.
|
||||
|
||||
def initialize(receiver, defined_in, executable, name)
|
||||
@receiver = receiver
|
||||
@defined_in = defined_in
|
||||
@executable = executable
|
||||
@name = name
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
attr_reader :receiver
|
||||
attr_reader :defined_in
|
||||
attr_reader :executable
|
||||
|
||||
def name
|
||||
@name
|
||||
end
|
||||
|
||||
##
|
||||
# Method objects are equal if they have the same body and are bound to the
|
||||
# same object.
|
||||
|
||||
def ==(other)
|
||||
other.class == Method &&
|
||||
@receiver.equal?(other.receiver) &&
|
||||
(@executable == other.executable ||
|
||||
Rubinius::MethodEquality.method_equal_to_delegated_method_receiver?(self, other))
|
||||
end
|
||||
|
||||
alias_method :eql?, :==
|
||||
|
||||
##
|
||||
# Indication of how many arguments this method takes. It is defined so that
|
||||
# a non-negative Integer means the method takes that fixed amount of
|
||||
# arguments (up to 1024 currently.) A negative Integer is used to indicate a
|
||||
# variable argument count. The number is ((-n) - 1), where n is the number
|
||||
# of required args. Blocks are not counted.
|
||||
#
|
||||
# def foo(); end # arity => 0
|
||||
# def foo(a, b); end # arity => 2
|
||||
# def foo(a, &b); end # arity => 1
|
||||
# def foo(a, b = nil); end # arity => ((-1) -1) => -2
|
||||
# def foo(*a); end # arity => ((-0) -1) => -1
|
||||
# def foo(a, b, *c, &d); end # arity => ((-2) -1) => -3
|
||||
|
||||
def arity
|
||||
@executable.arity
|
||||
end
|
||||
|
||||
##
|
||||
# Execute the method. This works exactly like calling a method with the same
|
||||
# code on the receiver object. Arguments and a block can be supplied
|
||||
# optionally.
|
||||
|
||||
def call(*args, &block)
|
||||
@executable.invoke(@name, @defined_in, @receiver, args, block)
|
||||
end
|
||||
|
||||
alias_method :[], :call
|
||||
|
||||
##
|
||||
# String representation of this Method includes the method name, the Module
|
||||
# it is defined in and the Module that it was extracted from.
|
||||
|
||||
def inspect
|
||||
file, line = source_location()
|
||||
|
||||
if @executable.respond_to? :eval_source
|
||||
if src = @executable.eval_source
|
||||
src_line = src.split("\n")[line - 1]
|
||||
return "#<#{self.class}: #{@receiver.class}##{@name} (defined in #{@defined_in} from \"#{src_line}\")>"
|
||||
end
|
||||
end
|
||||
|
||||
if file
|
||||
"#<#{self.class}: #{@receiver.class}##{@name} (defined in #{@defined_in} at #{file}:#{line})>"
|
||||
else
|
||||
"#<#{self.class}: #{@receiver.class}##{@name} (defined in #{@defined_in})>"
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :to_s, :inspect
|
||||
|
||||
##
|
||||
# Location gives the file and line number of the start of this method's
|
||||
# definition.
|
||||
|
||||
def source_location
|
||||
if @executable.respond_to? :file
|
||||
[@executable.file.to_s, @executable.defined_line]
|
||||
elsif @executable.respond_to? :source_location
|
||||
@executable.source_location
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def source
|
||||
if @executable.respond_to? :eval_source
|
||||
return @executable.eval_source
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
def parameters
|
||||
if @executable.respond_to? :parameters
|
||||
@executable.parameters
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
def owner
|
||||
if @defined_in.class == Rubinius::IncludedModule
|
||||
@defined_in.module
|
||||
else
|
||||
@defined_in
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Returns a Proc object corresponding to this Method.
|
||||
|
||||
def to_proc
|
||||
Proc.from_method self
|
||||
end
|
||||
|
||||
##
|
||||
# Calls curry on the method in proc representation
|
||||
def curry(n = nil)
|
||||
to_proc.curry(n)
|
||||
end
|
||||
|
||||
##
|
||||
# Detach this Method from the receiver object it is bound to and create an
|
||||
# UnboundMethod object. Populates the UnboundMethod with the method data as
|
||||
# well as the Module it is defined in and the Module it was extracted from.
|
||||
#
|
||||
# See UnboundMethod for more information.
|
||||
|
||||
def unbind
|
||||
UnboundMethod.new(@defined_in, @executable, @receiver.class, @name)
|
||||
end
|
||||
|
||||
def super_method
|
||||
superclass = @defined_in.direct_superclass
|
||||
|
||||
if superclass
|
||||
mod, entry = superclass.lookup_method(@name)
|
||||
|
||||
if entry && entry.visibility != :undef
|
||||
return Method.new(@receiver, superclass, entry.method, @name)
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
def for_define_method(name, klass, callable_proc = nil)
|
||||
Rubinius::Type.bindable_method? self.defined_in, klass
|
||||
|
||||
scope = @executable.scope
|
||||
|
||||
if @executable.is_a? Rubinius::DelegatedMethod
|
||||
code = @executable
|
||||
else
|
||||
if callable_proc
|
||||
code = Rubinius::DelegatedMethod.new(name, :call, callable_proc, false)
|
||||
else
|
||||
code = Rubinius::DelegatedMethod.new(name, :call_on_instance, self.unbind, true)
|
||||
end
|
||||
end
|
||||
|
||||
[code, scope]
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# UnboundMethods are similar to Method objects except that they are not
|
||||
# connected to any particular object. They cannot be used standalone for this
|
||||
# reason, and must be bound to an object first. The object must be kind_of?
|
||||
# the Module in which this method was originally defined.
|
||||
#
|
||||
# UnboundMethods can be created in two ways: first, any existing Method object
|
||||
# can be sent #unbind to detach it from its current object and return an
|
||||
# UnboundMethod instead. Secondly, they can be directly created by calling
|
||||
# Module#instance_method with the desired method's name.
|
||||
#
|
||||
# The UnboundMethod is a copy of the method as it existed at the time of
|
||||
# creation. Any subsequent changes to the original will not affect any
|
||||
# existing UnboundMethods.
|
||||
|
||||
class UnboundMethod
|
||||
|
||||
##
|
||||
# Accepts and stores the Module where the method is defined in as well as
|
||||
# the CompiledCode itself. Class of the object the method was extracted
|
||||
# from can be given but will not be stored. This is always used internally
|
||||
# only.
|
||||
|
||||
def initialize(mod, executable, pulled_from, name)
|
||||
@defined_in = mod
|
||||
@executable = executable
|
||||
@pulled_from = pulled_from
|
||||
@name = name
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
attr_reader :executable
|
||||
attr_reader :defined_in
|
||||
|
||||
def name
|
||||
@name
|
||||
end
|
||||
|
||||
##
|
||||
# Convenience method for #binding to the given receiver object and calling
|
||||
# it with the optionally supplied arguments.
|
||||
|
||||
def call_on_instance(obj, *args, &block)
|
||||
@executable.invoke(@name, @defined_in, obj, args, block)
|
||||
end
|
||||
|
||||
##
|
||||
# UnboundMethod objects are equal if and only if they refer to the same
|
||||
# method. One may be an alias for the other or both for a common one. Both
|
||||
# must have been extracted from the same class or subclass. Two from
|
||||
# different subclasses will not be considered equal.
|
||||
|
||||
def ==(other)
|
||||
other.kind_of? UnboundMethod and
|
||||
@defined_in == other.defined_in and
|
||||
@executable == other.executable
|
||||
end
|
||||
|
||||
def hash
|
||||
@defined_in.hash ^ @executable.hash
|
||||
end
|
||||
|
||||
##
|
||||
# See Method#arity.
|
||||
|
||||
def arity
|
||||
@executable.arity
|
||||
end
|
||||
|
||||
##
|
||||
# Creates a new Method object by attaching this method to the supplied
|
||||
# receiver. One of the following must be true:
|
||||
# - the method was defined in a module
|
||||
# - the method is being bound to an instance of a subclass of the method's source
|
||||
# - the method is being bound to an instance of the same class
|
||||
|
||||
def bind(receiver)
|
||||
from_module = Rubinius::Type.object_kind_of? @defined_in, Module
|
||||
from_class = Rubinius::Type.object_kind_of? @defined_in, Class
|
||||
from_module_not_class = from_module && !from_class
|
||||
|
||||
unless Rubinius::Type.object_kind_of?(receiver, @defined_in) or from_module_not_class
|
||||
if Rubinius::Type.singleton_class_object(@defined_in)
|
||||
raise TypeError, "illegal attempt to rebind a singleton method to another object"
|
||||
end
|
||||
|
||||
raise TypeError, "Must be bound to an object of kind #{@defined_in}"
|
||||
end
|
||||
|
||||
Method.new receiver, @defined_in, @executable, @name
|
||||
end
|
||||
|
||||
##
|
||||
# String representation for UnboundMethod includes the method name, the
|
||||
# Module it is defined in and the Module that it was extracted from.
|
||||
|
||||
def inspect
|
||||
file, line = source_location()
|
||||
|
||||
if file
|
||||
"#<#{self.class}: #{@pulled_from}##{@name} (defined in #{@defined_in} at #{file}:#{line})>"
|
||||
else
|
||||
"#<#{self.class}: #{@pulled_from}##{@name} (defined in #{@defined_in})>"
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :to_s, :inspect
|
||||
|
||||
def source_location
|
||||
if @executable.respond_to? :file
|
||||
[@executable.file.to_s, @executable.defined_line]
|
||||
elsif @executable.respond_to? :source_location
|
||||
@executable.source_location
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def parameters
|
||||
return @executable.respond_to?(:parameters) ? @executable.parameters : []
|
||||
end
|
||||
|
||||
def owner
|
||||
if @defined_in.class == Rubinius::IncludedModule
|
||||
@defined_in.module
|
||||
else
|
||||
@defined_in
|
||||
end
|
||||
end
|
||||
|
||||
def super_method
|
||||
superclass = @defined_in.direct_superclass
|
||||
|
||||
if superclass
|
||||
mod, entry = superclass.lookup_method(@name)
|
||||
|
||||
if entry && entry.visibility != :undef
|
||||
return UnboundMethod.new(superclass, entry.method, @defined_in, @name)
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
def for_define_method(name, klass, callable_proc = nil)
|
||||
Rubinius::Type.bindable_method? self.defined_in, klass
|
||||
|
||||
case @executable
|
||||
when Rubinius::AccessVariable
|
||||
code = @executable
|
||||
scope = nil
|
||||
when Rubinius::DelegatedMethod
|
||||
code = @executable
|
||||
scope = code.scope
|
||||
else
|
||||
if callable_proc
|
||||
code = Rubinius::DelegatedMethod.new(name, :call, callable_proc, false)
|
||||
else
|
||||
code = Rubinius::DelegatedMethod.new(name, :call_on_instance, self, true)
|
||||
end
|
||||
scope = @executable.scope
|
||||
end
|
||||
|
||||
[code, scope]
|
||||
end
|
||||
end
|
||||
18
gxg/core/method_equality.rb
Normal file
18
gxg/core/method_equality.rb
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
module Rubinius
|
||||
module MethodEquality
|
||||
def self.method_equal_to_delegated_method_receiver?(meth, other)
|
||||
self_receiver = delegated_receiver_for(meth) || meth.unbind
|
||||
other_receiver = delegated_receiver_for(other) || other.unbind
|
||||
either_method_is_delegated?(meth, other) && (self_receiver == other_receiver)
|
||||
end
|
||||
|
||||
def self.either_method_is_delegated?(meth, other)
|
||||
(meth.executable.kind_of?(Rubinius::CompiledCode) && other.executable.kind_of?(Rubinius::DelegatedMethod)) ||
|
||||
(meth.executable.kind_of?(Rubinius::DelegatedMethod) && other.executable.kind_of?(Rubinius::CompiledCode))
|
||||
end
|
||||
|
||||
def self.delegated_receiver_for(meth)
|
||||
meth.executable.receiver if meth.executable.kind_of?(Rubinius::DelegatedMethod)
|
||||
end
|
||||
end
|
||||
end
|
||||
143
gxg/core/method_table.rb
Normal file
143
gxg/core/method_table.rb
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
##
|
||||
# Method table associates symbols (names) with methods (executables). Each
|
||||
# entry also contains the visibility of that method.
|
||||
#
|
||||
|
||||
module Rubinius
|
||||
class MethodTable
|
||||
class Bucket
|
||||
attr_reader :name
|
||||
attr_reader :visibility
|
||||
attr_reader :method
|
||||
attr_reader :prediction
|
||||
attr_reader :next
|
||||
end
|
||||
|
||||
attr_reader :values
|
||||
attr_reader :bins
|
||||
|
||||
def size
|
||||
@entries
|
||||
end
|
||||
|
||||
alias_method :length, :size
|
||||
|
||||
def self.allocate
|
||||
Rubinius.primitive :methodtable_allocate
|
||||
raise PrimitiveFailure, "MethodTable.allocate primitive failed"
|
||||
end
|
||||
|
||||
def initialize(hash=nil)
|
||||
return unless hash
|
||||
hash.each do |k, v|
|
||||
self[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def duplicate
|
||||
Rubinius.primitive :methodtable_duplicate
|
||||
raise PrimitiveFailure, "MethodTable#duplicate primitive failed"
|
||||
end
|
||||
|
||||
def dup
|
||||
copy = duplicate
|
||||
Rubinius.privately do
|
||||
copy.initialize_copy self
|
||||
end
|
||||
copy
|
||||
end
|
||||
|
||||
def name?(name)
|
||||
Rubinius.primitive :methodtable_has_name
|
||||
raise PrimitiveFailure, "MethodTable#name? primitive failed"
|
||||
end
|
||||
|
||||
alias_method :has_name?, :name?
|
||||
alias_method :include?, :name?
|
||||
alias_method :member?, :name?
|
||||
|
||||
def delete(name)
|
||||
Rubinius.primitive :methodtable_delete
|
||||
raise PrimitiveFailure, "MethodTable#delete primitive failed"
|
||||
end
|
||||
|
||||
def each
|
||||
raise LocalJumpError, "no block given" unless block_given?
|
||||
|
||||
i = 0
|
||||
max = @bins
|
||||
vals = @values
|
||||
|
||||
while i < max
|
||||
if entry = vals.at(i)
|
||||
while entry
|
||||
yield entry.name, entry.method, entry.visibility
|
||||
entry = entry.next
|
||||
end
|
||||
end
|
||||
i += 1
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
##
|
||||
# Holds Executables for lookup by the VM.
|
||||
#
|
||||
# When looking up an Executable, Rubinius starts at the real class (the object
|
||||
# in the class slot of the receiver) and looks in its MethodTable for the
|
||||
# method name. If the method is not found the ancestors list will be
|
||||
# walked looking for the method.
|
||||
#
|
||||
# If no method was found, Rubinius restarts at the real class looking for
|
||||
# "method_missing", walking the ancestors list if it is not found.
|
||||
#
|
||||
# If "method_missing" is not found, a VM assertion is triggered.
|
||||
|
||||
module Rubinius
|
||||
class MethodTable
|
||||
include Enumerable
|
||||
|
||||
def filter_entries
|
||||
out = []
|
||||
i = 0
|
||||
while i < @bins
|
||||
if entry = @values.at(i)
|
||||
while entry
|
||||
if val = yield(entry)
|
||||
out << val
|
||||
end
|
||||
|
||||
entry = entry.next
|
||||
end
|
||||
end
|
||||
i += 1
|
||||
end
|
||||
|
||||
out
|
||||
end
|
||||
|
||||
def public_names
|
||||
filter_entries do |entry|
|
||||
entry.visibility == :public ? entry.name : nil
|
||||
end
|
||||
end
|
||||
|
||||
def private_names
|
||||
filter_entries do |entry|
|
||||
entry.visibility == :private ? entry.name : nil
|
||||
end
|
||||
end
|
||||
|
||||
def protected_names
|
||||
filter_entries do |entry|
|
||||
entry.visibility == :protected ? entry.name : nil
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :to_a, :public_names
|
||||
end
|
||||
end
|
||||
32
gxg/core/metrics.rb
Normal file
32
gxg/core/metrics.rb
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
module Rubinius
|
||||
module Metrics
|
||||
@enabled = false
|
||||
|
||||
def self.enabled?
|
||||
!!@enabled
|
||||
end
|
||||
|
||||
def self.data
|
||||
@data ||= Data.new
|
||||
end
|
||||
|
||||
class Data
|
||||
def keys
|
||||
Map.keys
|
||||
end
|
||||
|
||||
def values
|
||||
Values.to_a
|
||||
end
|
||||
|
||||
def [](key)
|
||||
Values[Map[key]]
|
||||
end
|
||||
|
||||
def to_hash
|
||||
Map.map { |k, i| MetricsHash[k] = Values[i] }
|
||||
MetricsHash
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
48
gxg/core/mirror.rb
Normal file
48
gxg/core/mirror.rb
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
module Rubinius
|
||||
class Mirror
|
||||
def self.subject=(klass)
|
||||
@subject = klass
|
||||
end
|
||||
|
||||
def self.subject
|
||||
@subject
|
||||
end
|
||||
|
||||
def self.reflect(obj)
|
||||
klass = Rubinius.invoke_primitive :module_mirror, obj
|
||||
klass.new obj if klass
|
||||
end
|
||||
|
||||
attr_reader :object
|
||||
|
||||
def initialize(obj)
|
||||
@object = obj
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
class Object < Mirror
|
||||
self.subject = ::Object
|
||||
|
||||
def nil_code_id
|
||||
"%08x" % Rubinius.invoke_primitive(:object_nil_code_id, @object)
|
||||
end
|
||||
|
||||
def nil_ip
|
||||
Rubinius.invoke_primitive :object_nil_ip, @object
|
||||
end
|
||||
end
|
||||
|
||||
def instance_fields
|
||||
Rubinius.invoke_primitive :object_instance_fields, @object
|
||||
end
|
||||
|
||||
def instance_variables
|
||||
Rubinius.invoke_primitive :object_instance_variables, @object
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<#{self.class.name}:0x#{self.object_id.to_s(16)} object=#{@object.inspect}>"
|
||||
end
|
||||
end
|
||||
end
|
||||
37
gxg/core/missing_method.rb
Normal file
37
gxg/core/missing_method.rb
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
##
|
||||
# MissingMethod is used to implement the Method returned by Kernel#method
|
||||
# when #respond_to_missing? returns true.
|
||||
|
||||
module Rubinius
|
||||
class MissingMethod < Executable
|
||||
attr_reader :receiver
|
||||
attr_reader :method
|
||||
|
||||
def initialize(receiver, method)
|
||||
@receiver = receiver
|
||||
@method = method
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def call(called_object, called_method, *args, &block)
|
||||
@receiver.__send__(@method, *args, &block)
|
||||
end
|
||||
|
||||
def arity
|
||||
-1
|
||||
end
|
||||
|
||||
def parameters
|
||||
[[:rest]]
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
other.kind_of?(MissingMethod) &&
|
||||
@receiver == other.receiver &&
|
||||
@method == other.method
|
||||
end
|
||||
|
||||
alias_method :eql?, :==
|
||||
end
|
||||
end
|
||||
797
gxg/core/module.rb
Normal file
797
gxg/core/module.rb
Normal file
|
|
@ -0,0 +1,797 @@
|
|||
##
|
||||
# Some terminology notes:
|
||||
#
|
||||
# [Encloser] The Class or Module inside which this one is defined or, in the
|
||||
# event we are at top-level, Object.
|
||||
#
|
||||
# [Direct superclass] Whatever is next in the chain of superclass invocations.
|
||||
# This may be either an included Module, a Class or nil.
|
||||
#
|
||||
# [Superclass] The real semantic superclass and thus only applies to Class
|
||||
# objects.
|
||||
|
||||
class Module
|
||||
|
||||
attr_reader :constant_table
|
||||
attr_writer :method_table
|
||||
|
||||
private :included
|
||||
|
||||
def self.nesting
|
||||
scope = Rubinius::LexicalScope.of_sender
|
||||
nesting = []
|
||||
while scope and scope.module != Object
|
||||
nesting << scope.module
|
||||
scope = scope.parent
|
||||
end
|
||||
nesting
|
||||
end
|
||||
|
||||
def initialize(&block)
|
||||
# The method and constants tables are created and setup
|
||||
# by Class and Module allocate.
|
||||
module_eval(&block) if block
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def deprecate_constant(*syms)
|
||||
# no-op for now
|
||||
end
|
||||
|
||||
def const_get(name, inherit=true)
|
||||
Rubinius::Type.const_get self, name, inherit
|
||||
end
|
||||
|
||||
def const_defined?(name, inherit=true)
|
||||
c = Rubinius::Type.const_get self, name, inherit, false
|
||||
return c.equal?(undefined) ? false : true
|
||||
end
|
||||
|
||||
def attr(*attributes)
|
||||
vis = Rubinius::VariableScope.of_sender.method_visibility
|
||||
|
||||
if attributes.size == 2 && (attributes.last.is_a?(TrueClass) || attributes.last.is_a?(FalseClass))
|
||||
name = attributes.first
|
||||
Rubinius.add_reader name, self, vis
|
||||
Rubinius.add_writer name, self, vis if attributes.last
|
||||
else
|
||||
attributes.each do |name|
|
||||
if name.is_a?(Symbol) || (name.respond_to?(:to_str) && name.to_str.is_a?(String) && !name.to_str.empty?)
|
||||
Rubinius.add_reader(name, self, vis)
|
||||
else
|
||||
raise TypeError, "#{name.inspect} is not a symbol"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
private :attr
|
||||
|
||||
# Install a new Autoload object into the constants table
|
||||
# See core/autoload.rb
|
||||
def autoload(name, path)
|
||||
path = Rubinius::Type.coerce_to_path(path)
|
||||
|
||||
raise ArgumentError, "empty file name" if path.empty?
|
||||
|
||||
name = Rubinius::Type.coerce_to_constant_name name
|
||||
|
||||
Rubinius.check_frozen
|
||||
|
||||
if entry = @constant_table.lookup(name)
|
||||
if entry.constant.kind_of? Autoload
|
||||
# If there is already an Autoload here, just change the path to
|
||||
# autoload!
|
||||
entry.constant.set_path(path)
|
||||
else
|
||||
# Trying to register an autoload for a constant that already exists,
|
||||
# ignore the request entirely.
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
autoload_constant = Autoload.new(name, self, path)
|
||||
constant_table.store(name, autoload_constant, :public)
|
||||
if self == Kernel
|
||||
Object.singleton_class.constant_table.store(name, autoload_constant, :public)
|
||||
end
|
||||
|
||||
Rubinius.inc_global_serial
|
||||
return nil
|
||||
end
|
||||
|
||||
def constants(all=undefined)
|
||||
tbl = Rubinius::LookupTable.new
|
||||
|
||||
@constant_table.each do |name, constant, visibility|
|
||||
tbl[name] = true unless visibility == :private
|
||||
end
|
||||
|
||||
if all
|
||||
current = self.direct_superclass
|
||||
|
||||
while current and current != Object
|
||||
current.constant_table.each do |name, constant, visibility|
|
||||
tbl[name] = true unless visibility == :private
|
||||
end
|
||||
|
||||
current = current.direct_superclass
|
||||
end
|
||||
end
|
||||
|
||||
# special case: Module.constants returns Object's constants
|
||||
if self.equal?(Module) && undefined.equal?(all)
|
||||
Object.constant_table.each do |name, constant, visibility|
|
||||
tbl[name] = true unless visibility == :private
|
||||
end
|
||||
end
|
||||
|
||||
tbl.keys
|
||||
end
|
||||
|
||||
#--
|
||||
# These have to be aliases, not methods that call instance eval, because we
|
||||
# need to pull in the binding of the person that calls them, not the
|
||||
# intermediate binding.
|
||||
#++
|
||||
|
||||
def module_eval(string=undefined, filename="(eval)", line=1, &prc)
|
||||
# we have a custom version with the prc, rather than using instance_exec
|
||||
# so that we can setup the LexicalScope properly.
|
||||
if prc
|
||||
unless undefined.equal?(string)
|
||||
raise ArgumentError, "cannot pass both string and proc"
|
||||
end
|
||||
|
||||
# Return a copy of the BlockEnvironment with the receiver set to self
|
||||
env = prc.block
|
||||
lexical_scope = env.repoint_scope self
|
||||
return env.call_under(self, lexical_scope, true, self)
|
||||
elsif undefined.equal?(string)
|
||||
raise ArgumentError, 'block not supplied'
|
||||
end
|
||||
|
||||
string = StringValue(string)
|
||||
filename = StringValue(filename)
|
||||
|
||||
# The constantscope of a module_eval CM is the receiver of module_eval
|
||||
cs = Rubinius::LexicalScope.new self, Rubinius::LexicalScope.of_sender
|
||||
|
||||
binding = Binding.setup(Rubinius::VariableScope.of_sender,
|
||||
Rubinius::CompiledCode.of_sender,
|
||||
cs)
|
||||
|
||||
c = Rubinius::ToolSets::Runtime::Compiler
|
||||
be = c.construct_block string, binding, filename, line
|
||||
|
||||
be.call_under self, cs, true, self
|
||||
end
|
||||
|
||||
alias_method :class_eval, :module_eval
|
||||
|
||||
def private_constant(*names)
|
||||
names = names.map(&:to_sym)
|
||||
unknown_constants = names - @constant_table.keys
|
||||
if unknown_constants.size > 0
|
||||
raise NameError, "#{unknown_constants.size > 1 ? 'Constants' : 'Constant'} #{unknown_constants.map{|e| "#{name}::#{e}"}.join(', ')} undefined"
|
||||
end
|
||||
names.each do |name|
|
||||
entry = @constant_table.lookup(name)
|
||||
entry.visibility = :private
|
||||
end
|
||||
end
|
||||
|
||||
def public_constant(*names)
|
||||
names = names.map(&:to_sym)
|
||||
unknown_constants = names - @constant_table.keys
|
||||
if unknown_constants.size > 0
|
||||
raise NameError, "#{unknown_constants.size > 1 ? 'Constants' : 'Constant'} #{unknown_constants.map{|e| "#{name}::#{e}"}.join(', ')} undefined"
|
||||
end
|
||||
names.each do |name|
|
||||
entry = @constant_table.lookup(name)
|
||||
entry.visibility = :public
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Returns an UnboundMethod corresponding to the given name. The name will be
|
||||
# searched for in this Module as well as any included Modules or
|
||||
# superclasses. The UnboundMethod is populated with the method name and the
|
||||
# Module that the method was located in.
|
||||
#
|
||||
# Raises a TypeError if the given name.to_sym fails and a NameError if the
|
||||
# name cannot be located.
|
||||
|
||||
def instance_method(name)
|
||||
name = Rubinius::Type.coerce_to_symbol name
|
||||
|
||||
mod = self
|
||||
while mod
|
||||
if mod == mod.origin && entry = mod.method_table.lookup(name)
|
||||
break if entry.visibility == :undef
|
||||
|
||||
if meth = entry.get_method
|
||||
if meth.kind_of? Rubinius::Alias
|
||||
meth = meth.original_exec
|
||||
end
|
||||
|
||||
mod = mod.module if mod.class == Rubinius::IncludedModule
|
||||
|
||||
return UnboundMethod.new(mod, meth, self, name)
|
||||
end
|
||||
end
|
||||
|
||||
mod = mod.direct_superclass
|
||||
end
|
||||
|
||||
raise NameError.new("undefined method `#{name}' for #{self}", name)
|
||||
end
|
||||
|
||||
def public_instance_method(name)
|
||||
name = Rubinius::Type.coerce_to_symbol name
|
||||
|
||||
mod = self
|
||||
while mod
|
||||
if mod == mod.origin && entry = mod.method_table.lookup(name)
|
||||
vis = entry.visibility
|
||||
break if vis == :undef
|
||||
|
||||
if meth = entry.method
|
||||
if meth.kind_of? Rubinius::Alias
|
||||
meth = meth.original_exec
|
||||
end
|
||||
|
||||
mod = mod.module if mod.class == Rubinius::IncludedModule
|
||||
|
||||
if vis == :public
|
||||
return UnboundMethod.new(mod, meth, self, name)
|
||||
else
|
||||
raise NameError.new("method `#{name}' for module `#{self}' is #{vis}", name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
mod = mod.direct_superclass
|
||||
end
|
||||
|
||||
raise NameError.new("undefined method `#{name}' for #{self}", name)
|
||||
end
|
||||
|
||||
def visibility_for_aliased_method(new_name, current_name_visibility)
|
||||
special_methods = [
|
||||
:initialize,
|
||||
:initialize_copy,
|
||||
:initialize_clone,
|
||||
:initialize_dup,
|
||||
:respond_to_missing?
|
||||
]
|
||||
|
||||
if special_methods.include?(new_name)
|
||||
:private
|
||||
else
|
||||
current_name_visibility
|
||||
end
|
||||
end
|
||||
private :visibility_for_aliased_method
|
||||
|
||||
def verify_class_variable_name(name)
|
||||
if name.kind_of? Symbol
|
||||
return name if name.is_cvar?
|
||||
raise NameError, "#{name} is not an allowed class variable name"
|
||||
end
|
||||
|
||||
name = StringValue(name).to_sym
|
||||
unless name.is_cvar?
|
||||
raise NameError, "#{name} is not an allowed class variable name"
|
||||
end
|
||||
name
|
||||
end
|
||||
private :verify_class_variable_name
|
||||
|
||||
def class_variable_set(name, val)
|
||||
Rubinius.primitive :module_cvar_set
|
||||
|
||||
class_variable_set verify_class_variable_name(name), val
|
||||
end
|
||||
|
||||
def class_variable_get(name)
|
||||
Rubinius.primitive :module_cvar_get
|
||||
|
||||
class_variable_get verify_class_variable_name(name)
|
||||
end
|
||||
|
||||
def class_variable_defined?(name)
|
||||
Rubinius.primitive :module_cvar_defined
|
||||
|
||||
class_variable_defined? verify_class_variable_name(name)
|
||||
end
|
||||
|
||||
def remove_class_variable(name)
|
||||
Rubinius.primitive :module_cvar_remove
|
||||
|
||||
remove_class_variable verify_class_variable_name(name)
|
||||
end
|
||||
|
||||
def __class_variables__
|
||||
Rubinius.primitive :module_class_variables
|
||||
|
||||
raise PrimitiveFailure, "Module#__class_variables__ primitive failed"
|
||||
end
|
||||
alias_method :class_variables, :__class_variables__
|
||||
|
||||
def lookup_method(sym, check_object_too=true, trim_im=true)
|
||||
mod = self
|
||||
|
||||
while mod
|
||||
if mod == mod.origin && entry = mod.method_table.lookup(sym.to_sym)
|
||||
mod = mod.module if trim_im and mod.kind_of? Rubinius::IncludedModule
|
||||
return [mod, entry]
|
||||
end
|
||||
|
||||
mod = mod.direct_superclass
|
||||
end
|
||||
|
||||
# Optionally also search Object (and everything included in Object);
|
||||
# this lets a module alias methods on Object or Kernel.
|
||||
if check_object_too and instance_of?(Module)
|
||||
return Object.lookup_method(sym, true, trim_im)
|
||||
end
|
||||
end
|
||||
|
||||
def ancestors
|
||||
out = []
|
||||
Rubinius::Type.each_ancestor(self) { |mod| out << mod }
|
||||
return out
|
||||
end
|
||||
|
||||
def public_method_defined?(sym)
|
||||
sym = Rubinius::Type.coerce_to_reflection_name(sym)
|
||||
mod, meth = lookup_method(sym, false)
|
||||
meth ? meth.public? : false
|
||||
end
|
||||
|
||||
def private_method_defined?(sym)
|
||||
sym = Rubinius::Type.coerce_to_reflection_name(sym)
|
||||
mod, meth = lookup_method(sym, false)
|
||||
meth ? meth.private? : false
|
||||
end
|
||||
|
||||
def protected_method_defined?(sym)
|
||||
sym = Rubinius::Type.coerce_to_reflection_name(sym)
|
||||
mod, meth = lookup_method(sym, false)
|
||||
meth ? meth.protected? : false
|
||||
end
|
||||
|
||||
def method_defined?(sym)
|
||||
sym = Rubinius::Type.coerce_to_symbol(sym)
|
||||
mod, meth = lookup_method(sym, false)
|
||||
meth ? meth.public? || meth.protected? : false
|
||||
end
|
||||
|
||||
def instance_methods(all=true)
|
||||
ary = []
|
||||
if all
|
||||
table = Rubinius::LookupTable.new
|
||||
|
||||
mod = self
|
||||
|
||||
while mod
|
||||
mod.method_table.each do |name, obj, vis|
|
||||
unless table.key?(name)
|
||||
table[name] = true
|
||||
ary << name if vis == :public or vis == :protected
|
||||
end
|
||||
end
|
||||
|
||||
mod = mod.direct_superclass
|
||||
end
|
||||
else
|
||||
method_table.each do |name, obj, vis|
|
||||
ary << name if vis == :public or vis == :protected
|
||||
end
|
||||
end
|
||||
|
||||
ary
|
||||
end
|
||||
|
||||
def public_instance_methods(all=true)
|
||||
filter_methods(:public, all)
|
||||
end
|
||||
|
||||
def private_instance_methods(all=true)
|
||||
filter_methods(:private, all)
|
||||
end
|
||||
|
||||
def protected_instance_methods(all=true)
|
||||
filter_methods(:protected, all)
|
||||
end
|
||||
|
||||
def filter_methods(filter, all)
|
||||
ary = []
|
||||
if all and self.direct_superclass
|
||||
table = Rubinius::LookupTable.new
|
||||
mod = self
|
||||
|
||||
while mod
|
||||
mod.method_table.each do |name, obj, vis|
|
||||
unless table.key?(name)
|
||||
table[name] = true
|
||||
ary << name if vis == filter
|
||||
end
|
||||
end
|
||||
|
||||
mod = mod.direct_superclass
|
||||
end
|
||||
else
|
||||
method_table.each do |name, obj, vis|
|
||||
ary << name if vis == filter
|
||||
end
|
||||
end
|
||||
|
||||
ary
|
||||
end
|
||||
|
||||
private :filter_methods
|
||||
|
||||
def define_method(name, meth = undefined, &prc)
|
||||
if undefined.equal?(meth) and !block_given?
|
||||
raise ArgumentError, "tried to create Proc object without a block"
|
||||
end
|
||||
|
||||
meth = prc if undefined.equal?(meth)
|
||||
|
||||
name = Rubinius::Type.coerce_to_symbol name
|
||||
|
||||
if meth.respond_to?(:for_define_method)
|
||||
code, scope = meth.for_define_method(name, self.class)
|
||||
else
|
||||
raise TypeError, "wrong argument type #{meth.class} (expected Proc/Method)"
|
||||
end
|
||||
|
||||
Rubinius.add_method name, code, self, scope, 0, :public
|
||||
|
||||
name
|
||||
end
|
||||
|
||||
private :define_method
|
||||
|
||||
def thunk_method(name, value)
|
||||
thunk = Rubinius::Thunk.new(value)
|
||||
name = Rubinius::Type.coerce_to_symbol name
|
||||
Rubinius.add_method name, thunk, self, nil, 0, :public
|
||||
|
||||
name
|
||||
end
|
||||
|
||||
def extend_object(obj)
|
||||
include_into Rubinius::Type.object_singleton_class(obj)
|
||||
end
|
||||
private :extend_object
|
||||
|
||||
def include?(mod)
|
||||
if !mod.kind_of?(Module) or mod.kind_of?(Class)
|
||||
raise TypeError, "wrong argument type #{mod.class} (expected Module)"
|
||||
end
|
||||
|
||||
return false if self.equal?(mod)
|
||||
|
||||
Rubinius::Type.each_ancestor(self) { |m| return true if mod.equal?(m) }
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
def included_modules
|
||||
out = []
|
||||
sup = direct_superclass
|
||||
|
||||
while sup
|
||||
if sup.class == Rubinius::IncludedModule
|
||||
out << sup.module
|
||||
end
|
||||
|
||||
sup = sup.direct_superclass
|
||||
end
|
||||
|
||||
out
|
||||
end
|
||||
|
||||
def set_visibility(meth, vis, where=nil)
|
||||
name = Rubinius::Type.coerce_to_symbol(meth)
|
||||
|
||||
Rubinius::VM.reset_method_cache self, name
|
||||
|
||||
vis = vis.to_sym
|
||||
|
||||
if entry = @method_table.lookup(name)
|
||||
entry.visibility = vis
|
||||
elsif lookup_method(name)
|
||||
@method_table.store name, nil, nil, nil, 0, vis
|
||||
else
|
||||
raise NameError.new("Unknown #{where}method '#{name}' to make #{vis.to_s} (#{self})", name)
|
||||
end
|
||||
|
||||
return name
|
||||
end
|
||||
|
||||
def set_class_visibility(meth, vis)
|
||||
Rubinius::Type.object_singleton_class(self).set_visibility meth, vis, "class "
|
||||
end
|
||||
private :set_class_visibility
|
||||
|
||||
def module_exec(*args, &prc)
|
||||
raise LocalJumpError, "Missing block" unless block_given?
|
||||
|
||||
env = prc.block
|
||||
lexical_scope = env.repoint_scope self
|
||||
return env.call_under(self, lexical_scope, true, *args)
|
||||
end
|
||||
alias_method :class_exec, :module_exec
|
||||
|
||||
##
|
||||
# Return the named constant enclosed in this Module.
|
||||
#
|
||||
# Included Modules and, for Class objects, superclasses are also searched.
|
||||
# Modules will in addition look in Object. The name is attempted to convert
|
||||
# using #to_str. If the constant is not found, #const_missing is called
|
||||
# with the name.
|
||||
|
||||
def const_missing(name)
|
||||
unless self == Object
|
||||
mod_name = "#{Rubinius::Type.module_name self}::"
|
||||
end
|
||||
case Rubinius.constant_missing_reason
|
||||
when :private
|
||||
msg = "Private constant: #{mod_name}#{name}"
|
||||
else
|
||||
msg = "Missing or uninitialized constant: #{mod_name}#{name}"
|
||||
end
|
||||
|
||||
raise NameError.new(msg, name)
|
||||
end
|
||||
|
||||
def <(other)
|
||||
unless other.kind_of? Module
|
||||
raise TypeError, "compared with non class/module"
|
||||
end
|
||||
|
||||
# We're not an ancestor of ourself
|
||||
return false if self.equal? other
|
||||
|
||||
Rubinius::Type.each_ancestor(self) { |mod| return true if mod.equal?(other) }
|
||||
Rubinius::Type.each_ancestor(other) { |mod| return false if mod.equal?(self) }
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def <=(other)
|
||||
equal?(other) || (self < other)
|
||||
end
|
||||
|
||||
def >(other)
|
||||
unless other.kind_of? Module
|
||||
raise TypeError, "compared with non class/module"
|
||||
end
|
||||
other < self
|
||||
end
|
||||
|
||||
def >=(other)
|
||||
equal?(other) || (self > other)
|
||||
end
|
||||
|
||||
def <=>(other)
|
||||
return 0 if self.equal? other
|
||||
return nil unless other.kind_of? Module
|
||||
|
||||
lt = self < other
|
||||
return nil if lt.nil?
|
||||
lt ? -1 : 1
|
||||
end
|
||||
|
||||
def ===(inst)
|
||||
Rubinius::Type.object_kind_of?(inst, self)
|
||||
end
|
||||
|
||||
# Is an autoload trigger defined for the given path?
|
||||
def autoload?(name)
|
||||
name = name.to_sym
|
||||
entry = constant_table.lookup(name)
|
||||
return unless entry
|
||||
trigger = entry.constant
|
||||
return unless trigger && trigger.kind_of?(Autoload)
|
||||
trigger.path
|
||||
end
|
||||
|
||||
def remove_const(name)
|
||||
unless name.kind_of? Symbol
|
||||
name = StringValue name
|
||||
|
||||
unless Rubinius::CType.isupper name.getbyte(0)
|
||||
raise NameError, "constant names must begin with a capital letter: #{name}"
|
||||
end
|
||||
end
|
||||
|
||||
sym = name.to_sym
|
||||
unless constant_table.has_name?(sym)
|
||||
mod_name = Rubinius::Type.module_name self
|
||||
raise NameError, "Missing or uninitialized constant: #{mod_name}::#{name}"
|
||||
end
|
||||
|
||||
val = constant_table.delete(sym)
|
||||
Rubinius.inc_global_serial
|
||||
|
||||
# Silly API compact. Shield Autoload instances
|
||||
return nil if Rubinius::Type.object_kind_of?(val, Autoload)
|
||||
val
|
||||
end
|
||||
|
||||
private :remove_const
|
||||
|
||||
def extended(name)
|
||||
end
|
||||
|
||||
private :extended
|
||||
|
||||
def method_added(name)
|
||||
end
|
||||
|
||||
private :method_added
|
||||
|
||||
def method_removed(name)
|
||||
end
|
||||
|
||||
private :method_removed
|
||||
|
||||
def method_undefined(name)
|
||||
end
|
||||
|
||||
private :method_undefined
|
||||
|
||||
def initialize_copy(other)
|
||||
# If the method table is already different, we already
|
||||
# initialized this module.
|
||||
unless @method_table == other.method_table
|
||||
raise TypeError, "already initialized module"
|
||||
end
|
||||
|
||||
@method_table = other.method_table.dup
|
||||
|
||||
sc_s = Rubinius::Type.object_singleton_class self
|
||||
sc_o = Rubinius::Type.object_singleton_class other
|
||||
|
||||
sc_s.method_table = sc_o.method_table.dup
|
||||
sc_s.superclass = sc_o.direct_superclass
|
||||
|
||||
@constant_table = Rubinius::ConstantTable.new
|
||||
|
||||
other.constant_table.each do |name, val|
|
||||
if val.kind_of? Autoload
|
||||
val = Autoload.new(val.name, self, val.path)
|
||||
end
|
||||
|
||||
@constant_table.store(name, val, :public)
|
||||
end
|
||||
|
||||
if other == other.origin
|
||||
@origin = self
|
||||
else
|
||||
@origin = other.origin.dup
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
private :initialize_copy
|
||||
|
||||
def add_ivars(code)
|
||||
case code
|
||||
when Rubinius::CompiledCode
|
||||
new_ivars = code.literals.select { |l| l.kind_of?(Symbol) and l.is_ivar? }
|
||||
return if new_ivars.empty?
|
||||
|
||||
if @seen_ivars
|
||||
new_ivars.each do |x|
|
||||
unless @seen_ivars.include?(x)
|
||||
@seen_ivars << x
|
||||
end
|
||||
end
|
||||
else
|
||||
@seen_ivars = new_ivars
|
||||
end
|
||||
when Rubinius::AccessVariable
|
||||
if @seen_ivars
|
||||
unless @seen_ivars.include?(code.name)
|
||||
@seen_ivars << code.name
|
||||
end
|
||||
else
|
||||
@seen_ivars = [code.name]
|
||||
end
|
||||
else
|
||||
raise "Unknown type of method to learn ivars - #{code.class}"
|
||||
end
|
||||
end
|
||||
|
||||
def dynamic_method(name, file="(dynamic)", line=1)
|
||||
g = Rubinius::ToolSets::Runtime::Generator.new
|
||||
g.name = name.to_sym
|
||||
g.file = file.to_sym
|
||||
g.set_line Integer(line)
|
||||
|
||||
yield g
|
||||
|
||||
g.close
|
||||
|
||||
g.use_detected
|
||||
g.encode
|
||||
|
||||
code = g.package Rubinius::CompiledCode
|
||||
|
||||
scope = Rubinius::LexicalScope.new(self, Rubinius::LexicalScope.new(Object))
|
||||
code.scope = scope
|
||||
|
||||
Rubinius.add_method name, code, self, scope, 0, :public
|
||||
|
||||
return code
|
||||
end
|
||||
|
||||
def freeze
|
||||
@method_table.freeze
|
||||
@constant_table.freeze
|
||||
super
|
||||
end
|
||||
|
||||
def origin= origin
|
||||
@origin = origin
|
||||
end
|
||||
|
||||
def prepended(mod); end
|
||||
private :prepended
|
||||
|
||||
def prepend(*modules)
|
||||
modules.reverse_each do |mod|
|
||||
if !mod.kind_of?(Module) or mod.kind_of?(Class)
|
||||
raise TypeError, "wrong argument type #{mod.class} (expected Module)"
|
||||
end
|
||||
|
||||
Rubinius.privately do
|
||||
mod.prepend_features self
|
||||
end
|
||||
|
||||
Rubinius.privately do
|
||||
mod.prepended self
|
||||
end
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
def prepend_features(klass)
|
||||
unless klass.kind_of? Module
|
||||
raise TypeError, "invalid argument class #{klass.class}, expected Module"
|
||||
end
|
||||
|
||||
if kind_of? Class
|
||||
raise TypeError, "invalid receiver class #{__class__}, expected Module"
|
||||
end
|
||||
|
||||
if klass == klass.origin
|
||||
im = Rubinius::IncludedModule.new(klass)
|
||||
im.superclass = klass.direct_superclass
|
||||
klass.superclass = im
|
||||
klass.origin = im
|
||||
klass.method_table.each do |meth, obj, vis|
|
||||
Rubinius::VM.reset_method_cache klass, meth
|
||||
end
|
||||
end
|
||||
Rubinius::Type.include_modules_from(self, klass)
|
||||
Rubinius::Type.infect(klass, self)
|
||||
self
|
||||
end
|
||||
private :prepend_features
|
||||
|
||||
def singleton_class?
|
||||
!!Rubinius::Type.singleton_class_object(self)
|
||||
end
|
||||
end
|
||||
82
gxg/core/mutex.rb
Normal file
82
gxg/core/mutex.rb
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
class Mutex
|
||||
def initialize
|
||||
@owner = nil
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
# Check and only allow it to be marshal'd if there are no waiters.
|
||||
def marshal_dump
|
||||
raise "Unable to dump locked mutex" unless @waiters.empty?
|
||||
1
|
||||
end
|
||||
|
||||
# Implemented because we must since we use marshal_load PLUS we need
|
||||
# to create AND prime @lock. If we didn't do this, then Marshal
|
||||
# wouldn't prime the lock anyway.
|
||||
def marshal_load(bunk)
|
||||
initialize
|
||||
end
|
||||
|
||||
def locked?
|
||||
Rubinius.locked?(self)
|
||||
end
|
||||
|
||||
def try_lock
|
||||
# Locking implies a memory barrier, so we don't need to use
|
||||
# one explicitly.
|
||||
if Rubinius.try_lock(self)
|
||||
return false if @owner == Thread.current
|
||||
@owner = Thread.current
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def lock
|
||||
Rubinius.memory_barrier
|
||||
if @owner == Thread.current
|
||||
raise ThreadError, "Recursively locking not allowed"
|
||||
end
|
||||
|
||||
Rubinius.lock self
|
||||
@owner = Thread.current
|
||||
Rubinius.memory_barrier
|
||||
return self
|
||||
end
|
||||
|
||||
def unlock
|
||||
Rubinius.memory_barrier
|
||||
|
||||
if @owner != Thread.current
|
||||
raise ThreadError, "Not owner, #{@owner.inspect} is"
|
||||
end
|
||||
|
||||
@owner = nil
|
||||
Rubinius.unlock self
|
||||
self
|
||||
end
|
||||
|
||||
def synchronize
|
||||
lock
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
unlock
|
||||
end
|
||||
end
|
||||
|
||||
def sleep(duration=undefined)
|
||||
if duration.kind_of?(Numeric) && duration < 0
|
||||
raise ArgumentError, "time interval must be positive"
|
||||
end
|
||||
|
||||
unlock
|
||||
begin
|
||||
Kernel.sleep(duration)
|
||||
ensure
|
||||
lock
|
||||
end
|
||||
end
|
||||
end
|
||||
86
gxg/core/native_method.rb
Normal file
86
gxg/core/native_method.rb
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
##
|
||||
# A wrapper for a calling a function in a shared library that has been
|
||||
# attached via rb_define_method().
|
||||
#
|
||||
# The primitive slot for a NativeMethod points to the nmethod_call primitive
|
||||
# which dispatches to the underlying C function.
|
||||
|
||||
module Rubinius
|
||||
class NativeMethod < Executable
|
||||
|
||||
attr_reader :file
|
||||
attr_reader :name
|
||||
attr_reader :arity
|
||||
|
||||
#
|
||||
# Load a dynamic library at path +library+, having name +extension_name+.
|
||||
#
|
||||
# +extension_name+ is used to calculate what Init_ function to call.
|
||||
#
|
||||
def self.load_extension(library, extension_name)
|
||||
name = "Init_#{extension_name.sub(/\..*$/, '')}"
|
||||
|
||||
begin
|
||||
lib = FFI::DynamicLibrary.new(library, FFI::DynamicLibrary::RTLD_NOW | FFI::DynamicLibrary::RTLD_GLOBAL)
|
||||
rescue LoadError => e
|
||||
raise LoadError::InvalidExtensionError, e.message, e
|
||||
end
|
||||
|
||||
ver = lib.find_symbol("__X_rubinius_version")
|
||||
|
||||
unless ver
|
||||
raise LoadError::InvalidExtensionError, "Out-of-date or not compatible. Recompile or reinstall gem (#{library})"
|
||||
end
|
||||
|
||||
sym = lib.find_symbol(name)
|
||||
|
||||
unless sym
|
||||
raise LoadError::InvalidExtensionError, "Missing Init_ function (#{name})"
|
||||
end
|
||||
|
||||
entry_point = load_entry_point(library, name.to_sym, sym)
|
||||
|
||||
entry_point.invoke(:init, Rubinius, self, [], nil)
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
#
|
||||
# Load extension and generate a NativeMethod for its entry point.
|
||||
#
|
||||
# +ptr+ is an FFI::Pointer to a C function.
|
||||
#
|
||||
def self.load_entry_point(library, name, ptr)
|
||||
Rubinius.primitive :nativemethod_load_extension_entry_point
|
||||
raise PrimitiveFailure, "Rubinius::NativeMethod3load_entry_point primitive failed: #{library}"
|
||||
end
|
||||
|
||||
def lines
|
||||
nil
|
||||
end
|
||||
|
||||
def literals
|
||||
nil
|
||||
end
|
||||
|
||||
def line_from_ip(i)
|
||||
0
|
||||
end
|
||||
|
||||
def first_line
|
||||
0
|
||||
end
|
||||
|
||||
def defined_line
|
||||
0
|
||||
end
|
||||
|
||||
def active_path
|
||||
@file
|
||||
end
|
||||
|
||||
def scope
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
53
gxg/core/nil.rb
Normal file
53
gxg/core/nil.rb
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
class NilClass
|
||||
alias_method :eql?, :equal?
|
||||
alias_method :==, :equal?
|
||||
alias_method :===, :equal?
|
||||
|
||||
def &(other)
|
||||
false
|
||||
end
|
||||
|
||||
def ^(other)
|
||||
!!other
|
||||
end
|
||||
|
||||
alias_method :|, :^
|
||||
|
||||
def to_s
|
||||
""
|
||||
end
|
||||
|
||||
def inspect
|
||||
"nil"
|
||||
end
|
||||
|
||||
def to_a
|
||||
[]
|
||||
end
|
||||
|
||||
def to_f
|
||||
0.0
|
||||
end
|
||||
|
||||
def to_i
|
||||
0
|
||||
end
|
||||
|
||||
def to_r
|
||||
Rational(0, 1)
|
||||
end
|
||||
|
||||
def rationalize(eps = nil)
|
||||
to_r
|
||||
end
|
||||
|
||||
def to_c
|
||||
Complex(0)
|
||||
end
|
||||
|
||||
def to_h
|
||||
{}
|
||||
end
|
||||
end
|
||||
|
||||
NIL = nil
|
||||
289
gxg/core/numeric.rb
Normal file
289
gxg/core/numeric.rb
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
class Numeric
|
||||
include Comparable
|
||||
|
||||
# Always raises TypeError, as dup'ing Numerics is not allowed.
|
||||
def initialize_copy(other)
|
||||
raise TypeError, "copy of #{self.class} is not allowed"
|
||||
end
|
||||
|
||||
private :initialize_copy
|
||||
|
||||
def +@
|
||||
self
|
||||
end
|
||||
|
||||
def -@
|
||||
0 - self
|
||||
end
|
||||
|
||||
def divmod(other)
|
||||
[div(other), self % other]
|
||||
end
|
||||
|
||||
def eql?(other)
|
||||
return false unless other.instance_of? self.class
|
||||
self == other
|
||||
end
|
||||
|
||||
def <=>(other)
|
||||
# It's important this method NOT contain the coercion protocols!
|
||||
# MRI doesn't and doing so breaks stuff!
|
||||
|
||||
return 0 if self.equal? other
|
||||
return nil
|
||||
end
|
||||
|
||||
def step(limit=nil, step=nil, to: nil, by: nil)
|
||||
unless block_given?
|
||||
return to_enum(:step, limit, step, to: to, by: by) do
|
||||
Rubinius::Mirror::Numeric.reflect(self).step_size(limit, step, to, by)
|
||||
end
|
||||
end
|
||||
|
||||
m = Rubinius::Mirror::Numeric.reflect(self)
|
||||
values = m.step_fetch_args(limit, step, to, by)
|
||||
value = values[0]
|
||||
limit = values[1]
|
||||
step = values[2]
|
||||
asc = values[3]
|
||||
is_float = values[4]
|
||||
|
||||
if step == 0
|
||||
while true
|
||||
yield value
|
||||
end
|
||||
end
|
||||
|
||||
if is_float
|
||||
n = m.step_float_size(value, limit, step, asc)
|
||||
|
||||
if n > 0
|
||||
if step.infinite?
|
||||
yield value
|
||||
else
|
||||
i = 0
|
||||
if asc
|
||||
while i < n
|
||||
d = i * step + value
|
||||
d = limit if limit < d
|
||||
yield d
|
||||
i += 1
|
||||
end
|
||||
else
|
||||
while i < n
|
||||
d = i * step + value
|
||||
d = limit if limit > d
|
||||
yield d
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
if asc
|
||||
until value > limit
|
||||
yield value
|
||||
value += step
|
||||
end
|
||||
else
|
||||
until value < limit
|
||||
yield value
|
||||
value += step
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
def truncate
|
||||
Float(self).truncate
|
||||
end
|
||||
|
||||
# Delegate #to_int to #to_i in subclasses
|
||||
def to_int
|
||||
to_i
|
||||
end
|
||||
|
||||
def integer?
|
||||
false
|
||||
end
|
||||
|
||||
def zero?
|
||||
self == 0
|
||||
end
|
||||
|
||||
def nonzero?
|
||||
zero? ? nil : self
|
||||
end
|
||||
|
||||
def round
|
||||
to_f.round
|
||||
end
|
||||
|
||||
def abs
|
||||
self < 0 ? -self : self
|
||||
end
|
||||
|
||||
def floor
|
||||
FloatValue(self).floor
|
||||
end
|
||||
|
||||
def ceil
|
||||
FloatValue(self).ceil
|
||||
end
|
||||
|
||||
def remainder(other)
|
||||
mod = self % other
|
||||
|
||||
if mod != 0 and ((self < 0 and other > 0) or (self > 0 and other < 0))
|
||||
mod - other
|
||||
else
|
||||
mod
|
||||
end
|
||||
end
|
||||
|
||||
#--
|
||||
# We deviate from MRI behavior here because we ensure that Fixnum op Bignum
|
||||
# => Bignum (possibly normalized to Fixnum)
|
||||
#
|
||||
# Note these differences on MRI, where a is a Fixnum, b is a Bignum
|
||||
#
|
||||
# a.coerce b => [Float, Float]
|
||||
# b.coerce a => [Bignum, Bignum]
|
||||
#++
|
||||
|
||||
def coerce(other)
|
||||
if other.instance_of? self.class
|
||||
return [other, self]
|
||||
end
|
||||
|
||||
[Float(other), Float(self)]
|
||||
end
|
||||
|
||||
##
|
||||
# This method mimics the semantics of MRI's do_coerce function
|
||||
# in numeric.c. Note these differences between it and #coerce:
|
||||
#
|
||||
# 1.2.coerce("2") => [2.0, 1.2]
|
||||
# 1.2 + "2" => TypeError: String can't be coerced into Float
|
||||
#
|
||||
# See also Integer#coerce
|
||||
|
||||
def math_coerce(other, error=:coerce_error)
|
||||
begin
|
||||
values = other.coerce(self)
|
||||
rescue
|
||||
if error == :coerce_error
|
||||
raise TypeError, "#{other.class} can't be coerced into #{self.class}"
|
||||
else
|
||||
raise ArgumentError, "comparison of #{self.class} with #{other.class} failed"
|
||||
end
|
||||
end
|
||||
|
||||
unless Rubinius::Type.object_kind_of?(values, Array) && values.length == 2
|
||||
raise TypeError, "coerce must return [x, y]"
|
||||
end
|
||||
|
||||
return values[1], values[0]
|
||||
end
|
||||
private :math_coerce
|
||||
|
||||
def redo_coerced(meth, right)
|
||||
b, a = math_coerce(right)
|
||||
a.__send__ meth, b
|
||||
end
|
||||
private :redo_coerced
|
||||
|
||||
def redo_compare(meth, right)
|
||||
b, a = math_coerce(right, :compare_error)
|
||||
a.__send__ meth, b
|
||||
end
|
||||
|
||||
def div(other)
|
||||
raise ZeroDivisionError, "divided by 0" if other == 0
|
||||
(self / other).floor
|
||||
end
|
||||
|
||||
def fdiv(other)
|
||||
self.to_f / other
|
||||
end
|
||||
|
||||
def quo(other)
|
||||
Rubinius.privately do
|
||||
Rational.convert(self, 1, false) / other
|
||||
end
|
||||
end
|
||||
|
||||
def modulo(other)
|
||||
self - other * self.div(other)
|
||||
end
|
||||
alias_method :%, :modulo
|
||||
|
||||
def i
|
||||
Complex(0, self)
|
||||
end
|
||||
|
||||
def to_c
|
||||
Complex(self, 0)
|
||||
end
|
||||
|
||||
def real
|
||||
self
|
||||
end
|
||||
|
||||
def imag
|
||||
0
|
||||
end
|
||||
alias_method :imaginary, :imag
|
||||
|
||||
def arg
|
||||
if self < 0
|
||||
Math::PI
|
||||
else
|
||||
0
|
||||
end
|
||||
end
|
||||
alias_method :angle, :arg
|
||||
alias_method :phase, :arg
|
||||
|
||||
def polar
|
||||
return abs, arg
|
||||
end
|
||||
|
||||
def conjugate
|
||||
self
|
||||
end
|
||||
alias_method :conj, :conjugate
|
||||
|
||||
def rect
|
||||
[self, 0]
|
||||
end
|
||||
alias_method :rectangular, :rect
|
||||
|
||||
def abs2
|
||||
self * self
|
||||
end
|
||||
|
||||
alias_method :magnitude, :abs
|
||||
|
||||
def numerator
|
||||
to_r.numerator
|
||||
end
|
||||
|
||||
def denominator
|
||||
to_r.denominator
|
||||
end
|
||||
|
||||
def real?
|
||||
true
|
||||
end
|
||||
|
||||
def positive?
|
||||
self > 0
|
||||
end
|
||||
|
||||
def negative?
|
||||
self < 0
|
||||
end
|
||||
end
|
||||
74
gxg/core/numeric_mirror.rb
Normal file
74
gxg/core/numeric_mirror.rb
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
module Rubinius
|
||||
class Mirror
|
||||
class Numeric < Mirror
|
||||
self.subject = ::Numeric
|
||||
|
||||
def step_float_size(value, limit, step, asc)
|
||||
if (asc && value > limit) || (!asc && value < limit)
|
||||
return 0
|
||||
end
|
||||
|
||||
if step.infinite?
|
||||
1
|
||||
else
|
||||
err = (value.abs + limit.abs + (limit - value).abs) / step.abs * Float::EPSILON
|
||||
if err.finite?
|
||||
err = 0.5 if err > 0.5
|
||||
((limit - value) / step + err).floor + 1
|
||||
else
|
||||
0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def step_size(limit, step, to, by)
|
||||
values = step_fetch_args(limit, step, to, by)
|
||||
value = values[0]
|
||||
limit = values[1]
|
||||
step = values[2]
|
||||
asc = values[3]
|
||||
is_float = values[4]
|
||||
|
||||
if limit == Float::INFINITY
|
||||
if step == Float::INFINITY
|
||||
return 1
|
||||
else
|
||||
return Float::INFINITY
|
||||
end
|
||||
end
|
||||
|
||||
return Float::INFINITY if step == 0
|
||||
|
||||
if is_float
|
||||
# Ported from MRI
|
||||
|
||||
step_float_size(value, limit, step, asc)
|
||||
|
||||
else
|
||||
if (asc && value > limit) || (!asc && value < limit)
|
||||
0
|
||||
else
|
||||
((value - limit).abs + 1).fdiv(step.abs).ceil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def step_fetch_args(limit, step, to, by)
|
||||
raise ArgumentError, "limit is given twice" if limit && to
|
||||
raise ArgumentError, "step is given twice" if step && by
|
||||
raise ArgumentError, "step cannot be 0" if step == 0
|
||||
|
||||
step ||= by || 1
|
||||
asc = step > 0
|
||||
limit ||= to || (asc ? Float::INFINITY : -Float::INFINITY)
|
||||
value = @object
|
||||
if value.kind_of? Float or limit.kind_of? Float or step.kind_of? Float
|
||||
return FloatValue(value), FloatValue(limit), FloatValue(step), asc, true
|
||||
else
|
||||
return value, limit, step, asc, false
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
88
gxg/core/object_space.rb
Normal file
88
gxg/core/object_space.rb
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
module ObjectSpace
|
||||
def self.find_object(query, callable)
|
||||
Rubinius.primitive :vm_find_object
|
||||
raise PrimitiveFailure, "ObjectSpace#find_object primitive failed"
|
||||
end
|
||||
|
||||
def self._id2ref(id)
|
||||
ary = []
|
||||
|
||||
if find_object([:object_id, Integer(id)], ary) > 0
|
||||
return ary.first
|
||||
end
|
||||
|
||||
raise RangeError, "#{id} is not a valid ID value"
|
||||
end
|
||||
|
||||
def self.find_references(obj)
|
||||
ary = []
|
||||
find_object([:references, obj], ary)
|
||||
return ary
|
||||
end
|
||||
|
||||
# @todo rewrite each_object
|
||||
|
||||
# Tries to handle as much as it can.
|
||||
def self.each_object(what=nil, &block)
|
||||
return to_enum :each_object, what unless block_given?
|
||||
|
||||
what ||= Object
|
||||
|
||||
unless what.kind_of? Object
|
||||
raise TypeError, "class or module required"
|
||||
end
|
||||
|
||||
case what
|
||||
when Fixnum, Symbol
|
||||
return 0
|
||||
when TrueClass
|
||||
yield true
|
||||
return 1
|
||||
when FalseClass
|
||||
yield false
|
||||
return 1
|
||||
when NilClass
|
||||
yield nil
|
||||
return 1
|
||||
else
|
||||
return find_object([:kind_of, what], block)
|
||||
end
|
||||
end
|
||||
|
||||
def self.define_finalizer(obj, prc=nil, &block)
|
||||
prc ||= block
|
||||
|
||||
if obj.kind_of? ImmediateValue or obj.kind_of? Float
|
||||
raise ArgumentError, "can't define finalizer for #{obj.class}"
|
||||
end
|
||||
|
||||
if obj.frozen?
|
||||
raise RuntimeError, "can't modify frozen #{obj.class}"
|
||||
end
|
||||
|
||||
if obj.equal? prc
|
||||
# This is allowed. This is the Rubinius specific API that calls
|
||||
# __finalize__ when the object is finalized.
|
||||
elsif !prc and obj.respond_to?(:__finalize__)
|
||||
# Allowed. Same thing as above
|
||||
prc = obj
|
||||
elsif !prc or !prc.respond_to?(:call)
|
||||
raise ArgumentError, "action must respond to call"
|
||||
end
|
||||
|
||||
unless Rubinius.invoke_primitive(:vm_set_finalizer, obj, prc)
|
||||
raise RuntimeError, "cannot define a finalizer for a #{obj.class}"
|
||||
end
|
||||
|
||||
[0, prc]
|
||||
end
|
||||
|
||||
def self.undefine_finalizer(obj)
|
||||
Rubinius.invoke_primitive :vm_set_finalizer, obj, nil
|
||||
return obj
|
||||
end
|
||||
|
||||
def self.garbage_collect
|
||||
GC.start
|
||||
end
|
||||
end
|
||||
254
gxg/core/options.rb
Normal file
254
gxg/core/options.rb
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
module Rubinius
|
||||
|
||||
# A simple command line option parser.
|
||||
class Options
|
||||
|
||||
# A single command line option.
|
||||
class Option
|
||||
attr_reader :short
|
||||
attr_reader :long
|
||||
attr_reader :arg
|
||||
attr_reader :description
|
||||
attr_reader :block
|
||||
|
||||
def initialize(short, long, arg, description, block)
|
||||
@short = short
|
||||
@long = long
|
||||
@arg = arg
|
||||
@description = description
|
||||
@block = block
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def arg?
|
||||
@arg != nil
|
||||
end
|
||||
|
||||
def optional?
|
||||
@arg[0] == ?[ and @arg[-1] == ?]
|
||||
end
|
||||
|
||||
def match?(opt)
|
||||
opt == @short or opt == @long
|
||||
end
|
||||
end
|
||||
|
||||
# Raised if incorrect or incomplete formats are passed to #on.
|
||||
class OptionError < Exception; end
|
||||
|
||||
# Raised if an unrecognized option is encountered.
|
||||
class ParseError < Exception; end
|
||||
|
||||
attr_accessor :config
|
||||
attr_accessor :banner
|
||||
attr_accessor :width
|
||||
attr_accessor :options
|
||||
|
||||
def initialize(banner="", width=30, config=nil)
|
||||
@parse = true
|
||||
@banner = banner
|
||||
@config = config
|
||||
@width = width
|
||||
@options = []
|
||||
@doc = []
|
||||
@extra = []
|
||||
@align = true
|
||||
@on_extra = lambda { |x|
|
||||
raise ParseError, "Unrecognized option: #{x}" if x[0] == ?-
|
||||
@extra << x
|
||||
}
|
||||
|
||||
yield self if block_given?
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
# Documentation for options is left aligned. For example,
|
||||
#
|
||||
# -a ARG Some description
|
||||
# --big Another bit of info
|
||||
# -c, --class Yet more info
|
||||
#
|
||||
# See +option_align+.
|
||||
def left_align
|
||||
@align = nil
|
||||
end
|
||||
|
||||
# Documentation for options is aligned by option type. For example,
|
||||
#
|
||||
# -a ARG Some description
|
||||
# --big Another bit of info
|
||||
# -c, --class Yet more info
|
||||
#
|
||||
# See +left_align+
|
||||
def option_align
|
||||
@align = true
|
||||
end
|
||||
|
||||
# Registers an option. Acceptable formats for arguments are:
|
||||
#
|
||||
# on "-a", "description"
|
||||
# on "-a", "--abdc", "description"
|
||||
# on "-a", "ARG", "description"
|
||||
# on "--abdc", "ARG", "description"
|
||||
# on "-a", "--abdc", "ARG", "description"
|
||||
# on "-a", "[ARG]", "description"
|
||||
#
|
||||
# The [ARG] form specifies an optional argument. The argument
|
||||
# must be attached to the option. In the case of a short option,
|
||||
# the form "-aARG" must be used. For a long option, the form
|
||||
# '--opt=ARG' must be used.
|
||||
#
|
||||
# If an block is passed, it will be invoked when the option is
|
||||
# matched. Not passing a block is permitted, but nonsensical.
|
||||
def on(*args, &block)
|
||||
raise OptionError, "option and description are required" if args.size < 2
|
||||
|
||||
description = args.pop
|
||||
short, long, argument = nil
|
||||
args.each do |arg|
|
||||
if arg[0] == ?-
|
||||
if arg[1] == ?-
|
||||
long = arg
|
||||
else
|
||||
short = arg
|
||||
end
|
||||
else
|
||||
argument = arg
|
||||
end
|
||||
end
|
||||
|
||||
add short, long, argument, description, block
|
||||
end
|
||||
|
||||
# Adds documentation text for an option and adds an +Option+
|
||||
# instance to the list of registered options.
|
||||
def add(short, long, arg, description, block)
|
||||
pad = @align ? " " : ""
|
||||
s = short ? short.dup : pad
|
||||
s << (short ? ", " : pad) if long
|
||||
doc " #{s}#{long} #{arg}".ljust(@width-1) + " #{description}"
|
||||
@options << Option.new(short, long, arg, description, block)
|
||||
end
|
||||
|
||||
# Searches all registered options to find a match for +opt+. Returns
|
||||
# +nil+ if no registered options match.
|
||||
def match?(opt)
|
||||
@options.find { |o| o.match? opt }
|
||||
end
|
||||
|
||||
# Processes an option. Calles the #on_extra block (or default) for
|
||||
# unrecognized options. For registered options, possibly fetches an
|
||||
# argument and invokes the option's block if it is not nil.
|
||||
def process(argv, entry, opt, arg)
|
||||
unless option = match?(opt)
|
||||
@on_extra[entry]
|
||||
else
|
||||
if option.arg?
|
||||
if arg.nil?
|
||||
unless option.optional?
|
||||
arg = argv.shift
|
||||
end
|
||||
end
|
||||
|
||||
unless arg or option.optional?
|
||||
raise ParseError, "No argument provided for #{opt}"
|
||||
end
|
||||
|
||||
option.block[arg] if option.block
|
||||
else
|
||||
option.block[] if option.block
|
||||
end
|
||||
end
|
||||
option
|
||||
end
|
||||
|
||||
# Splits a string at +n+ characters into the +opt+ and the +rest+.
|
||||
# The +arg+ is set to +nil+ if +rest+ is an empty string.
|
||||
def split(str, n)
|
||||
opt = str[0, n]
|
||||
rest = str[n, str.size] || ""
|
||||
arg = rest == "" ? nil : rest
|
||||
return opt, arg, rest
|
||||
end
|
||||
|
||||
# Parses an array of command line entries, calling blocks for
|
||||
# registered options.
|
||||
def parse(argv=ARGV)
|
||||
argv = Array(argv)
|
||||
|
||||
while @parse and entry = argv.shift
|
||||
# collect everything that is not an option
|
||||
if entry[0] != ?-
|
||||
@on_extra[entry]
|
||||
next
|
||||
end
|
||||
|
||||
# this is a long option
|
||||
if entry[1] == ?-
|
||||
opt, arg = entry.split "="
|
||||
process argv, entry, opt, arg
|
||||
next
|
||||
end
|
||||
|
||||
# disambiguate short option group from short option with argument
|
||||
opt, arg, rest = split entry, 2
|
||||
|
||||
# process first option
|
||||
option = process argv, entry, opt, arg
|
||||
next unless option and not option.arg?
|
||||
|
||||
# process the rest of the options
|
||||
while rest.size > 0
|
||||
opt, arg, rest = split rest, 1
|
||||
opt = "-" + opt
|
||||
option = process argv, opt, opt, arg
|
||||
break if option.arg?
|
||||
end
|
||||
end
|
||||
|
||||
@extra
|
||||
rescue ParseError => e
|
||||
puts self
|
||||
puts e
|
||||
exit 1
|
||||
end
|
||||
|
||||
def start_parsing
|
||||
@parse = true
|
||||
end
|
||||
|
||||
def stop_parsing
|
||||
@parse = false
|
||||
end
|
||||
|
||||
# Adds a string of documentation text inline in the text generated
|
||||
# from the options. See #on and #add.
|
||||
def doc(str)
|
||||
@doc << str
|
||||
end
|
||||
|
||||
# Convenience method for providing -v, --version options.
|
||||
def version(version, &block)
|
||||
show = block || lambda { puts "#{File.basename $0} #{version}"; exit }
|
||||
on "-v", "--version", "Show version", &show
|
||||
end
|
||||
|
||||
# Convenience method for providing -h, --help options.
|
||||
def help(&block)
|
||||
help = block || lambda { puts self; exit 1 }
|
||||
on "-h", "--help", "Show this message", &help
|
||||
end
|
||||
|
||||
# Stores a block that will be called with unrecognized options
|
||||
def on_extra(&block)
|
||||
@on_extra = block
|
||||
end
|
||||
|
||||
# Returns a string representation of the options and doc strings.
|
||||
def to_s
|
||||
@banner + "\n\n" + @doc.join("\n") + "\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
27
gxg/core/pack.rb
Normal file
27
gxg/core/pack.rb
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
module Rubinius
|
||||
def self.pack_to_float(obj)
|
||||
unless obj.kind_of?(Numeric)
|
||||
raise TypeError, "unable to convert String to Float"
|
||||
end
|
||||
Type.coerce_to obj, Float, :to_f
|
||||
end
|
||||
|
||||
def self.pack_to_int(obj)
|
||||
Rubinius::Type.coerce_to obj, Integer, :to_int
|
||||
end
|
||||
|
||||
def self.pack_to_str(obj)
|
||||
Rubinius::Type.coerce_to obj, String, :to_str
|
||||
end
|
||||
|
||||
def self.pack_to_str_or_nil(obj)
|
||||
return "" if obj.nil?
|
||||
Rubinius::Type.coerce_to obj, String, :to_str
|
||||
end
|
||||
|
||||
def self.pack_to_s(obj)
|
||||
str = obj.to_s
|
||||
str = obj.inspect unless Type.object_kind_of? str, String
|
||||
str
|
||||
end
|
||||
end
|
||||
347
gxg/core/peg.rb
Normal file
347
gxg/core/peg.rb
Normal file
|
|
@ -0,0 +1,347 @@
|
|||
module Rubinius
|
||||
module PEG
|
||||
class InvalidPatternError < Exception; end
|
||||
|
||||
# Class Pattern is the base class for all nodes used to represent the AST
|
||||
# for a PEG. Following LPEG, the base is a pattern and not a grammar.
|
||||
class Pattern
|
||||
def graph
|
||||
if defined? Rubinius
|
||||
Rubinius::AST::AsciiGrapher.new(self, Pattern).print
|
||||
else
|
||||
inspect
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def visit(visitor)
|
||||
end
|
||||
|
||||
# Pattern operators
|
||||
|
||||
def /(other)
|
||||
Choice.new self, other
|
||||
end
|
||||
|
||||
# TODO: remove after core transform for :/ is removed
|
||||
alias_method :divide, :/
|
||||
|
||||
def +(other)
|
||||
Concatenation.new self, other
|
||||
end
|
||||
|
||||
def -(other)
|
||||
Difference.new self, other
|
||||
end
|
||||
|
||||
def *(other)
|
||||
Product.new self, other
|
||||
end
|
||||
|
||||
def +@
|
||||
If.new self
|
||||
end
|
||||
|
||||
def -@
|
||||
Unless.new self
|
||||
end
|
||||
|
||||
def =~(string)
|
||||
end
|
||||
|
||||
def match(string)
|
||||
end
|
||||
end
|
||||
|
||||
class UnaryOp < Pattern
|
||||
attr_accessor :pattern
|
||||
|
||||
def initialize(pattern)
|
||||
@pattern = PEG.pattern pattern
|
||||
end
|
||||
|
||||
private :initialize
|
||||
end
|
||||
|
||||
class BinaryOp < Pattern
|
||||
attr_accessor :first
|
||||
attr_accessor :second
|
||||
|
||||
def initialize(first, second)
|
||||
@first = PEG.pattern first
|
||||
@second = PEG.pattern second
|
||||
end
|
||||
|
||||
private :initialize
|
||||
end
|
||||
|
||||
class Grammar < Pattern
|
||||
attr_accessor :start
|
||||
|
||||
def initialize(variable=nil)
|
||||
if variable
|
||||
@start = variable.name
|
||||
set_variable variable.name, variable.pattern
|
||||
end
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def set_variable(name, pattern)
|
||||
variable = get_variable name
|
||||
variable.pattern = PEG.pattern pattern
|
||||
variable
|
||||
end
|
||||
|
||||
def get_variable(name)
|
||||
if singleton_class.method_defined? name
|
||||
variable = send name
|
||||
else
|
||||
vname = name.to_sym
|
||||
variable = Variable.new vname
|
||||
singleton_class.thunk_method vname, variable
|
||||
end
|
||||
|
||||
variable
|
||||
end
|
||||
|
||||
def method_missing(sym, *args)
|
||||
name = sym.to_s
|
||||
|
||||
if name[-1] == ?= and args.size == 1
|
||||
set_variable name[0..-2], args.first
|
||||
else
|
||||
get_variable name
|
||||
end
|
||||
end
|
||||
|
||||
def visit(visitor)
|
||||
visitor.grammar self
|
||||
end
|
||||
|
||||
def =~(string)
|
||||
end
|
||||
|
||||
def match(string)
|
||||
end
|
||||
end
|
||||
|
||||
class Always < Pattern
|
||||
def visit(visitor)
|
||||
visitor.always self
|
||||
end
|
||||
end
|
||||
|
||||
class AnyRange < Pattern
|
||||
attr_accessor :range
|
||||
|
||||
def initialize(range)
|
||||
@range = range
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def visit(visitor)
|
||||
visitor.any_range self
|
||||
end
|
||||
end
|
||||
|
||||
class Any < Pattern
|
||||
attr_accessor :count
|
||||
|
||||
def initialize(count)
|
||||
@count = count
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def visit(visitor)
|
||||
visitor.any self
|
||||
end
|
||||
end
|
||||
|
||||
class CharacterRange < Pattern
|
||||
attr_accessor :range
|
||||
|
||||
def initialize(range)
|
||||
@range = range
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def visit(visitor)
|
||||
visitor.character_range self
|
||||
end
|
||||
end
|
||||
|
||||
class Character < Pattern
|
||||
attr_accessor :string
|
||||
|
||||
def initialize(string)
|
||||
@string = string
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def visit(visitor)
|
||||
visitor.character self
|
||||
end
|
||||
end
|
||||
|
||||
class Choice < BinaryOp
|
||||
def visit(visitor)
|
||||
visitor.choice self
|
||||
end
|
||||
end
|
||||
|
||||
class Concatenation < BinaryOp
|
||||
def visit(visitor)
|
||||
visitor.concatenation self
|
||||
end
|
||||
end
|
||||
|
||||
class Difference < BinaryOp
|
||||
def visit(visitor)
|
||||
visitor.difference self
|
||||
end
|
||||
end
|
||||
|
||||
# Implements &patt in a PEG. The pattern succeeds if
|
||||
# patt matches. Does not consume any input.
|
||||
class If < UnaryOp
|
||||
def visit(visitor)
|
||||
visitor.if self
|
||||
end
|
||||
end
|
||||
|
||||
class Never < Pattern
|
||||
def visit(visitor)
|
||||
visitor.never self
|
||||
end
|
||||
end
|
||||
|
||||
class Product < Pattern
|
||||
attr_accessor :pattern
|
||||
attr_accessor :count
|
||||
|
||||
def initialize(pattern, count)
|
||||
@pattern = PEG.pattern pattern
|
||||
@count = Type.coerce_to(count, Integer, :to_int).abs
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def visit(visitor)
|
||||
visitor.product self
|
||||
end
|
||||
end
|
||||
|
||||
class Set < Pattern
|
||||
attr_accessor :set
|
||||
|
||||
def initialize(set)
|
||||
@set = set.uniq
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def visit(visitor)
|
||||
visitor.set self
|
||||
end
|
||||
end
|
||||
|
||||
# Implements !patt in a PEG. The pattern succeeds if
|
||||
# patt does not match. Does not consume any input.
|
||||
class Unless < UnaryOp
|
||||
def visit(visitor)
|
||||
visitor.unless self
|
||||
end
|
||||
end
|
||||
|
||||
class Variable < Pattern
|
||||
attr_accessor :name
|
||||
attr_accessor :pattern
|
||||
|
||||
def initialize(name, pattern=nil)
|
||||
@name = name.to_sym
|
||||
@pattern = PEG.pattern pattern
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def visit(visitor)
|
||||
visitor.variable self
|
||||
end
|
||||
end
|
||||
|
||||
# Accepts a Ruby object representation of simple PEG "atoms" and returns a
|
||||
# PEG AST node.
|
||||
def pattern(spec)
|
||||
case spec
|
||||
when Array
|
||||
Set.new spec
|
||||
when Integer
|
||||
node = Any.new spec.abs
|
||||
node = If.new node if spec < 0
|
||||
node
|
||||
when String
|
||||
Character.new spec
|
||||
when Pattern
|
||||
spec
|
||||
when true
|
||||
Always.new
|
||||
when false
|
||||
Never.new
|
||||
when nil
|
||||
nil
|
||||
else
|
||||
raise InvalidPatternError, "unknown specification type for Pattern: #{spec.inspect}"
|
||||
end
|
||||
end
|
||||
module_function :pattern
|
||||
|
||||
def grammar(spec=nil, pattern=nil)
|
||||
case spec
|
||||
when Symbol
|
||||
Grammar.new Variable.new(spec, pattern)
|
||||
when Variable
|
||||
Grammar.new spec
|
||||
when Pattern
|
||||
Grammar.new Variable.new(:S, spec)
|
||||
when nil
|
||||
Grammar.new
|
||||
else
|
||||
Grammar.new Variable.new(:S, pattern)
|
||||
end
|
||||
end
|
||||
module_function :grammar
|
||||
|
||||
def variable(name, pattern=nil)
|
||||
PEG::Variable.new name, pattern
|
||||
end
|
||||
module_function :variable
|
||||
|
||||
def self.compile(pattern)
|
||||
end
|
||||
|
||||
def self.peg
|
||||
g = grammar = grammar(:grammar)
|
||||
g.grammar = (g.nonterminal + "=" + g.sp + g.pattern) * 1
|
||||
g.pattern = g.alternative + (pattern("/") + g.sp + g.alternative) * 0
|
||||
g.alternative = ((+g.predicate + g.predicate + g.sp + g.suffix) /
|
||||
(g.sp + g.suffix)) * 1
|
||||
g.predicate = ["!&"]
|
||||
g.suffix = g.primary + (pattern(["*+?"]) + g.sp) * 0
|
||||
g.primary = (pattern("(") + g.sp + g.pattern + ")" + g.sp) / (pattern(1) + g.sp) /
|
||||
g.literal / g.char_class / (g.nonterminal + -pattern("="))
|
||||
g.literal = pattern("'") + (-pattern("'") + 1) * 0 + "'" + g.sp
|
||||
g.char_class = (pattern("[") + (-pattern("]") +
|
||||
((pattern(1) + "-" + 1) / 1)) * 0) + "]" + g.sp
|
||||
g.nonterminal = (pattern("_") / ["a".."z"] / ["A".."Z"]) * 1 + g.sp
|
||||
g.sp = pattern([" \t\n"]) * 0
|
||||
|
||||
grammar
|
||||
end
|
||||
end
|
||||
end
|
||||
468
gxg/core/pointer.rb
Normal file
468
gxg/core/pointer.rb
Normal file
|
|
@ -0,0 +1,468 @@
|
|||
module Rubinius
|
||||
module FFI
|
||||
# We need to define this so we can include it. Since the file is
|
||||
# auto-generated, we can't pull it in here but we don't want to break the
|
||||
# alphabetized load order for random cases.
|
||||
module PointerAccessors; end
|
||||
|
||||
##
|
||||
# Pointer is Rubinius's "fat" pointer class. It represents an actual
|
||||
# pointer, in C language terms, to an address in memory. They're called
|
||||
# fat pointers because the Pointer object is an wrapper around
|
||||
# the actual pointer, the Rubinius runtime doesn't have direct access
|
||||
# to the raw address.
|
||||
#
|
||||
# This class is used extensively in FFI usage to interface with various
|
||||
# parts of the underlying system. It provides a number of operations
|
||||
# for operating on the memory that is pointed to. These operations effectively
|
||||
# give Rubinius the cast/read capabilities available in C, but using
|
||||
# high level methods.
|
||||
#
|
||||
# MemoryPointer objects can be put in autorelease mode. In this mode,
|
||||
# when the GC cleans up a MemoryPointer object, the memory it points
|
||||
# to is passed to free(3), releasing the memory back to the OS.
|
||||
#
|
||||
# NOTE: MemoryPointer exposes direct, unmanaged operations on any
|
||||
# memory. It therefore MUST be used carefully. Reading or writing to
|
||||
# invalid address will cause bus errors and segmentation faults.
|
||||
#
|
||||
class Pointer
|
||||
include PointerAccessors
|
||||
|
||||
def initialize(a1, a2=undefined)
|
||||
if undefined.equal? a2
|
||||
self.address = a1
|
||||
else
|
||||
@type = a1
|
||||
self.address = a2
|
||||
end
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def inspect
|
||||
# Don't have this print the data at the location. It can crash everything.
|
||||
addr = address()
|
||||
|
||||
if addr < 0
|
||||
sign = "-"
|
||||
addr = -addr
|
||||
else
|
||||
sign = ""
|
||||
end
|
||||
|
||||
"#<#{self.class.name} address=#{sign}0x#{addr.to_s(16)}>"
|
||||
end
|
||||
|
||||
# Return the address pointed to as an Integer
|
||||
def address
|
||||
Rubinius.primitive :pointer_address
|
||||
raise PrimitiveFailure, "FFI::Pointer#address primitive failed"
|
||||
end
|
||||
|
||||
alias_method :to_i, :address
|
||||
|
||||
# Set the address pointed to from an Integer
|
||||
def address=(address)
|
||||
Rubinius.primitive :pointer_set_address
|
||||
raise PrimitiveFailure, "FFI::Pointer#address= primitive failed"
|
||||
end
|
||||
|
||||
def null?
|
||||
address == 0x0
|
||||
end
|
||||
|
||||
# Add +value+ to the address pointed to and return a new Pointer
|
||||
def +(value)
|
||||
Rubinius.primitive :pointer_add
|
||||
raise PrimitiveFailure, "FFI::Pointer#+ primitive failed"
|
||||
end
|
||||
|
||||
# Indicates if +self+ and +other+ point to the same address
|
||||
def ==(other)
|
||||
return false unless other.kind_of? Pointer
|
||||
return address == other.address
|
||||
end
|
||||
|
||||
def network_order(start, size)
|
||||
Rubinius.primitive :pointer_network_order
|
||||
raise PrimitiveFailure, "FFI::Pointer#network_order primitive failed"
|
||||
end
|
||||
|
||||
# Read +len+ bytes from the memory pointed to and return them as
|
||||
# a String
|
||||
def read_string_length(len)
|
||||
Rubinius.primitive :pointer_read_string
|
||||
raise PrimitiveFailure, "FFI::Pointer#read_string_length primitive failed"
|
||||
end
|
||||
|
||||
# Read bytes from the memory pointed to until a NULL is seen, return
|
||||
# the bytes as a String
|
||||
def read_string_to_null
|
||||
Rubinius.primitive :pointer_read_string_to_null
|
||||
raise PrimitiveFailure, "FFI::Pointer#read_string_to_null primitive failed"
|
||||
end
|
||||
|
||||
# Read bytes as a String from the memory pointed to
|
||||
def read_string(len=nil)
|
||||
if len
|
||||
read_string_length(len)
|
||||
else
|
||||
read_string_to_null
|
||||
end
|
||||
end
|
||||
|
||||
# FFI compat methods
|
||||
def get_bytes(offset, length)
|
||||
(self + offset).read_string_length(length)
|
||||
end
|
||||
|
||||
# Write String +str+ as bytes into the memory pointed to. Only
|
||||
# write up to +len+ bytes.
|
||||
def write_string_length(str, len)
|
||||
Rubinius.primitive :pointer_write_string
|
||||
raise PrimitiveFailure, "FFI::Pointer#write_string_length primitive failed"
|
||||
end
|
||||
|
||||
# Write a String +str+ as bytes to the memory pointed to.
|
||||
def write_string(str, len=nil)
|
||||
len = str.bytesize unless len
|
||||
|
||||
write_string_length(str, len);
|
||||
end
|
||||
|
||||
# Read a sequence of types +type+, length +length+, using method +reader+
|
||||
def read_array_of_type(type, reader, length, signed=nil)
|
||||
# If signed is not nil and is actually a boolean,
|
||||
# then use that as an argument to the reader, which
|
||||
# is then assumed to support signed reading.
|
||||
args = []
|
||||
args = [signed] if !signed.nil?
|
||||
|
||||
# Build up the array
|
||||
ary = []
|
||||
size = FFI.type_size(FFI.find_type type)
|
||||
tmp = self
|
||||
length.times {
|
||||
ary << tmp.send(reader, *args)
|
||||
tmp += size
|
||||
}
|
||||
ary
|
||||
end
|
||||
|
||||
# Write a sequence of types +type+ using method +reader+ from +ary+
|
||||
def write_array_of_type(type, writer, ary)
|
||||
size = FFI.type_size(FFI.find_type type)
|
||||
tmp = self
|
||||
ary.each do |i|
|
||||
tmp.send(writer, i)
|
||||
tmp += size
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
# Read bytes from +offset+ from the memory pointed to as type +type+
|
||||
def get_at_offset(offset, type)
|
||||
Rubinius.primitive :pointer_get_at_offset
|
||||
raise PrimitiveFailure, "FFI::Pointer#get_at_offset primitive failed"
|
||||
end
|
||||
|
||||
# Write +val+ as type +type+ to bytes from +offset+
|
||||
def set_at_offset(offset, type, val)
|
||||
Rubinius.primitive :pointer_set_at_offset
|
||||
raise PrimitiveFailure, "FFI::Pointer#set_at_offset primitive failed"
|
||||
end
|
||||
|
||||
# Number of bytes taken up by a pointer.
|
||||
def self.size
|
||||
Rubinius::WORDSIZE / 8
|
||||
end
|
||||
|
||||
# Primitive methods
|
||||
def primitive_read_char(signed)
|
||||
Rubinius.primitive :pointer_read_char
|
||||
raise PrimitiveFailure, "FFI::Pointer#primitive_read_char primitive failed"
|
||||
end
|
||||
|
||||
def primitive_write_char(obj)
|
||||
Rubinius.primitive :pointer_write_char
|
||||
raise PrimitiveFailure, "FFI::Pointer#primitive_write_char primitive failed"
|
||||
end
|
||||
|
||||
def primitive_read_short(signed)
|
||||
Rubinius.primitive :pointer_read_short
|
||||
raise PrimitiveFailure, "FFI::Pointer#primitive_read_short primitive failed"
|
||||
end
|
||||
|
||||
def primitive_write_short(obj)
|
||||
Rubinius.primitive :pointer_write_short
|
||||
raise PrimitiveFailure, "FFI::Pointer#primitive_write_short primitive failed"
|
||||
end
|
||||
|
||||
def primitive_read_int(signed)
|
||||
Rubinius.primitive :pointer_read_int
|
||||
raise PrimitiveFailure, "FFI::Pointer#primitive_read_int primitive failed"
|
||||
end
|
||||
|
||||
def primitive_write_int(obj)
|
||||
Rubinius.primitive :pointer_write_int
|
||||
raise PrimitiveFailure, "FFI::Pointer#primitive_write_int primitive failed"
|
||||
end
|
||||
|
||||
def primitive_read_long(signed)
|
||||
Rubinius.primitive :pointer_read_long
|
||||
raise PrimitiveFailure, "FFI::Pointer#primitive_read_long primitive failed"
|
||||
end
|
||||
|
||||
def primitive_write_long(obj)
|
||||
Rubinius.primitive :pointer_write_long
|
||||
raise PrimitiveFailure, "FFI::Pointer#primitive_write_long primitive failed"
|
||||
end
|
||||
|
||||
def primitive_read_long_long(signed)
|
||||
Rubinius.primitive :pointer_read_long_long
|
||||
raise PrimitiveFailure, "FFI::Pointer#primitive_read_long_long primitive failed"
|
||||
end
|
||||
|
||||
def primitive_write_long_long(obj)
|
||||
Rubinius.primitive :pointer_write_long_long
|
||||
raise PrimitiveFailure, "FFI::Pointer#primitive_write_long_long primitive failed"
|
||||
end
|
||||
|
||||
def primitive_read_float
|
||||
Rubinius.primitive :pointer_read_float
|
||||
raise PrimitiveFailure, "FFI::Pointer#primitive_read_float primitive failed"
|
||||
end
|
||||
|
||||
def primitive_write_float(obj)
|
||||
Rubinius.primitive :pointer_write_float
|
||||
raise PrimitiveFailure, "FFI::Pointer#primitive_write_float primitive failed"
|
||||
end
|
||||
|
||||
def primitive_read_double
|
||||
Rubinius.primitive :pointer_read_double
|
||||
raise PrimitiveFailure, "FFI::Pointer#primitive_read_double primitive failed"
|
||||
end
|
||||
|
||||
def primitive_write_double(obj)
|
||||
Rubinius.primitive :pointer_write_double
|
||||
raise PrimitiveFailure, "FFI::Pointer#primitive_write_double primitive failed"
|
||||
end
|
||||
|
||||
def primitive_read_pointer
|
||||
Rubinius.primitive :pointer_read_pointer
|
||||
raise PrimitiveFailure, "FFI::Pointer#primitive_read_pointer primitive failed"
|
||||
end
|
||||
|
||||
def primitive_write_pointer(obj)
|
||||
Rubinius.primitive :pointer_write_pointer
|
||||
raise PrimitiveFailure, "FFI::Pointer#primitive_write_pointer primitive failed"
|
||||
end
|
||||
|
||||
##
|
||||
# If +val+ is true, this Pointer object will call
|
||||
# free() on it's address when it is garbage collected.
|
||||
def autorelease=(val)
|
||||
Rubinius.primitive :pointer_set_autorelease
|
||||
raise PrimitiveFailure, "FFI::Pointer#autorelease= primitive failed"
|
||||
end
|
||||
|
||||
##
|
||||
# Returns true if autorelease is enabled, otherwise false.
|
||||
def autorelease?
|
||||
Rubinius.primitive :pointer_autorelease_p
|
||||
raise PrimitiveFailure, "FFI::Pointer#pointer_autorelease_p primitive failed"
|
||||
end
|
||||
|
||||
NULL = Pointer.new(0x0)
|
||||
end
|
||||
|
||||
class MemoryPointer < Pointer
|
||||
|
||||
# call-seq:
|
||||
# MemoryPointer.new(num) => MemoryPointer instance of <i>num</i> bytes
|
||||
# MemoryPointer.new(sym) => MemoryPointer instance with number
|
||||
# of bytes need by FFI type <i>sym</i>
|
||||
# MemoryPointer.new(obj) => MemoryPointer instance with number
|
||||
# of <i>obj.size</i> bytes
|
||||
# MemoryPointer.new(sym, count) => MemoryPointer instance with number
|
||||
# of bytes need by length-<i>count</i> array
|
||||
# of FFI type <i>sym</i>
|
||||
# MemoryPointer.new(obj, count) => MemoryPointer instance with number
|
||||
# of bytes need by length-<i>count</i> array
|
||||
# of <i>obj.size</i> bytes
|
||||
# MemoryPointer.new(arg) { |p| ... }
|
||||
#
|
||||
# Both forms create a MemoryPointer instance. The number of bytes to
|
||||
# allocate is either specified directly or by passing an FFI type, which
|
||||
# specifies the number of bytes needed for that type.
|
||||
#
|
||||
# The form without a block returns the MemoryPointer instance. The form
|
||||
# with a block yields the MemoryPointer instance and frees the memory
|
||||
# when the block returns. The value returned is the value of the block.
|
||||
#
|
||||
def self.new(type, count=nil, clear=true)
|
||||
if type.kind_of? Fixnum
|
||||
size = type
|
||||
elsif type.kind_of? Symbol
|
||||
type = FFI.find_type type
|
||||
size = FFI.type_size(type)
|
||||
else
|
||||
size = type.size
|
||||
end
|
||||
|
||||
if count
|
||||
total = size * count
|
||||
else
|
||||
total = size
|
||||
end
|
||||
|
||||
return NULL if total < 0
|
||||
|
||||
ptr = malloc total
|
||||
ptr.total = total
|
||||
ptr.type_size = size
|
||||
FFI::Platform::POSIX.memset ptr, 0, total if clear
|
||||
|
||||
if block_given?
|
||||
begin
|
||||
value = yield ptr
|
||||
ensure
|
||||
ptr.free
|
||||
end
|
||||
|
||||
return value
|
||||
else
|
||||
ptr.autorelease = true
|
||||
ptr
|
||||
end
|
||||
end
|
||||
|
||||
def self.malloc(total)
|
||||
Rubinius.primitive :pointer_malloc
|
||||
raise PrimitiveFailure, "FFI::MemoryPointer.malloc primitive failed"
|
||||
end
|
||||
|
||||
def self.from_string(str)
|
||||
ptr = new str.bytesize + 1
|
||||
ptr.write_string str + "\0"
|
||||
|
||||
ptr
|
||||
end
|
||||
|
||||
def copy
|
||||
other = malloc total
|
||||
other.total = total
|
||||
other.type_size = type_size
|
||||
FFI::Platform::POSIX.memcpy other, self, total
|
||||
|
||||
Rubinius.privately do
|
||||
other.initialize_copy self
|
||||
end
|
||||
|
||||
other
|
||||
end
|
||||
|
||||
# Indicates how many bytes the chunk of memory that is pointed to takes up.
|
||||
attr_accessor :total
|
||||
|
||||
# Indicates how many bytes the type that the pointer is cast as uses.
|
||||
attr_accessor :type_size
|
||||
|
||||
# Access the MemoryPointer like a C array, accessing the +which+ number
|
||||
# element in memory. The position of the element is calculate from
|
||||
# +@type_size+ and +which+. A new MemoryPointer object is returned, which
|
||||
# points to the address of the element.
|
||||
#
|
||||
# Example:
|
||||
# ptr = MemoryPointer.new(:int, 20)
|
||||
# new_ptr = ptr[9]
|
||||
#
|
||||
# c-equiv:
|
||||
# int *ptr = (int*)malloc(sizeof(int) * 20);
|
||||
# int *new_ptr;
|
||||
# new_ptr = &ptr[9];
|
||||
#
|
||||
def [](which)
|
||||
raise ArgumentError, "unknown type size" unless @type_size
|
||||
self + (which * @type_size)
|
||||
end
|
||||
|
||||
# Release the memory pointed to back to the OS.
|
||||
def free
|
||||
Rubinius.primitive :pointer_free
|
||||
raise PrimitiveFailure, "FFI::MemoryPointer#free primitive failed"
|
||||
end
|
||||
end
|
||||
|
||||
class DynamicLibrary
|
||||
class Symbol < Pointer
|
||||
def initialize(library, ptr, name)
|
||||
@library = library
|
||||
@name = name
|
||||
self.address = ptr.address
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def inspect
|
||||
"#<FFI::Library::Symbol name=#{@name} address=#{address.to_s(16)}>"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Function < Pointer
|
||||
def initialize(ret_type, arg_types, val=nil, options=nil, &block)
|
||||
if block
|
||||
if val or options
|
||||
raise ArgumentError, "specify a block or a proc/address, not both"
|
||||
end
|
||||
|
||||
val = block
|
||||
end
|
||||
|
||||
args = arg_types.map { |x| FFI.find_type(x) }
|
||||
ret = FFI.find_type(ret_type)
|
||||
|
||||
if val.kind_of? Pointer
|
||||
@function = FFI.generate_function(val, :func, args, ret)
|
||||
self.address = val.address
|
||||
elsif val.respond_to? :call
|
||||
@function, ptr = FFI.generate_trampoline(val, :func, args, ret)
|
||||
self.address = ptr.address
|
||||
else
|
||||
raise ArgumentError, "value wasn't a FFI::Pointer and didn't respond to call"
|
||||
end
|
||||
|
||||
# Hook the created function into the method_table so that #call goes
|
||||
# straight to it.
|
||||
sc = Rubinius::Type.object_singleton_class(self)
|
||||
Rubinius::VM.reset_method_cache sc, :call
|
||||
sc.method_table.store :call, nil, @function, nil, 0, :public
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
attr_reader :function
|
||||
|
||||
# Hook this Function up to be an instance/class method +name+ on +mod+
|
||||
def attach(mod, name)
|
||||
unless mod.kind_of?(Module)
|
||||
raise TypeError, "mod must be a Module"
|
||||
end
|
||||
|
||||
name = name.to_sym
|
||||
|
||||
# Make it available as a method callable directly..
|
||||
sc = Rubinius::Type.object_singleton_class(mod)
|
||||
Rubinius::VM.reset_method_cache sc, name
|
||||
sc.method_table.store name, nil, @function, nil, 0, :public
|
||||
|
||||
# and expose it as a private method for people who
|
||||
# want to include this module.
|
||||
Rubinius::VM.reset_method_cache mod, name
|
||||
mod.method_table.store name, nil, @function, nil, 0, :public
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
838
gxg/core/pointer_accessors.rb
Normal file
838
gxg/core/pointer_accessors.rb
Normal file
|
|
@ -0,0 +1,838 @@
|
|||
# This file is automatically generated. It should _not_ be
|
||||
# modified manually. Due to the vast amount of very similar
|
||||
# methods in this module, it is automatically generated.
|
||||
|
||||
module Rubinius
|
||||
module FFI
|
||||
##
|
||||
# This module provides accessors for various different types. It
|
||||
# provides methods for:
|
||||
#
|
||||
# * reading data
|
||||
# * writing data
|
||||
# * reading an array of some data
|
||||
# * writing an array of some data
|
||||
module PointerAccessors
|
||||
#
|
||||
# Name: int8
|
||||
# Type: char
|
||||
# Signed: true
|
||||
#
|
||||
|
||||
def read_int8(signed=true)
|
||||
primitive_read_char(signed)
|
||||
end
|
||||
|
||||
def write_int8(obj)
|
||||
primitive_write_char(obj)
|
||||
end
|
||||
|
||||
def get_int8(offset)
|
||||
(self + offset).read_int8
|
||||
end
|
||||
|
||||
def put_int8(offset, obj)
|
||||
(self + offset).write_int8(obj)
|
||||
end
|
||||
|
||||
def read_array_of_int8(length, signed=true)
|
||||
read_array_of_type(:char, :read_char, length, signed)
|
||||
end
|
||||
|
||||
def write_array_of_int8(ary)
|
||||
write_array_of_type(:char, :write_char, ary)
|
||||
end
|
||||
|
||||
def get_array_of_int8(offset, length, signed=true)
|
||||
(self + offset).read_array_of_int8(length, signed)
|
||||
end
|
||||
|
||||
def put_array_of_int8(offset, ary)
|
||||
(self + offset).write_array_of_int8(ary)
|
||||
end
|
||||
|
||||
def read_uint8
|
||||
self.read_int8(false)
|
||||
end
|
||||
|
||||
def write_uint8(obj)
|
||||
self.write_int8(obj)
|
||||
end
|
||||
|
||||
def get_uint8(offset)
|
||||
(self + offset).read_uint8
|
||||
end
|
||||
|
||||
def put_uint8(offset, obj)
|
||||
(self + offset).write_uint8(obj)
|
||||
end
|
||||
|
||||
def read_array_of_uint8(length)
|
||||
self.read_array_of_int8(length, false)
|
||||
end
|
||||
|
||||
def write_array_of_uint8(ary)
|
||||
self.write_array_of_int8(ary)
|
||||
end
|
||||
|
||||
def get_array_of_uint8(offset, length)
|
||||
(self + offset).read_array_of_uint8(length)
|
||||
end
|
||||
|
||||
def put_array_of_uint8(offset, ary)
|
||||
(self + offset).write_array_of_uint8(ary)
|
||||
end
|
||||
|
||||
#
|
||||
# Name: int16
|
||||
# Type: short
|
||||
# Signed: true
|
||||
#
|
||||
|
||||
def read_int16(signed=true)
|
||||
primitive_read_short(signed)
|
||||
end
|
||||
|
||||
def write_int16(obj)
|
||||
primitive_write_short(obj)
|
||||
end
|
||||
|
||||
def get_int16(offset)
|
||||
(self + offset).read_int16
|
||||
end
|
||||
|
||||
def put_int16(offset, obj)
|
||||
(self + offset).write_int16(obj)
|
||||
end
|
||||
|
||||
def read_array_of_int16(length, signed=true)
|
||||
read_array_of_type(:short, :read_short, length, signed)
|
||||
end
|
||||
|
||||
def write_array_of_int16(ary)
|
||||
write_array_of_type(:short, :write_short, ary)
|
||||
end
|
||||
|
||||
def get_array_of_int16(offset, length, signed=true)
|
||||
(self + offset).read_array_of_int16(length, signed)
|
||||
end
|
||||
|
||||
def put_array_of_int16(offset, ary)
|
||||
(self + offset).write_array_of_int16(ary)
|
||||
end
|
||||
|
||||
def read_uint16
|
||||
self.read_int16(false)
|
||||
end
|
||||
|
||||
def write_uint16(obj)
|
||||
self.write_int16(obj)
|
||||
end
|
||||
|
||||
def get_uint16(offset)
|
||||
(self + offset).read_uint16
|
||||
end
|
||||
|
||||
def put_uint16(offset, obj)
|
||||
(self + offset).write_uint16(obj)
|
||||
end
|
||||
|
||||
def read_array_of_uint16(length)
|
||||
self.read_array_of_int16(length, false)
|
||||
end
|
||||
|
||||
def write_array_of_uint16(ary)
|
||||
self.write_array_of_int16(ary)
|
||||
end
|
||||
|
||||
def get_array_of_uint16(offset, length)
|
||||
(self + offset).read_array_of_uint16(length)
|
||||
end
|
||||
|
||||
def put_array_of_uint16(offset, ary)
|
||||
(self + offset).write_array_of_uint16(ary)
|
||||
end
|
||||
|
||||
#
|
||||
# Name: int32
|
||||
# Type: int
|
||||
# Signed: true
|
||||
#
|
||||
|
||||
def read_int32(signed=true)
|
||||
primitive_read_int(signed)
|
||||
end
|
||||
|
||||
def write_int32(obj)
|
||||
primitive_write_int(obj)
|
||||
end
|
||||
|
||||
def get_int32(offset)
|
||||
(self + offset).read_int32
|
||||
end
|
||||
|
||||
def put_int32(offset, obj)
|
||||
(self + offset).write_int32(obj)
|
||||
end
|
||||
|
||||
def read_array_of_int32(length, signed=true)
|
||||
read_array_of_type(:int, :read_int, length, signed)
|
||||
end
|
||||
|
||||
def write_array_of_int32(ary)
|
||||
write_array_of_type(:int, :write_int, ary)
|
||||
end
|
||||
|
||||
def get_array_of_int32(offset, length, signed=true)
|
||||
(self + offset).read_array_of_int32(length, signed)
|
||||
end
|
||||
|
||||
def put_array_of_int32(offset, ary)
|
||||
(self + offset).write_array_of_int32(ary)
|
||||
end
|
||||
|
||||
def read_uint32
|
||||
self.read_int32(false)
|
||||
end
|
||||
|
||||
def write_uint32(obj)
|
||||
self.write_int32(obj)
|
||||
end
|
||||
|
||||
def get_uint32(offset)
|
||||
(self + offset).read_uint32
|
||||
end
|
||||
|
||||
def put_uint32(offset, obj)
|
||||
(self + offset).write_uint32(obj)
|
||||
end
|
||||
|
||||
def read_array_of_uint32(length)
|
||||
self.read_array_of_int32(length, false)
|
||||
end
|
||||
|
||||
def write_array_of_uint32(ary)
|
||||
self.write_array_of_int32(ary)
|
||||
end
|
||||
|
||||
def get_array_of_uint32(offset, length)
|
||||
(self + offset).read_array_of_uint32(length)
|
||||
end
|
||||
|
||||
def put_array_of_uint32(offset, ary)
|
||||
(self + offset).write_array_of_uint32(ary)
|
||||
end
|
||||
|
||||
#
|
||||
# Name: int64
|
||||
# Type: long_long
|
||||
# Signed: true
|
||||
#
|
||||
|
||||
def read_int64(signed=true)
|
||||
primitive_read_long_long(signed)
|
||||
end
|
||||
|
||||
def write_int64(obj)
|
||||
primitive_write_long_long(obj)
|
||||
end
|
||||
|
||||
def get_int64(offset)
|
||||
(self + offset).read_int64
|
||||
end
|
||||
|
||||
def put_int64(offset, obj)
|
||||
(self + offset).write_int64(obj)
|
||||
end
|
||||
|
||||
def read_array_of_int64(length, signed=true)
|
||||
read_array_of_type(:long_long, :read_long_long, length, signed)
|
||||
end
|
||||
|
||||
def write_array_of_int64(ary)
|
||||
write_array_of_type(:long_long, :write_long_long, ary)
|
||||
end
|
||||
|
||||
def get_array_of_int64(offset, length, signed=true)
|
||||
(self + offset).read_array_of_int64(length, signed)
|
||||
end
|
||||
|
||||
def put_array_of_int64(offset, ary)
|
||||
(self + offset).write_array_of_int64(ary)
|
||||
end
|
||||
|
||||
def read_uint64
|
||||
self.read_int64(false)
|
||||
end
|
||||
|
||||
def write_uint64(obj)
|
||||
self.write_int64(obj)
|
||||
end
|
||||
|
||||
def get_uint64(offset)
|
||||
(self + offset).read_uint64
|
||||
end
|
||||
|
||||
def put_uint64(offset, obj)
|
||||
(self + offset).write_uint64(obj)
|
||||
end
|
||||
|
||||
def read_array_of_uint64(length)
|
||||
self.read_array_of_int64(length, false)
|
||||
end
|
||||
|
||||
def write_array_of_uint64(ary)
|
||||
self.write_array_of_int64(ary)
|
||||
end
|
||||
|
||||
def get_array_of_uint64(offset, length)
|
||||
(self + offset).read_array_of_uint64(length)
|
||||
end
|
||||
|
||||
def put_array_of_uint64(offset, ary)
|
||||
(self + offset).write_array_of_uint64(ary)
|
||||
end
|
||||
|
||||
#
|
||||
# Name: long
|
||||
# Type: long
|
||||
# Signed: true
|
||||
#
|
||||
|
||||
def read_long(signed=true)
|
||||
primitive_read_long(signed)
|
||||
end
|
||||
|
||||
def write_long(obj)
|
||||
primitive_write_long(obj)
|
||||
end
|
||||
|
||||
def get_long(offset)
|
||||
(self + offset).read_long
|
||||
end
|
||||
|
||||
def put_long(offset, obj)
|
||||
(self + offset).write_long(obj)
|
||||
end
|
||||
|
||||
def read_array_of_long(length, signed=true)
|
||||
read_array_of_type(:long, :read_long, length, signed)
|
||||
end
|
||||
|
||||
def write_array_of_long(ary)
|
||||
write_array_of_type(:long, :write_long, ary)
|
||||
end
|
||||
|
||||
def get_array_of_long(offset, length, signed=true)
|
||||
(self + offset).read_array_of_long(length, signed)
|
||||
end
|
||||
|
||||
def put_array_of_long(offset, ary)
|
||||
(self + offset).write_array_of_long(ary)
|
||||
end
|
||||
|
||||
def read_ulong
|
||||
self.read_long(false)
|
||||
end
|
||||
|
||||
def write_ulong(obj)
|
||||
self.write_long(obj)
|
||||
end
|
||||
|
||||
def get_ulong(offset)
|
||||
(self + offset).read_ulong
|
||||
end
|
||||
|
||||
def put_ulong(offset, obj)
|
||||
(self + offset).write_ulong(obj)
|
||||
end
|
||||
|
||||
def read_array_of_ulong(length)
|
||||
self.read_array_of_long(length, false)
|
||||
end
|
||||
|
||||
def write_array_of_ulong(ary)
|
||||
self.write_array_of_long(ary)
|
||||
end
|
||||
|
||||
def get_array_of_ulong(offset, length)
|
||||
(self + offset).read_array_of_ulong(length)
|
||||
end
|
||||
|
||||
def put_array_of_ulong(offset, ary)
|
||||
(self + offset).write_array_of_ulong(ary)
|
||||
end
|
||||
|
||||
#
|
||||
# Name: float32
|
||||
# Type: float
|
||||
# Signed: false
|
||||
#
|
||||
|
||||
def read_float32
|
||||
primitive_read_float
|
||||
end
|
||||
|
||||
def write_float32(obj)
|
||||
primitive_write_float(obj)
|
||||
end
|
||||
|
||||
def get_float32(offset)
|
||||
(self + offset).read_float32
|
||||
end
|
||||
|
||||
def put_float32(offset, obj)
|
||||
(self + offset).write_float32(obj)
|
||||
end
|
||||
|
||||
def read_array_of_float32(length)
|
||||
read_array_of_type(:float, :read_float, length)
|
||||
end
|
||||
|
||||
def write_array_of_float32(ary)
|
||||
write_array_of_type(:float, :write_float, ary)
|
||||
end
|
||||
|
||||
def get_array_of_float32(offset, length)
|
||||
(self + offset).read_array_of_float32(length)
|
||||
end
|
||||
|
||||
def put_array_of_float32(offset, ary)
|
||||
(self + offset).write_array_of_float32(ary)
|
||||
end
|
||||
|
||||
#
|
||||
# Name: float64
|
||||
# Type: double
|
||||
# Signed: false
|
||||
#
|
||||
|
||||
def read_float64
|
||||
primitive_read_double
|
||||
end
|
||||
|
||||
def write_float64(obj)
|
||||
primitive_write_double(obj)
|
||||
end
|
||||
|
||||
def get_float64(offset)
|
||||
(self + offset).read_float64
|
||||
end
|
||||
|
||||
def put_float64(offset, obj)
|
||||
(self + offset).write_float64(obj)
|
||||
end
|
||||
|
||||
def read_array_of_float64(length)
|
||||
read_array_of_type(:double, :read_double, length)
|
||||
end
|
||||
|
||||
def write_array_of_float64(ary)
|
||||
write_array_of_type(:double, :write_double, ary)
|
||||
end
|
||||
|
||||
def get_array_of_float64(offset, length)
|
||||
(self + offset).read_array_of_float64(length)
|
||||
end
|
||||
|
||||
def put_array_of_float64(offset, ary)
|
||||
(self + offset).write_array_of_float64(ary)
|
||||
end
|
||||
|
||||
#
|
||||
# Name: float
|
||||
# Type: float
|
||||
# Signed: false
|
||||
#
|
||||
|
||||
def read_float
|
||||
primitive_read_float
|
||||
end
|
||||
|
||||
def write_float(obj)
|
||||
primitive_write_float(obj)
|
||||
end
|
||||
|
||||
def get_float(offset)
|
||||
(self + offset).read_float
|
||||
end
|
||||
|
||||
def put_float(offset, obj)
|
||||
(self + offset).write_float(obj)
|
||||
end
|
||||
|
||||
def read_array_of_float(length)
|
||||
read_array_of_type(:float, :read_float, length)
|
||||
end
|
||||
|
||||
def write_array_of_float(ary)
|
||||
write_array_of_type(:float, :write_float, ary)
|
||||
end
|
||||
|
||||
def get_array_of_float(offset, length)
|
||||
(self + offset).read_array_of_float(length)
|
||||
end
|
||||
|
||||
def put_array_of_float(offset, ary)
|
||||
(self + offset).write_array_of_float(ary)
|
||||
end
|
||||
|
||||
#
|
||||
# Name: double
|
||||
# Type: double
|
||||
# Signed: false
|
||||
#
|
||||
|
||||
def read_double
|
||||
primitive_read_double
|
||||
end
|
||||
|
||||
def write_double(obj)
|
||||
primitive_write_double(obj)
|
||||
end
|
||||
|
||||
def get_double(offset)
|
||||
(self + offset).read_double
|
||||
end
|
||||
|
||||
def put_double(offset, obj)
|
||||
(self + offset).write_double(obj)
|
||||
end
|
||||
|
||||
def read_array_of_double(length)
|
||||
read_array_of_type(:double, :read_double, length)
|
||||
end
|
||||
|
||||
def write_array_of_double(ary)
|
||||
write_array_of_type(:double, :write_double, ary)
|
||||
end
|
||||
|
||||
def get_array_of_double(offset, length)
|
||||
(self + offset).read_array_of_double(length)
|
||||
end
|
||||
|
||||
def put_array_of_double(offset, ary)
|
||||
(self + offset).write_array_of_double(ary)
|
||||
end
|
||||
|
||||
#
|
||||
# Name: char
|
||||
# Type: char
|
||||
# Signed: true
|
||||
#
|
||||
|
||||
def read_char(signed=true)
|
||||
primitive_read_char(signed)
|
||||
end
|
||||
|
||||
def write_char(obj)
|
||||
primitive_write_char(obj)
|
||||
end
|
||||
|
||||
def get_char(offset)
|
||||
(self + offset).read_char
|
||||
end
|
||||
|
||||
def put_char(offset, obj)
|
||||
(self + offset).write_char(obj)
|
||||
end
|
||||
|
||||
def read_array_of_char(length, signed=true)
|
||||
read_array_of_type(:char, :read_char, length, signed)
|
||||
end
|
||||
|
||||
def write_array_of_char(ary)
|
||||
write_array_of_type(:char, :write_char, ary)
|
||||
end
|
||||
|
||||
def get_array_of_char(offset, length, signed=true)
|
||||
(self + offset).read_array_of_char(length, signed)
|
||||
end
|
||||
|
||||
def put_array_of_char(offset, ary)
|
||||
(self + offset).write_array_of_char(ary)
|
||||
end
|
||||
|
||||
def read_uchar
|
||||
self.read_char(false)
|
||||
end
|
||||
|
||||
def write_uchar(obj)
|
||||
self.write_char(obj)
|
||||
end
|
||||
|
||||
def get_uchar(offset)
|
||||
(self + offset).read_uchar
|
||||
end
|
||||
|
||||
def put_uchar(offset, obj)
|
||||
(self + offset).write_uchar(obj)
|
||||
end
|
||||
|
||||
def read_array_of_uchar(length)
|
||||
self.read_array_of_char(length, false)
|
||||
end
|
||||
|
||||
def write_array_of_uchar(ary)
|
||||
self.write_array_of_char(ary)
|
||||
end
|
||||
|
||||
def get_array_of_uchar(offset, length)
|
||||
(self + offset).read_array_of_uchar(length)
|
||||
end
|
||||
|
||||
def put_array_of_uchar(offset, ary)
|
||||
(self + offset).write_array_of_uchar(ary)
|
||||
end
|
||||
|
||||
#
|
||||
# Name: short
|
||||
# Type: short
|
||||
# Signed: true
|
||||
#
|
||||
|
||||
def read_short(signed=true)
|
||||
primitive_read_short(signed)
|
||||
end
|
||||
|
||||
def write_short(obj)
|
||||
primitive_write_short(obj)
|
||||
end
|
||||
|
||||
def get_short(offset)
|
||||
(self + offset).read_short
|
||||
end
|
||||
|
||||
def put_short(offset, obj)
|
||||
(self + offset).write_short(obj)
|
||||
end
|
||||
|
||||
def read_array_of_short(length, signed=true)
|
||||
read_array_of_type(:short, :read_short, length, signed)
|
||||
end
|
||||
|
||||
def write_array_of_short(ary)
|
||||
write_array_of_type(:short, :write_short, ary)
|
||||
end
|
||||
|
||||
def get_array_of_short(offset, length, signed=true)
|
||||
(self + offset).read_array_of_short(length, signed)
|
||||
end
|
||||
|
||||
def put_array_of_short(offset, ary)
|
||||
(self + offset).write_array_of_short(ary)
|
||||
end
|
||||
|
||||
def read_ushort
|
||||
self.read_short(false)
|
||||
end
|
||||
|
||||
def write_ushort(obj)
|
||||
self.write_short(obj)
|
||||
end
|
||||
|
||||
def get_ushort(offset)
|
||||
(self + offset).read_ushort
|
||||
end
|
||||
|
||||
def put_ushort(offset, obj)
|
||||
(self + offset).write_ushort(obj)
|
||||
end
|
||||
|
||||
def read_array_of_ushort(length)
|
||||
self.read_array_of_short(length, false)
|
||||
end
|
||||
|
||||
def write_array_of_ushort(ary)
|
||||
self.write_array_of_short(ary)
|
||||
end
|
||||
|
||||
def get_array_of_ushort(offset, length)
|
||||
(self + offset).read_array_of_ushort(length)
|
||||
end
|
||||
|
||||
def put_array_of_ushort(offset, ary)
|
||||
(self + offset).write_array_of_ushort(ary)
|
||||
end
|
||||
|
||||
#
|
||||
# Name: int
|
||||
# Type: int
|
||||
# Signed: true
|
||||
#
|
||||
|
||||
def read_int(signed=true)
|
||||
primitive_read_int(signed)
|
||||
end
|
||||
|
||||
def write_int(obj)
|
||||
primitive_write_int(obj)
|
||||
end
|
||||
|
||||
def get_int(offset)
|
||||
(self + offset).read_int
|
||||
end
|
||||
|
||||
def put_int(offset, obj)
|
||||
(self + offset).write_int(obj)
|
||||
end
|
||||
|
||||
def read_array_of_int(length, signed=true)
|
||||
read_array_of_type(:int, :read_int, length, signed)
|
||||
end
|
||||
|
||||
def write_array_of_int(ary)
|
||||
write_array_of_type(:int, :write_int, ary)
|
||||
end
|
||||
|
||||
def get_array_of_int(offset, length, signed=true)
|
||||
(self + offset).read_array_of_int(length, signed)
|
||||
end
|
||||
|
||||
def put_array_of_int(offset, ary)
|
||||
(self + offset).write_array_of_int(ary)
|
||||
end
|
||||
|
||||
def read_uint
|
||||
self.read_int(false)
|
||||
end
|
||||
|
||||
def write_uint(obj)
|
||||
self.write_int(obj)
|
||||
end
|
||||
|
||||
def get_uint(offset)
|
||||
(self + offset).read_uint
|
||||
end
|
||||
|
||||
def put_uint(offset, obj)
|
||||
(self + offset).write_uint(obj)
|
||||
end
|
||||
|
||||
def read_array_of_uint(length)
|
||||
self.read_array_of_int(length, false)
|
||||
end
|
||||
|
||||
def write_array_of_uint(ary)
|
||||
self.write_array_of_int(ary)
|
||||
end
|
||||
|
||||
def get_array_of_uint(offset, length)
|
||||
(self + offset).read_array_of_uint(length)
|
||||
end
|
||||
|
||||
def put_array_of_uint(offset, ary)
|
||||
(self + offset).write_array_of_uint(ary)
|
||||
end
|
||||
|
||||
#
|
||||
# Name: long_long
|
||||
# Type: long_long
|
||||
# Signed: true
|
||||
#
|
||||
|
||||
def read_long_long(signed=true)
|
||||
primitive_read_long_long(signed)
|
||||
end
|
||||
|
||||
def write_long_long(obj)
|
||||
primitive_write_long_long(obj)
|
||||
end
|
||||
|
||||
def get_long_long(offset)
|
||||
(self + offset).read_long_long
|
||||
end
|
||||
|
||||
def put_long_long(offset, obj)
|
||||
(self + offset).write_long_long(obj)
|
||||
end
|
||||
|
||||
def read_array_of_long_long(length, signed=true)
|
||||
read_array_of_type(:long_long, :read_long_long, length, signed)
|
||||
end
|
||||
|
||||
def write_array_of_long_long(ary)
|
||||
write_array_of_type(:long_long, :write_long_long, ary)
|
||||
end
|
||||
|
||||
def get_array_of_long_long(offset, length, signed=true)
|
||||
(self + offset).read_array_of_long_long(length, signed)
|
||||
end
|
||||
|
||||
def put_array_of_long_long(offset, ary)
|
||||
(self + offset).write_array_of_long_long(ary)
|
||||
end
|
||||
|
||||
def read_ulong_long
|
||||
self.read_long_long(false)
|
||||
end
|
||||
|
||||
def write_ulong_long(obj)
|
||||
self.write_long_long(obj)
|
||||
end
|
||||
|
||||
def get_ulong_long(offset)
|
||||
(self + offset).read_ulong_long
|
||||
end
|
||||
|
||||
def put_ulong_long(offset, obj)
|
||||
(self + offset).write_ulong_long(obj)
|
||||
end
|
||||
|
||||
def read_array_of_ulong_long(length)
|
||||
self.read_array_of_long_long(length, false)
|
||||
end
|
||||
|
||||
def write_array_of_ulong_long(ary)
|
||||
self.write_array_of_long_long(ary)
|
||||
end
|
||||
|
||||
def get_array_of_ulong_long(offset, length)
|
||||
(self + offset).read_array_of_ulong_long(length)
|
||||
end
|
||||
|
||||
def put_array_of_ulong_long(offset, ary)
|
||||
(self + offset).write_array_of_ulong_long(ary)
|
||||
end
|
||||
|
||||
#
|
||||
# Name: pointer
|
||||
# Type: pointer
|
||||
# Signed: false
|
||||
#
|
||||
|
||||
def read_pointer
|
||||
primitive_read_pointer
|
||||
end
|
||||
|
||||
def write_pointer(obj)
|
||||
primitive_write_pointer(obj)
|
||||
end
|
||||
|
||||
def get_pointer(offset)
|
||||
(self + offset).read_pointer
|
||||
end
|
||||
|
||||
def put_pointer(offset, obj)
|
||||
(self + offset).write_pointer(obj)
|
||||
end
|
||||
|
||||
def read_array_of_pointer(length)
|
||||
read_array_of_type(:pointer, :read_pointer, length)
|
||||
end
|
||||
|
||||
def write_array_of_pointer(ary)
|
||||
write_array_of_type(:pointer, :write_pointer, ary)
|
||||
end
|
||||
|
||||
def get_array_of_pointer(offset, length)
|
||||
(self + offset).read_array_of_pointer(length)
|
||||
end
|
||||
|
||||
def put_array_of_pointer(offset, ary)
|
||||
(self + offset).write_array_of_pointer(ary)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
14
gxg/core/prediction.rb
Normal file
14
gxg/core/prediction.rb
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
module Rubinius
|
||||
class Prediction
|
||||
def inspect
|
||||
"#<#{self.class.name}:0x#{object_id.to_s(16)} valid=#{valid?}>"
|
||||
end
|
||||
|
||||
alias_method :to_s, :inspect
|
||||
|
||||
def valid?
|
||||
Rubinius.primitive :prediction_valid_p
|
||||
raise PrimitiveFailure, "Prediction#valid? primitive failed"
|
||||
end
|
||||
end
|
||||
end
|
||||
276
gxg/core/proc.rb
Normal file
276
gxg/core/proc.rb
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
class Proc
|
||||
def self.allocate
|
||||
raise TypeError, "allocator undefined for Proc"
|
||||
end
|
||||
|
||||
def self.__allocate__
|
||||
Rubinius.primitive :proc_allocate
|
||||
raise PrimitiveFailure, "Proc#allocate primitive failed"
|
||||
end
|
||||
|
||||
def call_prim(*args)
|
||||
Rubinius.primitive :proc_call
|
||||
raise PrimitiveFailure, "Proc#call primitive failed"
|
||||
end
|
||||
|
||||
def call(*args, &block)
|
||||
if @ruby_method
|
||||
@ruby_method.call(*args, &block)
|
||||
else
|
||||
call_prim(*args, &block)
|
||||
end
|
||||
end
|
||||
|
||||
def call_on_object(*args)
|
||||
Rubinius.primitive :proc_call_on_object
|
||||
raise PrimitiveFailure, "Proc#call_on_object primitive failed"
|
||||
end
|
||||
|
||||
def lambda_style!
|
||||
@lambda = true
|
||||
end
|
||||
|
||||
def lambda?
|
||||
!!@lambda
|
||||
end
|
||||
end
|
||||
class Proc
|
||||
def self.__from_block__(env)
|
||||
# The compiler must be fixed before this method can be removed.
|
||||
Rubinius::Mirror::Proc.from_block self, env
|
||||
end
|
||||
|
||||
def self.new(*args)
|
||||
env = nil
|
||||
|
||||
Rubinius.asm do
|
||||
push_block
|
||||
# assign a pushed block to the above local variable "env"
|
||||
# Note that "env" is indexed at 1, not 0. "args" is indexed at 0.
|
||||
set_local 1
|
||||
end
|
||||
|
||||
unless env
|
||||
# Support for ancient pre-block-pass style:
|
||||
# def something; Proc.new; end
|
||||
# something { a_block } => Proc instance
|
||||
env = Rubinius::BlockEnvironment.of_sender
|
||||
|
||||
unless env
|
||||
raise ArgumentError, "tried to create a Proc object without a block"
|
||||
end
|
||||
end
|
||||
|
||||
block = Rubinius::Mirror::Proc.from_block self, env
|
||||
|
||||
if block.class != self
|
||||
block = block.dup
|
||||
Rubinius::Unsafe.set_class(block, self)
|
||||
end
|
||||
|
||||
Rubinius.asm(block, args) do |b, a|
|
||||
run b
|
||||
run a
|
||||
run b
|
||||
send_with_splat :initialize, 0, true
|
||||
end
|
||||
|
||||
return block
|
||||
end
|
||||
|
||||
attr_accessor :block
|
||||
attr_accessor :bound_method
|
||||
attr_accessor :ruby_method
|
||||
|
||||
def binding
|
||||
bind = @block.to_binding
|
||||
bind.proc_environment = @block
|
||||
bind
|
||||
end
|
||||
|
||||
def arity
|
||||
if @ruby_method
|
||||
return @ruby_method.arity
|
||||
elsif @bound_method
|
||||
arity = @bound_method.arity
|
||||
return arity < 0 ? -1 : arity
|
||||
end
|
||||
|
||||
arity = @block.arity
|
||||
return arity if self.lambda? ||
|
||||
@block.compiled_code.splat ||
|
||||
@block.compiled_code.total_args == arity
|
||||
|
||||
-arity - 1
|
||||
end
|
||||
|
||||
alias_method :===, :call
|
||||
|
||||
def curry(curried_arity = nil)
|
||||
if lambda? && curried_arity
|
||||
if arity >= 0 && curried_arity != arity
|
||||
raise ArgumentError, "Wrong number of arguments (%i for %i)" % [
|
||||
curried_arity,
|
||||
arity
|
||||
]
|
||||
end
|
||||
|
||||
if arity < 0 && curried_arity < (-arity - 1)
|
||||
raise ArgumentError, "Wrong number of arguments (%i for %i)" % [
|
||||
curried_arity,
|
||||
-arity - 1
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
args = []
|
||||
|
||||
m = Rubinius::Mirror.reflect self
|
||||
f = m.curry self, [], arity
|
||||
|
||||
f.singleton_class.send(:define_method, :binding) do
|
||||
raise ArgumentError, "cannot create binding from f proc"
|
||||
end
|
||||
|
||||
f.singleton_class.thunk_method :parameters, [[:rest]]
|
||||
f.singleton_class.thunk_method :source_location, nil
|
||||
|
||||
f
|
||||
end
|
||||
|
||||
def source_location
|
||||
if @ruby_method
|
||||
@ruby_method.source_location
|
||||
elsif @bound_method
|
||||
if @bound_method.respond_to?(:source_location)
|
||||
@bound_method.source_location
|
||||
else
|
||||
nil
|
||||
end
|
||||
else
|
||||
@block.source_location
|
||||
end
|
||||
end
|
||||
|
||||
def to_s
|
||||
file, line = source_location
|
||||
|
||||
l = " (lambda)" if lambda?
|
||||
if file and line
|
||||
"#<#{self.class}:0x#{self.object_id.to_s(16)}@#{file}:#{line}#{l}>"
|
||||
else
|
||||
"#<#{self.class}:0x#{self.object_id.to_s(16)}#{l}>"
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :inspect, :to_s
|
||||
|
||||
def self.__from_method__(meth)
|
||||
obj = __allocate__
|
||||
obj.ruby_method = meth
|
||||
obj.lambda_style!
|
||||
|
||||
return obj
|
||||
end
|
||||
|
||||
def __yield__(*args, &block)
|
||||
@ruby_method.call(*args, &block)
|
||||
end
|
||||
|
||||
def parameters
|
||||
if @ruby_method
|
||||
return @ruby_method.parameters
|
||||
elsif @bound_method
|
||||
return @bound_method.parameters
|
||||
end
|
||||
|
||||
code = @block.compiled_code
|
||||
|
||||
params = []
|
||||
|
||||
return params unless code.respond_to? :local_names
|
||||
|
||||
m = code.required_args - code.post_args
|
||||
o = m + code.total_args - code.required_args
|
||||
p = o + code.post_args
|
||||
p += 1 if code.splat
|
||||
|
||||
required_status = self.lambda? ? :req : :opt
|
||||
|
||||
code.local_names.each_with_index do |name, i|
|
||||
if i < m
|
||||
params << [required_status, name]
|
||||
elsif i < o
|
||||
params << [:opt, name]
|
||||
elsif code.splat == i
|
||||
if name == :*
|
||||
params << [:rest]
|
||||
else
|
||||
params << [:rest, name]
|
||||
end
|
||||
elsif i < p
|
||||
params << [required_status, name]
|
||||
elsif code.block_index == i
|
||||
params << [:block, name]
|
||||
end
|
||||
end
|
||||
|
||||
params
|
||||
end
|
||||
|
||||
|
||||
def to_proc
|
||||
self
|
||||
end
|
||||
|
||||
alias_method :[], :call
|
||||
alias_method :yield, :call
|
||||
|
||||
def clone
|
||||
copy = self.class.__allocate__
|
||||
Rubinius.invoke_primitive :object_copy_object, copy, self
|
||||
Rubinius.invoke_primitive :object_copy_singleton_class, copy, self
|
||||
|
||||
Rubinius.privately do
|
||||
copy.initialize_copy self
|
||||
end
|
||||
|
||||
copy.freeze if frozen?
|
||||
copy
|
||||
end
|
||||
|
||||
def dup
|
||||
copy = self.class.__allocate__
|
||||
Rubinius.invoke_primitive :object_copy_object, copy, self
|
||||
|
||||
Rubinius.privately do
|
||||
copy.initialize_copy self
|
||||
end
|
||||
copy
|
||||
end
|
||||
|
||||
def for_define_method(name, klass)
|
||||
if @ruby_method
|
||||
code, scope = @ruby_method.for_define_method(name, klass, self)
|
||||
else
|
||||
be = @block.dup
|
||||
be.change_name name
|
||||
|
||||
duped_proc = self.dup
|
||||
duped_proc.lambda_style!
|
||||
|
||||
code = Rubinius::BlockEnvironment::AsMethod.new(be)
|
||||
scope = duped_proc.block.scope
|
||||
end
|
||||
|
||||
[code, scope]
|
||||
end
|
||||
|
||||
def self.from_method(meth)
|
||||
if meth.kind_of? Method
|
||||
return __from_method__(meth)
|
||||
else
|
||||
raise ArgumentError, "tried to create a Proc object without a Method"
|
||||
end
|
||||
end
|
||||
end
|
||||
37
gxg/core/proc_mirror.rb
Normal file
37
gxg/core/proc_mirror.rb
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
module Rubinius
|
||||
class Mirror
|
||||
class Proc < Mirror
|
||||
def self.from_block(klass, env)
|
||||
begin
|
||||
return Rubinius.invoke_primitive :proc_from_env, env, klass
|
||||
rescue Rubinius::Internal => exc
|
||||
if Type.object_kind_of? env, BlockEnvironment
|
||||
msg = "unable to create Proc from BlockEnvironment"
|
||||
raise PrimitiveFailure, msg, exc
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
env.to_proc
|
||||
rescue Exception
|
||||
raise ArgumentError, "Unable to convert #{env.inspect} to a Proc"
|
||||
end
|
||||
end
|
||||
|
||||
def curry(executable, args, arity)
|
||||
args.freeze
|
||||
|
||||
name = executable.lambda? ? :lambda : :proc
|
||||
|
||||
Proc.__send__(name) do |*a, &b|
|
||||
all_args = args + a
|
||||
if all_args.size < arity
|
||||
curry executable, all_args, arity
|
||||
else
|
||||
executable[*all_args]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
794
gxg/core/process.rb
Normal file
794
gxg/core/process.rb
Normal file
|
|
@ -0,0 +1,794 @@
|
|||
module Process
|
||||
def self.time
|
||||
Rubinius.primitive :vm_time
|
||||
raise PrimitiveFailure, "Process.time primitive failed"
|
||||
end
|
||||
|
||||
def self.cpu_times
|
||||
Rubinius.primitive :vm_times
|
||||
raise PrimitiveFailure, "Process.cpu_times primitive failed"
|
||||
end
|
||||
|
||||
##
|
||||
# Sets the process title. Calling this method does not affect the value of
|
||||
# `$0` as per MRI behaviour. This method returns the title set.
|
||||
#
|
||||
# @param [String] title
|
||||
# @return [Title]
|
||||
#
|
||||
def self.setproctitle(title)
|
||||
val = Rubinius::Type.coerce_to(title, String, :to_str)
|
||||
|
||||
Rubinius.invoke_primitive(:vm_set_process_title, val)
|
||||
end
|
||||
|
||||
def self.setrlimit(resource, cur_limit, max_limit=undefined)
|
||||
resource = coerce_rlimit_resource(resource)
|
||||
cur_limit = Rubinius::Type.coerce_to cur_limit, Integer, :to_int
|
||||
|
||||
unless undefined.equal? max_limit
|
||||
max_limit = Rubinius::Type.coerce_to max_limit, Integer, :to_int
|
||||
end
|
||||
|
||||
rlimit = Rlimit.new
|
||||
rlimit[:rlim_cur] = cur_limit
|
||||
rlimit[:rlim_max] = undefined.equal?(max_limit) ? cur_limit : max_limit
|
||||
|
||||
ret = FFI::Platform::POSIX.setrlimit(resource, rlimit.pointer)
|
||||
Errno.handle if ret == -1
|
||||
nil
|
||||
end
|
||||
|
||||
def self.getrlimit(resource)
|
||||
resource = coerce_rlimit_resource(resource)
|
||||
|
||||
lim_max = []
|
||||
rlimit = Rlimit.new
|
||||
ret = FFI::Platform::POSIX.getrlimit(resource, rlimit.pointer)
|
||||
Errno.handle if ret == -1
|
||||
|
||||
[rlimit[:rlim_cur], rlimit[:rlim_max]]
|
||||
end
|
||||
|
||||
def self.setsid
|
||||
pgid = FFI::Platform::POSIX.setsid
|
||||
Errno.handle if pgid == -1
|
||||
pgid
|
||||
end
|
||||
|
||||
def self.fork
|
||||
pid = Rubinius::Mirror::Process.fork
|
||||
|
||||
if block_given? and pid.nil?
|
||||
begin
|
||||
yield nil
|
||||
status = 0
|
||||
rescue SystemExit => e
|
||||
status = e.status
|
||||
rescue Exception => e
|
||||
e.render "An exception occurred in a forked block"
|
||||
status = 1
|
||||
end
|
||||
|
||||
until Rubinius::AtExit.empty?
|
||||
begin
|
||||
Rubinius::AtExit.shift.call
|
||||
rescue SystemExit => e
|
||||
status = e.status
|
||||
end
|
||||
end
|
||||
|
||||
# Do not use Kernel.exit. This raises a SystemExit exception, which
|
||||
# will run ensure blocks. This is not what MRI does and causes bugs
|
||||
# in programs. See issue http://github.com/rubinius/rubinius/issues#issue/289 for
|
||||
# an example
|
||||
|
||||
Kernel.exit! status
|
||||
end
|
||||
pid
|
||||
end
|
||||
|
||||
def self.times
|
||||
Struct::Tms.new(*cpu_times)
|
||||
end
|
||||
|
||||
def self.kill(signal, *pids)
|
||||
raise ArgumentError, "PID argument required" if pids.length == 0
|
||||
|
||||
use_process_group = false
|
||||
signal = signal.to_s if signal.kind_of?(Symbol)
|
||||
|
||||
if signal.kind_of?(String)
|
||||
if signal[0] == ?-
|
||||
signal = signal[1..-1]
|
||||
use_process_group = true
|
||||
end
|
||||
|
||||
if signal[0..2] == "SIG"
|
||||
signal = signal[3..-1]
|
||||
end
|
||||
|
||||
signal = Signal::Names[signal]
|
||||
end
|
||||
|
||||
raise ArgumentError unless signal.kind_of? Fixnum
|
||||
|
||||
if signal < 0
|
||||
signal = -signal
|
||||
use_process_group = true
|
||||
end
|
||||
|
||||
pids.each do |pid|
|
||||
pid = Rubinius::Type.coerce_to_pid pid
|
||||
|
||||
pid = -pid if use_process_group
|
||||
result = FFI::Platform::POSIX.kill(pid, signal)
|
||||
|
||||
Errno.handle if result == -1
|
||||
end
|
||||
|
||||
return pids.length
|
||||
end
|
||||
|
||||
def self.abort(msg=nil)
|
||||
if msg
|
||||
msg = StringValue(msg)
|
||||
$stderr.puts(msg)
|
||||
end
|
||||
raise SystemExit.new(1, msg)
|
||||
end
|
||||
|
||||
def self.getpgid(pid)
|
||||
pid = Rubinius::Type.coerce_to pid, Integer, :to_int
|
||||
|
||||
ret = FFI::Platform::POSIX.getpgid(pid)
|
||||
Errno.handle if ret == -1
|
||||
ret
|
||||
end
|
||||
|
||||
def self.setpgid(pid, int)
|
||||
pid = Rubinius::Type.coerce_to pid, Integer, :to_int
|
||||
int = Rubinius::Type.coerce_to int, Integer, :to_int
|
||||
|
||||
ret = FFI::Platform::POSIX.setpgid(pid, int)
|
||||
Errno.handle if ret == -1
|
||||
ret
|
||||
end
|
||||
|
||||
@maxgroups = 32
|
||||
class << self
|
||||
attr_reader :maxgroups
|
||||
def maxgroups=(m)
|
||||
@maxgroups = m
|
||||
end
|
||||
end
|
||||
|
||||
def self.setpgrp
|
||||
setpgid(0, 0)
|
||||
end
|
||||
def self.getpgrp
|
||||
ret = FFI::Platform::POSIX.getpgrp
|
||||
Errno.handle if ret == -1
|
||||
ret
|
||||
end
|
||||
|
||||
def self.pid
|
||||
ret = FFI::Platform::POSIX.getpid
|
||||
Errno.handle if ret == -1
|
||||
ret
|
||||
end
|
||||
|
||||
def self.ppid
|
||||
ret = FFI::Platform::POSIX.getppid
|
||||
Errno.handle if ret == -1
|
||||
ret
|
||||
end
|
||||
|
||||
def self.uid=(uid)
|
||||
# the 4 rescue clauses below are needed
|
||||
# until respond_to? can be used to query the implementation of methods attached via FFI
|
||||
# atm respond_to returns true if a method is attached but not implemented on the platform
|
||||
uid = Rubinius::Type.coerce_to uid, Integer, :to_int
|
||||
begin
|
||||
ret = FFI::Platform::POSIX.setresuid(uid, -1, -1)
|
||||
rescue NotImplementedError
|
||||
begin
|
||||
ret = FFI::Platform::POSIX.setreuid(uid, -1)
|
||||
rescue NotImplementedError
|
||||
begin
|
||||
ret = FFI::Platform::POSIX.setruid(uid)
|
||||
rescue NotImplementedError
|
||||
if Process.euid == uid
|
||||
ret = FFI::Platform::POSIX.setuid(uid)
|
||||
else
|
||||
raise NotImplementedError
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Errno.handle if ret == -1
|
||||
|
||||
uid
|
||||
end
|
||||
|
||||
def self.gid=(gid)
|
||||
gid = Rubinius::Type.coerce_to gid, Integer, :to_int
|
||||
Process::Sys.setgid gid
|
||||
end
|
||||
|
||||
def self.euid=(uid)
|
||||
# the 4 rescue clauses below are needed
|
||||
# until respond_to? can be used to query the implementation of methods attached via FFI
|
||||
# atm respond_to returns true if a method is attached but not implemented on the platform
|
||||
uid = Rubinius::Type.coerce_to uid, Integer, :to_int
|
||||
begin
|
||||
ret = FFI::Platform::POSIX.setresuid(-1, uid, -1)
|
||||
rescue NotImplementedError
|
||||
begin
|
||||
ret = FFI::Platform::POSIX.setreuid(-1, uid)
|
||||
rescue NotImplementedError
|
||||
begin
|
||||
ret = FFI::Platform::POSIX.seteuid(uid)
|
||||
rescue NotImplementedError
|
||||
if Process.uid == uid
|
||||
ret = FFI::Platform::POSIX.setuid(uid)
|
||||
else
|
||||
raise NotImplementedError
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Errno.handle if ret == -1
|
||||
|
||||
uid
|
||||
end
|
||||
|
||||
def self.egid=(gid)
|
||||
gid = Rubinius::Type.coerce_to gid, Integer, :to_int
|
||||
Process::Sys.setegid gid
|
||||
end
|
||||
|
||||
def self.uid
|
||||
ret = FFI::Platform::POSIX.getuid
|
||||
Errno.handle if ret == -1
|
||||
ret
|
||||
end
|
||||
|
||||
def self.gid
|
||||
ret = FFI::Platform::POSIX.getgid
|
||||
Errno.handle if ret == -1
|
||||
ret
|
||||
end
|
||||
|
||||
def self.euid
|
||||
ret = FFI::Platform::POSIX.geteuid
|
||||
Errno.handle if ret == -1
|
||||
ret
|
||||
end
|
||||
|
||||
def self.egid
|
||||
ret = FFI::Platform::POSIX.getegid
|
||||
Errno.handle if ret == -1
|
||||
ret
|
||||
end
|
||||
|
||||
def self.getpriority(kind, id)
|
||||
kind = Rubinius::Type.coerce_to kind, Integer, :to_int
|
||||
id = Rubinius::Type.coerce_to id, Integer, :to_int
|
||||
|
||||
FFI::Platform::POSIX.errno = 0
|
||||
ret = FFI::Platform::POSIX.getpriority(kind, id)
|
||||
Errno.handle
|
||||
ret
|
||||
end
|
||||
|
||||
def self.setpriority(kind, id, priority)
|
||||
kind = Rubinius::Type.coerce_to kind, Integer, :to_int
|
||||
id = Rubinius::Type.coerce_to id, Integer, :to_int
|
||||
priority = Rubinius::Type.coerce_to priority, Integer, :to_int
|
||||
|
||||
ret = FFI::Platform::POSIX.setpriority(kind, id, priority)
|
||||
Errno.handle if ret == -1
|
||||
ret
|
||||
end
|
||||
|
||||
def self.groups
|
||||
g = []
|
||||
count = Rubinius::FFI::Platform::POSIX.getgroups(0, nil)
|
||||
FFI::MemoryPointer.new(:int, count) { |p|
|
||||
num_groups = FFI::Platform::POSIX.getgroups(count, p)
|
||||
Errno.handle if num_groups == -1
|
||||
g = p.read_array_of_int(num_groups)
|
||||
}
|
||||
g
|
||||
end
|
||||
|
||||
def self.groups=(g)
|
||||
@maxgroups = g.length if g.length > @maxgroups
|
||||
FFI::MemoryPointer.new(:int, @maxgroups) { |p|
|
||||
p.write_array_of_int(g)
|
||||
Errno.handle if FFI::Platform::POSIX.setgroups(g.length, p) == -1
|
||||
}
|
||||
g
|
||||
end
|
||||
|
||||
def self.initgroups(username, gid)
|
||||
username = StringValue(username)
|
||||
gid = Rubinius::Type.coerce_to gid, Integer, :to_int
|
||||
|
||||
if FFI::Platform::POSIX.initgroups(username, gid) == -1
|
||||
Errno.handle
|
||||
end
|
||||
|
||||
Process.groups
|
||||
end
|
||||
|
||||
#
|
||||
# Wait for the given process to exit.
|
||||
#
|
||||
# The pid may be the specific pid of some previously forked
|
||||
# process, or -1 to indicate to watch for *any* child process
|
||||
# exiting. Other options, such as process groups, may be available
|
||||
# depending on the system.
|
||||
#
|
||||
# With no arguments the default is to block waiting for any
|
||||
# child processes (pid -1.)
|
||||
#
|
||||
# The flag may be Process::WNOHANG, which indicates that
|
||||
# the child should only be quickly checked. If it has not
|
||||
# exited yet, nil is returned immediately instead.
|
||||
#
|
||||
# The return value is the exited pid or nil if Process::WNOHANG
|
||||
# was used and the child had not yet exited.
|
||||
#
|
||||
# If the pid has exited, the global $? is set to a Process::Status
|
||||
# object representing the exit status (and possibly other info) of
|
||||
# the child.
|
||||
#
|
||||
# If there exists no such pid (e.g. never forked or already
|
||||
# waited for), or no children at all, Errno::ECHILD is raised.
|
||||
#
|
||||
# TODO: Support other options such as WUNTRACED? --rue
|
||||
#
|
||||
def self.wait2(input_pid=-1, flags=nil)
|
||||
input_pid = Rubinius::Type.coerce_to input_pid, Integer, :to_int
|
||||
|
||||
if flags and (flags & WNOHANG) == WNOHANG
|
||||
value = Rubinius.invoke_primitive :vm_waitpid, input_pid, true
|
||||
return if value.nil?
|
||||
else
|
||||
value = Rubinius.invoke_primitive :vm_waitpid, input_pid, false
|
||||
end
|
||||
|
||||
if value == false
|
||||
raise Errno::ECHILD, "No child process: #{input_pid}"
|
||||
end
|
||||
|
||||
# vm_waitpid returns a tuple when wait needs to communicate the pid that
|
||||
# was actually detected as stopped (since wait can wait for all child
|
||||
# pids, groups, etc)
|
||||
status, termsig, stopsig, pid = value
|
||||
|
||||
status = Process::Status.new(pid, status, termsig, stopsig)
|
||||
Rubinius::Mirror::Process.set_status_global status
|
||||
|
||||
[pid, status]
|
||||
end
|
||||
|
||||
#
|
||||
# Wait for all child processes.
|
||||
#
|
||||
# Blocks until all child processes have exited, and returns
|
||||
# an Array of [pid, Process::Status] results, one for each
|
||||
# child.
|
||||
#
|
||||
# Be mindful of the effects of creating new processes while
|
||||
# .waitall has been called (usually in a different thread.)
|
||||
# The .waitall call does not in any way check that it is only
|
||||
# waiting for children that existed at the time it was called.
|
||||
#
|
||||
def self.waitall
|
||||
statuses = []
|
||||
|
||||
begin
|
||||
while true
|
||||
statuses << Process.wait2
|
||||
end
|
||||
rescue Errno::ECHILD
|
||||
end
|
||||
|
||||
statuses
|
||||
end
|
||||
|
||||
def self.wait(pid=-1, flags=nil)
|
||||
pid, status = Process.wait2(pid, flags)
|
||||
return pid
|
||||
end
|
||||
|
||||
class << self
|
||||
alias_method :waitpid, :wait
|
||||
alias_method :waitpid2, :wait2
|
||||
end
|
||||
|
||||
def self.daemon(stay_in_dir=false, keep_stdio_open=false)
|
||||
# Do not run at_exit handlers in the parent
|
||||
exit!(0) if fork
|
||||
|
||||
Process.setsid
|
||||
|
||||
exit!(0) if fork
|
||||
|
||||
Dir.chdir("/") unless stay_in_dir
|
||||
|
||||
unless keep_stdio_open
|
||||
io = File.open "/dev/null", File::RDWR, 0
|
||||
$stdin.reopen io
|
||||
$stdout.reopen io
|
||||
$stderr.reopen io
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
def self.exec(*args)
|
||||
Rubinius::Mirror::Process.exec(*args)
|
||||
end
|
||||
|
||||
def self.spawn(*args)
|
||||
Rubinius::Mirror::Process.spawn(*args)
|
||||
end
|
||||
|
||||
# TODO: Should an error be raised on ECHILD? --rue
|
||||
#
|
||||
# TODO: This operates on the assumption that waiting on
|
||||
# the event consumes very little resources. If this
|
||||
# is not the case, the check should be made WNOHANG
|
||||
# and called periodically.
|
||||
#
|
||||
def self.detach(pid)
|
||||
raise ArgumentError, "Only positive pids may be detached" unless pid > 0
|
||||
|
||||
thread = Thread.new { Process.wait pid; $? }
|
||||
thread[:pid] = pid
|
||||
def thread.pid; self[:pid] end
|
||||
|
||||
thread
|
||||
end
|
||||
|
||||
def self.coerce_rlimit_resource(resource)
|
||||
case resource
|
||||
when Integer
|
||||
return resource
|
||||
when Symbol, String
|
||||
# do nothing
|
||||
else
|
||||
unless r = Rubinius::Type.check_convert_type(resource, String, :to_str)
|
||||
return Rubinius::Type.coerce_to resource, Integer, :to_int
|
||||
end
|
||||
|
||||
resource = r
|
||||
end
|
||||
|
||||
constant = "RLIMIT_#{resource}"
|
||||
unless const_defined? constant
|
||||
raise ArgumentError, "invalid resource name: #{constant}"
|
||||
end
|
||||
const_get constant
|
||||
end
|
||||
|
||||
class << self
|
||||
private :coerce_rlimit_resource
|
||||
end
|
||||
|
||||
#--
|
||||
# TODO: Most of the fields aren't implemented yet.
|
||||
# TODO: Also, these objects should only need to be constructed by
|
||||
# Process.wait and family.
|
||||
#++
|
||||
|
||||
class Status
|
||||
|
||||
attr_reader :termsig
|
||||
attr_reader :stopsig
|
||||
|
||||
def initialize(pid=nil, status=nil, termsig=nil, stopsig=nil)
|
||||
@pid = pid
|
||||
@status = status
|
||||
@termsig = termsig
|
||||
@stopsig = stopsig
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def exitstatus
|
||||
@status
|
||||
end
|
||||
|
||||
def to_i
|
||||
@status
|
||||
end
|
||||
|
||||
def to_s
|
||||
@status.to_s
|
||||
end
|
||||
|
||||
def &(num)
|
||||
@status & num
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
other = other.to_i if other.kind_of? Process::Status
|
||||
@status == other
|
||||
end
|
||||
|
||||
def >>(num)
|
||||
@status >> num
|
||||
end
|
||||
|
||||
def coredump?
|
||||
false
|
||||
end
|
||||
|
||||
def exited?
|
||||
@status != nil
|
||||
end
|
||||
|
||||
def pid
|
||||
@pid
|
||||
end
|
||||
|
||||
def signaled?
|
||||
@termsig != nil
|
||||
end
|
||||
|
||||
def stopped?
|
||||
@stopsig != nil
|
||||
end
|
||||
|
||||
def success?
|
||||
if exited?
|
||||
@status == 0
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Sys
|
||||
class << self
|
||||
def getegid
|
||||
ret = FFI::Platform::POSIX.getegid
|
||||
Errno.handle if ret == -1
|
||||
ret
|
||||
end
|
||||
|
||||
def geteuid
|
||||
ret = FFI::Platform::POSIX.geteuid
|
||||
Errno.handle if ret == -1
|
||||
ret
|
||||
end
|
||||
|
||||
def getgid
|
||||
ret = FFI::Platform::POSIX.getgid
|
||||
Errno.handle if ret == -1
|
||||
ret
|
||||
end
|
||||
|
||||
def getuid
|
||||
ret = FFI::Platform::POSIX.getuid
|
||||
Errno.handle if ret == -1
|
||||
ret
|
||||
end
|
||||
|
||||
def issetugid
|
||||
raise "not implemented"
|
||||
end
|
||||
|
||||
def setgid(gid)
|
||||
gid = Rubinius::Type.coerce_to gid, Integer, :to_int
|
||||
|
||||
ret = FFI::Platform::POSIX.setgid gid
|
||||
Errno.handle if ret == -1
|
||||
nil
|
||||
end
|
||||
|
||||
def setuid(uid)
|
||||
uid = Rubinius::Type.coerce_to uid, Integer, :to_int
|
||||
|
||||
ret = FFI::Platform::POSIX.setuid uid
|
||||
Errno.handle if ret == -1
|
||||
nil
|
||||
end
|
||||
|
||||
def setegid(egid)
|
||||
egid = Rubinius::Type.coerce_to egid, Integer, :to_int
|
||||
|
||||
ret = FFI::Platform::POSIX.setegid egid
|
||||
Errno.handle if ret == -1
|
||||
nil
|
||||
end
|
||||
|
||||
def seteuid(euid)
|
||||
euid = Rubinius::Type.coerce_to euid, Integer, :to_int
|
||||
|
||||
ret = FFI::Platform::POSIX.seteuid euid
|
||||
Errno.handle if ret == -1
|
||||
nil
|
||||
end
|
||||
|
||||
def setrgid(rgid)
|
||||
setregid(rgid, -1)
|
||||
end
|
||||
|
||||
def setruid(ruid)
|
||||
setreuid(ruid, -1)
|
||||
end
|
||||
|
||||
def setregid(rid, eid)
|
||||
rid = Rubinius::Type.coerce_to rid, Integer, :to_int
|
||||
eid = Rubinius::Type.coerce_to eid, Integer, :to_int
|
||||
|
||||
ret = FFI::Platform::POSIX.setregid rid, eid
|
||||
Errno.handle if ret == -1
|
||||
nil
|
||||
end
|
||||
|
||||
def setreuid(rid, eid)
|
||||
rid = Rubinius::Type.coerce_to rid, Integer, :to_int
|
||||
eid = Rubinius::Type.coerce_to eid, Integer, :to_int
|
||||
|
||||
ret = FFI::Platform::POSIX.setreuid rid, eid
|
||||
Errno.handle if ret == -1
|
||||
nil
|
||||
end
|
||||
|
||||
def setresgid(rid, eid, sid)
|
||||
rid = Rubinius::Type.coerce_to rid, Integer, :to_int
|
||||
eid = Rubinius::Type.coerce_to eid, Integer, :to_int
|
||||
sid = Rubinius::Type.coerce_to sid, Integer, :to_int
|
||||
|
||||
ret = FFI::Platform::POSIX.setresgid rid, eid, sid
|
||||
Errno.handle if ret == -1
|
||||
nil
|
||||
end
|
||||
|
||||
def setresuid(rid, eid, sid)
|
||||
rid = Rubinius::Type.coerce_to rid, Integer, :to_int
|
||||
eid = Rubinius::Type.coerce_to eid, Integer, :to_int
|
||||
sid = Rubinius::Type.coerce_to sid, Integer, :to_int
|
||||
|
||||
ret = FFI::Platform::POSIX.setresuid rid, eid, sid
|
||||
Errno.handle if ret == -1
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module UID
|
||||
class << self
|
||||
def change_privilege(uid)
|
||||
uid = Rubinius::Type.coerce_to uid, Integer, :to_int
|
||||
|
||||
ret = FFI::Platform::POSIX.setreuid(uid, uid)
|
||||
Errno.handle if ret == -1
|
||||
uid
|
||||
end
|
||||
|
||||
def eid
|
||||
ret = FFI::Platform::POSIX.geteuid
|
||||
Errno.handle if ret == -1
|
||||
ret
|
||||
end
|
||||
|
||||
def eid=(uid)
|
||||
uid = Rubinius::Type.coerce_to uid, Integer, :to_int
|
||||
|
||||
ret = FFI::Platform::POSIX.seteuid(uid)
|
||||
Errno.handle if ret == -1
|
||||
uid
|
||||
end
|
||||
alias_method :grant_privilege, :eid=
|
||||
|
||||
def re_exchange
|
||||
real = FFI::Platform::POSIX.getuid
|
||||
Errno.handle if real == -1
|
||||
eff = FFI::Platform::POSIX.geteuid
|
||||
Errno.handle if eff == -1
|
||||
ret = FFI::Platform::POSIX.setreuid(eff, real)
|
||||
Errno.handle if ret == -1
|
||||
eff
|
||||
end
|
||||
|
||||
def re_exchangeable?
|
||||
true
|
||||
end
|
||||
|
||||
def rid
|
||||
ret = FFI::Platform::POSIX.getuid
|
||||
Errno.handle if ret == -1
|
||||
ret
|
||||
end
|
||||
|
||||
def sid_available?
|
||||
true
|
||||
end
|
||||
|
||||
def switch
|
||||
eff = re_exchange
|
||||
if block_given?
|
||||
ret = yield
|
||||
re_exchange
|
||||
return ret
|
||||
else
|
||||
return eff
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
module GID
|
||||
class << self
|
||||
def change_privilege(gid)
|
||||
gid = Rubinius::Type.coerce_to gid, Integer, :to_int
|
||||
|
||||
ret = FFI::Platform::POSIX.setregid(gid, gid)
|
||||
Errno.handle if ret == -1
|
||||
gid
|
||||
end
|
||||
|
||||
def eid
|
||||
ret = FFI::Platform::POSIX.getegid
|
||||
Errno.handle if ret == -1
|
||||
ret
|
||||
end
|
||||
|
||||
def eid=(gid)
|
||||
gid = Rubinius::Type.coerce_to gid, Integer, :to_int
|
||||
|
||||
ret = FFI::Platform::POSIX.setegid(gid)
|
||||
Errno.handle if ret == -1
|
||||
gid
|
||||
end
|
||||
alias_method :grant_privilege, :eid=
|
||||
|
||||
def re_exchange
|
||||
real = FFI::Platform::POSIX.getgid
|
||||
Errno.handle if real == -1
|
||||
eff = FFI::Platform::POSIX.getegid
|
||||
Errno.handle if eff == -1
|
||||
ret = FFI::Platform::POSIX.setregid(eff, real)
|
||||
Errno.handle if ret == -1
|
||||
eff
|
||||
end
|
||||
|
||||
def re_exchangeable?
|
||||
true
|
||||
end
|
||||
|
||||
def rid
|
||||
ret = FFI::Platform::POSIX.getgid
|
||||
Errno.handle if ret == -1
|
||||
ret
|
||||
end
|
||||
|
||||
def sid_available?
|
||||
true
|
||||
end
|
||||
|
||||
def switch
|
||||
eff = re_exchange
|
||||
if block_given?
|
||||
ret = yield
|
||||
re_exchange
|
||||
return ret
|
||||
else
|
||||
return eff
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
256
gxg/core/process_mirror.rb
Normal file
256
gxg/core/process_mirror.rb
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
module Rubinius
|
||||
class Mirror
|
||||
module Process
|
||||
def self.set_status_global(status)
|
||||
::Thread.current[:$?] = status
|
||||
end
|
||||
|
||||
def self.fork
|
||||
Rubinius.primitive :vm_fork
|
||||
raise PrimitiveFailure, "Rubinius::Mirror::Process.fork primitive failed"
|
||||
end
|
||||
|
||||
def self.exec(*args)
|
||||
exe = Execute.new(*args)
|
||||
exe.spawn_setup
|
||||
exe.exec exe.command, exe.argv
|
||||
end
|
||||
|
||||
def self.spawn(*args)
|
||||
exe = Execute.new(*args)
|
||||
|
||||
begin
|
||||
pid = exe.spawn exe.options, exe.command, exe.argv
|
||||
rescue SystemCallError => error
|
||||
set_status_global ::Process::Status.new(pid, 127)
|
||||
raise error
|
||||
end
|
||||
|
||||
pid
|
||||
end
|
||||
|
||||
def self.backtick(str)
|
||||
Rubinius.primitive :vm_backtick
|
||||
raise PrimitiveFailure, "Rubinius::Mirror::Process.backtick primitive failed"
|
||||
end
|
||||
|
||||
class Execute
|
||||
attr_reader :command
|
||||
attr_reader :argv
|
||||
attr_reader :options
|
||||
|
||||
# Turns the various varargs incantations supported by Process.spawn into a
|
||||
# [env, prog, argv, redirects, options] tuple.
|
||||
#
|
||||
# The following method signature is supported:
|
||||
# Process.spawn([env], command, ..., [options])
|
||||
#
|
||||
# The env and options hashes are optional. The command may be a variable
|
||||
# number of strings or an Array full of strings that make up the new process's
|
||||
# argv.
|
||||
#
|
||||
# Assigns @environment, @command, @argv, @redirects, @options. All
|
||||
# elements are guaranteed to be non-nil. When no env or options are
|
||||
# given, empty hashes are returned.
|
||||
def initialize(env_or_cmd, *args)
|
||||
if options = Rubinius::Type.try_convert(args.last, ::Hash, :to_hash)
|
||||
args.pop
|
||||
end
|
||||
|
||||
if env = Rubinius::Type.try_convert(env_or_cmd, ::Hash, :to_hash)
|
||||
unless command = args.shift
|
||||
raise ArgumentError, "command argument expected"
|
||||
end
|
||||
else
|
||||
command = env_or_cmd
|
||||
end
|
||||
|
||||
if args.empty? and
|
||||
cmd = Rubinius::Type.try_convert(command, ::String, :to_str)
|
||||
raise Errno::ENOENT if cmd.empty?
|
||||
|
||||
@command = cmd
|
||||
@argv = []
|
||||
else
|
||||
if cmd = Rubinius::Type.try_convert(command, ::Array, :to_ary)
|
||||
raise ArgumentError, "wrong first argument" unless cmd.size == 2
|
||||
command = StringValue(cmd[0])
|
||||
name = StringValue(cmd[1])
|
||||
else
|
||||
name = command = StringValue(command)
|
||||
end
|
||||
|
||||
argv = [name]
|
||||
args.each { |arg| argv << StringValue(arg) }
|
||||
|
||||
@command = command
|
||||
@argv = argv
|
||||
end
|
||||
|
||||
@options = Rubinius::LookupTable.new if options or env
|
||||
|
||||
if options
|
||||
options.each do |key, value|
|
||||
case key
|
||||
when ::IO, ::Fixnum, :in, :out, :err
|
||||
from = convert_io_fd key
|
||||
to = convert_to_fd value, from
|
||||
redirect @options, from, to
|
||||
when ::Array
|
||||
from = convert_io_fd key.first
|
||||
to = convert_to_fd value, from
|
||||
key.each { |k| redirect @options, convert_io_fd(k), to }
|
||||
when :unsetenv_others
|
||||
if value
|
||||
array = @options[:env] = []
|
||||
ENV.each_key { |k| array << convert_env_key(k) << nil }
|
||||
end
|
||||
when :pgroup
|
||||
if value == true
|
||||
value = 0
|
||||
elsif value
|
||||
value = Rubinius::Type.coerce_to value, ::Integer, :to_int
|
||||
raise ArgumentError, "negative process group ID : #{value}" if value < 0
|
||||
end
|
||||
@options[key] = value
|
||||
when :chdir
|
||||
@options[key] = Rubinius::Type.coerce_to_path(value)
|
||||
when :umask
|
||||
@options[key] = value
|
||||
when :close_others
|
||||
@options[key] = value if value
|
||||
else
|
||||
raise ArgumentError, "unknown exec option: #{key.inspect}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if env
|
||||
array = (@options[:env] ||= [])
|
||||
|
||||
env.each do |key, value|
|
||||
array << convert_env_key(key) << convert_env_value(value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def redirect(options, from, to)
|
||||
case to
|
||||
when ::Fixnum
|
||||
map = (options[:redirect_fd] ||= [])
|
||||
map << from << to
|
||||
when ::Array
|
||||
map = (options[:assign_fd] ||= [])
|
||||
map << from
|
||||
map.concat to
|
||||
end
|
||||
end
|
||||
|
||||
def convert_io_fd(obj)
|
||||
case obj
|
||||
when ::Fixnum
|
||||
obj
|
||||
when :in
|
||||
0
|
||||
when :out
|
||||
1
|
||||
when :err
|
||||
2
|
||||
when ::IO
|
||||
obj.fileno
|
||||
else
|
||||
raise ArgementError, "wrong exec option: #{obj.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
def convert_to_fd(obj, target)
|
||||
case obj
|
||||
when ::Fixnum
|
||||
obj
|
||||
when :in
|
||||
0
|
||||
when :out
|
||||
1
|
||||
when :err
|
||||
2
|
||||
when :close
|
||||
nil
|
||||
when ::IO
|
||||
obj.fileno
|
||||
when ::String
|
||||
[obj, default_mode(target), 0644]
|
||||
when ::Array
|
||||
case obj.size
|
||||
when 1
|
||||
[obj[0], File::RDONLY, 0644]
|
||||
when 2
|
||||
if obj[0] == :child
|
||||
convert_to_fd obj[1], target
|
||||
else
|
||||
[obj[0], convert_file_mode(obj[1]), 0644]
|
||||
end
|
||||
when 3
|
||||
[obj[0], convert_file_mode(obj[1]), obj[2]]
|
||||
end
|
||||
else
|
||||
raise ArgumentError, "wrong exec redirect: #{obj.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
def default_mode(target)
|
||||
if target == 1 or target == 2
|
||||
OFLAGS["w"]
|
||||
else
|
||||
OFLAGS["r"]
|
||||
end
|
||||
end
|
||||
|
||||
def convert_file_mode(obj)
|
||||
case obj
|
||||
when ::Fixnum
|
||||
obj
|
||||
when ::String
|
||||
OFLAGS[obj]
|
||||
when nil
|
||||
OFLAG["r"]
|
||||
else
|
||||
Rubinius::Type.coerce_to obj, Integer, :to_int
|
||||
end
|
||||
end
|
||||
|
||||
def convert_env_key(key)
|
||||
key = Rubinius::Type.check_null_safe(StringValue(key))
|
||||
|
||||
if key.include?("=")
|
||||
raise ArgumentError, "environment name contains a equal : #{key}"
|
||||
end
|
||||
|
||||
key
|
||||
end
|
||||
|
||||
def convert_env_value(value)
|
||||
return if value.nil?
|
||||
Rubinius::Type.check_null_safe(StringValue(value))
|
||||
end
|
||||
|
||||
def spawn_setup
|
||||
Rubinius.invoke_primitive :vm_spawn_setup, @options
|
||||
end
|
||||
|
||||
def spawn(exe, command, args)
|
||||
Rubinius.primitive :vm_spawn
|
||||
raise PrimitiveFailure,
|
||||
"Rubinius::Mirror::Process::Execute#spawn primitive failed"
|
||||
end
|
||||
|
||||
def exec(command, args)
|
||||
Rubinius.primitive :vm_exec
|
||||
raise PrimitiveFailure,
|
||||
"Rubinius::Mirror::Process::Execute#exec primitive failed"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
188
gxg/core/random.rb
Normal file
188
gxg/core/random.rb
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
class Rubinius::Randomizer
|
||||
def self.allocate
|
||||
Rubinius.primitive :randomizer_allocate
|
||||
raise PrimitiveFailure, "Randomizer.allocate primitive failed"
|
||||
end
|
||||
|
||||
def initialize
|
||||
self.seed = generate_seed
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
attr_reader :seed
|
||||
def seed=(new_seed)
|
||||
set_seed new_seed
|
||||
@seed = new_seed
|
||||
end
|
||||
|
||||
def set_seed(new_seed)
|
||||
Rubinius.primitive :randomizer_seed
|
||||
raise PrimitiveFailure, "Randomizer#seed primitive failed"
|
||||
end
|
||||
|
||||
def swap_seed(new_seed)
|
||||
old_seed = self.seed
|
||||
self.seed = new_seed
|
||||
old_seed
|
||||
end
|
||||
|
||||
def random(limit)
|
||||
if undefined.equal?(limit)
|
||||
random_float
|
||||
else
|
||||
if limit.kind_of?(Range)
|
||||
if time_value?(limit.min)
|
||||
random_time_range(limit)
|
||||
else
|
||||
random_range(limit)
|
||||
end
|
||||
elsif limit.kind_of?(Float)
|
||||
raise ArgumentError, "invalid argument - #{limit}" if limit <= 0
|
||||
random_float * limit
|
||||
elsif limit.is_a?(Integer)
|
||||
raise ArgumentError, "invalid argument - #{limit}" if limit <= 0
|
||||
random_integer(limit - 1)
|
||||
elsif limit.respond_to?(:to_f)
|
||||
raise ArgumentError, "invalid argument - #{limit}" if limit <= 0
|
||||
random_float * limit
|
||||
else
|
||||
limit_int = Rubinius::Type.coerce_to limit, Integer, :to_int
|
||||
raise ArgumentError, "invalid argument - #{limit}" if limit_int <= 0
|
||||
|
||||
random_integer(limit_int - 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Generate a random Float, in the range 0...1.0
|
||||
def random_float
|
||||
Rubinius.primitive :randomizer_rand_float
|
||||
raise PrimitiveFailure, "Randomizer#rand_float primitive failed"
|
||||
end
|
||||
|
||||
# Generate a random Integer, in the range 0...limit
|
||||
def random_integer(limit)
|
||||
Rubinius.primitive :randomizer_rand_int
|
||||
raise PrimitiveFailure, "Randomizer#rand_int primitive failed"
|
||||
end
|
||||
|
||||
def random_range(limit)
|
||||
return nil if limit.first.nil? || limit.last.nil?
|
||||
|
||||
min, max = limit.last.coerce(limit.first)
|
||||
diff = max - min
|
||||
|
||||
return if diff < 0
|
||||
return min if diff == 0
|
||||
|
||||
diff += 1 if max.kind_of?(Integer) && !limit.exclude_end?
|
||||
random(diff) + min
|
||||
end
|
||||
|
||||
def generate_seed
|
||||
Rubinius.primitive :randomizer_gen_seed
|
||||
raise PrimitiveFailure, "Randomizer#gen_seed primitive failed"
|
||||
end
|
||||
|
||||
##
|
||||
# Returns a random value from a range made out of Time, Date or DateTime
|
||||
# instances.
|
||||
#
|
||||
# @param [Range] range
|
||||
# @return [Time|Date|DateTime]
|
||||
#
|
||||
def random_time_range(range)
|
||||
min = time_to_float(range.min)
|
||||
max = time_to_float(range.max)
|
||||
time = Time.at(random(min..max))
|
||||
|
||||
if Object.const_defined?(:DateTime) && range.min.is_a?(DateTime)
|
||||
time = time.to_datetime
|
||||
elsif Object.const_defined?(:DateTime) && range.min.is_a?(Date)
|
||||
time = time.to_date
|
||||
end
|
||||
|
||||
return time
|
||||
end
|
||||
|
||||
##
|
||||
# Casts a Time/Date/DateTime instance to a Float.
|
||||
#
|
||||
# @param [Time|Date|DateTime] input
|
||||
# @return [Float]
|
||||
#
|
||||
def time_to_float(input)
|
||||
return input.respond_to?(:to_time) ? input.to_time.to_f : input.to_f
|
||||
end
|
||||
|
||||
##
|
||||
# Checks if a given value is a Time, Date or DateTime object.
|
||||
#
|
||||
# @param [Mixed] input
|
||||
# @return [TrueClass|FalseClass]
|
||||
#
|
||||
def time_value?(input)
|
||||
return input.is_a?(Time) || (Object.const_defined?(:Date) && input.is_a?(Date))
|
||||
end
|
||||
end
|
||||
|
||||
class Random
|
||||
def self.new_seed
|
||||
Thread.current.randomizer.generate_seed
|
||||
end
|
||||
|
||||
def self.srand(seed=undefined)
|
||||
if undefined.equal? seed
|
||||
seed = Thread.current.randomizer.generate_seed
|
||||
end
|
||||
|
||||
seed = Rubinius::Type.coerce_to seed, Integer, :to_int
|
||||
Thread.current.randomizer.swap_seed seed
|
||||
end
|
||||
|
||||
def self.rand(limit=undefined)
|
||||
Thread.current.randomizer.random(limit)
|
||||
end
|
||||
|
||||
def initialize(seed=undefined)
|
||||
@randomizer = Rubinius::Randomizer.new
|
||||
if !undefined.equal?(seed)
|
||||
@randomizer.swap_seed seed.to_int
|
||||
end
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def rand(limit=undefined)
|
||||
@randomizer.random(limit)
|
||||
end
|
||||
|
||||
def seed
|
||||
@randomizer.seed
|
||||
end
|
||||
|
||||
def state
|
||||
@randomizer.seed
|
||||
end
|
||||
private :state
|
||||
|
||||
def ==(other)
|
||||
return false unless other.kind_of?(Random)
|
||||
seed == other.seed
|
||||
end
|
||||
|
||||
# Returns a random binary string.
|
||||
# The argument size specified the length of the result string.
|
||||
def bytes(length)
|
||||
length = Rubinius::Type.coerce_to length, Integer, :to_int
|
||||
s = ''
|
||||
i = 0
|
||||
while i < length
|
||||
s << @randomizer.random_integer(255).chr
|
||||
i += 1
|
||||
end
|
||||
|
||||
s
|
||||
end
|
||||
end
|
||||
318
gxg/core/range.rb
Normal file
318
gxg/core/range.rb
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
class Range
|
||||
include Enumerable
|
||||
|
||||
def initialize(first, last, exclude_end = false)
|
||||
raise NameError, "`initialize' called twice" if @begin
|
||||
|
||||
unless first.kind_of?(Fixnum) && last.kind_of?(Fixnum)
|
||||
begin
|
||||
raise ArgumentError, "bad value for range" unless first <=> last
|
||||
rescue
|
||||
raise ArgumentError, "bad value for range"
|
||||
end
|
||||
end
|
||||
|
||||
@begin = first
|
||||
@end = last
|
||||
@excl = exclude_end
|
||||
end
|
||||
private :initialize
|
||||
|
||||
def ==(other)
|
||||
return true if equal? other
|
||||
|
||||
other.kind_of?(Range) and
|
||||
self.first == other.first and
|
||||
self.last == other.last and
|
||||
self.exclude_end? == other.exclude_end?
|
||||
end
|
||||
|
||||
alias_method :eql?, :==
|
||||
|
||||
attr_reader_specific :excl, :exclude_end?
|
||||
|
||||
attr_reader :begin
|
||||
attr_reader :end
|
||||
|
||||
def bsearch
|
||||
return to_enum :bsearch unless block_given?
|
||||
|
||||
unless @begin.kind_of? Numeric and @end.kind_of? Numeric
|
||||
raise TypeError, "bsearch is not available for #{@begin.class}"
|
||||
end
|
||||
|
||||
min = @begin
|
||||
max = @end
|
||||
|
||||
max -= 1 if max.kind_of? Integer and @excl
|
||||
|
||||
start = min = Rubinius::Type.coerce_to min, Integer, :to_int
|
||||
total = max = Rubinius::Type.coerce_to max, Integer, :to_int
|
||||
|
||||
last_true = nil
|
||||
|
||||
seeker = Proc.new do |current|
|
||||
x = yield current
|
||||
|
||||
return current if x == 0
|
||||
|
||||
case x
|
||||
when Numeric
|
||||
if x > 0
|
||||
min = current + 1
|
||||
else
|
||||
max = current
|
||||
end
|
||||
when true
|
||||
last_true = current
|
||||
max = current
|
||||
when false, nil
|
||||
min = current + 1
|
||||
else
|
||||
raise TypeError, "Range#bsearch block must return Numeric or boolean"
|
||||
end
|
||||
end
|
||||
|
||||
while min < max
|
||||
if max < 0 and min < 0
|
||||
mid = min + (max - min) / 2
|
||||
elsif min < -max
|
||||
mid = -((-1 - min - max) / 2 + 1)
|
||||
else
|
||||
mid = (min + max) / 2
|
||||
end
|
||||
|
||||
seeker.call mid
|
||||
end
|
||||
|
||||
if min == max
|
||||
seeker.call min
|
||||
end
|
||||
|
||||
if min < max
|
||||
return @begin if mid == start
|
||||
return @begin.kind_of?(Float) ? mid.to_f : mid
|
||||
end
|
||||
|
||||
if last_true
|
||||
return @begin if last_true == start
|
||||
return @begin.kind_of?(Float) ? last_true.to_f : last_true
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def each
|
||||
return to_enum { size } unless block_given?
|
||||
first, last = @begin, @end
|
||||
|
||||
unless first.respond_to?(:succ) && !first.kind_of?(Time)
|
||||
raise TypeError, "can't iterate from #{first.class}"
|
||||
end
|
||||
|
||||
case first
|
||||
when Fixnum
|
||||
last -= 1 if @excl
|
||||
|
||||
i = first
|
||||
while i <= last
|
||||
yield i
|
||||
i += 1
|
||||
end
|
||||
when String
|
||||
first.upto(last, @excl) do |str|
|
||||
yield str
|
||||
end
|
||||
when Symbol
|
||||
first.to_s.upto(last.to_s, @excl) do |str|
|
||||
yield str.to_sym
|
||||
end
|
||||
else
|
||||
current = first
|
||||
if @excl
|
||||
while (current <=> last) < 0
|
||||
yield current
|
||||
current = current.succ
|
||||
end
|
||||
else
|
||||
while (c = current <=> last) && c <= 0
|
||||
yield current
|
||||
break if c == 0
|
||||
current = current.succ
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
def first(n=undefined)
|
||||
return @begin if undefined.equal? n
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def hash
|
||||
excl = @excl ? 1 : 0
|
||||
hash = excl
|
||||
hash ^= @begin.hash << 1
|
||||
hash ^= @end.hash << 9
|
||||
hash ^= excl << 24;
|
||||
# Are we throwing away too much here for a good hash value distribution?
|
||||
return hash & Fixnum::MAX
|
||||
end
|
||||
|
||||
def include?(value)
|
||||
if @begin.respond_to?(:to_int) ||
|
||||
@end.respond_to?(:to_int) ||
|
||||
@begin.kind_of?(Numeric) ||
|
||||
@end.kind_of?(Numeric)
|
||||
cover? value
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :member?, :include?
|
||||
|
||||
def ===(value)
|
||||
include?(value)
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#{@begin.inspect}#{@excl ? "..." : ".."}#{@end.inspect}"
|
||||
end
|
||||
|
||||
def last(n=undefined)
|
||||
return @end if undefined.equal? n
|
||||
|
||||
to_a.last(n)
|
||||
end
|
||||
|
||||
def max
|
||||
return super if block_given? || (@excl && !@end.kind_of?(Numeric))
|
||||
return nil if @end < @begin || (@excl && @end == @begin)
|
||||
return @end unless @excl
|
||||
|
||||
unless @end.kind_of?(Integer)
|
||||
raise TypeError, "cannot exclude non Integer end value"
|
||||
end
|
||||
|
||||
unless @begin.kind_of?(Integer)
|
||||
raise TypeError, "cannot exclude end value with non Integer begin value"
|
||||
end
|
||||
|
||||
@end - 1
|
||||
end
|
||||
|
||||
def min
|
||||
return super if block_given?
|
||||
return nil if @end < @begin || (@excl && @end == @begin)
|
||||
|
||||
@begin
|
||||
end
|
||||
|
||||
def step(step_size=1) # :yields: object
|
||||
return to_enum(:step, step_size) do
|
||||
m = Rubinius::Mirror::Range.reflect(self)
|
||||
m.step_iterations_size(*m.validate_step_size(@begin, @end, step_size))
|
||||
end unless block_given?
|
||||
|
||||
m = Rubinius::Mirror::Range.reflect(self)
|
||||
values = m.validate_step_size(@begin, @end, step_size)
|
||||
first = values[0]
|
||||
last = values[1]
|
||||
step_size = values[2]
|
||||
|
||||
case first
|
||||
when Float
|
||||
iterations = m.step_float_iterations_size(first, last, step_size)
|
||||
|
||||
i = 0
|
||||
while i < iterations
|
||||
curr = i * step_size + first
|
||||
curr = last if last < curr
|
||||
yield curr
|
||||
i += 1
|
||||
end
|
||||
when Numeric
|
||||
curr = first
|
||||
last -= 1 if @excl
|
||||
|
||||
while curr <= last
|
||||
yield curr
|
||||
curr += step_size
|
||||
end
|
||||
else
|
||||
i = 0
|
||||
each do |curr|
|
||||
yield curr if i % step_size == 0
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
def to_s
|
||||
"#{@begin}#{@excl ? "..." : ".."}#{@end}"
|
||||
end
|
||||
|
||||
def to_a
|
||||
return super unless @begin.kind_of? Fixnum and @end.kind_of? Fixnum
|
||||
|
||||
fin = @end
|
||||
fin += 1 unless @excl
|
||||
|
||||
size = fin - @begin
|
||||
return [] if size <= 0
|
||||
|
||||
ary = Array.new(size)
|
||||
|
||||
i = 0
|
||||
while i < size
|
||||
ary[i] = @begin + i
|
||||
i += 1
|
||||
end
|
||||
|
||||
ary
|
||||
end
|
||||
|
||||
def cover?(value)
|
||||
# MRI uses <=> to compare, so must we.
|
||||
|
||||
beg_compare = (@begin <=> value)
|
||||
return false unless beg_compare
|
||||
|
||||
if Comparable.compare_int(beg_compare) <= 0
|
||||
end_compare = (value <=> @end)
|
||||
|
||||
if @excl
|
||||
return true if Comparable.compare_int(end_compare) < 0
|
||||
else
|
||||
return true if Comparable.compare_int(end_compare) <= 0
|
||||
end
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
def size
|
||||
return nil unless @begin.kind_of?(Numeric)
|
||||
|
||||
delta = @end - @begin
|
||||
return 0 if delta < 0
|
||||
|
||||
if @begin.kind_of?(Float) || @end.kind_of?(Float)
|
||||
return delta if delta == Float::INFINITY
|
||||
|
||||
err = (@begin.abs + @end.abs + delta.abs) * Float::EPSILON
|
||||
err = 0.5 if err > 0.5
|
||||
|
||||
(@excl ? delta - err : delta + err).floor + 1
|
||||
else
|
||||
delta += 1 unless @excl
|
||||
delta
|
||||
end
|
||||
end
|
||||
end
|
||||
61
gxg/core/range_mirror.rb
Normal file
61
gxg/core/range_mirror.rb
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
module Rubinius
|
||||
class Mirror
|
||||
class Range < Mirror
|
||||
self.subject = ::Range
|
||||
|
||||
def excl
|
||||
Rubinius.invoke_primitive :object_get_ivar, @object, :@excl
|
||||
end
|
||||
|
||||
def step_float_iterations_size(first, last, step_size)
|
||||
err = (first.abs + last.abs + (last - first).abs) / step_size.abs * Float::EPSILON
|
||||
err = 0.5 if err > 0.5
|
||||
|
||||
if excl
|
||||
iterations = ((last - first) / step_size - err).floor
|
||||
iterations += 1 if iterations * step_size + first < last
|
||||
else
|
||||
iterations = ((last - first) / step_size + err).floor + 1
|
||||
end
|
||||
|
||||
iterations
|
||||
end
|
||||
|
||||
def step_iterations_size(first, last, step_size)
|
||||
case first
|
||||
when Float
|
||||
step_float_iterations_size(first, last, step_size)
|
||||
else
|
||||
@object.size.nil? ? nil : (@object.size.fdiv(step_size)).ceil
|
||||
end
|
||||
end
|
||||
|
||||
def validate_step_size(first, last, step_size)
|
||||
if step_size.kind_of? Float or first.kind_of? Float or last.kind_of? Float
|
||||
# if any are floats they all must be
|
||||
begin
|
||||
step_size = Float(from = step_size)
|
||||
first = Float(from = first)
|
||||
last = Float(from = last)
|
||||
rescue ArgumentError
|
||||
raise TypeError, "no implicit conversion to float from #{from.class}"
|
||||
end
|
||||
else
|
||||
step_size = Integer(from = step_size)
|
||||
|
||||
unless step_size.kind_of? Integer
|
||||
raise TypeError, "can't convert #{from.class} to Integer (#{from.class}#to_int gives #{step_size.class})"
|
||||
end
|
||||
end
|
||||
|
||||
if step_size <= 0
|
||||
raise ArgumentError, "step can't be negative" if step_size < 0
|
||||
raise ArgumentError, "step can't be 0"
|
||||
end
|
||||
|
||||
return first, last, step_size
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
383
gxg/core/rational.rb
Normal file
383
gxg/core/rational.rb
Normal file
|
|
@ -0,0 +1,383 @@
|
|||
#
|
||||
# rational.rb -
|
||||
# $Release Version: 0.5 $
|
||||
# $Revision: 1.7 $
|
||||
# $Date: 1999/08/24 12:49:28 $
|
||||
# by Keiju ISHITSUKA(SHL Japan Inc.)
|
||||
#
|
||||
|
||||
class Rational < Numeric
|
||||
attr_reader :numerator
|
||||
attr_reader :denominator
|
||||
|
||||
def *(other)
|
||||
case other
|
||||
when Rational
|
||||
num = @numerator * other.numerator
|
||||
den = @denominator * other.denominator
|
||||
Rational(num, den)
|
||||
when Integer
|
||||
Rational(@numerator * other, @denominator)
|
||||
when Float
|
||||
to_f * other
|
||||
else
|
||||
a, b = other.coerce(self)
|
||||
a * b
|
||||
end
|
||||
end
|
||||
|
||||
def **(other)
|
||||
if other.kind_of?(Rational) && other.denominator == 1
|
||||
other = other.numerator
|
||||
end
|
||||
|
||||
case other
|
||||
when Fixnum
|
||||
if other > 0
|
||||
Rational(@numerator ** other, @denominator ** other)
|
||||
elsif other < 0
|
||||
raise ZeroDivisionError, "divided by 0" if self == 0
|
||||
Rational(@denominator ** -other, @numerator ** -other)
|
||||
elsif other == 0
|
||||
Rational.new(1, 1)
|
||||
end
|
||||
when Bignum
|
||||
if self == 0
|
||||
if other < 0
|
||||
raise ZeroDivisionError, "divided by 0"
|
||||
elsif other > 0
|
||||
Rational.new(0, 1)
|
||||
end
|
||||
elsif self == 1
|
||||
Rational.new(1, 1)
|
||||
elsif self == -1
|
||||
Rational.new(other.even? ? 1 : -1, 1)
|
||||
else
|
||||
to_f ** other
|
||||
end
|
||||
when Float
|
||||
to_f ** other
|
||||
when Rational
|
||||
if self == 0 && other < 0
|
||||
raise ZeroDivisionError, "divided by 0"
|
||||
end
|
||||
|
||||
to_f ** other
|
||||
else
|
||||
a, b = other.coerce(self)
|
||||
a ** b
|
||||
end
|
||||
end
|
||||
|
||||
def +(other)
|
||||
case other
|
||||
when Rational
|
||||
num = @numerator * other.denominator + @denominator * other.numerator
|
||||
den = @denominator * other.denominator
|
||||
Rational(num, den)
|
||||
when Integer
|
||||
Rational(@numerator + other * @denominator, @denominator)
|
||||
when Float
|
||||
to_f + other
|
||||
else
|
||||
a, b = other.coerce(self)
|
||||
a + b
|
||||
end
|
||||
end
|
||||
|
||||
def -(other)
|
||||
case other
|
||||
when Rational
|
||||
num = @numerator * other.denominator - @denominator * other.numerator
|
||||
den = @denominator * other.denominator
|
||||
Rational(num, den)
|
||||
when Integer
|
||||
Rational(@numerator - other * @denominator, @denominator)
|
||||
when Float
|
||||
to_f - other
|
||||
else
|
||||
a, b = other.coerce(self)
|
||||
a - b
|
||||
end
|
||||
end
|
||||
|
||||
def /(other)
|
||||
case other
|
||||
when Rational
|
||||
num = @numerator * other.denominator
|
||||
den = @denominator * other.numerator
|
||||
Rational(num, den)
|
||||
when Integer
|
||||
raise ZeroDivisionError, "divided by 0" if other == 0
|
||||
Rational(@numerator, @denominator * other)
|
||||
when Float
|
||||
to_f / other
|
||||
else
|
||||
redo_coerced :/, other
|
||||
end
|
||||
end
|
||||
alias_method :divide, :/
|
||||
alias_method :quo, :/
|
||||
|
||||
def <=>(other)
|
||||
case other
|
||||
when Rational
|
||||
diff = @numerator * other.denominator - @denominator * other.numerator
|
||||
diff <=> 0
|
||||
when Integer
|
||||
diff = @numerator - @denominator * other
|
||||
diff <=> 0
|
||||
when Float
|
||||
to_f <=> other
|
||||
else
|
||||
if defined?(other.coerce)
|
||||
a, b = other.coerce(self)
|
||||
a <=> b
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
case other
|
||||
when Rational
|
||||
@numerator == other.numerator && @denominator == other.denominator
|
||||
when Integer
|
||||
@numerator == other && @denominator == 1
|
||||
when Float
|
||||
to_f == other
|
||||
else
|
||||
other == self
|
||||
end
|
||||
end
|
||||
|
||||
def abs
|
||||
return self if @numerator >= 0
|
||||
|
||||
Rational.new(-@numerator, @denominator)
|
||||
end
|
||||
|
||||
def ceil(precision = 0)
|
||||
if precision == 0
|
||||
-(-@numerator / @denominator)
|
||||
else
|
||||
with_precision(:ceil, precision)
|
||||
end
|
||||
end
|
||||
|
||||
def coerce(other)
|
||||
case other
|
||||
when Integer
|
||||
return Rational.new(other, 1), self
|
||||
when Float
|
||||
return other, self.to_f
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def floor(precision = 0)
|
||||
if precision == 0
|
||||
@numerator / @denominator
|
||||
else
|
||||
with_precision(:floor, precision)
|
||||
end
|
||||
end
|
||||
|
||||
def hash
|
||||
@numerator.hash ^ @denominator.hash
|
||||
end
|
||||
|
||||
def inspect
|
||||
"(#{to_s})"
|
||||
end
|
||||
|
||||
def rationalize(eps = undefined)
|
||||
return self if undefined.equal?(eps)
|
||||
|
||||
e = eps.abs
|
||||
a = self - e
|
||||
b = self + e
|
||||
|
||||
p0 = 0
|
||||
p1 = 1
|
||||
q0 = 1
|
||||
q1 = 0
|
||||
|
||||
while true
|
||||
c = a.ceil
|
||||
|
||||
break if c < b
|
||||
|
||||
k = c - 1
|
||||
p2 = k * p1 + p0
|
||||
q2 = k * q1 + q0
|
||||
t = 1 / (b - k)
|
||||
b = 1 / (a - k)
|
||||
a = t
|
||||
|
||||
p0 = p1
|
||||
q0 = q1
|
||||
p1 = p2
|
||||
q1 = q2
|
||||
end
|
||||
|
||||
# The rational number is guaranteed to be in lowest terms.
|
||||
Rational.new(c * p1 + p0, c * q1 + q0)
|
||||
end
|
||||
|
||||
def round(precision = 0)
|
||||
return with_precision(:round, precision) unless precision == 0
|
||||
return 0 if @numerator == 0
|
||||
return @numerator if @denominator == 1
|
||||
|
||||
num = @numerator.abs * 2 + @denominator
|
||||
den = @denominator * 2
|
||||
|
||||
approx = num / den
|
||||
|
||||
if @numerator < 0
|
||||
-approx
|
||||
else
|
||||
approx
|
||||
end
|
||||
end
|
||||
|
||||
def to_f
|
||||
@numerator.to_f / @denominator.to_f
|
||||
end
|
||||
|
||||
def to_i
|
||||
truncate
|
||||
end
|
||||
|
||||
def to_r
|
||||
self
|
||||
end
|
||||
|
||||
def to_s
|
||||
"#{@numerator.to_s}/#{@denominator.to_s}"
|
||||
end
|
||||
|
||||
def truncate(precision = 0)
|
||||
if precision == 0
|
||||
@numerator < 0 ? ceil : floor
|
||||
else
|
||||
with_precision(:truncate, precision)
|
||||
end
|
||||
end
|
||||
|
||||
def self.convert(num, den, mathn = true)
|
||||
if num.nil? || den.nil?
|
||||
raise TypeError, "cannot convert nil into Rational"
|
||||
end
|
||||
|
||||
if num.kind_of?(Integer) && den.kind_of?(Integer)
|
||||
return reduce(num, den, mathn)
|
||||
end
|
||||
|
||||
case num
|
||||
when Integer
|
||||
# nothing
|
||||
when Float, String, Complex
|
||||
num = num.to_r
|
||||
end
|
||||
|
||||
case den
|
||||
when Integer
|
||||
# nothing
|
||||
when Float, String, Complex
|
||||
den = den.to_r
|
||||
end
|
||||
|
||||
if den.equal?(1) && !num.kind_of?(Integer)
|
||||
return Rubinius::Type.coerce_to(num, Rational, :to_r)
|
||||
elsif num.kind_of?(Numeric) && den.kind_of?(Numeric) &&
|
||||
!(num.kind_of?(Integer) && den.kind_of?(Integer))
|
||||
return num / den
|
||||
end
|
||||
|
||||
reduce(num, den)
|
||||
end
|
||||
|
||||
def self.reduce(num, den, mathn = true)
|
||||
case num
|
||||
when Integer
|
||||
# nothing
|
||||
when Numeric
|
||||
num = num.to_i
|
||||
else
|
||||
raise TypeError, "numerator is not an Integer"
|
||||
end
|
||||
|
||||
case den
|
||||
when Integer
|
||||
if den == 0
|
||||
raise ZeroDivisionError, "divided by 0"
|
||||
elsif den < 0
|
||||
num = -num
|
||||
den = -den
|
||||
end
|
||||
|
||||
if den == 1
|
||||
return (mathn && Rubinius.mathn_loaded?) ? num : new(num, den)
|
||||
end
|
||||
when Numeric
|
||||
den = den.to_i
|
||||
else
|
||||
raise TypeError, "denominator is not an Integer"
|
||||
end
|
||||
|
||||
gcd = num.gcd(den)
|
||||
num = num / gcd
|
||||
den = den / gcd
|
||||
|
||||
return num if mathn && Rubinius.mathn_loaded? && den == 1
|
||||
|
||||
new(num, den)
|
||||
end
|
||||
|
||||
class << self
|
||||
private :convert
|
||||
private :reduce
|
||||
end
|
||||
|
||||
def initialize(num, den)
|
||||
@numerator = num
|
||||
@denominator = den
|
||||
end
|
||||
private :initialize
|
||||
|
||||
def marshal_dump
|
||||
ary = [@numerator, @denominator]
|
||||
|
||||
instance_variables.each do |ivar|
|
||||
ary.instance_variable_set(ivar, instance_variable_get(ivar))
|
||||
end
|
||||
|
||||
ary
|
||||
end
|
||||
private :marshal_dump
|
||||
|
||||
def marshal_load(ary)
|
||||
@numerator, @denominator = ary
|
||||
|
||||
ary.instance_variables.each do |ivar|
|
||||
instance_variable_set(ivar, ary.instance_variable_get(ivar))
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
private :marshal_load
|
||||
|
||||
def with_precision(method, n)
|
||||
raise TypeError, "not an Integer" unless n.kind_of?(Integer)
|
||||
|
||||
p = 10 ** n
|
||||
s = self * p
|
||||
|
||||
r = Rational(s.send(method), p)
|
||||
|
||||
n < 1 ? r.to_i : r
|
||||
end
|
||||
private :with_precision
|
||||
end
|
||||
52
gxg/core/rationalizer.rb
Normal file
52
gxg/core/rationalizer.rb
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# Float#rationalize and String#to_r require complex algorithms. The Regexp
|
||||
# required for String#to_r cannot be created at class scope when loading the
|
||||
# core library because Regexp needs to load after String and making a simpler
|
||||
# Regexp#initialize for bootstrapping isn't really feasible. So these
|
||||
# algorithms are located here.
|
||||
#
|
||||
class String
|
||||
class Rationalizer
|
||||
def initialize(value)
|
||||
@value = value
|
||||
end
|
||||
|
||||
private :initialize
|
||||
|
||||
def convert
|
||||
if m = PATTERN.match(@value)
|
||||
si = m[1]
|
||||
nu = m[2]
|
||||
de = m[3]
|
||||
re = m.post_match
|
||||
|
||||
ifp, exp = nu.split /[eE]/
|
||||
ip, fp = ifp.split /\./
|
||||
|
||||
value = Rational.new(ip.to_i, 1)
|
||||
|
||||
if fp
|
||||
ctype = Rubinius::CType
|
||||
i = count = 0
|
||||
size = fp.size
|
||||
while i < size
|
||||
count += 1 if ctype.isdigit fp.getbyte(i)
|
||||
i += 1
|
||||
end
|
||||
|
||||
l = 10 ** count
|
||||
value *= l
|
||||
value += fp.to_i
|
||||
value = value.quo(l)
|
||||
end
|
||||
|
||||
value = -value if si == "-"
|
||||
value *= 10 ** exp.to_i if exp
|
||||
value = value.quo(de.to_i) if de
|
||||
|
||||
value
|
||||
else
|
||||
Rational(0, 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue