Skip to content

Jp/wip/fixes#141

Merged
melkyades merged 11 commits intomainfrom
jp/wip/fixes
May 5, 2026
Merged

Jp/wip/fixes#141
melkyades merged 11 commits intomainfrom
jp/wip/fixes

Conversation

@melkyades
Copy link
Copy Markdown
Contributor


Fix DNU dispatch, ProcessVMStack, and module error handling

DNU fix: The VM was looking up doesNotUnderstand: (1-arg) after already pushing a 2-arg frame. Changed to _doesNotUnderstand:with: so unhandled messages surface cleanly instead of crashing.

ProcessVMStack: Each process now owns an explicit buffer/bufferSize (slots 4/5). Previously stubbed-out primitives (InitializeWithNewBuffer/WithActiveBuffer, ContextSwitchTo, etc.) are fully implemented. GC correctly skips the active stack when scanning suspended ones.

Module load errors: failPrimitiveWith_ publishes the error string to Smalltalk fallback code. primitiveHostLoadModule[FromPath] catch C++ exceptions and signal them as Smalltalk Error. HostSystem>>suspended:because: logs and exits instead of calling a missing primitive.

Search paths: Both C++ and Smalltalk now use relative paths, keeping output readable from any launch directory.

Bootstrap errors: TonelReader includes file:line:col in parse errors; SourceModuleLoader annotates compile failures with "while compiling ClassName >> selector".

Minor: String class >> new, HostExit primitive.

melkyades added 10 commits May 5, 2026 00:39
Process>>initializeStack now creates a ProcessVMStack instead of a plain ProcessStack and instances are built via `ProcessVMStack on:` so the per-process VM buffer is set up at the right time.

Split basicInitialize into initializeWithNewBuffer and initializeWithActiveBuffer; pick one in ProcessVMStack>>process: based on the new isActive predicate on Process/ActiveProcess.

ProcessStack now keeps the last environment in a dedicated `env` instvar instead of stuffing it past the top of the stack. Context>>copyAllTo: walks `caller` (not the obsolete `parent`).
Mirrors the kernel-side split between ProcessStack ivars (process/sp/bp/env) and the ProcessVMStack-only buffer + bufferSize. KnownConstants offsets and the Runtime accessors are renamed accordingly (processStack*_ for the shared ivars, processVMStack{Buffer,BufferSize}_ for the rest).

EvaluationContext now tracks the size of its native stack and exposes bindToBuffer_size_/stackPointer_/environment_ so context switches can swap buffers in and out.

Implements the previously stubbed-out primitives: ProcessVMStackInitializeWithNewBuffer / WithActiveBuffer, ProcessVMStackBufferSize/AtPut/BpAtPut/PcAtPut, ProcessVMStackContextSwitchTo, PrepareForExecution (no-op in this runtime).

GarbageCollector: replace the placeholder scanFirstStackChunk_ with scanStack_sp_bp_ + scanSuspendedProcessStack_, and skip a suspended stack when its buffer is the one currently bound to the live evaluator context (already walked by scanCurrentContext).
Same as failPrimitive but also publishes an errorObject to the fallback Smalltalk code by writing it into the method's first temp (slot 1; temp indices are 1-based across the compiler/linearizer). The fallback method picks it up by declaring a | error | temp.
The VM pushes selector + Array(arguments) before invoking the DNU shim, so it must look up the 2-arg _doesNotUnderstand:with: method (provided on ProtoObject), not the 1-arg user-facing doesNotUnderstand:. Looking up the wrong selector left the call site with a mismatched stack and could crash; with the right shim, unhandled-message errors now surface cleanly via the standard exception path.

Drop the dead active snapshot ifNil: [^self] comment in KernelModule>>suspendBecause: while we are nearby.
…dule/HostLoadModuleFromPath)

Wrap both primitives in try/catch and use failPrimitiveWith_ to publish the error string to the fallback Smalltalk code. This ensures that parse errors and other exceptions during module load are surfaced as Smalltalk-level primitive failures, not C++ crashes.
Registers HostExit as a primitive. Implements primitiveHostExit to call std::exit with the given SmallInteger code (default 0). Returns nilObj for completeness, but this is unreachable. Allows Smalltalk code to terminate the VM process with a specific exit code.
Loader: reorder search prefixes so the closest match ('./') is tried first, matching the Smalltalk-side setupDefaultSearchPaths order.

Loader: replace error() calls with throw std::runtime_error so the module-not-found case is caught by the new try/catch wrappers in primitiveHostLoadModule[FromPath] and surfaced as a Smalltalk error.

HostSystem>>setupDefaultSearchPaths: drop the cwd prefix from each candidate and rely on relative paths only; this keeps the displayed search path short and readable regardless of where the binary is launched from.

HostSystem>>primitiveLoad: / primitiveLoadModuleFromPath: add a | error | temp so failPrimitiveWith_ can publish the exception message, then re-signal it as an Error.

HostSystem>>suspended:because: replace the unimplemented HostSuspendedBecause primitive with a direct log+exit sequence so unhandled errors produce a readable message before exiting.
TonelReader: add optional `filename` parameter to `parseFile`; store it in `_filename`. Replace bare `error_()` calls with `parseError_()`, which walks `_source` up to `_pos` to compute a 1-based line/column and throws `std::runtime_error("file:line:col: message")`.

Bootstrapper / SourceModuleLoader: pass `entry.path().string()` as the filename so every parse error now names the .st file and position.

SourceModuleLoader>>createMethodsOf_: wrap each `createNewMethod_` call in a try/catch that delegates to the new `rethrowWithContext_` static helper, which appends "while compiling ClassName >> <first-line-of-source>" to the rethrown exception, giving a clear breadcrumb from a compile error back to the failing file and method.
String>>new was missing; sending `new` to String would fall through to Object>>new, which allocates a fixed-size object rather than a byte object. Delegate to `new: 0` to create an empty, properly-formatted String instead.
- Add primitiveProcessVMStackInitializeWithActiveBuffer and primitiveProcessVMStackInitializeWithNewBuffer following kernel process changes (should check if they work as expected)
- Fix kernel and kernel prim paths
Implements primitiveHostExit: reads the first argument, calls std::exit with its SmallInteger value (default 0), and returns nilObj for completeness (unreachable). Registers the primitive under the name 'HostExit'. Declares it in Evaluator.h.
@melkyades melkyades merged commit b062093 into main May 5, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant