code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen 1.9.6"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>AngelScript: Concurrent scripts</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>

<link href="navtree.css" rel="stylesheet" type="text/css"/>

<link href="search/search.css" rel="stylesheet" type="text/css"/>

<link href="doxygen.css" rel="stylesheet" type="text/css" />

AngelScript

          <input type="text" id="MSearchField" value="" placeholder="Search" accesskey="S"
               onfocus="searchBox.OnSearchFieldFocus(true)"
               onblur="searchBox.OnSearchFieldFocus(false)"
               onkeyup="searchBox.OnSearchFieldChange(event)"/>

Loading...

Searching...

No Matches

Concurrent scripts

The script engine can run multiple scripts in parallel, i.e. concurrent scripts. This can be done easily using multithreading where each thread runs its own script context, but this article is going to explain how it is done without multithreading.

In order to execute multiple scripts in parallel, each script must have it's own asIScriptContext, where the context is set up as for a normal function call. Then the application needs to set a timeout for each context. When the timeout is reached, the context should be suspended, so that the next context can execute for a while.

Here's a very simple example of how this can be done:

// The contexts have already been prepared before this function is called

void ExecuteScripts(std::vector<asIScriptContext *> contexts)

{

  // This function will run until all scripts have completed

  while( contexts.size() > 0 )

  {

    for( asUINT n = 0; n < contexts.size(); n++ )

    {

      // Set a 10 millisecond timeout for this context

      SetTimeoutForContext(contexts[n], 10);

      // Resume the execution of this context by calling Execute

      int r = contexts[n]->Execute();

      // Remove the timeout so it won't be triggered accidentally

      RemoveTimeoutForContext();

      // Determine if the script completed, or was timed out

      if( r == asEXECUTION_SUSPENDED )

      {

        // The script will continue in the next iteration

      }

      else

      {

        // The script has completed the execution, so we take it out of the list of scripts

        contexts[n--] = contexts.back();

        contexts.pop_back();

      }

    }

  }

}

asEXECUTION_SUSPENDED

@ asEXECUTION_SUSPENDED

The execution is suspended and can be resumed.

Definition: angelscript.h:404

asUINT

unsigned int asUINT

32 bit unsigned integer

Definition: angelscript.h:610

The application can manage the execution of the scripts in a simple round-robin fashion, where each script gets equal amount of execution time, or a more intricate management algorithm can be built, e.g. to give more execution time to high priority scripts etc.

The time out function, i.e. SetTimeoutForContext in the example above, can be implemented in two different ways. Through the use of line callbacks, where the context will invoke the callback for each statement in the script. The callback can then check if the timeout limit has been reached and suspend the context.

The other way is through the use of a timeout thread, where the thread simply sleeps until the timeout limit has been reached, and when the thread wakes up it suspends the context (if it is still running).

The timeout thread is probably the easiest to implement, and also doesn't impact the performance as much as the line callback. The line callback may still have to be used if the target OS doesn't support multithreading though.

Here's a simple example of how to implement the timeout function with a separate thread:

static HANDLE            thread_handle = 0;

static asIScriptContext *thread_ctx;

DWORD WINAPI TimeoutThread(void *sleeptime)

{

  Sleep(*reinterpret_cast<int*>(sleeptime));

  if( thread_ctx )

    thread_ctx->Suspend();

  return 0;

}

void SetTimeoutForContext(asIScriptContext *ctx, int milliseconds)

{

  thread_ctx    = ctx;

  thread_handle = CreateThread(0, 50, TimeoutThread, reinterpret_cast<void*>(&milliseconds), 0, 0);

}

void RemoveTimeoutForContext()

{

  // TerminateThread should be used with extreme care, but in this

  // case the TimeoutThread can't do any harm even if interrupted

  // in the middle of the execution

  TerminateThread(thread_handle, 0);

  thread_handle = 0;

}

asIScriptContext

The interface to the virtual machine.

Definition: angelscript.h:2735

asIScriptContext::Suspend

virtual int Suspend()=0

Suspends the execution, which can then be resumed by calling Execute again.

Observe that the routines for multithreading usually differ a lot depending on the target system. The above code is for Windows and will most likely require adaption to work on any other system.

See also

Context manager, Concurrent scripts, Co-routines

Generated on Sun May 21 2023 10:12:39 for AngelScript by  1.9.6


code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen 1.9.6"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>AngelScript: Co-routines</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>

<link href="navtree.css" rel="stylesheet" type="text/css"/>

<link href="search/search.css" rel="stylesheet" type="text/css"/>

<link href="doxygen.css" rel="stylesheet" type="text/css" />

AngelScript

          <input type="text" id="MSearchField" value="" placeholder="Search" accesskey="S"
               onfocus="searchBox.OnSearchFieldFocus(true)"
               onblur="searchBox.OnSearchFieldFocus(false)"
               onkeyup="searchBox.OnSearchFieldChange(event)"/>

Loading...

Searching...

No Matches

Co-routines

Co-routines is a way to allow multiple execution paths in parallel, but without the hazards of pre-emptive scheduling in multithreading where one thread can be suspended at any moment so another can resume. Because co-routines always voluntarily suspend themselves in favor of the next co-routine, there is no need to worry about atomic instructions and critical sections.

Co-routines are not natively built-into the AngelScript library, but it can easily be implemented from the application side. In fact, the Context manager add-on already provides a default implementation for this.

To implement your own version of co-routines you will need a couple of pieces:

The co-routine itself, which is just an instance of the asIScriptContext object. Each co-routine will have its own context object that holds the callstack of the co-routine.

A function that will permit the script to create, or spawn, new co-routines. This function will need to be able to refer to starting function of the new co-routine. This reference can be done by name, or perhaps more elegantly with a function pointer. Once invoked this function will instanciate the new context, and prepare it with the starting function.

A function that will permit a co-routine to yield control to the next co-routine. This function will simply suspend the current context so the next co-routine can be resumed.

A simple control algorithm for the co-routines. This can just be a loop that will iterate over an array of co-routines, i.e. contexts, until all of them have finished executing. When a new co-routine is created it is simply appended to the array to be picked up when the current co-routine yields the control.

A simple implementation of the function that spawns a new co-routine may look like this:

void CreateCoRoutine(string &func)

{

  asIScriptContext *ctx = asGetActiveContext();

  if( ctx )

  {

    asIScriptEngine *engine = ctx->GetEngine();

    string mod = ctx->GetFunction()->GetModuleName();

    // We need to find the function that will be created as the co-routine

    string decl = "void " + func + "()";

    asIScriptFunction *funcPtr = engine->GetModule(mod.c_str())->GetFunctionByDecl(decl.c_str());

    if( funcPtr == 0 )

    {

      // No function could be found, raise an exception

      ctx->SetException(("Function '" + decl + "' doesn't exist").c_str());

      return;

    }

    // Create a new context for the co-routine

    asIScriptContext *coctx = engine->CreateContext();

    coctx->Prepare(funcPtr);

    // Add the new co-routine context to the array of co-routines

    coroutines.push_back(coctx);

  }

}

asIScriptContext

The interface to the virtual machine.

Definition: angelscript.h:2735

asIScriptContext::GetFunction

virtual asIScriptFunction * GetFunction(asUINT stackLevel=0)=0

Returns the function at the specified callstack level.

asIScriptContext::Prepare

virtual int Prepare(asIScriptFunction *func)=0

Prepares the context for execution of the function.

asIScriptContext::GetEngine

virtual asIScriptEngine * GetEngine() const =0

Returns a pointer to the engine.

asIScriptContext::SetException

virtual int SetException(const char *info, bool allowCatch=true)=0

Sets an exception, which aborts the execution.

asIScriptEngine

The engine interface.

Definition: angelscript.h:1102

asIScriptEngine::GetModule

virtual asIScriptModule * GetModule(const char *module, asEGMFlags flag=asGM_ONLY_IF_EXISTS)=0

Return an interface pointer to the module.

asIScriptEngine::CreateContext

virtual asIScriptContext * CreateContext()=0

Creates a new script context.

asIScriptFunction

The interface for a script function description.

Definition: angelscript.h:4000

asIScriptFunction::GetModuleName

virtual const char * GetModuleName() const =0

Returns the name of the module where the function was implemented.

asIScriptModule::GetFunctionByDecl

virtual asIScriptFunction * GetFunctionByDecl(const char *decl) const =0

Returns the function by its declaration.

asGetActiveContext

AS_API asIScriptContext * asGetActiveContext()

Returns the currently active context.

The yield function is even simpler:

void Yield()

{

  asIScriptContext *ctx = asGetActiveContext();

  if( ctx )

  {

    // Suspend the context so the next co-routine can be resumed

    ctx->Suspend();

  }

}

asIScriptContext::Suspend

virtual int Suspend()=0

Suspends the execution, which can then be resumed by calling Execute again.

A basic control algorithm might look like this:

std::vector<asIScriptContext *> coroutines;

void Execute()

{

  int n = 0;

  while( coroutines.size() > 0 )

  {

    // Resume the co-routine

    int r = coroutines[n]->Execute();

    if( r == asEXECUTION_SUSPENDED )

    {

      // Resume the next co-routine

      if( ++n == coroutines.size() )

        n = 0;

    }

    else

    {

      // The co-routine finished so let's remove it

      coroutines[n]->Release();

      coroutines.erase(n);

    }

  }

}

asEXECUTION_SUSPENDED

@ asEXECUTION_SUSPENDED

The execution is suspended and can be resumed.

Definition: angelscript.h:404

See also

Context manager, Co-routines, Concurrent scripts

Generated on Sun May 21 2023 10:12:39 for AngelScript by  1.9.6

repo_name: Antidote/openrpgeditor

license: mit


code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen 1.9.6"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>AngelScript: Custom options</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>

<link href="navtree.css" rel="stylesheet" type="text/css"/>

<link href="search/search.css" rel="stylesheet" type="text/css"/>

<link href="doxygen.css" rel="stylesheet" type="text/css" />

AngelScript

          <input type="text" id="MSearchField" value="" placeholder="Search" accesskey="S"
               onfocus="searchBox.OnSearchFieldFocus(true)"
               onblur="searchBox.OnSearchFieldFocus(false)"
               onkeyup="searchBox.OnSearchFieldChange(event)"/>

Loading...

Searching...

No Matches

Custom options

Most applications have different needs and AngelScript is meant to be as flexible as possible to suit everybody. For this reason there are several options for customizing the script engine for the best fit.

Registerable types

Even types that one might normally expect to be built-in to a script language, e.g. string, array, and other container classes, are registered by the application. This is to allow the application to provide the exact implementation desired.

Of course, should the application developer want to use a premade implementation the add-ons provide just that.

Language modifications

The engine method SetEngineProperty permits making runtime choices about several different behaviours in the engine.

The following modify the script language in one way or the other:

asEP_DISALLOW_EMPTY_LIST_ELEMENTS

By turning on this option the compiler will no longer accept empty list elements in initialization lists. The following will for example not be supported:

  array<int> arr = {1,2,,4,5,};

When not turned on, the compiler will leave the empty list elements with the uninitialized value, just as an uninitialized variable of the same type.

asEP_DISALLOW_VALUE_ASSIGN_FOR_REF_TYPE

By turning on this the compiler will no longer allow the use of value assignment operators on reference types. While it may seem drastic, it can help reduce risk for bugs in the scripts, as the script writer will no longer be able to do a value assignment by mistake when he meant to a handle assignment. Reference types should usually

 not be copied, so the imposed restriction will likely not cause problems anyway.

asEP_ALLOW_UNSAFE_REFERENCES

By turning on unsafe references you allow in-out references to be used for primitives and value types too. Normally this will work fine, just as it does in ordinary languages such as C++, but know that it is quite possible to write scripts that will cause memory invasions or crashes if the references are not properly guarded. With this option turned on you cannot consider the scripts to be sand-boxed any longer.

Turning this option on also makes temporary objects created within expressions to be destroyed only at the end of expressions, rather than as soon as possible. This makes it possible to pass references to temporary objects to function calls within the expression, without the references becoming invalid too early.

asEP_USE_CHARACTER_LITERALS, asEP_ALLOW_MULTILINE_STRINGS, asEP_SCRIPT_SCANNER, asEP_STRING_ENCODING

These options are used to determine how strings are treated by the compiler. The details are described in Custom string type.

asEP_HEREDOC_TRIM_MODE

With this option the compiler can be set to always trim heredoc strings, only trim if it is multiple lines, or never trim them.

asEP_ALLOW_IMPLICIT_HANDLE_TYPES

This option is experimental. By turning it on script classes can be declared to always be treated as handles by declaring the class with @ before the name of the class. When this is done all variables of that type will be handles, and the assignment operator will always perform a handle assignment.

asEP_REQUIRE_ENUM_SCOPE

With this option all enum values must be prefixed with the enum type using the scope operator to qualify.

asEP_PROPERTY_ACCESSOR_MODE

By default virtual property accessors when declared as individual functions need to be marked as such with the keyword 'property', or else the compiler won't consider the functions as virtual properties. This behaviour was introduced in version 2.33.1.

Before this version, the compiler would automatically identify functions with the pre-fix 'get_' or 'set_' as virtual property accessors if the function signature was appropriate. This led to undesired behaviour when developers would declare such functions without the intention of them being used as virtual properties and yet the compiler used them as such.

For backwards compatibility it is still possible to configure the engine to use this behaviour by setting the engine property asEP_PROPERTY_ACCESSOR_MODE to 2.

If it also possible to disable virtual property accessors all together by setting the engine property to 0. Setting the option to 1 only allows virtual property accessors for registered functions, but still without requiring the keyword 'property'.

asEP_DISALLOW_GLOBAL_VARS

The application can disable the ability to declare global variables in scripts completely. This can be useful in such cases when the application wish to have full control of what can and cannot be stored by a script.

asEP_ALWAYS_IMPL_DEFAULT_CONSTRUCT

If this flag is set to true, the compiler will always provide a default constructor for classes even if it hasn't been implemented by the script. Normally this option is not recommended, because if a script class provides a non-default constructor but not the default constructor it is most likely because it is desired that the class should always be initialized with the non-default constructor.

asEP_ALTER_SYNTAX_NAMED_ARGS

If this flag is set to 1 or 2 the compiler will accept the '=' instead of ':' for naming arguments in function calls. When option 1 is used the compiler will warn about it, so the script writer can update the script to the correct ':' token. When option 2 is used both '=' and ':' are accepted silently.

Observe that the use of the '=' token for naming arguments may cause a bit of a confusion. Especially if there is a variable with the same name as the function argument as the script writer may think he's updating the value of the variable, but is just naming the function argument.

asEP_DISABLE_INTEGER_DIVISION

This option changes the default behaviour of the / and /= operators. When true, this option replaces integer division with floating-point division, e.g. 1/2 == 0.5 instead of 1/2 == 0

asEP_PRIVATE_PROP_AS_PROTECTED

When this option is set to true the compiler will not prevent the access to private properties inherited from the parent class. It will only write a warning.

This option is provided to give backwards compatibility for scripts written between versions 2.19.1, when support for private properties was added, albeit working more like protected properties, and version 2.30.0 when the behaviour was changed so that private properties work like in other languages, i.e. they can't be accessed by derived classes.

asEP_ALLOW_UNICODE_IDENTIFIERS

When this option is set to true the compiler will accept identifiers that contain characters with byte value higher than 127. This permits the use of international characters in the identifiers as the script can be encoded in UTF-8 format and compiler normally.

asEP_IGNORE_DUPLICATE_SHARED_INTF

By turning on this option the compiler will silently accept duplicate declarations of shared interfaces in modules. This option was added to provide backwards compatibility with 2.35.0.

Engine behaviours

There are also several options in SetEngineProperty for modifying the way the engine behaves.

asEP_MAX_NESTED_CALLS

This property defines how many recursive nested calls the engine should accept. In this context a nested call is when a script calls into an application registered function that in turn calls another script function. The second call is then said to be a nested call.

By default the engine will accept 100 nested calls.

asEP_OPTIMIZE_BYTECODE

Normally this option is only used for testing the library, but should you find that the compilation time takes too long, then it may be of interest to turn off the bytecode optimization pass by setting this option to false.

asEP_COPY_SCRIPT_SECTIONS

If you want to spare some dynamic memory and the script sections passed to the engine is already stored somewhere in memory then you can turn off this options. If you do you'll need to be careful not to modify or deallocate the script sections until module has been built.

asEP_MAX_STACK_SIZE, asEP_INIT_STACK_SIZE, asEP_MAX_CALL_STACK_SIZE, asEP_INIT_CALL_STACK_SIZE

The script context's data stack grows dynamically as needed during executions. By default there is no limit to how large it may grow, but if you wish to set this limit you can do with the asEP_MAX_STACK_SIZE option. The limit is given in bytes, but it is only an approximate limit. Setting the limit to 1MB doesn't mean the stack will grow exactly to 1MB, but you can be certain that it will not grow much beyond that.

Whenever a context attempts to grow the stack more than it is allowed, it will abort the execution and return a 'stack overflow' script exception.

Similarly the call stack also grows dynamically as needed during executions. The default here is also no limit, with the ability to limit maximum size by setting the asEP_MAX_CALL_STACK_SIZE. This limit is set in number of function calls.

In some cases it may be useful to set the initial stack size too, e.g. if you know beforehand that a large stack is needed, or if you wish to avoid any runtime memory allocations during an execution. In this case you can use the asEP_INIT_STACK_SIZE for the data stack, and asEP_INIT_CALL_STACK_SIZE for the call stack.

asEP_BUILD_WITHOUT_LINE_CUES

This option can be used to speed up the execution of scripts a bit. It is best described in Compile scripts without line cues.

asEP_INIT_GLOBAL_VARS_AFTER_BUILD

By default the global variables in the script modules are initialized immediately after building the scripts, but sometimes this is not desired. For example, if you debug the initialization of the variables, or if you're building an offline compiler, or even if you want to add extra validation of the variables to make sure the script doesn't declare any variables you don't want it to.

See also

Pre-compiled byte code

asEP_INCLUDE_JIT_INSTRUCTIONS

In order for JIT compilation to work properly it needs to have some hints in the bytecode so it can know where a switch from bytecode execution to JIT compiled execution shall be done. By default this option is turned off as it would just add an overhead to the bytecode, but when using JIT compilation this overhead will be compensated by the faster execution with direct machine code.

See also

How to build a JIT compiler

asEP_EXPAND_DEF_ARRAY_TO_TMPL

This option is to determine how the default array type is displayed in compiler messages and function declarations, e.g. int[] or array<int>.

asEP_AUTO_GARBAGE_COLLECT

By default AngelScript's incremental garbage collector is executed as scripts are invoked by the application. This does add a slight overhead to the execution, which is normally tolerable but in some applications the CPU cycles may be needed for other things. In this case the automatic execution of the garbage collector can be turned off to hold off on the execution until the CPU can be spared.

See also

Garbage collection

asEP_COMPILER_WARNINGS

Compiler warnings can be turned off or treated as errors by setting this engine property.

asEP_GENERIC_CALL_MODE

By default the generic calling convention treats the ref counts in the handles the same way that the native calling convention, i.e. releases any handles passed to a function if they have been marked as auto handles, and increments the returned handle if it has been marked as auto handle.

If the behaviour used before 2.33.0 is desired for backwards compatibility, then set this property to 0. In this case the generic calling convention will always release references for handles received in arguments, and never increment references for returned handles.

asEP_NO_DEBUG_OUTPUT

When the library is built with AS_DEBUG it will write debug output to the folder AS_DEBUG by default. By turning on this engine property this debug output is disabled.

Generated on Sun May 21 2023 10:12:39 for AngelScript by  1.9.6


code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen 1.9.6"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>AngelScript: Dynamic compilations</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>

<link href="navtree.css" rel="stylesheet" type="text/css"/>

<link href="search/search.css" rel="stylesheet" type="text/css"/>

<link href="doxygen.css" rel="stylesheet" type="text/css" />

AngelScript

          <input type="text" id="MSearchField" value="" placeholder="Search" accesskey="S"
               onfocus="searchBox.OnSearchFieldFocus(true)"
               onblur="searchBox.OnSearchFieldFocus(false)"
               onkeyup="searchBox.OnSearchFieldChange(event)"/>

Loading...

Searching...

No Matches

Dynamic compilations

There are various forms of dynamic compilations of scripts. In the following subtopics I try to explain the theory of them and give some information on how they can be implemented.

On demand builds

The most common form of dynamic compilations is building new modules on demand, i.e. as they are needed. When using this the application or game engine is typically designed to have separate scripts to perform different tasks, e.g. menu handling, different types of AI controllers, player controller, etc. If these separate scripts have an abstraction layer through the application to interact with each other it is very easy to build the scripts on demand.

To build a new script on demand, simply get a new module, add the needed script sections to it and build it just as you would when building the first script. As each module is independent the previously existing module will be unaffected.

Each module can be configured to see a different application interface, so there is no need to create different engine instances.

Though modules are independent, they can still interact with each other if the application provides the interface for that. To make the communication between modules easier one can chose to use shared script entities. Shared script entities will be compiled only once by the first module that includes the code. Any subsequent module that also include the code for the shared entity will reuse what was built by the first module, i.e. will share the internal type and bytecode.

Another option is to import global functions from a previously compiled module. In this case the application must make sure the function that exports the functions is compiled first, then after building the module that imports the function the application must call the asIScriptModule::BindAllImportedFunctions so conclude the imports.

See also

Game sample

Incremental builds

Besides the ordinary compilation of scripts and subsequent executions, AngelScript also support dynamically compiling additional functions and global variables to incrementally add to the scope of a module. These functions and variables will become part of the scope, just as if they had been included in the initial script compilation which means that subsequent executions or incremental compilations can use them.

It is also possible to dynamically remove functions and variables. Doing so doesn't immediately discard the functions or variables, so other functions that still refer to them will not fail when executing them. They will however no longer be visible for new compilations or when searching for functions or variables in the module.

This kind of dynamic compilations is most useful when dealing with user interaction, e.g. an ingame console, or perhaps event handlers, e.g. a trigger on a GUI button click.

See also

ExecuteString() add-on, Console sample

Hot reloading scripts

Hot reloading is most often used when quick change & test cycles are wanted. It is quite common for game engines to have a debug mode that continuously monitors the script files to detect any updates that are made, and when detected automatically reloads the scripts without restarting the game. This allows the developers, for example, to quickly change the behaviour of the AI and immediately see the result without having to play through the entire level over and over again.

Hot reloading without preserving the state of existing objects is quite trivial. Simply discard everything, reload the scripts, and create new objects. On the other hand, hot reloading scripts while preserving the state of existing objects can be quite tricky, but is also the most rewarding when implemented successfully.

The first part to reloading while preserving the state is keeping track of the object instances so the application knows which objects needs to be serialized when a module is hot reloaded. It is entirely up to the application to build and maintain this mapping.

The second part is knowing which script files that will trigger a reload when changed. When building the module the application needs to store the list of the files that were added to it. If you use the standard script builder add-on, then it provides methods to enumerate the included sections or files.

The third part is the hardest, and that is to implement the actual serialization. Serializing object members of value types is normally quite trivial, simply enumerate the object properties and store a name-value pair for each. Serializing object members of reference types needs some consideration, as depending on the object that is referred to, either you will be able to store the actual pointer, or you will need to store some kind of descriptor as the referred to object will also be recreated during the reload.

Once all the objects that will need to be recreated after the reload have been serialized for backup, the module can be rebuilt using the normal procedure for compiling a script. Afterwards the serialized objects needs to be recreated. Once created you'll enumerate the members and set their values or references according to the backed up data.

Things to consider

Design the engine and script interface with hot reloading in mind from the start. Hot reloading is much easier to implement the less complex interface you have, e.g. script objects that can only interact with other script objects indirectly through the application will not be able to hold references to objects unknown by the application.

Shared script entities will not be recompiled if some module or existing object (including those pending destruction in the garbage collector) still refers to the code. It's recommended that you minimize the use of shared entities to where necessary so as to minimize the need to modify them.

Before discarding the original objects, consider doing a test compilation of the modified scripts in a temporary module first, and only go ahead with the hot reloading if the test compilation is successful. That way you can catch compilation errors that would otherwise cause the recreation of the new objects to fail.

When recreating object instances you can take advantage of the method asIScriptEngine::CreateUninitializedScriptObject to create the objects without actually executing the script class constructor, which may have unwanted side effects during the reload.

Decide what to do when the script modifications introduces new object properties or removes existing properties, as you will not be able to restore the exact same object state then. Perhaps the new property can be filled with some default value to allow the continuation of the execution? Or perhaps the script itself can have a special event handler to provide the initial value of the new member?

The Serializer add-on has been implemented to aid the implementation of hot reloading scripts, though it may not suit all types of applications.

Although it is possible to serialize a script context, if any function on the context's call stack is modified the program position cannot be reliably restored. Therefore it is recommended that you design your application so that contexts can be aborted when hot reloading and any long running scripts restarted, or avoid scripts that run over multiple frames all together so that it is guaranteed that no contexts are active when hot reloading.

See also

Serializer, Serialization

Generated on Sun May 21 2023 10:12:39 for AngelScript by  1.9.6


code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen 1.9.6"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>AngelScript: Dynamic configurations</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>

<link href="navtree.css" rel="stylesheet" type="text/css"/>

<link href="search/search.css" rel="stylesheet" type="text/css"/>

<link href="doxygen.css" rel="stylesheet" type="text/css" />

AngelScript

          <input type="text" id="MSearchField" value="" placeholder="Search" accesskey="S"
               onfocus="searchBox.OnSearchFieldFocus(true)"
               onblur="searchBox.OnSearchFieldFocus(false)"
               onkeyup="searchBox.OnSearchFieldChange(event)"/>

Loading...

Searching...

No Matches

Dynamic configurations

AngelScript supports the concept for configuration groups. This can be used for example by application plug-ins that wish to register their own interface with the script engine. When the plug-in is later removed, the configuration group for that plug-in can also be removed from the AngelScript interface without having to reinitialize everything.

To register part of the interface in a configuration group, the registration should be done between calls to BeginConfigGroup and EndConfigGroup. This can be done as many times as desired, but groups cannot be nested. Observe that object methods, behaviours, and properties will always be placed in the same group where the object type was placed even if another group has been specified between the calls.

To remove a configuration group the method RemoveConfigGroup should be called with the name given to the BeginConfigGroup. It is only possible to remove a config group that is not currently in use. Possible causes that prevents the removal of a group may be:

Another registered a function outside the group uses a type from the group

A script is currently built that uses a type or function from the group

An instance of a script object that uses a type or function from the group is alive

It can be difficult to determine exactly where the use comes from so here's a few steps to do when RemoveConfigGroup returns asCONFIG_GROUP_IS_IN_USE.

Run a full cycle on the garbage collector to destroy lingering objects

Discard any module that may have been compiled with the group

Double check that no other group uses the entities from the group

Generated on Sun May 21 2023 10:12:39 for AngelScript by  1.9.6


code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen 1.9.6"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>AngelScript: Registering a generic handle type</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>

<link href="navtree.css" rel="stylesheet" type="text/css"/>

<link href="search/search.css" rel="stylesheet" type="text/css"/>

<link href="doxygen.css" rel="stylesheet" type="text/css" />

AngelScript

          <input type="text" id="MSearchField" value="" placeholder="Search" accesskey="S"
               onfocus="searchBox.OnSearchFieldFocus(true)"
               onblur="searchBox.OnSearchFieldFocus(false)"
               onkeyup="searchBox.OnSearchFieldChange(event)"/>

Loading...

Searching...

No Matches

Registering a generic handle type

Sometimes it is useful to be able to take a reference to an object without having to force specific implementation on the object, e.g. when providing a generic storage class to scripts, or providing a generic messaging system.

In AngelScript there is no common denominator for all object types, the reasons for this is that script classes and application registered classes have completely different implementation and cannot be generalized under a single superclass.

Instead, AngelScript provides an alternative by allowing the application to register a generic handle type. This type would be implemented according to the application's needs, but to the script it would look like it was a generic handle that can reference any reference type. With this the type will be able to hold objects, functions, and arrays.

To register the generic handle type, the application should follow the same principles as for a value type, except for a few details, that will be described here.

The type must be registered with the additional flag asOBJ_ASHANDLE. This is the flag that tells AngelScript that the type simulates a generic handle.

In order to allow assigning any handle to the type, the opHndlAssign method must be registered with a variable parameter type, e.g. 'ref &opHndlAssign(const ?&in)'.

The opEquals method must also be registered with a The variable parameter type in order to allow the is and !is operators to behave as expected for a handle, e.g. 'bool opEquals(const ?&in)'.

Finally the opCast must be registered with the signature 'void opCast(?&out)' in order to allow a dynamic cast to any other type.

As this is a quite useful type, with really very little need for customization, a standard add-on with this implementation has been provided with the SDK.

See also

ref object

Generated on Sun May 21 2023 10:12:39 for AngelScript by  1.9.6


code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen 1.9.6"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>AngelScript: Import functions</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>

<link href="navtree.css" rel="stylesheet" type="text/css"/>

<link href="search/search.css" rel="stylesheet" type="text/css"/>

<link href="doxygen.css" rel="stylesheet" type="text/css" />

AngelScript

          <input type="text" id="MSearchField" value="" placeholder="Search" accesskey="S"
               onfocus="searchBox.OnSearchFieldFocus(true)"
               onblur="searchBox.OnSearchFieldFocus(false)"
               onkeyup="searchBox.OnSearchFieldChange(event)"/>

Loading...

Searching...

No Matches

Import functions

Importing functions is a form of sharing code between script modules. Unlike the shared keyword though, the import requires specific code in the application to bind the imported functions after the script has been compiled.

This may be useful when the application wants to control specifically what can and cannot be imported.

To bind all the imported functions, without any specific treatment, the application just needs to call the BindAllImportedFunctions method after the build has completed.

If fine grained control is desired, then the application should use the methods GetImportedFunctionCount, GetImportedFunctionDeclaration, GetImportedFunctionSourceModule, GetFunctionByDecl, and BindImportedFunction, to enumerate and bind the imported functions one by one, and raise an error in case the script tries to import a function that the application doesn't allow.

Another advantage that the import function feature has over shared entities, is that the imported functions can be unbound and then bound to another script module if there is a need to change the sourcing module. To unbind the bound functions use the methods UnbindAllImportedFunctions or UnbindImportedFunction.

Generated on Sun May 21 2023 10:12:39 for AngelScript by  1.9.6


code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen 1.9.6"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>AngelScript: Inheriting from application registered class</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>

<link href="navtree.css" rel="stylesheet" type="text/css"/>

<link href="search/search.css" rel="stylesheet" type="text/css"/>

<link href="doxygen.css" rel="stylesheet" type="text/css" />

AngelScript

          <input type="text" id="MSearchField" value="" placeholder="Search" accesskey="S"
               onfocus="searchBox.OnSearchFieldFocus(true)"
               onblur="searchBox.OnSearchFieldFocus(false)"
               onkeyup="searchBox.OnSearchFieldChange(event)"/>

Loading...

Searching...

No Matches

Inheriting from application registered class

A script class cannot directly inherit from an application registered class, as the script classes are not compiled into native machine code like the application classes are.

It is however possible to emulate the inheritance by using a proxy class to hide the underlying differences in an abstract layer. The proxy class has two parts, one is the C++ side that the application sees, and the other is the script side that the scripts can see and inherit from.

The following is an example implementation of such a proxy class.

// On the C++ side

class FooScripted

{

public:

  // Public interface that we want the script to be able to override

  void CallMe()

  {

    // If the script side is still alive, then call the scripted function

    if( !m_isDead->Get() )

    {

      asIScriptEngine *engine = m_obj->GetEngine();

      asIScriptContext *ctx = engine->RequestContext();

      // GetMethodByDecl returns the virtual function on the script class

      // thus when calling it, the VM will execute the derived method

      ctx->Prepare(m_obj->GetObjectType()->GetMethodByDecl("void CallMe()"));

      ctx->SetObject(m_obj);

      ctx->Execute();

      engine->ReturnContext(ctx);

    }

  }

  int m_value;

  // A factory function that can be used by the script side to create

  static FooScripted *Factory()

  {

    asIScriptContext *ctx = asGetActiveContext();

    // Get the function that is calling the factory so we can be certain it is the FooScript script class

    asIScriptFunction *func = ctx->GetFunction(0);

    if( func->GetObjectType() == 0 || std::string(func->GetObjectType()->GetName()) != "FooScripted" )

    {

      ctx->SetException("Invalid attempt to manually instantiate FooScript_t");

      return 0;

    }

    // Get the this pointer from the calling function so the FooScript C++

    // class can be linked with the FooScript script class

    asIScriptObject *obj = reinterpret_cast<asIScriptObject*>(ctx->GetThisPointer(0));

    return new FooScripted(obj);

  }

  // Reference counting

  void AddRef()

  {

    m_refCount++;

    // Increment also the reference counter to the script side so

    // it isn't accidentally destroyed before the C++ side

    if( !m_isDead->Get() )

      m_obj->AddRef();

  }

  void Release()

  {

    // Release the script instance too

    if( !m_isDead->Get() )

      m_obj->Release();

    if( --m_refCount == 0 ) delete this;

  }

  // Assignment operator

  FooScripted &operator=(const FooScripted &o)

  {

    // Copy only the content, not the script proxy class

    m_value = o.m_value;

    return *this;

  }

protected:

  // The constructor and destructor are indirectly called

  FooScripted(asIScriptObject *obj) : m_obj(0), m_isDead(0), m_value(0), m_refCount(1)

  {

    // Get the weak ref flag for the script object to

    // avoid holding a strong reference to the script class

    m_isDead = obj->GetWeakRefFlag();

    m_isDead->AddRef();

    m_obj = obj;

  }

  ~FooScripted()

  {

    // Release the weak ref flag

    m_isDead->Release();

  }

  // Reference count

  int m_refCount;

  // The C++ side holds a weak link to the script side to

  // avoid a circular reference between the C++ side and

  // script side

  asILockableSharedBool *m_isDead;

  asIScriptObject *m_obj;

};

asILockableSharedBool

A lockable shared boolean.

Definition: angelscript.h:4257

asILockableSharedBool::AddRef

virtual int AddRef() const =0

Adds a reference to the shared boolean.

asIScriptContext

The interface to the virtual machine.

Definition: angelscript.h:2735

asIScriptContext::SetObject

virtual int SetObject(void *obj)=0

Sets the object for a class method call.

asIScriptContext::GetThisPointer

virtual void * GetThisPointer(asUINT stackLevel=0)=0

Returns a pointer to the object, if a class method is being executed.

asIScriptContext::GetFunction

virtual asIScriptFunction * GetFunction(asUINT stackLevel=0)=0

Returns the function at the specified callstack level.

asIScriptContext::Prepare

virtual int Prepare(asIScriptFunction *func)=0

Prepares the context for execution of the function.

asIScriptContext::SetException

virtual int SetException(const char *info, bool allowCatch=true)=0

Sets an exception, which aborts the execution.

asIScriptContext::Execute

virtual int Execute()=0

Executes the prepared function.

asIScriptEngine

The engine interface.

Definition: angelscript.h:1102

asIScriptEngine::RequestContext

virtual asIScriptContext * RequestContext()=0

Request a context.

asIScriptEngine::ReturnContext

virtual void ReturnContext(asIScriptContext *ctx)=0

Return a context when it won't be used anymore.

asIScriptFunction

The interface for a script function description.

Definition: angelscript.h:4000

asIScriptFunction::GetObjectType

virtual asITypeInfo * GetObjectType() const =0

Returns the object type for class or interface method.

asIScriptObject

The interface for an instance of a script object.

Definition: angelscript.h:3590

asIScriptObject::Release

virtual int Release() const =0

Decrease reference counter.

asIScriptObject::GetWeakRefFlag

virtual asILockableSharedBool * GetWeakRefFlag() const =0

Returns the weak ref flag for the object.

asITypeInfo::GetName

virtual const char * GetName() const =0

Returns a temporary pointer to the name of the datatype.

asGetActiveContext

AS_API asIScriptContext * asGetActiveContext()

Returns the currently active context.

This type is registered with the engine as the following:

void RegisterFooScripted(asIScriptEngine *engine)

{

  engine->RegisterObjectType("FooScripted_t", 0, asOBJ_REF);

  engine->RegisterObjectBehaviour("FooScripted_t", asBEHAVE_FACTORY, "FooScripted_t @f()", asFUNCTION(FooScripted::Factory), asCALL_CDECL);

  engine->RegisterObjectBehaviour("FooScripted_t", asBEHAVE_ADDREF, "void f()", asMETHOD(FooScripted, AddRef), asCALL_THISCALL);

  engine->RegisterObjectBehaviour("FooScripted_t", asBEHAVE_RELEASE, "void f()", asMETHOD(FooScripted, Release), asCALL_THISCALL);

  engine->RegisterObjectMethod("FooScripted_t", "FooScripted_t &opAssign(const FooScripted_t &in)", asMETHOD(FooScripted, operator=), asCALL_THISCALL);

  engine->RegisterObjectMethod("FooScripted_t", "void CallMe()", asMETHOD(FooScripted, CallMe), asCALL_THISCALL);

  engine->RegisterObjectProperty("FooScripted_t", "int m_value", asOFFSET(FooScripted, m_value));

}

asCALL_CDECL

@ asCALL_CDECL

A cdecl function.

Definition: angelscript.h:230

asCALL_THISCALL

@ asCALL_THISCALL

A thiscall class method.

Definition: angelscript.h:236

asOFFSET

#define asOFFSET(s, m)

Returns the offset of an attribute in a struct.

Definition: angelscript.h:682

asMETHOD

#define asMETHOD(c, m)

Returns an asSFuncPtr representing the class method specified by class and method name.

Definition: angelscript.h:750

asFUNCTION

#define asFUNCTION(f)

Returns an asSFuncPtr representing the function specified by the name.

Definition: angelscript.h:685

asBEHAVE_FACTORY

@ asBEHAVE_FACTORY

Factory.

Definition: angelscript.h:366

asBEHAVE_ADDREF

@ asBEHAVE_ADDREF

AddRef.

Definition: angelscript.h:370

asBEHAVE_RELEASE

@ asBEHAVE_RELEASE

Release.

Definition: angelscript.h:372

asOBJ_REF

@ asOBJ_REF

A reference type.

Definition: angelscript.h:254

asIScriptEngine::RegisterObjectProperty

virtual int RegisterObjectProperty(const char *obj, const char *declaration, int byteOffset, int compositeOffset=0, bool isCompositeIndirect=false)=0

Registers a property for the object type.

asIScriptEngine::RegisterObjectType

virtual int RegisterObjectType(const char *obj, int byteSize, asDWORD flags)=0

Registers a new object type.

asIScriptEngine::RegisterObjectBehaviour

virtual int RegisterObjectBehaviour(const char *obj, asEBehaviours behaviour, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv, void *auxiliary=0, int compositeOffset=0, bool isCompositeIndirect=false)=0

Registers a behaviour for the object type.

asIScriptEngine::RegisterObjectMethod

virtual int RegisterObjectMethod(const char *obj, const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv, void *auxiliary=0, int compositeOffset=0, bool isCompositeIndirect=false)=0

Registers a method for the object type.

The script side is declared as shared so it can be used in all script modules. It is also declared as abstract so it cannot be instantiated by itself, only as a parent class of another script class.

This script section should preferably be included automatically by the application in all the modules that should be able to derive from the FooScripted class.

  // On the script side
  shared abstract class FooScripted
  {
    // Allow scripts to create instances
    FooScripted()
    {
      // Create the C++ side of the proxy
      @m_obj = FooScripted_t();
    }

    // The copy constructor performs a deep copy
    FooScripted(const FooScripted &o)
    {
      // Create a new C++ instance and copy content
      @m_obj = FooScripted_t();
      m_obj = o.m_obj;
    }

    // Do a deep a copy of the C++ object
    FooScripted &opAssign(const FooScripted &o)
    {
      // copy content of C++ instance
      m_obj = o.m_obj;
      return this;
    }

    // The script side forwards the call to the C++ side
    void CallMe() { m_obj.CallMe(); }

    // The C++ side property is exposed to the script through accessors
    int m_value
    {
      get { return m_obj.m_value; }
      set { m_obj.m_value = value; }
    }

    // The script class can be implicitly cast to the C++ type through the opImplCast method
    FooScripted_t @opImplCast() { return m_obj; }

    // Hold a reference to the C++ side of the proxy
    private FooScripted_t @m_obj;
  }

Now the scripts classes can derive from the FooScripted class

 and access the properties and methods of the parent class normally.

  // Implement a script class that derives from the application class
  class FooDerived : FooScripted
  {
    void CallMe()
    {
       m_value += 1;
    }
  }

  void main()
  {
    // When newly created the m_value is 0
    FooDerived d;
    assert( d.m_value == 0 );

    // When calling the method the m_value is incremented with 1
    d.CallMe();
    assert( d.m_value == 1 );
  }

It is of course also possible to create an instance of the scripted class from the application and access it through the FooScripted C++ proxy, thus making it transparent from the rest of the application that the implementation is actually in the script.

FooScripted *CreateFooDerived(asIScriptEngine *engine)

{

  // Create an instance of the FooDerived script class that inherits from the FooScripted C++ class

  asIScriptObject *obj = reinterpret_cast<asIScriptObject*>(engine->CreateScriptObject(mod->GetTypeInfoByName("FooDerived")));

  // Get the pointer to the C++ side of the FooScripted class

  FooScripted *obj2 = *reinterpret_cast<FooScripted**>(obj->GetAddressOfProperty(0));

  // Increase the reference count to the C++ object, as this is what will

  // be used to control the life time of the object from the application side

  obj2->AddRef();

  // Release the reference to the script side

  obj->Release();

  return obj2;

}

void Foo(asIScriptEngine *engine)

{

  FooScripted *obj = CreateFooDerived(engine);

  // Once the object is created the application can access it normally through

  // the FooScripted pointer, without having to know that the implementation

  // is actually done in the script.

  // When newly created the m_value is 0

  assert( obj->m_value == 0 );

  // When calling the method the m_value is incremented with 1 by the script

  obj->CallMe();

  assert( obj->m_value == 1 );

  // Release the object to destroy the instance (this will also destroy the script side)

  obj->Release();

}

asIScriptEngine::CreateScriptObject

virtual void * CreateScriptObject(const asITypeInfo *type)=0

Creates an object defined by its type.

asIScriptObject::AddRef

virtual int AddRef() const =0

Increase reference counter.

asIScriptObject::GetAddressOfProperty

virtual void * GetAddressOfProperty(asUINT prop)=0

Returns a pointer to the property referenced by prop.

Generated on Sun May 21 2023 10:12:39 for AngelScript by  1.9.6


code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen 1.9.6"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>AngelScript: How to build a JIT compiler</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>

<link href="navtree.css" rel="stylesheet" type="text/css"/>

<link href="search/search.css" rel="stylesheet" type="text/css"/>

<link href="doxygen.css" rel="stylesheet" type="text/css" />

AngelScript

          <input type="text" id="MSearchField" value="" placeholder="Search" accesskey="S"
               onfocus="searchBox.OnSearchFieldFocus(true)"
               onblur="searchBox.OnSearchFieldFocus(false)"
               onkeyup="searchBox.OnSearchFieldChange(event)"/>

Loading...

Searching...

No Matches

How to build a JIT compiler

AngelScript doesn't provide a built-in JIT compiler, instead it permits an external JIT compiler to be implemented through a public interface.

To use JIT compilation, the scripts must be compiled with a few extra instructions that provide hints to the JIT compiler and also entry points so that the VM will know when to pass control to the JIT compiled function. By default this is turned off, and must thus be turned on by setting the engine property asEP_INCLUDE_JIT_INSTRUCTIONS.

If the application sets the JIT compiler with SetJITCompiler AngelScript will automatically invoke it to provide the JIT functions with each compilation or loading of pre-compiled bytecode.

The structure of the JIT function

The JIT compiled function must follow certain rules in order to behave well with the virtual machine. The intention is that the VM will pass the control to the JIT function, and when the execution is to be suspended the JIT function returns the control to the VM, updating the internal state of the VM so that the VM can resume the execution when requested. Each time the JIT function returns control to the VM it must make sure that the VM registers and stack values have been updated according to the code that was executed.

The byte code will have a special instruction, JitEntry, which defines the positions where the VM can pass the control to the JIT function. These are usually placed for every script statement, and after each instruction that calls another function. This implies that the JIT compiled function needs to be able to start the execution at different points based on the argument in the JitEntry instruction. The value of the argument is defined by the JIT compiler and how it is interpreted is also up to the JIT compiler, with the exception of 0 that means that the control should not be passed to the JIT function.

Some byte code instructions are not meant to be converted into native code. These are usually the ones that have a more global effect on the VM, e.g. the instructions that setup a call to a new script function, or that return from a previous instruction. When these functions are encountered, the JIT function should return the control to the VM, and then the VM will execute the instruction.

Other byte code instructions may be partially implemented by the JIT function, for example those that can throw an exception based on specific conditions. One such example is the instructions for divisions, if the divider is 0 the VM will set an exception and abort the execution. For these instructions the JIT compiler should preferrably implement the condition that doesn't throw an exception, and if an exception is to be thrown the JIT function will instead break out to the VM.

The following shows a possible structure of a JIT compiled function:

  void jitCompiledFunc(asSVMRegisters *regs, asPWORD jitArg)
  {
    Read desired VM registers into CPU registers.
    Jump to the current position of the function based on the 'jitArg' argument.
  1:
    Execute code in block 1.
    Jump to exit if an illegal operation is done, e.g. divide by zero.
    Jump to exit if block ends with an instruction that should not be executed by JIT function.
  2:
    ...
  3:
    ...
  exit:
    Update the VM registers before returning control to VM.
    If necessary the function can invoke the methods of the context informed
    in the regs, e.g. to suspend the execution, or to set a script exception.
  }

Traversing the byte code

int CJITCompiler::CompileFunction(asIScriptFunction *func, asJITFunction *output)

{

  bool success = StartNewCompilation();

  // Get the script byte code

  asUINT   length;

  asDWORD *byteCode = func->GetByteCode(&length);

  asDWORD *end = byteCode + length;

  while( byteCode < end )

  {

    // Determine the instruction

    asEBCInstr op = asEBCInstr(*(asBYTE*)byteCode);

    switch( op )

    {

    // Translate each byte code instruction into native code.

    // The codes that cannot be translated should return the control

    // to the VM, so that it can continue the processing. When

    // the VM encounters the next JitEntry instruction it will

    // transfer the control back to the JIT function.

    ...

    case asBC_JitEntry:

      // Update the argument for the JitEntry instruction with

      // the argument that should be sent to the jit function.

      // Remember that 0 means that the VM should not pass

      // control to the JIT function.

      asBC_PTRARG(byteCode) = DetermineJitEntryArg();

      break;

    }

    // Move to next instruction

    byteCode += asBCTypeSize[asBCInfo[op].type];

  }

  if( success )

  {

    *output = GetCompiledFunction();

    return 0;

  }

  return -1;

}

asBYTE

unsigned char asBYTE

8 bit unsigned integer

Definition: angelscript.h:608

asDWORD

unsigned long asDWORD

32 bit unsigned integer

Definition: angelscript.h:624

asBCTypeSize

const int asBCTypeSize[21]

Lookup table for determining the size of each type of bytecode instruction.

Definition: angelscript.h:4995

asBC_PTRARG

#define asBC_PTRARG(x)

Macro to access the first pointer argument in the bytecode instruction.

Definition: angelscript.h:5336

asEBCInstr

asEBCInstr

The bytecode instructions used by the VM.

Definition: angelscript.h:4531

asBC_JitEntry

@ asBC_JitEntry

If a JIT function is available and the argument is not 0 then call the JIT function.

Definition: angelscript.h:4883

asBCInfo

const asSBCInfo asBCInfo[256]

Information on each bytecode instruction.

Definition: angelscript.h:5064

asUINT

unsigned int asUINT

32 bit unsigned integer

Definition: angelscript.h:610

asJITFunction

void(* asJITFunction)(asSVMRegisters *registers, asPWORD jitArg)

The function signature of a JIT compiled function.

Definition: angelscript.h:4496

asIScriptFunction

The interface for a script function description.

Definition: angelscript.h:4000

asIScriptFunction::GetByteCode

virtual asDWORD * GetByteCode(asUINT *length=0)=0

Returns the byte code buffer and length.

asSBCInfo::type

asEBCType type

Instruction argument layout.

Definition: angelscript.h:5035

The following macros should be used to read the arguments from the bytecode instruction. The layout of the arguments is determined from the asBCInfo array.

asBC_DWORDARG

asBC_INTARG

asBC_QWORDARG

asBC_FLOATARG

asBC_PTRARG

asBC_WORDARG0

asBC_WORDARG1

asBC_SWORDARG0

asBC_SWORDARG1

asBC_SWORDARG2

What each byte code instruction does is described in Byte code instructions, but the exact implementation of each byte code instruction is best determined from the implementation in the VM, i.e. the asCScriptContext::ExecuteNext method.

Generated on Sun May 21 2023 10:12:39 for AngelScript by  1.9.6


code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen 1.9.6"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>AngelScript: Byte code instructions</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>

<link href="navtree.css" rel="stylesheet" type="text/css"/>

<link href="search/search.css" rel="stylesheet" type="text/css"/>

<link href="doxygen.css" rel="stylesheet" type="text/css" />

AngelScript

          <input type="text" id="MSearchField" value="" placeholder="Search" accesskey="S"
               onfocus="searchBox.OnSearchFieldFocus(true)"
               onblur="searchBox.OnSearchFieldFocus(false)"
               onkeyup="searchBox.OnSearchFieldChange(event)"/>

Loading...

Searching...

No Matches

Byte code instructions

This page gives a brief description of each of the byte code instructions that the virtual machine has.

Object management

Math instructions

Bitwise instructions

Comparisons

Type conversions

Increment and decrement

Flow control

Stack and data management

Global variables

Initialization list management

Object management

Perform a bitwise copy of a memory buffer to another

asBC_COPY

Push the address and length of a string on the stack

asBC_STR

Allocate the memory for an object and setup the VM to execute the constructor

asBC_ALLOC

Release the memory of an object or list buffer

asBC_FREE

Move the address in an object variable to the object register. The address in the variable is then cleared.

asBC_LOADOBJ

Move the address from the object register to an object variable. The address in the object register is then cleared.

asBC_STOREOBJ

Copy the object handle from one address to another. The reference count of the object is updated to reflect the copy.

asBC_REFCPY

Copy the object handle on stack to a handle in a variable. The reference count of the object is updated to reflect the copy.

asBC_RefCpyV

Push the pointer of an object type on the stack

asBC_OBJTYPE

Push the type id on the stack

asBC_TYPEID

Pop an address to a script object from the stack. If the desired cast can be made store the address in the object register.

asBC_Cast

Push the function pointer on the stack

asBC_FuncPtr

Load the address to a property of the object into the value register. Substitutes the sequence PshV4/8, ADDSi, PopRPtr.

asBC_LoadThisR

asBC_LoadRObjR

Load the address to a property of a value object into the value register. Substitutes the sequence PSF, ADDSi, PopRPtr.

asBC_LoadVObjR

Math instructions

Negate the value in the variable. The original value is overwritten.

asBC_NEGi

asBC_NEGf

asBC_NEGd

asBC_NEGi64

Perform the operation with the value of two variables and store the result in a third variable.

asBC_ADDi

asBC_SUBi

asBC_MULi

asBC_DIVi

asBC_MODi

asBC_POWi

asBC_POWu

asBC_ADDf

asBC_SUBf

asBC_MULf

asBC_DIVf

asBC_MODf

asBC_POWf

asBC_ADDd

asBC_SUBd

asBC_MULd

asBC_DIVd

asBC_MODd

asBC_POWd

asBC_POWdi

asBC_ADDi64

asBC_SUBi64

asBC_MULi64

asBC_DIVi64

asBC_MODi64

asBC_POWi64

asBC_POWu64

Perform the operation with a constant value and the value of the variable. The original value is overwritten.

asBC_ADDIi

asBC_SUBIi

asBC_MULIi

asBC_ADDIf

asBC_SUBIf

asBC_MULIf

Bitwise instructions

Perform a boolean not operation on the value in the variable. The original value is overwritten.

asBC_NOT

Perform a bitwise complement on the value in the variable. The original value is overwritten.

asBC_BNOT

asBC_BNOT64

Perform the operation with the value of two variables and store the result in a third variable.

asBC_BAND

asBC_BOR

asBC_BXOR

asBC_BAND64

asBC_BOR64

asBC_BXOR64

asBC_BSLL

asBC_BSRL

asBC_BSRA

asBC_BSLL64

asBC_BSRL64

asBC_BSRA64

Comparisons

Compare the value of two variables and store the result in the value register.

asBC_CMPi

asBC_CMPu

asBC_CMPf

asBC_CMPd

asBC_CMPi64

asBC_CMPu64

asBC_CmpPtr

Compare the value of a variable with a constant and store the result in the value register.

asBC_CMPIi

asBC_CMPIu

asBC_CMPIf

Test the value in the value register. Update the value register according to the result.

asBC_TZ

asBC_TNZ

asBC_TS

asBC_TNS

asBC_TP

asBC_TNP

Type conversions

Convert the value in the variable. The original value is overwritten.

asBC_iTOb

asBC_iTOw

asBC_sbTOi

asBC_swTOi

asBC_ubTOi

asBC_uwTOi

asBC_iTOf

asBC_fTOi

asBC_uTOf

asBC_fTOu

asBC_dTOi64

asBC_dTOu64

asBC_i64TOd

asBC_u64TOd

Convert the value of a variable and store the result in another variable.

asBC_dTOi

asBC_dTOu

asBC_dTOf

asBC_iTOd

asBC_uTOd

asBC_fTOd

asBC_i64TOi

asBC_i64TOf

asBC_u64TOf

asBC_uTOi64

asBC_iTOi64

asBC_fTOi64

asBC_fTOu64

Increment and decrement

Increment or decrement the value pointed to by the address in the value register.

asBC_INCi8

asBC_DECi8

asBC_INCi16

asBC_DECi16

asBC_INCi

asBC_DECi

asBC_INCi64

asBC_DECi64

asBC_INCf

asBC_DECf

asBC_INCd

asBC_DECd

Increment or decrement the value in the variable.

asBC_IncVi

asBC_DecVi

Flow control

Setup the VM to begin execution of the other script function

asBC_CALL

asBC_CALLINTF

asBC_CALLBND

asBC_CallPtr

Setup the VM to return to the calling function

asBC_RET

Make an unconditional jump to a relative position

asBC_JMP

Make a jump to a relative position depending on the value in the value register

asBC_JZ

asBC_JNZ

asBC_JS

asBC_JNS

asBC_JP

asBC_JNP

asBC_JMPP

asBC_JLowZ

asBC_JLowNZ

Call an application registered function

asBC_CALLSYS

asBC_Thiscall1

Save the state and suspend execution, then return control to the application

asBC_SUSPEND

Give control of execution to the JIT compiled function

asBC_JitEntry

Stack and data management

Push a constant value on the stack.

asBC_PshC4

asBC_PshC8

asBC_PshNull

Push the stack frame pointer on the stack.

asBC_PSF

Swap the top values on the stack.

asBC_SwapPtr

Dereference top pointer on stack. Raises exception if pointer is null.

asBC_RDSPtr

Add an offset to the top address on the stack. Raises exception if address is null.

asBC_ADDSi

Push the value of a variable on the stack.

asBC_PshV4

asBC_PshV8

asBC_PshVPtr

Initialize the value of a variable with a constant.

asBC_SetV1

asBC_SetV2

asBC_SetV4

asBC_SetV8

asBC_ClrVPtr

Copy the value of one variable to another.

asBC_CpyVtoV4

asBC_CpyVtoV8

Validate that an expected pointer is not null.

asBC_CHKREF

asBC_ChkRefS

asBC_ChkNullV

asBC_ChkNullS

Push the variable index with the size of a pointer on the stack.

asBC_VAR

Replace a variable index on the stack with an address.

asBC_GETREF

asBC_GETOBJ

asBC_GETOBJREF

Pop and discard an address from the stack.

asBC_PopPtr

Pop or push an address to or from the value register.

asBC_PopRPtr

asBC_PshRPtr

Copy a value between value register and a variable.

asBC_CpyVtoR4

asBC_CpyVtoR8

asBC_CpyRtoV4

asBC_CpyRtoV8

Copy a value from a variable to the address held in the value register

asBC_WRTV1

asBC_WRTV2

asBC_WRTV4

asBC_WRTV8

Copy a value from the address held in the value register to a variable

asBC_RDR1

asBC_RDR2

asBC_RDR4

asBC_RDR8

Load the address of the variable into the value register

asBC_LDV

Clear the upper bytes of the value register

asBC_ClrHi

Global variables

Push the value of a global variable on the stack

asBC_PshG4

asBC_PshGPtr

Load the address of a global variable into the value register

asBC_LDG

Load the address of a global variable into the value register and copy the value of the global variable to local variable

asBC_LdGRdR4

Copy a value between local variable and global variable.

asBC_CpyVtoG4

asBC_CpyGtoV4

Push the address of the global variable on the stack.

asBC_PGA

Initialize the variable of a global variable with a constant.

asBC_SetG4

Initialization list management

Allocates the memory for the initialization list buffer

asBC_AllocMem

Sets the number of elements that will be repeated afterwards

asBC_SetListSize

Sets the type of the next element

asBC_SetListType

Pushes the address of the list element on the stack

asBC_PshListElmnt

Generated on Sun May 21 2023 10:12:40 for AngelScript by  1.9.6


code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen 1.9.6"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>AngelScript: JIT compilation</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>

<link href="navtree.css" rel="stylesheet" type="text/css"/>

<link href="search/search.css" rel="stylesheet" type="text/css"/>

<link href="doxygen.css" rel="stylesheet" type="text/css" />

AngelScript

          <input type="text" id="MSearchField" value="" placeholder="Search" accesskey="S"
               onfocus="searchBox.OnSearchFieldFocus(true)"
               onblur="searchBox.OnSearchFieldFocus(false)"
               onkeyup="searchBox.OnSearchFieldChange(event)"/>

Loading...

Searching...

No Matches

JIT compilation

How to build a JIT compiler

Byte code instructions

Generated on Sun May 21 2023 10:12:40 for AngelScript by  1.9.6


code:
var doc_adv_jit_topic =
[
    [ "How to build a JIT compiler", "doc_adv_jit.html", [
      [ "The structure of the JIT function", "doc_adv_jit.html#doc_adv_jit_3", null ],
      [ "Traversing the byte code", "doc_adv_jit.html#doc_adv_jit_2", null ]
    ] ],
    [ "Byte code instructions", "doc_adv_jit_1.html", [
      [ "Object management", "doc_adv_jit_1.html#doc_adv_jit_1_1", null ],
      [ "Math instructions", "doc_adv_jit_1.html#doc_adv_jit_1_2", null ],
      [ "Bitwise instructions", "doc_adv_jit_1.html#doc_adv_jit_1_3", null ],
      [ "Comparisons", "doc_adv_jit_1.html#doc_adv_jit_1_4", null ],
      [ "Type conversions", "doc_adv_jit_1.html#doc_adv_jit_1_5", null ],
      [ "Increment and decrement", "doc_adv_jit_1.html#doc_adv_jit_1_6", null ],
      [ "Flow control", "doc_adv_jit_1.html#doc_adv_jit_1_7", null ],
      [ "Stack and data management", "doc_adv_jit_1.html#doc_adv_jit_1_8", null ],
      [ "Global variables", "doc_adv_jit_1.html#doc_adv_jit_1_9", null ],
      [ "Initialization list management", "doc_adv_jit_1.html#doc_adv_jit_1_10", null ]
    ] ]
];


code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen 1.9.6"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>AngelScript: Multithreading</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>

<link href="navtree.css" rel="stylesheet" type="text/css"/>

<link href="search/search.css" rel="stylesheet" type="text/css"/>

<link href="doxygen.css" rel="stylesheet" type="text/css" />

AngelScript

          <input type="text" id="MSearchField" value="" placeholder="Search" accesskey="S"
               onfocus="searchBox.OnSearchFieldFocus(true)"
               onblur="searchBox.OnSearchFieldFocus(false)"
               onkeyup="searchBox.OnSearchFieldChange(event)"/>

Loading...

Searching...

No Matches

Multithreading

AngelScript supports multithreading, though not yet on all platforms. You can determine if multithreading is supported on your platform by calling the asGetLibraryOptions function and checking the returned string for "AS_NO_THREADS". If the identifier is in the returned string then multithreading is not supported.

Even if you don't want or can't use multithreading, you can still write applications that execute multiple scripts simultaneously.

Things to think about with a multithreaded environment

If you plan on creating engines in multiple threads, the application should call asPrepareMultithread before the first engine is created, and asUnprepareMultithread after the last engine has been released. This is to avoid race conditions when the engine creates the internal shared thread manager. If only one script engine is used, then it is not necessary to do go through these extra steps.

Always call asThreadCleanup before terminating a thread that accesses the script engine. If this is not done, the memory allocated specifically for that thread will be lost until the script engine is freed.

If the application is composed of multiple application modules (dlls) then it may be necessary to share a single thread manager across the modules. Do this by calling asGetThreadManager in the main module and then call asPrepareMultithread on the secondary modules with the returned pointer.

Multiple threads may execute scripts in separate contexts. The contexts may execute scripts from the same module, but if the module has global variables you need to make sure the scripts perform proper access control so that they do not get corrupted, if multiple threads try to update them simultaneously.

The engine will only allow one thread to build scripts at any one time, since this is something that changes the internal state of the engine and cannot safely be done in multiple threads simultaneously.

Reference counters for objects that will be referred to by scripts in different threads must be thread safe in order to avoid race conditions as multiple threads attempt to update the same reference counter. The library provides a pair of functions to facilitate the implementation of thread safe reference counters. See asAtomicInc and asAtomicDec.

Resources that are shared by script modules such as registered properties and objects must be protected by the application for simultaneous access, as the script engine doesn't do this automatically. A read-write lock is provided by the library that can be used for this. See asAcquireExclusiveLock, asReleaseExclusiveLock, asAcquireSharedLock, and asReleaseSharedLock.

If you use automatic garbage collection (turned on by default) then you must make sure the behaviours registered for types to support garbage collection must be thread safe as the garbage collector can be invoked from any thread that is executing a script.

Many of the add-ons are not thread safe. Either create your own or make sure you review the add-ons you wish to use to guarantee thread safety.

Fibers

AngelScript can be used with fibers as well. However, as fibers are not real threads you need to be careful if multiple fibers need to execute scripts. AngelScript keeps track of active contexts by pushing and popping the context pointers on a stack. With fibers you can easily corrupt this stack if you do not pay attention to where and when script executions are performed.

Try to avoid switching fibers when a script is executing, instead you should suspend the current script execution and only switch to the other fiber when the context's Execute method returns. If you really must switch fibers while executing a script, then make sure the second fiber initiates and concludes its own script execution before switching back to the original fiber.

Generated on Sun May 21 2023 10:12:40 for AngelScript by  1.9.6


code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen 1.9.6"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>AngelScript: Using namespaces</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>

<link href="navtree.css" rel="stylesheet" type="text/css"/>

<link href="search/search.css" rel="stylesheet" type="text/css"/>

<link href="doxygen.css" rel="stylesheet" type="text/css" />

AngelScript

          <input type="text" id="MSearchField" value="" placeholder="Search" accesskey="S"
               onfocus="searchBox.OnSearchFieldFocus(true)"
               onblur="searchBox.OnSearchFieldFocus(false)"
               onkeyup="searchBox.OnSearchFieldChange(event)"/>

Loading...

Searching...

No Matches

Using namespaces

Namespaces can be used to group related functions and other entities together. Doing so avoids potential conflicts with other entities that happen to use the same name, but is otherwise unrelated.

Namespaces can be used in the application registered interface, as well as in the scripts.

Registering the interface with namespaces

To register a function, or other entity in a specific namespace, the application should first call the method SetDefaultNamespace to define the desired namespace. After that the registration follows the normal procedure as described in the chapter on registering the interface.

void RegisterInNamespace(asIScriptEngine *engine)

{

  int r;

  // Register the type and function in the namespace

  r = engine->SetDefaultNamespace("myspace"); assert( r >= 0 );

  r = engine->RegisterObjectType("mytype", 0, asOBJ_REF); assert( r >= 0 );

  r = engine->RegisterGlobalFunction("void myfunc()", asFUNCTION(myfunc), asCALL_CDECL); assert( r >= 0 );

}

asCALL_CDECL

@ asCALL_CDECL

A cdecl function.

Definition: angelscript.h:230

asFUNCTION

#define asFUNCTION(f)

Returns an asSFuncPtr representing the function specified by the name.

Definition: angelscript.h:685

asOBJ_REF

@ asOBJ_REF

A reference type.

Definition: angelscript.h:254

asIScriptEngine

The engine interface.

Definition: angelscript.h:1102

asIScriptEngine::RegisterObjectType

virtual int RegisterObjectType(const char *obj, int byteSize, asDWORD flags)=0

Registers a new object type.

asIScriptEngine::RegisterGlobalFunction

virtual int RegisterGlobalFunction(const char *declaration, const asSFuncPtr &funcPointer, asDWORD callConv, void *auxiliary=0)=0

Registers a global function.

asIScriptEngine::SetDefaultNamespace

virtual int SetDefaultNamespace(const char *nameSpace)=0

Sets the current default namespace for registrations and searches.

If desired nested namespaces can also be used by separating them with the scoping token, ::, e.g. SetDefaultNamespace("outer::inner");

Finding entities in namespaces

As namespaces allow multiple declarations with the same signature, it is necessary to specify in which namespace a search for an entity is to be done. This is also done with the SetDefaultNamespace method. This applies to both the engine and the module interfaces.

void FindFuncInNamespace(asIScriptModule *module)

{

  int r;

  // Look for the function in the namespace, i.e. myspace::myfunc

  r = module->SetDefaultNamespace("myspace"); assert( r >= 0 );

  asIScriptFunction *func1 = module->GetFunctionByName("myfunc");

  // When searching for a matching declaration the default namespace is also

  // used unless an explicit namespace is given in the declaration itself.

  asIScriptFunction *funcA = module->GetFunctionByDecl("void myfunc()");

  asIScriptFunction *funcB = module->GetFunctionByDecl("void myspace::myfunc()");

  assert( funcA == funcB );

}

asIScriptFunction

The interface for a script function description.

Definition: angelscript.h:4000

asIScriptModule

The interface to the script modules.

Definition: angelscript.h:2232

asIScriptModule::GetFunctionByDecl

virtual asIScriptFunction * GetFunctionByDecl(const char *decl) const =0

Returns the function by its declaration.

asIScriptModule::SetDefaultNamespace

virtual int SetDefaultNamespace(const char *nameSpace)=0

Sets the default namespace that should be used in the following calls.

asIScriptModule::GetFunctionByName

virtual asIScriptFunction * GetFunctionByName(const char *name) const =0

Returns the function by its name.

Generated on Sun May 21 2023 10:12:40 for AngelScript by  1.9.6


code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen 1.9.6"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>AngelScript: Pre-compiled byte code</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>

<link href="navtree.css" rel="stylesheet" type="text/css"/>

<link href="search/search.css" rel="stylesheet" type="text/css"/>

<link href="doxygen.css" rel="stylesheet" type="text/css" />

AngelScript

          <input type="text" id="MSearchField" value="" placeholder="Search" accesskey="S"
               onfocus="searchBox.OnSearchFieldFocus(true)"
               onblur="searchBox.OnSearchFieldFocus(false)"
               onkeyup="searchBox.OnSearchFieldChange(event)"/>

Loading...

Searching...

No Matches

Pre-compiled byte code

Sometimes it may be useful to use pre-compiled bytecode instead of re-building the scripts every time a script is loaded. This can for example reduce load times, or if scripts are transferred over the net, it can reduce the transfer time for large scripts.

To save already compiled code you should call the SaveByteCode method on the asIScriptModule, passing in a pointer to the object that implements the asIBinaryStream interface. The script module will then push all the bytecode and related data onto this object with the Write method.

When loading the pre-compiled bytecode you should first get an instance of the asIScriptModule, just as if compiling a new script. Instead of calling AddScriptSection followed by Build you will call the LoadByteCode method, passing it a pointer to the binary stream object. The script module will then pull all the bytecode and related data from that object with the Read method to reconstruct the script module.

Here's a very simple example of how to implement the asIBinaryStream interface:

class CBytecodeStream : public asIBinaryStream

{

public:

  CBytecodeStream(FILE *fp) : f(fp) {}

  void Write(const void *ptr, asUINT size)

  {

    if( size == 0 ) return;

    fwrite(ptr, size, 1, f);

  }

  void Read(void *ptr, asUINT size)

  {

    if( size == 0 ) return;

    fread(ptr, size, 1, f);

  }

protected:

  FILE *f;

};

asUINT

unsigned int asUINT

32 bit unsigned integer

Definition: angelscript.h:610

asIBinaryStream

A binary stream interface.

Definition: angelscript.h:4231

asIBinaryStream::Write

virtual int Write(const void *ptr, asUINT size)=0

Write size bytes to the stream from the memory pointed to by ptr.

asIBinaryStream::Read

virtual int Read(void *ptr, asUINT size)=0

Read size bytes from the stream into the memory pointed to by ptr.

See also

Generic compiler

Things to remember

All the objects, methods, properties, etc, used when compiling the bytecode must also be registered when loading the precompiled bytecode, otherwise the load will fail when not finding the correct entity.

The script engine doesn't perform validation of the pre-compiled bytecode, so the application is responsible for authenticating the source. If this authentication isn't performed by the application you'll have potential security risks in your application as the bytecode may have been manually manipulated to perform otherwise illegal tasks.

If the application that compiles the script code is separate from the application that will execute them, then you may register the functions and methods with null pointers so you don't need to actually implement all of them. Object properties must be registered with different offsets so that they can be differentiated by the bytecode loader. You should also set the engine property asEP_INIT_GLOBAL_VARS_AFTER_BUILD to false with asIScriptEngine::SetEngineProperty, so the script engine doesn't attempt to initialize the global variables after the script has been built. The helper add-on has a couple of functions to facilitate building an offline compiler.

If the application is not going to compile scripts from source code, but only load pre-compiled scripts, then it may be beneficial to compile the library with the AS_NO_COMPILER define so as to reduce the size of the executable.

