coder-mike / microvium

A compact, embeddable scripting engine for applications and microcontrollers for executing programs written in a subset of the JavaScript language.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Shared microvium instances with private heaps

davidchisnall opened this issue · comments

We'd like to be able to have a single instance of the microvium code, but allow different compartments to use it with different heaps. This requires our malloc / free callbacks to have some way of identifying which mvm_VM they are being called on behalf of (and, ideally, getting some state from it). My ideal interface would make it possible to add a header to the mvm_VM structure and have the mvm_VM be passed to the alloc and free callbacks.

Somewhat related: it would be nice if the functions in microvium.h that are implemented in microvium.c had a MICROVIUM_EXPORT macro in front of them (with a default empty definition) that integrators can use to define custom calling conventions, visibility, and so on.

Hi David. Yes, that sounds like something useful. However, mvm_restore also performs a malloc but before the vm exists (because it's the malloc for the vm itself). What if the MVM_MALLOC macro instead accepted the context as an argument rather than the VM itself?

My ideal interface would make it possible to add a header to the mvm_VM structure

I'm not sure what you mean by "header" here. If you're talking about a field, then the context field may be what you're looking for. User code passes it in to mvm_restore, and can access it again using mvm_getContext. It can be anything you like that fits in a pointer.

Somewhat related: it would be nice if the functions in microvium.h that are implemented in microvium.c had a MICROVIUM_EXPORT macro in front of them (with a default empty definition) that integrators can use to define custom calling conventions, visibility, and so on.

Can you give me an example to make it clearer?

I'm not sure what you mean by "header" here. If you're talking about a field, then the context field may be what you're looking for. User code passes it in to mvm_restore, and can access it again using mvm_getContext. It can be anything you like that fits in a pointer.

That would be ideal. If the allocate and deallocate functions were simply passed this context, then this would be very easy for us to support. I believe this could be implemented in a backwards-compatible way by doing something like:

#ifndef MVM_CONTEXT_MALLOC
#    define MVM_CONTEXT_MALLOC(ctx, size) MVM_MALLOC(size)
#endif

And then replacing uses of MVM_MALLOC with MVM_CONTEXT_MALLOC in the implementaiton.

That way, if someone implements MVM_MALLOC but not MVM_CONTEXT_MALLOC, it just calls that. For our use, this would let us put Microvium in a shared code region (read-only, no globals) but still have it allocate memory that can be accounted to each the compartment that instantiates a JavaScript heap (so we could very cheaply have multiple hardware-isolated JavaScript VMs on a tiny device).

Can you give me an example to make it clearer?

#ifndef MICROVIUM_EXPORT
#    define MICROVIUM_EXPORT
#endif
...
MICROVIUM_EXPORT mvm_TeError mvm_restore(mvm_VM** result, MVM_LONG_PTR_TYPE snapshotBytecode, size_t bytecodeSize, void* context, mvm_TfResolveImport resolveImport);

We would then #define this to an attribute that specifies the calling convention that we want. If embedders don't care about this, it doesn't affect them.

Ok, sure. Do you want to take a look at this PR and tell me if it looks right for you?

#52

In particular, I'm not sure if MVM_EXPORT needs to be on both the header declaration and the implementation.

I've tested this and, with the following diff, am now able to run multiple JavaScript VMs, in different compartments, sharing the same code:

diff --git a/dist-c/microvium.h b/dist-c/microvium.h
index 29024d8..1985778 100644
--- a/dist-c/microvium.h
+++ b/dist-c/microvium.h
@@ -202,8 +202,8 @@ MVM_EXPORT void* mvm_getContext(mvm_VM* vm);
 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; }
+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; }

 /**
  * Roughly like the `typeof` operator in JS, except with distinct values for

I didn't notice when I looked at the PR that the export macro was added to static inline functions (which are not exported).

Yeah, sorry, good point. Thanks for the PR -- I've merged it.

I think this can be closed now - everything seems to be working nicely for us. Thanks!