initial pointer objects

This commit is contained in:
dzaima 2024-02-04 23:18:54 +02:00
parent 297302cad0
commit 7edcccdf82
4 changed files with 417 additions and 126 deletions

341
src/ffi.c
View File

@ -167,8 +167,8 @@ typedef struct BoundFn {
i32 xLen; // 0: length 0 array; else, ↑ i32 xLen; // 0: length 0 array; else, ↑
#endif #endif
} BoundFn; } BoundFn;
GLOBAL NFnDesc* boundFnDesc; STATIC_GLOBAL NFnDesc* boundFnDesc;
GLOBAL NFnDesc* foreignFnDesc; STATIC_GLOBAL NFnDesc* foreignFnDesc;
B boundFn_c1(B t, B x) { BoundFn* c = c(BoundFn,t); return getB(((bqn_boundFn1)c->w_c1)(makeX(inc(c->obj)), makeX(x))); } B boundFn_c1(B t, B x) { BoundFn* c = c(BoundFn,t); return getB(((bqn_boundFn1)c->w_c1)(makeX(inc(c->obj)), makeX(x))); }
B boundFn_c2(B t, B w, B x) { BoundFn* c = c(BoundFn,t); return getB(((bqn_boundFn2)c->w_c2)(makeX(inc(c->obj)), makeX(w), makeX(x))); } B boundFn_c2(B t, B w, B x) { BoundFn* c = c(BoundFn,t); return getB(((bqn_boundFn2)c->w_c2)(makeX(inc(c->obj)), makeX(w), makeX(x))); }
@ -233,21 +233,30 @@ DEF_FREE(ffiFn) { dec(((BoundFn*)x)->obj); }
// ..continuing under "#if FFI" // ..continuing under "#if FFI"
typedef struct BQNFFIEnt { typedef struct BQNFFIEnt {
union {
struct { // regular BQNFFIEnt usage
union { union {
B o; // usual case B o; // usual case
TAlloc* structData; // pointer stored in last element of cty_struct TAlloc* structData; // pointer stored in last element of cty_struct
}; };
#if FFI==2 #if FFI==2
ffi_type t; ffi_type t;
#endif #endif
// generic ffi_parseType ffi_parseTypeStr cty_ptr cty_repr // generic ffi_parseType ffi_parseTypeStr cty_ptr cty_repr
union { u8 extra; u8 onW; u8 canRetype; u8 mutPtr; u8 reType; }; union { u8 extra; u8 onW; u8 canRetype; u8 mutPtr; u8 reType; };
union { u8 extra2; u8 mutates; /*mutates*/ u8 reWidth; }; union { u8 extra2; u8 mutates; /*mutates*/ u8 reWidth; };
union { union {
struct { u8 wholeArg; u8 resSingle; }; // ffi_parseType struct { u8 wholeArg; u8 resSingle; }; // ffi_parseType
u16 offset; // cty_struct u16 offset; // cty_struct, cty_starr
}; };
u16 staticOffset; // only at the top level; offset into allocation u16 staticOffset; // only at the top level; offset into allocation
};
struct { // second element of a pointer holder
void* ptrh_ptr; // actual pointer
ux ptrh_stride; // stride size
};
};
} BQNFFIEnt; } BQNFFIEnt;
typedef struct BQNFFIType { typedef struct BQNFFIType {
struct Value; struct Value;
@ -328,14 +337,15 @@ static char* const sty_names[] = {
[sty_f32]="f32", [sty_f64]="f64" [sty_f32]="f32", [sty_f64]="f64"
}; };
enum CompoundTy { enum CompoundTy {
cty_ptr, // *... / &... cty_ptr, // *... / &...; .a = {{.mutPtr = startsWith("&"), .o=eltype}}
cty_repr, // something:type cty_tlarr, // top-level array; {{.o=eltype}}
cty_struct, // {...} cty_struct, // {...}; .a = {{.o=el0}, {.o=el1}, ..., {.o=elLast}, {.structData = ffi_type**}}
cty_starr, // struct-based array cty_starr, // struct-based array; same as cty_struct
cty_tlarr, // top-level array cty_repr, // something:type; {{.o=el, .t=rt, .reType=one of "icuf", .reWidth=bit width of retype}}
cty_ptrh, // data holder for pointer objects; {{.o=eltype}, {.ptrh_ptr, .ptrh_stride}}
}; };
static B m_bqnFFIType(BQNFFIEnt** rp, u8 ty, usz ia) { static NOINLINE B m_bqnFFIType(BQNFFIEnt** rp, u8 ty, usz ia) {
BQNFFIType* r = mm_alloc(fsizeof(BQNFFIType, a, BQNFFIEnt, ia), t_ffiType); BQNFFIType* r = mm_alloc(fsizeof(BQNFFIType, a, BQNFFIEnt, ia), t_ffiType);
r->ty = ty; r->ty = ty;
r->ia = ia; r->ia = ia;
@ -348,7 +358,7 @@ static u32 readUInt(u32** p) {
u32* c = *p; u32* c = *p;
u32 r = 0; u32 r = 0;
while (*c>='0' & *c<='9') { while (*c>='0' & *c<='9') {
if (r >= U32_MAX/10 - 10) thrM("FFI: number literal too large"); if (r >= U32_MAX/10 - 10) thrM("Type parser: number literal too large");
r = r*10 + *c-'0'; r = r*10 + *c-'0';
c++; c++;
} }
@ -364,19 +374,19 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual ty
u32 myWidth = 0; // used if parseRepr u32 myWidth = 0; // used if parseRepr
switch (c0) { switch (c0) {
default: default:
if (c0 == '\0') thrF("FFI: Error parsing type: Unexpected end of input"); if (c0 == '\0') thrF("Type parser: Unexpected end of input");
thrF("FFI: Error parsing type: Unexpected character '%c'", c0); thrF("Type parser: Unexpected character '%c'", c0);
case '[': { case '[': {
u32 n = readUInt(&c); u32 n = readUInt(&c);
if (*c++!=']') thrM("FFI: Bad array type"); if (*c++!=']') thrM("Type parser: Bad array type");
if (n==0) thrM("FFI: 0-item arrays not supported"); if (n==0) thrM("Type parser: 0-item arrays not supported");
BQNFFIEnt e = ffi_parseTypeStr(&c, top, false); BQNFFIEnt e = ffi_parseTypeStr(&c, top, false);
if (top) { // largely copy-pasted from `case '*': case '&':` if (top) { // largely copy-pasted from `case '*': case '&':`
myWidth = sizeof(void*); myWidth = sizeof(void*);
BQNFFIEnt* rp; ro = m_bqnFFIType(&rp, cty_tlarr, 1); BQNFFIEnt* rp; ro = m_bqnFFIType(&rp, cty_tlarr, 1);
rp[0] = e; rp[0] = e;
if (n>U16_MAX) thrM("FFI: Top-level array too large; limit is 65535 elements"); if (n>U16_MAX) thrM("Type parser: Top-level array too large; limit is 65535 elements");
c(BQNFFIType, ro)->arrCount = n; c(BQNFFIType, ro)->arrCount = n;
mut|= rp[0].mutates; mut|= rp[0].mutates;
parseRepr = rp[0].canRetype; parseRepr = rp[0].canRetype;
@ -398,8 +408,8 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual ty
rt.type = FFI_TYPE_STRUCT; rt.type = FFI_TYPE_STRUCT;
TALLOC(size_t, offsets, n); TALLOC(size_t, offsets, n);
if (ffi_get_struct_offsets(FFI_DEFAULT_ABI, &rt, offsets) != FFI_OK) thrM("FFI: Failed getting array offsets"); if (ffi_get_struct_offsets(FFI_DEFAULT_ABI, &rt, offsets) != FFI_OK) thrM("Type parser: Failed getting array offsets");
if (rt.size>=U16_MAX) thrM("FFI: Array too large; limit is 65534 bytes"); if (rt.size>=U16_MAX) thrM("Type parser: Array too large; limit is 65534 bytes");
PLAINLOOP for (usz i = 0; i < n; i++) rp[i].offset = offsets[i]; PLAINLOOP for (usz i = 0; i < n; i++) rp[i].offset = offsets[i];
c(BQNFFIType, ro)->structSize = rt.size; c(BQNFFIType, ro)->structSize = rt.size;
TFREE(offsets); TFREE(offsets);
@ -412,7 +422,7 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual ty
if (c0=='f') { if (c0=='f') {
if (n==32) rt = ffi_type_float; if (n==32) rt = ffi_type_float;
else if (n==64) rt = ffi_type_double; else if (n==64) rt = ffi_type_double;
else thrM("FFI: Bad float width"); else thrM("Type parser: Bad float width");
ro = m_c32(n==32? sty_f32 : sty_f64); ro = m_c32(n==32? sty_f32 : sty_f64);
} else { } else {
u32 scty; u32 scty;
@ -420,7 +430,7 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual ty
else if (n==16) { scty = c0=='i'? sty_i16 : sty_u16; rt = c0=='i'? ffi_type_sint16 : ffi_type_uint16; } else if (n==16) { scty = c0=='i'? sty_i16 : sty_u16; rt = c0=='i'? ffi_type_sint16 : ffi_type_uint16; }
else if (n==32) { scty = c0=='i'? sty_i32 : sty_u32; rt = c0=='i'? ffi_type_sint32 : ffi_type_uint32; } else if (n==32) { scty = c0=='i'? sty_i32 : sty_u32; rt = c0=='i'? ffi_type_sint32 : ffi_type_uint32; }
else if (n==64) { scty = c0=='i'? sty_i64 : sty_u64; rt = c0=='i'? ffi_type_sint64 : ffi_type_uint64; } else if (n==64) { scty = c0=='i'? sty_i64 : sty_u64; rt = c0=='i'? ffi_type_sint64 : ffi_type_uint64; }
else thrM("FFI: Bad integer width"); else thrM("Type parser: Bad integer width");
ro = m_c32(scty); ro = m_c32(scty);
} }
parseRepr = !inPtr; myWidth = sty_w[o2cG(ro)]; parseRepr = !inPtr; myWidth = sty_w[o2cG(ro)];
@ -437,7 +447,7 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual ty
case '*': case '&': { case '*': case '&': {
myWidth = sizeof(void*); myWidth = sizeof(void*);
if (c0=='*' && (0==*c || ':'==*c)) { if (c0=='*' && (0==*c || ':'==*c || '}'==*c || ','==*c)) {
ro = m_c32(sty_ptr); ro = m_c32(sty_ptr);
parseRepr = !inPtr; parseRepr = !inPtr;
canRetype = inPtr; canRetype = inPtr;
@ -458,10 +468,10 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual ty
TSALLOC(BQNFFIEnt, es, 4); TSALLOC(BQNFFIEnt, es, 4);
while (true) { while (true) {
BQNFFIEnt e = TSADD(es, ffi_parseTypeStr(&c, false, false)); BQNFFIEnt e = TSADD(es, ffi_parseTypeStr(&c, false, false));
if (e.mutates) thrM("FFI: Structs currently cannot contain mutable references"); if (e.mutates) thrM("Type parser: Structs currently cannot contain mutable references");
u32 m = *c++; u32 m = *c++;
if (m=='}') break; if (m=='}') break;
if (m!=',') thrM("FFI: Improper struct separator or end"); if (m!=',') thrM("Type parser: Improper struct separator or end");
} }
usz n = TSSIZE(es); usz n = TSSIZE(es);
BQNFFIEnt* rp; ro = m_bqnFFIType(&rp, cty_struct, n+1); BQNFFIEnt* rp; ro = m_bqnFFIType(&rp, cty_struct, n+1);
@ -478,8 +488,8 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual ty
rt.type = FFI_TYPE_STRUCT; rt.type = FFI_TYPE_STRUCT;
TALLOC(size_t, offsets, n); TALLOC(size_t, offsets, n);
if (ffi_get_struct_offsets(FFI_DEFAULT_ABI, &rt, offsets) != FFI_OK) thrM("FFI: Failed getting struct offsets"); if (ffi_get_struct_offsets(FFI_DEFAULT_ABI, &rt, offsets) != FFI_OK) thrM("Type parser: Failed getting struct offsets");
if (rt.size>=U16_MAX) thrM("FFI: Struct too large; limit is 65534 bytes"); if (rt.size>=U16_MAX) thrM("Type parser: Struct too large; limit is 65534 bytes");
PLAINLOOP for (usz i = 0; i < n; i++) rp[i].offset = offsets[i]; PLAINLOOP for (usz i = 0; i < n; i++) rp[i].offset = offsets[i];
c(BQNFFIType, ro)->structSize = rt.size; c(BQNFFIType, ro)->structSize = rt.size;
TFREE(offsets); TFREE(offsets);
@ -491,14 +501,14 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual ty
u8 t = *c++; u8 t = *c++;
u32 n = readUInt(&c); u32 n = readUInt(&c);
if (t=='i' || t=='c') { if (t=='i' || t=='c') {
if (n!=8 & n!=16 & n!=32) { badW: thrF("FFI: Unsupported width in :%c%i", (u32)t, n); } if (n!=8 & n!=16 & n!=32) { badW: thrF("Type parser: Unsupported width in :%c%i", (u32)t, n); }
} else if (t=='u') { } else if (t=='u') {
if (n!=1) goto badW; if (n!=1) goto badW;
} else if (t=='f') { } else if (t=='f') {
if (n!=64) goto badW; if (n!=64) goto badW;
} else thrM("FFI: Unexpected character after \":\""); } else thrM("Type parser: 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); if (isC32(ro) && n > myWidth*8) thrF("Type parser: 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 // TODO figure out what to do with i32:i32 etc
B roP = ro; B roP = ro;
@ -508,25 +518,32 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual ty
*src = c; *src = c;
return (BQNFFIEnt){.t=rt, .o=ro, .canRetype=canRetype, .extra2=mut}; return (BQNFFIEnt){.t=rt, .o=ro, .canRetype=canRetype, .extra2=mut};
} }
static NOINLINE u32* parseType_pre(B arg, ux ia) { // doesn't consume; arg can be freed immediately after
TALLOC(u32, xp, ia+1);
xp[ia] = 0;
COPY_TO(xp, el_c32, 0, arg, 0, ia);
return xp;
}
static NOINLINE void parseType_post(u32* xp0, u32* xp, usz ia) {
if (xp0+ia != xp) thrM("Type parser: Garbage at end of type");
TFREE(xp0);
}
BQNFFIEnt ffi_parseType(B arg, bool forRes) { // doesn't consume; parse argument side & other global decorators BQNFFIEnt ffi_parseType(B arg, bool forRes) { // doesn't consume; parse argument side & other global decorators
vfyStr(arg, "FFI", "type"); vfyStr(arg, "FFI", "type");
usz ia = IA(arg); usz ia = IA(arg);
if (ia==0) { if (ia==0) {
if (!forRes) thrM("FFI: Argument type empty"); if (!forRes) thrM("Type parser: Type was empty");
return (BQNFFIEnt){.t = ffi_type_void, .o=m_c32(sty_void), .resSingle=false}; return (BQNFFIEnt){.t = ffi_type_void, .o=m_c32(sty_void), .resSingle=false};
} }
arg = chr_squeezeChk(incG(arg)); arg = chr_squeezeChk(incG(arg));
MAKE_MUT_INIT(tmp, ia+1, el_c32); MUTG_INIT(tmp); u32* xp0 = parseType_pre(arg, ia);
mut_copyG(tmp, 0, arg, 0, ia); u32* xp = xp0;
mut_setG(tmp, ia, m_c32(0));
u32* xp = tmp->a;
u32* xpN = xp + ia;
BQNFFIEnt t; BQNFFIEnt t;
if (xp[0]=='&' && xp[1]=='\0') { if (xp[0]=='&' && xp[1]=='\0') {
t = (BQNFFIEnt){.t = ffi_type_void, .o=m_c32(sty_void), .resSingle=true}; t = (BQNFFIEnt){.t = ffi_type_void, .o=m_c32(sty_void), .resSingle=true};
xp+= 1;
} else { } else {
u8 side = 0; u8 side = 0;
bool whole = false; bool whole = false;
@ -543,13 +560,14 @@ BQNFFIEnt ffi_parseType(B arg, bool forRes) { // doesn't consume; parse argument
t = ffi_parseTypeStr(&xp, false, true); t = ffi_parseTypeStr(&xp, false, true);
// printI(arg); printf(": "); printFFIType(stdout, t.o); printf("\n"); // printI(arg); printf(": "); printFFIType(stdout, t.o); printf("\n");
if (xp!=xpN) thrM("FFI: Garbage at end of type");
t.onW = side; t.onW = side;
// keep .mutates // keep .mutates
t.wholeArg = whole; t.wholeArg = whole;
t.resSingle = false; t.resSingle = false;
} }
mut_pfree(tmp, 0); parseType_post(xp0, xp, ia);
decG(arg); decG(arg);
return t; return t;
} }
@ -614,16 +632,17 @@ NOINLINE B readU8Bits(B x) { usz ia=IA(x); u8* xp=tyarr_ptr(x); i16* rp; B r=m
NOINLINE B readU16Bits(B x) { usz ia=IA(x); u16* xp=tyarr_ptr(x); i32* rp; B r=m_i32arrv(&rp, ia); for (usz i=0; i<ia; i++) rp[i]=xp[i]; return num_squeeze(r); } NOINLINE B readU16Bits(B x) { usz ia=IA(x); u16* xp=tyarr_ptr(x); i32* rp; B r=m_i32arrv(&rp, ia); for (usz i=0; i<ia; i++) rp[i]=xp[i]; return num_squeeze(r); }
NOINLINE B readU32Bits(B x) { usz ia=IA(x); u32* xp=tyarr_ptr(x); f64* rp; B r=m_f64arrv(&rp, ia); for (usz i=0; i<ia; i++) rp[i]=xp[i]; return num_squeeze(r); } NOINLINE B readU32Bits(B x) { usz ia=IA(x); u32* xp=tyarr_ptr(x); f64* rp; B r=m_f64arrv(&rp, ia); for (usz i=0; i<ia; i++) rp[i]=xp[i]; return num_squeeze(r); }
NOINLINE B readF32Bits(B x) { usz ia=IA(x); f32* xp=tyarr_ptr(x); f64* rp; B r=m_f64arrv(&rp, ia); for (usz i=0; i<ia; i++) rp[i]=xp[i]; return r; } NOINLINE B readF32Bits(B x) { usz ia=IA(x); f32* xp=tyarr_ptr(x); f64* rp; B r=m_f64arrv(&rp, ia); for (usz i=0; i<ia; i++) rp[i]=xp[i]; return r; }
static void* ptrobj_checkget(B x); // doesn't consume
STATIC_GLOBAL B ffiObjsGlobal; STATIC_GLOBAL B ffiObjsGlobal;
void genObj(B o, B c, bool anyMut, void* ptr) { void genObj(B o, B c, bool anyMut, void* ptr) { // doesn't consume
// printFFIType(stdout,o); printf(" = "); printI(c); printf("\n"); // printFFIType(stdout,o); printf(" = "); printI(c); printf("\n");
if (isC32(o)) { // scalar if (isC32(o)) { // scalar
u32 t = o2cG(o); u32 t = o2cG(o);
f64 f = c.f; f64 f = c.f;
switch(t) { default: UD; // thrF("FFI: Unimplemented scalar type \"%S\"", sty_names[t]); switch(t) { default: UD; // thrF("FFI: Unimplemented scalar type \"%S\"", sty_names[t]);
case sty_a: *(BQNV*)ptr = makeX(inc(c)); break; case sty_a: *(BQNV*)ptr = makeX(inc(c)); break;
case sty_ptr: thrM("FFI: \"*\" unimplemented"); break; case sty_ptr: *(void**)ptr = ptrobj_checkget(c); break;
case sty_u8: { if(!q_fu8 (f)) thrM("FFI: improper value for u8" ); *( u8*)ptr = ( u8)f; break; } case sty_u8: { if(!q_fu8 (f)) thrM("FFI: improper value for u8" ); *( u8*)ptr = ( u8)f; break; }
case sty_i8: { if(!q_fi8 (f)) thrM("FFI: improper value for i8" ); *( i8*)ptr = ( i8)f; break; } case sty_i8: { if(!q_fi8 (f)) thrM("FFI: improper value for i8" ); *( i8*)ptr = ( i8)f; break; }
case sty_u16: { if(!q_fu16(f)) thrM("FFI: improper value for u16"); *(u16*)ptr = (u16)f; break; } case sty_u16: { if(!q_fu16(f)) thrM("FFI: improper value for u16"); *(u16*)ptr = (u16)f; break; }
@ -727,23 +746,24 @@ void genObj(B o, B c, bool anyMut, void* ptr) {
} }
} }
B readAny(BQNFFIEnt e, u8* ptr); B readAny(B o, u8* ptr);
B readStruct(BQNFFIType* t, u8* ptr) { B readStruct(BQNFFIType* t, u8* ptr) {
usz ia = t->ia-1; usz ia = t->ia-1;
M_HARR(r, ia); M_HARR(r, ia);
for (usz i = 0; i < ia; i++) { for (usz i = 0; i < ia; i++) {
void* c = ptr + t->a[i].offset; void* c = ptr + t->a[i].offset;
HARR_ADD(r, i, readAny(t->a[i], c)); HARR_ADD(r, i, readAny(t->a[i].o, c));
} }
return HARR_FV(r); return HARR_FV(r);
} }
B m_ptrobj_s(void* ptr, B o); // consumes o, sets stride to size of o
B m_ptrobj(void* ptr, B o, ux stride); // consumes o
B readSimple(u8 resCType, u8* ptr) { B readSimple(u8 resCType, u8* ptr) {
B r; B r;
switch(resCType) { default: UD; // thrM("FFI: Unimplemented type"); switch(resCType) { default: UD; // thrM("FFI: Unimplemented type");
case sty_void: r = m_c32(0); break; case sty_void: r = m_c32(0); break;
case sty_ptr: thrM("FFI: \"*\" not yet implemented"); break; case sty_ptr: r = m_ptrobj_s(*(void**)ptr, m_c32(sty_void)); break;
case sty_a: r = getB(*(BQNV*)ptr); 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_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; case sty_i16: r = m_i32(*(i16*)ptr); break; case sty_u16: r = m_i32(*(u16*)ptr); break;
@ -764,18 +784,19 @@ B readRe(BQNFFIEnt e, u8* ptr) {
return makeRe(reT, reW, ptr, etw); return makeRe(reT, reW, ptr, etw);
} }
B readAny(BQNFFIEnt e, u8* ptr) { B readAny(B o, u8* ptr) { // doesn't consume
if (isC32(e.o)) { if (isC32(o)) {
return readSimple(o2cG(e.o), ptr); return readSimple(o2cG(o), ptr);
} else { } else {
BQNFFIType* t = c(BQNFFIType, e.o); BQNFFIType* t = c(BQNFFIType, o);
if (t->ty == cty_repr) { // cty_repr, scalar:x if (t->ty == cty_repr) { // cty_repr, scalar:x
return readRe(t->a[0], ptr); return readRe(t->a[0], ptr);
} else if (t->ty==cty_struct || t->ty==cty_starr) { // {...} } else if (t->ty==cty_struct || t->ty==cty_starr) { // {...}, [n]...
return readStruct(c(BQNFFIType, e.o), ptr); return readStruct(c(BQNFFIType, o), ptr);
} }
} }
thrM("FFI: Unimplemented struct field type for reading");
thrM("FFI: Unimplemented in-memory type for reading");
} }
B buildObj(BQNFFIEnt ent, bool anyMut, B* objs, usz* objPos) { B buildObj(BQNFFIEnt ent, bool anyMut, B* objs, usz* objPos) {
@ -894,9 +915,11 @@ B libffiFn_c2(B t, B w, B x) {
BQNFFIType* t = c(BQNFFIType, ents[0].o); BQNFFIType* t = c(BQNFFIType, ents[0].o);
if (t->ty == cty_repr) { // cty_repr, scalar:x if (t->ty == cty_repr) { // cty_repr, scalar:x
r = readRe(t->a[0], res); r = readRe(t->a[0], res);
} else { // {...} } else if (t->ty == cty_struct) { // {...}
r = readStruct(c(BQNFFIType, ents[0].o), res); r = readStruct(c(BQNFFIType, ents[0].o), res);
} } else if (t->ty == cty_ptr) { // *...
r = m_ptrobj_s(*(void**)res, inc(t->a[0].o));
} else UD;
} }
TFREE(tmpAlloc); TFREE(tmpAlloc);
@ -939,23 +962,25 @@ BQNFFIEnt ffi_parseType(B arg, bool forRes) {
static u64 atomSize(B chr) { static u64 calcAtomSize(B chr) {
return o2cG(chr)==sty_a? sizeof(BQNV) : sizeof(ffi_arg)>8? sizeof(ffi_arg) : 8; return o2cG(chr)==sty_a? sizeof(BQNV) : sizeof(ffi_arg)>8? sizeof(ffi_arg) : 8;
} }
static u64 calcStaticSize(BQNFFIEnt e) { static NOINLINE u64 calcMemSizeComplex(B o) {
if (isC32(e.o)) { // scalar BQNFFIType* t = c(BQNFFIType, o);
return atomSize(e.o);
} else {
BQNFFIType* t = c(BQNFFIType, e.o);
if (t->ty==cty_ptr || t->ty==cty_tlarr) return sizeof(void*); // *any / &any / top-level [n]any if (t->ty==cty_ptr || t->ty==cty_tlarr) return sizeof(void*); // *any / &any / top-level [n]any
else if (t->ty==cty_struct || t->ty==cty_starr) return t->structSize; // {...} else if (t->ty==cty_struct || t->ty==cty_starr) return t->structSize; // {...}
else if (t->ty==cty_repr) { // any:any else if (t->ty==cty_repr) { // any:any
B o2 = t->a[0].o; B o2 = t->a[0].o;
if (isC32(o2)) return atomSize(o2); if (isC32(o2)) return calcAtomSize(o2);
if (c(BQNFFIType,o2)->ty != cty_ptr) thrM("FFI: Bad type with reinterpretation"); if (c(BQNFFIType,o2)->ty != cty_ptr) thrM("FFI: Bad type with reinterpretation");
return sizeof(void*); return sizeof(void*);
} else thrM("FFI: Unimplemented type (size calculation)"); } else thrM("FFI: Unimplemented type (size calculation)");
} }
static u64 calcMemSize(B o) {
return isC32(o)? sty_w[o2cG(o)] : calcMemSizeComplex(o);
}
static u64 calcStaticSize(B o) {
return isC32(o)? calcAtomSize(o) : calcMemSizeComplex(o);
} }
B ffiload_c2(B t, B w, B x) { B ffiload_c2(B t, B w, B x) {
@ -974,20 +999,27 @@ B ffiload_c2(B t, B w, B x) {
{ {
B atomType; B atomType;
usz size; usz size;
if (isC32(tRes.o)) { atomType = tRes.o; goto calcRes; } if (isC32(tRes.o)) { atomType = tRes.o; goto calcAtomSize; }
BQNFFIType* t = c(BQNFFIType, tRes.o); BQNFFIType* t = c(BQNFFIType, tRes.o);
if (t->ty == cty_repr) { if (t->ty == cty_repr) {
B o2 = t->a[0].o; B o2 = t->a[0].o;
if (!isC32(o2)) thrM("FFI: Unimplemented result type"); if (!isC32(o2)) thrM("FFI: Unimplemented result type");
atomType = o2; atomType = o2;
goto calcRes; goto calcAtomSize;
} }
if (t->ty==cty_struct) { size = t->structSize; goto allocRes; } if (t->ty==cty_struct) { size = t->structSize; goto allocRes; }
if (t->ty==cty_tlarr) thrM("FFI: Cannot return array"); if (t->ty==cty_tlarr) thrM("FFI: Cannot return array");
if (t->ty==cty_ptr) {
size = sizeof(void*);
goto allocRes;
}
thrM("FFI: Unimplemented result type"); thrM("FFI: Unimplemented result type");
calcRes:; size = atomSize(atomType); calcAtomSize:;
size = calcAtomSize(atomType);
goto allocRes;
allocRes:; STATIC_ALLOC(tRes, size); allocRes:; STATIC_ALLOC(tRes, size);
} }
@ -1000,7 +1032,7 @@ B ffiload_c2(B t, B w, B x) {
for (usz i = 0; i < argn; i++) { for (usz i = 0; i < argn; i++) {
BQNFFIEnt e = ffi_parseType(GetU(x,i+2), false); BQNFFIEnt e = ffi_parseType(GetU(x,i+2), false);
STATIC_ALLOC(e, calcStaticSize(e)); STATIC_ALLOC(e, calcStaticSize(e.o));
args[i+1] = e; args[i+1] = e;
if (e.wholeArg) whole[e.onW] = true; if (e.wholeArg) whole[e.onW] = true;
@ -1063,7 +1095,9 @@ B ffiload_c2(B t, B w, B x) {
#define FFI_TYPE_FLDS(OBJ, PTR) \ #define FFI_TYPE_FLDS(OBJ, PTR) \
BQNFFIType* t = (BQNFFIType*) x; \ BQNFFIType* t = (BQNFFIType*) x; \
BQNFFIEnt* arr=t->a; usz ia=t->ia; \ BQNFFIEnt* arr=t->a; usz ia=t->ia; \
if (t->ty==cty_struct || t->ty==cty_starr) { \ if (t->ty==cty_ptrh) { \
ia = 1; \
} if (t->ty==cty_struct || t->ty==cty_starr) { \
ia--; \ ia--; \
if (arr[ia].structData!=NULL) { \ if (arr[ia].structData!=NULL) { \
PTR(arr[ia].structData); \ PTR(arr[ia].structData); \
@ -1089,9 +1123,184 @@ void ffiType_print(FILE* f, B x) {
fprintf(f, ""); fprintf(f, "");
} }
STATIC_GLOBAL Body* ptrobj_ns;
STATIC_GLOBAL NFnDesc* ptrReadDesc;
STATIC_GLOBAL NFnDesc* ptrWriteDesc;
STATIC_GLOBAL NFnDesc* ptrCastDesc;
STATIC_GLOBAL NFnDesc* ptrAddDesc;
STATIC_GLOBAL NFnDesc* ptrSubDesc;
STATIC_GLOBAL NFnDesc* ptrFieldDesc;
// ptrh - pointer holder - BQNFFIType object of {{.o=eltype}, {.ptrh_ptr, .ptrh_stride}}
// eltype of sty_void is used for an untyped pointer
static B ptrh_type(B n) { return c(BQNFFIType, n)->a[0].o; }
static BQNFFIEnt* ptrh_more(B n) { return &c(BQNFFIType, n)->a[1]; }
static void* ptrh_ptr(B n) { return ptrh_more(n)->ptrh_ptr; }
static ux ptrh_stride(B n) { return ptrh_more(n)->ptrh_stride; }
B m_ptrobj_s(void* ptr, B o) {
return m_ptrobj(ptr, o, calcMemSize(o));
}
B m_ptrobj(void* ptr, B o, ux stride) {
BQNFFIEnt* op;
B obj = m_bqnFFIType(&op, cty_ptrh, 2);
op[0].o = o;
op[1].ptrh_ptr = ptr;
op[1].ptrh_stride = stride;
return m_nns(ptrobj_ns,
m_nfn(ptrReadDesc, incG(obj)),
m_nfn(ptrWriteDesc, incG(obj)),
m_nfn(ptrCastDesc, incG(obj)),
m_nfn(ptrAddDesc, incG(obj)),
m_nfn(ptrSubDesc, incG(obj)),
m_nfn(ptrFieldDesc, obj)
);
}
static B ptrobj_checkget_holder(B x) { // doesn't consume
if (!isNsp(x)) thrF("Expected pointer object, got %S", genericDesc(x));
if (c(NS,x)->desc != ptrobj_ns->nsDesc) thrM("Expected pointer object, got some other kind of namespace");
return nfn_objU(c(NS,x)->sc->vars[0]);
}
static NOINLINE void* ptrobj_checkget(B x) {
return ptrh_ptr(ptrobj_checkget_holder(x));
}
static void* ptrobj_elbase(B h, B off, bool negate) { // doesn't consume h, off doesn't matter
if (ptrh_type(h).u == m_c32(sty_void).u) thrM("Cannot offset an untyped pointer");
char* ptr = ptrh_ptr(h);
i64 el = o2i64(off);
if (negate) el = -el;
return ptr + ptrh_stride(h)*el;
}
static B ptrobjRead_c1(B t, B x) {
B h = nfn_objU(t);
return readAny(ptrh_type(h), ptrobj_elbase(h, x, false));
}
static B ptrobjWrite_c2(B t, B w, B x) {
B h = nfn_objU(t);
genObj(ptrh_type(h), x, false, ptrobj_elbase(h, w, false));
dec(x);
return m_f64(1);
}
static B ptrobjWrite_c1(B t, B x) { return ptrobjWrite_c2(t, m_f64(0), x); }
static B ptrobjCast_c1(B t, B x) {
vfyStr(x, "Type parser", "Pointer type");
usz ia = IA(x);
void* ptr = ptrh_ptr(nfn_objU(t));
if (ia==0) {
decG(x);
return m_ptrobj(ptr, m_c32(sty_void), 0);
}
u32* xp0 = parseType_pre(x, ia);
u32* xp = xp0;
decG(x);
BQNFFIEnt parsed = ffi_parseTypeStr(&xp, false, false);
B r = m_ptrobj_s(ptr, parsed.o);
parseType_post(xp0, xp, ia);
return r;
}
static B ptrobjField_c1(B t, B x) {
B h = nfn_objU(t);
u64 fld = o2u64(x);
B el = ptrh_type(h);
if (isC32(el)) thrM("Cannot get a field of a pointer to a scalar");
BQNFFIType* elc = c(BQNFFIType, el);
char* ptr = ptrh_ptr(h);
B elNew;
if (elc->ty == cty_starr || elc->ty == cty_struct) {
bool isStruct = elc->ty == cty_struct;
u64 n = elc->ia-1;
if (fld >= n) thrF(isStruct? "Cannot get field %l of a struct with %s fields" : "Cannot get pointer to element %l of an array with %s elements", fld, n);
BQNFFIEnt* fldptr = &elc->a[fld];
elNew = inc(fldptr->o);
ptr+= fldptr->offset;
} else {
thrM("Pointer object: Field must be applied to a pointer to a struct or array");
}
return m_ptrobj(ptr, elNew, ptrh_stride(h));
}
static B ptrobjAdd_c1(B t, B x) {
B h = nfn_objU(t);
return m_ptrobj(ptrobj_elbase(h, x, false), inc(ptrh_type(h)), ptrh_stride(h));
}
static B ptrty_simplify(B x) {
assert(!isC32(x));
BQNFFIType* xt = c(BQNFFIType,x);
if (xt->ty == cty_repr) return xt->a[0].o;
return x;
}
static bool ptrty_equal(B a, B b) {
if (!isC32(a)) a = ptrty_simplify(a);
if (a.u == m_c32(sty_ptr).u) return true;
if (!isC32(b)) b = ptrty_simplify(b);
if (b.u == m_c32(sty_ptr).u) return true;
if (isC32(a) || isC32(b)) return a.u==b.u; // if only one is a character, this test trivially fails
BQNFFIType* at = c(BQNFFIType,a);
BQNFFIType* bt = c(BQNFFIType,b);
if (at->ty != bt->ty) return false;
switch (at->ty) { default: UD;
case cty_ptr: case cty_tlarr: {
return ptrty_equal(at->a[0].o, bt->a[0].o);
}
case cty_repr: {
UD; // should be handled by ptrty_simplify
// if (at->a[0].reType != bt->a[0].reType) return false;
// if (at->a[0].reWidth != bt->a[0].reWidth) return false;
// if (!ptrty_equal(at->a[0].o, bt->a[0].o)) return false;
// return true;
}
case cty_struct: case cty_starr: {
if (at->ia != bt->ia) return false;
ux n = at->ia-1;
for (ux i = 0; i < n; i++) {
if (!ptrty_equal(at->a[i].o, bt->a[i].o)) return false;
}
return true;
}
}
}
static B ptrobjSub_c1(B t, B x) {
B h = nfn_objU(t);
if (isNum(x)) return m_ptrobj(ptrobj_elbase(h, x, true), inc(ptrh_type(h)), ptrh_stride(h));
if (isNsp(x)) {
B h2 = ptrobj_checkget_holder(x);
B t1 = ptrh_type(h);
B t2 = ptrh_type(h2);
if (t1.u==m_c32(sty_void).u || t2.u==m_c32(sty_void).u) thrM("(pointer).Sub ptr: Both pointers must be typed");
ux stride = ptrh_stride(h);
if (stride!=ptrh_stride(h2)) thrM("(pointer).Sub ptr: Arguments must have the same stride");
if (!ptrty_equal(t1, t2)) thrM("(pointer).Sub ptr: Arguments must have compatible types");
ptrdiff_t diff = ptrh_ptr(h) - ptrh_ptr(h2);
ptrdiff_t eldiff = diff / (ptrdiff_t)stride;
if (eldiff*stride != diff) thrM("(pointer).Sub ptr: Distance between pointers isn't an exact multiple of stride");
decG(x);
return m_f64(eldiff);
}
thrF("(pointer).Sub: Unexpected argument type: %S", genericDesc(x));
}
void ffi_init(void) { void ffi_init(void) {
boundFnDesc = registerNFn(m_c8vec_0("(foreign function)"), boundFn_c1, boundFn_c2); boundFnDesc = registerNFn(m_c8vec_0("(foreign function)"), boundFn_c1, boundFn_c2);
foreignFnDesc = registerNFn(m_c8vec_0("(foreign function)"), directFn_c1, directFn_c2); foreignFnDesc = registerNFn(m_c8vec_0("(foreign function)"), directFn_c1, directFn_c2);
ptrobj_ns = m_nnsDesc("read","write","cast","add","sub","field"); // first field must be an nfn whose object is the ptrh (needed for ptrobj_checkget_holder)
ptrReadDesc = registerNFn(m_c8vec_0("(pointer).Read"), ptrobjRead_c1, c2_bad);
ptrWriteDesc = registerNFn(m_c8vec_0("(pointer).Write"), ptrobjWrite_c1, ptrobjWrite_c2);
ptrCastDesc = registerNFn(m_c8vec_0("(pointer).Cast"), ptrobjCast_c1, c2_bad);
ptrAddDesc = registerNFn(m_c8vec_0("(pointer).Add"), ptrobjAdd_c1, c2_bad);
ptrSubDesc = registerNFn(m_c8vec_0("(pointer).Sub"), ptrobjSub_c1, c2_bad);
ptrFieldDesc = registerNFn(m_c8vec_0("(pointer).Field"), ptrobjField_c1, c2_bad);
TIi(t_ffiType,freeO) = ffiType_freeO; TIi(t_ffiType,freeO) = ffiType_freeO;
TIi(t_ffiType,freeF) = ffiType_freeF; TIi(t_ffiType,freeF) = ffiType_freeF;
TIi(t_ffiType,visit) = ffiType_visit; TIi(t_ffiType,visit) = ffiType_visit;

View File

@ -6,25 +6,26 @@
# bad •FFI invocation # bad •FFI invocation
# generally weird # generally weird
!"•FFI: Path must be a list of characters" % 12 •FFI """bqn_init" !"•FFI: Path must be a list of characters" % 12 •FFI """bqn_init"
"file that doesn't exist" •FFI'e' """abort" %% 'e' !"This function can't be called monadically" % •FFI """bqn_init"
@ •FFI'e' """bqn_this symbol doesn't exist" %% 'e' @ •FFI{𝕊: 28•CurrentError@} """bqn_this symbol doesn't exist" %% "FFI: Failed to find symbol: "
!"FFI: Type must be a string" % @•FFI "hello" !"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: Too many arguments" % @•FFI"""bqn_init"70000<"i32"
!"Type parser: Unexpected character '?'" % @•FFI"""bqn_init""?"
!"Type parser: Bad array type" % @•FFI"""bqn_init""["
!"Type parser: Bad array type" % @•FFI"""bqn_init""[0"
!"Type parser: Unexpected end of input" % @•FFI"""bqn_init""[1]"
!"Type parser: Unexpected end of input" % @•FFI"""bqn_init""{"
!"Type parser: Bad integer width" % @•FFI"""bqn_init""i"
!"Type parser: Bad float width" % @•FFI"""bqn_init""f80"
!"Type parser: Bad float width" % @•FFI"""bqn_init""f128"
!"Type parser: Bad float width" % @•FFI"""bqn_init""f16"
!"Type parser: Bad float width" % @•FFI"""bqn_init""f1"
!"Type parser: Bad integer width" % @•FFI"""bqn_init""u1"
!"Type parser: Bad integer width" % @•FFI"""bqn_init""i0"
!"Type parser: Bad integer width" % @•FFI"""bqn_init""i10"
!"Type parser: Bad integer width" % @•FFI"""bqn_init""u128"
!"Type parser: Type was empty" % @•FFI"""bqn_init"""
!"Type parser: number literal too large" % @•FFI"""bqn_init""u99999999999999"
# >/𝕨/𝕩 # >/𝕨/𝕩
!"FFI: Multiple occurrences of '>' within one argument" % @•FFI"""bqn_init"">>i32" !"FFI: Multiple occurrences of '>' within one argument" % @•FFI"""bqn_init"">>i32"
@ -37,38 +38,37 @@
!"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 # arrays
!"FFI: 0-item arrays not supported" % @•FFI"""bqn_init""[0]i32" !"Type parser: 0-item arrays not supported" % @•FFI"""bqn_init""[0]i32"
!"FFI: number literal too large" % @•FFI"""bqn_init""[999999999999]i32" !"Type parser: number literal too large" % @•FFI"""bqn_init""[999999999999]i32"
!"FFI: Top-level array too large; limit is 65535 elements" % @•FFI"""bqn_init""[65536]i32" !"Type parser: 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" !"Type parser: 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" !"Type parser: Array too large; limit is 65534 bytes" % @•FFI"""bqn_init""*[20000]i32"
!"FFI: Cannot return array" % @•FFI"[4]i32""bqn_init" !"FFI: Cannot return array" % @•FFI"[4]i32""bqn_init"
# structs # structs
!"FFI: Improper struct separator or end" % @•FFI"""bqn_init""{*i32*i32}" !"Type parser: Improper struct separator or end" % @•FFI"""bqn_init""{*i32*i32}"
!"FFI: Improper struct separator or end" % @•FFI"""bqn_init""{*i32" !"Type parser: Improper struct separator or end" % @•FFI"""bqn_init""{*i32"
!"FFI: Error parsing type: Unexpected end of input" % @•FFI"""bqn_init""{*i32," !"Type parser: Unexpected end of input" % @•FFI"""bqn_init""{*i32,"
!"FFI: Structs currently cannot contain mutable references" % @•FFI"""bqn_init""{&i32}" !"Type parser: Structs currently cannot contain mutable references" % @•FFI"""bqn_init""{&i32}"
# !"Type parser: Structs currently cannot contain mutable references" % @•FFI""‿"bqn_init"‿"{[2]&i32}" # TODO
# : # :
!"FFI: Unexpected character after "":""" % @•FFI"""bqn_init""i32:" !"Type parser: Unexpected character after "":""" % @•FFI"""bqn_init""i32:"
!"FFI: Unexpected character after "":""" % f@•FFI"""bqn_init"">*u64:q8" !"Type parser: Unexpected character after "":""" % f@•FFI"""bqn_init"">*u64:q8"
!"FFI: Unsupported width in :i0" % @•FFI"""bqn_init""i32:i" !"Type parser: Unsupported width in :i0" % @•FFI"""bqn_init""i32:i"
!"FFI: Unsupported width in :i9" % @•FFI"""bqn_init""i32:i9" !"Type parser: Unsupported width in :i9" % @•FFI"""bqn_init""i32:i9"
!"FFI: Unsupported width in :u16" % @•FFI"""bqn_init""i32:u16" !"Type parser: Unsupported width in :u16" % @•FFI"""bqn_init""i32:u16"
!"FFI: Unsupported width in :i64" % @•FFI"i32:i64""bqn_init" !"Type parser: Unsupported width in :i64" % @•FFI"i32:i64""bqn_init"
!"FFI: Unsupported width in :f32" % @•FFI"""bqn_init""i32:f32" !"Type parser: Unsupported width in :f32" % @•FFI"""bqn_init""i32:f32"
!"FFI: number literal too large" % @•FFI"""bqn_init""i32:u9999999999999999" !"Type parser: number literal too large" % @•FFI"""bqn_init""i32:u9999999999999999"
!"FFI: Representation wider than the value for ""i32:f64""" % @•FFI"""bqn_init""i32:f64" !"Type parser: 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" !"Type parser: Garbage at end of type" % @•FFI"""bqn_init""i32 hello world"
# return value # return value
!"FFI: Function specification must have at least two items" % @•FFI "&" !"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 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: 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: 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 # correct elements
@ -87,7 +87,7 @@
# bad array elements # 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 !"Type parser: Structs currently cannot contain mutable references" % size_t "u64" size_tw 64 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 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 &u8 contained ¯99" % f@•FFI"&""bqn_init"">&u8" F -100
@ -166,11 +166,93 @@
# unimplemented stuff # 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: ""*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: ""*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 !"FFI: Pointer element type not implemented" % f@•FFI"""bqn_init"">**u64" F 2
# pointer objects
%DEF mm %USE defs Alloc {1𝕩 {𝕏𝕨} @•FFI"*"𝕨, "calloc", size_t, size_t} free @•FFI"""free"">*"
%DEF WallocE Cpy +´(28×8)×256|(@•FFI"*:i8""memcpy""*""*""u64"){𝔽} {%USE mm allocs Clean{Free¨allocs𝕩} Clean 𝕏{𝕊: !•CurrentError Clean @} {{r allocs r 𝕨 Alloc 𝕩}}}
%DEF Walloc Cpy +´(28×8)×256|(@•FFI"*:i8""memcpy""*""*""u64"){𝔽} {%USE mm allocs Clean{Free¨allocs𝕩} Clean 𝕏 {{r allocs r 𝕨 Alloc 𝕩}}}
# .Field
%USE Walloc {{𝕊al: 1("{i32,i32,i32}" Al 99).Field0¨ ¯2+8}} %% 00111000
%USE Walloc {{𝕊al: 1("[5]u8" Al 99).Field0¨ ¯2+9}} %% 001111100
%USE Walloc {{𝕊al: 1("[5]u8" Al 99).Field0¨ ,¯∞,0÷0,@,"foo",{}}} %% 60
# .Write, .Read
%USE Walloc {{𝕊al: p"{i16,i16,i16,i64}" Al 99 p.Write ¯410000¯300007e12 s-{Cpy 0˜˜𝕩}´ p.Field 2, p Cpy p.Add 1, p, s p.Read¨ 2}} %% ¯410000¯300007e12, ¯41000000
%USE Walloc {{𝕊al: p"{i32,{i64,i64}}" Al 999 {𝕩 p.Write 𝕩×10,𝕩×2030}¨ 4 ((p.Field 1).Field 0).Read¨ 4 }} %% 20×4
!"Cannot get a field of a pointer to a scalar" % %USE WallocE {{𝕊al: ("i32" Al 99).Field 0}}
# garbage arguments
!"Expected integer, got character" % %USE WallocE {{𝕊al: p"i8" Al 1 p.Add '!'}}
!"(pointer).Sub: Unexpected argument type: character" % %USE WallocE {{𝕊al: p"i8" Al 1 p.Sub '!'}}
!"Expected non-negative integer, got character" % %USE WallocE {{𝕊al: p"i8" Al 1 p.Field '!'}}
!"Type parser: Pointer type must be a string" % %USE WallocE {{𝕊al: p"i8" Al 1 p.Cast '!'}}
!"Expected integer, got character" % %USE WallocE {{𝕊al: p"i8" Al 1 p.Read '!'}}
!"FFI: improper value for i8" % %USE WallocE {{𝕊al: p"i8" Al 1 p.Write '!'}}
# untyped pointers
!"Cannot get a field of a pointer to a scalar" % %USE WallocE {{𝕊al: ("" Al 99).Field 0}}
# %USE Walloc {{𝕊al: p←"i32" Al 99 ⋄ p.Write 123 ⋄ (((p.Cast "").Add 0).Cast"i32").Read 0}} %% 123
!"Cannot offset an untyped pointer" % %USE WallocE {{𝕊al: ("" Al 99).Add 0}}
!"Cannot offset an untyped pointer" % %USE WallocE {{𝕊al: ("" Al 99).Add 1}}
# .Add, .Sub, .Cast
%USE Walloc {{𝕊al: p"{i32,*}" Al 1 p.Sub p.Cast "{i32,*i32}"}} %% 0
%USE Walloc {{𝕊al: p"{i32,*}" Al 1 p.Sub p.Add 123}} %% ¯123
%USE Walloc {{𝕊al: p"{i32,*}" Al 1 p.Sub p.Sub 123}} %% 123
# ptr1.Sub ptr2
!"(pointer).Sub ptr: Both pointers must be typed" % %USE WallocE {{𝕊al: p"" Al 1 p.Sub p}}
!"(pointer).Sub ptr: Both pointers must be typed" % %USE WallocE {{𝕊al: p"i8" Al 1 (p.Cast "").Sub p.Cast "{{i8,i16},i32}"}}
!"(pointer).Sub ptr: Both pointers must be typed" % %USE WallocE {{𝕊al: p"i8" Al 1 (p.Cast "{{i8,i16},i32}").Sub p.Cast ""}}
!"(pointer).Sub ptr: Arguments must have the same stride" % %USE WallocE {{𝕊al: p"i8" Al 1 ((p.Cast "{{i8,i16},i32}").Field 0).Sub p.Cast "{i8,i16}"}}
!"(pointer).Sub ptr: Arguments must have compatible types" % %USE WallocE {{𝕊al: p"i8" Al 1 p.Sub p.Cast "u8"}}
!"(pointer).Sub ptr: Distance between pointers isn't an exact multiple of stride" % %USE WallocE {{𝕊al: p"i8" Al 1 (p.Cast "i32").Sub (p.Add 6).Cast "i32"}}
!"Type parser: Structs currently cannot contain mutable references" % @•FFI """bqn_init""{&i8}"
# !"" % %USE Walloc {{𝕊al: p←"i8" Al 1 ⋄ (p.Cast "{{i8,i16},i32}").Sub p.Cast "{{u8,i16},i32}"}}
# !"" % %USE Walloc {{𝕊al: p←"i8" Al 1 ⋄ (p.Cast "{{i8,*i16},i32}").Sub p.Cast "{{u8,*u16},i32}"}}
# type comparison
%USE Walloc {{𝕊al: p"i8" Al 1 t"{{i8,i16,**i8,[12]i8,*:i32,i64:f64,*,*f64},i32,i64:c8}" (p.Cast t).Sub p.Cast t}} %% 0 # ⎊-less test of most passing things to run under heapverify
%DEF TyEq {exp𝕊ab: %USE Walloc {{𝕊al: p"i8" Al 1 ! (exp990) ((p.Cast a).Sub)99 p.Cast b}}}
1 %USE TyEq "{*i16}", "{*i16}"
0 %USE TyEq "{*i16}", "{*u16}"
1 %USE TyEq "{*i16}", "{*}"
1 %USE TyEq "{*}", "{*i16}"
1 %USE TyEq "{*:i8}", "{*i16}"
1 %USE TyEq "{*:i8}", "{*i16:i8}"
1 %USE TyEq "{*:i8}", "{*i16:i32}"
1 %USE TyEq "{{i8,i16},i32}", "{{i8,i16},i32}"
0 %USE TyEq "{{i8,i16},i32}", "{{u8,i16},i32}"
1 %USE TyEq "{[3]i32,[5]i32}", "{[3]i32,[5]i32}"
1 %USE TyEq "{[3]i32,[5]i32}", "{[3]i32,[5]i32:i8}"
0 %USE TyEq "{[3]i32,[5]i32}", "{[3]i32,[5]u32}"
0 %USE TyEq "{[3]i32,[5]i32}", "{[5]i32,[3]i32}"
0 %USE TyEq "[4]i32", "[3]i32"
0 %USE TyEq "*[4]i32", "*[3]i32"
0 %USE TyEq "{i32}", "i32"
0 %USE TyEq "i16", "i32"
0 %USE TyEq "i16", "u16"
1 %USE TyEq "{i32:i8,i64}", "{i32:i8,i64}"
0 %USE TyEq "{i32:i8,i64}", "{u32:i8,i64}"
0 %USE TyEq "{i32:i8,i64}", "{i16:i8,i64}"
0 %USE TyEq "*{i32:i8}", "*{i16:i8}"
1 %USE TyEq "{i32:i8,i64}", "{i32:i16,i64}"
1 %USE TyEq "{i32:i32,i64}","{i32:u1,i64}"
1 %USE TyEq "{i32:i8,i64}", "{i32,i64}"
1 %USE TyEq "*i32:i8", "*"
1 %USE TyEq "*i32:i8", "*:c32"
# !"FFI: Unimplemented result type" % @•FFI"*i32"‿"bqn_init"
# !"FFI: Unimplemented result type" % @•FFI"&i32"‿"bqn_init"
# unchecked stuff # unchecked stuff
# !"FFI: Bad array corresponding to ""i64:c8"": expected 8 elements, got 1" % f←@•FFI""‿"bqn_init"‿">i64:c8" ⋄ F 8⥊2 # !"FFI: Bad array corresponding to ""i64:c8"": expected 8 elements, got 1" % f←@•FFI""‿"bqn_init"‿">i64:c8" ⋄ F 8⥊2