Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
683129b6a5 | ||
|
|
7f4022120e | ||
|
|
3f9e86c37a | ||
|
|
12f460e376 | ||
|
|
44c4488fcc | ||
|
|
44617b6554 | ||
|
|
ccc7d056ba | ||
|
|
566d5ef96f | ||
|
|
cf53f3a347 | ||
|
|
74b48ff56b | ||
|
|
9410370cc5 | ||
|
|
743ddf83c1 | ||
|
|
d2538d08a7 | ||
|
|
75fe9d61a3 | ||
|
|
fce0672282 | ||
|
|
69a2a01bc7 | ||
|
|
a2d4e79c20 | ||
|
|
6e0a9b2b48 | ||
|
|
276f2070ee | ||
|
|
1a158b5de3 | ||
|
|
d4a72bf661 | ||
|
|
caa707325a | ||
|
|
3c588e67b8 | ||
|
|
6c26dad048 | ||
|
|
393e2c125e | ||
|
|
e56dc2ed6a | ||
|
|
4a449ed85e | ||
|
|
88dbe90075 | ||
|
|
a6f6451434 | ||
|
|
8a4370c1f6 | ||
|
|
7a068450b3 | ||
|
|
ae3222e4c2 | ||
|
|
27b4bfa3fa | ||
|
|
8af229947f | ||
|
|
f235a2f539 | ||
|
|
b0ebb479f6 | ||
|
|
df92e21520 | ||
|
|
a1a25cc57f | ||
|
|
e9aaf8e0af | ||
|
|
7a80e407af | ||
|
|
57f8d535fb | ||
|
|
fcc444a100 | ||
|
|
8ccb3c21e1 | ||
|
|
3ce9567f62 | ||
|
|
765e812b77 |
669
c/cjson/cjson.go
669
c/cjson/cjson.go
@@ -26,87 +26,25 @@ const (
|
||||
LLGoPackage = "link: $(pkg-config --libs libcjson); -lcjson"
|
||||
)
|
||||
|
||||
type Bool c.Int
|
||||
|
||||
// llgo:type C
|
||||
type JSON struct {
|
||||
Unused [0]byte
|
||||
}
|
||||
|
||||
//go:linkname Parse C.cJSON_Parse
|
||||
func Parse(value *c.Char) *JSON
|
||||
|
||||
//go:linkname ParseWithLength C.cJSON_ParseWithLength
|
||||
func ParseWithLength(value *byte, valueLength uintptr) *JSON
|
||||
|
||||
func ParseBytes(value []byte) *JSON {
|
||||
return ParseWithLength(unsafe.SliceData(value), uintptr(len(value)))
|
||||
return ParseWithLength((*c.Char)(unsafe.Pointer(unsafe.SliceData(value))), uintptr(len(value)))
|
||||
}
|
||||
|
||||
func ParseString(value string) *JSON {
|
||||
return ParseWithLength(unsafe.StringData(value), uintptr(len(value)))
|
||||
return ParseWithLength((*c.Char)(unsafe.Pointer(unsafe.StringData(value))), uintptr(len(value)))
|
||||
}
|
||||
|
||||
//go:linkname Null C.cJSON_CreateNull
|
||||
func Null() *JSON
|
||||
|
||||
//go:linkname True C.cJSON_CreateTrue
|
||||
func True() *JSON
|
||||
|
||||
//go:linkname False C.cJSON_CreateFalse
|
||||
func False() *JSON
|
||||
|
||||
//go:linkname Bool C.cJSON_CreateBool
|
||||
func Bool(boolean c.Int) *JSON
|
||||
|
||||
//go:linkname Number C.cJSON_CreateNumber
|
||||
func Number(num float64) *JSON
|
||||
|
||||
//go:linkname String C.cJSON_CreateString
|
||||
func String(str *c.Char) *JSON
|
||||
|
||||
//go:linkname Array C.cJSON_CreateArray
|
||||
func Array() *JSON
|
||||
|
||||
//go:linkname Object C.cJSON_CreateObject
|
||||
func Object() *JSON
|
||||
|
||||
// raw json
|
||||
// CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
|
||||
//
|
||||
//go:linkname Raw C.cJSON_CreateRaw
|
||||
func Raw(raw *c.Char) *JSON
|
||||
|
||||
// Create a string where valuestring references a string so
|
||||
// it will not be freed by Delete
|
||||
// Render a cJSON entity to text for transfer/storage without any formatting.
|
||||
//
|
||||
//go:linkname StringRef C.cJSON_CreateStringReference
|
||||
func StringRef(str *c.Char) *JSON
|
||||
|
||||
// Create an object that only references it's elements so
|
||||
// they will not be freed by Delete
|
||||
//
|
||||
//go:linkname ObjectRef C.cJSON_CreateObjectReference
|
||||
func ObjectRef(child *JSON) *JSON
|
||||
|
||||
// Create an array that only references it's elements so
|
||||
// they will not be freed by Delete
|
||||
//
|
||||
//go:linkname ArrayRef C.cJSON_CreateArrayReference
|
||||
func ArrayRef(child *JSON) *JSON
|
||||
|
||||
// Delete a JSON entity and all subentities.
|
||||
//
|
||||
// llgo:link (*JSON).Delete C.cJSON_Delete
|
||||
func (o *JSON) Delete() {}
|
||||
|
||||
// Append item to the specified array.
|
||||
//
|
||||
// llgo:link (*JSON).AddItem C.cJSON_AddItemToArray
|
||||
func (o *JSON) AddItem(item *JSON) c.Int { return 0 }
|
||||
|
||||
// Append item to the specified object.
|
||||
//
|
||||
// llgo:link (*JSON).SetItem C.cJSON_AddItemToObject
|
||||
func (o *JSON) SetItem(key *c.Char, item *JSON) c.Int { return 0 }
|
||||
|
||||
// llgo:link (*JSON).CStr C.cJSON_PrintUnformatted
|
||||
func (o *JSON) CStr() *c.Char { return nil }
|
||||
|
||||
@@ -115,39 +53,618 @@ func (o *JSON) CStr() *c.Char { return nil }
|
||||
// llgo:link (*JSON).Cstr C.cJSON_PrintUnformatted
|
||||
func (o *JSON) Cstr() *c.Char { return nil }
|
||||
|
||||
// malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks
|
||||
//
|
||||
//go:linkname FreeCStr C.cJSON_free
|
||||
func FreeCStr(*c.Char)
|
||||
|
||||
// CJSON_PUBLIC(const char*) cJSON_Version(void);
|
||||
//
|
||||
// returns the version of cJSON as a string
|
||||
//
|
||||
//go:linkname Version C.cJSON_Version
|
||||
func Version() *c.Char
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
|
||||
//
|
||||
// Memory Management: the caller is always responsible to free
|
||||
// the results from all variants of cJSON_Parse (with cJSON_Delete)
|
||||
// and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or
|
||||
// cJSON_free as appropriate). The exception is cJSON_PrintPreallocated,
|
||||
// where the caller has full responsibility of the buffer.
|
||||
//
|
||||
//go:linkname Parse C.cJSON_Parse
|
||||
func Parse(value *c.Char) *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
|
||||
//
|
||||
// Memory Management: the caller is always responsible to free
|
||||
// the results from all variants of cJSON_Parse (with cJSON_Delete)
|
||||
// and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or
|
||||
// cJSON_free as appropriate). The exception is cJSON_PrintPreallocated,
|
||||
// where the caller has full responsibility of the buffer.
|
||||
//
|
||||
//go:linkname ParseWithLength C.cJSON_ParseWithLength
|
||||
func ParseWithLength(value *c.Char, valueLength uintptr) *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_Bool require_null_terminated);
|
||||
//
|
||||
// ParseWithOpts allows you to require (and check) that the JSON is null terminated,
|
||||
// and to retrieve the pointer to the final byte parsed.
|
||||
// If you supply a ptr in return_parse_end and parsing fails, then
|
||||
// return_parse_end will contain a pointer to the error so will match
|
||||
// cJSON_GetErrorPtr().
|
||||
//
|
||||
//go:linkname ParseWithOpts C.cJSON_ParseWithOpts
|
||||
func ParseWithOpts(value *c.Char, return_parse_end **c.Char, require_null_terminated Bool) *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_Bool require_null_terminated);
|
||||
//
|
||||
// ParseWithOpts allows you to require (and check) that the JSON is null terminated,
|
||||
// and to retrieve the pointer to the final byte parsed.
|
||||
// If you supply a ptr in return_parse_end and parsing fails, then
|
||||
// return_parse_end will contain a pointer to the error so will match
|
||||
// cJSON_GetErrorPtr().
|
||||
//
|
||||
//go:linkname ParseWithLengthOpts C.cJSON_ParseWithLengthOpts
|
||||
func ParseWithLengthOpts(value *c.Char, buffer_length uintptr, return_parse_end **c.Char, require_null_terminated Bool) *JSON
|
||||
|
||||
// CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
|
||||
// Render a JSON entity to text for transfer/storage.
|
||||
//
|
||||
// llgo:link (*JSON).Print C.cJSON_Print
|
||||
func (o *JSON) Print() *c.Char { return nil }
|
||||
|
||||
// CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
|
||||
// Render a JSON entity to text for transfer/storage without any formatting.
|
||||
//
|
||||
// llgo:link (*JSON).PrintUnformatted C.cJSON_PrintUnformatted
|
||||
func (o *JSON) PrintUnformatted() *c.Char { return nil }
|
||||
|
||||
// CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_Bool fmt);
|
||||
//
|
||||
// Render a JSON entity to text using a buffered strategy.
|
||||
//
|
||||
// prebuffer is a guess at the final size. guessing well reduces reallocation.
|
||||
//
|
||||
// fmt=0 gives unformatted, =1 gives formatted.
|
||||
//
|
||||
// llgo:link (*JSON).PrintBuffered C.cJSON_PrintBuffered
|
||||
func (o *JSON) PrintBuffered(prebuffer c.Int, fmt c.Int) *c.Char { return nil }
|
||||
func (o *JSON) PrintBuffered(prebuffer c.Int, fmt Bool) *c.Char { return nil }
|
||||
|
||||
// llgo:link (*JSON).GetObjectItemCaseSensitive C.cJSON_GetObjectItemCaseSensitive
|
||||
func (o *JSON) GetObjectItemCaseSensitive(key *c.Char) *JSON { return nil }
|
||||
// CJSON_PUBLIC(cJSON_Bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_Bool format);
|
||||
//
|
||||
// Render a cJSON entity to text using a buffer already allocated in memory with given
|
||||
// length. Returns 1 on success and 0 on failure.
|
||||
// note that cJSON is not always 100% accurate in estimating how much memory it will use,
|
||||
// so to be safe allocate 5 bytes more than you actually need
|
||||
//
|
||||
// llgo:link (*JSON).PrintPreallocated C.cJSON_PrintPreallocated
|
||||
func (o *JSON) PrintPreallocated(buffer *c.Char, length c.Int, format Bool) Bool {
|
||||
return Bool(0)
|
||||
}
|
||||
|
||||
// CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
|
||||
// Delete a JSON entity and all subentities.
|
||||
//
|
||||
// llgo:link (*JSON).Delete C.cJSON_Delete
|
||||
func (o *JSON) Delete() {}
|
||||
|
||||
// CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
|
||||
//
|
||||
// Returns the number of items in an array (or object).
|
||||
//
|
||||
// llgo:link (*JSON).GetArraySize C.cJSON_GetArraySize
|
||||
func (o *JSON) GetArraySize() c.Int { return 0 }
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
|
||||
//
|
||||
// Retrieve item number "index" from array "array". Returns NULL if unsuccessful.
|
||||
//
|
||||
// llgo:link (*JSON).GetArrayItem C.cJSON_GetArrayItem
|
||||
func (o *JSON) GetArrayItem(index c.Int) *JSON { return nil }
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
|
||||
//
|
||||
// Get item "string" from object. Case insensitive.
|
||||
//
|
||||
// llgo:link (*JSON).GetObjectItem C.cJSON_GetObjectItem
|
||||
func (o *JSON) GetObjectItem(s *c.Char) *JSON { return nil }
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
|
||||
//
|
||||
// Get item "string" from object. Case sensitive.
|
||||
//
|
||||
// llgo:link (*JSON).GetObjectItemCaseSensitive C.cJSON_GetObjectItemCaseSensitive
|
||||
func (o *JSON) GetObjectItemCaseSensitive(key *c.Char) *JSON { return nil }
|
||||
|
||||
// CJSON_PUBLIC(cJSON_Bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
|
||||
//
|
||||
// llgo:link (*JSON).HasObjectItem C.cJSON_HasObjectItem
|
||||
func (o *JSON) HasObjectItem(s *c.Char) Bool { return Bool(0) }
|
||||
|
||||
// CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
|
||||
//
|
||||
// For analysing failed parses. This returns a pointer to the parse error.
|
||||
// You'll probably need to look a few chars back to make sense of it.
|
||||
// Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds.
|
||||
//
|
||||
//go:linkname GetErrorPtr C.cJSON_GetErrorPtr
|
||||
func GetErrorPtr() *c.Char
|
||||
|
||||
// CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
|
||||
//
|
||||
// Check item type and return its value
|
||||
//
|
||||
// llgo:link (*JSON).GetStringValue C.cJSON_GetStringValue
|
||||
func (o *JSON) GetStringValue() *c.Char { return nil }
|
||||
|
||||
// CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item);
|
||||
//
|
||||
// Check item type and return its value
|
||||
//
|
||||
// llgo:link (*JSON).GetNumberValue C.cJSON_GetNumberValue
|
||||
func (o *JSON) GetNumberValue() c.Double { return 0 }
|
||||
|
||||
// CJSON_PUBLIC(cJSON_Bool) cJSON_IsInvalid(const cJSON * const item);
|
||||
//
|
||||
// These functions check the type of an item
|
||||
//
|
||||
// llgo:link (*JSON).IsInvalid C.cJSON_IsInvalid
|
||||
func (o *JSON) IsInvalid() Bool { return Bool(0) }
|
||||
|
||||
// CJSON_PUBLIC(cJSON_Bool) cJSON_IsFalse(const cJSON * const item);
|
||||
//
|
||||
// These functions check the type of an item
|
||||
//
|
||||
// llgo:link (*JSON).IsFalse C.cJSON_IsFalse
|
||||
func (o *JSON) IsFalse() Bool { return Bool(0) }
|
||||
|
||||
// CJSON_PUBLIC(cJSON_Bool) cJSON_IsTrue(const cJSON * const item);
|
||||
//
|
||||
// These functions check the type of an item
|
||||
//
|
||||
// llgo:link (*JSON).IsTrue C.cJSON_IsTrue
|
||||
func (o *JSON) IsTrue() Bool { return Bool(0) }
|
||||
|
||||
// CJSON_PUBLIC(cJSON_Bool) cJSON_IsBool(const cJSON * const item);
|
||||
//
|
||||
// These functions check the type of an item
|
||||
//
|
||||
// llgo:link (*JSON).IsBool C.cJSON_IsBool
|
||||
func (o *JSON) IsBool() Bool { return Bool(0) }
|
||||
|
||||
// CJSON_PUBLIC(cJSON_Bool) cJSON_IsNull(const cJSON * const item);
|
||||
//
|
||||
// These functions check the type of an item
|
||||
//
|
||||
// llgo:link (*JSON).IsNull C.cJSON_IsNull
|
||||
func (o *JSON) IsNull() Bool { return Bool(0) }
|
||||
|
||||
// CJSON_PUBLIC(cJSON_Bool) cJSON_IsNumber(const cJSON * const item);
|
||||
//
|
||||
// These functions check the type of an item
|
||||
//
|
||||
// llgo:link (*JSON).IsNumber C.cJSON_IsNumber
|
||||
func (o *JSON) IsNumber() Bool { return Bool(0) }
|
||||
|
||||
// CJSON_PUBLIC(cJSON_Bool) cJSON_IsString(const cJSON * const item);
|
||||
//
|
||||
// These functions check the type of an item
|
||||
//
|
||||
// llgo:link (*JSON).IsString C.cJSON_IsString
|
||||
func (o *JSON) IsString() Bool { return Bool(0) }
|
||||
|
||||
// CJSON_PUBLIC(cJSON_Bool) cJSON_IsArray(const cJSON * const item);
|
||||
//
|
||||
// These functions check the type of an item
|
||||
//
|
||||
// llgo:link (*JSON).IsArray C.cJSON_IsArray
|
||||
func (o *JSON) IsArray() Bool { return Bool(0) }
|
||||
|
||||
// CJSON_PUBLIC(cJSON_Bool) cJSON_IsObject(const cJSON * const item);
|
||||
//
|
||||
// These functions check the type of an item
|
||||
//
|
||||
// llgo:link (*JSON).IsObject C.cJSON_IsObject
|
||||
func (o *JSON) IsObject() Bool { return Bool(0) }
|
||||
|
||||
// CJSON_PUBLIC(cJSON_Bool) cJSON_IsRaw(const cJSON * const item);
|
||||
//
|
||||
// These functions check the type of an item
|
||||
//
|
||||
// llgo:link (*JSON).IsRaw C.cJSON_IsRaw
|
||||
func (o *JSON) IsRaw() Bool { return Bool(0) }
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
|
||||
//
|
||||
// These calls create a cJSON item of the appropriate type.
|
||||
//
|
||||
//go:linkname Null C.cJSON_CreateNull
|
||||
func Null() *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
|
||||
//
|
||||
// same as Null func
|
||||
//
|
||||
//go:linkname CreateNull C.cJSON_CreateNull
|
||||
func CreateNull() *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
|
||||
//
|
||||
//go:linkname True C.cJSON_CreateTrue
|
||||
func True() *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
|
||||
//
|
||||
// same as True func
|
||||
//
|
||||
//go:linkname CreateTrue C.cJSON_CreateTrue
|
||||
func CreateTrue() *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
|
||||
//
|
||||
//go:linkname False C.cJSON_CreateFalse
|
||||
func False() *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
|
||||
//
|
||||
// same as False func
|
||||
//
|
||||
//go:linkname CreateFalse C.cJSON_CreateFalse
|
||||
func CreateFalse() *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_Bool boolean);
|
||||
//
|
||||
// same as Bool func
|
||||
//
|
||||
//go:linkname CreateBool C.cJSON_CreateBool
|
||||
func CreateBool(boolean c.Int) *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
|
||||
//
|
||||
//go:linkname Number C.cJSON_CreateNumber
|
||||
func Number(num float64) *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
|
||||
//
|
||||
// same as Number func
|
||||
//
|
||||
//go:linkname CreateNumber C.cJSON_CreateNumber
|
||||
func CreateNumber(num float64) *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
|
||||
//
|
||||
//go:linkname String C.cJSON_CreateString
|
||||
func String(str *c.Char) *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
|
||||
//
|
||||
// same as String func
|
||||
//
|
||||
//go:linkname CreateString C.cJSON_CreateString
|
||||
func CreateString(str *c.Char) *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
|
||||
// raw json
|
||||
//
|
||||
//go:linkname Raw C.cJSON_CreateRaw
|
||||
func Raw(raw *c.Char) *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
|
||||
//
|
||||
// same as Raw func
|
||||
//
|
||||
//go:linkname CreateRaw C.cJSON_CreateRaw
|
||||
func CreateRaw(raw *c.Char) *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
|
||||
//
|
||||
//go:linkname Array C.cJSON_CreateArray
|
||||
func Array() *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
|
||||
//
|
||||
// same as Array func
|
||||
//
|
||||
//go:linkname CreateArray C.cJSON_CreateArray
|
||||
func CreateArray() *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
|
||||
//
|
||||
//go:linkname Object C.cJSON_CreateObject
|
||||
func Object() *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
|
||||
//
|
||||
// same as Object func
|
||||
//
|
||||
//go:linkname CreateObject C.cJSON_CreateObject
|
||||
func CreateObject() *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
|
||||
//
|
||||
// Create a string where valuestring references a string so
|
||||
// it will not be freed by Delete
|
||||
//
|
||||
//go:linkname StringRef C.cJSON_CreateStringReference
|
||||
func StringRef(str *c.Char) *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
|
||||
//
|
||||
// same as StringRef func
|
||||
//
|
||||
//go:linkname CreateStringReference C.cJSON_CreateStringReference
|
||||
func CreateStringReference(str *c.Char) *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
|
||||
//
|
||||
// Create an object that only references it's elements so
|
||||
// they will not be freed by Delete
|
||||
//
|
||||
//go:linkname ObjectRef C.cJSON_CreateObjectReference
|
||||
func ObjectRef(child *JSON) *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
|
||||
//
|
||||
// same as ObjectRef func
|
||||
//
|
||||
//go:linkname CreateObjectReference C.cJSON_CreateObjectReference
|
||||
func CreateObjectReference(child *JSON) *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
|
||||
//
|
||||
// Create an array that only references it's elements so
|
||||
// they will not be freed by Delete
|
||||
//
|
||||
//go:linkname ArrayRef C.cJSON_CreateArrayReference
|
||||
func ArrayRef(child *JSON) *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
|
||||
//
|
||||
// same as ArrayRef func
|
||||
//
|
||||
//go:linkname CreateArrayReference C.cJSON_CreateArrayReference
|
||||
func CreateArrayReference(child *JSON) *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
|
||||
//
|
||||
//go:linkname CreateIntArray C.cJSON_CreateIntArray
|
||||
func CreateIntArray(numbers *c.Int, count c.Int) *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
|
||||
//
|
||||
//go:linkname CreateFloatArray C.cJSON_CreateFloatArray
|
||||
func CreateFloatArray(numbers *c.Float, count c.Int) *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
|
||||
//
|
||||
//go:linkname CreateDoubleArray C.cJSON_CreateDoubleArray
|
||||
func CreateDoubleArray(numbers *c.Double, count c.Int) *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
|
||||
//
|
||||
//go:linkname CreateStringArray C.cJSON_CreateStringArray
|
||||
func CreateStringArray(strings *c.Char, count c.Int) *JSON
|
||||
|
||||
// CJSON_PUBLIC(cJSON_Bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
|
||||
//
|
||||
// Append item to the specified array.
|
||||
//
|
||||
// llgo:link (*JSON).AddItem C.cJSON_AddItemToArray
|
||||
func (o *JSON) AddItem(item *JSON) Bool { return Bool(0) }
|
||||
|
||||
// CJSON_PUBLIC(cJSON_Bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
|
||||
//
|
||||
// same as AddItem func
|
||||
//
|
||||
// llgo:link (*JSON).AddItemToArray C.cJSON_AddItemToArray
|
||||
func (o *JSON) AddItemToArray(item *JSON) Bool { return Bool(0) }
|
||||
|
||||
// CJSON_PUBLIC(cJSON_Bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
|
||||
//
|
||||
// Append item to the specified object.
|
||||
//
|
||||
// llgo:link (*JSON).SetItem C.cJSON_AddItemToObject
|
||||
func (o *JSON) SetItem(key *c.Char, item *JSON) Bool { return Bool(0) }
|
||||
|
||||
// CJSON_PUBLIC(cJSON_Bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
|
||||
//
|
||||
// same as SetItem func
|
||||
//
|
||||
// llgo:link (*JSON).AddItemToObject C.cJSON_AddItemToObject
|
||||
func (o *JSON) AddItemToObject(key *c.Char, item *JSON) Bool { return Bool(0) }
|
||||
|
||||
// CJSON_PUBLIC(cJSON_Bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
|
||||
//
|
||||
// Use this when string is definitely const (i.e. a literal, or as good as),
|
||||
// and will definitely survive the cJSON object.
|
||||
// warning that When this function was used, make sure to always check that
|
||||
// (item->type & cJSON_StringIsConst) is zero before writing to `item->string`
|
||||
//
|
||||
// llgo:link (*JSON).AddItemToObjectCS C.cJSON_AddItemToObjectCS
|
||||
func (o *JSON) AddItemToObjectCS(s *c.Char, item *JSON) Bool { return Bool(0) }
|
||||
|
||||
// CJSON_PUBLIC(cJSON_Bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
|
||||
//
|
||||
// Append reference to item to the specified array/object.
|
||||
// Use this when you want to add an existing cJSON to a new cJSON,
|
||||
// but don't want to corrupt your existing cJSON.
|
||||
//
|
||||
// llgo:link (*JSON).AddItemReferenceToArray C.cJSON_AddItemReferenceToArray
|
||||
func (o *JSON) AddItemReferenceToArray(item *JSON) Bool { return Bool(0) }
|
||||
|
||||
// CJSON_PUBLIC(cJSON_Bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
|
||||
//
|
||||
// llgo:link (*JSON).AddItemReferenceToObject C.cJSON_AddItemReferenceToObject
|
||||
func (o *JSON) AddItemReferenceToObject(s *c.Char, item *JSON) Bool { return Bool(0) }
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
|
||||
//
|
||||
// Remove/Detach items from Arrays/Objects.
|
||||
//
|
||||
// llgo:link (*JSON).DetachItemViaPointer C.cJSON_DetachItemViaPointer
|
||||
func (o *JSON) DetachItemViaPointer(item *JSON) *JSON { return nil }
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
|
||||
//
|
||||
// llgo:link (*JSON).DetachItemFromArray C.cJSON_DetachItemFromArray
|
||||
func (o *JSON) DetachItemFromArray(which c.Int) *JSON { return nil }
|
||||
|
||||
// CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
|
||||
//
|
||||
// llgo:link (*JSON).DeleteItemFromArray C.cJSON_DeleteItemFromArray
|
||||
func (o *JSON) DeleteItemFromArray(which c.Int) {}
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
|
||||
//
|
||||
// llgo:link (*JSON).DetachItemFromObject C.cJSON_DetachItemFromObject
|
||||
func (o *JSON) DetachItemFromObject(s *c.Char) *JSON { return nil }
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
|
||||
//
|
||||
// llgo:link (*JSON).DetachItemFromObjectCaseSensitive C.cJSON_DetachItemFromObjectCaseSensitive
|
||||
func (o *JSON) DetachItemFromObjectCaseSensitive(s *c.Char) *JSON { return nil }
|
||||
|
||||
// CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
|
||||
//
|
||||
// llgo:link (*JSON).DeleteItemFromObject C.cJSON_DeleteItemFromObject
|
||||
func (o *JSON) DeleteItemFromObject(s *c.Char) {}
|
||||
|
||||
// CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
|
||||
//
|
||||
// llgo:link (*JSON).DeleteItemFromObjectCaseSensitive C.cJSON_DeleteItemFromObjectCaseSensitive
|
||||
func (o *JSON) DeleteItemFromObjectCaseSensitive(s *c.Char) {}
|
||||
|
||||
// CJSON_PUBLIC(cJSON_Bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem);
|
||||
//
|
||||
// Update array items.
|
||||
// Shifts pre-existing items to the right.
|
||||
//
|
||||
// llgo:link (*JSON).InsertItemInArray C.cJSON_InsertItemInArray
|
||||
func (o *JSON) InsertItemInArray(which c.Int, newitem *JSON) Bool { return Bool(0) }
|
||||
|
||||
// CJSON_PUBLIC(cJSON_Bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
|
||||
//
|
||||
// llgo:link (*JSON).ReplaceItemViaPointer C.cJSON_ReplaceItemViaPointer
|
||||
func (o *JSON) ReplaceItemViaPointer(item *JSON, replacement *JSON) Bool { return Bool(0) }
|
||||
|
||||
// CJSON_PUBLIC(cJSON_Bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
|
||||
//
|
||||
// llgo:link (*JSON).ReplaceItemInArray C.cJSON_ReplaceItemInArray
|
||||
func (o *JSON) ReplaceItemInArray(which c.Int, newitem *JSON) Bool { return Bool(0) }
|
||||
|
||||
// CJSON_PUBLIC(cJSON_Bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
|
||||
//
|
||||
// llgo:link (*JSON).ReplaceItemInObject C.cJSON_ReplaceItemInObject
|
||||
func (o *JSON) ReplaceItemInObject(s *c.Char, newitem *JSON) Bool { return Bool(0) }
|
||||
|
||||
// CJSON_PUBLIC(cJSON_Bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
|
||||
//
|
||||
// llgo:link (*JSON).ReplaceItemInObjectCaseSensitive C.cJSON_ReplaceItemInObjectCaseSensitive
|
||||
func (o *JSON) ReplaceItemInObjectCaseSensitive(s *c.Char, newitem *JSON) Bool {
|
||||
return Bool(0)
|
||||
}
|
||||
|
||||
// CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_Bool recurse);
|
||||
//
|
||||
// Duplicate a cJSON item
|
||||
//
|
||||
// Duplicate will create a new, identical cJSON item to the one you pass,
|
||||
// in new memory that will need to be released. With recurse!=0,
|
||||
// it will duplicate any children connected to the item.
|
||||
// The item->next and ->prev pointers are always zero on return from Duplicate.
|
||||
//
|
||||
// llgo:link (*JSON).Duplicate C.cJSON_Duplicate
|
||||
func (o *JSON) Duplicate(recurse Bool) *JSON { return nil }
|
||||
|
||||
// CJSON_PUBLIC(cJSON_Bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_Bool case_sensitive);
|
||||
//
|
||||
// Recursively compare two cJSON items for equality. If either a or b is NULL or invalid,
|
||||
// they will be considered unequal. case_sensitive determines if object keys are treated
|
||||
// case sensitive (1) or case insensitive (0)
|
||||
//
|
||||
// llgo:link (*JSON).Compare C.cJSON_Compare
|
||||
func (o *JSON) Compare(b *JSON, case_sensitive Bool) Bool { return Bool(0) }
|
||||
|
||||
// CJSON_PUBLIC(void) cJSON_Minify(char *json);
|
||||
//
|
||||
// Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
|
||||
// The input pointer json cannot point to a read-only address area, such as a string constant,
|
||||
// but should point to a readable and writable address area.
|
||||
//
|
||||
//go:linkname Minify C.cJSON_Minify
|
||||
func Minify()
|
||||
|
||||
// CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
|
||||
//
|
||||
// Helper functions for creating and adding items to an object at the same time.
|
||||
// They return the added item or NULL on failure.
|
||||
//
|
||||
// llgo:link (*JSON).AddNullToObject C.cJSON_AddNullToObject
|
||||
func (o *JSON) AddNullToObject(name *c.Char) *JSON { return nil }
|
||||
|
||||
// CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
|
||||
//
|
||||
// llgo:link (*JSON).AddTrueToObject C.cJSON_AddTrueToObject
|
||||
func (o *JSON) AddTrueToObject(name *c.Char) *JSON { return nil }
|
||||
|
||||
// CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
|
||||
//
|
||||
// llgo:link (*JSON).AddFalseToObject C.cJSON_AddFalseToObject
|
||||
func (o *JSON) AddFalseToObject(name *c.Char) *JSON { return nil }
|
||||
|
||||
// CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_Bool boolean);
|
||||
//
|
||||
// llgo:link (*JSON).AddBoolToObject C.cJSON_AddBoolToObject
|
||||
func (o *JSON) AddBoolToObject(name *c.Char, b Bool) *JSON { return nil }
|
||||
|
||||
// CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
|
||||
//
|
||||
// llgo:link (*JSON).AddNumberToObject C.cJSON_AddNumberToObject
|
||||
func (o *JSON) AddNumberToObject(name *c.Char, num c.Double) *JSON { return nil }
|
||||
|
||||
// CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
|
||||
//
|
||||
// llgo:link (*JSON).AddStringToObject C.cJSON_AddStringToObject
|
||||
func (o *JSON) AddStringToObject(name *c.Char, s *c.Char) *JSON { return nil }
|
||||
|
||||
// CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
|
||||
//
|
||||
// llgo:link (*JSON).AddRawToObject C.cJSON_AddRawToObject
|
||||
func (o *JSON) AddRawToObject(name *c.Char, raw *c.Char) *JSON { return nil }
|
||||
|
||||
// CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
|
||||
//
|
||||
// llgo:link (*JSON).AddObjectToObject C.cJSON_AddObjectToObject
|
||||
func (o *JSON) AddObjectToObject(name *c.Char) *JSON { return nil }
|
||||
|
||||
// CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
|
||||
//
|
||||
// llgo:link (*JSON).AddArrayToObject C.cJSON_AddArrayToObject
|
||||
func (o *JSON) AddArrayToObject(name *c.Char) *JSON { return nil }
|
||||
|
||||
// CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
|
||||
//
|
||||
// helper for the cJSON_SetNumberValue macro
|
||||
//
|
||||
// llgo:link (*JSON).SetNumberHelper C.cJSON_SetNumberHelper
|
||||
func (o *JSON) SetNumberHelper(number c.Double) c.Double { return 0 }
|
||||
|
||||
// CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
|
||||
//
|
||||
// Change the valuestring of a cJSON_String object, only takes effect when type of
|
||||
// object is cJSON_String
|
||||
//
|
||||
// llgo:link (*JSON).SetValuestring C.cJSON_SetValuestring
|
||||
func (o *JSON) SetValuestring(v *c.Char) *c.Char { return nil }
|
||||
|
||||
// CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
|
||||
//
|
||||
// malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks
|
||||
//
|
||||
//go:linkname Malloc C.cJSON_malloc
|
||||
func Malloc(size uintptr)
|
||||
|
||||
// CJSON_PUBLIC(void) cJSON_free(void *object);
|
||||
//
|
||||
//go:linkname Free C.cJSON_free
|
||||
func Free(ptr unsafe.Pointer)
|
||||
|
||||
//go:linkname FreeCStr C.cJSON_free
|
||||
func FreeCStr(*c.Char)
|
||||
|
||||
@@ -109,6 +109,8 @@ enum CX_StorageClass wrap_clang_Cursor_getStorageClass(CXCursor *cursor) {
|
||||
return clang_Cursor_getStorageClass(*cursor);
|
||||
}
|
||||
|
||||
CXString wrap_clang_getCursorUSR(CXCursor *cur) { return clang_getCursorUSR(*cur); }
|
||||
|
||||
CXString wrap_clang_getCursorSpelling(CXCursor *cur) { return clang_getCursorSpelling(*cur); }
|
||||
|
||||
unsigned wrap_clang_Cursor_isVariadic(CXCursor *cur) { return clang_Cursor_isVariadic(*cur); }
|
||||
@@ -181,4 +183,8 @@ void wrap_clang_getSpellingLocation(CXSourceLocation *loc, CXFile *file, unsigne
|
||||
clang_getSpellingLocation(*loc, file, line, column, offset);
|
||||
}
|
||||
|
||||
void wrap_clang_getRangeStart(CXSourceRange *range, CXSourceLocation *loc) { *loc = clang_getRangeStart(*range); }
|
||||
|
||||
void wrap_clang_getRangeEnd(CXSourceRange *range, CXSourceLocation *loc) { *loc = clang_getRangeEnd(*range); }
|
||||
|
||||
} // extern "C"
|
||||
|
||||
@@ -2166,6 +2166,24 @@ func (c Cursor) StorageClass() (ret StorageClass) {
|
||||
return c.wrapStorageClass()
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a Unified Symbol Resolution (USR) for the entity referenced
|
||||
* by the given cursor.
|
||||
*
|
||||
* A Unified Symbol Resolution (USR) is a string that identifies a particular
|
||||
* entity (function, class, variable, etc.) within a program. USRs can be
|
||||
* compared across translation units to determine, e.g., when references in
|
||||
* one translation refer to an entity defined in another translation unit.
|
||||
*/
|
||||
// llgo:link (*Cursor).wrapUSR C.wrap_clang_getCursorUSR
|
||||
func (*Cursor) wrapUSR() (ret String) {
|
||||
return
|
||||
}
|
||||
|
||||
func (c Cursor) USR() (ret String) {
|
||||
return c.wrapUSR()
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a name for the entity referenced by this cursor.
|
||||
*/
|
||||
@@ -2637,5 +2655,29 @@ func (l SourceLocation) SpellingLocation(file *File, line, column, offset *c.Uin
|
||||
l.wrapSpellingLocation(file, line, column, offset)
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a source location representing the first character within a
|
||||
* source range.
|
||||
*/
|
||||
// llgo:link (*SourceRange).wrapRangeStart C.wrap_clang_getRangeStart
|
||||
func (r *SourceRange) wrapRangeStart(loc *SourceLocation) { return }
|
||||
|
||||
func (r SourceRange) RangeStart() (loc SourceLocation) {
|
||||
r.wrapRangeStart(&loc)
|
||||
return
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a source location representing the last character within a
|
||||
* source range.
|
||||
*/
|
||||
// llgo:link (*SourceRange).wrapRangeEnd C.wrap_clang_getRangeEnd
|
||||
func (r *SourceRange) wrapRangeEnd(loc *SourceLocation) { return }
|
||||
|
||||
func (r SourceRange) RangeEnd() (loc SourceLocation) {
|
||||
r.wrapRangeEnd(&loc)
|
||||
return
|
||||
}
|
||||
|
||||
//llgo:link File.FileName C.clang_getFileName
|
||||
func (File) FileName() (ret String) { return }
|
||||
|
||||
44
c/lua/_demo/thread/thread.go
Normal file
44
c/lua/_demo/thread/thread.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/goplus/llgo/c"
|
||||
"github.com/goplus/llgo/c/lua"
|
||||
)
|
||||
|
||||
func pushThread(state *lua.State, name string) {
|
||||
isMain := state.Pushthread()
|
||||
if isMain != 0 {
|
||||
c.Printf(c.Str("%s Thread is main\n"), c.AllocaCStr(name))
|
||||
} else {
|
||||
c.Printf(c.Str("%s Thread is not main\n"), c.AllocaCStr(name))
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
L := lua.Newstate()
|
||||
defer L.Close()
|
||||
|
||||
L.Openlibs()
|
||||
pushThread(L, "main")
|
||||
L.Pop(1)
|
||||
newThread := L.Newthread()
|
||||
pushThread(newThread, "newthread")
|
||||
|
||||
state := newThread.Tothread(-1)
|
||||
if newThread == state {
|
||||
c.Printf(c.Str("Successfully retrieved thread from stack\n"))
|
||||
}
|
||||
status := state.Status()
|
||||
c.Printf(c.Str("New thread status: %d"), status)
|
||||
|
||||
if L.Closethread(newThread) != lua.OK {
|
||||
println("Failed to close thread status %d", state.Status())
|
||||
}
|
||||
}
|
||||
|
||||
/* Expected output:
|
||||
main Thread is main
|
||||
newthread Thread is not main
|
||||
Successfully retrieved thread from stack
|
||||
New thread status: 0
|
||||
*/
|
||||
17
c/lua/lua.go
17
c/lua/lua.go
@@ -159,13 +159,17 @@ func (L *State) Close() {}
|
||||
// llgo:link (*State).Newthread C.lua_newthread
|
||||
func (L *State) Newthread() *State { return nil }
|
||||
|
||||
// int (lua_closethread) (State *L, State *from);
|
||||
// int (lua_resetthread) (State *L); /* Deprecated! */
|
||||
// llgo:link (*State).Closethread C.lua_closethread
|
||||
func (L *State) Closethread(from *State) c.Int { return 0 }
|
||||
|
||||
// llgo:link (*State).Resetthread C.lua_resetthread
|
||||
func (L *State) Resetthread(from *State) c.Int { return 0 }
|
||||
|
||||
// llgo:link (*State).Atpanic C.lua_atpanic
|
||||
func (L *State) Atpanic(panicf CFunction) CFunction { return nil }
|
||||
|
||||
// lua_Number (lua_version) (State *L);
|
||||
// llgo:link (*State).Version C.lua_version
|
||||
func (L *State) Version() Number { return 0 }
|
||||
|
||||
// /*
|
||||
// ** basic stack manipulation
|
||||
@@ -240,7 +244,9 @@ func (L *State) Tocfunction(idx c.Int) CFunction { return nil }
|
||||
// llgo:link (*State).Touserdata C.lua_touserdata
|
||||
func (L *State) Touserdata(idx c.Int) c.Pointer { return nil }
|
||||
|
||||
// LUA_API State *(lua_tothread) (State *L, int idx);
|
||||
// llgo:link (*State).Tothread C.lua_tothread
|
||||
func (L *State) Tothread(idx c.Int) *State { return nil }
|
||||
|
||||
// LUA_API const void *(lua_topointer) (State *L, int idx);
|
||||
|
||||
// /*
|
||||
@@ -277,7 +283,8 @@ func (L *State) Pushboolean(b c.Int) {}
|
||||
// llgo:link (*State).Pushlightuserdata C.lua_pushlightuserdata
|
||||
func (L *State) Pushlightuserdata(p c.Pointer) {}
|
||||
|
||||
//int (lua_pushthread) (State *L);
|
||||
// llgo:link (*State).Pushthread C.lua_pushthread
|
||||
func (L *State) Pushthread() c.Int { return 0 }
|
||||
|
||||
// /*
|
||||
// ** get functions (Lua -> stack)
|
||||
|
||||
@@ -61,7 +61,9 @@ func printType(t clang.Type, data *Data) {
|
||||
case clang.TypeIncompleteArray, clang.TypeVariableArray, clang.TypeDependentSizedArray, clang.TypeConstantArray:
|
||||
printType(t.ArrayElementType(), data)
|
||||
case clang.TypeTypedef:
|
||||
printType(t.CanonicalType(), data)
|
||||
printType(t.TypeDeclaration().TypedefDeclUnderlyingType(), data)
|
||||
case clang.TypeElaborated:
|
||||
printType(t.NamedType(), data)
|
||||
case clang.TypeFunctionProto:
|
||||
printType(t.ResultType(), data)
|
||||
for i := 0; i < int(t.NumArgTypes()); i++ {
|
||||
|
||||
@@ -26,6 +26,7 @@ func GetConf(data []byte) (Conf, error) {
|
||||
Libs: GetStringItem(parsedConf, "libs", ""),
|
||||
Include: GetStringArrayItem(parsedConf, "include"),
|
||||
TrimPrefixes: GetStringArrayItem(parsedConf, "trimPrefixes"),
|
||||
Cplusplus: GetBoolItem(parsedConf, "cplusplus"),
|
||||
}
|
||||
|
||||
return Conf{
|
||||
@@ -58,3 +59,14 @@ func GetStringArrayItem(obj *cjson.JSON, key string) (value []string) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetBoolItem(obj *cjson.JSON, key string) bool {
|
||||
item := obj.GetObjectItemCaseSensitive(c.AllocaCStr(key))
|
||||
if item == nil {
|
||||
return false
|
||||
}
|
||||
if item.IsBool() != 0 {
|
||||
return item.IsTrue() != 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -18,7 +18,8 @@ If `config-file` is not specified, a `llcppg.cfg` file is used in current direct
|
||||
"AnotherHeaderFile.h"
|
||||
],
|
||||
"libs": "$(pkg-config --libs inireader)",
|
||||
"trimPrefixes": ["Ini", "INI"]
|
||||
"trimPrefixes": ["Ini", "INI"],
|
||||
"cplusplus":true
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ type Config struct {
|
||||
Libs string `json:"libs"`
|
||||
Include []string `json:"include"`
|
||||
TrimPrefixes []string `json:"trimPrefixes"`
|
||||
Cplusplus bool `json:"cplusplus"`
|
||||
}
|
||||
|
||||
type SymbolInfo struct {
|
||||
|
||||
7
cl/_testdata/llgotag/in.go
Normal file
7
cl/_testdata/llgotag/in.go
Normal file
@@ -0,0 +1,7 @@
|
||||
//go:build llgo
|
||||
// +build llgo
|
||||
|
||||
package llgotag
|
||||
|
||||
func Foo() {
|
||||
}
|
||||
22
cl/_testdata/llgotag/out.ll
Normal file
22
cl/_testdata/llgotag/out.ll
Normal file
@@ -0,0 +1,22 @@
|
||||
; ModuleID = 'llgotag'
|
||||
source_filename = "llgotag"
|
||||
|
||||
@"llgotag.init$guard" = global i1 false, align 1
|
||||
|
||||
define void @llgotag.Foo() {
|
||||
_llgo_0:
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @llgotag.init() {
|
||||
_llgo_0:
|
||||
%0 = load i1, ptr @"llgotag.init$guard", align 1
|
||||
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
store i1 true, ptr @"llgotag.init$guard", align 1
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
26
cl/_testgo/tpnamed/in.go
Normal file
26
cl/_testgo/tpnamed/in.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
type Void = [0]byte
|
||||
type Future[T any] func() T
|
||||
|
||||
type IO[T any] func() Future[T]
|
||||
|
||||
func WriteFile(fileName string) IO[error] {
|
||||
return func() Future[error] {
|
||||
return func() error {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func RunIO[T any](call IO[T]) T {
|
||||
return call()()
|
||||
}
|
||||
|
||||
func main() {
|
||||
RunIO[Void](func() Future[Void] {
|
||||
return func() (ret Void) {
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
122
cl/_testgo/tpnamed/out.ll
Normal file
122
cl/_testgo/tpnamed/out.ll
Normal file
@@ -0,0 +1,122 @@
|
||||
; ModuleID = 'main'
|
||||
source_filename = "main"
|
||||
|
||||
%"main.IO[error]" = type { ptr, ptr }
|
||||
%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 }
|
||||
%"main.Future[error]" = type { ptr, ptr }
|
||||
%"github.com/goplus/llgo/internal/runtime.iface" = type { ptr, ptr }
|
||||
%"main.IO[[0]byte]" = type { ptr, ptr }
|
||||
%"main.Future[[0]byte]" = type { ptr, ptr }
|
||||
|
||||
@"main.init$guard" = global i1 false, align 1
|
||||
@__llgo_argc = global i32 0, align 4
|
||||
@__llgo_argv = global ptr null, align 8
|
||||
|
||||
define %"main.IO[error]" @main.WriteFile(%"github.com/goplus/llgo/internal/runtime.String" %0) {
|
||||
_llgo_0:
|
||||
%1 = alloca %"main.IO[error]", align 8
|
||||
%2 = getelementptr inbounds %"main.IO[error]", ptr %1, i32 0, i32 0
|
||||
store ptr @"__llgo_stub.main.WriteFile$1", ptr %2, align 8
|
||||
%3 = getelementptr inbounds %"main.IO[error]", ptr %1, i32 0, i32 1
|
||||
store ptr null, ptr %3, align 8
|
||||
%4 = load %"main.IO[error]", ptr %1, align 8
|
||||
ret %"main.IO[error]" %4
|
||||
}
|
||||
|
||||
define %"main.Future[error]" @"main.WriteFile$1"() {
|
||||
_llgo_0:
|
||||
%0 = alloca %"main.Future[error]", align 8
|
||||
%1 = getelementptr inbounds %"main.Future[error]", ptr %0, i32 0, i32 0
|
||||
store ptr @"__llgo_stub.main.WriteFile$1$1", ptr %1, align 8
|
||||
%2 = getelementptr inbounds %"main.Future[error]", ptr %0, i32 0, i32 1
|
||||
store ptr null, ptr %2, align 8
|
||||
%3 = load %"main.Future[error]", ptr %0, align 8
|
||||
ret %"main.Future[error]" %3
|
||||
}
|
||||
|
||||
define %"github.com/goplus/llgo/internal/runtime.iface" @"main.WriteFile$1$1"() {
|
||||
_llgo_0:
|
||||
ret %"github.com/goplus/llgo/internal/runtime.iface" zeroinitializer
|
||||
}
|
||||
|
||||
define void @main.init() {
|
||||
_llgo_0:
|
||||
%0 = load i1, ptr @"main.init$guard", align 1
|
||||
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
store i1 true, ptr @"main.init$guard", align 1
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
define i32 @main(i32 %0, ptr %1) {
|
||||
_llgo_0:
|
||||
store i32 %0, ptr @__llgo_argc, align 4
|
||||
store ptr %1, ptr @__llgo_argv, align 8
|
||||
call void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||
call void @main.init()
|
||||
%2 = alloca %"main.IO[[0]byte]", align 8
|
||||
%3 = getelementptr inbounds %"main.IO[[0]byte]", ptr %2, i32 0, i32 0
|
||||
store ptr @"__llgo_stub.main.main$1", ptr %3, align 8
|
||||
%4 = getelementptr inbounds %"main.IO[[0]byte]", ptr %2, i32 0, i32 1
|
||||
store ptr null, ptr %4, align 8
|
||||
%5 = load %"main.IO[[0]byte]", ptr %2, align 8
|
||||
%6 = call [0 x i8] @"main.RunIO[[0]byte]"(%"main.IO[[0]byte]" %5)
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
define %"main.Future[[0]byte]" @"main.main$1"() {
|
||||
_llgo_0:
|
||||
%0 = alloca %"main.Future[[0]byte]", align 8
|
||||
%1 = getelementptr inbounds %"main.Future[[0]byte]", ptr %0, i32 0, i32 0
|
||||
store ptr @"__llgo_stub.main.main$1$1", ptr %1, align 8
|
||||
%2 = getelementptr inbounds %"main.Future[[0]byte]", ptr %0, i32 0, i32 1
|
||||
store ptr null, ptr %2, align 8
|
||||
%3 = load %"main.Future[[0]byte]", ptr %0, align 8
|
||||
ret %"main.Future[[0]byte]" %3
|
||||
}
|
||||
|
||||
define [0 x i8] @"main.main$1$1"() {
|
||||
_llgo_0:
|
||||
ret [0 x i8] zeroinitializer
|
||||
}
|
||||
|
||||
define linkonce %"main.Future[error]" @"__llgo_stub.main.WriteFile$1"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = tail call %"main.Future[error]" @"main.WriteFile$1"()
|
||||
ret %"main.Future[error]" %1
|
||||
}
|
||||
|
||||
define linkonce %"github.com/goplus/llgo/internal/runtime.iface" @"__llgo_stub.main.WriteFile$1$1"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = tail call %"github.com/goplus/llgo/internal/runtime.iface" @"main.WriteFile$1$1"()
|
||||
ret %"github.com/goplus/llgo/internal/runtime.iface" %1
|
||||
}
|
||||
|
||||
declare void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||
|
||||
define linkonce %"main.Future[[0]byte]" @"__llgo_stub.main.main$1"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = tail call %"main.Future[[0]byte]" @"main.main$1"()
|
||||
ret %"main.Future[[0]byte]" %1
|
||||
}
|
||||
|
||||
define linkonce [0 x i8] @"main.RunIO[[0]byte]"(%"main.IO[[0]byte]" %0) {
|
||||
_llgo_0:
|
||||
%1 = extractvalue %"main.IO[[0]byte]" %0, 1
|
||||
%2 = extractvalue %"main.IO[[0]byte]" %0, 0
|
||||
%3 = call %"main.Future[[0]byte]" %2(ptr %1)
|
||||
%4 = extractvalue %"main.Future[[0]byte]" %3, 1
|
||||
%5 = extractvalue %"main.Future[[0]byte]" %3, 0
|
||||
%6 = call [0 x i8] %5(ptr %4)
|
||||
ret [0 x i8] %6
|
||||
}
|
||||
|
||||
define linkonce [0 x i8] @"__llgo_stub.main.main$1$1"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = tail call [0 x i8] @"main.main$1$1"()
|
||||
ret [0 x i8] %1
|
||||
}
|
||||
14
cl/_testrt/freevars/in.go
Normal file
14
cl/_testrt/freevars/in.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
func(resolve func(error)) {
|
||||
func(err error) {
|
||||
if err != nil {
|
||||
resolve(err)
|
||||
return
|
||||
}
|
||||
resolve(nil)
|
||||
}(nil)
|
||||
}(func(err error) {
|
||||
})
|
||||
}
|
||||
118
cl/_testrt/freevars/out.ll
Normal file
118
cl/_testrt/freevars/out.ll
Normal file
@@ -0,0 +1,118 @@
|
||||
; ModuleID = 'main'
|
||||
source_filename = "main"
|
||||
|
||||
%"github.com/goplus/llgo/internal/runtime.iface" = type { ptr, ptr }
|
||||
%"github.com/goplus/llgo/internal/runtime.eface" = type { ptr, ptr }
|
||||
|
||||
@"main.init$guard" = global i1 false, align 1
|
||||
@__llgo_argc = global i32 0, align 4
|
||||
@__llgo_argv = global ptr null, align 8
|
||||
|
||||
define void @main.init() {
|
||||
_llgo_0:
|
||||
%0 = load i1, ptr @"main.init$guard", align 1
|
||||
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
store i1 true, ptr @"main.init$guard", align 1
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
define i32 @main(i32 %0, ptr %1) {
|
||||
_llgo_0:
|
||||
store i32 %0, ptr @__llgo_argc, align 4
|
||||
store ptr %1, ptr @__llgo_argv, align 8
|
||||
call void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||
call void @main.init()
|
||||
%2 = alloca { ptr, ptr }, align 8
|
||||
%3 = getelementptr inbounds { ptr, ptr }, ptr %2, i32 0, i32 0
|
||||
store ptr @"__llgo_stub.main.main$2", ptr %3, align 8
|
||||
%4 = getelementptr inbounds { ptr, ptr }, ptr %2, i32 0, i32 1
|
||||
store ptr null, ptr %4, align 8
|
||||
%5 = load { ptr, ptr }, ptr %2, align 8
|
||||
call void @"main.main$1"({ ptr, ptr } %5)
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
define void @"main.main$1"({ ptr, ptr } %0) {
|
||||
_llgo_0:
|
||||
%1 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 16)
|
||||
store { ptr, ptr } %0, ptr %1, align 8
|
||||
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 8)
|
||||
%3 = getelementptr inbounds { ptr }, ptr %2, i32 0, i32 0
|
||||
store ptr %1, ptr %3, align 8
|
||||
%4 = alloca { ptr, ptr }, align 8
|
||||
%5 = getelementptr inbounds { ptr, ptr }, ptr %4, i32 0, i32 0
|
||||
store ptr @"main.main$1$1", ptr %5, align 8
|
||||
%6 = getelementptr inbounds { ptr, ptr }, ptr %4, i32 0, i32 1
|
||||
store ptr %2, ptr %6, align 8
|
||||
%7 = load { ptr, ptr }, ptr %4, align 8
|
||||
%8 = extractvalue { ptr, ptr } %7, 1
|
||||
%9 = extractvalue { ptr, ptr } %7, 0
|
||||
call void %9(ptr %8, %"github.com/goplus/llgo/internal/runtime.iface" zeroinitializer)
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @"main.main$1$1"(ptr %0, %"github.com/goplus/llgo/internal/runtime.iface" %1) {
|
||||
_llgo_0:
|
||||
%2 = call ptr @"github.com/goplus/llgo/internal/runtime.IfaceType"(%"github.com/goplus/llgo/internal/runtime.iface" %1)
|
||||
%3 = extractvalue %"github.com/goplus/llgo/internal/runtime.iface" %1, 1
|
||||
%4 = alloca %"github.com/goplus/llgo/internal/runtime.eface", align 8
|
||||
%5 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.eface", ptr %4, i32 0, i32 0
|
||||
store ptr %2, ptr %5, align 8
|
||||
%6 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.eface", ptr %4, i32 0, i32 1
|
||||
store ptr %3, ptr %6, align 8
|
||||
%7 = load %"github.com/goplus/llgo/internal/runtime.eface", ptr %4, align 8
|
||||
%8 = call ptr @"github.com/goplus/llgo/internal/runtime.IfaceType"(%"github.com/goplus/llgo/internal/runtime.iface" zeroinitializer)
|
||||
%9 = alloca %"github.com/goplus/llgo/internal/runtime.eface", align 8
|
||||
%10 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.eface", ptr %9, i32 0, i32 0
|
||||
store ptr %8, ptr %10, align 8
|
||||
%11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.eface", ptr %9, i32 0, i32 1
|
||||
store ptr null, ptr %11, align 8
|
||||
%12 = load %"github.com/goplus/llgo/internal/runtime.eface", ptr %9, align 8
|
||||
%13 = call i1 @"github.com/goplus/llgo/internal/runtime.EfaceEqual"(%"github.com/goplus/llgo/internal/runtime.eface" %7, %"github.com/goplus/llgo/internal/runtime.eface" %12)
|
||||
%14 = xor i1 %13, true
|
||||
br i1 %14, label %_llgo_1, label %_llgo_2
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
%15 = load { ptr }, ptr %0, align 8
|
||||
%16 = extractvalue { ptr } %15, 0
|
||||
%17 = load { ptr, ptr }, ptr %16, align 8
|
||||
%18 = extractvalue { ptr, ptr } %17, 1
|
||||
%19 = extractvalue { ptr, ptr } %17, 0
|
||||
call void %19(ptr %18, %"github.com/goplus/llgo/internal/runtime.iface" %1)
|
||||
ret void
|
||||
|
||||
_llgo_2: ; preds = %_llgo_0
|
||||
%20 = load { ptr }, ptr %0, align 8
|
||||
%21 = extractvalue { ptr } %20, 0
|
||||
%22 = load { ptr, ptr }, ptr %21, align 8
|
||||
%23 = extractvalue { ptr, ptr } %22, 1
|
||||
%24 = extractvalue { ptr, ptr } %22, 0
|
||||
call void %24(ptr %23, %"github.com/goplus/llgo/internal/runtime.iface" zeroinitializer)
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @"main.main$2"(%"github.com/goplus/llgo/internal/runtime.iface" %0) {
|
||||
_llgo_0:
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||
|
||||
define linkonce void @"__llgo_stub.main.main$2"(ptr %0, %"github.com/goplus/llgo/internal/runtime.iface" %1) {
|
||||
_llgo_0:
|
||||
tail call void @"main.main$2"(%"github.com/goplus/llgo/internal/runtime.iface" %1)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64)
|
||||
|
||||
declare ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64)
|
||||
|
||||
declare i1 @"github.com/goplus/llgo/internal/runtime.EfaceEqual"(%"github.com/goplus/llgo/internal/runtime.eface", %"github.com/goplus/llgo/internal/runtime.eface")
|
||||
|
||||
declare ptr @"github.com/goplus/llgo/internal/runtime.IfaceType"(%"github.com/goplus/llgo/internal/runtime.iface")
|
||||
26
cl/_testrt/tpfunc/in.go
Normal file
26
cl/_testrt/tpfunc/in.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type Func func(*int)
|
||||
|
||||
//llgo:type C
|
||||
type CFunc func(*int)
|
||||
|
||||
//llgo:type C
|
||||
type Callback[T any] func(*T)
|
||||
|
||||
func main() {
|
||||
var fn1 Func = func(v *int) {
|
||||
println(*v)
|
||||
}
|
||||
var fn2 CFunc = func(v *int) {
|
||||
println(*v)
|
||||
}
|
||||
var fn3 Callback[int] = func(v *int) {
|
||||
println(*v)
|
||||
}
|
||||
println(unsafe.Sizeof(fn1), unsafe.Sizeof(fn2), unsafe.Sizeof(fn3))
|
||||
}
|
||||
80
cl/_testrt/tpfunc/out.ll
Normal file
80
cl/_testrt/tpfunc/out.ll
Normal file
@@ -0,0 +1,80 @@
|
||||
; ModuleID = 'main'
|
||||
source_filename = "main"
|
||||
|
||||
%main.Func = type { ptr, ptr }
|
||||
|
||||
@"main.init$guard" = global i1 false, align 1
|
||||
@__llgo_argc = global i32 0, align 4
|
||||
@__llgo_argv = global ptr null, align 8
|
||||
|
||||
define void @main.init() {
|
||||
_llgo_0:
|
||||
%0 = load i1, ptr @"main.init$guard", align 1
|
||||
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
store i1 true, ptr @"main.init$guard", align 1
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
define i32 @main(i32 %0, ptr %1) {
|
||||
_llgo_0:
|
||||
store i32 %0, ptr @__llgo_argc, align 4
|
||||
store ptr %1, ptr @__llgo_argv, align 8
|
||||
call void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||
call void @main.init()
|
||||
%2 = alloca %main.Func, align 8
|
||||
%3 = getelementptr inbounds %main.Func, ptr %2, i32 0, i32 0
|
||||
store ptr @"__llgo_stub.main.main$1", ptr %3, align 8
|
||||
%4 = getelementptr inbounds %main.Func, ptr %2, i32 0, i32 1
|
||||
store ptr null, ptr %4, align 8
|
||||
%5 = load %main.Func, ptr %2, align 8
|
||||
call void @"github.com/goplus/llgo/internal/runtime.PrintUint"(i64 16)
|
||||
call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32)
|
||||
call void @"github.com/goplus/llgo/internal/runtime.PrintUint"(i64 8)
|
||||
call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32)
|
||||
call void @"github.com/goplus/llgo/internal/runtime.PrintUint"(i64 8)
|
||||
call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10)
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
define void @"main.main$1"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = load i64, ptr %0, align 4
|
||||
call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %1)
|
||||
call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10)
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @"main.main$2"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = load i64, ptr %0, align 4
|
||||
call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %1)
|
||||
call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10)
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @"main.main$3"(ptr %0) {
|
||||
_llgo_0:
|
||||
%1 = load i64, ptr %0, align 4
|
||||
call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %1)
|
||||
call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @"github.com/goplus/llgo/internal/runtime.init"()
|
||||
|
||||
define linkonce void @"__llgo_stub.main.main$1"(ptr %0, ptr %1) {
|
||||
_llgo_0:
|
||||
tail call void @"main.main$1"(ptr %1)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @"github.com/goplus/llgo/internal/runtime.PrintUint"(i64)
|
||||
|
||||
declare void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8)
|
||||
|
||||
declare void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64)
|
||||
@@ -554,7 +554,7 @@ func addChecked(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer {
|
||||
func splitName(s string) (pkg string, name string) {
|
||||
i := lastDot(s)
|
||||
if i == -1 {
|
||||
return s, ""
|
||||
return "", s
|
||||
}
|
||||
return s[:i], s[i+1:]
|
||||
}
|
||||
|
||||
@@ -121,6 +121,7 @@ const (
|
||||
|
||||
func Do(args []string, conf *Config) {
|
||||
flags, patterns, verbose := ParseArgs(args, buildFlags)
|
||||
flags = append(flags, "-tags", "llgo")
|
||||
cfg := &packages.Config{
|
||||
Mode: loadSyntax | packages.NeedDeps | packages.NeedModule | packages.NeedExportFile,
|
||||
BuildFlags: flags,
|
||||
@@ -259,6 +260,9 @@ func buildAllPkgs(ctx *context, initial []*packages.Package, verbose bool) (pkgs
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "cannot build SSA for package", errPkg)
|
||||
}
|
||||
if len(errPkgs) > 0 {
|
||||
os.Exit(1)
|
||||
}
|
||||
built := ctx.built
|
||||
for _, aPkg := range pkgs {
|
||||
pkg := aPkg.Package
|
||||
|
||||
@@ -171,6 +171,7 @@ type aFunction struct {
|
||||
|
||||
params []Type
|
||||
freeVars Expr
|
||||
freeVarsBlock int
|
||||
base int // base = 1 if hasFreeVars; base = 0 otherwise
|
||||
hasVArg bool
|
||||
}
|
||||
@@ -249,12 +250,13 @@ func (p Function) Param(i int) Expr {
|
||||
}
|
||||
|
||||
func (p Function) closureCtx(b Builder) Expr {
|
||||
if p.freeVars.IsNil() {
|
||||
if p.freeVars.IsNil() || (p.freeVarsBlock != 0 && p.freeVarsBlock != b.blk.Index()) {
|
||||
if p.base == 0 {
|
||||
panic("ssa: function has no free variables")
|
||||
}
|
||||
ptr := Expr{p.impl.Param(0), p.params[0]}
|
||||
p.freeVars = b.Load(ptr)
|
||||
p.freeVarsBlock = b.blk.Index()
|
||||
}
|
||||
return p.freeVars
|
||||
}
|
||||
|
||||
@@ -244,7 +244,7 @@ func (p Program) SetRuntime(runtime any) {
|
||||
}
|
||||
|
||||
func (p Program) SetTypeBackground(fullName string, bg Background) {
|
||||
p.gocvt.typbg[fullName] = bg
|
||||
p.gocvt.typbg.Store(fullName, bg)
|
||||
}
|
||||
|
||||
func (p Program) SetLinkname(name, link string) {
|
||||
|
||||
@@ -125,7 +125,7 @@ func (p *goProgram) extraSize(typ types.Type, ptrSize int64) (ret int64) {
|
||||
retry:
|
||||
switch t := typ.(type) {
|
||||
case *types.Named:
|
||||
if p.gocvt.typbg[t.String()] == InC {
|
||||
if v, ok := p.gocvt.typbg.Load(namedLinkname(t)); ok && v.(Background) == InC {
|
||||
return 0
|
||||
}
|
||||
typ = t.Underlying()
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -27,13 +28,12 @@ import (
|
||||
|
||||
type goTypes struct {
|
||||
typs map[unsafe.Pointer]unsafe.Pointer
|
||||
typbg map[string]Background
|
||||
typbg sync.Map
|
||||
}
|
||||
|
||||
func newGoTypes() goTypes {
|
||||
typs := make(map[unsafe.Pointer]unsafe.Pointer)
|
||||
typbk := make(map[string]Background)
|
||||
return goTypes{typs, typbk}
|
||||
return goTypes{typs: typs}
|
||||
}
|
||||
|
||||
type Background int
|
||||
@@ -95,7 +95,7 @@ func (p goTypes) cvtType(typ types.Type) (raw types.Type, cvt bool) {
|
||||
case *types.Struct:
|
||||
return p.cvtStruct(t)
|
||||
case *types.Named:
|
||||
if p.typbg[t.String()] == InC {
|
||||
if v, ok := p.typbg.Load(namedLinkname(t)); ok && v.(Background) == InC {
|
||||
break
|
||||
}
|
||||
return p.cvtNamed(t)
|
||||
@@ -115,6 +115,14 @@ func (p goTypes) cvtType(typ types.Type) (raw types.Type, cvt bool) {
|
||||
return typ, false
|
||||
}
|
||||
|
||||
func namedLinkname(t *types.Named) string {
|
||||
obj := t.Obj()
|
||||
if obj.Pkg() != nil {
|
||||
return obj.Pkg().Path() + "." + obj.Name()
|
||||
}
|
||||
return obj.Name()
|
||||
}
|
||||
|
||||
func (p goTypes) cvtNamed(t *types.Named) (raw *types.Named, cvt bool) {
|
||||
if v, ok := p.typs[unsafe.Pointer(t)]; ok {
|
||||
raw = (*types.Named)(v)
|
||||
@@ -128,9 +136,29 @@ func (p goTypes) cvtNamed(t *types.Named) (raw *types.Named, cvt bool) {
|
||||
methods[i] = m
|
||||
}
|
||||
named := types.NewNamed(t.Obj(), types.Typ[types.Int], methods)
|
||||
if tp := t.TypeParams(); tp != nil {
|
||||
list := make([]*types.TypeParam, tp.Len())
|
||||
for i := 0; i < tp.Len(); i++ {
|
||||
param := tp.At(i)
|
||||
list[i] = types.NewTypeParam(param.Obj(), param.Constraint())
|
||||
}
|
||||
named.SetTypeParams(list)
|
||||
}
|
||||
p.typs[unsafe.Pointer(t)] = unsafe.Pointer(named)
|
||||
if tund, cvt := p.cvtType(t.Underlying()); cvt {
|
||||
named.SetUnderlying(tund)
|
||||
if tp := t.TypeArgs(); tp != nil {
|
||||
targs := make([]types.Type, tp.Len())
|
||||
for i := 0; i < tp.Len(); i++ {
|
||||
targs[i] = tp.At(i)
|
||||
}
|
||||
typ, err := types.Instantiate(nil, named, targs, true)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cvtNamed error: %v", err))
|
||||
}
|
||||
named = typ.(*types.Named)
|
||||
p.typs[unsafe.Pointer(t)] = unsafe.Pointer(named)
|
||||
}
|
||||
return named, true
|
||||
}
|
||||
p.typs[unsafe.Pointer(t)] = unsafe.Pointer(t)
|
||||
|
||||
@@ -361,104 +361,83 @@ In some situations, you may want to get the first result of multiple async opera
|
||||
|
||||
## Design considerations in LLGo
|
||||
|
||||
- Don't introduce `async`/`await` keywords to compatible with Go compiler (just compiling)
|
||||
- For performance reason don't implement async functions with goroutines
|
||||
- Avoid implementing `Promise` by using `chan` to avoid blocking the thread, but it can be wrapped as a `chan` to make it compatible `select` statement
|
||||
- Don't introduce `async`/`await` keywords to compatible with Go
|
||||
- For performance and memory reasons don't implement async functions with goroutines, coroutines, or other mechanisms that require per-task stack allocation
|
||||
- Avoid implementing async task by using `chan` that blocking the thread
|
||||
|
||||
## Design
|
||||
|
||||
Introduce `Promise` type to represent an asynchronous operation and its resulting value. `Promise` can be resolved with a value with an error. `Promise` can be awaited to get the value and error.
|
||||
### `async.Future[T]` type
|
||||
|
||||
`Promise` just a type indicating the asynchronous operation, it can't be created and assigned directly. It be replaced to `PromiseImpl` by the LLGo compiler.
|
||||
Introduce `async.Future[T]` type to represent an eventual completion (or failure) of an asynchronous operation and its resulting value, similar to `Promise`/`Future` in other languages. Functions that return `async.Future[T]` are considered asynchronous functions.
|
||||
|
||||
### Future creation
|
||||
|
||||
`async.Future[T]` can be created by `async.Async[T]` function that takes a function that accepts a `resolve` function to produce a value of type `T`.
|
||||
|
||||
### Future chaining (asynchronous callbacks style)
|
||||
|
||||
`async.Future[T]` can be chained with `Then` method to add multiple callbacks to be executed when the operation is completed, it just runs once and calls every callbacks. Currently `Then` method can't be chained multiple times because Go doesn't support generics method (Need support `func (f Future[T]) Then[U any](f func(T) Future[U]) Future[U]`), maybe implements in Go+.
|
||||
|
||||
### Future waiting (synchronous style)
|
||||
|
||||
`async.Await[T]` function can be used to wait for the completion of a `Future[T]` and return the value produced by the operation. In LLGo, `async.Await[T]` is a blocking function that waits for the completion of the `Future[T]` and returns the value synchronously, it would be transformed to `Future.Then` callback in the frontend.
|
||||
|
||||
### `async.Run[T]` function
|
||||
|
||||
`async.Run[T]` function can be used to create an global asynchronous context and run async functions, and it would be hidden by the compiler in the future.
|
||||
|
||||
Currently it will switch the callbacks to the goroutine that calls `async.Run[T]` function, this maybe changed in the future to reduce the overhead of switching goroutines and make it more parallel.
|
||||
|
||||
### Prototype
|
||||
|
||||
```go
|
||||
// Some native async functions
|
||||
func timeoutAsync(d time.Duration, cb func()) {
|
||||
go func() {
|
||||
time.Sleep(d)
|
||||
cb()
|
||||
}()
|
||||
package async
|
||||
|
||||
type Future[T any] interface {
|
||||
Then(f func(T))
|
||||
}
|
||||
|
||||
// Wrap callback-based async function into Promise
|
||||
func resolveAfter1Second() (resolve Promise[string]) {
|
||||
timeoutAsync(1 * time.Second, func() {
|
||||
resolve("Resolved after 1 second", nil)
|
||||
})
|
||||
}
|
||||
func Async[T any](f func(resolve func(T))) Future[T]
|
||||
|
||||
// Compiled to:
|
||||
func resolveAfter1Second() (resolve PromiseImpl[string]) {
|
||||
promise := io.NewPromiseImpl[string](resolve func(value string, err error) {
|
||||
resolve: func(value string, err error) {
|
||||
for true {
|
||||
switch (promise.prev = promise.next) {
|
||||
case 0:
|
||||
timeoutAsync(1 * time.Second, func() {
|
||||
resolve("Resolved after 1 second", nil)
|
||||
func Await[T any](future Future[T]) T
|
||||
```
|
||||
|
||||
### Some async functions
|
||||
|
||||
```go
|
||||
package async
|
||||
|
||||
|
||||
func Race[T1 any](futures ...Future[T1]) Future[T1]
|
||||
|
||||
func All[T1 any](futures ...Future[T1]) Future[[]T1]
|
||||
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
func main() {
|
||||
async.Run(func() {
|
||||
hello := func() async.Future[string] {
|
||||
return async.Async(func(resolve func(string)) {
|
||||
resolve("Hello, World!")
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
return promise
|
||||
}
|
||||
|
||||
func asyncCall() (resolve Promise[string]) {
|
||||
str, err := resolveAfter1Second().Await()
|
||||
resolve("AsyncCall: " + str, err)
|
||||
}
|
||||
|
||||
// Compiled to:
|
||||
func asyncCall() (resolve PromiseImpl[string]) {
|
||||
promise := io.NewPromiseImpl[string](resolve func(value string, err error) {
|
||||
for true {
|
||||
switch (promise.prev = promise.next) {
|
||||
case 0:
|
||||
resolveAfter1Second()
|
||||
return
|
||||
case 1:
|
||||
str, err := promise.value, promise.err
|
||||
resolve("AsyncCall: " + str, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
future := hello()
|
||||
future.Then(func(value string) {
|
||||
println("first callback:", value)
|
||||
})
|
||||
return promise
|
||||
}
|
||||
|
||||
// Directly return Promise
|
||||
func asyncCall2() Promise[string] {
|
||||
return resolveAfter1Second()
|
||||
}
|
||||
|
||||
// Compiled to:
|
||||
func asyncCall2() PromiseImpl[string] {
|
||||
return resolveAfter1Second()
|
||||
}
|
||||
|
||||
// Don't wait for Promise to complete
|
||||
func asyncCall3() {
|
||||
resolveAfter1Second().Then(func(result string) {
|
||||
fmt.Println("AsyncCall3: " + result)
|
||||
future.Then(func(value string) {
|
||||
println("second callback:", value)
|
||||
})
|
||||
}
|
||||
|
||||
func asyncMain() {
|
||||
fmt.Println("Starting AsyncCall")
|
||||
result1 := asyncCall().Await()
|
||||
fmt.Println(result1)
|
||||
|
||||
fmt.Println("Starting AsyncCall2")
|
||||
result2 := asyncCall2().Await()
|
||||
fmt.Println(result2)
|
||||
|
||||
fmt.Println("Starting AsyncCall3")
|
||||
asyncCall3()
|
||||
|
||||
// Wait for AsyncCall3 to complete
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
fmt.Println("Main function completed")
|
||||
println("first await:", async.Await(future))
|
||||
println("second await:", async.Await(future))
|
||||
})
|
||||
}
|
||||
```
|
||||
17
x/async/TODO.md
Normal file
17
x/async/TODO.md
Normal file
@@ -0,0 +1,17 @@
|
||||
讨论:
|
||||
|
||||
1. Future 用 interface 还是闭包:性能应该差不多,如果没有其他方法要暴露,感觉也没有换成 interface 的必要,interface 多一个对象分配。先添加 Then 方法方便未来替换。
|
||||
2. 几个方法提供不同参数个数的版本还是用 tuple:如果编译器不支持可变泛型参数个数和特化,我倾向用 tuple 先简化实现,tuple 的开销应该也容易被编译器优化掉。多个方法让用户选择 Await2/Await3 这种也恶心。
|
||||
3. 是否 Cancellable,暂时不加进去,多一个 context,也不一定能快速稳定下来,可以后面根据实践再改。
|
||||
4. Executor 可能会变化,目前提供的 Run 是阻塞的,也可以把它做成异步。
|
||||
5. 尽量再隐藏一些辅助类型,比如 TupleN,可能之提供 tuple 的构造和返回多值。内部的 libuv 如果隐藏可能要暴露同等接口,先不动了
|
||||
6. 性能可能做个简单测试,但不是关键,只要别太差。未来可能会尽量减少 executor 的切换、尽量多并行
|
||||
7. 异常兼容性:目前没考虑,这个要在回调里处理可能困难,要么就在 await 上处理,可以往后放一下,毕竟 golang 主要是以 error 为主
|
||||
8. 可能先看一下如何在 go+里面集成,判断目前的设计实现是否合理
|
||||
9. 多封装一些库看看通用性和易用性,\_demo 里几个简单例子基本符合预期,还需要更多检验
|
||||
|
||||
TODO:
|
||||
|
||||
[ ] 1. select 兼容 (可能把 Future 改为 interface 更合理?)
|
||||
[x] 2. Future 多个 Await 只会被执行一次
|
||||
[x] 3. Future 添加 Then 方法,不推荐直接当作函数调用,方便未来切换
|
||||
289
x/async/_demo/all/all.go
Normal file
289
x/async/_demo/all/all.go
Normal file
@@ -0,0 +1,289 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/goplus/llgo/x/async"
|
||||
"github.com/goplus/llgo/x/async/timeout"
|
||||
"github.com/goplus/llgo/x/socketio"
|
||||
"github.com/goplus/llgo/x/tuple"
|
||||
)
|
||||
|
||||
func ReadFile(fileName string) async.Future[tuple.Tuple2[[]byte, error]] {
|
||||
return async.Async(func(resolve func(tuple.Tuple2[[]byte, error])) {
|
||||
go func() {
|
||||
println("read file", fileName)
|
||||
bytes, err := os.ReadFile(fileName)
|
||||
resolve(tuple.T2(bytes, err))
|
||||
}()
|
||||
})
|
||||
}
|
||||
|
||||
func WriteFile(fileName string, content []byte) async.Future[error] {
|
||||
return async.Async(func(resolve func(error)) {
|
||||
go func() {
|
||||
err := os.WriteFile(fileName, content, 0644)
|
||||
resolve(err)
|
||||
}()
|
||||
})
|
||||
}
|
||||
|
||||
func sleep(i int, d time.Duration) async.Future[int] {
|
||||
return async.Async(func(resolve func(int)) {
|
||||
timeout.Timeout(d).Then(func(async.Void) {
|
||||
resolve(i)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func main() {
|
||||
async.Run(func(resolve func(async.Void)) {
|
||||
RunIO()
|
||||
RunAllAndRace()
|
||||
RunTimeout()
|
||||
RunMultipleCallbacksNodelay()
|
||||
RunMultipleCallbacksDelay()
|
||||
RunSocket()
|
||||
})
|
||||
}
|
||||
|
||||
func RunIO() {
|
||||
println("RunIO with Await")
|
||||
|
||||
// Hide `resolve` in Go+
|
||||
|
||||
println("read file")
|
||||
content, err := async.Await(ReadFile("all.go")).Get()
|
||||
if err != nil {
|
||||
fmt.Printf("read err: %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("read content: %s\n", content)
|
||||
err = async.Await(WriteFile("2.out", content))
|
||||
if err != nil {
|
||||
fmt.Printf("write err: %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("write done\n")
|
||||
|
||||
// Translated Await to BindIO in Go+:
|
||||
println("RunIO with BindIO")
|
||||
|
||||
ReadFile("all.go").Then(func(v tuple.Tuple2[[]byte, error]) {
|
||||
content, err := v.Get()
|
||||
if err != nil {
|
||||
fmt.Printf("read err: %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("read content: %s\n", content)
|
||||
WriteFile("2.out", content).Then(func(v error) {
|
||||
err = v
|
||||
if err != nil {
|
||||
fmt.Printf("write err: %v\n", err)
|
||||
return
|
||||
}
|
||||
println("write done")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func RunAllAndRace() {
|
||||
ms100 := 100 * time.Millisecond
|
||||
ms200 := 200 * time.Millisecond
|
||||
ms300 := 300 * time.Millisecond
|
||||
|
||||
println("Run All with Await")
|
||||
|
||||
async.All(sleep(1, ms200), sleep(2, ms100), sleep(3, ms300)).Then(func(v []int) {
|
||||
fmt.Printf("All: %v\n", v)
|
||||
})
|
||||
|
||||
println("Run Race with Await")
|
||||
|
||||
first := async.Race(sleep(1, ms200), sleep(2, ms100), sleep(3, ms300))
|
||||
v := async.Await(first)
|
||||
fmt.Printf("Race: %v\n", v)
|
||||
|
||||
// Translated to in Go+:
|
||||
|
||||
println("Run All with BindIO")
|
||||
|
||||
async.All(sleep(1, ms200), sleep(2, ms100), sleep(3, ms300)).Then(func(v []int) {
|
||||
fmt.Printf("All: %v\n", v)
|
||||
})
|
||||
|
||||
println("Run Race with BindIO")
|
||||
|
||||
async.Race(sleep(1, ms200), sleep(2, ms100), sleep(3, ms300)).Then(func(v int) {
|
||||
fmt.Printf("Race: %v\n", v)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func RunTimeout() {
|
||||
println("Run Timeout with Await")
|
||||
|
||||
fmt.Printf("Start 100 ms timeout\n")
|
||||
async.Await(timeout.Timeout(100 * time.Millisecond))
|
||||
fmt.Printf("timeout\n")
|
||||
|
||||
// Translated to in Go+:
|
||||
|
||||
println("Run Timeout with BindIO")
|
||||
|
||||
fmt.Printf("Start 100 ms timeout\n")
|
||||
timeout.Timeout(100 * time.Millisecond).Then(func(async.Void) {
|
||||
fmt.Printf("timeout\n")
|
||||
})
|
||||
}
|
||||
|
||||
func RunMultipleCallbacksNodelay() {
|
||||
println("Run Multiple Callbacks")
|
||||
|
||||
runCnt := atomic.Int32{}
|
||||
|
||||
nodelay := async.Async(func(resolve func(async.Void)) {
|
||||
println("nodelay")
|
||||
runCnt.Add(1)
|
||||
})
|
||||
|
||||
cbCnt := atomic.Int32{}
|
||||
cb := func() {
|
||||
if cbCnt.Add(1) == 2 {
|
||||
if runCnt.Load() != 1 {
|
||||
panic("runCnt != 1, got: " + fmt.Sprint(runCnt.Load()))
|
||||
} else {
|
||||
println("runCnt == 1")
|
||||
}
|
||||
}
|
||||
}
|
||||
nodelay.Then(func(async.Void) {
|
||||
println("nodelay done")
|
||||
cb()
|
||||
})
|
||||
|
||||
nodelay.Then(func(async.Void) {
|
||||
println("nodelay done again")
|
||||
cb()
|
||||
})
|
||||
}
|
||||
|
||||
func RunMultipleCallbacksDelay() {
|
||||
println("Run Multiple Callbacks")
|
||||
|
||||
runCnt := atomic.Int32{}
|
||||
|
||||
delay := async.Async(func(resolve func(async.Void)) {
|
||||
timeout.Timeout(100 * time.Millisecond).Then(func(async.Void) {
|
||||
println("delay")
|
||||
runCnt.Add(1)
|
||||
})
|
||||
})
|
||||
|
||||
cbCnt := atomic.Int32{}
|
||||
cb := func() {
|
||||
if cbCnt.Add(1) == 2 {
|
||||
if runCnt.Load() != 1 {
|
||||
panic("runCnt != 1, got: " + fmt.Sprint(runCnt.Load()))
|
||||
} else {
|
||||
println("runCnt == 1")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delay.Then(func(async.Void) {
|
||||
println("delay done")
|
||||
cb()
|
||||
})
|
||||
|
||||
delay.Then(func(async.Void) {
|
||||
println("delay done again")
|
||||
cb()
|
||||
})
|
||||
}
|
||||
|
||||
func RunSocket() {
|
||||
println("Run Socket")
|
||||
|
||||
println("RunServer")
|
||||
|
||||
RunServer().Then(func(async.Void) {
|
||||
println("RunServer done")
|
||||
})
|
||||
|
||||
println("RunClient")
|
||||
|
||||
timeout.Timeout(100 * time.Millisecond).Then(func(async.Void) {
|
||||
RunClient("Bob").Then(func(async.Void) {
|
||||
println("RunClient done")
|
||||
})
|
||||
RunClient("Uncle").Then(func(async.Void) {
|
||||
println("RunClient done")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func RunClient(name string) async.Future[async.Void] {
|
||||
return async.Async(func(resolve func(async.Void)) {
|
||||
addr := "127.0.0.1:3927"
|
||||
socketio.Connect("tcp", addr).Then(func(v tuple.Tuple2[*socketio.Conn, error]) {
|
||||
client, err := v.Get()
|
||||
println("Connected", client, err)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
counter := 0
|
||||
var loop func(client *socketio.Conn)
|
||||
loop = func(client *socketio.Conn) {
|
||||
counter++
|
||||
data := fmt.Sprintf("Hello from %s %d", name, counter)
|
||||
client.Write([]byte(data)).Then(func(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
client.Read().Then(func(v tuple.Tuple2[[]byte, error]) {
|
||||
data, err := v.Get()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
println("Read from server:", string(data))
|
||||
timeout.Timeout(1 * time.Second).Then(func(async.Void) {
|
||||
loop(client)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
loop(client)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func RunServer() async.Future[async.Void] {
|
||||
return async.Async(func(resolve func(async.Void)) {
|
||||
socketio.Listen("tcp", "0.0.0.0:3927", func(client *socketio.Conn, err error) {
|
||||
println("Client connected", client, err)
|
||||
var loop func(client *socketio.Conn)
|
||||
loop = func(client *socketio.Conn) {
|
||||
client.Read().Then(func(v tuple.Tuple2[[]byte, error]) {
|
||||
data, err := v.Get()
|
||||
if err != nil {
|
||||
println("Read error", err)
|
||||
} else {
|
||||
println("Read from client:", string(data))
|
||||
client.Write(data).Then(func(err error) {
|
||||
if err != nil {
|
||||
println("Write error", err)
|
||||
} else {
|
||||
loop(client)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
loop(client)
|
||||
})
|
||||
})
|
||||
}
|
||||
34
x/async/async.go
Normal file
34
x/async/async.go
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package async
|
||||
|
||||
import (
|
||||
_ "unsafe"
|
||||
)
|
||||
|
||||
type Void = [0]byte
|
||||
|
||||
type Future[T any] func(func(T))
|
||||
|
||||
func (f Future[T]) Then(cb func(T)) {
|
||||
f(cb)
|
||||
}
|
||||
|
||||
// Just for pure LLGo/Go, transpile to callback in Go+
|
||||
func Await[T1 any](future Future[T1]) T1 {
|
||||
return Run(future)
|
||||
}
|
||||
84
x/async/async_go.go
Normal file
84
x/async/async_go.go
Normal file
@@ -0,0 +1,84 @@
|
||||
//go:build !llgo
|
||||
// +build !llgo
|
||||
|
||||
/*
|
||||
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package async
|
||||
|
||||
import "sync"
|
||||
|
||||
func Async[T any](fn func(func(T))) Future[T] {
|
||||
var once sync.Once
|
||||
var result T
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
|
||||
once.Do(func() {
|
||||
go func() {
|
||||
fn(func(v T) {
|
||||
result = v
|
||||
wg.Done()
|
||||
})
|
||||
}()
|
||||
})
|
||||
|
||||
return func(chain func(T)) {
|
||||
go func() {
|
||||
wg.Wait()
|
||||
chain(result)
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
func Race[T1 any](futures ...Future[T1]) Future[T1] {
|
||||
return Async(func(resolve func(T1)) {
|
||||
ch := make(chan T1)
|
||||
for _, future := range futures {
|
||||
future := future
|
||||
future.Then(func(v T1) {
|
||||
defer func() {
|
||||
// Avoid panic when the channel is closed.
|
||||
_ = recover()
|
||||
}()
|
||||
ch <- v
|
||||
})
|
||||
}
|
||||
v := <-ch
|
||||
close(ch)
|
||||
resolve(v)
|
||||
})
|
||||
}
|
||||
|
||||
func All[T1 any](futures ...Future[T1]) Future[[]T1] {
|
||||
return Async(func(resolve func([]T1)) {
|
||||
n := len(futures)
|
||||
results := make([]T1, n)
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(n)
|
||||
for i, future := range futures {
|
||||
i := i
|
||||
future.Then(func(v T1) {
|
||||
results[i] = v
|
||||
wg.Done()
|
||||
})
|
||||
}
|
||||
wg.Wait()
|
||||
resolve(results)
|
||||
})
|
||||
}
|
||||
104
x/async/async_llgo.go
Normal file
104
x/async/async_llgo.go
Normal file
@@ -0,0 +1,104 @@
|
||||
//go:build llgo
|
||||
// +build llgo
|
||||
|
||||
/*
|
||||
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package async
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/goplus/llgo/c/libuv"
|
||||
"github.com/goplus/llgo/x/cbind"
|
||||
)
|
||||
|
||||
// Currently Async run chain a future that call chain in the goroutine running `async.Run`.
|
||||
func Async[T any](fn func(func(T))) Future[T] {
|
||||
var result T
|
||||
var resultReady atomic.Bool
|
||||
var callbacks []func(T)
|
||||
var mutex sync.Mutex
|
||||
loop := Exec().L
|
||||
|
||||
var a *libuv.Async
|
||||
var cb libuv.AsyncCb
|
||||
a, cb = cbind.BindF[libuv.Async, libuv.AsyncCb](func(a *libuv.Async) {
|
||||
a.Close(nil)
|
||||
mutex.Lock()
|
||||
currentCallbacks := callbacks
|
||||
callbacks = nil
|
||||
mutex.Unlock()
|
||||
|
||||
for _, callback := range currentCallbacks {
|
||||
callback(result)
|
||||
}
|
||||
})
|
||||
loop.Async(a, cb)
|
||||
|
||||
// Execute fn immediately
|
||||
fn(func(v T) {
|
||||
result = v
|
||||
resultReady.Store(true)
|
||||
a.Send()
|
||||
})
|
||||
|
||||
return func(chain func(T)) {
|
||||
mutex.Lock()
|
||||
if resultReady.Load() {
|
||||
mutex.Unlock()
|
||||
chain(result)
|
||||
} else {
|
||||
callbacks = append(callbacks, chain)
|
||||
mutex.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
func Race[T1 any](futures ...Future[T1]) Future[T1] {
|
||||
return Async(func(resolve func(T1)) {
|
||||
done := atomic.Bool{}
|
||||
for _, future := range futures {
|
||||
future.Then(func(v T1) {
|
||||
if !done.Swap(true) {
|
||||
// Just resolve the first one.
|
||||
resolve(v)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func All[T1 any](futures ...Future[T1]) Future[[]T1] {
|
||||
return Async(func(resolve func([]T1)) {
|
||||
n := len(futures)
|
||||
results := make([]T1, n)
|
||||
var done uint32
|
||||
for i, future := range futures {
|
||||
i := i
|
||||
future.Then(func(v T1) {
|
||||
results[i] = v
|
||||
if atomic.AddUint32(&done, 1) == uint32(n) {
|
||||
// All done.
|
||||
resolve(results)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
30
x/async/executor_go.go
Normal file
30
x/async/executor_go.go
Normal file
@@ -0,0 +1,30 @@
|
||||
//go:build !llgo
|
||||
// +build !llgo
|
||||
|
||||
/*
|
||||
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package async
|
||||
|
||||
func Run[T any](future Future[T]) T {
|
||||
ch := make(chan T)
|
||||
go func() {
|
||||
future.Then(func(v T) {
|
||||
ch <- v
|
||||
})
|
||||
}()
|
||||
return <-ch
|
||||
}
|
||||
69
x/async/executor_llgo.go
Normal file
69
x/async/executor_llgo.go
Normal file
@@ -0,0 +1,69 @@
|
||||
//go:build llgo
|
||||
// +build llgo
|
||||
|
||||
/*
|
||||
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package async
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/llgo/c/libuv"
|
||||
"github.com/goplus/llgo/c/pthread"
|
||||
)
|
||||
|
||||
var execKey pthread.Key
|
||||
|
||||
func init() {
|
||||
execKey.Create(nil)
|
||||
}
|
||||
|
||||
type Executor struct {
|
||||
L *libuv.Loop
|
||||
}
|
||||
|
||||
func Exec() *Executor {
|
||||
v := execKey.Get()
|
||||
if v == nil {
|
||||
panic("async.Exec: no executor")
|
||||
}
|
||||
return (*Executor)(v)
|
||||
}
|
||||
|
||||
func setExec(e *Executor) (old *Executor) {
|
||||
old = (*Executor)(execKey.Get())
|
||||
execKey.Set(unsafe.Pointer(e))
|
||||
return
|
||||
}
|
||||
|
||||
func (e *Executor) Run() {
|
||||
e.L.Run(libuv.RUN_DEFAULT)
|
||||
}
|
||||
|
||||
func Run[T any](future Future[T]) T {
|
||||
loop := libuv.LoopNew()
|
||||
exec := &Executor{loop}
|
||||
oldExec := setExec(exec)
|
||||
var ret T
|
||||
future.Then(func(v T) {
|
||||
ret = v
|
||||
})
|
||||
exec.Run()
|
||||
loop.Close()
|
||||
setExec(oldExec)
|
||||
return ret
|
||||
}
|
||||
35
x/async/timeout/timeout_go.go
Normal file
35
x/async/timeout/timeout_go.go
Normal file
@@ -0,0 +1,35 @@
|
||||
//go:build !llgo
|
||||
// +build !llgo
|
||||
|
||||
/*
|
||||
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package timeout
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/goplus/llgo/x/async"
|
||||
)
|
||||
|
||||
func Timeout(d time.Duration) async.Future[async.Void] {
|
||||
return async.Async(func(resolve func(async.Void)) {
|
||||
go func() {
|
||||
time.Sleep(d)
|
||||
resolve(async.Void{})
|
||||
}()
|
||||
})
|
||||
}
|
||||
44
x/async/timeout/timeout_llgo.go
Normal file
44
x/async/timeout/timeout_llgo.go
Normal file
@@ -0,0 +1,44 @@
|
||||
//go:build llgo
|
||||
// +build llgo
|
||||
|
||||
/*
|
||||
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package timeout
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/goplus/llgo/c/libuv"
|
||||
"github.com/goplus/llgo/x/async"
|
||||
"github.com/goplus/llgo/x/cbind"
|
||||
)
|
||||
|
||||
func Timeout(d time.Duration) async.Future[async.Void] {
|
||||
return async.Async(func(resolve func(async.Void)) {
|
||||
t, cb := cbind.BindF[libuv.Timer, libuv.TimerCb](func(t *libuv.Timer) {
|
||||
resolve(async.Void{})
|
||||
})
|
||||
r := libuv.InitTimer(async.Exec().L, t)
|
||||
if r != 0 {
|
||||
panic("InitTimer failed")
|
||||
}
|
||||
r = t.Start(cb, uint64(d/time.Millisecond), 0)
|
||||
if r != 0 {
|
||||
panic("Start failed")
|
||||
}
|
||||
})
|
||||
}
|
||||
16
x/cbind/buf.go
Normal file
16
x/cbind/buf.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package cbind
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type slice struct {
|
||||
data unsafe.Pointer
|
||||
len int
|
||||
}
|
||||
|
||||
func GoBytes(buf *int8, n int) []byte {
|
||||
return *(*[]byte)(unsafe.Pointer(&slice{unsafe.Pointer(buf), n}))
|
||||
}
|
||||
|
||||
func CBuffer(data []byte) (*int8, int) {
|
||||
return (*int8)(unsafe.Pointer(&data[0])), len(data)
|
||||
}
|
||||
143
x/cbind/cbind.go
Normal file
143
x/cbind/cbind.go
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cbind
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// llgo:type C
|
||||
type Cb[T any] func(*T)
|
||||
|
||||
// llgo:type C
|
||||
type Cb1[T any, A any] func(*T, A)
|
||||
|
||||
// llgo:type C
|
||||
type Cb2[T any, A any, B any] func(*T, A, B)
|
||||
|
||||
// llgo:type C
|
||||
type Cb3[T any, A any, B any, C any] func(*T, A, B, C)
|
||||
|
||||
type bind[Base any] struct {
|
||||
b Base
|
||||
fn func(*Base)
|
||||
}
|
||||
|
||||
type bind1[Base any, A any] struct {
|
||||
b Base
|
||||
fn func(*Base, A)
|
||||
}
|
||||
|
||||
type bind2[Base any, A any, B any] struct {
|
||||
b Base
|
||||
fn func(*Base, A, B)
|
||||
}
|
||||
|
||||
type bind3[Base any, A any, B any, C any] struct {
|
||||
b Base
|
||||
fn func(*Base, A, B, C)
|
||||
}
|
||||
|
||||
func callback[Base any](base *Base) {
|
||||
bind := (*bind[Base])(unsafe.Pointer(base))
|
||||
bind.fn(base)
|
||||
}
|
||||
|
||||
func callback1[Base any, A any](base *Base, a A) {
|
||||
bind := (*bind1[Base, A])(unsafe.Pointer(base))
|
||||
bind.fn(base, a)
|
||||
}
|
||||
|
||||
func callback2[Base any, A any, B any](base *Base, a A, b B) {
|
||||
bind := (*bind2[Base, A, B])(unsafe.Pointer(base))
|
||||
bind.fn(base, a, b)
|
||||
}
|
||||
|
||||
func callback3[Base any, A any, B any, C any](base *Base, a A, b B, c C) {
|
||||
bind := (*bind3[Base, A, B, C])(unsafe.Pointer(base))
|
||||
bind.fn(base, a, b, c)
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind[N] binds a Go function to a C callback function.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* timer, cb := cbind.Bind[libuv.Timer](func() {
|
||||
* println("hello")
|
||||
* })
|
||||
* libuv.InitTimer(async.Exec().L, timer)
|
||||
* timer.Start(cb, 1000, 0)
|
||||
*
|
||||
* @param call The Go function to bind.
|
||||
* @return The data pointer and the C callback function.
|
||||
*/
|
||||
func Bind[T any](call func(*T)) (p *T, cb Cb[T]) {
|
||||
bb := &bind[T]{fn: call}
|
||||
p = (*T)(unsafe.Pointer(bb))
|
||||
cb = callback[T]
|
||||
return
|
||||
}
|
||||
|
||||
func BindF[T any, F ~func(*T)](call func(*T)) (*T, F) {
|
||||
bb := &bind[T]{fn: call}
|
||||
p := (*T)(unsafe.Pointer(bb))
|
||||
var fn F = callback[T]
|
||||
return p, fn
|
||||
}
|
||||
|
||||
func Bind1[T any, A any](call func(*T, A)) (p *T, cb Cb1[T, A]) {
|
||||
bb := &bind1[T, A]{fn: call}
|
||||
p = (*T)(unsafe.Pointer(bb))
|
||||
cb = callback1[T, A]
|
||||
return
|
||||
}
|
||||
|
||||
func Bind1F[T any, F ~func(*T, A), A any](call func(*T, A)) (*T, F) {
|
||||
bb := &bind1[T, A]{fn: call}
|
||||
p := (*T)(unsafe.Pointer(bb))
|
||||
var fn F = callback1[T, A]
|
||||
return p, fn
|
||||
}
|
||||
|
||||
func Bind2[T any, A any, B any](call func(*T, A, B)) (p *T, cb Cb2[T, A, B]) {
|
||||
bb := &bind2[T, A, B]{fn: call}
|
||||
p = (*T)(unsafe.Pointer(bb))
|
||||
cb = callback2[T, A, B]
|
||||
return
|
||||
}
|
||||
|
||||
func Bind2F[T any, F ~func(*T, A, B), A any, B any](call func(*T, A, B)) (*T, F) {
|
||||
bb := &bind2[T, A, B]{fn: call}
|
||||
p := (*T)(unsafe.Pointer(bb))
|
||||
var fn F = callback2[T, A, B]
|
||||
return p, fn
|
||||
}
|
||||
|
||||
func Bind3[T any, A any, B any, C any](call func(*T, A, B, C), a A, b B, c C) (p *T, cb Cb3[T, A, B, C]) {
|
||||
bb := &bind3[T, A, B, C]{fn: call}
|
||||
p = (*T)(unsafe.Pointer(bb))
|
||||
cb = callback3[T, A, B, C]
|
||||
return
|
||||
}
|
||||
|
||||
func Bind3F[T any, F ~func(*T, A, B, C), A any, B any, C any](call func(*T, A, B, C), a A, b B, c C) (*T, F) {
|
||||
bb := &bind3[T, A, B, C]{fn: call}
|
||||
p := (*T)(unsafe.Pointer(bb))
|
||||
var fn F = callback3[T, A, B, C]
|
||||
return p, fn
|
||||
}
|
||||
@@ -1,290 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/goplus/llgo/x/io"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type Response struct {
|
||||
StatusCode int
|
||||
|
||||
mockBody string
|
||||
}
|
||||
|
||||
func (r *Response) mock(body string) {
|
||||
r.mockBody = body
|
||||
}
|
||||
|
||||
func (r *Response) Text() (resolve io.Promise[string]) {
|
||||
resolve(r.mockBody, nil)
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Response) TextCompiled() *io.PromiseImpl[string] {
|
||||
P := &io.PromiseImpl[string]{}
|
||||
P.Func = func(resolve func(string, error)) {
|
||||
for {
|
||||
switch P.Prev = P.Next; P.Prev {
|
||||
case 0:
|
||||
resolve(r.mockBody, nil)
|
||||
P.Next = -1
|
||||
return
|
||||
default:
|
||||
panic("Promise already done")
|
||||
}
|
||||
}
|
||||
}
|
||||
return P
|
||||
}
|
||||
|
||||
func HttpGet(url string, callback func(resp *Response, err error)) {
|
||||
resp := &Response{StatusCode: 200}
|
||||
callback(resp, nil)
|
||||
}
|
||||
|
||||
func AsyncHttpGet(url string) (resolve io.Promise[*Response]) {
|
||||
HttpGet(url, resolve)
|
||||
return
|
||||
}
|
||||
|
||||
func AsyncHttpGetCompiled(url string) *io.PromiseImpl[*Response] {
|
||||
P := &io.PromiseImpl[*Response]{}
|
||||
P.Func = func(resolve func(*Response, error)) {
|
||||
for {
|
||||
switch P.Prev = P.Next; P.Prev {
|
||||
case 0:
|
||||
HttpGet(url, resolve)
|
||||
P.Next = -1
|
||||
return
|
||||
default:
|
||||
panic("Promise already done")
|
||||
}
|
||||
}
|
||||
}
|
||||
return P
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type User struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func GetUser(uid string) (resolve io.Promise[User]) {
|
||||
resp, err := AsyncHttpGet("http://example.com/user/" + uid).Await()
|
||||
if err != nil {
|
||||
resolve(User{}, err)
|
||||
return
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
resolve(User{}, fmt.Errorf("http status code: %d", resp.StatusCode))
|
||||
return
|
||||
}
|
||||
|
||||
resp.mock(`{"name":"Alice"}`)
|
||||
|
||||
body, err := resp.Text().Await()
|
||||
if err != nil {
|
||||
resolve(User{}, err)
|
||||
return
|
||||
}
|
||||
user := User{}
|
||||
if err := json.Unmarshal([]byte(body), &user); err != nil {
|
||||
resolve(User{}, err)
|
||||
return
|
||||
}
|
||||
|
||||
resolve(user, nil)
|
||||
return
|
||||
}
|
||||
|
||||
func GetUserCompiled(uid string) *io.PromiseImpl[User] {
|
||||
var state1 *io.PromiseImpl[*Response]
|
||||
var state2 *io.PromiseImpl[string]
|
||||
|
||||
P := &io.PromiseImpl[User]{}
|
||||
P.Func = func(resolve func(User, error)) {
|
||||
for {
|
||||
switch P.Prev = P.Next; P.Prev {
|
||||
case 0:
|
||||
state1 = AsyncHttpGetCompiled("http://example.com/user/" + uid)
|
||||
P.Next = 1
|
||||
return
|
||||
case 1:
|
||||
state1.EnsureDone()
|
||||
resp, err := state1.Value, state1.Err
|
||||
if err != nil {
|
||||
resolve(User{}, err)
|
||||
return
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
resolve(User{}, fmt.Errorf("http status code: %d", resp.StatusCode))
|
||||
return
|
||||
}
|
||||
|
||||
resp.mock(`{"name":"Alice"}`)
|
||||
|
||||
state2 = resp.TextCompiled()
|
||||
P.Next = 2
|
||||
return
|
||||
case 2:
|
||||
state2.EnsureDone()
|
||||
body, err := state2.Value, state2.Err
|
||||
if err != nil {
|
||||
resolve(User{}, err)
|
||||
return
|
||||
}
|
||||
user := User{}
|
||||
if err := json.Unmarshal([]byte(body), &user); err != nil {
|
||||
resolve(User{}, err)
|
||||
return
|
||||
}
|
||||
|
||||
resolve(user, nil)
|
||||
P.Next = -1
|
||||
return
|
||||
default:
|
||||
panic("Promise already done")
|
||||
}
|
||||
}
|
||||
}
|
||||
return P
|
||||
}
|
||||
|
||||
func GetScore() *io.Promise[float64] {
|
||||
panic("todo: GetScore")
|
||||
}
|
||||
|
||||
func GetScoreCompiled() *io.PromiseImpl[float64] {
|
||||
P := &io.PromiseImpl[float64]{}
|
||||
P.Func = func(resolve func(float64, error)) {
|
||||
for {
|
||||
switch P.Prev = P.Next; P.Prev {
|
||||
case 0:
|
||||
panic("todo: GetScore")
|
||||
default:
|
||||
panic("Promise already done")
|
||||
}
|
||||
}
|
||||
}
|
||||
return P
|
||||
}
|
||||
|
||||
func DoUpdate(op string) *io.Promise[io.Void] {
|
||||
panic("todo: DoUpdate")
|
||||
}
|
||||
|
||||
func DoUpdateCompiled(op string) *io.PromiseImpl[io.Void] {
|
||||
P := &io.PromiseImpl[io.Void]{}
|
||||
P.Func = func(resolve func(io.Void, error)) {
|
||||
for {
|
||||
switch P.Prev = P.Next; P.Prev {
|
||||
case 0:
|
||||
panic("todo: DoUpdate")
|
||||
default:
|
||||
panic("Promise already done")
|
||||
}
|
||||
}
|
||||
}
|
||||
return P
|
||||
}
|
||||
|
||||
func Demo() (resolve io.Promise[io.Void]) {
|
||||
user, err := GetUser("123").Await()
|
||||
log.Println(user, err)
|
||||
|
||||
user, err = io.Race[User](GetUser("123"), GetUser("456"), GetUser("789")).Await()
|
||||
log.Println(user, err)
|
||||
|
||||
users, err := io.All[User]([]io.AsyncCall[User]{GetUser("123"), GetUser("456"), GetUser("789")}).Await()
|
||||
log.Println(users, err)
|
||||
|
||||
user, score, _, err := io.Await3[User, float64, io.Void](GetUser("123"), GetScore(), DoUpdate("update sth."))
|
||||
log.Println(user, score, err)
|
||||
|
||||
// TODO(lijie): select from multiple promises without channel
|
||||
select {
|
||||
case user := <-GetUser("123").Chan():
|
||||
log.Println("user:", user)
|
||||
case score := <-GetScore().Chan():
|
||||
log.Println("score:", score)
|
||||
case <-io.Timeout(5 * time.Second).Chan():
|
||||
log.Println("timeout")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func DemoCompiled() *io.PromiseImpl[io.Void] {
|
||||
var state1 *io.PromiseImpl[User]
|
||||
var state2 *io.PromiseImpl[User]
|
||||
var state3 *io.PromiseImpl[[]User]
|
||||
var state4 *io.PromiseImpl[io.Await3Result[User, float64, io.Void]]
|
||||
|
||||
P := &io.PromiseImpl[io.Void]{}
|
||||
P.Func = func(resolve func(io.Void, error)) {
|
||||
for {
|
||||
switch P.Prev = P.Next; P.Prev {
|
||||
case 0:
|
||||
state1 = GetUserCompiled("123")
|
||||
P.Next = 1
|
||||
return
|
||||
case 1:
|
||||
state1.EnsureDone()
|
||||
user, err := state1.Value, state1.Err
|
||||
log.Printf("user: %v, err: %v\n", user, err)
|
||||
|
||||
state2 = io.Race[User](GetUserCompiled("123"), GetUserCompiled("456"), GetUserCompiled("789"))
|
||||
P.Next = 2
|
||||
return
|
||||
case 2:
|
||||
state2.EnsureDone()
|
||||
user, err := state2.Value, state2.Err
|
||||
log.Println(user, err)
|
||||
|
||||
state3 = io.All[User]([]io.AsyncCall[User]{GetUserCompiled("123"), GetUserCompiled("456"), GetUserCompiled("789")})
|
||||
P.Next = 3
|
||||
return
|
||||
case 3:
|
||||
state3.EnsureDone()
|
||||
users, err := state3.Value, state3.Err
|
||||
log.Println(users, err)
|
||||
|
||||
state4 = io.Await3Compiled[User, float64, io.Void](GetUserCompiled("123"), GetScoreCompiled(), DoUpdateCompiled("update sth."))
|
||||
P.Next = 4
|
||||
return
|
||||
case 4:
|
||||
state4.EnsureDone()
|
||||
user, score, _, err := state4.Value.V1, state4.Value.V2, state4.Value.V3, state4.Value.Err
|
||||
log.Println(user, score, err)
|
||||
|
||||
select {
|
||||
case user := <-GetUserCompiled("123").Chan():
|
||||
log.Println("user:", user)
|
||||
case score := <-GetScoreCompiled().Chan():
|
||||
log.Println("score:", score)
|
||||
case <-io.TimeoutCompiled(5 * time.Second).Chan():
|
||||
log.Println("timeout")
|
||||
}
|
||||
P.Next = -1
|
||||
return
|
||||
default:
|
||||
panic("Promise already done")
|
||||
}
|
||||
}
|
||||
}
|
||||
return P
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.SetFlags(log.Lshortfile | log.LstdFlags)
|
||||
// io.Run(Demo())
|
||||
io.Run(DemoCompiled())
|
||||
}
|
||||
170
x/io/io.go
170
x/io/io.go
@@ -1,170 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io
|
||||
|
||||
import (
|
||||
_ "unsafe"
|
||||
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
LLGoPackage = "decl"
|
||||
)
|
||||
|
||||
type Void = [0]byte
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type AsyncCall[OutT any] interface {
|
||||
Await(timeout ...time.Duration) (ret OutT, err error)
|
||||
Chan() <-chan OutT
|
||||
EnsureDone()
|
||||
}
|
||||
|
||||
// llgo:link AsyncCall.Await llgo.await
|
||||
func Await[OutT any](call AsyncCall[OutT], timeout ...time.Duration) (ret OutT, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
//go:linkname Timeout llgo.timeout
|
||||
func Timeout(time.Duration) (ret AsyncCall[Void])
|
||||
|
||||
func TimeoutCompiled(d time.Duration) *PromiseImpl[Void] {
|
||||
P := &PromiseImpl[Void]{}
|
||||
P.Func = func(resolve func(Void, error)) {
|
||||
go func() {
|
||||
time.Sleep(d)
|
||||
resolve(Void{}, nil)
|
||||
}()
|
||||
}
|
||||
return P
|
||||
}
|
||||
|
||||
// llgo:link Race llgo.race
|
||||
func Race[OutT any](acs ...AsyncCall[OutT]) (ret *PromiseImpl[OutT]) {
|
||||
return
|
||||
}
|
||||
|
||||
func All[OutT any](acs []AsyncCall[OutT]) (ret *PromiseImpl[[]OutT]) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// llgo:link Await2 llgo.await
|
||||
func Await2[OutT1, OutT2 any](
|
||||
ac1 AsyncCall[OutT1], ac2 AsyncCall[OutT2],
|
||||
timeout ...time.Duration) (ret1 OutT1, ret2 OutT2, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
type Await2Result[T1 any, T2 any] struct {
|
||||
V1 T1
|
||||
V2 T2
|
||||
Err error
|
||||
}
|
||||
|
||||
func Await2Compiled[OutT1, OutT2 any](
|
||||
ac1 AsyncCall[OutT1], ac2 AsyncCall[OutT2],
|
||||
timeout ...time.Duration) (ret *PromiseImpl[Await2Result[OutT1, OutT2]]) {
|
||||
return
|
||||
}
|
||||
|
||||
// llgo:link Await3 llgo.await
|
||||
func Await3[OutT1, OutT2, OutT3 any](
|
||||
ac1 AsyncCall[OutT1], ac2 AsyncCall[OutT2], ac3 AsyncCall[OutT3],
|
||||
timeout ...time.Duration) (ret1 OutT1, ret2 OutT2, ret3 OutT3, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
type Await3Result[T1 any, T2 any, T3 any] struct {
|
||||
V1 T1
|
||||
V2 T2
|
||||
V3 T3
|
||||
Err error
|
||||
}
|
||||
|
||||
func Await3Compiled[OutT1, OutT2, OutT3 any](
|
||||
ac1 AsyncCall[OutT1], ac2 AsyncCall[OutT2], ac3 AsyncCall[OutT3],
|
||||
timeout ...time.Duration) (ret *PromiseImpl[Await3Result[OutT1, OutT2, OutT3]]) {
|
||||
return
|
||||
}
|
||||
|
||||
func Run(ac AsyncCall[Void]) {
|
||||
p := ac.(*PromiseImpl[Void])
|
||||
p.Resume()
|
||||
<-ac.Chan()
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type Promise[OutT any] func(OutT, error)
|
||||
|
||||
// llgo:link Promise.Await llgo.await
|
||||
func (p Promise[OutT]) Await(timeout ...time.Duration) (ret OutT, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (p Promise[OutT]) Chan() <-chan OutT {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p Promise[OutT]) EnsureDone() {
|
||||
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
type PromiseImpl[TOut any] struct {
|
||||
Func func(resolve func(TOut, error))
|
||||
Value TOut
|
||||
Err error
|
||||
Prev int
|
||||
Next int
|
||||
|
||||
c chan TOut
|
||||
}
|
||||
|
||||
func (p *PromiseImpl[TOut]) Resume() {
|
||||
p.Func(func(v TOut, err error) {
|
||||
p.Value = v
|
||||
p.Err = err
|
||||
})
|
||||
}
|
||||
|
||||
func (p *PromiseImpl[TOut]) EnsureDone() {
|
||||
if p.Next == -1 {
|
||||
panic("Promise already done")
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PromiseImpl[TOut]) Chan() <-chan TOut {
|
||||
if p.c == nil {
|
||||
p.c = make(chan TOut, 1)
|
||||
p.Func(func(v TOut, err error) {
|
||||
p.Value = v
|
||||
p.Err = err
|
||||
p.c <- v
|
||||
})
|
||||
}
|
||||
return p.c
|
||||
}
|
||||
|
||||
func (p *PromiseImpl[TOut]) Await(timeout ...time.Duration) (ret TOut, err error) {
|
||||
panic("should not called")
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
92
x/socketio/socketio_go.go
Normal file
92
x/socketio/socketio_go.go
Normal file
@@ -0,0 +1,92 @@
|
||||
//go:build !llgo
|
||||
// +build !llgo
|
||||
|
||||
/*
|
||||
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package socketio
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/goplus/llgo/x/async"
|
||||
"github.com/goplus/llgo/x/tuple"
|
||||
)
|
||||
|
||||
type Conn struct {
|
||||
conn net.Conn
|
||||
}
|
||||
|
||||
func Listen(protocol, bindAddr string, listenCb func(client *Conn, err error)) {
|
||||
go func() {
|
||||
listener, err := net.Listen(protocol, bindAddr)
|
||||
if err != nil {
|
||||
listenCb(nil, err)
|
||||
return
|
||||
}
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
listenCb(nil, err)
|
||||
return
|
||||
}
|
||||
listenCb(&Conn{conn: conn}, nil)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func Connect(network, addr string) async.Future[tuple.Tuple2[*Conn, error]] {
|
||||
return async.Async(func(resolve func(tuple.Tuple2[*Conn, error])) {
|
||||
go func() {
|
||||
conn, err := net.Dial(network, addr)
|
||||
if err != nil {
|
||||
resolve(tuple.T2[*Conn, error](nil, err))
|
||||
return
|
||||
}
|
||||
resolve(tuple.T2[*Conn, error](&Conn{conn: conn}, nil))
|
||||
}()
|
||||
})
|
||||
}
|
||||
|
||||
// Read once from the TCP connection.
|
||||
func (t *Conn) Read() async.Future[tuple.Tuple2[[]byte, error]] {
|
||||
return async.Async(func(resolve func(tuple.Tuple2[[]byte, error])) {
|
||||
go func() {
|
||||
buf := make([]byte, 1024)
|
||||
n, err := t.conn.Read(buf)
|
||||
if err != nil {
|
||||
resolve(tuple.T2[[]byte, error](nil, err))
|
||||
return
|
||||
}
|
||||
resolve(tuple.T2[[]byte, error](buf[:n], nil))
|
||||
}()
|
||||
})
|
||||
}
|
||||
|
||||
func (t *Conn) Write(data []byte) async.Future[error] {
|
||||
return async.Async(func(resolve func(error)) {
|
||||
go func() {
|
||||
_, err := t.conn.Write(data)
|
||||
resolve(err)
|
||||
}()
|
||||
})
|
||||
}
|
||||
|
||||
func (t *Conn) Close() {
|
||||
if t.conn != nil {
|
||||
t.conn.Close()
|
||||
}
|
||||
}
|
||||
260
x/socketio/socketio_llgo.go
Normal file
260
x/socketio/socketio_llgo.go
Normal file
@@ -0,0 +1,260 @@
|
||||
//go:build llgo
|
||||
// +build llgo
|
||||
|
||||
/*
|
||||
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package socketio
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
_ "unsafe"
|
||||
|
||||
"github.com/goplus/llgo/c"
|
||||
"github.com/goplus/llgo/c/libuv"
|
||||
"github.com/goplus/llgo/c/net"
|
||||
"github.com/goplus/llgo/x/async"
|
||||
"github.com/goplus/llgo/x/cbind"
|
||||
"github.com/goplus/llgo/x/tuple"
|
||||
)
|
||||
|
||||
type Listener struct {
|
||||
tcp libuv.Tcp
|
||||
listenCb func(server *Listener, err error)
|
||||
}
|
||||
|
||||
type Conn struct {
|
||||
tcp libuv.Tcp
|
||||
readCb func([]byte, error)
|
||||
}
|
||||
|
||||
type libuvError libuv.Errno
|
||||
|
||||
func (e libuvError) Error() string {
|
||||
s := libuv.Strerror(libuv.Errno(e))
|
||||
return c.GoString(s, c.Strlen(s))
|
||||
}
|
||||
|
||||
type getAddrInfoBind struct {
|
||||
libuv.GetAddrInfo
|
||||
resolve func(tuple.Tuple2[*net.SockAddr, error])
|
||||
}
|
||||
|
||||
func getAddrInfoCb(p *libuv.GetAddrInfo, status c.Int, addr *net.AddrInfo) {
|
||||
bind := (*getAddrInfoBind)(unsafe.Pointer(p))
|
||||
if status != 0 {
|
||||
bind.resolve(tuple.T2[*net.SockAddr, error](nil, libuvError(status)))
|
||||
return
|
||||
}
|
||||
bind.resolve(tuple.T2[*net.SockAddr, error](addr.Addr, nil))
|
||||
}
|
||||
|
||||
func parseAddr(addr string) async.Future[tuple.Tuple2[*net.SockAddr, error]] {
|
||||
return async.Async(func(resolve func(tuple.Tuple2[*net.SockAddr, error])) {
|
||||
host := "127.0.0.1"
|
||||
var port string
|
||||
// split host and service by last colon
|
||||
idx := strings.LastIndex(addr, ":")
|
||||
if idx < 0 {
|
||||
port = addr
|
||||
} else {
|
||||
host = addr[:idx]
|
||||
port = addr[idx+1:]
|
||||
}
|
||||
|
||||
hints := &net.AddrInfo{
|
||||
Family: net.AF_INET,
|
||||
SockType: net.SOCK_STREAM,
|
||||
Protocol: syscall.IPPROTO_TCP,
|
||||
Flags: 0,
|
||||
}
|
||||
|
||||
req, cb := cbind.Bind2F[libuv.GetAddrInfo, libuv.GetaddrinfoCb](func(i *libuv.GetAddrInfo, status c.Int, addr *net.AddrInfo) {
|
||||
if status != 0 {
|
||||
resolve(tuple.T2[*net.SockAddr, error](nil, libuvError(status)))
|
||||
return
|
||||
}
|
||||
resolve(tuple.T2[*net.SockAddr, error](addr.Addr, nil))
|
||||
})
|
||||
if res := libuv.Getaddrinfo(async.Exec().L, req, cb, c.AllocaCStr(host), c.AllocaCStr(port), hints); res != 0 {
|
||||
resolve(tuple.T2[*net.SockAddr, error](nil, libuvError(res)))
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Listen(protocol, bindAddr string, listenCb func(client *Conn, err error)) {
|
||||
tcp, err := newListener()
|
||||
if err != nil {
|
||||
listenCb(nil, err)
|
||||
return
|
||||
}
|
||||
parseAddr(bindAddr).Then(func(v tuple.Tuple2[*net.SockAddr, error]) {
|
||||
addr, err := v.Get()
|
||||
if err != nil {
|
||||
listenCb(nil, err)
|
||||
return
|
||||
}
|
||||
if err := tcp.bind(addr, 0); err != nil {
|
||||
listenCb(nil, err)
|
||||
return
|
||||
}
|
||||
if err := tcp.listen(128, func(server *Listener, err error) {
|
||||
client, err := server.accept()
|
||||
listenCb(client, err)
|
||||
}); err != nil {
|
||||
listenCb(nil, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func newListener() (*Listener, error) {
|
||||
t := &Listener{}
|
||||
if res := libuv.InitTcp(async.Exec().L, &t.tcp); res != 0 {
|
||||
return nil, libuvError(res)
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (t *Listener) bind(addr *net.SockAddr, flags uint) error {
|
||||
if res := t.tcp.Bind(addr, c.Uint(flags)); res != 0 {
|
||||
return libuvError(res)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Listener) listen(backlog int, cb func(server *Listener, err error)) error {
|
||||
l.listenCb = cb
|
||||
res := (*libuv.Stream)(&l.tcp).Listen(c.Int(backlog), func(s *libuv.Stream, status c.Int) {
|
||||
server := (*Listener)(unsafe.Pointer(s))
|
||||
if status != 0 {
|
||||
server.listenCb(server, libuvError(libuv.Errno(status)))
|
||||
} else {
|
||||
server.listenCb(server, nil)
|
||||
}
|
||||
})
|
||||
if res != 0 {
|
||||
return libuvError(res)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Listener) accept() (client *Conn, err error) {
|
||||
tcp := &Conn{}
|
||||
if res := libuv.InitTcp(async.Exec().L, &tcp.tcp); res != 0 {
|
||||
return nil, libuvError(res)
|
||||
}
|
||||
if res := (*libuv.Stream)(&l.tcp).Accept((*libuv.Stream)(&tcp.tcp)); res != 0 {
|
||||
return nil, libuvError(res)
|
||||
}
|
||||
return tcp, nil
|
||||
}
|
||||
|
||||
func Connect(network, addr string) async.Future[tuple.Tuple2[*Conn, error]] {
|
||||
return async.Async(func(resolve func(tuple.Tuple2[*Conn, error])) {
|
||||
parseAddr(addr).Then(func(v tuple.Tuple2[*net.SockAddr, error]) {
|
||||
addr, err := v.Get()
|
||||
if err != nil {
|
||||
resolve(tuple.T2[*Conn, error]((*Conn)(nil), err))
|
||||
return
|
||||
}
|
||||
|
||||
tcp := &Conn{}
|
||||
if res := libuv.InitTcp(async.Exec().L, &tcp.tcp); res != 0 {
|
||||
resolve(tuple.T2[*Conn, error]((*Conn)(nil), libuvError(res)))
|
||||
return
|
||||
}
|
||||
req, cb := cbind.Bind1F[libuv.Connect, libuv.ConnectCb](func(c *libuv.Connect, status c.Int) {
|
||||
if status != 0 {
|
||||
resolve(tuple.T2[*Conn, error]((*Conn)(nil), libuvError(libuv.Errno(status))))
|
||||
} else {
|
||||
resolve(tuple.T2[*Conn, error](tcp, nil))
|
||||
}
|
||||
})
|
||||
if res := libuv.TcpConnect(req, &tcp.tcp, addr, cb); res != 0 {
|
||||
resolve(tuple.T2[*Conn, error]((*Conn)(nil), libuvError(res)))
|
||||
return
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func allocBuffer(handle *libuv.Handle, suggestedSize uintptr, buf *libuv.Buf) {
|
||||
buf.Base = (*c.Char)(c.Malloc(suggestedSize))
|
||||
buf.Len = suggestedSize
|
||||
}
|
||||
|
||||
func (t *Conn) StartRead(fn func(data []byte, err error)) {
|
||||
t.readCb = func(data []byte, err error) {
|
||||
fn(data, err)
|
||||
}
|
||||
tcp := (*libuv.Stream)(&t.tcp)
|
||||
res := tcp.StartRead(allocBuffer, func(client *libuv.Stream, nread c.Long, buf *libuv.Buf) {
|
||||
tcp := (*Conn)(unsafe.Pointer(client))
|
||||
if nread > 0 {
|
||||
tcp.readCb(cbind.GoBytes(buf.Base, int(nread)), nil)
|
||||
} else if nread < 0 {
|
||||
tcp.readCb(nil, libuvError(libuv.Errno(nread)))
|
||||
} else {
|
||||
tcp.readCb(nil, nil)
|
||||
}
|
||||
})
|
||||
if res != 0 {
|
||||
t.readCb(nil, libuvError(libuv.Errno(res)))
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Conn) StopRead() error {
|
||||
tcp := (*libuv.Stream)(&t.tcp)
|
||||
if res := tcp.StopRead(); res != 0 {
|
||||
return libuvError(libuv.Errno(res))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read once from the TCP connection.
|
||||
func (t *Conn) Read() async.Future[tuple.Tuple2[[]byte, error]] {
|
||||
return async.Async(func(resolve func(tuple.Tuple2[[]byte, error])) {
|
||||
t.StartRead(func(data []byte, err error) {
|
||||
if err := t.StopRead(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
resolve(tuple.T2[[]byte, error](data, err))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func (t *Conn) Write(data []byte) async.Future[error] {
|
||||
return async.Async(func(resolve func(error)) {
|
||||
writer, cb := cbind.Bind1F[libuv.Write, libuv.WriteCb](func(req *libuv.Write, status c.Int) {
|
||||
var result error
|
||||
if status != 0 {
|
||||
result = libuvError(libuv.Errno(status))
|
||||
}
|
||||
resolve(result)
|
||||
})
|
||||
tcp := (*libuv.Stream)(&t.tcp)
|
||||
buf, len := cbind.CBuffer(data)
|
||||
bufs := &libuv.Buf{Base: buf, Len: uintptr(len)}
|
||||
writer.Write(tcp, bufs, 1, cb)
|
||||
})
|
||||
}
|
||||
|
||||
func (t *Conn) Close() {
|
||||
(*libuv.Handle)(unsafe.Pointer(&t.tcp)).Close(nil)
|
||||
}
|
||||
40
x/tuple/tuple.go
Normal file
40
x/tuple/tuple.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package tuple
|
||||
|
||||
type Tuple[T any] struct {
|
||||
v T
|
||||
}
|
||||
|
||||
func T[T any](v T) Tuple[T] {
|
||||
return Tuple[T]{v: v}
|
||||
}
|
||||
|
||||
func (t Tuple[T]) Get() T {
|
||||
return t.v
|
||||
}
|
||||
|
||||
type Tuple2[T1 any, T2 any] struct {
|
||||
v1 T1
|
||||
v2 T2
|
||||
}
|
||||
|
||||
func T2[T1 any, T2 any](v1 T1, v2 T2) Tuple2[T1, T2] {
|
||||
return Tuple2[T1, T2]{v1: v1, v2: v2}
|
||||
}
|
||||
|
||||
func (t Tuple2[T1, T2]) Get() (T1, T2) {
|
||||
return t.v1, t.v2
|
||||
}
|
||||
|
||||
type Tuple3[T1 any, T2 any, T3 any] struct {
|
||||
v1 T1
|
||||
v2 T2
|
||||
v3 T3
|
||||
}
|
||||
|
||||
func T3[T1 any, T2 any, T3 any](v1 T1, v2 T2, v3 T3) Tuple3[T1, T2, T3] {
|
||||
return Tuple3[T1, T2, T3]{v1: v1, v2: v2, v3: v3}
|
||||
}
|
||||
|
||||
func (t Tuple3[T1, T2, T3]) Get() (T1, T2, T3) {
|
||||
return t.v1, t.v2, t.v3
|
||||
}
|
||||
Reference in New Issue
Block a user