载入中...
搜索中...
未找到
Runtime.cpp
浏览该文件的文档.
1#include "common/Runtime.h"
2
3#include "common/Assert.h"
4#include "common/Module.h"
5
6#include <algorithm>
7#include <sstream>
8#include <utility>
9
10namespace eve {
11namespace {
12
13thread_local std::vector<Runtime*> runtime_stack;
14
15const char* stageName(ScriptStage stage) {
16 switch (stage) {
17 case ScriptStage::Compile: return "compile";
18 case ScriptStage::Execute: return "execute";
19 case ScriptStage::Reflect: return "reflect";
20 case ScriptStage::Unload: return "unload";
21 case ScriptStage::Shutdown: return "shutdown";
22 }
23 return "unknown";
24}
25
26std::string valueString(HSQUIRRELVM vm, SQInteger index) {
27 switch (sq_gettype(vm, index)) {
28 case OT_NULL: return "null";
29 case OT_BOOL: {
30 SQBool value = SQFalse;
31 return SQ_SUCCEEDED(sq_getbool(vm, index, &value)) && value ? "true" : "false";
32 }
33 case OT_INTEGER: {
34 SQInteger value = 0;
35 if (SQ_SUCCEEDED(sq_getinteger(vm, index, &value))) return std::to_string(value);
36 break;
37 }
38 case OT_FLOAT: {
39 SQFloat value = 0;
40 if (SQ_SUCCEEDED(sq_getfloat(vm, index, &value))) {
41 std::ostringstream out;
42 out << value;
43 return out.str();
44 }
45 break;
46 }
47 case OT_STRING: {
48 const SQChar* value = nullptr;
49 if (SQ_SUCCEEDED(sq_getstring(vm, index, &value)) && value) return value;
50 break;
51 }
52 default: break;
53 }
54 return {};
55}
56
57SQUserPointer objectIdentity(const HSQOBJECT& object) {
58 return reinterpret_cast<SQUserPointer>(object._unVal.pRefCounted);
59}
60
61std::unique_ptr<ssq::VM> createVm(size_t stackSize, ssq::Libs::Flag libraries) {
62 // Validate before constructing the VM: with assertions compiled out the
63 // check must not be the only thing standing between a bad stack size and a
64 // half-constructed runtime.
65 EV_PARAM_CHECK(stackSize > 0, "Runtime stack size must be positive");
66 return std::make_unique<ssq::VM>(stackSize, libraries);
67}
68
70SQInteger scriptErrorHook(HSQUIRRELVM vm) {
71 script::ScriptErrorContext ctx = script::captureScriptError(vm);
72 script::setLastScriptError(vm, std::move(ctx));
73 return 0;
74}
75
76} // namespace
77
80 std::string source_text;
81 bool from_file = false;
82 std::unique_ptr<ssq::Script> compiled;
83 std::unordered_map<std::string, ssq::Class> class_objects;
84};
85
86ScriptException::ScriptException(ScriptStage stage, std::string source, uint64_t scriptId,
87 const std::string& message)
88 : std::runtime_error([&] {
89 std::ostringstream out;
90 out << "Script " << stageName(stage);
91 if (!source.empty()) out << " failed in '" << source << "'";
92 if (scriptId != 0) out << " [id=" << scriptId << "]";
93 out << ": " << message;
94 return out.str();
95 }()),
96 stage_(stage), source_(std::move(source)), script_id_(scriptId) {}
97
98ScriptException::ScriptException(ScriptStage stage, std::string source, uint64_t scriptId,
99 const script::ScriptErrorContext& context)
100 : std::runtime_error([&] {
101 std::ostringstream out;
102 out << "Script " << stageName(stage);
103 if (!source.empty()) out << " failed in '" << source << "'";
104 if (scriptId != 0) out << " [id=" << scriptId << "]";
105 out << ": " << script::formatScriptError(context);
106 return out.str();
107 }()),
108 stage_(stage),
109 source_(std::move(source)),
110 script_id_(scriptId),
111 line_(context.line),
112 column_(context.column),
113 function_(context.function),
114 stack_trace_(script::formatStackTrace(context.stack)),
115 reported_(context.reported) {
116 if (!stack_trace_.empty() && stack_trace_.back() == '\n') stack_trace_.pop_back();
117}
118
120 : vm_(runtime.handle()), top_(vm_ ? sq_gettop(vm_) : 0) {}
121
123 if (vm_) sq_settop(vm_, top_);
124}
125
127 : vm_(std::exchange(other.vm_, nullptr)), top_(other.top_) {}
128
130 if (this == &other) return *this;
131 if (vm_) sq_settop(vm_, top_);
132 vm_ = std::exchange(other.vm_, nullptr);
133 top_ = other.top_;
134 return *this;
135}
136
137Runtime::Scope::Scope(Runtime& runtime) : runtime_(&runtime) { runtime_stack.push_back(&runtime); }
138
140 if (!runtime_) return;
141 auto it = std::find(runtime_stack.rbegin(), runtime_stack.rend(), runtime_);
142 if (it != runtime_stack.rend()) runtime_stack.erase(std::next(it).base());
143}
144
146 : runtime_(std::exchange(other.runtime_, nullptr)) {}
147
148Runtime::Runtime(size_t stackSize, ssq::Libs::Flag libraries)
149 : vm_(createVm(stackSize, libraries)) {
150 installErrorHandler();
151}
152
154
156 if (initialized_) return;
157 auto scope = enter();
158 auto stack = guard();
159 try {
161 initialized_ = true;
162 } catch (const std::exception& error) {
164 fail(ScriptStage::Execute, "<module reflection>", 0, error);
165 }
166}
167
168void Runtime::installErrorHandler() {
169 if (!vm_) return;
171 sq_newclosure(vm, &scriptErrorHook, 0); // pushes the closure
172 sq_seterrorhandler(vm); // pops the closure
173}
174
175void Runtime::shutdown() noexcept {
176 if (shutting_down_ || stopped_) return;
177 shutting_down_ = true;
178 unloadAll();
181 while (true) {
182 auto it = std::find(runtime_stack.begin(), runtime_stack.end(), this);
183 if (it == runtime_stack.end()) break;
184 runtime_stack.erase(it);
185 }
186 classes_.clear();
187 class_owners_.clear();
188 initialized_ = false;
189 vm_.reset();
190 stopped_ = true;
191 shutting_down_ = false;
192}
193
194bool Runtime::initialized() const noexcept { return initialized_; }
195ssq::VM& Runtime::vm() noexcept { return *vm_; }
196const ssq::VM& Runtime::vm() const noexcept { return *vm_; }
197HSQUIRRELVM Runtime::handle() const noexcept { return vm_ ? vm_->getHandle() : nullptr; }
198ssq::Table Runtime::root() const { return ssq::Table(static_cast<const ssq::Object&>(*vm_)); }
199ssq::Table Runtime::table(const char* name) const {
200 const bool validName = name != nullptr && name[0] != '\0';
201 EV_PARAM_CHECK(validName, "table name must not be null or empty");
202 return ssq::Table(vm_->find(name));
203}
204
206 return runtime_stack.empty() ? nullptr : runtime_stack.back();
207}
208
209size_t Runtime::stackDepth() noexcept { return runtime_stack.size(); }
210
211Runtime::ScriptId Runtime::compileSource(std::string source, std::string sourceName) {
212 if (stopped_) throw ScriptException(ScriptStage::Compile, sourceName, 0, "runtime is shut down");
213 auto scope = enter();
214 auto stack = guard();
215 const ScriptId id = next_script_id_++;
216 auto record = std::make_unique<ScriptRecord>();
217 record->info.id = id;
218 record->info.source = sourceName;
219 record->source_text = std::move(source);
220 try {
221 record->compiled =
222 std::make_unique<ssq::Script>(vm_->compileSource(record->source_text.c_str(), sourceName.c_str()));
223 auto [it, inserted] = scripts_.emplace(id, std::move(record));
224 (void)inserted;
225 notifyLifecycle(it->second->info);
226 return id;
227 } catch (const std::exception& error) {
228 script::ScriptErrorContext ctx = compileErrorContext(error.what(), record->source_text);
229 fail(ScriptStage::Compile, sourceName, id, std::move(ctx));
230 }
231}
232
233Runtime::ScriptId Runtime::compileFile(const std::string& path) {
234 const bool validPath = !path.empty();
235 EV_PARAM_CHECK(validPath, "script file path must not be empty");
236 if (stopped_) throw ScriptException(ScriptStage::Compile, path, 0, "runtime is shut down");
237 auto scope = enter();
238 auto stack = guard();
239 const ScriptId id = next_script_id_++;
240 auto record = std::make_unique<ScriptRecord>();
241 record->info.id = id;
242 record->info.source = path;
243 record->source_text = path;
244 record->from_file = true;
245 try {
246 record->compiled = std::make_unique<ssq::Script>(vm_->compileFile(path.c_str()));
247 auto [it, inserted] = scripts_.emplace(id, std::move(record));
248 (void)inserted;
249 notifyLifecycle(it->second->info);
250 return id;
251 } catch (const std::exception& error) {
252 script::ScriptErrorContext ctx = compileErrorContext(error.what(), {});
253 fail(ScriptStage::Compile, path, id, std::move(ctx));
254 }
255}
256
258 auto found = scripts_.find(id);
259 if (found == scripts_.end())
260 throw ScriptException(ScriptStage::Execute, {}, id, "unknown script");
261 ScriptRecord& record = *found->second;
262 if (!record.compiled || record.info.state == ScriptState::Unloaded)
263 throw ScriptException(ScriptStage::Execute, record.info.source, id, "script is unloaded");
264
265 auto scope = enter();
266 auto stack = guard();
267 const auto before = rootClasses();
268 record.info.state = ScriptState::Running;
269 record.info.error.clear();
270 notifyLifecycle(record.info);
271 try {
272 // Drive the Squirrel call directly instead of ssq::VM::run(): when a
273 // DevTool hook replaces the VM's error handler, ssq never populates its
274 // stored RuntimeException and would dereference a null unique_ptr on
275 // failure. The raw call reports through the installed handler (which
276 // captures the live stack) and lets us throw an enriched ScriptException.
278 sq_pushobject(vm, record.compiled->getRaw());
279 sq_pushroottable(vm);
280 const SQRESULT result = sq_call(vm, 1, SQFalse, SQTrue);
281 if (SQ_FAILED(result)) {
283 if (ctx.empty()) ctx.message = "script error";
284 try {
285 discoverClasses(record, before);
286 } catch (...) {
287 }
288 record.info.state = ScriptState::Failed;
289 record.info.error = script::formatScriptError(ctx);
290 notifyLifecycle(record.info);
291 fail(ScriptStage::Execute, record.info.source, id, std::move(ctx));
292 }
293 discoverClasses(record, before);
294 record.info.state = ScriptState::Loaded;
295 notifyLifecycle(record.info);
296 return record.info;
297 } catch (const ScriptException&) {
298 throw;
299 } catch (const std::exception& error) {
300 try {
301 discoverClasses(record, before);
302 } catch (...) {
303 }
304 record.info.state = ScriptState::Failed;
305 record.info.error = error.what();
306 notifyLifecycle(record.info);
307 fail(ScriptStage::Execute, record.info.source, id, error);
308 }
309}
310
311Runtime::ScriptId Runtime::runSource(std::string source, std::string sourceName) {
312 const ScriptId id = compileSource(std::move(source), std::move(sourceName));
313 execute(id);
314 return id;
315}
316
317Runtime::ScriptId Runtime::runFile(const std::string& path) {
318 const ScriptId id = compileFile(path);
319 execute(id);
320 return id;
321}
322
323const ReflectedClass& Runtime::reflectClass(const std::string& name, const std::string& source) {
324 const bool validName = !name.empty();
325 EV_PARAM_CHECK(validName, "reflected class name must not be empty");
326 auto scope = enter();
327 auto stack = guard();
328 try {
329 ssq::Class cls = vm_->findClass(name.c_str());
330 classes_[name] = inspectClass(name, cls, source);
331 return classes_.at(name);
332 } catch (const std::exception& error) {
333 fail(ScriptStage::Reflect, source.empty() ? name : source, 0, error);
334 }
335}
336
338 auto found = scripts_.find(id);
339 if (found == scripts_.end())
340 throw ScriptException(ScriptStage::Compile, {}, id, "unknown script");
341 const bool fromFile = found->second->from_file;
342 const std::string input = found->second->source_text;
343 const std::string source = found->second->info.source;
344 unload(id);
345 return fromFile ? runFile(input) : runSource(input, source);
346}
347
349 auto found = scripts_.find(id);
350 if (found == scripts_.end()) return false;
351 ScriptRecord& record = *found->second;
352 if (record.info.state == ScriptState::Unloaded) return false;
353 auto scope = enter();
354 auto stack = guard();
356 notifyLifecycle(record.info);
357 try {
358 for (const auto& pair : record.class_objects) {
359 auto owner = class_owners_.find(pair.first);
360 if (owner == class_owners_.end() || owner->second != id) continue;
361 ssq::Object currentObject = vm_->find(pair.first.c_str());
362 if (currentObject.getType() == ssq::Type::CLASS &&
363 objectIdentity(currentObject.getRaw()) == objectIdentity(pair.second.getRaw())) {
364 sq_pushroottable(handle());
365 sq_pushstring(handle(), pair.first.c_str(), static_cast<SQInteger>(pair.first.size()));
366 sq_rawdeleteslot(handle(), -2, SQFalse);
367 sq_pop(handle(), 1);
368 }
369 classes_.erase(pair.first);
370 class_owners_.erase(owner);
371 }
372 record.class_objects.clear();
373 record.compiled.reset();
375 notifyLifecycle(record.info);
376 return true;
377 } catch (const std::exception& error) {
379 record.info.error = error.what();
380 notifyLifecycle(record.info);
381 fail(ScriptStage::Unload, record.info.source, id, error);
382 }
383}
384
385void Runtime::unloadAll() noexcept {
386 std::vector<ScriptId> ids;
387 ids.reserve(scripts_.size());
388 for (const auto& pair : scripts_) ids.push_back(pair.first);
389 std::reverse(ids.begin(), ids.end());
390 for (ScriptId id : ids) {
391 try {
392 unload(id);
393 } catch (...) {
394 }
395 }
396 scripts_.clear();
397}
398
399bool Runtime::contains(ScriptId id) const noexcept { return scripts_.count(id) != 0; }
400
401const ScriptInfo* Runtime::script(ScriptId id) const noexcept {
402 auto found = scripts_.find(id);
403 return found == scripts_.end() ? nullptr : &found->second->info;
404}
405
406std::vector<ScriptInfo> Runtime::scripts() const {
407 std::vector<ScriptInfo> result;
408 result.reserve(scripts_.size());
409 for (const auto& pair : scripts_) result.push_back(pair.second->info);
410 std::sort(result.begin(), result.end(), [](const ScriptInfo& a, const ScriptInfo& b) {
411 return a.id < b.id;
412 });
413 return result;
414}
415
416const ReflectedClass* Runtime::reflectedClass(const std::string& name) const noexcept {
417 auto found = classes_.find(name);
418 return found == classes_.end() ? nullptr : &found->second;
419}
420
421std::vector<ReflectedClass> Runtime::reflectedClasses() const {
422 std::vector<ReflectedClass> result;
423 result.reserve(classes_.size());
424 for (const auto& pair : classes_) result.push_back(pair.second);
425 std::sort(result.begin(), result.end(), [](const ReflectedClass& a, const ReflectedClass& b) {
426 return a.name < b.name;
427 });
428 return result;
429}
430
431ssq::Class Runtime::findClass(const std::string& name) const {
432 const bool validName = !name.empty();
433 EV_PARAM_CHECK(validName, "class name must not be empty");
434 return vm_->findClass(name.c_str());
435}
436
437void Runtime::notifyLifecycle(const ScriptInfo& info) noexcept {
438 if (!lifecycle_handler_) return;
439 try {
440 lifecycle_handler_(info);
441 } catch (...) {
442 }
443}
444
445script::ScriptErrorContext Runtime::compileErrorContext(const std::string& what,
446 const std::string& sourceText) {
447 script::ScriptErrorContext ctx;
448 ctx.message = what;
449 if (!script::parseCompileError(what, &ctx.source, &ctx.line, &ctx.column, &ctx.message))
450 return ctx;
451 const std::string lineText = script::sourceLineText(sourceText, ctx.line);
452 if (lineText.empty()) return ctx;
453 std::string hint = std::to_string(ctx.line) + " | " + lineText;
454 if (ctx.column > 0) {
455 hint += "\n";
456 const size_t caret = static_cast<size_t>(ctx.column > 1 ? ctx.column - 1 : 0);
457 hint.append(caret, ' ');
458 hint += '^';
459 }
460 ctx.hint = std::move(hint);
461 return ctx;
462}
463
464[[noreturn]] void Runtime::fail(ScriptStage stage, const std::string& source, ScriptId id,
465 script::ScriptErrorContext context) {
466 ScriptException wrapped(stage, source, id, context);
467 if (error_handler_) {
468 try {
469 error_handler_(wrapped);
470 } catch (...) {
471 }
472 }
473 throw wrapped;
474}
475
476[[noreturn]] void Runtime::fail(ScriptStage stage, const std::string& source, ScriptId id,
477 const std::exception& error) {
478 script::ScriptErrorContext ctx = script::takeLastScriptError(handle());
479 if (ctx.empty()) ctx.message = error.what();
480 fail(stage, source, id, std::move(ctx));
481}
482
483std::unordered_map<std::string, SQUserPointer> Runtime::rootClasses() const {
484 std::unordered_map<std::string, SQUserPointer> result;
485 StackGuard stack(*const_cast<Runtime*>(this));
486 HSQUIRRELVM squirrel = handle();
487 sq_pushroottable(squirrel);
488 sq_pushnull(squirrel);
489 while (SQ_SUCCEEDED(sq_next(squirrel, -2))) {
490 if (sq_gettype(squirrel, -2) == OT_STRING && sq_gettype(squirrel, -1) == OT_CLASS) {
491 const SQChar* name = nullptr;
492 HSQOBJECT object;
493 if (SQ_SUCCEEDED(sq_getstring(squirrel, -2, &name)) && name &&
494 SQ_SUCCEEDED(sq_getstackobj(squirrel, -1, &object)))
495 result[name] = objectIdentity(object);
496 }
497 sq_pop(squirrel, 2);
498 }
499 return result;
500}
501
502void Runtime::discoverClasses(
503 ScriptRecord& record, const std::unordered_map<std::string, SQUserPointer>& before) {
504 const auto after = rootClasses();
505 for (const auto& pair : after) {
506 auto old = before.find(pair.first);
507 if (old != before.end() && old->second == pair.second) continue;
508 ssq::Class cls = vm_->findClass(pair.first.c_str());
509 record.class_objects.insert_or_assign(pair.first, cls);
510 if (std::find(record.info.classes.begin(), record.info.classes.end(), pair.first) ==
511 record.info.classes.end())
512 record.info.classes.push_back(pair.first);
513 classes_[pair.first] = inspectClass(pair.first, cls, record.info.source);
514 class_owners_[pair.first] = record.info.id;
515 }
516 std::sort(record.info.classes.begin(), record.info.classes.end());
517
518 // Resolve base class names after every newly defined class is registered.
519 for (const std::string& name : record.info.classes) {
520 auto info = classes_.find(name);
521 auto cls = record.class_objects.find(name);
522 if (info == classes_.end() || cls == record.class_objects.end()) continue;
523 StackGuard stack(*this);
524 sq_pushobject(handle(), cls->second.getRaw());
525 if (SQ_SUCCEEDED(sq_getbase(handle(), -1)) && sq_gettype(handle(), -1) == OT_CLASS) {
526 HSQOBJECT baseObject;
527 if (SQ_SUCCEEDED(sq_getstackobj(handle(), -1, &baseObject))) {
528 const SQUserPointer identity = objectIdentity(baseObject);
529 for (const auto& candidate : after) {
530 if (candidate.second == identity) {
531 info->second.base = candidate.first;
532 break;
533 }
534 }
535 }
536 }
537 }
538}
539
540ReflectedClass Runtime::inspectClass(const std::string& name, const ssq::Class& cls,
541 const std::string& source) const {
542 ReflectedClass info;
543 info.name = name;
544 info.source = source;
545 StackGuard stack(*const_cast<Runtime*>(this));
546 HSQUIRRELVM squirrel = handle();
547 sq_pushobject(squirrel, cls.getRaw());
548 sq_pushnull(squirrel);
549 while (SQ_SUCCEEDED(sq_next(squirrel, -2))) {
550 if (sq_gettype(squirrel, -2) == OT_STRING) {
551 const SQChar* memberName = nullptr;
552 if (SQ_SUCCEEDED(sq_getstring(squirrel, -2, &memberName)) && memberName) {
553 ReflectedMember member;
554 member.name = memberName;
555 member.type = static_cast<ssq::Type>(sq_gettype(squirrel, -1));
556 member.method = member.type == ssq::Type::CLOSURE ||
557 member.type == ssq::Type::NATIVECLOSURE;
558
559 // sq_getattributes consumes the key at the stack top and replaces it
560 // with the attribute table (or null).
561 const SQInteger memberTop = sq_gettop(squirrel);
562 sq_push(squirrel, -2);
563 if (SQ_SUCCEEDED(sq_getattributes(squirrel, -5)) &&
564 sq_gettype(squirrel, -1) == OT_TABLE) {
565 sq_pushnull(squirrel);
566 while (SQ_SUCCEEDED(sq_next(squirrel, -2))) {
567 if (sq_gettype(squirrel, -2) == OT_STRING) {
568 const SQChar* attributeName = nullptr;
569 if (SQ_SUCCEEDED(sq_getstring(squirrel, -2, &attributeName)) &&
570 attributeName) {
571 ReflectedAttribute attribute;
572 attribute.name = attributeName;
573 attribute.type =
574 static_cast<ssq::Type>(sq_gettype(squirrel, -1));
575 attribute.value = valueString(squirrel, -1);
576 member.attributes.push_back(std::move(attribute));
577 }
578 }
579 sq_pop(squirrel, 2);
580 }
581 }
582 sq_settop(squirrel, memberTop);
583 std::sort(member.attributes.begin(), member.attributes.end(),
584 [](const ReflectedAttribute& a, const ReflectedAttribute& b) {
585 return a.name < b.name;
586 });
587 info.members.push_back(std::move(member));
588 }
589 }
590 sq_pop(squirrel, 2);
591 }
592 std::sort(info.members.begin(), info.members.end(),
593 [](const ReflectedMember& a, const ReflectedMember& b) { return a.name < b.name; });
594 return info;
595}
596
597} // namespace eve
EVEngine assertion entry point, backed by zeroerr.
#define EV_PARAM_CHECK(cond,...)
Validate a function parameter / public API precondition.
Definition Assert.h:30
struct SQVM * HSQUIRRELVM
std::string value
HSQUIRRELVM vm
Definition ECS.cpp:20
HSQOBJECT cls
Definition ECS.cpp:21
std::string id
std::string error
JobScope scope
uint32_t a
uint32_t b
const char * name
Definition RockMesh.cpp:21
static void detach(Runtime *runtime)
Clears the active runtime if it matches; called during shutdown.
Definition Module.cpp:75
static void expose(Runtime &runtime)
Exposes every registered module into the given runtime's root table.
Definition Module.cpp:58
Pushes this Runtime on the current thread's runtime stack.
Definition Runtime.h:130
Scope(Runtime &runtime)
Definition Runtime.cpp:137
Restores the native Squirrel stack when a binding operation leaves scope.
Definition Runtime.h:112
StackGuard & operator=(StackGuard &&other) noexcept
Definition Runtime.cpp:129
StackGuard(Runtime &runtime) noexcept
Definition Runtime.cpp:119
void shutdown() noexcept
Tears down the VM and detaches the runtime; idempotent and noexcept.
Definition Runtime.cpp:175
ssq::Class findClass(const std::string &name) const
Finds a script class by name (throws if it does not exist).
Definition Runtime.cpp:431
ScriptId runFile(const std::string &path)
Convenience: compileFile() then execute().
Definition Runtime.cpp:317
void unloadAll() noexcept
Unloads every script.
Definition Runtime.cpp:385
static Runtime * current() noexcept
The runtime currently at the top of this thread's runtime stack, or nullptr.
Definition Runtime.cpp:205
ssq::Table table(const char *name) const
Looks up a named global table.
Definition Runtime.cpp:199
bool contains(ScriptId id) const noexcept
True if a script with the given id is tracked.
Definition Runtime.cpp:399
ScriptId compileSource(std::string source, std::string sourceName="buffer")
Compiles script source without running it.
Definition Runtime.cpp:211
bool unload(ScriptId id)
Unloads a script and removes its declared classes; false if unknown/unloaded.
Definition Runtime.cpp:348
ScriptId reload(ScriptId id)
Recompiles and re-runs a script from its original source.
Definition Runtime.cpp:337
Runtime(size_t stackSize=2048, ssq::Libs::Flag libraries=ssq::Libs::ALL)
Creates a script runtime with its own Squirrel VM.
Definition Runtime.cpp:148
std::vector< ReflectedClass > reflectedClasses() const
All reflected classes, sorted by name.
Definition Runtime.cpp:421
const ReflectedClass * reflectedClass(const std::string &name) const noexcept
Reflected class by name, or nullptr.
Definition Runtime.cpp:416
static size_t stackDepth() noexcept
Depth of the thread-local runtime stack.
Definition Runtime.cpp:209
ScriptId runSource(std::string source, std::string sourceName="buffer")
Convenience: compileSource() then execute().
Definition Runtime.cpp:311
StackGuard guard() noexcept
RAII guard that restores the Squirrel stack top on scope exit.
Definition Runtime.h:176
bool initialized() const noexcept
True once initialize() has completed successfully.
Definition Runtime.cpp:194
ScriptId compileFile(const std::string &path)
Compiles a script file without running it.
Definition Runtime.cpp:233
void initialize()
Exposes registered engine modules into the script root table. Safe to call more than once.
Definition Runtime.cpp:155
uint64_t ScriptId
Definition Runtime.h:107
const ReflectedClass & reflectClass(const std::string &name, const std::string &source={})
Reflects a class by name, storing its inspected members.
Definition Runtime.cpp:323
const ScriptInfo * script(ScriptId id) const noexcept
Script metadata for an id, or nullptr if unknown.
Definition Runtime.cpp:401
HSQUIRRELVM handle() const noexcept
Raw Squirrel VM handle; nullptr after shutdown.
Definition Runtime.cpp:197
ssq::Table root() const
Root script table of the VM.
Definition Runtime.cpp:198
Scope enter()
Pushes this runtime on the thread-local runtime stack (RAII pop).
Definition Runtime.h:178
ssq::VM & vm() noexcept
Underlying SimpleSquirrel VM (requires a live, initialized runtime).
Definition Runtime.cpp:195
std::vector< ScriptInfo > scripts() const
Metadata for all tracked scripts, sorted by id.
Definition Runtime.cpp:406
const ScriptInfo & execute(ScriptId id)
Runs a previously compiled script; throws ScriptException on failure.
Definition Runtime.cpp:257
Exception raised at the public Runtime boundary.
Definition Runtime.h:36
ScriptStage stage() const noexcept
Definition Runtime.h:50
const std::string & source() const noexcept
Definition Runtime.h:51
ScriptException(ScriptStage stage, std::string source, uint64_t scriptId, const std::string &message)
Definition Runtime.cpp:86
uint64_t scriptId() const noexcept
Definition Runtime.h:52
void clearLastScriptError(HSQUIRRELVM vm)
Drops any recorded error for a VM.
bool parseCompileError(const std::string &text, std::string *source, int *line, int *column, std::string *message)
Parses the ssq compile message "Compile error at source:line:column msg".
std::string formatStackTrace(const std::vector< ScriptFrame > &frames)
Formats just the call-stack portion of a context.
std::string formatScriptError(const ScriptErrorContext &ctx)
Formats a context into a human-readable multi-line report.
std::string sourceLineText(const std::string &sourceText, int line)
Extracts one 1-based line from script source text.
ScriptErrorContext takeLastScriptError(HSQUIRRELVM vm)
Consumes and clears the last recorded error for a VM.
void setLastScriptError(HSQUIRRELVM vm, ScriptErrorContext ctx)
Records the last error for a VM (thread-local, synchronous consumers).
ScriptErrorContext captureScriptError(HSQUIRRELVM vm)
Captures the pending runtime error from inside the Squirrel error handler.
Definition Build.cpp:11
ScriptStage
Definition Runtime.h:27
std::unique_ptr< ssq::Script > compiled
Definition Runtime.cpp:82
std::unordered_map< std::string, ssq::Class > class_objects
Definition Runtime.cpp:83
std::string source
Definition Runtime.h:99
ScriptState state
Definition Runtime.h:100
std::string error
Definition Runtime.h:102
Structured snapshot of a script error: message, throw site and stack.
Definition ScriptError.h:25
bool empty() const noexcept
True when no error payload was captured.
Definition ScriptError.h:36
std::string message
Raw error value ("kaboom", "42", ...).
Definition ScriptError.h:26