initial pointer objects
This commit is contained in:
parent
297302cad0
commit
7edcccdf82
335
src/ffi.c
335
src/ffi.c
@ -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,6 +233,8 @@ 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
|
||||||
@ -245,9 +247,16 @@ typedef struct BQNFFIEnt {
|
|||||||
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;
|
||||||
|
|||||||
@ -6,25 +6,26 @@
|
|||||||
# bad •FFI invocation
|
# bad •FFI invocation
|
||||||
# generally weird
|
# generally weird
|
||||||
!"•FFI: Path must be a list of characters" % 1‿2 •FFI ""‿"bqn_init"
|
!"•FFI: Path must be a list of characters" % 1‿2 •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 ⋈∘⋈¨¯32768‿32767
|
!"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 ⋈∘⋈¨¯32768‿32767
|
||||||
|
|
||||||
!"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 ← +´(2⋆8×↕8)×256|(@•FFI"*:i8"‿"memcpy"‿"*"‿"*"‿"u64"){𝔽} ⋄ {%USE mm ⋄ allocs←⟨⟩ ⋄ Clean←{Free¨allocs⋄𝕩} ⋄ Clean 𝕏⎊{𝕊: !•CurrentError Clean @} {{r ⊣ allocs∾↩ r← 𝕨 Alloc 𝕩}}}
|
||||||
|
%DEF Walloc Cpy ← +´(2⋆8×↕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).Field⎊0¨ ¯2+↕8}} %% 0‿0‿1‿1‿1‿0‿0‿0
|
||||||
|
%USE Walloc {{𝕊al: 1∘("[5]u8" Al 99).Field⎊0¨ ¯2+↕9}} %% 0‿0‿1‿1‿1‿1‿1‿0‿0
|
||||||
|
%USE Walloc {{𝕊al: 1∘("[5]u8" Al 99).Field⎊0¨ ⟨∞,¯∞,0÷0,@,"foo",{⇐}⟩}} %% 6⥊0
|
||||||
|
|
||||||
|
# .Write, .Read
|
||||||
|
%USE Walloc {{𝕊al: p←"{i16,i16,i16,i64}" Al 99 ⋄ p.Write ¯4‿10000‿¯30000‿7e12 ⋄ s←-○{Cpy 0∾˜⋈˜𝕩}´ ⟨p.Field 2, p⟩ ⋄ Cpy ⟨p.Add 1, p, s⟩ ⋄ p.Read¨ ↕2}} %% ⟨¯4‿10000‿¯30000‿7e12, ¯4‿10000‿0‿0⟩
|
||||||
|
%USE Walloc {{𝕊al: p←"{i32,{i64,i64}}" Al 999 ⋄ {𝕩 p.Write ⟨𝕩×10,𝕩×20‿30⟩}¨ ↕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𝕊a‿b: %USE Walloc {{𝕊al: p←"i8" Al 1 ⋄ ! (exp⊑99‿0) ≡ ((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
|
||||||
Loading…
Reference in New Issue
Block a user