WrenValue -> WrenHandle.

This commit is contained in:
Bob Nystrom
2016-05-20 20:55:28 -07:00
parent 5d98d20175
commit 0a060a9678
22 changed files with 235 additions and 266 deletions
@@ -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 &rarr;</a>
<a href="slots-and-values.html">&larr; Slots and Values</a>
<a href="slots-and-handles.html">&larr; Slots and Handles</a>
+3 -31
View File
@@ -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 &rarr;</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 &rarr;</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&mdash;strings, numbers, instances of classes,
collections, whatever.
To address those, we have WrenHandle. A WrenHandle wraps a reference to an
object of any kind&mdash;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&mdash;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.
+2 -2
View File
@@ -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>