To reduce the size of the saved bytecode you can strip the debug information from the code. This will skip saving information that is not necessary for executing the scripts, such as the name of script sections, line numbers, and the name and type of local variables.

If the scripts use shared entities then the size of the saved bytecode can be further reduced if the entities are declared as external. To allow the offline compiler to compile scripts with external shared entities, the offline compiler must first build another module with all the shared entities that should be allowed to be declared as external.

The saved bytecode is platform independent with only a couple of exceptions that is unlikely to affect application developers. The exceptions are 1) difference in size of primitive types (e.g. bool on older Mac PPC platforms) and 2) CPU floating point representation (i.e. IEEE 754 versus non-IEEE 754). Differences in pointer size (32bit vs 64bit), CPU endianess, different size of registered types and offsets of registered properties are all handled to provide platform independence.

The saved bytecode is not guaranteed to be compatible for different versions of the AngelScript library so make sure you use the same library version when saving the bytecode that will be used when loading it.

Generated on Sun May 21 2023 10:12:40 for AngelScript by  1.9.6


code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen 1.9.6"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>AngelScript: Reflection</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>

<link href="navtree.css" rel="stylesheet" type="text/css"/>

<link href="search/search.css" rel="stylesheet" type="text/css"/>

<link href="doxygen.css" rel="stylesheet" type="text/css" />

AngelScript

          <input type="text" id="MSearchField" value="" placeholder="Search" accesskey="S"
               onfocus="searchBox.OnSearchFieldFocus(true)"
               onblur="searchBox.OnSearchFieldFocus(false)"
               onkeyup="searchBox.OnSearchFieldChange(event)"/>

Loading...

Searching...

No Matches

Reflection

While the script language doesn't offer built-in functionality for reflection, the application interface do provide all the necessary methods to enumerate everything within the scripts.

The article on debugging shows a little of these interfaces with the focus on debugging rather than reflection. The article on dynamic compilations shows another aspect where scripts may be partially modified at runtime.

The following will do an overview of the available methods for enumerating the various entities in a script.

Enumerating variables and properties

Global variables in a script module are enumerated with the interface asIScriptModule. Specifically the methods GetGlobalVarCount and GetGlobalVar. GetGlobalVarIndexByName and GetGlobalVarIndexByDecl can be used if the name and/or declaration of the desired variable is known before hand. To inspect or even modify the value of the global variable the method GetAddressOfGlobalVar should be used.

The engine interface asIScriptEngine has a similar set of methods for enumerating application registered global properties, i.e. GetGlobalPropertyCount, GetGlobalPropertyByIndex, GetGlobalPropertyIndexByName, and GetGlobalPropertyIndexByDecl.

Member properties of classes are accessed through the asIScriptObject interface for live object instances, and asITypeInfo interface for inspecting the class declarations without any live object instance.

Local variables within functions can also be enumerated as long as the script has been compiled with debug information. These are then enumerated through the asIScriptFunction interface for inspecting the declarations, and directly through asIScriptContext for inspecting and/or modifying them on the stack.

Enumerating functions and methods

Global functions in a script are enumerated with the interface asIScriptModule, using the methods GetFunctionCount, GetFunctionByIndex, GetFunctionByName, and GetFunctionByDecl.

The engine interface asIScriptEngine also exposes methods for enumerating application registered functions in a similar manner.

To enumerate methods of classes the interface asITypeInfo should be used.

Enumerating types

asIScriptModule is of course used to enumerate the types declared in the scripts too. The methods GetObjectTypeCount, GetObjectTypeByIndex, and GetTypeInfoByName are for enumerating classes and interfaces. The methods GetEnumCount and GetEnumByIndex are for enumerating enums.

The asIScriptEngine interface has near identical methods for enumerating the application registered types.

Many of the above methods return values called type ids that describes the type of the respective variable, property, or function argument. In many cases the type id can be directly inspected as a bitfield to get necessary information on the what the type is. The lower bits are just a sequence number where the first 12 numbers represents the built-in primitives, and anything higher represents either application registered types or script declared types. The higher bits indicate if the type represents a primitive, object, or handle. Use the flags in asETypeIdFlags to do the necessary verifications on the type id.

For type ids that represent object types it may be necessary to obtain the asITypeInfo instance to get further information on what the type is. The method GetTypeInfoById is used to do this translate from type id to asITypeInfo.

Generated on Sun May 21 2023 10:12:40 for AngelScript by  1.9.6


code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen 1.9.6"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>AngelScript: Registering a scoped reference type</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>

<link href="navtree.css" rel="stylesheet" type="text/css"/>

<link href="search/search.css" rel="stylesheet" type="text/css"/>

<link href="doxygen.css" rel="stylesheet" type="text/css" />

AngelScript

          <input type="text" id="MSearchField" value="" placeholder="Search" accesskey="S"
               onfocus="searchBox.OnSearchFieldFocus(true)"
               onblur="searchBox.OnSearchFieldFocus(false)"
               onkeyup="searchBox.OnSearchFieldChange(event)"/>

Loading...

Searching...

No Matches

Registering a scoped reference type

Some C++ value types have special requirements for the memory where they are located, e.g. specific alignment needs, or memory pooling. Since AngelScript doesn't provide that much control over where and how value types are allocated, they must be registered as reference types. In this case you'd register the type as a scoped reference type.

A scoped reference type will have the life time controlled by the scope of the variable that instanciate it, i.e. as soon as the variable goes out of scope the instance is destroyed. This means that the type doesn't permit handles to be taken for the type.

A scoped reference type requires only that the release behaviour is registered. The addref behaviour is not permitted. If the factory behaviour is not registered the script will not be able to instanciate objects of this type, but it can still receive them as parameters from the application.

Since no handles can be taken for the object type, there is no need to keep track of the number of references held to the object. This means that the release behaviour should simply destroy and deallocate the object as soon as it's called.

scoped *Scoped_Factory()

{

  return new scoped;

}

void Scoped_Release(scoped *s)

{

  if( s ) delete s;

}

// Registering a scoped reference type

r = engine->RegisterObjectType("scoped", 0, asOBJ_REF | asOBJ_SCOPED); assert( r >= 0 );

r = engine->RegisterObjectBehaviour("scoped", asBEHAVE_FACTORY, "scoped @f()", asFUNCTION(Scoped_Factory), asCALL_CDECL); assert( r >= 0 );

r = engine->RegisterObjectBehaviour("scoped", asBEHAVE_RELEASE, "void f()", asFUNCTION(Scoped_Release), asCALL_CDECL_OBJLAST); assert( r >= 0 );

asCALL_CDECL

@ asCALL_CDECL

A cdecl function.

Definition: angelscript.h:230

asCALL_CDECL_OBJLAST

@ asCALL_CDECL_OBJLAST

A cdecl function that takes the object pointer as the last parameter.

Definition: angelscript.h:238

asFUNCTION

#define asFUNCTION(f)

Returns an asSFuncPtr representing the function specified by the name.

Definition: angelscript.h:685

asBEHAVE_FACTORY

@ asBEHAVE_FACTORY

Factory.

Definition: angelscript.h:366

asBEHAVE_RELEASE

@ asBEHAVE_RELEASE

Release.

Definition: angelscript.h:372

asOBJ_REF

@ asOBJ_REF

A reference type.

Definition: angelscript.h:254

asOBJ_SCOPED

@ asOBJ_SCOPED

The life time of objects of this type are controlled by the scope of the variable....

Definition: angelscript.h:264

Unfortunately any function that either takes or returns the type by value in C++ must be wrapped in order to permit AngelScript to manage the life time of the values.

Here's an example of a function that takes a value and returns another and the corresponding wrapper.

scoped Foo(scoped a)

{

  scoped b;

  return b;

}

scoped *Foo_wrapper(const scoped &a)

{

  return new scoped(Foo(a));

}

// Registering the function

r = engine->RegisterGlobalFunction("scoped @Foo(const scoped &in)", asFUNCTION(Foo_wrapper), asCALL_CDECL); assert( r >= 0 );

Observe how the function is registered to return the scoped value by handle even though the scoped types really don't support handles. This is done because AngelScript will call Release on the returned instance after it is done with the value it received.

See also

Registering a reference type

Generated on Sun May 21 2023 10:12:40 for AngelScript by  1.9.6


code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen 1.9.6"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>AngelScript: Registering a single-reference type</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>

<link href="navtree.css" rel="stylesheet" type="text/css"/>

<link href="search/search.css" rel="stylesheet" type="text/css"/>

<link href="doxygen.css" rel="stylesheet" type="text/css" />

AngelScript

          <input type="text" id="MSearchField" value="" placeholder="Search" accesskey="S"
               onfocus="searchBox.OnSearchFieldFocus(true)"
               onblur="searchBox.OnSearchFieldFocus(false)"
               onkeyup="searchBox.OnSearchFieldChange(event)"/>

Loading...

Searching...

No Matches

Registering a single-reference type

A variant of the uninstanciable reference types is the single-reference type. This is a type that have only 1 reference accessing it, i.e. the script cannot store any extra references to the object during execution. The script is forced to use the reference it receives from the application at the moment the application passes it on to the script.

The reference can be passed to the script through a property, either global or a class member, or it can be returned from an application registered function or class method.

The script engine will not permit declaration of functions that take this type as a parameter, neither as a reference nor as a handle. If that was allowed it would mean that a reference to the instance is placed on the stack, which in turn means that it is no longer a single-reference type.

// Registering the type so that it cannot be instanciated

// by the script, nor allow scripts to store references to the type

r = engine->RegisterObjectType("single", 0, asOBJ_REF | asOBJ_NOHANDLE); assert( r >= 0 );

asOBJ_REF

@ asOBJ_REF

A reference type.

Definition: angelscript.h:254

asOBJ_NOHANDLE

@ asOBJ_NOHANDLE

This reference type doesn't allow handles to be held. Only valid for reference types.

Definition: angelscript.h:262

This sort of type is most useful when you want to have complete control over references to an object, for example so that the application can destroy and recreate objects of the type without having to worry about potential references held by scripts. This allows the application to control when a script has access to an object and it's members.

See also

Registering a reference type

Generated on Sun May 21 2023 10:12:40 for AngelScript by  1.9.6