Ver código fonte

ffi: add CString helpers for C returns and buffers

Eric Streit 1 dia atrás
pai
commit
09fefdd286

+ 1 - 0
compiler/run_tests.sh

@@ -344,6 +344,7 @@ expect_run_files DBasicProg 49 d_basic.def d_basic.mod d_basic_prog.mod
 expect_run_files CForeignProg 42 c_foreign.def c_foreign.mod c_foreign_prog.mod
 expect_run_files TCStringProg 42 t_cstring.def t_cstring_prog.mod
 expect_run_files TCStringQualProg 42 t_cstring.def t_cstring_qual_prog.mod
+expect_run_files CStrProg 42 ../runtime/syslib/CStr.def ../runtime/syslib/CStr.mod tests/cstr_test.def tests/cstr_prog.mod
 expect_run_files EEnumProg 42 e_enum.def e_enum_re.def e_enum_prog.mod
 expect_run_files TClassDefProg 8 t_classdef.def t_classdef_prog.mod
 expect_run_files DQualProg 46 d_basic.def d_basic.mod d_qual_prog.mod

+ 52 - 0
compiler/tests/cstr_prog.mod

@@ -0,0 +1,52 @@
+MODULE CStrProg;
+(* C string returns, char ** outputs and C-written CHAR buffers.
+
+   CStr.CString is an explicit ADDRESS alias; CStr.Copy is the
+   C-to-Modula-2 conversion.  Expected exit code: 42. *)
+
+FROM CStr IMPORT CString, Length, Copy, IsNil;
+FROM CStrTest IMPORT m2_cstr_hello, m2_cstr_out, m2_cstr_fill;
+
+VAR
+  ExitCode : INTEGER;
+  buf : ARRAY [0..15] OF CHAR;
+  p : CString;
+  n : CARDINAL;
+  ok : BOOLEAN;
+
+BEGIN
+  p := m2_cstr_hello();
+  ok := (NOT IsNil(p)) AND (Length(p) = 5);
+  IF ok THEN
+    n := Copy(p, buf);
+    ok := (n = 5) AND (buf[0] = 'h') AND (buf[4] = 'o')
+          AND (buf[5] = CHR(0))
+  END;
+
+  IF ok THEN
+    p := NIL;
+    ok := IsNil(p) AND (Length(p) = 0);
+    IF ok THEN
+      m2_cstr_out(p);
+      ok := (NOT IsNil(p)) AND (Length(p) = 5);
+      IF ok THEN
+        n := Copy(p, buf);
+        ok := (n = 5) AND (buf[0] = 'w') AND (buf[4] = 'd')
+              AND (buf[5] = CHR(0))
+      END
+    END
+  END;
+
+  IF ok THEN
+    n := m2_cstr_fill(buf, 16);
+    ok := (n = 6) AND (buf[0] = 'b') AND (buf[5] = 'r')
+          AND (buf[6] = CHR(0))
+  END;
+
+  IF ok THEN
+    n := Copy(NIL, buf);
+    ok := (n = 0) AND (buf[0] = CHR(0))
+  END;
+
+  IF ok THEN ExitCode := 42 ELSE ExitCode := 1 END
+END CStrProg.

+ 13 - 0
compiler/tests/cstr_test.def

@@ -0,0 +1,13 @@
+DEFINITION MODULE FOR "C" CStrTest;
+(* C ABI test interface: C strings in results and char ** outputs. *)
+
+FROM CStr IMPORT CString;
+
+EXPORT UNQUALIFIED m2_cstr_hello, m2_cstr_out, m2_cstr_fill;
+
+PROCEDURE m2_cstr_hello () : CString;
+PROCEDURE m2_cstr_out (VAR p : CString);
+PROCEDURE m2_cstr_fill (VAR buf : ARRAY OF CHAR;
+                        cap : CARDINAL) : CARDINAL;
+
+END CStrTest.

+ 5 - 0
docs/features.md

@@ -84,6 +84,11 @@ Legend: ✅ done · 🔄 partial · ⏸ not started / deferred.
   C ABI, so `ARRAY OF CHAR` actuals are passed as NUL-terminated
   payload pointers (`descriptor+8`).  Plain `EXTERNAL` procedures in
   ordinary modules keep the descriptor ABI used by the runtime shim.
