691 lines
25 KiB
C++
691 lines
25 KiB
C++
#include "CrossLang.hpp"
|
|
#include "TessesFramework/Serialization/BitConverter.hpp"
|
|
#include "TessesFramework/Streams/ByteReader.hpp"
|
|
#include "TessesFramework/Uuid.hpp"
|
|
#include <cmath>
|
|
#include <cstddef>
|
|
#include <cstring>
|
|
#include <exception>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <variant>
|
|
namespace Tesses::CrossLang {
|
|
bool InterperterThread::GetField(std::shared_ptr<GC> gc) {
|
|
std::vector<CallStackEntry *> &cse = this->call_stack_entries;
|
|
|
|
if (!cse.empty()) {
|
|
auto stk = cse.back();
|
|
GCList ls(gc);
|
|
TObject _key = stk->Pop(ls);
|
|
|
|
TObject instance = stk->Pop(ls);
|
|
|
|
if (!std::holds_alternative<std::string>(_key)) {
|
|
stk->Push(gc, Undefined());
|
|
return false;
|
|
}
|
|
|
|
std::string key = std::get<std::string>(_key);
|
|
|
|
if (std::holds_alternative<std::string>(instance)) {
|
|
std::string str = std::get<std::string>(instance);
|
|
|
|
if (key == "Count" || key == "Length") {
|
|
int64_t len = (int64_t)str.size();
|
|
if (len < 0)
|
|
len = 0;
|
|
|
|
cse.back()->Push(gc, len);
|
|
return false;
|
|
}
|
|
cse.back()->Push(gc, Undefined());
|
|
return false;
|
|
}
|
|
if (std::holds_alternative<
|
|
std::shared_ptr<Tesses::Framework::TF_Timer_Handle>>(
|
|
instance)) {
|
|
auto timer =
|
|
std::get<std::shared_ptr<Tesses::Framework::TF_Timer_Handle>>(
|
|
instance);
|
|
if (timer) {
|
|
if (key == "Interval") {
|
|
cse.back()->Push(gc, timer->GetIntervalMilliseconds());
|
|
return false;
|
|
}
|
|
if (key == "Enabled") {
|
|
cse.back()->Push(gc, timer->GetEnabled());
|
|
return false;
|
|
}
|
|
}
|
|
cse.back()->Push(gc, Undefined());
|
|
return false;
|
|
}
|
|
if (std::holds_alternative<
|
|
std::shared_ptr<Tesses::Framework::Filesystem::VFS>>(
|
|
instance)) {
|
|
auto vfs =
|
|
std::get<std::shared_ptr<Tesses::Framework::Filesystem::VFS>>(
|
|
instance);
|
|
auto tmpFS = std::dynamic_pointer_cast<
|
|
Tesses::Framework::Filesystem::TempFS>(vfs);
|
|
auto relative = std::dynamic_pointer_cast<
|
|
Tesses::Framework::Filesystem::RelativeFilesystem>(vfs);
|
|
auto myFS = std::dynamic_pointer_cast<TObjectVFS>(vfs);
|
|
if (relative) {
|
|
if (key == "Working") {
|
|
cse.back()->Push(gc, relative->GetWorking());
|
|
return false;
|
|
}
|
|
if (key == "Filesystem") {
|
|
cse.back()->Push(gc, relative->GetVFS());
|
|
return false;
|
|
}
|
|
}
|
|
if (myFS) {
|
|
if (key == "Inner") {
|
|
cse.back()->Push(gc, myFS->obj);
|
|
return false;
|
|
}
|
|
}
|
|
if (tmpFS) {
|
|
if (key == "TempDirectoryName") {
|
|
|
|
cse.back()->Push(gc, tmpFS->TempDirectoryName());
|
|
return false;
|
|
}
|
|
}
|
|
cse.back()->Push(gc, Undefined());
|
|
return false;
|
|
}
|
|
if (std::holds_alternative<
|
|
std::shared_ptr<Tesses::Framework::TextStreams::TextWriter>>(
|
|
instance)) {
|
|
auto writer = std::get<
|
|
std::shared_ptr<Tesses::Framework::TextStreams::TextWriter>>(
|
|
instance);
|
|
auto stringWriter = std::dynamic_pointer_cast<
|
|
Tesses::Framework::TextStreams::StringWriter>(writer);
|
|
if (stringWriter != nullptr) {
|
|
if (key == "Text") {
|
|
cse.back()->Push(gc, stringWriter->GetString());
|
|
return false;
|
|
}
|
|
}
|
|
if (key == "NewLine") {
|
|
cse.back()->Push(gc, writer->newline);
|
|
return false;
|
|
}
|
|
cse.back()->Push(gc, Undefined());
|
|
return false;
|
|
}
|
|
if (std::holds_alternative<
|
|
std::shared_ptr<Tesses::Framework::Streams::ByteReader>>(
|
|
instance)) {
|
|
auto br = std::get<
|
|
std::shared_ptr<Tesses::Framework::Streams::ByteReader>>(
|
|
instance);
|
|
if (key == "Stream") {
|
|
cse.back()->Push(gc, br->GetStream());
|
|
return false;
|
|
}
|
|
cse.back()->Push(gc, Undefined());
|
|
return false;
|
|
}
|
|
if (std::holds_alternative<
|
|
std::shared_ptr<Tesses::Framework::Streams::ByteWriter>>(
|
|
instance)) {
|
|
auto bw = std::get<
|
|
std::shared_ptr<Tesses::Framework::Streams::ByteWriter>>(
|
|
instance);
|
|
if (key == "Stream") {
|
|
|
|
cse.back()->Push(gc, bw->GetStream());
|
|
return false;
|
|
}
|
|
cse.back()->Push(gc, Undefined());
|
|
return false;
|
|
}
|
|
if (std::holds_alternative<
|
|
std::shared_ptr<Tesses::Framework::Streams::Stream>>(
|
|
instance)) {
|
|
auto strm =
|
|
std::get<std::shared_ptr<Tesses::Framework::Streams::Stream>>(
|
|
instance);
|
|
if (strm != nullptr) {
|
|
auto netStrm = std::dynamic_pointer_cast<
|
|
Tesses::Framework::Streams::NetworkStream>(strm);
|
|
|
|
if (key == "CanRead") {
|
|
|
|
cse.back()->Push(gc, strm->CanRead());
|
|
return false;
|
|
}
|
|
if (key == "CanWrite") {
|
|
|
|
cse.back()->Push(gc, strm->CanWrite());
|
|
return false;
|
|
}
|
|
if (key == "CanSeek") {
|
|
|
|
cse.back()->Push(gc, strm->CanSeek());
|
|
return false;
|
|
}
|
|
if (key == "EndOfStream") {
|
|
|
|
cse.back()->Push(gc, strm->EndOfStream());
|
|
return false;
|
|
}
|
|
if (key == "Length") {
|
|
|
|
cse.back()->Push(gc, strm->GetLength());
|
|
return false;
|
|
}
|
|
if (key == "Position") {
|
|
|
|
cse.back()->Push(gc, strm->GetPosition());
|
|
return false;
|
|
}
|
|
|
|
if (netStrm != nullptr) {
|
|
if (key == "Port") {
|
|
cse.back()->Push(gc, netStrm->GetPort());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
cse.back()->Push(gc, Undefined());
|
|
|
|
return false;
|
|
}
|
|
}
|
|
if (std::holds_alternative<TVMVersion>(instance)) {
|
|
TVMVersion &version = std::get<TVMVersion>(instance);
|
|
if (key == "Major") {
|
|
stk->Push(gc, (int64_t)version.Major());
|
|
return false;
|
|
}
|
|
if (key == "Minor") {
|
|
stk->Push(gc, (int64_t)version.Minor());
|
|
return false;
|
|
}
|
|
if (key == "Patch") {
|
|
stk->Push(gc, (int64_t)version.Patch());
|
|
return false;
|
|
}
|
|
if (key == "Build") {
|
|
stk->Push(gc, (int64_t)version.Build());
|
|
return false;
|
|
}
|
|
if (key == "VersionInt") {
|
|
stk->Push(gc, (int64_t)version.AsLong());
|
|
return false;
|
|
}
|
|
if (key == "Stage") {
|
|
switch (version.VersionStage()) {
|
|
case TVMVersionStage::DevVersion:
|
|
stk->Push(gc, "dev");
|
|
break;
|
|
case TVMVersionStage::AlphaVersion:
|
|
stk->Push(gc, "alpha");
|
|
break;
|
|
case TVMVersionStage::BetaVersion:
|
|
stk->Push(gc, "beta");
|
|
break;
|
|
case TVMVersionStage::ProductionVersion:
|
|
stk->Push(gc, "prod");
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
stk->Push(gc, Undefined());
|
|
return false;
|
|
}
|
|
if (std::holds_alternative<
|
|
std::shared_ptr<Tesses::Framework::Date::TimeSpan>>(instance)) {
|
|
auto time =
|
|
std::get<std::shared_ptr<Tesses::Framework::Date::TimeSpan>>(
|
|
instance);
|
|
if (key == "Days") {
|
|
stk->Push(gc, (int64_t)time->Days());
|
|
return false;
|
|
}
|
|
if (key == "Hours") {
|
|
stk->Push(gc, (int64_t)time->Hours());
|
|
return false;
|
|
}
|
|
|
|
if (key == "Minutes") {
|
|
stk->Push(gc, (int64_t)time->Minutes());
|
|
return false;
|
|
}
|
|
if (key == "Seconds") {
|
|
stk->Push(gc, (int64_t)time->Seconds());
|
|
return false;
|
|
}
|
|
|
|
if (key == "TotalHours") {
|
|
stk->Push(gc, time->TotalHours());
|
|
return false;
|
|
}
|
|
|
|
if (key == "TotalMinutes") {
|
|
stk->Push(gc, time->TotalMinutes());
|
|
return false;
|
|
}
|
|
|
|
if (key == "TotalSeconds") {
|
|
stk->Push(gc, time->TotalSeconds());
|
|
return false;
|
|
}
|
|
|
|
stk->Push(gc, Undefined());
|
|
return false;
|
|
}
|
|
if (std::holds_alternative<
|
|
std::shared_ptr<Tesses::Framework::Date::DateTime>>(instance)) {
|
|
auto &date =
|
|
std::get<std::shared_ptr<Tesses::Framework::Date::DateTime>>(
|
|
instance);
|
|
if (key == "IsLocal") {
|
|
stk->Push(gc, date->IsLocal());
|
|
return false;
|
|
}
|
|
if (key == "Year") {
|
|
stk->Push(gc, (int64_t)date->Year());
|
|
return false;
|
|
}
|
|
if (key == "Month") {
|
|
stk->Push(gc, (int64_t)date->Month());
|
|
return false;
|
|
}
|
|
if (key == "Day") {
|
|
stk->Push(gc, (int64_t)date->Day());
|
|
return false;
|
|
}
|
|
if (key == "Hour") {
|
|
stk->Push(gc, (int64_t)date->Hour());
|
|
return false;
|
|
}
|
|
if (key == "Minute") {
|
|
stk->Push(gc, (int64_t)date->Minute());
|
|
return false;
|
|
}
|
|
if (key == "Second") {
|
|
stk->Push(gc, (int64_t)date->Second());
|
|
return false;
|
|
}
|
|
if (key == "DayOfWeek") {
|
|
stk->Push(gc, (int64_t)date->DayOfWeek());
|
|
return false;
|
|
}
|
|
|
|
stk->Push(gc, Undefined());
|
|
return false;
|
|
}
|
|
if (std::holds_alternative<THeapObjectHolder>(instance)) {
|
|
auto obj = std::get<THeapObjectHolder>(instance).obj;
|
|
auto bA = dynamic_cast<TByteArray *>(obj);
|
|
auto list = dynamic_cast<TList *>(obj);
|
|
auto dict = dynamic_cast<TDictionary *>(obj);
|
|
auto dynDict = dynamic_cast<TDynamicDictionary *>(obj);
|
|
auto dynList = dynamic_cast<TDynamicList *>(obj);
|
|
auto tcallable = dynamic_cast<TCallable *>(obj);
|
|
auto closure = dynamic_cast<TClosure *>(obj);
|
|
auto externalMethod = dynamic_cast<TExternalMethod *>(obj);
|
|
auto ittr = dynamic_cast<TEnumerator *>(obj);
|
|
|
|
auto callstackEntry = dynamic_cast<CallStackEntry *>(obj);
|
|
auto file = dynamic_cast<TFile *>(obj);
|
|
auto chunk = dynamic_cast<TFileChunk *>(obj);
|
|
auto natObj = dynamic_cast<TNativeObject *>(obj);
|
|
auto cls = dynamic_cast<TClassObject *>(obj);
|
|
auto aarray = dynamic_cast<TAssociativeArray *>(obj);
|
|
auto task = dynamic_cast<TTask *>(obj);
|
|
if (task != nullptr) {
|
|
if (key == "IsCompleted") {
|
|
cse.back()->Push(gc, task->IsCompleted());
|
|
return false;
|
|
}
|
|
cse.back()->Push(gc, Undefined());
|
|
return false;
|
|
}
|
|
if (aarray != nullptr) {
|
|
if (key == "Count" || key == "Length") {
|
|
cse.back()->Push(gc, aarray->Count());
|
|
return false;
|
|
}
|
|
cse.back()->Push(gc, Undefined());
|
|
return false;
|
|
} else if (cls != nullptr) {
|
|
gc->BarrierBegin();
|
|
auto obj =
|
|
cls->GetValue(cse.back()->callable->className, "get" + key);
|
|
gc->BarrierEnd();
|
|
TClosure *clos;
|
|
TCallable *callable;
|
|
if (GetObjectHeap(obj, clos)) {
|
|
this->AddCallStackEntry(ls, clos, {});
|
|
return true;
|
|
} else if (GetObjectHeap(obj, callable)) {
|
|
cse.back()->Push(gc, callable->Call(ls, {}));
|
|
return false;
|
|
}
|
|
cse.back()->Push(
|
|
gc, cls->GetValue(cse.back()->callable->className, key));
|
|
return false;
|
|
|
|
} else if (natObj != nullptr) {
|
|
cse.back()->Push(gc, natObj->CallMethod(ls, "get" + key, {}));
|
|
return false;
|
|
}
|
|
|
|
if (file != nullptr) {
|
|
if (key == "Version") {
|
|
cse.back()->Push(gc, file->version);
|
|
return false;
|
|
} else if (key == "Name") {
|
|
cse.back()->Push(gc, file->name);
|
|
return false;
|
|
} else if (key == "Info") {
|
|
cse.back()->Push(gc, file->info);
|
|
return false;
|
|
} else if (key == "Dependencies") {
|
|
auto list = TList::Create(ls);
|
|
gc->BarrierBegin();
|
|
for (auto item : file->dependencies) {
|
|
auto res = TDictionary::Create(ls);
|
|
res->SetValue("Name", item.first);
|
|
res->SetValue("Version", item.second);
|
|
list->Add(res);
|
|
}
|
|
gc->BarrierEnd();
|
|
cse.back()->Push(gc, list);
|
|
return false;
|
|
} else if (key == "Tools") {
|
|
auto list = TList::Create(ls);
|
|
gc->BarrierBegin();
|
|
for (auto item : file->tools) {
|
|
auto res = TDictionary::Create(ls);
|
|
res->SetValue("Name", item.first);
|
|
res->SetValue("Version", item.second);
|
|
list->Add(res);
|
|
}
|
|
gc->BarrierEnd();
|
|
cse.back()->Push(gc, list);
|
|
return false;
|
|
} else if (key == "Strings") {
|
|
auto list = TList::Create(ls);
|
|
gc->BarrierBegin();
|
|
for (auto item : file->name) {
|
|
list->Add(item);
|
|
}
|
|
gc->BarrierEnd();
|
|
|
|
cse.back()->Push(gc, list);
|
|
return false;
|
|
} else if (key == "MetadataCount") {
|
|
|
|
cse.back()->Push(gc, (int64_t)file->metadata.size());
|
|
return false;
|
|
} else if (key == "Metadata") {
|
|
TList *meta = TList::Create(ls);
|
|
gc->BarrierBegin();
|
|
for (size_t i = 0; i < file->metadata.size(); i++) {
|
|
meta->Add(TDictionary::Create(
|
|
ls, {TDItem("Name", file->metadata[i].first),
|
|
TDItem("Index", (int64_t)i)}));
|
|
}
|
|
gc->BarrierEnd();
|
|
|
|
cse.back()->Push(gc, meta);
|
|
return false;
|
|
} else if (key == "Sections") {
|
|
TList *sections = TList::Create(ls);
|
|
gc->BarrierBegin();
|
|
for (auto &item : file->sections) {
|
|
TByteArray *ba = TByteArray::Create(ls);
|
|
ba->data = item.second;
|
|
sections->Add(
|
|
TDictionary::Create(ls, {TDItem("Name", item.first),
|
|
TDItem("Data", ba)}));
|
|
}
|
|
gc->BarrierEnd();
|
|
|
|
cse.back()->Push(gc, sections);
|
|
return false;
|
|
} else if (key == "SupportedVMs") {
|
|
TList *supported = TList::Create(ls);
|
|
gc->BarrierBegin();
|
|
if (file->vms.empty()) {
|
|
supported->Add(TDictionary::Create(
|
|
ls, {TDItem("Name", std::string(VMName)),
|
|
TDItem("HowToGet", std::string(VMHowToGet))}));
|
|
} else {
|
|
for (auto item : file->vms)
|
|
supported->Add(TDictionary::Create(
|
|
ls, {TDItem("Name", item.first),
|
|
TDItem("HowToGet", item.second)}));
|
|
}
|
|
gc->BarrierEnd();
|
|
cse.back()->Push(gc, supported);
|
|
return false;
|
|
} else if (key == "Chunks") {
|
|
auto list = TList::Create(ls);
|
|
gc->BarrierBegin();
|
|
for (auto item : file->chunks) {
|
|
list->Add(item);
|
|
}
|
|
|
|
gc->BarrierEnd();
|
|
|
|
cse.back()->Push(gc, list);
|
|
return false;
|
|
} else if (key == "Classes") {
|
|
auto list = TList::Create(ls);
|
|
gc->BarrierBegin();
|
|
|
|
for (uint32_t i = 0; i < (uint32_t)file->classes.size();
|
|
i++) {
|
|
list->Add(GetClassInfo(ls, file, i));
|
|
}
|
|
|
|
cse.back()->Push(gc, list);
|
|
gc->BarrierEnd();
|
|
return false;
|
|
} else if (key == "Functions") {
|
|
auto list = TList::Create(ls);
|
|
gc->BarrierBegin();
|
|
for (auto &item : file->functions) {
|
|
TDictionary *dict = TDictionary::Create(ls);
|
|
if (!item.first.empty())
|
|
dict->SetValue("Documentation", item.first[0]);
|
|
TList *nameParts = TList::Create(ls);
|
|
for (size_t i = 1; i < item.first.size(); i++) {
|
|
nameParts->Add(item.first[i]);
|
|
}
|
|
dict->SetValue("NameParts", nameParts);
|
|
dict->SetValue("ChunkId", (int64_t)item.second);
|
|
|
|
list->Add(dict);
|
|
}
|
|
|
|
cse.back()->Push(gc, list);
|
|
gc->BarrierEnd();
|
|
return false;
|
|
} else if (key == "Icon") {
|
|
if (file->icon >= 0 &&
|
|
file->icon < file->resources.size()) {
|
|
TByteArray *ba = TByteArray::Create(ls);
|
|
ba->data = file->resources[file->icon];
|
|
cse.back()->Push(gc, ba);
|
|
return false;
|
|
} else {
|
|
cse.back()->Push(gc, nullptr);
|
|
return false;
|
|
}
|
|
}
|
|
cse.back()->Push(gc, Undefined());
|
|
return false;
|
|
}
|
|
if (chunk != nullptr) {
|
|
if (key == "Arguments") {
|
|
auto myargs = TList::Create(ls);
|
|
gc->BarrierBegin();
|
|
for (auto item : chunk->args) {
|
|
myargs->Add(item);
|
|
}
|
|
gc->BarrierEnd();
|
|
cse.back()->Push(gc, myargs);
|
|
return false;
|
|
}
|
|
if (key == "Code") {
|
|
auto ba = TByteArray::Create(ls);
|
|
ba->data = chunk->code;
|
|
cse.back()->Push(gc, ba);
|
|
return false;
|
|
}
|
|
if (key == "File") {
|
|
cse.back()->Push(gc, chunk->file);
|
|
return false;
|
|
}
|
|
cse.back()->Push(gc, Undefined());
|
|
return false;
|
|
}
|
|
if (callstackEntry != nullptr) {
|
|
if (key == "IP") {
|
|
cse.back()->Push(gc, (int64_t)callstackEntry->ip);
|
|
return false;
|
|
}
|
|
if (key == "IsDone") {
|
|
cse.back()->Push(
|
|
gc, callstackEntry->ip >=
|
|
callstackEntry->callable->closure->code.size());
|
|
return false;
|
|
}
|
|
if (key == "Closure") {
|
|
cse.back()->Push(gc, callstackEntry->callable);
|
|
return false;
|
|
}
|
|
if (key == "StackEmpty") {
|
|
cse.back()->Push(gc, callstackEntry->stack.empty());
|
|
return false;
|
|
}
|
|
cse.back()->Push(gc, Undefined());
|
|
return false;
|
|
}
|
|
|
|
if (ittr != nullptr) {
|
|
if (key == "Current") {
|
|
cse.back()->Push(gc, ittr->GetCurrent(ls));
|
|
return false;
|
|
}
|
|
cse.back()->Push(gc, Undefined());
|
|
return false;
|
|
}
|
|
if (closure != nullptr) {
|
|
if (key == "Arguments") {
|
|
GCList ls2(gc);
|
|
TList *ls = TList::Create(ls2);
|
|
for (auto arg : closure->closure->args) {
|
|
ls->Add(arg);
|
|
}
|
|
cse.back()->Push(gc, ls);
|
|
return false;
|
|
}
|
|
if (key == "File") {
|
|
cse.back()->Push(gc, closure->file);
|
|
return false;
|
|
}
|
|
}
|
|
if (externalMethod != nullptr) {
|
|
if (key == "Arguments") {
|
|
GCList ls2(gc);
|
|
TList *ls = TList::Create(ls2);
|
|
for (auto arg : externalMethod->args) {
|
|
ls->Add(arg);
|
|
}
|
|
cse.back()->Push(gc, ls);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (tcallable != nullptr) {
|
|
if (key == "Documentation") {
|
|
cse.back()->Push(gc, tcallable->documentation);
|
|
return false;
|
|
}
|
|
if (key == "Tag") {
|
|
gc->BarrierBegin();
|
|
cse.back()->Push(gc, tcallable->tag);
|
|
gc->BarrierEnd();
|
|
return false;
|
|
}
|
|
cse.back()->Push(gc, Undefined());
|
|
return false;
|
|
}
|
|
|
|
if (bA != nullptr) {
|
|
if (key == "Count" || key == "Length") {
|
|
int64_t len = (int64_t)bA->data.size();
|
|
if (len < 0)
|
|
len = 0;
|
|
|
|
stk->Push(gc, len);
|
|
return false;
|
|
}
|
|
}
|
|
if (list != nullptr) {
|
|
if (key == "Count" || key == "Length") {
|
|
int64_t len = list->Count();
|
|
if (len < 0)
|
|
len = 0;
|
|
|
|
stk->Push(gc, len);
|
|
return false;
|
|
}
|
|
}
|
|
if (dynList != nullptr) {
|
|
if (key == "Count" || key == "Length") {
|
|
int64_t len = dynList->Count(ls);
|
|
if (len < 0)
|
|
len = 0;
|
|
|
|
stk->Push(gc, len);
|
|
return false;
|
|
}
|
|
}
|
|
if (dynDict != nullptr) {
|
|
if (dynDict->MethodExists(ls, "get" + key)) {
|
|
cse.back()->Push(gc,
|
|
dynDict->CallMethod(ls, "get" + key, {}));
|
|
} else {
|
|
cse.back()->Push(gc, dynDict->GetField(ls, key));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (dict != nullptr) {
|
|
gc->BarrierBegin();
|
|
TObject fn = dict->GetValue("get" + key);
|
|
gc->BarrierEnd();
|
|
if (std::holds_alternative<THeapObjectHolder>(fn) &&
|
|
dynamic_cast<TCallable *>(
|
|
std::get<THeapObjectHolder>(fn).obj) != nullptr) {
|
|
return InvokeOne(ls, fn, dict);
|
|
} else {
|
|
gc->BarrierBegin();
|
|
fn = dict->GetValue(key);
|
|
stk->Push(gc, fn);
|
|
gc->BarrierEnd();
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
stk->Push(gc, Undefined());
|
|
}
|
|
|
|
return false;
|
|
}
|
|
} // namespace Tesses::CrossLang
|