//----------------------------------------------------------------------------- // // 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 #include #include #include //----------------------------------------------------------------------------| // Types | // namespace ACSVM { // // Environment::PrivData // struct Environment::PrivData { using FuncName = std::pair; using FuncElem = HashMapElem; 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 functionByIdx{nullptr}; HashMapKeyExt functionByName{16, 16}; HashMapKeyMem modules; HashMapKeyMem scopes; std::vector tableCallFunc { #define ACSVM_FuncList(name) \ CallFunc_Func_##name, #include "CodeList.hpp" CallFunc_Func_Nop }; std::unordered_map 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 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 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(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(in); n--;) { ModuleName name = readModuleName(in); String *str = &stringTable[ReadVLN(in)]; Word idx = ReadVLN(in); pd->functionByName.insert(new PrivData::FuncElem{{name, str}, idx}); } // Function vector. auto oldTable = pd->functionByIdx; pd->functionByIdx.clear(); pd->functionByIdx.resize(ReadVLN(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(in); n--;) getGlobalScope(ReadVLN(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(in); return {s, nullptr, i}; } // // Environment::readScript // Script *Environment::readScript(Serial &in) const { auto idx = ReadVLN(in); return &findModule(readModuleName(in))->scriptV[idx]; } // // Environment::readScriptAction // ScriptAction *Environment::readScriptAction(Serial &in) const { auto action = static_cast(ReadVLN(in)); Vector argV; argV.alloc(ReadVLN(in)); for(auto &arg : argV) arg = ReadVLN(in); ScopeID id; id.global = ReadVLN(in); id.hub = ReadVLN(in); id.map = ReadVLN(in); ScriptName name = readScriptName(in); return new ScriptAction{id, name, action, std::move(argV)}; } // // Environment::readScriptActions // void Environment::readScriptActions(Serial &in, ListLink &out) const { // Clear existing actions. while(out.next->obj) delete out.next->obj; for(auto n = ReadVLN(in); n--;) readScriptAction(in)->link.insert(&out); } // // Environment::readScriptName // ScriptName Environment::readScriptName(Serial &in) const { String *s = in.in->get() ? &stringTable[ReadVLN(in)] : nullptr; Word i = ReadVLN(in); return {s, i}; } // // Environment::readString // String *Environment::readString(Serial &in) const { if(auto idx = ReadVLN(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(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 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(out, in->idx + 1); else WriteVLN(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