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,473 +1,474 @@ /** \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) && (defined(LGI_64BIT) || defined(MAC)) && !defined(HAIKU) #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 GV_NULL, /// 32-bit integer GV_INT32, /// 64-bit integer GV_INT64, /// true or false boolean. GV_BOOL, /// C++ double GV_DOUBLE, /// Null terminated string value GV_STRING, /// Block of binary data GV_BINARY, /// List of LVariant GV_LIST, /// Pointer to LDom object GV_DOM, /// DOM reference, ie. a variable in a DOM object GV_DOMREF, /// Untyped pointer GV_VOID_PTR, /// LDateTime class. GV_DATETIME, /// Hash table class, containing pointers to LVariants GV_HASHTABLE, // Scripting language operator GV_OPERATOR, // Custom scripting lang type GV_CUSTOM, // Wide string GV_WSTRING, // LSurface ptr GV_LSURFACE, /// Pointer to LView GV_GVIEW, /// Pointer to LMouse GV_LMOUSE, /// Pointer to LKey GV_LKEY, /// Pointer to LStream GV_STREAM, /// The maximum value for the variant type. /// (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, }; 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; ssize_t Sizeof(); bool GetVariant(const char *Name, LVariant &Value, const char *Array = NULL); }; public: struct Method : public LDom { LString Name; LArray Params; size_t Address; int FrameSize; Method() { Address = -1; FrameSize = -1; } }; 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(); 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); bool SetVariant(const char *Name, LVariant &Value, const char *Array = NULL); bool CallMethod(const char *MethodName, LScriptArguments &Args); }; /// 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_GVIEW, #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_GVIEW 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 /// behaviour 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_GVIEW ? 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); + 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); }; #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,2045 +1,2045 @@ /* 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 (Log) Log->Print("\t%p Unknown instruction %i (0x%x)\n", CurrentScriptAddress - 1, c.u8[-1], 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 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); + 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; } } - Arg.Length(0); // It doesn't own the variants, so don't delete them. + 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); + 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); bool 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); bool 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]) { int Index = Arg.Length() > 1 ? Arg[1]->CastInt32() : -1; LVariant *v = new LVariant; *v = *Arg[0]; Dom->Value.Lst->Insert(v, Index); } break; } case ContainerDelete: { for (unsigned i=0; iCastInt32(); LVariant *Elem = Dom->Value.Lst->ItemAt(n); if (Elem) { Dom->Value.Lst->Delete(Elem); DeleteObj(Elem); } } } break; } case ContainerHasKey: { if (Arg.Length() > 0 && Arg[0]) { int Index = Arg[0]->CastInt32(); *Dst = (bool) (Index >= 0 && Index < (int)Dom->Value.Lst->Length()); } else { *Dst = false; } break; } case ContainerSort: { LVariant *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; } 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 = WhiteSpace; 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; } } - Arg.Length(0); // It doesn't own the variants, so don't delete them. + 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/ScriptVM.cpp b/src/common/Coding/ScriptVM.cpp --- a/src/common/Coding/ScriptVM.cpp +++ b/src/common/Coding/ScriptVM.cpp @@ -1,2168 +1,2160 @@ #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; } 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 - - /* - if (!Debugger->GetCode()) - { - LAutoPtr Cp(new LCompiledCode(*Code)); - Debugger->OwnCompiledCode(Cp); - - LStringPipe AsmBuf; - Decompile(Code->UserContext, Code, &AsmBuf); - LAutoString Asm(AsmBuf.NewStr()); - Debugger->SetSource(Asm); - } - */ - 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; } } 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 - - #ifdef BREAK_POINT - if (c.u8 - Base == BREAK_POINT) - { - int asd=0; - } - #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; - size_t CurrentAddr = -1; + 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(TEXTED_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()) { char *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: { - LAssert(!"Impl me."); + 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/Lgi/GuiUtils.cpp b/src/common/Lgi/GuiUtils.cpp --- a/src/common/Lgi/GuiUtils.cpp +++ b/src/common/Lgi/GuiUtils.cpp @@ -1,291 +1,294 @@ #include #include "lgi/common/Lgi.h" #include "lgi/common/SkinEngine.h" #if defined(LINUX) && !defined(LGI_SDL) #include "LgiWinManGlue.h" #endif ////////////////////////////////////////////////////////////////////////////////// #if !defined(LK_CONTEXTKEY) // LK_CONTEXTKEY is the key that brings up the context menu #if defined(WINDOWS) #define LK_CONTEXTKEY 0x5d #elif defined(MAC) #define LK_CONTEXTKEY 0x6e #elif defined(__GTK_H__) #define LK_CONTEXTKEY GDK_KEY_Menu #else #define LK_CONTEXTKEY 0x5d #warning "Check local platform def for app menu key." #endif #endif LKey::LKey(int Vkey, uint32_t flags) { c16 = vkey = Vkey; - Flags = flags; - Data = 0; - IsChar = false; + + #ifdef WINDOWS + Data = flags; + #else + Flags = flags; + #endif } bool LKey::IsContextMenu() const { return !IsChar && vkey == LK_CONTEXTKEY; } ////////////////////////////////////////////////////////////////////////////////// LString LMouse::ToString() const { LString s; s.Printf("LMouse(pos=%i,%i view=%p/%s btns=%i/%i/%i/%i/%i dwn=%i dbl=%i " "ctrl=%i alt=%i sh=%i sys=%i)", x, y, // pos Target, Target?Target->GetClass():NULL, // view Left(), Middle(), Right(), Button1(), Button2(), // btns Down(), Double(), // dwn Ctrl(), Alt(), Shift(), System()); // mod keys return s; } bool LMouse::IsContextMenu() const { if (Right()) return true; #if defined(MAC) if (Left() && Ctrl()) return true; #endif return false; } bool LMouse::ToScreen() { if (ViewCoords) { if (!Target) { printf("%s:%i - ToScreen Error: Target=%p ViewCoords=%i\n", _FL, Target, ViewCoords); return false; } LPoint p(x, y); Target->PointToScreen(p); x = p.x; y = p.y; ViewCoords = false; } return true; } bool LMouse::ToView() { if (!ViewCoords) { if (!Target) { printf("%s:%i - ToView Error: Target=%p ViewCoords=%i\n", _FL, Target, ViewCoords); return false; } LPoint p(x, y); Target->PointToView(p); x = p.x; y = p.y; ViewCoords = true; } return true; } #if WINNATIVE #if !defined(DM_POSITION) #define DM_POSITION 0x00000020L #endif typedef WINUSERAPI BOOL (WINAPI *pEnumDisplayDevicesA)(PVOID, DWORD, PDISPLAY_DEVICEA, DWORD); typedef WINUSERAPI BOOL (WINAPI *pEnumDisplayDevicesW)(PVOID, DWORD, PDISPLAY_DEVICEW, DWORD); typedef WINUSERAPI BOOL (WINAPI *pEnumDisplaySettingsA)(LPCSTR lpszDeviceName, DWORD iModeNum, LPDEVMODEA lpDevMode); typedef WINUSERAPI BOOL (WINAPI *pEnumDisplaySettingsW)(LPCWSTR lpszDeviceName, DWORD iModeNum, LPDEVMODEW lpDevMode); #endif LPointF GDisplayInfo::Scale() { LPointF p((double)Dpi.x / 96.0, (double)Dpi.y / 96.0); return p; } bool LGetDisplays(::LArray &Displays, LRect *AllDisplays) { #if WINNATIVE if (AllDisplays) AllDisplays->ZOff(-1, -1); LLibrary User32("User32"); DISPLAY_DEVICEW disp; ZeroObj(disp); disp.cb = sizeof(disp); pEnumDisplayDevicesW EnumDisplayDevicesW = (pEnumDisplayDevicesW) User32.GetAddress("EnumDisplayDevicesW"); pEnumDisplaySettingsW EnumDisplaySettingsW = (pEnumDisplaySettingsW) User32.GetAddress("EnumDisplaySettingsW"); for (int i=0; EnumDisplayDevicesW(0, i, &disp, 0); i++) { DEVMODEW mode; ZeroObj(mode); mode.dmSize = sizeof(mode); mode.dmDriverExtra = sizeof(mode); if (EnumDisplaySettingsW(disp.DeviceName, ENUM_CURRENT_SETTINGS, &mode)) { GDisplayInfo *Dsp = new GDisplayInfo; if (Dsp) { Dsp->r.ZOff(mode.dmPelsWidth-1, mode.dmPelsHeight-1); if (mode.dmFields & DM_POSITION) { Dsp->r.Offset(mode.dmPosition.x, mode.dmPosition.y); } if (AllDisplays) { if (AllDisplays->Valid()) AllDisplays->Union(&Dsp->r); else *AllDisplays = Dsp->r; } disp.cb = sizeof(disp); Dsp->BitDepth = mode.dmBitsPerPel; Dsp->Refresh = mode.dmDisplayFrequency; Dsp->Name = disp.DeviceString; Dsp->Device = disp.DeviceName; Dsp->Dpi.x = Dsp->Dpi.y = mode.dmLogPixels; DISPLAY_DEVICEW temp = disp; if (EnumDisplayDevicesW(temp.DeviceName, 0, &disp, 0)) Dsp->Monitor = disp.DeviceString; Displays.Add(Dsp); } } disp.cb = sizeof(disp); } #elif defined __GTK_H__ Gtk::GdkDisplay *Dsp = Gtk::gdk_display_get_default(); #if GtkVer(3, 22) int monitors = Gtk::gdk_display_get_n_monitors(Dsp); for (int i=0; ir = geometry; di->Device = Gtk::gdk_monitor_get_manufacturer(m); di->Name = Gtk::gdk_monitor_get_model(m); di->Refresh = Gtk::gdk_monitor_get_refresh_rate(m); Displays.Add(di); } #endif #elif LGI_COCOA for (NSScreen *s in [NSScreen screens]) { GDisplayInfo *di = new GDisplayInfo; di->r = s.frame; Displays.Add(di); } #endif return Displays.Length() > 0; } void GetChildrenList(LViewI *w, List &l) { if (!w) return; for (auto v: w->IterateViews()) { #if WINNATIVE int Style = GetWindowLong(v->Handle(), GWL_STYLE); if (TestFlag(Style, WS_VISIBLE) && !TestFlag(Style, WS_DISABLED)) { if (TestFlag(Style, WS_TABSTOP)) { l.Insert(v); } GetChildrenList(v, l); } #else if (v->Visible() && v->Enabled()) { if (v->GetTabStop()) { l.Insert(v); } GetChildrenList(v, l); } #endif } } LViewI *GetNextTabStop(LViewI *v, bool Back) { if (v) { LWindow *Wnd = v->GetWindow(); if (Wnd) { List All; GetChildrenList(Wnd, All); ssize_t MyIndex = All.IndexOf(v); if (MyIndex >= 0) { int Inc = Back ? -1 : 1; size_t NewIndex = (MyIndex + All.Length() + Inc) % All.Length(); return All.ItemAt(NewIndex); } else { return All[0]; } } } return 0; } diff --git a/src/common/Lgi/Variant.cpp b/src/common/Lgi/Variant.cpp --- a/src/common/Lgi/Variant.cpp +++ b/src/common/Lgi/Variant.cpp @@ -1,2405 +1,2406 @@ #include #include #include "lgi/common/Lgi.h" #include "lgi/common/Variant.h" const char *LVariant::TypeToString(LVariantType t) { switch (t) { case GV_NULL: return "NULL"; case GV_INT32: return "int32"; case GV_INT64: return "int64"; case GV_BOOL: return "bool"; case GV_DOUBLE: return "double"; case GV_STRING: return "String"; case GV_BINARY: return "Binary"; case GV_LIST: return "List"; case GV_DOM: return "Dom"; case GV_DOMREF: return "DomReference"; case GV_VOID_PTR: return "VoidPtr"; case GV_DATETIME: return "DateTime"; case GV_HASHTABLE: return "HashTable"; case GV_OPERATOR: return "Operator"; case GV_CUSTOM: return "Custom"; case GV_WSTRING: return "WString"; case GV_GVIEW: return "View"; case GV_STREAM: return "Stream"; case GV_LSURFACE: return "Surface"; case GV_LMOUSE: return "MouseEvent"; case GV_LKEY: return "KeyboardEvent"; default: return "Unknown"; } return NULL; } const char *LVariant::OperatorToString(LOperator op) { switch (op) { case OpNull: return "OpNull"; case OpAssign: return "OpAssign"; case OpPlus: return "OpPlus"; case OpUnaryPlus: return "OpUnaryPlus"; case OpMinus: return "OpMinus"; case OpUnaryMinus: return "OpUnaryMinus"; case OpMul: return "OpMul"; case OpDiv: return "OpDiv"; case OpMod: return "OpMod"; case OpLessThan: return "OpLessThan"; case OpLessThanEqual: return "OpLessThanEqual"; case OpGreaterThan: return "OpGreaterThan"; case OpGreaterThanEqual: return "OpGreaterThanEqual"; case OpEquals: return "OpEquals"; case OpNotEquals: return "OpNotEquals"; case OpPlusEquals: return "OpPlusEquals"; case OpMinusEquals: return "OpMinusEquals"; case OpMulEquals: return "OpMulEquals"; case OpDivEquals: return "OpDivEquals"; case OpPostInc: return "OpPostInc"; case OpPostDec: return "OpPostDec"; case OpPreInc: return "OpPreInc"; case OpPreDec: return "OpPreDec"; case OpAnd: return "OpAnd"; case OpOr: return "OpOr"; case OpNot: return "OpNot"; } return NULL; } LVariant::LVariant() { Type = GV_NULL; ZeroObj(Value); } LVariant::LVariant(LVariant const &v) { Type = GV_NULL; ZeroObj(Value); *this = v; } #if LVARIANT_SIZET LVariant::LVariant(size_t i) { Type = GV_NULL; *this = i; } #endif #if LVARIANT_SSIZET LVariant::LVariant(ssize_t i) { Type = GV_NULL; *this = i; } #endif LVariant::LVariant(int32_t i) { Type = GV_INT32; Value.Int = i; } LVariant::LVariant(uint32_t i) { Type = GV_INT32; Value.Int = i; } LVariant::LVariant(int64_t i) { Type = GV_INT64; Value.Int64 = i; } LVariant::LVariant(uint64_t i) { Type = GV_INT64; Value.Int64 = i; } LVariant::LVariant(double i) { Type = GV_DOUBLE; Value.Dbl = i; } LVariant::LVariant(const char *s) { Value.String = NewStr(s); Type = Value.String ? GV_STRING : GV_NULL; } LVariant::LVariant(const char16 *s) { Value.WString = NewStrW(s); Type = Value.WString ? GV_WSTRING : GV_NULL; } LVariant::LVariant(void *p) { Type = GV_NULL; *this = p; } LVariant::LVariant(LDom *p) { Type = GV_NULL; *this = p; } LVariant::LVariant(LDom *p, char *name) { Type = GV_NULL; SetDomRef(p, name); } LVariant::LVariant(const LDateTime *d) { Type = GV_NULL; *this = d; } LVariant::LVariant(LOperator Op) { Type = GV_OPERATOR; Value.Op = Op; } LVariant::~LVariant() { Empty(); } bool LVariant::operator ==(LVariant &v) { switch (Type) { default: case GV_NULL: return v.Type == Type; case GV_INT32: return Value.Int == v.CastInt32(); case GV_INT64: return Value.Int64 == v.CastInt64(); case GV_BOOL: return Value.Bool == v.CastBool(); case GV_DOUBLE: return Value.Dbl == v.CastDouble(); case GV_STRING: { char *s = v.Str(); if (Value.String && s) return !strcmp(Value.String, s); break; } case GV_WSTRING: { char16 *w = v.WStr(); if (Value.WString && w) return !StrcmpW(Value.WString, w); break; } case GV_BINARY: { if (v.Type == Type && Value.Binary.Data == v.Value.Binary.Data && Value.Binary.Length == v.Value.Binary.Length) { return true; } break; } case GV_LIST: { if (!Value.Lst || !v.Value.Lst) return false; if (Value.Lst->Length() != v.Value.Lst->Length()) return false; auto ValIt = Value.Lst->begin(); auto VIt = v.Value.Lst->begin(); LVariant *a, *b; while ( (a = *ValIt) && (b = *VIt) ) { if (!(*a == *b)) return false; ValIt++; VIt++; } return true; } case GV_DOMREF: { return Value.DomRef.Dom == v.Value.DomRef.Dom && Value.DomRef.Name != 0 && v.Value.DomRef.Name != 0 && !stricmp(Value.DomRef.Name, v.Value.DomRef.Name); } case GV_DATETIME: { if (Value.Date && v.Value.Date) { return Value.Date->Compare(v.Value.Date) == 0; } break; } case GV_DOM: return Value.Dom == v.Value.Dom; case GV_OPERATOR: return Value.Op == v.Value.Op; case GV_CUSTOM: return Value.Custom == v.Value.Custom; case GV_LSURFACE: return Value.Surface.Ptr == v.Value.Surface.Ptr; case GV_GVIEW: return Value.View == v.Value.View; /* case GV_GFILE: return Value.File.Ptr == v.Value.File.Ptr; */ case GV_STREAM: return Value.Stream.Ptr == v.Value.Stream.Ptr; case GV_LMOUSE: return Value.Mouse == v.Value.Mouse; case GV_LKEY: return Value.Key == v.Value.Key; case GV_VOID_PTR: return Value.Ptr == v.Value.Ptr; case GV_HASHTABLE: { LAssert(0); break; } } return false; } LVariant &LVariant::operator =(const LDateTime *d) { Empty(); if (d) { Type = GV_DATETIME; Value.Date = new LDateTime; if (Value.Date) { *Value.Date = *d; // if (Dirty) *Dirty = true; } } return *this; } LVariant &LVariant::operator =(bool i) { Empty(); Type = GV_BOOL; Value.Bool = i; // if (Dirty) *Dirty = true; return *this; } #if LVARIANT_SIZET LVariant &LVariant::operator =(size_t i) { Empty(); #if LGI_64BIT Type = GV_INT64; Value.Int64 = i; #else Type = GV_INT32; Value.Int = i; #endif return *this; } #endif #if LVARIANT_SSIZET LVariant &LVariant::operator =(ssize_t i) { Empty(); #if LGI_64BIT Type = GV_INT64; Value.Int64 = i; #else Type = GV_INT32; Value.Int = i; #endif return *this; } #endif LVariant &LVariant::operator =(int32 i) { Empty(); Type = GV_INT32; Value.Int = i; return *this; } LVariant &LVariant::operator =(uint32_t i) { Empty(); Type = GV_INT32; Value.Int = i; return *this; } LVariant &LVariant::operator =(int64_t i) { Empty(); Type = GV_INT64; Value.Int64 = i; return *this; } LVariant &LVariant::operator =(uint64_t i) { Empty(); Type = GV_INT64; Value.Int64 = i; return *this; } LVariant &LVariant::operator =(double i) { Empty(); Type = GV_DOUBLE; Value.Dbl = i; // if (Dirty) *Dirty = true; return *this; } LVariant &LVariant::operator =(const char *s) { Empty(); if (s) { Type = GV_STRING; Value.String = NewStr(s); } return *this; } LVariant &LVariant::operator =(const char16 *s) { Empty(); if (s) { Type = GV_WSTRING; Value.WString = NewStrW(s); } // if (Dirty) *Dirty = true; return *this; } LVariant &LVariant::operator =(void *p) { Empty(); if (p) { Type = GV_VOID_PTR; Value.Ptr = p; // if (Dirty) *Dirty = true; } return *this; } LVariant &LVariant::operator =(LDom *p) { Empty(); if (p) { Type = GV_DOM; Value.Dom = p; // if (Dirty) *Dirty = true; } return *this; } LVariant &LVariant::operator =(LView *p) { Empty(); if (p) { Type = GV_GVIEW; Value.View = p; // if (Dirty) *Dirty = true; } return *this; } LVariant &LVariant::operator =(LMouse *p) { Empty(); if (p) { Type = GV_LMOUSE; Value.Mouse = p; // if (Dirty) *Dirty = true; } return *this; } LVariant &LVariant::operator =(LKey *p) { Empty(); if (p) { Type = GV_LKEY; Value.Key = p; } return *this; } LVariant &LVariant::operator =(LStream *s) { Empty(); if (s) { Type = GV_STREAM; Value.Stream.Ptr = s; Value.Stream.Own = false; } return *this; } LVariant &LVariant::operator =(LVariant const &i) { if (&i == this) return *this; Empty(); Type = i.Type; switch (Type) { case GV_NULL: { break; } case GV_INT32: { Value.Int = i.Value.Int; break; } case GV_BOOL: { Value.Bool = i.Value.Bool; break; } case GV_INT64: { Value.Int64 = i.Value.Int64; break; } case GV_DOUBLE: { Value.Dbl = i.Value.Dbl; break; } case GV_STRING: { Value.String = NewStr(((LVariant&)i).Str()); break; } case GV_WSTRING: { Value.WString = NewStrW(i.Value.WString); break; } case GV_BINARY: { SetBinary(i.Value.Binary.Length, i.Value.Binary.Data); break; } case GV_LIST: { SetList(i.Value.Lst); break; } case GV_DOM: { Value.Dom = i.Value.Dom; break; } case GV_VOID_PTR: case GV_GVIEW: case GV_LMOUSE: case GV_LKEY: { Value.Ptr = i.Value.Ptr; break; } case GV_DATETIME: { if (i.Value.Date) { Value.Date = new LDateTime; if (Value.Date) { *Value.Date = *i.Value.Date; } } break; } case GV_HASHTABLE: { if ((Value.Hash = new LHash)) { if (i.Value.Hash) { // const char *k; // for (LVariant *var = i.Value.Hash->First(&k); var; var = i.Value.Hash->Next(&k)) for (auto it : *i.Value.Hash) { Value.Hash->Add(it.key, new LVariant(*it.value)); } } } break; } case GV_CUSTOM: { Value.Custom.Data = i.Value.Custom.Data; Value.Custom.Dom = i.Value.Custom.Dom; break; } case GV_LSURFACE: { Value.Surface = i.Value.Surface; if (Value.Surface.Own && Value.Surface.Ptr) Value.Surface.Ptr->IncRef(); break; } /* case GV_GFILE: { Value.File = i.Value.File; if (Value.File.Own && Value.File.Ptr) Value.File.Ptr->AddRef(); break; } */ case GV_STREAM: { Value.Stream.Ptr = i.Value.Stream.Ptr; Value.Stream.Own = false; break; } default: { printf("%s:%i - Unknown variant type '%i'\n", _FL, Type); LAssert(0); break; } } // if (Dirty) *Dirty = true; return *this; } bool LVariant::SetDomRef(LDom *obj, char *name) { Empty(); Type = GV_DOMREF; Value.DomRef.Dom = obj; Value.DomRef.Name = NewStr(name); return Value.DomRef.Name != 0; } bool LVariant::SetBinary(ssize_t Len, void *Data, bool Own) { bool Status = false; Empty(); Type = GV_BINARY; Value.Binary.Length = Len; if (Own) { Value.Binary.Data = Data; Status = true; } else { if ((Value.Binary.Data = new uchar[Value.Binary.Length])) { if (Data) memcpy(Value.Binary.Data, Data, Value.Binary.Length); else memset(Value.Binary.Data, 0, Value.Binary.Length); Status = true; } } return Status; } List *LVariant::SetList(List *Lst) { Empty(); Type = GV_LIST; if ((Value.Lst = new List) && Lst) { for (auto s: *Lst) { LVariant *New = new LVariant; if (New) { *New = *s; Value.Lst->Insert(New); } } } return Value.Lst; } bool LVariant::SetHashTable(LHash *Table, bool Copy) { Empty(); Type = GV_HASHTABLE; if (Copy && Table) { if ((Value.Hash = new LHash)) { // const char *k; // for (LVariant *p = Table->First(&k); p; p = Table->Next(&k)) for (auto i : *Table) { Value.Hash->Add(i.key, i.value); } } } else { Value.Hash = Table ? Table : new LHash; } return Value.Hash != 0; } bool LVariant::SetSurface(class LSurface *Ptr, bool Own) { Empty(); if (!Ptr) return false; Type = GV_LSURFACE; Value.Surface.Ptr = Ptr; if ((Value.Surface.Own = Own)) Value.Surface.Ptr->IncRef(); return true; } bool LVariant::SetStream(class LStreamI *Ptr, bool Own) { Empty(); if (!Ptr) return false; Type = GV_STREAM; Value.Stream.Ptr = Ptr; Value.Stream.Own = Own; return true; } bool LVariant::OwnStr(char *s) { Empty(); if (!s) return false; Type = GV_STRING; Value.String = s; return true; } bool LVariant::OwnStr(char16 *w) { Empty(); if (!w) return false; Type = GV_WSTRING; Value.WString = w; return true; } char *LVariant::ReleaseStr() { char *Ret = Str(); if (Ret) { Value.String = 0; Type = GV_NULL; } return Ret; } LString LVariant::LStr() { return Str(); } char *LVariant::Str() { if (Type == GV_STRING) return Value.String; if (Type == GV_WSTRING) { char *u = WideToUtf8(Value.WString); DeleteArray(Value.WString); Type = GV_STRING; return Value.String = u; } return 0; } char16 *LVariant::ReleaseWStr() { char16 *Ret = WStr(); if (Ret) { Value.WString = 0; Type = GV_NULL; } return Ret; } char16 *LVariant::WStr() { if (Type == GV_WSTRING) return Value.WString; if (Type == GV_STRING) { char16 *w = Utf8ToWide(Value.String); DeleteArray(Value.String); Type = GV_WSTRING; return Value.WString = w; } return 0; } void LVariant::Empty() { switch (Type) { default: break; case GV_CUSTOM: { Value.Custom.Data = 0; Value.Custom.Dom = 0; break; } case GV_DOMREF: { DeleteArray(Value.DomRef.Name); Value.DomRef.Dom = 0; break; } case GV_STRING: { DeleteArray(Value.String); break; } case GV_WSTRING: { DeleteArray(Value.WString); break; } case GV_BINARY: { char *d = (char*) Value.Binary.Data; DeleteArray(d); Value.Binary.Data = 0; break; } case GV_DATETIME: { DeleteObj(Value.Date); break; } case GV_LIST: { if (Value.Lst) { Value.Lst->DeleteObjects(); DeleteObj(Value.Lst); } break; } case GV_HASHTABLE: { if (Value.Hash) { // for (LVariant *v = (LVariant*) Value.Hash->First(); v; v = (LVariant*) Value.Hash->Next()) for (auto i : *Value.Hash) { DeleteObj(i.value); } DeleteObj(Value.Hash); } break; } case GV_LSURFACE: { if (Value.Surface.Own && Value.Surface.Ptr) { Value.Surface.Ptr->DecRef(); Value.Surface.Ptr = NULL; } break; } /* case GV_GFILE: { if (Value.File.Ptr && Value.File.Own) { Value.File.Ptr->DecRef(); Value.File.Ptr = NULL; } break; } */ case GV_STREAM: { if (Value.Stream.Ptr) { if (Value.Stream.Own) delete Value.Stream.Ptr; Value.Stream.Ptr = NULL; } break; } } Type = GV_NULL; ZeroObj(Value); } int64 LVariant::Length() { switch (Type) { case GV_INT32: return sizeof(Value.Int); case GV_INT64: return sizeof(Value.Int64); case GV_BOOL: return sizeof(Value.Bool); case GV_DOUBLE: return sizeof(Value.Dbl); case GV_STRING: return Value.String ? strlen(Value.String) : 0; case GV_BINARY: return Value.Binary.Length; case GV_LIST: { int64 Sz = 0; if (Value.Lst) { for (auto v : *Value.Lst) Sz += v->Length(); } return Sz; } case GV_DOM: { LVariant v; if (Value.Dom) Value.Dom->GetValue("length", v); return v.CastInt32(); } case GV_DOMREF: break; case GV_VOID_PTR: return sizeof(Value.Ptr); case GV_DATETIME: return sizeof(*Value.Date); case GV_HASHTABLE: { int64 Sz = 0; if (Value.Hash) { // for (LVariant *v=Value.Hash->First(); v; v=Value.Hash->Next()) for (auto i : *Value.Hash) Sz += i.value->Length(); } return Sz; } case GV_OPERATOR: return sizeof(Value.Op); case GV_CUSTOM: break; case GV_WSTRING: return Value.WString ? StrlenW(Value.WString) * sizeof(char16) : 0; case GV_LSURFACE: { int64 Sz = 0; if (Value.Surface.Ptr) { LRect r = Value.Surface.Ptr->Bounds(); int Bytes = Value.Surface.Ptr->GetBits() >> 3; Sz = r.X() * r.Y() * Bytes; } return Sz; } case GV_GVIEW: return sizeof(LView); case GV_LMOUSE: return sizeof(LMouse); case GV_LKEY: return sizeof(LKey); case GV_STREAM: return Value.Stream.Ptr->GetSize(); default: break; } return 0; } bool LVariant::IsInt() { return Type == GV_INT32 || Type == GV_INT64; } bool LVariant::IsBool() { return Type == GV_BOOL; } bool LVariant::IsDouble() { return Type == GV_DOUBLE; } bool LVariant::IsString() { return Type == GV_STRING; } bool LVariant::IsBinary() { return Type == GV_BINARY; } bool LVariant::IsNull() { return Type == GV_NULL; } #define IsList() (Type == GV_LIST && Value.Lst) LVariant &LVariant::Cast(LVariantType NewType) { if (NewType != Type) { switch (NewType) { default: { // No conversion possible break; } case GV_INT32: { *this = (int)CastInt32(); break; } case GV_INT64: { *this = (int64_t) CastInt64(); break; } case GV_BOOL: { if (Type == GV_DOUBLE) { *this = Value.Dbl != 0.0; } else { *this = CastInt32() != 0; } break; } case GV_DOUBLE: { *this = CastDouble(); break; } case GV_STRING: { *this = CastString(); break; } case GV_DATETIME: { switch (Type) { case GV_STRING: { // String -> LDateTime LDateTime *Dt = new LDateTime; if (Dt) { Dt->Set(Value.String); Empty(); Value.Date = Dt; Type = NewType; } break; } case GV_INT64: { // Int64 (system date) -> LDateTime LDateTime *Dt = new LDateTime; if (Dt) { Dt->Set((uint64_t)Value.Int64); Empty(); Value.Date = Dt; Type = NewType; } break; } default: { // No conversion available break; } } break; } } } return *this; } void *LVariant::CastVoidPtr() const { switch (Type) { default: break; case GV_STRING: return Value.String; case GV_BINARY: return Value.Binary.Data; case GV_LIST: return Value.Lst; case GV_DOM: return Value.Dom; case GV_DOMREF: return Value.DomRef.Dom; case GV_VOID_PTR: return Value.Ptr; case GV_DATETIME: return Value.Date; case GV_HASHTABLE: return Value.Hash; case GV_CUSTOM: return Value.Custom.Data; case GV_WSTRING: return Value.WString; case GV_LSURFACE: return Value.Surface.Ptr; case GV_GVIEW: return Value.View; case GV_LMOUSE: return Value.Mouse; case GV_LKEY: return Value.Key; } return 0; } LDom *LVariant::CastDom() const { switch (Type) { default: break; case GV_DOM: return Value.Dom; case GV_DOMREF: return Value.DomRef.Dom; case GV_STREAM: return dynamic_cast(Value.Stream.Ptr); case GV_LSURFACE: return Value.Surface.Ptr; case GV_CUSTOM: return Value.Custom.Dom; } return NULL; } bool LVariant::CastBool() const { switch (Type) { default: LAssert(0); break; case GV_NULL: return false; case GV_INT32: return Value.Int != 0; case GV_INT64: return Value.Int64 != 0; case GV_BOOL: return Value.Bool; case GV_DOUBLE: return Value.Dbl != 0.0; case GV_BINARY: return Value.Binary.Data != NULL; case GV_LIST: return Value.Lst != NULL; case GV_DOM: return Value.Dom != NULL; case GV_DOMREF: return Value.DomRef.Dom != NULL; case GV_VOID_PTR: return Value.Ptr != NULL; case GV_GVIEW: return Value.View != NULL; case GV_LMOUSE: return Value.Mouse != NULL; case GV_LKEY: return Value.Key != NULL; case GV_DATETIME: return Value.Date != NULL; case GV_HASHTABLE: return Value.Hash != NULL; case GV_OPERATOR: return Value.Op != OpNull; case GV_CUSTOM: return Value.Custom.Dom != 0 && Value.Custom.Data != 0; /* case GV_GFILE: return Value.File.Ptr != NULL; */ case GV_STREAM: return Value.Stream.Ptr != NULL; // As far as I understand this is the behavour in Python, which I'm using for // a reference to what the "correct" thing to do here is. Basically it's treating // the string like a pointer instead of a value. If the pointer is valid the // conversion to bool return true, and false if it's not a valid pointer. This // means things like if (!StrinLVariant) evaluate correctly in the scripting engine // but it means that if you want to evaluate the value of the varient you should // use CastInt32 instead. case GV_STRING: return ValidStr(Value.String); case GV_WSTRING: return ValidStrW(Value.WString); } return false; } double LVariant::CastDouble() const { switch (Type) { default: break; case GV_BOOL: return Value.Bool ? 1.0 : 0.0; case GV_DOUBLE: return Value.Dbl; case GV_INT32: return (double)Value.Int; case GV_INT64: return (double)Value.Int64; case GV_STRING: return Value.String ? atof(Value.String) : 0; case GV_DOMREF: { static LVariant v; if (Value.DomRef.Dom) { if (Value.DomRef.Dom->GetValue(Value.DomRef.Name, v)) { return v.CastDouble(); } } break; } } return 0; } int32 LVariant::CastInt32() const { switch (Type) { default: break; case GV_BOOL: return (int32)Value.Bool; case GV_DOUBLE: return (int32)Value.Dbl; case GV_INT32: return Value.Int; case GV_INT64: return (int32)Value.Int64; case GV_STRING: if (!Value.String) return 0; if (IsAlpha(Value.String[0])) return !Stricmp(Value.String, "true"); else if (Value.String[0] == '0' && tolower(Value.String[1]) == 'x') return static_cast(Atoi(Value.String, 16)); return atoi(Value.String); case GV_DOM: return Value.Dom != 0; case GV_DOMREF: { static LVariant v; if (Value.DomRef.Dom) { if (Value.DomRef.Dom->GetValue(Value.DomRef.Name, v)) { return v.CastInt32(); } } break; } case GV_LIST: return Value.Lst != NULL; case GV_HASHTABLE: return Value.Hash != NULL; case GV_LSURFACE: return Value.Surface.Ptr != NULL; case GV_GVIEW: return Value.View != NULL; case GV_LMOUSE: return Value.Mouse != NULL; case GV_LKEY: return Value.Key != NULL; case GV_STREAM: return Value.Stream.Ptr != NULL; } return 0; } int64 LVariant::CastInt64() const { switch (Type) { default: break; case GV_BOOL: return (int64)Value.Bool; case GV_DOUBLE: return (int64)Value.Dbl; case GV_INT32: return Value.Int; case GV_INT64: return Value.Int64; case GV_STRING: { if (!Value.String) return 0; if (IsAlpha(Value.String[0])) return !Stricmp(Value.String, "true"); else if (Value.String[0] == '0' && tolower(Value.String[1]) == 'x') return Atoi(Value.String, 16); return Atoi(Value.String); } case GV_DOMREF: { static LVariant v; if (Value.DomRef.Dom) { if (Value.DomRef.Dom->GetValue(Value.DomRef.Name, v)) { return v.CastInt64(); } } break; } } return 0; } char *LVariant::CastString() { char i[40]; switch (Type) { case GV_LIST: { LStringPipe p(256); List::I it = Value.Lst->begin(); bool First = true; p.Print("{"); for (LVariant *v = *it; v; v = *++it) { if (v->Type == GV_STRING || v->Type == GV_WSTRING) p.Print("%s\"%s\"", First ? "" : ", ", v->CastString()); else p.Print("%s%s", First ? "" : ", ", v->CastString()); First = false; } p.Print("}"); OwnStr(p.NewStr()); return Str(); break; } case GV_HASHTABLE: { LStringPipe p(256); p.Print("{"); bool First = true; // const char *k; // for (LVariant *v = Value.Hash->First(&k); v; v = Value.Hash->Next(&k)) for (auto i : *Value.Hash) { p.Print("%s%s = %s", First ? "" : ", ", i.key, i.value->CastString()); First = false; } p.Print("}"); OwnStr(p.NewStr()); return Str(); break; } case GV_DOMREF: { static LVariant v; if (Value.DomRef.Dom) { if (Value.DomRef.Dom->GetValue(Value.DomRef.Name, v)) { return v.CastString(); } } break; } case GV_INT32: { sprintf_s(i, sizeof(i), "%i", Value.Int); *this = i; return Str(); } case GV_DOUBLE: { sprintf_s(i, sizeof(i), "%f", Value.Dbl); *this = i; return Str(); } case GV_BOOL: { sprintf_s(i, sizeof(i), "%i", Value.Bool); *this = i; return Str(); } case GV_INT64: { sprintf_s(i, sizeof(i), LPrintfInt64, Value.Int64); *this = i; return Str(); } case GV_STRING: case GV_WSTRING: { return Str(); } case GV_DATETIME: { if (Value.Date) { char s[64]; Value.Date->Get(s, sizeof(s)); *this = s; return Str(); } break; } case GV_DOM: { sprintf_s(i, sizeof(i), "dom:%p", Value.Dom); *this = i; break; } default: { break; } } return 0; } ///////////////////////////////////////////////////////////////////////////////// LDom *LDom::ResolveObject(const char *Var, LString &Name, LString &Array) { LDom *Object = this; try { // Tokenize the string LString::Array t; for (auto *s = Var; s && *s; ) { const char *e = s; while (*e && *e != '.') { if (*e == '[') { e++; while (*e && *e != ']') { if (*e == '\"' || *e == '\'') { char d = *e++; while (*e && *e != d) e++; if (*e == d) e++; } else e++; } if (*e == ']') e++; } else e++; } LString part = LString(s, e - s).Strip(); if (part.Length() > 0) t.New() = part; // Store non-empty part s = *e ? e + 1 : e; } // Process elements for (int i=0; iGetVariant(Obj, v, Index)) { if (v.Type == GV_LIST) { int N = atoi(Index); LVariant *Element = v.Value.Lst->ItemAt(N); if (Element && Element->Type == GV_DOM) { Object = Element->Value.Dom; } else { return NULL; } } else if (v.Type == GV_DOM) { Object = v.Value.Dom; } else { return NULL; } } else { return NULL; } } else { if (Object->GetVariant(Obj, v) && v.Type == GV_DOM) { Object = v.Value.Dom; } else { return NULL; } } } } } catch (...) { LgiTrace("LDom::ResolveObject crashed: '%s'\n", Var); return NULL; } return Object; } struct LDomPropMap { LHashTbl, LDomProperty> ToProp; LHashTbl, const char *> ToString; LDomPropMap() { #undef _ #define _(symbol, txt) Define(txt, symbol); #include "lgi/common/DomFields.h" #undef _ } void Define(const char *s, LDomProperty p) { if (!s) return; #if defined(_DEBUG) // Check for duplicates. auto existing_prop = ToProp.Find(s); LAssert(existing_prop == ObjNone); auto existing_str = ToString.Find(p); LAssert(existing_str == NULL); #endif ToProp.Add(s, p); ToString.Add(p, s); } } DomPropMap; LDomProperty LStringToDomProp(const char *Str) { return DomPropMap.ToProp.Find(Str); } const char *LDomPropToString(LDomProperty Prop) { return DomPropMap.ToString.Find(Prop); } bool LDom::GetValue(const char *Var, LVariant &Value) { if (!Var) return false; if (!_OnAccess(true)) { LgiTrace("%s:%i - Locking error\n", _FL); LAssert(0); return false; } bool Status = false; LString Name, Arr; LDom *Object = ResolveObject(Var, Name, Arr); if (Object) { if (Name.IsEmpty()) LgiTrace("%s:%i - Warning name parse failed for '%s'\n", _FL, Var); else Status = Object->GetVariant(Name, Value, Arr.IsEmpty() ? NULL : Arr.Get()); } _OnAccess(false); return Status; } bool LDom::SetValue(const char *Var, LVariant &Value) { bool Status = false; if (Var) { // LMutex *Sem = dynamic_cast(this); if (_OnAccess(true)) { LString Name, Arr; LDom *Object = ResolveObject(Var, Name, Arr); if (Object) { if (Name.IsEmpty()) LgiTrace("%s:%i - Warning name parse failed for '%s'\n", _FL, Var); else Status = Object->SetVariant(Name, Value, Arr.IsEmpty() ? NULL : Arr.Get()); } _OnAccess(false); } else { LgiTrace("%s:%i - Locking error\n", _FL); LAssert(0); } } return Status; } bool LVariant::Add(LVariant *v, int Where) { if (!v) { LAssert(!"No value to insert."); return false; } if (Type == GV_NULL) SetList(); if (Type != GV_LIST) { LAssert(!"Not a list variant"); return false; } return Value.Lst->Insert(v, Where); } LString LVariant::ToString() { LString s; switch (Type) { case GV_NULL: s = "NULL"; break; case GV_INT32: s.Printf("(int)%i", Value.Int); break; case GV_INT64: s.Printf("(int64)" LPrintfInt64, Value.Int64); break; case GV_BOOL: s.Printf("(bool)%s", Value.Bool ? "true" : "false"); break; case GV_DOUBLE: s.Printf("(double)%f", Value.Dbl); break; case GV_STRING: s.Printf("(string)\"%s\"", Value.String); break; case GV_BINARY: s.Printf("(binary[%i])%p", Value.Binary.Length, Value.Binary.Data); break; case GV_LIST: s.Printf("(list[%i])%p", Value.Lst?Value.Lst->Length():0, Value.Lst); break; case GV_DOM: s.Printf("(dom)%p", Value.Dom); break; case GV_DOMREF: s.Printf("(dom)%p.%s", Value.DomRef.Dom, Value.DomRef.Name); break; case GV_VOID_PTR: s.Printf("(void*)%p", Value.Ptr); break; case GV_DATETIME: { char dt[64]; Value.Date->Get(dt, sizeof(dt)); s.Printf("(datetime)%s", dt); break; } case GV_HASHTABLE: s.Printf("(hashtbl)%p", Value.Hash); break; case GV_OPERATOR: s.Printf("(operator)%s", OperatorToString(Value.Op)); break; case GV_CUSTOM: s.Printf("(custom.%s)%p", Value.Custom.Dom->GetName(), Value.Custom.Data); break; case GV_WSTRING: s.Printf("(wstring)\"%S\"", Value.WString); break; case GV_LSURFACE: s.Printf("(gsurface)%p", Value.Surface.Ptr); break; case GV_GVIEW: s.Printf("(gview)%p", Value.View); break; case GV_LMOUSE: s.Printf("(gmouse)%p", Value.Mouse); break; case GV_LKEY: s.Printf("(gkey)%p", Value.Key); break; case GV_STREAM: s.Printf("(stream)%p", Value.Stream.Ptr); break; default: s = "(unknown)NULL"; break; } return s; } ///////////////////////////////////////////////////////////////////////////////////////////////////// LCustomType::LCustomType(const char *name, int pack) : FldMap(0, -1) { Name = name; Pack = 1; Size = 0; } LCustomType::LCustomType(const char16 *name, int pack) : FldMap(0, -1) { Name = name; Pack = 1; Size = 0; } LCustomType::~LCustomType() { Flds.DeleteObjects(); Methods.DeleteObjects(); } size_t LCustomType::Sizeof() { return (size_t)PadSize(); } ssize_t LCustomType::PadSize() { if (Pack > 1) { // Bump size to the pack boundary... int Remain = Size % Pack; if (Remain) return Size + Pack - Remain; } return Size; } int LCustomType::IndexOf(const char *Field) { return FldMap.Find(Field); } int LCustomType::AddressOf(const char *Field) { if (!Field) return -1; for (unsigned i=0; iName, Field)) return (int)i; } return -1; } bool LCustomType::DefineField(const char *Name, LCustomType *Type, int ArrayLen) { if (ArrayLen < 1) { LAssert(!"Can't have zero size field."); return false; } if (Name == NULL || Type == NULL) { LAssert(!"Invalid parameter."); return false; } if (FldMap.Find(Name) >= 0) { LAssert(!"Field already exists."); return false; } FldMap.Add(Name, (int)Flds.Length()); CustomField *Def; Flds.Add(Def = new CustomField); Size = PadSize(); Def->Offset = Size; Def->Name = Name; Def->Type = GV_CUSTOM; Def->Bytes = Type->Sizeof(); Def->ArrayLen = ArrayLen; Size += Def->Bytes * ArrayLen; return true; } bool LCustomType::DefineField(const char *Name, LVariantType Type, int Bytes, int ArrayLen) { if (ArrayLen < 1) { LAssert(!"Can't have zero size field."); return false; } if (Name == NULL) { LAssert(!"No field name."); return false; } if (FldMap.Find(Name) >= 0) { LAssert(!"Field already exists."); return false; } FldMap.Add(Name, (int)Flds.Length()); CustomField *Def; Flds.Add(Def = new CustomField); Size = PadSize(); Def->Offset = Size; Def->Name = Name; Def->Type = Type; Def->Bytes = Bytes; Def->ArrayLen = ArrayLen; Size += Bytes * ArrayLen; return true; } LCustomType::Method *LCustomType::GetMethod(const char *Name) { return MethodMap.Find(Name); } LCustomType::Method *LCustomType::DefineMethod(const char *Name, LArray &Params, size_t Address) { Method *m = MethodMap.Find(Name); if (m) { LAssert(!"Method already defined."); return NULL; } Methods.Add(m = new Method); m->Name = Name; m->Params = Params; m->Address = Address; MethodMap.Add(Name, m); return m; } bool LCustomType::CustomField::GetVariant(const char *Field, LVariant &Value, const char *Array) { LDomProperty p = LStringToDomProp(Field); switch (p) { case ObjName: // Type: String Value = Name; break; case ObjLength: // Type: Int32 Value = Bytes; break; default: return false; } return true; } ssize_t LCustomType::CustomField::Sizeof() { switch (Type) { case GV_INT32: return sizeof(int32); case GV_INT64: return sizeof(int64); case GV_BOOL: return sizeof(bool); case GV_DOUBLE: return sizeof(double); case GV_STRING: return sizeof(char); case GV_DATETIME: return sizeof(LDateTime); case GV_HASHTABLE: return sizeof(LVariant::LHash); case GV_OPERATOR: return sizeof(LOperator); case GV_LMOUSE: return sizeof(LMouse); case GV_LKEY: return sizeof(LKey); case GV_CUSTOM: return Nested->Sizeof(); default: LAssert(!"Unknown type."); break; } return 0; } bool LCustomType::Get(int Index, LVariant &Out, uint8_t *This, int ArrayIndex) { if (Index < 0 || Index >= Flds.Length() || !This) { LAssert(!"Invalid parameter error."); return false; } CustomField *Def = Flds[Index]; if (ArrayIndex < 0 || ArrayIndex >= Def->ArrayLen) { LAssert(!"Array out of bounds."); return false; } uint8_t *Ptr = This + Def->Offset; Out.Empty(); switch (Def->Type) { case GV_STRING: { int Len; for (Len = 0; Ptr[Len] && Len < Def->ArrayLen-1; Len++) ; Out.OwnStr(NewStr((char*)Ptr, Len)); break; } case GV_WSTRING: { char16 *p = (char16*)Ptr; int Len; for (Len = 0; p[Len] && Len < Def->ArrayLen-1; Len++) ; Out.OwnStr(NewStrW(p, Len)); break; } case GV_INT32: case GV_INT64: { switch (Def->Bytes) { case 1: { Out.Value.Int = Ptr[ArrayIndex]; Out.Type = GV_INT32; break; } case 2: { Out.Value.Int = ((uint16*)Ptr)[ArrayIndex]; Out.Type = GV_INT32; break; } case 4: { Out.Value.Int = ((uint32_t*)Ptr)[ArrayIndex]; Out.Type = GV_INT32; break; } case 8: { Out.Value.Int64 = ((uint64*)Ptr)[ArrayIndex]; Out.Type = GV_INT64; break; } default: { LAssert(!"Unknown integer size."); return false; } } break; } case GV_MAX: { Out = *((LVariant*)Ptr); break; } default: { LAssert(!"Impl this type."); return false; } } return true; } bool LCustomType::Set(int Index, LVariant &In, uint8_t *This, int ArrayIndex) { if (Index < 0 || Index >= Flds.Length() || !This) { LAssert(!"Invalid parameter error."); return false; } CustomField *Def = Flds[Index]; uint8_t *Ptr = This + Def->Offset; if (ArrayIndex < 0 || ArrayIndex >= Def->ArrayLen) { LAssert(!"Array out of bounds."); return false; } switch (Def->Type) { case GV_STRING: { char *s = In.Str(); if (!s) { *Ptr = 0; break; } if (Def->Bytes == 1) { // Straight up string copy... if (s) strcpy_s((char*)Ptr, Def->ArrayLen, s); else *Ptr = 0; } else if (Def->Bytes == sizeof(char16)) { // utf8 -> wide conversion... const void *In = Ptr; ssize_t Len = strlen(s); ssize_t Ch = LBufConvertCp(Ptr, LGI_WideCharset, Def->ArrayLen-1, In, "utf-8", Len); if (Ch >= 0) { // Null terminate Ptr[Ch] = 0; } else { LAssert(!"LBufConvertCp failed."); return false; } } break; } case GV_WSTRING: { char16 *p = (char16*)Ptr; char16 *w = In.WStr(); if (!w) { *p = 0; break; } if (Def->Bytes == sizeof(char16)) { // Straight string copy... Strcpy(p, Def->ArrayLen, w); } else { // Conversion to utf-8 const void *In = Ptr; ssize_t Len = StrlenW(w) * sizeof(char16); ssize_t Ch = LBufConvertCp(Ptr, "utf-8", Def->ArrayLen-sizeof(char16), In, LGI_WideCharset, Len); if (Ch >= 0) { // Null terminate p[Ch/sizeof(char16)] = 0; } else { LAssert(!"LBufConvertCp failed."); return false; } } break; } case GV_INT32: case GV_INT64: { switch (Def->Bytes) { case 1: { Ptr[ArrayIndex] = In.CastInt32(); break; } case 2: { ((uint16*)Ptr)[ArrayIndex] = In.CastInt32(); break; } case 4: { ((uint32_t*)Ptr)[ArrayIndex] = In.CastInt32(); break; } case 8: { ((uint64*)Ptr)[ArrayIndex] = In.CastInt64(); break; } default: { LAssert(!"Unknown integer size."); return false; } } break; } case GV_MAX: { *((LVariant*)Ptr) = In; break; } default: LAssert(!"Impl this type."); break; } return true; } bool LCustomType::GetVariant(const char *Field, LVariant &Value, const char *Array) { LDomProperty p = LStringToDomProp(Field); switch (p) { case ObjName: // Type: String { Value = Name; return true; } case ObjType: // Type: String { Value = "LCustomType"; return true; } case ObjLength: // Type: Int32 { Value = (int)Sizeof(); return true; } case ObjField: // Type: CustomField[] { if (Array) { int Index = atoi(Array); if (Index >= 0 && Index < Flds.Length()) { Value = (LDom*)&Flds[Index]; return true; } } else { Value = (int)Flds.Length(); break; } break; } default: break; } LAssert(0); return false; } bool LCustomType::SetVariant(const char *Name, LVariant &Value, const char *Array) { LAssert(0); return false; } bool LCustomType::CallMethod(const char *MethodName, LScriptArguments &Args) { if (!MethodName) return false; if (!_stricmp(MethodName, "New")) { Args.GetReturn()->Empty(); Args.GetReturn()->Type = GV_CUSTOM; Args.GetReturn()->Value.Custom.Dom = this; Args.GetReturn()->Value.Custom.Data = new uint8_t[Sizeof()]; return true; } if (!_stricmp(MethodName, "Delete")) // Type: (Object) { for (unsigned i=0; iType == GV_CUSTOM) { DeleteArray(v->Value.Custom.Data); v->Empty(); } } return true; } LAssert(0); return false; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////// LStream LScriptArguments::NullConsole; -LScriptArguments::LScriptArguments(LVirtualMachineI *vm, LVariant *ret, LStream *console) +LScriptArguments::LScriptArguments(LVirtualMachineI *vm, LVariant *ret, LStream *console, ssize_t address) { Vm = vm; + Address = address; if (ret) Return = ret; else Return = LocalReturn = new LVariant; if (console) Console = console; else Console = &NullConsole; } LScriptArguments::~LScriptArguments() { DeleteObjects(); DeleteObj(LocalReturn); } const char *LScriptArguments::StringAt(size_t i) { return IdxCheck(i) ? (*this)[i]->Str() : NULL; } int32_t LScriptArguments::Int32At(size_t i, int32_t Default) { return IdxCheck(i) ? (*this)[i]->CastInt32() : Default; } int64_t LScriptArguments::Int64At(size_t i, int64_t Default) { return IdxCheck(i) ? (*this)[i]->CastInt64() : Default; } double LScriptArguments::DoubleAt(size_t i, double Default) { return IdxCheck(i) ? (*this)[i]->CastDouble() : Default; } bool LScriptArguments::Throw(const char *file, int line, const char *Msg, ...) { if (!Vm) return false; File = file; Line = line; va_list Arg; va_start(Arg, Msg); ExceptionMsg.Printf(Arg, Msg); va_end(Arg); - Vm->OnException(File, Line, -1, ExceptionMsg); + Vm->OnException(File, Line, Address, ExceptionMsg); return true; } diff --git a/src/win/Lgi/View.cpp b/src/win/Lgi/View.cpp --- a/src/win/Lgi/View.cpp +++ b/src/win/Lgi/View.cpp @@ -1,2195 +1,2155 @@ /*hdr ** FILE: LView.cpp ** AUTHOR: Matthew Allen ** DATE: 23/4/98 ** DESCRIPTION: Win32 LView Implementation ** ** Copyright (C) 1998-2003, Matthew Allen ** fret@memecode.com */ #include #include #include "lgi/common/Lgi.h" #include "lgi/common/Base64.h" #include "lgi/common/Com.h" #include "lgi/common/DragAndDrop.h" #include "lgi/common/DropFiles.h" #include "lgi/common/GdiLeak.h" #include "lgi/common/Css.h" #include "lgi/common/Edit.h" #include "lgi/common/LgiRes.h" #include "lgi/common/Menu.h" #include "lgi/common/Thread.h" #include "ViewPriv.h" #define DEBUG_MOUSE_CLICKS 0 #define DEBUG_OVER 0 #define OLD_WM_CHAR_MODE 1 //////////////////////////////////////////////////////////////////////////////////////////////////// bool In_SetWindowPos = false; HWND LViewPrivate::hPrevCapture = 0; LViewPrivate::LViewPrivate(LView *view) : View(view) { WndStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN; IsThemed = LResources::DefaultColours; } LViewPrivate::~LViewPrivate() { while (EventTargets.Length()) delete EventTargets[0]; if (hTheme) { CloseThemeData(hTheme); hTheme = NULL; } if (FontOwnType == GV_FontOwned) { DeleteObj(Font); } } //////////////////////////////////////////////////////////////////////////////////////////////////// // Helper Stuff #include "zmouse.h" int MouseRollMsg = 0; #ifdef __GNUC__ #define MSH_WHEELMODULE_CLASS "MouseZ" #define MSH_WHEELMODULE_TITLE "Magellan MSWHEEL" #define MSH_SCROLL_LINES "MSH_SCROLL_LINES_MSG" #endif int _lgi_mouse_wheel_lines() { UINT nScrollLines; if (SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, (PVOID) &nScrollLines, 0)) return nScrollLines; return 3; } #define SetKeyFlag(v, k, f) if (GetKeyState(k)&0xFF00) { v |= f; } int _lgi_get_key_flags() { int Flags = 0; if (LGetOs() == LGI_OS_WIN9X) { SetKeyFlag(Flags, VK_MENU, LGI_EF_ALT); SetKeyFlag(Flags, VK_SHIFT, LGI_EF_SHIFT); SetKeyFlag(Flags, VK_CONTROL, LGI_EF_CTRL); } else // is NT/2K/XP { SetKeyFlag(Flags, VK_LMENU, LGI_EF_LALT); SetKeyFlag(Flags, VK_RMENU, LGI_EF_RALT); SetKeyFlag(Flags, VK_LSHIFT, LGI_EF_LSHIFT); SetKeyFlag(Flags, VK_RSHIFT, LGI_EF_RSHIFT); SetKeyFlag(Flags, VK_LCONTROL, LGI_EF_LCTRL); SetKeyFlag(Flags, VK_RCONTROL, LGI_EF_RCTRL); } if (GetKeyState(VK_CAPITAL)) SetFlag(Flags, LGI_EF_CAPS_LOCK); return Flags; } //////////////////////////////////////////////////////////////////////////////////////////////////// int GetInputACP() { char16 Str[16]; LCID Lcid = (NativeInt)GetKeyboardLayout(GetCurrentThreadId()) & 0xffff; GetLocaleInfo(Lcid, LOCALE_IDEFAULTANSICODEPAGE, Str, sizeof(Str)); return _wtoi(Str); } -LKey::LKey(int v, uint32_t flags) -{ - const char *Cp = 0; - - vkey = v; - Data = flags; - c16 = 0; - - #if OLD_WM_CHAR_MODE - - c16 = vkey; - - #else - - typedef int (WINAPI *p_ToUnicode)(UINT, UINT, PBYTE, LPWSTR, int, UINT); - - static bool First = true; - static p_ToUnicode ToUnicode = 0; - - if (First) - { - ToUnicode = (p_ToUnicode) GetProcAddress(LoadLibrary("User32.dll"), "ToUnicode"); - First = false; - } - - if (ToUnicode) - { - BYTE state[256]; - GetKeyboardState(state); - char16 w[4]; - int r = ToUnicode(vkey, flags & 0x7f, state, w, CountOf(w), 0); - if (r == 1) - { - c16 = w[0]; - } - } - - #endif -} - //////////////////////////////////////////////////////////////////////////////////////////////////// template bool CastHwnd(T *&Ptr, HWND hWnd) { #if _MSC_VER >= _MSC_VER_VS2005 LONG_PTR user = GetWindowLongPtr(hWnd, GWLP_USERDATA); #else LONG user = GetWindowLong(hWnd, GWL_USERDATA); #endif LONG magic = GetWindowLong(hWnd, GWL_LGI_MAGIC); if (magic != LGI_GViewMagic) { TCHAR ClsName[256] = {0}; int Ch = GetClassName(hWnd, ClsName, CountOf(ClsName)); LString Cls = ClsName; // LgiTrace("%s:%i - Error: hWnd=%p/%s, GWL_LGI_MAGIC=%i\n", _FL, hWnd, Cls.Get(), magic); return false; } Ptr = dynamic_cast((LViewI*)user); return Ptr != NULL; } bool SetLgiMagic(HWND hWnd) { SetLastError(0); LONG res = SetWindowLong(hWnd, GWL_LGI_MAGIC, LGI_GViewMagic); bool Status = res != 0; if (!Status) { DWORD err = GetLastError(); Status = err == 0; } LONG v = GetWindowLong(hWnd, GWL_LGI_MAGIC); // LgiTrace("set LGI_GViewMagic for %p, %i, %i\n", hWnd, Status, v); return Status; } LRESULT CALLBACK LWindowsClass::Redir(HWND hWnd, UINT m, WPARAM a, LPARAM b) { if (m == WM_NCCREATE) { LPCREATESTRUCT Info = (LPCREATESTRUCT) b; LViewI *ViewI = (LViewI*) Info->lpCreateParams; if (ViewI) { LView *View = ViewI->GetGView(); if (View) View->_View = hWnd; #if _MSC_VER >= _MSC_VER_VS2005 SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)ViewI); #else SetWindowLong(hWnd, GWL_USERDATA, (LONG)ViewI); #endif SetLgiMagic(hWnd); } } LViewI *Wnd = (LViewI*) #if _MSC_VER >= _MSC_VER_VS2005 GetWindowLongPtr(hWnd, GWLP_USERDATA); #else GetWindowLong(hWnd, GWL_USERDATA); #endif if (Wnd) { LMessage Msg(m, a, b); Msg.hWnd = hWnd; return Wnd->OnEvent(&Msg); } return DefWindowProcW(hWnd, m, a, b); } LRESULT CALLBACK LWindowsClass::SubClassRedir(HWND hWnd, UINT m, WPARAM a, LPARAM b) { if (m == WM_NCCREATE) { LPCREATESTRUCT Info = (LPCREATESTRUCT) b; LViewI *ViewI = 0; if (Info->lpCreateParams) { if (ViewI = (LViewI*) Info->lpCreateParams) { LView *View = ViewI->GetGView(); if (View) View->_View = hWnd; } } #if _MSC_VER >= _MSC_VER_VS2005 SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) ViewI); #else SetWindowLong(hWnd, GWL_USERDATA, (LONG) ViewI); #endif SetLgiMagic(hWnd); } LViewI *Wnd = (LViewI*) #if _MSC_VER >= _MSC_VER_VS2005 GetWindowLongPtr(hWnd, GWLP_USERDATA); #else GetWindowLong(hWnd, GWL_USERDATA); #endif if (Wnd) { LMessage Msg(m, a, b); Msg.hWnd = hWnd; LMessage::Result Status = Wnd->OnEvent(&Msg); return Status; } return DefWindowProcW(hWnd, m, a, b); } LWindowsClass::LWindowsClass(const char *name) { Name(name); ZeroObj(Class); Class.lpfnWndProc = (WNDPROC) Redir; Class.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW; Class.cbWndExtra = GWL_EXTRA_BYTES; Class.cbSize = sizeof(Class); ParentProc = 0; } LWindowsClass::~LWindowsClass() { UnregisterClassW(NameW(), LProcessInst()); Class.lpszClassName = NULL; } LWindowsClass *LWindowsClass::Create(const char *ClassName) { if (!LAppInst) return NULL; LApp::ClassContainer *Classes = LAppInst->GetClasses(); if (!Classes) return NULL; LWindowsClass *c = Classes->Find(ClassName); if (!c) { c = new LWindowsClass(ClassName); if (c) Classes->Add(ClassName, c); } return c; } bool LWindowsClass::IsSystem(const char *Cls) { if (!_stricmp(Cls, WC_BUTTONA) || !_stricmp(Cls, WC_COMBOBOXA) || !_stricmp(Cls, WC_STATICA)|| !_stricmp(Cls, WC_LISTBOXA)|| !_stricmp(Cls, WC_SCROLLBARA)|| !_stricmp(Cls, WC_HEADERA)|| !_stricmp(Cls, WC_LISTVIEWA)|| !_stricmp(Cls, WC_TREEVIEWA)|| !_stricmp(Cls, WC_COMBOBOXEXA)|| !_stricmp(Cls, WC_TABCONTROLA)|| !_stricmp(Cls, WC_IPADDRESSA)|| !_stricmp(Cls, WC_EDITA)) { return true; } return false; } bool LWindowsClass::Register() { bool Status = false; if (IsSystem(Name())) { ZeroObj(Class); Class.cbSize = sizeof(Class); Status = GetClassInfoExW(LProcessInst(), NameW(), &Class) != 0; LAssert(Status); } else // if (!Class.lpszClassName) { Class.hInstance = LProcessInst(); if (!Class.lpszClassName) Class.lpszClassName = NameW(); Status = RegisterClassExW(&Class) != 0; if (!Status) { auto err = GetLastError(); if (err == 1410) Status = true; else LAssert(Status); } } return Status; } bool LWindowsClass::SubClass(char *Parent) { bool Status = false; if (!Class.lpszClassName) { HBRUSH hBr = Class.hbrBackground; LAutoWString p(Utf8ToWide(Parent)); if (p) { if (GetClassInfoExW(LProcessInst(), p, &Class)) { ParentProc = Class.lpfnWndProc; if (hBr) { Class.hbrBackground = hBr; } Class.cbWndExtra = max(Class.cbWndExtra, GWL_EXTRA_BYTES); Class.hInstance = LProcessInst(); Class.lpfnWndProc = (WNDPROC) SubClassRedir; Class.lpszClassName = NameW(); Status = RegisterClassExW(&Class) != 0; LAssert(Status); } } } else Status = true; return Status; } LRESULT CALLBACK LWindowsClass::CallParent(HWND hWnd, UINT m, WPARAM a, LPARAM b) { if (!ParentProc) return 0; if (IsWindowUnicode(hWnd)) { return CallWindowProcW(ParentProc, hWnd, m, a, b); } else { return CallWindowProcA(ParentProc, hWnd, m, a, b); } } ////////////////////////////////////////////////////////////////////////////// LViewI *LWindowFromHandle(HWND hWnd) { if (hWnd) { SetLastError(0); int32 m = GetWindowLong(hWnd, GWL_LGI_MAGIC); #if 0 //def _DEBUG DWORD err = GetLastError(); if (err == 1413) { TCHAR name[256]; if (GetClassName(hWnd, name, sizeof(name))) { WNDCLASSEX cls; ZeroObj(cls); cls.cbSize = sizeof(WNDCLASSEX); if (GetClassInfoEx(LAppInst->GetInstance(), name, &cls)) { if (cls.cbWndExtra >= 8) { LAssert(!"Really?"); } } } } #endif if (m == LGI_GViewMagic) { return (LViewI*) #if _MSC_VER >= _MSC_VER_VS2005 GetWindowLongPtr(hWnd, GWLP_USERDATA); #else GetWindowLong(hWnd, GWL_USERDATA); #endif } } return 0; } ////////////////////////////////////////////////////////////////////////////// const char *LView::GetClass() { return "LView"; } void LView::_Delete() { if (_View && d->DropTarget) { RevokeDragDrop(_View); } #ifdef _DEBUG // Sanity check.. // LArray HasView; for (auto c: Children) { auto par = c->GetParent(); bool ok = ((LViewI*)par) == this || par == NULL; if (!ok) LAssert(!"heirachy error"); } #endif // Delete myself out of my parent's list if (d->Parent) { d->Parent->OnChildrenChanged(this, false); d->Parent->DelView(this); d->Parent = 0; d->ParentI = 0; } // Delete all children LViewI *c; while (c = Children[0]) { // If it has no parent, remove the pointer from the child list, // Because the child isn't going to do it... if (c->GetParent() == 0) Children.Delete(c); // Delete the child view DeleteObj(c); } // Delete the OS representation of myself if (_View && IsWindow(_View)) { WndFlags |= GWF_DESTRUCTOR; BOOL Status = DestroyWindow(_View); LAssert(Status != 0); } // NULL my handles and flags _View = 0; WndFlags = 0; // Remove static references to myself if (_Over == this) _Over = 0; if (_Capturing == this) { #if DEBUG_CAPTURE LgiTrace("%s:%i - _Capturing %p/%s -> NULL\n", _FL, this, GetClass()); #endif _Capturing = 0; } LWindow *Wnd = GetWindow(); if (Wnd) Wnd->SetFocus(this, LWindow::ViewDelete); // this should only exist in an ex-LWindow, due to the way // C++ deletes objects it needs to be here. DeleteObj(_Lock); } void LView::Quit(bool DontDelete) { if (_View) { if (!DontDelete) { WndFlags |= GWF_QUIT_WND; } DestroyWindow(_View); } } uint32_t LView::GetDlgCode() { return d->WndDlgCode; } void LView::SetDlgCode(uint32_t i) { d->WndDlgCode = i; } uint32_t LView::GetStyle() { return d->WndStyle; } void LView::SetStyle(uint32_t i) { d->WndStyle = i; } uint32_t LView::GetExStyle() { return d->WndExStyle; } void LView::SetExStyle(uint32_t i) { d->WndExStyle = i; } const char *LView::GetClassW32() { return d->WndClass; } void LView::SetClassW32(const char *c) { d->WndClass = c; } LWindowsClass *LView::CreateClassW32(const char *Class, HICON Icon, int AddStyles) { if (Class) { SetClassW32(Class); } if (GetClassW32()) { LWindowsClass *c = LWindowsClass::Create(GetClassW32()); if (c) { if (Icon) { c->Class.hIcon = Icon; } if (AddStyles) { c->Class.style |= AddStyles; } c->Register(); return c; } } return 0; } bool LView::IsAttached() { return _View && IsWindow(_View); } bool LView::Attach(LViewI *p) { bool Status = false; SetParent(p); LView *Parent = d->GetParent(); auto IsWnd = dynamic_cast(this); if (Parent && !_Window) _Window = Parent->_Window; const char *ClsName = GetClassW32(); if (!ClsName) ClsName = GetClass(); if (ClsName) { // Real window with HWND bool Enab = Enabled(); // Check the class is created bool IsSystemClass = LWindowsClass::IsSystem(ClsName); LWindowsClass *Cls = LWindowsClass::Create(ClsName); if (Cls) { auto r = Cls->Register(); if (!r) { LAssert(0); } } else if (!IsSystemClass) return false; if (!IsWnd) LAssert(!Parent || Parent->Handle() != 0); DWORD Style = GetStyle(); DWORD ExStyle = GetExStyle() & ~WS_EX_CONTROLPARENT; if (!TestFlag(WndFlags, GWF_SYS_BORDER)) ExStyle &= ~(WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE); auto Text = LBase::NameW(); LAutoWString WCls(Utf8ToWide(ClsName)); bool hasCaption = (GetStyle() & WS_CAPTION) != 0; int Shadow = WINDOWS_SHADOW_AMOUNT; /* LgiTrace("%p/%s::Attach %s\n", this, Name(), Pos.GetStr()); LRect r = Pos; r.x2 += Shadow; r.y2 += Shadow; LgiTrace(" %s\n", r.GetStr()); */ _View = CreateWindowExW(ExStyle, WCls, Text, Style, Pos.x1, Pos.y1, Pos.X(), Pos.Y(), Parent ? Parent->Handle() : 0, NULL, LProcessInst(), (LViewI*) this); #ifdef _DEBUG if (!_View) { DWORD e = GetLastError(); LgiTrace("%s:%i - CreateWindowExW failed with 0x%x\n", _FL, e); LAssert(!"CreateWindowEx failed"); } #endif if (_View) { Status = (_View != NULL); if (d->Font) SendMessage(_View, WM_SETFONT, (WPARAM) d->Font->Handle(), 0); if (d->DropTarget) RegisterDragDrop(_View, d->DropTarget); if (TestFlag(WndFlags, GWF_FOCUS)) SetFocus(_View); if (d->WantsPulse > 0) { SetPulse(d->WantsPulse); d->WantsPulse = -1; } } OnAttach(); } else { // Virtual window (no HWND) Status = true; } if (Status && d->Parent) { auto isWnd = dynamic_cast(this); if (!isWnd) { if (!d->Parent->HasView(this)) d->Parent->AddView(this); d->Parent->OnChildrenChanged(this, true); } } return Status; } bool LView::Detach() { bool Status = false; if (_Window) { LWindow *Wnd = dynamic_cast(_Window); if (Wnd) Wnd->SetFocus(this, LWindow::ViewDelete); _Window = NULL; } if (d->Parent) { d->Parent->DelView(this); d->Parent->OnChildrenChanged(this, false); d->Parent = 0; d->ParentI = 0; Status = true; WndFlags &= ~GWF_FOCUS; if (_Capturing == this) { if (_View) ReleaseCapture(); #if DEBUG_CAPTURE LgiTrace("%s:%i - _Capturing %p/%s -> NULL\n", _FL, this, GetClass()); #endif _Capturing = 0; } if (_View) { WndFlags &= ~GWF_QUIT_WND; BOOL Status = DestroyWindow(_View); DWORD Err = GetLastError(); LAssert(Status != 0); } } return Status; } LRect &LView::GetClient(bool InClientSpace) { static LRect Client; if (_View) { RECT rc; GetClientRect(_View, &rc); Client = rc; } else { Client.Set(0, 0, Pos.X()-1, Pos.Y()-1); if (dynamic_cast(this) || dynamic_cast(this)) { Client.x1 += GetSystemMetrics(SM_CXFRAME); Client.x2 -= GetSystemMetrics(SM_CXFRAME); Client.y1 += GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION); Client.y2 -= GetSystemMetrics(SM_CYFRAME); } else if (Sunken() || Raised()) { Client.Inset(_BorderSize, _BorderSize); } } if (InClientSpace) Client.Offset(-Client.x1, -Client.y1); return Client; } LCursor LView::GetCursor(int x, int y) { return LCUR_Normal; } #ifndef GCL_HCURSOR #define GCL_HCURSOR -12 #endif bool LgiToWindowsCursor(OsView Hnd, LCursor Cursor) { char16 *Set = 0; switch (Cursor) { case LCUR_UpArrow: Set = IDC_UPARROW; break; case LCUR_Cross: Set = IDC_CROSS; break; case LCUR_Wait: Set = IDC_WAIT; break; case LCUR_Ibeam: Set = IDC_IBEAM; break; case LCUR_SizeVer: Set = IDC_SIZENS; break; case LCUR_SizeHor: Set = IDC_SIZEWE; break; case LCUR_SizeBDiag: Set = IDC_SIZENESW; break; case LCUR_SizeFDiag: Set = IDC_SIZENWSE; break; case LCUR_SizeAll: Set = IDC_SIZEALL; break; case LCUR_PointingHand: { LArray Ver; int Os = LGetOs(&Ver); if ( ( Os == LGI_OS_WIN32 || Os == LGI_OS_WIN64 ) && Ver[0] >= 5) { #ifndef IDC_HAND #define IDC_HAND MAKEINTRESOURCE(32649) #endif Set = IDC_HAND; } // else not supported break; } case LCUR_Forbidden: Set = IDC_NO; break; // Not impl case LCUR_SplitV: break; case LCUR_SplitH: break; case LCUR_Blank: break; } HCURSOR cur = LoadCursor(0, Set ? Set : IDC_ARROW); SetCursor(cur); if (Hnd) SetWindowLongPtr(Hnd, GCL_HCURSOR, (LONG_PTR)cur); return true; } bool LView::PointToScreen(LPoint &p) { POINT pt = {p.x, p.y}; LViewI *t = this; while ( t && t->GetParent() && !t->Handle()) { pt.x += t->GetPos().x1; pt.y += t->GetPos().y1; t = t->GetParent(); } ClientToScreen(t->Handle(), &pt); p.x = pt.x; p.y = pt.y; return true; } bool LView::PointToView(LPoint &p) { POINT pt = {p.x, p.y}; LViewI *t = this; while ( t && t->GetParent() && !t->Handle()) { pt.x -= t->GetPos().x1; pt.y -= t->GetPos().y1; t = t->GetParent(); } ScreenToClient(t->Handle(), &pt); p.x = pt.x; p.y = pt.y; return true; } bool LView::GetMouse(LMouse &m, bool ScreenCoords) { // position POINT p; GetCursorPos(&p); if (!ScreenCoords) { ScreenToClient(_View, &p); } m.x = p.x; m.y = p.y; m.Target = this; // buttons m.Flags = ((GetAsyncKeyState(VK_LBUTTON)&0x8000) ? LGI_EF_LEFT : 0) | ((GetAsyncKeyState(VK_MBUTTON)&0x8000) ? LGI_EF_MIDDLE : 0) | ((GetAsyncKeyState(VK_RBUTTON)&0x8000) ? LGI_EF_RIGHT : 0) | ((GetAsyncKeyState(VK_CONTROL)&0x8000) ? LGI_EF_CTRL : 0) | ((GetAsyncKeyState(VK_MENU) &0x8000) ? LGI_EF_ALT : 0) | ((GetAsyncKeyState(VK_LWIN) &0x8000) ? LGI_EF_SYSTEM : 0) | ((GetAsyncKeyState(VK_RWIN) &0x8000) ? LGI_EF_SYSTEM : 0) | ((GetAsyncKeyState(VK_SHIFT) &0x8000) ? LGI_EF_SHIFT : 0); if (m.Flags & (LGI_EF_LEFT | LGI_EF_MIDDLE | LGI_EF_RIGHT)) { m.Flags |= LGI_EF_DOWN; } return true; } bool LView::SetPos(LRect &p, bool Repaint) { bool Status = true; LRect OldPos = Pos; if (Pos != p) { Pos = p; if (_View) { HWND hOld = GetFocus(); bool WasVis = IsWindowVisible(_View) != 0; int Shadow = WINDOWS_SHADOW_AMOUNT; In_SetWindowPos = true; Status = SetWindowPos( _View, NULL, Pos.x1, Pos.y1, Pos.X() + Shadow, Pos.Y() + Shadow, // ((Repaint) ? 0 : SWP_NOREDRAW) | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER) != 0; In_SetWindowPos = false; } else if (GetParent()) { OnPosChange(); } if (Repaint) { Invalidate(); } } return Status; } bool LView::Invalidate(LRect *r, bool Repaint, bool Frame) { if (_View) { bool Status = false; if (Frame) { RedrawWindow( _View, NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN | ((Repaint) ? RDW_UPDATENOW : 0)); } else { if (r) { Status = InvalidateRect(_View, &((RECT)*r), false) != 0; } else { RECT c = GetClient(); Status = InvalidateRect(_View, &c, false) != 0; } } if (Repaint) { UpdateWindow(_View); } return Status; } else { LRect Up; LViewI *p = this; if (r) { Up = *r; } else { Up.Set(0, 0, Pos.X()-1, Pos.Y()-1); } if (dynamic_cast(this)) return true; while (p && !p->Handle()) { LViewI *Par = p->GetParent(); LView *VPar = Par?Par->GetGView():0; LRect w = p->GetPos(); LRect c = p->GetClient(false); if (Frame && p == this) Up.Offset(w.x1, w.y1); else Up.Offset(w.x1 + c.x1, w.y1 + c.y1); p = Par; } if (p && p->Handle()) { return p->Invalidate(&Up, Repaint); } } return false; } void CALLBACK LView::TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, uint32_t dwTime) { LView *View = (LView*) idEvent; if (View) { View->OnPulse(); } } void LView::SetPulse(int Length) { if (_View) { if (Length > 0) { d->TimerId = SetTimer(_View, (UINT_PTR) this, Length, (TIMERPROC) TimerProc); } else { KillTimer(_View, d->TimerId); d->TimerId = 0; } } else { d->WantsPulse = Length; } } static int ConsumeTabKey = 0; bool SysOnKey(LView *w, LMessage *m) { if (m->a == VK_TAB && (m->m == WM_KEYDOWN || m->m == WM_SYSKEYDOWN) ) { if (!TestFlag(w->d->WndDlgCode, DLGC_WANTTAB) && !TestFlag(w->d->WndDlgCode, DLGC_WANTALLKEYS)) { // push the focus to the next control bool Shifted = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0; LViewI *Wnd = GetNextTabStop(w, Shifted); if (Wnd) { if (In_SetWindowPos) { assert(0); LgiTrace("%s:%i - SetFocus(%p)\\n", _FL, Wnd->Handle()); } ConsumeTabKey = 2; ::SetFocus(Wnd->Handle()); return true; } } } return false; } #ifdef _MSC_VER #include "vsstyle.h" void LView::DrawThemeBorder(LSurface *pDC, LRect &r) { if (!d->hTheme) d->hTheme = OpenThemeData(_View, VSCLASS_EDIT); if (d->hTheme) { RECT rc = r; int StateId; if (!Enabled()) StateId = EPSN_DISABLED; else if (GetFocus() == _View) StateId = EPSN_FOCUSED; else StateId = EPSN_NORMAL; // LgiTrace("ThemeDraw %s: %i\n", GetClass(), StateId); RECT clip[4]; clip[0] = LRect(r.x1, r.y1, r.x1 + 1, r.y2); // left clip[1] = LRect(r.x1 + 2, r.y1, r.x2 - 2, r.y1 + 1); // top clip[2] = LRect(r.x2 - 1, r.y1, r.x2, r.y2); // right clip[3] = LRect(r.x1 + 2, r.y2 - 1, r.x2 - 2, r.y2); // bottom LColour cols[4] = { LColour(255, 0, 0), LColour(0, 255, 0), LColour(0, 0, 255), LColour(255, 255, 0) }; for (int i=0; iColour(cols[i]); pDC->Rectangle(&tmp); #else DrawThemeBackground(d->hTheme, pDC->Handle(), EP_EDITBORDER_NOSCROLL, StateId, &rc, &clip[i]); #endif } pDC->Colour(L_MED); pDC->Set(r.x1, r.y1); pDC->Set(r.x2, r.y1); pDC->Set(r.x1, r.y2); pDC->Set(r.x2, r.y2); r.Inset(2, 2); } else { LWideBorder(pDC, r, Sunken() ? DefaultSunkenEdge : DefaultRaisedEdge); d->IsThemed = false; } } #else void LView::DrawThemeBorder(LSurface *pDC, LRect &r) { LWideBorder(pDC, r, DefaultSunkenEdge); } #endif bool IsKeyChar(LKey &k, int vk) { if (k.Ctrl() || k.Alt() || k.System()) return false; switch (vk) { case VK_BACK: case VK_TAB: case VK_RETURN: case VK_SPACE: case 0xba: // ; case 0xbb: // = case 0xbc: // , case 0xbd: // - case 0xbe: // . case 0xbf: // / case 0xc0: // ` case 0xdb: // [ case 0xdc: // | case 0xdd: // ] case 0xde: // ' return true; } if (vk >= VK_NUMPAD0 && vk <= VK_DIVIDE) return true; if (vk >= '0' && vk <= '9') return true; if (vk >= 'A' && vk <= 'Z') return true; return false; } #define KEY_FLAGS (~(MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) LMessage::Result LView::OnEvent(LMessage *Msg) { int Status = 0; if (Msg->Msg() == MouseRollMsg) { HWND hFocus = GetFocus(); if (_View) { int Flags = ((GetKeyState(VK_SHIFT)&0xF000) ? VK_SHIFT : 0) | ((GetKeyState(VK_CONTROL)&0xF000) ? VK_CONTROL : 0); PostMessage(hFocus, WM_MOUSEWHEEL, MAKELONG(Flags, (short)Msg->a), Msg->b); } return 0; } for (auto target: d->EventTargets) { if (target->Msgs.Length() == 0 || target->Msgs.Find(Msg->Msg())) target->OnEvent(Msg); } if (_View) { switch (Msg->m) { #if 1 case WM_CTLCOLORBTN: case WM_CTLCOLOREDIT: case WM_CTLCOLORSTATIC: { HDC hdc = (HDC)Msg->A(); HWND hwnd = (HWND)Msg->B(); LViewI *v = FindControl(hwnd); LView *gv = v ? v->GetGView() : NULL; if (gv) { int Depth = dynamic_cast(gv) ? 1 : 10; LColour Fore = gv->StyleColour(LCss::PropColor, LColour(), Depth); LColour Back = gv->StyleColour(LCss::PropBackgroundColor, LColour(), Depth); if (Fore.IsValid()) { COLORREF c = RGB(Fore.r(), Fore.g(), Fore.b()); SetTextColor(hdc, c); } if (Back.IsValid()) { COLORREF c = RGB(Back.r(), Back.g(), Back.b()); SetBkColor(hdc, c); SetDCBrushColor(hdc, c); } if (Fore.IsValid() || Back.IsValid()) { #if !defined(DC_BRUSH) #define DC_BRUSH 18 #endif return (LRESULT) GetStockObject(DC_BRUSH); } } goto ReturnDefaultProc; return 0; } #endif case 5700: { // I forget what this is for... break; } case WM_ERASEBKGND: { return 1; } case WM_GETFONT: { LFont *f = GetFont(); if (!f || f == LSysFont) return (LMessage::Result) LSysFont->Handle(); return (LMessage::Result) f->Handle(); break; } case WM_MENUCHAR: case WM_MEASUREITEM: { return LMenu::_OnEvent(Msg); break; } case WM_DRAWITEM: { DRAWITEMSTRUCT *di = (DRAWITEMSTRUCT*)Msg->B(); if (di) { if (di->CtlType == ODT_MENU) { return LMenu::_OnEvent(Msg); } /* else if (di->CtlType == ODT_BUTTON) { LView *b; if (CastHwnd(b, di->hwndItem) && b->GetCss()) { LScreenDC dc(di->hDC, di->hwndItem); switch (di->itemAction) { case ODA_DRAWENTIRE: { LRect c = di->rcItem; LMemDC m(c.X(), c.Y(), GdcD->GetColourSpace()); HDC hdc = m.StartDC(); m.Colour(LColour(255, 0, 255)); m.Line(0, 0, m.X()-1, m.Y()-1); LONG s = GetWindowLong(_View, GWL_STYLE); SetWindowLong(_View, GWL_STYLE, (s & ~BS_TYPEMASK) | BS_PUSHBUTTON); SendMessage(_View, WM_PRINT, (WPARAM)hdc, PRF_ERASEBKGND|PRF_CLIENT); SetWindowLong(_View, GWL_STYLE, (s & ~BS_TYPEMASK) | BS_OWNERDRAW); m.EndDC(); dc.Blt(0, 0, &m); break; } case ODA_FOCUS: { break; } case ODA_SELECT: { break; } } return true; } } */ } if (!(WndFlags & GWF_DIALOG)) goto ReturnDefaultProc; break; } case WM_ENABLE: { Invalidate(&Pos); break; } case WM_HSCROLL: case WM_VSCROLL: { LViewI *Wnd = FindControl((HWND) Msg->b); if (Wnd) { Wnd->OnEvent(Msg); } break; } case WM_GETDLGCODE: { // we handle all tab control stuff return DLGC_WANTALLKEYS; // d->WndDlgCode | DLGC_WANTTAB; } case WM_MOUSEWHEEL: { // short fwKeys = LOWORD(Msg->a); // key flags short zDelta = (short) HIWORD(Msg->a); // wheel rotation int nScrollLines = - _lgi_mouse_wheel_lines(); double Lines = ((double)zDelta * (double)nScrollLines) / WHEEL_DELTA; if (ABS(Lines) < 1.0) Lines *= 1.0 / ABS(Lines); // LgiTrace("Lines = %g, zDelta = %i, nScrollLines = %i\n", Lines, zDelta, nScrollLines); // Try giving the event to the current window... if (!OnMouseWheel(Lines)) { // Find the window under the cursor... and try giving it the mouse wheel event short xPos = (short) LOWORD(Msg->b); // horizontal position of pointer short yPos = (short) HIWORD(Msg->b); // vertical position of pointer POINT Point = {xPos, yPos}; HWND hUnder = ::WindowFromPoint(Point); HWND hParent = ::GetParent(hUnder); if (hUnder && hUnder != _View && // Don't want to send ourselves a message... hParent != _View) // WM_MOUSEWHEEL will propagate back up to us and cause an infinite loop { // Do a post event in case the window is deleting... at least it won't crash. PostMessage(hUnder, Msg->m, Msg->a, Msg->b); } } return 0; } case M_CHANGE: { LWindow *w = GetWindow(); LAutoPtr note((LNotification*)Msg->B()); LViewI *Ctrl = w ? w->FindControl((int)Msg->a) : 0; if (Ctrl) { LAssert(note.Get() != NULL); return OnNotify(Ctrl, note ? *note : LNotifyNull); } else { LgiTrace("Ctrl %i not found.\n", Msg->a); } break; } case M_COMMAND: { // LViewI *Ci = FindControl((HWND) Msg->b); // LView *Ctrl = Ci ? Ci->GetGView() : 0; LView *Ctrl; if (Msg->b && CastHwnd(Ctrl, (HWND)Msg->b)) { short Code = HIWORD(Msg->a); switch (Code) { case CBN_CLOSEUP: { PostMessage(_View, WM_COMMAND, MAKELONG(Ctrl->GetId(), CBN_EDITCHANGE), Msg->b); break; } case CBN_EDITCHANGE: // COMBO { Ctrl->SysOnNotify(Msg->Msg(), Code); OnNotify(Ctrl, LNotifyValueChanged); break; } /* case BN_CLICKED: // BUTTON case EN_CHANGE: // EDIT */ default: { Ctrl->SysOnNotify(Msg->Msg(), Code); break; } } } break; } case WM_NCDESTROY: { #if _MSC_VER >= _MSC_VER_VS2005 SetWindowLongPtr(_View, GWLP_USERDATA, 0); #else SetWindowLong(_View, GWL_USERDATA, 0); #endif _View = NULL; if (WndFlags & GWF_QUIT_WND) { delete this; } break; } case WM_CLOSE: { if (OnRequestClose(false)) { Quit(); } break; } case WM_DESTROY: { OnDestroy(); break; } case WM_CREATE: { SetId(d->CtrlId); LWindow *w = GetWindow(); if (w && w->GetFocus() == this) { HWND hCur = GetFocus(); if (hCur != _View) { if (In_SetWindowPos) { assert(0); LgiTrace("%s:%i - SetFocus(%p) (%s)\\n", __FILE__, __LINE__, Handle(), GetClass()); } SetFocus(_View); } } if (TestFlag(GViewFlags, GWF_DROP_TARGET)) { DropTarget(true); } OnCreate(); break; } case WM_SETFOCUS: { LWindow *w = GetWindow(); if (w) { w->SetFocus(this, LWindow::GainFocus); } else { // This can happen in popup sub-trees of views. Where the focus // is tracked separately from the main LWindow. OnFocus(true); Invalidate((LRect*)NULL, false, true); } break; } case WM_KILLFOCUS: { LWindow *w = GetWindow(); if (w) { w->SetFocus(this, LWindow::LoseFocus); } else { // This can happen when the LWindow is being destroyed Invalidate((LRect*)NULL, false, true); OnFocus(false); } break; } case WM_WINDOWPOSCHANGED: { if (!IsIconic(_View)) { WINDOWPOS *Info = (LPWINDOWPOS) Msg->b; if (Info) { if (Info->x == -32000 && Info->y == -32000) { #if 0 LgiTrace("WM_WINDOWPOSCHANGED %i,%i,%i,%i (icon=%i)\\n", Info->x, Info->y, Info->cx, Info->cy, IsIconic(Handle())); #endif } else { int Shadow = WINDOWS_SHADOW_AMOUNT; LRect r; r.ZOff(Info->cx-1, Info->cy-1); r.Offset(Info->x, Info->y); if (r.Valid() && r != Pos) { Pos = r; /* LgiTrace("%p/%s::PosChange %s\n", this, Name(), Pos.GetStr()); Pos.x2 -= Shadow; Pos.y2 -= Shadow; LgiTrace(" %s\n", Pos.GetStr()); */ } } } OnPosChange(); } if (!(WndFlags & GWF_DIALOG)) { goto ReturnDefaultProc; } break; } case WM_CAPTURECHANGED: { LViewI *Wnd; if (Msg->B() && CastHwnd(Wnd, (HWND)Msg->B())) { if (Wnd != _Capturing) { #if DEBUG_CAPTURE LgiTrace("%s:%i - _Capturing %p/%s -> %p/%s\n", _FL, _Capturing, _Capturing?_Capturing->GetClass():0, Wnd, Wnd?Wnd->GetClass() : 0); #endif _Capturing = Wnd; } } else if (_Capturing) { #if DEBUG_CAPTURE LgiTrace("%s:%i - _Capturing %p/%s -> NULL\n", _FL, _Capturing, _Capturing?_Capturing->GetClass():0); #endif _Capturing = NULL; } break; } case M_MOUSEENTER: { LMouse Ms; Ms.Target = this; Ms.x = (short) (Msg->b&0xFFFF); Ms.y = (short) (Msg->b>>16); Ms.Flags = 0; LViewI *MouseOver = WindowFromPoint(Ms.x, Ms.y); if (MouseOver && _Over != MouseOver && !(MouseOver == this || MouseOver->Handle() == 0)) { if (_Capturing) { if (MouseOver == _Capturing) { Ms = lgi_adjust_click(Ms, _Capturing); _Capturing->OnMouseEnter(Ms); } } else { if (_Over) { LMouse m = lgi_adjust_click(Ms, _Over); _Over->OnMouseExit(m); #if DEBUG_OVER LgiTrace("Enter.LoseOver=%p/%s '%-20s'\n", _Over, _Over->GetClass(), _Over->Name()); #endif } _Over = MouseOver; if (_Over) { #if DEBUG_OVER LgiTrace("Enter.GetOver=%p/%s '%-20s'\n", _Over, _Over->GetClass(), _Over->Name()); #endif LMouse m = lgi_adjust_click(Ms, _Over); _Over->OnMouseEnter(m); } } } break; } case M_MOUSEEXIT: { if (_Over) { LMouse Ms; Ms.Target = this; Ms.x = (short) (Msg->b&0xFFFF); Ms.y = (short) (Msg->b>>16); Ms.Flags = 0; bool Mine = false; if (_Over->Handle()) { Mine = _Over == this; } else { for (LViewI *o = _Capturing ? _Capturing : _Over; o; o = o->GetParent()) { if (o == this) { Mine = true; break; } } } if (Mine) { if (_Capturing) { LMouse m = lgi_adjust_click(Ms, _Capturing); _Capturing->OnMouseExit(m); } else { #if DEBUG_OVER LgiTrace("Exit.LoseOver=%p '%-20s'\n", _Over, _Over->Name()); #endif _Over->OnMouseExit(Ms); _Over = 0; } } } break; } case WM_MOUSEMOVE: { LMouse Ms; Ms.Target = this; Ms.x = (short) (Msg->b&0xFFFF); Ms.y = (short) (Msg->b>>16); Ms.Flags = _lgi_get_key_flags(); Ms.IsMove(true); if (TestFlag(Msg->a, MK_LBUTTON)) SetFlag(Ms.Flags, LGI_EF_LEFT); if (TestFlag(Msg->a, MK_RBUTTON)) SetFlag(Ms.Flags, LGI_EF_RIGHT); if (TestFlag(Msg->a, MK_MBUTTON)) SetFlag(Ms.Flags, LGI_EF_MIDDLE); SetKeyFlag(Ms.Flags, VK_MENU, MK_ALT); Ms.Down((Msg->a & (MK_LBUTTON|MK_MBUTTON|MK_RBUTTON)) != 0); LViewI *MouseOver = WindowFromPoint(Ms.x, Ms.y); if (_Over != MouseOver) { if (_Over) { #if DEBUG_OVER LgiTrace("Move.LoseOver=%p/%s '%-20s'\n", _Over, _Over->GetClass(), _Over->Name()); #endif LMouse m = lgi_adjust_click(Ms, _Over); _Over->OnMouseExit(m); } _Over = MouseOver; if (_Over) { LMouse m = lgi_adjust_click(Ms, _Over); _Over->OnMouseEnter(m); #if DEBUG_OVER LgiTrace("Move.GetOver=%p/%s '%-20s'\n", _Over, _Over->GetClass(), _Over->Name()); #endif } } // int CurX = Ms.x, CurY = Ms.y; LCursor Cursor = (_Over ? _Over : this)->GetCursor(Ms.x, Ms.y); LgiToWindowsCursor(_View, Cursor); #if 0 LgiTrace("WM_MOUSEMOVE %i,%i target=%p/%s, over=%p/%s, cap=%p/%s\n", Ms.x, Ms.y, Ms.Target, Ms.Target?Ms.Target->GetClass():0, _Over, _Over?_Over->GetClass():0, _Capturing, _Capturing?_Capturing->GetClass():0); #endif if (_Capturing) Ms = lgi_adjust_click(Ms, _Capturing, true); else if (_Over) Ms = lgi_adjust_click(Ms, _Over); else return 0; LWindow *Wnd = GetWindow(); if (!Wnd || Wnd->HandleViewMouse(dynamic_cast(Ms.Target), Ms)) { Ms.Target->OnMouseMove(Ms); } break; } case WM_NCHITTEST: { POINT Pt = { LOWORD(Msg->b), HIWORD(Msg->b) }; ScreenToClient(_View, &Pt); int Hit = OnHitTest(Pt.x, Pt.y); if (Hit >= 0) { // LgiTrace("%I64i Hit=%i\n", LCurrentTime(), Hit); return Hit; } if (!(WndFlags & GWF_DIALOG)) { goto ReturnDefaultProc; } break; } case WM_LBUTTONDBLCLK: case WM_LBUTTONDOWN: case WM_LBUTTONUP: { LMouse Ms; Ms.x = (short) (Msg->b&0xFFFF); Ms.y = (short) (Msg->b>>16); Ms.Flags = _lgi_get_key_flags() | LGI_EF_LEFT; Ms.Down(Msg->m != WM_LBUTTONUP); Ms.Double(Msg->m == WM_LBUTTONDBLCLK); if (_Capturing) Ms = lgi_adjust_click(Ms, _Capturing, true); else if (_Over) Ms = lgi_adjust_click(Ms, _Over); else Ms.Target = this; #if DEBUG_MOUSE_CLICKS LString Msg; Msg.Printf("%s.Click", Ms.Target->GetClass()); Ms.Trace(Msg); #endif LWindow *Wnd = GetWindow(); if (!Wnd || Wnd->HandleViewMouse(dynamic_cast(Ms.Target), Ms)) Ms.Target->OnMouseClick(Ms); break; } case WM_RBUTTONDBLCLK: case WM_RBUTTONDOWN: case WM_RBUTTONUP: { LMouse Ms; Ms.x = (short) (Msg->b&0xFFFF); Ms.y = (short) (Msg->b>>16); Ms.Flags = _lgi_get_key_flags() | LGI_EF_RIGHT; Ms.Down(Msg->m != WM_RBUTTONUP); Ms.Double(Msg->m == WM_RBUTTONDBLCLK); if (_Capturing) Ms = lgi_adjust_click(Ms, _Capturing, true); else if (_Over) Ms = lgi_adjust_click(Ms, _Over); else Ms.Target = this; #if DEBUG_MOUSE_CLICKS LString Msg; Msg.Printf("%s.Click", Ms.Target->GetClass()); Ms.Trace(Msg); #endif LWindow *Wnd = GetWindow(); if (!Wnd || Wnd->HandleViewMouse(dynamic_cast(Ms.Target), Ms)) Ms.Target->OnMouseClick(Ms); break; } case WM_MBUTTONDBLCLK: case WM_MBUTTONDOWN: case WM_MBUTTONUP: { LMouse Ms; Ms.x = (short) (Msg->b&0xFFFF); Ms.y = (short) (Msg->b>>16); Ms.Flags = _lgi_get_key_flags() | LGI_EF_MIDDLE; Ms.Down(Msg->m != WM_MBUTTONUP); Ms.Double(Msg->m == WM_MBUTTONDBLCLK); if (_Capturing) Ms = lgi_adjust_click(Ms, _Capturing, true); else if (_Over) Ms = lgi_adjust_click(Ms, _Over); else Ms.Target = this; LWindow *Wnd = GetWindow(); if (!Wnd || Wnd->HandleViewMouse(dynamic_cast(Ms.Target), Ms)) Ms.Target->OnMouseClick(Ms); break; } case WM_XBUTTONDBLCLK: case WM_XBUTTONDOWN: case WM_XBUTTONUP: { LMouse Ms; int Clicked = (Msg->a >> 16) & 0xffff; Ms.x = (short) (Msg->b&0xFFFF); Ms.y = (short) (Msg->b>>16); Ms.Flags = _lgi_get_key_flags() | LGI_EF_MIDDLE; Ms.Button1(TestFlag(Clicked, XBUTTON1)); Ms.Button2(TestFlag(Clicked, XBUTTON2)); Ms.Down(Msg->m != WM_XBUTTONUP); Ms.Double(Msg->m == WM_XBUTTONDBLCLK); if (_Capturing) Ms = lgi_adjust_click(Ms, _Capturing, true); else if (_Over) Ms = lgi_adjust_click(Ms, _Over); else Ms.Target = this; LWindow *Wnd = GetWindow(); if (!Wnd || Wnd->HandleViewMouse(dynamic_cast(Ms.Target), Ms)) Ms.Target->OnMouseClick(Ms); break; } case WM_SYSKEYUP: case WM_SYSKEYDOWN: case WM_KEYDOWN: case WM_KEYUP: { static char AltCode[32]; bool IsDialog = TestFlag(WndFlags, GWF_DIALOG); bool IsDown = Msg->m == WM_KEYDOWN || Msg->m == WM_SYSKEYDOWN; int KeyFlags = _lgi_get_key_flags(); HWND hwnd = _View; if (SysOnKey(this, Msg)) { // LgiTrace("SysOnKey true, Msg=0x%x %x,%x\n", Msg->m, Msg->a, Msg->b); return 0; } else { // Key LKey Key((int)Msg->a, (int)Msg->b); Key.Flags = KeyFlags; Key.Down(IsDown); Key.IsChar = false; if (Key.Ctrl()) { Key.c16 = (char16)Msg->a; } if (Key.c16 == VK_TAB && ConsumeTabKey) { ConsumeTabKey--; } else { LWindow *Wnd = GetWindow(); if (Wnd) { if (Key.Alt() || Key.Ctrl() || (Key.c16 < 'A' || Key.c16 > 'Z')) { Wnd->HandleViewKey(this, Key); } } else { OnKey(Key); } } if (Msg->m == WM_SYSKEYUP || Msg->m == WM_SYSKEYDOWN) { if (Key.vkey >= VK_F1 && Key.vkey <= VK_F12 && Key.Alt() == false) { // So in LgiIde if you press F10 (debug next) you get a hang // sometimes in DefWindowProc. Until I figure out what's going // on this code exits before calling DefWindowProc without // breaking other WM_SYSKEY* functionality (esp Alt+F4). return 0; } } } if (!IsDialog) { // required for Alt-Key function (eg Alt-F4 closes window) goto ReturnDefaultProc; } break; } #if OLD_WM_CHAR_MODE case WM_CHAR: { LKey Key((int)Msg->a, (int)Msg->b); Key.Flags = _lgi_get_key_flags(); Key.Down(true); Key.IsChar = true; bool Shift = Key.Shift(); bool Caps = TestFlag(Key.Flags, LGI_EF_CAPS_LOCK); if (!(Shift ^ Caps)) { Key.c16 = ToLower(Key.c16); } else { Key.c16 = ToUpper(Key.c16); } if (Key.c16 == LK_TAB && ConsumeTabKey) { ConsumeTabKey--; } else { LWindow *Wnd = GetWindow(); if (Wnd) { Wnd->HandleViewKey(this, Key); } else { OnKey(Key); } } break; } #endif case M_SET_WND_STYLE: { SetWindowLong(Handle(), GWL_STYLE, (LONG)Msg->b); SetWindowPos( Handle(), 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOZORDER | SWP_NOSIZE | SWP_FRAMECHANGED); break; } case WM_PAINT: { _Paint(); break; } case WM_NCPAINT: { if (GetWindow() != this && !TestFlag(WndFlags, GWF_SYS_BORDER)) { HDC hDC = GetWindowDC(_View); LScreenDC Dc(hDC, _View, true); LRect p(0, 0, Dc.X()-1, Dc.Y()-1); OnNcPaint(&Dc, p); } goto ReturnDefaultProc; break; } case WM_NCCALCSIZE: { LMessage::Param Status = 0; int Edge = (Sunken() || Raised()) ? _BorderSize : 0; RECT *rc = NULL; if (Msg->a) { NCCALCSIZE_PARAMS *p = (NCCALCSIZE_PARAMS*) Msg->b; rc = p->rgrc; } else { rc = (RECT*)Msg->b; } if (!(WndFlags & GWF_DIALOG)) { Status = DefWindowProcW(_View, Msg->m, Msg->a, Msg->b); } if (Edge && rc && !TestFlag(WndFlags, GWF_SYS_BORDER)) { rc->left += Edge; rc->top += Edge; rc->right -= Edge; rc->bottom -= Edge; return 0; } return Status; } case WM_NOTIFY: { NMHDR *Hdr = (NMHDR*)Msg->B(); if (Hdr) { LView *Wnd; if (CastHwnd(Wnd, Hdr->hwndFrom)) Wnd->SysOnNotify(Msg->Msg(), Hdr->code); } break; } case M_THREAD_COMPLETED: { auto Th = (LThread*)Msg->A(); if (!Th) break; Th->OnComplete(); if (Th->GetDeleteOnExit()) delete Th; return true; } default: { if (!(WndFlags & GWF_DIALOG)) goto ReturnDefaultProc; break; } } } return 0; ReturnDefaultProc: #ifdef _DEBUG uint64 start = LCurrentTime(); #endif LRESULT r = DefWindowProcW(_View, Msg->m, Msg->a, Msg->b); #ifdef _DEBUG uint64 now = LCurrentTime(); if (now - start > 1000) { LgiTrace("DefWindowProc(0x%.4x, %i, %i) took %ims\n", Msg->m, Msg->a, Msg->b, (int)(now - start)); } #endif return r; } LViewI *LView::FindControl(OsView hCtrl) { if (_View == hCtrl) { return this; } for (List::I i = Children.begin(); i.In(); i++) { LViewI *Ctrl = (*i)->FindControl(hCtrl); if (Ctrl) return Ctrl; } return 0; }