object

package
v0.0.0-...-aaf2ed9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 11, 2026 License: BSD-3-Clause Imports: 7 Imported by: 0

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

Constants

This section is empty.

Variables

View Source
var (
	True  = Bool(true)
	False = Bool(false)
	NilV  = Nil{}
)

Singletons shared across the VM.

View Source
var CustomKeyHook func(k Value) (rubyHash int64, eql func(stored Value) bool, ok bool)

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 BigOf

func BigOf(v Value) (*big.Int, bool)

BigOf returns the big.Int value of an Integer or Bignum (ok=false otherwise).

func IsNil

func IsNil(v Value) bool

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

func NewArray(elems ...Value) *Array

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

func NewArrayFromSlice(elems []Value) *Array

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.

func (*Array) Inspect

func (a *Array) Inspect() string

func (*Array) ToS

func (a *Array) ToS() string

func (*Array) Truthy

func (a *Array) Truthy() bool

type Bignum

type Bignum struct{ I *big.Int }

Bignum is an arbitrary-precision integer (the int64 overflow case). It is held immutably: results are always fresh big.Ints, never mutated in place.

func (*Bignum) Inspect

func (b *Bignum) Inspect() string

func (*Bignum) ToS

func (b *Bignum) ToS() string

func (*Bignum) Truthy

func (b *Bignum) Truthy() bool

type Bool

type Bool bool

Bool is true or false.

func (Bool) Inspect

func (b Bool) Inspect() string

func (Bool) ToS

func (b Bool) ToS() string

func (Bool) Truthy

func (b Bool) Truthy() bool

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)").

func (*Complex) Inspect

func (c *Complex) Inspect() string

Inspect renders the parenthesised form, e.g. "(1+2i)". A Rational component is shown in its own parenthesised inspect form and a Rational imaginary part is written as "(n/d)*i" (matching MRI: Complex(0, Rational(5,2)) → "(0+(5/2)*i)").

func (*Complex) ToS

func (c *Complex) ToS() string

func (*Complex) Truthy

func (c *Complex) Truthy() bool

type Float

type Float float64

Float is a 64-bit float.

func (Float) Inspect

func (f Float) Inspect() string

func (Float) ToS

func (f Float) ToS() string

func (Float) Truthy

func (f Float) Truthy() bool

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 NewHash

func NewHash() *Hash

NewHash returns an empty hash.

func NewHashCap

func NewHashCap(n int) *Hash

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

func (h *Hash) Delete(k Value) (Value, bool)

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

func (h *Hash) Get(k Value) (Value, bool)

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.

func (*Hash) Inspect

func (h *Hash) Inspect() string

func (*Hash) Len

func (h *Hash) Len() int

Len returns the number of entries.

func (*Hash) Set

func (h *Hash) Set(k, v Value)

Set inserts or updates k→v, preserving first-insertion order. A String key takes the strVals fast path: an overwrite mutates the stored entry in place (no allocation), and only a genuine insert dups+freezes the key snapshot and allocates the owned key string.

func (*Hash) ToS

func (h *Hash) ToS() string

func (*Hash) Truthy

func (h *Hash) Truthy() bool

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).

func (Integer) Inspect

func (i Integer) Inspect() string

func (Integer) ToS

func (i Integer) ToS() string

func (Integer) Truthy

func (i Integer) Truthy() bool

type KeyUnwrapper

type KeyUnwrapper interface {
	HashUnwrap() (Value, bool)
}

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.

func (*Main) Inspect

func (m *Main) Inspect() string

func (*Main) IvarTable

func (m *Main) IvarTable() map[string]Value

IvarTable exposes main's instance variables to the VM.

func (*Main) ToS

func (m *Main) ToS() string

func (*Main) Truthy

func (m *Main) Truthy() bool

type Nil

type Nil struct{}

Nil is the single nil value.

func (Nil) Inspect

func (Nil) Inspect() string

func (Nil) ToS

func (Nil) ToS() string

func (Nil) Truthy

func (Nil) Truthy() bool

type Range

type Range struct {
	Lo, Hi    Value
	Exclusive bool
}

Range is Lo..Hi (Exclusive ? "...". A reference type; immutable in practice.

func NewRange

func NewRange(lo, hi Value, exclusive bool) *Range

NewRange returns a *Range spanning lo..hi (exclusive of hi when exclusive is true), exactly like &Range{Lo: lo, Hi: hi, Exclusive: exclusive}. It is the construction chokepoint for ranges entering a Value, mirroring NewArray.

func (*Range) Inspect

func (r *Range) Inspect() string

func (*Range) ToS

func (r *Range) ToS() string

func (*Range) Truthy

func (r *Range) Truthy() bool

type Rational

type Rational struct{ R *big.Rat }

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.

func (*Rational) Inspect

func (r *Rational) Inspect() string

func (*Rational) ToS

func (r *Rational) ToS() string

func (*Rational) Truthy

func (r *Rational) Truthy() bool

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

func NewFrozenStringView(s string) *String

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 NewString

func NewString(s string) *String

NewString builds an owned String from a Go string, copying its bytes.

func NewStringBytes

func NewStringBytes(b []byte) *String

NewStringBytes builds an owned String that takes ownership of b without copying it. The caller must not retain or mutate b afterwards.

func NewStringBytesEnc

func NewStringBytesEnc(b []byte, enc string) *String

NewStringBytesEnc is NewStringBytes with an explicit encoding tag.

func NewStringView

func NewStringView(s string) *String

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

func (s *String) Bytes() []byte

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

func (s *String) Dup() *String

Dup returns an unfrozen shallow copy with its own backing array, preserving the encoding.

func (*String) EncName

func (s *String) EncName() string

EncName returns the string's encoding name, defaulting to UTF-8.

func (*String) Inspect

func (s *String) Inspect() string

func (*String) IsBinary

func (s *String) IsBinary() bool

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) Len

func (s *String) Len() int

Len returns the string's byte length on either representation.

func (*String) MutableBytes

func (s *String) MutableBytes() []byte

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

func (s *String) SetBytes(b []byte)

SetBytes replaces the string's contents with b, taking ownership of it and switching to the owned representation.

func (*String) Str

func (s *String) Str() string

Str returns the string's contents as a Go string.

func (*String) TakeFrom

func (s *String) TakeFrom(o *String)

TakeFrom makes s share o's representation (owned slice or view), used when o is a freshly produced, otherwise-discarded String so the transfer is zero-copy and cannot alias any live value. Enc and Frozen are left untouched.

func (*String) ToS

func (s *String) ToS() string

func (*String) Truthy

func (s *String) Truthy() bool

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.

func (Symbol) Inspect

func (s Symbol) Inspect() string

func (Symbol) ToS

func (s Symbol) ToS() string

func (Symbol) Truthy

func (s Symbol) Truthy() bool

type Value

type Value interface {
	ToS() string
	Inspect() string
	Truthy() bool
}

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

func IntValue(v int64) Value

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

func NormInt(z *big.Int) Value

NormInt returns an Integer when z fits in int64, else a Bignum — so a result that shrinks back into range demotes automatically.

func SymVal

func SymVal(name string) Value

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL