expand & test FFI error checking
This commit is contained in:
parent
79f72d84b8
commit
1454ca3a1a
@ -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
159
src/ffi.c
@ -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
|
||||
}
|
||||
|
||||
@ -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
143
test/cases/ffi.bqn
Normal 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
|
||||
@ •FFI⎊9 ""‿"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 100⥊1‿0
|
||||
%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 ¯32768‿32767 %% ¯32768‿32767
|
||||
%USE defs ⋄ f←@•FFI"&"‿"memcpy"‿"&{i16}"‿"*i8"‿size_t ⋄ G←{F⟨𝕩,↕0,0⟩} ⋄ G ⋈¨¯32768‿32767 %% ⋈¨¯32768‿32767
|
||||
|
||||
|
||||
|
||||
# bad array elements
|
||||
!"FFI: Structs currently cannot contain mutable references" % %USE defs ⋄ f←@•FFI"&"‿"memcpy"‿"&{&i16}"‿"*i8"‿size_t ⋄ G←{F⟨𝕩,↕0,0⟩} ⋄ G ⋈∘⋈¨¯32768‿32767
|
||||
|
||||
!"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 ⋈2⋆32
|
||||
!"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
|
||||
!"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 2⋆32
|
||||
!"FFI: u32 argument not exact" % f←@•FFI""‿"bqn_init"‿">u32" ⋄ F 1e20
|
||||
!"FFI: u64 argument value ≥ 2⋆53" % f←@•FFI""‿"bqn_init"‿">u64" ⋄ F 2⋆53
|
||||
!"FFI: i64 argument absolute value ≥ 2⋆53" % f←@•FFI""‿"bqn_init"‿">i64" ⋄ F 2⋆53
|
||||
!"FFI: i64 argument absolute value ≥ 2⋆53" % f←@•FFI""‿"bqn_init"‿">i64" ⋄ F -2⋆53
|
||||
|
||||
|
||||
|
||||
# bad overall argument separation
|
||||
!"FFI: Expected array 𝕩" % f←@•FFI""‿"bqn_init"‿"i32"‿"i32" ⋄ F @
|
||||
!"FFI: Unnecessary 𝕨 given" % f←@•FFI""‿"bqn_init"‿"i32"‿"i32" ⋄ @ F 1‿2
|
||||
!"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 ⟨1‿2⟩
|
||||
!"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⟩
|
||||
|
||||
|
||||
|
||||
# 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⟩
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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 ⥊¨ 10‿30
|
||||
f ↩ "lib.so" •FFI ⟨"","incInts","&i32",">𝕨&i16","&i8"⟩ ⋄ •Show ⟨20⟩ F ⥊¨ 10‿30
|
||||
|
||||
Section "# u64 tests"
|
||||
Section "# u64 & i64 tests"
|
||||
f ↩ "lib.so" •FFI ⟨"u64", "ident_u64",">u64:i32"⟩ ⋄ •Show F 1234‿12
|
||||
f ↩ "lib.so" •FFI ⟨"u64", "ident_u64",">u64" ⟩ ⋄ •Show F +´2⋆52‿20
|
||||
f ↩ "lib.so" •FFI ⟨"u64", "ident_u64",">u64" ⟩ ⋄ •Show F ¯1+2⋆53
|
||||
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+2⋆53
|
||||
f ↩ "lib.so" •FFI ⟨"i64", "ident_i64",">i64"⟩ ⋄ •Show F - ¯1+2⋆53
|
||||
|
||||
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, 1‿2‿3‿4, 3.25⟩
|
||||
f ↩ "lib.so" •FFI ⟨s1, "incMembers", ">"∾s1⟩ ⋄ •Show F ⟨200, 2e9, ¯30000, 1‿2‿3‿4, 3.25⟩
|
||||
f ↩ "lib.so" •FFI ⟨"&", "incMany", "&"∾s2, "u64"⟩ ⋄ •Show¨ F ⟨0‿3‿6+3⥊<0‿1+2⥊<⟨200, 2e9, ¯30000, 1‿2‿3‿4, 3.25⟩, 3⟩
|
||||
|
||||
@ -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 ⟩
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user