Skip to content

Commit

Permalink
Add Vala<->Wren marshalling
Browse files Browse the repository at this point in the history
- Use the userdata so we can support any number of classes/methods
  using only one callsite for each Wren interface
- Add marshalling for a number of types
- VMV.expose_class: take Type parameter.  Instead of accepting the type
  as a template parameter, accept it as a straight runtime function
  parameter.  We do not do any compile-time processing in that function,
  so do not imply that we do.
- Add getting and setting of foreign properties on exposed classes

Also:
- add tests
- VMV: add accessor for VM
- Rename wrennull.c to shim.c
- Add obj_from_ppobj() to shim.c
- Add Object to Marshal.to_value_raw()
- pretty-print
  • Loading branch information
cxw42 committed Apr 25, 2021
1 parent e7a3c6c commit 454b323
Show file tree
Hide file tree
Showing 16 changed files with 850 additions and 1,098 deletions.
1 change: 1 addition & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ prettyprint:
t/140-roundtrip-data-t.vala \
t/150-marshal-t.vala \
t/200-expose-class-t.vala \
t/210-marshal-class-t.vala \
$(EOL)
-$(AM_V_GEN)uncrustify -c $(top_srcdir)/.uncrustifyrc --replace \
$(c_all_sources) \
Expand Down
4 changes: 2 additions & 2 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ DISTCLEANFILES += $(pkgconfig_DATA)

# === Library, pass 1 ===
# The Vala files, as a convenience library. This is to get the ordering
# right with respect to wrennull.c.
# right with respect to shim.c.

noinst_LTLIBRARIES += libinternal.la

Expand All @@ -42,7 +42,7 @@ libinternal_la_SOURCES = \
libinternal_stamp = $(srcdir)/libinternal_la_vala.stamp

# Make libinternal get built first. That way $(generated_header) will exist
# when it's time to build wrennull.c.
# when it's time to build shim.c.
BUILT_SOURCES += $(libinternal_stamp)

