|
@@ -80,160 +80,179 @@ namespace InvercargillJson {
|
|
|
|
|
|
|
|
node = new Json.Node (Json.NodeType.VALUE);
|
|
node = new Json.Node (Json.NodeType.VALUE);
|
|
|
|
|
|
|
|
- // For ValueElement scalars, read the underlying GValue directly instead
|
|
|
|
|
- // of going through element.as<T?>(). The generic extraction path is the
|
|
|
|
|
- // authoritative source of the held type but is unsafe for 64-bit
|
|
|
|
|
- // integers (it returns a corrupt value that crashes Json.Node.set_int)
|
|
|
|
|
- // and the dispatch below has no branches for long/ulong (so they fall
|
|
|
|
|
- // through to the string fallback and serialise as quoted JSON strings).
|
|
|
|
|
- // Reading the raw GValue lets us dispatch on the fundamental type and
|
|
|
|
|
- // use the matching g_value_get_* accessor for every numeric type.
|
|
|
|
|
- // Fixes upstream issues I-1 (int64/uint64 segfault), I-2 (bool) and
|
|
|
|
|
- // I-3 (numeric serialised as string).
|
|
|
|
|
- if (element is Invercargill.ValueElement) {
|
|
|
|
|
- var raw = ((Invercargill.ValueElement) element).get_value();
|
|
|
|
|
- var value_type = raw.type();
|
|
|
|
|
- if (value_type.is_a(Type.STRING)) {
|
|
|
|
|
- node.set_string(raw.get_string());
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- if (value_type.is_a(Type.BOOLEAN)) {
|
|
|
|
|
- node.set_boolean(raw.get_boolean());
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- if (value_type.is_a(Type.DOUBLE)) {
|
|
|
|
|
- node.set_double(raw.get_double());
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- if (value_type.is_a(Type.FLOAT)) {
|
|
|
|
|
- node.set_double((double)raw.get_float());
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- // Every integer fundamental collapses to JSON's single int node.
|
|
|
|
|
- if (value_type.is_a(Type.INT64)) {
|
|
|
|
|
- node.set_int(raw.get_int64());
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- if (value_type.is_a(Type.UINT64)) {
|
|
|
|
|
- node.set_int((int64)raw.get_uint64());
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- if (value_type.is_a(Type.LONG)) {
|
|
|
|
|
- node.set_int((int64)raw.get_long());
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- if (value_type.is_a(Type.ULONG)) {
|
|
|
|
|
- node.set_int((int64)raw.get_ulong());
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- if (value_type.is_a(Type.INT)) {
|
|
|
|
|
- node.set_int((int64)raw.get_int());
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- if (value_type.is_a(Type.UINT)) {
|
|
|
|
|
- node.set_int((int64)raw.get_uint());
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- if (value_type.is_a(Type.CHAR)) {
|
|
|
|
|
- node.set_int((int64)raw.get_schar());
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- if (value_type.is_a(Type.UCHAR)) {
|
|
|
|
|
- node.set_int((int64)raw.get_uchar());
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- if (value_type.is_a(Type.ENUM)) {
|
|
|
|
|
- node.set_int((int64)raw.get_enum());
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- if (value_type.is_a(Type.FLAGS)) {
|
|
|
|
|
- node.set_int((int64)raw.get_flags());
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- // Non-fundamental held types (DateTime, BinaryData, objects, ...)
|
|
|
|
|
- // fall through to the dispatch below.
|
|
|
|
|
|
|
+ // Everything scalar lives in out-of-line helpers: the fully
|
|
|
|
|
+ // inlined version produced a single ~13KB function whose
|
|
|
|
|
+ // optimised stack-slot reuse read stale pointers on some
|
|
|
|
|
+ // layouts (intermittent SIGSEGV during Statum slot signing —
|
|
|
|
|
+ // first the double/int64 GValue paths, then the generic
|
|
|
|
|
+ // element.as<T> dispatch). Keeping this function small
|
|
|
|
|
+ // sidesteps that codegen bug entirely.
|
|
|
|
|
+ if (element is Invercargill.ValueElement
|
|
|
|
|
+ && set_node_from_value(node, ((Invercargill.ValueElement) element).get_value())) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (try_set_node_from_element(node, element)) {
|
|
|
|
|
+ return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Could not convert
|
|
|
|
|
+ var type = element.type();
|
|
|
|
|
+ throw new Invercargill.ElementError.INVALID_CONVERSION(@"No way to convert element $(element.get_type ().name()) containing type $(type.name()) to a type suitable for a JsonElement.");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Out-of-line typed dispatch for {@link JsonElement.from_element}:
|
|
|
|
|
+ * known element types first, then the element's native
|
|
|
|
|
+ * conversions. Returns true when `node` was filled.
|
|
|
|
|
+ */
|
|
|
|
|
+ private static bool try_set_node_from_element(Json.Node node, Invercargill.Element element) throws Invercargill.ElementError {
|
|
|
var type = element.type();
|
|
var type = element.type();
|
|
|
|
|
|
|
|
// Third priority is conversion of known types
|
|
// Third priority is conversion of known types
|
|
|
if(type == typeof(DateTime)) {
|
|
if(type == typeof(DateTime)) {
|
|
|
node.set_string (element.as<DateTime>().format_iso8601());
|
|
node.set_string (element.as<DateTime>().format_iso8601());
|
|
|
- return;
|
|
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
if(type == typeof(Invercargill.BinaryData)) {
|
|
if(type == typeof(Invercargill.BinaryData)) {
|
|
|
node.set_string (element.as<Invercargill.BinaryData>().to_base64());
|
|
node.set_string (element.as<Invercargill.BinaryData>().to_base64());
|
|
|
- return;
|
|
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
if(type == typeof(string)) {
|
|
if(type == typeof(string)) {
|
|
|
node.set_string (element.as<string>());
|
|
node.set_string (element.as<string>());
|
|
|
- return;
|
|
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
if(type == typeof(bool)) {
|
|
if(type == typeof(bool)) {
|
|
|
node.set_boolean (element.as<bool>());
|
|
node.set_boolean (element.as<bool>());
|
|
|
- return;
|
|
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
if(type == typeof(double)) {
|
|
if(type == typeof(double)) {
|
|
|
node.set_double (element.as<double?>());
|
|
node.set_double (element.as<double?>());
|
|
|
- return;
|
|
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
if(type == typeof(int64)) {
|
|
if(type == typeof(int64)) {
|
|
|
node.set_int (element.as<int64?>());
|
|
node.set_int (element.as<int64?>());
|
|
|
- return;
|
|
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
if(type == typeof(uint8)) {
|
|
if(type == typeof(uint8)) {
|
|
|
node.set_int (element.as<uint>());
|
|
node.set_int (element.as<uint>());
|
|
|
- return;
|
|
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
if(type == typeof(int8)) {
|
|
if(type == typeof(int8)) {
|
|
|
node.set_int (element.as<int>());
|
|
node.set_int (element.as<int>());
|
|
|
- return;
|
|
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
if(type == typeof(uint16)) {
|
|
if(type == typeof(uint16)) {
|
|
|
node.set_int (element.as<uint16>());
|
|
node.set_int (element.as<uint16>());
|
|
|
- return;
|
|
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
if(type == typeof(int16)) {
|
|
if(type == typeof(int16)) {
|
|
|
node.set_int (element.as<int16>());
|
|
node.set_int (element.as<int16>());
|
|
|
- return;
|
|
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
if(type == typeof(uint32)) {
|
|
if(type == typeof(uint32)) {
|
|
|
node.set_int (element.as<uint32>());
|
|
node.set_int (element.as<uint32>());
|
|
|
- return;
|
|
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
if(type == typeof(int32)) {
|
|
if(type == typeof(int32)) {
|
|
|
node.set_int (element.as<int32>());
|
|
node.set_int (element.as<int32>());
|
|
|
- return;
|
|
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
if(type == typeof(uint64?)) {
|
|
if(type == typeof(uint64?)) {
|
|
|
node.set_int ((int64)element.as<uint64?>());
|
|
node.set_int ((int64)element.as<uint64?>());
|
|
|
- return;
|
|
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
if(type == typeof(float?)) {
|
|
if(type == typeof(float?)) {
|
|
|
node.set_double (element.as<float?>());
|
|
node.set_double (element.as<float?>());
|
|
|
- return;
|
|
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Last priority is the element's native conversions
|
|
// Last priority is the element's native conversions
|
|
|
if(element.assignable_to<string>()) {
|
|
if(element.assignable_to<string>()) {
|
|
|
node.set_string (element.as<string>());
|
|
node.set_string (element.as<string>());
|
|
|
- return;
|
|
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
if(element.assignable_to<bool> ()) {
|
|
if(element.assignable_to<bool> ()) {
|
|
|
node.set_boolean(element.as<bool>());
|
|
node.set_boolean(element.as<bool>());
|
|
|
- return;
|
|
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
if(element.assignable_to<double?>()) {
|
|
if(element.assignable_to<double?>()) {
|
|
|
node.set_double(element.as<double?>());
|
|
node.set_double(element.as<double?>());
|
|
|
- return;
|
|
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
if(element.assignable_to<int64?>()) {
|
|
if(element.assignable_to<int64?>()) {
|
|
|
node.set_int(element.as<int64?>());
|
|
node.set_int(element.as<int64?>());
|
|
|
- return;
|
|
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Could not convert
|
|
|
|
|
- throw new Invercargill.ElementError.INVALID_CONVERSION(@"No way to convert element $(element.get_type ().name()) containing type $(type.name()) to a type suitable for a JsonElement.");
|
|
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Fills `node` from a ValueElement's raw GValue when it holds a
|
|
|
|
|
+ * fundamental JSON-representable type, and returns true; false when
|
|
|
|
|
+ * the held type is non-fundamental (DateTime, objects, ...) and the
|
|
|
|
|
+ * caller must fall through to the generic dispatch.
|
|
|
|
|
+ */
|
|
|
|
|
+ private static bool set_node_from_value(Json.Node node, Value raw) {
|
|
|
|
|
+ var value_type = raw.type();
|
|
|
|
|
+ if (value_type.is_a(Type.STRING)) {
|
|
|
|
|
+ node.set_string(raw.get_string());
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (value_type.is_a(Type.BOOLEAN)) {
|
|
|
|
|
+ node.set_boolean(raw.get_boolean());
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (value_type.is_a(Type.DOUBLE)) {
|
|
|
|
|
+ node.set_double(raw.get_double());
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (value_type.is_a(Type.FLOAT)) {
|
|
|
|
|
+ node.set_double((double)raw.get_float());
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ // Every integer fundamental collapses to JSON's single int node.
|
|
|
|
|
+ if (value_type.is_a(Type.INT64)) {
|
|
|
|
|
+ node.set_int(raw.get_int64());
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (value_type.is_a(Type.UINT64)) {
|
|
|
|
|
+ node.set_int((int64)raw.get_uint64());
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (value_type.is_a(Type.LONG)) {
|
|
|
|
|
+ node.set_int((int64)raw.get_long());
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (value_type.is_a(Type.ULONG)) {
|
|
|
|
|
+ node.set_int((int64)raw.get_ulong());
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (value_type.is_a(Type.INT)) {
|
|
|
|
|
+ node.set_int((int64)raw.get_int());
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (value_type.is_a(Type.UINT)) {
|
|
|
|
|
+ node.set_int((int64)raw.get_uint());
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (value_type.is_a(Type.CHAR)) {
|
|
|
|
|
+ node.set_int((int64)raw.get_schar());
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (value_type.is_a(Type.UCHAR)) {
|
|
|
|
|
+ node.set_int((int64)raw.get_uchar());
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (value_type.is_a(Type.ENUM)) {
|
|
|
|
|
+ node.set_int((int64)raw.get_enum());
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (value_type.is_a(Type.FLAGS)) {
|
|
|
|
|
+ node.set_int((int64)raw.get_flags());
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public bool assignable_to_type (GLib.Type type) {
|
|
public bool assignable_to_type (GLib.Type type) {
|