mirror of
https://github.com/mistergibson/gxg-web-client.git
synced 2026-08-15 03:26:00 -07:00
Initial Commit
This commit is contained in:
parent
356ad3315b
commit
81daf6f387
283 changed files with 130672 additions and 1 deletions
31
LICENSE.txt
Normal file
31
LICENSE.txt
Normal 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 Licensor’s 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 Licensor’s 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 Licensor’s 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, Licensee’s 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 tribunal’s 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 Licensor’s reasonable attorneys’ fees, arising out of or relating to Licensee’s 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.
|
||||||
11
README.md
11
README.md
|
|
@ -1,2 +1,11 @@
|
||||||
# gxg-web-client
|
|
||||||
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
7
gxg/core/alias.rb
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
module Rubinius
|
||||||
|
class Alias
|
||||||
|
attr_reader :original_exec
|
||||||
|
attr_reader :original_module
|
||||||
|
attr_reader :original_name
|
||||||
|
end
|
||||||
|
end
|
||||||
810
gxg/core/alpha.rb
Normal file
810
gxg/core/alpha.rb
Normal file
|
|
@ -0,0 +1,810 @@
|
||||||
|
# This is the beginning of loading Ruby code. At this point, the VM is
|
||||||
|
# bootstrapped and the fundamental data structures, primitive functions and
|
||||||
|
# the basic classes and objects are available.
|
||||||
|
#
|
||||||
|
# The classes, modules, and methods defined here provide basic functionality
|
||||||
|
# needed to load the bootstrap directory. By the end of this file, the
|
||||||
|
# following methods are available:
|
||||||
|
#
|
||||||
|
# attr_reader :sym
|
||||||
|
# attr_writer :sym
|
||||||
|
# attr_accessor :sym
|
||||||
|
#
|
||||||
|
# private :sym
|
||||||
|
# protected :sym
|
||||||
|
#
|
||||||
|
# module_function :sym
|
||||||
|
#
|
||||||
|
# These forms should be used in the rest of the core library. In core/zed.rb,
|
||||||
|
# more complete forms of these methods are provided for user code.
|
||||||
|
#
|
||||||
|
# NOTE: The order of these definitions is important. Do not
|
||||||
|
# change it without consultation.
|
||||||
|
|
||||||
|
class NilClass
|
||||||
|
def nil?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def !
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def !=(other)
|
||||||
|
other.nil? ? false : true
|
||||||
|
end
|
||||||
|
|
||||||
|
def ==(other)
|
||||||
|
other.nil? ? true : false
|
||||||
|
end
|
||||||
|
|
||||||
|
# alias_method is not available yet
|
||||||
|
def equal?(other)
|
||||||
|
other.nil? ? true : false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# This class encapsulates primitives that involve the VM itself rather than
|
||||||
|
# something in Ruby-land.
|
||||||
|
#
|
||||||
|
# See core/vm.rb
|
||||||
|
#
|
||||||
|
class Rubinius::VM
|
||||||
|
|
||||||
|
# Write given string to stderr.
|
||||||
|
#
|
||||||
|
# Used to support error reporting where IO is not reliable yet.
|
||||||
|
#
|
||||||
|
def self.write_error(str)
|
||||||
|
Rubinius.primitive :vm_write_error
|
||||||
|
raise PrimitiveFailure, "Rubinius::VM.write_error primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Prints Ruby backtrace at point of call.
|
||||||
|
#
|
||||||
|
def self.show_backtrace
|
||||||
|
Rubinius.primitive :vm_show_backtrace
|
||||||
|
raise PrimitiveFailure, "Rubinius::VM.show_backtrace primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Reset the method cache globally for given method name.
|
||||||
|
#
|
||||||
|
def self.reset_method_cache(mod, sym)
|
||||||
|
Rubinius.primitive :vm_reset_method_cache
|
||||||
|
raise PrimitiveFailure, "Rubinius::VM.reset_method_cache primitive failed"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
class Object
|
||||||
|
# Prints basic information about the object to stdout.
|
||||||
|
#
|
||||||
|
def __show__
|
||||||
|
Rubinius.primitive :object_show
|
||||||
|
"<unknown>"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
class Class
|
||||||
|
|
||||||
|
# Allocate memory for an instance of the class without initialization.
|
||||||
|
#
|
||||||
|
# The object returned is valid to use, but its #initialize method has not
|
||||||
|
# been called. In almost all cases, .new is the correct method to use
|
||||||
|
# instead.
|
||||||
|
#
|
||||||
|
# See .new
|
||||||
|
#
|
||||||
|
def allocate
|
||||||
|
Rubinius.primitive :class_allocate
|
||||||
|
raise RuntimeError, "Class#allocate primitive failed on #{self.inspect}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Allocate and initialize an instance of the class.
|
||||||
|
#
|
||||||
|
# Default implementation: merely allocates the instance, and then calls the
|
||||||
|
# #initialize method on the object with the given arguments and block, if
|
||||||
|
# provided.
|
||||||
|
#
|
||||||
|
# See .allocate
|
||||||
|
#
|
||||||
|
def new(*args)
|
||||||
|
obj = allocate()
|
||||||
|
|
||||||
|
Rubinius.asm(args, obj) do |args, obj|
|
||||||
|
run obj
|
||||||
|
run args
|
||||||
|
push_block
|
||||||
|
send_with_splat :initialize, 0, true
|
||||||
|
# no pop here, as .asm blocks imply a pop as they're not
|
||||||
|
# allowed to leak a stack value
|
||||||
|
end
|
||||||
|
|
||||||
|
obj
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module Kernel
|
||||||
|
def nil?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return the Class object this object is an instance of.
|
||||||
|
#
|
||||||
|
# Note that this method must always be called with an explicit receiver,
|
||||||
|
# since class by itself is a keyword.
|
||||||
|
#
|
||||||
|
def class
|
||||||
|
Rubinius.primitive :object_class
|
||||||
|
raise PrimitiveFailure, "Kernel#class primitive failed."
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return the class object of self.
|
||||||
|
#
|
||||||
|
# This is the same as #class, but named to always be available.
|
||||||
|
def __class__
|
||||||
|
Rubinius.primitive :object_class
|
||||||
|
raise PrimitiveFailure, "Kernel#class primitive failed."
|
||||||
|
end
|
||||||
|
|
||||||
|
# String representation of an object.
|
||||||
|
#
|
||||||
|
# By default, the representation is the name of the object's class preceded
|
||||||
|
# by a # to indicate the object is an instance thereof.
|
||||||
|
#
|
||||||
|
def to_s
|
||||||
|
"#<#{self.class.name}>"
|
||||||
|
end
|
||||||
|
|
||||||
|
# :internal:
|
||||||
|
#
|
||||||
|
# Lowest-level implementation of raise, used internally by core library code
|
||||||
|
# until a more sophisticated version is loaded.
|
||||||
|
#
|
||||||
|
# Redefined later.
|
||||||
|
#
|
||||||
|
def raise(cls, str="", junk=nil)
|
||||||
|
Rubinius::VM.write_error "Fatal error loading core library:\n "
|
||||||
|
Rubinius::VM.write_error str
|
||||||
|
Rubinius::VM.write_error "\n"
|
||||||
|
Rubinius::VM.show_backtrace
|
||||||
|
Process.exit! 1
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns true if the given Class is either the class or superclass of the
|
||||||
|
# object or, when given a Module, if the Module has been included in
|
||||||
|
# object's class or one of its superclasses. Returns false otherwise.
|
||||||
|
#
|
||||||
|
# If the argument is not a Class or Module, a TypeError is raised.
|
||||||
|
#
|
||||||
|
def kind_of?(cls)
|
||||||
|
Rubinius.primitive :object_kind_of
|
||||||
|
raise TypeError, "Kernel#kind_of? requires a Class or Module argument"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Hook method invoked when object is sent a message it cannot handle.
|
||||||
|
#
|
||||||
|
# The default implementation will merely raise a NoMethodError with
|
||||||
|
# information about the message.
|
||||||
|
#
|
||||||
|
# This method may be overridden, and is often used to provide dynamic
|
||||||
|
# behaviour. An overriding version should call super if it fails to resolve
|
||||||
|
# the message. This practice ensures that the default version will be called
|
||||||
|
# if all else fails.
|
||||||
|
#
|
||||||
|
def method_missing(meth, *args)
|
||||||
|
raise NoMethodError, "Unable to send '#{meth}' on '#{self}' (#{self.class})"
|
||||||
|
end
|
||||||
|
|
||||||
|
# :internal:
|
||||||
|
#
|
||||||
|
# Backend method for Object#dup and Object#clone.
|
||||||
|
#
|
||||||
|
# Redefined in core/kernel.rb
|
||||||
|
#
|
||||||
|
def initialize_copy(other)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generic shallow copy of object.
|
||||||
|
#
|
||||||
|
# Copies instance variables, but does not recursively copy the objects they
|
||||||
|
# reference. Copies taintedness.
|
||||||
|
#
|
||||||
|
# In contrast to .clone, .dup can be considered as creating a new object of
|
||||||
|
# the same class and populating it with data from the object.
|
||||||
|
#
|
||||||
|
# If class-specific behaviour is desired, the class should define
|
||||||
|
# #initialize_copy and implement the behaviour there. #initialize_copy will
|
||||||
|
# automatically be called on the new object - the copy - with the original
|
||||||
|
# object as argument if defined.
|
||||||
|
#
|
||||||
|
def dup
|
||||||
|
copy = Rubinius::Type.object_class(self).allocate
|
||||||
|
|
||||||
|
Rubinius.invoke_primitive :object_copy_object, copy, self
|
||||||
|
|
||||||
|
Rubinius::Type.object_initialize_dup self, copy
|
||||||
|
copy
|
||||||
|
end
|
||||||
|
|
||||||
|
# Direct shallow copy of object.
|
||||||
|
#
|
||||||
|
# Copies instance variables, but does not recursively copy the objects they
|
||||||
|
# reference. Copies taintedness and frozenness.
|
||||||
|
#
|
||||||
|
# In contrast to .dup, .clone can be considered to actually clone the
|
||||||
|
# existing object, including its internal state and any singleton methods.
|
||||||
|
#
|
||||||
|
# If class-specific behaviour is desired, the class should define
|
||||||
|
# #initialize_copy and implement the behaviour there. #initialize_copy will
|
||||||
|
# automatically be called on the new object - the copy - with the original
|
||||||
|
# object as argument if defined.
|
||||||
|
#
|
||||||
|
def clone
|
||||||
|
# Do not implement in terms of dup. It breaks rails.
|
||||||
|
#
|
||||||
|
copy = Rubinius::Type.object_class(self).allocate
|
||||||
|
|
||||||
|
Rubinius.invoke_primitive :object_copy_object, copy, self
|
||||||
|
Rubinius.invoke_primitive :object_copy_singleton_class, copy, self
|
||||||
|
|
||||||
|
Rubinius::Type.object_initialize_clone self, copy
|
||||||
|
|
||||||
|
copy.freeze if frozen?
|
||||||
|
copy
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Module for internals.
|
||||||
|
#
|
||||||
|
# See core/rubinius.rb
|
||||||
|
#
|
||||||
|
module Rubinius
|
||||||
|
class Mirror
|
||||||
|
def self.subject=(klass)
|
||||||
|
@subject = klass
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.subject
|
||||||
|
@subject
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module Runtime
|
||||||
|
def self.dup_as_array(obj)
|
||||||
|
Rubinius.primitive :array_dup_as_array
|
||||||
|
raise PrimitiveFailure, "Rubinius::Runtime.dup_as_array primitive failed"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Executable abstraction for accessors.
|
||||||
|
#
|
||||||
|
class AccessVariable
|
||||||
|
|
||||||
|
# Specialised allocation.
|
||||||
|
#
|
||||||
|
def self.allocate
|
||||||
|
Rubinius.primitive :accessvariable_allocate
|
||||||
|
raise PrimitiveFailure, "AccessVariable.allocate primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Set up the executable.
|
||||||
|
#
|
||||||
|
# Name of variable provided without leading @, the second parameter should
|
||||||
|
# be true if the attr is writable.
|
||||||
|
#
|
||||||
|
def initialize(variable, write)
|
||||||
|
@primitive = nil
|
||||||
|
@serial = 0
|
||||||
|
@name = "@#{variable}".to_sym
|
||||||
|
@write = write
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get the arity for this reader or writr
|
||||||
|
#
|
||||||
|
def arity
|
||||||
|
@write ? 1 : 0
|
||||||
|
end
|
||||||
|
|
||||||
|
# Create a getter for named instance var, without leading @.
|
||||||
|
#
|
||||||
|
def self.get_ivar(name)
|
||||||
|
new(name, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Create a setter for named instance var, without leading @.
|
||||||
|
#
|
||||||
|
def self.set_ivar(name)
|
||||||
|
new(name, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Simplified lookup table.
|
||||||
|
#
|
||||||
|
# See core/lookup_table.rb.
|
||||||
|
#
|
||||||
|
class LookupTable
|
||||||
|
|
||||||
|
# Retrieve value for given key.
|
||||||
|
#
|
||||||
|
def [](key)
|
||||||
|
Rubinius.primitive :lookuptable_aref
|
||||||
|
raise PrimitiveFailure, "LookupTable#[] primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Store value under key.
|
||||||
|
#
|
||||||
|
def []=(key, val)
|
||||||
|
Rubinius.primitive :lookuptable_store
|
||||||
|
raise PrimitiveFailure, "LookupTable#[]= primitive failed"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Constant table for storing methods.
|
||||||
|
#
|
||||||
|
# See core/constant_table.rb
|
||||||
|
#
|
||||||
|
class ConstantTable
|
||||||
|
# Perform lookup for constant name.
|
||||||
|
#
|
||||||
|
def lookup(name)
|
||||||
|
Rubinius.primitive :constant_table_lookup
|
||||||
|
raise PrimitiveFailure, "ConstantTable#lookup primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Store Constant under name, with given visibility.
|
||||||
|
#
|
||||||
|
def store(name, constant, visibility)
|
||||||
|
Rubinius.primitive :constant_table_store
|
||||||
|
raise PrimitiveFailure, "ConstantTable#store primitive failed"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Lookup table for storing methods.
|
||||||
|
#
|
||||||
|
# See core/method_table.rb
|
||||||
|
#
|
||||||
|
class MethodTable
|
||||||
|
# Perform lookup for method name.
|
||||||
|
#
|
||||||
|
def lookup(name)
|
||||||
|
Rubinius.primitive :methodtable_lookup
|
||||||
|
raise PrimitiveFailure, "MethodTable#lookup primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Store Executable under name, with given visibility.
|
||||||
|
#
|
||||||
|
def store(name, method_id, exec, scope, serial, visibility)
|
||||||
|
Rubinius.primitive :methodtable_store
|
||||||
|
raise PrimitiveFailure, "MethodTable#store primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Make an alias from +original_name+ in +original_mod+ to +name+ with
|
||||||
|
# visibility +vis+
|
||||||
|
def alias(name, visibility, original_name, original_exec, original_mod)
|
||||||
|
Rubinius.primitive :methodtable_alias
|
||||||
|
raise PrimitiveFailure, "MethodTable#alias primitive failed"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Symbol
|
||||||
|
# Produce String representation of this Symbol.
|
||||||
|
#
|
||||||
|
def to_s
|
||||||
|
Rubinius.primitive :symbol_to_s
|
||||||
|
raise PrimitiveFailure, "Symbol#to_s primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
# For completeness, returns self.
|
||||||
|
#
|
||||||
|
def to_sym
|
||||||
|
self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class String
|
||||||
|
# Returns the <code>Symbol</code> corresponding to <i>self</i>, creating the
|
||||||
|
# symbol if it did not previously exist. See <code>Symbol#id2name</code>.
|
||||||
|
#
|
||||||
|
# "Koala".intern #=> :Koala
|
||||||
|
# s = 'cat'.to_sym #=> :cat
|
||||||
|
# s == :cat #=> true
|
||||||
|
# s = '@cat'.to_sym #=> :@cat
|
||||||
|
# s == :@cat #=> true
|
||||||
|
#
|
||||||
|
# This can also be used to create symbols that cannot be represented using the
|
||||||
|
# <code>:xxx</code> notation.
|
||||||
|
#
|
||||||
|
# 'cat and dog'.to_sym #=> :"cat and dog"
|
||||||
|
#--
|
||||||
|
# TODO: Add taintedness-check
|
||||||
|
#++
|
||||||
|
def to_sym
|
||||||
|
Rubinius.primitive :symbol_lookup
|
||||||
|
raise PrimitiveFailure, "String#to_sym primitive failed: #{self.dump}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# For completeness, returns self.
|
||||||
|
#
|
||||||
|
def to_s
|
||||||
|
self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module Process
|
||||||
|
# Terminate with given status code.
|
||||||
|
#
|
||||||
|
def self.exit(code=0)
|
||||||
|
case code
|
||||||
|
when true
|
||||||
|
code = 0
|
||||||
|
when false
|
||||||
|
code = 1
|
||||||
|
else
|
||||||
|
code = Rubinius::Type.coerce_to code, Integer, :to_int
|
||||||
|
end
|
||||||
|
|
||||||
|
raise SystemExit.new(code)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.exit!(code=1)
|
||||||
|
Rubinius.primitive :vm_exit
|
||||||
|
|
||||||
|
case code
|
||||||
|
when true
|
||||||
|
exit! 0
|
||||||
|
when false
|
||||||
|
exit! 1
|
||||||
|
else
|
||||||
|
exit! Rubinius::Type.coerce_to(code, Integer, :to_int)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Module
|
||||||
|
def method_table ; @method_table ; end
|
||||||
|
def constant_table ; @constant_table ; end
|
||||||
|
def name ; @module_name.to_s ; end
|
||||||
|
def origin ; @origin ; end
|
||||||
|
|
||||||
|
# Specialised allocator.
|
||||||
|
#
|
||||||
|
def self.allocate
|
||||||
|
Rubinius.primitive :module_allocate
|
||||||
|
raise PrimitiveFailure, "Module.allocate primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
# :internal:
|
||||||
|
#
|
||||||
|
# Hook called when a constant cannot be located.
|
||||||
|
#
|
||||||
|
# Default implementation 'raises', but we don't use #raise to prevent
|
||||||
|
# infinite recursion.
|
||||||
|
#
|
||||||
|
# Redefined in core/module.rb
|
||||||
|
#
|
||||||
|
def const_missing(name)
|
||||||
|
Rubinius::VM.write_error "Missing or uninitialized constant: #{name.to_s}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Set Module's direct superclass.
|
||||||
|
#
|
||||||
|
# The corresponding 'getter' #superclass method is defined in class.rb,
|
||||||
|
# because it is more complex than a mere accessor
|
||||||
|
#
|
||||||
|
def superclass=(other)
|
||||||
|
Rubinius.check_frozen
|
||||||
|
@superclass = other
|
||||||
|
end
|
||||||
|
|
||||||
|
# Module's stored superclass.
|
||||||
|
#
|
||||||
|
# This may be either an included Module or an inherited Class.
|
||||||
|
#
|
||||||
|
def direct_superclass
|
||||||
|
@superclass
|
||||||
|
end
|
||||||
|
|
||||||
|
# :internal:
|
||||||
|
#
|
||||||
|
# Perform actual work for including a Module in this one.
|
||||||
|
#
|
||||||
|
# Redefined in core/module.rb.
|
||||||
|
#
|
||||||
|
def append_features(mod)
|
||||||
|
im = Rubinius::IncludedModule.new(self)
|
||||||
|
im.attach_to mod
|
||||||
|
end
|
||||||
|
|
||||||
|
# Hook method called on Module when another Module is .include'd into it.
|
||||||
|
#
|
||||||
|
# Override for module-specific behaviour.
|
||||||
|
#
|
||||||
|
def included(mod); end
|
||||||
|
|
||||||
|
# :internal:
|
||||||
|
#
|
||||||
|
# Basic version of .include used while loading core library code.
|
||||||
|
#
|
||||||
|
# Redefined in core/module.rb.
|
||||||
|
#
|
||||||
|
def include(mod)
|
||||||
|
mod.append_features(self)
|
||||||
|
Rubinius.privately do
|
||||||
|
mod.included self
|
||||||
|
end
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
# :internal:
|
||||||
|
#
|
||||||
|
# Basic version used while loading core library code.
|
||||||
|
#
|
||||||
|
# Redefined in core/module.rb.
|
||||||
|
#
|
||||||
|
def attr_reader(name)
|
||||||
|
Rubinius::VM.reset_method_cache self, name
|
||||||
|
meth = Rubinius::AccessVariable.get_ivar name
|
||||||
|
@method_table.store name, nil, meth, nil, 0, :public
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def attr_reader_specific(name, method_name)
|
||||||
|
Rubinius::VM.reset_method_cache self, method_name
|
||||||
|
meth = Rubinius::AccessVariable.get_ivar name
|
||||||
|
@method_table.store method_name, nil, meth, nil, 0, :public
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# :internal:
|
||||||
|
#
|
||||||
|
# Basic version used while loading core library code.
|
||||||
|
#
|
||||||
|
# Redefined in core/module.rb.
|
||||||
|
#
|
||||||
|
def attr_writer(name)
|
||||||
|
meth = Rubinius::AccessVariable.set_ivar name
|
||||||
|
writer_name = "#{name}=".to_sym
|
||||||
|
Rubinius::VM.reset_method_cache self, writer_name
|
||||||
|
@method_table.store writer_name, nil, meth, nil, 0, :public
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# :internal:
|
||||||
|
#
|
||||||
|
# Basic version used while loading core library code.
|
||||||
|
#
|
||||||
|
# Redefined in core/module.rb.
|
||||||
|
#
|
||||||
|
def attr_accessor(name)
|
||||||
|
attr_reader(name)
|
||||||
|
attr_writer(name)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# :internal:
|
||||||
|
#
|
||||||
|
# Basic version used while loading core library code. Cannot be used as a
|
||||||
|
# toggle, and only takes a single method name.
|
||||||
|
#
|
||||||
|
# Redefined in core/module.rb.
|
||||||
|
#
|
||||||
|
def public(name)
|
||||||
|
if entry = @method_table.lookup(name)
|
||||||
|
entry.visibility = :public
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# :internal:
|
||||||
|
#
|
||||||
|
# Basic version used while loading core library code. Cannot be used as a
|
||||||
|
# toggle, and only takes a single method name.
|
||||||
|
#
|
||||||
|
# Redefined in core/module.rb.
|
||||||
|
#
|
||||||
|
def private(name)
|
||||||
|
if entry = @method_table.lookup(name)
|
||||||
|
entry.visibility = :private
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# :internal:
|
||||||
|
#
|
||||||
|
# Basic version used while loading core library code. Cannot be used as a
|
||||||
|
# toggle, and only takes a single method name.
|
||||||
|
#
|
||||||
|
# Redefined in core/module.rb.
|
||||||
|
#
|
||||||
|
def protected(name)
|
||||||
|
if entry = @method_table.lookup(name)
|
||||||
|
entry.visibility = :protected
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# :internal:
|
||||||
|
#
|
||||||
|
# Basic version used while loading core library code. Creates a copy of
|
||||||
|
# current method and stores it under the new name. The two are independent.
|
||||||
|
#
|
||||||
|
# Redefined in core/module.rb.
|
||||||
|
#
|
||||||
|
def alias_method(new_name, current_name)
|
||||||
|
Rubinius::VM.reset_method_cache self, new_name
|
||||||
|
|
||||||
|
# If we're aliasing a method we contain, just reference it directly, no
|
||||||
|
# need for the alias wrapper
|
||||||
|
if entry = @method_table.lookup(current_name)
|
||||||
|
@method_table.store new_name, entry.method_id, entry.get_method,
|
||||||
|
entry.scope, entry.serial, entry.visibility
|
||||||
|
else
|
||||||
|
mod = direct_superclass()
|
||||||
|
while mod
|
||||||
|
entry = mod.method_table.lookup(current_name)
|
||||||
|
break if entry
|
||||||
|
mod = mod.direct_superclass
|
||||||
|
end
|
||||||
|
|
||||||
|
unless entry
|
||||||
|
raise NoMethodError, "No method '#{current_name}' to alias to '#{new_name}'"
|
||||||
|
end
|
||||||
|
|
||||||
|
@method_table.alias new_name, entry.visibility, current_name,
|
||||||
|
entry.get_method, mod
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# :internal:
|
||||||
|
#
|
||||||
|
# Basic version used while loading core library code. Only takes a single
|
||||||
|
# method name.
|
||||||
|
#
|
||||||
|
# Redefined in core/module.rb.
|
||||||
|
#
|
||||||
|
def module_function(name)
|
||||||
|
if entry = @method_table.lookup(name)
|
||||||
|
sc = class << self; self; end
|
||||||
|
Rubinius::VM.reset_method_cache sc, name
|
||||||
|
sc.method_table.store name, entry.method_id, entry.method,
|
||||||
|
entry.scope, entry.serial, :public
|
||||||
|
private name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def custom_call_site(name)
|
||||||
|
if entry = @method_table.lookup(name)
|
||||||
|
entry.get_method.custom_call_site
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def undef_method(name)
|
||||||
|
Rubinius::VM.reset_method_cache self, name
|
||||||
|
@method_table.store name, nil, nil, nil, 0, :undef
|
||||||
|
name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module Rubinius
|
||||||
|
class AccessVariable
|
||||||
|
attr_reader :name
|
||||||
|
end
|
||||||
|
|
||||||
|
# Visibility handling for MethodTables.
|
||||||
|
#
|
||||||
|
# See core/method_table.rb
|
||||||
|
#
|
||||||
|
class MethodTable::Bucket
|
||||||
|
attr_accessor :visibility
|
||||||
|
attr_accessor :method_id
|
||||||
|
attr_accessor :method
|
||||||
|
attr_accessor :scope
|
||||||
|
attr_accessor :serial
|
||||||
|
|
||||||
|
def get_method
|
||||||
|
Rubinius.primitive :methodtable_bucket_get_method
|
||||||
|
raise PrimitiveFailure, "MethodTable::Bucket#get_method primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def public?
|
||||||
|
@visibility == :public
|
||||||
|
end
|
||||||
|
|
||||||
|
def private?
|
||||||
|
@visibility == :private
|
||||||
|
end
|
||||||
|
|
||||||
|
def protected?
|
||||||
|
@visibility == :protected
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# :internal:
|
||||||
|
#
|
||||||
|
# Internal representation of a Module's inclusion in another.
|
||||||
|
#
|
||||||
|
# Abstracts the injection of the included Module into the ancestor hierarchy
|
||||||
|
# for method- and constant lookup in a roughly transparent fashion.
|
||||||
|
#
|
||||||
|
# This class is known to the VM.
|
||||||
|
#
|
||||||
|
class IncludedModule < Module
|
||||||
|
attr_reader :superclass
|
||||||
|
attr_reader :module
|
||||||
|
|
||||||
|
# :internal:
|
||||||
|
#
|
||||||
|
# Specialised allocator.
|
||||||
|
#
|
||||||
|
def self.allocate
|
||||||
|
Rubinius.primitive :included_module_allocate
|
||||||
|
raise PrimitiveFailure, "IncludedModule.allocate primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
# :internal:
|
||||||
|
#
|
||||||
|
# Created referencing the Module that is being included.
|
||||||
|
#
|
||||||
|
def initialize(mod)
|
||||||
|
@method_table = mod.method_table
|
||||||
|
@constant_table = mod.constant_table
|
||||||
|
@module = mod
|
||||||
|
end
|
||||||
|
|
||||||
|
def module
|
||||||
|
@module
|
||||||
|
end
|
||||||
|
|
||||||
|
# :internal:
|
||||||
|
#
|
||||||
|
# Inject self inbetween class and its previous direct superclass.
|
||||||
|
#
|
||||||
|
def attach_to(cls)
|
||||||
|
@superclass = cls.direct_superclass
|
||||||
|
cls.superclass = self
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
# :internal:
|
||||||
|
#
|
||||||
|
# Name of the included Module.
|
||||||
|
#
|
||||||
|
def name
|
||||||
|
@module.name
|
||||||
|
end
|
||||||
|
|
||||||
|
# :internal:
|
||||||
|
#
|
||||||
|
# String representation of the included Module.
|
||||||
|
#
|
||||||
|
def to_s
|
||||||
|
@module.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
"#<IncludedModule #{@module.to_s}>"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns true if +other+ is the same object as self or if +other+ is the
|
||||||
|
# module this IncludedModule is for.
|
||||||
|
#
|
||||||
|
def ==(other)
|
||||||
|
if other.kind_of? IncludedModule
|
||||||
|
super || other.module == @module
|
||||||
|
else
|
||||||
|
super || other == @module
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Object
|
||||||
|
include Kernel
|
||||||
|
end
|
||||||
|
|
||||||
|
module Enumerable; end
|
||||||
|
class Integer; end
|
||||||
|
class IO; end
|
||||||
|
class Numeric; end
|
||||||
|
class String; end
|
||||||
573
gxg/core/argf.rb
Normal file
573
gxg/core/argf.rb
Normal file
|
|
@ -0,0 +1,573 @@
|
||||||
|
module Rubinius
|
||||||
|
|
||||||
|
# :internal:
|
||||||
|
#
|
||||||
|
# The virtual concatenation file of the files given on command line (or
|
||||||
|
# from $stdin if no files were given.)
|
||||||
|
#
|
||||||
|
# The only instance hereof is the object referred to by ARGF.
|
||||||
|
#
|
||||||
|
# @see ARGF
|
||||||
|
#
|
||||||
|
class ARGFClass
|
||||||
|
include Enumerable
|
||||||
|
|
||||||
|
# :internal:
|
||||||
|
#
|
||||||
|
# Create a stateless ARGF.
|
||||||
|
#
|
||||||
|
# The actual setup is done on the fly:
|
||||||
|
#
|
||||||
|
# @see #advance!
|
||||||
|
#
|
||||||
|
def initialize
|
||||||
|
@lineno = 0
|
||||||
|
@advance = true
|
||||||
|
@init = false
|
||||||
|
@use_stdin_only = false
|
||||||
|
@encoding_args = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
#
|
||||||
|
# Set stream into binary mode.
|
||||||
|
#
|
||||||
|
# Stream is set into binary mode, i.e. 8-bit ASCII.
|
||||||
|
# Once set, the binary mode cannot be undone. Returns
|
||||||
|
# self.
|
||||||
|
#
|
||||||
|
def binmode
|
||||||
|
@binmode = true
|
||||||
|
@external = Encoding::ASCII_8BIT
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def binmode?
|
||||||
|
@binmode
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Close stream.
|
||||||
|
#
|
||||||
|
def close
|
||||||
|
advance!
|
||||||
|
@stream.close
|
||||||
|
@advance = true unless @use_stdin_only
|
||||||
|
@lineno = 0
|
||||||
|
@binmode = false
|
||||||
|
@external = nil
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# True if the stream is closed.
|
||||||
|
#
|
||||||
|
def closed?
|
||||||
|
advance!
|
||||||
|
@stream.closed?
|
||||||
|
end
|
||||||
|
|
||||||
|
def default_value
|
||||||
|
"".encode(encoding)
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Linewise iteration.
|
||||||
|
#
|
||||||
|
# Yields one line from stream at a time, as given by
|
||||||
|
# #gets. An Enumerator is returned if no block is
|
||||||
|
# provided. Returns nil if no content, self otherwise.
|
||||||
|
#
|
||||||
|
# @see #gets.
|
||||||
|
#
|
||||||
|
def each_line(sep=$/)
|
||||||
|
return to_enum :each_line, sep unless block_given?
|
||||||
|
return nil unless advance!
|
||||||
|
|
||||||
|
while line = gets(sep)
|
||||||
|
yield line
|
||||||
|
end
|
||||||
|
self
|
||||||
|
end
|
||||||
|
alias_method :lines, :each_line
|
||||||
|
alias_method :each, :each_line
|
||||||
|
|
||||||
|
#
|
||||||
|
# Bytewise iteration.
|
||||||
|
#
|
||||||
|
# Yields one byte at a time from stream, an Integer
|
||||||
|
# as given by #getc. An Enumerator is returned if no
|
||||||
|
# block is provided. Returns self.
|
||||||
|
#
|
||||||
|
# @see #getc
|
||||||
|
#
|
||||||
|
def each_byte
|
||||||
|
return to_enum :each_byte unless block_given?
|
||||||
|
while ch = getbyte()
|
||||||
|
yield ch
|
||||||
|
end
|
||||||
|
self
|
||||||
|
end
|
||||||
|
alias_method :bytes, :each_byte
|
||||||
|
|
||||||
|
#
|
||||||
|
# Character-wise iteration.
|
||||||
|
#
|
||||||
|
# Yields one character at a time from stream. An
|
||||||
|
# Enumerator is returned if no block is provided.
|
||||||
|
# Returns self.
|
||||||
|
#
|
||||||
|
# The characters yielded are gotten from #getc.
|
||||||
|
#
|
||||||
|
# @see #getc
|
||||||
|
#
|
||||||
|
def each_char
|
||||||
|
return to_enum :each_char unless block_given?
|
||||||
|
while c = getc()
|
||||||
|
yield c.chr
|
||||||
|
end
|
||||||
|
self
|
||||||
|
end
|
||||||
|
alias_method :chars, :each_char
|
||||||
|
|
||||||
|
def each_codepoint
|
||||||
|
return to_enum :each_codepoint unless block_given?
|
||||||
|
|
||||||
|
while c = getc
|
||||||
|
yield c.ord
|
||||||
|
end
|
||||||
|
|
||||||
|
self
|
||||||
|
end
|
||||||
|
alias_method :codepoints, :each_codepoint
|
||||||
|
|
||||||
|
def encoding
|
||||||
|
@external || Encoding.default_external
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Query whether stream is at end-of-file.
|
||||||
|
#
|
||||||
|
# True if there is a stream and it is in EOF
|
||||||
|
# status.
|
||||||
|
#
|
||||||
|
def eof?
|
||||||
|
@stream and @stream.eof?
|
||||||
|
end
|
||||||
|
alias_method :eof, :eof?
|
||||||
|
|
||||||
|
#
|
||||||
|
# File descriptor number for stream.
|
||||||
|
#
|
||||||
|
# Returns a file descriptor number for the stream being
|
||||||
|
# read out of.
|
||||||
|
#
|
||||||
|
# @todo Check correctness, does this imply there may be
|
||||||
|
# multiple FDs and if so, is this correct? --rue
|
||||||
|
#
|
||||||
|
def fileno
|
||||||
|
raise ArgumentError, "No stream" unless advance!
|
||||||
|
@stream.fileno
|
||||||
|
end
|
||||||
|
alias_method :to_i, :fileno
|
||||||
|
|
||||||
|
#
|
||||||
|
# File path currently in use.
|
||||||
|
#
|
||||||
|
# Path to file from which read currently is
|
||||||
|
# occurring, or an indication that the stream
|
||||||
|
# is STDIN.
|
||||||
|
#
|
||||||
|
def filename
|
||||||
|
advance!
|
||||||
|
@filename
|
||||||
|
end
|
||||||
|
alias_method :path, :filename
|
||||||
|
|
||||||
|
#
|
||||||
|
# Current stream object.
|
||||||
|
#
|
||||||
|
# This may change during the course of execution,
|
||||||
|
# but is the current one!
|
||||||
|
#
|
||||||
|
def file
|
||||||
|
advance!
|
||||||
|
@stream
|
||||||
|
end
|
||||||
|
|
||||||
|
def getbyte
|
||||||
|
while true
|
||||||
|
return nil unless advance!
|
||||||
|
if val = @stream.getbyte
|
||||||
|
return val
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil if @use_stdin_only
|
||||||
|
@stream.close unless @stream.closed?
|
||||||
|
@advance = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Return one character from stream.
|
||||||
|
#
|
||||||
|
# If a character cannot be returned and we are
|
||||||
|
# reading from a file, the stream is closed.
|
||||||
|
#
|
||||||
|
def getc
|
||||||
|
while true
|
||||||
|
return nil unless advance!
|
||||||
|
if val = @stream.getc
|
||||||
|
return val
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil if @use_stdin_only
|
||||||
|
@stream.close unless @stream.closed?
|
||||||
|
@advance = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Return next line of text from stream.
|
||||||
|
#
|
||||||
|
# If a line cannot be returned and we are
|
||||||
|
# reading from a file, the stream is closed.
|
||||||
|
#
|
||||||
|
# The mechanism does track the line numbers,
|
||||||
|
# and updates $. accordingly.
|
||||||
|
#
|
||||||
|
def gets(sep=$/)
|
||||||
|
while true
|
||||||
|
return nil unless advance!
|
||||||
|
line = @stream.gets(sep)
|
||||||
|
|
||||||
|
unless line
|
||||||
|
return nil if @use_stdin_only
|
||||||
|
@stream.close unless @stream.closed?
|
||||||
|
@advance = true
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
@lineno += 1
|
||||||
|
$. = @lineno
|
||||||
|
return line
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Return current line number.
|
||||||
|
#
|
||||||
|
# Line numbers are maintained when using the linewise
|
||||||
|
# access methods.
|
||||||
|
#
|
||||||
|
# @see #gets
|
||||||
|
# @see #each_line
|
||||||
|
#
|
||||||
|
attr_reader :lineno
|
||||||
|
|
||||||
|
#
|
||||||
|
# Set current line number.
|
||||||
|
#
|
||||||
|
# Also sets $. accordingly.
|
||||||
|
#
|
||||||
|
# @todo Should this be public? --rue
|
||||||
|
#
|
||||||
|
def lineno=(val)
|
||||||
|
$. = @lineno = val
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Return stream position for seeking etc.
|
||||||
|
#
|
||||||
|
# @see IO#pos.
|
||||||
|
#
|
||||||
|
def pos
|
||||||
|
raise ArgumentError, "no stream" unless advance!
|
||||||
|
@stream.tell
|
||||||
|
end
|
||||||
|
alias_method :tell, :pos
|
||||||
|
|
||||||
|
#
|
||||||
|
# Set stream position to a previously obtained position.
|
||||||
|
#
|
||||||
|
# @see IO#pos=
|
||||||
|
#
|
||||||
|
def pos=(position)
|
||||||
|
raise ArgumentError, "no stream" unless advance!
|
||||||
|
@stream.pos = position
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Read a byte from stream.
|
||||||
|
#
|
||||||
|
# Similar to #getc, but raises an EOFError if
|
||||||
|
# EOF has been reached.
|
||||||
|
#
|
||||||
|
# @see #getc
|
||||||
|
#
|
||||||
|
def readbyte
|
||||||
|
advance!
|
||||||
|
|
||||||
|
if val = getc()
|
||||||
|
return val
|
||||||
|
end
|
||||||
|
|
||||||
|
raise EOFError, "ARGF at end"
|
||||||
|
end
|
||||||
|
alias_method :readchar, :readbyte
|
||||||
|
|
||||||
|
#
|
||||||
|
# Read number of bytes or all, optionally into buffer.
|
||||||
|
#
|
||||||
|
# If number of bytes is not given or is nil, tries to read
|
||||||
|
# all of the stream, which is then closed. If the number is
|
||||||
|
# specified, then at most that many bytes will be read.
|
||||||
|
#
|
||||||
|
# A buffer responding to #<< may be provided as the second
|
||||||
|
# argument. The data read is pushed into it. If no buffer
|
||||||
|
# is provided, as by default, a String with the data is
|
||||||
|
# returned instead.
|
||||||
|
#
|
||||||
|
def read(bytes=nil, output=nil)
|
||||||
|
# The user might try to pass in nil, so we have to check here
|
||||||
|
output ||= default_value
|
||||||
|
output.clear
|
||||||
|
|
||||||
|
if bytes
|
||||||
|
bytes_left = bytes
|
||||||
|
|
||||||
|
until bytes_left == 0
|
||||||
|
return output unless advance!
|
||||||
|
|
||||||
|
if res = @stream.read(bytes_left)
|
||||||
|
output << res
|
||||||
|
bytes_left -= res.size
|
||||||
|
else
|
||||||
|
break if @use_stdin_only
|
||||||
|
@stream.close unless @stream.closed?
|
||||||
|
@advance = true
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
|
||||||
|
while advance!
|
||||||
|
output << @stream.read
|
||||||
|
|
||||||
|
break if @use_stdin_only
|
||||||
|
@stream.close unless @stream.closed?
|
||||||
|
@advance = true
|
||||||
|
end
|
||||||
|
|
||||||
|
output
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_nonblock(maxlen, output = nil, exception: true)
|
||||||
|
output ||= default_value
|
||||||
|
|
||||||
|
unless advance!
|
||||||
|
output.clear
|
||||||
|
raise EOFError, "ARGF at end"
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
out = @stream.read_nonblock(maxlen, output, exception: exception)
|
||||||
|
|
||||||
|
return out if out == :wait_readable
|
||||||
|
rescue EOFError => e
|
||||||
|
raise e if @use_stdin_only
|
||||||
|
|
||||||
|
@stream.close
|
||||||
|
@advance = true
|
||||||
|
advance! or raise e
|
||||||
|
end
|
||||||
|
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Read next line of text.
|
||||||
|
#
|
||||||
|
# As #gets, but an EOFError is raised if the stream
|
||||||
|
# is at EOF.
|
||||||
|
#
|
||||||
|
# @see #gets
|
||||||
|
#
|
||||||
|
def readline(sep=$/)
|
||||||
|
raise EOFError, "ARGF at end" unless advance!
|
||||||
|
|
||||||
|
if line = gets(sep)
|
||||||
|
return line
|
||||||
|
end
|
||||||
|
|
||||||
|
raise EOFError, "ARGF at end"
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Read all lines from stream.
|
||||||
|
#
|
||||||
|
# Reads all lines into an Array using #gets and
|
||||||
|
# returns the Array.
|
||||||
|
#
|
||||||
|
# @see #gets
|
||||||
|
#
|
||||||
|
def readlines(sep=$/)
|
||||||
|
return [] unless advance!
|
||||||
|
|
||||||
|
lines = []
|
||||||
|
while line = gets(sep)
|
||||||
|
lines << line
|
||||||
|
end
|
||||||
|
|
||||||
|
lines
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :to_a, :readlines
|
||||||
|
|
||||||
|
def readpartial(maxlen, output=nil)
|
||||||
|
output ||= default_value
|
||||||
|
|
||||||
|
unless advance!
|
||||||
|
output.clear
|
||||||
|
raise EOFError, "ARGF at end"
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
@stream.readpartial(maxlen, output)
|
||||||
|
rescue EOFError => e
|
||||||
|
raise e if @use_stdin_only
|
||||||
|
|
||||||
|
@stream.close
|
||||||
|
@advance = true
|
||||||
|
advance! or raise e
|
||||||
|
end
|
||||||
|
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Rewind the stream to its beginning.
|
||||||
|
#
|
||||||
|
# Line number is updated accordingly.
|
||||||
|
#
|
||||||
|
# @todo Is this correct, only current stream is rewound? --rue
|
||||||
|
#
|
||||||
|
def rewind
|
||||||
|
raise ArgumentError, "no stream to rewind" unless advance!
|
||||||
|
@lineno -= @stream.lineno
|
||||||
|
@stream.rewind
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Seek into a previous position in the stream.
|
||||||
|
#
|
||||||
|
# @see IO#seek.
|
||||||
|
#
|
||||||
|
def seek(*args)
|
||||||
|
raise ArgumentError, "no stream" unless advance!
|
||||||
|
@stream.seek(*args)
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_encoding(*args)
|
||||||
|
@encoding_args = args
|
||||||
|
if @stream and !@stream.closed?
|
||||||
|
@stream.set_encoding *args
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Close file stream and return self.
|
||||||
|
#
|
||||||
|
# STDIN is not closed if being used, otherwise the
|
||||||
|
# stream gets closed. Returns self.
|
||||||
|
#
|
||||||
|
def skip
|
||||||
|
return self if @use_stdin_only
|
||||||
|
@stream.close unless @stream.closed?
|
||||||
|
@advance = true
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def stream(file)
|
||||||
|
stream = file == "-" ? STDIN : File.open(file, "r", :external_encoding => encoding)
|
||||||
|
|
||||||
|
if @encoding_args
|
||||||
|
stream.set_encoding *@encoding_args
|
||||||
|
elsif encoding
|
||||||
|
stream.set_encoding encoding
|
||||||
|
end
|
||||||
|
|
||||||
|
stream
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Return IO object for current stream.
|
||||||
|
#
|
||||||
|
# @see IO#to_io
|
||||||
|
#
|
||||||
|
def to_io
|
||||||
|
advance!
|
||||||
|
@stream.to_io
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Returns "ARGF" as the string representation of this object.
|
||||||
|
#
|
||||||
|
def to_s
|
||||||
|
"ARGF"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Internals
|
||||||
|
|
||||||
|
#
|
||||||
|
# Main processing.
|
||||||
|
#
|
||||||
|
# If not initialised yet, sets the object up on either
|
||||||
|
# first of provided file names or STDIN.
|
||||||
|
#
|
||||||
|
# Does nothing further or later if using STDIN, but if
|
||||||
|
# there are further file names in ARGV, tries to open
|
||||||
|
# the next one as the current stream.
|
||||||
|
#
|
||||||
|
def advance!
|
||||||
|
return true unless @advance
|
||||||
|
|
||||||
|
unless @init
|
||||||
|
|
||||||
|
if ARGV.empty?
|
||||||
|
@advance = false
|
||||||
|
@stream = STDIN
|
||||||
|
@filename = "-"
|
||||||
|
@use_stdin_only = true
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
@init = true
|
||||||
|
end
|
||||||
|
|
||||||
|
File.unlink(@backup_filename) if @backup_filename && $-i == ""
|
||||||
|
|
||||||
|
return false if @use_stdin_only || ARGV.empty?
|
||||||
|
|
||||||
|
@advance = false
|
||||||
|
|
||||||
|
file = ARGV.shift
|
||||||
|
@stream = stream(file)
|
||||||
|
@filename = file
|
||||||
|
|
||||||
|
if $-i && @stream != STDIN
|
||||||
|
backup_extension = $-i == "" ? ".bak" : $-i
|
||||||
|
@backup_filename = "#{@filename}#{backup_extension}"
|
||||||
|
File.rename(@filename, @backup_filename)
|
||||||
|
@stream = File.open(@backup_filename, "r")
|
||||||
|
$stdout = File.open(@filename, "w")
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
private :advance!
|
||||||
|
end
|
||||||
|
end
|
||||||
2324
gxg/core/array.rb
Normal file
2324
gxg/core/array.rb
Normal file
File diff suppressed because it is too large
Load diff
96
gxg/core/array_mirror.rb
Normal file
96
gxg/core/array_mirror.rb
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
module Rubinius
|
||||||
|
class Mirror
|
||||||
|
class Array < Mirror
|
||||||
|
self.subject = ::Array
|
||||||
|
|
||||||
|
def self.reflect(object)
|
||||||
|
m = super(object)
|
||||||
|
|
||||||
|
if Rubinius::Type.object_kind_of? m.object, ::Array
|
||||||
|
m
|
||||||
|
elsif ary = Rubinius::Type.try_convert(m.object, ::Array, :to_ary)
|
||||||
|
super(ary)
|
||||||
|
else
|
||||||
|
message = "expected Array, given #{Rubinius::Type.object_class(object)}"
|
||||||
|
raise TypeError, message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: implement mirror_attribute :name
|
||||||
|
def total
|
||||||
|
Rubinius.invoke_primitive :object_get_ivar, @object, :@total
|
||||||
|
end
|
||||||
|
|
||||||
|
def total=(value)
|
||||||
|
Rubinius.invoke_primitive :object_set_ivar, @object, :@total, value
|
||||||
|
end
|
||||||
|
|
||||||
|
def tuple
|
||||||
|
Rubinius.invoke_primitive :object_get_ivar, @object, :@tuple
|
||||||
|
end
|
||||||
|
|
||||||
|
def tuple=(value)
|
||||||
|
Rubinius.invoke_primitive :object_set_ivar, @object, :@tuple, value
|
||||||
|
end
|
||||||
|
|
||||||
|
def start
|
||||||
|
Rubinius.invoke_primitive :object_get_ivar, @object, :@start
|
||||||
|
end
|
||||||
|
|
||||||
|
def start=(value)
|
||||||
|
Rubinius.invoke_primitive :object_set_ivar, @object, :@start, value
|
||||||
|
end
|
||||||
|
|
||||||
|
# Implementation based on the binomial_coefficient
|
||||||
|
# implemented on MRI array.c.
|
||||||
|
def binomial_coefficient(comb, size)
|
||||||
|
comb = size-comb if (comb > size-comb)
|
||||||
|
return 0 if comb < 0
|
||||||
|
descending_factorial(size, comb) / descending_factorial(comb, comb)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Implementation based on the rb_ary_combination_size
|
||||||
|
# implemented on MRI array.c.
|
||||||
|
def combination_size(num)
|
||||||
|
binomial_coefficient(num, @object.size)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Implementation based on the descending_factorial
|
||||||
|
# implemented on MRI array.c.
|
||||||
|
# Comment from there:
|
||||||
|
# Returns the product of from, from-1, ..., from - how_many + 1.
|
||||||
|
# http://en.wikipedia.org/wiki/Pochhammer_symbol
|
||||||
|
def descending_factorial(from, how_many)
|
||||||
|
cnt = how_many >= 0 ? 1 : 0
|
||||||
|
while (how_many) > 0
|
||||||
|
cnt = cnt*(from)
|
||||||
|
from -= 1
|
||||||
|
how_many -= 1
|
||||||
|
end
|
||||||
|
cnt
|
||||||
|
end
|
||||||
|
|
||||||
|
# Implementation based on the rb_ary_permutation_size
|
||||||
|
# implemented on MRI array.c.
|
||||||
|
def permutation_size(num)
|
||||||
|
n = @object.size
|
||||||
|
if undefined.equal? num
|
||||||
|
k = @object.size
|
||||||
|
else
|
||||||
|
k = Rubinius::Type.coerce_to_collection_index num
|
||||||
|
end
|
||||||
|
descending_factorial(n, k)
|
||||||
|
end
|
||||||
|
|
||||||
|
def repeated_combination_size(combination_size)
|
||||||
|
return 1 if combination_size == 0
|
||||||
|
return binomial_coefficient(combination_size, @object.size + combination_size - 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def repeated_permutation_size(combination_size)
|
||||||
|
return 0 if combination_size < 0
|
||||||
|
@object.size ** combination_size
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
43
gxg/core/atomic.rb
Normal file
43
gxg/core/atomic.rb
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
module Rubinius
|
||||||
|
class AtomicReference
|
||||||
|
def initialize(val=nil)
|
||||||
|
set(val) unless val.nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def marshal_dump
|
||||||
|
get
|
||||||
|
end
|
||||||
|
|
||||||
|
def marshal_load(val)
|
||||||
|
set(val)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get
|
||||||
|
Rubinius.primitive :atomic_get
|
||||||
|
raise PrimitiveFailure, "Rubinius::AtomicReference#get primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :value, :get
|
||||||
|
|
||||||
|
def set(val)
|
||||||
|
Rubinius.primitive :atomic_set
|
||||||
|
raise PrimitiveFailure, "Rubinius::AtomicReference#set primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :value=, :set
|
||||||
|
|
||||||
|
def compare_and_set(old, new)
|
||||||
|
Rubinius.primitive :atomic_compare_and_set
|
||||||
|
raise PrimitiveFailure, "Rubinius::AtomicReference#compare_and_set primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_and_set(new)
|
||||||
|
while true
|
||||||
|
val = get
|
||||||
|
return val if compare_and_set(val, new)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
119
gxg/core/autoload.rb
Normal file
119
gxg/core/autoload.rb
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
##
|
||||||
|
# Used to implement Module#autoload.
|
||||||
|
|
||||||
|
class Autoload
|
||||||
|
attr_reader :name
|
||||||
|
attr_reader :scope
|
||||||
|
attr_reader :path
|
||||||
|
attr_reader :constant
|
||||||
|
|
||||||
|
attr_accessor :loaded
|
||||||
|
attr_accessor :loading
|
||||||
|
|
||||||
|
def self.allocate
|
||||||
|
Rubinius.primitive :autoload_allocate
|
||||||
|
raise PrimtiveFailure, "Autoload.allocate primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(name, scope, path)
|
||||||
|
@name = name
|
||||||
|
@scope = scope
|
||||||
|
@path = path
|
||||||
|
@constant = undefined
|
||||||
|
@loading = false
|
||||||
|
@loaded = false
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
"#<#{self.class.name}:0x#{object_id.to_s(16)} name=#{@name} scope=#{@scope}" \
|
||||||
|
" path=#{@path} constant=#{@constant} loading=#{@loading} loaded=#{@loaded}>"
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :to_s, :inspect
|
||||||
|
|
||||||
|
def loading?
|
||||||
|
Rubinius.synchronize(self) { @loading }
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
#
|
||||||
|
# Change the file to autoload. Used by Module#autoload
|
||||||
|
def set_path(path)
|
||||||
|
@path = path
|
||||||
|
@loading = false
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# When any code that finds a constant sees an instance of Autoload as its match,
|
||||||
|
# it calls this method on us
|
||||||
|
def call(under, honor_require=false)
|
||||||
|
return constant unless undefined.equal? constant
|
||||||
|
|
||||||
|
if resolve or not honor_require
|
||||||
|
find_const under
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def resolve
|
||||||
|
# The protocol that MRI defines for resolving an Autoload instance
|
||||||
|
# requires that the constant table entry be removed _during_ the load so
|
||||||
|
# that Module#const_defined? returns false and defined?() returns nil for
|
||||||
|
# the constant that triggered the load.
|
||||||
|
|
||||||
|
Rubinius.synchronize(self) do
|
||||||
|
unless @loaded or @loading
|
||||||
|
@loading = true
|
||||||
|
|
||||||
|
result = Rubinius::CodeLoader.new(@path).require
|
||||||
|
|
||||||
|
@loaded = true if result
|
||||||
|
@loading = false
|
||||||
|
|
||||||
|
result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_const(under)
|
||||||
|
current = under
|
||||||
|
constant = undefined
|
||||||
|
|
||||||
|
while current
|
||||||
|
if entry = current.constant_table.lookup(name)
|
||||||
|
constant = entry.constant
|
||||||
|
if constant.equal? self
|
||||||
|
if undefined.equal?(constant.constant)
|
||||||
|
unless Object.constant_table.lookup(name)
|
||||||
|
return under.const_missing(name)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
entry.constant = constant.constant
|
||||||
|
return constant.constant
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return constant
|
||||||
|
end
|
||||||
|
|
||||||
|
current = current.direct_superclass
|
||||||
|
end
|
||||||
|
|
||||||
|
if instance_of?(Module)
|
||||||
|
if entry = Object.constant_table.lookup(name)
|
||||||
|
constant = entry.constant
|
||||||
|
if constant.equal? self
|
||||||
|
if undefined.equal?(constant.constant)
|
||||||
|
return under.const_missing(name)
|
||||||
|
else
|
||||||
|
entry.constant = constant.constant
|
||||||
|
return constant.constant
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return constant
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
under.const_missing(name)
|
||||||
|
end
|
||||||
|
end
|
||||||
163
gxg/core/backtrace.rb
Normal file
163
gxg/core/backtrace.rb
Normal file
|
|
@ -0,0 +1,163 @@
|
||||||
|
##
|
||||||
|
# Contains all logic for gathering and displaying backtraces.
|
||||||
|
|
||||||
|
class Rubinius::Backtrace
|
||||||
|
include Enumerable
|
||||||
|
|
||||||
|
MAX_WIDTH_PERCENTAGE = 45
|
||||||
|
MIN_WIDTH = 20
|
||||||
|
|
||||||
|
# If passed nil, we assume someone forgot to create a backtrace
|
||||||
|
# in the VM.
|
||||||
|
def self.backtrace(locations)
|
||||||
|
locations ? new(locations) : new([Rubinius::Location::Missing.new])
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(locations, width = Rubinius::TERMINAL_WIDTH, min_width = MIN_WIDTH)
|
||||||
|
color_config = Rubinius::Config["rbx.colorize_backtraces"]
|
||||||
|
if ENV['RBX_NOCOLOR'] or color_config == "no" or color_config == "NO"
|
||||||
|
@colorize = false
|
||||||
|
else
|
||||||
|
@colorize = true
|
||||||
|
end
|
||||||
|
|
||||||
|
@locations = locations
|
||||||
|
@bold = "\033[0;1m"
|
||||||
|
@inline_effect = "\033[0;4m"
|
||||||
|
|
||||||
|
@width = width
|
||||||
|
@min_width = min_width
|
||||||
|
|
||||||
|
@mri_backtrace = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def [](index)
|
||||||
|
@locations[index]
|
||||||
|
end
|
||||||
|
|
||||||
|
def show(sep="\n", show_color=true)
|
||||||
|
show_color = false unless @colorize
|
||||||
|
clear = show_color ? "\033[0m" : ""
|
||||||
|
|
||||||
|
max = 0
|
||||||
|
lines = []
|
||||||
|
last_method = nil
|
||||||
|
last_line = nil
|
||||||
|
last_name = nil
|
||||||
|
times = 0
|
||||||
|
|
||||||
|
@locations.reverse_each do |loc|
|
||||||
|
if loc.name == last_name and loc.method == last_method \
|
||||||
|
and loc.line == last_line
|
||||||
|
times += 1
|
||||||
|
else
|
||||||
|
lines.last[-1] = times if lines.size > 0
|
||||||
|
last_method = loc.method
|
||||||
|
last_line = loc.line
|
||||||
|
last_name = loc.name
|
||||||
|
|
||||||
|
receiver_name = loc.describe_receiver
|
||||||
|
|
||||||
|
if loc.is_block
|
||||||
|
method_name = "{ } in #{loc.describe_method}"
|
||||||
|
else
|
||||||
|
method_name = loc.describe_method
|
||||||
|
end
|
||||||
|
|
||||||
|
size = method_name.size + 4 # additional space needed for {#, .}
|
||||||
|
max = size if size > max
|
||||||
|
|
||||||
|
lines << [receiver_name, method_name, loc, 1]
|
||||||
|
times = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
cwd = Dir.getwd
|
||||||
|
|
||||||
|
str = ""
|
||||||
|
lines.each do |receiver_name, method_name, location, rec_times|
|
||||||
|
next unless location
|
||||||
|
|
||||||
|
full_name = "#{method_name} #{receiver_name[-1]} "
|
||||||
|
|
||||||
|
if show_color
|
||||||
|
receiver_method = "#{@bold}#{full_name}#{receiver_name[0..-2]}#{clear}"
|
||||||
|
else
|
||||||
|
receiver_method = "#{full_name}#{receiver_name[0..-2]}"
|
||||||
|
end
|
||||||
|
|
||||||
|
spaces = max - full_name.size
|
||||||
|
spaces = 0 if spaces < 0
|
||||||
|
|
||||||
|
if show_color and location.inlined?
|
||||||
|
start = " #{' ' * spaces}#{receiver_method} #{@inline_effect}at#{clear}#{color} "
|
||||||
|
else
|
||||||
|
start = " #{' ' * spaces}#{receiver_method} at "
|
||||||
|
end
|
||||||
|
|
||||||
|
# start.size without the escapes
|
||||||
|
start_size = 1 + spaces + receiver_method.size + 4
|
||||||
|
|
||||||
|
pos = location.position cwd
|
||||||
|
|
||||||
|
pos << " (+#{location.ip})"
|
||||||
|
|
||||||
|
if rec_times > 1
|
||||||
|
pos << " (#{rec_times} times)"
|
||||||
|
end
|
||||||
|
|
||||||
|
if location.is_jit and $DEBUG
|
||||||
|
pos << " (jit)"
|
||||||
|
end
|
||||||
|
|
||||||
|
str << " #{start}#{pos}"
|
||||||
|
|
||||||
|
str << sep
|
||||||
|
end
|
||||||
|
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
|
||||||
|
def join(sep)
|
||||||
|
show
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :to_s, :show
|
||||||
|
|
||||||
|
def each
|
||||||
|
@locations.each { |f| yield f }
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.detect_backtrace(obj)
|
||||||
|
if bt = obj.instance_variable_get(:@rbx_backtrace)
|
||||||
|
return bt if bt.same_mri_backtrace?(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_mri
|
||||||
|
return @mri_backtrace if @mri_backtrace
|
||||||
|
|
||||||
|
@mri_backtrace = @locations.map do |loc|
|
||||||
|
"#{loc.position}:in `#{loc.describe_method}'"
|
||||||
|
end
|
||||||
|
|
||||||
|
# A little extra magic, so we can carry the backtrace along and reuse it
|
||||||
|
|
||||||
|
@mri_backtrace.instance_variable_set(:@rbx_backtrace, self)
|
||||||
|
|
||||||
|
return @mri_backtrace
|
||||||
|
end
|
||||||
|
|
||||||
|
def same_mri_backtrace?(obj)
|
||||||
|
currently = @locations.map do |loc|
|
||||||
|
"#{loc.position}:in `#{loc.describe_method}'"
|
||||||
|
end
|
||||||
|
|
||||||
|
currently == obj
|
||||||
|
end
|
||||||
|
end
|
||||||
162
gxg/core/basic_object.rb
Normal file
162
gxg/core/basic_object.rb
Normal file
|
|
@ -0,0 +1,162 @@
|
||||||
|
class BasicObject
|
||||||
|
def initialize
|
||||||
|
# do nothing
|
||||||
|
end
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def equal?(other)
|
||||||
|
Rubinius.primitive :object_equal
|
||||||
|
raise ::PrimitiveFailure, "BasicObject#equal? primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :==, :equal?
|
||||||
|
|
||||||
|
def !
|
||||||
|
Rubinius.invoke_primitive(:object_equal, self, false) ||
|
||||||
|
Rubinius.invoke_primitive(:object_equal, self, nil) ? true : false
|
||||||
|
end
|
||||||
|
|
||||||
|
def !=(other)
|
||||||
|
self == other ? false : true
|
||||||
|
end
|
||||||
|
|
||||||
|
# Send message to object with given arguments.
|
||||||
|
#
|
||||||
|
# Ignores visibility of method, and may therefore be used to
|
||||||
|
# invoke protected or private methods.
|
||||||
|
#
|
||||||
|
# As denoted by the double-underscore, this method must not
|
||||||
|
# be removed or redefined by user code.
|
||||||
|
#
|
||||||
|
def __send__(message, *args)
|
||||||
|
Rubinius.primitive :object_send
|
||||||
|
raise ::PrimitiveFailure, "BasicObject#__send__ primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def __id__
|
||||||
|
Rubinius.primitive :object_id
|
||||||
|
raise ::PrimitiveFailure, "BasicObject#__id__ primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def method_missing(meth, *args)
|
||||||
|
error = ::NoMethodError
|
||||||
|
.new("Unable to send '#{meth}' on instance of BasicObject", receiver: self)
|
||||||
|
|
||||||
|
::Kernel.raise(error)
|
||||||
|
end
|
||||||
|
private :method_missing
|
||||||
|
|
||||||
|
def singleton_method_added(name) end
|
||||||
|
private :singleton_method_added
|
||||||
|
|
||||||
|
def singleton_method_removed(name) end
|
||||||
|
private :singleton_method_removed
|
||||||
|
|
||||||
|
def singleton_method_undefined(name) end
|
||||||
|
private :singleton_method_undefined
|
||||||
|
|
||||||
|
##
|
||||||
|
# :call-seq:
|
||||||
|
# obj.instance_eval(string [, filename [, lineno]] ) => obj
|
||||||
|
# obj.instance_eval { | | block } => obj
|
||||||
|
#
|
||||||
|
# Evaluates a string containing Ruby source code, or the given block, within
|
||||||
|
# the context of the receiver +obj+. In order to set the context, the
|
||||||
|
# variable +self+ is set to +obj+ while the code is executing, giving the
|
||||||
|
# code access to +obj+'s instance variables. In the version of
|
||||||
|
# #instance_eval that takes a +String+, the optional second and third
|
||||||
|
# parameters supply a filename and starting line number that are used when
|
||||||
|
# reporting compilation errors.
|
||||||
|
#
|
||||||
|
# class Klass
|
||||||
|
# def initialize
|
||||||
|
# @secret = 99
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# k = Klass.new
|
||||||
|
# k.instance_eval { @secret } #=> 99
|
||||||
|
|
||||||
|
def instance_eval(string=nil, filename="(eval)", line=1, &prc)
|
||||||
|
if ::ImmediateValue === self
|
||||||
|
sc = nil
|
||||||
|
else
|
||||||
|
sc = ::Rubinius::Type.object_singleton_class(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
if prc
|
||||||
|
if string
|
||||||
|
raise ::ArgumentError, 'cannot pass both a block and a string to evaluate'
|
||||||
|
end
|
||||||
|
|
||||||
|
return prc.ruby_method.call(self) if prc.ruby_method
|
||||||
|
|
||||||
|
# Return a copy of the BlockEnvironment with the receiver set to self
|
||||||
|
env = prc.block
|
||||||
|
|
||||||
|
if sc
|
||||||
|
lexical_scope = env.repoint_scope sc
|
||||||
|
else
|
||||||
|
lexical_scope = env.disable_scope!
|
||||||
|
end
|
||||||
|
|
||||||
|
return env.call_under(self, lexical_scope, true, self)
|
||||||
|
elsif string
|
||||||
|
string = ::Kernel.StringValue(string)
|
||||||
|
|
||||||
|
lexical_scope = ::Rubinius::LexicalScope.of_sender
|
||||||
|
|
||||||
|
if sc
|
||||||
|
lexical_scope = ::Rubinius::LexicalScope.new(sc, lexical_scope)
|
||||||
|
else
|
||||||
|
lexical_scope = lexical_scope.using_disabled_scope
|
||||||
|
end
|
||||||
|
|
||||||
|
binding = ::Binding.setup(::Rubinius::VariableScope.of_sender,
|
||||||
|
::Rubinius::CompiledCode.of_sender,
|
||||||
|
lexical_scope)
|
||||||
|
|
||||||
|
c = ::Rubinius::ToolSets::Runtime::Compiler
|
||||||
|
be = c.construct_block string, binding, filename, line
|
||||||
|
|
||||||
|
be.call_on_instance(self)
|
||||||
|
else
|
||||||
|
raise ::ArgumentError, 'block not supplied'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# :call-seq:
|
||||||
|
# obj.instance_exec(arg, ...) { |var,...| block } => obj
|
||||||
|
#
|
||||||
|
# Executes the given block within the context of the receiver +obj+. In
|
||||||
|
# order to set the context, the variable +self+ is set to +obj+ while the
|
||||||
|
# code is executing, giving the code access to +obj+'s instance variables.
|
||||||
|
#
|
||||||
|
# Arguments are passed as block parameters.
|
||||||
|
#
|
||||||
|
# class Klass
|
||||||
|
# def initialize
|
||||||
|
# @secret = 99
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# k = Klass.new
|
||||||
|
# k.instance_exec(5) { |x| @secret+x } #=> 104
|
||||||
|
|
||||||
|
def instance_exec(*args, &prc)
|
||||||
|
raise ::LocalJumpError, "Missing block" unless block_given?
|
||||||
|
env = prc.block
|
||||||
|
|
||||||
|
return prc.ruby_method.call(*args) if prc.ruby_method
|
||||||
|
|
||||||
|
lexical_scope = env.lexical_scope
|
||||||
|
if ::ImmediateValue === self
|
||||||
|
lexical_scope = lexical_scope.using_disabled_scope
|
||||||
|
else
|
||||||
|
sc = ::Rubinius::Type.object_singleton_class(self)
|
||||||
|
lexical_scope = lexical_scope.using_current_as(sc)
|
||||||
|
end
|
||||||
|
|
||||||
|
return env.call_under(self, lexical_scope, true, *args)
|
||||||
|
end
|
||||||
|
end
|
||||||
116
gxg/core/binding.rb
Normal file
116
gxg/core/binding.rb
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
class Binding
|
||||||
|
attr_accessor :variables
|
||||||
|
attr_accessor :compiled_code
|
||||||
|
attr_accessor :lexical_scope
|
||||||
|
attr_accessor :proc_environment
|
||||||
|
attr_accessor :location
|
||||||
|
attr_accessor :receiver
|
||||||
|
|
||||||
|
def from_proc?
|
||||||
|
@proc_environment
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.self_context(recv, variables)
|
||||||
|
Rubinius::Type.object_equal(recv, Kernel) ? recv : variables.self
|
||||||
|
end
|
||||||
|
private :self_context
|
||||||
|
|
||||||
|
# Create a new Binding object. MRI does not allow .new to be called, so
|
||||||
|
# we used .setup(). Any code can use this as they wish, provided they have
|
||||||
|
# all the right arguments.
|
||||||
|
#
|
||||||
|
# +variables+ is a Rubinius::VariableScope object
|
||||||
|
# +code+ is a Rubinius::CompiledCode object
|
||||||
|
# +lexical_scope+ is a Rubinius::LexicalScope object
|
||||||
|
#
|
||||||
|
# See Kernel#binding in core/eval.rb for a simple example of creating a
|
||||||
|
# Binding object.
|
||||||
|
#
|
||||||
|
def self.setup(variables, code, lexical_scope, recv=nil, location=nil)
|
||||||
|
bind = allocate()
|
||||||
|
|
||||||
|
bind.receiver = self_context(recv, variables)
|
||||||
|
bind.variables = variables
|
||||||
|
bind.compiled_code = code
|
||||||
|
bind.lexical_scope = lexical_scope
|
||||||
|
bind.location = location
|
||||||
|
|
||||||
|
return bind
|
||||||
|
end
|
||||||
|
|
||||||
|
def line_number
|
||||||
|
if proc_environment
|
||||||
|
proc_environment.line
|
||||||
|
else
|
||||||
|
location ? location.line : 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Evaluates the Ruby expression(s) in string, in the binding‘s context.
|
||||||
|
# If the optional filename and lineno parameters are present,
|
||||||
|
# they will be used when reporting syntax errors.
|
||||||
|
def eval(expr, filename=nil, lineno=nil)
|
||||||
|
lineno ||= filename ? 1 : line_number
|
||||||
|
|
||||||
|
Kernel.eval(expr, self, filename, lineno)
|
||||||
|
end
|
||||||
|
|
||||||
|
def local_variables
|
||||||
|
variables.local_variables
|
||||||
|
end
|
||||||
|
|
||||||
|
def local_variable_set(name, value)
|
||||||
|
unless name.is_a?(Symbol)
|
||||||
|
name = Rubinius::Type.coerce_to(name, String, :to_str).to_sym
|
||||||
|
end
|
||||||
|
|
||||||
|
vars = variables
|
||||||
|
|
||||||
|
# If a local variable is defined in a parent scope we should update the
|
||||||
|
# variable in said scope and all child scopes, instead of _only_ setting it
|
||||||
|
# in the current scope.
|
||||||
|
while vars
|
||||||
|
meth = vars.method
|
||||||
|
|
||||||
|
if meth.local_names.include?(name)
|
||||||
|
return vars.set_local(meth.local_slot(name), value)
|
||||||
|
elsif vars.eval_local_defined?(name)
|
||||||
|
return vars.set_eval_local(name, value)
|
||||||
|
end
|
||||||
|
|
||||||
|
vars = vars.parent
|
||||||
|
end
|
||||||
|
|
||||||
|
variables.set_eval_local(name, value)
|
||||||
|
end
|
||||||
|
|
||||||
|
def local_variable_get(name)
|
||||||
|
unless name.is_a?(Symbol)
|
||||||
|
name = Rubinius::Type.coerce_to(name, String, :to_str).to_sym
|
||||||
|
end
|
||||||
|
|
||||||
|
vars = variables
|
||||||
|
|
||||||
|
while vars
|
||||||
|
meth = vars.method
|
||||||
|
|
||||||
|
if meth.local_names.include?(name)
|
||||||
|
return vars.locals[meth.local_slot(name)]
|
||||||
|
elsif vars.eval_local_defined?(name)
|
||||||
|
return vars.get_eval_local(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
vars = vars.parent
|
||||||
|
end
|
||||||
|
|
||||||
|
raise NameErrror, "local variable #{name.inspect} not defined for #{inspect}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def local_variable_defined?(name)
|
||||||
|
unless name.is_a?(Symbol)
|
||||||
|
name = Rubinius::Type.coerce_to(name, String, :to_str).to_sym
|
||||||
|
end
|
||||||
|
|
||||||
|
variables.local_defined?(name)
|
||||||
|
end
|
||||||
|
end
|
||||||
183
gxg/core/block_environment.rb
Normal file
183
gxg/core/block_environment.rb
Normal file
|
|
@ -0,0 +1,183 @@
|
||||||
|
##
|
||||||
|
# Describes the environment a block was created in. BlockEnvironment is used
|
||||||
|
# to create a BlockContext.
|
||||||
|
|
||||||
|
module Rubinius
|
||||||
|
class BlockEnvironment
|
||||||
|
attr_reader :scope
|
||||||
|
attr_reader :top_scope
|
||||||
|
attr_reader :module
|
||||||
|
|
||||||
|
# The CompiledCode that implements the code for the block
|
||||||
|
attr_reader :compiled_code
|
||||||
|
attr_reader :lexical_scope
|
||||||
|
|
||||||
|
attr_accessor :proc_environment
|
||||||
|
|
||||||
|
def self.allocate
|
||||||
|
Rubinius.primitive :blockenvironment_allocate
|
||||||
|
raise PrimitiveFailure, "BlockEnvironment.allocate primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(*args)
|
||||||
|
Rubinius.primitive :block_call
|
||||||
|
raise PrimitiveFailure, "BlockEnvironment#call primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def call_under(recv, lexical_scope, visibility_scope, *args)
|
||||||
|
Rubinius.primitive :block_call_under
|
||||||
|
raise PrimitiveFailure, "BlockEnvironment#call_under primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.of_sender
|
||||||
|
Rubinius.primitive :block_env_of_sender
|
||||||
|
raise PrimitiveFailure, "BlockEnvironment#of_sender primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
class AsMethod < Executable
|
||||||
|
attr_reader :block_env
|
||||||
|
|
||||||
|
def self.new(block_env)
|
||||||
|
Rubinius.primitive :block_as_method_create
|
||||||
|
raise PrimitiveFailure, "BlockEnvironment::AsMethod.new primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def ==(other)
|
||||||
|
# Given the following code:
|
||||||
|
# class Foo
|
||||||
|
# p = Proc.new { :cool }
|
||||||
|
# define_method :a, p
|
||||||
|
# define_method :a, p
|
||||||
|
# end
|
||||||
|
# foo = Foo.new
|
||||||
|
# foo.method(:a)
|
||||||
|
# foo.method(:b)
|
||||||
|
#
|
||||||
|
# The Method instances for :a and :b have different
|
||||||
|
# BlockEnvironments as define_method dups the BE
|
||||||
|
# when given a Proc.
|
||||||
|
#
|
||||||
|
# The methods are equal if the BEs are equal.
|
||||||
|
return false unless other.kind_of? AsMethod
|
||||||
|
|
||||||
|
@block_env == other.block_env
|
||||||
|
end
|
||||||
|
|
||||||
|
def parameters
|
||||||
|
@block_env.parameters
|
||||||
|
end
|
||||||
|
|
||||||
|
def arity
|
||||||
|
@block_env.compiled_code.arity
|
||||||
|
end
|
||||||
|
|
||||||
|
def local_names
|
||||||
|
@block_env.compiled_code.local_names
|
||||||
|
end
|
||||||
|
|
||||||
|
def required_args
|
||||||
|
@block_env.compiled_code.required_args
|
||||||
|
end
|
||||||
|
|
||||||
|
def total_args
|
||||||
|
@block_env.compiled_code.total_args
|
||||||
|
end
|
||||||
|
|
||||||
|
def post_args
|
||||||
|
@block_env.compiled_code.post_args
|
||||||
|
end
|
||||||
|
|
||||||
|
def splat
|
||||||
|
@block_env.compiled_code.splat
|
||||||
|
end
|
||||||
|
|
||||||
|
def block_index
|
||||||
|
@block_env.compiled_code.block_index
|
||||||
|
end
|
||||||
|
|
||||||
|
def file
|
||||||
|
@block_env.file
|
||||||
|
end
|
||||||
|
|
||||||
|
def defined_line
|
||||||
|
@block_env.line
|
||||||
|
end
|
||||||
|
|
||||||
|
def scope
|
||||||
|
@block_env.scope
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def from_proc?
|
||||||
|
@proc_environment
|
||||||
|
end
|
||||||
|
|
||||||
|
def under_context(scope, code)
|
||||||
|
@top_scope = scope
|
||||||
|
@scope = scope
|
||||||
|
@compiled_code = code
|
||||||
|
@lexical_scope = code.scope
|
||||||
|
@module = code.scope.module
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
def change_name(name)
|
||||||
|
@compiled_code = @compiled_code.change_name name
|
||||||
|
end
|
||||||
|
|
||||||
|
def repoint_scope(where)
|
||||||
|
@lexical_scope.using_current_as where
|
||||||
|
end
|
||||||
|
|
||||||
|
def disable_scope!
|
||||||
|
@lexical_scope.using_disabled_scope
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_binding
|
||||||
|
Binding.setup @scope, @compiled_code, @lexical_scope
|
||||||
|
end
|
||||||
|
|
||||||
|
def make_independent
|
||||||
|
@top_scope = @top_scope.dup
|
||||||
|
@scope = @scope.dup
|
||||||
|
end
|
||||||
|
|
||||||
|
def call_on_instance(obj, *args)
|
||||||
|
call_under obj, @lexical_scope, false, *args
|
||||||
|
end
|
||||||
|
|
||||||
|
def arity
|
||||||
|
@compiled_code.arity
|
||||||
|
end
|
||||||
|
|
||||||
|
def parameters
|
||||||
|
@compiled_code.parameters
|
||||||
|
end
|
||||||
|
|
||||||
|
def file
|
||||||
|
@compiled_code.file
|
||||||
|
end
|
||||||
|
|
||||||
|
def line
|
||||||
|
@compiled_code.defined_line
|
||||||
|
end
|
||||||
|
|
||||||
|
def source_location
|
||||||
|
[file.to_s, line]
|
||||||
|
end
|
||||||
|
|
||||||
|
def ==(other)
|
||||||
|
return false unless other.kind_of? BlockEnvironment
|
||||||
|
|
||||||
|
@top_scope == other.top_scope and
|
||||||
|
@scope == other.scope and
|
||||||
|
@module == other.module and
|
||||||
|
@compiled_code.equivalent_body?(other.compiled_code)
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
"#<#{self.class.name}:0x#{self.object_id.to_s(16)} scope=#{@scope.inspect} top_scope=#{@top_scope.inspect} module=#{@module.inspect} compiled_code=#{@compiled_code.inspect} lexical_scope=#{@lexical_scope.inspect}>"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
127
gxg/core/byte_array.rb
Normal file
127
gxg/core/byte_array.rb
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
##
|
||||||
|
# An array of bytes, used as a low-level data store for implementing various
|
||||||
|
# other classes.
|
||||||
|
module Rubinius
|
||||||
|
class ByteArray
|
||||||
|
def self.allocate
|
||||||
|
raise TypeError, "ByteArray cannot be created via allocate()"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.allocate_sized(cnt)
|
||||||
|
Rubinius.primitive :bytearray_allocate
|
||||||
|
raise PrimitiveFailure, "ByteArray#allocate primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.new(cnt)
|
||||||
|
obj = allocate_sized cnt
|
||||||
|
Rubinius.asm(obj) do |obj|
|
||||||
|
run obj
|
||||||
|
send :initialize, 0, true
|
||||||
|
end
|
||||||
|
|
||||||
|
obj
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_bytes(start, count)
|
||||||
|
Rubinius.primitive :bytearray_fetch_bytes
|
||||||
|
raise PrimitiveFailure, "ByteArray#fetch_bytes primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def move_bytes(start, count, dest)
|
||||||
|
Rubinius.primitive :bytearray_move_bytes
|
||||||
|
raise ArgumentError, "ByteArray#move_bytes primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_byte(index)
|
||||||
|
Rubinius.primitive :bytearray_get_byte
|
||||||
|
raise PrimitiveFailure, "ByteArray#get_byte primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :[], :get_byte
|
||||||
|
|
||||||
|
def set_byte(index, value)
|
||||||
|
Rubinius.primitive :bytearray_set_byte
|
||||||
|
raise PrimitiveFailure, "ByteArray#set_byte primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :[]=, :set_byte
|
||||||
|
|
||||||
|
def compare_bytes(other, a, b)
|
||||||
|
Rubinius.primitive :bytearray_compare_bytes
|
||||||
|
raise PrimitiveFailure, "ByteArray#compare_bytes primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def size
|
||||||
|
Rubinius.primitive :bytearray_size
|
||||||
|
raise PrimitiveFailure, "ByteArray#size primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def dup
|
||||||
|
obj = Rubinius::Type.object_class(self).allocate_sized(self.size)
|
||||||
|
|
||||||
|
Rubinius.invoke_primitive :object_copy_object, obj, self
|
||||||
|
|
||||||
|
Rubinius.privately do
|
||||||
|
obj.initialize_copy self
|
||||||
|
end
|
||||||
|
|
||||||
|
return obj
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Searches for +pattern+ in the ByteArray. Returns the number
|
||||||
|
# of characters from the front of the ByteArray to the end
|
||||||
|
# of the pattern if a match is found. Returns Qnil if a match
|
||||||
|
# is not found. Starts searching at index +start+.
|
||||||
|
def locate(pattern, start, max)
|
||||||
|
Rubinius.primitive :bytearray_locate
|
||||||
|
raise PrimitiveFailure, "ByteArray#locate primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return a new ByteArray by taking the bytes from +string+ and +self+
|
||||||
|
# together.
|
||||||
|
def prepend(string)
|
||||||
|
Rubinius.primitive :bytearray_prepend
|
||||||
|
|
||||||
|
if string.kind_of? String
|
||||||
|
raise PrimitiveFailure, "ByteArray#prepend failed"
|
||||||
|
else
|
||||||
|
prepend(StringValue(string))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def reverse(start, total)
|
||||||
|
Rubinius.primitive :bytearray_reverse
|
||||||
|
raise PrimitiveFailure, "ByteArray#reverse primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: encoding
|
||||||
|
def character_at_index(index)
|
||||||
|
"" << self[index]
|
||||||
|
end
|
||||||
|
|
||||||
|
def each
|
||||||
|
i = 0
|
||||||
|
max = size()
|
||||||
|
|
||||||
|
while i < max
|
||||||
|
yield get_byte(i)
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
"#<#{self.class}:0x#{object_id.to_s(16)} #{size} bytes>"
|
||||||
|
end
|
||||||
|
|
||||||
|
def <=>(other)
|
||||||
|
compare_bytes other, size, other.size
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sets the first character to be an ASCII capitalize letter
|
||||||
|
# if it's an ASCII lower case letter
|
||||||
|
def first_capitalize!
|
||||||
|
self[0] = self[0].toupper
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
76
gxg/core/call_site.rb
Normal file
76
gxg/core/call_site.rb
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
module Rubinius
|
||||||
|
class CallSite
|
||||||
|
attr_reader :name
|
||||||
|
attr_reader :cache
|
||||||
|
|
||||||
|
def ip
|
||||||
|
Rubinius.primitive :call_site_ip
|
||||||
|
raise PrimitiveFailure, "CallSite#ip primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def size
|
||||||
|
Rubinius.primitive :call_site_size
|
||||||
|
raise PrimitiveFailure, "CallSite#size primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def invokes
|
||||||
|
Rubinius.primitive :call_site_invokes
|
||||||
|
raise PrimitiveFailure, "CallSite#invokes primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def hits
|
||||||
|
Rubinius.primitive :call_site_hits
|
||||||
|
raise PrimitiveFailure, "CallSite#hits primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def misses
|
||||||
|
Rubinius.primitive :call_site_misses
|
||||||
|
raise PrimitiveFailure, "CallSite#misses primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def evictions
|
||||||
|
Rubinius.primitive :call_site_evictions
|
||||||
|
raise PrimitiveFailure, "CallSite#evictions primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def rotations
|
||||||
|
Rubinius.primitive :call_site_rotations
|
||||||
|
raise PrimitiveFailure, "CallSite#rotations primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def dead_list_size
|
||||||
|
Rubinius.primitive :call_site_dead_list_size
|
||||||
|
raise PrimitiveFailure, "CallSite#dead_list_size primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def cache_entries
|
||||||
|
Rubinius.primitive :call_site_cache_entries
|
||||||
|
raise PrimitiveFailure, "CallSite#caches primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def reset
|
||||||
|
Rubinius.primitive :call_site_reset
|
||||||
|
raise PrimitiveFailure, "CallSite#reset primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
str = "#<#{self.class.name}:0x#{self.object_id.to_s(16)} name=#{@name} ip=#{ip} " \
|
||||||
|
"size=#{size} invokes=#{invokes} hits=#{hits} misses=#{misses} " \
|
||||||
|
"evictions=#{evictions} rotations=#{rotations} dead_list=#{dead_list_size}"
|
||||||
|
|
||||||
|
cache_entries.each do |entry|
|
||||||
|
str << " #<Entry"
|
||||||
|
i = 0
|
||||||
|
while i < entry.size
|
||||||
|
str << " #{entry[i]}=#{entry[i+1]}"
|
||||||
|
i += 2
|
||||||
|
end
|
||||||
|
str << ">"
|
||||||
|
end
|
||||||
|
|
||||||
|
str << ">"
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :to_s, :inspect
|
||||||
|
end
|
||||||
|
end
|
||||||
166
gxg/core/capi.rb
Normal file
166
gxg/core/capi.rb
Normal file
|
|
@ -0,0 +1,166 @@
|
||||||
|
module Rubinius
|
||||||
|
# CAPI contains utility methods used by capi.
|
||||||
|
module CAPI
|
||||||
|
def self.coerce(x, y, error=false)
|
||||||
|
begin
|
||||||
|
ary = y.coerce x
|
||||||
|
rescue
|
||||||
|
if error
|
||||||
|
raise TypeError, "#{y.inspect} can't be coerced into #{x.class}"
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
unless ary.kind_of? Array and ary.size == 2
|
||||||
|
if error
|
||||||
|
raise TypeError, "coerce must return [x, y]"
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return ary
|
||||||
|
end
|
||||||
|
|
||||||
|
# C-API method corresponding to rb_num_coerce_bin
|
||||||
|
def self.rb_num_coerce_bin(x, y, op)
|
||||||
|
a, b = coerce x, y, true
|
||||||
|
|
||||||
|
a.__send__ op, b
|
||||||
|
end
|
||||||
|
|
||||||
|
# C-API method corresponding to rb_num_coerce_cmp
|
||||||
|
def self.rb_num_coerce_cmp(x, y, op)
|
||||||
|
ary = coerce x, y
|
||||||
|
|
||||||
|
return ary.first.__send__ op, ary.last if ary
|
||||||
|
end
|
||||||
|
|
||||||
|
# C-API method corresponding to rb_num_coerce_relop
|
||||||
|
def self.rb_num_coerce_relop(x, y, op)
|
||||||
|
ary = coerce x, y, false
|
||||||
|
|
||||||
|
if ary
|
||||||
|
rel = ary.first.__send__ op, ary.last
|
||||||
|
return rel unless rel.equal? nil
|
||||||
|
end
|
||||||
|
|
||||||
|
raise ArgumentError, "comparison of #{x.class} with #{y.class} failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.sprintf(format, args)
|
||||||
|
::Rubinius::Sprinter.get(format).call(*args)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.rb_str_encode(str, enc, flags, opts)
|
||||||
|
# TODO: set flags from opts
|
||||||
|
if str.encoding == enc
|
||||||
|
str.dup
|
||||||
|
else
|
||||||
|
ec = Encoding::Converter.new str.encoding, enc, flags
|
||||||
|
ec.replacement = opts[:replace] if opts
|
||||||
|
|
||||||
|
ec.convert str
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.rb_str_conv_enc_opts(str, from, to, flags, opts)
|
||||||
|
return str if to.nil?
|
||||||
|
return str if from == to
|
||||||
|
|
||||||
|
if (to.ascii_compatible? and str.ascii_only?) or to == Encoding::ASCII_8BIT
|
||||||
|
if str.encoding != to
|
||||||
|
return str.dup.force_encoding(to)
|
||||||
|
else
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: set flags from opts
|
||||||
|
ec = Encoding::Converter.new from, to, flags
|
||||||
|
ec.replacement = opts[:replace] if opts
|
||||||
|
|
||||||
|
result = ""
|
||||||
|
status = ec.primitive_convert str.dup, result, 0, nil, flags
|
||||||
|
|
||||||
|
return status == :finished ? result : str
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.rb_integer_pack(value, words, numwords, wordsize, nails, flags)
|
||||||
|
digits = INTEGER_PACK_DIGITS
|
||||||
|
numbytes = numwords * wordsize
|
||||||
|
nibble_size = numbytes * 2
|
||||||
|
|
||||||
|
nibbles = value.abs.to_s(16)
|
||||||
|
nibbles = nibbles[-nibble_size..-1] if nibble_size < nibbles.size
|
||||||
|
|
||||||
|
complement = value < 0 && (flags & INTEGER_PACK_2COMP) != 0
|
||||||
|
|
||||||
|
if (ENDIAN == :little and (flags & INTEGER_PACK_BIG_ENDIAN) != 0) or
|
||||||
|
(ENDIAN == :big and (flags & INTEGER_PACK_LITTLE_ENDIAN) != 0)
|
||||||
|
c_index = index = numbytes - 1
|
||||||
|
incr = -1
|
||||||
|
else
|
||||||
|
c_index = index = 0
|
||||||
|
incr = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
i = nibbles.size - 1
|
||||||
|
stop = nibbles.size & 1
|
||||||
|
|
||||||
|
while i > stop
|
||||||
|
high = digits[nibbles.getbyte(i - 1)]
|
||||||
|
low = digits[nibbles.getbyte(i)]
|
||||||
|
byte = (high << 4) | low
|
||||||
|
|
||||||
|
byte ^= 0xff if complement
|
||||||
|
words.setbyte index, byte
|
||||||
|
|
||||||
|
index += incr
|
||||||
|
i -= 2
|
||||||
|
end
|
||||||
|
|
||||||
|
if stop == 1
|
||||||
|
byte = digits[nibbles.getbyte(0)]
|
||||||
|
byte ^= 0xff if complement
|
||||||
|
words.setbyte index, byte
|
||||||
|
end
|
||||||
|
|
||||||
|
if complement
|
||||||
|
c_stop = c_index == 0 ? numbytes : -1
|
||||||
|
|
||||||
|
while c_index != c_stop
|
||||||
|
byte = words.getbyte(c_index) + 1
|
||||||
|
words.setbyte(c_index, byte)
|
||||||
|
|
||||||
|
break if byte <= 0xff
|
||||||
|
|
||||||
|
c_index += incr
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
overflow = 2**(numwords * wordsize * 8)
|
||||||
|
|
||||||
|
if value < 0
|
||||||
|
if complement
|
||||||
|
return -2 if value < -overflow
|
||||||
|
else
|
||||||
|
return -2 if value <= -overflow
|
||||||
|
end
|
||||||
|
return -1
|
||||||
|
else
|
||||||
|
return 2 if value >= overflow
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.rb_block_proc(env)
|
||||||
|
Rubinius::Mirror::Proc.from_block ::Proc, env
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.rb_obj_method_arity(obj, sym)
|
||||||
|
obj.method(sym).arity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
40
gxg/core/channel.rb
Normal file
40
gxg/core/channel.rb
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Channel is a FIFO. If #receive is called and no value is available, the
|
||||||
|
# Thread will sleep until a value is available. If #receive_timeout is called
|
||||||
|
# and no value is availale, the Thread will sleep until a value is available
|
||||||
|
# or duration time has elapsed. If #try_receive is called, it returns
|
||||||
|
# immediately with a value or nil. It's impossible to know whether nil was in
|
||||||
|
# the channel in the case that the return value is nil.
|
||||||
|
module Rubinius
|
||||||
|
class Channel
|
||||||
|
def self.allocate
|
||||||
|
Rubinius.primitive :channel_new
|
||||||
|
raise PrimitiveFailure, "Channel.allocate primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def send(obj)
|
||||||
|
Rubinius.primitive :channel_send
|
||||||
|
raise PrimitiveFailure, "Channel#send primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :<<, :send
|
||||||
|
|
||||||
|
def receive
|
||||||
|
Rubinius.primitive :channel_receive
|
||||||
|
raise PrimitiveFailure, "Channel#receive primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def receive_timeout(duration)
|
||||||
|
Rubinius.primitive :channel_receive_timeout
|
||||||
|
raise PrimitiveFailure, "Channel#receive_timeout primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def try_receive
|
||||||
|
Rubinius.primitive :channel_try_receive
|
||||||
|
raise PrimitiveFailure, "Channel#try_receive primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
"#<Rubinius::Channel:0x#{object_id.to_s(16)}>"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
55
gxg/core/character.rb
Normal file
55
gxg/core/character.rb
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
module Rubinius
|
||||||
|
class Character < ::String
|
||||||
|
def self.__allocate__
|
||||||
|
Rubinius.primitive :character_allocate
|
||||||
|
raise PrimitiveFailure, "Rubinius::Character.allocate primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.allocate
|
||||||
|
chr = __allocate__
|
||||||
|
chr.__data__ = Rubinius::ByteArray.allocate_sized(1)
|
||||||
|
chr.num_bytes = 0
|
||||||
|
chr
|
||||||
|
end
|
||||||
|
|
||||||
|
def alphabetical?
|
||||||
|
Rubinius.primitive :character_alphabetical_p
|
||||||
|
raise PrimitiveFailure, "Rubinius::Character#alphabetical? primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def ascii?
|
||||||
|
Rubinius.primitive :character_ascii_p
|
||||||
|
raise PrimitiveFailure, "Rubinius::Character#ascii? primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def digit?
|
||||||
|
Rubinius.primitive :character_digit_p
|
||||||
|
raise PrimitiveFailure, "Rubinius::Character#digit? primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def lower?
|
||||||
|
Rubinius.primitive :character_lower_p
|
||||||
|
raise PrimitiveFailure, "Rubinius::Character#lower? primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def printable?
|
||||||
|
Rubinius.primitive :character_printable_p
|
||||||
|
raise PrimitiveFailure, "Rubinius::Character#printable? primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def punctuation?
|
||||||
|
Rubinius.primitive :character_punctuation_p
|
||||||
|
raise PrimitiveFailure, "Rubinius::Character#punctuation? primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def space?
|
||||||
|
Rubinius.primitive :character_space_p
|
||||||
|
raise PrimitiveFailure, "Rubinius::Character#space? primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def upper?
|
||||||
|
Rubinius.primitive :character_upper_p
|
||||||
|
raise PrimitiveFailure, "Rubinius::Character#upper? primitive failed"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
67
gxg/core/class.rb
Normal file
67
gxg/core/class.rb
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
class Class
|
||||||
|
def self.allocate
|
||||||
|
Rubinius.primitive :class_s_allocate
|
||||||
|
raise PrimitiveFailure, "Class.allocate primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_superclass(sup)
|
||||||
|
Rubinius.primitive :class_set_superclass
|
||||||
|
kind = Rubinius::Type.object_class(sup)
|
||||||
|
raise TypeError, "Class.set_superclass: argument must be a Class (#{kind} given)"
|
||||||
|
end
|
||||||
|
|
||||||
|
private :set_superclass
|
||||||
|
|
||||||
|
def initialize(sclass=Object, name=nil, under=nil)
|
||||||
|
raise TypeError, "already initialized class" if @instance_type
|
||||||
|
raise TypeError, "can't make subclass of Class" if Class.equal?(sclass)
|
||||||
|
|
||||||
|
set_superclass sclass
|
||||||
|
|
||||||
|
# Things (rails) depend on the fact that a normal class is in the constant
|
||||||
|
# table and have a name BEFORE inherited is run.
|
||||||
|
under.const_set name, self if under
|
||||||
|
|
||||||
|
if sclass
|
||||||
|
Rubinius.privately do
|
||||||
|
sclass.inherited self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
super()
|
||||||
|
end
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
##
|
||||||
|
# Specialized initialize_copy because Class needs additional protection
|
||||||
|
def initialize_copy(other)
|
||||||
|
# The code in super (Module#initialize_copy) will duplicate
|
||||||
|
# the method table. Therefore do this check here to see whether
|
||||||
|
# we've already initialized this class before continuing.
|
||||||
|
unless @method_table == other.method_table
|
||||||
|
raise TypeError, "already initialized class"
|
||||||
|
end
|
||||||
|
|
||||||
|
@module_name = nil
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize_copy
|
||||||
|
|
||||||
|
def inherited(name)
|
||||||
|
end
|
||||||
|
private :inherited
|
||||||
|
|
||||||
|
def superclass
|
||||||
|
cls = direct_superclass
|
||||||
|
unless cls
|
||||||
|
return nil if self == BasicObject
|
||||||
|
raise TypeError, "uninitialized class"
|
||||||
|
end
|
||||||
|
|
||||||
|
while cls and cls.kind_of? Rubinius::IncludedModule
|
||||||
|
cls = cls.direct_superclass
|
||||||
|
end
|
||||||
|
|
||||||
|
return cls
|
||||||
|
end
|
||||||
|
end
|
||||||
41
gxg/core/code_db.rb
Normal file
41
gxg/core/code_db.rb
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
module Rubinius
|
||||||
|
class CodeDB
|
||||||
|
attr_accessor :loaded_features
|
||||||
|
attr_accessor :load_path
|
||||||
|
|
||||||
|
class << self
|
||||||
|
# CodeDB.open is the constructor
|
||||||
|
private :new
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.open(path)
|
||||||
|
Rubinius.primitive :code_db_open
|
||||||
|
raise PrimitiveFailure, "Rubinius::CodeDB.open primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_db
|
||||||
|
Rubinius.primitive :code_db_load_db
|
||||||
|
raise PrimitiveFailure, "Rubinius::CodeDB#load_db primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def close
|
||||||
|
Rubinius.primitive :code_db_close
|
||||||
|
raise PrimitiveFailure, "Rubinius::CodeDB#close primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def load(id)
|
||||||
|
Rubinius.primitive :code_db_load
|
||||||
|
raise PrimitiveFailure, "Rubinius::CodeDB#load primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_feature(stem, ext, reload, record)
|
||||||
|
Rubinius.primitive :code_db_load_feature
|
||||||
|
raise PrimitiveFailure, "Rubinius::CodeDB#load_feature primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def store(code, stem, path, feature, record)
|
||||||
|
Rubinius.primitive :code_db_store
|
||||||
|
raise PrimitiveFailure, "Rubinius::CodeDB#store primitive failed"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
348
gxg/core/code_loader.rb
Normal file
348
gxg/core/code_loader.rb
Normal file
|
|
@ -0,0 +1,348 @@
|
||||||
|
module Rubinius
|
||||||
|
class CodeLoader
|
||||||
|
def self.registry
|
||||||
|
Rubinius.synchronize(self) { @registry ||= [] }
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.loading?(path)
|
||||||
|
feature = nil
|
||||||
|
|
||||||
|
resource = Feature.new path
|
||||||
|
if resource.loadable? or resource.search
|
||||||
|
feature = resource.feature
|
||||||
|
end
|
||||||
|
|
||||||
|
unless feature
|
||||||
|
resource = Library.new path
|
||||||
|
if resource.loadable? or resource.search
|
||||||
|
feature = resource.feature
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if feature
|
||||||
|
Rubinius.synchronize(registry) do
|
||||||
|
return true if registry.include? feature
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
class Resource
|
||||||
|
attr_reader :feature
|
||||||
|
|
||||||
|
def initialize(stem, ext)
|
||||||
|
stem = Type.coerce_to_path stem
|
||||||
|
stem = "#{stem}#{ext}" unless stem.suffix? ext
|
||||||
|
|
||||||
|
@relative = false
|
||||||
|
|
||||||
|
if stem[0] == ?~
|
||||||
|
@prefix = ""
|
||||||
|
stem = File.expand_path stem
|
||||||
|
elsif stem[0] == ?/
|
||||||
|
@prefix = ""
|
||||||
|
elsif stem[0] == ?. and (stem[1] == ?/ or
|
||||||
|
(stem[1] == ?. and stem[2] == ?/))
|
||||||
|
@relative = true
|
||||||
|
@prefix = Dir.pwd
|
||||||
|
else
|
||||||
|
@prefix = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
@stem = stem
|
||||||
|
@feature = nil
|
||||||
|
@record = true
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def resolve
|
||||||
|
return true if loadable?
|
||||||
|
return true if search
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
def search
|
||||||
|
return false if @relative
|
||||||
|
|
||||||
|
$LOAD_PATH.each do |dir|
|
||||||
|
if dir[-1] == ?/
|
||||||
|
@prefix = dir[0...-1]
|
||||||
|
else
|
||||||
|
@prefix = dir
|
||||||
|
end
|
||||||
|
|
||||||
|
return true if loadable?
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
def loadable?
|
||||||
|
feature = "#{@prefix}/#{@stem}"
|
||||||
|
|
||||||
|
stat = File::Stat.stat feature
|
||||||
|
|
||||||
|
if stat and stat.file? and stat.readable?
|
||||||
|
@feature = File.expand_path feature
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def unload_feature
|
||||||
|
Rubinius.synchronize($LOADED_FEATURES) do
|
||||||
|
$LOADED_FEATURES.delete_if { |f| f == @feature }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Source < Resource
|
||||||
|
def initialize(stem, ext)
|
||||||
|
super stem, ext
|
||||||
|
|
||||||
|
@code = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def load
|
||||||
|
if @code
|
||||||
|
@feature = @code.file.to_s
|
||||||
|
else
|
||||||
|
@code = ToolSets::Runtime::Compiler.compile_file @feature
|
||||||
|
CoreDB.store @code, @stem, @prefix, @feature, @record
|
||||||
|
end
|
||||||
|
|
||||||
|
run_script
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
def make_script
|
||||||
|
script = @code.create_script @wrap
|
||||||
|
script.file_path = script.data_path = @code.file.to_s
|
||||||
|
script
|
||||||
|
end
|
||||||
|
|
||||||
|
def run_script
|
||||||
|
make_script
|
||||||
|
Rubinius.run_script @code
|
||||||
|
rescue Object
|
||||||
|
unload_feature
|
||||||
|
raise
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Feature < Source
|
||||||
|
def initialize(stem)
|
||||||
|
super stem, CodeLoader.source_extension
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def resolve
|
||||||
|
@code = CoreDB.load_feature @stem, CodeLoader.source_extension, false, @record
|
||||||
|
|
||||||
|
return true if @code or false.equal? @code
|
||||||
|
return true if loadable?
|
||||||
|
return true if search
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
def load
|
||||||
|
return false if false.equal? @code
|
||||||
|
|
||||||
|
super()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class CodeModule < Source
|
||||||
|
def initialize(stem, wrap)
|
||||||
|
super stem, ""
|
||||||
|
|
||||||
|
@wrap = wrap
|
||||||
|
@record = false
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def resolve
|
||||||
|
@code = CoreDB.load_feature @stem, CodeLoader.source_extension, true, @record
|
||||||
|
|
||||||
|
return true if @code
|
||||||
|
return true if loadable?
|
||||||
|
return true if search
|
||||||
|
|
||||||
|
@prefix = Dir.pwd
|
||||||
|
return true if loadable?
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Script < Source
|
||||||
|
def initialize(stem)
|
||||||
|
super File.expand_path(stem), ""
|
||||||
|
|
||||||
|
@file = stem
|
||||||
|
@record = false
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def resolve
|
||||||
|
@code = CoreDB.load_feature @stem, "", true, @record
|
||||||
|
|
||||||
|
return true if @code
|
||||||
|
return true if loadable?
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
def make_script
|
||||||
|
script = super()
|
||||||
|
script.file_path = @file
|
||||||
|
script.make_main!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Library < Resource
|
||||||
|
def initialize(stem)
|
||||||
|
if stem.suffix? ".so"
|
||||||
|
super stem[0...-3], CodeLoader.library_extension
|
||||||
|
else
|
||||||
|
super stem, CodeLoader.library_extension
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def load
|
||||||
|
CoreDB.store nil, @stem, @prefix, @feature, @record
|
||||||
|
|
||||||
|
name = File.basename @feature, CodeLoader.library_extension
|
||||||
|
NativeMethod.load_extension(@feature, name)
|
||||||
|
|
||||||
|
return true
|
||||||
|
rescue Object
|
||||||
|
unload_feature
|
||||||
|
raise
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.source_extension
|
||||||
|
".rb"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.library_extension
|
||||||
|
LIBSUFFIX
|
||||||
|
end
|
||||||
|
|
||||||
|
# Loads rubygems using the bootstrap standard library files.
|
||||||
|
def self.load_rubygems
|
||||||
|
require "rubygems"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Loads the pre-compiled bytecode compiler. Sets up paths needed by the
|
||||||
|
# compiler to find dependencies like the parser.
|
||||||
|
def self.load_compiler
|
||||||
|
begin
|
||||||
|
require "rubinius/code/toolset"
|
||||||
|
|
||||||
|
ToolSets.create :runtime do
|
||||||
|
begin
|
||||||
|
require "rubinius/code/melbourne"
|
||||||
|
rescue LoadError
|
||||||
|
STDERR.puts "Melbourne failed to load, Ruby source parsing disabled"
|
||||||
|
end
|
||||||
|
require "rubinius/code/processor"
|
||||||
|
require "rubinius/code/compiler"
|
||||||
|
require "rubinius/code/ast"
|
||||||
|
end
|
||||||
|
rescue Object => e
|
||||||
|
raise LoadError, "Unable to load the bytecode compiler", e
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(stem)
|
||||||
|
@stem = stem
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def load_script
|
||||||
|
resource = Script.new @stem
|
||||||
|
|
||||||
|
load_error unless resource.resolve
|
||||||
|
resource.load
|
||||||
|
end
|
||||||
|
|
||||||
|
def load(wrap)
|
||||||
|
resource = CodeModule.new @stem, wrap
|
||||||
|
|
||||||
|
load_error unless resource.resolve
|
||||||
|
resource.load
|
||||||
|
end
|
||||||
|
|
||||||
|
def require
|
||||||
|
resource = Feature.new @stem
|
||||||
|
if resource.resolve
|
||||||
|
register_file resource.feature
|
||||||
|
return resource.load
|
||||||
|
end
|
||||||
|
|
||||||
|
resource = Library.new @stem
|
||||||
|
if resource.resolve
|
||||||
|
register_file resource.feature
|
||||||
|
return resource.load
|
||||||
|
end
|
||||||
|
|
||||||
|
load_error
|
||||||
|
ensure
|
||||||
|
unregister_file resource.feature if resource
|
||||||
|
end
|
||||||
|
|
||||||
|
def require_relative(scope)
|
||||||
|
script = scope.current_script
|
||||||
|
if script
|
||||||
|
if script.data_path
|
||||||
|
path = File.dirname(File.realdirpath(script.data_path))
|
||||||
|
else
|
||||||
|
path = Dir.pwd
|
||||||
|
end
|
||||||
|
|
||||||
|
@stem = File.expand_path(@stem, path)
|
||||||
|
|
||||||
|
require
|
||||||
|
else
|
||||||
|
raise LoadError.new "unable to get relative path"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def register_file(path)
|
||||||
|
registry = self.class.registry
|
||||||
|
|
||||||
|
Rubinius.synchronize(registry) do
|
||||||
|
registry << path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def unregister_file(path)
|
||||||
|
registry = self.class.registry
|
||||||
|
|
||||||
|
Rubinius.synchronize(registry) do
|
||||||
|
registry.delete_if { |x| x == path }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_error
|
||||||
|
# Some versions of Rails override LoadError.new but do NOT override
|
||||||
|
# LoadError.exception, so we must use LoadError.new here to match MRI
|
||||||
|
# behavior.
|
||||||
|
raise LoadError.new("no such file to load -- #{@stem}", path: @stem)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
33
gxg/core/compact_lookup_table.rb
Normal file
33
gxg/core/compact_lookup_table.rb
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
module Rubinius
|
||||||
|
class CompactLookupTable
|
||||||
|
def self.allocate
|
||||||
|
Rubinius.primitive :compactlookuptable_allocate
|
||||||
|
raise PrimitiveFailure, "CompactLookupTable.allocate primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def [](key)
|
||||||
|
Rubinius.primitive :compactlookuptable_fetch
|
||||||
|
raise PrimitiveFailure, "CompactLookupTable#[] primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def []=(key, val)
|
||||||
|
Rubinius.primitive :compactlookuptable_store
|
||||||
|
raise PrimitiveFailure, "CompactLookupTable#[]= primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def key?(key)
|
||||||
|
Rubinius.primitive :compactlookuptable_has_key
|
||||||
|
raise PrimitiveFailure, "CompactLookupTable#key? primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def keys
|
||||||
|
Rubinius.primitive :compactlookuptable_keys
|
||||||
|
raise PrimitiveFailure, "CompactLookupTable#keys primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def values
|
||||||
|
Rubinius.primitive :compactlookuptable_values
|
||||||
|
raise PrimitiveFailure, "CompactLookupTable#keys primitive failed"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
63
gxg/core/comparable.rb
Normal file
63
gxg/core/comparable.rb
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
module Comparable
|
||||||
|
def ==(other)
|
||||||
|
return true if equal?(other)
|
||||||
|
|
||||||
|
return false if Thread.detect_recursion(self, other) do
|
||||||
|
unless comp = (self <=> other)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
return Comparable.compare_int(comp) == 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def >(other)
|
||||||
|
unless comp = (self <=> other)
|
||||||
|
raise ArgumentError, "comparison of #{self.class} with #{other.class}"
|
||||||
|
end
|
||||||
|
|
||||||
|
Comparable.compare_int(comp) > 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def >=(other)
|
||||||
|
unless comp = (self <=> other)
|
||||||
|
raise ArgumentError, "comparison of #{self.class} with #{other.class}"
|
||||||
|
end
|
||||||
|
|
||||||
|
Comparable.compare_int(comp) >= 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def <(other)
|
||||||
|
unless comp = (self <=> other)
|
||||||
|
raise ArgumentError, "comparison of #{self.class} with #{other.class}"
|
||||||
|
end
|
||||||
|
|
||||||
|
Comparable.compare_int(comp) < 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def <=(other)
|
||||||
|
unless comp = (self <=> other)
|
||||||
|
raise ArgumentError, "comparison of #{self.class} with #{other.class}"
|
||||||
|
end
|
||||||
|
|
||||||
|
Comparable.compare_int(comp) <= 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def between?(min, max)
|
||||||
|
# This could be more elegant, but we need to use <= and => on self to match
|
||||||
|
# MRI.
|
||||||
|
return false if self < min
|
||||||
|
return false if self > max
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
# A version of MRI's rb_cmpint (sort of)
|
||||||
|
def self.compare_int(int)
|
||||||
|
return int if int.kind_of? Fixnum
|
||||||
|
|
||||||
|
return 1 if int > 0
|
||||||
|
return -1 if int < 0
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
726
gxg/core/compiled_code.rb
Normal file
726
gxg/core/compiled_code.rb
Normal file
|
|
@ -0,0 +1,726 @@
|
||||||
|
module Rubinius
|
||||||
|
class CompiledCode < Executable
|
||||||
|
def self.allocate
|
||||||
|
Rubinius.primitive :compiledcode_allocate
|
||||||
|
raise PrimitiveFailure, "CompiledCode.allocate primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def dup
|
||||||
|
Rubinius.primitive :compiledcode_dup
|
||||||
|
raise PrimitiveFailure, "CompiledCode#dup primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def call_sites
|
||||||
|
Rubinius.primitive :compiledcode_call_sites
|
||||||
|
raise PrimitiveFailure, "CompiledCode#call_sites primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def code_id
|
||||||
|
@code_id || stamp_id
|
||||||
|
end
|
||||||
|
|
||||||
|
def constant_caches
|
||||||
|
Rubinius.primitive :compiledcode_constant_caches
|
||||||
|
raise PrimitiveFailure, "CompiledCode#constant_caches primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return the CompiledCode for caller of the method that called
|
||||||
|
# .of_sender.
|
||||||
|
#
|
||||||
|
# For example:
|
||||||
|
#
|
||||||
|
# def g
|
||||||
|
# f_code = Rubinius::CompiledCode.of_sender
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# def f
|
||||||
|
# g
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# f_code is the CompiledCode of f as requested by g.
|
||||||
|
#
|
||||||
|
def self.of_sender
|
||||||
|
Rubinius.primitive :compiledcode_of_sender
|
||||||
|
raise PrimitiveFailure, "CompiledCode.of_sender primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def jitted?
|
||||||
|
Rubinius.primitive :compiledcode_jitted_p
|
||||||
|
raise PrimitiveFailure, "CompiledCode#jitted? primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def sample_count
|
||||||
|
Rubinius.primitive :compiledcode_sample_count
|
||||||
|
raise PrimitiveFailure, "CompiledCode#sample_count primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns the CompiledCode object for the currently executing Ruby
|
||||||
|
# method. For example:
|
||||||
|
#
|
||||||
|
# def m
|
||||||
|
# p Rubinius::CompiledCode.current.name
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
def self.current
|
||||||
|
Rubinius.primitive :compiledcode_current
|
||||||
|
raise PrimitiveFailure, "CompiledCode.current primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Any CompiledCode with this value in it's serial slot is expected to be
|
||||||
|
# the default, core library version.
|
||||||
|
KernelMethodSerial = 47
|
||||||
|
|
||||||
|
##
|
||||||
|
# Ivars: instance_variables, primitive, serial, name, iseq, stack_size,
|
||||||
|
# local_count, required_args, total_args, splat, literals, exceptions,
|
||||||
|
# lines, file, compiled, scope
|
||||||
|
##
|
||||||
|
# This is runtime hints, added to the method by the VM to indicate how it's
|
||||||
|
# being used.
|
||||||
|
|
||||||
|
attr_accessor :name
|
||||||
|
attr_accessor :hints # added by the VM to indicate how it's being used.
|
||||||
|
attr_accessor :metadata # [Tuple] extra data
|
||||||
|
attr_accessor :name # [Symbol] name of the method
|
||||||
|
attr_accessor :iseq # [Tuple] instructions to execute
|
||||||
|
attr_accessor :stack_size # [Integer] size of stack at compile time
|
||||||
|
attr_accessor :local_count # [Integer] number of local vars
|
||||||
|
attr_accessor :required_args # [Integer] number of required args
|
||||||
|
attr_accessor :post_args # [Integer] number of args after splat
|
||||||
|
attr_accessor :total_args # [Integer] number of total args
|
||||||
|
attr_accessor :splat # [Integer] POSITION of the splat arg
|
||||||
|
attr_accessor :literals # [Tuple] tuple of the literals
|
||||||
|
attr_accessor :lines # [Tuple] tuple of the lines where its found
|
||||||
|
attr_accessor :file # [Symbol] the file where this comes from
|
||||||
|
attr_accessor :local_names # [Array<Symbol>] names of the local vars
|
||||||
|
attr_accessor :scope # [LexicalScope] scope for looking up constants
|
||||||
|
attr_accessor :keywords # [Tuple] pairs of Symbol name, required flag
|
||||||
|
attr_accessor :arity # [Integer] number of arguments, negative if variadic.
|
||||||
|
attr_accessor :registers # [Integer] number of registers used.
|
||||||
|
|
||||||
|
##
|
||||||
|
# Compare this method with +other+. Instead of bugging out if +other+
|
||||||
|
# isn't a {CompiledCode}, this returns +false+ immediately unless
|
||||||
|
# we're comparing two apples, AKA, {CompiledCode}s. The methods have
|
||||||
|
# to be the exact same in implementation, but their scoping (location)
|
||||||
|
# can differ.
|
||||||
|
#
|
||||||
|
# For instance:
|
||||||
|
#
|
||||||
|
# (module A; def m; 5; end; end) == (def m; 5; end)
|
||||||
|
#
|
||||||
|
# and
|
||||||
|
#
|
||||||
|
# def monkey; 5; end
|
||||||
|
#
|
||||||
|
# and
|
||||||
|
#
|
||||||
|
# module B
|
||||||
|
# def monkey; 5; end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# would all be the same, despite their access to different ivars and scopes
|
||||||
|
# (which {CompiledCode}s DO keep track of)
|
||||||
|
#
|
||||||
|
# @todo Make example (in method documentation) match reality
|
||||||
|
# @param [Rubinius::CompiledCode] other the other part to compare
|
||||||
|
# @param [Boolean]
|
||||||
|
#
|
||||||
|
def ==(other)
|
||||||
|
return false unless other.kind_of? CompiledCode
|
||||||
|
equivalent_body?(other) and
|
||||||
|
@lines == other.lines and
|
||||||
|
@name == other.name
|
||||||
|
end
|
||||||
|
|
||||||
|
def equivalent_body?(other)
|
||||||
|
@tags == other.tags and
|
||||||
|
@primitive == other.primitive and
|
||||||
|
@iseq == other.iseq and
|
||||||
|
@stack_size == other.stack_size and
|
||||||
|
@local_count == other.local_count and
|
||||||
|
@required_args == other.required_args and
|
||||||
|
@total_args == other.total_args and
|
||||||
|
@splat == other.splat and
|
||||||
|
block_index == other.block_index and
|
||||||
|
@literals == other.literals and
|
||||||
|
@file == other.file and
|
||||||
|
@local_names == other.local_names
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns the index of local +name+ or nil if there is no local.
|
||||||
|
#
|
||||||
|
def local_slot(name)
|
||||||
|
slot = 0
|
||||||
|
while slot < @local_count
|
||||||
|
return slot if @local_names[slot] == name
|
||||||
|
slot += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Stores the index of the block argument in the metadata storage.
|
||||||
|
#
|
||||||
|
# @param [Fixnum] position
|
||||||
|
#
|
||||||
|
def block_index=(position)
|
||||||
|
if position
|
||||||
|
add_metadata(:block_index, position)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# @return [Fixnum|NilClass]
|
||||||
|
#
|
||||||
|
def block_index
|
||||||
|
return get_metadata(:block_index)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Return a human readable interpretation of this method.
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
def inspect
|
||||||
|
"#<#{self.class.name}:0x#{object_id.to_s(16)} name=#{@name} file=#{@file} line=#{defined_line}>"
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :to_s, :inspect
|
||||||
|
|
||||||
|
##
|
||||||
|
# Make the method change its scope so that it can act as though
|
||||||
|
# it's from somewhere else. You can pass in a method and +self+
|
||||||
|
# will borrow its scope.
|
||||||
|
#
|
||||||
|
# @param [#scope] other the other method that has a scope we can borrow
|
||||||
|
def inherit_scope(other)
|
||||||
|
@scope = other.scope
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Set a breakpoint on +ip+. +obj+ can be any object. When the breakpoint
|
||||||
|
# is hit +obj+ is sent to the debugger so it can distinguish which breakpoint
|
||||||
|
# this is.
|
||||||
|
#
|
||||||
|
def set_breakpoint(ip, obj)
|
||||||
|
Rubinius.primitive :compiledcode_set_breakpoint
|
||||||
|
raise ArgumentError, "Unable to set breakpoint on #{inspect} at invalid bytecode address #{ip}"
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Erase a breakpoint at +ip+
|
||||||
|
#
|
||||||
|
def clear_breakpoint(ip)
|
||||||
|
Rubinius.primitive :compiledcode_clear_breakpoint
|
||||||
|
raise ArgumentError, "Unable to clear breakpoint on #{inspect} at invalid bytecode address #{ip}"
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Indicate if there is a breakpoint set at +ip+
|
||||||
|
#
|
||||||
|
def breakpoint?(ip)
|
||||||
|
Rubinius.primitive :compiledcode_is_breakpoint
|
||||||
|
raise ArgumentError, "Unable to retrieve breakpoint status on #{inspect} at bytecode address #{ip}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def stamp_id
|
||||||
|
Rubinius.primitive :compiledcode_stamp_id
|
||||||
|
raise ArgumentError, "CompiledCode#stamp_id primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
class Script
|
||||||
|
attr_accessor :compiled_code
|
||||||
|
attr_accessor :file_path
|
||||||
|
attr_accessor :data_path
|
||||||
|
attr_accessor :eval_source
|
||||||
|
|
||||||
|
def initialize(method, path=nil, for_eval=false)
|
||||||
|
@compiled_code = method
|
||||||
|
@file_path = path
|
||||||
|
@for_eval = for_eval
|
||||||
|
@eval_source = nil
|
||||||
|
@main = false
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def eval?
|
||||||
|
@for_eval
|
||||||
|
end
|
||||||
|
|
||||||
|
def make_main!
|
||||||
|
@main = true
|
||||||
|
end
|
||||||
|
|
||||||
|
def main?
|
||||||
|
@main
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Creates the Script instance for a toplevel compiled method.
|
||||||
|
def create_script(wrap=false)
|
||||||
|
script = CompiledCode::Script.new(self)
|
||||||
|
|
||||||
|
# Setup the scoping.
|
||||||
|
cs = LexicalScope.new(Object)
|
||||||
|
cs.script = script
|
||||||
|
|
||||||
|
if wrap
|
||||||
|
@scope = LexicalScope.new(Module.new, cs)
|
||||||
|
else
|
||||||
|
@scope = cs
|
||||||
|
end
|
||||||
|
|
||||||
|
sc = Rubinius::Type.object_singleton_class(MAIN)
|
||||||
|
VM.reset_method_cache sc, :__script__
|
||||||
|
sc.method_table.store :__script__, nil, self, @scope, 0, :public
|
||||||
|
|
||||||
|
script
|
||||||
|
end
|
||||||
|
|
||||||
|
def active_path
|
||||||
|
if @scope and script = @scope.current_script
|
||||||
|
if fp = script.file_path
|
||||||
|
return fp.dup
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@file.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def eval_source
|
||||||
|
if @scope and script = @scope.current_script
|
||||||
|
return script.eval_source
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Return the line of source code at +ip+.
|
||||||
|
#
|
||||||
|
# @param [Fixnum] ip
|
||||||
|
# @return [Fixnum] line
|
||||||
|
def line_from_ip(ip)
|
||||||
|
return -1 unless @lines
|
||||||
|
return 0 if @lines.size < 2
|
||||||
|
|
||||||
|
low = 0
|
||||||
|
high = @lines.size / 2 - 1
|
||||||
|
|
||||||
|
while low <= high
|
||||||
|
# the chance that we're going from a fixnum to a bignum
|
||||||
|
# here is low, but we still try to prevent that.
|
||||||
|
mid = low + ((high - low) / 2)
|
||||||
|
|
||||||
|
line_index = mid * 2 + 1
|
||||||
|
|
||||||
|
if ip < @lines.at(line_index - 1)
|
||||||
|
high = mid - 1
|
||||||
|
elsif ip >= @lines.at(line_index + 1)
|
||||||
|
low = mid + 1
|
||||||
|
else
|
||||||
|
return @lines.at(line_index)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@lines.at(@lines.size - 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns the address (IP) of the first instruction in this CompiledCode
|
||||||
|
# that is on the specified line, or the address of the first instruction on
|
||||||
|
# the next code line after the specified line if there are no instructions
|
||||||
|
# on the requested line.
|
||||||
|
# This method only looks at instructions within the current CompiledCode;
|
||||||
|
# see #locate_line for an alternate method that also searches inside the child
|
||||||
|
# CompiledCodes.
|
||||||
|
#
|
||||||
|
# Optionally only consider ip's greater than +start+
|
||||||
|
#
|
||||||
|
# @return [Fixnum] the address of the first instruction
|
||||||
|
# OR nil if there is no ip for the given line
|
||||||
|
def first_ip_on_line(line, start=nil)
|
||||||
|
i = 1
|
||||||
|
total = @lines.size
|
||||||
|
|
||||||
|
while i < total
|
||||||
|
cur_line = @lines.at(i)
|
||||||
|
if cur_line >= line
|
||||||
|
ip = @lines.at(i-1)
|
||||||
|
|
||||||
|
if !start or ip > start
|
||||||
|
# matched the definition line, return 0
|
||||||
|
return 0 if ip == -1
|
||||||
|
return ip
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
i += 2
|
||||||
|
end
|
||||||
|
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# The first line where instructions are located.
|
||||||
|
#
|
||||||
|
# @return [Fixnum]
|
||||||
|
def first_line
|
||||||
|
line_from_ip(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Indicate the line in the source code that this
|
||||||
|
# was defined on.
|
||||||
|
#
|
||||||
|
def defined_line
|
||||||
|
# Detect a -1 ip, which indicates a definition entry.
|
||||||
|
return @lines[1] if @lines[0] == -1
|
||||||
|
first_line
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
#
|
||||||
|
# Given all CompiledCodes in the system, find one that
|
||||||
|
# was defined in file +file+ and encompasses +line+
|
||||||
|
#
|
||||||
|
def self.locate(file, line=nil)
|
||||||
|
file = StringValue file
|
||||||
|
|
||||||
|
if line
|
||||||
|
line = Integer line
|
||||||
|
elsif m = /\A(.*):(\d+)\Z/.match(file)
|
||||||
|
file = m[1]
|
||||||
|
line = m[2].to_i
|
||||||
|
end
|
||||||
|
|
||||||
|
ary = []
|
||||||
|
ObjectSpace.find_object([:kind_of, Rubinius::CompiledCode], ary)
|
||||||
|
|
||||||
|
methods = ary.find_all do |x|
|
||||||
|
x.scope and path = x.scope.absolute_active_path and \
|
||||||
|
path.suffix?(file)
|
||||||
|
end
|
||||||
|
|
||||||
|
return methods unless line
|
||||||
|
|
||||||
|
methods.find_all { |x| x.first_ip_on_line(line) }
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Is this actually a block of code?
|
||||||
|
#
|
||||||
|
# @return [Boolean]
|
||||||
|
def is_block?
|
||||||
|
get_metadata(:for_block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def for_eval?
|
||||||
|
get_metadata(:for_eval)
|
||||||
|
end
|
||||||
|
|
||||||
|
def for_module_body?
|
||||||
|
get_metadata(:for_module_body)
|
||||||
|
end
|
||||||
|
|
||||||
|
def describe
|
||||||
|
str = "method #{@name}: #{@total_args} arg(s), #{@required_args} required"
|
||||||
|
if @splat
|
||||||
|
str << ", splatted."
|
||||||
|
end
|
||||||
|
str
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Convenience method to return an array of the child CompiledCodes from
|
||||||
|
# this CompiledCode's literals.
|
||||||
|
#
|
||||||
|
# @return [Tuple]
|
||||||
|
def child_methods
|
||||||
|
literals.select { |lit| lit.kind_of? CompiledCode }
|
||||||
|
end
|
||||||
|
|
||||||
|
def change_name(name)
|
||||||
|
code = dup
|
||||||
|
code.name = name
|
||||||
|
|
||||||
|
lits = Tuple.new(code.literals.size)
|
||||||
|
code.literals.each_with_index do |lit, idx|
|
||||||
|
if lit.kind_of? CompiledCode and lit.is_block?
|
||||||
|
lit = lit.change_name name
|
||||||
|
end
|
||||||
|
|
||||||
|
lits[idx] = lit
|
||||||
|
end
|
||||||
|
|
||||||
|
code.literals = lits
|
||||||
|
return code
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Locates the CompiledCode and instruction address (IP) of the first
|
||||||
|
# instruction on the specified line. This method recursively examines child
|
||||||
|
# compiled methods until an exact match for the searched line is found.
|
||||||
|
# It returns both the matching CompiledCode and the IP of the first
|
||||||
|
# instruction on the requested line, or nil if no match for the specified line
|
||||||
|
# is found.
|
||||||
|
#
|
||||||
|
# @return [(Rubinius::CompiledCode, Fixnum), NilClass] returns
|
||||||
|
# nil if nothing is found, else an array of size 2 containing the method
|
||||||
|
# the line was found in and the IP pointing there.
|
||||||
|
def locate_line(line)
|
||||||
|
i = 1
|
||||||
|
total = @lines.size
|
||||||
|
while i < total
|
||||||
|
cur_line = @lines.at(i)
|
||||||
|
if cur_line == line
|
||||||
|
ip = @lines.at(i-1)
|
||||||
|
return nil if ip < 0
|
||||||
|
return [self, ip]
|
||||||
|
elsif cur_line > line
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
i += 2
|
||||||
|
end
|
||||||
|
|
||||||
|
# Didn't find line in this CM, so check if a contained
|
||||||
|
# CM encompasses the line searched for
|
||||||
|
child_methods.each do |child|
|
||||||
|
if res = child.locate_line(line)
|
||||||
|
return res
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# No child method is a match - fail
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Decodes the instruction sequence that is represented by this compileed
|
||||||
|
# method. Delegates to InstructionSequence to do the instruction decoding,
|
||||||
|
# but then converts opcode literal arguments to their actual values by looking
|
||||||
|
# them up in the literals tuple.
|
||||||
|
# Takes an optional bytecodes argument representing the bytecode that is to
|
||||||
|
# be decoded using this CompiledCode's locals and literals. This is provided
|
||||||
|
# for use by the debugger, where the bytecode sequence to be decoded may not
|
||||||
|
# exactly match the bytecode currently held by the CompiledCode, typically
|
||||||
|
# as a result of substituting yield_debugger instructions into the bytecode.
|
||||||
|
def decode(bytecodes = @iseq)
|
||||||
|
decoder = Rubinius::InstructionDecoder.new(bytecodes)
|
||||||
|
stream = decoder.decode(false)
|
||||||
|
ip = 0
|
||||||
|
|
||||||
|
stream.map do |inst|
|
||||||
|
instruct = Instruction.new(inst, self, ip)
|
||||||
|
ip += instruct.size
|
||||||
|
instruct
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_metadata(key, val)
|
||||||
|
raise TypeError, "key must be a symbol" unless key.kind_of? Symbol
|
||||||
|
|
||||||
|
case val
|
||||||
|
when true, false, Symbol, Fixnum, String
|
||||||
|
# ok
|
||||||
|
else
|
||||||
|
raise TypeError, "invalid type of value"
|
||||||
|
end
|
||||||
|
|
||||||
|
@metadata ||= nil # to deal with MRI seeing @metadata as not set
|
||||||
|
|
||||||
|
unless @metadata
|
||||||
|
@metadata = Tuple.new(2)
|
||||||
|
@metadata[0] = key
|
||||||
|
@metadata[1] = val
|
||||||
|
return val
|
||||||
|
end
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
fin = @metadata.size
|
||||||
|
|
||||||
|
while i < fin
|
||||||
|
if @metadata[i] == key
|
||||||
|
@metadata[i + 1] = val
|
||||||
|
return val
|
||||||
|
end
|
||||||
|
|
||||||
|
i += 2
|
||||||
|
end
|
||||||
|
|
||||||
|
tup = Tuple.new(fin + 2)
|
||||||
|
tup.copy_from @metadata, 0, fin, 0
|
||||||
|
tup[fin] = key
|
||||||
|
tup[fin + 1] = val
|
||||||
|
|
||||||
|
@metadata = tup
|
||||||
|
|
||||||
|
return val
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_metadata(key)
|
||||||
|
return nil unless @metadata.kind_of? Tuple
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
while i < @metadata.size
|
||||||
|
if @metadata[i] == key
|
||||||
|
return @metadata[i + 1]
|
||||||
|
end
|
||||||
|
|
||||||
|
i += 2
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# For Method#parameters
|
||||||
|
def parameters
|
||||||
|
params = []
|
||||||
|
|
||||||
|
return params unless respond_to?(:local_names)
|
||||||
|
|
||||||
|
m = required_args - post_args
|
||||||
|
o = m + total_args - required_args
|
||||||
|
p = o + post_args
|
||||||
|
p += 1 if splat
|
||||||
|
|
||||||
|
local_names.each_with_index do |name, i|
|
||||||
|
if i < m
|
||||||
|
params << parameter(:req, name)
|
||||||
|
elsif i < o
|
||||||
|
params << [:opt, name]
|
||||||
|
elsif splat == i
|
||||||
|
params << parameter(:rest, name)
|
||||||
|
elsif i < p
|
||||||
|
params << parameter(:req, name)
|
||||||
|
elsif block_index == i
|
||||||
|
params << [:block, name]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
params
|
||||||
|
end
|
||||||
|
|
||||||
|
def parameter(kind, param)
|
||||||
|
array = [kind]
|
||||||
|
array << param unless param == :* or param[0, 2] == "_:"
|
||||||
|
array
|
||||||
|
end
|
||||||
|
private :parameter
|
||||||
|
|
||||||
|
##
|
||||||
|
# Represents virtual machine's CPU instruction.
|
||||||
|
# Instructions are organized into instruction
|
||||||
|
# sequences known as iSeq, forming body
|
||||||
|
# of CompiledCodes.
|
||||||
|
#
|
||||||
|
# To generate VM opcodes documentation
|
||||||
|
# use rake doc:vm task.
|
||||||
|
class Instruction
|
||||||
|
class Association
|
||||||
|
def initialize(index)
|
||||||
|
@index = index
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
"literals[#{@index}]"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Location
|
||||||
|
FORMAT = "%04d:"
|
||||||
|
|
||||||
|
def initialize(location)
|
||||||
|
@location = location
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
FORMAT % @location
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(inst, code, ip)
|
||||||
|
@instruction = inst[0]
|
||||||
|
@args = inst[1..-1]
|
||||||
|
@comment = nil
|
||||||
|
|
||||||
|
@args.each_index do |i|
|
||||||
|
case @instruction.args[i]
|
||||||
|
when :literal
|
||||||
|
@args[i] = code.literals[@args[i]]
|
||||||
|
when :local
|
||||||
|
# TODO: Blocks should be able to retrieve local names as well,
|
||||||
|
# but need access to method corresponding to home context
|
||||||
|
if code.local_names and !code.is_block?
|
||||||
|
@comment = code.local_names[args[i]].to_s
|
||||||
|
end
|
||||||
|
when :association
|
||||||
|
@args[i] = Association.new(args[i])
|
||||||
|
when :location
|
||||||
|
@args[i] = Location.new(args[i])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@compiled_code = code
|
||||||
|
@ip = ip
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
# Instruction pointer
|
||||||
|
attr_reader :ip
|
||||||
|
|
||||||
|
##
|
||||||
|
# Return the line that this instruction is on in the method
|
||||||
|
#
|
||||||
|
def line
|
||||||
|
@compiled_code.line_from_ip(ip)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns the OpCode object
|
||||||
|
#
|
||||||
|
attr_reader :instruction
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns the symbol representing the opcode for this instruction.
|
||||||
|
#
|
||||||
|
def opcode
|
||||||
|
@instruction.opcode
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns an array of 0 to 2 arguments, depending on the opcode.
|
||||||
|
#
|
||||||
|
attr_reader :args
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns a Fixnum indicating how wide the instruction takes up
|
||||||
|
# in the instruction stream
|
||||||
|
#
|
||||||
|
def size
|
||||||
|
@args.size + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# A nice human readable interpretation of this set of instructions
|
||||||
|
def to_s
|
||||||
|
str = "#{Location::FORMAT} %-27s" % [@ip, opcode]
|
||||||
|
str << @args.map{ |a| a.inspect }.join(', ')
|
||||||
|
if @comment
|
||||||
|
str << " # #{@comment}"
|
||||||
|
end
|
||||||
|
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
340
gxg/core/complex.rb
Normal file
340
gxg/core/complex.rb
Normal file
|
|
@ -0,0 +1,340 @@
|
||||||
|
#
|
||||||
|
# complex.rb -
|
||||||
|
# $Release Version: 0.5 $
|
||||||
|
# $Revision: 1.3 $
|
||||||
|
# $Date: 1998/07/08 10:05:28 $
|
||||||
|
# by Keiju ISHITSUKA(SHL Japan Inc.)
|
||||||
|
#
|
||||||
|
|
||||||
|
class Complex < Numeric
|
||||||
|
|
||||||
|
undef_method :%
|
||||||
|
undef_method :<
|
||||||
|
undef_method :<=
|
||||||
|
undef_method :<=>
|
||||||
|
undef_method :>
|
||||||
|
undef_method :>=
|
||||||
|
undef_method :between?
|
||||||
|
undef_method :div
|
||||||
|
undef_method :divmod
|
||||||
|
undef_method :floor
|
||||||
|
undef_method :ceil
|
||||||
|
undef_method :modulo
|
||||||
|
undef_method :remainder
|
||||||
|
undef_method :round
|
||||||
|
undef_method :step
|
||||||
|
undef_method :truncate
|
||||||
|
undef_method :i
|
||||||
|
undef_method :positive?
|
||||||
|
undef_method :negative?
|
||||||
|
|
||||||
|
def self.convert(real, imag = undefined)
|
||||||
|
if real.equal?(nil) || imag.equal?(nil)
|
||||||
|
raise TypeError, "cannot convert nil into Complex"
|
||||||
|
end
|
||||||
|
|
||||||
|
real = real.to_c if real.kind_of?(String)
|
||||||
|
imag = imag.to_c if imag.kind_of?(String)
|
||||||
|
|
||||||
|
if real.kind_of?(Complex) && !real.imag.kind_of?(Float) && real.imag == 0
|
||||||
|
real = real.real
|
||||||
|
end
|
||||||
|
|
||||||
|
if imag.kind_of?(Complex) && !imag.imag.kind_of?(Float) && imag.imag == 0
|
||||||
|
imag = imag.real
|
||||||
|
end
|
||||||
|
|
||||||
|
if real.kind_of?(Complex) && !imag.kind_of?(Float) && imag == 0
|
||||||
|
return real
|
||||||
|
end
|
||||||
|
|
||||||
|
if undefined.equal?(imag)
|
||||||
|
if real.kind_of?(Numeric) && !real.real?
|
||||||
|
return real
|
||||||
|
elsif !real.kind_of?(Numeric)
|
||||||
|
return Rubinius::Type.coerce_to(real, Complex, :to_c)
|
||||||
|
else
|
||||||
|
imag = 0
|
||||||
|
end
|
||||||
|
elsif real.kind_of?(Numeric) && imag.kind_of?(Numeric) && (!real.real? || !imag.real?)
|
||||||
|
return real + imag * Complex.new(0, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
return real if Rubinius.mathn_loaded? && imag.equal?(0)
|
||||||
|
rect(real, imag)
|
||||||
|
end
|
||||||
|
|
||||||
|
def Complex.generic?(other) # :nodoc:
|
||||||
|
other.kind_of?(Integer) or
|
||||||
|
other.kind_of?(Float) or
|
||||||
|
(defined?(Rational) and other.kind_of?(Rational))
|
||||||
|
end
|
||||||
|
|
||||||
|
def Complex.rect(real, imag=0)
|
||||||
|
raise TypeError, 'not a real' unless check_real?(real) && check_real?(imag)
|
||||||
|
new(real, imag)
|
||||||
|
end
|
||||||
|
|
||||||
|
def Complex.polar(r, theta=0)
|
||||||
|
raise TypeError, 'not a real' unless check_real?(r) && check_real?(theta)
|
||||||
|
|
||||||
|
Complex(r*Math.cos(theta), r*Math.sin(theta))
|
||||||
|
end
|
||||||
|
|
||||||
|
def Complex.check_real?(obj)
|
||||||
|
obj.kind_of?(Numeric) && obj.real?
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(a, b = 0)
|
||||||
|
@real = a
|
||||||
|
@imag = b
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def -@
|
||||||
|
Complex(-real, -imag)
|
||||||
|
end
|
||||||
|
|
||||||
|
def +(other)
|
||||||
|
if other.kind_of?(Complex)
|
||||||
|
Complex(real + other.real, imag + other.imag)
|
||||||
|
elsif other.kind_of?(Numeric) && other.real?
|
||||||
|
Complex(real + other, imag)
|
||||||
|
else
|
||||||
|
redo_coerced(:+, other)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def -(other)
|
||||||
|
if other.kind_of?(Complex)
|
||||||
|
Complex(real - other.real, imag - other.imag)
|
||||||
|
elsif other.kind_of?(Numeric) && other.real?
|
||||||
|
Complex(real - other, imag)
|
||||||
|
else
|
||||||
|
redo_coerced(:-, other)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def *(other)
|
||||||
|
if other.kind_of?(Complex)
|
||||||
|
Complex(real * other.real - imag * other.imag,
|
||||||
|
real * other.imag + imag * other.real)
|
||||||
|
elsif other.kind_of?(Numeric) && other.real?
|
||||||
|
Complex(real * other, imag * other)
|
||||||
|
else
|
||||||
|
redo_coerced(:*, other)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def divide(other)
|
||||||
|
if other.kind_of?(Complex)
|
||||||
|
self * other.conjugate / other.abs2
|
||||||
|
elsif other.kind_of?(Numeric) && other.real?
|
||||||
|
Complex(real.quo(other), imag.quo(other))
|
||||||
|
else
|
||||||
|
redo_coerced(:quo, other)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :/, :divide
|
||||||
|
alias_method :quo, :divide
|
||||||
|
|
||||||
|
def ** (other)
|
||||||
|
if !other.kind_of?(Float) && other == 0
|
||||||
|
return Complex(1)
|
||||||
|
end
|
||||||
|
if other.kind_of?(Complex)
|
||||||
|
r, theta = polar
|
||||||
|
ore = other.real
|
||||||
|
oim = other.imag
|
||||||
|
nr = Math.exp(ore*Math.log(r) - oim * theta)
|
||||||
|
ntheta = theta*ore + oim*Math.log(r)
|
||||||
|
Complex.polar(nr, ntheta)
|
||||||
|
elsif other.kind_of?(Integer)
|
||||||
|
if other > 0
|
||||||
|
x = self
|
||||||
|
z = x
|
||||||
|
n = other - 1
|
||||||
|
while n != 0
|
||||||
|
while (div, mod = n.divmod(2)
|
||||||
|
mod == 0)
|
||||||
|
x = Complex(x.real*x.real - x.imag*x.imag, 2*x.real*x.imag)
|
||||||
|
n = div
|
||||||
|
end
|
||||||
|
z *= x
|
||||||
|
n -= 1
|
||||||
|
end
|
||||||
|
z
|
||||||
|
else
|
||||||
|
if defined? Rational
|
||||||
|
(Rational.new(1, 1) / self) ** -other
|
||||||
|
else
|
||||||
|
self ** Float(other)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elsif Complex.generic?(other)
|
||||||
|
r, theta = polar
|
||||||
|
Complex.polar(r**other, theta*other)
|
||||||
|
else
|
||||||
|
x, y = other.coerce(self)
|
||||||
|
x**y
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def abs
|
||||||
|
Math.hypot(@real, @imag)
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :magnitude, :abs
|
||||||
|
|
||||||
|
def abs2
|
||||||
|
@real*@real + @imag*@imag
|
||||||
|
end
|
||||||
|
|
||||||
|
def arg
|
||||||
|
Math.atan2(@imag, @real)
|
||||||
|
end
|
||||||
|
alias_method :angle, :arg
|
||||||
|
alias_method :phase, :arg
|
||||||
|
|
||||||
|
def polar
|
||||||
|
[abs, arg]
|
||||||
|
end
|
||||||
|
|
||||||
|
def conjugate
|
||||||
|
Complex(@real, -@imag)
|
||||||
|
end
|
||||||
|
alias_method :conj, :conjugate
|
||||||
|
|
||||||
|
def ==(other)
|
||||||
|
if other.kind_of?(Complex)
|
||||||
|
real == other.real && imag == other.imag
|
||||||
|
elsif other.kind_of?(Numeric) && other.real?
|
||||||
|
real == other && imag == 0
|
||||||
|
else
|
||||||
|
other == self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def eql?(other)
|
||||||
|
other.kind_of?(Complex) and
|
||||||
|
imag.class == other.imag.class and
|
||||||
|
real.class == other.real.class and
|
||||||
|
self == other
|
||||||
|
end
|
||||||
|
|
||||||
|
def coerce(other)
|
||||||
|
if other.kind_of?(Numeric) && other.real?
|
||||||
|
[Complex.new(other, 0), self]
|
||||||
|
elsif other.kind_of?(Complex)
|
||||||
|
[other, self]
|
||||||
|
else
|
||||||
|
raise TypeError, "#{other.class} can't be coerced into Complex"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def denominator
|
||||||
|
@real.denominator.lcm(@imag.denominator)
|
||||||
|
end
|
||||||
|
|
||||||
|
def numerator
|
||||||
|
cd = denominator
|
||||||
|
Complex(@real.numerator*(cd/@real.denominator),
|
||||||
|
@imag.numerator*(cd/@imag.denominator))
|
||||||
|
end
|
||||||
|
|
||||||
|
def real?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def rect
|
||||||
|
[@real, @imag]
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :rectangular, :rect
|
||||||
|
|
||||||
|
def to_f
|
||||||
|
raise RangeError, "can't convert #{self} into Float" unless !imag.kind_of?(Float) && imag == 0
|
||||||
|
real.to_f
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_i
|
||||||
|
raise RangeError, "can't convert #{self} into Integer" unless !imag.kind_of?(Float) && imag == 0
|
||||||
|
real.to_i
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_r
|
||||||
|
raise RangeError, "can't' convert #{self} into Rational" unless !imag.kind_of?(Float) && imag == 0
|
||||||
|
real.to_r
|
||||||
|
end
|
||||||
|
|
||||||
|
def rationalize(eps = nil)
|
||||||
|
raise RangeError, "can't' convert #{self} into Rational" unless !imag.kind_of?(Float) && imag == 0
|
||||||
|
real.rationalize(eps)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
result = real.to_s
|
||||||
|
|
||||||
|
if imag.kind_of?(Float) ? !imag.nan? && imag.signbit? : imag < 0
|
||||||
|
result << "-"
|
||||||
|
else
|
||||||
|
result << "+"
|
||||||
|
end
|
||||||
|
|
||||||
|
imag_s = imag.abs.to_s
|
||||||
|
result << imag_s
|
||||||
|
|
||||||
|
unless imag_s[-1] =~ /\d/
|
||||||
|
result << "*"
|
||||||
|
end
|
||||||
|
|
||||||
|
result << "i"
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
def hash
|
||||||
|
@real.hash ^ @imag.hash
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
"(#{to_s})"
|
||||||
|
end
|
||||||
|
|
||||||
|
def fdiv(other)
|
||||||
|
raise TypeError, "#{other.class} can't be coerced into Complex" unless other.is_a?(Numeric)
|
||||||
|
|
||||||
|
# FIXME
|
||||||
|
self / other
|
||||||
|
end
|
||||||
|
|
||||||
|
def marshal_dump
|
||||||
|
ary = [real, imag]
|
||||||
|
instance_variables.each do |ivar|
|
||||||
|
ary.instance_variable_set(ivar, instance_variable_get(ivar))
|
||||||
|
end
|
||||||
|
ary
|
||||||
|
end
|
||||||
|
|
||||||
|
private :marshal_dump
|
||||||
|
|
||||||
|
def marshal_load(ary)
|
||||||
|
@real, @imag = ary
|
||||||
|
ary.instance_variables.each do |ivar|
|
||||||
|
instance_variable_set(ivar, ary.instance_variable_get(ivar))
|
||||||
|
end
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
class << self
|
||||||
|
private :convert
|
||||||
|
private :check_real?
|
||||||
|
|
||||||
|
alias_method :rectangular, :rect
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :real
|
||||||
|
attr_reader :imag
|
||||||
|
alias_method :imaginary, :imag
|
||||||
|
end
|
||||||
59
gxg/core/complexifier.rb
Normal file
59
gxg/core/complexifier.rb
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
class String
|
||||||
|
class Complexifier
|
||||||
|
def initialize(value)
|
||||||
|
@value = value
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def convert
|
||||||
|
if m = PATTERN0.match(@value)
|
||||||
|
sr = m[1]
|
||||||
|
si = m[2]
|
||||||
|
re = m.post_match
|
||||||
|
po = true
|
||||||
|
elsif m = PATTERN1.match(@value)
|
||||||
|
sr = nil
|
||||||
|
si = (m[1] || "") + (m[2] || "1")
|
||||||
|
re = m.post_match
|
||||||
|
po = false
|
||||||
|
elsif m = PATTERN2.match(@value)
|
||||||
|
sr = m[1]
|
||||||
|
si = m[2] ? m[3] + (m[4] || "1") : nil
|
||||||
|
re = m.post_match
|
||||||
|
po = false
|
||||||
|
else
|
||||||
|
return Complex.new(0, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
r = 0
|
||||||
|
i = 0
|
||||||
|
|
||||||
|
if sr
|
||||||
|
if sr.include?("/")
|
||||||
|
r = sr.to_r
|
||||||
|
elsif sr.match(/[.eE]/)
|
||||||
|
r = sr.to_f
|
||||||
|
else
|
||||||
|
r = sr.to_i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if si
|
||||||
|
if si.include?("/")
|
||||||
|
i = si.to_r
|
||||||
|
elsif si.match(/[.eE]/)
|
||||||
|
i = si.to_f
|
||||||
|
else
|
||||||
|
i = si.to_i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if po
|
||||||
|
Complex.polar(r, i)
|
||||||
|
else
|
||||||
|
Complex.rect(r, i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
34
gxg/core/configuration.rb
Normal file
34
gxg/core/configuration.rb
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
module Rubinius
|
||||||
|
class Configuration
|
||||||
|
def initialize
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def get_variable(name)
|
||||||
|
Rubinius.primitive :vm_get_config_item
|
||||||
|
raise PrimitiveFailure, "Rubinius::Configuration#get_variable primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_section(section)
|
||||||
|
Rubinius.primitive :vm_get_config_section
|
||||||
|
raise PrimitiveFailure, "Rubinius::Configuration#get_section primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def section(section)
|
||||||
|
ary = get_section(section)
|
||||||
|
i = 0
|
||||||
|
while i < ary.size
|
||||||
|
tup = ary[i]
|
||||||
|
yield tup[0], tup[1]
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def [](name)
|
||||||
|
get_variable(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :get, :[]
|
||||||
|
end
|
||||||
|
end
|
||||||
31
gxg/core/console.rb
Normal file
31
gxg/core/console.rb
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
module Rubinius
|
||||||
|
module Console
|
||||||
|
class Server
|
||||||
|
attr_reader :inbox
|
||||||
|
attr_reader :outbox
|
||||||
|
attr_reader :thread
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@inbox = Channel.new
|
||||||
|
@outbox = Channel.new
|
||||||
|
|
||||||
|
@thread = Thread.new do
|
||||||
|
source = @inbox.receive
|
||||||
|
break unless source.size
|
||||||
|
|
||||||
|
@outbox.send evaluate(source)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def evaluate(source)
|
||||||
|
begin
|
||||||
|
eval(source, TOPLEVEL_BINDING, "(console)", 1).inspect
|
||||||
|
rescue Object => e
|
||||||
|
e.message + e.backtrace.join("\n")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
28
gxg/core/constant_cache.rb
Normal file
28
gxg/core/constant_cache.rb
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
module Rubinius
|
||||||
|
class ConstantCache
|
||||||
|
attr_reader :name
|
||||||
|
attr_reader :value
|
||||||
|
attr_reader :module
|
||||||
|
attr_reader :lexical_scope
|
||||||
|
attr_reader :executable
|
||||||
|
|
||||||
|
def ip
|
||||||
|
Rubinius.primitive :constant_cache_ip
|
||||||
|
raise PrimitiveFailure, "CallSite#ip primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def serial
|
||||||
|
Rubinius.primitive :constant_cache_serial
|
||||||
|
raise PrimitiveFailure, "CallSite#serial primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def location
|
||||||
|
"#{@executable.file}:#{@executable.line_from_ip(ip)}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
"#<#{self.class.name}:0x#{self.object_id.to_s(16)} #{location}##{@name} constant=#{@value} module=#{@module} lexical_scope=#{@lexical_scope}>"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
97
gxg/core/constant_table.rb
Normal file
97
gxg/core/constant_table.rb
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
module Rubinius
|
||||||
|
class ConstantTable
|
||||||
|
class Bucket
|
||||||
|
attr_reader :name
|
||||||
|
attr_accessor :constant
|
||||||
|
attr_accessor :visibility
|
||||||
|
attr_reader :next
|
||||||
|
|
||||||
|
def private?
|
||||||
|
@visibility == :private
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
"#<#{self.class}:0x#{object_id.to_s(16)} name=#{@name.inspect} constant=#{@constant.inspect} visibility=#{visibility.inspect}>"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :bins
|
||||||
|
|
||||||
|
def size
|
||||||
|
@entries
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :length, :size
|
||||||
|
|
||||||
|
def self.allocate
|
||||||
|
Rubinius.primitive :constant_table_allocate
|
||||||
|
raise PrimitiveFailure, "ConstantTable.allocate primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(hash=nil)
|
||||||
|
return unless hash
|
||||||
|
hash.each do |k, v|
|
||||||
|
self[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def duplicate
|
||||||
|
Rubinius.primitive :constant_table_duplicate
|
||||||
|
raise PrimitiveFailure, "ConstantTable#duplicate primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def dup
|
||||||
|
copy = duplicate
|
||||||
|
Rubinius.privately do
|
||||||
|
copy.initialize_copy self
|
||||||
|
end
|
||||||
|
copy
|
||||||
|
end
|
||||||
|
|
||||||
|
def name?(name)
|
||||||
|
Rubinius.primitive :constant_table_has_name
|
||||||
|
raise PrimitiveFailure, "ConstantTable#name? primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :has_name?, :name?
|
||||||
|
alias_method :include?, :name?
|
||||||
|
alias_method :member?, :name?
|
||||||
|
|
||||||
|
def keys
|
||||||
|
Rubinius.primitive :constant_table_keys
|
||||||
|
raise PrimitiveFailure, "ConstantTable#keys primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete(name)
|
||||||
|
Rubinius.primitive :constant_table_delete
|
||||||
|
raise PrimitiveFailure, "ConstantTable#delete primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def each
|
||||||
|
raise LocalJumpError, "no block given" unless block_given?
|
||||||
|
|
||||||
|
vals = values
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
max = @bins
|
||||||
|
|
||||||
|
while i < max
|
||||||
|
if entry = vals.at(i)
|
||||||
|
while entry
|
||||||
|
yield entry.name, entry.constant, entry.visibility
|
||||||
|
entry = entry.next
|
||||||
|
end
|
||||||
|
end
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def values
|
||||||
|
Rubinius.primitive :constant_table_values
|
||||||
|
raise PrimitiveFailure, "ConstantTable#values primitive failed"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
43
gxg/core/continuation.rb
Normal file
43
gxg/core/continuation.rb
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
class Continuation
|
||||||
|
# def self.create(task=nil)
|
||||||
|
# cont = self.allocate
|
||||||
|
# cont.setup(task)
|
||||||
|
# cont
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# def setup(task=nil)
|
||||||
|
# @task = task
|
||||||
|
# @value = nil
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# def call(*value)
|
||||||
|
# task = @task.dup
|
||||||
|
# if value.empty?
|
||||||
|
# @value = nil
|
||||||
|
# elsif value.size == 1
|
||||||
|
# @value = value.pop
|
||||||
|
# else
|
||||||
|
# @value = value
|
||||||
|
# end
|
||||||
|
# Rubinius::Task.current = task
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# alias_method :[], :call
|
||||||
|
end
|
||||||
|
|
||||||
|
module Kernel
|
||||||
|
def callcc
|
||||||
|
raise NotImplementedError, "call/cc is not implemented!"
|
||||||
|
# cont = Continuation.create
|
||||||
|
# # Task#dup appears as though it returns nil in the dup'd
|
||||||
|
# # task, kinda like fork().
|
||||||
|
# task = Rubinius::Task.current.dup
|
||||||
|
# if task
|
||||||
|
# cont.task = task
|
||||||
|
# yield cont
|
||||||
|
# else
|
||||||
|
# return cont.value
|
||||||
|
# end
|
||||||
|
end
|
||||||
|
module_function :callcc
|
||||||
|
end
|
||||||
90
gxg/core/ctype.rb
Normal file
90
gxg/core/ctype.rb
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
##
|
||||||
|
# Mixin containing byte classification methods.
|
||||||
|
#--
|
||||||
|
# isspace, islower, ... are not in MRI core library.
|
||||||
|
|
||||||
|
module Rubinius::CType
|
||||||
|
# The character literals (?x) are Fixnums in 1.8 and Strings in 1.9
|
||||||
|
# so we use literal values instead so this is 1.8/1.9 compatible.
|
||||||
|
|
||||||
|
# \n \r \t \f \v \a \b \e
|
||||||
|
def self.isctrl(num)
|
||||||
|
(num >= 7 and num <= 13) or num == 27
|
||||||
|
end
|
||||||
|
|
||||||
|
# ' ' \n \t \r \f \v
|
||||||
|
def self.isspace(num)
|
||||||
|
num == 32 or (num >= 9 and num <= 13)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.isupper(num)
|
||||||
|
num >= 65 and num <= 90
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.islower(num)
|
||||||
|
num >= 97 and num <= 122
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.isdigit(num)
|
||||||
|
num >= 48 and num <= 57
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.isalnum(num)
|
||||||
|
islower(num) or isupper(num) or isdigit(num)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.toupper!(num)
|
||||||
|
num - 32
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.toupper(num)
|
||||||
|
islower(num) ? toupper!(num) : num
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.tolower!(num)
|
||||||
|
num + 32
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.tolower(num)
|
||||||
|
isupper(num) ? tolower!(num) : num
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.toprint(num)
|
||||||
|
# The character literals (?x) are Fixnums in 1.8 and Strings in 1.9
|
||||||
|
# so we use literal values instead so this is 1.8/1.9 compatible.
|
||||||
|
case num
|
||||||
|
when 7; '\a'
|
||||||
|
when 8; '\b'
|
||||||
|
when 9; '\t'
|
||||||
|
when 10; '\n'
|
||||||
|
when 11; '\v'
|
||||||
|
when 12; '\f'
|
||||||
|
when 13; '\r'
|
||||||
|
when 27; '\e'
|
||||||
|
when 34; '\"'
|
||||||
|
when 35; Rubinius::Tuple['#$', '\#$', '#@', '\#@', '#{', '\#{', '#', '#']
|
||||||
|
when 92; '\\\\'
|
||||||
|
else
|
||||||
|
if num < 32 || num > 126
|
||||||
|
unprintable_chr(num)
|
||||||
|
else
|
||||||
|
num.chr
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.unprintable_chr(num)
|
||||||
|
str = "\\x00"
|
||||||
|
str.modify!
|
||||||
|
m = Rubinius::Mirror.reflect str
|
||||||
|
|
||||||
|
c = num.to_s(16).upcase
|
||||||
|
m.copy_from c, 0, c.size, 4-c.size
|
||||||
|
|
||||||
|
str
|
||||||
|
end
|
||||||
|
|
||||||
|
def toprint
|
||||||
|
Printed[self]
|
||||||
|
end
|
||||||
|
end
|
||||||
41
gxg/core/delegated_method.rb
Normal file
41
gxg/core/delegated_method.rb
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
##
|
||||||
|
# DelegatedMethod is used to implement Module#define_method
|
||||||
|
|
||||||
|
module Rubinius
|
||||||
|
class DelegatedMethod < Executable
|
||||||
|
def initialize(name, method, receiver, pass_self=false)
|
||||||
|
@name = name
|
||||||
|
@method = method
|
||||||
|
@receiver = receiver
|
||||||
|
@pass_self = pass_self
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
attr_reader :name
|
||||||
|
attr_reader :receiver
|
||||||
|
|
||||||
|
def call(called_object, called_method, *args, &block)
|
||||||
|
args.unshift called_object if @pass_self
|
||||||
|
@receiver.__send__(@method, *args, &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :activate, :call
|
||||||
|
|
||||||
|
def arity
|
||||||
|
@receiver.arity
|
||||||
|
end
|
||||||
|
|
||||||
|
def parameters
|
||||||
|
@receiver.parameters
|
||||||
|
end
|
||||||
|
|
||||||
|
def source_location
|
||||||
|
@receiver.source_location
|
||||||
|
end
|
||||||
|
|
||||||
|
def scope
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
29
gxg/core/deprecations.rb
Normal file
29
gxg/core/deprecations.rb
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
module Rubinius
|
||||||
|
DEPRECATIONS = {
|
||||||
|
# "Description" => "Alternative"
|
||||||
|
"Rubinius::Executable#primitive is deprecated." =>
|
||||||
|
"Use Rubinius::Executable#tags instead.",
|
||||||
|
"Non-UTF-8 encodings for String objects are deprecated." =>
|
||||||
|
"Use String objects encoded as UTF-8.",
|
||||||
|
"Encoding objects other than UTF-8 are deprecated." =>
|
||||||
|
"Use ByteString (when available) to passed text data with non-UTF-8 encodings through a program. Use transcoding facilities at IO boundaries to read from or write to non-UTF-8 encodings. Use File.transcode to transcode a file from one encoding to another.",
|
||||||
|
"Mutating a String object is deprecated." =>
|
||||||
|
"Use ByteString for binary data or MutableString for text (when available).",
|
||||||
|
"Insertion ordering of Hash is deprecated." =>
|
||||||
|
"Use OrderedHash when insertion ordering is desired.",
|
||||||
|
"OpenSSL is deprecated." =>
|
||||||
|
"Use the core cryptography and TLS libraries when available.",
|
||||||
|
"The Ruby stdlib is deprecated." =>
|
||||||
|
"Use core libraries (eg Socket) or alternative gems when available.",
|
||||||
|
"Rubygems and Bundler are deprecated." =>
|
||||||
|
"Use the IPFS library facility when available.",
|
||||||
|
"Use of the identifier 'type' at script scope is deprecated." =>
|
||||||
|
"Use an identifier not named 'type' at script scope.",
|
||||||
|
"Use of the identifier 'import' at script, class/module, method, block, or function scope is deprecated." =>
|
||||||
|
"Use an identifier not named 'import' at script, class/module, method, block, or function scope.",
|
||||||
|
"Defining a new method without removing an existing method definition for that name is deprecated." =>
|
||||||
|
"Defining a new method with the same name as an existing method will define multiple methods. Use `remove_method` followed by `def` to replace a method.",
|
||||||
|
"Fixnum and Bignum classes are deprecated" =>
|
||||||
|
"Use Integer instead of Fixnum or Bignum",
|
||||||
|
}
|
||||||
|
end
|
||||||
269
gxg/core/dir.rb
Normal file
269
gxg/core/dir.rb
Normal file
|
|
@ -0,0 +1,269 @@
|
||||||
|
class Dir
|
||||||
|
include Enumerable
|
||||||
|
|
||||||
|
def self.allocate
|
||||||
|
Rubinius.primitive :dir_allocate
|
||||||
|
raise PrimitiveFailure, "Dir.allocate primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(path, options=undefined)
|
||||||
|
path = Rubinius::Type.coerce_to_path path
|
||||||
|
|
||||||
|
if options.equal? undefined
|
||||||
|
enc = nil
|
||||||
|
else
|
||||||
|
options = Rubinius::Type.coerce_to options, Hash, :to_hash
|
||||||
|
enc = options[:encoding]
|
||||||
|
enc = Rubinius::Type.coerce_to_encoding enc if enc
|
||||||
|
end
|
||||||
|
|
||||||
|
Rubinius.invoke_primitive :dir_open, self, path, enc
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def close
|
||||||
|
Rubinius.primitive :dir_close
|
||||||
|
raise PrimitiveFailure, "Dir#close primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def closed?
|
||||||
|
Rubinius.primitive :dir_closed_p
|
||||||
|
raise PrimitiveFailure, "Dir#closed? primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def read
|
||||||
|
entry = Rubinius.invoke_primitive :dir_read, self
|
||||||
|
return unless entry
|
||||||
|
|
||||||
|
if Encoding.default_external == Encoding::US_ASCII && !entry.valid_encoding?
|
||||||
|
entry.force_encoding Encoding::ASCII_8BIT
|
||||||
|
return entry
|
||||||
|
end
|
||||||
|
|
||||||
|
enc = Encoding.default_internal
|
||||||
|
enc ? entry.encode(enc) : entry
|
||||||
|
end
|
||||||
|
|
||||||
|
def control(kind, pos)
|
||||||
|
Rubinius.primitive :dir_control
|
||||||
|
raise PrimitiveFailure, "Dir#__control__ primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
private :control
|
||||||
|
|
||||||
|
def self.open(path, options=undefined)
|
||||||
|
dir = new path, options
|
||||||
|
if block_given?
|
||||||
|
begin
|
||||||
|
value = yield dir
|
||||||
|
ensure
|
||||||
|
dir.close
|
||||||
|
end
|
||||||
|
|
||||||
|
return value
|
||||||
|
else
|
||||||
|
return dir
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.entries(path, options=undefined)
|
||||||
|
ret = []
|
||||||
|
|
||||||
|
open(path, options) do |dir|
|
||||||
|
while s = dir.read
|
||||||
|
ret << s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.exist?(path)
|
||||||
|
PrivateFile.directory?(path)
|
||||||
|
end
|
||||||
|
|
||||||
|
class << self
|
||||||
|
alias_method :exists?, :exist?
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.home(user=nil)
|
||||||
|
PrivateFile.expand_path("~#{user}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.[](*patterns)
|
||||||
|
if patterns.size == 1
|
||||||
|
pattern = Rubinius::Type.coerce_to_path(patterns[0])
|
||||||
|
return [] if pattern.empty?
|
||||||
|
patterns = glob_split pattern
|
||||||
|
end
|
||||||
|
|
||||||
|
glob patterns
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.glob(pattern, flags=0, &block)
|
||||||
|
if pattern.kind_of? Array
|
||||||
|
patterns = pattern
|
||||||
|
else
|
||||||
|
pattern = Rubinius::Type.coerce_to_path pattern
|
||||||
|
|
||||||
|
return [] if pattern.empty?
|
||||||
|
|
||||||
|
patterns = glob_split pattern
|
||||||
|
end
|
||||||
|
|
||||||
|
matches = []
|
||||||
|
index = 0
|
||||||
|
|
||||||
|
patterns.each do |pat|
|
||||||
|
pat = Rubinius::Type.coerce_to_path pat
|
||||||
|
enc = Rubinius::Type.ascii_compatible_encoding pat
|
||||||
|
Dir::Glob.glob pat, flags, matches
|
||||||
|
|
||||||
|
total = matches.size
|
||||||
|
while index < total
|
||||||
|
Rubinius::Type.encode_string matches[index], enc
|
||||||
|
index += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if block
|
||||||
|
matches.each(&block)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return matches
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.glob_split(pattern)
|
||||||
|
result = []
|
||||||
|
start = 0
|
||||||
|
m = Rubinius::Mirror.reflect pattern
|
||||||
|
while idx = m.find_string("\0", start)
|
||||||
|
result << pattern.byteslice(start, idx)
|
||||||
|
start = idx + 1
|
||||||
|
end
|
||||||
|
result << pattern.byteslice(start, pattern.bytesize)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.foreach(path)
|
||||||
|
return to_enum(:foreach, path) unless block_given?
|
||||||
|
|
||||||
|
open(path) do |dir|
|
||||||
|
while s = dir.read
|
||||||
|
yield s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.join_path(p1, p2, dirsep)
|
||||||
|
"#{p1}#{dirsep ? '/' : ''}#{p2}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.chdir(path = ENV['HOME'])
|
||||||
|
path = Rubinius::Type.coerce_to_path path
|
||||||
|
|
||||||
|
if block_given?
|
||||||
|
original_path = self.getwd
|
||||||
|
error = FFI::Platform::POSIX.chdir path
|
||||||
|
Errno.handle(path) if error != 0
|
||||||
|
|
||||||
|
begin
|
||||||
|
value = yield path
|
||||||
|
ensure
|
||||||
|
error = FFI::Platform::POSIX.chdir original_path
|
||||||
|
Errno.handle(original_path) if error != 0
|
||||||
|
end
|
||||||
|
|
||||||
|
return value
|
||||||
|
else
|
||||||
|
error = FFI::Platform::POSIX.chdir path
|
||||||
|
Errno.handle path if error != 0
|
||||||
|
error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.mkdir(path, mode = 0777)
|
||||||
|
error = FFI::Platform::POSIX.mkdir(Rubinius::Type.coerce_to_path(path), mode)
|
||||||
|
Errno.handle path if error != 0
|
||||||
|
error
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.rmdir(path)
|
||||||
|
error = FFI::Platform::POSIX.rmdir(Rubinius::Type.coerce_to_path(path))
|
||||||
|
Errno.handle path if error != 0
|
||||||
|
error
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.getwd
|
||||||
|
buf = String.pattern Rubinius::PATH_MAX, 0
|
||||||
|
wd = FFI::Platform::POSIX.getcwd(buf, buf.length)
|
||||||
|
Errno.handle unless wd
|
||||||
|
Rubinius::Type.external_string wd
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.chroot(path)
|
||||||
|
ret = FFI::Platform::POSIX.chroot Rubinius::Type.coerce_to_path(path)
|
||||||
|
Errno.handle path if ret != 0
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
|
||||||
|
def each
|
||||||
|
return to_enum unless block_given?
|
||||||
|
|
||||||
|
while s = read
|
||||||
|
yield s
|
||||||
|
end
|
||||||
|
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def fileno
|
||||||
|
Rubinius.primitive :dir_fileno
|
||||||
|
raise PrimitiveFailure, "Dir#fileno primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :path
|
||||||
|
|
||||||
|
alias_method :to_path, :path
|
||||||
|
|
||||||
|
SeekKind = 0
|
||||||
|
RewindKind = 1
|
||||||
|
TellKind = 2
|
||||||
|
|
||||||
|
def pos
|
||||||
|
control TellKind, 0
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :tell, :pos
|
||||||
|
|
||||||
|
def pos=(position)
|
||||||
|
seek(position)
|
||||||
|
|
||||||
|
position
|
||||||
|
end
|
||||||
|
|
||||||
|
def seek(position)
|
||||||
|
control SeekKind, position
|
||||||
|
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def rewind
|
||||||
|
control RewindKind, 0
|
||||||
|
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
"#<#{self.class}:#{object_id.to_s(16)} @path=#{@path}>"
|
||||||
|
end
|
||||||
|
|
||||||
|
class << self
|
||||||
|
alias_method :pwd, :getwd
|
||||||
|
alias_method :delete, :rmdir
|
||||||
|
alias_method :unlink, :rmdir
|
||||||
|
end
|
||||||
|
end
|
||||||
460
gxg/core/dir_glob.rb
Normal file
460
gxg/core/dir_glob.rb
Normal file
|
|
@ -0,0 +1,460 @@
|
||||||
|
class Dir
|
||||||
|
module Glob
|
||||||
|
class Node
|
||||||
|
def initialize(nxt, flags)
|
||||||
|
@flags = flags
|
||||||
|
@next = nxt
|
||||||
|
@separator = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
attr_writer :separator
|
||||||
|
|
||||||
|
def separator
|
||||||
|
@separator || "/"
|
||||||
|
end
|
||||||
|
|
||||||
|
def path_join(parent, ent)
|
||||||
|
return ent unless parent
|
||||||
|
if parent == "/"
|
||||||
|
"/#{ent}"
|
||||||
|
else
|
||||||
|
"#{parent}#{separator}#{ent}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class ConstantDirectory < Node
|
||||||
|
def initialize(nxt, flags, dir)
|
||||||
|
super nxt, flags
|
||||||
|
@dir = dir
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def call(env, path)
|
||||||
|
full = path_join(path, @dir)
|
||||||
|
|
||||||
|
# Don't check if full exists. It just costs us time
|
||||||
|
# and the downstream node will be able to check properly.
|
||||||
|
@next.call env, full
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class ConstantEntry < Node
|
||||||
|
def initialize(nxt, flags, name)
|
||||||
|
super nxt, flags
|
||||||
|
@name = name
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def call(env, parent)
|
||||||
|
path = path_join(parent, @name)
|
||||||
|
|
||||||
|
if File.exist? path
|
||||||
|
env.matches << path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class RootDirectory < Node
|
||||||
|
def call(env, path)
|
||||||
|
@next.call env, "/"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class RecursiveDirectories < Node
|
||||||
|
def call(env, start)
|
||||||
|
return if !start || !File.exist?(start)
|
||||||
|
|
||||||
|
# Even though the recursive entry is zero width
|
||||||
|
# in this case, its left separator is still the
|
||||||
|
# dominant one, so we fix things up to use it.
|
||||||
|
switched = @next.dup
|
||||||
|
switched.separator = @separator
|
||||||
|
switched.call env, start
|
||||||
|
|
||||||
|
stack = [start]
|
||||||
|
|
||||||
|
allow_dots = ((@flags & File::FNM_DOTMATCH) != 0)
|
||||||
|
|
||||||
|
until stack.empty?
|
||||||
|
path = stack.pop
|
||||||
|
begin
|
||||||
|
dir = Dir.new(path)
|
||||||
|
rescue Errno::ENOTDIR
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
while ent = dir.read
|
||||||
|
next if ent == "." || ent == ".."
|
||||||
|
full = path_join(path, ent)
|
||||||
|
stat = File::Stat.lstat full
|
||||||
|
|
||||||
|
if stat.directory? and (allow_dots or ent.getbyte(0) != 46) # ?.
|
||||||
|
stack << full
|
||||||
|
@next.call env, full
|
||||||
|
end
|
||||||
|
end
|
||||||
|
dir.close
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class StartRecursiveDirectories < Node
|
||||||
|
def call(env, start)
|
||||||
|
raise "invalid usage" if start
|
||||||
|
|
||||||
|
# Even though the recursive entry is zero width
|
||||||
|
# in this case, its left separator is still the
|
||||||
|
# dominant one, so we fix things up to use it.
|
||||||
|
if @separator
|
||||||
|
switched = @next.dup
|
||||||
|
switched.separator = @separator
|
||||||
|
switched.call env, start
|
||||||
|
else
|
||||||
|
@next.call env, start
|
||||||
|
end
|
||||||
|
|
||||||
|
stack = []
|
||||||
|
|
||||||
|
allow_dots = ((@flags & File::FNM_DOTMATCH) != 0)
|
||||||
|
|
||||||
|
dir = Dir.new(".")
|
||||||
|
while ent = dir.read
|
||||||
|
next if ent == "." || ent == ".."
|
||||||
|
stat = File::Stat.lstat ent
|
||||||
|
|
||||||
|
if stat.directory? and (allow_dots or ent.getbyte(0) != 46) # ?.
|
||||||
|
stack << ent
|
||||||
|
@next.call env, ent
|
||||||
|
end
|
||||||
|
end
|
||||||
|
dir.close
|
||||||
|
|
||||||
|
until stack.empty?
|
||||||
|
path = stack.pop
|
||||||
|
dir = Dir.new(path)
|
||||||
|
while ent = dir.read
|
||||||
|
next if ent == "." || ent == ".."
|
||||||
|
full = path_join(path, ent)
|
||||||
|
stat = File::Stat.lstat full
|
||||||
|
|
||||||
|
if stat.directory? and ent.getbyte(0) != 46 # ?.
|
||||||
|
stack << full
|
||||||
|
@next.call env, full
|
||||||
|
end
|
||||||
|
end
|
||||||
|
dir.close
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Match < Node
|
||||||
|
def initialize(nxt, flags, glob)
|
||||||
|
super nxt, flags
|
||||||
|
@glob = glob || ""
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def match?(str)
|
||||||
|
File.fnmatch @glob, str, @flags
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class DirectoryMatch < Match
|
||||||
|
def initialize(nxt, flags, glob)
|
||||||
|
super
|
||||||
|
|
||||||
|
@glob.gsub! "**", "*"
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def call(env, path)
|
||||||
|
return if path and !File.exist?("#{path}/.")
|
||||||
|
|
||||||
|
dir = Dir.new(path ? path : ".")
|
||||||
|
while ent = dir.read
|
||||||
|
if match? ent
|
||||||
|
full = path_join(path, ent)
|
||||||
|
|
||||||
|
if File.directory? full
|
||||||
|
@next.call env, full
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
dir.close
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class EntryMatch < Match
|
||||||
|
def call(env, path)
|
||||||
|
return if path and !File.exist?("#{path}/.")
|
||||||
|
|
||||||
|
begin
|
||||||
|
dir = Dir.new(path ? path : ".")
|
||||||
|
rescue SystemCallError
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
while ent = dir.read
|
||||||
|
if match? ent
|
||||||
|
env.matches << path_join(path, ent)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
dir.close
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class DirectoriesOnly < Node
|
||||||
|
def call(env, path)
|
||||||
|
if path and File.exist?("#{path}/.")
|
||||||
|
env.matches << "#{path}/"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Environment
|
||||||
|
attr_reader :matches
|
||||||
|
|
||||||
|
def initialize(matches=[])
|
||||||
|
@matches = matches
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.path_split(str)
|
||||||
|
start = 0
|
||||||
|
ret = []
|
||||||
|
|
||||||
|
last_match = nil
|
||||||
|
|
||||||
|
while match = %r!/+!.match_from(str, start)
|
||||||
|
cur_start, cur_end = match.full
|
||||||
|
ret << str.byteslice(start, cur_start - start)
|
||||||
|
ret << str.byteslice(cur_start, cur_end - cur_start)
|
||||||
|
|
||||||
|
start = cur_end
|
||||||
|
|
||||||
|
last_match = match
|
||||||
|
end
|
||||||
|
|
||||||
|
if last_match
|
||||||
|
ret << last_match.post_match
|
||||||
|
else
|
||||||
|
ret << str
|
||||||
|
end
|
||||||
|
|
||||||
|
# Trim from end
|
||||||
|
if !ret.empty?
|
||||||
|
while s = ret.last and s.empty?
|
||||||
|
ret.pop
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.single_compile(glob, flags=0)
|
||||||
|
parts = path_split(glob)
|
||||||
|
|
||||||
|
if glob.getbyte(-1) == 47 # ?/
|
||||||
|
last = DirectoriesOnly.new nil, flags
|
||||||
|
else
|
||||||
|
file = parts.pop
|
||||||
|
if /^[a-zA-Z0-9._]+$/.match(file)
|
||||||
|
last = ConstantEntry.new nil, flags, file
|
||||||
|
else
|
||||||
|
last = EntryMatch.new nil, flags, file
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
until parts.empty?
|
||||||
|
last.separator = parts.pop
|
||||||
|
dir = parts.pop
|
||||||
|
|
||||||
|
if dir == "**"
|
||||||
|
if parts.empty?
|
||||||
|
last = StartRecursiveDirectories.new last, flags
|
||||||
|
else
|
||||||
|
last = RecursiveDirectories.new last, flags
|
||||||
|
end
|
||||||
|
elsif /^[^\*\?\]]+$/.match(dir)
|
||||||
|
while /^[^\*\?\]]+$/.match(parts[-2])
|
||||||
|
next_sep = parts.pop
|
||||||
|
next_sect = parts.pop
|
||||||
|
|
||||||
|
dir = next_sect << next_sep << dir
|
||||||
|
end
|
||||||
|
|
||||||
|
last = ConstantDirectory.new last, flags, dir
|
||||||
|
elsif !dir.empty?
|
||||||
|
last = DirectoryMatch.new last, flags, dir
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if glob.getbyte(0) == 47 # ?/
|
||||||
|
last = RootDirectory.new last, flags
|
||||||
|
end
|
||||||
|
|
||||||
|
last
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.run(node, matches=[])
|
||||||
|
env = Environment.new(matches)
|
||||||
|
node.call env, nil
|
||||||
|
env.matches
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.glob_cache
|
||||||
|
@glob_cache
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.glob(pattern, flags, matches=[])
|
||||||
|
# Rubygems uses Dir[] as a glorified File.exist? to check for multiple
|
||||||
|
# extensions. So we went ahead and sped up that specific case.
|
||||||
|
|
||||||
|
if flags == 0 and
|
||||||
|
m = /^([a-zA-Z0-9_.\/\s]*[a-zA-Z0-9_.])(?:\{([^{}\/\*\?]*)\})?$/.match(pattern)
|
||||||
|
# no meta characters, so this is a glorified
|
||||||
|
# File.exist? check. We allow for a brace expansion
|
||||||
|
# only as a suffix.
|
||||||
|
|
||||||
|
if braces = m[2]
|
||||||
|
stem = m[1]
|
||||||
|
|
||||||
|
braces.split(",").each do |s|
|
||||||
|
path = "#{stem}#{s}"
|
||||||
|
if File.exist? path
|
||||||
|
matches << path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Split strips an empty closing part, so we need to add it back in
|
||||||
|
if braces.getbyte(-1) == 44 # ?,
|
||||||
|
matches << stem if File.exist? stem
|
||||||
|
end
|
||||||
|
else
|
||||||
|
matches << pattern if File.exist?(pattern)
|
||||||
|
end
|
||||||
|
|
||||||
|
return matches
|
||||||
|
end
|
||||||
|
|
||||||
|
ec_key = nil
|
||||||
|
|
||||||
|
if gc = @glob_cache
|
||||||
|
ec_key = [pattern, flags]
|
||||||
|
if patterns = gc.retrieve(ec_key)
|
||||||
|
patterns.each do |node|
|
||||||
|
run node, matches
|
||||||
|
end
|
||||||
|
|
||||||
|
return matches
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if pattern.include? "{"
|
||||||
|
patterns = compile(pattern, flags)
|
||||||
|
|
||||||
|
gc.set ec_key, patterns if ec_key
|
||||||
|
|
||||||
|
patterns.each do |node|
|
||||||
|
run node, matches
|
||||||
|
end
|
||||||
|
elsif node = single_compile(pattern, flags)
|
||||||
|
gc.set ec_key, [node] if ec_key
|
||||||
|
|
||||||
|
run node, matches
|
||||||
|
else
|
||||||
|
matches
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.compile(pattern, flags=0, patterns=[])
|
||||||
|
escape = (flags & File::FNM_NOESCAPE) == 0
|
||||||
|
|
||||||
|
rbrace = nil
|
||||||
|
lbrace = nil
|
||||||
|
|
||||||
|
# Do a quick search for a { to start the search better
|
||||||
|
i = pattern.index("{")
|
||||||
|
|
||||||
|
# If there was a { found, then search
|
||||||
|
if i
|
||||||
|
nest = 0
|
||||||
|
|
||||||
|
while i < pattern.size
|
||||||
|
char = pattern[i]
|
||||||
|
|
||||||
|
if char == "{"
|
||||||
|
lbrace = i if nest == 0
|
||||||
|
nest += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if char == "}"
|
||||||
|
nest -= 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if nest == 0
|
||||||
|
rbrace = i
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
if char == "\\" and escape
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# There was a full {} expression detected, expand each part of it
|
||||||
|
# recursively.
|
||||||
|
if lbrace and rbrace
|
||||||
|
pos = lbrace
|
||||||
|
front = pattern[0...lbrace]
|
||||||
|
back = pattern[(rbrace + 1)..-1]
|
||||||
|
|
||||||
|
while pos < rbrace
|
||||||
|
nest = 0
|
||||||
|
pos += 1
|
||||||
|
last = pos
|
||||||
|
|
||||||
|
while pos < rbrace and not (pattern[pos] == "," and nest == 0)
|
||||||
|
nest += 1 if pattern[pos] == "{"
|
||||||
|
nest -= 1 if pattern[pos] == "}"
|
||||||
|
|
||||||
|
if pattern[pos] == "\\" and escape
|
||||||
|
pos += 1
|
||||||
|
break if pos == rbrace
|
||||||
|
end
|
||||||
|
|
||||||
|
pos += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
brace_pattern = "#{front}#{pattern[last...pos]}#{back}"
|
||||||
|
|
||||||
|
compile brace_pattern, flags, patterns
|
||||||
|
end
|
||||||
|
|
||||||
|
# No braces found, match the pattern normally
|
||||||
|
else
|
||||||
|
# Don't use .glob here because this code can detect properly
|
||||||
|
# if a { is a brace or a just a normal character, but .glob can't.
|
||||||
|
# if .glob is used and there is a { as a normal character, it will
|
||||||
|
# recurse forever.
|
||||||
|
if node = single_compile(pattern, flags)
|
||||||
|
patterns << node
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
patterns
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
570
gxg/core/encoding.rb
Normal file
570
gxg/core/encoding.rb
Normal file
|
|
@ -0,0 +1,570 @@
|
||||||
|
class EncodingError < StandardError
|
||||||
|
end
|
||||||
|
|
||||||
|
class Encoding
|
||||||
|
class UndefinedConversionError < EncodingError
|
||||||
|
attr_accessor :source_encoding_name
|
||||||
|
attr_accessor :destination_encoding_name
|
||||||
|
attr_accessor :source_encoding
|
||||||
|
attr_accessor :destination_encoding
|
||||||
|
attr_accessor :error_char
|
||||||
|
|
||||||
|
private :source_encoding_name=
|
||||||
|
private :destination_encoding_name=
|
||||||
|
private :source_encoding=
|
||||||
|
private :destination_encoding=
|
||||||
|
private :error_char=
|
||||||
|
end
|
||||||
|
|
||||||
|
class InvalidByteSequenceError < EncodingError
|
||||||
|
attr_accessor :source_encoding_name
|
||||||
|
attr_accessor :destination_encoding_name
|
||||||
|
attr_accessor :source_encoding
|
||||||
|
attr_accessor :destination_encoding
|
||||||
|
attr_accessor :error_bytes
|
||||||
|
attr_accessor :readagain_bytes
|
||||||
|
attr_writer :incomplete_input
|
||||||
|
|
||||||
|
private :source_encoding_name=
|
||||||
|
private :destination_encoding_name=
|
||||||
|
private :source_encoding=
|
||||||
|
private :destination_encoding=
|
||||||
|
private :error_bytes=
|
||||||
|
private :readagain_bytes=
|
||||||
|
private :incomplete_input=
|
||||||
|
|
||||||
|
def initialize(message="")
|
||||||
|
super(message)
|
||||||
|
|
||||||
|
@incomplete_input = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def incomplete_input?
|
||||||
|
@incomplete_input
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class ConverterNotFoundError < EncodingError
|
||||||
|
end
|
||||||
|
|
||||||
|
class CompatibilityError < EncodingError
|
||||||
|
end
|
||||||
|
|
||||||
|
class Transcoding
|
||||||
|
attr_accessor :source
|
||||||
|
attr_accessor :target
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
"#<#{super} #{source} to #{target}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Converter
|
||||||
|
attr_accessor :source_encoding
|
||||||
|
attr_accessor :destination_encoding
|
||||||
|
attr_reader :replacement
|
||||||
|
attr_reader :options
|
||||||
|
|
||||||
|
def self.allocate
|
||||||
|
Rubinius.primitive :encoding_converter_allocate
|
||||||
|
raise PrimitiveFailure, "Encoding::Converter.allocate primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.asciicompat_encoding(string_or_encoding)
|
||||||
|
encoding = Rubinius::Type.try_convert_to_encoding string_or_encoding
|
||||||
|
|
||||||
|
return if not encoding or undefined.equal? encoding
|
||||||
|
return if encoding.ascii_compatible?
|
||||||
|
|
||||||
|
transcoding = TranscodingMap[encoding.name.upcase]
|
||||||
|
return unless transcoding and transcoding.size == 1
|
||||||
|
|
||||||
|
Encoding.find transcoding.keys.first.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.search_convpath(from, to, options=undefined)
|
||||||
|
new(from, to, options).convpath
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(from, to, options=undefined)
|
||||||
|
@source_encoding = Rubinius::Type.coerce_to_encoding from
|
||||||
|
@destination_encoding = Rubinius::Type.coerce_to_encoding to
|
||||||
|
|
||||||
|
if options.kind_of? Fixnum
|
||||||
|
@options = options
|
||||||
|
elsif !undefined.equal? options
|
||||||
|
options = Rubinius::Type.coerce_to options, Hash, :to_hash
|
||||||
|
|
||||||
|
@options = 0
|
||||||
|
unless options.empty?
|
||||||
|
@options |= INVALID_REPLACE if options[:invalid] == :replace
|
||||||
|
@options |= UNDEF_REPLACE if options[:undef] == :replace
|
||||||
|
|
||||||
|
if options[:newline] == :universal or options[:universal_newline]
|
||||||
|
@options |= UNIVERSAL_NEWLINE_DECORATOR
|
||||||
|
end
|
||||||
|
|
||||||
|
if options[:newline] == :crlf or options[:crlf_newline]
|
||||||
|
@options |= CRLF_NEWLINE_DECORATOR
|
||||||
|
end
|
||||||
|
|
||||||
|
if options[:newline] == :cr or options[:cr_newline]
|
||||||
|
@options |= CR_NEWLINE_DECORATOR
|
||||||
|
end
|
||||||
|
|
||||||
|
@options |= XML_TEXT_DECORATOR if options[:xml] == :text
|
||||||
|
if options[:xml] == :attr
|
||||||
|
@options |= XML_ATTR_CONTENT_DECORATOR
|
||||||
|
@options |= XML_ATTR_QUOTE_DECORATOR
|
||||||
|
end
|
||||||
|
|
||||||
|
replacement = options[:replace]
|
||||||
|
end
|
||||||
|
else
|
||||||
|
@options = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
source_name = @source_encoding.name.upcase.to_sym
|
||||||
|
dest_name = @destination_encoding.name.upcase.to_sym
|
||||||
|
|
||||||
|
unless source_name == dest_name
|
||||||
|
@convpath, @converters = TranscodingPath[source_name, dest_name]
|
||||||
|
end
|
||||||
|
|
||||||
|
unless @convpath
|
||||||
|
conversion = "(#{@source_encoding.name} to #{@destination_encoding.name})"
|
||||||
|
msg = "code converter not found #{conversion}"
|
||||||
|
raise ConverterNotFoundError, msg
|
||||||
|
end
|
||||||
|
|
||||||
|
if @options & (INVALID_REPLACE | UNDEF_REPLACE | UNDEF_HEX_CHARREF)
|
||||||
|
if replacement.nil?
|
||||||
|
if @destination_encoding == Encoding::UTF_8
|
||||||
|
@replacement = "\xef\xbf\xbd".force_encoding(Encoding::UTF_8)
|
||||||
|
else
|
||||||
|
@replacement = "?".force_encoding(Encoding::US_ASCII)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
@replacement = Rubinius::Type.coerce_to replacement, String, :to_str
|
||||||
|
end
|
||||||
|
|
||||||
|
replacement_encoding_name = @replacement.encoding.name.upcase
|
||||||
|
@replacement_converters = []
|
||||||
|
|
||||||
|
@convpath.each do |enc|
|
||||||
|
name = enc.to_s.upcase
|
||||||
|
next if name == replacement_encoding_name
|
||||||
|
|
||||||
|
_, converters = TranscodingPath[replacement_encoding_name, enc]
|
||||||
|
@replacement_converters << name << converters
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def convert(str)
|
||||||
|
str = StringValue(str)
|
||||||
|
|
||||||
|
dest = ""
|
||||||
|
status = primitive_convert str.dup, dest, nil, nil, @options | PARTIAL_INPUT
|
||||||
|
|
||||||
|
if status == :invalid_byte_sequence or
|
||||||
|
status == :undefined_conversion or
|
||||||
|
status == :incomplete_input
|
||||||
|
raise last_error
|
||||||
|
end
|
||||||
|
|
||||||
|
if status == :finished
|
||||||
|
raise ArgumentError, "converter already finished"
|
||||||
|
end
|
||||||
|
|
||||||
|
if status != :source_buffer_empty
|
||||||
|
raise RuntimeError, "unexpected result of Encoding::Converter#primitive_convert: #{status}"
|
||||||
|
end
|
||||||
|
|
||||||
|
dest
|
||||||
|
end
|
||||||
|
|
||||||
|
def primitive_convert(source, target, offset=nil, size=nil, options=0)
|
||||||
|
source = StringValue(source) if source
|
||||||
|
target = StringValue(target)
|
||||||
|
|
||||||
|
if offset.nil?
|
||||||
|
offset = target.bytesize
|
||||||
|
else
|
||||||
|
offset = Rubinius::Type.coerce_to offset, Fixnum, :to_int
|
||||||
|
end
|
||||||
|
|
||||||
|
if size.nil?
|
||||||
|
size = -1
|
||||||
|
else
|
||||||
|
size = Rubinius::Type.coerce_to size, Fixnum, :to_int
|
||||||
|
|
||||||
|
if size < 0
|
||||||
|
raise ArgumentError, "byte size is negative"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if offset < 0
|
||||||
|
raise ArgumentError, "byte offset is negative"
|
||||||
|
end
|
||||||
|
|
||||||
|
if offset > target.bytesize
|
||||||
|
raise ArgumentError, "byte offset is greater than destination buffer size"
|
||||||
|
end
|
||||||
|
|
||||||
|
if !options.kind_of? Fixnum
|
||||||
|
opts = Rubinius::Type.coerce_to options, Hash, :to_hash
|
||||||
|
|
||||||
|
options = 0
|
||||||
|
options |= PARTIAL_INPUT if opts[:partial_input]
|
||||||
|
options |= AFTER_OUTPUT if opts[:after_output]
|
||||||
|
end
|
||||||
|
|
||||||
|
if primitive_errinfo.first == :invalid_byte_sequence
|
||||||
|
source.prepend putback
|
||||||
|
end
|
||||||
|
|
||||||
|
Rubinius.invoke_primitive(:encoding_converter_primitive_convert,
|
||||||
|
self, source, target, offset, size, options)
|
||||||
|
end
|
||||||
|
|
||||||
|
def putback(maxbytes=nil)
|
||||||
|
Rubinius.primitive :encoding_converter_putback
|
||||||
|
|
||||||
|
putback maxbytes
|
||||||
|
end
|
||||||
|
|
||||||
|
def finish
|
||||||
|
dest = ""
|
||||||
|
status = primitive_convert nil, dest
|
||||||
|
|
||||||
|
if status == :invalid_byte_sequence or
|
||||||
|
status == :undefined_conversion or
|
||||||
|
status == :incomplete_input
|
||||||
|
raise last_error
|
||||||
|
end
|
||||||
|
|
||||||
|
if status != :finished
|
||||||
|
raise RuntimeError, "unexpected result of Encoding::Converter#finish: #{status}"
|
||||||
|
end
|
||||||
|
|
||||||
|
dest
|
||||||
|
end
|
||||||
|
|
||||||
|
def last_error
|
||||||
|
error = Rubinius.invoke_primitive :encoding_converter_last_error, self
|
||||||
|
return if error.nil?
|
||||||
|
|
||||||
|
result = error[:result]
|
||||||
|
error_bytes = error[:error_bytes]
|
||||||
|
error_bytes_msg = error_bytes.dump
|
||||||
|
source_encoding_name = error[:source_encoding_name]
|
||||||
|
destination_encoding_name = error[:destination_encoding_name]
|
||||||
|
|
||||||
|
case result
|
||||||
|
when :invalid_byte_sequence
|
||||||
|
read_again_string = error[:read_again_string]
|
||||||
|
if read_again_string
|
||||||
|
msg = "#{error_bytes_msg} followed by #{read_again_string.dump} on #{source_encoding_name}"
|
||||||
|
else
|
||||||
|
msg = "#{error_bytes_msg} on #{source_encoding_name}"
|
||||||
|
end
|
||||||
|
|
||||||
|
exc = InvalidByteSequenceError.new msg
|
||||||
|
when :incomplete_input
|
||||||
|
msg = "incomplete #{error_bytes_msg} on #{source_encoding_name}"
|
||||||
|
|
||||||
|
exc = InvalidByteSequenceError.new msg
|
||||||
|
when :undefined_conversion
|
||||||
|
error_char = error_bytes
|
||||||
|
if codepoint = error[:codepoint]
|
||||||
|
error_bytes_msg = "U+%04X" % codepoint
|
||||||
|
end
|
||||||
|
|
||||||
|
if source_encoding_name.to_sym == @source_encoding.name and
|
||||||
|
destination_encoding_name.to_sym == @destination_encoding.name
|
||||||
|
msg = "#{error_bytes_msg} from #{source_encoding_name} to #{destination_encoding_name}"
|
||||||
|
else
|
||||||
|
msg = "#{error_bytes_msg} to #{destination_encoding_name} in conversion from #{source_encoding_name}"
|
||||||
|
transcoder = @converters.first
|
||||||
|
msg << " to #{transcoder.target}"
|
||||||
|
end
|
||||||
|
|
||||||
|
exc = UndefinedConversionError.new msg
|
||||||
|
end
|
||||||
|
|
||||||
|
Rubinius.privately do
|
||||||
|
exc.source_encoding_name = source_encoding_name
|
||||||
|
src = Rubinius::Type.try_convert_to_encoding source_encoding_name
|
||||||
|
exc.source_encoding = src unless undefined.equal? src
|
||||||
|
|
||||||
|
exc.destination_encoding_name = destination_encoding_name
|
||||||
|
dst = Rubinius::Type.try_convert_to_encoding destination_encoding_name
|
||||||
|
exc.destination_encoding = dst unless undefined.equal? dst
|
||||||
|
|
||||||
|
if error_char
|
||||||
|
error_char.force_encoding src unless undefined.equal? src
|
||||||
|
exc.error_char = error_char
|
||||||
|
end
|
||||||
|
|
||||||
|
if result == :invalid_byte_sequence or result == :incomplete_input
|
||||||
|
exc.error_bytes = error_bytes.force_encoding Encoding::ASCII_8BIT
|
||||||
|
|
||||||
|
if bytes = error[:read_again_bytes]
|
||||||
|
exc.readagain_bytes = bytes.force_encoding Encoding::ASCII_8BIT
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if result == :invalid_byte_sequence
|
||||||
|
exc.incomplete_input = false
|
||||||
|
elsif result == :incomplete_input
|
||||||
|
exc.incomplete_input = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
exc
|
||||||
|
end
|
||||||
|
|
||||||
|
def primitive_errinfo
|
||||||
|
Rubinius.primitive :encoding_converter_primitive_errinfo
|
||||||
|
raise PrimitiveFailure, "Encoding::Converter#primitive_errinfo primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def convpath
|
||||||
|
path = []
|
||||||
|
a = 0
|
||||||
|
b = @convpath.size - 1
|
||||||
|
|
||||||
|
while a < b
|
||||||
|
path << [Encoding.find(@convpath[a].to_s), Encoding.find(@convpath[a + 1].to_s)]
|
||||||
|
a += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
path << "xml_text_escape" if @options & XML_TEXT_DECORATOR != 0
|
||||||
|
path << "xml_attr_content_escape" if @options & XML_ATTR_CONTENT_DECORATOR != 0
|
||||||
|
path << "xml_attr_quote" if @options & XML_ATTR_QUOTE_DECORATOR != 0
|
||||||
|
path << "universal_newline" if @options & UNIVERSAL_NEWLINE_DECORATOR != 0
|
||||||
|
path << "crlf_newline" if @options & CRLF_NEWLINE_DECORATOR != 0
|
||||||
|
path << "cr_newline" if @options & CR_NEWLINE_DECORATOR != 0
|
||||||
|
|
||||||
|
path
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
"#<Encoding::Converter: #{source_encoding.name} to #{destination_encoding.name}>"
|
||||||
|
end
|
||||||
|
|
||||||
|
def replacement=(str)
|
||||||
|
str = StringValue(str)
|
||||||
|
|
||||||
|
@replacement = str.encode(@destination_encoding)
|
||||||
|
end
|
||||||
|
|
||||||
|
class TranscodingPath
|
||||||
|
def self.paths
|
||||||
|
@paths
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.default_transcoders?
|
||||||
|
@transcoders_count == TranscodingMap.size
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.[](source, target)
|
||||||
|
key = "[#{source}, #{target}]"
|
||||||
|
|
||||||
|
path, converters = paths[key]
|
||||||
|
|
||||||
|
unless path
|
||||||
|
return unless path = search(source, target)
|
||||||
|
paths[key] = [path]
|
||||||
|
end
|
||||||
|
|
||||||
|
unless converters
|
||||||
|
converters = get_converters path
|
||||||
|
paths[key][1] = converters
|
||||||
|
end
|
||||||
|
|
||||||
|
return path, converters
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.search(source, target)
|
||||||
|
if entry = TranscodingMap[source]
|
||||||
|
if entry[target]
|
||||||
|
return [source, target]
|
||||||
|
else
|
||||||
|
visited = { source => true }
|
||||||
|
search = { [source] => entry }
|
||||||
|
|
||||||
|
until search.empty?
|
||||||
|
path, table = search.shift
|
||||||
|
|
||||||
|
table.each do |key, _|
|
||||||
|
next if visited.key? key
|
||||||
|
next unless entry = TranscodingMap[key]
|
||||||
|
|
||||||
|
return path << key << target if entry[target]
|
||||||
|
|
||||||
|
unless visited.key? key
|
||||||
|
search[path.dup << key] = entry
|
||||||
|
visited[key] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.get_converters(path)
|
||||||
|
converters = []
|
||||||
|
total = path.size - 1
|
||||||
|
i = 0
|
||||||
|
|
||||||
|
while i < total
|
||||||
|
entry = TranscodingMap[path[i]][path[i + 1]]
|
||||||
|
|
||||||
|
if entry.kind_of? String
|
||||||
|
lib = "#{Rubinius::ENC_PATH}/#{entry}"
|
||||||
|
Rubinius::NativeMethod.load_extension lib, entry
|
||||||
|
|
||||||
|
entry = TranscodingMap[path[i]][path[i + 1]]
|
||||||
|
end
|
||||||
|
|
||||||
|
converters << entry
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
converters
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :name
|
||||||
|
attr_reader :dummy
|
||||||
|
|
||||||
|
alias_method :to_s, :name
|
||||||
|
alias_method :dummy?, :dummy
|
||||||
|
|
||||||
|
def self.compatible?(a, b)
|
||||||
|
Rubinius.primitive :encoding_compatible_p
|
||||||
|
raise PrimitiveFailure, "Encoding.compatible? primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def replicate(name)
|
||||||
|
Rubinius.primitive :encoding_replicate
|
||||||
|
raise PrimitiveFailure, "Encoding#replicate primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def ascii_compatible?
|
||||||
|
Rubinius.primitive :encoding_ascii_compatible_p
|
||||||
|
raise PrimitiveFailure, "Encoding#ascii_compatible? primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.aliases
|
||||||
|
aliases = {}
|
||||||
|
EncodingMap.each do |n, r|
|
||||||
|
index = r.last
|
||||||
|
next unless index
|
||||||
|
|
||||||
|
aname = r.first
|
||||||
|
aliases[aname] = EncodingList[index].name if aname
|
||||||
|
end
|
||||||
|
|
||||||
|
aliases
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.set_alias_index(name, obj)
|
||||||
|
key = name.upcase.to_sym
|
||||||
|
|
||||||
|
case obj
|
||||||
|
when Encoding
|
||||||
|
source_name = obj.name
|
||||||
|
when nil
|
||||||
|
EncodingMap[key][1] = nil
|
||||||
|
return
|
||||||
|
else
|
||||||
|
source_name = StringValue(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
entry = EncodingMap[source_name.upcase.to_sym]
|
||||||
|
raise ArgumentError, "unknown encoding name - #{source_name}" unless entry
|
||||||
|
index = entry.last
|
||||||
|
|
||||||
|
EncodingMap[key][1] = index
|
||||||
|
end
|
||||||
|
|
||||||
|
class << self
|
||||||
|
private :set_alias_index
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.default_external
|
||||||
|
if undefined.equal? @default_external
|
||||||
|
@default_external = find "external"
|
||||||
|
end
|
||||||
|
@default_external
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.default_external=(enc)
|
||||||
|
raise ArgumentError, "default external encoding cannot be nil" if enc.nil?
|
||||||
|
|
||||||
|
set_alias_index "external", enc
|
||||||
|
set_alias_index "filesystem", enc
|
||||||
|
@default_external = undefined
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.default_internal
|
||||||
|
# Rubinius internal encoding is always UTF-8
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.default_internal=(enc)
|
||||||
|
Rubinius::Logger.system.warn "Encoding.default_internal= is deprecated. Rubinius internal encoding is always UTF-8"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.find(name)
|
||||||
|
enc = Rubinius::Type.try_convert_to_encoding name
|
||||||
|
return enc unless undefined.equal? enc
|
||||||
|
|
||||||
|
raise ArgumentError, "unknown encoding name - #{name}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.list
|
||||||
|
EncodingList
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.locale_charmap
|
||||||
|
LocaleCharmap
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.name_list
|
||||||
|
EncodingMap.map do |n, r|
|
||||||
|
index = r.last
|
||||||
|
r.first or (index and EncodingList[index].name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
"#<Encoding:#{name}#{" (dummy)" if dummy?}>"
|
||||||
|
end
|
||||||
|
|
||||||
|
def names
|
||||||
|
entry = EncodingMap[name.upcase.to_sym]
|
||||||
|
names = [name]
|
||||||
|
EncodingMap.each do |k, r|
|
||||||
|
aname = r.first
|
||||||
|
names << aname if aname and r.last == entry.last
|
||||||
|
end
|
||||||
|
names
|
||||||
|
end
|
||||||
|
|
||||||
|
def _dump(depth)
|
||||||
|
name
|
||||||
|
end
|
||||||
|
|
||||||
|
def self._load(name)
|
||||||
|
find name
|
||||||
|
end
|
||||||
|
end
|
||||||
864
gxg/core/enumerable.rb
Normal file
864
gxg/core/enumerable.rb
Normal file
|
|
@ -0,0 +1,864 @@
|
||||||
|
# Just to save you 10 seconds, the reason we always use #each to extract
|
||||||
|
# elements instead of something simpler is because Enumerable can not assume
|
||||||
|
# any other methods than #each. If needed, class-specific versions of any of
|
||||||
|
# these methods can be written *in those classes* to override these.
|
||||||
|
|
||||||
|
module Enumerable
|
||||||
|
def chunk
|
||||||
|
raise ArgumentError, "no block given" unless block_given?
|
||||||
|
::Enumerator.new do |yielder|
|
||||||
|
previous = nil
|
||||||
|
accumulate = []
|
||||||
|
each do |element|
|
||||||
|
key = yield(element)
|
||||||
|
if key.nil? || (key.is_a?(Symbol) && key.to_s[0, 1] == "_")
|
||||||
|
yielder.yield [previous, accumulate] unless accumulate.empty?
|
||||||
|
accumulate = []
|
||||||
|
previous = nil
|
||||||
|
case key
|
||||||
|
when nil, :_separator
|
||||||
|
when :_alone
|
||||||
|
yielder.yield [key, [element]]
|
||||||
|
else
|
||||||
|
raise RuntimeError, "symbols beginning with an underscore are reserved"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if previous.nil? || previous == key
|
||||||
|
accumulate << element
|
||||||
|
else
|
||||||
|
yielder.yield [previous, accumulate] unless accumulate.empty?
|
||||||
|
accumulate = [element]
|
||||||
|
end
|
||||||
|
previous = key
|
||||||
|
end
|
||||||
|
end
|
||||||
|
yielder.yield [previous, accumulate] unless accumulate.empty?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def collect
|
||||||
|
if block_given?
|
||||||
|
ary = []
|
||||||
|
each do |*element|
|
||||||
|
ary << yield(*element)
|
||||||
|
end
|
||||||
|
ary
|
||||||
|
else
|
||||||
|
to_enum(:collect) { enumerator_size }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :map, :collect
|
||||||
|
|
||||||
|
def count(item = undefined)
|
||||||
|
seq = 0
|
||||||
|
if !undefined.equal?(item)
|
||||||
|
each do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
seq += 1 if item == element
|
||||||
|
end
|
||||||
|
elsif block_given?
|
||||||
|
each { |element| seq += 1 if yield(element) }
|
||||||
|
else
|
||||||
|
each { seq += 1 }
|
||||||
|
end
|
||||||
|
seq
|
||||||
|
end
|
||||||
|
|
||||||
|
def each_entry(*pass)
|
||||||
|
return to_enum(:each_entry, *pass) { enumerator_size } unless block_given?
|
||||||
|
each(*pass) do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
yield element
|
||||||
|
end
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def each_with_object(memo)
|
||||||
|
return to_enum(:each_with_object, memo) { enumerator_size } unless block_given?
|
||||||
|
each do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
yield element, memo
|
||||||
|
end
|
||||||
|
memo
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :with_object, :each_with_object
|
||||||
|
|
||||||
|
def flat_map
|
||||||
|
return to_enum(:flat_map) { enumerator_size } unless block_given?
|
||||||
|
|
||||||
|
array = []
|
||||||
|
each do |*element|
|
||||||
|
result = yield(*element)
|
||||||
|
|
||||||
|
value = Rubinius::Type.try_convert(result, Array, :to_ary) || result
|
||||||
|
|
||||||
|
if value.kind_of? Array
|
||||||
|
array.concat value
|
||||||
|
else
|
||||||
|
array.push value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
array
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :collect_concat, :flat_map
|
||||||
|
|
||||||
|
def lazy
|
||||||
|
Enumerator::Lazy.new(self, enumerator_size) do |yielder, *args|
|
||||||
|
yielder.<<(*args)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def enumerator_size
|
||||||
|
Rubinius::Type.object_respond_to?(self, :size) ? size : nil
|
||||||
|
end
|
||||||
|
private :enumerator_size
|
||||||
|
|
||||||
|
def group_by
|
||||||
|
return to_enum(:group_by) { enumerator_size } unless block_given?
|
||||||
|
|
||||||
|
h = {}
|
||||||
|
each do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
key = yield(element)
|
||||||
|
if h.key?(key)
|
||||||
|
h[key] << element
|
||||||
|
else
|
||||||
|
h[key] = [element]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
Rubinius::Type.infect h, self
|
||||||
|
h
|
||||||
|
end
|
||||||
|
|
||||||
|
def slice_before(pattern = undefined, &block)
|
||||||
|
pattern_given = !(undefined.equal? pattern)
|
||||||
|
|
||||||
|
raise ArgumentError, "cannot pass both pattern and block" if pattern_given && block_given?
|
||||||
|
raise ArgumentError, "a pattern or a block must be provided" if !pattern_given && !block_given?
|
||||||
|
|
||||||
|
block = Proc.new{ |elem| pattern === elem } if pattern_given
|
||||||
|
|
||||||
|
Enumerator.new do |yielder|
|
||||||
|
accumulator = nil
|
||||||
|
each do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
start_new = block.yield(element)
|
||||||
|
if start_new
|
||||||
|
yielder.yield accumulator if accumulator
|
||||||
|
accumulator = [element]
|
||||||
|
else
|
||||||
|
accumulator ||= []
|
||||||
|
accumulator << element
|
||||||
|
end
|
||||||
|
end
|
||||||
|
yielder.yield accumulator if accumulator
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def slice_after(pattern = undefined, &block)
|
||||||
|
pattern_given = !undefined.equal?(pattern)
|
||||||
|
|
||||||
|
raise ArgumentError, "cannot pass both pattern and block" if pattern_given && block_given?
|
||||||
|
raise ArgumentError, "a pattern or a block must be provided" if !pattern_given && !block_given?
|
||||||
|
|
||||||
|
block = Proc.new { |elem| pattern === elem } if pattern_given
|
||||||
|
|
||||||
|
Enumerator.new do |yielder|
|
||||||
|
accumulator = nil
|
||||||
|
each do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
end_chunk = block.yield(element)
|
||||||
|
accumulator ||= []
|
||||||
|
if end_chunk
|
||||||
|
accumulator << element
|
||||||
|
yielder.yield accumulator
|
||||||
|
accumulator = nil
|
||||||
|
else
|
||||||
|
accumulator << element
|
||||||
|
end
|
||||||
|
end
|
||||||
|
yielder.yield accumulator if accumulator
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def slice_when(&block)
|
||||||
|
raise ArgumentError, "wrong number of arguments (0 for 1)" unless block_given?
|
||||||
|
|
||||||
|
Enumerator.new do |enum|
|
||||||
|
ary = nil
|
||||||
|
last_after = nil
|
||||||
|
each_cons(2) do |before, after|
|
||||||
|
last_after = after
|
||||||
|
match = block.call before, after
|
||||||
|
|
||||||
|
ary ||= []
|
||||||
|
if match
|
||||||
|
ary << before
|
||||||
|
enum.yield ary
|
||||||
|
ary = []
|
||||||
|
else
|
||||||
|
ary << before
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
unless ary.nil?
|
||||||
|
ary << last_after
|
||||||
|
enum.yield ary
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def chunk_while(&block)
|
||||||
|
raise ArgumentError, "no block given" unless block_given?
|
||||||
|
|
||||||
|
slice_when { |before, after| !(yield before, after) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_a(*arg)
|
||||||
|
ary = []
|
||||||
|
each(*arg) do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
ary << element
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
Rubinius::Type.infect ary, self
|
||||||
|
ary
|
||||||
|
end
|
||||||
|
alias_method :entries, :to_a
|
||||||
|
|
||||||
|
def to_h(*arg)
|
||||||
|
h = {}
|
||||||
|
each_with_index(*arg) do |element, i|
|
||||||
|
unless element.respond_to?(:to_ary)
|
||||||
|
raise TypeError, "wrong element type #{element.class} at #{i} (expected array)"
|
||||||
|
end
|
||||||
|
|
||||||
|
ary = element.to_ary
|
||||||
|
if ary.size != 2
|
||||||
|
raise ArgumentError, "wrong array length at #{i} (expected 2, was #{ary.size})"
|
||||||
|
end
|
||||||
|
|
||||||
|
h[ary[0]] = ary[1]
|
||||||
|
end
|
||||||
|
h
|
||||||
|
end
|
||||||
|
|
||||||
|
def zip(*args)
|
||||||
|
args.map! do |a|
|
||||||
|
if a.respond_to? :to_ary
|
||||||
|
a.to_ary
|
||||||
|
else
|
||||||
|
a.to_enum(:each)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
results = []
|
||||||
|
i = 0
|
||||||
|
each do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
entry = args.inject([element]) do |ary, a|
|
||||||
|
ary << case a
|
||||||
|
when Array
|
||||||
|
a[i]
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
a.next
|
||||||
|
rescue StopIteration
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
yield entry if block_given?
|
||||||
|
|
||||||
|
results << entry
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil if block_given?
|
||||||
|
results
|
||||||
|
end
|
||||||
|
|
||||||
|
def each_with_index(*args)
|
||||||
|
return to_enum(:each_with_index, *args) { enumerator_size } unless block_given?
|
||||||
|
|
||||||
|
idx = 0
|
||||||
|
each(*args) do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
yield element, idx
|
||||||
|
idx += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def grep(pattern)
|
||||||
|
ary = []
|
||||||
|
|
||||||
|
each do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
if pattern === element
|
||||||
|
Regexp.set_block_last_match
|
||||||
|
if block_given?
|
||||||
|
ary << yield(element)
|
||||||
|
else
|
||||||
|
ary << element
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ary
|
||||||
|
end
|
||||||
|
|
||||||
|
def grep_v(pattern)
|
||||||
|
ary = []
|
||||||
|
|
||||||
|
each do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
unless pattern === element
|
||||||
|
if block_given?
|
||||||
|
ary << yield(element)
|
||||||
|
else
|
||||||
|
ary << element
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ary
|
||||||
|
end
|
||||||
|
|
||||||
|
def sort(&prc)
|
||||||
|
ary = to_a
|
||||||
|
ary.frozen? ? ary.sort(&prc) : ary.sort!(&prc)
|
||||||
|
end
|
||||||
|
|
||||||
|
class SortedElement
|
||||||
|
def initialize(val, sort_id)
|
||||||
|
@value, @sort_id = val, sort_id
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
attr_reader :value
|
||||||
|
attr_reader :sort_id
|
||||||
|
|
||||||
|
def <=>(other)
|
||||||
|
@sort_id <=> other.sort_id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def sort_by
|
||||||
|
return to_enum(:sort_by) { enumerator_size } unless block_given?
|
||||||
|
|
||||||
|
# Transform each value to a tuple with the value and it's sort by value
|
||||||
|
sort_values = map do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
SortedElement.new(element, yield(element))
|
||||||
|
end.to_a
|
||||||
|
|
||||||
|
# Now sort the tuple according to the sort by value
|
||||||
|
sort_values.sort!
|
||||||
|
|
||||||
|
# Now strip of the tuple leaving the original value
|
||||||
|
sort_values.map! { |ary| ary.value }
|
||||||
|
end
|
||||||
|
|
||||||
|
def inject(initial=undefined, sym=undefined)
|
||||||
|
if !block_given? or !undefined.equal?(sym)
|
||||||
|
if undefined.equal?(sym)
|
||||||
|
sym = initial
|
||||||
|
initial = undefined
|
||||||
|
end
|
||||||
|
|
||||||
|
# Do the sym version
|
||||||
|
|
||||||
|
sym = sym.to_sym
|
||||||
|
|
||||||
|
each do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
if undefined.equal? initial
|
||||||
|
initial = element
|
||||||
|
else
|
||||||
|
initial = initial.__send__(sym, element)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Block version
|
||||||
|
else
|
||||||
|
each do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
if undefined.equal? initial
|
||||||
|
initial = element
|
||||||
|
else
|
||||||
|
initial = yield(initial, element)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
undefined.equal?(initial) ? nil : initial
|
||||||
|
end
|
||||||
|
alias_method :reduce, :inject
|
||||||
|
|
||||||
|
def all?
|
||||||
|
if block_given?
|
||||||
|
each { |*element| return false unless yield(*element) }
|
||||||
|
else
|
||||||
|
each { return false unless Rubinius.single_block_arg }
|
||||||
|
end
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def any?
|
||||||
|
if block_given?
|
||||||
|
each { |*element| return true if yield(*element) }
|
||||||
|
else
|
||||||
|
each { return true if Rubinius.single_block_arg }
|
||||||
|
end
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def cycle(many=nil)
|
||||||
|
unless block_given?
|
||||||
|
return to_enum(:cycle, many) do
|
||||||
|
Rubinius::EnumerableHelper.cycle_size(enumerator_size, many)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if many
|
||||||
|
many = Rubinius::Type.coerce_to_collection_index many
|
||||||
|
return nil if many <= 0
|
||||||
|
else
|
||||||
|
many = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
cache = []
|
||||||
|
each do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
cache << element
|
||||||
|
yield element
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil if cache.empty?
|
||||||
|
|
||||||
|
if many
|
||||||
|
i = 0
|
||||||
|
many -= 1
|
||||||
|
while i < many
|
||||||
|
cache.each { |element| yield element }
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
while true
|
||||||
|
cache.each { |element| yield element }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def drop(n)
|
||||||
|
n = Rubinius::Type.coerce_to_collection_index n
|
||||||
|
raise ArgumentError, "attempt to drop negative size" if n < 0
|
||||||
|
|
||||||
|
ary = to_a
|
||||||
|
return [] if n > ary.size
|
||||||
|
ary[n...ary.size]
|
||||||
|
end
|
||||||
|
|
||||||
|
def drop_while
|
||||||
|
return to_enum(:drop_while) unless block_given?
|
||||||
|
|
||||||
|
ary = []
|
||||||
|
dropping = true
|
||||||
|
each do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
ary << element unless dropping &&= yield(element)
|
||||||
|
end
|
||||||
|
|
||||||
|
ary
|
||||||
|
end
|
||||||
|
|
||||||
|
def each_cons(num)
|
||||||
|
n = Rubinius::Type.coerce_to_collection_index num
|
||||||
|
raise ArgumentError, "invalid size: #{n}" if n <= 0
|
||||||
|
|
||||||
|
unless block_given?
|
||||||
|
return to_enum(:each_cons, num) do
|
||||||
|
enum_size = enumerator_size
|
||||||
|
if enum_size.nil?
|
||||||
|
nil
|
||||||
|
elsif enum_size == 0 || enum_size < n
|
||||||
|
0
|
||||||
|
else
|
||||||
|
enum_size - n + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
array = []
|
||||||
|
each do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
array << element
|
||||||
|
array.shift if array.size > n
|
||||||
|
yield array.dup if array.size == n
|
||||||
|
end
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def each_slice(slice_size)
|
||||||
|
n = Rubinius::Type.coerce_to_collection_index slice_size
|
||||||
|
raise ArgumentError, "invalid slice size: #{n}" if n <= 0
|
||||||
|
|
||||||
|
unless block_given?
|
||||||
|
return to_enum(:each_slice, slice_size) do
|
||||||
|
enum_size = enumerator_size
|
||||||
|
enum_size.nil? ? nil : (enum_size.to_f / n).ceil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
a = []
|
||||||
|
each do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
a << element
|
||||||
|
if a.length == n
|
||||||
|
yield a
|
||||||
|
a = []
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
yield a unless a.empty?
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def find(ifnone=nil)
|
||||||
|
return to_enum(:find, ifnone) unless block_given?
|
||||||
|
|
||||||
|
each do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
return element if yield(element)
|
||||||
|
end
|
||||||
|
|
||||||
|
ifnone.call if ifnone
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :detect, :find
|
||||||
|
|
||||||
|
def find_all
|
||||||
|
return to_enum(:find_all) { enumerator_size } unless block_given?
|
||||||
|
|
||||||
|
ary = []
|
||||||
|
each do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
ary << element if yield(element)
|
||||||
|
end
|
||||||
|
ary
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :select, :find_all
|
||||||
|
|
||||||
|
def find_index(value=undefined)
|
||||||
|
if undefined.equal? value
|
||||||
|
return to_enum(:find_index) unless block_given?
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
each do |*args|
|
||||||
|
return i if yield(*args)
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
i = 0
|
||||||
|
each do
|
||||||
|
e = Rubinius.single_block_arg
|
||||||
|
return i if e == value
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def first(n=undefined)
|
||||||
|
return __take__(n) unless undefined.equal?(n)
|
||||||
|
each do
|
||||||
|
return Rubinius.single_block_arg
|
||||||
|
end
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def min(n=nil)
|
||||||
|
if n.nil?
|
||||||
|
min = undefined
|
||||||
|
each do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
if undefined.equal? min
|
||||||
|
min = element
|
||||||
|
else
|
||||||
|
comp = block_given? ? yield(element, min) : element <=> min
|
||||||
|
unless comp
|
||||||
|
raise ArgumentError, "comparison of #{element.class} with #{min} failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
if Comparable.compare_int(comp) < 0
|
||||||
|
min = element
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
undefined.equal?(min) ? nil : min
|
||||||
|
else
|
||||||
|
if block_given?
|
||||||
|
sort { |a, b| yield a, b }.take n
|
||||||
|
else
|
||||||
|
sort.take n
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def max(n=nil)
|
||||||
|
if n.nil?
|
||||||
|
max = undefined
|
||||||
|
each do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
if undefined.equal? max
|
||||||
|
max = element
|
||||||
|
else
|
||||||
|
comp = block_given? ? yield(element, max) : element <=> max
|
||||||
|
unless comp
|
||||||
|
raise ArgumentError, "comparison of #{element.class} with #{max} failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
if Comparable.compare_int(comp) > 0
|
||||||
|
max = element
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
undefined.equal?(max) ? nil : max
|
||||||
|
else
|
||||||
|
if block_given?
|
||||||
|
sort { |a, b| yield a, b }.reverse.take n
|
||||||
|
else
|
||||||
|
sort.reverse.take n
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def max_by(n=nil)
|
||||||
|
return to_enum(:max_by, n) { enumerator_size } unless block_given?
|
||||||
|
|
||||||
|
if n.nil?
|
||||||
|
max_element = nil
|
||||||
|
max_result = undefined
|
||||||
|
|
||||||
|
each do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
result = yield element
|
||||||
|
|
||||||
|
if undefined.equal?(max_result) or \
|
||||||
|
Rubinius::Type.coerce_to_comparison(max_result, result) < 0
|
||||||
|
max_element = element
|
||||||
|
max_result = result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
max_element
|
||||||
|
else
|
||||||
|
sort_by { |element| yield element }.reverse.take n
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def min_by(n=nil)
|
||||||
|
return to_enum(:min_by, n) { enumerator_size } unless block_given?
|
||||||
|
|
||||||
|
if n.nil?
|
||||||
|
min_element = nil
|
||||||
|
min_result = undefined
|
||||||
|
|
||||||
|
each do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
result = yield element
|
||||||
|
|
||||||
|
if undefined.equal?(min_result) or \
|
||||||
|
Rubinius::Type.coerce_to_comparison(min_result, result) > 0
|
||||||
|
min_element = element
|
||||||
|
min_result = result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
min_element
|
||||||
|
else
|
||||||
|
sort_by { |element| yield element }.take n
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.sort_proc
|
||||||
|
@sort_proc ||= Proc.new do |a, b|
|
||||||
|
unless ret = a <=> b
|
||||||
|
raise ArgumentError, "Improper spaceship value"
|
||||||
|
end
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def minmax(&block)
|
||||||
|
block = Enumerable.sort_proc unless block
|
||||||
|
first_time = true
|
||||||
|
min, max = nil
|
||||||
|
|
||||||
|
each do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
if first_time
|
||||||
|
min = max = element
|
||||||
|
first_time = false
|
||||||
|
else
|
||||||
|
unless min_cmp = block.call(min, element)
|
||||||
|
raise ArgumentError, "comparison failed"
|
||||||
|
end
|
||||||
|
min = element if min_cmp > 0
|
||||||
|
|
||||||
|
unless max_cmp = block.call(max, element)
|
||||||
|
raise ArgumentError, "comparison failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
max = element if max_cmp < 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
[min, max]
|
||||||
|
end
|
||||||
|
|
||||||
|
def minmax_by(&block)
|
||||||
|
return to_enum(:minmax_by) { enumerator_size } unless block_given?
|
||||||
|
|
||||||
|
min_element = nil
|
||||||
|
min_result = undefined
|
||||||
|
|
||||||
|
max_element = nil
|
||||||
|
max_result = undefined
|
||||||
|
|
||||||
|
each do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
result = yield element
|
||||||
|
|
||||||
|
if undefined.equal?(min_result) or \
|
||||||
|
Rubinius::Type.coerce_to_comparison(min_result, result) > 0
|
||||||
|
min_element = element
|
||||||
|
min_result = result
|
||||||
|
end
|
||||||
|
|
||||||
|
if undefined.equal?(max_result) or \
|
||||||
|
Rubinius::Type.coerce_to_comparison(max_result, result) < 0
|
||||||
|
max_element = element
|
||||||
|
max_result = result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
[min_element, max_element]
|
||||||
|
end
|
||||||
|
|
||||||
|
def none?
|
||||||
|
if block_given?
|
||||||
|
each { |*element| return false if yield(*element) }
|
||||||
|
else
|
||||||
|
each { return false if Rubinius.single_block_arg }
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
def one?
|
||||||
|
found_one = false
|
||||||
|
|
||||||
|
if block_given?
|
||||||
|
each do |*element|
|
||||||
|
if yield(*element)
|
||||||
|
return false if found_one
|
||||||
|
found_one = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
each do
|
||||||
|
if Rubinius.single_block_arg
|
||||||
|
return false if found_one
|
||||||
|
found_one = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
found_one
|
||||||
|
end
|
||||||
|
|
||||||
|
def partition
|
||||||
|
return to_enum(:partition) { enumerator_size } unless block_given?
|
||||||
|
|
||||||
|
left = []
|
||||||
|
right = []
|
||||||
|
each do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
yield(element) ? left.push(element) : right.push(element)
|
||||||
|
end
|
||||||
|
|
||||||
|
return [left, right]
|
||||||
|
end
|
||||||
|
|
||||||
|
def reject
|
||||||
|
return to_enum(:reject) { enumerator_size } unless block_given?
|
||||||
|
|
||||||
|
ary = []
|
||||||
|
each do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
ary << element unless yield(element)
|
||||||
|
end
|
||||||
|
|
||||||
|
ary
|
||||||
|
end
|
||||||
|
|
||||||
|
def reverse_each(&block)
|
||||||
|
return to_enum(:reverse_each) { enumerator_size } unless block_given?
|
||||||
|
|
||||||
|
# There is no other way then to convert to an array first... see 1.9's source.
|
||||||
|
to_a.reverse_each(&block)
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def take(n)
|
||||||
|
n = Rubinius::Type.coerce_to_collection_index n
|
||||||
|
raise ArgumentError, "attempt to take negative size: #{n}" if n < 0
|
||||||
|
|
||||||
|
array = []
|
||||||
|
|
||||||
|
unless n <= 0
|
||||||
|
each do
|
||||||
|
element = Rubinius.single_block_arg
|
||||||
|
array << element
|
||||||
|
break if array.size >= n
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
array
|
||||||
|
end
|
||||||
|
alias_method :__take__, :take
|
||||||
|
private :__take__
|
||||||
|
|
||||||
|
def take_while
|
||||||
|
return to_enum(:take_while) unless block_given?
|
||||||
|
|
||||||
|
array = []
|
||||||
|
each do |element|
|
||||||
|
return array unless yield(element)
|
||||||
|
array << element
|
||||||
|
end
|
||||||
|
|
||||||
|
array
|
||||||
|
end
|
||||||
|
|
||||||
|
def include?(obj)
|
||||||
|
each { return true if Rubinius.single_block_arg == obj }
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :member?, :include?
|
||||||
|
|
||||||
|
end
|
||||||
13
gxg/core/enumerable_helper.rb
Normal file
13
gxg/core/enumerable_helper.rb
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
module Rubinius
|
||||||
|
module EnumerableHelper
|
||||||
|
def self.cycle_size(enum_size, many)
|
||||||
|
if many
|
||||||
|
many = Rubinius::Type.coerce_to_collection_index many
|
||||||
|
many = 0 if many < 0
|
||||||
|
(_size = enum_size).nil? ? nil : _size * many
|
||||||
|
else
|
||||||
|
enum_size.nil? ? nil : Float::INFINITY
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
500
gxg/core/enumerator.rb
Normal file
500
gxg/core/enumerator.rb
Normal file
|
|
@ -0,0 +1,500 @@
|
||||||
|
class Enumerator
|
||||||
|
include Enumerable
|
||||||
|
|
||||||
|
attr_writer :args
|
||||||
|
private :args=
|
||||||
|
|
||||||
|
attr_writer :size
|
||||||
|
private :size=
|
||||||
|
|
||||||
|
attr_reader :generator
|
||||||
|
protected :generator
|
||||||
|
|
||||||
|
attr_reader :lookahead
|
||||||
|
protected :lookahead
|
||||||
|
|
||||||
|
def initialize_enumerator(receiver, size, method_name, *method_args)
|
||||||
|
@object = receiver
|
||||||
|
@size = size
|
||||||
|
@iter = method_name
|
||||||
|
@args = method_args
|
||||||
|
@generator = nil
|
||||||
|
@lookahead = []
|
||||||
|
@feedvalue = nil
|
||||||
|
|
||||||
|
self
|
||||||
|
end
|
||||||
|
private :initialize_enumerator
|
||||||
|
|
||||||
|
def initialize(receiver_or_size=undefined, method_name=:each, *method_args, &block)
|
||||||
|
size = nil
|
||||||
|
|
||||||
|
if block_given?
|
||||||
|
unless undefined.equal? receiver_or_size
|
||||||
|
size = if receiver_or_size.nil? || receiver_or_size == Float::INFINITY || receiver_or_size.respond_to?(:call)
|
||||||
|
receiver_or_size
|
||||||
|
else
|
||||||
|
Rubinius::Type.coerce_to receiver_or_size, Integer, :to_int
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
receiver = Generator.new(&block)
|
||||||
|
else
|
||||||
|
if undefined.equal? receiver_or_size
|
||||||
|
raise ArgumentError, "Enumerator#initialize requires a block when called without arguments"
|
||||||
|
end
|
||||||
|
|
||||||
|
receiver = receiver_or_size
|
||||||
|
end
|
||||||
|
|
||||||
|
method_name = Rubinius::Type.coerce_to method_name, Symbol, :to_sym
|
||||||
|
|
||||||
|
initialize_enumerator receiver, size, method_name, *method_args
|
||||||
|
|
||||||
|
self
|
||||||
|
end
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def each(*args)
|
||||||
|
enumerator = self
|
||||||
|
new_args = @args
|
||||||
|
|
||||||
|
unless args.empty?
|
||||||
|
enumerator = dup
|
||||||
|
new_args = @args.empty? ? args : (@args + args)
|
||||||
|
end
|
||||||
|
|
||||||
|
Rubinius.privately do
|
||||||
|
enumerator.args = new_args
|
||||||
|
end
|
||||||
|
|
||||||
|
if block_given?
|
||||||
|
Rubinius.privately do
|
||||||
|
enumerator.each_with_block do |*yield_args|
|
||||||
|
yield(*yield_args)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
enumerator
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def each_with_block
|
||||||
|
@object.__send__ @iter, *@args do |*args|
|
||||||
|
ret = yield(*args)
|
||||||
|
unless @feedvalue.nil?
|
||||||
|
ret = @feedvalue
|
||||||
|
@feedvalue = nil
|
||||||
|
end
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
end
|
||||||
|
private :each_with_block
|
||||||
|
|
||||||
|
def each_with_index
|
||||||
|
return to_enum(:each_with_index) { size } unless block_given?
|
||||||
|
|
||||||
|
idx = 0
|
||||||
|
|
||||||
|
each do
|
||||||
|
o = Rubinius.single_block_arg
|
||||||
|
val = yield(o, idx)
|
||||||
|
idx += 1
|
||||||
|
val
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def next
|
||||||
|
return @lookahead.shift unless @lookahead.empty?
|
||||||
|
|
||||||
|
@generator ||= Iterator.new self
|
||||||
|
|
||||||
|
begin
|
||||||
|
return @generator.next if @generator.next?
|
||||||
|
rescue StopIteration
|
||||||
|
end
|
||||||
|
|
||||||
|
exception = StopIteration.new "iteration reached end"
|
||||||
|
Rubinius.privately do
|
||||||
|
exception.result = @generator.result
|
||||||
|
end
|
||||||
|
|
||||||
|
raise exception
|
||||||
|
end
|
||||||
|
|
||||||
|
def next_values
|
||||||
|
Array(self.next)
|
||||||
|
end
|
||||||
|
|
||||||
|
def peek
|
||||||
|
return @lookahead.first unless @lookahead.empty?
|
||||||
|
item = self.next
|
||||||
|
@lookahead << item
|
||||||
|
item
|
||||||
|
end
|
||||||
|
|
||||||
|
def peek_values
|
||||||
|
Array(self.peek).dup
|
||||||
|
end
|
||||||
|
|
||||||
|
def rewind
|
||||||
|
@object.rewind if @object.respond_to? :rewind
|
||||||
|
@generator.rewind if @generator
|
||||||
|
@lookahead = []
|
||||||
|
@feedvalue = nil
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def size
|
||||||
|
@size.respond_to?(:call) ? @size.call : @size
|
||||||
|
end
|
||||||
|
|
||||||
|
def with_index(offset=0)
|
||||||
|
if offset
|
||||||
|
offset = Rubinius::Type.coerce_to offset, Integer, :to_int
|
||||||
|
else
|
||||||
|
offset = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
return to_enum(:with_index, offset) { size } unless block_given?
|
||||||
|
|
||||||
|
each do
|
||||||
|
o = Rubinius.single_block_arg
|
||||||
|
val = yield(o, offset)
|
||||||
|
offset += 1
|
||||||
|
val
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def feed(val)
|
||||||
|
raise TypeError, "Feed value already set" unless @feedvalue.nil?
|
||||||
|
@feedvalue = val
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def dup
|
||||||
|
raise TypeError, "Can't copy iteration state" if @generator&.instance_variable_get(:@fiber)
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize_copy(other)
|
||||||
|
@generator = other.generator&.dup
|
||||||
|
@lookahead = other.lookahead.dup
|
||||||
|
super
|
||||||
|
end
|
||||||
|
private :initialize_copy
|
||||||
|
|
||||||
|
class Yielder
|
||||||
|
def initialize(&block)
|
||||||
|
raise LocalJumpError, "Expected a block to be given" unless block_given?
|
||||||
|
|
||||||
|
@proc = block
|
||||||
|
|
||||||
|
self
|
||||||
|
end
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def yield(*args)
|
||||||
|
@proc.call *args
|
||||||
|
end
|
||||||
|
|
||||||
|
def <<(*args)
|
||||||
|
self.yield(*args)
|
||||||
|
|
||||||
|
self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Generator
|
||||||
|
include Enumerable
|
||||||
|
|
||||||
|
def initialize(&block)
|
||||||
|
raise LocalJumpError, "Expected a block to be given" unless block_given?
|
||||||
|
|
||||||
|
@proc = block
|
||||||
|
|
||||||
|
self
|
||||||
|
end
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def each(*args)
|
||||||
|
enclosed_yield = Proc.new { |*enclosed_args| yield *enclosed_args }
|
||||||
|
|
||||||
|
@proc.call Yielder.new(&enclosed_yield), *args
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Lazy < self
|
||||||
|
class StopLazyError < Exception; end
|
||||||
|
|
||||||
|
def initialize(receiver, size=nil)
|
||||||
|
raise ArgumentError, "Lazy#initialize requires a block" unless block_given?
|
||||||
|
Rubinius.check_frozen
|
||||||
|
|
||||||
|
super(size) do |yielder, *each_args|
|
||||||
|
begin
|
||||||
|
receiver.each(*each_args) do |*args|
|
||||||
|
yield yielder, *args
|
||||||
|
end
|
||||||
|
rescue Exception
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self
|
||||||
|
end
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def to_enum(method_name=:each, *method_args, &block)
|
||||||
|
size = block_given? ? block : nil
|
||||||
|
ret = Lazy.allocate
|
||||||
|
|
||||||
|
Rubinius.privately do
|
||||||
|
ret.initialize_enumerator self, size, method_name, *method_args
|
||||||
|
end
|
||||||
|
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
alias_method :enum_for, :to_enum
|
||||||
|
|
||||||
|
def lazy
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :force, :to_a
|
||||||
|
|
||||||
|
def take(n)
|
||||||
|
n = Rubinius::Type.coerce_to n, Integer, :to_int
|
||||||
|
raise ArgumentError, "attempt to take negative size" if n < 0
|
||||||
|
|
||||||
|
current_size = enumerator_size
|
||||||
|
set_size = if current_size.kind_of?(Numeric)
|
||||||
|
n < current_size ? n : current_size
|
||||||
|
else
|
||||||
|
current_size
|
||||||
|
end
|
||||||
|
|
||||||
|
return to_enum(:cycle, 0).lazy if n.zero?
|
||||||
|
|
||||||
|
taken = 0
|
||||||
|
Lazy.new(self, set_size) do |yielder, *args|
|
||||||
|
if taken < n
|
||||||
|
yielder.yield(*args)
|
||||||
|
taken += 1
|
||||||
|
raise StopLazyError unless taken < n
|
||||||
|
else
|
||||||
|
raise StopLazyError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def drop(n)
|
||||||
|
n = Rubinius::Type.coerce_to n, Integer, :to_int
|
||||||
|
raise ArgumentError, "attempt to drop negative size" if n < 0
|
||||||
|
|
||||||
|
current_size = enumerator_size
|
||||||
|
set_size = if current_size.kind_of?(Integer)
|
||||||
|
n < current_size ? current_size - n : 0
|
||||||
|
else
|
||||||
|
current_size
|
||||||
|
end
|
||||||
|
|
||||||
|
dropped = 0
|
||||||
|
Lazy.new(self, set_size) do |yielder, *args|
|
||||||
|
if dropped < n
|
||||||
|
dropped += 1
|
||||||
|
else
|
||||||
|
yielder.yield(*args)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def take_while
|
||||||
|
raise ArgumentError, "Lazy#take_while requires a block" unless block_given?
|
||||||
|
|
||||||
|
Lazy.new(self, nil) do |yielder, *args|
|
||||||
|
if yield(*args)
|
||||||
|
yielder.yield(*args)
|
||||||
|
else
|
||||||
|
raise StopLazyError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def drop_while
|
||||||
|
raise ArgumentError, "Lazy#drop_while requires a block" unless block_given?
|
||||||
|
|
||||||
|
succeeding = true
|
||||||
|
Lazy.new(self, nil) do |yielder, *args|
|
||||||
|
if succeeding
|
||||||
|
unless yield(*args)
|
||||||
|
succeeding = false
|
||||||
|
yielder.yield(*args)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
yielder.yield(*args)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def select
|
||||||
|
raise ArgumentError, 'Lazy#{select,find_all} requires a block' unless block_given?
|
||||||
|
|
||||||
|
Lazy.new(self, nil) do |yielder, *args|
|
||||||
|
val = args.length >= 2 ? args : args.first
|
||||||
|
yielder.yield(*args) if yield(val)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
alias_method :find_all, :select
|
||||||
|
|
||||||
|
def reject
|
||||||
|
raise ArgumentError, "Lazy#reject requires a block" unless block_given?
|
||||||
|
|
||||||
|
Lazy.new(self, nil) do |yielder, *args|
|
||||||
|
val = args.length >= 2 ? args : args.first
|
||||||
|
yielder.yield(*args) unless yield(val)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def grep(pattern)
|
||||||
|
Lazy.new(self, nil) do |yielder, *args|
|
||||||
|
val = args.length >= 2 ? args : args.first
|
||||||
|
|
||||||
|
if pattern === val
|
||||||
|
Regexp.set_block_last_match
|
||||||
|
|
||||||
|
if block_given?
|
||||||
|
yielder.yield yield(val)
|
||||||
|
else
|
||||||
|
yielder.yield val
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def grep_v(pattern)
|
||||||
|
Lazy.new(self, nil) do |yielder, *args|
|
||||||
|
val = args.length >= 2 ? args : args.first
|
||||||
|
|
||||||
|
unless pattern === val
|
||||||
|
if block_given?
|
||||||
|
yielder.yield yield(val)
|
||||||
|
else
|
||||||
|
yielder.yield val
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def map
|
||||||
|
raise ArgumentError, 'Lazy#{map,collect} requires a block' unless block_given?
|
||||||
|
|
||||||
|
Lazy.new(self, enumerator_size) do |yielder, *args|
|
||||||
|
yielder.yield yield(*args)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
alias_method :collect, :map
|
||||||
|
|
||||||
|
def collect_concat
|
||||||
|
raise ArgumentError, 'Lazy#{collect_concat,flat_map} requires a block' unless block_given?
|
||||||
|
|
||||||
|
Lazy.new(self, nil) do |yielder, *args|
|
||||||
|
yield_ret = yield(*args)
|
||||||
|
|
||||||
|
if Rubinius::Type.object_respond_to?(yield_ret, :force) &&
|
||||||
|
Rubinius::Type.object_respond_to?(yield_ret, :each)
|
||||||
|
yield_ret.each do |v|
|
||||||
|
yielder.yield v
|
||||||
|
end
|
||||||
|
else
|
||||||
|
array = Rubinius::Type.check_convert_type yield_ret, Array, :to_ary
|
||||||
|
if array
|
||||||
|
array.each do |v|
|
||||||
|
yielder.yield v
|
||||||
|
end
|
||||||
|
else
|
||||||
|
yielder.yield yield_ret
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
alias_method :flat_map, :collect_concat
|
||||||
|
|
||||||
|
def zip(*lists)
|
||||||
|
return super(*lists) { |entry| yield entry } if block_given?
|
||||||
|
|
||||||
|
lists.map! do |list|
|
||||||
|
array = Rubinius::Type.check_convert_type list, Array, :to_ary
|
||||||
|
|
||||||
|
case
|
||||||
|
when array
|
||||||
|
array
|
||||||
|
when Rubinius::Type.object_respond_to?(list, :each)
|
||||||
|
list.to_enum :each
|
||||||
|
else
|
||||||
|
raise TypeError, "wrong argument type #{list.class} (must respond to :each)"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
index = 0
|
||||||
|
Lazy.new(self, enumerator_size) do |yielder, *args|
|
||||||
|
val = args.length >= 2 ? args : args.first
|
||||||
|
rests = lists.map { |list|
|
||||||
|
case list
|
||||||
|
when Array
|
||||||
|
list[index]
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
list.next
|
||||||
|
rescue StopIteration
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
}
|
||||||
|
yielder.yield [val, *rests]
|
||||||
|
index += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Iterator
|
||||||
|
STACK_SIZE = 1_048_576
|
||||||
|
|
||||||
|
attr_reader :result
|
||||||
|
|
||||||
|
def initialize(obj)
|
||||||
|
@object = obj
|
||||||
|
rewind
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def next?
|
||||||
|
!@done
|
||||||
|
end
|
||||||
|
|
||||||
|
def next
|
||||||
|
reset unless @fiber
|
||||||
|
|
||||||
|
val = @fiber.resume
|
||||||
|
|
||||||
|
raise StopIteration, "iteration has ended" if @done
|
||||||
|
|
||||||
|
return val
|
||||||
|
end
|
||||||
|
|
||||||
|
def rewind
|
||||||
|
@fiber = nil
|
||||||
|
@done = false
|
||||||
|
end
|
||||||
|
|
||||||
|
def reset
|
||||||
|
@done = false
|
||||||
|
@fiber = Fiber.new stack_size: STACK_SIZE do
|
||||||
|
obj = @object
|
||||||
|
@result = obj.each { |*val| Fiber.yield *val }
|
||||||
|
@done = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
294
gxg/core/env.rb
Normal file
294
gxg/core/env.rb
Normal file
|
|
@ -0,0 +1,294 @@
|
||||||
|
module Rubinius
|
||||||
|
class EnvironmentVariables
|
||||||
|
include Enumerable
|
||||||
|
|
||||||
|
def [](key)
|
||||||
|
value = getenv(StringValue(key))
|
||||||
|
if value
|
||||||
|
value = set_encoding value
|
||||||
|
value.freeze
|
||||||
|
end
|
||||||
|
value
|
||||||
|
end
|
||||||
|
|
||||||
|
def []=(key, value)
|
||||||
|
key = StringValue(key)
|
||||||
|
if value.nil?
|
||||||
|
unsetenv(key)
|
||||||
|
else
|
||||||
|
if setenv(key, StringValue(value), 1) != 0
|
||||||
|
Errno.handle("setenv")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
value
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :store, :[]=
|
||||||
|
|
||||||
|
def each_key
|
||||||
|
return to_enum(:each_key) { size } unless block_given?
|
||||||
|
|
||||||
|
each { |k, v| yield k }
|
||||||
|
end
|
||||||
|
|
||||||
|
def each_value
|
||||||
|
return to_enum(:each_value) { size } unless block_given?
|
||||||
|
|
||||||
|
each { |k, v| yield v }
|
||||||
|
end
|
||||||
|
|
||||||
|
def each
|
||||||
|
return to_enum(:each) { size } unless block_given?
|
||||||
|
|
||||||
|
env = environ()
|
||||||
|
ptr_size = FFI.type_size FFI.find_type(:pointer)
|
||||||
|
|
||||||
|
offset = 0
|
||||||
|
cur = env + offset
|
||||||
|
|
||||||
|
until cur.read_pointer.null?
|
||||||
|
entry = cur.read_pointer.read_string
|
||||||
|
key, value = entry.split '=', 2
|
||||||
|
value.taint if value
|
||||||
|
key.taint if key
|
||||||
|
|
||||||
|
key = set_encoding key
|
||||||
|
value = set_encoding value
|
||||||
|
|
||||||
|
yield key, value
|
||||||
|
|
||||||
|
offset += ptr_size
|
||||||
|
cur = env + offset
|
||||||
|
end
|
||||||
|
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :each_pair, :each
|
||||||
|
|
||||||
|
def delete(key)
|
||||||
|
existing_value = self[key]
|
||||||
|
if existing_value
|
||||||
|
self[key] = nil
|
||||||
|
elsif block_given?
|
||||||
|
yield key
|
||||||
|
end
|
||||||
|
existing_value
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_if(&block)
|
||||||
|
return to_enum(:delete_if) { size } unless block_given?
|
||||||
|
reject!(&block)
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def include?(key)
|
||||||
|
!self[key].nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :has_key?, :include?
|
||||||
|
alias_method :key?, :include?
|
||||||
|
|
||||||
|
# More efficient than using the one from Enumerable
|
||||||
|
alias_method :member?, :include?
|
||||||
|
|
||||||
|
def fetch(key, absent=undefined)
|
||||||
|
if block_given? and !undefined.equal?(absent)
|
||||||
|
warn "block supersedes default value argument"
|
||||||
|
end
|
||||||
|
|
||||||
|
if value = self[key]
|
||||||
|
return value
|
||||||
|
end
|
||||||
|
|
||||||
|
if block_given?
|
||||||
|
return yield(key)
|
||||||
|
elsif undefined.equal?(absent)
|
||||||
|
raise KeyError, "key not found"
|
||||||
|
end
|
||||||
|
|
||||||
|
return absent
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"ENV"
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
to_hash.inspect
|
||||||
|
end
|
||||||
|
|
||||||
|
def reject(&block)
|
||||||
|
to_hash.reject(&block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def reject!
|
||||||
|
return to_enum(:reject!) { size } unless block_given?
|
||||||
|
|
||||||
|
# Avoid deleting from environ while iterating because the
|
||||||
|
# OS can handle that in a million different bad ways.
|
||||||
|
|
||||||
|
keys = []
|
||||||
|
each { |k, v| keys << k if yield(k, v) }
|
||||||
|
keys.each { |k| delete k }
|
||||||
|
|
||||||
|
keys.empty? ? nil : self
|
||||||
|
end
|
||||||
|
|
||||||
|
def clear
|
||||||
|
# Avoid deleting from environ while iterating because the
|
||||||
|
# OS can handle that in a million different bad ways.
|
||||||
|
|
||||||
|
keys = []
|
||||||
|
each { |k, v| keys << k }
|
||||||
|
keys.each { |k| delete k }
|
||||||
|
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_value?(value)
|
||||||
|
each { |k, v| return true if v == value }
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :value?, :has_value?
|
||||||
|
|
||||||
|
def values_at(*params)
|
||||||
|
params.map{ |k| self[k] }
|
||||||
|
end
|
||||||
|
|
||||||
|
def index(value)
|
||||||
|
each do |k, v|
|
||||||
|
return k if v == value
|
||||||
|
end
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def invert
|
||||||
|
to_hash.invert
|
||||||
|
end
|
||||||
|
|
||||||
|
def key(value)
|
||||||
|
index(value)
|
||||||
|
end
|
||||||
|
|
||||||
|
def keys
|
||||||
|
keys = []
|
||||||
|
each { |k, v| keys << k }
|
||||||
|
keys
|
||||||
|
end
|
||||||
|
|
||||||
|
def values
|
||||||
|
vals = []
|
||||||
|
each { |k, v| vals << v }
|
||||||
|
vals
|
||||||
|
end
|
||||||
|
|
||||||
|
def empty?
|
||||||
|
each { return false }
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
def length
|
||||||
|
sz = 0
|
||||||
|
each { |k, v| sz += 1 }
|
||||||
|
sz
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :size, :length
|
||||||
|
|
||||||
|
def rehash
|
||||||
|
# No need to do anything, our keys are always strings
|
||||||
|
end
|
||||||
|
|
||||||
|
def replace(other)
|
||||||
|
clear
|
||||||
|
other.each { |k, v| self[k] = v }
|
||||||
|
end
|
||||||
|
|
||||||
|
def select(&blk)
|
||||||
|
return to_enum { size } unless block_given?
|
||||||
|
to_hash.select(&blk)
|
||||||
|
end
|
||||||
|
|
||||||
|
def shift
|
||||||
|
env = environ()
|
||||||
|
|
||||||
|
offset = 0
|
||||||
|
cur = env + offset
|
||||||
|
|
||||||
|
ptr = cur.read_pointer
|
||||||
|
return nil unless ptr
|
||||||
|
|
||||||
|
key, value = ptr.read_string.split "=", 2
|
||||||
|
|
||||||
|
return nil unless key
|
||||||
|
|
||||||
|
key.taint if key
|
||||||
|
value.taint if value
|
||||||
|
|
||||||
|
delete key
|
||||||
|
|
||||||
|
key = set_encoding key
|
||||||
|
value = set_encoding value
|
||||||
|
|
||||||
|
return [key, value]
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_a
|
||||||
|
ary = []
|
||||||
|
each { |k, v| ary << [k, v] }
|
||||||
|
ary
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_hash
|
||||||
|
hsh = {}
|
||||||
|
each { |k, v| hsh[k] = v }
|
||||||
|
hsh
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :to_h, :to_hash
|
||||||
|
|
||||||
|
def update(other)
|
||||||
|
if block_given?
|
||||||
|
other.each { |k, v| self[k] = yield(k, self[k], v) }
|
||||||
|
else
|
||||||
|
other.each { |k, v| self[k] = v }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def keep_if(&block)
|
||||||
|
return to_enum(:keep_if) { size } unless block_given?
|
||||||
|
select!(&block)
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def select!
|
||||||
|
return to_enum(:select!) { size } unless block_given?
|
||||||
|
reject! { |k, v| !yield(k, v) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def assoc(key)
|
||||||
|
key = StringValue(key)
|
||||||
|
value = self[key]
|
||||||
|
value ? [key, value] : nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def rassoc(value)
|
||||||
|
value = StringValue(value)
|
||||||
|
key = index(value)
|
||||||
|
key ? [key, value] : nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_encoding(value)
|
||||||
|
return unless value.kind_of? String
|
||||||
|
if Encoding.default_internal && value.ascii_only?
|
||||||
|
value.encode Encoding.default_internal, Encoding.find("locale")
|
||||||
|
else
|
||||||
|
value.force_encoding Encoding.find("locale")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private :set_encoding
|
||||||
|
end
|
||||||
|
end
|
||||||
42
gxg/core/errno.rb
Normal file
42
gxg/core/errno.rb
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
##
|
||||||
|
# Interface to the C errno integer.
|
||||||
|
|
||||||
|
module Errno
|
||||||
|
##
|
||||||
|
# Raises the appropriate SystemCallError exception with +additional+ as the
|
||||||
|
# message. Equivalent to MRI's rb_sys_fail().
|
||||||
|
#
|
||||||
|
# Unlike rb_sys_fail(), handle does not raise an exception if errno is 0.
|
||||||
|
|
||||||
|
def self.handle(additional = nil)
|
||||||
|
err = errno
|
||||||
|
return if err == 0
|
||||||
|
|
||||||
|
raise SystemCallError.new(additional, err)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.errno
|
||||||
|
FFI.errno
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.eql?(code)
|
||||||
|
FFI.errno == code
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.raise_waitreadable(message=nil)
|
||||||
|
raise IO::EAGAINWaitReadable, message
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.raise_waitwritable(message=nil)
|
||||||
|
raise IO::EAGAINWaitWritable, message
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.raise_eagain(message=nil)
|
||||||
|
raise_errno(message, Errno::EAGAIN::Errno)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.raise_errno(message, errno)
|
||||||
|
raise SystemCallError.new(message, errno)
|
||||||
|
end
|
||||||
|
private :raise_errno
|
||||||
|
end
|
||||||
578
gxg/core/exception.rb
Normal file
578
gxg/core/exception.rb
Normal file
|
|
@ -0,0 +1,578 @@
|
||||||
|
class Exception
|
||||||
|
attr_accessor :locations
|
||||||
|
attr_accessor :cause
|
||||||
|
attr_accessor :custom_backtrace
|
||||||
|
|
||||||
|
def initialize(message = nil)
|
||||||
|
@reason_message = message
|
||||||
|
@locations = nil
|
||||||
|
@backtrace = nil
|
||||||
|
@custom_backtrace = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def capture_backtrace!(offset=1)
|
||||||
|
@locations = Rubinius::VM.backtrace offset
|
||||||
|
end
|
||||||
|
|
||||||
|
def ==(other)
|
||||||
|
other.instance_of?(__class__) &&
|
||||||
|
message == other.message &&
|
||||||
|
backtrace == other.backtrace
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
if @reason_message
|
||||||
|
@reason_message.to_s
|
||||||
|
else
|
||||||
|
self.class.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# This is here rather than in yaml.rb because it contains "private"
|
||||||
|
# information, ie, the list of ivars. Putting it over in the yaml
|
||||||
|
# source means it's easy to forget about.
|
||||||
|
def to_yaml_properties
|
||||||
|
list = super
|
||||||
|
list.delete :@backtrace
|
||||||
|
list.delete :@custom_backtrace
|
||||||
|
return list
|
||||||
|
end
|
||||||
|
|
||||||
|
def message
|
||||||
|
@reason_message
|
||||||
|
end
|
||||||
|
|
||||||
|
# Needed to properly implement #exception, which must clone and call
|
||||||
|
# #initialize again, BUT not a subclasses initialize.
|
||||||
|
alias_method :__initialize__, :initialize
|
||||||
|
|
||||||
|
def backtrace
|
||||||
|
return @custom_backtrace if @custom_backtrace
|
||||||
|
|
||||||
|
if backtrace?
|
||||||
|
awesome_backtrace.to_mri
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Indicates if the Exception has a backtrace set
|
||||||
|
def backtrace?
|
||||||
|
(@backtrace || @locations) ? true : false
|
||||||
|
end
|
||||||
|
|
||||||
|
def awesome_backtrace
|
||||||
|
@backtrace ||= Rubinius::Backtrace.backtrace(@locations)
|
||||||
|
end
|
||||||
|
|
||||||
|
def render(header="An exception occurred", io=STDERR, color=true)
|
||||||
|
exception = self
|
||||||
|
exceptions = []
|
||||||
|
|
||||||
|
while exception
|
||||||
|
exceptions.unshift exception
|
||||||
|
exception = exception.cause
|
||||||
|
end
|
||||||
|
|
||||||
|
while exception = exceptions.shift
|
||||||
|
if custom_trace = exception.custom_backtrace
|
||||||
|
io.puts "User-defined backtrace:", "\n"
|
||||||
|
|
||||||
|
custom_trace.reverse_each { |line| io.puts line }
|
||||||
|
|
||||||
|
io.puts "\nBacktrace:", "\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
io.puts exception.awesome_backtrace.show("\n", color)
|
||||||
|
|
||||||
|
io.puts "\n#{exception.message} (#{exception.class})"
|
||||||
|
|
||||||
|
io.puts "\nCausing:", "\n" unless exceptions.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
message_lines = message.to_s.split("\n")
|
||||||
|
|
||||||
|
io.puts header
|
||||||
|
io.puts
|
||||||
|
io.puts " #{message_lines.shift} (#{self.class})"
|
||||||
|
|
||||||
|
message_lines.each do |line|
|
||||||
|
io.puts " #{line}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_backtrace(bt)
|
||||||
|
if bt.kind_of? Rubinius::Backtrace
|
||||||
|
@backtrace = bt
|
||||||
|
else
|
||||||
|
# See if we stashed a Backtrace object away, and use it.
|
||||||
|
if hidden_bt = Rubinius::Backtrace.detect_backtrace(bt)
|
||||||
|
@backtrace = hidden_bt
|
||||||
|
else
|
||||||
|
type_error = TypeError.new "backtrace must be Array of String"
|
||||||
|
case bt
|
||||||
|
when Array
|
||||||
|
if bt.all? { |s| s.kind_of? String }
|
||||||
|
@custom_backtrace = bt
|
||||||
|
else
|
||||||
|
raise type_error
|
||||||
|
end
|
||||||
|
when String
|
||||||
|
@custom_backtrace = [bt]
|
||||||
|
when nil
|
||||||
|
@custom_backtrace = nil
|
||||||
|
else
|
||||||
|
raise type_error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# This is important, because I subclass can just override #to_s and calling
|
||||||
|
# #message will call it. Using an alias doesn't achieve that.
|
||||||
|
def message
|
||||||
|
to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
s = self.to_s
|
||||||
|
if s.empty?
|
||||||
|
self.class.name
|
||||||
|
else
|
||||||
|
"#<#{self.class.name}: #{s}>"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class << self
|
||||||
|
alias_method :exception, :new
|
||||||
|
end
|
||||||
|
|
||||||
|
def exception(message=nil)
|
||||||
|
if message
|
||||||
|
unless message.equal? self
|
||||||
|
# As strange as this might seem, this IS actually the protocol
|
||||||
|
# that MRI implements for this. The explicit call to
|
||||||
|
# Exception#initialize (via __initialize__) is exactly what MRI
|
||||||
|
# does.
|
||||||
|
e = clone
|
||||||
|
Rubinius.privately { e.__initialize__(message) }
|
||||||
|
return e
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def location
|
||||||
|
[context.file.to_s, context.line]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class PrimitiveFailure < Exception
|
||||||
|
end
|
||||||
|
|
||||||
|
class ScriptError < Exception
|
||||||
|
end
|
||||||
|
|
||||||
|
class StandardError < Exception
|
||||||
|
end
|
||||||
|
|
||||||
|
class SignalException < Exception
|
||||||
|
end
|
||||||
|
|
||||||
|
class NoMemoryError < Exception
|
||||||
|
end
|
||||||
|
|
||||||
|
class ZeroDivisionError < StandardError
|
||||||
|
end
|
||||||
|
|
||||||
|
class ArgumentError < StandardError
|
||||||
|
def to_s
|
||||||
|
if @given and @expected
|
||||||
|
if @method_name
|
||||||
|
"method '#{@method_name}': given #{@given}, expected #{@expected}"
|
||||||
|
else
|
||||||
|
"given #{@given}, expected #{@expected}"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class UncaughtThrowError < ArgumentError
|
||||||
|
attr_accessor :tag
|
||||||
|
end
|
||||||
|
|
||||||
|
class IndexError < StandardError
|
||||||
|
end
|
||||||
|
|
||||||
|
class StopIteration < IndexError
|
||||||
|
end
|
||||||
|
|
||||||
|
class RangeError < StandardError
|
||||||
|
end
|
||||||
|
|
||||||
|
class FloatDomainError < RangeError
|
||||||
|
end
|
||||||
|
|
||||||
|
class LocalJumpError < StandardError
|
||||||
|
end
|
||||||
|
|
||||||
|
class NameError < StandardError
|
||||||
|
attr_reader :name
|
||||||
|
|
||||||
|
def initialize(*args, receiver: nil)
|
||||||
|
super(args.shift)
|
||||||
|
|
||||||
|
@name = args.shift
|
||||||
|
@receiver = receiver
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def receiver
|
||||||
|
if @receiver
|
||||||
|
@receiver
|
||||||
|
else
|
||||||
|
raise ArgumentError, 'no receiver is available'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class NoMethodError < NameError
|
||||||
|
attr_reader :name
|
||||||
|
attr_reader :args
|
||||||
|
|
||||||
|
def initialize(*arguments, **options)
|
||||||
|
super(arguments.shift, **options)
|
||||||
|
@name = arguments.shift
|
||||||
|
@args = arguments.shift
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
end
|
||||||
|
|
||||||
|
class RuntimeError < StandardError
|
||||||
|
end
|
||||||
|
|
||||||
|
class SecurityError < StandardError
|
||||||
|
end
|
||||||
|
|
||||||
|
class ThreadError < StandardError
|
||||||
|
end
|
||||||
|
|
||||||
|
class FiberError < StandardError
|
||||||
|
end
|
||||||
|
|
||||||
|
class TypeError < StandardError
|
||||||
|
end
|
||||||
|
|
||||||
|
class FloatDomainError < RangeError
|
||||||
|
end
|
||||||
|
|
||||||
|
class RegexpError < StandardError
|
||||||
|
end
|
||||||
|
|
||||||
|
class LoadError < ScriptError
|
||||||
|
attr_accessor :path
|
||||||
|
|
||||||
|
def initialize(message=nil, path: nil)
|
||||||
|
super message
|
||||||
|
@path = path
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
class InvalidExtensionError < LoadError
|
||||||
|
end
|
||||||
|
|
||||||
|
class MRIExtensionError < InvalidExtensionError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class NotImplementedError < ScriptError
|
||||||
|
end
|
||||||
|
|
||||||
|
class Interrupt < SignalException
|
||||||
|
def initialize(*args)
|
||||||
|
super(args.shift)
|
||||||
|
@name = args.shift
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
end
|
||||||
|
|
||||||
|
class IOError < StandardError
|
||||||
|
end
|
||||||
|
|
||||||
|
class EOFError < IOError
|
||||||
|
end
|
||||||
|
|
||||||
|
class LocalJumpError < StandardError
|
||||||
|
end
|
||||||
|
|
||||||
|
class SyntaxError < ScriptError
|
||||||
|
attr_accessor :column
|
||||||
|
attr_accessor :line
|
||||||
|
attr_accessor :file
|
||||||
|
attr_accessor :code
|
||||||
|
|
||||||
|
def self.from(message, column, line, code, file)
|
||||||
|
exc = new message
|
||||||
|
exc.file = file
|
||||||
|
exc.line = line
|
||||||
|
exc.column = column
|
||||||
|
exc.code = code
|
||||||
|
exc
|
||||||
|
end
|
||||||
|
|
||||||
|
def reason
|
||||||
|
@reason_message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class SystemExit < Exception
|
||||||
|
|
||||||
|
##
|
||||||
|
# Process exit status if this exception is raised
|
||||||
|
|
||||||
|
attr_reader :status
|
||||||
|
|
||||||
|
##
|
||||||
|
# Creates a SystemExit exception with optional status and message. If the
|
||||||
|
# status is omitted, Process::EXIT_SUCCESS is used.
|
||||||
|
#--
|
||||||
|
# *args is used to simulate optional prepended argument like MRI
|
||||||
|
|
||||||
|
def initialize(first=nil, *args)
|
||||||
|
if first.kind_of?(Fixnum)
|
||||||
|
status = first
|
||||||
|
super(*args)
|
||||||
|
else
|
||||||
|
status = Process::EXIT_SUCCESS
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
@status = status
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns true is exiting successfully, false if not. A successful exit is
|
||||||
|
# one with a status equal to 0 (zero). Any other status is considered a
|
||||||
|
# unsuccessful exit.
|
||||||
|
|
||||||
|
def success?
|
||||||
|
status == Process::EXIT_SUCCESS
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
class SystemCallError < StandardError
|
||||||
|
|
||||||
|
attr_reader :errno
|
||||||
|
|
||||||
|
def self.errno_error(message, errno, location)
|
||||||
|
Rubinius.primitive :exception_errno_error
|
||||||
|
raise PrimitiveFailure, "SystemCallError.errno_error failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
# We use .new here because when errno is set, we attempt to
|
||||||
|
# lookup and return a subclass of SystemCallError, specificly,
|
||||||
|
# one of the Errno subclasses.
|
||||||
|
def self.new(*args)
|
||||||
|
# This method is used 2 completely different ways. One is when it's called
|
||||||
|
# on SystemCallError, in which case it tries to construct a Errno subclass
|
||||||
|
# or makes a generic instead of itself.
|
||||||
|
#
|
||||||
|
# Otherwise it's called on a Errno subclass and just helps setup
|
||||||
|
# a instance of the subclass
|
||||||
|
if self.equal? SystemCallError
|
||||||
|
case args.size
|
||||||
|
when 1
|
||||||
|
if args.first.kind_of?(Fixnum)
|
||||||
|
errno = args.first
|
||||||
|
message = nil
|
||||||
|
else
|
||||||
|
errno = nil
|
||||||
|
message = StringValue(args.first)
|
||||||
|
end
|
||||||
|
location = nil
|
||||||
|
when 2
|
||||||
|
message, errno = args
|
||||||
|
location = nil
|
||||||
|
when 3
|
||||||
|
message, errno, location = args
|
||||||
|
else
|
||||||
|
raise ArgumentError, "wrong number of arguments (#{args.size} for 1..3)"
|
||||||
|
end
|
||||||
|
|
||||||
|
# If it corresponds to a known Errno class, create and return it now
|
||||||
|
if errno && error = SystemCallError.errno_error(message, errno, location)
|
||||||
|
return error
|
||||||
|
else
|
||||||
|
return super(message, errno, location)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
case args.size
|
||||||
|
when 0
|
||||||
|
message = nil
|
||||||
|
location = nil
|
||||||
|
when 1
|
||||||
|
message = StringValue(args.first)
|
||||||
|
location = nil
|
||||||
|
when 2
|
||||||
|
message, location = args
|
||||||
|
else
|
||||||
|
raise ArgumentError, "wrong number of arguments (#{args.size} for 0..2)"
|
||||||
|
end
|
||||||
|
|
||||||
|
if defined?(self::Errno) && self::Errno.kind_of?(Fixnum)
|
||||||
|
errno = self::Errno
|
||||||
|
error = SystemCallError.errno_error(message, self::Errno, location)
|
||||||
|
if error && error.class.equal?(self)
|
||||||
|
return error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
error = allocate
|
||||||
|
Rubinius::Unsafe.set_class error, self
|
||||||
|
Rubinius.privately { error.initialize(*args) }
|
||||||
|
return error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Must do this here because we have a unique new and otherwise .exception will
|
||||||
|
# call Exception.new because of the alias in Exception.
|
||||||
|
class << self
|
||||||
|
alias_method :exception, :new
|
||||||
|
end
|
||||||
|
|
||||||
|
# Use splat args here so that arity returns -1 to match MRI.
|
||||||
|
def initialize(*args)
|
||||||
|
kls = self.class
|
||||||
|
message, errno, location = args
|
||||||
|
@errno = errno
|
||||||
|
|
||||||
|
msg = "unknown error"
|
||||||
|
msg << " @ #{StringValue(location)}" if location
|
||||||
|
msg << " - #{StringValue(message)}" if message
|
||||||
|
super(msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
end
|
||||||
|
|
||||||
|
class KeyError < IndexError
|
||||||
|
end
|
||||||
|
|
||||||
|
class SignalException < Exception
|
||||||
|
|
||||||
|
attr_reader :signo
|
||||||
|
attr_reader :signm
|
||||||
|
|
||||||
|
def initialize(signo = nil, signm = nil)
|
||||||
|
# MRI overrides this behavior just for SignalException itself
|
||||||
|
# but not for anything that inherits from it, therefore we
|
||||||
|
# need this ugly check to make sure it works as intented.
|
||||||
|
return super(signo) unless self.class == SignalException
|
||||||
|
if signo.is_a? Integer
|
||||||
|
unless @signm = Signal::Numbers[signo]
|
||||||
|
raise ArgumentError, "invalid signal number #{signo}"
|
||||||
|
end
|
||||||
|
@signo = signo
|
||||||
|
@signm = signm || "SIG#{@signm}"
|
||||||
|
elsif signo
|
||||||
|
if signm
|
||||||
|
raise ArgumentError, "wrong number of arguments (2 for 1)"
|
||||||
|
end
|
||||||
|
signm = signo
|
||||||
|
if signo.kind_of?(Symbol)
|
||||||
|
signm = signm.to_s
|
||||||
|
else
|
||||||
|
signm = StringValue(signm)
|
||||||
|
end
|
||||||
|
signm = signm[3..-1] if signm.prefix? "SIG"
|
||||||
|
unless @signo = Signal::Names[signm]
|
||||||
|
raise ArgumentError, "invalid signal name #{signm}"
|
||||||
|
end
|
||||||
|
@signm = "SIG#{signm}"
|
||||||
|
end
|
||||||
|
super(@signm)
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
end
|
||||||
|
|
||||||
|
class StopIteration
|
||||||
|
attr_accessor :result
|
||||||
|
private :result=
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Base class for various exceptions raised in the VM.
|
||||||
|
|
||||||
|
class Rubinius::MachineException < Exception
|
||||||
|
end
|
||||||
|
|
||||||
|
class Rubinius::ConcurrentUpdateError < Rubinius::MachineException
|
||||||
|
end
|
||||||
|
|
||||||
|
class Rubinius::VMException < Exception
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Raised in the VM when an assertion fails.
|
||||||
|
|
||||||
|
class Rubinius::AssertionError < Rubinius::VMException
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Raised in the VM when attempting to read/write outside
|
||||||
|
# the bounds of an object.
|
||||||
|
|
||||||
|
class Rubinius::ObjectBoundsExceededError < Rubinius::VMException
|
||||||
|
end
|
||||||
|
|
||||||
|
# Defined by the VM itself
|
||||||
|
class Rubinius::InvalidBytecode < Rubinius::Internal
|
||||||
|
attr_reader :compiled_code
|
||||||
|
attr_reader :ip
|
||||||
|
|
||||||
|
def message
|
||||||
|
if @compiled_code
|
||||||
|
if @ip and @ip >= 0
|
||||||
|
"#{super} - at #{@compiled_code.name}+#{@ip}"
|
||||||
|
else
|
||||||
|
"#{super} - method #{@compiled_code.name}"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class InterpreterError < Exception
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
class DeadlockError < Exception
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
# MRI has an Exception class named "fatal" that is raised
|
||||||
|
# by the rb_fatal function. The class is not accessible from
|
||||||
|
# ruby because the name is begins with a lower-case letter.
|
||||||
|
# Also, the exception cannot be rescued.
|
||||||
|
#
|
||||||
|
# To support rb_fatal in the C-API, Rubinius provides the
|
||||||
|
# following FatalError class. If it clashes with code in
|
||||||
|
# the wild, we can rename it.
|
||||||
|
|
||||||
|
class FatalError < Exception
|
||||||
|
end
|
||||||
44
gxg/core/executable.rb
Normal file
44
gxg/core/executable.rb
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
##
|
||||||
|
# Parent class of everything that can live in a MethodTable.
|
||||||
|
#
|
||||||
|
# When calling an Executable, first the primitive slot is checked and executed
|
||||||
|
# if it exists. If execution of the primitive is successful, the value is put
|
||||||
|
# on the stack, and execution of the method is finished. If unsuccessful or
|
||||||
|
# absent (only in the case of a CompiledCode), the ruby code code
|
||||||
|
# in the CompiledCode is run.
|
||||||
|
module Rubinius
|
||||||
|
class Executable
|
||||||
|
attr_accessor :serial
|
||||||
|
attr_accessor :primitive
|
||||||
|
attr_accessor :tags
|
||||||
|
|
||||||
|
def self.allocate
|
||||||
|
Rubinius.primitive :executable_allocate
|
||||||
|
raise PrimitiveFailure, "Executable.allocate primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def invoke(name, mod, recv, args, block)
|
||||||
|
Rubinius.primitive :executable_invoke
|
||||||
|
raise PrimitiveFailure, "Executable#invoke primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def custom_call_site
|
||||||
|
Rubinius.primitive :executable_set_custom_call_site
|
||||||
|
raise PrimitiveFailure, "Executable#set_custom_call_site primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Allows instances of Executable and its subclasses
|
||||||
|
# to duck-type as CompiledCode::Visibility since
|
||||||
|
# they can both be entries in a MethodTable.
|
||||||
|
def public?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def private?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :protected?, :private?
|
||||||
|
end
|
||||||
|
end
|
||||||
23
gxg/core/false.rb
Normal file
23
gxg/core/false.rb
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
class FalseClass
|
||||||
|
def &(other)
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def ^(other)
|
||||||
|
!!other
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"false"
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :|, :^
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
# Call #to_s rather than alias it so that people can change #to_s if they
|
||||||
|
# wish. Yes, sounds dumb.
|
||||||
|
to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
FALSE = false
|
||||||
528
gxg/core/ffi.rb
Normal file
528
gxg/core/ffi.rb
Normal file
|
|
@ -0,0 +1,528 @@
|
||||||
|
##
|
||||||
|
# A Foreign Function Interface used to bind C libraries to ruby.
|
||||||
|
|
||||||
|
module Rubinius
|
||||||
|
module FFI
|
||||||
|
|
||||||
|
# Specialised error classes
|
||||||
|
class TypeError < RuntimeError; end
|
||||||
|
|
||||||
|
class NotFoundError < RuntimeError; end
|
||||||
|
|
||||||
|
class << self
|
||||||
|
def add_typedef(current, add)
|
||||||
|
if current.kind_of? Fixnum
|
||||||
|
code = current
|
||||||
|
else
|
||||||
|
code = FFI::TypeDefs[current]
|
||||||
|
raise TypeError, "Unable to resolve type '#{current}'" unless code
|
||||||
|
end
|
||||||
|
|
||||||
|
FFI::TypeDefs[add] = code
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_type(name)
|
||||||
|
code = FFI::TypeDefs[name]
|
||||||
|
raise TypeError, "Unable to resolve type '#{name}'" unless code
|
||||||
|
return code
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Given a +type+ as a number, indicate how many bytes that type
|
||||||
|
# takes up on this platform.
|
||||||
|
|
||||||
|
def type_size(type)
|
||||||
|
Rubinius.primitive :nativefunction_type_size
|
||||||
|
|
||||||
|
case type
|
||||||
|
when Symbol
|
||||||
|
return type_size(find_type(type))
|
||||||
|
when Rubinius::NativeFunction
|
||||||
|
return type_size(TYPE_PTR)
|
||||||
|
when FFI::Enum
|
||||||
|
return type_size(TYPE_ENUM)
|
||||||
|
end
|
||||||
|
|
||||||
|
raise PrimitiveFailure, "FFI.type_size primitive failed: #{type}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def size_to_type(size)
|
||||||
|
if sz = TypeSizes[size]
|
||||||
|
return sz
|
||||||
|
end
|
||||||
|
|
||||||
|
# Be like C, use int as the default type size.
|
||||||
|
return :int
|
||||||
|
end
|
||||||
|
|
||||||
|
def config(name)
|
||||||
|
Rubinius::Config["rbx.platform.#{name}"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def config_hash(name)
|
||||||
|
vals = {}
|
||||||
|
section = "rbx.platform.#{name}."
|
||||||
|
Rubinius::Config.section section do |key, value|
|
||||||
|
vals[key.substring(section.size, key.length)] = value
|
||||||
|
end
|
||||||
|
vals
|
||||||
|
end
|
||||||
|
|
||||||
|
def errno
|
||||||
|
FFI::Platform::POSIX.errno
|
||||||
|
end
|
||||||
|
|
||||||
|
# Convenience method for determining if a function call succeeded or failed
|
||||||
|
def call_failed?(return_code)
|
||||||
|
return_code == -1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Represents a C enum.
|
||||||
|
class Enum
|
||||||
|
attr_reader :tag
|
||||||
|
attr_reader :kv_map
|
||||||
|
|
||||||
|
def initialize(info, tag=nil)
|
||||||
|
@tag = tag
|
||||||
|
@kv_map = Hash.new
|
||||||
|
|
||||||
|
unless info.nil?
|
||||||
|
last_cst = nil
|
||||||
|
value = 0
|
||||||
|
info.each do |i|
|
||||||
|
case i
|
||||||
|
when Symbol
|
||||||
|
@kv_map[i] = value
|
||||||
|
last_cst = i
|
||||||
|
value += 1
|
||||||
|
when Integer
|
||||||
|
@kv_map[last_cst] = i
|
||||||
|
value = i + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
"#<%s:0x%x %s=>%s>" % [self.class, self.object_id, @tag, @kv_map.inspect]
|
||||||
|
end
|
||||||
|
|
||||||
|
def anonym?
|
||||||
|
@tag.nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
def values
|
||||||
|
@kv_map.values
|
||||||
|
end
|
||||||
|
|
||||||
|
def symbols
|
||||||
|
@kv_map.keys
|
||||||
|
end
|
||||||
|
|
||||||
|
def [](sym)
|
||||||
|
@kv_map[sym]
|
||||||
|
end
|
||||||
|
|
||||||
|
def symbol(value)
|
||||||
|
key, val = @kv_map.detect { |key, val| val == value }
|
||||||
|
key
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# It's a class to be compat with the ffi gem.
|
||||||
|
class Type
|
||||||
|
class Array
|
||||||
|
def initialize(element_type, size, impl_class=nil)
|
||||||
|
@element_type = element_type
|
||||||
|
@size = size
|
||||||
|
@implementation = impl_class
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
attr_reader :element_type
|
||||||
|
attr_reader :size
|
||||||
|
attr_reader :implementation
|
||||||
|
end
|
||||||
|
|
||||||
|
class StructByValue
|
||||||
|
def initialize(struct)
|
||||||
|
@implementation = struct
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
attr_reader :implementation
|
||||||
|
end
|
||||||
|
|
||||||
|
Struct = StructByValue
|
||||||
|
|
||||||
|
CHAR = TYPE_CHAR
|
||||||
|
UCHAR = TYPE_UCHAR
|
||||||
|
BOOL = TYPE_BOOL
|
||||||
|
SHORT = TYPE_SHORT
|
||||||
|
USHORT = TYPE_USHORT
|
||||||
|
INT = TYPE_INT
|
||||||
|
UINT = TYPE_UINT
|
||||||
|
LONG = TYPE_LONG
|
||||||
|
ULONG = TYPE_ULONG
|
||||||
|
LL = TYPE_LL
|
||||||
|
ULL = TYPE_ULL
|
||||||
|
FLOAT = TYPE_FLOAT
|
||||||
|
DOUBLE = TYPE_DOUBLE
|
||||||
|
PTR = TYPE_PTR
|
||||||
|
VOID = TYPE_VOID
|
||||||
|
STRING = TYPE_STRING
|
||||||
|
STRPTR = TYPE_STRPTR
|
||||||
|
CHARARR = TYPE_CHARARR
|
||||||
|
ENUM = TYPE_ENUM
|
||||||
|
VARARGS = TYPE_VARARGS
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Namespace for holding platform-specific C constants.
|
||||||
|
|
||||||
|
module Platform
|
||||||
|
def self.windows?
|
||||||
|
Rubinius.windows?
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.mac?
|
||||||
|
Rubinius.darwin?
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.unix?
|
||||||
|
! windows?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Represents a C struct as ruby class.
|
||||||
|
|
||||||
|
class Struct
|
||||||
|
class InlineArray
|
||||||
|
include Enumerable
|
||||||
|
|
||||||
|
def initialize(type, ptr)
|
||||||
|
@pointer = ptr
|
||||||
|
@type = type.element_type
|
||||||
|
@size = type.size
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
attr_reader :size
|
||||||
|
|
||||||
|
def [](idx)
|
||||||
|
if idx >= @size
|
||||||
|
raise ArgumentError, "index out of range (#{idx} >= #{@size})"
|
||||||
|
end
|
||||||
|
|
||||||
|
@pointer.get_at_offset(idx * @type, @type)
|
||||||
|
end
|
||||||
|
|
||||||
|
def []=(idx, val)
|
||||||
|
if idx >= @size
|
||||||
|
raise ArgumentError, "index out of range (#{idx} >= #{@size})"
|
||||||
|
end
|
||||||
|
|
||||||
|
@pointer.set_at_offset(idx * @type, @type, val)
|
||||||
|
end
|
||||||
|
|
||||||
|
def each
|
||||||
|
@size.times do |ele|
|
||||||
|
yield @pointer.get_at_offset(ele * @type, @type)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_a
|
||||||
|
ary = []
|
||||||
|
each { |x| ary << x }
|
||||||
|
ary
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_ptr
|
||||||
|
@pointer
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class InlineCharArray < InlineArray
|
||||||
|
def to_s
|
||||||
|
str = @pointer.read_string(@size)
|
||||||
|
|
||||||
|
# NULL clamp this, to be the same as the ffi gem.
|
||||||
|
if idx = str.index("\0")
|
||||||
|
return str[0, idx]
|
||||||
|
end
|
||||||
|
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :to_str, :to_s
|
||||||
|
alias_method :inspect, :to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.find_nested_parent
|
||||||
|
return nil if self.name.nil?
|
||||||
|
|
||||||
|
path = self.name.split("::")
|
||||||
|
path.pop # remove ourself
|
||||||
|
|
||||||
|
mod = Object
|
||||||
|
|
||||||
|
begin
|
||||||
|
path.each { |c| mod = mod.const_get(c) }
|
||||||
|
rescue NameError
|
||||||
|
# bail.
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
mod.respond_to?(:find_type) ? mod : nil
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :pointer
|
||||||
|
|
||||||
|
def self.layout(*spec)
|
||||||
|
return @layout if spec.size == 0
|
||||||
|
|
||||||
|
# Pick up a enclosing FFI::Library
|
||||||
|
@enclosing_module ||= find_nested_parent
|
||||||
|
|
||||||
|
cspec = Rubinius::LookupTable.new
|
||||||
|
i = 0
|
||||||
|
|
||||||
|
@size = 0
|
||||||
|
@members = []
|
||||||
|
|
||||||
|
while i < spec.size
|
||||||
|
name = spec[i]
|
||||||
|
@members << name
|
||||||
|
|
||||||
|
f = spec[i + 1]
|
||||||
|
|
||||||
|
type_code = nil
|
||||||
|
|
||||||
|
if f.kind_of? Array
|
||||||
|
ary_type = f[0]
|
||||||
|
ary_size = f[1]
|
||||||
|
|
||||||
|
if @enclosing_module
|
||||||
|
type_code = @enclosing_module.find_type(ary_type)
|
||||||
|
end
|
||||||
|
|
||||||
|
type_code ||= FFI.find_type(ary_type)
|
||||||
|
type_size = FFI.type_size(type_code)
|
||||||
|
|
||||||
|
case type_code
|
||||||
|
when TYPE_CHAR, TYPE_UCHAR
|
||||||
|
klass = InlineCharArray
|
||||||
|
else
|
||||||
|
klass = InlineArray
|
||||||
|
end
|
||||||
|
|
||||||
|
type = FFI::Type::Array.new(type_code, ary_size, klass)
|
||||||
|
element_size = type_size * ary_size
|
||||||
|
elsif f.kind_of?(Class) and (f < FFI::Struct || f < FFI::Union)
|
||||||
|
type = FFI::Type::StructByValue.new(f)
|
||||||
|
element_size = type_size = f.size
|
||||||
|
else
|
||||||
|
if @enclosing_module
|
||||||
|
type_code = @enclosing_module.find_type(f) rescue nil
|
||||||
|
end
|
||||||
|
|
||||||
|
type_code ||= FFI.find_type(f)
|
||||||
|
|
||||||
|
type = type_code
|
||||||
|
element_size = type_size = FFI.type_size(type_code)
|
||||||
|
end
|
||||||
|
|
||||||
|
offset = spec[i + 2]
|
||||||
|
|
||||||
|
if offset.kind_of?(Fixnum)
|
||||||
|
i += 3
|
||||||
|
else
|
||||||
|
if self < FFI::Union
|
||||||
|
offset = 0
|
||||||
|
else
|
||||||
|
offset = @size
|
||||||
|
|
||||||
|
mod = offset % type_size
|
||||||
|
unless mod == 0
|
||||||
|
# we need to align it.
|
||||||
|
offset += (type_size - mod)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
i += 2
|
||||||
|
end
|
||||||
|
|
||||||
|
cspec[name] = [offset, type]
|
||||||
|
ending = offset + element_size
|
||||||
|
@size = ending if @size < ending
|
||||||
|
end
|
||||||
|
|
||||||
|
@layout = cspec unless self == FFI::Struct
|
||||||
|
|
||||||
|
return cspec
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.config(base, *fields)
|
||||||
|
@size = Rubinius::Config["#{base}.sizeof"]
|
||||||
|
cspec = Rubinius::LookupTable.new
|
||||||
|
|
||||||
|
fields.each do |field|
|
||||||
|
offset = Rubinius::Config["#{base}.#{field}.offset"]
|
||||||
|
element_size = Rubinius::Config["#{base}.#{field}.size"]
|
||||||
|
type = Rubinius::Config["#{base}.#{field}.type"]
|
||||||
|
type = type ? type.to_sym : FFI.size_to_type(size)
|
||||||
|
|
||||||
|
type_code = FFI.find_type type
|
||||||
|
|
||||||
|
if :char_array == type
|
||||||
|
type_code = FFI::Type::Array.new(FFI.find_type(:char), element_size, InlineCharArray)
|
||||||
|
end
|
||||||
|
|
||||||
|
cspec[field] = [offset, type_code]
|
||||||
|
ending = offset + element_size
|
||||||
|
@size = ending if @size < ending
|
||||||
|
end
|
||||||
|
|
||||||
|
@layout = cspec
|
||||||
|
|
||||||
|
return cspec
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.size
|
||||||
|
@size
|
||||||
|
end
|
||||||
|
|
||||||
|
def size
|
||||||
|
self.class.size
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.offset_of(name)
|
||||||
|
@layout[name].first
|
||||||
|
end
|
||||||
|
|
||||||
|
def offset_of(name)
|
||||||
|
@cspec[name].first
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.offsets
|
||||||
|
members.map do |member|
|
||||||
|
[member, @layout[member].first]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def offsets
|
||||||
|
members.map do |member|
|
||||||
|
[member, @cspec[member].first]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.members
|
||||||
|
@members
|
||||||
|
end
|
||||||
|
|
||||||
|
def members
|
||||||
|
self.class.members
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(pointer=nil, *spec)
|
||||||
|
@cspec = self.class.layout(*spec)
|
||||||
|
|
||||||
|
if pointer
|
||||||
|
@pointer = pointer
|
||||||
|
else
|
||||||
|
@pointer = MemoryPointer.new size
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def to_ptr
|
||||||
|
@pointer
|
||||||
|
end
|
||||||
|
|
||||||
|
def free
|
||||||
|
@pointer.free
|
||||||
|
end
|
||||||
|
|
||||||
|
def ==(other)
|
||||||
|
return false unless other.is_a?(self.class)
|
||||||
|
@pointer == other.pointer
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize_copy(ptr)
|
||||||
|
@pointer = ptr.pointer.dup
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def []=(field, val)
|
||||||
|
offset, type = @cspec[field]
|
||||||
|
raise "Unknown field #{field}" unless offset
|
||||||
|
|
||||||
|
case type
|
||||||
|
when Fixnum
|
||||||
|
@pointer.set_at_offset(offset, type, val)
|
||||||
|
when FFI::Type::Array
|
||||||
|
if type.implementation == InlineCharArray
|
||||||
|
(@pointer + offset).write_string StringValue(val), type.size
|
||||||
|
return val
|
||||||
|
end
|
||||||
|
|
||||||
|
raise TypeError, "Unable to set inline array"
|
||||||
|
when Rubinius::NativeFunction
|
||||||
|
@pointer.set_at_offset(offset, FFI::TYPE_PTR, val)
|
||||||
|
else
|
||||||
|
@pointer.set_at_offset(offset, type, val)
|
||||||
|
end
|
||||||
|
|
||||||
|
return val
|
||||||
|
end
|
||||||
|
|
||||||
|
def [](field)
|
||||||
|
offset, type = @cspec[field]
|
||||||
|
raise "Unknown field #{field}" unless offset
|
||||||
|
|
||||||
|
case type
|
||||||
|
when FFI::TYPE_CHARARR
|
||||||
|
(@pointer + offset).read_string
|
||||||
|
when Fixnum
|
||||||
|
@pointer.get_at_offset(offset, type)
|
||||||
|
when FFI::Type::Array
|
||||||
|
type.implementation.new(type, @pointer + offset)
|
||||||
|
when FFI::Type::StructByValue
|
||||||
|
type.implementation.new(@pointer + offset)
|
||||||
|
when Rubinius::NativeFunction
|
||||||
|
ptr = @pointer.get_at_offset(offset, FFI::TYPE_PTR)
|
||||||
|
if ptr
|
||||||
|
FFI::Function.new(type.return_type, type.argument_types, ptr)
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
else
|
||||||
|
@pointer.get_at_offset(offset, type)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def values
|
||||||
|
members.map { |m| self[m] }
|
||||||
|
end
|
||||||
|
|
||||||
|
def null?
|
||||||
|
@pointer == FFI::Pointer::NULL
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Represents a C union as ruby class.
|
||||||
|
|
||||||
|
class Union < Struct
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
73
gxg/core/fiber.rb
Normal file
73
gxg/core/fiber.rb
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
class Fiber
|
||||||
|
attr_reader :stack_size
|
||||||
|
attr_reader :thread_name
|
||||||
|
attr_reader :fiber_id
|
||||||
|
attr_reader :source
|
||||||
|
|
||||||
|
def self.new(**kw, &block)
|
||||||
|
if block.nil?
|
||||||
|
raise ArgumentError, "Fiber.new requires a block"
|
||||||
|
end
|
||||||
|
|
||||||
|
stack_size = Rubinius::Type.try_convert kw[:stack_size], Fixnum, :to_int
|
||||||
|
|
||||||
|
Rubinius.invoke_primitive :fiber_new, stack_size, block, self
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.current
|
||||||
|
Rubinius.primitive :fiber_s_current
|
||||||
|
raise PrimitiveFailure, "Fiber.current primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.yield(*args)
|
||||||
|
Rubinius.primitive :fiber_s_yield
|
||||||
|
raise PrimitiveFailure, "Fiber.yield primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.list
|
||||||
|
Rubinius.primitive :fiber_s_list
|
||||||
|
raise PrimitiveFailure, "Fiber.list primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.main
|
||||||
|
Rubinius.primitive :fiber_s_main
|
||||||
|
raise PrimitiveFailure, "Fiber.main primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.count
|
||||||
|
Rubinius.primitive :fiber_s_count
|
||||||
|
raise PrimitiveFailure, "Fiber.count primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def status
|
||||||
|
Rubinius.primitive :fiber_status
|
||||||
|
raise PrimitiveFailure, "Fiber#status primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def resume(*args)
|
||||||
|
Rubinius.primitive :fiber_resume
|
||||||
|
raise PrimitiveFailure, "Fiber#resume primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def transfer(*args)
|
||||||
|
Rubinius.primitive :fiber_transfer
|
||||||
|
raise PrimitiveFailure, "Fiber#transfer primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def dispose
|
||||||
|
Rubinius.primitive :fiber_dispose
|
||||||
|
raise PrimitiveFailure, "Fiber#dispose primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def alive?
|
||||||
|
status != "dead"
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
str = "#<#{self.class}:0x#{object_id.to_s(16)} thread_name=#{@thread_name} fiber_id=#{@fiber_id} status=#{status}"
|
||||||
|
str << " source=#{@source}" if @source
|
||||||
|
str << ">"
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :to_s, :inspect
|
||||||
|
end
|
||||||
1289
gxg/core/file.rb
Normal file
1289
gxg/core/file.rb
Normal file
File diff suppressed because it is too large
Load diff
119
gxg/core/file_test.rb
Normal file
119
gxg/core/file_test.rb
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
module FileTest
|
||||||
|
def blockdev?(path)
|
||||||
|
File.blockdev? path
|
||||||
|
end
|
||||||
|
module_function :blockdev?
|
||||||
|
|
||||||
|
def chardev?(path)
|
||||||
|
File.chardev? path
|
||||||
|
end
|
||||||
|
module_function :chardev?
|
||||||
|
|
||||||
|
def directory?(path)
|
||||||
|
File.directory? path
|
||||||
|
end
|
||||||
|
module_function :directory?
|
||||||
|
|
||||||
|
def executable?(path)
|
||||||
|
File.executable? path
|
||||||
|
end
|
||||||
|
module_function :executable?
|
||||||
|
|
||||||
|
def executable_real?(path)
|
||||||
|
File.executable_real? path
|
||||||
|
end
|
||||||
|
module_function :executable_real?
|
||||||
|
|
||||||
|
def exist?(path)
|
||||||
|
File.exist? path
|
||||||
|
end
|
||||||
|
module_function :exist?
|
||||||
|
|
||||||
|
alias_method :exists?, :exist?
|
||||||
|
module_function :exists?
|
||||||
|
|
||||||
|
def file?(path)
|
||||||
|
File.file? path
|
||||||
|
end
|
||||||
|
module_function :file?
|
||||||
|
|
||||||
|
def grpowned?(path)
|
||||||
|
File.grpowned? path
|
||||||
|
end
|
||||||
|
module_function :grpowned?
|
||||||
|
|
||||||
|
def identical?(a, b)
|
||||||
|
File.identical? a, b
|
||||||
|
end
|
||||||
|
module_function :identical?
|
||||||
|
|
||||||
|
def owned?(path)
|
||||||
|
File.owned? path
|
||||||
|
end
|
||||||
|
module_function :owned?
|
||||||
|
|
||||||
|
def pipe?(path)
|
||||||
|
File.pipe? path
|
||||||
|
end
|
||||||
|
module_function :pipe?
|
||||||
|
|
||||||
|
def readable?(path)
|
||||||
|
File.readable? path
|
||||||
|
end
|
||||||
|
module_function :readable?
|
||||||
|
|
||||||
|
def readable_real?(path)
|
||||||
|
File.readable_real? path
|
||||||
|
end
|
||||||
|
module_function :readable_real?
|
||||||
|
|
||||||
|
def setgid?(path)
|
||||||
|
File.setgid? path
|
||||||
|
end
|
||||||
|
module_function :setgid?
|
||||||
|
|
||||||
|
def setuid?(path)
|
||||||
|
File.setuid? path
|
||||||
|
end
|
||||||
|
module_function :setuid?
|
||||||
|
|
||||||
|
def size(path)
|
||||||
|
File.size path
|
||||||
|
end
|
||||||
|
module_function :size
|
||||||
|
|
||||||
|
def size?(path)
|
||||||
|
File.size? path
|
||||||
|
end
|
||||||
|
module_function :size?
|
||||||
|
|
||||||
|
def socket?(path)
|
||||||
|
File.socket? path
|
||||||
|
end
|
||||||
|
module_function :socket?
|
||||||
|
|
||||||
|
def sticky?(path)
|
||||||
|
File.sticky? path
|
||||||
|
end
|
||||||
|
module_function :sticky?
|
||||||
|
|
||||||
|
def symlink?(path)
|
||||||
|
File.symlink? path
|
||||||
|
end
|
||||||
|
module_function :symlink?
|
||||||
|
|
||||||
|
def writable?(path)
|
||||||
|
File.writable? path
|
||||||
|
end
|
||||||
|
module_function :writable?
|
||||||
|
|
||||||
|
def writable_real?(path)
|
||||||
|
File.writable_real? path
|
||||||
|
end
|
||||||
|
module_function :writable_real?
|
||||||
|
|
||||||
|
def zero?(path)
|
||||||
|
File.zero? path
|
||||||
|
end
|
||||||
|
module_function :zero?
|
||||||
|
end
|
||||||
602
gxg/core/float.rb
Normal file
602
gxg/core/float.rb
Normal file
|
|
@ -0,0 +1,602 @@
|
||||||
|
class Float < Numeric
|
||||||
|
def **(other)
|
||||||
|
Rubinius.primitive :float_pow
|
||||||
|
|
||||||
|
if other.is_a?(Float) && self < 0 && other != other.round
|
||||||
|
return Complex.new(self, 0) ** other
|
||||||
|
end
|
||||||
|
|
||||||
|
b, a = math_coerce other
|
||||||
|
a ** b
|
||||||
|
end
|
||||||
|
|
||||||
|
def imaginary
|
||||||
|
0
|
||||||
|
end
|
||||||
|
|
||||||
|
def numerator
|
||||||
|
if nan?
|
||||||
|
NAN
|
||||||
|
elsif infinite? == 1
|
||||||
|
INFINITY
|
||||||
|
elsif infinite? == -1
|
||||||
|
-INFINITY
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def denominator
|
||||||
|
if infinite? || nan?
|
||||||
|
1
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_r
|
||||||
|
f, e = Math.frexp self
|
||||||
|
f = Math.ldexp(f, MANT_DIG).to_i
|
||||||
|
e -= MANT_DIG
|
||||||
|
|
||||||
|
(f * (RADIX ** e)).to_r
|
||||||
|
end
|
||||||
|
|
||||||
|
def arg
|
||||||
|
if nan?
|
||||||
|
self
|
||||||
|
elsif signbit?
|
||||||
|
Math::PI
|
||||||
|
else
|
||||||
|
0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
alias_method :angle, :arg
|
||||||
|
alias_method :phase, :arg
|
||||||
|
|
||||||
|
def rationalize(eps=undefined)
|
||||||
|
if undefined.equal?(eps)
|
||||||
|
f, n = Math.frexp self
|
||||||
|
f = Math.ldexp(f, Float::MANT_DIG).to_i
|
||||||
|
n -= Float::MANT_DIG
|
||||||
|
|
||||||
|
Rational.new(2 * f, 1 << (1 - n)).rationalize(Rational.new(1, 1 << (1 - n)))
|
||||||
|
else
|
||||||
|
to_r.rationalize(eps)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def round(ndigits=0)
|
||||||
|
ndigits = Rubinius::Type.coerce_to(ndigits, Integer, :to_int)
|
||||||
|
|
||||||
|
if ndigits == 0
|
||||||
|
return Rubinius.invoke_primitive :float_round, self
|
||||||
|
elsif ndigits < 0
|
||||||
|
return truncate.round ndigits
|
||||||
|
end
|
||||||
|
|
||||||
|
return self if infinite? or nan?
|
||||||
|
|
||||||
|
_, exp = Math.frexp(self)
|
||||||
|
|
||||||
|
if ndigits >= (Float::DIG + 2) - (exp > 0 ? exp / 4 : exp / 3 - 1)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
if ndigits < -(exp > 0 ? exp / 3 + 1 : exp / 4)
|
||||||
|
return 0.0
|
||||||
|
end
|
||||||
|
|
||||||
|
f = 10**ndigits
|
||||||
|
Rubinius.invoke_primitive(:float_round, self * f) / f.to_f
|
||||||
|
end
|
||||||
|
def coerce(other)
|
||||||
|
return [other, self] if other.kind_of? Float
|
||||||
|
[Float(other), self]
|
||||||
|
end
|
||||||
|
|
||||||
|
def -@
|
||||||
|
Rubinius.primitive :float_neg
|
||||||
|
raise PrimitiveFailure, "Float#-@ primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def abs
|
||||||
|
FFI::Platform::Math.fabs(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :magnitude, :abs
|
||||||
|
|
||||||
|
def signbit?
|
||||||
|
Rubinius.primitive :float_signbit_p
|
||||||
|
raise PrimitiveFailure, "Float#signbit? primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def +(other)
|
||||||
|
Rubinius.asm do
|
||||||
|
flt = new_label
|
||||||
|
done = new_label
|
||||||
|
|
||||||
|
r0 = new_register
|
||||||
|
r1 = new_register
|
||||||
|
r2 = new_register
|
||||||
|
r3 = new_register
|
||||||
|
|
||||||
|
r_load_m_binops r0, r1
|
||||||
|
|
||||||
|
n_promote r2, r0, r1
|
||||||
|
|
||||||
|
r_load_1 r3
|
||||||
|
n_ieq r3, r3, r2
|
||||||
|
b_if r3, flt
|
||||||
|
goto done
|
||||||
|
|
||||||
|
flt.set!
|
||||||
|
n_dadd r0, r0, r1
|
||||||
|
r_store_float r0, r0
|
||||||
|
r_ret r0
|
||||||
|
|
||||||
|
done.set!
|
||||||
|
|
||||||
|
# TODO: teach the bytecode compiler better
|
||||||
|
push_true
|
||||||
|
end
|
||||||
|
|
||||||
|
b, a = math_coerce other
|
||||||
|
a + b
|
||||||
|
end
|
||||||
|
|
||||||
|
def -(other)
|
||||||
|
Rubinius.asm do
|
||||||
|
flt = new_label
|
||||||
|
done = new_label
|
||||||
|
|
||||||
|
r0 = new_register
|
||||||
|
r1 = new_register
|
||||||
|
r2 = new_register
|
||||||
|
r3 = new_register
|
||||||
|
|
||||||
|
r_load_m_binops r0, r1
|
||||||
|
|
||||||
|
n_promote r2, r0, r1
|
||||||
|
|
||||||
|
r_load_1 r3
|
||||||
|
n_ieq r3, r3, r2
|
||||||
|
b_if r3, flt
|
||||||
|
goto done
|
||||||
|
|
||||||
|
flt.set!
|
||||||
|
n_dsub r0, r0, r1
|
||||||
|
r_store_float r0, r0
|
||||||
|
r_ret r0
|
||||||
|
|
||||||
|
done.set!
|
||||||
|
|
||||||
|
# TODO: teach the bytecode compiler better
|
||||||
|
push_true
|
||||||
|
end
|
||||||
|
|
||||||
|
b, a = math_coerce other
|
||||||
|
a - b
|
||||||
|
end
|
||||||
|
|
||||||
|
def *(other)
|
||||||
|
Rubinius.asm do
|
||||||
|
flt = new_label
|
||||||
|
done = new_label
|
||||||
|
|
||||||
|
r0 = new_register
|
||||||
|
r1 = new_register
|
||||||
|
r2 = new_register
|
||||||
|
r3 = new_register
|
||||||
|
|
||||||
|
r_load_m_binops r0, r1
|
||||||
|
|
||||||
|
n_promote r2, r0, r1
|
||||||
|
|
||||||
|
r_load_1 r3
|
||||||
|
n_ieq r3, r3, r2
|
||||||
|
b_if r3, flt
|
||||||
|
goto done
|
||||||
|
|
||||||
|
flt.set!
|
||||||
|
n_dmul r0, r0, r1
|
||||||
|
r_store_float r0, r0
|
||||||
|
r_ret r0
|
||||||
|
|
||||||
|
done.set!
|
||||||
|
|
||||||
|
# TODO: teach the bytecode compiler better
|
||||||
|
push_true
|
||||||
|
end
|
||||||
|
|
||||||
|
b, a = math_coerce other
|
||||||
|
a * b
|
||||||
|
end
|
||||||
|
|
||||||
|
def /(other)
|
||||||
|
Rubinius.asm do
|
||||||
|
flt = new_label
|
||||||
|
done = new_label
|
||||||
|
|
||||||
|
r0 = new_register
|
||||||
|
r1 = new_register
|
||||||
|
r2 = new_register
|
||||||
|
r3 = new_register
|
||||||
|
|
||||||
|
r_load_m_binops r0, r1
|
||||||
|
|
||||||
|
n_promote r2, r0, r1
|
||||||
|
|
||||||
|
r_load_1 r3
|
||||||
|
n_ieq r3, r3, r2
|
||||||
|
b_if r3, flt
|
||||||
|
goto done
|
||||||
|
|
||||||
|
flt.set!
|
||||||
|
n_ddiv r0, r0, r1
|
||||||
|
r_store_float r0, r0
|
||||||
|
r_ret r0
|
||||||
|
|
||||||
|
done.set!
|
||||||
|
|
||||||
|
# TODO: teach the bytecode compiler better
|
||||||
|
push_true
|
||||||
|
end
|
||||||
|
|
||||||
|
redo_coerced :/, other
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :divide, :/
|
||||||
|
alias_method :quo, :/
|
||||||
|
alias_method :fdiv, :/
|
||||||
|
|
||||||
|
INFINITY = 1.0 / 0.0
|
||||||
|
NAN = 0.0 / 0.0
|
||||||
|
|
||||||
|
def divmod(other)
|
||||||
|
Rubinius.primitive :float_divmod
|
||||||
|
b, a = math_coerce other
|
||||||
|
a.divmod b
|
||||||
|
end
|
||||||
|
|
||||||
|
def %(other)
|
||||||
|
return 0 / 0.to_f if other == 0
|
||||||
|
Rubinius.primitive :float_mod
|
||||||
|
b, a = math_coerce other
|
||||||
|
a % b
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :modulo, :%
|
||||||
|
|
||||||
|
def <(other)
|
||||||
|
Rubinius.asm do
|
||||||
|
done = new_label
|
||||||
|
|
||||||
|
r0 = new_register
|
||||||
|
r1 = new_register
|
||||||
|
r2 = new_register
|
||||||
|
r3 = new_register
|
||||||
|
|
||||||
|
r_load_m_binops r0, r1
|
||||||
|
|
||||||
|
n_promote r2, r0, r1
|
||||||
|
|
||||||
|
r_load_0 r3
|
||||||
|
n_ieq r3, r3, r2
|
||||||
|
b_if r3, done
|
||||||
|
|
||||||
|
n_dlt r0, r0, r1
|
||||||
|
r_load_bool r0, r0
|
||||||
|
r_ret r0
|
||||||
|
|
||||||
|
done.set!
|
||||||
|
|
||||||
|
# TODO: teach the bytecode compiler better
|
||||||
|
push_true
|
||||||
|
end
|
||||||
|
|
||||||
|
b, a = math_coerce other, :compare_error
|
||||||
|
a < b
|
||||||
|
end
|
||||||
|
|
||||||
|
def <=(other)
|
||||||
|
Rubinius.asm do
|
||||||
|
done = new_label
|
||||||
|
|
||||||
|
r0 = new_register
|
||||||
|
r1 = new_register
|
||||||
|
r2 = new_register
|
||||||
|
r3 = new_register
|
||||||
|
|
||||||
|
r_load_m_binops r0, r1
|
||||||
|
|
||||||
|
n_promote r2, r0, r1
|
||||||
|
|
||||||
|
r_load_0 r3
|
||||||
|
n_ieq r3, r3, r2
|
||||||
|
b_if r3, done
|
||||||
|
|
||||||
|
n_dle r0, r0, r1
|
||||||
|
r_load_bool r0, r0
|
||||||
|
r_ret r0
|
||||||
|
|
||||||
|
done.set!
|
||||||
|
|
||||||
|
# TODO: teach the bytecode compiler better
|
||||||
|
push_true
|
||||||
|
end
|
||||||
|
|
||||||
|
b, a = math_coerce other, :compare_error
|
||||||
|
a <= b
|
||||||
|
end
|
||||||
|
|
||||||
|
def >(other)
|
||||||
|
Rubinius.asm do
|
||||||
|
done = new_label
|
||||||
|
|
||||||
|
r0 = new_register
|
||||||
|
r1 = new_register
|
||||||
|
r2 = new_register
|
||||||
|
r3 = new_register
|
||||||
|
|
||||||
|
r_load_m_binops r0, r1
|
||||||
|
|
||||||
|
n_promote r2, r0, r1
|
||||||
|
|
||||||
|
r_load_0 r3
|
||||||
|
n_ieq r3, r3, r2
|
||||||
|
b_if r3, done
|
||||||
|
|
||||||
|
n_dgt r0, r0, r1
|
||||||
|
r_load_bool r0, r0
|
||||||
|
r_ret r0
|
||||||
|
|
||||||
|
done.set!
|
||||||
|
|
||||||
|
# TODO: teach the bytecode compiler better
|
||||||
|
push_true
|
||||||
|
end
|
||||||
|
|
||||||
|
b, a = math_coerce other, :compare_error
|
||||||
|
a > b
|
||||||
|
end
|
||||||
|
|
||||||
|
def >=(other)
|
||||||
|
Rubinius.asm do
|
||||||
|
done = new_label
|
||||||
|
|
||||||
|
r0 = new_register
|
||||||
|
r1 = new_register
|
||||||
|
r2 = new_register
|
||||||
|
r3 = new_register
|
||||||
|
|
||||||
|
r_load_m_binops r0, r1
|
||||||
|
|
||||||
|
n_promote r2, r0, r1
|
||||||
|
|
||||||
|
r_load_0 r3
|
||||||
|
n_ieq r3, r3, r2
|
||||||
|
b_if r3, done
|
||||||
|
|
||||||
|
n_dge r0, r0, r1
|
||||||
|
r_load_bool r0, r0
|
||||||
|
r_ret r0
|
||||||
|
|
||||||
|
done.set!
|
||||||
|
|
||||||
|
# TODO: teach the bytecode compiler better
|
||||||
|
push_true
|
||||||
|
end
|
||||||
|
|
||||||
|
b, a = math_coerce other, :compare_error
|
||||||
|
a >= b
|
||||||
|
end
|
||||||
|
|
||||||
|
def <=>(other)
|
||||||
|
Rubinius.primitive :float_compare
|
||||||
|
b, a = math_coerce other, :compare_error
|
||||||
|
a <=> b
|
||||||
|
rescue ArgumentError
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def !=(other)
|
||||||
|
Rubinius.asm do
|
||||||
|
done = new_label
|
||||||
|
|
||||||
|
r0 = new_register
|
||||||
|
r1 = new_register
|
||||||
|
r2 = new_register
|
||||||
|
r3 = new_register
|
||||||
|
|
||||||
|
r_load_m_binops r0, r1
|
||||||
|
|
||||||
|
n_promote r2, r0, r1
|
||||||
|
|
||||||
|
r_load_0 r3
|
||||||
|
n_ieq r3, r3, r2
|
||||||
|
b_if r3, done
|
||||||
|
|
||||||
|
n_dne r0, r0, r1
|
||||||
|
r_load_bool r0, r0
|
||||||
|
r_ret r0
|
||||||
|
|
||||||
|
done.set!
|
||||||
|
|
||||||
|
# TODO: teach the bytecode compiler better
|
||||||
|
push_true
|
||||||
|
end
|
||||||
|
|
||||||
|
b, a = math_coerce(other)
|
||||||
|
a != b ? true : false
|
||||||
|
end
|
||||||
|
|
||||||
|
def ==(other)
|
||||||
|
Rubinius.asm do
|
||||||
|
done = new_label
|
||||||
|
|
||||||
|
r0 = new_register
|
||||||
|
r1 = new_register
|
||||||
|
r2 = new_register
|
||||||
|
r3 = new_register
|
||||||
|
|
||||||
|
r_load_m_binops r0, r1
|
||||||
|
|
||||||
|
n_promote r2, r0, r1
|
||||||
|
|
||||||
|
r_load_0 r3
|
||||||
|
n_ieq r3, r3, r2
|
||||||
|
b_if r3, done
|
||||||
|
|
||||||
|
n_deq r0, r0, r1
|
||||||
|
r_load_bool r0, r0
|
||||||
|
r_ret r0
|
||||||
|
|
||||||
|
done.set!
|
||||||
|
|
||||||
|
# TODO: teach the bytecode compiler better
|
||||||
|
push_true
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
b, a = math_coerce(other)
|
||||||
|
a == b ? true : false
|
||||||
|
rescue TypeError
|
||||||
|
return other == self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def eql?(other)
|
||||||
|
Rubinius.primitive :float_eql
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def nan?
|
||||||
|
Rubinius.asm do
|
||||||
|
r0 = new_register
|
||||||
|
r1 = new_register
|
||||||
|
|
||||||
|
r_load_self r0
|
||||||
|
r_load_float r0, r0
|
||||||
|
n_dnan r1, r0
|
||||||
|
r_load_bool r0, r1
|
||||||
|
r_ret r0
|
||||||
|
|
||||||
|
# TODO: teach the bytecode compiler better
|
||||||
|
push_true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def infinite?
|
||||||
|
Rubinius.asm do
|
||||||
|
no = new_label
|
||||||
|
|
||||||
|
r0 = new_register
|
||||||
|
r1 = new_register
|
||||||
|
r2 = new_register
|
||||||
|
|
||||||
|
r_load_self r0
|
||||||
|
r_load_float r0, r0
|
||||||
|
n_dinf r1, r0
|
||||||
|
|
||||||
|
r_load_0 r0
|
||||||
|
n_ieq r2, r1, r0
|
||||||
|
b_if r2, no
|
||||||
|
|
||||||
|
r_store_int r0, r1
|
||||||
|
r_ret r0
|
||||||
|
|
||||||
|
no.set!
|
||||||
|
r_load_nil r0, 0
|
||||||
|
r_ret r0
|
||||||
|
|
||||||
|
# TODO: teach the bytecode compiler better
|
||||||
|
push_true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def finite?
|
||||||
|
not (nan? or infinite?)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_f
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_i
|
||||||
|
Rubinius.primitive :float_to_i
|
||||||
|
raise PrimitiveFailure, "Float#to_i primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :to_int, :to_i
|
||||||
|
alias_method :truncate, :to_i
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
to_s_minimal
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :inspect, :to_s
|
||||||
|
|
||||||
|
def to_s_minimal
|
||||||
|
Rubinius.primitive :float_to_s_minimal
|
||||||
|
raise PrimitiveFailure, "Float#to_s_minimal primitive failed: output exceeds buffer size"
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s_formatted(fmt)
|
||||||
|
Rubinius.primitive :float_to_s_formatted
|
||||||
|
raise PrimitiveFailure, "Float#to_s_formatted primitive failed: output exceeds buffer size"
|
||||||
|
end
|
||||||
|
private :to_s_formatted
|
||||||
|
|
||||||
|
def dtoa
|
||||||
|
Rubinius.primitive :float_dtoa
|
||||||
|
raise PrimitiveFailure, "Fload#dtoa primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_packed(size)
|
||||||
|
Rubinius.primitive :float_to_packed
|
||||||
|
raise PrimitiveFailure, "Float#to_packed primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def ceil
|
||||||
|
int = to_i()
|
||||||
|
|
||||||
|
return int if self == int or self < 0
|
||||||
|
return int + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def floor
|
||||||
|
int = to_i()
|
||||||
|
|
||||||
|
return int if self > 0 or self == int
|
||||||
|
return int - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def next_float
|
||||||
|
return NAN if self.nan?
|
||||||
|
return -MAX if self == -INFINITY
|
||||||
|
return INFINITY if self == MAX
|
||||||
|
return Math.ldexp(0.5, MIN_EXP - MANT_DIG + 1) if self.zero?
|
||||||
|
|
||||||
|
frac, exp = Math.frexp self
|
||||||
|
|
||||||
|
if frac == -0.5
|
||||||
|
frac *= 2
|
||||||
|
exp -= 1
|
||||||
|
end
|
||||||
|
|
||||||
|
smallest_frac = EPSILON / 2
|
||||||
|
smallest_frac = Math.ldexp(smallest_frac, MIN_EXP - exp) if exp < MIN_EXP
|
||||||
|
|
||||||
|
result_frac = frac + smallest_frac
|
||||||
|
|
||||||
|
return -0.0 if result_frac.zero? && frac < 0
|
||||||
|
return 0.0 if result_frac.zero? && frac > 0
|
||||||
|
|
||||||
|
return Math.ldexp result_frac, exp
|
||||||
|
end
|
||||||
|
|
||||||
|
def prev_float
|
||||||
|
return -(-self).next_float
|
||||||
|
end
|
||||||
|
end
|
||||||
33
gxg/core/fsevent.rb
Normal file
33
gxg/core/fsevent.rb
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
module Rubinius
|
||||||
|
class FSEvent
|
||||||
|
attr_accessor :fileno
|
||||||
|
attr_accessor :path
|
||||||
|
|
||||||
|
def self.allocate
|
||||||
|
Rubinius.primitive :fsevent_allocate
|
||||||
|
raise PrimitiveFailure, "Rubinius::FSEvent.allocate failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def watch_file(fd, path)
|
||||||
|
Rubinius.primitive :fsevent_watch_file
|
||||||
|
raise PrimitiveFailure, "Rubinius::FSEvent#watch_file failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def wait_for_event
|
||||||
|
Rubinius.primitive :fsevent_wait_for_event
|
||||||
|
raise PrimitiveFailure, "Rubinius::FSEvent#wait_for_event failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
str = "#<#{self.class}:0x#{self.__id__.to_s(16)}"
|
||||||
|
|
||||||
|
str << " fileno=#{fileno}" if fileno
|
||||||
|
str << " path=#{path}" if path
|
||||||
|
str << ">"
|
||||||
|
|
||||||
|
Rubinius::Type.infect str, self
|
||||||
|
|
||||||
|
str
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
137
gxg/core/gc.rb
Normal file
137
gxg/core/gc.rb
Normal file
|
|
@ -0,0 +1,137 @@
|
||||||
|
module GC
|
||||||
|
def self.start
|
||||||
|
run(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.run(force)
|
||||||
|
Rubinius.primitive :vm_gc_start
|
||||||
|
raise PrimitiveFailure, "GC.run primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Totally fake.
|
||||||
|
def self.stress
|
||||||
|
@stress_level ||= false
|
||||||
|
end
|
||||||
|
|
||||||
|
# Totally fake.
|
||||||
|
def self.stress=(flag)
|
||||||
|
@stress_level = !!flag
|
||||||
|
end
|
||||||
|
|
||||||
|
# Totally fake.
|
||||||
|
@enabled = true
|
||||||
|
|
||||||
|
def self.enable
|
||||||
|
# We don't support disable, so sure! enabled!
|
||||||
|
ret = !@enabled
|
||||||
|
@enabled = true
|
||||||
|
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
|
# Totally fake.
|
||||||
|
def self.disable
|
||||||
|
# Treat this like a request that we don't honor.
|
||||||
|
ret = !@enabled
|
||||||
|
@enabled = false
|
||||||
|
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
|
def garbage_collect
|
||||||
|
GC.start
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.count
|
||||||
|
if data = stat
|
||||||
|
data[:"gc.young.count"] + data[:"gc.immix.count"]
|
||||||
|
else
|
||||||
|
0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.time
|
||||||
|
if data = stat
|
||||||
|
data[:"gc.young.ms"] +
|
||||||
|
data[:"gc.immix.stop.ms"] +
|
||||||
|
data[:"gc.large.sweep.us"] * 1_000
|
||||||
|
else
|
||||||
|
0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.stat
|
||||||
|
if Rubinius::Metrics.enabled?
|
||||||
|
return Rubinius::Metrics.data.to_hash
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module Profiler
|
||||||
|
@enabled = true
|
||||||
|
@since = 0
|
||||||
|
|
||||||
|
def self.clear
|
||||||
|
@since = GC.time
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.disable
|
||||||
|
# Treat this like a request that we don't honor.
|
||||||
|
ret = !@enabled
|
||||||
|
@enabled = false
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.enable
|
||||||
|
# We don't support disable, so sure! enabled!
|
||||||
|
ret = !@enabled
|
||||||
|
@enabled = true
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.enabled?
|
||||||
|
@enabled
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.report(out = $stdout)
|
||||||
|
out.write result
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.result
|
||||||
|
unless Rubinius::Metrics.enabled?
|
||||||
|
return "Rubinius::Metrics is disabled. GC::Profiler results are unavailable"
|
||||||
|
end
|
||||||
|
|
||||||
|
stats = GC.stat
|
||||||
|
|
||||||
|
out = <<-OUT
|
||||||
|
Complete process runtime statistics
|
||||||
|
===================================
|
||||||
|
|
||||||
|
Collections
|
||||||
|
Count Total time / concurrent (ms)
|
||||||
|
Young #{sprintf("% 22d", stats[:'gc.young.count'])} #{sprintf("% 16d ", stats[:'gc.young.ms'])}
|
||||||
|
Full #{sprintf("% 22d", stats[:'gc.immix.count'])} #{sprintf("% 16d / % 10d", stats[:'gc.immix.stop.ms'], stats[:'gc.immix.concurrent.ms'])}
|
||||||
|
|
||||||
|
Allocation
|
||||||
|
Objects allocated Bytes allocated
|
||||||
|
Young #{sprintf("% 22d", stats[:'memory.young.objects'])} #{sprintf("% 22d", stats[:'memory.young.bytes'])}
|
||||||
|
Promoted#{sprintf("% 22d", stats[:'memory.promoted.objects'])} #{sprintf("% 22d", stats[:'memory.promoted.bytes'])}
|
||||||
|
Mature #{sprintf("% 22d", stats[:'memory.immix.objects'])} #{sprintf("% 22d", stats[:'memory.immix.bytes'])}
|
||||||
|
|
||||||
|
|
||||||
|
Usage
|
||||||
|
Bytes used
|
||||||
|
Young #{sprintf("% 22d", stats[:'memory.young.bytes'])}
|
||||||
|
Mature #{sprintf("% 22d", stats[:'memory.immix.bytes'])}
|
||||||
|
Large #{sprintf("% 22d", stats[:'memory.large.bytes'])}
|
||||||
|
Code #{sprintf("% 22d", stats[:'memory.code.bytes'])}
|
||||||
|
Symbols #{sprintf("% 22d", stats[:'memory.symbols.bytes'])}
|
||||||
|
OUT
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.total_time
|
||||||
|
(GC.time - @since) / 1000.0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
159
gxg/core/global.rb
Normal file
159
gxg/core/global.rb
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
##
|
||||||
|
# Stores global variables and global variable aliases.
|
||||||
|
|
||||||
|
module Rubinius
|
||||||
|
class GlobalVariables
|
||||||
|
def initialize
|
||||||
|
load_path = CoreDB.load_path
|
||||||
|
loaded_features = CoreDB.loaded_features
|
||||||
|
|
||||||
|
@internal = LookupTable.new
|
||||||
|
|
||||||
|
@internal[:$;] = nil
|
||||||
|
@internal[:$/] = "\n" # Input record separator
|
||||||
|
@internal[:$\] = nil # Output record separator
|
||||||
|
@internal[:$<] = ARGF
|
||||||
|
@internal[:$:] = load_path
|
||||||
|
@internal[:$-I] = load_path
|
||||||
|
@internal[:$"] = loaded_features
|
||||||
|
@internal[:$,] = nil # Output field separator
|
||||||
|
@internal[:$.] = 0
|
||||||
|
@internal[:$=] = false # ignore case, whatever that is
|
||||||
|
@internal[:$0] = nil
|
||||||
|
@internal[:$CONSOLE] = STDOUT
|
||||||
|
@internal[:$DEBUG] = false
|
||||||
|
@internal[:$LOADED_FEATURES] = loaded_features
|
||||||
|
@internal[:$LOAD_PATH] = load_path
|
||||||
|
@internal[:$SAFE] = 0
|
||||||
|
|
||||||
|
@internal[:$stderr] = STDERR
|
||||||
|
@internal[:$stdin] = STDIN
|
||||||
|
@internal[:$stdout] = STDOUT
|
||||||
|
|
||||||
|
@alias = LookupTable.new
|
||||||
|
@hooks = LookupTable.new
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def key?(key)
|
||||||
|
@internal.key?(key) || @alias.key?(key)
|
||||||
|
end
|
||||||
|
|
||||||
|
def variables
|
||||||
|
@internal.keys + @alias.keys
|
||||||
|
end
|
||||||
|
|
||||||
|
def [](key)
|
||||||
|
if @internal.key? key
|
||||||
|
@internal[key]
|
||||||
|
elsif @hooks.key? key
|
||||||
|
@hooks[key][0].call(key)
|
||||||
|
elsif @alias.key? key
|
||||||
|
self[@alias[key]]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def []=(key, data)
|
||||||
|
if alias_key = @alias[key]
|
||||||
|
self[alias_key] = data
|
||||||
|
elsif @hooks.key? key
|
||||||
|
hook = @hooks[key]
|
||||||
|
val = hook[1].call(key, data)
|
||||||
|
if hook[2]
|
||||||
|
@internal[key] = val
|
||||||
|
end
|
||||||
|
val
|
||||||
|
else
|
||||||
|
@internal[key] = data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Respects internal and alias, but not hooks. Useful to bypass
|
||||||
|
# normal read only checks.
|
||||||
|
def set!(key, data)
|
||||||
|
if alias_key = @alias[key]
|
||||||
|
set! alias_key, data
|
||||||
|
else
|
||||||
|
@internal[key] = data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_alias(current_name, alias_name)
|
||||||
|
current_name = current_name.to_sym
|
||||||
|
alias_name = alias_name.to_sym
|
||||||
|
|
||||||
|
if @internal.key? alias_name
|
||||||
|
@internal.delete alias_name
|
||||||
|
end
|
||||||
|
|
||||||
|
if @hooks.key? alias_name
|
||||||
|
@hooks.delete alias_name
|
||||||
|
end
|
||||||
|
|
||||||
|
@alias[alias_name] = current_name
|
||||||
|
end
|
||||||
|
|
||||||
|
def illegal_set(*args)
|
||||||
|
raise NameError, "unable to set global"
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_hook(var, getter=nil, setter=nil, &block)
|
||||||
|
if block
|
||||||
|
if getter or setter
|
||||||
|
raise ArgumentError, "duplicate procs provided with block"
|
||||||
|
end
|
||||||
|
|
||||||
|
@hooks[var] = [block, method(:illegal_set)]
|
||||||
|
else
|
||||||
|
set_internal = false
|
||||||
|
set_internal = true if getter == :[]
|
||||||
|
|
||||||
|
if !getter
|
||||||
|
getter = method(:nil_return)
|
||||||
|
elsif getter.kind_of? Symbol
|
||||||
|
getter = method(getter)
|
||||||
|
end
|
||||||
|
|
||||||
|
unless getter.respond_to?(:call)
|
||||||
|
raise ArgumentError, "getter must respond to call"
|
||||||
|
end
|
||||||
|
|
||||||
|
if !setter
|
||||||
|
setter = method(:illegal_set)
|
||||||
|
elsif setter.kind_of? Symbol
|
||||||
|
setter = method(setter)
|
||||||
|
end
|
||||||
|
|
||||||
|
unless setter.respond_to?(:call)
|
||||||
|
raise ArgumentError, "setter must respond to call"
|
||||||
|
end
|
||||||
|
|
||||||
|
@hooks[var] = [getter, setter, set_internal]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_hook(var)
|
||||||
|
@hooks.delete(var)
|
||||||
|
end
|
||||||
|
|
||||||
|
def nil_return
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_filter(var, block)
|
||||||
|
@hooks[var] = [method(:nil_return), block, true]
|
||||||
|
end
|
||||||
|
|
||||||
|
def raise_readonly(key, data)
|
||||||
|
raise NameError, "#{key} is a read-only global variable"
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_only(*vars)
|
||||||
|
mo = method(:raise_readonly)
|
||||||
|
vars.each do |v|
|
||||||
|
set_filter v, mo
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
1004
gxg/core/hash.rb
Normal file
1004
gxg/core/hash.rb
Normal file
File diff suppressed because it is too large
Load diff
23
gxg/core/hook.rb
Normal file
23
gxg/core/hook.rb
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
module Rubinius
|
||||||
|
class Hook
|
||||||
|
def initialize
|
||||||
|
@hooks = []
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def add(obj)
|
||||||
|
@hooks << obj
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove(obj)
|
||||||
|
@hooks.delete(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def trigger!(*args)
|
||||||
|
@hooks.each do |obj|
|
||||||
|
obj.call(*args)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
307
gxg/core/identity_map.rb
Normal file
307
gxg/core/identity_map.rb
Normal file
|
|
@ -0,0 +1,307 @@
|
||||||
|
module Rubinius
|
||||||
|
|
||||||
|
# IdentityMap is customized for uniquely storing elements from an Array to
|
||||||
|
# implement the following Array methods: #&, #|, #-, #uniq, and #uniq!
|
||||||
|
#
|
||||||
|
# The algorithm used is double hashing with an overflow array. For each
|
||||||
|
# array element, the element, its hash value, and its ordinal in the
|
||||||
|
# sequence of elements added to the map are stored.
|
||||||
|
#
|
||||||
|
# Methods are provided to test an element for inclusion in the map and to
|
||||||
|
# delete an entry from the map. The contents of a map can be returned as an
|
||||||
|
# array in the order the elements were added to the map.
|
||||||
|
|
||||||
|
class IdentityMap
|
||||||
|
attr_reader :size
|
||||||
|
|
||||||
|
Row = Table = Rubinius::Tuple
|
||||||
|
MIN_CAPACITY = 64
|
||||||
|
MIN_ROW = 10
|
||||||
|
ROW_GROWTH = 9
|
||||||
|
|
||||||
|
# Converts one or more Enumerable instances to a single IdentityMap
|
||||||
|
def self.from(*arrays, &block)
|
||||||
|
im = allocate
|
||||||
|
Rubinius.privately { im.load(arrays, &block) }
|
||||||
|
im
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
capacity = MIN_CAPACITY
|
||||||
|
@table = Table.new capacity
|
||||||
|
@mask = capacity - 4
|
||||||
|
@max = capacity
|
||||||
|
@size = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
# Adds +item+ to the IdentityMap if it does not already exist. May cause
|
||||||
|
# a row to be added or enlarged. Returns +self+.
|
||||||
|
def insert(item, &block)
|
||||||
|
redistribute if @size > @max
|
||||||
|
|
||||||
|
if block_given?
|
||||||
|
item_hash = yield(item).hash
|
||||||
|
else
|
||||||
|
item_hash = item.hash
|
||||||
|
end
|
||||||
|
|
||||||
|
index = item_hash & @mask
|
||||||
|
table = @table
|
||||||
|
|
||||||
|
if num_entries = table[index]
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
if num_entries == 1
|
||||||
|
return self if match?(table, index, item, item_hash, &block)
|
||||||
|
|
||||||
|
table[index-1] = 2
|
||||||
|
table[index] = promote_row table, index, item, item_hash, @size
|
||||||
|
else
|
||||||
|
i = 1
|
||||||
|
row = table[index]
|
||||||
|
total = row[0]
|
||||||
|
|
||||||
|
while i < total
|
||||||
|
return self if match?(row, i, item, item_hash, &block)
|
||||||
|
i += 3
|
||||||
|
end
|
||||||
|
|
||||||
|
if total == row.size
|
||||||
|
table[index] = enlarge_row row, item, item_hash, @size
|
||||||
|
else
|
||||||
|
i = row[0]
|
||||||
|
set_item row, i, item, item_hash, @size
|
||||||
|
row[0] = i + 3
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
table[index] = 1
|
||||||
|
set_item table, index+1, item, item_hash, @size
|
||||||
|
end
|
||||||
|
@size += 1
|
||||||
|
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns +true+ if +item+ is in the IdentityMap, +false+ otherwise.
|
||||||
|
def include?(item)
|
||||||
|
item_hash = item.hash
|
||||||
|
|
||||||
|
index = item_hash & @mask
|
||||||
|
table = @table
|
||||||
|
if num_entries = table[index]
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
if num_entries == 1
|
||||||
|
return true if match? table, index, item, item_hash
|
||||||
|
else
|
||||||
|
row = table[index]
|
||||||
|
i = 1
|
||||||
|
total = row[0]
|
||||||
|
while i < total
|
||||||
|
return true if match? row, i, item, item_hash
|
||||||
|
i += 3
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
# If +item+ is in the IdentityMap, removes it and returns +true+.
|
||||||
|
# Otherwise, returns +false+.
|
||||||
|
def delete(item)
|
||||||
|
item_hash = item.hash
|
||||||
|
|
||||||
|
index = item_hash & @mask
|
||||||
|
table = @table
|
||||||
|
|
||||||
|
if num_entries = table[index]
|
||||||
|
index += 1
|
||||||
|
if num_entries == 1
|
||||||
|
if match? table, index, item, item_hash
|
||||||
|
table[index] = nil
|
||||||
|
@size -= 1
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
row = table[index]
|
||||||
|
i = 1
|
||||||
|
total = row[0]
|
||||||
|
while i < total
|
||||||
|
if match? row, i, item, item_hash
|
||||||
|
row[i] = nil
|
||||||
|
@size -= 1
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
i += 3
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns an Array containing all items in the IdentityMap in the order
|
||||||
|
# in which they were added to the IdentityMap.
|
||||||
|
def to_array
|
||||||
|
array = Array.new @size
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
table = @table
|
||||||
|
total = table.size
|
||||||
|
|
||||||
|
while i < total
|
||||||
|
if num_entries = table[i]
|
||||||
|
if num_entries == 1
|
||||||
|
array[table[i+3]] = table[i+2] if table[i+1]
|
||||||
|
else
|
||||||
|
row = table[i+1]
|
||||||
|
k = row[0]
|
||||||
|
j = 1
|
||||||
|
while j < k
|
||||||
|
array[row[j+2]] = row[j+1] if row[j]
|
||||||
|
j += 3
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
i += 4
|
||||||
|
end
|
||||||
|
|
||||||
|
array
|
||||||
|
end
|
||||||
|
|
||||||
|
# Private implementation methods
|
||||||
|
|
||||||
|
def resize(total)
|
||||||
|
capacity = MIN_CAPACITY
|
||||||
|
while capacity < total
|
||||||
|
capacity <<= 2
|
||||||
|
end
|
||||||
|
|
||||||
|
@table = Table.new capacity
|
||||||
|
@mask = capacity - 4
|
||||||
|
@max = capacity
|
||||||
|
end
|
||||||
|
private :resize
|
||||||
|
|
||||||
|
def redistribute
|
||||||
|
table = @table
|
||||||
|
resize @size
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
total = table.size
|
||||||
|
|
||||||
|
while i < total
|
||||||
|
if num_entries = table[i]
|
||||||
|
if num_entries == 1
|
||||||
|
if item_hash = table[i+1]
|
||||||
|
add_item table[i+2], item_hash, table[i+3]
|
||||||
|
end
|
||||||
|
else
|
||||||
|
row = table[i+1]
|
||||||
|
k = row[0]
|
||||||
|
j = 1
|
||||||
|
while j < k
|
||||||
|
if item_hash = row[j]
|
||||||
|
add_item row[j+1], item_hash, row[j+2]
|
||||||
|
end
|
||||||
|
j += 3
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
i += 4
|
||||||
|
end
|
||||||
|
end
|
||||||
|
private :redistribute
|
||||||
|
|
||||||
|
def add_item(item, item_hash, ordinal)
|
||||||
|
index = item_hash & @mask
|
||||||
|
table = @table
|
||||||
|
|
||||||
|
if num_entries = table[index]
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
if num_entries == 1
|
||||||
|
table[index-1] = 2
|
||||||
|
table[index] = promote_row table, index, item, item_hash, ordinal
|
||||||
|
else
|
||||||
|
row = table[index]
|
||||||
|
i = row[0]
|
||||||
|
|
||||||
|
if i == row.size
|
||||||
|
table[index] = enlarge_row row, item, item_hash, ordinal
|
||||||
|
else
|
||||||
|
set_item row, i, item, item_hash, ordinal
|
||||||
|
row[0] = i + 3
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
table[index] = 1
|
||||||
|
set_item table, index+1, item, item_hash, ordinal
|
||||||
|
end
|
||||||
|
end
|
||||||
|
private :add_item
|
||||||
|
|
||||||
|
# Given an Array of Enumerable instances, computes a bounding set
|
||||||
|
# to contain them and then adds each item to the IdentityMap.
|
||||||
|
def load(arrays, &block)
|
||||||
|
resize(arrays.inject(0) { |sum, array| sum + array.size })
|
||||||
|
@size = 0
|
||||||
|
|
||||||
|
arrays.each do |array|
|
||||||
|
array.each { |item| insert(item, &block) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
private :load
|
||||||
|
|
||||||
|
def match?(table, index, item, item_hash)
|
||||||
|
return false unless table[index] == item_hash
|
||||||
|
other = table[index+1]
|
||||||
|
if block_given?
|
||||||
|
item = yield item
|
||||||
|
other = yield other
|
||||||
|
end
|
||||||
|
Rubinius::Type.object_equal(item, other) or item.eql?(other)
|
||||||
|
end
|
||||||
|
private :match?
|
||||||
|
|
||||||
|
def set_item(table, index, item, item_hash, ordinal)
|
||||||
|
table[index] = item_hash
|
||||||
|
table[index+1] = item
|
||||||
|
table[index+2] = ordinal
|
||||||
|
end
|
||||||
|
private :set_item
|
||||||
|
|
||||||
|
def promote_row(row, index, item, item_hash, ordinal)
|
||||||
|
new_row = Row.new MIN_ROW
|
||||||
|
|
||||||
|
new_row[0] = 7
|
||||||
|
new_row[1] = row[index]
|
||||||
|
new_row[2] = row[index+1]
|
||||||
|
new_row[3] = row[index+2]
|
||||||
|
new_row[4] = item_hash
|
||||||
|
new_row[5] = item
|
||||||
|
new_row[6] = ordinal
|
||||||
|
|
||||||
|
new_row
|
||||||
|
end
|
||||||
|
private :promote_row
|
||||||
|
|
||||||
|
def enlarge_row(row, item, item_hash, ordinal)
|
||||||
|
new_row = Row.new row.size + ROW_GROWTH
|
||||||
|
new_row.copy_from row, 1, row.size-1, 1
|
||||||
|
|
||||||
|
index = row[0]
|
||||||
|
new_row[0] = index + 3
|
||||||
|
set_item new_row, index, item, item_hash, ordinal
|
||||||
|
|
||||||
|
new_row
|
||||||
|
end
|
||||||
|
private :enlarge_row
|
||||||
|
end
|
||||||
|
end
|
||||||
31
gxg/core/immediate.rb
Normal file
31
gxg/core/immediate.rb
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
##
|
||||||
|
# Mixin used to identify classes which have no memory storage.
|
||||||
|
|
||||||
|
module ImmediateValue
|
||||||
|
def singleton_methods(all=true)
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
|
def public_singleton_methods
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
private :public_singleton_methods
|
||||||
|
|
||||||
|
def private_singleton_methods
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
private :private_singleton_methods
|
||||||
|
|
||||||
|
def protected_singleton_methods
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
private :protected_singleton_methods
|
||||||
|
|
||||||
|
def dup
|
||||||
|
raise TypeError, "can't dup #{self.class.name}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def clone
|
||||||
|
raise TypeError, "can't clone #{self.class.name}"
|
||||||
|
end
|
||||||
|
end
|
||||||
1580
gxg/core/integer.rb
Normal file
1580
gxg/core/integer.rb
Normal file
File diff suppressed because it is too large
Load diff
3609
gxg/core/io.rb
Normal file
3609
gxg/core/io.rb
Normal file
File diff suppressed because it is too large
Load diff
36
gxg/core/iseq.rb
Normal file
36
gxg/core/iseq.rb
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
module Rubinius
|
||||||
|
class InstructionSequence
|
||||||
|
def self.allocate
|
||||||
|
Rubinius.primitive :instruction_sequence_allocate
|
||||||
|
raise PrimitiveFailure, "Rubinius::InstructionSequence.allocate primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(size)
|
||||||
|
if size.kind_of? Tuple
|
||||||
|
@opcodes = size
|
||||||
|
else
|
||||||
|
@opcodes = Tuple.new(size)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
attr_reader :opcodes
|
||||||
|
|
||||||
|
def ==(other)
|
||||||
|
other.kind_of?(InstructionSequence) and @opcodes == other.opcodes
|
||||||
|
end
|
||||||
|
|
||||||
|
def []=(idx, val)
|
||||||
|
@opcodes[idx] = val
|
||||||
|
end
|
||||||
|
|
||||||
|
def [](idx)
|
||||||
|
@opcodes[idx]
|
||||||
|
end
|
||||||
|
|
||||||
|
def size
|
||||||
|
@opcodes.size
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
32
gxg/core/jit.rb
Normal file
32
gxg/core/jit.rb
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
module Rubinius
|
||||||
|
module JIT
|
||||||
|
class << self
|
||||||
|
attr_accessor :properties
|
||||||
|
|
||||||
|
def compile(object, compiled_code, block_environment=nil)
|
||||||
|
Rubinius.invoke_primitive :jit_compile, self, object, compiled_code, block_environment
|
||||||
|
end
|
||||||
|
|
||||||
|
def enabled?
|
||||||
|
Rubinius.primitive :jit_enabled_p
|
||||||
|
raise PrimitiveFailure, "Rubinius::JIT.enabled? primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: Fix configuration
|
||||||
|
def compile_threshold
|
||||||
|
Rubinius.primitive :jit_compile_threshold
|
||||||
|
raise PrimitiveFailure, "Rubinius::JIT.compile_threshold primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def sync=(flag)
|
||||||
|
Rubinius.primitive :jit_sync_set
|
||||||
|
raise PrimitiveFailure, "Rubinius::JIT.sync primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def sync
|
||||||
|
Rubinius.primitive :jit_sync_get
|
||||||
|
raise PrimitiveFailure, "Rubinius::JIT.sync primitive failed"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
1142
gxg/core/kernel.rb
Normal file
1142
gxg/core/kernel.rb
Normal file
File diff suppressed because it is too large
Load diff
181
gxg/core/lexical_scope.rb
Normal file
181
gxg/core/lexical_scope.rb
Normal file
|
|
@ -0,0 +1,181 @@
|
||||||
|
##
|
||||||
|
# A linked list that details the static, lexical scope the method was created
|
||||||
|
# in.
|
||||||
|
#
|
||||||
|
# TODO: document
|
||||||
|
|
||||||
|
module Rubinius
|
||||||
|
class LexicalScope
|
||||||
|
def self.of_sender
|
||||||
|
Rubinius.primitive :lexical_scope_of_sender
|
||||||
|
raise PrimitiveFailure, "primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def class_variable_get(sym)
|
||||||
|
Rubinius.primitive :lexical_scope_cvar_get
|
||||||
|
|
||||||
|
raise NameError, "Invalid class variable name '#{sym}'"
|
||||||
|
end
|
||||||
|
|
||||||
|
def class_variable_set(sym, val)
|
||||||
|
Rubinius.primitive :lexical_scope_cvar_set
|
||||||
|
|
||||||
|
raise NameError, "Invalid class variable name '#{sym}'"
|
||||||
|
end
|
||||||
|
|
||||||
|
def class_variable_defined?(sym)
|
||||||
|
Rubinius.primitive :lexical_scope_cvar_defined
|
||||||
|
|
||||||
|
raise NameError, "Invalid class variable name '#{sym}'"
|
||||||
|
end
|
||||||
|
|
||||||
|
def class_variable_get_or_set(sym, val)
|
||||||
|
Rubinius.primitive :lexical_scope_cvar_get_or_set
|
||||||
|
|
||||||
|
raise NameError, "Invalid class variable name '#{sym}'"
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# @todo Verify the recursion here does not cause problems. --rue
|
||||||
|
#
|
||||||
|
def initialize(mod, par = nil)
|
||||||
|
@module = mod
|
||||||
|
@parent = par
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
# Source code of this scope.
|
||||||
|
attr_accessor :script
|
||||||
|
|
||||||
|
# The function map for this scope references its top-most parent's map
|
||||||
|
attr_accessor :functions
|
||||||
|
|
||||||
|
# Module or class this lexical scope enclosed into.
|
||||||
|
attr_reader :module
|
||||||
|
|
||||||
|
# Constant scope object this scope enclosed into.
|
||||||
|
attr_reader :parent
|
||||||
|
|
||||||
|
# Module or class representing the 'current class'. MRI manipulates
|
||||||
|
# this outside of the lexical scope and uses it for undef and method
|
||||||
|
# definition.
|
||||||
|
attr_accessor :current_module
|
||||||
|
|
||||||
|
# Set to indicate that no methods may be add to this scope
|
||||||
|
attr_accessor :disabled_for_methods
|
||||||
|
|
||||||
|
# Lazy initialized hash map used for flip-flops
|
||||||
|
attr_accessor :flip_flops
|
||||||
|
|
||||||
|
def const_defined?(name, search_parents=true)
|
||||||
|
scope = self
|
||||||
|
while scope and scope.module != Object
|
||||||
|
return true if scope.module.const_defined?(name)
|
||||||
|
scope = scope.parent
|
||||||
|
end
|
||||||
|
|
||||||
|
return Object.const_defined?(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
current = " current=#{@current_module}" if @current_module
|
||||||
|
"#<#{self.class.name}:0x#{self.object_id.to_s(16)} parent=#{@parent.inspect} module=#{@module}#{current}>"
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
inspect
|
||||||
|
end
|
||||||
|
|
||||||
|
# Indicates if this is a toplevel/default scope
|
||||||
|
def top_level?
|
||||||
|
!@parent
|
||||||
|
end
|
||||||
|
|
||||||
|
# Use the same info as the current LexicalScope, but set current_module to
|
||||||
|
# +mod+. Chains off the current LexicalScope.
|
||||||
|
def using_current_as(mod)
|
||||||
|
if top_level?
|
||||||
|
# Don't chain up if this is a toplevel, create a new one.
|
||||||
|
cs = dup
|
||||||
|
else
|
||||||
|
cs = LexicalScope.new @module, self
|
||||||
|
end
|
||||||
|
|
||||||
|
cs.current_module = mod
|
||||||
|
return cs
|
||||||
|
end
|
||||||
|
|
||||||
|
def using_disabled_scope
|
||||||
|
cs = using_current_as(@module)
|
||||||
|
cs.disabled_for_methods = true
|
||||||
|
return cs
|
||||||
|
end
|
||||||
|
|
||||||
|
def for_method_definition
|
||||||
|
if @disabled_for_methods
|
||||||
|
raise TypeError, "unable to create methods in this scope"
|
||||||
|
end
|
||||||
|
|
||||||
|
return @current_module if @current_module
|
||||||
|
return @module
|
||||||
|
end
|
||||||
|
|
||||||
|
def alias_method(name, original)
|
||||||
|
Rubinius.privately do
|
||||||
|
for_method_definition.alias_method name, original
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def __undef_method__(name)
|
||||||
|
Rubinius.privately do
|
||||||
|
for_method_definition.undef_method name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def active_path
|
||||||
|
script = current_script
|
||||||
|
if script
|
||||||
|
if path = script.file_path
|
||||||
|
return path.dup
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return "__unknown__.rb"
|
||||||
|
end
|
||||||
|
|
||||||
|
def absolute_active_path
|
||||||
|
script = current_script
|
||||||
|
if script
|
||||||
|
if path = script.data_path
|
||||||
|
return path.dup
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def data_path
|
||||||
|
script = current_script
|
||||||
|
if script and script.main?
|
||||||
|
if path = script.data_path
|
||||||
|
return path.dup
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def current_script
|
||||||
|
scope = self
|
||||||
|
while scope and !scope.script
|
||||||
|
scope = scope.parent
|
||||||
|
end
|
||||||
|
return scope && scope.script
|
||||||
|
end
|
||||||
|
|
||||||
|
def const_set(name, value)
|
||||||
|
@module.const_set name, value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
319
gxg/core/library.rb
Normal file
319
gxg/core/library.rb
Normal file
|
|
@ -0,0 +1,319 @@
|
||||||
|
class Rubinius::NativeFunction
|
||||||
|
attr_accessor :return_type
|
||||||
|
attr_accessor :argument_types
|
||||||
|
end
|
||||||
|
|
||||||
|
module Rubinius
|
||||||
|
module FFI
|
||||||
|
def self.generate_function(ptr, name, args, ret)
|
||||||
|
Rubinius.primitive :nativefunction_generate
|
||||||
|
raise PrimitiveFailure, "FFI.generate_function primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.generate_trampoline(obj, name, args, ret)
|
||||||
|
Rubinius.primitive :nativefunction_generate_tramp
|
||||||
|
raise PrimitiveFailure, "FFI.generate_function_tramp primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
module Library
|
||||||
|
LIBC = Rubinius::LIBC
|
||||||
|
|
||||||
|
# Set which library or libraries +attach_function+ should
|
||||||
|
# look in. By default it only searches for the function in
|
||||||
|
# the current process. If you want to specify this as one
|
||||||
|
# of the locations, add FFI::USE_THIS_PROCESS_AS_LIBRARY.
|
||||||
|
# The libraries are tried in the order given.
|
||||||
|
#
|
||||||
|
def ffi_lib(*names)
|
||||||
|
@ffi_lib = names.map do |x|
|
||||||
|
if x == FFI::CURRENT_PROCESS
|
||||||
|
DynamicLibrary::CURRENT_PROCESS
|
||||||
|
|
||||||
|
# When the element is an array, it's an ordered choice,
|
||||||
|
# ie, pick the first library that works.
|
||||||
|
elsif x.kind_of? Array
|
||||||
|
lib = nil
|
||||||
|
|
||||||
|
x.each do |name|
|
||||||
|
begin
|
||||||
|
lib = DynamicLibrary.new(name, @ffi_lib_flags)
|
||||||
|
break
|
||||||
|
rescue LoadError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# If .new worked, then lib is set and we can use it.
|
||||||
|
unless lib
|
||||||
|
raise LoadError, "Unable to find library among: #{x.inspect}"
|
||||||
|
end
|
||||||
|
|
||||||
|
lib
|
||||||
|
else
|
||||||
|
DynamicLibrary.new(x)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def ffi_libraries
|
||||||
|
@ffi_lib or [DynamicLibrary::CURRENT_PROCESS]
|
||||||
|
end
|
||||||
|
private :ffi_libraries
|
||||||
|
|
||||||
|
# Flags used in {#ffi_lib}.
|
||||||
|
#
|
||||||
|
# This map allows you to supply symbols to {#ffi_lib_flags} instead of
|
||||||
|
# the actual constants.
|
||||||
|
def flags_map
|
||||||
|
{
|
||||||
|
:global => DynamicLibrary::RTLD_GLOBAL,
|
||||||
|
:local => DynamicLibrary::RTLD_LOCAL,
|
||||||
|
:lazy => DynamicLibrary::RTLD_LAZY,
|
||||||
|
:now => DynamicLibrary::RTLD_NOW
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sets library flags for {#ffi_lib}.
|
||||||
|
#
|
||||||
|
# @example
|
||||||
|
# ffi_lib_flags(:lazy, :local) # => 5
|
||||||
|
#
|
||||||
|
# @param [Symbol, …] flags (see {FlagsMap})
|
||||||
|
# @return [Fixnum] the new value
|
||||||
|
def ffi_lib_flags(*flags)
|
||||||
|
@ffi_lib_flags = flags.inject(0) { |result, f| result | flags_map[f] }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Attach a C function to this module. The arguments can have two forms:
|
||||||
|
#
|
||||||
|
# attach_function c_name, [c_arg1, c_arg2], ret
|
||||||
|
# attach_function mod_name, c_name, [c_arg1, c_arg2], ret
|
||||||
|
#
|
||||||
|
# In the first form, +c_name+ will also be used for the name of the module
|
||||||
|
# method. In the second form, the module method name is +mod_name+.
|
||||||
|
#
|
||||||
|
# In either form, an optional options hash will be accepted as an
|
||||||
|
# additional argument, although currently all options are ignored.
|
||||||
|
#
|
||||||
|
# The +c_name+ and +mod_name+ can be given as Strings or Symbols.
|
||||||
|
#
|
||||||
|
# The types of the arguments to the C function, +c_arg1+, +c_arg2+, etc, are
|
||||||
|
# given as an array even if there is only one.
|
||||||
|
#
|
||||||
|
# The final argument, +ret+, is the type of the return value from the C
|
||||||
|
# function.
|
||||||
|
def attach_function(name, a2, a3, a4=nil, a5=nil)
|
||||||
|
if a4 && (a2.kind_of?(String) || a2.kind_of?(Symbol))
|
||||||
|
cname = a2.to_s
|
||||||
|
args = a3
|
||||||
|
ret = a4
|
||||||
|
else
|
||||||
|
cname = name.to_s
|
||||||
|
args = a2
|
||||||
|
ret = a3
|
||||||
|
end
|
||||||
|
|
||||||
|
mname = name.to_sym
|
||||||
|
|
||||||
|
ffi_libraries.each do |lib|
|
||||||
|
if ptr = lib.find_symbol(cname)
|
||||||
|
return pointer_as_function(mname, ptr, args, ret)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ffi_function_missing cname, mname, args, ret
|
||||||
|
end
|
||||||
|
|
||||||
|
# Attach a C variable to this module. The arguments can have two forms:
|
||||||
|
#
|
||||||
|
# attach_variable mod_name, type
|
||||||
|
# attach_variable mod_name, c_name, type
|
||||||
|
#
|
||||||
|
# In the first form, +mod_name+ will also be used for the name of the C variable.
|
||||||
|
# In the second form, the C variable name is +c_name+.
|
||||||
|
#
|
||||||
|
# The +mod_name+ and +c_name+ can be given as Strings or Symbols.
|
||||||
|
#
|
||||||
|
# The final argument, +type+, is the type of the C variable
|
||||||
|
def attach_variable(mname, a1, a2=nil)
|
||||||
|
cname, type = a2 ? [ a1, a2 ] : [ mname.to_s, a1 ]
|
||||||
|
ptr = nil
|
||||||
|
|
||||||
|
ffi_libraries.each do |lib|
|
||||||
|
ptr = lib.find_symbol(cname.to_s)
|
||||||
|
break unless ptr.nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
raise FFI::NotFoundError, "Unable to find '#{cname}'" if ptr.nil? || ptr.null?
|
||||||
|
|
||||||
|
if type.kind_of?(Class) and type.ancestors.include?(FFI::Struct)
|
||||||
|
c = type.new(ptr)
|
||||||
|
|
||||||
|
self.module_eval <<-code, __FILE__, __LINE__
|
||||||
|
@ffi_gvar_#{mname} = c
|
||||||
|
def self.#{mname}
|
||||||
|
@ffi_gvar_#{mname}
|
||||||
|
end
|
||||||
|
code
|
||||||
|
|
||||||
|
else
|
||||||
|
enclosing_module = self
|
||||||
|
|
||||||
|
cs = Class.new(FFI::Struct) do
|
||||||
|
@enclosing_module = enclosing_module
|
||||||
|
end
|
||||||
|
|
||||||
|
cs.layout :gvar, type
|
||||||
|
c = cs.new(ptr)
|
||||||
|
|
||||||
|
self.module_eval <<-code, __FILE__, __LINE__
|
||||||
|
@ffi_gvar_#{mname} = c
|
||||||
|
def self.#{mname}
|
||||||
|
@ffi_gvar_#{mname}[:gvar]
|
||||||
|
end
|
||||||
|
def self.#{mname}=(value)
|
||||||
|
@ffi_gvar_#{mname}[:gvar] = value
|
||||||
|
end
|
||||||
|
code
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return ptr
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generic error method attached in place of missing foreign functions
|
||||||
|
# during loading the core library. See core/zed.rb for a version that
|
||||||
|
# raises immediately if the foreign function is unavaiblable.
|
||||||
|
def ffi_function_not_implemented(*args)
|
||||||
|
raise NotImplementedError, "function not implemented on this platform"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Protocol for attaching foregin functions. If #attach_function fails to
|
||||||
|
# find a foreign function, this method will be called. Client code can
|
||||||
|
# provide an override to customize features.
|
||||||
|
def ffi_function_missing(cname, mname, args, ret)
|
||||||
|
if func = Rubinius.find_method(self, :ffi_function_not_implemented)
|
||||||
|
func = func[0].dup
|
||||||
|
func.name = cname.to_sym
|
||||||
|
add_function mname, func
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_function(name, func)
|
||||||
|
# Make it available as a method callable directly..
|
||||||
|
sc = Rubinius::Type.object_singleton_class(self)
|
||||||
|
Rubinius::VM.reset_method_cache sc, name
|
||||||
|
|
||||||
|
sc.method_table.store name, nil, func, nil, 0, :public
|
||||||
|
|
||||||
|
# and expose it as a private method for people who
|
||||||
|
# want to include this module.
|
||||||
|
method_table.store name, nil, func, nil, 0, :private
|
||||||
|
end
|
||||||
|
private :add_function
|
||||||
|
|
||||||
|
def pointer_as_function(name, ptr, args, ret)
|
||||||
|
args.map! { |a| find_type a }
|
||||||
|
|
||||||
|
if func = FFI.generate_function(ptr, name.to_sym, args, find_type(ret))
|
||||||
|
add_function name, func
|
||||||
|
return func
|
||||||
|
end
|
||||||
|
|
||||||
|
raise FFI::NotFoundError, "Unable to attach pointer"
|
||||||
|
end
|
||||||
|
|
||||||
|
def callback(a1, a2, a3=nil)
|
||||||
|
if a3
|
||||||
|
name = a1
|
||||||
|
params = a2
|
||||||
|
ret = a3
|
||||||
|
else
|
||||||
|
name = nil
|
||||||
|
params = a1
|
||||||
|
ret = a2
|
||||||
|
end
|
||||||
|
|
||||||
|
args = params.map { |x| find_type(x) }
|
||||||
|
|
||||||
|
func, ptr = FFI.generate_trampoline nil, :ffi_tramp,
|
||||||
|
args, find_type(ret)
|
||||||
|
|
||||||
|
func.argument_types = params
|
||||||
|
func.return_type = ret
|
||||||
|
|
||||||
|
if name
|
||||||
|
@ffi_callbacks ||= {}
|
||||||
|
@ffi_callbacks[name] = func
|
||||||
|
|
||||||
|
typedef func, name
|
||||||
|
end
|
||||||
|
|
||||||
|
return func
|
||||||
|
end
|
||||||
|
|
||||||
|
def typedef(old, add)
|
||||||
|
@typedefs ||= Rubinius::LookupTable.new
|
||||||
|
@typedefs[add] = find_type(old)
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_type(name)
|
||||||
|
@typedefs ||= Rubinius::LookupTable.new
|
||||||
|
|
||||||
|
if name.kind_of? Rubinius::NativeFunction or name.kind_of? FFI::Enum
|
||||||
|
return name
|
||||||
|
end
|
||||||
|
|
||||||
|
if type = @typedefs[name]
|
||||||
|
return type
|
||||||
|
end
|
||||||
|
|
||||||
|
FFI.find_type(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def enum(*args)
|
||||||
|
@tagged_enums ||= Rubinius::LookupTable.new
|
||||||
|
@anon_enums ||= Array.new
|
||||||
|
|
||||||
|
tag, values = if args[0].kind_of?(Symbol) && args[1].kind_of?(Array)
|
||||||
|
[ args[0], args[1] ]
|
||||||
|
elsif args[0].kind_of?(Array)
|
||||||
|
[ nil, args[0] ]
|
||||||
|
else
|
||||||
|
[ nil, args ]
|
||||||
|
end
|
||||||
|
|
||||||
|
enum = FFI::Enum.new values, tag
|
||||||
|
|
||||||
|
if tag
|
||||||
|
typedef(enum, tag)
|
||||||
|
@tagged_enums[tag] = enum
|
||||||
|
else
|
||||||
|
@anon_enums << enum
|
||||||
|
end
|
||||||
|
|
||||||
|
return enum
|
||||||
|
end
|
||||||
|
|
||||||
|
def enum_type(tag)
|
||||||
|
|
||||||
|
if enum = @tagged_enums[tag]
|
||||||
|
enum
|
||||||
|
else
|
||||||
|
@anon_enums.detect { |enum| enum.symbols.include?(tag) }
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def enum_value(value)
|
||||||
|
|
||||||
|
if enum = @anon_enums.detect { |enum| enum.symbols.include?(value) }
|
||||||
|
enum
|
||||||
|
else
|
||||||
|
tag,enum = @tagged_enums.detect { |tag,enum| enum.symbols.include?(value) }
|
||||||
|
return enum
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
130
gxg/core/load_order.txt
Normal file
130
gxg/core/load_order.txt
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
alpha.rb
|
||||||
|
alias.rb
|
||||||
|
argf.rb
|
||||||
|
array.rb
|
||||||
|
array_mirror.rb
|
||||||
|
atomic.rb
|
||||||
|
autoload.rb
|
||||||
|
backtrace.rb
|
||||||
|
basic_object.rb
|
||||||
|
binding.rb
|
||||||
|
block_environment.rb
|
||||||
|
byte_array.rb
|
||||||
|
call_site.rb
|
||||||
|
capi.rb
|
||||||
|
channel.rb
|
||||||
|
character.rb
|
||||||
|
class.rb
|
||||||
|
code_db.rb
|
||||||
|
code_loader.rb
|
||||||
|
compact_lookup_table.rb
|
||||||
|
comparable.rb
|
||||||
|
compiled_code.rb
|
||||||
|
complex.rb
|
||||||
|
complexifier.rb
|
||||||
|
configuration.rb
|
||||||
|
console.rb
|
||||||
|
constant_cache.rb
|
||||||
|
lexical_scope.rb
|
||||||
|
constant_table.rb
|
||||||
|
continuation.rb
|
||||||
|
ctype.rb
|
||||||
|
delegated_method.rb
|
||||||
|
dir.rb
|
||||||
|
dir_glob.rb
|
||||||
|
encoding.rb
|
||||||
|
enumerable.rb
|
||||||
|
enumerable_helper.rb
|
||||||
|
enumerator.rb
|
||||||
|
env.rb
|
||||||
|
errno.rb
|
||||||
|
exception.rb
|
||||||
|
executable.rb
|
||||||
|
false.rb
|
||||||
|
ffi.rb
|
||||||
|
fiber.rb
|
||||||
|
file.rb
|
||||||
|
file_test.rb
|
||||||
|
float.rb
|
||||||
|
fsevent.rb
|
||||||
|
gc.rb
|
||||||
|
global.rb
|
||||||
|
hash.rb
|
||||||
|
hook.rb
|
||||||
|
identity_map.rb
|
||||||
|
immediate.rb
|
||||||
|
integer.rb
|
||||||
|
io.rb
|
||||||
|
iseq.rb
|
||||||
|
jit.rb
|
||||||
|
kernel.rb
|
||||||
|
library.rb
|
||||||
|
location.rb
|
||||||
|
logger.rb
|
||||||
|
lookup_table.rb
|
||||||
|
lru_cache.rb
|
||||||
|
main.rb
|
||||||
|
marshal.rb
|
||||||
|
match_data.rb
|
||||||
|
math.rb
|
||||||
|
method.rb
|
||||||
|
method_equality.rb
|
||||||
|
method_table.rb
|
||||||
|
metrics.rb
|
||||||
|
mirror.rb
|
||||||
|
missing_method.rb
|
||||||
|
module.rb
|
||||||
|
mutex.rb
|
||||||
|
native_method.rb
|
||||||
|
nil.rb
|
||||||
|
numeric.rb
|
||||||
|
numeric_mirror.rb
|
||||||
|
object_space.rb
|
||||||
|
options.rb
|
||||||
|
pack.rb
|
||||||
|
peg.rb
|
||||||
|
pointer.rb
|
||||||
|
pointer_accessors.rb
|
||||||
|
prediction.rb
|
||||||
|
proc.rb
|
||||||
|
proc_mirror.rb
|
||||||
|
process.rb
|
||||||
|
process_mirror.rb
|
||||||
|
random.rb
|
||||||
|
range.rb
|
||||||
|
range_mirror.rb
|
||||||
|
rational.rb
|
||||||
|
rationalizer.rb
|
||||||
|
regexp.rb
|
||||||
|
respond_to_cache.rb
|
||||||
|
rubinius.rb
|
||||||
|
runtime.rb
|
||||||
|
signal.rb
|
||||||
|
splitter.rb
|
||||||
|
sprinter.rb
|
||||||
|
stat.rb
|
||||||
|
stats.rb
|
||||||
|
string.rb
|
||||||
|
string_mirror.rb
|
||||||
|
struct.rb
|
||||||
|
symbol.rb
|
||||||
|
thread.rb
|
||||||
|
thread_group.rb
|
||||||
|
thread_group_mirror.rb
|
||||||
|
thread_mirror.rb
|
||||||
|
throw_catch.rb
|
||||||
|
thunk.rb
|
||||||
|
time.rb
|
||||||
|
true.rb
|
||||||
|
tuple.rb
|
||||||
|
type.rb
|
||||||
|
undefined.rb
|
||||||
|
variable_scope.rb
|
||||||
|
vm.rb
|
||||||
|
weakref.rb
|
||||||
|
zed.rb
|
||||||
|
ruby_constants.rb
|
||||||
|
build_config.rb
|
||||||
|
rbconfig.rb
|
||||||
|
deprecations.rb
|
||||||
|
loader.rb
|
||||||
872
gxg/core/loader.rb
Normal file
872
gxg/core/loader.rb
Normal file
|
|
@ -0,0 +1,872 @@
|
||||||
|
TOPLEVEL_BINDING = binding()
|
||||||
|
|
||||||
|
module Rubinius
|
||||||
|
class Loader
|
||||||
|
def initialize
|
||||||
|
@exit_code = 0
|
||||||
|
@process_id = Process.pid
|
||||||
|
@load_paths = []
|
||||||
|
@requires = []
|
||||||
|
@evals = []
|
||||||
|
@script = nil
|
||||||
|
@debugging = false
|
||||||
|
@repl = true
|
||||||
|
@printed_version = false
|
||||||
|
@input_loop = false
|
||||||
|
@input_loop_print = false
|
||||||
|
@input_loop_split = false
|
||||||
|
@simple_options = false
|
||||||
|
@early_option_stop = false
|
||||||
|
@check_syntax = false
|
||||||
|
@load_profiler = false
|
||||||
|
@enable_gems = true
|
||||||
|
@load_gemfile = false
|
||||||
|
@deprecations = false
|
||||||
|
|
||||||
|
version = RUBY_VERSION.split(".").first(2).join(".")
|
||||||
|
@gem_bins = ["#{version}/bin", "bin"].map do |dir|
|
||||||
|
File.join Rubinius::GEMS_PATH, dir
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def self.debugger
|
||||||
|
@debugger_proc
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.debugger=(prc)
|
||||||
|
@debugger_proc = prc
|
||||||
|
end
|
||||||
|
|
||||||
|
# Finish setting up after loading the core library.
|
||||||
|
def preamble
|
||||||
|
set_program_name "rbx"
|
||||||
|
@stage = "running Loader preamble"
|
||||||
|
|
||||||
|
# Set the default visibility for the top level binding
|
||||||
|
TOPLEVEL_BINDING.variables.method_visibility = :private
|
||||||
|
|
||||||
|
$VERBOSE = false
|
||||||
|
end
|
||||||
|
|
||||||
|
# Set up $LOAD_PATH.
|
||||||
|
def system_load_path
|
||||||
|
@stage = "setting up system load path"
|
||||||
|
|
||||||
|
@lib_bin = "#{Rubinius::CODEDB_PATH}/source/bin"
|
||||||
|
|
||||||
|
$LOAD_PATH.unshift(Rubinius::ARCH_PATH,
|
||||||
|
Rubinius::SITE_PATH,
|
||||||
|
"#{Rubinius::SITE_PATH}/#{RUBY_VERSION}",
|
||||||
|
Rubinius::VENDOR_PATH,
|
||||||
|
"#{Rubinius::CODEDB_PATH}/source")
|
||||||
|
|
||||||
|
if libs = ENV['RUBYLIB']
|
||||||
|
Rubinius::Logger.system.write "RUBYLIB: #{libs}"
|
||||||
|
|
||||||
|
$LOAD_PATH.unshift(*libs.split(File::PATH_SEPARATOR).compact)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Load customization code:
|
||||||
|
# /etc/rbxrc
|
||||||
|
# $HOME/.rbxrc
|
||||||
|
# $RBX_PRELOAD
|
||||||
|
def preload
|
||||||
|
@stage = "preloading rbxrc code"
|
||||||
|
|
||||||
|
['/etc/rbxrc',"#{ENV['HOME']}/.rbxrc",ENV['RBX_PRELOAD']].each do |file|
|
||||||
|
begin
|
||||||
|
load file if file and File.exist?(file)
|
||||||
|
rescue LoadError
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Register signal handlers.
|
||||||
|
def signals
|
||||||
|
@stage = "registering signal handlers"
|
||||||
|
|
||||||
|
# Set up a handler for SIGINT that raises Interrupt on the main thread
|
||||||
|
Signal.trap("INT") do |sig|
|
||||||
|
raise Interrupt, "Thread has been interrupted"
|
||||||
|
end
|
||||||
|
|
||||||
|
["HUP", "QUIT", "TERM", "ALRM", "USR1", "USR2"].each do |signal|
|
||||||
|
Signal.trap(signal) do |sig|
|
||||||
|
raise SignalException, sig
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_syntax_error(e)
|
||||||
|
STDERR.puts "A syntax error has occurred:"
|
||||||
|
STDERR.puts " #{e.message}"
|
||||||
|
STDERR.puts "\nCode:\n#{e.code}"
|
||||||
|
if e.column
|
||||||
|
STDERR.puts((" " * (e.column - 1)) + "^")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_syntax_errors(syns)
|
||||||
|
syns.each do |e|
|
||||||
|
STDERR.puts "#{e.file}:#{e.line}: #{e.reason}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Checks if a subcommand with basename +base+ exists. Returns the full
|
||||||
|
# path to the subcommand if it does; otherwise, returns nil.
|
||||||
|
def find_subcommand(base)
|
||||||
|
command = File.join @lib_bin, "#{base}.rb"
|
||||||
|
return command if File.exist? command
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# Checks if a gem wrapper named +base+ exists. Returns the full path to
|
||||||
|
# the gem wrapper if it does; otherwise, returns nil.
|
||||||
|
def find_gem_wrapper(base)
|
||||||
|
@gem_bins.each do |dir|
|
||||||
|
wrapper = File.join dir, base
|
||||||
|
return wrapper if File.exist? wrapper
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# Detects if the Rubinius executable was aliased to a subcommand or a
|
||||||
|
# rubygems executable. If so, changes ARGV as if Rubinius were invoked
|
||||||
|
# to run the subcommand or rubygems executable.
|
||||||
|
def detect_alias
|
||||||
|
cmd = ARG0.split("/").last
|
||||||
|
|
||||||
|
# ignore the common ones straight away
|
||||||
|
return if cmd == "rbx" or cmd == "ruby"
|
||||||
|
|
||||||
|
# Check if we are aliased to a Rubinius subcommand.
|
||||||
|
if subcommand = find_subcommand(cmd)
|
||||||
|
ARGV.unshift subcommand
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check if we are aliased to a rubygems executable.
|
||||||
|
if gem_wrapper = find_gem_wrapper(cmd)
|
||||||
|
ARGV.unshift "-S", gem_wrapper
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_simple_options(argv)
|
||||||
|
argv.delete_if do |x|
|
||||||
|
if x[0] == ?-
|
||||||
|
if equal = x.index("=")
|
||||||
|
name = x.substring(1, equal-1)
|
||||||
|
|
||||||
|
equal += 1
|
||||||
|
val = x.substring(equal, x.size - equal)
|
||||||
|
else
|
||||||
|
name = x.substring(1, x.size - 1)
|
||||||
|
val = true
|
||||||
|
end
|
||||||
|
|
||||||
|
name.gsub! "-", "_"
|
||||||
|
|
||||||
|
Rubinius::Globals[:"$#{name}"] = val
|
||||||
|
|
||||||
|
true
|
||||||
|
else
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def define_global_methods
|
||||||
|
Kernel.module_eval do
|
||||||
|
def gsub(pattern, rep=nil, &block)
|
||||||
|
target = $_
|
||||||
|
raise TypeError, "$_ must be a String, but is #{target.inspect}" unless target.kind_of? String
|
||||||
|
$_ = target.gsub(pattern, rep, &block)
|
||||||
|
end
|
||||||
|
module_function :gsub
|
||||||
|
|
||||||
|
def sub(pattern, rep=nil, &block)
|
||||||
|
target = $_
|
||||||
|
raise TypeError, "$_ must be a String, but is #{target.inspect}" unless target.kind_of? String
|
||||||
|
$_ = target.sub(pattern, rep, &block)
|
||||||
|
end
|
||||||
|
module_function :sub
|
||||||
|
|
||||||
|
def chomp(string=$/)
|
||||||
|
raise TypeError, "$_ must be a String" unless $_.kind_of? String
|
||||||
|
$_ = $_.chomp(string)
|
||||||
|
end
|
||||||
|
module_function :chomp
|
||||||
|
|
||||||
|
def chop
|
||||||
|
raise TypeError, "$_ must be a String" unless $_.kind_of? String
|
||||||
|
$_ = $_.chop
|
||||||
|
end
|
||||||
|
module_function :chop
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Process all command line arguments.
|
||||||
|
def options(argv=ARGV)
|
||||||
|
@stage = "processing command line arguments"
|
||||||
|
|
||||||
|
options = Options.new "Usage: rbx [subcommand] [options] [--] [script] [arguments]", 25
|
||||||
|
|
||||||
|
options.left_align
|
||||||
|
options.on_extra do |x|
|
||||||
|
raise Options::ParseError, "Unrecognized option: #{x}" if x[0] == ?-
|
||||||
|
if @script.nil? and @evals.empty?
|
||||||
|
@script = x
|
||||||
|
else
|
||||||
|
argv.unshift x
|
||||||
|
end
|
||||||
|
options.stop_parsing
|
||||||
|
end
|
||||||
|
|
||||||
|
options.doc "Script is any valid Ruby source code file"
|
||||||
|
|
||||||
|
options.doc "\nRuby options"
|
||||||
|
options.on "-", "Read and evaluate code from STDIN" do
|
||||||
|
@repl = false
|
||||||
|
set_program_name "-"
|
||||||
|
stdin = STDIN.dup
|
||||||
|
script = STDIN.read
|
||||||
|
STDIN.reopen(stdin)
|
||||||
|
execute_script script
|
||||||
|
end
|
||||||
|
|
||||||
|
options.on "--", "Stop processing command line arguments" do
|
||||||
|
@early_option_stop = true
|
||||||
|
options.stop_parsing
|
||||||
|
end
|
||||||
|
|
||||||
|
options.on "-a", "Used with -n and -p, splits $_ into $F" do
|
||||||
|
@input_loop_split = true
|
||||||
|
Rubinius::Globals.set!(:$-a, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
options.on "-c", "Only check the syntax" do
|
||||||
|
@repl = false
|
||||||
|
@check_syntax = true
|
||||||
|
end
|
||||||
|
|
||||||
|
options.on "-C", "DIR", "Change directory to DIR before running scripts" do |dir|
|
||||||
|
@directory = dir
|
||||||
|
end
|
||||||
|
|
||||||
|
options.on "-d", "Enable debugging output and set $DEBUG to true" do
|
||||||
|
$VERBOSE = true
|
||||||
|
$DEBUG = true
|
||||||
|
end
|
||||||
|
|
||||||
|
options.on "--disable-gems", "Do not automatically load rubygems on startup" do
|
||||||
|
@enable_gems = false
|
||||||
|
end
|
||||||
|
|
||||||
|
options.on "-e", "CODE", "Compile and execute CODE" do |code|
|
||||||
|
@repl = false
|
||||||
|
set_program_name "(eval)"
|
||||||
|
@evals << code
|
||||||
|
end
|
||||||
|
|
||||||
|
options.on '-F', "PATTERN", "Set $; to PATTERN" do |pattern|
|
||||||
|
Rubinius::Globals.set!(:$;, Regexp.new(pattern))
|
||||||
|
end
|
||||||
|
|
||||||
|
options.on '-G', '--gemfile', 'Respect a Gemfile in the current path' do
|
||||||
|
@load_gemfile = true
|
||||||
|
end
|
||||||
|
|
||||||
|
options.on "-h", "--help", "Display this help" do
|
||||||
|
@repl = false
|
||||||
|
puts options
|
||||||
|
done
|
||||||
|
end
|
||||||
|
|
||||||
|
options.on "-i", "[EXT]", "Edit ARGV files in place, making backup with EXT" do |ext|
|
||||||
|
# in place edit mode
|
||||||
|
ext ||= ''
|
||||||
|
$-i = ext
|
||||||
|
end
|
||||||
|
|
||||||
|
options.on "-I", "DIR1[:DIR2]", "Add directories to $LOAD_PATH" do |dir|
|
||||||
|
@load_paths << dir
|
||||||
|
end
|
||||||
|
|
||||||
|
options.on "-K", "Ignored $KCODE option for compatibility" do
|
||||||
|
msg = "The -K option is deprecated. Rubinius internal encoding is always UTF-8"
|
||||||
|
STDERR.puts msg
|
||||||
|
Rubinius::Logger.system.warn msg
|
||||||
|
end
|
||||||
|
|
||||||
|
options.on "-U", "Set Encoding.default_internal to UTF-8" do
|
||||||
|
msg = "The -U option is deprecated. Rubinius internal encoding is always UTF-8"
|
||||||
|
STDERR.puts msg
|
||||||
|
Rubinius::Logger.system.warn msg
|
||||||
|
end
|
||||||
|
|
||||||
|
options.on "-E", "ENC", "Set external:internal character encoding to ENC" do |enc|
|
||||||
|
external, internal = enc.split(":")
|
||||||
|
Encoding.default_external = external if external and !external.empty?
|
||||||
|
|
||||||
|
if internal and !internal.empty?
|
||||||
|
msg = "The -E option setting internal encoding is deprecated. Rubinius internal encoding is always UTF-8"
|
||||||
|
STDERR.puts msg
|
||||||
|
Rubinius::Logger.system.warn msg
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
options.on "--main", "PATH", "Load PATH directly from CodeDB" do |path|
|
||||||
|
code = Rubinius::CoreDB.load_feature path, "", false, false
|
||||||
|
if code
|
||||||
|
code.create_script false
|
||||||
|
Rubinius.run_script code
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
puts "file not found: #{path}"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
options.on "-n", "Wrap running code in 'while(gets()) ...'" do
|
||||||
|
@input_loop = true
|
||||||
|
define_global_methods
|
||||||
|
end
|
||||||
|
|
||||||
|
options.on "-p", "Same as -n, but also print $_" do
|
||||||
|
@input_loop = true
|
||||||
|
@input_loop_print = true
|
||||||
|
Rubinius::Globals.set!(:$-p, true)
|
||||||
|
define_global_methods
|
||||||
|
end
|
||||||
|
|
||||||
|
options.on "-r", "LIBRARY", "Require library before execution" do |file|
|
||||||
|
@requires << file
|
||||||
|
end
|
||||||
|
|
||||||
|
options.on("-s", "Process options after the script into globals") do
|
||||||
|
@simple_options = true
|
||||||
|
end
|
||||||
|
|
||||||
|
options.on("-S", "SCRIPT",
|
||||||
|
"Run SCRIPT using PATH environment variable to find it") do |script|
|
||||||
|
options.stop_parsing
|
||||||
|
@repl = false
|
||||||
|
|
||||||
|
# Load a gemfile now if we need to so that -S can see binstubs
|
||||||
|
# internal to the Gemfile
|
||||||
|
gemfile
|
||||||
|
|
||||||
|
# First, check if any existing gem wrappers match.
|
||||||
|
unless file = find_gem_wrapper(script)
|
||||||
|
# Then, check if any Rubinius subcommands match.
|
||||||
|
unless file = find_subcommand(script)
|
||||||
|
# Finally, check if any file on PATH matches.
|
||||||
|
search = ENV['PATH'].split(File::PATH_SEPARATOR)
|
||||||
|
search.each do |d|
|
||||||
|
path = File.join d, script
|
||||||
|
if File.exist? path
|
||||||
|
file = path
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
set_program_name script if file
|
||||||
|
|
||||||
|
# if missing, let it die a natural death
|
||||||
|
@script = file ? file : script
|
||||||
|
end
|
||||||
|
|
||||||
|
options.on "-v", "Display the version and set $VERBOSE to true" do
|
||||||
|
@repl = false
|
||||||
|
$VERBOSE = true
|
||||||
|
|
||||||
|
unless @printed_version
|
||||||
|
puts Rubinius.version
|
||||||
|
@printed_version = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
options.on "-w", "Enable warnings" do
|
||||||
|
$VERBOSE = true
|
||||||
|
end
|
||||||
|
|
||||||
|
options.on("-W", "[level]",
|
||||||
|
"Set warning level: 0=silence, 1=medium, 2=verbose (default)") do |l|
|
||||||
|
case l
|
||||||
|
when "0"
|
||||||
|
$VERBOSE = nil
|
||||||
|
when "1"
|
||||||
|
$VERBOSE = false
|
||||||
|
when nil
|
||||||
|
$VERBOSE = true
|
||||||
|
else
|
||||||
|
# MRI -h says -W2 sets $VERBOSE to true, but behaviorally
|
||||||
|
# any value >= 2 sets $VERBOSE to true.
|
||||||
|
$VERBOSE = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
options.on "--show-nil-location", "Show where nil value originated when NoMethodError on nil is raised" do
|
||||||
|
$rbx_show_nil_location = true
|
||||||
|
end
|
||||||
|
|
||||||
|
options.on "--deprecated", "Display any deprecations" do
|
||||||
|
@deprecations = true
|
||||||
|
end
|
||||||
|
|
||||||
|
options.on "--version", "Display the version" do
|
||||||
|
@repl = false
|
||||||
|
puts Rubinius.version
|
||||||
|
end
|
||||||
|
|
||||||
|
options.doc <<-DOC
|
||||||
|
\nRubinius subcommands
|
||||||
|
|
||||||
|
Rubinius provides subcommands that implement various facilities. Each
|
||||||
|
subcommand provides its own help. For example:
|
||||||
|
|
||||||
|
rbx compile -h
|
||||||
|
|
||||||
|
Available subcommands:
|
||||||
|
|
||||||
|
compile Run the bytecode compiler
|
||||||
|
gem Run RubyGems 'gem' command
|
||||||
|
irb Run IRB
|
||||||
|
erb Run ERb
|
||||||
|
DOC
|
||||||
|
|
||||||
|
options.doc <<-DOC
|
||||||
|
VM Options
|
||||||
|
-X<variable>[=<value>]
|
||||||
|
This option is recognized by the VM before any ruby code is loaded.
|
||||||
|
It is used to set VM configuration options.
|
||||||
|
|
||||||
|
Use -Xhelp to see the list of options the VM recognizes.
|
||||||
|
All variables, even ones that the VM doesn't understand, are available
|
||||||
|
in Rubinius::Config.
|
||||||
|
|
||||||
|
Options are parsed from these sources, in this order:
|
||||||
|
|
||||||
|
1. The file .rbxrc in the current working directory.
|
||||||
|
2. The RBXOPT environment variable.
|
||||||
|
3. The command line options.
|
||||||
|
|
||||||
|
This order permits environment and command line options to override
|
||||||
|
"application" configuration. Likewise, command line options can override
|
||||||
|
environment configuration.
|
||||||
|
|
||||||
|
A number of Rubinius features are driven by setting these variables.
|
||||||
|
DOC
|
||||||
|
|
||||||
|
options.parse ARGV
|
||||||
|
|
||||||
|
handle_rubyopt(options)
|
||||||
|
|
||||||
|
if @early_option_stop and @simple_options
|
||||||
|
handle_simple_options(argv)
|
||||||
|
end
|
||||||
|
|
||||||
|
if Rubinius::Config['help']
|
||||||
|
STDOUT.puts "Rubinius configuration variables:"
|
||||||
|
STDOUT.puts " These variables are normally set via -X and read via Rubinius::Config[var]."
|
||||||
|
STDOUT.puts
|
||||||
|
|
||||||
|
require 'rubinius/configuration'
|
||||||
|
Rubinius::ConfigurationVariables.instance.show_help(STDOUT)
|
||||||
|
exit 0
|
||||||
|
end
|
||||||
|
|
||||||
|
exit 0 if Rubinius::Config["config.print"]
|
||||||
|
|
||||||
|
if str = Rubinius::Config['tool.require']
|
||||||
|
begin
|
||||||
|
require str
|
||||||
|
rescue LoadError
|
||||||
|
STDERR.puts "Unable to require file for tool: '#{str}'"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if Rubinius::Config['profile'] || Rubinius::Config['jit.profile']
|
||||||
|
@load_profiler = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if @check_syntax
|
||||||
|
check_syntax
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sets $0 ($PROGRAM_NAME) without changing the process title
|
||||||
|
def set_program_name(name)
|
||||||
|
Rubinius::Globals.set! :$0, name
|
||||||
|
end
|
||||||
|
private :set_program_name
|
||||||
|
|
||||||
|
def handle_rubyopt(options)
|
||||||
|
if env_opts = ENV['RUBYOPT']
|
||||||
|
Rubinius::Logger.system.write "RUBYOPT: #{env_opts}"
|
||||||
|
|
||||||
|
options.start_parsing
|
||||||
|
env_opts = env_opts.strip.split(/\s+/)
|
||||||
|
|
||||||
|
until env_opts.empty?
|
||||||
|
entry = env_opts.shift
|
||||||
|
|
||||||
|
unless entry[0] == ?-
|
||||||
|
entry = "-#{entry}"
|
||||||
|
end
|
||||||
|
|
||||||
|
opt, arg, rest = options.split entry, 2
|
||||||
|
|
||||||
|
options.process env_opts, entry, opt, arg
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
# Update the load paths with any -I arguments.
|
||||||
|
def load_paths
|
||||||
|
@stage = "setting load paths"
|
||||||
|
|
||||||
|
@load_paths.each do |path|
|
||||||
|
path.split(":").reverse_each do |path|
|
||||||
|
# We used to run expand_path on path first, but MRI
|
||||||
|
# doesn't and it breaks some code if we do.
|
||||||
|
$LOAD_PATH.unshift(path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def deprecations
|
||||||
|
@stage = "showing deprecations"
|
||||||
|
|
||||||
|
return unless @deprecations
|
||||||
|
|
||||||
|
if Rubinius::DEPRECATIONS.size > 0
|
||||||
|
Rubinius::DEPRECATIONS.each do |desc, alt|
|
||||||
|
STDERR.puts <<-EOM
|
||||||
|
Deprecation: #{desc}
|
||||||
|
Alternative: #{alt}
|
||||||
|
|
||||||
|
EOM
|
||||||
|
end
|
||||||
|
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
STDERR.puts "Rubinius #{Rubinius::VERSION} has no deprecation notices"
|
||||||
|
exit 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_compiler
|
||||||
|
@stage = "loading the compiler"
|
||||||
|
|
||||||
|
begin
|
||||||
|
CodeLoader.load_compiler
|
||||||
|
rescue LoadError => e
|
||||||
|
message = "Unable to load the bytecode compiler."
|
||||||
|
Rubinius::Logger.system.error message
|
||||||
|
Rubinius::Logger.system.error(
|
||||||
|
"Please run 'rake' or 'rake install' to rebuild the compiler.")
|
||||||
|
Rubinius::Logger.log_exception message, e
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def debugger
|
||||||
|
@stage = "running the debugger"
|
||||||
|
|
||||||
|
if Rubinius::Config['debug']
|
||||||
|
if custom = Loader.debugger
|
||||||
|
custom.call
|
||||||
|
else
|
||||||
|
require 'rubinius/debugger'
|
||||||
|
Rubinius::Debugger.start
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def rubygems
|
||||||
|
@stage = "loading Rubygems"
|
||||||
|
|
||||||
|
CodeLoader.load_rubygems if @enable_gems
|
||||||
|
end
|
||||||
|
|
||||||
|
def gemfile
|
||||||
|
@stage = "loading Gemfile"
|
||||||
|
|
||||||
|
if @load_gemfile
|
||||||
|
CodeLoader.load_rubygems
|
||||||
|
require 'bundler/setup'
|
||||||
|
@load_gemfile = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def profiler
|
||||||
|
return unless @load_profiler
|
||||||
|
@stage = "loading profiler"
|
||||||
|
|
||||||
|
require 'profile'
|
||||||
|
end
|
||||||
|
|
||||||
|
# Require any -r arguments
|
||||||
|
def requires
|
||||||
|
@stage = "requiring command line files"
|
||||||
|
|
||||||
|
@requires.each { |file| require file }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Evaluate any -e arguments
|
||||||
|
def evals
|
||||||
|
return if @evals.empty?
|
||||||
|
|
||||||
|
@repl = false
|
||||||
|
@stage = "evaluating command line code"
|
||||||
|
|
||||||
|
Dir.chdir(@directory) if @directory
|
||||||
|
|
||||||
|
if @input_loop
|
||||||
|
while gets
|
||||||
|
$F = $_.split if @input_loop_split
|
||||||
|
eval @evals.join("\n"), TOPLEVEL_BINDING, "-e", 1
|
||||||
|
print $_ if @input_loop_print
|
||||||
|
end
|
||||||
|
else
|
||||||
|
eval @evals.join("\n"), TOPLEVEL_BINDING, "-e", 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run the script passed on the command line
|
||||||
|
def script
|
||||||
|
return unless @script and @evals.empty?
|
||||||
|
|
||||||
|
@repl = false
|
||||||
|
|
||||||
|
handle_simple_options(ARGV) if @simple_options
|
||||||
|
|
||||||
|
@stage = "running #{@script}"
|
||||||
|
Dir.chdir @directory if @directory
|
||||||
|
|
||||||
|
if File.exist? @script
|
||||||
|
if IO.read(@script, 6) == "!RBIX\n"
|
||||||
|
raise LoadError, "'#{@script}' is not a Ruby source file"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if @script.suffix?(".rb")
|
||||||
|
raise LoadError, "unable to find '#{@script}'"
|
||||||
|
else
|
||||||
|
command = File.join @lib_bin, "#{@script}.rb"
|
||||||
|
unless File.exist? command
|
||||||
|
raise LoadError, "unable to find Rubinius command '#{@script}'"
|
||||||
|
else
|
||||||
|
@script = command
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
set_program_name @script
|
||||||
|
CodeLoader.new(@script).load_script
|
||||||
|
end
|
||||||
|
|
||||||
|
#Check Ruby syntax of source
|
||||||
|
def check_syntax
|
||||||
|
parser_class = Rubinius::ToolSets::Runtime::Melbourne
|
||||||
|
|
||||||
|
if @script
|
||||||
|
if File.exist?(@script)
|
||||||
|
parser = parser_class.new @script, 1, []
|
||||||
|
parser.parse_file
|
||||||
|
else
|
||||||
|
puts "rbx: Unable to find file -- #{@script} (LoadError)"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
elsif not @evals.empty?
|
||||||
|
parser = parser_class.new('-e', 1, [])
|
||||||
|
parser.parse_string(@evals.join("\n"))
|
||||||
|
else
|
||||||
|
parser = parser_class.new('-', 1, [])
|
||||||
|
parser.parse_string(STDIN.read)
|
||||||
|
end
|
||||||
|
|
||||||
|
puts "Syntax OK"
|
||||||
|
exit 0
|
||||||
|
rescue SyntaxError => e
|
||||||
|
show_syntax_errors(parser.syntax_errors)
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run IRB unless we were passed -e, -S arguments or a script to run.
|
||||||
|
def repl
|
||||||
|
return unless @repl
|
||||||
|
|
||||||
|
@stage = "running IRB"
|
||||||
|
|
||||||
|
# Check if a different REPL is set.
|
||||||
|
repl = ENV["RBX_REPL"] || "irb"
|
||||||
|
|
||||||
|
if Terminal
|
||||||
|
load File.join(Rubinius::GEMS_PATH, "bin", repl)
|
||||||
|
else
|
||||||
|
set_program_name "(eval)"
|
||||||
|
execute_script "p #{STDIN.read}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute_script(script)
|
||||||
|
eval script, TOPLEVEL_BINDING
|
||||||
|
end
|
||||||
|
|
||||||
|
def run_at_exits
|
||||||
|
until AtExit.empty?
|
||||||
|
begin
|
||||||
|
AtExit.shift.call
|
||||||
|
rescue SystemExit => e
|
||||||
|
@exit_code = e.status
|
||||||
|
# We recurse down so that future at_exits
|
||||||
|
# see the current exception
|
||||||
|
run_at_exits
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def flush_stdio
|
||||||
|
STDOUT.flush unless STDOUT.closed?
|
||||||
|
STDERR.flush unless STDERR.closed?
|
||||||
|
end
|
||||||
|
|
||||||
|
def exit_with_exception(e)
|
||||||
|
@exit_code = 1
|
||||||
|
Rubinius::Logger.log_exception "An exception occurred #{@stage}", e
|
||||||
|
end
|
||||||
|
|
||||||
|
# Cleanup and at_exit processing.
|
||||||
|
def epilogue
|
||||||
|
@stage = "running at_exit handlers"
|
||||||
|
|
||||||
|
begin
|
||||||
|
Signal.run_handler Signal::Names["EXIT"]
|
||||||
|
rescue SystemExit => e
|
||||||
|
@exit_code = e.status
|
||||||
|
end
|
||||||
|
|
||||||
|
run_at_exits
|
||||||
|
|
||||||
|
flush_stdio
|
||||||
|
|
||||||
|
rescue Object => e
|
||||||
|
exit_with_exception e
|
||||||
|
end
|
||||||
|
|
||||||
|
# Exit.
|
||||||
|
def done
|
||||||
|
# check that this is a valid exit rather than failing to process
|
||||||
|
# unwinding properly.
|
||||||
|
#
|
||||||
|
# TODO: this code is pretty gross, nice object inspectors, please.
|
||||||
|
thread_state = Rubinius.thread_state
|
||||||
|
reason = thread_state[0]
|
||||||
|
unless reason == :none
|
||||||
|
STDERR.puts "\nERROR: the VM is exiting improperly #{@stage}"
|
||||||
|
STDERR.puts "intended operation: #{reason.inspect}"
|
||||||
|
STDERR.puts "associated value: #{thread_state[1].inspect}"
|
||||||
|
|
||||||
|
destination = thread_state[2]
|
||||||
|
|
||||||
|
if destination
|
||||||
|
method = destination.method
|
||||||
|
|
||||||
|
STDERR.puts "destination scope:"
|
||||||
|
STDERR.puts " method: #{method.name} at #{method.file}:#{method.first_line}"
|
||||||
|
STDERR.puts " module: #{destination.module.name}"
|
||||||
|
STDERR.puts " block: #{destination.block}" if destination.block
|
||||||
|
else
|
||||||
|
STDERR.puts "destination scope: unknown"
|
||||||
|
end
|
||||||
|
|
||||||
|
if reason == :catch_throw
|
||||||
|
STDERR.puts "throw destination: #{thread_state[4].inspect}"
|
||||||
|
end
|
||||||
|
|
||||||
|
if exception = thread_state[3]
|
||||||
|
begin
|
||||||
|
exception.render
|
||||||
|
rescue Exception => e
|
||||||
|
STDERR.puts "Unable to render backtrace: #{e.message} (#{e.class})"
|
||||||
|
STDERR.puts "Raw backtrace data:"
|
||||||
|
|
||||||
|
exception.locations.each do |loc|
|
||||||
|
p [loc.file, loc.line]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@exit_code = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
Process.exit! @exit_code
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_exception(e)
|
||||||
|
case e
|
||||||
|
when SystemExit
|
||||||
|
@exit_code = e.status
|
||||||
|
when SyntaxError
|
||||||
|
@exit_code = 1
|
||||||
|
|
||||||
|
STDERR.puts e.awesome_backtrace.show
|
||||||
|
STDERR.puts
|
||||||
|
|
||||||
|
show_syntax_error(e)
|
||||||
|
when Interrupt
|
||||||
|
exit_with_exception e
|
||||||
|
when SignalException
|
||||||
|
Signal.trap(e.signo, "SIG_DFL")
|
||||||
|
Process.kill e.signo, Process.pid
|
||||||
|
when nil
|
||||||
|
# what?
|
||||||
|
else
|
||||||
|
exit_with_exception e
|
||||||
|
end
|
||||||
|
rescue Object => e
|
||||||
|
exit_with_exception e
|
||||||
|
ensure
|
||||||
|
epilogue
|
||||||
|
end
|
||||||
|
|
||||||
|
# Orchestrate everything.
|
||||||
|
def main
|
||||||
|
preamble
|
||||||
|
system_load_path
|
||||||
|
signals
|
||||||
|
load_compiler
|
||||||
|
preload
|
||||||
|
detect_alias
|
||||||
|
options
|
||||||
|
load_paths
|
||||||
|
deprecations
|
||||||
|
rubygems
|
||||||
|
gemfile
|
||||||
|
debugger
|
||||||
|
profiler
|
||||||
|
requires
|
||||||
|
evals
|
||||||
|
script
|
||||||
|
repl
|
||||||
|
|
||||||
|
rescue Object => e
|
||||||
|
handle_exception e
|
||||||
|
else
|
||||||
|
# We do this, run epilogue both in the rescue blocks and also here,
|
||||||
|
# so that at_exit{} hooks can read $!.
|
||||||
|
epilogue
|
||||||
|
ensure
|
||||||
|
done
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
167
gxg/core/location.rb
Normal file
167
gxg/core/location.rb
Normal file
|
|
@ -0,0 +1,167 @@
|
||||||
|
module Rubinius
|
||||||
|
class Location
|
||||||
|
|
||||||
|
attr_reader :method
|
||||||
|
attr_reader :name
|
||||||
|
|
||||||
|
attr_reader :ip
|
||||||
|
attr_reader :variables
|
||||||
|
attr_reader :lexical_scope
|
||||||
|
|
||||||
|
def self.of_closest_ruby_method
|
||||||
|
Rubinius.primitive :location_of_closest_ruby_method
|
||||||
|
raise PrimitiveFailure, "Location.of_closest_ruby_method primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_block
|
||||||
|
@flags & 1 == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_jit
|
||||||
|
@flags & 2 == 2
|
||||||
|
end
|
||||||
|
|
||||||
|
def ip_on_current?
|
||||||
|
@flags & 4 == 4
|
||||||
|
end
|
||||||
|
|
||||||
|
def inlined?
|
||||||
|
@name.nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
def describe_receiver
|
||||||
|
# We can't call @receiver.class because it might be overriden (or in
|
||||||
|
# the case of Mocha, mocked out) and easily causes stack overflows
|
||||||
|
klass = Rubinius::Type.object_class @receiver
|
||||||
|
|
||||||
|
if @method_module.equal?(Kernel)
|
||||||
|
"Kernel."
|
||||||
|
elsif Rubinius::Type.object_kind_of?(@method_module, Class) and
|
||||||
|
ao = Rubinius::Type.singleton_class_object(@method_module)
|
||||||
|
"#{ao}."
|
||||||
|
elsif @method_module and @method_module != klass
|
||||||
|
"#{@method_module}(#{klass})#"
|
||||||
|
else
|
||||||
|
"#{klass}#"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def describe
|
||||||
|
if is_block
|
||||||
|
"{ } in #{describe_receiver}#{describe_method}"
|
||||||
|
else
|
||||||
|
"#{describe_receiver}#{describe_method}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
"#<Rubinius::Location #{describe}>"
|
||||||
|
end
|
||||||
|
|
||||||
|
def describe_method
|
||||||
|
if is_block or @name == @method.name
|
||||||
|
@name.to_s
|
||||||
|
elsif !@name # inlined methods have no name
|
||||||
|
@method.name.to_s
|
||||||
|
else
|
||||||
|
"#{@name} (#{@method.name})"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Current line being executed by the VM.
|
||||||
|
def line
|
||||||
|
return 0 unless @method
|
||||||
|
|
||||||
|
ip = @ip
|
||||||
|
ip -= 1 unless ip_on_current?
|
||||||
|
|
||||||
|
return @method.first_line unless ip > 0
|
||||||
|
|
||||||
|
@method.line_from_ip(ip)
|
||||||
|
end
|
||||||
|
|
||||||
|
def file(relative_to=nil)
|
||||||
|
path = @method.active_path
|
||||||
|
|
||||||
|
if relative_to
|
||||||
|
# Be sure we can bail out if something doesn't work and still
|
||||||
|
# show something.
|
||||||
|
begin
|
||||||
|
full = File.expand_path path
|
||||||
|
if full.prefix? relative_to
|
||||||
|
return full[relative_to.size+1..-1]
|
||||||
|
else
|
||||||
|
return path
|
||||||
|
end
|
||||||
|
rescue StandardError
|
||||||
|
return path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
path
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Place in the source that this method was created at.
|
||||||
|
def position(relative_to=nil)
|
||||||
|
l = line()
|
||||||
|
|
||||||
|
if l == 0
|
||||||
|
if @ip < 0
|
||||||
|
file(relative_to)
|
||||||
|
else
|
||||||
|
"#{file(relative_to)}+#{@ip}"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
"#{file(relative_to)}:#{l}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# See Rubinius::Backtrace.backtrace
|
||||||
|
class Missing
|
||||||
|
def describe
|
||||||
|
"*** Missing backtrace! Did the VM not create one? ***"
|
||||||
|
end
|
||||||
|
|
||||||
|
def describe_receiver
|
||||||
|
"missing_backtrace_unknown_receiver"
|
||||||
|
end
|
||||||
|
|
||||||
|
def describe_method
|
||||||
|
"missing_backtrace_unknown_location"
|
||||||
|
end
|
||||||
|
|
||||||
|
def method
|
||||||
|
"missing_backtrace_unknown_location"
|
||||||
|
end
|
||||||
|
|
||||||
|
def line
|
||||||
|
"<no line>"
|
||||||
|
end
|
||||||
|
|
||||||
|
def position(dir=nil)
|
||||||
|
"<no file>"
|
||||||
|
end
|
||||||
|
|
||||||
|
def ip
|
||||||
|
0
|
||||||
|
end
|
||||||
|
|
||||||
|
def name
|
||||||
|
"missing_backtrace_unknown_name"
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_block
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_jit
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def inlined?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
99
gxg/core/logger.rb
Normal file
99
gxg/core/logger.rb
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
module Rubinius
|
||||||
|
class Logger
|
||||||
|
attr_accessor :name
|
||||||
|
attr_accessor :format
|
||||||
|
|
||||||
|
def self.allocate
|
||||||
|
Rubinius.primitive :logger_allocate
|
||||||
|
raise PrimitiveFailure, "Rubinius::Logger.allocate failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.system
|
||||||
|
@system_logger ||= new "system"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.log_exception(message, e)
|
||||||
|
log = system
|
||||||
|
|
||||||
|
exception = e
|
||||||
|
exceptions = []
|
||||||
|
|
||||||
|
while exception
|
||||||
|
exceptions.unshift exception
|
||||||
|
exception = exception.cause
|
||||||
|
end
|
||||||
|
|
||||||
|
color = STDERR.tty?
|
||||||
|
|
||||||
|
while exception = exceptions.shift
|
||||||
|
if custom_trace = exception.custom_backtrace
|
||||||
|
msg = "User-defined backtrace:"
|
||||||
|
log.error msg
|
||||||
|
STDERR.puts msg
|
||||||
|
|
||||||
|
custom_trace.reverse_each do |line|
|
||||||
|
log.error line
|
||||||
|
STDERR.puts line
|
||||||
|
end
|
||||||
|
|
||||||
|
msg = "Backtrace: "
|
||||||
|
log.error msg
|
||||||
|
STDERR.puts "\n", msg, "\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
exception.backtrace.reverse_each { |line| log.error line }
|
||||||
|
STDERR.puts exception.awesome_backtrace.show("\n", color)
|
||||||
|
|
||||||
|
msg = "#{exception.message} (#{exception.class})"
|
||||||
|
log.error msg
|
||||||
|
STDERR.puts "\n", msg
|
||||||
|
|
||||||
|
unless exceptions.empty?
|
||||||
|
msg = "Causing:"
|
||||||
|
log.error msg
|
||||||
|
STDERR.puts "\n", msg, "\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
log.error message
|
||||||
|
STDERR.puts "\n", message
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(name)
|
||||||
|
@name = name
|
||||||
|
@format = "#{name}: %s"
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def write(message)
|
||||||
|
Rubinius.primitive :logger_write
|
||||||
|
raise PrimitiveFailure, "Rubinius::Logger#write primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def fatal(message)
|
||||||
|
Rubinius.primitive :logger_fatal
|
||||||
|
raise PrimitiveFailure, "Rubinius::Logger#fatal primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def error(message)
|
||||||
|
Rubinius.primitive :logger_error
|
||||||
|
raise PrimitiveFailure, "Rubinius::Logger#error primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def warn(message)
|
||||||
|
Rubinius.primitive :logger_warn
|
||||||
|
raise PrimitiveFailure, "Rubinius::Logger#warn primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def info(message)
|
||||||
|
Rubinius.primitive :logger_info
|
||||||
|
raise PrimitiveFailure, "Rubinius::Logger#info primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def debug(message)
|
||||||
|
Rubinius.primitive :logger_debug
|
||||||
|
raise PrimitiveFailure, "Rubinius::Logger#debug primitive failed"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
134
gxg/core/lookup_table.rb
Normal file
134
gxg/core/lookup_table.rb
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
##
|
||||||
|
# A LookupTable is similar to a Hash in that keys are used to set and
|
||||||
|
# reference values. However, unlike Hash, whether a key matches an
|
||||||
|
# entry in LookupTable is determined by using the == comparison operator
|
||||||
|
# in C code. In effect, two keys are equal if they are the same pointer.
|
||||||
|
#
|
||||||
|
# NOTE: the value used to determine the bin is the "pointer" value of
|
||||||
|
# key >> 2. This makes it possible to calculate the bin from Ruby with
|
||||||
|
# the same result as in C. For example:
|
||||||
|
#
|
||||||
|
# l = LookupTable.new
|
||||||
|
# loc = :a.hash & (l.bins - 1) # => 12
|
||||||
|
# class LookupTable
|
||||||
|
# def show
|
||||||
|
# @values
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# l[:a] = 1
|
||||||
|
# l.show # => #<Tuple: nil, ..., nil, #<Bucket: @key=>:a, @value=>1,
|
||||||
|
# @next=>nil>, nil, nil, nil>
|
||||||
|
#
|
||||||
|
# where ... is ten "nil, " entries.
|
||||||
|
#
|
||||||
|
# LookupTable is intended to be used with Symbol or Fixnum keys because
|
||||||
|
# it is a strict identity map. Usage of Strings as keys will likely result
|
||||||
|
# in strange behavior.
|
||||||
|
# LookupTable is NOT intended to be used generally like Hash.
|
||||||
|
|
||||||
|
module Rubinius
|
||||||
|
class LookupTable
|
||||||
|
include Enumerable
|
||||||
|
|
||||||
|
class Bucket
|
||||||
|
attr_reader :key
|
||||||
|
attr_reader :value
|
||||||
|
attr_reader :next
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :values
|
||||||
|
attr_reader :bins
|
||||||
|
|
||||||
|
attr_reader_specific :entries, :size
|
||||||
|
alias_method :length, :size
|
||||||
|
|
||||||
|
def self.allocate
|
||||||
|
Rubinius.primitive :lookuptable_allocate
|
||||||
|
raise PrimitiveFailure, "LookupTable.allocate primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(hash=nil)
|
||||||
|
return unless hash
|
||||||
|
hash.each do |k, v|
|
||||||
|
self[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def duplicate
|
||||||
|
Rubinius.primitive :lookuptable_duplicate
|
||||||
|
raise PrimitiveFailure, "LookupTable#duplicate primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def dup
|
||||||
|
copy = duplicate
|
||||||
|
Rubinius.privately do
|
||||||
|
copy.initialize_copy self
|
||||||
|
end
|
||||||
|
copy
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :clone, :dup
|
||||||
|
|
||||||
|
def fetch(key, return_on_failure)
|
||||||
|
Rubinius.primitive :lookuptable_fetch
|
||||||
|
raise PrimitiveFailure, "LookupTable#fetch primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def key?(key)
|
||||||
|
Rubinius.primitive :lookuptable_has_key
|
||||||
|
raise PrimitiveFailure, "LookupTable#key? primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :has_key?, :key?
|
||||||
|
alias_method :include?, :key?
|
||||||
|
alias_method :member?, :key?
|
||||||
|
|
||||||
|
def delete(key)
|
||||||
|
Rubinius.primitive :lookuptable_delete
|
||||||
|
raise PrimitiveFailure, "LookupTable#delete primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def keys
|
||||||
|
Rubinius.primitive :lookuptable_keys
|
||||||
|
raise PrimitiveFailure, "LookupTable#keys primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def values
|
||||||
|
Rubinius.primitive :lookuptable_values
|
||||||
|
raise PrimitiveFailure, "LookupTable#values primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def each
|
||||||
|
max = @bins
|
||||||
|
i = 0
|
||||||
|
vals = @values
|
||||||
|
|
||||||
|
while i < max
|
||||||
|
entry = vals.at(i)
|
||||||
|
|
||||||
|
while entry
|
||||||
|
yield entry.key, entry.value
|
||||||
|
entry = entry.next
|
||||||
|
end
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def empty?
|
||||||
|
@entries == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"#<#{self.class}:0x#{object_id.to_s(16)} #{entries} entries>"
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
ents = collect { |k, v| " #{k.inspect}=>#{v.inspect}" }.join(",")
|
||||||
|
"#<#{self.class}#{ents}>"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
147
gxg/core/lru_cache.rb
Normal file
147
gxg/core/lru_cache.rb
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
module Rubinius
|
||||||
|
class LRUCache
|
||||||
|
class Entry
|
||||||
|
attr_reader :hits
|
||||||
|
attr_reader :key
|
||||||
|
attr_accessor :value
|
||||||
|
attr_accessor :next_entry
|
||||||
|
attr_accessor :prev_entry
|
||||||
|
|
||||||
|
def initialize(key, value)
|
||||||
|
@key = key
|
||||||
|
@value = value
|
||||||
|
@hits = 0
|
||||||
|
@next_entry = nil
|
||||||
|
@prev_entry = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def insert_after(entry)
|
||||||
|
nxt = entry.next_entry
|
||||||
|
|
||||||
|
@prev_entry = entry
|
||||||
|
@next_entry = nxt
|
||||||
|
|
||||||
|
entry.next_entry = self
|
||||||
|
nxt.prev_entry = self if nxt
|
||||||
|
end
|
||||||
|
|
||||||
|
def insert_before(entry)
|
||||||
|
prev = entry.prev_entry
|
||||||
|
|
||||||
|
@prev_entry = prev
|
||||||
|
@next_entry = entry
|
||||||
|
|
||||||
|
entry.prev_entry = self
|
||||||
|
prev.next_entry = self if prev
|
||||||
|
end
|
||||||
|
|
||||||
|
def detach!
|
||||||
|
@next_entry.prev_entry = @prev_entry if @next_entry
|
||||||
|
@prev_entry.next_entry = @next_entry if @prev_entry
|
||||||
|
|
||||||
|
@next_entry = nil
|
||||||
|
@prev_entry = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def become_first!
|
||||||
|
@prev_entry = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def inc!
|
||||||
|
@hits += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(total)
|
||||||
|
@cache = {}
|
||||||
|
@total = total
|
||||||
|
@current = 0
|
||||||
|
|
||||||
|
@head = Entry.new(nil, nil)
|
||||||
|
@tail = Entry.new(nil, nil)
|
||||||
|
|
||||||
|
@tail.insert_after(@head)
|
||||||
|
|
||||||
|
@misses = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
attr_reader :current
|
||||||
|
attr_reader :misses
|
||||||
|
|
||||||
|
def clear!
|
||||||
|
Rubinius.synchronize(self) do
|
||||||
|
@cache = {}
|
||||||
|
@current = 0
|
||||||
|
|
||||||
|
@head = Entry.new(nil, nil, -1)
|
||||||
|
@tail = Entry.new(nil, nil, -2)
|
||||||
|
|
||||||
|
@tail.insert_after(@head)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def explain
|
||||||
|
entry = @head.next_entry
|
||||||
|
while entry != @tail
|
||||||
|
str, layout = entry.key
|
||||||
|
puts "hits: #{entry.hits}"
|
||||||
|
puts "layout: #{layout.inspect}"
|
||||||
|
puts "<STRING>"
|
||||||
|
puts str
|
||||||
|
puts "</STRING>"
|
||||||
|
|
||||||
|
entry = entry.next_entry
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def retrieve(key)
|
||||||
|
Rubinius.synchronize(self) do
|
||||||
|
if entry = @cache[key]
|
||||||
|
entry.inc!
|
||||||
|
|
||||||
|
entry.detach!
|
||||||
|
entry.insert_before @tail
|
||||||
|
|
||||||
|
return entry.value
|
||||||
|
end
|
||||||
|
|
||||||
|
@misses += 1
|
||||||
|
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def set(key, value)
|
||||||
|
Rubinius.synchronize(self) do
|
||||||
|
if entry = @cache[key]
|
||||||
|
entry.value = value
|
||||||
|
|
||||||
|
entry.detach!
|
||||||
|
entry.insert_before @tail
|
||||||
|
|
||||||
|
return value
|
||||||
|
end
|
||||||
|
|
||||||
|
if @current == @total
|
||||||
|
entry = @head.next_entry
|
||||||
|
|
||||||
|
entry.detach!
|
||||||
|
|
||||||
|
@cache.delete entry.key
|
||||||
|
else
|
||||||
|
@current += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
entry = Entry.new(key, value)
|
||||||
|
|
||||||
|
entry.insert_before @tail
|
||||||
|
|
||||||
|
@cache[key] = entry
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
35
gxg/core/main.rb
Normal file
35
gxg/core/main.rb
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
class << MAIN
|
||||||
|
def include(*mods)
|
||||||
|
Rubinius.privately do
|
||||||
|
Object.include(*mods)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def public(*methods)
|
||||||
|
Rubinius.privately do
|
||||||
|
Object.public(*methods)
|
||||||
|
end
|
||||||
|
|
||||||
|
Object
|
||||||
|
end
|
||||||
|
|
||||||
|
def private(*methods)
|
||||||
|
Rubinius.privately do
|
||||||
|
Object.private(*methods)
|
||||||
|
end
|
||||||
|
|
||||||
|
Object
|
||||||
|
end
|
||||||
|
|
||||||
|
def define_method(*args, &block)
|
||||||
|
Rubinius.privately do
|
||||||
|
Object.define_method(*args, &block)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"main"
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :inspect, :to_s
|
||||||
|
end
|
||||||
1227
gxg/core/marshal.rb
Normal file
1227
gxg/core/marshal.rb
Normal file
File diff suppressed because it is too large
Load diff
208
gxg/core/match_data.rb
Normal file
208
gxg/core/match_data.rb
Normal file
|
|
@ -0,0 +1,208 @@
|
||||||
|
class MatchData
|
||||||
|
def begin(idx)
|
||||||
|
if idx == 0
|
||||||
|
start = @full.at(0)
|
||||||
|
else
|
||||||
|
start = @region.at(idx - 1).at(0)
|
||||||
|
return nil if start == -1
|
||||||
|
end
|
||||||
|
m = Rubinius::Mirror.reflect @source
|
||||||
|
m.byte_to_character_index start
|
||||||
|
end
|
||||||
|
|
||||||
|
def end(idx)
|
||||||
|
if idx == 0
|
||||||
|
fin = @full.at(1)
|
||||||
|
else
|
||||||
|
fin = @region.at(idx - 1).at(1)
|
||||||
|
return nil if fin == -1
|
||||||
|
end
|
||||||
|
m = Rubinius::Mirror.reflect @source
|
||||||
|
m.byte_to_character_index fin
|
||||||
|
end
|
||||||
|
|
||||||
|
def offset(idx)
|
||||||
|
out = []
|
||||||
|
out << self.begin(idx)
|
||||||
|
out << self.end(idx)
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
|
def [](idx, len = nil)
|
||||||
|
return to_a[idx, len] if len
|
||||||
|
|
||||||
|
case idx
|
||||||
|
when Fixnum
|
||||||
|
if idx <= 0
|
||||||
|
return matched_area() if idx == 0
|
||||||
|
return to_a[idx]
|
||||||
|
elsif idx <= @region.size
|
||||||
|
tup = @region[idx - 1]
|
||||||
|
|
||||||
|
x = tup.at(0)
|
||||||
|
return nil if x == -1
|
||||||
|
|
||||||
|
y = tup.at(1)
|
||||||
|
return @source.byteslice(x, y-x)
|
||||||
|
end
|
||||||
|
when String
|
||||||
|
if @regexp.name_table
|
||||||
|
return self[idx.to_sym]
|
||||||
|
end
|
||||||
|
raise IndexError, "Unknown named group '#{idx}'"
|
||||||
|
when Symbol
|
||||||
|
if @regexp.name_table
|
||||||
|
if nums = @regexp.name_table[idx]
|
||||||
|
nums.reverse_each do |num|
|
||||||
|
val = self[num]
|
||||||
|
return val if val
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
raise IndexError, "Unknown named group '#{idx}'"
|
||||||
|
end
|
||||||
|
|
||||||
|
return to_a[idx]
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :regexp
|
||||||
|
|
||||||
|
def ==(other)
|
||||||
|
other.kind_of?(MatchData) &&
|
||||||
|
string == other.string &&
|
||||||
|
regexp == other.regexp &&
|
||||||
|
captures == other.captures
|
||||||
|
end
|
||||||
|
alias_method :eql?, :==
|
||||||
|
|
||||||
|
def string
|
||||||
|
@source.dup.freeze
|
||||||
|
end
|
||||||
|
|
||||||
|
def source
|
||||||
|
@source
|
||||||
|
end
|
||||||
|
|
||||||
|
def full
|
||||||
|
@full
|
||||||
|
end
|
||||||
|
|
||||||
|
def length
|
||||||
|
@region.fields + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def captures
|
||||||
|
out = Array.new(@region.fields)
|
||||||
|
|
||||||
|
idx = 0
|
||||||
|
@region.each do |tup|
|
||||||
|
x = tup.at(0)
|
||||||
|
|
||||||
|
if x == -1
|
||||||
|
val = nil
|
||||||
|
else
|
||||||
|
y = tup.at(1)
|
||||||
|
val = @source.byteslice(x, y-x)
|
||||||
|
end
|
||||||
|
|
||||||
|
out[idx] = val
|
||||||
|
idx += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
|
def names
|
||||||
|
@regexp.names
|
||||||
|
end
|
||||||
|
|
||||||
|
def pre_match
|
||||||
|
return @source.byteslice(0, 0) if @full.at(0) == 0
|
||||||
|
nd = @full.at(0) - 1
|
||||||
|
@source.byteslice(0, nd+1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def pre_match_from(idx)
|
||||||
|
return @source.byteslice(0, 0) if @full.at(0) == 0
|
||||||
|
nd = @full.at(0) - 1
|
||||||
|
@source.byteslice(idx, nd-idx+1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def collapsing?
|
||||||
|
@full[0] == @full[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
def post_match
|
||||||
|
nd = @source.bytesize - 1
|
||||||
|
st = @full.at(1)
|
||||||
|
@source.byteslice(st, nd-st+1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
capts = captures
|
||||||
|
if capts.empty?
|
||||||
|
"#<MatchData \"#{matched_area}\">"
|
||||||
|
else
|
||||||
|
idx = 0
|
||||||
|
capts.map! { |capture| "#{idx += 1}:#{capture.inspect}" }
|
||||||
|
"#<MatchData \"#{matched_area}\" #{capts.join(" ")}>"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def select
|
||||||
|
unless block_given?
|
||||||
|
raise LocalJumpError, "no block given"
|
||||||
|
end
|
||||||
|
|
||||||
|
out = []
|
||||||
|
ma = matched_area()
|
||||||
|
out << ma if yield ma
|
||||||
|
|
||||||
|
each_capture do |str|
|
||||||
|
if yield(str)
|
||||||
|
out << str
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :size, :length
|
||||||
|
|
||||||
|
def to_a
|
||||||
|
ary = captures()
|
||||||
|
ary.unshift matched_area()
|
||||||
|
return ary
|
||||||
|
end
|
||||||
|
|
||||||
|
def values_at(*indexes)
|
||||||
|
indexes.map { |i| self[i] }.flatten(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def matched_area
|
||||||
|
x = @full.at(0)
|
||||||
|
y = @full.at(1)
|
||||||
|
@source.byteslice(x, y-x)
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :to_s, :matched_area
|
||||||
|
private :matched_area
|
||||||
|
|
||||||
|
def get_capture(num)
|
||||||
|
x, y = @region[num]
|
||||||
|
return nil if !y or x == -1
|
||||||
|
|
||||||
|
return @source.byteslice(x, y-x)
|
||||||
|
end
|
||||||
|
|
||||||
|
private :get_capture
|
||||||
|
|
||||||
|
def each_capture
|
||||||
|
@region.each do |tup|
|
||||||
|
x, y = *tup
|
||||||
|
yield @source.byteslice(x, y-x)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private :each_capture
|
||||||
|
end
|
||||||
250
gxg/core/math.rb
Normal file
250
gxg/core/math.rb
Normal file
|
|
@ -0,0 +1,250 @@
|
||||||
|
module Math
|
||||||
|
def atan2(y, x)
|
||||||
|
y = Rubinius::Type.coerce_to_float y
|
||||||
|
x = Rubinius::Type.coerce_to_float x
|
||||||
|
FFI::Platform::Math.atan2 y, x
|
||||||
|
end
|
||||||
|
module_function :atan2
|
||||||
|
|
||||||
|
def cos(x)
|
||||||
|
FFI::Platform::Math.cos Rubinius::Type.coerce_to_float(x)
|
||||||
|
end
|
||||||
|
module_function :cos
|
||||||
|
|
||||||
|
def sin(x)
|
||||||
|
FFI::Platform::Math.sin Rubinius::Type.coerce_to_float(x)
|
||||||
|
end
|
||||||
|
module_function :sin
|
||||||
|
|
||||||
|
def tan(x)
|
||||||
|
FFI::Platform::Math.tan Rubinius::Type.coerce_to_float(x)
|
||||||
|
end
|
||||||
|
module_function :tan
|
||||||
|
|
||||||
|
def atan(x)
|
||||||
|
FFI::Platform::Math.atan Rubinius::Type.coerce_to_float(x)
|
||||||
|
end
|
||||||
|
module_function :atan
|
||||||
|
|
||||||
|
def cosh(x)
|
||||||
|
FFI::Platform::Math.cosh Rubinius::Type.coerce_to_float(x)
|
||||||
|
end
|
||||||
|
module_function :cosh
|
||||||
|
|
||||||
|
def sinh(x)
|
||||||
|
FFI::Platform::Math.sinh Rubinius::Type.coerce_to_float(x)
|
||||||
|
end
|
||||||
|
module_function :sinh
|
||||||
|
|
||||||
|
def tanh(x)
|
||||||
|
FFI::Platform::Math.tanh Rubinius::Type.coerce_to_float(x)
|
||||||
|
end
|
||||||
|
module_function :tanh
|
||||||
|
|
||||||
|
def asinh(x)
|
||||||
|
FFI::Platform::Math.asinh Rubinius::Type.coerce_to_float(x)
|
||||||
|
end
|
||||||
|
module_function :asinh
|
||||||
|
|
||||||
|
def atanh(x)
|
||||||
|
x = Rubinius::Type.coerce_to_float(x)
|
||||||
|
raise DomainError, 'atanh' unless x.abs <= 1.0
|
||||||
|
|
||||||
|
FFI::Platform::POSIX.errno = 0
|
||||||
|
|
||||||
|
ret = FFI::Platform::Math.atanh x
|
||||||
|
begin
|
||||||
|
Errno.handle
|
||||||
|
|
||||||
|
# Linux sets errno to ERANGE, but it should be EDOM
|
||||||
|
rescue Errno::ERANGE
|
||||||
|
raise Errno::EDOM
|
||||||
|
end
|
||||||
|
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
module_function :atanh
|
||||||
|
|
||||||
|
def exp(x)
|
||||||
|
FFI::Platform::Math.exp Rubinius::Type.coerce_to_float(x)
|
||||||
|
end
|
||||||
|
module_function :exp
|
||||||
|
|
||||||
|
def frexp(x)
|
||||||
|
x = Rubinius::Type.coerce_to_float(x)
|
||||||
|
FFI::MemoryPointer.new :int do |exp|
|
||||||
|
result = FFI::Platform::Math.frexp x, exp
|
||||||
|
[result, exp.read_int]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
module_function :frexp
|
||||||
|
|
||||||
|
def ldexp(x, n)
|
||||||
|
if n.kind_of? Float and n.nan?
|
||||||
|
raise RangeError, "NaN cannot be converted to an Integer"
|
||||||
|
end
|
||||||
|
|
||||||
|
n = Rubinius::Type.coerce_to(n, Integer, :to_int)
|
||||||
|
|
||||||
|
FFI::Platform::Math.ldexp Rubinius::Type.coerce_to_float(x), n
|
||||||
|
end
|
||||||
|
module_function :ldexp
|
||||||
|
|
||||||
|
# Rubinius-specific, used in Marshal
|
||||||
|
def modf(x)
|
||||||
|
FFI::MemoryPointer.new :double do |integral|
|
||||||
|
fractional = FFI::Platform::Math.modf x, integral
|
||||||
|
[fractional, integral.read_double]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
module_function :modf
|
||||||
|
|
||||||
|
def hypot(x, y)
|
||||||
|
x = Rubinius::Type.coerce_to_float x
|
||||||
|
y = Rubinius::Type.coerce_to_float y
|
||||||
|
FFI::Platform::Math.hypot x, y
|
||||||
|
end
|
||||||
|
module_function :hypot
|
||||||
|
|
||||||
|
def erf(x)
|
||||||
|
FFI::Platform::Math.erf Rubinius::Type.coerce_to_float(x)
|
||||||
|
end
|
||||||
|
module_function :erf
|
||||||
|
|
||||||
|
def erfc(x)
|
||||||
|
FFI::Platform::Math.erfc Rubinius::Type.coerce_to_float(x)
|
||||||
|
end
|
||||||
|
module_function :erfc
|
||||||
|
|
||||||
|
def asin(x)
|
||||||
|
return Float::NAN if x.kind_of? Float and x.nan?
|
||||||
|
|
||||||
|
x = Rubinius::Type.coerce_to_float(x)
|
||||||
|
raise DomainError, 'asin' unless x.abs <= 1.0
|
||||||
|
FFI::Platform::Math.asin x
|
||||||
|
end
|
||||||
|
module_function :asin
|
||||||
|
|
||||||
|
def acos(x)
|
||||||
|
return Float::NAN if x.kind_of? Float and x.nan?
|
||||||
|
|
||||||
|
x = Rubinius::Type.coerce_to_float(x)
|
||||||
|
raise DomainError, 'acos' unless x.abs <= 1.0
|
||||||
|
|
||||||
|
FFI::Platform::POSIX.errno = 0
|
||||||
|
|
||||||
|
ret = FFI::Platform::Math.acos x
|
||||||
|
Errno.handle
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
module_function :acos
|
||||||
|
|
||||||
|
def acosh(x)
|
||||||
|
return Float::NAN if x.kind_of? Float and x.nan?
|
||||||
|
|
||||||
|
x = Rubinius::Type.coerce_to_float(x)
|
||||||
|
raise DomainError, 'acosh' unless x >= 1.0
|
||||||
|
FFI::Platform::Math.acosh x
|
||||||
|
end
|
||||||
|
module_function :acosh
|
||||||
|
|
||||||
|
def cbrt(x)
|
||||||
|
x = Rubinius::Type.coerce_to_float x
|
||||||
|
FFI::Platform::Math.cbrt x
|
||||||
|
end
|
||||||
|
module_function :cbrt
|
||||||
|
|
||||||
|
def gamma(x)
|
||||||
|
x = Rubinius::Type.coerce_to_float x
|
||||||
|
|
||||||
|
# if x is negative zero, return -infinity
|
||||||
|
return -Float::INFINITY if (1 / x) == -Float::INFINITY
|
||||||
|
return Float::INFINITY if x == 0.0
|
||||||
|
return Float::NAN if x.nan?
|
||||||
|
|
||||||
|
if sign = x.infinite?
|
||||||
|
raise DomainError, "gamma" if sign == -1
|
||||||
|
|
||||||
|
return Float::INFINITY
|
||||||
|
end
|
||||||
|
|
||||||
|
FFI::MemoryPointer.new :double do |integral|
|
||||||
|
fractional = FFI::Platform::Math.modf x, integral
|
||||||
|
int = integral.read_double.to_i
|
||||||
|
|
||||||
|
if fractional == 0.0
|
||||||
|
raise DomainError, "gamma" if int < 0
|
||||||
|
return FactorialTable[int - 1] if int <= FactorialTable.size
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
FFI::Platform::Math.tgamma x
|
||||||
|
end
|
||||||
|
module_function :gamma
|
||||||
|
|
||||||
|
def lgamma(x)
|
||||||
|
x = Rubinius::Type.coerce_to_float x
|
||||||
|
|
||||||
|
if sign = x.infinite?
|
||||||
|
raise DomainError, "lgamma" if sign == -1
|
||||||
|
|
||||||
|
return [Float::INFINITY, 1]
|
||||||
|
end
|
||||||
|
|
||||||
|
FFI::MemoryPointer.new :int do |sign|
|
||||||
|
sign.write_int 1
|
||||||
|
result = FFI::Platform::Math.lgamma_r x, sign
|
||||||
|
[result, sign.read_int]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
module_function :lgamma
|
||||||
|
|
||||||
|
def log(x, base=undefined)
|
||||||
|
return Float::NAN if x.kind_of? Float and x.nan?
|
||||||
|
|
||||||
|
x = Rubinius::Type.coerce_to_float x
|
||||||
|
raise DomainError, 'log' unless x >= 0.0
|
||||||
|
return -Float::INFINITY if x == 0.0
|
||||||
|
y = FFI::Platform::Math.log x
|
||||||
|
unless undefined.equal? base
|
||||||
|
base = Rubinius::Type.coerce_to_float base
|
||||||
|
y /= log(base)
|
||||||
|
end
|
||||||
|
y
|
||||||
|
end
|
||||||
|
module_function :log
|
||||||
|
|
||||||
|
def log2(x)
|
||||||
|
return Float::NAN if x.kind_of? Float and x.nan?
|
||||||
|
|
||||||
|
if x.kind_of? Bignum and x >= 0 and Float::MAX_EXP <= (bits = x.bit_length)
|
||||||
|
bits -= Float::MANT_DIG
|
||||||
|
x >>= bits
|
||||||
|
else
|
||||||
|
bits = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
x = Rubinius::Type.coerce_to_float(x)
|
||||||
|
raise DomainError, 'log2' unless x >= 0.0
|
||||||
|
FFI::Platform::Math.log2(x) + bits
|
||||||
|
end
|
||||||
|
module_function :log2
|
||||||
|
|
||||||
|
def log10(x)
|
||||||
|
return Float::NAN if x.kind_of? Float and x.nan?
|
||||||
|
|
||||||
|
x = Rubinius::Type.coerce_to_float(x)
|
||||||
|
raise DomainError, 'log10' unless x >= 0.0
|
||||||
|
FFI::Platform::Math.log10 x
|
||||||
|
end
|
||||||
|
module_function :log10
|
||||||
|
|
||||||
|
def sqrt(x)
|
||||||
|
return Float::NAN if x.kind_of? Float and x.nan?
|
||||||
|
|
||||||
|
x = Rubinius::Type.coerce_to_float(x)
|
||||||
|
raise DomainError, 'sqrt' unless x >= 0.0
|
||||||
|
FFI::Platform::Math.sqrt x
|
||||||
|
end
|
||||||
|
module_function :sqrt
|
||||||
|
end
|
||||||
362
gxg/core/method.rb
Normal file
362
gxg/core/method.rb
Normal file
|
|
@ -0,0 +1,362 @@
|
||||||
|
##
|
||||||
|
# Method objects are essentially detached, freely passed-around methods. The
|
||||||
|
# Method is a copy of the method on the object at the time of extraction, so
|
||||||
|
# if the method itself is changed, overridden, aliased or removed from the
|
||||||
|
# object, the Method object still contains the old functionality. In addition,
|
||||||
|
# the call itself is not in any way stored so it will reflect the state of the
|
||||||
|
# object at the time of calling.
|
||||||
|
#
|
||||||
|
# Methods are normally bound to a particular object but it is possible to use
|
||||||
|
# Method#unbind to create an UnboundMethod object for the purpose of
|
||||||
|
# re-binding to a different object.
|
||||||
|
|
||||||
|
class Method
|
||||||
|
|
||||||
|
##
|
||||||
|
# Takes and stores the receiver object, the method's bytecodes and the
|
||||||
|
# Module that the method is defined in.
|
||||||
|
|
||||||
|
def initialize(receiver, defined_in, executable, name)
|
||||||
|
@receiver = receiver
|
||||||
|
@defined_in = defined_in
|
||||||
|
@executable = executable
|
||||||
|
@name = name
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
attr_reader :receiver
|
||||||
|
attr_reader :defined_in
|
||||||
|
attr_reader :executable
|
||||||
|
|
||||||
|
def name
|
||||||
|
@name
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Method objects are equal if they have the same body and are bound to the
|
||||||
|
# same object.
|
||||||
|
|
||||||
|
def ==(other)
|
||||||
|
other.class == Method &&
|
||||||
|
@receiver.equal?(other.receiver) &&
|
||||||
|
(@executable == other.executable ||
|
||||||
|
Rubinius::MethodEquality.method_equal_to_delegated_method_receiver?(self, other))
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :eql?, :==
|
||||||
|
|
||||||
|
##
|
||||||
|
# Indication of how many arguments this method takes. It is defined so that
|
||||||
|
# a non-negative Integer means the method takes that fixed amount of
|
||||||
|
# arguments (up to 1024 currently.) A negative Integer is used to indicate a
|
||||||
|
# variable argument count. The number is ((-n) - 1), where n is the number
|
||||||
|
# of required args. Blocks are not counted.
|
||||||
|
#
|
||||||
|
# def foo(); end # arity => 0
|
||||||
|
# def foo(a, b); end # arity => 2
|
||||||
|
# def foo(a, &b); end # arity => 1
|
||||||
|
# def foo(a, b = nil); end # arity => ((-1) -1) => -2
|
||||||
|
# def foo(*a); end # arity => ((-0) -1) => -1
|
||||||
|
# def foo(a, b, *c, &d); end # arity => ((-2) -1) => -3
|
||||||
|
|
||||||
|
def arity
|
||||||
|
@executable.arity
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Execute the method. This works exactly like calling a method with the same
|
||||||
|
# code on the receiver object. Arguments and a block can be supplied
|
||||||
|
# optionally.
|
||||||
|
|
||||||
|
def call(*args, &block)
|
||||||
|
@executable.invoke(@name, @defined_in, @receiver, args, block)
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :[], :call
|
||||||
|
|
||||||
|
##
|
||||||
|
# String representation of this Method includes the method name, the Module
|
||||||
|
# it is defined in and the Module that it was extracted from.
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
file, line = source_location()
|
||||||
|
|
||||||
|
if @executable.respond_to? :eval_source
|
||||||
|
if src = @executable.eval_source
|
||||||
|
src_line = src.split("\n")[line - 1]
|
||||||
|
return "#<#{self.class}: #{@receiver.class}##{@name} (defined in #{@defined_in} from \"#{src_line}\")>"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if file
|
||||||
|
"#<#{self.class}: #{@receiver.class}##{@name} (defined in #{@defined_in} at #{file}:#{line})>"
|
||||||
|
else
|
||||||
|
"#<#{self.class}: #{@receiver.class}##{@name} (defined in #{@defined_in})>"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :to_s, :inspect
|
||||||
|
|
||||||
|
##
|
||||||
|
# Location gives the file and line number of the start of this method's
|
||||||
|
# definition.
|
||||||
|
|
||||||
|
def source_location
|
||||||
|
if @executable.respond_to? :file
|
||||||
|
[@executable.file.to_s, @executable.defined_line]
|
||||||
|
elsif @executable.respond_to? :source_location
|
||||||
|
@executable.source_location
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def source
|
||||||
|
if @executable.respond_to? :eval_source
|
||||||
|
return @executable.eval_source
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def parameters
|
||||||
|
if @executable.respond_to? :parameters
|
||||||
|
@executable.parameters
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def owner
|
||||||
|
if @defined_in.class == Rubinius::IncludedModule
|
||||||
|
@defined_in.module
|
||||||
|
else
|
||||||
|
@defined_in
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns a Proc object corresponding to this Method.
|
||||||
|
|
||||||
|
def to_proc
|
||||||
|
Proc.from_method self
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Calls curry on the method in proc representation
|
||||||
|
def curry(n = nil)
|
||||||
|
to_proc.curry(n)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Detach this Method from the receiver object it is bound to and create an
|
||||||
|
# UnboundMethod object. Populates the UnboundMethod with the method data as
|
||||||
|
# well as the Module it is defined in and the Module it was extracted from.
|
||||||
|
#
|
||||||
|
# See UnboundMethod for more information.
|
||||||
|
|
||||||
|
def unbind
|
||||||
|
UnboundMethod.new(@defined_in, @executable, @receiver.class, @name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def super_method
|
||||||
|
superclass = @defined_in.direct_superclass
|
||||||
|
|
||||||
|
if superclass
|
||||||
|
mod, entry = superclass.lookup_method(@name)
|
||||||
|
|
||||||
|
if entry && entry.visibility != :undef
|
||||||
|
return Method.new(@receiver, superclass, entry.method, @name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def for_define_method(name, klass, callable_proc = nil)
|
||||||
|
Rubinius::Type.bindable_method? self.defined_in, klass
|
||||||
|
|
||||||
|
scope = @executable.scope
|
||||||
|
|
||||||
|
if @executable.is_a? Rubinius::DelegatedMethod
|
||||||
|
code = @executable
|
||||||
|
else
|
||||||
|
if callable_proc
|
||||||
|
code = Rubinius::DelegatedMethod.new(name, :call, callable_proc, false)
|
||||||
|
else
|
||||||
|
code = Rubinius::DelegatedMethod.new(name, :call_on_instance, self.unbind, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
[code, scope]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# UnboundMethods are similar to Method objects except that they are not
|
||||||
|
# connected to any particular object. They cannot be used standalone for this
|
||||||
|
# reason, and must be bound to an object first. The object must be kind_of?
|
||||||
|
# the Module in which this method was originally defined.
|
||||||
|
#
|
||||||
|
# UnboundMethods can be created in two ways: first, any existing Method object
|
||||||
|
# can be sent #unbind to detach it from its current object and return an
|
||||||
|
# UnboundMethod instead. Secondly, they can be directly created by calling
|
||||||
|
# Module#instance_method with the desired method's name.
|
||||||
|
#
|
||||||
|
# The UnboundMethod is a copy of the method as it existed at the time of
|
||||||
|
# creation. Any subsequent changes to the original will not affect any
|
||||||
|
# existing UnboundMethods.
|
||||||
|
|
||||||
|
class UnboundMethod
|
||||||
|
|
||||||
|
##
|
||||||
|
# Accepts and stores the Module where the method is defined in as well as
|
||||||
|
# the CompiledCode itself. Class of the object the method was extracted
|
||||||
|
# from can be given but will not be stored. This is always used internally
|
||||||
|
# only.
|
||||||
|
|
||||||
|
def initialize(mod, executable, pulled_from, name)
|
||||||
|
@defined_in = mod
|
||||||
|
@executable = executable
|
||||||
|
@pulled_from = pulled_from
|
||||||
|
@name = name
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
attr_reader :executable
|
||||||
|
attr_reader :defined_in
|
||||||
|
|
||||||
|
def name
|
||||||
|
@name
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Convenience method for #binding to the given receiver object and calling
|
||||||
|
# it with the optionally supplied arguments.
|
||||||
|
|
||||||
|
def call_on_instance(obj, *args, &block)
|
||||||
|
@executable.invoke(@name, @defined_in, obj, args, block)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# UnboundMethod objects are equal if and only if they refer to the same
|
||||||
|
# method. One may be an alias for the other or both for a common one. Both
|
||||||
|
# must have been extracted from the same class or subclass. Two from
|
||||||
|
# different subclasses will not be considered equal.
|
||||||
|
|
||||||
|
def ==(other)
|
||||||
|
other.kind_of? UnboundMethod and
|
||||||
|
@defined_in == other.defined_in and
|
||||||
|
@executable == other.executable
|
||||||
|
end
|
||||||
|
|
||||||
|
def hash
|
||||||
|
@defined_in.hash ^ @executable.hash
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# See Method#arity.
|
||||||
|
|
||||||
|
def arity
|
||||||
|
@executable.arity
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Creates a new Method object by attaching this method to the supplied
|
||||||
|
# receiver. One of the following must be true:
|
||||||
|
# - the method was defined in a module
|
||||||
|
# - the method is being bound to an instance of a subclass of the method's source
|
||||||
|
# - the method is being bound to an instance of the same class
|
||||||
|
|
||||||
|
def bind(receiver)
|
||||||
|
from_module = Rubinius::Type.object_kind_of? @defined_in, Module
|
||||||
|
from_class = Rubinius::Type.object_kind_of? @defined_in, Class
|
||||||
|
from_module_not_class = from_module && !from_class
|
||||||
|
|
||||||
|
unless Rubinius::Type.object_kind_of?(receiver, @defined_in) or from_module_not_class
|
||||||
|
if Rubinius::Type.singleton_class_object(@defined_in)
|
||||||
|
raise TypeError, "illegal attempt to rebind a singleton method to another object"
|
||||||
|
end
|
||||||
|
|
||||||
|
raise TypeError, "Must be bound to an object of kind #{@defined_in}"
|
||||||
|
end
|
||||||
|
|
||||||
|
Method.new receiver, @defined_in, @executable, @name
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# String representation for UnboundMethod includes the method name, the
|
||||||
|
# Module it is defined in and the Module that it was extracted from.
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
file, line = source_location()
|
||||||
|
|
||||||
|
if file
|
||||||
|
"#<#{self.class}: #{@pulled_from}##{@name} (defined in #{@defined_in} at #{file}:#{line})>"
|
||||||
|
else
|
||||||
|
"#<#{self.class}: #{@pulled_from}##{@name} (defined in #{@defined_in})>"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :to_s, :inspect
|
||||||
|
|
||||||
|
def source_location
|
||||||
|
if @executable.respond_to? :file
|
||||||
|
[@executable.file.to_s, @executable.defined_line]
|
||||||
|
elsif @executable.respond_to? :source_location
|
||||||
|
@executable.source_location
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def parameters
|
||||||
|
return @executable.respond_to?(:parameters) ? @executable.parameters : []
|
||||||
|
end
|
||||||
|
|
||||||
|
def owner
|
||||||
|
if @defined_in.class == Rubinius::IncludedModule
|
||||||
|
@defined_in.module
|
||||||
|
else
|
||||||
|
@defined_in
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def super_method
|
||||||
|
superclass = @defined_in.direct_superclass
|
||||||
|
|
||||||
|
if superclass
|
||||||
|
mod, entry = superclass.lookup_method(@name)
|
||||||
|
|
||||||
|
if entry && entry.visibility != :undef
|
||||||
|
return UnboundMethod.new(superclass, entry.method, @defined_in, @name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def for_define_method(name, klass, callable_proc = nil)
|
||||||
|
Rubinius::Type.bindable_method? self.defined_in, klass
|
||||||
|
|
||||||
|
case @executable
|
||||||
|
when Rubinius::AccessVariable
|
||||||
|
code = @executable
|
||||||
|
scope = nil
|
||||||
|
when Rubinius::DelegatedMethod
|
||||||
|
code = @executable
|
||||||
|
scope = code.scope
|
||||||
|
else
|
||||||
|
if callable_proc
|
||||||
|
code = Rubinius::DelegatedMethod.new(name, :call, callable_proc, false)
|
||||||
|
else
|
||||||
|
code = Rubinius::DelegatedMethod.new(name, :call_on_instance, self, true)
|
||||||
|
end
|
||||||
|
scope = @executable.scope
|
||||||
|
end
|
||||||
|
|
||||||
|
[code, scope]
|
||||||
|
end
|
||||||
|
end
|
||||||
18
gxg/core/method_equality.rb
Normal file
18
gxg/core/method_equality.rb
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
module Rubinius
|
||||||
|
module MethodEquality
|
||||||
|
def self.method_equal_to_delegated_method_receiver?(meth, other)
|
||||||
|
self_receiver = delegated_receiver_for(meth) || meth.unbind
|
||||||
|
other_receiver = delegated_receiver_for(other) || other.unbind
|
||||||
|
either_method_is_delegated?(meth, other) && (self_receiver == other_receiver)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.either_method_is_delegated?(meth, other)
|
||||||
|
(meth.executable.kind_of?(Rubinius::CompiledCode) && other.executable.kind_of?(Rubinius::DelegatedMethod)) ||
|
||||||
|
(meth.executable.kind_of?(Rubinius::DelegatedMethod) && other.executable.kind_of?(Rubinius::CompiledCode))
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.delegated_receiver_for(meth)
|
||||||
|
meth.executable.receiver if meth.executable.kind_of?(Rubinius::DelegatedMethod)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
143
gxg/core/method_table.rb
Normal file
143
gxg/core/method_table.rb
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
##
|
||||||
|
# Method table associates symbols (names) with methods (executables). Each
|
||||||
|
# entry also contains the visibility of that method.
|
||||||
|
#
|
||||||
|
|
||||||
|
module Rubinius
|
||||||
|
class MethodTable
|
||||||
|
class Bucket
|
||||||
|
attr_reader :name
|
||||||
|
attr_reader :visibility
|
||||||
|
attr_reader :method
|
||||||
|
attr_reader :prediction
|
||||||
|
attr_reader :next
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :values
|
||||||
|
attr_reader :bins
|
||||||
|
|
||||||
|
def size
|
||||||
|
@entries
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :length, :size
|
||||||
|
|
||||||
|
def self.allocate
|
||||||
|
Rubinius.primitive :methodtable_allocate
|
||||||
|
raise PrimitiveFailure, "MethodTable.allocate primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(hash=nil)
|
||||||
|
return unless hash
|
||||||
|
hash.each do |k, v|
|
||||||
|
self[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def duplicate
|
||||||
|
Rubinius.primitive :methodtable_duplicate
|
||||||
|
raise PrimitiveFailure, "MethodTable#duplicate primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def dup
|
||||||
|
copy = duplicate
|
||||||
|
Rubinius.privately do
|
||||||
|
copy.initialize_copy self
|
||||||
|
end
|
||||||
|
copy
|
||||||
|
end
|
||||||
|
|
||||||
|
def name?(name)
|
||||||
|
Rubinius.primitive :methodtable_has_name
|
||||||
|
raise PrimitiveFailure, "MethodTable#name? primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :has_name?, :name?
|
||||||
|
alias_method :include?, :name?
|
||||||
|
alias_method :member?, :name?
|
||||||
|
|
||||||
|
def delete(name)
|
||||||
|
Rubinius.primitive :methodtable_delete
|
||||||
|
raise PrimitiveFailure, "MethodTable#delete primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def each
|
||||||
|
raise LocalJumpError, "no block given" unless block_given?
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
max = @bins
|
||||||
|
vals = @values
|
||||||
|
|
||||||
|
while i < max
|
||||||
|
if entry = vals.at(i)
|
||||||
|
while entry
|
||||||
|
yield entry.name, entry.method, entry.visibility
|
||||||
|
entry = entry.next
|
||||||
|
end
|
||||||
|
end
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
##
|
||||||
|
# Holds Executables for lookup by the VM.
|
||||||
|
#
|
||||||
|
# When looking up an Executable, Rubinius starts at the real class (the object
|
||||||
|
# in the class slot of the receiver) and looks in its MethodTable for the
|
||||||
|
# method name. If the method is not found the ancestors list will be
|
||||||
|
# walked looking for the method.
|
||||||
|
#
|
||||||
|
# If no method was found, Rubinius restarts at the real class looking for
|
||||||
|
# "method_missing", walking the ancestors list if it is not found.
|
||||||
|
#
|
||||||
|
# If "method_missing" is not found, a VM assertion is triggered.
|
||||||
|
|
||||||
|
module Rubinius
|
||||||
|
class MethodTable
|
||||||
|
include Enumerable
|
||||||
|
|
||||||
|
def filter_entries
|
||||||
|
out = []
|
||||||
|
i = 0
|
||||||
|
while i < @bins
|
||||||
|
if entry = @values.at(i)
|
||||||
|
while entry
|
||||||
|
if val = yield(entry)
|
||||||
|
out << val
|
||||||
|
end
|
||||||
|
|
||||||
|
entry = entry.next
|
||||||
|
end
|
||||||
|
end
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
out
|
||||||
|
end
|
||||||
|
|
||||||
|
def public_names
|
||||||
|
filter_entries do |entry|
|
||||||
|
entry.visibility == :public ? entry.name : nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def private_names
|
||||||
|
filter_entries do |entry|
|
||||||
|
entry.visibility == :private ? entry.name : nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def protected_names
|
||||||
|
filter_entries do |entry|
|
||||||
|
entry.visibility == :protected ? entry.name : nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :to_a, :public_names
|
||||||
|
end
|
||||||
|
end
|
||||||
32
gxg/core/metrics.rb
Normal file
32
gxg/core/metrics.rb
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
module Rubinius
|
||||||
|
module Metrics
|
||||||
|
@enabled = false
|
||||||
|
|
||||||
|
def self.enabled?
|
||||||
|
!!@enabled
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.data
|
||||||
|
@data ||= Data.new
|
||||||
|
end
|
||||||
|
|
||||||
|
class Data
|
||||||
|
def keys
|
||||||
|
Map.keys
|
||||||
|
end
|
||||||
|
|
||||||
|
def values
|
||||||
|
Values.to_a
|
||||||
|
end
|
||||||
|
|
||||||
|
def [](key)
|
||||||
|
Values[Map[key]]
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_hash
|
||||||
|
Map.map { |k, i| MetricsHash[k] = Values[i] }
|
||||||
|
MetricsHash
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
48
gxg/core/mirror.rb
Normal file
48
gxg/core/mirror.rb
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
module Rubinius
|
||||||
|
class Mirror
|
||||||
|
def self.subject=(klass)
|
||||||
|
@subject = klass
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.subject
|
||||||
|
@subject
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.reflect(obj)
|
||||||
|
klass = Rubinius.invoke_primitive :module_mirror, obj
|
||||||
|
klass.new obj if klass
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :object
|
||||||
|
|
||||||
|
def initialize(obj)
|
||||||
|
@object = obj
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
class Object < Mirror
|
||||||
|
self.subject = ::Object
|
||||||
|
|
||||||
|
def nil_code_id
|
||||||
|
"%08x" % Rubinius.invoke_primitive(:object_nil_code_id, @object)
|
||||||
|
end
|
||||||
|
|
||||||
|
def nil_ip
|
||||||
|
Rubinius.invoke_primitive :object_nil_ip, @object
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def instance_fields
|
||||||
|
Rubinius.invoke_primitive :object_instance_fields, @object
|
||||||
|
end
|
||||||
|
|
||||||
|
def instance_variables
|
||||||
|
Rubinius.invoke_primitive :object_instance_variables, @object
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
"#<#{self.class.name}:0x#{self.object_id.to_s(16)} object=#{@object.inspect}>"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
37
gxg/core/missing_method.rb
Normal file
37
gxg/core/missing_method.rb
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
##
|
||||||
|
# MissingMethod is used to implement the Method returned by Kernel#method
|
||||||
|
# when #respond_to_missing? returns true.
|
||||||
|
|
||||||
|
module Rubinius
|
||||||
|
class MissingMethod < Executable
|
||||||
|
attr_reader :receiver
|
||||||
|
attr_reader :method
|
||||||
|
|
||||||
|
def initialize(receiver, method)
|
||||||
|
@receiver = receiver
|
||||||
|
@method = method
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def call(called_object, called_method, *args, &block)
|
||||||
|
@receiver.__send__(@method, *args, &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def arity
|
||||||
|
-1
|
||||||
|
end
|
||||||
|
|
||||||
|
def parameters
|
||||||
|
[[:rest]]
|
||||||
|
end
|
||||||
|
|
||||||
|
def ==(other)
|
||||||
|
other.kind_of?(MissingMethod) &&
|
||||||
|
@receiver == other.receiver &&
|
||||||
|
@method == other.method
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :eql?, :==
|
||||||
|
end
|
||||||
|
end
|
||||||
797
gxg/core/module.rb
Normal file
797
gxg/core/module.rb
Normal file
|
|
@ -0,0 +1,797 @@
|
||||||
|
##
|
||||||
|
# Some terminology notes:
|
||||||
|
#
|
||||||
|
# [Encloser] The Class or Module inside which this one is defined or, in the
|
||||||
|
# event we are at top-level, Object.
|
||||||
|
#
|
||||||
|
# [Direct superclass] Whatever is next in the chain of superclass invocations.
|
||||||
|
# This may be either an included Module, a Class or nil.
|
||||||
|
#
|
||||||
|
# [Superclass] The real semantic superclass and thus only applies to Class
|
||||||
|
# objects.
|
||||||
|
|
||||||
|
class Module
|
||||||
|
|
||||||
|
attr_reader :constant_table
|
||||||
|
attr_writer :method_table
|
||||||
|
|
||||||
|
private :included
|
||||||
|
|
||||||
|
def self.nesting
|
||||||
|
scope = Rubinius::LexicalScope.of_sender
|
||||||
|
nesting = []
|
||||||
|
while scope and scope.module != Object
|
||||||
|
nesting << scope.module
|
||||||
|
scope = scope.parent
|
||||||
|
end
|
||||||
|
nesting
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(&block)
|
||||||
|
# The method and constants tables are created and setup
|
||||||
|
# by Class and Module allocate.
|
||||||
|
module_eval(&block) if block
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def deprecate_constant(*syms)
|
||||||
|
# no-op for now
|
||||||
|
end
|
||||||
|
|
||||||
|
def const_get(name, inherit=true)
|
||||||
|
Rubinius::Type.const_get self, name, inherit
|
||||||
|
end
|
||||||
|
|
||||||
|
def const_defined?(name, inherit=true)
|
||||||
|
c = Rubinius::Type.const_get self, name, inherit, false
|
||||||
|
return c.equal?(undefined) ? false : true
|
||||||
|
end
|
||||||
|
|
||||||
|
def attr(*attributes)
|
||||||
|
vis = Rubinius::VariableScope.of_sender.method_visibility
|
||||||
|
|
||||||
|
if attributes.size == 2 && (attributes.last.is_a?(TrueClass) || attributes.last.is_a?(FalseClass))
|
||||||
|
name = attributes.first
|
||||||
|
Rubinius.add_reader name, self, vis
|
||||||
|
Rubinius.add_writer name, self, vis if attributes.last
|
||||||
|
else
|
||||||
|
attributes.each do |name|
|
||||||
|
if name.is_a?(Symbol) || (name.respond_to?(:to_str) && name.to_str.is_a?(String) && !name.to_str.empty?)
|
||||||
|
Rubinius.add_reader(name, self, vis)
|
||||||
|
else
|
||||||
|
raise TypeError, "#{name.inspect} is not a symbol"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
private :attr
|
||||||
|
|
||||||
|
# Install a new Autoload object into the constants table
|
||||||
|
# See core/autoload.rb
|
||||||
|
def autoload(name, path)
|
||||||
|
path = Rubinius::Type.coerce_to_path(path)
|
||||||
|
|
||||||
|
raise ArgumentError, "empty file name" if path.empty?
|
||||||
|
|
||||||
|
name = Rubinius::Type.coerce_to_constant_name name
|
||||||
|
|
||||||
|
Rubinius.check_frozen
|
||||||
|
|
||||||
|
if entry = @constant_table.lookup(name)
|
||||||
|
if entry.constant.kind_of? Autoload
|
||||||
|
# If there is already an Autoload here, just change the path to
|
||||||
|
# autoload!
|
||||||
|
entry.constant.set_path(path)
|
||||||
|
else
|
||||||
|
# Trying to register an autoload for a constant that already exists,
|
||||||
|
# ignore the request entirely.
|
||||||
|
end
|
||||||
|
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
autoload_constant = Autoload.new(name, self, path)
|
||||||
|
constant_table.store(name, autoload_constant, :public)
|
||||||
|
if self == Kernel
|
||||||
|
Object.singleton_class.constant_table.store(name, autoload_constant, :public)
|
||||||
|
end
|
||||||
|
|
||||||
|
Rubinius.inc_global_serial
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def constants(all=undefined)
|
||||||
|
tbl = Rubinius::LookupTable.new
|
||||||
|
|
||||||
|
@constant_table.each do |name, constant, visibility|
|
||||||
|
tbl[name] = true unless visibility == :private
|
||||||
|
end
|
||||||
|
|
||||||
|
if all
|
||||||
|
current = self.direct_superclass
|
||||||
|
|
||||||
|
while current and current != Object
|
||||||
|
current.constant_table.each do |name, constant, visibility|
|
||||||
|
tbl[name] = true unless visibility == :private
|
||||||
|
end
|
||||||
|
|
||||||
|
current = current.direct_superclass
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# special case: Module.constants returns Object's constants
|
||||||
|
if self.equal?(Module) && undefined.equal?(all)
|
||||||
|
Object.constant_table.each do |name, constant, visibility|
|
||||||
|
tbl[name] = true unless visibility == :private
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
tbl.keys
|
||||||
|
end
|
||||||
|
|
||||||
|
#--
|
||||||
|
# These have to be aliases, not methods that call instance eval, because we
|
||||||
|
# need to pull in the binding of the person that calls them, not the
|
||||||
|
# intermediate binding.
|
||||||
|
#++
|
||||||
|
|
||||||
|
def module_eval(string=undefined, filename="(eval)", line=1, &prc)
|
||||||
|
# we have a custom version with the prc, rather than using instance_exec
|
||||||
|
# so that we can setup the LexicalScope properly.
|
||||||
|
if prc
|
||||||
|
unless undefined.equal?(string)
|
||||||
|
raise ArgumentError, "cannot pass both string and proc"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return a copy of the BlockEnvironment with the receiver set to self
|
||||||
|
env = prc.block
|
||||||
|
lexical_scope = env.repoint_scope self
|
||||||
|
return env.call_under(self, lexical_scope, true, self)
|
||||||
|
elsif undefined.equal?(string)
|
||||||
|
raise ArgumentError, 'block not supplied'
|
||||||
|
end
|
||||||
|
|
||||||
|
string = StringValue(string)
|
||||||
|
filename = StringValue(filename)
|
||||||
|
|
||||||
|
# The constantscope of a module_eval CM is the receiver of module_eval
|
||||||
|
cs = Rubinius::LexicalScope.new self, Rubinius::LexicalScope.of_sender
|
||||||
|
|
||||||
|
binding = Binding.setup(Rubinius::VariableScope.of_sender,
|
||||||
|
Rubinius::CompiledCode.of_sender,
|
||||||
|
cs)
|
||||||
|
|
||||||
|
c = Rubinius::ToolSets::Runtime::Compiler
|
||||||
|
be = c.construct_block string, binding, filename, line
|
||||||
|
|
||||||
|
be.call_under self, cs, true, self
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :class_eval, :module_eval
|
||||||
|
|
||||||
|
def private_constant(*names)
|
||||||
|
names = names.map(&:to_sym)
|
||||||
|
unknown_constants = names - @constant_table.keys
|
||||||
|
if unknown_constants.size > 0
|
||||||
|
raise NameError, "#{unknown_constants.size > 1 ? 'Constants' : 'Constant'} #{unknown_constants.map{|e| "#{name}::#{e}"}.join(', ')} undefined"
|
||||||
|
end
|
||||||
|
names.each do |name|
|
||||||
|
entry = @constant_table.lookup(name)
|
||||||
|
entry.visibility = :private
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def public_constant(*names)
|
||||||
|
names = names.map(&:to_sym)
|
||||||
|
unknown_constants = names - @constant_table.keys
|
||||||
|
if unknown_constants.size > 0
|
||||||
|
raise NameError, "#{unknown_constants.size > 1 ? 'Constants' : 'Constant'} #{unknown_constants.map{|e| "#{name}::#{e}"}.join(', ')} undefined"
|
||||||
|
end
|
||||||
|
names.each do |name|
|
||||||
|
entry = @constant_table.lookup(name)
|
||||||
|
entry.visibility = :public
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns an UnboundMethod corresponding to the given name. The name will be
|
||||||
|
# searched for in this Module as well as any included Modules or
|
||||||
|
# superclasses. The UnboundMethod is populated with the method name and the
|
||||||
|
# Module that the method was located in.
|
||||||
|
#
|
||||||
|
# Raises a TypeError if the given name.to_sym fails and a NameError if the
|
||||||
|
# name cannot be located.
|
||||||
|
|
||||||
|
def instance_method(name)
|
||||||
|
name = Rubinius::Type.coerce_to_symbol name
|
||||||
|
|
||||||
|
mod = self
|
||||||
|
while mod
|
||||||
|
if mod == mod.origin && entry = mod.method_table.lookup(name)
|
||||||
|
break if entry.visibility == :undef
|
||||||
|
|
||||||
|
if meth = entry.get_method
|
||||||
|
if meth.kind_of? Rubinius::Alias
|
||||||
|
meth = meth.original_exec
|
||||||
|
end
|
||||||
|
|
||||||
|
mod = mod.module if mod.class == Rubinius::IncludedModule
|
||||||
|
|
||||||
|
return UnboundMethod.new(mod, meth, self, name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
mod = mod.direct_superclass
|
||||||
|
end
|
||||||
|
|
||||||
|
raise NameError.new("undefined method `#{name}' for #{self}", name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def public_instance_method(name)
|
||||||
|
name = Rubinius::Type.coerce_to_symbol name
|
||||||
|
|
||||||
|
mod = self
|
||||||
|
while mod
|
||||||
|
if mod == mod.origin && entry = mod.method_table.lookup(name)
|
||||||
|
vis = entry.visibility
|
||||||
|
break if vis == :undef
|
||||||
|
|
||||||
|
if meth = entry.method
|
||||||
|
if meth.kind_of? Rubinius::Alias
|
||||||
|
meth = meth.original_exec
|
||||||
|
end
|
||||||
|
|
||||||
|
mod = mod.module if mod.class == Rubinius::IncludedModule
|
||||||
|
|
||||||
|
if vis == :public
|
||||||
|
return UnboundMethod.new(mod, meth, self, name)
|
||||||
|
else
|
||||||
|
raise NameError.new("method `#{name}' for module `#{self}' is #{vis}", name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
mod = mod.direct_superclass
|
||||||
|
end
|
||||||
|
|
||||||
|
raise NameError.new("undefined method `#{name}' for #{self}", name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def visibility_for_aliased_method(new_name, current_name_visibility)
|
||||||
|
special_methods = [
|
||||||
|
:initialize,
|
||||||
|
:initialize_copy,
|
||||||
|
:initialize_clone,
|
||||||
|
:initialize_dup,
|
||||||
|
:respond_to_missing?
|
||||||
|
]
|
||||||
|
|
||||||
|
if special_methods.include?(new_name)
|
||||||
|
:private
|
||||||
|
else
|
||||||
|
current_name_visibility
|
||||||
|
end
|
||||||
|
end
|
||||||
|
private :visibility_for_aliased_method
|
||||||
|
|
||||||
|
def verify_class_variable_name(name)
|
||||||
|
if name.kind_of? Symbol
|
||||||
|
return name if name.is_cvar?
|
||||||
|
raise NameError, "#{name} is not an allowed class variable name"
|
||||||
|
end
|
||||||
|
|
||||||
|
name = StringValue(name).to_sym
|
||||||
|
unless name.is_cvar?
|
||||||
|
raise NameError, "#{name} is not an allowed class variable name"
|
||||||
|
end
|
||||||
|
name
|
||||||
|
end
|
||||||
|
private :verify_class_variable_name
|
||||||
|
|
||||||
|
def class_variable_set(name, val)
|
||||||
|
Rubinius.primitive :module_cvar_set
|
||||||
|
|
||||||
|
class_variable_set verify_class_variable_name(name), val
|
||||||
|
end
|
||||||
|
|
||||||
|
def class_variable_get(name)
|
||||||
|
Rubinius.primitive :module_cvar_get
|
||||||
|
|
||||||
|
class_variable_get verify_class_variable_name(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def class_variable_defined?(name)
|
||||||
|
Rubinius.primitive :module_cvar_defined
|
||||||
|
|
||||||
|
class_variable_defined? verify_class_variable_name(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_class_variable(name)
|
||||||
|
Rubinius.primitive :module_cvar_remove
|
||||||
|
|
||||||
|
remove_class_variable verify_class_variable_name(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def __class_variables__
|
||||||
|
Rubinius.primitive :module_class_variables
|
||||||
|
|
||||||
|
raise PrimitiveFailure, "Module#__class_variables__ primitive failed"
|
||||||
|
end
|
||||||
|
alias_method :class_variables, :__class_variables__
|
||||||
|
|
||||||
|
def lookup_method(sym, check_object_too=true, trim_im=true)
|
||||||
|
mod = self
|
||||||
|
|
||||||
|
while mod
|
||||||
|
if mod == mod.origin && entry = mod.method_table.lookup(sym.to_sym)
|
||||||
|
mod = mod.module if trim_im and mod.kind_of? Rubinius::IncludedModule
|
||||||
|
return [mod, entry]
|
||||||
|
end
|
||||||
|
|
||||||
|
mod = mod.direct_superclass
|
||||||
|
end
|
||||||
|
|
||||||
|
# Optionally also search Object (and everything included in Object);
|
||||||
|
# this lets a module alias methods on Object or Kernel.
|
||||||
|
if check_object_too and instance_of?(Module)
|
||||||
|
return Object.lookup_method(sym, true, trim_im)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def ancestors
|
||||||
|
out = []
|
||||||
|
Rubinius::Type.each_ancestor(self) { |mod| out << mod }
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
|
def public_method_defined?(sym)
|
||||||
|
sym = Rubinius::Type.coerce_to_reflection_name(sym)
|
||||||
|
mod, meth = lookup_method(sym, false)
|
||||||
|
meth ? meth.public? : false
|
||||||
|
end
|
||||||
|
|
||||||
|
def private_method_defined?(sym)
|
||||||
|
sym = Rubinius::Type.coerce_to_reflection_name(sym)
|
||||||
|
mod, meth = lookup_method(sym, false)
|
||||||
|
meth ? meth.private? : false
|
||||||
|
end
|
||||||
|
|
||||||
|
def protected_method_defined?(sym)
|
||||||
|
sym = Rubinius::Type.coerce_to_reflection_name(sym)
|
||||||
|
mod, meth = lookup_method(sym, false)
|
||||||
|
meth ? meth.protected? : false
|
||||||
|
end
|
||||||
|
|
||||||
|
def method_defined?(sym)
|
||||||
|
sym = Rubinius::Type.coerce_to_symbol(sym)
|
||||||
|
mod, meth = lookup_method(sym, false)
|
||||||
|
meth ? meth.public? || meth.protected? : false
|
||||||
|
end
|
||||||
|
|
||||||
|
def instance_methods(all=true)
|
||||||
|
ary = []
|
||||||
|
if all
|
||||||
|
table = Rubinius::LookupTable.new
|
||||||
|
|
||||||
|
mod = self
|
||||||
|
|
||||||
|
while mod
|
||||||
|
mod.method_table.each do |name, obj, vis|
|
||||||
|
unless table.key?(name)
|
||||||
|
table[name] = true
|
||||||
|
ary << name if vis == :public or vis == :protected
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
mod = mod.direct_superclass
|
||||||
|
end
|
||||||
|
else
|
||||||
|
method_table.each do |name, obj, vis|
|
||||||
|
ary << name if vis == :public or vis == :protected
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ary
|
||||||
|
end
|
||||||
|
|
||||||
|
def public_instance_methods(all=true)
|
||||||
|
filter_methods(:public, all)
|
||||||
|
end
|
||||||
|
|
||||||
|
def private_instance_methods(all=true)
|
||||||
|
filter_methods(:private, all)
|
||||||
|
end
|
||||||
|
|
||||||
|
def protected_instance_methods(all=true)
|
||||||
|
filter_methods(:protected, all)
|
||||||
|
end
|
||||||
|
|
||||||
|
def filter_methods(filter, all)
|
||||||
|
ary = []
|
||||||
|
if all and self.direct_superclass
|
||||||
|
table = Rubinius::LookupTable.new
|
||||||
|
mod = self
|
||||||
|
|
||||||
|
while mod
|
||||||
|
mod.method_table.each do |name, obj, vis|
|
||||||
|
unless table.key?(name)
|
||||||
|
table[name] = true
|
||||||
|
ary << name if vis == filter
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
mod = mod.direct_superclass
|
||||||
|
end
|
||||||
|
else
|
||||||
|
method_table.each do |name, obj, vis|
|
||||||
|
ary << name if vis == filter
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ary
|
||||||
|
end
|
||||||
|
|
||||||
|
private :filter_methods
|
||||||
|
|
||||||
|
def define_method(name, meth = undefined, &prc)
|
||||||
|
if undefined.equal?(meth) and !block_given?
|
||||||
|
raise ArgumentError, "tried to create Proc object without a block"
|
||||||
|
end
|
||||||
|
|
||||||
|
meth = prc if undefined.equal?(meth)
|
||||||
|
|
||||||
|
name = Rubinius::Type.coerce_to_symbol name
|
||||||
|
|
||||||
|
if meth.respond_to?(:for_define_method)
|
||||||
|
code, scope = meth.for_define_method(name, self.class)
|
||||||
|
else
|
||||||
|
raise TypeError, "wrong argument type #{meth.class} (expected Proc/Method)"
|
||||||
|
end
|
||||||
|
|
||||||
|
Rubinius.add_method name, code, self, scope, 0, :public
|
||||||
|
|
||||||
|
name
|
||||||
|
end
|
||||||
|
|
||||||
|
private :define_method
|
||||||
|
|
||||||
|
def thunk_method(name, value)
|
||||||
|
thunk = Rubinius::Thunk.new(value)
|
||||||
|
name = Rubinius::Type.coerce_to_symbol name
|
||||||
|
Rubinius.add_method name, thunk, self, nil, 0, :public
|
||||||
|
|
||||||
|
name
|
||||||
|
end
|
||||||
|
|
||||||
|
def extend_object(obj)
|
||||||
|
include_into Rubinius::Type.object_singleton_class(obj)
|
||||||
|
end
|
||||||
|
private :extend_object
|
||||||
|
|
||||||
|
def include?(mod)
|
||||||
|
if !mod.kind_of?(Module) or mod.kind_of?(Class)
|
||||||
|
raise TypeError, "wrong argument type #{mod.class} (expected Module)"
|
||||||
|
end
|
||||||
|
|
||||||
|
return false if self.equal?(mod)
|
||||||
|
|
||||||
|
Rubinius::Type.each_ancestor(self) { |m| return true if mod.equal?(m) }
|
||||||
|
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def included_modules
|
||||||
|
out = []
|
||||||
|
sup = direct_superclass
|
||||||
|
|
||||||
|
while sup
|
||||||
|
if sup.class == Rubinius::IncludedModule
|
||||||
|
out << sup.module
|
||||||
|
end
|
||||||
|
|
||||||
|
sup = sup.direct_superclass
|
||||||
|
end
|
||||||
|
|
||||||
|
out
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_visibility(meth, vis, where=nil)
|
||||||
|
name = Rubinius::Type.coerce_to_symbol(meth)
|
||||||
|
|
||||||
|
Rubinius::VM.reset_method_cache self, name
|
||||||
|
|
||||||
|
vis = vis.to_sym
|
||||||
|
|
||||||
|
if entry = @method_table.lookup(name)
|
||||||
|
entry.visibility = vis
|
||||||
|
elsif lookup_method(name)
|
||||||
|
@method_table.store name, nil, nil, nil, 0, vis
|
||||||
|
else
|
||||||
|
raise NameError.new("Unknown #{where}method '#{name}' to make #{vis.to_s} (#{self})", name)
|
||||||
|
end
|
||||||
|
|
||||||
|
return name
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_class_visibility(meth, vis)
|
||||||
|
Rubinius::Type.object_singleton_class(self).set_visibility meth, vis, "class "
|
||||||
|
end
|
||||||
|
private :set_class_visibility
|
||||||
|
|
||||||
|
def module_exec(*args, &prc)
|
||||||
|
raise LocalJumpError, "Missing block" unless block_given?
|
||||||
|
|
||||||
|
env = prc.block
|
||||||
|
lexical_scope = env.repoint_scope self
|
||||||
|
return env.call_under(self, lexical_scope, true, *args)
|
||||||
|
end
|
||||||
|
alias_method :class_exec, :module_exec
|
||||||
|
|
||||||
|
##
|
||||||
|
# Return the named constant enclosed in this Module.
|
||||||
|
#
|
||||||
|
# Included Modules and, for Class objects, superclasses are also searched.
|
||||||
|
# Modules will in addition look in Object. The name is attempted to convert
|
||||||
|
# using #to_str. If the constant is not found, #const_missing is called
|
||||||
|
# with the name.
|
||||||
|
|
||||||
|
def const_missing(name)
|
||||||
|
unless self == Object
|
||||||
|
mod_name = "#{Rubinius::Type.module_name self}::"
|
||||||
|
end
|
||||||
|
case Rubinius.constant_missing_reason
|
||||||
|
when :private
|
||||||
|
msg = "Private constant: #{mod_name}#{name}"
|
||||||
|
else
|
||||||
|
msg = "Missing or uninitialized constant: #{mod_name}#{name}"
|
||||||
|
end
|
||||||
|
|
||||||
|
raise NameError.new(msg, name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def <(other)
|
||||||
|
unless other.kind_of? Module
|
||||||
|
raise TypeError, "compared with non class/module"
|
||||||
|
end
|
||||||
|
|
||||||
|
# We're not an ancestor of ourself
|
||||||
|
return false if self.equal? other
|
||||||
|
|
||||||
|
Rubinius::Type.each_ancestor(self) { |mod| return true if mod.equal?(other) }
|
||||||
|
Rubinius::Type.each_ancestor(other) { |mod| return false if mod.equal?(self) }
|
||||||
|
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def <=(other)
|
||||||
|
equal?(other) || (self < other)
|
||||||
|
end
|
||||||
|
|
||||||
|
def >(other)
|
||||||
|
unless other.kind_of? Module
|
||||||
|
raise TypeError, "compared with non class/module"
|
||||||
|
end
|
||||||
|
other < self
|
||||||
|
end
|
||||||
|
|
||||||
|
def >=(other)
|
||||||
|
equal?(other) || (self > other)
|
||||||
|
end
|
||||||
|
|
||||||
|
def <=>(other)
|
||||||
|
return 0 if self.equal? other
|
||||||
|
return nil unless other.kind_of? Module
|
||||||
|
|
||||||
|
lt = self < other
|
||||||
|
return nil if lt.nil?
|
||||||
|
lt ? -1 : 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def ===(inst)
|
||||||
|
Rubinius::Type.object_kind_of?(inst, self)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Is an autoload trigger defined for the given path?
|
||||||
|
def autoload?(name)
|
||||||
|
name = name.to_sym
|
||||||
|
entry = constant_table.lookup(name)
|
||||||
|
return unless entry
|
||||||
|
trigger = entry.constant
|
||||||
|
return unless trigger && trigger.kind_of?(Autoload)
|
||||||
|
trigger.path
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_const(name)
|
||||||
|
unless name.kind_of? Symbol
|
||||||
|
name = StringValue name
|
||||||
|
|
||||||
|
unless Rubinius::CType.isupper name.getbyte(0)
|
||||||
|
raise NameError, "constant names must begin with a capital letter: #{name}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
sym = name.to_sym
|
||||||
|
unless constant_table.has_name?(sym)
|
||||||
|
mod_name = Rubinius::Type.module_name self
|
||||||
|
raise NameError, "Missing or uninitialized constant: #{mod_name}::#{name}"
|
||||||
|
end
|
||||||
|
|
||||||
|
val = constant_table.delete(sym)
|
||||||
|
Rubinius.inc_global_serial
|
||||||
|
|
||||||
|
# Silly API compact. Shield Autoload instances
|
||||||
|
return nil if Rubinius::Type.object_kind_of?(val, Autoload)
|
||||||
|
val
|
||||||
|
end
|
||||||
|
|
||||||
|
private :remove_const
|
||||||
|
|
||||||
|
def extended(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
private :extended
|
||||||
|
|
||||||
|
def method_added(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
private :method_added
|
||||||
|
|
||||||
|
def method_removed(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
private :method_removed
|
||||||
|
|
||||||
|
def method_undefined(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
private :method_undefined
|
||||||
|
|
||||||
|
def initialize_copy(other)
|
||||||
|
# If the method table is already different, we already
|
||||||
|
# initialized this module.
|
||||||
|
unless @method_table == other.method_table
|
||||||
|
raise TypeError, "already initialized module"
|
||||||
|
end
|
||||||
|
|
||||||
|
@method_table = other.method_table.dup
|
||||||
|
|
||||||
|
sc_s = Rubinius::Type.object_singleton_class self
|
||||||
|
sc_o = Rubinius::Type.object_singleton_class other
|
||||||
|
|
||||||
|
sc_s.method_table = sc_o.method_table.dup
|
||||||
|
sc_s.superclass = sc_o.direct_superclass
|
||||||
|
|
||||||
|
@constant_table = Rubinius::ConstantTable.new
|
||||||
|
|
||||||
|
other.constant_table.each do |name, val|
|
||||||
|
if val.kind_of? Autoload
|
||||||
|
val = Autoload.new(val.name, self, val.path)
|
||||||
|
end
|
||||||
|
|
||||||
|
@constant_table.store(name, val, :public)
|
||||||
|
end
|
||||||
|
|
||||||
|
if other == other.origin
|
||||||
|
@origin = self
|
||||||
|
else
|
||||||
|
@origin = other.origin.dup
|
||||||
|
end
|
||||||
|
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize_copy
|
||||||
|
|
||||||
|
def add_ivars(code)
|
||||||
|
case code
|
||||||
|
when Rubinius::CompiledCode
|
||||||
|
new_ivars = code.literals.select { |l| l.kind_of?(Symbol) and l.is_ivar? }
|
||||||
|
return if new_ivars.empty?
|
||||||
|
|
||||||
|
if @seen_ivars
|
||||||
|
new_ivars.each do |x|
|
||||||
|
unless @seen_ivars.include?(x)
|
||||||
|
@seen_ivars << x
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
@seen_ivars = new_ivars
|
||||||
|
end
|
||||||
|
when Rubinius::AccessVariable
|
||||||
|
if @seen_ivars
|
||||||
|
unless @seen_ivars.include?(code.name)
|
||||||
|
@seen_ivars << code.name
|
||||||
|
end
|
||||||
|
else
|
||||||
|
@seen_ivars = [code.name]
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise "Unknown type of method to learn ivars - #{code.class}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def dynamic_method(name, file="(dynamic)", line=1)
|
||||||
|
g = Rubinius::ToolSets::Runtime::Generator.new
|
||||||
|
g.name = name.to_sym
|
||||||
|
g.file = file.to_sym
|
||||||
|
g.set_line Integer(line)
|
||||||
|
|
||||||
|
yield g
|
||||||
|
|
||||||
|
g.close
|
||||||
|
|
||||||
|
g.use_detected
|
||||||
|
g.encode
|
||||||
|
|
||||||
|
code = g.package Rubinius::CompiledCode
|
||||||
|
|
||||||
|
scope = Rubinius::LexicalScope.new(self, Rubinius::LexicalScope.new(Object))
|
||||||
|
code.scope = scope
|
||||||
|
|
||||||
|
Rubinius.add_method name, code, self, scope, 0, :public
|
||||||
|
|
||||||
|
return code
|
||||||
|
end
|
||||||
|
|
||||||
|
def freeze
|
||||||
|
@method_table.freeze
|
||||||
|
@constant_table.freeze
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
def origin= origin
|
||||||
|
@origin = origin
|
||||||
|
end
|
||||||
|
|
||||||
|
def prepended(mod); end
|
||||||
|
private :prepended
|
||||||
|
|
||||||
|
def prepend(*modules)
|
||||||
|
modules.reverse_each do |mod|
|
||||||
|
if !mod.kind_of?(Module) or mod.kind_of?(Class)
|
||||||
|
raise TypeError, "wrong argument type #{mod.class} (expected Module)"
|
||||||
|
end
|
||||||
|
|
||||||
|
Rubinius.privately do
|
||||||
|
mod.prepend_features self
|
||||||
|
end
|
||||||
|
|
||||||
|
Rubinius.privately do
|
||||||
|
mod.prepended self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def prepend_features(klass)
|
||||||
|
unless klass.kind_of? Module
|
||||||
|
raise TypeError, "invalid argument class #{klass.class}, expected Module"
|
||||||
|
end
|
||||||
|
|
||||||
|
if kind_of? Class
|
||||||
|
raise TypeError, "invalid receiver class #{__class__}, expected Module"
|
||||||
|
end
|
||||||
|
|
||||||
|
if klass == klass.origin
|
||||||
|
im = Rubinius::IncludedModule.new(klass)
|
||||||
|
im.superclass = klass.direct_superclass
|
||||||
|
klass.superclass = im
|
||||||
|
klass.origin = im
|
||||||
|
klass.method_table.each do |meth, obj, vis|
|
||||||
|
Rubinius::VM.reset_method_cache klass, meth
|
||||||
|
end
|
||||||
|
end
|
||||||
|
Rubinius::Type.include_modules_from(self, klass)
|
||||||
|
Rubinius::Type.infect(klass, self)
|
||||||
|
self
|
||||||
|
end
|
||||||
|
private :prepend_features
|
||||||
|
|
||||||
|
def singleton_class?
|
||||||
|
!!Rubinius::Type.singleton_class_object(self)
|
||||||
|
end
|
||||||
|
end
|
||||||
82
gxg/core/mutex.rb
Normal file
82
gxg/core/mutex.rb
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
class Mutex
|
||||||
|
def initialize
|
||||||
|
@owner = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
# Check and only allow it to be marshal'd if there are no waiters.
|
||||||
|
def marshal_dump
|
||||||
|
raise "Unable to dump locked mutex" unless @waiters.empty?
|
||||||
|
1
|
||||||
|
end
|
||||||
|
|
||||||
|
# Implemented because we must since we use marshal_load PLUS we need
|
||||||
|
# to create AND prime @lock. If we didn't do this, then Marshal
|
||||||
|
# wouldn't prime the lock anyway.
|
||||||
|
def marshal_load(bunk)
|
||||||
|
initialize
|
||||||
|
end
|
||||||
|
|
||||||
|
def locked?
|
||||||
|
Rubinius.locked?(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
def try_lock
|
||||||
|
# Locking implies a memory barrier, so we don't need to use
|
||||||
|
# one explicitly.
|
||||||
|
if Rubinius.try_lock(self)
|
||||||
|
return false if @owner == Thread.current
|
||||||
|
@owner = Thread.current
|
||||||
|
true
|
||||||
|
else
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def lock
|
||||||
|
Rubinius.memory_barrier
|
||||||
|
if @owner == Thread.current
|
||||||
|
raise ThreadError, "Recursively locking not allowed"
|
||||||
|
end
|
||||||
|
|
||||||
|
Rubinius.lock self
|
||||||
|
@owner = Thread.current
|
||||||
|
Rubinius.memory_barrier
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
def unlock
|
||||||
|
Rubinius.memory_barrier
|
||||||
|
|
||||||
|
if @owner != Thread.current
|
||||||
|
raise ThreadError, "Not owner, #{@owner.inspect} is"
|
||||||
|
end
|
||||||
|
|
||||||
|
@owner = nil
|
||||||
|
Rubinius.unlock self
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def synchronize
|
||||||
|
lock
|
||||||
|
begin
|
||||||
|
yield
|
||||||
|
ensure
|
||||||
|
unlock
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def sleep(duration=undefined)
|
||||||
|
if duration.kind_of?(Numeric) && duration < 0
|
||||||
|
raise ArgumentError, "time interval must be positive"
|
||||||
|
end
|
||||||
|
|
||||||
|
unlock
|
||||||
|
begin
|
||||||
|
Kernel.sleep(duration)
|
||||||
|
ensure
|
||||||
|
lock
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
86
gxg/core/native_method.rb
Normal file
86
gxg/core/native_method.rb
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
##
|
||||||
|
# A wrapper for a calling a function in a shared library that has been
|
||||||
|
# attached via rb_define_method().
|
||||||
|
#
|
||||||
|
# The primitive slot for a NativeMethod points to the nmethod_call primitive
|
||||||
|
# which dispatches to the underlying C function.
|
||||||
|
|
||||||
|
module Rubinius
|
||||||
|
class NativeMethod < Executable
|
||||||
|
|
||||||
|
attr_reader :file
|
||||||
|
attr_reader :name
|
||||||
|
attr_reader :arity
|
||||||
|
|
||||||
|
#
|
||||||
|
# Load a dynamic library at path +library+, having name +extension_name+.
|
||||||
|
#
|
||||||
|
# +extension_name+ is used to calculate what Init_ function to call.
|
||||||
|
#
|
||||||
|
def self.load_extension(library, extension_name)
|
||||||
|
name = "Init_#{extension_name.sub(/\..*$/, '')}"
|
||||||
|
|
||||||
|
begin
|
||||||
|
lib = FFI::DynamicLibrary.new(library, FFI::DynamicLibrary::RTLD_NOW | FFI::DynamicLibrary::RTLD_GLOBAL)
|
||||||
|
rescue LoadError => e
|
||||||
|
raise LoadError::InvalidExtensionError, e.message, e
|
||||||
|
end
|
||||||
|
|
||||||
|
ver = lib.find_symbol("__X_rubinius_version")
|
||||||
|
|
||||||
|
unless ver
|
||||||
|
raise LoadError::InvalidExtensionError, "Out-of-date or not compatible. Recompile or reinstall gem (#{library})"
|
||||||
|
end
|
||||||
|
|
||||||
|
sym = lib.find_symbol(name)
|
||||||
|
|
||||||
|
unless sym
|
||||||
|
raise LoadError::InvalidExtensionError, "Missing Init_ function (#{name})"
|
||||||
|
end
|
||||||
|
|
||||||
|
entry_point = load_entry_point(library, name.to_sym, sym)
|
||||||
|
|
||||||
|
entry_point.invoke(:init, Rubinius, self, [], nil)
|
||||||
|
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Load extension and generate a NativeMethod for its entry point.
|
||||||
|
#
|
||||||
|
# +ptr+ is an FFI::Pointer to a C function.
|
||||||
|
#
|
||||||
|
def self.load_entry_point(library, name, ptr)
|
||||||
|
Rubinius.primitive :nativemethod_load_extension_entry_point
|
||||||
|
raise PrimitiveFailure, "Rubinius::NativeMethod3load_entry_point primitive failed: #{library}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def lines
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def literals
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def line_from_ip(i)
|
||||||
|
0
|
||||||
|
end
|
||||||
|
|
||||||
|
def first_line
|
||||||
|
0
|
||||||
|
end
|
||||||
|
|
||||||
|
def defined_line
|
||||||
|
0
|
||||||
|
end
|
||||||
|
|
||||||
|
def active_path
|
||||||
|
@file
|
||||||
|
end
|
||||||
|
|
||||||
|
def scope
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
53
gxg/core/nil.rb
Normal file
53
gxg/core/nil.rb
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
class NilClass
|
||||||
|
alias_method :eql?, :equal?
|
||||||
|
alias_method :==, :equal?
|
||||||
|
alias_method :===, :equal?
|
||||||
|
|
||||||
|
def &(other)
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def ^(other)
|
||||||
|
!!other
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :|, :^
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
""
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
"nil"
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_a
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_f
|
||||||
|
0.0
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_i
|
||||||
|
0
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_r
|
||||||
|
Rational(0, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def rationalize(eps = nil)
|
||||||
|
to_r
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_c
|
||||||
|
Complex(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_h
|
||||||
|
{}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
NIL = nil
|
||||||
289
gxg/core/numeric.rb
Normal file
289
gxg/core/numeric.rb
Normal file
|
|
@ -0,0 +1,289 @@
|
||||||
|
class Numeric
|
||||||
|
include Comparable
|
||||||
|
|
||||||
|
# Always raises TypeError, as dup'ing Numerics is not allowed.
|
||||||
|
def initialize_copy(other)
|
||||||
|
raise TypeError, "copy of #{self.class} is not allowed"
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize_copy
|
||||||
|
|
||||||
|
def +@
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def -@
|
||||||
|
0 - self
|
||||||
|
end
|
||||||
|
|
||||||
|
def divmod(other)
|
||||||
|
[div(other), self % other]
|
||||||
|
end
|
||||||
|
|
||||||
|
def eql?(other)
|
||||||
|
return false unless other.instance_of? self.class
|
||||||
|
self == other
|
||||||
|
end
|
||||||
|
|
||||||
|
def <=>(other)
|
||||||
|
# It's important this method NOT contain the coercion protocols!
|
||||||
|
# MRI doesn't and doing so breaks stuff!
|
||||||
|
|
||||||
|
return 0 if self.equal? other
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def step(limit=nil, step=nil, to: nil, by: nil)
|
||||||
|
unless block_given?
|
||||||
|
return to_enum(:step, limit, step, to: to, by: by) do
|
||||||
|
Rubinius::Mirror::Numeric.reflect(self).step_size(limit, step, to, by)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
m = Rubinius::Mirror::Numeric.reflect(self)
|
||||||
|
values = m.step_fetch_args(limit, step, to, by)
|
||||||
|
value = values[0]
|
||||||
|
limit = values[1]
|
||||||
|
step = values[2]
|
||||||
|
asc = values[3]
|
||||||
|
is_float = values[4]
|
||||||
|
|
||||||
|
if step == 0
|
||||||
|
while true
|
||||||
|
yield value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if is_float
|
||||||
|
n = m.step_float_size(value, limit, step, asc)
|
||||||
|
|
||||||
|
if n > 0
|
||||||
|
if step.infinite?
|
||||||
|
yield value
|
||||||
|
else
|
||||||
|
i = 0
|
||||||
|
if asc
|
||||||
|
while i < n
|
||||||
|
d = i * step + value
|
||||||
|
d = limit if limit < d
|
||||||
|
yield d
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
while i < n
|
||||||
|
d = i * step + value
|
||||||
|
d = limit if limit > d
|
||||||
|
yield d
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if asc
|
||||||
|
until value > limit
|
||||||
|
yield value
|
||||||
|
value += step
|
||||||
|
end
|
||||||
|
else
|
||||||
|
until value < limit
|
||||||
|
yield value
|
||||||
|
value += step
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
def truncate
|
||||||
|
Float(self).truncate
|
||||||
|
end
|
||||||
|
|
||||||
|
# Delegate #to_int to #to_i in subclasses
|
||||||
|
def to_int
|
||||||
|
to_i
|
||||||
|
end
|
||||||
|
|
||||||
|
def integer?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def zero?
|
||||||
|
self == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def nonzero?
|
||||||
|
zero? ? nil : self
|
||||||
|
end
|
||||||
|
|
||||||
|
def round
|
||||||
|
to_f.round
|
||||||
|
end
|
||||||
|
|
||||||
|
def abs
|
||||||
|
self < 0 ? -self : self
|
||||||
|
end
|
||||||
|
|
||||||
|
def floor
|
||||||
|
FloatValue(self).floor
|
||||||
|
end
|
||||||
|
|
||||||
|
def ceil
|
||||||
|
FloatValue(self).ceil
|
||||||
|
end
|
||||||
|
|
||||||
|
def remainder(other)
|
||||||
|
mod = self % other
|
||||||
|
|
||||||
|
if mod != 0 and ((self < 0 and other > 0) or (self > 0 and other < 0))
|
||||||
|
mod - other
|
||||||
|
else
|
||||||
|
mod
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#--
|
||||||
|
# We deviate from MRI behavior here because we ensure that Fixnum op Bignum
|
||||||
|
# => Bignum (possibly normalized to Fixnum)
|
||||||
|
#
|
||||||
|
# Note these differences on MRI, where a is a Fixnum, b is a Bignum
|
||||||
|
#
|
||||||
|
# a.coerce b => [Float, Float]
|
||||||
|
# b.coerce a => [Bignum, Bignum]
|
||||||
|
#++
|
||||||
|
|
||||||
|
def coerce(other)
|
||||||
|
if other.instance_of? self.class
|
||||||
|
return [other, self]
|
||||||
|
end
|
||||||
|
|
||||||
|
[Float(other), Float(self)]
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# This method mimics the semantics of MRI's do_coerce function
|
||||||
|
# in numeric.c. Note these differences between it and #coerce:
|
||||||
|
#
|
||||||
|
# 1.2.coerce("2") => [2.0, 1.2]
|
||||||
|
# 1.2 + "2" => TypeError: String can't be coerced into Float
|
||||||
|
#
|
||||||
|
# See also Integer#coerce
|
||||||
|
|
||||||
|
def math_coerce(other, error=:coerce_error)
|
||||||
|
begin
|
||||||
|
values = other.coerce(self)
|
||||||
|
rescue
|
||||||
|
if error == :coerce_error
|
||||||
|
raise TypeError, "#{other.class} can't be coerced into #{self.class}"
|
||||||
|
else
|
||||||
|
raise ArgumentError, "comparison of #{self.class} with #{other.class} failed"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
unless Rubinius::Type.object_kind_of?(values, Array) && values.length == 2
|
||||||
|
raise TypeError, "coerce must return [x, y]"
|
||||||
|
end
|
||||||
|
|
||||||
|
return values[1], values[0]
|
||||||
|
end
|
||||||
|
private :math_coerce
|
||||||
|
|
||||||
|
def redo_coerced(meth, right)
|
||||||
|
b, a = math_coerce(right)
|
||||||
|
a.__send__ meth, b
|
||||||
|
end
|
||||||
|
private :redo_coerced
|
||||||
|
|
||||||
|
def redo_compare(meth, right)
|
||||||
|
b, a = math_coerce(right, :compare_error)
|
||||||
|
a.__send__ meth, b
|
||||||
|
end
|
||||||
|
|
||||||
|
def div(other)
|
||||||
|
raise ZeroDivisionError, "divided by 0" if other == 0
|
||||||
|
(self / other).floor
|
||||||
|
end
|
||||||
|
|
||||||
|
def fdiv(other)
|
||||||
|
self.to_f / other
|
||||||
|
end
|
||||||
|
|
||||||
|
def quo(other)
|
||||||
|
Rubinius.privately do
|
||||||
|
Rational.convert(self, 1, false) / other
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def modulo(other)
|
||||||
|
self - other * self.div(other)
|
||||||
|
end
|
||||||
|
alias_method :%, :modulo
|
||||||
|
|
||||||
|
def i
|
||||||
|
Complex(0, self)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_c
|
||||||
|
Complex(self, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
def real
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def imag
|
||||||
|
0
|
||||||
|
end
|
||||||
|
alias_method :imaginary, :imag
|
||||||
|
|
||||||
|
def arg
|
||||||
|
if self < 0
|
||||||
|
Math::PI
|
||||||
|
else
|
||||||
|
0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
alias_method :angle, :arg
|
||||||
|
alias_method :phase, :arg
|
||||||
|
|
||||||
|
def polar
|
||||||
|
return abs, arg
|
||||||
|
end
|
||||||
|
|
||||||
|
def conjugate
|
||||||
|
self
|
||||||
|
end
|
||||||
|
alias_method :conj, :conjugate
|
||||||
|
|
||||||
|
def rect
|
||||||
|
[self, 0]
|
||||||
|
end
|
||||||
|
alias_method :rectangular, :rect
|
||||||
|
|
||||||
|
def abs2
|
||||||
|
self * self
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :magnitude, :abs
|
||||||
|
|
||||||
|
def numerator
|
||||||
|
to_r.numerator
|
||||||
|
end
|
||||||
|
|
||||||
|
def denominator
|
||||||
|
to_r.denominator
|
||||||
|
end
|
||||||
|
|
||||||
|
def real?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def positive?
|
||||||
|
self > 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def negative?
|
||||||
|
self < 0
|
||||||
|
end
|
||||||
|
end
|
||||||
74
gxg/core/numeric_mirror.rb
Normal file
74
gxg/core/numeric_mirror.rb
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
module Rubinius
|
||||||
|
class Mirror
|
||||||
|
class Numeric < Mirror
|
||||||
|
self.subject = ::Numeric
|
||||||
|
|
||||||
|
def step_float_size(value, limit, step, asc)
|
||||||
|
if (asc && value > limit) || (!asc && value < limit)
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
if step.infinite?
|
||||||
|
1
|
||||||
|
else
|
||||||
|
err = (value.abs + limit.abs + (limit - value).abs) / step.abs * Float::EPSILON
|
||||||
|
if err.finite?
|
||||||
|
err = 0.5 if err > 0.5
|
||||||
|
((limit - value) / step + err).floor + 1
|
||||||
|
else
|
||||||
|
0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def step_size(limit, step, to, by)
|
||||||
|
values = step_fetch_args(limit, step, to, by)
|
||||||
|
value = values[0]
|
||||||
|
limit = values[1]
|
||||||
|
step = values[2]
|
||||||
|
asc = values[3]
|
||||||
|
is_float = values[4]
|
||||||
|
|
||||||
|
if limit == Float::INFINITY
|
||||||
|
if step == Float::INFINITY
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
return Float::INFINITY
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return Float::INFINITY if step == 0
|
||||||
|
|
||||||
|
if is_float
|
||||||
|
# Ported from MRI
|
||||||
|
|
||||||
|
step_float_size(value, limit, step, asc)
|
||||||
|
|
||||||
|
else
|
||||||
|
if (asc && value > limit) || (!asc && value < limit)
|
||||||
|
0
|
||||||
|
else
|
||||||
|
((value - limit).abs + 1).fdiv(step.abs).ceil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def step_fetch_args(limit, step, to, by)
|
||||||
|
raise ArgumentError, "limit is given twice" if limit && to
|
||||||
|
raise ArgumentError, "step is given twice" if step && by
|
||||||
|
raise ArgumentError, "step cannot be 0" if step == 0
|
||||||
|
|
||||||
|
step ||= by || 1
|
||||||
|
asc = step > 0
|
||||||
|
limit ||= to || (asc ? Float::INFINITY : -Float::INFINITY)
|
||||||
|
value = @object
|
||||||
|
if value.kind_of? Float or limit.kind_of? Float or step.kind_of? Float
|
||||||
|
return FloatValue(value), FloatValue(limit), FloatValue(step), asc, true
|
||||||
|
else
|
||||||
|
return value, limit, step, asc, false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
88
gxg/core/object_space.rb
Normal file
88
gxg/core/object_space.rb
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
module ObjectSpace
|
||||||
|
def self.find_object(query, callable)
|
||||||
|
Rubinius.primitive :vm_find_object
|
||||||
|
raise PrimitiveFailure, "ObjectSpace#find_object primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self._id2ref(id)
|
||||||
|
ary = []
|
||||||
|
|
||||||
|
if find_object([:object_id, Integer(id)], ary) > 0
|
||||||
|
return ary.first
|
||||||
|
end
|
||||||
|
|
||||||
|
raise RangeError, "#{id} is not a valid ID value"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.find_references(obj)
|
||||||
|
ary = []
|
||||||
|
find_object([:references, obj], ary)
|
||||||
|
return ary
|
||||||
|
end
|
||||||
|
|
||||||
|
# @todo rewrite each_object
|
||||||
|
|
||||||
|
# Tries to handle as much as it can.
|
||||||
|
def self.each_object(what=nil, &block)
|
||||||
|
return to_enum :each_object, what unless block_given?
|
||||||
|
|
||||||
|
what ||= Object
|
||||||
|
|
||||||
|
unless what.kind_of? Object
|
||||||
|
raise TypeError, "class or module required"
|
||||||
|
end
|
||||||
|
|
||||||
|
case what
|
||||||
|
when Fixnum, Symbol
|
||||||
|
return 0
|
||||||
|
when TrueClass
|
||||||
|
yield true
|
||||||
|
return 1
|
||||||
|
when FalseClass
|
||||||
|
yield false
|
||||||
|
return 1
|
||||||
|
when NilClass
|
||||||
|
yield nil
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
return find_object([:kind_of, what], block)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.define_finalizer(obj, prc=nil, &block)
|
||||||
|
prc ||= block
|
||||||
|
|
||||||
|
if obj.kind_of? ImmediateValue or obj.kind_of? Float
|
||||||
|
raise ArgumentError, "can't define finalizer for #{obj.class}"
|
||||||
|
end
|
||||||
|
|
||||||
|
if obj.frozen?
|
||||||
|
raise RuntimeError, "can't modify frozen #{obj.class}"
|
||||||
|
end
|
||||||
|
|
||||||
|
if obj.equal? prc
|
||||||
|
# This is allowed. This is the Rubinius specific API that calls
|
||||||
|
# __finalize__ when the object is finalized.
|
||||||
|
elsif !prc and obj.respond_to?(:__finalize__)
|
||||||
|
# Allowed. Same thing as above
|
||||||
|
prc = obj
|
||||||
|
elsif !prc or !prc.respond_to?(:call)
|
||||||
|
raise ArgumentError, "action must respond to call"
|
||||||
|
end
|
||||||
|
|
||||||
|
unless Rubinius.invoke_primitive(:vm_set_finalizer, obj, prc)
|
||||||
|
raise RuntimeError, "cannot define a finalizer for a #{obj.class}"
|
||||||
|
end
|
||||||
|
|
||||||
|
[0, prc]
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.undefine_finalizer(obj)
|
||||||
|
Rubinius.invoke_primitive :vm_set_finalizer, obj, nil
|
||||||
|
return obj
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.garbage_collect
|
||||||
|
GC.start
|
||||||
|
end
|
||||||
|
end
|
||||||
254
gxg/core/options.rb
Normal file
254
gxg/core/options.rb
Normal file
|
|
@ -0,0 +1,254 @@
|
||||||
|
module Rubinius
|
||||||
|
|
||||||
|
# A simple command line option parser.
|
||||||
|
class Options
|
||||||
|
|
||||||
|
# A single command line option.
|
||||||
|
class Option
|
||||||
|
attr_reader :short
|
||||||
|
attr_reader :long
|
||||||
|
attr_reader :arg
|
||||||
|
attr_reader :description
|
||||||
|
attr_reader :block
|
||||||
|
|
||||||
|
def initialize(short, long, arg, description, block)
|
||||||
|
@short = short
|
||||||
|
@long = long
|
||||||
|
@arg = arg
|
||||||
|
@description = description
|
||||||
|
@block = block
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def arg?
|
||||||
|
@arg != nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def optional?
|
||||||
|
@arg[0] == ?[ and @arg[-1] == ?]
|
||||||
|
end
|
||||||
|
|
||||||
|
def match?(opt)
|
||||||
|
opt == @short or opt == @long
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Raised if incorrect or incomplete formats are passed to #on.
|
||||||
|
class OptionError < Exception; end
|
||||||
|
|
||||||
|
# Raised if an unrecognized option is encountered.
|
||||||
|
class ParseError < Exception; end
|
||||||
|
|
||||||
|
attr_accessor :config
|
||||||
|
attr_accessor :banner
|
||||||
|
attr_accessor :width
|
||||||
|
attr_accessor :options
|
||||||
|
|
||||||
|
def initialize(banner="", width=30, config=nil)
|
||||||
|
@parse = true
|
||||||
|
@banner = banner
|
||||||
|
@config = config
|
||||||
|
@width = width
|
||||||
|
@options = []
|
||||||
|
@doc = []
|
||||||
|
@extra = []
|
||||||
|
@align = true
|
||||||
|
@on_extra = lambda { |x|
|
||||||
|
raise ParseError, "Unrecognized option: #{x}" if x[0] == ?-
|
||||||
|
@extra << x
|
||||||
|
}
|
||||||
|
|
||||||
|
yield self if block_given?
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
# Documentation for options is left aligned. For example,
|
||||||
|
#
|
||||||
|
# -a ARG Some description
|
||||||
|
# --big Another bit of info
|
||||||
|
# -c, --class Yet more info
|
||||||
|
#
|
||||||
|
# See +option_align+.
|
||||||
|
def left_align
|
||||||
|
@align = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# Documentation for options is aligned by option type. For example,
|
||||||
|
#
|
||||||
|
# -a ARG Some description
|
||||||
|
# --big Another bit of info
|
||||||
|
# -c, --class Yet more info
|
||||||
|
#
|
||||||
|
# See +left_align+
|
||||||
|
def option_align
|
||||||
|
@align = true
|
||||||
|
end
|
||||||
|
|
||||||
|
# Registers an option. Acceptable formats for arguments are:
|
||||||
|
#
|
||||||
|
# on "-a", "description"
|
||||||
|
# on "-a", "--abdc", "description"
|
||||||
|
# on "-a", "ARG", "description"
|
||||||
|
# on "--abdc", "ARG", "description"
|
||||||
|
# on "-a", "--abdc", "ARG", "description"
|
||||||
|
# on "-a", "[ARG]", "description"
|
||||||
|
#
|
||||||
|
# The [ARG] form specifies an optional argument. The argument
|
||||||
|
# must be attached to the option. In the case of a short option,
|
||||||
|
# the form "-aARG" must be used. For a long option, the form
|
||||||
|
# '--opt=ARG' must be used.
|
||||||
|
#
|
||||||
|
# If an block is passed, it will be invoked when the option is
|
||||||
|
# matched. Not passing a block is permitted, but nonsensical.
|
||||||
|
def on(*args, &block)
|
||||||
|
raise OptionError, "option and description are required" if args.size < 2
|
||||||
|
|
||||||
|
description = args.pop
|
||||||
|
short, long, argument = nil
|
||||||
|
args.each do |arg|
|
||||||
|
if arg[0] == ?-
|
||||||
|
if arg[1] == ?-
|
||||||
|
long = arg
|
||||||
|
else
|
||||||
|
short = arg
|
||||||
|
end
|
||||||
|
else
|
||||||
|
argument = arg
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
add short, long, argument, description, block
|
||||||
|
end
|
||||||
|
|
||||||
|
# Adds documentation text for an option and adds an +Option+
|
||||||
|
# instance to the list of registered options.
|
||||||
|
def add(short, long, arg, description, block)
|
||||||
|
pad = @align ? " " : ""
|
||||||
|
s = short ? short.dup : pad
|
||||||
|
s << (short ? ", " : pad) if long
|
||||||
|
doc " #{s}#{long} #{arg}".ljust(@width-1) + " #{description}"
|
||||||
|
@options << Option.new(short, long, arg, description, block)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Searches all registered options to find a match for +opt+. Returns
|
||||||
|
# +nil+ if no registered options match.
|
||||||
|
def match?(opt)
|
||||||
|
@options.find { |o| o.match? opt }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Processes an option. Calles the #on_extra block (or default) for
|
||||||
|
# unrecognized options. For registered options, possibly fetches an
|
||||||
|
# argument and invokes the option's block if it is not nil.
|
||||||
|
def process(argv, entry, opt, arg)
|
||||||
|
unless option = match?(opt)
|
||||||
|
@on_extra[entry]
|
||||||
|
else
|
||||||
|
if option.arg?
|
||||||
|
if arg.nil?
|
||||||
|
unless option.optional?
|
||||||
|
arg = argv.shift
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
unless arg or option.optional?
|
||||||
|
raise ParseError, "No argument provided for #{opt}"
|
||||||
|
end
|
||||||
|
|
||||||
|
option.block[arg] if option.block
|
||||||
|
else
|
||||||
|
option.block[] if option.block
|
||||||
|
end
|
||||||
|
end
|
||||||
|
option
|
||||||
|
end
|
||||||
|
|
||||||
|
# Splits a string at +n+ characters into the +opt+ and the +rest+.
|
||||||
|
# The +arg+ is set to +nil+ if +rest+ is an empty string.
|
||||||
|
def split(str, n)
|
||||||
|
opt = str[0, n]
|
||||||
|
rest = str[n, str.size] || ""
|
||||||
|
arg = rest == "" ? nil : rest
|
||||||
|
return opt, arg, rest
|
||||||
|
end
|
||||||
|
|
||||||
|
# Parses an array of command line entries, calling blocks for
|
||||||
|
# registered options.
|
||||||
|
def parse(argv=ARGV)
|
||||||
|
argv = Array(argv)
|
||||||
|
|
||||||
|
while @parse and entry = argv.shift
|
||||||
|
# collect everything that is not an option
|
||||||
|
if entry[0] != ?-
|
||||||
|
@on_extra[entry]
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
# this is a long option
|
||||||
|
if entry[1] == ?-
|
||||||
|
opt, arg = entry.split "="
|
||||||
|
process argv, entry, opt, arg
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
# disambiguate short option group from short option with argument
|
||||||
|
opt, arg, rest = split entry, 2
|
||||||
|
|
||||||
|
# process first option
|
||||||
|
option = process argv, entry, opt, arg
|
||||||
|
next unless option and not option.arg?
|
||||||
|
|
||||||
|
# process the rest of the options
|
||||||
|
while rest.size > 0
|
||||||
|
opt, arg, rest = split rest, 1
|
||||||
|
opt = "-" + opt
|
||||||
|
option = process argv, opt, opt, arg
|
||||||
|
break if option.arg?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@extra
|
||||||
|
rescue ParseError => e
|
||||||
|
puts self
|
||||||
|
puts e
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def start_parsing
|
||||||
|
@parse = true
|
||||||
|
end
|
||||||
|
|
||||||
|
def stop_parsing
|
||||||
|
@parse = false
|
||||||
|
end
|
||||||
|
|
||||||
|
# Adds a string of documentation text inline in the text generated
|
||||||
|
# from the options. See #on and #add.
|
||||||
|
def doc(str)
|
||||||
|
@doc << str
|
||||||
|
end
|
||||||
|
|
||||||
|
# Convenience method for providing -v, --version options.
|
||||||
|
def version(version, &block)
|
||||||
|
show = block || lambda { puts "#{File.basename $0} #{version}"; exit }
|
||||||
|
on "-v", "--version", "Show version", &show
|
||||||
|
end
|
||||||
|
|
||||||
|
# Convenience method for providing -h, --help options.
|
||||||
|
def help(&block)
|
||||||
|
help = block || lambda { puts self; exit 1 }
|
||||||
|
on "-h", "--help", "Show this message", &help
|
||||||
|
end
|
||||||
|
|
||||||
|
# Stores a block that will be called with unrecognized options
|
||||||
|
def on_extra(&block)
|
||||||
|
@on_extra = block
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns a string representation of the options and doc strings.
|
||||||
|
def to_s
|
||||||
|
@banner + "\n\n" + @doc.join("\n") + "\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
27
gxg/core/pack.rb
Normal file
27
gxg/core/pack.rb
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
module Rubinius
|
||||||
|
def self.pack_to_float(obj)
|
||||||
|
unless obj.kind_of?(Numeric)
|
||||||
|
raise TypeError, "unable to convert String to Float"
|
||||||
|
end
|
||||||
|
Type.coerce_to obj, Float, :to_f
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.pack_to_int(obj)
|
||||||
|
Rubinius::Type.coerce_to obj, Integer, :to_int
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.pack_to_str(obj)
|
||||||
|
Rubinius::Type.coerce_to obj, String, :to_str
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.pack_to_str_or_nil(obj)
|
||||||
|
return "" if obj.nil?
|
||||||
|
Rubinius::Type.coerce_to obj, String, :to_str
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.pack_to_s(obj)
|
||||||
|
str = obj.to_s
|
||||||
|
str = obj.inspect unless Type.object_kind_of? str, String
|
||||||
|
str
|
||||||
|
end
|
||||||
|
end
|
||||||
347
gxg/core/peg.rb
Normal file
347
gxg/core/peg.rb
Normal file
|
|
@ -0,0 +1,347 @@
|
||||||
|
module Rubinius
|
||||||
|
module PEG
|
||||||
|
class InvalidPatternError < Exception; end
|
||||||
|
|
||||||
|
# Class Pattern is the base class for all nodes used to represent the AST
|
||||||
|
# for a PEG. Following LPEG, the base is a pattern and not a grammar.
|
||||||
|
class Pattern
|
||||||
|
def graph
|
||||||
|
if defined? Rubinius
|
||||||
|
Rubinius::AST::AsciiGrapher.new(self, Pattern).print
|
||||||
|
else
|
||||||
|
inspect
|
||||||
|
end
|
||||||
|
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def visit(visitor)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Pattern operators
|
||||||
|
|
||||||
|
def /(other)
|
||||||
|
Choice.new self, other
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: remove after core transform for :/ is removed
|
||||||
|
alias_method :divide, :/
|
||||||
|
|
||||||
|
def +(other)
|
||||||
|
Concatenation.new self, other
|
||||||
|
end
|
||||||
|
|
||||||
|
def -(other)
|
||||||
|
Difference.new self, other
|
||||||
|
end
|
||||||
|
|
||||||
|
def *(other)
|
||||||
|
Product.new self, other
|
||||||
|
end
|
||||||
|
|
||||||
|
def +@
|
||||||
|
If.new self
|
||||||
|
end
|
||||||
|
|
||||||
|
def -@
|
||||||
|
Unless.new self
|
||||||
|
end
|
||||||
|
|
||||||
|
def =~(string)
|
||||||
|
end
|
||||||
|
|
||||||
|
def match(string)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class UnaryOp < Pattern
|
||||||
|
attr_accessor :pattern
|
||||||
|
|
||||||
|
def initialize(pattern)
|
||||||
|
@pattern = PEG.pattern pattern
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
end
|
||||||
|
|
||||||
|
class BinaryOp < Pattern
|
||||||
|
attr_accessor :first
|
||||||
|
attr_accessor :second
|
||||||
|
|
||||||
|
def initialize(first, second)
|
||||||
|
@first = PEG.pattern first
|
||||||
|
@second = PEG.pattern second
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
end
|
||||||
|
|
||||||
|
class Grammar < Pattern
|
||||||
|
attr_accessor :start
|
||||||
|
|
||||||
|
def initialize(variable=nil)
|
||||||
|
if variable
|
||||||
|
@start = variable.name
|
||||||
|
set_variable variable.name, variable.pattern
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def set_variable(name, pattern)
|
||||||
|
variable = get_variable name
|
||||||
|
variable.pattern = PEG.pattern pattern
|
||||||
|
variable
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_variable(name)
|
||||||
|
if singleton_class.method_defined? name
|
||||||
|
variable = send name
|
||||||
|
else
|
||||||
|
vname = name.to_sym
|
||||||
|
variable = Variable.new vname
|
||||||
|
singleton_class.thunk_method vname, variable
|
||||||
|
end
|
||||||
|
|
||||||
|
variable
|
||||||
|
end
|
||||||
|
|
||||||
|
def method_missing(sym, *args)
|
||||||
|
name = sym.to_s
|
||||||
|
|
||||||
|
if name[-1] == ?= and args.size == 1
|
||||||
|
set_variable name[0..-2], args.first
|
||||||
|
else
|
||||||
|
get_variable name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def visit(visitor)
|
||||||
|
visitor.grammar self
|
||||||
|
end
|
||||||
|
|
||||||
|
def =~(string)
|
||||||
|
end
|
||||||
|
|
||||||
|
def match(string)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Always < Pattern
|
||||||
|
def visit(visitor)
|
||||||
|
visitor.always self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class AnyRange < Pattern
|
||||||
|
attr_accessor :range
|
||||||
|
|
||||||
|
def initialize(range)
|
||||||
|
@range = range
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def visit(visitor)
|
||||||
|
visitor.any_range self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Any < Pattern
|
||||||
|
attr_accessor :count
|
||||||
|
|
||||||
|
def initialize(count)
|
||||||
|
@count = count
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def visit(visitor)
|
||||||
|
visitor.any self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class CharacterRange < Pattern
|
||||||
|
attr_accessor :range
|
||||||
|
|
||||||
|
def initialize(range)
|
||||||
|
@range = range
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def visit(visitor)
|
||||||
|
visitor.character_range self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Character < Pattern
|
||||||
|
attr_accessor :string
|
||||||
|
|
||||||
|
def initialize(string)
|
||||||
|
@string = string
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def visit(visitor)
|
||||||
|
visitor.character self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Choice < BinaryOp
|
||||||
|
def visit(visitor)
|
||||||
|
visitor.choice self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Concatenation < BinaryOp
|
||||||
|
def visit(visitor)
|
||||||
|
visitor.concatenation self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Difference < BinaryOp
|
||||||
|
def visit(visitor)
|
||||||
|
visitor.difference self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Implements &patt in a PEG. The pattern succeeds if
|
||||||
|
# patt matches. Does not consume any input.
|
||||||
|
class If < UnaryOp
|
||||||
|
def visit(visitor)
|
||||||
|
visitor.if self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Never < Pattern
|
||||||
|
def visit(visitor)
|
||||||
|
visitor.never self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Product < Pattern
|
||||||
|
attr_accessor :pattern
|
||||||
|
attr_accessor :count
|
||||||
|
|
||||||
|
def initialize(pattern, count)
|
||||||
|
@pattern = PEG.pattern pattern
|
||||||
|
@count = Type.coerce_to(count, Integer, :to_int).abs
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def visit(visitor)
|
||||||
|
visitor.product self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Set < Pattern
|
||||||
|
attr_accessor :set
|
||||||
|
|
||||||
|
def initialize(set)
|
||||||
|
@set = set.uniq
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def visit(visitor)
|
||||||
|
visitor.set self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Implements !patt in a PEG. The pattern succeeds if
|
||||||
|
# patt does not match. Does not consume any input.
|
||||||
|
class Unless < UnaryOp
|
||||||
|
def visit(visitor)
|
||||||
|
visitor.unless self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Variable < Pattern
|
||||||
|
attr_accessor :name
|
||||||
|
attr_accessor :pattern
|
||||||
|
|
||||||
|
def initialize(name, pattern=nil)
|
||||||
|
@name = name.to_sym
|
||||||
|
@pattern = PEG.pattern pattern
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def visit(visitor)
|
||||||
|
visitor.variable self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Accepts a Ruby object representation of simple PEG "atoms" and returns a
|
||||||
|
# PEG AST node.
|
||||||
|
def pattern(spec)
|
||||||
|
case spec
|
||||||
|
when Array
|
||||||
|
Set.new spec
|
||||||
|
when Integer
|
||||||
|
node = Any.new spec.abs
|
||||||
|
node = If.new node if spec < 0
|
||||||
|
node
|
||||||
|
when String
|
||||||
|
Character.new spec
|
||||||
|
when Pattern
|
||||||
|
spec
|
||||||
|
when true
|
||||||
|
Always.new
|
||||||
|
when false
|
||||||
|
Never.new
|
||||||
|
when nil
|
||||||
|
nil
|
||||||
|
else
|
||||||
|
raise InvalidPatternError, "unknown specification type for Pattern: #{spec.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
module_function :pattern
|
||||||
|
|
||||||
|
def grammar(spec=nil, pattern=nil)
|
||||||
|
case spec
|
||||||
|
when Symbol
|
||||||
|
Grammar.new Variable.new(spec, pattern)
|
||||||
|
when Variable
|
||||||
|
Grammar.new spec
|
||||||
|
when Pattern
|
||||||
|
Grammar.new Variable.new(:S, spec)
|
||||||
|
when nil
|
||||||
|
Grammar.new
|
||||||
|
else
|
||||||
|
Grammar.new Variable.new(:S, pattern)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
module_function :grammar
|
||||||
|
|
||||||
|
def variable(name, pattern=nil)
|
||||||
|
PEG::Variable.new name, pattern
|
||||||
|
end
|
||||||
|
module_function :variable
|
||||||
|
|
||||||
|
def self.compile(pattern)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.peg
|
||||||
|
g = grammar = grammar(:grammar)
|
||||||
|
g.grammar = (g.nonterminal + "=" + g.sp + g.pattern) * 1
|
||||||
|
g.pattern = g.alternative + (pattern("/") + g.sp + g.alternative) * 0
|
||||||
|
g.alternative = ((+g.predicate + g.predicate + g.sp + g.suffix) /
|
||||||
|
(g.sp + g.suffix)) * 1
|
||||||
|
g.predicate = ["!&"]
|
||||||
|
g.suffix = g.primary + (pattern(["*+?"]) + g.sp) * 0
|
||||||
|
g.primary = (pattern("(") + g.sp + g.pattern + ")" + g.sp) / (pattern(1) + g.sp) /
|
||||||
|
g.literal / g.char_class / (g.nonterminal + -pattern("="))
|
||||||
|
g.literal = pattern("'") + (-pattern("'") + 1) * 0 + "'" + g.sp
|
||||||
|
g.char_class = (pattern("[") + (-pattern("]") +
|
||||||
|
((pattern(1) + "-" + 1) / 1)) * 0) + "]" + g.sp
|
||||||
|
g.nonterminal = (pattern("_") / ["a".."z"] / ["A".."Z"]) * 1 + g.sp
|
||||||
|
g.sp = pattern([" \t\n"]) * 0
|
||||||
|
|
||||||
|
grammar
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
468
gxg/core/pointer.rb
Normal file
468
gxg/core/pointer.rb
Normal file
|
|
@ -0,0 +1,468 @@
|
||||||
|
module Rubinius
|
||||||
|
module FFI
|
||||||
|
# We need to define this so we can include it. Since the file is
|
||||||
|
# auto-generated, we can't pull it in here but we don't want to break the
|
||||||
|
# alphabetized load order for random cases.
|
||||||
|
module PointerAccessors; end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Pointer is Rubinius's "fat" pointer class. It represents an actual
|
||||||
|
# pointer, in C language terms, to an address in memory. They're called
|
||||||
|
# fat pointers because the Pointer object is an wrapper around
|
||||||
|
# the actual pointer, the Rubinius runtime doesn't have direct access
|
||||||
|
# to the raw address.
|
||||||
|
#
|
||||||
|
# This class is used extensively in FFI usage to interface with various
|
||||||
|
# parts of the underlying system. It provides a number of operations
|
||||||
|
# for operating on the memory that is pointed to. These operations effectively
|
||||||
|
# give Rubinius the cast/read capabilities available in C, but using
|
||||||
|
# high level methods.
|
||||||
|
#
|
||||||
|
# MemoryPointer objects can be put in autorelease mode. In this mode,
|
||||||
|
# when the GC cleans up a MemoryPointer object, the memory it points
|
||||||
|
# to is passed to free(3), releasing the memory back to the OS.
|
||||||
|
#
|
||||||
|
# NOTE: MemoryPointer exposes direct, unmanaged operations on any
|
||||||
|
# memory. It therefore MUST be used carefully. Reading or writing to
|
||||||
|
# invalid address will cause bus errors and segmentation faults.
|
||||||
|
#
|
||||||
|
class Pointer
|
||||||
|
include PointerAccessors
|
||||||
|
|
||||||
|
def initialize(a1, a2=undefined)
|
||||||
|
if undefined.equal? a2
|
||||||
|
self.address = a1
|
||||||
|
else
|
||||||
|
@type = a1
|
||||||
|
self.address = a2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
# Don't have this print the data at the location. It can crash everything.
|
||||||
|
addr = address()
|
||||||
|
|
||||||
|
if addr < 0
|
||||||
|
sign = "-"
|
||||||
|
addr = -addr
|
||||||
|
else
|
||||||
|
sign = ""
|
||||||
|
end
|
||||||
|
|
||||||
|
"#<#{self.class.name} address=#{sign}0x#{addr.to_s(16)}>"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return the address pointed to as an Integer
|
||||||
|
def address
|
||||||
|
Rubinius.primitive :pointer_address
|
||||||
|
raise PrimitiveFailure, "FFI::Pointer#address primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :to_i, :address
|
||||||
|
|
||||||
|
# Set the address pointed to from an Integer
|
||||||
|
def address=(address)
|
||||||
|
Rubinius.primitive :pointer_set_address
|
||||||
|
raise PrimitiveFailure, "FFI::Pointer#address= primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def null?
|
||||||
|
address == 0x0
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add +value+ to the address pointed to and return a new Pointer
|
||||||
|
def +(value)
|
||||||
|
Rubinius.primitive :pointer_add
|
||||||
|
raise PrimitiveFailure, "FFI::Pointer#+ primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Indicates if +self+ and +other+ point to the same address
|
||||||
|
def ==(other)
|
||||||
|
return false unless other.kind_of? Pointer
|
||||||
|
return address == other.address
|
||||||
|
end
|
||||||
|
|
||||||
|
def network_order(start, size)
|
||||||
|
Rubinius.primitive :pointer_network_order
|
||||||
|
raise PrimitiveFailure, "FFI::Pointer#network_order primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Read +len+ bytes from the memory pointed to and return them as
|
||||||
|
# a String
|
||||||
|
def read_string_length(len)
|
||||||
|
Rubinius.primitive :pointer_read_string
|
||||||
|
raise PrimitiveFailure, "FFI::Pointer#read_string_length primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Read bytes from the memory pointed to until a NULL is seen, return
|
||||||
|
# the bytes as a String
|
||||||
|
def read_string_to_null
|
||||||
|
Rubinius.primitive :pointer_read_string_to_null
|
||||||
|
raise PrimitiveFailure, "FFI::Pointer#read_string_to_null primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Read bytes as a String from the memory pointed to
|
||||||
|
def read_string(len=nil)
|
||||||
|
if len
|
||||||
|
read_string_length(len)
|
||||||
|
else
|
||||||
|
read_string_to_null
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# FFI compat methods
|
||||||
|
def get_bytes(offset, length)
|
||||||
|
(self + offset).read_string_length(length)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Write String +str+ as bytes into the memory pointed to. Only
|
||||||
|
# write up to +len+ bytes.
|
||||||
|
def write_string_length(str, len)
|
||||||
|
Rubinius.primitive :pointer_write_string
|
||||||
|
raise PrimitiveFailure, "FFI::Pointer#write_string_length primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Write a String +str+ as bytes to the memory pointed to.
|
||||||
|
def write_string(str, len=nil)
|
||||||
|
len = str.bytesize unless len
|
||||||
|
|
||||||
|
write_string_length(str, len);
|
||||||
|
end
|
||||||
|
|
||||||
|
# Read a sequence of types +type+, length +length+, using method +reader+
|
||||||
|
def read_array_of_type(type, reader, length, signed=nil)
|
||||||
|
# If signed is not nil and is actually a boolean,
|
||||||
|
# then use that as an argument to the reader, which
|
||||||
|
# is then assumed to support signed reading.
|
||||||
|
args = []
|
||||||
|
args = [signed] if !signed.nil?
|
||||||
|
|
||||||
|
# Build up the array
|
||||||
|
ary = []
|
||||||
|
size = FFI.type_size(FFI.find_type type)
|
||||||
|
tmp = self
|
||||||
|
length.times {
|
||||||
|
ary << tmp.send(reader, *args)
|
||||||
|
tmp += size
|
||||||
|
}
|
||||||
|
ary
|
||||||
|
end
|
||||||
|
|
||||||
|
# Write a sequence of types +type+ using method +reader+ from +ary+
|
||||||
|
def write_array_of_type(type, writer, ary)
|
||||||
|
size = FFI.type_size(FFI.find_type type)
|
||||||
|
tmp = self
|
||||||
|
ary.each do |i|
|
||||||
|
tmp.send(writer, i)
|
||||||
|
tmp += size
|
||||||
|
end
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
# Read bytes from +offset+ from the memory pointed to as type +type+
|
||||||
|
def get_at_offset(offset, type)
|
||||||
|
Rubinius.primitive :pointer_get_at_offset
|
||||||
|
raise PrimitiveFailure, "FFI::Pointer#get_at_offset primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Write +val+ as type +type+ to bytes from +offset+
|
||||||
|
def set_at_offset(offset, type, val)
|
||||||
|
Rubinius.primitive :pointer_set_at_offset
|
||||||
|
raise PrimitiveFailure, "FFI::Pointer#set_at_offset primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Number of bytes taken up by a pointer.
|
||||||
|
def self.size
|
||||||
|
Rubinius::WORDSIZE / 8
|
||||||
|
end
|
||||||
|
|
||||||
|
# Primitive methods
|
||||||
|
def primitive_read_char(signed)
|
||||||
|
Rubinius.primitive :pointer_read_char
|
||||||
|
raise PrimitiveFailure, "FFI::Pointer#primitive_read_char primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def primitive_write_char(obj)
|
||||||
|
Rubinius.primitive :pointer_write_char
|
||||||
|
raise PrimitiveFailure, "FFI::Pointer#primitive_write_char primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def primitive_read_short(signed)
|
||||||
|
Rubinius.primitive :pointer_read_short
|
||||||
|
raise PrimitiveFailure, "FFI::Pointer#primitive_read_short primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def primitive_write_short(obj)
|
||||||
|
Rubinius.primitive :pointer_write_short
|
||||||
|
raise PrimitiveFailure, "FFI::Pointer#primitive_write_short primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def primitive_read_int(signed)
|
||||||
|
Rubinius.primitive :pointer_read_int
|
||||||
|
raise PrimitiveFailure, "FFI::Pointer#primitive_read_int primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def primitive_write_int(obj)
|
||||||
|
Rubinius.primitive :pointer_write_int
|
||||||
|
raise PrimitiveFailure, "FFI::Pointer#primitive_write_int primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def primitive_read_long(signed)
|
||||||
|
Rubinius.primitive :pointer_read_long
|
||||||
|
raise PrimitiveFailure, "FFI::Pointer#primitive_read_long primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def primitive_write_long(obj)
|
||||||
|
Rubinius.primitive :pointer_write_long
|
||||||
|
raise PrimitiveFailure, "FFI::Pointer#primitive_write_long primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def primitive_read_long_long(signed)
|
||||||
|
Rubinius.primitive :pointer_read_long_long
|
||||||
|
raise PrimitiveFailure, "FFI::Pointer#primitive_read_long_long primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def primitive_write_long_long(obj)
|
||||||
|
Rubinius.primitive :pointer_write_long_long
|
||||||
|
raise PrimitiveFailure, "FFI::Pointer#primitive_write_long_long primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def primitive_read_float
|
||||||
|
Rubinius.primitive :pointer_read_float
|
||||||
|
raise PrimitiveFailure, "FFI::Pointer#primitive_read_float primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def primitive_write_float(obj)
|
||||||
|
Rubinius.primitive :pointer_write_float
|
||||||
|
raise PrimitiveFailure, "FFI::Pointer#primitive_write_float primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def primitive_read_double
|
||||||
|
Rubinius.primitive :pointer_read_double
|
||||||
|
raise PrimitiveFailure, "FFI::Pointer#primitive_read_double primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def primitive_write_double(obj)
|
||||||
|
Rubinius.primitive :pointer_write_double
|
||||||
|
raise PrimitiveFailure, "FFI::Pointer#primitive_write_double primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def primitive_read_pointer
|
||||||
|
Rubinius.primitive :pointer_read_pointer
|
||||||
|
raise PrimitiveFailure, "FFI::Pointer#primitive_read_pointer primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def primitive_write_pointer(obj)
|
||||||
|
Rubinius.primitive :pointer_write_pointer
|
||||||
|
raise PrimitiveFailure, "FFI::Pointer#primitive_write_pointer primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# If +val+ is true, this Pointer object will call
|
||||||
|
# free() on it's address when it is garbage collected.
|
||||||
|
def autorelease=(val)
|
||||||
|
Rubinius.primitive :pointer_set_autorelease
|
||||||
|
raise PrimitiveFailure, "FFI::Pointer#autorelease= primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns true if autorelease is enabled, otherwise false.
|
||||||
|
def autorelease?
|
||||||
|
Rubinius.primitive :pointer_autorelease_p
|
||||||
|
raise PrimitiveFailure, "FFI::Pointer#pointer_autorelease_p primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
NULL = Pointer.new(0x0)
|
||||||
|
end
|
||||||
|
|
||||||
|
class MemoryPointer < Pointer
|
||||||
|
|
||||||
|
# call-seq:
|
||||||
|
# MemoryPointer.new(num) => MemoryPointer instance of <i>num</i> bytes
|
||||||
|
# MemoryPointer.new(sym) => MemoryPointer instance with number
|
||||||
|
# of bytes need by FFI type <i>sym</i>
|
||||||
|
# MemoryPointer.new(obj) => MemoryPointer instance with number
|
||||||
|
# of <i>obj.size</i> bytes
|
||||||
|
# MemoryPointer.new(sym, count) => MemoryPointer instance with number
|
||||||
|
# of bytes need by length-<i>count</i> array
|
||||||
|
# of FFI type <i>sym</i>
|
||||||
|
# MemoryPointer.new(obj, count) => MemoryPointer instance with number
|
||||||
|
# of bytes need by length-<i>count</i> array
|
||||||
|
# of <i>obj.size</i> bytes
|
||||||
|
# MemoryPointer.new(arg) { |p| ... }
|
||||||
|
#
|
||||||
|
# Both forms create a MemoryPointer instance. The number of bytes to
|
||||||
|
# allocate is either specified directly or by passing an FFI type, which
|
||||||
|
# specifies the number of bytes needed for that type.
|
||||||
|
#
|
||||||
|
# The form without a block returns the MemoryPointer instance. The form
|
||||||
|
# with a block yields the MemoryPointer instance and frees the memory
|
||||||
|
# when the block returns. The value returned is the value of the block.
|
||||||
|
#
|
||||||
|
def self.new(type, count=nil, clear=true)
|
||||||
|
if type.kind_of? Fixnum
|
||||||
|
size = type
|
||||||
|
elsif type.kind_of? Symbol
|
||||||
|
type = FFI.find_type type
|
||||||
|
size = FFI.type_size(type)
|
||||||
|
else
|
||||||
|
size = type.size
|
||||||
|
end
|
||||||
|
|
||||||
|
if count
|
||||||
|
total = size * count
|
||||||
|
else
|
||||||
|
total = size
|
||||||
|
end
|
||||||
|
|
||||||
|
return NULL if total < 0
|
||||||
|
|
||||||
|
ptr = malloc total
|
||||||
|
ptr.total = total
|
||||||
|
ptr.type_size = size
|
||||||
|
FFI::Platform::POSIX.memset ptr, 0, total if clear
|
||||||
|
|
||||||
|
if block_given?
|
||||||
|
begin
|
||||||
|
value = yield ptr
|
||||||
|
ensure
|
||||||
|
ptr.free
|
||||||
|
end
|
||||||
|
|
||||||
|
return value
|
||||||
|
else
|
||||||
|
ptr.autorelease = true
|
||||||
|
ptr
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.malloc(total)
|
||||||
|
Rubinius.primitive :pointer_malloc
|
||||||
|
raise PrimitiveFailure, "FFI::MemoryPointer.malloc primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.from_string(str)
|
||||||
|
ptr = new str.bytesize + 1
|
||||||
|
ptr.write_string str + "\0"
|
||||||
|
|
||||||
|
ptr
|
||||||
|
end
|
||||||
|
|
||||||
|
def copy
|
||||||
|
other = malloc total
|
||||||
|
other.total = total
|
||||||
|
other.type_size = type_size
|
||||||
|
FFI::Platform::POSIX.memcpy other, self, total
|
||||||
|
|
||||||
|
Rubinius.privately do
|
||||||
|
other.initialize_copy self
|
||||||
|
end
|
||||||
|
|
||||||
|
other
|
||||||
|
end
|
||||||
|
|
||||||
|
# Indicates how many bytes the chunk of memory that is pointed to takes up.
|
||||||
|
attr_accessor :total
|
||||||
|
|
||||||
|
# Indicates how many bytes the type that the pointer is cast as uses.
|
||||||
|
attr_accessor :type_size
|
||||||
|
|
||||||
|
# Access the MemoryPointer like a C array, accessing the +which+ number
|
||||||
|
# element in memory. The position of the element is calculate from
|
||||||
|
# +@type_size+ and +which+. A new MemoryPointer object is returned, which
|
||||||
|
# points to the address of the element.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# ptr = MemoryPointer.new(:int, 20)
|
||||||
|
# new_ptr = ptr[9]
|
||||||
|
#
|
||||||
|
# c-equiv:
|
||||||
|
# int *ptr = (int*)malloc(sizeof(int) * 20);
|
||||||
|
# int *new_ptr;
|
||||||
|
# new_ptr = &ptr[9];
|
||||||
|
#
|
||||||
|
def [](which)
|
||||||
|
raise ArgumentError, "unknown type size" unless @type_size
|
||||||
|
self + (which * @type_size)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Release the memory pointed to back to the OS.
|
||||||
|
def free
|
||||||
|
Rubinius.primitive :pointer_free
|
||||||
|
raise PrimitiveFailure, "FFI::MemoryPointer#free primitive failed"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class DynamicLibrary
|
||||||
|
class Symbol < Pointer
|
||||||
|
def initialize(library, ptr, name)
|
||||||
|
@library = library
|
||||||
|
@name = name
|
||||||
|
self.address = ptr.address
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
"#<FFI::Library::Symbol name=#{@name} address=#{address.to_s(16)}>"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Function < Pointer
|
||||||
|
def initialize(ret_type, arg_types, val=nil, options=nil, &block)
|
||||||
|
if block
|
||||||
|
if val or options
|
||||||
|
raise ArgumentError, "specify a block or a proc/address, not both"
|
||||||
|
end
|
||||||
|
|
||||||
|
val = block
|
||||||
|
end
|
||||||
|
|
||||||
|
args = arg_types.map { |x| FFI.find_type(x) }
|
||||||
|
ret = FFI.find_type(ret_type)
|
||||||
|
|
||||||
|
if val.kind_of? Pointer
|
||||||
|
@function = FFI.generate_function(val, :func, args, ret)
|
||||||
|
self.address = val.address
|
||||||
|
elsif val.respond_to? :call
|
||||||
|
@function, ptr = FFI.generate_trampoline(val, :func, args, ret)
|
||||||
|
self.address = ptr.address
|
||||||
|
else
|
||||||
|
raise ArgumentError, "value wasn't a FFI::Pointer and didn't respond to call"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Hook the created function into the method_table so that #call goes
|
||||||
|
# straight to it.
|
||||||
|
sc = Rubinius::Type.object_singleton_class(self)
|
||||||
|
Rubinius::VM.reset_method_cache sc, :call
|
||||||
|
sc.method_table.store :call, nil, @function, nil, 0, :public
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
attr_reader :function
|
||||||
|
|
||||||
|
# Hook this Function up to be an instance/class method +name+ on +mod+
|
||||||
|
def attach(mod, name)
|
||||||
|
unless mod.kind_of?(Module)
|
||||||
|
raise TypeError, "mod must be a Module"
|
||||||
|
end
|
||||||
|
|
||||||
|
name = name.to_sym
|
||||||
|
|
||||||
|
# Make it available as a method callable directly..
|
||||||
|
sc = Rubinius::Type.object_singleton_class(mod)
|
||||||
|
Rubinius::VM.reset_method_cache sc, name
|
||||||
|
sc.method_table.store name, nil, @function, nil, 0, :public
|
||||||
|
|
||||||
|
# and expose it as a private method for people who
|
||||||
|
# want to include this module.
|
||||||
|
Rubinius::VM.reset_method_cache mod, name
|
||||||
|
mod.method_table.store name, nil, @function, nil, 0, :public
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
838
gxg/core/pointer_accessors.rb
Normal file
838
gxg/core/pointer_accessors.rb
Normal file
|
|
@ -0,0 +1,838 @@
|
||||||
|
# This file is automatically generated. It should _not_ be
|
||||||
|
# modified manually. Due to the vast amount of very similar
|
||||||
|
# methods in this module, it is automatically generated.
|
||||||
|
|
||||||
|
module Rubinius
|
||||||
|
module FFI
|
||||||
|
##
|
||||||
|
# This module provides accessors for various different types. It
|
||||||
|
# provides methods for:
|
||||||
|
#
|
||||||
|
# * reading data
|
||||||
|
# * writing data
|
||||||
|
# * reading an array of some data
|
||||||
|
# * writing an array of some data
|
||||||
|
module PointerAccessors
|
||||||
|
#
|
||||||
|
# Name: int8
|
||||||
|
# Type: char
|
||||||
|
# Signed: true
|
||||||
|
#
|
||||||
|
|
||||||
|
def read_int8(signed=true)
|
||||||
|
primitive_read_char(signed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_int8(obj)
|
||||||
|
primitive_write_char(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_int8(offset)
|
||||||
|
(self + offset).read_int8
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_int8(offset, obj)
|
||||||
|
(self + offset).write_int8(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_array_of_int8(length, signed=true)
|
||||||
|
read_array_of_type(:char, :read_char, length, signed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_array_of_int8(ary)
|
||||||
|
write_array_of_type(:char, :write_char, ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_array_of_int8(offset, length, signed=true)
|
||||||
|
(self + offset).read_array_of_int8(length, signed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_array_of_int8(offset, ary)
|
||||||
|
(self + offset).write_array_of_int8(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_uint8
|
||||||
|
self.read_int8(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_uint8(obj)
|
||||||
|
self.write_int8(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_uint8(offset)
|
||||||
|
(self + offset).read_uint8
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_uint8(offset, obj)
|
||||||
|
(self + offset).write_uint8(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_array_of_uint8(length)
|
||||||
|
self.read_array_of_int8(length, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_array_of_uint8(ary)
|
||||||
|
self.write_array_of_int8(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_array_of_uint8(offset, length)
|
||||||
|
(self + offset).read_array_of_uint8(length)
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_array_of_uint8(offset, ary)
|
||||||
|
(self + offset).write_array_of_uint8(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Name: int16
|
||||||
|
# Type: short
|
||||||
|
# Signed: true
|
||||||
|
#
|
||||||
|
|
||||||
|
def read_int16(signed=true)
|
||||||
|
primitive_read_short(signed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_int16(obj)
|
||||||
|
primitive_write_short(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_int16(offset)
|
||||||
|
(self + offset).read_int16
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_int16(offset, obj)
|
||||||
|
(self + offset).write_int16(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_array_of_int16(length, signed=true)
|
||||||
|
read_array_of_type(:short, :read_short, length, signed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_array_of_int16(ary)
|
||||||
|
write_array_of_type(:short, :write_short, ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_array_of_int16(offset, length, signed=true)
|
||||||
|
(self + offset).read_array_of_int16(length, signed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_array_of_int16(offset, ary)
|
||||||
|
(self + offset).write_array_of_int16(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_uint16
|
||||||
|
self.read_int16(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_uint16(obj)
|
||||||
|
self.write_int16(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_uint16(offset)
|
||||||
|
(self + offset).read_uint16
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_uint16(offset, obj)
|
||||||
|
(self + offset).write_uint16(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_array_of_uint16(length)
|
||||||
|
self.read_array_of_int16(length, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_array_of_uint16(ary)
|
||||||
|
self.write_array_of_int16(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_array_of_uint16(offset, length)
|
||||||
|
(self + offset).read_array_of_uint16(length)
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_array_of_uint16(offset, ary)
|
||||||
|
(self + offset).write_array_of_uint16(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Name: int32
|
||||||
|
# Type: int
|
||||||
|
# Signed: true
|
||||||
|
#
|
||||||
|
|
||||||
|
def read_int32(signed=true)
|
||||||
|
primitive_read_int(signed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_int32(obj)
|
||||||
|
primitive_write_int(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_int32(offset)
|
||||||
|
(self + offset).read_int32
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_int32(offset, obj)
|
||||||
|
(self + offset).write_int32(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_array_of_int32(length, signed=true)
|
||||||
|
read_array_of_type(:int, :read_int, length, signed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_array_of_int32(ary)
|
||||||
|
write_array_of_type(:int, :write_int, ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_array_of_int32(offset, length, signed=true)
|
||||||
|
(self + offset).read_array_of_int32(length, signed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_array_of_int32(offset, ary)
|
||||||
|
(self + offset).write_array_of_int32(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_uint32
|
||||||
|
self.read_int32(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_uint32(obj)
|
||||||
|
self.write_int32(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_uint32(offset)
|
||||||
|
(self + offset).read_uint32
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_uint32(offset, obj)
|
||||||
|
(self + offset).write_uint32(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_array_of_uint32(length)
|
||||||
|
self.read_array_of_int32(length, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_array_of_uint32(ary)
|
||||||
|
self.write_array_of_int32(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_array_of_uint32(offset, length)
|
||||||
|
(self + offset).read_array_of_uint32(length)
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_array_of_uint32(offset, ary)
|
||||||
|
(self + offset).write_array_of_uint32(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Name: int64
|
||||||
|
# Type: long_long
|
||||||
|
# Signed: true
|
||||||
|
#
|
||||||
|
|
||||||
|
def read_int64(signed=true)
|
||||||
|
primitive_read_long_long(signed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_int64(obj)
|
||||||
|
primitive_write_long_long(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_int64(offset)
|
||||||
|
(self + offset).read_int64
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_int64(offset, obj)
|
||||||
|
(self + offset).write_int64(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_array_of_int64(length, signed=true)
|
||||||
|
read_array_of_type(:long_long, :read_long_long, length, signed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_array_of_int64(ary)
|
||||||
|
write_array_of_type(:long_long, :write_long_long, ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_array_of_int64(offset, length, signed=true)
|
||||||
|
(self + offset).read_array_of_int64(length, signed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_array_of_int64(offset, ary)
|
||||||
|
(self + offset).write_array_of_int64(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_uint64
|
||||||
|
self.read_int64(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_uint64(obj)
|
||||||
|
self.write_int64(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_uint64(offset)
|
||||||
|
(self + offset).read_uint64
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_uint64(offset, obj)
|
||||||
|
(self + offset).write_uint64(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_array_of_uint64(length)
|
||||||
|
self.read_array_of_int64(length, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_array_of_uint64(ary)
|
||||||
|
self.write_array_of_int64(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_array_of_uint64(offset, length)
|
||||||
|
(self + offset).read_array_of_uint64(length)
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_array_of_uint64(offset, ary)
|
||||||
|
(self + offset).write_array_of_uint64(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Name: long
|
||||||
|
# Type: long
|
||||||
|
# Signed: true
|
||||||
|
#
|
||||||
|
|
||||||
|
def read_long(signed=true)
|
||||||
|
primitive_read_long(signed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_long(obj)
|
||||||
|
primitive_write_long(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_long(offset)
|
||||||
|
(self + offset).read_long
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_long(offset, obj)
|
||||||
|
(self + offset).write_long(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_array_of_long(length, signed=true)
|
||||||
|
read_array_of_type(:long, :read_long, length, signed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_array_of_long(ary)
|
||||||
|
write_array_of_type(:long, :write_long, ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_array_of_long(offset, length, signed=true)
|
||||||
|
(self + offset).read_array_of_long(length, signed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_array_of_long(offset, ary)
|
||||||
|
(self + offset).write_array_of_long(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_ulong
|
||||||
|
self.read_long(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_ulong(obj)
|
||||||
|
self.write_long(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_ulong(offset)
|
||||||
|
(self + offset).read_ulong
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_ulong(offset, obj)
|
||||||
|
(self + offset).write_ulong(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_array_of_ulong(length)
|
||||||
|
self.read_array_of_long(length, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_array_of_ulong(ary)
|
||||||
|
self.write_array_of_long(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_array_of_ulong(offset, length)
|
||||||
|
(self + offset).read_array_of_ulong(length)
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_array_of_ulong(offset, ary)
|
||||||
|
(self + offset).write_array_of_ulong(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Name: float32
|
||||||
|
# Type: float
|
||||||
|
# Signed: false
|
||||||
|
#
|
||||||
|
|
||||||
|
def read_float32
|
||||||
|
primitive_read_float
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_float32(obj)
|
||||||
|
primitive_write_float(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_float32(offset)
|
||||||
|
(self + offset).read_float32
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_float32(offset, obj)
|
||||||
|
(self + offset).write_float32(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_array_of_float32(length)
|
||||||
|
read_array_of_type(:float, :read_float, length)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_array_of_float32(ary)
|
||||||
|
write_array_of_type(:float, :write_float, ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_array_of_float32(offset, length)
|
||||||
|
(self + offset).read_array_of_float32(length)
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_array_of_float32(offset, ary)
|
||||||
|
(self + offset).write_array_of_float32(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Name: float64
|
||||||
|
# Type: double
|
||||||
|
# Signed: false
|
||||||
|
#
|
||||||
|
|
||||||
|
def read_float64
|
||||||
|
primitive_read_double
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_float64(obj)
|
||||||
|
primitive_write_double(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_float64(offset)
|
||||||
|
(self + offset).read_float64
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_float64(offset, obj)
|
||||||
|
(self + offset).write_float64(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_array_of_float64(length)
|
||||||
|
read_array_of_type(:double, :read_double, length)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_array_of_float64(ary)
|
||||||
|
write_array_of_type(:double, :write_double, ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_array_of_float64(offset, length)
|
||||||
|
(self + offset).read_array_of_float64(length)
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_array_of_float64(offset, ary)
|
||||||
|
(self + offset).write_array_of_float64(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Name: float
|
||||||
|
# Type: float
|
||||||
|
# Signed: false
|
||||||
|
#
|
||||||
|
|
||||||
|
def read_float
|
||||||
|
primitive_read_float
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_float(obj)
|
||||||
|
primitive_write_float(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_float(offset)
|
||||||
|
(self + offset).read_float
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_float(offset, obj)
|
||||||
|
(self + offset).write_float(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_array_of_float(length)
|
||||||
|
read_array_of_type(:float, :read_float, length)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_array_of_float(ary)
|
||||||
|
write_array_of_type(:float, :write_float, ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_array_of_float(offset, length)
|
||||||
|
(self + offset).read_array_of_float(length)
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_array_of_float(offset, ary)
|
||||||
|
(self + offset).write_array_of_float(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Name: double
|
||||||
|
# Type: double
|
||||||
|
# Signed: false
|
||||||
|
#
|
||||||
|
|
||||||
|
def read_double
|
||||||
|
primitive_read_double
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_double(obj)
|
||||||
|
primitive_write_double(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_double(offset)
|
||||||
|
(self + offset).read_double
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_double(offset, obj)
|
||||||
|
(self + offset).write_double(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_array_of_double(length)
|
||||||
|
read_array_of_type(:double, :read_double, length)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_array_of_double(ary)
|
||||||
|
write_array_of_type(:double, :write_double, ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_array_of_double(offset, length)
|
||||||
|
(self + offset).read_array_of_double(length)
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_array_of_double(offset, ary)
|
||||||
|
(self + offset).write_array_of_double(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Name: char
|
||||||
|
# Type: char
|
||||||
|
# Signed: true
|
||||||
|
#
|
||||||
|
|
||||||
|
def read_char(signed=true)
|
||||||
|
primitive_read_char(signed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_char(obj)
|
||||||
|
primitive_write_char(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_char(offset)
|
||||||
|
(self + offset).read_char
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_char(offset, obj)
|
||||||
|
(self + offset).write_char(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_array_of_char(length, signed=true)
|
||||||
|
read_array_of_type(:char, :read_char, length, signed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_array_of_char(ary)
|
||||||
|
write_array_of_type(:char, :write_char, ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_array_of_char(offset, length, signed=true)
|
||||||
|
(self + offset).read_array_of_char(length, signed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_array_of_char(offset, ary)
|
||||||
|
(self + offset).write_array_of_char(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_uchar
|
||||||
|
self.read_char(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_uchar(obj)
|
||||||
|
self.write_char(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_uchar(offset)
|
||||||
|
(self + offset).read_uchar
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_uchar(offset, obj)
|
||||||
|
(self + offset).write_uchar(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_array_of_uchar(length)
|
||||||
|
self.read_array_of_char(length, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_array_of_uchar(ary)
|
||||||
|
self.write_array_of_char(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_array_of_uchar(offset, length)
|
||||||
|
(self + offset).read_array_of_uchar(length)
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_array_of_uchar(offset, ary)
|
||||||
|
(self + offset).write_array_of_uchar(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Name: short
|
||||||
|
# Type: short
|
||||||
|
# Signed: true
|
||||||
|
#
|
||||||
|
|
||||||
|
def read_short(signed=true)
|
||||||
|
primitive_read_short(signed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_short(obj)
|
||||||
|
primitive_write_short(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_short(offset)
|
||||||
|
(self + offset).read_short
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_short(offset, obj)
|
||||||
|
(self + offset).write_short(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_array_of_short(length, signed=true)
|
||||||
|
read_array_of_type(:short, :read_short, length, signed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_array_of_short(ary)
|
||||||
|
write_array_of_type(:short, :write_short, ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_array_of_short(offset, length, signed=true)
|
||||||
|
(self + offset).read_array_of_short(length, signed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_array_of_short(offset, ary)
|
||||||
|
(self + offset).write_array_of_short(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_ushort
|
||||||
|
self.read_short(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_ushort(obj)
|
||||||
|
self.write_short(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_ushort(offset)
|
||||||
|
(self + offset).read_ushort
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_ushort(offset, obj)
|
||||||
|
(self + offset).write_ushort(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_array_of_ushort(length)
|
||||||
|
self.read_array_of_short(length, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_array_of_ushort(ary)
|
||||||
|
self.write_array_of_short(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_array_of_ushort(offset, length)
|
||||||
|
(self + offset).read_array_of_ushort(length)
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_array_of_ushort(offset, ary)
|
||||||
|
(self + offset).write_array_of_ushort(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Name: int
|
||||||
|
# Type: int
|
||||||
|
# Signed: true
|
||||||
|
#
|
||||||
|
|
||||||
|
def read_int(signed=true)
|
||||||
|
primitive_read_int(signed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_int(obj)
|
||||||
|
primitive_write_int(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_int(offset)
|
||||||
|
(self + offset).read_int
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_int(offset, obj)
|
||||||
|
(self + offset).write_int(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_array_of_int(length, signed=true)
|
||||||
|
read_array_of_type(:int, :read_int, length, signed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_array_of_int(ary)
|
||||||
|
write_array_of_type(:int, :write_int, ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_array_of_int(offset, length, signed=true)
|
||||||
|
(self + offset).read_array_of_int(length, signed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_array_of_int(offset, ary)
|
||||||
|
(self + offset).write_array_of_int(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_uint
|
||||||
|
self.read_int(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_uint(obj)
|
||||||
|
self.write_int(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_uint(offset)
|
||||||
|
(self + offset).read_uint
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_uint(offset, obj)
|
||||||
|
(self + offset).write_uint(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_array_of_uint(length)
|
||||||
|
self.read_array_of_int(length, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_array_of_uint(ary)
|
||||||
|
self.write_array_of_int(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_array_of_uint(offset, length)
|
||||||
|
(self + offset).read_array_of_uint(length)
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_array_of_uint(offset, ary)
|
||||||
|
(self + offset).write_array_of_uint(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Name: long_long
|
||||||
|
# Type: long_long
|
||||||
|
# Signed: true
|
||||||
|
#
|
||||||
|
|
||||||
|
def read_long_long(signed=true)
|
||||||
|
primitive_read_long_long(signed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_long_long(obj)
|
||||||
|
primitive_write_long_long(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_long_long(offset)
|
||||||
|
(self + offset).read_long_long
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_long_long(offset, obj)
|
||||||
|
(self + offset).write_long_long(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_array_of_long_long(length, signed=true)
|
||||||
|
read_array_of_type(:long_long, :read_long_long, length, signed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_array_of_long_long(ary)
|
||||||
|
write_array_of_type(:long_long, :write_long_long, ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_array_of_long_long(offset, length, signed=true)
|
||||||
|
(self + offset).read_array_of_long_long(length, signed)
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_array_of_long_long(offset, ary)
|
||||||
|
(self + offset).write_array_of_long_long(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_ulong_long
|
||||||
|
self.read_long_long(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_ulong_long(obj)
|
||||||
|
self.write_long_long(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_ulong_long(offset)
|
||||||
|
(self + offset).read_ulong_long
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_ulong_long(offset, obj)
|
||||||
|
(self + offset).write_ulong_long(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_array_of_ulong_long(length)
|
||||||
|
self.read_array_of_long_long(length, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_array_of_ulong_long(ary)
|
||||||
|
self.write_array_of_long_long(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_array_of_ulong_long(offset, length)
|
||||||
|
(self + offset).read_array_of_ulong_long(length)
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_array_of_ulong_long(offset, ary)
|
||||||
|
(self + offset).write_array_of_ulong_long(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Name: pointer
|
||||||
|
# Type: pointer
|
||||||
|
# Signed: false
|
||||||
|
#
|
||||||
|
|
||||||
|
def read_pointer
|
||||||
|
primitive_read_pointer
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_pointer(obj)
|
||||||
|
primitive_write_pointer(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_pointer(offset)
|
||||||
|
(self + offset).read_pointer
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_pointer(offset, obj)
|
||||||
|
(self + offset).write_pointer(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_array_of_pointer(length)
|
||||||
|
read_array_of_type(:pointer, :read_pointer, length)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_array_of_pointer(ary)
|
||||||
|
write_array_of_type(:pointer, :write_pointer, ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_array_of_pointer(offset, length)
|
||||||
|
(self + offset).read_array_of_pointer(length)
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_array_of_pointer(offset, ary)
|
||||||
|
(self + offset).write_array_of_pointer(ary)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
14
gxg/core/prediction.rb
Normal file
14
gxg/core/prediction.rb
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
module Rubinius
|
||||||
|
class Prediction
|
||||||
|
def inspect
|
||||||
|
"#<#{self.class.name}:0x#{object_id.to_s(16)} valid=#{valid?}>"
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :to_s, :inspect
|
||||||
|
|
||||||
|
def valid?
|
||||||
|
Rubinius.primitive :prediction_valid_p
|
||||||
|
raise PrimitiveFailure, "Prediction#valid? primitive failed"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
276
gxg/core/proc.rb
Normal file
276
gxg/core/proc.rb
Normal file
|
|
@ -0,0 +1,276 @@
|
||||||
|
class Proc
|
||||||
|
def self.allocate
|
||||||
|
raise TypeError, "allocator undefined for Proc"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.__allocate__
|
||||||
|
Rubinius.primitive :proc_allocate
|
||||||
|
raise PrimitiveFailure, "Proc#allocate primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def call_prim(*args)
|
||||||
|
Rubinius.primitive :proc_call
|
||||||
|
raise PrimitiveFailure, "Proc#call primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(*args, &block)
|
||||||
|
if @ruby_method
|
||||||
|
@ruby_method.call(*args, &block)
|
||||||
|
else
|
||||||
|
call_prim(*args, &block)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def call_on_object(*args)
|
||||||
|
Rubinius.primitive :proc_call_on_object
|
||||||
|
raise PrimitiveFailure, "Proc#call_on_object primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def lambda_style!
|
||||||
|
@lambda = true
|
||||||
|
end
|
||||||
|
|
||||||
|
def lambda?
|
||||||
|
!!@lambda
|
||||||
|
end
|
||||||
|
end
|
||||||
|
class Proc
|
||||||
|
def self.__from_block__(env)
|
||||||
|
# The compiler must be fixed before this method can be removed.
|
||||||
|
Rubinius::Mirror::Proc.from_block self, env
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.new(*args)
|
||||||
|
env = nil
|
||||||
|
|
||||||
|
Rubinius.asm do
|
||||||
|
push_block
|
||||||
|
# assign a pushed block to the above local variable "env"
|
||||||
|
# Note that "env" is indexed at 1, not 0. "args" is indexed at 0.
|
||||||
|
set_local 1
|
||||||
|
end
|
||||||
|
|
||||||
|
unless env
|
||||||
|
# Support for ancient pre-block-pass style:
|
||||||
|
# def something; Proc.new; end
|
||||||
|
# something { a_block } => Proc instance
|
||||||
|
env = Rubinius::BlockEnvironment.of_sender
|
||||||
|
|
||||||
|
unless env
|
||||||
|
raise ArgumentError, "tried to create a Proc object without a block"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
block = Rubinius::Mirror::Proc.from_block self, env
|
||||||
|
|
||||||
|
if block.class != self
|
||||||
|
block = block.dup
|
||||||
|
Rubinius::Unsafe.set_class(block, self)
|
||||||
|
end
|
||||||
|
|
||||||
|
Rubinius.asm(block, args) do |b, a|
|
||||||
|
run b
|
||||||
|
run a
|
||||||
|
run b
|
||||||
|
send_with_splat :initialize, 0, true
|
||||||
|
end
|
||||||
|
|
||||||
|
return block
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_accessor :block
|
||||||
|
attr_accessor :bound_method
|
||||||
|
attr_accessor :ruby_method
|
||||||
|
|
||||||
|
def binding
|
||||||
|
bind = @block.to_binding
|
||||||
|
bind.proc_environment = @block
|
||||||
|
bind
|
||||||
|
end
|
||||||
|
|
||||||
|
def arity
|
||||||
|
if @ruby_method
|
||||||
|
return @ruby_method.arity
|
||||||
|
elsif @bound_method
|
||||||
|
arity = @bound_method.arity
|
||||||
|
return arity < 0 ? -1 : arity
|
||||||
|
end
|
||||||
|
|
||||||
|
arity = @block.arity
|
||||||
|
return arity if self.lambda? ||
|
||||||
|
@block.compiled_code.splat ||
|
||||||
|
@block.compiled_code.total_args == arity
|
||||||
|
|
||||||
|
-arity - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :===, :call
|
||||||
|
|
||||||
|
def curry(curried_arity = nil)
|
||||||
|
if lambda? && curried_arity
|
||||||
|
if arity >= 0 && curried_arity != arity
|
||||||
|
raise ArgumentError, "Wrong number of arguments (%i for %i)" % [
|
||||||
|
curried_arity,
|
||||||
|
arity
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
if arity < 0 && curried_arity < (-arity - 1)
|
||||||
|
raise ArgumentError, "Wrong number of arguments (%i for %i)" % [
|
||||||
|
curried_arity,
|
||||||
|
-arity - 1
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
args = []
|
||||||
|
|
||||||
|
m = Rubinius::Mirror.reflect self
|
||||||
|
f = m.curry self, [], arity
|
||||||
|
|
||||||
|
f.singleton_class.send(:define_method, :binding) do
|
||||||
|
raise ArgumentError, "cannot create binding from f proc"
|
||||||
|
end
|
||||||
|
|
||||||
|
f.singleton_class.thunk_method :parameters, [[:rest]]
|
||||||
|
f.singleton_class.thunk_method :source_location, nil
|
||||||
|
|
||||||
|
f
|
||||||
|
end
|
||||||
|
|
||||||
|
def source_location
|
||||||
|
if @ruby_method
|
||||||
|
@ruby_method.source_location
|
||||||
|
elsif @bound_method
|
||||||
|
if @bound_method.respond_to?(:source_location)
|
||||||
|
@bound_method.source_location
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
else
|
||||||
|
@block.source_location
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
file, line = source_location
|
||||||
|
|
||||||
|
l = " (lambda)" if lambda?
|
||||||
|
if file and line
|
||||||
|
"#<#{self.class}:0x#{self.object_id.to_s(16)}@#{file}:#{line}#{l}>"
|
||||||
|
else
|
||||||
|
"#<#{self.class}:0x#{self.object_id.to_s(16)}#{l}>"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :inspect, :to_s
|
||||||
|
|
||||||
|
def self.__from_method__(meth)
|
||||||
|
obj = __allocate__
|
||||||
|
obj.ruby_method = meth
|
||||||
|
obj.lambda_style!
|
||||||
|
|
||||||
|
return obj
|
||||||
|
end
|
||||||
|
|
||||||
|
def __yield__(*args, &block)
|
||||||
|
@ruby_method.call(*args, &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def parameters
|
||||||
|
if @ruby_method
|
||||||
|
return @ruby_method.parameters
|
||||||
|
elsif @bound_method
|
||||||
|
return @bound_method.parameters
|
||||||
|
end
|
||||||
|
|
||||||
|
code = @block.compiled_code
|
||||||
|
|
||||||
|
params = []
|
||||||
|
|
||||||
|
return params unless code.respond_to? :local_names
|
||||||
|
|
||||||
|
m = code.required_args - code.post_args
|
||||||
|
o = m + code.total_args - code.required_args
|
||||||
|
p = o + code.post_args
|
||||||
|
p += 1 if code.splat
|
||||||
|
|
||||||
|
required_status = self.lambda? ? :req : :opt
|
||||||
|
|
||||||
|
code.local_names.each_with_index do |name, i|
|
||||||
|
if i < m
|
||||||
|
params << [required_status, name]
|
||||||
|
elsif i < o
|
||||||
|
params << [:opt, name]
|
||||||
|
elsif code.splat == i
|
||||||
|
if name == :*
|
||||||
|
params << [:rest]
|
||||||
|
else
|
||||||
|
params << [:rest, name]
|
||||||
|
end
|
||||||
|
elsif i < p
|
||||||
|
params << [required_status, name]
|
||||||
|
elsif code.block_index == i
|
||||||
|
params << [:block, name]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
params
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def to_proc
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :[], :call
|
||||||
|
alias_method :yield, :call
|
||||||
|
|
||||||
|
def clone
|
||||||
|
copy = self.class.__allocate__
|
||||||
|
Rubinius.invoke_primitive :object_copy_object, copy, self
|
||||||
|
Rubinius.invoke_primitive :object_copy_singleton_class, copy, self
|
||||||
|
|
||||||
|
Rubinius.privately do
|
||||||
|
copy.initialize_copy self
|
||||||
|
end
|
||||||
|
|
||||||
|
copy.freeze if frozen?
|
||||||
|
copy
|
||||||
|
end
|
||||||
|
|
||||||
|
def dup
|
||||||
|
copy = self.class.__allocate__
|
||||||
|
Rubinius.invoke_primitive :object_copy_object, copy, self
|
||||||
|
|
||||||
|
Rubinius.privately do
|
||||||
|
copy.initialize_copy self
|
||||||
|
end
|
||||||
|
copy
|
||||||
|
end
|
||||||
|
|
||||||
|
def for_define_method(name, klass)
|
||||||
|
if @ruby_method
|
||||||
|
code, scope = @ruby_method.for_define_method(name, klass, self)
|
||||||
|
else
|
||||||
|
be = @block.dup
|
||||||
|
be.change_name name
|
||||||
|
|
||||||
|
duped_proc = self.dup
|
||||||
|
duped_proc.lambda_style!
|
||||||
|
|
||||||
|
code = Rubinius::BlockEnvironment::AsMethod.new(be)
|
||||||
|
scope = duped_proc.block.scope
|
||||||
|
end
|
||||||
|
|
||||||
|
[code, scope]
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.from_method(meth)
|
||||||
|
if meth.kind_of? Method
|
||||||
|
return __from_method__(meth)
|
||||||
|
else
|
||||||
|
raise ArgumentError, "tried to create a Proc object without a Method"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
37
gxg/core/proc_mirror.rb
Normal file
37
gxg/core/proc_mirror.rb
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
module Rubinius
|
||||||
|
class Mirror
|
||||||
|
class Proc < Mirror
|
||||||
|
def self.from_block(klass, env)
|
||||||
|
begin
|
||||||
|
return Rubinius.invoke_primitive :proc_from_env, env, klass
|
||||||
|
rescue Rubinius::Internal => exc
|
||||||
|
if Type.object_kind_of? env, BlockEnvironment
|
||||||
|
msg = "unable to create Proc from BlockEnvironment"
|
||||||
|
raise PrimitiveFailure, msg, exc
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
env.to_proc
|
||||||
|
rescue Exception
|
||||||
|
raise ArgumentError, "Unable to convert #{env.inspect} to a Proc"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def curry(executable, args, arity)
|
||||||
|
args.freeze
|
||||||
|
|
||||||
|
name = executable.lambda? ? :lambda : :proc
|
||||||
|
|
||||||
|
Proc.__send__(name) do |*a, &b|
|
||||||
|
all_args = args + a
|
||||||
|
if all_args.size < arity
|
||||||
|
curry executable, all_args, arity
|
||||||
|
else
|
||||||
|
executable[*all_args]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
794
gxg/core/process.rb
Normal file
794
gxg/core/process.rb
Normal file
|
|
@ -0,0 +1,794 @@
|
||||||
|
module Process
|
||||||
|
def self.time
|
||||||
|
Rubinius.primitive :vm_time
|
||||||
|
raise PrimitiveFailure, "Process.time primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.cpu_times
|
||||||
|
Rubinius.primitive :vm_times
|
||||||
|
raise PrimitiveFailure, "Process.cpu_times primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Sets the process title. Calling this method does not affect the value of
|
||||||
|
# `$0` as per MRI behaviour. This method returns the title set.
|
||||||
|
#
|
||||||
|
# @param [String] title
|
||||||
|
# @return [Title]
|
||||||
|
#
|
||||||
|
def self.setproctitle(title)
|
||||||
|
val = Rubinius::Type.coerce_to(title, String, :to_str)
|
||||||
|
|
||||||
|
Rubinius.invoke_primitive(:vm_set_process_title, val)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.setrlimit(resource, cur_limit, max_limit=undefined)
|
||||||
|
resource = coerce_rlimit_resource(resource)
|
||||||
|
cur_limit = Rubinius::Type.coerce_to cur_limit, Integer, :to_int
|
||||||
|
|
||||||
|
unless undefined.equal? max_limit
|
||||||
|
max_limit = Rubinius::Type.coerce_to max_limit, Integer, :to_int
|
||||||
|
end
|
||||||
|
|
||||||
|
rlimit = Rlimit.new
|
||||||
|
rlimit[:rlim_cur] = cur_limit
|
||||||
|
rlimit[:rlim_max] = undefined.equal?(max_limit) ? cur_limit : max_limit
|
||||||
|
|
||||||
|
ret = FFI::Platform::POSIX.setrlimit(resource, rlimit.pointer)
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.getrlimit(resource)
|
||||||
|
resource = coerce_rlimit_resource(resource)
|
||||||
|
|
||||||
|
lim_max = []
|
||||||
|
rlimit = Rlimit.new
|
||||||
|
ret = FFI::Platform::POSIX.getrlimit(resource, rlimit.pointer)
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
|
||||||
|
[rlimit[:rlim_cur], rlimit[:rlim_max]]
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.setsid
|
||||||
|
pgid = FFI::Platform::POSIX.setsid
|
||||||
|
Errno.handle if pgid == -1
|
||||||
|
pgid
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.fork
|
||||||
|
pid = Rubinius::Mirror::Process.fork
|
||||||
|
|
||||||
|
if block_given? and pid.nil?
|
||||||
|
begin
|
||||||
|
yield nil
|
||||||
|
status = 0
|
||||||
|
rescue SystemExit => e
|
||||||
|
status = e.status
|
||||||
|
rescue Exception => e
|
||||||
|
e.render "An exception occurred in a forked block"
|
||||||
|
status = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
until Rubinius::AtExit.empty?
|
||||||
|
begin
|
||||||
|
Rubinius::AtExit.shift.call
|
||||||
|
rescue SystemExit => e
|
||||||
|
status = e.status
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Do not use Kernel.exit. This raises a SystemExit exception, which
|
||||||
|
# will run ensure blocks. This is not what MRI does and causes bugs
|
||||||
|
# in programs. See issue http://github.com/rubinius/rubinius/issues#issue/289 for
|
||||||
|
# an example
|
||||||
|
|
||||||
|
Kernel.exit! status
|
||||||
|
end
|
||||||
|
pid
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.times
|
||||||
|
Struct::Tms.new(*cpu_times)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.kill(signal, *pids)
|
||||||
|
raise ArgumentError, "PID argument required" if pids.length == 0
|
||||||
|
|
||||||
|
use_process_group = false
|
||||||
|
signal = signal.to_s if signal.kind_of?(Symbol)
|
||||||
|
|
||||||
|
if signal.kind_of?(String)
|
||||||
|
if signal[0] == ?-
|
||||||
|
signal = signal[1..-1]
|
||||||
|
use_process_group = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if signal[0..2] == "SIG"
|
||||||
|
signal = signal[3..-1]
|
||||||
|
end
|
||||||
|
|
||||||
|
signal = Signal::Names[signal]
|
||||||
|
end
|
||||||
|
|
||||||
|
raise ArgumentError unless signal.kind_of? Fixnum
|
||||||
|
|
||||||
|
if signal < 0
|
||||||
|
signal = -signal
|
||||||
|
use_process_group = true
|
||||||
|
end
|
||||||
|
|
||||||
|
pids.each do |pid|
|
||||||
|
pid = Rubinius::Type.coerce_to_pid pid
|
||||||
|
|
||||||
|
pid = -pid if use_process_group
|
||||||
|
result = FFI::Platform::POSIX.kill(pid, signal)
|
||||||
|
|
||||||
|
Errno.handle if result == -1
|
||||||
|
end
|
||||||
|
|
||||||
|
return pids.length
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.abort(msg=nil)
|
||||||
|
if msg
|
||||||
|
msg = StringValue(msg)
|
||||||
|
$stderr.puts(msg)
|
||||||
|
end
|
||||||
|
raise SystemExit.new(1, msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.getpgid(pid)
|
||||||
|
pid = Rubinius::Type.coerce_to pid, Integer, :to_int
|
||||||
|
|
||||||
|
ret = FFI::Platform::POSIX.getpgid(pid)
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.setpgid(pid, int)
|
||||||
|
pid = Rubinius::Type.coerce_to pid, Integer, :to_int
|
||||||
|
int = Rubinius::Type.coerce_to int, Integer, :to_int
|
||||||
|
|
||||||
|
ret = FFI::Platform::POSIX.setpgid(pid, int)
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
|
||||||
|
@maxgroups = 32
|
||||||
|
class << self
|
||||||
|
attr_reader :maxgroups
|
||||||
|
def maxgroups=(m)
|
||||||
|
@maxgroups = m
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.setpgrp
|
||||||
|
setpgid(0, 0)
|
||||||
|
end
|
||||||
|
def self.getpgrp
|
||||||
|
ret = FFI::Platform::POSIX.getpgrp
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.pid
|
||||||
|
ret = FFI::Platform::POSIX.getpid
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.ppid
|
||||||
|
ret = FFI::Platform::POSIX.getppid
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.uid=(uid)
|
||||||
|
# the 4 rescue clauses below are needed
|
||||||
|
# until respond_to? can be used to query the implementation of methods attached via FFI
|
||||||
|
# atm respond_to returns true if a method is attached but not implemented on the platform
|
||||||
|
uid = Rubinius::Type.coerce_to uid, Integer, :to_int
|
||||||
|
begin
|
||||||
|
ret = FFI::Platform::POSIX.setresuid(uid, -1, -1)
|
||||||
|
rescue NotImplementedError
|
||||||
|
begin
|
||||||
|
ret = FFI::Platform::POSIX.setreuid(uid, -1)
|
||||||
|
rescue NotImplementedError
|
||||||
|
begin
|
||||||
|
ret = FFI::Platform::POSIX.setruid(uid)
|
||||||
|
rescue NotImplementedError
|
||||||
|
if Process.euid == uid
|
||||||
|
ret = FFI::Platform::POSIX.setuid(uid)
|
||||||
|
else
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
|
||||||
|
uid
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.gid=(gid)
|
||||||
|
gid = Rubinius::Type.coerce_to gid, Integer, :to_int
|
||||||
|
Process::Sys.setgid gid
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.euid=(uid)
|
||||||
|
# the 4 rescue clauses below are needed
|
||||||
|
# until respond_to? can be used to query the implementation of methods attached via FFI
|
||||||
|
# atm respond_to returns true if a method is attached but not implemented on the platform
|
||||||
|
uid = Rubinius::Type.coerce_to uid, Integer, :to_int
|
||||||
|
begin
|
||||||
|
ret = FFI::Platform::POSIX.setresuid(-1, uid, -1)
|
||||||
|
rescue NotImplementedError
|
||||||
|
begin
|
||||||
|
ret = FFI::Platform::POSIX.setreuid(-1, uid)
|
||||||
|
rescue NotImplementedError
|
||||||
|
begin
|
||||||
|
ret = FFI::Platform::POSIX.seteuid(uid)
|
||||||
|
rescue NotImplementedError
|
||||||
|
if Process.uid == uid
|
||||||
|
ret = FFI::Platform::POSIX.setuid(uid)
|
||||||
|
else
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
|
||||||
|
uid
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.egid=(gid)
|
||||||
|
gid = Rubinius::Type.coerce_to gid, Integer, :to_int
|
||||||
|
Process::Sys.setegid gid
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.uid
|
||||||
|
ret = FFI::Platform::POSIX.getuid
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.gid
|
||||||
|
ret = FFI::Platform::POSIX.getgid
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.euid
|
||||||
|
ret = FFI::Platform::POSIX.geteuid
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.egid
|
||||||
|
ret = FFI::Platform::POSIX.getegid
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.getpriority(kind, id)
|
||||||
|
kind = Rubinius::Type.coerce_to kind, Integer, :to_int
|
||||||
|
id = Rubinius::Type.coerce_to id, Integer, :to_int
|
||||||
|
|
||||||
|
FFI::Platform::POSIX.errno = 0
|
||||||
|
ret = FFI::Platform::POSIX.getpriority(kind, id)
|
||||||
|
Errno.handle
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.setpriority(kind, id, priority)
|
||||||
|
kind = Rubinius::Type.coerce_to kind, Integer, :to_int
|
||||||
|
id = Rubinius::Type.coerce_to id, Integer, :to_int
|
||||||
|
priority = Rubinius::Type.coerce_to priority, Integer, :to_int
|
||||||
|
|
||||||
|
ret = FFI::Platform::POSIX.setpriority(kind, id, priority)
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.groups
|
||||||
|
g = []
|
||||||
|
count = Rubinius::FFI::Platform::POSIX.getgroups(0, nil)
|
||||||
|
FFI::MemoryPointer.new(:int, count) { |p|
|
||||||
|
num_groups = FFI::Platform::POSIX.getgroups(count, p)
|
||||||
|
Errno.handle if num_groups == -1
|
||||||
|
g = p.read_array_of_int(num_groups)
|
||||||
|
}
|
||||||
|
g
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.groups=(g)
|
||||||
|
@maxgroups = g.length if g.length > @maxgroups
|
||||||
|
FFI::MemoryPointer.new(:int, @maxgroups) { |p|
|
||||||
|
p.write_array_of_int(g)
|
||||||
|
Errno.handle if FFI::Platform::POSIX.setgroups(g.length, p) == -1
|
||||||
|
}
|
||||||
|
g
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.initgroups(username, gid)
|
||||||
|
username = StringValue(username)
|
||||||
|
gid = Rubinius::Type.coerce_to gid, Integer, :to_int
|
||||||
|
|
||||||
|
if FFI::Platform::POSIX.initgroups(username, gid) == -1
|
||||||
|
Errno.handle
|
||||||
|
end
|
||||||
|
|
||||||
|
Process.groups
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Wait for the given process to exit.
|
||||||
|
#
|
||||||
|
# The pid may be the specific pid of some previously forked
|
||||||
|
# process, or -1 to indicate to watch for *any* child process
|
||||||
|
# exiting. Other options, such as process groups, may be available
|
||||||
|
# depending on the system.
|
||||||
|
#
|
||||||
|
# With no arguments the default is to block waiting for any
|
||||||
|
# child processes (pid -1.)
|
||||||
|
#
|
||||||
|
# The flag may be Process::WNOHANG, which indicates that
|
||||||
|
# the child should only be quickly checked. If it has not
|
||||||
|
# exited yet, nil is returned immediately instead.
|
||||||
|
#
|
||||||
|
# The return value is the exited pid or nil if Process::WNOHANG
|
||||||
|
# was used and the child had not yet exited.
|
||||||
|
#
|
||||||
|
# If the pid has exited, the global $? is set to a Process::Status
|
||||||
|
# object representing the exit status (and possibly other info) of
|
||||||
|
# the child.
|
||||||
|
#
|
||||||
|
# If there exists no such pid (e.g. never forked or already
|
||||||
|
# waited for), or no children at all, Errno::ECHILD is raised.
|
||||||
|
#
|
||||||
|
# TODO: Support other options such as WUNTRACED? --rue
|
||||||
|
#
|
||||||
|
def self.wait2(input_pid=-1, flags=nil)
|
||||||
|
input_pid = Rubinius::Type.coerce_to input_pid, Integer, :to_int
|
||||||
|
|
||||||
|
if flags and (flags & WNOHANG) == WNOHANG
|
||||||
|
value = Rubinius.invoke_primitive :vm_waitpid, input_pid, true
|
||||||
|
return if value.nil?
|
||||||
|
else
|
||||||
|
value = Rubinius.invoke_primitive :vm_waitpid, input_pid, false
|
||||||
|
end
|
||||||
|
|
||||||
|
if value == false
|
||||||
|
raise Errno::ECHILD, "No child process: #{input_pid}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# vm_waitpid returns a tuple when wait needs to communicate the pid that
|
||||||
|
# was actually detected as stopped (since wait can wait for all child
|
||||||
|
# pids, groups, etc)
|
||||||
|
status, termsig, stopsig, pid = value
|
||||||
|
|
||||||
|
status = Process::Status.new(pid, status, termsig, stopsig)
|
||||||
|
Rubinius::Mirror::Process.set_status_global status
|
||||||
|
|
||||||
|
[pid, status]
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Wait for all child processes.
|
||||||
|
#
|
||||||
|
# Blocks until all child processes have exited, and returns
|
||||||
|
# an Array of [pid, Process::Status] results, one for each
|
||||||
|
# child.
|
||||||
|
#
|
||||||
|
# Be mindful of the effects of creating new processes while
|
||||||
|
# .waitall has been called (usually in a different thread.)
|
||||||
|
# The .waitall call does not in any way check that it is only
|
||||||
|
# waiting for children that existed at the time it was called.
|
||||||
|
#
|
||||||
|
def self.waitall
|
||||||
|
statuses = []
|
||||||
|
|
||||||
|
begin
|
||||||
|
while true
|
||||||
|
statuses << Process.wait2
|
||||||
|
end
|
||||||
|
rescue Errno::ECHILD
|
||||||
|
end
|
||||||
|
|
||||||
|
statuses
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.wait(pid=-1, flags=nil)
|
||||||
|
pid, status = Process.wait2(pid, flags)
|
||||||
|
return pid
|
||||||
|
end
|
||||||
|
|
||||||
|
class << self
|
||||||
|
alias_method :waitpid, :wait
|
||||||
|
alias_method :waitpid2, :wait2
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.daemon(stay_in_dir=false, keep_stdio_open=false)
|
||||||
|
# Do not run at_exit handlers in the parent
|
||||||
|
exit!(0) if fork
|
||||||
|
|
||||||
|
Process.setsid
|
||||||
|
|
||||||
|
exit!(0) if fork
|
||||||
|
|
||||||
|
Dir.chdir("/") unless stay_in_dir
|
||||||
|
|
||||||
|
unless keep_stdio_open
|
||||||
|
io = File.open "/dev/null", File::RDWR, 0
|
||||||
|
$stdin.reopen io
|
||||||
|
$stdout.reopen io
|
||||||
|
$stderr.reopen io
|
||||||
|
end
|
||||||
|
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.exec(*args)
|
||||||
|
Rubinius::Mirror::Process.exec(*args)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.spawn(*args)
|
||||||
|
Rubinius::Mirror::Process.spawn(*args)
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: Should an error be raised on ECHILD? --rue
|
||||||
|
#
|
||||||
|
# TODO: This operates on the assumption that waiting on
|
||||||
|
# the event consumes very little resources. If this
|
||||||
|
# is not the case, the check should be made WNOHANG
|
||||||
|
# and called periodically.
|
||||||
|
#
|
||||||
|
def self.detach(pid)
|
||||||
|
raise ArgumentError, "Only positive pids may be detached" unless pid > 0
|
||||||
|
|
||||||
|
thread = Thread.new { Process.wait pid; $? }
|
||||||
|
thread[:pid] = pid
|
||||||
|
def thread.pid; self[:pid] end
|
||||||
|
|
||||||
|
thread
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.coerce_rlimit_resource(resource)
|
||||||
|
case resource
|
||||||
|
when Integer
|
||||||
|
return resource
|
||||||
|
when Symbol, String
|
||||||
|
# do nothing
|
||||||
|
else
|
||||||
|
unless r = Rubinius::Type.check_convert_type(resource, String, :to_str)
|
||||||
|
return Rubinius::Type.coerce_to resource, Integer, :to_int
|
||||||
|
end
|
||||||
|
|
||||||
|
resource = r
|
||||||
|
end
|
||||||
|
|
||||||
|
constant = "RLIMIT_#{resource}"
|
||||||
|
unless const_defined? constant
|
||||||
|
raise ArgumentError, "invalid resource name: #{constant}"
|
||||||
|
end
|
||||||
|
const_get constant
|
||||||
|
end
|
||||||
|
|
||||||
|
class << self
|
||||||
|
private :coerce_rlimit_resource
|
||||||
|
end
|
||||||
|
|
||||||
|
#--
|
||||||
|
# TODO: Most of the fields aren't implemented yet.
|
||||||
|
# TODO: Also, these objects should only need to be constructed by
|
||||||
|
# Process.wait and family.
|
||||||
|
#++
|
||||||
|
|
||||||
|
class Status
|
||||||
|
|
||||||
|
attr_reader :termsig
|
||||||
|
attr_reader :stopsig
|
||||||
|
|
||||||
|
def initialize(pid=nil, status=nil, termsig=nil, stopsig=nil)
|
||||||
|
@pid = pid
|
||||||
|
@status = status
|
||||||
|
@termsig = termsig
|
||||||
|
@stopsig = stopsig
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def exitstatus
|
||||||
|
@status
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_i
|
||||||
|
@status
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
@status.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def &(num)
|
||||||
|
@status & num
|
||||||
|
end
|
||||||
|
|
||||||
|
def ==(other)
|
||||||
|
other = other.to_i if other.kind_of? Process::Status
|
||||||
|
@status == other
|
||||||
|
end
|
||||||
|
|
||||||
|
def >>(num)
|
||||||
|
@status >> num
|
||||||
|
end
|
||||||
|
|
||||||
|
def coredump?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def exited?
|
||||||
|
@status != nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def pid
|
||||||
|
@pid
|
||||||
|
end
|
||||||
|
|
||||||
|
def signaled?
|
||||||
|
@termsig != nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def stopped?
|
||||||
|
@stopsig != nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def success?
|
||||||
|
if exited?
|
||||||
|
@status == 0
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module Sys
|
||||||
|
class << self
|
||||||
|
def getegid
|
||||||
|
ret = FFI::Platform::POSIX.getegid
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
|
||||||
|
def geteuid
|
||||||
|
ret = FFI::Platform::POSIX.geteuid
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
|
||||||
|
def getgid
|
||||||
|
ret = FFI::Platform::POSIX.getgid
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
|
||||||
|
def getuid
|
||||||
|
ret = FFI::Platform::POSIX.getuid
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
|
||||||
|
def issetugid
|
||||||
|
raise "not implemented"
|
||||||
|
end
|
||||||
|
|
||||||
|
def setgid(gid)
|
||||||
|
gid = Rubinius::Type.coerce_to gid, Integer, :to_int
|
||||||
|
|
||||||
|
ret = FFI::Platform::POSIX.setgid gid
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def setuid(uid)
|
||||||
|
uid = Rubinius::Type.coerce_to uid, Integer, :to_int
|
||||||
|
|
||||||
|
ret = FFI::Platform::POSIX.setuid uid
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def setegid(egid)
|
||||||
|
egid = Rubinius::Type.coerce_to egid, Integer, :to_int
|
||||||
|
|
||||||
|
ret = FFI::Platform::POSIX.setegid egid
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def seteuid(euid)
|
||||||
|
euid = Rubinius::Type.coerce_to euid, Integer, :to_int
|
||||||
|
|
||||||
|
ret = FFI::Platform::POSIX.seteuid euid
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def setrgid(rgid)
|
||||||
|
setregid(rgid, -1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def setruid(ruid)
|
||||||
|
setreuid(ruid, -1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def setregid(rid, eid)
|
||||||
|
rid = Rubinius::Type.coerce_to rid, Integer, :to_int
|
||||||
|
eid = Rubinius::Type.coerce_to eid, Integer, :to_int
|
||||||
|
|
||||||
|
ret = FFI::Platform::POSIX.setregid rid, eid
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def setreuid(rid, eid)
|
||||||
|
rid = Rubinius::Type.coerce_to rid, Integer, :to_int
|
||||||
|
eid = Rubinius::Type.coerce_to eid, Integer, :to_int
|
||||||
|
|
||||||
|
ret = FFI::Platform::POSIX.setreuid rid, eid
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def setresgid(rid, eid, sid)
|
||||||
|
rid = Rubinius::Type.coerce_to rid, Integer, :to_int
|
||||||
|
eid = Rubinius::Type.coerce_to eid, Integer, :to_int
|
||||||
|
sid = Rubinius::Type.coerce_to sid, Integer, :to_int
|
||||||
|
|
||||||
|
ret = FFI::Platform::POSIX.setresgid rid, eid, sid
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def setresuid(rid, eid, sid)
|
||||||
|
rid = Rubinius::Type.coerce_to rid, Integer, :to_int
|
||||||
|
eid = Rubinius::Type.coerce_to eid, Integer, :to_int
|
||||||
|
sid = Rubinius::Type.coerce_to sid, Integer, :to_int
|
||||||
|
|
||||||
|
ret = FFI::Platform::POSIX.setresuid rid, eid, sid
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module UID
|
||||||
|
class << self
|
||||||
|
def change_privilege(uid)
|
||||||
|
uid = Rubinius::Type.coerce_to uid, Integer, :to_int
|
||||||
|
|
||||||
|
ret = FFI::Platform::POSIX.setreuid(uid, uid)
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
uid
|
||||||
|
end
|
||||||
|
|
||||||
|
def eid
|
||||||
|
ret = FFI::Platform::POSIX.geteuid
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
|
||||||
|
def eid=(uid)
|
||||||
|
uid = Rubinius::Type.coerce_to uid, Integer, :to_int
|
||||||
|
|
||||||
|
ret = FFI::Platform::POSIX.seteuid(uid)
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
uid
|
||||||
|
end
|
||||||
|
alias_method :grant_privilege, :eid=
|
||||||
|
|
||||||
|
def re_exchange
|
||||||
|
real = FFI::Platform::POSIX.getuid
|
||||||
|
Errno.handle if real == -1
|
||||||
|
eff = FFI::Platform::POSIX.geteuid
|
||||||
|
Errno.handle if eff == -1
|
||||||
|
ret = FFI::Platform::POSIX.setreuid(eff, real)
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
eff
|
||||||
|
end
|
||||||
|
|
||||||
|
def re_exchangeable?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def rid
|
||||||
|
ret = FFI::Platform::POSIX.getuid
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
|
||||||
|
def sid_available?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def switch
|
||||||
|
eff = re_exchange
|
||||||
|
if block_given?
|
||||||
|
ret = yield
|
||||||
|
re_exchange
|
||||||
|
return ret
|
||||||
|
else
|
||||||
|
return eff
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module GID
|
||||||
|
class << self
|
||||||
|
def change_privilege(gid)
|
||||||
|
gid = Rubinius::Type.coerce_to gid, Integer, :to_int
|
||||||
|
|
||||||
|
ret = FFI::Platform::POSIX.setregid(gid, gid)
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
gid
|
||||||
|
end
|
||||||
|
|
||||||
|
def eid
|
||||||
|
ret = FFI::Platform::POSIX.getegid
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
|
||||||
|
def eid=(gid)
|
||||||
|
gid = Rubinius::Type.coerce_to gid, Integer, :to_int
|
||||||
|
|
||||||
|
ret = FFI::Platform::POSIX.setegid(gid)
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
gid
|
||||||
|
end
|
||||||
|
alias_method :grant_privilege, :eid=
|
||||||
|
|
||||||
|
def re_exchange
|
||||||
|
real = FFI::Platform::POSIX.getgid
|
||||||
|
Errno.handle if real == -1
|
||||||
|
eff = FFI::Platform::POSIX.getegid
|
||||||
|
Errno.handle if eff == -1
|
||||||
|
ret = FFI::Platform::POSIX.setregid(eff, real)
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
eff
|
||||||
|
end
|
||||||
|
|
||||||
|
def re_exchangeable?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def rid
|
||||||
|
ret = FFI::Platform::POSIX.getgid
|
||||||
|
Errno.handle if ret == -1
|
||||||
|
ret
|
||||||
|
end
|
||||||
|
|
||||||
|
def sid_available?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def switch
|
||||||
|
eff = re_exchange
|
||||||
|
if block_given?
|
||||||
|
ret = yield
|
||||||
|
re_exchange
|
||||||
|
return ret
|
||||||
|
else
|
||||||
|
return eff
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
256
gxg/core/process_mirror.rb
Normal file
256
gxg/core/process_mirror.rb
Normal file
|
|
@ -0,0 +1,256 @@
|
||||||
|
module Rubinius
|
||||||
|
class Mirror
|
||||||
|
module Process
|
||||||
|
def self.set_status_global(status)
|
||||||
|
::Thread.current[:$?] = status
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.fork
|
||||||
|
Rubinius.primitive :vm_fork
|
||||||
|
raise PrimitiveFailure, "Rubinius::Mirror::Process.fork primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.exec(*args)
|
||||||
|
exe = Execute.new(*args)
|
||||||
|
exe.spawn_setup
|
||||||
|
exe.exec exe.command, exe.argv
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.spawn(*args)
|
||||||
|
exe = Execute.new(*args)
|
||||||
|
|
||||||
|
begin
|
||||||
|
pid = exe.spawn exe.options, exe.command, exe.argv
|
||||||
|
rescue SystemCallError => error
|
||||||
|
set_status_global ::Process::Status.new(pid, 127)
|
||||||
|
raise error
|
||||||
|
end
|
||||||
|
|
||||||
|
pid
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.backtick(str)
|
||||||
|
Rubinius.primitive :vm_backtick
|
||||||
|
raise PrimitiveFailure, "Rubinius::Mirror::Process.backtick primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
class Execute
|
||||||
|
attr_reader :command
|
||||||
|
attr_reader :argv
|
||||||
|
attr_reader :options
|
||||||
|
|
||||||
|
# Turns the various varargs incantations supported by Process.spawn into a
|
||||||
|
# [env, prog, argv, redirects, options] tuple.
|
||||||
|
#
|
||||||
|
# The following method signature is supported:
|
||||||
|
# Process.spawn([env], command, ..., [options])
|
||||||
|
#
|
||||||
|
# The env and options hashes are optional. The command may be a variable
|
||||||
|
# number of strings or an Array full of strings that make up the new process's
|
||||||
|
# argv.
|
||||||
|
#
|
||||||
|
# Assigns @environment, @command, @argv, @redirects, @options. All
|
||||||
|
# elements are guaranteed to be non-nil. When no env or options are
|
||||||
|
# given, empty hashes are returned.
|
||||||
|
def initialize(env_or_cmd, *args)
|
||||||
|
if options = Rubinius::Type.try_convert(args.last, ::Hash, :to_hash)
|
||||||
|
args.pop
|
||||||
|
end
|
||||||
|
|
||||||
|
if env = Rubinius::Type.try_convert(env_or_cmd, ::Hash, :to_hash)
|
||||||
|
unless command = args.shift
|
||||||
|
raise ArgumentError, "command argument expected"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
command = env_or_cmd
|
||||||
|
end
|
||||||
|
|
||||||
|
if args.empty? and
|
||||||
|
cmd = Rubinius::Type.try_convert(command, ::String, :to_str)
|
||||||
|
raise Errno::ENOENT if cmd.empty?
|
||||||
|
|
||||||
|
@command = cmd
|
||||||
|
@argv = []
|
||||||
|
else
|
||||||
|
if cmd = Rubinius::Type.try_convert(command, ::Array, :to_ary)
|
||||||
|
raise ArgumentError, "wrong first argument" unless cmd.size == 2
|
||||||
|
command = StringValue(cmd[0])
|
||||||
|
name = StringValue(cmd[1])
|
||||||
|
else
|
||||||
|
name = command = StringValue(command)
|
||||||
|
end
|
||||||
|
|
||||||
|
argv = [name]
|
||||||
|
args.each { |arg| argv << StringValue(arg) }
|
||||||
|
|
||||||
|
@command = command
|
||||||
|
@argv = argv
|
||||||
|
end
|
||||||
|
|
||||||
|
@options = Rubinius::LookupTable.new if options or env
|
||||||
|
|
||||||
|
if options
|
||||||
|
options.each do |key, value|
|
||||||
|
case key
|
||||||
|
when ::IO, ::Fixnum, :in, :out, :err
|
||||||
|
from = convert_io_fd key
|
||||||
|
to = convert_to_fd value, from
|
||||||
|
redirect @options, from, to
|
||||||
|
when ::Array
|
||||||
|
from = convert_io_fd key.first
|
||||||
|
to = convert_to_fd value, from
|
||||||
|
key.each { |k| redirect @options, convert_io_fd(k), to }
|
||||||
|
when :unsetenv_others
|
||||||
|
if value
|
||||||
|
array = @options[:env] = []
|
||||||
|
ENV.each_key { |k| array << convert_env_key(k) << nil }
|
||||||
|
end
|
||||||
|
when :pgroup
|
||||||
|
if value == true
|
||||||
|
value = 0
|
||||||
|
elsif value
|
||||||
|
value = Rubinius::Type.coerce_to value, ::Integer, :to_int
|
||||||
|
raise ArgumentError, "negative process group ID : #{value}" if value < 0
|
||||||
|
end
|
||||||
|
@options[key] = value
|
||||||
|
when :chdir
|
||||||
|
@options[key] = Rubinius::Type.coerce_to_path(value)
|
||||||
|
when :umask
|
||||||
|
@options[key] = value
|
||||||
|
when :close_others
|
||||||
|
@options[key] = value if value
|
||||||
|
else
|
||||||
|
raise ArgumentError, "unknown exec option: #{key.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if env
|
||||||
|
array = (@options[:env] ||= [])
|
||||||
|
|
||||||
|
env.each do |key, value|
|
||||||
|
array << convert_env_key(key) << convert_env_value(value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def redirect(options, from, to)
|
||||||
|
case to
|
||||||
|
when ::Fixnum
|
||||||
|
map = (options[:redirect_fd] ||= [])
|
||||||
|
map << from << to
|
||||||
|
when ::Array
|
||||||
|
map = (options[:assign_fd] ||= [])
|
||||||
|
map << from
|
||||||
|
map.concat to
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def convert_io_fd(obj)
|
||||||
|
case obj
|
||||||
|
when ::Fixnum
|
||||||
|
obj
|
||||||
|
when :in
|
||||||
|
0
|
||||||
|
when :out
|
||||||
|
1
|
||||||
|
when :err
|
||||||
|
2
|
||||||
|
when ::IO
|
||||||
|
obj.fileno
|
||||||
|
else
|
||||||
|
raise ArgementError, "wrong exec option: #{obj.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def convert_to_fd(obj, target)
|
||||||
|
case obj
|
||||||
|
when ::Fixnum
|
||||||
|
obj
|
||||||
|
when :in
|
||||||
|
0
|
||||||
|
when :out
|
||||||
|
1
|
||||||
|
when :err
|
||||||
|
2
|
||||||
|
when :close
|
||||||
|
nil
|
||||||
|
when ::IO
|
||||||
|
obj.fileno
|
||||||
|
when ::String
|
||||||
|
[obj, default_mode(target), 0644]
|
||||||
|
when ::Array
|
||||||
|
case obj.size
|
||||||
|
when 1
|
||||||
|
[obj[0], File::RDONLY, 0644]
|
||||||
|
when 2
|
||||||
|
if obj[0] == :child
|
||||||
|
convert_to_fd obj[1], target
|
||||||
|
else
|
||||||
|
[obj[0], convert_file_mode(obj[1]), 0644]
|
||||||
|
end
|
||||||
|
when 3
|
||||||
|
[obj[0], convert_file_mode(obj[1]), obj[2]]
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise ArgumentError, "wrong exec redirect: #{obj.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def default_mode(target)
|
||||||
|
if target == 1 or target == 2
|
||||||
|
OFLAGS["w"]
|
||||||
|
else
|
||||||
|
OFLAGS["r"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def convert_file_mode(obj)
|
||||||
|
case obj
|
||||||
|
when ::Fixnum
|
||||||
|
obj
|
||||||
|
when ::String
|
||||||
|
OFLAGS[obj]
|
||||||
|
when nil
|
||||||
|
OFLAG["r"]
|
||||||
|
else
|
||||||
|
Rubinius::Type.coerce_to obj, Integer, :to_int
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def convert_env_key(key)
|
||||||
|
key = Rubinius::Type.check_null_safe(StringValue(key))
|
||||||
|
|
||||||
|
if key.include?("=")
|
||||||
|
raise ArgumentError, "environment name contains a equal : #{key}"
|
||||||
|
end
|
||||||
|
|
||||||
|
key
|
||||||
|
end
|
||||||
|
|
||||||
|
def convert_env_value(value)
|
||||||
|
return if value.nil?
|
||||||
|
Rubinius::Type.check_null_safe(StringValue(value))
|
||||||
|
end
|
||||||
|
|
||||||
|
def spawn_setup
|
||||||
|
Rubinius.invoke_primitive :vm_spawn_setup, @options
|
||||||
|
end
|
||||||
|
|
||||||
|
def spawn(exe, command, args)
|
||||||
|
Rubinius.primitive :vm_spawn
|
||||||
|
raise PrimitiveFailure,
|
||||||
|
"Rubinius::Mirror::Process::Execute#spawn primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def exec(command, args)
|
||||||
|
Rubinius.primitive :vm_exec
|
||||||
|
raise PrimitiveFailure,
|
||||||
|
"Rubinius::Mirror::Process::Execute#exec primitive failed"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
188
gxg/core/random.rb
Normal file
188
gxg/core/random.rb
Normal file
|
|
@ -0,0 +1,188 @@
|
||||||
|
class Rubinius::Randomizer
|
||||||
|
def self.allocate
|
||||||
|
Rubinius.primitive :randomizer_allocate
|
||||||
|
raise PrimitiveFailure, "Randomizer.allocate primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
self.seed = generate_seed
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
attr_reader :seed
|
||||||
|
def seed=(new_seed)
|
||||||
|
set_seed new_seed
|
||||||
|
@seed = new_seed
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_seed(new_seed)
|
||||||
|
Rubinius.primitive :randomizer_seed
|
||||||
|
raise PrimitiveFailure, "Randomizer#seed primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def swap_seed(new_seed)
|
||||||
|
old_seed = self.seed
|
||||||
|
self.seed = new_seed
|
||||||
|
old_seed
|
||||||
|
end
|
||||||
|
|
||||||
|
def random(limit)
|
||||||
|
if undefined.equal?(limit)
|
||||||
|
random_float
|
||||||
|
else
|
||||||
|
if limit.kind_of?(Range)
|
||||||
|
if time_value?(limit.min)
|
||||||
|
random_time_range(limit)
|
||||||
|
else
|
||||||
|
random_range(limit)
|
||||||
|
end
|
||||||
|
elsif limit.kind_of?(Float)
|
||||||
|
raise ArgumentError, "invalid argument - #{limit}" if limit <= 0
|
||||||
|
random_float * limit
|
||||||
|
elsif limit.is_a?(Integer)
|
||||||
|
raise ArgumentError, "invalid argument - #{limit}" if limit <= 0
|
||||||
|
random_integer(limit - 1)
|
||||||
|
elsif limit.respond_to?(:to_f)
|
||||||
|
raise ArgumentError, "invalid argument - #{limit}" if limit <= 0
|
||||||
|
random_float * limit
|
||||||
|
else
|
||||||
|
limit_int = Rubinius::Type.coerce_to limit, Integer, :to_int
|
||||||
|
raise ArgumentError, "invalid argument - #{limit}" if limit_int <= 0
|
||||||
|
|
||||||
|
random_integer(limit_int - 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generate a random Float, in the range 0...1.0
|
||||||
|
def random_float
|
||||||
|
Rubinius.primitive :randomizer_rand_float
|
||||||
|
raise PrimitiveFailure, "Randomizer#rand_float primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generate a random Integer, in the range 0...limit
|
||||||
|
def random_integer(limit)
|
||||||
|
Rubinius.primitive :randomizer_rand_int
|
||||||
|
raise PrimitiveFailure, "Randomizer#rand_int primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
def random_range(limit)
|
||||||
|
return nil if limit.first.nil? || limit.last.nil?
|
||||||
|
|
||||||
|
min, max = limit.last.coerce(limit.first)
|
||||||
|
diff = max - min
|
||||||
|
|
||||||
|
return if diff < 0
|
||||||
|
return min if diff == 0
|
||||||
|
|
||||||
|
diff += 1 if max.kind_of?(Integer) && !limit.exclude_end?
|
||||||
|
random(diff) + min
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_seed
|
||||||
|
Rubinius.primitive :randomizer_gen_seed
|
||||||
|
raise PrimitiveFailure, "Randomizer#gen_seed primitive failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns a random value from a range made out of Time, Date or DateTime
|
||||||
|
# instances.
|
||||||
|
#
|
||||||
|
# @param [Range] range
|
||||||
|
# @return [Time|Date|DateTime]
|
||||||
|
#
|
||||||
|
def random_time_range(range)
|
||||||
|
min = time_to_float(range.min)
|
||||||
|
max = time_to_float(range.max)
|
||||||
|
time = Time.at(random(min..max))
|
||||||
|
|
||||||
|
if Object.const_defined?(:DateTime) && range.min.is_a?(DateTime)
|
||||||
|
time = time.to_datetime
|
||||||
|
elsif Object.const_defined?(:DateTime) && range.min.is_a?(Date)
|
||||||
|
time = time.to_date
|
||||||
|
end
|
||||||
|
|
||||||
|
return time
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Casts a Time/Date/DateTime instance to a Float.
|
||||||
|
#
|
||||||
|
# @param [Time|Date|DateTime] input
|
||||||
|
# @return [Float]
|
||||||
|
#
|
||||||
|
def time_to_float(input)
|
||||||
|
return input.respond_to?(:to_time) ? input.to_time.to_f : input.to_f
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Checks if a given value is a Time, Date or DateTime object.
|
||||||
|
#
|
||||||
|
# @param [Mixed] input
|
||||||
|
# @return [TrueClass|FalseClass]
|
||||||
|
#
|
||||||
|
def time_value?(input)
|
||||||
|
return input.is_a?(Time) || (Object.const_defined?(:Date) && input.is_a?(Date))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Random
|
||||||
|
def self.new_seed
|
||||||
|
Thread.current.randomizer.generate_seed
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.srand(seed=undefined)
|
||||||
|
if undefined.equal? seed
|
||||||
|
seed = Thread.current.randomizer.generate_seed
|
||||||
|
end
|
||||||
|
|
||||||
|
seed = Rubinius::Type.coerce_to seed, Integer, :to_int
|
||||||
|
Thread.current.randomizer.swap_seed seed
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.rand(limit=undefined)
|
||||||
|
Thread.current.randomizer.random(limit)
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(seed=undefined)
|
||||||
|
@randomizer = Rubinius::Randomizer.new
|
||||||
|
if !undefined.equal?(seed)
|
||||||
|
@randomizer.swap_seed seed.to_int
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def rand(limit=undefined)
|
||||||
|
@randomizer.random(limit)
|
||||||
|
end
|
||||||
|
|
||||||
|
def seed
|
||||||
|
@randomizer.seed
|
||||||
|
end
|
||||||
|
|
||||||
|
def state
|
||||||
|
@randomizer.seed
|
||||||
|
end
|
||||||
|
private :state
|
||||||
|
|
||||||
|
def ==(other)
|
||||||
|
return false unless other.kind_of?(Random)
|
||||||
|
seed == other.seed
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns a random binary string.
|
||||||
|
# The argument size specified the length of the result string.
|
||||||
|
def bytes(length)
|
||||||
|
length = Rubinius::Type.coerce_to length, Integer, :to_int
|
||||||
|
s = ''
|
||||||
|
i = 0
|
||||||
|
while i < length
|
||||||
|
s << @randomizer.random_integer(255).chr
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
s
|
||||||
|
end
|
||||||
|
end
|
||||||
318
gxg/core/range.rb
Normal file
318
gxg/core/range.rb
Normal file
|
|
@ -0,0 +1,318 @@
|
||||||
|
class Range
|
||||||
|
include Enumerable
|
||||||
|
|
||||||
|
def initialize(first, last, exclude_end = false)
|
||||||
|
raise NameError, "`initialize' called twice" if @begin
|
||||||
|
|
||||||
|
unless first.kind_of?(Fixnum) && last.kind_of?(Fixnum)
|
||||||
|
begin
|
||||||
|
raise ArgumentError, "bad value for range" unless first <=> last
|
||||||
|
rescue
|
||||||
|
raise ArgumentError, "bad value for range"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@begin = first
|
||||||
|
@end = last
|
||||||
|
@excl = exclude_end
|
||||||
|
end
|
||||||
|
private :initialize
|
||||||
|
|
||||||
|
def ==(other)
|
||||||
|
return true if equal? other
|
||||||
|
|
||||||
|
other.kind_of?(Range) and
|
||||||
|
self.first == other.first and
|
||||||
|
self.last == other.last and
|
||||||
|
self.exclude_end? == other.exclude_end?
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :eql?, :==
|
||||||
|
|
||||||
|
attr_reader_specific :excl, :exclude_end?
|
||||||
|
|
||||||
|
attr_reader :begin
|
||||||
|
attr_reader :end
|
||||||
|
|
||||||
|
def bsearch
|
||||||
|
return to_enum :bsearch unless block_given?
|
||||||
|
|
||||||
|
unless @begin.kind_of? Numeric and @end.kind_of? Numeric
|
||||||
|
raise TypeError, "bsearch is not available for #{@begin.class}"
|
||||||
|
end
|
||||||
|
|
||||||
|
min = @begin
|
||||||
|
max = @end
|
||||||
|
|
||||||
|
max -= 1 if max.kind_of? Integer and @excl
|
||||||
|
|
||||||
|
start = min = Rubinius::Type.coerce_to min, Integer, :to_int
|
||||||
|
total = max = Rubinius::Type.coerce_to max, Integer, :to_int
|
||||||
|
|
||||||
|
last_true = nil
|
||||||
|
|
||||||
|
seeker = Proc.new do |current|
|
||||||
|
x = yield current
|
||||||
|
|
||||||
|
return current if x == 0
|
||||||
|
|
||||||
|
case x
|
||||||
|
when Numeric
|
||||||
|
if x > 0
|
||||||
|
min = current + 1
|
||||||
|
else
|
||||||
|
max = current
|
||||||
|
end
|
||||||
|
when true
|
||||||
|
last_true = current
|
||||||
|
max = current
|
||||||
|
when false, nil
|
||||||
|
min = current + 1
|
||||||
|
else
|
||||||
|
raise TypeError, "Range#bsearch block must return Numeric or boolean"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
while min < max
|
||||||
|
if max < 0 and min < 0
|
||||||
|
mid = min + (max - min) / 2
|
||||||
|
elsif min < -max
|
||||||
|
mid = -((-1 - min - max) / 2 + 1)
|
||||||
|
else
|
||||||
|
mid = (min + max) / 2
|
||||||
|
end
|
||||||
|
|
||||||
|
seeker.call mid
|
||||||
|
end
|
||||||
|
|
||||||
|
if min == max
|
||||||
|
seeker.call min
|
||||||
|
end
|
||||||
|
|
||||||
|
if min < max
|
||||||
|
return @begin if mid == start
|
||||||
|
return @begin.kind_of?(Float) ? mid.to_f : mid
|
||||||
|
end
|
||||||
|
|
||||||
|
if last_true
|
||||||
|
return @begin if last_true == start
|
||||||
|
return @begin.kind_of?(Float) ? last_true.to_f : last_true
|
||||||
|
end
|
||||||
|
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def each
|
||||||
|
return to_enum { size } unless block_given?
|
||||||
|
first, last = @begin, @end
|
||||||
|
|
||||||
|
unless first.respond_to?(:succ) && !first.kind_of?(Time)
|
||||||
|
raise TypeError, "can't iterate from #{first.class}"
|
||||||
|
end
|
||||||
|
|
||||||
|
case first
|
||||||
|
when Fixnum
|
||||||
|
last -= 1 if @excl
|
||||||
|
|
||||||
|
i = first
|
||||||
|
while i <= last
|
||||||
|
yield i
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
when String
|
||||||
|
first.upto(last, @excl) do |str|
|
||||||
|
yield str
|
||||||
|
end
|
||||||
|
when Symbol
|
||||||
|
first.to_s.upto(last.to_s, @excl) do |str|
|
||||||
|
yield str.to_sym
|
||||||
|
end
|
||||||
|
else
|
||||||
|
current = first
|
||||||
|
if @excl
|
||||||
|
while (current <=> last) < 0
|
||||||
|
yield current
|
||||||
|
current = current.succ
|
||||||
|
end
|
||||||
|
else
|
||||||
|
while (c = current <=> last) && c <= 0
|
||||||
|
yield current
|
||||||
|
break if c == 0
|
||||||
|
current = current.succ
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def first(n=undefined)
|
||||||
|
return @begin if undefined.equal? n
|
||||||
|
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
def hash
|
||||||
|
excl = @excl ? 1 : 0
|
||||||
|
hash = excl
|
||||||
|
hash ^= @begin.hash << 1
|
||||||
|
hash ^= @end.hash << 9
|
||||||
|
hash ^= excl << 24;
|
||||||
|
# Are we throwing away too much here for a good hash value distribution?
|
||||||
|
return hash & Fixnum::MAX
|
||||||
|
end
|
||||||
|
|
||||||
|
def include?(value)
|
||||||
|
if @begin.respond_to?(:to_int) ||
|
||||||
|
@end.respond_to?(:to_int) ||
|
||||||
|
@begin.kind_of?(Numeric) ||
|
||||||
|
@end.kind_of?(Numeric)
|
||||||
|
cover? value
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
alias_method :member?, :include?
|
||||||
|
|
||||||
|
def ===(value)
|
||||||
|
include?(value)
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
"#{@begin.inspect}#{@excl ? "..." : ".."}#{@end.inspect}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def last(n=undefined)
|
||||||
|
return @end if undefined.equal? n
|
||||||
|
|
||||||
|
to_a.last(n)
|
||||||
|
end
|
||||||
|
|
||||||
|
def max
|
||||||
|
return super if block_given? || (@excl && !@end.kind_of?(Numeric))
|
||||||
|
return nil if @end < @begin || (@excl && @end == @begin)
|
||||||
|
return @end unless @excl
|
||||||
|
|
||||||
|
unless @end.kind_of?(Integer)
|
||||||
|
raise TypeError, "cannot exclude non Integer end value"
|
||||||
|
end
|
||||||
|
|
||||||
|
unless @begin.kind_of?(Integer)
|
||||||
|
raise TypeError, "cannot exclude end value with non Integer begin value"
|
||||||
|
end
|
||||||
|
|
||||||
|
@end - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def min
|
||||||
|
return super if block_given?
|
||||||
|
return nil if @end < @begin || (@excl && @end == @begin)
|
||||||
|
|
||||||
|
@begin
|
||||||
|
end
|
||||||
|
|
||||||
|
def step(step_size=1) # :yields: object
|
||||||
|
return to_enum(:step, step_size) do
|
||||||
|
m = Rubinius::Mirror::Range.reflect(self)
|
||||||
|
m.step_iterations_size(*m.validate_step_size(@begin, @end, step_size))
|
||||||
|
end unless block_given?
|
||||||
|
|
||||||
|
m = Rubinius::Mirror::Range.reflect(self)
|
||||||
|
values = m.validate_step_size(@begin, @end, step_size)
|
||||||
|
first = values[0]
|
||||||
|
last = values[1]
|
||||||
|
step_size = values[2]
|
||||||
|
|
||||||
|
case first
|
||||||
|
when Float
|
||||||
|
iterations = m.step_float_iterations_size(first, last, step_size)
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
while i < iterations
|
||||||
|
curr = i * step_size + first
|
||||||
|
curr = last if last < curr
|
||||||
|
yield curr
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
when Numeric
|
||||||
|
curr = first
|
||||||
|
last -= 1 if @excl
|
||||||
|
|
||||||
|
while curr <= last
|
||||||
|
yield curr
|
||||||
|
curr += step_size
|
||||||
|
end
|
||||||
|
else
|
||||||
|
i = 0
|
||||||
|
each do |curr|
|
||||||
|
yield curr if i % step_size == 0
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"#{@begin}#{@excl ? "..." : ".."}#{@end}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_a
|
||||||
|
return super unless @begin.kind_of? Fixnum and @end.kind_of? Fixnum
|
||||||
|
|
||||||
|
fin = @end
|
||||||
|
fin += 1 unless @excl
|
||||||
|
|
||||||
|
size = fin - @begin
|
||||||
|
return [] if size <= 0
|
||||||
|
|
||||||
|
ary = Array.new(size)
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
while i < size
|
||||||
|
ary[i] = @begin + i
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
ary
|
||||||
|
end
|
||||||
|
|
||||||
|
def cover?(value)
|
||||||
|
# MRI uses <=> to compare, so must we.
|
||||||
|
|
||||||
|
beg_compare = (@begin <=> value)
|
||||||
|
return false unless beg_compare
|
||||||
|
|
||||||
|
if Comparable.compare_int(beg_compare) <= 0
|
||||||
|
end_compare = (value <=> @end)
|
||||||
|
|
||||||
|
if @excl
|
||||||
|
return true if Comparable.compare_int(end_compare) < 0
|
||||||
|
else
|
||||||
|
return true if Comparable.compare_int(end_compare) <= 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def size
|
||||||
|
return nil unless @begin.kind_of?(Numeric)
|
||||||
|
|
||||||
|
delta = @end - @begin
|
||||||
|
return 0 if delta < 0
|
||||||
|
|
||||||
|
if @begin.kind_of?(Float) || @end.kind_of?(Float)
|
||||||
|
return delta if delta == Float::INFINITY
|
||||||
|
|
||||||
|
err = (@begin.abs + @end.abs + delta.abs) * Float::EPSILON
|
||||||
|
err = 0.5 if err > 0.5
|
||||||
|
|
||||||
|
(@excl ? delta - err : delta + err).floor + 1
|
||||||
|
else
|
||||||
|
delta += 1 unless @excl
|
||||||
|
delta
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
61
gxg/core/range_mirror.rb
Normal file
61
gxg/core/range_mirror.rb
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
module Rubinius
|
||||||
|
class Mirror
|
||||||
|
class Range < Mirror
|
||||||
|
self.subject = ::Range
|
||||||
|
|
||||||
|
def excl
|
||||||
|
Rubinius.invoke_primitive :object_get_ivar, @object, :@excl
|
||||||
|
end
|
||||||
|
|
||||||
|
def step_float_iterations_size(first, last, step_size)
|
||||||
|
err = (first.abs + last.abs + (last - first).abs) / step_size.abs * Float::EPSILON
|
||||||
|
err = 0.5 if err > 0.5
|
||||||
|
|
||||||
|
if excl
|
||||||
|
iterations = ((last - first) / step_size - err).floor
|
||||||
|
iterations += 1 if iterations * step_size + first < last
|
||||||
|
else
|
||||||
|
iterations = ((last - first) / step_size + err).floor + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
iterations
|
||||||
|
end
|
||||||
|
|
||||||
|
def step_iterations_size(first, last, step_size)
|
||||||
|
case first
|
||||||
|
when Float
|
||||||
|
step_float_iterations_size(first, last, step_size)
|
||||||
|
else
|
||||||
|
@object.size.nil? ? nil : (@object.size.fdiv(step_size)).ceil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_step_size(first, last, step_size)
|
||||||
|
if step_size.kind_of? Float or first.kind_of? Float or last.kind_of? Float
|
||||||
|
# if any are floats they all must be
|
||||||
|
begin
|
||||||
|
step_size = Float(from = step_size)
|
||||||
|
first = Float(from = first)
|
||||||
|
last = Float(from = last)
|
||||||
|
rescue ArgumentError
|
||||||
|
raise TypeError, "no implicit conversion to float from #{from.class}"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
step_size = Integer(from = step_size)
|
||||||
|
|
||||||
|
unless step_size.kind_of? Integer
|
||||||
|
raise TypeError, "can't convert #{from.class} to Integer (#{from.class}#to_int gives #{step_size.class})"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if step_size <= 0
|
||||||
|
raise ArgumentError, "step can't be negative" if step_size < 0
|
||||||
|
raise ArgumentError, "step can't be 0"
|
||||||
|
end
|
||||||
|
|
||||||
|
return first, last, step_size
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue