expand & test FFI error checking

This commit is contained in:
dzaima 2023-07-28 13:59:25 +03:00
parent 79f72d84b8
commit 1454ca3a1a
11 changed files with 283 additions and 57 deletions

View File

@ -284,8 +284,8 @@ static B args_path(B* fullpath, B w, char* name) { // consumes w, returns args,
if (!isArr(w) || RNK(w)!=1 || IA(w)>3) thrF("%U: 𝕨 must be a vector with at most 3 items, but had shape %H", name, w);
usz ia = IA(w);
SGet(w)
B path = ia>0? vfyStr(Get(w,0),name,"path" ) : inc(cdPath);
B file = ia>1? vfyStr(Get(w,1),name,"filename") : emptyCVec();
B path = ia>0? vfyStr(Get(w,0),name,"Path" ) : inc(cdPath);
B file = ia>1? vfyStr(Get(w,1),name,"Filename") : emptyCVec();
B args = ia>2? Get(w,2) : emptySVec();
*fullpath = vec_join(vec_addN(path, m_c32('/')), file);
decG(w);

159
src/ffi.c
View File

@ -4,6 +4,10 @@
#error "Expected CBQN_EXPORT if FFI is defined"
#endif
#ifndef FFI_CHECKS // option to disable extended correctness checks
#define FFI_CHECKS 1
#endif
#if CBQN_EXPORT
#if defined(_WIN32) || defined(_WIN64)
#define BQN_EXP __attribute__((__visibility__("default"))) __declspec(dllexport)
@ -11,7 +15,7 @@
#define BQN_EXP __attribute__((__visibility__("default")))
#endif
#include "../include/bqnffi.h"
#include "utils/utf.h"
#include "utils/calls.h"
#include "utils/cstr.h"
#include "nfns.h"
#include "ns.h"
@ -47,8 +51,11 @@ BQN_EXP void bqn_init() {
}
B type_c1(B t, B x);
static i32 typeInt(B x) { // doesn't consume
return o2i(C1(type, inc(x)));
}
BQN_EXP int bqn_type(BQNV v) {
return o2i(type_c1(bi_N, inc(getB(v))));
return typeInt(getB(v));
}
BQN_EXP BQNV bqn_call1(BQNV f, BQNV x) {
@ -156,6 +163,8 @@ typedef struct BoundFn {
void* w_c2;
#if FFI==2
i32 mutCount;
i32 wLen; // 0: not needed; -1: is whole array; ≥1: length
i32 xLen; // 0: length 0 array; else, ↑
#endif
} BoundFn;
NFnDesc* boundFnDesc;
@ -310,7 +319,9 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual ty
bool parseRepr=false, canRetype=false, mut=false;
u32 myWidth = 0; // used if parseRepr
switch (c0) {
default: thrF("FFI: Error parsing type: Unexpected character '%c'", c0);
default:
if (c0 == '\0') thrF("FFI: Error parsing type: Unexpected end of input");
thrF("FFI: Error parsing type: Unexpected character '%c'", c0);
case '[': {
u32 n = readUInt(&c);
if (*c++!=']') thrM("FFI: Bad array type");
@ -374,7 +385,7 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual ty
case 'a': {
ro = m_c32(sty_a);
assert(sizeof(BQNV)==8); // ffi_type_uint64 must be the same as BQNV
assert(sizeof(BQNV)==8); // if changed, change the below ffi_type_uint64 too
rt = ffi_type_uint64;
break;
}
@ -405,7 +416,7 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual ty
if (e.mutates) thrM("FFI: Structs currently cannot contain mutable references");
u32 m = *c++;
if (m=='}') break;
if (m!=',') thrM("FFI: Invalid struct member");
if (m!=',') thrM("FFI: Improper struct separator or end");
}
usz n = TSSIZE(es);
BQNFFIEnt* rp; ro = m_bqnFFIType(&rp, cty_struct, n+1);
@ -433,8 +444,8 @@ 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("Bad width in :%c%i", (u32)t, n); }
if (t=='u') if (n!=1 & n!=8 & n!=16 & n!=32) goto badW;
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 (isC32(ro) && n > myWidth*8) thrF("FFI: Representation wider than the value for \"%S:%c%i\"", sty_names[o2cG(ro)], (u32)t, n);
@ -472,7 +483,7 @@ BQNFFIEnt ffi_parseType(B arg, bool forRes) { // doesn't consume; parse argument
while (true) {
if (*xp == U'𝕩') { if (side) thrM("FFI: Multiple occurrences of argument side specified"); side = 1; }
else if (*xp == U'𝕨') { if (side) thrM("FFI: Multiple occurrences of argument side specified"); side = 2; }
else if (*xp == U'>') { if (whole) thrM("FFI: Multiple occurrences of '>'"); whole = true; }
else if (*xp == U'>') { if (whole) thrM("FFI: Multiple occurrences of '>' within one argument"); whole = true; }
else break;
xp++;
}
@ -482,7 +493,7 @@ BQNFFIEnt ffi_parseType(B arg, bool forRes) { // doesn't consume; parse argument
t = ffi_parseTypeStr(&xp, false, true);
// printI(arg); printf(": "); printFFIType(stdout, t.o); printf("\n");
if (xp!=xpN) thrM("FFI: Bad type descriptor");
if (xp!=xpN) thrM("FFI: Garbage at end of type");
t.onW = side;
// keep .mutates
t.wholeArg = whole;
@ -523,6 +534,48 @@ 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]<min) thrF("FFI: Array provided for %c%S contained %l", ref, desc, buf[0]);
if (buf[1]>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)); \
@ -559,14 +612,14 @@ void genObj(B o, B c, bool anyMut, void* ptr) {
switch(t) { default: UD; // thrF("FFI: Unimplemented scalar type \"%S\"", sty_names[t]);
case sty_a: *(BQNV*)ptr = makeX(inc(c)); break;
case sty_ptr: thrM("FFI: \"*\" unimplemented"); break;
case sty_u8: if(f!=( u8)f) thrM("FFI: u8 argument not exact" ); *( u8*)ptr = f; break;
case sty_i8: if(f!=( i8)f) thrM("FFI: i8 argument not exact" ); *( i8*)ptr = f; break;
case sty_u16: if(f!=(u16)f) thrM("FFI: u16 argument not exact"); *(u16*)ptr = f; break;
case sty_i16: if(f!=(i16)f) thrM("FFI: i16 argument not exact"); *(i16*)ptr = f; break;
case sty_u32: if(f!=(u32)f) thrM("FFI: u32 argument not exact"); *(u32*)ptr = f; break;
case sty_i32: if(f!=(i32)f) thrM("FFI: i32 argument not exact"); *(i32*)ptr = f; break;
case sty_u64: if(f!=(u64)f) thrM("FFI: u64 argument not exact"); if ( (u64)f >= (1ULL<<53)) thrM("FFI: u64 argument value ≥ 2⋆53"); *(u64*)ptr = f; break;
case sty_i64: if(f!=(i64)f) thrM("FFI: i64 argument not exact"); if ((u64)((1ULL<<53)+(u64)f) >= (2ULL<<53)) thrM("FFI: i64 argument absolute value ≥ 2⋆53"); *(i64*)ptr = f; break;
case sty_u8: { u8 i = ( u8)f; if(f!=i) thrM("FFI: u8 argument not exact" ); *( u8*)ptr = i; break; }
case sty_i8: { i8 i = ( i8)f; if(f!=i) thrM("FFI: i8 argument not exact" ); *( i8*)ptr = i; break; }
case sty_u16: { u16 i = (u16)f; if(f!=i) thrM("FFI: u16 argument not exact"); *(u16*)ptr = i; break; }
case sty_i16: { i16 i = (i16)f; if(f!=i) thrM("FFI: i16 argument not exact"); *(i16*)ptr = i; break; }
case sty_u32: { u32 i = (u32)f; if(f!=i) thrM("FFI: u32 argument not exact"); *(u32*)ptr = i; break; }
case sty_i32: { i32 i = (i32)f; if(f!=i) thrM("FFI: i32 argument not exact"); *(i32*)ptr = i; break; }
case sty_u64: { u64 i = (u64)f; if(f!=i) thrM("FFI: u64 argument not exact"); if (i>=(1ULL<<53)) thrM("FFI: u64 argument value ≥ 2⋆53"); *(u64*)ptr = i; break; }
case sty_i64: { i64 i = (i64)f; if(f!=i) thrM("FFI: i64 argument not exact"); if (i>=(1LL<<53) || i<=-(1LL<<53)) thrM("FFI: i64 argument absolute value ≥ 2⋆53"); *(i64*)ptr = i; break; }
case sty_f32: *(float* )ptr = f; break;
case sty_f64: *(double*)ptr = f; break;
}
@ -585,22 +638,22 @@ void genObj(B o, B c, bool anyMut, void* ptr) {
incG(c);
B cG;
bool mut = t->a[0].mutPtr;
switch(o2cG(e)) { default: thrF("FFI: \"*%S\" argument type NYI", sty_names[o2cG(e)]);
case sty_i8: cG = mut? taga(cpyI8Arr (c)) : toI8Any (c); break;
case sty_i16: cG = mut? taga(cpyI16Arr(c)) : toI16Any(c); break;
case sty_i32: cG = mut? taga(cpyI32Arr(c)) : toI32Any(c); break;
case sty_f64: cG = mut? taga(cpyF64Arr(c)) : toF64Any(c); break;
case sty_u8: cG = mut? cpyU8Bits (c) : toU8Bits (c); break;
case sty_u16: cG = mut? cpyU16Bits(c) : toU16Bits(c); break;
case sty_u32: cG = mut? cpyU32Bits(c) : toU32Bits(c); break;
case sty_f32: cG = cpyF32Bits(c); break; // no direct f32 type, so no direct reference option
switch(o2cG(e)) { default: thrF("FFI: \"*%S\" argument type not yet implemented", sty_names[o2cG(e)]);
case sty_i8: ffi_checkRange(c, mut, "i8", I8_MIN, I8_MAX); cG = mut? taga(cpyI8Arr (c)) : toI8Any (c); break;
case sty_i16: ffi_checkRange(c, mut, "i16", I16_MIN, I16_MAX); cG = mut? taga(cpyI16Arr(c)) : toI16Any(c); break;
case sty_i32: ffi_checkRange(c, mut, "i32", I32_MIN, I32_MAX); cG = mut? taga(cpyI32Arr(c)) : toI32Any(c); break;
case sty_f64: ffi_checkRange(c, mut, "f64", 0, 0); cG = mut? taga(cpyF64Arr(c)) : toF64Any(c); break;
case sty_u8: ffi_checkRange(c, mut, "u8", 0, U8_MAX); cG = mut? cpyU8Bits (c) : toU8Bits (c); break;
case sty_u16: ffi_checkRange(c, mut, "u16", 0, U16_MAX); cG = mut? cpyU16Bits(c) : toU16Bits(c); break;
case sty_u32: ffi_checkRange(c, mut, "u32", 0, U32_MAX); cG = mut? cpyU32Bits(c) : toU32Bits(c); break;
case sty_f32: ffi_checkRange(c, mut, "f64", 0, 0); cG = cpyF32Bits(c); break; // no direct f32 type, so no direct reference option
}
ffiObjs = vec_addN(ffiObjs, cG);
*(void**)ptr = tyany_ptr(cG);
} else { // *{...} / &{...} / *[n]any
BQNFFIType* t2 = c(BQNFFIType, e);
if (t2->ty!=cty_struct && t2->ty!=cty_starr) thrM("FFI: Unimplemented pointer element type");
if (t2->ty!=cty_struct && t2->ty!=cty_starr) thrM("FFI: Pointer element type not implemented");
usz elSz = t2->structSize;
@ -620,7 +673,7 @@ void genObj(B o, B c, bool anyMut, void* ptr) {
u8 et = o2cG(o2);
u8 etw = sty_w[et]*8;
if (!isArr(c)) thrF("FFI: Expected array corresponding to \"%S:%c%i\"", sty_names[et], (u32)reT, 1<<reW);
if (IA(c) != etw>>reW) thrM("FFI: Bad input array length");
if (IA(c) != etw>>reW) thrF("FFI: Bad array corresponding to \"%S:%c%i\": expected %s elements, got %s", sty_names[et], (u32)reT, 1<<reW, (usz)(etw>>reW), IA(c));
B cG = toW(reT, reW, incG(c));
memcpy(ptr, tyany_ptr(cG), 8); // may over-read, ¯\_(ツ)_/¯
dec(cG);
@ -674,7 +727,7 @@ B readSimple(u8 resCType, u8* ptr) {
B r;
switch(resCType) { default: UD; // thrM("FFI: Unimplemented type");
case sty_void: r = m_c32(0); break;
case sty_ptr: thrM("FFI: \"*\" unimplemented"); break;
case sty_ptr: thrM("FFI: \"*\" not yet implemented"); break;
case sty_a: r = getB(*(BQNV*)ptr); break;
case sty_i8: r = m_i32(*( i8*)ptr); break; case sty_u8: r = m_i32(*( u8*)ptr); break;
case sty_i16: r = m_i32(*(i16*)ptr); break; case sty_u16: r = m_i32(*(u16*)ptr); break;
@ -757,12 +810,21 @@ B libffiFn_c2(B t, B w, B x) {
BoundFn* bf = c(BoundFn,t);
B argObj = c(HArr,bf->obj)->a[0];
u32 flags = ptr2u64(bf->w_c1);
#define PROC_ARG(L, U, S, NG) \
Arr* L##a ONLY_GCC(=0); \
AS2B L##f ONLY_GCC(=0); \
if (bf->L##Len>0) { \
if (FFI_CHECKS) { \
if (!isArr(L)) thrM("FFI: Expected array " S); \
if (bf->L##Len>0 && IA(L)!=bf->L##Len) thrF("FFI: Wrong argument count in " S ": expected %s, got %s", bf->L##Len, IA(L)); \
} \
L##a = a(L); \
L##f = TIv(L##a,getU); \
} else { NG }
PROC_ARG(w, W, "𝕨", if (FFI_CHECKS && bf->wLen==0 && !q_N(w)) thrM("FFI: Unnecessary 𝕨 given");)
PROC_ARG(x, X, "𝕩", )
Arr* wa ONLY_GCC(=0); AS2B wf ONLY_GCC(=0);
Arr* xa ONLY_GCC(=0); AS2B xf ONLY_GCC(=0);
if (flags&1) { wa=a(w); wf=TIv(wa,getU); }
if (flags&2) { xa=a(x); xf=TIv(xa,getU); }
i32 idxs[2] = {0,0};
u8* tmpAlloc = TALLOCP(u8, c(BQNFFIType,argObj)->staticAllocTotal);
@ -775,7 +837,12 @@ B libffiFn_c2(B t, B w, B x) {
BQNFFIEnt* ents = c(BQNFFIType,argObj)->a;
for (usz i = 0; i < argn; i++) {
BQNFFIEnt e = ents[i+1];
B o = e.wholeArg? (e.onW? w : x) : (e.onW? wf : xf)(e.onW?wa:xa, idxs[e.onW]++);
B o;
if (e.wholeArg) {
o = e.onW? w : x;
} else {
o = (e.onW? wf : xf)(e.onW?wa:xa, idxs[e.onW]++);
}
genObj(e.o, o, e.extra2, tmpAlloc + e.staticOffset);
}
@ -811,6 +878,7 @@ B libffiFn_c2(B t, B w, B x) {
i32 mutArgs = bf->mutCount;
if (mutArgs) {
usz objPos = 0;
u32 flags = ptr2u64(bf->w_c1);
bool resSingle = flags&4;
if (resSingle) {
for (usz i = 0; i < argn; i++) {
@ -868,9 +936,10 @@ B ffiload_c2(B t, B w, B x) {
usz xia = IA(x);
if (xia<2) thrM("FFI: Function specification must have at least two items");
usz argn = xia-2;
if (argn >= U16_MAX) thrM("FFI: Too many arguments");
SGetU(x)
B name = GetU(x,1);
vfyStr(name, "FFI", "type");
vfyStr(name, "FFI", "Type");
u64 staticAlloc = ffiTmpAlign(argn * sizeof(void*)); // initial alloc is the argument pointer list
#define STATIC_ALLOC(O, SZ) ({ (O).staticOffset = staticAlloc; staticAlloc+= ffiTmpAlign(SZ); })
@ -899,8 +968,8 @@ B ffiload_c2(B t, B w, B x) {
#if FFI==2
BQNFFIEnt* args; B argObj = m_bqnFFIType(&args, 255, argn+1);
args[0] = tRes;
bool one [2]={0,0};
bool many[2]={0,0};
bool whole[2]={0,0};
i32 count[2]={0,0};
i32 mutCount = 0;
for (usz i = 0; i < argn; i++) {
BQNFFIEnt e = ffi_parseType(GetU(x,i+2), false);
@ -908,19 +977,21 @@ B ffiload_c2(B t, B w, B x) {
STATIC_ALLOC(e, calcStaticSize(e));
args[i+1] = e;
(e.wholeArg? one : many)[e.extra] = true;
if (e.wholeArg) whole[e.onW] = true;
count[e.onW]++;
if (e.mutates) mutCount++;
}
if (staticAlloc > U16_MAX-64) thrM("FFI: Static argument size too large");
c(BQNFFIType,argObj)->staticAllocTotal = ffiTmpAlign(staticAlloc);
if (one[0] && many[0]) thrM("FFI: Multiple arguments for 𝕩 specified, but one has '>'");
if (one[1] && many[1]) thrM("FFI: Multiple arguments for 𝕨 specified, but one has '>'");
if (count[0]>1 && whole[0]) thrM("FFI: Multiple arguments on 𝕩 specified, some with '>'");
if (count[1]>1 && whole[1]) thrM("FFI: Multiple arguments on 𝕨 specified, some with '>'");
if (count[0]==0 && count[1]>0) thrM("FFI: At least one argument should be in 𝕩");
#else
i32 mutCount = 0;
for (usz i = 0; i < argn; i++) ffi_parseType(GetU(x,i+2), false);
(void)tRes;
#endif
if (tRes.resSingle && mutCount!=1) thrF("FFI: Return was \"&\", but found %i mutated values", mutCount);
if (tRes.resSingle && mutCount!=1) thrF("FFI: Return value is specified as \"&\", but there are %i mutated values", mutCount);
char* ws = NULL;
if (w.u != m_c32(0).u) {
@ -955,9 +1026,11 @@ B ffiload_c2(B t, B w, B x) {
ffi_status s = ffi_prep_cif((ffi_cif*)cif->data, FFI_DEFAULT_ABI, argn, &args[0].t, argsRawArr);
if (s!=FFI_OK) thrM("FFI: Error preparing call interface");
// mm_free(argsRaw)
u32 flags = many[1] | many[0]<<1 | tRes.resSingle<<2;
u32 flags = tRes.resSingle<<2;
B r = m_ffiFn(foreignFnDesc, m_hvec3(argObj, tag(cif, OBJ_TAG), tag(argsRaw, OBJ_TAG)), libffiFn_c1, libffiFn_c2, TOPTR(void,flags), sym);
c(BoundFn,r)->mutCount = mutCount;
c(BoundFn,r)->wLen = whole[1]? -1 : count[1];
c(BoundFn,r)->xLen = whole[0]? -1 : count[0];
return r;
#endif
}

View File

@ -35,5 +35,5 @@ typedef struct { EqFn fn; u8 data; } EqFnObj;
#define EQFN_GET(W_ELT, X_ELT) ({ u8 eqfn_i_ = EQFN_INDEX(W_ELT, X_ELT); (EqFnObj){.fn=eqFns[eqfn_i_], .data=eqFnData[eqfn_i_]}; })
#define EQFN_CALL(FN, W, X, L) (FN).fn(W, X, L, (FN).data)
typedef bool (*RangeFn)(void* xp, i64* res, u64 len); // writes min,max in res; returns 0 and leaves res undefined if either any (floor(x)≠x or abs>2⋆53), or (x≠(i64)x)
extern RangeFn getRange_fns[el_f64+1];
typedef bool (*RangeFn)(void* xp, i64* res, u64 len); // writes min,max in res, assumes len≥1; returns 0 and leaves res undefined if either any (floor(x)≠x or abs>2⋆53), or (x≠(i64)x)
extern RangeFn getRange_fns[el_f64+1]; // limited to ≤el_f64

143
test/cases/ffi.bqn Normal file
View File

@ -0,0 +1,143 @@
%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"
# bad •FFI invocation
# generally weird
@ •FFI9 """bqn_this symbol doesn't exist" %% 9
!"FFI: Type must be a string" % @•FFI "hello"
!"FFI: Error parsing type: Unexpected character '?'" % @•FFI"""bqn_init""?"
!"FFI: Bad array type" % @•FFI"""bqn_init""["
!"FFI: Bad array type" % @•FFI"""bqn_init""[0"
!"FFI: Error parsing type: Unexpected end of input" % @•FFI"""bqn_init""[1]"
!"FFI: Error parsing type: Unexpected end of input" % @•FFI"""bqn_init""{"
!"FFI: Bad integer width" % @•FFI"""bqn_init""i"
!"FFI: Bad float width" % @•FFI"""bqn_init""f80"
!"FFI: Bad float width" % @•FFI"""bqn_init""f128"
!"FFI: Bad float width" % @•FFI"""bqn_init""f16"
!"FFI: Bad float width" % @•FFI"""bqn_init""f1"
!"FFI: Bad integer width" % @•FFI"""bqn_init""u1"
!"FFI: Bad integer width" % @•FFI"""bqn_init""i0"
!"FFI: Bad integer width" % @•FFI"""bqn_init""i10"
!"FFI: Bad integer width" % @•FFI"""bqn_init""u128"
!"FFI: number literal too large" % @•FFI"""bqn_init""u99999999999999"
!"FFI: Too many arguments" % @•FFI"""bqn_init"70000<"i32"
# >/𝕨/𝕩
!"FFI: At least one argument should be in 𝕩" % @•FFI"""bqn_init""𝕨i32""𝕨i32"
!"FFI: Multiple occurrences of '>' within one argument" % @•FFI"""bqn_init"">>i32"
!"FFI: Multiple occurrences of argument side specified" % @•FFI"""bqn_init""𝕨𝕨i32"
!"FFI: Multiple occurrences of argument side specified" % @•FFI"""bqn_init""𝕩𝕩i32"
!"FFI: Multiple occurrences of argument side specified" % @•FFI"""bqn_init""𝕨𝕩i32"
!"FFI: Multiple arguments on 𝕩 specified, some with '>'" % @•FFI"""bqn_init"">i32"">i32"
!"FFI: Multiple arguments on 𝕨 specified, some with '>'" % @•FFI"""bqn_init"">𝕨i32"">𝕨i32"
!"FFI: Multiple arguments on 𝕩 specified, some with '>'" % @•FFI"""bqn_init"">i32""i32"
!"FFI: Multiple arguments on 𝕨 specified, some with '>'" % @•FFI"""bqn_init"">𝕨i32""𝕨i32"
# arrays
!"FFI: 0-item arrays not supported" % @•FFI"""bqn_init""[0]i32"
!"FFI: number literal too large" % @•FFI"""bqn_init""[999999999999]i32"
!"FFI: Top-level array too large; limit is 65535 elements" % @•FFI"""bqn_init""[65536]i32"
!"FFI: Top-level array too large; limit is 65535 elements" % @•FFI"""bqn_init""[4000000000]i32"
!"FFI: Array too large; limit is 65534 bytes" % @•FFI"""bqn_init""*[20000]i32"
!"FFI: Cannot return array" % @•FFI"[4]i32""bqn_init"
# structs
!"FFI: Improper struct separator or end" % @•FFI"""bqn_init""{*i32*i32}"
!"FFI: Improper struct separator or end" % @•FFI"""bqn_init""{*i32"
!"FFI: Error parsing type: Unexpected end of input" % @•FFI"""bqn_init""{*i32,"
!"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: 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"
# return value
!"FFI: Function specification must have at least two items" % @•FFI "&"
!"FFI: Return value is specified as ""&"", but there are 0 mutated values" % @•FFI"&""bqn_init"">*i32"
!"FFI: Return value is specified as ""&"", but there are 2 mutated values" % @•FFI"&""bqn_init""&i32""&i32"
!"FFI: Cannot return array" % @•FFI"[4]i32""bqn_init"
!"FFI: Unimplemented result type" % @•FFI"*i32""bqn_init"
!"FFI: Unimplemented result type" % @•FFI"&i32""bqn_init"
# correct elements
%USE defs f@•FFI"&""memcpy""&u8""*i8"size_t G{F𝕩,0,0} %USE tvar !¨ G _tvar 10010
%USE defs f@•FFI"&""memcpy""&u8""*i8"size_t G{F𝕩,0,0} G 200 %% 200
%USE defs f@•FFI"&""memcpy""&u16""*i8"size_t G{F𝕩,0,0} G 65535 %% 65535
%USE defs f@•FFI"&""memcpy""&i16""*i8"size_t G{F𝕩,0,0} G ¯3276832767 %% ¯3276832767
%USE defs f@•FFI"&""memcpy""&{i16}""*i8"size_t G{F𝕩,0,0} G ¨¯3276832767 %% ¨¯3276832767
# bad array elements
!"FFI: Structs currently cannot contain mutable references" % %USE defs f@•FFI"&""memcpy""&{&i16}""*i8"size_t G{F𝕩,0,0} G ¨¯3276832767
!"FFI: Array provided for &u8 contained 299" % f@•FFI"&""bqn_init"">&u8" F 300
!"FFI: Array provided for &u8 contained ¯99" % f@•FFI"&""bqn_init"">&u8" F -100
!"FFI: Array provided for &u16 contained 65536" % f@•FFI"&""bqn_init"">&u16" F 65536
!"FFI: Array provided for &u32 contained 4294967296" % f@•FFI"&""bqn_init"">&u32" F 232
!"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 ¯3276832768
!"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 12{}
# 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
!"FFI: u32 argument not exact" % f@•FFI"""bqn_init"">u32" F ¯1
!"FFI: u64 argument not exact" % f@•FFI"""bqn_init"">u64" F ¯1
!"FFI: u32 argument not exact" % f@•FFI"""bqn_init"">u32" F 232
!"FFI: u32 argument not exact" % f@•FFI"""bqn_init"">u32" F 1e20
!"FFI: u64 argument value ≥ 2⋆53" % f@•FFI"""bqn_init"">u64" F 253
!"FFI: i64 argument absolute value ≥ 2⋆53" % f@•FFI"""bqn_init"">i64" F 253
!"FFI: i64 argument absolute value ≥ 2⋆53" % f@•FFI"""bqn_init"">i64" F -253
# bad overall argument separation
!"FFI: Expected array 𝕩" % f@•FFI"""bqn_init""i32""i32" F @
!"FFI: Unnecessary 𝕨 given" % f@•FFI"""bqn_init""i32""i32" @ F 12
!"FFI: Wrong argument count in 𝕩: expected 2, got 3" % f@•FFI"""bqn_init""i32""i32" F 2, 2, 2
!"FFI: Wrong argument count in 𝕩: expected 2, got 1" % f@•FFI"""bqn_init""i32""i32" F 2
!"FFI: Wrong argument count in 𝕨: expected 2, got 3" % f@•FFI"""bqn_init""i32""𝕨i32""𝕨i32" 2, 2, 2 F 4
# 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 @
!"FFI: Incorrect list length corresponding to a struct: expected 1, got 0" % f@•FFI"""bqn_init"">*{i32}" F
!"FFI: Incorrect list length corresponding to a struct: expected 1, got 2" % f@•FFI"""bqn_init"">*{i32}" F 12
!"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 123
# unimplemented stuff
!"FFI: ""*"" unimplemented" % f@•FFI"""bqn_init"">*" F 1
!"FFI: ""*i64"" argument type not yet implemented" % f@•FFI"""bqn_init"">&i64" F 10
!"FFI: ""*i64"" argument type not yet implemented" % f@•FFI"""bqn_init"">*i64" F 10
!"FFI: ""*u64"" argument type not yet implemented" % f@•FFI"""bqn_init"">&u64" F 10
!"FFI: Pointer element type not implemented" % f@•FFI"""bqn_init"">**u64" F 2

View File

@ -1,5 +1,5 @@
%DEF var V•internal.Variation LV•internal.ListVariations
%DEF tvar2 %USE var _tvar2_ {F _𝕣_ 𝕘 x: {F 𝕩 V x}¨ 𝕘 LV 𝕩; w F _𝕣_ 𝕘 x: (𝕘 LV 𝕨) {(𝕨 V w) F 𝕩 V x} 𝕘 LV 𝕩}
%DEF var V•internal.Variation LV•internal.ListVariations CLR•internal.ClearRefs
%DEF tvar2 %USE var _tvar2_ {F _𝕣_ 𝕘 x: (CLR@) {F 𝕩 V x}¨ 𝕘 LV 𝕩; w F _𝕣_ 𝕘 x: (CLR@) (𝕘 LV 𝕨) {(𝕨 V w) F 𝕩 V x} 𝕘 LV 𝕩}
%DEF fastone (
_fastone { F _𝕣 𝕩:
t 0

View File

@ -1,5 +1,5 @@
%DEF var V•internal.Variation LV•internal.ListVariations
%DEF tvar %USE var _tvar {F _𝕣 x: {F 𝕩 V x}¨ LV 𝕩; w F _𝕣 x: (•internal.ClearRefs@) (LV 𝕨) {(𝕨 V w) F 𝕩 V x} LV 𝕩}
%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 k _k {𝔽•internal.Keep}
# batch checks

View File

@ -1,7 +1,7 @@
# not tested here: •Out •Show •Exit •term •GetLine •Cmp •CurrentError •FFI •internal.HeapDump •file.MapBytes
%DEF var V•internal.Variation LV•internal.ListVariations
%DEF tvar %USE var _tvar {F _𝕣 x: {F 𝕩 V x}¨ LV 𝕩; w F _𝕣 x: (•internal.ClearRefs@) (LV 𝕨) {(𝕨 V w) F 𝕩 V x} LV 𝕩}
%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 𝕩}
# standard system values

View File

@ -180,6 +180,7 @@ void incInts(int32_t* a, int16_t* b, int8_t* c) {
}
uint64_t ident_u64(uint64_t x) { return x; }
int64_t ident_i64(int64_t x) { return x; }
double ident_f64(double a) { return a; }
void* pick_ptr(void** arr, int idx) {

View File

@ -55,12 +55,17 @@ f ↩ "lib.so" •FFI ⟨"","incInts","&i32", "&i16","&i8"⟩ ⋄ •Show F ⥊
f "lib.so" •FFI "","incInts","&i32", "𝕨&i16","&i8" •Show 20 F ¨ 1030
f "lib.so" •FFI "","incInts","&i32",">𝕨&i16","&i8" •Show 20 F ¨ 1030
Section "# u64 tests"
Section "# u64 & i64 tests"
f "lib.so" •FFI "u64", "ident_u64",">u64:i32" •Show F 123412
f "lib.so" •FFI "u64", "ident_u64",">u64" •Show F +´25220
f "lib.so" •FFI "u64", "ident_u64",">u64" •Show F ¯1+253
f "lib.so" •FFI "u64:i32","ident_u64",">u64" •Show F 123456789123456
f "lib.so" •FFI "u64:u1", "ident_u64",">u64:c8" •Show F "hellowor"
f "lib.so" •FFI "i64:u1", "ident_i64",">i64:c8" •Show F "hellowor"
f "lib.so" •FFI "i64", "ident_i64",">i64" •Show F ¯1+253
f "lib.so" •FFI "i64", "ident_i64",">i64" •Show F - ¯1+253
Section "# malloc test"
f "lib.so" •FFI "*:i32""malloc"">u64" •Show (•internal.Type) malloc F 123
f "lib.so" •FFI """free"">*:i32" F malloc
@ -73,7 +78,7 @@ f ↩ "lib.so" •FFI ⟨"u64:i8","pick_u64",">*u64:i8",">𝕨i32"⟩ ⋄ •Sho
f "lib.so" •FFI "u64", "pick_u64",">*u64:i8",">𝕨i32" •Show 1 F "000000001234560011122100"-'0'
Section "# structs"
s1 "{u8,i32,i16,u64:u16,f64}" s2 "{"s1","s1"}"
s1 "{u8,i32,i16,u64:i16,f64}" s2 "{"s1","s1"}"
f "lib.so" •FFI "i16", "thirdMember", ">"s1 •Show F 200, 2e9, ¯30000, 1234, 3.25
f "lib.so" •FFI s1, "incMembers", ">"s1 •Show F 200, 2e9, ¯30000, 1234, 3.25
f "lib.so" •FFI "&", "incMany", "&"s2, "u64" •Show¨ F 036+3<01+2<200, 2e9, ¯30000, 1234, 3.25, 3

View File

@ -72,11 +72,15 @@ ff7fdfefefdf7bb4 ff7fdfefefdf7bb4 fefffdfff7ffbffb bffff7fffdfffeff
10 20 30
⟨ ⟨ 11 ⟩ ⟨ 21 ⟩ ⟨ 31 ⟩ ⟩
# u64 tests
# u64 & i64 tests
51539608786
4.503599628419072e15
9.007199254740991e15
⟨ ¯2045800064 28744 ⟩
⟨ 0 0 0 1 0 1 1 0 1 0 1 0 0 1 1 0 0 0 1 1 0 1 1 0 0 0 1 1 0 1 1 0 1 1 1 1 0 1 1 0 1 1 1 0 1 1 1 0 1 1 1 1 0 1 1 0 0 1 0 0 1 1 1 0 ⟩
⟨ 0 0 0 1 0 1 1 0 1 0 1 0 0 1 1 0 0 0 1 1 0 1 1 0 0 0 1 1 0 1 1 0 1 1 1 1 0 1 1 0 1 1 1 0 1 1 1 0 1 1 1 1 0 1 1 0 0 1 0 0 1 1 1 0 ⟩
9.007199254740991e15
¯9.007199254740991e15
# malloc test
⟨ "i32arr" 2 ⟩

View File

@ -21,7 +21,7 @@ Trim ← {((`⌾⌽∧∨`)𝕩≠' ')/𝕩}
•Out " fuzz perf"
•Out " For specific configurations:"
•Out " test_range // -DTEST_RANGE"
•Out "Lint all: test/run.bqn lint prims cells syntax system fills hash patterns under fuzz perf test_range"
•Out "Lint all: test/run.bqn lint prims cells syntax system fills hash patterns under fuzz perf ffi test_range"
•Exit 0
} (0=•args) ´ "help""h""?"'-'/¨•args