Skip to content

#51 Private heaps and MVM_EXPORT #52

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions dist-c/microvium.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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





Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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) {
Expand Down
66 changes: 36 additions & 30 deletions dist-c/microvium.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand All @@ -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; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I spoke too soon. I didn't notice that you'd added the macro on these as well. We don't want it on static inline functions because they exist in the caller's compilation unit, not in the library.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My mistake. Let me know if #55 doesn't fix this.

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.
Expand All @@ -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
/**
Expand All @@ -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.
Expand All @@ -260,23 +266,23 @@ 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.
*
* @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
Expand All @@ -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
Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -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
Expand All @@ -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
/**
Expand All @@ -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
Expand All @@ -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.
Expand All @@ -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
Expand Down
13 changes: 8 additions & 5 deletions dist-c/microvium_port_example.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
15 changes: 9 additions & 6 deletions native-vm/microvium.c
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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) {
Expand Down
Loading