Ver Fonte

Merge branch 'web-stack' of Tilo15/Invercargill-Json into master

Billy Barrow há 1 mês atrás
pai
commit
9133f1f23a

+ 12 - 0
.usmignore

@@ -0,0 +1,12 @@
+# Build trees at any depth (directory-only: the directory and all beneath it)
+builddir*/
+
+# Database artifacts by basename at any depth
+*.sqlite*
+db.sqlite
+
+# Foreign build tree at the package root
+build/
+
+# Local agent session state (directory-only)
+.kilo/

+ 10 - 17
MANIFEST.usm

@@ -1,11 +1,12 @@
 {
   "name": "invercargill-json",
-  "version": "0.0.1",
+  "version": "0.1",
   "summary": "Json support for Invercargill",
   "licences": [ {"name": "LGPLv3", "category": "libre", "text": "LICENSE"} ],
   "flags": ["ninjaStyleProgress"],
   "provides": {
     "lib:libinvercargill-json.so": "as-expected",
+    "bin:invercargill-json-test-suite": "as-expected",
     "inc:invercargill-json.h": "as-expected",
     "vapi:invercargill-json.vapi": "as-expected",
     "gir:invercargill_json-1.0.gir": "as-expected",
@@ -14,28 +15,20 @@
   },
   "depends": {
     "runtime": [
-      "lib:libinvercargill-1.so",
       "lib:libglib-2.0.so.0",
       "lib:libgobject-2.0.so.0",
-      "lib:libgee-0.8.so.2",
-      "lib:libc.so.6",
-      "lib:libjson-glib-1.0.so",
       "lib:libgio-2.0.so.0",
-      "lib:libpcre2-8.so.0",
-      "lib:libffi.so.8",
-      "lib:libgmodule-2.0.so.0",
-      "lib:libz.so.1",
-      "lib:libmount.so.1",
-      "lib:libselinux.so.1",
-      "lib:libblkid.so.1"
+      "lib:libjson-glib-1.0.so.0",
+      "lib:libinvercargill-1.so"
     ],
     "build": [
       "bin:valac",
       "bin:meson",
+      "bin:ninja",
+      "bin:gcc",
       "bin:g-ir-compiler",
       "pc:glib-2.0.pc",
       "pc:gobject-2.0.pc",
-      "pc:gio-2.0.pc",
       "pc:gee-0.8.pc",
       "pc:json-glib-1.0.pc",
       "pc:invercargill-1.pc"
@@ -45,8 +38,8 @@
     ]
   },
   "execs": {
-    "build": "scripts/build.sh",
-    "install": "scripts/install.sh",
-    "post_install": "scripts/post-install.sh"
+    "build": "usm-scripts/build.sh",
+    "install": "usm-scripts/install.sh",
+    "postInstall": "usm-scripts/post-install.sh"
   }
-}
+}

+ 111 - 92
src/lib/Json.vala

@@ -80,160 +80,179 @@ namespace InvercargillJson {
 
             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();
 
             // Third priority is conversion of known types
             if(type == typeof(DateTime)) {
                 node.set_string (element.as<DateTime>().format_iso8601());
-                return;
+                return true;
             }
             if(type == typeof(Invercargill.BinaryData)) {
                 node.set_string (element.as<Invercargill.BinaryData>().to_base64());
-                return;
+                return true;
             }
             if(type == typeof(string)) {
                 node.set_string (element.as<string>());
-                return;
+                return true;
             }
             if(type == typeof(bool)) {
                 node.set_boolean (element.as<bool>());
-                return;
+                return true;
             }
             if(type == typeof(double)) {
                 node.set_double (element.as<double?>());
-                return;
+                return true;
             }
             if(type == typeof(int64)) {
                 node.set_int (element.as<int64?>());
-                return;
+                return true;
             }
             if(type == typeof(uint8)) {
                 node.set_int (element.as<uint>());
-                return;
+                return true;
             }
             if(type == typeof(int8)) {
                 node.set_int (element.as<int>());
-                return;
+                return true;
             }
             if(type == typeof(uint16)) {
                 node.set_int (element.as<uint16>());
-                return;
+                return true;
             }
             if(type == typeof(int16)) {
                 node.set_int (element.as<int16>());
-                return;
+                return true;
             }
             if(type == typeof(uint32)) {
                 node.set_int (element.as<uint32>());
-                return;
+                return true;
             }
             if(type == typeof(int32)) {
                 node.set_int (element.as<int32>());
-                return;
+                return true;
             }
             if(type == typeof(uint64?)) {
                 node.set_int ((int64)element.as<uint64?>());
-                return;
+                return true;
             }
             if(type == typeof(float?)) {
                 node.set_double (element.as<float?>());
-                return;
+                return true;
             }            
 
             // Last priority is the element's native conversions
             if(element.assignable_to<string>()) {
                 node.set_string (element.as<string>());
-                return;
+                return true;
             }
             if(element.assignable_to<bool> ()) {
                 node.set_boolean(element.as<bool>());
-                return;
+                return true;
             }
             if(element.assignable_to<double?>()) {
                 node.set_double(element.as<double?>());
-                return;
+                return true;
             }
             if(element.assignable_to<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) {

+ 1 - 3
src/lib/Jsonl.vala

@@ -24,9 +24,7 @@ namespace InvercargillJson {
                     return i != null;
                 }
                 catch (Error e) {
-                    warning (@"Premature end of JsonlInputStream due to error: $(e.message)");
-                    i = null;
-                    return false;
+                    error (@"JsonlInputStream iteration failed: $(e.message) — treated as a hard read failure, not end-of-stream");
                 }
             });
         }

+ 33 - 0
src/tests/Integration/Jsonl.vala

@@ -0,0 +1,33 @@
+using GLib;
+
+void jsonl_swallowed_error_tests() {
+
+    Test.add_func("/invercargill/json/jsonl/midstream_error_is_fatal_not_truncation", () => {
+        string exe;
+        if (Path.is_absolute(args_holder[0])) {
+            exe = args_holder[0];
+        }
+        else {
+            exe = Path.build_filename(Environment.get_current_dir(), args_holder[0]);
+        }
+        assert_true(File.new_for_path(exe).query_exists());
+
+        try {
+            string[] argv = { exe, "--jsonl-child" };
+            int status;
+            string child_stdout;
+            string child_stderr;
+            Process.spawn_sync(
+                null, argv, Environ.get(),
+                SpawnFlags.SEARCH_PATH,
+                null, out child_stdout, out child_stderr, out status);
+
+            assert_true(Process.if_signaled(status));
+            assert_true(child_stderr.contains("JsonlInputStream iteration failed"));
+            assert_false(child_stdout.contains("elements="));
+        }
+        catch (Error e) {
+            assert_no_error(e);
+        }
+    });
+}

+ 22 - 1
src/tests/TestRunner.vala

@@ -1,15 +1,36 @@
 
+string[] args_holder;
+
 public static int main(string[] args) {
 
+    if (args.length > 1 && args[1] == "--jsonl-child") {
+        run_jsonl_child();
+        printerr("jsonl-child completed without hitting the mid-stream failure\n");
+        return 1;
+    }
+
+    args_holder = args;
     Test.init(ref args);
 
     promotion_tests();
     property_mapper_tests();
     json_tests();
     from_element_scalar_tests();
-    
+    jsonl_swallowed_error_tests();
+
     Test.run();
 
 
     return 0;
+}
+
+void run_jsonl_child() {
+    uint8[] data = "{\"a\": 1}\nthis is not json\n".data;
+    var stream = new InvercargillJson.JsonlInputStream(
+        new MemoryInputStream.from_data(data));
+    uint count = 0;
+    foreach (var element in stream) {
+        count++;
+    }
+    print("elements=%u\n", count);
 }

+ 1 - 0
src/tests/meson.build

@@ -4,5 +4,6 @@ sources += files('Integration/Promotion.vala')
 sources += files('Integration/PropertyMapper.vala')
 sources += files('Integration/Json.vala')
 sources += files('Integration/FromElement.vala')
+sources += files('Integration/Jsonl.vala')
 
 executable('invercargill-json-test-suite', sources, dependencies: dependencies, install: true)

+ 10 - 0
usm-scripts/build.sh

@@ -0,0 +1,10 @@
+#!/bin/bash
+set -e
+
+src_dir=$(pwd)
+build_dir=$1
+
+cd ${build_dir}
+
+meson setup ${src_dir}/src --prefix=${PREFIX} --libdir=${LIBDIR} --bindir=${BINDIR} --includedir=${INCLUDEDIR}
+ninja

+ 10 - 0
usm-scripts/install.sh

@@ -0,0 +1,10 @@
+#!/bin/bash
+set -e
+
+src_dir=$(pwd)
+build_dir=$1
+install_dir=$2
+
+cd ${build_dir}
+
+meson install --destdir ${install_dir}

+ 2 - 0
usm-scripts/post-install.sh

@@ -0,0 +1,2 @@
+#!/bin/bash
+ldconfig