diff --git a/include/lgi/common/Variant.h b/include/lgi/common/Variant.h
--- a/include/lgi/common/Variant.h
+++ b/include/lgi/common/Variant.h
@@ -1,474 +1,480 @@
/**
\file
\author Matthew Allen
\brief Variant class.\n
Copyright (C), Matthew Allen
*/
#ifndef __LVariant_H__
#define __LVariant_H__
#undef Bool
#include "lgi/common/DateTime.h"
#include "lgi/common/Containers.h"
#include "lgi/common/HashTable.h"
#include "lgi/common/LgiString.h"
class LCompiledCode;
#if !defined(_MSC_VER) && !defined(LINUX) && (!HAIKU64)
// For all Mac and Haiku32
#define LVARIANT_SIZET 1
#define LVARIANT_SSIZET 1
#endif
/// The different types the varient can be.
/// \sa LVariant::TypeToString to convert to string.
enum LVariantType
{
// Main types
/// Null type (0)
GV_NULL,
/// 32-bit integer (1)
GV_INT32,
/// 64-bit integer (2)
GV_INT64,
/// true or false boolean. (3)
GV_BOOL,
/// C++ double (4)
GV_DOUBLE,
/// Null terminated string value (5)
GV_STRING,
/// Block of binary data (6)
GV_BINARY,
/// List of LVariant (7)
GV_LIST,
/// Pointer to LDom object (8)
GV_DOM,
/// DOM reference, ie. a variable in a DOM object (9)
GV_DOMREF,
/// Untyped pointer (10)
GV_VOID_PTR,
/// LDateTime class. (11)
GV_DATETIME,
/// Hash table class, containing pointers to LVariants (12)
GV_HASHTABLE,
// Scripting language operator (13)
GV_OPERATOR,
// Custom scripting lang type (14)
GV_CUSTOM,
// Wide string (15)
GV_WSTRING,
// LSurface ptr (16)
GV_LSURFACE,
/// Pointer to LView (17)
GV_LVIEW,
/// Pointer to LMouse (18)
GV_LMOUSE,
/// Pointer to LKey (19)
GV_LKEY,
/// Pointer to LStream (20)
GV_STREAM,
/// The maximum value for the variant type. (21)
/// (This is used by the scripting engine to refer to a LVariant itself)
GV_MAX,
};
/// Language operators
enum LOperator
{
OpNull,
OpAssign,
OpPlus,
OpUnaryPlus,
OpMinus,
OpUnaryMinus,
OpMul,
OpDiv,
OpMod,
OpLessThan,
OpLessThanEqual,
OpGreaterThan,
OpGreaterThanEqual,
OpEquals,
OpNotEquals,
OpPlusEquals,
OpMinusEquals,
OpMulEquals,
OpDivEquals,
OpPostInc,
OpPostDec,
OpPreInc,
OpPreDec,
OpAnd,
OpOr,
OpNot,
+ OpBitwiseAnd,
+ OpBitwiseOr,
+ OpBitwiseNegate,
+ OpBitwiseXor,
+ OpShiftLeft,
+ OpShiftRight,
};
class LgiClass LCustomType : public LDom
{
protected:
struct CustomField : public LDom
{
ssize_t Offset;
ssize_t Bytes;
ssize_t ArrayLen;
LVariantType Type;
LString Name;
LCustomType *Nested;
const char *GetClass() override { return "LCustomType.CustomField"; }
ssize_t Sizeof();
bool GetVariant(const char *Name, LVariant &Value, const char *Array = NULL) override;
};
public:
struct Method : public LDom
{
LString Name;
LArray Params;
size_t Address = -1;
int FrameSize = -1;
const char *GetClass() override { return "LCustomType.Method"; }
};
protected:
// Global vars
int Pack;
size_t Size;
LString Name;
// Fields
LArray Flds;
LHashTbl, int> FldMap;
// Methods
LArray Methods;
LHashTbl, Method*> MethodMap;
// Private methods
ssize_t PadSize();
public:
LCustomType(const char *name, int pack = 1);
LCustomType(const char16 *name, int pack = 1);
~LCustomType();
const char *GetClass() override { return "LCustomType"; }
size_t Sizeof();
const char *GetName() { return Name; }
ssize_t Members() { return Flds.Length(); }
int AddressOf(const char *Field);
int IndexOf(const char *Field);
bool DefineField(const char *Name, LVariantType Type, int Bytes, int ArrayLen = 1);
bool DefineField(const char *Name, LCustomType *Type, int ArrayLen = 1);
Method *DefineMethod(const char *Name, LArray &Params, size_t Address);
Method *GetMethod(const char *Name);
// Field access. You can't use the LDom interface to get/set member variables because
// there is no provision for the 'This' pointer.
bool Get(int Index, LVariant &Out, uint8_t *This, int ArrayIndex = 0);
bool Set(int Index, LVariant &In, uint8_t *This, int ArrayIndex = 0);
// Dom access. However the DOM can be used to access information about the type itself.
// Which doesn't need a 'This' pointer.
bool GetVariant(const char *Name, LVariant &Value, const char *Array = NULL) override;
bool SetVariant(const char *Name, LVariant &Value, const char *Array = NULL) override;
bool CallMethod(const char *MethodName, LScriptArguments &Args) override;
};
/// A class that can be different types
class LgiClass LVariant
{
public:
typedef LHashTbl,LVariant*> LHash;
/// The type of the variant
LVariantType Type;
/// The value of the variant
union
{
/// Valid when Type == #GV_INT32
int Int;
/// Valid when Type == #GV_BOOL
bool Bool;
/// Valid when Type == #GV_INT64
int64 Int64;
/// Valid when Type == #GV_DOUBLE
double Dbl;
/// Valid when Type == #GV_STRING
char *String;
/// Valid when Type == #GV_WSTRING
char16 *WString;
/// Valid when Type == #GV_DOM
LDom *Dom;
/// Valid when Type is #GV_VOID_PTR, #GV_LVIEW, #GV_LMOUSE or #GV_LKEY
void *Ptr;
/// Valid when Type == #GV_BINARY
struct _Binary
{
ssize_t Length;
void *Data;
} Binary;
/// Valid when Type == #GV_LIST
List *Lst;
/// Valid when Type == #GV_HASHTABLE
LHash *Hash;
/// Valid when Type == #GV_DATETIME
LDateTime *Date;
/// Valid when Type == #GV_CUSTOM
struct _Custom
{
LCustomType *Dom;
uint8_t *Data;
bool operator == (_Custom &c)
{
return Dom == c.Dom &&
Data == c.Data;
}
} Custom;
/// Valid when Type == #GV_DOMREF
struct _DomRef
{
/// The pointer to the dom object
LDom *Dom;
/// The name of the variable to set/get in the dom object
char *Name;
} DomRef;
/// Valid when Type == #GV_OPERATOR
LOperator Op;
/// Valid when Type == #GV_LSURFACE
struct
{
class LSurface *Ptr;
bool Own;
LSurface *Release()
{
auto p = Ptr;
Ptr = NULL;
Own = false;
return p;
}
} Surface;
/// Valid when Type == #GV_STREAM
struct
{
class LStreamI *Ptr;
bool Own;
LStreamI *Release()
{
auto p = Ptr;
Ptr = NULL;
Own = false;
return p;
}
} Stream;
/// Valid when Type == #GV_LVIEW
class LView *View;
/// Valid when Type == #GV_LMOUSE
class LMouse *Mouse;
/// Valid when Type == #GV_LKEY
class LKey *Key;
} Value;
/// Constructor to null
LVariant();
/// Constructor for integers
LVariant(int32_t i);
LVariant(uint32_t i);
LVariant(int64_t i);
LVariant(uint64_t i);
#if LVARIANT_SIZET
LVariant(size_t i);
#endif
#if LVARIANT_SSIZET
LVariant(ssize_t i);
#endif
/// Constructor for double
LVariant(double i);
/// Constructor for string
LVariant(const char *s);
/// Constructor for wide string
LVariant(const char16 *s);
/// Constructor for ptr
LVariant(void *p);
/// Constructor for DOM ptr
LVariant(LDom *p);
/// Constructor for DOM variable reference
LVariant(LDom *p, char *name);
/// Constructor for date
LVariant(const LDateTime *d);
/// Constructor for variant
LVariant(LVariant const &v);
/// Constructor for operator
LVariant(LOperator Op);
/// Destructor
~LVariant();
/// Assign bool value
LVariant &operator =(bool i);
/// Assign an integer value
LVariant &operator =(int32_t i);
LVariant &operator =(uint32_t i);
LVariant &operator =(int64_t i);
LVariant &operator =(uint64_t i);
#if LVARIANT_SIZET
LVariant &operator =(size_t i);
#endif
#if LVARIANT_SSIZET
LVariant &operator =(ssize_t i);
#endif
/// Assign double value
LVariant &operator =(double i);
/// Assign string value (makes a copy)
LVariant &operator =(const char *s);
/// Assign a wide string value (makes a copy)
LVariant &operator =(const char16 *s);
/// Assign another variant value
LVariant &operator =(LVariant const &i);
/// Assign value to a void ptr
LVariant &operator =(void *p);
/// Assign value to DOM ptr
LVariant &operator =(LDom *p);
/// Assign value to be a date/time
LVariant &operator =(const LDateTime *d);
LVariant &operator =(class LView *p);
LVariant &operator =(class LMouse *p);
LVariant &operator =(class LKey *k);
LVariant &operator =(class LStream *s);
bool operator ==(LVariant &v);
bool operator !=(LVariant &v) { return !(*this == v); }
/// Sets the value to a DOM variable reference
bool SetDomRef(LDom *obj, char *name);
/// Sets the value to a copy of block of binary data
bool SetBinary(ssize_t Len, void *Data, bool Own = false);
/// Sets the value to a copy of the list
List *SetList(List *Lst = NULL);
/// Sets the value to a hashtable
bool SetHashTable(LHash *Table = NULL, bool Copy = true);
/// Set the value to a surface
bool SetSurface(class LSurface *Ptr, bool Own);
/// Set the value to a stream
bool SetStream(class LStreamI *Ptr, bool Own);
/// Returns the string if valid (will convert a GV_WSTRING to utf)
char *Str();
/// Returns the value as an LString
LString LStr();
/// Returns a wide string if valid (will convert a GV_STRING to wide)
char16 *WStr();
/// Returns the string, releasing ownership of the memory to caller and
/// changing this LVariant to GV_NULL.
char *ReleaseStr();
/// Returns the wide string, releasing ownership of the memory to caller and
/// changing this LVariant to GV_NULL.
char16 *ReleaseWStr();
/// Sets the variant to a heap string and takes ownership of it
bool OwnStr(char *s);
/// Sets the variant to a wide heap string and takes ownership of it
bool OwnStr(char16 *s);
/// Sets the variant to NULL
void Empty();
/// Returns the byte length of the data
int64 Length();
/// True if currently a int
bool IsInt();
/// True if currently a bool
bool IsBool();
/// True if currently a double
bool IsDouble();
/// True if currently a string
bool IsString();
/// True if currently a binary block
bool IsBinary();
/// True if currently null
bool IsNull();
/// Changes the variant's type, maintaining the value where possible. If
/// no conversion is available then nothing happens.
LVariant &Cast(LVariantType NewType);
/// Casts the value to int, from whatever source type. The
/// LVariant type does not change after calling this.
int32 CastInt32() const;
/// Casts the value to a 64 bit int, from whatever source type. The
/// LVariant type does not change after calling this.
int64 CastInt64() const;
/// Casts the value to double, from whatever source type. The
/// LVariant type does not change after calling this.
double CastDouble() const;
/// Cast to a string from whatever source type, the LVariant will
/// take the type GV_STRING after calling this. This is because
/// returning a static string is not thread safe.
char *CastString();
/// Casts to a DOM ptr
LDom *CastDom() const;
/// Casts to a boolean. You probably DON'T want to use this function. The
/// behavior for strings -> bool is such that if the string is value it
/// always evaluates to true, and false if it's not a valid string. Commonly
/// what you want is to evaluate whether the string is zero or non-zero in
/// which cast you should use "CastInt32() != 0" instead.
bool CastBool() const;
/// Returns the pointer if available.
void *CastVoidPtr() const;
/// Returns a LView
LView *CastView() const { return Type == GV_LVIEW ? Value.View : NULL; }
/// List insert
bool Add(LVariant *v, int Where = -1);
/// Converts the varient type to a string
static const char *TypeToString(LVariantType t);
/// Converts an operator to a string
static const char *OperatorToString(LOperator op);
/// Converts the value to a string description include type.
LString ToString();
};
// General collection of arguments and a return value
class LgiClass LScriptArguments : public LArray
{
friend class LScriptEngine;
friend class LVirtualMachine;
friend class LVirtualMachinePriv;
friend struct ExecuteFunctionState;
LVirtualMachineI *Vm = NULL;
class LStream *Console = NULL;
LVariant *LocalReturn = NULL; // Owned by this instance
LVariant *Return = NULL;
const char *File = NULL;
int Line = 0;
LString ExceptionMsg;
ssize_t Address;
public:
static LStream NullConsole;
LScriptArguments(LVirtualMachineI *vm, LVariant *ret = NULL, LStream *console = NULL, ssize_t address = -1);
~LScriptArguments();
LVirtualMachineI *GetVm() { return Vm; }
void SetVm(LVirtualMachineI *vm) { Vm = vm; }
LVariant *GetReturn() { return Return; } // Must never be NULL.
LStream *GetConsole() { return Console; }
bool HasException() { return File != NULL || ExceptionMsg.Get() || Line != 0; }
bool Throw(const char *File, int Line, const char *Msg, ...);
// Accessor shortcuts
const char *StringAt(size_t i);
int32_t Int32At(size_t i, int32_t Default = 0);
int64_t Int64At(size_t i, int64_t Default = 0);
double DoubleAt(size_t i, double Default = 0);
LDom *DomAt(size_t i);
};
#endif
diff --git a/src/common/Coding/Instructions.h b/src/common/Coding/Instructions.h
--- a/src/common/Coding/Instructions.h
+++ b/src/common/Coding/Instructions.h
@@ -1,2081 +1,2187 @@
/*
This file is included in both the LVirtualMachinePriv::Execute and LVirtualMachinePriv::Decompile
That way the "parsing" of instructions is the same.
During decompile the define VM_DECOMP is active.
During execution the define VM_EXECUTE is active.
*/
#ifdef VM_EXECUTE
#define Resolve() &Scope[c.r->Scope][c.r->Index]; c.r++
#define LResolveRef(nm) LVariant *nm =
#else
#define Resolve() c.r++
#define LResolveRef(nm)
// LVarRef *
#endif
default:
{
- #if VM_DECOMP
+ #if 1// VM_DECOMP
if (Log)
- Log->Print("\t%p Unknown instruction %i (0x%x)\n",
+ Log->Print("\t%p Unknown instruction %i (%s)\n",
CurrentScriptAddress - 1,
- c.u8[-1], c.u8[-1]);
+ c.u8[-1], InstToString((LInstruction)c.u8[-1]));
#endif
OnException(_FL, CurrentScriptAddress, "Unknown instruction");
SetScriptError;
break;
}
case INop:
{
#if VM_DECOMP
if (Log)
Log->Print("%p Nop\n", CurrentScriptAddress - 1);
#endif
break;
}
case ICast:
{
#if VM_DECOMP
if (Log)
Log->Print("%p Cast %s",
CurrentScriptAddress - 1,
c.r[0].GetStr());
#endif
LResolveRef(Var) Resolve();
uint8_t Type = *c.u8++;
#if VM_DECOMP
if (Log)
Log->Print(" to %s\n", LVariant::TypeToString((LVariantType)Type));
#endif
#if VM_EXECUTE
switch (Type)
{
case GV_INT32:
{
*Var = Var->CastInt32();
break;
}
case GV_STRING:
{
*Var = Var->CastString();
break;
}
case GV_DOM:
{
*Var = Var->CastDom();
break;
}
case GV_DOUBLE:
{
*Var = Var->CastDouble();
break;
}
case GV_INT64:
{
*Var = Var->CastInt32();
break;
}
case GV_BOOL:
{
*Var = Var->CastBool();
break;
}
default:
{
LString s;
s.Printf("%s ICast warning: unknown type %i/%s\n",
Code->AddrToSourceRef(CurrentScriptAddress),
Var->Type,
LVariant::TypeToString(Var->Type));
if (Log)
Log->Write(s, s.Length());
if (LVirtualMachine::BreakOnWarning)
OnException(NULL, -1, CurrentScriptAddress, s);
Status = ScriptWarning;
break;
}
}
#endif
break;
}
case IAssign:
{
#if VM_DECOMP
if (Log)
Log->Print("%p Assign %s <- %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Src) Resolve();
#ifdef VM_EXECUTE
CheckParam(Dst != Src);
*Dst = *Src;
#endif
break;
}
case IJump:
{
#if VM_DECOMP
if (Log)
Log->Print("%p Jump by %i (to 0x%x)\n",
CurrentScriptAddress - 1,
c.i32[0],
CurrentScriptAddress + 4 + c.i32[0]);
#endif
#ifdef VM_EXECUTE
int32 Jmp = *
#endif
c.i32++;
#ifdef VM_EXECUTE
CheckParam(Jmp != 0);
c.u8 += Jmp;
#endif
break;
}
case IJumpZero:
{
#if VM_DECOMP
if (Log)
Log->Print("%p JumpZ(%s) by 0x%x\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.i32[1]);
#endif
LResolveRef(Exp) Resolve();
#ifdef VM_EXECUTE
int32 Jmp = *
#endif
c.i32++;
#ifdef VM_EXECUTE
CheckParam(Jmp != 0);
if (!Exp->CastInt32())
c.u8 += Jmp;
#endif
break;
}
// case IUnaryPlus:
case IUnaryMinus:
{
#if VM_DECOMP
if (Log)
Log->Print("%p UnaryMinus %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr());
#endif
LResolveRef(Var) Resolve();
#ifdef VM_EXECUTE
switch (Var->Type)
{
case GV_DOUBLE:
*Var = -Var->CastDouble();
break;
case GV_INT64:
*Var = -Var->CastInt64();
break;
default:
*Var = -Var->CastInt32();
break;
}
#endif
break;
}
+case IBitwiseOr:
+{
+ #if VM_DECOMP
+ if (Log)
+ Log->Print("%p BitwiseOr %s | %s\n",
+ CurrentScriptAddress - 1,
+ c.r[0].GetStr(),
+ c.r[1].GetStr());
+ #endif
+
+ LResolveRef(Dst) Resolve();
+ LResolveRef(Src) Resolve();
+
+ #ifdef VM_EXECUTE
+ *Dst = Dst->CastInt64() | Src->CastInt64();
+ #endif
+ break;
+}
+case IBitwiseAnd:
+{
+ #if VM_DECOMP
+ if (Log)
+ Log->Print("%p BitwiseAnd %s & %s\n",
+ CurrentScriptAddress - 1,
+ c.r[0].GetStr(),
+ c.r[1].GetStr());
+ #endif
+
+ LResolveRef(Dst) Resolve();
+ LResolveRef(Src) Resolve();
+
+ #ifdef VM_EXECUTE
+ *Dst = Dst->CastInt64() & Src->CastInt64();
+ #endif
+ break;
+}
+case IBitwiseXor:
+{
+ #if VM_DECOMP
+ if (Log)
+ Log->Print("%p BitwiseXor %s & %s\n",
+ CurrentScriptAddress - 1,
+ c.r[0].GetStr(),
+ c.r[1].GetStr());
+ #endif
+
+ LResolveRef(Dst) Resolve();
+ LResolveRef(Src) Resolve();
+
+ #ifdef VM_EXECUTE
+ *Dst = Dst->CastInt64() ^ Src->CastInt64();
+ #endif
+ break;
+}
+case IBitwiseNegate:
+{
+ #if VM_DECOMP
+ if (Log)
+ Log->Print("%p BitwiseNegate %s = ~%s\n",
+ CurrentScriptAddress - 1,
+ c.r[0].GetStr(),
+ c.r[0].GetStr());
+ #endif
+
+ LResolveRef(Dst) Resolve();
+ #ifdef VM_EXECUTE
+ *Dst = ~Dst->CastInt64();
+ #endif
+ break;
+}
+case IShiftLeft:
+{
+ #if VM_DECOMP
+ if (Log)
+ Log->Print("%p ShiftLeft %s << %s\n",
+ CurrentScriptAddress - 1,
+ c.r[0].GetStr(),
+ c.r[1].GetStr());
+ #endif
+
+ LResolveRef(Dst) Resolve();
+ LResolveRef(Src) Resolve();
+
+ #ifdef VM_EXECUTE
+ *Dst = Dst->CastInt64() << Src->CastInt64();
+ #endif
+ break;
+}
+case IShiftRight:
+{
+ #if VM_DECOMP
+ if (Log)
+ Log->Print("%p Plus %s += %s\n",
+ CurrentScriptAddress - 1,
+ c.r[0].GetStr(),
+ c.r[1].GetStr());
+ #endif
+
+ LResolveRef(Dst) Resolve();
+ LResolveRef(Src) Resolve();
+
+ #ifdef VM_EXECUTE
+ *Dst = Dst->CastInt64() >> Src->CastInt64();
+ #endif
+ break;
+}
case IPlus:
case IPlusEquals:
{
#if VM_DECOMP
if (Log)
Log->Print("%p Plus %s += %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Src) Resolve();
#ifdef VM_EXECUTE
if (Dst->Str())
{
size_t dlen = strlen(Dst->Str());
char *ss;
LVariant SrcTmp;
switch (Src->Type)
{
case GV_NULL:
ss = (char*)"(null)";
break;
case GV_STRING:
ss = Src->Str();
break;
default:
SrcTmp = *Src;
ss = SrcTmp.CastString();
break;
}
if (ss)
{
size_t slen = strlen(ss);
char *s = new char[slen + dlen + 1];
if (s)
{
memcpy(s, Dst->Value.String, dlen);
memcpy(s + dlen, ss, slen);
s[dlen + slen] = 0;
DeleteArray(Dst->Value.String);
Dst->Value.String = s;
}
}
}
else switch (DecidePrecision(Dst->Type, Src->Type))
{
case GV_DOUBLE:
*Dst = Dst->CastDouble() + Src->CastDouble();
break;
case GV_INT64:
*Dst = Dst->CastInt64() + Src->CastInt64();
break;
default:
*Dst = Dst->CastInt32() + Src->CastInt32();
break;
}
#endif
break;
}
case IMinus:
case IMinusEquals:
{
#if VM_DECOMP
if (Log)
Log->Print("%p Minus %s -= %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Src) Resolve();
#ifdef VM_EXECUTE
switch (DecidePrecision(Dst->Type, Src->Type))
{
case GV_DOUBLE:
*Dst = Dst->CastDouble() - Src->CastDouble();
break;
case GV_INT64:
*Dst = Dst->CastInt64() - Src->CastInt64();
break;
default:
*Dst = Dst->CastInt32() - Src->CastInt32();
break;
}
#endif
break;
}
case IMul:
case IMulEquals:
{
#if VM_DECOMP
if (Log)
Log->Print("%p Mul %s *= %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Src) Resolve();
#ifdef VM_EXECUTE
switch (DecidePrecision(Dst->Type, Src->Type))
{
case GV_DOUBLE:
*Dst = Dst->CastDouble() * Src->CastDouble();
break;
case GV_INT64:
*Dst = Dst->CastInt64() * Src->CastInt64();
break;
default:
*Dst = Dst->CastInt32() * Src->CastInt32();
break;
}
#endif
break;
}
case IDiv:
case IDivEquals:
{
#if VM_DECOMP
if (Log)
Log->Print("%p Div %s /= %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Src) Resolve();
#ifdef VM_EXECUTE
switch (DecidePrecision(Dst->Type, Src->Type))
{
case GV_DOUBLE:
*Dst = Dst->CastDouble() / Src->CastDouble();
break;
case GV_INT64:
*Dst = Dst->CastInt64() / Src->CastInt64();
break;
default:
*Dst = Dst->CastInt32() / Src->CastInt32();
break;
}
#endif
break;
}
case IMod:
{
#if VM_DECOMP
if (Log)
Log->Print("%p Mod %s %%= %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Src) Resolve();
#ifdef VM_EXECUTE
switch (DecidePrecision(Dst->Type, Src->Type))
{
case GV_DOUBLE:
*Dst = fmod(Dst->CastDouble(), Src->CastDouble());
break;
case GV_INT64:
*Dst = Dst->CastInt64() % Src->CastInt64();
break;
default:
*Dst = Dst->CastInt32() % Src->CastInt32();
break;
}
#endif
break;
}
case IPostInc:
case IPreInc:
{
#if VM_DECOMP
if (Log)
Log->Print("%p PostInc %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr());
#endif
LResolveRef(v) Resolve();
#ifdef VM_EXECUTE
switch (v->Type)
{
case GV_DOUBLE:
*v = v->Value.Dbl + 1;
break;
case GV_INT64:
*v = v->Value.Int64 + 1;
break;
default:
*v = v->CastInt32() + 1;
break;
}
#endif
break;
}
case IPostDec:
case IPreDec:
{
#if VM_DECOMP
if (Log)
Log->Print("%p PostDec %sn",
CurrentScriptAddress - 1,
c.r[0].GetStr());
#endif
LResolveRef(v) Resolve();
#ifdef VM_EXECUTE
switch (v->Type)
{
case GV_DOUBLE:
*v = v->Value.Dbl - 1;
break;
case GV_INT64:
*v = v->Value.Int64 - 1;
break;
default:
*v = v->CastInt32() - 1;
break;
}
#endif
break;
}
case IEquals:
{
#if VM_DECOMP
if (Log)
Log->Print("%p %s == %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Src) Resolve();
#ifdef VM_EXECUTE
*Dst = CompareVariants(Dst, Src) == 0;
#endif
break;
}
case INotEquals:
{
#if VM_DECOMP
if (Log)
Log->Print( "%p %s != %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Src) Resolve();
#ifdef VM_EXECUTE
*Dst = CompareVariants(Dst, Src) != 0;
#endif
break;
}
case ILessThan:
{
#if VM_DECOMP
if (Log)
Log->Print("%p %s < %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Src) Resolve();
#ifdef VM_EXECUTE
*Dst = CompareVariants(Dst, Src) < 0;
#endif
break;
}
case ILessThanEqual:
{
#if VM_DECOMP
if (Log)
Log->Print( "%p %s < %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Src) Resolve();
#ifdef VM_EXECUTE
*Dst = CompareVariants(Dst, Src) <= 0;
#endif
break;
}
case IGreaterThan:
{
#if VM_DECOMP
if (Log)
Log->Print("%p %s < %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Src) Resolve();
#ifdef VM_EXECUTE
*Dst = CompareVariants(Dst, Src) > 0;
#endif
break;
}
case IGreaterThanEqual:
{
#if VM_DECOMP
if (Log)
Log->Print("%p %s < %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Src) Resolve();
#ifdef VM_EXECUTE
*Dst = CompareVariants(Dst, Src) >= 0;
#endif
break;
}
case ICallMethod:
{
LFunc *Meth = *c.fn++;
if (!Meth)
{
Log->Print( "%s ICallMethod error: No method struct.\n",
Code->AddrToSourceRef(CurrentScriptAddress - sizeof(Meth)));
SetScriptError;
break;
}
#ifdef VM_DECOMP
if (Log)
{
Log->Print("%p Call: %s = %s(",
CurrentScriptAddress - sizeof(Meth) - 1,
c.r[0].GetStr(),
Meth->Method.Get());
}
#endif
LResolveRef(Ret) Resolve();
uint16 Args = *c.u16++;
#ifdef VM_EXECUTE
LScriptArguments Arg(Vm, Ret, NULL, CurrentScriptAddress);
#endif
for (int i=0; iPrint("%s%s", i?", ":"", c.r[0].GetStr());
#endif
#if VM_EXECUTE
Arg[i] = Resolve();
CheckParam(Arg[i] != NULL);
#else
c.r++;
#endif
}
#if VM_DECOMP
if (Log)
Log->Print(")\n");
#endif
#if VM_EXECUTE
LHostFunc *Hf = dynamic_cast(Meth);
if (Hf)
{
if (!(Hf->Context->*(Hf->Func))(Arg))
{
if (Log)
Log->Print( "%s ICallMethod error: Method '%s' failed.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
Meth->Method.Get());
SetScriptError;
}
}
else
{
// Fixme
if (!Meth->Call(NULL, Arg))
{
if (Log)
Log->Print( "%s ICallMethod error: Method '%s' failed.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
Meth->Method.Get());
SetScriptError;
}
}
ON_EXCEPTION(Arg);
#endif
break;
}
case ICallScript:
{
int32 FuncAddr = *c.i32++;
if (FuncAddr < 0 || (uint32_t)FuncAddr >= Code->ByteCode.Length())
{
if (Log)
Log->Print( "%s ICallScript error: Script function call invalid addr '%p'.\n",
Code->AddrToSourceRef(CurrentScriptAddress - sizeof(FuncAddr)),
FuncAddr);
SetScriptError;
break;
}
uint16 Frame = *c.u16++;
#if VM_DECOMP
if (Log)
Log->Print("%p CallScript: %s = %p(frame=%i)(",
CurrentScriptAddress - 5,
c.r[0].GetStr(),
FuncAddr,
Frame);
#endif
#ifdef VM_EXECUTE
// Set up stack for function call
int CurFrameSize = Frames.Last().CurrentFrameSize;
StackFrame &Sf = Frames.New();
Sf.CurrentFrameSize = Frame;
Sf.PrevFrameStart = Locals.Length() ? Scope[1] - &Locals[0] : 0;
Sf.ReturnValue = *c.r++;
if (Sf.ReturnValue.Scope == SCOPE_LOCAL)
Sf.ReturnValue.Index -= CurFrameSize;
uint16 Args = *c.u16++;
// Increase the local stack size
size_t LocalsBase = Locals.Length();
size_t LocalsPos = Scope[SCOPE_LOCAL] - Locals.AddressOf();
Locals.SetFixedLength(false);
Locals.Length(LocalsBase + Frame);
Locals.SetFixedLength();
Scope[SCOPE_LOCAL] = Locals.AddressOf(LocalsPos);
// Put the arguments of the function call into the local array
LArray Arg;
#else
LResolveRef(Ret) Resolve();
int Args = *c.u16++;
#endif
for (int i=0; iPrint("%s%s", i?",":"", c.r[0].GetStr());
#endif
#if VM_EXECUTE
Locals[LocalsBase+i] = *Resolve();
#else
c.r++;
#endif
}
#if VM_EXECUTE
// Set IP to start of function
Sf.ReturnIp = CurrentScriptAddress;
c.u8 = Base + FuncAddr;
Scope[SCOPE_LOCAL] = Locals.AddressOf(LocalsBase); // This can evaluation to NULL when there is NO locals.
#endif
#if VM_DECOMP
if (Log)
Log->Print(")\n");
#endif
break;
}
case IRet:
{
#if VM_DECOMP
if (Log)
Log->Print("%p Ret %s\n", CurrentScriptAddress - 1, c.r[0].GetStr());
#endif
LResolveRef(ReturnValue) Resolve();
#ifdef VM_EXECUTE
if (Frames.Length() > 0)
{
StackFrame Sf = Frames[Frames.Length()-1];
LVarRef &Ret = Sf.ReturnValue;
LVariant *RetVar = &Scope[Ret.Scope][Ret.Index];
// LgiTrace("IRet to %i:%i\n", Ret.Scope, Ret.Index);
if (Ret.Scope == SCOPE_LOCAL)
LAssert(Locals.PtrCheck(RetVar));
*RetVar = *ReturnValue;
CheckParam(RetVar->Type == ReturnValue->Type);
Frames.Length(Frames.Length()-1);
Locals.SetFixedLength(false);
if (Locals.Length() >= Sf.CurrentFrameSize)
{
ssize_t Base = Locals.Length() - Sf.CurrentFrameSize;
/*
if (ArgsOutput)
{
if (Frames.Length() == 0)
{
for (unsigned i=0; iLength(); i++)
{
*(*ArgsOutput)[i] = Locals[Base+i];
}
}
}
*/
// LgiTrace("%s:%i Locals %i -> %i\n", _FL, Locals.Length(), Base);
Locals.Length(Base);
Scope[SCOPE_LOCAL] = &Locals[Sf.PrevFrameStart];
}
else
{
// LgiTrace("%s:%i - Locals %i -> %i\n", _FL, Locals.Length(), 0);
Locals.Length(0);
Scope[SCOPE_LOCAL] = NULL;
}
Locals.SetFixedLength();
c.u8 = Base + Sf.ReturnIp;
}
else
{
ExitScriptExecution;
}
#endif
break;
}
case IArrayGet:
{
#if VM_DECOMP
if (Log)
Log->Print( "%p ArrayGet %s = %s[%s]\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr(),
c.r[2].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Var) Resolve();
LResolveRef(Idx) Resolve();
#ifdef VM_EXECUTE
switch (Var->Type)
{
case GV_LIST:
{
CheckParam(Var->Value.Lst);
LVariant *t = Var->Value.Lst->ItemAt(Idx->CastInt32());
if (t)
{
if (Var == Dst)
{
if (Var->Value.Lst->Delete(t))
{
*Var = *t;
DeleteObj(t);
}
else CheckParam(!"List delete failed.");
}
else *Dst = *t;
}
else Dst->Empty();
break;
}
case GV_HASHTABLE:
{
CheckParam(Var->Value.Hash);
LVariant *t = (LVariant*)Var->Value.Hash->Find(Idx->CastString());
if (t) *Dst = *t;
else Dst->Empty();
break;
}
case GV_CUSTOM:
{
LCustomType *T = Var->Value.Custom.Dom;
size_t Sz = T->Sizeof();
int Index = Idx->CastInt32();
Dst->Type = GV_CUSTOM;
Dst->Value.Custom.Dom = T;
Dst->Value.Custom.Data = Var->Value.Custom.Data + (Sz * Index);
break;
}
case GV_STRING:
{
auto c = Var->Str();
auto i = Idx->CastInt64();
if (!c || i < 0)
break;
LUtf8Ptr p(c);
uint32_t ch;
do
{
ch = p;
if (i-- == 0)
{
*Dst = ch;
break;
}
p++;
}
while (ch);
break;
}
default:
{
LString s;
s.Printf("%s IArrayGet warning: Can't array deref variant type %i\n",
Code->AddrToSourceRef(CurrentScriptAddress),
Var->Type);
if (Log)
Log->Write(s, s.Length());
if (LVirtualMachine::BreakOnWarning)
OnException(NULL, -1, CurrentScriptAddress, s);
Status = ScriptWarning;
break;
}
}
#endif
break;
}
case IArraySet:
{
#if VM_DECOMP
if (Log)
Log->Print( "%p ArraySet %s[%s] = %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr(),
c.r[2].GetStr());
#endif
LResolveRef(Var) Resolve();
LResolveRef(Idx) Resolve();
LResolveRef(Val) Resolve();
#ifdef VM_EXECUTE
switch (Var->Type)
{
case GV_LIST:
{
CheckParam(Var->Value.Lst);
(*Var->Value.Lst).Insert(new LVariant(*Val), Idx->CastInt32());
break;
}
case GV_HASHTABLE:
{
CheckParam(Var->Value.Hash);
LVariant *Old = (LVariant*)Var->Value.Hash->Find(Idx->CastString());
DeleteObj(Old);
Var->Value.Hash->Add(Idx->CastString(), new LVariant(*Val));
break;
}
default:
{
LString s;
s.Printf("%s IArraySet warning: Can't dereference type '%s'\n",
Code->AddrToSourceRef(CurrentScriptAddress),
LVariant::TypeToString(Var->Type));
if (Log)
Log->Write(s, s.Length());
if (LVirtualMachine::BreakOnWarning)
OnException(NULL, -1, CurrentScriptAddress, s);
Status = ScriptWarning;
break;
}
}
#endif
break;
}
case IAnd:
{
#if VM_DECOMP
if (Log)
Log->Print("%p %s && %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Src) Resolve();
#ifdef VM_EXECUTE
*Dst = (Dst->CastInt32() != 0) && (Src->CastInt32() != 0);
#endif
break;
}
case IOr:
{
#if VM_DECOMP
if (Log)
Log->Print("%p %s || %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Src) Resolve();
#ifdef VM_EXECUTE
*Dst = (Dst->CastInt32() != 0) || (Src->CastInt32() != 0);
#endif
break;
}
case INot:
{
#if VM_DECOMP
if (Log)
Log->Print("%p %s = !%s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[0].GetStr());
#endif
LResolveRef(Dst) Resolve();
#ifdef VM_EXECUTE
*Dst = !Dst->CastBool();
#endif
break;
}
case IDomGet:
{
#if VM_DECOMP
if (Log)
Log->Print("%p %s = %s->DomGet(%s, %s)\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr(),
c.r[2].GetStr(),
c.r[3].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Dom) Resolve();
LResolveRef(Name) Resolve();
LResolveRef(Arr) Resolve();
#ifdef VM_EXECUTE
// Return "NULL" in Dst on error
if (Dst != Dom)
Dst->Empty();
switch (Dom->Type)
{
case GV_DOM:
case GV_STREAM:
case GV_LSURFACE:
{
auto *dom = Dom->CastDom();
CheckParam(dom != NULL);
char *sName = Name->Str();
CheckParam(sName);
bool Ret = dom->GetVariant(sName, *Dst, CastArrayIndex(Arr));
if (!Ret)
{
Dst->Empty();
LString s;
s.Printf("%s IDomGet warning: Unexpected %s member '%s'.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
LVariant::TypeToString(Dom->Type),
sName);
if (Log)
Log->Write(s, s.Length());
if (LVirtualMachine::BreakOnWarning)
OnException(NULL, -1, CurrentScriptAddress, s);
Status = ScriptWarning;
}
break;
}
case GV_DATETIME:
{
CheckParam(Dom->Value.Date != NULL);
char *sName = Name->Str();
CheckParam(sName);
bool Ret = Dom->Value.Date->GetVariant(sName, *Dst, CastArrayIndex(Arr));
if (!Ret)
{
Dst->Empty();
LString s;
s.Printf("%s IDomGet warning: Unexpected %s member '%s'.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
LVariant::TypeToString(Dom->Type),
sName);
if (Log)
Log->Write(s, s.Length());
if (LVirtualMachine::BreakOnWarning)
OnException(NULL, -1, CurrentScriptAddress, s);
Status = ScriptWarning;
}
break;
}
case GV_CUSTOM:
{
LCustomType *Type = Dom->Value.Custom.Dom;
if (Type)
{
int Fld;
if (Name->Type == GV_INT32)
Fld = Name->Value.Int;
else
Fld = Type->IndexOf(Name->Str());
int Index = Arr ? Arr->CastInt32() : 0;
Type->Get(Fld, *Dst, Dom->Value.Custom.Data, Index);
}
break;
}
case GV_LIST:
{
CheckParam(Dom->Value.Lst);
char *sName = Name->Str();
CheckParam(sName);
LDomProperty p = LStringToDomProp(sName);
if (p == ObjLength)
(*Dst) = (int)Dom->Value.Lst->Length();
break;
}
case GV_HASHTABLE:
{
CheckParam(Dom->Value.Hash);
char *sName = Name->Str();
CheckParam(sName);
LDomProperty p = LStringToDomProp(sName);
if (p == ObjLength)
(*Dst) = (int)Dom->Value.Hash->Length();
break;
}
case GV_BINARY:
{
char *sName = Name->Str();
CheckParam(sName);
LDomProperty p = LStringToDomProp(sName);
if (p == ObjLength)
(*Dst) = Dom->Value.Binary.Length;
break;
}
case GV_INT32:
{
char *sName = Name->Str();
CheckParam(sName);
LDomProperty p = LStringToDomProp(sName);
switch (p)
{
case TypeString:
{
char s[32];
sprintf_s(s, sizeof(s), "%i", Dom->Value.Int);
*Dst = s;
break;
}
case TypeDouble:
{
*Dst = (double)Dom->Value.Int;
break;
}
default:
{
Dst->Empty();
LString s;
s.Printf("%s IDomGet warning: Unexpected int32 member '%s'.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
sName);
if (Log)
Log->Write(s, s.Length());
if (LVirtualMachine::BreakOnWarning)
OnException(NULL, -1, CurrentScriptAddress, s);
Status = ScriptWarning;
break;
}
}
break;
}
case GV_INT64:
{
char *sName = Name->Str();
CheckParam(sName);
LDomProperty p = LStringToDomProp(sName);
switch (p)
{
case TypeString:
{
char s[32];
sprintf_s(s, sizeof(s), LPrintfInt64, Dom->Value.Int64);
*Dst = s;
break;
}
case TypeDouble:
{
*Dst = (double)Dom->Value.Int64;
break;
}
default:
{
Dst->Empty();
LString s;
s.Printf("%s IDomGet warning: Unexpected int64 member '%s'.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
sName);
if (Log)
Log->Write(s, s.Length());
if (LVirtualMachine::BreakOnWarning)
OnException(NULL, -1, CurrentScriptAddress, s);
Status = ScriptWarning;
break;
}
}
break;
}
case GV_DOUBLE:
{
char *sName = Name->Str();
CheckParam(sName);
LDomProperty p = LStringToDomProp(sName);
switch (p)
{
case TypeString:
{
char s[32];
sprintf_s(s, sizeof(s), "%g", Dom->Value.Dbl);
*Dst = s;
break;
}
case TypeInt:
{
*Dst = (int64)Dom->Value.Dbl;
break;
}
default:
{
Dst->Empty();
LString s;
s.Printf("%s IDomGet warning: Unexpected double member '%s'.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
sName);
if (Log)
Log->Write(s, s.Length());
if (LVirtualMachine::BreakOnWarning)
OnException(NULL, -1, CurrentScriptAddress, s);
Status = ScriptWarning;
break;
}
}
break;
}
case GV_STRING:
{
char *sName = Name->Str();
CheckParam(sName);
LDomProperty p = LStringToDomProp(sName);
switch (p)
{
case ObjLength:
{
(*Dst) = (int)strlen(Dom->Str());
break;
}
case TypeInt:
{
(*Dst) = Dom->CastInt32();
break;
}
case TypeDouble:
{
(*Dst) = Dom->CastDouble();
break;
}
default:
{
Dst->Empty();
LString s;
s.Printf("%s IDomGet warning: Unexpected string member '%s'.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
sName);
if (Log)
Log->Write(s, s.Length());
if (LVirtualMachine::BreakOnWarning)
OnException(NULL, -1, CurrentScriptAddress, s);
Status = ScriptWarning;
break;
}
}
break;
}
case GV_NULL:
{
LString s;
s.Printf("%s IDomGet warning: Can't deref NULL object.\n",
Code->AddrToSourceRef(CurrentScriptAddress));
if (Log)
Log->Write(s, s.Length());
if (LVirtualMachine::BreakOnWarning)
OnException(NULL, -1, CurrentScriptAddress, s);
Status = ScriptWarning;
break;
}
default:
{
LString s;
s.Printf("%s IDomGet warning: Unexpected type %s (Src=%s:%i IP=0x%x).\n",
Code->AddrToSourceRef(CurrentScriptAddress),
LVariant::TypeToString(Dom->Type),
_FL,
CurrentScriptAddress);
if (Log)
Log->Write(s, s.Length());
if (LVirtualMachine::BreakOnWarning)
OnException(NULL, -1, CurrentScriptAddress, s);
Status = ScriptWarning;
break;
}
}
#endif
break;
}
case IDomSet:
{
#if VM_DECOMP
if (Log)
Log->Print("%p %s->DomSet(%s, %s) = %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr(),
c.r[2].GetStr(),
c.r[3].GetStr());
#endif
LResolveRef(Dom) Resolve();
LResolveRef(Name) Resolve();
LResolveRef(Arr) Resolve();
LResolveRef(Value) Resolve();
#ifdef VM_EXECUTE
char *sName = Name->Str();
if (!sName)
{
if (Log)
Log->Print("%s IDomSet error: No name string.\n",
Code->AddrToSourceRef(CurrentScriptAddress));
SetScriptError;
break;
}
switch (Dom->Type)
{
case GV_DOM:
// case GV_GFILE:
case GV_STREAM:
case GV_LSURFACE:
{
auto *dom = Dom->CastDom();
CheckParam(dom != NULL);
bool Ret = dom->SetVariant(sName, *Value, CastArrayIndex(Arr));
if (!Ret)
{
if (Log)
Log->Print("%s IDomSet warning: Unexpected %s member '%s'.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
LVariant::TypeToString(Dom->Type),
sName);
Status = ScriptWarning;
}
break;
}
case GV_DATETIME:
{
CheckParam(Dom->Value.Date != NULL);
bool Ret = Dom->Value.Date->SetVariant(sName, *Value, CastArrayIndex(Arr));
if (!Ret)
{
if (Log)
Log->Print("%s IDomSet warning: Unexpected %s member '%s'.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
LVariant::TypeToString(Dom->Type),
sName);
Status = ScriptWarning;
}
break;
}
case GV_CUSTOM:
{
LCustomType *Type = Dom->Value.Custom.Dom;
if (Type)
{
int Fld;
if (IsDigit(*sName))
Fld = atoi(sName);
else
Fld = Type->IndexOf(sName);
int Index = Arr ? Arr->CastInt32() : 0;
if (!Type->Set(Fld, *Value, Dom->Value.Custom.Data, Index) &&
Log)
{
Log->Print("%s IDomSet warning: Couldn't set '%s' on custom type.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
sName);
}
}
break;
}
case GV_STRING:
{
LDomProperty p = LStringToDomProp(sName);
switch (p)
{
case ObjLength:
{
char *s;
int DLen = Value->CastInt32();
if (DLen && (s = new char[DLen+1]))
{
size_t SLen = Dom->Str() ? strlen(Dom->Str()) : 0;
if (SLen)
memcpy(s, Dom->Str(), SLen);
memset(s+SLen, ' ', DLen-SLen);
s[DLen] = 0;
DeleteArray(Dom->Value.String);
Dom->Value.String = s;
}
else Dom->Empty();
break;
}
case TypeInt:
{
*Dom = Value->CastInt32();
Dom->Str();
break;
}
case TypeDouble:
{
*Dom = Value->CastDouble();
Dom->Str();
break;
}
default:
{
if (Log)
Log->Print("%s IDomSet warning: Unexpected string member %s.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
sName);
Status = ScriptWarning;
break;
}
}
break;
}
default:
{
if (Log)
Log->Print("%s IDomSet warning: Unexpected type %s.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
LVariant::TypeToString(Dom->Type));
Status = ScriptWarning;
break;
}
}
#endif
break;
}
case IDomCall:
{
#if VM_DECOMP
if (Log)
Log->Print("%p %s = %s->DomCall(%s, ",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr(),
c.r[2].GetStr());
#else
LVarRef DstRef = *c.r;
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Dom) Resolve();
LResolveRef(Name) Resolve();
#ifdef VM_EXECUTE
LResolveRef(Args) Resolve();
int ArgCount = Args->CastInt32();
char *sName = Name->Str();
CheckParam(sName)
if (Dom->Type == GV_CUSTOM)
{
#define DEBUG_CUSTOM_METHOD_CALL 1
auto t = Dom->Value.Custom.Dom;
CheckParam(t);
auto m = t->GetMethod(sName);
CheckParam(m);
CheckParam(m->Params.Length() == ArgCount);
// Set up new stack frame...
StackFrame &Sf = Frames.New();
Sf.CurrentFrameSize = m->FrameSize;
Sf.PrevFrameStart = Locals.Length() ? Scope[1] - &Locals[0] : 0;
Sf.ReturnValue = DstRef;
// Increase the local stack size
AddLocalSize(m->FrameSize + 1);
#if DEBUG_CUSTOM_METHOD_CALL
LgiTrace("CustomType.Call(%s) Args=%i, Frame=%i, Addr=%i, LocalsBase=%i ",
sName, ArgCount, m->FrameSize, m->Address, LocalsBase);
#endif
size_t i = LocalsBase;
Locals[i++] = *Dom; // this pointer...
#if DEBUG_CUSTOM_METHOD_CALL
LString s = Locals[i-1].ToString();
LgiTrace("This=%s, ", s.Get());
#endif
size_t end = i + ArgCount;
while (i < end)
{
Locals[i++] = *Resolve();
#if DEBUG_CUSTOM_METHOD_CALL
s = Locals[i-1].ToString();
LgiTrace("[%i]=%s, ", i-1, s.Get());
#endif
}
// Now adjust the local stack to point to the locals for the function
Scope[1] = Locals.Length() ? &Locals[LocalsBase] : NULL;
// Set IP to start of function
Sf.ReturnIp = CurrentScriptAddress;
c.u8 = Base + m->Address;
#if DEBUG_CUSTOM_METHOD_CALL
LgiTrace("\n");
#endif
break;
}
LScriptArguments Arg(Vm, Dst, NULL, CurrentScriptAddress);
Arg.Length(ArgCount);
for (int i=0; iType);
}
else switch (Dom->Type)
{
case GV_DOM:
case GV_STREAM:
case GV_LSURFACE:
{
auto *dom = Dom->CastDom();
CheckParam(dom);
auto Ret = dom->CallMethod(sName, Arg);
if (!Ret)
{
Dst->Empty();
if (Log)
Log->Print("%s IDomCall warning: %s(...) failed.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
sName);
Status = ScriptWarning;
}
break;
}
case GV_DATETIME:
{
CheckParam(Dom->Value.Date);
auto Ret = Dom->Value.Date->CallMethod(sName, Dst, Arg);
if (!Ret)
{
Dst->Empty();
if (Log)
Log->Print("%s IDomCall warning: %s(...) failed.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
sName);
Status = ScriptWarning;
}
break;
}
case GV_LIST:
{
CheckParam(Dom->Value.Lst);
switch (p)
{
case ObjLength:
{
*Dst = (int64)Dom->Value.Lst->Length();
break;
}
case ContainerAdd:
{
if (Arg.Length() > 0 &&
Arg[0])
{
auto Index = Arg.Length() > 1 ? Arg[1]->CastInt32() : -1;
auto v = new LVariant;
*v = *Arg[0];
Dom->Value.Lst->Insert(v, Index);
}
break;
}
case ContainerDelete:
{
for (unsigned i=0; iCastInt32();
auto Elem = Dom->Value.Lst->ItemAt(n);
if (Elem)
{
Dom->Value.Lst->Delete(Elem);
DeleteObj(Elem);
}
}
}
break;
}
case ContainerHasKey:
{
if (Arg.Length() > 0 && Arg[0])
{
auto Index = Arg[0]->CastInt32();
*Dst = (bool) (Index >= 0 && Index < (int)Dom->Value.Lst->Length());
}
else
{
*Dst = false;
}
break;
}
case ContainerHasItem:
{
bool found = false;
if (Arg.Length() > 0 && Arg[0])
{
auto item = Arg[0];
for (auto v: *Dom->Value.Lst)
{
if (*v == *item)
{
found = true;
break;
}
}
}
*Dst = found;
break;
}
case ContainerSort:
{
auto Param = Arg.Length() > 0 ? Arg[0] : NULL;
Dom->Value.Lst->Sort(LVariantCmp, (NativeInt)Param);
break;
}
default:
{
Dst->Empty();
if (Log)
Log->Print( "%s IDomCall warning: Unexpected list member '%s'.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
sName);
Status = ScriptWarning;
break;
}
}
break;
}
case GV_HASHTABLE:
{
CheckParam(Dom->Value.Hash);
switch (p)
{
case ObjLength:
{
*Dst = Dom->Value.Hash->Length();
break;
}
case ContainerAdd:
{
if (Arg.Length() == 2 &&
Arg[0] &&
Arg[1])
{
char *Key = Arg[1]->Str();
if (Key)
{
LVariant *v = new LVariant;
*v = *Arg[0];
Dom->Value.Hash->Add(Key, v);
}
}
break;
}
case ContainerDelete:
{
if (Arg.Length() == 1 &&
Arg[0])
{
char *Key = Arg[0]->Str();
if (Key)
{
LVariant *v = (LVariant*) Dom->Value.Hash->Find(Key);
if (v)
{
Dom->Value.Hash->Delete(Key);
delete v;
}
}
}
break;
}
case ContainerHasKey:
{
if (Arg.Length() > 0 && Arg[0])
{
char *Key = Arg[0]->Str();
*Dst = (bool) (Dom->Value.Hash->Find(Key) != NULL);
}
else
{
*Dst = false;
}
break;
}
case ContainerHasItem:
{
bool found = false;
if (Arg.Length() > 0 && Arg[0])
{
auto item = Arg[0];
for (auto p: *Dom->Value.Hash)
{
if (*p.value == *item)
{
found = true;
break;
}
}
}
*Dst = found;
break;
}
default:
{
Dst->Empty();
if (Log)
Log->Print("%s IDomCall warning: Unexpected hashtable member '%s'.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
sName);
Status = ScriptWarning;
break;
}
}
break;
}
case GV_BINARY:
{
switch (p)
{
default:
break;
case ObjLength:
*Dst = Dom->Value.Binary.Length;
break;
}
break;
}
case GV_STRING:
{
if (Arg.Length() > 0 && !Arg[0])
{
Dst->Empty();
break;
}
switch (p)
{
case ObjLength:
{
char *s = Dom->Str();
*Dst = (int) (s ? strlen(s) : 0);
break;
}
case StrJoin:
{
switch (Arg[0]->Type)
{
case GV_LIST:
{
LStringPipe p(256);
List *Lst = Arg[0]->Value.Lst;
const char *Sep = Dom->CastString();
auto It = Lst->begin();
LVariant *v = *It;
if (v)
{
LVariant Tmp = *v;
p.Print("%s", Tmp.CastString());
while ((v = *(++It)))
{
Tmp = *v;
p.Print("%s%s", Sep, Tmp.CastString());
}
}
Dst->OwnStr(p.NewStr());
break;
}
default:
{
*Dst = *Arg[0];
Dst->CastString();
break;
}
}
break;
}
case StrSplit:
{
const char *Sep = Arg[0]->Str();
if (!Sep)
{
Dst->Empty();
break;
}
LVariant Tmp;
if (Dst == Dom)
{
Tmp = *Dom;
Dom = &Tmp;
}
Dst->SetList();
size_t SepLen = strlen(Sep);
int MaxSplit = Arg.Length() > 1 ? Arg[1]->CastInt32() : -1;
const char *c = Dom->CastString();
while (c && *c)
{
if (MaxSplit > 0 && (int)Dst->Value.Lst->Length() >= MaxSplit)
break;
const char *next = strstr(c, Sep);
if (!next)
break;
LVariant *v = new LVariant;
v->OwnStr(NewStr(c, next - c));
Dst->Value.Lst->Insert(v);
c = next + SepLen;
}
if (c && *c)
{
LVariant *v = new LVariant;
v->OwnStr(NewStr(c));
Dst->Value.Lst->Insert(v);
}
break;
}
case StrSplitDelimit:
{
const char *Sep = Arg[0]->Str();
if (!Sep)
{
Dst->Empty();
break;
}
LVariant Tmp;
if (Dst == Dom)
{
Tmp = *Dom;
Dom = &Tmp;
}
Dst->SetList();
int MaxSplit = Arg.Length() > 1 ? Arg[1]->CastInt32() : -1;
const char *c = Dom->CastString();
while (c && *c)
{
if (MaxSplit > 0 && (int)Dst->Value.Lst->Length() >= MaxSplit)
break;
const char *next = c;
while (*next && !strchr(Sep, *next))
next++;
LVariant *v = new LVariant;
v->OwnStr(NewStr(c, next - c));
Dst->Add(v);
for (c = next; *c && strchr(Sep, *c); c++)
;
}
if (c && *c)
Dst->Add(new LVariant(c));
break;
}
case StrFind:
{
const char *s = Dom->Str();
if (!s)
{
*Dst = -1;
break;
}
ssize_t sLen = Strlen(s);
auto sub = Arg[0]->Str();
auto start = Arg.Length() > 1 ? Arg[1]->CastInt32() : 0;
auto end = Arg.Length() > 2 ? Arg[2]->CastInt32() : -1;
if (start >= sLen)
{
*Dst = -1;
break;
}
char *sStart = (char*)s + start;
char *pos;
if (end >= 0)
pos = Strnstr(sStart, sub, end);
else
pos = Strstr(sStart, sub);
if (pos)
*Dst = (int64) (pos - s);
else
*Dst = -1;
break;
}
case StrRfind:
{
const char *s = Dom->Str();
if (!s)
{
*Dst = -1;
break;
}
ssize_t sLen = strlen(s);
auto sub = Arg[0]->Str();
auto start_idx = Arg.Length() > 1 ? Arg[1]->CastInt32() : 0;
auto end_idx = Arg.Length() > 2 ? Arg[2]->CastInt32() : -1;
if (start_idx >= sLen)
{
*Dst = -1;
break;
}
auto sublen = Strlen(sub);
auto cur = s + start_idx;
auto end = end_idx >= 0 ? cur + end_idx : NULL;
const char *pos = NULL;
while (true)
{
cur = (end)
?
Strnstr(cur, sub, end - cur)
:
Strstr(cur, sub);
if (cur)
{
pos = cur;
cur += sublen;
}
else break;
}
if (pos)
*Dst = (int64) (pos - s);
else
*Dst = -1;
break;
}
case StrLower:
{
if (Dst != Dom)
*Dst = Dom->CastString();
StrLwr(Dst->Str());
break;
}
case StrUpper:
{
if (Dst != Dom)
*Dst = Dom->CastString();
StrUpr(Dst->Str());
break;
}
case StrStrip:
{
auto s = Dom->Str();
if (s)
{
const char *Delimit = Arg.Length() > 0 ? Arg[0]->Str() : NULL;
if (!Delimit)
Delimit = LWhiteSpace;
auto start = s;
auto end = s + Strlen(s);
while (start < end && strchr(Delimit, *start))
start++;
while (end > start && strchr(Delimit, end[-1]))
end--;
Dst->OwnStr(NewStr(start, end - start));
}
else Dst->Empty();
break;
}
case StrSub:
{
auto s = Dom->Str();
if (s)
{
ssize_t Start = Arg.Length() > 0 ? Arg[0]->CastInt32() : 0;
ssize_t End = Arg.Length() > 1 ? Arg[1]->CastInt32() : -1;
ssize_t Len = strlen(s);
if (End < 0 || End > Len)
End = Len;
if (Start < 0)
Start = 0;
if (Start <= End)
Dst->OwnStr(NewStr(s + Start, End - Start));
else
Dst->Empty();
}
else Dst->Empty();
break;
}
default:
{
Dst->Empty();
if (Log)
Log->Print("%p IDomCall warning: Unexpected string member %s (%s:%i).\n",
CurrentScriptAddress,
sName,
_FL);
Status = ScriptWarning;
break;
}
}
break;
}
default:
{
const char *Type = LVariant::TypeToString(Dom->Type);
char t[32];
if (!Type)
{
sprintf_s(t, sizeof(t), "UnknownType(%i)", Dom->Type);
Type = t;
}
Dst->Empty();
if (Log)
{
Log->Print("%s IDomCall warning: Unexpected type %s (Src=%s:%i IP=0x%x).\n",
Code->AddrToSourceRef(CurrentScriptAddress),
Type,
_FL,
CurrentScriptAddress);
}
Status = ScriptWarning;
break;
}
}
ON_EXCEPTION(Arg);
#else
LVariant *Count = NULL;
switch (c.r->Scope)
{
case SCOPE_GLOBAL:
Count = &Code->Globals[c.r->Index];
c.r++;
break;
default:
OnException(_FL, CurrentScriptAddress, "Unsupported scope.");
return ScriptError;
}
int Args = Count->CastInt32();
for (int i=0; iPrint("%s%s", i ? ", " : "", c.r->GetStr());
#endif
c.r++;
}
#if VM_DECOMP
if (Log)
Log->Print(")\n");
#endif
#endif
break;
}
case IBreakPoint:
{
#if VM_DECOMP
if (Log)
Log->Print("%p Debugger\n", CurrentScriptAddress-1);
#elif VM_EXECUTE
OnException(_FL, CurrentScriptAddress-1, "ShowDebugger");
return ScriptWarning;
#endif
break;
}
case IDebug:
{
#if VM_DECOMP
if (Log)
Log->Print("%p Debugger\n", CurrentScriptAddress-1);
#elif VM_EXECUTE
#ifdef WINDOWS
__debugbreak();
#elif defined MAC
__builtin_trap();
#elif defined LINUX
Gtk::raise(SIGINT);
#else
#warning "Not impl."
#endif
#endif
break;
}
#undef Resolve
#undef LResolveRef
\ No newline at end of file
diff --git a/src/common/Coding/LexCpp.cpp b/src/common/Coding/LexCpp.cpp
--- a/src/common/Coding/LexCpp.cpp
+++ b/src/common/Coding/LexCpp.cpp
@@ -1,246 +1,250 @@
#include "lgi/common/LexCpp.h"
#include
#include
#define iswhite(s) (s && strchr(White, s) != 0)
#define isword(s) (s && (IsDigit(s) || IsAlpha(s) || (s) == '_') )
#define skipws(s) while (*s) \
{ \
if (*s == '\n') \
{ \
if (LineCount) *LineCount = *LineCount + 1; \
s++; \
} \
else if (*s == ' ' || *s == '\t' || *s == '\r') \
s++; \
else \
break; \
}
char16 *LexStrdup(void *context, const char16 *start, ssize_t len)
{
if (len < 0)
len = StrlenW(start);
char16 *n = new char16[len + 1];
if (n)
{
memcpy(n, start, sizeof(*n) * len);
n[len] = 0;
}
return n;
}
char16 *LexNoReturn(void *context, const char16 *start, ssize_t len)
{
return NULL;
}
// Returns the next C++ token in 's'
//
// If 'ReturnString' is false then the next token is skipped and
// not returned. i.e. the return is always NULL but 's' is moved.
char16 *LexCpp
(
char16 *&s,
LexCppStrdup Strdup,
void *Context,
int *LineCount
)
{
LexAgain:
skipws(s);
if (*s == '#')
{
// Pre-processor command
char16 *Start = s++;
// Skip any whitespace
while (*s == ' ' || *s == '\t')
{
Start = s;
*s++ = '#';
}
// Non whitespace
while (*s && IsAlpha(*s))
{
s++;
}
return Strdup(Context, Start, s-Start);
}
else if (*s == '_' || IsAlpha(*s))
{
// Identifier
char16 *Start = s++;
while
(
*s
&&
(
*s == '_'
||
*s == ':'
||
IsAlpha(*s)
||
IsDigit(*s)
)
)
{
s++;
}
return Strdup(Context, Start, s-Start);
}
else if (s[0] == '/' && s[1] == '/')
{
// C++ Comment
s += 2;
while (*s)
{
if (*s == '\n')
{
if (LineCount) *LineCount = *LineCount+1;
s++;
goto LexAgain;
}
s++;
}
return NULL;
}
else if (s[0] == '/' && s[1] == '*')
{
// C comment
s += 2;
while (*s)
{
if (*s == '\n')
{
if (LineCount) *LineCount = *LineCount+1;
}
else if (s[0] == '*' && s[1] == '/')
{
s += 2;
goto LexAgain;
}
s++;
}
return NULL;
}
else if
(
(s[0] == '-' && s[1] == '>')
||
(s[0] == '|' && s[1] == '|')
||
(s[0] == '&' && s[1] == '&')
||
(s[0] == '+' && s[1] == '+')
||
(s[0] == '-' && s[1] == '-')
||
(s[0] == '/' && s[1] == '=')
||
(s[0] == '-' && s[1] == '=')
||
(s[0] == '*' && s[1] == '=')
||
(s[0] == '+' && s[1] == '=')
||
(s[0] == '^' && s[1] == '=')
||
(s[0] == '>' && s[1] == '=')
||
(s[0] == '<' && s[1] == '=')
||
(s[0] == '-' && s[1] == '>')
||
(s[0] == '=' && s[1] == '=')
||
(s[0] == '!' && s[1] == '=')
+ ||
+ (s[0] == '<' && s[1] == '<')
+ ||
+ (s[0] == '>' && s[1] == '>')
)
{
// 2 char delimiter
char16 *Status = Strdup(Context, s, 2);
s += 2;
return Status;
}
else if (IsDigit(*s) || (*s == '-' && IsDigit(s[1]) ) )
{
// Constant
char16 *Start = s;
bool IsHex = false;
// Skip hex prefix...
if (s[0] == '0' && tolower(s[1]) == 'x')
{
s += 2;
IsHex = true;
}
if (*s == '-')
s++;
// Seek to end of number
while
(
*s
&&
(
*s == '.'
||
*s == 'e'
||
IsDigit(*s)
||
(
IsHex
&&
strchr("abcdef", tolower(*s))
)
)
)
{
s++;
}
return Strdup(Context, Start, s-Start);
}
else if (*s && strchr("-()*[]&,{};:=!<>?.\\+/%^|~@", *s))
{
// Delimiter
return Strdup(Context, s++, 1);
}
else if (*s && strchr("\"\'", *s))
{
// String
char16 Delim = *s;
char16 *Start = s++;
while (*s)
{
if (*s == '\\')
{
s += 2;
}
else if (*s == Delim)
{
s++;
break;
}
else
{
s++;
}
}
return Strdup(Context, Start, s-Start);
}
return NULL;
};
\ No newline at end of file
diff --git a/src/common/Coding/ScriptCompiler.cpp b/src/common/Coding/ScriptCompiler.cpp
--- a/src/common/Coding/ScriptCompiler.cpp
+++ b/src/common/Coding/ScriptCompiler.cpp
@@ -1,3873 +1,3856 @@
/// \file
#include "lgi/common/Lgi.h"
#include "lgi/common/Scripting.h"
#include "lgi/common/LexCpp.h"
#include "lgi/common/LgiString.h"
#include "ScriptingPriv.h"
// #define DEBUG_SCRIPT_FILE "Mail Filters Menu.script"
#define GetTok(c) ((c) < Tokens.Length() ? Tokens[c] : NULL)
#define GetTokType(c) ((c) < Tokens.Length() ? ExpTok.Find(Tokens[c]) : TNone)
#define GV_VARIANT GV_MAX
const char *sDebugger = "Debugger";
int LFunctionInfo::_Infos = 0;
enum LTokenType
{
#define _(type, str) T##type,
#include "TokenType.h"
#undef _
};
struct LinkFixup
{
int Tok;
size_t Offset;
int Args;
LFunctionInfo *Func;
};
struct Node
{
typedef LArray NodeExp;
struct VariablePart
{
LVariant Name;
NodeExp Array;
bool Call;
LArray Args;
VariablePart()
{
Call = false;
}
~VariablePart()
{
Args.DeleteObjects();
}
};
// Hierarchy
NodeExp Child;
// One of the following are valid:
LOperator Op = OpNull;
// -or-
bool Constant = false;
int Tok = -1;
LArray Lst;
LTokenType ConstTok = TNone;
// -or-
LFunc *ContextFunc = NULL;
LArray Args;
// -or-
LFunctionInfo *ScriptFunc = NULL;
// -or-
LArray Variable;
// Used during building
LVarRef Reg;
LVarRef ArrayIdx;
void Init()
{
Reg.Empty();
ArrayIdx.Empty();
}
void SetOp(LOperator o, int t)
{
Init();
Op = o;
Tok = t;
}
void SetConst(int t, LTokenType e)
{
Init();
Constant = true;
Tok = t;
ConstTok = e;
}
void SetConst(LArray &list_tokens, LTokenType e)
{
Init();
Constant = true;
Lst = list_tokens;
ConstTok = e;
}
void SetContextFunction(LFunc *m, int tok)
{
Init();
ContextFunc = m;
Tok = tok;
}
void SetScriptFunction(LFunctionInfo *m, int tok)
{
Init();
ScriptFunc = m;
Tok = tok;
}
void SetVar(char16 *Var, int t)
{
Init();
Variable[0].Name = Var;
Tok = t;
}
bool IsVar() { return Variable.Length() > 0; }
bool IsContextFunc() { return ContextFunc != 0; }
bool IsScriptFunc() { return ScriptFunc != 0; }
bool IsConst() { return Constant; }
};
LCompiledCode::LCompiledCode() : Globals(SCOPE_GLOBAL), Debug(0, -1)
{
SysContext = NULL;
UserContext = NULL;
}
LCompiledCode::LCompiledCode(LCompiledCode ©) : Globals(SCOPE_GLOBAL), Debug(0, -1)
{
*this = copy;
}
LCompiledCode::~LCompiledCode()
{
for (auto e: Externs)
e->InUse = false;
Externs.DeleteObjects();
}
LCompiledCode &LCompiledCode::operator =(const LCompiledCode &c)
{
Globals = c.Globals;
ByteCode = c.ByteCode;
Types = c.Types;
Debug = c.Debug;
Methods = c.Methods;
FileName = c.FileName;
Source = c.Source;
return *this;
}
LFunctionInfo *LCompiledCode::GetMethod(const char *Name, bool Create)
{
for (auto &m: Methods)
{
const char *Fn = m->GetName();
if (!strcmp(Fn, Name))
{
return m;
}
}
if (Create)
{
LAutoRefPtr n(new LFunctionInfo(Name));
if (n)
{
Methods.Add(n);
return n;
}
}
return 0;
}
int LCompiledCode::ObjectToSourceAddress(size_t ObjAddr)
{
auto Idx = Debug.Find(ObjAddr);
return Idx < 0 ? 1 : Idx;
}
const char *LCompiledCode::AddrToSourceRef(size_t ObjAddr)
{
static char Status[256];
size_t Addr = ObjAddr;
int LineNum = ObjectToSourceAddress(Addr);
// Search for the start of the instruction...
while (Addr > 0 && LineNum < 0)
LineNum = ObjectToSourceAddress(--Addr);
char *Dir = FileName ? strrchr(FileName, DIR_CHAR) : NULL;
size_t PathLen = Dir ? Dir - FileName : 0;
LString FileRef = FileName ? (PathLen > 24 ? Dir + 1 : FileName.Get()) : "(untitled)";
if (LineNum >= 0)
sprintf_s( Status, sizeof(Status),
"%s:%i",
FileRef.Get(),
LineNum);
else
sprintf_s(Status, sizeof(Status), "%s:0x%x", FileRef.Get(), (int)ObjAddr);
return Status;
}
LVariant *LCompiledCode::Set(const char *Name, LVariant &v)
{
int i = Globals.Var(Name, true);
if (i >= 0)
{
Globals[i] = v;
return &Globals[i];
}
return 0;
}
template
void UnEscape(T *t)
{
T *i = t, *o = t;
while (*i)
{
if (*i == '\\')
{
i++;
switch (*i)
{
case '\\':
*o++ = '\\'; i++;
break;
case 'n':
*o++ = '\n'; i++;
break;
case 'r':
*o++ = '\r'; i++;
break;
case 't':
*o++ = '\t'; i++;
break;
case 'b':
*o++ = '\b'; i++;
break;
}
}
else *o++ = *i++;
}
*o++ = 0;
}
class TokenRanges
{
LArray FileNames;
struct Range
{
int Start, End;
ssize_t File;
int Line;
};
LArray Ranges;
char fl[MAX_PATH_LEN + 32];
public:
TokenRanges()
{
Empty();
}
void Empty()
{
Ranges.Length(0);
}
ssize_t Length()
{
return Ranges.Length();
}
/// Gets the file/line at a given token
const char *operator [](ssize_t Tok)
{
Range *r = NULL;
if (Ranges.Length() == 0)
return "#errNoRanges";
for (unsigned i=0; i= rng->Start && Tok <= rng->End)
{
r = rng;
break;
}
}
if (!r)
r = &Ranges.Last();
if (r->File >= (ssize_t)FileNames.Length())
{
LAssert(!"Invalid file index.");
return "#err: invalid file index";
}
else
{
sprintf_s(fl, sizeof(fl), "%s:%i", FileNames[r->File].Get(), r->Line);
}
return fl;
}
const char *GetLine(int Line)
{
if (Ranges.Length() > 0)
{
Range &r = Ranges.Last();
if (r.File < (ssize_t)FileNames.Length())
sprintf_s(fl, sizeof(fl), "%s:%i", FileNames[r.File].Get(), Line);
else
return "#err: invalid file index.";
}
else
{
sprintf_s(fl, sizeof(fl), "$unknown:%i", Line);
}
return fl;
}
ssize_t GetFileIndex(const char *FileName)
{
for (unsigned i=0; iFile != FileId || r->Line != Line)
{
// Start a new range...
r = &Ranges.New();
r->Start = r->End = TokIndex;
r->File = FileId;
r->Line = Line;
}
else
{
r->End = TokIndex;
}
}
};
/// Scripting language compiler implementation
class LCompilerPriv :
public LCompileTools,
public LScriptUtils
{
LHashTbl, LVariantType> Types;
size_t JumpLoc;
LArray> FuncMem;
public:
- LScriptContext *SysCtx;
- LScriptContext *UserCtx;
- LCompiledCode *Code;
- LStream *Log;
+ LScriptContext *SysCtx = NULL;
+ LScriptContext *UserCtx = NULL;
+ LCompiledCode *Code = NULL;
+ LStream *Log = NULL;
LArray Tokens;
TokenRanges Lines;
- char16 *Script;
+ char16 *Script = NULL;
LHashTbl, LFunc*> Methods;
- uint64_t Regs;
+ uint64_t Regs = 0;
LArray Scopes;
LArray Fixups;
LHashTbl, char16*> Defines;
LHashTbl, LTokenType> ExpTok;
- LDom *ScriptArgs;
+ LDom *ScriptArgs = NULL;
LVarRef ScriptArgsRef;
- bool ErrShowFirstOnly;
+ bool ErrShowFirstOnly = true;
LArray ErrLog;
- bool Debug;
+ bool Debug = false;
#ifdef _DEBUG
LString::Array RegAllocators;
#endif
LCompilerPriv()
{
- Debug = false;
- ErrShowFirstOnly = true;
- SysCtx = NULL;
- UserCtx = NULL;
- Code = 0;
- Log = 0;
- Script = 0;
- Regs = 0;
- ScriptArgs = NULL;
ScriptArgsRef.Empty();
#define LNULL NULL
#define _(type, str) if (str) ExpTok.Add(L##str, T##type);
#include "TokenType.h"
#undef _
#undef LNULL
Types.Add("int", GV_INT32);
Types.Add("short", GV_INT32);
Types.Add("long", GV_INT32);
Types.Add("int8", GV_INT32);
Types.Add("int16", GV_INT32);
Types.Add("int32", GV_INT32);
Types.Add("int64", GV_INT64);
Types.Add("uint8", GV_INT32);
Types.Add("uint16", GV_INT32);
Types.Add("uint32", GV_INT32);
Types.Add("uint64", GV_INT64);
Types.Add("bool", GV_BOOL);
Types.Add("boolean", GV_BOOL);
Types.Add("double", GV_DOUBLE);
Types.Add("float", GV_DOUBLE);
Types.Add("char", GV_STRING);
Types.Add("LDom", GV_DOM);
Types.Add("void", GV_VOID_PTR);
Types.Add("LDateTime", GV_DATETIME);
Types.Add("GHashTable", GV_HASHTABLE);
Types.Add("LOperator", GV_OPERATOR);
Types.Add("LView", GV_LVIEW);
Types.Add("LMouse", GV_LMOUSE);
Types.Add("LKey", GV_LKEY);
Types.Add("LVariant", GV_VARIANT);
// Types.Add("binary", GV_BINARY);
// Types.Add("List", GV_LIST);
// Types.Add("LDom&", GV_DOMREF);
}
~LCompilerPriv()
{
Empty();
}
void Empty()
{
SysCtx = NULL;
UserCtx = NULL;
DeleteObj(Code);
- Log = 0;
+ Log = NULL;
Tokens.DeleteArrays();
Lines.Empty();
DeleteArray(Script);
Defines.DeleteArrays();
Methods.Empty();
}
/// Prints the error message
bool OnError
(
/// The line number (less than 0) or Token index (greater than 0)
int LineOrTok,
/// The format for the string (printf)
const char *Msg,
/// Variable argument list
...
)
{
if (!ErrShowFirstOnly || ErrLog.Length() == 0)
{
char Buf[512];
va_list Arg;
va_start(Arg, Msg);
#ifndef WIN32
#define _vsnprintf vsnprintf
#endif
vsprintf_s(Buf, sizeof(Buf), Msg, Arg);
Log->Print("%s - Error: %s\n", LineOrTok < 0 ? Lines.GetLine(-LineOrTok) : Lines[LineOrTok], Buf);
va_end(Arg);
ErrLog.New() = Buf;
}
return false; // Always return false to previous caller
}
void DebugInfo(int Tok)
{
const char *Line = Lines[Tok];
if (Line)
{
const char *c = strrchr(Line, ':');
if (c)
{
Code->Debug.Add(Code->ByteCode.Length(), ::atoi(c+1));
}
}
}
/// Assemble a zero argument instruction
bool Asm0(int Tok, uint8_t Op)
{
DebugInfo(Tok);
ssize_t Len = Code->ByteCode.Length();
if (Code->ByteCode.Length(Len + 1))
{
LScriptPtr p;
p.u8 = &Code->ByteCode[Len];
*p.u8++ = Op;
}
else return false;
return true;
}
/// Assemble one arg instruction
bool Asm1(int Tok, uint8_t Op, LVarRef a)
{
DebugInfo(Tok);
ssize_t Len = Code->ByteCode.Length();
if (Code->ByteCode.Length(Len + 5))
{
LScriptPtr p;
p.u8 = &Code->ByteCode[Len];
*p.u8++ = Op;
*p.r++ = a;
}
else return false;
return true;
}
/// Assemble two arg instruction
bool Asm2(int Tok, uint8_t Op, LVarRef a, LVarRef b)
{
DebugInfo(Tok);
ssize_t Len = Code->ByteCode.Length();
if (Code->ByteCode.Length(Len + 9))
{
LScriptPtr p;
p.u8 = &Code->ByteCode[Len];
*p.u8++ = Op;
*p.r++ = a;
*p.r++ = b;
}
else return false;
return true;
}
/// Assemble three arg instruction
bool Asm3(int Tok, uint8_t Op, LVarRef a, LVarRef b, LVarRef c)
{
DebugInfo(Tok);
ssize_t Len = Code->ByteCode.Length();
if (Code->ByteCode.Length(Len + 1 + (sizeof(LVarRef) * 3) ))
{
LScriptPtr p;
p.u8 = &Code->ByteCode[Len];
*p.u8++ = Op;
*p.r++ = a;
*p.r++ = b;
*p.r++ = c;
}
else return false;
return true;
}
/// Assemble four arg instruction
bool Asm4(int Tok, uint8_t Op, LVarRef a, LVarRef b, LVarRef c, LVarRef d)
{
DebugInfo(Tok);
ssize_t Len = Code->ByteCode.Length();
if (Code->ByteCode.Length(Len + 1 + (sizeof(LVarRef) * 4) ))
{
LScriptPtr p;
p.u8 = &Code->ByteCode[Len];
*p.u8++ = Op;
if (!a.Valid()) AllocNull(a);
*p.r++ = a;
if (!b.Valid()) AllocNull(b);
*p.r++ = b;
if (!c.Valid()) AllocNull(c);
*p.r++ = c;
if (!d.Valid()) AllocNull(d);
*p.r++ = d;
}
else return false;
return true;
}
/// Assemble 'n' length arg instruction
bool AsmN(int Tok, uint8_t Op, LArray &Args)
{
DebugInfo(Tok);
ssize_t Len = Code->ByteCode.Length();
if (Code->ByteCode.Length(Len + 1 + (sizeof(LVarRef) * Args.Length()) ))
{
LScriptPtr p;
p.u8 = &Code->ByteCode[Len];
*p.u8++ = Op;
for (unsigned i=0; iDefines.Find(wName);
if (v)
{
// Is it a number? Cause we should really convert to an int if possible...
#define INVALID_CONVERSION -11111
int64 i = INVALID_CONVERSION;
if (!Strnicmp(v, L"0x", 2))
i = Atoi(v, 16, INVALID_CONVERSION);
else if (IsDigit(*v) || strchr("-.", *v))
i = Atoi(v);
if (i != INVALID_CONVERSION)
Value = i;
else
Value = v;
return true;
}
return false;
}
};
/// This will look at resolving the expression to something simpler before storing it...
void SimplifyDefine(LScriptEngine &Eng, char16 *Name, LAutoWString &Value)
{
LString Exp = Value.Get();
if (Exp.Find("(") >= 0) // Filter out simple expressions...
{
LVariant Result;
CompilerDefineSource Src(this);
if (Eng.EvaluateExpression(&Result, &Src, Exp))
{
auto Val = Result.CastString();
if (Val)
{
// LgiTrace("Simplify: %S -> %s\n", Value.Get(), Val);
Value.Reset(Utf8ToWide(Val));
}
}
}
}
/// Convert the source from one big string into an array of tokens
bool Lex(char *Source, const char *FileName)
{
char16 *w = Utf8ToWide(Source);
if (!w)
return OnError(0, "Couldn't convert source to wide chars.");
LScriptEngine Eng(NULL, NULL, NULL);
ssize_t FileIndex = Lines.GetFileIndex(FileName);
int Line = 1;
char16 *s = w, *t;
while ((t = LexCpp(s, LexStrdup, NULL, &Line)))
{
if (*t == '#')
{
ssize_t Len;
if (!StrnicmpW(t + 1, sInclude, Len = StrlenW(sInclude)))
{
LAutoWString Raw(LexCpp(s, LexStrdup));
LAutoWString File(TrimStrW(Raw, (char16*)L"\"\'"));
if (File)
{
LVariant v;
LString IncCode;
v.OwnStr(File.Release());
if (UserCtx)
{
IncCode = UserCtx->GetIncludeFile(v.Str());
if (IncCode)
{
Lex(IncCode, v.Str());
}
else
{
DeleteArray(t);
return OnError(-Line, "Ctx failed to include '%s'", v.Str());
}
}
else
{
if (LIsRelativePath(v.Str()))
{
char p[MAX_PATH_LEN];
LMakePath(p, sizeof(p), FileName, "..");
LMakePath(p, sizeof(p), p, v.Str());
v = p;
}
if (LFileExists(v.Str()))
{
LFile f(v.Str());
if (f &&
(IncCode = f.Read()))
{
Lex(IncCode, v.Str());
}
else
{
DeleteArray(t);
return OnError(-Line, "Couldn't read '%s'", v.Str());
}
}
else
{
DeleteArray(t);
return OnError(-Line, "Couldn't include '%s'", v.Str());
}
}
}
else
{
OnError(-Line, "No file for #include.");
}
}
else if (!StrnicmpW(t + 1, sDefine, Len = StrlenW(sDefine)))
{
LAutoWString Name(LexCpp(s, LexStrdup));
if (Name && IsAlpha(*Name))
{
char16 *Start = s;
while (*Start && strchr(LWhiteSpace, *Start))
Start++;
char16 *Eol = StrchrW(Start, '\n');
if (!Eol)
Eol = Start + StrlenW(Start);
while (Eol > Start && strchr(LWhiteSpace, Eol[-1]))
Eol--;
LAutoWString Value(NewStrW(Start, Eol - Start));
SimplifyDefine(Eng, Name, Value);
Defines.Add(Name, Value.Release());
s = Eol;
}
}
DeleteArray(t);
continue;
}
if (!ResolveDefine(t, FileIndex, Line))
{
Lines.Add((int)Tokens.Length(), FileIndex, Line);
Tokens.Add(t);
}
} // end of "while (t = LexCpp)" loop
if (!Script)
{
Script = w;
}
else
{
DeleteArray(w);
}
return true;
}
/// Create a null var ref
void AllocNull(LVarRef &r)
{
r.Scope = SCOPE_GLOBAL;
if (Code->Globals.NullIndex < 0)
Code->Globals.NullIndex = (int)Code->Globals.Length();
r.Index = Code->Globals.NullIndex;
Code->Globals[r.Index].Type = GV_NULL;
}
/// Allocate a variant and ref
LVariant *PreAllocVariant(LVarRef &r)
{
r.Scope = SCOPE_GLOBAL;
r.Index = (int)Code->Globals.Length();
return &Code->Globals[r.Index];
}
/// Allocate a constant double
void AllocConst(LVarRef &r, double d)
{
r.Scope = SCOPE_GLOBAL;
if (Code->Globals.Length())
{
// Check for existing int
LVariant *p = &Code->Globals[0];
LVariant *e = p + Code->Globals.Length();
while (p < e)
{
if (p->Type == GV_DOUBLE &&
p->Value.Dbl == d)
{
r.Index = (int) (p - &Code->Globals[0]);
return;
}
p++;
}
}
r.Index = (int)Code->Globals.Length();
Code->Globals[r.Index] = d;
}
/// Allocate a constant bool
void AllocConst(LVarRef &r, bool b)
{
r.Scope = SCOPE_GLOBAL;
if (Code->Globals.Length())
{
// Check for existing int
LVariant *p = &Code->Globals[0];
LVariant *e = p + Code->Globals.Length();
while (p < e)
{
if (p->Type == GV_BOOL &&
p->Value.Bool == b)
{
r.Index = (int)(p - &Code->Globals[0]);
return;
}
p++;
}
}
r.Index = (int)Code->Globals.Length();
Code->Globals[r.Index] = b;
}
/// Allocate a constant int
void AllocConst(LVarRef &r, int64 i)
{
r.Scope = SCOPE_GLOBAL;
LVariantType Type = i <= INT_MAX && i >= INT_MIN ? GV_INT32 : GV_INT64;
if (Code->Globals.Length())
{
// Check for existing int
LVariant *p = &Code->Globals[0];
LVariant *e = p + Code->Globals.Length();
while (p < e)
{
if (p->Type == Type)
{
if (Type == GV_INT32 &&
p->Value.Int == i)
{
r.Index = (int) (p - &Code->Globals[0]);
return;
}
else if (Type == GV_INT64 &&
p->Value.Int64 == i)
{
r.Index = (int) (p - &Code->Globals[0]);
return;
}
}
p++;
}
}
// Allocate new global
r.Index = (int)Code->Globals.Length();
if (Type == GV_INT32)
Code->Globals[r.Index] = (int32)i;
else
Code->Globals[r.Index] = i;
}
void AllocConst(LVarRef &r, int i)
{
AllocConst(r, (int64)i);
}
/// Allocate a constant string
void AllocConst(LVarRef &r, char *s, ssize_t len = -1)
{
LAssert(s != 0);
if (len < 0)
len = strlen(s);
r.Scope = SCOPE_GLOBAL;
r.Index = (int)Code->Globals.Length();
for (unsigned i=0; iGlobals.Length(); i++)
{
if (Code->Globals[i].Type == GV_STRING)
{
char *c = Code->Globals[i].Str();
if (*s == *c && strcmp(s, c) == 0)
{
r.Index = i;
return;
}
}
}
LVariant &v = Code->Globals[r.Index];
v.Type = GV_STRING;
if ((v.Value.String = NewStr(s, len)))
{
UnEscape(v.Value.String);
}
}
/// Allocate a constant wide string
void AllocConst(LVarRef &r, char16 *s, ssize_t len)
{
LAssert(s != 0);
char *utf = WideToUtf8(s, len);
if (!utf)
utf = NewStr("");
r.Scope = SCOPE_GLOBAL;
r.Index = (int)Code->Globals.Length();
for (unsigned i=0; iGlobals.Length(); i++)
{
if (Code->Globals[i].Type == GV_STRING)
{
char *c = Code->Globals[i].Str();
if (*utf == *c && strcmp(utf, c) == 0)
{
r.Index = i;
DeleteArray(utf);
return;
}
}
}
LVariant &v = Code->Globals[r.Index];
v.Type = GV_STRING;
if ((v.Value.String = utf))
{
UnEscape(v.Value.String);
}
}
/// Find a variable by name, creating it if needed
LVarRef FindVariable(LVariant &Name, bool Create)
{
LVarRef r = {0, -1};
// Look for existing variable...
ssize_t i;
for (i=Scopes.Length()-1; i>=0; i--)
{
r.Index = Scopes[i]->Var(Name.Str(), false);
if (r.Index >= 0)
{
r.Scope = Scopes[i]->Scope;
return r;
}
}
// Create new variable on most recent scope
i = Scopes.Length() - 1;
r.Index = Scopes[i]->Var(Name.Str(), Create);
if (r.Index >= 0)
{
r.Scope = Scopes[i]->Scope;
}
return r;
}
/// Build asm to assign a var ref
bool AssignVarRef(Node &n, LVarRef &Value)
{
/*
Examples and what their assembly should look like:
a = Value
Assign a <- Value
a[b] = Value
R0 = AsmExpression(b);
ArraySet a[R0] <- Value
a[b].c = Value
R0 = AsmExpression(b);
R0 = ArrayGet a[R0]
DomSet(R0, "c", Null, Value)
a[b].c[d]
R0 = AsmExpression(b);
R0 = ArrayGet a[R0];
R1 = AsmExpression(d);
R0 = DomGet(R0, "c", R1);
ArraySet R0[R1] = Value
a.b[c].d = Value
R1 = AsmExpression(c);
R0 = DomGet(a, "b", R1);
DomSet(R1, "d", Null, Value)
// resolve initial array
if (parts > 1 && part[0].array)
{
//
cur = ArrayGet(part[0].var, part[0].array)
}
else
{
cur = part[0]
}
// dom loop over
loop (p = 0 to parts - 2)
{
if (part[p+1].array)
arr = exp(part[p+1].array)
else
arr = null
cur = DomGet(cur, part[p+1].var, arr)
}
// final part
if (part[parts-1].arr)
{
arr = exp(part[parts-1].arr)
ArraySet(cur, arr)
}
else
{
DomSet(cur, part[parts-1].var, null, value)
}
*/
if (!Value.Valid())
{
LAssert(!"Invalid value to assign.\n");
return false;
}
if (!n.IsVar())
{
LAssert(!"Target must be a variable.");
return false;
}
// Gets the first part of the variable.
LVarRef Cur = FindVariable(n.Variable[0].Name, true);
if (!Cur.Valid())
return false;
if (n.Variable.Length() > 1)
{
// Do any initial array dereference
if (n.Variable[0].Array.Length())
{
// Assemble the array index's expression into 'Idx'
LVarRef Idx;
Idx.Empty();
if (!AsmExpression(&Idx, n.Variable[0].Array))
return OnError(n.Tok, "Error creating bytecode for array index.");
LVarRef Dst = Cur;
if (!Dst.IsReg())
{
AllocReg(Dst, n.Tok, _FL);
}
// Assemble array load instruction
Asm3(n.Tok, IArrayGet, Dst, Cur, Idx);
Cur = Dst;
// Cleanup
DeallocReg(Idx);
}
// Do all the DOM "get" instructions
unsigned p;
for (p=0; pOwnStr(NewStrW(t + 1, Len - 2));
}
else if (StrchrW(t, '.'))
{
// double
*v = atof(t);
}
else if (t[0] == '0' && tolower(t[1]) == 'x')
{
// hex integer
*v = htoi(t + 2);
}
else
{
// decimal integer
int64 i = Atoi(t);
if (i <= INT_MAX && i >= INT_MIN)
*v = (int32)i;
else
*v = i;
}
return true;
}
/// Convert a token stream to a var ref
bool TokenToVarRef(Node &n, LVarRef *&LValue)
{
if (n.Reg.Valid())
{
if (LValue && n.Reg != *LValue)
{
// Need to assign to LValue
LAssert(*LValue != n.Reg);
Asm2(n.Tok, IAssign, *LValue, n.Reg);
}
}
else
{
if (n.IsVar())
{
// Variable
unsigned p = 0;
bool ArrayDeindexed = false;
bool HasScriptArgs = Scopes.Length() <= 1 && ScriptArgs != NULL;
LVarRef v = FindVariable(n.Variable[p].Name, /*!HasScriptArgs ||*/ LValue != NULL);
if (v.Index < 0)
{
if (HasScriptArgs)
{
if (AllocReg(v, n.Tok, _FL))
{
char16 *VarName = GetTok((unsigned)n.Tok);
if (!ScriptArgsRef.Valid())
{
// Setup the global variable to address the script argument variable
ScriptArgsRef.Scope = SCOPE_GLOBAL;
ScriptArgsRef.Index = (int)Code->Globals.Length();
LVariant &v = Code->Globals[ScriptArgsRef.Index];
v.Type = GV_DOM;
v.Value.Dom = ScriptArgs;
}
LVarRef Name, Array;
AllocConst(Name, VarName, -1);
if (n.Variable[p].Array.Length())
{
if (!AsmExpression(&Array, n.Variable[p].Array))
return OnError(n.Tok, "Can't assemble array expression.");
ArrayDeindexed = true;
}
else AllocNull(Array);
Asm4(n.Tok, IDomGet, v, ScriptArgsRef, Name, Array);
}
else return false;
}
else
{
Node::VariablePart &vp = n.Variable[p];
return OnError(n.Tok, "Undefined variable: %s", vp.Name.Str());
}
}
else
{
if (v.Scope == SCOPE_OBJECT)
{
// We have to load the variable into a register
LVarRef Reg, ThisPtr, MemberIndex, Null;
if (!AllocReg(Reg, n.Tok, _FL))
return OnError(n.Tok, "Couldn't alloc register.");
ThisPtr.Scope = SCOPE_LOCAL;
ThisPtr.Index = 0;
AllocConst(MemberIndex, v.Index);
AllocNull(Null);
Asm4(n.Tok, IDomGet, Reg, ThisPtr, MemberIndex, Null);
v = Reg; // Object variable now in 'Reg'
}
if (n.ConstTok == TTypeId)
{
if (!v.IsReg())
{
// Because we are casting to it's DOM ptr,
// make sure it's a register first so we don't lose the
// actual variable.
LVarRef reg;
if (!AllocReg(reg, n.Tok, _FL))
return OnError(n.Tok, "Couldn't alloc register.");
LAssert(reg != v);
Asm2(n.Tok, IAssign, reg, v);
v = reg;
}
// Casting to DOM will give as access to the type info for a LCustomType.
// This currently doesn't work with other types :(
Asm1(n.Tok, ICast, v);
Code->ByteCode.Add(GV_DOM);
}
}
n.Reg = v;
LValue = NULL;
LAssert(v.Scope != SCOPE_OBJECT);
// Does it have an array deref?
if (!ArrayDeindexed &&
n.Variable[p].Array.Length())
{
// Evaluate the array indexing expression
if (!AsmExpression(&n.ArrayIdx, n.Variable[p].Array))
{
return OnError(n.Tok, "Error creating byte code for array index.");
}
// Do we need to create code to load the value from the array?
LVarRef Val;
if (AllocReg(Val, n.Tok, _FL))
{
Asm3(n.Tok, IArrayGet, Val, n.Reg, n.ArrayIdx);
n.Reg = Val;
}
else return OnError(n.Tok, "Error allocating register.");
}
// Load DOM parts...
for (++p; p Args;
Node::VariablePart &Part = n.Variable[p];
Name.Empty();
Arr.Empty();
char *nm = Part.Name.Str();
AllocConst(Name, nm, strlen(nm));
if (Part.Array.Length())
{
if (!AsmExpression(&Arr, Part.Array))
{
return OnError(n.Tok, "Can't assemble array expression.");
}
}
else if (Part.Call)
{
for (unsigned i=0; i Call;
Call[0] = Dst;
Call[1] = n.Reg;
Call[2] = Name;
// This must always be a global, the decompiler requires access
// to the constant to know how many arguments to print. And it
// can only see the globals.
AllocConst(Call[3], (int)Args.Length());
Call.Add(Args);
AsmN(n.Tok, IDomCall, Call);
}
else
{
Asm4(n.Tok, IDomGet, Dst, n.Reg, Name, Arr);
}
n.Reg = Dst;
}
}
else if (n.IsConst())
{
// Constant
switch (n.ConstTok)
{
case TTrue:
{
AllocConst(n.Reg, true);
break;
}
case TFalse:
{
AllocConst(n.Reg, false);
break;
}
case TNull:
{
AllocNull(n.Reg);
break;
}
default:
{
if (n.Lst.Length())
{
// List/array constant
LVariant *v = PreAllocVariant(n.Reg);
if (v)
{
v->SetList();
for (unsigned i=0; i a(new LVariant);
if (!ConvertStringToVariant(a, t))
break;
v->Value.Lst->Insert(a.Release());
}
}
}
else
{
char16 *t = Tokens[n.Tok];
if (*t == '\"' || *t == '\'')
{
// string
ssize_t Len = StrlenW(t);
AllocConst(n.Reg, t + 1, Len - 2);
}
else if (StrchrW(t, '.'))
{
// double
AllocConst(n.Reg, atof(t));
}
else if (t[0] == '0' && tolower(t[1]) == 'x')
{
// hex integer
AllocConst(n.Reg, htoi(t + 2));
}
else
{
// decimal integer
AllocConst(n.Reg, Atoi(t));
}
}
break;
}
}
LValue = NULL;
}
else if (n.IsContextFunc())
{
// Method call, create byte codes to put func value into n.Reg
LVarRef *OutRef;
if (LValue)
{
OutRef = LValue;
}
else
{
if (!AllocReg(n.Reg, n.Tok, _FL))
{
return OnError(n.Tok, "Can't allocate register for method return value.");
}
OutRef = &n.Reg;
}
DebugInfo(n.Tok);
LArray a;
for (unsigned i=0; iByteCode.Length();
ssize_t Size = 1 + sizeof(LFunc*) + sizeof(LVarRef) + 2 + (a.Length() * sizeof(LVarRef));
Code->ByteCode.Length(Len + Size);
LScriptPtr p;
uint8_t *Start = &Code->ByteCode[Len];
p.u8 = Start;
*p.u8++ = ICallMethod;
*p.fn++ = n.ContextFunc;
n.ContextFunc->InUse = true;
*p.r++ = *OutRef;
*p.u16++ = (uint16) n.Args.Length();
for (unsigned i=0; iGetName();
if (*FnName == 'D' &&
!_stricmp(FnName, sDebugger))
{
Asm0(n.Tok, IDebug);
return true;
}
if (n.ScriptFunc->GetParams().Length() != n.Args.Length())
{
if (n.ScriptFunc->ValidStartAddr())
{
// Function is already defined and doesn't have the right arg count...
return OnError( n.Tok,
"Wrong number of parameters: %i (%s expects %i)",
n.Args.Length(),
FnName,
n.ScriptFunc->GetParams().Length());
}
else
{
// Maybe it's defined later or in a separate file?
// Or maybe it's just not defined at all?
// Can't know until later... flag it as a unresolved external...
//
// Allow the assembly for the call to occur.
}
}
// Call to a script function, create byte code to call function
LVarRef *OutRef;
if (LValue)
{
OutRef = LValue;
}
else
{
if (!AllocReg(n.Reg, n.Tok, _FL))
{
return OnError(n.Tok, "Can't allocate register for method return value.");
}
OutRef = &n.Reg;
}
DebugInfo(n.Tok);
LArray a;
for (unsigned i=0; iByteCode.Length();
size_t Size = 1 + // instruction
sizeof(uint32_t) + // address of function
sizeof(uint16) + // size of frame
sizeof(LVarRef) + // return value
2 + // number of args
(a.Length() * sizeof(LVarRef)); // args
Code->ByteCode.Length(Len + Size);
LScriptPtr p;
uint8_t *Start = &Code->ByteCode[Len];
p.u8 = Start;
*p.u8++ = ICallScript;
if (n.ScriptFunc->ValidStartAddr() &&
n.ScriptFunc->ValidFrameSize())
{
// Compile func address straight into code...
*p.u32++ = n.ScriptFunc->GetStartAddr();
*p.u16++ = n.ScriptFunc->GetFrameSize();
}
else
{
// Add link time fix up
LinkFixup &Fix = Fixups.New();
Fix.Tok = n.Tok;
Fix.Args = (int)n.Args.Length();
Fix.Offset = p.u8 - &Code->ByteCode[0];
Fix.Func = n.ScriptFunc;
*p.u32++ = 0;
*p.u16++ = 0;
}
*p.r++ = *OutRef;
*p.u16++ = (uint16) n.Args.Length();
for (unsigned i=0; i e(new Node::NodeExp);
if (!e)
return OnError(Cur, "Mem alloc error.");
if (!Expression(Cur, *e))
return OnError(Cur, "Couldn't parse func call argument expression.");
vp.Args.Add(e.Release());
t = GetTok(Cur);
if (!t)
return OnError(Cur, "Unexpected end of file.");
if (!StricmpW(t, sComma))
Cur++;
else if (!StricmpW(t, sEndRdBracket))
break;
else
return OnError(Cur, "Expecting ',', didn't get it.");
}
}
t = GetTok(Cur+1);
}
// Check for DOM operator...
if (StricmpW(t, sPeriod) == 0)
{
// Got Dom operator
Cur += 2;
t = GetTok(Cur);
if (!t)
return OnError(Cur, "Unexpected eof.");
Var.Variable.New().Name = t;
}
else break;
}
return true;
}
/// Parse expression into a node tree
bool Expression(uint32_t &Cur, LArray &n, int Depth = 0)
{
if (Cur >= Tokens.Length())
return OnError(Cur, "Unexpected end of file.");
char16 *t;
bool PrevIsOp = true;
while ((t = Tokens[Cur]))
{
LTokenType Tok = ExpTok.Find(t);
if (Tok == TTypeId)
{
char16 *v;
if (!DoTypeId(Cur, v))
return false;
Node &Var = n.New();
t = v;
Cur--;
DoVariableNode(Cur, Var, t);
Var.ConstTok = TTypeId;
Cur++;
}
else if (Tok == TStartRdBracket)
{
Cur++;
auto &Next = n.New();
if (!Expression(Cur, Next.Child, Depth + 1))
return false;
PrevIsOp = false;
if (Next.Child.Length() > 0)
Next.Tok = Next.Child[0].Tok;
}
else if (Tok == TEndRdBracket)
{
if (Depth > 0)
Cur++;
break;
}
else if (Tok == TComma || Tok == TSemiColon)
{
break;
}
else if (Depth == 0 && Tok == TEndSqBracket)
{
break;
}
else if (Tok == TTrue || Tok == TFalse || Tok == TNull)
{
n.New().SetConst(Cur++, Tok);
}
else
{
LOperator o = IsOp(t, PrevIsOp);
if (o != OpNull)
{
// Operator
PrevIsOp = 1;
n.New().SetOp(o, Cur);
}
else
{
PrevIsOp = 0;
LVariant m;
m = t;
LFunc *f = Methods.Find(m.Str());
LFunctionInfo *sf = 0;
char16 *Next = GetTok(Cur+1);
bool IsFunctionCall = !StricmpW(Next, sStartRdBracket);
if (f && IsFunctionCall)
{
Node &Call = n.New();
Call.SetContextFunction(f, Cur++);
// Now parse arguments
// Get the start bracket
if ((t = GetTok(Cur)))
{
if (StricmpW(t, sStartRdBracket) == 0)
Cur++;
else
return OnError(Cur, "Function missing '('");
}
else return OnError(Cur, "No token.");
// Parse the args as expressions
while ((t = GetTok(Cur)))
{
LTokenType Tok = ExpTok.Find(t);
if (Tok == TComma)
{
// Do nothing...
Cur++;
}
else if (Tok == TEndRdBracket)
{
break;
}
else if (Tok == TSemiColon)
{
return OnError(Cur, "Unexpected ';'");
}
else if (!Expression(Cur, Call.Args.New()))
{
return OnError(Cur, "Can't parse function argument.");
}
}
}
else if
(
(sf = Code->GetMethod(m.Str(), IsFunctionCall))
)
{
Node &Call = n.New();
Call.SetScriptFunction(sf, Cur++);
// Now parse arguments
// Get the start bracket
if ((t = GetTok(Cur)))
{
if (StricmpW(t, sStartRdBracket) == 0)
Cur++;
else
return OnError(Cur, "Function missing '('");
}
else return OnError(Cur, "No token.");
// Parse the args as expressions
while ((t = GetTok(Cur)))
{
if (StricmpW(t, sComma) == 0)
{
// Do nothing...
Cur++;
}
else if (StricmpW(t, sEndRdBracket) == 0)
{
break;
}
else if (!Expression(Cur, Call.Args[Call.Args.Length()]))
{
return OnError(Cur, "Can't parse function argument.");
}
}
}
else if (IsAlpha(*t))
{
// Variable...
Node &Var = n.New();
if (!DoVariableNode(Cur, Var, t))
return false;
}
else if (*t == '\'' ||
*t == '\"' ||
LIsNumber(t))
{
// Constant string or number
n.New().SetConst(Cur, TLiteral);
}
else if (Tok == TStartCurlyBracket)
{
// List definition
LArray Values;
Cur++;
int Index = 0;
while ((t = Tokens[Cur]))
{
LTokenType Tok = ExpTok.Find(t);
if (Tok == TComma)
{
Index++;
}
else if (Tok == TEndCurlyBracket)
{
break;
}
else if (*t == '\'' ||
*t == '\"' ||
LIsNumber(t))
{
Values[Index] = Cur;
}
else
{
return OnError(Cur, "Unexpected token '%S' in list definition", t);
}
Cur++;
}
n.New().SetConst(Values, TLiteral);
}
else
{
// Unknown
return OnError(Cur, "Unknown token '%S'", t);
}
}
Cur++;
}
}
return true;
}
/// Allocate a register (must be mirrored with DeallocReg)
bool AllocReg(LVarRef &r, int LineOrTok, const char *file, int line)
{
for (int i=0; iPrint("CompileError:Register[%i] allocated by %s\n", n, a);
}
}
#endif
return false;
}
/// Deallocate a register
bool DeallocReg(LVarRef &r)
{
if (r.Scope == SCOPE_REGISTER && r.Index >= 0)
{
int Bit = 1 << r.Index;
// LAssert((Bit & Regs) != 0);
Regs &= ~Bit;
#ifdef _DEBUG
RegAllocators[r.Index].Empty();
#endif
}
return true;
}
/// Count allocated registers
int RegAllocCount()
{
int c = 0;
for (int i=0; i &n)
{
LStringPipe e;
for (unsigned i=0; iMethod.Get());
}
else if (n[i].ScriptFunc)
{
e.Print("%s(...)", n[i].ScriptFunc->GetName());
}
else
{
e.Print("#err#");
}
}
return e.NewStr();
}
/// Creates byte code to evaluate an expression
bool AsmExpression
(
/// Where the result got stored
LVarRef *Result,
/// The nodes to create code for
LArray &n,
/// The depth of recursion
int Depth = 0
)
{
// Resolve any sub-expressions and store their values
for (unsigned i = 0; i < n.Length(); i++)
{
auto &k = n[i];
if (!k.IsVar() &&
k.Child.Length())
{
AllocReg(k.Reg, k.Tok, _FL);
AsmExpression(&k.Reg, k.Child, Depth + 1);
}
}
while (n.Length() > 1)
{
// Find which operator to handle first
#ifdef _DEBUG
size_t StartLength = n.Length();
#endif
int OpIdx = -1;
int Prec = -1;
int Ops = 0;
for (unsigned i=0; i Jmp;
if (!TokenToVarRef(a, NullRef))
return OnError(a.Tok, "Can't convert left token to var ref.");
if (Op == OpAnd)
{
// Jump over 'b' if 'a' is FALSE
Jmp.Reset(new LJumpZero(this, a.Tok, a.Reg, false));
}
else if (Op == OpOr)
{
// Jump over 'b' if 'a' is TRUE
}
if (!TokenToVarRef(b, LValue))
return OnError(b.Tok, "Can't convert right token to var ref.");
LVarRef Reg;
Reg.Empty();
if (a.Reg.Scope != SCOPE_REGISTER)
{
if (AllocReg(Reg, a.Tok, _FL))
{
LAssert(Reg != a.Reg);
Asm2(a.Tok, IAssign, Reg, a.Reg);
a.Reg = Reg;
}
else return OnError(a.Tok, "Can't alloc register, Regs=0x%x", Regs);
}
Asm2(a.Tok, Op, a.Reg, b.Reg);
if (Op == OpPlusEquals ||
Op == OpMinusEquals ||
Op == OpMulEquals ||
Op == OpDivEquals)
{
AssignVarRef(a, a.Reg);
}
}
if (a.Reg != b.Reg)
DeallocReg(b.Reg);
n.DeleteAt(OpIdx+1, true);
n.DeleteAt(OpIdx, true);
// Result is in n[OpIdx-1] (ie the 'a' node)
}
else
{
LAssert(!"Not a valid type");
return OnError(n[0].Tok, "Not a valid type.");
}
#ifdef _DEBUG
if (StartLength == n.Length())
{
// No nodes removed... infinite loop!
LAssert(!"No nodes removed.");
return false;
}
#endif
}
if (n.Length() == 1)
{
auto &k = n[0];
if (!k.Reg.Valid())
{
LVarRef *NullRef = NULL;
if (!TokenToVarRef(k, NullRef))
{
return false;
}
}
if (Result)
{
if (Result->Valid() && *Result != k.Reg)
DeallocReg(*Result);
*Result = k.Reg;
}
else
{
DeallocReg(k.Reg);
}
return true;
}
return false;
}
/// Parses and assembles an expression
bool DoExpression(uint32_t &Cur, LVarRef *Result)
{
LArray n;
if (Expression(Cur, n))
{
bool Status = AsmExpression(Result, n);
return Status;
}
return false;
}
/// Converts a variable to it's type information
bool DoTypeId(uint32_t &Cur, char16 *&Var)
{
if (GetTokType(Cur) != TTypeId)
return OnError(Cur, "Expecting 'typeid'.");
Cur++;
if (GetTokType(Cur) != TStartRdBracket)
return OnError(Cur, "Expecting '('.");
Cur++;
char16 *t = GetTok(Cur);
if (!t || !IsAlpha(*t))
return OnError(Cur, "Expecting variable name.");
Cur++;
if (GetTokType(Cur) != TEndRdBracket)
return OnError(Cur, "Expecting ')'.");
Cur++;
Var = t;
return true;
}
/// Parses statements
bool DoStatements(uint32_t &Cur, bool *LastWasReturn, bool MoreThanOne = true)
{
while (Cur < Tokens.Length())
{
char16 *t = GetTok(Cur);
if (!t)
break;
LTokenType Tok = ExpTok.Find(t);
switch (Tok)
{
case TTypeId:
{
return OnError(Cur, "typeif only valid in an expression.");
}
case TSemiColon:
{
Cur++;
break;
}
case TDebug:
{
Asm0(Cur++, IDebug);
break;
}
case TBreakPoint:
{
Asm0(Cur++, IBreakPoint);
break;
}
case TReturn:
{
Cur++;
if (!DoReturn(Cur))
return false;
if (LastWasReturn)
*LastWasReturn = true;
break;
}
case TEndCurlyBracket:
case TFunction:
{
return true;
}
case TIf:
{
if (!DoIf(Cur))
return false;
if (LastWasReturn)
*LastWasReturn = false;
break;
}
case TFor:
{
if (!DoFor(Cur))
return false;
if (LastWasReturn)
*LastWasReturn = false;
break;
}
case TWhile:
{
if (!DoWhile(Cur))
return false;
if (LastWasReturn)
*LastWasReturn = false;
break;
}
case TExtern:
{
if (!DoExtern(Cur))
return false;
if (LastWasReturn)
*LastWasReturn = false;
break;
}
default:
{
if (!DoExpression(Cur, 0))
return false;
LTokenType Tok = ExpTok.Find(GetTok(Cur));
if (Tok == TSemiColon)
Cur++;
if (LastWasReturn)
*LastWasReturn = false;
break;
}
}
if (!MoreThanOne)
break;
}
return true;
}
/// Parses if/else if/else construct
bool DoIf(uint32_t &Cur)
{
Cur++;
char16 *t;
if (GetTokType(Cur) == TStartRdBracket)
{
Cur++;
// Compile and asm code to evaluate the expression
LVarRef Result;
int ExpressionTok = Cur;
Result.Empty();
if (DoExpression(Cur, &Result))
{
t = GetTok(Cur);
if (!t || StricmpW(t, sEndRdBracket))
return OnError(Cur, "if missing ')'.");
Cur++;
t = GetTok(Cur);
if (!t)
return OnError(Cur, "if missing body statement.");
// Output the jump instruction
LAutoPtr Jmp(new LJumpZero(this, ExpressionTok, Result));
if (!StricmpW(t, sStartCurlyBracket))
{
// Statement block
Cur++;
while ((t = GetTok(Cur)))
{
if (!StricmpW(t, sSemiColon))
{
Cur++;
}
else if (!StricmpW(t, sEndCurlyBracket))
{
Cur++;
break;
}
else if (!DoStatements(Cur, NULL))
{
return false;
}
}
}
else
{
// Single statement
if (!DoStatements(Cur, NULL, false))
return false;
}
// Check for else...
if ((t = GetTok(Cur)) && StricmpW(t, sElse) == 0)
{
// Add a jump for the "true" part of the expression to
// jump over the "else" part.
Asm0(Cur, IJump);
size_t JOffset = Code->ByteCode.Length();
if (Code->ByteCode.Length(JOffset + 4))
{
// Initialize the ptr to zero
int32 *Ptr = (int32*)&Code->ByteCode[JOffset];
*Ptr = 0;
// Resolve jz to here...
Jmp.Reset();
// Compile the else block
Cur++;
if ((t = GetTok(Cur)) && StricmpW(t, sStartCurlyBracket) == 0)
{
// 'Else' Statement block
Cur++;
while ((t = GetTok(Cur)))
{
if (!StricmpW(t, sSemiColon))
{
Cur++;
}
else if (!StricmpW(t, sEndCurlyBracket))
{
Cur++;
break;
}
else if (!DoStatements(Cur, NULL))
{
return false;
}
}
}
else
{
// Single statement
if (!DoStatements(Cur, NULL, false))
return false;
}
// Resolve the "JOffset" jump that takes execution of
// the 'true' part over the 'else' part
Ptr = (int32*)&Code->ByteCode[JOffset];
*Ptr = (int32) (Code->ByteCode.Length() - JOffset - sizeof(int32));
if (*Ptr == 0)
{
// Empty statement... so delete the Jump instruction
Code->ByteCode.Length(JOffset - 1);
}
}
else OnError(Cur, "Mem alloc");
}
return true;
}
}
else return OnError(Cur, "if missing '('");
return false;
}
LArray &GetByteCode()
{
return Code->ByteCode;
}
class LJumpZero
{
LCompilerPriv *Comp;
int JzOffset;
public:
LJumpZero(LCompilerPriv *d, int Tok, LVarRef &r, bool DeallocReg = true)
{
// Create jump instruction to jump over the body if the expression evaluates to false
Comp = d;
Comp->Asm1(Tok, IJumpZero, r);
if (DeallocReg)
Comp->DeallocReg(r);
JzOffset = (int) Comp->GetByteCode().Length();
Comp->GetByteCode().Length(JzOffset + sizeof(int32));
}
~LJumpZero()
{
// Resolve jump
int32 *Ptr = (int32*) &Comp->GetByteCode()[JzOffset];
*Ptr = (int32) (Comp->GetByteCode().Length() - (JzOffset + sizeof(int32)));
}
};
/// Parses while construct
bool DoWhile(uint32_t &Cur)
{
Cur++;
char16 *t = GetTok(Cur);
if (!t || StricmpW(t, sStartRdBracket))
return OnError(Cur, "Expecting '(' after 'while'");
Cur++;
// Store start of condition code
size_t ConditionStart = Code->ByteCode.Length();
// Compile condition evalulation
LVarRef r;
r.Empty();
if (!DoExpression(Cur, &r))
return false;
// Create jump instruction to jump over the body if the expression evaluates to false
{
LJumpZero Jump(this, Cur, r);
if (!(t = GetTok(Cur)) || StricmpW(t, sEndRdBracket))
return OnError(Cur, "Expecting ')'");
Cur++;
// Compile the body of the loop
if (!(t = GetTok(Cur)))
return OnError(Cur, "Unexpected eof.");
Cur++;
if (StricmpW(t, sStartCurlyBracket) == 0)
{
// Block
while ((t = GetTok(Cur)))
{
if (!StricmpW(t, sSemiColon))
{
Cur++;
}
else if (!StricmpW(t, sEndCurlyBracket))
{
Cur++;
break;
}
else if (!DoStatements(Cur, NULL))
{
return false;
}
}
}
else
{
// Single statement
DoStatements(Cur, NULL, false);
}
// Jump to condition evaluation at 'ConditionStart'
Asm0(Cur, IJump);
size_t JOffset = Code->ByteCode.Length();
Code->ByteCode.Length(JOffset + sizeof(int32));
int32 *Ptr = (int32*) &Code->ByteCode[JOffset];
*Ptr = (int32) (ConditionStart - Code->ByteCode.Length());
}
return true;
}
/// Parses for construct
bool DoFor(uint32_t &Cur)
{
/*
For loop asm structure:
+---------------------------+
| Pre-condition expression |
+---------------------------+
| Eval loop contdition exp. |<--+
| | |
| JUMP ZERO (jumpz) |---+-+
+---------------------------+ | |
| Body of loop... | | |
| | | |
| | | |
| | | |
| | | |
| | | |
+---------------------------+ | |
| Post-cond. expression | | |
| | | |
| JUMP |---+ |
+---------------------------+ |
| Following code... |<----+
. .
*/
Cur++;
char16 *t = GetTok(Cur);
if (!t || StricmpW(t, sStartRdBracket))
return OnError(Cur, "Expecting '(' after 'for'");
Cur++;
// Compile initial statement
LVarRef r;
r.Empty();
t = GetTok(Cur);
if (!t)
return false;
if (StricmpW(t, sSemiColon) && !DoExpression(Cur, 0))
return false;
t = GetTok(Cur);
// Look for ';'
if (!t || StricmpW(t, sSemiColon))
return OnError(Cur, "Expecting ';'");
Cur++;
// Store start of condition code
size_t ConditionStart = Code->ByteCode.Length();
// Compile condition evalulation
if (!DoExpression(Cur, &r))
return false;
{
LJumpZero Jmp(this, Cur, r);
t = GetTok(Cur);
// Look for ';'
if (!t || StricmpW(t, sSemiColon))
return OnError(Cur, "Expecting ';'");
Cur++;
// Compile the post expression code
size_t PostCodeStart = Code->ByteCode.Length();
t = GetTok(Cur);
if (StricmpW(t, sEndRdBracket) && !DoExpression(Cur, 0))
return false;
// Store post expression code in temp variable
LArray PostCode;
size_t PostCodeLen = Code->ByteCode.Length() - PostCodeStart;
if (PostCodeLen)
{
PostCode.Length(PostCodeLen);
memcpy(&PostCode[0], &Code->ByteCode[PostCodeStart], PostCodeLen);
// Delete the post expression off the byte code, we are putting it after the block code
Code->ByteCode.Length(PostCodeStart);
}
// Look for ')'
t = GetTok(Cur);
if (!t || StricmpW(t, sEndRdBracket))
return OnError(Cur, "Expecting ')'");
Cur++;
// Compile body of loop
if ((t = GetTok(Cur)) && StricmpW(t, sStartCurlyBracket) == 0)
{
Cur++;
while ((t = GetTok(Cur)))
{
if (!StricmpW(t, sSemiColon))
{
Cur++;
}
else if (!StricmpW(t, sEndCurlyBracket))
{
Cur++;
break;
}
else if (!DoStatements(Cur, NULL))
{
return false;
}
}
}
// Add post expression code
if (PostCodeLen)
{
size_t Len = Code->ByteCode.Length();
Code->ByteCode.Length(Len + PostCodeLen);
memcpy(&Code->ByteCode[Len], &PostCode[0], PostCodeLen);
}
// Jump to condition evaluation at 'ConditionStart'
Asm0(Cur, IJump);
size_t JOffset = Code->ByteCode.Length();
Code->ByteCode.Length(JOffset + sizeof(int32));
int32 *Ptr = (int32*) &Code->ByteCode[JOffset];
*Ptr = (int32) (ConditionStart - Code->ByteCode.Length());
}
return true;
}
/// Compiles return construct
bool DoReturn(uint32_t &Cur)
{
LVarRef ReturnValue;
char16 *t;
int StartTok = Cur;
ReturnValue.Empty();
LArray Exp;
if (!Expression(Cur, Exp))
{
return OnError(Cur, "Failed to compile return expression.");
}
else if (!AsmExpression(&ReturnValue, Exp))
{
return OnError(Cur, "Failed to assemble return expression.");
}
if (!(t = GetTok(Cur)) ||
StricmpW(t, sSemiColon))
{
return OnError(Cur, "Expecting ';' after return expression.");
}
Asm1(StartTok, IRet, ReturnValue);
DeallocReg(ReturnValue);
Cur++;
return true;
}
// Compile a method definition
bool DoFunction
(
/// Cursor token index
uint32_t &Cur,
/// [Optional] Current struct / class.
/// If not NULL this is a method definition for said class.
LCustomType *Struct = NULL
)
{
bool Status = false;
bool LastInstIsReturn = false;
if (!JumpLoc)
{
size_t Len = Code->ByteCode.Length();
if (Code->ByteCode.Length(Len + 5))
{
LScriptPtr p;
p.u8 = &Code->ByteCode[Len];
*p.u8++ = IJump;
*p.i32++ = 0;
JumpLoc = Len + 1;
}
else OnError(Cur, "Mem alloc failed.");
}
LString FunctionName;
LCustomType::Method *StructMethod = NULL; // Member function of script struct/class
LFunctionInfo *ScriptMethod = NULL; // Standalone scripting function
char16 *Name = GetTok(Cur);
if (Name)
{
Cur++;
char16 *t = GetTok(Cur);
if (!t || StricmpW(t, sStartRdBracket))
return OnError(Cur, "Expecting '(' in function.");
FunctionName = Name;
// Parse parameters
LArray Params;
Cur++;
while ((t = GetTok(Cur)))
{
if (IsAlpha(*t))
{
Params.New() = t;
Cur++;
if (!(t = GetTok(Cur)))
goto UnexpectedFuncEof;
}
if (!StricmpW(t, sComma))
;
else if (!StricmpW(t, sEndRdBracket))
{
Cur++;
break;
}
else
goto UnexpectedFuncEof;
Cur++;
}
if (Struct)
{
StructMethod = Struct->DefineMethod(FunctionName, Params, Code->ByteCode.Length());
}
else
{
ScriptMethod = Code->GetMethod(FunctionName, true);
if (!ScriptMethod)
return OnError(Cur, "Can't define method '%s'.", FunctionName.Get());
ScriptMethod->GetParams() = Params;
ScriptMethod->SetStartAddr((int32_t)Code->ByteCode.Length());
}
// Parse start of body
if (!(t = GetTok(Cur)))
goto UnexpectedFuncEof;
if (StricmpW(t, sStartCurlyBracket))
return OnError(Cur, "Expecting '{'.");
// Setup new scope(s)
LAutoPtr ObjectScope;
if (Struct)
{
// The object scope has to be first so that local variables take
// precedence over object member variables.
if (ObjectScope.Reset(new LVariables(Struct)))
Scopes.Add(ObjectScope);
}
LVariables LocalScope(SCOPE_LOCAL);
if (Struct)
LocalScope.Var("This", true);
for (unsigned i=0; iFrameSize = (uint16)LocalScope.Length();
else if (ScriptMethod)
ScriptMethod->SetFrameSize(LocalScope.Length());
else
LAssert(!"What are you defining exactly?");
Status = true;
Cur++;
// LgiTrace("Added method %s @ %i, stack=%i, args=%i\n", f->Name.Str(), f->StartAddr, f->FrameSize, f->Params.Length());
break;
}
// Parse statement in the body
if (!DoStatements(Cur, &LastInstIsReturn))
return OnError(Cur, "Can't compile function body.");
}
// Remove any scopes we created
Scopes.PopLast();
if (Struct)
Scopes.PopLast();
}
if (!LastInstIsReturn)
{
LVarRef RetVal;
AllocNull(RetVal);
Asm1(Cur, IRet, RetVal);
}
return Status;
UnexpectedFuncEof:
return OnError(Cur, "Unexpected EOF in function.");
}
LExternFunc::ExternType ParseExternType(LArray &Strs)
{
LExternFunc::ExternType Type;
Type.Out = false;
Type.Ptr = 0;
Type.ArrayLen = 1;
Type.Base = GV_NULL;
Type.Unsigned = false;
bool InArray = false;
for (unsigned i=0; i Tok;
const char16 *t;
while ((t = GetTok(Cur)))
{
if (!StricmpW(t, sStartRdBracket))
{
Cur++;
break;
}
else Tok.Add(t);
Cur++;
}
if (Tok.Length() < 3)
{
return OnError(Cur, "Not enough tokens in extern decl.");
}
// First token is library name
LExternFunc *e = new LExternFunc;
if (!e)
return OnError(Cur, "Alloc error.");
Code->Externs.Add(e);
e->Type = ExternFunc;
char16 sQuotes[] = {'\'','\"',0};
LAutoWString LibName(TrimStrW(Tok[0], sQuotes));
e->Lib.Reset(WideToUtf8(LibName));
Tok.DeleteAt(0, true);
e->Method = Tok.Last();
Tok.DeleteAt(Tok.Length()-1, true);
if (!ValidStr(e->Method))
{
Code->Externs.Length(Code->Externs.Length()-1);
return OnError(Cur, "Failed to get extern method name.");
}
// Parse return type
e->ReturnType = ParseExternType(Tok);
// Parse argument types
Tok.Length(0);
while ((t = GetTok(Cur)))
{
if (!StricmpW(t, sEndRdBracket))
{
e->ArgType.New() = ParseExternType(Tok);
Cur++;
break;
}
else if (!StricmpW(t, sComma))
{
e->ArgType.New() = ParseExternType(Tok);
}
else Tok.Add(t);
Cur++;
}
if ((t = GetTok(Cur)))
{
if (StricmpW(t, sSemiColon))
return OnError(Cur, "Expecting ';' in extern decl.");
Cur++;
}
Methods.Add(e->Method, e);
return true;
}
struct ExpPart
{
LOperator Op;
int Value;
ExpPart()
{
Op = OpNull;
Value = 0;
}
};
int Evaluate(LArray &Exp, size_t Start, size_t End)
{
LArray p;
// Find outer brackets
ssize_t e;
for (e = End; e >= (ssize_t)Start; e--)
{
if (Exp[e][0] == ')') break;
}
for (size_t i = Start; i <= End; i++)
{
char16 *t = Exp[i];
if (*t == '(')
{
p.New().Value = Evaluate(Exp, i + 1, e - 1);
i = e;
}
else
{
LOperator op = IsOp(t, 0);
if (op)
{
p.New().Op = op;
}
else if (IsDigit(*t))
{
p.New().Value = AtoiW(t);
}
else
{
LAssert(0);
break;
}
}
}
while (p.Length() > 1)
{
int HighPrec = 32;
int Idx = -1;
for (unsigned i=0; i 0 && Idx < (int)p.Length() - 1)
{
switch (p[Idx].Op)
{
case OpPlus:
p[Idx-1].Value += p[Idx+1].Value;
break;
case OpMinus:
p[Idx-1].Value -= p[Idx+1].Value;
break;
case OpMul:
p[Idx-1].Value *= p[Idx+1].Value;
break;
case OpDiv:
if (p[Idx+1].Value)
p[Idx-1].Value /= p[Idx+1].Value;
else
LAssert(!"Div 0");
break;
default:
LAssert(!"Impl me.");
break;
}
p.DeleteAt(Idx, true);
p.DeleteAt(Idx, true);
}
else
{
LAssert(0);
break;
}
}
else
{
LAssert(!"Impl me.");
}
}
if (p.Length() == 1)
{
LAssert(p[0].Op == OpNull);
return p[0].Value;
}
LAssert(0);
return 0;
}
int ByteSizeFromType(char *s, LVariantType t)
{
switch (t)
{
case GV_INT32:
{
char n[16], *o = n;
for (char *c = s; *c; c++)
{
if (IsDigit(*c) && o < n + sizeof(n) - 1)
*o++ = *c;
}
*o++ = 0;
int bits = ::atoi(n);
switch (bits)
{
case 8:
return 1;
case 16:
return 2;
}
return 4;
break;
}
case GV_INT64:
{
return 8;
}
case GV_WSTRING:
{
return sizeof(char16);
}
default:
break;
}
return 1;
}
/// Compiles struct construct
bool DoStruct(uint32_t &Cur)
{
bool Status = false;
// Parse struct name and setup a type
char16 *t;
LCustomType *Def = Code->Types.Find(t = GetTok(Cur));
if (!Def)
Code->Types.Add(t, Def = new LCustomType(t));
Cur++;
t = GetTok(Cur);
if (!t || StricmpW(t, sStartCurlyBracket))
return OnError(Cur, "Expecting '{'");
Cur++;
// Parse members
while ((t = GetTok(Cur)))
{
// End of type def?
LTokenType tt = ExpTok.Find(t);
if (tt == TEndCurlyBracket)
{
Cur++;
tt = GetTokType(Cur);
if (!tt || tt != TSemiColon)
return OnError(Cur, "Expecting ';' after '}'");
Status = true;
break;
}
if (tt == TFunction)
{
Cur++;
if (!DoFunction(Cur, Def))
return false;
}
else
{
// Parse member field
LVariant TypeName = t;
LCustomType *NestedType = 0;
LVariantType Type = Types.Find(TypeName.Str());
if (!Type)
{
// Check other custom types
NestedType = Code->GetType(t);
if (!NestedType)
return OnError(Cur, "Unknown type '%S' in struct definition.", t);
// Ok, nested type.
Type = GV_CUSTOM;
}
Cur++;
if (!(t = GetTok(Cur)))
goto EofError;
bool Pointer = false;
if (t[0] == '*' && t[1] == 0)
{
Pointer = true;
Cur++;
if (!(t = GetTok(Cur)))
goto EofError;
}
LVariant Name = t;
Cur++;
if (!(t = GetTok(Cur)))
goto EofError;
int Array = 1;
if (!StricmpW(t, sStartSqBracket))
{
// Array
Cur++;
LArray Exp;
while ((t = GetTok(Cur)))
{
Cur++;
if (!StricmpW(t, sEndSqBracket))
break;
Exp.Add(t);
}
Array = Evaluate(Exp, 0, Exp.Length()-1);
}
int MemberAddr = Def->AddressOf(Name.Str());
if (MemberAddr >= 0)
return OnError(Cur, "Member '%s' can't be defined twice.", Name.Str());
if (NestedType)
{
if (!Def->DefineField(Name.Str(), NestedType, Array))
return OnError(Cur, "Failed to define field '%s'.", Name.Str());
}
else
{
int Bytes = ByteSizeFromType(TypeName.Str(), Type);
if (!Def->DefineField(Name.Str(), Type, Bytes, Array))
return OnError(Cur, "Failed to define field '%s'.", Name.Str());
}
t = GetTok(Cur);
if (StricmpW(t, sSemiColon))
return OnError(Cur, "Expecting ';'");
Cur++;
}
}
return Status;
EofError:
return OnError(Cur, "Unexpected EOF.");
}
/// Compiler entry point
bool Compile()
{
uint32_t Cur = 0;
JumpLoc = 0;
// Setup the global scope
Scopes.Length(0);
Scopes.Add(&Code->Globals);
// Compile the code...
while (Cur < Tokens.Length())
{
char16 *t = GetTok(Cur);
if (!t)
break;
if (*t == '#' ||
StricmpW(t, sSemiColon) == 0)
{
Cur++;
}
else if (!StricmpW(t, sFunction))
{
if (!DoFunction(++Cur))
return false;
}
else if (!StricmpW(t, sStruct))
{
if (!DoStruct(++Cur))
return false;
}
else if (!StricmpW(t, sEndCurlyBracket))
{
return OnError(Cur, "Not expecting '}'.");
}
else if (!StricmpW(t, sExtern))
{
if (!DoExtern(++Cur))
return false;
}
else
{
if (JumpLoc)
{
LScriptPtr p;
p.u8 = &Code->ByteCode[JumpLoc];
*p.u32 = (uint32_t) (Code->ByteCode.Length() - (JumpLoc + 4));
JumpLoc = 0;
}
if (!DoStatements(Cur, NULL))
{
return OnError(Cur, "Statement compilation failed.");
}
}
}
if (JumpLoc)
{
LScriptPtr p;
p.u8 = &Code->ByteCode[JumpLoc];
*p.u32 = (uint32_t) (Code->ByteCode.Length() - (JumpLoc + 4));
JumpLoc = 0;
}
// Do link time fix ups...
for (unsigned i=0; iValidStartAddr())
{
if (f.Args != f.Func->GetParams().Length())
{
return OnError(f.Tok, "Function call '%s' has wrong arg count (caller=%i, method=%i).",
f.Func->GetName(),
f.Args,
f.Func->GetParams().Length());
}
else if (!f.Func->ValidFrameSize())
{
return OnError(f.Tok, "Function call '%s' has no frame size.",
f.Func->GetName());
}
else
{
LScriptPtr p;
p.u8 = &Code->ByteCode[f.Offset];
LAssert(*p.u32 == 0);
*p.u32++ = f.Func->GetStartAddr();
*p.u16++ = f.Func->GetFrameSize();
}
}
else
{
return OnError(f.Tok, "Function '%s' not defined.", f.Func->GetName());
}
}
Fixups.Length(0);
return true;
}
};
LCompiler::LCompiler()
{
d = new LCompilerPriv;
}
LCompiler::~LCompiler()
{
DeleteObj(d);
}
bool LCompiler::Compile
(
LAutoPtr &Code,
LScriptContext *SysContext,
LScriptContext *UserContext,
const char *FileName,
const char *Script,
LDom *Args
)
{
if (!Script)
return false;
LStringPipe p;
#ifdef DEBUG_SCRIPT_FILE
d->Debug = Stristr(FileName, DEBUG_SCRIPT_FILE) != NULL;
#endif
if (SysContext && SysContext->GetLog())
d->Log = SysContext->GetLog();
else if (UserContext && UserContext->GetLog())
d->Log = UserContext->GetLog();
else
d->Log = &p;
d->Methods.Empty();
if (SysContext)
{
- LHostFunc *f = SysContext->GetCommands();
+ auto f = SysContext->GetCommands();
for (int i=0; f[i].Method; i++)
{
f[i].Context = SysContext;
d->Methods.Add(f[i].Method, &f[i]);
}
}
d->SysCtx = SysContext;
d->UserCtx = UserContext;
if (d->UserCtx)
{
- LHostFunc *f = d->UserCtx->GetCommands();
+ auto f = d->UserCtx->GetCommands();
if (f)
{
for (int i=0; f[i].Method; i++)
{
f[i].Context = d->UserCtx;
if (!d->Methods.Find(f[i].Method))
d->Methods.Add(f[i].Method, f+i);
else
{
LgiTrace("%s:%i - Conflicting name of method in application's context: '%s'\n", _FL, f[i].Method.Get());
LAssert(!"Conflicting name of method in application's context.");
}
}
}
}
if (!Code)
Code.Reset(new LCompiledCode);
bool Status = false;
d->Code = dynamic_cast(Code.Get());
if (d->Code)
{
d->Code->UserContext = UserContext;
d->Code->SysContext = SysContext;
d->Code->SetSource(FileName, Script);
bool LexResult = d->Lex((char*)Script, FileName);
if (LexResult)
{
d->ScriptArgs = Args;
Status = d->Compile();
}
else
{
d->OnError(0, "Failed to lex script.\n");
}
}
else
{
d->OnError(0, "Allocation failed.\n");
}
d->Code = NULL;
return Status;
}
//////////////////////////////////////////////////////////////////////
class LScriptEnginePrivate
{
public:
- LViewI *Parent;
+ LViewI *Parent = NULL;
SystemFunctions SysContext;
- LScriptContext *UserContext;
- LCompiledCode *Code;
- LVmCallback *Callback;
+ LScriptContext *UserContext = NULL;
+ LCompiledCode *Code = NULL;
+ LVmCallback *Callback = NULL;
LVariant ReturnValue;
-
- LScriptEnginePrivate()
- {
- UserContext = NULL;
- Parent = NULL;
- Code = NULL;
- Callback = NULL;
- }
};
LScriptEngine::LScriptEngine(LViewI *parent, LScriptContext *UserContext, LVmCallback *Callback)
{
d = new LScriptEnginePrivate;
d->Parent = parent;
d->UserContext = UserContext;
d->Callback = Callback;
d->SysContext.SetEngine(this);
}
LScriptEngine::~LScriptEngine()
{
DeleteObj(d);
}
LCompiledCode *LScriptEngine::GetCurrentCode()
{
return d->Code;
}
bool LScriptEngine::Compile(LAutoPtr &Obj, LScriptContext *UserContext, const char *Script, const char *FileName, LDom *Args)
{
if (!Script)
{
LAssert(!"Param error");
return NULL;
}
LCompiler Comp;
return Comp.Compile(Obj,
&d->SysContext,
UserContext ? UserContext : d->UserContext,
FileName,
Script,
Args);
}
LExecutionStatus LScriptEngine::Run(LCompiledCode *Obj, LVariant *Ret, const char *TempPath)
{
LExecutionStatus Status = ScriptError;
d->Code = Obj;
if (d->Code)
{
LVirtualMachine Vm(d->Callback);
if (TempPath)
Vm.SetTempPath(TempPath);
Status = Vm.Execute(d->Code, 0, NULL, true, Ret ? Ret : &d->ReturnValue);
d->Code = NULL;
}
return Status;
}
LExecutionStatus LScriptEngine::RunTemporary(LCompiledCode *Obj, char *Script, LVariant *Ret)
{
LExecutionStatus Status = ScriptError;
LCompiledCode *Code = dynamic_cast(Obj);
if (Script && Code)
{
LAutoPtr Temp(new LCompiledCode(*Code));
uint32_t TempLen = (uint32_t) Temp->Length();
d->Code = Temp;
LCompiler Comp;
if (Comp.Compile(Temp, &d->SysContext, d->UserContext, Temp->GetFileName(), Script, NULL))
{
LVirtualMachine Vm(d->Callback);
Status = Vm.Execute(dynamic_cast(Temp.Get()), TempLen, NULL, true, Ret ? Ret : &d->ReturnValue);
}
d->Code = NULL;
}
return Status;
}
bool LScriptEngine::EvaluateExpression(LVariant *Result, LDom *VariableSource, const char *Expression)
{
if (!Result || !VariableSource || !Expression)
{
LAssert(!"Param error");
return false;
}
// Create trivial script to evaluate the expression
LString a;
a.Printf("return %s;", Expression);
// Compile the script
LCompiler Comp;
LAutoPtr Obj;
if (!Comp.Compile(Obj, &d->SysContext, d->UserContext, NULL, a, VariableSource))
{
LAssert(0);
return false;
}
// Execute the script
LVirtualMachine Vm(d->Callback);
LCompiledCode *Code = dynamic_cast(Obj.Get());
auto ReturnVal = Result ? Result : &d->ReturnValue;
LExecutionStatus s = Vm.Execute(Code, 0, NULL, true, ReturnVal);
if (s != ScriptSuccess)
{
return false;
}
if (ReturnVal->Type == GV_INT64 &&
!(ReturnVal->Value.Int64 >> 32))
{
*ReturnVal = ReturnVal->CastInt32();
}
return true;
}
LStream *LScriptEngine::GetConsole()
{
if (d->SysContext.GetLog())
return d->SysContext.GetLog();
if (d->UserContext && d->UserContext->GetLog())
return d->UserContext->GetLog();
return NULL;
}
bool LScriptEngine::SetConsole(LStream *t)
{
d->SysContext.SetLog(t);
if (d->UserContext)
d->UserContext->SetLog(t);
return true;
}
bool LScriptEngine::CallMethod(LCompiledCode *Obj, const char *Method, LScriptArguments &Args)
{
LCompiledCode *Code = dynamic_cast(Obj);
if (!Code || !Method)
return false;
LFunctionInfo *i = Code->GetMethod(Method);
if (!i)
return false;
LVirtualMachine Vm(d->Callback);
Args.Vm = &Vm;
LExecutionStatus Status = Vm.ExecuteFunction(Code, i, Args, NULL);
Args.Vm = NULL;
return Status != ScriptError;
}
LScriptContext *LScriptEngine::GetSystemContext()
{
return &d->SysContext;
}
diff --git a/src/common/Coding/ScriptLibrary.cpp b/src/common/Coding/ScriptLibrary.cpp
--- a/src/common/Coding/ScriptLibrary.cpp
+++ b/src/common/Coding/ScriptLibrary.cpp
@@ -1,1262 +1,1262 @@
#include
#include
#include "lgi/common/Lgi.h"
#include "lgi/common/Scripting.h"
#include "lgi/common/SubProcess.h"
#include "lgi/common/LgiRes.h"
#include "lgi/common/FileSelect.h"
#include "ScriptingPriv.h"
//////////////////////////////////////////////////////////////////////////////////////
char16 sChar[] = L"char";
char16 sInt[] = { 'i','n','t', 0 };
char16 sUInt[] = { 'u','i','n','t', 0 };
char16 sInt32[] = { 'i','n','t','3','2', 0 };
char16 sUInt32[] = { 'u','i','n','t','3','2', 0 };
char16 sInt64[] = { 'i','n','t','6','4', 0 };
char16 sHWND[] = { 'H','W','N','D', 0 };
char16 sDWORD[] = { 'D','W','O','R','D', 0 };
char16 sLPTSTR[] = { 's','L','P','T','S','T','R', 0 };
char16 sLPCTSTR[] = { 's','L','P','C','T','S','T','R', 0 };
char16 sElse[] = { 'e','l','s','e', 0 };
char16 sIf[] = { 'i','f',0 };
char16 sFunction[] = { 'f','u','n','c','t','i','o','n',0 };
char16 sExtern[] = { 'e','x','t','e','r','n',0 };
char16 sFor[] = { 'f','o','r',0 };
char16 sWhile[] = { 'w','h','i','l','e',0 };
char16 sReturn[] = { 'r','e','t','u','r','n',0 };
char16 sInclude[] = { 'i','n','c','l','u','d','e',0 };
char16 sDefine[] = { 'd','e','f','i','n','e',0 };
char16 sStruct[] = { 's','t','r','u','c','t',0 };
char16 sTrue[] = { 't','r','u','e',0 };
char16 sFalse[] = { 'f','a','l','s','e',0 };
char16 sNull[] = { 'n','u','l','l',0 };
char16 sOutParam[] = { '_','o','u','t','_',0};
char16 sHash[] = { '#', 0 };
char16 sPeriod[] = { '.', 0 };
char16 sComma[] = { ',', 0 };
char16 sSemiColon[] = { ';', 0 };
char16 sStartRdBracket[] = { '(', 0 };
char16 sEndRdBracket[] = { ')', 0 };
char16 sStartSqBracket[] = { '[', 0 };
char16 sEndSqBracket[] = { ']', 0 };
char16 sStartCurlyBracket[] = { '{', 0 };
char16 sEndCurlyBracket[] = { '}', 0 };
//////////////////////////////////////////////////////////////////////////////////////
LExecutionStatus LHostFunc::Call(LScriptContext *Ctx, LScriptArguments &Args)
{
return (Ctx->*(Func))(Args) ? ScriptSuccess : ScriptError;
}
-const char *InstToString(GInstruction i)
+const char *InstToString(LInstruction i)
{
#undef _i
#define _i(name, opcode, desc) \
case name: return desc;
switch (i)
{
AllInstructions
}
return "#err";
}
//////////////////////////////////////////////////////////////////////////////////////
int LScriptUtils::atoi(char16 *s)
{
int i = 0;
if (s)
{
char b[64];
ssize_t Len = StrlenW(s) * sizeof(*s);
ssize_t Bytes = LBufConvertCp(b, "utf-8", sizeof(b), (const void*&)s, LGI_WideCharset, Len);
b[Bytes/sizeof(*b)] = 0;
i = ::atoi(b);
}
return i;
}
int64 LScriptUtils::atoi64(char16 *s)
{
int64 i = 0;
if (s)
{
#ifdef _MSC_VER
i = _wtoi64(s);
#else
char b[64];
ssize_t Len = StrlenW(s) * sizeof(*s);
ssize_t Bytes = LBufConvertCp(b, "utf-8", sizeof(b), (const void*&)s, LGI_WideCharset, Len);
b[Bytes/sizeof(*b)] = 0;
i = strtoll(b, 0, 10);
#endif
}
return i;
}
double LScriptUtils::atof(char16 *s)
{
double i = 0;
if (s)
{
char b[64];
ssize_t Len = StrlenW(s) * sizeof(*s);
ssize_t Bytes = LBufConvertCp(b, "utf-8", sizeof(b), (const void*&)s, LGI_WideCharset, Len);
b[Bytes/sizeof(*b)] = 0;
i = ::atof(b);
}
return i;
}
int LScriptUtils::htoi(char16 *s)
{
int i = 0;
if (s)
{
char b[64];
ssize_t Len = StrlenW(s) * sizeof(*s);
ssize_t Bytes = LBufConvertCp(b, "utf-8", sizeof(b), (const void*&)s, LGI_WideCharset, Len);
b[Bytes/sizeof(*b)] = 0;
i = ::htoi(b);
}
return i;
}
//////////////////////////////////////////////////////////////////////////////////////
SystemFunctions::SystemFunctions()
{
Engine = NULL;
Log = NULL;
#ifdef WINNATIVE
Brk = NULL;
#endif
}
SystemFunctions::~SystemFunctions()
{
}
LStream *SystemFunctions::GetLog()
{
return Log;
}
bool SystemFunctions::SetLog(LStream *log)
{
LAssert(Log == NULL);
Log = log;
return true;
}
void SystemFunctions::SetEngine(LScriptEngine *Eng)
{
Engine = Eng;
}
bool SystemFunctions::Assert(LScriptArguments &Args)
{
*Args.GetReturn() = true;
if (Args.Length() == 0)
return true;
auto v = Args[0]->CastInt32();
if (!v)
{
const char *Msg = Args.Length() > 1 ? Args[1]->CastString() : NULL;
*Args.GetReturn() = false;
Args.Throw(NULL, -1, Msg);
}
return true;
}
bool SystemFunctions::DebuggerEnabled(LScriptArguments &Args)
{
if (Args.Length() == 0)
{
LAssert(!"Wrong args.");
return false;
}
Args.GetVm()->SetDebuggerEnabled(Args[0]->CastInt32() != 0);
return true;
}
bool SystemFunctions::Throw(LScriptArguments &Args)
{
const char *Msg = Args.Length() > 0 ? Args[0]->CastString() : NULL;
Args.Throw(NULL, -1, Msg);
return true;
}
bool SystemFunctions::LoadString(LScriptArguments &Args)
{
if (Args.Length() != 1)
{
LAssert(!"Wrong args.");
return false;
}
*Args.GetReturn() = LLoadString(Args[0]->CastInt32());
return true;
}
bool SystemFunctions::Sprintf(LScriptArguments &Args)
{
if (Args.Length() < 1)
{
LAssert(!"Wrong args.");
return false;
}
char *Fmt = Args[0]->Str();
if (!Fmt)
return false;
#if defined(LINUX) || defined(MAC) || defined(HAIKU)
// No support for sprintf with generated args... hack a string up
// Formatting widths etc not supported.
LArray s;
int i = 1;
for (char *f = Fmt; *f; f++)
{
if (f[0] == '%' && f[1] != '%')
{
f++; // Skip '%'
// char *Fmt = f;
while (*f && !IsAlpha(*f))
f++; // Skip formatting..
if (i >= Args.Length())
break; // No more arguments...
switch (*f)
{
case 's':
{
// String...
char *v = Args[i]->CastString();
if (v)
s.Add(v, strlen(v));
else
s.Add((char*)"(null)", 4);
break;
}
case 'c':
{
char *Str = Args[i]->Str();
s.Add(Str ? *Str : '?');
break;
}
case 'f':
case 'g':
{
break;
}
case 'u':
case 'd':
case 'i':
{
// Int...
LString v;
v.Printf("%i", Args[i]->CastInt32());
s.Add(v.Get(), v.Length());
break;
}
}
i++;
}
else s.Add(*f);
}
s.Add(0); // NULL terminate
*Args.GetReturn() = s.AddressOf();
#else
LArray Params;
va_list a;
unsigned i = 1;
for (char *f = Fmt; *f; f++)
{
if (f[0] == '%' && f[1] != '%')
{
char *t = f + 1;
while (*t && !IsAlpha(*t))
t++;
if (i >= Args.Length())
{
LAssert(!"Not enough args.");
break;
}
switch (*t)
{
case 's':
{
Params.Add((UNativeInt)Args[i++]->Str());
break;
}
case 'c':
{
char *Str = Args[i++]->Str();
Params.Add(Str ? *Str : '?');
break;
}
case 'f':
case 'g':
{
union tmp
{
double Dbl;
struct
{
uint32_t High;
uint32_t Low;
};
} Tmp;
Tmp.Dbl = Args[i++]->CastDouble();
Params.Add(Tmp.High);
Params.Add(Tmp.Low);
break;
}
default:
{
Params.Add(Args[i++]->CastInt32());
break;
}
}
f = *t ? t + 1 : t;
}
}
a = (va_list) &Params[0];
#ifndef WIN32
#define _vsnprintf vsnprintf
#endif
char Buf[1024];
vsprintf_s(Buf, sizeof(Buf), Fmt, a);
*Args.GetReturn() = Buf;
#endif
return true;
}
bool SystemFunctions::ReadTextFile(LScriptArguments &Args)
{
if (Args.Length() == 1 &&
LFileExists(Args[0]->CastString()))
{
LFile f(Args[0]->CastString(), O_READ);
if (!f)
return false;
auto sz = f.GetSize();
if (sz < 0)
return false;
char *txt = new char[sz+1];
if (!txt)
return false;
auto rd = f.Read(txt, sz);
if (rd >= 0)
{
txt[rd] = 0;
Args.GetReturn()->OwnStr(txt);
}
delete [] txt;
return rd == sz;
}
return false;
}
bool SystemFunctions::WriteTextFile(LScriptArguments &Args)
{
if (Args.Length() == 2)
{
LFile f;
if (f.Open(Args[0]->CastString(), O_WRITE))
{
f.SetSize(0);
LVariant *v = Args[1];
if (v)
{
switch (v->Type)
{
default: break;
case GV_STRING:
{
size_t Len = strlen(v->Value.String);
*Args.GetReturn() = f.Write(v->Value.String, Len) == Len;
return true;
break;
}
case GV_BINARY:
{
*Args.GetReturn() = f.Write(v->Value.Binary.Data, v->Value.Binary.Length) == v->Value.Binary.Length;
return true;
break;
}
}
}
}
}
return false;
}
LView *SystemFunctions::CastLView(LVariant &v)
{
switch (v.Type)
{
default: break;
case GV_DOM:
return dynamic_cast(v.Value.Dom);
case GV_LVIEW:
return v.Value.View;
}
return 0;
}
bool SystemFunctions::SelectFiles(LScriptArguments &Args)
{
Args.GetReturn()->Empty();
if (Args.Length() < 2)
{
Args.Throw(_FL, "SelectFiles(Parent, Callback[, FileTypes[, InitialDir[, MultiSelect[, SaveAs]]]]) expects at least 2 arguments.");
return false;
}
auto Vm = dynamic_cast(Args.GetVm());
if (!Vm)
return false;
auto Ctx = Vm->SaveContext();
if (!Ctx)
{
Args.Throw(_FL, "SelectFiles(...) requires a valid callback context.");
return false;
}
LFileSelect *s = new LFileSelect;
if (!s)
return false;
s->Parent(CastLView(*Args[0]));
auto Callback = Args[1]->Str();
auto Types = LString(Args.IdxCheck(2) ? Args[2]->CastString() : NULL).SplitDelimit(",;:");
for (auto c: Types)
{
char *sp = strrchr(c, ' ');
if (sp)
{
*sp++ = 0;
s->Type(sp, c);
}
else
{
char *dot = strrchr(c, '.');
if (dot)
{
char Type[256];
sprintf_s(Type, sizeof(Type), "%s files", dot + 1);
s->Type(Type, c);
}
}
}
s->Type("All Files", LGI_ALL_FILES);
s->InitialDir (Args.IdxCheck(3) ? Args[3]->CastString() : 0);
s->MultiSelect(Args.IdxCheck(4) ? Args[4]->CastInt32() != 0 : true);
bool SaveAs = Args.IdxCheck(5) ? Args[5]->CastInt32() != 0 : false;
auto Process = [Callback=LString(Callback),Ctx](LFileSelect *s, bool ok)
{
if (ok)
{
LScriptArguments Args(NULL);
LVariant *v = new LVariant;
if (auto Lst = v->SetList())
{
for (unsigned i=0; iLength(); i++)
{
auto path = (*s)[i];
Lst->Insert(new LVariant(path));
}
}
Args.Add(v);
Ctx.Call(Callback, Args);
Args.DeleteObjects();
}
delete s;
};
if (SaveAs)
s->Save(Process);
else
s->Open(Process);
return true;
}
bool SystemFunctions::SelectFolder(LScriptArguments &Args)
{
Args.GetReturn()->Empty();
if (Args.Length() < 2)
{
Args.Throw(_FL, "SelectFolder(Parent, Callback[, InitialDir]) expects at least 2 arguments.");
return false;
}
auto Vm = dynamic_cast(Args.GetVm());
if (!Vm)
return false;
auto Ctx = Vm->SaveContext();
if (!Ctx)
{
Args.Throw(_FL, "SelectFiles(...) requires a valid callback context.");
return false;
}
LFileSelect *s = new LFileSelect;
if (!s)
return false;
s->Parent(CastLView(*Args[0]));
auto Callback = Args[1]->Str();
if (Args.IdxCheck(2))
s->InitialDir(Args[2]->CastString());
s->OpenFolder([Ctx, Callback = LString(Callback)](auto s, bool ok)
{
if (ok)
{
LScriptArguments Args(NULL);
Args.Add(new LVariant(s->Name()));
Ctx.Call(Callback, Args);
Args.DeleteObjects();
}
delete s;
});
return true;
}
bool SystemFunctions::Sleep(LScriptArguments &Args)
{
if (Args.Length() != 1)
{
LAssert(!"Wrong args.");
return false;
}
LSleep(Args[0]->CastInt32());
return true;
}
bool SystemFunctions::ToString(LScriptArguments &Args)
{
LStringPipe p;
const char *Sep = ", ";
for (unsigned i=0; iToString();
p.Print("%s%s", i?Sep:"", s.Get());
}
Args.GetReturn()->OwnStr(p.NewStr());
return true;
}
bool SystemFunctions::Lgi4CC(LScriptArguments &Args)
{
auto s = Args.StringAt(0);
if (!s)
return false;
auto i = ::Lgi4CC(s);
*Args.GetReturn() = i;
return true;
}
bool SystemFunctions::Print(LScriptArguments &Args)
{
LStream *Out = Log ? Log : (Engine ? Engine->GetConsole() : NULL);
for (unsigned n=0; Out && nPrint("%s", v.ToString().Get());
break;
}
default:
{
auto f = v.CastString();
if (f)
{
size_t Len = strlen(f);
Out->Write(f, Len);
}
else
{
Out->Write("NULL", 4);
}
break;
}
}
}
return true;
}
bool SystemFunctions::FormatSize(LScriptArguments &Args)
{
if (Args.Length() != 1)
return false;
char s[64];
LFormatSize(s, sizeof(s), Args[0]->CastInt64());
*Args.GetReturn() = s;
return true;
}
bool SystemFunctions::ClockTick(LScriptArguments &Args)
{
*Args.GetReturn() = (int64)LCurrentTime();
return true;
}
bool SystemFunctions::Now(LScriptArguments &Args)
{
Args.GetReturn()->Empty();
Args.GetReturn()->Type = GV_DATETIME;
Args.GetReturn()->Value.Date = new LDateTime;
Args.GetReturn()->Value.Date->SetNow();
return true;
}
bool SystemFunctions::New(LScriptArguments &Args)
{
if (Args.Length() < 1 || !Args[0])
{
LAssert(!"Wrong args.");
return false;
}
Args.GetReturn()->Empty();
char *sType = Args[0]->CastString();
if (!sType)
return false;
if (IsDigit(*sType))
{
// Binary block
int Bytes = ::atoi(sType);
if (!Bytes)
return false;
return Args.GetReturn()->SetBinary(Bytes, new char[Bytes], true);
}
LVariant *Ret = Args.GetReturn();
LDomProperty Type = LStringToDomProp(sType);
switch (Type)
{
case TypeList:
{
Ret->SetList();
break;
}
case TypeHashTable:
{
Ret->SetHashTable();
break;
}
case TypeSurface:
{
Ret->Empty();
Ret->Type = GV_LSURFACE;
if ((Ret->Value.Surface.Ptr = new LMemDC))
{
Ret->Value.Surface.Ptr->IncRef();
Ret->Value.Surface.Own = true;
}
break;
}
case TypeFile:
{
Ret->Empty();
#if 1
Ret->Type = GV_STREAM;
Ret->Value.Stream.Ptr = new LFile;
if (Ret->Value.Stream.Ptr)
Ret->Value.Stream.Own = true;
#else
Ret->Type = GV_GFILE;
if ((Ret->Value.File.Ptr = new LFile))
{
Ret->Value.File.Ptr->AddRef();
Ret->Value.File.Own = true;
}
#endif
break;
}
case TypeDateTime:
{
Ret->Empty();
Ret->Type = GV_DATETIME;
Ret->Value.Date = new LDateTime;
break;
}
default:
{
Ret->Empty();
LCompiledCode *c = Engine ? Engine->GetCurrentCode() : NULL;
if (!c)
return false;
LAutoWString o(Utf8ToWide(sType));
LCustomType *t = c->GetType(o);
if (t)
{
int ArrayLength = Args.Length() > 1 ? Args[1]->CastInt32() : 1;
if (ArrayLength > 0)
{
Ret->Type = GV_CUSTOM;
Ret->Value.Custom.Dom = t;
Ret->Value.Custom.Data = new uint8_t[t->Sizeof() * ArrayLength];
}
}
}
}
return true;
}
bool SystemFunctions::Len(LScriptArguments &Args)
{
size_t i = 0;
for (LVariant *v: Args)
{
switch (v->Type)
{
case GV_LIST:
i += v->Value.Lst->Length();
break;
case GV_HASHTABLE:
i += v->Value.Hash->Length();
break;
case GV_BINARY:
i += v->Value.Binary.Length;
break;
case GV_STRING:
i += Strlen(v->Value.String);
break;
case GV_WSTRING:
i += Strlen(v->Value.WString);
break;
default:
i += 1;
break;
}
}
*Args.GetReturn() = i;
return true;
}
bool SystemFunctions::Delete(LScriptArguments &Args)
{
if (Args.Length() != 1)
{
LAssert(!"Wrong args.");
return false;
}
LVariant *v = Args[0];
if (v->Type == GV_CUSTOM)
{
DeleteArray(v->Value.Custom.Data);
v->Empty();
}
else
{
v->Empty();
}
*Args.GetReturn() = true;
return true;
}
class LFileListEntry : public LDom
{
bool Folder;
LVariant Name;
int64 Size;
LDateTime Modified;
public:
LFileListEntry(LDirectory *d)
{
Folder = d->IsDir();
Name = d->GetName();
Size = d->GetSize();
Modified.Set(d->GetLastWriteTime());
}
const char *GetClass() override { return "LFileListEntry"; }
bool GetVariant(const char *Var, LVariant &Value, const char *Arr = NULL) override
{
LDomProperty p = LStringToDomProp(Var);
switch (p)
{
case ObjName:
Value = Name;
break;
case ObjLength:
Value = Size;
break;
case FileFolder:
Value = Folder;
break;
case FileModified:
Value = &Modified;
break;
default:
return false;
}
return true;
}
};
bool SystemFunctions::DeleteFile(LScriptArguments &Args)
{
if (Args.Length() != 1)
{
LAssert(!"Wrong args.");
return false;
}
char *f = Args[0]->CastString();
if (f)
*Args.GetReturn() = FileDev->Delete(Args[0]->CastString());
else
*Args.GetReturn() = false;
return true;
}
bool SystemFunctions::CurrentScript(LScriptArguments &Args)
{
LCompiledCode *Code;
if (Engine &&
(Code = Engine->GetCurrentCode()))
{
*Args.GetReturn() = Code->GetFileName();
return true;
}
return false;
}
bool SystemFunctions::PathExists(LScriptArguments &Args)
{
if (Args.Length() == 0)
{
LAssert(!"Wrong args.");
return false;
}
LDirectory d;
if (d.First(Args[0]->CastString(), NULL))
{
if (d.IsDir())
*Args.GetReturn() = 2;
else
*Args.GetReturn() = 1;
}
else
{
*Args.GetReturn() = 0;
}
return true;
}
bool SystemFunctions::PathJoin(LScriptArguments &Args)
{
char p[MAX_PATH_LEN] = "";
for (unsigned i=0; iCastString();
if (i)
LMakePath(p, sizeof(p), p, s);
else
strcpy_s(p, sizeof(p), s);
}
if (*p)
*Args.GetReturn() = p;
else
Args.GetReturn()->Empty();
return true;
}
bool SystemFunctions::PathSep(LScriptArguments &Args)
{
*Args.GetReturn() = DIR_STR;
return true;
}
bool SystemFunctions::ListFiles(LScriptArguments &Args)
{
if (Args.Length() < 1)
{
Args.GetReturn()->Empty();
Args.Throw(_FL, "ListFiles(FolderPath[, FilterPattern]) expects at least one argument.");
return false;
}
Args.GetReturn()->SetList();
auto Folder = Args[0]->CastString();
auto Pattern = Args.Length() > 1 ? Args[1]->CastString() : NULL;
LDirectory d;
for (auto b=d.First(Folder); b; b=d.Next())
{
if (!Pattern || MatchStr(Pattern, d.GetName()))
Args.GetReturn()->Value.Lst->Insert(new LVariant(new LFileListEntry(&d)));
}
return true;
}
bool SystemFunctions::CreateSurface(LScriptArguments &Args)
{
Args.GetReturn()->Empty();
if (Args.Length() < 2)
{
Args.Throw(_FL, "CreateSurface(x, y[, Bits|ColourSpace]) expects at least two arguments.");
return false;
}
auto x = Args[0]->CastInt32();
auto y = Args[1]->CastInt32();
LColourSpace Cs = CsNone;
if (Args.Length() > 2)
{
LVariant *Type = Args[2];
const char *c;
if (Type->IsInt())
{
// Bit depth... convert to default Colour Space.
Cs = LBitsToColourSpace(Type->CastInt32());
}
else if ((c = Type->Str()))
{
// Parse string colour space def
Cs = LStringToColourSpace(Type->Str());
}
}
if (!Cs) // Catch all error cases and make it the default screen depth.
Cs = GdcD->GetColourSpace();
auto r = Args.GetReturn();
if ((r->Value.Surface.Ptr = new LMemDC(x, y, Cs)))
{
r->Type = GV_LSURFACE;
r->Value.Surface.Own = true;
r->Value.Surface.Ptr->IncRef();
}
return true;
}
bool SystemFunctions::ColourSpaceToString(LScriptArguments &Args)
{
Args.GetReturn()->Empty();
if (Args.Length() != 1)
{
Args.Throw(_FL, "ColourSpaceToString(ColourSpaceInt) expects at least one argument.");
return false;
}
*Args.GetReturn() = LColourSpaceToString((LColourSpace)Args[0]->CastInt32());
return true;
}
bool SystemFunctions::StringToColourSpace(LScriptArguments &Args)
{
Args.GetReturn()->Empty();
if (Args.Length() != 1)
{
Args.Throw(_FL, "StringToColourSpace(ColourSpaceStr) expects at least one argument.");
return false;
}
*Args.GetReturn() = (int)LStringToColourSpace(Args[0]->Str());
return true;
}
bool SystemFunctions::MessageDlg(LScriptArguments &Args)
{
if (Args.Length() < 2)
{
Args.Throw(_FL, "MessageDlg(Parent, Message[, Title[, Buttons]]) expects at least 2 arguments.");
return false;
}
auto Parent = CastLView(*Args[0]);
auto Msg = Args[1]->Str();
auto Title = Args.IdxCheck(2) ? Args[2]->Str() : LAppInst->Name();
auto Btns = Args.IdxCheck(3) ? Args[3]->CastInt32() : MB_OK;
auto Btn = LgiMsg(Parent, Msg, Title, Btns);
*Args.GetReturn() = Btn;
return true;
}
bool SystemFunctions::GetInputDlg(LScriptArguments &Args)
{
if (Args.Length() < 5)
{
Args.Throw(_FL, "GetInputDlg(Parent, DefaultInput, Message, Title, IsPassword, Callback) expects 5 arguments.");
return false;
}
auto Vm = dynamic_cast(Args.GetVm());
if (!Vm)
return false;
auto Ctx = Vm->SaveContext();
if (!Ctx)
{
Args.Throw(_FL, "GetInputDlg requires a valid VM context.");
return false;
}
auto Parent = CastLView(*Args[0]);
auto InitVal = Args[1]->Str();
auto Msg = Args[2]->Str();
auto Title = Args[3]->Str();
auto Pass = Args[4]->CastInt32() != 0;
auto Callback = Args[5]->Str();
Args.GetReturn()->Empty();
auto Dlg = new LInput(Parent, InitVal, Msg, Title, Pass);
Dlg->DoModal([this, Dlg, Ctx, Callback=LString(Callback)](auto d, auto ok)
{
if (ok)
{
LScriptArguments Args(NULL);
Args.Add(new LVariant(Dlg->GetStr()));
Ctx.Call(Callback, Args);
Args.DeleteObjects();
}
delete Dlg;
});
// Don't wait here...
return true;
}
bool SystemFunctions::GetViewById(LScriptArguments &Args)
{
Args.GetReturn()->Empty();
if (Args.Length() < 2)
{
LAssert(!"Wrong args.");
return false;
}
LViewI *Parent = CastLView(*Args[0]);
int Id = Args[1]->CastInt32();
if (!Parent || Id <= 0)
return false;
if (Parent->GetViewById(Id, Args.GetReturn()->Value.View))
{
Args.GetReturn()->Type = GV_LVIEW;
}
return true;
}
bool SystemFunctions::Execute(LScriptArguments &Args)
{
if (Args.Length() < 2)
{
LAssert(!"Wrong args.");
return false;
}
LStringPipe p;
char *Exe = Args[0]->CastString();
char *Arguments = Args[1]->CastString();
LSubProcess e(Exe, Arguments);
bool Status = e.Start();
if (Status)
{
e.Communicate(&p);
LAutoString o(p.NewStr());
*Args.GetReturn() = o;
}
else if (Log)
{
uint32_t ErrCode = e.GetErrorCode();
LString ErrMsg = LErrorCodeToString(ErrCode);
if (ErrMsg)
Log->Print("Error: Execute(\"%s\",\"%s\") failed with '%s'\n", Exe, Arguments, ErrMsg.Get());
else
Log->Print("Error: Execute(\"%s\",\"%s\") failed with '0x%x'\n", Exe, Arguments, ErrCode);
}
return Status;
}
bool SystemFunctions::System(LScriptArguments &Args)
{
if (Args.Length() < 2)
{
LAssert(!"Wrong args.");
return false;
}
char *Exe = Args[0]->Str();
char *Arg = Args[1]->Str();
*Args.GetReturn() = LExecute(Exe, Arg);
return true;
}
bool SystemFunctions::OsName(LScriptArguments &Args)
{
*Args.GetReturn() = LGetOsName();
return true;
}
bool SystemFunctions::OsVersion(LScriptArguments &Args)
{
LArray Ver;
LGetOs(&Ver);
Args.GetReturn()->SetList();
for (int i=0; i<3; i++)
Args.GetReturn()->Value.Lst->Insert(new LVariant(Ver[i]));
return true;
}
#define DefFn(Name) \
LHostFunc(#Name, 0, (ScriptCmd)&SystemFunctions::Name)
LHostFunc SystemLibrary[] =
{
// Debug
DefFn(Assert),
DefFn(Throw),
DefFn(DebuggerEnabled),
// String handling
DefFn(LoadString),
DefFn(FormatSize),
DefFn(Sprintf),
DefFn(Print),
DefFn(ToString),
DefFn(Lgi4CC),
// Containers/objects
DefFn(New),
DefFn(Delete),
DefFn(Len),
// Files
DefFn(ReadTextFile),
DefFn(WriteTextFile),
DefFn(SelectFiles),
DefFn(SelectFolder),
DefFn(ListFiles),
DefFn(DeleteFile),
DefFn(CurrentScript),
DefFn(PathJoin),
DefFn(PathExists),
DefFn(PathSep),
// Time
DefFn(ClockTick),
DefFn(Sleep),
DefFn(Now),
// Images
DefFn(CreateSurface),
DefFn(ColourSpaceToString),
DefFn(StringToColourSpace),
// UI
DefFn(MessageDlg),
DefFn(GetInputDlg),
DefFn(GetViewById),
// System
DefFn(Execute),
DefFn(System),
DefFn(OsName),
DefFn(OsVersion),
// End of list marker
LHostFunc(0, 0, 0),
};
LHostFunc *SystemFunctions::GetCommands()
{
return SystemLibrary;
}
diff --git a/src/common/Coding/ScriptVM.cpp b/src/common/Coding/ScriptVM.cpp
--- a/src/common/Coding/ScriptVM.cpp
+++ b/src/common/Coding/ScriptVM.cpp
@@ -1,2160 +1,2167 @@
#include
#include "lgi/common/Lgi.h"
#include "lgi/common/Scripting.h"
#include "lgi/common/Box.h"
#include "lgi/common/TabView.h"
#include "lgi/common/TextLog.h"
#include "lgi/common/List.h"
#include "lgi/common/ToolBar.h"
#include "lgi/common/TableLayout.h"
#include "lgi/common/TextLabel.h"
#include "lgi/common/ScrollBar.h"
#include "lgi/common/Matrix.h"
#include "lgi/common/Menu.h"
#include "ScriptingPriv.h"
#define TIME_INSTRUCTIONS 0
#define POST_EXECUTE_STATE 0
// #define BREAK_POINT 0x0000009F
#define ExitScriptExecution c.u8 = e
#define SetScriptError c.u8 = e; Status = ScriptError
#define CurrentScriptAddress (c.u8 - Base)
#define CheckParam(ptr) if (!(ptr)) \
{ \
OnException(_FL, CurrentScriptAddress-1, #ptr); \
c.u8 = e; \
Status = ScriptError; \
break; \
}
#define AddLocalSize(NewSize) \
size_t LocalsBase = Locals.Length(); \
Locals.SetFixedLength(false); \
/* LgiTrace("%s:%i - Locals %i -> %i\n", _FL, LocalsBase, LocalsBase + NewSize); */ \
Locals.Length(LocalsBase + NewSize); \
Scope[SCOPE_LOCAL] = &Locals[LocalsBase]; \
Locals.SetFixedLength();
#ifdef WIN32
extern "C" uint64 __cdecl CallExtern64(void *FuncAddr, NativeInt *Ret, uint32_t Args, void *Arg);
#elif defined(LINUX)
#include
#endif
int LVariantCmp(LVariant *a, LVariant *b, NativeInt Data)
{
LVariant *Param = (LVariant*) Data;
if (!a || !b)
return 0;
if (a->Type == GV_STRING &&
b->Type == GV_STRING)
{
const char *Empty = "";
const char *as = a->Str();
const char *bs = b->Str();
return _stricmp(as?as:Empty, bs?bs:Empty);
}
else if (a->Type == GV_DOM &&
b->Type == GV_DOM &&
Param)
{
const char *Fld = Param->Str();
int Dir = 1;
if (Fld && *Fld == '-')
{
Fld++;
Dir = -1;
}
LVariant av, bv;
if (a->Value.Dom->GetValue(Fld, av) &&
b->Value.Dom->GetValue(Fld, bv))
{
return LVariantCmp(&av, &bv, 0) * Dir;
}
}
else if (a->Type == GV_INT32 &&
b->Type == GV_INT32)
{
return a->CastInt32() - b->CastInt32();
}
else if (a->Type == GV_DATETIME &&
b->Type == GV_DATETIME)
{
return a->Value.Date->Compare(b->Value.Date);
}
else
{
LAssert(!"Impl a handler for this type.");
}
return 0;
}
inline LVariantType DecidePrecision(LVariantType a, LVariantType b)
{
if (a == GV_DOUBLE || b == GV_DOUBLE)
return GV_DOUBLE;
if (a == GV_INT64 || b == GV_INT64)
return GV_INT64;
return GV_INT32;
}
inline LVariantType ComparePrecision(LVariantType a, LVariantType b)
{
if (a == GV_NULL || b == GV_NULL)
return GV_NULL;
if (a == GV_DATETIME && b == GV_DATETIME)
return GV_DATETIME;
if (a == GV_DOUBLE || b == GV_DOUBLE)
return GV_DOUBLE;
if (a == GV_STRING || b == GV_STRING)
return GV_STRING;
if (a == GV_INT64 || b == GV_INT64)
return GV_INT64;
return GV_INT32;
}
inline char *CastString(LVariant *v, LVariant &cache)
{
if (v->Type == GV_STRING)
return v->Str();
cache = *v;
return cache.CastString();
}
inline int CompareVariants(LVariant *a, LVariant *b)
{
// Calculates "a - b"
switch (ComparePrecision(a->Type, b->Type))
{
case GV_DATETIME:
return a->Value.Date->Compare(b->Value.Date);
break;
case GV_DOUBLE:
{
double d = a->CastDouble() - b->CastDouble();
if (d < -MATRIX_DOUBLE_EPSILON)
return -1;
return d > MATRIX_DOUBLE_EPSILON;
}
case GV_STRING:
{
LVariant as, bs;
char *A = CastString(a, as);
char *B = CastString(b, bs);
if (!A || !B)
return -1;
else
return strcmp(A, B);
break;
}
case GV_INT64:
{
int64 d = a->CastInt64() - b->CastInt64();
if (d < 0)
return -1;
return d > 0;
}
case GV_NULL:
{
// One or more values is NULL
if (a->IsNull() && b->IsNull())
return 0; // The same..
LVariant *Val = a->IsNull() ? b : a;
if (Val->IsNull())
{
LAssert(0);
return 0;
}
switch (Val->Type)
{
case GV_INT32:
case GV_INT64:
return Val->CastInt64() != 0;
case GV_STRING:
return Val->Str() != NULL;
default:
return Val->CastVoidPtr() != NULL;
}
break;
}
default:
return a->CastInt32() - b->CastInt32();
break;
}
}
LExecutionStatus LExternFunc::Call(LScriptContext *Ctx, LScriptArguments &Args)
{
if (!Lib || !Method)
return ScriptError;
LStream *Log = Ctx ? Ctx->GetLog() : NULL;
if (Args.Length() != ArgType.Length())
{
if (Log)
Log->Print("Error: Extern '%s.%s' expecting %i arguments, not %i given.\n",
Lib.Get(), Method.Get(),
ArgType.Length(),
Args.Length());
return ScriptError;
}
LArray Val;
LArray Mem;
bool UnsupportedArg = false;
Val.Length(Args.Length() << 1);
LPointer Ptr;
Ptr.ni = &Val[0];
for (unsigned i=0; !UnsupportedArg && iCastVoidPtr();
*Ptr.vp++ = cp;
}
else
{
char *s = NewStr(v->CastString());
if (!s)
{
UnsupportedArg = true;
break;
}
Mem.Add(s);
*Ptr.vp++ = s;
}
break;
}
case GV_VOID_PTR:
{
*Ptr.vp++ = v->CastVoidPtr();
break;
}
default:
{
UnsupportedArg = true;
break;
}
}
}
else
{
// Plain type
switch (t.Base)
{
case GV_INT32:
{
#if defined(_WIN64)
*Ptr.s64++ = v->CastInt32();
#else
*Ptr.s32++ = v->CastInt32();
#endif
break;
}
case GV_INT64:
{
*Ptr.s64++ = v->CastInt64();
break;
}
default:
{
UnsupportedArg = true;
break;
}
}
}
}
LLibrary Library(Lib);
if (!Library.IsLoaded())
{
if (Log)
Log->Print("Error: Extern library '%s' missing.\n", Lib.Get());
return ScriptError;
}
void *c = Library.GetAddress(Method);
if (!c)
{
if (Log)
Log->Print("Error: Extern library '%s' has no method '%s'.\n", Lib.Get(), Method.Get());
return ScriptError;
}
#if defined(_MSC_VER) || (defined(MAC) && defined(LGI_32BIT) && !LGI_COCOA)
ssize_t a = Ptr.ni - &Val[0];
#endif
NativeInt r = 0;
#if defined(_MSC_VER)
#if defined(_WIN64)
// 64bit... boooo no inline asm!
void *b = &Val[0];
r = CallExtern64(c, &r, (uint32_t)a, b);
#else
// 32bit... yay inline asm!
void *b = Ptr.ni - 1;
_asm
{
mov ecx, a
mov ebx, b
}
label1:
_asm
{
push [ebx]
sub ebx, 4
loop label1
mov ebx, c
call ebx
mov r, eax
}
#endif
#elif defined(MAC)
#if LGI_COCOA
#warning FIXME
#elif LGI_32BIT
// 32bit only
void *b = Ptr.ni - 1;
asm ( "movl %2, %%ecx;"
"movl %3, %%ebx;"
"label1:"
"pushl (%%ebx);"
"subl %%ebx, 4;"
"loop label1;"
"call *%1;"
:"=a"(r) /* output */
:"r"(c), "r"(a), "r"(b) /* input */
:/*"%eax",*/ "%ecx", "%ebx" /* clobbered register */
);
#endif
#else
// Not implemented, gcc???
LAssert(0);
#endif
*Args.GetReturn() = (int) r;
for (unsigned i=0; iType == GV_BINARY &&
v->Value.Binary.Data != NULL &&
t.Base == GV_STRING)
{
// Cast the type back to t.Base
char *p = (char*)v->Value.Binary.Data;
v->Type = t.Base;
v->Value.String = p;
}
}
}
Mem.DeleteArrays();
return ScriptSuccess;
}
struct CodeBlock
{
unsigned SrcLine;
LArray AsmAddr;
unsigned ViewLine;
LAutoString Source;
int SrcLines;
LAutoString Asm;
int AsmLines;
};
class LVirtualMachinePriv : public LRefCount
{
LVariant ArrayTemp;
char *CastArrayIndex(LVariant *Idx)
{
if (Idx == NULL || Idx->Type == GV_NULL)
return NULL;
if (Idx->Type == GV_STRING)
return Idx->Str();
ArrayTemp = *Idx;
return ArrayTemp.CastString();
}
public:
struct StackFrame
{
uint32_t CurrentFrameSize;
ssize_t PrevFrameStart;
size_t ReturnIp;
LVarRef ReturnValue;
};
enum RunType
{
RunContinue,
RunStepInstruction,
RunStepLine,
RunStepOut
};
LVirtualMachine *Vm;
LStream *Log = NULL;
LCompiledCode *Code = NULL;
LExecutionStatus Status = ScriptNotStarted;
LScriptPtr c;
LVariant Reg[MAX_REGISTER];
LArray Locals;
LVariant *Scope[SCOPE_MAX];
LArray Frames;
RunType StepType;
LVmCallback *Callback = NULL;
LVmDebugger *Debugger = NULL;
LScriptArguments *ArgsOutput = NULL;
LArray BreakPts;
LString TempPath;
bool DebuggerEnabled = false;
bool BreakCpp = false;
LVirtualMachinePriv(LVirtualMachine *vm, LVmCallback *callback)
{
Vm = vm;
Callback = callback;
ZeroObj(Scope);
}
~LVirtualMachinePriv()
{
}
void DumpVariant(LStream *Log, LVariant &v)
{
if (!Log)
return;
switch (v.Type)
{
case GV_INT32:
Log->Print("(int) %i", v.Value.Int);
break;
case GV_INT64:
Log->Print("(int64) %I64i", v.Value.Int64);
break;
case GV_STRING:
{
char *nl = strchr(v.Value.String, '\n');
if (nl)
Log->Print("(string) '%.*s...' (%i bytes)", nl - v.Value.String, v.Value.String, strlen(v.Value.String));
else
Log->Print("(string) '%s'", v.Value.String);
break;
}
case GV_DOUBLE:
Log->Print("(double) %g", v.Value.Dbl);
break;
case GV_BOOL:
Log->Print("(bool) %s", v.Value.Bool ? "true" : "false");
break;
case GV_DOM:
Log->Print("(LDom*) %p", v.Value.Dom);
break;
case GV_HASHTABLE:
{
Log->Print("(GHashTable*) %p {", v.Value.Hash);
int n = 0;
// const char *k;
// for (LVariant *p = v.Value.Hash->First(&k); p; p = v.Value.Hash->Next(&k), n++)
for (auto it : *v.Value.Hash)
{
Log->Print("%s\"%s\"=", n?",":"", it.key);
DumpVariant(Log, *it.value);
}
Log->Print("}");
break;
}
case GV_LIST:
{
Log->Print("(LList*) %p {", v.Value.Lst);
int n=0;
for (auto i: *v.Value.Lst)
{
Log->Print("%s%i=", n?",":"", n);
DumpVariant(Log, *i);
n++;
}
Log->Print("}");
break;
}
case GV_NULL:
{
Log->Print("null");
break;
}
case GV_BINARY:
{
Log->Print("(Binary[%i])", v.Value.Binary.Length);
if (v.Value.Binary.Data)
{
int i;
for (i=0; i<16 && i < v.Value.Binary.Length; i++)
Log->Print(" %.2x", ((uint8_t*)v.Value.Binary.Data)[i]);
if (i < v.Value.Binary.Length)
Log->Print("...");
}
break;
}
default:
Log->Print("(Type-%i) ????", v.Type);
break;
}
}
void DumpVariables(LVariant *v, int len)
{
if (!Log)
return;
for (int i=0; iPrint("[%i] = ", i);
DumpVariant(Log, v[i]);
Log->Print("\n");
}
}
}
void OnException(const char *File, int Line, ssize_t Address, const char *Msg)
{
if (!Code)
{
LgiTrace("%s:%i - Exception without Code object: %s:%i, %s\n",
_FL,
File, Line,
Msg);
return;
}
+ static bool InException = false;
+ if (InException)
+ return;
+
+ InException = true;
if (Address < 0)
{
uint8_t *Base = &Code->ByteCode[0];
Address = c.u8 - Base;
}
if (!File || Line < 0)
{
// Extract the file / line from the current script location
File = Code->GetFileName();
Line = Code->ObjectToSourceAddress(Address);
}
if (Log)
{
char *Last = strrchr((char*)File, DIR_CHAR);
Log->Print("%s Exception: %s (%s:%i)\n", Code->AddrToSourceRef(Address), Msg, Last?Last+1:File, Line);
}
else
{
LgiTrace("%s:%i - Exception @ %i: %s\n", File, Line, Address, Msg);
}
LStringPipe AsmBuf;
Decompile(Code->UserContext, Code, &AsmBuf);
auto Asm = AsmBuf.NewLStr();
if (Vm && Vm->OpenDebugger(Code, Asm))
{
LAssert(Debugger->GetCode()); // It should have taken a copy of the Code we passed in
Debugger->OnAddress(Address);
LString m;
m.Printf("%s (%s:%i)", Msg, LGetLeaf(File), Line);
Debugger->OnError(m);
Debugger->Run();
}
else
{
// Set the script return value to FALSE
if (Frames.Length())
{
StackFrame Sf = Frames[0];
LVarRef &Ret = Sf.ReturnValue;
LVariant *RetVar = &Scope[Ret.Scope][Ret.Index];
*RetVar = false;
}
// Exit the script
c.u8 = Code->ByteCode.AddressOf() + Code->ByteCode.Length();
// Set the script status...
Status = ScriptError;
}
+
+ InException = false;
}
LExecutionStatus Decompile(LScriptContext *Context, LCompiledCode *Code, LStream *log)
{
LExecutionStatus Status = ScriptSuccess;
LAssert(sizeof(LVarRef) == 4);
LScriptPtr c;
uint8_t *Base = &Code->ByteCode[0];
c.u8 = Base;
uint8_t *e = c.u8 + Code->ByteCode.Length();
LStream *OldLog = Log;
if (log)
Log = log;
for (unsigned k=0; kGlobals.Length(); k++)
{
Log->Print("G%i = ", k);
DumpVariant(Log, Code->Globals[k]);
Log->Print("\n");
}
Log->Print("\n");
LHashTbl, char*> Fn;
for (unsigned m=0; mMethods.Length(); m++)
{
LFunctionInfo *Info = Code->Methods[m];
if (Info->ValidStartAddr())
Fn.Add(Info->GetStartAddr(), Info->GetName());
else
LAssert(!"Method not defined.");
}
int OldLineNum = 0;
while (c.u8 < e)
{
char *Meth = Fn.Find(CurrentScriptAddress);
if (Meth)
{
Log->Print("%s:\n", Meth);
}
int LineNum = Code->ObjectToSourceAddress(CurrentScriptAddress);
if (LineNum >= 0 && LineNum != OldLineNum)
{
Log->Print(" %i:\n", OldLineNum = LineNum);
}
switch (*c.u8++)
{
#define VM_DECOMP 1
#include "Instructions.h"
#undef VM_DECOMP
}
}
if (log)
Log = OldLog;
return Status;
}
LExecutionStatus Setup(LCompiledCode *code, uint32_t StartOffset, LStream *log, LFunctionInfo *Func, LScriptArguments *Args)
{
Status = ScriptSuccess;
Code = code;
if (!Code)
return ScriptError;
if (log)
Log = log;
else if (Code->SysContext && Code->SysContext->GetLog())
Log = Code->SysContext->GetLog();
else if (Code->UserContext && Code->UserContext->GetLog())
Log = Code->UserContext->GetLog();
// else LgiTrace("%s:%i - Execution without a log?\n", _FL);
LAssert(sizeof(LVarRef) == 4);
uint8_t *Base = c.u8 = &Code->ByteCode[0];
uint8_t *e = c.u8 + Code->ByteCode.Length();
Scope[SCOPE_REGISTER] = Reg;
Scope[SCOPE_LOCAL] = NULL;
Scope[SCOPE_GLOBAL] = &Code->Globals[0];
Scope[SCOPE_OBJECT] = NULL;
Scope[SCOPE_RETURN] = Args->GetReturn();
#ifdef _DEBUG
const char *SourceFileName = Code->GetFileName();
char Obj[MAX_PATH_LEN];
if (SourceFileName)
{
if (strchr(SourceFileName, DIR_CHAR))
strcpy_s(Obj, sizeof(Obj), SourceFileName);
else if (TempPath != NULL)
LMakePath(Obj, sizeof(Obj), TempPath, SourceFileName);
else
{
LGetSystemPath(LSP_TEMP, Obj, sizeof(Obj));
LMakePath(Obj, sizeof(Obj), Obj, SourceFileName);
}
char *Ext = LGetExtension(Obj);
if (Ext)
strcpy_s(Ext, sizeof(Obj)-(Ext-Obj), "asm");
else
strcat_s(Obj, sizeof(Obj), ".asm");
}
else
{
LAutoString DataPath;
if (Code->UserContext)
DataPath = Code->UserContext->GetDataFolder();
if (!DataPath)
{
char p[256];
if (LGetSystemPath(LSP_APP_INSTALL, p, sizeof(p)))
DataPath.Reset(NewStr(p));
}
LMakePath(Obj, sizeof(Obj), DataPath, "Script.asm");
}
{
LDirectory SrcD, ObjD;
bool OutOfDate = true;
if (LFileExists(SourceFileName) &&
SrcD.First(SourceFileName, NULL) != 0 &&
ObjD.First(Obj, NULL) != 0)
{
OutOfDate = ObjD.GetLastWriteTime() < SrcD.GetLastWriteTime();
}
if (OutOfDate || Debugger)
{
LFile f;
LStringPipe p;
LStream *Out = NULL;
if (Debugger)
{
Out = &p;
}
else if (f.Open(Obj, O_WRITE))
{
f.SetSize(0);
Out = &f;
}
if (Out)
{
LExecutionStatus Decomp = Decompile(Code->UserContext, Code, Out);
f.Close();
if (Decomp != ScriptSuccess)
{
LAssert(!"Decompilation failed.");
return ScriptError;
}
if (Debugger)
{
auto a = p.NewLStr();
Debugger->OnAddress(CurrentScriptAddress);
Debugger->SetSource(a);
}
}
}
}
#endif
#if TIME_INSTRUCTIONS
LARGE_INTEGER freq = {0}, start, end;
QueryPerformanceFrequency(&freq);
LHashTbl, int64> Timings;
LHashTbl, int> TimingFreq;
#endif
// Calling a function only, not the whole script
StackFrame &Sf = Frames.New();
Sf.ReturnIp = e - c.u8;
Sf.PrevFrameStart = 0;
Sf.ReturnValue.Scope = SCOPE_RETURN;
Sf.ReturnValue.Index = 0; // array is only one item long anyway
if (Func)
{
// Set up stack for function call
if (!Func->ValidFrameSize())
{
Log->Print("%s:%i - Function '%s' has an invalid frame size. (Script: %s).\n",
_FL, Func->GetName(), Code->AddrToSourceRef(Func->GetStartAddr()));
return ScriptError;
}
Sf.CurrentFrameSize = Func->GetFrameSize();
AddLocalSize(Sf.CurrentFrameSize);
if (Args)
{
// Check the local frame size is at least big enough for the args...
if (Args->Length() > Sf.CurrentFrameSize)
{
Log->Print("%s:%i - Arg count mismatch, Supplied: %i, FrameSize: %i (Script: %s).\n",
_FL, (int)Args->Length(), (int)Sf.CurrentFrameSize,
Code->AddrToSourceRef(Func->GetStartAddr()));
return ScriptError;
}
// Put the arguments of the function call into the local array
for (unsigned i=0; iLength(); i++)
{
Locals[LocalsBase+i] = *(*Args)[i];
}
}
if (!Func->ValidStartAddr())
{
Log->Print("%s:%i - Function '%s' is not defined. (Script: %s).\n",
_FL, Func->GetName(), Code->AddrToSourceRef(Func->GetStartAddr()));
return ScriptError;
}
// Set IP to start of function
c.u8 = Base + Func->GetStartAddr();
}
else
{
// Executing body of script
Sf.CurrentFrameSize = 0;
if (StartOffset > 0)
c.u8 = Base + StartOffset;
}
return Status;
}
int NearestLine(size_t Addr)
{
int l = Code->Debug.Find(Addr);
if (l >= 0)
return l;
for (int Off = 1; Off < 20; Off++)
{
int l = Code->Debug.Find(Addr + Off);
if (l >= 0)
{
return l;
}
l = Code->Debug.Find(Addr - Off);
if (l >= 0)
{
return l;
}
}
return -1;
}
LExecutionStatus Run(RunType Type)
{
LAssert(Code != NULL);
uint8_t *Base = &Code->ByteCode[0];
uint8_t *e = Base + Code->ByteCode.Length();
#define ON_EXCEPTION(args) \
args.Length(0); \
args.Address = -1; \
if (args.HasException()) \
{ \
Status = ScriptError; \
goto ExitExecutionLoop; \
}
if (Type == RunContinue && BreakPts.Length() == 0)
{
// Unconstrained execution
while (c.u8 < e)
{
#if TIME_INSTRUCTIONS
uint8 TimedOpCode = *c.u8;
QueryPerformanceCounter(&start);
#endif
switch (*c.u8++)
{
#define VM_EXECUTE 1
#include "Instructions.h"
#undef VM_EXECUTE
}
#if TIME_INSTRUCTIONS
QueryPerformanceCounter(&end);
int Ticks = end.QuadPart - start.QuadPart;
int64 i = Timings.Find(TimedOpCode);
Timings.Add(TimedOpCode, i + Ticks);
i = TimingFreq.Find(TimedOpCode);
TimingFreq.Add(TimedOpCode, i + 1);
#endif
}
if (Log)
{
#if TIME_INSTRUCTIONS
Log->Print("\nTimings:\n");
Log->Print("%-20s%-10s%-10s%-10s\n", "Instr", "Total", "Freq", "Ave");
int Op;
for (int64 t=Timings.First(&Op); t; t=Timings.Next(&Op))
{
int Frq = TimingFreq.Find(Op);
int MilliSec = t * 1000000 / freq.QuadPart;
Log->Print("%-20s%-10i%-10i%-10i\n", InstToString((GInstruction)Op), MilliSec, Frq, MilliSec / Frq);
}
Log->Print("\n");
#endif
#if POST_EXECUTE_STATE
Log->Print("Stack:\n");
char *v;
for (void *i=Code->Globals.Lut.First(&v); i; i=Code->Globals.Lut.Next(&v))
{
int Idx = (int)i - 1;
if (Idx >= 0 && Idx < Code->Globals.Length())
{
Log->Print("%s = ", v);
DumpVariant(Log, Code->Globals[Idx]);
Log->Print("\n");
}
}
Log->Print("\nRegisters:\n");
DumpVariables(Reg, MAX_REGISTER);
#endif
}
}
else
{
// Stepping through code
int Param = 0;
switch (Type)
{
case RunStepLine:
Param = NearestLine(CurrentScriptAddress);
break;
case RunStepOut:
Param = (int)Frames.Length();
break;
default:
break;
}
if (BreakCpp)
#if defined(WIN32) && !defined(_WIN64)
_asm int 3
#else
assert(!"BreakPoint");
#endif
while (c.u8 < e)
{
if (Type == RunContinue &&
BreakPts.HasItem(c.u8 - Base))
break;
switch (*c.u8++)
{
#define VM_EXECUTE 1
#include "Instructions.h"
#undef VM_EXECUTE
}
if (Type == RunContinue)
continue;
if (Type == RunStepLine)
{
int CurLine = NearestLine(CurrentScriptAddress);
if (CurLine && CurLine != Param)
break;
}
else if (Type == RunStepOut)
{
if ((int)Frames.Length() < Param)
break;
}
else if (Type == RunStepInstruction)
{
break;
}
else LAssert(!"Invalid Type.");
}
}
ExitExecutionLoop:
if (Debugger && Status != ScriptError)
Debugger->OnAddress(CurrentScriptAddress);
return Status;
}
};
bool LVirtualMachine::BreakOnWarning = false;
LVirtualMachine::LVirtualMachine(LVmCallback *callback)
{
d = new LVirtualMachinePriv(this, callback);
d->IncRef();
}
LVirtualMachine::LVirtualMachine(Context ctx)
{
d = new LVirtualMachinePriv(this, ctx.Callback);
d->IncRef();
if ((d->Code = ctx.Code))
{
if (d->Code->ByteCode.IdxCheck(ctx.Addr))
d->c.u8 = d->Code->ByteCode.AddressOf() + ctx.Addr;
}
else
d->c.u8 = NULL;
}
LVirtualMachine::LVirtualMachine(LVirtualMachine *vm)
{
d = vm->d;
d->IncRef();
}
LVirtualMachine::~LVirtualMachine()
{
if (d->Vm == this)
d->Vm = NULL;
d->DecRef();
}
void LVirtualMachine::OnException(const char *File, int Line, ssize_t Address, const char *Msg)
{
d->OnException(File, Line, Address, Msg);
}
LExecutionStatus LVirtualMachine::Execute(LCompiledCode *Code, uint32_t StartOffset, LStream *Log, bool StartImmediately, LVariant *Return)
{
if (!Code)
return ScriptError;
LScriptArguments Args(this, Return);
LExecutionStatus s = d->Setup(Code, StartOffset, Log, NULL, &Args);
if (s != ScriptSuccess || !StartImmediately)
return s;
return d->Run(LVirtualMachinePriv::RunContinue);
}
LExecutionStatus LVirtualMachine::ExecuteFunction(LCompiledCode *Code, LFunctionInfo *Func, LScriptArguments &Args, LStream *Log, LScriptArguments *ArgsOut)
{
LCompiledCode *Cc = dynamic_cast(Code);
if (!Cc || !Func)
return ScriptError;
LExecutionStatus s = d->Setup(Cc, 0, Log, Func, &Args);
if (s != ScriptSuccess)
return s;
d->ArgsOutput = ArgsOut;
auto Prev = Args.Vm;
Args.Vm = this;
LExecutionStatus r = d->Run(LVirtualMachinePriv::RunContinue);
Args.Vm = Prev;
return r;
}
void LVirtualMachine::SetDebuggerEnabled(bool b)
{
d->DebuggerEnabled = b;
}
LVmDebugger *LVirtualMachine::OpenDebugger(LCompiledCode *Code, const char *Assembly)
{
if (d->DebuggerEnabled)
{
if (!d->Callback)
return NULL;
d->Debugger = d->Callback->AttachVm(this, Code, Assembly);
}
return d->Debugger;
}
bool LVirtualMachine::StepInstruction()
{
LExecutionStatus s = d->Run(LVirtualMachinePriv::RunStepInstruction);
return s != ScriptError;
}
bool LVirtualMachine::StepLine()
{
LExecutionStatus s = d->Run(LVirtualMachinePriv::RunStepLine);
return s != ScriptError;
}
bool LVirtualMachine::StepOut()
{
LExecutionStatus s = d->Run(LVirtualMachinePriv::RunStepOut);
return s != ScriptError;
}
bool LVirtualMachine::BreakExecution()
{
return false;
}
bool LVirtualMachine::Continue()
{
LExecutionStatus s = d->Run(LVirtualMachinePriv::RunContinue);
return s != ScriptError;
}
bool LVirtualMachine::BreakPoint(const char *File, int Line, bool Add)
{
return false;
}
bool LVirtualMachine::BreakPoint(int Addr, bool Add)
{
if (Add)
d->BreakPts.Add(Addr);
else
d->BreakPts.Delete(Addr);
return true;
}
void LVirtualMachine::SetBreakCpp(bool Brk)
{
d->BreakCpp = Brk;
}
LVirtualMachine::Context LVirtualMachine::SaveContext()
{
LVirtualMachine::Context ctx;
ctx.Callback = d->Callback;
if ((ctx.Code = d->Code) &&
ctx.Code->ByteCode.PtrCheck(d->c.u8))
{
ctx.Addr = d->c.u8 - ctx.Code->ByteCode.AddressOf();
}
return ctx;
}
LVmCallback *LVirtualMachine::GetCallback()
{
return d->Callback;
}
void LVirtualMachine::SetTempPath(const char *Path)
{
d->TempPath = Path;
}
////////////////////////////////////////////////////////////////////
/*
bool GTypeDef::GetVariant(const char *Name, LVariant &Value, char *Arr)
{
GMember *m = Members.Find(Name);
if (!m || !Object)
{
LAssert(!"No member?");
return false;
}
GPtr p;
p.i8 = Object;
p.i8 += m->Offset;
switch (m->Type)
{
case GV_INT32:
{
Value = *p.i32;
break;
}
case GV_DOUBLE:
{
Value = *p.dbl;
break;
}
case GV_STRING:
{
Value = p.i8;
break;
}
case GV_CUSTOM:
{
Value.Empty();
Value.Type = GV_CUSTOM;
Value.Value.Custom.Dom = m->Nest;
Value.Value.Custom.Data = p.i8;
break;
}
default:
{
return false;
}
}
return true;
}
bool GTypeDef::SetVariant(const char *Name, LVariant &Value, char *Arr)
{
GMember *m = Members.Find(Name);
if (!m || !Object)
{
LAssert(!"No member?");
return false;
}
GPtr p;
p.i8 = Object;
p.i8 += m->Offset;
switch (m->Type)
{
case GV_INT32:
{
*p.i32 = Value.CastInt32();
break;
}
case GV_DOUBLE:
{
*p.dbl = Value.CastDouble();
break;
}
case GV_STRING:
{
char *s = Value.CastString();
if (!s)
return false;
int i;
for (i = 0; *s && i < m->Size - 1; i++)
{
*p.i8++ = *s++;
}
if (i < m->Size - 1)
*p.i8 = 0;
break;
}
case GV_CUSTOM:
{
GTypeDef *t = dynamic_cast(Value.Value.Custom.Dom);
if (m->Nest == t)
{
memcpy(p.i8, Value.Value.Custom.Data, t->Sizeof());
}
break;
}
default:
{
return false;
}
}
return true;
}
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
uint32_t IconsData[] =
{
0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x9D9CCEBE, 0x3B166419, 0x74594357, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x543CF81F, 0xCEDE647C, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F,
0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x7C998D1B, 0xF81FB61C, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xCEBFF81F, 0x43DB4C1C, 0xDF1E955B, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F,
0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x8C0CF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x8D5CF81F, 0x43595C1A, 0x3AF74338, 0x8CFA4B57, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xC69D6C39, 0xF81FF81F, 0xF81FF81F, 0x0000F81F, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0x647BADFD, 0x543C53FB, 0x3B1553FB, 0x329132B2, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81F64CB, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F,
0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x8D9FF81F, 0x8D9F8D9F, 0x855E857E, 0x7CFD7D1D, 0x74BC74DC, 0xF81F74DC, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x8BEBF81F, 0xF81F83AB, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x7CBB8D5C, 0xF81FB63D, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81F5BD8, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x959D647C, 0xCEBDF81F, 0x32913AD3, 0xB61B5353, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x3BA564CB, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x8D9FF81F, 0x74DC8D9F,
0x8D9FF81F, 0x74DC8D9F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x8D9F8D9F, 0x855E857E, 0x7CFD7D1D, 0x74BC74DC, 0xF81F74DC, 0xF81FF81F, 0xF81FF81F, 0x8D9FF81F, 0xAE1EB65E, 0xD71FA5FE, 0x853D8D7D, 0x6CBD7CFD, 0xF81F2AB5, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x8BEBF81F, 0x7329FF98, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xCE9E5C1A, 0xF81FF81F, 0xF81FF81F, 0x0000F81F, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0xF81F5398, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x00000000, 0x00000000, 0xF81F0000, 0xF81F6C7C, 0x5BD6F81F, 0xD6DD5BB5, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xB6D45CAA, 0xF81F3BA5, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x8D9FF81F, 0x2AB5D71F, 0x8D9FF81F, 0x2AB5D71F, 0xF81FF81F,
0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xD71F8D9F, 0xC6DFD71F, 0xB65FBE7F, 0x9DDEAE3F, 0xF81F2AB5, 0xF81FF81F, 0xF81FF81F, 0x857EF81F, 0x9DDEAE1E, 0xFFFFDF3F, 0x74FD853D, 0x647C6CBC, 0xF81F2274, 0xF81FF81F, 0xF81FF81F, 0x9C6CF81F, 0x944D944C, 0x944D8C0C, 0xFF54FF76, 0xF81F62A8, 0xF81FF81F, 0xF81FF81F, 0xBE5D4B99, 0x543CF81F, 0xC6BE5C7C, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81F5398, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81F53DB, 0xF81FF81F, 0xF81FF81F, 0x0000F81F, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x75ED5489, 0x3BA5B6D4, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x857EF81F, 0x2274DF3F, 0x857EF81F, 0x2274DF3F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F,
0xDF3F857E, 0xB65FCEDF, 0x9DBEAE1F, 0x851B959E, 0xF81F2274, 0xF81FF81F, 0xF81FF81F, 0x855EF81F, 0xE75F9DDE, 0xFFFFFFFF, 0x6CBC74DD, 0x543C647C, 0xF81F1A33, 0xF81FF81F, 0xF81FF81F, 0x944BF81F, 0xFFD9F756, 0xFF53FF97, 0xFEEEFF31, 0x5226F66A, 0xF81FF81F, 0xF81FF81F, 0x84DA6C3A, 0xBE7EADDC, 0x43DB4C1C, 0xF81F953B, 0xF81FF81F, 0x00000000, 0x00000000, 0xF81F0000, 0xF81F4B77, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x00000000, 0x00000000, 0xF81F0000, 0x9D7C5C3B, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x75ED4C48, 0x5D0A75ED, 0xF81F3BA5, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x855EF81F, 0x1A33D71F, 0x855EF81F, 0x1A33D71F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xD71F855E, 0xA5FFBE7F, 0x855E959E,
0x6C7A7CFD, 0xF81F1A33, 0xF81FF81F, 0xF81FF81F, 0x7D1DF81F, 0xFFFFDF1E, 0xFFFFFFFF, 0xFFFFFFFF, 0x4BFCC69D, 0xF81F11F2, 0xF81FF81F, 0xF81FF81F, 0x940AF81F, 0xFF75FF97, 0xFEEEFF31, 0xF6ABFECD, 0xBCA7E5E8, 0xF81F49C5, 0xF81FF81F, 0x7CBAB61C, 0x541C53FA, 0x3B1553FB, 0x329132B2, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81F4B57, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x6C9CB63D, 0x43584BBA, 0x3AD53B16, 0x743742F4, 0xF81FF81F, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x6D8B4407, 0x3C055D0A, 0x1B212B84, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x7D1DF81F, 0x11F2CEBF, 0x7D1DF81F, 0x11F2CEBF, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xCEBF7D1D, 0x9DBEAE3F, 0x7CFD8D5E, 0x53B76CBC, 0xF81F11F2, 0xF81FF81F,
0xF81FF81F, 0x7CFDF81F, 0xCEBD853D, 0xFFFFFFFF, 0x541C5C5C, 0x43BBFFFF, 0xF81F09B1, 0xF81FF81F, 0xF81FF81F, 0x83A9F81F, 0xFF31FF74, 0xF6ACF6CF, 0xDDEAF68B, 0x41A4B467, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xC69DF81F, 0x32913AD3, 0xB5FB4B53, 0xF81FF81F, 0x00000000, 0x00000000, 0xF81F0000, 0xF81F4336, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x5D0A33E5, 0x2B843C05, 0xF81F0B00, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x7CFDF81F, 0x09B1BE9F, 0x7CFDF81F, 0x09B1BE9F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xBE9F7CFD, 0x959EA5FF, 0x6C9C7CFD, 0x4B565C3B, 0xF81F09B1, 0xF81FF81F, 0xF81FF81F, 0x74DCF81F, 0x6CBC74FD,
0xFFFFBE3B, 0x43DC541C, 0x337BFFFF, 0xF81F0990, 0xF81FF81F, 0xF81FF81F, 0x8389F81F, 0x6AA472E7, 0x72C56264, 0xB487DDA9, 0xF81F41A4, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x5BD6F81F, 0xF81F5BB5, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81F4336, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x3C052BA4, 0x0B002B84, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x74DCF81F, 0x0990AE5F, 0x74DCF81F, 0x0990AE5F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xAE5F74DC, 0x7D1D95BE, 0x5C3B6CBC, 0x43354BD9, 0xF81F0990, 0xF81FF81F, 0xF81FF81F, 0x74BCF81F, 0x647C6CBC, 0x9D7A543C, 0x3B9B43DB, 0x2B5BFFFF,
0xF81F0170, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x5A45F81F, 0x41A4AC26, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x00000000, 0x00000000, 0xF81F0000, 0xF81F5377, 0xF81FF81F, 0xF81FF81F, 0x0000F81F, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x2B842363, 0xF81F0B00, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x74BCF81F, 0x0170A5FE, 0x74BCF81F, 0x0170A5FE, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xA5FE74BC, 0x6C79851A, 0x4B555BF8, 0x32D34314, 0xF81F0170, 0xF81FF81F, 0xF81FF81F, 0x74DCF81F, 0x543C5C7C, 0x43BB4BFC, 0x335B3B9B, 0x231BFFFF, 0xF81F0170, 0xF81FF81F, 0xF81FF81F,
0xF81FF81F, 0xF81FF81F, 0x49E4F81F, 0xF81F41A4, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x9D7A53B7, 0x4BBAF81F, 0xB61C6459, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x0B001B42, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x74DCF81F, 0x01700170, 0x74DCF81F, 0x01700170, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x3B1674DC, 0x2A9432F6, 0x11F22253, 0x017009B1, 0xF81F0170, 0xF81FF81F, 0xF81FF81F, 0x74DCF81F, 0x3B163B16, 0x2A9432F6, 0x11F22253, 0x017009B1, 0xF81F0170, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x41A4F81F,
0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x0000F81F, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0x747863F7, 0x953BB61C, 0x4BB843B9, 0xB61B7C98, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81F1301, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F,
0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x6C18ADBA, 0x53D953B7, 0x3B144B98, 0x32503291, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x0000F81F, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F,
0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xB61BF81F, 0x32503291, 0xA5794B12, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F,
0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x5B95F81F, 0xB61A5373, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F,
};
LInlineBmp DbgIcons = {128, 16, 16, IconsData};
enum DbgCtrls
{
IDC_STATIC = -1,
IDC_TABS = 300,
IDC_BOX,
IDC_BOX2,
IDC_TEXT,
IDC_LOCALS,
IDC_GLOBALS,
IDC_REGISTERS,
IDC_STACK,
IDC_LOG,
IDC_RUN,
IDC_PAUSE,
IDC_STOP,
IDC_RESTART,
IDC_GOTO,
IDC_STEP_INSTR,
IDC_STEP_LINE,
IDC_STEP_OUT,
IDC_SOURCE_LST,
IDC_BREAK_POINT,
IDC_BREAK_CPP,
IDC_VARS_TBL
};
struct LScriptVmDebuggerPriv;
class LDebugView : public LTextView3
{
LScriptVmDebuggerPriv *d;
int CurLine;
int ErrorLine;
LString Error;
LArray BreakPts;
public:
LDebugView(LScriptVmDebuggerPriv *priv);
~LDebugView();
void SetError(const char *Err);
int GetCurLine() { return CurLine; }
int GetAddr();
void ScrollToCurLine();
void PourText(size_t Start, ssize_t Length) override;
void OnPaintLeftMargin(LSurface *pDC, LRect &r, LColour &colour) override;
void OnPaint(LSurface *pDC) override;
bool Breakpoint(int Addr);
};
struct LScriptVmDebuggerPriv
{
// Current script
LAutoPtr Vm;
LAutoPtr Obj;
LVmCallback *Callback = NULL;
LString Script, Assembly;
LArray Blocks;
ssize_t CurrentAddr = -1;
LArray LineIsAsm;
LVariant Return;
bool AcceptNotify = false;
// Ui
LView *Parent = NULL;
LBox *Main = NULL;
LBox *Sub = NULL;
LList *SourceLst = NULL;
LTabView *Tabs = NULL;
LDebugView *Text = NULL;
LList *Locals = NULL, *Globals = NULL, *Registers = NULL, *Stack = NULL;
LTextLog *Log = NULL;
LToolBar *Tools = NULL;
LTableLayout *VarsTbl = NULL;
};
LDebugView::LDebugView(LScriptVmDebuggerPriv *priv) : LTextView3(IDC_TEXT, 0, 0, 100, 100)
{
d = priv;
ErrorLine = -1;
SetWrapType(L_WRAP_NONE);
GetCss(true)->PaddingLeft(LCss::Len(LCss::LenPx, 18));
}
LDebugView::~LDebugView()
{
}
void LDebugView::SetError(const char *Err)
{
ErrorLine = CurLine;
Error = Err;
}
#define IsHexChar(c) \
( \
IsDigit(c) \
|| \
((c) >= 'a' && (c) <= 'f') \
|| \
((c) >= 'A' && (c) <= 'F') \
)
int IsAddr(char16 *Ln)
{
int Addr = 0;
for (char16 *s = Ln; *s && *s != '\n' && s < Ln + 8; s++)
{
Addr += IsHexChar(*s);
}
if (Addr != 8)
return -1;
return HtoiW(Ln);
}
int LDebugView::GetAddr()
{
ssize_t Index;
LTextLine *t = GetTextLine(Cursor, &Index);
if (!t)
return -1;
int Addr = IsAddr(Text + t->Start);
return Addr;
}
void LDebugView::ScrollToCurLine()
{
SetLine(CurLine);
}
bool LDebugView::Breakpoint(int Addr)
{
if (BreakPts.HasItem(Addr))
{
BreakPts.Delete(Addr);
Invalidate();
return false;
}
else
{
BreakPts.Add(Addr);
Invalidate();
return true;
}
}
void LDebugView::OnPaintLeftMargin(LSurface *pDC, LRect &r, LColour &colour)
{
LTextView3::OnPaintLeftMargin(pDC, r, colour);
pDC->Colour(LColour(192, 0, 0));
LFont *f = GetFont();
f->Colour(L_LOW, L_WORKSPACE);
f->Transparent(true);
int Fy = f->GetHeight();
int Start = VScroll ? (int)VScroll->Value() : 0;
int Page = (r.Y() + Fy - 1) / Fy;
int Ln = Start;
int Rad = (Fy >> 1) - 1;
int PadY = GetCss(true)->PaddingTop().ToPx(Y(), f) + ((Fy - Rad) >> 1);
auto It = Line.begin(Start);
for (auto i = *It; i && Ln <= Start + Page; i = *(++It), Ln++)
{
int OffY = (Ln - Start) * f->GetHeight();
/*
LString Num;
Num.Printf("%i", Ln);
LDisplayString Ds(f, Num);
Ds.Draw(pDC, 0, r.y1+OffY);
*/
char16 *s = Text + i->Start;
int Addr = IsAddr(s);
if (BreakPts.HasItem(Addr))
{
pDC->FilledCircle(r.x1 + Rad + 2, OffY + PadY + Rad, Rad);
}
}
f->Transparent(false);
f->Colour(L_TEXT, L_WORKSPACE);
}
void LDebugView::OnPaint(LSurface *pDC)
{
LTextView3::OnPaint(pDC);
if (Error)
{
LTextLine *Ln = Line[ErrorLine];
LFont *f = GetFont();
LRect c = GetClient();
int Pad = 3;
LDisplayString Ds(f, Error);
LRect r(0, 0, Ds.X()-1, Ds.Y()-1);
r.Inset(-Pad, -Pad);
r.Offset(c.X()-r.X(), Ln ? Ln->r.y1 - ScrollYPixel(): 0);
f->Transparent(false);
f->Colour(LColour::White, LColour::Red);
Ds.Draw(pDC, r.x1 + Pad, r.y1 + Pad, &r);
}
}
void LDebugView::PourText(size_t Start, ssize_t Len)
{
LTextView3::PourText(Start, Len);
CurLine = -1;
for (unsigned i=0; iBlocks.Length(); i++)
{
CodeBlock &b = d->Blocks[i];
for (unsigned n=0; nCurrentAddr=%i b.AsmAddr[%i,%i]=%u\n", (int)d->CurrentAddr, i, n, b.AsmAddr[n]);
if (d->CurrentAddr >= b.AsmAddr[n])
{
LgiTrace("b.ViewLine=%i b.SrcLines=%i n=%i\n", b.ViewLine, b.SrcLines, n);
CurLine = b.ViewLine + b.SrcLines + n - 1;
}
}
}
unsigned Idx = 0;
for (auto l: Line)
{
// char16 *t = Text + l->Start;
// char16 *e = t + l->Len;
if (CurLine == Idx)
{
l->c.Rgb(0, 0, 0);
l->Back = LColour(L_DEBUG_CURRENT_LINE);
}
else
{
bool IsAsm = Idx < d->LineIsAsm.Length() ? d->LineIsAsm[Idx] : false;
if (IsAsm)
{
l->c.Rgb(0, 0, 255);
l->Back.Rgb(0xf0, 0xf0, 0xf0);
}
}
Idx++;
}
}
LVmDebuggerWnd::LVmDebuggerWnd(LView *Parent, LVmCallback *Callback, LAutoPtr Vm, LAutoPtr Code, const char *Assembly)
{
d = new LScriptVmDebuggerPriv;
d->Parent = Parent;
d->Vm = Vm;
d->Callback = Callback;
d->Obj = Code;
if (d->Obj)
d->Script = d->Obj->GetSource();
d->Assembly = Assembly;
LRect r(0, 0, 1000, 900);
SetPos(r);
if (Parent)
MoveSameScreen(Parent);
else
MoveToCenter();
Name("Script Debugger");
if (Attach(NULL))
{
if ((Menu = new LMenu))
{
Menu->Attach(this);
LSubMenu *s = Menu->AppendSub("Debug");
s->AppendItem("Run", IDC_RUN, true, -1, "F5");
s->AppendItem("Pause", IDC_PAUSE, true, -1, NULL);
s->AppendItem("Stop", IDC_STOP, true, -1, "Ctrl+Break");
s->AppendItem("Restart", IDC_RESTART, true, -1, NULL);
s->AppendItem("Goto", IDC_GOTO, true, -1, NULL);
s->AppendSeparator();
s->AppendItem("Step Instruction", IDC_STEP_INSTR, true, -1, "F11");
s->AppendItem("Step Line", IDC_STEP_LINE, true, -1, "F10");
s->AppendItem("Step Out", IDC_STEP_OUT, true, -1, "Shift+F11");
s->AppendSeparator();
s->AppendItem("Breakpoint", IDC_BREAK_POINT, true, -1, "F9");
s->AppendItem("Break Into C++", IDC_BREAK_CPP, true, -1, "Ctrl+F9");
}
AddView(d->Tools = new LToolBar);
uint16 *Px = (uint16*) DbgIcons.Data;
LImageList *il = new LImageList(16, 16, DbgIcons.Create(*Px));
if (il)
d->Tools->SetImageList(il, 16, 16, true);
d->Tools->AppendButton("Run", IDC_RUN);
d->Tools->AppendButton("Pause", IDC_PAUSE);
d->Tools->AppendButton("Stop", IDC_STOP);
d->Tools->AppendButton("Restart", IDC_RESTART);
d->Tools->AppendButton("Goto", IDC_GOTO);
d->Tools->AppendSeparator();
d->Tools->AppendButton("Step Instruction", IDC_STEP_INSTR);
d->Tools->AppendButton("Step Line", IDC_STEP_LINE);
d->Tools->AppendButton("Step Out", IDC_STEP_OUT);
AddView(d->Main = new LBox(IDC_BOX));
d->Main->SetVertical(true);
d->Main->AddView(d->Sub = new LBox(IDC_BOX2));
d->Sub->SetVertical(false);
d->Sub->AddView(d->SourceLst = new LList(IDC_SOURCE_LST, 0, 0, 100, 100));
d->SourceLst->GetCss(true)->Width(LCss::Len("200px"));
d->SourceLst->AddColumn("Source", 200);
d->Sub->AddView(d->Text = new LDebugView(d));
d->Main->AddView(d->Tabs = new LTabView(IDC_TABS));
d->Tabs->GetCss(true)->Height(LCss::Len("250px"));
LTabPage *p = d->Tabs->Append("Variables");
p->Append(d->VarsTbl = new LTableLayout(IDC_VARS_TBL));
int x = 0, y = 0;
auto *c = d->VarsTbl->GetCell(x++, y);
c->Add(new LTextLabel(IDC_STATIC, 0, 0, -1, -1, "Globals:"));
c = d->VarsTbl->GetCell(x++, y);
c->Add(new LTextLabel(IDC_STATIC, 0, 0, -1, -1, "Locals:"));
c = d->VarsTbl->GetCell(x++, y);
c->Add(new LTextLabel(IDC_STATIC, 0, 0, -1, -1, "Registers:"));
x = 0; y++;
c = d->VarsTbl->GetCell(x++, y);
c->Add(d->Globals = new LList(IDC_GLOBALS, 0, 0, 100, 100));
d->Globals->AddColumn("Name",100);
d->Globals->AddColumn("Value",400);
c = d->VarsTbl->GetCell(x++, y);
c->Add(d->Locals = new LList(IDC_LOCALS, 0, 0, 100, 100));
d->Locals->AddColumn("Name",100);
d->Locals->AddColumn("Value",400);
c = d->VarsTbl->GetCell(x++, y);
c->Add(d->Registers = new LList(IDC_REGISTERS, 0, 0, 100, 100));
d->Registers->AddColumn("Name",100);
d->Registers->AddColumn("Value",400);
p = d->Tabs->Append("Stack");
p->Append(d->Stack = new LList(IDC_STACK, 0, 0, 100, 100));
d->Stack->SetPourLargest(true);
d->Stack->AddColumn("Address", 100);
d->Stack->AddColumn("Function", 300);
p = d->Tabs->Append("Log");
p->Append(d->Log = new LTextLog(IDC_LOG));
AttachChildren();
Visible(true);
{
char p[MAX_PATH_LEN];
LMakePath(p, sizeof(p), LGetExePath(), "../Scripts");
LDirectory dir;
LListItem *Match = NULL;
d->SourceLst->MultiSelect(false);
for (int b = dir.First(p); b; b = dir.Next())
{
if (!dir.IsDir())
{
auto n = dir.GetName();
if (stristr(n, ".script") &&
dir.Path(p, sizeof(p)))
{
LListItem *it = new LListItem;
it->SetText(dir.GetName(), 0);
it->SetText(p, 1);
if (d->Obj && d->Obj->GetFileName())
{
if (_stricmp(p, d->Obj->GetFileName()) == 0)
Match = it;
}
d->SourceLst->Insert(it);
}
}
}
if (!Match && d->Obj)
{
LListItem *it = new LListItem;
if (it)
{
it->SetText(LGetLeaf(d->Obj->GetFileName()), 0);
it->SetText(d->Obj->GetFileName(), 1);
d->SourceLst->Insert(it);
it->Select(true);
}
}
}
}
d->AcceptNotify = true;
if (d->Assembly)
SetSource(d->Assembly);
}
LVmDebuggerWnd::~LVmDebuggerWnd()
{
}
bool LVmDebuggerWnd::OnRequestClose(bool OsShuttingDown)
{
return LWindow::OnRequestClose(OsShuttingDown);
}
void LVmDebuggerWnd::Run()
{
Visible(true);
}
LStream *LVmDebuggerWnd::GetLog()
{
return d->Log;
}
void LVmDebuggerWnd::SetCode(LAutoPtr Cc)
{
d->Obj = Cc;
}
LCompiledCode *LVmDebuggerWnd::GetCode()
{
return d->Obj;
}
void LVmDebuggerWnd::SetSource(const char *Mixed)
{
#if 1
LStringPipe Glob(256);
LStringPipe Tmp(256);
d->Blocks.Length(0);
CodeBlock *Cur = &d->Blocks.New();
// Parse the mixed source
auto t = LString(Mixed).SplitDelimit("\n", -1, false);
bool InGlobals = true;
int InAsm = -1;
for (unsigned i=0; i Code
Cur->Asm.Reset(Tmp.NewStr());
Cur = &d->Blocks.New();
}
else
{
// Code -> Asm
Tmp.Empty();
}
InAsm = IsAsm;
}
Tmp.Print("%s\n", l);
if (InAsm)
{
Cur->AsmLines++;
Cur->AsmAddr.Add(htoi(l));
}
else if (!Cur->SrcLine)
{
while (*l == ' ') l++;
if (IsDigit(*l))
Cur->SrcLine = atoi(l);
}
}
if (InAsm)
Cur->Asm.Reset(Tmp.NewStr());
Tmp.Empty();
LStringPipe Txt;
auto Src = d->Script.SplitDelimit("\n", -1, false);
unsigned SrcLine = 1;
unsigned ViewLine = 1;
for (unsigned i=0; iBlocks.Length(); i++)
{
CodeBlock &b = d->Blocks[i];
if (b.SrcLine > 0)
{
while (SrcLine <= b.SrcLine)
{
char *s = Src[SrcLine-1];
Tmp.Print("%i: %s\n", SrcLine, s ? s : "");
b.SrcLines++;
SrcLine++;
}
b.Source.Reset(Tmp.NewStr());
}
if (b.Source && b.Asm)
{
b.ViewLine = ViewLine;
ViewLine += b.SrcLines + b.AsmLines;
Txt.Print("%s%s", b.Source.Get(), b.Asm.Get());
}
else if (b.Source)
{
b.ViewLine = ViewLine;
ViewLine += b.SrcLines;
Txt.Print("%s", b.Source.Get());
}
else if (b.Asm)
{
b.ViewLine = ViewLine;
ViewLine += b.AsmLines;
Txt.Print("%s", b.Asm.Get());
}
}
while (SrcLine <= Src.Length())
{
Txt.Print("%i: %s\n", SrcLine, Src[SrcLine-1].Get());
SrcLine++;
}
for (unsigned i=0; iBlocks.Length(); i++)
{
CodeBlock &b = d->Blocks[i];
int Base = b.ViewLine + b.SrcLines;
for (int n = Base; nLineIsAsm[n-1] = true;
}
LAutoString a(Txt.NewStr());
d->Text->Name(a);
#else
d->Text->Name(Mixed);
#endif
}
void LVmDebuggerWnd::UpdateVariables(LList *Lst, LVariant *Arr, ssize_t Len, char Prefix)
{
if (!d->Vm || !Lst || !Arr)
return;
List all;
Lst->GetAll(all);
LListItem *it;
for (ssize_t i=0; iVm->d->DumpVariant(&p, *v);
LAutoString a(p.NewStr());
char nm[32];
sprintf_s(nm, sizeof(nm), "%c" LPrintfSSizeT, Prefix, i);
if (i >= (ssize_t)all.Length())
{
it = new LListItem;
all.Insert(it);
Lst->Insert(it);
}
it = i < (ssize_t)all.Length() ? all[i] : NULL;
if (it)
{
it->SetText(nm, 0);
it->SetText(a, 1);
}
}
Lst->ResizeColumnsToContent();
}
void LVmDebuggerWnd::OnAddress(size_t Addr)
{
d->CurrentAddr = Addr;
if (d->Text)
{
ssize_t Sz = d->Text->Length();
d->Text->PourText(0, Sz);
d->Text->ScrollToCurLine();
d->Text->Invalidate();
}
OnNotify(d->Tabs, LNotifyValueChanged);
}
void LVmDebuggerWnd::OnError(const char *Msg)
{
if (Msg)
d->Text->SetError(Msg);
}
void LVmDebuggerWnd::OnRun(bool Running)
{
}
void LVmDebuggerWnd::LoadFile(const char *File)
{
if (!d->Vm || !d->Callback)
{
LAssert(0);
return;
}
LFile f;
if (f.Open(File, O_READ))
d->Script = f.Read();
else
d->Script.Empty();
d->Obj.Reset();
if (d->Callback->CompileScript(d->Obj, File, d->Script))
{
LCompiledCode *Code = dynamic_cast(d->Obj.Get());
if (Code)
{
d->Return.Empty();
d->Vm->d->Frames.Length(0);
LScriptArguments Args(d->Vm, &d->Return);
d->Vm->d->Setup(Code, 0, d->Log, NULL, &Args);
}
}
}
int LVmDebuggerWnd::OnCommand(int Cmd, int Event, OsView Wnd)
{
if (d->Vm &&
d->Vm->d->Vm == NULL)
{
// This happens when the original VM decides to go away and leave
// our copy of the VM as the only one left. This means we have to
// update the pointer in the VM's private data to point to us.
d->Vm->d->Vm = d->Vm;
}
switch (Cmd)
{
case IDC_RUN:
{
if (d->Vm)
d->Vm->Continue();
break;
}
case IDC_PAUSE:
{
if (d->Vm)
d->Vm->BreakExecution();
break;
}
case IDC_STOP:
{
Quit();
break;
}
case IDC_RESTART:
{
if (d->Vm && d->Obj)
{
LCompiledCode *Code = dynamic_cast(d->Obj.Get());
if (Code)
d->Vm->Execute(Code, 0, d->Log, false);
}
break;
}
case IDC_GOTO:
{
break;
}
case IDC_STEP_INSTR:
{
if (d->Vm)
d->Vm->StepInstruction();
break;
}
case IDC_STEP_LINE:
{
if (d->Vm)
d->Vm->StepLine();
break;
}
case IDC_STEP_OUT:
{
if (d->Vm)
d->Vm->StepOut();
break;
}
case IDC_BREAK_POINT:
{
int Addr = d->Text->GetAddr();
if (Addr >= 0)
d->Vm->BreakPoint(Addr, d->Text->Breakpoint(Addr));
break;
}
case IDC_BREAK_CPP:
{
if (!d->Vm)
{
LAssert(0);
break;
}
LMenuItem *i = Menu->FindItem(IDC_BREAK_CPP);
if (!i)
{
LAssert(0);
break;
}
bool b = !i->Checked();
i->Checked(b);
d->Vm->SetBreakCpp(b);
break;
}
}
return LWindow::OnCommand(Cmd, Event, Wnd);
}
int LVmDebuggerWnd::OnNotify(LViewI *Ctrl, LNotification n)
{
if (!d->AcceptNotify)
return 0;
switch (Ctrl->GetId())
{
case IDC_TABS:
{
switch (Ctrl->Value())
{
case 0: // Variables
{
if (d->Obj)
{
UpdateVariables(d->Globals,
d->Vm->d->Scope[SCOPE_GLOBAL],
d->Obj->Globals.Length(),
'G');
}
if (d->Vm->d->Frames.Length())
{
LVirtualMachinePriv::StackFrame &frm = d->Vm->d->Frames.Last();
UpdateVariables(d->Locals,
d->Vm->d->Scope[SCOPE_LOCAL],
frm.CurrentFrameSize,
'L');
}
else d->Locals->Empty();
UpdateVariables(d->Registers,
d->Vm->d->Scope[SCOPE_REGISTER],
MAX_REGISTER,
'R');
break;
}
case 1: // Call stack
{
d->Stack->Empty();
LArray &Frames = d->Vm->d->Frames;
for (int i=(int)Frames.Length()-1; i>=0; i--)
{
LVirtualMachinePriv::StackFrame &Sf = Frames[i];
LListItem *li = new LListItem;
LString s;
s.Printf("%p/%i", Sf.ReturnIp, Sf.ReturnIp);
li->SetText(s, 0);
const char *Src = d->Vm->d->Code->AddrToSourceRef(Sf.ReturnIp);
li->SetText(Src, 1);
d->Stack->Insert(li);
}
break;
}
case 2: // Log
{
break;
}
}
break;
}
case IDC_SOURCE_LST:
{
if (n.Type == LNotifyItemSelect)
{
LListItem *it = d->SourceLst->GetSelected();
if (!it)
break;
const char *full = it->GetText(1);
if (!LFileExists(full))
break;
LoadFile(full);
}
break;
}
}
return LWindow::OnNotify(Ctrl, n);
}
LMessage::Param LVmDebuggerWnd::OnEvent(LMessage *Msg)
{
return LWindow::OnEvent(Msg);
}
diff --git a/src/common/Coding/ScriptingPriv.h b/src/common/Coding/ScriptingPriv.h
--- a/src/common/Coding/ScriptingPriv.h
+++ b/src/common/Coding/ScriptingPriv.h
@@ -1,597 +1,630 @@
#ifndef _GSCRIPTING_PRIV_H_
#define _GSCRIPTING_PRIV_H_
#include
#include "lgi/common/Scripting.h"
#include "lgi/common/RefCount.h"
// Instructions
#define _i(name, opcode, desc) \
name = opcode,
#define AllInstructions \
_i(INop, 0, "Nop") \
- _i(IAssign, OpAssign, "OpAssign") \
- _i(IPlus, OpPlus, "OpPlus") \
- _i(IUnaryPlus, OpUnaryPlus, "OpUnaryPlus") \
- _i(IMinus, OpMinus, "OpMinus") \
- _i(IUnaryMinus, OpUnaryMinus, "OpUnaryMinus") \
- _i(IMul, OpMul, "OpMul") \
- _i(IDiv, OpDiv, "OpDiv") \
- _i(IMod, OpMod, "OpMod") \
- _i(ILessThan, OpLessThan, "OpLessThan") \
- _i(ILessThanEqual, OpLessThanEqual, "OpLessThanEqual") \
- _i(IGreaterThan, OpGreaterThan, "OpGreaterThan") \
- _i(IGreaterThanEqual, OpGreaterThanEqual, "OpGreaterThanEqual") \
- _i(IEquals, OpEquals, "OpEquals") \
- _i(INotEquals, OpNotEquals, "OpNotEquals") \
- _i(IPlusEquals, OpPlusEquals, "OpPlusEquals") \
- _i(IMinusEquals, OpMinusEquals, "OpMinusEquals") \
- _i(IMulEquals, OpMulEquals, "OpMulEquals") \
- _i(IDivEquals, OpDivEquals, "OpDivEquals") \
- _i(IPostInc, OpPostInc, "OpPostInc") \
- _i(IPostDec, OpPostDec, "OpPostDec") \
- _i(IPreInc, OpPreInc, "OpPreInc") \
- _i(IPreDec, OpPreDec, "OpPreDec") \
- _i(IAnd, OpAnd, "OpAnd") \
- _i(IOr, OpOr, "OpOr") \
- _i(INot, OpNot, "OpNot") \
+ _i(IAssign, OpAssign, "IAssign") \
+ _i(IPlus, OpPlus, "IPlus") \
+ _i(IUnaryPlus, OpUnaryPlus, "IUnaryPlus") \
+ _i(IMinus, OpMinus, "IMinus") \
+ _i(IUnaryMinus, OpUnaryMinus, "IUnaryMinus") \
+ _i(IMul, OpMul, "IMul") \
+ _i(IDiv, OpDiv, "IDiv") \
+ _i(IMod, OpMod, "IMod") \
+ _i(ILessThan, OpLessThan, "ILessThan") \
+ _i(ILessThanEqual, OpLessThanEqual, "ILessThanEqual") \
+ _i(IGreaterThan, OpGreaterThan, "IGreaterThan") \
+ _i(IGreaterThanEqual, OpGreaterThanEqual, "IGreaterThanEqual") \
+ _i(IEquals, OpEquals, "IEquals") \
+ _i(INotEquals, OpNotEquals, "INotEquals") \
+ _i(IPlusEquals, OpPlusEquals, "IPlusEquals") \
+ _i(IMinusEquals, OpMinusEquals, "IMinusEquals") \
+ _i(IMulEquals, OpMulEquals, "IMulEquals") \
+ _i(IDivEquals, OpDivEquals, "IDivEquals") \
+ _i(IPostInc, OpPostInc, "IPostInc") \
+ _i(IPostDec, OpPostDec, "IPostDec") \
+ _i(IPreInc, OpPreInc, "IPreInc") \
+ _i(IPreDec, OpPreDec, "IPreDec") \
+ _i(IAnd, OpAnd, "IAnd") \
+ _i(IOr, OpOr, "IOr") \
+ _i(INot, OpNot, "INot") \
+ _i(IBitwiseAnd, OpBitwiseAnd, "IBitwiseAnd") \
+ _i(IBitwiseOr, OpBitwiseOr, "IBitwiseOr") \
+ _i(IBitwiseNegate, OpBitwiseNegate, "IBitwiseNegate") \
+ _i(IBitwiseXor, OpBitwiseXor, "IBitwiseXor") \
+ _i(IShiftLeft, OpShiftLeft, "IShiftLeft") \
+ _i(IShiftRight, OpShiftRight, "IShiftRight") \
\
/** Calls a another part of the script */ \
_i(ICallScript, 64, "CallScript") \
/** Calls a method defined by the script context */ \
_i(ICallMethod, 65, "CallMethod") \
_i(IDomGet, 67, "DomGet") \
_i(IDomSet, 68, "DomSet") \
_i(IPush, 69, "Push") \
_i(IPop, 70, "Pop") \
_i(IJump, 71, "Jump") \
_i(IJumpZero, 72, "JumpZ") \
_i(IArrayGet, 73, "ArrayGet") \
_i(IArraySet, 74, "ArraySet") \
_i(IRet, 75, "Return") \
_i(IDomCall, 76, "DomCall") \
/* Stop in the VM at instruction */ \
_i(IBreakPoint, 77, "BreakPoint") \
_i(ICast, 78, "Cast") \
/* Open the debugger */ \
_i(IDebug, 79, "Debug") \
-enum GInstruction {
+enum LInstruction {
AllInstructions
};
enum OperatorType
{
OpPrefix,
OpInfix,
OpPostfix,
};
extern char16 sChar[];
extern char16 sInt[];
extern char16 sUInt[];
extern char16 sInt32[];
extern char16 sUInt32[];
extern char16 sInt64[];
extern char16 sHWND[];
extern char16 sDWORD[];
extern char16 sLPTSTR[];
extern char16 sLPCTSTR[];
extern char16 sElse[];
extern char16 sIf[];
extern char16 sFunction[];
extern char16 sExtern[];
extern char16 sFor[];
extern char16 sWhile[];
extern char16 sReturn[];
extern char16 sInclude[];
extern char16 sDefine[];
extern char16 sStruct[];
extern char16 sTrue[];
extern char16 sFalse[];
extern char16 sNull[];
extern char16 sOutParam[];
extern char16 sHash[];
extern char16 sPeriod[];
extern char16 sComma[];
extern char16 sSemiColon[];
extern char16 sStartRdBracket[];
extern char16 sEndRdBracket[];
extern char16 sStartSqBracket[];
extern char16 sEndSqBracket[];
extern char16 sStartCurlyBracket[];
extern char16 sEndCurlyBracket[];
-extern const char *InstToString(GInstruction i);
+extern const char *InstToString(LInstruction i);
/*
Variable Reference:
Can either be a:
- stack variable
- global variable
- register (0 .. MAX_REGISTER - 1)
Thus a variable reference encodes 2 pieces of information, first the scope of
the reference (as above) and secondly the index into that scope. Scopes are
stored as arrays of variables.
The scope is one byte, the index is 3 bytes following, totally 4 bytes per ref.
*/
#define MAX_REGISTER 16
/// 32bit variable reference, used to track where a variable is during compilation.
struct LVarRef
{
/// \sa #SCOPE_REGISTER, #SCOPE_LOCAL or #SCOPE_GLOBAL
unsigned Scope : 8;
/// Index into scope
int Index : 24;
bool Valid()
{
return Index >= 0;
}
void Empty()
{
Scope = 0;
Index = -1;
}
bool IsReg()
{
return Scope == SCOPE_REGISTER && Index >= 0 && Index < MAX_REGISTER;
}
void SetReg(int i)
{
Scope = SCOPE_REGISTER;
Index = i;
}
bool operator ==(LVarRef &r)
{
return r.Scope == Scope && r.Index == Index;
}
bool operator !=(LVarRef &r)
{
return r.Scope != Scope || r.Index != Index;
}
const char *GetStr()
{
if (Index < 0)
{
LAssert(!"Invalid reference");
return "NoRef";
}
#define GETSTR_BUF_SIZE 16
static char Buf[8][GETSTR_BUF_SIZE];
static int Cur = 0;
static char Names[] = {'R', 'L', 'G'};
char *b = Buf[Cur++];
if (Cur >= 8) Cur = 0;
LAssert(Scope <= SCOPE_GLOBAL);
sprintf_s(b, GETSTR_BUF_SIZE, "%c%i", Names[Scope], Index);
return b;
}
};
union LScriptPtr
{
uint8_t *u8;
uint16 *u16;
uint32_t *u32;
int8 *i8;
int16 *i16;
int32 *i32;
double *dbl;
float *flt;
LVarRef *r;
LFunc **fn;
};
class SystemFunctions;
class LCompileTools
{
protected:
OperatorType OpType(LOperator o)
{
switch (o)
{
case OpUnaryPlus:
case OpUnaryMinus:
case OpPreInc:
case OpPreDec:
case OpNot:
+ case OpBitwiseNegate:
return OpPrefix;
case OpPostInc:
case OpPostDec:
return OpPostfix;
default:
return OpInfix;
}
}
int GetPrecedence(LOperator o)
{
// Taken from:
// http://www.cppreference.com/operator_precedence.html
switch (o)
{
case OpAssign:
case OpMinusEquals:
case OpPlusEquals:
case OpMulEquals:
case OpDivEquals:
return 16;
+ case OpOr:
+ return 15;
+
case OpAnd:
+ return 14;
+
+ case OpBitwiseOr:
return 13;
- case OpOr:
- return 14;
+ case OpBitwiseXor:
+ return 12;
+
+ case OpBitwiseAnd:
+ return 11;
case OpEquals:
case OpNotEquals:
- return 9;
+ return 10;
case OpLessThan:
case OpLessThanEqual:
case OpGreaterThan:
case OpGreaterThanEqual:
- return 8;
+ return 9;
+
+ case OpShiftLeft:
+ case OpShiftRight:
+ return 7;
case OpPlus:
case OpMinus:
return 6;
case OpMul:
case OpDiv:
case OpMod:
return 5;
case OpUnaryPlus:
case OpUnaryMinus:
case OpPreInc:
case OpPreDec:
case OpNot:
+ case OpBitwiseNegate:
return 3;
case OpPostInc:
case OpPostDec:
return 2;
case OpNull:
return 0;
default:
LAssert(!"Really?");
break;
}
return -1;
}
LOperator IsOp(char16 *s, int PrevIsOp)
{
if (!s) return OpNull;
if (s[0] != 0 && !s[1])
{
// One character operator
switch (*s)
{
case '=': return OpAssign;
case '*': return OpMul;
case '/': return OpDiv;
case '<': return OpLessThan;
case '>': return OpGreaterThan;
case '%': return OpMod;
case '!': return OpNot;
case '+':
{
if (PrevIsOp == 0)
return OpPlus;
return OpUnaryPlus;
}
case '-':
{
if (PrevIsOp == 0)
return OpMinus;
return OpUnaryMinus;
}
+ case '&': return OpBitwiseAnd;
+ case '|': return OpBitwiseOr;
+ case '~': return OpBitwiseNegate;
+ case '^': return OpBitwiseXor;
}
}
else if (s[0] != 0 && s[1] == '=' && !s[2])
{
// 2 chars, "something" equals operator
switch (*s)
{
case '!': return OpNotEquals;
case '=': return OpEquals;
case '<': return OpLessThanEqual;
case '>': return OpGreaterThanEqual;
case '+': return OpPlusEquals;
case '-': return OpMinusEquals;
case '*': return OpMulEquals;
case '/': return OpDivEquals;
}
}
else if (s[0] == '+' && s[1] == '+' && !s[2])
{
if (PrevIsOp == 0)
return OpPostInc;
return OpPreInc;
}
else if (s[0] == '-' && s[1] == '-' && !s[2])
{
if (PrevIsOp == 0)
return OpPostDec;
return OpPreDec;
}
else if (s[0] == '&' && s[1] == '&' && !s[2])
{
return OpAnd;
}
else if (s[0] == '|' && s[1] == '|' && !s[2])
{
return OpOr;
}
+ else if (s[0] == '<' && s[1] == '<' && !s[2])
+ {
+ return OpShiftLeft;
+ }
+ else if (s[0] == '>' && s[1] == '>' && !s[2])
+ {
+ return OpShiftRight;
+ }
return OpNull;
}
};
/// This class compiles the source down to byte code
class LCompiler : public LScriptUtils
{
class LCompilerPriv *d;
public:
/// Constructor
LCompiler();
~LCompiler();
/// Compile the source into byte code.
bool Compile
(
LAutoPtr &Code,
LScriptContext *SysContext,
LScriptContext *UserContext,
const char *FileName,
const char *Script,
LDom *Args
);
};
/// This class is the VM for the byte language
class LVirtualMachine :
public LVirtualMachineI,
public LScriptUtils
{
friend class LVmDebuggerWnd;
friend class LScriptArguments;
class LVirtualMachinePriv *d;
public:
static bool BreakOnWarning;
class Context
{
friend class LVirtualMachine;
LCompiledCode *Code = NULL;
LVmCallback *Callback = NULL;
ssize_t Addr = -1;
public:
operator bool()
{
return Code != NULL && Callback != NULL;
}
bool Call(LString CallbackName, LScriptArguments &Args) const
{
if (!Callback)
return false;
LVirtualMachine Vm(*this);
auto Prev = Args.GetVm();
Args.SetVm(&Vm);
auto result = Callback->CallCallback(Vm, CallbackName, Args);
Args.SetVm(Prev);
return result;
}
};
LVirtualMachine(LVmCallback *callback = NULL);
LVirtualMachine(Context ctx);
LVirtualMachine(LVirtualMachine *vm);
~LVirtualMachine();
void OnException(const char *File, int Line, ssize_t Address, const char *Msg);
/// Executes the whole script starting at the top
LExecutionStatus Execute
(
/// [In] The code to execute
LCompiledCode *Code,
/// [In] The instruction to start at... [defaults to the start of script)
uint32_t StartOffset = 0,
/// [Optional] Log file for execution
LStream *Log = NULL,
/// Start the script execution straight away?
bool StartImmediately = true,
/// Optional return value
LVariant *Return = NULL
);
/// Execute just one method and return
LExecutionStatus ExecuteFunction
(
/// [In] The code to execute
LCompiledCode *Code,
/// [In] The function to execute
LFunctionInfo *Func,
/// [In/Out] The function's arguments
LScriptArguments &Args,
/// [Optional] Log file for execution
LStream *Log = NULL,
/// [Optional] Copy arguments back to this array
LScriptArguments *ArgsOut = NULL
);
// Debugging commands
LVmDebugger *OpenDebugger(LCompiledCode *Code = NULL, const char *Assembly = NULL);
bool StepInstruction();
bool StepLine();
bool StepOut();
bool BreakExecution();
bool Continue();
bool BreakPoint(const char *File, int Line, bool Add);
bool BreakPoint(int Addr, bool Add);
void SetBreakCpp(bool Brk);
void SetDebuggerEnabled(bool b);
// Other methods
void SetTempPath(const char *Path);
LVmCallback *GetCallback();
Context SaveContext();
};
/// Scripting engine system functions
class SystemFunctions : public LScriptContext
{
LScriptEngine *Engine;
LStream *Log;
#ifdef WINNATIVE
HANDLE Brk;
#endif
LView *CastLView(LVariant &v);
public:
SystemFunctions();
~SystemFunctions();
LStream *GetLog() override;
bool SetLog(LStream *log) override;
void SetEngine(LScriptEngine *Eng);
LString GetIncludeFile(const char *FileName) override
{
return LString();
}
LHostFunc *GetCommands() override;
// Debug
bool Assert(LScriptArguments &Args);
bool Throw(LScriptArguments &Args);
bool DebuggerEnabled(LScriptArguments &Args);
// String
bool LoadString(LScriptArguments &Args);
/// Formats a string
bool Sprintf(LScriptArguments &Args);
/// Formats a file size
bool FormatSize(LScriptArguments &Args);
/// Prints items to the console
bool Print(LScriptArguments &Args);
/// Converts args to string
bool ToString(LScriptArguments &Args);
/// Turn a 4 char string into an int
bool Lgi4CC(LScriptArguments &Args);
// Object creation/deletion
bool New(LScriptArguments &Args);
bool Delete(LScriptArguments &Args);
bool Len(LScriptArguments &Args);
// File
/// Reads a text file into a variable
bool ReadTextFile(LScriptArguments &Args);
/// Writes a text file from a variable
bool WriteTextFile(LScriptArguments &Args);
/// \brief Opens a file open dialog to select files.
///
/// Args: LView *Parent, char *Patterns,
/// char *InitFolder, bool Multiselect
bool SelectFiles(LScriptArguments &Args);
/// Open a folder select dialog
///
/// Args: LView *Parent, char *InitFolder
bool SelectFolder(LScriptArguments &Args);
/// Lists file in folder
///
/// Args; char *Path, [optional] char *Pattern
/// Returns: List of DOM objects with the following fields:
/// Name - File/dir name
/// Size - Size of entry
/// Folder - bool, true if folder
/// Modified - LDateTime, modified time
bool ListFiles(LScriptArguments &Args);
/// Deletes a file
bool DeleteFile(LScriptArguments &Args);
/// Gets the current script path.
bool CurrentScript(LScriptArguments &Args);
/// Finds out if a path exists.
bool PathExists(LScriptArguments &Args);
/// Joins path segments together.
bool PathJoin(LScriptArguments &Args);
/// Returns the current OS path separator.
bool PathSep(LScriptArguments &Args);
// Time
/// Sleeps a number of milliseconds
bool Sleep(LScriptArguments &Args);
/// Get the current tick count
bool ClockTick(LScriptArguments &Args);
/// Get the date time
bool Now(LScriptArguments &Args);
// Bitmaps
/// Creates a memory context
bool CreateSurface(LScriptArguments &Args);
bool ColourSpaceToString(LScriptArguments &Args);
bool StringToColourSpace(LScriptArguments &Args);
// User interface
/// Standard alert message box
bool MessageDlg(LScriptArguments &Args);
/// Gets an input string from the user
/// String GetInputDlg(Window Parent, String InitialValue, String Question, String Title, bool IsPassword, String Callback.
bool GetInputDlg(LScriptArguments &Args);
/// Gets a view by id
bool GetViewById(LScriptArguments &Args);
// System
/// Executes a command, waits for it to finish, then returns it's output:
/// String Execute(String Application, String CmdLine);
bool Execute(LScriptArguments &Args);
/// Executes a command and doesn't wait for it to return:
/// Bool System(String Application, String CmdLine);
bool System(LScriptArguments &Args);
/// Gets the operating system name.
bool OsName(LScriptArguments &Args);
/// Gets the operating system version.
bool OsVersion(LScriptArguments &Args);
};
#endif
diff --git a/test/ScriptingUnitTests/LgiScript.vcxproj b/test/ScriptingUnitTests/LgiScript.vcxproj
--- a/test/ScriptingUnitTests/LgiScript.vcxproj
+++ b/test/ScriptingUnitTests/LgiScript.vcxproj
@@ -1,225 +1,224 @@
Debug
Win32
Debug
x64
Release
Win32
Release
x64
LgiScript
{D21A823F-9A21-43BB-8F2C-FA665D16126B}
LgiScript
Win32Proj
10.0
Application
v142
Unicode
true
Application
v142
Unicode
Application
v142
Unicode
true
Application
v142
Unicode
<_ProjectFileVersion>12.0.30501.0
$(Platform)$(Configuration)14\
$(Platform)$(Configuration)14\
true
$(Platform)$(Configuration)14\
$(Platform)$(Configuration)14\
true
$(Platform)$(Configuration)14\
$(Platform)$(Configuration)14\
false
$(Platform)$(Configuration)14\
$(Platform)$(Configuration)14\
false
Disabled
..\..\include;..\..\include\lgi\win;%(AdditionalIncludeDirectories)
WIN32;_DEBUG;_CONSOLE;WINDOWS;%(PreprocessorDefinitions)
true
EnableFastChecks
MultiThreadedDebugDLL
Level2
ProgramDatabase
imm32.lib;%(AdditionalDependencies)
true
Console
MachineX86
X64
Disabled
..\..\include;..\..\include\lgi\win;%(AdditionalIncludeDirectories)
WIN64;_DEBUG;_CONSOLE;WINDOWS;%(PreprocessorDefinitions)
EnableFastChecks
MultiThreadedDebugDLL
Level2
ProgramDatabase
imm32.lib;%(AdditionalDependencies)
true
Console
MachineX64
MaxSpeed
true
..\..\include;..\..\include\lgi\win;%(AdditionalIncludeDirectories)
WIN32;NDEBUG;_CONSOLE;WINDOWS;%(PreprocessorDefinitions)
MultiThreadedDLL
true
Level2
ProgramDatabase
imm32.lib;%(AdditionalDependencies)
true
Console
true
true
MachineX86
X64
MaxSpeed
true
..\..\include;..\..\include\lgi\win;%(AdditionalIncludeDirectories)
WIN64;NDEBUG;_CONSOLE;WINDOWS;%(PreprocessorDefinitions)
MultiThreadedDLL
true
Level2
ProgramDatabase
imm32.lib;%(AdditionalDependencies)
true
Console
true
true
MachineX64
-
-
-
-
cd $(IntDir)
"c:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\bin\Hostx64\x64\ml64.exe" %(FullPath) /c
$(IntDir)%(Filename).obj;%(Outputs)
cd $(IntDir)
"c:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\bin\Hostx64\x64\ml64.exe" %(FullPath) /c
$(IntDir)%(Filename).obj;%(Outputs)
+
+
+
+
+
-
-
{95df9ca4-6d37-4a85-a648-80c2712e0da1}
false
\ No newline at end of file
diff --git a/test/ScriptingUnitTests/LgiScript.vcxproj.filters b/test/ScriptingUnitTests/LgiScript.vcxproj.filters
--- a/test/ScriptingUnitTests/LgiScript.vcxproj.filters
+++ b/test/ScriptingUnitTests/LgiScript.vcxproj.filters
@@ -1,91 +1,104 @@
{4FC737F1-C7A5-4376-A066-2A32D752A2FF}
cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
{2560d89b-1bfb-4676-b2de-a8afd5231429}
{2e2d13df-4e87-4b6b-82ae-f301b34bdd22}
{43a1e628-add4-441b-8ca4-ffea4bf40ba5}
{9d89d250-b2ed-4a08-8963-629ca0d826cf}
-
- Source Files
+
+ Source Files\Scripting
-
- Source Files
+
+ Source Files\Scripting
- Source Files
+ Source Files\Scripting
- Source Files
+ Source Files\Scripting
- Source Files
+ Source Files\Scripting
-
+
Source Files
Source Files\Test Scripts
Source Files\Test Scripts
Source Files\Test Scripts
Source Files\Test Scripts
Source Files\Test Scripts
Source Files\Test Scripts
Source Files\Test Scripts
Source Files\Test Scripts
Source Files\Test Scripts
Source Files\Test Scripts
Source Files\Test Scripts
-
-
-
-
+
+ Documentation
+
+
+ Documentation
+
+
+ Documentation
+
+
+ Documentation
+
+
+ Source Files\Test Scripts
+
-
-
-
-
+
+ Source Files\Scripting\Hdrs
+
+
+ Source Files\Scripting\Hdrs
+
- Source Files
+ Source Files\Scripting
\ No newline at end of file
diff --git a/test/ScriptingUnitTests/Main.cpp b/test/ScriptingUnitTests/Main.cpp
--- a/test/ScriptingUnitTests/Main.cpp
+++ b/test/ScriptingUnitTests/Main.cpp
@@ -1,164 +1,167 @@
#include "lgi/common/Lgi.h"
#include "lgi/common/Scripting.h"
#include "../src/common/Coding/ScriptingPriv.h"
#include "lgi/common/StringClass.h"
#include "lgi/common/LgiRes.h"
struct ConsoleLog : public LStream
{
ssize_t Write(const void *Ptr, ssize_t Size, int Flags)
{
return printf("%.*s", (int)Size, (char*)Ptr);
}
};
-class App : public LApp, public LScriptContext, public LVmCallback
+class App :
+ public LApp,
+ public LScriptContext,
+ public LVmCallback
{
LScriptEngine *Engine;
- LAutoString SrcFile;
+ LString SrcFile;
ConsoleLog Log;
+ LStream *GetLog()
+ {
+ return &Log;
+ }
+
bool CallCallback(LVirtualMachine &Vm, LString CallbackName, LScriptArguments &Args)
{
Args.Throw(_FL, "Not implemented.");
return false;
}
LVmDebugger *AttachVm(LVirtualMachine *Vm, LCompiledCode *Code, const char *Assembly)
{
LAutoPtr vm(new LVirtualMachine(Vm));
LAutoPtr code(new LCompiledCode(*Code));
return new LVmDebuggerWnd(NULL, this, vm, code, Assembly);
}
bool CompileScript(LAutoPtr &Output, const char *FileName, const char *Source)
{
return false;
}
public:
int Status;
const char* GetClass() override { return "App"; }
App(OsAppArguments &AppArgs) : LApp(AppArgs, "LgiScript")
{
LFile::Path p(LSP_APP_INSTALL);
p += "Resources";
p += "LgiScript.lr8";
LgiGetResObj(true, p);
Engine = NULL;
Status = 0;
}
LHostFunc *GetCommands() { return NULL; }
void SetEngine(LScriptEngine *Eng) { Engine = Eng; }
void OnReceiveFiles(LArray &Files)
{
- for (int i=0; iGetOption("disassemble");
+ auto Disassemble = LAppInst->GetOption("disassemble");
if (!LFileExists(File))
{
printf("Error: '%s' not found.\n", File);
return false;
}
- if (!SrcFile.Reset(NewStr(File)))
- {
- printf("Error: Mem alloc failed.\n");
- return false;
- }
-
- LScriptEngine Eng(NULL, NULL, this);
+ SrcFile = File;
+ LScriptEngine Eng(NULL, this, this);
Eng.SetConsole(&Log);
auto Src = LReadFile(SrcFile);
if (!Src)
{
printf("Error: Failed to read '%s'.\n", SrcFile.Get());
return false;
}
LAutoPtr Obj;
if (!Eng.Compile(Obj, NULL, Src, File))
{
printf("Error: Compilation failed '%s'.\n", SrcFile.Get());
return false;
}
LVariant Ret;
- LExecutionStatus s = Eng.Run(Obj, &Ret);
+ auto s = Eng.Run(Obj, &Ret);
if (s == ScriptError)
{
printf("Error: Execution failed '%s'.\n", SrcFile.Get());
return false;
}
else if (s == ScriptWarning)
{
printf("Warning: Execution succeeded with warnings '%s'.\n", SrcFile.Get());
return false;
}
if (Ret.CastInt32())
printf("Success: %s\n", File);
else
{
printf("Failed: %s\n", File);
s = ScriptError;
}
if (Disassemble)
{
LString f = File;
int Idx = f.RFind(".");
if (Idx > 0)
{
f = f(0, Idx) + ".asm";
auto a = LReadFile(f);
if (a)
{
printf("%s\n", a.Get());
}
}
}
return s == ScriptSuccess;
}
};
int LgiMain(OsAppArguments &AppArgs)
{
App a(AppArgs);
if (a.IsOk())
{
a.OnCommandLine();
}
return a.Status;
}
\ No newline at end of file
diff --git a/test/ScriptingUnitTests/Scripts/BitwiseOps.script b/test/ScriptingUnitTests/Scripts/BitwiseOps.script
new file mode 100644
--- /dev/null
+++ b/test/ScriptingUnitTests/Scripts/BitwiseOps.script
@@ -0,0 +1,46 @@
+
+a = 0x04;
+b = a | 0x80;
+if (b != 0x84)
+{
+ Print("Bitwise OR failed.\n");
+ return false;
+}
+
+b = a & 0x84;
+if (b != 0x04)
+{
+ Print("Bitwise AND failed.\n");
+ return false;
+}
+
+b = ~0xaa;
+if (b != -171)
+{
+ Print("Bitwise NEGATE failed.\n");
+ return false;
+}
+
+b = a ^ 0x84;
+if (b != 0x80)
+{
+ Print("Bitwise XOR failed.\n");
+ return false;
+}
+
+b = a << 4;
+if (b != 0x40)
+{
+ Print("<< operator failed.\n");
+ return false;
+}
+
+b = a >> 1;
+if (b != 0x2)
+{
+ Print(">> operator failed.\n");
+ return false;
+}
+
+return true;
+
diff --git a/test/UnitTests/win/UnitTests.vcxproj b/test/UnitTests/win/UnitTests.vcxproj
--- a/test/UnitTests/win/UnitTests.vcxproj
+++ b/test/UnitTests/win/UnitTests.vcxproj
@@ -1,280 +1,277 @@
Debug
Win32
Debug
x64
Release
Win32
Release
x64
{18BF30E1-D77B-496E-8761-99A426DD3B41}
10.0
Application
v142
false
MultiByte
Application
v142
false
MultiByte
Application
v142
false
MultiByte
Application
v142
false
MultiByte
<_ProjectFileVersion>12.0.30501.0
$(Platform)$(Configuration)14\
$(Platform)$(Configuration)14\
true
true
$(Platform)$(Configuration)14\
$(Platform)$(Configuration)14\
$(Platform)$(Configuration)14\
$(Platform)$(Configuration)14\
false
false
$(Platform)$(Configuration)14\
$(Platform)$(Configuration)14\
.\Debug/UnitTests.tlb
Disabled
..\..\..\include;..\..\..\include\lgi\win;%(AdditionalIncludeDirectories)
WIN32;_DEBUG;_CONSOLE;WINDOWS;LGI_UNIT_TESTS;%(PreprocessorDefinitions)
true
EnableFastChecks
MultiThreadedDebugDLL
true
$(IntDir)/UnitTests.pch
$(IntDir)
$(IntDir)
$(IntDir)vc$(PlatformToolsetVersion).pdb
true
ProgramDatabase
_DEBUG;%(PreprocessorDefinitions)
0x0c09
$(OutDir)$(TargetName)$(TargetExt)
true
true
$(IntDir)/UnitTests.pdb
Console
false
MachineX86
true
.\Debug/UnitTests.bsc
.\Debug/UnitTests.tlb
Disabled
..\..\..\include;..\..\..\include\lgi\win;%(AdditionalIncludeDirectories)
WIN32;_DEBUG;_CONSOLE;WINDOWS;LGI_UNIT_TESTS;%(PreprocessorDefinitions)
EnableFastChecks
MultiThreadedDebugDLL
true
$(IntDir)/UnitTests.pch
$(IntDir)
$(IntDir)
$(IntDir)vc$(PlatformToolsetVersion).pdb
true
ProgramDatabase
Level3
_DEBUG;%(PreprocessorDefinitions)
0x0c09
true
true
$(IntDir)/UnitTests.pdb
Console
false
true
.\Debug/UnitTests.bsc
.\Release/UnitTests.tlb
MinSpace
OnlyExplicitInline
..\..\include;..\..\include\lgi\win;%(AdditionalIncludeDirectories)
WIN32;NDEBUG;_CONSOLE;WINDOWS;LGI_UNIT_TESTS;%(PreprocessorDefinitions)
true
MultiThreadedDLL
true
true
$(IntDir)/UnitTests.pch
$(IntDir)
$(IntDir)
$(IntDir)vc$(PlatformToolsetVersion).pdb
true
NDEBUG;%(PreprocessorDefinitions)
0x0c09
$(OutDir)$(TargetName)$(TargetExt)
true
$(IntDir)/UnitTests.pdb
Console
false
MachineX86
true
.\Release/UnitTests.bsc
.\Release/UnitTests.tlb
MinSpace
OnlyExplicitInline
..\..\include;..\..\include\lgi\win;%(AdditionalIncludeDirectories)
WIN32;NDEBUG;_CONSOLE;WINDOWS;LGI_UNIT_TESTS;%(PreprocessorDefinitions)
true
MultiThreadedDLL
true
true
$(IntDir)/UnitTests.pch
$(IntDir)
$(IntDir)
$(IntDir)vc$(PlatformToolsetVersion).pdb
true
NDEBUG;%(PreprocessorDefinitions)
0x0c09
$(OutDir)$(TargetName)$(TargetExt)
true
$(IntDir)/UnitTests.pdb
Console
false
true
.\Release/UnitTests.bsc
-
-
-
{95df9ca4-6d37-4a85-a648-80c2712e0da1}
\ No newline at end of file
diff --git a/test/UnitTests/win/UnitTests.vcxproj.filters b/test/UnitTests/win/UnitTests.vcxproj.filters
--- a/test/UnitTests/win/UnitTests.vcxproj.filters
+++ b/test/UnitTests/win/UnitTests.vcxproj.filters
@@ -1,83 +1,74 @@
{1b5d1f69-ef66-4844-baa5-25ec23d2151c}
cpp;c;cxx;rc;def;r;odl;idl;hpj;bat
{a734023f-ceb2-4882-9efc-5719c6bf4c8d}
h;hpp;hxx;hm;inl
{134d3911-e459-4919-8af8-82198f95a179}
{8b34f4e6-17d2-42af-8c12-1386ba1acae0}
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
+
Source Files
-
- Source Files
+
+ Source Files\Testing
+
+
+ Source Files\Testing
+
+
+ Source Files\Testing
-
- Lgi
+
+ Source Files\Testing
-
+
+ Source Files\Testing
+
+
+ Source Files\Testing
+
+
Source Files\Testing
-
- Source Files
+
+ Source Files\Testing
+
+
+ Source Files\Testing
-
- Source Files
+
+ Source Files\Testing
-
+
+ Source Files\Testing
+
+
+ Source Files\Testing
+
+
Lgi
-
- Source Files
-
-
- Source Files
+
+ Lgi
-
- Header Files
-
-
+
Header Files
-
- Header Files
-
-
- Source Files\Testing
-
-
+
Lgi
\ No newline at end of file