#include "daScript/misc/platform.h"
#include "daScript/simulate/simulate.h"
#include "daScript/simulate/simulate_nodes.h"
#include "daScript/simulate/runtime_string.h"
#include "daScript/simulate/debug_print.h"
#include "daScript/misc/fpe.h"
#include "daScript/misc/string_writer.h"
#include "daScript/misc/debug_break.h"
#include "daScript/ast/ast.h"
#include "misc/include_fmt.h"
#include
#include
namespace das
{
GcRootLambda::GcRootLambda( const Lambda & that, Context * _context ) : Lambda(that.capture) {
context = _context;
context->addGcRoot( (void *)capture, nullptr );
}
GcRootLambda::~GcRootLambda() {
if ( capture && context && (context->category.value & uint32_t(das::ContextCategory::dead)) == 0u ) {
context->removeGcRoot( (void *)capture );
}
}
bool PointerDimIterator::first ( Context &, char * _value ) {
char ** value = (char **) _value;
if ( data != data_end ) {
*value = *data;
return true;
} else {
return false;
}
}
bool PointerDimIterator::next ( Context &, char * _value ) {
char ** value = (char **) _value;
if ( ++data != data_end ) {
*value = *data;
return true;
} else {
return false;
}
}
void PointerDimIterator::close ( Context & context, char * _value ) {
if ( _value ) {
char ** value = (char **) _value;
*value = nullptr;
}
context.freeIterator((char *)this, debugInfo);
}
// this is here to occasionally investigate untyped evaluation paths
#define WARN_SLOW_CAST(TYPE)
// #define WARN_SLOW_CAST(TYPE) DAS_ASSERTF(0, "internal perofrmance issue, casting eval to eval##TYPE" );
SimNode * SimNode::copyNode ( Context &, NodeAllocator * code ) {
auto prefix = ((NodePrefix *)this) - 1;
#ifndef DAS_NO_ASSERTIONS
DAS_ASSERTF(prefix->magic==0xdeadc0de,"node was allocated on the heap without prefix");
#endif
char * newNode;
if ( code->prefixWithHeader ) {
newNode = code->allocate(prefix->size + sizeof(NodePrefix));
memcpy ( newNode, ((char *)this) - sizeof(NodePrefix), sizeof(NodePrefix));
newNode += sizeof(NodePrefix);
} else {
newNode = code->allocate(prefix->size);
}
memcpy ( newNode, (char *)this, prefix->size );
return (SimNode *) newNode;
}
bool SimNode::evalBool ( Context & context ) {
WARN_SLOW_CAST(Bool);
return cast::to(eval(context));
}
float SimNode::evalFloat ( Context & context ) {
WARN_SLOW_CAST(Float);
return cast::to(eval(context));
}
double SimNode::evalDouble(Context & context) {
WARN_SLOW_CAST(Double);
return cast::to(eval(context));
}
int32_t SimNode::evalInt ( Context & context ) {
WARN_SLOW_CAST(Int);
return cast::to(eval(context));
}
uint32_t SimNode::evalUInt ( Context & context ) {
WARN_SLOW_CAST(UInt);
return cast::to(eval(context));
}
int64_t SimNode::evalInt64 ( Context & context ) {
WARN_SLOW_CAST(Int64);
return cast::to(eval(context));
}
uint64_t SimNode::evalUInt64 ( Context & context ) {
WARN_SLOW_CAST(UInt64);
return cast::to(eval(context));
}
char * SimNode::evalPtr ( Context & context ) {
WARN_SLOW_CAST(Ptr);
return cast::to(eval(context));
}
SimNode * SimNode_WithErrorMessage::copyNode ( Context & context, NodeAllocator * code ) {
SimNode_WithErrorMessage * that = (SimNode_WithErrorMessage *) SimNode::copyNode(context, code);
if ( errorMessage ) {
that->errorMessage = errorMessage[0]==0 ? "" : code->allocateName(errorMessage);
}
return that;
}
vec4f SimNode_NOP::eval ( Context & ) {
return v_zero();
}
vec4f SimNode_DeleteStructPtr::eval ( Context & context ) {
DAS_PROFILE_NODE
auto pStruct = (char **) subexpr->evalPtr(context);
pStruct = pStruct + total - 1;
for ( uint32_t i=0, is=total; i!=is; ++i, pStruct-- ) {
if ( *pStruct ) {
if ( persistent ) {
das_aligned_free16(*pStruct);
} else if ( isLambda ) {
context.free(*pStruct - 16, structSize + 16, &debugInfo);
} else {
context.free(*pStruct, structSize, &debugInfo);
}
*pStruct = nullptr;
}
}
return v_zero();
}
vec4f SimNode_DeleteClassPtr::eval ( Context & context ) {
DAS_PROFILE_NODE
auto pStruct = (char **) subexpr->evalPtr(context);
pStruct = pStruct + total - 1;
auto sizeOf = sizeexpr->evalInt(context);
for ( uint32_t i=0, is=total; i!=is; ++i, pStruct-- ) {
if ( *pStruct ) {
if (persistent) {
das_aligned_free16(*pStruct);
} else {
context.free(*pStruct, sizeOf, &debugInfo);
}
*pStruct = nullptr;
}
}
return v_zero();
}
vec4f SimNode_DeleteLambda::eval ( Context & context ) {
DAS_PROFILE_NODE
auto pLambda = (Lambda *) subexpr->evalPtr(context);
pLambda = pLambda + total - 1;
for ( uint32_t i=0, is=total; i!=is; ++i, pLambda-- ) {
if ( pLambda->capture ) {
SimFunction ** fnMnh = (SimFunction **) pLambda->capture;
SimFunction * simFunc = fnMnh[1];
if (!simFunc) context.throw_error_at(debugInfo, "lambda finalizer is a null function%s", errorMessage);
vec4f argValues[1] = {
cast::from(pLambda->capture)
};
context.call(simFunc, argValues, 0);
pLambda->capture = nullptr;
}
}
return v_zero();
}
vec4f SimNode_Swizzle::eval ( Context & context ) {
DAS_PROFILE_NODE
union {
vec4f res;
int32_t val[4];
} R, S;
S.res = value->eval(context);
R.val[0] = S.val[fields[0]];
R.val[1] = S.val[fields[1]];
R.val[2] = S.val[fields[2]];
R.val[3] = S.val[fields[3]];
return R.res;
}
vec4f SimNode_Swizzle64::eval ( Context & context ) {
DAS_PROFILE_NODE
union {
vec4f res;
int64_t val[2];
} R, S;
S.res = value->eval(context);
R.val[0] = S.val[fields[0]];
R.val[1] = S.val[fields[1]];
return R.res;
}
// SimNode_MakeBlock
vec4f SimNode_MakeBlock::eval ( Context & context ) {
DAS_PROFILE_NODE
Block * block = (Block *) ( context.stack.sp() + stackTop );
block->stackOffset = context.stack.spi();
block->argumentsOffset = argStackTop ? (context.stack.spi() + argStackTop) : 0;
block->body = subexpr;
block->aotFunction = nullptr;
block->jitFunction = nullptr;
block->functionArguments = context.abiArguments();
block->info = info;
return cast::from(block);
}
// SimNode_Debug
void das_debug ( Context * context, TypeInfo * typeInfo, const char * FILE, int LINE, vec4f res, const char * message ) {
TextWriter ssw;
if ( message ) ssw source_iterators, totalSources*sizeof(SimNode *));
that->source_iterators = new_source_iterators;
auto newStackTop = (uint32_t *) (bytes + totalSources * sizeof(SimNode *));
memcpy ( newStackTop, that->stackTop, totalSources * sizeof(uint32_t));
that->stackTop = newStackTop;
} else {
source_iterators = nullptr;
stackTop = nullptr;
}
return that;
}
void SimNode_ForWithIteratorBase::closeIterators ( Iterator ** sources, char ** pi, Context & context ) {
for ( int t=int(totalSources)-1; t>=0; --t ) {
sources[t]->close(context, pi[t]);
}
}
vec4f SimNode_ForWithIteratorBase::eval ( Context & context ) {
// note: this is the 'slow' version, to which we fall back when there are too many sources
DAS_PROFILE_NODE
int totalCount = int(totalSources);
vector pi(totalCount);
for ( int t=0; t!=totalCount; ++t ) {
pi[t] = context.stack.sp() + stackTop[t];
}
vector sources(totalCount);
for ( int t=0; t!=totalCount; ++t ) {
vec4f ll = source_iterators[t]->eval(context);
sources[t] = cast::to(ll);
}
bool needLoop = true;
SimNode ** __restrict tail = list + total;
for ( int t=0; t!=totalCount; ++t ) {
sources[t]->isOpen = true;
needLoop = sources[t]->first(context, pi[t]) && needLoop;
if ( context.stopFlags ) goto loopend;
}
if ( !needLoop ) goto loopend;
while ( !context.stopFlags ) {
SimNode ** __restrict body = list;
loopbegin:;
for (; body!=tail; ++body) {
(*body)->eval(context);
DAS_PROCESS_LOOP_FLAGS(break);
}
for ( int t=0; t!=totalCount; ++t ){
if ( !sources[t]->next(context, pi[t]) ) goto loopend;
if ( context.stopFlags ) goto loopend;
}
}
loopend:
closeIterators(sources.data(), pi.data(), context);
evalFinal(context);
context.stopFlags &= ~EvalFlags::stopForBreak;
return v_zero();
}
#if DAS_ENABLE_KEEPALIVE
vec4f SimNodeKeepAlive_ForWithIteratorBase::eval ( Context & context ) {
// note: this is the 'slow' version, to which we fall back when there are too many sources
DAS_PROFILE_NODE
int totalCount = int(totalSources);
vector pi(totalCount);
for ( int t=0; t!=totalCount; ++t ) {
pi[t] = context.stack.sp() + stackTop[t];
}
vector sources(totalCount);
for ( int t=0; t!=totalCount; ++t ) {
vec4f ll = source_iterators[t]->eval(context);
sources[t] = cast::to(ll);
}
bool needLoop = true;
SimNode ** __restrict tail = list + total;
for ( int t=0; t!=totalCount; ++t ) {
sources[t]->isOpen = true;
needLoop = sources[t]->first(context, pi[t]) && needLoop;
if ( context.stopFlags ) goto loopend;
}
if ( !needLoop ) goto loopend;
while ( !context.stopFlags ) {
SimNode ** __restrict body = list;
loopbegin:;
DAS_KEEPALIVE_LOOP(&context);
for (; body!=tail; ++body) {
(*body)->eval(context);
DAS_PROCESS_LOOP_FLAGS(break);
}
for ( int t=0; t!=totalCount; ++t ){
if ( !sources[t]->next(context, pi[t]) ) goto loopend;
if ( context.stopFlags ) goto loopend;
}
}
loopend:
closeIterators(sources.data(), pi.data(), context);
evalFinal(context);
context.stopFlags &= ~EvalFlags::stopForBreak;
return v_zero();
}
#endif
#if DAS_DEBUGGER
vec4f SimNodeDebug_ForWithIteratorBase::eval ( Context & context ) {
DAS_PROFILE_NODE
auto totalCount = int(totalSources);
vector pi(totalCount);
for ( int t=0; t!=totalCount; ++t ) {
pi[t] = context.stack.sp() + this->stackTop[t];
}
vector sources(totalCount);
for ( int t=0; t!=totalCount; ++t ) {
vec4f ll = this->source_iterators[t]->eval(context);
sources[t] = cast::to(ll);
}
bool needLoop = true;
SimNode ** __restrict tail = this-> list + this->total;
for ( int t=0; t!=totalCount; ++t ) {
sources[t]->isOpen = true;
needLoop = sources[t]->first(context, pi[t]) && needLoop;
if ( context.stopFlags ) goto loopend;
}
if ( !needLoop ) goto loopend;
while ( !context.stopFlags ) {
SimNode ** __restrict body = this->list;
loopbegin:;
for (; body!=tail; ++body) {
DAS_SINGLE_STEP(context,(*body)->debugInfo,true);
(*body)->eval(context);
DAS_PROCESS_LOOP_FLAGS(break);
}
for ( int t=0; t!=totalCount; ++t ){
if ( !sources[t]->next(context, pi[t]) ) goto loopend;
if ( context.stopFlags ) goto loopend;
}
}
loopend:
closeIterators(sources.data(), pi.data(), context);
this->evalFinal(context);
context.stopFlags &= ~EvalFlags::stopForBreak;
return v_zero();
}
#endif
// SimNode_CallBase
SimNode * SimNode_CallBase::copyNode ( Context & context, NodeAllocator * code ) {
SimNode_CallBase * that = (SimNode_CallBase *) SimNode_WithErrorMessage::copyNode(context, code);
if ( nArguments ) {
SimNode ** newArguments = (SimNode **) code->allocate(nArguments * sizeof(SimNode *));
memcpy ( newArguments, that->arguments, nArguments * sizeof(SimNode *));
that->arguments = newArguments;
if ( that->types ) {
TypeInfo ** newTypes = (TypeInfo **) code->allocate(nArguments * sizeof(TypeInfo **));
memcpy ( newTypes, that->types, nArguments * sizeof(TypeInfo **));
that->types = newTypes;
}
}
if ( fnPtr ) {
that->fnPtr = context.fnByMangledName(fnPtr->mangledNameHash);
// printf("CALL %p -> %p\n", fnPtr, fnPtr );
}
return that;
}
// SimNode_Final
SimNode * SimNode_Final::copyNode ( Context & context, NodeAllocator * code ) {
SimNode_Final * that = (SimNode_Final *) SimNode::copyNode(context, code);
if ( totalFinal ) {
SimNode ** newList = (SimNode **) code->allocate(totalFinal * sizeof(SimNode *));
memcpy ( newList, that->finalList, totalFinal*sizeof(SimNode *));
that->finalList = newList;
}
return that;
}
// SimNode_Block
SimNode * SimNode_Block::copyNode ( Context & context, NodeAllocator * code ) {
SimNode_Block * that = (SimNode_Block *) SimNode_Final::copyNode(context, code);
if ( total ) {
SimNode ** newList = (SimNode **) code->allocate(total * sizeof(SimNode *));
memcpy ( newList, that->list, total*sizeof(SimNode *));
that->list = newList;
}
if ( totalLabels ) {
uint32_t * newLabels = (uint32_t *) code->allocate(totalLabels * sizeof(uint32_t));
memcpy ( newLabels, that->labels, totalLabels*sizeof(uint32_t));
that->labels = newLabels;
}
return that;
}
vec4f SimNode_Block::eval ( Context & context ) {
DAS_PROFILE_NODE
SimNode ** __restrict tail = list + total;
for (SimNode ** __restrict body = list; body!=tail; ++body) {
(*body)->eval(context);
if ( context.stopFlags ) break;
}
evalFinal(context);
return v_zero();
}
#if DAS_DEBUGGER
vec4f SimNodeDebug_Block::eval ( Context & context ) {
DAS_PROFILE_NODE
SimNode ** __restrict tail = list + total;
for (SimNode ** __restrict body = list; body!=tail; ++body) {
DAS_SINGLE_STEP(context,(*body)->debugInfo,false);
(*body)->eval(context);
if ( context.stopFlags ) break;
}
evalFinalSingleStep(context);
return v_zero();
}
#endif
vec4f SimNode_BlockNF::eval ( Context & context ) {
DAS_PROFILE_NODE
SimNode ** __restrict tail = list + total;
for (SimNode ** __restrict body = list; body!=tail; ++body) {
(*body)->eval(context);
if ( context.stopFlags ) break;
}
return v_zero();
}
#if DAS_DEBUGGER
vec4f SimNodeDebug_BlockNF::eval ( Context & context ) {
DAS_PROFILE_NODE
SimNode ** __restrict tail = list + total;
for (SimNode ** __restrict body = list; body!=tail; ++body) {
DAS_SINGLE_STEP(context,(*body)->debugInfo,false);
(*body)->eval(context);
if ( context.stopFlags ) break;
}
return v_zero();
}
#endif
vec4f SimNode_ClosureBlock::eval ( Context & context ) {
DAS_PROFILE_NODE
SimNode ** __restrict tail = list + total;
for (SimNode ** __restrict body = list; body!=tail; ++body) {
(*body)->eval(context);
if ( context.stopFlags ) break;
}
evalFinal(context);
if ( context.stopFlags & EvalFlags::stopForReturn ) {
context.stopFlags &= ~EvalFlags::stopForReturn;
return context.abiResult();
} else {
if ( needResult ) context.throw_error_at(debugInfo,"end of block without return");
return v_zero();
}
}
#if DAS_DEBUGGER
vec4f SimNodeDebug_ClosureBlock::eval ( Context & context ) {
DAS_PROFILE_NODE
SimNode ** __restrict tail = list + total;
for (SimNode ** __restrict body = list; body!=tail; ++body) {
DAS_SINGLE_STEP(context,(*body)->debugInfo,false);
(*body)->eval(context);
if ( context.stopFlags ) break;
}
evalFinalSingleStep(context);
if ( context.stopFlags & EvalFlags::stopForReturn ) {
context.stopFlags &= ~EvalFlags::stopForReturn;
return context.abiResult();
} else {
if ( needResult ) context.throw_error_at(debugInfo,"end of block without return");
return v_zero();
}
}
#endif
// SimNode_BlockWithLabels
vec4f SimNode_BlockWithLabels::eval ( Context & context ) {
DAS_PROFILE_NODE
SimNode ** __restrict tail = list + total;
SimNode ** __restrict body = list;
loopbegin:;
for (; body!=tail; ++body) {
(*body)->eval(context);
{ if ( context.stopFlags ) {
if (context.stopFlags&EvalFlags::jumpToLabel) {
if ( context.gotoLabel>=totalLabels ) {
context.throw_error_at(debugInfo, "invalid label index %u", context.gotoLabel);
}
body=list+labels[context.gotoLabel];
if ( body>=list && bodydebugInfo,false);
(*body)->eval(context);
{ if ( context.stopFlags ) {
if (context.stopFlags&EvalFlags::jumpToLabel) {
if ( context.gotoLabel>=totalLabels ) {
context.throw_error_at(debugInfo, "invalid label index %u", context.gotoLabel);
}
body=list+labels[context.gotoLabel];
if ( body>=list && bodyeval(context);
}
return v_zero();
}
// SimNode_While
vec4f SimNode_While::eval ( Context & context ) {
DAS_PROFILE_NODE
SimNode ** __restrict tail = list + total;
while ( cond->evalBool(context) && !context.stopFlags ) {
SimNode ** __restrict body = list;
loopbegin:;
for (; body!=tail; ++body) {
(*body)->eval(context);
DAS_PROCESS_LOOP_FLAGS(break);
}
}
loopend:;
evalFinal(context);
context.stopFlags &= ~EvalFlags::stopForBreak;
return v_zero();
}
#if DAS_DEBUGGER
vec4f SimNodeDebug_While::eval ( Context & context ) {
DAS_PROFILE_NODE
SimNode ** __restrict tail = list + total;
while ( cond->evalBool(context) && !context.stopFlags ) {
SimNode ** __restrict body = list;
loopbegin:;
for (; body!=tail; ++body) {
DAS_SINGLE_STEP(context,(*body)->debugInfo,true);
(*body)->eval(context);
DAS_PROCESS_LOOP_FLAGS(break);
}
}
loopend:;
evalFinalSingleStep(context);
context.stopFlags &= ~EvalFlags::stopForBreak;
return v_zero();
}
#endif
#if DAS_ENABLE_KEEPALIVE
vec4f SimNodeKeepAlive_While::eval ( Context & context ) {
DAS_PROFILE_NODE
SimNode ** __restrict tail = list + total;
while ( cond->evalBool(context) && !context.stopFlags ) {
SimNode ** __restrict body = list;
loopbegin:;
DAS_KEEPALIVE_LOOP(&context);
for (; body!=tail; ++body) {
(*body)->eval(context);
DAS_PROCESS_LOOP_FLAGS(break);
}
}
loopend:;
evalFinal(context);
context.stopFlags &= ~EvalFlags::stopForBreak;
return v_zero();
}
#endif
// Return
vec4f SimNode_Return::eval ( Context & context ) {
DAS_PROFILE_NODE
if ( subexpr ) context.abiResult() = subexpr->eval(context);
context.stopFlags |= EvalFlags::stopForReturn;
return v_zero();
}
vec4f SimNode_ReturnNothing::eval ( Context & context ) {
DAS_PROFILE_NODE
context.stopFlags |= EvalFlags::stopForReturn;
return v_zero();
}
vec4f SimNode_ReturnConst::eval ( Context & context ) {
DAS_PROFILE_NODE
context.abiResult() = value;
context.stopFlags |= EvalFlags::stopForReturn;
return v_zero();
}
vec4f SimNode_ReturnConstString::eval ( Context & context ) {
DAS_PROFILE_NODE
context.abiResult() = cast::from(value);
context.stopFlags |= EvalFlags::stopForReturn;
return v_zero();
}
vec4f SimNode_ReturnRefAndEval::eval ( Context & context ) {
DAS_PROFILE_NODE
auto pl = context.abiCopyOrMoveResult();
DAS_ASSERT(pl);
auto pR = ((char **)(context.stack.sp() + stackTop));
*pR = pl;
subexpr->evalPtr(context);;
context.abiResult() = cast::from(pl);
context.stopFlags |= EvalFlags::stopForReturn;
return v_zero();
}
vec4f SimNode_ReturnAndCopy::eval ( Context & context ) {
DAS_PROFILE_NODE
auto pr = subexpr->evalPtr(context);
auto pl = context.abiCopyOrMoveResult();
DAS_ASSERT(pl);
memcpy ( pl, pr, size);
context.abiResult() = cast::from(pl);
context.stopFlags |= EvalFlags::stopForReturn;
return v_zero();
}
vec4f SimNode_ReturnAndMove::eval ( Context & context ) {
DAS_PROFILE_NODE
auto pr = subexpr->evalPtr(context);
auto pl = context.abiCopyOrMoveResult();
DAS_ASSERT(pl);
if ( pl != pr ) {
memcpy ( pl, pr, size);
memset ( pr, 0, size);
}
context.abiResult() = cast::from(pl);
context.stopFlags |= EvalFlags::stopForReturn;
return v_zero();
}
vec4f SimNode_ReturnReference::eval ( Context & context ) {
DAS_PROFILE_NODE
char * ref = subexpr->evalPtr(context);
if ( context.stack.bottom()stackSize;
if ( context.stack.sp()evalPtr(context);;
context.abiResult() = cast::from(pl);
context.stopFlags |= EvalFlags::stopForReturn;
return v_zero();
}
vec4f SimNode_ReturnAndCopyFromBlock::eval ( Context & context ) {
DAS_PROFILE_NODE
auto pr = subexpr->evalPtr(context);
auto ba = (BlockArguments *) ( context.stack.sp() + argStackTop );
auto pl = ba->copyOrMoveResult;
memcpy ( pl, pr, size);
context.abiResult() = cast::from(pl);
context.stopFlags |= EvalFlags::stopForReturn;
return v_zero();
}
vec4f SimNode_ReturnAndMoveFromBlock::eval ( Context & context ) {
DAS_PROFILE_NODE
auto pr = subexpr->evalPtr(context);
auto ba = (BlockArguments *) ( context.stack.sp() + argStackTop );
auto pl = ba->copyOrMoveResult;
if ( pl != pr ) {
memcpy ( pl, pr, size);
memset ( pr, 0, size);
}
context.abiResult() = cast::from(pl);
context.stopFlags |= EvalFlags::stopForReturn;
return v_zero();
}
vec4f SimNode_ReturnReferenceFromBlock::eval ( Context & context ) {
DAS_PROFILE_NODE
char * ref = subexpr->evalPtr(context);
if ( context.stack.bottom() 0 && *daScriptEnvironment::g_threadLocalDebugAgent && (*daScriptEnvironment::g_threadLocalDebugAgent)->debugAgent ) {
lmbd ( (*daScriptEnvironment::g_threadLocalDebugAgent)->debugAgent );
}
std::lock_guard guard(g_DebugAgentMutex);
for ( auto & it : g_DebugAgents ) {
if ( !it.second.debugAgent ) continue;
lmbd ( it.second.debugAgent );
}
}
void dapiReportContextState ( Context & ctx, const char * category, const char * name, const TypeInfo * info, void * data ) {
for_each_debug_agent([&](const DebugAgentPtr & pAgent){
pAgent->onVariable(&ctx,category,name,(TypeInfo*)info,data);
});
}
void dapiSimulateContext ( Context & ctx ) {
for_each_debug_agent([&]( const DebugAgentPtr & pAgent ){
pAgent->onSimulateContext(&ctx);
});
}
void dapiUserCommand ( const char * command ) {
if ( !command ) command = "";
bool any = false;
for_each_debug_agent([&]( const DebugAgentPtr & pAgent ){
if ( !any ) any = pAgent->onUserCommand(command);
});
}
void dapiOnBeforeGC ( Context & ctx ) {
for_each_debug_agent([&]( const DebugAgentPtr & pAgent ){
pAgent->onBeforeGC(&ctx);
});
}
void dapiOnAfterGC ( Context & ctx ) {
for_each_debug_agent([&]( const DebugAgentPtr & pAgent ){
pAgent->onAfterGC(&ctx);
});
}
Context::Context(uint32_t stackSize, bool ph) : stack(stackSize) {
code = make_shared();
constStringHeap = make_shared();
debugInfo = make_shared();
ownStack = (stackSize != 0);
persistent = ph;
}
void Context::setup(int totalVars, uint32_t globalStringHeapSize, CodeOfPolicies policies, AnnotationArgumentList options) {
verySafeContext = options.getBoolOption("very_safe_context",policies.very_safe_context);
breakOnException |= policies.debugger;
gcEnabled = options.getBoolOption("gc", false);
persistent = options.getBoolOption("persistent_heap", policies.persistent_heap);
if ( persistent ) {
heap = make_smart();
stringHeap = make_smart();
} else {
heap = make_smart();
stringHeap = make_smart();
}
heap->setInitialSize ( options.getIntOption("heap_size_hint", policies.heap_size_hint) );
heap->setLimit ( options.getUInt64OptionEx("heap_size_limit", "max_heap_allocated", policies.max_heap_allocated) );
stringHeap->setInitialSize ( options.getIntOption("string_heap_size_hint", policies.string_heap_size_hint) );
stringHeap->setLimit ( options.getUInt64OptionEx("string_heap_size_limit", "max_string_heap_allocated", policies.max_string_heap_allocated) );
constStringHeap = make_shared();
totalVariables = totalVars;
if ( globalStringHeapSize ) {
constStringHeap->setInitialSize(globalStringHeapSize);
}
globalVariables = (GlobalVariable *) code->allocate( uint32_t(totalVars*sizeof(GlobalVariable)) );
globalsSize = 0;
sharedSize = 0;
}
void Context::strip() {
stringHeap.reset();
heap.reset();
stack.strip();
if ( globals && globalsOwner ) {
das_aligned_free16(globals);
globals = nullptr;
}
if ( shared && sharedOwner ) {
das_aligned_free16(shared);
shared = nullptr;
}
}
void Context::logMemInfo(TextWriter & tw) {
uint64_t bytesTotal = 0, bytesUsed = 0;
// context
tw name, fnname)==0 ) {
found = fn;
candidates++;
}
}
isUnique = candidates == 1;
return found;
}
int Context::findVariable ( const char * fnname ) const {
for ( int vni=0, vnis=totalVariables; vni!=vnis; ++vni ) {
if ( strcmp(globalVariables[vni].name, fnname)==0 ) {
return vni;
}
}
return -1;
}
void Context::stackWalk( const LineInfo * at, bool showArguments, bool showLocalVariables ) {
auto str = getStackWalk(at, showArguments, showLocalVariables);
to_out(at, str.c_str());
}
class StackWalkerTextWriter : public StackWalker {
public:
StackWalkerTextWriter ( TextWriter & tw, Context * ctx ) : ssw(tw), context(ctx) {}
virtual bool canWalkArguments () override {
return showArguments;
}
virtual bool canWalkVariables () override {
return showLocalVariables;
}
virtual bool canWalkOutOfScopeVariables() override {
return showOutOfScope;
}
virtual void onCallAOT ( Prologue *, const char * fileName ) override {
ssw callOrFastcall(fun, args, lineinfo);
}
bool isInDebugAgentCreation() {
return *g_isInDebugAgentCreation;
}
void shutdownDebugAgent() {
bool hasThreadLocal = *daScriptEnvironment::g_threadLocalDebugAgent && (*daScriptEnvironment::g_threadLocalDebugAgent)->debugAgent;
DebugAgent * threadLocalDebugAgent = hasThreadLocal ? (*daScriptEnvironment::g_threadLocalDebugAgent)->debugAgent.get() : nullptr;
for_each_debug_agent([&](const DebugAgentPtr & pAgent){
if ( hasThreadLocal ) {
threadLocalDebugAgent->onUninstall(pAgent.get());
}
for ( auto & ap : g_DebugAgents ) {
if ( ap.second.debugAgent ) {
ap.second.debugAgent->onUninstall(pAgent.get());
}
}
});
das_safe_map agents;
{
std::lock_guard guard(g_DebugAgentMutex);
swap(agents, g_DebugAgents);
delete (*daScriptEnvironment::g_threadLocalDebugAgent);
(*daScriptEnvironment::g_threadLocalDebugAgent) = {};
}
// release agents before contexts to avoid use-after-free
// (agent objects live on the context heap)
for ( auto & ap : agents ) {
ap.second.debugAgent.reset();
}
}
void shutdownThreadLocalDebugAgent() {
bool hasThreadLocal = *daScriptEnvironment::g_threadLocalDebugAgent && (*daScriptEnvironment::g_threadLocalDebugAgent)->debugAgent;
if ( hasThreadLocal ) {
DebugAgent * threadLocalDebugAgent = (*daScriptEnvironment::g_threadLocalDebugAgent)->debugAgent.get();
for_each_debug_agent([&](const DebugAgentPtr & pAgent){
pAgent->onUninstall(threadLocalDebugAgent);
});
{
std::lock_guard guard(g_DebugAgentMutex);
delete (*daScriptEnvironment::g_threadLocalDebugAgent);
(*daScriptEnvironment::g_threadLocalDebugAgent) = {};
}
}
}
void Context::triggerHwBreakpoint ( void * addr, int index ) {
singleStepMode = true;
hwBpAddress = addr;
hwBpIndex = index;
}
void Context::breakPoint(const LineInfo & at, const char * reason, const char * text) {
if ( debugger ) {
bool any = false;
for_each_debug_agent([&](const DebugAgentPtr & pAgent){
pAgent->onBreakpoint(this, at, reason, text);
any = true;
});
if ( any ) return;
}
os_debug_break();
}
static DAS_THREAD_LOCAL(bool) g_inLogger;
void Context::to_out ( const LineInfo * at, int level, const char * message ) {
if (message) {
if ( !*g_inLogger ) {
*g_inLogger = true;
bool any = false;
for_each_debug_agent([&](const DebugAgentPtr & pAgent){
any |= pAgent->onLog(this, at, level, message);
});
*g_inLogger = false;
if ( any ) return;
}
const char * prefix = getLogMarker(level);
das_to_stdout_level_prefix_text(level, prefix, message);
}
}
void Context::throw_error_at ( const LineInfo * at, DAS_FORMAT_STRING_PREFIX const char * message, ... ) {
const int PRINT_BUFFER_SIZE = 8192;
char buffer[PRINT_BUFFER_SIZE];
va_list args;
va_start (args, message);
vsnprintf (buffer,PRINT_BUFFER_SIZE,message, args);
va_end (args);
throw_fatal_error(buffer, at ? *at : LineInfo());
}
void Context::throw_error_at ( const LineInfo & at, DAS_FORMAT_STRING_PREFIX const char * message, ... ) {
const int PRINT_BUFFER_SIZE = 8192;
char buffer[PRINT_BUFFER_SIZE];
va_list args;
va_start (args, message);
vsnprintf (buffer,PRINT_BUFFER_SIZE,message, args);
va_end (args);
throw_fatal_error(buffer, at);
}
void Context::throw_out_of_memory ( bool isStringHeap, uint32_t size, const LineInfo * at ) {
if ( isStringHeap ) {
throw_error_at(at, "out of string heap memory, requested %u bytes, used %llu / limit %llu", size, (unsigned long long) stringHeap->bytesAllocated(), (unsigned long long) stringHeap->getLimit());
} else {
throw_error_at(at, "out of heap memory, requested %u bytes, used %llu / limit %llu", size, (unsigned long long) heap->bytesAllocated(), (unsigned long long) heap->getLimit());
}
}
void Context::throw_error_ex ( DAS_FORMAT_STRING_PREFIX const char * message, ... ) {
const int PRINT_BUFFER_SIZE = 8192;
char buffer[PRINT_BUFFER_SIZE];
va_list args;
va_start (args, message);
vsnprintf (buffer,PRINT_BUFFER_SIZE,message, args);
va_end (args);
throw_fatal_error(buffer, LineInfo());
}
void Context::throw_error ( const char * message ) {
throw_fatal_error(message, LineInfo());
}
struct FileInfoCollector : SimVisitor {
virtual void preVisit ( SimNode * node ) override {
SimVisitor::preVisit(node);
if ( auto fi = node->debugInfo.fileInfo ) {
allFiles.insert(fi);
}
}
das_hash_set allFiles;
};
vector Context::getAllFiles() const {
vector allFiles;
FileInfoCollector collector;
runVisitor(&collector);
for ( auto & it : collector.allFiles ) {
allFiles.push_back(it);
}
sort ( allFiles.begin(), allFiles.end(), [&]( FileInfo * a, FileInfo * b ){
return a->name > b->name;
});
return allFiles;
}
void Context::resetProfiler() {
#if DAS_ENABLE_PROFILER
auto allFiles = getAllFiles();
for ( auto fi : allFiles ) {
fi->profileData.clear();
}
#endif
}
void Context::collectProfileInfo( TextWriter & tout ) {
#if DAS_ENABLE_PROFILER
uint64_t totalGoo = 0;
auto allFiles = getAllFiles();
for ( auto info : allFiles ) {
for ( auto counter : info->profileData ) {
totalGoo += counter;
}
}
tout profileData.size()>size_t(line) && fi->profileData[line] ) {
uint64_t samples = fi->profileData[line];
auto result = fmt::format_to(total, FMT_STRING("{:6.2f}"), samples*100.0/totalGoo); *result = 0;
tout visit(*vis);
}
for ( int fni=0, fnis=totalFunctions; fni!=fnis; ++fni ) {
const auto & fn = functions[fni];
if ( fn.code ) fn.code->visit(*vis);
}
}
void Context::onAllocateString ( void * ptr, uint64_t size, bool tempString, const LineInfo & at ) {
if ( g_envTotal > 0 && *daScriptEnvironment::g_threadLocalDebugAgent && (*daScriptEnvironment::g_threadLocalDebugAgent)->debugAgent ) {
(*daScriptEnvironment::g_threadLocalDebugAgent)->debugAgent->onAllocateString(this, ptr, size, tempString, at);
}
}
void Context::onFreeString ( void * ptr, bool tempString, const LineInfo & at ) {
if ( g_envTotal > 0 && *daScriptEnvironment::g_threadLocalDebugAgent && (*daScriptEnvironment::g_threadLocalDebugAgent)->debugAgent ) {
(*daScriptEnvironment::g_threadLocalDebugAgent)->debugAgent->onFreeString(this, ptr, tempString, at);
}
}
void Context::onAllocate ( void * ptr, uint64_t size, const LineInfo & at ) {
if ( g_envTotal > 0 && *daScriptEnvironment::g_threadLocalDebugAgent && (*daScriptEnvironment::g_threadLocalDebugAgent)->debugAgent ) {
(*daScriptEnvironment::g_threadLocalDebugAgent)->debugAgent->onAllocate(this, ptr, size, at);
}
}
void Context::onReallocate ( void * ptr, uint64_t size, void * newPtr, uint64_t newSize, const LineInfo & at ) {
if ( g_envTotal > 0 && *daScriptEnvironment::g_threadLocalDebugAgent && (*daScriptEnvironment::g_threadLocalDebugAgent)->debugAgent ) {
(*daScriptEnvironment::g_threadLocalDebugAgent)->debugAgent->onReallocate(this, ptr, size, newPtr, newSize, at);
}
}
void Context::onFree ( void * ptr, const LineInfo & at ) {
if ( g_envTotal > 0 && *daScriptEnvironment::g_threadLocalDebugAgent && (*daScriptEnvironment::g_threadLocalDebugAgent)->debugAgent ) {
(*daScriptEnvironment::g_threadLocalDebugAgent)->debugAgent->onFree(this, ptr, at);
}
}
const LineInfo * SimFunction::getLineInfo() const { return &code->debugInfo; }
}
//workaround compiler bug in MSVC 32 bit
#if defined(_MSC_VER) && !defined(__clang__) && INTPTR_MAX == INT32_MAX
VECTORCALL vec4i v_ldu_ptr(const void * a) {return v_seti_x((int32_t)a);}
#endif