From 9055a011418be4c15c50702fcb0f54536dcc9ca3 Mon Sep 17 00:00:00 2001 From: Yui <50331474+SirYodaJedi@users.noreply.github.com> Date: Mon, 24 Mar 2025 17:15:38 -0400 Subject: [PATCH 01/42] Replicate %alphatexture from Gmod --- sp/src/utils/vrad/vradstaticprops.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/sp/src/utils/vrad/vradstaticprops.cpp b/sp/src/utils/vrad/vradstaticprops.cpp index f4f04a69..ebd77326 100644 --- a/sp/src/utils/vrad/vradstaticprops.cpp +++ b/sp/src/utils/vrad/vradstaticprops.cpp @@ -528,9 +528,17 @@ public: if ( pVMT->LoadFromBuffer( pMaterialName, buf ) ) { bFound = true; - if ( pVMT->FindKey("$translucent") || pVMT->FindKey("$alphatest") ) + if ( pVMT->FindKey("$alphatest") || pVMT->FindKey("$translucent") || pVMT->FindKey("%alphatexture") ) { - KeyValues *pBaseTexture = pVMT->FindKey("$basetexture"); + KeyValues *pBaseTexture = NULL; + if ( pVMT->FindKey("%alphatexture") ) + { + pBaseTexture = pVMT->FindKey("%alphatexture"); + } + else + { + pBaseTexture = pVMT->FindKey("$basetexture"); + } if ( pBaseTexture ) { const char *pBaseTextureName = pBaseTexture->GetString(); From bb209357c2606a912d29ed41e0305ff60318ac7f Mon Sep 17 00:00:00 2001 From: Yui <50331474+SirYodaJedi@users.noreply.github.com> Date: Mon, 24 Mar 2025 20:19:30 -0400 Subject: [PATCH 02/42] VRAD - reduce duplicate %alphatexture searches Per comment on https://github.com/ValveSoftware/source-sdk-2013/pull/1150 by @ficool2 --- sp/src/utils/vrad/vradstaticprops.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/sp/src/utils/vrad/vradstaticprops.cpp b/sp/src/utils/vrad/vradstaticprops.cpp index ebd77326..3a812da6 100644 --- a/sp/src/utils/vrad/vradstaticprops.cpp +++ b/sp/src/utils/vrad/vradstaticprops.cpp @@ -528,14 +528,10 @@ public: if ( pVMT->LoadFromBuffer( pMaterialName, buf ) ) { bFound = true; - if ( pVMT->FindKey("$alphatest") || pVMT->FindKey("$translucent") || pVMT->FindKey("%alphatexture") ) + KeyValues *pBaseTexture = pVMT->FindKey("%alphatexture"); + if ( pBaseTexture || pVMT->FindKey("$alphatest") || pVMT->FindKey("$translucent") ) { - KeyValues *pBaseTexture = NULL; - if ( pVMT->FindKey("%alphatexture") ) - { - pBaseTexture = pVMT->FindKey("%alphatexture"); - } - else + if ( !pBaseTexture ) { pBaseTexture = pVMT->FindKey("$basetexture"); } From 10a936f55d91c332d9d42295412ea85ef9119b84 Mon Sep 17 00:00:00 2001 From: Zeldaboy14 Date: Sun, 30 Mar 2025 20:53:40 -0500 Subject: [PATCH 03/42] Fix Lightmappedgeneric and vbsp matrix functions Shiro assisted in this fix, and now we don't need the custom matrixinvert.h functions anymore, because we use built-in engine functions for inversing Parallax corrected cubemaps. LightmappedGeneric has also been updated so mat_specular 0 doesn't break brushs anymore --- .../lightmappedgeneric_dx9_helper.cpp | 4 +- sp/src/utils/vbsp/map.cpp | 23 ++-- sp/src/utils/vbsp/matrixinvert.h | 117 ------------------ 3 files changed, 14 insertions(+), 130 deletions(-) delete mode 100644 sp/src/utils/vbsp/matrixinvert.h diff --git a/sp/src/materialsystem/stdshaders/lightmappedgeneric_dx9_helper.cpp b/sp/src/materialsystem/stdshaders/lightmappedgeneric_dx9_helper.cpp index 2273db49..edd6f05c 100644 --- a/sp/src/materialsystem/stdshaders/lightmappedgeneric_dx9_helper.cpp +++ b/sp/src/materialsystem/stdshaders/lightmappedgeneric_dx9_helper.cpp @@ -805,8 +805,8 @@ void DrawLightmappedGeneric_DX9_Internal(CBaseVSShader *pShader, IMaterialVar** (params[info.m_nBlendModulateTexture]->IsTexture() ); bool hasNormalMapAlphaEnvmapMask = IS_FLAG_SET( MATERIAL_VAR_NORMALMAPALPHAENVMAPMASK ); #ifdef PARALLAX_CORRECTED_CUBEMAPS - // Parallax cubemaps - bool hasParallaxCorrection = params[info.m_nEnvmapParallax]->GetIntValue() > 0; + // Parallax cubemaps. Check for envmap because if we don't, white splotchs can appear at certain viewing angles when mat_specular is 0. + bool hasParallaxCorrection = params[info.m_nEnvmap]->IsDefined() && params[info.m_nEnvmapParallax]->GetIntValue() > 0; #endif if ( hasFlashlight && !IsX360() ) diff --git a/sp/src/utils/vbsp/map.cpp b/sp/src/utils/vbsp/map.cpp index c479b279..d8bce641 100644 --- a/sp/src/utils/vbsp/map.cpp +++ b/sp/src/utils/vbsp/map.cpp @@ -15,9 +15,6 @@ #include "materialsub.h" #include "fgdlib/fgdlib.h" #include "manifest.h" -#ifdef PARALLAX_CORRECTED_CUBEMAPS -#include "matrixinvert.h" -#endif #ifdef MAPBASE_VSCRIPT #include "vscript_vbsp.h" #endif @@ -1657,9 +1654,11 @@ ChunkFileResult_t CMapFile::LoadEntityCallback(CChunkFile *pFile, int nParam) // if (!strcmp("parallax_obb", pClassName)) { - matrix3x4_t obbMatrix, invObbMatrix; - SetIdentityMatrix(obbMatrix); - SetIdentityMatrix(invObbMatrix); + //Originally was matrix3x2. Now we use built-in functions to the engine instead of a custom invert matrix + VMatrix obbMatrix, invObbMatrix; + MatrixSetIdentity(obbMatrix); + MatrixSetIdentity(invObbMatrix); + // Get corner and its 3 edges (scaled, local x, y, and z axes) mapbrush_t *brush = &mapbrushes[mapent->firstbrush]; @@ -1714,13 +1713,15 @@ ChunkFileResult_t CMapFile::LoadEntityCallback(CChunkFile *pFile, int nParam) x *= abs(DotProduct(diag, x)); // Build transformation matrix (what is needed to turn a [0,0,0] - [1,1,1] cube into this brush) - MatrixSetColumn(x, 0, obbMatrix); - MatrixSetColumn(y, 1, obbMatrix); - MatrixSetColumn(z, 2, obbMatrix); - MatrixSetColumn(corner, 3, obbMatrix); + //Originally was MatrixSetColum. Since we use VMatrix now, changed to obbMatrix + obbMatrix.SetForward(x); + obbMatrix.SetLeft(y); + obbMatrix.SetUp(z); + obbMatrix.SetTranslation(corner); //find inverse (we need the world to local matrix, "transformationmatrix" is kind of a misnomer) - MatrixInversion(obbMatrix, invObbMatrix); + //Originally was MatrixInversion. This is now using the built in functions, not relying on MatrixInversion and matrixinvert.h anymore + MatrixInverseGeneral(obbMatrix, invObbMatrix); break; } diff --git a/sp/src/utils/vbsp/matrixinvert.h b/sp/src/utils/vbsp/matrixinvert.h deleted file mode 100644 index 6e676ad3..00000000 --- a/sp/src/utils/vbsp/matrixinvert.h +++ /dev/null @@ -1,117 +0,0 @@ -// By Jason Yu-Tseh Chi -// From http://chi3x10.wordpress.com/2008/05/28/calculate-matrix-inversion-in-c/ -// Modified to work with valve's matrix_3x4_t - -#include "mathlib\mathlib.h" - -// Calculate the cofactor of element (row,col) -int GetMatrixMinor(float **src, float **dest, int row, int col, int order) -{ - // Indicate which col and row is being copied to dest - int colCount = 0, rowCount = 0; - - for (int i = 0; i < order; i++) - { - if (i != row) - { - colCount = 0; - for (int j = 0; j < order; j++) - { - // When j is not the element - if (j != col) - { - dest[rowCount][colCount] = src[i][j]; - colCount++; - } - } - rowCount++; - } - } - - return 1; -} - -// Calculate the determinant recursively. -double CalcMatrixDeterminant(float **mat, int order) -{ - // Order must be >= 0 - // Stop the recursion when matrix is a single element - if (order == 1) - return mat[0][0]; - - // The determinant value - float det = 0; - - // Allocate the cofactor matrix - float **minor; - minor = new float*[order - 1]; - for (int i = 0; i Date: Sat, 1 Mar 2025 21:30:10 +0300 Subject: [PATCH 04/42] Update sqdbg --- sp/src/vscript/sqdbg/LICENSE | 19 + sp/src/vscript/sqdbg/include/sqdbg.h | 7 +- sp/src/vscript/sqdbg/sqdbg/json.h | 359 +- sp/src/vscript/sqdbg/sqdbg/net.h | 3 + sp/src/vscript/sqdbg/sqdbg/protocol.h | 70 +- sp/src/vscript/sqdbg/sqdbg/server.cpp | 4854 ++++++++++++++++--------- sp/src/vscript/sqdbg/sqdbg/str.h | 283 +- sp/src/vscript/sqdbg/sqdbg/vec.h | 293 +- 8 files changed, 3691 insertions(+), 2197 deletions(-) create mode 100644 sp/src/vscript/sqdbg/LICENSE diff --git a/sp/src/vscript/sqdbg/LICENSE b/sp/src/vscript/sqdbg/LICENSE new file mode 100644 index 00000000..2b607699 --- /dev/null +++ b/sp/src/vscript/sqdbg/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) samisalreadytaken + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sp/src/vscript/sqdbg/include/sqdbg.h b/sp/src/vscript/sqdbg/include/sqdbg.h index fe1415d4..aa0cd6ef 100644 --- a/sp/src/vscript/sqdbg/include/sqdbg.h +++ b/sp/src/vscript/sqdbg/include/sqdbg.h @@ -60,9 +60,14 @@ SQDBG_API int sqdbg_listen_socket( HSQDEBUGSERVER dbg, unsigned short port ); SQDBG_API void sqdbg_frame( HSQDEBUGSERVER dbg ); // Copies the script to be able to source it to debugger clients -SQDBG_API void sqdbg_on_script_compile( HSQDEBUGSERVER dbg, const SQChar *script, SQInteger size, +SQDBG_API void sqdbg_on_script_compile( HSQDEBUGSERVER dbg, + const SQChar *script, SQInteger scriptlen, const SQChar *sourcename, SQInteger sourcenamelen ); +// Check if a client is connected to the debugger +// Returns 0 if there is no client connected +SQDBG_API int sqdbg_is_client_connected( HSQDEBUGSERVER dbg ); + #ifdef __cplusplus } #endif diff --git a/sp/src/vscript/sqdbg/sqdbg/json.h b/sp/src/vscript/sqdbg/sqdbg/json.h index 45808d00..d42a5365 100644 --- a/sp/src/vscript/sqdbg/sqdbg/json.h +++ b/sp/src/vscript/sqdbg/sqdbg/json.h @@ -62,12 +62,12 @@ class json_array_t { public: const char *m_pBase; - CScratch< JSON_SCRATCH_CHUNK_SIZE > *m_Allocator; + CScratch< true, JSON_SCRATCH_CHUNK_SIZE > *m_Allocator; int *m_Elements; unsigned short m_nElementCount; unsigned short m_nElementsSize; - void Init( const char *base, CScratch< JSON_SCRATCH_CHUNK_SIZE > *allocator ) + void Init( const char *base, CScratch< true, JSON_SCRATCH_CHUNK_SIZE > *allocator ) { m_pBase = base; m_Allocator = allocator; @@ -96,7 +96,7 @@ public: return ret; } - int size() const + int Size() const { return m_nElementCount; } @@ -138,12 +138,12 @@ class json_table_t { public: const char *m_pBase; - CScratch< JSON_SCRATCH_CHUNK_SIZE > *m_Allocator; + CScratch< true, JSON_SCRATCH_CHUNK_SIZE > *m_Allocator; int *m_Elements; unsigned short m_nElementCount; unsigned short m_nElementsSize; - void Init( const char *base, CScratch< JSON_SCRATCH_CHUNK_SIZE > *allocator ) + void Init( const char *base, CScratch< true, JSON_SCRATCH_CHUNK_SIZE > *allocator ) { m_pBase = base; m_Allocator = allocator; @@ -266,11 +266,20 @@ public: bool Get( const string_t &key, json_array_t **out ) const { return GetArray( key, out ); } }; -static inline void PutStrL( CBuffer *buffer, const string_t &str ) +static inline void PutStr( CBuffer *buffer, const string_t &str ) { - buffer->_base.Ensure( buffer->size() + str.len ); - memcpy( buffer->base() + buffer->size(), str.ptr, str.len ); - buffer->_size += str.len; + buffer->base.Ensure( buffer->Size() + str.len ); + memcpy( buffer->Base() + buffer->Size(), str.ptr, str.len ); + buffer->size += str.len; + +#ifdef SQDBG_VALIDATE_SENT_MSG + for ( unsigned int i = 0; i < str.len; i++ ) + { + AssertMsg( IN_RANGE_CHAR( str.ptr[i], 0x20, 0x7E ) && + ( ( str.ptr[i] != '\\' && str.ptr[i] != '\"' ) || ( i && str.ptr[i-1] == '\\' ) ), + "control char in json string" ); + } +#endif } static inline void PutStr( CBuffer *buffer, const string_t &str, bool quote ) @@ -278,7 +287,7 @@ static inline void PutStr( CBuffer *buffer, const string_t &str, bool quote ) const char *c = str.ptr; unsigned int i = str.len; - unsigned int len = 2 + i; + unsigned int len = i; if ( quote ) len += 4; @@ -287,18 +296,19 @@ static inline void PutStr( CBuffer *buffer, const string_t &str, bool quote ) { switch ( *c ) { - case '\"': case '\\': case '\b': - case '\f': case '\n': case '\r': case '\t': + case '\\': case '\"': + case '\a': case '\b': case '\f': + case '\n': case '\r': case '\t': case '\v': len++; if ( quote ) { len++; - if ( *c == '\"' || *c == '\\' ) + if ( *c == '\\' || *c == '\"' ) len++; } break; default: - if ( !IN_RANGE_CHAR( *(unsigned char*)c, 0x20, 0x7E ) ) + if ( !IN_RANGE_CHAR( *c, 0x20, 0x7E ) ) { int ret = IsValidUTF8( (unsigned char*)c, i + 1 ); if ( ret != 0 ) @@ -321,16 +331,14 @@ static inline void PutStr( CBuffer *buffer, const string_t &str, bool quote ) } } - buffer->_base.Ensure( buffer->size() + len ); + buffer->base.Ensure( buffer->Size() + len ); - char *mem = buffer->base(); - unsigned int idx = buffer->size(); + char *mem = buffer->Base(); + unsigned int idx = buffer->Size(); c = str.ptr; i = str.len; - mem[idx++] = '\"'; - if ( quote ) { mem[idx++] = '\\'; @@ -343,6 +351,7 @@ static inline void PutStr( CBuffer *buffer, const string_t &str, bool quote ) switch ( *c ) { + case '\\': case '\"': mem[idx-1] = '\\'; if ( quote ) @@ -350,15 +359,13 @@ static inline void PutStr( CBuffer *buffer, const string_t &str, bool quote ) mem[idx++] = '\\'; mem[idx++] = '\\'; } - mem[idx++] = '\"'; + mem[idx++] = *c; break; - case '\\': + case '\a': + mem[idx-1] = '\\'; if ( quote ) - { mem[idx++] = '\\'; - mem[idx++] = '\\'; - } - mem[idx++] = '\\'; + mem[idx++] = 'a'; break; case '\b': mem[idx-1] = '\\'; @@ -390,8 +397,14 @@ static inline void PutStr( CBuffer *buffer, const string_t &str, bool quote ) mem[idx++] = '\\'; mem[idx++] = 't'; break; + case '\v': + mem[idx-1] = '\\'; + if ( quote ) + mem[idx++] = '\\'; + mem[idx++] = 'v'; + break; default: - if ( !IN_RANGE_CHAR( *(unsigned char*)c, 0x20, 0x7E ) ) + if ( !IN_RANGE_CHAR( *c, 0x20, 0x7E ) ) { int ret = IsValidUTF8( (unsigned char*)c, i + 1 ); if ( ret != 0 ) @@ -410,7 +423,7 @@ static inline void PutStr( CBuffer *buffer, const string_t &str, bool quote ) mem[idx++] = 'u'; idx += printhex< true, false >( mem + idx, - buffer->capacity() - idx, + buffer->Capacity() - idx, (uint16_t)*(unsigned char*)c ); } else @@ -419,7 +432,7 @@ static inline void PutStr( CBuffer *buffer, const string_t &str, bool quote ) mem[idx++] = 'x'; idx += printhex< true, false >( mem + idx, - buffer->capacity() - idx, + buffer->Capacity() - idx, (SQChar)*(unsigned char*)c ); } } @@ -433,9 +446,7 @@ static inline void PutStr( CBuffer *buffer, const string_t &str, bool quote ) mem[idx++] = '\"'; } - mem[idx++] = '\"'; - - buffer->_size = idx; + buffer->size = idx; } #ifdef SQUNICODE @@ -445,64 +456,126 @@ static inline void PutStr( CBuffer *buffer, const sqstring_t &str, bool quote ) if ( !quote ) { - len = 2 + UTF8Length< kUTFEscapeJSON >( str.ptr, str.len ); + len = UTF8Length< kUTFEscapeJSON >( str.ptr, str.len ); } else { - len = 2 + UTF8Length< kUTFEscapeQuoted >( str.ptr, str.len ); + len = UTF8Length< kUTFEscapeQuoted >( str.ptr, str.len ); } - buffer->_base.Ensure( buffer->size() + len ); - buffer->base()[buffer->_size++] = '\"'; + buffer->base.Ensure( buffer->Size() + len ); if ( !quote ) { len = SQUnicodeToUTF8< kUTFEscapeJSON >( - buffer->base() + buffer->size(), - buffer->capacity() - buffer->size(), + buffer->Base() + buffer->Size(), + buffer->Capacity() - buffer->Size(), str.ptr, str.len ); } else { len = SQUnicodeToUTF8< kUTFEscapeQuoted >( - buffer->base() + buffer->size(), - buffer->capacity() - buffer->size(), + buffer->Base() + buffer->Size(), + buffer->Capacity() - buffer->Size(), str.ptr, str.len ); } - buffer->_size += len; - buffer->base()[buffer->_size++] = '\"'; + buffer->size += len; } #endif static inline void PutChar( CBuffer *buffer, char c ) { - buffer->_base.Ensure( buffer->size() + 1 ); - buffer->base()[buffer->_size++] = c; + buffer->base.Ensure( buffer->Size() + 1 ); + buffer->Base()[buffer->size++] = c; } template < typename I > -static inline void PutInt( CBuffer *buffer, I val, bool hex = false ) +static inline void PutInt( CBuffer *buffer, I val ) { - int len; - buffer->_base.Ensure( buffer->size() + countdigits( val ) + 1 ); - - if ( !hex ) - { - len = printint( buffer->base() + buffer->size(), buffer->capacity() - buffer->size(), val ); - } - else - { - len = printhex< false >( buffer->base() + buffer->size(), buffer->capacity() - buffer->size(), val ); - } - - buffer->_size += len; + buffer->base.Ensure( buffer->Size() + countdigits( val ) + 1 ); + int len = printint( buffer->Base() + buffer->Size(), buffer->Capacity() - buffer->Size(), val ); + buffer->size += len; } -class wjson_table_t; -class wjson_array_t; +template < bool padding, typename I > +static inline void PutHex( CBuffer *buffer, I val ) +{ + buffer->base.Ensure( buffer->Size() + countdigits<16>( val ) + 1 ); + int len = printhex< padding >( buffer->Base() + buffer->Size(), buffer->Capacity() - buffer->Size(), val ); + buffer->size += len; +} + +struct jstringbuf_t +{ + CBuffer *m_pBuffer; + + jstringbuf_t( CBuffer *b ) : m_pBuffer(b) + { + ::PutChar( m_pBuffer, '\"' ); + } + + ~jstringbuf_t() + { + ::PutChar( m_pBuffer, '\"' ); + } + + jstringbuf_t( const jstringbuf_t &src ); + + void Seek( int i ) + { + m_pBuffer->size += i; + } + + template < int SIZE > + void Puts( const char (&str)[SIZE] ) + { + ::PutStr( m_pBuffer, str ); + } + + void Puts( const conststring_t &str ) + { + ::PutStr( m_pBuffer, str ); + } + + void Puts( const string_t &str, bool quote = false ) + { + ::PutStr( m_pBuffer, str, quote ); + } + +#ifdef SQUNICODE + void Puts( const sqstring_t &str, bool quote = false ) + { + ::PutStr( m_pBuffer, str, quote ); + } +#endif + + void Put( char c ) + { + ::PutChar( m_pBuffer, c ); + } + + template < typename I > + void PutInt( I val ) + { + ::PutInt( m_pBuffer, val ); + } + + template < typename I > + void PutHex( I val, bool padding = true ) + { + if ( padding ) + { + ::PutHex< true >( m_pBuffer, val ); + } + else + { + ::PutHex< false >( m_pBuffer, val ); + } + } +}; class wjson_t { @@ -542,7 +615,7 @@ public: PutChar( m_pBuffer, ',' ); PutChar( m_pBuffer, '\"' ); - PutStrL( m_pBuffer, key ); + PutStr( m_pBuffer, key ); PutChar( m_pBuffer, '\"' ); PutChar( m_pBuffer, ':' ); } @@ -556,26 +629,53 @@ public: void SetNull( const string_t &key ) { PutKey( key ); - PutStrL( m_pBuffer, "null" ); + PutStr( m_pBuffer, "null" ); } void SetBool( const string_t &key, bool val ) { PutKey( key ); - PutStrL( m_pBuffer, val ? string_t("true") : string_t("false") ); + PutStr( m_pBuffer, val ? string_t("true") : string_t("false") ); + } + + jstringbuf_t SetStringAsBuf( const string_t &key ) + { + PutKey( key ); + return { m_pBuffer }; + } + + template < int SIZE > + void SetString( const string_t &key, const char (&val)[SIZE] ) + { + PutKey( key ); + PutChar( m_pBuffer, '\"' ); + PutStr( m_pBuffer, val ); + PutChar( m_pBuffer, '\"' ); + } + + void SetString( const string_t &key, const conststring_t &val ) + { + PutKey( key ); + PutChar( m_pBuffer, '\"' ); + PutStr( m_pBuffer, val ); + PutChar( m_pBuffer, '\"' ); } void SetString( const string_t &key, const string_t &val, bool quote = false ) { PutKey( key ); + PutChar( m_pBuffer, '\"' ); PutStr( m_pBuffer, val, quote ); + PutChar( m_pBuffer, '\"' ); } #ifdef SQUNICODE void SetString( const string_t &key, const sqstring_t &val, bool quote = false ) { PutKey( key ); + PutChar( m_pBuffer, '\"' ); PutStr( m_pBuffer, val, quote ); + PutChar( m_pBuffer, '\"' ); } #endif @@ -593,7 +693,14 @@ public: PutKey( key ); PutChar( m_pBuffer, '\"' ); PutChar( m_pBuffer, '[' ); - PutInt( m_pBuffer, val, hex ); + if ( !hex ) + { + PutInt( m_pBuffer, val ); + } + else + { + PutHex< false >( m_pBuffer, val ); + } PutChar( m_pBuffer, ']' ); PutChar( m_pBuffer, '\"' ); } @@ -637,7 +744,7 @@ public: wjson_array_t( const wjson_array_t &src ); - int size() + int Size() { return m_nElementCount; } @@ -665,7 +772,7 @@ public: PutChar( m_pBuffer, ',' ); PutChar( m_pBuffer, '\"' ); - PutStrL( m_pBuffer, val ); + PutStr( m_pBuffer, val ); PutChar( m_pBuffer, '\"' ); } }; @@ -676,7 +783,7 @@ private: char *m_cur; char *m_end; char *m_start; - CScratch< JSON_SCRATCH_CHUNK_SIZE > *m_Allocator; + CScratch< true, JSON_SCRATCH_CHUNK_SIZE > *m_Allocator; char *m_error; enum @@ -693,7 +800,7 @@ private: }; public: - JSONParser( CScratch< JSON_SCRATCH_CHUNK_SIZE > *allocator, char *ptr, int len, json_table_t *pTable ) : + JSONParser( CScratch< true, JSON_SCRATCH_CHUNK_SIZE > *allocator, char *ptr, int len, json_table_t *pTable ) : m_cur( ptr ), m_end( ptr + len + 1 ), m_start( ptr ), @@ -710,7 +817,7 @@ public: } else { - SetError( "expected '%c', got '0x%02x' @ %i", '{', Char(type), Index() ); + SetError( "expected '%c', got %s @ %i", '{', Char(type), Index() ); } } @@ -725,12 +832,30 @@ private: return m_cur - m_start; } - unsigned char Char( char token ) + char *Char( char token ) { - if ( token != Token_Error ) - return (unsigned char)token; + char *buf; - return *(unsigned char*)m_cur; + if ( token == Token_Error ) + token = *m_cur; + + if ( IN_RANGE_CHAR( token, 0x20, 0x7E ) ) + { + buf = m_Allocator->Alloc(4); + buf[0] = '\''; + buf[1] = token; + buf[2] = '\''; + buf[3] = 0; + } + else + { + buf = m_Allocator->Alloc(5); + int i = printhex< true, true, false >( buf, 5, token ); + Assert( i == 4 ); + buf[i] = 0; + } + + return buf; } void SetError( const char *fmt, ... ) @@ -776,37 +901,37 @@ private: return *m_cur++; case 't': - if ( m_cur + 4 >= m_end || - m_cur[1] != 'r' || m_cur[2] != 'u' || m_cur[3] != 'e' ) + if ( m_cur + 4 < m_end && + m_cur[1] == 'r' && m_cur[2] == 'u' && m_cur[3] == 'e' ) { - SetError( "expected %s @ %i", "\"true\"", Index() ); - return Token_Error; + m_cur += 4; + return Token_True; } - m_cur += 4; - return Token_True; + SetError( "expected %s @ %i", "\"true\"", Index() ); + return Token_Error; case 'f': - if ( m_cur + 5 >= m_end || - m_cur[1] != 'a' || m_cur[2] != 'l' || m_cur[3] != 's' || m_cur[4] != 'e' ) + if ( m_cur + 5 < m_end && + m_cur[1] == 'a' && m_cur[2] == 'l' && m_cur[3] == 's' && m_cur[4] == 'e' ) { - SetError( "expected %s @ %i", "\"false\"", Index() ); - return Token_Error; + m_cur += 5; + return Token_False; } - m_cur += 5; - return Token_False; + SetError( "expected %s @ %i", "\"false\"", Index() ); + return Token_Error; case 'n': - if ( m_cur + 4 >= m_end || - m_cur[1] != 'u' || m_cur[2] != 'l' || m_cur[3] != 'l' ) + if ( m_cur + 4 < m_end && + m_cur[1] == 'u' && m_cur[2] == 'l' && m_cur[3] == 'l' ) { - SetError( "expected %s @ %i", "\"null\"", Index() ); - return Token_Error; + m_cur += 4; + return Token_Null; } - m_cur += 4; - return Token_Null; + SetError( "expected %s @ %i", "\"null\"", Index() ); + return Token_Error; default: return Token_Error; @@ -829,12 +954,10 @@ private: return Token_Error; } - // end if ( *m_cur == '\"' ) { - *m_cur = 0; token.Assign( pStart, m_cur - pStart ); - m_cur++; + *m_cur++ = 0; break; } @@ -857,7 +980,7 @@ private: // Defer unescape until the end of the string is found switch ( *m_cur ) { - case '\"': case '\\': case '/': + case '\\': case '\"': case '/': case 'b': case 'f': case 'n': case 'r': case 't': m_cur++; @@ -876,7 +999,7 @@ private: break; default: - SetError( "invalid escape char '0x%02x' @ %i", *(unsigned char*)m_cur, Index() ); + SetError( "invalid escape char 0x%02x @ %i", *(unsigned char*)m_cur, Index() ); return Token_Error; } } @@ -895,6 +1018,7 @@ private: } #define _shift( bytesWritten, bytesRead ) \ + Assert( (bytesWritten) < (bytesRead) ); \ memmove( cur + (bytesWritten), cur + (bytesRead), end - ( cur + (bytesRead) ) ); \ cur += (bytesWritten); \ end -= (bytesRead) - (bytesWritten); @@ -903,22 +1027,21 @@ private: { case '\\': shift_one: - _shift( 0, 1 ); - cur++; + _shift( 1, 2 ); break; - case '\"': goto shift_one; - case '/': goto shift_one; - case 'b': cur[1] = '\b'; goto shift_one; - case 'f': cur[1] = '\f'; goto shift_one; - case 'n': cur[1] = '\n'; goto shift_one; - case 'r': cur[1] = '\r'; goto shift_one; - case 't': cur[1] = '\t'; goto shift_one; + case '\"': cur[0] = '\"'; goto shift_one; + case '/': cur[0] = '/'; goto shift_one; + case 'b': cur[0] = '\b'; goto shift_one; + case 'f': cur[0] = '\f'; goto shift_one; + case 'n': cur[0] = '\n'; goto shift_one; + case 'r': cur[0] = '\r'; goto shift_one; + case 't': cur[0] = '\t'; goto shift_one; case 'u': { unsigned int val; Verify( atox( { cur + 2, 4 }, &val ) ); - if ( val <= 0xFF ) + if ( val <= 0x7F ) { cur[0] = (char)val; @@ -993,7 +1116,7 @@ shift_one: if ( m_cur >= m_end ) goto err_eof; } - else if ( IN_RANGE_CHAR( *(unsigned char*)m_cur, '1', '9' ) ) + else if ( IN_RANGE_CHAR( *m_cur, '1', '9' ) ) { do { @@ -1001,11 +1124,11 @@ shift_one: if ( m_cur >= m_end ) goto err_eof; } - while ( IN_RANGE_CHAR( *(unsigned char*)m_cur, '0', '9' ) ); + while ( IN_RANGE_CHAR( *m_cur, '0', '9' ) ); } else { - SetError( "unexpected char '0x%02x' in number @ %i", *(unsigned char*)m_cur, Index() ); + SetError( "unexpected char 0x%02x in number @ %i", *(unsigned char*)m_cur, Index() ); return Token_Error; } @@ -1016,7 +1139,7 @@ shift_one: type = Token_Float; m_cur++; - while ( m_cur < m_end && IN_RANGE_CHAR( *(unsigned char*)m_cur, '0', '9' ) ) + while ( m_cur < m_end && IN_RANGE_CHAR( *m_cur, '0', '9' ) ) m_cur++; if ( m_cur >= m_end ) @@ -1039,7 +1162,7 @@ shift_one: goto err_eof; } - while ( m_cur < m_end && IN_RANGE_CHAR( *(unsigned char*)m_cur, '0', '9' ) ) + while ( m_cur < m_end && IN_RANGE_CHAR( *m_cur, '0', '9' ) ) m_cur++; } @@ -1062,7 +1185,7 @@ err_eof: { if ( type != Token_String ) { - SetError( "expected %s, got '0x%02x' @ %i", "string", Char(type), Index() ); + SetError( "expected '%c', got %s @ %i", '\"', Char(type), Index() ); return Token_Error; } @@ -1070,7 +1193,7 @@ err_eof: if ( type != ':' ) { - SetError( "expected '%c', got '0x%02x' @ %i", ':', Char(type), Index() ); + SetError( "expected '%c', got %s @ %i", ':', Char(type), Index() ); return Token_Error; } @@ -1085,7 +1208,7 @@ err_eof: if ( type == Token_Error ) { - SetError( "invalid token '0x%02x' @ %i", Char(type), Index() ); + SetError( "invalid token %s @ %i", Char(type), Index() ); return Token_Error; } @@ -1101,7 +1224,7 @@ err_eof: } else { - SetError( "expected '%c', got '0x%02x' @ %i", '}', Char(type), Index() ); + SetError( "expected '%c', got %s @ %i", '}', Char(type), Index() ); return Token_Error; } } @@ -1118,7 +1241,7 @@ err_eof: { if ( type == Token_Error ) { - SetError( "expected '%c', got '0x%02x' @ %i", ']', Char(type), Index() ); + SetError( "expected '%c', got %s @ %i", ']', Char(type), Index() ); return Token_Error; } @@ -1127,7 +1250,7 @@ err_eof: if ( type == Token_Error ) { - SetError( "invalid token '0x%02x' @ %i", Char(type), Index() ); + SetError( "invalid token %s @ %i", Char(type), Index() ); return Token_Error; } @@ -1143,7 +1266,7 @@ err_eof: } else { - SetError( "expected '%c', got '0x%02x' @ %i", ']', Char(type), Index() ); + SetError( "expected '%c', got %s @ %i", ']', Char(type), Index() ); return Token_Error; } } @@ -1154,7 +1277,7 @@ err_eof: switch ( type ) { case Token_Integer: - if ( token.len > FMT_INT_LEN ) + if ( token.len > FMT_UINT32_LEN + 1 ) { SetError( "invalid integer literal @ %i", Index() ); return Token_Error; diff --git a/sp/src/vscript/sqdbg/sqdbg/net.h b/sp/src/vscript/sqdbg/sqdbg/net.h index d8a1ad8d..34f5f407 100644 --- a/sp/src/vscript/sqdbg/sqdbg/net.h +++ b/sp/src/vscript/sqdbg/sqdbg/net.h @@ -602,6 +602,9 @@ public: bool Listen() { + if ( m_ServerSocket == INVALID_SOCKET ) + return false; + timeval tv; tv.tv_sec = 0; tv.tv_usec = 0; diff --git a/sp/src/vscript/sqdbg/sqdbg/protocol.h b/sp/src/vscript/sqdbg/sqdbg/protocol.h index 9d91ef2f..9f28c61c 100644 --- a/sp/src/vscript/sqdbg/sqdbg/protocol.h +++ b/sp/src/vscript/sqdbg/sqdbg/protocol.h @@ -12,10 +12,10 @@ inline void DAP_Serialise( CBuffer *buffer ) { - Assert( buffer->size() > 0 && buffer->size() < INT_MAX ); + Assert( buffer->Size() > 0 && buffer->Size() < INT_MAX ); - char *mem = buffer->base(); - int contentSize = buffer->size() - DAP_HEADER_MAXSIZE; + char *mem = buffer->Base(); + int contentSize = buffer->Size() - DAP_HEADER_MAXSIZE; int digits = countdigits( contentSize ); int padding = FMT_UINT32_LEN - digits; @@ -31,8 +31,8 @@ inline void DAP_Serialise( CBuffer *buffer ) // add padding in the end to match padding--; digits++; - buffer->_base.Ensure( buffer->size() + 1 ); - mem[buffer->_size++] = ' '; + buffer->base.Ensure( buffer->Size() + 1 ); + mem[buffer->size++] = ' '; } memcpy( mem, DAP_HEADER_CONTENTLENGTH ": ", STRLEN(DAP_HEADER_CONTENTLENGTH ": ") ); @@ -53,10 +53,10 @@ inline void DAP_Serialise( CBuffer *buffer ) inline void DAP_Free( CBuffer *buffer ) { - buffer->_size = 0; + buffer->size = 0; } -static inline void ParseFieldName( const char *pMemEnd, char *pStart, int *len ) +static inline int ParseFieldName( const char *pMemEnd, char *pStart ) { char *c = pStart; @@ -65,56 +65,39 @@ static inline void ParseFieldName( const char *pMemEnd, char *pStart, int *len ) if ( IN_RANGE_CHAR( ((unsigned char*)c)[0], 0x20, 0x7E ) ) { if ( c + 1 >= pMemEnd ) - { - *len = -1; - return; - } + return -1; if ( c[0] == ':' ) { if ( c[1] == ' ' ) - { - *len = c - pStart; - return; - } + return c - pStart; - *len = 0; - return; + return 0; } c++; } else { - *len = 0; - return; + return 0; } } } -static inline void ParseFieldValue( const char *pMemEnd, char *pStart, int *len ) +static inline int ParseFieldValue( const char *pMemEnd, char *pStart ) { char *c = pStart; for (;;) { if ( c + 1 >= pMemEnd ) - { - *len = -1; - return; - } + return -1; if ( c[0] == '\n' ) - { - *len = 0; - return; - } + return 0; if ( c[0] == '\r' && c[1] == '\n' ) - { - *len = c - pStart; - return; - } + return c - pStart; c++; } @@ -128,8 +111,7 @@ inline bool DAP_ReadHeader( char **ppMsg, int *pLength ) for (;;) { - int len; - ParseFieldName( pMemEnd, pMsg, &len ); + int len = ParseFieldName( pMemEnd, pMsg ); if ( len == 0 ) goto invalid; @@ -151,7 +133,7 @@ inline bool DAP_ReadHeader( char **ppMsg, int *pLength ) if ( pMsg >= pMemEnd ) return false; - if ( IN_RANGE_CHAR( *(unsigned char*)pMsg, '0', '9' ) ) + if ( IN_RANGE_CHAR( *pMsg, '0', '9' ) ) { nContentLength = nContentLength * 10 + *pMsg - '0'; pMsg++; @@ -187,7 +169,7 @@ inline bool DAP_ReadHeader( char **ppMsg, int *pLength ) ignore: pMsg += len + 2; - ParseFieldValue( pMemEnd, pMsg, &len ); + len = ParseFieldValue( pMemEnd, pMsg ); if ( len == 0 ) goto invalid; @@ -216,13 +198,13 @@ invalid: } #ifdef SQDBG_VALIDATE_SENT_MSG -inline void DAP_Test( CScratch< JSON_SCRATCH_CHUNK_SIZE > *scratch, CBuffer *buffer ) +inline void DAP_Test( CScratch< true, JSON_SCRATCH_CHUNK_SIZE > *scratch, CBuffer *buffer ) { - char *pMsg = buffer->base(); - int nLength = buffer->size(); + char *pMsg = buffer->Base(); + int nLength = buffer->Size(); bool res = DAP_ReadHeader( &pMsg, &nLength ); - Assert( res && nLength < buffer->size() ); + Assert( res && nLength < buffer->Size() ); if ( res ) { @@ -238,7 +220,7 @@ inline void DAP_Test( CScratch< JSON_SCRATCH_CHUNK_SIZE > *scratch, CBuffer *buf #define _DAP_INIT_BUF( _buf ) \ CBufTmpCache _bufcache( (_buf) ); \ - (_buf)->_size = DAP_HEADER_MAXSIZE; \ + (_buf)->size = DAP_HEADER_MAXSIZE; \ (void)0 #define DAP_START_REQUEST( _seq, _cmd ) \ @@ -262,10 +244,10 @@ if ( IsClientConnected() ) \ packet.SetBool( "success", _suc ); #define DAP_START_RESPONSE( _seq, _cmd ) \ - _DAP_START_RESPONSE( _seq, _cmd, true ); + _DAP_START_RESPONSE( _seq, _cmd, true ) #define DAP_ERROR_RESPONSE( _seq, _cmd ) \ - _DAP_START_RESPONSE( _seq, _cmd, false ); + _DAP_START_RESPONSE( _seq, _cmd, false ) #define DAP_ERROR_BODY( _id, _fmt ) \ wjson_table_t body = packet.SetTable( "body" ); \ @@ -292,7 +274,7 @@ if ( IsClientConnected() ) \ } \ \ DAP_Serialise( &m_SendBuf ); \ - Send( m_SendBuf.base(), m_SendBuf.size() ); \ + Send( m_SendBuf.Base(), m_SendBuf.Size() ); \ DAP_Test( &m_ReadBuf, &m_SendBuf ); \ DAP_Free( &m_SendBuf ); \ } diff --git a/sp/src/vscript/sqdbg/sqdbg/server.cpp b/sp/src/vscript/sqdbg/sqdbg/server.cpp index 75009911..899796e9 100644 --- a/sp/src/vscript/sqdbg/sqdbg/server.cpp +++ b/sp/src/vscript/sqdbg/sqdbg/server.cpp @@ -5,23 +5,21 @@ // Squirrel Debugger // -#define SQDBG_SV_VER 3 +#define SQDBG_SV_VER 5 #include "sqdbg.h" -#ifndef _WIN32 -#include // isfinite #include // INT_MIN -#endif -#include +#include // FLT_MAX +#include // offsetof #include -#include -#include +#include // qsort, strtod +#include // snprintf #include -#include #include #ifndef SQDBG_DISABLE_PROFILER -#include +#include // isfinite +#include // high_resolution_clock #endif #define ___CAT(a, b) a##b @@ -34,27 +32,33 @@ #include "vec.h" #include "net.h" +// Everything works fine, but client sent invalid message +#ifndef AssertClient +#define AssertClient Assert +#define AssertClientMsg1 AssertMsg1 +#endif + // For Squirrel headers #ifndef assert #define assert Assert #endif #include -#include #include #include -#include #include #include #include #include -#include -#include #include #include #include #include +#if defined(SQUNICODE) && !defined(_WIN32) +#include // swprintf +#endif + #ifdef _WIN32 void sqdbg_sleep( int ms ) @@ -92,8 +96,12 @@ void sqdbg_sleep( int ms ) #endif va_end( va ); - if ( len < 0 || len > (int)(sizeof(buf)/sizeof(SQChar))-1 ) - len = (int)(sizeof(buf)/sizeof(SQChar))-1; + #if defined(_MSC_VER) && _MSC_VER < 1900 + if ( len < 0 || len > (int)( sizeof(buf) / sizeof(SQChar) ) - 1 ) + buf[ sizeof(buf) / sizeof(SQChar) - 1 ] = 0; + #else + (void)len; + #endif _OutputDebugString( buf ); } @@ -153,12 +161,20 @@ void sqdbg_sleep( int ms ) #endif #endif +#ifndef scstrcmp +#ifdef SQUNICODE + #define scstrcmp wcscmp +#else + #define scstrcmp strcmp +#endif +#endif + #ifndef scstricmp #ifdef SQUNICODE #ifdef _WIN32 #define scstricmp _wcsicmp #else - #define scstricmp wcscmp + #define scstricmp sqdbg_wcsicmp #endif #else #ifdef _WIN32 @@ -169,21 +185,19 @@ void sqdbg_sleep( int ms ) #endif #endif -#ifndef scsprintf +#undef scsprintf #ifdef SQUNICODE #define scsprintf swprintf #else #define scsprintf snprintf #endif -#endif -#ifndef scvsprintf +#undef scvsprintf #ifdef SQUNICODE #define scvsprintf vswprintf #else #define scvsprintf vsnprintf #endif -#endif #ifndef sq_rsl #define sq_rsl(l) ((l)*sizeof(SQChar)) @@ -252,17 +266,13 @@ void sqdbg_sleep( int ms ) #undef type #undef is_delegable #define is_delegable(t) (sq_type(t) & SQOBJECT_DELEGABLE) +#endif - #ifndef SQUNICODE - #undef scvsprintf - #define scvsprintf vsnprintf - #endif +#if !defined(SQDBG_DISABLE_COMPILER) && !defined(NO_GARBAGE_COLLECTOR) + #define SUPPORTS_DEREF_OP - #undef scsprintf - #ifdef SQUNICODE - #define scsprintf swprintf - #else - #define scsprintf snprintf + #if SQUIRREL_VERSION_NUMBER >= 300 + #define CHAINABLE_FUNCPROTO #endif #endif @@ -271,18 +281,16 @@ void sqdbg_sleep( int ms ) #include "protocol.h" #define SQ_FOREACH_OP( obj, key, val ) \ - { \ - int _jump; \ - for ( SQObjectPtr _pos, key, val; \ - m_pCurVM->FOREACH_OP( obj, key, val, _pos, 0, 666, _jump ) && \ - _jump != 666; ) \ - { - -#define SQ_FOREACH_END() } } + int __CAT(_jump, __LINE__); \ + for ( SQObjectPtr _pos; \ + m_pCurVM->FOREACH_OP( obj, key, val, _pos, 0, 666, __CAT(_jump, __LINE__) ) && \ + __CAT(_jump, __LINE__) != 666; ) \ #define FOREACH_SQTABLE( pTable, key, val )\ - SQInteger _i = 0;\ - for ( SQObjectPtr pi = _i; (_i = pTable->Next( false, pi, key, val )) != -1; pi._unVal.nInteger = _i ) + SQInteger __CAT(_i, __LINE__) = 0; \ + for ( SQObjectPtr _pi = __CAT(_i, __LINE__); \ + (__CAT(_i, __LINE__) = pTable->Next( false, _pi, key, val )) != -1; \ + _pi._unVal.nInteger = __CAT(_i, __LINE__) ) #ifndef SQDBG_EXCLUDE_DEFAULT_MEMFUNCTIONS inline void *sqdbg_malloc( unsigned int size ) @@ -304,8 +312,19 @@ inline void sqdbg_free( void *p, unsigned int size ) } #endif +static inline HSQDEBUGSERVER sqdbg_get( HSQUIRRELVM vm ); +static inline HSQDEBUGSERVER sqdbg_get_debugger( HSQUIRRELVM vm ); +#ifdef NATIVE_DEBUG_HOOK +#ifdef DEBUG_HOOK_CACHED_SQDBG +static inline HSQDEBUGSERVER sqdbg_get_debugger_cached_debughook( HSQUIRRELVM vm ); +#else +#define sqdbg_get_debugger_cached_debughook sqdbg_get_debugger +#endif +#endif +static inline void sqdbg_get_debugger_ref( HSQUIRRELVM vm, SQObjectPtr &ref ); + template < typename T > -void CopyString( CScratch<> *allocator, const T &src, T *dst ) +void CopyString( CScratch< false > *allocator, const T &src, T *dst ) { Assert( src.ptr ); @@ -316,7 +335,7 @@ void CopyString( CScratch<> *allocator, const T &src, T *dst ) if ( dst->len ) allocator->Free( dst->ptr ); - void *mem = allocator->Alloc( ( src.len + 1 ) * sizeof(*dst->ptr), NULL, false ); + void *mem = allocator->Alloc( ( src.len + 1 ) * sizeof(*dst->ptr) ); if ( !mem ) { @@ -363,7 +382,7 @@ void CopyString( CScratch<> *allocator, const T &src, T *dst ) } #ifdef SQUNICODE -void CopyString( CScratch<> *allocator, const string_t &src, sqstring_t *dst ) +void CopyString( CScratch< false > *allocator, const string_t &src, sqstring_t *dst ) { Assert( src.ptr ); @@ -376,7 +395,7 @@ void CopyString( CScratch<> *allocator, const string_t &src, sqstring_t *dst ) if ( dst->len ) allocator->Free( dst->ptr ); - void *mem = allocator->Alloc( ( srclen + 1 ) * sizeof(*dst->ptr), NULL, false ); + void *mem = allocator->Alloc( ( srclen + 1 ) * sizeof(*dst->ptr) ); if ( !mem ) { @@ -413,7 +432,7 @@ void CopyString( CScratch<> *allocator, const string_t &src, sqstring_t *dst ) } } -void CopyString( CScratch<> *allocator, const sqstring_t &src, string_t *dst ) +void CopyString( CScratch< false > *allocator, const sqstring_t &src, string_t *dst ) { Assert( src.ptr ); @@ -426,7 +445,7 @@ void CopyString( CScratch<> *allocator, const sqstring_t &src, string_t *dst ) if ( dst->len ) allocator->Free( dst->ptr ); - void *mem = allocator->Alloc( ( srclen + 1 ) * sizeof(*dst->ptr), NULL, false ); + void *mem = allocator->Alloc( ( srclen + 1 ) * sizeof(*dst->ptr) ); if ( !mem ) { @@ -465,7 +484,7 @@ void CopyString( CScratch<> *allocator, const sqstring_t &src, string_t *dst ) #endif template < typename T > -void FreeString( CScratch<> *allocator, T *dst ) +void FreeString( CScratch< false > *allocator, T *dst ) { if ( dst->len ) { @@ -552,6 +571,12 @@ inline bool IsEqual( const SQObject &o1, const SQObject &o2 ) return ( _rawval(o1) == _rawval(o2) && sq_type(o1) == sq_type(o2) ); } +template < int s1size > +inline bool IsEqual( const SQChar (&s1)[s1size], const SQString *s2 ) +{ + return sqstring_t(s1).IsEqualTo( s2 ); +} + template < typename C > inline void StripFileName( C **ptr, unsigned int *len ) { @@ -567,6 +592,36 @@ inline void StripFileName( C **ptr, unsigned int *len ) } } +inline void StripWhitespace( string_t &str ) +{ + char *end = str.ptr + str.len; + + for ( char *c = str.ptr; c < end; c++ ) + { + if ( *c == ' ' || *c == '\t' || *c == '\n' ) + { + str.ptr++; + str.len--; + } + else + { + break; + } + } + + for ( char *c = end - 1; c >= str.ptr; c-- ) + { + if ( *c == ' ' || *c == '\t' || *c == '\n' ) + { + str.len--; + } + else + { + break; + } + } +} + #ifdef _DEBUG class CStackCheck { @@ -658,7 +713,7 @@ private: private: node_t *FindNode( hnode_t caller, void *func, hnode_t *handle ) { - hnode_t count = m_Nodes.size(); + hnode_t count = m_Nodes.Size(); // Start searching from caller, // new nodes are added after them @@ -680,7 +735,7 @@ private: group_t *FindGroup( SQString *tag, hgroup_t *idx ) { - for ( hgroup_t i = m_Groups.size(); i--; ) + for ( hgroup_t i = m_Groups.Size(); i--; ) { group_t &group = m_Groups[i]; if ( group.tag == tag ) @@ -713,14 +768,14 @@ public: m_State = kProfActive; - Assert( m_Nodes.capacity() == 0 ); - Assert( m_NodeTags.capacity() == 0 ); - Assert( m_CallStack.capacity() == 0 ); - Assert( m_GroupStack.capacity() == 0 ); + Assert( m_Nodes.Capacity() == 0 ); + Assert( m_NodeTags.Capacity() == 0 ); + Assert( m_CallStack.Capacity() == 0 ); + Assert( m_GroupStack.Capacity() == 0 ); - m_Nodes.reserve( max( vm->_alloccallsstacksize, 256 ) ); - m_NodeTags.reserve( m_Nodes.capacity() ); - m_CallStack.reserve( max( vm->_alloccallsstacksize, 8 ) ); + m_Nodes.Reserve( max( vm->_alloccallsstacksize, 256 ) ); + m_NodeTags.Reserve( m_Nodes.Capacity() ); + m_CallStack.Reserve( max( vm->_alloccallsstacksize, 8 ) ); for ( int i = 0; i < vm->_callsstacksize; i++ ) { @@ -740,24 +795,24 @@ public: m_State = kProfDisabled; m_nPauseLevel = 0; - for ( hnode_t i = 0; i < m_NodeTags.size(); i++ ) + for ( hnode_t i = 0; i < m_NodeTags.Size(); i++ ) { nodetag_t *node = &m_NodeTags[i]; __ObjRelease( node->funcsrc ); __ObjRelease( node->funcname ); } - for ( hnode_t i = 0; i < m_Groups.size(); i++ ) + for ( hnode_t i = 0; i < m_Groups.Size(); i++ ) { group_t *group = &m_Groups[i]; __ObjRelease( group->tag ); } - m_Nodes.purge(); - m_NodeTags.purge(); - m_CallStack.purge(); - m_Groups.purge(); - m_GroupStack.purge(); + m_Nodes.Purge(); + m_NodeTags.Purge(); + m_CallStack.Purge(); + m_Groups.Purge(); + m_GroupStack.Purge(); } void Reset( HSQUIRRELVM vm, SQString *tag ) @@ -780,7 +835,7 @@ public: } else { - for ( hnode_t i = 0; i < m_Nodes.size(); i++ ) + for ( hnode_t i = 0; i < m_Nodes.Size(); i++ ) { node_t &node = m_Nodes[i]; node.calls = 0; @@ -806,15 +861,15 @@ public: if ( group ) { - m_GroupStack.append( idx ); + m_GroupStack.Append( idx ); group->hits++; group->sampleStart = Sample(); return; } - m_GroupStack.append( m_Groups.size() ); + m_GroupStack.Append( m_Groups.Size() ); - group = &m_Groups.append(); + group = &m_Groups.Append(); group->tag = tag; __ObjAddRef( tag ); group->hits = 1; @@ -827,19 +882,19 @@ public: sample_t sample = Sample(); - if ( !m_GroupStack.size() ) + if ( !m_GroupStack.Size() ) { - Assert(!"profiler group mismatch"); + AssertClient(!"profiler group mismatch"); return; } - hgroup_t idx = m_GroupStack.top(); - m_GroupStack.pop(); + hgroup_t idx = m_GroupStack.Top(); + m_GroupStack.Pop(); group_t *group = &m_Groups[idx]; // group was ended while profiler was paused - Assert( group->sampleStart != 0.0 ); + AssertClient( group->sampleStart != 0.0 ); sample_t dt = sample - group->sampleStart; group->samples += dt; @@ -863,9 +918,9 @@ public: { m_State = kProfPaused; - if ( m_CallStack.size() ) + if ( m_CallStack.Size() ) { - hnode_t caller = m_CallStack.top(); + hnode_t caller = m_CallStack.Top(); node_t *node = &m_Nodes[caller]; node->samples += sample - node->sampleStart; #ifdef _DEBUG @@ -873,9 +928,9 @@ public: #endif } - if ( m_GroupStack.size() ) + if ( m_GroupStack.Size() ) { - hgroup_t idx = m_GroupStack.top(); + hgroup_t idx = m_GroupStack.Top(); group_t *group = &m_Groups[idx]; sample_t dt = sample - group->sampleStart; group->samples += dt; @@ -902,16 +957,16 @@ public: sample_t sample = Sample(); - if ( m_CallStack.size() ) + if ( m_CallStack.Size() ) { - hnode_t caller = m_CallStack.top(); + hnode_t caller = m_CallStack.Top(); node_t *node = &m_Nodes[caller]; node->sampleStart = sample; } - if ( m_GroupStack.size() ) + if ( m_GroupStack.Size() ) { - hgroup_t idx = m_GroupStack.top(); + hgroup_t idx = m_GroupStack.Top(); group_t *group = &m_Groups[idx]; group->sampleStart = sample; } @@ -922,14 +977,14 @@ public: { Assert( IsActive() ); - hnode_t caller = m_CallStack.size() ? m_CallStack.top() : INVALID_HANDLE; + hnode_t caller = m_CallStack.Size() ? m_CallStack.Top() : INVALID_HANDLE; hnode_t id; node_t *node = FindNode( caller, func, &id ); if ( node ) { - m_CallStack.append( id ); + m_CallStack.Append( id ); node->calls++; node->sampleStart = Sample(); return; @@ -943,11 +998,11 @@ public: _string(func->_sourcename) : NULL; - id = m_Nodes.size(); - node = &m_Nodes.append(); - nodetag_t *tag = &m_NodeTags.append(); + id = m_Nodes.Size(); + node = &m_Nodes.Append(); + nodetag_t *tag = &m_NodeTags.Append(); - m_CallStack.append( id ); + m_CallStack.Append( id ); node->id = id; node->func = func; @@ -1016,29 +1071,29 @@ public: sample_t sample = Sample(); - if ( !m_CallStack.size() ) + if ( !m_CallStack.Size() ) { // metamethod calls don't execute debug hook pre-221 #if SQUIRREL_VERSION_NUMBER >= 221 - Assert(!"profiler call mismatch"); + AssertClient(!"profiler call mismatch"); #endif return; } - hnode_t id = m_CallStack.top(); - m_CallStack.pop(); + hnode_t id = m_CallStack.Top(); + m_CallStack.Pop(); node_t *node = &m_Nodes[id]; // call ended while profiler was paused - Assert( node->sampleStart != 0.0 ); + AssertClient( node->sampleStart != 0.0 ); node->samples += sample - node->sampleStart; } void CallEndAll() { - while ( m_CallStack.size() ) + while ( m_CallStack.Size() ) CallEnd(); } @@ -1053,13 +1108,17 @@ public: "(sqdbg) prof | : " \ "total 100.00 ms, avg 100.00 ms, peak 100.00 ms(0x7fffffff), hits 0x7fffffff\n" +#ifndef PROF_GROUP_NAME_LEN_ALIGNMENT +#define PROF_GROUP_NAME_LEN_ALIGNMENT 16 +#endif + // Returns character length int GetMaxOutputLen( SQString *tag, int type ) { if ( tag ) { return STRLEN(PROF_GROUP_OUTPUT_TEMPLATE) + - ALIGN( tag->_len, 16 ) + + ALIGN( tag->_len, PROF_GROUP_NAME_LEN_ALIGNMENT ) + 1; } @@ -1069,7 +1128,7 @@ public: case 0: { const int header = STRLEN(PROF_OUTPUT_HEADER); - const int bufsize = header + m_Nodes.size() * + const int bufsize = header + m_Nodes.Size() * ( header - STRLEN("func") + // depth[CALLGRAPH_MAX_DEPTH*3]func, src (addr)\n CALLGRAPH_MAX_DEPTH * 3 + @@ -1079,22 +1138,22 @@ public: 1 ) + 1; - int strlen = 0; + int len = 0; - for ( hnode_t i = 0; i < m_NodeTags.size(); i++ ) + for ( hnode_t i = 0; i < m_NodeTags.Size(); i++ ) { nodetag_t *node = &m_NodeTags[i]; - strlen += (int)node->funcsrc->_len; - strlen += (int)node->funcname->_len; + len += (int)node->funcsrc->_len; + len += (int)node->funcname->_len; } - return bufsize + strlen; + return bufsize + len; } // flat case 1: { const int header = STRLEN(PROF_OUTPUT_HEADER); - const int bufsize = header + m_Nodes.size() * + const int bufsize = header + m_Nodes.Size() * ( header - STRLEN("func") + // func, src (addr)\n /*func*/ 2 + @@ -1103,16 +1162,16 @@ public: 1 ) + 1; - int strlen = 0; + int len = 0; - for ( hnode_t i = 0; i < m_NodeTags.size(); i++ ) + for ( hnode_t i = 0; i < m_NodeTags.Size(); i++ ) { nodetag_t *node = &m_NodeTags[i]; - strlen += (int)node->funcsrc->_len; - strlen += (int)node->funcname->_len; + len += (int)node->funcsrc->_len; + len += (int)node->funcname->_len; } - return bufsize + strlen; + return bufsize + len; } default: { @@ -1124,6 +1183,8 @@ public: // Returns character length int Output( SQString *tag, int type, SQChar *buf, int size ) { + Assert( size > 0 ); + if ( tag ) { hgroup_t idx; @@ -1142,7 +1203,7 @@ public: memcpy( buf, group->tag->_val, sq_rsl(len) ); buf += len; size -= len; - for ( int i = ALIGN( len, 16 ) - len; i-- > 0; ) + for ( int i = ALIGN( len, PROF_GROUP_NAME_LEN_ALIGNMENT ) - len; i-- > 0; ) { *buf++ = ' '; size--; @@ -1229,7 +1290,7 @@ public: // merge all calls of identical functions case 1: { - hnode_t c = nodes.size(); + hnode_t c = nodes.Size(); for ( hnode_t i = 0; i < c; i++ ) { @@ -1245,7 +1306,7 @@ public: node.samples += nj.samples; node.calls += nj.calls; - nodes.remove(j); + nodes.Remove(j); j--; c--; } @@ -1260,11 +1321,11 @@ public: } } - hnode_t nodecount = nodes.size(); + hnode_t nodecount = nodes.Size(); sample_t totalSamples = 0.0; const SQChar *bufstart = buf; - nodes.sort( _sort ); + nodes.Sort( _sort ); for ( hnode_t i = 0; i < nodecount; i++ ) { @@ -1273,7 +1334,7 @@ public: // Only accumulate parent call times if ( node.caller == INVALID_HANDLE ) { - if ( !m_CallStack.size() || m_CallStack.top() != node.id ) + if ( !m_CallStack.Size() || m_CallStack.Top() != node.id ) { totalSamples += node.samples; } @@ -1443,7 +1504,7 @@ private: int more = 0; avg = 0.0; - for ( hnode_t j = i + 1; j < nodes.size(); j++ ) + for ( hnode_t j = i + 1; j < nodes.Size(); j++ ) { const node_t &nj = nodes[j]; if ( nj.caller == node.id ) @@ -1633,7 +1694,7 @@ LNAN: // // Longest return value is 16 bytes including nul byte // -inline string_t GetType( const SQObjectPtr &obj ) +inline conststring_t GetType( const SQObjectPtr &obj ) { switch ( _RAW_TYPE( sq_type(obj) ) ) { @@ -1650,19 +1711,19 @@ inline string_t GetType( const SQObjectPtr &obj ) case _RT_USERDATA: case _RT_USERPOINTER: return "userdata"; case _RT_THREAD: return "thread"; - case _RT_FUNCPROTO: return "function"; + case _RT_FUNCPROTO: return "funcproto"; case _RT_CLASS: return "class"; case _RT_INSTANCE: return "instance"; case _RT_WEAKREF: return "weakref"; #if SQUIRREL_VERSION_NUMBER >= 300 case _RT_OUTER: return "outer"; #endif - default: Assert(!"unknown type"); return "unknown"; + default: UNREACHABLE(); } } #if SQUIRREL_VERSION_NUMBER >= 300 -string_t const g_InstructionName[ _OP_CLOSE + 1 ]= +conststring_t const g_InstructionName[ _OP_CLOSE + 1 ] = { "LINE", "LOAD", @@ -1727,7 +1788,7 @@ string_t const g_InstructionName[ _OP_CLOSE + 1 ]= "CLOSE", }; #elif SQUIRREL_VERSION_NUMBER >= 212 -string_t const g_InstructionName[ _OP_NEWSLOTA + 1 ]= +conststring_t const g_InstructionName[ _OP_NEWSLOTA + 1 ] = { "LINE", "LOAD", @@ -1793,7 +1854,7 @@ string_t const g_InstructionName[ _OP_NEWSLOTA + 1 ]= }; #endif -string_t const g_MetaMethodName[ MT_LAST ] = +conststring_t const g_MetaMethodName[ MT_LAST ] = { "_add", "_sub", @@ -1829,11 +1890,13 @@ string_t const g_MetaMethodName[ MT_LAST ] = #define IS_INTERNAL_TAG( str ) ((str)[0] == '$') #define INTERNAL_TAG( name ) "$" name +#define INTERNAL_TAG_PREFIX '$' #define ANONYMOUS_FUNCTION_BREAKPOINT_NAME "()" #define INVALID_ID -1 #define DUPLICATE_ID -2 #define ISVALID_ID(i) ((i) > 0) #define ARRAY_PAGE_LIMIT 1024 +#define INVALID_FRAME -1 typedef enum { @@ -1949,13 +2012,7 @@ struct varref_t struct { - union - { - SQWeakRef *weakref; - SQObject obj; - }; - - bool isWeak; + SQWeakRef *weakref; bool isStrong; // temporary strong reference for inspecting vars bool hasNonStringMembers; } obj; @@ -1974,10 +2031,9 @@ struct varref_t const SQObject &GetVar() const { Assert( IsObjectRef( type ) ); - Assert( !obj.isWeak || ( obj.isWeak && obj.weakref ) ); - Assert( !obj.isStrong || ( obj.isStrong && ISREFCOUNTED( sq_type(obj.obj) ) ) ); - Assert( ( !obj.isWeak && !obj.isStrong ) || ( obj.isWeak != obj.isStrong ) ); - return obj.isWeak ? obj.weakref->_obj : obj.obj; + Assert( obj.weakref ); + Assert( !obj.isStrong || ISREFCOUNTED( sq_type(obj.weakref->_obj) ) ); + return obj.weakref->_obj; } }; @@ -2024,6 +2080,10 @@ struct objref_t CUSTOMMEMBER, STACK, INT, + VIRTUAL_REF, + VIRTUAL_SIZE, + VIRTUAL_ALLOCATED, + VIRTUAL_STATE, PTR = 0x1000, READONLY = 0x2000, } EOBJREF; @@ -2040,6 +2100,7 @@ struct objref_t { SQWeakRef *thread; int frame; + int start; int end; int index; } stack; @@ -2079,7 +2140,12 @@ struct returnvalue_t struct cachedinstr_t { +#ifdef SQDBG_WEAK_INSTRUCTION_REF + SQWeakRef *func; + int index; +#else SQInstruction *ip; +#endif SQInstruction instr; }; @@ -2124,12 +2190,12 @@ public: ~CFilePathMap() { - Assert( map.size() == 0 ); + Assert( map.Size() == 0 ); } - void Add( CScratch<> *allocator, const string_t &name, const string_t &path ) + void Add( CScratch< false > *allocator, const string_t &name, const string_t &path ) { - for ( unsigned int i = 0; i < map.size(); i++ ) + for ( unsigned int i = 0; i < map.Size(); i++ ) { pair_t &v = map[i]; if ( v.name.IsEqualTo( name ) ) @@ -2143,14 +2209,14 @@ public: } } - pair_t &v = map.append(); + pair_t &v = map.Append(); CopyString( allocator, name, &v.name ); CopyString( allocator, path, &v.path ); } pair_t *Get( const string_t &name ) { - for ( unsigned int i = 0; i < map.size(); i++ ) + for ( unsigned int i = 0; i < map.Size(); i++ ) { pair_t &v = map[i]; if ( v.name.IsEqualTo( name ) ) @@ -2160,16 +2226,16 @@ public: return NULL; } - void Clear( CScratch<> *allocator ) + void Clear( CScratch< false > *allocator ) { - for ( unsigned int i = 0; i < map.size(); i++ ) + for ( unsigned int i = 0; i < map.Size(); i++ ) { pair_t &v = map[i]; FreeString( allocator, &v.name ); FreeString( allocator, &v.path ); } - map.purge(); + map.Purge(); } }; @@ -2199,6 +2265,7 @@ public: private: HSQUIRRELVM m_pCurVM; HSQUIRRELVM m_pStateVM; + HSQUIRRELVM m_pPausedThread; SQPRINTFUNCTION m_Print; SQPRINTFUNCTION m_PrintError; @@ -2210,22 +2277,18 @@ private: bool m_bProfilerEnabled; #endif - bool m_bBreakOnExceptions; - bool m_bDebugHookGuard; -public: bool m_bInREPL; -private: + bool m_bBreakOnExceptions; + bool m_bExceptionPause; + bool m_bDebugHookGuard; bool m_bDebugHookGuardAlways; #if SQUIRREL_VERSION_NUMBER < 300 bool m_bInDebugHook; #endif - bool m_bExceptionPause; #ifndef SQDBG_DISABLE_COMPILER bool m_bClientColumnOffset; #endif - HSQUIRRELVM m_pPausedThread; - // Ignore debug hook calls from debugger executed scripts class CCallGuard { @@ -2283,10 +2346,10 @@ private: vector< script_t > m_Scripts; CBuffer m_SendBuf; - CScratch< JSON_SCRATCH_CHUNK_SIZE > m_ReadBuf; + CScratch< true, JSON_SCRATCH_CHUNK_SIZE > m_ReadBuf; CMemory m_Scratch; CMemory m_VarMemberCache; - CScratch<> m_Strings; + CScratch< false > m_Strings; CServerSocket m_Server; @@ -2448,7 +2511,7 @@ private: static inline bool HasCondition( const breakpoint_t *bp ); bool CheckBreakpointCondition( breakpoint_t *bp, HSQUIRRELVM vm, const SQVM::CallInfo *ci ); - int EvalAndWriteExpr( HSQUIRRELVM vm, const SQVM::CallInfo *ci, string_t &expression, char *buf, int size ); + int EvalAndWriteExpr( HSQUIRRELVM vm, int frame, string_t &expression, char *buf, int size ); void TracePoint( breakpoint_t *bp, HSQUIRRELVM vm, int frame ); enum @@ -2481,7 +2544,11 @@ private: bool InstructionStep( HSQUIRRELVM vm, SQVM::CallInfo *ci, int instrOffset = 0 ); bool Step( HSQUIRRELVM vm, SQVM::CallInfo *ci ); +#ifdef SQDBG_WEAK_INSTRUCTION_REF + void CacheInstruction( SQFunctionProto *func, SQInstruction *instr ); +#else void CacheInstruction( SQInstruction *instr ); +#endif void ClearCachedInstructions(); void RestoreCachedInstructions(); void UndoRestoreCachedInstructions(); @@ -2494,14 +2561,19 @@ public: static int DeduceJumpCount( const SQInstruction *instr ); private: - static inline bool IsValidStackFrame( HSQUIRRELVM vm, int id ); - static inline SQVM::CallInfo *GetStackFrame( HSQUIRRELVM vm, int id ); + static inline bool IsValidStackFrame( HSQUIRRELVM vm, int frame ); + static inline SQVM::CallInfo *GetStackFrame( HSQUIRRELVM vm, int frame ); static inline int GetStackBase( HSQUIRRELVM vm, const SQVM::CallInfo *ci ); + static inline int GetStackBase( HSQUIRRELVM vm, int frame ) + { + return GetStackBase( vm, vm->_callsstack + frame ); + } static HSQUIRRELVM GetThread( SQWeakRef *wr ) { Assert( wr ); Assert( sq_type(wr->_obj) == OT_THREAD ); + Assert( _thread(wr->_obj) ); return _thread(wr->_obj); } @@ -2513,12 +2585,16 @@ private: string_t GetValue( const SQObject &obj, int flags = 0 ); - string_t GetValue( SQInteger val, int flags = 0 ) + conststring_t GetValue( SQInteger val, int flags = 0 ) { SQObject obj; obj._type = OT_INTEGER; obj._unVal.nInteger = val; - return GetValue( obj, flags ); + string_t str = GetValue( obj, flags & ~kFS_Character ); + conststring_t ret; + ret.ptr = str.ptr; + ret.len = str.len; + return ret; } static SQObject ToSQObject( SQClass *val ) @@ -2537,6 +2613,14 @@ private: return obj; } + static SQObject ToSQObject( SQFunctionProto *val ) + { + SQObject obj; + obj._type = OT_FUNCPROTO; + obj._unVal.pFunctionProto = val; + return obj; + } + void JSONSetString( wjson_table_t &elem, const string_t &key, const SQObject &obj, int flags = 0 ); #ifndef SQDBG_DISABLE_COMPILER @@ -2547,9 +2631,11 @@ public: CompileReturnCode_Success, // Lookup failed CompileReturnCode_DoesNotExist, + CompileReturnCode_CallError, // String/number parsing failed CompileReturnCode_SyntaxError, CompileReturnCode_Fallback, + CompileReturnCode_OpFailure, CompileReturnCode_NoValue, // Unrecognised token or token sequences CompileReturnCode_Unsupported, @@ -2563,10 +2649,10 @@ public: // This may cause RunExpression fallback after Evaluate to fail // This can be avoided by exiting compilation before escaped strings are parsed // by putting "0," at the beginning of the expression - this is valid squirrel while not allowed here - ECompileReturnCode Evaluate( string_t &expression, HSQUIRRELVM vm, const SQVM::CallInfo *ci, SQObjectPtr &ret ); - ECompileReturnCode Evaluate( string_t &expression, HSQUIRRELVM vm, int frame, SQObjectPtr &ret ) + ECompileReturnCode Evaluate( string_t &expression, HSQUIRRELVM vm, int frame, SQObjectPtr &ret ); + ECompileReturnCode Evaluate( string_t &expression, HSQUIRRELVM vm, const SQVM::CallInfo *ci, SQObjectPtr &ret ) { - return Evaluate( expression, vm, GetStackFrame( vm, frame ), ret ); + return Evaluate( expression, vm, ci - vm->_callsstack, ret ); } ECompileReturnCode Evaluate( string_t &expression, HSQUIRRELVM vm, const SQVM::CallInfo *ci, SQObjectPtr &ret, @@ -2638,27 +2724,11 @@ private: return RunClosure( m_pCurVM, closure, env, p1, p2, ret ); } - bool RunClosure( const SQObjectPtr &closure, const SQObject *env, - const SQObjectPtr &p1, const SQObjectPtr &p2, const SQObjectPtr &p3, SQObjectPtr &ret ) + bool RunClosure( const SQObjectPtr &closure, + const SQObjectPtr *argv, int argc, SQObjectPtr &ret ) { CCallGuard cg( this, m_pCurVM ); - return RunClosure( m_pCurVM, closure, env, p1, p2, p3, ret ); - } - - bool RunClosure( const SQObjectPtr &closure, const SQObject *env, - const SQObjectPtr &p1, const SQObjectPtr &p2, const SQObjectPtr &p3, const SQObjectPtr &p4, - SQObjectPtr &ret ) - { - CCallGuard cg( this, m_pCurVM ); - return RunClosure( m_pCurVM, closure, env, p1, p2, p3, p4, ret ); - } - - bool RunClosure( const SQObjectPtr &closure, const SQObject *env, - const SQObjectPtr &p1, const SQObjectPtr &p2, const SQObjectPtr &p3, const SQObjectPtr &p4, - const SQObjectPtr &p5, SQObjectPtr &ret ) - { - CCallGuard cg( this, m_pCurVM ); - return RunClosure( m_pCurVM, closure, env, p1, p2, p3, p4, p5, ret ); + return RunClosure( m_pCurVM, closure, argv, argc, ret ); } static inline bool RunClosure( HSQUIRRELVM vm, const SQObjectPtr &closure, const SQObject *env, @@ -2667,14 +2737,8 @@ private: const SQObjectPtr &p1, SQObjectPtr &ret ); static inline bool RunClosure( HSQUIRRELVM vm, const SQObjectPtr &closure, const SQObject *env, const SQObjectPtr &p1, const SQObjectPtr &p2, SQObjectPtr &ret ); - static inline bool RunClosure( HSQUIRRELVM vm, const SQObjectPtr &closure, const SQObject *env, - const SQObjectPtr &p1, const SQObjectPtr &p2, const SQObjectPtr &p3, SQObjectPtr &ret ); - static inline bool RunClosure( HSQUIRRELVM vm, const SQObjectPtr &closure, const SQObject *env, - const SQObjectPtr &p1, const SQObjectPtr &p2, const SQObjectPtr &p3, const SQObjectPtr &p4, - SQObjectPtr &ret ); - static inline bool RunClosure( HSQUIRRELVM vm, const SQObjectPtr &closure, const SQObject *env, - const SQObjectPtr &p1, const SQObjectPtr &p2, const SQObjectPtr &p3, const SQObjectPtr &p4, - const SQObjectPtr &p5, SQObjectPtr &ret ); + static inline bool RunClosure( HSQUIRRELVM vm, const SQObjectPtr &closure, + const SQObjectPtr *argv, int argc, SQObjectPtr &ret ); static SQInteger SQMM_Get( HSQUIRRELVM vm ); static SQInteger SQMM_Set( HSQUIRRELVM vm ); @@ -2702,21 +2766,21 @@ private: static inline bool ShouldParseEvaluateName( const string_t &expression ); static inline bool ShouldPageArray( const SQObject &obj, unsigned int limit ); #ifndef SQDBG_DISABLE_COMPILER - void FillCompletions( const SQObjectPtr &target, HSQUIRRELVM vm, const SQVM::CallInfo *ci, + void FillCompletions( const SQObjectPtr &target, HSQUIRRELVM vm, int frame, int token, const string_t &partial, int start, int length, wjson_array_t &targets ); #endif private: void InitEnv_GetVal( SQObjectPtr &env ); void SetCallFrame( SQObjectPtr &env, HSQUIRRELVM vm, const SQVM::CallInfo *ci ); - void SetEnvDelegate( SQObjectPtr &env, const SQObject &delegate ); + void SetEnvDelegate( SQObjectPtr &env, HSQUIRRELVM vm, const SQVM::CallInfo *ci ); void ClearEnvDelegate( SQObjectPtr &env ); #ifdef CLOSURE_ROOT void SetEnvRoot( SQObjectPtr &env, const SQObjectPtr &root ); #endif private: - static inline bool ShouldIgnoreStackFrame( const SQVM::CallInfo &ci ); + static inline bool ShouldIgnoreStackFrame( HSQUIRRELVM vm, const SQVM::CallInfo &ci ); int ConvertToFrameID( int threadId, int stackFrame ); bool TranslateFrameID( int frameId, HSQUIRRELVM *thread, int *stackFrame ); @@ -2726,10 +2790,10 @@ private: private: int ToVarRef( EVARREF type, HSQUIRRELVM vm, int frame ); - int ToVarRef( EVARREF type, const SQObject &obj, bool isWeak = false, bool isStrong = false ); - int ToVarRef( const SQObject &obj, bool isWeak = false, bool isStrong = false ) + int ToVarRef( EVARREF type, const SQObject &obj, bool isStrong = false ); + int ToVarRef( const SQObject &obj, bool isStrong = false ) { - return ToVarRef( VARREF_OBJ, obj, isWeak, isStrong ); + return ToVarRef( VARREF_OBJ, obj, isStrong ); } static inline void ConvertToWeakRef( varref_t &v ); @@ -2796,10 +2860,10 @@ public: private: void ErrorHandler( HSQUIRRELVM vm ); - void DebugHook( HSQUIRRELVM vm, SQInteger type, - const SQChar *sourcename, SQInteger line, const SQChar *funcname ); + void DebugHook( HSQUIRRELVM vm, int type, + const SQChar *sourcename, int line, const SQChar *funcname ); #ifndef SQDBG_DISABLE_PROFILER - void ProfHook( HSQUIRRELVM vm, SQInteger type ); + void ProfHook( HSQUIRRELVM vm, int type ); #endif void OnSQPrint( HSQUIRRELVM vm, const SQChar *buf, int len ); void OnSQError( HSQUIRRELVM vm, const SQChar *buf, int len ); @@ -2831,8 +2895,8 @@ public: #ifndef SQDBG_CALL_DEFAULT_ERROR_HANDLER static void SQErrorAtFrame( HSQUIRRELVM vm, const SQVM::CallInfo *ci, const SQChar *fmt, ... ); - static void PrintVar( HSQUIRRELVM vm, const SQChar *name, const SQObjectPtr &obj ); - static void PrintStack( HSQUIRRELVM vm ); + void PrintVar( HSQUIRRELVM vm, const SQChar *name, const SQObjectPtr &obj ); + void PrintStack( HSQUIRRELVM vm ); #endif static SQInteger SQErrorHandler( HSQUIRRELVM vm ); @@ -2894,8 +2958,6 @@ inline bool SQTable_Get( SQDebugServer *dbg, SQTable *table, const string_t &key #endif } -static inline void sqdbg_get_debugger_ref( HSQUIRRELVM vm, SQObjectPtr &ref ); - void SQDebugServer::Attach( HSQUIRRELVM vm ) { if ( m_pRootVM ) @@ -2915,7 +2977,7 @@ void SQDebugServer::Attach( HSQUIRRELVM vm ) m_pRootVM = _thread(_ss(vm)->_root_vm); m_pCurVM = vm; - Assert( m_Threads.size() == 0 ); + Assert( m_Threads.Size() == 0 ); ThreadToID( m_pRootVM ); #if SQUIRREL_VERSION_NUMBER >= 300 @@ -2993,7 +3055,7 @@ void SQDebugServer::Attach( HSQUIRRELVM vm ) sq_pushobject( m_pRootVM, ref ); sq_newclosure( m_pRootVM, &SQDebugServer::SQAddDataBreakpoint, 1 ); sq_setnativeclosurename( m_pRootVM, -1, _SC("sqdbg_watch") ); - sq_setparamscheck( m_pRootVM, -2, _SC(".ssn") ); + sq_setparamscheck( m_pRootVM, -2, _SC(".ssi") ); sq_newslot( m_pRootVM, -3, SQFalse ); #endif @@ -3069,7 +3131,7 @@ void SQDebugServer::Attach( HSQUIRRELVM vm ) } #define FOREACH_THREAD_BEGIN( _vm ) \ - for ( unsigned int i = m_Threads.size(); i--; ) \ + for ( unsigned int i = m_Threads.Size(); i--; ) \ { \ SQWeakRef *wr = m_Threads[i]; \ if ( wr && sq_type(wr->_obj) == OT_THREAD ) \ @@ -3189,8 +3251,8 @@ void SQDebugServer::Shutdown() if ( IsProfilerEnabled() ) ProfStop(); - Assert( m_Profilers.size() == 0 ); - m_Profilers.purge(); + Assert( m_Profilers.Size() == 0 ); + m_Profilers.Purge(); #endif SetErrorHandler( false ); @@ -3203,12 +3265,12 @@ void SQDebugServer::Shutdown() m_nCalls = 0; m_bInREPL = false; - m_bDebugHookGuardAlways = false; + m_bExceptionPause = false; m_bDebugHookGuard = false; + m_bDebugHookGuardAlways = false; #if SQUIRREL_VERSION_NUMBER < 300 m_bInDebugHook = false; #endif - m_bExceptionPause = false; m_pPausedThread = NULL; RemoveReturnValues(); @@ -3220,18 +3282,18 @@ void SQDebugServer::Shutdown() RestoreCachedInstructions(); ClearCachedInstructions(); - m_CachedInstructions.purge(); - m_ReturnValues.purge(); - m_Breakpoints.purge(); - m_DataWatches.purge(); + m_CachedInstructions.Purge(); + m_ReturnValues.Purge(); + m_Breakpoints.Purge(); + m_DataWatches.Purge(); RemoveThreads(); RemoveClassDefs(); RemoveScripts(); - m_FrameIDs.purge(); + m_FrameIDs.Purge(); m_FilePathMap.Clear( &m_Strings ); - m_SendBuf.purge(); + m_SendBuf.Purge(); m_ReadBuf.Free(); m_Scratch.Free(); m_VarMemberCache.Free(); @@ -3298,12 +3360,12 @@ void SQDebugServer::DisconnectClient() m_nCalls = 0; m_bInREPL = false; - m_bDebugHookGuardAlways = false; + m_bExceptionPause = false; m_bDebugHookGuard = false; + m_bDebugHookGuardAlways = false; #if SQUIRREL_VERSION_NUMBER < 300 m_bInDebugHook = false; #endif - m_bExceptionPause = false; m_pPausedThread = NULL; RemoveReturnValues(); @@ -3315,12 +3377,12 @@ void SQDebugServer::DisconnectClient() RestoreCachedInstructions(); ClearCachedInstructions(); - m_CachedInstructions.purge(); - m_ReturnValues.purge(); - m_Breakpoints.purge(); - m_DataWatches.purge(); + m_CachedInstructions.Purge(); + m_ReturnValues.Purge(); + m_Breakpoints.Purge(); + m_DataWatches.Purge(); - m_SendBuf.purge(); + m_SendBuf.Purge(); m_ReadBuf.Free(); m_Scratch.Free(); m_VarMemberCache.Free(); @@ -3351,14 +3413,14 @@ void SQDebugServer::OnClientConnected( const char *addr ) InitEnv_GetVal( m_EnvGetVal ); #define _check( var, size ) \ - if ( var.capacity() < size ) \ - var.reserve( size ); + if ( var.Capacity() < size ) \ + var.Reserve( size ); _check( m_ReturnValues, 1 ); _check( m_Vars, 64 ); _check( m_FrameIDs, 8 ); - m_SendBuf.reserve( 16384 ); + m_SendBuf.Reserve( 16384 ); #undef _check } @@ -3378,22 +3440,22 @@ void SQDebugServer::Frame() } #define GET_OR_FAIL( _base, _val ) \ - if ( !(_base).Get( #_val, &_val ) ) \ - { \ - PrintError(_SC("(sqdbg) invalid DAP message, could not find '" #_val "'\n")); \ - DisconnectClient(); \ - return; \ - } + if ( !(_base).Get( #_val, &_val ) ) \ + { \ + PrintError(_SC("(sqdbg) invalid DAP message, could not find '" #_val "'\n")); \ + DisconnectClient(); \ + return; \ + } #define GET_OR_ERROR_RESPONSE( _cmd, _base, _val ) \ - if ( !(_base).Get( #_val, &_val ) ) \ - { \ - PrintError(_SC("(sqdbg) invalid DAP message, could not find '" #_val "'\n")); \ - DAP_ERROR_RESPONSE( seq, _cmd ); \ - DAP_ERROR_BODY( 0, "invalid DAP message" ); \ - DAP_SEND(); \ - return; \ - } + if ( !(_base).Get( #_val, &_val ) ) \ + { \ + PrintError(_SC("(sqdbg) invalid DAP message, could not find '" #_val "'\n")); \ + DAP_ERROR_RESPONSE( seq, _cmd ); \ + DAP_ERROR_BODY( 0, "invalid DAP message" ); \ + DAP_SEND(); \ + return; \ + } void SQDebugServer::OnMessageReceived( char *ptr, int len ) { @@ -3403,7 +3465,7 @@ void SQDebugServer::OnMessageReceived( char *ptr, int len ) if ( parser.GetError() ) { PrintError(_SC("(sqdbg) Invalid JSON : " FMT_CSTR "\n"), parser.GetError()); - AssertMsg1( 0, "Invalid JSON : %s", parser.GetError() ); + AssertClientMsg1( 0, "Invalid JSON : %s", parser.GetError() ); DisconnectClient(); return; } @@ -3427,7 +3489,7 @@ void SQDebugServer::OnMessageReceived( char *ptr, int len ) table.GetString( "command", &command ); PrintError(_SC("(sqdbg) Unrecognised response '" FMT_CSTR "'\n"), command.ptr); - AssertMsg1( 0, "Unrecognised response '%s'", command.ptr ); + AssertClientMsg1( 0, "Unrecognised response '%s'", command.ptr ); } else if ( type.IsEqualTo( "event" ) ) { @@ -3435,12 +3497,13 @@ void SQDebugServer::OnMessageReceived( char *ptr, int len ) table.GetString( "event", &event ); PrintError(_SC("(sqdbg) Unrecognised event '" FMT_CSTR "'\n"), event.ptr); - AssertMsg1( 0, "Unrecognised event '%s'", event.ptr ); + AssertClientMsg1( 0, "Unrecognised event '%s'", event.ptr ); } else { PrintError(_SC("(sqdbg) Invalid DAP type '" FMT_CSTR "'\n"), type.ptr); - AssertMsg1( 0, "Invalid DAP type '%s'", type.ptr ); + AssertClientMsg1( 0, "Invalid DAP type '%s'", type.ptr ); + DisconnectClient(); } m_ReadBuf.Release(); @@ -3556,7 +3619,7 @@ void SQDebugServer::ProcessRequest( const json_table_t &table, int seq ) if ( breakpointId > 0 && breakpointId < m_nBreakpointIndex ) { #define _check( vec, type ) \ - for ( unsigned int i = 0; i < vec.size(); i++ ) \ + for ( unsigned int i = 0; i < vec.Size(); i++ ) \ { \ type &bp = vec[i]; \ if ( bp.id == breakpointId ) \ @@ -3842,7 +3905,8 @@ void SQDebugServer::ProcessRequest( const json_table_t &table, int seq ) wjson_table_t variables = error.SetTable( "variables" ); variables.SetString( "command", command ); DAP_SEND(); - AssertMsg1( 0, "Unrecognised request '%s'", command.ptr ); + + AssertClientMsg1( 0, "Unrecognised request '%s'", command.ptr ); } } @@ -3871,9 +3935,9 @@ void SQDebugServer::OnScriptCompile( const SQChar *script, unsigned int scriptle if ( !scr ) { - scr = &m_Scripts.append(); - scr->sourceptr = m_Strings.Alloc( source.len, NULL, false ); - scr->scriptptr = m_Strings.Alloc( scriptbufsize, NULL, false ); + scr = &m_Scripts.Append(); + scr->sourceptr = m_Strings.Alloc( source.len ); + scr->scriptptr = m_Strings.Alloc( scriptbufsize ); if ( scr->sourceptr ) { @@ -3887,7 +3951,7 @@ void SQDebugServer::OnScriptCompile( const SQChar *script, unsigned int scriptle if ( oldsize != scriptbufsize ) { m_Strings.Free( scr->scriptptr ); - scr->scriptptr = m_Strings.Alloc( scriptbufsize, NULL, false ); + scr->scriptptr = m_Strings.Alloc( scriptbufsize ); } } @@ -3899,7 +3963,7 @@ void SQDebugServer::OnScriptCompile( const SQChar *script, unsigned int scriptle script_t *SQDebugServer::GetScript( const string_t &source ) { - for ( unsigned int i = 0; i < m_Scripts.size(); i++ ) + for ( unsigned int i = 0; i < m_Scripts.Size(); i++ ) { script_t &scr = m_Scripts[i]; if ( source.IsEqualTo( scr.sourceptr, scr.sourcelen ) ) @@ -3911,14 +3975,14 @@ script_t *SQDebugServer::GetScript( const string_t &source ) void SQDebugServer::RemoveScripts() { - for ( unsigned int i = 0; i < m_Scripts.size(); i++ ) + for ( unsigned int i = 0; i < m_Scripts.Size(); i++ ) { script_t &scr = m_Scripts[i]; m_Strings.Free( scr.sourceptr ); m_Strings.Free( scr.scriptptr ); } - m_Scripts.purge(); + m_Scripts.Purge(); } void SQDebugServer::OnRequest_Initialize( const json_table_t &arguments, int seq ) @@ -4064,7 +4128,7 @@ void SQDebugServer::OnRequest_SetBreakpoints( const json_table_t &arguments, int DAP_SET_TABLE( body ); wjson_array_t obps = body.SetArray( "breakpoints" ); - for ( int i = 0; i < breakpoints->size(); i++ ) + for ( int i = 0; i < breakpoints->Size(); i++ ) { json_table_t *bp; @@ -4105,7 +4169,7 @@ void SQDebugServer::OnRequest_SetBreakpoints( const json_table_t &arguments, int else { obp.SetString( "reason", "failed" ); - obp.SetString( "message", GetValue( m_pCurVM->_lasterror, kFS_NoQuote ) ); + JSONSetString( obp, "message", m_pCurVM->_lasterror, kFS_NoQuote ); } } @@ -4115,7 +4179,7 @@ void SQDebugServer::OnRequest_SetBreakpoints( const json_table_t &arguments, int for ( unsigned int i = 0; i < m_nFunctionBreakpointsIdx; i++ ) Assert( m_Breakpoints[i].line != 0 ); - for ( unsigned int i = m_nFunctionBreakpointsIdx; i < m_Breakpoints.size(); i++ ) + for ( unsigned int i = m_nFunctionBreakpointsIdx; i < m_Breakpoints.Size(); i++ ) Assert( m_Breakpoints[i].line == 0 ); #endif } @@ -4131,7 +4195,7 @@ void SQDebugServer::OnRequest_SetFunctionBreakpoints( const json_table_t &argume DAP_SET_TABLE( body ); wjson_array_t obps = body.SetArray( "breakpoints" ); - for ( int i = 0; i < breakpoints->size(); i++ ) + for ( int i = 0; i < breakpoints->Size(); i++ ) { json_table_t *bp; @@ -4199,7 +4263,7 @@ void SQDebugServer::OnRequest_SetFunctionBreakpoints( const json_table_t &argume else { obp.SetString( "reason", "failed" ); - obp.SetString( "message", GetValue( m_pCurVM->_lasterror, kFS_NoQuote ) ); + JSONSetString( obp, "message", m_pCurVM->_lasterror, kFS_NoQuote ); } } @@ -4209,7 +4273,7 @@ void SQDebugServer::OnRequest_SetFunctionBreakpoints( const json_table_t &argume for ( unsigned int i = 0; i < m_nFunctionBreakpointsIdx; i++ ) Assert( m_Breakpoints[i].line != 0 ); - for ( unsigned int i = m_nFunctionBreakpointsIdx; i < m_Breakpoints.size(); i++ ) + for ( unsigned int i = m_nFunctionBreakpointsIdx; i < m_Breakpoints.Size(); i++ ) Assert( m_Breakpoints[i].line == 0 ); #endif } @@ -4221,7 +4285,7 @@ void SQDebugServer::OnRequest_SetExceptionBreakpoints( const json_table_t &argum json_array_t *filters; GET_OR_ERROR_RESPONSE( "setExceptionBreakpoints", arguments, filters ); - for ( int i = 0; i < filters->size(); i++ ) + for ( int i = 0; i < filters->Size(); i++ ) { string_t filter; @@ -4238,7 +4302,7 @@ void SQDebugServer::OnRequest_SetExceptionBreakpoints( const json_table_t &argum } } - if ( filters->size() == 0 ) + if ( filters->Size() == 0 ) { m_bBreakOnExceptions = false; sq_notifyallexceptions( m_pRootVM, 0 ); @@ -4274,7 +4338,7 @@ void SQDebugServer::OnRequest_SetDataBreakpoints( const json_table_t &arguments, DAP_SET_TABLE( body ); wjson_array_t obps = body.SetArray( "breakpoints" ); - for ( int i = 0; i < breakpoints->size(); i++ ) + for ( int i = 0; i < breakpoints->Size(); i++ ) { json_table_t *bp; @@ -4293,7 +4357,7 @@ void SQDebugServer::OnRequest_SetDataBreakpoints( const json_table_t &arguments, bp->GetString( "dataId", &dataId ); bp->GetString( "condition", &condition ); - condition.Strip(); + StripWhitespace( condition ); if ( bp->GetString( "hitCondition", &hitCondition ) && !hitCondition.IsEmpty() ) { @@ -4315,49 +4379,28 @@ void SQDebugServer::OnRequest_SetDataBreakpoints( const json_table_t &arguments, else { obp.SetString( "reason", "failed" ); - obp.SetString( "message", GetValue( m_pCurVM->_lasterror, kFS_NoQuote ) ); + JSONSetString( obp, "message", m_pCurVM->_lasterror, kFS_NoQuote ); } } DAP_SEND(); } -#ifndef MAX_DATA_WATCH_BUF_SIZE -#define MAX_DATA_WATCH_BUF_SIZE 512 -#endif -#define MAX_DATA_WATCH_NAME_LENGTH ( MAX_DATA_WATCH_BUF_SIZE - FMT_PTR_LEN - 32 ) - void SQDebugServer::OnRequest_DataBreakpointInfo( const json_table_t &arguments, int seq ) { int variablesReference; string_t name; + objref_t obj; + SQObjectPtr value; arguments.GetString( "name", &name ); arguments.GetInt( "variablesReference", &variablesReference ); - if ( name.len > MAX_DATA_WATCH_NAME_LENGTH ) - { - DAP_START_RESPONSE( seq, "dataBreakpointInfo" ); - DAP_SET_TABLE( body ); - body.SetNull( "dataId" ); - body.SetString( "description", "" ); - DAP_SEND(); - return; - } - if ( variablesReference ) { - objref_t obj; - SQObjectPtr dummy; varref_t *ref = FromVarRef( variablesReference ); - // don't modify name in GetObj - stringbuf_t< MAX_DATA_WATCH_BUF_SIZE > tmpbuf; - tmpbuf.Puts( name ); - string_t tmp = tmpbuf; - - if ( !ref || ref->type != VARREF_OBJ || - !GetObj_VarRef( ref, tmp, obj, dummy ) ) + if ( !ref || ref->type != VARREF_OBJ ) { DAP_START_RESPONSE( seq, "dataBreakpointInfo" ); DAP_SET_TABLE( body ); @@ -4367,33 +4410,49 @@ void SQDebugServer::OnRequest_DataBreakpointInfo( const json_table_t &arguments, return; } + if ( !name.IsEqualTo( INTERNAL_TAG("refs") ) && + !name.IsEqualTo( INTERNAL_TAG("allocated") ) && + !name.IsEqualTo( INTERNAL_TAG("state") ) ) + { + // don't modify name in GetObj + stringbufext_t tmpbuf( m_ReadBuf.Alloc( name.len ), name.len ); + tmpbuf.Puts( name ); + string_t tmp = tmpbuf; + + bool test = !GetObj_VarRef( ref, tmp, obj, value ); + + m_ReadBuf.ReleaseTop(); + + if ( test ) + { + DAP_START_RESPONSE( seq, "dataBreakpointInfo" ); + DAP_SET_TABLE( body ); + body.SetNull( "dataId" ); + body.SetString( "description", "" ); + DAP_SEND(); + return; + } + } + DAP_START_RESPONSE( seq, "dataBreakpointInfo" ); DAP_SET_TABLE( body ); { - stringbuf_t< MAX_DATA_WATCH_BUF_SIZE > bufId; - - // 1:varref:name - bufId.Put('1'); - bufId.Put(':'); - bufId.PutInt( variablesReference ); - bufId.Put(':'); - bufId.Puts( name ); - - body.SetString( "dataId", bufId ); + jstringbuf_t buf = body.SetStringAsBuf( "dataId" ); + buf.Put('1'); + buf.PutInt( variablesReference ); + buf.Put(':'); + buf.Puts( name ); } { - stringbuf_t< MAX_DATA_WATCH_BUF_SIZE > bufName; - - bufName.Put('['); - bufName.PutHex( (uintptr_t)_refcounted(ref->GetVar()) ); - bufName.Put(' '); - bufName.Puts( GetType( ref->GetVar() ) ); - bufName.Put(']'); - bufName.Put('-'); - bufName.Put('>'); - bufName.Puts( name ); - - body.SetString( "description", bufName ); + jstringbuf_t buf = body.SetStringAsBuf( "description" ); + buf.Put('['); + buf.PutHex( (uintptr_t)_refcounted(ref->GetVar()) ); + buf.Put(' '); + buf.Puts( GetType( ref->GetVar() ) ); + buf.Put(']'); + buf.Put('-'); + buf.Put('>'); + buf.Puts( name ); } wjson_array_t at = body.SetArray( "accessTypes" ); at.Append( "write" ); @@ -4403,14 +4462,14 @@ void SQDebugServer::OnRequest_DataBreakpointInfo( const json_table_t &arguments, { #ifndef SQDBG_DISABLE_COMPILER // don't modify name in CCompiler::ParseString - stringbuf_t< MAX_DATA_WATCH_BUF_SIZE > tmpbuf; + stringbufext_t tmpbuf( m_ReadBuf.Alloc( name.len ), name.len ); tmpbuf.Puts( name ); string_t tmp = tmpbuf; - objref_t obj; - SQObjectPtr value; ECompileReturnCode r = Evaluate( tmp, m_pCurVM, m_pCurVM->ci, value, obj ); + m_ReadBuf.ReleaseTop(); + // Check value again to see if the compiled expression was really a reference SQObjectPtr val; @@ -4430,23 +4489,15 @@ void SQDebugServer::OnRequest_DataBreakpointInfo( const json_table_t &arguments, DAP_START_RESPONSE( seq, "dataBreakpointInfo" ); DAP_SET_TABLE( body ); { - stringbuf_t< MAX_DATA_WATCH_BUF_SIZE > bufId; - - // 0:expr - bufId.Put('0'); - bufId.Put(':'); - bufId.Puts( name ); - - body.SetString( "dataId", bufId ); + jstringbuf_t buf = body.SetStringAsBuf( "dataId" ); + buf.Put('0'); + buf.Puts( name ); } { - stringbuf_t< MAX_DATA_WATCH_BUF_SIZE > bufName; - - bufName.Put('`'); - bufName.Puts( name ); - bufName.Put('`'); - - body.SetString( "description", bufName ); + jstringbuf_t buf = body.SetStringAsBuf( "description" ); + buf.Put('`'); + buf.Puts( name ); + buf.Put('`'); } wjson_array_t at = body.SetArray( "accessTypes" ); at.Append( "write" ); @@ -4464,7 +4515,7 @@ void SQDebugServer::OnRequest_DataBreakpointInfo( const json_table_t &arguments, int SQDebugServer::AddDataBreakpoint( HSQUIRRELVM vm, const SQVM::CallInfo *ci, const string_t &dataId, const string_t &strCondition, int hitsTarget ) { - if ( dataId.len < 2 || dataId.ptr[1] != ':' ) + if ( dataId.len < 2 ) return INVALID_ID; string_t name; @@ -4474,23 +4525,17 @@ int SQDebugServer::AddDataBreakpoint( HSQUIRRELVM vm, const SQVM::CallInfo *ci, if ( dataId.ptr[0] == '1' ) { - char *pEnd = strchr( dataId.ptr + 1 + 1, ':' ); + char *pEnd = strchr( dataId.ptr + 1, ':' ); if ( !pEnd ) return INVALID_ID; string_t container; - container.ptr = dataId.ptr + 1 + 1; + container.ptr = dataId.ptr + 1; container.len = pEnd - container.ptr; name.ptr = pEnd + 1; name.len = ( dataId.ptr + dataId.len ) - name.ptr; - if ( name.len > MAX_DATA_WATCH_NAME_LENGTH ) - { - vm->_lasterror = CreateSQString( vm, _SC("name is too long") ); - return INVALID_ID; - } - int variablesReference; if ( !atoi( container, &variablesReference ) ) @@ -4501,18 +4546,78 @@ int SQDebugServer::AddDataBreakpoint( HSQUIRRELVM vm, const SQVM::CallInfo *ci, varref_t *ref = FromVarRef( variablesReference ); - // don't modify name in GetObj - stringbuf_t< MAX_DATA_WATCH_BUF_SIZE > tmpbuf; - tmpbuf.Puts( name ); - string_t tmp = tmpbuf; - - if ( !ref || ref->type != VARREF_OBJ || - !GetObj_VarRef( ref, tmp, obj, value ) ) + if ( !ref || ref->type != VARREF_OBJ ) { vm->_lasterror = CreateSQString( vm, _SC("invalid object") ); return INVALID_ID; } + // Prioritise virtual members, + // real member watches can be added with sqdbg_watch as well + if ( name.IsEqualTo( INTERNAL_TAG("refs") ) ) + { + Assert( ISREFCOUNTED( sq_type(ref->GetVar()) ) ); + obj.type = objref_t::VIRTUAL_REF; + obj.src = ref->GetVar(); + value = (SQInteger)_refcounted(obj.src)->_uiRef; + } + else if ( name.IsEqualTo( INTERNAL_TAG("allocated") ) ) + { + if ( sq_type(ref->GetVar()) == OT_ARRAY ) + { + obj.type = objref_t::VIRTUAL_ALLOCATED; + obj.src = ref->GetVar(); + value = (SQInteger)_array(obj.src)->_values.capacity(); + } + else + { + vm->_lasterror = CreateSQString( vm, _SC("invalid object") ); + return INVALID_ID; + } + } + else if ( name.IsEqualTo( INTERNAL_TAG("state") ) ) + { + switch ( sq_type(ref->GetVar()) ) + { + case OT_THREAD: + { + obj.type = objref_t::VIRTUAL_STATE; + obj.src = ref->GetVar(); + value = sq_getvmstate( _thread(obj.src) ); + break; + } + case OT_GENERATOR: + { + obj.type = objref_t::VIRTUAL_STATE; + obj.src = ref->GetVar(); + value = (SQInteger)_generator(obj.src)->_state; + break; + } + default: + { + vm->_lasterror = CreateSQString( vm, _SC("invalid object") ); + return INVALID_ID; + } + } + } + else + { + // don't modify name in GetObj + stringbufext_t tmpbuf( m_ReadBuf.Alloc( name.len ), name.len ); + tmpbuf.Puts( name ); + string_t tmp = tmpbuf; + + bool test = !GetObj_VarRef( ref, tmp, obj, value ); + + m_ReadBuf.ReleaseTop(); + + if ( test ) + { + vm->_lasterror = CreateSQString( vm, _SC("invalid object") ); + return INVALID_ID; + } + } + ConvertPtr( obj ); Assert( obj.type != objref_t::PTR ); @@ -4522,34 +4627,43 @@ int SQDebugServer::AddDataBreakpoint( HSQUIRRELVM vm, const SQVM::CallInfo *ci, #ifndef SQDBG_DISABLE_COMPILER else if ( dataId.ptr[0] == '0' ) { - name.ptr = dataId.ptr + 1 + 1; + name.ptr = dataId.ptr + 1; name.len = ( dataId.ptr + dataId.len ) - name.ptr; - if ( name.len > MAX_DATA_WATCH_NAME_LENGTH ) - { - vm->_lasterror = CreateSQString( vm, _SC("name is too long") ); - return INVALID_ID; - } - // don't modify name in CCompiler::ParseString - stringbuf_t< MAX_DATA_WATCH_BUF_SIZE > tmpbuf; + stringbufext_t tmpbuf( m_ReadBuf.Alloc( name.len ), name.len ); tmpbuf.Puts( name ); string_t tmp = tmpbuf; ECompileReturnCode r = Evaluate( tmp, vm, ci, value, obj ); + m_ReadBuf.ReleaseTop(); ConvertPtr( obj ); - Assert( obj.type != objref_t::PTR ); // Check value again to see if the compiled expression was really a reference SQObjectPtr val; - if ( r != CompileReturnCode_Success || - obj.type == objref_t::INVALID || obj.type == objref_t::PTR || + if ( r == CompileReturnCode_DoesNotExist ) + { + vm->_lasterror = CreateSQString( vm, _SC("index does not exist") ); + return INVALID_ID; + } + + if ( r != CompileReturnCode_Success ) + { + vm->_lasterror = CreateSQString( vm, _SC("invalid expression") ); + return INVALID_ID; + } + + // objref_t::src will hold an additional one + if ( obj.type == objref_t::VIRTUAL_REF && sq_type(value) == OT_INTEGER ) + _integer(value)++; + + if ( obj.type == objref_t::INVALID || obj.type == objref_t::PTR || ( !ISREFCOUNTED( sq_type(obj.src) ) && obj.type != objref_t::STACK ) || !Get( obj, val ) || !IsEqual( val, value ) ) { - vm->_lasterror = CreateSQString( vm, _SC("invalid expression") ); + vm->_lasterror = CreateSQString( vm, _SC("invalid object") ); return INVALID_ID; } @@ -4569,7 +4683,7 @@ int SQDebugServer::AddDataBreakpoint( HSQUIRRELVM vm, const SQVM::CallInfo *ci, // Duplicate? if ( obj.type != objref_t::STACK ) - for ( unsigned int i = m_DataWatches.size(); i--; ) + for ( unsigned int i = m_DataWatches.Size(); i--; ) { const datawatch_t &dw = m_DataWatches[i]; if ( dw.container == pContainer && _refcounted(dw.obj.src) == _refcounted(obj.src) && @@ -4598,8 +4712,8 @@ int SQDebugServer::AddDataBreakpoint( HSQUIRRELVM vm, const SQVM::CallInfo *ci, if ( duplicate ) { - // Re-iterate through watches to match this exact compiled condition - for ( unsigned int i = m_DataWatches.size(); i--; ) + // Match this exact compiled condition + for ( unsigned int i = m_DataWatches.Size(); i--; ) { const datawatch_t &dw = m_DataWatches[i]; if ( dw.container == pContainer && _refcounted(dw.obj.src) == _refcounted(obj.src) && @@ -4614,7 +4728,7 @@ int SQDebugServer::AddDataBreakpoint( HSQUIRRELVM vm, const SQVM::CallInfo *ci, Assert( m_nBreakpointIndex < INT_MAX ); - datawatch_t &dw = m_DataWatches.append(); + datawatch_t &dw = m_DataWatches.Append(); dw.id = ++m_nBreakpointIndex; CopyString( &m_Strings, name, &dw.name ); @@ -4795,6 +4909,15 @@ int SQDebugServer::CompareObj( const SQObjectPtr &lhs, const SQObjectPtr &rhs ) case OT_FLOAT: return _float(lhs) < _float(rhs) ? ECMP_L : ECMP_G; + case OT_STRING: + { + int res = scstrcmp( _string(lhs)->_val, _string(rhs)->_val ); + + if ( res < 0 ) + return ECMP_L; + + return ECMP_G; + } default: if ( is_delegable(lhs) && _delegable(lhs)->_delegate ) { @@ -4856,7 +4979,7 @@ bool SQDebugServer::CheckDataBreakpoints( HSQUIRRELVM vm ) { bool ret = false; - for ( unsigned int i = m_DataWatches.size(); i--; ) + for ( unsigned int i = m_DataWatches.Size(); i--; ) { datawatch_t &dw = m_DataWatches[i]; @@ -4888,7 +5011,7 @@ bool SQDebugServer::CheckDataBreakpoints( HSQUIRRELVM vm ) DAP_SEND(); FreeDataWatch( dw ); - m_DataWatches.remove(i); + m_DataWatches.Remove(i); continue; } } @@ -4957,6 +5080,10 @@ bool SQDebugServer::CheckDataBreakpoints( HSQUIRRELVM vm ) stringbuf_t< 256 > buf; + string_t tmp = dw.name; + if ( tmp.len > 128 ) + tmp.len = 128; + if ( dw.container ) { buf.Put('['); @@ -4966,19 +5093,25 @@ bool SQDebugServer::CheckDataBreakpoints( HSQUIRRELVM vm ) buf.Put(']'); buf.Put('-'); buf.Put('>'); - buf.Puts( dw.name ); + buf.Puts( tmp ); } else { buf.Put('`'); - buf.Puts( dw.name ); + buf.Puts( tmp ); buf.Put('`'); } buf.Puts(" changed ("); - buf.Puts( GetValue( oldvalue ) ); + tmp = GetValue( oldvalue ); + if ( tmp.len > 128 ) + tmp.len = 128; + buf.Puts( tmp ); buf.Puts(")->("); - buf.Puts( GetValue( value ) ); + tmp = GetValue( value ); + if ( tmp.len > 128 ) + tmp.len = 128; + buf.Puts( tmp ); buf.Put(')'); buf.Term(); @@ -5030,7 +5163,7 @@ bool SQDebugServer::CheckDataBreakpoints( HSQUIRRELVM vm ) } FreeDataWatch( dw ); - m_DataWatches.remove(i); + m_DataWatches.Remove(i); } } @@ -5056,10 +5189,10 @@ void SQDebugServer::FreeDataWatch( datawatch_t &dw ) void SQDebugServer::RemoveDataBreakpoints() { - for ( unsigned int i = 0; i < m_DataWatches.size(); i++ ) + for ( unsigned int i = 0; i < m_DataWatches.Size(); i++ ) FreeDataWatch( m_DataWatches[i] ); - m_DataWatches.clear(); + m_DataWatches.Clear(); } static inline bool HasEscapes( const SQChar *src, SQInteger len ) @@ -5070,54 +5203,32 @@ static inline bool HasEscapes( const SQChar *src, SQInteger len ) { switch ( *src ) { - case '\"': case '\\': + case '\\': case '\"': case '\a': case '\b': case '\f': case '\n': case '\r': case '\t': case '\v': - case '\0': return true; default: #ifdef SQUNICODE if ( !IN_RANGE( *src, 0x20, 0x7E ) ) #else - if ( !IN_RANGE_CHAR( *(unsigned char*)src, 0x20, 0x7E ) ) + if ( !IN_RANGE_CHAR( *src, 0x20, 0x7E ) ) #endif { #ifdef SQUNICODE - if ( *src < 0x80 ) - { - return true; - } - else - { - int ret = IsValidUnicode( src, end - src ); - if ( ret > 0 ) - { - src += ret - 1; - } - else - { - return true; - } - } + int ret = IsValidUnicode( src, end - src ); + if ( ret > 0 ) #else - if ( *(unsigned char*)src < 0x80 ) + int ret = IsValidUTF8( (unsigned char*)src, end - src ); + if ( ret != 0 ) +#endif { - return true; + src += ret - 1; } else { - int ret = IsValidUTF8( (unsigned char*)src, end - src ); - if ( ret != 0 ) - { - src += ret - 1; - } - else - { - return true; - } + return true; } -#endif } } } @@ -5135,15 +5246,14 @@ static inline int CountEscapes( const char *src, SQInteger len ) { switch ( *src ) { - case '\"': case '\\': + case '\\': case '\"': case '\a': case '\b': case '\f': case '\n': case '\r': case '\t': case '\v': - case '\0': count++; break; default: - if ( !IN_RANGE_CHAR( *(unsigned char*)src, 0x20, 0x7E ) ) + if ( !IN_RANGE_CHAR( *src, 0x20, 0x7E ) ) { int ret = IsValidUTF8( (unsigned char*)src, end - src ); if ( ret != 0 ) @@ -5163,8 +5273,10 @@ static inline int CountEscapes( const char *src, SQInteger len ) #endif // !SQUNICODE #define _memmove( dst, src, count, bound ) \ +{ \ Assert( (dst) + (count) <= bound ); \ - memmove( (dst), (src), (count) ); + memmove( (dst), (src), (count) ); \ +} static void Escape( char *dst, unsigned int *len, unsigned int size ) { @@ -5178,14 +5290,13 @@ static void Escape( char *dst, unsigned int *len, unsigned int size ) { switch ( *dst ) { - case '\"': case '\\': + case '\\': case '\"': case '\a': case '\b': case '\f': case '\n': case '\r': case '\t': case '\v': - case '\0': { if ( dst + 1 >= memEnd ) { - dst[0] = '?'; + *dst = '?'; break; } @@ -5194,8 +5305,7 @@ static void Escape( char *dst, unsigned int *len, unsigned int size ) switch ( *dst ) { - case '\"': - case '\\': break; + case '\\': case '\"': break; case '\a': *dst = 'a'; break; case '\b': *dst = 'b'; break; case '\f': *dst = 'f'; break; @@ -5203,7 +5313,6 @@ static void Escape( char *dst, unsigned int *len, unsigned int size ) case '\r': *dst = 'r'; break; case '\t': *dst = 't'; break; case '\v': *dst = 'v'; break; - case '\0': *dst = '0'; break; default: UNREACHABLE(); } @@ -5216,11 +5325,10 @@ static void Escape( char *dst, unsigned int *len, unsigned int size ) break; } default: - if ( !IN_RANGE_CHAR( *(unsigned char*)dst, 0x20, 0x7E ) ) + if ( !IN_RANGE_CHAR( *dst, 0x20, 0x7E ) ) { - int ret = 0; - if ( *(unsigned char*)dst < 0x80 || - ( ret = IsValidUTF8( (unsigned char*)dst, strEnd - dst ) ) == 0 ) + int ret = IsValidUTF8( (unsigned char*)dst, strEnd - dst ); + if ( ret == 0 ) { if ( dst + 1 + sizeof(SQChar) * 2 >= memEnd ) { @@ -5268,37 +5376,45 @@ static void UndoEscape( char *dst, unsigned int *len ) { char *end = dst + *len; - for ( ; dst < end; dst++ ) + for ( ; dst < end; ) { if ( *dst != '\\' ) + { + dst++; continue; + } + +#define _shift( bytesWritten, bytesRead ) \ + Assert( (bytesWritten) < (bytesRead) ); \ + memmove( dst + (bytesWritten), dst + (bytesRead), end - ( dst + (bytesRead) ) ); \ + dst += (bytesWritten); \ + end -= (bytesRead) - (bytesWritten); \ + *len -= (bytesRead) - (bytesWritten); switch ( dst[1] ) { case '\\': shift_one: - memmove( dst, dst + 1, end - dst ); - end--; - (*len)--; + _shift( 1, 2 ); break; - case '\"': goto shift_one; - case 'a': dst[1] = '\a'; goto shift_one; - case 'b': dst[1] = '\b'; goto shift_one; - case 'f': dst[1] = '\f'; goto shift_one; - case 'n': dst[1] = '\n'; goto shift_one; - case 'r': dst[1] = '\r'; goto shift_one; - case 't': dst[1] = '\t'; goto shift_one; - case 'v': dst[1] = '\v'; goto shift_one; - case '0': dst[1] = '\0'; goto shift_one; + case '\"': *dst = '\"'; goto shift_one; + case '\'': *dst = '\''; goto shift_one; + case 'a': *dst = '\a'; goto shift_one; + case 'b': *dst = '\b'; goto shift_one; + case 'f': *dst = '\f'; goto shift_one; + case 'n': *dst = '\n'; goto shift_one; + case 'r': *dst = '\r'; goto shift_one; + case 't': *dst = '\t'; goto shift_one; + case 'v': *dst = '\v'; goto shift_one; case 'x': { atox( { dst + 2, sizeof(SQChar) * 2 }, (SQChar*)dst ); - memmove( dst + 1, (dst + 2) + sizeof(SQChar) * 2, end - ( (dst + 2) + sizeof(SQChar) * 2 ) ); - end -= sizeof(SQChar) * 2 + 1; - *len -= sizeof(SQChar) * 2 + 1; + _shift( 1, 2 + sizeof(SQChar) * 2 ); break; } } + +#undef _shift } } #endif // !SQUNICODE @@ -5344,7 +5460,7 @@ static bool ReadStringifiedBytes( char *dst, int *len ) memmove( dst + sizeof(SQChar), dst + 2 + sizeof(SQChar) * 2, end - ( dst + 2 + sizeof(SQChar) * 2 ) ); end -= 2 + sizeof(SQChar) * 2 - sizeof(SQChar); - (*len) -= 2 + sizeof(SQChar) * 2 - sizeof(SQChar); + *len -= 2 + sizeof(SQChar) * 2 - sizeof(SQChar); dst += sizeof(SQChar) - 1; } } @@ -5591,7 +5707,7 @@ getfloat: SQChar ch = (SQChar)_integer(obj); - if ( !(flags & kFS_Hexadecimal) ) + if ( !( flags & kFS_Hexadecimal ) ) { #ifdef SQUNICODE len = printint( buf, size, ch ); @@ -5617,24 +5733,25 @@ getfloat: buf[len++] = (char)ch; } +#ifdef SQUNICODE + else if ( IsValidUnicode( &ch, 1 ) ) + { + len += SQUnicodeToUTF8( buf + len, size - len - 1, &ch, 1 ); + } +#endif else { + buf[len++] = '\\'; + buf[len++] = 'x'; + #ifdef SQUNICODE if ( ch <= (SQChar)0xFF ) { - buf[len++] = '\\'; - buf[len++] = 'x'; - len += printhex< true, false, false >( buf + len, size, (char)ch ); - } - else if ( IsValidUnicode( &ch, 1 ) ) - { - len += SQUnicodeToUTF8( buf + len, size - len - 1, &ch, 1 ); + len += printhex< true, false, false >( buf + len, size, (unsigned char)ch ); } else #endif { - buf[len++] = '\\'; - buf[len++] = 'x'; len += printhex< true, false, false >( buf + len, size, ch ); } } @@ -5985,13 +6102,31 @@ void SQDebugServer::JSONSetString( wjson_table_t &elem, const string_t &key, con switch ( sq_type(obj) ) { case OT_STRING: - { elem.SetString( key, _string(obj), !( flags & kFS_NoQuote ) ); break; - } - default: - { + case OT_INTEGER: + if ( !( flags & kFS_Character ) ) + goto conststr; + goto process; + // Possibly contains characters to be escaped through class def and member list + case OT_TABLE: + case OT_ARRAY: + if ( !( flags & kFS_ListMembers ) ) + goto conststr; + case OT_INSTANCE: + case OT_CLASS: +process: elem.SetString( key, GetValue( obj, flags ) ); + break; + // Cannot contain escapable characters, copy string verbatim to json + default: +conststr: + { + string_t str = GetValue( obj, flags ); + conststring_t ret; + ret.ptr = str.ptr; + ret.len = str.len; + elem.SetString( key, ret ); } } } @@ -6053,14 +6188,14 @@ SQUnsignedInteger SQDebugServer::s_nTargetAssignment = 0; SQString *SQDebugServer::GetLocalVarName( const SQFunctionProto *func, const SQInstruction *instr, unsigned int pos ) { - SQUnsignedInteger ip = (SQUnsignedInteger)( instr - func->_instructions ); + SQUnsignedInteger ip = instr - func->_instructions; for ( int i = 0; i < func->_nlocalvarinfos; i++ ) { const SQLocalVarInfo &var = func->_localvarinfos[i]; if ( (unsigned int)var._pos == pos && - var._start_op <= ip + s_nTargetAssignment && var._end_op >= ip - 1 ) + var._start_op <= ip + s_nTargetAssignment && var._end_op + 1 >= ip ) { return sq_type(var._name) == OT_STRING ? _string(var._name) : NULL; } @@ -6778,18 +6913,19 @@ LBOOL: #endif } -bool SQDebugServer::IsValidStackFrame( HSQUIRRELVM vm, int id ) +bool SQDebugServer::IsValidStackFrame( HSQUIRRELVM vm, int frame ) { Assert( !!vm->_callsstacksize == !!vm->ci ); - return id >= 0 && id < vm->_callsstacksize && vm->_callsstacksize > 0; + return frame >= 0 && frame < vm->_callsstacksize && vm->_callsstacksize != 0; } -SQVM::CallInfo *SQDebugServer::GetStackFrame( HSQUIRRELVM vm, int id ) +SQVM::CallInfo *SQDebugServer::GetStackFrame( HSQUIRRELVM vm, int frame ) { Assert( !!vm->_callsstacksize == !!vm->ci ); + Assert( frame == INVALID_FRAME || frame >= 0 ); - if ( id >= 0 && id < vm->_callsstacksize && vm->_callsstacksize > 0 ) - return &vm->_callsstack[id]; + if ( frame >= 0 && frame < vm->_callsstacksize && vm->_callsstacksize != 0 ) + return &vm->_callsstack[ frame ]; return NULL; } @@ -6840,23 +6976,24 @@ void SQDebugServer::SetCallFrame( SQObjectPtr &env, HSQUIRRELVM vm, const SQVM:: { Assert( sq_type(env) == OT_TABLE ); Assert( _table(env)->_delegate ); + Assert( ci >= vm->_callsstack && ci < vm->_callsstack + vm->_callsstacksize ); SQObjectPtr frame = (SQInteger)( ci - vm->_callsstack ); - Assert( _integer(frame) >= 0 && _integer(frame) < vm->_callsstacksize ); - Verify( _table(env)->_delegate->Set( m_sqstrCallFrame, frame ) ); + _table(env)->_delegate->NewSlot( m_sqstrCallFrame, frame ); } -void SQDebugServer::SetEnvDelegate( SQObjectPtr &env, const SQObject &delegate ) +void SQDebugServer::SetEnvDelegate( SQObjectPtr &env, HSQUIRRELVM vm, const SQVM::CallInfo *ci ) { Assert( sq_type(env) == OT_TABLE ); Assert( _table(env)->_delegate ); SQObjectPtr weakref; + const SQObjectPtr &delegate = vm->_stack._vals[ GetStackBase( vm, ci ) ]; if ( ISREFCOUNTED( sq_type(delegate) ) ) weakref = GetWeakRef( _refcounted(delegate), sq_type(delegate) ); - Verify( _table(env)->_delegate->Set( m_sqstrDelegate, weakref ) ); + _table(env)->_delegate->NewSlot( m_sqstrDelegate, weakref ); } void SQDebugServer::ClearEnvDelegate( SQObjectPtr &env ) @@ -6876,7 +7013,7 @@ void SQDebugServer::SetEnvRoot( SQObjectPtr &env, const SQObjectPtr &root ) Assert( sq_type(root) == OT_TABLE || ( sq_type(root) == OT_WEAKREF && sq_type(_weakref(root)->_obj) == OT_TABLE ) ); - Verify( _table(env)->_delegate->Set( m_sqstrRoot, root ) ); + _table(env)->_delegate->NewSlot( m_sqstrRoot, root ); } #endif @@ -6902,7 +7039,7 @@ bool SQDebugServer::RunExpression( const string_t &expression, HSQUIRRELVM vm, c #endif SetCallFrame( m_EnvGetVal, vm, ci ); - SetEnvDelegate( m_EnvGetVal, vm->_stack._vals[ GetStackBase( vm, ci ) ] ); + SetEnvDelegate( m_EnvGetVal, vm, ci ); #ifdef CLOSURE_ROOT SQWeakRef *root = bRoot ? _closure(ci->_closure)->_root : NULL; @@ -7140,63 +7277,15 @@ bool SQDebugServer::RunClosure( HSQUIRRELVM vm, const SQObjectPtr &closure, cons return false; } -bool SQDebugServer::RunClosure( HSQUIRRELVM vm, const SQObjectPtr &closure, const SQObject *env, - const SQObjectPtr &p1, const SQObjectPtr &p2, const SQObjectPtr &p3, SQObjectPtr &ret ) +bool SQDebugServer::RunClosure( HSQUIRRELVM vm, const SQObjectPtr &closure, + const SQObjectPtr *argv, int argc, SQObjectPtr &ret ) { vm->Push( closure ); - vm->Push( env ? *env : vm->_roottable ); - vm->Push( p1 ); - vm->Push( p2 ); - vm->Push( p3 ); - if ( SQ_SUCCEEDED( sq_call( vm, 4, SQTrue, SQFalse ) ) ) - { - ret = vm->Top(); - vm->Pop(); - vm->Pop(); - return true; - } + for ( int i = 0; i < argc; i++ ) + vm->Push( argv[i] ); - vm->Pop(); - return false; -} - -bool SQDebugServer::RunClosure( HSQUIRRELVM vm, const SQObjectPtr &closure, const SQObject *env, - const SQObjectPtr &p1, const SQObjectPtr &p2, const SQObjectPtr &p3, const SQObjectPtr &p4, - SQObjectPtr &ret ) -{ - vm->Push( closure ); - vm->Push( env ? *env : vm->_roottable ); - vm->Push( p1 ); - vm->Push( p2 ); - vm->Push( p3 ); - vm->Push( p4 ); - - if ( SQ_SUCCEEDED( sq_call( vm, 5, SQTrue, SQFalse ) ) ) - { - ret = vm->Top(); - vm->Pop(); - vm->Pop(); - return true; - } - - vm->Pop(); - return false; -} - -bool SQDebugServer::RunClosure( HSQUIRRELVM vm, const SQObjectPtr &closure, const SQObject *env, - const SQObjectPtr &p1, const SQObjectPtr &p2, const SQObjectPtr &p3, const SQObjectPtr &p4, - const SQObjectPtr &p5, SQObjectPtr &ret ) -{ - vm->Push( closure ); - vm->Push( env ? *env : vm->_roottable ); - vm->Push( p1 ); - vm->Push( p2 ); - vm->Push( p3 ); - vm->Push( p4 ); - vm->Push( p5 ); - - if ( SQ_SUCCEEDED( sq_call( vm, 6, SQTrue, SQFalse ) ) ) + if ( SQ_SUCCEEDED( sq_call( vm, argc, SQTrue, SQFalse ) ) ) { ret = vm->Top(); vm->Pop(); @@ -7285,12 +7374,12 @@ bool SQDebugServer::GetVariable( HSQUIRRELVM vm, const SQVM::CallInfo *ci, { SQClosure *pClosure = _closure(ci->_closure); SQFunctionProto *func = _fp(pClosure->_function); - SQUnsignedInteger ip = (SQUnsignedInteger)( ci->_ip - func->_instructions - 1 ); + SQUnsignedInteger ip = ci->_ip - func->_instructions; for ( int i = 0; i < func->_nlocalvarinfos; i++ ) { const SQLocalVarInfo &var = func->_localvarinfos[i]; - if ( var._start_op <= ip + 1 && var._end_op >= ip && + if ( var._start_op <= ip && var._end_op + 1 >= ip && _string(index) == _string(var._name) ) { int stackbase = GetStackBase( vm, ci ); @@ -7313,7 +7402,7 @@ bool SQDebugServer::GetVariable( HSQUIRRELVM vm, const SQVM::CallInfo *ci, // Having locals named __this/__vargv will break this hack if ( _string(index)->_len == 6 || _string(index)->_len == 7 ) { - if ( sqstring_t(_SC(KW_THIS)).IsEqualTo( _string(index) ) ) + if ( IsEqual( _SC(KW_THIS), _string(index) ) ) { int stackbase = GetStackBase( vm, ci ); value = vm->_stack._vals[ stackbase ]; @@ -7322,20 +7411,20 @@ bool SQDebugServer::GetVariable( HSQUIRRELVM vm, const SQVM::CallInfo *ci, #if SQUIRREL_VERSION_NUMBER >= 300 else if ( func->_varparams && func->_nlocalvarinfos >= 2 ) { - if ( sqstring_t(_SC(KW_VARGV)).IsEqualTo( _string(index) ) ) + if ( IsEqual( _SC(KW_VARGV), _string(index) ) ) { const SQLocalVarInfo &var = func->_localvarinfos[ func->_nlocalvarinfos - 2 ]; - if ( sqstring_t(_SC("vargv")).IsEqualTo( _string(var._name) ) ) + if ( IsEqual( _SC("vargv"), _string(var._name) ) ) { int stackbase = GetStackBase( vm, ci ); value = vm->_stack._vals[ stackbase + 1 ]; return true; } } - else if ( sqstring_t(_SC(KW_VARGC)).IsEqualTo( _string(index) ) ) + else if ( IsEqual( _SC(KW_VARGC), _string(index) ) ) { const SQLocalVarInfo &var = func->_localvarinfos[ func->_nlocalvarinfos - 2 ]; - if ( sqstring_t(_SC("vargv")).IsEqualTo( _string(var._name) ) ) + if ( IsEqual( _SC("vargv"), _string(var._name) ) ) { int stackbase = GetStackBase( vm, ci ); value = vm->_stack._vals[ stackbase + 1 ]; @@ -7354,15 +7443,36 @@ bool SQDebugServer::GetVariable( HSQUIRRELVM vm, const SQVM::CallInfo *ci, #else else if ( func->_varparams ) { - if ( sqstring_t(_SC(KW_VARGV)).IsEqualTo( _string(index) ) ) + if ( IsEqual( _SC(KW_VARGV), _string(index) ) ) { + // Accessor allows setting of vargv elements, + // uses more memory and is a bit slower in general cases +#if 0 + SQDebugServer *dbg = sqdbg_get_debugger( vm ); + Assert( dbg ); + + if ( sq_type(dbg->m_vargvDelegate) != OT_TABLE ) + { + SQNativeClosure *fnGet = SQNativeClosure::Create( _ss(vm), SQMM_VA_Get ); + SQNativeClosure *fnSet = SQNativeClosure::Create( _ss(vm), SQMM_VA_Set ); + fnGet->_nparamscheck = 2; + fnSet->_nparamscheck = 3; + + SQTable *mt = SQTable::Create( _ss(vm), 2 ); + mt->NewSlot( CreateSQString( vm, _SC("_get") ), fnGet ); + mt->NewSlot( CreateSQString( vm, _SC("_set") ), fnSet ); + dbg->m_vargvDelegate = mt; + } + + SQUserData *ud = SQUserData::Create( _ss(vm), sizeof(int) ); + ud->SetDelegate( _table(dbg->m_vargvDelegate) ); + *(int*)ud->_val = (int)( ci - vm->_callsstack ); + + value = ud; + return true; +#else int size = ci->_vargs.size; - if ( !size ) - return false; - - // This could alternatively register a userdata or table with _get that returns varg params SQArray *arr = SQArray::Create( _ss(vm), size ); - Assert( arr->Size() == size ); for ( int i = 0; i < size; i++ ) @@ -7373,8 +7483,9 @@ bool SQDebugServer::GetVariable( HSQUIRRELVM vm, const SQVM::CallInfo *ci, value = arr; return true; +#endif } - else if ( sqstring_t(_SC(KW_VARGC)).IsEqualTo( _string(index) ) ) + else if ( IsEqual( _SC(KW_VARGC), _string(index) ) ) { value = (SQInteger)ci->_vargs.size; return true; @@ -7395,6 +7506,7 @@ bool SQDebugServer::GetVariable( HSQUIRRELVM vm, const SQVM::CallInfo *ci, case OT_TABLE: { SQTable *t = _table(env); + do { if ( t->Get( index, value ) ) @@ -7443,14 +7555,21 @@ bool SQDebugServer::GetVariable( HSQUIRRELVM vm, const SQVM::CallInfo *ci, // root #ifdef CLOSURE_ROOT SQObjectPtr root; - Verify( SQTable_Get( _table(mtenv)->_delegate, _SC(KW_ROOT), root ) ); - if ( sq_type(root) == OT_TABLE ) // else the user invalidated it, reconnect the client to fix + if ( !SQTable_Get( _table(mtenv)->_delegate, _SC(KW_ROOT), root ) || sq_type(root) != OT_TABLE ) + { + // the user invalidated it, fix it + root = _thread(_ss(vm)->_root_vm)->_roottable; + _table(mtenv)->_delegate->NewSlot( CreateSQString( vm, _SC(KW_ROOT) ), root ); + } #else const SQObjectPtr &root = vm->_roottable; #endif + + if ( _refcounted(env) != _refcounted(root) ) { SQTable *t = _table(root); + do { if ( t->Get( index, value ) ) @@ -7474,12 +7593,12 @@ bool SQDebugServer::SetVariable( HSQUIRRELVM vm, const SQVM::CallInfo *ci, { SQClosure *pClosure = _closure(ci->_closure); SQFunctionProto *func = _fp(pClosure->_function); - SQUnsignedInteger ip = (SQUnsignedInteger)( ci->_ip - func->_instructions - 1 ); + SQUnsignedInteger ip = ci->_ip - func->_instructions; for ( int i = 0; i < func->_nlocalvarinfos; i++ ) { const SQLocalVarInfo &var = func->_localvarinfos[i]; - if ( var._start_op <= ip + 1 && var._end_op >= ip && + if ( var._start_op <= ip && var._end_op + 1 >= ip && _string(index) == _string(var._name) ) { int stackbase = GetStackBase( vm, ci ); @@ -7510,6 +7629,7 @@ bool SQDebugServer::SetVariable( HSQUIRRELVM vm, const SQVM::CallInfo *ci, case OT_TABLE: { SQTable *t = _table(env); + do { if ( t->Set( index, value ) ) @@ -7554,14 +7674,21 @@ bool SQDebugServer::SetVariable( HSQUIRRELVM vm, const SQVM::CallInfo *ci, // root #ifdef CLOSURE_ROOT SQObjectPtr root; - Verify( SQTable_Get( _table(mtenv)->_delegate, _SC(KW_ROOT), root ) ); - if ( sq_type(root) == OT_TABLE ) // else the user invalidated it, reconnect the client to fix + if ( !SQTable_Get( _table(mtenv)->_delegate, _SC(KW_ROOT), root ) || sq_type(root) != OT_TABLE ) + { + // the user invalidated it, fix it + root = _thread(_ss(vm)->_root_vm)->_roottable; + _table(mtenv)->_delegate->NewSlot( CreateSQString( vm, _SC(KW_ROOT) ), root ); + } #else const SQObjectPtr &root = vm->_roottable; #endif + + if ( _refcounted(env) != _refcounted(root) ) { SQTable *t = _table(root); + do { if ( t->Set( index, value ) ) @@ -7704,13 +7831,16 @@ bool SQDebugServer::Get( const objref_t &obj, SQObjectPtr &value ) case objref_t::STACK: { HSQUIRRELVM vm = GetThread( obj.stack.thread ); + const SQVM::CallInfo *ci = vm->_callsstack + obj.stack.frame; - if ( vm && obj.stack.frame >= 0 && obj.stack.frame <= vm->ci - vm->_callsstack ) + if ( vm && + obj.stack.frame >= 0 && obj.stack.frame <= vm->ci - vm->_callsstack && + sq_type(ci->_closure) == OT_CLOSURE ) { - const SQVM::CallInfo &ci = vm->_callsstack[ obj.stack.frame ]; - if ( sq_type(ci._closure) == OT_CLOSURE && - ( ci._ip - _fp(_closure(ci._closure)->_function)->_instructions <= obj.stack.end ) && - obj.stack.index >= 0 && obj.stack.index < (int)vm->_stack.size() ) + int ip = ci->_ip - _fp(_closure(ci->_closure)->_function)->_instructions; + + if ( ip >= obj.stack.start && ip <= obj.stack.end && + obj.stack.index >= 0 && obj.stack.index < (int)vm->_stack.size() ) { value = vm->_stack._vals[ obj.stack.index ]; return true; @@ -7724,6 +7854,54 @@ bool SQDebugServer::Get( const objref_t &obj, SQObjectPtr &value ) value = (SQInteger)obj.val; return true; } + case objref_t::VIRTUAL_REF: + { + if ( ISREFCOUNTED( sq_type(obj.src) ) ) + { + value = (SQInteger)_refcounted(obj.src)->_uiRef; + return true; + } + + return false; + } + case objref_t::VIRTUAL_SIZE: + { + switch ( sq_type(obj.src) ) + { + case OT_ARRAY: + value = _array(obj.src)->Size(); + return true; + case OT_TABLE: + value = _table(obj.src)->CountUsed(); + return true; + default: + return false; + } + } + case objref_t::VIRTUAL_ALLOCATED: + { + if ( sq_type(obj.src) == OT_ARRAY ) + { + value = (SQInteger)_array(obj.src)->_values.capacity(); + return true; + } + + return false; + } + case objref_t::VIRTUAL_STATE: + { + switch ( sq_type(obj.src) ) + { + case OT_THREAD: + value = sq_getvmstate( _thread(obj.src) ); + return true; + case OT_GENERATOR: + value = (SQInteger)_generator(obj.src)->_state; + return true; + default: + return false; + } + } default: UNREACHABLE(); } } @@ -7735,7 +7913,7 @@ bool SQDebugServer::Set( const objref_t &obj, const SQObjectPtr &value ) if ( obj.type & objref_t::PTR ) { - *(obj.ptr) = value; + *obj.ptr = value; return true; } @@ -7840,13 +8018,16 @@ bool SQDebugServer::Set( const objref_t &obj, const SQObjectPtr &value ) case objref_t::STACK: { HSQUIRRELVM vm = GetThread( obj.stack.thread ); + const SQVM::CallInfo *ci = vm->_callsstack + obj.stack.frame; - if ( vm && obj.stack.frame >= 0 && obj.stack.frame <= vm->ci - vm->_callsstack ) + if ( vm && + obj.stack.frame >= 0 && obj.stack.frame <= vm->ci - vm->_callsstack && + sq_type(ci->_closure) == OT_CLOSURE ) { - const SQVM::CallInfo &ci = vm->_callsstack[ obj.stack.frame ]; - if ( sq_type(ci._closure) == OT_CLOSURE && - ( ci._ip - _fp(_closure(ci._closure)->_function)->_instructions <= obj.stack.end ) && - obj.stack.index >= 0 && obj.stack.index < (int)vm->_stack.size() ) + int ip = ci->_ip - _fp(_closure(ci->_closure)->_function)->_instructions; + + if ( ip >= obj.stack.start && ip <= obj.stack.end && + obj.stack.index >= 0 && obj.stack.index < (int)vm->_stack.size() ) { vm->_stack._vals[ obj.stack.index ] = value; return true; @@ -7855,10 +8036,6 @@ bool SQDebugServer::Set( const objref_t &obj, const SQObjectPtr &value ) return false; } - case objref_t::INT: - { - return false; - } default: UNREACHABLE(); } } @@ -7952,7 +8129,7 @@ bool SQDebugServer::Increment( const objref_t &obj, int amt ) if ( obj.type & objref_t::PTR ) { - _check( *(obj.ptr) ); + _check( *obj.ptr ); return true; } @@ -8004,13 +8181,16 @@ bool SQDebugServer::Increment( const objref_t &obj, int amt ) case objref_t::STACK: { HSQUIRRELVM vm = GetThread( obj.stack.thread ); + const SQVM::CallInfo *ci = vm->_callsstack + obj.stack.frame; - if ( vm && obj.stack.frame >= 0 && obj.stack.frame <= vm->ci - vm->_callsstack ) + if ( vm && + obj.stack.frame >= 0 && obj.stack.frame <= vm->ci - vm->_callsstack && + sq_type(ci->_closure) == OT_CLOSURE ) { - const SQVM::CallInfo &ci = vm->_callsstack[ obj.stack.frame ]; - if ( sq_type(ci._closure) == OT_CLOSURE && - ( ci._ip - _fp(_closure(ci._closure)->_function)->_instructions <= obj.stack.end ) && - obj.stack.index >= 0 && obj.stack.index < (int)vm->_stack.size() ) + int ip = ci->_ip - _fp(_closure(ci->_closure)->_function)->_instructions; + + if ( ip >= obj.stack.start && ip <= obj.stack.end && + obj.stack.index >= 0 && obj.stack.index < (int)vm->_stack.size() ) { SQObjectPtr &value = vm->_stack._vals[ obj.stack.index ]; _check( value ); @@ -8029,12 +8209,16 @@ bool SQDebugServer::Increment( const objref_t &obj, int amt ) // // A very basic compiler that parses strings, characters, numbers (dec, hex, oct, bin, flt), identifiers, +// block comments (/**/), // keywords (this, null, true, false), -// unary operators (-, ~, !, typeof, delete, clone), -// binary operators (+, -, *, /, %, <<, >>, >>>, &, |, ^, <, >, <=, >=, <=>, ==, !=, &&, ||, in, instanceof), +// unary operators (-, ~, !, typeof, delete, clone, *, &), +// binary operators (+, -, *, /, %, <<, >>, >>>, &, |, ^, <, >, <=, >=, <=>, ==, !=, &&, ||, in, !in, instanceof), +// ternary operator (a ? b : c), // prefix/postfix increment/decrement operators, // newslot and (compound) assignment operators, -// root (::) and identifier access (a.b, a[b]), function calls and grouping parantheses. +// root (::) and identifier access (a.b, a[b]), +// function calls and grouping parantheses, +// new array and table constructor ([1, 2, 3], {a = 1, [b] = 2}) // // Variable evaluation is done in given stack frame as they are parsed, state is not kept // @@ -8044,9 +8228,11 @@ public: enum { Err_InvalidToken = -50, + Err_UnfinishedComment, Err_UnfinishedString, Err_UnfinishedChar, Err_InvalidEscape, + Err_InvalidOctalEscape, Err_InvalidXEscape, Err_InvalidU16Escape, Err_InvalidU32Escape, @@ -8119,7 +8305,8 @@ public: Token_BwOr = 0xA0 | _op, Token_In = 0xB0 | _op, - Token_InstanceOf = 0xB1 | _op, + Token_NotIn = 0xB1 | _op, + Token_InstanceOf = 0xB2 | _op, Token_LogicalAnd = 0xC0 | _op, Token_LogicalOr = 0xD0 | _op, @@ -8154,7 +8341,7 @@ public: }; #ifndef SQDBG_COMPILER_MAX_PARAMETER_COUNT -#define SQDBG_COMPILER_MAX_PARAMETER_COUNT 6 +#define SQDBG_COMPILER_MAX_PARAMETER_COUNT 8 #endif #ifndef SQDBG_COMPILER_MAX_UNARY_STACK #define SQDBG_COMPILER_MAX_UNARY_STACK 4 @@ -8198,9 +8385,11 @@ public: CCompiler( const CCompiler & ); CCompiler &operator=( const CCompiler & ); - ECompileReturnCode Evaluate( SQDebugServer *dbg, HSQUIRRELVM vm, const SQVM::CallInfo *ci, SQObjectPtr &val, - int closer = 0 ) + ECompileReturnCode Evaluate( SQDebugServer *dbg, HSQUIRRELVM vm, int frame, SQObjectPtr &val, + int closer = Token_End ) { + Assert( frame == INVALID_FRAME || IsValidStackFrame( vm, frame ) ); + token_t token; int prevtoken = 0; @@ -8216,6 +8405,8 @@ public: char incrop = 0; SQObjectPtr callenv; + callenv._type = (SQObjectType)0; + SQObjectPtr valbuf[2]; objref_t obj; @@ -8234,14 +8425,16 @@ public: m_lastToken = token; - if ( !dbg->GetObj_Frame( vm, ci, token._string, obj, val ) ) + if ( !dbg->GetObj_Frame( vm, frame, token._string, obj, val ) ) { // allow non-existent key if ( Next() != Token_NewSlot ) return CompileReturnCode_DoesNotExist; // implicit this. - val = ci ? vm->_stack._vals[ GetStackBase( vm, ci ) ] : vm->_roottable; + val = frame != INVALID_FRAME ? + vm->_stack._vals[ GetStackBase( vm, frame ) ] : + vm->_roottable; switch ( sq_type(val) ) { @@ -8289,9 +8482,7 @@ public: if ( !ExpectsValue( prevtoken ) ) return CompileReturnCode_Unsupported; - val.Null(); - val._type = OT_INTEGER; - val._unVal.nInteger = token._integer; + val = (SQInteger)token._integer; prevtoken = Token_Integer; break; @@ -8301,9 +8492,7 @@ public: if ( !ExpectsValue( prevtoken ) ) return CompileReturnCode_Unsupported; - val.Null(); - val._type = OT_FLOAT; - val._unVal.fFloat = token._float; + val = (SQFloat)token._float; prevtoken = Token_Float; break; @@ -8336,7 +8525,9 @@ public: if ( !ExpectsIdentifier( prevtoken ) ) return CompileReturnCode_Unsupported; - val = ci ? vm->_stack._vals[ GetStackBase( vm, ci ) ] : vm->_roottable; + val = frame != INVALID_FRAME ? + vm->_stack._vals[ GetStackBase( vm, frame ) ] : + vm->_roottable; prevtoken = Token_Value; break; @@ -8353,7 +8544,7 @@ unary: if ( deleteop ) { if ( !dbg->Delete( obj, val ) ) - return CompileReturnCode_Unsupported; + return CompileReturnCode_OpFailure; deleteop = 0; prevtoken = Token_Value; @@ -8361,7 +8552,7 @@ unary: else if ( incrop ) { if ( !dbg->Increment( obj, ( incrop & 0x1 ) ? 1 : -1 ) ) - return CompileReturnCode_Unsupported; + return CompileReturnCode_OpFailure; if ( incrop & 0x8 ) // prefix dbg->Get( obj, val ); @@ -8376,14 +8567,24 @@ unary: while ( unaryidx != -1 ) { if ( !UnaryOp( dbg, unarybuf[unaryidx] | _op, val ) ) - return CompileReturnCode_Unsupported; + return CompileReturnCode_OpFailure; unaryidx--; } } if ( !ExpectsValue( prevtoken ) ) + { + token = Lex(); + + if ( token.type == Token_In ) + { + token.type = Token_NotIn; + goto binary; + } + return CompileReturnCode_Unsupported; + } if ( unaryidx + 1 >= (int)sizeof(unarybuf) ) return CompileReturnCode_OpBufferFull; @@ -8395,6 +8596,10 @@ unary: break; } case Token_Sub: +#ifdef SUPPORTS_DEREF_OP + case Token_Mul: + case Token_BwAnd: +#endif { if ( ExpectsValue( prevtoken ) ) goto unary; @@ -8402,13 +8607,17 @@ unary: case Token_In: case Token_InstanceOf: case Token_Add: +#ifndef SUPPORTS_DEREF_OP case Token_Mul: +#endif case Token_Div: case Token_Mod: case Token_LShift: case Token_RShift: case Token_URShift: +#ifndef SUPPORTS_DEREF_OP case Token_BwAnd: +#endif case Token_BwXor: case Token_BwOr: case Token_LogicalAnd: @@ -8421,6 +8630,7 @@ unary: case Token_Eq: case Token_NotEq: { +binary: // identifier boundary if ( !IsValue( prevtoken ) ) return CompileReturnCode_Unsupported; @@ -8430,7 +8640,7 @@ unary: if ( deleteop ) { if ( !dbg->Delete( obj, val ) ) - return CompileReturnCode_Unsupported; + return CompileReturnCode_OpFailure; deleteop = 0; prevtoken = Token_Value; @@ -8438,7 +8648,7 @@ unary: else if ( incrop ) { if ( !dbg->Increment( obj, ( incrop & 0x1 ) ? 1 : -1 ) ) - return CompileReturnCode_Unsupported; + return CompileReturnCode_OpFailure; if ( incrop & 0x8 ) // prefix dbg->Get( obj, val ); @@ -8451,7 +8661,7 @@ unary: while ( unaryidx != -1 ) { if ( !UnaryOp( dbg, unarybuf[unaryidx] | _op, val ) ) - return CompileReturnCode_Unsupported; + return CompileReturnCode_OpFailure; unaryidx--; } @@ -8465,8 +8675,8 @@ unary: { SQObjectPtr &lhs = valbuf[opbufidx]; - if ( !TwoArgOp( dbg, prev, lhs, val, val ) ) - return CompileReturnCode_Unsupported; + if ( !BinaryOp( dbg, prev, lhs, val, val ) ) + return CompileReturnCode_OpFailure; lhs.Null(); opbufidx--; @@ -8479,10 +8689,19 @@ unary: // Don't evaluate both sides // In both shortcuts, lhs is returned - switch ( token.type ) + if ( ( token.type == Token_LogicalAnd && IsFalse( val ) ) || + ( token.type == Token_LogicalOr && !IsFalse( val ) ) ) { - case Token_LogicalAnd: if ( IsFalse( val ) ) return LexAll( closer ); break; - case Token_LogicalOr: if ( !IsFalse( val ) ) return LexAll( closer ); break; + ECompileReturnCode res = LexAll( closer, true ); + + if ( res != CompileReturnCode_Success ) + return res; + + if ( m_prevToken != '?' && closer != Token_End ) + m_cur--; + + prevtoken = Token_Value; + break; } Assert( opbufidx + 1 < 2 ); @@ -8493,7 +8712,6 @@ unary: opbufidx++; opbuf[opbufidx] = (unsigned char)( token.type & ~_op ); valbuf[opbufidx] = val; - prevtoken = Token_Operator; break; } @@ -8519,32 +8737,125 @@ unary: break; } + case '?': + { + // identifier boundary + if ( !IsValue( prevtoken ) ) + return CompileReturnCode_Unsupported; + + if ( prevtoken == Token_Ref ) + { + if ( deleteop ) + { + if ( !dbg->Delete( obj, val ) ) + return CompileReturnCode_OpFailure; + + deleteop = 0; + prevtoken = Token_Value; + } + else if ( incrop ) + { + if ( !dbg->Increment( obj, ( incrop & 0x1 ) ? 1 : -1 ) ) + return CompileReturnCode_OpFailure; + + if ( incrop & 0x8 ) // prefix + dbg->Get( obj, val ); + + incrop = 0; + prevtoken = Token_Value; + } + } + + while ( unaryidx != -1 ) + { + if ( !UnaryOp( dbg, unarybuf[unaryidx] | _op, val ) ) + return CompileReturnCode_OpFailure; + + unaryidx--; + } + + while ( opbufidx != -1 ) + { + if ( !BinaryOp( dbg, opbuf[opbufidx] | _op, valbuf[opbufidx], val, val ) ) + return CompileReturnCode_OpFailure; + + opbufidx--; + } + + if ( !IsFalse( val ) ) + { + ECompileReturnCode res = Evaluate( dbg, vm, frame, val, ':' ); + + if ( res != CompileReturnCode_Success ) + return res; + + if ( m_prevToken != ':' ) + return CompileReturnCode_Unsupported; + + res = LexAll( closer ); + + if ( res != CompileReturnCode_Success ) + return res; + + if ( closer != Token_End ) + m_cur--; + } + else + { + ECompileReturnCode res = LexAll( ':' ); + + if ( res != CompileReturnCode_Success ) + return res; + + res = Evaluate( dbg, vm, frame, val, closer ); + + if ( res != CompileReturnCode_Success ) + return res; + + if ( closer != Token_End ) + m_cur--; + } + + prevtoken = Token_Value; + break; + } case Token_DoubleColon: { if ( !ExpectsIdentifier( prevtoken ) ) return CompileReturnCode_Unsupported; - token_t next = Lex(); + token = Lex(); - if ( next.type != Token_Identifier ) +#ifdef CLOSURE_ROOT + const SQVM::CallInfo *ci = vm->_callsstack + frame; + const SQObjectPtr &root = + ( frame != INVALID_FRAME && + sq_type(ci->_closure) == OT_CLOSURE && _closure(ci->_closure)->_root ) ? + _closure(ci->_closure)->_root->_obj : + vm->_roottable; +#else + const SQObjectPtr &root = vm->_roottable; +#endif + + if ( token.type != Token_Identifier ) { m_lastToken.type = 0; - val = vm->_roottable; + val = root; return CompileReturnCode_Unsupported; } - m_lastToken = next; + m_lastToken = token; - if ( !dbg->GetObj_Var( vm->_roottable, next._string, true, obj, val ) ) + if ( !dbg->GetObj_Var( root, token._string, true, obj, val ) ) { // allow non-existent key if ( Next() != Token_NewSlot ) return CompileReturnCode_DoesNotExist; - Assert( sq_type(vm->_roottable) == OT_TABLE ); + Assert( sq_type(root) == OT_TABLE ); obj.type = objref_t::TABLE; - obj.src = vm->_roottable; - obj.key = CreateSQString( dbg, next._string ); + obj.src = root; + obj.key = CreateSQString( dbg, token._string ); prevtoken = Token_PendingKey; } else @@ -8563,9 +8874,87 @@ unary: token_t next = Lex(); - if ( next.type != Token_Identifier ) + if ( next.type == INTERNAL_TAG_PREFIX ) { - m_lastToken = token; + next = Lex(); + + if ( next.type == Token_Identifier ) + { + if ( next._string.IsEqualTo( "refs" ) ) + { + if ( ISREFCOUNTED( sq_type(val) ) ) + { + SQInteger refs = (SQInteger)_refcounted(val)->_uiRef - 1; + obj.type = objref_t::VIRTUAL_REF; + obj.src = val; + val = refs; + m_lastRef = obj; + prevtoken = Token_Value; + break; + } + } + else if ( next._string.IsEqualTo( "size" ) ) + { + if ( sq_type(val) == OT_ARRAY ) + { + obj.type = objref_t::VIRTUAL_SIZE; + obj.src = val; + val = _array(val)->Size(); + m_lastRef = obj; + prevtoken = Token_Value; + break; + } + else if ( sq_type(val) == OT_TABLE ) + { + obj.type = objref_t::VIRTUAL_SIZE; + obj.src = val; + val = _table(val)->CountUsed(); + m_lastRef = obj; + prevtoken = Token_Value; + break; + } + } + else if ( next._string.IsEqualTo( "allocated" ) ) + { + if ( sq_type(val) == OT_ARRAY ) + { + obj.type = objref_t::VIRTUAL_ALLOCATED; + obj.src = val; + val = (SQInteger)_array(val)->_values.capacity(); + m_lastRef = obj; + prevtoken = Token_Value; + break; + } + } + else if ( next._string.IsEqualTo( "state" ) ) + { + if ( sq_type(val) == OT_THREAD ) + { + obj.type = objref_t::VIRTUAL_STATE; + obj.src = val; + val = sq_getvmstate( _thread(val) ); + m_lastRef = obj; + prevtoken = Token_Value; + break; + } + else if ( sq_type(val) == OT_GENERATOR ) + { + obj.type = objref_t::VIRTUAL_STATE; + obj.src = val; + val = (SQInteger)_generator(val)->_state; + m_lastRef = obj; + prevtoken = Token_Value; + break; + } + } + } + + m_lastToken.type = '.'; + return CompileReturnCode_Unsupported; + } + else if ( next.type != Token_Identifier ) + { + m_lastToken.type = '.'; return CompileReturnCode_Unsupported; } @@ -8627,14 +9016,127 @@ unary: break; } + case '{': + { + if ( !ExpectsValue( prevtoken ) ) + return CompileReturnCode_Unsupported; + + // new table + SQObjectPtr key; + SQTable *tbl = SQTable::Create( _ss(vm), 0 ); + + for (;;) + { + token_t next = Lex(); + + if ( next.type == '[' ) + { + ECompileReturnCode res = Evaluate( dbg, vm, frame, key, ']' ); + + if ( res != CompileReturnCode_Success ) + { + val = key; + tbl->Release(); + return res; + } + + if ( m_prevToken != ']' ) + { + val = key; + tbl->Release(); + return CompileReturnCode_Unsupported; + } + } + else if ( next.type == Token_Identifier ) + { + key = CreateSQString( dbg, next._string ); + } + else if ( next.type == '}' ) + { + break; + } + else + { + tbl->Release(); + return CompileReturnCode_Unsupported; + } + + next = Lex(); + + if ( next.type != Token_Assign ) + { + tbl->Release(); + return CompileReturnCode_Unsupported; + } + + ECompileReturnCode res = Evaluate( dbg, vm, frame, val, '}' ); + + if ( res != CompileReturnCode_Success ) + { + tbl->Release(); + return res; + } + + tbl->NewSlot( key, val ); + val.Null(); + + if ( m_prevToken == ',' ) + continue; + + if ( m_prevToken == '}' ) + break; + + tbl->Release(); + return CompileReturnCode_Unsupported; + } + + val = tbl; + prevtoken = Token_Value; + break; + } case '[': { + // new array + if ( ExpectsValue( prevtoken ) ) + { + SQArray *arr = SQArray::Create( _ss(vm), 0 ); + + for (;;) + { + ECompileReturnCode res = Evaluate( dbg, vm, frame, val, ']' ); + + if ( res == CompileReturnCode_Success ) + { + arr->Append( val ); + val.Null(); + } + else if ( res != CompileReturnCode_NoValue ) + { + arr->Release(); + return res; + } + + if ( m_prevToken == ',' ) + continue; + + if ( m_prevToken == ']' ) + break; + + arr->Release(); + return CompileReturnCode_Unsupported; + } + + val = arr; + prevtoken = Token_Value; + break; + } + if ( !IsValue( prevtoken ) ) return CompileReturnCode_Unsupported; - SQObjectPtr inner, tmp; + SQObjectPtr tmp, self = val; - ECompileReturnCode res = Evaluate( dbg, vm, ci, inner, ']' ); + ECompileReturnCode res = Evaluate( dbg, vm, frame, val, ']' ); if ( res != CompileReturnCode_Success ) return res; @@ -8642,16 +9144,16 @@ unary: if ( m_prevToken != ']' ) return CompileReturnCode_Unsupported; - if ( !dbg->GetObj_Var( val, inner, obj, tmp ) ) + if ( !dbg->GetObj_Var( self, val, obj, tmp ) ) { - SQTable *del = GetDefaultDelegate( vm, sq_type(val) ); - if ( !del || !del->Get( inner, inner ) ) + SQTable *del = GetDefaultDelegate( vm, sq_type(self) ); + if ( !del || !del->Get( val, val ) ) { // allow non-existent key if ( Next() != Token_NewSlot ) return CompileReturnCode_DoesNotExist; - switch ( sq_type(val) ) + switch ( sq_type(self) ) { case OT_TABLE: obj.type = objref_t::TABLE; @@ -8660,7 +9162,7 @@ unary: obj.type = objref_t::CLASS; break; default: - if ( is_delegable(val) && _delegable(val)->_delegate ) + if ( is_delegable(self) && _delegable(self)->_delegate ) { obj.type = objref_t::DELEGABLE_META; break; @@ -8669,14 +9171,13 @@ unary: return CompileReturnCode_DoesNotExist; } - obj.src = val; - obj.key = inner; + obj.src = self; + obj.key = val; prevtoken = Token_PendingKey; } else { - callenv = val; - val = inner; + callenv = self; prevtoken = Token_Value; } } @@ -8692,9 +9193,9 @@ unary: } case '(': { - if ( !IsValue( prevtoken ) ) + if ( ExpectsValue( prevtoken ) ) { - ECompileReturnCode res = Evaluate( dbg, vm, ci, val, ')' ); + ECompileReturnCode res = Evaluate( dbg, vm, frame, val, ')' ); if ( res != CompileReturnCode_Success ) return res; @@ -8706,6 +9207,8 @@ unary: break; } + m_lastToken.type = 0; + callparams_t cp{}; switch ( sq_type(val) ) @@ -8733,11 +9236,13 @@ unary: return CompileReturnCode_Unsupported; } - if ( !_rawval(callenv) ) + // Check against 0 to make 'null' env valid + if ( sq_type(callenv) == 0 ) { - const SQObjectPtr &env = ci ? - vm->_stack._vals[ GetStackBase( vm, ci ) ] : + const SQObjectPtr &env = frame != INVALID_FRAME ? + vm->_stack._vals[ GetStackBase( vm, frame ) ] : vm->_roottable; + cp.params[ cp.paramCount++ ] = env; } else @@ -8749,7 +9254,7 @@ unary: // call parameters for (;;) { - ECompileReturnCode res = Evaluate( dbg, vm, ci, val, ')' ); + ECompileReturnCode res = Evaluate( dbg, vm, frame, val, ')' ); if ( res == CompileReturnCode_Success ) { @@ -8757,9 +9262,14 @@ unary: return CompileReturnCode_CallBufferFull; cp.params[ cp.paramCount++ ] = val; + val.Null(); } else if ( res != CompileReturnCode_NoValue ) { + // For completions to make more sense + if ( res == CompileReturnCode_DoesNotExist && m_lastToken.type != Token_Identifier ) + val.Null(); + return res; } @@ -8769,74 +9279,12 @@ unary: if ( m_prevToken == ')' ) break; + m_lastToken.type = 0; return CompileReturnCode_Unsupported; } - switch ( cp.paramCount ) - { - case 1: - { - if ( !dbg->RunClosure( cp.func, &cp.params[0], val ) ) - return CompileReturnCode_DoesNotExist; - break; - } - case 2: - { - if ( !dbg->RunClosure( cp.func, - &cp.params[0], - cp.params[1], - val ) ) - return CompileReturnCode_DoesNotExist; - break; - } - case 3: - { - if ( !dbg->RunClosure( cp.func, - &cp.params[0], - cp.params[1], - cp.params[2], - val ) ) - return CompileReturnCode_DoesNotExist; - break; - } - case 4: - { - if ( !dbg->RunClosure( cp.func, - &cp.params[0], - cp.params[1], - cp.params[2], - cp.params[3], - val ) ) - return CompileReturnCode_DoesNotExist; - break; - } - case 5: - { - if ( !dbg->RunClosure( cp.func, - &cp.params[0], - cp.params[1], - cp.params[2], - cp.params[3], - cp.params[4], - val ) ) - return CompileReturnCode_DoesNotExist; - break; - } - case 6: - { - if ( !dbg->RunClosure( cp.func, - &cp.params[0], - cp.params[1], - cp.params[2], - cp.params[3], - cp.params[4], - cp.params[5], - val ) ) - return CompileReturnCode_DoesNotExist; - break; - } - default: UNREACHABLE(); - } + if ( !dbg->RunClosure( cp.func, cp.params, cp.paramCount, val ) ) + return CompileReturnCode_CallError; prevtoken = Token_Value; break; @@ -8859,7 +9307,9 @@ unary: return CompileReturnCode_Unsupported; // implicit this. - val = ci ? vm->_stack._vals[ GetStackBase( vm, ci ) ] : vm->_roottable; + val = frame != INVALID_FRAME ? + vm->_stack._vals[ GetStackBase( vm, frame ) ] : + vm->_roottable; } switch ( sq_type(val) ) @@ -8902,7 +9352,7 @@ unary: if ( !ExpectsValue( prevtoken ) ) return CompileReturnCode_Unsupported; - if ( !ci ) + if ( frame == INVALID_FRAME ) return CompileReturnCode_DoesNotExist; token_t next = Lex(); @@ -8910,7 +9360,7 @@ unary: if ( next.type != '[' ) return CompileReturnCode_DoesNotExist; - ECompileReturnCode res = Evaluate( dbg, vm, ci, val, ']' ); + ECompileReturnCode res = Evaluate( dbg, vm, frame, val, ']' ); if ( res != CompileReturnCode_Success ) return res; @@ -8921,6 +9371,8 @@ unary: if ( sq_type(val) != OT_INTEGER ) return CompileReturnCode_DoesNotExist; + const SQVM::CallInfo *ci = vm->_callsstack + frame; + if ( _integer(val) < 0 || _integer(val) >= ci->_vargs.size ) return CompileReturnCode_DoesNotExist; @@ -8934,12 +9386,12 @@ unary: if ( !ExpectsValue( prevtoken ) ) return CompileReturnCode_Unsupported; - if ( !ci ) + if ( frame == INVALID_FRAME ) return CompileReturnCode_DoesNotExist; - val.Null(); - val._type = OT_INTEGER; - val._unVal.nInteger = ci->_vargs.size; + const SQVM::CallInfo *ci = vm->_callsstack + frame; + + val = (SQInteger)ci->_vargs.size; prevtoken = Token_Value; break; @@ -8950,7 +9402,9 @@ unary: if ( !ExpectsValue( prevtoken ) ) return CompileReturnCode_Unsupported; - if ( ci && sq_type(ci->_closure) == OT_CLOSURE ) + const SQVM::CallInfo *ci = vm->_callsstack + frame; + + if ( frame != INVALID_FRAME && sq_type(ci->_closure) == OT_CLOSURE ) { val = _fp(_closure(ci->_closure)->_function)->_sourcename; } @@ -8967,19 +9421,18 @@ unary: if ( !ExpectsValue( prevtoken ) ) return CompileReturnCode_Unsupported; - val.Null(); - val._type = OT_INTEGER; + const SQVM::CallInfo *ci = vm->_callsstack + frame; - if ( ci && sq_type(ci->_closure) == OT_CLOSURE ) + if ( frame != INVALID_FRAME && sq_type(ci->_closure) == OT_CLOSURE ) { Assert( ci->_ip >= _fp(_closure(ci->_closure)->_function)->_instructions && ci->_ip < _fp(_closure(ci->_closure)->_function)->_instructions + _fp(_closure(ci->_closure)->_function)->_ninstructions ); - val._unVal.nInteger = _fp(_closure(ci->_closure)->_function)->GetLine( ci->_ip ); + val = (SQInteger)_fp(_closure(ci->_closure)->_function)->GetLine( ci->_ip ); } else { - val._unVal.nInteger = 0; + val = (SQInteger)0; } prevtoken = Token_Value; @@ -9008,7 +9461,7 @@ unary: SQObjectPtr target = val; - ECompileReturnCode res = Evaluate( dbg, vm, ci, val, closer ); + ECompileReturnCode res = Evaluate( dbg, vm, frame, val, closer ); if ( res != CompileReturnCode_Success ) return res; @@ -9027,20 +9480,20 @@ unary: case Token_AssignRS: case Token_AssignURS: { - if ( !TwoArgOp( dbg, token.type & ~_assign, target, val, val ) ) - return CompileReturnCode_Unsupported; + if ( !BinaryOp( dbg, token.type & ~_assign, target, val, val ) ) + return CompileReturnCode_OpFailure; } case Token_Assign: { if ( !dbg->Set( obj, val ) ) - return CompileReturnCode_Unsupported; + return CompileReturnCode_OpFailure; return CompileReturnCode_Success; } case Token_NewSlot: { if ( !dbg->NewSlot( obj, val ) ) - return CompileReturnCode_Unsupported; + return CompileReturnCode_OpFailure; return CompileReturnCode_Success; } @@ -9065,7 +9518,7 @@ unary: if ( deleteop ) { if ( !dbg->Delete( obj, val ) ) - return CompileReturnCode_Unsupported; + return CompileReturnCode_OpFailure; deleteop = 0; prevtoken = Token_Value; @@ -9073,7 +9526,7 @@ unary: else if ( incrop ) { if ( !dbg->Increment( obj, ( incrop & 0x1 ) ? 1 : -1 ) ) - return CompileReturnCode_Unsupported; + return CompileReturnCode_OpFailure; if ( incrop & 0x8 ) // prefix dbg->Get( obj, val ); @@ -9086,15 +9539,15 @@ unary: while ( unaryidx != -1 ) { if ( !UnaryOp( dbg, unarybuf[unaryidx] | _op, val ) ) - return CompileReturnCode_Unsupported; + return CompileReturnCode_OpFailure; unaryidx--; } while ( opbufidx != -1 ) { - if ( !TwoArgOp( dbg, opbuf[opbufidx] | _op, valbuf[opbufidx], val, val ) ) - return CompileReturnCode_Unsupported; + if ( !BinaryOp( dbg, opbuf[opbufidx] | _op, valbuf[opbufidx], val, val ) ) + return CompileReturnCode_OpFailure; opbufidx--; } @@ -9103,25 +9556,43 @@ unary: { case ']': { - if ( closer != ']' ) - return CompileReturnCode_Unsupported; + if ( closer == ']' ) + return CompileReturnCode_Success; break; } case ')': { - if ( closer != ')' ) - return CompileReturnCode_Unsupported; + if ( closer == ')' ) + return CompileReturnCode_Success; + break; + } + case '}': + { + if ( closer == '}' ) + return CompileReturnCode_Success; break; } case ',': { - if ( closer != ')' ) - return CompileReturnCode_Unsupported; + if ( closer == ')' || closer == ']' || closer == '}' ) + return CompileReturnCode_Success; + break; + } + case ':': + { + if ( closer == ':' ) + return CompileReturnCode_Success; + break; + } + case Token_End: + { + if ( closer == Token_End ) + return CompileReturnCode_Success; break; } } - return CompileReturnCode_Success; + return CompileReturnCode_Unsupported; } else if ( token.type == Err_InvalidToken ) { @@ -9129,6 +9600,7 @@ unary: } else { + m_prevToken = token.type; return CompileReturnCode_SyntaxError; } } @@ -9136,55 +9608,494 @@ unary: } } -private: - ECompileReturnCode LexAll( int closer ) + sqstring_t LastError() { - // fast shortcut - ignore everything - if ( closer == 0 ) - return CompileReturnCode_Success; - - m_prevToken = closer; - - int depth = 0; - int opener; - - switch ( closer ) + switch ( m_prevToken ) { - case ')': opener = '('; break; - case ']': opener = '['; break; - default: UNREACHABLE(); + case Err_InvalidFloat: + return _SC("invalid float literal"); + case Err_InvalidBinary: + return _SC("invalid binary literal"); + case Err_InvalidHexadecimal: + return _SC("invalid hexadecimal literal"); + case Err_InvalidOctal: + return _SC("invalid octal literal"); + case Err_InvalidDecimal: + return _SC("invalid decimal literal"); + case Err_InvalidU32Escape: + return _SC("invalid UTF32 escape"); + case Err_InvalidU16Escape: + return _SC("invalid UTF16 escape"); + case Err_InvalidXEscape: + return _SC("invalid hexadecimal escape"); + case Err_InvalidOctalEscape: + return _SC("invalid octal escape"); + case Err_InvalidEscape: + return _SC("invalid escape char"); + case Err_UnfinishedChar: + return _SC("unfinished char literal"); + case Err_UnfinishedString: + return _SC("unfinished string literal"); + case Err_UnfinishedComment: + return _SC("unfinished block comment"); + default: + return _SC(""); } + } - for (;;) +private: + ECompileReturnCode LexAll( int closer, bool jump = false ) + { + token_t token; + int prevtoken = 0; + + for ( ;; m_prevToken = token.type ) { - token_t token = Lex(); + token = Lex(); - if ( token.type > 0 ) + switch ( token.type ) { - if ( token.type == opener ) + case Token_Identifier: { - depth++; + if ( !ExpectsIdentifier( prevtoken ) ) + return CompileReturnCode_Unsupported; + + prevtoken = Token_Ref; + break; } - else if ( token.type == closer ) + case Token_String: + case Token_Integer: + case Token_Float: + case Token_Null: + case Token_False: + case Token_True: { - if ( depth ) + if ( !ExpectsValue( prevtoken ) ) + return CompileReturnCode_Unsupported; + + prevtoken = Token_Value; + break; + } + case Token_This: + { + if ( !ExpectsIdentifier( prevtoken ) ) + return CompileReturnCode_Unsupported; + + prevtoken = Token_Value; + break; + } + case Token_BwNot: + case Token_Not: + case Token_Typeof: + case Token_Clone: + { +unary: + // identifier boundary + if ( !ExpectsValue( prevtoken ) ) { - depth--; + token = Lex(); + + if ( token.type == Token_In ) + { + token.type = Token_NotIn; + goto binary; + } + + return CompileReturnCode_Unsupported; + } + + prevtoken = Token_Operator; + break; + } + case Token_Sub: +#ifdef SUPPORTS_DEREF_OP + case Token_Mul: + case Token_BwAnd: +#endif + { + if ( ExpectsValue( prevtoken ) ) + goto unary; + } + case Token_In: + case Token_InstanceOf: + case Token_Add: +#ifndef SUPPORTS_DEREF_OP + case Token_Mul: +#endif + case Token_Div: + case Token_Mod: + case Token_LShift: + case Token_RShift: + case Token_URShift: +#ifndef SUPPORTS_DEREF_OP + case Token_BwAnd: +#endif + case Token_BwXor: + case Token_BwOr: + case Token_LogicalAnd: + case Token_LogicalOr: + case Token_Greater: + case Token_GreaterEq: + case Token_Less: + case Token_LessEq: + case Token_Cmp3W: + case Token_Eq: + case Token_NotEq: + { +binary: + // identifier boundary + if ( !IsValue( prevtoken ) ) + return CompileReturnCode_Unsupported; + + prevtoken = Token_Operator; + break; + } + case Token_Increment: + case Token_Decrement: + { + if ( !( prevtoken == Token_Ref || ExpectsIdentifier( prevtoken ) ) ) + return CompileReturnCode_Unsupported; + + break; + } + case '?': + { + // identifier boundary + if ( !IsValue( prevtoken ) ) + return CompileReturnCode_Unsupported; + + // HACKHACK: Exit if the previous statement is to be evaluated + // Breaks easily, but makes valid syntax work correctly + if ( jump ) + { + m_prevToken = token.type; + m_cur--; + return CompileReturnCode_Success; + } + + ECompileReturnCode res = LexAll( ':' ); + + if ( res != CompileReturnCode_Success ) + return res; + + res = LexAll( closer ); + + if ( res != CompileReturnCode_Success ) + return res; + + if ( closer != Token_End ) + m_cur--; + + break; + } + case Token_DoubleColon: + { + token = Lex(); + + if ( token.type != Token_Identifier ) + return CompileReturnCode_Unsupported; + + prevtoken = Token_Ref; + break; + } + case '.': + { + if ( !IsValue( prevtoken ) ) + return CompileReturnCode_Unsupported; + + token_t next = Lex(); + + if ( next.type == INTERNAL_TAG_PREFIX ) + { + next = Lex(); + + if ( next.type == Token_Identifier ) + prevtoken = Token_Value; + + m_lastToken.type = '.'; + return CompileReturnCode_Unsupported; + } + else if ( next.type != Token_Identifier ) + { + m_lastToken.type = '.'; + return CompileReturnCode_Unsupported; + } + + m_lastToken = next; + prevtoken = Token_Ref; + break; + } + case '{': + { + if ( !ExpectsValue( prevtoken ) ) + return CompileReturnCode_Unsupported; + + // new table + for (;;) + { + token_t next = Lex(); + + if ( next.type == '[' ) + { + ECompileReturnCode res = LexAll( ']' ); + + if ( res != CompileReturnCode_Success ) + return res; + + if ( m_prevToken != ']' ) + return CompileReturnCode_Unsupported; + } + else if ( next.type == Token_Identifier ) + { + } + else if ( next.type == '}' ) + { + break; + } + else + { + return CompileReturnCode_Unsupported; + } + + next = Lex(); + + if ( next.type != Token_Assign ) + return CompileReturnCode_Unsupported; + + ECompileReturnCode res = LexAll( '}' ); + + if ( res != CompileReturnCode_Success ) + return res; + + if ( m_prevToken == ',' ) + continue; + + if ( m_prevToken == '}' ) + break; + + return CompileReturnCode_Unsupported; + } + + prevtoken = Token_Value; + break; + } + case '[': + { + // new array + if ( ExpectsValue( prevtoken ) ) + { + for (;;) + { + ECompileReturnCode res = LexAll( ']' ); + + if ( res == CompileReturnCode_Success ) + { + } + else if ( res != CompileReturnCode_NoValue ) + { + return res; + } + + if ( m_prevToken == ',' ) + continue; + + if ( m_prevToken == ']' ) + break; + + return CompileReturnCode_Unsupported; + } + + prevtoken = Token_Value; + break; + } + + if ( !IsValue( prevtoken ) ) + return CompileReturnCode_Unsupported; + + ECompileReturnCode res = LexAll( ']' ); + + if ( res != CompileReturnCode_Success ) + return res; + + if ( m_prevToken != ']' ) + return CompileReturnCode_Unsupported; + + prevtoken = Token_Ref; + break; + } + case '(': + { + if ( ExpectsValue( prevtoken ) ) + { + ECompileReturnCode res = LexAll( ')' ); + + if ( res != CompileReturnCode_Success ) + return res; + + if ( m_prevToken != ')' ) + return CompileReturnCode_Unsupported; + + prevtoken = Token_Value; + break; + } + + // call parameters + for (;;) + { + ECompileReturnCode res = LexAll( ')' ); + + if ( res == CompileReturnCode_Success ) + { + } + else if ( res != CompileReturnCode_NoValue ) + { + return res; + } + + if ( m_prevToken == ',' ) + continue; + + if ( m_prevToken == ')' ) + break; + + return CompileReturnCode_Unsupported; + } + + prevtoken = Token_Value; + break; + } + case Token_Delete: + { + if ( !ExpectsValue( prevtoken ) ) + return CompileReturnCode_Unsupported; + + prevtoken = Token_Delete; + break; + } +#if SQUIRREL_VERSION_NUMBER < 300 + case Token_Parent: + { + if ( !IsValue( prevtoken ) && !ExpectsValue( prevtoken ) ) + return CompileReturnCode_Unsupported; + + prevtoken = Token_Value; + break; + } + case Token_Vargv: + { + if ( !ExpectsValue( prevtoken ) ) + return CompileReturnCode_Unsupported; + + token_t next = Lex(); + + if ( next.type != '[' ) + return CompileReturnCode_DoesNotExist; + + ECompileReturnCode res = LexAll( ']' ); + + if ( res != CompileReturnCode_Success ) + return res; + + if ( m_prevToken != ']' ) + return CompileReturnCode_Unsupported; + + prevtoken = Token_Value; + break; + } + case Token_Vargc: +#endif + case Token_File: + case Token_Line: + { + if ( !ExpectsValue( prevtoken ) ) + return CompileReturnCode_Unsupported; + + prevtoken = Token_Value; + break; + } + case Token_Assign: + case Token_AssignAdd: + case Token_AssignSub: + case Token_AssignMul: + case Token_AssignDiv: + case Token_AssignMod: + case Token_AssignAnd: + case Token_AssignXor: + case Token_AssignOr: + case Token_AssignLS: + case Token_AssignRS: + case Token_AssignURS: + case Token_NewSlot: + { + if ( prevtoken != Token_Ref ) + return CompileReturnCode_Unsupported; + + ECompileReturnCode res = LexAll( closer ); + return res; + } + default: + { + // identifier boundary + if ( token.type > 0 ) + { + m_prevToken = token.type; + + if ( ExpectsValue( prevtoken ) ) + return CompileReturnCode_NoValue; + + switch ( token.type ) + { + case ']': + { + if ( closer == ']' ) + return CompileReturnCode_Success; + break; + } + case ')': + { + if ( closer == ')' ) + return CompileReturnCode_Success; + break; + } + case '}': + { + if ( closer == '}' ) + return CompileReturnCode_Success; + break; + } + case ',': + { + if ( closer == ')' || closer == ']' || closer == '}' ) + return CompileReturnCode_Success; + break; + } + case ':': + { + if ( closer == ':' ) + return CompileReturnCode_Success; + break; + } + case Token_End: + { + if ( closer == Token_End ) + return CompileReturnCode_Success; + break; + } + } + + return CompileReturnCode_Unsupported; + } + else if ( token.type == Err_InvalidToken ) + { + return CompileReturnCode_Unsupported; } else { - return CompileReturnCode_Success; + m_prevToken = token.type; + return CompileReturnCode_SyntaxError; } } } - else if ( token.type == Err_InvalidToken ) - { - return CompileReturnCode_Unsupported; - } - else - { - return CompileReturnCode_SyntaxError; - } } } @@ -9232,7 +10143,7 @@ private: } } - static bool TwoArgOp( SQDebugServer *dbg, + static bool BinaryOp( SQDebugServer *dbg, int op, const SQObjectPtr &lhs, const SQObjectPtr &rhs, SQObjectPtr &out ) { Assert( ( op & _op ) == _op ); @@ -9245,6 +10156,7 @@ private: case Token_Div: return dbg->ArithOp( '/', lhs, rhs, out ); case Token_Mod: return dbg->ArithOp( '%', lhs, rhs, out ); case Token_In: + case Token_NotIn: { switch ( sq_type(rhs) ) { @@ -9270,6 +10182,9 @@ private: SetBool( out, false ); } + if ( op == Token_NotIn ) + _integer(out) = !_integer(out); + return true; } case Token_InstanceOf: @@ -9277,7 +10192,7 @@ private: if ( sq_type(rhs) == OT_CLASS ) { Assert( sq_type(lhs) != OT_INSTANCE || _instance(lhs)->_class ); - SetBool( out, sq_type(lhs) == OT_INSTANCE && _instance(lhs)->_class == _class(rhs) ); + SetBool( out, sq_type(lhs) == OT_INSTANCE && _instance(lhs)->InstanceOf( _class(rhs) ) ); return true; } @@ -9405,12 +10320,11 @@ private: switch ( res ) { case ECMP_EQ: res = 0; break; - case ECMP_G: - case ECMP_GE: res = 1; break; - case ECMP_L: - case ECMP_LE: res = -1; break; + case ECMP_G: res = 1; break; + case ECMP_L: res = -1; break; default: UNREACHABLE(); } + out = (SQInteger)res; return true; } @@ -9521,6 +10435,69 @@ private: return false; } } +#ifdef SUPPORTS_DEREF_OP + case Token_Mul: + { + if ( sq_type(val) == OT_INTEGER ) + { + if ( _integer(val) == 0 ) + { + val.Null(); + return true; + } + + // Find address in gc chain + for ( SQCollectable *t = _ss(dbg->m_pRootVM)->_gc_chain; t; t = t->_next ) + { + if ( (uintptr_t)t == (uintptr_t)_integer(val) ) + { +#if SQUIRREL_VERSION_NUMBER >= 300 + if ( t->GetType() == OT_OUTER ) + break; + + SQObject o; + o._type = t->GetType(); +#else +#define _check( _c, _t ) \ + if ( dynamic_cast< _c * >( t ) ) { o._type = _t; } + SQObject o; + _check( SQTable, OT_TABLE ) + else _check( SQArray, OT_ARRAY ) + else _check( SQInstance, OT_INSTANCE ) + else _check( SQClass, OT_CLASS ) + else _check( SQClosure, OT_CLOSURE ) + else _check( SQNativeClosure, OT_NATIVECLOSURE ) + else _check( SQGenerator, OT_GENERATOR ) + else _check( SQVM, OT_THREAD ) + else _check( SQUserData, OT_USERDATA ) + else break; +#undef _check +#endif + o._unVal.pRefCounted = t; + val = o; + return true; + } + } + } + + return false; + } + case Token_BwAnd: + { + if ( ISREFCOUNTED( sq_type(val) ) && sq_type(val) != OT_STRING ) + { + val = (SQInteger)_refcounted(val); + return true; + } + else if ( sq_type(val) == OT_NULL ) + { + val = (SQInteger)0; + return true; + } + + return false; + } +#endif default: UNREACHABLE(); } } @@ -9532,7 +10509,7 @@ private: { switch ( *m_cur ) { - case ' ': case '\t': case '\r': case '\n': + case ' ': case '\n': case '\r': case '\t': { m_cur++; continue; @@ -9541,6 +10518,10 @@ private: { return *m_cur; } + case '\'': + { + return *m_cur; + } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { @@ -9549,6 +10530,9 @@ private: case '.': case ',': case '[': case ']': case '(': case ')': + case '{': case '}': + case INTERNAL_TAG_PREFIX: + case '?': { return *m_cur; } @@ -9595,7 +10579,16 @@ private: } case '/': { - if ( m_cur + 1 < m_end && m_cur[1] == '=' ) + if ( m_cur + 1 < m_end && m_cur[1] == '*' ) + { + m_cur += 2; + + if ( LexBlockComment() ) + continue; + + return Err_UnfinishedComment; + } + else if ( m_cur + 1 < m_end && m_cur[1] == '=' ) { return Token_AssignDiv; } @@ -9756,16 +10749,12 @@ private: } else { - return Err_InvalidToken; + return ':'; } } - case '\'': - { - return *m_cur; - } default: { - if ( _isalpha( *(unsigned char*)m_cur ) || *m_cur == '_' ) + if ( _isalpha( *m_cur ) || *m_cur == '_' ) { return *m_cur; } @@ -9788,7 +10777,7 @@ private: { switch ( *m_cur ) { - case ' ': case '\t': case '\r': case '\n': + case ' ': case '\n': case '\r': case '\t': { m_cur++; continue; @@ -9798,15 +10787,23 @@ private: ParseString( token ); return token; } + case '\'': + { + ParseChar( token ); + return token; + } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { - ParseNumber( token, false ); + ParseNumber( token ); return token; } case '.': case ',': case '[': case ']': case '(': case ')': + case '{': case '}': + case INTERNAL_TAG_PREFIX: + case '?': { token.type = *m_cur++; return token; @@ -9873,7 +10870,16 @@ private: { m_cur++; - if ( m_cur < m_end && *m_cur == '=' ) + if ( m_cur < m_end && *m_cur == '*' ) + { + m_cur++; + + if ( LexBlockComment() ) + continue; + + token.type = Err_UnfinishedComment; + } + else if ( m_cur < m_end && *m_cur == '=' ) { m_cur++; token.type = Token_AssignDiv; @@ -10096,35 +11102,32 @@ private: } else { - token.type = Err_InvalidToken; + token.type = ':'; } return token; } - case '\'': - { - ParseChar( token ); - return token; - } default: { - if ( _isalpha( *(unsigned char*)m_cur ) || *m_cur == '_' ) + if ( _isalpha( *m_cur ) || *m_cur == '_' ) { char *pStart = m_cur++; - while ( m_cur < m_end && ( _isalnum( *(unsigned char*)m_cur ) || *m_cur == '_' ) ) + while ( m_cur < m_end && ( _isalnum( *m_cur ) || *m_cur == '_' ) ) m_cur++; token.type = Token_Identifier; token._string.Assign( pStart, m_cur - pStart ); - if ( token._string.len == 2 || - token._string.len == 4 || token._string.len == 5 || token._string.len == 6 || - token._string.len == 8 ) + if ( token._string.len >= 2 && token._string.len <= 10 ) { if ( token._string.IsEqualTo("in") ) { token.type = Token_In; } + else if ( token._string.IsEqualTo("instanceof") ) + { + token.type = Token_InstanceOf; + } else if ( token._string.IsEqualTo("this") ) { token.type = Token_This; @@ -10177,6 +11180,20 @@ private: { token.type = Token_Line; } + else if ( token._string.IsEqualTo("if") || + token._string.IsEqualTo("do") || + token._string.IsEqualTo("for") || + token._string.IsEqualTo("while") || + token._string.IsEqualTo("local") || + token._string.IsEqualTo("const") || + token._string.IsEqualTo("throw") || + token._string.IsEqualTo("static") || + token._string.IsEqualTo("return") || + token._string.IsEqualTo("foreach") || + token._string.IsEqualTo("function") ) + { + token.type = Err_InvalidToken; + } } } else @@ -10193,6 +11210,18 @@ private: return token; } + bool LexBlockComment() + { + for (;;) + { + if ( m_cur + 1 >= m_end ) + return false; + + if ( *m_cur++ == '*' && *m_cur++ == '/' ) + return true; + } + } + void ParseString( token_t &token ) { char *pStart = ++m_cur; @@ -10205,7 +11234,6 @@ private: return; } - // end if ( *m_cur == '\"' ) { token.type = Token_String; @@ -10237,19 +11265,17 @@ private: { case '\\': shift_one: - _shift( 0, 1 ); - m_cur++; + _shift( 1, 2 ); break; - case '\"': goto shift_one; - case '\'': goto shift_one; - case 'a': m_cur[1] = '\a'; goto shift_one; - case 'b': m_cur[1] = '\b'; goto shift_one; - case 'f': m_cur[1] = '\f'; goto shift_one; - case 'n': m_cur[1] = '\n'; goto shift_one; - case 'r': m_cur[1] = '\r'; goto shift_one; - case 't': m_cur[1] = '\t'; goto shift_one; - case 'v': m_cur[1] = '\v'; goto shift_one; - case '0': m_cur[1] = '\0'; goto shift_one; + case '\"': *m_cur = '\"'; goto shift_one; + case '\'': *m_cur = '\''; goto shift_one; + case 'a': *m_cur = '\a'; goto shift_one; + case 'b': *m_cur = '\b'; goto shift_one; + case 'f': *m_cur = '\f'; goto shift_one; + case 'n': *m_cur = '\n'; goto shift_one; + case 'r': *m_cur = '\r'; goto shift_one; + case 't': *m_cur = '\t'; goto shift_one; + case 'v': *m_cur = '\v'; goto shift_one; case 'x': #ifndef SQUNICODE { @@ -10261,7 +11287,7 @@ shift_one: unsigned int len = 0; for ( char *x = min( m_cur + 2, m_end ), *end = min( x + 2, m_end ); - x < end && _isxdigit( *(unsigned char*)x ); + x < end && _isxdigit( *x ); x++ ) { len++; @@ -10296,7 +11322,7 @@ shift_one: unsigned int len = 0; for ( char *x = min( m_cur + 2, m_end ), *end = min( x + 4, m_end ); - x < end && _isxdigit( *(unsigned char*)x ); + x < end && _isxdigit( *x ); x++ ) { len++; @@ -10315,7 +11341,7 @@ shift_one: return; } - if ( val <= 0xFF ) + if ( val <= 0x7F ) { m_cur[0] = (char)val; @@ -10370,7 +11396,7 @@ shift_one: unsigned int len = 0; for ( char *x = min( m_cur + 2, m_end ), *end = min( x + 8, m_end ); - x < end && _isxdigit( *(unsigned char*)x ); + x < end && _isxdigit( *x ); x++ ) { len++; @@ -10389,7 +11415,7 @@ shift_one: return; } - if ( val <= 0xFF ) + if ( val <= 0x7F ) { m_cur[0] = (char)val; @@ -10403,15 +11429,16 @@ shift_one: _shift( 2, len + 2 ); break; } - else if ( UTF_SURROGATE(val) ) - { - // next could be \u or \U with differing lengths, - // just ignore surrogates - token.type = Err_InvalidToken; - return; - } else if ( val <= 0xFFFF ) { + if ( UTF_SURROGATE(val) ) + { + // next could be \u or \U with differing lengths, + // just ignore surrogates + token.type = Err_InvalidToken; + return; + } + UTF8_3_FROM_UTF32( (unsigned char*)m_cur, val ); _shift( 3, len + 2 ); @@ -10425,6 +11452,31 @@ shift_one: break; } } + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': + { + unsigned int len = 0; + for ( char *x = m_cur + 1, *end = min( x + 3, m_end ); + x < end && IN_RANGE_CHAR( *x, '0', '7' ); + x++ ) + { + len++; + } + + Assert( len >= 1 ); + + unsigned int val; + if ( !atoo( { m_cur + 1, len }, &val ) || val > 255 ) + { + token.type = Err_InvalidOctalEscape; + return; + } + + m_cur[0] = (char)val; + + _shift( 1, len + 1 ); + break; + } default: token.type = Err_InvalidEscape; return; @@ -10468,7 +11520,6 @@ shift_one: case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'v': c = '\v'; break; - case '0': c = '\0'; break; case 'x': { char *pStart = ++m_cur; @@ -10480,14 +11531,42 @@ shift_one: m_cur >= m_end || *m_cur != '\'' ) { - token.type = Err_UnfinishedChar; + token.type = Err_InvalidXEscape; return; } unsigned int val; if ( !atox( { pStart, (unsigned int)( m_cur - pStart ) }, &val ) ) { - token.type = Err_UnfinishedChar; + token.type = Err_InvalidXEscape; + return; + } + + token.type = Token_Integer; + token._integer = val; + m_cur++; + return; + } + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': + { + char *pStart = m_cur++; + while ( m_cur < m_end && IN_RANGE_CHAR( *m_cur, '0', '7' ) ) + m_cur++; + + if ( m_cur == pStart || + (int)( m_cur - pStart ) > 3 || + m_cur >= m_end || + *m_cur != '\'' ) + { + token.type = Err_InvalidOctalEscape; + return; + } + + unsigned int val; + if ( !atoo( { pStart, (unsigned int)( m_cur - pStart ) }, &val ) || val > 255 ) + { + token.type = Err_InvalidOctalEscape; return; } @@ -10497,7 +11576,7 @@ shift_one: return; } default: - token.type = Err_UnfinishedChar; + token.type = Err_InvalidEscape; return; } } @@ -10516,7 +11595,7 @@ shift_one: } } - void ParseNumber( token_t &token, bool neg ) + void ParseNumber( token_t &token ) { const char *pStart = m_cur; static const int DECIMAL = 0; @@ -10534,8 +11613,22 @@ shift_one: { m_cur++; - while ( m_cur < m_end && _isxdigit( *(unsigned char*)m_cur ) ) - m_cur++; + while ( m_cur < m_end ) + { + if ( _isxdigit( *m_cur ) ) + { + m_cur++; + } + else if ( !_isalnum( *m_cur ) ) + { + break; + } + else + { + token.type = Err_InvalidHexadecimal; + return; + } + } string_t str; str.Assign( pStart, m_cur - pStart ); @@ -10548,18 +11641,28 @@ shift_one: Verify( atox( str, &token._integer ) ); token.type = Token_Integer; - - if ( neg ) - token._integer = -token._integer; - return; } case 'b': { m_cur++; - while ( m_cur < m_end && IN_RANGE_CHAR( *(unsigned char*)m_cur, '0', '1' ) ) - m_cur++; + while ( m_cur < m_end ) + { + if ( IN_RANGE_CHAR( *m_cur, '0', '1' ) ) + { + m_cur++; + } + else if ( !_isalnum( *m_cur ) ) + { + break; + } + else + { + token.type = Err_InvalidBinary; + return; + } + } string_t str; str.Assign( pStart, m_cur - pStart ); @@ -10587,15 +11690,26 @@ shift_one: } } - if ( neg ) - token._integer = -token._integer; - return; } default: { - while ( m_cur < m_end && IN_RANGE_CHAR( *(unsigned char*)m_cur, '0', '7' ) ) - m_cur++; + while ( m_cur < m_end ) + { + if ( IN_RANGE_CHAR( *m_cur, '0', '7' ) ) + { + m_cur++; + } + else if ( !_isalnum( *m_cur ) ) + { + break; + } + else + { + token.type = Err_InvalidOctal; + return; + } + } type = OCTAL; break; @@ -10604,13 +11718,13 @@ shift_one: } else { - Assert( IN_RANGE_CHAR( *(unsigned char*)m_cur, '1', '9' ) ); + Assert( IN_RANGE_CHAR( *m_cur, '1', '9' ) ); do { m_cur++; } - while ( IN_RANGE_CHAR( *(unsigned char*)m_cur, '0', '9' ) ); + while ( IN_RANGE_CHAR( *m_cur, '0', '9' ) ); type = DECIMAL; } @@ -10620,7 +11734,7 @@ shift_one: type = FLOAT; m_cur++; - while ( m_cur < m_end && IN_RANGE_CHAR( *(unsigned char*)m_cur, '0', '9' ) ) + while ( m_cur < m_end && IN_RANGE_CHAR( *m_cur, '0', '9' ) ) m_cur++; } @@ -10646,7 +11760,7 @@ shift_one: } } - while ( m_cur < m_end && IN_RANGE_CHAR( *(unsigned char*)m_cur, '0', '9' ) ) + while ( m_cur < m_end && IN_RANGE_CHAR( *m_cur, '0', '9' ) ) m_cur++; } @@ -10665,10 +11779,6 @@ shift_one: Verify( atoi( str, &token._integer ) ); token.type = Token_Integer; - - if ( neg ) - token._integer = -token._integer; - return; } case OCTAL: @@ -10681,10 +11791,6 @@ shift_one: Verify( atoo( str, &token._integer ) ); token.type = Token_Integer; - - if ( neg ) - token._integer = -token._integer; - return; } case FLOAT: @@ -10694,10 +11800,6 @@ shift_one: token.type = Token_Float; token._float = (SQFloat)strtod( str.ptr, NULL ); str.ptr[str.len] = c; - - if ( neg ) - token._float = -token._float; - return; } default: UNREACHABLE(); @@ -10706,10 +11808,10 @@ shift_one: }; SQDebugServer::ECompileReturnCode SQDebugServer::Evaluate( string_t &expression, - HSQUIRRELVM vm, const SQVM::CallInfo *ci, SQObjectPtr &ret ) + HSQUIRRELVM vm, int frame, SQObjectPtr &ret ) { CCompiler c( expression ); - ECompileReturnCode r = c.Evaluate( this, vm, ci, ret ); + ECompileReturnCode r = c.Evaluate( this, vm, frame, ret ); return r; } @@ -10717,7 +11819,7 @@ SQDebugServer::ECompileReturnCode SQDebugServer::Evaluate( string_t &expression, HSQUIRRELVM vm, const SQVM::CallInfo *ci, SQObjectPtr &ret, objref_t &obj ) { CCompiler c( expression ); - ECompileReturnCode r = c.Evaluate( this, vm, ci, ret ); + ECompileReturnCode r = c.Evaluate( this, vm, ci - vm->_callsstack, ret ); obj = c.m_lastRef; return r; } @@ -10923,6 +12025,7 @@ bool SQDebugServer::GetObj_Var( const SQObjectPtr &var, const SQObjectPtr &key, case OT_TABLE: { SQTable *t = _table(var); + do { if ( t->Get( key, value ) ) @@ -10995,7 +12098,7 @@ bool SQDebugServer::GetObj_Var( const SQObjectPtr &var, const SQObjectPtr &key, if ( sq_type(key) == OT_INTEGER && _integer(key) >= 0 && _integer(key) < _string(var)->_len ) { - out.type = objref_t::INT; + out.type = (objref_t::EOBJREF)( objref_t::INT | objref_t::READONLY ); #ifdef SQUNICODE out.val = (int)_string(var)->_val[ _integer(key) ]; #else @@ -11020,15 +12123,18 @@ bool SQDebugServer::GetObj_Var( const SQObjectPtr &var, const SQObjectPtr &key, case OT_TABLE: case OT_USERDATA: { - SQObjectPtr mm; - if ( _delegable(var)->GetMetaMethod( m_pRootVM, MT_GET, mm ) ) + if ( _delegable(var)->_delegate ) { - if ( RunClosure( mm, &var, key, value ) ) + SQObjectPtr mm; + if ( _delegable(var)->GetMetaMethod( m_pRootVM, MT_GET, mm ) ) { - out.type = objref_t::DELEGABLE_META; - out.src = var; - out.key = key; - return true; + if ( RunClosure( mm, &var, key, value ) ) + { + out.type = objref_t::DELEGABLE_META; + out.src = var; + out.key = key; + return true; + } } } @@ -11112,31 +12218,6 @@ bool SQDebugServer::GetObj_Var( const SQObjectPtr &var, string_t &expression, bo return false; } - // Used in local indexing - case OT_STRING: - { - if ( expression.len <= 2 || !( expression.ptr[0] == '[' && expression.ptr[expression.len-1] == ']' ) ) - return false; - - expression.ptr++; - expression.len -= 2; - - SQInteger idx; - if ( atoi( expression, &idx ) && - idx >= 0 && idx < _string(var)->_len ) - { - out.type = objref_t::INT; -#ifdef SQUNICODE - out.val = (int)_string(var)->_val[idx]; -#else - out.val = (int)(unsigned char)_string(var)->_val[idx]; -#endif - value = (SQInteger)out.val; - return true; - } - - return false; - } case OT_TABLE: case OT_INSTANCE: case OT_CLASS: @@ -11159,9 +12240,9 @@ bool SQDebugServer::GetObj_Var( const SQObjectPtr &var, string_t &expression, bo // string case '\"': { - Assert( expression.len > 2 ); + AssertClient( expression.len >= 2 ); - if ( expression.len <= 2 ) + if ( expression.len < 2 ) return false; expression.ptr++; @@ -11186,7 +12267,7 @@ bool SQDebugServer::GetObj_Var( const SQObjectPtr &var, string_t &expression, bo // integer case '[': { - Assert( expression.len > 2 ); + AssertClient( expression.len > 2 ); if ( expression.len <= 2 ) return false; @@ -11205,12 +12286,12 @@ bool SQDebugServer::GetObj_Var( const SQObjectPtr &var, string_t &expression, bo // raw bytes case 'R': { - Assert( expression.len > 4 ); + AssertClient( expression.len > 4 ); if ( expression.len <= 4 ) return false; - Assert( expression.ptr[1] == '\"' ); + AssertClient( expression.ptr[1] == '\"' ); expression.ptr += 2; expression.len -= 3; @@ -11228,7 +12309,7 @@ bool SQDebugServer::GetObj_Var( const SQObjectPtr &var, string_t &expression, bo // object, check every member if ( expression.StartsWith("0x") ) { - Assert( expression.len >= FMT_PTR_LEN ); + AssertClient( expression.len >= FMT_PTR_LEN ); if ( expression.len < FMT_PTR_LEN ) return false; @@ -11236,10 +12317,12 @@ bool SQDebugServer::GetObj_Var( const SQObjectPtr &var, string_t &expression, bo expression.len = FMT_PTR_LEN; uintptr_t pKey; + if ( !atox( expression, &pKey ) ) return false; - SQObjectPtr obj = var; + SQObjectPtr obj = var, key, val; + SQ_FOREACH_OP( obj, key, val ) { if ( _rawval(key) == (SQRawObjectVal)pKey ) @@ -11282,7 +12365,6 @@ bool SQDebugServer::GetObj_Var( const SQObjectPtr &var, string_t &expression, bo return true; } } - SQ_FOREACH_END() return false; } @@ -11310,19 +12392,20 @@ bool SQDebugServer::GetObj_Frame( HSQUIRRELVM vm, const SQVM::CallInfo *ci, cons { SQClosure *pClosure = _closure(ci->_closure); SQFunctionProto *func = _fp(pClosure->_function); - SQUnsignedInteger ip = (SQUnsignedInteger)( ci->_ip - func->_instructions - 1 ); + SQUnsignedInteger ip = ci->_ip - func->_instructions; for ( int i = 0; i < func->_nlocalvarinfos; i++ ) { const SQLocalVarInfo &var = func->_localvarinfos[i]; - if ( var._start_op <= ip + 1 && var._end_op >= ip && + if ( var._start_op <= ip && var._end_op + 1 >= ip && expression.IsEqualTo( _string(var._name) ) ) { int stackbase = GetStackBase( vm, ci ); out.type = objref_t::STACK; out.stack.thread = GetWeakRef( vm ); out.stack.frame = ci - vm->_callsstack; - Assert( var._end_op < INT_MAX ); + Assert( var._start_op < INT_MAX && var._end_op < INT_MAX ); + out.stack.start = var._start_op; out.stack.end = var._end_op + 1; out.stack.index = stackbase + var._pos; value = vm->_stack._vals[ out.stack.index ]; @@ -11358,7 +12441,7 @@ bool SQDebugServer::GetObj_Frame( HSQUIRRELVM vm, const SQVM::CallInfo *ci, cons if ( expression.IsEqualTo( KW_VARGV ) ) { const SQLocalVarInfo &var = func->_localvarinfos[ func->_nlocalvarinfos - 2 ]; - if ( sqstring_t(_SC("vargv")).IsEqualTo( _string(var._name) ) ) + if ( IsEqual( _SC("vargv"), _string(var._name) ) ) { int stackbase = GetStackBase( vm, ci ); out.type = objref_t::PTR; @@ -11370,14 +12453,14 @@ bool SQDebugServer::GetObj_Frame( HSQUIRRELVM vm, const SQVM::CallInfo *ci, cons else if ( expression.IsEqualTo( KW_VARGC ) ) { const SQLocalVarInfo &var = func->_localvarinfos[ func->_nlocalvarinfos - 2 ]; - if ( sqstring_t(_SC("vargv")).IsEqualTo( _string(var._name) ) ) + if ( IsEqual( _SC("vargv"), _string(var._name) ) ) { int stackbase = GetStackBase( vm, ci ); const SQObjectPtr &val = vm->_stack._vals[ stackbase + 1 ]; if ( sq_type(val) == OT_ARRAY ) { - out.type = objref_t::INT; + out.type = (objref_t::EOBJREF)( objref_t::INT | objref_t::READONLY ); out.val = (int)_array(val)->Size(); value = _array(val)->Size(); return true; @@ -11394,7 +12477,7 @@ bool SQDebugServer::GetObj_Frame( HSQUIRRELVM vm, const SQVM::CallInfo *ci, cons // and extra work for completions if ( expression.IsEqualTo( KW_VARGC ) ) { - out.type = objref_t::INT; + out.type = (objref_t::EOBJREF)( objref_t::INT | objref_t::READONLY ); out.val = (int)ci->_vargs.size; value = (SQInteger)ci->_vargs.size; return true; @@ -11425,60 +12508,104 @@ bool SQDebugServer::GetObj_Frame( HSQUIRRELVM vm, const SQVM::CallInfo *ci, cons } #endif + SQTable *pEnvTable = NULL; + if ( ci ) { const SQObjectPtr &env = vm->_stack._vals[ GetStackBase( vm, ci ) ]; - if ( sq_type(env) == OT_TABLE ) + + switch ( sq_type(env) ) { - SQTable *t = _table(env); - do + case OT_TABLE: { - if ( SQTable_Get( this, t, expression, value ) ) + pEnvTable = _table(env); + SQTable *t = _table(env); + + do { - out.type = objref_t::TABLE; - out.src = t; - out.key = CreateSQString( this, expression ); + if ( SQTable_Get( this, t, expression, value ) ) + { + out.type = objref_t::TABLE; + out.src = t; + out.key = CreateSQString( this, expression ); + return true; + } + } + while ( ( t = t->_delegate ) != NULL ); + + break; + } + case OT_INSTANCE: + { + SQObjectPtr strExpression = CreateSQString( this, expression ); + + if ( _instance(env)->Get( strExpression, value ) ) + { + out.type = objref_t::INSTANCE; + out.src = env; + out.key = strExpression; return true; } + + break; } - while ( ( t = t->_delegate ) != NULL ); + default: break; } - else if ( sq_type(env) == OT_INSTANCE ) + + // metamethods + if ( is_delegable(env) && _delegable(env)->_delegate ) { - SQObjectPtr pExpression = CreateSQString( this, expression ); - if ( _instance(env)->Get( pExpression, value ) ) + SQObjectPtr mm; + if ( _delegable(env)->GetMetaMethod( vm, MT_GET, mm ) ) { - out.type = objref_t::INSTANCE; - out.src = env; - out.key = pExpression; - return true; + SQObjectPtr strExpression = CreateSQString( this, expression ); + + // Function call can reallocate call stack + int frame = ci - vm->_callsstack; + + if ( RunClosure( vm, mm, &env, strExpression, value ) ) + { + out.type = objref_t::DELEGABLE_META; + out.src = env; + out.key = strExpression; + return true; + } + + ci = vm->_callsstack + frame; } } } - SQTable *root = _table(vm->_roottable); + SQTable *root; #ifdef CLOSURE_ROOT - if ( ci && sq_type(ci->_closure) == OT_CLOSURE && - _closure(ci->_closure)->_root && - _table(_closure(ci->_closure)->_root->_obj) != root ) + if ( ci && sq_type(ci->_closure) == OT_CLOSURE && _closure(ci->_closure)->_root ) { Assert( sq_type(_closure(ci->_closure)->_root->_obj) == OT_TABLE ); root = _table(_closure(ci->_closure)->_root->_obj); } + else + { + root = _table(vm->_roottable); + } +#else + root = _table(vm->_roottable); #endif - do + if ( root != pEnvTable ) { - if ( SQTable_Get( this, root, expression, value ) ) + do { - out.type = objref_t::TABLE; - out.src = root; - out.key = CreateSQString( this, expression ); - return true; + if ( SQTable_Get( this, root, expression, value ) ) + { + out.type = objref_t::TABLE; + out.src = root; + out.key = CreateSQString( this, expression ); + return true; + } } + while ( ( root = root->_delegate ) != NULL ); } - while ( ( root = root->_delegate ) != NULL ); return false; } @@ -11498,7 +12625,7 @@ bool SQDebugServer::GetObj_VarRef( const varref_t *ref, string_t &expression, ob } case VARREF_OUTERS: { - Assert( sq_type(ref->GetVar()) == OT_CLOSURE ); + AssertClient( sq_type(ref->GetVar()) == OT_CLOSURE ); if ( sq_type(ref->GetVar()) == OT_CLOSURE ) { @@ -11522,7 +12649,7 @@ bool SQDebugServer::GetObj_VarRef( const varref_t *ref, string_t &expression, ob } case VARREF_LITERALS: { - Assert( sq_type(ref->GetVar()) == OT_CLOSURE ); + AssertClient( sq_type(ref->GetVar()) == OT_CLOSURE ); if ( sq_type(ref->GetVar()) == OT_CLOSURE ) { @@ -11552,7 +12679,7 @@ bool SQDebugServer::GetObj_VarRef( const varref_t *ref, string_t &expression, ob } } - Assert( mm != -1 ); + AssertClient( mm != -1 ); if ( mm != -1 ) { @@ -11575,7 +12702,8 @@ bool SQDebugServer::GetObj_VarRef( const varref_t *ref, string_t &expression, ob out.key = CreateSQString( this, expression ); return _table(out.src)->Get( out.key, value ); } - default: Assert(0); + default: + AssertClient(!"invalid object"); } } @@ -11583,7 +12711,7 @@ bool SQDebugServer::GetObj_VarRef( const varref_t *ref, string_t &expression, ob } case VARREF_STACK: { - Assert( expression.len > 2 ); + AssertClient( expression.len > 2 ); if ( expression.len <= 2 ) return false; @@ -11614,13 +12742,13 @@ bool SQDebugServer::GetObj_VarRef( const varref_t *ref, string_t &expression, ob default: { PrintError(_SC("(sqdbg) Invalid varref requested (%d)\n"), ref->type); - AssertMsg1( 0, "Invalid varref requested (%d)", ref->type ); + AssertClientMsg1( 0, "Invalid varref requested (%d)", ref->type ); return false; } } } -static inline string_t GetPresentationHintKind( const SQObjectPtr &obj ) +static inline conststring_t GetPresentationHintKind( const SQObjectPtr &obj ) { switch ( sq_type(obj) ) { @@ -11665,7 +12793,7 @@ bool SQDebugServer::ParseEvaluateName( const string_t &expression, HSQUIRRELVM v SQClosure *pClosure = _closure(ci._closure); SQFunctionProto *func = _fp(pClosure->_function); - SQUnsignedInteger ip = (SQUnsignedInteger)( ci._ip - func->_instructions - 1 ); + SQUnsignedInteger ip = ci._ip - func->_instructions; idx = func->_nlocalvarinfos - idx - 1; @@ -11673,7 +12801,7 @@ bool SQDebugServer::ParseEvaluateName( const string_t &expression, HSQUIRRELVM v return false; const SQLocalVarInfo &var = func->_localvarinfos[idx]; - if ( var._start_op <= ip + 1 && var._end_op >= ip ) + if ( var._start_op <= ip && var._end_op + 1 >= ip ) { int stackbase = GetStackBase( vm, &ci ); out.type = objref_t::PTR; @@ -11854,7 +12982,7 @@ void SQDebugServer::OnRequest_Evaluate( const json_table_t &arguments, int seq ) if ( !TranslateFrameID( frame, &vm, &frame ) ) { vm = m_pCurVM; - frame = -1; + frame = INVALID_FRAME; } int flags = 0; @@ -11892,9 +13020,9 @@ void SQDebugServer::OnRequest_Evaluate( const json_table_t &arguments, int seq ) DAP_START_RESPONSE( seq, "evaluate" ); DAP_SET_TABLE( body ); - body.SetString( "result", GetValue( res, flags ) ); + JSONSetString( body, "result", res, flags ); body.SetString( "type", GetType( res ) ); - body.SetInt( "variablesReference", ToVarRef( res, true ) ); + body.SetInt( "variablesReference", ToVarRef( res ) ); wjson_table_t hint = body.SetTable( "presentationHint" ); wjson_array_t attributes = hint.SetArray( "attributes" ); attributes.Append( "readOnly" ); @@ -11917,9 +13045,9 @@ void SQDebugServer::OnRequest_Evaluate( const json_table_t &arguments, int seq ) DAP_START_RESPONSE( seq, "evaluate" ); DAP_SET_TABLE( body ); - body.SetString( "result", GetValue( res, flags ) ); + JSONSetString( body, "result", res, flags ); body.SetString( "type", GetType( res ) ); - body.SetInt( "variablesReference", ToVarRef( res, true ) ); + body.SetInt( "variablesReference", ToVarRef( res ) ); wjson_table_t hint = body.SetTable( "presentationHint" ); wjson_array_t attributes = hint.SetArray( "attributes" ); attributes.Append( "readOnly" ); @@ -11965,7 +13093,7 @@ void SQDebugServer::OnRequest_Evaluate( const json_table_t &arguments, int seq ) DAP_SET_TABLE( body ); JSONSetString( body, "result", res, flags ); body.SetString( "type", GetType( res ) ); - body.SetInt( "variablesReference", ToVarRef( res, true ) ); + body.SetInt( "variablesReference", ToVarRef( res ) ); if ( ShouldPageArray( res, ARRAY_PAGE_LIMIT ) ) body.SetInt( "indexedVariables", (int)_array(res)->_values.size() ); wjson_table_t hint = body.SetTable( "presentationHint" ); @@ -11986,13 +13114,13 @@ void SQDebugServer::OnRequest_Evaluate( const json_table_t &arguments, int seq ) { bool foundWatch = false; - for ( unsigned int i = 0; i < m_LockedWatches.size(); i++ ) + for ( unsigned int i = 0; i < m_LockedWatches.Size(); i++ ) { const watch_t &w = m_LockedWatches[i]; if ( w.expression.IsEqualTo( expression ) ) { vm = GetThread( w.thread ); - frame = w.frame; + frame = IsValidStackFrame( vm, w.frame ) ? w.frame : INVALID_FRAME; foundWatch = true; break; } @@ -12000,7 +13128,7 @@ void SQDebugServer::OnRequest_Evaluate( const json_table_t &arguments, int seq ) if ( !foundWatch ) { - watch_t &w = m_LockedWatches.append(); + watch_t &w = m_LockedWatches.Append(); CopyString( &m_Strings, expression, &w.expression ); w.thread = GetWeakRef( vm ); w.frame = frame; @@ -12016,7 +13144,7 @@ void SQDebugServer::OnRequest_Evaluate( const json_table_t &arguments, int seq ) DAP_SET_TABLE( body ); JSONSetString( body, "result", res, flags ); body.SetString( "type", GetType( res ) ); - body.SetInt( "variablesReference", ToVarRef( res, true ) ); + body.SetInt( "variablesReference", ToVarRef( res ) ); if ( ShouldPageArray( res, ARRAY_PAGE_LIMIT ) ) body.SetInt( "indexedVariables", (int)_array(res)->_values.size() ); wjson_table_t hint = body.SetTable( "presentationHint" ); @@ -12024,52 +13152,43 @@ void SQDebugServer::OnRequest_Evaluate( const json_table_t &arguments, int seq ) DAP_SEND(); } #else - if ( GetObj_Frame( vm, frame, expression, obj, res ) ) + int success = GetObj_Frame( vm, frame, expression, obj, res ); + + if ( !success && + ( RunExpression( expression, vm, frame, res ) || ParseBinaryNumber( expression, res ) ) ) + success = 2; + + if ( success ) { DAP_START_RESPONSE( seq, "evaluate" ); DAP_SET_TABLE( body ); JSONSetString( body, "result", res, flags ); body.SetString( "type", GetType( res ) ); - body.SetInt( "variablesReference", ToVarRef( res, true ) ); + body.SetInt( "variablesReference", ToVarRef( res ) ); if ( ShouldPageArray( res, ARRAY_PAGE_LIMIT ) ) body.SetInt( "indexedVariables", (int)_array(res)->_values.size() ); wjson_table_t hint = body.SetTable( "presentationHint" ); hint.SetString( "kind", GetPresentationHintKind( res ) ); + + if ( success == 2 ) + { + wjson_array_t attributes = hint.SetArray( "attributes" ); + attributes.Append( "readOnly" ); + } DAP_SEND(); } #endif -#ifndef SQDBG_DISABLE_COMPILER - else if ( cres > CompileReturnCode_Fallback && - RunExpression( expression, vm, frame, res ) ) -#else - else if ( RunExpression( expression, vm, frame, res ) || - ParseBinaryNumber( expression, res ) ) -#endif - { - DAP_START_RESPONSE( seq, "evaluate" ); - DAP_SET_TABLE( body ); - JSONSetString( body, "result", res, flags ); - body.SetString( "type", GetType( res ) ); - body.SetInt( "variablesReference", ToVarRef( res, true ) ); - if ( ShouldPageArray( res, ARRAY_PAGE_LIMIT ) ) - body.SetInt( "indexedVariables", (int)_array(res)->_values.size() ); - wjson_table_t hint = body.SetTable( "presentationHint" ); - hint.SetString( "kind", GetPresentationHintKind( res ) ); - wjson_array_t attributes = hint.SetArray( "attributes" ); - attributes.Append( "readOnly" ); - DAP_SEND(); - } else { if ( flags & kFS_Lock ) { - for ( unsigned int i = 0; i < m_LockedWatches.size(); i++ ) + for ( unsigned int i = 0; i < m_LockedWatches.Size(); i++ ) { watch_t &w = m_LockedWatches[i]; if ( w.expression.IsEqualTo( expression ) ) { FreeString( &m_Strings, &w.expression ); - m_LockedWatches.remove( i ); + m_LockedWatches.Remove( i ); break; } } @@ -12082,15 +13201,50 @@ void SQDebugServer::OnRequest_Evaluate( const json_table_t &arguments, int seq ) } else { - Assert( context.IsEqualTo( "repl" ) || context.IsEqualTo( "variables" ) ); + AssertClient( context.IsEqualTo( "repl" ) || context.IsEqualTo( "variables" ) ); +#if defined(SQDBG_USE_COMPILER_FOR_REPL) && !defined(SQDBG_DISABLE_COMPILER) + bool success; + + if ( !expression.Contains('\n') ) + { + CCompiler c( expression ); + ECompileReturnCode cres = c.Evaluate( this, vm, frame, res ); + + switch ( cres ) + { + case CompileReturnCode_Success: + success = true; + break; + case CompileReturnCode_DoesNotExist: + vm->_lasterror = CreateSQString( vm, _SC("index does not exist") ); + success = false; + break; + case CompileReturnCode_SyntaxError: + vm->_lasterror = CreateSQString( vm, c.LastError() ); + case CompileReturnCode_CallError: + success = false; + break; + default: + Assert( cres > CompileReturnCode_Fallback ); + success = RunExpression( expression, vm, frame, res ); + } + } + else + { + success = RunExpression( expression, vm, frame, res, true ); + } + + if ( success ) +#else if ( RunExpression( expression, vm, frame, res, expression.Contains('\n') ) || ParseBinaryNumber( expression, res ) ) +#endif { DAP_START_RESPONSE( seq, "evaluate" ); DAP_SET_TABLE( body ); JSONSetString( body, "result", res, flags ); - body.SetInt( "variablesReference", ToVarRef( res, context.IsEqualTo( "repl" ) ) ); + body.SetInt( "variablesReference", ToVarRef( res ) ); if ( ShouldPageArray( res, ARRAY_PAGE_LIMIT ) ) body.SetInt( "indexedVariables", (int)_array(res)->_values.size() ); DAP_SEND(); @@ -12100,7 +13254,7 @@ void SQDebugServer::OnRequest_Evaluate( const json_table_t &arguments, int seq ) DAP_ERROR_RESPONSE( seq, "evaluate" ); DAP_ERROR_BODY( 0, "{reason}" ); wjson_table_t variables = error.SetTable( "variables" ); - variables.SetString( "reason", GetValue( vm->_lasterror, kFS_NoQuote ) ); + JSONSetString( variables, "reason", vm->_lasterror, kFS_NoQuote ); DAP_SEND(); } } @@ -12143,14 +13297,14 @@ void SQDebugServer::OnRequest_Completions( const json_table_t &arguments, int se if ( !TranslateFrameID( frame, &vm, &frame ) ) { vm = m_pCurVM; - frame = -1; + frame = INVALID_FRAME; } SQObjectPtr target; string_t expr( text.ptr, column ); CCompiler c( expr ); - ECompileReturnCode r = c.Evaluate( this, vm, GetStackFrame( vm, frame ), target ); + ECompileReturnCode r = c.Evaluate( this, vm, frame, target ); CCompiler::token_t token = c.m_lastToken; @@ -12175,18 +13329,20 @@ void SQDebugServer::OnRequest_Completions( const json_table_t &arguments, int se } int start = column; + int length = 0; if ( token.type == CCompiler::Token_Identifier ) + { start = token._string.ptr - text.ptr; - - int length = text.len - start; + length = token._string.len; + } DAP_START_RESPONSE( seq, "completions" ); DAP_SET_TABLE( body ); wjson_array_t targets = body.SetArray( "targets" ); FillCompletions( target, vm, - GetStackFrame( vm, frame ), + frame, token.type, token._string, start == column ? -1 : start, @@ -12195,7 +13351,7 @@ void SQDebugServer::OnRequest_Completions( const json_table_t &arguments, int se DAP_SEND(); } -void SQDebugServer::FillCompletions( const SQObjectPtr &target, HSQUIRRELVM vm, const SQVM::CallInfo *ci, +void SQDebugServer::FillCompletions( const SQObjectPtr &target, HSQUIRRELVM vm, int frame, int token, const string_t &partial, int start, int length, wjson_array_t &targets ) { #define _check( key ) \ @@ -12206,10 +13362,11 @@ void SQDebugServer::FillCompletions( const SQObjectPtr &target, HSQUIRRELVM vm, #define _set( priority, label, val ) \ wjson_table_t elem = targets.AppendTable(); \ elem.SetString( "label", label ); \ - stringbuf_t< 64 > sortbuf; \ - sortbuf.PutInt( priority ); \ - sortbuf.Puts( label ); \ - elem.SetString( "sortText", sortbuf ); \ + { \ + jstringbuf_t sortbuf = elem.SetStringAsBuf( "sortText" ); \ + sortbuf.PutInt( priority ); \ + sortbuf.Puts( label ); \ + } \ switch ( sq_type(val) ) \ { \ case OT_CLOSURE: \ @@ -12244,9 +13401,10 @@ void SQDebugServer::FillCompletions( const SQObjectPtr &target, HSQUIRRELVM vm, case OT_TABLE: { SQTable *t = _table(target); + SQObjectPtr key, val; + do { - SQObjectPtr key, val; FOREACH_SQTABLE( t, key, val ) { if ( sq_type(key) == OT_STRING && _check( _string(key) ) ) @@ -12288,26 +13446,22 @@ void SQDebugServer::FillCompletions( const SQObjectPtr &target, HSQUIRRELVM vm, SQObjectPtr key, val; // values + FOREACH_SQTABLE( base->_members, key, val ) { - FOREACH_SQTABLE( base->_members, key, val ) + if ( _isfield(val) && sq_type(key) == OT_STRING && _check( _string(key) ) ) { - if ( _isfield(val) && sq_type(key) == OT_STRING && _check( _string(key) ) ) - { - _instance(target)->Get( key, val ); - _set( 0, _string(key), val ); - } + _instance(target)->Get( key, val ); + _set( 0, _string(key), val ); } } // methods + FOREACH_SQTABLE( base->_members, key, val ) { - FOREACH_SQTABLE( base->_members, key, val ) + if ( !_isfield(val) && sq_type(key) == OT_STRING && _check( _string(key) ) ) { - if ( !_isfield(val) && sq_type(key) == OT_STRING && _check( _string(key) ) ) - { - _instance(target)->Get( key, val ); - _set( 1, _string(key), val ); - } + _instance(target)->Get( key, val ); + _set( 1, _string(key), val ); } } @@ -12318,26 +13472,22 @@ void SQDebugServer::FillCompletions( const SQObjectPtr &target, HSQUIRRELVM vm, SQObjectPtr key, val; // values + FOREACH_SQTABLE( _class(target)->_members, key, val ) { - FOREACH_SQTABLE( _class(target)->_members, key, val ) + if ( _isfield(val) && sq_type(key) == OT_STRING && _check( _string(key) ) ) { - if ( _isfield(val) && sq_type(key) == OT_STRING && _check( _string(key) ) ) - { - _class(target)->Get( key, val ); - _set( 0, _string(key), val ); - } + _class(target)->Get( key, val ); + _set( 0, _string(key), val ); } } // methods + FOREACH_SQTABLE( _class(target)->_members, key, val ) { - FOREACH_SQTABLE( _class(target)->_members, key, val ) + if ( !_isfield(val) && sq_type(key) == OT_STRING && _check( _string(key) ) ) { - if ( !_isfield(val) && sq_type(key) == OT_STRING && _check( _string(key) ) ) - { - _class(target)->Get( key, val ); - _set( 1, _string(key), val ); - } + _class(target)->Get( key, val ); + _set( 1, _string(key), val ); } } @@ -12347,6 +13497,7 @@ void SQDebugServer::FillCompletions( const SQObjectPtr &target, HSQUIRRELVM vm, } SQTable *del = GetDefaultDelegate( vm, sq_type(target) ); + if ( del ) { SQObjectPtr key, val; @@ -12359,116 +13510,123 @@ void SQDebugServer::FillCompletions( const SQObjectPtr &target, HSQUIRRELVM vm, } } - SQTable *pEnvTable = NULL; - if ( sq_type(target) == OT_NULL ) { - // locals - if ( ci && sq_type(ci->_closure) == OT_CLOSURE ) + SQTable *pEnvTable = NULL; + + if ( frame != INVALID_FRAME ) { - SQClosure *pClosure = _closure(ci->_closure); - SQFunctionProto *func = _fp(pClosure->_function); - SQUnsignedInteger ip = (SQUnsignedInteger)( ci->_ip - func->_instructions - 1 ); + const SQVM::CallInfo *ci = vm->_callsstack + frame; + int stackbase = GetStackBase( vm, ci ); - for ( int i = 0; i < func->_nlocalvarinfos; i++ ) + // locals + if ( sq_type(ci->_closure) == OT_CLOSURE ) { - const SQLocalVarInfo &var = func->_localvarinfos[i]; - if ( var._start_op <= ip + 1 && var._end_op >= ip && - _check( _string(var._name) ) ) - { - _set( 0, _string(var._name), vm->_stack._vals[ GetStackBase( vm, ci ) + var._pos ] ); + SQClosure *pClosure = _closure(ci->_closure); + SQFunctionProto *func = _fp(pClosure->_function); + SQUnsignedInteger ip = ci->_ip - func->_instructions; - if ( sqstring_t( _string(var._name) ).IsEqualTo(_SC("this")) ) + for ( int i = 0; i < func->_nlocalvarinfos; i++ ) + { + const SQLocalVarInfo &var = func->_localvarinfos[i]; + if ( var._start_op <= ip && var._end_op + 1 >= ip && + _check( _string(var._name) ) ) { - elem.SetString( "text", KW_THIS ); - } + _set( 0, _string(var._name), vm->_stack._vals[ stackbase + var._pos ] ); + + if ( IsEqual( _SC("this"), _string(var._name) ) ) + { + elem.SetString( "text", KW_THIS ); + } #if SQUIRREL_VERSION_NUMBER >= 300 - else if ( sqstring_t( _string(var._name) ).IsEqualTo(_SC("vargv")) ) - { - elem.SetString( "text", KW_VARGV ); - } + else if ( IsEqual( _SC("vargv"), _string(var._name) ) ) + { + elem.SetString( "text", KW_VARGV ); + } #endif + } } - } - for ( int i = 0; i < func->_noutervalues; i++ ) - { - const SQOuterVar &var = func->_outervalues[i]; - if ( _check( _string(var._name) ) ) + for ( int i = 0; i < func->_noutervalues; i++ ) { - _set( 0, _string(var._name), *_outervalptr( pClosure->_outervalues[i] ) ); + const SQOuterVar &var = func->_outervalues[i]; + if ( _check( _string(var._name) ) ) + { + _set( 0, _string(var._name), *_outervalptr( pClosure->_outervalues[i] ) ); + } } - } #if SQUIRREL_VERSION_NUMBER < 300 - if ( func->_varparams ) - { - if ( token == CCompiler::Token_Identifier && string_t("vargv").StartsWith( partial ) ) + if ( func->_varparams ) { - _set( 0, "vargv", SQObjectPtr() ); - elem.SetString( "text", KW_VARGV ); - } + if ( token == CCompiler::Token_Identifier && string_t("vargv").StartsWith( partial ) ) + { + _set( 0, "vargv", SQObjectPtr() ); + elem.SetString( "text", KW_VARGV ); + } - if ( token == CCompiler::Token_Identifier && string_t("vargc").StartsWith( partial ) ) - { - _set( 0, "vargc", SQObjectPtr((SQInteger)0) ); - elem.SetString( "text", KW_VARGC ); + if ( token == CCompiler::Token_Identifier && string_t("vargc").StartsWith( partial ) ) + { + _set( 0, "vargc", SQObjectPtr((SQInteger)0) ); + elem.SetString( "text", KW_VARGC ); + } } - } #endif - } - - // env - if ( ci ) - { - const SQObjectPtr &env = vm->_stack._vals[ GetStackBase( vm, ci ) ]; - if ( sq_type(env) == OT_TABLE ) - { - pEnvTable = _table(env); - SQTable *t = _table(env); - do - { - SQObjectPtr key, val; - FOREACH_SQTABLE( t, key, val ) - { - if ( sq_type(key) == OT_STRING && _check( _string(key) ) ) - { - _set( 1, _string(key), val ); - } - } - } - while ( ( t = t->_delegate ) != NULL ); } - else if ( sq_type(env) == OT_INSTANCE ) + + const SQObjectPtr &env = vm->_stack._vals[ stackbase ]; + + switch ( sq_type(env) ) { - SQClass *base = _instance(env)->_class; - Assert( base ); - - // metamembers - SQObjectPtr mm; - const classdef_t *def = FindClassDef( base ); - - if ( def && - sq_type(def->metamembers) != OT_NULL && - _instance(target)->GetMetaMethod( vm, MT_GET, mm ) ) + case OT_TABLE: { - for ( unsigned int i = 0; i < _array(def->metamembers)->_values.size(); i++ ) - { - const SQObjectPtr &key = _array(def->metamembers)->_values[i]; - SQObjectPtr val; + pEnvTable = _table(env); + SQTable *t = _table(env); + SQObjectPtr key, val; - if ( sq_type(key) == OT_STRING && _check( _string(key) ) ) + do + { + FOREACH_SQTABLE( t, key, val ) { - RunClosure( mm, &env, key, val ); - _set( 1, _string(key), val ); + if ( sq_type(key) == OT_STRING && _check( _string(key) ) ) + { + _set( 1, _string(key), val ); + } } } + while ( ( t = t->_delegate ) != NULL ); + + break; } - - SQObjectPtr key, val; - - // values + case OT_INSTANCE: { + SQClass *base = _instance(env)->_class; + Assert( base ); + + // metamembers + SQObjectPtr mm; + const classdef_t *def = FindClassDef( base ); + + if ( def && + sq_type(def->metamembers) != OT_NULL && + _instance(target)->GetMetaMethod( vm, MT_GET, mm ) ) + { + for ( unsigned int i = 0; i < _array(def->metamembers)->_values.size(); i++ ) + { + const SQObjectPtr &key = _array(def->metamembers)->_values[i]; + SQObjectPtr val; + + if ( sq_type(key) == OT_STRING && _check( _string(key) ) ) + { + RunClosure( mm, &env, key, val ); + _set( 1, _string(key), val ); + } + } + } + + SQObjectPtr key, val; + + // values FOREACH_SQTABLE( base->_members, key, val ) { if ( _isfield(val) && sq_type(key) == OT_STRING && _check( _string(key) ) ) @@ -12477,10 +13635,8 @@ void SQDebugServer::FillCompletions( const SQObjectPtr &target, HSQUIRRELVM vm, _set( 1, _string(key), val ); } } - } - // methods - { + // methods FOREACH_SQTABLE( base->_members, key, val ) { if ( !_isfield(val) && sq_type(key) == OT_STRING && _check( _string(key) ) ) @@ -12489,27 +13645,37 @@ void SQDebugServer::FillCompletions( const SQObjectPtr &target, HSQUIRRELVM vm, _set( 2, _string(key), val ); } } + + break; } + default: break; } } - SQTable *root = _table(vm->_roottable); + SQTable *root; #ifdef CLOSURE_ROOT - if ( ci && sq_type(ci->_closure) == OT_CLOSURE && - _closure(ci->_closure)->_root && - _table(_closure(ci->_closure)->_root->_obj) != root ) + const SQVM::CallInfo *ci = vm->_callsstack + frame; + + if ( frame != INVALID_FRAME && sq_type(ci->_closure) == OT_CLOSURE && _closure(ci->_closure)->_root ) { Assert( sq_type(_closure(ci->_closure)->_root->_obj) == OT_TABLE ); root = _table(_closure(ci->_closure)->_root->_obj); } + else + { + root = _table(vm->_roottable); + } +#else + root = _table(vm->_roottable); #endif if ( root != pEnvTable ) { + SQObjectPtr key, val; + do { - SQObjectPtr key, val; FOREACH_SQTABLE( root, key, val ) { if ( sq_type(key) == OT_STRING && _check( _string(key) ) ) @@ -12578,13 +13744,13 @@ void SQDebugServer::OnRequest_Scopes( const json_table_t &arguments, int seq ) int SQDebugServer::ThreadToID( HSQUIRRELVM vm ) { - if ( m_Threads.size() >= INT_MAX - 1 ) + if ( m_Threads.Size() >= INT_MAX - 1 ) { RemoveThreads(); ThreadToID( m_pRootVM ); } - for ( int i = m_Threads.size(); i--; ) + for ( int i = m_Threads.Size(); i--; ) { SQWeakRef *wr = m_Threads[i]; @@ -12596,21 +13762,21 @@ int SQDebugServer::ThreadToID( HSQUIRRELVM vm ) else { __ObjRelease( wr ); - m_Threads.remove(i); + m_Threads.Remove(i); } } SQWeakRef *wr = GetWeakRef( vm ); __ObjAddRef( wr ); - int i = m_Threads.size(); - m_Threads.append( wr ); + int i = m_Threads.Size(); + m_Threads.Append( wr ); return i; } HSQUIRRELVM SQDebugServer::ThreadFromID( int id ) { - if ( id >= 0 && id < (int)m_Threads.size() ) + if ( id >= 0 && id < (int)m_Threads.Size() ) { SQWeakRef *wr = m_Threads[id]; @@ -12618,7 +13784,7 @@ HSQUIRRELVM SQDebugServer::ThreadFromID( int id ) return _thread(wr->_obj); __ObjRelease( wr ); - m_Threads.remove(id); + m_Threads.Remove(id); } return NULL; @@ -12626,13 +13792,13 @@ HSQUIRRELVM SQDebugServer::ThreadFromID( int id ) void SQDebugServer::RemoveThreads() { - for ( int i = m_Threads.size(); i--; ) + for ( int i = m_Threads.Size(); i--; ) { SQWeakRef *wr = m_Threads[i]; __ObjRelease( wr ); } - m_Threads.purge(); + m_Threads.Purge(); } void SQDebugServer::OnRequest_Threads( int seq ) @@ -12641,7 +13807,7 @@ void SQDebugServer::OnRequest_Threads( int seq ) DAP_SET_TABLE( body ); wjson_array_t threads = body.SetArray( "threads" ); - for ( int i = 0; i < (int)m_Threads.size(); i++ ) + for ( int i = 0; i < (int)m_Threads.Size(); i++ ) { SQWeakRef *wr = m_Threads[i]; @@ -12656,28 +13822,32 @@ void SQDebugServer::OnRequest_Threads( int seq ) } else { - stringbuf_t< STRLEN("Thread ") + FMT_PTR_LEN > name; + jstringbuf_t name = thread.SetStringAsBuf( "name" ); name.Puts("Thread "); name.PutHex( (uintptr_t)_thread(wr->_obj) ); - thread.SetString( "name", name ); } } else { __ObjRelease( wr ); - m_Threads.remove(i); + m_Threads.Remove(i); i--; } } DAP_SEND(); } -bool SQDebugServer::ShouldIgnoreStackFrame( const SQVM::CallInfo &ci ) +bool SQDebugServer::ShouldIgnoreStackFrame( HSQUIRRELVM vm, const SQVM::CallInfo &ci ) { + // Don't ignore debugger functions + // if they were somehow stepped into, to prevent confusion + if ( vm->_callsstacksize == 1 ) + return false; + // Ignore RunScript (first frame) if ( sq_type(ci._closure) == OT_CLOSURE && sq_type(_fp(_closure(ci._closure)->_function)->_sourcename) == OT_STRING && - sqstring_t(_SC("sqdbg")).IsEqualTo( _string(_fp(_closure(ci._closure)->_function)->_sourcename) ) ) + IsEqual( _SC("sqdbg"), _string(_fp(_closure(ci._closure)->_function)->_sourcename) ) ) return true; // Ignore error handler / debug hook (last frame) @@ -12694,15 +13864,15 @@ bool SQDebugServer::ShouldIgnoreStackFrame( const SQVM::CallInfo &ci ) int SQDebugServer::ConvertToFrameID( int threadId, int stackFrame ) { - for ( int i = 0; i < (int)m_FrameIDs.size(); i++ ) + for ( int i = 0; i < (int)m_FrameIDs.Size(); i++ ) { const frameid_t &v = m_FrameIDs[i]; if ( v.threadId == threadId && v.frame == stackFrame ) return i; } - int i = m_FrameIDs.size(); - frameid_t &v = m_FrameIDs.append(); + int i = m_FrameIDs.Size(); + frameid_t &v = m_FrameIDs.Append(); v.threadId = threadId; v.frame = stackFrame; @@ -12711,7 +13881,7 @@ int SQDebugServer::ConvertToFrameID( int threadId, int stackFrame ) bool SQDebugServer::TranslateFrameID( int frameId, HSQUIRRELVM *thread, int *stackFrame ) { - if ( frameId >= 0 && frameId < (int)m_FrameIDs.size() ) + if ( frameId >= 0 && frameId < (int)m_FrameIDs.Size() ) { frameid_t &v = m_FrameIDs[frameId]; *thread = ThreadFromID( v.threadId ); @@ -12727,14 +13897,22 @@ void SQDebugServer::OnRequest_StackTrace( const json_table_t &arguments, int seq { int threadId, startFrame, levels; json_table_t *format; - bool parameters = true; + bool parameters = true, + parameterTypes = false, + parameterNames = true, + parameterValues = false; arguments.GetInt( "threadId", &threadId, -1 ); arguments.GetInt( "startFrame", &startFrame ); arguments.GetInt( "levels", &levels ); if ( arguments.GetTable( "format", &format ) ) + { format->GetBool( "parameters", ¶meters ); + format->GetBool( "parameterTypes", ¶meterTypes ); + format->GetBool( "parameterNames", ¶meterNames ); + format->GetBool( "parameterValues", ¶meterValues ); + } HSQUIRRELVM vm = ThreadFromID( threadId ); @@ -12780,13 +13958,12 @@ void SQDebugServer::OnRequest_StackTrace( const json_table_t &arguments, int seq DAP_SET_TABLE( body ); { wjson_array_t stackFrames = body.SetArray( "stackFrames" ); - stringbufext_t buf = ScratchPadBuf( 256 ); for ( int i = startFrame; i >= targetFrame; i-- ) { const SQVM::CallInfo &ci = vm->_callsstack[i]; - if ( ShouldIgnoreStackFrame(ci) ) + if ( ShouldIgnoreStackFrame( vm, ci ) ) continue; if ( sq_type(ci._closure) == OT_CLOSURE ) @@ -12796,60 +13973,101 @@ void SQDebugServer::OnRequest_StackTrace( const json_table_t &arguments, int seq wjson_table_t frame = stackFrames.AppendTable(); frame.SetInt( "id", ConvertToFrameID( threadId, i ) ); - buf.len = 0; - - if ( sq_type(func->_name) == OT_STRING ) { - buf.Puts( _string(func->_name) ); - } - else - { - buf.PutHex( (uintptr_t)func ); - } + jstringbuf_t buf = frame.SetStringAsBuf( "name" ); - if ( parameters ) - { - buf.Put('('); - - Assert( func->_nparameters ); - - int nparams = func->_nparameters; -#if SQUIRREL_VERSION_NUMBER >= 300 - if ( nparams > 1 ) -#else - if ( nparams > 1 || func->_varparams ) -#endif + if ( sq_type(func->_name) == OT_STRING ) { -#if SQUIRREL_VERSION_NUMBER >= 300 - if ( func->_varparams ) - nparams--; -#endif - for ( int j = 1; j < nparams; j++ ) - { - const SQObjectPtr ¶m = func->_parameters[j]; - Assert( sq_type(param) == OT_STRING ); - - buf.Puts( _string(param) ); - buf.Put(','); - buf.Put(' '); - } - - if ( !func->_varparams ) - { - buf.len -= 2; - } - else - { - buf.Put('.'); - buf.Put('.'); - buf.Put('.'); - } + buf.Puts( _string(func->_name) ); + } + else + { + buf.PutHex( (uintptr_t)func ); } - buf.Put(')'); - } + if ( parameters ) + { + buf.Put('('); - frame.SetString( "name", buf ); + Assert( func->_nparameters ); + + int nparams = func->_nparameters; +#if SQUIRREL_VERSION_NUMBER >= 300 + if ( nparams > 1 ) +#else + if ( nparams > 1 || func->_varparams ) +#endif + { +#if SQUIRREL_VERSION_NUMBER >= 300 + if ( func->_varparams ) + nparams--; +#endif + for ( int j = 1; j < nparams; j++ ) + { + const SQObjectPtr ¶m = func->_parameters[j]; + Assert( sq_type(param) == OT_STRING ); + + if ( parameterTypes ) + { + const SQObjectPtr &val = vm->_stack._vals[ GetStackBase( vm, &ci ) + j ]; + buf.Puts( GetType( val ) ); + buf.Put(' '); + } + + if ( parameterNames ) + buf.Puts( _string(param) ); + + if ( parameterValues ) + { + if ( parameterNames ) + buf.Puts(" = "); + + const SQObjectPtr &val = vm->_stack._vals[ GetStackBase( vm, &ci ) + j ]; + + switch ( sq_type(val) ) + { + case OT_INTEGER: + case OT_FLOAT: + case OT_BOOL: + case OT_NULL: + case OT_STRING: + case OT_TABLE: + case OT_ARRAY: + case OT_CLASS: + { + string_t str = GetValue( val ); + + if ( str.len > 32 && sq_type(val) == OT_STRING ) + str.len = 32; + + buf.Puts( str ); + break; + } + default: + Assert( ISREFCOUNTED( sq_type(val) ) ); + buf.PutHex( (uintptr_t)_refcounted(val) ); + } + } + + buf.Put(','); + buf.Put(' '); + } + + if ( !func->_varparams ) + { + buf.Seek( -2 ); + } + else + { + buf.Put('.'); + buf.Put('.'); + buf.Put('.'); + } + } + + buf.Put(')'); + } + } if ( sq_type(func->_sourcename) == OT_STRING ) { @@ -12859,10 +14077,10 @@ void SQDebugServer::OnRequest_StackTrace( const json_table_t &arguments, int seq frame.SetInt( "line", (int)func->GetLine( ci._ip ) ); frame.SetInt( "column", 1 ); - - buf.len = 0; - buf.PutHex( (uintptr_t)ci._ip ); - frame.SetString( "instructionPointerReference", buf ); + { + jstringbuf_t buf = frame.SetStringAsBuf( "instructionPointerReference" ); + buf.PutHex( (uintptr_t)ci._ip ); + } } else if ( sq_type(ci._closure) == OT_NATIVECLOSURE ) { @@ -12876,24 +14094,25 @@ void SQDebugServer::OnRequest_StackTrace( const json_table_t &arguments, int seq source.SetString( "name", "NATIVE" ); } - buf.len = 0; + { + jstringbuf_t buf = frame.SetStringAsBuf( "name" ); - if ( sq_type(closure->_name) == OT_STRING ) - { - buf.Puts( _string(closure->_name) ); - } - else - { - buf.PutHex( (uintptr_t)closure ); + if ( sq_type(closure->_name) == OT_STRING ) + { + buf.Puts( _string(closure->_name) ); + } + else + { + buf.PutHex( (uintptr_t)closure ); + } + + if ( parameters ) + { + buf.Put('('); + buf.Put(')'); + } } - if ( parameters ) - { - buf.Put('('); - buf.Put(')'); - } - - frame.SetString( "name", buf ); frame.SetInt( "line", -1 ); frame.SetInt( "column", 1 ); frame.SetString( "presentationHint", "subtle" ); @@ -12975,7 +14194,9 @@ static int _sortkeys( const SQObjectPtr *a, const SQObjectPtr *b ) } #define _checkNonStringMembers(key) \ - ( sq_type(key) != OT_STRING || HasEscapes( _string(key)->_val, _string(key)->_len ) ) + ( sq_type(key) != OT_STRING || \ + _string(key)->_len == 0 || \ + HasEscapes( _string(key)->_val, _string(key)->_len ) ) static inline void SortKeys( SQTable *table, vector< SQObjectPtr, true > *values, @@ -12986,13 +14207,13 @@ static inline void SortKeys( SQTable *table, SQObjectPtr key, val; FOREACH_SQTABLE( table, key, val ) { - values->append( key ); + values->Append( key ); if ( !nsm ) nsm = _checkNonStringMembers( key ); } - values->sort( _sortkeys ); + values->Sort( _sortkeys ); *hasNonStringMembers = nsm; } @@ -13025,11 +14246,11 @@ static inline void SortKeys( SQClass *pClass, if ( _isfield(idx) ) { - values->append( key ); + values->Append( key ); } else { - methods->append( key ); + methods->Append( key ); } const SQObjectPtr &attr = _isfield(idx) ? @@ -13043,11 +14264,11 @@ static inline void SortKeys( SQClass *pClass, nsm = _checkNonStringMembers( key ); } - if ( values->size() ) - values->sort( _sortkeys ); + if ( values->Size() ) + values->Sort( _sortkeys ); - if ( methods->size() ) - methods->sort( _sortkeys ); + if ( methods->Size() ) + methods->Sort( _sortkeys ); *hasNonStringMembers = nsm; } @@ -13063,15 +14284,15 @@ static inline void SortKeys( SQClass *pClass, { if ( _isfield(idx) ) { - values->append( key ); + values->Append( key ); if ( !nsm ) nsm = _checkNonStringMembers( key ); } } - if ( values->size() ) - values->sort( _sortkeys ); + if ( values->Size() ) + values->Sort( _sortkeys ); *hasNonStringMembers = nsm; } @@ -13127,39 +14348,37 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq int stackbase = GetStackBase( vm, &ci ); SQClosure *pClosure = _closure(ci._closure); SQFunctionProto *func = _fp(pClosure->_function); - SQUnsignedInteger ip = (SQUnsignedInteger)( ci._ip - func->_instructions - 1 ); + SQUnsignedInteger ip = ci._ip - func->_instructions; DAP_START_RESPONSE( seq, "variables" ); DAP_SET_TABLE( body ); wjson_array_t variables = body.SetArray( "variables" ); - stringbufext_t buf = ScratchPadBuf( 256 ); - for ( unsigned int i = 0; i < m_ReturnValues.size(); i++ ) + for ( unsigned int i = 0; i < m_ReturnValues.Size(); i++ ) { const returnvalue_t &rv = m_ReturnValues[i]; wjson_table_t elem = variables.AppendTable(); - - buf.len = 0; - - if ( !( m_iYieldValues & (1<<(i+1)) ) ) { - buf.Puts( "return@" ); - } - else - { - buf.Puts( "yield@" ); - } + jstringbuf_t buf = elem.SetStringAsBuf( "name" ); - if ( rv.funcname ) - { - buf.Puts( rv.funcname ); - } - else - { - buf.PutHex( rv.funcptr ); - } + if ( !( m_iYieldValues & (1<<(i+1)) ) ) + { + buf.Puts( "return@" ); + } + else + { + buf.Puts( "yield@" ); + } - elem.SetString( "name", buf ); + if ( rv.funcname ) + { + buf.Puts( rv.funcname ); + } + else + { + buf.PutHex( rv.funcptr ); + } + } JSONSetString( elem, "value", rv.value, flags ); elem.SetString( "type", GetType( rv.value ) ); elem.SetInt( "variablesReference", ToVarRef( rv.value ) ); @@ -13173,20 +14392,18 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq const SQLocalVarInfo &var = func->_localvarinfos[i]; Assert( sq_type(var._name) == OT_STRING ); - if ( var._start_op <= ip + 1 && var._end_op >= ip ) + if ( var._start_op <= ip && var._end_op + 1 >= ip ) { - buf.len = 0; - buf.Put('@'); - buf.Put('L'); - buf.Put('@'); - buf.PutInt( (int)func->_nlocalvarinfos - i - 1 ); - const SQObjectPtr &val = vm->_stack._vals[ stackbase + var._pos ]; wjson_table_t elem = variables.AppendTable(); elem.SetString( "name", _string(var._name) ); JSONSetString( elem, "value", val, flags ); elem.SetString( "type", GetType( val ) ); - elem.SetString( "evaluateName", buf ); + { + jstringbuf_t buf = elem.SetStringAsBuf( "evaluateName" ); + buf.Put('@'); buf.Put('L'); buf.Put('@'); + buf.PutInt( (int)func->_nlocalvarinfos - i - 1 ); + } elem.SetInt( "variablesReference", ToVarRef( val ) ); if ( ShouldPageArray( val, 16 * ARRAY_PAGE_LIMIT ) ) elem.SetInt( "indexedVariables", (int)_array(val)->_values.size() ); @@ -13254,7 +14471,7 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq if ( _refcounted(target)->_weakref ) { - for ( int i = m_DataWatches.size(); i--; ) + for ( int i = m_DataWatches.Size(); i--; ) { const datawatch_t &dw = m_DataWatches[i]; if ( _refcounted(target)->_weakref == dw.container ) @@ -13279,7 +14496,7 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq } else { - stringbufext_t buf = ScratchPadBuf( 256 ); + jstringbuf_t buf = elem.SetStringAsBuf( "value" ); buf.PutInt( _refcounted(target)->_uiRef ); do @@ -13289,15 +14506,8 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq buf.Put('>'); buf.Put( ( sq_type(target) != OT_WEAKREF ) ? '*' : ' ' ); buf.PutInt( _refcounted(target)->_uiRef ); - - if ( buf.len >= sizeof(buf.ptr) - 4 ) - break; } while ( sq_type(target) == OT_WEAKREF ); - - buf.Term(); - - elem.SetString( "value", buf ); } elem.SetInt( "variablesReference", -1 ); @@ -13381,7 +14591,7 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq { wjson_table_t elem = variables.AppendTable(); elem.SetString( "name", INTERNAL_TAG("class") ); - elem.SetString( "value", GetValue( ToSQObject( _instance(target)->_class ) ) ); + JSONSetString( elem, "value", ToSQObject( _instance(target)->_class ) ); elem.SetInt( "variablesReference", ToVarRef( ToSQObject( _instance(target)->_class ) ) ); SetVirtualHint( elem ); } @@ -13394,7 +14604,7 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq { wjson_table_t elem = variables.AppendTable(); elem.SetString( "name", INTERNAL_TAG("base") ); - elem.SetString( "value", GetValue( ToSQObject( _class(target)->_base ) ) ); + JSONSetString( elem, "value", ToSQObject( _class(target)->_base ) ); elem.SetInt( "variablesReference", ToVarRef( ToSQObject( _class(target)->_base ) ) ); SetVirtualHint( elem ); } @@ -13407,7 +14617,7 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq { wjson_table_t elem = variables.AppendTable(); elem.SetString( "name", INTERNAL_TAG("delegate") ); - elem.SetString( "value", GetValue( ToSQObject( _table(target)->_delegate ) ) ); + JSONSetString( elem, "value", ToSQObject( _table(target)->_delegate ) ); elem.SetInt( "variablesReference", ToVarRef( ToSQObject( _table(target)->_delegate ) ) ); SetVirtualHint( elem ); } @@ -13448,7 +14658,7 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq if ( shouldQuoteKeys ) keyflags &= ~kFS_NoQuote; - for ( unsigned int i = 0; i < keys.size(); i++ ) + for ( unsigned int i = 0; i < keys.Size(); i++ ) { const SQObjectPtr &key = keys[i]; SQObjectPtr val; @@ -13510,7 +14720,7 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq { wjson_table_t elem = variables.AppendTable(); elem.SetString( "name", INTERNAL_TAG("attributes") ); - elem.SetString( "value", GetValue( _class(target)->_attributes, flags ) ); + JSONSetString( elem, "value", _class(target)->_attributes, flags ); elem.SetInt( "variablesReference", ToVarRef( _class(target)->_attributes ) ); SetVirtualHint( elem ); } @@ -13523,7 +14733,7 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq SetVirtualHint( elem ); } - for ( unsigned int i = 0; i < values.size(); i++ ) + for ( unsigned int i = 0; i < values.Size(); i++ ) { const SQObjectPtr &key = values[i]; SQObjectPtr val; @@ -13549,7 +14759,7 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq hint.SetString( "kind", GetPresentationHintKind( val ) ); } - for ( unsigned int i = 0; i < methods.size(); i++ ) + for ( unsigned int i = 0; i < methods.Size(); i++ ) { const SQObjectPtr &key = methods[i]; SQObjectPtr val; @@ -13614,7 +14824,7 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq // NOTE: val can be temporary, keep strong ref for inspection JSONSetString( elem, "value", val, flags ); elem.SetString( "type", GetType( val ) ); - elem.SetInt( "variablesReference", ToVarRef( val, false, true ) ); + elem.SetInt( "variablesReference", ToVarRef( val, true ) ); if ( ShouldPageArray( val, 16 * ARRAY_PAGE_LIMIT ) ) elem.SetInt( "indexedVariables", (int)_array(val)->_values.size() ); wjson_table_t hint = elem.SetTable( "presentationHint" ); @@ -13670,7 +14880,7 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq // NOTE: val can be temporary, keep strong ref for inspection JSONSetString( elem, "value", val, flags ); elem.SetString( "type", GetType( val ) ); - elem.SetInt( "variablesReference", ToVarRef( val, false, true ) ); + elem.SetInt( "variablesReference", ToVarRef( val, true ) ); if ( ShouldPageArray( val, 16 * ARRAY_PAGE_LIMIT ) ) elem.SetInt( "indexedVariables", (int)_array(val)->_values.size() ); wjson_table_t hint = elem.SetTable( "presentationHint" ); @@ -13693,7 +14903,7 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq } } - for ( unsigned int i = 0; i < values.size(); i++ ) + for ( unsigned int i = 0; i < values.Size(); i++ ) { const SQObjectPtr &key = values[i]; SQObjectPtr val; @@ -13728,8 +14938,18 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq break; } case OT_CLOSURE: +#ifdef CHAINABLE_FUNCPROTO + // Only reachable through compiler address deref + case OT_FUNCPROTO: +#endif { +#ifdef CHAINABLE_FUNCPROTO + SQFunctionProto *func = ( sq_type(target) == OT_CLOSURE ) ? + _fp(_closure(target)->_function) : + _funcproto(target); +#else SQFunctionProto *func = _fp(_closure(target)->_function); +#endif Assert( func->_ninstructions <= INT_MAX ); Assert( func->GetLine( &func->_instructions[ func->_ninstructions - 1 ] ) <= INT_MAX ); @@ -13755,15 +14975,10 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq int line = GetFunctionDeclarationLine( func ); if ( line ) { - stringbufext_t buf = ScratchPadBuf( - scstombslen( - _string(func->_sourcename)->_val, - _string(func->_sourcename)->_len ) + - FMT_INT_LEN ); + jstringbuf_t buf = elem.SetStringAsBuf( "value" ); buf.Puts( _string(func->_sourcename) ); buf.Put(':'); buf.PutInt( line ); - elem.SetString( "value", buf ); } else { @@ -13803,7 +15018,7 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq #if SQUIRREL_VERSION_NUMBER >= 300 nparams--; #endif - stringbuf_t< 16 > buf; + jstringbuf_t buf = elem.SetStringAsBuf( "value" ); if ( !( flags & kFS_Hexadecimal ) ) { @@ -13815,7 +15030,6 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq } buf.Puts("..."); - elem.SetString( "value", buf ); } elem.SetInt( "variablesReference", -1 ); @@ -13844,7 +15058,7 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq wjson_table_t elem = variables.AppendTable(); elem.SetString( "name", INTERNAL_TAG("instructions") ); elem.SetString( "value", GetValue( c, flags ) ); - elem.SetInt( "variablesReference", ToVarRef( VARREF_INSTRUCTIONS, target ) ); + elem.SetInt( "variablesReference", ToVarRef( VARREF_INSTRUCTIONS, ToSQObject( func ) ) ); SetVirtualHint( elem ); } @@ -13853,41 +15067,46 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq wjson_table_t elem = variables.AppendTable(); elem.SetString( "name", INTERNAL_TAG("literals") ); elem.SetString( "value", GetValue( func->_nliterals, flags ) ); - elem.SetInt( "variablesReference", ToVarRef( VARREF_LITERALS, target ) ); + elem.SetInt( "variablesReference", ToVarRef( VARREF_LITERALS, ToSQObject( func ) ) ); SetVirtualHint( elem ); } - if ( func->_noutervalues ) +#ifdef CHAINABLE_FUNCPROTO + if ( sq_type(target) == OT_CLOSURE ) +#endif { - wjson_table_t elem = variables.AppendTable(); - elem.SetString( "name", INTERNAL_TAG("outervalues") ); - elem.SetString( "value", GetValue( func->_noutervalues, flags ) ); - elem.SetInt( "variablesReference", ToVarRef( VARREF_OUTERS, target ) ); - SetVirtualHint( elem ); - } + if ( func->_noutervalues ) + { + wjson_table_t elem = variables.AppendTable(); + elem.SetString( "name", INTERNAL_TAG("outervalues") ); + elem.SetString( "value", GetValue( func->_noutervalues, flags ) ); + elem.SetInt( "variablesReference", ToVarRef( VARREF_OUTERS, target ) ); + SetVirtualHint( elem ); + } #ifdef CLOSURE_ENV_ISVALID - if ( CLOSURE_ENV_ISVALID( _closure(target)->_env ) ) - { - wjson_table_t elem = variables.AppendTable(); - elem.SetString( "name", INTERNAL_TAG("env") ); - elem.SetString( "value", GetValue( - CLOSURE_ENV_OBJ( _closure(target)->_env ) ) ); - elem.SetInt( "variablesReference", ToVarRef( - CLOSURE_ENV_OBJ( _closure(target)->_env ) ) ); - SetVirtualHint( elem ); - } + if ( CLOSURE_ENV_ISVALID( _closure(target)->_env ) ) + { + wjson_table_t elem = variables.AppendTable(); + elem.SetString( "name", INTERNAL_TAG("env") ); + JSONSetString( elem, "value", CLOSURE_ENV_OBJ( _closure(target)->_env ) ); + elem.SetInt( "variablesReference", ToVarRef( + CLOSURE_ENV_OBJ( _closure(target)->_env ) ) ); + SetVirtualHint( elem ); + } #endif #ifdef CLOSURE_ROOT - if ( _closure(target)->_root && - _table(_closure(target)->_root->_obj) != _table(m_pRootVM->_roottable) ) - { - wjson_table_t elem = variables.AppendTable(); - elem.SetString( "name", INTERNAL_TAG("root") ); - elem.SetString( "value", GetValue( _closure(target)->_root->_obj ) ); - elem.SetInt( "variablesReference", ToVarRef( _closure(target)->_root->_obj ) ); - SetVirtualHint( elem ); - } + if ( _closure(target)->_root && + _table(_closure(target)->_root->_obj) != _table(m_pRootVM->_roottable) ) + { + wjson_table_t elem = variables.AppendTable(); + elem.SetString( "name", INTERNAL_TAG("root") ); + JSONSetString( elem, "value", _closure(target)->_root->_obj ); + elem.SetInt( "variablesReference", ToVarRef( _closure(target)->_root->_obj ) ); + SetVirtualHint( elem ); + } #endif + } + break; } case OT_NATIVECLOSURE: @@ -13910,88 +15129,88 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq // if only parameter has no type check, ignore !( nparams == 1 && _nativeclosure(target)->_typecheck.size() == 0 ) ) { - stringbuf_t< 64 > buf; - - if ( !( flags & kFS_Hexadecimal ) ) - { - buf.PutInt( nparams < 0 ? -nparams : nparams ); - } - else - { - buf.PutHex( nparams < 0 ? -nparams : nparams, false ); - } - - if ( nparams < 0 ) - buf.Puts("..."); - - if ( _nativeclosure(target)->_typecheck.size() ) - { - buf.Put(' '); - buf.Put('('); - - for ( int i = 0; i < (int)_nativeclosure(target)->_typecheck.size(); i++ ) - { - int mask = _nativeclosure(target)->_typecheck[i]; - Assert( mask ); - - if ( mask == -1 ) - { - buf.Put('.'); - buf.Put(','); - buf.Put(' '); - continue; - } - - #define _check( t, c ) \ - if ( mask & (t) ) \ - { \ - buf.Put((c)); \ - buf.Put('|'); \ - } - - #define _check_match( t, c ) \ - if ( ( mask & (t) ) == (t) ) \ - { \ - buf.Put((c)); \ - buf.Put('|'); \ - } - - _check( _RT_NULL, 'o' ) - _check_match( _RT_INTEGER | _RT_FLOAT, 'n' ) - else - { - _check( _RT_INTEGER, 'i' ) - else - _check( _RT_FLOAT, 'f' ) - } - _check( _RT_BOOL, 'b' ) - _check( _RT_STRING, 's' ) - _check( _RT_CLOSURE | _RT_NATIVECLOSURE, 'c' ) - _check( _RT_TABLE, 't' ) - _check( _RT_ARRAY, 'a' ) - _check( _RT_INSTANCE, 'x' ) - _check( _RT_CLASS, 'y' ) - _check( _RT_USERDATA, 'u' ) - _check( _RT_USERPOINTER, 'p' ) - _check( _RT_GENERATOR, 'g' ) - _check( _RT_THREAD, 'v' ) - _check( _RT_WEAKREF, 'r' ) - - #undef _check - #undef _check_match - - buf.len--; - buf.Put(','); - buf.Put(' '); - } - - buf.len -= 2; - buf.Put(')'); - } - wjson_table_t elem = variables.AppendTable(); elem.SetString( "name", INTERNAL_TAG("parameters") ); - elem.SetString( "value", buf ); + { + jstringbuf_t buf = elem.SetStringAsBuf( "value" ); + + if ( !( flags & kFS_Hexadecimal ) ) + { + buf.PutInt( nparams < 0 ? -nparams : nparams ); + } + else + { + buf.PutHex( nparams < 0 ? -nparams : nparams, false ); + } + + if ( nparams < 0 ) + buf.Puts("..."); + + if ( _nativeclosure(target)->_typecheck.size() ) + { + buf.Put(' '); + buf.Put('('); + + for ( int i = 0; i < (int)_nativeclosure(target)->_typecheck.size(); i++ ) + { + int mask = _nativeclosure(target)->_typecheck[i]; + Assert( mask ); + + if ( mask == -1 ) + { + buf.Put('.'); + buf.Put(','); + buf.Put(' '); + continue; + } + + #define _check( t, c ) \ + if ( mask & (t) ) \ + { \ + buf.Put((c)); \ + buf.Put('|'); \ + } + + #define _check_match( t, c ) \ + if ( ( mask & (t) ) == (t) ) \ + { \ + buf.Put((c)); \ + buf.Put('|'); \ + } + + _check( _RT_NULL, 'o' ) + _check_match( _RT_INTEGER | _RT_FLOAT, 'n' ) + else + { + _check( _RT_INTEGER, 'i' ) + else + _check( _RT_FLOAT, 'f' ) + } + _check( _RT_BOOL, 'b' ) + _check( _RT_STRING, 's' ) + _check( _RT_CLOSURE | _RT_NATIVECLOSURE, 'c' ) + _check( _RT_TABLE, 't' ) + _check( _RT_ARRAY, 'a' ) + _check( _RT_INSTANCE, 'x' ) + _check( _RT_CLASS, 'y' ) + _check( _RT_USERDATA, 'u' ) + _check( _RT_USERPOINTER, 'p' ) + _check( _RT_GENERATOR, 'g' ) + _check( _RT_THREAD, 'v' ) + _check( _RT_WEAKREF, 'r' ) + + #undef _check + #undef _check_match + + buf.Seek( -1 ); + buf.Put(','); + buf.Put(' '); + } + + buf.Seek( -2 ); + buf.Put(')'); + } + } elem.SetInt( "variablesReference", -1 ); SetVirtualHint( elem ); } @@ -14010,8 +15229,7 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq { wjson_table_t elem = variables.AppendTable(); elem.SetString( "name", INTERNAL_TAG("env") ); - elem.SetString( "value", GetValue( - CLOSURE_ENV_OBJ( _nativeclosure(target)->_env ) ) ); + JSONSetString( elem, "value", CLOSURE_ENV_OBJ( _nativeclosure(target)->_env ) ); elem.SetInt( "variablesReference", ToVarRef( CLOSURE_ENV_OBJ( _nativeclosure(target)->_env ) ) ); SetVirtualHint( elem ); @@ -14041,7 +15259,7 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq { wjson_table_t elem = variables.AppendTable(); elem.SetString( "name", INTERNAL_TAG("root") ); - elem.SetString( "value", GetValue( _thread(target)->_roottable ) ); + JSONSetString( elem, "value", _thread(target)->_roottable ); elem.SetInt( "variablesReference", -1 ); SetVirtualHint( elem ); } @@ -14053,7 +15271,7 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq wjson_table_t elem = variables.AppendTable(); elem.SetString( "name", INTERNAL_TAG("function") ); - elem.SetString( "value", GetValue( val ) ); + JSONSetString( elem, "value", val ); elem.SetInt( "variablesReference", ToVarRef( val ) ); SetVirtualHint( elem ); } @@ -14096,14 +15314,14 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq sqstring_t(_string(func->_sourcename)) : sqstring_t(_SC("??")); - stringbufext_t buf = ScratchPadBuf( scstombslen( source.ptr, source.len ) + FMT_INT_LEN ); - buf.Puts( source ); - buf.Put(':'); - buf.PutInt( (int)func->GetLine( ci._ip ) ); - wjson_table_t elem = variables.AppendTable(); elem.SetString( "name", INTERNAL_TAG("frame") ); - elem.SetString( "value", buf ); + { + jstringbuf_t buf = elem.SetStringAsBuf( "value" ); + buf.Puts( source ); + buf.Put(':'); + buf.PutInt( (int)func->GetLine( ci._ip ) ); + } elem.SetInt( "variablesReference", -1 ); SetVirtualHint( elem ); } @@ -14115,7 +15333,7 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq wjson_table_t elem = variables.AppendTable(); elem.SetString( "name", INTERNAL_TAG("function") ); - elem.SetString( "value", GetValue( val ) ); + JSONSetString( elem, "value", val ); elem.SetInt( "variablesReference", ToVarRef( val ) ); SetVirtualHint( elem ); } @@ -14137,7 +15355,7 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq { SQObject target = ref->GetVar(); - if ( sq_type(target) != OT_CLOSURE ) + if ( sq_type(target) != OT_FUNCPROTO ) { DAP_ERROR_RESPONSE( seq, "variables" ); DAP_ERROR_BODY( 0, "invalid object" ); @@ -14147,7 +15365,7 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq RestoreCachedInstructions(); - SQFunctionProto *func = _fp(_closure(target)->_function); + SQFunctionProto *func = _funcproto(target); int ninstructions = func->_ninstructions; DAP_START_RESPONSE( seq, "variables" ); @@ -14169,20 +15387,20 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq wjson_table_t elem = variables.AppendTable(); { - stringbuf_t< 32 > instrBytes; // "0xFF -2147483648 255 255 255" + // "0xFF -2147483648 255 255 255" + jstringbuf_t instrBytes = elem.SetStringAsBuf( "value" ); instrBytes.PutHex( instr->op ); instrBytes.Put(' '); instrBytes.PutInt( instr->_arg0 ); instrBytes.Put(' '); instrBytes.PutInt( instr->_arg1 ); instrBytes.Put(' '); instrBytes.PutInt( instr->_arg2 ); instrBytes.Put(' '); instrBytes.PutInt( instr->_arg3 ); - elem.SetString( "value", instrBytes ); } { - stringbuf_t< FMT_UINT32_LEN * 2 + 1 > name; // index:line + // index:line + jstringbuf_t name = elem.SetStringAsBuf( "name" ); name.PutInt( i - lines ); name.Put(':'); name.PutInt( (int)func->GetLine( instr ) ); - elem.SetString( "name", name ); } elem.SetInt( "variablesReference", -1 ); #ifndef SQDBG_SUPPORTS_SET_INSTRUCTION @@ -14238,7 +15456,7 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq { SQObject target = ref->GetVar(); - if ( sq_type(target) != OT_CLOSURE ) + if ( sq_type(target) != OT_FUNCPROTO ) { DAP_ERROR_RESPONSE( seq, "variables" ); DAP_ERROR_BODY( 0, "invalid object" ); @@ -14246,8 +15464,7 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq return; } - SQClosure *pClosure = _closure(target); - SQFunctionProto *func = _fp(pClosure->_function); + SQFunctionProto *func = _funcproto(target); DAP_START_RESPONSE( seq, "variables" ); DAP_SET_TABLE( body ); @@ -14297,7 +15514,7 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq { wjson_table_t elem = variables.AppendTable(); elem.SetString( "name", g_MetaMethodName[i] ); - elem.SetString( "value", GetValue( val ) ); + JSONSetString( elem, "value", val ); elem.SetString( "type", GetType( val ) ); elem.SetInt( "variablesReference", ToVarRef( val ) ); } @@ -14318,7 +15535,7 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq { wjson_table_t elem = variables.AppendTable(); elem.SetString( "name", g_MetaMethodName[i] ); - elem.SetString( "value", GetValue( val ) ); + JSONSetString( elem, "value", val ); elem.SetString( "type", GetType( val ) ); elem.SetInt( "variablesReference", ToVarRef( val ) ); } @@ -14400,42 +15617,41 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq DAP_SET_TABLE( body ); int i = _thread(target)->_callsstacksize; wjson_array_t variables = body.SetArray( "variables" ); - stringbufext_t buf = ScratchPadBuf( 256 ); while ( i-- ) { const SQVM::CallInfo &ci = _thread(target)->_callsstack[i]; - if ( ShouldIgnoreStackFrame(ci) ) + if ( ShouldIgnoreStackFrame( _thread(target), ci ) ) continue; - if ( sq_type(ci._closure) == OT_CLOSURE ) - { - SQFunctionProto *func = _fp(_closure(ci._closure)->_function); - - buf.len = 0; - - if ( sq_type(func->_sourcename) == OT_STRING ) - { - buf.Puts( _string(func->_sourcename) ); - } - else - { - buf.Puts("??"); - } - - buf.Put(':'); - buf.PutInt( (int)func->GetLine( ci._ip ) ); - } - else if ( sq_type(ci._closure) == OT_NATIVECLOSURE ) - { - buf.Puts("NATIVE"); - } - else UNREACHABLE(); - wjson_table_t elem = variables.AppendTable(); elem.SetIntBrackets( "name", i ); - elem.SetString( "value", buf ); + { + jstringbuf_t buf = elem.SetStringAsBuf( "value" ); + + if ( sq_type(ci._closure) == OT_CLOSURE ) + { + SQFunctionProto *func = _fp(_closure(ci._closure)->_function); + + if ( sq_type(func->_sourcename) == OT_STRING ) + { + buf.Puts( _string(func->_sourcename) ); + } + else + { + buf.Puts("??"); + } + + buf.Put(':'); + buf.PutInt( (int)func->GetLine( ci._ip ) ); + } + else if ( sq_type(ci._closure) == OT_NATIVECLOSURE ) + { + buf.Puts("NATIVE"); + } + else UNREACHABLE(); + } elem.SetInt( "variablesReference", ToVarRef( ci._closure ) ); wjson_table_t hint = elem.SetTable( "presentationHint" ); wjson_array_t attributes = hint.SetArray( "attributes" ); @@ -14451,7 +15667,7 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq int frame = ref->scope.frame; if ( !IsValidStackFrame( vm, frame ) ) - frame = -1; + frame = INVALID_FRAME; DAP_START_RESPONSE( seq, "variables" ); DAP_SET_TABLE( body ); @@ -14470,51 +15686,51 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq if ( i > vm->_top && sq_type(val) == OT_NULL ) continue; - stringbuf_t< 2 + FMT_UINT32_LEN + 2 > name; - name.Put('['); - - if ( !( flags & kFS_Hexadecimal ) ) + wjson_table_t elem = variables.AppendTable(); { - name.PutInt( i ); - } - else - { - name.PutHex( i, false ); - } + jstringbuf_t name = elem.SetStringAsBuf( "name" ); + name.Put('['); - name.Put(']'); - - if ( stackbase == i ) - { - name.Put('*'); - - if ( callframe == frame ) + if ( !( flags & kFS_Hexadecimal ) ) { - name.Put('~'); + name.PutInt( i ); } -#ifndef NATIVE_DEBUG_HOOK - else if ( m_State == ThreadState_Suspended && - callframe == vm->_callsstacksize - 1 ) + else { - if ( sq_type(vm->_callsstack[callframe]._closure) == OT_NATIVECLOSURE && - _nativeclosure(vm->_callsstack[callframe]._closure)->_function == - &SQDebugServer::SQDebugHook ) + name.PutHex( i, false ); + } + + name.Put(']'); + + if ( stackbase == i ) + { + name.Put('*'); + + if ( callframe == frame ) { - name.Put('d'); + name.Put('~'); + } +#ifndef NATIVE_DEBUG_HOOK + else if ( m_State == ThreadState_Suspended && + callframe == vm->_callsstacksize - 1 ) + { + if ( sq_type(vm->_callsstack[callframe]._closure) == OT_NATIVECLOSURE && + _nativeclosure(vm->_callsstack[callframe]._closure)->_function == + &SQDebugServer::SQDebugHook ) + { + name.Put('d'); + } } - } #endif - if ( ++callframe < vm->_callsstacksize ) - stackbase += vm->_callsstack[callframe]._prevstkbase; + if ( ++callframe < vm->_callsstacksize ) + stackbase += vm->_callsstack[callframe]._prevstkbase; + } + else if ( vm->_top == i ) + { + name.Put('_'); + } } - else if ( vm->_top == i ) - { - name.Put('_'); - } - - wjson_table_t elem = variables.AppendTable(); - elem.SetString( "name", name ); JSONSetString( elem, "value", val, flags ); elem.SetString( "type", GetType( val ) ); elem.SetInt( "variablesReference", ToVarRef( val ) ); @@ -14574,7 +15790,7 @@ void SQDebugServer::OnRequest_SetVariable( const json_table_t &arguments, int se else { vm = m_pCurVM; - frame = -1; + frame = INVALID_FRAME; } // Requires special value parsing @@ -14634,11 +15850,11 @@ void SQDebugServer::OnRequest_SetVariable( const json_table_t &arguments, int se #endif { DAP_ERROR_RESPONSE( seq, "setVariable" ); - DAP_ERROR_BODY( 0, "failed to evaluate value '{name}'\n\n[{reason}]" ); + DAP_ERROR_BODY( 0, "failed to evaluate value '{name}'\\n[{reason}]" ); error.SetBool( "showUser", true ); wjson_table_t variables = error.SetTable( "variables" ); variables.SetString( "name", strValue ); - variables.SetString( "reason", GetValue( vm->_lasterror, kFS_NoQuote ) ); + JSONSetString( variables, "reason", vm->_lasterror, kFS_NoQuote ); DAP_SEND(); return; } @@ -14649,16 +15865,16 @@ void SQDebugServer::OnRequest_SetVariable( const json_table_t &arguments, int se DAP_ERROR_BODY( 0, "could not set '{name}'" ); error.SetBool( "showUser", true ); wjson_table_t variables = error.SetTable( "variables" ); - variables.SetString( "name", GetValue( obj.key ) ); + JSONSetString( variables, "name", obj.key ); DAP_SEND(); return; } // Update data watch - if ( IsObjectRef( ref->type ) && ref->obj.isWeak && + if ( IsObjectRef( ref->type ) && _refcounted(ref->GetVar())->_weakref ) { - for ( int i = m_DataWatches.size(); i--; ) + for ( int i = m_DataWatches.Size(); i--; ) { datawatch_t &dw = m_DataWatches[i]; if ( _refcounted(ref->GetVar())->_weakref == dw.container && @@ -14686,7 +15902,7 @@ void SQDebugServer::OnRequest_SetVariable_Instruction( const varref_t *ref, string_t &strValue, int seq ) { - if ( sq_type(ref->GetVar()) != OT_CLOSURE ) + if ( sq_type(ref->GetVar()) != OT_FUNCPROTO ) { DAP_ERROR_RESPONSE( seq, "setVariable" ); DAP_ERROR_BODY( 0, "invalid object" ); @@ -14720,10 +15936,10 @@ void SQDebugServer::OnRequest_SetVariable_Instruction( const varref_t *ref, if ( op != _OP_LOADFLOAT ) { char buf[96]; - int len = snprintf( buf, sizeof(buf), + unsigned int len = snprintf( buf, sizeof(buf), "Warning: Setting float value (%.2f) to non-float instruction\n", farg1 ); - SendEvent_OutputStdOut( string_t( buf, min( len, (int)sizeof(buf) ) ), NULL ); + SendEvent_OutputStdOut( string_t( buf, min( len, sizeof(buf) ) ), NULL ); } } } @@ -14744,7 +15960,7 @@ void SQDebugServer::OnRequest_SetVariable_Instruction( const varref_t *ref, RestoreCachedInstructions(); - SQFunctionProto *func = _fp(_closure(ref->GetVar())->_function); + SQFunctionProto *func = _funcproto(ref->GetVar()); // line ops are ignored in the index for ( int c = 0; c < func->_ninstructions; c++ ) @@ -14776,16 +15992,16 @@ void SQDebugServer::OnRequest_SetVariable_Instruction( const varref_t *ref, instr->_arg2 = arg2 & 0xff; instr->_arg3 = arg3 & 0xff; - stringbuf_t< 32 > instrBytes; - instrBytes.PutHex( instr->op ); instrBytes.Put(' '); - instrBytes.PutInt( instr->_arg0 ); instrBytes.Put(' '); - instrBytes.PutInt( instr->_arg1 ); instrBytes.Put(' '); - instrBytes.PutInt( instr->_arg2 ); instrBytes.Put(' '); - instrBytes.PutInt( instr->_arg3 ); - DAP_START_RESPONSE( seq, "setVariable" ); DAP_SET_TABLE( body ); - body.SetString( "value", instrBytes ); + { + jstringbuf_t instrBytes = body.SetStringAsBuf( "value" ); + instrBytes.PutHex( instr->op ); instrBytes.Put(' '); + instrBytes.PutInt( instr->_arg0 ); instrBytes.Put(' '); + instrBytes.PutInt( instr->_arg1 ); instrBytes.Put(' '); + instrBytes.PutInt( instr->_arg2 ); instrBytes.Put(' '); + instrBytes.PutInt( instr->_arg3 ); + } body.SetInt( "variablesReference", -1 ); DAP_SEND(); } @@ -14813,7 +16029,7 @@ void SQDebugServer::OnRequest_SetExpression( const json_table_t &arguments, int if ( !TranslateFrameID( frame, &vm, &frame ) ) { vm = m_pCurVM; - frame = -1; + frame = INVALID_FRAME; } int flags = ParseFormatSpecifiers( expression ); @@ -14855,7 +16071,7 @@ void SQDebugServer::OnRequest_SetExpression( const json_table_t &arguments, int #ifdef _DEBUG bool foundWatch = false; #endif - for ( unsigned int i = 0; i < m_LockedWatches.size(); i++ ) + for ( unsigned int i = 0; i < m_LockedWatches.Size(); i++ ) { const watch_t &w = m_LockedWatches[i]; if ( w.expression.IsEqualTo( expression ) ) @@ -14916,7 +16132,7 @@ void SQDebugServer::OnRequest_SetExpression( const json_table_t &arguments, int DAP_ERROR_BODY( 0, "could not set '{name}'" ); error.SetBool( "showUser", true ); wjson_table_t variables = error.SetTable( "variables" ); - variables.SetString( "name", GetValue( obj.key ) ); + JSONSetString( variables, "name", obj.key ); DAP_SEND(); return; } @@ -14985,26 +16201,26 @@ void SQDebugServer::OnRequest_SetExpression( const json_table_t &arguments, int else { DAP_ERROR_RESPONSE( seq, "setExpression" ); - DAP_ERROR_BODY( 0, "failed to evaluate expression: {exp} = {val}\n\n[{reason}]" ); + DAP_ERROR_BODY( 0, "failed to evaluate expression: {exp} = {val}\\n[{reason}]" ); error.SetBool( "showUser", true ); wjson_table_t variables = error.SetTable( "variables" ); variables.SetString( "exp", expression ); variables.SetString( "val", strValue ); - variables.SetString( "reason", GetValue( vm->_lasterror, kFS_NoQuote ) ); + JSONSetString( variables, "reason", vm->_lasterror, kFS_NoQuote ); DAP_SEND(); return; } } // Update data watch - for ( int i = m_DataWatches.size(); i--; ) + for ( int i = m_DataWatches.Size(); i--; ) { datawatch_t &dw = m_DataWatches[i]; if ( dw.container && sq_type(dw.container->_obj) == OT_NULL ) { FreeDataWatch( dw ); - m_DataWatches.remove(i); + m_DataWatches.Remove(i); continue; } @@ -15060,7 +16276,7 @@ void SQDebugServer::OnRequest_Disassemble( const json_table_t &arguments, int se int targetEnd = targetStart + instructionCount; int validStart = max( 0, targetStart ); - int validEnd = min( func->_ninstructions - 1, targetEnd ); + int validEnd = min( (int)func->_ninstructions - 1, targetEnd ); DAP_START_RESPONSE( seq, "disassemble" ); DAP_SET_TABLE( body ); @@ -15072,9 +16288,10 @@ void SQDebugServer::OnRequest_Disassemble( const json_table_t &arguments, int se SQInstruction *instr = func->_instructions + index; - stringbuf_t< FMT_PTR_LEN > addr; - addr.PutHex( (uintptr_t)instr ); - elem.SetString( "address", addr ); + { + jstringbuf_t addr = elem.SetStringAsBuf( "address" ); + addr.PutHex( (uintptr_t)instr ); + } if ( index >= validStart && index <= validEnd ) { @@ -15086,13 +16303,12 @@ void SQDebugServer::OnRequest_Disassemble( const json_table_t &arguments, int se elem.SetString( "instruction", instrStr ); } { - stringbuf_t< 32 > instrBytes; + jstringbuf_t instrBytes = elem.SetStringAsBuf( "instructionBytes" ); instrBytes.PutHex( instr->op ); instrBytes.Put(' '); instrBytes.PutInt( instr->_arg0 ); instrBytes.Put(' '); instrBytes.PutInt( instr->_arg1 ); instrBytes.Put(' '); instrBytes.PutInt( instr->_arg2 ); instrBytes.Put(' '); instrBytes.PutInt( instr->_arg3 ); - elem.SetString( "instructionBytes", instrBytes ); } elem.SetInt( "line", (int)func->GetLine( instr ) ); @@ -15117,7 +16333,7 @@ void SQDebugServer::OnRequest_Disassemble( const json_table_t &arguments, int se } } - Assert( instructions.size() == instructionCount ); + Assert( instructions.Size() == instructionCount ); DAP_SEND(); UndoRestoreCachedInstructions(); @@ -15142,14 +16358,6 @@ void SQDebugServer::OnRequest_RestartFrame( const json_table_t &arguments, int s return; } - if ( vm != m_pCurVM ) - { - DAP_ERROR_RESPONSE( seq, "restartFrame" ); - DAP_ERROR_BODY( 0, "cannot restart frame on a different thread" ); - DAP_SEND(); - return; - } - const SQVM::CallInfo *pCurrent = vm->ci; const SQVM::CallInfo *pTarget = vm->_callsstack + frame; @@ -15348,15 +16556,15 @@ void SQDebugServer::OnRequest_GotoTargets( const json_table_t &arguments, int se return; } - stringbuf_t< FMT_INT_LEN > label; - label.Put('L'); - label.PutInt( line ); - DAP_START_RESPONSE( seq, "gotoTargets" ); DAP_SET_TABLE( body ); wjson_array_t targets = body.SetArray( "targets" ); wjson_table_t elem = targets.AppendTable(); - elem.SetString( "label", label ); + { + jstringbuf_t label = elem.SetStringAsBuf( "label" ); + label.Put('L'); + label.PutInt( line ); + } elem.SetInt( "line", line ); elem.SetInt( "id", line ); DAP_SEND(); @@ -15378,14 +16586,6 @@ void SQDebugServer::OnRequest_Goto( const json_table_t &arguments, int seq ) HSQUIRRELVM vm = ThreadFromID( threadId ); - if ( vm != m_pCurVM ) - { - DAP_ERROR_RESPONSE( seq, "goto" ); - DAP_ERROR_BODY( 0, "cannot change execution on a different thread" ); - DAP_SEND(); - return; - } - if ( !vm->ci ) { DAP_ERROR_RESPONSE( seq, "goto" ); @@ -15615,6 +16815,12 @@ void SQDebugServer::OnRequest_StepOut( const json_table_t &arguments, int seq ) DAP_SEND(); } +#ifdef SQDBG_WEAK_INSTRUCTION_REF +#define _CacheInstruction( _f, _i ) CacheInstruction( (_f), (_i) ) +#else +#define _CacheInstruction( _f, _i ) CacheInstruction( (_i) ) +#endif + bool SQDebugServer::InstructionStep( HSQUIRRELVM vm, SQVM::CallInfo *ci, int instrOffset ) { Assert( m_State == ThreadState_Suspended || @@ -15670,14 +16876,14 @@ bool SQDebugServer::InstructionStep( HSQUIRRELVM vm, SQVM::CallInfo *ci, int ins { if ( trapIp->op != _OP_LINE ) { - CacheInstruction( trapIp ); + _CacheInstruction( func, trapIp ); break; } } } } - CacheInstruction( ip ); + _CacheInstruction( func, ip ); } ip = ci->_ip - instrOffset; @@ -15692,7 +16898,7 @@ bool SQDebugServer::InstructionStep( HSQUIRRELVM vm, SQVM::CallInfo *ci, int ins { if ( p->op != _OP_LINE ) { - CacheInstruction( p ); + _CacheInstruction( func, p ); break; } } @@ -15729,14 +16935,14 @@ bool SQDebugServer::InstructionStep( HSQUIRRELVM vm, SQVM::CallInfo *ci, int ins { if ( trapIp->op != _OP_LINE ) { - CacheInstruction( trapIp ); + _CacheInstruction( func, trapIp ); break; } } } } - CacheInstruction( ip ); + _CacheInstruction( func, ip ); } return true; @@ -15791,7 +16997,7 @@ bool SQDebugServer::Step( HSQUIRRELVM vm, SQVM::CallInfo *ci ) Assert( op < func->_ninstructions ); Assert( func->_instructions + op != ci->_ip ); - CacheInstruction( func->_instructions + op ); + _CacheInstruction( func, func->_instructions + op ); // Set break point at every possible jump target for ( SQInstruction *ip = ci->_ip + 1; ip <= func->_instructions + op; ip++ ) @@ -15799,48 +17005,77 @@ bool SQDebugServer::Step( HSQUIRRELVM vm, SQVM::CallInfo *ci ) if ( IsJumpOp( ip ) && GetJumpCount( ip ) != 0 ) { if ( ip->op == _OP_FOREACH ) - CacheInstruction( ip + 1 ); + _CacheInstruction( func, ip + 1 ); - CacheInstruction( ip + GetJumpCount( ip ) + 1 ); + _CacheInstruction( func, ip + GetJumpCount( ip ) + 1 ); } } return true; } +#undef _CacheInstruction + +#ifdef SQDBG_WEAK_INSTRUCTION_REF +void SQDebugServer::CacheInstruction( SQFunctionProto *func, SQInstruction *instr ) +#else void SQDebugServer::CacheInstruction( SQInstruction *instr ) +#endif { - // A way to keep a weak ref to this pointer would be keeping SQFunctionProto as SQWeakRef - // This would only work in SQ3 because SQFunctionProto is ref counted since then - cachedinstr_t &cached = m_CachedInstructions.append(); + cachedinstr_t &cached = m_CachedInstructions.Append(); +#ifdef SQDBG_WEAK_INSTRUCTION_REF + Assert( instr >= func->_instructions && instr < func->_instructions + func->_ninstructions ); + Assert( instr - func->_instructions <= INT_MAX ); + cached.func = func->GetWeakRef( OT_FUNCPROTO ); + __ObjAddRef( cached.func ); + cached.index = instr - func->_instructions; +#else cached.ip = instr; - cached.instr = *cached.ip; - memzero( cached.ip ); - Assert( cached.ip->op == _OP_LINE ); +#endif + cached.instr = *instr; + + memzero( instr ); + Assert( instr->op == _OP_LINE ); } void SQDebugServer::ClearCachedInstructions() { - m_CachedInstructions.clear(); + m_CachedInstructions.Clear(); } void SQDebugServer::RestoreCachedInstructions() { - for ( int i = m_CachedInstructions.size(); i--; ) + for ( int i = m_CachedInstructions.Size(); i--; ) { cachedinstr_t &cached = m_CachedInstructions[i]; +#ifdef SQDBG_WEAK_INSTRUCTION_REF + if ( cached.func && sq_type(cached.func->_obj) == OT_FUNCPROTO ) + { + _funcproto(cached.func->_obj)->_instructions[ cached.index ] = cached.instr; + __ObjRelease( cached.func ); + } +#else if ( cached.ip ) *cached.ip = cached.instr; +#endif } } void SQDebugServer::UndoRestoreCachedInstructions() { - for ( int i = m_CachedInstructions.size(); i--; ) + for ( int i = m_CachedInstructions.Size(); i--; ) { cachedinstr_t &cached = m_CachedInstructions[i]; +#ifdef SQDBG_WEAK_INSTRUCTION_REF + if ( cached.func && sq_type(cached.func->_obj) == OT_FUNCPROTO ) + { + memzero( &_funcproto(cached.func->_obj)->_instructions[ cached.index ] ); + __ObjAddRef( cached.func ); + } +#else if ( cached.ip ) memzero( cached.ip ); +#endif } } @@ -15850,14 +17085,14 @@ int SQDebugServer::ToVarRef( EVARREF type, HSQUIRRELVM vm, int frame ) SQWeakRef *thread = GetWeakRef( vm ); - for ( unsigned int i = 0; i < m_Vars.size(); i++ ) + for ( unsigned int i = 0; i < m_Vars.Size(); i++ ) { varref_t &v = m_Vars[i]; if ( v.type == type && v.scope.frame == frame && v.scope.thread == thread ) return v.id; } - varref_t &var = m_Vars.append(); + varref_t &var = m_Vars.Append(); var.id = ++m_nVarRefIndex; var.type = type; var.scope.frame = frame; @@ -15870,69 +17105,54 @@ void SQDebugServer::ConvertToWeakRef( varref_t &v ) { Assert( IsObjectRef( v.type ) ); - if ( !v.obj.isWeak ) + if ( v.obj.isStrong ) { - v.obj.weakref = GetWeakRef( _refcounted(v.obj.obj), sq_type(v.obj.obj) ); - __ObjAddRef( v.obj.weakref ); - v.obj.isWeak = true; - - if ( v.obj.isStrong ) - { - __ObjRelease( _refcounted(v.obj.obj) ); - v.obj.isStrong = false; - } + v.obj.isStrong = false; + SQObject obj = v.GetVar(); + __ObjRelease( _refcounted(obj) ); } } -int SQDebugServer::ToVarRef( EVARREF type, const SQObject &obj, bool isWeak, bool isStrong ) +int SQDebugServer::ToVarRef( EVARREF type, const SQObject &obj, bool isStrong ) { Assert( IsObjectRef( type ) ); - Assert( ( !isWeak && !isStrong ) || ( isWeak != isStrong ) ); if ( !ISREFCOUNTED( sq_type(obj) ) ) return INVALID_ID; - if ( sq_type(obj) == OT_WEAKREF && isWeak ) + if ( sq_type(obj) == OT_WEAKREF ) { if ( sq_type(_weakref(obj)->_obj) == OT_NULL ) return INVALID_ID; } - for ( int i = m_Vars.size(); i--; ) + for ( int i = m_Vars.Size(); i--; ) { varref_t &v = m_Vars[i]; if ( v.type == type && _rawval(v.GetVar()) == _rawval(obj) ) { - if ( isWeak ) - ConvertToWeakRef( v ); - + ConvertToWeakRef( v ); return v.id; } } Assert( m_nVarRefIndex < INT_MAX ); - varref_t *var = &m_Vars.append(); + varref_t *var = &m_Vars.Append(); var->id = ++m_nVarRefIndex; var->type = type; - var->obj.isWeak = isWeak; var->obj.isStrong = isStrong; - if ( isWeak ) - { - var->obj.weakref = GetWeakRef( _refcounted(obj), sq_type(obj) ); - __ObjAddRef( var->obj.weakref ); + // Clients can request nested and invalidated objects, always use weakrefs + var->obj.weakref = GetWeakRef( _refcounted(obj), sq_type(obj) ); + __ObjAddRef( var->obj.weakref ); - Assert( sq_type(var->obj.weakref->_obj) != OT_NULL ); - } - else - { - var->obj.obj = obj; + Assert( sq_type(var->obj.weakref->_obj) != OT_NULL ); + Assert( _rawval(var->obj.weakref->_obj) == _rawval(obj) ); - if ( isStrong ) - { - __ObjAddRef( _refcounted(var->obj.obj) ); - } + if ( isStrong ) + { + __ObjAddRef( _refcounted(obj) ); } return var->id; @@ -15940,7 +17160,7 @@ int SQDebugServer::ToVarRef( EVARREF type, const SQObject &obj, bool isWeak, boo varref_t *SQDebugServer::FromVarRef( int id ) { - int hi = m_Vars.size() - 1; + int hi = m_Vars.Size() - 1; int lo = 0; while ( lo <= hi ) @@ -15962,14 +17182,13 @@ varref_t *SQDebugServer::FromVarRef( int id ) Assert( var->type >= 0 && var->type < VARREF_MAX ); if ( IsScopeRef( var->type ) || - ( !var->obj.isWeak || sq_type(var->GetVar()) != OT_NULL ) ) + var->obj.isStrong || sq_type(var->GetVar()) != OT_NULL ) return var; - Assert( var->obj.isWeak ); Assert( var->obj.weakref ); __ObjRelease( var->obj.weakref ); - m_Vars.remove(mid); + m_Vars.Remove(mid); return NULL; } @@ -15982,72 +17201,72 @@ void SQDebugServer::RemoveVarRefs( bool all ) { if ( !all ) { - for ( int i = m_Vars.size(); i--; ) + for ( int i = m_Vars.Size(); i--; ) { varref_t &v = m_Vars[i]; // Keep living weakrefs, client might refer to them later if ( IsScopeRef( v.type ) ) { - m_Vars.remove(i); + m_Vars.Remove(i); } - else if ( IsObjectRef( v.type ) && !v.obj.isWeak ) + else if ( IsObjectRef( v.type ) ) { + bool rem = false; + if ( v.obj.isStrong ) { - Assert( ISREFCOUNTED( sq_type(v.obj.obj) ) ); - __ObjRelease( _refcounted(v.obj.obj) ); + SQObject obj = v.GetVar(); + Assert( ISREFCOUNTED( sq_type(obj) ) ); + __ObjRelease( _refcounted(obj) ); + rem = true; } - m_Vars.remove(i); - } - else - { - Assert( v.obj.isWeak ); Assert( v.obj.weakref ); if ( sq_type(v.obj.weakref->_obj) == OT_NULL ) { __ObjRelease( v.obj.weakref ); - m_Vars.remove(i); + rem = true; } + + if ( rem ) + m_Vars.Remove(i); } + else UNREACHABLE(); } } else { - for ( int i = m_Vars.size(); i--; ) + for ( int i = m_Vars.Size(); i--; ) { varref_t &v = m_Vars[i]; // Release all refs the debugger is holding if ( IsObjectRef( v.type ) ) { - Assert( ( !v.obj.isWeak && !v.obj.isStrong ) || ( v.obj.isWeak != v.obj.isStrong ) ); + if ( v.obj.isStrong ) + { + SQObject obj = v.GetVar(); + Assert( ISREFCOUNTED( sq_type(obj) ) ); + __ObjRelease( _refcounted(obj) ); + } - if ( v.obj.isWeak ) - { - Assert( v.obj.weakref ); - __ObjRelease( v.obj.weakref ); - } - else if ( v.obj.isStrong ) - { - Assert( ISREFCOUNTED( sq_type(v.obj.obj) ) ); - __ObjRelease( _refcounted(v.obj.obj) ); - } + Assert( v.obj.weakref ); + __ObjRelease( v.obj.weakref ); } } - m_Vars.purge(); + m_Vars.Purge(); } } void SQDebugServer::RemoveLockedWatches() { - for ( unsigned int i = 0; i < m_LockedWatches.size(); i++ ) + for ( unsigned int i = 0; i < m_LockedWatches.Size(); i++ ) FreeString( &m_Strings, &m_LockedWatches[i].expression ); - m_LockedWatches.purge(); + m_LockedWatches.Purge(); } int SQDebugServer::AddBreakpoint( int line, const string_t &src, @@ -16056,7 +17275,7 @@ int SQDebugServer::AddBreakpoint( int line, const string_t &src, Assert( line > 0 && !src.IsEmpty() ); #ifdef SQUNICODE - int size = sq_rsl( SQUnicodeLength( src.ptr, src.len ) ); + unsigned int size = sq_rsl( SQUnicodeLength( src.ptr, src.len ) ); SQChar *pSrc = (SQChar*)ScratchPad( size ); sqstring_t wsrc; wsrc.Assign( pSrc, UTF8ToSQUnicode( pSrc, size, src.ptr, src.len ) ); @@ -16079,7 +17298,7 @@ int SQDebugServer::AddBreakpoint( int line, const string_t &src, Assert( m_nBreakpointIndex < INT_MAX ); - bp = &m_Breakpoints.insert( m_nFunctionBreakpointsIdx++ ); + bp = &m_Breakpoints.Insert( m_nFunctionBreakpointsIdx++ ); bp->id = ++m_nBreakpointIndex; CopyString( &m_Strings, src, &bp->src ); @@ -16147,7 +17366,7 @@ int SQDebugServer::AddFunctionBreakpoint( const string_t &func, const string_t & Assert( m_nBreakpointIndex < INT_MAX ); - bp = &m_Breakpoints.append(); + bp = &m_Breakpoints.Append(); bp->id = ++m_nBreakpointIndex; if ( !func.IsEmpty() ) @@ -16208,7 +17427,7 @@ breakpoint_t *SQDebugServer::GetFunctionBreakpoint( const sqstring_t &func, cons { Assert( func.ptr ); - for ( unsigned int i = m_nFunctionBreakpointsIdx; i < m_Breakpoints.size(); i++ ) + for ( unsigned int i = m_nFunctionBreakpointsIdx; i < m_Breakpoints.Size(); i++ ) { breakpoint_t &bp = m_Breakpoints[i]; Assert( bp.line == 0 ); @@ -16249,23 +17468,22 @@ void SQDebugServer::FreeBreakpoint( breakpoint_t &bp ) void SQDebugServer::RemoveAllBreakpoints() { - for ( int i = m_Breakpoints.size(); i--; ) + for ( int i = m_Breakpoints.Size(); i--; ) FreeBreakpoint( m_Breakpoints[i] ); - m_Breakpoints.clear(); + m_Breakpoints.Clear(); m_nFunctionBreakpointsIdx = 0; } void SQDebugServer::RemoveBreakpoints( const string_t &source ) { - sqstring_t src; - #ifdef SQUNICODE - int size = sq_rsl( SQUnicodeLength( source.ptr, source.len ) ); + unsigned int size = sq_rsl( SQUnicodeLength( source.ptr, source.len ) ); SQChar *tmp = (SQChar*)ScratchPad( size ); + sqstring_t src; src.Assign( tmp, UTF8ToSQUnicode( tmp, size, source.ptr, source.len ) ); #else - src = source; + const sqstring_t &src = source; #endif for ( unsigned int i = 0; i < m_nFunctionBreakpointsIdx; ) @@ -16276,7 +17494,7 @@ void SQDebugServer::RemoveBreakpoints( const string_t &source ) if ( bp.src.IsEqualTo( src ) ) { FreeBreakpoint( bp ); - m_Breakpoints.remove(i); + m_Breakpoints.Remove(i); m_nFunctionBreakpointsIdx--; } else @@ -16288,21 +17506,21 @@ void SQDebugServer::RemoveBreakpoints( const string_t &source ) void SQDebugServer::RemoveFunctionBreakpoints() { - for ( unsigned int i = m_nFunctionBreakpointsIdx; i < m_Breakpoints.size(); ) + for ( unsigned int i = m_nFunctionBreakpointsIdx; i < m_Breakpoints.Size(); ) { breakpoint_t &bp = m_Breakpoints[i]; Assert( bp.line == 0 ); FreeBreakpoint( bp ); - m_Breakpoints.remove(i); + m_Breakpoints.Remove(i); } - m_nFunctionBreakpointsIdx = m_Breakpoints.size(); + m_nFunctionBreakpointsIdx = m_Breakpoints.Size(); } classdef_t *SQDebugServer::FindClassDef( SQClass *base ) { - for ( unsigned int i = 0; i < m_ClassDefinitions.size(); i++ ) + for ( unsigned int i = 0; i < m_ClassDefinitions.Size(); i++ ) { classdef_t &def = m_ClassDefinitions[i]; if ( def.base == base ) @@ -16360,7 +17578,7 @@ void SQDebugServer::DefineClass( SQClass *target, SQTable *params ) if ( !def ) { - def = &m_ClassDefinitions.append(); + def = &m_ClassDefinitions.Append(); def->base = target; } @@ -16500,7 +17718,7 @@ bool SQDebugServer::CallCustomMembersSetFunc( const SQObjectPtr &closure, const void SQDebugServer::RemoveClassDefs() { - for ( unsigned int i = 0; i < m_ClassDefinitions.size(); i++ ) + for ( unsigned int i = 0; i < m_ClassDefinitions.Size(); i++ ) { classdef_t &def = m_ClassDefinitions[i]; @@ -16528,25 +17746,27 @@ void SQDebugServer::RemoveClassDefs() } } - m_ClassDefinitions.purge(); + m_ClassDefinitions.Purge(); } +#define DISASM_DIVIDER_LEN 6 +#define DISASM_MAX_PARAM_NAME_LEN 64 + int SQDebugServer::DisassemblyBufLen( SQClosure *target ) { SQFunctionProto *func = _fp(target->_function); - const int maxParamNameLen = 64; const int buflen = STRLEN("stacksize \n") + FMT_INT_LEN + STRLEN("instructions \n") + FMT_INT_LEN + STRLEN("literals \n") + FMT_INT_LEN + STRLEN("localvarinfos \n") + FMT_INT_LEN + - 6 + 1 + STRLEN("parameters \n") + FMT_INT_LEN + - func->_nparameters * ( maxParamNameLen + 2 ) - 2 + 1 + + DISASM_DIVIDER_LEN + 1 + + func->_nparameters * ( DISASM_MAX_PARAM_NAME_LEN + 2 ) - 2 + 1 + #if SQUIRREL_VERSION_NUMBER > 212 - func->_ndefaultparams * ( maxParamNameLen + 3 ) + + func->_ndefaultparams * ( DISASM_MAX_PARAM_NAME_LEN + 3 ) + #endif - 6 + 1 + + DISASM_DIVIDER_LEN + 1 + func->_ninstructions * ( 6 + 30 + 128 + 1 ) - 1 + 1; @@ -16556,7 +17776,6 @@ int SQDebugServer::DisassemblyBufLen( SQClosure *target ) sqstring_t SQDebugServer::PrintDisassembly( SQClosure *target, SQChar *scratch, int bufsize ) { SQFunctionProto *func = _fp(target->_function); - const int maxParamNameLen = 64; SQChar *buf = scratch; #define _bs (bufsize - (int)((char*)buf - (char*)scratch)) @@ -16591,7 +17810,7 @@ sqstring_t SQDebugServer::PrintDisassembly( SQClosure *target, SQChar *scratch, #undef putint - for ( int i = 6; i--; ) + for ( int i = DISASM_DIVIDER_LEN; i--; ) *buf++ = '-'; *buf++ = '\n'; @@ -16601,7 +17820,7 @@ sqstring_t SQDebugServer::PrintDisassembly( SQClosure *target, SQChar *scratch, const SQObjectPtr ¶m = func->_parameters[i]; Assert( sq_type(param) == OT_STRING ); - int len = min( (int)_string(param)->_len, maxParamNameLen ); + int len = min( (int)_string(param)->_len, DISASM_MAX_PARAM_NAME_LEN ); memcpy( buf, _string(param)->_val, sq_rsl(len) ); buf += len; @@ -16618,9 +17837,7 @@ sqstring_t SQDebugServer::PrintDisassembly( SQClosure *target, SQChar *scratch, case OT_FLOAT: case OT_BOOL: case OT_NULL: - case OT_TABLE: - case OT_ARRAY: - case OT_CLASS: + case OT_STRING: str = GetValue( val, kFS_NoAddr ); break; default: @@ -16631,7 +17848,7 @@ sqstring_t SQDebugServer::PrintDisassembly( SQClosure *target, SQChar *scratch, memcpy( buf, _SC(" = "), sq_rsl(len) ); buf += len; - len = min( str.len, maxParamNameLen - 3 ); + len = min( str.len, DISASM_MAX_PARAM_NAME_LEN - 3 ); #ifdef SQUNICODE UTF8ToSQUnicode( buf, _bs, str.ptr, str.len ); #else @@ -16658,7 +17875,7 @@ sqstring_t SQDebugServer::PrintDisassembly( SQClosure *target, SQChar *scratch, *buf++ = '\n'; - for ( int i = 6; i--; ) + for ( int i = DISASM_DIVIDER_LEN; i--; ) *buf++ = '-'; *buf++ = '\n'; @@ -16725,7 +17942,7 @@ sqstring_t SQDebugServer::PrintDisassembly( SQClosure *target, SQChar *scratch, #ifndef SQDBG_DISABLE_PROFILER CProfiler *SQDebugServer::GetProfiler( HSQUIRRELVM vm ) { - for ( unsigned int i = 0; i < m_Profilers.size(); i++ ) + for ( unsigned int i = 0; i < m_Profilers.Size(); i++ ) { threadprofiler_t &tp = m_Profilers[i]; if ( tp.thread && sq_type(tp.thread->_obj) == OT_THREAD ) @@ -16755,10 +17972,10 @@ void SQDebugServer::ProfSwitchThread( HSQUIRRELVM vm ) { Assert( IsProfilerEnabled() ); - if ( m_Profilers.size() == 0 ) - m_Profilers.reserve(1); + if ( m_Profilers.Size() == 0 ) + m_Profilers.Reserve(1); - for ( unsigned int i = 0; i < m_Profilers.size(); i++ ) + for ( unsigned int i = 0; i < m_Profilers.Size(); i++ ) { threadprofiler_t &tp = m_Profilers[i]; if ( tp.thread && sq_type(tp.thread->_obj) == OT_THREAD ) @@ -16772,12 +17989,12 @@ void SQDebugServer::ProfSwitchThread( HSQUIRRELVM vm ) else { __ObjRelease( tp.thread ); - m_Profilers.remove(i); + m_Profilers.Remove(i); i--; } } - threadprofiler_t &tp = m_Profilers.append(); + threadprofiler_t &tp = m_Profilers.Append(); tp.thread = GetWeakRef( vm ); __ObjAddRef( tp.thread ); @@ -16799,7 +18016,7 @@ void SQDebugServer::ProfStop() if ( !IsClientConnected() ) SetDebugHook( NULL ); - for ( unsigned int i = 0; i < m_Profilers.size(); i++ ) + for ( unsigned int i = 0; i < m_Profilers.Size(); i++ ) { threadprofiler_t &tp = m_Profilers[i]; __ObjRelease( tp.thread ); @@ -16807,7 +18024,7 @@ void SQDebugServer::ProfStop() tp.prof.Stop(); } - m_Profilers.clear(); + m_Profilers.Clear(); m_pProfiler = NULL; m_bProfilerEnabled = false; } @@ -16863,7 +18080,7 @@ sqstring_t SQDebugServer::ProfGets( HSQUIRRELVM vm, SQString *tag, int type ) CProfiler *pProfiler = NULL; - for ( unsigned int i = 0; i < m_Profilers.size(); i++ ) + for ( unsigned int i = 0; i < m_Profilers.Size(); i++ ) { threadprofiler_t &tp = m_Profilers[i]; if ( tp.thread && sq_type(tp.thread->_obj) == OT_THREAD ) @@ -16877,7 +18094,7 @@ sqstring_t SQDebugServer::ProfGets( HSQUIRRELVM vm, SQString *tag, int type ) else { __ObjRelease( tp.thread ); - m_Profilers.remove(i); + m_Profilers.Remove(i); i--; } } @@ -16886,6 +18103,10 @@ sqstring_t SQDebugServer::ProfGets( HSQUIRRELVM vm, SQString *tag, int type ) return { 0, 0 }; const int size = pProfiler->GetMaxOutputLen( tag, type ); + + if ( size <= 0 ) + return { 0, 0 }; + SQChar *buf = _ss(m_pRootVM)->GetScratchPad( sq_rsl(size) ); int len = pProfiler->Output( tag, type, buf, size ); Assert( len >= 0 ); @@ -16938,13 +18159,13 @@ void SQDebugServer::PrintVar( HSQUIRRELVM vm, const SQChar *name, const SQObject SQErrorAtFrame( vm, NULL, _SC("[%s] USERPOINTER\n"), name ); break; case OT_STRING: - SQErrorAtFrame( vm, NULL, _SC("[%s] \"%s\"\n"), name, _string(obj)->_val ); + SQErrorAtFrame( vm, NULL, _SC("[%s] \"%.50s\"\n"), name, _string(obj)->_val ); break; case OT_TABLE: - SQErrorAtFrame( vm, NULL, _SC("[%s] TABLE\n"), name ); + SQErrorAtFrame( vm, NULL, _SC("[%s] TABLE (#" FMT_INT ")\n"), name, _table(obj)->CountUsed() ); break; case OT_ARRAY: - SQErrorAtFrame( vm, NULL, _SC("[%s] ARRAY\n"), name ); + SQErrorAtFrame( vm, NULL, _SC("[%s] ARRAY (#" FMT_INT ")\n"), name, _array(obj)->Size() ); break; case OT_CLOSURE: SQErrorAtFrame( vm, NULL, _SC("[%s] CLOSURE\n"), name ); @@ -16953,8 +18174,20 @@ void SQDebugServer::PrintVar( HSQUIRRELVM vm, const SQChar *name, const SQObject SQErrorAtFrame( vm, NULL, _SC("[%s] NATIVECLOSURE\n"), name ); break; case OT_GENERATOR: - SQErrorAtFrame( vm, NULL, _SC("[%s] GENERATOR\n"), name ); + { + const SQObjectPtr &funcname = _fp(_closure(_generator(obj)->_ci._closure)->_function)->_name; + + if ( sq_type(funcname) == OT_STRING ) + { + SQErrorAtFrame( vm, NULL, _SC("[%s] GENERATOR (%s)\n"), name, _string(funcname)->_val ); + } + else + { + SQErrorAtFrame( vm, NULL, _SC("[%s] GENERATOR\n"), name ); + } + break; + } case OT_USERDATA: SQErrorAtFrame( vm, NULL, _SC("[%s] USERDATA\n"), name ); break; @@ -16962,11 +18195,37 @@ void SQDebugServer::PrintVar( HSQUIRRELVM vm, const SQChar *name, const SQObject SQErrorAtFrame( vm, NULL, _SC("[%s] THREAD\n"), name ); break; case OT_CLASS: - SQErrorAtFrame( vm, NULL, _SC("[%s] CLASS\n"), name ); + { + const classdef_t *def = FindClassDef( _class(obj) ); + + if ( def && def->name.ptr ) + { + SQErrorAtFrame( vm, NULL, _SC("[%s] CLASS (" FMT_CSTR ")\n"), + name, def->name.ptr + FMT_PTR_LEN + 1 ); + } + else + { + SQErrorAtFrame( vm, NULL, _SC("[%s] CLASS\n"), name ); + } + break; + } case OT_INSTANCE: - SQErrorAtFrame( vm, NULL, _SC("[%s] INSTANCE\n"), name ); + { + const classdef_t *def = FindClassDef( _instance(obj)->_class ); + + if ( def && def->name.ptr ) + { + SQErrorAtFrame( vm, NULL, _SC("[%s] INSTANCE (" FMT_CSTR ")\n"), + name, def->name.ptr + FMT_PTR_LEN + 1 ); + } + else + { + SQErrorAtFrame( vm, NULL, _SC("[%s] INSTANCE\n"), name ); + } + break; + } case OT_WEAKREF: PrintVar( vm, name, _weakref(obj)->_obj ); break; @@ -16981,71 +18240,72 @@ void SQDebugServer::PrintStack( HSQUIRRELVM vm ) { SQErrorAtFrame( vm, NULL, _SC("\nCALLSTACK\n") ); - int i = vm->_callsstacksize; - while ( i-- ) - { - const SQVM::CallInfo &ci = vm->_callsstack[i]; + int frame = vm->_callsstacksize; - if ( ShouldIgnoreStackFrame(ci) ) + while ( frame-- ) + { + const SQVM::CallInfo &ci = vm->_callsstack[ frame ]; + + if ( ShouldIgnoreStackFrame( vm, ci ) ) continue; + const SQChar *fn = _SC("??"); + const SQChar *src = _SC("??"); + int line; + if ( sq_type(ci._closure) == OT_CLOSURE ) { SQFunctionProto *func = _fp(_closure(ci._closure)->_function); - const SQChar *fn = _SC("??"); - const SQChar *src = _SC("??"); - int line = func->GetLine( ci._ip ); + line = func->GetLine( ci._ip ); if ( sq_type(func->_name) == OT_STRING ) fn = _string(func->_name)->_val; if ( sq_type(func->_sourcename) == OT_STRING ) src = _string(func->_sourcename)->_val; - - SQErrorAtFrame( vm, &ci, _SC("*FUNCTION [%s()] %s line [%d]\n"), fn, src, line ); } else if ( sq_type(ci._closure) == OT_NATIVECLOSURE ) { SQNativeClosure *closure = _nativeclosure(ci._closure); - const SQChar *fn = _SC("??"); - const SQChar *src = _SC("NATIVE"); - int line = -1; + src = _SC("NATIVE"); + line = -1; if ( sq_type(closure->_name) == OT_STRING ) fn = _string(closure->_name)->_val; - - SQErrorAtFrame( vm, NULL, _SC("*FUNCTION [%s()] %s line [%d]\n"), fn, src, line ); } else UNREACHABLE(); + + SQErrorAtFrame( vm, NULL, _SC("*FUNCTION [%s()] %s line [%d]\n"), fn, src, line ); } SQErrorAtFrame( vm, NULL, _SC("\nLOCALS\n") ); - i = vm->_callsstacksize; - if ( i > 10 ) - i = 10; + frame = vm->_callsstacksize; - while ( i-- ) + if ( frame > 10 ) + frame = 10; + + while ( frame-- ) { - const SQVM::CallInfo &ci = vm->_callsstack[i]; + const SQVM::CallInfo &ci = vm->_callsstack[ frame ]; if ( sq_type(ci._closure) != OT_CLOSURE ) continue; - if ( ShouldIgnoreStackFrame(ci) ) + if ( ShouldIgnoreStackFrame( vm, ci ) ) continue; int stackbase = GetStackBase( vm, &ci ); SQClosure *pClosure = _closure(ci._closure); SQFunctionProto *func = _fp(pClosure->_function); - SQUnsignedInteger ip = (SQUnsignedInteger)( ci._ip - func->_instructions - 1 ); + SQUnsignedInteger ip = ci._ip - func->_instructions; for ( int i = 0; i < func->_nlocalvarinfos; i++ ) { const SQLocalVarInfo &var = func->_localvarinfos[i]; - if ( var._start_op <= ip + 1 && var._end_op >= ip ) + if ( var._start_op <= ip && var._end_op + 1 >= ip ) { PrintVar( vm, _string(var._name)->_val, vm->_stack._vals[ stackbase + var._pos ] ); } @@ -17083,6 +18343,8 @@ void SQDebugServer::ErrorHandler( HSQUIRRELVM vm ) err.Assign( "??" ); } + bool getError = !err.ptr; + // An error handler is required to detect exceptions. // The downside of calling the default error handler instead of // replicating it in the debugger is the extra stack frame and redundant print locations. @@ -17090,20 +18352,16 @@ void SQDebugServer::ErrorHandler( HSQUIRRELVM vm ) #ifdef SQDBG_CALL_DEFAULT_ERROR_HANDLER SQObjectPtr dummy; vm->Call( m_ErrorHandler, 2, vm->_top-2, dummy, SQFalse ); - - if ( !err.ptr ) - err = GetValue( oe, kFS_NoQuote ); #else - bool getError = !err.ptr; if ( getError ) err = GetValue( oe, kFS_NoQuote ); SQErrorAtFrame( vm, NULL, _SC("\nAN ERROR HAS OCCURRED [" FMT_VCSTR "]\n"), STR_EXPAND(err) ); PrintStack( vm ); +#endif if ( getError ) err = GetValue( oe, kFS_NoQuote ); -#endif if ( m_bBreakOnExceptions ) { @@ -17255,18 +18513,18 @@ void SQDebugServer::Continue( HSQUIRRELVM vm ) void SQDebugServer::RemoveReturnValues() { - for ( unsigned int i = 0; i < m_ReturnValues.size(); i++ ) + for ( unsigned int i = 0; i < m_ReturnValues.Size(); i++ ) { returnvalue_t &rv = m_ReturnValues[i]; if ( rv.funcname ) __ObjRelease( rv.funcname ); } - m_ReturnValues.clear(); + m_ReturnValues.Clear(); m_iYieldValues = 0; } -int SQDebugServer::EvalAndWriteExpr( HSQUIRRELVM vm, const SQVM::CallInfo *ci, string_t &expression, +int SQDebugServer::EvalAndWriteExpr( HSQUIRRELVM vm, int frame, string_t &expression, char *buf, int size ) { // Don't modify logMessage @@ -17281,30 +18539,22 @@ int SQDebugServer::EvalAndWriteExpr( HSQUIRRELVM vm, const SQVM::CallInfo *ci, s SQObjectPtr value; #ifndef SQDBG_DISABLE_COMPILER - bool res; + // 'expression' is a substring of breakpoint_t::logMessage + // don't modify it in CCompiler::ParseString + char cpy[512]; + Assert( expression.len <= sizeof(cpy) ); + string_t expr; + expr.Assign( cpy, expression.len ); + memcpy( cpy, expression.ptr, expression.len ); - if ( expression.len <= 256 ) - { - // 'expression' is a substring of breakpoint_t::logMessage - // don't modify its bytes in CCompiler::ParseString - char cpy[256]; - string_t expr; - expr.Assign( cpy, expression.len ); - memcpy( cpy, expression.ptr, expression.len ); + ECompileReturnCode cres = Evaluate( expr, vm, frame, value ); - ECompileReturnCode cres = Evaluate( expr, vm, ci, value ); - res = ( cres == CompileReturnCode_Success || - ( cres > CompileReturnCode_Fallback && RunExpression( expression, vm, ci, value ) ) ); - } - else - { - res = RunExpression( expression, vm, ci, value ); - } - - if ( res ) + if ( cres == CompileReturnCode_Success ) #else objref_t obj; - if ( GetObj_Frame( vm, ci, expression, obj, value ) || RunExpression( expression, vm, ci, value ) ) + + if ( GetObj_Frame( vm, frame, expression, obj, value ) || + RunExpression( expression, vm, frame, value ) ) #endif { if ( comma ) @@ -17323,46 +18573,32 @@ int SQDebugServer::EvalAndWriteExpr( HSQUIRRELVM vm, const SQVM::CallInfo *ci, s return 0; } -// -// Expressions within `{}` are evaluated. -// Escape the opening bracket to print brackets `\{` -// -// Special keywords: $FUNCTION, $CALLER, $HITCOUNT -// void SQDebugServer::TracePoint( breakpoint_t *bp, HSQUIRRELVM vm, int frame ) { char buf[512]; - int bufsize = sizeof(buf) - 2; // \n\0 + const int bufsize = sizeof(buf) - 2; // \n\0 int readlen = min( (int)bp->logMessage.len, bufsize ); char *pWrite = buf; char *logMessage = bp->logMessage.ptr; - // if logMessage is surrounded with \{ and }/, + // if logMessage is surrounded with {/ and }, // evaluate the expression but don't print. // A simple way to inject expression evaluation without side effects // although still limited by print buffer size - bool escapePrint = readlen > 4 && - logMessage[0] == '\\' && - logMessage[1] == '{' && - logMessage[readlen-2] == '}' && - logMessage[readlen-1] == '/'; + bool escapePrint = readlen > 3 && + logMessage[0] == '{' && + logMessage[1] == '/' && + logMessage[readlen-1] == '}'; if ( escapePrint ) - logMessage[0] = 0; + logMessage[1] = ' '; - for ( int iRead = 0; iRead < readlen; iRead++ ) + for ( int iRead = 0; iRead < readlen && pWrite - buf < bufsize; iRead++ ) { switch ( logMessage[iRead] ) { case '{': { - // '\' preceeds '{' - if ( iRead && logMessage[iRead-1] == '\\' ) - { - pWrite[-1] = '{'; - continue; - } - int depth = 1; for ( int j = iRead + 1; j < readlen; j++ ) { @@ -17375,15 +18611,18 @@ void SQDebugServer::TracePoint( breakpoint_t *bp, HSQUIRRELVM vm, int frame ) // Found expression if ( depth == 0 ) { - const SQVM::CallInfo *ci = vm->_callsstack + frame; - string_t expression; expression.Assign( logMessage + iRead + 1, j - iRead - 1 ); - if ( expression.len ) - pWrite += EvalAndWriteExpr( vm, ci, expression, pWrite, bufsize - j ); - iRead = j; + + if ( expression.len ) + { + int remaining = bufsize - ( pWrite - buf ); + int writelen = EvalAndWriteExpr( vm, frame, expression, pWrite, remaining ); + pWrite += writelen; + } + goto exit; } @@ -17396,85 +18635,142 @@ void SQDebugServer::TracePoint( breakpoint_t *bp, HSQUIRRELVM vm, int frame ) } } } - exit:; +exit:; + break; + } + case '\\': + { + if ( iRead + 1 < readlen ) + { + switch ( logMessage[iRead+1] ) + { + case 'n': + { + *pWrite++ = '\n'; + iRead++; + break; + } + case 't': + { + *pWrite++ = '\t'; + iRead++; + break; + } + case '\\': + case '{': + case '$': + { + *pWrite++ = logMessage[iRead+1]; + iRead++; + break; + } + default: + *pWrite++ = '\\'; + } + } + else + { + *pWrite++ = '\\'; + } + break; } case '$': { - if ( iRead && logMessage[iRead-1] == '\\' ) - { - pWrite[-1] = '$'; - continue; - } - - #define STRCMP( s, StrLiteral ) \ - memcmp( (s), (StrLiteral), sizeof(StrLiteral)-1 ) - #define CHECK_KEYWORD(s) \ ( ( iRead + (int)STRLEN(s) < readlen ) && \ - !STRCMP( logMessage + iRead + 1, s ) ) + !memcmp( logMessage + iRead + 1, s, sizeof(s)-1 ) ) if ( CHECK_KEYWORD("FUNCTION") ) { + iRead += STRLEN("FUNCTION"); + const SQVM::CallInfo *ci = vm->_callsstack + frame; - const SQObjectPtr &funcname = _fp(_closure(ci->_closure)->_function)->_name; + SQFunctionProto *func = _fp(_closure(ci->_closure)->_function); - if ( sq_type(funcname) == OT_STRING ) + if ( sq_type(func->_name) == OT_STRING ) { - SQString *name = _string(funcname); + SQString *name = _string(func->_name); - int writelen = scstombs( pWrite, bufsize - iRead, name->_val, name->_len ); + int remaining = bufsize - ( pWrite - buf ); + int writelen = scstombs( pWrite, remaining, name->_val, name->_len ); + pWrite += writelen; + } + else + { + int remaining = bufsize - ( pWrite - buf ); + int writelen = printhex( pWrite, remaining, (uintptr_t)func ); pWrite += writelen; } - iRead += STRLEN("FUNCTION"); break; } else if ( CHECK_KEYWORD("CALLER") ) { - const SQVM::CallInfo *ci = vm->_callsstack + frame; - if ( ci > vm->_callsstack ) + iRead += STRLEN("CALLER"); + + const SQVM::CallInfo *ci = vm->_callsstack + frame - 1; + + if ( ci >= vm->_callsstack ) { - const SQVM::CallInfo *cii = ci - 1; - - if ( sq_type(cii->_closure) == OT_CLOSURE ) + if ( sq_type(ci->_closure) == OT_CLOSURE ) { - if ( sq_type(_fp(_closure(cii->_closure)->_function)->_name) == OT_STRING ) - { - SQString *name = _string(_fp(_closure(cii->_closure)->_function)->_name); + SQFunctionProto *func = _fp(_closure(ci->_closure)->_function); - int writelen = scstombs( pWrite, bufsize - iRead, name->_val, name->_len ); + if ( sq_type(func->_name) == OT_STRING ) + { + SQString *name = _string(func->_name); + + int remaining = bufsize - ( pWrite - buf ); + int writelen = scstombs( pWrite, remaining, name->_val, name->_len ); + pWrite += writelen; + } + else + { + int remaining = bufsize - ( pWrite - buf ); + int writelen = printhex( pWrite, remaining, (uintptr_t)func ); pWrite += writelen; } } - else if ( sq_type(cii->_closure) == OT_NATIVECLOSURE ) + else if ( sq_type(ci->_closure) == OT_NATIVECLOSURE ) { - if ( sq_type(_nativeclosure(cii->_closure)->_name) == OT_STRING ) - { - SQString *name = _string(_nativeclosure(cii->_closure)->_name); + SQNativeClosure *closure = _nativeclosure(ci->_closure); - int writelen = scstombs( pWrite, bufsize - iRead, name->_val, name->_len ); + if ( sq_type(closure->_name) == OT_STRING ) + { + SQString *name = _string(closure->_name); + + int remaining = bufsize - ( pWrite - buf ); + int writelen = scstombs( pWrite, remaining, name->_val, name->_len ); + pWrite += writelen; + } + else + { + int remaining = bufsize - ( pWrite - buf ); + int writelen = printhex( pWrite, remaining, (uintptr_t)closure ); pWrite += writelen; } } else UNREACHABLE(); } - iRead += STRLEN("CALLER"); break; } else if ( CHECK_KEYWORD("HITCOUNT") ) { + iRead += STRLEN("HITCOUNT"); + // lazy hack, hit count was reset after hitting the target // if this count is to ignore hit target, keep trace hit count separately int hits = bp->hits ? bp->hits : bp->hitsTarget; - pWrite += printint( pWrite, bufsize - iRead, hits ); - iRead += STRLEN("HITCOUNT"); + + int remaining = bufsize - ( pWrite - buf ); + int writelen = printint( pWrite, remaining, hits ); + pWrite += writelen; break; } // else fallthrough - #undef STRCMP #undef CHECK_KEYWORD } default: @@ -17484,7 +18780,7 @@ void SQDebugServer::TracePoint( breakpoint_t *bp, HSQUIRRELVM vm, int frame ) if ( escapePrint ) { - logMessage[0] = '\\'; + logMessage[1] = '/'; return; } @@ -17514,7 +18810,7 @@ bool SQDebugServer::CheckBreakpointCondition( breakpoint_t *bp, HSQUIRRELVM vm, sq_type(bp->conditionEnv) != OT_NULL ); SetCallFrame( bp->conditionEnv, vm, ci ); - SetEnvDelegate( bp->conditionEnv, vm->_stack._vals[ GetStackBase( vm, ci ) ] ); + SetEnvDelegate( bp->conditionEnv, vm, ci ); SQObjectPtr res; @@ -17544,8 +18840,8 @@ bool SQDebugServer::CheckBreakpointCondition( breakpoint_t *bp, HSQUIRRELVM vm, #define SQ_HOOK_CALL 'c' #define SQ_HOOK_RETURN 'r' -void SQDebugServer::DebugHook( HSQUIRRELVM vm, SQInteger type, - const SQChar *sourcename, SQInteger line, const SQChar *funcname ) +void SQDebugServer::DebugHook( HSQUIRRELVM vm, int type, + const SQChar *sourcename, int line, const SQChar *funcname ) { Assert( IsClientConnected() ); @@ -17583,7 +18879,7 @@ void SQDebugServer::DebugHook( HSQUIRRELVM vm, SQInteger type, const SQObjectPtr &val = m_pCurVM->_stack._vals[ m_pCurVM->_stackbase + pip->_arg1 ]; if ( sq_type(val) == OT_NATIVECLOSURE && sq_type(_nativeclosure(val)->_name) == OT_STRING && - sqstring_t(_SC("suspend")).IsEqualTo( _string(_nativeclosure(val)->_name) ) ) + IsEqual( _SC("suspend"), _string(_nativeclosure(val)->_name) ) ) { m_nCalls -= (int)( m_pCurVM->ci - m_pCurVM->_callsstack ) + 1; @@ -17629,10 +18925,8 @@ void SQDebugServer::DebugHook( HSQUIRRELVM vm, SQInteger type, { if ( sq_type(m_pCurVM->ci->_closure) == OT_NATIVECLOSURE && sq_type(_nativeclosure(m_pCurVM->ci->_closure)->_name) == OT_STRING && - ( sqstring_t(_SC("wakeup")).IsEqualTo( - _string(_nativeclosure(m_pCurVM->ci->_closure)->_name) ) || - sqstring_t(_SC("call")).IsEqualTo( - _string(_nativeclosure(m_pCurVM->ci->_closure)->_name) ) ) ) + ( IsEqual( _SC("wakeup"), _string(_nativeclosure(m_pCurVM->ci->_closure)->_name) ) || + IsEqual( _SC("call"), _string(_nativeclosure(m_pCurVM->ci->_closure)->_name) ) ) ) { m_nCalls += (int)( vm->ci - vm->_callsstack ) + 1; } @@ -17648,7 +18942,7 @@ void SQDebugServer::DebugHook( HSQUIRRELVM vm, SQInteger type, // NOTE: This isn't reliable, a thread could've been called from repl // profiler is validated on step ( !sourcename || - !sqstring_t(_SC("sqdbg")).IsEqualTo( SQStringFromSQChar( sourcename ) ) ) ) + !IsEqual( _SC("sqdbg"), SQStringFromSQChar( sourcename ) ) ) ) { ProfSwitchThread( vm ); } @@ -17658,14 +18952,14 @@ void SQDebugServer::DebugHook( HSQUIRRELVM vm, SQInteger type, #ifndef SQDBG_DISABLE_PROFILER Assert( !IsProfilerEnabled() || !sourcename || - sqstring_t(_SC("sqdbg")).IsEqualTo( SQStringFromSQChar( sourcename ) ) || + IsEqual( _SC("sqdbg"), SQStringFromSQChar( sourcename ) ) || m_pProfiler == GetProfiler(vm) ); #endif if ( m_pPausedThread == vm && // Ignore repl ( !sourcename || - !sqstring_t(_SC("sqdbg")).IsEqualTo( SQStringFromSQChar( sourcename ) ) )) + !IsEqual( _SC("sqdbg"), SQStringFromSQChar( sourcename ) ) )) { m_pPausedThread = NULL; @@ -17691,6 +18985,8 @@ void SQDebugServer::DebugHook( HSQUIRRELVM vm, SQInteger type, } #endif + Assert( type != SQ_HOOK_RETURN ); + Break( vm, breakreason_t::Pause ); if ( m_State == ThreadState_SuspendNow ) @@ -17999,8 +19295,8 @@ void SQDebugServer::DebugHook( HSQUIRRELVM vm, SQInteger type, if ( funcname ) { - int funclen = SQStringFromSQChar( funcname )->_len; - Assert( (int)scstrlen(funcname) == funclen ); + unsigned int funclen = SQStringFromSQChar( funcname )->_len; + Assert( scstrlen(funcname) == funclen ); func.Assign( funcname, funclen ); } else @@ -18028,7 +19324,7 @@ void SQDebugServer::DebugHook( HSQUIRRELVM vm, SQInteger type, bool bGenerator = ( pFunc->_bgenerator && ci->_ip != pFunc->_instructions && (ci->_ip-1)->op == _OP_YIELD ); int decline = GetFunctionDeclarationLine( pFunc ); - AssertMsg2( line == decline || bGenerator, "unexpected func line %d != %d", (int)line, decline ); + AssertMsg2( line == decline || bGenerator, "unexpected func line %d != %d", line, decline ); } #endif @@ -18076,15 +19372,15 @@ void SQDebugServer::DebugHook( HSQUIRRELVM vm, SQInteger type, } buf.Put('\n'); - buf.Term(); } else { buf.Puts( "(sqdbg) Breakpoint hit " ); buf.Puts( func ); buf.Puts( "()\n" ); - buf.Term(); } + + buf.Term(); } else { @@ -18100,13 +19396,13 @@ void SQDebugServer::DebugHook( HSQUIRRELVM vm, SQInteger type, } buf.Put('\n'); - buf.Term(); } else { buf.Puts( "(sqdbg) Breakpoint hit 'anonymous function'\n" ); - buf.Term(); } + + buf.Term(); } _OutputDebugStringA( buf.ptr ); @@ -18137,7 +19433,7 @@ void SQDebugServer::DebugHook( HSQUIRRELVM vm, SQInteger type, if ( IsProfilerEnabled() && m_pProfiler && m_pProfiler->IsActive() && // Ignore repl ( !sourcename || - !sqstring_t(_SC("sqdbg")).IsEqualTo( SQStringFromSQChar( sourcename ) ) ) ) + !IsEqual( _SC("sqdbg"), SQStringFromSQChar( sourcename ) ) ) ) { SQFunctionProto *func = _fp(_closure(ci->_closure)->_function); bool bGenerator = ( func->_bgenerator && ci->_ip == func->_instructions ); @@ -18177,10 +19473,10 @@ void SQDebugServer::DebugHook( HSQUIRRELVM vm, SQInteger type, #endif const SQObjectPtr &val = vm->_stack._vals[ index ]; - if ( !m_ReturnValues.size() || - !IsEqual( m_ReturnValues.top().value, val ) ) + if ( !m_ReturnValues.Size() || + !IsEqual( m_ReturnValues.Top().value, val ) ) { - returnvalue_t &rv = m_ReturnValues.append(); + returnvalue_t &rv = m_ReturnValues.Append(); rv.value = val; if ( funcname ) @@ -18191,14 +19487,14 @@ void SQDebugServer::DebugHook( HSQUIRRELVM vm, SQInteger type, else { rv.funcname = NULL; - rv.funcptr = (uintptr_t)func; + rv.funcptr = (uintptr_t)_closure(ci->_closure); } if ( (ci->_ip-1)->op == _OP_YIELD && // Keep track of yields up to 32 times at once - m_ReturnValues.size() < ( sizeof(m_iYieldValues) << 3 ) ) + m_ReturnValues.Size() < ( sizeof(m_iYieldValues) << 3 ) ) { - m_iYieldValues |= 1 << m_ReturnValues.size(); + m_iYieldValues |= 1 << m_ReturnValues.Size(); } } } @@ -18210,7 +19506,7 @@ void SQDebugServer::DebugHook( HSQUIRRELVM vm, SQInteger type, } // NOTE: CMP metamethod function call can reallocate call stack - if ( !( m_DataWatches.size() && CheckDataBreakpoints( vm ) ) ) + if ( !( type != SQ_HOOK_RETURN && m_DataWatches.Size() && CheckDataBreakpoints( vm ) ) ) { if ( breakReason.reason ) Break( vm, breakReason ); @@ -18225,7 +19521,7 @@ void SQDebugServer::DebugHook( HSQUIRRELVM vm, SQInteger type, } #ifndef SQDBG_DISABLE_PROFILER -void SQDebugServer::ProfHook( HSQUIRRELVM vm, SQInteger type ) +void SQDebugServer::ProfHook( HSQUIRRELVM vm, int type ) { Assert( !IsClientConnected() ); @@ -18286,7 +19582,7 @@ void SQDebugServer::SendEvent_OutputStdOut( const T &strOutput, const SQVM::Call if ( ci ) { SQFunctionProto *func = _fp(_closure(ci->_closure)->_function); - if ( !sqstring_t(_SC("sqdbg")).IsEqualTo( _string(func->_sourcename) ) ) + if ( !IsEqual( _SC("sqdbg"), _string(func->_sourcename) ) ) { body.SetInt( "line", (int)func->GetLine( ci->_ip ) ); wjson_table_t source = body.SetTable( "source" ); @@ -18336,16 +19632,6 @@ void SQDebugServer::OnSQError( HSQUIRRELVM vm, const SQChar *buf, int len ) } -static inline HSQDEBUGSERVER sqdbg_get( HSQUIRRELVM vm ); -static inline HSQDEBUGSERVER sqdbg_get_debugger( HSQUIRRELVM vm ); -#ifdef NATIVE_DEBUG_HOOK -#ifdef DEBUG_HOOK_CACHED_SQDBG -static inline HSQDEBUGSERVER sqdbg_get_debugger_cached_debughook( HSQUIRRELVM vm ); -#else -#define sqdbg_get_debugger_cached_debughook sqdbg_get_debugger -#endif -#endif - SQInteger SQDebugServer::SQDefineClass( HSQUIRRELVM vm ) { SQDebugServer *dbg = sqdbg_get( vm ); @@ -18630,7 +19916,11 @@ SQInteger SQDebugServer::SQBreak( HSQUIRRELVM vm ) if ( dbg && dbg->IsClientConnected() ) { if ( dbg->m_State != ThreadState_Suspended && - ( !dbg->m_bDebugHookGuard || !dbg->m_bInREPL ) ) + !dbg->m_bDebugHookGuard && + !( dbg->m_bInREPL && + vm->ci - 1 >= vm->_callsstack && + sq_type((vm->ci-1)->_closure) == OT_CLOSURE && + IsEqual( _SC("sqdbg"), _string(_fp(_closure((vm->ci-1)->_closure)->_function)->_sourcename) ) ) ) { dbg->m_pPausedThread = vm; dbg->InstructionStep( vm, vm->ci - 1, 1 ); @@ -18666,6 +19956,9 @@ SQInteger SQDebugServer::SQAddDataBreakpoint( HSQUIRRELVM vm ) { sq_getstackobj( vm, 4, &hits ); Assert( sq_type(hits) == OT_INTEGER ); + + if ( _integer(hits) < 0 ) + _integer(hits) = 0; } else { @@ -18674,36 +19967,43 @@ SQInteger SQDebugServer::SQAddDataBreakpoint( HSQUIRRELVM vm ) } } - if ( _string(expression)->_len > (SQInteger)MAX_DATA_WATCH_NAME_LENGTH ) - return sq_throwerror( vm, _SC("name is too long") ); + unsigned int size = 2 + scstombslen( _string(expression)->_val, _string(expression)->_len ); - stringbuf_t< MAX_DATA_WATCH_BUF_SIZE > bufId; + if ( sq_type(condition) == OT_STRING ) + size += scstombslen( _string(condition)->_val, _string(condition)->_len ); + + // NOTE: Both sqdbg and sq scratch pads are reused within this function call + char *scratch = (char*)dbg->m_ReadBuf.Alloc( size ); + + if ( !scratch ) + size = 0; + + stringbufext_t bufId( scratch, size ); bufId.Put('0'); - bufId.Put(':'); bufId.Puts( _string(expression) ); - stringbuf_t< MAX_DATA_WATCH_BUF_SIZE > cond; + string_t cond( 0, 0 ); if ( sq_type(condition) == OT_STRING ) { - if ( _string(condition)->_len > (SQInteger)MAX_DATA_WATCH_BUF_SIZE ) - return sq_throwerror( vm, _SC("condition is too long") ); - - cond.Puts( _string(condition) ); + cond.ptr = bufId.ptr + bufId.len; + cond.len = scstombs( cond.ptr, size - bufId.len, + _string(condition)->_val, _string(condition)->_len ); } - Assert( vm->ci > vm->_callsstack && + Assert( vm->ci >= vm->_callsstack && sq_type(vm->ci->_closure) == OT_NATIVECLOSURE && _nativeclosure(vm->ci->_closure)->_function == &SQDebugServer::SQAddDataBreakpoint ); int repl = dbg->m_bInREPL && - vm->ci - 1 > vm->_callsstack && + vm->ci - 1 - 1 >= vm->_callsstack && sq_type((vm->ci-1)->_closure) == OT_CLOSURE && - sqstring_t(_SC("sqdbg")).IsEqualTo( - _string(_fp(_closure((vm->ci-1)->_closure)->_function)->_sourcename) ); + IsEqual( _SC("sqdbg"), _string(_fp(_closure((vm->ci-1)->_closure)->_function)->_sourcename) ); int id = dbg->AddDataBreakpoint( vm, vm->ci - 1 - repl, bufId, cond, _integer(hits) ); + dbg->m_ReadBuf.ReleaseTop(); + if ( ISVALID_ID(id) ) { // TODO: Send new breakpoint event when the protocol supports it @@ -18735,7 +20035,12 @@ void SQDebugServer::SQPrint( HSQUIRRELVM vm, const SQChar *fmt, ... ) va_end( va ); if ( len < 0 || len > SQDBG_PRINTBUF_SIZE-1 ) + { len = SQDBG_PRINTBUF_SIZE-1; +#if defined(_MSC_VER) && _MSC_VER < 1900 + buf[len] = 0; +#endif + } _OutputDebugString( buf ); dbg->OnSQPrint( vm, buf, len ); @@ -18755,7 +20060,12 @@ void SQDebugServer::SQError( HSQUIRRELVM vm, const SQChar *fmt, ... ) va_end( va ); if ( len < 0 || len > SQDBG_PRINTBUF_SIZE-1 ) + { len = SQDBG_PRINTBUF_SIZE-1; +#if defined(_MSC_VER) && _MSC_VER < 1900 + buf[len] = 0; +#endif + } _OutputDebugString( buf ); dbg->OnSQError( vm, buf, len ); @@ -18776,7 +20086,12 @@ void SQDebugServer::SQErrorAtFrame( HSQUIRRELVM vm, const SQVM::CallInfo *ci, co va_end( va ); if ( len < 0 || len > SQDBG_PRINTBUF_SIZE-1 ) + { len = SQDBG_PRINTBUF_SIZE-1; +#if defined(_MSC_VER) && _MSC_VER < 1900 + buf[len] = 0; +#endif + } _OutputDebugString( buf ); dbg->m_PrintError( vm, buf ); @@ -18809,6 +20124,7 @@ void SQDebugServer::SQDebugHook( HSQUIRRELVM vm, SQInteger type, // Check IsClientConnected here to catch those threads. if ( dbg->IsClientConnected() ) { + Assert( type <= INT_MAX && line <= INT_MAX ); dbg->DebugHook( vm, type, sourcename, line, funcname ); } else @@ -18843,6 +20159,7 @@ SQInteger SQDebugServer::SQDebugHook( HSQUIRRELVM vm ) const SQChar *src = sq_type(sourcename) == OT_STRING ? _string(sourcename)->_val : NULL; const SQChar *fun = sq_type(funcname) == OT_STRING ? _string(funcname)->_val : NULL; + Assert( _integer(type) <= INT_MAX && _integer(line) <= INT_MAX ); dbg->DebugHook( vm, _integer(type), src, _integer(line), fun ); } else @@ -18868,8 +20185,9 @@ void SQDebugServer::SQProfHook( HSQUIRRELVM vm, SQInteger type, { // Rare case, client disconnected while waiting for repl response if ( !sourcename || - !sqstring_t(_SC("sqdbg")).IsEqualTo( SQStringFromSQChar( sourcename ) ) ) + !IsEqual( _SC("sqdbg"), SQStringFromSQChar( sourcename ) ) ) { + Assert( type <= INT_MAX ); dbg->ProfHook( vm, type ); } } @@ -18891,8 +20209,9 @@ SQInteger SQDebugServer::SQProfHook( HSQUIRRELVM vm ) HSQOBJECT src; sq_getstackobj( vm, -3 - 1, &src ); // Rare case, client disconnected while waiting for repl response - if ( sq_type(src) != OT_STRING || !sqstring_t(_SC("sqdbg")).IsEqualTo( _string(src) ) ) + if ( sq_type(src) != OT_STRING || !IsEqual( _SC("sqdbg"), _string(src) ) ) { + Assert( _integer(type) <= INT_MAX ); dbg->ProfHook( vm, _integer(type) ); } } @@ -18905,9 +20224,8 @@ SQInteger SQDebugServer::SQProfHook( HSQUIRRELVM vm ) #define SQDBG_SV_TAG "__sqdbg__" -class CDebuggerScriptRef +struct CDebuggerScriptRef { -public: SQDebugServer *dbg; }; @@ -19032,9 +20350,7 @@ HSQDEBUGSERVER sqdbg_attach_debugger( HSQUIRRELVM vm ) sq_setreleasehook( vm, -1, &OnSQVMShutdown ); sq_newslot( vm, -3, SQFalse ); - SQObjectPtr o; - sqdbg_get_debugger_ref( vm, o ); - *ppRef = o; + sqdbg_get_debugger_ref( vm, *ppRef ); sq_poptop( vm ); } @@ -19087,8 +20403,14 @@ void sqdbg_frame( HSQDEBUGSERVER dbg ) dbg->Frame(); } -void sqdbg_on_script_compile( HSQDEBUGSERVER dbg, const SQChar *script, SQInteger size, +void sqdbg_on_script_compile( HSQDEBUGSERVER dbg, + const SQChar *script, SQInteger scriptlen, const SQChar *sourcename, SQInteger sourcenamelen ) { - dbg->OnScriptCompile( script, size, sourcename, sourcenamelen ); + dbg->OnScriptCompile( script, scriptlen, sourcename, sourcenamelen ); +} + +int sqdbg_is_client_connected( HSQDEBUGSERVER dbg ) +{ + return dbg->IsClientConnected(); } diff --git a/sp/src/vscript/sqdbg/sqdbg/str.h b/sp/src/vscript/sqdbg/sqdbg/str.h index 0274159c..e68b8866 100644 --- a/sp/src/vscript/sqdbg/sqdbg/str.h +++ b/sp/src/vscript/sqdbg/sqdbg/str.h @@ -76,12 +76,19 @@ template < typename I > bool atoo( string_t str, I *out ); -#ifdef SQUNICODE -void CopyString( const string_t &src, sqstring_t *dst ); -void CopyString( const sqstring_t &src, string_t *dst ); -#endif -template < typename T > void CopyString( const T &src, T *dst ); -template < typename T > void FreeString( T *dst ); +#define _isdigit( c ) \ + IN_RANGE_CHAR( c, '0', '9' ) + +#define _isxdigit( c ) \ + ( IN_RANGE_CHAR( c, '0', '9' ) || \ + IN_RANGE_CHAR( c, 'A', 'F' ) || \ + IN_RANGE_CHAR( c, 'a', 'f' ) ) + +#define _isalpha( c ) \ + ( IN_RANGE_CHAR( c, 'A', 'Z' ) || IN_RANGE_CHAR( c, 'a', 'z' ) ) + +#define _isalnum( c ) \ + ( _isalpha(c) || _isdigit(c) ) #define IN_RANGE(c, min, max) \ ((uint32_t)((uint32_t)(c) - (uint32_t)(min)) <= (uint32_t)((max)-(min))) @@ -249,51 +256,21 @@ struct string_t } #endif - template < int size > - string_t( const char (&src)[size] ) : + template < int SIZE > + string_t( const char (&src)[SIZE] ) : ptr((char*)src), - len(size-1) + len(SIZE-1) { // input wasn't a string literal, // call ( src, size ) constructor instead Assert( strlen(src) == len ); } - void Strip() + template < int SIZE > + bool StartsWith( const char (&other)[SIZE] ) const { - char *end = ptr + len; - - for ( char *c = ptr; c < end; c++ ) - { - if ( *c == ' ' || *c == '\t' || *c == '\n' ) - { - ptr++; - len--; - } - else - { - break; - } - } - - for ( char *c = end - 1; c >= ptr; c-- ) - { - if ( *c == ' ' || *c == '\t' || *c == '\n' ) - { - len--; - } - else - { - break; - } - } - } - - template < int size > - bool StartsWith( const char (&other)[size] ) const - { - if ( size-1 <= len && *ptr == *other ) - return !memcmp( ptr, other, size-1 ); + if ( SIZE-1 <= len && *ptr == *other ) + return !memcmp( ptr, other, SIZE-1 ); return false; } @@ -306,11 +283,11 @@ struct string_t return false; } - template < int size > - bool IsEqualTo( const char (&other)[size] ) const + template < int SIZE > + bool IsEqualTo( const char (&other)[SIZE] ) const { - if ( size-1 == len && *ptr == *other ) - return !memcmp( ptr, other, size-1 ); + if ( SIZE-1 == len && *ptr == *other ) + return !memcmp( ptr, other, SIZE-1 ); return false; } @@ -333,6 +310,14 @@ struct string_t #ifdef SQUNICODE bool IsEqualTo( const sqstring_t &other ) const; +#else + bool IsEqualTo( const SQString *other ) const + { + if ( (SQUnsignedInteger)len == (SQUnsignedInteger)other->_len && *ptr == *other->_val ) + return !memcmp( ptr, other->_val, sq_rsl(len) ); + + return false; + } #endif bool IsEmpty() const @@ -345,11 +330,11 @@ struct string_t return ( memchr( ptr, ch, len ) != NULL ); } - template < int size > - void Assign( const char (&src)[size] ) + template < int SIZE > + void Assign( const char (&src)[SIZE] ) { ptr = (char*)src; - len = size - 1; + len = SIZE - 1; Assert( strlen(src) == len ); } @@ -373,6 +358,14 @@ private: string_t &operator=( const char *src ); }; +struct conststring_t : string_t +{ + template < int SIZE > + conststring_t( const char (&src)[SIZE] ) : string_t(src) {} + + conststring_t() {} +}; + #ifdef SQUNICODE struct sqstring_t { @@ -393,10 +386,10 @@ struct sqstring_t { } - template < int size > - sqstring_t( const SQChar (&src)[size] ) : + template < int SIZE > + sqstring_t( const SQChar (&src)[SIZE] ) : ptr((SQChar*)src), - len(size-1) + len(SIZE-1) { Assert( scstrlen(src) == len ); } @@ -434,7 +427,7 @@ struct sqstring_t bool IsEqualTo( const SQString *other ) const { - if ( len == other->_len && *ptr == *other->_val ) + if ( (SQUnsignedInteger)len == (SQUnsignedInteger)other->_len && *ptr == *other->_val ) return !memcmp( ptr, other->_val, sq_rsl(len) ); return false; @@ -445,11 +438,11 @@ struct sqstring_t return !len; } - template < int size > - void Assign( const SQChar (&src)[size] ) + template < int SIZE > + void Assign( const SQChar (&src)[SIZE] ) { ptr = (SQChar*)src; - len = size - 1; + len = SIZE - 1; Assert( scstrlen(src) == len ); } @@ -471,12 +464,12 @@ struct stringbufbase_t { char *ptr; unsigned int len; - const int size; + const unsigned int size; - stringbufbase_t( char *src, unsigned int size ) : + stringbufbase_t( char *src, unsigned int nSize ) : ptr(src), len(0), - size(size) + size(nSize) { } @@ -622,20 +615,6 @@ struct stringbuf_t : stringbufbase_t } }; -#define _isdigit( c ) \ - IN_RANGE_CHAR(c, '0', '9') - -#define _isxdigit( c ) \ - ( IN_RANGE_CHAR(c, '0', '9') || \ - IN_RANGE_CHAR(c, 'A', 'F') || \ - IN_RANGE_CHAR(c, 'a', 'f') ) - -#define _isalpha( c ) \ - ( IN_RANGE_CHAR(c, 'A', 'Z') || IN_RANGE_CHAR(c, 'a', 'z') ) - -#define _isalnum( c ) \ - ( _isalpha(c) || _isdigit(c) ) - template < int BASE = 10, typename I > inline int countdigits( I input ) { @@ -789,7 +768,7 @@ inline bool atoi( string_t str, I *out ) { unsigned char ch = *str.ptr; - if ( IN_RANGE_CHAR(ch, '0', '9') ) + if ( IN_RANGE_CHAR( ch, '0', '9' ) ) { val *= 10; val += ch - '0'; @@ -820,17 +799,17 @@ inline bool atox( string_t str, I *out ) { unsigned char ch = *str.ptr; - if ( IN_RANGE_CHAR(ch, '0', '9') ) + if ( IN_RANGE_CHAR( ch, '0', '9' ) ) { val <<= 4; val += ch - '0'; } - else if ( IN_RANGE_CHAR(ch, 'A', 'F') ) + else if ( IN_RANGE_CHAR( ch, 'A', 'F' ) ) { val <<= 4; val += ch - 'A' + 10; } - else if ( IN_RANGE_CHAR(ch, 'a', 'f') ) + else if ( IN_RANGE_CHAR( ch, 'a', 'f' ) ) { val <<= 4; val += ch - 'a' + 10; @@ -855,7 +834,7 @@ inline bool atoo( string_t str, I *out ) { unsigned char ch = *str.ptr; - if ( IN_RANGE_CHAR(ch, '0', '7') ) + if ( IN_RANGE_CHAR( ch, '0', '7' ) ) { val <<= 3; val += ch - '0'; @@ -898,7 +877,7 @@ inline int IsValidUTF8( unsigned char *src, unsigned int srclen ) return 0; } - else if ( IN_RANGE_CHAR(cp, 0xC2, 0xF4) ) + else if ( IN_RANGE_CHAR( cp, 0xC2, 0xF4 ) ) { if ( UTF8_2_LEAD(cp) ) { @@ -938,7 +917,7 @@ inline int IsValidUTF8( unsigned char *src, unsigned int srclen ) if ( !UTF8_TRAIL(cp) ) { - if ( IN_RANGE_CHAR(cp, 0xC2, 0xF4) ) + if ( IN_RANGE_CHAR( cp, 0xC2, 0xF4 ) ) goto check; return 0; @@ -967,34 +946,8 @@ inline int IsValidUnicode( const SQChar *src, unsigned int srclen ) return 0; } - else if ( cp <= 0xFF ) + else if ( cp < 0xA0 ) { - if ( IN_RANGE(cp, 0xC2, 0xF4) ) - { - if ( UTF8_2_LEAD(cp) ) - { - if ( UTF8_2( srclen, cp, src ) ) - { - return 2; - } - } - else if ( UTF8_3_LEAD(cp) ) - { - if ( UTF8_3( srclen, cp, src ) ) - { - return 3; - } - } - else if ( UTF8_4_LEAD(cp) ) - { - if ( UTF8_4( srclen, cp, src ) ) - { - return 4; - } - } - } - // else [0x7F, 0xC2) & (0xF4, 0xFF] - return 0; } @@ -1041,18 +994,21 @@ inline unsigned int UTF8ToSQUnicode( SQChar *dst, unsigned int destSize, const c { switch ( ((unsigned char*)src)[1] ) { - case '\"': cp = '\"'; src++; break; case '\\': src++; break; + case '\"': cp = '\"'; src++; break; + case '\'': cp = '\''; src++; break; + case 'a': cp = '\a'; src++; break; case 'b': cp = '\b'; src++; break; case 'f': cp = '\f'; src++; break; case 'n': cp = '\n'; src++; break; case 'r': cp = '\r'; src++; break; case 't': cp = '\t'; src++; break; + case 'v': cp = '\v'; src++; break; case 'x': { if ( src + sizeof(SQChar) * 2 + 1 < end ) { - Verify( atox( { src + 2, sizeof(SQChar) * 2 }, &cp ) ); + atox( { src + 2, sizeof(SQChar) * 2 }, &cp ); src += sizeof(SQChar) * 2 + 1; } @@ -1062,7 +1018,7 @@ inline unsigned int UTF8ToSQUnicode( SQChar *dst, unsigned int destSize, const c { if ( src + sizeof(uint16_t) * 2 + 1 < end ) { - Verify( atox( { src + 2, sizeof(uint16_t) * 2 }, &cp ) ); + atox( { src + 2, sizeof(uint16_t) * 2 }, &cp ); src += sizeof(uint16_t) * 2 + 1; } @@ -1074,7 +1030,7 @@ inline unsigned int UTF8ToSQUnicode( SQChar *dst, unsigned int destSize, const c goto xffff; } - else if ( IN_RANGE(cp, 0xC2, 0xF4) ) + else if ( IN_RANGE( cp, 0xC2, 0xF4 ) ) { if ( UTF8_2_LEAD(cp) ) { @@ -1111,34 +1067,13 @@ inline unsigned int UTF8ToSQUnicode( SQChar *dst, unsigned int destSize, const c goto xffff; } +xffff: #if WCHAR_SIZE == 4 -xffff: supplementary: +#endif if ( dst ) { - if ( destSize >= sizeof(SQChar) ) - { - *dst++ = cp; - destSize -= sizeof(SQChar); - count += 1; - } - else - { - // out of space - break; - } - } - else - { - count += 1; - } - - continue; -#else // WCHAR_SIZE == 2 -xffff: - if ( dst ) - { - if ( destSize >= sizeof(SQChar) ) + if ( sizeof(SQChar) <= destSize ) { *dst++ = (SQChar)cp; destSize -= sizeof(SQChar); @@ -1157,14 +1092,15 @@ xffff: continue; +#if WCHAR_SIZE == 2 supplementary: if ( dst ) { - if ( destSize > sizeof(SQChar) ) + if ( sizeof(SQChar) * 2 <= destSize ) { UTF16_SURROGATE_FROM_UTF32( dst, cp ); dst += 2; - destSize -= 2 * sizeof(SQChar); + destSize -= sizeof(SQChar) * 2; count += 2; } else @@ -1241,6 +1177,7 @@ inline unsigned int SQUnicodeToUTF8( char *dst, unsigned int destSize, const SQC switch ( cp ) { + case '\\': case '\"': if ( escape == kUTFEscapeQuoted ) { @@ -1248,16 +1185,15 @@ inline unsigned int SQUnicodeToUTF8( char *dst, unsigned int destSize, const SQC mbc[bytes++] = '\\'; } mbc[bytes++] = '\\'; - mbc[bytes++] = '\"'; + mbc[bytes++] = (unsigned char)cp; goto write; - case '\\': + case '\a': + if ( escape == kUTFEscapeJSON ) + goto doescape; if ( escape == kUTFEscapeQuoted ) - { mbc[bytes++] = '\\'; - mbc[bytes++] = '\\'; - } - mbc[bytes++] = '\\'; mbc[bytes++] = '\\'; + mbc[bytes++] = 'a'; goto write; case '\b': if ( escape == kUTFEscapeQuoted ) @@ -1289,15 +1225,25 @@ inline unsigned int SQUnicodeToUTF8( char *dst, unsigned int destSize, const SQC mbc[bytes++] = '\\'; mbc[bytes++] = 't'; goto write; + case '\v': + if ( escape == kUTFEscapeJSON ) + goto doescape; + if ( escape == kUTFEscapeQuoted ) + mbc[bytes++] = '\\'; + mbc[bytes++] = '\\'; + mbc[bytes++] = 'v'; + goto write; default: - - if ( !IN_RANGE_CHAR(cp, 0x20, 0x7E) ) + if ( !IN_RANGE_CHAR( cp, 0x20, 0x7E ) ) { - // While UTF8 bytes are valid UTF16, converting them will - // make distinct SQ strings indistinguishable to the client -#ifndef SQDBG_ESCAPE_UTF8_BYTES_IN_UTF16 - if ( IN_RANGE(cp, 0xC2, 0xF4) ) + // Convert UTF8 bytes in UTF16 by default with the assumption of + // most editors using UTF8 without BOM, + // and files being likely read plain (no conversion/ISO 8859-1) + // However, this will make certain distinct SQ strings (e.g. "\xC3\xBC", "\xFC") + // indistinguishable to the client +#ifndef SQDBG_DONT_CONVERT_UTF8_BYTES_IN_UTF16 + if ( IN_RANGE( cp, 0xC2, 0xF4 ) ) { if ( UTF8_2_LEAD(cp) ) { @@ -1337,7 +1283,12 @@ inline unsigned int SQUnicodeToUTF8( char *dst, unsigned int destSize, const SQC } } #endif - // [0x7F, 0xC2) & (0xF4, 0xFF] + + if ( cp >= 0xA0 ) // [0xA0, 0xFF] + goto x7ff; + +doescape: + // [0x00, 0x20) & (0x7E, 0xA0) if ( escape == kUTFEscapeQuoted ) mbc[bytes++] = '\\'; @@ -1364,6 +1315,7 @@ inline unsigned int SQUnicodeToUTF8( char *dst, unsigned int destSize, const SQC } else if ( cp <= 0x7FF ) { +x7ff: UTF8_2_FROM_UTF32( mbc, cp ); bytes = 2; } @@ -1478,4 +1430,33 @@ write: } #endif +#if defined(SQUNICODE) && !defined(_WIN32) +// Do case insensitive comparison for ASCII characters, ignore the rest +inline int sqdbg_wcsicmp( const SQChar *s1, const SQChar *s2 ) +{ + for (;;) + { + SQChar c1 = *s1++; + SQChar c2 = *s2++; + + if ( !c1 || !c2 ) + return c1 - c2; + + if ( c1 == c2 ) + continue; + + if ( c1 >= 'A' && c1 <= 'Z' ) + c1 |= 0x20; + + if ( c2 >= 'A' && c2 <= 'Z' ) + c2 |= 0x20; + + if ( c1 == c2 ) + continue; + + return c1 - c2; + } +} +#endif + #endif // SQDBG_STRING_H diff --git a/sp/src/vscript/sqdbg/sqdbg/vec.h b/sp/src/vscript/sqdbg/sqdbg/vec.h index 22c8fe50..a6073acb 100644 --- a/sp/src/vscript/sqdbg/sqdbg/vec.h +++ b/sp/src/vscript/sqdbg/sqdbg/vec.h @@ -111,7 +111,44 @@ public: } }; -template< int MEM_CACHE_CHUNKS_ALIGN = 2048 > +// GCC requires this to be outside of the class +template < bool S > +struct _CScratch_members; + +template <> +struct _CScratch_members< true > +{ + int m_LastFreeChunk; + int m_LastFreeIndex; + int m_PrevChunk; + int m_PrevIndex; + + int LastFreeChunk() { return m_LastFreeChunk; } + int LastFreeIndex() { return m_LastFreeIndex; } + int PrevChunk() { return m_PrevChunk; } + int PrevIndex() { return m_PrevIndex; } + + void SetLastFreeChunk( int i ) { m_LastFreeChunk = i; } + void SetLastFreeIndex( int i ) { m_LastFreeIndex = i; } + void SetPrevChunk( int i ) { m_PrevChunk = i; } + void SetPrevIndex( int i ) { m_PrevIndex = i; } +}; + +template <> +struct _CScratch_members< false > +{ + int LastFreeChunk() { return 0; } + int LastFreeIndex() { return 0; } + int PrevChunk() { return 0; } + int PrevIndex() { return 0; } + + void SetLastFreeChunk( int ) {} + void SetLastFreeIndex( int ) {} + void SetPrevChunk( int ) {} + void SetPrevIndex( int ) {} +}; + +template< bool SEQUENTIAL, int MEM_CACHE_CHUNKS_ALIGN = 2048 > class CScratch { public: @@ -126,8 +163,7 @@ public: chunk_t *m_Memory; int m_MemChunkCount; - int m_LastFreeChunk; - int m_LastFreeIndex; + _CScratch_members< SEQUENTIAL > m; char *Get( int index ) { @@ -145,7 +181,7 @@ public: return &chunk->ptr[ msgIdx * MEM_CACHE_CHUNKSIZE ]; } - char *Alloc( int size, int *index = NULL, bool sequential = true ) + char *Alloc( int size, int *index = NULL ) { if ( !m_Memory ) { @@ -166,11 +202,11 @@ public: int chunkIdx; int matchedChunks = 0; - if ( sequential ) + if ( SEQUENTIAL ) { requiredChunks = ( size - 1 ) / MEM_CACHE_CHUNKSIZE + 1; - msgIdx = m_LastFreeIndex; - chunkIdx = m_LastFreeChunk; + msgIdx = m.LastFreeIndex(); + chunkIdx = m.LastFreeChunk(); } else { @@ -184,14 +220,17 @@ public: chunk_t *chunk = &m_Memory[ chunkIdx ]; Assert( chunk->count && chunk->ptr ); - if ( sequential ) + if ( SEQUENTIAL ) { int remainingChunks = chunk->count - msgIdx; if ( remainingChunks >= requiredChunks ) { - m_LastFreeIndex = msgIdx + requiredChunks; - m_LastFreeChunk = chunkIdx; + m.SetPrevChunk( m.LastFreeChunk() ); + m.SetPrevIndex( m.LastFreeIndex() ); + + m.SetLastFreeIndex( msgIdx + requiredChunks ); + m.SetLastFreeChunk( chunkIdx ); if ( index ) { @@ -267,6 +306,7 @@ public: void Free( void *ptr ) { + Assert( !SEQUENTIAL ); Assert( m_Memory ); Assert( ptr ); @@ -290,7 +330,8 @@ public: Assert( found ); - (*(unsigned char**)&ptr)[ *(int*)ptr + sizeof(int) - 1 ] = 0xdd; + if ( *(int*)ptr ) + (*(unsigned char**)&ptr)[ *(int*)ptr + sizeof(int) - 1 ] = 0xdd; #endif memset( (char*)ptr, 0, *(int*)ptr + sizeof(int) ); @@ -315,12 +356,16 @@ public: m_Memory = NULL; m_MemChunkCount = 4; - m_LastFreeChunk = 0; - m_LastFreeIndex = 0; + m.SetLastFreeChunk( 0 ); + m.SetLastFreeIndex( 0 ); + m.SetPrevChunk( 0 ); + m.SetPrevIndex( 0 ); } void ReleaseShrink() { + Assert( SEQUENTIAL ); + if ( !m_Memory ) return; @@ -359,13 +404,17 @@ public: AssertOOM( m_Memory, m_MemChunkCount * sizeof(chunk_t) ); } - m_LastFreeChunk = 0; - m_LastFreeIndex = 0; + m.SetLastFreeChunk( 0 ); + m.SetLastFreeIndex( 0 ); + m.SetPrevChunk( 0 ); + m.SetPrevIndex( 0 ); } void Release() { - if ( !m_Memory || ( !m_LastFreeChunk && !m_LastFreeIndex ) ) + Assert( SEQUENTIAL ); + + if ( !m_Memory || ( !m.LastFreeChunk() && !m.LastFreeIndex() ) ) return; #ifdef _DEBUG @@ -380,8 +429,18 @@ public: } #endif - m_LastFreeChunk = 0; - m_LastFreeIndex = 0; + m.SetLastFreeChunk( 0 ); + m.SetLastFreeIndex( 0 ); + m.SetPrevChunk( 0 ); + m.SetPrevIndex( 0 ); + } + + void ReleaseTop() + { + Assert( SEQUENTIAL ); + + m.SetLastFreeChunk( m.PrevChunk() ); + m.SetLastFreeIndex( m.PrevIndex() ); } }; @@ -391,212 +450,212 @@ class vector public: typedef unsigned int I; - CAllocator _base; - I _size; + CAllocator base; + I size; - vector() : _base(), _size(0) + vector() : base(), size(0) { Assert( !bExternalMem ); } - vector( CAllocator &a ) : _base(a), _size(0) + vector( CAllocator &a ) : base(a), size(0) { Assert( bExternalMem ); } - vector( I count ) : _base(), _size(0) + vector( I count ) : base(), size(0) { Assert( !bExternalMem ); - _base.Alloc( count * sizeof(T) ); + base.Alloc( count * sizeof(T) ); } - vector( const vector< T > &src ) : _base() + vector( const vector< T > &src ) : base() { Assert( !bExternalMem ); - _base.Alloc( src._base.Size() ); - _size = src._size; + base.Alloc( src.base.Size() ); + size = src.size; - for ( I i = 0; i < _size; i++ ) - new( &_base[ i * sizeof(T) ] ) T( (T&)src._base[ i * sizeof(T) ] ); + for ( I i = 0; i < size; i++ ) + new( &base[ i * sizeof(T) ] ) T( (T&)src.base[ i * sizeof(T) ] ); } ~vector() { - Assert( (unsigned int)_size <= _base.Size() ); + Assert( (unsigned int)size <= base.Size() ); - for ( I i = 0; i < _size; i++ ) - ((T&)(_base[ i * sizeof(T) ])).~T(); + for ( I i = 0; i < size; i++ ) + ((T&)(base[ i * sizeof(T) ])).~T(); if ( !bExternalMem ) - _base.Free(); + base.Free(); } T &operator[]( I i ) const { - Assert( _size > 0 ); - Assert( i >= 0 && i < _size ); - Assert( _size * sizeof(T) <= _base.Size() ); - return (T&)_base[ i * sizeof(T) ]; + Assert( size > 0 ); + Assert( i >= 0 && i < size ); + Assert( size * sizeof(T) <= base.Size() ); + return (T&)base[ i * sizeof(T) ]; } - T *base() + T *Base() { - return _base.Base(); + return base.Base(); } - I size() const + I Size() const { - return _size; + return size; } - I capacity() const + I Capacity() const { - return _base.Size() / sizeof(T); + return base.Size() / sizeof(T); } - T &top() const + T &Top() const { - Assert( _size > 0 ); - return (T&)_base[ ( _size - 1 ) * sizeof(T) ]; + Assert( size > 0 ); + return (T&)base[ ( size - 1 ) * sizeof(T) ]; } - void pop() + void Pop() { - Assert( _size > 0 ); - ((T&)_base[ --_size * sizeof(T) ]).~T(); + Assert( size > 0 ); + ((T&)base[ --size * sizeof(T) ]).~T(); } - T &append() + T &Append() { - _base.Ensure( ++_size * sizeof(T) ); - Assert( _size * sizeof(T) <= _base.Size() ); - return *( new( &_base[ ( _size - 1 ) * sizeof(T) ] ) T() ); + base.Ensure( ++size * sizeof(T) ); + Assert( size * sizeof(T) <= base.Size() ); + return *( new( &base[ ( size - 1 ) * sizeof(T) ] ) T() ); } - void append( const T &src ) + void Append( const T &src ) { - _base.Ensure( ++_size * sizeof(T) ); - Assert( _size * sizeof(T) <= _base.Size() ); - new( &_base[ ( _size - 1 ) * sizeof(T) ] ) T( src ); + base.Ensure( ++size * sizeof(T) ); + Assert( size * sizeof(T) <= base.Size() ); + new( &base[ ( size - 1 ) * sizeof(T) ] ) T( src ); } - T &insert( I i ) + T &Insert( I i ) { - Assert( i >= 0 && i <= _size ); + Assert( i >= 0 && i <= size ); - _base.Ensure( ++_size * sizeof(T) ); - Assert( _size * sizeof(T) <= _base.Size() ); + base.Ensure( ++size * sizeof(T) ); + Assert( size * sizeof(T) <= base.Size() ); - if ( i != _size - 1 ) + if ( i != size - 1 ) { - memmove( &_base[ ( i + 1 ) * sizeof(T) ], - &_base[ i * sizeof(T) ], - ( _size - ( i + 1 ) ) * sizeof(T) ); + memmove( &base[ ( i + 1 ) * sizeof(T) ], + &base[ i * sizeof(T) ], + ( size - ( i + 1 ) ) * sizeof(T) ); } - return *( new( &_base[ i * sizeof(T) ] ) T() ); + return *( new( &base[ i * sizeof(T) ] ) T() ); } - void remove( I i ) + void Remove( I i ) { - Assert( _size > 0 ); - Assert( i >= 0 && i < _size ); + Assert( size > 0 ); + Assert( i >= 0 && i < size ); - ((T&)_base[ i * sizeof(T) ]).~T(); + ((T&)base[ i * sizeof(T) ]).~T(); - if ( i != _size - 1 ) + if ( i != size - 1 ) { - memmove( &_base[ i * sizeof(T) ], - &_base[ ( i + 1 ) * sizeof(T) ], - ( _size - ( i + 1 ) ) * sizeof(T) ); + memmove( &base[ i * sizeof(T) ], + &base[ ( i + 1 ) * sizeof(T) ], + ( size - ( i + 1 ) ) * sizeof(T) ); } - _size--; + size--; } - void clear() + void Clear() { - for ( I i = 0; i < _size; i++ ) - ((T&)_base[ i * sizeof(T) ]).~T(); + for ( I i = 0; i < size; i++ ) + ((T&)base[ i * sizeof(T) ]).~T(); - _size = 0; + size = 0; } - void sort( int (*fn)(const T *, const T *) ) + void Sort( int (*fn)(const T *, const T *) ) { - Assert( _size * sizeof(T) <= _base.Size() ); + Assert( size * sizeof(T) <= base.Size() ); - if ( _size > 1 ) + if ( size > 1 ) { - qsort( _base.Base(), _size, sizeof(T), (int (*)(const void *, const void *))fn ); + qsort( base.Base(), size, sizeof(T), (int (*)(const void *, const void *))fn ); } } - void reserve( I count ) + void Reserve( I count ) { - Assert( (unsigned int)_size <= _base.Size() ); + Assert( (unsigned int)size <= base.Size() ); if ( count == 0 ) count = 4; - if ( (unsigned int)count == _base.Size() ) + if ( (unsigned int)count == base.Size() ) return; - for ( I i = count; i < _size; i++ ) - ((T&)_base[ i * sizeof(T) ]).~T(); + for ( I i = count; i < size; i++ ) + ((T&)base[ i * sizeof(T) ]).~T(); - _base.Alloc( count * sizeof(T) ); + base.Alloc( count * sizeof(T) ); } - void purge() + void Purge() { - Assert( _size * sizeof(T) <= _base.Size() ); + Assert( size * sizeof(T) <= base.Size() ); - for ( I i = 0; i < _size; i++ ) - ((T&)_base[ i * sizeof(T) ]).~T(); + for ( I i = 0; i < size; i++ ) + ((T&)base[ i * sizeof(T) ]).~T(); - _base.Free(); - _size = 0; + base.Free(); + size = 0; } }; class CBuffer { public: - CMemory _base; - int _size; - int _offset; + CMemory base; + int size; + int offset; - char *base() + char *Base() { - return _base.Base() + _offset; + return base.Base() + offset; } - int size() const + int Size() const { - return _size; + return size; } - int capacity() const + int Capacity() const { - return _base.Size(); + return base.Size(); } - void reserve( int count ) + void Reserve( int count ) { - Assert( (unsigned int)_size <= _base.Size() ); + Assert( (unsigned int)size <= base.Size() ); - if ( (unsigned int)count == _base.Size() ) + if ( (unsigned int)count == base.Size() ) return; - _base.Alloc( count ); + base.Alloc( count ); } - void purge() + void Purge() { - _base.Free(); - _size = 0; - _offset = 0; + base.Free(); + size = 0; + offset = 0; } }; @@ -608,16 +667,16 @@ public: CBufTmpCache( CBuffer *b ) : buffer(b), - size(buffer->_size) + size(buffer->size) { - buffer->_offset += buffer->_size; - buffer->_size = 0; + buffer->offset += buffer->size; + buffer->size = 0; } ~CBufTmpCache() { - buffer->_offset -= size; - buffer->_size = size; + buffer->offset -= size; + buffer->size = size; } }; From cf4014a97cc06370556cbe6fbcde24bed2bbe5fc Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Sat, 1 Mar 2025 21:35:43 +0300 Subject: [PATCH 05/42] Don't disconnect vscript debugger if socket couldn't be opened --- sp/src/vscript/vscript_squirrel.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/sp/src/vscript/vscript_squirrel.cpp b/sp/src/vscript/vscript_squirrel.cpp index d635d16f..038a790a 100644 --- a/sp/src/vscript/vscript_squirrel.cpp +++ b/sp/src/vscript/vscript_squirrel.cpp @@ -2082,13 +2082,7 @@ bool SquirrelVM::ConnectDebugger( int port ) if ( !debugger_ ) { debugger_ = sqdbg_attach_debugger( vm_ ); - - if ( sqdbg_listen_socket( debugger_, port ) != 0 ) - { - sqdbg_destroy_debugger( vm_ ); - debugger_ = nullptr; - return false; - } + sqdbg_listen_socket( debugger_, port ); } else { From 2b77baefbec5a0e3829e9b0c9bb8f669282bbc7a Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Mon, 3 Mar 2025 20:54:42 +0300 Subject: [PATCH 06/42] Add script_connect_debugger_on_mapspawn cvar --- sp/src/game/client/vscript_client.cpp | 9 ++++++--- sp/src/game/server/vscript_server.cpp | 9 ++++++--- sp/src/game/shared/vscript_shared.cpp | 16 +--------------- sp/src/game/shared/vscript_shared.h | 6 ++++++ sp/src/public/vscript/ivscript.h | 2 +- sp/src/vscript/vscript.cpp | 1 - sp/src/vscript/vscript_squirrel.cpp | 21 ++++++++++++++++++--- 7 files changed, 38 insertions(+), 26 deletions(-) diff --git a/sp/src/game/client/vscript_client.cpp b/sp/src/game/client/vscript_client.cpp index 14b40837..34d47291 100644 --- a/sp/src/game/client/vscript_client.cpp +++ b/sp/src/game/client/vscript_client.cpp @@ -29,7 +29,7 @@ extern IScriptManager *scriptmanager; extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * ); #ifdef MAPBASE_VSCRIPT -extern int vscript_debugger_port; +ConVar script_connect_debugger_on_mapspawn_client( "script_connect_debugger_on_mapspawn_client", "0" ); #endif // #define VMPROFILE 1 @@ -687,10 +687,13 @@ bool VScriptClientInit() #endif #ifdef MAPBASE_VSCRIPT - if ( vscript_debugger_port ) + if ( script_connect_debugger_on_mapspawn_client.GetInt() == 2 ) + { + g_pScriptVM->ConnectDebugger( vscript_debugger_port, 10.0f ); + } + else if ( script_connect_debugger_on_mapspawn_client.GetInt() != 0 ) { g_pScriptVM->ConnectDebugger( vscript_debugger_port ); - vscript_debugger_port = 0; } #endif diff --git a/sp/src/game/server/vscript_server.cpp b/sp/src/game/server/vscript_server.cpp index 121ecaef..2f59fa62 100644 --- a/sp/src/game/server/vscript_server.cpp +++ b/sp/src/game/server/vscript_server.cpp @@ -23,7 +23,7 @@ extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * ); #ifdef MAPBASE_VSCRIPT -extern int vscript_debugger_port; +ConVar script_connect_debugger_on_mapspawn( "script_connect_debugger_on_mapspawn", "0" ); #endif // #define VMPROFILE 1 @@ -668,10 +668,13 @@ bool VScriptServerInit() #endif #ifdef MAPBASE_VSCRIPT - if ( vscript_debugger_port ) + if ( script_connect_debugger_on_mapspawn.GetInt() == 2 ) + { + g_pScriptVM->ConnectDebugger( vscript_debugger_port, 10.0f ); + } + else if ( script_connect_debugger_on_mapspawn.GetInt() != 0 ) { g_pScriptVM->ConnectDebugger( vscript_debugger_port ); - vscript_debugger_port = 0; } #endif diff --git a/sp/src/game/shared/vscript_shared.cpp b/sp/src/game/shared/vscript_shared.cpp index f487ac4b..8b5452ec 100644 --- a/sp/src/game/shared/vscript_shared.cpp +++ b/sp/src/game/shared/vscript_shared.cpp @@ -37,7 +37,6 @@ extern ScriptClassDesc_t * GetScriptDesc( CBaseEntity * ); #ifdef MAPBASE_VSCRIPT // This is to ensure a dependency exists between the vscript library and the game DLLs extern int vscript_token; -extern int vscript_debugger_port; int vscript_token_hack = vscript_token; #endif @@ -391,27 +390,14 @@ CON_COMMAND_F( script_debug, "Connect the vscript VM to the script debugger", FC if ( !IsCommandIssuedByServerAdmin() ) return; -#ifdef MAPBASE_VSCRIPT -#ifdef GAME_DLL - int port = 1212; -#else - int port = 1213; -#endif -#endif - if ( !g_pScriptVM ) { -#ifdef MAPBASE_VSCRIPT - vscript_debugger_port = port; - CGMsg( 0, CON_GROUP_VSCRIPT, "VScript VM is not running, waiting for it to attach the debugger to port %d...\n", port ); -#else CGWarning( 0, CON_GROUP_VSCRIPT, "Scripting disabled or no server running\n" ); -#endif return; } #ifdef MAPBASE_VSCRIPT - g_pScriptVM->ConnectDebugger( port ); + g_pScriptVM->ConnectDebugger( vscript_debugger_port ); #else g_pScriptVM->ConnectDebugger(); #endif diff --git a/sp/src/game/shared/vscript_shared.h b/sp/src/game/shared/vscript_shared.h index 50834220..db2e7d79 100644 --- a/sp/src/game/shared/vscript_shared.h +++ b/sp/src/game/shared/vscript_shared.h @@ -43,6 +43,12 @@ class CBaseEntityScriptInstanceHelper : public IScriptInstanceHelper extern CBaseEntityScriptInstanceHelper g_BaseEntityScriptInstanceHelper; #ifdef MAPBASE_VSCRIPT +#ifdef GAME_DLL +const int vscript_debugger_port = 1212; +#else +const int vscript_debugger_port = 1213; +#endif + void RegisterSharedScriptConstants(); void RegisterSharedScriptFunctions(); diff --git a/sp/src/public/vscript/ivscript.h b/sp/src/public/vscript/ivscript.h index 8081968e..6408aa51 100644 --- a/sp/src/public/vscript/ivscript.h +++ b/sp/src/public/vscript/ivscript.h @@ -839,7 +839,7 @@ public: virtual void Shutdown() = 0; #ifdef MAPBASE_VSCRIPT - virtual bool ConnectDebugger( int port = 0 ) = 0; + virtual bool ConnectDebugger( int port = 0, float timeout = 0.0f ) = 0; #else virtual bool ConnectDebugger() = 0; #endif diff --git a/sp/src/vscript/vscript.cpp b/sp/src/vscript/vscript.cpp index 2bf0a8d6..a91008d7 100644 --- a/sp/src/vscript/vscript.cpp +++ b/sp/src/vscript/vscript.cpp @@ -17,7 +17,6 @@ IScriptVM* makeSquirrelVM(); int vscript_token = 0; -int vscript_debugger_port = 0; class CScriptManager : public CTier1AppSystem { diff --git a/sp/src/vscript/vscript_squirrel.cpp b/sp/src/vscript/vscript_squirrel.cpp index 038a790a..be9aebd1 100644 --- a/sp/src/vscript/vscript_squirrel.cpp +++ b/sp/src/vscript/vscript_squirrel.cpp @@ -141,7 +141,7 @@ public: virtual bool Init() override; virtual void Shutdown() override; - virtual bool ConnectDebugger( int port = 0 ) override; + virtual bool ConnectDebugger( int port = 0, float timeout = 0.0f ) override; virtual void DisconnectDebugger() override; virtual ScriptLanguage_t GetLanguage() override; @@ -2077,12 +2077,27 @@ void SquirrelVM::Shutdown() bool VScriptRunScript( const char *pszScriptName, HSCRIPT hScope, bool bWarnMissing ); -bool SquirrelVM::ConnectDebugger( int port ) +bool SquirrelVM::ConnectDebugger( int port, float timeout ) { if ( !debugger_ ) { debugger_ = sqdbg_attach_debugger( vm_ ); - sqdbg_listen_socket( debugger_, port ); + + if ( sqdbg_listen_socket( debugger_, port ) == 0 && timeout ) + { + float startTime = Plat_FloatTime(); + + while ( !sqdbg_is_client_connected( debugger_ ) ) + { + float time = Plat_FloatTime(); + if ( time - startTime > timeout ) + break; + + ThreadSleep( 50 ); + + sqdbg_frame( debugger_ ); + } + } } else { From 93efcbbcfd703eaede8d73365f08abd3dc6ddcee Mon Sep 17 00:00:00 2001 From: Wikot235 <149392035+Wikot235@users.noreply.github.com> Date: Sat, 24 May 2025 17:38:36 +0200 Subject: [PATCH 07/42] Headshot damage multiplier cvar for npc_combine_s (#420) --- sp/src/game/server/hl2/npc_combines.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sp/src/game/server/hl2/npc_combines.cpp b/sp/src/game/server/hl2/npc_combines.cpp index be85d895..61f8156d 100644 --- a/sp/src/game/server/hl2/npc_combines.cpp +++ b/sp/src/game/server/hl2/npc_combines.cpp @@ -38,6 +38,11 @@ ConVar sk_combine_guard_kick( "sk_combine_guard_kick", "0"); ConVar combine_guard_spawn_health( "combine_guard_spawn_health", "1" ); extern ConVar sk_plr_dmg_buckshot; + +#ifdef MAPBASE + ConVar sk_combine_head_dmg_multiplier( "sk_combine_head_dmg_multiplier", "2" ); +#endif + extern ConVar sk_plr_num_shotgun_pellets; //Whether or not the combine should spawn health on death @@ -222,8 +227,14 @@ float CNPC_CombineS::GetHitgroupDamageMultiplier( int iHitGroup, const CTakeDama { case HITGROUP_HEAD: { + +#ifdef MAPBASE + // Now you can change the multiplier of headshot damage in console! + return sk_combine_head_dmg_multiplier.GetFloat(); +#else // Soldiers take double headshot damage return 2.0f; +#endif } } From db3d6d1573f6af9871368a3391a6767cfc12030b Mon Sep 17 00:00:00 2001 From: AnOldLady Date: Sun, 25 May 2025 01:25:07 +0800 Subject: [PATCH 08/42] Added server ragdoll deathpose --- sp/src/game/server/ai_basenpc.h | 5 ++ sp/src/game/server/physics_prop_ragdoll.cpp | 52 +++++++++++++++++++++ sp/src/game/server/physics_prop_ragdoll.h | 4 ++ 3 files changed, 61 insertions(+) diff --git a/sp/src/game/server/ai_basenpc.h b/sp/src/game/server/ai_basenpc.h index 7db67d33..bd167844 100644 --- a/sp/src/game/server/ai_basenpc.h +++ b/sp/src/game/server/ai_basenpc.h @@ -1180,6 +1180,11 @@ public: void SetDeathPose( const int &iDeathPose ) { m_iDeathPose = iDeathPose; } void SetDeathPoseFrame( const int &iDeathPoseFrame ) { m_iDeathFrame = iDeathPoseFrame; } + +#ifdef MAPBASE + int GetDeathPose() { return m_iDeathPose; } + int GetDeathPoseFrame() { return m_iDeathFrame; } +#endif void SelectDeathPose( const CTakeDamageInfo &info ); virtual bool ShouldPickADeathPose( void ) { return true; } diff --git a/sp/src/game/server/physics_prop_ragdoll.cpp b/sp/src/game/server/physics_prop_ragdoll.cpp index e208e620..772f9e31 100644 --- a/sp/src/game/server/physics_prop_ragdoll.cpp +++ b/sp/src/game/server/physics_prop_ragdoll.cpp @@ -22,6 +22,7 @@ #include "hierarchy.h" #ifdef MAPBASE #include "decals.h" +#include "death_pose.h" #endif // memdbgon must be the last include file in a .cpp file!!! @@ -29,6 +30,7 @@ #ifdef MAPBASE ConVar ragdoll_autointeractions("ragdoll_autointeractions", "1", FCVAR_NONE, "Controls whether we should rely on hardcoded keyvalues or automatic flesh checks for ragdoll physgun interactions."); +ConVar ai_death_pose_server_enabled("ai_death_pose_server_enabled", "1", FCVAR_NONE, "Toggles the death pose fix code, but for server ragdolls."); #define IsBody() VPhysicsIsFlesh() ConVar ragdoll_always_allow_use( "ragdoll_always_allow_use", "0", FCVAR_NONE, "Allows all ragdolls to be used and, if they aren't explicitly set to prevent pickup, picked up." ); @@ -788,7 +790,11 @@ void CRagdollProp::SetOverlaySequence( Activity activity ) } } +#ifdef MAPBASE +void CRagdollProp::InitRagdoll( const Vector& forceVector, int forceBone, const Vector& forcePos, matrix3x4_t* pPrevBones, matrix3x4_t* pBoneToWorld, float dt, int collisionGroup, bool activateRagdoll, bool bWakeRagdoll, bool bDeathPose ) +#else void CRagdollProp::InitRagdoll( const Vector &forceVector, int forceBone, const Vector &forcePos, matrix3x4_t *pPrevBones, matrix3x4_t *pBoneToWorld, float dt, int collisionGroup, bool activateRagdoll, bool bWakeRagdoll ) +#endif { SetCollisionGroup( collisionGroup ); @@ -811,7 +817,11 @@ void CRagdollProp::InitRagdoll( const Vector &forceVector, int forceBone, const params.forceVector = forceVector; params.forceBoneIndex = forceBone; params.forcePosition = forcePos; +#ifdef MAPBASE + params.pCurrentBones = bDeathPose ? pPrevBones : pBoneToWorld; +#else params.pCurrentBones = pBoneToWorld; +#endif params.jointFrictionScale = 1.0; params.allowStretch = HasSpawnFlags(SF_RAGDOLLPROP_ALLOW_STRETCH); #ifdef MAPBASE @@ -1492,6 +1502,43 @@ CBaseEntity *CreateServerRagdoll( CBaseAnimating *pAnimating, int forceBone, con float fPreviousCycle = clamp(pAnimating->GetCycle()-( dt * ( 1 / fSequenceDuration ) ),0.f,1.f); float fCurCycle = pAnimating->GetCycle(); + +#ifdef MAPBASE + int deathpose = ACT_INVALID; + int deathframe = 0; + if (ai_death_pose_server_enabled.GetBool() && pAnimating->IsNPC()) { + CAI_BaseNPC* npc = (CAI_BaseNPC*)pAnimating; + if (npc) { + deathpose = Activity(npc->GetDeathPose()); + deathframe = npc->GetDeathPoseFrame(); + } + } + if (deathpose != ACT_INVALID) { + int currentSequence = pAnimating->GetSequence(); + + //Force pAnimating to position the deathpose + pAnimating->SetSequence(deathpose); + pAnimating->SetCycle((float)deathframe / MAX_DEATHPOSE_FRAMES); + + //Store the position + pAnimating->SetupBones(pBoneToWorldNext, BONE_USED_BY_ANYTHING); + + //Restore the current sequence and cycle + pAnimating->SetSequence(currentSequence); + + pAnimating->SetCycle(fCurCycle); + pAnimating->SetupBones(pBoneToWorld, BONE_USED_BY_ANYTHING); + } + else { + // Get current bones positions + pAnimating->SetupBones(pBoneToWorldNext, BONE_USED_BY_ANYTHING); + // Get previous bones positions + pAnimating->SetCycle(fPreviousCycle); + pAnimating->SetupBones(pBoneToWorld, BONE_USED_BY_ANYTHING); + // Restore current cycle + pAnimating->SetCycle(fCurCycle); + } +#else // Get current bones positions pAnimating->SetupBones( pBoneToWorldNext, BONE_USED_BY_ANYTHING ); // Get previous bones positions @@ -1499,6 +1546,7 @@ CBaseEntity *CreateServerRagdoll( CBaseAnimating *pAnimating, int forceBone, con pAnimating->SetupBones( pBoneToWorld, BONE_USED_BY_ANYTHING ); // Restore current cycle pAnimating->SetCycle( fCurCycle ); +#endif // Reset previous bone flags pAnimating->ClearBoneCacheFlags( BCF_NO_ANIMATION_SKIP ); @@ -1573,7 +1621,11 @@ CBaseEntity *CreateServerRagdoll( CBaseAnimating *pAnimating, int forceBone, con } else { +#ifdef MAPBASE + pRagdoll->InitRagdoll(info.GetDamageForce(), forceBone, info.GetDamagePosition(), pBoneToWorld, pBoneToWorldNext, dt, collisionGroup, true, true, deathpose != ACT_INVALID); +#else pRagdoll->InitRagdoll( info.GetDamageForce(), forceBone, info.GetDamagePosition(), pBoneToWorld, pBoneToWorldNext, dt, collisionGroup, true ); +#endif } // Are we dissolving? diff --git a/sp/src/game/server/physics_prop_ragdoll.h b/sp/src/game/server/physics_prop_ragdoll.h index 82a3d77a..134cfec1 100644 --- a/sp/src/game/server/physics_prop_ragdoll.h +++ b/sp/src/game/server/physics_prop_ragdoll.h @@ -75,7 +75,11 @@ public: // locals void InitRagdollAnimation( void ); +#ifdef MAPBASE + void InitRagdoll( const Vector& forceVector, int forceBone, const Vector& forcePos, matrix3x4_t* pPrevBones, matrix3x4_t* pBoneToWorld, float dt, int collisionGroup, bool activateRagdoll, bool bWakeRagdoll = true, bool bDeathPose = false ); +#else void InitRagdoll( const Vector &forceVector, int forceBone, const Vector &forcePos, matrix3x4_t *pPrevBones, matrix3x4_t *pBoneToWorld, float dt, int collisionGroup, bool activateRagdoll, bool bWakeRagdoll = true ); +#endif void RecheckCollisionFilter( void ); void SetDebrisThink(); From 41945409d890a2c5e3eaac05cc10de119324c270 Mon Sep 17 00:00:00 2001 From: Wikot235 <149392035+Wikot235@users.noreply.github.com> Date: Sun, 25 May 2025 18:08:41 +0200 Subject: [PATCH 09/42] Fixed a crash when npc_helicopter that is awaiting input is killed (#419) --- sp/src/game/server/hl2/npc_attackchopper.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sp/src/game/server/hl2/npc_attackchopper.cpp b/sp/src/game/server/hl2/npc_attackchopper.cpp index 266cc468..efbe12e7 100644 --- a/sp/src/game/server/hl2/npc_attackchopper.cpp +++ b/sp/src/game/server/hl2/npc_attackchopper.cpp @@ -4055,9 +4055,12 @@ void CNPC_AttackHelicopter::Event_Killed( const CTakeDamageInfo &info ) } m_lifeState = LIFE_DYING; - - CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController(); - controller.SoundChangeVolume( m_pGunFiringSound, 0.0, 0.1f ); + + if ( GetSleepState() != AISS_WAITING_FOR_INPUT ) + { + CSoundEnvelopeController& controller = CSoundEnvelopeController::GetController(); + controller.SoundChangeVolume( m_pGunFiringSound, 0.0, 0.1f ); + } if( GetCrashPoint() == NULL ) { From c3382cb481c7397eb3edbe7729fe8a51de5acf7f Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Mon, 26 May 2025 19:53:30 +0300 Subject: [PATCH 10/42] Fix GetPropFloatArray indexing on Vector members --- .../shared/mapbase/vscript_singletons.cpp | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/sp/src/game/shared/mapbase/vscript_singletons.cpp b/sp/src/game/shared/mapbase/vscript_singletons.cpp index 88bb41ff..81459ebf 100644 --- a/sp/src/game/shared/mapbase/vscript_singletons.cpp +++ b/sp/src/game/shared/mapbase/vscript_singletons.cpp @@ -1243,17 +1243,20 @@ public: return -1; } - if ( pInfo->datatype == types::_VEC3 ) - index /= 3; + unsigned int arraysize = pInfo->arraysize; - if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) + if ( pInfo->datatype == types::_VEC3 ) + arraysize *= 3; + + if ( index < 0 || (unsigned int)index >= arraysize ) return -1; switch ( pInfo->datatype ) { - case types::_VEC3: case types::_FLOAT: return *(float*)((char*)pEnt + pInfo->GetOffset( index )); + case types::_VEC3: + return ((float*)((char*)pEnt + pInfo->GetOffset( index / 3 )))[ index % 3 ]; #ifdef GAME_DLL case types::_DAR_FLOAT: { @@ -1285,19 +1288,24 @@ public: return; } - if ( pInfo->datatype == types::_VEC3 ) - index /= 3; + unsigned int arraysize = pInfo->arraysize; - if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) + if ( pInfo->datatype == types::_VEC3 ) + arraysize *= 3; + + if ( index < 0 || (unsigned int)index >= arraysize ) return; switch ( pInfo->datatype ) { - case types::_VEC3: case types::_FLOAT: *(float*)((char*)pEnt + pInfo->GetOffset( index )) = value; NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); break; + case types::_VEC3: + ((float*)((char*)pEnt + pInfo->GetOffset( index / 3 )))[ index % 3 ] = value; + NetworkStateChanged( pEnt, pInfo->GetOffset( index / 3 ) ); + break; #ifdef GAME_DLL case types::_DAR_FLOAT: { From ec55c3011dd5125f6fc2d18abc28e0a03496e8cb Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Wed, 8 Jan 2025 09:31:56 -0600 Subject: [PATCH 11/42] Fix citizen SelectModel VScript hook having issues in save/restore --- sp/src/game/server/hl2/npc_citizen17.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sp/src/game/server/hl2/npc_citizen17.cpp b/sp/src/game/server/hl2/npc_citizen17.cpp index 02baaf07..c66ca4e5 100644 --- a/sp/src/game/server/hl2/npc_citizen17.cpp +++ b/sp/src/game/server/hl2/npc_citizen17.cpp @@ -839,6 +839,9 @@ void CNPC_Citizen::SelectModel() } } + // Models selected this way must be unique to avoid conflicts in save/restore + m_Type = CT_UNIQUE; + // Just set the model right here SetModelName( AllocPooledString( returnValue.m_pszString ) ); return; From 3d435c6dec59e1860edf2efd101b2116b4b4ca62 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sun, 25 May 2025 23:00:26 -0500 Subject: [PATCH 12/42] Improved custom model support on most NPCs Co-authored-by: Maestra Fenix --- sp/src/game/server/hl2/npc_PoisonZombie.cpp | 4 ++-- sp/src/game/server/hl2/npc_antlion.cpp | 8 ++++---- sp/src/game/server/hl2/npc_combinecamera.cpp | 5 ++--- sp/src/game/server/hl2/npc_combinedropship.cpp | 4 ++-- sp/src/game/server/hl2/npc_combinegunship.cpp | 10 +++++----- sp/src/game/server/hl2/npc_dog.cpp | 4 ++-- sp/src/game/server/hl2/npc_fastzombie.cpp | 4 ++-- sp/src/game/server/hl2/npc_fisherman.cpp | 2 +- sp/src/game/server/hl2/npc_gman.cpp | 4 ++-- sp/src/game/server/hl2/npc_monk.cpp | 4 ++-- sp/src/game/server/hl2/npc_mossman.cpp | 4 ++-- sp/src/game/server/hl2/npc_zombie.cpp | 4 ++-- sp/src/game/server/hl2/npc_zombine.cpp | 4 ++-- 13 files changed, 30 insertions(+), 31 deletions(-) diff --git a/sp/src/game/server/hl2/npc_PoisonZombie.cpp b/sp/src/game/server/hl2/npc_PoisonZombie.cpp index 1611e15b..4618e5c9 100644 --- a/sp/src/game/server/hl2/npc_PoisonZombie.cpp +++ b/sp/src/game/server/hl2/npc_PoisonZombie.cpp @@ -256,7 +256,7 @@ END_DATADESC() //----------------------------------------------------------------------------- void CNPC_PoisonZombie::Precache( void ) { - PrecacheModel("models/zombie/poison.mdl"); + PrecacheModel( DefaultOrCustomModel( "models/zombie/poison.mdl" ) ); PrecacheScriptSound( "NPC_PoisonZombie.Die" ); PrecacheScriptSound( "NPC_PoisonZombie.ThrowWarn" ); @@ -509,7 +509,7 @@ void CNPC_PoisonZombie::SetZombieModel( void ) } else { - SetModel( "models/zombie/poison.mdl" ); + SetModel( DefaultOrCustomModel( "models/zombie/poison.mdl" ) ); SetHullType(HULL_HUMAN); } diff --git a/sp/src/game/server/hl2/npc_antlion.cpp b/sp/src/game/server/hl2/npc_antlion.cpp index bda685ea..d7bc9080 100644 --- a/sp/src/game/server/hl2/npc_antlion.cpp +++ b/sp/src/game/server/hl2/npc_antlion.cpp @@ -472,8 +472,8 @@ void CNPC_Antlion::Precache( void ) #ifdef HL2_EPISODIC if ( IsWorker() ) { - PrecacheModel( ANTLION_WORKER_MODEL ); - PropBreakablePrecacheAll( MAKE_STRING( ANTLION_WORKER_MODEL ) ); + PrecacheModel( DefaultOrCustomModel( ANTLION_WORKER_MODEL ) ); + PropBreakablePrecacheAll( MAKE_STRING( DefaultOrCustomModel( ANTLION_WORKER_MODEL ) ) ); UTIL_PrecacheOther( "grenade_spit" ); PrecacheParticleSystem( "blood_impact_antlion_worker_01" ); PrecacheParticleSystem( "antlion_gib_02" ); @@ -482,8 +482,8 @@ void CNPC_Antlion::Precache( void ) else #endif // HL2_EPISODIC { - PrecacheModel( ANTLION_MODEL ); - PropBreakablePrecacheAll( MAKE_STRING( ANTLION_MODEL ) ); + PrecacheModel( DefaultOrCustomModel( ANTLION_MODEL ) ); + PropBreakablePrecacheAll( MAKE_STRING( DefaultOrCustomModel( ANTLION_MODEL ) ) ); PrecacheParticleSystem( "blood_impact_antlion_01" ); PrecacheParticleSystem( "AntlionGib" ); } diff --git a/sp/src/game/server/hl2/npc_combinecamera.cpp b/sp/src/game/server/hl2/npc_combinecamera.cpp index 13bbba1b..3d384582 100644 --- a/sp/src/game/server/hl2/npc_combinecamera.cpp +++ b/sp/src/game/server/hl2/npc_combinecamera.cpp @@ -275,7 +275,7 @@ CNPC_CombineCamera::~CNPC_CombineCamera() //----------------------------------------------------------------------------- void CNPC_CombineCamera::Precache() { - PrecacheModel(COMBINE_CAMERA_MODEL); + PrecacheModel( DefaultOrCustomModel( COMBINE_CAMERA_MODEL ) ); PrecacheModel(COMBINE_CAMERA_GLOW_SPRITE); PrecacheModel(COMBINE_CAMERA_FLASH_SPRITE); @@ -304,8 +304,7 @@ void CNPC_CombineCamera::Precache() void CNPC_CombineCamera::Spawn() { Precache(); - - SetModel(COMBINE_CAMERA_MODEL); + SetModel( DefaultOrCustomModel( COMBINE_CAMERA_MODEL ) ); m_pEyeFlash = CSprite::SpriteCreate(COMBINE_CAMERA_FLASH_SPRITE, GetLocalOrigin(), FALSE); m_pEyeFlash->SetTransparency(kRenderGlow, 255, 255, 255, 0, kRenderFxNoDissipation); diff --git a/sp/src/game/server/hl2/npc_combinedropship.cpp b/sp/src/game/server/hl2/npc_combinedropship.cpp index 557756e7..b6a7c85c 100644 --- a/sp/src/game/server/hl2/npc_combinedropship.cpp +++ b/sp/src/game/server/hl2/npc_combinedropship.cpp @@ -921,7 +921,7 @@ CNPC_CombineDropship::~CNPC_CombineDropship(void) void CNPC_CombineDropship::Spawn( void ) { Precache( ); - SetModel( "models/combine_dropship.mdl" ); + SetModel( DefaultOrCustomModel( "models/combine_dropship.mdl" ) ); #ifdef _XBOX AddEffects( EF_NOSHADOW ); @@ -1188,7 +1188,7 @@ void CNPC_CombineDropship::Activate( void ) void CNPC_CombineDropship::Precache( void ) { // Models - PrecacheModel("models/combine_dropship.mdl"); + PrecacheModel( DefaultOrCustomModel( "models/combine_dropship.mdl" ) ); switch ( m_iCrateType ) { case CRATE_SOLDIER: diff --git a/sp/src/game/server/hl2/npc_combinegunship.cpp b/sp/src/game/server/hl2/npc_combinegunship.cpp index 49e7183c..15a99167 100644 --- a/sp/src/game/server/hl2/npc_combinegunship.cpp +++ b/sp/src/game/server/hl2/npc_combinegunship.cpp @@ -564,11 +564,11 @@ void CNPC_CombineGunship::Spawn( void ) if ( HasSpawnFlags( SF_GUNSHIP_USE_CHOPPER_MODEL ) ) { - SetModel( "models/combine_helicopter.mdl" ); + SetModel( DefaultOrCustomModel( "models/combine_helicopter.mdl" ) ); } else { - SetModel( "models/gunship.mdl" ); + SetModel( DefaultOrCustomModel( "models/gunship.mdl" ) ); } ExtractBbox( SelectHeaviestSequence( ACT_GUNSHIP_PATROL ), m_cullBoxMins, m_cullBoxMaxs ); @@ -690,12 +690,12 @@ void CNPC_CombineGunship::Precache( void ) { if ( HasSpawnFlags( SF_GUNSHIP_USE_CHOPPER_MODEL ) ) { - PrecacheModel( "models/combine_helicopter.mdl" ); + PrecacheModel( DefaultOrCustomModel( "models/combine_helicopter.mdl" ) ); Chopper_PrecacheChunks( this ); } else { - PrecacheModel("models/gunship.mdl"); + PrecacheModel( DefaultOrCustomModel( "models/gunship.mdl" ) ); } PrecacheModel("sprites/lgtning.vmt"); @@ -725,7 +725,7 @@ void CNPC_CombineGunship::Precache( void ) g_iGunshipEffectIndex = PrecacheModel( "sprites/physbeam.vmt" ); } - PropBreakablePrecacheAll( MAKE_STRING("models/gunship.mdl") ); + PropBreakablePrecacheAll( MAKE_STRING( DefaultOrCustomModel( "models/gunship.mdl" ) ) ); BaseClass::Precache(); } diff --git a/sp/src/game/server/hl2/npc_dog.cpp b/sp/src/game/server/hl2/npc_dog.cpp index 6e05cf4d..09c1837f 100644 --- a/sp/src/game/server/hl2/npc_dog.cpp +++ b/sp/src/game/server/hl2/npc_dog.cpp @@ -455,7 +455,7 @@ void CNPC_Dog::Spawn( void ) BaseClass::Spawn(); - SetModel( "models/dog.mdl" ); + SetModel( DefaultOrCustomModel( "models/dog.mdl" ) ); SetHullType( HULL_WIDE_HUMAN ); SetHullSizeNormal(); @@ -638,7 +638,7 @@ void CNPC_Dog::PullObject( bool bMantain ) //----------------------------------------------------------------------------- void CNPC_Dog::Precache( void ) { - PrecacheModel( "models/dog.mdl" ); + PrecacheModel( DefaultOrCustomModel( "models/dog.mdl" ) ); PrecacheScriptSound( "Weapon_PhysCannon.Launch" ); diff --git a/sp/src/game/server/hl2/npc_fastzombie.cpp b/sp/src/game/server/hl2/npc_fastzombie.cpp index 6e5ec136..a533cb18 100644 --- a/sp/src/game/server/hl2/npc_fastzombie.cpp +++ b/sp/src/game/server/hl2/npc_fastzombie.cpp @@ -396,7 +396,7 @@ static const char *s_pLegsModel = "models/gibs/fast_zombie_legs.mdl"; //----------------------------------------------------------------------------- void CFastZombie::Precache( void ) { - PrecacheModel("models/zombie/fast.mdl"); + PrecacheModel( DefaultOrCustomModel( "models/zombie/fast.mdl" ) ); #ifdef HL2_EPISODIC PrecacheModel("models/zombie/Fast_torso.mdl"); PrecacheScriptSound( "NPC_FastZombie.CarEnter1" ); @@ -773,7 +773,7 @@ void CFastZombie::SetZombieModel( void ) } else { - SetModel( "models/zombie/fast.mdl" ); + SetModel( DefaultOrCustomModel( "models/zombie/fast.mdl" ) ); SetHullType(HULL_HUMAN); } diff --git a/sp/src/game/server/hl2/npc_fisherman.cpp b/sp/src/game/server/hl2/npc_fisherman.cpp index 967c5795..c98ce6c5 100644 --- a/sp/src/game/server/hl2/npc_fisherman.cpp +++ b/sp/src/game/server/hl2/npc_fisherman.cpp @@ -133,7 +133,7 @@ END_DATADESC() //----------------------------------------------------------------------------- void CNPC_Fisherman::SelectModel() { - SetModelName( AllocPooledString( FISHERMAN_MODEL ) ); + SetModelName( AllocPooledString( DefaultOrCustomModel( FISHERMAN_MODEL ) ) ); } //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/hl2/npc_gman.cpp b/sp/src/game/server/hl2/npc_gman.cpp index a13fd083..48f95fb3 100644 --- a/sp/src/game/server/hl2/npc_gman.cpp +++ b/sp/src/game/server/hl2/npc_gman.cpp @@ -97,7 +97,7 @@ void CNPC_GMan::Spawn() BaseClass::Spawn(); - SetModel( "models/gman.mdl" ); + SetModel( DefaultOrCustomModel( "models/gman.mdl" ) ); SetHullType(HULL_HUMAN); SetHullSizeNormal(); @@ -123,7 +123,7 @@ void CNPC_GMan::Spawn() //----------------------------------------------------------------------------- void CNPC_GMan::Precache() { - PrecacheModel( "models/gman.mdl" ); + PrecacheModel( DefaultOrCustomModel( "models/gman.mdl" ) ); BaseClass::Precache(); } diff --git a/sp/src/game/server/hl2/npc_monk.cpp b/sp/src/game/server/hl2/npc_monk.cpp index e517a6fe..7f840771 100644 --- a/sp/src/game/server/hl2/npc_monk.cpp +++ b/sp/src/game/server/hl2/npc_monk.cpp @@ -299,7 +299,7 @@ Activity CNPC_Monk::NPC_TranslateActivity( Activity eNewActivity ) //----------------------------------------------------------------------------- void CNPC_Monk::Precache() { - PrecacheModel( "models/Monk.mdl" ); + PrecacheModel( DefaultOrCustomModel( "models/Monk.mdl" ) ); PrecacheScriptSound( "NPC_Citizen.FootstepLeft" ); PrecacheScriptSound( "NPC_Citizen.FootstepRight" ); @@ -317,7 +317,7 @@ void CNPC_Monk::Spawn() BaseClass::Spawn(); - SetModel( "models/Monk.mdl" ); + SetModel( DefaultOrCustomModel( "models/Monk.mdl" ) ); SetHullType(HULL_HUMAN); SetHullSizeNormal(); diff --git a/sp/src/game/server/hl2/npc_mossman.cpp b/sp/src/game/server/hl2/npc_mossman.cpp index f92a8f51..2d160680 100644 --- a/sp/src/game/server/hl2/npc_mossman.cpp +++ b/sp/src/game/server/hl2/npc_mossman.cpp @@ -99,7 +99,7 @@ void CNPC_Mossman::Spawn() BaseClass::Spawn(); - SetModel( "models/mossman.mdl" ); + SetModel( DefaultOrCustomModel( "models/mossman.mdl" ) ); SetHullType(HULL_HUMAN); SetHullSizeNormal(); @@ -124,7 +124,7 @@ void CNPC_Mossman::Spawn() //----------------------------------------------------------------------------- void CNPC_Mossman::Precache() { - PrecacheModel( "models/mossman.mdl" ); + PrecacheModel( DefaultOrCustomModel( "models/mossman.mdl" ) ); BaseClass::Precache(); } diff --git a/sp/src/game/server/hl2/npc_zombie.cpp b/sp/src/game/server/hl2/npc_zombie.cpp index 83a6ed9e..4699271b 100644 --- a/sp/src/game/server/hl2/npc_zombie.cpp +++ b/sp/src/game/server/hl2/npc_zombie.cpp @@ -250,7 +250,7 @@ void CZombie::Precache( void ) { BaseClass::Precache(); - PrecacheModel( "models/zombie/classic.mdl" ); + PrecacheModel( DefaultOrCustomModel( "models/zombie/classic.mdl" ) ); PrecacheModel( "models/zombie/classic_torso.mdl" ); PrecacheModel( "models/zombie/classic_legs.mdl" ); @@ -515,7 +515,7 @@ void CZombie::SetZombieModel( void ) } else { - SetModel( "models/zombie/classic.mdl" ); + SetModel( DefaultOrCustomModel( "models/zombie/classic.mdl" ) ); SetHullType( HULL_HUMAN ); } diff --git a/sp/src/game/server/hl2/npc_zombine.cpp b/sp/src/game/server/hl2/npc_zombine.cpp index c25104d2..9c9c506f 100644 --- a/sp/src/game/server/hl2/npc_zombine.cpp +++ b/sp/src/game/server/hl2/npc_zombine.cpp @@ -249,7 +249,7 @@ void CNPC_Zombine::Precache( void ) { BaseClass::Precache(); - PrecacheModel( "models/zombie/zombie_soldier.mdl" ); + PrecacheModel( DefaultOrCustomModel( "models/zombie/zombie_soldier.mdl" ) ); PrecacheScriptSound( "Zombie.FootstepRight" ); PrecacheScriptSound( "Zombie.FootstepLeft" ); @@ -270,7 +270,7 @@ void CNPC_Zombine::Precache( void ) void CNPC_Zombine::SetZombieModel( void ) { - SetModel( "models/zombie/zombie_soldier.mdl" ); + SetModel( DefaultOrCustomModel( "models/zombie/zombie_soldier.mdl" ) ); SetHullType( HULL_HUMAN ); SetBodygroup( ZOMBIE_BODYGROUP_HEADCRAB, !m_fIsHeadless ); From bd43d3d7bd96b34ad5e5a076c698660717d774d6 Mon Sep 17 00:00:00 2001 From: meatspace Date: Thu, 29 May 2025 16:19:12 -0800 Subject: [PATCH 13/42] typo fix in basecombatweapon_shared.cpp credit goes to Petercov for finding this not me --- sp/src/game/shared/basecombatweapon_shared.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index a847a5e8..bfba16e3 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -1132,7 +1132,7 @@ WeaponClass_t CBaseCombatWeapon::WeaponClassFromString(const char *str) return WEPCLASS_RIFLE; else if (FStrEq(str, "WEPCLASS_SHOTGUN")) return WEPCLASS_SHOTGUN; - else if (FStrEq(str, "WEPCLASS_HEAY")) + else if (FStrEq(str, "WEPCLASS_HEAVY")) return WEPCLASS_HEAVY; else if (FStrEq(str, "WEPCLASS_MELEE")) From 5dda250bb44d5ca3f77bd04426a533b6844f7b02 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 30 May 2025 18:15:43 -0500 Subject: [PATCH 14/42] Fix protagonist not being set in CHL2_Player::Spawn() --- sp/src/game/server/hl2/hl2_player.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sp/src/game/server/hl2/hl2_player.cpp b/sp/src/game/server/hl2/hl2_player.cpp index e9983bf7..673a0e39 100644 --- a/sp/src/game/server/hl2/hl2_player.cpp +++ b/sp/src/game/server/hl2/hl2_player.cpp @@ -1560,6 +1560,9 @@ void CHL2_Player::Spawn(void) if (m_iszProtagonistName == NULL_STRING && *g_szDefaultProtagonist) m_iszProtagonistName = MAKE_STRING( g_szDefaultProtagonist ); + + if (m_iszProtagonistName != NULL_STRING) + SetProtagonist( STRING( m_iszProtagonistName ) ); #endif // From db162631060858aa3fc7c991e29339d0475ca99a Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 30 May 2025 18:18:05 -0500 Subject: [PATCH 15/42] Update README --- README | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README b/README index ab107f1d..915bef9d 100644 --- a/README +++ b/README @@ -52,6 +52,7 @@ Mapbase uses content from the following non-Source SDK 2013 Valve games or SDKs: -- Alien Swarm (Used to port assets from the aforementioned SDK code features, e.g. game instructor icons) -- Left 4 Dead (Used to port certain animations as well as assets from the aforementioned SDK code features, e.g. particle rain) -- Half-Life: Source (Used to port friction tool textures) +-- Half-Life 2: Survivor (Used to port Gordon Freeman model) Valve allows assets from these titles to be distributed for modding purposes. Note that ported assets are only used in the release build, not the code repository. @@ -118,6 +119,7 @@ Direct contributions: - https://github.com/mapbase-source/source-sdk-2013/pull/245 (ViewPunch random fix by Mr0maks) - https://github.com/mapbase-source/source-sdk-2013/pull/248 (soundlevel_t conversation warning fix by Mechami) - https://github.com/mapbase-source/source-sdk-2013/pull/266 ("OnPhysGunPull" output in CPhysicsProp by rlenhub) +- https://github.com/mapbase-source/source-sdk-2013/pull/281 (npc_combinedropship DropStrider input by Bronzehawk75) - https://github.com/mapbase-source/source-sdk-2013/pull/292 (env_headcrabcanister random spawn type by arbabf) - https://github.com/mapbase-source/source-sdk-2013/pull/293 (Restore text selection code by SanyaSho) - https://github.com/mapbase-source/source-sdk-2013/pull/294 (SDK_LightmappedGeneric editor blend swap fix by azzyr) @@ -133,6 +135,12 @@ Direct contributions: - https://github.com/mapbase-source/source-sdk-2013/pull/391 (VRAD -extrapasses command by Unusuario2) - https://github.com/mapbase-source/source-sdk-2013/pull/393 (Additional VBSP options doc by Unusuario2) - https://github.com/mapbase-source/source-sdk-2013/pull/397 (Viewmodel camera bone by Nbc66) +- https://github.com/mapbase-source/source-sdk-2013/pull/418 (Server ragdoll death poses by AnOldLady) +- https://github.com/mapbase-source/source-sdk-2013/pull/419 (npc_helicopter removal crash fix by Wikot235) +- https://github.com/mapbase-source/source-sdk-2013/pull/420 (sk_combine_head_dmg_multiplier cvar by Wikot235) +- https://github.com/mapbase-source/source-sdk-2013/pull/414 (VRAD %alphatexture by SirYodaJedi) +- https://github.com/mapbase-source/source-sdk-2013/pull/422 (Expanded HL2 NPC custom model support by Maestra Fenix) +- https://github.com/mapbase-source/source-sdk-2013/pull/427 (CBaseCombatWeapon::WeaponClassFromString typo fix by vizzys/Petercov) - https://github.com/mapbase-source/mapbase-game-src/pull/1 (Advanced video options duplicate field name fix by arbabf; This is asset-based and not reflected in the code) - https://github.com/mapbase-source/mapbase-game-src/pull/2 (gameinfo.txt typo fix by CarePackage17; This is asset-based and not reflected in the code) - https://github.com/mapbase-source/mapbase-game-src/pull/3 (HudMessage cutoff fix by arbabf; This is asset-based and not reflected in the code) @@ -144,6 +152,8 @@ Direct contributions: - npc_combine cover behavior patches provided by iohnnyboy - logic_playmovie icon created by URAKOLOUY5 (This is asset-based and not reflected in the code) - Dropship APC save/load fix provided by Cvoxulary +- c_arms support on HL2 viewmodels by Inaki (This is asset-based and not reflected in the code) +- Custom c_arms for HL2 characters by Notewell (This is asset-based and not reflected in the code) == Contributions from samisalreadytaken: =-- https://github.com/mapbase-source/source-sdk-2013/pull/47 (VScript utility/consistency changes) @@ -166,8 +176,11 @@ Direct contributions: =-- https://github.com/mapbase-source/source-sdk-2013/pull/260 (CScriptNetPropManager rewrite) =-- https://github.com/mapbase-source/source-sdk-2013/pull/261 (Misc VScript additions) =-- https://github.com/mapbase-source/source-sdk-2013/pull/279 (weapon_custom_scripted fixes) +=-- https://github.com/mapbase-source/source-sdk-2013/pull/302 (VScript debugger) =-- https://github.com/mapbase-source/source-sdk-2013/pull/331 (VScript leak fixes) =-- https://github.com/mapbase-source/source-sdk-2013/pull/332 (Fix OOB access) +=-- https://github.com/mapbase-source/source-sdk-2013/pull/411 (VScript debugger cleanup) +=-- https://github.com/mapbase-source/source-sdk-2013/pull/423 (GetPropFloatArray Vector indexing fix) == Contributions from z33ky: =-- https://github.com/mapbase-source/source-sdk-2013/pull/21 (Various GCC/Linux compilation fixes) From f3c98722624710cd1f08781b40bfc0cdfec7f05d Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sat, 31 May 2025 12:40:04 -0500 Subject: [PATCH 16/42] Fix missing include in sp/src/utils/vbsp/map.cpp --- sp/src/utils/vbsp/map.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sp/src/utils/vbsp/map.cpp b/sp/src/utils/vbsp/map.cpp index d8bce641..57c9394b 100644 --- a/sp/src/utils/vbsp/map.cpp +++ b/sp/src/utils/vbsp/map.cpp @@ -15,6 +15,9 @@ #include "materialsub.h" #include "fgdlib/fgdlib.h" #include "manifest.h" +#ifdef PARALLAX_CORRECTED_CUBEMAPS +#include "mathlib/vmatrix.h" +#endif #ifdef MAPBASE_VSCRIPT #include "vscript_vbsp.h" #endif From 13557bffc975dde46d1c10f6f769f1d0b8cbd1ce Mon Sep 17 00:00:00 2001 From: Carlos suarez <110506367+Unusuario2@users.noreply.github.com> Date: Tue, 4 Mar 2025 17:47:46 +0100 Subject: [PATCH 17/42] "-thread" allows negative values. --- sp/src/utils/common/threads.cpp | 26 ++++++++++++++++++++++++-- sp/src/utils/vbsp/vbsp.cpp | 5 +++-- sp/src/utils/vrad/vrad.cpp | 5 ++++- sp/src/utils/vvis/vvis.cpp | 3 ++- 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/sp/src/utils/common/threads.cpp b/sp/src/utils/common/threads.cpp index 28f0894c..af34cf01 100644 --- a/sp/src/utils/common/threads.cpp +++ b/sp/src/utils/common/threads.cpp @@ -112,7 +112,11 @@ WIN32 =================================================================== */ +#ifdef MAPBASE +int numthreads = -(MAX_TOOL_THREADS + 1); +#else int numthreads = -1; +#endif CRITICAL_SECTION crit; static int enter; @@ -133,19 +137,37 @@ void SetLowPriority() SetPriorityClass( GetCurrentProcess(), IDLE_PRIORITY_CLASS ); } - +//Threads can be negative; if so, they will be subtracted from the total thread count. void ThreadSetDefault (void) { SYSTEM_INFO info; +#ifdef MAPBASE + if (numthreads == -(MAX_TOOL_THREADS + 1)) // not set manually +#else if (numthreads == -1) // not set manually +#endif { GetSystemInfo (&info); numthreads = info.dwNumberOfProcessors; - if (numthreads < 1 || numthreads > 32) +#ifdef MAPBASE + if (numthreads > 32) +#else + if (numthreads < 1 ||numthreads > 32) +#endif numthreads = 1; } +#ifdef MAPBASE + if (numthreads <= 0) + { + GetSystemInfo(&info); + numthreads = info.dwNumberOfProcessors + numthreads; + if (numthreads <= 0) + Error("\nIncrease the number of threads! Threads: %i, they cannot be negative or 0.\n", numthreads); + } +#endif + Msg ("%i threads\n", numthreads); } diff --git a/sp/src/utils/vbsp/vbsp.cpp b/sp/src/utils/vbsp/vbsp.cpp index 54d9e681..efcbd386 100644 --- a/sp/src/utils/vbsp/vbsp.cpp +++ b/sp/src/utils/vbsp/vbsp.cpp @@ -1313,8 +1313,9 @@ int RunVBSP( int argc, char **argv ) Warning( "Other options :\n" " -novconfig : Don't bring up graphical UI on vproject errors.\n" - " -threads : Control the number of threads vbsp uses (defaults to the # of\n" - " processors on your machine).\n" + " -threads # : Control the number of threads vbsp uses (defaults to the #\n" + " or processors on your machine).\n" + " Threads can be negative; if so, they will be subtracted from the total thread count.\n" " -verboseentities: If -v is on, this disables verbose output for submodels.\n" " -noweld : Don't join face vertices together.\n" " -nocsg : Don't chop out intersecting brush areas.\n" diff --git a/sp/src/utils/vrad/vrad.cpp b/sp/src/utils/vrad/vrad.cpp index 79cc467d..774af247 100644 --- a/sp/src/utils/vrad/vrad.cpp +++ b/sp/src/utils/vrad/vrad.cpp @@ -2431,11 +2431,13 @@ int ParseCommandLine( int argc, char **argv, bool *onlydetail ) if ( ++i < argc ) { numthreads = atoi (argv[i]); +#ifndef MAPBASE //Mapbase allows threads to be negative, go to ThreadSetDefault(void) in threads.cpp for a explanation. if ( numthreads <= 0 ) { Warning("Error: expected positive value after '-threads'\n" ); return 1; } +#endif } else { @@ -2827,8 +2829,9 @@ void PrintUsage( int argc, char **argv ) " -dump : Write debugging .txt files.\n" " -dumpnormals : Write normals to debug files.\n" " -dumptrace : Write ray-tracing environment to debug files.\n" - " -threads : Control the number of threads vbsp uses (defaults to the #\n" + " -threads # : Control the number of threads vrad uses (defaults to the #\n" " or processors on your machine).\n" + " Threads can be negative; if so, they will be subtracted from the total thread count.\n" " -lights : Load a lights file in addition to lights.rad and the\n" " level lights file.\n" " -noextra : Disable supersampling.\n" diff --git a/sp/src/utils/vvis/vvis.cpp b/sp/src/utils/vvis/vvis.cpp index 0fb61388..05f52041 100644 --- a/sp/src/utils/vvis/vvis.cpp +++ b/sp/src/utils/vvis/vvis.cpp @@ -1027,8 +1027,9 @@ void PrintUsage( int argc, char **argv ) " -novconfig : Don't bring up graphical UI on vproject errors.\n" " -radius_override: Force a vis radius, regardless of whether an\n" " -mpi_pw : Use a password to choose a specific set of VMPI workers.\n" - " -threads : Control the number of threads vbsp uses (defaults to the #\n" + " -threads # : Control the number of threads vvis uses (defaults to the #\n" " or processors on your machine).\n" + " Threads can be negative; if so, they will be subtracted from the total thread count.\n" " -nosort : Don't sort portals (sorting is an optimization).\n" " -tmpin : Make portals come from \\tmp\\.\n" " -tmpout : Make portals come from \\tmp\\.\n" From dfc1e01e155dcfa2ddfab6bdb35571bab59b06e5 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Mon, 2 Jun 2025 17:57:20 -0500 Subject: [PATCH 18/42] Update README --- README | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README b/README index 915bef9d..ca133aa7 100644 --- a/README +++ b/README @@ -135,12 +135,13 @@ Direct contributions: - https://github.com/mapbase-source/source-sdk-2013/pull/391 (VRAD -extrapasses command by Unusuario2) - https://github.com/mapbase-source/source-sdk-2013/pull/393 (Additional VBSP options doc by Unusuario2) - https://github.com/mapbase-source/source-sdk-2013/pull/397 (Viewmodel camera bone by Nbc66) +- https://github.com/mapbase-source/source-sdk-2013/pull/412 (Compile tool negative threads support by Unusuario2) +- https://github.com/mapbase-source/source-sdk-2013/pull/414 (VRAD %alphatexture by SirYodaJedi) +- https://github.com/mapbase-source/source-sdk-2013/pull/415 (Parallax corrected cubemaps optimization and fix by Zeldaboy14 and White_Red_Dragons) - https://github.com/mapbase-source/source-sdk-2013/pull/418 (Server ragdoll death poses by AnOldLady) - https://github.com/mapbase-source/source-sdk-2013/pull/419 (npc_helicopter removal crash fix by Wikot235) - https://github.com/mapbase-source/source-sdk-2013/pull/420 (sk_combine_head_dmg_multiplier cvar by Wikot235) -- https://github.com/mapbase-source/source-sdk-2013/pull/414 (VRAD %alphatexture by SirYodaJedi) -- https://github.com/mapbase-source/source-sdk-2013/pull/422 (Expanded HL2 NPC custom model support by Maestra Fenix) -- https://github.com/mapbase-source/source-sdk-2013/pull/427 (CBaseCombatWeapon::WeaponClassFromString typo fix by vizzys/Petercov) +- https://github.com/mapbase-source/source-sdk-2013/pull/422 (Expanded HL2 NPC custom model support by Maestra Fenix [committed by Blixibon]) - https://github.com/mapbase-source/mapbase-game-src/pull/1 (Advanced video options duplicate field name fix by arbabf; This is asset-based and not reflected in the code) - https://github.com/mapbase-source/mapbase-game-src/pull/2 (gameinfo.txt typo fix by CarePackage17; This is asset-based and not reflected in the code) - https://github.com/mapbase-source/mapbase-game-src/pull/3 (HudMessage cutoff fix by arbabf; This is asset-based and not reflected in the code) @@ -207,6 +208,7 @@ Direct contributions: =-- https://github.com/mapbase-source/source-sdk-2013/pull/230 (Caption fixes) =-- https://github.com/mapbase-source/source-sdk-2013/pull/231 (Sentence source bug fix) =-- https://github.com/mapbase-source/source-sdk-2013/pull/264 (Outputs for vgui_screen) +=-- https://github.com/mapbase-source/source-sdk-2013/pull/427 (CBaseCombatWeapon::WeaponClassFromString typo fix [committed by vizzys]) //------------------------------------------------------------------------------------------------------------------------- From 985ebc0dbb8574a5de76c9b64a8e3bbf11b3efea Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sun, 9 Feb 2025 10:37:44 -0600 Subject: [PATCH 19/42] Allow NPCs to face targets while parented --- sp/src/game/server/ai_basenpc.cpp | 16 ++++++++-------- sp/src/game/server/ai_basenpc_schedule.cpp | 12 ++++++------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/sp/src/game/server/ai_basenpc.cpp b/sp/src/game/server/ai_basenpc.cpp index bdface8c..b690f9a0 100644 --- a/sp/src/game/server/ai_basenpc.cpp +++ b/sp/src/game/server/ai_basenpc.cpp @@ -3043,7 +3043,7 @@ void CAI_BaseNPC::SetHeadDirection( const Vector &vTargetPos, float flInterval) //-------------------------------------- // Set head yaw //-------------------------------------- - float flDesiredYaw = VecToYaw(vTargetPos - GetLocalOrigin()) - GetLocalAngles().y; + float flDesiredYaw = VecToYaw(vTargetPos - GetAbsOrigin()) - GetAbsAngles().y; if (flDesiredYaw > 180) flDesiredYaw -= 360; if (flDesiredYaw < -180) @@ -8003,7 +8003,7 @@ void CAI_BaseNPC::NPCInit ( void ) SetGravity(1.0); // Don't change m_takedamage = DAMAGE_YES; - GetMotor()->SetIdealYaw( GetLocalAngles().y ); + GetMotor()->SetIdealYaw( GetAbsAngles().y ); m_iMaxHealth = m_iHealth; m_lifeState = LIFE_ALIVE; SetIdealState( NPC_STATE_IDLE );// Assume npc will be idle, until proven otherwise @@ -9644,14 +9644,14 @@ float CAI_BaseNPC::CalcIdealYaw( const Vector &vecTarget ) vecProjection.x = -vecTarget.y; vecProjection.y = vecTarget.x; - return UTIL_VecToYaw( vecProjection - GetLocalOrigin() ); + return UTIL_VecToYaw( vecProjection - GetAbsOrigin() ); } else if ( GetNavigator()->GetMovementActivity() == ACT_STRAFE_RIGHT ) { vecProjection.x = vecTarget.y; vecProjection.y = vecTarget.x; - return UTIL_VecToYaw( vecProjection - GetLocalOrigin() ); + return UTIL_VecToYaw( vecProjection - GetAbsOrigin() ); } #ifdef MAPBASE // Allow hint nodes to override the yaw without needing to control AI @@ -9662,7 +9662,7 @@ float CAI_BaseNPC::CalcIdealYaw( const Vector &vecTarget ) #endif else { - return UTIL_VecToYaw ( vecTarget - GetLocalOrigin() ); + return UTIL_VecToYaw ( vecTarget - GetAbsOrigin() ); } } @@ -9862,7 +9862,7 @@ void CAI_BaseNPC::HandleAnimEvent( animevent_t *pEvent ) //DevMsg( "Turned!\n" ); SetIdealActivity( ACT_IDLE ); Forget( bits_MEMORY_TURNING ); - SetBoneController( 0, GetLocalAngles().y ); + SetBoneController( 0, GetAbsAngles().y ); IncrementInterpolationFrame(); break; } @@ -11141,7 +11141,7 @@ Vector CAI_BaseNPC::GetShootEnemyDir( const Vector &shootOrigin, bool bNoisy ) else { Vector forward; - AngleVectors( GetLocalAngles(), &forward ); + AngleVectors( GetAbsAngles(), &forward ); return forward; } } @@ -14179,7 +14179,7 @@ bool CAI_BaseNPC::OverrideMove( float flInterval ) float CAI_BaseNPC::VecToYaw( const Vector &vecDir ) { if (vecDir.x == 0 && vecDir.y == 0 && vecDir.z == 0) - return GetLocalAngles().y; + return GetAbsAngles().y; return UTIL_VecToYaw( vecDir ); } diff --git a/sp/src/game/server/ai_basenpc_schedule.cpp b/sp/src/game/server/ai_basenpc_schedule.cpp index 8a8bee88..0520bb71 100644 --- a/sp/src/game/server/ai_basenpc_schedule.cpp +++ b/sp/src/game/server/ai_basenpc_schedule.cpp @@ -964,7 +964,7 @@ void CAI_BaseNPC::StartTurn( float flDeltaYaw ) { float flCurrentYaw; - flCurrentYaw = UTIL_AngleMod( GetLocalAngles().y ); + flCurrentYaw = UTIL_AngleMod( GetAbsAngles().y ); GetMotor()->SetIdealYaw( UTIL_AngleMod( flCurrentYaw + flDeltaYaw ) ); SetTurnActivity(); } @@ -1157,7 +1157,7 @@ void CAI_BaseNPC::StartScriptMoveToTargetTask( int task ) { TaskFail(FAIL_NO_TARGET); } - else if ( (m_hTargetEnt->GetAbsOrigin() - GetLocalOrigin()).Length() < 1 ) + else if ( (m_hTargetEnt->GetAbsOrigin() - GetAbsOrigin()).Length() < 1 ) { TaskComplete(); } @@ -1622,7 +1622,7 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask ) break; case TASK_SET_IDEAL_YAW_TO_CURRENT: - GetMotor()->SetIdealYaw( UTIL_AngleMod( GetLocalAngles().y ) ); + GetMotor()->SetIdealYaw( UTIL_AngleMod( GetAbsAngles().y ) ); TaskComplete(); break; @@ -1776,7 +1776,7 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask ) { TaskFail(FAIL_NO_TARGET); } - else if ( (pTarget->GetAbsOrigin() - GetLocalOrigin()).Length() < 1 ) + else if ( (pTarget->GetAbsOrigin() - GetAbsOrigin()).Length() < 1 ) { TaskComplete(); } @@ -3021,7 +3021,7 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask ) { if ( m_hTargetEnt != NULL ) { - GetMotor()->SetIdealYaw( UTIL_AngleMod( m_hTargetEnt->GetLocalAngles().y ) ); + GetMotor()->SetIdealYaw( UTIL_AngleMod( m_hTargetEnt->GetAbsAngles().y ) ); } if ( m_scriptState != SCRIPT_CUSTOM_MOVE_TO_MARK ) @@ -3350,7 +3350,7 @@ void CAI_BaseNPC::RunTask( const Task_t *pTask ) pTarget = GetEnemy(); if ( pTarget ) { - GetMotor()->SetIdealYawAndUpdate( pTarget->GetAbsOrigin() - GetLocalOrigin() , AI_KEEP_YAW_SPEED ); + GetMotor()->SetIdealYawAndUpdate( pTarget->GetAbsOrigin() - GetAbsOrigin(), AI_KEEP_YAW_SPEED ); } if ( IsActivityFinished() ) From cdafc1278ebd367483dc67d1242390972ec70a18 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Thu, 13 Feb 2025 09:00:19 -0600 Subject: [PATCH 20/42] game_menu entity and associated CHudMenu changes --- sp/src/game/client/menu.cpp | 255 +++++++++- sp/src/game/client/menu.h | 13 + sp/src/game/server/maprules.cpp | 447 ++++++++++++++++++ sp/src/game/server/maprules.h | 48 ++ sp/src/game/shared/gamerules.cpp | 24 + .../shared/mapbase/mapbase_usermessages.cpp | 4 + 6 files changed, 771 insertions(+), 20 deletions(-) diff --git a/sp/src/game/client/menu.cpp b/sp/src/game/client/menu.cpp index f5ee3169..b11eb43b 100644 --- a/sp/src/game/client/menu.cpp +++ b/sp/src/game/client/menu.cpp @@ -37,6 +37,9 @@ char g_szPrelocalisedMenuString[MAX_MENU_STRING]; DECLARE_HUDELEMENT( CHudMenu ); DECLARE_HUD_MESSAGE( CHudMenu, ShowMenu ); +#ifdef MAPBASE +DECLARE_HUD_MESSAGE( CHudMenu, ShowMenuComplex ); +#endif // //----------------------------------------------------- @@ -71,6 +74,9 @@ CHudMenu::CHudMenu( const char *pElementName ) : void CHudMenu::Init( void ) { HOOK_HUD_MESSAGE( CHudMenu, ShowMenu ); +#ifdef MAPBASE + HOOK_HUD_MESSAGE( CHudMenu, ShowMenuComplex ); +#endif m_bMenuTakesInput = false; m_bMenuDisplayed = false; @@ -81,6 +87,24 @@ void CHudMenu::Init( void ) Reset(); } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudMenu::LevelInit() +{ + CHudElement::LevelInit(); + +#ifdef MAPBASE + if (m_bMapDefinedMenu) + { + // Fixes menu retaining on level change/reload + // TODO: Would non-map menus benefit from this as well? + m_bMenuTakesInput = false; + m_bMenuDisplayed = false; + } +#endif +} + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -114,7 +138,11 @@ void CHudMenu::OnThink() float flSelectionTimeout = MENU_SELECTION_TIMEOUT; // If we've been open for a while without input, hide +#ifdef MAPBASE + if ( m_bMenuDisplayed && ( gpGlobals->curtime - m_flSelectionTime > flSelectionTimeout && !m_bMapDefinedMenu ) ) +#else if ( m_bMenuDisplayed && ( gpGlobals->curtime - m_flSelectionTime > flSelectionTimeout ) ) +#endif { m_bMenuDisplayed = false; } @@ -130,11 +158,24 @@ bool CHudMenu::ShouldDraw( void ) return false; // check for if menu is set to disappear - if ( m_flShutoffTime > 0 && m_flShutoffTime <= gpGlobals->realtime ) + if ( m_flShutoffTime > 0 ) { - // times up, shutoff - m_bMenuDisplayed = false; - return false; +#ifdef MAPBASE + if ( m_bMapDefinedMenu && !m_bPlayingFadeout && (m_flShutoffTime - m_flOpenCloseTime) <= GetMenuTime() ) + { + // Begin closing the menu + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "MenuClose" ); + m_bMenuTakesInput = false; + m_bPlayingFadeout = true; + } + else +#endif + if ( m_flShutoffTime <= GetMenuTime() ) + { + // times up, shutoff + m_bMenuDisplayed = false; + return false; + } } return draw; @@ -169,23 +210,21 @@ void CHudMenu::Paint() return; // center it - int x = 20; + int x = m_nBorder; Color menuColor = m_MenuColor; Color itemColor = m_ItemColor; int c = m_Processed.Count(); - int border = 20; - - int wide = m_nMaxPixels + border; - int tall = m_nHeight + border; + int wide = m_nMaxPixels + m_nBorder; + int tall = m_nHeight + m_nBorder; int y = ( ScreenHeight() - tall ) * 0.5f; - DrawBox( x - border/2, y - border/2, wide, tall, m_BoxColor, m_flSelectionAlphaOverride / 255.0f ); + DrawBox( x - m_nBorder/2, y - m_nBorder/2, wide, tall, m_BoxColor, m_flSelectionAlphaOverride / 255.0f ); - //DrawTexturedBox( x - border/2, y - border/2, wide, tall, m_BoxColor, m_flSelectionAlphaOverride / 255.0f ); + //DrawTexturedBox( x - m_nBorder/2, y - m_nBorder/2, wide, tall, m_BoxColor, m_flSelectionAlphaOverride / 255.0f ); menuColor[3] = menuColor[3] * ( m_flSelectionAlphaOverride / 255.0f ); itemColor[3] = itemColor[3] * ( m_flSelectionAlphaOverride / 255.0f ); @@ -195,7 +234,18 @@ void CHudMenu::Paint() ProcessedLine *line = &m_Processed[ i ]; Assert( line ); - Color clr = line->menuitem != 0 ? itemColor : menuColor; +#ifdef MAPBASE + bool isItem = true; + if (line->menuitem == 0 && line->startchar < (MAX_MENU_STRING-1) && g_szMenuString[ line->startchar ] != L'0' && g_szMenuString[ line->startchar+1 ] != L'.') + { + // Can't use 0 directly because it gets conflated with the cancel item + isItem = false; + } +#else + bool isItem = line->menuitem != 0; +#endif + + Color clr = isItem ? itemColor : menuColor; bool canblur = false; if ( line->menuitem != 0 && @@ -208,15 +258,15 @@ void CHudMenu::Paint() vgui::surface()->DrawSetTextColor( clr ); int drawLen = line->length; - if ( line->menuitem != 0 ) + if (isItem) { drawLen *= m_flTextScan; } - vgui::surface()->DrawSetTextFont( line->menuitem != 0 ? m_hItemFont : m_hTextFont ); + vgui::surface()->DrawSetTextFont( isItem ? m_hItemFont : m_hTextFont ); PaintString( &g_szMenuString[ line->startchar ], drawLen, - line->menuitem != 0 ? m_hItemFont : m_hTextFont, x, y ); + isItem ? m_hItemFont : m_hTextFont, x, y ); if ( canblur ) { @@ -242,6 +292,20 @@ void CHudMenu::Paint() } } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline float CHudMenu::GetMenuTime( void ) +{ +#ifdef MAPBASE + // In singleplayer, use the curtime instead. This fixes issues with menus disappearing after pausing + if (gpGlobals->maxClients <= 1) + return gpGlobals->curtime; +#endif + + return gpGlobals->realtime; +} + //----------------------------------------------------------------------------- // Purpose: selects an item from the menu //----------------------------------------------------------------------------- @@ -260,7 +324,7 @@ void CHudMenu::SelectMenuItem( int menu_item ) // remove the menu quickly m_bMenuTakesInput = false; - m_flShutoffTime = gpGlobals->realtime + m_flOpenCloseTime; + m_flShutoffTime = GetMenuTime() + m_flOpenCloseTime; g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("MenuClose"); } } @@ -339,9 +403,20 @@ void CHudMenu::ProcessText( void ) { ProcessedLine *l = &m_Processed[ i ]; Assert( l ); + +#ifdef MAPBASE + bool isItem = true; + if (l->menuitem == 0 && l->startchar < (MAX_MENU_STRING-1) && g_szMenuString[ l->startchar ] != L'0' && g_szMenuString[ l->startchar+1 ] != L'.') + { + // Can't use 0 directly because it gets conflated with the cancel item + isItem = false; + } +#else + bool isItem = l->menuitem != 0; +#endif int pixels = 0; - vgui::HFont font = l->menuitem != 0 ? m_hItemFont : m_hTextFont; + vgui::HFont font = isItem ? m_hItemFont : m_hTextFont; for ( int ch = 0; ch < l->length; ch++ ) { @@ -364,7 +439,7 @@ void CHudMenu::ProcessText( void ) void CHudMenu::HideMenu( void ) { m_bMenuTakesInput = false; - m_flShutoffTime = gpGlobals->realtime + m_flOpenCloseTime; + m_flShutoffTime = GetMenuTime() + m_flOpenCloseTime; g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("MenuClose"); } @@ -381,6 +456,11 @@ void CHudMenu::ShowMenu( const char * menuName, int validSlots ) m_flShutoffTime = -1; m_bitsValidSlots = validSlots; m_fWaitingForMore = 0; + m_nBorder = 20; +#ifdef MAPBASE + m_bMapDefinedMenu = false; + m_bPlayingFadeout = false; +#endif Q_strncpy( g_szPrelocalisedMenuString, menuName, sizeof( g_szPrelocalisedMenuString ) ); @@ -408,6 +488,11 @@ void CHudMenu::ShowMenu_KeyValueItems( KeyValues *pKV ) m_flShutoffTime = -1; m_fWaitingForMore = 0; m_bitsValidSlots = 0; + m_nBorder = 20; +#ifdef MAPBASE + m_bMapDefinedMenu = false; + m_bPlayingFadeout = false; +#endif g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("MenuOpen"); m_nSelectedItem = -1; @@ -426,7 +511,11 @@ void CHudMenu::ShowMenu_KeyValueItems( KeyValues *pKV ) const char *pszItem = item->GetName(); const wchar_t *wLocalizedItem = g_pVGuiLocalize->Find( pszItem ); +#ifdef MAPBASE + nCount = _snwprintf( pWritePosition, nRemaining, L"->%d. %ls\n", i+1, wLocalizedItem ); +#else nCount = _snwprintf( pWritePosition, nRemaining, L"%d. %ls\n", i+1, wLocalizedItem ); +#endif nRemaining -= nCount; pWritePosition += nCount; @@ -436,7 +525,11 @@ void CHudMenu::ShowMenu_KeyValueItems( KeyValues *pKV ) // put a cancel on the end m_bitsValidSlots |= (1<<9); +#ifdef MAPBASE + nCount = _snwprintf( pWritePosition, nRemaining, L"->0. %ls\n", g_pVGuiLocalize->Find( "#Cancel" ) ); +#else nCount = _snwprintf( pWritePosition, nRemaining, L"0. %ls\n", g_pVGuiLocalize->Find( "#Cancel" ) ); +#endif nRemaining -= nCount; pWritePosition += nCount; @@ -465,8 +558,7 @@ void CHudMenu::MsgFunc_ShowMenu( bf_read &msg) if ( DisplayTime > 0 ) { - m_flShutoffTime = m_flOpenCloseTime + DisplayTime + gpGlobals->realtime; - + m_flShutoffTime = m_flOpenCloseTime + DisplayTime + GetMenuTime(); } else { @@ -511,8 +603,131 @@ void CHudMenu::MsgFunc_ShowMenu( bf_read &msg) } m_fWaitingForMore = NeedMore; + m_nBorder = 20; +#ifdef MAPBASE + m_bMapDefinedMenu = false; + m_bPlayingFadeout = false; +#endif } +#ifdef MAPBASE +ConVar hud_menu_complex_border( "hud_menu_complex_border", "30" ); + +//----------------------------------------------------------------------------- +// Purpose: Message handler for ShowMenu message with more options for game_menu +// takes four values: +// short : a bitfield of keys that are valid input +// float : the duration, in seconds, the menu should stay up. -1 means it stays until something is chosen. +// byte : a boolean, TRUE if there is more string yet to be received before displaying the menu, false if it's the last string +// string: menu string to display +// if this message is never received, then scores will simply be the combined totals of the players. +//----------------------------------------------------------------------------- +void CHudMenu::MsgFunc_ShowMenuComplex( bf_read &msg) +{ + m_bitsValidSlots = (short)msg.ReadWord(); + float DisplayTime = msg.ReadFloat(); + int NeedMore = msg.ReadByte(); + + m_nBorder = hud_menu_complex_border.GetInt(); + m_bMapDefinedMenu = true; + m_bPlayingFadeout = false; + + if ( DisplayTime > 0 ) + { + m_flShutoffTime = m_flOpenCloseTime + DisplayTime + GetMenuTime(); + } + else + { + m_flShutoffTime = -1; + } + + if ( m_bitsValidSlots > -1 ) + { + char szString[2048]; + msg.ReadString( szString, sizeof(szString) ); + + if ( !m_fWaitingForMore ) // this is the start of a new menu + { + Q_strncpy( g_szPrelocalisedMenuString, szString, sizeof( g_szPrelocalisedMenuString ) ); + } + else + { // append to the current menu string + Q_strncat( g_szPrelocalisedMenuString, szString, sizeof( g_szPrelocalisedMenuString ), COPY_ALL_CHARACTERS ); + } + + if ( !NeedMore ) + { + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("MenuOpenFlash"); + m_nSelectedItem = -1; + + // we have the whole string, so we can localise it now + wchar_t *pWritePosition = g_szMenuString; + int nRemaining = sizeof( g_szMenuString ) / sizeof( wchar_t ); + int nCount; + + char *pszToken = strtok( szString, "\n" ); + int nCurItem = 0; + for (; pszToken != NULL; pszToken = strtok( NULL, "\n" ), nCurItem++) + { + if (!*pszToken || *pszToken == ' ') + continue; + + wchar_t wszMenuItem[128]; + + const wchar_t *wLocalizedItem = g_pVGuiLocalize->Find( pszToken ); + if (wLocalizedItem) + { + V_wcsncpy( wszMenuItem, wLocalizedItem, sizeof( wszMenuItem ) ); + } + else + { + g_pVGuiLocalize->ConvertANSIToUnicode( pszToken, wszMenuItem, sizeof( wszMenuItem ) ); + } + + if (nCurItem == 0) + { + // First item is title + nCount = _snwprintf( pWritePosition, nRemaining, L"%ls\n", wszMenuItem ); + } + else + { + // If this item isn't valid, skip until it is + //while (!(m_bitsValidSlots & (1 << nCurItem)) && nCurItem < 10) + //{ + // nCurItem++; + //} + + if (nCurItem == 10) + nCurItem = 0; + + nCount = _snwprintf( pWritePosition, nRemaining, L"->%d. %ls\n", nCurItem, wszMenuItem ); + } + + nRemaining -= nCount; + pWritePosition += nCount; + } + + ProcessText(); + } + + m_bMenuDisplayed = true; + + if (m_bitsValidSlots > 0) + m_bMenuTakesInput = true; + else + m_bMenuTakesInput = false; + + m_flSelectionTime = gpGlobals->curtime; + } + else + { + HideMenu(); + } + + m_fWaitingForMore = NeedMore; +} +#endif + //----------------------------------------------------------------------------- // Purpose: hud scheme settings //----------------------------------------------------------------------------- diff --git a/sp/src/game/client/menu.h b/sp/src/game/client/menu.h index 0b3ae9a7..80f14eae 100644 --- a/sp/src/game/client/menu.h +++ b/sp/src/game/client/menu.h @@ -26,10 +26,14 @@ class CHudMenu : public CHudElement, public vgui::Panel public: CHudMenu( const char *pElementName ); void Init( void ); + void LevelInit( void ); void VidInit( void ); void Reset( void ); virtual bool ShouldDraw( void ); void MsgFunc_ShowMenu( bf_read &msg ); +#ifdef MAPBASE + void MsgFunc_ShowMenuComplex( bf_read &msg ); +#endif void HideMenu( void ); void ShowMenu( const char * menuName, int keySlot ); void ShowMenu_KeyValueItems( KeyValues *pKV ); @@ -42,6 +46,8 @@ private: virtual void Paint(); virtual void ApplySchemeSettings(vgui::IScheme *pScheme); private: + float GetMenuTime( void ); + void ProcessText( void ); void PaintString( const wchar_t *text, int textlen, vgui::HFont& font, int x, int y ); @@ -59,6 +65,7 @@ private: int m_nMaxPixels; int m_nHeight; + int m_nBorder; bool m_bMenuDisplayed; int m_bitsValidSlots; @@ -69,6 +76,12 @@ private: float m_flSelectionTime; +#ifdef MAPBASE + // Indicates this menu is defined by game_menu + bool m_bMapDefinedMenu; + bool m_bPlayingFadeout; +#endif + CPanelAnimationVar( float, m_flOpenCloseTime, "OpenCloseTime", "1" ); CPanelAnimationVar( float, m_flBlur, "Blur", "0" ); diff --git a/sp/src/game/server/maprules.cpp b/sp/src/game/server/maprules.cpp index 60279244..7422ed83 100644 --- a/sp/src/game/server/maprules.cpp +++ b/sp/src/game/server/maprules.cpp @@ -14,6 +14,7 @@ #include "entityoutput.h" #ifdef MAPBASE #include "eventqueue.h" +#include "saverestore_utlvector.h" #endif // memdbgon must be the last include file in a .cpp file!!! @@ -825,3 +826,449 @@ void CGamePlayerTeam::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TY } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Displays a custom number menu for player(s) +//----------------------------------------------------------------------------- +LINK_ENTITY_TO_CLASS( game_menu, CGameMenu ); + +BEGIN_DATADESC( CGameMenu ) + + DEFINE_UTLVECTOR( m_ActivePlayers, FIELD_EHANDLE ), + DEFINE_UTLVECTOR( m_ActivePlayerTimes, FIELD_TIME ), + + DEFINE_KEYFIELD( m_flDisplayTime, FIELD_FLOAT, "holdtime" ), + + DEFINE_KEYFIELD( m_iszTitle, FIELD_STRING, "Title" ), + + DEFINE_KEYFIELD( m_iszOption[0], FIELD_STRING, "Case01" ), + DEFINE_KEYFIELD( m_iszOption[1], FIELD_STRING, "Case02" ), + DEFINE_KEYFIELD( m_iszOption[2], FIELD_STRING, "Case03" ), + DEFINE_KEYFIELD( m_iszOption[3], FIELD_STRING, "Case04" ), + DEFINE_KEYFIELD( m_iszOption[4], FIELD_STRING, "Case05" ), + DEFINE_KEYFIELD( m_iszOption[5], FIELD_STRING, "Case06" ), + DEFINE_KEYFIELD( m_iszOption[6], FIELD_STRING, "Case07" ), + DEFINE_KEYFIELD( m_iszOption[7], FIELD_STRING, "Case08" ), + DEFINE_KEYFIELD( m_iszOption[8], FIELD_STRING, "Case09" ), + DEFINE_KEYFIELD( m_iszOption[9], FIELD_STRING, "Case10" ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "ShowMenu", InputShowMenu ), + DEFINE_INPUTFUNC( FIELD_VOID, "HideMenu", InputHideMenu ), + DEFINE_INPUTFUNC( FIELD_VOID, "__DoRestore", InputDoRestore ), + + // Outputs + DEFINE_OUTPUT( m_OnCase[0], "OnCase01" ), + DEFINE_OUTPUT( m_OnCase[1], "OnCase02" ), + DEFINE_OUTPUT( m_OnCase[2], "OnCase03" ), + DEFINE_OUTPUT( m_OnCase[3], "OnCase04" ), + DEFINE_OUTPUT( m_OnCase[4], "OnCase05" ), + DEFINE_OUTPUT( m_OnCase[5], "OnCase06" ), + DEFINE_OUTPUT( m_OnCase[6], "OnCase07" ), + DEFINE_OUTPUT( m_OnCase[7], "OnCase08" ), + DEFINE_OUTPUT( m_OnCase[8], "OnCase09" ), + DEFINE_OUTPUT( m_OnCase[9], "OnCase10" ), + DEFINE_OUTPUT( m_OutValue, "OutValue" ), + DEFINE_OUTPUT( m_OnTimeout, "OnTimeout" ), + + DEFINE_THINKFUNC( TimeoutThink ), + +END_DATADESC() + +IMPLEMENT_AUTO_LIST( IGameMenuAutoList ); + +static const char *s_pTimeoutContext = "TimeoutContext"; + +#define MENU_INFINITE_TIME -1.0f + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CGameMenu::CGameMenu() +{ + m_flDisplayTime = 5.0f; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameMenu::OnRestore() +{ + BaseClass::OnRestore(); + + // Do this a bit after we restore since the HUD might not be ready yet + g_EventQueue.AddEvent( this, "__DoRestore", 0.4f, this, this ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameMenu::InputDoRestore( inputdata_t &inputdata ) +{ + // Check if we should restore the menu on anyone + FOR_EACH_VEC_BACK( m_ActivePlayers, i ) + { + if (m_ActivePlayers[i].Get()) + { + if (m_ActivePlayerTimes[i] > gpGlobals->curtime || m_ActivePlayerTimes[i] == MENU_INFINITE_TIME) + { + CRecipientFilter filter; + filter.AddRecipient( static_cast( m_ActivePlayers[i].Get() ) ); + + ShowMenu( filter, m_ActivePlayerTimes[i] - gpGlobals->curtime ); + continue; + } + } + + // Remove this player since it's no longer valid + m_ActivePlayers.Remove( i ); + m_ActivePlayerTimes.Remove( i ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameMenu::TimeoutThink() +{ + float flNextLowestTime = FLT_MAX; + FOR_EACH_VEC( m_ActivePlayerTimes, i ) + { + // If the player is still in our list, then they must not have selected an option + if (m_ActivePlayerTimes[i] != MENU_INFINITE_TIME) + { + if (m_ActivePlayerTimes[i] <= gpGlobals->curtime) + { + m_OnTimeout.FireOutput( m_ActivePlayers[i], this ); + + // Remove this player since it's no longer valid + m_ActivePlayers.Remove( i ); + m_ActivePlayerTimes.Remove( i ); + break; + } + else if (m_ActivePlayerTimes[i] < flNextLowestTime) + { + flNextLowestTime = m_ActivePlayerTimes[i]; + } + } + } + + if (flNextLowestTime < FLT_MAX) + { + SetNextThink( flNextLowestTime, s_pTimeoutContext ); + } + else + { + SetContextThink( NULL, TICK_NEVER_THINK, s_pTimeoutContext ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameMenu::ShowMenu( CRecipientFilter &filter, float flDisplayTime ) +{ + // Before showing the menu, check each menu to see if there's already one being shown to one of our recipients + for ( int i = 0; i < IGameMenuAutoList::AutoList().Count(); i++ ) + { + CGameMenu *pMenu = static_cast( IGameMenuAutoList::AutoList()[i] ); + if ( pMenu != this && pMenu->IsActive() ) + { + for ( int j = 0; j < filter.GetRecipientCount(); j++ ) + { + CBaseEntity *ent = CBaseEntity::Instance( filter.GetRecipientIndex( j ) ); + if ( pMenu->IsActiveOnTarget( ent ) ) + { + Msg( "%s overriding menu %s for player %i\n", GetDebugName(), pMenu->GetDebugName(), j ); + pMenu->RemoveTarget( ent ); + } + } + } + } + + if (flDisplayTime == 0.0f) + { + flDisplayTime = m_flDisplayTime; + } + + char szString[512] = { 0 }; + int nBitsValidSlots = 0; + + if (m_iszTitle != NULL_STRING) + { + V_strncat( szString, STRING( m_iszTitle ), sizeof( szString ) ); + } + else + { + // Insert space to tell menu code to skip + V_strncat( szString, " ", sizeof( szString ) ); + } + + // Insert newline even if there's no string + V_strncat( szString, "\n", sizeof( szString ) ); + + // Populate the options + for (int i = 0; i < MAX_MENU_OPTIONS; i++) + { + if (m_iszOption[i] != NULL_STRING) + { + nBitsValidSlots |= (1 << i); + + V_strncat( szString, STRING( m_iszOption[i] ), sizeof( szString ) ); + } + else + { + // Insert space to tell menu code to skip + V_strncat( szString, " ", sizeof( szString ) ); + } + + // Insert newline even if there's no string + V_strncat( szString, "\n", sizeof( szString ) ); + } + + if (nBitsValidSlots <= 0 && m_iszTitle == NULL_STRING) + { + Warning( "%s ShowMenu: Can't show menu with no options or title\n", GetDebugName() ); + return; + } + + UserMessageBegin( filter, "ShowMenuComplex" ); + WRITE_WORD( nBitsValidSlots ); + WRITE_FLOAT( flDisplayTime ); + WRITE_BYTE( 0 ); + WRITE_STRING( szString ); + MessageEnd(); + + float flMenuTime; + if (flDisplayTime <= 0.0f) + { + flMenuTime = MENU_INFINITE_TIME; + } + else + { + flMenuTime = gpGlobals->curtime + flDisplayTime; + } + + for ( int j = 0; j < filter.GetRecipientCount(); j++ ) + { + CBaseEntity *ent = CBaseEntity::Instance( filter.GetRecipientIndex( j ) ); + + // Check if we already track this player. If not, make a new one + bool bFound = false; + FOR_EACH_VEC( m_ActivePlayers, i ) + { + if (m_ActivePlayers[i].Get() == ent) + { + m_ActivePlayerTimes[i] = flMenuTime; + bFound = true; + break; + } + } + + if (!bFound) + { + m_ActivePlayers.AddToTail( ent ); + m_ActivePlayerTimes.AddToTail( flMenuTime ); + } + } + + if (GetNextThink( s_pTimeoutContext ) == TICK_NEVER_THINK) + { + SetContextThink( &CGameMenu::TimeoutThink, gpGlobals->curtime + flDisplayTime, s_pTimeoutContext ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameMenu::HideMenu( CRecipientFilter &filter ) +{ + UserMessageBegin( filter, "ShowMenuComplex" ); + WRITE_WORD( -1 ); + WRITE_FLOAT( 0.0f ); + WRITE_BYTE( 0 ); + WRITE_STRING( "" ); + MessageEnd(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameMenu::MenuSelected( int nSlot, CBaseEntity *pActivator ) +{ + if (nSlot <= 0 || nSlot > MAX_MENU_OPTIONS) + { + Warning( "%s: Invalid slot %i\n", GetDebugName(), nSlot ); + return; + } + + m_OnCase[nSlot-1].FireOutput( pActivator, this ); + m_OutValue.Set( nSlot, pActivator, this ); + + RemoveTarget( pActivator ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CGameMenu::IsActive() +{ + FOR_EACH_VEC_BACK( m_ActivePlayers, i ) + { + if (m_ActivePlayers[i].Get()) + { + if (m_ActivePlayerTimes[i] > gpGlobals->curtime || m_ActivePlayerTimes[i] == MENU_INFINITE_TIME) + return true; + } + + // Remove this player since it's no longer valid + m_ActivePlayers.Remove( i ); + m_ActivePlayerTimes.Remove( i ); + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CGameMenu::IsActiveOnTarget( CBaseEntity *pPlayer ) +{ + FOR_EACH_VEC_BACK( m_ActivePlayers, i ) + { + if (m_ActivePlayers[i].Get() == pPlayer) + { + if (m_ActivePlayerTimes[i] > gpGlobals->curtime || m_ActivePlayerTimes[i] == MENU_INFINITE_TIME) + return true; + + // Remove this player since it's no longer valid + m_ActivePlayers.Remove( i ); + m_ActivePlayerTimes.Remove( i ); + return false; + } + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameMenu::RemoveTarget( CBaseEntity *pPlayer ) +{ + FOR_EACH_VEC_BACK( m_ActivePlayers, i ) + { + if (m_ActivePlayers[i].Get() == pPlayer) + { + m_ActivePlayers.Remove( i ); + m_ActivePlayerTimes.Remove( i ); + break; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameMenu::InputShowMenu( inputdata_t &inputdata ) +{ + if (HasSpawnFlags( SF_GAMEMENU_ALLPLAYERS )) + { + CRecipientFilter filter; + filter.AddAllPlayers(); + + ShowMenu( filter ); + } + else + { + CBasePlayer *pPlayer = NULL; + + // If we're in singleplayer, show the message to the player. + if ( gpGlobals->maxClients == 1 ) + { + pPlayer = UTIL_GetLocalPlayer(); + } + // Otherwise show the message to the player that triggered us. + else if ( inputdata.pActivator && inputdata.pActivator->IsNetClient() ) + { + pPlayer = ToBasePlayer( inputdata.pActivator ); + } + + if (pPlayer) + { + CRecipientFilter filter; + filter.AddRecipient( pPlayer ); + + ShowMenu( filter ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameMenu::InputHideMenu( inputdata_t &inputdata ) +{ + if (HasSpawnFlags( SF_GAMEMENU_ALLPLAYERS )) + { + CRecipientFilter filter; + + FOR_EACH_VEC_BACK( m_ActivePlayers, i ) + { + // Select all players in our list who are still active, and remove them + if (m_ActivePlayerTimes[i] > gpGlobals->curtime || m_ActivePlayerTimes[i] == MENU_INFINITE_TIME) + { + filter.AddRecipient( static_cast(m_ActivePlayers[i].Get()) ); + } + + m_ActivePlayers.Remove( i ); + m_ActivePlayerTimes.Remove( i ); + } + + if (filter.GetRecipientCount() <= 0) + return; + + HideMenu( filter ); + } + else + { + CBasePlayer *pPlayer = NULL; + + // If we're in singleplayer, show the message to the player. + if ( gpGlobals->maxClients == 1 ) + { + pPlayer = UTIL_GetLocalPlayer(); + } + // Otherwise show the message to the player that triggered us. + else if ( inputdata.pActivator && inputdata.pActivator->IsNetClient() ) + { + pPlayer = ToBasePlayer( inputdata.pActivator ); + } + + if (!pPlayer) + return; + + // Verify that this player is in our list + CRecipientFilter filter; + FOR_EACH_VEC( m_ActivePlayers, i ) + { + if (m_ActivePlayers[i].Get() == pPlayer && (m_ActivePlayerTimes[i] > gpGlobals->curtime || m_ActivePlayerTimes[i] == MENU_INFINITE_TIME)) + { + filter.AddRecipient( pPlayer ); + + // Remove since the player won't have the menu anymore + m_ActivePlayers.Remove( i ); + m_ActivePlayerTimes.Remove( i ); + break; + } + } + + if (filter.GetRecipientCount() <= 0) + return; + + HideMenu( filter ); + } +} +#endif + + diff --git a/sp/src/game/server/maprules.h b/sp/src/game/server/maprules.h index 7a4b1553..38b9e6dc 100644 --- a/sp/src/game/server/maprules.h +++ b/sp/src/game/server/maprules.h @@ -9,7 +9,55 @@ #ifndef MAPRULES_H #define MAPRULES_H +#ifdef MAPBASE +#define MAX_MENU_OPTIONS 10 +#define SF_GAMEMENU_ALLPLAYERS 0x0001 + +//----------------------------------------------------------------------------- +// Purpose: Displays a custom number menu for player(s) +//----------------------------------------------------------------------------- +DECLARE_AUTO_LIST( IGameMenuAutoList ); +class CGameMenu : public CLogicalEntity, public IGameMenuAutoList +{ +public: + DECLARE_CLASS( CGameMenu, CLogicalEntity ); + DECLARE_DATADESC(); + CGameMenu(); + + void OnRestore(); + void InputDoRestore( inputdata_t &inputdata ); + + void TimeoutThink(); + + void ShowMenu( CRecipientFilter &filter, float flDisplayTime = 0.0f ); + void HideMenu( CRecipientFilter &filter ); + void MenuSelected( int nSlot, CBaseEntity *pActivator ); + + bool IsActive(); + bool IsActiveOnTarget( CBaseEntity *pPlayer ); + void RemoveTarget( CBaseEntity *pPlayer ); + + // Inputs + void InputShowMenu( inputdata_t &inputdata ); + void InputHideMenu( inputdata_t &inputdata ); + +private: + + CUtlVector m_ActivePlayers; + CUtlVector m_ActivePlayerTimes; + + float m_flDisplayTime; + + string_t m_iszTitle; + string_t m_iszOption[MAX_MENU_OPTIONS]; + + // Outputs + COutputEvent m_OnCase[MAX_MENU_OPTIONS]; // Fired for the option chosen + COutputInt m_OutValue; // Outputs the option chosen + COutputEvent m_OnTimeout; // Fires when no option was chosen in time +}; +#endif #endif // MAPRULES_H diff --git a/sp/src/game/shared/gamerules.cpp b/sp/src/game/shared/gamerules.cpp index face9d54..82f7428a 100644 --- a/sp/src/game/shared/gamerules.cpp +++ b/sp/src/game/shared/gamerules.cpp @@ -27,6 +27,9 @@ #include "player_resource.h" #include "tactical_mission.h" #include "gamestats.h" +#ifdef MAPBASE + #include "maprules.h" +#endif #endif @@ -619,6 +622,27 @@ bool CGameRules::ClientCommand( CBaseEntity *pEdict, const CCommand &args ) { if( GetVoiceGameMgr()->ClientCommand( static_cast(pEdict), args ) ) return true; + +#ifdef MAPBASE + if ( FStrEq( args[0], "menuselect" ) ) + { + if ( args.ArgC() >= 2 ) + { + int slot = atoi( args[1] ); + + // See if this is from a game_menu + for ( int i = 0; i < IGameMenuAutoList::AutoList().Count(); i++ ) + { + CGameMenu *pMenu = static_cast( IGameMenuAutoList::AutoList()[i] ); + if ( pMenu->IsActiveOnTarget( pEdict ) ) + { + pMenu->MenuSelected( slot, pEdict ); + return true; + } + } + } + } +#endif } return false; diff --git a/sp/src/game/shared/mapbase/mapbase_usermessages.cpp b/sp/src/game/shared/mapbase/mapbase_usermessages.cpp index 5af946e6..f0a71fd4 100644 --- a/sp/src/game/shared/mapbase/mapbase_usermessages.cpp +++ b/sp/src/game/shared/mapbase/mapbase_usermessages.cpp @@ -20,6 +20,8 @@ void HookMapbaseUserMessages( void ) { // VScript //HOOK_MESSAGE( ScriptMsg ); // Hooked in CNetMsgScriptHelper + + //HOOK_MESSAGE( ShowMenuComplex ); // Hooked in CHudMenu } #endif @@ -28,6 +30,8 @@ void RegisterMapbaseUserMessages( void ) // VScript usermessages->Register( "ScriptMsg", -1 ); // CNetMsgScriptHelper + usermessages->Register( "ShowMenuComplex", -1 ); // CHudMenu + #ifdef CLIENT_DLL // TODO: Better placement? HookMapbaseUserMessages(); From ab03538347e62b7f89b0cb7a60c9bf76b48670ef Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Tue, 3 Jun 2025 19:09:50 +0300 Subject: [PATCH 21/42] Update sqdbg --- sp/src/vscript/sqdbg/sqdbg/debug.h | 4 + sp/src/vscript/sqdbg/sqdbg/json.h | 41 +- sp/src/vscript/sqdbg/sqdbg/net.h | 2 +- sp/src/vscript/sqdbg/sqdbg/server.cpp | 653 ++++++++++++++++++-------- sp/src/vscript/sqdbg/sqdbg/str.h | 44 +- sp/src/vscript/sqdbg/sqdbg/vec.h | 16 +- 6 files changed, 535 insertions(+), 225 deletions(-) diff --git a/sp/src/vscript/sqdbg/sqdbg/debug.h b/sp/src/vscript/sqdbg/sqdbg/debug.h index e7d04b9c..90cb1ea8 100644 --- a/sp/src/vscript/sqdbg/sqdbg/debug.h +++ b/sp/src/vscript/sqdbg/sqdbg/debug.h @@ -106,6 +106,7 @@ } while(0) #endif #define Verify( x ) Assert(x) + #define STATIC_ASSERT( x ) static_assert( x, #x ) #else #define DebuggerBreak() ((void)0) #define Assert( x ) ((void)0) @@ -113,12 +114,15 @@ #define AssertMsg1( x, msg, a1 ) ((void)0) #define AssertMsg2( x, msg, a1, a2 ) ((void)0) #define Verify( x ) x + #define STATIC_ASSERT( x ) #endif // _DEBUG #endif #include +#define STATIC_ASSERT COMPILE_TIME_ASSERT + // Misdefined for GCC in platform.h #undef UNREACHABLE diff --git a/sp/src/vscript/sqdbg/sqdbg/json.h b/sp/src/vscript/sqdbg/sqdbg/json.h index d42a5365..1a199691 100644 --- a/sp/src/vscript/sqdbg/sqdbg/json.h +++ b/sp/src/vscript/sqdbg/sqdbg/json.h @@ -275,9 +275,13 @@ static inline void PutStr( CBuffer *buffer, const string_t &str ) #ifdef SQDBG_VALIDATE_SENT_MSG for ( unsigned int i = 0; i < str.len; i++ ) { - AssertMsg( IN_RANGE_CHAR( str.ptr[i], 0x20, 0x7E ) && - ( ( str.ptr[i] != '\\' && str.ptr[i] != '\"' ) || ( i && str.ptr[i-1] == '\\' ) ), - "control char in json string" ); + if ( str.ptr[i] == '\\' && ( str.ptr[i+1] == '\\' || str.ptr[i+1] == '\"' ) ) + { + i++; + continue; + } + + AssertMsg( str.ptr[i] != '\\' && IN_RANGE_CHAR( str.ptr[i], 0x20, 0x7E ), "control char in json string" ); } #endif } @@ -433,7 +437,7 @@ static inline void PutStr( CBuffer *buffer, const string_t &str, bool quote ) idx += printhex< true, false >( mem + idx, buffer->Capacity() - idx, - (SQChar)*(unsigned char*)c ); + (SQUnsignedChar)*(unsigned char*)c ); } } } @@ -503,6 +507,7 @@ static inline void PutInt( CBuffer *buffer, I val ) template < bool padding, typename I > static inline void PutHex( CBuffer *buffer, I val ) { + STATIC_ASSERT( IS_UNSIGNED( I ) ); buffer->base.Ensure( buffer->Size() + countdigits<16>( val ) + 1 ); int len = printhex< padding >( buffer->Base() + buffer->Size(), buffer->Capacity() - buffer->Size(), val ); buffer->size += len; @@ -699,7 +704,7 @@ public: } else { - PutHex< false >( m_pBuffer, val ); + PutHex< false >( m_pBuffer, cast_unsigned( I, val ) ); } PutChar( m_pBuffer, ']' ); PutChar( m_pBuffer, '\"' ); @@ -850,7 +855,7 @@ private: else { buf = m_Allocator->Alloc(5); - int i = printhex< true, true, false >( buf, 5, token ); + int i = printhex< true, true, false >( buf, 5, (unsigned char)token ); Assert( i == 4 ); buf[i] = 0; } @@ -877,6 +882,24 @@ private: m_error[len] = 0; } + bool IsValue( char token ) + { + switch ( token ) + { + case Token_String: + case Token_Integer: + case Token_Float: + case Token_False: + case Token_True: + case Token_Null: + case Token_Table: + case Token_Array: + return true; + default: + return false; + } + } + char NextToken( string_t &token ) { while ( m_cur < m_end ) @@ -1206,7 +1229,7 @@ err_eof: type = NextToken( token ); type = ParseValue( type, token, &kv->val ); - if ( type == Token_Error ) + if ( !IsValue( type ) ) { SetError( "invalid token %s @ %i", Char(type), Index() ); return Token_Error; @@ -1239,7 +1262,7 @@ err_eof: for (;;) { - if ( type == Token_Error ) + if ( !IsValue( type ) ) { SetError( "expected '%c', got %s @ %i", ']', Char(type), Index() ); return Token_Error; @@ -1315,7 +1338,7 @@ err_eof: value->type = JSON_NULL; return type; default: - return type; + return Token_Error; } } }; diff --git a/sp/src/vscript/sqdbg/sqdbg/net.h b/sp/src/vscript/sqdbg/sqdbg/net.h index 34f5f407..f228aa7a 100644 --- a/sp/src/vscript/sqdbg/sqdbg/net.h +++ b/sp/src/vscript/sqdbg/sqdbg/net.h @@ -842,7 +842,7 @@ public: m_pRecvBufPtr( m_pRecvBuf ), m_bWSAInit( false ) { - Assert( sizeof(m_pRecvBuf) <= ( 1 << ( sizeof(CMessagePool::message_t::len) * 8 ) ) ); + STATIC_ASSERT( sizeof(m_pRecvBuf) <= ( 1 << ( sizeof(CMessagePool::message_t::len) * 8 ) ) ); } }; diff --git a/sp/src/vscript/sqdbg/sqdbg/server.cpp b/sp/src/vscript/sqdbg/sqdbg/server.cpp index 899796e9..89d3bae4 100644 --- a/sp/src/vscript/sqdbg/sqdbg/server.cpp +++ b/sp/src/vscript/sqdbg/sqdbg/server.cpp @@ -5,7 +5,7 @@ // Squirrel Debugger // -#define SQDBG_SV_VER 5 +#define SQDBG_SV_VER 6 #include "sqdbg.h" @@ -116,6 +116,7 @@ void sqdbg_sleep( int ms ) #define memzero(p) memset( (char*)(p), 0, sizeof(*(p)) ) #define ALIGN(v, a) (((v) + ((a)-1)) & ~((a)-1)) +#define ROUND(v, a) ((v) + (a) - (v) % (a)) #ifndef _WIN32 #undef offsetof @@ -137,14 +138,24 @@ void sqdbg_sleep( int ms ) #define _SC(s) s #endif -#if defined(SQUNICODE) && !defined(WCHAR_SIZE) -#ifdef _WIN32 - #define WCHAR_SIZE 2 +#ifdef SQUNICODE + #ifdef _WIN32 + #ifndef WCHAR_SIZE + #define WCHAR_SIZE 2 + #endif + typedef uint16_t SQUnsignedChar; + #else + #ifndef WCHAR_SIZE + #define WCHAR_SIZE 4 + #endif + typedef uint32_t SQUnsignedChar; + #endif #else - #define WCHAR_SIZE 4 -#endif + typedef unsigned char SQUnsignedChar; #endif +STATIC_ASSERT( sizeof(SQChar) == sizeof(SQUnsignedChar) ); + #ifndef scstrlen #ifdef SQUNICODE #define scstrlen wcslen @@ -272,10 +283,14 @@ void sqdbg_sleep( int ms ) #define SUPPORTS_DEREF_OP #if SQUIRREL_VERSION_NUMBER >= 300 - #define CHAINABLE_FUNCPROTO + #define ACCESSIBLE_FUNCPROTO #endif #endif +#if defined(SQDBG_SUPPORTS_FUNCPROTO_LIST) && !defined(ACCESSIBLE_FUNCPROTO) + #define ACCESSIBLE_FUNCPROTO +#endif + #include "str.h" #include "json.h" #include "protocol.h" @@ -641,12 +656,9 @@ public: Assert( vm->_top == top ); } }; +#define STACKCHECK( vm ) CStackCheck stackcheck( vm ) #else -class CStackCheck -{ -public: - CStackCheck( HSQUIRRELVM ) {} -}; +#define STACKCHECK( vm ) (void)0 #endif @@ -659,7 +671,6 @@ public: typedef unsigned int hgroup_t; static const hnode_t INVALID_HANDLE = (hnode_t)-1; -#pragma pack(push, 4) struct node_t { void *func; @@ -669,7 +680,6 @@ public: sample_t sampleStart; hnode_t id; }; -#pragma pack(pop) struct nodetag_t { @@ -1118,7 +1128,7 @@ public: if ( tag ) { return STRLEN(PROF_GROUP_OUTPUT_TEMPLATE) + - ALIGN( tag->_len, PROF_GROUP_NAME_LEN_ALIGNMENT ) + + ROUND( tag->_len, PROF_GROUP_NAME_LEN_ALIGNMENT ) + 1; } @@ -1203,7 +1213,7 @@ public: memcpy( buf, group->tag->_val, sq_rsl(len) ); buf += len; size -= len; - for ( int i = ALIGN( len, PROF_GROUP_NAME_LEN_ALIGNMENT ) - len; i-- > 0; ) + for ( int i = ROUND( len, PROF_GROUP_NAME_LEN_ALIGNMENT ) - len; i-- > 0; ) { *buf++ = ' '; size--; @@ -1972,6 +1982,9 @@ typedef enum VARREF_INSTRUCTIONS, VARREF_OUTERS, VARREF_LITERALS, +#ifdef SQDBG_SUPPORTS_FUNCPROTO_LIST + VARREF_FUNCTIONS, +#endif VARREF_METAMETHODS, VARREF_ATTRIBUTES, VARREF_CALLSTACK, @@ -1993,6 +2006,9 @@ inline bool IsObjectRef( EVARREF type ) type == VARREF_INSTRUCTIONS || type == VARREF_OUTERS || type == VARREF_LITERALS || +#ifdef SQDBG_SUPPORTS_FUNCPROTO_LIST + type == VARREF_FUNCTIONS || +#endif type == VARREF_METAMETHODS || type == VARREF_ATTRIBUTES || type == VARREF_CALLSTACK ); @@ -2079,6 +2095,9 @@ struct objref_t DELEGABLE_META, CUSTOMMEMBER, STACK, +#ifdef SQDBG_SUPPORTS_FUNCPROTO_LIST + FUNCPROTO, +#endif INT, VIRTUAL_REF, VIRTUAL_SIZE, @@ -3022,7 +3041,7 @@ void SQDebugServer::Attach( HSQUIRRELVM vm ) sq_addref( m_pRootVM, &m_EnvGetVal ); { - CStackCheck stackcheck( m_pRootVM ); + STACKCHECK( m_pRootVM ); SQObjectPtr ref; sqdbg_get_debugger_ref( m_pRootVM, ref ); @@ -3700,12 +3719,12 @@ void SQDebugServer::ProcessRequest( const json_table_t &table, int seq ) ProfSwitchThread( m_pCurVM ); #endif - OnRequest_RestartFrame( *arguments, seq ); - RestoreCachedInstructions(); ClearCachedInstructions(); RemoveReturnValues(); + + OnRequest_RestartFrame( *arguments, seq ); } #endif else if ( command.IsEqualTo( "gotoTargets" ) ) @@ -3735,12 +3754,12 @@ void SQDebugServer::ProcessRequest( const json_table_t &table, int seq ) ProfSwitchThread( m_pCurVM ); #endif - OnRequest_Goto( *arguments, seq ); - RestoreCachedInstructions(); ClearCachedInstructions(); RemoveReturnValues(); + + OnRequest_Goto( *arguments, seq ); } else if ( command.IsEqualTo( "next" ) ) { @@ -4227,7 +4246,7 @@ void SQDebugServer::OnRequest_SetFunctionBreakpoints( const json_table_t &argume int line = 0; // function source: funcname,filename:line - for ( int j = name.len - 1; j > 1; j-- ) + for ( int j = name.len - 1; j >= 0; j-- ) { if ( !line && name.ptr[j] == ':' ) { @@ -5341,7 +5360,7 @@ static void Escape( char *dst, unsigned int *len, unsigned int size ) break; } - SQChar val = (SQChar)((unsigned char*)dst)[0]; + SQUnsignedChar val = (SQUnsignedChar)((unsigned char*)dst)[0]; _memmove( dst + 2 + sizeof(SQChar) * 2, dst + 1, @@ -5699,21 +5718,21 @@ getfloat: char *buf = ScratchPad( size ); int len; - if ( _integer(obj) > (SQInteger)( 1 << ( ( sizeof(SQChar) << 3 ) - 1 ) ) ) + // Allow both signed and unsigned char values ( -1 == 255, -128 == 128, -127 == 129 ) + if ( _integer(obj) > + (SQInteger)( (SQUnsignedChar)-1 >> (int)( sizeof(SQChar) >= sizeof(SQInteger) ) ) || + _integer(obj) < + (SQInteger)-( ( (SQUnsignedChar)-1 >> 1 ) + 1 ) ) { len = printint( buf, size, _integer(obj) ); return { buf, (unsigned int)len }; } - SQChar ch = (SQChar)_integer(obj); + SQUnsignedChar ch = (SQUnsignedChar)_integer(obj); if ( !( flags & kFS_Hexadecimal ) ) { -#ifdef SQUNICODE - len = printint( buf, size, ch ); -#else - len = printint( buf, size, (unsigned char)ch ); -#endif + len = printint( buf, size, (SQUnsignedInteger)ch ); } else { @@ -5734,9 +5753,9 @@ getfloat: buf[len++] = (char)ch; } #ifdef SQUNICODE - else if ( IsValidUnicode( &ch, 1 ) ) + else if ( IsValidUnicode( (SQChar*)&ch, 1 ) ) { - len += SQUnicodeToUTF8( buf + len, size - len - 1, &ch, 1 ); + len += SQUnicodeToUTF8( buf + len, size - len - 1, (SQChar*)&ch, 1 ); } #endif else @@ -5745,7 +5764,7 @@ getfloat: buf[len++] = 'x'; #ifdef SQUNICODE - if ( ch <= (SQChar)0xFF ) + if ( ch <= 0xFF ) { len += printhex< true, false, false >( buf + len, size, (unsigned char)ch ); } @@ -5945,7 +5964,7 @@ getfloat: } else { - buf.PutHex( _table(obj)->CountUsed(), false ); + buf.PutHex( (SQUnsignedInteger)_table(obj)->CountUsed(), false ); } buf.Put('}'); @@ -6056,6 +6075,9 @@ getfloat: } case OT_CLOSURE: case OT_NATIVECLOSURE: +#ifdef ACCESSIBLE_FUNCPROTO + case OT_FUNCPROTO: +#endif { const SQObjectPtr *name; @@ -6063,10 +6085,17 @@ getfloat: { name = &_fp(_closure(obj)->_function)->_name; } - else + else if ( sq_type(obj) == OT_NATIVECLOSURE ) { name = &_nativeclosure(obj)->_name; } +#ifdef ACCESSIBLE_FUNCPROTO + else if ( sq_type(obj) == OT_FUNCPROTO ) + { + name = &_funcproto(obj)->_name; + } +#endif + else UNREACHABLE(); if ( sq_type(*name) == OT_STRING ) { @@ -6195,7 +6224,7 @@ SQString *SQDebugServer::GetLocalVarName( const SQFunctionProto *func, const SQI const SQLocalVarInfo &var = func->_localvarinfos[i]; if ( (unsigned int)var._pos == pos && - var._start_op <= ip + s_nTargetAssignment && var._end_op + 1 >= ip ) + var._start_op <= ip + s_nTargetAssignment && var._end_op >= ip ) { return sq_type(var._name) == OT_STRING ? _string(var._name) : NULL; } @@ -6235,12 +6264,48 @@ void SQDebugServer::PrintOuter( const SQFunctionProto *func, int pos, stringbufe void SQDebugServer::PrintLiteral( const SQFunctionProto *func, int pos, stringbufext_t &buf ) { - string_t val = GetValue( func->_literals[pos] ); + const SQObjectPtr &val = func->_literals[pos]; - if ( val.len > 64 ) - val.len = 64; + switch ( sq_type(val) ) + { + case OT_INTEGER: + case OT_FLOAT: + case OT_BOOL: + case OT_NULL: + case OT_STRING: + { + string_t str = GetValue( val ); - buf.Puts( val ); + if ( str.len > 64 ) + str.len = 64; + + buf.Puts( str ); + break; + } + case OT_CLASS: + case OT_INSTANCE: + { + const classdef_t *def = + FindClassDef( sq_type(val) == OT_CLASS ? _class(val) : _instance(val)->_class ); + + if ( def && def->name.ptr ) + { + buf.Puts( GetType( val ) ); + buf.Put( ' ' ); + + string_t str; + str.Assign( def->name.ptr + FMT_PTR_LEN + 1, def->name.len - FMT_PTR_LEN - 1 ); + + if ( str.len > 64 - 9 ) + str.len = 64 - 9; + + buf.Puts( str ); + break; + } + } + default: + buf.Puts( GetType( val ) ); + } } void SQDebugServer::PrintStackTarget( const SQFunctionProto *func, const SQInstruction *instr, @@ -6287,9 +6352,19 @@ void SQDebugServer::DescribeInstruction( const SQInstruction *instr, const SQFun { case _OP_LOADNULLS: { - PrintStackVar( func, instr, instr->_arg0, buf ); - buf.Put( ' ' ); - buf.PutInt( instr->_arg1 ); + if ( instr->_arg1 == 1 ) + { + PrintStackVar( func, instr, instr->_arg0, buf ); + } + else + { + buf.Put( '[' ); + buf.PutInt( (int)instr->_arg0 ); + buf.Put( ']' ); + buf.Put( ' ' ); + buf.PutInt( instr->_arg1 ); + } + break; } case _OP_LOADINT: @@ -7849,6 +7924,13 @@ bool SQDebugServer::Get( const objref_t &obj, SQObjectPtr &value ) return false; } +#ifdef SQDBG_SUPPORTS_FUNCPROTO_LIST + case objref_t::FUNCPROTO: + { + value = *obj.ptr; + return true; + } +#endif case objref_t::INT: { value = (SQInteger)obj.val; @@ -8036,6 +8118,23 @@ bool SQDebugServer::Set( const objref_t &obj, const SQObjectPtr &value ) return false; } +#ifdef SQDBG_SUPPORTS_FUNCPROTO_LIST + case objref_t::FUNCPROTO: + { + if ( sq_type(value) == OT_FUNCPROTO ) + { + *obj.ptr = value; + return true; + } + else if ( sq_type(value) == OT_CLOSURE ) + { + *obj.ptr = _closure(value)->_function; + return true; + } + + return false; + } +#endif default: UNREACHABLE(); } } @@ -8396,9 +8495,6 @@ public: int opbufidx = -1; int unaryidx = -1; - // Recursively parsing unary operators adds the complexity - // of keeping track of and reverting to the previous token - // Using an operator stack is simpler but limits the amount unsigned char unarybuf[ SQDBG_COMPILER_MAX_UNARY_STACK ]; unsigned char opbuf[2]; char deleteop = 0; @@ -10486,7 +10582,7 @@ private: { if ( ISREFCOUNTED( sq_type(val) ) && sq_type(val) != OT_STRING ) { - val = (SQInteger)_refcounted(val); + val = (SQInteger)(uintptr_t)_refcounted(val); return true; } else if ( sq_type(val) == OT_NULL ) @@ -11189,6 +11285,7 @@ private: token._string.IsEqualTo("throw") || token._string.IsEqualTo("static") || token._string.IsEqualTo("return") || + token._string.IsEqualTo("resume") || token._string.IsEqualTo("foreach") || token._string.IsEqualTo("function") ) { @@ -12099,11 +12196,8 @@ bool SQDebugServer::GetObj_Var( const SQObjectPtr &var, const SQObjectPtr &key, _integer(key) >= 0 && _integer(key) < _string(var)->_len ) { out.type = (objref_t::EOBJREF)( objref_t::INT | objref_t::READONLY ); -#ifdef SQUNICODE + // Sign will be extended out.val = (int)_string(var)->_val[ _integer(key) ]; -#else - out.val = (int)(unsigned char)_string(var)->_val[ _integer(key) ]; -#endif value = (SQInteger)out.val; return true; } @@ -12625,11 +12719,13 @@ bool SQDebugServer::GetObj_VarRef( const varref_t *ref, string_t &expression, ob } case VARREF_OUTERS: { - AssertClient( sq_type(ref->GetVar()) == OT_CLOSURE ); + SQObject target = ref->GetVar(); - if ( sq_type(ref->GetVar()) == OT_CLOSURE ) + AssertClient( sq_type(target) == OT_CLOSURE ); + + if ( sq_type(target) == OT_CLOSURE ) { - SQClosure *pClosure = _closure(ref->GetVar()); + SQClosure *pClosure = _closure(target); SQFunctionProto *func = _fp(pClosure->_function); for ( int i = 0; i < func->_noutervalues; i++ ) @@ -12649,16 +12745,18 @@ bool SQDebugServer::GetObj_VarRef( const varref_t *ref, string_t &expression, ob } case VARREF_LITERALS: { - AssertClient( sq_type(ref->GetVar()) == OT_CLOSURE ); + SQObject target = ref->GetVar(); - if ( sq_type(ref->GetVar()) == OT_CLOSURE ) + AssertClient( sq_type(target) == OT_FUNCPROTO ); + + if ( sq_type(target) == OT_FUNCPROTO ) { int idx; if ( atoi( expression, &idx ) && - idx >= 0 && idx < (int)_fp(_closure(ref->GetVar())->_function)->_nliterals ) + idx >= 0 && idx < (int)_funcproto(target)->_nliterals ) { out.type = objref_t::PTR; - out.ptr = &_fp(_closure(ref->GetVar())->_function)->_literals[idx]; + out.ptr = &_funcproto(target)->_literals[idx]; value = *out.ptr; return true; } @@ -12666,6 +12764,29 @@ bool SQDebugServer::GetObj_VarRef( const varref_t *ref, string_t &expression, ob return false; } +#ifdef SQDBG_SUPPORTS_FUNCPROTO_LIST + case VARREF_FUNCTIONS: + { + SQObject target = ref->GetVar(); + + AssertClient( sq_type(target) == OT_FUNCPROTO ); + + if ( sq_type(target) == OT_FUNCPROTO ) + { + int idx; + if ( atoi( expression, &idx ) && + idx >= 0 && idx < (int)_funcproto(target)->_nfunctions ) + { + out.type = objref_t::FUNCPROTO; + out.ptr = &_funcproto(target)->_functions[idx]; + value = *out.ptr; + return true; + } + } + + return false; + } +#endif case VARREF_METAMETHODS: { int mm = -1; @@ -12711,28 +12832,44 @@ bool SQDebugServer::GetObj_VarRef( const varref_t *ref, string_t &expression, ob } case VARREF_STACK: { - AssertClient( expression.len > 2 ); + AssertClient( expression.len > 5 ); - if ( expression.len <= 2 ) + if ( expression.len <= 5 ) return false; - while ( expression.len > 3 && expression.ptr[expression.len-1] != ']' ) + while ( expression.len > 5 && expression.ptr[expression.len-1] != ']' ) expression.len--; expression.ptr++; expression.len -= 2; - int idx; - if ( strtoint( expression, &idx ) && - idx >= 0 && idx < (int)ref->GetThread()->_stack.size() ) - { - out.type = objref_t::PTR; - out.ptr = &ref->GetThread()->_stack._vals[idx]; - value = *out.ptr; - return true; - } + AssertClient( expression.ptr[-1] == '[' ); + char *pIdx = strchr( expression.ptr, ']' ); + string_t strFrame( expression.ptr, pIdx - expression.ptr ); - return false; + HSQUIRRELVM vm = ref->GetThread(); + + int frame; + if ( !pIdx || !strtoint( strFrame, &frame ) || + frame < 0 || frame >= (int)vm->_callsstacksize ) + return false; + + int stackbase = GetStackBase( vm, frame ); + + expression.ptr += strFrame.len + 2; + expression.len -= strFrame.len + 2; + + AssertClient( expression.ptr[-1] == '[' ); + + int idx; + if ( !strtoint( expression, &idx ) || + stackbase + idx < 0 || stackbase + idx >= (int)vm->_stack.size() ) + return false; + + out.type = objref_t::PTR; + out.ptr = &vm->_stack._vals[ stackbase + idx ]; + value = *out.ptr; + return true; } case VARREF_INSTRUCTIONS: case VARREF_CALLSTACK: @@ -13609,7 +13746,7 @@ void SQDebugServer::FillCompletions( const SQObjectPtr &target, HSQUIRRELVM vm, if ( def && sq_type(def->metamembers) != OT_NULL && - _instance(target)->GetMetaMethod( vm, MT_GET, mm ) ) + _instance(env)->GetMetaMethod( vm, MT_GET, mm ) ) { for ( unsigned int i = 0; i < _array(def->metamembers)->_values.size(); i++ ) { @@ -14938,12 +15075,11 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq break; } case OT_CLOSURE: -#ifdef CHAINABLE_FUNCPROTO - // Only reachable through compiler address deref +#ifdef ACCESSIBLE_FUNCPROTO case OT_FUNCPROTO: #endif { -#ifdef CHAINABLE_FUNCPROTO +#ifdef ACCESSIBLE_FUNCPROTO SQFunctionProto *func = ( sq_type(target) == OT_CLOSURE ) ? _fp(_closure(target)->_function) : _funcproto(target); @@ -15026,7 +15162,7 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq } else { - buf.PutHex( nparams, false ); + buf.PutHex( (unsigned int)nparams, false ); } buf.Puts("..."); @@ -15071,18 +15207,36 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq SetVirtualHint( elem ); } -#ifdef CHAINABLE_FUNCPROTO +#ifdef SQDBG_SUPPORTS_FUNCPROTO_LIST + if ( func->_nfunctions ) + { + wjson_table_t elem = variables.AppendTable(); + elem.SetString( "name", INTERNAL_TAG("functions") ); + elem.SetString( "value", GetValue( func->_nfunctions, flags ) ); + elem.SetInt( "variablesReference", ToVarRef( VARREF_FUNCTIONS, ToSQObject( func ) ) ); + SetVirtualHint( elem ); + } +#endif + + if ( func->_noutervalues ) + { + wjson_table_t elem = variables.AppendTable(); + elem.SetString( "name", INTERNAL_TAG("outervalues") ); + elem.SetString( "value", GetValue( func->_noutervalues, flags ) ); +#ifdef ACCESSIBLE_FUNCPROTO + int v = ( sq_type(target) == OT_CLOSURE ) ? ToVarRef( VARREF_OUTERS, target ) : -1; + elem.SetInt( "variablesReference", v ); +#else + Assert( sq_type(target) == OT_CLOSURE ); + elem.SetInt( "variablesReference", ToVarRef( VARREF_OUTERS, target ) ); +#endif + SetVirtualHint( elem ); + } + +#ifdef ACCESSIBLE_FUNCPROTO if ( sq_type(target) == OT_CLOSURE ) #endif { - if ( func->_noutervalues ) - { - wjson_table_t elem = variables.AppendTable(); - elem.SetString( "name", INTERNAL_TAG("outervalues") ); - elem.SetString( "value", GetValue( func->_noutervalues, flags ) ); - elem.SetInt( "variablesReference", ToVarRef( VARREF_OUTERS, target ) ); - SetVirtualHint( elem ); - } #ifdef CLOSURE_ENV_ISVALID if ( CLOSURE_ENV_ISVALID( _closure(target)->_env ) ) { @@ -15140,7 +15294,7 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq } else { - buf.PutHex( nparams < 0 ? -nparams : nparams, false ); + buf.PutHex( (unsigned int)( nparams < 0 ? -nparams : nparams ), false ); } if ( nparams < 0 ) @@ -15486,6 +15640,40 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq break; } +#ifdef SQDBG_SUPPORTS_FUNCPROTO_LIST + case VARREF_FUNCTIONS: + { + SQObject target = ref->GetVar(); + + if ( sq_type(target) != OT_FUNCPROTO ) + { + DAP_ERROR_RESPONSE( seq, "variables" ); + DAP_ERROR_BODY( 0, "invalid object" ); + DAP_SEND(); + return; + } + + SQFunctionProto *func = _funcproto(target); + + DAP_START_RESPONSE( seq, "variables" ); + DAP_SET_TABLE( body ); + wjson_array_t variables = body.SetArray( "variables" ); + + for ( int i = 0; i < func->_nfunctions; i++ ) + { + const SQObjectPtr &val = func->_functions[i]; + + wjson_table_t elem = variables.AppendTable(); + elem.SetIntString( "name", i ); + JSONSetString( elem, "value", val, flags ); + elem.SetString( "type", GetType( val ) ); + elem.SetInt( "variablesReference", ToVarRef( val ) ); + } + DAP_SEND(); + + break; + } +#endif case VARREF_METAMETHODS: { SQObject target = ref->GetVar(); @@ -15673,69 +15861,107 @@ void SQDebugServer::OnRequest_Variables( const json_table_t &arguments, int seq DAP_SET_TABLE( body ); wjson_array_t variables = body.SetArray( "variables" ); - int stackbase = -1; - int callframe = 0; + int stackbase = 0; + int callframe = -1; + int next = vm->_callsstacksize ? vm->_callsstack[0]._prevstkbase : 0; + int idx = 0; + const int max = vm->_stack.size(); - if ( vm->_callsstacksize ) - stackbase = vm->_callsstack[callframe]._prevstkbase; - - for ( int i = 0; i < (int)vm->_stack.size(); i++ ) + for (;;) { - const SQObjectPtr &val = vm->_stack._vals[i]; + callframe++; - if ( i > vm->_top && sq_type(val) == OT_NULL ) - continue; - - wjson_table_t elem = variables.AppendTable(); + if ( callframe < vm->_callsstacksize ) { - jstringbuf_t name = elem.SetStringAsBuf( "name" ); - name.Put('['); + stackbase = next; - if ( !( flags & kFS_Hexadecimal ) ) + if ( callframe + 1 < vm->_callsstacksize ) { - name.PutInt( i ); + next = stackbase + vm->_callsstack[ callframe + 1 ]._prevstkbase; } else { - name.PutHex( i, false ); - } - - name.Put(']'); - - if ( stackbase == i ) - { - name.Put('*'); - - if ( callframe == frame ) - { - name.Put('~'); - } -#ifndef NATIVE_DEBUG_HOOK - else if ( m_State == ThreadState_Suspended && - callframe == vm->_callsstacksize - 1 ) - { - if ( sq_type(vm->_callsstack[callframe]._closure) == OT_NATIVECLOSURE && - _nativeclosure(vm->_callsstack[callframe]._closure)->_function == - &SQDebugServer::SQDebugHook ) - { - name.Put('d'); - } - } -#endif - - if ( ++callframe < vm->_callsstacksize ) - stackbase += vm->_callsstack[callframe]._prevstkbase; - } - else if ( vm->_top == i ) - { - name.Put('_'); + next = max; } } - JSONSetString( elem, "value", val, flags ); - elem.SetString( "type", GetType( val ) ); - elem.SetInt( "variablesReference", ToVarRef( val ) ); - if ( ShouldPageArray( val, 16 * ARRAY_PAGE_LIMIT ) ) - elem.SetInt( "indexedVariables", (int)_array(val)->_values.size() ); + else + { + if ( callframe ) + callframe--; + + next = max; + } + + for ( ; idx < next; idx++ ) + { + const SQObjectPtr &val = vm->_stack._vals[idx]; + + if ( idx > vm->_top && sq_type(val) == OT_NULL ) + continue; + + wjson_table_t elem = variables.AppendTable(); + { + jstringbuf_t name = elem.SetStringAsBuf( "name" ); + name.Put('['); + + if ( !( flags & kFS_Hexadecimal ) ) + { + name.PutInt( callframe ); + } + else + { + name.PutHex( (unsigned int)callframe, false ); + } + + name.Put(']'); + name.Put('['); + + if ( !( flags & kFS_Hexadecimal ) ) + { + name.PutInt( idx - stackbase ); + } + else + { + name.PutHex( (unsigned int)( idx - stackbase ), false ); + } + + name.Put(']'); + + if ( idx == stackbase ) + { + name.Put('*'); + + if ( callframe == frame ) + { + name.Put('~'); + } +#ifndef NATIVE_DEBUG_HOOK + else if ( m_State == ThreadState_Suspended && + callframe == vm->_callsstacksize - 1 ) + { + if ( sq_type(vm->_callsstack[callframe]._closure) == OT_NATIVECLOSURE && + _nativeclosure(vm->_callsstack[callframe]._closure)->_function == + &SQDebugServer::SQDebugHook ) + { + name.Put('d'); + } + } +#endif + } + else if ( vm->_top == idx ) + { + name.Put('_'); + } + } + JSONSetString( elem, "value", val, flags ); + elem.SetString( "type", GetType( val ) ); + elem.SetInt( "variablesReference", ToVarRef( val ) ); + if ( ShouldPageArray( val, 16 * ARRAY_PAGE_LIMIT ) ) + elem.SetInt( "indexedVariables", (int)_array(val)->_values.size() ); + } + + if ( next == max ) + break; } DAP_SEND(); @@ -15865,7 +16091,7 @@ void SQDebugServer::OnRequest_SetVariable( const json_table_t &arguments, int se DAP_ERROR_BODY( 0, "could not set '{name}'" ); error.SetBool( "showUser", true ); wjson_table_t variables = error.SetTable( "variables" ); - JSONSetString( variables, "name", obj.key ); + variables.SetString( "name", strName ); DAP_SEND(); return; } @@ -16342,7 +16568,7 @@ void SQDebugServer::OnRequest_Disassemble( const json_table_t &arguments, int se #ifdef SUPPORTS_RESTART_FRAME void SQDebugServer::OnRequest_RestartFrame( const json_table_t &arguments, int seq ) { - Assert( m_State == ThreadState_Suspended ); + AssertClient( m_State != ThreadState_Running ); HSQUIRRELVM vm; int frame; @@ -16846,7 +17072,7 @@ bool SQDebugServer::InstructionStep( HSQUIRRELVM vm, SQVM::CallInfo *ci, int ins SQInstruction *instrEnd = func->_instructions + func->_ninstructions; SQInstruction *ip = ci->_ip - instrOffset; - Assert( ip >= func->_instructions && ip < instrEnd ); + Assert( ip >= func->_instructions ); { for (;;) @@ -16973,7 +17199,7 @@ bool SQDebugServer::Step( HSQUIRRELVM vm, SQVM::CallInfo *ci ) ClearCachedInstructions(); SQFunctionProto *func = _fp(_closure(ci->_closure)->_function); - Assert( ci->_ip >= func->_instructions && ci->_ip < func->_instructions + func->_ninstructions ); + Assert( ci->_ip >= func->_instructions ); // Check if this function was compiled with debug info // The first op is going to be a line op @@ -17000,7 +17226,7 @@ bool SQDebugServer::Step( HSQUIRRELVM vm, SQVM::CallInfo *ci ) _CacheInstruction( func, func->_instructions + op ); // Set break point at every possible jump target - for ( SQInstruction *ip = ci->_ip + 1; ip <= func->_instructions + op; ip++ ) + for ( SQInstruction *ip = ci->_ip; ip <= func->_instructions + op; ip++ ) { if ( IsJumpOp( ip ) && GetJumpCount( ip ) != 0 ) { @@ -17040,6 +17266,14 @@ void SQDebugServer::CacheInstruction( SQInstruction *instr ) void SQDebugServer::ClearCachedInstructions() { +#ifdef SQDBG_WEAK_INSTRUCTION_REF + for ( int i = m_CachedInstructions.Size(); i--; ) + { + cachedinstr_t &cached = m_CachedInstructions[i]; + __ObjRelease( cached.func ); + } +#endif + m_CachedInstructions.Clear(); } @@ -17050,10 +17284,7 @@ void SQDebugServer::RestoreCachedInstructions() cachedinstr_t &cached = m_CachedInstructions[i]; #ifdef SQDBG_WEAK_INSTRUCTION_REF if ( cached.func && sq_type(cached.func->_obj) == OT_FUNCPROTO ) - { _funcproto(cached.func->_obj)->_instructions[ cached.index ] = cached.instr; - __ObjRelease( cached.func ); - } #else if ( cached.ip ) *cached.ip = cached.instr; @@ -17068,10 +17299,7 @@ void SQDebugServer::UndoRestoreCachedInstructions() cachedinstr_t &cached = m_CachedInstructions[i]; #ifdef SQDBG_WEAK_INSTRUCTION_REF if ( cached.func && sq_type(cached.func->_obj) == OT_FUNCPROTO ) - { memzero( &_funcproto(cached.func->_obj)->_instructions[ cached.index ] ); - __ObjAddRef( cached.func ); - } #else if ( cached.ip ) memzero( cached.ip ); @@ -17337,7 +17565,15 @@ int SQDebugServer::AddFunctionBreakpoint( const string_t &func, const string_t & SQChar *pSrc = pFunc + funcsize; sqstring_t wfunc, wsrc; - wfunc.Assign( pFunc, UTF8ToSQUnicode( pFunc, funcsize, func.ptr, func.len ) ); + + if ( func.IsEmpty() ) + { + wfunc.Assign( _SC("") ); + } + else + { + wfunc.Assign( pFunc, UTF8ToSQUnicode( pFunc, funcsize, func.ptr, func.len ) ); + } if ( funcsrc.IsEmpty() ) { @@ -17828,6 +18064,10 @@ sqstring_t SQDebugServer::PrintDisassembly( SQClosure *target, SQChar *scratch, int idx; if ( func->_ndefaultparams && ( idx = (int)func->_ndefaultparams - ( nparams - i ) ) >= 0 ) { + len = STRLEN(" = "); + memcpy( buf, _SC(" = "), sq_rsl(len) ); + buf += len; + const SQObjectPtr &val = target->_defaultparams[idx]; string_t str; @@ -17838,19 +18078,39 @@ sqstring_t SQDebugServer::PrintDisassembly( SQClosure *target, SQChar *scratch, case OT_BOOL: case OT_NULL: case OT_STRING: - str = GetValue( val, kFS_NoAddr ); + str = GetValue( val ); + len = min( str.len, DISASM_MAX_PARAM_NAME_LEN - 3 ); break; + case OT_CLASS: + case OT_INSTANCE: + { + const classdef_t *def = + FindClassDef( sq_type(val) == OT_CLASS ? _class(val) : _instance(val)->_class ); + + if ( def && def->name.ptr ) + { + str = GetType( val ); + len = min( (int)str.len, DISASM_MAX_PARAM_NAME_LEN - 4 ); +#ifdef SQUNICODE + UTF8ToSQUnicode( buf, _bs, str.ptr, len ); +#else + memcpy( buf, str.ptr, sq_rsl(len) ); +#endif + buf += len; + *buf++ = ' '; + + str.Assign( def->name.ptr + FMT_PTR_LEN + 1, def->name.len - FMT_PTR_LEN - 1 ); + len = min( (int)str.len, DISASM_MAX_PARAM_NAME_LEN - 4 - len ); + break; + } + } default: str = GetType( val ); + len = min( (int)str.len, DISASM_MAX_PARAM_NAME_LEN - 3 ); } - len = STRLEN(" = "); - memcpy( buf, _SC(" = "), sq_rsl(len) ); - buf += len; - - len = min( str.len, DISASM_MAX_PARAM_NAME_LEN - 3 ); #ifdef SQUNICODE - UTF8ToSQUnicode( buf, _bs, str.ptr, str.len ); + UTF8ToSQUnicode( buf, _bs, str.ptr, len ); #else memcpy( buf, str.ptr, sq_rsl(len) ); #endif @@ -18872,51 +19132,54 @@ void SQDebugServer::DebugHook( HSQUIRRELVM vm, int type, { if ( m_pCurVM->_suspended ) { - SQInstruction *pip = m_pCurVM->ci->_ip - 1; - - if ( pip->op == _OP_CALL ) + if ( m_pCurVM->ci->_ip ) { - const SQObjectPtr &val = m_pCurVM->_stack._vals[ m_pCurVM->_stackbase + pip->_arg1 ]; - if ( sq_type(val) == OT_NATIVECLOSURE && - sq_type(_nativeclosure(val)->_name) == OT_STRING && - IsEqual( _SC("suspend"), _string(_nativeclosure(val)->_name) ) ) + SQInstruction *pip = m_pCurVM->ci->_ip - 1; + + if ( pip->op == _OP_CALL ) { - m_nCalls -= (int)( m_pCurVM->ci - m_pCurVM->_callsstack ) + 1; - - switch ( m_State ) + const SQObjectPtr &val = m_pCurVM->_stack._vals[ m_pCurVM->_stackbase + pip->_arg1 ]; + if ( sq_type(val) == OT_NATIVECLOSURE && + sq_type(_nativeclosure(val)->_name) == OT_STRING && + IsEqual( _SC("suspend"), _string(_nativeclosure(val)->_name) ) ) { - case ThreadState_StepOut: + m_nCalls -= (int)( m_pCurVM->ci - m_pCurVM->_callsstack ) + 1; - if ( m_pStateVM != m_pCurVM ) + switch ( m_State ) + { + case ThreadState_StepOut: + + if ( m_pStateVM != m_pCurVM ) + break; + + case ThreadState_StepOver: + + m_State = ThreadState_StepIn; break; - case ThreadState_StepOver: + case ThreadState_StepOutInstruction: - m_State = ThreadState_StepIn; - break; + if ( m_pStateVM != m_pCurVM ) + break; - case ThreadState_StepOutInstruction: + case ThreadState_StepOverInstruction: + case ThreadState_StepInInstruction: - if ( m_pStateVM != m_pCurVM ) + RestoreCachedInstructions(); + ClearCachedInstructions(); + + if ( InstructionStep( vm, ci, 2 ) ) + { + m_State = ThreadState_StepInInstruction; + } + else + { + m_State = ThreadState_NextStatement; + } + + default: break; - - case ThreadState_StepOverInstruction: - case ThreadState_StepInInstruction: - - RestoreCachedInstructions(); - ClearCachedInstructions(); - - if ( InstructionStep( vm, ci, 2 ) ) - { - m_State = ThreadState_StepInInstruction; - } - else - { - m_State = ThreadState_NextStatement; - } - - default: - break; + } } } } @@ -18928,7 +19191,7 @@ void SQDebugServer::DebugHook( HSQUIRRELVM vm, int type, ( IsEqual( _SC("wakeup"), _string(_nativeclosure(m_pCurVM->ci->_closure)->_name) ) || IsEqual( _SC("call"), _string(_nativeclosure(m_pCurVM->ci->_closure)->_name) ) ) ) { - m_nCalls += (int)( vm->ci - vm->_callsstack ) + 1; + m_nCalls += (int)( vm->ci - vm->_callsstack ) + ( type != SQ_HOOK_CALL ); } } } @@ -20302,7 +20565,7 @@ HSQDEBUGSERVER sqdbg_attach_debugger( HSQUIRRELVM vm ) { CDebuggerScriptRef *ref = NULL; - CStackCheck stackcheck( vm ); + STACKCHECK( vm ); sq_pushregistrytable( vm ); sq_pushstring( vm, _SC(SQDBG_SV_TAG), -1 ); @@ -20366,7 +20629,7 @@ HSQDEBUGSERVER sqdbg_attach_debugger( HSQUIRRELVM vm ) void sqdbg_destroy_debugger( HSQUIRRELVM vm ) { - CStackCheck stackcheck( vm ); + STACKCHECK( vm ); sq_pushregistrytable( vm ); sq_pushstring( vm, _SC(SQDBG_SV_TAG), -1 ); diff --git a/sp/src/vscript/sqdbg/sqdbg/str.h b/sp/src/vscript/sqdbg/sqdbg/str.h index e68b8866..c06e1720 100644 --- a/sp/src/vscript/sqdbg/sqdbg/str.h +++ b/sp/src/vscript/sqdbg/sqdbg/str.h @@ -75,6 +75,21 @@ bool atox( string_t str, I *out ); template < typename I > bool atoo( string_t str, I *out ); +template < typename I > +struct _as_unsigned { typedef I T; }; + +template <> +struct _as_unsigned< int > { typedef unsigned int T; }; + +#ifdef _SQ64 +template <> +struct _as_unsigned< SQInteger > { typedef SQUnsignedInteger T; }; +#endif + +#define as_unsigned_type( I ) typename _as_unsigned::T +#define cast_unsigned( I, v ) (as_unsigned_type(I)(v)) +#define IS_UNSIGNED( I ) ((I)0 < (I)-1) + #define _isdigit( c ) \ IN_RANGE_CHAR( c, '0', '9' ) @@ -403,9 +418,10 @@ struct sqstring_t unsigned int i = 0; do { - if ( ptr[i] > 0x7E || other.ptr[i] != (char)ptr[i] ) + if ( (SQUnsignedChar)ptr[i] > 0x7E || other.ptr[i] != (char)ptr[i] ) { - AssertMsg( ptr[i] <= 0x7E, "not implemented" ); + // > 0x7E can be reached through completions request + // unicode identifiers are not supported, ignore them return false; } } @@ -556,6 +572,8 @@ struct stringbufbase_t template < typename I > void PutHex( I value, bool padding = true ) { + STATIC_ASSERT( IS_UNSIGNED( I ) ); + int space = BytesLeft(); if ( space < 3 ) @@ -590,9 +608,9 @@ bool string_t::IsEqualTo( const sqstring_t &other ) const { // Used for comparing against locals and outers, // implement unicode conversion if locals can have unicode characters - if ( other.ptr[i] > 0x7E || (char)other.ptr[i] != ptr[i] ) + if ( (SQUnsignedChar)other.ptr[i] > 0x7E || (char)other.ptr[i] != ptr[i] ) { - AssertMsg( other.ptr[i] <= 0x7E, "not implemented" ); + AssertMsg( (SQUnsignedChar)other.ptr[i] <= 0x7E, "not implemented" ); return false; } } @@ -675,7 +693,7 @@ inline int printint( C *buf, int size, I value ) value /= 10; buf[i--] = !neg ? ( '0' + c ) : ( '0' - c ); } - while ( value && i >= 0 ); + while ( value ); return len; } @@ -683,10 +701,11 @@ inline int printint( C *buf, int size, I value ) template < bool padding, bool prefix, bool uppercase, typename C, typename I > inline int printhex( C *buf, int size, I value ) { + STATIC_ASSERT( IS_UNSIGNED( as_unsigned_type( I ) ) ); Assert( buf ); Assert( size > 0 ); - int len = ( prefix ? 2 : 0 ) + ( padding ? sizeof(I) * 2 : countdigits<16>( value ) ); + int len = ( prefix ? 2 : 0 ) + ( padding ? sizeof(I) * 2 : countdigits<16>( cast_unsigned( I, value ) ) ); if ( len > size ) len = size; @@ -695,11 +714,11 @@ inline int printhex( C *buf, int size, I value ) do { - C c = value & 0xf; - value >>= 4; + C c = cast_unsigned( I, value ) & 0xf; + *(as_unsigned_type(I)*)&value >>= 4; buf[i--] = c + ( ( c < 10 ) ? '0' : ( ( uppercase ? 'A' : 'a' ) - 10 ) ); } - while ( value && i >= 0 ); + while ( value ); if ( padding ) { @@ -725,6 +744,7 @@ inline int printhex( C *buf, int size, I value ) template < typename C, typename I > inline int printoct( C *buf, int size, I value ) { + STATIC_ASSERT( IS_UNSIGNED( I ) ); Assert( buf ); Assert( size > 0 ); @@ -741,7 +761,7 @@ inline int printoct( C *buf, int size, I value ) value >>= 3; buf[i--] = '0' + c; } - while ( value && i >= 0 ); + while ( value ); if ( i >= 0 ) buf[i--] = '0'; @@ -1302,7 +1322,7 @@ doescape: else { mbc[bytes++] = 'x'; - bytes += printhex< true, false >( mbc + bytes, sizeof(mbc) - bytes, (SQChar)cp ); + bytes += printhex< true, false >( mbc + bytes, sizeof(mbc) - bytes, (SQUnsignedChar)cp ); } goto write; @@ -1339,7 +1359,7 @@ x7ff: mbc[bytes++] = '\\'; mbc[bytes++] = 'x'; - bytes = bytes + printhex< true, false >( mbc + bytes, sizeof(mbc) - bytes, (SQChar)cp ); + bytes = bytes + printhex< true, false >( mbc + bytes, sizeof(mbc) - bytes, (SQUnsignedChar)cp ); goto write; } } diff --git a/sp/src/vscript/sqdbg/sqdbg/vec.h b/sp/src/vscript/sqdbg/sqdbg/vec.h index a6073acb..a8c7488e 100644 --- a/sp/src/vscript/sqdbg/sqdbg/vec.h +++ b/sp/src/vscript/sqdbg/sqdbg/vec.h @@ -306,7 +306,7 @@ public: void Free( void *ptr ) { - Assert( !SEQUENTIAL ); + STATIC_ASSERT( !SEQUENTIAL ); Assert( m_Memory ); Assert( ptr ); @@ -364,7 +364,7 @@ public: void ReleaseShrink() { - Assert( SEQUENTIAL ); + STATIC_ASSERT( SEQUENTIAL ); if ( !m_Memory ) return; @@ -412,7 +412,7 @@ public: void Release() { - Assert( SEQUENTIAL ); + STATIC_ASSERT( SEQUENTIAL ); if ( !m_Memory || ( !m.LastFreeChunk() && !m.LastFreeIndex() ) ) return; @@ -437,7 +437,7 @@ public: void ReleaseTop() { - Assert( SEQUENTIAL ); + STATIC_ASSERT( SEQUENTIAL ); m.SetLastFreeChunk( m.PrevChunk() ); m.SetLastFreeIndex( m.PrevIndex() ); @@ -455,23 +455,23 @@ public: vector() : base(), size(0) { - Assert( !bExternalMem ); + STATIC_ASSERT( !bExternalMem ); } vector( CAllocator &a ) : base(a), size(0) { - Assert( bExternalMem ); + STATIC_ASSERT( bExternalMem ); } vector( I count ) : base(), size(0) { - Assert( !bExternalMem ); + STATIC_ASSERT( !bExternalMem ); base.Alloc( count * sizeof(T) ); } vector( const vector< T > &src ) : base() { - Assert( !bExternalMem ); + STATIC_ASSERT( !bExternalMem ); base.Alloc( src.base.Size() ); size = src.size; From 07ab21e2d5fbefaa06350f6e0055f0608460fdfd Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Tue, 3 Jun 2025 19:05:17 +0300 Subject: [PATCH 22/42] Remove invalid assertions --- sp/src/vscript/vscript_squirrel.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/sp/src/vscript/vscript_squirrel.cpp b/sp/src/vscript/vscript_squirrel.cpp index 93008683..bf8379ea 100644 --- a/sp/src/vscript/vscript_squirrel.cpp +++ b/sp/src/vscript/vscript_squirrel.cpp @@ -3367,7 +3367,8 @@ void SquirrelVM::WriteObject( const SQObjectPtr &obj, CUtlBuffer* pBuffer, Write #ifdef _DEBUG bool bAsserted = false; - if ( pThis->_noutervalues && pThis->_name._type == OT_STRING && pThis->_name._unVal.pString ) + if ( pThis->_noutervalues && pThis->_name._type == OT_STRING && pThis->_name._unVal.pString && + pThis->_outervalues[0]._type == OT_USERPOINTER ) { Assert( pThis->_noutervalues == 1 ); Assert( pThis->_outervalues[0]._type == OT_USERPOINTER ); @@ -3771,7 +3772,6 @@ void SquirrelVM::WriteObject( const SQObjectPtr &obj, CUtlBuffer* pBuffer, Write } case OT_USERDATA: case OT_USERPOINTER: - Assert(0); break; default: AssertMsgAlways( 0, "SquirrelVM::WriteObject: unknown type" ); @@ -4458,10 +4458,7 @@ void SquirrelVM::ReadObject( SQObjectPtr &pObj, CUtlBuffer* pBuffer, ReadStateMa } case OT_USERDATA: case OT_USERPOINTER: - { - Assert(0); break; - } default: AssertMsgAlways( 0, "SquirrelVM::ReadObject: serialisation error" ); } From 776c4e78bcc8cbf47ea8a6e3035636e2e1a27fbb Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Tue, 3 Jun 2025 19:05:25 +0300 Subject: [PATCH 23/42] Fix suspended Squirrel thread and generator save/restore --- sp/src/vscript/vscript_squirrel.cpp | 182 +++++++++++++++++----------- 1 file changed, 109 insertions(+), 73 deletions(-) diff --git a/sp/src/vscript/vscript_squirrel.cpp b/sp/src/vscript/vscript_squirrel.cpp index bf8379ea..7c1ac92f 100644 --- a/sp/src/vscript/vscript_squirrel.cpp +++ b/sp/src/vscript/vscript_squirrel.cpp @@ -284,6 +284,14 @@ public: WriteObject( (const SQObjectPtr&)obj, pBuffer, writeState ); } + void WriteObject( SQGenerator *pObj, CUtlBuffer* pBuffer, WriteStateMap& writeState ) + { + SQObject obj; + obj._type = OT_GENERATOR; + obj._unVal.pUserPointer = pObj; + WriteObject( (const SQObjectPtr&)obj, pBuffer, writeState ); + } + void ReadObject( SQObjectPtr &obj, CUtlBuffer* pBuffer, ReadStateMap& readState ); // Do not implicity add/remove ref @@ -3671,23 +3679,25 @@ void SquirrelVM::WriteObject( const SQObjectPtr &obj, CUtlBuffer* pBuffer, Write if ( pThis->_callsstacksize ) { - int stackidx = -1; - - for ( int i = pThis->_callsstacksize; i--; ) + for ( int i = 0; i < pThis->_callsstacksize; i++ ) { const SQVM::CallInfo *ci = &pThis->_callsstack[i]; - if ( pThis->ci == ci ) - stackidx = i; - - Assert( !ci->_generator ); - Assert( ci->_ip && ci->_ip >= ci->_closure._unVal.pClosure->_function->_instructions ); + Assert( ci->_ip >= ci->_closure._unVal.pClosure->_function->_instructions && + ci->_ip < ci->_closure._unVal.pClosure->_function->_instructions + + ci->_closure._unVal.pClosure->_function->_ninstructions ); Assert( pThis->_etraps.size() >= (SQUnsignedInteger)ci->_etraps ); Assert( ci->_closure._type == OT_CLOSURE && ci->_closure._unVal.pClosure ); WriteObject( ci->_closure, pBuffer, writeState ); - int offset = (int)ci->_ip - (int)ci->_closure._unVal.pClosure->_function->_instructions; + Assert( ci->_ip - ci->_closure._unVal.pClosure->_function->_instructions <= INT_MAX ); + + pBuffer->PutChar( ci->_generator != 0 ); + if ( ci->_generator ) + WriteObject( ci->_generator, pBuffer, writeState ); + + int offset = ci->_ip - ci->_closure._unVal.pClosure->_function->_instructions; pBuffer->PutInt( offset ); pBuffer->PutInt( ci->_etraps ); pBuffer->PutInt( ci->_prevstkbase ); @@ -3696,16 +3706,18 @@ void SquirrelVM::WriteObject( const SQObjectPtr &obj, CUtlBuffer* pBuffer, Write pBuffer->PutInt( ci->_ncalls ); pBuffer->PutChar( ci->_root ); - for ( int j = ci->_etraps; j--; ) + for ( int j = 0; j < ci->_etraps; j++ ) { const SQExceptionTrap &et = pThis->_etraps[j]; - pBuffer->PutInt( et._extarget ); - pBuffer->PutInt( et._stackbase ); pBuffer->PutInt( et._stacksize ); - Assert( et._ip == ci->_ip ); + pBuffer->PutInt( et._stackbase ); + Assert( et._ip - ci->_ip <= INT_MAX ); + pBuffer->PutInt( et._ip - ci->_ip ); + pBuffer->PutInt( et._extarget ); } } + int stackidx = pThis->ci - pThis->_callsstack; Assert( stackidx >= 0 && stackidx < pThis->_callsstacksize ); pBuffer->PutInt( stackidx ); } @@ -3736,29 +3748,37 @@ void SquirrelVM::WriteObject( const SQObjectPtr &obj, CUtlBuffer* pBuffer, Write WriteObject( pThis->_closure, pBuffer, writeState ); - const SQVM::CallInfo &ci = pThis->_ci; + const SQVM::CallInfo *ci = &pThis->_ci; - Assert( !ci._generator ); - Assert( pThis->_closure._unVal.pClosure == ci._closure._unVal.pClosure ); - Assert( ci._ip && ci._ip >= ci._closure._unVal.pClosure->_function->_instructions ); - Assert( pThis->_etraps.size() >= (SQUnsignedInteger)ci._etraps ); + Assert( pThis->_closure._unVal.pClosure == ci->_closure._unVal.pClosure ); + Assert( ci->_ip >= ci->_closure._unVal.pClosure->_function->_instructions && + ci->_ip < ci->_closure._unVal.pClosure->_function->_instructions + + ci->_closure._unVal.pClosure->_function->_ninstructions ); + Assert( pThis->_etraps.size() >= (SQUnsignedInteger)ci->_etraps ); - int offset = (int)ci._ip - (int)ci._closure._unVal.pClosure->_function->_instructions; + Assert( ci->_ip - ci->_closure._unVal.pClosure->_function->_instructions <= INT_MAX ); + + pBuffer->PutChar( ci->_generator != 0 ); + if ( ci->_generator ) + WriteObject( ci->_generator, pBuffer, writeState ); + + int offset = ci->_ip - ci->_closure._unVal.pClosure->_function->_instructions; pBuffer->PutInt( offset ); - pBuffer->PutInt( ci._etraps ); - pBuffer->PutInt( ci._prevstkbase ); - pBuffer->PutInt( ci._prevtop ); - pBuffer->PutInt( ci._target ); - pBuffer->PutInt( ci._ncalls ); - pBuffer->PutChar( ci._root ); + pBuffer->PutInt( ci->_etraps ); + pBuffer->PutInt( ci->_prevstkbase ); + pBuffer->PutInt( ci->_prevtop ); + pBuffer->PutInt( ci->_target ); + pBuffer->PutInt( ci->_ncalls ); + pBuffer->PutChar( ci->_root ); - for ( int j = ci._etraps; j--; ) + for ( int j = 0; j < ci->_etraps; j++ ) { const SQExceptionTrap &et = pThis->_etraps[j]; - pBuffer->PutInt( et._extarget ); - pBuffer->PutInt( et._stackbase ); pBuffer->PutInt( et._stacksize ); - Assert( et._ip == ci._ip ); + pBuffer->PutInt( et._stackbase ); + Assert( et._ip - ci->_ip <= INT_MAX ); + pBuffer->PutInt( et._ip - ci->_ip ); + pBuffer->PutInt( et._extarget ); } int stacksize = pThis->_stack.size(); @@ -4320,10 +4340,10 @@ void SquirrelVM::ReadObject( SQObjectPtr &pObj, CUtlBuffer* pBuffer, ReadStateMa if ( pThis->_callsstacksize ) { - if ( pThis->_callsstacksize >= pThis->_alloccallsstacksize ) + while ( pThis->_callsstacksize >= pThis->_alloccallsstacksize ) pThis->GrowCallStack(); - for ( int i = pThis->_callsstacksize; i--; ) + for ( int i = 0; i < pThis->_callsstacksize; i++ ) { SQVM::CallInfo *ci = &pThis->_callsstack[i]; @@ -4331,23 +4351,31 @@ void SquirrelVM::ReadObject( SQObjectPtr &pObj, CUtlBuffer* pBuffer, ReadStateMa ReadObject( closure, pBuffer, readState ); Assert( closure._type == OT_CLOSURE && closure._unVal.pClosure ); - int offset = pBuffer->GetInt(); - int funcsize = sizeof(SQInstruction) * closure._unVal.pClosure->_function->_ninstructions; - int start = (int)(closure._unVal.pClosure->_function->_instructions); - int pos = start + offset; - ci->_ip = (SQInstruction*)pos; - - Assert( pos < (start + funcsize) ); - - // don't read past boundary - if ( pos >= (start + funcsize) ) + if ( pBuffer->GetChar() ) { - ci->_ip = (SQInstruction*)start; + SQObject generator; + ReadObject( generator, pBuffer, readState ); + Assert( generator._type == OT_GENERATOR && generator._unVal.pGenerator ); + ci->_generator = generator._unVal.pGenerator; + } + else + { + ci->_generator = NULL; } + int offset = pBuffer->GetInt(); + SQInstruction *start = closure._unVal.pClosure->_function->_instructions; + SQInstruction *end = start + closure._unVal.pClosure->_function->_ninstructions; + SQInstruction *pos = start + offset; + + Assert( pos >= start && pos < end ); + + if ( pos < start || pos >= end ) + pos = start; + + ci->_ip = pos; ci->_literals = closure._unVal.pClosure->_function->_literals; ci->_closure = closure; - ci->_generator = NULL; ci->_etraps = pBuffer->GetInt(); ci->_prevstkbase = pBuffer->GetInt(); ci->_prevtop = pBuffer->GetInt(); @@ -4357,13 +4385,13 @@ void SquirrelVM::ReadObject( SQObjectPtr &pObj, CUtlBuffer* pBuffer, ReadStateMa pThis->_etraps.resize( ci->_etraps ); - for ( int j = ci->_etraps; j--; ) + for ( int j = 0; j < ci->_etraps; j++ ) { SQExceptionTrap &et = pThis->_etraps[j]; - et._extarget = pBuffer->GetInt(); - et._stackbase = pBuffer->GetInt(); et._stacksize = pBuffer->GetInt(); - et._ip = ci->_ip; + et._stackbase = pBuffer->GetInt(); + et._ip = ci->_ip + pBuffer->GetInt(); + et._extarget = pBuffer->GetInt(); } } @@ -4410,41 +4438,49 @@ void SquirrelVM::ReadObject( SQObjectPtr &pObj, CUtlBuffer* pBuffer, ReadStateMa pThis->_state = (SQGenerator::SQGeneratorState)state; - SQVM::CallInfo &ci = pThis->_ci; + SQVM::CallInfo *ci = &pThis->_ci; - int offset = pBuffer->GetInt(); - int funcsize = sizeof(SQInstruction) * closure._unVal.pClosure->_function->_ninstructions; - int start = (int)(closure._unVal.pClosure->_function->_instructions); - int pos = start + offset; - ci._ip = (SQInstruction*)pos; - - Assert( pos < (start + funcsize) ); - - // don't read past boundary - if ( pos >= (start + funcsize) ) + if ( pBuffer->GetChar() ) { - ci._ip = (SQInstruction*)start; + SQObject generator; + ReadObject( generator, pBuffer, readState ); + Assert( generator._type == OT_GENERATOR && generator._unVal.pGenerator ); + ci->_generator = generator._unVal.pGenerator; + } + else + { + ci->_generator = NULL; } - ci._literals = closure._unVal.pClosure->_function->_literals; - ci._closure = closure; - ci._generator = NULL; - ci._etraps = pBuffer->GetInt(); - ci._prevstkbase = pBuffer->GetInt(); - ci._prevtop = pBuffer->GetInt(); - ci._target = pBuffer->GetInt(); - ci._ncalls = pBuffer->GetInt(); - ci._root = pBuffer->GetChar(); + int offset = pBuffer->GetInt(); + SQInstruction *start = closure._unVal.pClosure->_function->_instructions; + SQInstruction *end = start + closure._unVal.pClosure->_function->_ninstructions; + SQInstruction *pos = start + offset; - pThis->_etraps.resize( ci._etraps ); + Assert( pos >= start && pos < end ); - for ( int j = ci._etraps; j--; ) + if ( pos < start || pos >= end ) + pos = start; + + ci->_ip = pos; + ci->_literals = closure._unVal.pClosure->_function->_literals; + ci->_closure = closure; + ci->_etraps = pBuffer->GetInt(); + ci->_prevstkbase = pBuffer->GetInt(); + ci->_prevtop = pBuffer->GetInt(); + ci->_target = pBuffer->GetInt(); + ci->_ncalls = pBuffer->GetInt(); + ci->_root = pBuffer->GetChar(); + + pThis->_etraps.resize( ci->_etraps ); + + for ( int j = 0; j < ci->_etraps; j++ ) { SQExceptionTrap &et = pThis->_etraps[j]; - et._extarget = pBuffer->GetInt(); - et._stackbase = pBuffer->GetInt(); et._stacksize = pBuffer->GetInt(); - et._ip = ci._ip; + et._stackbase = pBuffer->GetInt(); + et._ip = ci->_ip + pBuffer->GetInt(); + et._extarget = pBuffer->GetInt(); } int stacksize = pBuffer->GetInt(); From b431151bacad2c0bb90efa3192edc712685f9b86 Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Sun, 8 Jun 2025 13:21:44 +0300 Subject: [PATCH 24/42] Optimise string read in vscript save/restore --- sp/src/vscript/vscript_squirrel.cpp | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/sp/src/vscript/vscript_squirrel.cpp b/sp/src/vscript/vscript_squirrel.cpp index 7c1ac92f..f5379f0f 100644 --- a/sp/src/vscript/vscript_squirrel.cpp +++ b/sp/src/vscript/vscript_squirrel.cpp @@ -3835,26 +3835,10 @@ void SquirrelVM::ReadObject( SQObjectPtr &pObj, CUtlBuffer* pBuffer, ReadStateMa case OT_STRING: { int len = pBuffer->GetInt(); - char *psz; - - if ( len < 1024 ) - { - psz = (char*)stackalloc( len ); - } - else - { - psz = (char*)malloc( len ); - } - - pBuffer->Get( psz, len ); - + char *psz = (char*)pBuffer->PeekGet( 0 ); + pBuffer->SeekGet( CUtlBuffer::SEEK_CURRENT, len ); + Assert( pBuffer->IsValid() ); obj._unVal.pString = SQString::Create( _ss(vm_), psz, len ); - - if ( len >= 1024 ) - { - free( psz ); - } - break; } case OT_TABLE: From c851fc9bfbafb152a6a8ed91824475b15f7dd98f Mon Sep 17 00:00:00 2001 From: Alexander 'z33ky' Hirsch <1zeeky@gmail.com> Date: Wed, 11 Jun 2025 02:11:09 +0200 Subject: [PATCH 25/42] Ensure the squirrel instance for native member function call actually has userpointer type This prevents memory errors when incorrectly invoking native member functions from Squirrel. --- sp/src/vscript/vscript_squirrel.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/sp/src/vscript/vscript_squirrel.cpp b/sp/src/vscript/vscript_squirrel.cpp index 93008683..584402a1 100644 --- a/sp/src/vscript/vscript_squirrel.cpp +++ b/sp/src/vscript/vscript_squirrel.cpp @@ -1324,7 +1324,10 @@ SQInteger function_stub(HSQUIRRELVM vm) SQInteger top = sq_gettop(vm); SQUserPointer userptr = nullptr; - sq_getuserpointer(vm, top, &userptr); + if (SQ_FAILED(sq_getuserpointer(vm, top, &userptr))) + { + return sq_throwerror(vm, "Expected userpointer"); + } Assert(userptr); @@ -1425,7 +1428,10 @@ SQInteger function_stub(HSQUIRRELVM vm) if (pFunc->m_flags & SF_MEMBER_FUNC) { SQUserPointer self; - sq_getinstanceup(vm, 1, &self, nullptr); + if (SQ_FAILED(sq_getinstanceup(vm, 1, &self, 0))) + { + return sq_throwerror(vm, "Expected class userpointer"); + } if (!self) { From 9b8ba3ca6eb892330c9c00c446d4e5ed448ec5b6 Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Tue, 24 Jun 2025 15:31:56 +0300 Subject: [PATCH 26/42] Block script_connect_debugger_on_mapspawn on CScriptConvarAccessor --- sp/src/game/shared/mapbase/vscript_singletons.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sp/src/game/shared/mapbase/vscript_singletons.cpp b/sp/src/game/shared/mapbase/vscript_singletons.cpp index 1be1e97c..a7b28151 100644 --- a/sp/src/game/shared/mapbase/vscript_singletons.cpp +++ b/sp/src/game/shared/mapbase/vscript_singletons.cpp @@ -5146,6 +5146,11 @@ bool CScriptConvarAccessor::Init() AddBlockedConVar( "cl_allowdownload" ); AddBlockedConVar( "cl_allowupload" ); AddBlockedConVar( "cl_downloadfilter" ); +#ifdef GAME_DLL + AddBlockedConVar( "script_connect_debugger_on_mapspawn" ); +#else + AddBlockedConVar( "script_connect_debugger_on_mapspawn_client" ); +#endif return true; } From 681a75a6a708934a85b7acf3dd34d71a04690df6 Mon Sep 17 00:00:00 2001 From: Alexander 'z33ky' Hirsch <1zeeky@gmail.com> Date: Wed, 11 Jun 2025 02:14:13 +0200 Subject: [PATCH 27/42] Check ScriptClassDesc_t of squirrel instance for native member function call This enhances the ScriptFuncDescriptor_t to record the ScriptClassDesc_t of the class for which it gets invoked. The ScriptClassDesc_t are traversed in the function_stub() for native function calls for member functions, to ensure the passed in instance has a compatible type. This prevents memory errors when incorrectly invoking native member functions from Squirrel. --- sp/src/public/vscript/ivscript.h | 4 ++++ sp/src/public/vscript/vscript_templates.h | 2 +- sp/src/vscript/vscript_squirrel.cpp | 22 +++++++++++++++++----- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/sp/src/public/vscript/ivscript.h b/sp/src/public/vscript/ivscript.h index c0a0cf08..1eba404c 100644 --- a/sp/src/public/vscript/ivscript.h +++ b/sp/src/public/vscript/ivscript.h @@ -263,6 +263,8 @@ inline const char * ScriptFieldTypeName( int16 eType) //--------------------------------------------------------- +struct ScriptClassDesc_t; + struct ScriptFuncDescriptor_t { ScriptFuncDescriptor_t() @@ -270,11 +272,13 @@ struct ScriptFuncDescriptor_t m_pszFunction = NULL; m_ReturnType = FIELD_TYPEUNKNOWN; m_pszDescription = NULL; + m_pScriptClassDesc = NULL; } const char *m_pszScriptName; const char *m_pszFunction; const char *m_pszDescription; + ScriptClassDesc_t *m_pScriptClassDesc; ScriptDataType_t m_ReturnType; CUtlVector m_Parameters; }; diff --git a/sp/src/public/vscript/vscript_templates.h b/sp/src/public/vscript/vscript_templates.h index 9f2054cc..bda6bc9e 100644 --- a/sp/src/public/vscript/vscript_templates.h +++ b/sp/src/public/vscript/vscript_templates.h @@ -61,7 +61,7 @@ FUNC_GENERATE_ALL( DEFINE_MEMBER_FUNC_TYPE_DEDUCER ); FUNC_GENERATE_ALL( DEFINE_CONST_MEMBER_FUNC_TYPE_DEDUCER ); -#define ScriptInitMemberFuncDescriptor_( pDesc, class, func, scriptName ) if ( 0 ) {} else { (pDesc)->m_pszScriptName = scriptName; (pDesc)->m_pszFunction = #func; ScriptDeduceFunctionSignature( pDesc, (class *)(0), &class::func ); } +#define ScriptInitMemberFuncDescriptor_( pDesc, class, func, scriptName ) if ( 0 ) {} else { (pDesc)->m_pszScriptName = scriptName; (pDesc)->m_pszFunction = #func; ScriptDeduceFunctionSignature( pDesc, (class *)(0), &class::func ); (pDesc)->m_pScriptClassDesc = GetScriptDesc(nullptr); } #define ScriptInitFuncDescriptorNamed( pDesc, func, scriptName ) if ( 0 ) {} else { (pDesc)->m_pszScriptName = scriptName; (pDesc)->m_pszFunction = #func; ScriptDeduceFunctionSignature( pDesc, &func ); } #define ScriptInitFuncDescriptor( pDesc, func ) ScriptInitFuncDescriptorNamed( pDesc, func, #func ) diff --git a/sp/src/vscript/vscript_squirrel.cpp b/sp/src/vscript/vscript_squirrel.cpp index 584402a1..39139fec 100644 --- a/sp/src/vscript/vscript_squirrel.cpp +++ b/sp/src/vscript/vscript_squirrel.cpp @@ -1427,18 +1427,30 @@ SQInteger function_stub(HSQUIRRELVM vm) if (pFunc->m_flags & SF_MEMBER_FUNC) { - SQUserPointer self; - if (SQ_FAILED(sq_getinstanceup(vm, 1, &self, 0))) + ClassInstanceData* classInstanceData; + if (SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0))) { - return sq_throwerror(vm, "Expected class userpointer"); + return SQ_ERROR; } - if (!self) + if (!classInstanceData) { return sq_throwerror(vm, "Accessed null instance"); } - instance = ((ClassInstanceData*)self)->instance; + // check that the type of self, or any basetype, matches the function description + ScriptClassDesc_t *selfType = classInstanceData->desc; + while (selfType != pFunc->m_desc.m_pScriptClassDesc) + { + if (!selfType) + { + return sq_throwerror(vm, "Mismatched instance type"); + } + selfType = selfType->m_pBaseDesc; + Assert(selfType != classInstanceData->desc); // there should be no infinite loop + } + + instance = classInstanceData->instance; } ScriptVariant_t script_retval; From 655679e7da51e8e82af6f906e99b382a8e8eba44 Mon Sep 17 00:00:00 2001 From: Alexander 'z33ky' Hirsch <1zeeky@gmail.com> Date: Mon, 23 Jun 2025 20:49:23 +0200 Subject: [PATCH 28/42] Check type of SQVector construction instance This prevents manual invocations of the Vector.constructor with an invalid value. --- sp/src/vscript/vscript_squirrel.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/sp/src/vscript/vscript_squirrel.cpp b/sp/src/vscript/vscript_squirrel.cpp index 39139fec..f2a27755 100644 --- a/sp/src/vscript/vscript_squirrel.cpp +++ b/sp/src/vscript/vscript_squirrel.cpp @@ -327,7 +327,16 @@ namespace SQVector } SQUserPointer p; - sq_getinstanceup(vm, 1, &p, 0); + if (SQ_FAILED(sq_getinstanceup(vm, 1, &p, 0))) + { + return SQ_ERROR; + } + + if (!p) + { + return sq_throwerror(vm, "Accessed null instance"); + } + new (p) Vector(x, y, z); return 0; From ca7bc5da5718e864992f6be0a5c6e9b0e0dfa281 Mon Sep 17 00:00:00 2001 From: Alexander 'z33ky' Hirsch <1zeeky@gmail.com> Date: Mon, 23 Jun 2025 20:51:23 +0200 Subject: [PATCH 29/42] Guard Squirrel constructor_stub() invocations from invalid class parameters This prevents manual invocations of the native class constructor for non-class values or non-native classes. --- sp/src/vscript/vscript_squirrel.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sp/src/vscript/vscript_squirrel.cpp b/sp/src/vscript/vscript_squirrel.cpp index f2a27755..8acba22f 100644 --- a/sp/src/vscript/vscript_squirrel.cpp +++ b/sp/src/vscript/vscript_squirrel.cpp @@ -1548,7 +1548,15 @@ SQInteger destructor_stub_instance(SQUserPointer p, SQInteger size) SQInteger constructor_stub(HSQUIRRELVM vm) { ScriptClassDesc_t* pClassDesc = nullptr; - sq_gettypetag(vm, 1, (SQUserPointer*)&pClassDesc); + if (SQ_FAILED(sq_gettypetag(vm, 1, (SQUserPointer*)&pClassDesc))) + { + return sq_throwerror(vm, "Expected native class"); + } + + if (!pClassDesc || (void*)pClassDesc == TYPETAG_VECTOR) + { + return sq_throwerror(vm, "Unable to obtain native class description"); + } if (!pClassDesc->m_pfnConstruct) { From 9c494b6eebfa5c2baec7dae907e5af7b455e0839 Mon Sep 17 00:00:00 2001 From: Alexander 'z33ky' Hirsch <1zeeky@gmail.com> Date: Tue, 1 Jul 2025 09:40:57 +0200 Subject: [PATCH 30/42] fxp streaml --- sp/src/vscript/vscript_squirrel.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/sp/src/vscript/vscript_squirrel.cpp b/sp/src/vscript/vscript_squirrel.cpp index 8acba22f..6856a2a7 100644 --- a/sp/src/vscript/vscript_squirrel.cpp +++ b/sp/src/vscript/vscript_squirrel.cpp @@ -1332,15 +1332,10 @@ SQInteger function_stub(HSQUIRRELVM vm) { SQInteger top = sq_gettop(vm); - SQUserPointer userptr = nullptr; - if (SQ_FAILED(sq_getuserpointer(vm, top, &userptr))) - { - return sq_throwerror(vm, "Expected userpointer"); - } + ScriptFunctionBinding_t* pFunc = nullptr; + sq_getuserpointer(vm, top, (SQUserPointer*)&pFunc); - Assert(userptr); - - ScriptFunctionBinding_t* pFunc = (ScriptFunctionBinding_t*)userptr; + Assert(pFunc); int nargs = pFunc->m_desc.m_Parameters.Count(); int nLastHScriptIdx = -1; From 9c740a891e4734ff9d9a31d3f1c00086128e6eb2 Mon Sep 17 00:00:00 2001 From: Alexander 'z33ky' Hirsch <1zeeky@gmail.com> Date: Mon, 23 Jun 2025 20:55:13 +0200 Subject: [PATCH 31/42] Check type of Squirrel constructor_stub() instance This prevents manual invocations of the native class constructor with an invalid value. --- sp/src/vscript/vscript_squirrel.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/sp/src/vscript/vscript_squirrel.cpp b/sp/src/vscript/vscript_squirrel.cpp index 6856a2a7..8cbcb35e 100644 --- a/sp/src/vscript/vscript_squirrel.cpp +++ b/sp/src/vscript/vscript_squirrel.cpp @@ -1562,15 +1562,23 @@ SQInteger constructor_stub(HSQUIRRELVM vm) Assert(pSquirrelVM); sq_resetobject(&pSquirrelVM->lastError_); - - void* instance = pClassDesc->m_pfnConstruct(); - - // expect construction to always succeed - Assert(sq_isnull(pSquirrelVM->lastError_)); - { SQUserPointer p; - sq_getinstanceup(vm, 1, &p, 0); + if (SQ_FAILED(sq_getinstanceup(vm, 1, &p, 0))) + { + return SQ_ERROR; + } + + if (!p) + { + return sq_throwerror(vm, "Accessed null instance"); + } + + void* instance = pClassDesc->m_pfnConstruct(); + + // expect construction to always succeed + Assert(sq_isnull(pSquirrelVM->lastError_)); + new(p) ClassInstanceData(instance, pClassDesc, nullptr, true); } From 503fdd2ee372f2dc04ab5957367372e03aa01728 Mon Sep 17 00:00:00 2001 From: Alexander 'z33ky' Hirsch <1zeeky@gmail.com> Date: Tue, 24 Jun 2025 20:54:01 +0200 Subject: [PATCH 32/42] Fix SQVector member access check --- sp/src/vscript/vscript_squirrel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sp/src/vscript/vscript_squirrel.cpp b/sp/src/vscript/vscript_squirrel.cpp index 8cbcb35e..0664fea8 100644 --- a/sp/src/vscript/vscript_squirrel.cpp +++ b/sp/src/vscript/vscript_squirrel.cpp @@ -352,7 +352,7 @@ namespace SQVector return sq_throwerror(vm, "Expected Vector._get(string)"); } - if (key[0] < 'x' || key['0'] > 'z' || key[1] != '\0') + if (key[0] < 'x' || key[0] > 'z' || key[1] != '\0') { return sqstd_throwerrorf(vm, "the index '%.50s' does not exist", key); } @@ -378,7 +378,7 @@ namespace SQVector return sq_throwerror(vm, "Expected Vector._set(string)"); } - if (key[0] < 'x' || key['0'] > 'z' || key[1] != '\0') + if (key[0] < 'x' || key[0] > 'z' || key[1] != '\0') { return sqstd_throwerrorf(vm, "the index '%.50s' does not exist", key); } From be3ad93edba0b579776f47da2ece34004354abad Mon Sep 17 00:00:00 2001 From: Alexander 'z33ky' Hirsch <1zeeky@gmail.com> Date: Tue, 24 Jun 2025 20:54:34 +0200 Subject: [PATCH 33/42] Remove redundant typetag check from vscript_squirrel getVariant() --- sp/src/vscript/vscript_squirrel.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sp/src/vscript/vscript_squirrel.cpp b/sp/src/vscript/vscript_squirrel.cpp index 0664fea8..ee0c0102 100644 --- a/sp/src/vscript/vscript_squirrel.cpp +++ b/sp/src/vscript/vscript_squirrel.cpp @@ -1300,10 +1300,7 @@ bool getVariant(HSQUIRRELVM vm, SQInteger idx, ScriptVariant_t& variant) case OT_INSTANCE: { Vector* v = nullptr; - SQUserPointer tag; - if (SQ_SUCCEEDED(sq_gettypetag(vm, idx, &tag)) && - tag == TYPETAG_VECTOR && - SQ_SUCCEEDED(sq_getinstanceup(vm, idx, (SQUserPointer*)&v, TYPETAG_VECTOR))) + if (SQ_SUCCEEDED(sq_getinstanceup(vm, idx, (SQUserPointer*)&v, TYPETAG_VECTOR))) { variant.Free(); variant = (Vector*)malloc(sizeof(Vector)); From 9c38e4e2951805494ed48205ab914c1660d6e396 Mon Sep 17 00:00:00 2001 From: Alexander 'z33ky' Hirsch <1zeeky@gmail.com> Date: Tue, 24 Jun 2025 20:56:26 +0200 Subject: [PATCH 34/42] Remove redundant sq_resetobject() for SquirrelVM::lastError_ The function_stub() resets it when used. --- sp/src/vscript/vscript_squirrel.cpp | 40 +++++++++++++---------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/sp/src/vscript/vscript_squirrel.cpp b/sp/src/vscript/vscript_squirrel.cpp index ee0c0102..c3d7897d 100644 --- a/sp/src/vscript/vscript_squirrel.cpp +++ b/sp/src/vscript/vscript_squirrel.cpp @@ -1460,8 +1460,6 @@ SQInteger function_stub(HSQUIRRELVM vm) SquirrelVM* pSquirrelVM = (SquirrelVM*)sq_getsharedforeignptr(vm); Assert(pSquirrelVM); - sq_resetobject(&pSquirrelVM->lastError_); - bool call_success = (*pFunc->m_pfnBinding)(pFunc->m_pFunction, instance, params.Base(), nargs, pFunc->m_desc.m_ReturnType == FIELD_VOID ? nullptr : &script_retval, script_retval_storage); Assert(call_success); @@ -1555,29 +1553,27 @@ SQInteger constructor_stub(HSQUIRRELVM vm) return sqstd_throwerrorf(vm, "Unable to construct instances of %s", pClassDesc->m_pszScriptName); } + SQUserPointer p; + if (SQ_FAILED(sq_getinstanceup(vm, 1, &p, 0))) + { + return SQ_ERROR; + } + + if (!p) + { + return sq_throwerror(vm, "Accessed null instance"); + } + + void* instance = pClassDesc->m_pfnConstruct(); + +#ifdef DBGFLAG_ASSERT SquirrelVM* pSquirrelVM = (SquirrelVM*)sq_getsharedforeignptr(vm); Assert(pSquirrelVM); + // expect construction to always succeed + Assert(sq_isnull(pSquirrelVM->lastError_)); +#endif - sq_resetobject(&pSquirrelVM->lastError_); - { - SQUserPointer p; - if (SQ_FAILED(sq_getinstanceup(vm, 1, &p, 0))) - { - return SQ_ERROR; - } - - if (!p) - { - return sq_throwerror(vm, "Accessed null instance"); - } - - void* instance = pClassDesc->m_pfnConstruct(); - - // expect construction to always succeed - Assert(sq_isnull(pSquirrelVM->lastError_)); - - new(p) ClassInstanceData(instance, pClassDesc, nullptr, true); - } + new(p) ClassInstanceData(instance, pClassDesc, nullptr, true); sq_setreleasehook(vm, 1, &destructor_stub); From 3301edc54d95b10373bb5a4a4e97124b3c753937 Mon Sep 17 00:00:00 2001 From: Alexander 'z33ky' Hirsch <1zeeky@gmail.com> Date: Tue, 24 Jun 2025 20:58:54 +0200 Subject: [PATCH 35/42] Guard get/set/tostring_stub() against illegal Squirrel instances Also streamline SQUserPointer usage in sq_getinstanceup() and sq_getuserpointer() calls to write directly to a pointer of the expected type. --- sp/src/vscript/vscript_squirrel.cpp | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/sp/src/vscript/vscript_squirrel.cpp b/sp/src/vscript/vscript_squirrel.cpp index c3d7897d..7c2861a6 100644 --- a/sp/src/vscript/vscript_squirrel.cpp +++ b/sp/src/vscript/vscript_squirrel.cpp @@ -1583,7 +1583,10 @@ SQInteger constructor_stub(HSQUIRRELVM vm) SQInteger tostring_stub(HSQUIRRELVM vm) { ClassInstanceData* classInstanceData = nullptr; - sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0); + if (SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0))) + { + return SQ_ERROR; + } char buffer[128] = ""; @@ -1613,7 +1616,10 @@ SQInteger tostring_stub(HSQUIRRELVM vm) SQInteger get_stub(HSQUIRRELVM vm) { ClassInstanceData* classInstanceData = nullptr; - sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0); + if (SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0))) + { + return SQ_ERROR; + } const char* key = nullptr; sq_getstring(vm, 2, &key); @@ -1645,7 +1651,10 @@ SQInteger get_stub(HSQUIRRELVM vm) SQInteger set_stub(HSQUIRRELVM vm) { ClassInstanceData* classInstanceData = nullptr; - sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0); + if (SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&classInstanceData, 0))) + { + return SQ_ERROR; + } const char* key = nullptr; sq_getstring(vm, 2, &key); @@ -2741,10 +2750,8 @@ void SquirrelVM::SetInstanceUniqeId(HSCRIPT hInstance, const char* pszId) HSQOBJECT* obj = (HSQOBJECT*)hInstance; sq_pushobject(vm_, *obj); - SQUserPointer self; - sq_getinstanceup(vm_, -1, &self, nullptr); - - auto classInstanceData = (ClassInstanceData*)self; + ClassInstanceData* classInstanceData; + sq_getinstanceup(vm_, -1, (SQUserPointer*)&classInstanceData, nullptr); classInstanceData->instanceId = pszId; @@ -2802,11 +2809,10 @@ void* SquirrelVM::GetInstanceValue(HSCRIPT hInstance, ScriptClassDesc_t* pExpect } sq_pushobject(vm_, *obj); - SQUserPointer self; - sq_getinstanceup(vm_, -1, &self, nullptr); + ClassInstanceData* classInstanceData; + sq_getinstanceup(vm_, -1, (SQUserPointer*)&classInstanceData, nullptr); sq_pop(vm_, 1); - auto classInstanceData = (ClassInstanceData*)self; if (!classInstanceData) { From c659af5944c0dacbd4b7d46d1d91defb81b6c19f Mon Sep 17 00:00:00 2001 From: Alexander 'z33ky' Hirsch <1zeeky@gmail.com> Date: Tue, 24 Jun 2025 21:07:57 +0200 Subject: [PATCH 36/42] Release SquirrelVM::lastError_ after pushing it to the VM stack This fixes a memory leak. --- sp/src/vscript/vscript_squirrel.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/sp/src/vscript/vscript_squirrel.cpp b/sp/src/vscript/vscript_squirrel.cpp index 7c2861a6..b689f757 100644 --- a/sp/src/vscript/vscript_squirrel.cpp +++ b/sp/src/vscript/vscript_squirrel.cpp @@ -1469,6 +1469,7 @@ SQInteger function_stub(HSQUIRRELVM vm) if (!sq_isnull(pSquirrelVM->lastError_)) { sq_pushobject(vm, pSquirrelVM->lastError_); + sq_release(vm, &pSquirrelVM->lastError_); sq_resetobject(&pSquirrelVM->lastError_); sq_retval = sq_throwobject(vm); } From 69b68fa2ac9067cb90a36d4d4ecc48981af49bed Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Tue, 1 Jul 2025 20:09:45 +0300 Subject: [PATCH 37/42] Fallback if IScriptInstanceHelper::Get/Set are not implemented --- sp/src/vscript/vscript_squirrel.cpp | 46 +++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/sp/src/vscript/vscript_squirrel.cpp b/sp/src/vscript/vscript_squirrel.cpp index 93008683..38cadd0b 100644 --- a/sp/src/vscript/vscript_squirrel.cpp +++ b/sp/src/vscript/vscript_squirrel.cpp @@ -1604,7 +1604,19 @@ SQInteger get_stub(HSQUIRRELVM vm) } else { - sq_retval = sqstd_throwerrorf(vm, "the index '%.50s' does not exist", key); + // Fallback + // Extra stack variables don't need to be popped, they are cleaned up on exit + sq_pushroottable(vm); + sq_push(vm, -2); + + if ( SQ_SUCCEEDED( sq_rawget(vm, -2) ) ) + { + sq_retval = 1; + } + else + { + sq_retval = sqstd_throwerrorf(vm, "the index '%.50s' does not exist", key); + } } var.Free(); @@ -1635,11 +1647,24 @@ SQInteger set_stub(HSQUIRRELVM vm) classInstanceData->desc->pHelper->Set(classInstanceData->instance, key, var) )) { - sq_retval = sqstd_throwerrorf(vm, "the index '%.50s' does not exist", key); + // Fallback + sq_pushroottable(vm); + sq_push(vm, -3); + sq_push(vm, -3); + + if ( SQ_SUCCEEDED( sq_rawset(vm, -3) ) ) + { + // rawset doesn't return correctly, pop env to return val + sq_pop(vm, 1); + sq_retval = 1; + } + else + { + sq_retval = sqstd_throwerrorf(vm, "the index '%.50s' does not exist", key); + } } var.Free(); - sq_pop(vm, 1); return sq_retval; } @@ -2515,13 +2540,16 @@ bool SquirrelVM::RegisterClass(ScriptClassDesc_t* pClassDesc) sq_newclosure(vm_, tostring_stub, 0); sq_newslot(vm_, -3, SQFalse); - sq_pushstring(vm_, "_get", -1); - sq_newclosure(vm_, get_stub, 0); - sq_newslot(vm_, -3, SQFalse); + if ( pClassDesc->pHelper ) + { + sq_pushstring(vm_, "_get", -1); + sq_newclosure(vm_, get_stub, 0); + sq_newslot(vm_, -3, SQFalse); - sq_pushstring(vm_, "_set", -1); - sq_newclosure(vm_, set_stub, 0); - sq_newslot(vm_, -3, SQFalse); + sq_pushstring(vm_, "_set", -1); + sq_newclosure(vm_, set_stub, 0); + sq_newslot(vm_, -3, SQFalse); + } sq_pushstring(vm_, "IsValid", -1); sq_newclosure(vm_, IsValid_stub, 0); From 84aaf3bb331e1ddd213c3f25ab68df0b7c626e32 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Mon, 7 Jul 2025 10:03:44 -0500 Subject: [PATCH 38/42] Mapbase player anim state HL2MP and debug build fixes --- .../mapbase/mapbase_playeranimstate.cpp | 38 ++++++++++++++----- .../shared/mapbase/mapbase_playeranimstate.h | 2 + 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/sp/src/game/shared/mapbase/mapbase_playeranimstate.cpp b/sp/src/game/shared/mapbase/mapbase_playeranimstate.cpp index 246da424..50154d63 100644 --- a/sp/src/game/shared/mapbase/mapbase_playeranimstate.cpp +++ b/sp/src/game/shared/mapbase/mapbase_playeranimstate.cpp @@ -27,6 +27,9 @@ #include "datacache/imdlcache.h" #ifdef CLIENT_DLL #include "input.h" +#ifdef HL2MP +#include "c_hl2mp_player.h" +#endif #endif extern ConVar mp_facefronttime, mp_feetyawrate; @@ -76,6 +79,13 @@ extern ConVar mp_facefronttime; CMapbasePlayerAnimState::CMapbasePlayerAnimState( CBasePlayer *pPlayer ): m_pPlayer( pPlayer ) { + if (pPlayer) + { + m_nPoseAimYaw = pPlayer->LookupPoseParameter( "aim_yaw" ); + m_nPoseAimPitch = pPlayer->LookupPoseParameter( "aim_pitch" ); + m_nPoseHeadPitch = pPlayer->LookupPoseParameter( "head_pitch" ); + m_nPoseWeaponLower = pPlayer->LookupPoseParameter( "weapon_lower" ); + } }; //----------------------------------------------------------------------------- @@ -288,6 +298,14 @@ void CMapbasePlayerAnimState::ComputeSequences( CStudioHdr *pStudioHdr ) ComputeReloadSequence(); ComputeWeaponSwitchSequence(); ComputeRelaxSequence(); + +#if defined(HL2MP) && defined(CLIENT_DLL) + C_HL2MP_Player *pHL2MPPlayer = static_cast(GetOuter()); + if (pHL2MPPlayer) + { + pHL2MPPlayer->UpdateLookAt(); + } +#endif } //----------------------------------------------------------------------------- @@ -343,6 +361,7 @@ void CMapbasePlayerAnimState::ClearAnimationState() m_bReloading = false; m_bWeaponSwitching = false; m_bWeaponRelaxing = false; + m_flWeaponRelaxAmount = 0.0f; m_bPlayingMisc = false; m_flReloadBlendIn = 0.0f; m_flReloadBlendOut = 0.0f; @@ -527,7 +546,7 @@ void CMapbasePlayerAnimState::ComputeRelaxSequence() m_flWeaponRelaxAmount = clamp( m_flWeaponRelaxAmount, 0.0f, 1.0f ); - GetOuter()->SetPoseParameter( GetOuter()->LookupPoseParameter( "weapon_lower" ), m_flWeaponRelaxAmount ); + GetOuter()->SetPoseParameter( m_nPoseWeaponLower, m_flWeaponRelaxAmount ); /*int nPose = GetOuter()->LookupPoseParameter( "weapon_lower" ); if (nPose != -1) @@ -548,7 +567,7 @@ void CMapbasePlayerAnimState::ComputeRelaxSequence() } else if (bRelaxing) { - GetOuter()->SetPoseParameter( GetOuter()->LookupPoseParameter( "weapon_lower" ), 1.0f ); + GetOuter()->SetPoseParameter( m_nPoseWeaponLower, 1.0f ); } /*bool bEnabled = m_bWeaponRelaxing; @@ -646,7 +665,7 @@ float CMapbasePlayerAnimState::SetOuterBodyYaw( float flValue ) { float flAimPoseBlend = GetAimPoseBlend(); - GetOuter()->SetPoseParameter( GetOuter()->LookupPoseParameter( "aim_yaw" ), flValue * flAimPoseBlend ); + GetOuter()->SetPoseParameter( m_nPoseAimYaw, flValue * flAimPoseBlend ); return CBasePlayerAnimState::SetOuterBodyYaw( flValue * (1.0f - flAimPoseBlend) ); } @@ -667,7 +686,7 @@ void CMapbasePlayerAnimState::ComputePoseParam_BodyYaw( void ) void CMapbasePlayerAnimState::ComputePoseParam_BodyLookYaw( void ) { // See if we even have a blender for pitch - int upper_body_yaw = GetOuter()->LookupPoseParameter( "aim_yaw" ); + int upper_body_yaw = m_nPoseAimYaw; if ( upper_body_yaw < 0 ) { return; @@ -815,8 +834,10 @@ void CMapbasePlayerAnimState::ComputePoseParam_BodyPitch( CStudioHdr *pStudioHdr //float flAimPoseBlend = GetAimPoseBlend(); // See if we have a blender for pitch - GetOuter()->SetPoseParameter( pStudioHdr, "aim_pitch", flPitch ); - GetOuter()->SetPoseParameter( pStudioHdr, "head_pitch", flPitch ); + if (m_nPoseAimPitch >= 0) + GetOuter()->SetPoseParameter( pStudioHdr, m_nPoseAimPitch, flPitch ); + if (m_nPoseHeadPitch >= 0) + GetOuter()->SetPoseParameter( pStudioHdr, m_nPoseHeadPitch, flPitch ); //ComputePoseParam_HeadPitch( pStudioHdr ); } @@ -826,9 +847,6 @@ void CMapbasePlayerAnimState::ComputePoseParam_BodyPitch( CStudioHdr *pStudioHdr //----------------------------------------------------------------------------- void CMapbasePlayerAnimState::ComputePoseParam_HeadPitch( CStudioHdr *pStudioHdr ) { - // Get pitch from v_angle - int iHeadPitch = GetOuter()->LookupPoseParameter("head_pitch"); - float flPitch = m_flEyePitch; if ( flPitch > 180.0f ) @@ -837,5 +855,5 @@ void CMapbasePlayerAnimState::ComputePoseParam_HeadPitch( CStudioHdr *pStudioHdr } flPitch = clamp( flPitch, -90, 90 ); - GetOuter()->SetPoseParameter( pStudioHdr, iHeadPitch, flPitch ); + GetOuter()->SetPoseParameter( pStudioHdr, m_nPoseHeadPitch, flPitch ); } diff --git a/sp/src/game/shared/mapbase/mapbase_playeranimstate.h b/sp/src/game/shared/mapbase/mapbase_playeranimstate.h index 5e3bf514..3c1403ed 100644 --- a/sp/src/game/shared/mapbase/mapbase_playeranimstate.h +++ b/sp/src/game/shared/mapbase/mapbase_playeranimstate.h @@ -119,6 +119,8 @@ private: // until it completes. int m_iFireSequence; // (For any sequences in the fire layer, including grenade throw). float m_flFireCycle; + + int m_nPoseAimYaw, m_nPoseAimPitch, m_nPoseHeadPitch, m_nPoseWeaponLower; }; CMapbasePlayerAnimState *CreatePlayerAnimationState( CBasePlayer *pPlayer ); From 834196c2eb2d6090b5e669fca0105faf2db53518 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Mon, 7 Jul 2025 10:04:44 -0500 Subject: [PATCH 39/42] Mapbase protagonist system team and precache fixes --- .../shared/mapbase/protagonist_system.cpp | 28 ++++++++++++++++--- .../game/shared/mapbase/protagonist_system.h | 3 ++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/sp/src/game/shared/mapbase/protagonist_system.cpp b/sp/src/game/shared/mapbase/protagonist_system.cpp index 8a8720ac..b4eb1313 100644 --- a/sp/src/game/shared/mapbase/protagonist_system.cpp +++ b/sp/src/game/shared/mapbase/protagonist_system.cpp @@ -176,11 +176,11 @@ void CProtagonistSystem::LoadProtagonistFile( const char *pszFile ) else if (FStrEq( pszSubKeyName, "team" )) { #ifdef HL2MP - if (FStrEq( pszSubKeyName, "combine" )) + if (FStrEq( pSubKey->GetString(), "combine" )) { pProtag->nTeam = TEAM_COMBINE; } - else if (FStrEq( pszSubKeyName, "rebels" )) + else if (FStrEq( pSubKey->GetString(), "rebels" )) { pProtag->nTeam = TEAM_REBELS; } @@ -188,7 +188,7 @@ void CProtagonistSystem::LoadProtagonistFile( const char *pszFile ) #endif { // Try to get a direct integer - pProtag->nTeam = atoi( pszSubKeyName ); + pProtag->nTeam = atoi( pSubKey->GetString() ); } } #endif @@ -335,6 +335,12 @@ void CProtagonistSystem::PrecacheProtagonist( CBaseEntity *pSource, int nIdx ) ProtagonistData_t &pProtag = m_Protagonists[nIdx]; + // Don't if it's already precached + if (pProtag.bPrecached) + return; + + CBaseEntity::SetAllowPrecache( true ); + // Playermodel if (pProtag.pszPlayerModel) { @@ -358,6 +364,16 @@ void CProtagonistSystem::PrecacheProtagonist( CBaseEntity *pSource, int nIdx ) pSource->PrecacheModel( pProtag.dictWpnData[i].pszVM ); } } + + CBaseEntity::SetAllowPrecache( false ); + + // Precache parents + FOR_EACH_VEC( pProtag.vecParents, i ) + { + PrecacheProtagonist( pSource, pProtag.vecParents[i] ); + } + + pProtag.bPrecached = true; #endif } @@ -416,7 +432,6 @@ GetProtagParamBody( ResponseContexts, bool, GetProtagParamInner( ResponseConte int nLast = V_strlen( pszContexts )-1; if (pszContexts[nLast] == ',') { - Msg( "Removing trailing comma from \"%s\"\n", pszContexts ); pszContexts[nLast] = '\0'; } } @@ -681,6 +696,11 @@ void CProtagonistSystem::PrintProtagonistData() if (pProtag.pszPlayerModel) Msg( "\t\tPlayer model: \"%s\" (%i, %i)\n", pProtag.pszPlayerModel, pProtag.nPlayerSkin, pProtag.nPlayerBody ); +#ifdef HL2MP + if ( pProtag.nTeam != TEAM_ANY ) + Msg( "\t\tTeam: %i\n", pProtag.nTeam ); +#endif + for (int j = 0; j < NUM_HAND_RIG_TYPES; j++) { extern const char *pHandRigs[NUM_HAND_RIG_TYPES]; diff --git a/sp/src/game/shared/mapbase/protagonist_system.h b/sp/src/game/shared/mapbase/protagonist_system.h index 396187c2..e678bc90 100644 --- a/sp/src/game/shared/mapbase/protagonist_system.h +++ b/sp/src/game/shared/mapbase/protagonist_system.h @@ -61,6 +61,9 @@ private: // Multiplayer int nTeam = TEAM_ANY; + + // Precached (used by system, not actual data) + bool bPrecached = false; #endif // Weapon Data From 8de011987bb6e991966eaa44ecd1c8878e72ee7a Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Mon, 7 Jul 2025 10:06:38 -0500 Subject: [PATCH 40/42] game_timer entity and HL2 generic timer HUD element --- sp/src/game/client/client_mapbase.vpc | 3 + .../game/client/mapbase/hud_generic_timer.cpp | 511 ++++++++++++++ sp/src/game/server/server_mapbase.vpc | 2 + sp/src/game/shared/mapbase/game_timer.cpp | 658 ++++++++++++++++++ sp/src/game/shared/mapbase/game_timer.h | 145 ++++ 5 files changed, 1319 insertions(+) create mode 100644 sp/src/game/client/mapbase/hud_generic_timer.cpp create mode 100644 sp/src/game/shared/mapbase/game_timer.cpp create mode 100644 sp/src/game/shared/mapbase/game_timer.h diff --git a/sp/src/game/client/client_mapbase.vpc b/sp/src/game/client/client_mapbase.vpc index 5aef63ce..08d38e4e 100644 --- a/sp/src/game/client/client_mapbase.vpc +++ b/sp/src/game/client/client_mapbase.vpc @@ -51,6 +51,8 @@ $Project $File "$SRCDIR\game\shared\mapbase\mapbase_playeranimstate.h" $File "$SRCDIR\game\shared\mapbase\protagonist_system.cpp" $File "$SRCDIR\game\shared\mapbase\protagonist_system.h" + $File "$SRCDIR\game\shared\mapbase\game_timer.cpp" + $File "$SRCDIR\game\shared\mapbase\game_timer.h" $File "$SRCDIR\game\shared\mapbase\vscript_funcs_shared.cpp" [$MAPBASE_VSCRIPT] $File "$SRCDIR\game\shared\mapbase\vscript_funcs_shared.h" [$MAPBASE_VSCRIPT] $File "$SRCDIR\game\shared\mapbase\vscript_singletons.cpp" [$MAPBASE_VSCRIPT] @@ -73,6 +75,7 @@ $Project $File "mapbase\c_vgui_text_display.cpp" $File "mapbase\c_weapon_custom_hl2.cpp" $File "mapbase\mapbase_autocubemap.cpp" + $File "mapbase\hud_generic_timer.cpp" } $Folder "HL2 DLL" diff --git a/sp/src/game/client/mapbase/hud_generic_timer.cpp b/sp/src/game/client/mapbase/hud_generic_timer.cpp new file mode 100644 index 00000000..b3464d55 --- /dev/null +++ b/sp/src/game/client/mapbase/hud_generic_timer.cpp @@ -0,0 +1,511 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Generic timer HUD element for HL2-based mods +// +// Author: Blixibon +// +//=============================================================================// + +#include "cbase.h" +#include "hud.h" +#include "hud_macros.h" +#include "hudelement.h" +#include "hud_numericdisplay.h" +#include "iclientmode.h" +#include +#include +#include +#include +#include "baseviewport.h" +#include "mapbase/game_timer.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern bool g_bAnyGameTimerActive; + +ConVar hud_timer_max_bars( "hud_timer_max_bars", "15" ); + +//----------------------------------------------------------------------------- +// Purpose: Timer panel +//----------------------------------------------------------------------------- +class CHudGenericGameTimer : public CHudElement, public vgui::Panel +{ + DECLARE_CLASS_SIMPLE( CHudGenericGameTimer, vgui::Panel ); + +public: + CHudGenericGameTimer( const char *pElementName ); + virtual void Init( void ); + virtual void VidInit( void ); + virtual void Reset( void ); + virtual void OnThink(); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + virtual bool ShouldDraw( void ); + virtual void Paint(); + + C_GameTimer *GetTimer(); + bool FindTimer(); + + void SetDisplayValue( int value ) { m_iValue = value; } + void SetLabelText( const wchar_t *text ); + + virtual void PaintNumbers( vgui::HFont font, int xpos, int ypos ); + virtual void PaintBars( int xpos, int ypos, int wide ); + + int GetTimerIndex() const { return m_iTimerIndex; } + +private: + void SetTimerLabel( void ); + +private: + + int m_iTimerIndex; + + bool m_bTimerDisplayed; + bool m_bPlayingFadeout; + float m_flShutoffTime; + + bool m_bTimerPaused; + bool m_bTimerWarned; + + int m_iValue; + int m_iTimerMaxLength; + bool m_bShowTimeRemaining; + int m_nProgressBarMax; + int m_nProgressBarOverride; + float m_flOverrideX, m_flOverrideY; + wchar_t m_LabelText[32]; + + int m_nLabelWidth, m_nLabelHeight; + int m_nTimerWidth, m_nTimerHeight; + + CPanelAnimationVarAliasType( float, m_flMinWidth, "MinWidth", "100", "proportional_float" ); + CPanelAnimationVarAliasType( float, m_flBorder, "Border", "24", "proportional_float" ); + CPanelAnimationVarAliasType( float, m_flLabelTimerSpacing, "LabelTimerSpacing", "2", "proportional_float" ); + + CPanelAnimationVarAliasType( float, m_flBarHeight, "BarHeight", "5", "proportional_float" ); + CPanelAnimationVarAliasType( float, m_flBarChunkGap, "BarChunkGap", "3", "proportional_float" ); + CPanelAnimationVarAliasType( float, m_flBarVerticalGap, "BarVerticalGap", "8", "proportional_float" ); + CPanelAnimationVar( int, m_iBarDisabledAlpha, "BarDisabledAlpha", "70" ); + + CPanelAnimationVar( float, m_flBlur, "Blur", "0" ); + + CPanelAnimationVar( vgui::HFont, m_hNumberFont, "NumberFont", "HudHintTextLarge" ); + CPanelAnimationVar( vgui::HFont, m_hNumberGlowFont, "NumberGlowFont", "HudHintTextLarge" ); + CPanelAnimationVar( vgui::HFont, m_hTextFont, "TextFont", "HudHintTextSmall" ); +}; + +DECLARE_HUDELEMENT( CHudGenericGameTimer ); + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CHudGenericGameTimer::CHudGenericGameTimer( const char *pElementName ) + : CHudElement( pElementName ), + BaseClass( NULL, "HudGenericGameTimer" ) +{ + Panel *pParent = g_pClientMode->GetViewport(); + SetParent( pParent ); + + SetHiddenBits( HIDEHUD_MISCSTATUS ); + + m_iTimerIndex = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudGenericGameTimer::Init() +{ + Reset(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudGenericGameTimer::Reset() +{ + SetDisplayValue( 0 ); + SetAlpha( 0 ); + + m_flShutoffTime = 0.0f; + m_bTimerDisplayed = false; + m_iTimerIndex = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudGenericGameTimer::VidInit() +{ + Reset(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CHudGenericGameTimer::OnThink() +{ + C_BasePlayer *local = C_BasePlayer::GetLocalPlayer(); + if ( !local ) + { + // Not ready to init! + return; + } + + // If our timer has been disabled, close the menu + C_GameTimer *pTimer = GetTimer(); + if ( !pTimer || pTimer->IsDisabled() || pTimer->IsMarkedForDeletion() ) + { + if ( m_flShutoffTime == 0.0f ) + { + m_flShutoffTime = gpGlobals->curtime + 1.0f; + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "GenericGameTimerClose" ); + } + m_iTimerIndex = 0; + return; + } + + // Check pause state + if ( m_bTimerPaused && !pTimer->IsTimerPaused() ) + { + m_bTimerPaused = false; + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "GenericGameTimerPulse" ); + } + else if ( pTimer->IsTimerPaused() ) + { + m_bTimerPaused = true; + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "GenericGameTimerPulseOnce" ); + } + + float flTimeRemaining = floorf( pTimer->GetTimeRemaining() ); + + // Check warn state + float flWarnTime = pTimer->GetWarnTime(); + + if (flWarnTime > 0.0f) + { + if ( m_bTimerWarned && flTimeRemaining > flWarnTime ) + { + // Turn back to normal + m_bTimerWarned = false; + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "GenericGameTimerUnwarn" ); + } + else if ( flTimeRemaining <= flWarnTime ) + { + // Turn red + m_bTimerWarned = true; + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "GenericGameTimerWarn" ); + } + } + + SetDisplayValue( flTimeRemaining ); + + m_iTimerMaxLength = pTimer->GetTimerMaxLength(); + m_bShowTimeRemaining = pTimer->ShowTimeRemaining(); + + m_nProgressBarMax = pTimer->GetProgressBarMaxSegments(); + if (m_nProgressBarMax == -1) + { + // Default to timer max length + m_nProgressBarMax = min( m_iTimerMaxLength, hud_timer_max_bars.GetInt() ); + } + + m_nProgressBarOverride = pTimer->GetProgressBarOverride(); + + pTimer->GetPositionOverride( m_flOverrideX, m_flOverrideY ); +} + +//----------------------------------------------------------------------------- +// Purpose: hud scheme settings +//----------------------------------------------------------------------------- +void CHudGenericGameTimer::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + + SetPaintBackgroundEnabled( false ); + + // set our size + int screenWide, screenTall; + int x, y; + GetPos( x, y ); + GetHudSize( screenWide, screenTall ); + SetBounds( 0, y, screenWide, screenTall - y ); + + // Start with a 0:00 timer + m_nTimerWidth = (vgui::surface()->GetCharacterWidth( m_hNumberFont, '0' ) * 3); + m_nTimerWidth += vgui::surface()->GetCharacterWidth( m_hNumberFont, ':' ); + + m_nTimerHeight = vgui::surface()->GetFontTall( m_hNumberFont ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CHudGenericGameTimer::ShouldDraw( void ) +{ + if ( !CHudElement::ShouldDraw() ) + return false; + + if ( !m_bTimerDisplayed ) + { + // Check if we should find a new timer + if ( g_bAnyGameTimerActive ) + { + if ( FindTimer() ) + return true; + } + + return false; + } + + // check for if menu is set to disappear + if ( m_flShutoffTime > 0 && m_flShutoffTime <= gpGlobals->curtime ) + { + // times up, shutoff + m_bTimerDisplayed = false; + return false; + } + + if ( m_flOverrideX == -1.0f && m_flOverrideY == -1.0f ) + { + // Don't overlap with the weapon selection HUD + vgui::Panel *pWeaponSelection = GetParent()->FindChildByName( "HudWeaponSelection" ); + if ( pWeaponSelection && pWeaponSelection->IsVisible() ) + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +inline C_GameTimer *CHudGenericGameTimer::GetTimer() +{ + if (m_iTimerIndex <= 0) + return NULL; + + // Need to do a dynamic_cast because this entity index could've been replaced since it was last used + return dynamic_cast(C_BaseEntity::Instance( m_iTimerIndex )); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CHudGenericGameTimer::FindTimer() +{ + // Find a new timer + for ( int i = 0; i < IGameTimerAutoList::AutoList().Count(); i++ ) + { + C_GameTimer *pTimer = static_cast( IGameTimerAutoList::AutoList()[i] ); + if ( !pTimer->IsDisabled() && !pTimer->IsMarkedForDeletion() ) + { + m_iTimerIndex = pTimer->entindex(); + break; + } + } + + C_GameTimer *pTimer = GetTimer(); + if ( pTimer ) + { + // New timer selected, set the caption + wchar_t wszLabelText[128]; + const wchar_t *wLocalizedItem = g_pVGuiLocalize->Find( pTimer->GetTimerCaption() ); + if (wLocalizedItem) + { + V_wcsncpy( wszLabelText, wLocalizedItem, sizeof( wszLabelText ) ); + } + else + { + g_pVGuiLocalize->ConvertANSIToUnicode( pTimer->GetTimerCaption(), wszLabelText, sizeof( wszLabelText ) ); + } + SetLabelText( wszLabelText ); + + m_flShutoffTime = 0; + m_bTimerDisplayed = true; + m_bTimerPaused = pTimer->IsTimerPaused(); + m_bTimerWarned = false; + g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( m_bTimerPaused ? "GenericGameTimerShow" : "GenericGameTimerShowFlash" ); + + SetDisplayValue( ceil( pTimer->GetTimeRemaining() ) ); + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: data accessor +//----------------------------------------------------------------------------- +void CHudGenericGameTimer::SetLabelText( const wchar_t *text ) +{ + wcsncpy( m_LabelText, text, sizeof( m_LabelText ) / sizeof( wchar_t ) ); + m_LabelText[(sizeof( m_LabelText ) / sizeof( wchar_t )) - 1] = 0; + + m_nLabelWidth = 0; + m_nLabelHeight = 0; + + if (m_LabelText[0] != '\0') + { + int nLabelLen = V_wcslen( m_LabelText ); + for (int ch = 0; ch < nLabelLen; ch++) + { + m_nLabelWidth += vgui::surface()->GetCharacterWidth( m_hTextFont, m_LabelText[ch] ); + } + + m_nLabelHeight = vgui::surface()->GetFontTall( m_hTextFont ); + m_nLabelHeight += m_flLabelTimerSpacing; + } +} + +//----------------------------------------------------------------------------- +// Purpose: paints a number at the specified position +//----------------------------------------------------------------------------- +void CHudGenericGameTimer::PaintNumbers( vgui::HFont font, int xpos, int ypos ) +{ + vgui::surface()->DrawSetTextFont(font); + + int nTimeToDisplay = m_iValue; + + if ( !m_bShowTimeRemaining ) + { + nTimeToDisplay = m_iTimerMaxLength - nTimeToDisplay; + } + + int nMinutes = 0; + int nSeconds = 0; + wchar_t unicode[6]; + + if (nTimeToDisplay <= 0) + { + nMinutes = 0; + nSeconds = 0; + } + else + { + nMinutes = nTimeToDisplay / 60; + nSeconds = nTimeToDisplay % 60; + } + + V_snwprintf( unicode, ARRAYSIZE(unicode), L"%d:%02d", nMinutes, nSeconds ); + + vgui::surface()->DrawSetTextPos( xpos, ypos ); + vgui::surface()->DrawUnicodeString( unicode ); + + // draw the overbright blur + for (float fl = m_flBlur; fl > 0.0f; fl -= 1.0f) + { + if (fl >= 1.0f) + { + vgui::surface()->DrawSetTextPos( xpos, ypos ); + vgui::surface()->DrawUnicodeString( unicode ); + } + else + { + // draw a percentage of the last one + Color col = GetFgColor(); + col[3] *= fl; + vgui::surface()->DrawSetTextColor( col ); + vgui::surface()->DrawSetTextPos( xpos, ypos ); + vgui::surface()->DrawUnicodeString( unicode ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: paints bars at the specified position +//----------------------------------------------------------------------------- +void CHudGenericGameTimer::PaintBars( int xpos, int ypos, int wide ) +{ + // get bar chunks + int barChunkWidth = (wide / m_nProgressBarMax) - m_flBarChunkGap; + + int enabledChunks; + if (m_nProgressBarOverride > -1) + enabledChunks = m_nProgressBarOverride; + else + { + enabledChunks = (int)floorf( ((float)m_iValue / (float)m_iTimerMaxLength) * (float)m_nProgressBarMax ); + if (!m_bShowTimeRemaining) + { + enabledChunks = m_nProgressBarMax - enabledChunks; + } + } + + // draw the suit power bar + vgui::surface()->DrawSetColor( GetFgColor() ); + for (int i = 0; i < enabledChunks; i++) + { + vgui::surface()->DrawFilledRect( xpos, ypos, xpos + barChunkWidth, ypos + m_flBarHeight ); + xpos += (barChunkWidth + m_flBarChunkGap); + } + // draw the exhausted portion of the bar. + Color clrExhausted = GetFgColor(); + clrExhausted[3] = ((float)clrExhausted[3] / 255.0f) * m_iBarDisabledAlpha; + vgui::surface()->DrawSetColor( clrExhausted ); + for (int i = enabledChunks; i < m_nProgressBarMax; i++) + { + vgui::surface()->DrawFilledRect( xpos, ypos, xpos + barChunkWidth, ypos + m_flBarHeight ); + xpos += (barChunkWidth + m_flBarChunkGap); + } +} + +//----------------------------------------------------------------------------- +// Purpose: renders the vgui panel +//----------------------------------------------------------------------------- +void CHudGenericGameTimer::Paint() +{ + // Check for 00:00 timer + int nTimerWidth = m_nTimerWidth; + if (m_iValue > 600) + nTimerWidth += vgui::surface()->GetCharacterWidth( m_hNumberFont, '0' ); + + // draw the background + int wide = max( nTimerWidth, m_nLabelWidth ) + m_flBorder; + if (m_flMinWidth > wide) + wide = m_flMinWidth; + + int tall = m_nTimerHeight + m_nLabelHeight + (m_flBorder/2); + if (m_nProgressBarMax > 0) + tall += m_flBarHeight + m_flBarVerticalGap; + else + tall += (m_flBorder/2); + + int screenW, screenH; + GetHudSize( screenW, screenH ); + + float flScalarX = (m_flOverrideX != -1.0f ? m_flOverrideX : 0.5f); + float flScalarY = (m_flOverrideY != -1.0f ? m_flOverrideY : 0.05f); + + int x = (screenW - wide) * flScalarX; + int y = (screenH - tall) * flScalarY; + + DrawBox( x, y, wide, tall, GetBgColor(), 1.0f); + + y += (m_flBorder/2); + + // draw our bars + if (m_nProgressBarMax > 0) + { + int barX = x + (m_flBorder/2); + PaintBars( barX, y, wide - m_flBorder ); + y += m_flBarVerticalGap; + } + + if (m_nLabelHeight > 0) + { + vgui::surface()->DrawSetTextFont( m_hTextFont ); + vgui::surface()->DrawSetTextColor( GetFgColor() ); + vgui::surface()->DrawSetTextPos( x + ((wide - m_nLabelWidth) * 0.5f), y ); + vgui::surface()->DrawUnicodeString( m_LabelText ); + y += m_nLabelHeight; + } + + // draw our numbers + vgui::surface()->DrawSetTextColor( GetFgColor() ); + + int digitX = x + ((wide - nTimerWidth) * 0.5f); + int digitY = y; + PaintNumbers( m_hNumberFont, digitX, digitY ); +} diff --git a/sp/src/game/server/server_mapbase.vpc b/sp/src/game/server/server_mapbase.vpc index 2d1113d2..b49ad158 100644 --- a/sp/src/game/server/server_mapbase.vpc +++ b/sp/src/game/server/server_mapbase.vpc @@ -50,6 +50,8 @@ $Project $File "$SRCDIR\game\shared\mapbase\mapbase_playeranimstate.h" $File "$SRCDIR\game\shared\mapbase\protagonist_system.cpp" $File "$SRCDIR\game\shared\mapbase\protagonist_system.h" + $File "$SRCDIR\game\shared\mapbase\game_timer.cpp" + $File "$SRCDIR\game\shared\mapbase\game_timer.h" $File "$SRCDIR\game\shared\mapbase\vscript_funcs_shared.cpp" [$MAPBASE_VSCRIPT] $File "$SRCDIR\game\shared\mapbase\vscript_funcs_shared.h" [$MAPBASE_VSCRIPT] $File "$SRCDIR\game\shared\mapbase\vscript_singletons.cpp" [$MAPBASE_VSCRIPT] diff --git a/sp/src/game/shared/mapbase/game_timer.cpp b/sp/src/game/shared/mapbase/game_timer.cpp new file mode 100644 index 00000000..67f73955 --- /dev/null +++ b/sp/src/game/shared/mapbase/game_timer.cpp @@ -0,0 +1,658 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Generic custom timer entity +// +// Author: Blixibon +// +//=============================================================================// + +#include "cbase.h" +#include "game_timer.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern bool IsInCommentaryMode( void ); + +ConVar sv_game_menu_default_warn_frac( "sv_game_menu_default_warn_frac", "0.25", FCVAR_REPLICATED ); + +LINK_ENTITY_TO_CLASS( game_timer, CGameTimer ); + +IMPLEMENT_NETWORKCLASS_ALIASED( GameTimer, DT_GameTimer ) + +BEGIN_NETWORK_TABLE_NOBASE( CGameTimer, DT_GameTimer ) +#ifdef CLIENT_DLL + + RecvPropBool( RECVINFO( m_bTimerPaused ) ), + RecvPropTime( RECVINFO( m_flTimerInitialLength ) ), + RecvPropTime( RECVINFO( m_flTimerMaxLength ) ), + RecvPropTime( RECVINFO( m_flTimeRemaining ) ), + RecvPropTime( RECVINFO( m_flTimerEndTime ) ), + RecvPropTime( RECVINFO( m_flWarnTime ) ), + RecvPropBool( RECVINFO( m_bIsDisabled ) ), + RecvPropBool( RECVINFO( m_bStartPaused ) ), + RecvPropBool( RECVINFO( m_bShowTimeRemaining ) ), + RecvPropInt( RECVINFO( m_nProgressBarMaxSegments ) ), + RecvPropInt( RECVINFO( m_nProgressBarOverride ) ), + RecvPropFloat( RECVINFO( m_flOverrideX ) ), + RecvPropFloat( RECVINFO( m_flOverrideY ) ), + RecvPropString( RECVINFO( m_szTimerCaption ) ), + + RecvPropInt( RECVINFO( m_iTeamNum ) ), + +#else + + SendPropBool( SENDINFO( m_bTimerPaused ) ), + SendPropTime( SENDINFO( m_flTimerInitialLength ) ), + SendPropTime( SENDINFO( m_flTimerMaxLength ) ), + SendPropTime( SENDINFO( m_flTimeRemaining ) ), + SendPropTime( SENDINFO( m_flTimerEndTime ) ), + SendPropTime( SENDINFO( m_flWarnTime ) ), + SendPropBool( SENDINFO( m_bIsDisabled ) ), + SendPropBool( SENDINFO( m_bStartPaused ) ), + SendPropBool( SENDINFO( m_bShowTimeRemaining ) ), + SendPropInt( SENDINFO( m_nProgressBarMaxSegments ) ), + SendPropInt( SENDINFO( m_nProgressBarOverride ) ), + SendPropFloat( SENDINFO( m_flOverrideX ) ), + SendPropFloat( SENDINFO( m_flOverrideY ) ), + SendPropString( SENDINFO( m_szTimerCaption ) ), + + SendPropInt( SENDINFO( m_iTeamNum ), TEAMNUM_NUM_BITS, 0 ), + +#endif +END_NETWORK_TABLE() + +#ifndef CLIENT_DLL +BEGIN_DATADESC( CGameTimer ) + + DEFINE_KEYFIELD( m_flTimerInitialLength, FIELD_FLOAT, "timer_length" ), + DEFINE_KEYFIELD( m_flTimerMaxLength, FIELD_FLOAT, "max_length" ), + DEFINE_KEYFIELD( m_flWarnTime, FIELD_FLOAT, "warn_time" ), + DEFINE_KEYFIELD( m_bIsDisabled, FIELD_BOOLEAN, "StartDisabled" ), + DEFINE_KEYFIELD( m_bStartPaused, FIELD_BOOLEAN, "start_paused" ), + DEFINE_KEYFIELD( m_bShowTimeRemaining, FIELD_BOOLEAN, "show_time_remaining" ), + DEFINE_KEYFIELD( m_bDisableOnFinish, FIELD_BOOLEAN, "disable_on_finish" ), + DEFINE_KEYFIELD( m_bShowInHUD, FIELD_BOOLEAN, "show_in_hud" ), + DEFINE_KEYFIELD( m_nProgressBarMaxSegments, FIELD_INTEGER, "progress_bar_max" ), + DEFINE_KEYFIELD( m_nProgressBarOverride, FIELD_INTEGER, "progress_bar_override" ), + DEFINE_KEYFIELD( m_flOverrideX, FIELD_FLOAT, "x" ), + DEFINE_KEYFIELD( m_flOverrideY, FIELD_FLOAT, "y" ), + DEFINE_AUTO_ARRAY( m_szTimerCaption, FIELD_CHARACTER ), + DEFINE_KEYFIELD( m_iszPlayerFilterName, FIELD_STRING, "PlayerFilter" ), + DEFINE_FIELD( m_hPlayerFilter, FIELD_EHANDLE ), + + DEFINE_FIELD( m_flTimerEndTime, FIELD_TIME ), + DEFINE_FIELD( m_flTimeRemaining, FIELD_FLOAT ), + DEFINE_FIELD( m_bTimerPaused, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bStartedWarn, FIELD_BOOLEAN ), + + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Pause", InputPause ), + DEFINE_INPUTFUNC( FIELD_VOID, "Resume", InputResume ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetTime", InputSetTime ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "AddTime", InputAddTime ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "RemoveTime", InputRemoveTime ), + DEFINE_INPUTFUNC( FIELD_VOID, "Restart", InputRestart ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetMaxTime", InputSetMaxTime ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetTimerCaption", InputSetTimerCaption ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetProgressBarMaxSegments", InputSetProgressBarMaxSegments ), + DEFINE_INPUTFUNC( FIELD_INTEGER, "SetProgressBarOverride", InputSetProgressBarOverride ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetX", InputSetX ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetY", InputSetY ), + DEFINE_INPUTFUNC( FIELD_VOID, "GetTimeRemaining", InputGetTimeRemaining ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetPlayerFilter", InputSetPlayerFilter ), + + DEFINE_OUTPUT( m_OnFinished, "OnFinished" ), + DEFINE_OUTPUT( m_OnPaused, "OnPaused" ), + DEFINE_OUTPUT( m_OnResumed, "OnResumed" ), + DEFINE_OUTPUT( m_OnWarned, "OnWarned" ), + DEFINE_OUTPUT( m_OnTick, "OnTick" ), + DEFINE_OUTPUT( m_OnGetTimeRemaining, "OnGetTimeRemaining" ), + + DEFINE_THINKFUNC( TimerThink ), + +END_DATADESC(); +#endif + +#ifdef CLIENT_DLL +IMPLEMENT_AUTO_LIST( IGameTimerAutoList ); +#else +#define TIMER_THINK "GameTimerThink" +#endif + +//----------------------------------------------------------------------------- +// Purpose: constructor +//----------------------------------------------------------------------------- +CGameTimer::CGameTimer( void ) +{ + m_bIsDisabled = true; + m_bTimerPaused = true; + m_flTimeRemaining = 0; + m_flTimerEndTime = 0; + m_flWarnTime = -1.0f; + m_bStartPaused = false; + m_bShowTimeRemaining = true; + m_flTimerInitialLength = 0; + m_flTimerMaxLength = 0; + m_nProgressBarMaxSegments = -1; + m_nProgressBarOverride = -1; + m_flOverrideX = m_flOverrideY = -1.0f; +#ifndef CLIENT_DLL + m_bDisableOnFinish = true; + m_bShowInHUD = true; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: destructor +//----------------------------------------------------------------------------- +CGameTimer::~CGameTimer( void ) +{ + +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameTimer::Spawn( void ) +{ +#ifndef CLIENT_DLL + if ( IsDisabled() ) // we need to get the data initialized before actually become disabled + { + m_bIsDisabled = false; + SetTimeRemaining( m_flTimerInitialLength ); + m_bIsDisabled = true; + } + else + { + SetTimeRemaining( m_flTimerInitialLength ); + if ( !m_bStartPaused ) + ResumeTimer(); + } + + if ( m_iszPlayerFilterName != NULL_STRING ) + { + m_hPlayerFilter = dynamic_cast(gEntList.FindEntityByName( NULL, m_iszPlayerFilterName, this )); + } +#endif + + BaseClass::Spawn(); +} + +#ifndef CLIENT_DLL + +bool CGameTimer::KeyValue( const char *szKeyName, const char *szValue ) +{ + if ( FStrEq( szKeyName, "timer_caption" ) ) + { + Q_strncpy( m_szTimerCaption.GetForModify(), szValue, MAX_GAME_TIMER_CAPTION ); + } + else + { + return BaseClass::KeyValue( szKeyName, szValue ); + } + + return true; +} + +bool CGameTimer::GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ) +{ + if ( FStrEq( szKeyName, "timer_caption" ) ) + { + Q_snprintf( szValue, iMaxLen, "%s", m_szTimerCaption.Get() ); + return true; + } + return BaseClass::GetKeyValue( szKeyName, szValue, iMaxLen ); +} + +#endif + +//----------------------------------------------------------------------------- +// Purpose: Gets the seconds left on the timer, paused or not. +//----------------------------------------------------------------------------- +float CGameTimer::GetTimeRemaining( void ) +{ + float flSecondsRemaining; + + if ( m_bTimerPaused ) + { + flSecondsRemaining = m_flTimeRemaining; + } + else + { + flSecondsRemaining = m_flTimerEndTime - gpGlobals->curtime; + } + + if ( flSecondsRemaining < 0 ) + { + flSecondsRemaining = 0.0f; + } + + return flSecondsRemaining; +} + +//----------------------------------------------------------------------------- +// Purpose: Gets the timer's warning time +//----------------------------------------------------------------------------- +float CGameTimer::GetWarnTime( void ) +{ + if ( m_flWarnTime < 0 ) + { + // TODO: All of the default warning stuff is on the client!!! + return GetTimerMaxLength() * sv_game_menu_default_warn_frac.GetFloat(); + } + + return m_flWarnTime; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +float CGameTimer::GetTimerMaxLength( void ) +{ + if ( m_flTimerMaxLength ) + return m_flTimerMaxLength; + + return m_flTimerInitialLength; +} + +#ifdef CLIENT_DLL + +bool g_bAnyGameTimerActive = false; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_GameTimer::OnDataChanged( DataUpdateType_t updateType ) +{ + BaseClass::OnDataChanged( updateType ); + + if ( !m_bIsDisabled ) + { + g_bAnyGameTimerActive = true; + } + else if ( !m_bOldDisabled ) + { + // Go through all of the timers and mark when one is active + g_bAnyGameTimerActive = false; + for ( int i = 0; i < IGameTimerAutoList::AutoList().Count(); i++ ) + { + C_GameTimer *pTimer = static_cast( IGameTimerAutoList::AutoList()[i] ); + if ( !pTimer->IsDisabled() && !pTimer->IsMarkedForDeletion() ) + { + g_bAnyGameTimerActive = true; + break; + } + } + } + + m_bOldDisabled = m_bIsDisabled; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_GameTimer::UpdateOnRemove( void ) +{ + BaseClass::UpdateOnRemove(); + + // Update timer presence state + g_bAnyGameTimerActive = false; + for ( int i = 0; i < IGameTimerAutoList::AutoList().Count(); i++ ) + { + C_GameTimer *pTimer = static_cast( IGameTimerAutoList::AutoList()[i] ); + if ( pTimer != this && !pTimer->IsDisabled() && !pTimer->IsMarkedForDeletion() ) + { + g_bAnyGameTimerActive = true; + break; + } + } +} + +#else + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameTimer::TimerThink( void ) +{ + if ( IsDisabled() /*|| IsInCommentaryMode() || gpGlobals->eLoadType == MapLoad_Background*/ ) + { + SetContextThink( &CGameTimer::TimerThink, gpGlobals->curtime + 0.05, TIMER_THINK ); + return; + } + + float flTime = GetTimeRemaining(); + + int nTick = (int)floorf( flTime ); + if (nTick != m_OnTick.Get()) + { + m_OnTick.Set( nTick, this, this ); + } + + if ( flTime <= 0.0f ) + { + OnTimerFinished(); + PauseTimer(); + if (m_bDisableOnFinish) + m_bIsDisabled = true; + return; + } + else if ( flTime <= GetWarnTime() && !m_bStartedWarn) + { + OnTimerWarned(); + m_bStartedWarn = true; + } + + SetContextThink( &CGameTimer::TimerThink, gpGlobals->curtime + 0.05, TIMER_THINK ); +} + +//----------------------------------------------------------------------------- +// Purpose: To set the initial timer duration +//----------------------------------------------------------------------------- +void CGameTimer::SetTimeRemaining( float flTime ) +{ + // make sure we don't go over our max length + flTime = m_flTimerMaxLength > 0 ? MIN( flTime, m_flTimerMaxLength ) : flTime; + + m_flTimeRemaining = flTime; + m_flTimerEndTime = gpGlobals->curtime + m_flTimeRemaining; + + if ( m_flTimeRemaining > m_flWarnTime ) + { + m_bStartedWarn = false; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameTimer::OnTimerFinished() +{ + m_OnFinished.FireOutput( this, this ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameTimer::OnTimerWarned() +{ + m_OnWarned.FireOutput( this, this ); +} + +//----------------------------------------------------------------------------- +// Purpose: Timer is paused at round end, stops the countdown +//----------------------------------------------------------------------------- +void CGameTimer::PauseTimer( CBaseEntity *pActivator ) +{ + if ( IsDisabled() ) + return; + + if ( m_bTimerPaused == false ) + { + m_bTimerPaused = true; + m_flTimeRemaining = m_flTimerEndTime - gpGlobals->curtime; + + m_OnPaused.FireOutput( pActivator ? pActivator : this, this ); + + SetContextThink( NULL, TICK_NEVER_THINK, TIMER_THINK ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: To start or re-start the timer after a pause +//----------------------------------------------------------------------------- +void CGameTimer::ResumeTimer( CBaseEntity *pActivator ) +{ + if ( IsDisabled() ) + return; + + if ( m_bTimerPaused == true ) + { + m_bTimerPaused = false; + m_flTimerEndTime = gpGlobals->curtime + m_flTimeRemaining; + + m_OnResumed.FireOutput( pActivator ? pActivator : this, this ); + + TimerThink(); + + SetContextThink( &CGameTimer::TimerThink, gpGlobals->curtime + 0.05, TIMER_THINK ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Add seconds to the timer while it is running or paused +//----------------------------------------------------------------------------- +void CGameTimer::AddTimerSeconds( float flTimeToAdd ) +{ + if ( IsDisabled() ) + return; + + if ( m_flTimerMaxLength > 0 ) + { + // will adding this many seconds push us over our max length? + if ( GetTimeRemaining() + flTimeToAdd > m_flTimerMaxLength) + { + // adjust to only add up to our max length + flTimeToAdd = m_flTimerMaxLength - GetTimeRemaining(); + } + } + + if ( m_bTimerPaused ) + { + m_flTimeRemaining += flTimeToAdd; + } + else + { + m_flTimerEndTime += flTimeToAdd; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Should we transmit it to the client? +//----------------------------------------------------------------------------- +int CGameTimer::UpdateTransmitState() +{ + if ( !m_bShowInHUD ) + { + return SetTransmitState( FL_EDICT_DONTSEND ); + } + + if ( m_hPlayerFilter || GetTeamNumber() > TEAM_UNASSIGNED ) + { + return SetTransmitState( FL_EDICT_FULLCHECK ); + } + + return SetTransmitState( FL_EDICT_ALWAYS ); +} + +//----------------------------------------------------------------------------- +// Purpose: Which clients should we be transmitting to? +//----------------------------------------------------------------------------- +int CGameTimer::ShouldTransmit( const CCheckTransmitInfo *pInfo ) +{ + int result = BaseClass::ShouldTransmit( pInfo ); + + if ( result != FL_EDICT_DONTSEND ) + { + CBaseEntity *pClient = (CBaseEntity *)(pInfo->m_pClientEnt->GetUnknown()); + if ( pClient ) + { + if ( m_hPlayerFilter ) + { + // Don't send to players who don't pass our filter + if ( !m_hPlayerFilter->PassesFilter( this, pClient ) ) + return FL_EDICT_DONTSEND; + } + else if ( GetTeamNumber() > TEAM_UNASSIGNED ) + { + // If we don't have an explicit filter, then just check if it's on the same team as us + if ( pClient->GetTeamNumber() != GetTeamNumber() ) + return FL_EDICT_DONTSEND; + } + + return FL_EDICT_ALWAYS; + } + } + + return result; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameTimer::InputPause( inputdata_t &input ) +{ + PauseTimer( input.pActivator ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameTimer::InputResume( inputdata_t &input ) +{ + ResumeTimer( input.pActivator ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameTimer::InputSetTime( inputdata_t &input ) +{ + float flTime = input.value.Float(); + SetTimeRemaining( flTime ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameTimer::InputAddTime( inputdata_t &input ) +{ + float flTime = input.value.Float(); + AddTimerSeconds( flTime ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameTimer::InputRemoveTime( inputdata_t &input ) +{ + float flTime = input.value.Float(); + AddTimerSeconds( -flTime ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameTimer::InputSetMaxTime( inputdata_t &input ) +{ + float flTime = input.value.Float(); + m_flTimerMaxLength = flTime; + + if (m_flTimerMaxLength > 0) + { + // make sure our current time is not above the max length + if (GetTimeRemaining() > m_flTimerMaxLength) + { + SetTimeRemaining( m_flTimerMaxLength ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameTimer::InputSetTimerCaption( inputdata_t &input ) +{ + Q_strncpy( m_szTimerCaption.GetForModify(), input.value.String(), MAX_GAME_TIMER_CAPTION ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameTimer::InputSetProgressBarMaxSegments( inputdata_t &input ) +{ + m_nProgressBarMaxSegments = input.value.Int(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameTimer::InputSetProgressBarOverride( inputdata_t &input ) +{ + m_nProgressBarOverride = input.value.Int(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameTimer::InputSetX( inputdata_t &input ) +{ + m_flOverrideX = input.value.Float(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameTimer::InputSetY( inputdata_t &input ) +{ + m_flOverrideY = input.value.Float(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameTimer::InputRestart( inputdata_t &input ) +{ + SetTimeRemaining( m_flTimerInitialLength ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameTimer::InputGetTimeRemaining( inputdata_t &input ) +{ + m_OnGetTimeRemaining.Set( GetTimeRemaining(), input.pActivator, this); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameTimer::InputEnable( inputdata_t &input ) +{ + if (GetTimeRemaining() == 0.0f) + SetTimeRemaining( m_flTimerInitialLength ); + + m_bIsDisabled = false; + if ( !m_bStartPaused ) + ResumeTimer( input.pActivator ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameTimer::InputDisable( inputdata_t &input ) +{ + PauseTimer( input.pActivator ); + m_bIsDisabled = true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CGameTimer::InputSetPlayerFilter( inputdata_t &inputdata ) +{ + m_iszPlayerFilterName = inputdata.value.StringID(); + if ( m_iszPlayerFilterName != NULL_STRING ) + { + m_hPlayerFilter = dynamic_cast(gEntList.FindEntityByName( NULL, m_iszPlayerFilterName, this )); + } + else + { + m_hPlayerFilter = NULL; + } +} + + +#endif diff --git a/sp/src/game/shared/mapbase/game_timer.h b/sp/src/game/shared/mapbase/game_timer.h new file mode 100644 index 00000000..02afb822 --- /dev/null +++ b/sp/src/game/shared/mapbase/game_timer.h @@ -0,0 +1,145 @@ +//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// +// +// Purpose: Generic custom timer entity +// +// Author: Blixibon +// +//=============================================================================// + +#include "cbase.h" +#ifdef CLIENT_DLL +#include "c_baseentity.h" +#else +#include "filters.h" +#endif + +#ifdef CLIENT_DLL +DECLARE_AUTO_LIST( IGameTimerAutoList ); + +#define CGameTimer C_GameTimer +#endif + +#define MAX_GAME_TIMER_CAPTION 32 + +//----------------------------------------------------------------------------- +// Purpose: Displays a custom timer +//----------------------------------------------------------------------------- +class CGameTimer : public CBaseEntity +#ifdef CLIENT_DLL + , public IGameTimerAutoList +#endif +{ +public: + DECLARE_CLASS( CGameTimer, CBaseEntity ); + DECLARE_NETWORKCLASS(); +#ifndef CLIENT_DLL + DECLARE_DATADESC(); +#endif + + CGameTimer(); + ~CGameTimer(); + + virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } + + void Spawn(); +#ifndef CLIENT_DLL + bool KeyValue( const char *szKeyName, const char *szValue ); + virtual bool GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen ); +#endif + + // Based on teamplay_round_timer + virtual float GetTimeRemaining( void ); + virtual float GetTimerMaxLength( void ); + virtual bool StartPaused( void ) { return m_bStartPaused; } + bool ShowTimeRemaining( void ) { return m_bShowTimeRemaining; } + float GetWarnTime( void ); + + const char *GetTimerCaption( void ) { return m_szTimerCaption.Get(); } + + int GetProgressBarMaxSegments( void ) { return m_nProgressBarMaxSegments; } + int GetProgressBarOverride( void ) { return m_nProgressBarOverride; } + + bool OverridesPosition( void ) { return m_flOverrideX != -1.0f || m_flOverrideY != -1.0f; } + void GetPositionOverride( float &flX, float &flY ) { flX = m_flOverrideX; flY = m_flOverrideY; } + + bool IsDisabled( void ) { return m_bIsDisabled; } + bool IsTimerPaused( void ) { return m_bTimerPaused; } + +#ifndef CLIENT_DLL + virtual void SetTimeRemaining( float flTime ); // Set the initial length of the timer + virtual void AddTimerSeconds( float flTimeToAdd ); // Add time to an already running ( or paused ) timer + virtual void OnTimerFinished(); + virtual void OnTimerWarned(); + virtual void PauseTimer( CBaseEntity *pActivator = NULL ); + virtual void ResumeTimer( CBaseEntity *pActivator = NULL ); + + void SetProgressBarMaxSegments( int nSegments ) { m_nProgressBarMaxSegments = nSegments; } + void SetProgressBarOverride( int nSegments ) { m_nProgressBarOverride = nSegments; } + + int UpdateTransmitState(); + + // Inputs + void InputEnable( inputdata_t &input ); + void InputDisable( inputdata_t &input ); + void InputPause( inputdata_t &input ); + void InputResume( inputdata_t &input ); + void InputSetTime( inputdata_t &input ); + void InputAddTime( inputdata_t &input ); + void InputRemoveTime( inputdata_t &input ); + void InputRestart( inputdata_t &input ); + void InputSetMaxTime( inputdata_t &input ); + void InputSetTimerCaption( inputdata_t &input ); + void InputSetProgressBarMaxSegments( inputdata_t &input ); + void InputSetProgressBarOverride( inputdata_t &input ); + void InputSetX( inputdata_t &input ); + void InputSetY( inputdata_t &input ); + void InputGetTimeRemaining( inputdata_t &input ); + void InputSetPlayerFilter( inputdata_t &inputdata ); +#endif + +private: + +#ifdef CLIENT_DLL + void OnDataChanged( DataUpdateType_t updateType ); + void UpdateOnRemove( void ); +#else + int ShouldTransmit( const CCheckTransmitInfo *pInfo ); + void TimerThink( void ); +#endif + +private: + + CNetworkVar( bool, m_bIsDisabled ); + CNetworkVar( float, m_flTimerInitialLength ); + CNetworkVar( float, m_flTimerMaxLength ); + CNetworkVar( float, m_flTimerEndTime ); + CNetworkVar( float, m_flTimeRemaining ); + CNetworkVar( float, m_flWarnTime ); // Time at which timer turns red, starts ticking loudly, etc. + CNetworkVar( int, m_nProgressBarMaxSegments ); // Overrides maximum segments in progress bar if greater than -1 + CNetworkVar( int, m_nProgressBarOverride ); // Overrides progress bar value if greater than -1 + CNetworkVar( float, m_flOverrideX ); + CNetworkVar( float, m_flOverrideY ); + CNetworkVar( bool, m_bTimerPaused ); + CNetworkVar( bool, m_bStartPaused ); + CNetworkVar( bool, m_bShowTimeRemaining ); + CNetworkString( m_szTimerCaption, MAX_GAME_TIMER_CAPTION ); + +#ifdef CLIENT_DLL + bool m_bOldDisabled; +#else + bool m_bStartedWarn; + bool m_bDisableOnFinish; + bool m_bShowInHUD; // TODO: ShowInHUD input? Would require client to know it shouldn't show the timer anymore + + string_t m_iszPlayerFilterName; + CHandle m_hPlayerFilter; + + // Outputs + COutputEvent m_OnFinished; + COutputEvent m_OnPaused; + COutputEvent m_OnResumed; + COutputEvent m_OnWarned; + COutputInt m_OnTick; + COutputFloat m_OnGetTimeRemaining; +#endif +}; From c42f5b69af2fb1b9ca3a0a1fd0bd0e2b35e2d3d8 Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Sat, 26 Jul 2025 22:19:25 +0300 Subject: [PATCH 41/42] Update sqdbg --- sp/src/vscript/sqdbg/sqdbg/server.cpp | 86 ++++++++++++++++++--------- 1 file changed, 57 insertions(+), 29 deletions(-) diff --git a/sp/src/vscript/sqdbg/sqdbg/server.cpp b/sp/src/vscript/sqdbg/sqdbg/server.cpp index 89d3bae4..4f19fa9f 100644 --- a/sp/src/vscript/sqdbg/sqdbg/server.cpp +++ b/sp/src/vscript/sqdbg/sqdbg/server.cpp @@ -5,7 +5,7 @@ // Squirrel Debugger // -#define SQDBG_SV_VER 6 +#define SQDBG_SV_VER 7 #include "sqdbg.h" @@ -845,14 +845,17 @@ public: } else { - for ( hnode_t i = 0; i < m_Nodes.Size(); i++ ) + for ( hnode_t i = 0; i < m_NodeTags.Size(); i++ ) { - node_t &node = m_Nodes[i]; - node.calls = 0; - node.samples = 0.0; - node.sampleStart = 0.0; + nodetag_t *node = &m_NodeTags[i]; + __ObjRelease( node->funcsrc ); + __ObjRelease( node->funcname ); } + m_Nodes.Clear(); + m_NodeTags.Clear(); + m_CallStack.Clear(); + for ( int i = 0; i < vm->_callsstacksize; i++ ) { const SQVM::CallInfo &ci = vm->_callsstack[i]; @@ -928,9 +931,9 @@ public: { m_State = kProfPaused; - if ( m_CallStack.Size() ) + for ( unsigned int i = 0; i < m_CallStack.Size(); i++ ) { - hnode_t caller = m_CallStack.Top(); + hnode_t caller = m_CallStack[i]; node_t *node = &m_Nodes[caller]; node->samples += sample - node->sampleStart; #ifdef _DEBUG @@ -938,9 +941,9 @@ public: #endif } - if ( m_GroupStack.Size() ) + for ( unsigned int i = 0; i < m_GroupStack.Size(); i++ ) { - hgroup_t idx = m_GroupStack.Top(); + hgroup_t idx = m_GroupStack[i]; group_t *group = &m_Groups[idx]; sample_t dt = sample - group->sampleStart; group->samples += dt; @@ -967,16 +970,16 @@ public: sample_t sample = Sample(); - if ( m_CallStack.Size() ) + for ( unsigned int i = 0; i < m_CallStack.Size(); i++ ) { - hnode_t caller = m_CallStack.Top(); + hnode_t caller = m_CallStack[i]; node_t *node = &m_Nodes[caller]; node->sampleStart = sample; } - if ( m_GroupStack.Size() ) + for ( unsigned int i = 0; i < m_GroupStack.Size(); i++ ) { - hgroup_t idx = m_GroupStack.Top(); + hgroup_t idx = m_GroupStack[i]; group_t *group = &m_Groups[idx]; group->sampleStart = sample; } @@ -985,7 +988,7 @@ public: void CallBegin( SQFunctionProto *func ) { - Assert( IsActive() ); + Assert( IsActive() || m_State == kProfPaused ); hnode_t caller = m_CallStack.Size() ? m_CallStack.Top() : INVALID_HANDLE; @@ -1287,6 +1290,10 @@ public: return (int)( buf - bufstart ); } + sample_t sample = ( m_CallStack.Size() && m_State != kProfPaused ) ? + Sample() : + 0.0; + vector< node_t > nodes( m_Nodes ); switch ( type ) @@ -1348,17 +1355,13 @@ public: { totalSamples += node.samples; } - // Within the call frame, accumulate children for a rough estimate - // This will miss any time spent in the body - // of the function excluding subcalls + // Within the call frame, take current time else { - for ( hnode_t j = i; j < nodecount; j++ ) - { - const node_t &nj = nodes[j]; - if ( nj.caller == node.id ) - totalSamples += nj.samples; - } + totalSamples += node.samples; + + if ( m_State != kProfPaused ) + totalSamples += sample - node.sampleStart; } } } @@ -1603,7 +1606,7 @@ private: { if ( us > 0.0 ) { - int len = scsprintf( buf, size, _SC("%6.2f ns"), us * 1.e3 ); + int len = scsprintf( buf, size, _SC("%6.2f ns"), min( us * 1.e3, 999.99 ) ); buf += len; size -= len; } @@ -2095,6 +2098,7 @@ struct objref_t DELEGABLE_META, CUSTOMMEMBER, STACK, + OUTER, #ifdef SQDBG_SUPPORTS_FUNCPROTO_LIST FUNCPROTO, #endif @@ -7922,6 +7926,18 @@ bool SQDebugServer::Get( const objref_t &obj, SQObjectPtr &value ) } } + return false; + } + case objref_t::OUTER: + { + if ( sq_type(obj.src) == OT_CLOSURE && + _integer(obj.key) >= 0 && + _integer(obj.key) < _fp(_closure(obj.src)->_function)->_noutervalues ) + { + value = *_outervalptr( _closure(obj.src)->_outervalues[ _integer(obj.key) ] ); + return true; + } + return false; } #ifdef SQDBG_SUPPORTS_FUNCPROTO_LIST @@ -8116,6 +8132,18 @@ bool SQDebugServer::Set( const objref_t &obj, const SQObjectPtr &value ) } } + return false; + } + case objref_t::OUTER: + { + if ( sq_type(obj.src) == OT_CLOSURE && + _integer(obj.key) >= 0 && + _integer(obj.key) < _fp(_closure(obj.src)->_function)->_noutervalues ) + { + *_outervalptr( _closure(obj.src)->_outervalues[ _integer(obj.key) ] ) = value; + return true; + } + return false; } #ifdef SQDBG_SUPPORTS_FUNCPROTO_LIST @@ -12108,7 +12136,6 @@ bool SQDebugServer::ArithOp( char op, const SQObjectPtr &lhs, const SQObjectPtr void SQDebugServer::ConvertPtr( objref_t &obj ) { - // doesn't convert outer vars if ( obj.type & ~objref_t::PTR ) obj.type = (objref_t::EOBJREF)( obj.type & ~objref_t::PTR ); } @@ -12512,8 +12539,10 @@ bool SQDebugServer::GetObj_Frame( HSQUIRRELVM vm, const SQVM::CallInfo *ci, cons const SQOuterVar &var = func->_outervalues[i]; if ( expression.IsEqualTo( _string(var._name) ) ) { - out.type = objref_t::PTR; + out.type = (objref_t::EOBJREF)( objref_t::PTR | objref_t::OUTER ); out.ptr = _outervalptr( pClosure->_outervalues[i] ); + out.src = pClosure; + out.key = (SQInteger)i; value = *out.ptr; return true; } @@ -20029,7 +20058,6 @@ SQInteger SQDebugServer::SQProfReset( HSQUIRRELVM vm ) tag = _string(arg1); break; default: - Assert(!"UNREACHABLE"); break; } @@ -20540,7 +20568,7 @@ void sqdbg_get_debugger_ref( HSQUIRRELVM vm, SQObjectPtr &ref ) #ifdef DEBUG_HOOK_CACHED_SQDBG // Cache the debugger in an unused variable in the VM -// for at least 20% faster access on debug hook +// for faster access on debug hook // compared to registry table access void sqdbg_set_debugger_cached_debughook( HSQUIRRELVM vm, bool state ) { From 34fb04fe3d59e06785e82d37cd134a1740fea1a1 Mon Sep 17 00:00:00 2001 From: Blixibon Date: Mon, 28 Jul 2025 18:22:26 -0500 Subject: [PATCH 42/42] Updated README --- README | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README b/README index ca133aa7..c77bfd66 100644 --- a/README +++ b/README @@ -147,13 +147,14 @@ Direct contributions: - https://github.com/mapbase-source/mapbase-game-src/pull/3 (HudMessage cutoff fix by arbabf; This is asset-based and not reflected in the code) - Demo autorecord code provided by Klems - cc_emit crash fix provided by 1upD +- npc_barnacle poison zombie crash fix provided by Agrimar - Custom HL2 ammo crate models created by Rykah (Textures created by Blixibon; This is asset-based and, aside from the SLAM crate, not reflected in the code) - Combine lock hardware on door01_left.mdl created by Kralich (This is asset-based and not reflected in the code) - npc_vehicledriver fixes provided by CrAzY - npc_combine cover behavior patches provided by iohnnyboy - logic_playmovie icon created by URAKOLOUY5 (This is asset-based and not reflected in the code) - Dropship APC save/load fix provided by Cvoxulary -- c_arms support on HL2 viewmodels by Inaki (This is asset-based and not reflected in the code) +- c_arms support on HL2 viewmodels by Inaki and ReverendV92 (This is asset-based and not reflected in the code) - Custom c_arms for HL2 characters by Notewell (This is asset-based and not reflected in the code) == Contributions from samisalreadytaken: @@ -182,6 +183,9 @@ Direct contributions: =-- https://github.com/mapbase-source/source-sdk-2013/pull/332 (Fix OOB access) =-- https://github.com/mapbase-source/source-sdk-2013/pull/411 (VScript debugger cleanup) =-- https://github.com/mapbase-source/source-sdk-2013/pull/423 (GetPropFloatArray Vector indexing fix) +=-- https://github.com/mapbase-source/source-sdk-2013/pull/431 (VScript save/restore and debugger fixes) +=-- https://github.com/mapbase-source/source-sdk-2013/pull/441 (VScript instance helper fallback) +=-- https://github.com/mapbase-source/source-sdk-2013/pull/447 (Update VScript sqdbg) == Contributions from z33ky: =-- https://github.com/mapbase-source/source-sdk-2013/pull/21 (Various GCC/Linux compilation fixes) @@ -198,6 +202,7 @@ Direct contributions: =-- https://github.com/mapbase-source/source-sdk-2013/pull/320 (Fix ScriptHook_t initialization order) =-- https://github.com/mapbase-source/source-sdk-2013/pull/321 (Prevent return of dangling Vector/QAngle to VScript) =-- https://github.com/mapbase-source/source-sdk-2013/pull/322 (Small Mapbase fixes) +=-- https://github.com/mapbase-source/source-sdk-2013/pull/436 (VScript member function call safety) == Contributions from Petercov: =-- https://github.com/mapbase-source/source-sdk-2013/pull/182 (NPCs load dynamic interactions from all animation MDLs)