Overview
Relevant Files
ruby.c- Main entry point and command-line processingvm.c- Virtual machine core and execution engineinits.c- Runtime initialization and builtin class setupcompile.c- AST to bytecode compilationvm_core.h- VM data structures and core definitionsparse.y- Parser (Yacc/Bison grammar)
Ruby is an interpreted, object-oriented programming language designed for simplicity and productivity. The CRuby interpreter is a sophisticated system that transforms source code into executable bytecode through multiple processing stages.
Architecture Overview
Loading diagram...
Core Components
Parser & Compilation Pipeline
The interpreter begins with parse.y, which uses Yacc/Bison to parse Ruby source code into an Abstract Syntax Tree (AST). The compile.c module then transforms this AST into bytecode instructions (ISeq), performing optimizations like dead code elimination and instruction unification along the way.
Virtual Machine Execution
The VM core (vm.c and vm_exec.c) executes bytecode through a threaded code interpreter. It maintains control frames for each method call, manages the stack, and handles instruction dispatch. The VM supports multiple execution modes including direct threaded code for performance.
Runtime Initialization
The inits.c file orchestrates a two-phase initialization: first, core classes (Object, String, Array, Hash, etc.) are initialized, then builtin libraries and JIT compilers (YJIT, ZJIT) are set up. This happens in ruby_setup() before user code executes.
Object System & Memory
Ruby objects are represented as C structures with type information. The garbage collector (gc.c) manages memory, supporting both mark-and-sweep and generational collection strategies. The shape system optimizes object layout for performance.
Execution Flow
- Startup (
ruby.c): Parse command-line options, initialize the VM - Setup (
inits.c): Initialize builtin classes and modules - Compilation: Parse and compile user code to bytecode
- Execution (
vm.c): Execute bytecode with JIT compilation support - Cleanup: Finalize threads and free resources
The interpreter supports advanced features like Ractors (parallel execution), Fibers (lightweight concurrency), and multiple JIT backends for performance optimization.
Architecture & Execution Model
Relevant Files
vm_core.hvm_exec.cvm_exec.hiseq.hiseq.c
Ruby's VM is a bytecode interpreter that executes instruction sequences (iseqs) through a stack-based execution model. The architecture consists of three core layers: compilation, instruction representation, and runtime execution.
Instruction Sequences (ISeq)
An instruction sequence is the compiled representation of Ruby code. Each iseq contains:
- Encoded bytecode (
iseq_encoded): Array of instruction opcodes and operands - Parameter metadata: Information about method/block parameters (lead, optional, rest, keyword, block)
- Local variables: Table of local variable names and their states
- Catch table: Exception handling information for rescue/ensure blocks
- Inline caches: Constant, instance variable, and call caches for optimization
ISeqs are hierarchical—methods, blocks, and classes each have their own iseq, with parent references for scope resolution.
Execution Context & Control Frames
The execution context (rb_execution_context_t) represents a fiber's execution state:
- VM stack: Operand stack for computation
- Control frame pointer: Current frame in the call stack
- Exception tag: For handling Ruby exceptions (raise/rescue/ensure)
- Interrupt flags: For thread scheduling and signal handling
Each control frame (rb_control_frame_t) tracks:
- Program counter (pc): Current instruction address
- Stack pointer (sp): Top of operand stack
- ISeq reference: Current instruction sequence
- Environment pointer (ep): Local variable storage
- Block handler: Captured block for yield operations
Execution Model
Loading diagram...
The VM executes through a dispatch loop in vm_exec_core(). Three dispatch strategies are supported:
- Direct threaded code (GCC): Jump table using computed goto for maximum speed
- Token threaded code (GCC fallback): Indirect jumps via instruction table
- Switch dispatch (portable): Traditional switch statement
Each instruction reads operands from the bytecode, manipulates the stack, and dispatches to the next instruction.
Inline Caching
Ruby uses inline caches embedded in iseqs to accelerate method calls and constant lookups:
- Call caches (CC): Cache method lookup results
- Constant caches (IC): Cache constant resolution
- Instance variable caches (IVC): Cache attribute access
- Class variable caches (ICVARC): Cache class variable lookups
Caches are invalidated when classes are modified, triggering recompilation or deoptimization.
JIT Integration
The VM supports two JIT compilers (YJIT and ZJIT) that generate native code:
- JIT entry points: Function pointers in iseq for compiled code
- Fallback to interpreter: If JIT compilation fails or code is deoptimized
- Exception handling: JIT code can throw exceptions back to the interpreter
The VM_EXEC and JIT_EXEC macros coordinate between interpreter and JIT execution.
Parser & Compilation Pipeline
Relevant Files
prism/prism.c- Core parser implementation and lexing logicprism/parser.h- Parser structure and initializationprism_compile.c- AST to ISEQ compilationcompile.c- Legacy compilation infrastructure and ISEQ generationinsns.def- VM instruction definitions
Ruby's compilation pipeline transforms source code into executable bytecode through three main stages: parsing, AST compilation, and instruction sequence generation.
Parsing Stage
The Prism parser (prism/prism.c) is the entry point. It initializes a pm_parser_t structure with source code and parses it into an Abstract Syntax Tree (AST). Key functions:
pm_parser_init()- Initializes the parser with source and optionspm_parse()- Performs the actual parsing, returning the root AST nodepm_parser_free()- Cleans up parser resources
The parser maintains state including the current token, lexer modes, constant pool, and newline tracking. It handles encoding detection via magic comments and supports callbacks for encoding changes.
AST to ISEQ Compilation
Once parsing completes, prism_compile.c converts the AST into an Instruction Sequence (ISEQ). The main compilation function is:
pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node,
LINK_ANCHOR *ret, bool popped,
pm_scope_node_t *scope_node)
This recursive function visits each AST node and emits corresponding VM instructions. Key aspects:
- Scope Management: Creates child ISEQs for methods, blocks, and classes via
pm_new_child_iseq() - Local Variables: Tracks local variable indices and nesting levels
- Control Flow: Generates labels and jumps for conditionals, loops, and exception handling
- Instruction Emission: Uses macros like
PUSH_INSN,PUSH_INSN1to append instructions to the instruction list
Instruction Sequence Structure
The ISEQ is a linked list of instructions defined in insns.def. Each instruction specifies:
- Operands: Parameters the instruction takes (e.g., local variable index)
- Stack Effects: How many values are popped and pushed
- Attributes: Metadata like whether it handles the stack pointer
Common instructions include getlocal, setlocal, send (method calls), jump, and leave (return).
Compilation Pipeline Flow
Loading diagram...
The compiled ISEQ is stored in rb_iseq_t structures, which the VM executes. Each ISEQ can reference child ISEQs for nested scopes, forming a tree of executable code units.
Object System & Type System
Relevant Files
object.cclass.cinclude/ruby/internal/value.hinclude/ruby/internal/core/rbasic.hinclude/ruby/internal/core/robject.hshape.handshape.c
VALUE: The Universal Object Handle
Ruby represents all objects using a single type: VALUE, an unsigned integer (typically uintptr_t). This is not a pointer—it's a tagged value that encodes both the object reference and type information. The key insight is that Ruby uses tagged immediates for small, frequently-used values like integers, symbols, and booleans, storing them directly in the VALUE without heap allocation.
Special constants like RUBY_Qnil, RUBY_Qtrue, and RUBY_Qfalse are fixed bit patterns that don't require heap storage. Fixnums (small integers) and flonums (small floats) are also encoded directly in the VALUE using low-order bits as flags.
RBasic: The Foundation of All Objects
Every heap-allocated Ruby object begins with a struct RBasic:
struct RBasic {
VALUE flags; // Per-object flags (frozen, type info, etc.)
const VALUE klass; // The object's class (immutable)
VALUE shape_id; // (Optional) Shape identifier for optimization
};
The flags field stores the object's type (extracted via RB_BUILTIN_TYPE()) and per-object properties. The klass field is const-qualified because an object's class cannot change in Ruby. The shape_id field (present on 32-bit systems) tracks the object's shape for optimized instance variable access.
Type System: 22 Built-in Types
Ruby's type system includes 22 built-in types defined in enum ruby_value_type:
- Heap objects:
T_OBJECT,T_CLASS,T_MODULE,T_STRING,T_ARRAY,T_HASH,T_STRUCT,T_FILE,T_DATA,T_REGEXP,T_MATCH,T_BIGNUM,T_COMPLEX,T_RATIONAL,T_FLOAT - Special constants:
T_NIL,T_TRUE,T_FALSE,T_SYMBOL,T_FIXNUM,T_UNDEF - Internal types:
T_IMEMO,T_NODE,T_ICLASS,T_ZOMBIE,T_MOVED
Type checking is optimized for the hot path: RB_TYPE_P() uses compile-time constants to generate fast inline code.
RObject: Instance Variables and Embedded Storage
User-defined objects use struct RObject, which extends RBasic with instance variable storage:
struct RObject {
struct RBasic basic;
union {
struct { VALUE *fields; } heap; // Separate buffer
VALUE ary[1]; // Embedded storage
} as;
};
Ruby optimizes small objects by embedding instance variables directly in the object slot (up to 3 VALUEs). Larger objects spill to a separate heap buffer, marked by the ROBJECT_HEAP flag.
Shape System: Optimizing Instance Variable Access
The shape system is a modern optimization that tracks the layout of instance variables in objects. Each object has a shape_id that identifies its instance variable configuration. Shapes form a tree structure where each node represents adding a new instance variable.
Key benefits:
- Fast ivar lookup: Given a shape ID and variable name, find the storage index in O(1) time
- JIT optimization: JIT compilers can inline ivar access based on shape information
- Memory efficiency: Objects with the same ivar layout share shape metadata
Shapes support transitions for frozen objects, complex objects (backed by hash tables), and objects with explicit object_id storage. The shape ID encodes flags for these states in its high bits.
Class Hierarchy and Metaclasses
Every object has a class, and every class is also an object. This creates a recursive hierarchy:
BasicObjectis the root classObjectinherits fromBasicObject- User classes inherit from
Objector other classes - Each class has a metaclass (its class), which is also a class
Classes themselves are instances of Class, and Class is an instance of itself. This elegant design makes Ruby's object model fully reflective.
Loading diagram...
Method Dispatch & Lookup
Relevant Files
vm_method.cmethod.hvm_callinfo.h
Method dispatch in Ruby is a multi-layered system that balances performance with flexibility. When a method is called, the VM must locate the correct implementation through a series of lookups and caches.
Core Data Structures
The method dispatch system relies on three key structures:
Method Entry (ME): Represents a method definition with visibility, type, and implementation details. Stored in class method tables (RCLASS_M_TBL).
Callable Method Entry (CME): A refined version of ME that guarantees the method is callable and includes a defined_class field. Used for actual dispatch.
Call Cache (CC): Inline cache storing the receiver class, CME, and call handler. Dramatically speeds up repeated calls to the same method on the same class.
Method Lookup Pipeline
Loading diagram...
Lookup Process
search_method0() walks the class hierarchy starting from the receiver's class, checking each class's method table. It returns the first matching method entry or NULL if not found.
prepare_callable_method_entry() converts a method entry into a callable method entry. For module methods, it complements the defined_class field to track where the method was actually defined.
callable_method_entry_or_negative() implements a two-tier cache strategy:
- Fast path: Lock-free atomic read from the CC table
- Slow path: Acquires VM lock, searches the hierarchy, and populates the cache
Caching Strategy
Call caches are organized in class CC entries (CCS) structures, which group multiple CCs for the same method ID. This allows efficient invalidation when methods are redefined.
Negative caching stores entries for methods that don't exist, preventing repeated failed lookups. These are stored in vm->negative_cme_table.
Cache Invalidation
When a method is redefined or a module is included, the VM must invalidate affected caches:
- Leaf invalidation: Only the specific class's CC table is cleared
- Tree invalidation: All subclasses' caches are invalidated by replacing the CME in their method tables
The clear_method_cache_by_id_in_class() function handles this, checking for subclasses and invalidating accordingly.
Refinements & Overloading
Refined methods wrap the original method and are resolved at call time based on the current refinement scope.
Overloaded methods (with mandatory-only variants) use a separate table to cache the optimized version, avoiding parameter checking overhead when all arguments are positional.
Memory Management & Garbage Collection
Relevant Files
gc.c- Main GC interface and public APIgc/default/default.c- Default mark-sweep-compact GC implementationgc/gc.h- GC internal API and structuresgc/gc_impl.h- Modular GC interfaceshape.candshape.h- Object shape tracking for GC
Ruby uses a generational mark-sweep-compact garbage collector with write barriers to optimize memory management. The system supports multiple GC implementations through a modular API.
Core Architecture
The GC divides the heap into multiple size pools (default: 5 heaps) to reduce fragmentation. Each heap contains pages of objects, with bitmaps tracking object state: marked, uncollectible, pinned, and age bits. The system maintains a remembered set to track references from old to young objects, enabling efficient generational collection.
typedef struct {
rb_heap_t heaps[HEAP_COUNT];
mark_stack_t mark_stack;
size_t marked_slots;
st_table *finalizer_table;
rb_darray(VALUE *) weak_references;
} rb_objspace_t;
Generational Collection (RGenGC)
Ruby implements generational GC where objects are promoted through age tiers. Young objects are collected frequently; old objects less often. The write barrier detects when old objects reference young ones, adding them to the remembered set for re-scanning during minor GC.
- Minor GC: Collects only young generation objects
- Major GC: Full heap collection triggered by old object pressure or malloc limits
- Promotion: Objects survive collection cycles and age up
Mark-Sweep-Compact Phases
- Mark Phase: Traverses reachable objects from roots (stack, globals, threads) and marks them. Incremental marking allows interleaving with application code.
- Sweep Phase: Reclaims unmarked objects. Lazy sweep defers reclamation to reduce pause times.
- Compact Phase: Moves objects to reduce fragmentation and improve cache locality. Objects can be pinned to prevent movement.
Write Barriers
Write barriers intercept object assignments to maintain the remembered set:
void gc_writebarrier_generational(VALUE a, VALUE b);
When an old object a references a young object b, the barrier marks a as remembered. This avoids scanning the entire old generation during minor GC.
Heap Management
Objects are allocated from free lists within heap pages. When free slots drop below thresholds, the heap grows. The system tracks allocatable slots and manages page pooling for efficiency. Malloc-allocated memory is also tracked with separate limits to trigger GC when external allocations exceed thresholds.
Modular GC API
Ruby supports pluggable GC implementations via gc/gc_impl.h. The default implementation is production-ready; MMTk provides experimental alternatives. Custom implementations must provide marking, allocation, and compaction functions.
Finalization & Weak References
Objects with finalizers are tracked separately. Weak references allow non-owning pointers that don't prevent collection. Finalizers run after GC in a deferred phase to avoid complications during collection.
Threading, Ractors & Concurrency
Relevant Files
thread.cractor.cractor_core.hcont.cscheduler.cvm_sync.cthread_pthread.cractor_sync.c
Ruby's concurrency model has evolved significantly. The system now supports Ractors (isolated parallel execution units) alongside traditional Threads (concurrent execution within a single Ractor), with Fibers providing lightweight coroutines for I/O-bound work.
Core Concepts
Threads are the traditional concurrency primitive. Multiple threads can exist within a single Ractor and share memory, but they're scheduled cooperatively. Each thread has its own execution context (rb_execution_context_t) and stack.
Ractors are isolated execution units introduced in Ruby 3.0 for true parallelism. Each Ractor has its own GVL (Global VM Lock equivalent), thread scheduler, and object heap. Ractors cannot directly share mutable objects—only immutable or explicitly shareable objects can cross Ractor boundaries via message passing.
Fibers are lightweight coroutines managed by a scheduler. They enable non-blocking I/O patterns without OS thread overhead. The scheduler can be customized for different I/O models.
Synchronization Primitives
The VM uses a VM Lock (not the old GVL) to protect global state. With multiple Ractors, there's one lock per Ractor plus one for VM-wide operations. The lock is recursive and tracks ownership.
Blocking regions allow threads to release their lock during I/O operations. When a thread enters a blocking region (e.g., rb_thread_call_without_gvl), it marks itself as THREAD_STOPPED, increments the ractor's blocking counter, and can be interrupted via an unblock function.
VM Barriers synchronize all Ractors when needed (e.g., during GC). A barrier acquires the VM lock and ensures all other Ractors pause at safe points.
Thread Scheduling
Within a Ractor, threads are scheduled by rb_thread_sched. The scheduler maintains a ready queue and handles context switches. On pthread systems with M:N threading enabled, shared native threads (SNT) execute Ruby threads from multiple Ractors, enabling true parallelism.
Thread states include: THREAD_RUNNABLE (ready to run), THREAD_STOPPED (blocked on I/O), THREAD_STOPPED_FOREVER (waiting), and THREAD_KILLED (terminated).
Ractor Architecture
Loading diagram...
Each Ractor maintains a thread set, a scheduler, and synchronization primitives. Ractors communicate via message passing through ports. The main Ractor is special—it's created at VM startup and cannot be garbage collected.
Blocking and Interrupts
When a thread needs to block (e.g., on I/O), it calls rb_thread_call_without_gvl with an unblock function. This releases the lock, allowing other threads to run. If an interrupt arrives, the unblock function is called to wake the thread. Upon resuming, the thread re-acquires the lock and checks for pending interrupts.
Interrupts include signals, timeouts, and explicit thread interruption. They're queued per-thread and checked at safe points in the interpreter loop.
Fibers and Schedulers
Fibers are stackful coroutines with their own execution context. The scheduler interface (ruby/fiber/scheduler.h) allows custom implementations for different I/O models. The default scheduler uses kqueue (macOS/BSD) or epoll (Linux) to multiplex I/O across many fibers efficiently.
Fibers can yield control back to the scheduler, enabling thousands of concurrent I/O operations with minimal overhead compared to OS threads.
Built-in Classes & Data Types
Relevant Files
string.carray.chash.cnumeric.cre.cinternal/string.hinternal/array.hinternal/hash.h
Ruby's built-in data types are implemented in C with careful attention to memory efficiency and performance. Each type is represented as a VALUE (an opaque pointer-like handle) and backed by a C struct that extends RBasic.
String (RString)
Strings use a hybrid storage strategy to minimize memory overhead. Small strings are embedded directly in the object header, while larger strings use heap allocation. The RString struct tracks:
- Embedded vs. heap storage: Determined by the
RSTRING_NOEMBEDflag - Shared buffers: Multiple strings can reference the same underlying buffer via the
STR_SHAREDflag - Encoding: Each string carries its encoding information for proper character handling
- Frozen state: Immutable strings are marked and cannot be modified
// Embedded string (small, fast)
VALUE str = rb_str_new("hello", 5);
// Heap string (large data)
VALUE big = rb_str_new(large_ptr, large_len);
Array (RArray)
Arrays optimize for both small and large collections using embedded storage for small arrays. The RArray struct manages:
- Embedded elements: Arrays with <= 3 elements store data inline
- Heap-allocated buffer: Larger arrays use a separate buffer with capacity tracking
- Shared arrays: Arrays can share buffers with copy-on-write semantics
- Reference counting: Frozen arrays use infinite reference counts for optimization
VALUE ary = rb_ary_new_capa(10); // Pre-allocate capacity
rb_ary_store(ary, 0, INT2FIX(42));
Hash (RHash)
Hashes use a two-tier strategy: small hashes use an array table (AR) for cache efficiency, while larger hashes switch to a hash table (ST) for better scaling. The RHash struct includes:
- AR table: Compact inline storage for up to 8 key-value pairs
- ST table: Traditional hash table for larger collections
- Default value/proc: Support for default values or callable defaults
- Iteration level: Tracks nesting depth to prevent modification during iteration
VALUE hash = rb_hash_new();
rb_hash_aset(hash, rb_str_new("key", 3), INT2FIX(42));
Numeric Types
Ruby distinguishes between immediate and heap-allocated numbers:
- Fixnum (Immediate Integer): Small integers stored directly in the VALUE (no allocation)
- Bignum (Heap Integer): Large integers requiring heap storage
- Float: IEEE 64-bit floating-point numbers
- Rational: Exact rational numbers (numerator & denominator)
- Complex: Complex numbers (real & imaginary parts)
VALUE small = INT2FIX(42); // Immediate fixnum
VALUE big = rb_int_new(LLONG_MAX); // Bignum
VALUE flt = DBL2NUM(3.14); // Float
Regexp (RRegexp)
Regular expressions are compiled once and cached. The RRegexp struct stores:
- Compiled pattern: Oniguruma regex engine data
- Source string: Original pattern for inspection
- Flags: Case-insensitive, multiline, etc.
- Encoding: Pattern encoding for proper matching
VALUE pattern = rb_reg_new("\\d+", 2, 0);
VALUE match = rb_reg_match(pattern, str);
Type Checking & Optimization
Ruby uses fast inline macros for type checking:
if (RB_TYPE_P(obj, T_STRING)) { /* ... */ }
if (FIXNUM_P(obj)) { /* immediate integer */ }
if (RB_FLOAT_TYPE_P(obj)) { /* float */ }
The JIT compilers (YJIT/ZJIT) track type information to generate specialized code paths, making type checks nearly free in hot code.
JIT Compilation (YJIT & ZJIT)
Relevant Files
yjit.candyjit.h- C interface for YJITyjit/src/core.rs- Basic block versioning logicyjit/src/codegen.rs- Bytecode to machine code translationzjit.candzjit.h- C interface for ZJITzjit/src/codegen.rs- ZJIT code generationzjit/src/hir.rs- High-level intermediate representationjit.c- Unified JIT execution interface
Ruby has two JIT compilers: YJIT (Yet Another Ruby JIT) and ZJIT (an advanced prototype). Both compile bytecode to native machine code, but they use fundamentally different strategies.
YJIT: Basic Block Versioning
YJIT is a lightweight, minimalistic JIT that uses Basic Block Versioning (BBV) architecture. It lazily compiles code on-demand, creating specialized versions of basic blocks based on observed runtime types and conditions.
Key characteristics:
- Compiles at the basic block level (sequences of instructions with single entry/exit)
- Creates multiple versions of the same block for different type combinations
- Uses inline caches and type guards to specialize code
- Supported on x86-64 and arm64/aarch64 (macOS, Linux, BSD)
- Requires Rust >= 1.58.0
Architecture:
core.rsmanages block versioning, type tracking, and control flow graphscodegen.rstranslates YARV bytecode instructions to machine codeasm/contains platform-specific assemblers for x86-64 and arm64backend/generates low-level IR and machine code
ZJIT: Profile-Guided Method Compilation
ZJIT is an advanced prototype that compiles entire methods using profile information from the interpreter. It uses a higher-level intermediate representation (HIR) for more aggressive optimizations.
Key characteristics:
- Method-based compilation (whole-method optimization)
- Profile-guided optimization using runtime statistics
- Higher-level IR enables more sophisticated transformations
- Supported on same platforms as YJIT
- Requires Rust >= 1.85.0
Architecture:
hir.rsdefines a high-level intermediate representationcodegen.rsgenerates entry points and compiles methodsprofile.rscollects runtime profiling databackend/lir.rsconverts HIR to low-level IR for code generation
Execution Flow
Both JITs integrate into the interpreter via jit_exec() in vm.c:
// Attempt YJIT compilation first
if (rb_yjit_enabled_p) {
rb_jit_func_t func = yjit_compile(ec);
if (func) return func(ec, ec->cfp);
}
// Fall back to ZJIT if enabled
if (rb_zjit_entry) {
rb_jit_func_t func = zjit_compile(ec);
if (func) return zjit_entry(ec, ec->cfp, func);
}
Compilation Thresholds
Both JITs use call thresholds to decide when to compile:
- YJIT:
rb_yjit_call_threshold(default: 1000 calls) - ZJIT:
rb_zjit_call_thresholdandrb_zjit_profile_threshold
Code is compiled lazily when methods are called frequently enough, reducing startup overhead.
Memory Management
Both JITs allocate executable memory pools and manage code generation:
- Configurable via command-line options (
--yjit-mem-size, etc.) - Use code invalidation when assumptions (method lookups, constants) change
- Garbage collection of compiled code when memory pressure increases
Enabling JITs
Enable YJIT: ruby --yjit script.rb or RubyVM::YJIT.enable
Enable ZJIT: ruby --zjit script.rb (experimental)
Both can be disabled with --yjit-disable or --zjit-disable while preserving tuning options.
Extensions & Standard Library
Relevant Files
ext/- C extensions directorylib/- Pure Ruby standard librarydoc/standard_library.md- Standard library documentation
Ruby's standard library is organized into two main categories: C extensions (in ext/) and pure Ruby libraries (in lib/). These provide essential functionality beyond the core language.
C Extensions
C extensions are compiled native modules that provide performance-critical or system-level functionality. Key extensions include:
- JSON - Fast JSON parsing and generation with C acceleration
- Digest - Cryptographic hash functions (MD5, SHA1, SHA2, RMD160)
- Socket - Low-level network socket operations
- OpenSSL - SSL/TLS and cryptographic operations
- Psych - YAML parsing and serialization
- Date - Date and time handling with timezone support
- Zlib - Compression and decompression
- StringIO - In-memory I/O operations on strings
- Ripper - Ruby parser for syntax analysis
- Coverage - Code coverage measurement
- PTY - Pseudo-terminal management
Each extension lives in its own directory under ext/ with an extconf.rb file that generates a Makefile during compilation.
Pure Ruby Libraries
The lib/ directory contains pure Ruby implementations of standard library modules. These include:
- Bundler - Dependency management and gem bundling
- ERB - Embedded Ruby templating
- FileUtils - File system operations
- Net::HTTP - HTTP client library
- OptionParser - Command-line argument parsing
- Pathname - Object-oriented file path handling
- URI - Uniform Resource Identifier parsing
- Prism - Error-tolerant Ruby parser
- Delegate - Method delegation patterns
- Forwardable - Forwarding method calls
Library Organization
Loading diagram...
Default vs Bundled Gems
Default gems ship with Ruby and cannot be uninstalled but can be updated via RubyGems. Examples: JSON, Digest, Date, Psych.
Bundled gems are included but can be removed. Examples: Rake, Minitest, RBS, Debug, Matrix.
Extension Development
To create a C extension, place source files in ext/your_extension/ with an extconf.rb file. The build system automatically compiles extensions during Ruby installation using the mkmf module.