C4Value C4Effect::DoCall(C4Object *pObj, const char *szFn, C4Value &rVal1, C4Value &rVal2, C4Value &rVal3, C4Value &rVal4, C4Value &rVal5, C4Value &rVal6, C4Value &rVal7) { // def script or global only? C4AulScript *pSrcScript; C4Def *pDef; if (pCommandTarget) { pSrcScript = &pCommandTarget->Def->Script; // overwrite ID for sync safety in runtime join idCommandTarget = pCommandTarget->id; } else if (idCommandTarget && (pDef = Game.Defs.ID2Def(idCommandTarget))) pSrcScript = &pDef->Script; else pSrcScript = &Game.ScriptEngine; // compose function name char fn[C4AUL_MAX_Identifier + 1]; sprintf(fn, PSF_FxCustom, Name, szFn); // call it C4AulFunc *pFn = pSrcScript->GetFuncRecursive(fn); if (!pFn) return C4Value(); return pFn->Exec(pCommandTarget, &C4AulParSet(C4VObj(pObj), C4VInt(iNumber), rVal1, rVal2, rVal3, rVal4, rVal5, rVal6, rVal7)); }
// ResolveAppends and ResolveIncludes must be called both // for each script. ResolveAppends has to be called first! BOOL C4AulScript::ResolveAppends(C4DefList *rDefs) { // resolve children appends for (C4AulScript *s = Child0; s; s = s->Next) s->ResolveAppends(rDefs); // resolve local appends if (State != ASS_PREPARSED) return FALSE; for (C4AListEntry *a = Appends; a; a = a->next()) { if ((long)a->Var != -1) { C4Def *Def = rDefs->ID2Def(C4ID(a->Var)); if (Def) AppendTo(Def->Script, true); else { // save id in buffer because AulWarn will use the buffer of C4IdText // to get the id of the object in which the error occurs... // (stupid static buffers...) char strID[5]; *strID = 0; strcpy(strID, C4IdText(C4ID(a->Var))); Warn("script to #appendto not found: ", strID); } } else { // append to all defs for (int i = 0; i < rDefs->GetDefCount(); i++) { C4Def *pDef = rDefs->GetDef(i); if (!pDef) break; if (pDef == Def) continue; // append AppendTo(pDef->Script, true); } } } return TRUE; }
bool C4AulScriptEngine::ReloadScript(const char *szScript, const char *szLanguage) { C4AulScript * s; for (s = Child0; s; s = s->Next) if (s->ReloadScript(szScript, szLanguage)) break; return !!s; }
TEST(DirectExecTest, SanityTests) { C4AulScript * pScript = new C4AulScript(); ASSERT_TRUE(pScript); C4Value rVal(pScript->DirectExec(nullptr, "5*8", "unit test script", false, nullptr)); EXPECT_EQ(rVal, C4Value(5*8)); delete pScript; }
void C4AulScriptEngine::UnLink() { warnCnt = errCnt = lineCnt = 0; // unlink scripts for (C4AulScript *s = Child0; s; s = s->Next) s->UnLink(); GetPropList()->Thaw(); if (State > ASS_PREPARSED) State = ASS_PREPARSED; // Do not clear global variables and constants, because they are registered by the // preparser or other parts. Note that keeping those fields means that you cannot delete a global // variable or constant at runtime by removing it from the script. }
void C4AulScriptEngine::Link(C4DefList *rDefs) { try { // resolve appends for (C4AulScript *s = Child0; s; s = s->Next) s->ResolveAppends(rDefs); // resolve includes for (C4AulScript *s = Child0; s; s = s->Next) s->ResolveIncludes(rDefs); // parse the scripts to byte code for (C4AulScript *s = Child0; s; s = s->Next) s->Parse(); // engine is always parsed (for global funcs) State = ASS_PARSED; if (rDefs) rDefs->CallEveryDefinition(); // Done modifying the proplists now for (C4AulScript *s = Child0; s; s = s->Next) s->GetPropList()->Freeze(); GetPropList()->Freeze(); } catch (C4AulError &err) { // error??! show it! err.show(); } }
BOOL C4AulScript::ResolveIncludes(C4DefList *rDefs) { // resolve children includes for (C4AulScript *s = Child0; s; s = s->Next) s->ResolveIncludes(rDefs); // Had been preparsed? if (State != ASS_PREPARSED) return FALSE; // has already been resolved? if (IncludesResolved) return TRUE; // catch circular includes if (Resolving) { C4AulParseError(this, "Circular include chain detected - ignoring all includes!") .show(); IncludesResolved = true; State = ASS_LINKED; return FALSE; } Resolving = true; // append all includes to local script for (C4AListEntry *i = Includes; i; i = i->next()) { C4Def *Def = rDefs->ID2Def(C4ID(i->Var)); if (Def) { // resolve #includes in included script first (#include-chains :( ) if (!((C4AulScript &)Def->Script).IncludesResolved) if (!Def->Script.ResolveIncludes(rDefs)) continue; // skip this #include Def->Script.AppendTo(*this, false); } else { // save id in buffer because AulWarn will use the buffer of C4IdText // to get the id of the object in which the error occurs... // (stupid static buffers...) char strID[5]; *strID = 0; strcpy(strID, C4IdText(C4ID(i->Var))); Warn("script to #include not found: ", strID); } } IncludesResolved = true; // includes/appends are resolved now (for this script) Resolving = false; State = ASS_LINKED; return TRUE; }
void C4AulScript::AfterLink() { // for all funcs: search functions that have the same name in // the whole script tree (for great fast direct object call) for (C4AulFunc *Func = Func0; Func; Func = Func->Next) // same-name ring not yet build for this function name? if (!Func->NextSNFunc && !Func->OverloadedBy) { // init Func->NextSNFunc = Func; // search complete tree for functions with same name // (expect all scripts "behind" this point to be already checked // - so after-link calls for childs must be done after this). C4AulScript *pPos = this; while (pPos) { // has children? go down in hierarchy if (pPos->Child0) pPos = pPos->Child0; else { // last child? go up in hierarchy while (!pPos->Next && pPos->Owner) pPos = pPos->Owner; // next node pPos = pPos->Next; } if (!pPos) break; // has function with same name? C4AulFunc *pFn = pPos->GetFunc(Func->Name); if (pFn) { // resolve overloads while (pFn->OverloadedBy) pFn = pFn->OverloadedBy; // link pFn->NextSNFunc = Func->NextSNFunc; Func->NextSNFunc = pFn; } } } // call for childs for (C4AulScript *s = Child0; s; s = s->Next) s->AfterLink(); }
void C4AulScript::UnLink() { // unlink children for (C4AulScript *s = Child0; s; s = s->Next) s->UnLink(); // do not unlink temporary (e.g., DirectExec-script in ReloadDef) if (Temporary) return; // check if byte code needs to be freed if (Code) { delete[] Code; Code = NULL; } // delete included/appended functions C4AulFunc *pFunc = Func0; while (pFunc) { C4AulFunc *pNextFunc = pFunc->Next; // clear stuff that's set in AfterLink pFunc->UnLink(); if (pFunc->SFunc()) if (pFunc->Owner != pFunc->SFunc()->pOrgScript) if (!pFunc->LinkedTo || pFunc->LinkedTo->SFunc()) // do not kill global links; those will // be deleted if corresponding sfunc in // script is deleted delete pFunc; pFunc = pNextFunc; } // includes will have to be re-resolved now IncludesResolved = false; if (State > ASS_PREPARSED) State = ASS_PREPARSED; }
void C4Effect::AssignCallbackFunctions() { C4AulScript *pSrcScript = GetCallbackScript(); // compose function names and search them char fn[C4AUL_MAX_Identifier + 1]; sprintf(fn, PSF_FxStart, Name); pFnStart = pSrcScript->GetFuncRecursive(fn); sprintf(fn, PSF_FxStop, Name); pFnStop = pSrcScript->GetFuncRecursive(fn); sprintf(fn, PSF_FxTimer, Name); pFnTimer = pSrcScript->GetFuncRecursive(fn); sprintf(fn, PSF_FxEffect, Name); pFnEffect = pSrcScript->GetFuncRecursive(fn); sprintf(fn, PSF_FxDamage, Name); pFnDamage = pSrcScript->GetFuncRecursive(fn); }
C4AulDebug::ProcessLineResult C4AulDebug::ProcessLine(const StdStrBuf &Line) { // Get command StdStrBuf Cmd; Cmd.CopyUntil(Line.getData(), ' '); // Get data const char *szData = Line.getPtr(Cmd.getLength()); if (*szData) szData++; // Identify command const char *szCmd = Cmd.getData(); if (SEqualNoCase(szCmd, "HELP")) return ProcessLineResult(false, "Yeah, like I'm going to explain that /here/"); else if (SEqualNoCase(szCmd, "BYE") || SEqualNoCase(szCmd, "QUIT")) C4NetIOTCP::Close(PeerAddr); else if (SEqualNoCase(szCmd, "SAY")) ::Control.DoInput(CID_Message, new C4ControlMessage(C4CMT_Normal, szData), CDT_Direct); else if (SEqualNoCase(szCmd, "CMD")) ::MessageInput.ProcessCommand(szData); else if (SEqualNoCase(szCmd, "STP") || SEqualNoCase(szCmd, "S")) eState = DS_Step; else if (SEqualNoCase(szCmd, "GO") || SEqualNoCase(szCmd, "G")) eState = DS_Go; else if (SEqualNoCase(szCmd, "STO") || SEqualNoCase(szCmd, "O")) eState = DS_StepOver; else if (SEqualNoCase(szCmd, "STR") || SEqualNoCase(szCmd, "R")) eState = DS_StepOut; else if (SEqualNoCase(szCmd, "EXC") || SEqualNoCase(szCmd, "E")) { C4AulScriptContext* context = pExec->GetContext(pExec->GetContextDepth()-1); int32_t objectNum = C4ControlScript::SCOPE_Global; if (context && context->Obj && context->Obj->GetObject()) objectNum = context->Obj->GetObject()->Number; ::Control.DoInput(CID_Script, new C4ControlScript(szData, objectNum, true), CDT_Decide); } else if (SEqualNoCase(szCmd, "PSE")) if (Game.IsPaused()) { Game.Unpause(); return ProcessLineResult(true, "Game unpaused."); } else { Game.Pause(); return ProcessLineResult(true, "Game paused."); } else if (SEqualNoCase(szCmd, "LST")) { for (C4AulScript* script = ScriptEngine.Child0; script; script = script->Next) { SendLine(RelativePath(script->ScriptName)); } } // toggle breakpoint else if (SEqualNoCase(szCmd, "TBR")) { using namespace std; // FIXME: this doesn't find functions which were included/appended string scriptPath = szData; size_t colonPos = scriptPath.find(':'); if (colonPos == string::npos) return ProcessLineResult(false, "Missing line in breakpoint request"); int line = atoi(&scriptPath[colonPos+1]); scriptPath.erase(colonPos); C4AulScript *script; for (script = ScriptEngine.Child0; script; script = script->Next) { if (SEqualNoCase(RelativePath(script->ScriptName), scriptPath.c_str())) break; } auto sh = script ? script->GetScriptHost() : NULL; if (sh) { C4AulBCC * found = NULL; for (auto script = ::ScriptEngine.Child0; script; script = script->Next) for (C4PropList *props = script->GetPropList(); props; props = props->GetPrototype()) for (auto fname = props->EnumerateOwnFuncs(); fname; fname = props->EnumerateOwnFuncs(fname)) { C4Value val; if (!props->GetPropertyByS(fname, &val)) continue; auto func = val.getFunction(); if (!func) continue; auto sfunc = func->SFunc(); if (!sfunc) continue; if (sfunc->pOrgScript != sh) continue; for (auto chunk = sfunc->GetCode(); chunk->bccType != AB_EOFN; chunk++) { if (chunk->bccType == AB_DEBUG) { int lineOfThisOne = sfunc->GetLineOfCode(chunk); if (lineOfThisOne == line) { found = chunk; goto Found; } } } } Found: if (found) found->Par.i = !found->Par.i; // activate breakpoint else return ProcessLineResult(false, "Can't set breakpoint (wrong line?)"); } else return ProcessLineResult(false, "Can't find script"); } else if (SEqualNoCase(szCmd, "SST")) { std::list<StdStrBuf*>::iterator it = StackTrace.begin(); for (it++; it != StackTrace.end(); it++) { SendLine("AT", (*it)->getData()); } SendLine("EST"); } else if (SEqualNoCase(szCmd, "VAR")) { C4Value *val = NULL; int varIndex; C4AulScriptContext* pCtx = pExec->GetContext(pExec->GetContextDepth() - 1); if (pCtx) { if ((varIndex = pCtx->Func->ParNamed.GetItemNr(szData)) != -1) { val = &pCtx->Pars[varIndex]; } else if ((varIndex = pCtx->Func->VarNamed.GetItemNr(szData)) != -1) { val = &pCtx->Vars[varIndex]; } } const char* typeName = val ? GetC4VName(val->GetType()) : "any"; StdStrBuf output = FormatString("%s %s %s", szData, typeName, val ? val->GetDataString().getData() : "Unknown"); SendLine("VAR", output.getData()); } else return ProcessLineResult(false, "Can't do that"); return ProcessLineResult(true, ""); }
void C4AulScriptEngine::Link(C4DefList *rDefs) { #ifdef C4ENGINE try { // resolve appends ResolveAppends(rDefs); // resolve includes ResolveIncludes(rDefs); // parse script funcs descs ParseDescs(); // parse the scripts to byte code Parse(); // engine is always parsed (for global funcs) State = ASS_PARSED; // get common funcs AfterLink(); // non-strict scripts? if (nonStrictCnt) { // warn! // find first non-#strict script C4AulScript *pNonStrictScr = FindFirstNonStrictScript(); if (pNonStrictScr) pNonStrictScr->Warn("using non-#strict syntax!", NULL); else { Warn("non-#strict script detected, but def is lost", NULL); Warn( "please contact [email protected] for further " "instructions", NULL); } Warn(FormatString("%d script%s use non-#strict syntax!", nonStrictCnt, (nonStrictCnt != 1 ? "s" : "")).getData(), NULL); } // update material pointers Game.Material.UpdateScriptPointers(); // display state sprintf(OSTR, "C4AulScriptEngine linked - %d line%s, %d warning%s, %d error%s", lineCnt, (lineCnt != 1 ? "s" : ""), warnCnt, (warnCnt != 1 ? "s" : ""), errCnt, (errCnt != 1 ? "s" : "")); Log(OSTR); // reset counters warnCnt = errCnt = nonStrictCnt = lineCnt = 0; } catch (C4AulError *err) { // error??! show it! err->show(); delete err; } #endif }
BOOL C4AulScript::ReloadScript(const char *szPath) { // call for childs for (C4AulScript *s = Child0; s; s = s->Next) if (s->ReloadScript(szPath)) return TRUE; return FALSE; }