From 48b736845ad07481acde30dda38e6c7d2937970e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Pim=C3=A1s?= Date: Tue, 5 May 2026 21:37:14 -0300 Subject: [PATCH 1/2] Fix DNU dispatch so unknown selectors raise cleanly Sending a selector that wasn't already interned in the kernel symbol table caused a crash instead of a MessageNotUnderstood, because Runtime::sendLocal_to_* and Evaluator::messageNotUnderstood_ used existingSymbolFrom_, which returns null for unknown selectors. The null was then dispatched as a real symbol and crashed in the lookup machinery. Switch sendLocal_to_* and the DNU lookup to addSymbol_ so the symbol is created on demand. ProtoObject>>doesNotUnderstand: now passes the receiver into MessageNotUnderstood so the error has a proper description and isResumable behaves correctly. Main wraps the launcher in a try/catch so a top-level error_ surfaces as a clean "Error: ..." message. Reproduces with `./egg Tonel`, which previously crashed and now prints `Error: #main: not understood by a TonelModule`. --- modules/Kernel/ProtoObject.st | 2 +- modules/Tonel/TonelModule.st | 4 ++-- runtime/cpp/Evaluator/Evaluator.cpp | 2 +- runtime/cpp/Evaluator/Runtime.cpp | 6 +++--- runtime/cpp/Main.cpp | 15 +++++++++++++-- 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/modules/Kernel/ProtoObject.st b/modules/Kernel/ProtoObject.st index d32b96cf..76acc16b 100644 --- a/modules/Kernel/ProtoObject.st +++ b/modules/Kernel/ProtoObject.st @@ -251,7 +251,7 @@ ProtoObject >> class [ { #category : #system } ProtoObject >> doesNotUnderstand: aMessage [ - ^MessageNotUnderstood message: aMessage + ^MessageNotUnderstood message: aMessage receiver: self ] { #category : #errors } diff --git a/modules/Tonel/TonelModule.st b/modules/Tonel/TonelModule.st index 79873345..c49c78df 100644 --- a/modules/Tonel/TonelModule.st +++ b/modules/Tonel/TonelModule.st @@ -9,6 +9,6 @@ TonelModule >> imports [ ^{ #STON -> #(#STONReader #STONWriter #STON). #Compiler -> #(#SSmalltalkParser). - #Kernel -> #(#OrderedDictionary #ReadStream) + #Kernel -> #(#Module #OrderedDictionary #ReadStream) } -] \ No newline at end of file +] diff --git a/runtime/cpp/Evaluator/Evaluator.cpp b/runtime/cpp/Evaluator/Evaluator.cpp index 9ee917e0..24e601ba 100644 --- a/runtime/cpp/Evaluator/Evaluator.cpp +++ b/runtime/cpp/Evaluator/Evaluator.cpp @@ -318,7 +318,7 @@ void Egg::Evaluator::messageNotUnderstood_(SAbstractMessage *message) auto array = _runtime->newArray_(args); _context->push_(message->selector()); _context->push_((Object*)array); - auto symbol = _runtime->existingSymbolFrom_("_doesNotUnderstand:with:"); + auto symbol = (Object*)_runtime->addSymbol_("_doesNotUnderstand:with:"); auto behavior = _runtime->behaviorOf_(_regR); auto dnu = _runtime->lookup_startingAt_((Object*)symbol, behavior); if (!dnu) diff --git a/runtime/cpp/Evaluator/Runtime.cpp b/runtime/cpp/Evaluator/Runtime.cpp index 634909a4..b6ef13a5 100644 --- a/runtime/cpp/Evaluator/Runtime.cpp +++ b/runtime/cpp/Evaluator/Runtime.cpp @@ -209,12 +209,12 @@ uintptr_t Runtime::hashFor_(Object *anObject) } Object* Runtime::sendLocal_to_withArgs_(const std::string &selector, Object *receiver, std::vector &arguments) { - auto symbol = this->existingSymbolFrom_(selector); + auto symbol = (Object*)this->addSymbol_(selector); return this->_evaluator->send_to_with_(symbol, receiver, arguments); } Object* Runtime::sendLocal_to_with_(const std::string &selector, Object *receiver, Object* arg1) { - auto symbol = this->existingSymbolFrom_(selector); + auto symbol = (Object*)this->addSymbol_(selector); std::vector args; args.push_back(arg1); @@ -222,7 +222,7 @@ Object* Runtime::sendLocal_to_with_(const std::string &selector, Object *receive } Object* Runtime::sendLocal_to_with_with_(const std::string &selector, Object *receiver, Object *arg1, Object* arg2) { - auto symbol = this->existingSymbolFrom_(selector); + auto symbol = (Object*)this->addSymbol_(selector); std::vector args; args.push_back(arg1); args.push_back(arg2); diff --git a/runtime/cpp/Main.cpp b/runtime/cpp/Main.cpp index acf6b7a5..bb2d057e 100644 --- a/runtime/cpp/Main.cpp +++ b/runtime/cpp/Main.cpp @@ -6,8 +6,19 @@ #include "Launcher.h" +#include +#include + int main(const int argc, const char** argv) { - Egg::Launcher launcher; - return launcher.main(argc, argv); + Egg::Launcher launcher; + try { + return launcher.main(argc, argv); + } catch (const std::exception& e) { + std::cerr << "Error: " << e.what() << std::endl; + return 1; + } catch (...) { + std::cerr << "Error: unknown exception reached top level" << std::endl; + return 1; + } } From 9a8b094603f860c6d339da602c5d7cab1045673b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Pim=C3=A1s?= Date: Tue, 5 May 2026 21:38:43 -0300 Subject: [PATCH 2/2] Skip undermessage registration when symbol is missing Evaluator::addUndermessage previously stored entries keyed by null when the corresponding selector wasn't yet interned, so any send whose selector resolved to null collided with a stale undermessage entry (e.g. _remainderTowardZero:). Skip registration in that case. This is a temporary safeguard; the proper fix is to ensure all undermessage selectors are present in the kernel symbol table at Evaluator construction time. --- runtime/cpp/Evaluator/Evaluator.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/runtime/cpp/Evaluator/Evaluator.cpp b/runtime/cpp/Evaluator/Evaluator.cpp index 24e601ba..569b0a5c 100644 --- a/runtime/cpp/Evaluator/Evaluator.cpp +++ b/runtime/cpp/Evaluator/Evaluator.cpp @@ -69,6 +69,12 @@ void Evaluator::addPrimitive(const std::string &name, Evaluator::PrimitivePointe void Evaluator::addUndermessage(const std::string &name, UndermessagePointer primitive) { Object *symbol = _runtime->existingSymbolFrom_(name); + if (!symbol) { + // Symbol does not (yet) exist in the kernel symbol table; skip registration + // to avoid storing the undermessage under a nullptr key, which would later + // be matched by any send whose selector cannot be resolved. + return; + } _undermessages[symbol] = primitive; }