Initial Commit

This commit is contained in:
G. Gibson 2021-11-16 08:58:51 -08:00
parent 356ad3315b
commit 81daf6f387
283 changed files with 130672 additions and 1 deletions

31
LICENSE.txt Normal file
View file

@ -0,0 +1,31 @@
GxG Web Client Copyright (2021) (COPYRIGHT G. GIBSON)(“Licensor”)
Hippocratic License Version Number: 2.1.
Purpose. The purpose of this License is for the Licensor named above to permit the Licensee (as defined below) broad permission, if consistent with Human Rights Laws and Human Rights Principles (as each is defined below), to use and work with the Software (as defined below) within the full scope of Licensors copyright and patent rights, if any, in the Software, while ensuring attribution and protecting the Licensor from liability.
Permission and Conditions. The Licensor grants permission by this license (“License”), free of charge, to the extent of Licensors rights under applicable copyright and patent law, to any person or entity (the “Licensee”) obtaining a copy of this software and associated documentation files (the “Software”), to do everything with the Software that would otherwise infringe (i) the Licensors copyright in the Software or (ii) any patent claims to the Software that the Licensor can license or becomes able to license, subject to all of the following terms and conditions:
Acceptance. This License is automatically offered to every person and entity subject to its terms and conditions. Licensee accepts this License and agrees to its terms and conditions by taking any action with the Software that, absent this License, would infringe any intellectual property right held by Licensor.
Notice. Licensee must ensure that everyone who gets a copy of any part of this Software from Licensee, with or without changes, also receives the License and the above copyright notice (and if included by the Licensor, patent, trademark and attribution notice). Licensee must cause any modified versions of the Software to carry prominent notices stating that Licensee changed the Software. For clarity, although Licensee is free to create modifications of the Software and distribute only the modified portion created by Licensee with additional or different terms, the portion of the Software not modified must be distributed pursuant to this License. If anyone notifies Licensee in writing that Licensee has not complied with this Notice section, Licensee can keep this License by taking all practical steps to comply within 30 days after the notice. If Licensee does not do so, Licensees License (and all rights licensed hereunder) shall end immediately.
Compliance with Human Rights Principles and Human Rights Laws.
Human Rights Principles.
(a) Licensee is advised to consult the articles of the United Nations Universal Declaration of Human Rights and the United Nations Global Compact that define recognized principles of international human rights (the “Human Rights Principles”). Licensee shall use the Software in a manner consistent with Human Rights Principles.
(b) Unless the Licensor and Licensee agree otherwise, any dispute, controversy, or claim arising out of or relating to (i) Section 1(a) regarding Human Rights Principles, including the breach of Section 1(a), termination of this License for breach of the Human Rights Principles, or invalidity of Section 1(a) or (ii) a determination of whether any Law is consistent or in conflict with Human Rights Principles pursuant to Section 2, below, shall be settled by arbitration in accordance with the Hague Rules on Business and Human Rights Arbitration (the “Rules”); provided, however, that Licensee may elect not to participate in such arbitration, in which event this License (and all rights licensed hereunder) shall end immediately. The number of arbitrators shall be one unless the Rules require otherwise.
Unless both the Licensor and Licensee agree to the contrary: (1) All documents and information concerning the arbitration shall be public and may be disclosed by any party; (2) The repository referred to under Article 43 of the Rules shall make available to the public in a timely manner all documents concerning the arbitration which are communicated to it, including all submissions of the parties, all evidence admitted into the record of the proceedings, all transcripts or other recordings of hearings and all orders, decisions and awards of the arbitral tribunal, subject only to the arbitral tribunals powers to take such measures as may be necessary to safeguard the integrity of the arbitral process pursuant to Articles 18, 33, 41 and 42 of the Rules; and (3) Article 26(6) of the Rules shall not apply.
Human Rights Laws. The Software shall not be used by any person or entity for any systems, activities, or other uses that violate any Human Rights Laws. “Human Rights Laws” means any applicable laws, regulations, or rules (collectively, “Laws”) that protect human, civil, labor, privacy, political, environmental, security, economic, due process, or similar rights; provided, however, that such Laws are consistent and not in conflict with Human Rights Principles (a dispute over the consistency or a conflict between Laws and Human Rights Principles shall be determined by arbitration as stated above). Where the Human Rights Laws of more than one jurisdiction are applicable or in conflict with respect to the use of the Software, the Human Rights Laws that are most protective of the individuals or groups harmed shall apply.
Indemnity. Licensee shall hold harmless and indemnify Licensor (and any other contributor) against all losses, damages, liabilities, deficiencies, claims, actions, judgments, settlements, interest, awards, penalties, fines, costs, or expenses of whatever kind, including Licensors reasonable attorneys fees, arising out of or relating to Licensees use of the Software in violation of Human Rights Laws or Human Rights Principles.
Failure to Comply. Any failure of Licensee to act according to the terms and conditions of this License is both a breach of the License and an infringement of the intellectual property rights of the Licensor (subject to exceptions under Laws, e.g., fair use). In the event of a breach or infringement, the terms and conditions of this License may be enforced by Licensor under the Laws of any jurisdiction to which Licensee is subject. Licensee also agrees that the Licensor may enforce the terms and conditions of this License against Licensee through specific performance (or similar remedy under Laws) to the extent permitted by Laws. For clarity, except in the event of a breach of this License, infringement, or as otherwise stated in this License, Licensor may not terminate this License with Licensee.
Enforceability and Interpretation. If any term or provision of this License is determined to be invalid, illegal, or unenforceable by a court of competent jurisdiction, then such invalidity, illegality, or unenforceability shall not affect any other term or provision of this License or invalidate or render unenforceable such term or provision in any other jurisdiction; provided, however, subject to a court modification pursuant to the immediately following sentence, if any term or provision of this License pertaining to Human Rights Laws or Human Rights Principles is deemed invalid, illegal, or unenforceable against Licensee by a court of competent jurisdiction, all rights in the Software granted to Licensee shall be deemed null and void as between Licensor and Licensee. Upon a determination that any term or provision is invalid, illegal, or unenforceable, to the extent permitted by Laws, the court may modify this License to affect the original purpose that the Software be used in compliance with Human Rights Principles and Human Rights Laws as closely as possible. The language in this License shall be interpreted as to its fair meaning and not strictly for or against any party.
Disclaimer. TO THE FULL EXTENT ALLOWED BY LAW, THIS SOFTWARE COMES “AS IS,” WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED, AND LICENSOR AND ANY OTHER CONTRIBUTOR SHALL NOT BE LIABLE TO ANYONE FOR ANY DAMAGES OR OTHER LIABILITY ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THIS LICENSE, UNDER ANY KIND OF LEGAL CLAIM.

View file

@ -1,2 +1,11 @@
# gxg-web-client
GxG Web Client
Installation:
sudo gem install opal
ln -s /path/to/opal/stdlib opal-stdlib
Building:
./update_web.sh

7
gxg/core/alias.rb Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

96
gxg/core/array_mirror.rb Normal file
View 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
View 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
View 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
View 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
View 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
View 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 bindings 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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

View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

119
gxg/core/file_test.rb Normal file
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

23
gxg/core/hook.rb Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

3609
gxg/core/io.rb Normal file

File diff suppressed because it is too large Load diff

36
gxg/core/iseq.rb Normal file
View 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
View 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

File diff suppressed because it is too large Load diff

181
gxg/core/lexical_scope.rb Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

208
gxg/core/match_data.rb Normal file
View 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
View 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
View 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

View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

Some files were not shown because too many files have changed in this diff Show more