mirror of
https://github.com/wren-lang/wren.git
synced 2026-01-10 13:48:40 +01:00
WrenValue -> WrenHandle.
This commit is contained in:
@ -40,11 +40,15 @@ It would be a shame if calling a method from C didn't have that same speed benef
|
||||
First, we create a handle that represents a "compiled" method signature. You do that using this:
|
||||
|
||||
:::c
|
||||
WrenValue* wrenMakeCallHandle(WrenVM* vm, const char* signature);
|
||||
WrenHandle* wrenMakeCallHandle(WrenVM* vm, const char* signature);
|
||||
|
||||
That takes a method signature as a string and gives you back an opaque handle that represents the compiled method symbol. Now you have a *reusable* handle that can be used to very quickly call a certain method given a receiver and some arguments.
|
||||
|
||||
This is just a regular WrenValue, which means you can hold onto it as long as you like. Typically, you'd call this once outside of your applications performance critical loops and reuse it as long as you need. Then, it is us up to you to release it when you no longer need it by calling `wrenReleaseValue()`.
|
||||
This is just a regular WrenHandle, which means you can hold onto it as long as
|
||||
you like. Typically, you'd call this once outside of your applications
|
||||
performance critical loops and reuse it as long as you need. Then, it is us up
|
||||
to you to release it when you no longer need it by calling
|
||||
`wrenReleaseHandle()`.
|
||||
|
||||
### Setting up a receiver
|
||||
|
||||
@ -52,7 +56,7 @@ OK, we have a method, but who are we calling it on? We need a receiver, and as y
|
||||
|
||||
Any object you store in that slot can be used as a receiver. You could even call `+` on a number by storing a number in there if you felt like it.
|
||||
|
||||
[last section]: slots-and-values.html
|
||||
[last section]: slots-and-handles.html
|
||||
|
||||
*Needing* to pick some kind of receiver from C might feel strange. C is procedural, so it's natural to want to just invoke a bare *function* from Wren, but Wren isn't procedural. Instead, if you want to define some executable operation that isn't logically tied to a specific object, the natural way is to define a static method on an appropriate class.
|
||||
|
||||
@ -69,7 +73,7 @@ So, very often, when you call a method from C, you'll be calling a static method
|
||||
|
||||
Assuming you declared that class at the top level, the C API [gives you a way to look it up][variable].
|
||||
|
||||
[variable]: slots-and-values.html#looking-up-variables
|
||||
[variable]: slots-and-handles.html#looking-up-variables
|
||||
|
||||
We can get a handle to the above class like so:
|
||||
|
||||
@ -84,12 +88,12 @@ We could do this every time we call `update()`, but, again, that's kind of slow
|
||||
// Load the class into slot 0.
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenGetVariable(vm, "main", "GameEngine", 0);
|
||||
WrenValue* gameEngine = wrenGetSlotValue(vm, 0);
|
||||
WrenHandle* gameEngine = wrenGetSlotHandle(vm, 0);
|
||||
|
||||
Now, each time we want to call a method on GameEngine, we store that value back in slot zero:
|
||||
|
||||
:::c
|
||||
wrenSetSlotValue(vm, 0, gameEngine);
|
||||
wrenSetSlotHandle(vm, 0, gameEngine);
|
||||
|
||||
Just like we hoisted `wrenMakeCallHandle()` out of our performance critical loop, we can hoist the call to `wrenGetVariable()` out. Of course, if your code isn't performance critical, you don't have to do this.
|
||||
|
||||
@ -105,7 +109,7 @@ We've got a receiver in slot zero now, next we need to pass in any other argumen
|
||||
We have all of the data in place, so all that's left is to pull the trigger and tell the VM to start running some code. There's one more function to call:
|
||||
|
||||
:::c
|
||||
WrenInterpretResult wrenCall(WrenVM* vm, WrenValue* method);
|
||||
WrenInterpretResult wrenCall(WrenVM* vm, WrenHandle* method);
|
||||
|
||||
It takes the method handle we created using `wrenMakeCallHandle()`. It assumes you have already set up the receiver and arguments in the slot array. Critically, it assumes you have as many arguments as the method signature defines. If you call a method like `takeThree(_,_,_)` and don't put three arguments in the slot array, bad things we'll happen.
|
||||
|
||||
@ -122,4 +126,4 @@ When `wrenCall()` returns, it leaves the slot array in place. In slot zero, you
|
||||
This is how you drive Wren from C, but how do you put control in Wren's hands? For that, you'll need the next section...
|
||||
|
||||
<a class="right" href="calling-c-from-wren.html">Calling C From Wren →</a>
|
||||
<a href="slots-and-values.html">← Slots and Values</a>
|
||||
<a href="slots-and-handles.html">← Slots and Handles</a>
|
||||
|
||||
@ -169,41 +169,13 @@ need to free any memory it allocated. You do that like so:
|
||||
After calling that, you obviously cannot use the `WrenVM*` you passed to it
|
||||
again. It's dead.
|
||||
|
||||
Note that Wren will yell at you if you still have any live [WrenValue][value]
|
||||
Note that Wren will yell at you if you still have any live [WrenHandle][handle]
|
||||
objects when you call this. This makes sure you haven't lost track of any of
|
||||
them (which leaks memory) and you don't try to use any of them after the VM has
|
||||
been freed.
|
||||
|
||||
[value]: slots-and-values.html#values
|
||||
[handle]: slots-and-handles.html#handles
|
||||
|
||||
Next, we'll learn to make that VM do useful stuff...
|
||||
|
||||
<a class="right" href="slots-and-values.html">Slots and Values →</a>
|
||||
|
||||
<!--
|
||||
- configuration
|
||||
- each field and what it means
|
||||
- preprocessor option
|
||||
- WREN_NAN_TAGGING
|
||||
- WREN_COMPUTED_GOTO
|
||||
- WREN_OPT_META
|
||||
- WREN_OPT_RANDOM
|
||||
- WREN_DEBUG_GC_STRESS 0
|
||||
- WREN_DEBUG_TRACE_MEMORY 0
|
||||
- WREN_DEBUG_TRACE_GC 0
|
||||
- WREN_DEBUG_DUMP_COMPILED_CODE 0
|
||||
- WREN_DEBUG_TRACE_INSTRUCTIONS 0
|
||||
- calling wren from c
|
||||
|
||||
Often a host application wants to load a bunch of Wren code into the VM and
|
||||
then periodically call a method. For example, a game engine might load all
|
||||
of the entity scripts into Wren. Then, each tick of the game loop, it calls
|
||||
Wren to tell all of the entities to update.
|
||||
|
||||
- calling c from wren
|
||||
- storing c data in wren objects
|
||||
- fibers and scheduling
|
||||
- app lifecycle, wren in charge
|
||||
- app lifecycle, c in charge
|
||||
|
||||
-->
|
||||
<a class="right" href="slots-and-handles.html">Slots and Handles →</a>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
^title Slots and Values
|
||||
^title Slots and Handles
|
||||
|
||||
With `wrenInterpret()`, we can execute code, but that code can't do anything
|
||||
particularly interesting. Out of the box, the VM is very isolated from the rest
|
||||
@ -8,7 +8,7 @@ laptop into a lap warmer, but that's about it.
|
||||
To make our Wren code *useful*, the VM needs to communicate with the outside
|
||||
world. Wren uses a single unified set of functions for passing data into and out
|
||||
of the VM. These functions are based on two fundamental concepts: **slots** and
|
||||
**values**.
|
||||
**handles**.
|
||||
|
||||
## The Slot Array
|
||||
|
||||
@ -154,7 +154,7 @@ hash the name and look it up in the module's string table. You might want to
|
||||
avoid calling this in the middle of a hot loop where performance is critical.
|
||||
|
||||
Instead, it's faster to look up the variable once outside the loop and store a
|
||||
reference to the object using a [WrenValue](#values).
|
||||
reference to the object using a [WrenHandle](#handles).
|
||||
|
||||
### Working with lists
|
||||
|
||||
@ -197,7 +197,7 @@ the list. This is kind of tedious, but it lets us use the same set of functions
|
||||
for moving values into slots of each primitive type. Otherwise, we'd need
|
||||
`wrenInsertInListDouble()`, `wrenInsertInListBool()`, etc.
|
||||
|
||||
## Values
|
||||
## Handles
|
||||
|
||||
Slots are pretty good for shuttling primitive data between C and Wren, but they
|
||||
have two limitations:
|
||||
@ -211,53 +211,51 @@ have two limitations:
|
||||
that aren't simple primitive ones. If you want to grab a reference to,
|
||||
say, an instance of some class, how do you do it?
|
||||
|
||||
To address those, we have WrenValue. A WrenValue is a handle that wraps a
|
||||
reference to an object of any kind—strings, numbers, instances of classes,
|
||||
collections, whatever.
|
||||
To address those, we have WrenHandle. A WrenHandle wraps a reference to an
|
||||
object of any kind—strings, numbers, instances of classes, collections,
|
||||
whatever.
|
||||
|
||||
*(Note: WrenValue will probably be renamed to WrenHandle soon.)*
|
||||
|
||||
You create a WrenValue using this:
|
||||
You create a WrenHandle using this:
|
||||
|
||||
:::c
|
||||
WrenValue* wrenGetSlotValue(WrenVM* vm, int slot);
|
||||
WrenHandle* wrenGetSlotHandle(WrenVM* vm, int slot);
|
||||
|
||||
This takes the object stored in the given slot, creates a new WrenValue to wrap
|
||||
This takes the object stored in the given slot, creates a new WrenHandle to wrap
|
||||
it, and returns a pointer to it back to you.
|
||||
|
||||
You can send that wrapped object back to Wren by calling:
|
||||
|
||||
:::c
|
||||
void wrenSetSlotValue(WrenVM* vm, int slot, WrenValue* value);
|
||||
void wrenSetSlotHandle(WrenVM* vm, int slot, WrenHandle* handle);
|
||||
|
||||
Note that this doesn't invalidate your WrenValue. You can still keep using it.
|
||||
Note that this doesn't invalidate your WrenHandle. You can still keep using it.
|
||||
|
||||
### Retaining and releasing values
|
||||
### Retaining and releasing handles
|
||||
|
||||
A WrenValue is an opaque wrapper around an object of any type, but just as
|
||||
A WrenHandle is an opaque wrapper around an object of any type, but just as
|
||||
important, it's a *persistent* one. When Wren gives you a pointer to a
|
||||
WrenValue, it guarantees that that pointer remains valid. You can keep it
|
||||
WrenHandle, it guarantees that that pointer remains valid. You can keep it
|
||||
around as long as you want. Even if a garbage collection occurs, Wren will
|
||||
ensure all of the WrenValues and the objects they wrap are kept safely in
|
||||
ensure all of the WrenHandles and the objects they wrap are kept safely in
|
||||
memory.
|
||||
|
||||
Internally, Wren keeps a list of all of the WrenValues that have been created.
|
||||
Internally, Wren keeps a list of all of the WrenHandles that have been created.
|
||||
That way, during garbage collection, it can find them all and make sure their
|
||||
objects aren't freed.
|
||||
|
||||
But what if you don't want it to be kept around any more? Since C relies on
|
||||
manual memory management, WrenValue does too. When you are done with one, you
|
||||
manual memory management, WrenHandle does too. When you are done with one, you
|
||||
must explicitly release it by calling:
|
||||
|
||||
:::c
|
||||
void wrenReleaseValue(WrenVM* vm, WrenValue* value);
|
||||
void wrenReleaseHandle(WrenVM* vm, WrenHandle* handle);
|
||||
|
||||
This does not immediately delete the wrapped object—after all, there may
|
||||
be other references to the same object in the program. It just invalidates the
|
||||
WrenValue wrapper itself. After you call this, you cannot use that pointer
|
||||
WrenHandle wrapper itself. After you call this, you cannot use that pointer
|
||||
again.
|
||||
|
||||
You must release every WrenValue you've created before shutting down the VM.
|
||||
You must release every WrenHandle you've created before shutting down the VM.
|
||||
Wren warns you if you don't, since it implies you've probably leaked a resource
|
||||
somewhere.
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
<h2>embedding</h2>
|
||||
<ul>
|
||||
<li><a href="./">Introduction</a></li>
|
||||
<li><a href="slots-and-values.html">Slots and Values</a></li>
|
||||
<li><a href="slots-and-handles.html">Slots and Handles</a></li>
|
||||
<li><a href="calling-wren-from-c.html">Calling Wren from C</a></li>
|
||||
<li><a href="calling-c-from-wren.html">Calling C from Wren</a></li>
|
||||
<li><a href="storing-c-data.html">Storing C Data</a></li>
|
||||
@ -44,7 +44,7 @@
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="./">Introduction</a></li>
|
||||
<li><a href="slots-and-values.html">Slots and Values</a></li>
|
||||
<li><a href="slots-and-handles.html">Slots and Handles</a></li>
|
||||
<li><a href="calling-wren-from-c.html">Calling Wren from C</a></li>
|
||||
<li><a href="calling-c-from-wren.html">Calling C from Wren</a></li>
|
||||
<li><a href="storing-c-data.html">Storing C Data</a></li>
|
||||
|
||||
@ -14,9 +14,9 @@ typedef struct WrenVM WrenVM;
|
||||
// A handle to a Wren object.
|
||||
//
|
||||
// This lets code outside of the VM hold a persistent reference to an object.
|
||||
// After a value is acquired, and until it is released, this ensures the
|
||||
// garbage collector will not reclaim it.
|
||||
typedef struct WrenValue WrenValue;
|
||||
// After a handle is acquired, and until it is released, this ensures the
|
||||
// garbage collector will not reclaim the object it references.
|
||||
typedef struct WrenHandle WrenHandle;
|
||||
|
||||
// A generic allocation function that handles all explicit memory management
|
||||
// used by Wren. It's used like so:
|
||||
@ -246,8 +246,8 @@ WrenInterpretResult wrenInterpret(WrenVM* vm, const char* source);
|
||||
// code using [wrenCall].
|
||||
//
|
||||
// When you are done with this handle, it must be released using
|
||||
// [wrenReleaseValue].
|
||||
WrenValue* wrenMakeCallHandle(WrenVM* vm, const char* signature);
|
||||
// [wrenReleaseHandle].
|
||||
WrenHandle* wrenMakeCallHandle(WrenVM* vm, const char* signature);
|
||||
|
||||
// Calls [method], using the receiver and arguments previously set up on the
|
||||
// stack.
|
||||
@ -259,17 +259,17 @@ WrenValue* wrenMakeCallHandle(WrenVM* vm, const char* signature);
|
||||
// signature.
|
||||
//
|
||||
// After this returns, you can access the return value from slot 0 on the stack.
|
||||
WrenInterpretResult wrenCall(WrenVM* vm, WrenValue* method);
|
||||
WrenInterpretResult wrenCall(WrenVM* vm, WrenHandle* method);
|
||||
|
||||
// Releases the reference stored in [value]. After calling this, [value] can no
|
||||
// longer be used.
|
||||
void wrenReleaseValue(WrenVM* vm, WrenValue* value);
|
||||
// Releases the reference stored in [handle]. After calling this, [handle] can
|
||||
// no longer be used.
|
||||
void wrenReleaseHandle(WrenVM* vm, WrenHandle* handle);
|
||||
|
||||
// The following functions are intended to be called from foreign methods or
|
||||
// finalizers. The interface Wren provides to a foreign method is like a
|
||||
// register machine: you are given a numbered array of slots that values can be
|
||||
// read from and written to. Values always live in a slot (unless explicitly
|
||||
// captured using wrenGetSlotValue(), which ensures the garbage collector can
|
||||
// captured using wrenGetSlotHandle(), which ensures the garbage collector can
|
||||
// find them.
|
||||
//
|
||||
// When your foreign function is called, you are given one slot for the receiver
|
||||
@ -357,8 +357,8 @@ const char* wrenGetSlotString(WrenVM* vm, int slot);
|
||||
// Creates a handle for the value stored in [slot].
|
||||
//
|
||||
// This will prevent the object that is referred to from being garbage collected
|
||||
// until the handle is released by calling [wrenReleaseValue()].
|
||||
WrenValue* wrenGetSlotValue(WrenVM* vm, int slot);
|
||||
// until the handle is released by calling [wrenReleaseHandle()].
|
||||
WrenHandle* wrenGetSlotHandle(WrenVM* vm, int slot);
|
||||
|
||||
// The following functions provide the return value for a foreign method back
|
||||
// to Wren. Like above, they may only be called during a foreign call invoked
|
||||
@ -406,10 +406,10 @@ void wrenSetSlotNull(WrenVM* vm, int slot);
|
||||
// should use [wrenSetSlotBytes()] instead.
|
||||
void wrenSetSlotString(WrenVM* vm, int slot, const char* text);
|
||||
|
||||
// Stores the value captured in [value] in [slot].
|
||||
// Stores the value captured in [handle] in [slot].
|
||||
//
|
||||
// This does not release the handle for the value.
|
||||
void wrenSetSlotValue(WrenVM* vm, int slot, WrenValue* value);
|
||||
void wrenSetSlotHandle(WrenVM* vm, int slot, WrenHandle* handle);
|
||||
|
||||
// Takes the value stored at [elementSlot] and inserts it into the list stored
|
||||
// at [listSlot] at [index].
|
||||
|
||||
@ -28,20 +28,20 @@
|
||||
|
||||
typedef struct sFileRequestData
|
||||
{
|
||||
WrenValue* fiber;
|
||||
WrenHandle* fiber;
|
||||
uv_buf_t buffer;
|
||||
} FileRequestData;
|
||||
|
||||
static const int stdinDescriptor = 0;
|
||||
|
||||
// Handle to the Stat class object.
|
||||
static WrenValue* statClass = NULL;
|
||||
static WrenHandle* statClass = NULL;
|
||||
|
||||
// Handle to the Stdin class object.
|
||||
static WrenValue* stdinClass = NULL;
|
||||
static WrenHandle* stdinClass = NULL;
|
||||
|
||||
// Handle to an onData_() method call. Called when libuv provides data on stdin.
|
||||
static WrenValue* stdinOnData = NULL;
|
||||
static WrenHandle* stdinOnData = NULL;
|
||||
|
||||
// The stream used to read from stdin. Initialized on the first read.
|
||||
static uv_stream_t* stdinStream = NULL;
|
||||
@ -61,13 +61,13 @@ static void shutdownStdin()
|
||||
|
||||
if (stdinClass != NULL)
|
||||
{
|
||||
wrenReleaseValue(getVM(), stdinClass);
|
||||
wrenReleaseHandle(getVM(), stdinClass);
|
||||
stdinClass = NULL;
|
||||
}
|
||||
|
||||
if (stdinOnData != NULL)
|
||||
{
|
||||
wrenReleaseValue(getVM(), stdinOnData);
|
||||
wrenReleaseHandle(getVM(), stdinOnData);
|
||||
stdinOnData = NULL;
|
||||
}
|
||||
|
||||
@ -80,7 +80,7 @@ void ioShutdown()
|
||||
|
||||
if (statClass != NULL)
|
||||
{
|
||||
wrenReleaseValue(getVM(), statClass);
|
||||
wrenReleaseHandle(getVM(), statClass);
|
||||
statClass = NULL;
|
||||
}
|
||||
}
|
||||
@ -94,7 +94,7 @@ static bool handleRequestError(uv_fs_t* request)
|
||||
if (request->result >= 0) return false;
|
||||
|
||||
FileRequestData* data = (FileRequestData*)request->data;
|
||||
WrenValue* fiber = (WrenValue*)data->fiber;
|
||||
WrenHandle* fiber = (WrenHandle*)data->fiber;
|
||||
|
||||
schedulerResumeError(fiber, uv_strerror((int)request->result));
|
||||
|
||||
@ -105,7 +105,7 @@ static bool handleRequestError(uv_fs_t* request)
|
||||
}
|
||||
|
||||
// Allocates a new request that resumes [fiber] when it completes.
|
||||
uv_fs_t* createRequest(WrenValue* fiber)
|
||||
uv_fs_t* createRequest(WrenHandle* fiber)
|
||||
{
|
||||
uv_fs_t* request = (uv_fs_t*)malloc(sizeof(uv_fs_t));
|
||||
|
||||
@ -119,10 +119,10 @@ uv_fs_t* createRequest(WrenValue* fiber)
|
||||
// Releases resources used by [request].
|
||||
//
|
||||
// Returns the fiber that should be resumed after [request] completes.
|
||||
WrenValue* freeRequest(uv_fs_t* request)
|
||||
WrenHandle* freeRequest(uv_fs_t* request)
|
||||
{
|
||||
FileRequestData* data = (FileRequestData*)request->data;
|
||||
WrenValue* fiber = data->fiber;
|
||||
WrenHandle* fiber = data->fiber;
|
||||
|
||||
free(data);
|
||||
uv_fs_req_cleanup(request);
|
||||
@ -154,7 +154,7 @@ static void directoryListCallback(uv_fs_t* request)
|
||||
void directoryList(WrenVM* vm)
|
||||
{
|
||||
const char* path = wrenGetSlotString(vm, 1);
|
||||
uv_fs_t* request = createRequest(wrenGetSlotValue(vm, 2));
|
||||
uv_fs_t* request = createRequest(wrenGetSlotHandle(vm, 2));
|
||||
|
||||
// TODO: Check return.
|
||||
uv_fs_scandir(getLoop(), request, path, 0, directoryListCallback);
|
||||
@ -189,7 +189,7 @@ static void fileDeleteCallback(uv_fs_t* request)
|
||||
void fileDelete(WrenVM* vm)
|
||||
{
|
||||
const char* path = wrenGetSlotString(vm, 1);
|
||||
uv_fs_t* request = createRequest(wrenGetSlotValue(vm, 2));
|
||||
uv_fs_t* request = createRequest(wrenGetSlotHandle(vm, 2));
|
||||
|
||||
// TODO: Check return.
|
||||
uv_fs_unlink(getLoop(), request, path, fileDeleteCallback);
|
||||
@ -227,7 +227,7 @@ void fileOpen(WrenVM* vm)
|
||||
{
|
||||
const char* path = wrenGetSlotString(vm, 1);
|
||||
int flags = (int)wrenGetSlotDouble(vm, 2);
|
||||
uv_fs_t* request = createRequest(wrenGetSlotValue(vm, 3));
|
||||
uv_fs_t* request = createRequest(wrenGetSlotHandle(vm, 3));
|
||||
|
||||
// TODO: Allow controlling access.
|
||||
uv_fs_open(getLoop(), request, path, mapFileFlags(flags), S_IRUSR | S_IWUSR,
|
||||
@ -248,7 +248,7 @@ static void fileSizeCallback(uv_fs_t* request)
|
||||
void fileSizePath(WrenVM* vm)
|
||||
{
|
||||
const char* path = wrenGetSlotString(vm, 1);
|
||||
uv_fs_t* request = createRequest(wrenGetSlotValue(vm, 2));
|
||||
uv_fs_t* request = createRequest(wrenGetSlotHandle(vm, 2));
|
||||
uv_fs_stat(getLoop(), request, path, fileSizeCallback);
|
||||
}
|
||||
|
||||
@ -274,7 +274,7 @@ void fileClose(WrenVM* vm)
|
||||
// Mark it closed immediately.
|
||||
*foreign = -1;
|
||||
|
||||
uv_fs_t* request = createRequest(wrenGetSlotValue(vm, 1));
|
||||
uv_fs_t* request = createRequest(wrenGetSlotHandle(vm, 1));
|
||||
uv_fs_close(getLoop(), request, fd, fileCloseCallback);
|
||||
wrenSetSlotBool(vm, 0, false);
|
||||
}
|
||||
@ -307,7 +307,7 @@ static void fileReadBytesCallback(uv_fs_t* request)
|
||||
|
||||
void fileReadBytes(WrenVM* vm)
|
||||
{
|
||||
uv_fs_t* request = createRequest(wrenGetSlotValue(vm, 3));
|
||||
uv_fs_t* request = createRequest(wrenGetSlotHandle(vm, 3));
|
||||
|
||||
int fd = *(int*)wrenGetSlotForeign(vm, 0);
|
||||
// TODO: Assert fd != -1.
|
||||
@ -336,7 +336,7 @@ static void realPathCallback(uv_fs_t* request)
|
||||
void fileRealPath(WrenVM* vm)
|
||||
{
|
||||
const char* path = wrenGetSlotString(vm, 1);
|
||||
uv_fs_t* request = createRequest(wrenGetSlotValue(vm, 2));
|
||||
uv_fs_t* request = createRequest(wrenGetSlotHandle(vm, 2));
|
||||
uv_fs_realpath(getLoop(), request, path, realPathCallback);
|
||||
}
|
||||
|
||||
@ -353,11 +353,11 @@ static void statCallback(uv_fs_t* request)
|
||||
if (statClass == NULL)
|
||||
{
|
||||
wrenGetVariable(vm, "io", "Stat", 0);
|
||||
statClass = wrenGetSlotValue(vm, 0);
|
||||
statClass = wrenGetSlotHandle(vm, 0);
|
||||
}
|
||||
|
||||
// Create a foreign Stat object to store the stat struct.
|
||||
wrenSetSlotValue(vm, 2, statClass);
|
||||
wrenSetSlotHandle(vm, 2, statClass);
|
||||
wrenSetSlotNewForeign(vm, 2, 2, sizeof(uv_stat_t));
|
||||
|
||||
// Copy the stat data.
|
||||
@ -371,14 +371,14 @@ static void statCallback(uv_fs_t* request)
|
||||
void fileStat(WrenVM* vm)
|
||||
{
|
||||
int fd = *(int*)wrenGetSlotForeign(vm, 0);
|
||||
uv_fs_t* request = createRequest(wrenGetSlotValue(vm, 1));
|
||||
uv_fs_t* request = createRequest(wrenGetSlotHandle(vm, 1));
|
||||
uv_fs_fstat(getLoop(), request, fd, statCallback);
|
||||
}
|
||||
|
||||
void fileSize(WrenVM* vm)
|
||||
{
|
||||
int fd = *(int*)wrenGetSlotForeign(vm, 0);
|
||||
uv_fs_t* request = createRequest(wrenGetSlotValue(vm, 1));
|
||||
uv_fs_t* request = createRequest(wrenGetSlotHandle(vm, 1));
|
||||
uv_fs_fstat(getLoop(), request, fd, fileSizeCallback);
|
||||
}
|
||||
|
||||
@ -398,12 +398,12 @@ void fileWriteBytes(WrenVM* vm)
|
||||
int length;
|
||||
const char* bytes = wrenGetSlotBytes(vm, 1, &length);
|
||||
size_t offset = (size_t)wrenGetSlotDouble(vm, 2);
|
||||
uv_fs_t* request = createRequest(wrenGetSlotValue(vm, 3));
|
||||
uv_fs_t* request = createRequest(wrenGetSlotHandle(vm, 3));
|
||||
|
||||
FileRequestData* data = (FileRequestData*)request->data;
|
||||
|
||||
data->buffer.len = length;
|
||||
// TODO: Instead of copying, just create a WrenValue for the byte string and
|
||||
// TODO: Instead of copying, just create a WrenHandle for the byte string and
|
||||
// hold on to it in the request until the write is done.
|
||||
// TODO: Handle allocation failure.
|
||||
data->buffer.base = (char*)malloc(length);
|
||||
@ -416,7 +416,7 @@ void fileWriteBytes(WrenVM* vm)
|
||||
void statPath(WrenVM* vm)
|
||||
{
|
||||
const char* path = wrenGetSlotString(vm, 1);
|
||||
uv_fs_t* request = createRequest(wrenGetSlotValue(vm, 2));
|
||||
uv_fs_t* request = createRequest(wrenGetSlotHandle(vm, 2));
|
||||
uv_fs_stat(getLoop(), request, path, statCallback);
|
||||
}
|
||||
|
||||
@ -556,7 +556,7 @@ static void stdinReadCallback(uv_stream_t* stream, ssize_t numRead,
|
||||
{
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenGetVariable(vm, "io", "Stdin", 0);
|
||||
stdinClass = wrenGetSlotValue(vm, 0);
|
||||
stdinClass = wrenGetSlotHandle(vm, 0);
|
||||
}
|
||||
|
||||
if (stdinOnData == NULL)
|
||||
@ -568,7 +568,7 @@ static void stdinReadCallback(uv_stream_t* stream, ssize_t numRead,
|
||||
if (numRead == UV_EOF)
|
||||
{
|
||||
wrenEnsureSlots(vm, 2);
|
||||
wrenSetSlotValue(vm, 0, stdinClass);
|
||||
wrenSetSlotHandle(vm, 0, stdinClass);
|
||||
wrenSetSlotNull(vm, 1);
|
||||
wrenCall(vm, stdinOnData);
|
||||
|
||||
@ -582,7 +582,7 @@ static void stdinReadCallback(uv_stream_t* stream, ssize_t numRead,
|
||||
// embedding API supported a way to *give* it bytes that were previously
|
||||
// allocated using Wren's own allocator.
|
||||
wrenEnsureSlots(vm, 2);
|
||||
wrenSetSlotValue(vm, 0, stdinClass);
|
||||
wrenSetSlotHandle(vm, 0, stdinClass);
|
||||
wrenSetSlotBytes(vm, 1, buffer->base, numRead);
|
||||
wrenCall(vm, stdinOnData);
|
||||
|
||||
|
||||
@ -8,16 +8,16 @@
|
||||
#include "vm.h"
|
||||
|
||||
// A handle to the "Scheduler" class object. Used to call static methods on it.
|
||||
static WrenValue* schedulerClass;
|
||||
static WrenHandle* schedulerClass;
|
||||
|
||||
// This method resumes a fiber that is suspended waiting on an asynchronous
|
||||
// operation. The first resumes it with zero arguments, and the second passes
|
||||
// one.
|
||||
static WrenValue* resume1;
|
||||
static WrenValue* resume2;
|
||||
static WrenValue* resumeError;
|
||||
static WrenHandle* resume1;
|
||||
static WrenHandle* resume2;
|
||||
static WrenHandle* resumeError;
|
||||
|
||||
static void resume(WrenValue* method)
|
||||
static void resume(WrenHandle* method)
|
||||
{
|
||||
WrenInterpretResult result = wrenCall(getVM(), method);
|
||||
|
||||
@ -34,20 +34,20 @@ void schedulerCaptureMethods(WrenVM* vm)
|
||||
{
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenGetVariable(vm, "scheduler", "Scheduler", 0);
|
||||
schedulerClass = wrenGetSlotValue(vm, 0);
|
||||
schedulerClass = wrenGetSlotHandle(vm, 0);
|
||||
|
||||
resume1 = wrenMakeCallHandle(vm, "resume_(_)");
|
||||
resume2 = wrenMakeCallHandle(vm, "resume_(_,_)");
|
||||
resumeError = wrenMakeCallHandle(vm, "resumeError_(_,_)");
|
||||
}
|
||||
|
||||
void schedulerResume(WrenValue* fiber, bool hasArgument)
|
||||
void schedulerResume(WrenHandle* fiber, bool hasArgument)
|
||||
{
|
||||
WrenVM* vm = getVM();
|
||||
wrenEnsureSlots(vm, 2 + (hasArgument ? 1 : 0));
|
||||
wrenSetSlotValue(vm, 0, schedulerClass);
|
||||
wrenSetSlotValue(vm, 1, fiber);
|
||||
wrenReleaseValue(vm, fiber);
|
||||
wrenSetSlotHandle(vm, 0, schedulerClass);
|
||||
wrenSetSlotHandle(vm, 1, fiber);
|
||||
wrenReleaseHandle(vm, fiber);
|
||||
|
||||
// If we don't need to wait for an argument to be stored on the stack, resume
|
||||
// it now.
|
||||
@ -59,7 +59,7 @@ void schedulerFinishResume()
|
||||
resume(resume2);
|
||||
}
|
||||
|
||||
void schedulerResumeError(WrenValue* fiber, const char* error)
|
||||
void schedulerResumeError(WrenHandle* fiber, const char* error)
|
||||
{
|
||||
schedulerResume(fiber, true);
|
||||
wrenSetSlotString(getVM(), 2, error);
|
||||
@ -72,8 +72,8 @@ void schedulerShutdown()
|
||||
if (schedulerClass == NULL) return;
|
||||
|
||||
WrenVM* vm = getVM();
|
||||
wrenReleaseValue(vm, schedulerClass);
|
||||
wrenReleaseValue(vm, resume1);
|
||||
wrenReleaseValue(vm, resume2);
|
||||
wrenReleaseValue(vm, resumeError);
|
||||
wrenReleaseHandle(vm, schedulerClass);
|
||||
wrenReleaseHandle(vm, resume1);
|
||||
wrenReleaseHandle(vm, resume2);
|
||||
wrenReleaseHandle(vm, resumeError);
|
||||
}
|
||||
|
||||
@ -10,10 +10,10 @@
|
||||
// on the stack and then call [schedulerFinishResume] to complete the call.
|
||||
//
|
||||
// Otherwise, the call resumes immediately. Releases [fiber] when called.
|
||||
void schedulerResume(WrenValue* fiber, bool hasArgument);
|
||||
void schedulerResume(WrenHandle* fiber, bool hasArgument);
|
||||
|
||||
void schedulerFinishResume();
|
||||
void schedulerResumeError(WrenValue* fiber, const char* error);
|
||||
void schedulerResumeError(WrenHandle* fiber, const char* error);
|
||||
|
||||
void schedulerShutdown();
|
||||
|
||||
|
||||
@ -16,7 +16,7 @@ static void timerCloseCallback(uv_handle_t* handle)
|
||||
// Called by libuv when the timer has completed.
|
||||
static void timerCallback(uv_timer_t* handle)
|
||||
{
|
||||
WrenValue* fiber = (WrenValue*)handle->data;
|
||||
WrenHandle* fiber = (WrenHandle*)handle->data;
|
||||
|
||||
// Tell libuv that we don't need the timer anymore.
|
||||
uv_close((uv_handle_t*)handle, timerCloseCallback);
|
||||
@ -28,7 +28,7 @@ static void timerCallback(uv_timer_t* handle)
|
||||
void timerStartTimer(WrenVM* vm)
|
||||
{
|
||||
int milliseconds = (int)wrenGetSlotDouble(vm, 1);
|
||||
WrenValue* fiber = wrenGetSlotValue(vm, 2);
|
||||
WrenHandle* fiber = wrenGetSlotHandle(vm, 2);
|
||||
|
||||
// Store the fiber to resume when the timer completes.
|
||||
uv_timer_t* handle = (uv_timer_t*)malloc(sizeof(uv_timer_t));
|
||||
|
||||
@ -99,7 +99,7 @@ void wrenFreeVM(WrenVM* vm)
|
||||
// Tell the user if they didn't free any handles. We don't want to just free
|
||||
// them here because the host app may still have pointers to them that they
|
||||
// may try to use. Better to tell them about the bug early.
|
||||
ASSERT(vm->valueHandles == NULL, "All values have not been released.");
|
||||
ASSERT(vm->handles == NULL, "All handles have not been released.");
|
||||
|
||||
wrenSymbolTableClear(vm, &vm->methodNames);
|
||||
|
||||
@ -138,12 +138,12 @@ void wrenCollectGarbage(WrenVM* vm)
|
||||
// The current fiber.
|
||||
wrenGrayObj(vm, (Obj*)vm->fiber);
|
||||
|
||||
// The value handles.
|
||||
for (WrenValue* value = vm->valueHandles;
|
||||
value != NULL;
|
||||
value = value->next)
|
||||
// The handles.
|
||||
for (WrenHandle* handle = vm->handles;
|
||||
handle != NULL;
|
||||
handle = handle->next)
|
||||
{
|
||||
wrenGrayValue(vm, value->value);
|
||||
wrenGrayValue(vm, handle->value);
|
||||
}
|
||||
|
||||
// Any object the compiler is using (if there is one).
|
||||
@ -1195,7 +1195,7 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
|
||||
#undef READ_SHORT
|
||||
}
|
||||
|
||||
WrenValue* wrenMakeCallHandle(WrenVM* vm, const char* signature)
|
||||
WrenHandle* wrenMakeCallHandle(WrenVM* vm, const char* signature)
|
||||
{
|
||||
ASSERT(signature != NULL, "Signature cannot be NULL.");
|
||||
|
||||
@ -1231,7 +1231,7 @@ WrenValue* wrenMakeCallHandle(WrenVM* vm, const char* signature)
|
||||
|
||||
// Wrap the function in a closure and then in a handle. Do this here so it
|
||||
// doesn't get collected as we fill it in.
|
||||
WrenValue* value = wrenCaptureValue(vm, OBJ_VAL(fn));
|
||||
WrenHandle* value = wrenMakeHandle(vm, OBJ_VAL(fn));
|
||||
value->value = OBJ_VAL(wrenNewClosure(vm, fn));
|
||||
|
||||
wrenByteBufferWrite(vm, &fn->code, (uint8_t)(CODE_CALL_0 + numParams));
|
||||
@ -1245,7 +1245,7 @@ WrenValue* wrenMakeCallHandle(WrenVM* vm, const char* signature)
|
||||
return value;
|
||||
}
|
||||
|
||||
WrenInterpretResult wrenCall(WrenVM* vm, WrenValue* method)
|
||||
WrenInterpretResult wrenCall(WrenVM* vm, WrenHandle* method)
|
||||
{
|
||||
ASSERT(method != NULL, "Method cannot be NULL.");
|
||||
ASSERT(IS_CLOSURE(method->value), "Method must be a method handle.");
|
||||
@ -1266,42 +1266,42 @@ WrenInterpretResult wrenCall(WrenVM* vm, WrenValue* method)
|
||||
return runInterpreter(vm, vm->fiber);
|
||||
}
|
||||
|
||||
WrenValue* wrenCaptureValue(WrenVM* vm, Value value)
|
||||
WrenHandle* wrenMakeHandle(WrenVM* vm, Value value)
|
||||
{
|
||||
if (IS_OBJ(value)) wrenPushRoot(vm, AS_OBJ(value));
|
||||
|
||||
// Make a handle for it.
|
||||
WrenValue* wrappedValue = ALLOCATE(vm, WrenValue);
|
||||
wrappedValue->value = value;
|
||||
WrenHandle* handle = ALLOCATE(vm, WrenHandle);
|
||||
handle->value = value;
|
||||
|
||||
if (IS_OBJ(value)) wrenPopRoot(vm);
|
||||
|
||||
// Add it to the front of the linked list of handles.
|
||||
if (vm->valueHandles != NULL) vm->valueHandles->prev = wrappedValue;
|
||||
wrappedValue->prev = NULL;
|
||||
wrappedValue->next = vm->valueHandles;
|
||||
vm->valueHandles = wrappedValue;
|
||||
if (vm->handles != NULL) vm->handles->prev = handle;
|
||||
handle->prev = NULL;
|
||||
handle->next = vm->handles;
|
||||
vm->handles = handle;
|
||||
|
||||
return wrappedValue;
|
||||
return handle;
|
||||
}
|
||||
|
||||
void wrenReleaseValue(WrenVM* vm, WrenValue* value)
|
||||
void wrenReleaseHandle(WrenVM* vm, WrenHandle* handle)
|
||||
{
|
||||
ASSERT(value != NULL, "Value cannot be NULL.");
|
||||
ASSERT(handle != NULL, "Handle cannot be NULL.");
|
||||
|
||||
// Update the VM's head pointer if we're releasing the first handle.
|
||||
if (vm->valueHandles == value) vm->valueHandles = value->next;
|
||||
if (vm->handles == handle) vm->handles = handle->next;
|
||||
|
||||
// Unlink it from the list.
|
||||
if (value->prev != NULL) value->prev->next = value->next;
|
||||
if (value->next != NULL) value->next->prev = value->prev;
|
||||
if (handle->prev != NULL) handle->prev->next = handle->next;
|
||||
if (handle->next != NULL) handle->next->prev = handle->prev;
|
||||
|
||||
// Clear it out. This isn't strictly necessary since we're going to free it,
|
||||
// but it makes for easier debugging.
|
||||
value->prev = NULL;
|
||||
value->next = NULL;
|
||||
value->value = NULL_VAL;
|
||||
DEALLOCATE(vm, value);
|
||||
handle->prev = NULL;
|
||||
handle->next = NULL;
|
||||
handle->value = NULL_VAL;
|
||||
DEALLOCATE(vm, handle);
|
||||
}
|
||||
|
||||
WrenInterpretResult wrenInterpret(WrenVM* vm, const char* source)
|
||||
@ -1555,10 +1555,10 @@ const char* wrenGetSlotString(WrenVM* vm, int slot)
|
||||
return AS_CSTRING(vm->apiStack[slot]);
|
||||
}
|
||||
|
||||
WrenValue* wrenGetSlotValue(WrenVM* vm, int slot)
|
||||
WrenHandle* wrenGetSlotHandle(WrenVM* vm, int slot)
|
||||
{
|
||||
validateApiSlot(vm, slot);
|
||||
return wrenCaptureValue(vm, vm->apiStack[slot]);
|
||||
return wrenMakeHandle(vm, vm->apiStack[slot]);
|
||||
}
|
||||
|
||||
// Stores [value] in [slot] in the foreign call stack.
|
||||
@ -1616,11 +1616,11 @@ void wrenSetSlotString(WrenVM* vm, int slot, const char* text)
|
||||
setSlot(vm, slot, wrenNewString(vm, text, strlen(text)));
|
||||
}
|
||||
|
||||
void wrenSetSlotValue(WrenVM* vm, int slot, WrenValue* value)
|
||||
void wrenSetSlotHandle(WrenVM* vm, int slot, WrenHandle* handle)
|
||||
{
|
||||
ASSERT(value != NULL, "Value cannot be NULL.");
|
||||
ASSERT(handle != NULL, "Handle cannot be NULL.");
|
||||
|
||||
setSlot(vm, slot, value->value);
|
||||
setSlot(vm, slot, handle->value);
|
||||
}
|
||||
|
||||
void wrenInsertInList(WrenVM* vm, int listSlot, int index, int elementSlot)
|
||||
@ -1639,8 +1639,6 @@ void wrenInsertInList(WrenVM* vm, int listSlot, int index, int elementSlot)
|
||||
wrenListInsert(vm, list, vm->apiStack[elementSlot], index);
|
||||
}
|
||||
|
||||
// TODO: Maybe just have this always return a WrenValue* instead of having to
|
||||
// deal with slots?
|
||||
void wrenGetVariable(WrenVM* vm, const char* module, const char* name,
|
||||
int slot)
|
||||
{
|
||||
|
||||
@ -20,12 +20,12 @@ typedef enum
|
||||
// A handle to a value, basically just a linked list of extra GC roots.
|
||||
//
|
||||
// Note that even non-heap-allocated values can be stored here.
|
||||
struct WrenValue
|
||||
struct WrenHandle
|
||||
{
|
||||
Value value;
|
||||
|
||||
WrenValue* prev;
|
||||
WrenValue* next;
|
||||
WrenHandle* prev;
|
||||
WrenHandle* next;
|
||||
};
|
||||
|
||||
struct WrenVM
|
||||
@ -80,9 +80,9 @@ struct WrenVM
|
||||
|
||||
int numTempRoots;
|
||||
|
||||
// Pointer to the first node in the linked list of active value handles or
|
||||
// NULL if there are no handles.
|
||||
WrenValue* valueHandles;
|
||||
// Pointer to the first node in the linked list of active handles or NULL if
|
||||
// there are none.
|
||||
WrenHandle* handles;
|
||||
|
||||
// Pointer to the bottom of the range of stack slots available for use from
|
||||
// the C API. During a foreign method, this will be in the stack of the fiber
|
||||
@ -128,8 +128,8 @@ void* wrenReallocate(WrenVM* vm, void* memory, size_t oldSize, size_t newSize);
|
||||
// Invoke the finalizer for the foreign object referenced by [foreign].
|
||||
void wrenFinalizeForeign(WrenVM* vm, ObjForeign* foreign);
|
||||
|
||||
// Creates a new [WrenValue] for [value].
|
||||
WrenValue* wrenCaptureValue(WrenVM* vm, Value value);
|
||||
// Creates a new [WrenHandle] for [value].
|
||||
WrenHandle* wrenMakeHandle(WrenVM* vm, Value value);
|
||||
|
||||
// Executes [source] in the context of [module].
|
||||
WrenInterpretResult wrenInterpretInModule(WrenVM* vm, const char* module,
|
||||
|
||||
@ -32,11 +32,11 @@ static void call(WrenVM* vm)
|
||||
|
||||
wrenInterpret(otherVM, testScript);
|
||||
|
||||
WrenValue* method = wrenMakeCallHandle(otherVM, "method(_,_,_,_)");
|
||||
WrenHandle* method = wrenMakeCallHandle(otherVM, "method(_,_,_,_)");
|
||||
|
||||
wrenEnsureSlots(otherVM, 1);
|
||||
wrenGetVariable(otherVM, "main", "Test", 0);
|
||||
WrenValue* testClass = wrenGetSlotValue(otherVM, 0);
|
||||
WrenHandle* testClass = wrenGetSlotHandle(otherVM, 0);
|
||||
|
||||
double startTime = (double)clock() / CLOCKS_PER_SEC;
|
||||
|
||||
@ -44,7 +44,7 @@ static void call(WrenVM* vm)
|
||||
for (int i = 0; i < iterations; i++)
|
||||
{
|
||||
wrenEnsureSlots(otherVM, 5);
|
||||
wrenSetSlotValue(otherVM, 0, testClass);
|
||||
wrenSetSlotHandle(otherVM, 0, testClass);
|
||||
wrenSetSlotDouble(otherVM, 1, 1.0);
|
||||
wrenSetSlotDouble(otherVM, 2, 2.0);
|
||||
wrenSetSlotDouble(otherVM, 3, 3.0);
|
||||
@ -57,8 +57,8 @@ static void call(WrenVM* vm)
|
||||
|
||||
double elapsed = (double)clock() / CLOCKS_PER_SEC - startTime;
|
||||
|
||||
wrenReleaseValue(otherVM, testClass);
|
||||
wrenReleaseValue(otherVM, method);
|
||||
wrenReleaseHandle(otherVM, testClass);
|
||||
wrenReleaseHandle(otherVM, method);
|
||||
wrenFreeVM(otherVM);
|
||||
|
||||
if (result == (1.0 + 2.0 + 3.0 + 4.0) * iterations)
|
||||
|
||||
@ -7,117 +7,117 @@ void callRunTests(WrenVM* vm)
|
||||
{
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenGetVariable(vm, "main", "Call", 0);
|
||||
WrenValue* callClass = wrenGetSlotValue(vm, 0);
|
||||
WrenHandle* callClass = wrenGetSlotHandle(vm, 0);
|
||||
|
||||
WrenValue* noParams = wrenMakeCallHandle(vm, "noParams");
|
||||
WrenValue* zero = wrenMakeCallHandle(vm, "zero()");
|
||||
WrenValue* one = wrenMakeCallHandle(vm, "one(_)");
|
||||
WrenValue* two = wrenMakeCallHandle(vm, "two(_,_)");
|
||||
WrenValue* unary = wrenMakeCallHandle(vm, "-");
|
||||
WrenValue* binary = wrenMakeCallHandle(vm, "-(_)");
|
||||
WrenValue* subscript = wrenMakeCallHandle(vm, "[_,_]");
|
||||
WrenValue* subscriptSet = wrenMakeCallHandle(vm, "[_,_]=(_)");
|
||||
WrenHandle* noParams = wrenMakeCallHandle(vm, "noParams");
|
||||
WrenHandle* zero = wrenMakeCallHandle(vm, "zero()");
|
||||
WrenHandle* one = wrenMakeCallHandle(vm, "one(_)");
|
||||
WrenHandle* two = wrenMakeCallHandle(vm, "two(_,_)");
|
||||
WrenHandle* unary = wrenMakeCallHandle(vm, "-");
|
||||
WrenHandle* binary = wrenMakeCallHandle(vm, "-(_)");
|
||||
WrenHandle* subscript = wrenMakeCallHandle(vm, "[_,_]");
|
||||
WrenHandle* subscriptSet = wrenMakeCallHandle(vm, "[_,_]=(_)");
|
||||
|
||||
// Different arity.
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenSetSlotValue(vm, 0, callClass);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenCall(vm, noParams);
|
||||
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenSetSlotValue(vm, 0, callClass);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenCall(vm, zero);
|
||||
|
||||
wrenEnsureSlots(vm, 2);
|
||||
wrenSetSlotValue(vm, 0, callClass);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenSetSlotDouble(vm, 1, 1.0);
|
||||
wrenCall(vm, one);
|
||||
|
||||
wrenEnsureSlots(vm, 3);
|
||||
wrenSetSlotValue(vm, 0, callClass);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenSetSlotDouble(vm, 1, 1.0);
|
||||
wrenSetSlotDouble(vm, 2, 2.0);
|
||||
wrenCall(vm, two);
|
||||
|
||||
// Operators.
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenSetSlotValue(vm, 0, callClass);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenCall(vm, unary);
|
||||
|
||||
wrenEnsureSlots(vm, 2);
|
||||
wrenSetSlotValue(vm, 0, callClass);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenSetSlotDouble(vm, 1, 1.0);
|
||||
wrenCall(vm, binary);
|
||||
|
||||
wrenEnsureSlots(vm, 3);
|
||||
wrenSetSlotValue(vm, 0, callClass);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenSetSlotDouble(vm, 1, 1.0);
|
||||
wrenSetSlotDouble(vm, 2, 2.0);
|
||||
wrenCall(vm, subscript);
|
||||
|
||||
wrenEnsureSlots(vm, 4);
|
||||
wrenSetSlotValue(vm, 0, callClass);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenSetSlotDouble(vm, 1, 1.0);
|
||||
wrenSetSlotDouble(vm, 2, 2.0);
|
||||
wrenSetSlotDouble(vm, 3, 3.0);
|
||||
wrenCall(vm, subscriptSet);
|
||||
|
||||
// Returning a value.
|
||||
WrenValue* getValue = wrenMakeCallHandle(vm, "getValue()");
|
||||
WrenHandle* getValue = wrenMakeCallHandle(vm, "getValue()");
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenSetSlotValue(vm, 0, callClass);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenCall(vm, getValue);
|
||||
WrenValue* value = wrenGetSlotValue(vm, 0);
|
||||
WrenHandle* value = wrenGetSlotHandle(vm, 0);
|
||||
|
||||
// Different argument types.
|
||||
wrenEnsureSlots(vm, 3);
|
||||
wrenSetSlotValue(vm, 0, callClass);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenSetSlotBool(vm, 1, true);
|
||||
wrenSetSlotBool(vm, 2, false);
|
||||
wrenCall(vm, two);
|
||||
|
||||
wrenEnsureSlots(vm, 3);
|
||||
wrenSetSlotValue(vm, 0, callClass);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenSetSlotDouble(vm, 1, 1.2);
|
||||
wrenSetSlotDouble(vm, 2, 3.4);
|
||||
wrenCall(vm, two);
|
||||
|
||||
wrenEnsureSlots(vm, 3);
|
||||
wrenSetSlotValue(vm, 0, callClass);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenSetSlotString(vm, 1, "string");
|
||||
wrenSetSlotString(vm, 2, "another");
|
||||
wrenCall(vm, two);
|
||||
|
||||
wrenEnsureSlots(vm, 3);
|
||||
wrenSetSlotValue(vm, 0, callClass);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenSetSlotNull(vm, 1);
|
||||
wrenSetSlotValue(vm, 2, value);
|
||||
wrenSetSlotHandle(vm, 2, value);
|
||||
wrenCall(vm, two);
|
||||
|
||||
// Truncate a string, or allow null bytes.
|
||||
wrenEnsureSlots(vm, 3);
|
||||
wrenSetSlotValue(vm, 0, callClass);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
wrenSetSlotBytes(vm, 1, "string", 3);
|
||||
wrenSetSlotBytes(vm, 2, "b\0y\0t\0e", 7);
|
||||
wrenCall(vm, two);
|
||||
|
||||
// Call ignores with extra temporary slots on stack.
|
||||
wrenEnsureSlots(vm, 10);
|
||||
wrenSetSlotValue(vm, 0, callClass);
|
||||
wrenSetSlotHandle(vm, 0, callClass);
|
||||
for (int i = 1; i < 10; i++)
|
||||
{
|
||||
wrenSetSlotDouble(vm, i, i * 0.1);
|
||||
}
|
||||
wrenCall(vm, one);
|
||||
|
||||
wrenReleaseValue(vm, callClass);
|
||||
wrenReleaseValue(vm, noParams);
|
||||
wrenReleaseValue(vm, zero);
|
||||
wrenReleaseValue(vm, one);
|
||||
wrenReleaseValue(vm, two);
|
||||
wrenReleaseValue(vm, getValue);
|
||||
wrenReleaseValue(vm, value);
|
||||
wrenReleaseValue(vm, unary);
|
||||
wrenReleaseValue(vm, binary);
|
||||
wrenReleaseValue(vm, subscript);
|
||||
wrenReleaseValue(vm, subscriptSet);
|
||||
wrenReleaseHandle(vm, callClass);
|
||||
wrenReleaseHandle(vm, noParams);
|
||||
wrenReleaseHandle(vm, zero);
|
||||
wrenReleaseHandle(vm, one);
|
||||
wrenReleaseHandle(vm, two);
|
||||
wrenReleaseHandle(vm, getValue);
|
||||
wrenReleaseHandle(vm, value);
|
||||
wrenReleaseHandle(vm, unary);
|
||||
wrenReleaseHandle(vm, binary);
|
||||
wrenReleaseHandle(vm, subscript);
|
||||
wrenReleaseHandle(vm, subscriptSet);
|
||||
}
|
||||
|
||||
24
test/api/handle.c
Normal file
24
test/api/handle.c
Normal file
@ -0,0 +1,24 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "handle.h"
|
||||
|
||||
static WrenHandle* handle;
|
||||
|
||||
static void setValue(WrenVM* vm)
|
||||
{
|
||||
handle = wrenGetSlotHandle(vm, 1);
|
||||
}
|
||||
|
||||
static void getValue(WrenVM* vm)
|
||||
{
|
||||
wrenSetSlotHandle(vm, 0, handle);
|
||||
wrenReleaseHandle(vm, handle);
|
||||
}
|
||||
|
||||
WrenForeignMethodFn handleBindMethod(const char* signature)
|
||||
{
|
||||
if (strcmp(signature, "static Handle.value=(_)") == 0) return setValue;
|
||||
if (strcmp(signature, "static Handle.value") == 0) return getValue;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
3
test/api/handle.h
Normal file
3
test/api/handle.h
Normal file
@ -0,0 +1,3 @@
|
||||
#include "wren.h"
|
||||
|
||||
WrenForeignMethodFn handleBindMethod(const char* signature);
|
||||
11
test/api/handle.wren
Normal file
11
test/api/handle.wren
Normal file
@ -0,0 +1,11 @@
|
||||
class Handle {
|
||||
foreign static value=(value)
|
||||
foreign static value
|
||||
}
|
||||
|
||||
Handle.value = ["list", "of", "strings"]
|
||||
|
||||
// Make sure the handle lives through a GC.
|
||||
System.gc()
|
||||
|
||||
System.print(Handle.value) // expect: [list, of, strings]
|
||||
@ -8,10 +8,10 @@
|
||||
#include "call.h"
|
||||
#include "get_variable.h"
|
||||
#include "foreign_class.h"
|
||||
#include "handle.h"
|
||||
#include "lists.h"
|
||||
#include "new_vm.h"
|
||||
#include "slots.h"
|
||||
#include "value.h"
|
||||
|
||||
// The name of the currently executing API test.
|
||||
const char* testName;
|
||||
@ -42,6 +42,9 @@ static WrenForeignMethodFn bindForeignMethod(
|
||||
method = foreignClassBindMethod(fullName);
|
||||
if (method != NULL) return method;
|
||||
|
||||
method = handleBindMethod(fullName);
|
||||
if (method != NULL) return method;
|
||||
|
||||
method = listsBindMethod(fullName);
|
||||
if (method != NULL) return method;
|
||||
|
||||
@ -50,9 +53,6 @@ static WrenForeignMethodFn bindForeignMethod(
|
||||
|
||||
method = slotsBindMethod(fullName);
|
||||
if (method != NULL) return method;
|
||||
|
||||
method = valueBindMethod(fullName);
|
||||
if (method != NULL) return method;
|
||||
|
||||
fprintf(stderr,
|
||||
"Unknown foreign method '%s' for test '%s'\n", fullName, testName);
|
||||
|
||||
@ -22,13 +22,13 @@ static void getSlots(WrenVM* vm)
|
||||
if (wrenGetSlotDouble(vm, 3) != 12.34) result = false;
|
||||
if (strcmp(wrenGetSlotString(vm, 4), "str") != 0) result = false;
|
||||
|
||||
WrenValue* value = wrenGetSlotValue(vm, 5);
|
||||
WrenHandle* handle = wrenGetSlotHandle(vm, 5);
|
||||
|
||||
if (result)
|
||||
{
|
||||
// Otherwise, return the value so we can tell if we captured it correctly.
|
||||
wrenSetSlotValue(vm, 0, value);
|
||||
wrenReleaseValue(vm, value);
|
||||
wrenSetSlotHandle(vm, 0, handle);
|
||||
wrenReleaseHandle(vm, handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -39,7 +39,7 @@ static void getSlots(WrenVM* vm)
|
||||
|
||||
static void setSlots(WrenVM* vm)
|
||||
{
|
||||
WrenValue* value = wrenGetSlotValue(vm, 1);
|
||||
WrenHandle* handle = wrenGetSlotHandle(vm, 1);
|
||||
|
||||
wrenSetSlotBool(vm, 1, true);
|
||||
wrenSetSlotBytes(vm, 2, "by\0te", 5);
|
||||
@ -64,8 +64,8 @@ static void setSlots(WrenVM* vm)
|
||||
if (result)
|
||||
{
|
||||
// Move the value into the return position.
|
||||
wrenSetSlotValue(vm, 0, value);
|
||||
wrenReleaseValue(vm, value);
|
||||
wrenSetSlotHandle(vm, 0, handle);
|
||||
wrenReleaseHandle(vm, handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@ -1,24 +0,0 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "value.h"
|
||||
|
||||
static WrenValue* value;
|
||||
|
||||
static void setValue(WrenVM* vm)
|
||||
{
|
||||
value = wrenGetSlotValue(vm, 1);
|
||||
}
|
||||
|
||||
static void getValue(WrenVM* vm)
|
||||
{
|
||||
wrenSetSlotValue(vm, 0, value);
|
||||
wrenReleaseValue(vm, value);
|
||||
}
|
||||
|
||||
WrenForeignMethodFn valueBindMethod(const char* signature)
|
||||
{
|
||||
if (strcmp(signature, "static Value.value=(_)") == 0) return setValue;
|
||||
if (strcmp(signature, "static Value.value") == 0) return getValue;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
#include "wren.h"
|
||||
|
||||
WrenForeignMethodFn valueBindMethod(const char* signature);
|
||||
@ -1,14 +0,0 @@
|
||||
class Value {
|
||||
foreign static value=(value)
|
||||
foreign static value
|
||||
}
|
||||
|
||||
Value.value = ["list", "of", "strings"]
|
||||
|
||||
// Do some stuff to trigger a GC (at least when GC stress testing enabled).
|
||||
var s = "string"
|
||||
for (i in 1...10) {
|
||||
s = s + " more"
|
||||
}
|
||||
|
||||
System.print(Value.value) // expect: [list, of, strings]
|
||||
@ -55,7 +55,7 @@
|
||||
29DC14AA1BBA3032008A8274 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 29D009A61B7E3993000CE58C /* main.c */; };
|
||||
29DC14AB1BBA3038008A8274 /* foreign_class.c in Sources */ = {isa = PBXBuildFile; fileRef = 29D009A81B7E39A8000CE58C /* foreign_class.c */; };
|
||||
29DC14AC1BBA303D008A8274 /* slots.c in Sources */ = {isa = PBXBuildFile; fileRef = 29D009AA1B7E39A8000CE58C /* slots.c */; };
|
||||
29DC14AD1BBA3040008A8274 /* value.c in Sources */ = {isa = PBXBuildFile; fileRef = 29D009AC1B7E39A8000CE58C /* value.c */; };
|
||||
29DC14AD1BBA3040008A8274 /* handle.c in Sources */ = {isa = PBXBuildFile; fileRef = 29D009AC1B7E39A8000CE58C /* handle.c */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
@ -133,8 +133,8 @@
|
||||
29D009A91B7E39A8000CE58C /* foreign_class.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = foreign_class.h; path = ../../test/api/foreign_class.h; sourceTree = "<group>"; };
|
||||
29D009AA1B7E39A8000CE58C /* slots.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = slots.c; path = ../../test/api/slots.c; sourceTree = "<group>"; };
|
||||
29D009AB1B7E39A8000CE58C /* slots.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = slots.h; path = ../../test/api/slots.h; sourceTree = "<group>"; };
|
||||
29D009AC1B7E39A8000CE58C /* value.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = value.c; path = ../../test/api/value.c; sourceTree = "<group>"; };
|
||||
29D009AD1B7E39A8000CE58C /* value.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = value.h; path = ../../test/api/value.h; sourceTree = "<group>"; };
|
||||
29D009AC1B7E39A8000CE58C /* handle.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = handle.c; path = ../../test/api/handle.c; sourceTree = "<group>"; };
|
||||
29D009AD1B7E39A8000CE58C /* handle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = handle.h; path = ../../test/api/handle.h; sourceTree = "<group>"; };
|
||||
29D025E01C19CD1000A3BB28 /* process.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = process.c; path = ../../src/module/process.c; sourceTree = "<group>"; };
|
||||
29D025E11C19CD1000A3BB28 /* process.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = process.h; path = ../../src/module/process.h; sourceTree = "<group>"; };
|
||||
29D025E21C19CD1000A3BB28 /* process.wren.inc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.pascal; name = process.wren.inc; path = ../../src/module/process.wren.inc; sourceTree = "<group>"; };
|
||||
@ -270,14 +270,14 @@
|
||||
29D009A91B7E39A8000CE58C /* foreign_class.h */,
|
||||
2949AA8B1C2F14F000B106BA /* get_variable.c */,
|
||||
2949AA8C1C2F14F000B106BA /* get_variable.h */,
|
||||
29D009AC1B7E39A8000CE58C /* handle.c */,
|
||||
29D009AD1B7E39A8000CE58C /* handle.h */,
|
||||
29932D521C210F8D00099DEE /* lists.c */,
|
||||
29932D531C210F8D00099DEE /* lists.h */,
|
||||
29C946961C88F14F00B4A4F3 /* new_vm.c */,
|
||||
29C946971C88F14F00B4A4F3 /* new_vm.h */,
|
||||
29D009AA1B7E39A8000CE58C /* slots.c */,
|
||||
29D009AB1B7E39A8000CE58C /* slots.h */,
|
||||
29D009AC1B7E39A8000CE58C /* value.c */,
|
||||
29D009AD1B7E39A8000CE58C /* value.h */,
|
||||
);
|
||||
name = api_test;
|
||||
sourceTree = "<group>";
|
||||
@ -409,7 +409,7 @@
|
||||
29A427351BDBE435001E6E22 /* wren_opt_meta.c in Sources */,
|
||||
29DC14AB1BBA3038008A8274 /* foreign_class.c in Sources */,
|
||||
29DC14AC1BBA303D008A8274 /* slots.c in Sources */,
|
||||
29DC14AD1BBA3040008A8274 /* value.c in Sources */,
|
||||
29DC14AD1BBA3040008A8274 /* handle.c in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user