mirror of
https://github.com/polserver/polserver
synced 2026-08-13 08:23:08 -04:00
* Add cppdap debugger library and simple server (#508) * Add cppdap library * Add cppdap server * fixup cmake scripts for windows * Enable caching of cppdap on GitHub actions * Fix CMake for cppdap target * Wait 1s for session close before thread ends * Address review comments - Remove unnecessary use of wait_for - Fix wait_for method * Do not use SocketClientThread for cppdap clients * Address review comments - Cleanup object ownership * Add authorization and event handling (#510) * Add authorization and event handling [WIP] * Use std::weak_ptr; Only allow one debugger conn * Only attach debugger if dbg file exists * Cleanup weak_ptr; Fix listeners; Add handler stubs * Address review comments - PolLock on uoexec and weakptr access - Use make_shared when creating client thread * Add ExecutorDebugListener on_destroy callback * Use cppdap sockets instead of clib sockets * Update cppdap repository to 2703cf4 * Implement server address binding * Update cppdap repository to eeca5ce * Fix warnings * Use weak_from_this * Fix stalled executors on disconnect * Fix stalled executors on re-attach * Clear uoexec weakptr on all debugger detaches * Move server to gamestate * move DapDebugServer into Network namespace * move DapDebugServer into GameState for storage * Move DapDebugServer into NetworkManager * Fix missing memory include * Add more events (#513) * Add more events * Implement dbg_step_out * Fix breakpoint clearing * Add even more events (#515) * add launch request * Add custom `processes` command * fixup loop * Refactoring to move handlers to member functions * Add VariablesRequest and ScopesRequest * use std::varaint for Handles * add pollocks * refactor Handles to new file * move custom messages to proto.h * move clientthread * some formatting / cleanup * Add missing PolLock * move dap variables creation to Handles * appobj members * rename ClientThread to DebugClientThread * rename LaunchRequest args to arg * Fix compiler error on size_t * Add support for EvaluateRequest and SetVariableRequest (#519) * Update grammar for evaluateUnit * Cleanup various compiler code * Add support for EvaluateRequest Only supports values, variables, element access, member access * Add report clearing to reinitialize state * Add support for SetVariableRequest * Fix CI build errors * Move ExpressionEvaluator to Bscript * Fix formatting * Fix include * Refactor global and frame variable references * Encapsulate Executor debugging environment; Fix step over (#524) * Fix step over * Some cleanup of executor - Remove unused debug states - Rename members for consistency * move debugging environment to new class * Move instruction inspection to class * Cleanup slashes on Windows; Add Executor state to processes response (#527) * Fix backspaces in script names on Windows * Fix compilation warnings with data loss * Use boost::icontains for string comparison * Add Executor state to processes response * Updates for libfmt * Fix tests; Fix cppdap build for Mac universal binary (#607) * fix tests * fix cppdap build for mac universal binary * Update cppdap to 2a4c7cf * Add tests; Modify launch behavior to not stop at entry (redo) (#609) * Remove unused setExceptionBreakpoints handler * Add debugger tests * Launching a script should not set attaching state * Add launch tests * Do not check for connection in debugger tests * Rename `program` to `script` in protocol structs * Fix launch stopAtEntry; quote string var values * Fix compilation error on Windows * Add configfiles doc entry * Various fixes for finishing touches; Add core-changes (#610) * switch cmake projects * simple docs * cmake move make_directory * Fix launch request with relative, absolute, package paths * make relative paths as absolute in source request * Add true, false, uninit to ExpressionEvaluator * Re-add setExceptionBreakpoints to avoid unhandled message errors * Better debug thread logging with instance numbers * launched scripts should terminate on disconnect (according to DAP spec) * Properly handle exited event in client thread * Add core-changes * Address Discord comments - Mention vscode-escript in core-changes - Re-add "cppdap already built" in cmake * change cmake escriptgrammarlib visibility to public
471 lines
11 KiB
C++
471 lines
11 KiB
C++
/** @file
|
|
*
|
|
* @par History
|
|
* - 2005/11/26 Shinigami: changed "strcmp" into "stricmp" to suppress Script Errors
|
|
* - 2008/02/11 Turley: BStruct::unpack() will accept zero length Structs
|
|
* - 2009/09/05 Turley: Added struct .? and .- as shortcut for .exists() and .erase()
|
|
* - 2009/12/21 Turley: ._method() call fix
|
|
*/
|
|
|
|
|
|
#include "bstruct.h"
|
|
|
|
#include <stddef.h>
|
|
|
|
#include "../clib/passert.h"
|
|
#include "../clib/stlutil.h"
|
|
#include "berror.h"
|
|
#include "bobject.h"
|
|
#include "contiter.h"
|
|
#include "executor.h"
|
|
#include "impstr.h"
|
|
#include "objmethods.h"
|
|
|
|
namespace Pol
|
|
{
|
|
namespace Bscript
|
|
{
|
|
BStruct::BStruct() : BObjectImp( OTStruct ), contents_() {}
|
|
|
|
BStruct::BStruct( BObjectType type ) : BObjectImp( type ), contents_() {}
|
|
|
|
BStruct::BStruct( const BStruct& other, BObjectType type ) : BObjectImp( type ), contents_()
|
|
{
|
|
for ( const auto& elem : other.contents_ )
|
|
{
|
|
const std::string& key = elem.first;
|
|
const BObjectRef& bvalref = elem.second;
|
|
|
|
contents_[key] = BObjectRef( new BObject( bvalref->impref().copy() ) );
|
|
}
|
|
}
|
|
|
|
BStruct::BStruct( std::istream& is, unsigned size, BObjectType type )
|
|
: BObjectImp( type ), contents_()
|
|
{
|
|
for ( unsigned i = 0; i < size; ++i )
|
|
{
|
|
BObjectImp* keyimp = BObjectImp::unpack( is );
|
|
BObjectImp* valimp = BObjectImp::unpack( is );
|
|
if ( keyimp != nullptr && valimp != nullptr && keyimp->isa( OTString ) )
|
|
{
|
|
String* str = static_cast<String*>( keyimp );
|
|
|
|
contents_[str->value()].set( new BObject( valimp ) );
|
|
|
|
BObject cleaner( str );
|
|
}
|
|
else
|
|
{
|
|
if ( keyimp )
|
|
BObject objk( keyimp );
|
|
if ( valimp )
|
|
BObject objv( valimp );
|
|
}
|
|
}
|
|
}
|
|
|
|
BObjectImp* BStruct::copy() const
|
|
{
|
|
passert( isa( OTStruct ) );
|
|
return new BStruct( *this, OTStruct );
|
|
}
|
|
char BStruct::packtype() const
|
|
{
|
|
return 't';
|
|
}
|
|
|
|
const char* BStruct::typetag() const
|
|
{
|
|
return "struct";
|
|
}
|
|
|
|
const char* BStruct::typeOf() const
|
|
{
|
|
return "Struct";
|
|
}
|
|
u8 BStruct::typeOfInt() const
|
|
{
|
|
return OTStruct;
|
|
}
|
|
|
|
|
|
BObjectImp* BStruct::unpack( std::istream& is )
|
|
{
|
|
unsigned size;
|
|
char colon;
|
|
if ( !( is >> size >> colon ) )
|
|
{
|
|
return new BError( "Unable to unpack struct elemcount" );
|
|
}
|
|
if ( (int)size < 0 )
|
|
{
|
|
return new BError(
|
|
"Unable to unpack struct elemcount. Length given must be positive integer!" );
|
|
}
|
|
if ( colon != ':' )
|
|
{
|
|
return new BError( "Unable to unpack struct elemcount. Bad format. Colon not found!" );
|
|
}
|
|
return new BStruct( is, size, OTStruct );
|
|
}
|
|
|
|
void BStruct::FormatForStringRep( std::ostream& os, const std::string& key,
|
|
const BObjectRef& bvalref ) const
|
|
{
|
|
os << key << " = " << bvalref->impref().getFormattedStringRep();
|
|
}
|
|
|
|
class BStructIterator final : public ContIterator
|
|
{
|
|
public:
|
|
BStructIterator( BStruct* pDict, BObject* pIterVal );
|
|
virtual BObject* step() override;
|
|
|
|
private:
|
|
BObject m_StructObj;
|
|
BStruct* m_pStruct;
|
|
BObjectRef m_IterVal;
|
|
std::string key;
|
|
bool m_First;
|
|
};
|
|
BStructIterator::BStructIterator( BStruct* pStruct, BObject* pIterVal )
|
|
: m_StructObj( pStruct ),
|
|
m_pStruct( pStruct ),
|
|
m_IterVal( pIterVal ),
|
|
key( "" ),
|
|
m_First( true )
|
|
{
|
|
}
|
|
|
|
BObject* BStructIterator::step()
|
|
{
|
|
if ( m_First )
|
|
{
|
|
auto itr = m_pStruct->contents_.begin();
|
|
if ( itr == m_pStruct->contents_.end() )
|
|
return nullptr;
|
|
|
|
m_First = false;
|
|
key = ( *itr ).first;
|
|
m_IterVal->setimp( new String( key ) );
|
|
|
|
BObjectRef& oref = ( *itr ).second;
|
|
return oref.get();
|
|
}
|
|
else
|
|
{
|
|
auto itr = m_pStruct->contents_.find( key );
|
|
if ( itr == m_pStruct->contents_.end() )
|
|
return nullptr;
|
|
++itr;
|
|
if ( itr == m_pStruct->contents_.end() )
|
|
return nullptr;
|
|
|
|
key = ( *itr ).first;
|
|
m_IterVal->setimp( new String( key ) );
|
|
|
|
BObjectRef& oref = ( *itr ).second;
|
|
return oref.get();
|
|
}
|
|
}
|
|
|
|
ContIterator* BStruct::createIterator( BObject* pIterVal )
|
|
{
|
|
return new BStructIterator( this, pIterVal );
|
|
}
|
|
|
|
size_t BStruct::sizeEstimate() const
|
|
{
|
|
size_t size = sizeof( BStruct );
|
|
for ( const auto& elem : contents_ )
|
|
{
|
|
const std::string& bkey = elem.first;
|
|
const BObjectRef& bvalref = elem.second;
|
|
size += bkey.capacity() + bvalref.sizeEstimate() + ( sizeof( void* ) * 3 + 1 ) / 2;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
size_t BStruct::mapcount() const
|
|
{
|
|
return contents_.size();
|
|
}
|
|
|
|
|
|
BObjectRef BStruct::set_member( const char* membername, BObjectImp* value, bool copy )
|
|
{
|
|
std::string key( membername );
|
|
BObjectImp* target = copy ? value->copy() : value;
|
|
auto itr = contents_.find( key );
|
|
if ( itr != contents_.end() )
|
|
{
|
|
BObjectRef& oref = ( *itr ).second;
|
|
oref->setimp( target );
|
|
return oref;
|
|
}
|
|
else
|
|
{
|
|
BObjectRef ref( new BObject( target ) );
|
|
contents_[key] = ref;
|
|
return ref;
|
|
}
|
|
}
|
|
|
|
// used programmatically
|
|
const BObjectImp* BStruct::FindMember( const char* name )
|
|
{
|
|
std::string key( name );
|
|
|
|
auto itr = contents_.find( key );
|
|
if ( itr != contents_.end() )
|
|
{
|
|
return ( *itr ).second->impptr();
|
|
}
|
|
else
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
BObjectRef BStruct::get_member( const char* membername )
|
|
{
|
|
std::string key( membername );
|
|
|
|
auto itr = contents_.find( key );
|
|
if ( itr != contents_.end() )
|
|
{
|
|
return ( *itr ).second;
|
|
}
|
|
else
|
|
{
|
|
return BObjectRef( UninitObject::create() );
|
|
}
|
|
}
|
|
|
|
BObjectRef BStruct::OperSubscript( const BObject& obj )
|
|
{
|
|
if ( obj->isa( OTString ) )
|
|
{
|
|
const String* keystr = static_cast<const String*>( obj.impptr() );
|
|
|
|
auto itr = contents_.find( keystr->value() );
|
|
if ( itr != contents_.end() )
|
|
{
|
|
BObjectRef& oref = ( *itr ).second;
|
|
return oref;
|
|
}
|
|
else
|
|
{
|
|
return BObjectRef( UninitObject::create() );
|
|
}
|
|
}
|
|
else if ( obj->isa( OTLong ) )
|
|
{
|
|
throw std::runtime_error( "Struct members cannot be accessed by an integer index" );
|
|
}
|
|
else
|
|
{
|
|
return BObjectRef( new BError( "Struct members can only be accessed by name" ) );
|
|
}
|
|
}
|
|
|
|
BObjectImp* BStruct::array_assign( BObjectImp* idx, BObjectImp* target, bool copy )
|
|
{
|
|
if ( idx->isa( OTString ) )
|
|
{
|
|
BObjectImp* new_target = copy ? target->copy() : target;
|
|
|
|
String* keystr = static_cast<String*>( idx );
|
|
|
|
auto itr = contents_.find( keystr->value() );
|
|
if ( itr != contents_.end() )
|
|
{
|
|
BObjectRef& oref = ( *itr ).second;
|
|
oref->setimp( new_target );
|
|
return new_target;
|
|
}
|
|
else
|
|
{
|
|
contents_[keystr->value()].set( new BObject( new_target ) );
|
|
return new_target;
|
|
}
|
|
}
|
|
else if ( idx->isa( OTLong ) )
|
|
{
|
|
throw std::runtime_error(
|
|
"some fool tried to use operator[] := on a struct, with an Integer index" );
|
|
}
|
|
else
|
|
{
|
|
return new BError( "Struct members can only be accessed by name" );
|
|
}
|
|
}
|
|
|
|
void BStruct::addMember( const char* name, BObjectRef val )
|
|
{
|
|
std::string key( name );
|
|
contents_[key] = val;
|
|
}
|
|
|
|
void BStruct::addMember( const char* name, BObjectImp* imp )
|
|
{
|
|
std::string key( name );
|
|
contents_[key] = BObjectRef( imp );
|
|
}
|
|
|
|
BObjectImp* BStruct::call_method_id( const int id, Executor& ex, bool /*forcebuiltin*/ )
|
|
{
|
|
BObject* keyobj;
|
|
BObject* valobj;
|
|
switch ( id )
|
|
{
|
|
case MTH_SIZE:
|
|
if ( ex.numParams() == 0 )
|
|
return new BLong( static_cast<int>( contents_.size() ) );
|
|
else
|
|
return new BError( "struct.size() doesn't take parameters." );
|
|
|
|
case MTH_ERASE:
|
|
if ( ex.numParams() == 1 && ( keyobj = ex.getParamObj( 0 ) ) != nullptr )
|
|
{
|
|
if ( !keyobj->isa( OTString ) )
|
|
return new BError( "Struct keys must be strings" );
|
|
String* strkey = static_cast<String*>( keyobj->impptr() );
|
|
int nremove = static_cast<int>( contents_.erase( strkey->value() ) );
|
|
return new BLong( nremove );
|
|
}
|
|
else
|
|
{
|
|
return new BError( "struct.erase(key) requires a parameter." );
|
|
}
|
|
break;
|
|
case MTH_INSERT:
|
|
if ( ex.numParams() == 2 && ( keyobj = ex.getParamObj( 0 ) ) != nullptr &&
|
|
( valobj = ex.getParamObj( 1 ) ) != nullptr )
|
|
{
|
|
if ( !keyobj->isa( OTString ) )
|
|
return new BError( "Struct keys must be strings" );
|
|
String* strkey = static_cast<String*>( keyobj->impptr() );
|
|
contents_[strkey->value()] = BObjectRef( new BObject( valobj->impptr()->copy() ) );
|
|
return new BLong( static_cast<int>( contents_.size() ) );
|
|
}
|
|
else
|
|
{
|
|
return new BError( "struct.insert(key,value) requires two parameters." );
|
|
}
|
|
break;
|
|
case MTH_EXISTS:
|
|
if ( ex.numParams() == 1 && ( keyobj = ex.getParamObj( 0 ) ) != nullptr )
|
|
{
|
|
if ( !keyobj->isa( OTString ) )
|
|
return new BError( "Struct keys must be strings" );
|
|
String* strkey = static_cast<String*>( keyobj->impptr() );
|
|
int count = static_cast<int>( contents_.count( strkey->value() ) );
|
|
return new BLong( count );
|
|
}
|
|
else
|
|
{
|
|
return new BError( "struct.exists(key) requires a parameter." );
|
|
}
|
|
|
|
case MTH_KEYS:
|
|
if ( ex.numParams() == 0 )
|
|
{
|
|
std::unique_ptr<ObjArray> arr( new ObjArray );
|
|
for ( const auto& content : contents_ )
|
|
{
|
|
arr->addElement( new String( content.first ) );
|
|
}
|
|
return arr.release();
|
|
}
|
|
else
|
|
return new BError( "struct.keys() doesn't take parameters." );
|
|
break;
|
|
default:
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
BObjectImp* BStruct::call_method( const char* methodname, Executor& ex )
|
|
{
|
|
ObjMethod* objmethod = getKnownObjMethod( methodname );
|
|
if ( objmethod != nullptr )
|
|
return this->call_method_id( objmethod->id, ex );
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
void BStruct::packonto( std::ostream& os ) const
|
|
{
|
|
os << packtype() << contents_.size() << ":";
|
|
for ( const auto& content : contents_ )
|
|
{
|
|
const std::string& key = content.first;
|
|
const BObjectRef& bvalref = content.second;
|
|
|
|
String::packonto( os, key );
|
|
bvalref->impref().packonto( os );
|
|
}
|
|
}
|
|
|
|
std::string BStruct::getStringRep() const
|
|
{
|
|
OSTRINGSTREAM os;
|
|
os << typetag() << "{ ";
|
|
bool any = false;
|
|
|
|
for ( const auto& content : contents_ )
|
|
{
|
|
const std::string& key = content.first;
|
|
const BObjectRef& bvalref = content.second;
|
|
|
|
if ( any )
|
|
os << ", ";
|
|
else
|
|
any = true;
|
|
|
|
FormatForStringRep( os, key, bvalref );
|
|
}
|
|
|
|
os << " }";
|
|
|
|
return OSTRINGSTREAM_STR( os );
|
|
}
|
|
|
|
|
|
BObjectRef BStruct::operDotPlus( const char* name )
|
|
{
|
|
std::string key( name );
|
|
if ( contents_.count( key ) == 0 )
|
|
{
|
|
auto pnewobj = new BObject( new UninitObject );
|
|
contents_[key] = BObjectRef( pnewobj );
|
|
return BObjectRef( pnewobj );
|
|
}
|
|
else
|
|
{
|
|
return BObjectRef( new BError( "Member already exists" ) );
|
|
}
|
|
}
|
|
|
|
BObjectRef BStruct::operDotMinus( const char* name )
|
|
{
|
|
std::string key( name );
|
|
contents_.erase( key );
|
|
return BObjectRef( new BLong( 1 ) );
|
|
}
|
|
|
|
BObjectRef BStruct::operDotQMark( const char* name )
|
|
{
|
|
std::string key( name );
|
|
int count = static_cast<int>( contents_.count( key ) );
|
|
return BObjectRef( new BLong( count ) );
|
|
}
|
|
|
|
const BStruct::Contents& BStruct::contents() const
|
|
{
|
|
return contents_;
|
|
}
|
|
}
|
|
}
|