FixScript Documentation

C API

Prerequisites

The C implementation is currently written for targets where int is 4 bytes and short is 2 bytes. This is true for the usual 32/64 bit targets but may not be true in embedded targets and modifications would be needed. The code also uses the indirect goto feature of GCC and Clang for increasing the performance. However it contains a fallback that uses switches with some performance degradation and noticeable increase in object size.

Temporary roots

To avoid premature garbage collection of temporary values in C code all created arrays, strings, etc. are added into list of temporary roots. This list is cleared only on explicit garbage collection, when calling script function or returning from the handler of a native function.

Types

typedef struct Heap Heap;
The heap contains all the data structures of the scripts as well as the compiled scripts themselves.
typedef struct Script Script;
Reference to specific script. This is to allow to query functions defined in that script. The scripts are owned by Heap and must not be mixed up between heaps or freed manually.
typedef struct { int value; int is_array; } Value;
Value struct contains the integer value and an indication if it's an array reference or a float value. It's preferred to use this type as an opaque value and always use the functions for handling the simple values.
Internal details: The distinction between array reference and floats is made based on the unsigned value, if it's outside the range 1..0x7FFFFF (meaning the exponent is present or the number is negative or zero) then it's outside of valid array references and it's a float value. Note that denormalized numbers are flushed to zero and thus don't collide with array references (the exponent would be stored as 0 in such case). The is_array is either 0 or 1, this allows to directly compare the struct for equivalence. This struct is always passed by value. You can test validity of any reference by comparing value to zero (all references are non-zero).
typedef struct SharedArrayHandle SharedArrayHandle;
Used for referencing shared arrays outside a heap.
typedef void (*HandleFreeFunc)(void *p);
Used for freeing the native handle. Called during garbage collection, it is advised to not trigger garbage collection in the function (directly or indirectly), however there is a safeguard preventing a recursive GC. Should GC occur again the heap will get larger instead (and there is a possibility of getting spurious out of memory errors if the heap was already big enough, also it can overinflate the heap without the ability to shrink because of fragmentation).
typedef void *(*HandleFunc)(Heap *heap, int op, void *p1, void *p2);
Used for handling the native value handle. The operation can be one of the following:
HANDLE_OP_FREE
free the value handle p1 (called during garbage collection, see the note on HandleFreeFunc type)
HANDLE_OP_COPY
create a copy of the value handle p1 or return NULL if copies are not supported (the destination heap pointer is in p2), this operation must be thread-safe as the copying can occur in multiple threads concurrently
HANDLE_OP_COMPARE
compare the value handles p1 and p2, result is negative (if less), zero (if equal) or positive (if greater), the result needs to be casted to intptr_t and then to void *
HANDLE_OP_HASH
return hash of the value handle p1 casted to void * (only low 32 bits are used)
HANDLE_OP_TO_STRING
return newly allocated string representation of the value handle p1 (return NULL for default string representation)
HANDLE_OP_MARK_REFS
called during GC to mark references (using the fixscript_mark_ref function) held on the native side (p1 points to handle)
HANDLE_OP_COPY_REFS
called during deep cloning to copy references (using the fixscript_copy_ref function) held on the native side (p1 points to handle and p2 to copy context), this operation must be thread-safe as the copying can occur in multiple threads concurrently
note: in some circumstances it won't be called for a deep copy (eg. in case of an error during cloning), handle such cases gracefully
typedef Script *(*LoadScriptFunc)(Heap *heap, const char *fname, Value *error, void *data);
Used for providing a callback to load other scripts when using the import directive.
typedef Value (*NativeFunc)(Heap *heap, Value *error, int num_params, Value *params, void *data);
Used for providing native functions. Use error to return second return value (usually used for errors, initialized to zero).

Error codes

Note: Error constants are negative values (success code is zero).

FIXSCRIPT_SUCCESS
No error occurred.
FIXSCRIPT_ERR_INVALID_ACCESS
The value is an invalid reference.
FIXSCRIPT_ERR_INVALID_BYTE_ARRAY
The value is invalid byte array (contains values outside the range of byte or contains non-integer values).
FIXSCRIPT_ERR_INVALID_SHORT_ARRAY
The value is invalid short array (contains values outside the range of short or contains non-integer values).
FIXSCRIPT_ERR_INVALID_NULL_STRING
The string contains zero character (reported only when the string is retrieved without getting the length and thus relying on the zero character termination).
FIXSCRIPT_ERR_STATIC_WRITE
The value is a string literal and can't be modified.
FIXSCRIPT_ERR_OUT_OF_BOUNDS
The index or range is out of the bounds.
FIXSCRIPT_ERR_OUT_OF_MEMORY
Tried to allocate more memory than available or an integer overflow occurred during computation of how much to allocate.
FIXSCRIPT_ERR_INVALID_SHARED_ARRAY_OPERATION
Tried to change length or element size on a shared array, or put a reference or native handle to such array.
FIXSCRIPT_ERR_KEY_NOT_FOUND
The requested key isn't present in the hash table.
FIXSCRIPT_ERR_RECURSION_LIMIT
The recursion limit was exceeded.
FIXSCRIPT_ERR_UNSERIALIZABLE_REF
An unserializable reference occurred (for example native handle).
FIXSCRIPT_ERR_BAD_FORMAT
Bad format or data too short (when unserializing).
FIXSCRIPT_ERR_FUNC_REF_LOAD_ERROR
Script load error during resolving of function reference (typically native function not defined or script wasn't loaded when resolving within already loaded code only).
FIXSCRIPT_ERR_NESTED_WEAKREF
Nested weak references are not allowed (referencing directly another weak reference or having a key as a weak reference).

Functions

Simple values handling

Note: these functions are all inlined.

Value fixscript_int(int value);
Makes a 32-bit integer value.
Value fixscript_float(float value);
Makes a 32-bit float value. The denormals are flushed to zero.
int fixscript_is_int(Value value);
Returns whether the value is a 32-bit integer.
int fixscript_is_float(Value value);
Returns whether the value is a 32-bit float.
int fixscript_get_int(Value value);
Returns the integer representation of the value. If the value is a float or an array reference it will return the bitwise representation.
float fixscript_get_float(Value value);
Returns the float representation of the value. Gives incorrect values if the value wasn't a float.

Heap management

Heap *fixscript_create_heap();
Creates a new heap, this contains the whole context of script execution. You can use only different heaps in different threads concurrently.
void fixscript_free_heap(Heap *heap);
Frees all the resources for given heap.
void fixscript_collect_heap(Heap *heap);
Runs garbage collection for given heap. It also clears the temporary roots, you need to store references directly in the heap or increase their external reference count to persist them.
long long fixscript_heap_size(Heap *heap);
Traverses the heap to obtain the overall size of the heap in bytes. This includes overheads from reserved memory for array growths and struct paddings, but doesn't include overhead of the underlying malloc implementation.
void fixscript_adjust_heap_size(Heap *heap, long long relative_change);
Adjusts tracked memory footprint of the heap. This is used for making sure the garbage collection knows about externally allocated memory (eg. when using native handles).
void fixscript_ref(Heap *heap, Value value);
Increases the number of external references, preventing the value from being garbage collected.
Note: Beware of reference cycles that will result into inability to reclaim memory. Often this is when the native handle needs to hold into some heap data which contains reference back to the native handle, creating a cycle. This is best solved by using a value handle and using the HANDLE_OP_MARK_REFS operation to mark any such references. Alternativelly you can use weak references on the native side (simply by not using fixscript_ref) and wrapping the native handle into an object (containing array of references that need to be retained) and referencing to that object instead of the handle.
void fixscript_unref(Heap *heap, Value value);
Decreases the number of external references, once at zero the value can be garbage collected.
void fixscript_register_cleanup(Heap *heap, HandleFreeFunc free_func, void *data);
Registers a cleanup function that will be called when the heap is freed.
void fixscript_register_heap_key(volatile int *key);
Registers a heap key if it's not registered already, this is done atomically. This is a global operation, and should be used for static initialization only.
int fixscript_set_heap_data(Heap *heap, int key, void *data, HandleFreeFunc free_func);
Sets per-heap value with given key, freeing the previous value if present.
void *fixscript_get_heap_data(Heap *heap, int key);
Returns per-heap value for given key.

Execution time limit

void fixscript_set_time_limit(Heap *heap, int limit);
Sets a time limit in milliseconds for execution of the scripts. The time limit starts counting after calling of this function. This function must be called before any scripts are loaded as they need to be instrumented with the time checks. To disable the time limit, pass -1 as a limit (avoid passing 0 as that would remove instrumentation for newly compiled scripts).
int fixscript_get_remaining_time(Heap *heap);
Returns the remaining time in milliseconds (capped to 0) for execution of the scripts. Returns -1 when the time limit is not set. The 0 is also returned when the heap is stopped asynchronously from another thread.
void fixscript_stop_execution(Heap *heap);
Stops the running script asynchronously from another thread. The heap must be using the time limit feature (use -1 for no actual time limit). To be able to run code again the time limit must be set again (it will reset the stop execution flag).

Reference handling in value handles

void fixscript_mark_ref(Heap *heap, Value value);
Marks a reference held on the native side. Call this only during the HANDLE_OP_MARK_REFS operation of the value handles.
Value fixscript_copy_ref(void *ctx, Value value);
Creates a copy of the reference, using the copy context that is available during HANDLE_OP_COPY_REFS operation of the value handles.

Array access

Value fixscript_create_array(Heap *heap, int len);
Creates a new array of given length. The reference is added into temporary roots to prevent it from premature deallocation.
Value fixscript_create_byte_array(Heap *heap, const char *buf, int len);
Creates a new byte array with given content. The reference is added into temporary roots to prevent it from premature deallocation.
int fixscript_set_array_length(Heap *heap, Value arr_val, int len);
Sets length of given array, expanding the capacity when needed. If the length is bigger than currently is the space is filled with zeros. Does nothing in case the array reference is invalid. Returns error code.
int fixscript_get_array_length(Heap *heap, Value arr_val, int *len);
Returns length of the array or hash in the output parameter. Returns error code.
int fixscript_is_array(Heap *heap, Value arr_val);
Returns whether the given value is valid reference to an array.
int fixscript_set_array_elem(Heap *heap, Value arr_val, int idx, Value value);
Sets value in the array at given index. Returns error code.
int fixscript_get_array_elem(Heap *heap, Value arr_val, int idx, Value *value);
Retrieves value in the array at given index. Returns error code.
int fixscript_append_array_elem(Heap *heap, Value arr_val, Value value);
Appends value in to the array. Returns error code.
int fixscript_get_array_range(Heap *heap, Value arr_val, int off, int len, Value *values);
Retrieves values from the array in specified range. Returns error code.
int fixscript_set_array_range(Heap *heap, Value arr_val, int off, int len, Value *values);
Stores values to the array in specified range. Returns error code.
int fixscript_get_array_bytes(Heap *heap, Value arr_val, int off, int len, char *bytes);
Retrieves byte values from the array in specified range. Returns error code.
int fixscript_set_array_bytes(Heap *heap, Value arr_val, int off, int len, char *bytes);
Stores byte values to the array in specified range. Returns error code.
int fixscript_copy_array(Heap *heap, Value dest, int dest_off, Value src, int src_off, int count);
Copies given amount of values between different arrays or within the same array. Returns error code.
int fixscript_lock_array(Heap *heap, Value arr_val, int off, int len, void **data, int elem_size, int read);
Obtains direct pointer access to an array (if it's a shared array) or allocates a temporary buffer and optionally copies the data to it from the array. You must unlock the access by calling fixscript_unlock_array with the same parameters (length can be made smaller). The reference is added into temporary roots to prevent it from premature deallocation.
void fixscript_unlock_array(Heap *heap, Value arr_val, int off, int len, void **data, int elem_size, int write);
Finishes direct pointer access to an array (if it's a shared array) or optionally copies the data from the temporary buffer back into the array and frees the temporary buffer if used.

Shared arrays

Value fixscript_create_shared_array(Heap *heap, int len, int elem_size);
Creates a new shared array. The reference is added into temporary roots to prevent it from premature deallocation.
Value fixscript_create_or_get_shared_array(Heap *heap, int type, void *ptr, int len, int elem_size, HandleFreeFunc free_func, void *data, int *created);
Creates a new shared array with user provided pointer or gets an existing instance. The pointer must be aligned to element size. The reference is added into temporary roots to prevent it from premature deallocation. Optionally you can retrieve whether the shared array was created or an existing instance was returned instead.
Note: The shared arrays are matched based on type, pointer, length, element size and data, therefore if any of these are unique (eg. resulting from a new allocation), the array is always created as new and there is no need to check for creation status.
void fixscript_ref_shared_array(SharedArrayHandle *sah);
Increases the number of references, preventing the shared array from being freed prematurely.
void fixscript_unref_shared_array(SharedArrayHandle *sah);
Decreases the number of references, once at zero the shared array is freed.
int fixscript_get_shared_array_reference_count(SharedArrayHandle *sah);
Returns the value of reference counter for given shared array.
SharedArrayHandle *fixscript_get_shared_array_handle(Heap *heap, Value arr_val, int expected_type, int *actual_type);
Obtains direct reference to shared array for usage outside of heap.
void *fixscript_get_shared_array_handle_data(SharedArrayHandle *sah, int *len, int *elem_size, void **data, int expected_type, int *actual_type);
Returns information about a shared array. All output parameters are optional.
Value fixscript_get_shared_array_value(Heap *heap, SharedArrayHandle *sah);
Creates or returns an existing reference to given shared array.
Value fixscript_get_shared_array(Heap *heap, int type, void *ptr, int len, int elem_size, void *data);
Returns an existing reference to a shared array, returns null in case the reference is not present.
void *fixscript_get_shared_array_data(Heap *heap, Value arr_val, int *len, int *elem_size, void **data, int expected_type, int *actual_type);
Returns information about a shared array. All output parameters are optional.

String access

Value fixscript_create_string(Heap *heap, const char *s, int len);
Creates a new string from UTF-8 encoded characters of given length. Any incorrectly encoded character is replaced by the replacement character (U+FFFD). If the length is negative the length is computed automatically. The reference is added into temporary roots to prevent it from premature deallocation.
Value fixscript_create_string_utf16(Heap *heap, const unsigned short *s, int len);
A variant of fixscript_create_string that uses UTF-16 encoded characters. Any invalid surrogate pair encoding is replaced by the replacement character (U+FFFD).
int fixscript_get_string(Heap *heap, Value str_val, int str_off, int str_len, char **str, int *len_out);
Returns the string contents as UTF-8 encoded characters (pass negative value for length to use the whole string). Any invalid character (outside of the valid range or within the surrogate pairs range) is replaced by the replacement character (U+FFFD). The string is always zero-terminated and you can optionally obtain the string length. The string must be deallocated by the caller using the free function. If the output length is not obtained and the string contains zero character it returns an error instead of returning shortened string. Returns error code.
int fixscript_get_string_utf16(Heap *heap, Value str_val, int str_off, int str_len, unsigned short **str, int *len_out);
A variant of fixscript_get_string that uses UTF-16 encoded characters.
int fixscript_is_string(Heap *heap, Value str_val);
Returns whether the given value is a valid reference to a string.

Hash access

Value fixscript_create_hash(Heap *heap);
Creates a new hash. The reference is added into temporary roots to prevent it from premature deallocation.
int fixscript_is_hash(Heap *heap, Value hash_val);
Returns whether the given value is valid reference to a hash.
int fixscript_set_hash_elem(Heap *heap, Value hash_val, Value key_val, Value value_val);
Sets value in the hash for given key. Returns error code.
int fixscript_get_hash_elem(Heap *heap, Value hash_val, Value key_val, Value *value_val);
Retrieves value in the hash for given key. Returns error code.
int fixscript_remove_hash_elem(Heap *heap, Value hash_val, Value key_val, Value *value_val);
Removes entry for given key. Returns error code.
int fixscript_clear_hash(Heap *heap, Value hash_val);
Clears all entries in the hash. Returns error code.
int fixscript_iter_hash(Heap *heap, Value hash_val, Value *key_val, Value *value_val, int *pos);
Retrieves the next key and value from the hash. The iteration position must be initialized to zero before retrieving first entry. Returns non-zero if entry was retrieved or zero when there are no more entries present.

Native handles

Value fixscript_create_handle(Heap *heap, int type, void *handle, HandleFreeFunc free_func);
Creates a new native handle. The type must not be negative. free_func is optional.
Value fixscript_create_value_handle(Heap *heap, int type, void *handle, HandleFunc handle_func);
Creates a new native value handle (supports comparing by value and cloning). The type must not be negative.
void *fixscript_get_handle(Heap *heap, Value handle_val, int expected_type, int *actual_type);
Obtains native handle with given type (or negative value to disable check), optionally you can get the type (set to -1 in case the handle is invalid). Returns NULL on error (invalid value or different type).
void fixscript_register_handle_types(volatile int *offset, int count);
Registers given number of native handle types if it's not registered already, this is done atomically. This is a global operation, and should be used for static initialization only. The counts are counted from INT_MAX and allocated by decrementing.

Weak references

int fixscript_create_weak_ref(Heap *heap, Value value, Value *container, Value *key, Value *weak_ref);
Creates a new weak reference (or already existing instance). Optionally you can pass a container (hash table or array) for automatic action to occur once the target object is garbage collected. In case of hash tables either the weak reference or provided key is removed. For arrays either the weak reference or provided key is appended to it. Be sure to periodically empty the array to prevent memory leaks.
Note: weak references can't reference directly other weak references (including the key).
int fixscript_get_weak_ref(Heap *heap, Value weak_ref, Value *value);
Obtains the reference value for given weak reference.
int fixscript_is_weak_ref(Heap *heap, Value weak_ref);
Returns whether the given value is weak reference.

Error handling

const char *fixscript_get_error_msg(int error_code);
Returns error message as a constant string, returns NULL for success or unknown error codes.
Value fixscript_create_error(Heap *heap, Value msg);
Creates error value (with the same format as the builtin error function) with given value for message.
Value fixscript_create_error_string(Heap *heap, const char *s);
Creates error value (with the same format as the builtin error function) with given error message.
Value fixscript_error(Heap *heap, Value *error, int code);
Creates error value (with the same format as the builtin error function) with error message that corresponds to given error code. For convenience it returns zero and stores the error into the provided parameter.
const char *fixscript_get_compiler_error(Heap *heap, Value error);
Returns a string representation of a compiler error. It also handles simplifying of syntax errors produced by token processors. The returned string is allocated internally and the previous pointer is freed for each invocation of this function.

Value inspection

int fixscript_dump_value(Heap *heap, Value value, int newlines);
Pretty prints the given value to standard error stream (stderr).
int fixscript_to_string(Heap *heap, Value value, int newlines, char **str, int *len);
Pretty prints the given value to newly allocated UTF-8 string (zero-terminated if length is not obtained). Returns error code.

Cloning & serialization

int fixscript_clone(Heap *heap, Value value, int deep, Value *clone);
Clones given value. Returns error code.
int fixscript_clone_between(Heap *dest, Heap *src, Value value, Value *clone, LoadScriptFunc load_func, void *load_data, Value *error);
Clones given value between different (or same) heaps. The optional load function is to load the scripts for cloned function references, otherwise the references will be unresolved until cloned again with the load function provided. You can pass the fixscript_resolve_existing function to allow references to be resolved in already loaded code without loading any other code. Returns error code. You can optionally obtain more precise error value when script load fails (if resolving is used). The error is stored in the destination heap, if no such detailed error is produced use the standard error codes. It is permitted to clone to multiple threads concurrently as long as the source heap is not used for anything else.
int fixscript_serialize(Heap *heap, Value *buf_val, Value value);
Serializes given value to byte array value (created when not provided).
int fixscript_unserialize(Heap *heap, Value buf_val, int *off, int len, Value *value);
Unserializes value from given byte array value. The provided offset is adjusted with the resulting position after the operation. If the length is passed as negative it will allow extra data after the serialized data (possibily another serialized data).
int fixscript_serialize_to_array(Heap *heap, char **buf, int *len_out, Value value);
Serializes given value to native byte array. If length is not obtained the serialized form is prepended by the size of the serialized data.
int fixscript_unserialize_from_array(Heap *heap, const char *buf, int *off_out, int len, Value *value);
Unserializes value from given native byte array. If length is negative it is read from the beginning of the serialized data (must be outputed in that form). Optionally you can retrieve offset after the serialized data, in that case it will allow extra data after the serialized data (possibly another serialized data).

Script loading & running

Script *fixscript_load(Heap *heap, const char *src, const char *fname, Value *error, LoadScriptFunc load_func, void *load_data);
Loads given script source under provided file name. On error it returns NULL and the error value is outputed (optional). Passing of load_func is optional (import statement will not work in such case).
Script *fixscript_load_file(Heap *heap, const char *name, Value *error, const char *dirname);
A variant of fixscript_load that loads scripts from file system.
Script *fixscript_load_embed(Heap *heap, const char *name, Value *error, const char * const * const embed_files);
A variant of fixscript_load that loads scripts from embedded static array as produced by the fixembed tool.
Script *fixscript_reload(Heap *heap, const char *src, const char *fname, Value *error, LoadScriptFunc load_func, void *load_data);
Reloads given script. The newly loaded version of the script replaces existing functions so new calls will go to the updated script. On error it returns NULL and the error value is outputed (optional). Passing of load_func is optional (import statement will not work in such case).
Script *fixscript_resolve_existing(Heap *heap, const char *name, Value *error, void *data);
Script loading function that always returns an error, used for enabling function reference resolving within already loaded scripts when cloning between heaps.
Script *fixscript_get(Heap *heap, const char *fname);
Returns script for given file name (or NULL if not found).
char *fixscript_get_script_name(Heap *heap, Script *script);
Returns newly allocated script name for given script (or NULL if no script is provided).
Value fixscript_get_function(Heap *heap, Script *script, const char *func_name);
Returns function handle for function with given name (must provide parameter count as part of the name). Returns zero in case the script is not provided.
int fixscript_get_function_list(Heap *heap, Script *script, char ***functions_out, int *count_out);
Obtains a list of functions in given script as two output parameters. Returns an error code. The individual strings and the list must be freed using the free function.
int fixscript_get_function_name(Heap *heap, Value func_val, char **script_name_out, char **func_name_out, int *num_params_out);
Obtains script name, function name and number of parameters for given function value (all obtained values are optional). Returns error code.
Value fixscript_run(Heap *heap, Script *script, const char *func_name, Value *error, ...);
Runs function with given name (including parameter count in the name) and (optionally) obtains the error value.
Value fixscript_run_args(Heap *heap, Script *script, const char *func_name, Value *error, Value *args);
A variant of fixscript_run with arguments passed as an array.
Value fixscript_call(Heap *heap, Value func, int num_params, Value *error, ...);
Calls function using given function value and (optionally) obtains the error value.
Value fixscript_call_args(Heap *heap, Value func, int num_params, Value *error, Value *args);
A variant of fixscript_call with arguments passed as an array.
void fixscript_register_native_func(Heap *heap, const char *name, NativeFunc func, void *data);
Registers (or replaces) native function with given name.
NativeFunc fixscript_get_native_func(Heap *heap, const char *name, void **data);
Returns registered native function and the associated data (optional).

Bytecode & heap inspection

char *fixscript_dump_code(Heap *heap, Script *script, const char *func_name);
Returns newly allocated string representation of bytecode for given function (or all functions if not provided).
char *fixscript_dump_heap(Heap *heap);
Returns newly allocated string representation of heap values.