Compare commits

...

45 Commits

Author SHA1 Message Date
xushiwei
683129b6a5 Merge pull request #781 from cpunion/future-io
Future IO update
2024-09-12 12:48:25 +08:00
Li Jie
7f4022120e fix deadlock 2024-09-10 14:38:46 +08:00
Li Jie
3f9e86c37a x 2024-09-10 11:49:42 +08:00
Li Jie
12f460e376 async.Run as global context, async operations run immediately 2024-09-10 11:43:44 +08:00
Li Jie
44c4488fcc async doc update 2024-09-09 10:41:22 +08:00
Li Jie
44617b6554 future supports multi-await but run once 2024-09-09 09:34:29 +08:00
Li Jie
ccc7d056ba socketio example: two tcp clients 2024-09-08 20:29:24 +08:00
Li Jie
566d5ef96f add Future.Then 2024-09-08 20:27:05 +08:00
xushiwei
cf53f3a347 Merge pull request #778 from cpunion/future-io
Future I/O
2024-09-08 17:37:33 +08:00
xushiwei
74b48ff56b Merge pull request #771 from luoliwoshang/chore/llcppsymg/config
llcppsymg:language config
2024-09-08 17:25:49 +08:00
xushiwei
9410370cc5 Merge pull request #777 from luoliwoshang/c/lua/thread
c/lua:thread
2024-09-08 17:22:01 +08:00
luoliwoshang
743ddf83c1 c/lua:thread 2024-09-07 18:55:24 +08:00
Li Jie
d2538d08a7 code clean 2024-09-07 10:20:02 +08:00
visualfc
75fe9d61a3 cl: function fix freevars cache 2024-09-07 10:04:38 +08:00
Li Jie
fce0672282 make future IO working both on go and llgo 2024-09-07 10:04:34 +08:00
Li Jie
69a2a01bc7 cbind.Bind: expose *Base argument 2024-09-07 09:45:23 +08:00
Li Jie
a2d4e79c20 new future IO and demo 2024-09-07 09:45:05 +08:00
Li Jie
6e0a9b2b48 cbind.BindF 2024-09-07 09:43:48 +08:00
Li Jie
276f2070ee hide tuple fields, only expose tuple.TN(...) and tuple.Get() 2024-09-07 09:43:48 +08:00
Li Jie
1a158b5de3 async: work both go and llgo 2024-09-07 09:43:48 +08:00
Li Jie
d4a72bf661 async.Run/Await/Race/All 2024-09-07 09:43:48 +08:00
luoliwoshang
caa707325a llcppsymg:language config 2024-09-06 09:05:35 +08:00
xushiwei
3c588e67b8 Merge pull request #767 from tsingbx/cjson
Improve the llgo cjson library
2024-09-06 06:56:49 +08:00
xushiwei
6c26dad048 Merge pull request #770 from visualfc/tpfunc
ssa: fix llgo:type c for typeparam named
2024-09-06 06:51:30 +08:00
xushiwei
393e2c125e Merge pull request #772 from luoliwoshang/castdump/typedef
castdump:use UnderlyingType instead CanonicalType in typedef
2024-09-06 06:48:12 +08:00
luoliwoshang
e56dc2ed6a castdump:use UnderlyingType instead CanonicalType 2024-09-05 15:45:36 +08:00
tsingbx
4a449ed85e change comment cBool to cJSON_Bool 2024-09-05 11:01:34 +08:00
tsingbx
88dbe90075 Revert "change comment cBool to Bool"
This reverts commit a6f6451434.
2024-09-05 11:00:20 +08:00
tsingbx
a6f6451434 change comment cBool to Bool 2024-09-05 10:58:40 +08:00
tsingbx
8a4370c1f6 change ParseBytes and ParseString, change JSON_bool to Bool, remove Bool() 2024-09-05 10:55:49 +08:00
xushiwei
7a068450b3 Merge pull request #764 from cpunion/ssa-error-exit
cl: exit 1 when SSA build error
2024-09-05 08:42:21 +08:00
xushiwei
ae3222e4c2 Merge pull request #761 from visualfc/abiname
internal/abi: fix splitName
2024-09-05 08:34:27 +08:00
xushiwei
27b4bfa3fa Merge pull request #758 from luoliwoshang/llcppg/language
llcppg/config:language
2024-09-05 08:32:49 +08:00
xushiwei
8af229947f Merge pull request #765 from cpunion/builtin-llgo-tag
cl: builtin llgo tag
2024-09-05 08:32:06 +08:00
xushiwei
f235a2f539 Merge pull request #763 from luoliwoshang/c/clang/usr
c/clang:usr & range
2024-09-05 08:31:37 +08:00
visualfc
b0ebb479f6 ssa: fix llgo:type c for typeparam named 2024-09-04 21:53:50 +08:00
tsingbx
df92e21520 fix error 2024-09-04 20:31:45 +08:00
tsingbx
a1a25cc57f complete cjson 2024-09-04 20:23:47 +08:00
Li Jie
e9aaf8e0af cl: builtin llgo tag 2024-09-04 10:07:22 +08:00
Li Jie
7a80e407af cl: exit 1 when SSA build error 2024-09-04 09:55:56 +08:00
luoliwoshang
57f8d535fb c/clang:usr & range 2024-09-03 18:33:40 +08:00
visualfc
fcc444a100 internal/abi: fix splitName 2024-09-03 15:24:08 +08:00
luoliwoshang
8ccb3c21e1 llcppg/config:language 2024-09-03 15:17:03 +08:00
xushiwei
3ce9567f62 Merge pull request #760 from visualfc/cvtnamed
ssa: cvtNamed check typeargs
2024-09-03 15:05:39 +08:00
visualfc
765e812b77 ssa: cvtNamed check typeargs 2024-09-03 11:37:31 +08:00
40 changed files with 2497 additions and 640 deletions

View File

@@ -26,87 +26,25 @@ const (
LLGoPackage = "link: $(pkg-config --libs libcjson); -lcjson" LLGoPackage = "link: $(pkg-config --libs libcjson); -lcjson"
) )
type Bool c.Int
// llgo:type C // llgo:type C
type JSON struct { type JSON struct {
Unused [0]byte 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 { 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 { 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 // CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
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
// //
//go:linkname Raw C.cJSON_CreateRaw // Render a cJSON entity to text for transfer/storage without any formatting.
func Raw(raw *c.Char) *JSON
// 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
// 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 // llgo:link (*JSON).CStr C.cJSON_PrintUnformatted
func (o *JSON) CStr() *c.Char { return nil } 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 // llgo:link (*JSON).Cstr C.cJSON_PrintUnformatted
func (o *JSON) Cstr() *c.Char { return nil } 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. // Render a JSON entity to text for transfer/storage.
// //
// llgo:link (*JSON).Print C.cJSON_Print // llgo:link (*JSON).Print C.cJSON_Print
func (o *JSON) Print() *c.Char { return nil } 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. // Render a JSON entity to text for transfer/storage without any formatting.
// //
// llgo:link (*JSON).PrintUnformatted C.cJSON_PrintUnformatted // llgo:link (*JSON).PrintUnformatted C.cJSON_PrintUnformatted
func (o *JSON) PrintUnformatted() *c.Char { return nil } 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. // Render a JSON entity to text using a buffered strategy.
//
// prebuffer is a guess at the final size. guessing well reduces reallocation. // prebuffer is a guess at the final size. guessing well reduces reallocation.
//
// fmt=0 gives unformatted, =1 gives formatted. // fmt=0 gives unformatted, =1 gives formatted.
// //
// llgo:link (*JSON).PrintBuffered C.cJSON_PrintBuffered // 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 // CJSON_PUBLIC(cJSON_Bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_Bool format);
func (o *JSON) GetObjectItemCaseSensitive(key *c.Char) *JSON { return nil } //
// 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 // llgo:link (*JSON).GetArraySize C.cJSON_GetArraySize
func (o *JSON) GetArraySize() c.Int { return 0 } 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 // llgo:link (*JSON).GetArrayItem C.cJSON_GetArrayItem
func (o *JSON) GetArrayItem(index c.Int) *JSON { return nil } 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 // llgo:link (*JSON).GetStringValue C.cJSON_GetStringValue
func (o *JSON) GetStringValue() *c.Char { return nil } 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 //go:linkname Free C.cJSON_free
func Free(ptr unsafe.Pointer) func Free(ptr unsafe.Pointer)
//go:linkname FreeCStr C.cJSON_free
func FreeCStr(*c.Char)

View File

@@ -109,6 +109,8 @@ enum CX_StorageClass wrap_clang_Cursor_getStorageClass(CXCursor *cursor) {
return clang_Cursor_getStorageClass(*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); } CXString wrap_clang_getCursorSpelling(CXCursor *cur) { return clang_getCursorSpelling(*cur); }
unsigned wrap_clang_Cursor_isVariadic(CXCursor *cur) { return clang_Cursor_isVariadic(*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); 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" } // extern "C"

View File

@@ -2166,6 +2166,24 @@ func (c Cursor) StorageClass() (ret StorageClass) {
return c.wrapStorageClass() 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. * 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) 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 //llgo:link File.FileName C.clang_getFileName
func (File) FileName() (ret String) { return } func (File) FileName() (ret String) { return }

View 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
*/

View File

@@ -159,13 +159,17 @@ func (L *State) Close() {}
// llgo:link (*State).Newthread C.lua_newthread // llgo:link (*State).Newthread C.lua_newthread
func (L *State) Newthread() *State { return nil } func (L *State) Newthread() *State { return nil }
// int (lua_closethread) (State *L, State *from); // llgo:link (*State).Closethread C.lua_closethread
// int (lua_resetthread) (State *L); /* Deprecated! */ 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 // llgo:link (*State).Atpanic C.lua_atpanic
func (L *State) Atpanic(panicf CFunction) CFunction { return nil } 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 // ** basic stack manipulation
@@ -240,7 +244,9 @@ func (L *State) Tocfunction(idx c.Int) CFunction { return nil }
// llgo:link (*State).Touserdata C.lua_touserdata // llgo:link (*State).Touserdata C.lua_touserdata
func (L *State) Touserdata(idx c.Int) c.Pointer { return nil } 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); // 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 // llgo:link (*State).Pushlightuserdata C.lua_pushlightuserdata
func (L *State) Pushlightuserdata(p c.Pointer) {} 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) // ** get functions (Lua -> stack)

View File

@@ -61,7 +61,9 @@ func printType(t clang.Type, data *Data) {
case clang.TypeIncompleteArray, clang.TypeVariableArray, clang.TypeDependentSizedArray, clang.TypeConstantArray: case clang.TypeIncompleteArray, clang.TypeVariableArray, clang.TypeDependentSizedArray, clang.TypeConstantArray:
printType(t.ArrayElementType(), data) printType(t.ArrayElementType(), data)
case clang.TypeTypedef: case clang.TypeTypedef:
printType(t.CanonicalType(), data) printType(t.TypeDeclaration().TypedefDeclUnderlyingType(), data)
case clang.TypeElaborated:
printType(t.NamedType(), data)
case clang.TypeFunctionProto: case clang.TypeFunctionProto:
printType(t.ResultType(), data) printType(t.ResultType(), data)
for i := 0; i < int(t.NumArgTypes()); i++ { for i := 0; i < int(t.NumArgTypes()); i++ {

View File

@@ -26,6 +26,7 @@ func GetConf(data []byte) (Conf, error) {
Libs: GetStringItem(parsedConf, "libs", ""), Libs: GetStringItem(parsedConf, "libs", ""),
Include: GetStringArrayItem(parsedConf, "include"), Include: GetStringArrayItem(parsedConf, "include"),
TrimPrefixes: GetStringArrayItem(parsedConf, "trimPrefixes"), TrimPrefixes: GetStringArrayItem(parsedConf, "trimPrefixes"),
Cplusplus: GetBoolItem(parsedConf, "cplusplus"),
} }
return Conf{ return Conf{
@@ -58,3 +59,14 @@ func GetStringArrayItem(obj *cjson.JSON, key string) (value []string) {
} }
return 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
}

View File

@@ -18,7 +18,8 @@ If `config-file` is not specified, a `llcppg.cfg` file is used in current direct
"AnotherHeaderFile.h" "AnotherHeaderFile.h"
], ],
"libs": "$(pkg-config --libs inireader)", "libs": "$(pkg-config --libs inireader)",
"trimPrefixes": ["Ini", "INI"] "trimPrefixes": ["Ini", "INI"],
"cplusplus":true
} }
``` ```

View File

@@ -23,6 +23,7 @@ type Config struct {
Libs string `json:"libs"` Libs string `json:"libs"`
Include []string `json:"include"` Include []string `json:"include"`
TrimPrefixes []string `json:"trimPrefixes"` TrimPrefixes []string `json:"trimPrefixes"`
Cplusplus bool `json:"cplusplus"`
} }
type SymbolInfo struct { type SymbolInfo struct {

View File

@@ -0,0 +1,7 @@
//go:build llgo
// +build llgo
package llgotag
func Foo() {
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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)

View File

@@ -554,7 +554,7 @@ func addChecked(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer {
func splitName(s string) (pkg string, name string) { func splitName(s string) (pkg string, name string) {
i := lastDot(s) i := lastDot(s)
if i == -1 { if i == -1 {
return s, "" return "", s
} }
return s[:i], s[i+1:] return s[:i], s[i+1:]
} }

View File

@@ -121,6 +121,7 @@ const (
func Do(args []string, conf *Config) { func Do(args []string, conf *Config) {
flags, patterns, verbose := ParseArgs(args, buildFlags) flags, patterns, verbose := ParseArgs(args, buildFlags)
flags = append(flags, "-tags", "llgo")
cfg := &packages.Config{ cfg := &packages.Config{
Mode: loadSyntax | packages.NeedDeps | packages.NeedModule | packages.NeedExportFile, Mode: loadSyntax | packages.NeedDeps | packages.NeedModule | packages.NeedExportFile,
BuildFlags: flags, 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) fmt.Fprintln(os.Stderr, "cannot build SSA for package", errPkg)
} }
if len(errPkgs) > 0 {
os.Exit(1)
}
built := ctx.built built := ctx.built
for _, aPkg := range pkgs { for _, aPkg := range pkgs {
pkg := aPkg.Package pkg := aPkg.Package

View File

@@ -169,10 +169,11 @@ type aFunction struct {
defer_ *aDefer defer_ *aDefer
recov BasicBlock recov BasicBlock
params []Type params []Type
freeVars Expr freeVars Expr
base int // base = 1 if hasFreeVars; base = 0 otherwise freeVarsBlock int
hasVArg bool base int // base = 1 if hasFreeVars; base = 0 otherwise
hasVArg bool
} }
// Function represents a function or method. // Function represents a function or method.
@@ -249,12 +250,13 @@ func (p Function) Param(i int) Expr {
} }
func (p Function) closureCtx(b Builder) 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 { if p.base == 0 {
panic("ssa: function has no free variables") panic("ssa: function has no free variables")
} }
ptr := Expr{p.impl.Param(0), p.params[0]} ptr := Expr{p.impl.Param(0), p.params[0]}
p.freeVars = b.Load(ptr) p.freeVars = b.Load(ptr)
p.freeVarsBlock = b.blk.Index()
} }
return p.freeVars return p.freeVars
} }

View File

@@ -244,7 +244,7 @@ func (p Program) SetRuntime(runtime any) {
} }
func (p Program) SetTypeBackground(fullName string, bg Background) { 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) { func (p Program) SetLinkname(name, link string) {

View File

@@ -125,7 +125,7 @@ func (p *goProgram) extraSize(typ types.Type, ptrSize int64) (ret int64) {
retry: retry:
switch t := typ.(type) { switch t := typ.(type) {
case *types.Named: 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 return 0
} }
typ = t.Underlying() typ = t.Underlying()

View File

@@ -20,6 +20,7 @@ import (
"fmt" "fmt"
"go/token" "go/token"
"go/types" "go/types"
"sync"
"unsafe" "unsafe"
) )
@@ -27,13 +28,12 @@ import (
type goTypes struct { type goTypes struct {
typs map[unsafe.Pointer]unsafe.Pointer typs map[unsafe.Pointer]unsafe.Pointer
typbg map[string]Background typbg sync.Map
} }
func newGoTypes() goTypes { func newGoTypes() goTypes {
typs := make(map[unsafe.Pointer]unsafe.Pointer) typs := make(map[unsafe.Pointer]unsafe.Pointer)
typbk := make(map[string]Background) return goTypes{typs: typs}
return goTypes{typs, typbk}
} }
type Background int type Background int
@@ -95,7 +95,7 @@ func (p goTypes) cvtType(typ types.Type) (raw types.Type, cvt bool) {
case *types.Struct: case *types.Struct:
return p.cvtStruct(t) return p.cvtStruct(t)
case *types.Named: case *types.Named:
if p.typbg[t.String()] == InC { if v, ok := p.typbg.Load(namedLinkname(t)); ok && v.(Background) == InC {
break break
} }
return p.cvtNamed(t) return p.cvtNamed(t)
@@ -115,6 +115,14 @@ func (p goTypes) cvtType(typ types.Type) (raw types.Type, cvt bool) {
return typ, false 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) { func (p goTypes) cvtNamed(t *types.Named) (raw *types.Named, cvt bool) {
if v, ok := p.typs[unsafe.Pointer(t)]; ok { if v, ok := p.typs[unsafe.Pointer(t)]; ok {
raw = (*types.Named)(v) raw = (*types.Named)(v)
@@ -128,9 +136,29 @@ func (p goTypes) cvtNamed(t *types.Named) (raw *types.Named, cvt bool) {
methods[i] = m methods[i] = m
} }
named := types.NewNamed(t.Obj(), types.Typ[types.Int], methods) 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) p.typs[unsafe.Pointer(t)] = unsafe.Pointer(named)
if tund, cvt := p.cvtType(t.Underlying()); cvt { if tund, cvt := p.cvtType(t.Underlying()); cvt {
named.SetUnderlying(tund) 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 return named, true
} }
p.typs[unsafe.Pointer(t)] = unsafe.Pointer(t) p.typs[unsafe.Pointer(t)] = unsafe.Pointer(t)

View File

@@ -361,104 +361,83 @@ In some situations, you may want to get the first result of multiple async opera
## Design considerations in LLGo ## Design considerations in LLGo
- Don't introduce `async`/`await` keywords to compatible with Go compiler (just compiling) - Don't introduce `async`/`await` keywords to compatible with Go
- For performance reason don't implement async functions with goroutines - For performance and memory reasons don't implement async functions with goroutines, coroutines, or other mechanisms that require per-task stack allocation
- 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 - Avoid implementing async task by using `chan` that blocking the thread
## Design ## 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 ```go
// Some native async functions package async
func timeoutAsync(d time.Duration, cb func()) {
go func() { type Future[T any] interface {
time.Sleep(d) Then(f func(T))
cb()
}()
} }
// Wrap callback-based async function into Promise func Async[T any](f func(resolve func(T))) Future[T]
func resolveAfter1Second() (resolve Promise[string]) {
timeoutAsync(1 * time.Second, func() {
resolve("Resolved after 1 second", nil)
})
}
// Compiled to: func Await[T any](future Future[T]) T
func resolveAfter1Second() (resolve PromiseImpl[string]) { ```
promise := io.NewPromiseImpl[string](resolve func(value string, err error) {
resolve: func(value string, err error) { ### Some async functions
for true {
switch (promise.prev = promise.next) { ```go
case 0: package async
timeoutAsync(1 * time.Second, func() {
resolve("Resolved after 1 second", nil)
}) 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]) { future := hello()
str, err := resolveAfter1Second().Await() future.Then(func(value string) {
resolve("AsyncCall: " + str, err) println("first callback:", value)
}
// 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
}
}
}) })
return promise future.Then(func(value string) {
} println("second callback:", value)
// 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)
}) })
}
func asyncMain() { println("first await:", async.Await(future))
fmt.Println("Starting AsyncCall") println("second await:", async.Await(future))
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")
} }
``` ```

17
x/async/TODO.md Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
}

View 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{})
}()
})
}

View 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
View 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
View 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
}

View File

@@ -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())
}

View File

@@ -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
View 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
View 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
View 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
}