diff --git a/src/ffi.c b/src/ffi.c index c3744655..2ee3c15f 100644 --- a/src/ffi.c +++ b/src/ffi.c @@ -265,6 +265,50 @@ static void printFFIType(FILE* f, B x) { #if FFI==2 +#if FFI_CHECKS + static char* typeDesc(i32 typeNum) { + switch(typeNum) { + default: return "(unknown)"; + case 0: return "an array"; + case 1: return "a number"; + case 2: return "a character"; + case 3: return "a function"; + case 4: return "a 1-modifier"; + case 5: return "a 2-modifier"; + case 6: return "a namespace"; + } + } + static NOINLINE i32 nonNumber(B x) { // returns -1 if x is all numeric, otherwise •Type of the offending element + if (elNum(TI(x,elType))) return -1; + usz ia = IA(x); SGetU(x) + i32 r = -1; + for (ux i = 0; i < ia; i++) { + B c = GetU(x,i); + if (!isNum(c)) { r = typeInt(c); break; } + } + return r; + } + static NOINLINE void ffi_checkRange(B x, i32 mode, char* desc, i64 min, i64 max) { // doesn't consume; assumes non-array has already been checked for; if min==max, doesn't check range + if (IA(x)==0) return; + + char* ref = mode==1? "&" : mode==0? "*" : ":"; + i32 nonNum = nonNumber(x); + if (nonNum!=-1) thrF("FFI: Array provided for %S%S contained %S", ref, desc, typeDesc(nonNum)); + + if (min==max) return; + incG(x); x = elNum(TI(x,elType))? x : taga(cpyF64Arr(x)); + + i64 buf[2]; + if (!getRange_fns[TI(x,elType)](tyany_ptr(x), buf, IA(x))) thrF("FFI: Array provided for %S%S contained non-integer", ref, desc); + if (buf[0]max) thrF("FFI: Array provided for %S%S contained %l", ref, desc, buf[1]); + decG(x); + } +#else + static void ffi_checkRange(B x, i32 mode, char* desc, i64 min, i64 max) { } +#endif + + enum ScalarTy { sty_void, sty_a, sty_ptr, sty_u8, sty_u16, sty_u32, sty_u64, @@ -444,9 +488,13 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual ty c++; u8 t = *c++; u32 n = readUInt(&c); - if (t=='i' | t=='c') if (n!=8 & n!=16 & n!=32) { badW: thrF("Unsupported width in :%c%i", (u32)t, n); } - if (t=='u') if (n!=1) goto badW; - if (t=='f') if (n!=64) goto badW; + if (t=='i' || t=='c') { + if (n!=8 & n!=16 & n!=32) { badW: thrF("FFI: Unsupported width in :%c%i", (u32)t, n); } + } else if (t=='u') { + if (n!=1) goto badW; + } else if (t=='f') { + if (n!=64) goto badW; + } else thrM("FFI: Unexpected character after \":\""); if (isC32(ro) && n > myWidth*8) thrF("FFI: Representation wider than the value for \"%S:%c%i\"", sty_names[o2cG(ro)], (u32)t, n); // TODO figure out what to do with i32:i32 etc @@ -512,13 +560,13 @@ static usz ffiTmpAlign(usz n) { static B ffiObjs; -static B toW(u8 reT, u8 reW, B x) { +static NOINLINE B toW(u8 reT, u8 reW, B x) { switch(reW) { default: UD; - case 0: return taga(toBitArr(x)); break; - case 3: return reT=='c'? toC8Any(x) : toI8Any(x); break; - case 4: return reT=='c'? toC16Any(x) : toI16Any(x); break; - case 5: return reT=='c'? toC32Any(x) : toI32Any(x); break; - case 6: return toF64Any(x); break; + case 0: ffi_checkRange(x, 2, "u1", 0, 1); return taga(toBitArr(x)); break; + case 3: if (reT=='i') ffi_checkRange(x, 2, "i8", I8_MIN, I8_MAX); return reT=='c'? toC8Any(x) : toI8Any(x); break; + case 4: if (reT=='i') ffi_checkRange(x, 2, "i16", I16_MIN, I16_MAX); return reT=='c'? toC16Any(x) : toI16Any(x); break; + case 5: if (reT=='i') ffi_checkRange(x, 2, "i32", I32_MIN, I32_MAX); return reT=='c'? toC32Any(x) : toI32Any(x); break; + case 6: ffi_checkRange(x, 2, "f64", 0, 0); return toF64Any(x); break; } } static u8 reTyMapC[] = { [3]=t_c8arr, [4]=t_c16arr, [5]=t_c32arr }; @@ -534,49 +582,6 @@ static B makeRe(u8 reT, u8 reW/*log*/, u8* src, u32 elW/*bytes*/) { FORCE_INLINE u64 i64abs(i64 x) { return x<0?-x:x; } -#if FFI_CHECKS - static char* typeDesc(i32 typeNum) { - switch(typeNum) { - default: return "(unknown)"; - case 0: return "an array"; - case 1: return "a number"; - case 2: return "a character"; - case 3: return "a function"; - case 4: return "a 1-modifier"; - case 5: return "a 2-modifier"; - case 6: return "a namespace"; - } - } - static NOINLINE i32 nonNumber(B x) { // returns -1 if x is all numeric, otherwise •Type of the offending element - if (elNum(TI(x,elType))) return -1; - usz ia = IA(x); SGetU(x) - i32 r = -1; - for (ux i = 0; i < ia; i++) { - B c = GetU(x,i); - if (!isNum(c)) { r = typeInt(c); break; } - } - return r; - } - static NOINLINE void ffi_checkRange(B x, bool mut, char* desc, i64 min, i64 max) { // doesn't consume; assumes non-array has already been checked for; if min==max, doesn't check range - if (IA(x)==0) return; - - char ref = mut? '&' : '*'; - i32 nonNum = nonNumber(x); - if (nonNum!=-1) thrF("FFI: Array provided for %c%S contained %S", ref, desc, typeDesc(nonNum)); - - if (min==max) return; - incG(x); x = elNum(TI(x,elType))? x : taga(cpyF64Arr(x)); - - i64 buf[2]; - if (!getRange_fns[TI(x,elType)](tyany_ptr(x), buf, IA(x))) thrF("Array provided for %c%S contained non-integer", ref, desc); - if (buf[0]max) thrF("FFI: Array provided for %c%S contained %l", ref, desc, buf[1]); - decG(x); - } -#else - static void ffi_checkRange(B x, bool mut, char* desc, i64 min, i64 max) { } -#endif - #define CPY_UNSIGNED(REL, UEL, DIRECT, WIDEN, WEL) \ if (TI(x,elType)<=el_##REL) return taga(DIRECT(x)); \ usz ia = IA(x); \ @@ -605,10 +610,10 @@ static B toU16Bits(B x) { return TI(x,elType)==el_i16? x : cpyU16Bits(x); } static B toU8Bits(B x) { return TI(x,elType)==el_i8? x : cpyU8Bits(x); } // read x as the specified type (assuming a container of the respective width signed integer array); consumes x -NOINLINE B readU8Bits(B x) { usz ia=IA(x); u8* xp=tyarr_ptr(x); i16* rp; B r=m_i16arrv(&rp, ia); for (usz i=0; ia[0].reWidth; if (isC32(o2)) { // scalar:any u8 et = o2cG(o2); - u8 etw = sty_w[et]*8; + u8 mul = (sty_w[et]*8) >> reW; if (!isArr(c)) thrF("FFI: Expected array corresponding to \"%S:%c%i\"", sty_names[et], (u32)reT, 1<>reW) thrF("FFI: Bad array corresponding to \"%S:%c%i\": expected %s elements, got %s", sty_names[et], (u32)reT, 1<>reW), IA(c)); + if (IA(c) != mul) thrF("FFI: Bad array corresponding to \"%S:%c%i\": expected %s elements, got %s", sty_names[et], (u32)reT, 1<a[0].o; assert(t2->ty==cty_ptr && isC32(ore)); // we shouldn't be generating anything else - incG(c); - B cG; - bool mut = t->a[0].mutPtr; + bool mut = t2->a[0].mutPtr; + + u8 et = o2cG(ore); + u8 mul = (sty_w[et]*8) >> reW; + if (!isArr(c)) thrF("FFI: Expected array corresponding to \"%S%S:%c%i\"", mut?"&":"*", sty_names[et], (u32)reT, 1<ty==cty_ptr) { // *any / &any B e = t->a[0].o; B f = objs[(*objPos)++]; - bool mut = t->a[0].mutPtr; - if (mut) { + if (t->a[0].mutPtr) { if (isC32(e)) { switch(o2cG(e)) { default: UD; case sty_i8: case sty_i16: case sty_i32: case sty_f64: return incG(f); @@ -801,10 +809,11 @@ B buildObj(BQNFFIEnt ent, bool anyMut, B* objs, usz* objPos) { } else if (t->ty==cty_repr) { // any:any B o2 = t->a[0].o; if (isC32(o2)) return m_f64(0); // scalar:any - // *scalar:any / &scalar:any + + BQNFFIType* t2 = c(BQNFFIType,o2); // *scalar:any / &scalar:any + assert(t2->ty == cty_ptr); B f = objs[(*objPos)++]; - bool mut = t->a[0].mutPtr; - if (!mut) return m_f64(0); + if (!t2->a[0].mutPtr) return m_f64(0); return inc(f); } else thrM("FFI: Unimplemented type (buildObj)"); } @@ -1007,14 +1016,14 @@ B ffiload_c2(B t, B w, B x) { if (ws) freeCStr(ws); dec(w); - if (dl==NULL) thrF("Failed to load: %S", dlerror()); + if (dl==NULL) thrF("FFI: Failed to load library: %S", dlerror()); char* nameStr = toCStr(name); void* sym = dlsym(dl, nameStr); freeCStr(nameStr); dec(x); - if (sym==NULL) thrF("Failed to find symbol: %S", dlerror()); + if (sym==NULL) thrF("FFI: Failed to find symbol: %S", dlerror()); #if FFI==1 return m_ffiFn(foreignFnDesc, bi_N, directFn_c1, directFn_c2, sym, sym); #else diff --git a/test/cases/ffi.bqn b/test/cases/ffi.bqn index a4473ca0..80ec73a7 100644 --- a/test/cases/ffi.bqn +++ b/test/cases/ffi.bqn @@ -1,11 +1,12 @@ %DEF var V←•internal.Variation ⋄ LV←•internal.ListVariations ⋄ CLR←•internal.ClearRefs %DEF tvar %USE var ⋄ _tvar ← {F _𝕣 x: (CLR@) ⊢ {F 𝕩 V x}¨ LV 𝕩; w F _𝕣 x: (CLR@) ⊢ (LV 𝕨) {(𝕨 V w) F 𝕩 V x}⌜ LV 𝕩} -%DEF defs size_t ← "u64" +%DEF defs size_t ← "u64" ⋄ size_tw ← 64 # bad •FFI invocation # generally weird -@ •FFI⎊9 ""‿"bqn_this symbol doesn't exist" %% 9 +"file that doesn't exist" •FFI⎊'e' ""‿"abort" %% 'e' +@ •FFI⎊'e' ""‿"bqn_this symbol doesn't exist" %% 'e' !"FFI: Type must be a string" % @•FFI "hello" !"FFI: Error parsing type: Unexpected character '?'" % @•FFI""‿"bqn_init"‿"?" !"FFI: Bad array type" % @•FFI""‿"bqn_init"‿"[" @@ -50,12 +51,13 @@ !"FFI: Structs currently cannot contain mutable references" % @•FFI""‿"bqn_init"‿"{&i32}" # : -!"FFI: Garbage at end of type" % @•FFI""‿"bqn_init"‿"i32:" -!"Unsupported width in :i0" % @•FFI""‿"bqn_init"‿"i32:i" -!"Unsupported width in :i9" % @•FFI""‿"bqn_init"‿"i32:i9" -!"Unsupported width in :u16" % @•FFI""‿"bqn_init"‿"i32:u16" -!"Unsupported width in :i64" % @•FFI"i32:i64"‿"bqn_init" -!"Unsupported width in :f32" % @•FFI""‿"bqn_init"‿"i32:f32" +!"FFI: Unexpected character after "":""" % @•FFI""‿"bqn_init"‿"i32:" +!"FFI: Unexpected character after "":""" % f←@•FFI""‿"bqn_init"‿">*u64:q8" +!"FFI: Unsupported width in :i0" % @•FFI""‿"bqn_init"‿"i32:i" +!"FFI: Unsupported width in :i9" % @•FFI""‿"bqn_init"‿"i32:i9" +!"FFI: Unsupported width in :u16" % @•FFI""‿"bqn_init"‿"i32:u16" +!"FFI: Unsupported width in :i64" % @•FFI"i32:i64"‿"bqn_init" +!"FFI: Unsupported width in :f32" % @•FFI""‿"bqn_init"‿"i32:f32" !"FFI: number literal too large" % @•FFI""‿"bqn_init"‿"i32:u9999999999999999" !"FFI: Representation wider than the value for ""i32:f64""" % @•FFI""‿"bqn_init"‿"i32:f64" !"FFI: Garbage at end of type" % @•FFI""‿"bqn_init"‿"i32 hello world" @@ -77,6 +79,9 @@ %USE defs ⋄ f←@•FFI"&"‿"memcpy"‿"&i16"‿"*i8"‿size_t ⋄ G←{F⟨𝕩,↕0,0⟩} ⋄ G ¯32768‿32767 %% ¯32768‿32767 %USE defs ⋄ f←@•FFI"&"‿"memcpy"‿"&{i16}"‿"*i8"‿size_t ⋄ G←{F⟨𝕩,↕0,0⟩} ⋄ G ⋈¨¯32768‿32767 %% ⋈¨¯32768‿32767 %USE defs ⋄ f←@•FFI"&"‿"memcpy"‿"&f32"‿"*i8"‿size_t ⋄ G←{F⟨𝕩,↕0,0⟩} ⋄ G ⟨∞,¯∞,∞÷∞⟩∾10÷˜↕10 %% ⟨∞,¯∞,∞÷∞⟩∾0‿0.10000000149011612‿0.20000000298023224‿0.30000001192092896‿0.4000000059604645‿0.5‿0.6000000238418579‿0.699999988079071‿0.800000011920929‿0.8999999761581421 +%USE defs ⋄ f←@•FFI"&"‿"memcpy"‿"&i32"‿"*i16:u1"‿size_t ⋄ F ⟨⋈5, 32⥊1, 4⟩ %% ⋈¯1 +%USE defs ⋄ f←@•FFI⟨"&","memcpy","&i8","*i8",size_t∾":u1"⟩ ⋄ F ⟨¯100+↕10, ↕10, size_tw↑0‿0‿1⟩ %% (↕4)∾4↓¯100+↕10 +%USE defs ⋄ f←@•FFI"&"‿"memcpy"‿"&i16"‿"*i16:i32"‿size_t ⋄ F ⟨¯100+↕10, ⟨123+456×2⋆16, ¯2⟩, 8⟩ %% 123‿456‿¯2‿¯1∾4↓¯100+↕10 @@ -90,13 +95,6 @@ !"FFI: Array provided for &u16 contained ¯1" % f←@•FFI"&"‿"bqn_init"‿">&u16" ⋄ F ⋈¯1 !"FFI: Array provided for &u32 contained ¯1" % f←@•FFI"&"‿"bqn_init"‿">&u32" ⋄ F ⋈¯1 -!"FFI: Array provided for &i16 contained 32768" % f←@•FFI"&"‿"bqn_init"‿">&i16" ⋄ F ¯32768‿32768 -!"FFI: Array provided for &i16 contained ¯32769" % f←@•FFI"&"‿"bqn_init"‿">&i16" ⋄ F ¯32768‿¯32769 - -!"FFI: Array provided for &u8 contained an array" % f←@•FFI"&"‿"bqn_init"‿">&u8" ⋄ F ⟨↕300⟩ -!"FFI: Array provided for &i16 contained a character" % f←@•FFI"&"‿"bqn_init"‿">&i16" ⋄ F "hi" -!"FFI: Array provided for &f64 contained a namespace" % f←@•FFI"&"‿"bqn_init"‿">&f64" ⋄ F 1‿2‿{⇐} - # bad scalars !"FFI: u8 argument not exact" % f←@•FFI""‿"bqn_init"‿">u8" ⋄ F 256 !"FFI: u16 argument not exact" % f←@•FFI""‿"bqn_init"‿">u16" ⋄ F ¯1 @@ -120,11 +118,6 @@ # wrong argument internal structure -!"FFI: Expected array corresponding to ""i32:i8""" % f←@•FFI""‿"bqn_init"‿">i32:i8" ⋄ F @ -!"FFI: Bad array corresponding to ""i32:i8"": expected 4 elements, got 10" % f←@•FFI""‿"bqn_init"‿">i32:i8" ⋄ F ↕10 - -!"FFI: Expected array corresponding to a pointer" % f←@•FFI""‿"bqn_init"‿">*i32:i8" ⋄ F @ - !"FFI: Expected array corresponding to *{...}" % f←@•FFI""‿"bqn_init"‿">*{i32}" ⋄ F @ !"FFI: Expected array corresponding to a struct" % f←@•FFI""‿"bqn_init"‿">{i32}" ⋄ F @ !"FFI: Expected array corresponding to a struct" % f←@•FFI""‿"bqn_init"‿">*{i32}" ⋄ F ⟨@⟩ @@ -133,6 +126,31 @@ !"FFI: Incorrect list length corresponding to an array: expected 2, got 0" % f←@•FFI""‿"bqn_init"‿">*[2]i32" ⋄ F ⟨⟨⟩⟩ !"FFI: Incorrect list length corresponding to an array: expected 2, got 3" % f←@•FFI""‿"bqn_init"‿">*[2]i32" ⋄ F ⟨1‿2‿3⟩ +# bad : +!"FFI: Expected array corresponding to ""i32:i8""" % f←@•FFI""‿"bqn_init"‿">i32:i8" ⋄ F @ +!"FFI: Expected array corresponding to ""*i32:i8""" % f←@•FFI""‿"bqn_init"‿">*i32:i8" ⋄ F @ + +!"FFI: Bad array corresponding to ""i32:i8"": expected 4 elements, got 10" % f←@•FFI""‿"bqn_init"‿">i32:i8" ⋄ F ↕10 +!"FFI: Bad array corresponding to ""u64:u1"": expected 64 elements, got 128" % f←@•FFI""‿"bqn_init"‿">u64:u1" ⋄ F 128⥊1 + +!"FFI: Array provided for :u1 contained 63" % f←@•FFI""‿"bqn_init"‿">u64:u1" ⋄ F ↕64 +!"FFI: Bad array corresponding to ""u64:i8"": expected 8 elements, got 64" % f←@•FFI""‿"bqn_init"‿">u64:i8" ⋄ F ↕64 + +!"FFI: Expected array corresponding to ""*u64:i8""" % f←@•FFI""‿"bqn_init"‿">*u64:i8" ⋄ F @ +!"FFI: Array provided for :i8 contained 199" % f←@•FFI""‿"bqn_init"‿">*u64:i8" ⋄ F ↕200 +!"FFI: Array provided for :i8 contained 199" % f←@•FFI""‿"bqn_init"‿">&u64:i8" ⋄ F ↕200 +!"FFI: Array provided for :u1 contained 63" % f←@•FFI""‿"bqn_init"‿">&u64:u1" ⋄ F ↕64 +!"FFI: Bad array corresponding to ""*u64:i8"": expected a multiple of 8 elements, got 2" % f←@•FFI""‿"bqn_init"‿">*u64:i8" ⋄ F ↕2 +!"FFI: Bad array corresponding to ""*u32:i16"": expected a multiple of 2 elements, got 1" % f←@•FFI""‿"bqn_init"‿">*u32:i16" ⋄ F ↕1 +!"FFI: Bad array corresponding to ""&u64:u1"": expected a multiple of 64 elements, got 32" % f←@•FFI""‿"bqn_init"‿">&u64:u1" ⋄ F 32⥊0 + +!"FFI: Array provided for &i16 contained 32768" % f←@•FFI"&"‿"bqn_init"‿">&i16" ⋄ F ¯32768‿32768 +!"FFI: Array provided for &i16 contained ¯32769" % f←@•FFI"&"‿"bqn_init"‿">&i16" ⋄ F ¯32768‿¯32769 + +!"FFI: Array provided for &u8 contained an array" % f←@•FFI"&"‿"bqn_init"‿">&u8" ⋄ F ⟨↕300⟩ +!"FFI: Array provided for &i16 contained a character" % f←@•FFI"&"‿"bqn_init"‿">&i16" ⋄ F "hi" +!"FFI: Array provided for &f64 contained a namespace" % f←@•FFI"&"‿"bqn_init"‿">&f64" ⋄ F 1‿2‿{⇐} + # unimplemented stuff diff --git a/test/ffi/test.bqn b/test/ffi/test.bqn index 3debd61a..c9d89b03 100644 --- a/test/ffi/test.bqn +++ b/test/ffi/test.bqn @@ -39,16 +39,16 @@ f ↩ "lib.so" •FFI "f32"‿"sumF32Arr"‿"*f32:i32"‿"i32" ⋄ •Show F ⟨ Section "# mutate i32*" f ↩ "lib.so" •FFI ⟨"","incI32s","&u32", "i32"⟩ ⋄ •Show F ⟨1e8×20+↕10 ⋄ 10⟩ f ↩ "lib.so" •FFI ⟨"","incI32s","&i32:u1", "i32"⟩ ⋄ •Show ⊑F ⟨1‿0‿1‿0‿1‿0‿1‿0‿0‿1‿0‿1‿0‿0‿1‿1‿0‿1‿1‿0‿0‿0‿0‿1‿0‿1‿0‿1‿0‿0‿1‿0‿0‿0‿1‿0‿0‿0‿1‿0‿0‿0‿1‿1‿1‿0‿1‿1‿1‿1‿1‿1‿0‿0‿1‿1‿0‿0‿1‿0‿0‿1‿0‿0, 2⟩ -f ↩ "lib.so" •FFI ⟨"","incI32s","&i32:c8", "i32"⟩ ⋄ •Show ⊑F ⟨"helloworld", 2⟩ -f ↩ "lib.so" •FFI ⟨"","incI32s",">𝕨&i32:c8",">i32"⟩ ⋄ •Show ⊑ "helloworld" F 2 +f ↩ "lib.so" •FFI ⟨"","incI32s","&i32:c8", "i32"⟩ ⋄ •Show ⊑F ⟨"hello, world", 2⟩ +f ↩ "lib.so" •FFI ⟨"","incI32s",">𝕨&i32:c8",">i32"⟩ ⋄ •Show ⊑ "hello, world" F 2 -f ↩ "lib.so" •FFI ⟨"", "incI32s", "&i32:c8", "i32"⟩ ⋄ •Show ⊑F ⟨"helloworld", 2⟩ -f ↩ "lib.so" •FFI ⟨"", "incI32s", "&i32:c8", "𝕨i32"⟩ ⋄ •Show ⊑ ⟨2⟩ F ⟨"helloworld"⟩ -f ↩ "lib.so" •FFI ⟨"", "incI32s", "&i32:c8",">𝕨i32"⟩ ⋄ •Show ⊑ 2 F ⟨"helloworld"⟩ -f ↩ "lib.so" •FFI ⟨"", "incI32s", ">&i32:c8",">𝕨i32"⟩ ⋄ •Show ⊑ 2 F "helloworld" -f ↩ "lib.so" •FFI ⟨"", "incI32s",">𝕨&i32:c8", ">i32"⟩ ⋄ •Show ⊑ "helloworld" F 2 -f ↩ "lib.so" •FFI ⟨"", "incI32s", "&i32:c8", "i32"⟩ ⋄ •Show F ⟨"helloworld", 2⟩ -f ↩ "lib.so" •FFI ⟨"&","incI32s", "&i32:c8", "i32"⟩ ⋄ •Show F ⟨"helloworld", 2⟩ +f ↩ "lib.so" •FFI ⟨"", "incI32s", "&i32:c8", "i32"⟩ ⋄ •Show ⊑F ⟨"hello, world", 2⟩ +f ↩ "lib.so" •FFI ⟨"", "incI32s", "&i32:c8", "𝕨i32"⟩ ⋄ •Show ⊑ ⟨2⟩ F ⟨"hello, world"⟩ +f ↩ "lib.so" •FFI ⟨"", "incI32s", "&i32:c8",">𝕨i32"⟩ ⋄ •Show ⊑ 2 F ⟨"hello, world"⟩ +f ↩ "lib.so" •FFI ⟨"", "incI32s", ">&i32:c8",">𝕨i32"⟩ ⋄ •Show ⊑ 2 F "hello, world" +f ↩ "lib.so" •FFI ⟨"", "incI32s",">𝕨&i32:c8", ">i32"⟩ ⋄ •Show ⊑ "hello, world" F 2 +f ↩ "lib.so" •FFI ⟨"", "incI32s", "&i32:c8", "i32"⟩ ⋄ •Show F ⟨"hello, world", 2⟩ +f ↩ "lib.so" •FFI ⟨"&","incI32s", "&i32:c8", "i32"⟩ ⋄ •Show F ⟨"hello, world", 2⟩ Section "# mutate i32*, i16*, i8*" f ↩ "lib.so" •FFI ⟨"","incInts","&i32", "&i16","&i8"⟩ ⋄ •Show F ⥊¨ 10‿20‿30 diff --git a/test/ffi/test.expected b/test/ffi/test.expected index b336bcc5..e3a28aad 100644 --- a/test/ffi/test.expected +++ b/test/ffi/test.expected @@ -54,15 +54,15 @@ ff7fdfefefdf7bb4 ff7fdfefefdf7bb4 fefffdfff7ffbffb bffff7fffdfffeff # mutate i32* ⟨ ⟨ 2000000001 2100000001 2200000001 2300000001 2400000001 2500000001 2600000001 2700000001 2800000001 2900000001 ⟩ ⟩ ⟨ 0 1 1 0 1 0 1 0 0 1 0 1 0 0 1 1 0 1 1 0 0 0 0 1 0 1 0 1 0 0 1 0 1 0 1 0 0 0 1 0 0 0 1 1 1 0 1 1 1 1 1 1 0 0 1 1 0 0 1 0 0 1 0 0 ⟩ -"iellpworld" -"iellpworld" -"iellpworld" -"iellpworld" -"iellpworld" -"iellpworld" -"iellpworld" -⟨ "iellpworld" ⟩ -"iellpworld" +"iellp, world" +"iellp, world" +"iellp, world" +"iellp, world" +"iellp, world" +"iellp, world" +"iellp, world" +⟨ "iellp, world" ⟩ +"iellp, world" # mutate i32*, i16*, i8* 10 20 30