mirror of
https://github.com/wren-lang/wren.git
synced 2026-01-11 22:28:45 +01:00
First pass at implementing foreign classes.
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.
This commit is contained in:
87
test/api/foreign_class.c
Normal file
87
test/api/foreign_class.c
Normal file
@ -0,0 +1,87 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "foreign_class.h"
|
||||
|
||||
static void counterAllocate(WrenVM* vm)
|
||||
{
|
||||
double* value = (double*)wrenAllocateForeign(vm, sizeof(double));
|
||||
*value = 0;
|
||||
}
|
||||
|
||||
static void counterIncrement(WrenVM* vm)
|
||||
{
|
||||
double* value = (double*)wrenGetArgumentForeign(vm, 0);
|
||||
double increment = wrenGetArgumentDouble(vm, 1);
|
||||
|
||||
*value += increment;
|
||||
}
|
||||
|
||||
static void counterValue(WrenVM* vm)
|
||||
{
|
||||
double value = *(double*)wrenGetArgumentForeign(vm, 0);
|
||||
wrenReturnDouble(vm, value);
|
||||
}
|
||||
|
||||
static void pointAllocate(WrenVM* vm)
|
||||
{
|
||||
double* coordinates = (double*)wrenAllocateForeign(vm, sizeof(double[3]));
|
||||
|
||||
// This gets called by both constructors, so sniff the argument count to see
|
||||
// which one was invoked.
|
||||
if (wrenGetArgumentCount(vm) == 1)
|
||||
{
|
||||
coordinates[0] = 0.0;
|
||||
coordinates[1] = 0.0;
|
||||
coordinates[2] = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
coordinates[0] = wrenGetArgumentDouble(vm, 1);
|
||||
coordinates[1] = wrenGetArgumentDouble(vm, 2);
|
||||
coordinates[2] = wrenGetArgumentDouble(vm, 3);
|
||||
}
|
||||
}
|
||||
|
||||
static void pointTranslate(WrenVM* vm)
|
||||
{
|
||||
double* coordinates = (double*)wrenGetArgumentForeign(vm, 0);
|
||||
coordinates[0] += wrenGetArgumentDouble(vm, 1);
|
||||
coordinates[1] += wrenGetArgumentDouble(vm, 2);
|
||||
coordinates[2] += wrenGetArgumentDouble(vm, 3);
|
||||
}
|
||||
|
||||
static void pointToString(WrenVM* vm)
|
||||
{
|
||||
double* coordinates = (double*)wrenGetArgumentForeign(vm, 0);
|
||||
char result[100];
|
||||
sprintf(result, "(%g, %g, %g)",
|
||||
coordinates[0], coordinates[1], coordinates[2]);
|
||||
wrenReturnString(vm, result, (int)strlen(result));
|
||||
}
|
||||
|
||||
WrenForeignMethodFn foreignClassBindMethod(const char* signature)
|
||||
{
|
||||
if (strcmp(signature, "Counter.increment(_)") == 0) return counterIncrement;
|
||||
if (strcmp(signature, "Counter.value") == 0) return counterValue;
|
||||
if (strcmp(signature, "Point.translate(_,_,_)") == 0) return pointTranslate;
|
||||
if (strcmp(signature, "Point.toString") == 0) return pointToString;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void foreignClassBindClass(
|
||||
const char* className, WrenForeignClassMethods* methods)
|
||||
{
|
||||
if (strcmp(className, "Counter") == 0)
|
||||
{
|
||||
methods->allocate = counterAllocate;
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(className, "Point") == 0)
|
||||
{
|
||||
methods->allocate = pointAllocate;
|
||||
return;
|
||||
}
|
||||
}
|
||||
5
test/api/foreign_class.h
Normal file
5
test/api/foreign_class.h
Normal file
@ -0,0 +1,5 @@
|
||||
#include "wren.h"
|
||||
|
||||
WrenForeignMethodFn foreignClassBindMethod(const char* signature);
|
||||
void foreignClassBindClass(
|
||||
const char* className, WrenForeignClassMethods* methods);
|
||||
48
test/api/foreign_class.wren
Normal file
48
test/api/foreign_class.wren
Normal file
@ -0,0 +1,48 @@
|
||||
// Class with a default constructor.
|
||||
foreign class Counter {
|
||||
foreign increment(amount)
|
||||
foreign value
|
||||
}
|
||||
|
||||
var counter = Counter.new()
|
||||
IO.print(counter.value) // expect: 0
|
||||
counter.increment(3.1)
|
||||
IO.print(counter.value) // expect: 3.1
|
||||
counter.increment(1.2)
|
||||
IO.print(counter.value) // expect: 4.3
|
||||
|
||||
// Foreign classes can inherit a class as long as it has no fields.
|
||||
class PointBase {
|
||||
inherited() {
|
||||
IO.print("inherited method")
|
||||
}
|
||||
}
|
||||
|
||||
// Class with non-default constructor.
|
||||
foreign class Point is PointBase {
|
||||
construct new() {
|
||||
IO.print("default")
|
||||
}
|
||||
|
||||
construct new(x, y, z) {
|
||||
IO.print(x, ", ", y, ", ", z)
|
||||
}
|
||||
|
||||
foreign translate(x, y, z)
|
||||
foreign toString
|
||||
}
|
||||
|
||||
var p = Point.new(1, 2, 3) // expect: 1, 2, 3
|
||||
IO.print(p) // expect: (1, 2, 3)
|
||||
p.translate(3, 4, 5)
|
||||
IO.print(p) // expect: (4, 6, 8)
|
||||
|
||||
p = Point.new() // expect: default
|
||||
IO.print(p) // expect: (0, 0, 0)
|
||||
|
||||
p.inherited() // expect: inherited method
|
||||
|
||||
var error = Fiber.new {
|
||||
class Subclass is Point {}
|
||||
}.try()
|
||||
IO.print(error) // expect: Class 'Subclass' cannot inherit from foreign class 'Point'.
|
||||
@ -5,16 +5,23 @@
|
||||
#include "vm.h"
|
||||
#include "wren.h"
|
||||
|
||||
#include "value.h"
|
||||
#include "foreign_class.h"
|
||||
#include "returns.h"
|
||||
#include "value.h"
|
||||
|
||||
#define REGISTER_TEST(name, camelCase) \
|
||||
if (strcmp(testName, #name) == 0) return camelCase##BindForeign(fullName)
|
||||
#define REGISTER_METHOD(name, camelCase) \
|
||||
if (strcmp(testName, #name) == 0) return camelCase##BindMethod(fullName)
|
||||
|
||||
#define REGISTER_CLASS(name, camelCase) \
|
||||
if (strcmp(testName, #name) == 0) \
|
||||
{ \
|
||||
camelCase##BindClass(className, &methods); \
|
||||
}
|
||||
|
||||
// The name of the currently executing API test.
|
||||
const char* testName;
|
||||
|
||||
static WrenForeignMethodFn bindForeign(
|
||||
static WrenForeignMethodFn bindForeignMethod(
|
||||
WrenVM* vm, const char* module, const char* className,
|
||||
bool isStatic, const char* signature)
|
||||
{
|
||||
@ -29,8 +36,9 @@ static WrenForeignMethodFn bindForeign(
|
||||
strcat(fullName, ".");
|
||||
strcat(fullName, signature);
|
||||
|
||||
REGISTER_TEST(returns, returns);
|
||||
REGISTER_TEST(value, value);
|
||||
REGISTER_METHOD(foreign_class, foreignClass);
|
||||
REGISTER_METHOD(returns, returns);
|
||||
REGISTER_METHOD(value, value);
|
||||
|
||||
fprintf(stderr,
|
||||
"Unknown foreign method '%s' for test '%s'\n", fullName, testName);
|
||||
@ -38,6 +46,18 @@ static WrenForeignMethodFn bindForeign(
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static WrenForeignClassMethods bindForeignClass(
|
||||
WrenVM* vm, const char* module, const char* className)
|
||||
{
|
||||
WrenForeignClassMethods methods = { NULL, NULL };
|
||||
if (strcmp(module, "main") != 0) return methods;
|
||||
|
||||
REGISTER_CLASS(foreign_class, foreignClass);
|
||||
|
||||
return methods;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
if (argc != 2)
|
||||
@ -54,6 +74,7 @@ int main(int argc, const char* argv[])
|
||||
strcat(testPath, testName);
|
||||
strcat(testPath, ".wren");
|
||||
|
||||
runFile(bindForeign, testPath);
|
||||
setForeignCallbacks(bindForeignMethod, bindForeignClass);
|
||||
runFile(testPath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ static void returnFalse(WrenVM* vm)
|
||||
wrenReturnBool(vm, false);
|
||||
}
|
||||
|
||||
WrenForeignMethodFn returnsBindForeign(const char* signature)
|
||||
WrenForeignMethodFn returnsBindMethod(const char* signature)
|
||||
{
|
||||
if (strcmp(signature, "static Api.implicitNull") == 0) return implicitNull;
|
||||
if (strcmp(signature, "static Api.returnInt") == 0) return returnInt;
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
#include "wren.h"
|
||||
|
||||
WrenForeignMethodFn returnsBindForeign(const char* signature);
|
||||
WrenForeignMethodFn returnsBindMethod(const char* signature);
|
||||
|
||||
@ -15,7 +15,7 @@ static void getValue(WrenVM* vm)
|
||||
wrenReleaseValue(vm, value);
|
||||
}
|
||||
|
||||
WrenForeignMethodFn valueBindForeign(const char* signature)
|
||||
WrenForeignMethodFn valueBindMethod(const char* signature)
|
||||
{
|
||||
if (strcmp(signature, "static Api.value=(_)") == 0) return setValue;
|
||||
if (strcmp(signature, "static Api.value") == 0) return getValue;
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
#include "wren.h"
|
||||
|
||||
WrenForeignMethodFn valueBindForeign(const char* signature);
|
||||
WrenForeignMethodFn valueBindMethod(const char* signature);
|
||||
|
||||
Reference in New Issue
Block a user