From 5750d6db750a4f9ab07b9ece0a7d2531385a90dd Mon Sep 17 00:00:00 2001 From: Michael Hunter Date: Fri, 2 Dec 2022 06:36:17 +1100 Subject: [PATCH] #51 Private heaps and MVM_EXPORT --- dist-c/microvium.c | 28 ++++++-- dist-c/microvium.h | 66 ++++++++++--------- dist-c/microvium_port_example.h | 13 ++-- native-vm/microvium.c | 15 +++-- native-vm/microvium.h | 66 ++++++++++--------- native-vm/microvium_internals.h | 13 +++- native-vm/microvium_port_example.h | 13 ++-- .../code/microvium/microvium.c | 28 ++++++-- .../code/microvium/microvium.h | 66 ++++++++++--------- test/getting-started/code/microvium_port.h | 13 ++-- 10 files changed, 195 insertions(+), 126 deletions(-) diff --git a/dist-c/microvium.c b/dist-c/microvium.c index 4a59f00f..a5c9fc04 100644 --- a/dist-c/microvium.c +++ b/dist-c/microvium.c @@ -1365,7 +1365,7 @@ static uint16_t getBucketOffsetEnd(TsBucket* bucket); static uint16_t getSectionSize(VM* vm, mvm_TeBytecodeSection section); static Value vm_intToStr(VM* vm, int32_t i); static Value vm_newStringFromCStrNT(VM* vm, const char* s); -static TeError vm_validatePortFileMacros(MVM_LONG_PTR_TYPE lpBytecode, mvm_TsBytecodeHeader* pHeader); +static TeError vm_validatePortFileMacros(MVM_LONG_PTR_TYPE lpBytecode, mvm_TsBytecodeHeader* pHeader, void* context); static LongPtr vm_toStringUtf8_long(VM* vm, Value value, size_t* out_sizeBytes); static LongPtr vm_findScopedVariable(VM* vm, uint16_t index); static Value vm_cloneContainer(VM* vm, Value* pArr); @@ -1516,6 +1516,17 @@ static int32_t mvm_float64ToInt32(MVM_FLOAT64 value); #define MVM_PORT_INT32_OVERFLOW_CHECKS 1 #endif +// Backwards compatibility with non-contextual malloc +#ifndef MVM_CONTEXTUAL_MALLOC +#define MVM_CONTEXTUAL_MALLOC(size, context) MVM_MALLOC(size) +#endif + +// Backwards compatibility with non-contextual free +#ifndef MVM_CONTEXTUAL_FREE +#define MVM_CONTEXTUAL_FREE(size, context) MVM_FREE(size) +#endif + + @@ -4076,7 +4087,7 @@ TeError mvm_restore(mvm_VM** result, MVM_LONG_PTR_TYPE lpBytecode, size_t byteco return MVM_E_BYTECODE_REQUIRES_FLOAT_SUPPORT; } - err = vm_validatePortFileMacros(lpBytecode, &header); + err = vm_validatePortFileMacros(lpBytecode, &header, context); if (err) return err; uint16_t importTableSize = header.sectionOffsets[vm_sectionAfter(vm, BCS_IMPORT_TABLE)] - header.sectionOffsets[BCS_IMPORT_TABLE]; @@ -7674,7 +7685,7 @@ void mvm_dbg_setBreakpointCallback(mvm_VM* vm, mvm_TfBreakpointCallback cb) { * point to actual bytecode, whereas pHeader should point to a local copy that's * been validated. */ -static TeError vm_validatePortFileMacros(MVM_LONG_PTR_TYPE lpBytecode, mvm_TsBytecodeHeader* pHeader) { +static TeError vm_validatePortFileMacros(MVM_LONG_PTR_TYPE lpBytecode, mvm_TsBytecodeHeader* pHeader, void* context) { uint32_t x1 = 0x12345678; uint32_t x2 = 0x12345678; uint32_t x3 = 0x87654321; @@ -7718,8 +7729,8 @@ static TeError vm_validatePortFileMacros(MVM_LONG_PTR_TYPE lpBytecode, mvm_TsByt if ((!MVM_NATIVE_POINTER_IS_16_BIT) && (sizeof(void*) == 2)) return MVM_E_EXPECTED_POINTER_SIZE_NOT_TO_BE_16_BIT; #if MVM_USE_SINGLE_RAM_PAGE - void* ptr = MVM_MALLOC(2); - MVM_FREE(ptr); + void* ptr = MVM_CONTEXTUAL_MALLOC(2, context); + MVM_CONTEXTUAL_FREE(ptr, context); if ((intptr_t)ptr - (intptr_t)MVM_RAM_PAGE_ADDR > 0xffff) return MVM_E_MALLOC_NOT_WITHIN_RAM_PAGE; #endif // MVM_USE_SINGLE_RAM_PAGE @@ -7781,7 +7792,7 @@ static TeError vm_newError(VM* vm, TeError err) { } static void* vm_malloc(VM* vm, size_t size) { - void* result = MVM_MALLOC(size); + void* result = MVM_CONTEXTUAL_MALLOC(size, vm->context); #if MVM_SAFE_MODE && MVM_USE_SINGLE_RAM_PAGE // See comment on MVM_RAM_PAGE_ADDR in microvium_port_example.h @@ -7792,12 +7803,15 @@ static void* vm_malloc(VM* vm, size_t size) { // Note: mvm_free frees the VM, while vm_free is the counterpart to vm_malloc static void vm_free(VM* vm, void* ptr) { + // Capture the context before freeing the ptr, since the pointer could be the vm + void* context = vm->context; + #if MVM_SAFE_MODE && MVM_USE_SINGLE_RAM_PAGE // See comment on MVM_RAM_PAGE_ADDR in microvium_port_example.h VM_ASSERT(vm, !ptr || ((intptr_t)ptr - (intptr_t)MVM_RAM_PAGE_ADDR <= 0xFFFF)); #endif - MVM_FREE(ptr); + MVM_CONTEXTUAL_FREE(ptr, context); } static mvm_TeError vm_uint8ArrayNew(VM* vm, Value* slot) { diff --git a/dist-c/microvium.h b/dist-c/microvium.h index b948ea3f..29024d86 100644 --- a/dist-c/microvium.h +++ b/dist-c/microvium.h @@ -90,6 +90,12 @@ typedef enum mvm_TeType { VM_T_END, } mvm_TeType; +// Prefix to attach to exported microvium API functions. If a user doesn't +// specify this, we just set it up as the empty macro. +#ifndef MVM_EXPORT +#define MVM_EXPORT +#endif + typedef struct mvm_VM mvm_VM; typedef mvm_TeError (*mvm_TfHostFunction)(mvm_VM* vm, mvm_HostFunctionID hostFunctionID, mvm_Value* result, mvm_Value* args, uint8_t argCount); @@ -169,12 +175,12 @@ extern "C" { * @param context Any value. The context for a VM can be retrieved later using * `mvm_getContext`. It can be used to attach user-defined data to a VM. */ -mvm_TeError mvm_restore(mvm_VM** result, MVM_LONG_PTR_TYPE snapshotBytecode, size_t bytecodeSize, void* context, mvm_TfResolveImport resolveImport); +MVM_EXPORT mvm_TeError mvm_restore(mvm_VM** result, MVM_LONG_PTR_TYPE snapshotBytecode, size_t bytecodeSize, void* context, mvm_TfResolveImport resolveImport); /** * Free all memory associated with a VM. The VM must not be used again after freeing. */ -void mvm_free(mvm_VM* vm); +MVM_EXPORT void mvm_free(mvm_VM* vm); /** * Call a function in the VM @@ -189,21 +195,21 @@ void mvm_free(mvm_VM* vm); * MVM_E_UNCAUGHT_EXCEPTION and the exception value will be put into * `out_result` */ -mvm_TeError mvm_call(mvm_VM* vm, mvm_Value func, mvm_Value* out_result, mvm_Value* args, uint8_t argCount); +MVM_EXPORT mvm_TeError mvm_call(mvm_VM* vm, mvm_Value func, mvm_Value* out_result, mvm_Value* args, uint8_t argCount); -void* mvm_getContext(mvm_VM* vm); +MVM_EXPORT void* mvm_getContext(mvm_VM* vm); -void mvm_initializeHandle(mvm_VM* vm, mvm_Handle* handle); // Handle must be released by mvm_releaseHandle -void mvm_cloneHandle(mvm_VM* vm, mvm_Handle* target, const mvm_Handle* source); // Target must be released by mvm_releaseHandle -mvm_TeError mvm_releaseHandle(mvm_VM* vm, mvm_Handle* handle); -static inline mvm_Value mvm_handleGet(const mvm_Handle* handle) { return handle->_value; } -static inline void mvm_handleSet(mvm_Handle* handle, mvm_Value value) { handle->_value = value; } +MVM_EXPORT void mvm_initializeHandle(mvm_VM* vm, mvm_Handle* handle); // Handle must be released by mvm_releaseHandle +MVM_EXPORT void mvm_cloneHandle(mvm_VM* vm, mvm_Handle* target, const mvm_Handle* source); // Target must be released by mvm_releaseHandle +MVM_EXPORT mvm_TeError mvm_releaseHandle(mvm_VM* vm, mvm_Handle* handle); +MVM_EXPORT static inline mvm_Value mvm_handleGet(const mvm_Handle* handle) { return handle->_value; } +MVM_EXPORT static inline void mvm_handleSet(mvm_Handle* handle, mvm_Value value) { handle->_value = value; } /** * Roughly like the `typeof` operator in JS, except with distinct values for * null and arrays */ -mvm_TeType mvm_typeOf(mvm_VM* vm, mvm_Value value); +MVM_EXPORT mvm_TeType mvm_typeOf(mvm_VM* vm, mvm_Value value); /** * Converts the value to a string encoded as UTF-8. @@ -226,21 +232,21 @@ mvm_TeType mvm_typeOf(mvm_VM* vm, mvm_Value value); * [memory-management.md](https://github.com/coder-mike/microvium/blob/master/doc/native-vm/memory-management.md) * for details. */ -const char* mvm_toStringUtf8(mvm_VM* vm, mvm_Value value, size_t* out_sizeBytes); +MVM_EXPORT const char* mvm_toStringUtf8(mvm_VM* vm, mvm_Value value, size_t* out_sizeBytes); /** * Convert the value to a bool based on its truthiness. * * See https://developer.mozilla.org/en-US/docs/Glossary/Truthy */ -bool mvm_toBool(mvm_VM* vm, mvm_Value value); +MVM_EXPORT bool mvm_toBool(mvm_VM* vm, mvm_Value value); /** * Converts the value to a 32-bit signed integer. * * The result of this should be the same as `value|0` in JavaScript code. */ -int32_t mvm_toInt32(mvm_VM* vm, mvm_Value value); +MVM_EXPORT int32_t mvm_toInt32(mvm_VM* vm, mvm_Value value); #if MVM_SUPPORT_FLOAT /** @@ -250,7 +256,7 @@ int32_t mvm_toInt32(mvm_VM* vm, mvm_Value value); * * For efficiency, use mvm_toInt32 instead if your value is an integer. */ -MVM_FLOAT64 mvm_toFloat64(mvm_VM* vm, mvm_Value value); +MVM_EXPORT MVM_FLOAT64 mvm_toFloat64(mvm_VM* vm, mvm_Value value); /** * Create a number value in the VM. @@ -260,15 +266,15 @@ MVM_FLOAT64 mvm_toFloat64(mvm_VM* vm, mvm_Value value); * Design note: mvm_newNumber creates a number *from* a float64, so it's named * `newNumber` and not `newFloat64` */ -mvm_Value mvm_newNumber(mvm_VM* vm, MVM_FLOAT64 value); +MVM_EXPORT mvm_Value mvm_newNumber(mvm_VM* vm, MVM_FLOAT64 value); #endif -bool mvm_isNaN(mvm_Value value); +MVM_EXPORT bool mvm_isNaN(mvm_Value value); extern const mvm_Value mvm_undefined; extern const mvm_Value mvm_null; -mvm_Value mvm_newBoolean(bool value); -mvm_Value mvm_newInt32(mvm_VM* vm, int32_t value); +MVM_EXPORT mvm_Value mvm_newBoolean(bool value); +MVM_EXPORT mvm_Value mvm_newInt32(mvm_VM* vm, int32_t value); /** * Create a new string in Microvium memory. @@ -276,7 +282,7 @@ mvm_Value mvm_newInt32(mvm_VM* vm, int32_t value); * @param valueUtf8 The a pointer to the string content. * @param sizeBytes The size in bytes of the string, excluding any null terminator. */ -mvm_Value mvm_newString(mvm_VM* vm, const char* valueUtf8, size_t sizeBytes); +MVM_EXPORT mvm_Value mvm_newString(mvm_VM* vm, const char* valueUtf8, size_t sizeBytes); /** * A Uint8Array in Microvium is an efficient buffer of bytes. It is mutable but @@ -288,7 +294,7 @@ mvm_Value mvm_newString(mvm_VM* vm, const char* valueUtf8, size_t sizeBytes); * * See also: mvm_uint8ArrayToBytes */ -mvm_Value mvm_uint8ArrayFromBytes(mvm_VM* vm, const uint8_t* data, size_t size); +MVM_EXPORT mvm_Value mvm_uint8ArrayFromBytes(mvm_VM* vm, const uint8_t* data, size_t size); /** * Given a Uint8Array, this will give a pointer to its data and its size (in @@ -302,7 +308,7 @@ mvm_Value mvm_uint8ArrayFromBytes(mvm_VM* vm, const uint8_t* data, size_t size); * * See also: mvm_newUint8Array */ -mvm_TeError mvm_uint8ArrayToBytes(mvm_VM* vm, mvm_Value uint8ArrayValue, uint8_t** out_data, size_t* out_size); +MVM_EXPORT mvm_TeError mvm_uint8ArrayToBytes(mvm_VM* vm, mvm_Value uint8ArrayValue, uint8_t** out_data, size_t* out_size); /** * Resolves (finds) the values exported by the VM, identified by ID. @@ -315,7 +321,7 @@ mvm_TeError mvm_uint8ArrayToBytes(mvm_VM* vm, mvm_Value uint8ArrayValue, uint8_t * captured by a mvm_Handle. In typical usage, exports will each be function * values, but any value type is valid. */ -mvm_TeError mvm_resolveExports(mvm_VM* vm, const mvm_VMExportID* ids, mvm_Value* results, uint8_t count); +MVM_EXPORT mvm_TeError mvm_resolveExports(mvm_VM* vm, const mvm_VMExportID* ids, mvm_Value* results, uint8_t count); /** * Run a garbage collection cycle. @@ -327,12 +333,12 @@ mvm_TeError mvm_resolveExports(mvm_VM* vm, const mvm_VMExportID* ids, mvm_Value* * of needed as the amount of space used after the last compaction, and then * adding blocks as-necessary. */ -void mvm_runGC(mvm_VM* vm, bool squeeze); +MVM_EXPORT void mvm_runGC(mvm_VM* vm, bool squeeze); /** * Compares two values for equality. The same semantics as JavaScript `===` */ -bool mvm_equal(mvm_VM* vm, mvm_Value a, mvm_Value b); +MVM_EXPORT bool mvm_equal(mvm_VM* vm, mvm_Value a, mvm_Value b); /** * The current bytecode address being executed (relative to the beginning of the @@ -341,12 +347,12 @@ bool mvm_equal(mvm_VM* vm, mvm_Value a, mvm_Value b); * This value can be looked up in the map file generated by the CLI flag * `--map-file` */ -uint16_t mvm_getCurrentAddress(mvm_VM* vm); +MVM_EXPORT uint16_t mvm_getCurrentAddress(mvm_VM* vm); /** * Get stats about the VM memory */ -void mvm_getMemoryStats(mvm_VM* vm, mvm_TsMemoryStats* out_stats); +MVM_EXPORT void mvm_getMemoryStats(mvm_VM* vm, mvm_TsMemoryStats* out_stats); #if MVM_INCLUDE_SNAPSHOT_CAPABILITY /** @@ -368,7 +374,7 @@ void mvm_getMemoryStats(mvm_VM* vm, mvm_TsMemoryStats* out_stats); * Note: The result is malloc'd on the host heap, and so needs to be freed with * a call to *free*. */ -void* mvm_createSnapshot(mvm_VM* vm, size_t* out_size); +MVM_EXPORT void* mvm_createSnapshot(mvm_VM* vm, size_t* out_size); #endif // MVM_INCLUDE_SNAPSHOT_CAPABILITY #if MVM_INCLUDE_DEBUG_CAPABILITY @@ -389,12 +395,12 @@ void* mvm_createSnapshot(mvm_VM* vm, size_t* out_size); * Setting a breakpoint a second time on the same address of an existing active * breakpoint will have no effect. */ -void mvm_dbg_setBreakpoint(mvm_VM* vm, uint16_t bytecodeAddress); +MVM_EXPORT void mvm_dbg_setBreakpoint(mvm_VM* vm, uint16_t bytecodeAddress); /** * Remove a breakpoint added by mvm_dbg_setBreakpoint */ -void mvm_dbg_removeBreakpoint(mvm_VM* vm, uint16_t bytecodeAddress); +MVM_EXPORT void mvm_dbg_removeBreakpoint(mvm_VM* vm, uint16_t bytecodeAddress); /** * Set the function to be called when any breakpoint is hit. @@ -412,7 +418,7 @@ void mvm_dbg_removeBreakpoint(mvm_VM* vm, uint16_t bytecodeAddress); * still active. This should *NOT* be used to continue execution, but could * theoretically be used to evaluate debug watch expressions. */ -void mvm_dbg_setBreakpointCallback(mvm_VM* vm, mvm_TfBreakpointCallback cb); +MVM_EXPORT void mvm_dbg_setBreakpointCallback(mvm_VM* vm, mvm_TfBreakpointCallback cb); #endif // MVM_INCLUDE_DEBUG_CAPABILITY #ifdef __cplusplus diff --git a/dist-c/microvium_port_example.h b/dist-c/microvium_port_example.h index 70b766c6..94888f59 100644 --- a/dist-c/microvium_port_example.h +++ b/dist-c/microvium_port_example.h @@ -279,10 +279,13 @@ static uint16_t crc16(MVM_LONG_PTR_TYPE lp, uint16_t size) { /** * Implementation of malloc and free to use. * - * Note that MVM_FREE needs to accept null pointers as well. + * Note that MVM_CONTEXTUAL_FREE needs to accept null pointers as well. * - * If MVM_USE_SINGLE_RAM_PAGE is set, pointers returned by MVM_MALLOC must - * always be within 64kB of MVM_RAM_PAGE_ADDR. + * If MVM_USE_SINGLE_RAM_PAGE is set, pointers returned by MVM_CONTEXTUAL_MALLOC + * must always be within 64kB of MVM_RAM_PAGE_ADDR. + * + * The `context` passed to these macros is whatever value that the host passes + * to `mvm_restore`. It can be any value that fits in a pointer. */ -#define MVM_MALLOC(size) malloc(size) -#define MVM_FREE(ptr) free(ptr) +#define MVM_CONTEXTUAL_MALLOC(size, context) malloc(size) +#define MVM_CONTEXTUAL_FREE(ptr, context) free(ptr) diff --git a/native-vm/microvium.c b/native-vm/microvium.c index 622d8d3c..58f2ea75 100644 --- a/native-vm/microvium.c +++ b/native-vm/microvium.c @@ -2596,7 +2596,7 @@ TeError mvm_restore(mvm_VM** result, MVM_LONG_PTR_TYPE lpBytecode, size_t byteco return MVM_E_BYTECODE_REQUIRES_FLOAT_SUPPORT; } - err = vm_validatePortFileMacros(lpBytecode, &header); + err = vm_validatePortFileMacros(lpBytecode, &header, context); if (err) return err; uint16_t importTableSize = header.sectionOffsets[vm_sectionAfter(vm, BCS_IMPORT_TABLE)] - header.sectionOffsets[BCS_IMPORT_TABLE]; @@ -6194,7 +6194,7 @@ void mvm_dbg_setBreakpointCallback(mvm_VM* vm, mvm_TfBreakpointCallback cb) { * point to actual bytecode, whereas pHeader should point to a local copy that's * been validated. */ -static TeError vm_validatePortFileMacros(MVM_LONG_PTR_TYPE lpBytecode, mvm_TsBytecodeHeader* pHeader) { +static TeError vm_validatePortFileMacros(MVM_LONG_PTR_TYPE lpBytecode, mvm_TsBytecodeHeader* pHeader, void* context) { uint32_t x1 = 0x12345678; uint32_t x2 = 0x12345678; uint32_t x3 = 0x87654321; @@ -6238,8 +6238,8 @@ static TeError vm_validatePortFileMacros(MVM_LONG_PTR_TYPE lpBytecode, mvm_TsByt if ((!MVM_NATIVE_POINTER_IS_16_BIT) && (sizeof(void*) == 2)) return MVM_E_EXPECTED_POINTER_SIZE_NOT_TO_BE_16_BIT; #if MVM_USE_SINGLE_RAM_PAGE - void* ptr = MVM_MALLOC(2); - MVM_FREE(ptr); + void* ptr = MVM_CONTEXTUAL_MALLOC(2, context); + MVM_CONTEXTUAL_FREE(ptr, context); if ((intptr_t)ptr - (intptr_t)MVM_RAM_PAGE_ADDR > 0xffff) return MVM_E_MALLOC_NOT_WITHIN_RAM_PAGE; #endif // MVM_USE_SINGLE_RAM_PAGE @@ -6301,7 +6301,7 @@ static TeError vm_newError(VM* vm, TeError err) { } static void* vm_malloc(VM* vm, size_t size) { - void* result = MVM_MALLOC(size); + void* result = MVM_CONTEXTUAL_MALLOC(size, vm->context); #if MVM_SAFE_MODE && MVM_USE_SINGLE_RAM_PAGE // See comment on MVM_RAM_PAGE_ADDR in microvium_port_example.h @@ -6312,12 +6312,15 @@ static void* vm_malloc(VM* vm, size_t size) { // Note: mvm_free frees the VM, while vm_free is the counterpart to vm_malloc static void vm_free(VM* vm, void* ptr) { + // Capture the context before freeing the ptr, since the pointer could be the vm + void* context = vm->context; + #if MVM_SAFE_MODE && MVM_USE_SINGLE_RAM_PAGE // See comment on MVM_RAM_PAGE_ADDR in microvium_port_example.h VM_ASSERT(vm, !ptr || ((intptr_t)ptr - (intptr_t)MVM_RAM_PAGE_ADDR <= 0xFFFF)); #endif - MVM_FREE(ptr); + MVM_CONTEXTUAL_FREE(ptr, context); } static mvm_TeError vm_uint8ArrayNew(VM* vm, Value* slot) { diff --git a/native-vm/microvium.h b/native-vm/microvium.h index b2f0bbfd..15169ade 100644 --- a/native-vm/microvium.h +++ b/native-vm/microvium.h @@ -90,6 +90,12 @@ typedef enum mvm_TeType { VM_T_END, } mvm_TeType; +// Prefix to attach to exported microvium API functions. If a user doesn't +// specify this, we just set it up as the empty macro. +#ifndef MVM_EXPORT +#define MVM_EXPORT +#endif + typedef struct mvm_VM mvm_VM; typedef mvm_TeError (*mvm_TfHostFunction)(mvm_VM* vm, mvm_HostFunctionID hostFunctionID, mvm_Value* result, mvm_Value* args, uint8_t argCount); @@ -169,12 +175,12 @@ extern "C" { * @param context Any value. The context for a VM can be retrieved later using * `mvm_getContext`. It can be used to attach user-defined data to a VM. */ -mvm_TeError mvm_restore(mvm_VM** result, MVM_LONG_PTR_TYPE snapshotBytecode, size_t bytecodeSize, void* context, mvm_TfResolveImport resolveImport); +MVM_EXPORT mvm_TeError mvm_restore(mvm_VM** result, MVM_LONG_PTR_TYPE snapshotBytecode, size_t bytecodeSize, void* context, mvm_TfResolveImport resolveImport); /** * Free all memory associated with a VM. The VM must not be used again after freeing. */ -void mvm_free(mvm_VM* vm); +MVM_EXPORT void mvm_free(mvm_VM* vm); /** * Call a function in the VM @@ -189,21 +195,21 @@ void mvm_free(mvm_VM* vm); * MVM_E_UNCAUGHT_EXCEPTION and the exception value will be put into * `out_result` */ -mvm_TeError mvm_call(mvm_VM* vm, mvm_Value func, mvm_Value* out_result, mvm_Value* args, uint8_t argCount); +MVM_EXPORT mvm_TeError mvm_call(mvm_VM* vm, mvm_Value func, mvm_Value* out_result, mvm_Value* args, uint8_t argCount); -void* mvm_getContext(mvm_VM* vm); +MVM_EXPORT void* mvm_getContext(mvm_VM* vm); -void mvm_initializeHandle(mvm_VM* vm, mvm_Handle* handle); // Handle must be released by mvm_releaseHandle -void mvm_cloneHandle(mvm_VM* vm, mvm_Handle* target, const mvm_Handle* source); // Target must be released by mvm_releaseHandle -mvm_TeError mvm_releaseHandle(mvm_VM* vm, mvm_Handle* handle); -static inline mvm_Value mvm_handleGet(const mvm_Handle* handle) { return handle->_value; } -static inline void mvm_handleSet(mvm_Handle* handle, mvm_Value value) { handle->_value = value; } +MVM_EXPORT void mvm_initializeHandle(mvm_VM* vm, mvm_Handle* handle); // Handle must be released by mvm_releaseHandle +MVM_EXPORT void mvm_cloneHandle(mvm_VM* vm, mvm_Handle* target, const mvm_Handle* source); // Target must be released by mvm_releaseHandle +MVM_EXPORT mvm_TeError mvm_releaseHandle(mvm_VM* vm, mvm_Handle* handle); +MVM_EXPORT static inline mvm_Value mvm_handleGet(const mvm_Handle* handle) { return handle->_value; } +MVM_EXPORT static inline void mvm_handleSet(mvm_Handle* handle, mvm_Value value) { handle->_value = value; } /** * Roughly like the `typeof` operator in JS, except with distinct values for * null and arrays */ -mvm_TeType mvm_typeOf(mvm_VM* vm, mvm_Value value); +MVM_EXPORT mvm_TeType mvm_typeOf(mvm_VM* vm, mvm_Value value); /** * Converts the value to a string encoded as UTF-8. @@ -226,21 +232,21 @@ mvm_TeType mvm_typeOf(mvm_VM* vm, mvm_Value value); * [memory-management.md](https://github.com/coder-mike/microvium/blob/master/doc/native-vm/memory-management.md) * for details. */ -const char* mvm_toStringUtf8(mvm_VM* vm, mvm_Value value, size_t* out_sizeBytes); +MVM_EXPORT const char* mvm_toStringUtf8(mvm_VM* vm, mvm_Value value, size_t* out_sizeBytes); /** * Convert the value to a bool based on its truthiness. * * See https://developer.mozilla.org/en-US/docs/Glossary/Truthy */ -bool mvm_toBool(mvm_VM* vm, mvm_Value value); +MVM_EXPORT bool mvm_toBool(mvm_VM* vm, mvm_Value value); /** * Converts the value to a 32-bit signed integer. * * The result of this should be the same as `value|0` in JavaScript code. */ -int32_t mvm_toInt32(mvm_VM* vm, mvm_Value value); +MVM_EXPORT int32_t mvm_toInt32(mvm_VM* vm, mvm_Value value); #if MVM_SUPPORT_FLOAT /** @@ -250,7 +256,7 @@ int32_t mvm_toInt32(mvm_VM* vm, mvm_Value value); * * For efficiency, use mvm_toInt32 instead if your value is an integer. */ -MVM_FLOAT64 mvm_toFloat64(mvm_VM* vm, mvm_Value value); +MVM_EXPORT MVM_FLOAT64 mvm_toFloat64(mvm_VM* vm, mvm_Value value); /** * Create a number value in the VM. @@ -260,15 +266,15 @@ MVM_FLOAT64 mvm_toFloat64(mvm_VM* vm, mvm_Value value); * Design note: mvm_newNumber creates a number *from* a float64, so it's named * `newNumber` and not `newFloat64` */ -mvm_Value mvm_newNumber(mvm_VM* vm, MVM_FLOAT64 value); +MVM_EXPORT mvm_Value mvm_newNumber(mvm_VM* vm, MVM_FLOAT64 value); #endif -bool mvm_isNaN(mvm_Value value); +MVM_EXPORT bool mvm_isNaN(mvm_Value value); extern const mvm_Value mvm_undefined; extern const mvm_Value mvm_null; -mvm_Value mvm_newBoolean(bool value); -mvm_Value mvm_newInt32(mvm_VM* vm, int32_t value); +MVM_EXPORT mvm_Value mvm_newBoolean(bool value); +MVM_EXPORT mvm_Value mvm_newInt32(mvm_VM* vm, int32_t value); /** * Create a new string in Microvium memory. @@ -276,7 +282,7 @@ mvm_Value mvm_newInt32(mvm_VM* vm, int32_t value); * @param valueUtf8 The a pointer to the string content. * @param sizeBytes The size in bytes of the string, excluding any null terminator. */ -mvm_Value mvm_newString(mvm_VM* vm, const char* valueUtf8, size_t sizeBytes); +MVM_EXPORT mvm_Value mvm_newString(mvm_VM* vm, const char* valueUtf8, size_t sizeBytes); /** * A Uint8Array in Microvium is an efficient buffer of bytes. It is mutable but @@ -288,7 +294,7 @@ mvm_Value mvm_newString(mvm_VM* vm, const char* valueUtf8, size_t sizeBytes); * * See also: mvm_uint8ArrayToBytes */ -mvm_Value mvm_uint8ArrayFromBytes(mvm_VM* vm, const uint8_t* data, size_t size); +MVM_EXPORT mvm_Value mvm_uint8ArrayFromBytes(mvm_VM* vm, const uint8_t* data, size_t size); /** * Given a Uint8Array, this will give a pointer to its data and its size (in @@ -302,7 +308,7 @@ mvm_Value mvm_uint8ArrayFromBytes(mvm_VM* vm, const uint8_t* data, size_t size); * * See also: mvm_newUint8Array */ -mvm_TeError mvm_uint8ArrayToBytes(mvm_VM* vm, mvm_Value uint8ArrayValue, uint8_t** out_data, size_t* out_size); +MVM_EXPORT mvm_TeError mvm_uint8ArrayToBytes(mvm_VM* vm, mvm_Value uint8ArrayValue, uint8_t** out_data, size_t* out_size); /** * Resolves (finds) the values exported by the VM, identified by ID. @@ -315,7 +321,7 @@ mvm_TeError mvm_uint8ArrayToBytes(mvm_VM* vm, mvm_Value uint8ArrayValue, uint8_t * captured by a mvm_Handle. In typical usage, exports will each be function * values, but any value type is valid. */ -mvm_TeError mvm_resolveExports(mvm_VM* vm, const mvm_VMExportID* ids, mvm_Value* results, uint8_t count); +MVM_EXPORT mvm_TeError mvm_resolveExports(mvm_VM* vm, const mvm_VMExportID* ids, mvm_Value* results, uint8_t count); /** * Run a garbage collection cycle. @@ -327,12 +333,12 @@ mvm_TeError mvm_resolveExports(mvm_VM* vm, const mvm_VMExportID* ids, mvm_Value* * of needed as the amount of space used after the last compaction, and then * adding blocks as-necessary. */ -void mvm_runGC(mvm_VM* vm, bool squeeze); +MVM_EXPORT void mvm_runGC(mvm_VM* vm, bool squeeze); /** * Compares two values for equality. The same semantics as JavaScript `===` */ -bool mvm_equal(mvm_VM* vm, mvm_Value a, mvm_Value b); +MVM_EXPORT bool mvm_equal(mvm_VM* vm, mvm_Value a, mvm_Value b); /** * The current bytecode address being executed (relative to the beginning of the @@ -341,12 +347,12 @@ bool mvm_equal(mvm_VM* vm, mvm_Value a, mvm_Value b); * This value can be looked up in the map file generated by the CLI flag * `--map-file` */ -uint16_t mvm_getCurrentAddress(mvm_VM* vm); +MVM_EXPORT uint16_t mvm_getCurrentAddress(mvm_VM* vm); /** * Get stats about the VM memory */ -void mvm_getMemoryStats(mvm_VM* vm, mvm_TsMemoryStats* out_stats); +MVM_EXPORT void mvm_getMemoryStats(mvm_VM* vm, mvm_TsMemoryStats* out_stats); #if MVM_INCLUDE_SNAPSHOT_CAPABILITY /** @@ -368,7 +374,7 @@ void mvm_getMemoryStats(mvm_VM* vm, mvm_TsMemoryStats* out_stats); * Note: The result is malloc'd on the host heap, and so needs to be freed with * a call to *free*. */ -void* mvm_createSnapshot(mvm_VM* vm, size_t* out_size); +MVM_EXPORT void* mvm_createSnapshot(mvm_VM* vm, size_t* out_size); #endif // MVM_INCLUDE_SNAPSHOT_CAPABILITY #if MVM_INCLUDE_DEBUG_CAPABILITY @@ -389,12 +395,12 @@ void* mvm_createSnapshot(mvm_VM* vm, size_t* out_size); * Setting a breakpoint a second time on the same address of an existing active * breakpoint will have no effect. */ -void mvm_dbg_setBreakpoint(mvm_VM* vm, uint16_t bytecodeAddress); +MVM_EXPORT void mvm_dbg_setBreakpoint(mvm_VM* vm, uint16_t bytecodeAddress); /** * Remove a breakpoint added by mvm_dbg_setBreakpoint */ -void mvm_dbg_removeBreakpoint(mvm_VM* vm, uint16_t bytecodeAddress); +MVM_EXPORT void mvm_dbg_removeBreakpoint(mvm_VM* vm, uint16_t bytecodeAddress); /** * Set the function to be called when any breakpoint is hit. @@ -412,7 +418,7 @@ void mvm_dbg_removeBreakpoint(mvm_VM* vm, uint16_t bytecodeAddress); * still active. This should *NOT* be used to continue execution, but could * theoretically be used to evaluate debug watch expressions. */ -void mvm_dbg_setBreakpointCallback(mvm_VM* vm, mvm_TfBreakpointCallback cb); +MVM_EXPORT void mvm_dbg_setBreakpointCallback(mvm_VM* vm, mvm_TfBreakpointCallback cb); #endif // MVM_INCLUDE_DEBUG_CAPABILITY #ifdef __cplusplus diff --git a/native-vm/microvium_internals.h b/native-vm/microvium_internals.h index f6ac9574..264aae07 100644 --- a/native-vm/microvium_internals.h +++ b/native-vm/microvium_internals.h @@ -816,7 +816,7 @@ static uint16_t getBucketOffsetEnd(TsBucket* bucket); static uint16_t getSectionSize(VM* vm, mvm_TeBytecodeSection section); static Value vm_intToStr(VM* vm, int32_t i); static Value vm_newStringFromCStrNT(VM* vm, const char* s); -static TeError vm_validatePortFileMacros(MVM_LONG_PTR_TYPE lpBytecode, mvm_TsBytecodeHeader* pHeader); +static TeError vm_validatePortFileMacros(MVM_LONG_PTR_TYPE lpBytecode, mvm_TsBytecodeHeader* pHeader, void* context); static LongPtr vm_toStringUtf8_long(VM* vm, Value value, size_t* out_sizeBytes); static LongPtr vm_findScopedVariable(VM* vm, uint16_t index); static Value vm_cloneContainer(VM* vm, Value* pArr); @@ -966,3 +966,14 @@ static int32_t mvm_float64ToInt32(MVM_FLOAT64 value); #ifndef MVM_PORT_INT32_OVERFLOW_CHECKS #define MVM_PORT_INT32_OVERFLOW_CHECKS 1 #endif + +// Backwards compatibility with non-contextual malloc +#ifndef MVM_CONTEXTUAL_MALLOC +#define MVM_CONTEXTUAL_MALLOC(size, context) MVM_MALLOC(size) +#endif + +// Backwards compatibility with non-contextual free +#ifndef MVM_CONTEXTUAL_FREE +#define MVM_CONTEXTUAL_FREE(size, context) MVM_FREE(size) +#endif + diff --git a/native-vm/microvium_port_example.h b/native-vm/microvium_port_example.h index 70b766c6..94888f59 100644 --- a/native-vm/microvium_port_example.h +++ b/native-vm/microvium_port_example.h @@ -279,10 +279,13 @@ static uint16_t crc16(MVM_LONG_PTR_TYPE lp, uint16_t size) { /** * Implementation of malloc and free to use. * - * Note that MVM_FREE needs to accept null pointers as well. + * Note that MVM_CONTEXTUAL_FREE needs to accept null pointers as well. * - * If MVM_USE_SINGLE_RAM_PAGE is set, pointers returned by MVM_MALLOC must - * always be within 64kB of MVM_RAM_PAGE_ADDR. + * If MVM_USE_SINGLE_RAM_PAGE is set, pointers returned by MVM_CONTEXTUAL_MALLOC + * must always be within 64kB of MVM_RAM_PAGE_ADDR. + * + * The `context` passed to these macros is whatever value that the host passes + * to `mvm_restore`. It can be any value that fits in a pointer. */ -#define MVM_MALLOC(size) malloc(size) -#define MVM_FREE(ptr) free(ptr) +#define MVM_CONTEXTUAL_MALLOC(size, context) malloc(size) +#define MVM_CONTEXTUAL_FREE(ptr, context) free(ptr) diff --git a/test/getting-started/code/microvium/microvium.c b/test/getting-started/code/microvium/microvium.c index 4a59f00f..a5c9fc04 100644 --- a/test/getting-started/code/microvium/microvium.c +++ b/test/getting-started/code/microvium/microvium.c @@ -1365,7 +1365,7 @@ static uint16_t getBucketOffsetEnd(TsBucket* bucket); static uint16_t getSectionSize(VM* vm, mvm_TeBytecodeSection section); static Value vm_intToStr(VM* vm, int32_t i); static Value vm_newStringFromCStrNT(VM* vm, const char* s); -static TeError vm_validatePortFileMacros(MVM_LONG_PTR_TYPE lpBytecode, mvm_TsBytecodeHeader* pHeader); +static TeError vm_validatePortFileMacros(MVM_LONG_PTR_TYPE lpBytecode, mvm_TsBytecodeHeader* pHeader, void* context); static LongPtr vm_toStringUtf8_long(VM* vm, Value value, size_t* out_sizeBytes); static LongPtr vm_findScopedVariable(VM* vm, uint16_t index); static Value vm_cloneContainer(VM* vm, Value* pArr); @@ -1516,6 +1516,17 @@ static int32_t mvm_float64ToInt32(MVM_FLOAT64 value); #define MVM_PORT_INT32_OVERFLOW_CHECKS 1 #endif +// Backwards compatibility with non-contextual malloc +#ifndef MVM_CONTEXTUAL_MALLOC +#define MVM_CONTEXTUAL_MALLOC(size, context) MVM_MALLOC(size) +#endif + +// Backwards compatibility with non-contextual free +#ifndef MVM_CONTEXTUAL_FREE +#define MVM_CONTEXTUAL_FREE(size, context) MVM_FREE(size) +#endif + + @@ -4076,7 +4087,7 @@ TeError mvm_restore(mvm_VM** result, MVM_LONG_PTR_TYPE lpBytecode, size_t byteco return MVM_E_BYTECODE_REQUIRES_FLOAT_SUPPORT; } - err = vm_validatePortFileMacros(lpBytecode, &header); + err = vm_validatePortFileMacros(lpBytecode, &header, context); if (err) return err; uint16_t importTableSize = header.sectionOffsets[vm_sectionAfter(vm, BCS_IMPORT_TABLE)] - header.sectionOffsets[BCS_IMPORT_TABLE]; @@ -7674,7 +7685,7 @@ void mvm_dbg_setBreakpointCallback(mvm_VM* vm, mvm_TfBreakpointCallback cb) { * point to actual bytecode, whereas pHeader should point to a local copy that's * been validated. */ -static TeError vm_validatePortFileMacros(MVM_LONG_PTR_TYPE lpBytecode, mvm_TsBytecodeHeader* pHeader) { +static TeError vm_validatePortFileMacros(MVM_LONG_PTR_TYPE lpBytecode, mvm_TsBytecodeHeader* pHeader, void* context) { uint32_t x1 = 0x12345678; uint32_t x2 = 0x12345678; uint32_t x3 = 0x87654321; @@ -7718,8 +7729,8 @@ static TeError vm_validatePortFileMacros(MVM_LONG_PTR_TYPE lpBytecode, mvm_TsByt if ((!MVM_NATIVE_POINTER_IS_16_BIT) && (sizeof(void*) == 2)) return MVM_E_EXPECTED_POINTER_SIZE_NOT_TO_BE_16_BIT; #if MVM_USE_SINGLE_RAM_PAGE - void* ptr = MVM_MALLOC(2); - MVM_FREE(ptr); + void* ptr = MVM_CONTEXTUAL_MALLOC(2, context); + MVM_CONTEXTUAL_FREE(ptr, context); if ((intptr_t)ptr - (intptr_t)MVM_RAM_PAGE_ADDR > 0xffff) return MVM_E_MALLOC_NOT_WITHIN_RAM_PAGE; #endif // MVM_USE_SINGLE_RAM_PAGE @@ -7781,7 +7792,7 @@ static TeError vm_newError(VM* vm, TeError err) { } static void* vm_malloc(VM* vm, size_t size) { - void* result = MVM_MALLOC(size); + void* result = MVM_CONTEXTUAL_MALLOC(size, vm->context); #if MVM_SAFE_MODE && MVM_USE_SINGLE_RAM_PAGE // See comment on MVM_RAM_PAGE_ADDR in microvium_port_example.h @@ -7792,12 +7803,15 @@ static void* vm_malloc(VM* vm, size_t size) { // Note: mvm_free frees the VM, while vm_free is the counterpart to vm_malloc static void vm_free(VM* vm, void* ptr) { + // Capture the context before freeing the ptr, since the pointer could be the vm + void* context = vm->context; + #if MVM_SAFE_MODE && MVM_USE_SINGLE_RAM_PAGE // See comment on MVM_RAM_PAGE_ADDR in microvium_port_example.h VM_ASSERT(vm, !ptr || ((intptr_t)ptr - (intptr_t)MVM_RAM_PAGE_ADDR <= 0xFFFF)); #endif - MVM_FREE(ptr); + MVM_CONTEXTUAL_FREE(ptr, context); } static mvm_TeError vm_uint8ArrayNew(VM* vm, Value* slot) { diff --git a/test/getting-started/code/microvium/microvium.h b/test/getting-started/code/microvium/microvium.h index b948ea3f..29024d86 100644 --- a/test/getting-started/code/microvium/microvium.h +++ b/test/getting-started/code/microvium/microvium.h @@ -90,6 +90,12 @@ typedef enum mvm_TeType { VM_T_END, } mvm_TeType; +// Prefix to attach to exported microvium API functions. If a user doesn't +// specify this, we just set it up as the empty macro. +#ifndef MVM_EXPORT +#define MVM_EXPORT +#endif + typedef struct mvm_VM mvm_VM; typedef mvm_TeError (*mvm_TfHostFunction)(mvm_VM* vm, mvm_HostFunctionID hostFunctionID, mvm_Value* result, mvm_Value* args, uint8_t argCount); @@ -169,12 +175,12 @@ extern "C" { * @param context Any value. The context for a VM can be retrieved later using * `mvm_getContext`. It can be used to attach user-defined data to a VM. */ -mvm_TeError mvm_restore(mvm_VM** result, MVM_LONG_PTR_TYPE snapshotBytecode, size_t bytecodeSize, void* context, mvm_TfResolveImport resolveImport); +MVM_EXPORT mvm_TeError mvm_restore(mvm_VM** result, MVM_LONG_PTR_TYPE snapshotBytecode, size_t bytecodeSize, void* context, mvm_TfResolveImport resolveImport); /** * Free all memory associated with a VM. The VM must not be used again after freeing. */ -void mvm_free(mvm_VM* vm); +MVM_EXPORT void mvm_free(mvm_VM* vm); /** * Call a function in the VM @@ -189,21 +195,21 @@ void mvm_free(mvm_VM* vm); * MVM_E_UNCAUGHT_EXCEPTION and the exception value will be put into * `out_result` */ -mvm_TeError mvm_call(mvm_VM* vm, mvm_Value func, mvm_Value* out_result, mvm_Value* args, uint8_t argCount); +MVM_EXPORT mvm_TeError mvm_call(mvm_VM* vm, mvm_Value func, mvm_Value* out_result, mvm_Value* args, uint8_t argCount); -void* mvm_getContext(mvm_VM* vm); +MVM_EXPORT void* mvm_getContext(mvm_VM* vm); -void mvm_initializeHandle(mvm_VM* vm, mvm_Handle* handle); // Handle must be released by mvm_releaseHandle -void mvm_cloneHandle(mvm_VM* vm, mvm_Handle* target, const mvm_Handle* source); // Target must be released by mvm_releaseHandle -mvm_TeError mvm_releaseHandle(mvm_VM* vm, mvm_Handle* handle); -static inline mvm_Value mvm_handleGet(const mvm_Handle* handle) { return handle->_value; } -static inline void mvm_handleSet(mvm_Handle* handle, mvm_Value value) { handle->_value = value; } +MVM_EXPORT void mvm_initializeHandle(mvm_VM* vm, mvm_Handle* handle); // Handle must be released by mvm_releaseHandle +MVM_EXPORT void mvm_cloneHandle(mvm_VM* vm, mvm_Handle* target, const mvm_Handle* source); // Target must be released by mvm_releaseHandle +MVM_EXPORT mvm_TeError mvm_releaseHandle(mvm_VM* vm, mvm_Handle* handle); +MVM_EXPORT static inline mvm_Value mvm_handleGet(const mvm_Handle* handle) { return handle->_value; } +MVM_EXPORT static inline void mvm_handleSet(mvm_Handle* handle, mvm_Value value) { handle->_value = value; } /** * Roughly like the `typeof` operator in JS, except with distinct values for * null and arrays */ -mvm_TeType mvm_typeOf(mvm_VM* vm, mvm_Value value); +MVM_EXPORT mvm_TeType mvm_typeOf(mvm_VM* vm, mvm_Value value); /** * Converts the value to a string encoded as UTF-8. @@ -226,21 +232,21 @@ mvm_TeType mvm_typeOf(mvm_VM* vm, mvm_Value value); * [memory-management.md](https://github.com/coder-mike/microvium/blob/master/doc/native-vm/memory-management.md) * for details. */ -const char* mvm_toStringUtf8(mvm_VM* vm, mvm_Value value, size_t* out_sizeBytes); +MVM_EXPORT const char* mvm_toStringUtf8(mvm_VM* vm, mvm_Value value, size_t* out_sizeBytes); /** * Convert the value to a bool based on its truthiness. * * See https://developer.mozilla.org/en-US/docs/Glossary/Truthy */ -bool mvm_toBool(mvm_VM* vm, mvm_Value value); +MVM_EXPORT bool mvm_toBool(mvm_VM* vm, mvm_Value value); /** * Converts the value to a 32-bit signed integer. * * The result of this should be the same as `value|0` in JavaScript code. */ -int32_t mvm_toInt32(mvm_VM* vm, mvm_Value value); +MVM_EXPORT int32_t mvm_toInt32(mvm_VM* vm, mvm_Value value); #if MVM_SUPPORT_FLOAT /** @@ -250,7 +256,7 @@ int32_t mvm_toInt32(mvm_VM* vm, mvm_Value value); * * For efficiency, use mvm_toInt32 instead if your value is an integer. */ -MVM_FLOAT64 mvm_toFloat64(mvm_VM* vm, mvm_Value value); +MVM_EXPORT MVM_FLOAT64 mvm_toFloat64(mvm_VM* vm, mvm_Value value); /** * Create a number value in the VM. @@ -260,15 +266,15 @@ MVM_FLOAT64 mvm_toFloat64(mvm_VM* vm, mvm_Value value); * Design note: mvm_newNumber creates a number *from* a float64, so it's named * `newNumber` and not `newFloat64` */ -mvm_Value mvm_newNumber(mvm_VM* vm, MVM_FLOAT64 value); +MVM_EXPORT mvm_Value mvm_newNumber(mvm_VM* vm, MVM_FLOAT64 value); #endif -bool mvm_isNaN(mvm_Value value); +MVM_EXPORT bool mvm_isNaN(mvm_Value value); extern const mvm_Value mvm_undefined; extern const mvm_Value mvm_null; -mvm_Value mvm_newBoolean(bool value); -mvm_Value mvm_newInt32(mvm_VM* vm, int32_t value); +MVM_EXPORT mvm_Value mvm_newBoolean(bool value); +MVM_EXPORT mvm_Value mvm_newInt32(mvm_VM* vm, int32_t value); /** * Create a new string in Microvium memory. @@ -276,7 +282,7 @@ mvm_Value mvm_newInt32(mvm_VM* vm, int32_t value); * @param valueUtf8 The a pointer to the string content. * @param sizeBytes The size in bytes of the string, excluding any null terminator. */ -mvm_Value mvm_newString(mvm_VM* vm, const char* valueUtf8, size_t sizeBytes); +MVM_EXPORT mvm_Value mvm_newString(mvm_VM* vm, const char* valueUtf8, size_t sizeBytes); /** * A Uint8Array in Microvium is an efficient buffer of bytes. It is mutable but @@ -288,7 +294,7 @@ mvm_Value mvm_newString(mvm_VM* vm, const char* valueUtf8, size_t sizeBytes); * * See also: mvm_uint8ArrayToBytes */ -mvm_Value mvm_uint8ArrayFromBytes(mvm_VM* vm, const uint8_t* data, size_t size); +MVM_EXPORT mvm_Value mvm_uint8ArrayFromBytes(mvm_VM* vm, const uint8_t* data, size_t size); /** * Given a Uint8Array, this will give a pointer to its data and its size (in @@ -302,7 +308,7 @@ mvm_Value mvm_uint8ArrayFromBytes(mvm_VM* vm, const uint8_t* data, size_t size); * * See also: mvm_newUint8Array */ -mvm_TeError mvm_uint8ArrayToBytes(mvm_VM* vm, mvm_Value uint8ArrayValue, uint8_t** out_data, size_t* out_size); +MVM_EXPORT mvm_TeError mvm_uint8ArrayToBytes(mvm_VM* vm, mvm_Value uint8ArrayValue, uint8_t** out_data, size_t* out_size); /** * Resolves (finds) the values exported by the VM, identified by ID. @@ -315,7 +321,7 @@ mvm_TeError mvm_uint8ArrayToBytes(mvm_VM* vm, mvm_Value uint8ArrayValue, uint8_t * captured by a mvm_Handle. In typical usage, exports will each be function * values, but any value type is valid. */ -mvm_TeError mvm_resolveExports(mvm_VM* vm, const mvm_VMExportID* ids, mvm_Value* results, uint8_t count); +MVM_EXPORT mvm_TeError mvm_resolveExports(mvm_VM* vm, const mvm_VMExportID* ids, mvm_Value* results, uint8_t count); /** * Run a garbage collection cycle. @@ -327,12 +333,12 @@ mvm_TeError mvm_resolveExports(mvm_VM* vm, const mvm_VMExportID* ids, mvm_Value* * of needed as the amount of space used after the last compaction, and then * adding blocks as-necessary. */ -void mvm_runGC(mvm_VM* vm, bool squeeze); +MVM_EXPORT void mvm_runGC(mvm_VM* vm, bool squeeze); /** * Compares two values for equality. The same semantics as JavaScript `===` */ -bool mvm_equal(mvm_VM* vm, mvm_Value a, mvm_Value b); +MVM_EXPORT bool mvm_equal(mvm_VM* vm, mvm_Value a, mvm_Value b); /** * The current bytecode address being executed (relative to the beginning of the @@ -341,12 +347,12 @@ bool mvm_equal(mvm_VM* vm, mvm_Value a, mvm_Value b); * This value can be looked up in the map file generated by the CLI flag * `--map-file` */ -uint16_t mvm_getCurrentAddress(mvm_VM* vm); +MVM_EXPORT uint16_t mvm_getCurrentAddress(mvm_VM* vm); /** * Get stats about the VM memory */ -void mvm_getMemoryStats(mvm_VM* vm, mvm_TsMemoryStats* out_stats); +MVM_EXPORT void mvm_getMemoryStats(mvm_VM* vm, mvm_TsMemoryStats* out_stats); #if MVM_INCLUDE_SNAPSHOT_CAPABILITY /** @@ -368,7 +374,7 @@ void mvm_getMemoryStats(mvm_VM* vm, mvm_TsMemoryStats* out_stats); * Note: The result is malloc'd on the host heap, and so needs to be freed with * a call to *free*. */ -void* mvm_createSnapshot(mvm_VM* vm, size_t* out_size); +MVM_EXPORT void* mvm_createSnapshot(mvm_VM* vm, size_t* out_size); #endif // MVM_INCLUDE_SNAPSHOT_CAPABILITY #if MVM_INCLUDE_DEBUG_CAPABILITY @@ -389,12 +395,12 @@ void* mvm_createSnapshot(mvm_VM* vm, size_t* out_size); * Setting a breakpoint a second time on the same address of an existing active * breakpoint will have no effect. */ -void mvm_dbg_setBreakpoint(mvm_VM* vm, uint16_t bytecodeAddress); +MVM_EXPORT void mvm_dbg_setBreakpoint(mvm_VM* vm, uint16_t bytecodeAddress); /** * Remove a breakpoint added by mvm_dbg_setBreakpoint */ -void mvm_dbg_removeBreakpoint(mvm_VM* vm, uint16_t bytecodeAddress); +MVM_EXPORT void mvm_dbg_removeBreakpoint(mvm_VM* vm, uint16_t bytecodeAddress); /** * Set the function to be called when any breakpoint is hit. @@ -412,7 +418,7 @@ void mvm_dbg_removeBreakpoint(mvm_VM* vm, uint16_t bytecodeAddress); * still active. This should *NOT* be used to continue execution, but could * theoretically be used to evaluate debug watch expressions. */ -void mvm_dbg_setBreakpointCallback(mvm_VM* vm, mvm_TfBreakpointCallback cb); +MVM_EXPORT void mvm_dbg_setBreakpointCallback(mvm_VM* vm, mvm_TfBreakpointCallback cb); #endif // MVM_INCLUDE_DEBUG_CAPABILITY #ifdef __cplusplus diff --git a/test/getting-started/code/microvium_port.h b/test/getting-started/code/microvium_port.h index 70b766c6..94888f59 100644 --- a/test/getting-started/code/microvium_port.h +++ b/test/getting-started/code/microvium_port.h @@ -279,10 +279,13 @@ static uint16_t crc16(MVM_LONG_PTR_TYPE lp, uint16_t size) { /** * Implementation of malloc and free to use. * - * Note that MVM_FREE needs to accept null pointers as well. + * Note that MVM_CONTEXTUAL_FREE needs to accept null pointers as well. * - * If MVM_USE_SINGLE_RAM_PAGE is set, pointers returned by MVM_MALLOC must - * always be within 64kB of MVM_RAM_PAGE_ADDR. + * If MVM_USE_SINGLE_RAM_PAGE is set, pointers returned by MVM_CONTEXTUAL_MALLOC + * must always be within 64kB of MVM_RAM_PAGE_ADDR. + * + * The `context` passed to these macros is whatever value that the host passes + * to `mvm_restore`. It can be any value that fits in a pointer. */ -#define MVM_MALLOC(size) malloc(size) -#define MVM_FREE(ptr) free(ptr) +#define MVM_CONTEXTUAL_MALLOC(size, context) malloc(size) +#define MVM_CONTEXTUAL_FREE(ptr, context) free(ptr)