mirror of
https://git.tesses.org/tesses50/crosslang.git
synced 2026-10-01 17:14:13 +00:00
Replace time-based GC with allocation-count trigger, migrate all Create factories to GCList::Create(), fix VFSPath root path, reorder TObject variant
This commit is contained in:
@@ -1490,12 +1490,12 @@ bool InterperterThread::ExecuteMethod2(std::shared_ptr<GC> gc, TObject instance,
|
||||
pattern,
|
||||
callable->ToRouteServerRequestHandler(gc));
|
||||
}
|
||||
} else if (key == "Trace") {
|
||||
} else if (key == "Query") {
|
||||
std::string pattern;
|
||||
TCallable *callable;
|
||||
if (GetArgument(args, 0, pattern) &&
|
||||
GetArgumentHeap(args, 1, callable)) {
|
||||
routable->Trace(
|
||||
routable->Query(
|
||||
pattern,
|
||||
callable->ToRouteServerRequestHandler(gc));
|
||||
}
|
||||
|
||||
@@ -58,13 +58,13 @@ void ThrowFatalError(std::exception &ex) {
|
||||
error.WriteLine(text);
|
||||
}
|
||||
|
||||
exit(1);
|
||||
std::exit(1);
|
||||
}
|
||||
if (clrtexcept != nullptr) {
|
||||
error.WriteLine("CrossLang has encountered a fatal runtime exception");
|
||||
error.WriteLine();
|
||||
error.WriteLine(clrtexcept->what());
|
||||
exit(1);
|
||||
std::exit(1);
|
||||
}
|
||||
if (compiler != nullptr) {
|
||||
error.WriteLine("CrossLang has encountered a compiler error");
|
||||
@@ -81,13 +81,13 @@ void ThrowFatalError(std::exception &ex) {
|
||||
error.Write(":");
|
||||
error.WriteLine((int64_t)li.offset);
|
||||
|
||||
exit(1);
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
error.WriteLine("CrossLang has encountered a fatal C++ exception");
|
||||
error.WriteLine();
|
||||
error.Write("what(): ");
|
||||
error.WriteLine(ex2->what());
|
||||
exit(1);
|
||||
std::exit(1);
|
||||
}
|
||||
} // namespace Tesses::CrossLang
|
||||
+4
-30
@@ -5,22 +5,8 @@
|
||||
#include <iostream>
|
||||
namespace Tesses::CrossLang {
|
||||
|
||||
TFile *TFile::Create(GCList &ls) {
|
||||
TFile *f = new TFile();
|
||||
f->icon = -1;
|
||||
std::shared_ptr<GC> _gc = ls.GetGC();
|
||||
ls.Add(f);
|
||||
_gc->Watch(f);
|
||||
return f;
|
||||
}
|
||||
TFile *TFile::Create(GCList *ls) {
|
||||
TFile *f = new TFile();
|
||||
f->icon = -1;
|
||||
std::shared_ptr<GC> _gc = ls->GetGC();
|
||||
ls->Add(f);
|
||||
_gc->Watch(f);
|
||||
return f;
|
||||
}
|
||||
TFile *TFile::Create(GCList &ls) { return ls.Create<TFile>(); }
|
||||
TFile *TFile::Create(GCList *ls) { return ls->Create<TFile>(); }
|
||||
void TFileChunk::Mark() {
|
||||
if (this->marked)
|
||||
return;
|
||||
@@ -28,20 +14,8 @@ void TFileChunk::Mark() {
|
||||
this->file->Mark();
|
||||
}
|
||||
|
||||
TFileChunk *TFileChunk::Create(GCList &ls) {
|
||||
TFileChunk *chk = new TFileChunk();
|
||||
std::shared_ptr<GC> _gc = ls.GetGC();
|
||||
ls.Add(chk);
|
||||
_gc->Watch(chk);
|
||||
return chk;
|
||||
}
|
||||
TFileChunk *TFileChunk::Create(GCList *ls) {
|
||||
TFileChunk *chk = new TFileChunk();
|
||||
std::shared_ptr<GC> _gc = ls->GetGC();
|
||||
ls->Add(chk);
|
||||
_gc->Watch(chk);
|
||||
return chk;
|
||||
}
|
||||
TFileChunk *TFileChunk::Create(GCList &ls) { return ls.Create<TFileChunk>(); }
|
||||
TFileChunk *TFileChunk::Create(GCList *ls) { return ls->Create<TFileChunk>(); }
|
||||
void TFile::Mark() {
|
||||
if (this->marked)
|
||||
return;
|
||||
|
||||
+87
-79
@@ -11,7 +11,18 @@
|
||||
using namespace Tesses::Framework::Threading;
|
||||
using namespace std::chrono;
|
||||
namespace Tesses::CrossLang {
|
||||
#if defined(GEKKO) || defined(_EE)
|
||||
|
||||
struct PthreadCmp {
|
||||
bool operator()(const pthread_t &a, const pthread_t &b) const {
|
||||
return pthread_equal(a, b) != 0;
|
||||
}
|
||||
};
|
||||
static std::map<pthread_t, CallStackEntry *, PthreadCmp> current_funcs;
|
||||
static Tesses::Framework::Threading::Mutex current_funcs_mtx;
|
||||
#else
|
||||
thread_local CallStackEntry *current_function;
|
||||
#endif
|
||||
bool GC::IsRunning() {
|
||||
|
||||
bool run = this->running;
|
||||
@@ -74,7 +85,14 @@ TDictionary *CreateThread(GCList &ls, TCallable *callable, bool detached) {
|
||||
TObject cb = th->callable->Call(ls, {});
|
||||
gc->BarrierBegin();
|
||||
th->returnValue = cb;
|
||||
std::map<pthread_t, int> mt;
|
||||
#if defined(GEKKO) || defined(_EE)
|
||||
current_funcs_mtx.Lock();
|
||||
current_funcs.erase(pthread_self());
|
||||
current_funcs_mtx.Unlock();
|
||||
#endif
|
||||
gc->BarrierEnd();
|
||||
|
||||
th->hasReturned = true;
|
||||
});
|
||||
while (!th->hasInit)
|
||||
@@ -93,50 +111,47 @@ void GC::Start() {
|
||||
return new Tesses::Framework::Threading::ThreadPool(threads);
|
||||
},
|
||||
[](Tesses::Framework::Threading::ThreadPool *p) -> void { delete p; });
|
||||
this->mtx = new Mutex();
|
||||
|
||||
this->running = true;
|
||||
this->thrd = new Thread([this]() -> void {
|
||||
std::chrono::time_point<std::chrono::system_clock> last_frame,
|
||||
this_frame;
|
||||
|
||||
this_frame = system_clock::now();
|
||||
last_frame = this_frame;
|
||||
|
||||
while (this->IsRunning()) {
|
||||
this_frame = system_clock::now();
|
||||
if ((this_frame - last_frame) > 10s) {
|
||||
this->BarrierBegin();
|
||||
while (this->allocs < ALLOC_THRESHOLD)
|
||||
this->cond.Wait(
|
||||
&this->mtx); // your wrapper around pthread_cond_wait
|
||||
this->allocs = 0;
|
||||
std::vector<THeapObject *> to_delete;
|
||||
this->Collect(to_delete);
|
||||
this->BarrierEnd();
|
||||
|
||||
last_frame = this_frame;
|
||||
this->Collect();
|
||||
}
|
||||
for (auto o : to_delete)
|
||||
delete o;
|
||||
}
|
||||
{
|
||||
std::vector<THeapObject *> to_delete;
|
||||
this->BarrierBegin();
|
||||
this->Collect(to_delete);
|
||||
this->BarrierEnd();
|
||||
|
||||
#if defined(_WIN32)
|
||||
Sleep(100);
|
||||
#else
|
||||
usleep(100000);
|
||||
#endif
|
||||
for (auto item : to_delete)
|
||||
delete item;
|
||||
}
|
||||
GC::Collect();
|
||||
});
|
||||
}
|
||||
|
||||
bool GC::UsingNullThreads() { return false; }
|
||||
|
||||
void GC::BarrierBegin() { this->mtx->Lock(); }
|
||||
void GC::BarrierEnd() { this->mtx->Unlock(); }
|
||||
void GC::BarrierBegin() { this->mtx.Lock(); }
|
||||
void GC::BarrierEnd() { this->mtx.Unlock(); }
|
||||
void GC::Watch(TObject obj) {
|
||||
if (std::holds_alternative<THeapObjectHolder>(obj)) {
|
||||
auto _item = std::get<THeapObjectHolder>(obj).obj;
|
||||
this->BarrierBegin();
|
||||
|
||||
for (auto item : this->objects) {
|
||||
if (item == _item) {
|
||||
this->BarrierEnd();
|
||||
return;
|
||||
}
|
||||
}
|
||||
this->objects.push_back(_item);
|
||||
this->objects.insert(_item);
|
||||
auto nowAllocs = ++this->allocs;
|
||||
this->BarrierEnd();
|
||||
if (nowAllocs >= ALLOC_THRESHOLD)
|
||||
this->cond.Signal();
|
||||
}
|
||||
}
|
||||
void GC::Mark(TObject obj) {
|
||||
@@ -149,65 +164,36 @@ void GC::Unwatch(TObject obj) {
|
||||
if (std::holds_alternative<THeapObjectHolder>(obj)) {
|
||||
auto _item = std::get<THeapObjectHolder>(obj).obj;
|
||||
this->BarrierBegin();
|
||||
for (auto index = this->objects.begin();
|
||||
index != this->objects.end();) {
|
||||
if (*index == _item) {
|
||||
index = this->objects.erase(index);
|
||||
continue;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
this->objects.erase(_item);
|
||||
this->BarrierEnd();
|
||||
}
|
||||
}
|
||||
void GC::SetRoot(TObject obj) {
|
||||
if (std::holds_alternative<THeapObjectHolder>(obj)) {
|
||||
auto _item = std::get<THeapObjectHolder>(obj).obj;
|
||||
this->BarrierBegin();
|
||||
void GC::SetRoot(GCList *_item) {
|
||||
if (_item == nullptr)
|
||||
return;
|
||||
this->BarrierBegin();
|
||||
|
||||
for (auto item : this->roots) {
|
||||
if (item == _item) {
|
||||
this->BarrierEnd();
|
||||
return;
|
||||
}
|
||||
}
|
||||
this->roots.push_back(_item);
|
||||
this->BarrierEnd();
|
||||
}
|
||||
this->roots.insert(_item);
|
||||
this->BarrierEnd();
|
||||
}
|
||||
Tesses::Framework::Threading::ThreadPool *GC::GetPool() {
|
||||
return this->tpool->GetValue();
|
||||
}
|
||||
|
||||
void GC::UnsetRoot(TObject obj) {
|
||||
if (std::holds_alternative<THeapObjectHolder>(obj)) {
|
||||
auto _item = std::get<THeapObjectHolder>(obj).obj;
|
||||
this->BarrierBegin();
|
||||
for (auto index = this->roots.begin(); index != this->roots.end();) {
|
||||
if (*index == _item) {
|
||||
index = this->roots.erase(index);
|
||||
continue;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
this->BarrierEnd();
|
||||
}
|
||||
void GC::UnsetRoot(GCList *_item) {
|
||||
if (_item == nullptr)
|
||||
return;
|
||||
this->BarrierBegin();
|
||||
this->roots.erase(_item);
|
||||
this->BarrierEnd();
|
||||
}
|
||||
|
||||
GC::~GC() {
|
||||
GC::BarrierBegin();
|
||||
|
||||
this->roots.clear();
|
||||
GC::BarrierEnd();
|
||||
|
||||
this->running = false;
|
||||
this->cond.Signal();
|
||||
this->thrd->Join();
|
||||
delete this->thrd;
|
||||
for (auto item : objects)
|
||||
delete item;
|
||||
|
||||
delete this->tpool;
|
||||
delete this->mtx;
|
||||
}
|
||||
|
||||
void GC::RegisterEverythingCallback(
|
||||
@@ -218,23 +204,45 @@ void GC::RegisterEverything(TRootEnvironment *env) {
|
||||
for (auto item : this->register_everything)
|
||||
item(this->shared_from_this(), env);
|
||||
}
|
||||
void GC::Collect() {
|
||||
this->BarrierBegin();
|
||||
|
||||
CallStackEntry *GC::GetCurrentFunction() {
|
||||
#if defined(GEKKO) || defined(_EE)
|
||||
current_funcs_mtx.Lock();
|
||||
auto val = current_funcs[pthread_self()];
|
||||
current_funcs_mtx.Unlock();
|
||||
|
||||
return val;
|
||||
|
||||
#else
|
||||
return current_function;
|
||||
#endif
|
||||
}
|
||||
void GC::SetCurrentFunction(CallStackEntry *cse) {
|
||||
#if defined(GEKKO) || defined(_EE)
|
||||
current_funcs_mtx.Lock();
|
||||
current_funcs[pthread_self()] = cse;
|
||||
current_funcs_mtx.Unlock();
|
||||
#else
|
||||
current_function = cse;
|
||||
#endif
|
||||
}
|
||||
|
||||
void GC::Collect(std::vector<THeapObject *> &to_delete) {
|
||||
|
||||
for (auto item : this->objects) {
|
||||
item->marked = false;
|
||||
}
|
||||
for (auto item : this->roots) {
|
||||
item->Mark();
|
||||
}
|
||||
for (auto index = this->objects.begin(); index != this->objects.end();
|
||||
index++) {
|
||||
for (auto index = this->objects.begin(); index != this->objects.end();) {
|
||||
THeapObject *o = *index;
|
||||
if (!o->marked) {
|
||||
delete o;
|
||||
this->objects.erase(index);
|
||||
index--;
|
||||
to_delete.push_back(o);
|
||||
index = this->objects.erase(index);
|
||||
} else {
|
||||
++index;
|
||||
}
|
||||
}
|
||||
this->BarrierEnd();
|
||||
}
|
||||
}; // namespace Tesses::CrossLang
|
||||
|
||||
+1
-2
@@ -8,7 +8,7 @@ GCList::GCList(std::shared_ptr<GC> gc) {
|
||||
gc->BarrierEnd();
|
||||
}
|
||||
|
||||
std::shared_ptr<GC> GCList::GetGC() { return this->gc; }
|
||||
std::shared_ptr<GC> GCList::GetGC() const { return this->gc; }
|
||||
void GCList::Remove(TObject obj) {
|
||||
if (std::holds_alternative<THeapObjectHolder>(obj)) {
|
||||
auto _item = std::get<THeapObjectHolder>(obj).obj;
|
||||
@@ -40,7 +40,6 @@ void GCList::Add(TObject obj) {
|
||||
}
|
||||
}
|
||||
void GCList::Mark() {
|
||||
this->marked = true;
|
||||
for (auto item : this->items) {
|
||||
item->Mark();
|
||||
}
|
||||
|
||||
+69
-55
@@ -12,8 +12,6 @@
|
||||
|
||||
namespace Tesses::CrossLang {
|
||||
|
||||
thread_local CallStackEntry *current_function = nullptr;
|
||||
|
||||
TObject ExecuteFunction(GCList &ls, TCallable *callable,
|
||||
std::vector<TObject> args) {
|
||||
return callable->Call(ls, args);
|
||||
@@ -2113,8 +2111,14 @@ bool InterperterThread::Throw(std::shared_ptr<GC> gc) {
|
||||
|
||||
if (!std::holds_alternative<Undefined>(_res2)) {
|
||||
auto env = cse.back()->env;
|
||||
if (!env->GetRootEnvironment()->HandleException(gc, env, _res2))
|
||||
if (!env->GetRootEnvironment()->HandleException(gc, env, _res2)) {
|
||||
TNativeException *nex;
|
||||
if (GetObjectHeap(_res2, nex)) {
|
||||
nex->ThrowIt();
|
||||
}
|
||||
|
||||
throw VMByteCodeException(gc, _res2, cse.back());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -2223,6 +2227,7 @@ bool InterperterThread::JumpUndefined(std::shared_ptr<GC> gc) {
|
||||
bool InterperterThread::Jump(std::shared_ptr<GC> gc) {
|
||||
|
||||
std::vector<CallStackEntry *> &cse = this->call_stack_entries;
|
||||
|
||||
auto stk = cse.back();
|
||||
|
||||
if (stk->ip + 4 <= stk->callable->closure->code.size()) {
|
||||
@@ -2409,23 +2414,39 @@ bool InterperterThread::TryCatch(std::shared_ptr<GC> gc) {
|
||||
if (GetObjectHeap(tryFn, tryC) && GetObjectHeap(catchFn, catchC)) {
|
||||
try {
|
||||
stk->Push(gc, tryC->Call(ls, {}));
|
||||
}
|
||||
|
||||
catch (std::exception &ex) {
|
||||
TDictionary *dict = TDictionary::Create(ls);
|
||||
auto gc = ls.GetGC();
|
||||
auto myEx = dynamic_cast<VMByteCodeException *>(&ex);
|
||||
if (myEx != nullptr) {
|
||||
stk->Push(gc, catchC->Call(ls, {myEx->exception}));
|
||||
} else {
|
||||
gc->BarrierBegin();
|
||||
} catch (std::bad_alloc &ex) {
|
||||
std::cerr
|
||||
<< "CrossLang has failed to allocate, sorry for termination."
|
||||
<< std::endl;
|
||||
std::exit(1);
|
||||
} catch (const std::string &ex) {
|
||||
stk->Push(gc, catchC->Call(ls, {ex}));
|
||||
} catch (const char *ex) {
|
||||
|
||||
dict->SetValue("Type", "NativeException");
|
||||
dict->SetValue("Text", ex.what());
|
||||
gc->BarrierEnd();
|
||||
stk->Push(gc,
|
||||
catchC->Call(ls, {std::string{ex != nullptr ? ex : ""}}));
|
||||
} catch (std::runtime_error &ex) {
|
||||
|
||||
stk->Push(gc, catchC->Call(ls, {dict}));
|
||||
}
|
||||
stk->Push(
|
||||
gc, catchC->Call(ls, {TNativeObject::Create<TException>(
|
||||
ls, ex.what(), "RuntimeException")}));
|
||||
} catch (VMException &ex) {
|
||||
stk->Push(
|
||||
gc, catchC->Call(ls, {TNativeObject::Create<TException>(
|
||||
ls, ex.GetMessage(), "VMException")}));
|
||||
} catch (VMByteCodeException &ex) {
|
||||
stk->Push(gc, catchC->Call(ls, {ex.exception}));
|
||||
} catch (std::exception &ex) {
|
||||
stk->Push(gc,
|
||||
catchC->Call(
|
||||
ls, {TNativeObject::Create<TNativeException>(
|
||||
ls, ex.what(), std::current_exception())}));
|
||||
} catch (...) {
|
||||
stk->Push(gc,
|
||||
catchC->Call(ls, {TNativeObject::Create<TNativeException>(
|
||||
ls, "<No Message>",
|
||||
std::current_exception())}));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -2693,14 +2714,14 @@ void InterperterThread::Execute(std::shared_ptr<GC> gc) {
|
||||
|
||||
std::vector<CallStackEntry *> &cse = this->call_stack_entries;
|
||||
#define VM_OPCODE_TABLE_INLINE
|
||||
#include "vm_opcode_table.h"
|
||||
#include "vm_opcode_table.def"
|
||||
#undef VM_OPCODE_TABLE_INLINE
|
||||
|
||||
execute:
|
||||
|
||||
if (!cse.empty()) {
|
||||
auto stk = cse.back();
|
||||
current_function = stk;
|
||||
GC::SetCurrentFunction(stk);
|
||||
|
||||
try {
|
||||
while (stk->ip < 0xFFFFFFFF &&
|
||||
@@ -2721,7 +2742,7 @@ execute:
|
||||
cse[cse.size() - 2]->Push(gc, o);
|
||||
|
||||
cse.erase(cse.end() - 1);
|
||||
current_function = cse.back();
|
||||
GC::SetCurrentFunction(cse.back());
|
||||
gc->BarrierEnd();
|
||||
goto execute;
|
||||
} else {
|
||||
@@ -2729,8 +2750,13 @@ execute:
|
||||
}
|
||||
}
|
||||
|
||||
if (gc->UsingNullThreads())
|
||||
gc->Collect();
|
||||
if (gc->UsingNullThreads()) {
|
||||
std::vector<THeapObject *> objs;
|
||||
gc->Collect(objs);
|
||||
for (auto &item : objs) {
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stk->mustReturn = false;
|
||||
@@ -2764,7 +2790,7 @@ execute:
|
||||
}
|
||||
if (cse.size() == 1) {
|
||||
|
||||
current_function = nullptr;
|
||||
GC::SetCurrentFunction(nullptr);
|
||||
{
|
||||
|
||||
gc->BarrierBegin();
|
||||
@@ -2817,7 +2843,7 @@ execute:
|
||||
cse[cse.size() - 2]->Push(gc, o);
|
||||
|
||||
cse.erase(cse.end() - 1);
|
||||
current_function = cse.back();
|
||||
GC::SetCurrentFunction(cse.back());
|
||||
gc->BarrierEnd();
|
||||
|
||||
for (auto item : callable) {
|
||||
@@ -2846,7 +2872,7 @@ void CallStackEntry::Push(std::shared_ptr<GC> gc, TObject o) {
|
||||
gc->BarrierEnd();
|
||||
}
|
||||
TObject CallStackEntry::Resume(GCList &ls) {
|
||||
auto cse = current_function;
|
||||
auto cse = GC::GetCurrentFunction();
|
||||
InterperterThread *thrd = InterperterThread::Create(ls);
|
||||
ls.GetGC()->BarrierBegin();
|
||||
thrd->call_stack_entries.push_back(this);
|
||||
@@ -2855,7 +2881,7 @@ TObject CallStackEntry::Resume(GCList &ls) {
|
||||
thrd->Execute(ls.GetGC());
|
||||
|
||||
TObject v = thrd->call_stack_entries[0]->Pop(ls);
|
||||
current_function = cse;
|
||||
GC::SetCurrentFunction(cse);
|
||||
return v;
|
||||
}
|
||||
TObject CallStackEntry::Pop(GCList &gc) {
|
||||
@@ -2870,41 +2896,17 @@ TObject CallStackEntry::Pop(GCList &gc) {
|
||||
}
|
||||
|
||||
InterperterThread *InterperterThread::Create(GCList &ls) {
|
||||
InterperterThread *it = new InterperterThread();
|
||||
std::shared_ptr<GC> _gc = ls.GetGC();
|
||||
ls.Add(it);
|
||||
_gc->Watch(it);
|
||||
return it;
|
||||
return ls.Create<InterperterThread>();
|
||||
}
|
||||
InterperterThread *InterperterThread::Create(GCList *ls) {
|
||||
InterperterThread *it = new InterperterThread();
|
||||
std::shared_ptr<GC> _gc = ls->GetGC();
|
||||
ls->Add(it);
|
||||
_gc->Watch(it);
|
||||
return it;
|
||||
return ls->Create<InterperterThread>();
|
||||
}
|
||||
CallStackEntry *CallStackEntry::Create(GCList &ls) {
|
||||
CallStackEntry *cse = new CallStackEntry();
|
||||
cse->mustReturn = false;
|
||||
cse->srcline = -1;
|
||||
cse->srcfile = "";
|
||||
cse->thread = nullptr;
|
||||
std::shared_ptr<GC> _gc = ls.GetGC();
|
||||
ls.Add(cse);
|
||||
_gc->Watch(cse);
|
||||
return cse;
|
||||
return ls.Create<CallStackEntry>();
|
||||
}
|
||||
|
||||
CallStackEntry *CallStackEntry::Create(GCList *ls) {
|
||||
CallStackEntry *cse = new CallStackEntry();
|
||||
cse->mustReturn = false;
|
||||
cse->srcline = -1;
|
||||
cse->srcfile = "";
|
||||
cse->thread = nullptr;
|
||||
std::shared_ptr<GC> _gc = ls->GetGC();
|
||||
ls->Add(cse);
|
||||
_gc->Watch(cse);
|
||||
return cse;
|
||||
return ls->Create<CallStackEntry>();
|
||||
}
|
||||
void InterperterThread::AddCallStackEntry(GCList &ls, TClosure *closure,
|
||||
std::vector<TObject> args) {
|
||||
@@ -2963,6 +2965,18 @@ void InterperterThread::AddCallStackEntry(GCList &ls, TClosure *closure,
|
||||
cse->env->DeclareVariable(trimStart(closure->closure->args[i]),
|
||||
args[i]);
|
||||
}
|
||||
|
||||
for (; i < closure->closure->args.size(); i++) {
|
||||
auto &a = closure->closure->args[i];
|
||||
if (a.size() > 1 && a[0] == '$') {
|
||||
if (a[1] == '$') {
|
||||
break;
|
||||
}
|
||||
cse->env->DeclareVariable(trimStart(a), Undefined());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::string back = closure->closure->args.empty()
|
||||
? std::string()
|
||||
: closure->closure->args.back();
|
||||
@@ -2979,7 +2993,7 @@ void InterperterThread::AddCallStackEntry(GCList &ls, TClosure *closure,
|
||||
throw VMException("Too many arguments");
|
||||
}
|
||||
|
||||
current_function = cse;
|
||||
GC::SetCurrentFunction(cse);
|
||||
|
||||
this->call_stack_entries.push_back(cse);
|
||||
ls.GetGC()->BarrierEnd();
|
||||
|
||||
Reference in New Issue
Block a user