This doesn't let you arbitrarily call back into the VM from within
foreign methods. I'm still not sure if that's even a good idea since
God knows what that would mean if you switch fibers while doing that.
But this does allow the very important use case of being able to call
a foreign method from within a call to wrenCall(). In other words,
foreign methods need to always be leaf calls on the call stack, but the
root of that stack can now come from runInterpreter() or wrenCall().
Fix#510.
The VM used to not detect this case. It meant you could get into a
situation where another fiber's caller had completed. Then, when it
tried to resume that fiber, the VM would crash because there was nothing
to resume to.
This is part of thinking through all the cases around re-entrancy. Added
some notes for that too.
There's a lot of changes here and surely some rough edges to iron out.
Also, I need to update the docs. But I want to get closer to landing
this so I can build on it.
This is a breaking change because existing imports in user Wren code
that assume the path is relative to the entrypoint file will now likely
fail.
Also, stack trace output and host API calls that take a module string
now need the resolved module string, not the short name that appears in
the import.
This is an interim step towards supporting relative imports. Previously,
the IMPORT_VARIABLE instruction had a constant string operand for the
import string of the module to import the variable from. However, with
relative imports, the import string needs to be resolved by the host
all into a canonical import string. At that point, the original import
string in the source is no longer useful.
This changes that to have IMPORT_VARIABLE access the imported ObjModule
directly. It works in two pieces:
1. When a module is compiled, it ends with an END_MODULE instruction.
That instruction stores the current ObjModule in vm->lastModule.
2. The IMPORT_VARIABLE instruction uses vm->lastModule as the module to
load the variable from. Since no interesting code can execute between
when a module body completes and the subsequent IMPORT_VARIABLE
statements, we know vm->lastModule will be the one we imported.
Build Wren for more targets, and run the test suite on both 32 and 64
bit builds.
* Update the build config to test both with and without NAN_TAGGING
defined.
* Updatest `util/test.py` to take the executable suffix as a
parameter. This allows the makefile to control which binaries will be
tested.
Adds a new target to the makefile to be run by travis, this runs the
test suite against all of the configurations it builds.
* Gcc on some 32 bit platforms was complaining about numeric overflows
when -INFINITY was used. Update the logic for converting a double to
a string to not explicitly check against the literal values.
* Make CI builds run the tests on both 64 _and_ 32 bit builds.
* If I limit the number of CPUs on my MBP I can get some of the tests
to time out, I'm imagining that the specs of the Travis Macs means
that the same is happening there too. Updated the test script to
allow an extra few seconds for the test to complete successfully
before killing it.
* Due to slight differences in accuracy in some computations tests were
failing on 32 bit builds. Stop comparing things quite as exactly in
the cases where it is causing issues.
For some reason 12.34 was refusing to compare equal to itself. Bad
show 12.34 :-/. I've also updated the test so it doesn't leak handles
even if the assertions fail.
* Double-cast from `double` to `uint32_t` to prevent undefined
behaviour on overflow of basic integers. This should hopefully
prevent 32 bit test failures on Linux.
* Move to a version of LibUV with a fix for the 32 bit build error on
Travis.
- Rename "size" -> "count" to be consistent with other usage.
- Remove "Slot" from the function names, since that's implicit.
- Make the getListElement() just move the element to another slot. This
matches other APIs where we distinguish accessing a value and
converting it to some specific type.
This PR adds support for retrieving list information from a slot. Rather
than take the whole list, two different methods are provided for
retrieving a) the length of the list, and b) a specific value in the
list.
Now, you call wrenEnsureSlots() and then wrenSetSlot___() to set up the
receiver and arguments before the call. Then wrenCall() is passed a
handle to the stub function that makes the call. After that, you can
get the result using wrenGetSlot___().
This is a little more verbose to use, but it's more flexible, simpler,
and much faster in the VM. The call benchmark is 185% of the previous
speed.
Instead, finalizers just get access to the foreign object's raw bytes.
This is deliberately limiting, since it discourages the user from
interacting with the VM in the middle of a GC.
- wrenEnsureSlots()
Lets you go the foreign slot stack to make room for a temporary work
area.
- wrenSetSlotNewList()
Creates a new empty list and stores it in a slot.
- wrenInsertInList()
Takes a value from one slot and inserts it into the list in another.
Still need more functions like getting elements from a list, removing,
etc. but this at least lets you create, populate, and return lists from
foreign methods.
This turns those functions into general-purpose functions for writing
raw C values into slots on the foreign call stack.
Writing a return just means writing a value to slot 0.
- wrenGetArgumentCount() -> wrenGetSlotCount()
- wrenGetArgument___() -> wrenGetSlot___()
Also, the get functions assert that the value is the right type instead
of checking at runtime. This puts the onus on the caller to be safe,
but maximizes performance.
I've got some ideas on how to tweak the embedding API, but I want to
see what performance impact they have first, so this adds a little
benchmark that just calls a foreign method a ton of times.
This allows "%(...)" inside a string literal to interpolate the
stringified result of an expression.
It doesn't support custom interpolators or format strings, but we can
consider extending that later.
- Add an explicit va_list version. That lets variadic functions
forward to it.
- Fix a GC bug in wrenCall() with return values.
- Make the call API test not re-enter the VM.
- Test that a foreign method can return strings.
- Test that a foreign method can return a string with null bytes.
- Test wrenCall().
- Allow passing NULL for "v" to wrenCall().
- Allow "a" for passing an explicit length byte array to wrenCall().
Get rid of the separate opt-in IO class and replace it with a core
System class.
- Remove wren_io.c, wren_io.h, and io.wren.
- Remove the flags that disable it.
- Remove the overloads for print() with different arity. (It was an
experiment, but I don't think it's that useful.)
- Remove IO.read(). That will reappear using libuv in the CLI at some
point.
- Remove IO.time. Doesn't seem to have been used.
- Update all of the tests, docs, etc.
I'm sorry for all the breakage this causes, but I think "System" is a
better name for this class (it makes it natural to add things like
"System.gc()") and frees up "IO" for referring to the CLI's IO module.
Most of the pieces are there:
- You can declare a foreign class.
- It will call your C function to provide an allocator function.
- Whenever a foreign object is created, it calls the allocator.
- Foreign methods can access the foreign bytes of an object.
- Most of the runtime checking is in place for things like subclassing
foreign classes.
There is still some loose ends to tie up:
- Finalizers are not called.
- Some of the error-handling could be better.
- The GC doesn't track how much memory a marked foreign object uses.