+- ✅ `CStr` runtime module: explicit `CString`/`CBuffer` aliases,
+  `Length`, `Copy` (C string to `ARRAY OF CHAR`) and `IsNil`.
+  `CString` results and `VAR CString` (`char **`) outputs cover C
+  string returns and out-parameters; writable C buffers can still be
+  declared as `VAR ARRAY OF CHAR`.
 - ✅ Set builtins `INCL`/`EXCL` and `SHIFT`/`ROTATE`; `SET OF <enum>`
   and `SET OF <char-range>`; bounded sets up to 1,000,000 bits;
   `SET OF CARDINAL` and `SET OF INTEGER` use a sorted range-list

+ 29 - 0
runtime/syslib/CStr.def

@@ -0,0 +1,29 @@
+DEFINITION MODULE CStr;
+(* Explicit C string types and conversions.
+
+   CString is a pointer to a NUL-terminated C byte string.  It is an
+   ADDRESS alias, so it can be stored, compared with NIL, returned from
+   a FOR "C" procedure, and passed to another C function without any
+   hidden descriptor conversion.
+
+   CBuffer is the same representation, used for a writable C buffer.
+   A V3 ARRAY OF CHAR can still be passed directly to a C function;
+   use Copy for the reverse direction (C string -> V3 string). *)
+
+FROM SYSTEM IMPORT ADDRESS;
+
+TYPE
+  CString = ADDRESS;
+  CBuffer = ADDRESS;
+
+PROCEDURE Length (s : CString) : CARDINAL;
+(* strlen-like length; NIL has length 0. *)
+
+PROCEDURE Copy (s : CString; VAR dst : ARRAY OF CHAR) : CARDINAL;
+(* Copy s into dst, truncating to dst's capacity and NUL-terminating.
+   NIL clears dst.  Returns the number of bytes copied. *)
+
+PROCEDURE IsNil (s : CString) : BOOLEAN;
+(* TRUE for a NIL C string. *)
+
+END CStr.

+ 31 - 0
runtime/syslib/CStr.mod

@@ -0,0 +1,31 @@
+IMPLEMENTATION MODULE CStr;
+
+FROM SYSTEM IMPORT ADDRESS;
+
+PROCEDURE m2_c_strlen (s : ADDRESS) : INTEGER;
+  EXTERNAL;
+
+PROCEDURE m2_cstr_copy (VAR dst : ARRAY OF CHAR;
+                        src : ADDRESS) : CARDINAL;
+  EXTERNAL;
+
+PROCEDURE Length (s : CString) : CARDINAL;
+  VAR n : INTEGER;
+  BEGIN
+    IF s = NIL THEN RETURN 0 END;
+    n := m2_c_strlen(s);
+    IF n < 0 THEN RETURN 0 END;
+    RETURN VAL(CARDINAL, n)
+  END Length;
+
+PROCEDURE Copy (s : CString; VAR dst : ARRAY OF CHAR) : CARDINAL;
+  BEGIN
+    RETURN m2_cstr_copy(dst, s)
+  END Copy;
+
+PROCEDURE IsNil (s : CString) : BOOLEAN;
+  BEGIN
+    RETURN s = NIL
+  END IsNil;
+
+END CStr.

+ 42 - 0
runtime/syslib/shim.c

@@ -380,6 +380,48 @@ long m2_c_fill(char *buf, long cap, int ch)
     return cap - 1;
 }
 
+/* Copy a C string into a V3 CHAR-array descriptor. */
+long m2_cstr_copy(long *dst, const char *src)
+{
+    char *d = (char *)(dst + 1);
+    long cap = dst[0];
+    size_t n;
+
+    if (cap <= 0) return 0;
+    if (!src) {
+        d[0] = 0;
+        return 0;
+    }
+    n = strlen(src);
+    if (n > (size_t)(cap - 1)) n = (size_t)(cap - 1);
+    if (n > 0) memcpy(d, src, n);
+    d[n] = 0;
+    return (long)n;
+}
+
+/* Test helpers for CString returns and char ** outputs. */
+const char *m2_cstr_hello(void)
+{
+    return "hello";
+}
+
+void m2_cstr_out(const char **p)
+{
+    if (p) *p = "world";
+}
+
+long m2_cstr_fill(char *buf, long cap)
+{
+    static const char text[] = "buffer";
+    size_t n = sizeof text - 1;
+
+    if (!buf || cap <= 0) return -1;
+    if (n > (size_t)(cap - 1)) n = (size_t)(cap - 1);
+    memcpy(buf, text, n);
+    buf[n] = 0;
+    return (long)n;
+}
+
 /* UString length: the codepoint count is the descriptor's header. */
 long m2ustrlen(long *s)
 {