generated_header = $(srcdir)/wren-vala-generated.h
Expand Down
13 changes: 10 additions & 3 deletions src/basics.vapi
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ namespace Wren {
public delegate void *ReallocateFn(void *memory, size_t newSize);

[CCode(has_target = false)]
public delegate void ForeignMethodFn(VM vm);
public delegate void ForeignMethodFn(VM vm, void *userData);

[CCode(has_target = false)]
public delegate void FinalizerFn(void *data);
public delegate void FinalizerFn(void *data, void *userData);

[CCode(has_target = false)]
public delegate string ResolveModuleFn(VM vm, string importer, string name);
Expand All @@ -53,8 +53,15 @@ namespace Wren {
[CCode(has_target = false)]
public delegate LoadModuleResult LoadModuleFn(VM vm, string name);

[SimpleType]
public struct BindForeignMethodResult
{
ForeignMethodFn executeFn;
void* userData;
}

[CCode(has_target = false)]
public delegate ForeignMethodFn BindForeignMethodFn(VM vm, string module,
public delegate BindForeignMethodResult BindForeignMethodFn(VM vm, string module,
string className,
bool isStatic,
string signature);
Expand Down
126 changes: 107 additions & 19 deletions src/marshal.vala
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,17 @@ namespace Wren {
* We use this because you can't create {@link GLib.Value}s of
* {@link GLib.Type.NONE}.
*
* In wrennull.c.
* Defined in shim.c.
*/
public extern GLib.Type get_null_type();

/**
* Convert GObject** to GObject*
*
* Defined in shim.c.
*/
private extern unowned Object obj_from_ppobj(void *ppobj);

/**
* Initialize the Wren Vala bindings.
*
Expand All @@ -27,7 +34,7 @@ namespace Wren {
*/
public void static_init()
{
if(!static_init_done) {
if(static_init_done) {
return;
}

Expand All @@ -44,22 +51,34 @@ namespace Wren {
[CCode(cheader_filename="wren-vala-merged.h")]
namespace Marshal {

public errordomain MarshalError {
public errordomain Error {
/** Slot or other item not found */
ENOENT,
/** Unknown type, or type I can't handle */
ENOTSUP,
/** Forbidden by the language (can't be fixed by a wren-vala change) */
EINVAL,
}

// === Wren -> Vala =============================================

/**
* Wrap a single Wren slot in a GValue.
* Get a GLib.Value from a single Wren slot.
*
* The mapping is:
* * Wren BOOL -> GLib.Type.BOOLEAN
* * Wren NULL -> GLib.Type.NONE
*
* * Wren BOOL -> GLib.Type.BOOLEAN
* * Wren NUM -> double
* * Wren FOREIGN -> GLib.Object (this assumes you don't create any
* objects using something other than wren-vala)
* * Wren STRING -> string
* * Wren NULL -> Wren.get_null_type()
*
* @param vm The vm to read from
* @param slot The slot to grab. It must exist.
*/
public Value to_value_raw(Wren.VM vm, int slot)
public Value value_from_slot(Wren.VM vm, int slot)
throws Marshal.Error
{
var ty = vm.GetSlotType(slot);

Expand All @@ -69,8 +88,10 @@ namespace Wren {
case NUM:
return vm.GetSlotDouble(slot);
case FOREIGN:
// TODO
break;
var retval = Value(GLib.Type.OBJECT);
var obj = obj_from_ppobj(vm.GetSlotForeign(slot));
retval.set_object(obj); // adds a ref
return retval;
case LIST:
// TODO
break;
Expand All @@ -85,11 +106,12 @@ namespace Wren {
// TODO
break;
default:
assert_not_reached();
break;
}

return Value(GLib.Type.INVALID);
}
throw new Marshal.Error.ENOTSUP(
"I don't know how to send type Wren %s to Vala".printf(ty.to_string()));
} // value_from_slot

/**
* Wrap Wren slots in a GValue array.
Expand All @@ -100,26 +122,92 @@ namespace Wren {
* slot array)
* @return An array of freshly-created GValues.
*/
public Value[] to_values_raw(Wren.VM vm, int first_slot = 0, int num_slots = -1)
throws MarshalError
public Value[] values_from_slots(Wren.VM vm, int first_slot = 0, int num_slots = -1)
throws Marshal.Error
{
if(num_slots == -1) {
num_slots = vm.GetSlotCount();
}

if(first_slot + num_slots >= vm.GetSlotCount()) {
throw new MarshalError.ENOENT(
if(first_slot + num_slots > vm.GetSlotCount()) {
throw new Marshal.Error.ENOENT(
"Slots up to %d requested, but only %d are available".printf(
first_slot + num_slots, vm.GetSlotCount()));
}

Value[] retval = new Value[num_slots];
for(int i=0; i<num_slots; ++i) {
int curr_slot = first_slot + i;
retval[i] = to_value_raw(vm, curr_slot);
retval[i] = value_from_slot(vm, curr_slot);
}

return retval;
}
}
} // values_from_slots()

// === Vala -> Wren =============================================

/**
* Fill a Wren slot from a GValue.
*
* * Bool, number, and string are passed straight through.
* * Object is invalid --- there's no way to load an instance
* you didn't create in Wren.
*
* @param vm The VM
* @param slot Slot to set
* @param val New value
*/
public void slot_from_value(Wren.VM vm, int slot, Value val)
throws Marshal.Error
{
var vty = val.type();

if(vty == get_null_type()) {
// Outside the switch because it's not a compile-time constant
vm.SetSlotNull(slot);
return;
}

switch(vty) {
case GLib.Type.BOOLEAN:
vm.SetSlotBool(slot, val.get_boolean());
return;
case GLib.Type.DOUBLE:
case GLib.Type.FLOAT:
case GLib.Type.INT:
case GLib.Type.INT64:
case GLib.Type.LONG:
case GLib.Type.UINT:
case GLib.Type.UINT64:
case GLib.Type.ULONG:
var dblval = Value(GLib.Type.DOUBLE);
val.transform(ref dblval);
vm.SetSlotDouble(slot, dblval.get_double());
return;
// FOREIGN: handled below
// case LIST:
// // TODO
// break;
// case MAP:
// // TODO
// break;
case GLib.Type.STRING:
vm.SetSlotString(slot, val.get_string());
return;
// case UNKNOWN:
// // TODO
// break;
default:
if(vty.is_object()) {
throw new Marshal.Error.EINVAL("Cannot send Object instances to Wren");
}
break;
}

throw new Marshal.Error.ENOTSUP(
"I don't know how to send Vala type %s to Wren".printf(vty.to_string()));
} // set_slot()

} // namespace Marshal

} // namespace Wren
24 changes: 23 additions & 1 deletion src/wrennull.c → src/shim.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
// wrennull.c: Wren's null type, as a GType
// shim.c: wren-vala routines that are easier to implement in C
//
// Currently contains:
// - A GType representing Wren's null type
// - Object** manipulation
//
// By Christopher White <[email protected]>
// Copyright (c) 2021 Christopher White. All Rights Reserved.
Expand All @@ -15,15 +19,18 @@ G_BEGIN_DECLS
static void value_nop(GValue *v)
{
}

static void value_nop2(const GValue *s, GValue *d)
{
}

static gchar *value_collect_nop(GValue *value, guint n_collect_values,
GTypeCValue *collect_values,
guint collect_flags)
{
return (gchar *)NULL;
}

static gchar *value_collect_nop_const(const GValue *value, guint n_collect_values,
GTypeCValue *collect_values,
guint collect_flags)
Expand Down Expand Up @@ -93,4 +100,19 @@ wrenget_null_type()
return wrennull_type_id__volatile;
}

/**
* wrenobj_from_ppobj:
* @ppobj: (transfer none): A GObject**, as a void*
*
* Does not touch the refcount of **ppobj. This exists because it was faster
* to write this function than to debug the compile-time errors I was getting
* when I tried to do the same in Vala! :)
*
* Return: (transfer none): A GObject*
*/
GObject *wrenobj_from_ppobj(void *ppobj)
{
return *(GObject **)ppobj;
}

G_END_DECLS
Loading

0 comments on commit 454b323

Please sign in to comment.