RingRacers/libs/ACSVM/include/ACSVM/Environment.cpp
Sally Coolatta 26477941ed Attempt using ACSVM to implement
It's having trouble linking the dll on Windows currently
2022-10-03 01:23:58 -04:00

908 lines
21 KiB
C++

//-----------------------------------------------------------------------------
//
// Copyright (C) 2015-2017 David Hill
//
// See COPYING for license information.
//
//-----------------------------------------------------------------------------
//
// Environment class.
//
//-----------------------------------------------------------------------------
#include "Environment.hpp"
#include "Action.hpp"
#include "BinaryIO.hpp"
#include "CallFunc.hpp"
#include "Code.hpp"
#include "CodeData.hpp"
#include "Function.hpp"
#include "HashMap.hpp"
#include "Module.hpp"
#include "PrintBuf.hpp"
#include "Scope.hpp"
#include "Script.hpp"
#include "Serial.hpp"
#include "Thread.hpp"
#include <iostream>
#include <list>
#include <unordered_map>
#include <vector>
//----------------------------------------------------------------------------|
// Types |
//
namespace ACSVM
{
//
// Environment::PrivData
//
struct Environment::PrivData
{
using FuncName = std::pair<ModuleName, String *>;
using FuncElem = HashMapElem<FuncName, Word>;
struct NameEqual
{
bool operator () (ModuleName const *l, ModuleName const *r) const
{return *l == *r;}
};
struct NameHash
{
std::size_t operator () (ModuleName const *name) const
{return name->hash();}
};
struct FuncNameHash
{
std::size_t operator () (FuncName const &name) const
{return name.first.hash() + name.second->hash;}
};
// Reserve index 0 as no function.
std::vector<Function *> functionByIdx{nullptr};
HashMapKeyExt<FuncName, Word, FuncNameHash> functionByName{16, 16};
HashMapKeyMem<ModuleName, Module, &Module::name, &Module::hashLink> modules;
HashMapKeyMem<Word, GlobalScope, &GlobalScope::id, &GlobalScope::hashLink> scopes;
std::vector<CallFunc> tableCallFunc
{
#define ACSVM_FuncList(name) \
CallFunc_Func_##name,
#include "CodeList.hpp"
CallFunc_Func_Nop
};
std::unordered_map<Word, CodeDataACS0> tableCodeDataACS0
{
#define ACSVM_CodeListACS0(name, code, args, transCode, stackArgC, transFunc) \
{code, {CodeACS0::name, args, Code::transCode, stackArgC, Func::transFunc}},
#include "CodeList.hpp"
};
std::unordered_map<Word, FuncDataACS0> tableFuncDataACS0
{
#define ACSVM_FuncListACS0(name, func, transFunc, ...) \
{func, {FuncACS0::name, Func::transFunc, __VA_ARGS__}},
#include "CodeList.hpp"
};
};
}
//----------------------------------------------------------------------------|
// Extern Functions |
//
namespace ACSVM
{
//
// Environment constructor
//
Environment::Environment() :
branchLimit {0},
scriptLocRegC{ScriptLocRegCDefault},
funcV{nullptr},
funcC{0},
pd{new PrivData}
{
funcV = pd->functionByIdx.data();
funcC = pd->functionByIdx.size();
}
//
// Environment destructor
//
Environment::~Environment()
{
pd->functionByName.free();
pd->modules.free();
pd->scopes.free();
while(scriptAction.next->obj)
delete scriptAction.next->obj;
delete pd;
// Deallocate threads. Do this after scopes have been destructed.
while(threadFree.next->obj)
delete threadFree.next->obj;
}
//
// Environment::addCallFunc
//
Word Environment::addCallFunc(CallFunc func)
{
pd->tableCallFunc.push_back(func);
return pd->tableCallFunc.size() - 1;
}
//
// Environment::addCodeDataACS0
//
void Environment::addCodeDataACS0(Word code, CodeDataACS0 &&data)
{
auto itr = pd->tableCodeDataACS0.find(code);
if(itr == pd->tableCodeDataACS0.end())
pd->tableCodeDataACS0.emplace(code, std::move(data));
else
itr->second = std::move(data);
}
//
// Environment::addFuncDataACS0
//
void Environment::addFuncDataACS0(Word func, FuncDataACS0 &&data)
{
auto itr = pd->tableFuncDataACS0.find(func);
if(itr == pd->tableFuncDataACS0.end())
pd->tableFuncDataACS0.emplace(func, std::move(data));
else
itr->second = std::move(data);
}
//
// Environment::allocThread
//
Thread *Environment::allocThread()
{
return new Thread(this);
}
//
// Environment::callFunc
//
bool Environment::callFunc(Thread *thread, Word func, Word const *argV, Word argC)
{
return pd->tableCallFunc[func](thread, argV, argC);
}
//
// Environment::callSpec
//
Word Environment::callSpec(Thread *thread, Word spec, Word const *argV, Word argC)
{
if(thread->scopeMap->clampCallSpec && thread->module->isACS0)
{
Vector<Word> argTmp{argV, argC};
for(auto &arg : argTmp) arg &= 0xFF;
return callSpecImpl(thread, spec, argTmp.data(), argTmp.size());
}
else
return callSpecImpl(thread, spec, argV, argC);
}
//
// Environment::callSpecImpl
//
Word Environment::callSpecImpl(Thread *, Word, Word const *, Word)
{
return 0;
}
//
// Environment::checkLock
//
bool Environment::checkLock(Thread *, Word, bool)
{
return false;
}
//
// Environment::checkTag
//
bool Environment::checkTag(Word, Word)
{
return false;
}
//
// Environment::collectStrings
//
void Environment::collectStrings()
{
stringTable.collectBegin();
refStrings();
stringTable.collectEnd();
}
//
// Environment::countActiveThread
//
std::size_t Environment::countActiveThread() const
{
std::size_t n = 0;
for(auto &scope : pd->scopes)
{
if(scope.active)
n += scope.countActiveThread();
}
return n;
}
//
// Environment::deferAction
//
void Environment::deferAction(ScriptAction &&action)
{
(new ScriptAction(std::move(action)))->link.insert(&scriptAction);
}
//
// Environment::exec
//
void Environment::exec()
{
// Delegate deferred script actions.
for(auto itr = scriptAction.begin(), end = scriptAction.end(); itr != end;)
{
auto scope = pd->scopes.find(itr->id.global);
if(scope && scope->active)
itr++->link.relink(&scope->scriptAction);
else
++itr;
}
for(auto &scope : pd->scopes)
{
if(scope.active)
scope.exec();
}
}
//
// Environment::findCodeDataACS0
//
CodeDataACS0 const *Environment::findCodeDataACS0(Word code)
{
auto itr = pd->tableCodeDataACS0.find(code);
return itr == pd->tableCodeDataACS0.end() ? nullptr : &itr->second;
}
//
// Environment::findFuncDataACS0
//
FuncDataACS0 const *Environment::findFuncDataACS0(Word func)
{
auto itr = pd->tableFuncDataACS0.find(func);
return itr == pd->tableFuncDataACS0.end() ? nullptr : &itr->second;
}
//
// Environment::findModule
//
Module *Environment::findModule(ModuleName const &name) const
{
return pd->modules.find(name);
}
//
// Environment::freeFunction
//
void Environment::freeFunction(Function *func)
{
// Null every reference to this function in every Module.
// O(N*M) is not very nice, but that can be fixed if/when it comes up.
for(auto &module : pd->modules)
{
for(Function *&funcItr : module.functionV)
{
if(funcItr == func)
funcItr = nullptr;
}
}
pd->functionByIdx[func->idx] = nullptr;
delete func;
}
//
// Environment::freeGlobalScope
//
void Environment::freeGlobalScope(GlobalScope *scope)
{
pd->scopes.unlink(scope);
delete scope;
}
//
// Environment::freeModule
//
void Environment::freeModule(Module *module)
{
pd->modules.unlink(module);
delete module;
}
//
// Environment::freeThread
//
void Environment::freeThread(Thread *thread)
{
thread->link.relink(&threadFree);
}
//
// Environment::getCodeData
//
CodeData const *Environment::getCodeData(Code code)
{
switch(code)
{
#define ACSVM_CodeList(name, argc) case Code::name: \
{static CodeData const data{Code::name, argc}; return &data;}
#include "CodeList.hpp"
default:
case Code::None:
static CodeData const dataNone{Code::None, 0};
return &dataNone;
}
}
//
// Environment::getFreeThread
//
Thread *Environment::getFreeThread()
{
if(threadFree.next->obj)
{
Thread *thread = threadFree.next->obj;
thread->link.unlink();
return thread;
}
else
return allocThread();
}
//
// Environment::getFunction
//
Function *Environment::getFunction(Module *module, String *funcName)
{
if(funcName)
{
PrivData::FuncName namePair{module->name, funcName};
auto idx = pd->functionByName.find(namePair);
if(!idx)
{
#if SIZE_MAX > UINT32_MAX
if(pd->functionByIdx.size() > UINT32_MAX)
throw std::bad_alloc();
#endif
idx = new PrivData::FuncElem{std::move(namePair),
static_cast<Word>(pd->functionByIdx.size())};
pd->functionByName.insert(idx);
pd->functionByIdx.emplace_back();
funcV = pd->functionByIdx.data();
funcC = pd->functionByIdx.size();
}
auto &ptr = pd->functionByIdx[idx->val];
if(!ptr)
ptr = new Function{module, funcName, idx->val};
return ptr;
}
else
return new Function{module, nullptr, 0};
}
//
// Environment::getGlobalScope
//
GlobalScope *Environment::getGlobalScope(Word id)
{
if(auto *scope = pd->scopes.find(id))
return scope;
auto scope = new GlobalScope(this, id);
pd->scopes.insert(scope);
return scope;
}
//
// Environment::getModule
//
Module *Environment::getModule(ModuleName const &name)
{
auto module = pd->modules.find(name);
if(!module)
{
module = new Module{this, name};
pd->modules.insert(module);
loadModule(module);
}
else
{
if(!module->loaded)
loadModule(module);
}
return module;
}
//
// Environment::getModuleName
//
ModuleName Environment::getModuleName(char const *str)
{
return getModuleName(str, std::strlen(str));
}
//
// Environment::getModuleName
//
ModuleName Environment::getModuleName(char const *str, std::size_t len)
{
return {getString(str, len), nullptr, 0};
}
//
// Environment::hasActiveThread
//
bool Environment::hasActiveThread() const
{
for(auto &scope : pd->scopes)
{
if(scope.active && scope.hasActiveThread())
return true;
}
return false;
}
//
// Environment::loadFunctions
//
void Environment::loadFunctions(Serial &in)
{
// Function index map.
pd->functionByName.free();
for(std::size_t n = ReadVLN<std::size_t>(in); n--;)
{
ModuleName name = readModuleName(in);
String *str = &stringTable[ReadVLN<Word>(in)];
Word idx = ReadVLN<Word>(in);
pd->functionByName.insert(new PrivData::FuncElem{{name, str}, idx});
}
// Function vector.
auto oldTable = pd->functionByIdx;
pd->functionByIdx.clear();
pd->functionByIdx.resize(ReadVLN<std::size_t>(in), nullptr);
funcV = pd->functionByIdx.data();
funcC = pd->functionByIdx.size();
// Reset function indexes.
for(Function *&func : oldTable)
{
if(func)
{
auto idx = pd->functionByName.find({func->module->name, func->name});
func->idx = idx ? idx->val : 0;
pd->functionByIdx[func->idx] = func;
}
}
}
//
// Environment::loadGlobalScopes
//
void Environment::loadGlobalScopes(Serial &in)
{
// Clear existing scopes.
pd->scopes.free();
for(auto n = ReadVLN<std::size_t>(in); n--;)
getGlobalScope(ReadVLN<Word>(in))->loadState(in);
}
//
// Environment::loadScriptActions
//
void Environment::loadScriptActions(Serial &in)
{
readScriptActions(in, scriptAction);
}
//
// Environment::loadState
//
void Environment::loadState(Serial &in)
{
in.readSign(Signature::Environment);
loadStringTable(in);
loadFunctions(in);
loadGlobalScopes(in);
loadScriptActions(in);
in.readSign(~Signature::Environment);
}
//
// Environment::loadStringTable
//
void Environment::loadStringTable(Serial &in)
{
StringTable oldTable{std::move(stringTable)};
stringTable.loadState(in);
resetStrings();
}
//
// Environment::printArray
//
void Environment::printArray(PrintBuf &buf, Array const &array, Word index, Word limit)
{
PrintArrayChar(buf, array, index, limit);
}
//
// Environment::printKill
//
void Environment::printKill(Thread *thread, Word type, Word data)
{
std::cerr << "ACSVM ERROR: Kill " << type << ':' << data
<< " at " << (thread->codePtr - thread->module->codeV.data() - 1) << '\n';
}
//
// Environment::readModuleName
//
ModuleName Environment::readModuleName(Serial &in) const
{
auto s = readString(in);
auto i = ReadVLN<std::size_t>(in);
return {s, nullptr, i};
}
//
// Environment::readScript
//
Script *Environment::readScript(Serial &in) const
{
auto idx = ReadVLN<std::size_t>(in);
return &findModule(readModuleName(in))->scriptV[idx];
}
//
// Environment::readScriptAction
//
ScriptAction *Environment::readScriptAction(Serial &in) const
{
auto action = static_cast<ScriptAction::Action>(ReadVLN<int>(in));
Vector<Word> argV;
argV.alloc(ReadVLN<std::size_t>(in));
for(auto &arg : argV)
arg = ReadVLN<Word>(in);
ScopeID id;
id.global = ReadVLN<Word>(in);
id.hub = ReadVLN<Word>(in);
id.map = ReadVLN<Word>(in);
ScriptName name = readScriptName(in);
return new ScriptAction{id, name, action, std::move(argV)};
}
//
// Environment::readScriptActions
//
void Environment::readScriptActions(Serial &in, ListLink<ScriptAction> &out) const
{
// Clear existing actions.
while(out.next->obj)
delete out.next->obj;
for(auto n = ReadVLN<std::size_t>(in); n--;)
readScriptAction(in)->link.insert(&out);
}
//
// Environment::readScriptName
//
ScriptName Environment::readScriptName(Serial &in) const
{
String *s = in.in->get() ? &stringTable[ReadVLN<Word>(in)] : nullptr;
Word i = ReadVLN<Word>(in);
return {s, i};
}
//
// Environment::readString
//
String *Environment::readString(Serial &in) const
{
if(auto idx = ReadVLN<std::size_t>(in))
return &stringTable[idx - 1];
else
return nullptr;
}
//
// Environment::refStrings
//
void Environment::refStrings()
{
for(auto &action : scriptAction)
action.refStrings(this);
for(auto &funcIdx : pd->functionByName)
{
funcIdx.key.first.s->ref = true;
funcIdx.key.second->ref = true;
}
for(auto &module : pd->modules)
module.refStrings();
for(auto &scope : pd->scopes)
scope.refStrings();
}
//
// Environment::resetStrings
//
void Environment::resetStrings()
{
for(auto &funcIdx : pd->functionByName)
{
funcIdx.key.first.s = getString(funcIdx.key.first.s);
funcIdx.key.second = getString(funcIdx.key.second);
}
for(auto &module : pd->modules)
module.resetStrings();
}
//
// Environment::saveFunctions
//
void Environment::saveFunctions(Serial &out) const
{
WriteVLN(out, pd->functionByName.size());
for(auto &funcIdx : pd->functionByName)
{
writeModuleName(out, funcIdx.key.first);
WriteVLN(out, funcIdx.key.second->idx);
WriteVLN(out, funcIdx.val);
}
WriteVLN(out, pd->functionByIdx.size());
}
//
// Environment::saveGlobalScopes
//
void Environment::saveGlobalScopes(Serial &out) const
{
WriteVLN(out, pd->scopes.size());
for(auto &scope : pd->scopes)
{
WriteVLN(out, scope.id);
scope.saveState(out);
}
}
//
// Environment::saveScriptActions
//
void Environment::saveScriptActions(Serial &out) const
{
writeScriptActions(out, scriptAction);
}
//
// Environment::saveState
//
void Environment::saveState(Serial &out) const
{
out.writeSign(Signature::Environment);
saveStringTable(out);
saveFunctions(out);
saveGlobalScopes(out);
saveScriptActions(out);
out.writeSign(~Signature::Environment);
}
//
// Environment::saveStringTable
//
void Environment::saveStringTable(Serial &out) const
{
stringTable.saveState(out);
}
//
// Environment::writeModuleName
//
void Environment::writeModuleName(Serial &out, ModuleName const &in) const
{
writeString(out, in.s);
WriteVLN(out, in.i);
}
//
// Environment::writeScript
//
void Environment::writeScript(Serial &out, Script *in) const
{
WriteVLN(out, in - in->module->scriptV.data());
writeModuleName(out, in->module->name);
}
//
// Environment::writeScriptAction
//
void Environment::writeScriptAction(Serial &out, ScriptAction const *in) const
{
WriteVLN<int>(out, in->action);
WriteVLN(out, in->argV.size());
for(auto &arg : in->argV)
WriteVLN(out, arg);
WriteVLN(out, in->id.global);
WriteVLN(out, in->id.hub);
WriteVLN(out, in->id.map);
writeScriptName(out, in->name);
}
//
// Environment::writeScriptActions
//
void Environment::writeScriptActions(Serial &out,
ListLink<ScriptAction> const &in) const
{
WriteVLN(out, in.size());
for(auto &action : in)
writeScriptAction(out, &action);
}
//
// Environment::writeScriptName
//
void Environment::writeScriptName(Serial &out, ScriptName const &in) const
{
if(in.s)
{
out.out->put('\1');
WriteVLN(out, in.s->idx);
}
else
out.out->put('\0');
WriteVLN(out, in.i);
}
//
// Environment::writeString
//
void Environment::writeString(Serial &out, String const *in) const
{
if(in)
WriteVLN<std::size_t>(out, in->idx + 1);
else
WriteVLN<std::size_t>(out, 0);
}
//
// Environment::PrintArrayChar
//
void Environment::PrintArrayChar(PrintBuf &buf, Array const &array, Word index, Word limit)
{
// Calculate output length and end index.
std::size_t len = 0;
Word end;
for(Word &itr = end = index; itr - index != limit; ++itr)
{
Word c = array.find(itr);
if(!c) break;
++len;
}
// Acquire output buffer.
buf.reserve(len);
char *s = buf.getBuf(len);
// Truncate elements to char.
for(Word itr = index; itr != end; ++itr)
*s++ = array.find(itr);
}
//
// Environment::PrintArrayUTF8
//
void Environment::PrintArrayUTF8(PrintBuf &buf, Array const &array, Word index, Word limit)
{
// Calculate output length and end index.
std::size_t len = 0;
Word end;
for(Word &itr = end = index; itr - index != limit; ++itr)
{
Word c = array.find(itr);
if(!c) break;
if(c > 0x10FFFF) c = 0xFFFD;
if(c <= 0x007F) len += 1;
else if(c <= 0x07FF) len += 2;
else if(c <= 0xFFFF) len += 3;
else len += 4;
}
// Acquire output buffer.
buf.reserve(len);
char *s = buf.getBuf(len);
// Convert UTF-32 sequence to UTF-8.
for(Word itr = index; itr != end; ++itr)
{
Word c = array.find(itr);
if(c > 0x10FFFF) c = 0xFFFD;
if(c <= 0x7F) {*s++ = 0x00 | (c >> 0); goto put0;}
if(c <= 0x7FF) {*s++ = 0xC0 | (c >> 6); goto put1;}
if(c <= 0xFFFF) {*s++ = 0xE0 | (c >> 12); goto put2;}
{*s++ = 0xF0 | (c >> 18); goto put3;}
put3: *s++ = 0x80 | ((c >> 12) & 0x3F);
put2: *s++ = 0x80 | ((c >> 6) & 0x3F);
put1: *s++ = 0x80 | ((c >> 0) & 0x3F);
put0:;
}
}
}
// EOF