forked from Mirror/wren
Start writing comprensive embedding docs.
- Break embedding out into a separate section with multiple pages. - Document the constraints on the API. - Document the slot array and WrenValue. Still lots more to do, but getting there...
This commit is contained in:
@ -1,131 +0,0 @@
|
||||
^title Embedding API
|
||||
|
||||
Wren is designed to be a scripting language, so the embedding API is as
|
||||
important as any of its language features. There are two (well, three) ways to
|
||||
get Wren into your application:
|
||||
|
||||
1. **Link to static or dynamic library.** When you [build Wren][build], it
|
||||
generates both shared and static libraries in `lib` that you can link to.
|
||||
|
||||
2. **Include the source directly in your application.** If you want to include
|
||||
the source directly in your program, you don't need to run any build steps.
|
||||
Just add the source files in `src/vm` to your project. They should compile
|
||||
cleanly as C99 or C++98 or anything later.
|
||||
|
||||
[build]: getting-started.html
|
||||
|
||||
In either case, you also want to add `src/include` to your include path so you
|
||||
can get to the [public header for Wren][wren.h]:
|
||||
|
||||
[wren.h]: https://github.com/munificent/wren/blob/master/src/include/wren.h
|
||||
|
||||
:::c
|
||||
#include "wren.h"
|
||||
|
||||
## Creating a Wren VM
|
||||
|
||||
Once you've integrated the code into your executable, you need to create a
|
||||
virtual machine. To do that, you first fill in a `WrenConfiguration`:
|
||||
|
||||
:::c
|
||||
WrenConfiguration config;
|
||||
wrenInitConfiguration(&config);
|
||||
|
||||
This gives you a basic configuration that has reasonable defaults for
|
||||
everything. If you don't need to tweak stuff, you can leave it at that. If you
|
||||
do want to turn some knobs and dials, it exposes some fields you can set:
|
||||
|
||||
:::c
|
||||
config.reallocateFn = ...;
|
||||
|
||||
The `reallocateFn` is a callback you can provide to control how Wren allocates
|
||||
and frees memory. If you leave that to the default, it uses `malloc()` and
|
||||
`free()`.
|
||||
|
||||
:::c
|
||||
config.loadModuleFn = ...;
|
||||
config.bindForeignMethodFn = ...;
|
||||
config.bindForeignClassFn = ...;
|
||||
|
||||
These three callbacks are how Wren talks back to your program. We'll cover
|
||||
them in detail later.
|
||||
|
||||
:::c
|
||||
config.initialHeapSize = ...;
|
||||
config.minHeapSize = ...;
|
||||
config.heapGrowthPercent = ...;
|
||||
|
||||
These let you tune how the garbage collector runs. You can tweak these if you
|
||||
want, but the defaults are usually fine.
|
||||
|
||||
With this ready, you can create the VM:
|
||||
|
||||
:::c
|
||||
WrenVM* vm = wrenNewVM(&config);
|
||||
|
||||
This allocates memory for a new VM using the same `reallocateFn` you provided.
|
||||
The Wren C implementation has no global state, so every single bit of data Wren
|
||||
uses is bundled up inside a `WrenVM`. You can have multiple Wren VMs running
|
||||
independently from each other without any problems.
|
||||
|
||||
`wrenNewVM()` stores its own copy of the configuration, so after calling it, you
|
||||
can discard the `WrenConfiguration` struct you filled in. Now you have a live
|
||||
VM, waiting to run some code!
|
||||
|
||||
## Executing Wren code
|
||||
|
||||
You can tell the VM to execute a string of Wren source code like so:
|
||||
|
||||
:::c
|
||||
WrenInterpretResult result = wrenInterpret(vm, "System.print(\"Hi!\")");
|
||||
|
||||
The first string is the chunk of code to execute—a series of one or more
|
||||
statements separated by newlines. Wren runs this code in a special "main"
|
||||
module. Each time you call this, the code is run in the same module. This way,
|
||||
top-level names defined in one call can be accessed in later ones.
|
||||
|
||||
When you call `wrenInterpret()`, Wren first compiles your source to bytecode. If
|
||||
an error occurs here, it returns immediately with `WREN_RESULT_COMPILE_ERROR`.
|
||||
Otherwise, Wren spins up a new [fiber][] and executes the code in that. Your
|
||||
code can in turn spawn whatever other fibers it wants. It keeps running fibers
|
||||
until they all complete.
|
||||
|
||||
[fiber]: concurrency.html
|
||||
|
||||
If a [runtime error][] occurs (and another fiber doesn't catch it), it will
|
||||
abort fibers all the way back to the main one and then return
|
||||
`WREN_RESULT_RUNTIME_ERROR`. Otherwise, when the last fiber successfully
|
||||
returns, it returns `WREN_RESULT_SUCCESS`.
|
||||
|
||||
[runtime error]: error-handling.html
|
||||
|
||||
## Calling a C function from Wren
|
||||
|
||||
**TODO**
|
||||
|
||||
## Calling a Wren method from C
|
||||
|
||||
**TODO**
|
||||
|
||||
## Storing a reference to a Wren object in C
|
||||
|
||||
**TODO**
|
||||
|
||||
## Storing C data in a Wren object
|
||||
|
||||
**TODO**
|
||||
|
||||
## Shutting down a VM
|
||||
|
||||
Once the party is over and you're ready to end your relationship with a VM, you
|
||||
need to free any memory it allocated. You do that like so:
|
||||
|
||||
:::c
|
||||
wrenFreeVM(vm);
|
||||
|
||||
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` 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.
|
||||
9
doc/site/embedding/application-lifecycle.markdown
Normal file
9
doc/site/embedding/application-lifecycle.markdown
Normal file
@ -0,0 +1,9 @@
|
||||
^title Application Lifecycle
|
||||
|
||||
**TODO: Write these docs.**
|
||||
|
||||
Until these are written, you can read the docs in [wren.h][].
|
||||
|
||||
[wren.h]: https://github.com/munificent/wren/blob/master/src/include/wren.h
|
||||
|
||||
<a href="configuring-the-vm.html">← Configuring the VM</a>
|
||||
10
doc/site/embedding/calling-c-from-wren.markdown
Normal file
10
doc/site/embedding/calling-c-from-wren.markdown
Normal file
@ -0,0 +1,10 @@
|
||||
^title Calling C from Wren
|
||||
|
||||
**TODO: Write these docs.**
|
||||
|
||||
Until these are written, you can read the docs in [wren.h][].
|
||||
|
||||
[wren.h]: https://github.com/munificent/wren/blob/master/src/include/wren.h
|
||||
|
||||
<a class="right" href="storing-c-data.html">Storing C Data →</a>
|
||||
<a href="calling-wren-from-c.html">← Calling Wren from C</a>
|
||||
10
doc/site/embedding/calling-wren-from-c.markdown
Normal file
10
doc/site/embedding/calling-wren-from-c.markdown
Normal file
@ -0,0 +1,10 @@
|
||||
^title Calling Wren from C
|
||||
|
||||
**TODO: Write these docs.**
|
||||
|
||||
Until these are written, you can read the docs in [wren.h][].
|
||||
|
||||
[wren.h]: https://github.com/munificent/wren/blob/master/src/include/wren.h
|
||||
|
||||
<a class="right" href="calling-c-from-wren.html">Calling C from Wren →</a>
|
||||
<a href="slots-and-values.html">← Slots and Values</a>
|
||||
10
doc/site/embedding/configuring-the-vm.markdown
Normal file
10
doc/site/embedding/configuring-the-vm.markdown
Normal file
@ -0,0 +1,10 @@
|
||||
^title Configuring the VM
|
||||
|
||||
**TODO: Write these docs.**
|
||||
|
||||
Until these are written, you can read the docs in [wren.h][].
|
||||
|
||||
[wren.h]: https://github.com/munificent/wren/blob/master/src/include/wren.h
|
||||
|
||||
<a class="right" href="application-lifecycle.html">Application Lifecycle →</a>
|
||||
<a href="storing-c-data.html">← Storing C Data</a>
|
||||
209
doc/site/embedding/index.markdown
Normal file
209
doc/site/embedding/index.markdown
Normal file
@ -0,0 +1,209 @@
|
||||
^title Embedding
|
||||
|
||||
Wren is designed to be a scripting language that lives inside a host
|
||||
application, so the embedding API is as important as any of its language
|
||||
features. Designing this API well requires satisfying several constraints:
|
||||
|
||||
1. **Wren is dynamically typed, but C is not.** A variable can hold a value of
|
||||
any type in Wren, but that's definitely not the case in C unless you define
|
||||
some sort of variant type, which ultimately just kicks the problem down the
|
||||
road. Eventually, we have to move data across the boundary between typed and
|
||||
untyped code.
|
||||
|
||||
2. **Wren uses garbage collection, but C manages memory manually.** GC adds a
|
||||
few constraints on the API. The VM must be able to find every Wren object
|
||||
that is still usable, even if that object is being held onto by native C
|
||||
code. Otherwise, it could free an object that's still in use.
|
||||
|
||||
Also, we ideally don't want to let native C code see a bare pointer to a
|
||||
chunk of memory managed by Wren. Many garbage collection strategies involve
|
||||
[moving objects][] in memory. If we allow C code to point directly to an
|
||||
object, that pointer will be left dangling when the object moves. To prevent
|
||||
that, we can't using moving GCs even though they have some nice performance
|
||||
properties.
|
||||
|
||||
3. **The embedding API needs to be fast.** Users may add layers of abstraction
|
||||
on top of the API to make it more pleasant to work with, but the base API
|
||||
defines the *maximum* performance you can get out of the system. It's the
|
||||
bottom of the stack, so there's no way for a user to optimize around it if
|
||||
it's too slow. There is no lower level alternative.
|
||||
|
||||
4. **We want the API to be pleasant to use.** This is the last constraint
|
||||
because it's the softest. Of course, we want a beautiful, usable, API. But we
|
||||
really *need* to handle the above, so we're willing to make things a bit more
|
||||
of a chore to reach the first three goals.
|
||||
|
||||
[moving objects]: https://en.wikipedia.org/wiki/Tracing_garbage_collection#Copying_vs._mark-and-sweep_vs._mark-and-don.27t-sweep
|
||||
|
||||
Fortunately, Wren isn't the first language to tackle this. If you're familiar
|
||||
with [Lua's C API][lua], you'll find Wren's to be fairly familiar.
|
||||
|
||||
[lua]: https://www.lua.org/pil/24.html
|
||||
|
||||
### Performance and safety
|
||||
|
||||
When code is safely snuggled within the confines of the VM, it's pretty safe.
|
||||
Method calls are dynamically checked and generate a runtime error which can be
|
||||
caught and handled. The stack grows if it gets close to overflowing. In general,
|
||||
when you're within Wren code, it tries very hard to avoid crashing and burning.
|
||||
|
||||
This is why you use a high level language after all—it's safer and more
|
||||
productive than C. C, meanwhile, really assumes you know what you're doing. You
|
||||
can cast pointers in invalid ways, misinterpret bits, use memory after freeing
|
||||
it, etc. What that gives you in return is blazing performance. Many of the
|
||||
reasons C is fast are because it takes all the governors and guardrails off.
|
||||
|
||||
Wren's embedding API defines the border between those worlds, and takes on some
|
||||
of the characteristics of C. When you call any of the embedding API functions,
|
||||
it assumes you are calling them correctly. If you invoke a Wren method from C
|
||||
that expects three arguments, it trusts that you gave it three arguments.
|
||||
|
||||
In debug builds, Wren has assertions to check as many things as it can, but in
|
||||
release builds, Wren expects you to do the right thing. This means you need to
|
||||
take care when using the embedding API, just like you do in all C code you
|
||||
write. In return, you get an API that is quite fast.
|
||||
|
||||
## Including Wren
|
||||
|
||||
There are two (well, three) ways to get the Wren VM into your program:
|
||||
|
||||
1. **Link to the static or dynamic library.** When you [build Wren][build], it
|
||||
generates both shared and static libraries in `lib` that you can link to.
|
||||
|
||||
2. **Include the source directly in your application.** If you want to include
|
||||
the source directly in your program, you don't need to run any build steps.
|
||||
Just add the source files in `src/vm` to your project. They should compile
|
||||
cleanly as C99 or C++98 or anything later.
|
||||
|
||||
[build]: ../getting-started.html
|
||||
|
||||
In either case, you also want to add `src/include` to your include path so you
|
||||
can get to the [public header for Wren][wren.h]:
|
||||
|
||||
[wren.h]: https://github.com/munificent/wren/blob/master/src/include/wren.h
|
||||
|
||||
:::c
|
||||
#include "wren.h"
|
||||
|
||||
If your program is in C++ but you are linking to the Wren library compiled as C,
|
||||
then you'll need to handle the calling convention differences like so:
|
||||
|
||||
:::c
|
||||
extern "C" {
|
||||
#include "wren.h"
|
||||
}
|
||||
|
||||
(Wren's source can be compiled as either C or C++, so you can avoid this by
|
||||
compiling the whole thing yourself as C++. Whatever floats your boat.)
|
||||
|
||||
## Creating a Wren VM
|
||||
|
||||
Once you've integrated the code into your executable, you need to create a
|
||||
virtual machine. To do that, you create a WrenConfiguration:
|
||||
|
||||
:::c
|
||||
WrenConfiguration config;
|
||||
wrenInitConfiguration(&config);
|
||||
|
||||
This gives you a basic configuration that has reasonable defaults for
|
||||
everything. If you don't need to tweak stuff, you can leave it at that. We'll
|
||||
[learn more][configuration] about what you can configure later.
|
||||
|
||||
[configuration]: configuration.html
|
||||
|
||||
With this ready, you can create the VM:
|
||||
|
||||
:::c
|
||||
WrenVM* vm = wrenNewVM(&config);
|
||||
|
||||
This allocates memory for a new VM and initializes it. The Wren C implementation
|
||||
has no global state, so every single bit of data Wren uses is bundled up inside
|
||||
a WrenVM. You can have multiple Wren VMs running independently of each other
|
||||
without any problems.
|
||||
|
||||
`wrenNewVM()` stores its own copy of the configuration, so after calling it, you
|
||||
can discard the `WrenConfiguration` struct you filled in. Now you have a live
|
||||
VM, waiting to run some code!
|
||||
|
||||
## Executing Wren code
|
||||
|
||||
You can tell the VM to execute a string of Wren source code like so:
|
||||
|
||||
:::c
|
||||
WrenInterpretResult result = wrenInterpret(vm,
|
||||
"System.print(\"I am running in a VM!\")");
|
||||
|
||||
The string is a chunk of Wren code to execute—a series of one or more
|
||||
statements separated by newlines. Wren copies the string, so you can free it
|
||||
after calling this.
|
||||
|
||||
When you call `wrenInterpret()`, Wren first compiles your source to bytecode. If
|
||||
an error occurs, it returns immediately with `WREN_RESULT_COMPILE_ERROR`.
|
||||
|
||||
Otherwise, Wren spins up a new [fiber][] and executes the code in that. Your
|
||||
code can in turn spawn whatever other fibers it wants. It keeps running fibers
|
||||
until they all complete or the current one [suspends].
|
||||
|
||||
[fiber]: ../concurrency.html
|
||||
[suspends]: ../modules/core/fiber.html#fiber.suspend()
|
||||
|
||||
The code runs in a special "main" module. Everytime you call `wrenInterpret()`,
|
||||
it runs in the same module. That way, top-level names defined in one call can be
|
||||
accessed in later ones.
|
||||
|
||||
If a [runtime error][] occurs (and another fiber doesn't handle it), Wren abort
|
||||
fibers all the way back to the main one and returns `WREN_RESULT_RUNTIME_ERROR`.
|
||||
Otherwise, when the last fiber successfully returns, it returns
|
||||
`WREN_RESULT_SUCCESS`.
|
||||
|
||||
[runtime error]: error-handling.html
|
||||
|
||||
## Shutting down a VM
|
||||
|
||||
Once the party is over and you're ready to end your relationship with a VM, you
|
||||
need to free any memory it allocated. You do that like so:
|
||||
|
||||
:::c
|
||||
wrenFreeVM(vm);
|
||||
|
||||
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]
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
-->
|
||||
269
doc/site/embedding/slots-and-values.markdown
Normal file
269
doc/site/embedding/slots-and-values.markdown
Normal file
@ -0,0 +1,269 @@
|
||||
^title Slots and Values
|
||||
|
||||
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
|
||||
of the world, so pretty much all it can do is burn CPU cycles. It can turn your
|
||||
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**.
|
||||
|
||||
## The Slot Array
|
||||
|
||||
When you want to send data to Wren, read data from it, or generally monkey
|
||||
around with Wren objects from C, you do so by going through an array of slots.
|
||||
You can think of it as a shared message board that both the VM and your C code
|
||||
can leave bits of data on for the other side to process.
|
||||
|
||||
The array is zero-based, and each slot holds a value of any type. It is
|
||||
dynamically sized, but it's your responsibility to ensure there are enough
|
||||
slots before you start using them. You do this by calling:
|
||||
|
||||
:::c
|
||||
wrenEnsureSlots(WrenVM* vm, int slotCount);
|
||||
|
||||
This grows the slot array if needed to ensure that many slots are available. If
|
||||
it's already big enough, this does nothing. You'll typically call this once
|
||||
before populating the slots with data that you want to send to Wren.
|
||||
|
||||
:::c
|
||||
wrenEnsureSlots(vm, 4);
|
||||
// Can now use slots 0 through 3, inclusive.
|
||||
|
||||
When Wren is [calling your C code][] and passing data to you, it ensures there
|
||||
are enough slots for the objects it is sending you.
|
||||
|
||||
[calling your c code]: calling-c-from-wren.html
|
||||
|
||||
After you ensure an array of slots, you can only rely on them being there until
|
||||
you pass control back to Wren. That includes calling `wrenCall()` or
|
||||
`wrenInterpret()`, or returning from a [foreign method][].
|
||||
|
||||
[foreign method]: calling-c-from-wren.html
|
||||
|
||||
If you read or write from a slot that you haven't ensured is valid, Wren makes
|
||||
no guarantees about what will happen. I've heard rumors of smoke and feathers
|
||||
flying out of a user's computer.
|
||||
|
||||
If you want to see how big the slot array is, you can call:
|
||||
|
||||
:::c
|
||||
int wrenGetSlotCount(WrenVM* vm);
|
||||
|
||||
It returns the number of slots in the array. Note that this may be higher than
|
||||
the size you've ensured. Wren reuses the memory for this array when possible,
|
||||
so you may get one bigger than you need if it happened to be laying around.
|
||||
|
||||
### Writing slots
|
||||
|
||||
Once you have some slots, you can store data in them using a number of functions all named `wrenSetSlot<type>()` where `<type>` is the kind of data.
|
||||
We'll start with the simple ones:
|
||||
|
||||
:::c
|
||||
void wrenSetSlotBool(WrenVM* vm, int slot, bool value);
|
||||
void wrenSetSlotDouble(WrenVM* vm, int slot, double value);
|
||||
void wrenSetSlotNull(WrenVM* vm, int slot);
|
||||
|
||||
Each of these takes a primitive C value and converts it to the corresponding
|
||||
[Wren value][]. (Since Wren's [native number type][] *is* a double, there's not
|
||||
really much *conversion* going on, but you get the idea.)
|
||||
|
||||
[wren value]: ../values.html
|
||||
[native number type]: ../values.html#numbers
|
||||
|
||||
You can also pass string data to Wren:
|
||||
|
||||
:::c
|
||||
void wrenSetSlotBytes(WrenVM* vm, int slot,
|
||||
const char* bytes, size_t length);
|
||||
|
||||
void wrenSetSlotString(WrenVM* vm, int slot,
|
||||
const char* text);
|
||||
|
||||
Both of these copy the bytes into a new [String][] object managed by Wren's
|
||||
garbage collector, so you can free your copy of it after you call this. The
|
||||
difference between the two is that `wrenSetSlotBytes()` takes an explicit
|
||||
length. Since Wren supports strings containing arbitrary byte values, including
|
||||
the null byte, this lets you pass those in. It's also a little faster to use
|
||||
this for regular strings if you happen to know the length. The latter calculates
|
||||
the length of the string using `strlen()`.
|
||||
|
||||
[string]: ../values.html#strings
|
||||
|
||||
### Reading slots
|
||||
|
||||
You can, of course, also pull data out of slots. Here are the simple ones:
|
||||
|
||||
:::c
|
||||
bool wrenGetSlotBool(WrenVM* vm, int slot);
|
||||
double wrenGetSlotDouble(WrenVM* vm, int slot);
|
||||
|
||||
These take a Wren value of the corresponding type and convert it to its raw C
|
||||
representation. For strings, we have:
|
||||
|
||||
:::c
|
||||
const char* wrenGetSlotString(WrenVM* vm, int slot);
|
||||
const char* wrenGetSlotBytes(WrenVM* vm, int slot,
|
||||
int* length);
|
||||
|
||||
These return a pointer to the first byte of the string. If you want to know the
|
||||
length, the latter stores it in the variable pointed to by `length`. Both of
|
||||
these return a direct pointer to the bytes managed by Wren. You should not hold
|
||||
on to this pointer for long. Wren does not promise that it won't move or free
|
||||
the data.
|
||||
|
||||
With these functions, you are going from dynamically typed Wren data to
|
||||
statically typed C. It's up to *you* to ensure that you read a value as the
|
||||
correct type. If you read a number from a slot that currently holds a string,
|
||||
you're gonna have a bad time.
|
||||
|
||||
Fortunately, you usually know what type of data you have in a slot. If not, you
|
||||
can ask:
|
||||
|
||||
:::c
|
||||
WrenType wrenGetSlotType(WrenVM* vm, int slot);
|
||||
|
||||
This returns an enum defining what type of value is in the slot. It only covers
|
||||
the primitive values that are supported by the C API. Thinks like ranges and
|
||||
instances of classes come back as `WREN_TYPE_UNKNOWN`. If you want to move that
|
||||
kind of data between Wren and C, you'll have to pull the object apart into
|
||||
simple primitive values first or use a [foreign class][].
|
||||
|
||||
[foreign class]: storing-c-data.html
|
||||
|
||||
### Looking up variables
|
||||
|
||||
There are a few other utility functions that move data into and out of slots.
|
||||
Here's the first:
|
||||
|
||||
:::c
|
||||
void wrenGetVariable(WrenVM* vm, const char* module,
|
||||
const char* name, int slot);
|
||||
|
||||
This looks up a top level variable with the given name in the module with the
|
||||
given name and stores its value in the given slot. (Code you executed using
|
||||
`wrenInterpret()` implicitly goes into a module named "main".)
|
||||
|
||||
Note that classes are just objects stored in variables too, so you can use this
|
||||
to look up a class by its name. Handy for calling static methods on it.
|
||||
|
||||
Like any method that works with strings, this one is a bit slow. It has to
|
||||
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).
|
||||
|
||||
### Working with lists
|
||||
|
||||
The slot array is fine for moving a fixed number of objects between Wren and
|
||||
C, but sometimes you need to shuttle a larger or dynamically-sized ball of
|
||||
stuff. [List objects][lists] work well for that, so the C API lets you work
|
||||
with them directly.
|
||||
|
||||
[lists]: ../lists.html
|
||||
|
||||
You can create a new empty list from C using:
|
||||
|
||||
:::c
|
||||
void wrenSetSlotNewList(WrenVM* vm, int slot);
|
||||
|
||||
It stores the resulting list in the given slot. If you have a list in a
|
||||
slot—either one you created from C or from Wren—you can add elements
|
||||
to it using:
|
||||
|
||||
:::c
|
||||
void wrenInsertInList(WrenVM* vm, int listSlot, int index,
|
||||
int elementSlot);
|
||||
|
||||
That's a lot of int parameters:
|
||||
|
||||
* `listSlot` is the slot where the list object is stored. That's the list you'll
|
||||
be modifying. If you created the list from C, it will be the slot you passed
|
||||
to `wrenSetSlotNewList()`.
|
||||
|
||||
* `index` is the index within the list where you want to insert the element.
|
||||
Just like from within Wren, you can use a negative number to count back from
|
||||
the end, so `-1` appends to the list.
|
||||
|
||||
* `elementSlot` identifies the slot where the value you want to insert in the
|
||||
list can be found.
|
||||
|
||||
This API means getting a value from C into a list is a two step operation. First
|
||||
you move the value into a slot, then you take it from the slot and insert it in
|
||||
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
|
||||
|
||||
Slots are pretty good for shuttling primitive data between C and Wren, but they
|
||||
have two limitations:
|
||||
|
||||
1. **They are short-lived.** As soon as you execute some more Wren code, the
|
||||
slot array is invalidated. You can't use a slot to persistently keep track
|
||||
of some object.
|
||||
|
||||
2. **They only support primitive types.** A slot can hold a value of any type,
|
||||
but the C API we've seen so far doesn't let you *do* anything with values
|
||||
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.
|
||||
|
||||
*(Note: WrenValue will probably be renamed to WrenHandle soon.)*
|
||||
|
||||
You create a WrenValue using this:
|
||||
|
||||
:::c
|
||||
WrenValue* wrenGetSlotValue(WrenVM* vm, int slot);
|
||||
|
||||
This takes the object stored in the given slot, creates a new WrenValue 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);
|
||||
|
||||
Note that this doesn't invalidate your WrenValue. You can still keep using it.
|
||||
|
||||
### Retaining and releasing values
|
||||
|
||||
A WrenValue 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
|
||||
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
|
||||
memory.
|
||||
|
||||
Internally, Wren keeps a list of all of the WrenValues 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
|
||||
must explicitly release it by calling:
|
||||
|
||||
:::c
|
||||
void wrenReleaseValue(WrenVM* vm, WrenValue* value);
|
||||
|
||||
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
|
||||
again.
|
||||
|
||||
You must release every WrenValue 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.
|
||||
|
||||
Now we know how to pass values between Wren and C, but we don't know how to
|
||||
actually *do* anything with them. Next, we'll learn how to use slots to pass
|
||||
parameters to a Wren method from C...
|
||||
|
||||
<a class="right" href="calling-wren-from-c.html">Calling Wren from C →</a>
|
||||
<a href="index.html">← Introduction</a>
|
||||
10
doc/site/embedding/storing-c-data.markdown
Normal file
10
doc/site/embedding/storing-c-data.markdown
Normal file
@ -0,0 +1,10 @@
|
||||
^title Storing C Data
|
||||
|
||||
**TODO: Write these docs.**
|
||||
|
||||
Until these are written, you can read the docs in [wren.h][].
|
||||
|
||||
[wren.h]: https://github.com/munificent/wren/blob/master/src/include/wren.h
|
||||
|
||||
<a class="right" href="configuring-the-vm.html">Configuring the VM →</a>
|
||||
<a href="calling-c-from-wren.html">← Calling C from Wren</a>
|
||||
84
doc/site/embedding/template.html
Normal file
84
doc/site/embedding/template.html
Normal file
@ -0,0 +1,84 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
||||
<title>{title} – Wren</title>
|
||||
<link rel="stylesheet" type="text/css" href="../style.css" />
|
||||
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic|Source+Code+Pro:400|Lato:400|Sanchez:400italic,400' rel='stylesheet' type='text/css'>
|
||||
<!-- Tell mobile browsers we're optimized for them and they don't need to crop
|
||||
the viewport. -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
|
||||
</head>
|
||||
<body id="top" class="embedding">
|
||||
<header>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<h1><a href="../">wren</a></h1>
|
||||
<h2>a classy little scripting language</h2>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page">
|
||||
<nav class="big">
|
||||
<section>
|
||||
<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="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>
|
||||
<li><a href="configuring-the-vm.html">Configuring the VM</a></li>
|
||||
<li><a href="application-lifecycle.html">Application Lifecycle</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
</nav>
|
||||
<nav class="small">
|
||||
<table>
|
||||
<tr>
|
||||
<td><h2>embedding</h2></td>
|
||||
<td><h2>?</h2></td>
|
||||
<td><h2>?</h2></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="./">Introduction</a></li>
|
||||
<li><a href="slots-and-values.html">Slots and Values</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>
|
||||
<li><a href="configuring-the-vm.html">Configuring the VM</a></li>
|
||||
<li><a href="application-lifecycle.html">Application Lifecycle</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</nav>
|
||||
<main>
|
||||
<h1>{title}</h1>
|
||||
{html}
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="page">
|
||||
<div class="main-column">
|
||||
<p>Wren lives
|
||||
<a href="https://github.com/munificent/wren">on GitHub</a>
|
||||
— Made with ❤ by
|
||||
<a href="http://journal.stuffwithstuff.com/">Bob Nystrom</a> and
|
||||
<a href="https://github.com/munificent/wren/blob/master/AUTHORS">friends</a>.
|
||||
</p>
|
||||
<div class="main-column">
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -51,7 +51,7 @@ involved][contribute]!
|
||||
[perf]: performance.html
|
||||
[classes]: classes.html
|
||||
[fibers]: concurrency.html
|
||||
[embedding]: embedding-api.html
|
||||
[embedding]: embedding
|
||||
[started]: getting-started.html
|
||||
[browser]: http://ppvk.github.io/wren-nest/
|
||||
[contribute]: contributing.html
|
||||
|
||||
@ -25,13 +25,17 @@ The currently executing fiber.
|
||||
Pauses the current fiber, and stops the interpreter. Control returns to the
|
||||
host application.
|
||||
|
||||
To resume execution, the host application will need to invoke the interpreter
|
||||
again. If there is still a reference to the suspended fiber, it can be resumed.
|
||||
Typically, you store a reference to the fiber using `Fiber.current` before
|
||||
calling this. The fiber can be resumed later by calling or transferring to that
|
||||
reference. If there are no references to it, it is eventually garbage collected.
|
||||
|
||||
Much like `yield()`, returns the value passed to `call()` or `transfer()` when
|
||||
the fiber is resumed.
|
||||
|
||||
### Fiber.**yield**()
|
||||
|
||||
Pauses the current fiber and transfers control to the parent fiber. "Parent"
|
||||
here means the last fiber that was started using `call` and not `run`.
|
||||
here means the last fiber that was started using `call` and not `transfer`.
|
||||
|
||||
:::wren
|
||||
var fiber = Fiber.new {
|
||||
@ -46,8 +50,8 @@ here means the last fiber that was started using `call` and not `run`.
|
||||
|
||||
When resumed, the parent fiber's `call()` method returns `null`.
|
||||
|
||||
If a yielded fiber is resumed by calling `call()` or `run()` with an argument,
|
||||
`yield()` returns that value.
|
||||
If a yielded fiber is resumed by calling `call()` or `transfer()` with an
|
||||
argument, `yield()` returns that value.
|
||||
|
||||
:::wren
|
||||
var fiber = Fiber.new {
|
||||
@ -57,8 +61,8 @@ If a yielded fiber is resumed by calling `call()` or `run()` with an argument,
|
||||
fiber.call() // Run until the first yield.
|
||||
fiber.call("value") // Resume the fiber.
|
||||
|
||||
If it was resumed by calling `call()` or `run()` with no argument, it returns
|
||||
`null`.
|
||||
If it was resumed by calling `call()` or `transfer()` with no argument, it
|
||||
returns `null`.
|
||||
|
||||
If there is no parent fiber to return to, this exits the interpreter. This can
|
||||
be useful to pause execution until the host application wants to resume it
|
||||
|
||||
@ -277,13 +277,16 @@ footer {
|
||||
span.c1, span.cm { color: mix($code-color, $code-bg, 60%); }
|
||||
|
||||
// Keywords.
|
||||
span.k, span.kd, span.kc, span.nb { color: hsl(200, 70%, 50%); }
|
||||
span.k, span.kd, span.kc, span.kt, span.nb { color: hsl(200, 70%, 50%); }
|
||||
|
||||
// Names.
|
||||
span.vg { color: hsl(180, 70%, 35%); }
|
||||
span.vi { color: hsl(90, 80%, 35%); }
|
||||
span.vc { color: hsl(130, 60%, 40%); }
|
||||
|
||||
// C function name.
|
||||
span.nf { color: hsl(180, 60%, 40%); }
|
||||
|
||||
// Numbers.
|
||||
span.m, span.mi, span.mf { color: hsl(90, 40%, 50%); }
|
||||
|
||||
|
||||
@ -45,7 +45,7 @@
|
||||
<h2>reference</h2>
|
||||
<ul>
|
||||
<li><a href="modules">Modules</a></li>
|
||||
<li><a href="embedding-api.html">Embedding API</a></li>
|
||||
<li><a href="embedding">Embedding</a></li>
|
||||
<li><a href="performance.html">Performance</a></li>
|
||||
<li><a href="qa.html">Q & A</a></li>
|
||||
</ul>
|
||||
@ -85,7 +85,7 @@
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="modules">Modules</a></li>
|
||||
<li><a href="embedding-api.html">Embedding API</a></li>
|
||||
<li><a href="embedding">Embedding</a></li>
|
||||
<li><a href="performance.html">Performance</a></li>
|
||||
<li><a href="qa.html">Q & A</a></li>
|
||||
</ul>
|
||||
|
||||
Reference in New Issue
Block a user