Store a copy of the proplist the preparser created for include/appendto

The parser now copies the contents of the proplists in the order of their
source scripts into the final proplist. This way, local variable contents
get properly included, and the list of functions has one user less.
rope
Günther Brammer 2012-05-28 00:31:55 +02:00
parent e26bc8e33a
commit 487a0ea38f
9 changed files with 84 additions and 76 deletions

View File

@ -148,6 +148,18 @@ C4AulScriptFunc::C4AulScriptFunc(C4AulScript *pOwner, const C4AulScriptFunc &Fro
ParType[i] = FromFunc.ParType[i];
}
C4AulScriptFunc::~C4AulScriptFunc()
{
if (OwnerOverloaded) OwnerOverloaded->DecRef();
}
void C4AulScriptFunc::SetOverloaded(C4AulFunc * f)
{
if (OwnerOverloaded) OwnerOverloaded->DecRef();
OwnerOverloaded = f;
if (f) f->IncRef();
}
/*--- C4AulScriptEngine ---*/
C4AulScriptEngine::C4AulScriptEngine():
@ -204,21 +216,6 @@ void C4AulScriptEngine::Clear()
GlobalNamed.SetNameList(&GlobalNamedNames);
}
void C4AulScriptEngine::UnLink()
{
// unlink scripts
for (C4ScriptHost *s = Child0; s; s = s->Next)
s->UnLink();
C4AulScript::UnLink();
// 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.
//GlobalNamedNames.Reset();
//GlobalConstNames.Reset();
}
void C4AulScriptEngine::RegisterGlobalConstant(const char *szName, const C4Value &rValue)
{
// Register name and set value.

View File

@ -186,6 +186,7 @@ class C4AulScriptFunc : public C4AulFunc
{
public:
C4AulFunc *OwnerOverloaded; // overloaded owner function; if present
void SetOverloaded(C4AulFunc *);
C4AulScriptFunc *SFunc() { return this; } // type check func...
protected:
int CodePos; // code pos
@ -201,9 +202,9 @@ public:
C4AulScriptFunc(C4AulScript *pOwner, C4ScriptHost *pOrgScript, const char *pName, const char *Script);
C4AulScriptFunc(C4AulScript *pOwner, const C4AulScriptFunc &FromFunc); // copy script/code, etc from given func
~C4AulScriptFunc();
void ParseFn(C4AulScriptContext* context = NULL);
virtual void UnLink();
virtual bool GetPublic() { return true; }
virtual int GetParCount() { return ParCount; }

View File

@ -750,8 +750,6 @@ C4Value C4AulExec::Exec(C4AulBCC *pCPos, bool fPassErrors)
if (!pFunc)
throw new C4AulExecError(FormatString("'->': no function \"%s\" in object \"%s\"", pCPos->Par.s->GetCStr(), pTargetVal->GetDataString().getData()).getData());
assert(!pFunc->OverloadedBy);
// Save current position
pCurCtx->CPos = pCPos;
assert(pCurCtx->Func->GetCode() <= pCPos);

View File

@ -25,8 +25,7 @@
C4AulFunc::C4AulFunc(C4AulScript *pOwner, const char *pName):
iRefCnt(0),
Name(pName ? Strings.RegString(pName) : 0),
MapNext(NULL),
OverloadedBy (NULL)
MapNext(NULL)
{
AppendToScript(pOwner);
IncRef(); // see C4AulScript::Clear()

View File

@ -77,8 +77,6 @@ protected:
virtual ~C4AulFunc();
public:
C4AulFunc *OverloadedBy; // function by which this one is overloaded
virtual C4AulScriptFunc *SFunc() { return NULL; } // type check func...
// Wether this function should be visible to players
@ -91,7 +89,6 @@ public:
return Exec(p, pPars->Par, fPassErrors);
}
virtual C4Value Exec(C4PropList * p, C4Value pPars[], bool fPassErrors=false) = 0;
virtual void UnLink() { OverloadedBy = NULL; }
};
#endif

View File

@ -116,26 +116,17 @@ bool C4ScriptHost::ResolveIncludes(C4DefList *rDefs)
return true;
}
void C4AulScript::LinkFunctions()
{
for (C4AulFunc *f = Func0; f; f = f->Next)
{
C4AulScriptFunc *sf = f->SFunc();
if (!sf) continue;
sf->OwnerOverloaded = GetPropList()->GetFunc(sf->Name);
if (sf->OwnerOverloaded && sf->OwnerOverloaded->Owner == this)
sf->OwnerOverloaded->OverloadedBy = sf;
GetPropList()->SetPropertyByS(sf->Name, C4VFunction(sf));
}
}
void C4ScriptHost::UnLink()
{
// do not unlink temporary (e.g., DirectExec-script in ReloadDef)
if (Temporary) return;
C4PropList * p = GetPropList();
if (p) p->C4PropList::Thaw();
if (p)
{
p->C4PropList::Clear();
p->SetProperty(P_Prototype, C4VPropList(Engine->GetPropList()));
}
// delete included/appended functions
C4AulFunc* pFunc = FuncL;
@ -143,17 +134,6 @@ void C4ScriptHost::UnLink()
{
C4AulFunc* pNextFunc = pFunc->Prev;
// clear stuff that's set in LinkFunctions
pFunc->UnLink();
C4Value v;
if (p && p->GetFunc(pFunc->Name) == pFunc && pFunc->SFunc())
{
p->ResetProperty(pFunc->Name);
C4AulFunc * overloaded = pFunc->SFunc()->OwnerOverloaded;
if (overloaded && overloaded != p->GetFunc(pFunc->Name))
p->SetPropertyByS(pFunc->Name, C4VFunction(overloaded));
}
if (pFunc->SFunc() && pFunc->Owner != Engine && pFunc->Owner != pFunc->SFunc()->pOrgScript)
{
pFunc->RemoveFromScript();
@ -169,11 +149,18 @@ void C4ScriptHost::UnLink()
if (State > ASS_PREPARSED) State = ASS_PREPARSED;
}
void C4AulScriptFunc::UnLink()
void C4AulScriptEngine::UnLink()
{
OwnerOverloaded = NULL;
C4AulFunc::UnLink();
// unlink scripts
for (C4ScriptHost *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.
//GlobalNamedNames.Reset();
//GlobalConstNames.Reset();
}
bool C4AulScript::ReloadScript(const char *szPath, const char *szLanguage)
@ -194,9 +181,6 @@ void C4AulScriptEngine::Link(C4DefList *rDefs)
for (C4ScriptHost *s = Child0; s; s = s->Next)
s->ResolveIncludes(rDefs);
// put script functions into the proplist
LinkFunctions();
// parse the scripts to byte code
for (C4ScriptHost *s = Child0; s; s = s->Next)
s->Parse();

View File

@ -770,11 +770,9 @@ bool C4ScriptHost::Preparse()
// clear stuff
Includes.clear(); Appends.clear();
if (GetPropList())
{
GetPropList()->C4PropList::Clear();
GetPropList()->SetProperty(P_Prototype, C4VPropList(Engine->GetPropList()));
}
GetPropList()->C4PropList::Clear();
GetPropList()->SetProperty(P_Prototype, C4VPropList(Engine->GetPropList()));
LocalValues.Clear();
// reset code
ClearCode();
@ -785,6 +783,9 @@ bool C4ScriptHost::Preparse()
// #include will have to be resolved now...
IncludesResolved = false;
// Parse will write the properties back after the ones from included scripts
GetPropList()->Properties.Swap(&LocalValues);
// return success
C4AulScript::State = ASS_PREPARSED;
return true;
@ -1389,22 +1390,24 @@ void C4AulParse::Parse_Function()
else
owner = a;
Fn = 0;
C4AulFunc * f = owner->Func0;
C4AulFunc * f = owner->GetPropList()->GetFunc(Idtf);
while (f)
{
if (SEqual(f->GetName(), Idtf) && f->SFunc() && f->SFunc()->pOrgScript == pOrgScript)
if (f->SFunc() && f->SFunc()->pOrgScript == pOrgScript && f->Owner == owner)
{
if (Fn)
//throw new C4AulParseError(this, "Duplicate function ", Idtf);
Warn("Duplicate function %s", Idtf);
Fn = f->SFunc();
}
f = f->Next;
f = f->SFunc() ? f->SFunc()->OwnerOverloaded : 0;
}
// first preparser run or a new func in a reloaded script
if (!Fn && Type == PREPARSER)
{
Fn = new C4AulScriptFunc(owner, pOrgScript, Idtf, SPos);
Fn->SetOverloaded(owner->GetPropList()->GetFunc(Fn->Name));
owner->GetPropList()->SetPropertyByS(Fn->Name, C4VFunction(Fn));
}
assert(Fn);
if (Type == PARSER)
@ -1898,8 +1901,9 @@ C4Value C4AulParse::Parse_ConstPropList(const C4PropListStatic * parent, C4Strin
throw new C4AulParseError(this, "internal error: constant proplist is not static");
if (p->GetParent() != parent || p->GetParentKeyName() != Name)
{
// the proplist was overwritten by one in another script
// create a temporary replacement
// the proplist is from another definition, so don't change it
// create a replacement for this defnition
// FIXME: shouldn't the copy be made in parse, like functions are handled?
v.SetPropList(C4PropList::NewAnon(NULL, parent, Name));
p = v.getPropList()->IsStatic();
}
@ -2921,8 +2925,34 @@ bool C4ScriptHost::Parse()
}
Func0 = FuncL = 0;
C4PropList * p = GetPropList();
for (std::list<C4ScriptHost *>::iterator s = SourceScripts.begin(); s != SourceScripts.end(); ++s)
{
// append all funcs and local variable initializations
const C4Property * prop = (*s)->LocalValues.First();
while (prop)
{
switch(prop->Value.GetType())
{
case C4V_Function:
{
C4AulScriptFunc * sf = prop->Value.getFunction()->SFunc();
assert(sf->pOrgScript == *s);
C4AulScriptFunc *sfc;
if (sf->pOrgScript != this)
sfc = new C4AulScriptFunc(this, *sf);
else
sfc = sf;
sfc->SetOverloaded(p->GetFunc(sf->Name));
p->SetPropertyByS(prop->Key, C4VFunction(sfc));
}
break;
default:
p->SetPropertyByS(prop->Key, prop->Value);
}
prop = (*s)->LocalValues.Next(prop);
}
if (*s == this)
{
C4AulFunc *f = thisfuncs, *n;
@ -2937,22 +2967,11 @@ bool C4ScriptHost::Parse()
// definition appends
if (GetPropList() && GetPropList()->GetDef() && (*s)->GetPropList() && (*s)->GetPropList()->GetDef())
GetPropList()->GetDef()->IncludeDefinition((*s)->GetPropList()->GetDef());
// append all funcs
C4AulScriptFunc *sf;
for (C4AulFunc *f = (*s)->Func0; f; f = f->Next)
// funcs from other scripts get appended directly from their script
if ((sf = f->SFunc()) && sf->pOrgScript == *s)
{
// append: create copy
C4AulScriptFunc *sfc = new C4AulScriptFunc(this, *sf);
}
// copy local var definitions
for (int ivar = 0; ivar < (*s)->LocalNamed.iSize; ivar ++)
LocalNamed.AddName((*s)->LocalNamed.pNames[ivar]);
}
LinkFunctions();
// parse
C4AulParse state(this, C4AulParse::PARSER);
for (std::list<C4ScriptHost *>::iterator s = SourceScripts.begin(); s != SourceScripts.end(); ++s)

View File

@ -126,6 +126,7 @@ private:
C4Set<C4Property> Properties;
C4PropList * prototype;
bool constant; // if true, this proplist is not changeable
friend class C4ScriptHost;
public:
int32_t Status;
};

View File

@ -215,6 +215,18 @@ public:
}
return 0;
}
void Swap(C4Set<T> * S2)
{
unsigned int Capacity2 = S2->Capacity;
unsigned int Size2 = S2->Size;
T * Table2 = S2->Table;
S2->Capacity = Capacity;
S2->Size = Size;
S2->Table = Table;
Capacity = Capacity2;
Size = Size2;
Table = Table2;
}
};
template<> template<>