mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-15 20:49:56 +00:00
6dcf6c565a
git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@83 ea6a568a-9f4f-0410-981a-c910a81bb256
401 lines
9.8 KiB
D
401 lines
9.8 KiB
D
/*
|
|
Monster - an advanced game scripting language
|
|
Copyright (C) 2007, 2008 Nicolay Korslund
|
|
Email: <korslund@gmail.com>
|
|
WWW: http://monster.snaptoad.com/
|
|
|
|
This file (stack.d) is part of the Monster script language
|
|
package.
|
|
|
|
Monster is distributed as free software: you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License
|
|
version 3, as published by the Free Software Foundation.
|
|
|
|
This program is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
version 3 along with this program. If not, see
|
|
http://www.gnu.org/licenses/ .
|
|
|
|
*/
|
|
|
|
module monster.vm.stack;
|
|
|
|
import std.string;
|
|
import std.stdio;
|
|
import std.utf;
|
|
|
|
import monster.compiler.scopes;
|
|
|
|
import monster.vm.mobject;
|
|
import monster.vm.mclass;
|
|
import monster.vm.arrays;
|
|
import monster.vm.error;
|
|
|
|
// Stack. There's only one global instance, but threads will make
|
|
// copies when they need it.
|
|
CodeStack stack;
|
|
|
|
// A simple stack frame. All data are in chunks of 4 bytes
|
|
struct CodeStack
|
|
{
|
|
private:
|
|
int[] data;
|
|
|
|
int left, total;
|
|
int *pos; // Current position
|
|
|
|
// Frame pointer, used for accessing local variables and parameters
|
|
int *frame;
|
|
int fleft; // A security measure to make sure we don't access
|
|
// variables outside the stack
|
|
|
|
public:
|
|
void init()
|
|
{
|
|
// 100 is just a random number - it should probably be quite a bit
|
|
// larger (but NOT dynamic, since we want to catch run-away code.)
|
|
const int size = 100;
|
|
|
|
data.length = size;
|
|
left = size;
|
|
total = size;
|
|
pos = data.ptr;
|
|
frame = null;
|
|
}
|
|
|
|
// Get the current position index.
|
|
int getPos()
|
|
{
|
|
return total-left;
|
|
}
|
|
|
|
// Sets the current position as the 'frame pointer', and return it.
|
|
int *setFrame()
|
|
{
|
|
frame = pos;
|
|
fleft = left;
|
|
//writefln("setFrame(): new=%s, old=%s", frame, old);
|
|
return frame;
|
|
}
|
|
|
|
// Sets the given frame pointer
|
|
void setFrame(int *frm)
|
|
{
|
|
//writefln("restoring frame: %s", frm);
|
|
frame = frm;
|
|
if(frm is null)
|
|
{
|
|
fleft = 0;
|
|
return;
|
|
}
|
|
fleft = left + (pos-frm);
|
|
assert(fleft >= 0 && fleft <= total);
|
|
}
|
|
|
|
// Reset the stack level to zero.
|
|
void reset()
|
|
{
|
|
left = total;
|
|
pos = data.ptr;
|
|
|
|
fleft = 0;
|
|
frame = null;
|
|
}
|
|
|
|
void pushInt(int i)
|
|
{
|
|
left--;
|
|
if(left<0) overflow("pushInt");
|
|
*pos = i;
|
|
pos++;
|
|
}
|
|
|
|
void pushLong(long i)
|
|
{
|
|
left -= 2;
|
|
if(left<0) overflow("pushLong");
|
|
*(cast(long*)pos) = i;
|
|
pos+=2;
|
|
}
|
|
|
|
int popInt()
|
|
{
|
|
left++;
|
|
if(left>total) overflow("popInt");
|
|
pos--;
|
|
return *pos;
|
|
}
|
|
|
|
long popLong()
|
|
{
|
|
left+=2;
|
|
if(left>total) overflow("popLong");
|
|
pos-=2;
|
|
return *(cast(long*)pos);
|
|
}
|
|
|
|
// Get the pointer from the start of the stack
|
|
int *getStart()
|
|
{
|
|
return cast(int*)data.ptr;
|
|
}
|
|
|
|
// Get the pointer to an int at the given position backwards from
|
|
// the current stack pointer. 0 means the first int, ie. the one we
|
|
// would get if we called popInt. 1 is the next, etc
|
|
int *getInt(int ptr)
|
|
{
|
|
ptr++;
|
|
if(ptr < 1 || ptr > (total-left) )
|
|
fail("CodeStack.getInt() pointer out of range");
|
|
return pos-ptr;
|
|
}
|
|
|
|
// Get the array _beginning_ at ptr
|
|
int[] getInts(int ptr, int len)
|
|
{
|
|
assert(len > 0 && ptr >= len-1);
|
|
if(left+len-ptr>total) overflow("getInts");
|
|
return getInt(ptr)[0..len];
|
|
}
|
|
|
|
// Pops the next len ints off the stack and returns them as an
|
|
// array. The array is ordered as the values were pushed, not as
|
|
// they would have been popped (ie. this function is like popping
|
|
// one big value of the stack.) The array is a direct slice of the
|
|
// stack, so don't store it or use it after pushing other values.
|
|
int[] popInts(int len)
|
|
{
|
|
assert(len > 0);
|
|
int[] r = getInts(len-1, len);
|
|
pop(len);
|
|
assert(r.length == len);
|
|
return r;
|
|
}
|
|
|
|
void pushInts(int[] arr)
|
|
{
|
|
left -= arr.length;
|
|
if(left<0) overflow("pushInts");
|
|
pos[0..arr.length] = arr[];
|
|
pos+=arr.length;
|
|
}
|
|
|
|
// Get an int a given position from frame pointer. Can be negative
|
|
// or positive. 0 means the int at the pointer, -1 is the one before
|
|
// and 1 the one after, etc.
|
|
int *getFrameInt(int ptr)
|
|
{
|
|
assert(frame !is null);
|
|
assert(frame <= pos);
|
|
if(ptr < (fleft-total) || ptr >= fleft)
|
|
fail("CodeStack.getFrameInt() pointer out of range");
|
|
return frame+ptr;
|
|
}
|
|
|
|
// Pushing and poping objects of the stack - will actually push/pop
|
|
// their index.
|
|
void pushObject(MonsterObject *mo)
|
|
{ pushInt(mo.getIndex); }
|
|
|
|
MonsterObject *popObject()
|
|
{ return getMObject(cast(MIndex)popInt()); }
|
|
|
|
// Push arrays of objects. TODO: These do memory allocation, and I'm
|
|
// not sure that belongs here. I will look into it later.
|
|
void pushObjects(MonsterObject *objs[])
|
|
{
|
|
int[] indices;
|
|
indices.length = objs.length;
|
|
|
|
foreach(i, mo; objs)
|
|
indices[i] = mo.getIndex();
|
|
|
|
pushIArray(indices);
|
|
}
|
|
|
|
MonsterObject*[] popObjects()
|
|
{
|
|
MIndex[] indices = cast(MIndex[]) popIArray();
|
|
MonsterObject* objs[];
|
|
|
|
objs.length = indices.length;
|
|
foreach(i, ind; indices)
|
|
objs[i] = getMObject(ind);
|
|
|
|
return objs;
|
|
}
|
|
|
|
// Push and pop array references.
|
|
void pushArray(ArrayRef *ar)
|
|
{ pushInt(ar.getIndex); }
|
|
ArrayRef *popArray()
|
|
{ return arrays.getRef(cast(AIndex)popInt()); }
|
|
ArrayRef *getArray(int i)
|
|
{ return arrays.getRef(cast(AIndex)*getInt(i)); }
|
|
|
|
// More easy versions. Note that pushArray() will create a new array
|
|
// reference each time it is called! Only use it if this is what you
|
|
// want.
|
|
void pushCArray(dchar[] str) { pushArray(arrays.create(str)); }
|
|
void pushIArray(int[] str) { pushArray(arrays.create(str)); }
|
|
void pushUArray(uint[] str) { pushArray(arrays.create(str)); }
|
|
void pushLArray(long[] str){ pushArray(arrays.create(str)); }
|
|
void pushULArray(ulong[] str) { pushArray(arrays.create(str)); }
|
|
void pushFArray(float[] str) { pushArray(arrays.create(str)); }
|
|
void pushDArray(double[] str) { pushArray(arrays.create(str)); }
|
|
void pushAArray(AIndex[] str) { pushArray(arrays.create(str)); }
|
|
void pushMArray(AIndex[] str) { pushArray(arrays.create(str)); }
|
|
|
|
alias pushCArray pushArray, pushString;
|
|
alias pushIArray pushArray;
|
|
alias pushFArray pushArray;
|
|
alias pushAArray pushArray;
|
|
alias pushString8 pushArray, pushString;
|
|
|
|
dchar[] popCArray() { return popArray().carr; }
|
|
int[] popIArray() { return popArray().iarr; }
|
|
float[] popFArray() { return popArray().farr; }
|
|
AIndex[] popAArray() { return popArray().aarr; }
|
|
alias popCArray popString;
|
|
|
|
void pushString8(char[] str)
|
|
{ pushArray(toUTF32(str)); }
|
|
char[] popString8()
|
|
{ return toUTF8(popString()); }
|
|
|
|
// For multibyte arrays
|
|
void pushArray(int[] str, int size)
|
|
{ pushArray(arrays.create(str, size)); }
|
|
|
|
// Various convenient conversion templates. These will be inlined,
|
|
// so don't worry :) The *4() functions are for types that are 4
|
|
// bytes long. These are mostly intended for use in native
|
|
// functions, so there is no equivalent of getFrameInt and similar
|
|
// functions.
|
|
void push4(T)(T var)
|
|
{
|
|
static assert(T.sizeof == 4);
|
|
pushInt(*(cast(int*)&var));
|
|
}
|
|
T pop4(T)()
|
|
{
|
|
static assert(T.sizeof == 4);
|
|
int i = popInt();
|
|
return *(cast(T*)&i);
|
|
}
|
|
T* get4(T)(int ptr) { return cast(T*)getInt(ptr); }
|
|
|
|
// 64 bit version
|
|
void push8(T)(T var)
|
|
{
|
|
static assert(T.sizeof == 8);
|
|
pushLong(*(cast(long*)&var));
|
|
}
|
|
T pop8(T)()
|
|
{
|
|
static assert(T.sizeof == 8);
|
|
long l = popLong();
|
|
return *(cast(T*)&l);
|
|
}
|
|
|
|
// Bools are 1 byte in D
|
|
void pushBool(bool b)
|
|
{
|
|
if(b) pushInt(1);
|
|
else pushInt(0);
|
|
}
|
|
bool popBool() { return popInt() != 0; }
|
|
alias get4!(bool) getBool;
|
|
|
|
// Template conversions
|
|
|
|
alias push4!(MIndex) pushMIndex;
|
|
alias pop4!(MIndex) popMIndex;
|
|
alias get4!(MIndex) getMIndex;
|
|
|
|
alias push4!(AIndex) pushAIndex;
|
|
alias pop4!(AIndex) popAIndex;
|
|
alias get4!(AIndex) getAIndex;
|
|
|
|
alias push4!(uint) pushUint;
|
|
alias pop4!(uint) popUint;
|
|
alias get4!(uint) getUint;
|
|
|
|
alias get4!(long) getLong;
|
|
|
|
alias push8!(ulong) pushUlong;
|
|
alias pop8!(ulong) popUlong;
|
|
alias get4!(ulong) getUlong;
|
|
|
|
alias push4!(float) pushFloat;
|
|
alias pop4!(float) popFloat;
|
|
alias get4!(float) getFloat;
|
|
|
|
alias push8!(double) pushDouble;
|
|
alias pop8!(double) popDouble;
|
|
alias get4!(double) getDouble;
|
|
|
|
alias push4!(dchar) pushChar;
|
|
alias pop4!(dchar) popChar;
|
|
alias get4!(dchar) getChar;
|
|
|
|
// Pop off and ignore a given amount of values
|
|
void pop(int num)
|
|
{
|
|
left += num;
|
|
if(left>total) overflow("pop1");
|
|
pos -= num;
|
|
}
|
|
|
|
// Pop off and ignore given values, but remember the top values
|
|
void pop(uint num, uint keep)
|
|
{
|
|
assert(keep>0);
|
|
assert(num>0);
|
|
|
|
left += num;
|
|
|
|
// We move the stack pointer back num values, but we access as far
|
|
// back as num+keep values, so we need to check that we are still
|
|
// within the stack.
|
|
if((left+keep)>total) overflow("pop2");
|
|
|
|
int *from = pos-keep; // Where to get the 'keep' values from
|
|
int *to = from-num; // Where they end up
|
|
pos -= num; // Where the final stack pointer should be
|
|
|
|
assert(to < from);
|
|
|
|
// Copy the values
|
|
for(; keep>0; keep--)
|
|
*(to++) = *(from++);
|
|
}
|
|
|
|
void debugPrint()
|
|
{
|
|
writefln("Stack:");
|
|
foreach(int i, int val; data[0..total-left])
|
|
writefln("%s: %s", i, val);
|
|
writefln();
|
|
}
|
|
|
|
private:
|
|
void overflow(char[] func)
|
|
{
|
|
char[] res;
|
|
if(left<0)
|
|
res = format("Stack overflow by %s ints in CodeStack.%s()",
|
|
-left, func);
|
|
else if(left>total)
|
|
res = format("Stack underflow by %s ints in CodeStack.%s()",
|
|
left-total, func);
|
|
else res = format("Internal error in CodeStack.%s(), left=%s, total=%s",
|
|
func, left, total);
|
|
fail(res);
|
|
}
|
|
}
|