Documentation
¶
Overview ¶
Package object holds the runtime value representation.
Phase 0 keeps Value minimal: a small interface plus concrete value types. Per plan-rbgo.md §4 (and the critique folded into it), the eventual design must add RClass(*VM) for dynamic dispatch and split Fixnum/Bignum so the common integer does not carry a nil *big.Int word. None of that exists yet — this is the deliberately-thin vertical slice.
Index ¶
- Variables
- func BigOf(v Value) (*big.Int, bool)
- func IsNil(v Value) bool
- type Array
- type Bignum
- type Bool
- type Complex
- type Float
- type Hash
- type Integer
- type KeyUnwrapper
- type Main
- type Nil
- type Range
- type Rational
- type String
- func (s *String) Bytes() []byte
- func (s *String) Dup() *String
- func (s *String) EncName() string
- func (s *String) Inspect() string
- func (s *String) IsBinary() bool
- func (s *String) Len() int
- func (s *String) MutableBytes() []byte
- func (s *String) SetBytes(b []byte)
- func (s *String) Str() string
- func (s *String) TakeFrom(o *String)
- func (s *String) ToS() string
- func (s *String) Truthy() bool
- type Symbol
- type Value
Constants ¶
This section is empty.
Variables ¶
Singletons shared across the VM.
CustomKeyHook lets the vm package supply Ruby-level hash/eql? semantics for a key that is neither a plain value type nor a built-in subclass — i.e. a user object that overrides #hash and #eql?. Given such a key it returns the int64 the object's #hash method yields and a predicate that reports whether a stored key is #eql? to it; ok is false for any key that should keep identity-based behaviour (the default Object#hash/#eql?). Set once by the VM at construction; nil in tests that never build a VM (then every object key uses identity).
Functions ¶
func IsNil ¶
IsNil reports whether v is the Ruby nil value or an absent (Go nil) Value.
It is the predicate half of the nil seam. Today, while Value is an interface, a Value can be Go-nil (an "absent" sentinel) or hold the Nil singleton; both read as nil, so IsNil folds them together. After Value becomes a tagged struct — where a Go-nil Value no longer exists and nil is a tag — this function's body changes to a single tag check while its call sites stay put.
Comparing v against NilV never panics: NilV's dynamic type (Nil) is comparable, so a v holding any other dynamic type simply compares unequal.
Types ¶
type Array ¶
type Array struct{ Elems []Value }
Array is a mutable, ordered list. It is a reference type (used as *Array), so aliasing and in-place mutation (push, []=) behave as in Ruby.
func NewArray ¶
NewArray returns an *Array whose backing slice holds elems. Calling it with a spread slice (NewArray(xs...)) passes xs through unchanged — the array shares that backing slice, exactly like &Array{Elems: xs} — while calling it with individual arguments (NewArray(a, b)) allocates a fresh slice, exactly like &Array{Elems: []Value{a, b}}. It is the single construction chokepoint for arrays entering a Value, so the migration to a tagged-struct Value can be localised here (compare NewString / NewHash).
func NewArrayFromSlice ¶
NewArrayFromSlice returns an *Array that shares (does not copy) the given backing slice, exactly like &Array{Elems: elems}. Use it for the aliasing case — a named slice, a make(...) that is filled in place, or an append result — where NewArray(elems...) would read awkwardly. Aliasing semantics are identical to the composite literal.
type Bignum ¶
Bignum is an arbitrary-precision integer (the int64 overflow case). It is held immutably: results are always fresh big.Ints, never mutated in place.
type Complex ¶
type Complex struct{ Re, Im Value }
Complex is a Ruby Complex number. Its real and imaginary parts are themselves numeric Values (Integer/Bignum/Float), so Complex(1, 2) keeps Integer parts and renders "(1+2i)" while Complex(1.5, 2) renders "(1.5+2i)", matching MRI. A part may also be a Rational (e.g. the `2.5ri` literal yields Complex(0, Rational(5,2)), inspected as "(0+(5/2)*i)").
type Hash ¶
type Hash struct {
Keys []Value // insertion order (string keys held as frozen snapshots)
// Default is the value returned for a missing key (Hash.new(default)); nil
// means none. DefaultProc, when set, is a Proc (held as a Value to avoid an
// import cycle) called with (hash, key) on a miss (Hash.new { … }); it takes
// precedence over Default. Both nil ⇒ a missing key reads as nil.
Default Value
DefaultProc Value
// contains filtered or unexported fields
}
Hash is an insertion-ordered map (as in Ruby). Keys are normalised by hashKey: a String keys by its byte content (Ruby dups+freezes string keys, so a stored key is a frozen snapshot), every other value keys by itself — value types by value, reference types by identity. It is a reference type.
String keys take a dedicated allocation-free fast path: they live in strVals, a map[string]*strEntry keyed by byte content, rather than in the general map[any]Value (vals). This matters because the general path routes a String key through hashKey, which returns an `any` — and once a []byte→string conversion is boxed into an interface the compiler can no longer elide it, so every Get/Set copied the key bytes. Reading and overwriting through strVals uses `h.strVals[string(b)]` directly, a form the compiler DOES elide (map index and map delete of a string([]byte) allocate nothing), and the *strEntry indirection lets an overwrite mutate the value in place without re-storing the key (a bare `m[string(b)] = v` would allocate the key string every time). Only a genuine first insert allocates: the owned key string plus the frozen key snapshot Ruby semantics require. strVals is nil until the first String key is inserted; nil-map reads are safe, so Get/repr need no guard.
func NewHashCap ¶
NewHashCap returns an empty hash whose backing map and key slice are pre-sized for about n entries, so a bulk builder (e.g. JSON.parse of a known-width object) fills it without re-growing. A negative n behaves like NewHash.
func (*Hash) Clear ¶
func (h *Hash) Clear()
Clear removes every entry, leaving an empty hash (the Default/DefaultProc are kept, as in MRI).
func (*Hash) Delete ¶
Delete removes k, returning its value and whether it was present. A String key is removed from the strVals fast path (and its snapshot from Keys); every other key type takes the general hashKey path.
func (*Hash) Get ¶
Get returns the value for k and whether it is present. A String key resolves through the allocation-free strVals fast path; every other key type takes the general hashKey path.
type Integer ¶
type Integer int64
Integer is a 64-bit integer (the common case). Arithmetic that overflows int64 promotes to a Bignum; both report their class as Integer, so the Fixnum/Bignum split is transparent (as in Ruby 4.0).
type KeyUnwrapper ¶
hashKey normalises a key to its comparable map form. KeyUnwrapper is implemented by a wrapper around a built-in value (an instance of a user subclass of String/Array/Hash) so that, used as a Hash key, it hashes and compares as the value it wraps rather than by object identity. Defined here (not in the vm package) to keep the hash key logic free of an import cycle.
type Main ¶
type Main struct {
// contains filtered or unexported fields
}
Main is the top-level self ("main" object) used while executing the program body. It is a real object with its own instance-variable table, so top-level @ivars persist (and are shared with top-level method bodies, whose self is also main).
func NewMain ¶
func NewMain() *Main
NewMain returns the top-level self with an empty instance-variable table.
type Range ¶
Range is Lo..Hi (Exclusive ? "...". A reference type; immutable in practice.
type Rational ¶
Rational is an exact ratio of two integers (Ruby Rational), held as a normalised math/big.Rat — always reduced, with a positive denominator — so Rational(4, 2) prints "2/1" and Rational(1, -2) prints "-1/2", matching MRI.
type String ¶
type String struct {
Frozen bool
// Enc is the encoding name, or "" for the UTF-8 default. Only a non-default
// (e.g. "ASCII-8BIT") changes behaviour, so existing UTF-8 strings are
// unaffected.
Enc string
// contains filtered or unexported fields
}
String is a Ruby string: a mutable, reference-typed byte sequence (always used as *String), so aliasing and in-place mutation (<<, []=, replace, the bang methods) behave as in Ruby. Frozen marks a string that may not be mutated (Hash keys are frozen snapshots; a frozen literal raises on mutation).
Copy-on-write: a String has two representations. An *owned* string holds a private, mutable byte slice in b (isView == false). A *view* string instead shares an immutable Go string in view (isView == true) and leaves b nil, avoiding the substring copy that dominates alloc-heavy paths such as String#split. Because Go strings are immutable, a view can never observe a mutation of whatever produced it, so views are provably free of source aliasing. The first in-place mutation calls ensureOwned, which copies the view into a private b and switches the representation; all reads go through the Bytes / Str / Len accessors, which work on both representations.
Invariants: exactly one representation is live. When isView is true, view holds the content and b is nil; when isView is false, b holds the content and view is "".
func NewFrozenStringView ¶
NewFrozenStringView builds a frozen copy-on-write view over the immutable Go string s. Because it is frozen it never mutates, so the view is permanent and serves every read without copying — ideal for interned/AOT string literals.
func NewStringBytes ¶
NewStringBytes builds an owned String that takes ownership of b without copying it. The caller must not retain or mutate b afterwards.
func NewStringBytesEnc ¶
NewStringBytesEnc is NewStringBytes with an explicit encoding tag.
func NewStringView ¶
NewStringView builds a copy-on-write String that shares s's immutable bytes without copying them. The first in-place mutation transparently materializes a private copy (see ensureOwned). Use this for substrings and other reads carved out of an already-immutable Go string.
func (*String) Bytes ¶
Bytes returns the string's bytes for READ-ONLY use, without copying. Callers MUST NOT mutate the returned slice; use MutableBytes for in-place mutation. For a view this aliases the underlying immutable Go string, so mutating it would corrupt every sibling sharing that source.
func (*String) Dup ¶
Dup returns an unfrozen shallow copy with its own backing array, preserving the encoding.
func (*String) IsBinary ¶
IsBinary reports whether the string is tagged ASCII-8BIT (BINARY), in which case it is treated as opaque bytes (length counts bytes, not characters).
func (*String) MutableBytes ¶
MutableBytes returns the owned byte slice for in-place mutation, first materializing a private copy if the string is a view. Callers may freely mutate the result in place.
func (*String) SetBytes ¶
SetBytes replaces the string's contents with b, taking ownership of it and switching to the owned representation.
type Symbol ¶
type Symbol string
Symbol is an interned name (:foo). It is an immutable value type, so equality and use as a hash key are just value comparison.
type Value ¶
Value is the interface implemented by every Ruby value.
ToS backs to_s / puts / string interpolation. Inspect backs inspect / p. Truthy implements Ruby truthiness: everything is truthy except false and nil.
func IntValue ¶
IntValue boxes v as a Value, reusing an interned box when v is small so no allocation occurs. Use it on hot paths that produce integers; for a value outside the cached range it is exactly object.Integer(v).
func NilVal ¶
func NilVal() Value
NilVal returns the single Ruby nil value. (It cannot be named Nil — that is the value type; the name follows the IntValue / SymVal constructor style.)
It is the constructor half of the nil seam used by the eventual migration of Value from an interface to a concrete tagged struct (plan-rbgo.md §4). A tagged-struct Value can never be a Go nil, so code that today produces an "absent Value" as the untyped nil must instead produce the nil object. Using NilVal() at those sites now — while Value is still an interface — is behaviour neutral (it yields NilV, the same singleton the interpreter already uses for Ruby nil) and lets the representation flip swap this one function's body rather than every call site.
func NormInt ¶
NormInt returns an Integer when z fits in int64, else a Bignum — so a result that shrinks back into range demotes automatically.
func SymVal ¶
SymVal boxes name as a Symbol Value, interning the box so a given symbol name yields the identical Value on every call. The first call for a name allocates its box; every later call is an allocation-free map load. Use it on hot paths that box a symbol per operation; the result is exactly object.Symbol(name) (same dynamic type and value), only shared.