2016-10-08 06:35:16 +00:00
/*
* * vmframe . cpp
* *
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* * Copyright - 2016 Randy Heit
2016-12-03 11:23:13 +00:00
* * Copyright 2016 Christoph Oelckers
2016-10-08 06:35:16 +00:00
* * All rights reserved .
* *
* * Redistribution and use in source and binary forms , with or without
* * modification , are permitted provided that the following conditions
* * are met :
* *
* * 1. Redistributions of source code must retain the above copyright
* * notice , this list of conditions and the following disclaimer .
* * 2. Redistributions in binary form must reproduce the above copyright
* * notice , this list of conditions and the following disclaimer in the
* * documentation and / or other materials provided with the distribution .
* * 3. The name of the author may not be used to endorse or promote products
* * derived from this software without specific prior written permission .
* *
* * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ` ` AS IS ' ' AND ANY EXPRESS OR
* * IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
* * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED .
* * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* * INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* * NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* * DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* * THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* * THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* *
*/
2016-03-01 15:47:10 +00:00
# include <new>
2016-11-17 12:10:19 +00:00
# include "dobject.h"
2016-12-03 11:23:13 +00:00
# include "v_text.h"
2016-12-31 16:59:48 +00:00
# include "stats.h"
2017-04-12 23:12:04 +00:00
# include "c_dispatch.h"
2017-01-17 00:50:31 +00:00
# include "templates.h"
2017-04-12 23:12:04 +00:00
# include "vmintern.h"
# include "types.h"
2018-08-18 15:50:47 +00:00
# include "jit.h"
2018-11-01 20:39:30 +00:00
# include "c_cvars.h"
# include "version.h"
2018-11-15 21:47:44 +00:00
# if (defined(_M_X64 ) || defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || defined(__amd64 ) || defined(__amd64__ ))
# define ARCH_X64
# endif
# ifdef ARCH_X64
2018-11-01 20:39:30 +00:00
CUSTOM_CVAR ( Bool , vm_jit , true , CVAR_NOINITCALL )
{
Printf ( " You must restart " GAMENAME " for this change to take effect. \n " ) ;
Printf ( " This cvar is currently not saved. You must specify it on the command line. " ) ;
}
2018-11-15 21:47:44 +00:00
# endif
2016-12-31 16:59:48 +00:00
cycle_t VMCycles [ 10 ] ;
int VMCalls [ 10 ] ;
2016-03-01 15:47:10 +00:00
2017-04-12 23:12:04 +00:00
#if 0
2016-11-24 20:36:02 +00:00
IMPLEMENT_CLASS ( VMException , false , false )
2017-04-12 23:12:04 +00:00
# endif
2016-11-05 16:08:54 +00:00
2017-02-08 10:13:41 +00:00
TArray < VMFunction * > VMFunction : : AllFunctions ;
2016-11-05 16:08:54 +00:00
2016-03-01 15:47:10 +00:00
VMScriptFunction : : VMScriptFunction ( FName name )
{
Name = name ;
2016-12-03 11:23:13 +00:00
LineInfo = nullptr ;
2016-03-01 15:47:10 +00:00
Code = NULL ;
KonstD = NULL ;
KonstF = NULL ;
KonstS = NULL ;
KonstA = NULL ;
2016-12-03 11:23:13 +00:00
LineInfoCount = 0 ;
2016-03-01 15:47:10 +00:00
ExtraSpace = 0 ;
CodeSize = 0 ;
NumRegD = 0 ;
NumRegF = 0 ;
NumRegS = 0 ;
NumRegA = 0 ;
NumKonstD = 0 ;
NumKonstF = 0 ;
NumKonstS = 0 ;
NumKonstA = 0 ;
MaxParam = 0 ;
NumArgs = 0 ;
2018-10-10 21:47:56 +00:00
ScriptCall = & VMScriptFunction : : FirstScriptCall ;
2016-03-01 15:47:10 +00:00
}
VMScriptFunction : : ~ VMScriptFunction ( )
{
if ( Code ! = NULL )
{
if ( KonstS ! = NULL )
{
for ( int i = 0 ; i < NumKonstS ; + + i )
{
KonstS [ i ] . ~ FString ( ) ;
}
}
}
}
2016-12-03 11:23:13 +00:00
void VMScriptFunction : : Alloc ( int numops , int numkonstd , int numkonstf , int numkonsts , int numkonsta , int numlinenumbers )
2016-03-01 15:47:10 +00:00
{
assert ( Code = = NULL ) ;
assert ( numops > 0 ) ;
2016-11-20 22:00:05 +00:00
assert ( numkonstd > = 0 & & numkonstd < = 65535 ) ;
assert ( numkonstf > = 0 & & numkonstf < = 65535 ) ;
assert ( numkonsts > = 0 & & numkonsts < = 65535 ) ;
assert ( numkonsta > = 0 & & numkonsta < = 65535 ) ;
2016-12-03 11:23:13 +00:00
assert ( numlinenumbers > = 0 & & numlinenumbers < = 65535 ) ;
2017-02-08 19:37:22 +00:00
void * mem = ClassDataAllocator . Alloc ( numops * sizeof ( VMOP ) +
2016-03-01 15:47:10 +00:00
numkonstd * sizeof ( int ) +
numkonstf * sizeof ( double ) +
numkonsts * sizeof ( FString ) +
2017-04-10 15:08:52 +00:00
numkonsta * sizeof ( FVoidObj ) +
2016-12-03 11:23:13 +00:00
numlinenumbers * sizeof ( FStatementInfo ) ) ;
2016-03-01 15:47:10 +00:00
Code = ( VMOP * ) mem ;
mem = ( void * ) ( ( VMOP * ) mem + numops ) ;
2016-12-03 11:23:13 +00:00
if ( numlinenumbers > 0 )
{
LineInfo = ( FStatementInfo * ) mem ;
LineInfoCount = numlinenumbers ;
mem = LineInfo + numlinenumbers ;
}
else
{
LineInfo = nullptr ;
LineInfoCount = 0 ;
}
2016-03-01 15:47:10 +00:00
if ( numkonstd > 0 )
{
KonstD = ( int * ) mem ;
mem = ( void * ) ( ( int * ) mem + numkonstd ) ;
}
else
{
KonstD = NULL ;
}
if ( numkonstf > 0 )
{
KonstF = ( double * ) mem ;
mem = ( void * ) ( ( double * ) mem + numkonstf ) ;
}
else
{
KonstF = NULL ;
}
if ( numkonsts > 0 )
{
KonstS = ( FString * ) mem ;
for ( int i = 0 ; i < numkonsts ; + + i )
{
: : new ( & KonstS [ i ] ) FString ;
}
mem = ( void * ) ( ( FString * ) mem + numkonsts ) ;
}
else
{
KonstS = NULL ;
}
if ( numkonsta > 0 )
{
KonstA = ( FVoidObj * ) mem ;
}
else
{
KonstA = NULL ;
}
CodeSize = numops ;
NumKonstD = numkonstd ;
NumKonstF = numkonstf ;
NumKonstS = numkonsts ;
NumKonstA = numkonsta ;
}
2016-11-17 12:10:19 +00:00
void VMScriptFunction : : InitExtra ( void * addr )
{
char * caddr = ( char * ) addr ;
for ( auto tao : SpecialInits )
{
tao . first - > InitializeValue ( caddr + tao . second , nullptr ) ;
}
}
void VMScriptFunction : : DestroyExtra ( void * addr )
{
char * caddr = ( char * ) addr ;
for ( auto tao : SpecialInits )
{
tao . first - > DestroyValue ( caddr + tao . second ) ;
}
}
int VMScriptFunction : : AllocExtraStack ( PType * type )
{
int address = ( ( ExtraSpace + type - > Align - 1 ) / type - > Align ) * type - > Align ;
ExtraSpace = address + type - > Size ;
2016-11-17 15:44:41 +00:00
type - > SetDefaultValue ( nullptr , address , & SpecialInits ) ;
2016-11-17 12:10:19 +00:00
return address ;
}
2016-12-03 11:23:13 +00:00
int VMScriptFunction : : PCToLine ( const VMOP * pc )
{
int PCIndex = int ( pc - Code ) ;
if ( LineInfoCount = = 1 ) return LineInfo [ 0 ] . LineNumber ;
for ( unsigned i = 1 ; i < LineInfoCount ; i + + )
{
if ( LineInfo [ i ] . InstructionIndex > PCIndex )
{
return LineInfo [ i - 1 ] . LineNumber ;
}
}
return - 1 ;
}
2018-11-16 23:04:15 +00:00
static bool CanJit ( VMScriptFunction * func )
{
// Asmjit has a 256 register limit. Stay safely away from it as the jit compiler uses a few for temporaries as well.
// Any function exceeding the limit will use the VM - a fair punishment to someone for writing a function so bloated ;)
2018-11-16 23:36:40 +00:00
int maxregs = 200 ;
if ( func - > NumRegA + func - > NumRegD + func - > NumRegF + func - > NumRegS < maxregs )
return true ;
Printf ( TEXTCOLOR_ORANGE " %s is using too many registers (%d of max %d)! Function will not use native code. \n " , func - > PrintableName . GetChars ( ) , func - > NumRegA + func - > NumRegD + func - > NumRegF + func - > NumRegS , maxregs ) ;
return false ;
2018-11-16 23:04:15 +00:00
}
2018-10-10 21:47:56 +00:00
int VMScriptFunction : : FirstScriptCall ( VMFunction * func , VMValue * params , int numparams , VMReturn * ret , int numret )
2018-10-09 00:52:07 +00:00
{
2018-11-15 21:47:44 +00:00
# ifdef ARCH_X64
2018-11-16 23:04:15 +00:00
if ( vm_jit & & CanJit ( static_cast < VMScriptFunction * > ( func ) ) )
2018-11-01 20:39:30 +00:00
{
func - > ScriptCall = JitCompile ( static_cast < VMScriptFunction * > ( func ) ) ;
if ( ! func - > ScriptCall )
func - > ScriptCall = VMExec ;
}
else
{
func - > ScriptCall = VMExec ;
}
2018-11-15 21:47:44 +00:00
# else
func - > ScriptCall = VMExec ;
# endif
2018-10-09 00:52:07 +00:00
return func - > ScriptCall ( func , params , numparams , ret , numret ) ;
}
2018-10-10 21:47:56 +00:00
int VMNativeFunction : : NativeScriptCall ( VMFunction * func , VMValue * params , int numparams , VMReturn * returns , int numret )
{
try
{
VMCycles [ 0 ] . Unclock ( ) ;
numret = static_cast < VMNativeFunction * > ( func ) - > NativeCall ( params , func - > DefaultArgs , numparams , returns , numret ) ;
VMCycles [ 0 ] . Clock ( ) ;
return numret ;
}
catch ( CVMAbortException & err )
{
err . MaybePrintMessage ( ) ;
err . stacktrace . AppendFormat ( " Called from %s \n " , func - > PrintableName . GetChars ( ) ) ;
VMThrowException ( std : : current_exception ( ) ) ;
return 0 ;
}
catch ( . . . )
{
VMThrowException ( std : : current_exception ( ) ) ;
return 0 ;
}
}
2016-03-01 15:47:10 +00:00
//===========================================================================
//
// VMFrame :: InitRegS
//
// Initialize the string registers of a newly-allocated VMFrame.
//
//===========================================================================
void VMFrame : : InitRegS ( )
{
FString * regs = GetRegS ( ) ;
for ( int i = 0 ; i < NumRegS ; + + i )
{
: : new ( & regs [ i ] ) FString ;
}
}
//===========================================================================
//
// VMFrameStack - Constructor
//
//===========================================================================
VMFrameStack : : VMFrameStack ( )
{
Blocks = NULL ;
UnusedBlocks = NULL ;
}
//===========================================================================
//
// VMFrameStack - Destructor
//
//===========================================================================
VMFrameStack : : ~ VMFrameStack ( )
{
while ( PopFrame ( ) ! = NULL )
{ }
if ( Blocks ! = NULL )
{
BlockHeader * block , * next ;
for ( block = Blocks ; block ! = NULL ; block = next )
{
next = block - > NextBlock ;
delete [ ] ( VM_UBYTE * ) block ;
}
2017-01-13 10:39:52 +00:00
Blocks = NULL ;
2016-03-01 15:47:10 +00:00
}
if ( UnusedBlocks ! = NULL )
{
BlockHeader * block , * next ;
for ( block = UnusedBlocks ; block ! = NULL ; block = next )
{
next = block - > NextBlock ;
delete [ ] ( VM_UBYTE * ) block ;
}
2017-01-13 10:39:52 +00:00
UnusedBlocks = NULL ;
2016-03-01 15:47:10 +00:00
}
}
//===========================================================================
//
// VMFrameStack :: AllocFrame
//
// Allocates a frame from the stack suitable for calling a particular
// function.
//
//===========================================================================
VMFrame * VMFrameStack : : AllocFrame ( VMScriptFunction * func )
{
2017-04-01 08:42:47 +00:00
VMFrame * frame = Alloc ( func - > StackSize ) ;
2017-04-01 10:04:31 +00:00
frame - > Func = func ;
2016-03-01 15:47:10 +00:00
frame - > NumRegD = func - > NumRegD ;
frame - > NumRegF = func - > NumRegF ;
frame - > NumRegS = func - > NumRegS ;
frame - > NumRegA = func - > NumRegA ;
frame - > MaxParam = func - > MaxParam ;
frame - > Func = func ;
frame - > InitRegS ( ) ;
2016-11-17 12:10:19 +00:00
if ( func - > SpecialInits . Size ( ) )
{
func - > InitExtra ( frame - > GetExtra ( ) ) ;
}
2016-03-01 15:47:10 +00:00
return frame ;
}
//===========================================================================
//
// VMFrameStack :: Alloc
//
// Allocates space for a frame. Its size will be rounded up to a multiple
// of 16 bytes.
//
//===========================================================================
VMFrame * VMFrameStack : : Alloc ( int size )
{
BlockHeader * block ;
VMFrame * frame , * parent ;
size = ( size + 15 ) & ~ 15 ;
block = Blocks ;
if ( block ! = NULL )
{
parent = block - > LastFrame ;
}
else
{
parent = NULL ;
}
if ( block = = NULL | | ( ( VM_UBYTE * ) block + block - > BlockSize ) < ( block - > FreeSpace + size ) )
{ // Not enough space. Allocate a new block.
int blocksize = ( ( sizeof ( BlockHeader ) + 15 ) & ~ 15 ) + size ;
BlockHeader * * blockp ;
if ( blocksize < BLOCK_SIZE )
{
blocksize = BLOCK_SIZE ;
}
for ( blockp = & UnusedBlocks , block = * blockp ; block ! = NULL ; block = block - > NextBlock )
{
if ( block - > BlockSize > = blocksize )
{
break ;
}
}
if ( block ! = NULL )
{
* blockp = block - > NextBlock ;
}
else
{
block = ( BlockHeader * ) new VM_UBYTE [ blocksize ] ;
block - > BlockSize = blocksize ;
}
block - > InitFreeSpace ( ) ;
block - > LastFrame = NULL ;
block - > NextBlock = Blocks ;
Blocks = block ;
}
frame = ( VMFrame * ) block - > FreeSpace ;
memset ( frame , 0 , size ) ;
frame - > ParentFrame = parent ;
block - > FreeSpace + = size ;
block - > LastFrame = frame ;
return frame ;
}
//===========================================================================
//
// VMFrameStack :: PopFrame
//
// Pops the top frame off the stack, returning a pointer to the new top
// frame.
//
//===========================================================================
VMFrame * VMFrameStack : : PopFrame ( )
{
if ( Blocks = = NULL )
{
return NULL ;
}
VMFrame * frame = Blocks - > LastFrame ;
if ( frame = = NULL )
{
return NULL ;
}
2016-11-17 12:10:19 +00:00
auto Func = static_cast < VMScriptFunction * > ( frame - > Func ) ;
if ( Func - > SpecialInits . Size ( ) )
{
Func - > DestroyExtra ( frame - > GetExtra ( ) ) ;
}
2016-03-01 15:47:10 +00:00
// Free any string registers this frame had.
FString * regs = frame - > GetRegS ( ) ;
for ( int i = frame - > NumRegS ; i ! = 0 ; - - i )
{
( regs + + ) - > ~ FString ( ) ;
}
VMFrame * parent = frame - > ParentFrame ;
if ( parent = = NULL )
{
// Popping the last frame off the stack.
if ( Blocks ! = NULL )
{
assert ( Blocks - > NextBlock = = NULL ) ;
Blocks - > LastFrame = NULL ;
Blocks - > InitFreeSpace ( ) ;
}
return NULL ;
}
if ( ( VM_UBYTE * ) parent < ( VM_UBYTE * ) Blocks | | ( VM_UBYTE * ) parent > = ( VM_UBYTE * ) Blocks + Blocks - > BlockSize )
{ // Parent frame is in a different block, so move this one to the unused list.
BlockHeader * next = Blocks - > NextBlock ;
assert ( next ! = NULL ) ;
assert ( ( VM_UBYTE * ) parent > = ( VM_UBYTE * ) next & & ( VM_UBYTE * ) parent < ( VM_UBYTE * ) next + next - > BlockSize ) ;
Blocks - > NextBlock = UnusedBlocks ;
UnusedBlocks = Blocks ;
Blocks = next ;
}
else
{
Blocks - > LastFrame = parent ;
Blocks - > FreeSpace = ( VM_UBYTE * ) frame ;
}
return parent ;
}
2018-10-09 14:30:55 +00:00
//===========================================================================
//
// The jitted code does not implement C++ exception handling.
// Catch them, longjmp out of the jitted functions, perform vmframe cleanup
// then rethrow the C++ exception
//
//===========================================================================
thread_local JitExceptionInfo * CurrentJitExceptInfo ;
void VMThrowException ( std : : exception_ptr cppException )
{
CurrentJitExceptInfo - > cppException = cppException ;
longjmp ( CurrentJitExceptInfo - > sjljbuf , 1 ) ;
}
static void VMRethrowException ( JitExceptionInfo * exceptInfo )
{
int c = exceptInfo - > vmframes ;
VMFrameStack * stack = & GlobalVMStack ;
for ( int i = 0 ; i < c ; i + + )
stack - > PopFrame ( ) ;
std : : rethrow_exception ( exceptInfo - > cppException ) ;
}
2016-03-01 15:47:10 +00:00
//===========================================================================
//
// VMFrameStack :: Call
//
// Calls a function, either native or scripted. If an exception occurs while
// executing, the stack is cleaned up. If trap is non-NULL, it is set to the
// VMException that was caught and the return value is negative. Otherwise,
// any caught exceptions will be rethrown. Under normal termination, the
// return value is the number of results from the function.
//
//===========================================================================
2017-04-12 23:12:04 +00:00
int VMCall ( VMFunction * func , VMValue * params , int numparams , VMReturn * results , int numresults /*, VMException **trap*/ )
2016-03-01 15:47:10 +00:00
{
2018-10-09 00:08:15 +00:00
#if 0
2016-03-01 15:47:10 +00:00
try
2018-10-09 00:08:15 +00:00
# endif
2016-11-27 14:50:58 +00:00
{
2017-03-04 23:24:22 +00:00
if ( func - > VarFlags & VARF_Native )
2016-03-01 15:47:10 +00:00
{
2016-11-30 16:15:01 +00:00
return static_cast < VMNativeFunction * > ( func ) - > NativeCall ( params , func - > DefaultArgs , numparams , results , numresults ) ;
2016-03-01 15:47:10 +00:00
}
else
{
2017-01-01 22:11:48 +00:00
auto code = static_cast < VMScriptFunction * > ( func ) - > Code ;
// handle empty functions consisting of a single return explicitly so that empty virtual callbacks do not need to set up an entire VM frame.
2017-02-25 09:57:12 +00:00
// code cann be null here in case of some non-fatal DECORATE errors.
2017-03-18 20:31:43 +00:00
if ( code = = nullptr | | code - > word = = ( 0x00808000 | OP_RET ) )
2017-01-01 22:11:48 +00:00
{
return 0 ;
}
2017-03-18 20:31:43 +00:00
else if ( code - > word = = ( 0x00048000 | OP_RET ) )
2017-01-01 22:11:48 +00:00
{
if ( numresults = = 0 ) return 0 ;
results [ 0 ] . SetInt ( static_cast < VMScriptFunction * > ( func ) - > KonstD [ 0 ] ) ;
return 1 ;
}
else
{
VMCycles [ 0 ] . Clock ( ) ;
2018-10-09 14:30:55 +00:00
JitExceptionInfo * prevExceptInfo = CurrentJitExceptInfo ;
JitExceptionInfo newExceptInfo ;
CurrentJitExceptInfo = & newExceptInfo ;
if ( setjmp ( CurrentJitExceptInfo - > sjljbuf ) = = 0 )
{
auto sfunc = static_cast < VMScriptFunction * > ( func ) ;
int numret = sfunc - > ScriptCall ( sfunc , params , numparams , results , numresults ) ;
CurrentJitExceptInfo = prevExceptInfo ;
VMCycles [ 0 ] . Unclock ( ) ;
return numret ;
}
else
{
VMCycles [ 0 ] . Unclock ( ) ;
auto exceptInfo = CurrentJitExceptInfo ;
CurrentJitExceptInfo = prevExceptInfo ;
VMRethrowException ( exceptInfo ) ;
2018-10-10 02:57:35 +00:00
return 0 ;
2018-10-09 14:30:55 +00:00
}
2017-01-01 22:11:48 +00:00
}
2016-03-01 15:47:10 +00:00
}
}
2017-04-12 23:12:04 +00:00
#if 0
2016-03-01 15:47:10 +00:00
catch ( VMException * exception )
{
if ( trap ! = NULL )
{
* trap = exception ;
return - 1 ;
}
throw ;
}
2017-04-12 23:12:04 +00:00
# endif
2016-12-03 11:23:13 +00:00
}
2018-11-16 18:18:33 +00:00
int VMCallWithDefaults ( VMFunction * func , TArray < VMValue > & params , VMReturn * results , int numresults /*, VMException **trap = NULL*/ )
{
if ( func - > DefaultArgs . Size ( ) > params . Size ( ) )
{
auto oldp = params . Size ( ) ;
params . Resize ( func - > DefaultArgs . Size ( ) ) ;
memcpy ( & params [ oldp ] , & func - > DefaultArgs [ oldp ] , ( params . Size ( ) - oldp ) * sizeof ( VMValue ) ) ;
}
return VMCall ( func , params . Data ( ) , params . Size ( ) , results , numresults ) ;
}
2016-12-03 11:23:13 +00:00
// Exception stuff for the VM is intentionally placed there, because having this in vmexec.cpp would subject it to inlining
// which we do not want because it increases the local stack requirements of Exec which are already too high.
FString CVMAbortException : : stacktrace ;
CVMAbortException : : CVMAbortException ( EVMAbortException reason , const char * moreinfo , va_list ap )
{
SetMessage ( " VM execution aborted: " ) ;
switch ( reason )
{
case X_READ_NIL :
AppendMessage ( " tried to read from address zero. " ) ;
break ;
case X_WRITE_NIL :
AppendMessage ( " tried to write to address zero. " ) ;
break ;
case X_TOO_MANY_TRIES :
AppendMessage ( " too many try-catch blocks. " ) ;
break ;
case X_ARRAY_OUT_OF_BOUNDS :
AppendMessage ( " array access out of bounds. " ) ;
break ;
2016-09-14 10:30:11 +00:00
2016-12-03 11:23:13 +00:00
case X_DIVISION_BY_ZERO :
AppendMessage ( " division by zero. " ) ;
break ;
case X_BAD_SELF :
AppendMessage ( " invalid self pointer. " ) ;
break ;
2017-01-13 19:44:34 +00:00
case X_FORMAT_ERROR :
AppendMessage ( " string format failed. " ) ;
break ;
2017-02-04 21:09:49 +00:00
case X_OTHER :
// no prepended message.
break ;
2016-12-03 11:23:13 +00:00
default :
{
size_t len = strlen ( m_Message ) ;
mysnprintf ( m_Message + len , MAX_ERRORTEXT - len , " Unknown reason %d " , reason ) ;
break ;
2016-09-14 10:30:11 +00:00
}
2016-12-03 11:23:13 +00:00
}
if ( moreinfo ! = nullptr )
2016-03-01 15:47:10 +00:00
{
2016-12-03 11:23:13 +00:00
AppendMessage ( " " ) ;
size_t len = strlen ( m_Message ) ;
myvsnprintf ( m_Message + len , MAX_ERRORTEXT - len , moreinfo , ap ) ;
2016-03-01 15:47:10 +00:00
}
2016-12-03 11:23:13 +00:00
stacktrace = " " ;
}
// Print this only once on the first catch block.
void CVMAbortException : : MaybePrintMessage ( )
{
auto m = GetMessage ( ) ;
if ( m ! = nullptr )
{
Printf ( TEXTCOLOR_RED ) ;
Printf ( " %s \n " , m ) ;
SetMessage ( " " ) ;
}
}
void ThrowAbortException ( EVMAbortException reason , const char * moreinfo , . . . )
{
va_list ap ;
va_start ( ap , moreinfo ) ;
throw CVMAbortException ( reason , moreinfo , ap ) ;
va_end ( ap ) ;
}
2018-08-18 20:41:18 +00:00
void ThrowAbortException ( VMScriptFunction * sfunc , VMOP * line , EVMAbortException reason , const char * moreinfo , . . . )
{
va_list ap ;
va_start ( ap , moreinfo ) ;
CVMAbortException err ( reason , moreinfo , ap ) ;
err . stacktrace . AppendFormat ( " Called from %s at %s, line %d \n " , sfunc - > PrintableName . GetChars ( ) , sfunc - > SourceFileName . GetChars ( ) , sfunc - > PCToLine ( line ) ) ;
throw err ;
va_end ( ap ) ;
}
2017-03-22 23:25:26 +00:00
DEFINE_ACTION_FUNCTION ( DObject , ThrowAbortException )
{
PARAM_PROLOGUE ;
FString s = FStringFormat ( param , defaultparam , numparam , ret , numret ) ;
ThrowAbortException ( X_OTHER , s . GetChars ( ) ) ;
return 0 ;
}
2016-12-03 11:23:13 +00:00
void NullParam ( const char * varname )
{
ThrowAbortException ( X_READ_NIL , " In function parameter %s " , varname ) ;
}
2017-04-12 23:12:04 +00:00
#if 0
2016-12-03 11:23:13 +00:00
void ThrowVMException ( VMException * x )
{
throw x ;
2016-03-01 15:47:10 +00:00
}
2017-04-12 23:12:04 +00:00
# endif
2016-12-31 16:59:48 +00:00
ADD_STAT ( VM )
{
double added = 0 ;
int addedc = 0 ;
2017-01-17 00:50:31 +00:00
double peak = 0 ;
for ( auto d : VMCycles )
{
added + = d . TimeMS ( ) ;
peak = MAX < double > ( peak , d . TimeMS ( ) ) ;
}
2016-12-31 16:59:48 +00:00
for ( auto d : VMCalls ) addedc + = d ;
memmove ( & VMCycles [ 1 ] , & VMCycles [ 0 ] , 9 * sizeof ( cycle_t ) ) ;
memmove ( & VMCalls [ 1 ] , & VMCalls [ 0 ] , 9 * sizeof ( int ) ) ;
VMCycles [ 0 ] . Reset ( ) ;
VMCalls [ 0 ] = 0 ;
2017-01-17 00:50:31 +00:00
return FStringf ( " VM time in last 10 tics: %f ms, %d calls, peak = %f ms " , added , addedc , peak ) ;
2017-01-13 10:39:52 +00:00
}
2017-04-12 23:12:04 +00:00
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
CCMD ( vmengine )
{
if ( argv . argc ( ) = = 2 )
{
if ( stricmp ( argv [ 1 ] , " default " ) = = 0 )
{
VMSelectEngine ( VMEngine_Default ) ;
return ;
}
else if ( stricmp ( argv [ 1 ] , " checked " ) = = 0 )
{
VMSelectEngine ( VMEngine_Checked ) ;
return ;
}
else if ( stricmp ( argv [ 1 ] , " unchecked " ) = = 0 )
{
VMSelectEngine ( VMEngine_Unchecked ) ;
return ;
}
}
Printf ( " Usage: vmengine <default|checked|unchecked> \n " ) ;
}