From 18de4e57ebc1c4e3466a61ba2dc0415535ddf7aa Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Mon, 22 Jul 2024 23:08:56 +0800 Subject: [PATCH 001/152] env:fix incorrect extra line breaks & subcmd match --- cl/_testlibc/demangle/in.go | 15 ++++++++ cl/_testlibc/demangle/out.ll | 69 ++++++++++++++++++++++++++++++++++++ internal/build/build.go | 2 +- xtool/env/env.go | 6 ++-- 4 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 cl/_testlibc/demangle/in.go create mode 100644 cl/_testlibc/demangle/out.ll diff --git a/cl/_testlibc/demangle/in.go b/cl/_testlibc/demangle/in.go new file mode 100644 index 00000000..d1b0d591 --- /dev/null +++ b/cl/_testlibc/demangle/in.go @@ -0,0 +1,15 @@ +package main + +import ( + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/cpp/llvm" +) + +func main() { + mangledName := "__ZNK9INIReader10ParseErrorEv" + if name := llvm.ItaniumDemangle(mangledName, true); name != nil { + c.Printf(c.Str("%s\n"), name) + } else { + println("Failed to demangle") + } +} diff --git a/cl/_testlibc/demangle/out.ll b/cl/_testlibc/demangle/out.ll new file mode 100644 index 00000000..a3886f6a --- /dev/null +++ b/cl/_testlibc/demangle/out.ll @@ -0,0 +1,69 @@ +; ModuleID = 'main' +source_filename = "main" + +%"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 } + +@"main.init$guard" = global i1 false, align 1 +@__llgo_argc = global i32 0, align 4 +@__llgo_argv = global ptr null, align 8 +@0 = private unnamed_addr constant [29 x i8] c"__ZNK9INIReader10ParseErrorEv", align 1 +@1 = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1 +@2 = private unnamed_addr constant [18 x i8] c"Failed to demangle", align 1 + +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 %"github.com/goplus/llgo/internal/runtime.String", align 8 + %3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %2, i32 0, i32 0 + store ptr @0, ptr %3, align 8 + %4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %2, i32 0, i32 1 + store i64 29, ptr %4, align 4 + %5 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %2, align 8 + %6 = call ptr @_ZN4llvm15itaniumDemangleENSt3__117basic_string_viewIcNS0_11char_traitsIcEEEEb(%"github.com/goplus/llgo/internal/runtime.String" %5, i1 true) + %7 = icmp ne ptr %6, null + br i1 %7, label %_llgo_1, label %_llgo_3 + +_llgo_1: ; preds = %_llgo_0 + %8 = call i32 (ptr, ...) @printf(ptr @1, ptr %6) + br label %_llgo_2 + +_llgo_2: ; preds = %_llgo_3, %_llgo_1 + ret i32 0 + +_llgo_3: ; preds = %_llgo_0 + %9 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %10 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %9, i32 0, i32 0 + store ptr @2, ptr %10, align 8 + %11 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %9, i32 0, i32 1 + store i64 18, ptr %11, align 4 + %12 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %9, align 8 + call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %12) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + br label %_llgo_2 +} + +declare void @"github.com/goplus/llgo/internal/runtime.init"() + +declare ptr @_ZN4llvm15itaniumDemangleENSt3__117basic_string_viewIcNS0_11char_traitsIcEEEEb(%"github.com/goplus/llgo/internal/runtime.String", i1) + +declare i32 @printf(ptr, ...) + +declare void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String") + +declare void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8) diff --git a/internal/build/build.go b/internal/build/build.go index 0d2165e5..a45a0251 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -283,7 +283,7 @@ func buildAllPkgs(ctx *context, initial []*packages.Package, verbose bool) (pkgs for _, param := range altParts { param = strings.TrimSpace(param) if strings.ContainsRune(param, '$') { - expd = strings.TrimSpace(env.ExpandEnv(param)) + expd = env.ExpandEnv(param) ctx.nLibdir++ } else { expd = param diff --git a/xtool/env/env.go b/xtool/env/env.go index ded23cff..062c043f 100644 --- a/xtool/env/env.go +++ b/xtool/env/env.go @@ -35,7 +35,7 @@ func ExpandEnv(s string) string { func expandEnvWithCmd(s string) string { expanded := reSubcmd.ReplaceAllStringFunc(s, func(m string) string { - subcmd := strings.TrimSpace(s[2 : len(s)-1]) + subcmd := strings.TrimSpace(m[2 : len(m)-1]) args := parseSubcmd(subcmd) @@ -55,9 +55,9 @@ func expandEnvWithCmd(s string) string { return "" } - return string(out) + return strings.Replace(strings.TrimSpace(string(out)), "\n", " ", -1) }) - return os.Expand(expanded, os.Getenv) + return strings.TrimSpace(os.Expand(expanded, os.Getenv)) } func parseSubcmd(s string) []string { From 0f8b202531728929f1b968c15c9a603d427c06d7 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Wed, 24 Jul 2024 14:22:41 +0800 Subject: [PATCH 002/152] c/clang:symbol dump demo --- c/clang/_demo/symboldump/symboldump.go | 121 +++ c/clang/_demo/symboldump/test.h | 40 + c/clang/_wrap/cursor.cpp | 72 +- c/clang/clang.go | 1196 +++++++++++++++++++++++- 4 files changed, 1408 insertions(+), 21 deletions(-) create mode 100644 c/clang/_demo/symboldump/symboldump.go create mode 100644 c/clang/_demo/symboldump/test.h diff --git a/c/clang/_demo/symboldump/symboldump.go b/c/clang/_demo/symboldump/symboldump.go new file mode 100644 index 00000000..4ec62fc1 --- /dev/null +++ b/c/clang/_demo/symboldump/symboldump.go @@ -0,0 +1,121 @@ +package main + +import ( + "fmt" + "os" + "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/clang" +) + +type Context struct { + namespaceName string + className string +} + +func newContext() *Context { + return &Context{} +} + +func (c *Context) setNamespaceName(name string) { + c.namespaceName = name +} + +func (c *Context) setClassName(name string) { + c.className = name +} + +var context = newContext() + +func print_cursor_info(cursor clang.Cursor) { + cursorStr := cursor.String() + symbol := cursor.Mangling() + defer symbol.Dispose() + defer cursorStr.Dispose() + + if context.namespaceName != "" && context.className != "" { + fmt.Printf("%s:%s", context.namespaceName, context.className) + } else if context.namespaceName != "" { + fmt.Printf("%s", context.namespaceName) + } + c.Printf(c.Str("%s\n"), cursorStr.CStr()) + + if cursor.Kind == clang.CXXMethod || cursor.Kind == clang.FunctionDecl { + c.Printf(c.Str("symbol:%s\n"), symbol.CStr()) + + typeStr := cursor.ResultType().String() + defer typeStr.Dispose() + c.Printf(c.Str("Return Type: %s\n"), typeStr.CStr()) + c.Printf(c.Str("Parameters(%d): ( "), cursor.NumArguments()) + + for i := 0; i < int(cursor.NumArguments()); i++ { + argCurSor := cursor.Argument(uint8(i)) + argType := argCurSor.Type().String() + argName := argCurSor.String() + c.Printf(c.Str("%s %s"), argType.CStr(), argName.CStr()) + if i < int(cursor.NumArguments())-1 { + c.Printf(c.Str(", ")) + } + + argType.Dispose() + argName.Dispose() + } + + c.Printf(c.Str(" )\n")) + println("--------------------------------") + } +} + +func visit(cursor, parent clang.Cursor, clientData c.Pointer) clang.ChildVisitResult { + if cursor.Kind == clang.Namespace { + nameStr := cursor.String() + context.setNamespaceName(c.GoString(nameStr.CStr())) + clang.VisitChildren(cursor, visit, nil) + context.setNamespaceName("") + } else if cursor.Kind == clang.ClassDecl { + nameStr := cursor.String() + context.setClassName(c.GoString(nameStr.CStr())) + clang.VisitChildren(cursor, visit, nil) + context.setClassName("") + } else if cursor.Kind == clang.CXXMethod || cursor.Kind == clang.FunctionDecl { + print_cursor_info(cursor) + } + + return clang.ChildVisit_Continue +} + +func parse(filename *c.Char) { + index := clang.CreateIndex(0, 0) + args := make([]*c.Char, 3) + args[0] = c.Str("-x") + args[1] = c.Str("c++") + args[2] = c.Str("-std=c++11") + unit := index.ParseTranslationUnit( + filename, + unsafe.SliceData(args), 3, + nil, 0, + clang.TranslationUnit_None, + ) + + if unit == nil { + println("Unable to parse translation unit. Quitting.") + c.Exit(1) + } + + cursor := unit.Cursor() + + clang.VisitChildren(cursor, visit, nil) + unit.Dispose() + index.Dispose() +} + +func main() { + if c.Argc != 2 { + fmt.Fprintln(os.Stderr, "Usage: \n") + return + } else { + sourceFile := *c.Advance(c.Argv, 1) + parse(sourceFile) + } +} diff --git a/c/clang/_demo/symboldump/test.h b/c/clang/_demo/symboldump/test.h new file mode 100644 index 00000000..2c72fd6a --- /dev/null +++ b/c/clang/_demo/symboldump/test.h @@ -0,0 +1,40 @@ +#ifndef ZOO_H +#define ZOO_H + +namespace forest +{ + +class Bear +{ + public: + void roar(); + int eat(int berries, int fish); + void sleep(const char *where); +}; + +} // namespace forest + +namespace ocean +{ + +class Shark +{ + public: + void roar(); + int eat(int fish, int seals); + void sleep(const char *where); +}; + +} // namespace ocean + +class Eagle +{ + public: + void roar(); + int eat(int mice, int fish); + void sleep(const char *where); +}; + +void zookeeper(int money); + +#endif // ZOO_H \ No newline at end of file diff --git a/c/clang/_wrap/cursor.cpp b/c/clang/_wrap/cursor.cpp index f757fe3d..3397d98a 100644 --- a/c/clang/_wrap/cursor.cpp +++ b/c/clang/_wrap/cursor.cpp @@ -1,29 +1,67 @@ -#include #include +#include -typedef enum CXChildVisitResult(* wrap_CXCursorVisitor) (CXCursor *cursor, CXCursor *parent, CXClientData client_data); +typedef enum CXChildVisitResult (*wrap_CXCursorVisitor)(CXCursor *cursor, CXCursor *parent, CXClientData client_data); -typedef struct { - CXClientData data; +typedef struct +{ + CXClientData data; wrap_CXCursorVisitor visitor; } wrap_data; -CXChildVisitResult wrap_visitor(CXCursor cursor, CXCursor parent, CXClientData data) { - wrap_data *d = (wrap_data*)(data); - return d->visitor(&cursor,&parent,d->data); +CXChildVisitResult wrap_visitor(CXCursor cursor, CXCursor parent, CXClientData data) +{ + wrap_data *d = (wrap_data *)(data); + return d->visitor(&cursor, &parent, d->data); } -extern "C" { +extern "C" +{ -CXString wrap_clang_getCursorSpelling(CXCursor *cur) { - return clang_getCursorSpelling(*cur); -} + CXString wrap_clang_getCursorSpelling(CXCursor *cur) + { + return clang_getCursorSpelling(*cur); + } -unsigned wrap_clang_visitChildren(CXCursor *parent, - wrap_CXCursorVisitor visitor, - CXClientData client_data) { - wrap_data data = {client_data,visitor}; - return clang_visitChildren(*parent,wrap_visitor,CXClientData(&data)); -} + CXString wrap_clang_Cursor_getMangling(CXCursor *cur) + { + return clang_Cursor_getMangling(*cur); + } + + int wrap_clang_Cursor_getNumArguments(CXCursor *cur) + { + return clang_Cursor_getNumArguments(*cur); + } + + void wrap_clang_Cursor_getArgument(CXCursor *C, unsigned i, CXCursor *argCur) + { + *argCur = clang_Cursor_getArgument(*C, i); + } + + void wrap_clang_getTranslationUnitCursor(CXTranslationUnit uint, CXCursor *cur) + { + *cur = clang_getTranslationUnitCursor(uint); + } + + void wrap_clang_getCursorType(CXCursor *cur, CXType *typ) + { + *typ = clang_getCursorType(*cur); + } + + void wrap_clang_getCursorResultType(CXCursor *cur, CXType *typ) + { + *typ = clang_getCursorResultType(*cur); + } + + CXString wrap_clang_getTypeSpelling(CXType *typ) + { + return clang_getTypeSpelling(*typ); + } + + unsigned wrap_clang_visitChildren(CXCursor *parent, wrap_CXCursorVisitor visitor, CXClientData client_data) + { + wrap_data data = {client_data, visitor}; + return clang_visitChildren(*parent, wrap_visitor, CXClientData(&data)); + } } // extern "C" diff --git a/c/clang/clang.go b/c/clang/clang.go index fe905e92..76527baa 100644 --- a/c/clang/clang.go +++ b/c/clang/clang.go @@ -162,11 +162,14 @@ func (*TranslationUnit) Dispose() {} * The translation unit cursor can be used to start traversing the * various declarations within the given translation unit. */ -// llgo:link (*TranslationUnit).Cursor C.clang_getTranslationUnitCursor -func (*TranslationUnit) Cursor() (ret Cursor) { +func (p *TranslationUnit) Cursor() (ret Cursor) { + p.wrapCursor(&ret) return } +//llgo:link (*TranslationUnit).wrapCursor C.wrap_clang_getTranslationUnitCursor +func (t *TranslationUnit) wrapCursor(cursor *Cursor) {} + /** * Describes the kind of entity that a cursor refers to. */ @@ -178,6 +181,1104 @@ func (CursorKind) String() (ret String) { return } +const ( + /* Declarations */ + /** + * A declaration whose specific kind is not exposed via this + * interface. + * + * Unexposed declarations have the same operations as any other kind + * of declaration; one can extract their location information, + * spelling, find their definitions, etc. However, the specific kind + * of the declaration is not reported. + */ + UnexposedDecl CursorKind = iota + 1 + + /** A C or C++ struct. */ + StructDecl + + /** A C or C++ union. */ + UnionDecl + + /** A C++ class. */ + ClassDecl + + /** An enumeration. */ + EnumDecl + + /** + * A field (in C) or non-static data member (in C++) in a + * struct, union, or C++ class. + */ + FieldDecl + + /** An enumerator constant. */ + EnumConstantDecl + + /** A function. */ + FunctionDecl + + /** A variable. */ + VarDecl + + /** A function or method parameter. */ + ParmDecl + + /** An Objective-C \@interface. */ + ObjCInterfaceDecl + + /** An Objective-C \@interface for a category. */ + ObjCCategoryDecl + + /** An Objective-C \@protocol declaration. */ + ObjCProtocolDecl + + /** An Objective-C \@property declaration. */ + ObjCPropertyDecl + + /** An Objective-C instance variable. */ + ObjCIvarDecl + + /** An Objective-C instance method. */ + ObjCInstanceMethodDecl + + /** An Objective-C class method. */ + ObjCClassMethodDecl + + /** An Objective-C \@implementation. */ + ObjCImplementationDecl + + /** An Objective-C \@implementation for a category. */ + ObjCCategoryImplDecl + + /** A typedef. */ + TypedefDecl + + /** A C++ class method. */ + CXXMethod + + /** A C++ namespace. */ + Namespace + + /** A linkage specification, e.g. 'extern "C"'. */ + LinkageSpec + + /** A C++ constructor. */ + Constructor + + /** A C++ destructor. */ + Destructor + + /** A C++ conversion function. */ + ConversionFunction + + /** A C++ template type parameter. */ + TemplateTypeParameter + + /** A C++ non-type template parameter. */ + NonTypeTemplateParameter + + /** A C++ template template parameter. */ + TemplateTemplateParameter + + /** A C++ function template. */ + FunctionTemplate + + /** A C++ class template. */ + ClassTemplate + + /** A C++ class template partial specialization. */ + ClassTemplatePartialSpecialization + + /** A C++ namespace alias declaration. */ + NamespaceAlias + + /** A C++ using directive. */ + UsingDirective + + /** A C++ using declaration. */ + UsingDeclaration + + /** A C++ alias declaration */ + TypeAliasDecl + + /** An Objective-C \@synthesize definition. */ + ObjCSynthesizeDecl + + /** An Objective-C \@dynamic definition. */ + ObjCDynamicDecl + + /** An access specifier. */ + CXXAccessSpecifier + + FirstDecl = UnexposedDecl + LastDecl = CXXAccessSpecifier + + /* References */ + FirstRef = 40 + ObjCSuperClassRef = iota - 2 //40 + ObjCProtocolRef + ObjCClassRef + + /** + * A reference to a type declaration. + * + * A type reference occurs anywhere where a type is named but not + * declared. For example, given: + * + * \code + * typedef unsigned size_type; + * size_type size; + * \endcode + * + * The typedef is a declaration of size_type (CXCursor_TypedefDecl), + * while the type of the variable "size" is referenced. The cursor + * referenced by the type of size is the typedef for size_type. + */ + TypeRef + + CXXBaseSpecifier + + /** + * A reference to a class template, function template, template + * template parameter, or class template partial specialization. + */ + TemplateRef + + /** + * A reference to a namespace or namespace alias. + */ + NamespaceRef + + /** + * A reference to a member of a struct, union, or class that occurs in + * some non-expression context, e.g., a designated initializer. + */ + MemberRef + + /** + * A reference to a labeled statement. + * + * This cursor kind is used to describe the jump to "start_over" in the + * goto statement in the following example: + * + * \code + * start_over: + * ++counter; + * + * goto start_over; + * \endcode + * + * A label reference cursor refers to a label statement. + */ + LabelRef + + /** + * A reference to a set of overloaded functions or function templates + * that has not yet been resolved to a specific function or function template. + * + * An overloaded declaration reference cursor occurs in C++ templates where + * a dependent name refers to a function. For example: + * + * \code + * template void swap(T&, T&); + * + * struct X { ... }; + * void swap(X&, X&); + * + * template + * void reverse(T* first, T* last) { + * while (first < last - 1) { + * swap(*first, *--last); + * ++first; + * } + * } + * + * struct Y { }; + * void swap(Y&, Y&); + * \endcode + * + * Here, the identifier "swap" is associated with an overloaded declaration + * reference. In the template definition, "swap" refers to either of the two + * "swap" functions declared above, so both results will be available. At + * instantiation time, "swap" may also refer to other functions found via + * argument-dependent lookup (e.g., the "swap" function at the end of the + * example). + * + * The functions \c clang_getNumOverloadedDecls() and + * \c clang_getOverloadedDecl() can be used to retrieve the definitions + * referenced by this cursor. + */ + OverloadedDeclRef + + /** + * A reference to a variable that occurs in some non-expression + * context, e.g., a C++ lambda capture list. + */ + VariableRef + + LastRef = VariableRef + + /* Error conditions */ + FirstInvalid = 70 + InvalidFile = iota + 15 //70 + NoDeclFound + NotImplemented + InvalidCode + LastInvalid = InvalidCode + + /* Expressions */ + FirstExpr = 100 + + /** + * An expression whose specific kind is not exposed via this + * interface. + * + * Unexposed expressions have the same operations as any other kind + * of expression; one can extract their location information, + * spelling, children, etc. However, the specific kind of the + * expression is not reported. + */ + UnexposedExpr = iota + 39 //100 + + /** + * An expression that refers to some value declaration, such + * as a function, variable, or enumerator. + */ + DeclRefExpr + + /** + * An expression that refers to a member of a struct, union, + * class, Objective-C class, etc. + */ + MemberRefExpr + + /** An expression that calls a function. */ + CallExpr + + /** An expression that sends a message to an Objective-C + object or class. */ + ObjCMessageExpr + + /** An expression that represents a block literal. */ + BlockExpr + + /** An integer literal. + */ + IntegerLiteral + + /** A floating point number literal. + */ + FloatingLiteral + + /** An imaginary number literal. + */ + ImaginaryLiteral + + /** A string literal. + */ + StringLiteral + + /** A character literal. + */ + CharacterLiteral + + /** A parenthesized expression, e.g. "(1)". + * + * This AST node is only formed if full location information is requested. + */ + ParenExpr + + /** This represents the unary-expression's (except sizeof and + * alignof). + */ + UnaryOperator + + /** [C99 6.5.2.1] Array Subscripting. + */ + ArraySubscriptExpr + + /** A builtin binary operation expression such as "x + y" or + * "x <= y". + */ + BinaryOperator + + /** Compound assignment such as "+=". + */ + CompoundAssignOperator + + /** The ?: ternary operator. + */ + ConditionalOperator + + /** An explicit cast in C (C99 6.5.4) or a C-style cast in C++ + * (C++ [expr.cast]), which uses the syntax (Type)expr. + * + * For example: (int)f. + */ + CStyleCastExpr + + /** [C99 6.5.2.5] + */ + CompoundLiteralExpr + + /** Describes an C or C++ initializer list. + */ + InitListExpr + + /** The GNU address of label extension, representing &&label. + */ + AddrLabelExpr + + /** This is the GNU Statement Expression extension: ({int X=4; X;}) + */ + StmtExpr + + /** Represents a C11 generic selection. + */ + GenericSelectionExpr + + /** Implements the GNU __null extension, which is a name for a null + * pointer constant that has integral type (e.g., int or long) and is the same + * size and alignment as a pointer. + * + * The __null extension is typically only used by system headers, which define + * NULL as __null in C++ rather than using 0 (which is an integer that may not + * match the size of a pointer). + */ + GNUNullExpr + + /** C++'s static_cast<> expression. + */ + CXXStaticCastExpr + + /** C++'s dynamic_cast<> expression. + */ + CXXDynamicCastExpr + + /** C++'s reinterpret_cast<> expression. + */ + CXXReinterpretCastExpr + + /** C++'s const_cast<> expression. + */ + CXXConstCastExpr + + /** Represents an explicit C++ type conversion that uses "functional" + * notion (C++ [expr.type.conv]). + * + * Example: + * \code + * x = int(0.5); + * \endcode + */ + CXXFunctionalCastExpr + + /** A C++ typeid expression (C++ [expr.typeid]). + */ + CXXTypeidExpr + + /** [C++ 2.13.5] C++ Boolean Literal. + */ + CXXBoolLiteralExpr + + /** [C++0x 2.14.7] C++ Pointer Literal. + */ + CXXNullPtrLiteralExpr + + /** Represents the "this" expression in C++ + */ + CXXThisExpr + + /** [C++ 15] C++ Throw Expression. + * + * This handles 'throw' and 'throw' assignment-expression. When + * assignment-expression isn't present, Op will be null. + */ + CXXThrowExpr + + /** A new expression for memory allocation and constructor calls, e.g: + * "new CXXNewExpr(foo)". + */ + CXXNewExpr + + /** A delete expression for memory deallocation and destructor calls, + * e.g. "delete[] pArray". + */ + CXXDeleteExpr + + /** A unary expression. (noexcept, sizeof, or other traits) + */ + UnaryExpr + + /** An Objective-C string literal i.e. @"foo". + */ + ObjCStringLiteral + + /** An Objective-C \@encode expression. + */ + ObjCEncodeExpr + + /** An Objective-C \@selector expression. + */ + ObjCSelectorExpr + + /** An Objective-C \@protocol expression. + */ + ObjCProtocolExpr + + /** An Objective-C "bridged" cast expression, which casts between + * Objective-C pointers and C pointers, transferring ownership in the process. + * + * \code + * NSString *str = (__bridge_transfer NSString *)CFCreateString(); + * \endcode + */ + ObjCBridgedCastExpr + + /** Represents a C++0x pack expansion that produces a sequence of + * expressions. + * + * A pack expansion expression contains a pattern (which itself is an + * expression) followed by an ellipsis. For example: + * + * \code + * template + * void forward(F f, Types &&...args) { + * f(static_cast(args)...); + * } + * \endcode + */ + PackExpansionExpr + + /** Represents an expression that computes the length of a parameter + * pack. + * + * \code + * template + * struct count { + * static const unsigned value = sizeof...(Types); + * }; + * \endcode + */ + SizeOfPackExpr + /* Represents a C++ lambda expression that produces a local function + * object. + * + * \code + * void abssort(float *x, unsigned N) { + * std::sort(x, x + N, + * [](float a, float b) { + * return std::abs(a) < std::abs(b); + * }); + * } + * \endcode + */ + LambdaExpr + + /** Objective-c Boolean Literal. + */ + ObjCBoolLiteralExpr + + /** Represents the "self" expression in an Objective-C method. + */ + ObjCSelfExpr + + /** OpenMP 5.0 [2.1.5, Array Section]. + */ + OMPArraySectionExpr + + /** Represents an @available(...) check. + */ + ObjCAvailabilityCheckExpr + + /** + * Fixed point literal + */ + FixedPointLiteral + + /** OpenMP 5.0 [2.1.4, Array Shaping]. + */ + OMPArrayShapingExpr + + /** + * OpenMP 5.0 [2.1.6 Iterators] + */ + OMPIteratorExpr + + /** OpenCL's addrspace_cast<> expression. + */ + CXXAddrspaceCastExpr + + /** + * Expression that references a C++20 concept. + */ + ConceptSpecializationExpr + + /** + * Expression that references a C++20 concept. + */ + RequiresExpr + + /** + * Expression that references a C++20 parenthesized list aggregate + * initializer. + */ + CXXParenListInitExpr + + LastExpr = CXXParenListInitExpr + + /* Statements */ + FirstStmt = 200 + /** + * A statement whose specific kind is not exposed via this + * interface. + * + * Unexposed statements have the same operations as any other kind of + * statement; one can extract their location information, spelling, + * children, etc. However, the specific kind of the statement is not + * reported. + */ + UnexposedStmt = iota + 81 //200 + + /** A labelled statement in a function. + * + * This cursor kind is used to describe the "start_over:" label statement in + * the following example: + * + * \code + * start_over: + * ++counter; + * \endcode + * + */ + LabelStmt + + /** A group of statements like { stmt stmt }. + * + * This cursor kind is used to describe compound statements, e.g. function + * bodies. + */ + CompoundStmt + + /** A case statement. + */ + CaseStmt + + /** A default statement. + */ + DefaultStmt + + /** An if statement + */ + IfStmt + + /** A switch statement. + */ + SwitchStmt + + /** A while statement. + */ + WhileStmt + + /** A do statement. + */ + DoStmt + + /** A for statement. + */ + ForStmt + + /** A goto statement. + */ + GotoStmt + + /** An indirect goto statement. + */ + IndirectGotoStmt + + /** A continue statement. + */ + ContinueStmt + + /** A break statement. + */ + BreakStmt + + /** A return statement. + */ + ReturnStmt + + /** A GCC inline assembly statement extension. + */ + GCCAsmStmt + AsmStmt = GCCAsmStmt + + /** Objective-C's overall \@try-\@catch-\@finally statement. + */ + ObjCAtTryStmt = iota + 80 //216 + + /** Objective-C's \@catch statement. + */ + ObjCAtCatchStmt + + /** Objective-C's \@finally statement. + */ + ObjCAtFinallyStmt + + /** Objective-C's \@throw statement. + */ + ObjCAtThrowStmt + + /** Objective-C's \@synchronized statement. + */ + ObjCAtSynchronizedStmt + + /** Objective-C's autorelease pool statement. + */ + ObjCAutoreleasePoolStmt + + /** Objective-C's collection statement. + */ + ObjCForCollectionStmt + + /** C++'s catch statement. + */ + CXXCatchStmt + + /** C++'s try statement. + */ + CXXTryStmt + + /** C++'s for (* : *) statement. + */ + CXXForRangeStmt + + /** Windows Structured Exception Handling's try statement. + */ + SEHTryStmt + + /** Windows Structured Exception Handling's except statement. + */ + SEHExceptStmt + + /** Windows Structured Exception Handling's finally statement. + */ + SEHFinallyStmt + + /** A MS inline assembly statement extension. + */ + MSAsmStmt + + /** The null statement ";": C99 6.8.3p3. + * + * This cursor kind is used to describe the null statement. + */ + NullStmt + + /** Adaptor class for mixing declarations with statements and + * expressions. + */ + DeclStmt + + /** OpenMP parallel directive. + */ + OMPParallelDirective + + /** OpenMP SIMD directive. + */ + OMPSimdDirective + + /** OpenMP for directive. + */ + OMPForDirective + + /** OpenMP sections directive. + */ + OMPSectionsDirective + + /** OpenMP section directive. + */ + OMPSectionDirective + + /** OpenMP single directive. + */ + OMPSingleDirective + + /** OpenMP parallel for directive. + */ + OMPParallelForDirective + + /** OpenMP parallel sections directive. + */ + OMPParallelSectionsDirective + + /** OpenMP task directive. + */ + OMPTaskDirective + + /** OpenMP master directive. + */ + OMPMasterDirective + + /** OpenMP critical directive. + */ + OMPCriticalDirective + + /** OpenMP taskyield directive. + */ + OMPTaskyieldDirective + + /** OpenMP barrier directive. + */ + OMPBarrierDirective + + /** OpenMP taskwait directive. + */ + OMPTaskwaitDirective + + /** OpenMP flush directive. + */ + OMPFlushDirective + + /** Windows Structured Exception Handling's leave statement. + */ + SEHLeaveStmt + + /** OpenMP ordered directive. + */ + OMPOrderedDirective + + /** OpenMP atomic directive. + */ + OMPAtomicDirective + + /** OpenMP for SIMD directive. + */ + OMPForSimdDirective + + /** OpenMP parallel for SIMD directive. + */ + OMPParallelForSimdDirective + + /** OpenMP target directive. + */ + OMPTargetDirective + + /** OpenMP teams directive. + */ + OMPTeamsDirective + + /** OpenMP taskgroup directive. + */ + OMPTaskgroupDirective + + /** OpenMP cancellation point directive. + */ + OMPCancellationPointDirective + + /** OpenMP cancel directive. + */ + OMPCancelDirective + + /** OpenMP target data directive. + */ + OMPTargetDataDirective + + /** OpenMP taskloop directive. + */ + OMPTaskLoopDirective + + /** OpenMP taskloop simd directive. + */ + OMPTaskLoopSimdDirective + + /** OpenMP distribute directive. + */ + OMPDistributeDirective + + /** OpenMP target enter data directive. + */ + OMPTargetEnterDataDirective + + /** OpenMP target exit data directive. + */ + OMPTargetExitDataDirective + + /** OpenMP target parallel directive. + */ + OMPTargetParallelDirective + + /** OpenMP target parallel for directive. + */ + OMPTargetParallelForDirective + + /** OpenMP target update directive. + */ + OMPTargetUpdateDirective + + /** OpenMP distribute parallel for directive. + */ + OMPDistributeParallelForDirective + + /** OpenMP distribute parallel for simd directive. + */ + OMPDistributeParallelForSimdDirective + + /** OpenMP distribute simd directive. + */ + OMPDistributeSimdDirective + + /** OpenMP target parallel for simd directive. + */ + OMPTargetParallelForSimdDirective + + /** OpenMP target simd directive. + */ + OMPTargetSimdDirective + + /** OpenMP teams distribute directive. + */ + OMPTeamsDistributeDirective + + /** OpenMP teams distribute simd directive. + */ + OMPTeamsDistributeSimdDirective + + /** OpenMP teams distribute parallel for simd directive. + */ + OMPTeamsDistributeParallelForSimdDirective + + /** OpenMP teams distribute parallel for directive. + */ + OMPTeamsDistributeParallelForDirective + + /** OpenMP target teams directive. + */ + OMPTargetTeamsDirective + + /** OpenMP target teams distribute directive. + */ + OMPTargetTeamsDistributeDirective + + /** OpenMP target teams distribute parallel for directive. + */ + OMPTargetTeamsDistributeParallelForDirective + + /** OpenMP target teams distribute parallel for simd directive. + */ + OMPTargetTeamsDistributeParallelForSimdDirective + + /** OpenMP target teams distribute simd directive. + */ + OMPTargetTeamsDistributeSimdDirective + + /** C++2a std::bit_cast expression. + */ + BuiltinBitCastExpr + + /** OpenMP master taskloop directive. + */ + OMPMasterTaskLoopDirective + + /** OpenMP parallel master taskloop directive. + */ + OMPParallelMasterTaskLoopDirective + + /** OpenMP master taskloop simd directive. + */ + OMPMasterTaskLoopSimdDirective + + /** OpenMP parallel master taskloop simd directive. + */ + OMPParallelMasterTaskLoopSimdDirective + + /** OpenMP parallel master directive. + */ + OMPParallelMasterDirective + + /** OpenMP depobj directive. + */ + OMPDepobjDirective + + /** OpenMP scan directive. + */ + OMPScanDirective + + /** OpenMP tile directive. + */ + OMPTileDirective + + /** OpenMP canonical loop. + */ + OMPCanonicalLoop + + /** OpenMP interop directive. + */ + OMPInteropDirective + + /** OpenMP dispatch directive. + */ + OMPDispatchDirective + + /** OpenMP masked directive. + */ + OMPMaskedDirective + + /** OpenMP unroll directive. + */ + OMPUnrollDirective + + /** OpenMP metadirective directive. + */ + OMPMetaDirective + + /** OpenMP loop directive. + */ + OMPGenericLoopDirective + + /** OpenMP teams loop directive. + */ + OMPTeamsGenericLoopDirective + + /** OpenMP target teams loop directive. + */ + OMPTargetTeamsGenericLoopDirective + + /** OpenMP parallel loop directive. + */ + OMPParallelGenericLoopDirective + + /** OpenMP target parallel loop directive. + */ + OMPTargetParallelGenericLoopDirective + + /** OpenMP parallel masked directive. + */ + OMPParallelMaskedDirective + + /** OpenMP masked taskloop directive. + */ + OMPMaskedTaskLoopDirective + + /** OpenMP masked taskloop simd directive. + */ + OMPMaskedTaskLoopSimdDirective + + /** OpenMP parallel masked taskloop directive. + */ + OMPParallelMaskedTaskLoopDirective + + /** OpenMP parallel masked taskloop simd directive. + */ + OMPParallelMaskedTaskLoopSimdDirective + + /** OpenMP error directive. + */ + OMPErrorDirective + + /** OpenMP scope directive. + */ + OMPScopeDirective + + LastStmt = OMPScopeDirective + + /** + * Cursor that represents the translation unit itself. + * + * The translation unit cursor exists primarily to act as the root + * cursor for traversing the contents of a translation unit. + */ + CursorTranslationUnit = 350 + + /* Attributes */ + FirstAttr = 400 + /** + * An attribute whose specific kind is not exposed via this + * interface. + */ + UnexposedAttr = iota + 170 + + IBActionAttr + IBOutletAttr + IBOutletCollectionAttr + CXXFinalAttr + CXXOverrideAttr + AnnotateAttr + AsmLabelAttr + PackedAttr + PureAttr + ConstAttr + NoDuplicateAttr + CUDAConstantAttr + CUDADeviceAttr + CUDAGlobalAttr + CUDAHostAttr + CUDASharedAttr + VisibilityAttr + DLLExport + DLLImport + NSReturnsRetained + NSReturnsNotRetained + NSReturnsAutoreleased + NSConsumesSelf + NSConsumed + ObjCException + ObjCNSObject + ObjCIndependentClass + ObjCPreciseLifetime + ObjCReturnsInnerPointer + ObjCRequiresSuper + ObjCRootClass + ObjCSubclassingRestricted + ObjCExplicitProtocolImpl + ObjCDesignatedInitializer + ObjCRuntimeVisible + ObjCBoxable + FlagEnum + ConvergentAttr + WarnUnusedAttr + WarnUnusedResultAttr + AlignedAttr + LastAttr = AlignedAttr + + /* Preprocessing */ + PreprocessingDirective = iota + 227 //500 + MacroDefinition + MacroExpansion + MacroInstantiation = MacroExpansion + InclusionDirective = 503 + FirstPreprocessing = PreprocessingDirective + LastPreprocessing = InclusionDirective + + /* Extra Declarations */ + /** + * A module import declaration. + */ + ModuleImportDecl = iota + 320 //600 + TypeAliasTemplateDecl + /** + * A static_assert or _Static_assert node + */ + StaticAssert + /** + * a friend declaration. + */ + FriendDecl + /** + * a concept declaration. + */ + ConceptDecl + + FirstExtraDecl = ModuleImportDecl + LastExtraDecl = ConceptDecl + + /** + * A code completion overload candidate. + */ + OverloadCandidate = 700 +) + /** * A cursor representing some element in the abstract syntax tree for * a translation unit. @@ -198,8 +1299,13 @@ func (CursorKind) String() (ret String) { */ type Cursor struct { Kind CursorKind - Xdata c.Int - Data [3]c.Pointer + xdata c.Int + data [3]c.Pointer +} + +type Type struct { + Kind CursorKind + data [2]c.Pointer } /** @@ -214,6 +1320,88 @@ func (c Cursor) String() (ret String) { return c.wrapString() } +/** + * Retrieve a name for the entity referenced by this cursor. + */ +// llgo:link (*Cursor).wrapMangling C.wrap_clang_Cursor_getMangling +func (*Cursor) wrapMangling() (ret String) { + return +} + +func (c Cursor) Mangling() (ret String) { + return c.wrapMangling() +} + +/** + * Retrieve the type of a CXCursor (if any). + */ +// llgo:link (*Cursor).wrapType C.wrap_clang_getCursorType +func (c *Cursor) wrapType(ret *Type) {} + +func (c *Cursor) Type() (ret Type) { + c.wrapType(&ret) + return +} + +/** + * Retrieve the return type associated with a given cursor. + * + * This only returns a valid type if the cursor refers to a function or method. + */ +// llgo:link (*Cursor).wrapResultType C.wrap_clang_getCursorResultType +func (c *Cursor) wrapResultType(ret *Type) {} + +func (c *Cursor) ResultType() (ret Type) { + c.wrapResultType(&ret) + return +} + +/** + * Retrieve the number of non-variadic arguments associated with a given + * cursor. + * + * The number of arguments can be determined for calls as well as for + * declarations of functions or methods. For other cursors -1 is returned. + */ +// llgo:link (*Cursor).wrapNumArguments C.wrap_clang_Cursor_getNumArguments +func (*Cursor) wrapNumArguments() (num c.Int) { + return 0 +} + +func (c Cursor) NumArguments() (num c.Int) { + return c.wrapNumArguments() +} + +/** + * Retrieve the argument cursor of a function or method. + * + * The argument cursor can be determined for calls as well as for declarations + * of functions or methods. For other cursors and for invalid indices, an + * invalid cursor is returned. + */ +// llgo:link (*Cursor).wrapArgument C.wrap_clang_Cursor_getArgument +func (*Cursor) wrapArgument(index uint8, arg *Cursor) {} + +func (c Cursor) Argument(index uint8) (arg Cursor) { + c.wrapArgument(index, &arg) + return +} + +/** + * Pretty-print the underlying type using the rules of the + * language of the translation unit from which it came. + * + * If the type is invalid, an empty string is returned. + */ +// llgo:link (*Type).wrapString C.wrap_clang_getTypeSpelling +func (t *Type) wrapString() (ret String) { + return +} + +func (t Type) String() (ret String) { + return t.wrapString() +} + /** * Describes how the traversal of the children of a particular * cursor should proceed after visiting a particular child cursor. From 400197def854ef261cb3a57bbfe9e5b6ab954cdf Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Sun, 14 Jul 2024 18:12:21 +0800 Subject: [PATCH 003/152] doc:Rust-to-LLGO-Migration-Guide.md --- doc/Rust-to-LLGO-Migration-Guide.md | 125 ++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 doc/Rust-to-LLGO-Migration-Guide.md diff --git a/doc/Rust-to-LLGO-Migration-Guide.md b/doc/Rust-to-LLGO-Migration-Guide.md new file mode 100644 index 00000000..253aca82 --- /dev/null +++ b/doc/Rust-to-LLGO-Migration-Guide.md @@ -0,0 +1,125 @@ +# Rust to LLGO Migration Document + +### Add Dependencies & Build Configuration + +Edit `Cargo.toml` to include necessary dependencies and configuration: + +```toml +[dependencies] +libc = "0.2" +csv = "1.1" + +[lib] +crate-type = ["cdylib"] # The generated dynamic library will conform to the C standard +``` + +### Import C Language Types + +Use types from the libc package for interoperability with C: + +```rust +use libc::{c_int, c_char, strlen}; +``` + +### Function Decoration and Attributes + +To ensure that Rust functions can be correctly called by C and LLGO, use the following decorators: + +- `#[no_mangle]` prevents the compiler from mangling the function name. + +- `unsafe` is used to mark operations that are unsafe, especially when dealing with raw pointers. + +- `extern "C"` specifies the use of C calling conventions. + +### Memory Management + +Use `Box` to manage dynamic memory to ensure correct memory release between Rust and C: + +```rust +pub unsafe extern "C" fn sled_create_config() -> \*mut Config { +Box::into_raw(Box::new(Config::new())) +} + +#[no_mangle] +pub unsafe extern "C" fn sled_free_config(config: \*mut Config) { +drop(Box::from_raw(config)); +} +``` + +### Handling Generic Pointers + +Address the interfacing issues between generic pointers in C and Rust: + +```rust +#[no_mangle] +pub extern "C" fn csv_reader_new(file_path: *const c_char) -> *mut c_void { /* ... */ } + +#[no_mangle] +pub extern "C" fn csv_reader_free(ptr: *mut c_void) { /* ... */ } +``` + +### String Handling + +Convert strings between C and Rust: + +```rust +#[no_mangle] +pub extern "C" fn csv_reader_read_record(ptr: *mut c_void) -> *const c_char { /* ... */ } + +#[no_mangle] +pub extern "C" fn free_string(s: *mut c_char) { + unsafe { + let c_string = CString::from_raw(s); + drop(c_string); + } +} +``` + +### Compilation and Installation + +Build the dynamic library and use `dylib-installer` to install it to the system path: + +```sh +cargo build --release +cargo install --git https://github.com/hackerchai/dylib-installer +sudo dylib_installer ./target/release/ +``` + +### Generate Header File + +Use cbindgen to generate a C header file, automating this process through a `build.rs` script: + +```rust +fn main() { + let config = cbindgen::Config::from_file("cbindgen.toml").expect("Config file not found."); + cbindgen::generate_with_config(&crate_dir, config).unwrap().write_to_file("target/include/csv_wrapper.h"); +} + +``` + +### LLGO Package Mapping + +Map functions from the Rust library to an LLGO package, ensuring type consistency: + +```go +const ( + LLGoPackage = "link: $(pkg-config --libs csv_wrapper); -lcsv_wrapper" +) + +type Reader struct { + Unused [8]byte +} + +//go:linkname NewReader C.csv_reader_new +func NewReader(file_path *c.Char) *Reader + +//llgo:link (*Reader).Free C.csv_reader_free +func (reader *Reader) Free() {} + +//llgo:link (*Reader).ReadRecord C.csv_reader_read_record +func (reader *Reader) ReadRecord() *c.Char { return nil } +``` + +### Writing Examples and README + +Provide example code and a detailed README file to help users understand how to use the generated library. From c0eeedc71ac4360f31f1963ce9926ff17a5ad2b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E8=8B=B1=E6=9D=B0?= Date: Wed, 17 Jul 2024 10:00:48 +0800 Subject: [PATCH 004/152] llgo/rust/readme: update readme --- doc/Rust-to-LLGO-Migration-Guide.md | 149 +++++++++++++++++++++++++--- 1 file changed, 134 insertions(+), 15 deletions(-) diff --git a/doc/Rust-to-LLGO-Migration-Guide.md b/doc/Rust-to-LLGO-Migration-Guide.md index 253aca82..48ae4609 100644 --- a/doc/Rust-to-LLGO-Migration-Guide.md +++ b/doc/Rust-to-LLGO-Migration-Guide.md @@ -11,6 +11,9 @@ csv = "1.1" [lib] crate-type = ["cdylib"] # The generated dynamic library will conform to the C standard + +[build-dependencies] +cbindgen = "0.26.0" ``` ### Import C Language Types @@ -26,11 +29,37 @@ use libc::{c_int, c_char, strlen}; To ensure that Rust functions can be correctly called by C and LLGO, use the following decorators: - `#[no_mangle]` prevents the compiler from mangling the function name. - - `unsafe` is used to mark operations that are unsafe, especially when dealing with raw pointers. - - `extern "C"` specifies the use of C calling conventions. +For example, in Rust, we write CSV code like this: + +```rust +use csv::ReaderBuilder; +use std::error::Error; + +fn main() -> Result<(), Box> { + // Define the CSV file path + let file_path = "example.csv"; + + // Creating a CSV Reader + let mut reader = ReaderBuilder::new().from_path(file_path)?; + + // Define a container to store records + let mut record = csv::StringRecord::new(); + + // Read records one by one + while reader.read_record(&mut record)? { + // Print each record + println!("{:?}", record); + } + + Ok(()) +} +``` + +If we need to migrate the CSV library to LLGO, we need to encapsulate each CSV method with a C API, such as the following: + ### Memory Management Use `Box` to manage dynamic memory to ensure correct memory release between Rust and C: @@ -75,18 +104,16 @@ pub extern "C" fn free_string(s: *mut c_char) { } ``` -### Compilation and Installation - -Build the dynamic library and use `dylib-installer` to install it to the system path: - -```sh -cargo build --release -cargo install --git https://github.com/hackerchai/dylib-installer -sudo dylib_installer ./target/release/ -``` - ### Generate Header File +Edit `cbindgen.toml` to configure the header file generation rules: + +```toml +# See https://github.com/mozilla/cbindgen/blob/master/docs.md#cbindgentoml for +# a list of possible configuration values. +language = "C" +``` + Use cbindgen to generate a C header file, automating this process through a `build.rs` script: ```rust @@ -94,32 +121,124 @@ fn main() { let config = cbindgen::Config::from_file("cbindgen.toml").expect("Config file not found."); cbindgen::generate_with_config(&crate_dir, config).unwrap().write_to_file("target/include/csv_wrapper.h"); } +``` +### Compilation and Installation + +Build the dynamic library: + +```sh +cargo build --release +``` + +### Install dylib-installer + +Install the [dylib-installer](https://github.com/hackerchai/dylib-installer) tool, which is used to install dynamic libraries: + +```SH +brew tap hackerchai/tap +brew install dylib-installer +``` + +Or you can install it using Cargo: + +```SH +cargo install dylib_installer +``` + +### Install Dynamic Library + +Use dylib-installer to install the built dynamic library and the header file into the system directory: + +```SH +sudo dylib_installer +``` + +### Check the Installation + +You can check the installation by running the following command: + +```SH +pkg-config --libs --cflags +``` + +if everything is installed correctly, you will see the output like this (depending on your system): + +```SH +-I/usr/local/include -L/usr/local/lib -l ``` ### LLGO Package Mapping Map functions from the Rust library to an LLGO package, ensuring type consistency: +- LLGoPackage + +Specify `LLGoPackage` and use `pkg-config` to find the location of the lib library. + ```go const ( LLGoPackage = "link: $(pkg-config --libs csv_wrapper); -lcsv_wrapper" ) +``` +- Type + +If you want to use variables inside the struct, you can add them accordingly. +If it can't be represented directly or is not needed, it can be represented in the form `Unused []byte`, the length of the array is determined by its size, and if the struct is only used as a pointer, then the array length can be `0`. + +```go type Reader struct { - Unused [8]byte + Unused [0]byte } +// type Reader struct { +// Unused [8]byte +// } +``` + +- Ordinary functions + +Ordinary functions can be mapped in the form of `//go:linkname`. + +```go //go:linkname NewReader C.csv_reader_new func NewReader(file_path *c.Char) *Reader +``` -//llgo:link (*Reader).Free C.csv_reader_free +- Method + +Methods need to be mapped in the form of `// llgo:link (*Receiver)`. + +```go +// llgo:link (*Reader).Free C.csv_reader_free func (reader *Reader) Free() {} -//llgo:link (*Reader).ReadRecord C.csv_reader_read_record +// llgo:link (*Reader).ReadRecord C.csv_reader_read_record func (reader *Reader) ReadRecord() *c.Char { return nil } ``` +- Function pointer + +If you use a function pointer, that is, declare the function as a type separately, you need to use `// llgo:type C`to declare it. + +```go +// llgo:type C +type IoReadCallback func(c.Pointer, *Context, *uint8, uintptr) uintptr + +// llgo:link (*Io).SetRead C.hyper_io_set_read +func (io *Io) SetRead(callback IoReadCallback) {} +``` + +Or declare the function directly in the parameter. + +```go +// llgo:link (*Io).SetRead C.hyper_io_set_read +func (io *Io) SetRead(callback func(c.Pointer, *Context, *uint8, uintptr) uintptr) {} +``` + ### Writing Examples and README Provide example code and a detailed README file to help users understand how to use the generated library. + +You can find the migrated instance from [llgoexamples](https://github.com/goplus/llgoexamples), in the lib directory is the migrated Rust library, and in the rust directory, the migrated mapping file and go demo. From 9379a41b371c3d7dd7b3b0f64c8a091b008b79ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E8=8B=B1=E6=9D=B0?= <2635879218@qq.com> Date: Thu, 25 Jul 2024 09:07:33 +0800 Subject: [PATCH 005/152] llgo/rust/readme: improve Rust-to-LLGO-Migration-Guide --- doc/Rust-to-LLGO-Migration-Guide.md | 158 ++++++++++++++++++++++------ 1 file changed, 123 insertions(+), 35 deletions(-) diff --git a/doc/Rust-to-LLGO-Migration-Guide.md b/doc/Rust-to-LLGO-Migration-Guide.md index 48ae4609..9f3c5940 100644 --- a/doc/Rust-to-LLGO-Migration-Guide.md +++ b/doc/Rust-to-LLGO-Migration-Guide.md @@ -32,38 +32,31 @@ To ensure that Rust functions can be correctly called by C and LLGO, use the fol - `unsafe` is used to mark operations that are unsafe, especially when dealing with raw pointers. - `extern "C"` specifies the use of C calling conventions. -For example, in Rust, we write CSV code like this: - ```rust -use csv::ReaderBuilder; -use std::error::Error; - -fn main() -> Result<(), Box> { - // Define the CSV file path - let file_path = "example.csv"; - - // Creating a CSV Reader - let mut reader = ReaderBuilder::new().from_path(file_path)?; - - // Define a container to store records - let mut record = csv::StringRecord::new(); - - // Read records one by one - while reader.read_record(&mut record)? { - // Print each record - println!("{:?}", record); - } - - Ok(()) +pub fn add_numbers(a: i32, b: i32) -> i32 { + a + b } ``` -If we need to migrate the CSV library to LLGO, we need to encapsulate each CSV method with a C API, such as the following: +After packaging: + +```rust +#[no_mangle] +pub unsafe extern "C" fn add_numbers_c(a: i32, b: i32) -> i32 { + add_numbers(a, b) +} +``` ### Memory Management Use `Box` to manage dynamic memory to ensure correct memory release between Rust and C: +```rust +let config = Config::new(); +``` + +After packaging: + ```rust pub unsafe extern "C" fn sled_create_config() -> \*mut Config { Box::into_raw(Box::new(Config::new())) @@ -79,12 +72,39 @@ drop(Box::from_raw(config)); Address the interfacing issues between generic pointers in C and Rust: -```rust -#[no_mangle] -pub extern "C" fn csv_reader_new(file_path: *const c_char) -> *mut c_void { /* ... */ } +```Rust +let mut reader = ReaderBuilder::new().from_path(file_path)?; +``` +After packaging: + +```rust +// Create a new CSV reader for the specified file path. #[no_mangle] -pub extern "C" fn csv_reader_free(ptr: *mut c_void) { /* ... */ } +pub extern "C" fn csv_reader_new(file_path: *const c_char) -> *mut c_void { + let file_path = unsafe { + if file_path.is_null() { return ptr::null_mut(); } + match CStr::from_ptr(file_path).to_str() { + Ok(s) => s, + Err(_) => return ptr::null_mut(), + } + }; + + let reader = csv::ReaderBuilder::new().from_path(file_path); + match reader { + Ok(r) => Box::into_raw(Box::new(r)) as *mut c_void, + Err(_) => ptr::null_mut(), + } +} + +// Free the memory allocated for the CSV reader. +#[no_mangle] +pub extern "C" fn csv_reader_free(ptr: *mut c_void) { + if !ptr.is_null() { + let reader: Box> = unsafe { Box::from_raw(ptr as *mut csv::Reader) }; + std::mem::drop(reader); + } +} ``` ### String Handling @@ -92,14 +112,44 @@ pub extern "C" fn csv_reader_free(ptr: *mut c_void) { /* ... */ } Convert strings between C and Rust: ```rust -#[no_mangle] -pub extern "C" fn csv_reader_read_record(ptr: *mut c_void) -> *const c_char { /* ... */ } +let mut record = csv::StringRecord::new(); +while reader.read_record(&mut record)? { + // Print each record + println!("{:?}", record); +} +``` + +After packaging: + +```rust +// Read the next record from the CSV reader and return it as a C string. +#[no_mangle] +pub extern "C" fn csv_reader_read_record(ptr: *mut c_void) -> *const c_char { + let reader = unsafe { + assert!(!ptr.is_null()); + &mut *(ptr as *mut csv::Reader) + }; + + let mut record = csv::StringRecord::new(); + match reader.read_record(&mut record) { + Ok(true) => match CString::new(format!("{:?}\n", record)) { + Ok(c_string) => c_string.into_raw(), + Err(_) => ptr::null(), + }, + _ => ptr::null(), + } +} + +// Free the memory allocated for a C string returned by other functions. #[no_mangle] pub extern "C" fn free_string(s: *mut c_char) { + if s.is_null() { + return; + } unsafe { let c_string = CString::from_raw(s); - drop(c_string); + std::mem::drop(c_string); } } ``` @@ -173,7 +223,7 @@ if everything is installed correctly, you will see the output like this (dependi Map functions from the Rust library to an LLGO package, ensuring type consistency: - LLGoPackage - + Specify `LLGoPackage` and use `pkg-config` to find the location of the lib library. ```go @@ -197,19 +247,41 @@ type Reader struct { // } ``` +If we want to calculate the size of this structure, we can use the following C code: + +```C +printf("%d\n", sizeof(csv_reader)); +``` + - Ordinary functions Ordinary functions can be mapped in the form of `//go:linkname`. +```C +csv_reader *csv_reader_new(const char *file_path); +``` + +After mapping: + ```go //go:linkname NewReader C.csv_reader_new func NewReader(file_path *c.Char) *Reader ``` - Method - + Methods need to be mapped in the form of `// llgo:link (*Receiver)`. +```C +void csv_reader_free(csv_reader *reader); + +const char *csv_reader_read_record(csv_reader *reader); +``` + +After mapping: + +We can extract the first parameter as Receiver: + ```go // llgo:link (*Reader).Free C.csv_reader_free func (reader *Reader) Free() {} @@ -219,9 +291,17 @@ func (reader *Reader) ReadRecord() *c.Char { return nil } ``` - Function pointer - + If you use a function pointer, that is, declare the function as a type separately, you need to use `// llgo:type C`to declare it. +```c +typedef size_t (*hyper_io_read_callback)(void*, struct hyper_context*, uint8_t*, size_t); + +void hyper_io_set_read(struct hyper_io *io, hyper_io_read_callback func); +``` + +After mapping: + ```go // llgo:type C type IoReadCallback func(c.Pointer, *Context, *uint8, uintptr) uintptr @@ -234,11 +314,19 @@ Or declare the function directly in the parameter. ```go // llgo:link (*Io).SetRead C.hyper_io_set_read -func (io *Io) SetRead(callback func(c.Pointer, *Context, *uint8, uintptr) uintptr) {} +func (io *Io) SetRead(ioSetReadCb func(c.Pointer, *Context, *uint8, uintptr) uintptr) {} ``` ### Writing Examples and README -Provide example code and a detailed README file to help users understand how to use the generated library. +Finally, provide example code and a detailed README file to help users understand how to use the generated library. + +### Example Code You can find the migrated instance from [llgoexamples](https://github.com/goplus/llgoexamples), in the lib directory is the migrated Rust library, and in the rust directory, the migrated mapping file and go demo. + +Such as: + +- CSV: [csv.rs](https://github.com/goplus/llgoexamples/blob/main/lib/rust/csv-wrapper/src/lib.rs) --> [csv.go](https://github.com/goplus/llgoexamples/blob/main/rust/csv/csv.go) +- Sled: [sled.rs](https://github.com/goplus/llgoexamples/blob/main/lib/rust/sled/src/lib.rs) --> [sled.go](https://github.com/goplus/llgoexamples/blob/main/rust/sled/sled.go) +- ... From db6930d9e4ccc4276c5d789982c7dad478333d4a Mon Sep 17 00:00:00 2001 From: hackerchai Date: Fri, 19 Jul 2024 17:27:18 +0800 Subject: [PATCH 006/152] feat(c/libuv): Add io, fs, signal, core, poll features Signed-off-by: hackerchai feat(c/libuv): Add io, fs, signal features Signed-off-by: hackerchai refactor(c/libuv): rename io into libuv Signed-off-by: hackerchai fix(c/libuv): fix some compile errors Signed-off-by: hackerchai --- c/libuv/fs.go | 451 ++++++++++++++++++++++++++++++++++++++++++++++ c/libuv/libuv.go | 141 +++++++++++++++ c/libuv/signal.go | 30 +++ 3 files changed, 622 insertions(+) create mode 100644 c/libuv/fs.go create mode 100644 c/libuv/libuv.go create mode 100644 c/libuv/signal.go diff --git a/c/libuv/fs.go b/c/libuv/fs.go new file mode 100644 index 00000000..3381e5bb --- /dev/null +++ b/c/libuv/fs.go @@ -0,0 +1,451 @@ +package libuv + +import ( + "github.com/goplus/llgo/c" + _ "unsafe" +) + +const ( + DirentUnknown DirentType = iota + DirentFile + DirentDir + DirentLink + DirentFifo + DirentSocket + DirentChar + DirentBlock +) + +type DirentType int + +/* Handle types. */ + +type Fs struct { + Unused [0]byte +} + +type FsEvent struct { + Unused [0]byte +} + +type FsPoll struct { + Unused [0]byte +} + +type Dirent struct { + Name *c.Char + Type DirentType +} + +type File struct { + Loop *Loop + Req *Fs +} + +// llgo:type C +type FsCb func(req *Fs) + +// llgo:type C +type FsEventCb func(handle *FsEvent, filename *c.Char, events c.Int, status c.Int) + +// llgo:type C +type FsPollCb func(handle *FsPoll, status c.Int, events c.Int) + +/* Request types. */ + +/* None of the above. */ + +// ---------------------------------------------- + +/* Fs related function and method */ + +//go:linkname FsReqCleanup C.uv_fs_req_cleanup +func FsReqCleanup(req *Fs) + +//go:linkname DefaultLoop C.uv_default_loop +func DefaultLoop() *Loop + +//go:linkname FsOpen C.uv_fs_open +func FsOpen(loop *Loop, req *Fs, path *c.Char, flags c.Int, mode c.Int, cb FsCb) c.Int + +//go:linkname FsClose C.uv_fs_close +func FsClose(loop *Loop, req *Fs, file c.Int, cb FsCb) c.Int + +//go:linkname FsRead C.uv_fs_read +func FsRead(loop *Loop, req *Fs, file c.Int, bufs []Buf, nbufs c.Uint, offset c.Int, cb FsCb) c.Int + +//go:linkname FsWrite C.uv_fs_write +func FsWrite(loop *Loop, req *Fs, file c.Int, bufs []Buf, nbufs c.Uint, offset c.Int, cb FsCb) c.Int + +//go:linkname FsUnlink C.uv_fs_unlink +func FsUnlink(loop *Loop, req *Fs, path *c.Char, cb FsCb) c.Int + +//go:linkname FsMkdir C.uv_fs_mkdir +func FsMkdir(loop *Loop, req *Fs, path *c.Char, mode c.Int, cb FsCb) c.Int + +//go:linkname FsMkdtemp C.uv_fs_mkdtemp +func FsMkdtemp(loop *Loop, req *Fs, tpl *c.Char, cb FsCb) c.Int + +//go:linkname FsMkStemp C.uv_fs_mkstemp +func FsMkStemp(loop *Loop, req *Fs, tpl *c.Char, cb FsCb) c.Int + +//go:linkname FsRmdir C.uv_fs_rmdir +func FsRmdir(loop *Loop, req *Fs, path *c.Char, cb FsCb) c.Int + +//go:linkname FsStat C.uv_fs_stat +func FsStat(loop *Loop, req *Fs, path *c.Char, cb FsCb) c.Int + +//go:linkname FsFstat C.uv_fs_fstat +func FsFstat(loop *Loop, req *Fs, file c.Int, cb FsCb) c.Int + +//go:linkname FsRename C.uv_fs_rename +func FsRename(loop *Loop, req *Fs, path *c.Char, newPath *c.Char, cb FsCb) c.Int + +//go:linkname FsFsync C.uv_fs_fsync +func FsFsync(loop *Loop, req *Fs, file c.Int, cb FsCb) c.Int + +//go:linkname FsFdatasync C.uv_fs_fdatasync +func FsFdatasync(loop *Loop, req *Fs, file c.Int, cb FsCb) c.Int + +//go:linkname FsFtruncate C.uv_fs_ftruncate +func FsFtruncate(loop *Loop, req *Fs, file c.Int, offset c.Int, cb FsCb) c.Int + +//go:linkname FsSendfile C.uv_fs_sendfile +func FsSendfile(loop *Loop, req *Fs, outFd c.Int, inFd c.Int, inOffset c.Int, length c.Int, cb FsCb) c.Int + +//go:linkname FsAccess C.uv_fs_access +func FsAccess(loop *Loop, req *Fs, path *c.Char, flags c.Int, cb FsCb) c.Int + +//go:linkname FsChmod C.uv_fs_chmod +func FsChmod(loop *Loop, req *Fs, path *c.Char, mode c.Int, cb FsCb) c.Int + +//go:linkname FsFchmod C.uv_fs_fchmod +func FsFchmod(loop *Loop, req *Fs, file c.Int, mode c.Int, cb FsCb) c.Int + +//go:linkname FsUtime C.uv_fs_utime +func FsUtime(loop *Loop, req *Fs, path *c.Char, atime c.Int, mtime c.Int, cb FsCb) c.Int + +//go:linkname FsFutime C.uv_fs_futime +func FsFutime(loop *Loop, req *Fs, file c.Int, atime c.Int, mtime c.Int, cb FsCb) c.Int + +//go:linkname FsLutime C.uv_fs_lutime +func FsLutime(loop *Loop, req *Fs, path *c.Char, atime c.Int, mtime c.Int, cb FsCb) c.Int + +//go:linkname FsLink C.uv_fs_link +func FsLink(loop *Loop, req *Fs, path *c.Char, newPath *c.Char, cb FsCb) c.Int + +//go:linkname FsSymlink C.uv_fs_symlink +func FsSymlink(loop *Loop, req *Fs, path *c.Char, newPath *c.Char, flags c.Int, cb FsCb) c.Int + +//go:linkname FsReadlink C.uv_fs_read +func FsReadlink(loop *Loop, req *Fs, path *c.Char, cb FsCb) c.Int + +//go:linkname FsRealpath C.uv_fs_realpath +func FsRealpath(loop *Loop, req *Fs, path *c.Char, cb FsCb) c.Int + +//go:linkname FsCopyfile C.uv_fs_copyfile +func FsCopyfile(loop *Loop, req *Fs, path *c.Char, newPath *c.Char, flags c.Int, cb FsCb) c.Int + +//go:linkname FsScandir C.uv_fs_scandir +func FsScandir(loop *Loop, req *Fs, path *c.Char, flags c.Int, cb FsCb) c.Int + +//go:linkname FsScandirNext C.uv_fs_scandir_next +func FsScandirNext(req *Fs, ent *Dirent) c.Int + +//go:linkname FsOpenDir C.uv_fs_opendir +func FsOpenDir(loop *Loop, req *Fs, path *c.Char, cb FsCb) c.Int + +//go:linkname FsReaddir C.uv_fs_readdir +func FsReaddir(loop *Loop, req *Fs, dir c.Int, cb FsCb) c.Int + +//go:linkname FsCloseDir C.uv_fs_closedir +func FsCloseDir(loop *Loop, req *Fs) c.Int + +//go:linkname FsStatfs C.uv_fs_statfs +func FsStatfs(loop *Loop, req *Fs, path *c.Char, cb FsCb) c.Int + +//go:linkname FsChown C.uv_fs_chown +func FsChown(loop *Loop, req *Fs, path *c.Char, uid c.Int, gid c.Int, cb FsCb) c.Int + +//go:linkname FsFchown C.uv_fs_fchown +func FsFchown(loop *Loop, req *Fs, file c.Int, uid c.Int, gid c.Int, cb FsCb) c.Int + +//go:linkname FsLchown C.uv_fs_lchown +func FsLchown(loop *Loop, req *Fs, path *c.Char, uid c.Int, gid c.Int, cb FsCb) c.Int + +//go:linkname FsLstat C.uv_fs_lstat +func FsLstat(loop *Loop, req *Fs, path *c.Char, cb FsCb) c.Int + +//go:linkname FsEventInit C.uv_fs_event_init +func FsEventInit(loop *Loop, handle *FsEvent) c.Int + +//go:linkname FsEventStart C.uv_fs_event_start +func FsEventStart(handle *FsEvent, cb FsEventCb, path *c.Char, flags c.Int) c.Int + +//go:linkname FsEventStop C.uv_fs_event_stop +func FsEventStop(handle *FsEvent) c.Int + +//go:linkname FsEventClose C.uv_fs_event_close +func FsEventClose(handle *FsEvent) c.Int + +//go:linkname FsEventGetpath C.uv_fs_event_getpath +func FsEventGetpath(handle *FsEvent) *c.Char + +//go:linkname FsPollInit C.uv_fs_poll_init +func FsPollInit(loop *Loop, handle *FsPoll) c.Int + +//go:linkname FsPollStart C.uv_fs_poll_start +func FsPollStart(handle *FsPoll, cb FsPollCb, path *c.Char, interval uint) c.Int + +//go:linkname FsPollStop C.uv_fs_poll_stop +func FsPollStop(handle *FsPoll) c.Int + +//go:linkname FsPollClose C.uv_fs_poll_close +func FsPollClose(handle *FsPoll) c.Int + +//go:linkname FsPollGetPath C.uv_fs_poll_getpath +func FsPollGetPath(handle *FsPoll) *c.Char + +//TODO: Implemnt uv_poll_init_socket + +// Cleanup cleans up the file system request. +func (f *File) Cleanup() { + FsReqCleanup(f.Req) +} + +func NewFile(loop *Loop, req *Fs) *File { + return &File{ + Loop: loop, + Req: req, + } +} + +// Open opens a file specified by the path with given flags and mode, and returns a file descriptor. +func (f *File) Open(path *c.Char, flags int, mode int, cb FsCb) int { + return FsOpen(f.Loop, f.Req, path, flags, mode, cb) +} + +// Close closes a file descriptor. +func (f *File) Close(file int, cb FsCb) int { + return FsClose(f.Loop, f.Req, file, cb) +} + +// Read reads data from a file descriptor into a buffer at a specified offset. +func (f *File) Read(file c.Int, bufs []Buf, nbufs c.Uint, offset c.Int, cb FsCb) int { + return FsRead(f.Loop, f.Req, file, bufs, nbufs, offset, cb) +} + +// Write writes data to a file descriptor from a buffer at a specified offset. +func (f *File) Write(file c.Int, bufs []Buf, nbufs c.Uint, offset c.Int, cb FsCb) int { + return FsWrite(f.Loop, f.Req, file, bufs, nbufs, offset, cb) +} + +// Unlink deletes a file specified by the path. +func (f *File) Unlink(path *c.Char, cb FsCb) int { + return FsUnlink(f.Loop, f.Req, path, cb) +} + +// Mkdir creates a new directory at the specified path with a specified mode. +func (f *File) Mkdir(path *c.Char, mode int, cb FsCb) int { + return FsMkdir(f.Loop, f.Req, path, mode, cb) +} + +// Mkdtemp creates a temporary directory with a template path. +func (f *File) Mkdtemp(tpl *c.Char, cb FsCb) int { + return FsMkdtemp(f.Loop, f.Req, tpl, cb) +} + +// MkStemp creates a temporary file from a template path. +func (f *File) MkStemp(tpl *c.Char, cb FsCb) int { + return FsMkStemp(f.Loop, f.Req, tpl, cb) +} + +// Rmdir removes a directory specified by the path. +func (f *File) Rmdir(path *c.Char, cb FsCb) int { + return FsRmdir(f.Loop, f.Req, path, cb) +} + +// Stat retrieves status information about the file specified by the path. +func (f *File) Stat(path *c.Char, cb FsCb) int { + return FsStat(f.Loop, f.Req, path, cb) +} + +// Fstat retrieves status information about a file descriptor. +func (f *File) Fstat(file int, cb FsCb) int { + return FsFstat(f.Loop, f.Req, file, cb) +} + +// Rename renames a file from the old path to the new path. +func (f *File) Rename(path *c.Char, newPath *c.Char, cb FsCb) int { + return FsRename(f.Loop, f.Req, path, newPath, cb) +} + +// Fsync synchronizes a file descriptor's state with storage device. +func (f *File) Fsync(file int, cb FsCb) int { + return FsFsync(f.Loop, f.Req, file, cb) +} + +// Fdatasync synchronizes a file descriptor's data with storage device. +func (f *File) Fdatasync(file int, cb FsCb) int { + return FsFdatasync(f.Loop, f.Req, file, cb) +} + +// Ftruncate truncates a file to a specified length. +func (f *File) Ftruncate(file int, offset int, cb FsCb) int { + return FsFtruncate(f.Loop, f.Req, file, offset, cb) +} + +// Sendfile sends data from one file descriptor to another. +func (f *File) Sendfile(outFd int, inFd int, inOffset int, length int, cb FsCb) int { + return FsSendfile(f.Loop, f.Req, outFd, inFd, inOffset, length, cb) +} + +// Access checks the access permissions of a file specified by the path. +func (f *File) Access(path *c.Char, flags int, cb FsCb) int { + return FsAccess(f.Loop, f.Req, path, flags, cb) +} + +// Chmod changes the permissions of a file specified by the path. +func (f *File) Chmod(path *c.Char, mode int, cb FsCb) int { + return FsChmod(f.Loop, f.Req, path, mode, cb) +} + +// Fchmod changes the permissions of a file descriptor. +func (f *File) Fchmod(file int, mode int, cb FsCb) int { + return FsFchmod(f.Loop, f.Req, file, mode, cb) +} + +// Utime updates the access and modification times of a file specified by the path. +func (f *File) Utime(path *c.Char, atime int, mtime int, cb FsCb) int { + return FsUtime(f.Loop, f.Req, path, atime, mtime, cb) +} + +// Futime updates the access and modification times of a file descriptor. +func (f *File) Futime(file int, atime int, mtime int, cb FsCb) int { + return FsFutime(f.Loop, f.Req, file, atime, mtime, cb) +} + +// Lutime updates the access and modification times of a file specified by the path, even if the path is a symbolic link. +func (f *File) Lutime(path *c.Char, atime int, mtime int, cb FsCb) int { + return FsLutime(f.Loop, f.Req, path, atime, mtime, cb) +} + +// Link creates a new link to an existing file. +func (f *File) Link(path *c.Char, newPath *c.Char, cb FsCb) int { + return FsLink(f.Loop, f.Req, path, newPath, cb) +} + +// Symlink creates a symbolic link from the path to the new path. +func (f *File) Symlink(path *c.Char, newPath *c.Char, flags int, cb FsCb) int { + return FsSymlink(f.Loop, f.Req, path, newPath, flags, cb) +} + +// Readlink reads the target of a symbolic link. +func (f *File) Readlink(path *c.Char, cb FsCb) int { + return FsReadlink(f.Loop, f.Req, path, cb) +} + +// Realpath resolves the absolute path of a file. +func (f *File) Realpath(path *c.Char, cb FsCb) int { + return FsRealpath(f.Loop, f.Req, path, cb) +} + +// Copyfile copies a file from the source path to the destination path. +func (f *File) Copyfile(path *c.Char, newPath *c.Char, flags int, cb FsCb) int { + return FsCopyfile(f.Loop, f.Req, path, newPath, flags, cb) +} + +// Scandir scans a directory for entries. +func (f *File) Scandir(path *c.Char, flags int, cb FsCb) int { + return FsScandir(f.Loop, f.Req, path, flags, cb) +} + +// OpenDir opens a directory specified by the path. +func (f *File) OpenDir(path *c.Char, cb FsCb) int { + return FsOpenDir(f.Loop, f.Req, path, cb) +} + +// Readdir reads entries from an open directory. +func (f *File) Readdir(dir int, cb FsCb) int { + return FsReaddir(f.Loop, f.Req, dir, cb) +} + +// CloseDir closes an open directory. +func (f *File) CloseDir() int { + return FsCloseDir(f.Loop, f.Req) +} + +// Statfs retrieves file system status information. +func (f *File) Statfs(path *c.Char, cb FsCb) int { + return FsStatfs(f.Loop, f.Req, path, cb) +} + +// Chown Change file ownership +func (f *File) Chown(path *c.Char, uid int, gid int, cb FsCb) int { + return FsChown(f.Loop, f.Req, path, uid, gid, cb) +} + +// Fchown Change file ownership by file descriptor +func (f *File) Fchown(file int, uid int, gid int, cb FsCb) int { + return FsFchown(f.Loop, f.Req, file, uid, gid, cb) +} + +// Lchown Change file ownership (symlink) +func (f *File) Lchown(path *c.Char, uid int, gid int, cb FsCb) int { + return FsLchown(f.Loop, f.Req, path, uid, gid, cb) +} + +// Lstat Get file status (symlink) +func (f *File) Lstat(path *c.Char, cb FsCb) int { + return FsLstat(f.Loop, f.Req, path, cb) +} + +// Init Initialize a file event handle +func (e *FsEvent) Init(loop *Loop) int { + return FsEventInit(loop, e) +} + +// Start listening for file events +func (e *FsEvent) Start(cb FsEventCb, path *c.Char, flags int) int { + return FsEventStart(e, cb, path, flags) +} + +// Stop listening for file events +func (e *FsEvent) Stop() int { + return FsEventStop(e) +} + +// Close the file event handle +func (e *FsEvent) Close() int { + return FsEventClose(e) +} + +// GetPath Get the path of the file event +func (e *FsEvent) GetPath() *c.Char { + return FsEventGetpath(e) +} + +// Init Initialize a file poll handle +func (p *FsPoll) Init(loop *Loop) int { + return FsPollInit(loop, p) +} + +// Start polling for file changes +func (p *FsPoll) Start(cb FsPollCb, path *c.Char, interval uint) int { + return FsPollStart(p, cb, path, interval) +} + +// Stop polling for file changes +func (p *FsPoll) Stop() int { + return FsPollStop(p) +} + +// Close the file poll handle +func (p *FsPoll) Close() int { + return FsPollClose(p) +} + +// GetPath Get the path of the file poll +func (p *FsPoll) GetPath() *c.Char { + return FsPollGetPath(p) +} diff --git a/c/libuv/libuv.go b/c/libuv/libuv.go new file mode 100644 index 00000000..26a6b6c8 --- /dev/null +++ b/c/libuv/libuv.go @@ -0,0 +1,141 @@ +package libuv + +import ( + "github.com/goplus/llgo/c" + _ "unsafe" +) + +const ( + LLGoPackage = "link: $(pkg-config --libs libuv); -luv" +) + +// ---------------------------------------------- + +const ( + RUN_DEFAULT RunMode = iota + RUN_ONCE + RUN_NOWAIT +) + +type RunMode int + +// ---------------------------------------------- + +/* Handle types. */ + +type Loop struct { + Unused [0]byte +} + +type Handle struct { + Unused [0]byte +} + +type Buf struct { + Base *c.Char + Len uintptr +} + +// ---------------------------------------------- + +/* Function type */ + +// llgo:type C +type WalkCb func(handle *Handle, arg c.Pointer) + +// ---------------------------------------------- + +//go:linkname LoopSize C.uv_loop_size +func LoopSize() uintptr + +// llgo:link (*Loop).Init C.uv_loop_init +func (loop *Loop) Init() c.Int { + return 0 +} + +// llgo:link (*Loop).Run C.uv_loop_run +func (l *Loop) Run(mode c.Int) c.Int { + return 0 +} + +// llgo:link (*Loop).Stop C.uv_loop_stop +func (l *Loop) Stop() { + return +} + +// llgo:link (*Loop).Default C.uv_default_loop +func (l *Loop) Default() *Loop { + return nil +} + +// llgo:link (*Loop).New C.uv_loop_new +func (l *Loop) New() *Loop { + return nil +} + +// Deprecated: use LoopClose instead. +// llgo:link (*Loop).Delete C.uv_loop_delete +func (l *Loop) Delete() { + return +} + +// llgo:link (*Loop).Alive C.uv_loop_alive +func (l *Loop) Alive() c.Int { + return 0 +} + +// llgo:link (*Loop).Close C.uv_loop_close +func (l *Loop) Close() c.Int { + return 0 +} + +// llgo:link (*Loop).Configure C.uv_loop_configure +func (l *Loop) Configure(loop *Loop, option c.Int, arg c.Int) c.Int { + return 0 +} + +// llgo:link (*Loop).Walk C.uv_walk +func (loop *Loop) Walk(walkCb WalkCb, arg c.Pointer) {} + +// llgo:link (*Loop).Fork C.uv_loop_fork +func (l *Loop) Fork(loop *Loop) int { + return 0 +} + +// llgo:link (*Loop).UpdateTime C.uv_update_time +func (l *Loop) UpdateTime() { + return +} + +// llgo:link (*Loop).Now C.uv_now +func (l *Loop) Now() uint64 { + return 0 +} + +// llgo:link (*Loop).BackendFd C.uv_backend_fd +func (l *Loop) BackendFd() int { + return 0 +} + +// llgo:link (*Loop).BackendTimeout C.uv_backend_timeout +func (l *Loop) BackendTimeout() int { + return 0 +} + +// llgo:link (*Handle).Ref C.uv_ref +func (h *Handle) Ref() { + return +} + +// llgo:link (*Handle).Unref C.uv_unref +func (h *Handle) Unref() { + return +} + +// llgo:link (*Handle).HasRef C.uv_has_ref +func (h *Handle) HasRef() int { + return 0 +} + +//go:linkname InitBuf C.uv_buf_init +func InitBuf(base *c.Char, len c.Uint) Buf diff --git a/c/libuv/signal.go b/c/libuv/signal.go new file mode 100644 index 00000000..46b6eb9d --- /dev/null +++ b/c/libuv/signal.go @@ -0,0 +1,30 @@ +package libuv + +import ( + "github.com/goplus/llgo/c" + _ "unsafe" +) + +type Signal struct { + Unused [0]byte +} + +// llgo:type C +type SignalCb func(handle *Signal, sigNum c.Int) + +//go:linkname UvSignalInit C.uv_signal_init +func UvSignalInit(loop *Loop, handle *Signal) c.Int + +// llgo:link (*Signal).Start C.uv_signal_start +func (s *Signal) Start(cb SignalCb, signum c.Int) c.Int { + return 0 +} + +// llgo:link (*Signal).StartOneshot C.uv_signal_start_oneshot +func (s *Signal) StartOneshot(handle *Signal, cb SignalCb, signum c.Int) c.Int { + return 0 +} + +func (s *Signal) Init(loop *Loop) int { + return UvSignalInit(loop, s) +} From 545f9f2ccae5e98506b98999cacfb2f61490a84e Mon Sep 17 00:00:00 2001 From: hackerchai Date: Tue, 23 Jul 2024 11:01:28 +0800 Subject: [PATCH 007/152] feat(c/libuv/demo): Add libuv async_fs demo Signed-off-by: hackerchai fix(c/libuv): fix fs demo Signed-off-by: hackerchai refactor(c/libuv): neat comment and adapt merge Signed-off-by: hackerchai --- c/libuv/_demo/libuv/fs.go | 70 +++++++++++++++++++++++++++++++ c/libuv/fs.go | 87 +++++++++++++++++++++++++++++++++++++-- c/libuv/libuv.go | 21 ++-------- c/libuv/signal.go | 6 +++ 4 files changed, 163 insertions(+), 21 deletions(-) create mode 100644 c/libuv/_demo/libuv/fs.go diff --git a/c/libuv/_demo/libuv/fs.go b/c/libuv/_demo/libuv/fs.go new file mode 100644 index 00000000..7d50f2e9 --- /dev/null +++ b/c/libuv/_demo/libuv/fs.go @@ -0,0 +1,70 @@ +package main + +import ( + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/libuv" + "github.com/goplus/llgo/c/os" + "golang.org/x/tools/container/intsets" + "unsafe" +) + +const bufferSize = 1024 + +var buffer []c.Char +var iov libuv.Buf + +var loop *libuv.Loop +var readReq *libuv.Fs +var closeReq *libuv.Fs +var openReq *libuv.Fs + +func initBuffer() { + buffer = make([]c.Char, bufferSize) +} + +func main() { + loop = libuv.DefaultLoop() + initBuffer() + + file := libuv.NewFile(loop, openReq) + + path := c.Str("example.txt") + + file.Open(path, os.O_RDONLY, 0, onOpen) + + loop.Run(libuv.RUN_DEFAULT) + + libuv.FsReqCleanup(openReq) + loop.Close() + c.Free(unsafe.Pointer(loop)) +} + +func onOpen(req *libuv.Fs) { + if req.GetResult() < 0 { + c.Fprintf(c.Stderr, c.Str("Error opening file: %s\n"), libuv.Strerror(req.GetResult())) + } else { + iov = libuv.InitBuf(unsafe.SliceData(buffer), c.Uint(unsafe.Sizeof(buffer))) + libuv.FsRead(loop, readReq, req.GetResult(), []libuv.Buf{iov}, 1, -1, onRead) + } + libuv.FsReqCleanup(req) +} + +func onRead(req *libuv.Fs) { + if req.GetResult() < 0 { + c.Fprintf(c.Stderr, c.Str("Read error: %s\n"), libuv.Strerror(req.GetResult())) + } else if req.GetResult() == 0 { + libuv.FsClose(loop, closeReq, req.GetResult(), onClose) + } else { + if req.GetResult() > intsets.MaxInt { + c.Fprintf(c.Stderr, c.Str("Too big file.\n")) + } + c.Printf(c.Str("%.*s"), c.Int(req.GetResult()), buffer) + libuv.FsRead(loop, req, req.GetResult(), []libuv.Buf{iov}, 1, -1, onRead) + } + libuv.FsReqCleanup(req) +} + +func onClose(req *libuv.Fs) { + c.Printf(c.Str("\nFile closed.\n")) + libuv.FsReqCleanup(req) +} diff --git a/c/libuv/fs.go b/c/libuv/fs.go index 3381e5bb..555b5b0f 100644 --- a/c/libuv/fs.go +++ b/c/libuv/fs.go @@ -5,6 +5,47 @@ import ( _ "unsafe" ) +const ( + FS_UNKNOWN FsType = -1 + FS_CUSTOM FsType = 0 + FS_OPEN FsType = 1 + FS_CLOSE FsType = 2 + FS_READ FsType = 3 + FS_WRITE FsType = 4 + FS_SENDFILE FsType = 5 + FS_STAT FsType = 6 + FS_LSTAT FsType = 7 + FS_FSTAT FsType = 8 + FS_FTRUNCATE FsType = 9 + FS_UTIME FsType = 10 + FS_FUTIME FsType = 11 + FS_ACCESS FsType = 12 + FS_CHMOD FsType = 13 + FS_FCHMOD FsType = 14 + FS_FSYNC FsType = 15 + FS_FDATASYNC FsType = 16 + FS_UNLINK FsType = 17 + FS_RMDIR FsType = 18 + FS_MKDIR FsType = 19 + FS_MKDTEMP FsType = 20 + FS_RENAME FsType = 21 + FS_SCANDIR FsType = 22 + FS_LINK FsType = 23 + FS_SYMLINK FsType = 24 + FS_READLINK FsType = 25 + FS_CHOWN FsType = 26 + FS_FCHOWN FsType = 27 + FS_REALPATH FsType = 28 + FS_COPYFILE FsType = 29 + FS_LCHOWN FsType = 30 + FS_OPENDIR FsType = 31 + FS_READDIR FsType = 32 + FS_CLOSEDIR FsType = 33 + FS_STATFS FsType = 34 + FS_MKSTEMP FsType = 35 + FS_LUTIME FsType = 36 +) + const ( DirentUnknown DirentType = iota DirentFile @@ -16,8 +57,12 @@ const ( DirentBlock ) +type FsType int + type DirentType int +// ---------------------------------------------- + /* Handle types. */ type Fs struct { @@ -42,6 +87,14 @@ type File struct { Req *Fs } +type Stat struct { + Unused [0]byte +} + +// ---------------------------------------------- + +/* Function type */ + // llgo:type C type FsCb func(req *Fs) @@ -51,14 +104,40 @@ type FsEventCb func(handle *FsEvent, filename *c.Char, events c.Int, status c.In // llgo:type C type FsPollCb func(handle *FsPoll, status c.Int, events c.Int) -/* Request types. */ - -/* None of the above. */ - // ---------------------------------------------- /* Fs related function and method */ +// llgo:link (*Fs).GetType C.uv_fs_get_type +func (f *Fs) GetType() *FsType { + return nil +} + +// llgo:link (*Fs).GetPath C.uv_fs_get_path +func (f *Fs) GetPath() *c.Char { + return nil +} + +// llgo:link (*Fs).GetResult C.uv_fs_get_result +func (f *Fs) GetResult() c.Int { + return 0 +} + +// llgo:link (*Fs).GetPtr C.uv_fs_get_ptr +func (f *Fs) GetPtr() c.Pointer { + return nil +} + +// llgo:link (*Fs).GetSystemError C.uv_fs_get_system_error +func (f *Fs) GetSystemError() c.Int { + return 0 +} + +// llgo:link (*Fs).GetStatBuf C.uv_fs_get_statbuf +func (f *Fs) GetStatBuf() *Stat { + return nil +} + //go:linkname FsReqCleanup C.uv_fs_req_cleanup func FsReqCleanup(req *Fs) diff --git a/c/libuv/libuv.go b/c/libuv/libuv.go index 26a6b6c8..7ca9e964 100644 --- a/c/libuv/libuv.go +++ b/c/libuv/libuv.go @@ -27,10 +27,6 @@ type Loop struct { Unused [0]byte } -type Handle struct { - Unused [0]byte -} - type Buf struct { Base *c.Char Len uintptr @@ -45,6 +41,8 @@ type WalkCb func(handle *Handle, arg c.Pointer) // ---------------------------------------------- +/* Loop related functions and method. */ + //go:linkname LoopSize C.uv_loop_size func LoopSize() uintptr @@ -122,20 +120,9 @@ func (l *Loop) BackendTimeout() int { return 0 } -// llgo:link (*Handle).Ref C.uv_ref -func (h *Handle) Ref() { - return -} +// ---------------------------------------------- -// llgo:link (*Handle).Unref C.uv_unref -func (h *Handle) Unref() { - return -} - -// llgo:link (*Handle).HasRef C.uv_has_ref -func (h *Handle) HasRef() int { - return 0 -} +/* Buf related functions and method. */ //go:linkname InitBuf C.uv_buf_init func InitBuf(base *c.Char, len c.Uint) Buf diff --git a/c/libuv/signal.go b/c/libuv/signal.go index 46b6eb9d..97847d1c 100644 --- a/c/libuv/signal.go +++ b/c/libuv/signal.go @@ -5,10 +5,16 @@ import ( _ "unsafe" ) +/* Handle types. */ + type Signal struct { Unused [0]byte } +// ---------------------------------------------- + +/* Signal related functions and method. */ + // llgo:type C type SignalCb func(handle *Signal, sigNum c.Int) From e9d4328fadaebc7192c4c98a5f692d95b5682f8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E8=8B=B1=E6=9D=B0?= Date: Fri, 19 Jul 2024 15:17:20 +0800 Subject: [PATCH 008/152] feat(c/libuv): Add tcp, udp, poll, core, stream, err features feat(c/io): add libuv async io with io, tcp, udp, timer, dns, loop feat(c/io): add libuv async io with stream, req, handle feat(c/libuv): rename c/io to c/libuv, and improve errro, net, handle, stream feat(c/libuv): Add a libuv demo: echo_server refactor(c/libuv): Adjust comments and file names to accommodate merge --- c/libuv/error.go | 198 ++++++++++++++++++++ c/libuv/libuv.go | 460 +++++++++++++++++++++++++++++++++++++++-------- c/libuv/net.go | 256 ++++++++++++++++++++++++++ c/libuv/timer.go | 42 +++++ c/net/net.go | 13 ++ 5 files changed, 895 insertions(+), 74 deletions(-) create mode 100644 c/libuv/error.go create mode 100644 c/libuv/net.go create mode 100644 c/libuv/timer.go diff --git a/c/libuv/error.go b/c/libuv/error.go new file mode 100644 index 00000000..95ee68dd --- /dev/null +++ b/c/libuv/error.go @@ -0,0 +1,198 @@ +package libuv + +import ( + "github.com/goplus/llgo/c" + _ "unsafe" +) + +const ( + E2BIG Errno = iota + EACCES + EADDRINUSE + EADDRNOTAVAIL + EAFNOSUPPORT + EAGAIN + EAI_ADDRFAMILY + EAI_AGAIN + EAI_BADFLAGS + EAI_BADHINTS + EAI_CANCELED + EAI_FAIL + EAI_FAMILY + EAI_MEMORY + EAI_NODATA + EAI_NONAME + EAI_OVERFLOW + EAI_PROTOCOL + EAI_SERVICE + EAI_SOCKTYPE + EALREADY + EBADF + EBUSY + ECANCELED + ECHARSET + ECONNABORTED + ECONNREFUSED + ECONNRESET + EDESTADDRREQ + EEXIST + EFAULT + EFBIG + EHOSTUNREACH + EINTR + EINVAL + EIO + EISCONN + EISDIR + ELOOP + EMFILE + EMSGSIZE + ENAMETOOLONG + ENETDOWN + ENETUNREACH + ENFILE + ENOBUFS + ENODEV + ENOENT + ENOMEM + ENONET + ENOPROTOOPT + ENOSPC + ENOSYS + ENOTCONN + ENOTDIR + ENOTEMPTY + ENOTSOCK + ENOTSUP + EOVERFLOW + EPERM + EPIPE + EPROTO + EPROTONOSUPPORT + EPROTOTYPE + ERANGE + EROFS + ESHUTDOWN + ESPIPE + ESRCH + ETIMEDOUT + ETXTBSY + EXDEV + UNKNOWN + EOF + ENXIO + EMLINK + EHOSTDOWN + EREMOTEIO + ENOTTY + EFTYPE + EILSEQ + ESOCKTNOSUPPORT + ENODATA + EUNATCH + ERRNO_MAX +) + +//var errnoDescriptions = map[Errno]string{ +// E2BIG: "argument list too long", +// EACCES: "permission denied", +// EADDRINUSE: "address already in use", +// EADDRNOTAVAIL: "address not available", +// EAFNOSUPPORT: "address family not supported", +// EAGAIN: "resource temporarily unavailable", +// EAI_ADDRFAMILY: "address family not supported", +// EAI_AGAIN: "temporary failure", +// EAI_BADFLAGS: "bad ai_flags value", +// EAI_BADHINTS: "invalid value for hints", +// EAI_CANCELED: "request canceled", +// EAI_FAIL: "permanent failure", +// EAI_FAMILY: "ai_family not supported", +// EAI_MEMORY: "out of memory", +// EAI_NODATA: "no address", +// EAI_NONAME: "unknown node or service", +// EAI_OVERFLOW: "argument buffer overflow", +// EAI_PROTOCOL: "resolved protocol is unknown", +// EAI_SERVICE: "service not available for socket type", +// EAI_SOCKTYPE: "socket type not supported", +// EALREADY: "connection already in progress", +// EBADF: "bad file descriptor", +// EBUSY: "resource busy or locked", +// ECANCELED: "operation canceled", +// ECHARSET: "invalid Unicode character", +// ECONNABORTED: "software caused connection abort", +// ECONNREFUSED: "connection refused", +// ECONNRESET: "connection reset by peer", +// EDESTADDRREQ: "destination address required", +// EEXIST: "file already exists", +// EFAULT: "bad address in system call argument", +// EFBIG: "file too large", +// EHOSTUNREACH: "host is unreachable", +// EINTR: "interrupted system call", +// EINVAL: "invalid argument", +// EIO: "i/o error", +// EISCONN: "socket is already connected", +// EISDIR: "illegal operation on a directory", +// ELOOP: "too many symbolic links encountered", +// EMFILE: "too many open files", +// EMSGSIZE: "message too long", +// ENAMETOOLONG: "name too long", +// ENETDOWN: "network is down", +// ENETUNREACH: "network is unreachable", +// ENFILE: "file table overflow", +// ENOBUFS: "no buffer space available", +// ENODEV: "no such device", +// ENOENT: "no such file or directory", +// ENOMEM: "not enough memory", +// ENONET: "machine is not on the network", +// ENOPROTOOPT: "protocol not available", +// ENOSPC: "no space left on device", +// ENOSYS: "function not implemented", +// ENOTCONN: "socket is not connected", +// ENOTDIR: "not a directory", +// ENOTEMPTY: "directory not empty", +// ENOTSOCK: "socket operation on non-socket", +// ENOTSUP: "operation not supported on socket", +// EOVERFLOW: "value too large for defined data type", +// EPERM: "operation not permitted", +// EPIPE: "broken pipe", +// EPROTO: "protocol error", +// EPROTONOSUPPORT: "protocol not supported", +// EPROTOTYPE: "protocol wrong type for socket", +// ERANGE: "result too large", +// EROFS: "read-only file system", +// ESHUTDOWN: "cannot send after transport endpoint shutdown", +// ESPIPE: "invalid seek", +// ESRCH: "no such process", +// ETIMEDOUT: "connection timed out", +// ETXTBSY: "text file is busy", +// EXDEV: "cross-device link not permitted", +// UNKNOWN: "unknown error", +// EOF: "end of file", +// ENXIO: "no such device or address", +// EMLINK: "too many links", +// EHOSTDOWN: "host is down", +// EREMOTEIO: "remote I/O error", +// ENOTTY: "inappropriate ioctl for device", +// EFTYPE: "inappropriate file type or format", +// EILSEQ: "illegal byte sequence", +// ESOCKTNOSUPPORT: "socket type not supported", +// ENODATA: "no data available", +// EUNATCH: "protocol driver not attached", +//} + +type Errno c.Int + +//go:linkname TranslateSysError C.uv_translate_sys_error +func TranslateSysError(sysErrno c.Int) c.Int + +//go:linkname Strerror C.uv_strerror +func Strerror(err c.Int) *c.Char + +//go:linkname StrerrorR C.uv_strerror_r +func StrerrorR(err c.Int, buf *c.Char, bufLen uintptr) *c.Char + +//go:linkname ErrName C.uv_err_name +func ErrName(err c.Int) *c.Char + +//go:linkname ErrNameR C.uv_err_name_r +func ErrNameR(err c.Int, buf *c.Char, bufLen uintptr) *c.Char diff --git a/c/libuv/libuv.go b/c/libuv/libuv.go index 7ca9e964..004b223d 100644 --- a/c/libuv/libuv.go +++ b/c/libuv/libuv.go @@ -2,6 +2,7 @@ package libuv import ( "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/net" _ "unsafe" ) @@ -11,22 +12,183 @@ const ( // ---------------------------------------------- +const ( + LOOP_BLOCK_SIGNAL LoopOption = iota + METRICS_IDLE_TIME +) + const ( RUN_DEFAULT RunMode = iota RUN_ONCE RUN_NOWAIT ) -type RunMode int +const ( + UV_LEAVE_GROUP Membership = iota + UV_JOIN_GROUP +) + +const ( + UNKNOWN_HANDLE HandleType = iota + ASYNC + CHECK + FS_EVENT + FS_POLL + HANDLE + IDLE + NAMED_PIPE + POLL + PREPARE + PROCESS + STREAM + TCP + TIMER + TTY + UDP + SIGNAL + FILE + HANDLE_TYPE_MAX +) + +const ( + UNKNOWN_REQ ReqType = iota + REQ + CONNECT + WRITE + SHUTDOWN + UDP_SEND + FS + WORK + GETADDRINFO + GETNAMEINFO + RANDOM + REQ_TYPE_PRIVATE + REQ_TYPE_MAX +) + +type LoopOption c.Int + +type RunMode c.Int + +type Membership c.Int + +type HandleType c.Int + +type ReqType c.Int + +type Uv_File c.Int + +type OsSock c.Int + +type OsFd c.Int // ---------------------------------------------- /* Handle types. */ - type Loop struct { Unused [0]byte } +type Handle struct { + Unused [0]byte +} + +type Dir struct { + Unused [0]byte +} + +type Stream struct { + Unused [0]byte +} + +type Tcp struct { + Unused [0]byte +} + +type Udp struct { + Unused [0]byte +} + +type Pipe struct { + Unused [0]byte +} + +type Tty struct { + Unused [0]byte +} + +type Poll struct { + Unused [0]byte +} + +type Timer struct { + Unused [0]byte +} + +type Prepare struct { + Unused [0]byte +} + +type Check struct { + Unused [0]byte +} + +type Idle struct { + Unused [0]byte +} + +type Async struct { + Unused [0]byte +} + +type Process struct { + Unused [0]byte +} + +type FsEvent struct { + Unused [0]byte +} + +type FsPoll struct { + Unused [0]byte +} + +type Signal struct { + Unused [0]byte +} + +/* Request types. */ + +type Req struct { + Unused [0]byte +} + +type GetAddrInfo struct { + Unused [0]byte +} + +type GetNameInfo struct { + Unused [0]byte +} + +type Shutdown struct { + Unused [0]byte +} + +type Write struct { + Unused [0]byte +} + +type Connect struct { + Unused [0]byte +} + +type UdpSend struct { + Unused [0]byte +} + +// ---------------------------------------------- + type Buf struct { Base *c.Char Len uintptr @@ -36,93 +198,243 @@ type Buf struct { /* Function type */ +// llgo:type C +type MallocFunc func(size uintptr) c.Pointer + +// llgo:type C +type ReallocFunc func(ptr c.Pointer, size uintptr) c.Pointer + +// llgo:type C +type CallocFunc func(count uintptr, size uintptr) c.Pointer + +// llgo:type C +type FreeFunc func(ptr c.Pointer) + +// llgo:type C +type AllocCb func(handle *Handle, suggestedSize uintptr, buf *Buf) + +// llgo:type C +type ReadCb func(stream *Stream, nread c.Long, buf *Buf) + +// llgo:type C +type WriteCb func(req *Write, status c.Int) + +// llgo:type C +type GetaddrinfoCb func(req *GetAddrInfo, status c.Int, res *net.AddrInfo) + +// llgo:type C +type GetnameinfoCb func(req *GetNameInfo, status c.Int, hostname *c.Char, service *c.Char) + +// llgo:type C +type ConnectionCb func(server *Stream, status c.Int) + +// llgo:type C +type ShutdownCb func(req *Shutdown, status c.Int) + // llgo:type C type WalkCb func(handle *Handle, arg c.Pointer) // ---------------------------------------------- -/* Loop related functions and method. */ +//go:linkname Version C.uv_version +func Version() c.Uint -//go:linkname LoopSize C.uv_loop_size -func LoopSize() uintptr +//go:linkname VersionString C.uv_version_string +func VersionString() *c.Char -// llgo:link (*Loop).Init C.uv_loop_init -func (loop *Loop) Init() c.Int { - return 0 -} +//go:linkname LibraryShutdown C.uv_library_shutdown +func LibraryShutdown() -// llgo:link (*Loop).Run C.uv_loop_run -func (l *Loop) Run(mode c.Int) c.Int { - return 0 -} +//go:linkname ReplaceAllocator C.uv_replace_allocator +func ReplaceAllocator(mallocFunc MallocFunc, reallocFunc ReallocFunc, callocFunc CallocFunc, freeFunc FreeFunc) c.Int -// llgo:link (*Loop).Stop C.uv_loop_stop -func (l *Loop) Stop() { - return -} +// ---------------------------------------------- -// llgo:link (*Loop).Default C.uv_default_loop -func (l *Loop) Default() *Loop { - return nil -} - -// llgo:link (*Loop).New C.uv_loop_new -func (l *Loop) New() *Loop { - return nil -} - -// Deprecated: use LoopClose instead. -// llgo:link (*Loop).Delete C.uv_loop_delete -func (l *Loop) Delete() { - return -} - -// llgo:link (*Loop).Alive C.uv_loop_alive -func (l *Loop) Alive() c.Int { - return 0 -} - -// llgo:link (*Loop).Close C.uv_loop_close -func (l *Loop) Close() c.Int { - return 0 -} - -// llgo:link (*Loop).Configure C.uv_loop_configure -func (l *Loop) Configure(loop *Loop, option c.Int, arg c.Int) c.Int { - return 0 -} - -// llgo:link (*Loop).Walk C.uv_walk -func (loop *Loop) Walk(walkCb WalkCb, arg c.Pointer) {} - -// llgo:link (*Loop).Fork C.uv_loop_fork -func (l *Loop) Fork(loop *Loop) int { - return 0 -} - -// llgo:link (*Loop).UpdateTime C.uv_update_time -func (l *Loop) UpdateTime() { - return -} - -// llgo:link (*Loop).Now C.uv_now -func (l *Loop) Now() uint64 { - return 0 -} - -// llgo:link (*Loop).BackendFd C.uv_backend_fd -func (l *Loop) BackendFd() int { - return 0 -} - -// llgo:link (*Loop).BackendTimeout C.uv_backend_timeout -func (l *Loop) BackendTimeout() int { +// llgo:link (*Shutdown).Shutdown C.uv_shutdown +func (shutdown *Shutdown) Shutdown(stream *Stream, shutdownCb ShutdownCb) c.Int { return 0 } // ---------------------------------------------- -/* Buf related functions and method. */ +/* HandleT related function and method */ + +// llgo:link (*Handle).Ref C.uv_ref +func (handle *Handle) Ref() {} + +// llgo:link (*Handle).Unref C.uv_unref +func (handle *Handle) Unref() {} + +// llgo:link (*Handle).HasRef C.uv_has_ref +func (handle *Handle) HasRef() c.Int { + return 0 +} + +//go:linkname HandleSize C.uv_handle_size +func HandleSize(handleType HandleType) uintptr + +// llgo:link (*Handle).GetType C.uv_handle_get_type +func (handle *Handle) GetType() HandleType { + return 0 +} + +//go:linkname HandleTypeName C.uv_handle_type_name +func HandleTypeName(handleType HandleType) *c.Char + +// llgo:link (*Handle).GetData C.uv_handle_get_data +func (handle *Handle) GetData() c.Pointer { + return nil +} + +// llgo:link (*Handle).GetLoop C.uv_handle_get_loop +func (handle *Handle) GetLoop() *Loop { + return nil +} + +// llgo:link (*Handle).SetData C.uv_handle_set_data +func (handle *Handle) SetData(data c.Pointer) {} + +// llgo:link (*Handle).IsActive C.uv_is_active +func (handle *Handle) IsActive() c.Int { + return 0 +} + +// llgo:link (*Handle).Close C.uv_close +func (handle *Handle) Close(closeCb CloseCb) {} + +// llgo:link (*Handle).SendBufferSize C.uv_send_buffer_size +func (handle *Handle) SendBufferSize(value *c.Int) c.Int { + return 0 +} + +// llgo:link (*Handle).RecvBufferSize C.uv_recv_buffer_size +func (handle *Handle) RecvBufferSize(value *c.Int) c.Int { + return 0 +} + +// llgo:link (*Handle).Fileno C.uv_fileno +func (handle *Handle) Fileno(fd *OsFd) c.Int { + return 0 +} //go:linkname InitBuf C.uv_buf_init func InitBuf(base *c.Char, len c.Uint) Buf + +//go:linkname UvPipe C.uv_pipe +func UvPipe(fds [2]Uv_File, readFlags c.Int, writeFlags c.Int) c.Int { + return 0 +} + +//go:linkname Socketpair C.uv_socketpair +func Socketpair(_type c.Int, protocol c.Int, socketVector [2]OsSock, flag0 c.Int, flag1 c.Int) c.Int { + return 0 +} + +// llgo:link (*Handle).IsClosing C.uv_is_closing +func (handle *Handle) IsClosing() c.Int { + return 0 +} + +// ---------------------------------------------- + +/* Req related function and method */ + +//go:linkname ReqSize C.uv_req_size +func ReqSize(reqType ReqType) uintptr + +// llgo:link (*Req).GetData C.uv_req_get_data +func (req *Req) GetData() c.Pointer { + return nil +} + +// llgo:link (*Req).SetData C.uv_handle_set_data +func (req *Req) SetData(data c.Pointer) {} + +// llgo:link (*Req).GetType C.uv_req_get_type +func (req *Req) GetType() ReqType { + return 0 +} + +//go:linkname TypeName C.uv_req_type_name +func TypeName(reqType ReqType) *c.Char + +// ---------------------------------------------- + +/* Stream related function and method */ + +// llgo:link (*Stream).GetWriteQueueSize C.uv_stream_get_write_queue_size +func (stream *Stream) GetWriteQueueSize() uintptr { + return 0 +} + +// llgo:link (*Stream).Listen C.uv_listen +func (stream *Stream) Listen(backlog c.Int, connectionCb ConnectionCb) c.Int { + return 0 +} + +// llgo:link (*Stream).Accept C.uv_accept +func (server *Stream) Accept(client *Stream) c.Int { + return 0 +} + +// llgo:link (*Stream).StartRead C.uv_read_start +func (stream *Stream) StartRead(allocCb AllocCb, readCb ReadCb) c.Int { + return 0 +} + +// llgo:link (*Stream).StopRead C.uv_read_stop +func (stream *Stream) StopRead() c.Int { + return 0 +} + +// llgo:link (*Write).Write C.uv_write +func (req *Write) Write(stream *Stream, bufs []Buf, nbufs c.Uint, writeCb WriteCb) c.Int { + return 0 +} + +// llgo:link (*Write).Write2 C.uv_write2 +func (req *Write) Write2(stream *Stream, bufs []Buf, nbufs c.Uint, sendStream *Stream, writeCb WriteCb) c.Int { + return 0 +} + +// llgo:link (*Stream).TryWrite C.uv_try_write +func (stream *Stream) TryWrite(bufs []Buf, nbufs c.Uint) c.Int { + return 0 +} + +// llgo:link (*Stream).TryWrite2 C.uv_try_write2 +func (stream *Stream) TryWrite2(bufs []Buf, nbufs c.Uint, sendStream *Stream) c.Int { + return 0 +} + +// llgo:link (*Stream).IsReadable C.uv_is_readable +func (stream *Stream) IsReadable() c.Int { + return 0 +} + +// llgo:link (*Stream).IsWritable C.uv_is_writable +func (stream *Stream) IsWritable() c.Int { + return 0 +} + +// llgo:link (*Stream).SetBlocking C.uv_stream_set_blocking +func (stream *Stream) SetBlocking(blocking c.Int) c.Int { + return 0 +} + +// ---------------------------------------------- + +/* Getaddrinfo related function and method */ + +//go:linkname Getaddrinfo C.uv_getaddrinfo +func Getaddrinfo(loop *Loop, req *GetAddrInfo, getaddrinfoCb GetaddrinfoCb, node *c.Char, service *c.Char, hints *net.AddrInfo) c.Int + +//go:linkname Freeaddrinfo C.uv_freeaddrinfo +func Freeaddrinfo(addrInfo *net.AddrInfo) + +// ---------------------------------------------- + +/* Getnameinfo related function and method */ + +//go:linkname Getnameinfo C.uv_getnameinfo +func Getnameinfo(loop *Loop, req *GetNameInfo, getnameinfoCb GetnameinfoCb, addr *net.SockAddr, flags c.Int) c.Int diff --git a/c/libuv/net.go b/c/libuv/net.go new file mode 100644 index 00000000..1e5f7590 --- /dev/null +++ b/c/libuv/net.go @@ -0,0 +1,256 @@ +package libuv + +import ( + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/net" + _ "unsafe" +) + +const ( + /* Used with uv_tcp_bind, when an IPv6 address is used. */ + TCP_IPV6ONLY TcpFlags = 1 +) + +/* + * UDP support. + */ +const ( + /* Disables dual stack mode. */ + UDP_IPV6ONLY UdpFlags = 1 + /* + * Indicates message was truncated because read buffer was too small. The + * remainder was discarded by the OS. Used in uv_udp_recv_cb. + */ + UDP_PARTIAL UdpFlags = 2 + /* + * Indicates if SO_REUSEADDR will be set when binding the handle. + * This sets the SO_REUSEPORT socket flag on the BSDs and OS X. On other + * Unix platforms, it sets the SO_REUSEADDR flag. What that means is that + * multiple threads or processes can bind to the same address without error + * (provided they all set the flag) but only the last one to bind will receive + * any traffic, in effect "stealing" the port from the previous listener. + */ + UDP_REUSEADDR UdpFlags = 4 + /* + * Indicates that the message was received by recvmmsg, so the buffer provided + * must not be freed by the recv_cb callback. + */ + UDP_MMSG_CHUNK UdpFlags = 8 + /* + * Indicates that the buffer provided has been fully utilized by recvmmsg and + * that it should now be freed by the recv_cb callback. When this flag is set + * in uv_udp_recv_cb, nread will always be 0 and addr will always be NULL. + */ + UDP_MMSG_FREE UdpFlags = 16 + /* + * Indicates if IP_RECVERR/IPV6_RECVERR will be set when binding the handle. + * This sets IP_RECVERR for IPv4 and IPV6_RECVERR for IPv6 UDP sockets on + * Linux. This stops the Linux kernel from suppressing some ICMP error + * messages and enables full ICMP error reporting for faster failover. + * This flag is no-op on platforms other than Linux. + */ + UDP_LINUX_RECVERR UdpFlags = 32 + /* + * Indicates that recvmmsg should be used, if available. + */ + UDP_RECVMMSG UdpFlags = 256 +) + +type TcpFlags c.Int + +type UdpFlags c.Int + +// ---------------------------------------------- + +/* Function type */ + +// llgo:type C +type CloseCb func(handle *Handle) + +// llgo:type C +type ConnectCb func(req *Connect, status c.Int) + +// llgo:type C +type UdpSendCb func(req *UdpSend, status c.Int) + +// llgo:type C +type UdpRecvCb func(handle *Udp, nread c.Long, buf *Buf, addr *net.SockAddr, flags c.Uint) + +// ---------------------------------------------- + +/* TcpT related function and method */ + +//go:linkname InitTcp C.uv_tcp_init +func InitTcp(loop *Loop, tcp *Tcp) c.Int + +//go:linkname InitTcpEx C.uv_tcp_init_ex +func InitTcpEx(loop *Loop, tcp *Tcp, flags c.Uint) c.Int + +// llgo:link (*Tcp).Open C.uv_tcp_open +func (tcp *Tcp) Open(sock OsSock) c.Int { + return 0 +} + +// llgo:link (*Tcp).Nodelay C.uv_tcp_nodelay +func (tcp *Tcp) Nodelay(enable c.Int) c.Int { + return 0 +} + +// llgo:link (*Tcp).KeepAlive C.uv_tcp_keepalive +func (tcp *Tcp) KeepAlive(enable c.Int, delay c.Uint) c.Int { + return 0 +} + +// llgo:link (*Tcp).SimultaneousAccepts C.uv_tcp_simultaneous_accepts +func (tcp *Tcp) SimultaneousAccepts(enable c.Int) c.Int { + return 0 +} + +// llgo:link (*Tcp).Bind C.uv_tcp_bind +func (tcp *Tcp) Bind(addr *net.SockAddr, flags c.Uint) c.Int { + return 0 +} + +// llgo:link (*Tcp).Getsockname C.uv_tcp_getsockname +func (tcp *Tcp) Getsockname(name *net.SockAddr, nameLen *c.Int) c.Int { + return 0 +} + +// llgo:link (*Tcp).Getpeername C.uv_tcp_getpeername +func (tcp *Tcp) Getpeername(name *net.SockAddr, nameLen *c.Int) c.Int { + return 0 +} + +// llgo:link (*Tcp).CloseReset C.uv_tcp_close_reset +func (tcp *Tcp) CloseReset(closeCb CloseCb) c.Int { + return 0 +} + +//go:linkname TcpConnect C.uv_tcp_connect +func TcpConnect(req *Connect, tcp *Tcp, addr *net.SockAddr, connectCb ConnectCb) c.Int + +// ---------------------------------------------- + +/* UdpT related function and method */ + +//go:linkname InitUdp C.uv_udp_init +func InitUdp(loop *Loop, udp *Udp) c.Int + +//go:linkname InitUdpEx C.uv_udp_init_ex +func InitUdpEx(loop *Loop, udp *Udp, flags c.Uint) c.Int + +// llgo:link (*Udp).Open C.uv_udp_open +func (udp *Udp) Open(sock OsSock) c.Int { + return 0 +} + +// llgo:link (*Udp).Bind C.uv_udp_bind +func (udp *Udp) Bind(addr *net.SockAddr, flags c.Uint) c.Int { + return 0 +} + +// llgo:link (*Udp).Connect C.uv_udp_connect +func (udp *Udp) Connect(addr *net.SockAddr) c.Int { + return 0 +} + +// llgo:link (*Udp).Getpeername C.uv_udp_getpeername +func (udp *Udp) Getpeername(name *net.SockAddr, nameLen *c.Int) c.Int { + return 0 +} + +// llgo:link (*Udp).Getsockname C.uv_udp_getsockname +func (udp *Udp) Getsockname(name *net.SockAddr, nameLen *c.Int) c.Int { + return 0 +} + +// llgo:link (*Udp).SetMembership C.uv_udp_set_membership +func (udp *Udp) SetMembership(multicastAddr *c.Char, interfaceAddr *c.Char, membership Membership) c.Int { + return 0 +} + +// llgo:link (*Udp).SourceMembership C.uv_udp_set_source_membership +func (udp *Udp) SourceMembership(multicastAddr *c.Char, interfaceAddr *c.Char, sourceAddr *c.Char, membership Membership) c.Int { + return 0 +} + +// llgo:link (*Udp).SetMulticastLoop C.uv_udp_set_multicast_loop +func (udp *Udp) SetMulticastLoop(on c.Int) c.Int { + return 0 +} + +// llgo:link (*Udp).SetMulticastTTL C.uv_udp_set_multicast_ttl +func (udp *Udp) SetMulticastTTL(ttl c.Int) c.Int { + return 0 +} + +// llgo:link (*Udp).SetMulticastInterface C.uv_udp_set_multicast_interface +func (udp *Udp) SetMulticastInterface(interfaceAddr *c.Char) c.Int { + return 0 +} + +// llgo:link (*Udp).SAetBroadcast C.uv_udp_set_broadcast +func (udp *Udp) SAetBroadcast(on c.Int) c.Int { + return 0 +} + +// llgo:link (*Udp).SetTTL C.uv_udp_set_ttl +func (udp *Udp) SetTTL(ttl c.Int) c.Int { + return 0 +} + +//go:linkname Send C.uv_udp_send +func Send(req *UdpSend, udp *Udp, bufs []Buf, nbufs c.Uint, addr *net.SockAddr, sendCb UdpSendCb) c.Int + +// llgo:link (*Udp).TrySend C.uv_udp_try_send +func (udp *Udp) TrySend(bufs []Buf, nbufs c.Uint, addr *net.SockAddr) c.Int { + return 0 +} + +// llgo:link (*Udp).RecvStart C.uv_udp_recv_start +func (udp *Udp) RecvStart(allocCb AllocCb, recvCb UdpRecvCb) c.Int { + return 0 +} + +// llgo:link (*Udp).UsingRecvmmsg C.uv_udp_using_recvmmsg +func (udp *Udp) UsingRecvmmsg() c.Int { + return 0 +} + +// llgo:link (*Udp).RecvStop C.uv_udp_recv_stop +func (udp *Udp) RecvStop() c.Int { + return 0 +} + +// llgo:link (*Udp).GetSendQueueSize C.uv_udp_get_send_queue_size +func (udp *Udp) GetSendQueueSize() uintptr { + return 0 +} + +// llgo:link (*Udp).GetSendQueueCount C.uv_udp_get_send_queue_count +func (udp *Udp) GetSendQueueCount() uintptr { + return 0 +} + +// ---------------------------------------------- + +//go:linkname Ip4Addr C.uv_ip4_addr +func Ip4Addr(ip *c.Char, port c.Int, addr *net.SockaddrIn) c.Int + +//go:linkname Ip6Addr C.uv_ip6_addr +func Ip6Addr(ip *c.Char, port c.Int, addr *net.SockaddrIn6) c.Int + +//go:linkname Ip4Name C.uv_ip4_name +func Ip4Name(src *net.SockaddrIn, dst *c.Char, size uintptr) c.Int + +//go:linkname Ip6Name C.uv_ip6_name +func Ip6Name(src *net.SockaddrIn6, dst *c.Char, size uintptr) c.Int + +//go:linkname IpName C.uv_ip_name +func IpName(src *net.SockAddr, dst *c.Char, size uintptr) c.Int + +//go:linkname InetNtop C.uv_inet_ntop +func InetNtop(af c.Int, src c.Pointer, dst *c.Char, size uintptr) c.Int + +//go:linkname InetPton C.uv_inet_pton +func InetPton(af c.Int, src *c.Char, dst c.Pointer) c.Int diff --git a/c/libuv/timer.go b/c/libuv/timer.go new file mode 100644 index 00000000..615da48b --- /dev/null +++ b/c/libuv/timer.go @@ -0,0 +1,42 @@ +package libuv + +import ( + "github.com/goplus/llgo/c" + _ "unsafe" +) + +// llgo:type C +type TimerCb func(timer *Timer) + +/* TimerT related function and method */ + +//go:linkname InitTimer C.uv_timer_init +func InitTimer(loop *Loop, timer *Timer) c.Int + +// llgo:link (*Timer).Start C.uv_timer_start +func (timer *Timer) Start(cb TimerCb, timeout uint64, repeat uint64) c.Int { + return 0 +} + +// llgo:link (*Timer).Stop C.uv_timer_stop +func (timer *Timer) Stop() c.Int { + return 0 +} + +// llgo:link (*Timer).Again C.uv_timer_again +func (timer *Timer) Again() c.Int { + return 0 +} + +// llgo:link (*Timer).SetRepeat C.uv_timer_set_repeat +func (timer *Timer) SetRepeat(repeat uint64) {} + +// llgo:link (*Timer).GetRepeat C.uv_timer_get_repeat +func (timer *Timer) GetRepeat() uint64 { + return 0 +} + +// llgo:link (*Timer).GetDueIn C.uv_timer_get_due_in +func (timer *Timer) GetDueIn() uint64 { + return 0 +} diff --git a/c/net/net.go b/c/net/net.go index 7b2608aa..0b794084 100644 --- a/c/net/net.go +++ b/c/net/net.go @@ -91,10 +91,23 @@ type SockaddrIn struct { Zero [8]c.Char } +type SockaddrIn6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo c.Uint + Addr In6Addr + ScopeId c.Uint +} + type InAddr struct { Addr c.Uint } +type In6Addr struct { + U6Addr [16]uint8 +} + type SockAddr struct { Len uint8 Family uint8 From c63580ee3813326c4179d349897aca99a9895b98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E8=8B=B1=E6=9D=B0?= <2635879218@qq.com> Date: Mon, 22 Jul 2024 18:02:09 +0800 Subject: [PATCH 009/152] feat(c/libuv/demo): Add libuv demo echo_server refactor(c/libuv): Adjust comments and file names to accommodate merge --- _demo/libuv/echo_server.go | 109 +++++++++++++++++++++++ c/libuv/_demo/echo_server/echo_server.go | 109 +++++++++++++++++++++++ c/libuv/error.go | 87 ------------------ c/libuv/libuv.go | 39 +------- c/libuv/net.go | 22 ++++- c/libuv/timer.go | 14 ++- 6 files changed, 252 insertions(+), 128 deletions(-) create mode 100644 _demo/libuv/echo_server.go create mode 100644 c/libuv/_demo/echo_server/echo_server.go diff --git a/_demo/libuv/echo_server.go b/_demo/libuv/echo_server.go new file mode 100644 index 00000000..81d18148 --- /dev/null +++ b/_demo/libuv/echo_server.go @@ -0,0 +1,109 @@ +package main + +import ( + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/libuv" + "github.com/goplus/llgo/c/net" + "unsafe" +) + +var DEFAULT_PORT c.Int = 8080 +var DEFAULT_BACKLOG c.Int = 128 + +var loop *libuv.Loop + +type WriteReq struct { + req libuv.Write + buf libuv.Buf +} + +func FreeWriteReq(req *libuv.Write) { + wr := (*WriteReq)(c.Pointer(req)) + c.Free(c.Pointer(wr.buf.Base)) + c.Free(c.Pointer(wr)) +} + +func AllocBuffer(handle *libuv.Handle, suggestedSize uintptr, buf *libuv.Buf) { + buf.Base = (*c.Char)(c.Malloc(suggestedSize)) + buf.Len = suggestedSize +} + +func EchoWrite(req *libuv.Write, status c.Int) { + if status != 0 { + c.Fprintf(c.Stderr, c.Str("write error: %s\n"), libuv.Strerror(status)) + } + defer FreeWriteReq(req) +} + +func EchoRead(client *libuv.Stream, nread c.Long, buf *libuv.Buf) { + if nread > 0 { + req := (*WriteReq)(c.Malloc(unsafe.Sizeof(WriteReq{}))) + if req == nil { + c.Fprintf(c.Stderr, c.Str("Failed to allocate memory for write request\n")) + defer c.Free(c.Pointer(buf.Base)) + return + } + req.buf = libuv.InitBuf(buf.Base, c.Uint(nread)) + (*libuv.Write)(c.Pointer(req)).Write(client, []libuv.Buf{req.buf}, 1, EchoWrite) + return + } + if nread < 0 { + if (libuv.Errno)(nread) != libuv.EOF { + c.Fprintf(c.Stderr, c.Str("Read error: %s\n"), libuv.Strerror(c.Int(nread))) + } + (*libuv.Handle)(client).Close(nil) + } + if buf.Base != nil { + c.Free(c.Pointer(buf.Base)) + } +} + +func OnNewConnection(server *libuv.Stream, status c.Int) { + if status < 0 { + c.Fprintf(c.Stderr, c.Str("New connection error: %s\n"), libuv.Strerror(status)) + return + } + + client := (*libuv.Tcp)(c.Malloc(unsafe.Sizeof(libuv.Tcp{}))) + + if client == nil { + c.Fprintf(c.Stderr, c.Str("Failed to allocate memory for client\n")) + return + } + + if libuv.InitTcp(loop, client) < 0 { + c.Fprintf(c.Stderr, c.Str("Failed to initialize client\n")) + c.Free(c.Pointer(client)) + return + } + + if server.Accept((*libuv.Stream)(client)) == 0 { + (*libuv.Stream)(client).StartRead(AllocBuffer, EchoRead) + } else { + (*libuv.Handle)(client).Close(nil) + } +} + +func main() { + // Initialize the default event loop + loop = libuv.DefaultLoop() + + // Initialize a TCP server + var server libuv.Tcp + libuv.InitTcp(loop, &server) + + // Set up the address to bind the server to + var addr net.SockaddrIn + libuv.Ip4Addr(c.Str("0.0.0.0"), DEFAULT_PORT, &addr) + + // Bind the server to the specified address and port + (&server).Bind((*net.SockAddr)(c.Pointer(&addr)), 0) + res := (*libuv.Stream)(&server).Listen(DEFAULT_BACKLOG, OnNewConnection) + if res != 0 { + c.Fprintf(c.Stderr, c.Str("Listen error: %s\n"), libuv.Strerror(res)) + return + } + + // Start listening for incoming connections + loop.Run(libuv.RUN_DEFAULT) +} diff --git a/c/libuv/_demo/echo_server/echo_server.go b/c/libuv/_demo/echo_server/echo_server.go new file mode 100644 index 00000000..81d18148 --- /dev/null +++ b/c/libuv/_demo/echo_server/echo_server.go @@ -0,0 +1,109 @@ +package main + +import ( + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/libuv" + "github.com/goplus/llgo/c/net" + "unsafe" +) + +var DEFAULT_PORT c.Int = 8080 +var DEFAULT_BACKLOG c.Int = 128 + +var loop *libuv.Loop + +type WriteReq struct { + req libuv.Write + buf libuv.Buf +} + +func FreeWriteReq(req *libuv.Write) { + wr := (*WriteReq)(c.Pointer(req)) + c.Free(c.Pointer(wr.buf.Base)) + c.Free(c.Pointer(wr)) +} + +func AllocBuffer(handle *libuv.Handle, suggestedSize uintptr, buf *libuv.Buf) { + buf.Base = (*c.Char)(c.Malloc(suggestedSize)) + buf.Len = suggestedSize +} + +func EchoWrite(req *libuv.Write, status c.Int) { + if status != 0 { + c.Fprintf(c.Stderr, c.Str("write error: %s\n"), libuv.Strerror(status)) + } + defer FreeWriteReq(req) +} + +func EchoRead(client *libuv.Stream, nread c.Long, buf *libuv.Buf) { + if nread > 0 { + req := (*WriteReq)(c.Malloc(unsafe.Sizeof(WriteReq{}))) + if req == nil { + c.Fprintf(c.Stderr, c.Str("Failed to allocate memory for write request\n")) + defer c.Free(c.Pointer(buf.Base)) + return + } + req.buf = libuv.InitBuf(buf.Base, c.Uint(nread)) + (*libuv.Write)(c.Pointer(req)).Write(client, []libuv.Buf{req.buf}, 1, EchoWrite) + return + } + if nread < 0 { + if (libuv.Errno)(nread) != libuv.EOF { + c.Fprintf(c.Stderr, c.Str("Read error: %s\n"), libuv.Strerror(c.Int(nread))) + } + (*libuv.Handle)(client).Close(nil) + } + if buf.Base != nil { + c.Free(c.Pointer(buf.Base)) + } +} + +func OnNewConnection(server *libuv.Stream, status c.Int) { + if status < 0 { + c.Fprintf(c.Stderr, c.Str("New connection error: %s\n"), libuv.Strerror(status)) + return + } + + client := (*libuv.Tcp)(c.Malloc(unsafe.Sizeof(libuv.Tcp{}))) + + if client == nil { + c.Fprintf(c.Stderr, c.Str("Failed to allocate memory for client\n")) + return + } + + if libuv.InitTcp(loop, client) < 0 { + c.Fprintf(c.Stderr, c.Str("Failed to initialize client\n")) + c.Free(c.Pointer(client)) + return + } + + if server.Accept((*libuv.Stream)(client)) == 0 { + (*libuv.Stream)(client).StartRead(AllocBuffer, EchoRead) + } else { + (*libuv.Handle)(client).Close(nil) + } +} + +func main() { + // Initialize the default event loop + loop = libuv.DefaultLoop() + + // Initialize a TCP server + var server libuv.Tcp + libuv.InitTcp(loop, &server) + + // Set up the address to bind the server to + var addr net.SockaddrIn + libuv.Ip4Addr(c.Str("0.0.0.0"), DEFAULT_PORT, &addr) + + // Bind the server to the specified address and port + (&server).Bind((*net.SockAddr)(c.Pointer(&addr)), 0) + res := (*libuv.Stream)(&server).Listen(DEFAULT_BACKLOG, OnNewConnection) + if res != 0 { + c.Fprintf(c.Stderr, c.Str("Listen error: %s\n"), libuv.Strerror(res)) + return + } + + // Start listening for incoming connections + loop.Run(libuv.RUN_DEFAULT) +} diff --git a/c/libuv/error.go b/c/libuv/error.go index 95ee68dd..b9397cf9 100644 --- a/c/libuv/error.go +++ b/c/libuv/error.go @@ -93,93 +93,6 @@ const ( ERRNO_MAX ) -//var errnoDescriptions = map[Errno]string{ -// E2BIG: "argument list too long", -// EACCES: "permission denied", -// EADDRINUSE: "address already in use", -// EADDRNOTAVAIL: "address not available", -// EAFNOSUPPORT: "address family not supported", -// EAGAIN: "resource temporarily unavailable", -// EAI_ADDRFAMILY: "address family not supported", -// EAI_AGAIN: "temporary failure", -// EAI_BADFLAGS: "bad ai_flags value", -// EAI_BADHINTS: "invalid value for hints", -// EAI_CANCELED: "request canceled", -// EAI_FAIL: "permanent failure", -// EAI_FAMILY: "ai_family not supported", -// EAI_MEMORY: "out of memory", -// EAI_NODATA: "no address", -// EAI_NONAME: "unknown node or service", -// EAI_OVERFLOW: "argument buffer overflow", -// EAI_PROTOCOL: "resolved protocol is unknown", -// EAI_SERVICE: "service not available for socket type", -// EAI_SOCKTYPE: "socket type not supported", -// EALREADY: "connection already in progress", -// EBADF: "bad file descriptor", -// EBUSY: "resource busy or locked", -// ECANCELED: "operation canceled", -// ECHARSET: "invalid Unicode character", -// ECONNABORTED: "software caused connection abort", -// ECONNREFUSED: "connection refused", -// ECONNRESET: "connection reset by peer", -// EDESTADDRREQ: "destination address required", -// EEXIST: "file already exists", -// EFAULT: "bad address in system call argument", -// EFBIG: "file too large", -// EHOSTUNREACH: "host is unreachable", -// EINTR: "interrupted system call", -// EINVAL: "invalid argument", -// EIO: "i/o error", -// EISCONN: "socket is already connected", -// EISDIR: "illegal operation on a directory", -// ELOOP: "too many symbolic links encountered", -// EMFILE: "too many open files", -// EMSGSIZE: "message too long", -// ENAMETOOLONG: "name too long", -// ENETDOWN: "network is down", -// ENETUNREACH: "network is unreachable", -// ENFILE: "file table overflow", -// ENOBUFS: "no buffer space available", -// ENODEV: "no such device", -// ENOENT: "no such file or directory", -// ENOMEM: "not enough memory", -// ENONET: "machine is not on the network", -// ENOPROTOOPT: "protocol not available", -// ENOSPC: "no space left on device", -// ENOSYS: "function not implemented", -// ENOTCONN: "socket is not connected", -// ENOTDIR: "not a directory", -// ENOTEMPTY: "directory not empty", -// ENOTSOCK: "socket operation on non-socket", -// ENOTSUP: "operation not supported on socket", -// EOVERFLOW: "value too large for defined data type", -// EPERM: "operation not permitted", -// EPIPE: "broken pipe", -// EPROTO: "protocol error", -// EPROTONOSUPPORT: "protocol not supported", -// EPROTOTYPE: "protocol wrong type for socket", -// ERANGE: "result too large", -// EROFS: "read-only file system", -// ESHUTDOWN: "cannot send after transport endpoint shutdown", -// ESPIPE: "invalid seek", -// ESRCH: "no such process", -// ETIMEDOUT: "connection timed out", -// ETXTBSY: "text file is busy", -// EXDEV: "cross-device link not permitted", -// UNKNOWN: "unknown error", -// EOF: "end of file", -// ENXIO: "no such device or address", -// EMLINK: "too many links", -// EHOSTDOWN: "host is down", -// EREMOTEIO: "remote I/O error", -// ENOTTY: "inappropriate ioctl for device", -// EFTYPE: "inappropriate file type or format", -// EILSEQ: "illegal byte sequence", -// ESOCKTNOSUPPORT: "socket type not supported", -// ENODATA: "no data available", -// EUNATCH: "protocol driver not attached", -//} - type Errno c.Int //go:linkname TranslateSysError C.uv_translate_sys_error diff --git a/c/libuv/libuv.go b/c/libuv/libuv.go index 004b223d..fb0c7664 100644 --- a/c/libuv/libuv.go +++ b/c/libuv/libuv.go @@ -17,12 +17,6 @@ const ( METRICS_IDLE_TIME ) -const ( - RUN_DEFAULT RunMode = iota - RUN_ONCE - RUN_NOWAIT -) - const ( UV_LEAVE_GROUP Membership = iota UV_JOIN_GROUP @@ -68,8 +62,6 @@ const ( type LoopOption c.Int -type RunMode c.Int - type Membership c.Int type HandleType c.Int @@ -85,9 +77,6 @@ type OsFd c.Int // ---------------------------------------------- /* Handle types. */ -type Loop struct { - Unused [0]byte -} type Handle struct { Unused [0]byte @@ -101,14 +90,6 @@ type Stream struct { Unused [0]byte } -type Tcp struct { - Unused [0]byte -} - -type Udp struct { - Unused [0]byte -} - type Pipe struct { Unused [0]byte } @@ -121,10 +102,6 @@ type Poll struct { Unused [0]byte } -type Timer struct { - Unused [0]byte -} - type Prepare struct { Unused [0]byte } @@ -183,17 +160,6 @@ type Connect struct { Unused [0]byte } -type UdpSend struct { - Unused [0]byte -} - -// ---------------------------------------------- - -type Buf struct { - Base *c.Char - Len uintptr -} - // ---------------------------------------------- /* Function type */ @@ -257,7 +223,7 @@ func (shutdown *Shutdown) Shutdown(stream *Stream, shutdownCb ShutdownCb) c.Int // ---------------------------------------------- -/* HandleT related function and method */ +/* Handle related function and method */ // llgo:link (*Handle).Ref C.uv_ref func (handle *Handle) Ref() {} @@ -317,9 +283,6 @@ func (handle *Handle) Fileno(fd *OsFd) c.Int { return 0 } -//go:linkname InitBuf C.uv_buf_init -func InitBuf(base *c.Char, len c.Uint) Buf - //go:linkname UvPipe C.uv_pipe func UvPipe(fds [2]Uv_File, readFlags c.Int, writeFlags c.Int) c.Int { return 0 diff --git a/c/libuv/net.go b/c/libuv/net.go index 1e5f7590..e2f824f6 100644 --- a/c/libuv/net.go +++ b/c/libuv/net.go @@ -62,6 +62,24 @@ type UdpFlags c.Int // ---------------------------------------------- +/* Handle types. */ + +type Tcp struct { + Unused [0]byte +} + +type Udp struct { + Unused [0]byte +} + +/* Request types. */ + +type UdpSend struct { + Unused [0]byte +} + +// ---------------------------------------------- + /* Function type */ // llgo:type C @@ -78,7 +96,7 @@ type UdpRecvCb func(handle *Udp, nread c.Long, buf *Buf, addr *net.SockAddr, fla // ---------------------------------------------- -/* TcpT related function and method */ +/* Tcp related function and method */ //go:linkname InitTcp C.uv_tcp_init func InitTcp(loop *Loop, tcp *Tcp) c.Int @@ -131,7 +149,7 @@ func TcpConnect(req *Connect, tcp *Tcp, addr *net.SockAddr, connectCb ConnectCb) // ---------------------------------------------- -/* UdpT related function and method */ +/* Udp related function and method */ //go:linkname InitUdp C.uv_udp_init func InitUdp(loop *Loop, udp *Udp) c.Int diff --git a/c/libuv/timer.go b/c/libuv/timer.go index 615da48b..fff1af5d 100644 --- a/c/libuv/timer.go +++ b/c/libuv/timer.go @@ -5,10 +5,22 @@ import ( _ "unsafe" ) +// ---------------------------------------------- + +/* Handle types. */ + +type Timer struct { + Unused [0]byte +} + +// ---------------------------------------------- + // llgo:type C type TimerCb func(timer *Timer) -/* TimerT related function and method */ +// ---------------------------------------------- + +/* Timer related function and method */ //go:linkname InitTimer C.uv_timer_init func InitTimer(loop *Loop, timer *Timer) c.Int From c27c654180eff36bbd5daaaacd086b48db8d488d Mon Sep 17 00:00:00 2001 From: hackerchai Date: Tue, 23 Jul 2024 11:43:02 +0800 Subject: [PATCH 010/152] refactor(c/libuv): general fixes & optimize code Signed-off-by: hackerchai refactor(c/libuv): optimize functions Signed-off-by: hackerchai refactor(c/libuv): mv name Signed-off-by: hackerchai refactor(c/libuv): modify libs Signed-off-by: hackerchai refactor(c/libuv): use new buffer arg Signed-off-by: hackerchai refactor(c/libuv/demo): optimize code style with go style Signed-off-by: hackerchai refactor(c/libuv): optimize code and add comment Signed-off-by: hackerchai fix(c/libuv): fix TranslateSysError Signed-off-by: hackerchai refactor(c/libuv): remove go wrapper Signed-off-by: hackerchai refactor(c/libuv/demo): refactor c style Signed-off-by: hackerchai refactor(c/liobuv/demo): Some adjustments after removing go wrapper refactor(c/libuv/demo): add print in echo_server Signed-off-by: hackerchai doc(c/libuv): add README.md for c/libuv Signed-off-by: hackerchai feat(c/libuv): implement poll_init_socket Signed-off-by: hackerchai refactor(c/libuv): mv poll_init_socket function Signed-off-by: hackerchai refactor(demo): remove libuv demo Signed-off-by: hackerchai --- _demo/libuv/echo_server.go | 109 -------- c/libuv/README.md | 44 ++++ c/libuv/_demo/async_fs/async_fs.go | 97 +++++++ c/libuv/_demo/async_fs/example.txt | 1 + c/libuv/_demo/echo_server/echo_server.go | 153 +++++------ c/libuv/_demo/libuv/fs.go | 70 ------ c/libuv/fs.go | 308 ++--------------------- c/libuv/libuv.go | 130 ++++++++-- c/libuv/net.go | 18 +- c/libuv/signal.go | 24 +- c/libuv/timer.go | 2 +- 11 files changed, 378 insertions(+), 578 deletions(-) delete mode 100644 _demo/libuv/echo_server.go create mode 100644 c/libuv/README.md create mode 100644 c/libuv/_demo/async_fs/async_fs.go create mode 100755 c/libuv/_demo/async_fs/example.txt delete mode 100644 c/libuv/_demo/libuv/fs.go diff --git a/_demo/libuv/echo_server.go b/_demo/libuv/echo_server.go deleted file mode 100644 index 81d18148..00000000 --- a/_demo/libuv/echo_server.go +++ /dev/null @@ -1,109 +0,0 @@ -package main - -import ( - "github.com/goplus/llgo/c" - "github.com/goplus/llgo/c/libuv" - "github.com/goplus/llgo/c/net" - "unsafe" -) - -var DEFAULT_PORT c.Int = 8080 -var DEFAULT_BACKLOG c.Int = 128 - -var loop *libuv.Loop - -type WriteReq struct { - req libuv.Write - buf libuv.Buf -} - -func FreeWriteReq(req *libuv.Write) { - wr := (*WriteReq)(c.Pointer(req)) - c.Free(c.Pointer(wr.buf.Base)) - c.Free(c.Pointer(wr)) -} - -func AllocBuffer(handle *libuv.Handle, suggestedSize uintptr, buf *libuv.Buf) { - buf.Base = (*c.Char)(c.Malloc(suggestedSize)) - buf.Len = suggestedSize -} - -func EchoWrite(req *libuv.Write, status c.Int) { - if status != 0 { - c.Fprintf(c.Stderr, c.Str("write error: %s\n"), libuv.Strerror(status)) - } - defer FreeWriteReq(req) -} - -func EchoRead(client *libuv.Stream, nread c.Long, buf *libuv.Buf) { - if nread > 0 { - req := (*WriteReq)(c.Malloc(unsafe.Sizeof(WriteReq{}))) - if req == nil { - c.Fprintf(c.Stderr, c.Str("Failed to allocate memory for write request\n")) - defer c.Free(c.Pointer(buf.Base)) - return - } - req.buf = libuv.InitBuf(buf.Base, c.Uint(nread)) - (*libuv.Write)(c.Pointer(req)).Write(client, []libuv.Buf{req.buf}, 1, EchoWrite) - return - } - if nread < 0 { - if (libuv.Errno)(nread) != libuv.EOF { - c.Fprintf(c.Stderr, c.Str("Read error: %s\n"), libuv.Strerror(c.Int(nread))) - } - (*libuv.Handle)(client).Close(nil) - } - if buf.Base != nil { - c.Free(c.Pointer(buf.Base)) - } -} - -func OnNewConnection(server *libuv.Stream, status c.Int) { - if status < 0 { - c.Fprintf(c.Stderr, c.Str("New connection error: %s\n"), libuv.Strerror(status)) - return - } - - client := (*libuv.Tcp)(c.Malloc(unsafe.Sizeof(libuv.Tcp{}))) - - if client == nil { - c.Fprintf(c.Stderr, c.Str("Failed to allocate memory for client\n")) - return - } - - if libuv.InitTcp(loop, client) < 0 { - c.Fprintf(c.Stderr, c.Str("Failed to initialize client\n")) - c.Free(c.Pointer(client)) - return - } - - if server.Accept((*libuv.Stream)(client)) == 0 { - (*libuv.Stream)(client).StartRead(AllocBuffer, EchoRead) - } else { - (*libuv.Handle)(client).Close(nil) - } -} - -func main() { - // Initialize the default event loop - loop = libuv.DefaultLoop() - - // Initialize a TCP server - var server libuv.Tcp - libuv.InitTcp(loop, &server) - - // Set up the address to bind the server to - var addr net.SockaddrIn - libuv.Ip4Addr(c.Str("0.0.0.0"), DEFAULT_PORT, &addr) - - // Bind the server to the specified address and port - (&server).Bind((*net.SockAddr)(c.Pointer(&addr)), 0) - res := (*libuv.Stream)(&server).Listen(DEFAULT_BACKLOG, OnNewConnection) - if res != 0 { - c.Fprintf(c.Stderr, c.Str("Listen error: %s\n"), libuv.Strerror(res)) - return - } - - // Start listening for incoming connections - loop.Run(libuv.RUN_DEFAULT) -} diff --git a/c/libuv/README.md b/c/libuv/README.md new file mode 100644 index 00000000..30f2ed5b --- /dev/null +++ b/c/libuv/README.md @@ -0,0 +1,44 @@ +LLGo wrapper of libuv +===== + +## How to install + +### on macOS (Homebrew) + +```sh +brew install libuv +``` + +### on Linux (Debian/Ubuntu) + +```sh +apt-get install -y libuv1-dev +``` + +### on Linux (CentOS/RHEL) + +```sh +yum install -y libuv-devel +``` + +### on Linux (Arch Linux) + +```sh +pacman -S libuv +``` + +## Demos + +The `_demo` directory contains our demos (it start with `_` to prevent the `go` command from compiling it): + +* [async_fs](_demo/async_fs/async_fs.go): a simple async file read demo +* [echo_server](_demo/echo_server/echo_server.go): a basic async tcp echo server + +### How to run demos + +To run the demos in directory `_demo`: + +```sh +cd # eg. cd _demo/sqlitedemo +llgo run . +``` diff --git a/c/libuv/_demo/async_fs/async_fs.go b/c/libuv/_demo/async_fs/async_fs.go new file mode 100644 index 00000000..6a549441 --- /dev/null +++ b/c/libuv/_demo/async_fs/async_fs.go @@ -0,0 +1,97 @@ +package main + +import ( + "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/libuv" + "github.com/goplus/llgo/c/os" +) + +const BUFFER_SIZE = 1024 + +var ( + loop *libuv.Loop + openReq libuv.Fs + readReq libuv.Fs + closeReq libuv.Fs + + buffer [BUFFER_SIZE]c.Char + iov libuv.Buf +) + +func main() { + // Print the libuv version + c.Printf(c.Str("libuv version: %d\n"), libuv.Version()) + + // Initialize the loop + loop = libuv.DefaultLoop() + + // Open the file + libuv.FsOpen(loop, &openReq, c.Str("example.txt"), os.O_RDONLY, 0, onOpen) + + // Run the loop + libuv.Run(loop, libuv.RUN_DEFAULT) + + // Cleanup + defer cleanup() +} + +func onOpen(req *libuv.Fs) { + // Check for errors + if libuv.FsGetResult(req) < 0 { + c.Fprintf(c.Stderr, c.Str("Error opening file: %s\n"), libuv.Strerror(libuv.LoopClose(loop))) + libuv.LoopClose(loop) + return + } + // Init buffer + iov = libuv.InitBuf((*c.Char)(unsafe.Pointer(&buffer[0])), c.Uint(unsafe.Sizeof(buffer))) + // Read the file + readRes := libuv.FsRead(loop, &readReq, libuv.UvFile(libuv.FsGetResult(req)), &iov, 1, -1, onRead) + if readRes != 0 { + c.Printf(c.Str("Error in FsRead: %s (code: %d)\n"), libuv.Strerror(c.Int(readRes)), readRes) + libuv.LoopClose(loop) + return + } +} + +func onRead(req *libuv.Fs) { + // Check for errors + if libuv.FsGetResult(req) < 0 { + c.Fprintf(c.Stderr, c.Str("Read error: %s\n"), libuv.Strerror(libuv.FsGetResult(req))) + libuv.LoopClose(loop) + } else if libuv.FsGetResult(req) == 0 { + c.Printf(c.Str("EOF\n")) + // Close the file + closeRes := libuv.FsClose(loop, &closeReq, libuv.UvFile(libuv.FsGetResult(&openReq)), onClose) + if closeRes != 0 { + // Print the content + c.Printf(c.Str("Error in FsClose: %s (code: %d)\n"), libuv.Strerror(c.Int(closeRes)), closeRes) + libuv.LoopClose(loop) + return + } + } else { + c.Printf(c.Str("Read %d bytes\n"), libuv.FsGetResult(req)) + c.Printf(c.Str("Read content: %.*s\n"), libuv.FsGetResult(req), (*c.Char)(unsafe.Pointer(&buffer[0]))) + libuv.LoopClose(loop) + } +} + +func onClose(req *libuv.Fs) { + // Check for errors + if libuv.FsGetResult(req) < 0 { + c.Fprintf(c.Stderr, c.Str("Error closing file: %s\n"), libuv.Strerror(libuv.FsGetResult(req))) + } else { + c.Printf(c.Str("\nFile closed successfully.\n")) + } + libuv.LoopClose(loop) +} + +func cleanup() { + // Cleanup the requests + libuv.FsReqCleanup(&openReq) + libuv.FsReqCleanup(&readReq) + libuv.FsReqCleanup(&closeReq) + // Close the loop + libuv.LoopClose(loop) +} diff --git a/c/libuv/_demo/async_fs/example.txt b/c/libuv/_demo/async_fs/example.txt new file mode 100755 index 00000000..190a1803 --- /dev/null +++ b/c/libuv/_demo/async_fs/example.txt @@ -0,0 +1 @@ +123 diff --git a/c/libuv/_demo/echo_server/echo_server.go b/c/libuv/_demo/echo_server/echo_server.go index 81d18148..fdb04b5b 100644 --- a/c/libuv/_demo/echo_server/echo_server.go +++ b/c/libuv/_demo/echo_server/echo_server.go @@ -1,10 +1,11 @@ package main import ( + "unsafe" + "github.com/goplus/llgo/c" "github.com/goplus/llgo/c/libuv" "github.com/goplus/llgo/c/net" - "unsafe" ) var DEFAULT_PORT c.Int = 8080 @@ -13,75 +14,8 @@ var DEFAULT_BACKLOG c.Int = 128 var loop *libuv.Loop type WriteReq struct { - req libuv.Write - buf libuv.Buf -} - -func FreeWriteReq(req *libuv.Write) { - wr := (*WriteReq)(c.Pointer(req)) - c.Free(c.Pointer(wr.buf.Base)) - c.Free(c.Pointer(wr)) -} - -func AllocBuffer(handle *libuv.Handle, suggestedSize uintptr, buf *libuv.Buf) { - buf.Base = (*c.Char)(c.Malloc(suggestedSize)) - buf.Len = suggestedSize -} - -func EchoWrite(req *libuv.Write, status c.Int) { - if status != 0 { - c.Fprintf(c.Stderr, c.Str("write error: %s\n"), libuv.Strerror(status)) - } - defer FreeWriteReq(req) -} - -func EchoRead(client *libuv.Stream, nread c.Long, buf *libuv.Buf) { - if nread > 0 { - req := (*WriteReq)(c.Malloc(unsafe.Sizeof(WriteReq{}))) - if req == nil { - c.Fprintf(c.Stderr, c.Str("Failed to allocate memory for write request\n")) - defer c.Free(c.Pointer(buf.Base)) - return - } - req.buf = libuv.InitBuf(buf.Base, c.Uint(nread)) - (*libuv.Write)(c.Pointer(req)).Write(client, []libuv.Buf{req.buf}, 1, EchoWrite) - return - } - if nread < 0 { - if (libuv.Errno)(nread) != libuv.EOF { - c.Fprintf(c.Stderr, c.Str("Read error: %s\n"), libuv.Strerror(c.Int(nread))) - } - (*libuv.Handle)(client).Close(nil) - } - if buf.Base != nil { - c.Free(c.Pointer(buf.Base)) - } -} - -func OnNewConnection(server *libuv.Stream, status c.Int) { - if status < 0 { - c.Fprintf(c.Stderr, c.Str("New connection error: %s\n"), libuv.Strerror(status)) - return - } - - client := (*libuv.Tcp)(c.Malloc(unsafe.Sizeof(libuv.Tcp{}))) - - if client == nil { - c.Fprintf(c.Stderr, c.Str("Failed to allocate memory for client\n")) - return - } - - if libuv.InitTcp(loop, client) < 0 { - c.Fprintf(c.Stderr, c.Str("Failed to initialize client\n")) - c.Free(c.Pointer(client)) - return - } - - if server.Accept((*libuv.Stream)(client)) == 0 { - (*libuv.Stream)(client).StartRead(AllocBuffer, EchoRead) - } else { - (*libuv.Handle)(client).Close(nil) - } + Req libuv.Write + Buf libuv.Buf } func main() { @@ -95,6 +29,7 @@ func main() { // Set up the address to bind the server to var addr net.SockaddrIn libuv.Ip4Addr(c.Str("0.0.0.0"), DEFAULT_PORT, &addr) + c.Printf(c.Str("Listening on %s:%d\n"), c.Str("0.0.0.0"), DEFAULT_PORT) // Bind the server to the specified address and port (&server).Bind((*net.SockAddr)(c.Pointer(&addr)), 0) @@ -105,5 +40,81 @@ func main() { } // Start listening for incoming connections - loop.Run(libuv.RUN_DEFAULT) + libuv.Run(loop, libuv.RUN_DEFAULT) +} + +func FreeWriteReq(req *libuv.Write) { + wr := (*WriteReq)(c.Pointer(req)) + // Free the buffer base and the WriteReq itself. + c.Free(c.Pointer(wr.Buf.Base)) + c.Free(c.Pointer(wr)) +} + +func AllocBuffer(handle *libuv.Handle, suggestedSize uintptr, buf *libuv.Buf) { + // Allocate memory for the buffer based on the suggested size. + buf.Base = (*c.Char)(c.Malloc(suggestedSize)) + buf.Len = suggestedSize +} + +func EchoWrite(req *libuv.Write, status c.Int) { + if status != 0 { + c.Fprintf(c.Stderr, c.Str("Write error: %s\n"), libuv.Strerror(status)) + } + FreeWriteReq(req) +} + +func EchoRead(client *libuv.Stream, nread c.Long, buf *libuv.Buf) { + if nread > 0 { + req := (*WriteReq)(c.Malloc(unsafe.Sizeof(WriteReq{}))) + if req == nil { + c.Fprintf(c.Stderr, c.Str("Failed to allocate memory for write request\n")) + c.Free(c.Pointer(buf.Base)) + return + } + // Initialize the buffer with the data read. + req.Buf = libuv.InitBuf(buf.Base, c.Uint(nread)) + // Write the data back to the client. + (&req.Req).Write(client, &req.Buf, 1, EchoWrite) + return + } + if nread < 0 { + // Handle read errors and EOF. + if (libuv.Errno)(nread) != libuv.EOF { + c.Fprintf(c.Stderr, c.Str("Read error: %s\n"), libuv.Strerror(c.Int(nread))) + } + (*libuv.Handle)(c.Pointer(client)).Close(nil) + } + // Free the buffer if it's no longer needed. + if buf.Base != nil { + c.Free(c.Pointer(buf.Base)) + } +} + +func OnNewConnection(server *libuv.Stream, status c.Int) { + if status < 0 { + c.Fprintf(c.Stderr, c.Str("New connection error: %s\n"), libuv.Strerror(status)) + return + } + + // Allocate memory for a new client. + client := (*libuv.Tcp)(c.Malloc(unsafe.Sizeof(libuv.Tcp{}))) + + if client == nil { + c.Fprintf(c.Stderr, c.Str("Failed to allocate memory for client\n")) + return + } + + // Initialize the client TCP handle. + if libuv.InitTcp(loop, client) < 0 { + c.Fprintf(c.Stderr, c.Str("Failed to initialize client\n")) + c.Free(c.Pointer(client)) + return + } + + // Accept the new connection and start reading data. + if server.Accept((*libuv.Stream)(client)) == 0 { + (*libuv.Stream)(client).StartRead(AllocBuffer, EchoRead) + } else { + (*libuv.Handle)(c.Pointer(client)).Close(nil) + } } diff --git a/c/libuv/_demo/libuv/fs.go b/c/libuv/_demo/libuv/fs.go deleted file mode 100644 index 7d50f2e9..00000000 --- a/c/libuv/_demo/libuv/fs.go +++ /dev/null @@ -1,70 +0,0 @@ -package main - -import ( - "github.com/goplus/llgo/c" - "github.com/goplus/llgo/c/libuv" - "github.com/goplus/llgo/c/os" - "golang.org/x/tools/container/intsets" - "unsafe" -) - -const bufferSize = 1024 - -var buffer []c.Char -var iov libuv.Buf - -var loop *libuv.Loop -var readReq *libuv.Fs -var closeReq *libuv.Fs -var openReq *libuv.Fs - -func initBuffer() { - buffer = make([]c.Char, bufferSize) -} - -func main() { - loop = libuv.DefaultLoop() - initBuffer() - - file := libuv.NewFile(loop, openReq) - - path := c.Str("example.txt") - - file.Open(path, os.O_RDONLY, 0, onOpen) - - loop.Run(libuv.RUN_DEFAULT) - - libuv.FsReqCleanup(openReq) - loop.Close() - c.Free(unsafe.Pointer(loop)) -} - -func onOpen(req *libuv.Fs) { - if req.GetResult() < 0 { - c.Fprintf(c.Stderr, c.Str("Error opening file: %s\n"), libuv.Strerror(req.GetResult())) - } else { - iov = libuv.InitBuf(unsafe.SliceData(buffer), c.Uint(unsafe.Sizeof(buffer))) - libuv.FsRead(loop, readReq, req.GetResult(), []libuv.Buf{iov}, 1, -1, onRead) - } - libuv.FsReqCleanup(req) -} - -func onRead(req *libuv.Fs) { - if req.GetResult() < 0 { - c.Fprintf(c.Stderr, c.Str("Read error: %s\n"), libuv.Strerror(req.GetResult())) - } else if req.GetResult() == 0 { - libuv.FsClose(loop, closeReq, req.GetResult(), onClose) - } else { - if req.GetResult() > intsets.MaxInt { - c.Fprintf(c.Stderr, c.Str("Too big file.\n")) - } - c.Printf(c.Str("%.*s"), c.Int(req.GetResult()), buffer) - libuv.FsRead(loop, req, req.GetResult(), []libuv.Buf{iov}, 1, -1, onRead) - } - libuv.FsReqCleanup(req) -} - -func onClose(req *libuv.Fs) { - c.Printf(c.Str("\nFile closed.\n")) - libuv.FsReqCleanup(req) -} diff --git a/c/libuv/fs.go b/c/libuv/fs.go index 555b5b0f..0de0a458 100644 --- a/c/libuv/fs.go +++ b/c/libuv/fs.go @@ -57,9 +57,11 @@ const ( DirentBlock ) -type FsType int +type FsType c.Int -type DirentType int +type DirentType c.Int + +type UvFile c.Int // ---------------------------------------------- @@ -108,35 +110,23 @@ type FsPollCb func(handle *FsPoll, status c.Int, events c.Int) /* Fs related function and method */ -// llgo:link (*Fs).GetType C.uv_fs_get_type -func (f *Fs) GetType() *FsType { - return nil -} +//go:linkname FsGetType C.uv_fs_get_type +func FsGetType(req *Fs) *FsType -// llgo:link (*Fs).GetPath C.uv_fs_get_path -func (f *Fs) GetPath() *c.Char { - return nil -} +//go:linkname FsGetPath C.uv_fs_get_path +func FsGetPath(req *Fs) *c.Char -// llgo:link (*Fs).GetResult C.uv_fs_get_result -func (f *Fs) GetResult() c.Int { - return 0 -} +//go:linkname FsGetResult C.uv_fs_get_result +func FsGetResult(req *Fs) c.Int -// llgo:link (*Fs).GetPtr C.uv_fs_get_ptr -func (f *Fs) GetPtr() c.Pointer { - return nil -} +//go:linkname FsGetPtr C.uv_fs_get_ptr +func FsGetPtr(req *Fs) c.Pointer -// llgo:link (*Fs).GetSystemError C.uv_fs_get_system_error -func (f *Fs) GetSystemError() c.Int { - return 0 -} +//go:linkname FsGetSystemError C.uv_fs_get_system_error +func FsGetSystemError(req *Fs) c.Int -// llgo:link (*Fs).GetStatBuf C.uv_fs_get_statbuf -func (f *Fs) GetStatBuf() *Stat { - return nil -} +//go:linkname FsGetStatBuf C.uv_fs_get_statbuf +func FsGetStatBuf(req *Fs) *Stat //go:linkname FsReqCleanup C.uv_fs_req_cleanup func FsReqCleanup(req *Fs) @@ -148,13 +138,13 @@ func DefaultLoop() *Loop func FsOpen(loop *Loop, req *Fs, path *c.Char, flags c.Int, mode c.Int, cb FsCb) c.Int //go:linkname FsClose C.uv_fs_close -func FsClose(loop *Loop, req *Fs, file c.Int, cb FsCb) c.Int +func FsClose(loop *Loop, req *Fs, file UvFile, cb FsCb) c.Int //go:linkname FsRead C.uv_fs_read -func FsRead(loop *Loop, req *Fs, file c.Int, bufs []Buf, nbufs c.Uint, offset c.Int, cb FsCb) c.Int +func FsRead(loop *Loop, req *Fs, file UvFile, bufs *Buf, nbufs c.Uint, offset c.LongLong, cb FsCb) c.Int //go:linkname FsWrite C.uv_fs_write -func FsWrite(loop *Loop, req *Fs, file c.Int, bufs []Buf, nbufs c.Uint, offset c.Int, cb FsCb) c.Int +func FsWrite(loop *Loop, req *Fs, file UvFile, bufs *Buf, nbufs c.Uint, offset c.LongLong, cb FsCb) c.Int //go:linkname FsUnlink C.uv_fs_unlink func FsUnlink(loop *Loop, req *Fs, path *c.Char, cb FsCb) c.Int @@ -175,22 +165,22 @@ func FsRmdir(loop *Loop, req *Fs, path *c.Char, cb FsCb) c.Int func FsStat(loop *Loop, req *Fs, path *c.Char, cb FsCb) c.Int //go:linkname FsFstat C.uv_fs_fstat -func FsFstat(loop *Loop, req *Fs, file c.Int, cb FsCb) c.Int +func FsFstat(loop *Loop, req *Fs, file UvFile, cb FsCb) c.Int //go:linkname FsRename C.uv_fs_rename func FsRename(loop *Loop, req *Fs, path *c.Char, newPath *c.Char, cb FsCb) c.Int //go:linkname FsFsync C.uv_fs_fsync -func FsFsync(loop *Loop, req *Fs, file c.Int, cb FsCb) c.Int +func FsFsync(loop *Loop, req *Fs, file UvFile, cb FsCb) c.Int //go:linkname FsFdatasync C.uv_fs_fdatasync -func FsFdatasync(loop *Loop, req *Fs, file c.Int, cb FsCb) c.Int +func FsFdatasync(loop *Loop, req *Fs, file UvFile, cb FsCb) c.Int //go:linkname FsFtruncate C.uv_fs_ftruncate -func FsFtruncate(loop *Loop, req *Fs, file c.Int, offset c.Int, cb FsCb) c.Int +func FsFtruncate(loop *Loop, req *Fs, file UvFile, offset c.LongLong, cb FsCb) c.Int //go:linkname FsSendfile C.uv_fs_sendfile -func FsSendfile(loop *Loop, req *Fs, outFd c.Int, inFd c.Int, inOffset c.Int, length c.Int, cb FsCb) c.Int +func FsSendfile(loop *Loop, req *Fs, outFd c.Int, inFd c.Int, inOffset c.LongLong, length c.Int, cb FsCb) c.Int //go:linkname FsAccess C.uv_fs_access func FsAccess(loop *Loop, req *Fs, path *c.Char, flags c.Int, cb FsCb) c.Int @@ -199,13 +189,13 @@ func FsAccess(loop *Loop, req *Fs, path *c.Char, flags c.Int, cb FsCb) c.Int func FsChmod(loop *Loop, req *Fs, path *c.Char, mode c.Int, cb FsCb) c.Int //go:linkname FsFchmod C.uv_fs_fchmod -func FsFchmod(loop *Loop, req *Fs, file c.Int, mode c.Int, cb FsCb) c.Int +func FsFchmod(loop *Loop, req *Fs, file UvFile, mode c.Int, cb FsCb) c.Int //go:linkname FsUtime C.uv_fs_utime func FsUtime(loop *Loop, req *Fs, path *c.Char, atime c.Int, mtime c.Int, cb FsCb) c.Int //go:linkname FsFutime C.uv_fs_futime -func FsFutime(loop *Loop, req *Fs, file c.Int, atime c.Int, mtime c.Int, cb FsCb) c.Int +func FsFutime(loop *Loop, req *Fs, file UvFile, atime c.Int, mtime c.Int, cb FsCb) c.Int //go:linkname FsLutime C.uv_fs_lutime func FsLutime(loop *Loop, req *Fs, path *c.Char, atime c.Int, mtime c.Int, cb FsCb) c.Int @@ -247,7 +237,7 @@ func FsStatfs(loop *Loop, req *Fs, path *c.Char, cb FsCb) c.Int func FsChown(loop *Loop, req *Fs, path *c.Char, uid c.Int, gid c.Int, cb FsCb) c.Int //go:linkname FsFchown C.uv_fs_fchown -func FsFchown(loop *Loop, req *Fs, file c.Int, uid c.Int, gid c.Int, cb FsCb) c.Int +func FsFchown(loop *Loop, req *Fs, file UvFile, uid c.Int, gid c.Int, cb FsCb) c.Int //go:linkname FsLchown C.uv_fs_lchown func FsLchown(loop *Loop, req *Fs, path *c.Char, uid c.Int, gid c.Int, cb FsCb) c.Int @@ -284,247 +274,3 @@ func FsPollClose(handle *FsPoll) c.Int //go:linkname FsPollGetPath C.uv_fs_poll_getpath func FsPollGetPath(handle *FsPoll) *c.Char - -//TODO: Implemnt uv_poll_init_socket - -// Cleanup cleans up the file system request. -func (f *File) Cleanup() { - FsReqCleanup(f.Req) -} - -func NewFile(loop *Loop, req *Fs) *File { - return &File{ - Loop: loop, - Req: req, - } -} - -// Open opens a file specified by the path with given flags and mode, and returns a file descriptor. -func (f *File) Open(path *c.Char, flags int, mode int, cb FsCb) int { - return FsOpen(f.Loop, f.Req, path, flags, mode, cb) -} - -// Close closes a file descriptor. -func (f *File) Close(file int, cb FsCb) int { - return FsClose(f.Loop, f.Req, file, cb) -} - -// Read reads data from a file descriptor into a buffer at a specified offset. -func (f *File) Read(file c.Int, bufs []Buf, nbufs c.Uint, offset c.Int, cb FsCb) int { - return FsRead(f.Loop, f.Req, file, bufs, nbufs, offset, cb) -} - -// Write writes data to a file descriptor from a buffer at a specified offset. -func (f *File) Write(file c.Int, bufs []Buf, nbufs c.Uint, offset c.Int, cb FsCb) int { - return FsWrite(f.Loop, f.Req, file, bufs, nbufs, offset, cb) -} - -// Unlink deletes a file specified by the path. -func (f *File) Unlink(path *c.Char, cb FsCb) int { - return FsUnlink(f.Loop, f.Req, path, cb) -} - -// Mkdir creates a new directory at the specified path with a specified mode. -func (f *File) Mkdir(path *c.Char, mode int, cb FsCb) int { - return FsMkdir(f.Loop, f.Req, path, mode, cb) -} - -// Mkdtemp creates a temporary directory with a template path. -func (f *File) Mkdtemp(tpl *c.Char, cb FsCb) int { - return FsMkdtemp(f.Loop, f.Req, tpl, cb) -} - -// MkStemp creates a temporary file from a template path. -func (f *File) MkStemp(tpl *c.Char, cb FsCb) int { - return FsMkStemp(f.Loop, f.Req, tpl, cb) -} - -// Rmdir removes a directory specified by the path. -func (f *File) Rmdir(path *c.Char, cb FsCb) int { - return FsRmdir(f.Loop, f.Req, path, cb) -} - -// Stat retrieves status information about the file specified by the path. -func (f *File) Stat(path *c.Char, cb FsCb) int { - return FsStat(f.Loop, f.Req, path, cb) -} - -// Fstat retrieves status information about a file descriptor. -func (f *File) Fstat(file int, cb FsCb) int { - return FsFstat(f.Loop, f.Req, file, cb) -} - -// Rename renames a file from the old path to the new path. -func (f *File) Rename(path *c.Char, newPath *c.Char, cb FsCb) int { - return FsRename(f.Loop, f.Req, path, newPath, cb) -} - -// Fsync synchronizes a file descriptor's state with storage device. -func (f *File) Fsync(file int, cb FsCb) int { - return FsFsync(f.Loop, f.Req, file, cb) -} - -// Fdatasync synchronizes a file descriptor's data with storage device. -func (f *File) Fdatasync(file int, cb FsCb) int { - return FsFdatasync(f.Loop, f.Req, file, cb) -} - -// Ftruncate truncates a file to a specified length. -func (f *File) Ftruncate(file int, offset int, cb FsCb) int { - return FsFtruncate(f.Loop, f.Req, file, offset, cb) -} - -// Sendfile sends data from one file descriptor to another. -func (f *File) Sendfile(outFd int, inFd int, inOffset int, length int, cb FsCb) int { - return FsSendfile(f.Loop, f.Req, outFd, inFd, inOffset, length, cb) -} - -// Access checks the access permissions of a file specified by the path. -func (f *File) Access(path *c.Char, flags int, cb FsCb) int { - return FsAccess(f.Loop, f.Req, path, flags, cb) -} - -// Chmod changes the permissions of a file specified by the path. -func (f *File) Chmod(path *c.Char, mode int, cb FsCb) int { - return FsChmod(f.Loop, f.Req, path, mode, cb) -} - -// Fchmod changes the permissions of a file descriptor. -func (f *File) Fchmod(file int, mode int, cb FsCb) int { - return FsFchmod(f.Loop, f.Req, file, mode, cb) -} - -// Utime updates the access and modification times of a file specified by the path. -func (f *File) Utime(path *c.Char, atime int, mtime int, cb FsCb) int { - return FsUtime(f.Loop, f.Req, path, atime, mtime, cb) -} - -// Futime updates the access and modification times of a file descriptor. -func (f *File) Futime(file int, atime int, mtime int, cb FsCb) int { - return FsFutime(f.Loop, f.Req, file, atime, mtime, cb) -} - -// Lutime updates the access and modification times of a file specified by the path, even if the path is a symbolic link. -func (f *File) Lutime(path *c.Char, atime int, mtime int, cb FsCb) int { - return FsLutime(f.Loop, f.Req, path, atime, mtime, cb) -} - -// Link creates a new link to an existing file. -func (f *File) Link(path *c.Char, newPath *c.Char, cb FsCb) int { - return FsLink(f.Loop, f.Req, path, newPath, cb) -} - -// Symlink creates a symbolic link from the path to the new path. -func (f *File) Symlink(path *c.Char, newPath *c.Char, flags int, cb FsCb) int { - return FsSymlink(f.Loop, f.Req, path, newPath, flags, cb) -} - -// Readlink reads the target of a symbolic link. -func (f *File) Readlink(path *c.Char, cb FsCb) int { - return FsReadlink(f.Loop, f.Req, path, cb) -} - -// Realpath resolves the absolute path of a file. -func (f *File) Realpath(path *c.Char, cb FsCb) int { - return FsRealpath(f.Loop, f.Req, path, cb) -} - -// Copyfile copies a file from the source path to the destination path. -func (f *File) Copyfile(path *c.Char, newPath *c.Char, flags int, cb FsCb) int { - return FsCopyfile(f.Loop, f.Req, path, newPath, flags, cb) -} - -// Scandir scans a directory for entries. -func (f *File) Scandir(path *c.Char, flags int, cb FsCb) int { - return FsScandir(f.Loop, f.Req, path, flags, cb) -} - -// OpenDir opens a directory specified by the path. -func (f *File) OpenDir(path *c.Char, cb FsCb) int { - return FsOpenDir(f.Loop, f.Req, path, cb) -} - -// Readdir reads entries from an open directory. -func (f *File) Readdir(dir int, cb FsCb) int { - return FsReaddir(f.Loop, f.Req, dir, cb) -} - -// CloseDir closes an open directory. -func (f *File) CloseDir() int { - return FsCloseDir(f.Loop, f.Req) -} - -// Statfs retrieves file system status information. -func (f *File) Statfs(path *c.Char, cb FsCb) int { - return FsStatfs(f.Loop, f.Req, path, cb) -} - -// Chown Change file ownership -func (f *File) Chown(path *c.Char, uid int, gid int, cb FsCb) int { - return FsChown(f.Loop, f.Req, path, uid, gid, cb) -} - -// Fchown Change file ownership by file descriptor -func (f *File) Fchown(file int, uid int, gid int, cb FsCb) int { - return FsFchown(f.Loop, f.Req, file, uid, gid, cb) -} - -// Lchown Change file ownership (symlink) -func (f *File) Lchown(path *c.Char, uid int, gid int, cb FsCb) int { - return FsLchown(f.Loop, f.Req, path, uid, gid, cb) -} - -// Lstat Get file status (symlink) -func (f *File) Lstat(path *c.Char, cb FsCb) int { - return FsLstat(f.Loop, f.Req, path, cb) -} - -// Init Initialize a file event handle -func (e *FsEvent) Init(loop *Loop) int { - return FsEventInit(loop, e) -} - -// Start listening for file events -func (e *FsEvent) Start(cb FsEventCb, path *c.Char, flags int) int { - return FsEventStart(e, cb, path, flags) -} - -// Stop listening for file events -func (e *FsEvent) Stop() int { - return FsEventStop(e) -} - -// Close the file event handle -func (e *FsEvent) Close() int { - return FsEventClose(e) -} - -// GetPath Get the path of the file event -func (e *FsEvent) GetPath() *c.Char { - return FsEventGetpath(e) -} - -// Init Initialize a file poll handle -func (p *FsPoll) Init(loop *Loop) int { - return FsPollInit(loop, p) -} - -// Start polling for file changes -func (p *FsPoll) Start(cb FsPollCb, path *c.Char, interval uint) int { - return FsPollStart(p, cb, path, interval) -} - -// Stop polling for file changes -func (p *FsPoll) Stop() int { - return FsPollStop(p) -} - -// Close the file poll handle -func (p *FsPoll) Close() int { - return FsPollClose(p) -} - -// GetPath Get the path of the file poll -func (p *FsPoll) GetPath() *c.Char { - return FsPollGetPath(p) -} diff --git a/c/libuv/libuv.go b/c/libuv/libuv.go index fb0c7664..11a36700 100644 --- a/c/libuv/libuv.go +++ b/c/libuv/libuv.go @@ -11,6 +11,11 @@ const ( ) // ---------------------------------------------- +const ( + RUN_DEFAULT RunMode = iota + RUN_ONCE + RUN_NOWAIT +) const ( LOOP_BLOCK_SIGNAL LoopOption = iota @@ -60,6 +65,15 @@ const ( REQ_TYPE_MAX ) +const ( + READABLE PollEvent = 1 << iota + WRITABLE + DISCONNECT + PRIPRIORITIZED +) + +type RunMode c.Int + type LoopOption c.Int type Membership c.Int @@ -68,26 +82,30 @@ type HandleType c.Int type ReqType c.Int -type Uv_File c.Int - type OsSock c.Int type OsFd c.Int +type PollEvent c.Int + // ---------------------------------------------- /* Handle types. */ -type Handle struct { +type Loop struct { Unused [0]byte } +type Handle struct { + Unused [96]byte +} + type Dir struct { Unused [0]byte } type Stream struct { - Unused [0]byte + Unused [264]byte } type Pipe struct { @@ -122,18 +140,6 @@ type Process struct { Unused [0]byte } -type FsEvent struct { - Unused [0]byte -} - -type FsPoll struct { - Unused [0]byte -} - -type Signal struct { - Unused [0]byte -} - /* Request types. */ type Req struct { @@ -153,14 +159,17 @@ type Shutdown struct { } type Write struct { - Unused [0]byte + Unused [192]byte } type Connect struct { Unused [0]byte } -// ---------------------------------------------- +type Buf struct { + Base *c.Char + Len uintptr +} // ---------------------------------------------- /* Function type */ @@ -200,6 +209,9 @@ type ShutdownCb func(req *Shutdown, status c.Int) // llgo:type C type WalkCb func(handle *Handle, arg c.Pointer) +// llgo:type C +type PollCb func(handle *Poll, status c.Int, events c.Int) + // ---------------------------------------------- //go:linkname Version C.uv_version @@ -284,7 +296,7 @@ func (handle *Handle) Fileno(fd *OsFd) c.Int { } //go:linkname UvPipe C.uv_pipe -func UvPipe(fds [2]Uv_File, readFlags c.Int, writeFlags c.Int) c.Int { +func UvPipe(fds [2]UvFile, readFlags c.Int, writeFlags c.Int) c.Int { return 0 } @@ -351,22 +363,22 @@ func (stream *Stream) StopRead() c.Int { } // llgo:link (*Write).Write C.uv_write -func (req *Write) Write(stream *Stream, bufs []Buf, nbufs c.Uint, writeCb WriteCb) c.Int { +func (req *Write) Write(stream *Stream, bufs *Buf, nbufs c.Uint, writeCb WriteCb) c.Int { return 0 } // llgo:link (*Write).Write2 C.uv_write2 -func (req *Write) Write2(stream *Stream, bufs []Buf, nbufs c.Uint, sendStream *Stream, writeCb WriteCb) c.Int { +func (req *Write) Write2(stream *Stream, bufs *Buf, nbufs c.Uint, sendStream *Stream, writeCb WriteCb) c.Int { return 0 } // llgo:link (*Stream).TryWrite C.uv_try_write -func (stream *Stream) TryWrite(bufs []Buf, nbufs c.Uint) c.Int { +func (stream *Stream) TryWrite(bufs *Buf, nbufs c.Uint) c.Int { return 0 } // llgo:link (*Stream).TryWrite2 C.uv_try_write2 -func (stream *Stream) TryWrite2(bufs []Buf, nbufs c.Uint, sendStream *Stream) c.Int { +func (stream *Stream) TryWrite2(bufs *Buf, nbufs c.Uint, sendStream *Stream) c.Int { return 0 } @@ -387,6 +399,78 @@ func (stream *Stream) SetBlocking(blocking c.Int) c.Int { // ---------------------------------------------- +/* Loop related functions and method. */ + +//go:linkname LoopSize C.uv_loop_size +func LoopSize() uintptr + +//go:linkname Run C.uv_run +func Run(loop *Loop, mode RunMode) c.Int + +//go:linkname LoopAlive C.uv_loop_alive +func LoopAlive(loop *Loop) c.Int + +//go:linkname LoopClose C.uv_loop_close +func LoopClose(loop *Loop) c.Int + +//go:linkname LoopConfigure C.uv_loop_configure +func LoopConfigure(loop *Loop, option LoopOption, arg c.Int) c.Int + +//go:linkname LoopDefault C.uv_default_loop +func LoopDefault() *Loop + +//go:linkname LoopDelete C.uv_loop_delete +func LoopDelete(loop *Loop) c.Int + +//go:linkname LoopFork C.uv_loop_fork +func LoopFork(loop *Loop) c.Int + +//go:linkname LoopInit C.uv_loop_init +func LoopInit(loop *Loop) c.Int + +//go:linkname LoopNew C.uv_loop_new +func LoopNew() *Loop + +//go:linkname LoopNow C.uv_now +func LoopNow(loop *Loop) c.UlongLong + +//go:linkname LoopUpdateTime C.uv_update_time +func LoopUpdateTime(loop *Loop) + +//go:linkname LoopBackendFd C.uv_backend_fd +func LoopBackendFd(loop *Loop) c.Int + +//go:linkname LoopBackendTimeout C.uv_backend_timeout +func LoopBackendTimeout(loop *Loop) c.Int + +//go:linkname LoopWalk C.uv_walk +func LoopWalk(loop *Loop, walkCb WalkCb, arg c.Pointer) + +// ---------------------------------------------- + +/* Buf related functions and method. */ + +//go:linkname InitBuf C.uv_buf_init +func InitBuf(base *c.Char, len c.Uint) Buf + +// ---------------------------------------------- + +/* Poll related function and method */ + +//go:linkname PollInit C.uv_poll_init +func PollInit(loop *Loop, handle *Poll, fd OsFd) c.Int + +//go:linkname PollStart C.uv_poll_start +func PollStart(handle *Poll, events c.Int, cb PollCb) c.Int + +//go:linkname PollStop C.uv_poll_stop +func PollStop(handle *Poll) c.Int + +//go:linkname PollInitSocket C.uv_poll_init_socket +func PollInitSocket(loop *Loop, handle *Poll, socket c.Int) c.Int + +// ---------------------------------------------- + /* Getaddrinfo related function and method */ //go:linkname Getaddrinfo C.uv_getaddrinfo diff --git a/c/libuv/net.go b/c/libuv/net.go index e2f824f6..3fbb8f96 100644 --- a/c/libuv/net.go +++ b/c/libuv/net.go @@ -65,7 +65,7 @@ type UdpFlags c.Int /* Handle types. */ type Tcp struct { - Unused [0]byte + Unused [264]byte } type Udp struct { @@ -207,8 +207,8 @@ func (udp *Udp) SetMulticastInterface(interfaceAddr *c.Char) c.Int { return 0 } -// llgo:link (*Udp).SAetBroadcast C.uv_udp_set_broadcast -func (udp *Udp) SAetBroadcast(on c.Int) c.Int { +// llgo:link (*Udp).SetBroadcast C.uv_udp_set_broadcast +func (udp *Udp) SetBroadcast(on c.Int) c.Int { return 0 } @@ -218,15 +218,15 @@ func (udp *Udp) SetTTL(ttl c.Int) c.Int { } //go:linkname Send C.uv_udp_send -func Send(req *UdpSend, udp *Udp, bufs []Buf, nbufs c.Uint, addr *net.SockAddr, sendCb UdpSendCb) c.Int +func Send(req *UdpSend, udp *Udp, bufs *Buf, nbufs c.Uint, addr *net.SockAddr, sendCb UdpSendCb) c.Int // llgo:link (*Udp).TrySend C.uv_udp_try_send -func (udp *Udp) TrySend(bufs []Buf, nbufs c.Uint, addr *net.SockAddr) c.Int { +func (udp *Udp) TrySend(bufs *Buf, nbufs c.Uint, addr *net.SockAddr) c.Int { return 0 } -// llgo:link (*Udp).RecvStart C.uv_udp_recv_start -func (udp *Udp) RecvStart(allocCb AllocCb, recvCb UdpRecvCb) c.Int { +// llgo:link (*Udp).StartRecv C.uv_udp_recv_start +func (udp *Udp) StartRecv(allocCb AllocCb, recvCb UdpRecvCb) c.Int { return 0 } @@ -235,8 +235,8 @@ func (udp *Udp) UsingRecvmmsg() c.Int { return 0 } -// llgo:link (*Udp).RecvStop C.uv_udp_recv_stop -func (udp *Udp) RecvStop() c.Int { +// llgo:link (*Udp).StopRecv C.uv_udp_recv_stop +func (udp *Udp) StopRecv() c.Int { return 0 } diff --git a/c/libuv/signal.go b/c/libuv/signal.go index 97847d1c..8aad3b0e 100644 --- a/c/libuv/signal.go +++ b/c/libuv/signal.go @@ -13,24 +13,20 @@ type Signal struct { // ---------------------------------------------- -/* Signal related functions and method. */ +/* Function type */ // llgo:type C type SignalCb func(handle *Signal, sigNum c.Int) -//go:linkname UvSignalInit C.uv_signal_init -func UvSignalInit(loop *Loop, handle *Signal) c.Int +// ---------------------------------------------- -// llgo:link (*Signal).Start C.uv_signal_start -func (s *Signal) Start(cb SignalCb, signum c.Int) c.Int { - return 0 -} +/* Signal related functions and method. */ -// llgo:link (*Signal).StartOneshot C.uv_signal_start_oneshot -func (s *Signal) StartOneshot(handle *Signal, cb SignalCb, signum c.Int) c.Int { - return 0 -} +//go:linkname SignalInit C.uv_signal_init +func SignalInit(loop *Loop, handle *Signal) c.Int -func (s *Signal) Init(loop *Loop) int { - return UvSignalInit(loop, s) -} +//go:linkname SignalStart C.uv_signal_start +func SignalStart(handle *Signal, cb SignalCb, signum c.Int) c.Int + +//go:linkname SignalStartOneshot C.uv_signal_start_oneshot +func SignalStartOneshot(handle *Signal, cb SignalCb, signum c.Int) c.Int diff --git a/c/libuv/timer.go b/c/libuv/timer.go index fff1af5d..feeabe5a 100644 --- a/c/libuv/timer.go +++ b/c/libuv/timer.go @@ -15,7 +15,7 @@ type Timer struct { // ---------------------------------------------- -// llgo:type C +// llgo:type Cgit type TimerCb func(timer *Timer) // ---------------------------------------------- From 9741574516da1ecd0454451c98734dcc140b29de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E8=8B=B1=E6=9D=B0?= <2635879218@qq.com> Date: Thu, 25 Jul 2024 12:03:01 +0800 Subject: [PATCH 011/152] llgo/rust/readme: Some modifications --- doc/Rust-to-LLGO-Migration-Guide.md | 44 +++++++++++++++-------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/doc/Rust-to-LLGO-Migration-Guide.md b/doc/Rust-to-LLGO-Migration-Guide.md index 9f3c5940..c6b3a3c8 100644 --- a/doc/Rust-to-LLGO-Migration-Guide.md +++ b/doc/Rust-to-LLGO-Migration-Guide.md @@ -1,6 +1,6 @@ # Rust to LLGO Migration Document -### Add Dependencies & Build Configuration +## Add Dependencies & Build Configuration Edit `Cargo.toml` to include necessary dependencies and configuration: @@ -16,6 +16,8 @@ crate-type = ["cdylib"] # The generated dynamic library will conform to the C st cbindgen = "0.26.0" ``` +## C-style wrapper for Rust + ### Import C Language Types Use types from the libc package for interoperability with C: @@ -59,12 +61,12 @@ After packaging: ```rust pub unsafe extern "C" fn sled_create_config() -> \*mut Config { -Box::into_raw(Box::new(Config::new())) + Box::into_raw(Box::new(Config::new())) } #[no_mangle] pub unsafe extern "C" fn sled_free_config(config: \*mut Config) { -drop(Box::from_raw(config)); + drop(Box::from_raw(config)); } ``` @@ -72,7 +74,7 @@ drop(Box::from_raw(config)); Address the interfacing issues between generic pointers in C and Rust: -```Rust +```rust let mut reader = ReaderBuilder::new().from_path(file_path)?; ``` @@ -154,7 +156,7 @@ pub extern "C" fn free_string(s: *mut c_char) { } ``` -### Generate Header File +## Generate Header File Edit `cbindgen.toml` to configure the header file generation rules: @@ -173,9 +175,9 @@ fn main() { } ``` -### Compilation and Installation +## Compilation and Installation -Build the dynamic library: +### Build the dynamic library: ```sh cargo build --release @@ -185,14 +187,14 @@ cargo build --release Install the [dylib-installer](https://github.com/hackerchai/dylib-installer) tool, which is used to install dynamic libraries: -```SH +```sh brew tap hackerchai/tap brew install dylib-installer ``` Or you can install it using Cargo: -```SH +```sh cargo install dylib_installer ``` @@ -200,7 +202,7 @@ cargo install dylib_installer Use dylib-installer to install the built dynamic library and the header file into the system directory: -```SH +```sh sudo dylib_installer ``` @@ -208,17 +210,17 @@ sudo dylib_installer You can check the installation by running the following command: -```SH +```sh pkg-config --libs --cflags ``` -if everything is installed correctly, you will see the output like this (depending on your system): +If everything is installed correctly, you will see the output like this (depending on your system): -```SH +```sh -I/usr/local/include -L/usr/local/lib -l ``` -### LLGO Package Mapping +## LLGO Mapping Map functions from the Rust library to an LLGO package, ensuring type consistency: @@ -249,7 +251,7 @@ type Reader struct { If we want to calculate the size of this structure, we can use the following C code: -```C +```c printf("%d\n", sizeof(csv_reader)); ``` @@ -257,7 +259,7 @@ printf("%d\n", sizeof(csv_reader)); Ordinary functions can be mapped in the form of `//go:linkname`. -```C +```c csv_reader *csv_reader_new(const char *file_path); ``` @@ -272,7 +274,7 @@ func NewReader(file_path *c.Char) *Reader Methods need to be mapped in the form of `// llgo:link (*Receiver)`. -```C +```c void csv_reader_free(csv_reader *reader); const char *csv_reader_read_record(csv_reader *reader); @@ -292,7 +294,7 @@ func (reader *Reader) ReadRecord() *c.Char { return nil } - Function pointer -If you use a function pointer, that is, declare the function as a type separately, you need to use `// llgo:type C`to declare it. +If you use a function pointer, that is, declare the function as a type separately, you need to use `// llgo:type C` to declare it. ```c typedef size_t (*hyper_io_read_callback)(void*, struct hyper_context*, uint8_t*, size_t); @@ -317,13 +319,13 @@ Or declare the function directly in the parameter. func (io *Io) SetRead(ioSetReadCb func(c.Pointer, *Context, *uint8, uintptr) uintptr) {} ``` -### Writing Examples and README +## Writing Examples and README Finally, provide example code and a detailed README file to help users understand how to use the generated library. -### Example Code +## Example Code -You can find the migrated instance from [llgoexamples](https://github.com/goplus/llgoexamples), in the lib directory is the migrated Rust library, and in the rust directory, the migrated mapping file and go demo. +You can find the migrated examples in the [llgoexamples](https://github.com/goplus/llgoexamples). The migrated Rust libraries are in the `lib` directory, and the migrated mapping files and Go demos are in the `rust` directory. Such as: From 65e1f261c0fb47373cb419c60df373d60d351b49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E8=8B=B1=E6=9D=B0?= <2635879218@qq.com> Date: Thu, 25 Jul 2024 15:40:02 +0800 Subject: [PATCH 012/152] refactor(c-libuv): Ajust Errno --- c/libuv/_demo/async_fs/async_fs.go | 10 +- c/libuv/_demo/echo_server/echo_server.go | 8 +- c/libuv/error.go | 181 ++++++++++++----------- c/net/net.go | 17 +++ 4 files changed, 117 insertions(+), 99 deletions(-) diff --git a/c/libuv/_demo/async_fs/async_fs.go b/c/libuv/_demo/async_fs/async_fs.go index 6a549441..6f715974 100644 --- a/c/libuv/_demo/async_fs/async_fs.go +++ b/c/libuv/_demo/async_fs/async_fs.go @@ -40,7 +40,7 @@ func main() { func onOpen(req *libuv.Fs) { // Check for errors if libuv.FsGetResult(req) < 0 { - c.Fprintf(c.Stderr, c.Str("Error opening file: %s\n"), libuv.Strerror(libuv.LoopClose(loop))) + c.Fprintf(c.Stderr, c.Str("Error opening file: %s\n"), libuv.Strerror(libuv.Errno(libuv.LoopClose(loop)))) libuv.LoopClose(loop) return } @@ -49,7 +49,7 @@ func onOpen(req *libuv.Fs) { // Read the file readRes := libuv.FsRead(loop, &readReq, libuv.UvFile(libuv.FsGetResult(req)), &iov, 1, -1, onRead) if readRes != 0 { - c.Printf(c.Str("Error in FsRead: %s (code: %d)\n"), libuv.Strerror(c.Int(readRes)), readRes) + c.Printf(c.Str("Error in FsRead: %s (code: %d)\n"), libuv.Strerror(libuv.Errno(readRes)), readRes) libuv.LoopClose(loop) return } @@ -58,7 +58,7 @@ func onOpen(req *libuv.Fs) { func onRead(req *libuv.Fs) { // Check for errors if libuv.FsGetResult(req) < 0 { - c.Fprintf(c.Stderr, c.Str("Read error: %s\n"), libuv.Strerror(libuv.FsGetResult(req))) + c.Fprintf(c.Stderr, c.Str("Read error: %s\n"), libuv.Strerror(libuv.Errno(libuv.FsGetResult(req)))) libuv.LoopClose(loop) } else if libuv.FsGetResult(req) == 0 { c.Printf(c.Str("EOF\n")) @@ -66,7 +66,7 @@ func onRead(req *libuv.Fs) { closeRes := libuv.FsClose(loop, &closeReq, libuv.UvFile(libuv.FsGetResult(&openReq)), onClose) if closeRes != 0 { // Print the content - c.Printf(c.Str("Error in FsClose: %s (code: %d)\n"), libuv.Strerror(c.Int(closeRes)), closeRes) + c.Printf(c.Str("Error in FsClose: %s (code: %d)\n"), libuv.Strerror(libuv.Errno(closeRes)), closeRes) libuv.LoopClose(loop) return } @@ -80,7 +80,7 @@ func onRead(req *libuv.Fs) { func onClose(req *libuv.Fs) { // Check for errors if libuv.FsGetResult(req) < 0 { - c.Fprintf(c.Stderr, c.Str("Error closing file: %s\n"), libuv.Strerror(libuv.FsGetResult(req))) + c.Fprintf(c.Stderr, c.Str("Error closing file: %s\n"), libuv.Strerror(libuv.Errno(libuv.FsGetResult(req)))) } else { c.Printf(c.Str("\nFile closed successfully.\n")) } diff --git a/c/libuv/_demo/echo_server/echo_server.go b/c/libuv/_demo/echo_server/echo_server.go index fdb04b5b..ee7faafc 100644 --- a/c/libuv/_demo/echo_server/echo_server.go +++ b/c/libuv/_demo/echo_server/echo_server.go @@ -35,7 +35,7 @@ func main() { (&server).Bind((*net.SockAddr)(c.Pointer(&addr)), 0) res := (*libuv.Stream)(&server).Listen(DEFAULT_BACKLOG, OnNewConnection) if res != 0 { - c.Fprintf(c.Stderr, c.Str("Listen error: %s\n"), libuv.Strerror(res)) + c.Fprintf(c.Stderr, c.Str("Listen error: %s\n"), libuv.Strerror((libuv.Errno(res)))) return } @@ -58,7 +58,7 @@ func AllocBuffer(handle *libuv.Handle, suggestedSize uintptr, buf *libuv.Buf) { func EchoWrite(req *libuv.Write, status c.Int) { if status != 0 { - c.Fprintf(c.Stderr, c.Str("Write error: %s\n"), libuv.Strerror(status)) + c.Fprintf(c.Stderr, c.Str("Write error: %s\n"), libuv.Strerror((libuv.Errno(status)))) } FreeWriteReq(req) } @@ -80,7 +80,7 @@ func EchoRead(client *libuv.Stream, nread c.Long, buf *libuv.Buf) { if nread < 0 { // Handle read errors and EOF. if (libuv.Errno)(nread) != libuv.EOF { - c.Fprintf(c.Stderr, c.Str("Read error: %s\n"), libuv.Strerror(c.Int(nread))) + c.Fprintf(c.Stderr, c.Str("Read error: %s\n"), libuv.Strerror((libuv.Errno)(nread))) } (*libuv.Handle)(c.Pointer(client)).Close(nil) } @@ -92,7 +92,7 @@ func EchoRead(client *libuv.Stream, nread c.Long, buf *libuv.Buf) { func OnNewConnection(server *libuv.Stream, status c.Int) { if status < 0 { - c.Fprintf(c.Stderr, c.Str("New connection error: %s\n"), libuv.Strerror(status)) + c.Fprintf(c.Stderr, c.Str("New connection error: %s\n"), libuv.Strerror(libuv.Errno(status))) return } diff --git a/c/libuv/error.go b/c/libuv/error.go index b9397cf9..beb286a2 100644 --- a/c/libuv/error.go +++ b/c/libuv/error.go @@ -2,110 +2,111 @@ package libuv import ( "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/net" + "github.com/goplus/llgo/c/syscall" _ "unsafe" ) const ( - E2BIG Errno = iota - EACCES - EADDRINUSE - EADDRNOTAVAIL - EAFNOSUPPORT - EAGAIN - EAI_ADDRFAMILY - EAI_AGAIN - EAI_BADFLAGS - EAI_BADHINTS - EAI_CANCELED - EAI_FAIL - EAI_FAMILY - EAI_MEMORY - EAI_NODATA - EAI_NONAME - EAI_OVERFLOW - EAI_PROTOCOL - EAI_SERVICE - EAI_SOCKTYPE - EALREADY - EBADF - EBUSY - ECANCELED - ECHARSET - ECONNABORTED - ECONNREFUSED - ECONNRESET - EDESTADDRREQ - EEXIST - EFAULT - EFBIG - EHOSTUNREACH - EINTR - EINVAL - EIO - EISCONN - EISDIR - ELOOP - EMFILE - EMSGSIZE - ENAMETOOLONG - ENETDOWN - ENETUNREACH - ENFILE - ENOBUFS - ENODEV - ENOENT - ENOMEM - ENONET - ENOPROTOOPT - ENOSPC - ENOSYS - ENOTCONN - ENOTDIR - ENOTEMPTY - ENOTSOCK - ENOTSUP - EOVERFLOW - EPERM - EPIPE - EPROTO - EPROTONOSUPPORT - EPROTOTYPE - ERANGE - EROFS - ESHUTDOWN - ESPIPE - ESRCH - ETIMEDOUT - ETXTBSY - EXDEV - UNKNOWN - EOF - ENXIO - EMLINK - EHOSTDOWN - EREMOTEIO - ENOTTY - EFTYPE - EILSEQ - ESOCKTNOSUPPORT - ENODATA - EUNATCH - ERRNO_MAX + E2BIG Errno = (Errno)(syscall.E2BIG) + EACCES Errno = (Errno)(syscall.EACCES) + EADDRINUSE Errno = (Errno)(syscall.EADDRINUSE) + EADDRNOTAVAIL Errno = (Errno)(syscall.EADDRNOTAVAIL) + EAFNOSUPPORT Errno = (Errno)(syscall.EAFNOSUPPORT) + EAGAIN Errno = (Errno)(syscall.EAGAIN) + EAI_ADDRFAMILY Errno = net.EAI_ADDRFAMILY + EAI_AGAIN Errno = net.EAI_AGAIN + EAI_BADFLAGS Errno = net.EAI_BADFLAGS + EAI_BADHINTS Errno = net.EAI_BADHINTS + EAI_CANCELED Errno = -3003 + EAI_FAIL Errno = net.EAI_FAIL + EAI_FAMILY Errno = net.EAI_FAMILY + EAI_MEMORY Errno = net.EAI_MEMORY + EAI_NODATA Errno = net.EAI_NODATA + EAI_NONAME Errno = net.EAI_NONAME + EAI_OVERFLOW Errno = net.EAI_OVERFLOW + EAI_PROTOCOL Errno = net.EAI_PROTOCOL + EAI_SERVICE Errno = net.EAI_SERVICE + EAI_SOCKTYPE Errno = net.EAI_SOCKTYPE + EALREADY Errno = (Errno)(syscall.EALREADY) + EBADF Errno = (Errno)(syscall.EBADF) + EBUSY Errno = (Errno)(syscall.EBUSY) + ECANCELED Errno = (Errno)(syscall.ECANCELED) + ECHARSET Errno = -4080 + ECONNABORTED Errno = (Errno)(syscall.ECONNABORTED) + ECONNREFUSED Errno = (Errno)(syscall.ECONNREFUSED) + ECONNRESET Errno = (Errno)(syscall.ECONNRESET) + EDESTADDRREQ Errno = (Errno)(syscall.EDESTADDRREQ) + EEXIST Errno = (Errno)(syscall.EEXIST) + EFAULT Errno = (Errno)(syscall.EFAULT) + EFBIG Errno = (Errno)(syscall.EFBIG) + EHOSTUNREACH Errno = (Errno)(syscall.EHOSTUNREACH) + EINTR Errno = (Errno)(syscall.EINTR) + EINVAL Errno = (Errno)(syscall.EINVAL) + EIO Errno = (Errno)(syscall.EIO) + EISCONN Errno = (Errno)(syscall.EISCONN) + EISDIR Errno = (Errno)(syscall.EISDIR) + ELOOP Errno = (Errno)(syscall.ELOOP) + EMFILE Errno = (Errno)(syscall.EMFILE) + EMSGSIZE Errno = (Errno)(syscall.EMSGSIZE) + ENAMETOOLONG Errno = (Errno)(syscall.ENAMETOOLONG) + ENETDOWN Errno = (Errno)(syscall.ENETDOWN) + ENETUNREACH Errno = (Errno)(syscall.ENETUNREACH) + ENFILE Errno = (Errno)(syscall.ENFILE) + ENOBUFS Errno = (Errno)(syscall.ENOBUFS) + ENODEV Errno = (Errno)(syscall.ENODEV) + ENOENT Errno = (Errno)(syscall.ENOENT) + ENOMEM Errno = (Errno)(syscall.ENOMEM) + ENONET Errno = -4056 + ENOPROTOOPT Errno = (Errno)(syscall.ENOPROTOOPT) + ENOSPC Errno = (Errno)(syscall.ENOSPC) + ENOSYS Errno = (Errno)(syscall.ENOSYS) + ENOTCONN Errno = (Errno)(syscall.ENOTCONN) + ENOTDIR Errno = (Errno)(syscall.ENOTDIR) + ENOTEMPTY Errno = (Errno)(syscall.ENOTEMPTY) + ENOTSOCK Errno = (Errno)(syscall.ENOTSOCK) + ENOTSUP Errno = (Errno)(syscall.ENOTSUP) + EOVERFLOW Errno = (Errno)(syscall.EOVERFLOW) + EPERM Errno = (Errno)(syscall.EPERM) + EPIPE Errno = (Errno)(syscall.EPIPE) + EPROTO Errno = (Errno)(syscall.EPROTO) + EPROTONOSUPPORT Errno = (Errno)(syscall.EPROTONOSUPPORT) + EPROTOTYPE Errno = (Errno)(syscall.EPROTOTYPE) + ERANGE Errno = (Errno)(syscall.ERANGE) + EROFS Errno = (Errno)(syscall.EROFS) + ESHUTDOWN Errno = (Errno)(syscall.ESHUTDOWN) + ESPIPE Errno = (Errno)(syscall.ESPIPE) + ESRCH Errno = (Errno)(syscall.ESRCH) + ETIMEDOUT Errno = (Errno)(syscall.ETIMEDOUT) + ETXTBSY Errno = (Errno)(syscall.ETXTBSY) + EXDEV Errno = (Errno)(syscall.EXDEV) + UNKNOWN Errno = -4094 + EOF Errno = -1 + ENXIO Errno = (Errno)(syscall.ENXIO) + EMLINK Errno = (Errno)(syscall.EMLINK) + EHOSTDOWN Errno = (Errno)(syscall.EHOSTDOWN) + EREMOTEIO Errno = -4030 + ENOTTY Errno = (Errno)(syscall.ENOTTY) + EFTYPE Errno = (Errno)(syscall.EFTYPE) + EILSEQ Errno = (Errno)(syscall.EILSEQ) + ESOCKTNOSUPPORT Errno = (Errno)(syscall.ESOCKTNOSUPPORT) + + ERRNO_MAX = EOF - 1 ) type Errno c.Int //go:linkname TranslateSysError C.uv_translate_sys_error -func TranslateSysError(sysErrno c.Int) c.Int +func TranslateSysError(sysErrno c.Int) Errno //go:linkname Strerror C.uv_strerror -func Strerror(err c.Int) *c.Char +func Strerror(err Errno) *c.Char //go:linkname StrerrorR C.uv_strerror_r -func StrerrorR(err c.Int, buf *c.Char, bufLen uintptr) *c.Char +func StrerrorR(err Errno, buf *c.Char, bufLen uintptr) *c.Char //go:linkname ErrName C.uv_err_name -func ErrName(err c.Int) *c.Char +func ErrName(err Errno) *c.Char //go:linkname ErrNameR C.uv_err_name_r -func ErrNameR(err c.Int, buf *c.Char, bufLen uintptr) *c.Char +func ErrNameR(err Errno, buf *c.Char, bufLen uintptr) *c.Char diff --git a/c/net/net.go b/c/net/net.go index 0b794084..e811d316 100644 --- a/c/net/net.go +++ b/c/net/net.go @@ -80,6 +80,23 @@ const ( SOCK_SEQPACKET = 5 // sequenced packet stream ) +const ( + EAI_ADDRFAMILY = iota + 1 /* address family for hostname not supported */ + EAI_AGAIN /* temporary failure in name resolution */ + EAI_BADFLAGS /* invalid value for ai_flags */ + EAI_FAIL /* non-recoverable failure in name resolution */ + EAI_FAMILY /* ai_family not supported */ + EAI_MEMORY /* memory allocation failure */ + EAI_NODATA /* no address associated with hostname */ + EAI_NONAME /* hostname nor servname provided, or not known */ + EAI_SERVICE /* servname not supported for ai_socktype */ + EAI_SOCKTYPE /* ai_socktype not supported */ + EAI_SYSTEM /* system error returned in errno */ + EAI_BADHINTS /* invalid value for hints */ + EAI_PROTOCOL /* resolved protocol is unknown */ + EAI_OVERFLOW /* argument buffer overflow */ +) + // (TODO) merge to inet const INET_ADDRSTRLEN = 16 From 429732088644cefaf797edbffa5bd908274148b0 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Thu, 25 Jul 2024 17:23:34 +0800 Subject: [PATCH 013/152] llcppg design --- chore/llcppg/README.md | 5 +++-- chore/llcppg/design.md | 32 ++++++++++++++++++++++++++++---- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/chore/llcppg/README.md b/chore/llcppg/README.md index 43a3c237..971e0a21 100644 --- a/chore/llcppg/README.md +++ b/chore/llcppg/README.md @@ -17,10 +17,11 @@ If `config-file` is not specified, a `llcppg.cfg` file is used in current direct "INIReader.h", "AnotherHeaderFile.h" ], - "libs": "$(pkg-config --libs inireader)" + "libs": "$(pkg-config --libs inireader)", + "trimPrefixes": ["Ini", "INI"] } ``` ## Design -See [Design of llcppg](design.md). +See [llcppg Design](design.md). diff --git a/chore/llcppg/design.md b/chore/llcppg/design.md index 29247d50..5c841675 100644 --- a/chore/llcppg/design.md +++ b/chore/llcppg/design.md @@ -1,4 +1,4 @@ -Design of llcppg +llcppg Design ===== ## Usage @@ -11,13 +11,14 @@ If `config-file` is not specified, a `llcppg.cfg` file is used in current direct ```json { - "name": "inireader", + "name": "inih", "cflags": "$(pkg-config --cflags inireader)", "include": [ "INIReader.h", "AnotherHeaderFile.h" ], - "libs": "$(pkg-config --libs inireader)" + "libs": "$(pkg-config --libs inireader)", + "trimPrefixes": ["Ini", "INI"] } ``` @@ -33,9 +34,10 @@ If `config-file` is not specified, a `llcppg.cfg` file is used in current direct ```sh llcppsymg config-file +llcppsymg - # read config from stdin ``` -It generates symbol tables. Its file format is as follows: +It generates a symbol table file named `llcppg.symb.json`. Its file format is as follows: ```json [ @@ -44,9 +46,31 @@ It generates symbol tables. Its file format is as follows: "c++": "INIReader::INIReader(char const*, unsigned long)", "go": "(*Reader).Init__0" }, + ... ] ``` + +### llcppsigfetch + +```sh +llcppsigfetch config-file +llcppsigfetch - # read config from stdin +``` + +It fetches information of C/C++ symbols and print to stdout. Its format is as follows: + +```json +TODO: see llgo/xtool/clang/ast +``` + +### gogensig + +```sh +gogensig ast-file +gogensig - # read AST from stdin +``` + ## Overall ### Process From baaddd395cff2dd3bd153bc6c874eb82c870c864 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Wed, 24 Jul 2024 17:43:54 +0800 Subject: [PATCH 014/152] c/clang:file location c/clang/demo:filename c/clang:fix undefined filename c/clang:file use uintptr c/clang:change cpp format --- c/clang/_demo/symboldump/symboldump.go | 16 +- c/clang/_demo/symboldump/test.h | 40 --- c/clang/_wrap/cursor.cpp | 69 ++--- c/clang/clang.go | 356 ++++++++++++++----------- 4 files changed, 237 insertions(+), 244 deletions(-) delete mode 100644 c/clang/_demo/symboldump/test.h diff --git a/c/clang/_demo/symboldump/symboldump.go b/c/clang/_demo/symboldump/symboldump.go index 4ec62fc1..a2eb0448 100644 --- a/c/clang/_demo/symboldump/symboldump.go +++ b/c/clang/_demo/symboldump/symboldump.go @@ -29,15 +29,25 @@ func (c *Context) setClassName(name string) { var context = newContext() func print_cursor_info(cursor clang.Cursor) { + loc := cursor.Location() + var file clang.File + var line, column c.Uint + + loc.SpellingLocation(&file, &line, &column, nil) + filename := file.FileName() + + c.Printf(c.Str("%s:%d:%d\n"), filename.CStr(), line, column) + cursorStr := cursor.String() symbol := cursor.Mangling() defer symbol.Dispose() defer cursorStr.Dispose() + defer filename.Dispose() if context.namespaceName != "" && context.className != "" { - fmt.Printf("%s:%s", context.namespaceName, context.className) + fmt.Printf("%s:%s:", context.namespaceName, context.className) } else if context.namespaceName != "" { - fmt.Printf("%s", context.namespaceName) + fmt.Printf("%s:", context.namespaceName) } c.Printf(c.Str("%s\n"), cursorStr.CStr()) @@ -50,7 +60,7 @@ func print_cursor_info(cursor clang.Cursor) { c.Printf(c.Str("Parameters(%d): ( "), cursor.NumArguments()) for i := 0; i < int(cursor.NumArguments()); i++ { - argCurSor := cursor.Argument(uint8(i)) + argCurSor := cursor.Argument(c.Uint(i)) argType := argCurSor.Type().String() argName := argCurSor.String() c.Printf(c.Str("%s %s"), argType.CStr(), argName.CStr()) diff --git a/c/clang/_demo/symboldump/test.h b/c/clang/_demo/symboldump/test.h deleted file mode 100644 index 2c72fd6a..00000000 --- a/c/clang/_demo/symboldump/test.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef ZOO_H -#define ZOO_H - -namespace forest -{ - -class Bear -{ - public: - void roar(); - int eat(int berries, int fish); - void sleep(const char *where); -}; - -} // namespace forest - -namespace ocean -{ - -class Shark -{ - public: - void roar(); - int eat(int fish, int seals); - void sleep(const char *where); -}; - -} // namespace ocean - -class Eagle -{ - public: - void roar(); - int eat(int mice, int fish); - void sleep(const char *where); -}; - -void zookeeper(int money); - -#endif // ZOO_H \ No newline at end of file diff --git a/c/clang/_wrap/cursor.cpp b/c/clang/_wrap/cursor.cpp index 3397d98a..7b2df776 100644 --- a/c/clang/_wrap/cursor.cpp +++ b/c/clang/_wrap/cursor.cpp @@ -3,65 +3,48 @@ typedef enum CXChildVisitResult (*wrap_CXCursorVisitor)(CXCursor *cursor, CXCursor *parent, CXClientData client_data); -typedef struct -{ +typedef struct { CXClientData data; wrap_CXCursorVisitor visitor; } wrap_data; -CXChildVisitResult wrap_visitor(CXCursor cursor, CXCursor parent, CXClientData data) -{ +CXChildVisitResult wrap_visitor(CXCursor cursor, CXCursor parent, CXClientData data) { wrap_data *d = (wrap_data *)(data); return d->visitor(&cursor, &parent, d->data); } -extern "C" -{ +extern "C" { - CXString wrap_clang_getCursorSpelling(CXCursor *cur) - { - return clang_getCursorSpelling(*cur); - } +CXString wrap_clang_getCursorSpelling(CXCursor *cur) { return clang_getCursorSpelling(*cur); } - CXString wrap_clang_Cursor_getMangling(CXCursor *cur) - { - return clang_Cursor_getMangling(*cur); - } +CXString wrap_clang_Cursor_getMangling(CXCursor *cur) { return clang_Cursor_getMangling(*cur); } - int wrap_clang_Cursor_getNumArguments(CXCursor *cur) - { - return clang_Cursor_getNumArguments(*cur); - } +int wrap_clang_Cursor_getNumArguments(CXCursor *cur) { return clang_Cursor_getNumArguments(*cur); } - void wrap_clang_Cursor_getArgument(CXCursor *C, unsigned i, CXCursor *argCur) - { - *argCur = clang_Cursor_getArgument(*C, i); - } +void wrap_clang_Cursor_getArgument(CXCursor *C, unsigned i, CXCursor *argCur) { + *argCur = clang_Cursor_getArgument(*C, i); +} - void wrap_clang_getTranslationUnitCursor(CXTranslationUnit uint, CXCursor *cur) - { - *cur = clang_getTranslationUnitCursor(uint); - } +void wrap_clang_getTranslationUnitCursor(CXTranslationUnit uint, CXCursor *cur) { + *cur = clang_getTranslationUnitCursor(uint); +} - void wrap_clang_getCursorType(CXCursor *cur, CXType *typ) - { - *typ = clang_getCursorType(*cur); - } +void wrap_clang_getCursorType(CXCursor *cur, CXType *typ) { *typ = clang_getCursorType(*cur); } - void wrap_clang_getCursorResultType(CXCursor *cur, CXType *typ) - { - *typ = clang_getCursorResultType(*cur); - } +void wrap_clang_getCursorResultType(CXCursor *cur, CXType *typ) { *typ = clang_getCursorResultType(*cur); } - CXString wrap_clang_getTypeSpelling(CXType *typ) - { - return clang_getTypeSpelling(*typ); - } +CXString wrap_clang_getTypeSpelling(CXType *typ) { return clang_getTypeSpelling(*typ); } - unsigned wrap_clang_visitChildren(CXCursor *parent, wrap_CXCursorVisitor visitor, CXClientData client_data) - { - wrap_data data = {client_data, visitor}; - return clang_visitChildren(*parent, wrap_visitor, CXClientData(&data)); - } +void wrap_clang_getCursorLocation(CXCursor *cur, CXSourceLocation *loc) { *loc = clang_getCursorLocation(*cur); } + +void wrap_clang_getSpellingLocation(CXSourceLocation *loc, CXFile *file, unsigned *line, unsigned *column, + unsigned *offset) { + clang_getSpellingLocation(*loc, file, line, column, offset); +} + +unsigned wrap_clang_visitChildren(CXCursor *parent, wrap_CXCursorVisitor visitor, CXClientData client_data) { + wrap_data data = {client_data, visitor}; + return clang_visitChildren(*parent, wrap_visitor, CXClientData(&data)); +} } // extern "C" diff --git a/c/clang/clang.go b/c/clang/clang.go index 76527baa..9bc123a5 100644 --- a/c/clang/clang.go +++ b/c/clang/clang.go @@ -27,160 +27,6 @@ const ( LLGoPackage = "link: -L$(llvm-config --libdir) -lclang; -lclang" ) -/** - * Opaque pointer representing client data that will be passed through - * to various callbacks and visitors. - */ -type ClientData = c.Pointer - -/** - * Provides the contents of a file that has not yet been saved to disk. - * - * Each CXUnsavedFile instance provides the name of a file on the - * system along with the current contents of that file that have not - * yet been saved to disk. - */ -type UnsavedFile struct { - /** - * The file whose contents have not yet been saved. - * - * This file must already exist in the file system. - */ - Filename *c.Char - - /** - * A buffer containing the unsaved contents of this file. - */ - Contents *c.Char - - /** - * The length of the unsaved contents of this buffer. - */ - Length c.Ulong -} - -/** - * An "index" that consists of a set of translation units that would - * typically be linked together into an executable or library. - */ -type Index struct { - Unused [0]byte -} - -/** - * Provides a shared context for creating translation units. - * - * It provides two options: - * - * - excludeDeclarationsFromPCH: When non-zero, allows enumeration of "local" - * declarations (when loading any new translation units). A "local" declaration - * is one that belongs in the translation unit itself and not in a precompiled - * header that was used by the translation unit. If zero, all declarations - * will be enumerated. - * - * Here is an example: - * - * \code - * // excludeDeclsFromPCH = 1, displayDiagnostics=1 - * Idx = clang_createIndex(1, 1); - * - * // IndexTest.pch was produced with the following command: - * // "clang -x c IndexTest.h -emit-ast -o IndexTest.pch" - * TU = clang_createTranslationUnit(Idx, "IndexTest.pch"); - * - * // This will load all the symbols from 'IndexTest.pch' - * clang_visitChildren(clang_getTranslationUnitCursor(TU), - * TranslationUnitVisitor, 0); - * clang_disposeTranslationUnit(TU); - * - * // This will load all the symbols from 'IndexTest.c', excluding symbols - * // from 'IndexTest.pch'. - * char *args[] = { "-Xclang", "-include-pch=IndexTest.pch" }; - * TU = clang_createTranslationUnitFromSourceFile(Idx, "IndexTest.c", 2, args, - * 0, 0); - * clang_visitChildren(clang_getTranslationUnitCursor(TU), - * TranslationUnitVisitor, 0); - * clang_disposeTranslationUnit(TU); - * \endcode - * - * This process of creating the 'pch', loading it separately, and using it (via - * -include-pch) allows 'excludeDeclsFromPCH' to remove redundant callbacks - * (which gives the indexer the same performance benefit as the compiler). - */ -//go:linkname CreateIndex C.clang_createIndex -func CreateIndex(excludeDeclarationsFromPCH, displayDiagnostics c.Int) *Index - -/** - * Destroy the given index. - * - * The index must not be destroyed until all of the translation units created - * within that index have been destroyed. - */ -// llgo:link (*Index).Dispose C.clang_disposeIndex -func (*Index) Dispose() {} - -/** - * Flags that control the creation of translation units. - * - * The enumerators in this enumeration type are meant to be bitwise - * ORed together to specify which options should be used when - * constructing the translation unit. - */ -const ( - TranslationUnit_None = 0x0 -) - -/** - * Same as \c clang_parseTranslationUnit2, but returns - * the \c CXTranslationUnit instead of an error code. In case of an error this - * routine returns a \c NULL \c CXTranslationUnit, without further detailed - * error codes. - */ -// llgo:link (*Index).ParseTranslationUnit C.clang_parseTranslationUnit -func (*Index) ParseTranslationUnit( - sourceFilename *c.Char, commandLineArgs **c.Char, numCommandLineArgs c.Int, - unsavedFiles *UnsavedFile, numUnsavedFiles c.Uint, options c.Uint) *TranslationUnit { - return nil -} - -/** - * A single translation unit, which resides in an index. - */ -type TranslationUnit struct { - Unused [0]byte -} - -/** - * Destroy the specified CXTranslationUnit object. - */ -// llgo:link (*TranslationUnit).Dispose C.clang_disposeTranslationUnit -func (*TranslationUnit) Dispose() {} - -/** - * Retrieve the cursor that represents the given translation unit. - * - * The translation unit cursor can be used to start traversing the - * various declarations within the given translation unit. - */ -func (p *TranslationUnit) Cursor() (ret Cursor) { - p.wrapCursor(&ret) - return -} - -//llgo:link (*TranslationUnit).wrapCursor C.wrap_clang_getTranslationUnitCursor -func (t *TranslationUnit) wrapCursor(cursor *Cursor) {} - -/** - * Describes the kind of entity that a cursor refers to. - */ -type CursorKind c.Int - -/* for debug/testing */ -// llgo:link CursorKind.String C.clang_getCursorKindSpelling -func (CursorKind) String() (ret String) { - return -} - const ( /* Declarations */ /** @@ -1279,6 +1125,161 @@ const ( OverloadCandidate = 700 ) +/** + * Opaque pointer representing client data that will be passed through + * to various callbacks and visitors. + */ +type ClientData = c.Pointer + +/** + * Provides the contents of a file that has not yet been saved to disk. + * + * Each CXUnsavedFile instance provides the name of a file on the + * system along with the current contents of that file that have not + * yet been saved to disk. + */ +type UnsavedFile struct { + /** + * The file whose contents have not yet been saved. + * + * This file must already exist in the file system. + */ + Filename *c.Char + + /** + * A buffer containing the unsaved contents of this file. + */ + Contents *c.Char + + /** + * The length of the unsaved contents of this buffer. + */ + Length c.Ulong +} + +/** + * An "index" that consists of a set of translation units that would + * typically be linked together into an executable or library. + */ +type Index struct { + Unused [0]byte +} + +/** + * Provides a shared context for creating translation units. + * + * It provides two options: + * + * - excludeDeclarationsFromPCH: When non-zero, allows enumeration of "local" + * declarations (when loading any new translation units). A "local" declaration + * is one that belongs in the translation unit itself and not in a precompiled + * header that was used by the translation unit. If zero, all declarations + * will be enumerated. + * + * Here is an example: + * + * \code + * // excludeDeclsFromPCH = 1, displayDiagnostics=1 + * Idx = clang_createIndex(1, 1); + * + * // IndexTest.pch was produced with the following command: + * // "clang -x c IndexTest.h -emit-ast -o IndexTest.pch" + * TU = clang_createTranslationUnit(Idx, "IndexTest.pch"); + * + * // This will load all the symbols from 'IndexTest.pch' + * clang_visitChildren(clang_getTranslationUnitCursor(TU), + * TranslationUnitVisitor, 0); + * clang_disposeTranslationUnit(TU); + * + * // This will load all the symbols from 'IndexTest.c', excluding symbols + * // from 'IndexTest.pch'. + * char *args[] = { "-Xclang", "-include-pch=IndexTest.pch" }; + * TU = clang_createTranslationUnitFromSourceFile(Idx, "IndexTest.c", 2, args, + * 0, 0); + * clang_visitChildren(clang_getTranslationUnitCursor(TU), + * TranslationUnitVisitor, 0); + * clang_disposeTranslationUnit(TU); + * \endcode + * + * This process of creating the 'pch', loading it separately, and using it (via + * -include-pch) allows 'excludeDeclsFromPCH' to remove redundant callbacks + * (which gives the indexer the same performance benefit as the compiler). + */ +//go:linkname CreateIndex C.clang_createIndex +func CreateIndex(excludeDeclarationsFromPCH, displayDiagnostics c.Int) *Index + +/** + * Destroy the given index. + * + * The index must not be destroyed until all of the translation units created + * within that index have been destroyed. + */ +// llgo:link (*Index).Dispose C.clang_disposeIndex +func (*Index) Dispose() {} + +/** + * Flags that control the creation of translation units. + * + * The enumerators in this enumeration type are meant to be bitwise + * ORed together to specify which options should be used when + * constructing the translation unit. + */ +const ( + TranslationUnit_None = 0x0 +) + +/** + * Same as \c clang_parseTranslationUnit2, but returns + * the \c CXTranslationUnit instead of an error code. In case of an error this + * routine returns a \c NULL \c CXTranslationUnit, without further detailed + * error codes. + */ +// llgo:link (*Index).ParseTranslationUnit C.clang_parseTranslationUnit +func (*Index) ParseTranslationUnit( + sourceFilename *c.Char, commandLineArgs **c.Char, numCommandLineArgs c.Int, + unsavedFiles *UnsavedFile, numUnsavedFiles c.Uint, options c.Uint) *TranslationUnit { + return nil +} + +/** + * A single translation unit, which resides in an index. + */ +type TranslationUnit struct { + Unused [0]byte +} + +/** + * Destroy the specified CXTranslationUnit object. + */ +// llgo:link (*TranslationUnit).Dispose C.clang_disposeTranslationUnit +func (*TranslationUnit) Dispose() {} + +/** + * Retrieve the cursor that represents the given translation unit. + * + * The translation unit cursor can be used to start traversing the + * various declarations within the given translation unit. + */ + +//llgo:link (*TranslationUnit).wrapCursor C.wrap_clang_getTranslationUnitCursor +func (t *TranslationUnit) wrapCursor(cursor *Cursor) {} + +func (t *TranslationUnit) Cursor() (ret Cursor) { + t.wrapCursor(&ret) + return +} + +/** + * Describes the kind of entity that a cursor refers to. + */ +type CursorKind c.Int + +/* for debug/testing */ +// llgo:link CursorKind.String C.clang_getCursorKindSpelling +func (CursorKind) String() (ret String) { + return +} + /** * A cursor representing some element in the abstract syntax tree for * a translation unit. @@ -1303,11 +1304,32 @@ type Cursor struct { data [3]c.Pointer } +/** + * The type of an element in the abstract syntax tree. + * + */ type Type struct { Kind CursorKind data [2]c.Pointer } +/** + * A particular source file that is part of a translation unit. + */ +type File uintptr + +/** + * Identifies a specific source location within a translation + * unit. + * + * Use clang_getExpansionLocation() or clang_getSpellingLocation() + * to map a source location to a particular file, line, and column. + */ +type SourceLocation struct { + ptrData [2]c.Pointer + intData c.Uint +} + /** * Retrieve a name for the entity referenced by this cursor. */ @@ -1338,7 +1360,7 @@ func (c Cursor) Mangling() (ret String) { // llgo:link (*Cursor).wrapType C.wrap_clang_getCursorType func (c *Cursor) wrapType(ret *Type) {} -func (c *Cursor) Type() (ret Type) { +func (c Cursor) Type() (ret Type) { c.wrapType(&ret) return } @@ -1351,7 +1373,7 @@ func (c *Cursor) Type() (ret Type) { // llgo:link (*Cursor).wrapResultType C.wrap_clang_getCursorResultType func (c *Cursor) wrapResultType(ret *Type) {} -func (c *Cursor) ResultType() (ret Type) { +func (c Cursor) ResultType() (ret Type) { c.wrapResultType(&ret) return } @@ -1380,13 +1402,28 @@ func (c Cursor) NumArguments() (num c.Int) { * invalid cursor is returned. */ // llgo:link (*Cursor).wrapArgument C.wrap_clang_Cursor_getArgument -func (*Cursor) wrapArgument(index uint8, arg *Cursor) {} +func (*Cursor) wrapArgument(index c.Uint, arg *Cursor) {} -func (c Cursor) Argument(index uint8) (arg Cursor) { +func (c Cursor) Argument(index c.Uint) (arg Cursor) { c.wrapArgument(index, &arg) return } +// llgo:link (*Cursor).wrapLocation C.wrap_clang_getCursorLocation +func (c *Cursor) wrapLocation(loc *SourceLocation) {} + +func (c Cursor) Location() (loc SourceLocation) { + c.wrapLocation(&loc) + return +} + +// llgo:link (*SourceLocation).wrapSpellingLocation C.wrap_clang_getSpellingLocation +func (l *SourceLocation) wrapSpellingLocation(file *File, line, column, offset *c.Uint) {} + +func (l SourceLocation) SpellingLocation(file *File, line, column, offset *c.Uint) { + l.wrapSpellingLocation(file, line, column, offset) +} + /** * Pretty-print the underlying type using the rules of the * language of the translation unit from which it came. @@ -1402,6 +1439,9 @@ func (t Type) String() (ret String) { return t.wrapString() } +//llgo:link File.FileName C.clang_getFileName +func (File) FileName() (ret String) { return } + /** * Describes how the traversal of the children of a particular * cursor should proceed after visiting a particular child cursor. From 8029bb6142406be107bedaec8bf2464cb60d3598 Mon Sep 17 00:00:00 2001 From: visualfc Date: Thu, 25 Jul 2024 18:08:25 +0800 Subject: [PATCH 015/152] ssa: fix map next for named --- cl/_testrt/makemap/in.go | 12 +++++ cl/_testrt/makemap/out.ll | 102 ++++++++++++++++++++++++++++++++++++++ ssa/datastruct.go | 4 +- 3 files changed, 116 insertions(+), 2 deletions(-) diff --git a/cl/_testrt/makemap/in.go b/cl/_testrt/makemap/in.go index 4cf5ce72..ff44ce64 100644 --- a/cl/_testrt/makemap/in.go +++ b/cl/_testrt/makemap/in.go @@ -6,6 +6,7 @@ func main() { make3() make4() make5() + make6() } func make1() { @@ -101,3 +102,14 @@ func make5() { println(k, v) } } + +type M map[int]string + +func make6() { + var m M + m = make(map[int]string) + m[1] = "hello" + for k, v := range m { + println(k, v) + } +} diff --git a/cl/_testrt/makemap/out.ll b/cl/_testrt/makemap/out.ll index d0a6ad31..dd5a8217 100644 --- a/cl/_testrt/makemap/out.ll +++ b/cl/_testrt/makemap/out.ll @@ -46,6 +46,8 @@ source_filename = "main" @"chan _llgo_int" = linkonce global ptr null, align 8 @19 = private unnamed_addr constant [4 x i8] c"chan", align 1 @"map[chan _llgo_int]_llgo_int" = linkonce global ptr null, align 8 +@_llgo_main.M = linkonce global ptr null, align 8 +@20 = private unnamed_addr constant [1 x i8] c"M", align 1 define void @main.init() { _llgo_0: @@ -72,6 +74,7 @@ _llgo_0: call void @main.make3() call void @main.make4() call void @main.make5() + call void @main.make6() ret i32 0 } @@ -998,6 +1001,74 @@ _llgo_6: ; preds = %_llgo_5, %_llgo_4 br i1 %41, label %_llgo_2, label %_llgo_3 } +define void @main.make6() { +_llgo_0: + %0 = load ptr, ptr @"map[_llgo_int]_llgo_string", align 8 + %1 = call ptr @"github.com/goplus/llgo/internal/runtime.MakeMap"(ptr %0, i64 0) + %2 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %3 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %2, i32 0, i32 0 + store ptr @5, ptr %3, align 8 + %4 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %2, i32 0, i32 1 + store i64 5, ptr %4, align 4 + %5 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %2, align 8 + %6 = load ptr, ptr @_llgo_main.M, align 8 + %7 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 8) + store i64 1, ptr %7, align 4 + %8 = call ptr @"github.com/goplus/llgo/internal/runtime.MapAssign"(ptr %6, ptr %1, ptr %7) + store %"github.com/goplus/llgo/internal/runtime.String" %5, ptr %8, align 8 + %9 = load ptr, ptr @_llgo_main.M, align 8 + %10 = call ptr @"github.com/goplus/llgo/internal/runtime.NewMapIter"(ptr %9, ptr %1) + br label %_llgo_1 + +_llgo_1: ; preds = %_llgo_2, %_llgo_0 + %11 = call { i1, ptr, ptr } @"github.com/goplus/llgo/internal/runtime.MapIterNext"(ptr %10) + %12 = extractvalue { i1, ptr, ptr } %11, 0 + br i1 %12, label %_llgo_4, label %_llgo_5 + +_llgo_2: ; preds = %_llgo_6 + %13 = extractvalue { i1, i64, %"github.com/goplus/llgo/internal/runtime.String" } %29, 1 + %14 = extractvalue { i1, i64, %"github.com/goplus/llgo/internal/runtime.String" } %29, 2 + call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %13) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 32) + call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" %14) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + br label %_llgo_1 + +_llgo_3: ; preds = %_llgo_6 + ret void + +_llgo_4: ; preds = %_llgo_1 + %15 = extractvalue { i1, ptr, ptr } %11, 1 + %16 = extractvalue { i1, ptr, ptr } %11, 2 + %17 = load i64, ptr %15, align 4 + %18 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %16, align 8 + %19 = alloca { i1, i64, %"github.com/goplus/llgo/internal/runtime.String" }, align 8 + %20 = getelementptr inbounds { i1, i64, %"github.com/goplus/llgo/internal/runtime.String" }, ptr %19, i32 0, i32 0 + store i1 true, ptr %20, align 1 + %21 = getelementptr inbounds { i1, i64, %"github.com/goplus/llgo/internal/runtime.String" }, ptr %19, i32 0, i32 1 + store i64 %17, ptr %21, align 4 + %22 = getelementptr inbounds { i1, i64, %"github.com/goplus/llgo/internal/runtime.String" }, ptr %19, i32 0, i32 2 + store %"github.com/goplus/llgo/internal/runtime.String" %18, ptr %22, align 8 + %23 = load { i1, i64, %"github.com/goplus/llgo/internal/runtime.String" }, ptr %19, align 8 + br label %_llgo_6 + +_llgo_5: ; preds = %_llgo_1 + %24 = alloca { i1, i64, %"github.com/goplus/llgo/internal/runtime.String" }, align 8 + %25 = getelementptr inbounds { i1, i64, %"github.com/goplus/llgo/internal/runtime.String" }, ptr %24, i32 0, i32 0 + store i1 false, ptr %25, align 1 + %26 = getelementptr inbounds { i1, i64, %"github.com/goplus/llgo/internal/runtime.String" }, ptr %24, i32 0, i32 1 + store i64 0, ptr %26, align 4 + %27 = getelementptr inbounds { i1, i64, %"github.com/goplus/llgo/internal/runtime.String" }, ptr %24, i32 0, i32 2 + store %"github.com/goplus/llgo/internal/runtime.String" zeroinitializer, ptr %27, align 8 + %28 = load { i1, i64, %"github.com/goplus/llgo/internal/runtime.String" }, ptr %24, align 8 + br label %_llgo_6 + +_llgo_6: ; preds = %_llgo_5, %_llgo_4 + %29 = phi { i1, i64, %"github.com/goplus/llgo/internal/runtime.String" } [ %23, %_llgo_4 ], [ %28, %_llgo_5 ] + %30 = extractvalue { i1, i64, %"github.com/goplus/llgo/internal/runtime.String" } %29, 0 + br i1 %30, label %_llgo_2, label %_llgo_3 +} + declare void @"github.com/goplus/llgo/internal/runtime.init"() define void @"main.init$after"() { @@ -1697,6 +1768,37 @@ _llgo_37: ; preds = %_llgo_36 br label %_llgo_38 _llgo_38: ; preds = %_llgo_37, %_llgo_36 + %402 = call ptr @"github.com/goplus/llgo/internal/runtime.NewNamed"(i64 21, i64 8, i64 0, i64 0) + %403 = load ptr, ptr @_llgo_main.M, align 8 + %404 = icmp eq ptr %403, null + br i1 %404, label %_llgo_39, label %_llgo_40 + +_llgo_39: ; preds = %_llgo_38 + call void @"github.com/goplus/llgo/internal/runtime.SetDirectIface"(ptr %402) + store ptr %402, ptr @_llgo_main.M, align 8 + br label %_llgo_40 + +_llgo_40: ; preds = %_llgo_39, %_llgo_38 + %405 = load ptr, ptr @"map[_llgo_int]_llgo_string", align 8 + br i1 %404, label %_llgo_41, label %_llgo_42 + +_llgo_41: ; preds = %_llgo_40 + %406 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %407 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %406, i32 0, i32 0 + store ptr @4, ptr %407, align 8 + %408 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %406, i32 0, i32 1 + store i64 4, ptr %408, align 4 + %409 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %406, align 8 + %410 = alloca %"github.com/goplus/llgo/internal/runtime.String", align 8 + %411 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %410, i32 0, i32 0 + store ptr @20, ptr %411, align 8 + %412 = getelementptr inbounds %"github.com/goplus/llgo/internal/runtime.String", ptr %410, i32 0, i32 1 + store i64 1, ptr %412, align 4 + %413 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %410, align 8 + call void @"github.com/goplus/llgo/internal/runtime.InitNamed"(ptr %402, %"github.com/goplus/llgo/internal/runtime.String" %409, %"github.com/goplus/llgo/internal/runtime.String" %413, ptr %405, { ptr, i64, i64 } zeroinitializer, { ptr, i64, i64 } zeroinitializer) + br label %_llgo_42 + +_llgo_42: ; preds = %_llgo_41, %_llgo_40 ret void } diff --git a/ssa/datastruct.go b/ssa/datastruct.go index 6157d57d..503f5682 100644 --- a/ssa/datastruct.go +++ b/ssa/datastruct.go @@ -579,8 +579,8 @@ func (b Builder) Next(typ Type, iter Expr, isString bool) Expr { return b.InlineCall(b.Pkg.rtFunc("StringIterNext"), iter) } prog := b.Prog - ktyp := prog.Type(typ.raw.Type.(*types.Map).Key(), InGo) - vtyp := prog.Type(typ.raw.Type.(*types.Map).Elem(), InGo) + ktyp := prog.Type(typ.raw.Type.Underlying().(*types.Map).Key(), InGo) + vtyp := prog.Type(typ.raw.Type.Underlying().(*types.Map).Elem(), InGo) rets := b.InlineCall(b.Pkg.rtFunc("MapIterNext"), iter) ok := b.impl.CreateExtractValue(rets.impl, 0, "") t := prog.Struct(prog.Bool(), ktyp, vtyp) From d8026833dc509852a8c7da315050e93a3c6a6a45 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Thu, 25 Jul 2024 18:46:40 +0800 Subject: [PATCH 016/152] llcppg --- c/cjson/cjson.go | 16 +++- chore/_xtool/llcppsigfetch/llcppsigfetch.go | 21 +++++ chore/_xtool/llcppsymg/llcppsymg.go | 57 +++++++++++++ chore/gogensig/gogensig.go | 21 +++++ chore/llcppg/design.md | 5 +- chore/llcppg/llcppg.go | 92 +++++++++++++++++++++ chore/llcppg/types/types.go | 26 ++++++ 7 files changed, 234 insertions(+), 4 deletions(-) create mode 100644 chore/_xtool/llcppsigfetch/llcppsigfetch.go create mode 100644 chore/_xtool/llcppsymg/llcppsymg.go create mode 100644 chore/gogensig/gogensig.go create mode 100644 chore/llcppg/llcppg.go create mode 100644 chore/llcppg/types/types.go diff --git a/c/cjson/cjson.go b/c/cjson/cjson.go index 03a8e5ec..56936469 100644 --- a/c/cjson/cjson.go +++ b/c/cjson/cjson.go @@ -17,7 +17,7 @@ package cjson import ( - _ "unsafe" + "unsafe" "github.com/goplus/llgo/c" ) @@ -31,6 +31,20 @@ type JSON struct { Unused [0]byte } +//go:linkname Parse C.cJSON_Parse +func Parse(value *c.Char) *JSON + +//go:linkname ParseWithLength C.cJSON_ParseWithLength +func ParseWithLength(value *byte, valueLength uintptr) *JSON + +func ParseBytes(value []byte) *JSON { + return ParseWithLength(unsafe.SliceData(value), uintptr(len(value))) +} + +func ParseString(value string) *JSON { + return ParseWithLength(unsafe.StringData(value), uintptr(len(value))) +} + //go:linkname Null C.cJSON_CreateNull func Null() *JSON diff --git a/chore/_xtool/llcppsigfetch/llcppsigfetch.go b/chore/_xtool/llcppsigfetch/llcppsigfetch.go new file mode 100644 index 00000000..30190a18 --- /dev/null +++ b/chore/_xtool/llcppsigfetch/llcppsigfetch.go @@ -0,0 +1,21 @@ +/* + * 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 main + +func main() { + // TODO(xsw): implement llcppsigfetch tool +} diff --git a/chore/_xtool/llcppsymg/llcppsymg.go b/chore/_xtool/llcppsymg/llcppsymg.go new file mode 100644 index 00000000..ea60dffe --- /dev/null +++ b/chore/_xtool/llcppsymg/llcppsymg.go @@ -0,0 +1,57 @@ +/* + * 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 main + +import ( + "fmt" + "io" + "os" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/cjson" +) + +func main() { + cfgFile := "llcppg.cfg" + if len(os.Args) > 1 { + cfgFile = os.Args[1] + } + + var data []byte + var err error + if cfgFile == "-" { + data, err = io.ReadAll(os.Stdin) + } else { + data, err = os.ReadFile(cfgFile) + } + check(err) + + conf := cjson.ParseBytes(data) + if conf == nil { + fmt.Fprintln(os.Stderr, "Failed to parse config file:", cfgFile) + os.Exit(1) + } + defer conf.Delete() + + c.Printf(c.Str("%s"), conf.Print()) +} + +func check(err error) { + if err != nil { + panic(err) + } +} diff --git a/chore/gogensig/gogensig.go b/chore/gogensig/gogensig.go new file mode 100644 index 00000000..2079aaa2 --- /dev/null +++ b/chore/gogensig/gogensig.go @@ -0,0 +1,21 @@ +/* + * 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 main + +func main() { + // TODO(xsw): implement gogensig tool +} diff --git a/chore/llcppg/design.md b/chore/llcppg/design.md index 5c841675..7cecf46a 100644 --- a/chore/llcppg/design.md +++ b/chore/llcppg/design.md @@ -45,8 +45,7 @@ It generates a symbol table file named `llcppg.symb.json`. Its file format is as "mangle": "_ZN9INIReaderC1EPKcm", "c++": "INIReader::INIReader(char const*, unsigned long)", "go": "(*Reader).Init__0" - }, - ... + } ] ``` @@ -60,7 +59,7 @@ llcppsigfetch - # read config from stdin It fetches information of C/C++ symbols and print to stdout. Its format is as follows: -```json +``` TODO: see llgo/xtool/clang/ast ``` diff --git a/chore/llcppg/llcppg.go b/chore/llcppg/llcppg.go new file mode 100644 index 00000000..471b6c6c --- /dev/null +++ b/chore/llcppg/llcppg.go @@ -0,0 +1,92 @@ +/* + * 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 main + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "os" + "os/exec" + + "github.com/goplus/llgo/chore/llcppg/types" + "github.com/goplus/llgo/xtool/env" +) + +func llcppsymg(conf []byte) error { + cmd := exec.Command("llcppsymg", "-") + cmd.Stdin = bytes.NewReader(conf) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd.Run() +} + +func llcppsigfetch(conf []byte, out io.Writer) { + cmd := exec.Command("llcppsigfetch", "-") + cmd.Stdin = bytes.NewReader(conf) + cmd.Stdout = out + cmd.Stderr = os.Stderr + err := cmd.Run() + check(err) +} + +func gogensig(in io.Reader) error { + cmd := exec.Command("gogensig") + cmd.Stdin = in + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd.Run() +} + +func main() { + cfgFile := "llcppg.cfg" + if len(os.Args) > 1 { + cfgFile = os.Args[1] + } + if cfgFile == "-h" || cfgFile == "--help" { + fmt.Fprintln(os.Stderr, "Usage: llcppg [config-file]") + return + } + + f, err := os.Open(cfgFile) + check(err) + defer f.Close() + + var conf types.Config + json.NewDecoder(f).Decode(&conf) + conf.CFlags = env.ExpandEnv(conf.CFlags) + conf.Libs = env.ExpandEnv(conf.Libs) + + b, err := json.MarshalIndent(&conf, "", " ") + check(err) + + err = llcppsymg(b) + check(err) + + r, w := io.Pipe() + go llcppsigfetch(b, w) + + err = gogensig(r) + check(err) +} + +func check(err error) { + if err != nil { + panic(err) + } +} diff --git a/chore/llcppg/types/types.go b/chore/llcppg/types/types.go new file mode 100644 index 00000000..2e39bbdd --- /dev/null +++ b/chore/llcppg/types/types.go @@ -0,0 +1,26 @@ +/* + * 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 types + +// Config represents a configuration for the llcppg tool. +type Config struct { + Name string `json:"name"` + CFlags string `json:"cflags"` + Libs string `json:"libs"` + Include []string `json:"include"` + TrimPrefixes []string `json:"trimPrefixes"` +} From 5828e7f576a51658f8e9d84b3c7ae50156d58a4f Mon Sep 17 00:00:00 2001 From: xushiwei Date: Thu, 25 Jul 2024 19:18:26 +0800 Subject: [PATCH 017/152] Update llcppg.go --- chore/llcppg/llcppg.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chore/llcppg/llcppg.go b/chore/llcppg/llcppg.go index 471b6c6c..e0e9745d 100644 --- a/chore/llcppg/llcppg.go +++ b/chore/llcppg/llcppg.go @@ -46,7 +46,7 @@ func llcppsigfetch(conf []byte, out io.Writer) { } func gogensig(in io.Reader) error { - cmd := exec.Command("gogensig") + cmd := exec.Command("gogensig", "-") cmd.Stdin = in cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr From 9ebdddad1f1a874f7ae6cf4c740275ee49c1b626 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Thu, 25 Jul 2024 19:55:13 +0800 Subject: [PATCH 018/152] Delete rust/.gitkeep --- rust/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 rust/.gitkeep diff --git a/rust/.gitkeep b/rust/.gitkeep deleted file mode 100644 index e69de29b..00000000 From e82c33716aab668491c48d1f1fb9389790db590a Mon Sep 17 00:00:00 2001 From: xushiwei Date: Thu, 25 Jul 2024 20:37:09 +0800 Subject: [PATCH 019/152] Update and rename Rust-to-LLGO-Migration-Guide.md to How-to-support-a-Rust-Library.md --- ...LGO-Migration-Guide.md => How-to-support-a-Rust-Library.md} | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) rename doc/{Rust-to-LLGO-Migration-Guide.md => How-to-support-a-Rust-Library.md} (99%) diff --git a/doc/Rust-to-LLGO-Migration-Guide.md b/doc/How-to-support-a-Rust-Library.md similarity index 99% rename from doc/Rust-to-LLGO-Migration-Guide.md rename to doc/How-to-support-a-Rust-Library.md index c6b3a3c8..7b7ba70d 100644 --- a/doc/Rust-to-LLGO-Migration-Guide.md +++ b/doc/How-to-support-a-Rust-Library.md @@ -1,4 +1,5 @@ -# Rust to LLGO Migration Document +How to support a Rust Library +===== ## Add Dependencies & Build Configuration From 87a78091049c0be0df8f2a8d4bfb9843b21a9dfa Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 26 Jul 2024 07:42:26 +0800 Subject: [PATCH 020/152] library os: ReadFile --- internal/lib/os/file.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/lib/os/file.go b/internal/lib/os/file.go index 5b309661..f0f87abc 100644 --- a/internal/lib/os/file.go +++ b/internal/lib/os/file.go @@ -467,6 +467,7 @@ func (dir dirFS) join(name string) (string, error) { } return string(dir) + string(PathSeparator) + name, nil } +*/ // ReadFile reads the named file and returns the contents. // A successful call returns err == nil, not err == EOF. @@ -529,4 +530,3 @@ func WriteFile(name string, data []byte, perm FileMode) error { } return err } -*/ From 37650fae754960678c7320f20fc7d865d99f702f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E8=8B=B1=E6=9D=B0?= <2635879218@qq.com> Date: Fri, 26 Jul 2024 09:19:15 +0800 Subject: [PATCH 021/152] refactor(c-libuv): Adjust the style of Errno definitions --- c/libuv/error.go | 171 ++++++++++++++++++++++++----------------------- 1 file changed, 88 insertions(+), 83 deletions(-) diff --git a/c/libuv/error.go b/c/libuv/error.go index beb286a2..3b67a22b 100644 --- a/c/libuv/error.go +++ b/c/libuv/error.go @@ -8,90 +8,95 @@ import ( ) const ( - E2BIG Errno = (Errno)(syscall.E2BIG) - EACCES Errno = (Errno)(syscall.EACCES) - EADDRINUSE Errno = (Errno)(syscall.EADDRINUSE) - EADDRNOTAVAIL Errno = (Errno)(syscall.EADDRNOTAVAIL) - EAFNOSUPPORT Errno = (Errno)(syscall.EAFNOSUPPORT) - EAGAIN Errno = (Errno)(syscall.EAGAIN) - EAI_ADDRFAMILY Errno = net.EAI_ADDRFAMILY - EAI_AGAIN Errno = net.EAI_AGAIN - EAI_BADFLAGS Errno = net.EAI_BADFLAGS - EAI_BADHINTS Errno = net.EAI_BADHINTS - EAI_CANCELED Errno = -3003 - EAI_FAIL Errno = net.EAI_FAIL - EAI_FAMILY Errno = net.EAI_FAMILY - EAI_MEMORY Errno = net.EAI_MEMORY - EAI_NODATA Errno = net.EAI_NODATA - EAI_NONAME Errno = net.EAI_NONAME - EAI_OVERFLOW Errno = net.EAI_OVERFLOW - EAI_PROTOCOL Errno = net.EAI_PROTOCOL - EAI_SERVICE Errno = net.EAI_SERVICE - EAI_SOCKTYPE Errno = net.EAI_SOCKTYPE - EALREADY Errno = (Errno)(syscall.EALREADY) - EBADF Errno = (Errno)(syscall.EBADF) - EBUSY Errno = (Errno)(syscall.EBUSY) - ECANCELED Errno = (Errno)(syscall.ECANCELED) - ECHARSET Errno = -4080 - ECONNABORTED Errno = (Errno)(syscall.ECONNABORTED) - ECONNREFUSED Errno = (Errno)(syscall.ECONNREFUSED) - ECONNRESET Errno = (Errno)(syscall.ECONNRESET) - EDESTADDRREQ Errno = (Errno)(syscall.EDESTADDRREQ) - EEXIST Errno = (Errno)(syscall.EEXIST) - EFAULT Errno = (Errno)(syscall.EFAULT) - EFBIG Errno = (Errno)(syscall.EFBIG) - EHOSTUNREACH Errno = (Errno)(syscall.EHOSTUNREACH) - EINTR Errno = (Errno)(syscall.EINTR) - EINVAL Errno = (Errno)(syscall.EINVAL) - EIO Errno = (Errno)(syscall.EIO) - EISCONN Errno = (Errno)(syscall.EISCONN) - EISDIR Errno = (Errno)(syscall.EISDIR) - ELOOP Errno = (Errno)(syscall.ELOOP) - EMFILE Errno = (Errno)(syscall.EMFILE) - EMSGSIZE Errno = (Errno)(syscall.EMSGSIZE) - ENAMETOOLONG Errno = (Errno)(syscall.ENAMETOOLONG) - ENETDOWN Errno = (Errno)(syscall.ENETDOWN) - ENETUNREACH Errno = (Errno)(syscall.ENETUNREACH) - ENFILE Errno = (Errno)(syscall.ENFILE) - ENOBUFS Errno = (Errno)(syscall.ENOBUFS) - ENODEV Errno = (Errno)(syscall.ENODEV) - ENOENT Errno = (Errno)(syscall.ENOENT) - ENOMEM Errno = (Errno)(syscall.ENOMEM) - ENONET Errno = -4056 - ENOPROTOOPT Errno = (Errno)(syscall.ENOPROTOOPT) - ENOSPC Errno = (Errno)(syscall.ENOSPC) - ENOSYS Errno = (Errno)(syscall.ENOSYS) - ENOTCONN Errno = (Errno)(syscall.ENOTCONN) - ENOTDIR Errno = (Errno)(syscall.ENOTDIR) - ENOTEMPTY Errno = (Errno)(syscall.ENOTEMPTY) - ENOTSOCK Errno = (Errno)(syscall.ENOTSOCK) - ENOTSUP Errno = (Errno)(syscall.ENOTSUP) - EOVERFLOW Errno = (Errno)(syscall.EOVERFLOW) - EPERM Errno = (Errno)(syscall.EPERM) - EPIPE Errno = (Errno)(syscall.EPIPE) - EPROTO Errno = (Errno)(syscall.EPROTO) - EPROTONOSUPPORT Errno = (Errno)(syscall.EPROTONOSUPPORT) - EPROTOTYPE Errno = (Errno)(syscall.EPROTOTYPE) - ERANGE Errno = (Errno)(syscall.ERANGE) - EROFS Errno = (Errno)(syscall.EROFS) - ESHUTDOWN Errno = (Errno)(syscall.ESHUTDOWN) - ESPIPE Errno = (Errno)(syscall.ESPIPE) - ESRCH Errno = (Errno)(syscall.ESRCH) - ETIMEDOUT Errno = (Errno)(syscall.ETIMEDOUT) - ETXTBSY Errno = (Errno)(syscall.ETXTBSY) - EXDEV Errno = (Errno)(syscall.EXDEV) - UNKNOWN Errno = -4094 - EOF Errno = -1 - ENXIO Errno = (Errno)(syscall.ENXIO) - EMLINK Errno = (Errno)(syscall.EMLINK) - EHOSTDOWN Errno = (Errno)(syscall.EHOSTDOWN) - EREMOTEIO Errno = -4030 - ENOTTY Errno = (Errno)(syscall.ENOTTY) - EFTYPE Errno = (Errno)(syscall.EFTYPE) - EILSEQ Errno = (Errno)(syscall.EILSEQ) - ESOCKTNOSUPPORT Errno = (Errno)(syscall.ESOCKTNOSUPPORT) + E2BIG Errno = Errno(syscall.E2BIG) + EACCES Errno = Errno(syscall.EACCES) + EADDRINUSE Errno = Errno(syscall.EADDRINUSE) + EADDRNOTAVAIL Errno = Errno(syscall.EADDRNOTAVAIL) + EAFNOSUPPORT Errno = Errno(syscall.EAFNOSUPPORT) + EAGAIN Errno = Errno(syscall.EAGAIN) + EALREADY Errno = Errno(syscall.EALREADY) + EBADF Errno = Errno(syscall.EBADF) + EBUSY Errno = Errno(syscall.EBUSY) + ECANCELED Errno = Errno(syscall.ECANCELED) + ECONNABORTED Errno = Errno(syscall.ECONNABORTED) + ECONNREFUSED Errno = Errno(syscall.ECONNREFUSED) + ECONNRESET Errno = Errno(syscall.ECONNRESET) + EDESTADDRREQ Errno = Errno(syscall.EDESTADDRREQ) + EEXIST Errno = Errno(syscall.EEXIST) + EFAULT Errno = Errno(syscall.EFAULT) + EFBIG Errno = Errno(syscall.EFBIG) + EHOSTUNREACH Errno = Errno(syscall.EHOSTUNREACH) + EINTR Errno = Errno(syscall.EINTR) + EINVAL Errno = Errno(syscall.EINVAL) + EIO Errno = Errno(syscall.EIO) + EISCONN Errno = Errno(syscall.EISCONN) + EISDIR Errno = Errno(syscall.EISDIR) + ELOOP Errno = Errno(syscall.ELOOP) + EMFILE Errno = Errno(syscall.EMFILE) + EMSGSIZE Errno = Errno(syscall.EMSGSIZE) + ENAMETOOLONG Errno = Errno(syscall.ENAMETOOLONG) + ENETDOWN Errno = Errno(syscall.ENETDOWN) + ENETUNREACH Errno = Errno(syscall.ENETUNREACH) + ENFILE Errno = Errno(syscall.ENFILE) + ENOBUFS Errno = Errno(syscall.ENOBUFS) + ENODEV Errno = Errno(syscall.ENODEV) + ENOENT Errno = Errno(syscall.ENOENT) + ENOMEM Errno = Errno(syscall.ENOMEM) + ENOPROTOOPT Errno = Errno(syscall.ENOPROTOOPT) + ENOSPC Errno = Errno(syscall.ENOSPC) + ENOSYS Errno = Errno(syscall.ENOSYS) + ENOTCONN Errno = Errno(syscall.ENOTCONN) + ENOTDIR Errno = Errno(syscall.ENOTDIR) + ENOTEMPTY Errno = Errno(syscall.ENOTEMPTY) + ENOTSOCK Errno = Errno(syscall.ENOTSOCK) + ENOTSUP Errno = Errno(syscall.ENOTSUP) + EOVERFLOW Errno = Errno(syscall.EOVERFLOW) + EPERM Errno = Errno(syscall.EPERM) + EPIPE Errno = Errno(syscall.EPIPE) + EPROTO Errno = Errno(syscall.EPROTO) + EPROTONOSUPPORT Errno = Errno(syscall.EPROTONOSUPPORT) + EPROTOTYPE Errno = Errno(syscall.EPROTOTYPE) + ERANGE Errno = Errno(syscall.ERANGE) + EROFS Errno = Errno(syscall.EROFS) + ESHUTDOWN Errno = Errno(syscall.ESHUTDOWN) + ESPIPE Errno = Errno(syscall.ESPIPE) + ESRCH Errno = Errno(syscall.ESRCH) + ETIMEDOUT Errno = Errno(syscall.ETIMEDOUT) + ETXTBSY Errno = Errno(syscall.ETXTBSY) + EXDEV Errno = Errno(syscall.EXDEV) + ENXIO Errno = Errno(syscall.ENXIO) + EMLINK Errno = Errno(syscall.EMLINK) + EHOSTDOWN Errno = Errno(syscall.EHOSTDOWN) + ENOTTY Errno = Errno(syscall.ENOTTY) + EFTYPE Errno = Errno(syscall.EFTYPE) + EILSEQ Errno = Errno(syscall.EILSEQ) + ESOCKTNOSUPPORT Errno = Errno(syscall.ESOCKTNOSUPPORT) +) - ERRNO_MAX = EOF - 1 +const ( + EAI_ADDRFAMILY Errno = Errno(net.EAI_ADDRFAMILY) + EAI_AGAIN Errno = Errno(net.EAI_AGAIN) + EAI_BADFLAGS Errno = Errno(net.EAI_BADFLAGS) + EAI_BADHINTS Errno = Errno(net.EAI_BADHINTS) + EAI_FAIL Errno = Errno(net.EAI_FAIL) + EAI_FAMILY Errno = Errno(net.EAI_FAMILY) + EAI_MEMORY Errno = Errno(net.EAI_MEMORY) + EAI_NODATA Errno = Errno(net.EAI_NODATA) + EAI_NONAME Errno = Errno(net.EAI_NONAME) + EAI_OVERFLOW Errno = Errno(net.EAI_OVERFLOW) + EAI_PROTOCOL Errno = Errno(net.EAI_PROTOCOL) + EAI_SERVICE Errno = Errno(net.EAI_SERVICE) + EAI_SOCKTYPE Errno = Errno(net.EAI_SOCKTYPE) +) + +const ( + EAI_CANCELED Errno = -3003 + ECHARSET Errno = -4080 + ENONET Errno = -4056 + UNKNOWN Errno = -4094 + EOF Errno = -1 + EREMOTEIO Errno = -4030 + ERRNO_MAX Errno = EOF - 1 ) type Errno c.Int From 1a7ecda67c4a74527415ce558fbefd233295d5f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E8=8B=B1=E6=9D=B0?= <2635879218@qq.com> Date: Fri, 26 Jul 2024 09:37:17 +0800 Subject: [PATCH 022/152] refactor(c-libuv): Separate third-party libraries from standard libraries --- c/libuv/error.go | 3 ++- c/libuv/fs.go | 3 ++- c/libuv/libuv.go | 3 ++- c/libuv/net.go | 3 ++- c/libuv/signal.go | 3 ++- c/libuv/timer.go | 3 ++- 6 files changed, 12 insertions(+), 6 deletions(-) diff --git a/c/libuv/error.go b/c/libuv/error.go index 3b67a22b..309604e4 100644 --- a/c/libuv/error.go +++ b/c/libuv/error.go @@ -1,10 +1,11 @@ package libuv import ( + _ "unsafe" + "github.com/goplus/llgo/c" "github.com/goplus/llgo/c/net" "github.com/goplus/llgo/c/syscall" - _ "unsafe" ) const ( diff --git a/c/libuv/fs.go b/c/libuv/fs.go index 0de0a458..42afbe8a 100644 --- a/c/libuv/fs.go +++ b/c/libuv/fs.go @@ -1,8 +1,9 @@ package libuv import ( - "github.com/goplus/llgo/c" _ "unsafe" + + "github.com/goplus/llgo/c" ) const ( diff --git a/c/libuv/libuv.go b/c/libuv/libuv.go index 11a36700..ff1c7c78 100644 --- a/c/libuv/libuv.go +++ b/c/libuv/libuv.go @@ -1,9 +1,10 @@ package libuv import ( + _ "unsafe" + "github.com/goplus/llgo/c" "github.com/goplus/llgo/c/net" - _ "unsafe" ) const ( diff --git a/c/libuv/net.go b/c/libuv/net.go index 3fbb8f96..28cf10b6 100644 --- a/c/libuv/net.go +++ b/c/libuv/net.go @@ -1,9 +1,10 @@ package libuv import ( + _ "unsafe" + "github.com/goplus/llgo/c" "github.com/goplus/llgo/c/net" - _ "unsafe" ) const ( diff --git a/c/libuv/signal.go b/c/libuv/signal.go index 8aad3b0e..a3ede8ff 100644 --- a/c/libuv/signal.go +++ b/c/libuv/signal.go @@ -1,8 +1,9 @@ package libuv import ( - "github.com/goplus/llgo/c" _ "unsafe" + + "github.com/goplus/llgo/c" ) /* Handle types. */ diff --git a/c/libuv/timer.go b/c/libuv/timer.go index feeabe5a..98970402 100644 --- a/c/libuv/timer.go +++ b/c/libuv/timer.go @@ -1,8 +1,9 @@ package libuv import ( - "github.com/goplus/llgo/c" _ "unsafe" + + "github.com/goplus/llgo/c" ) // ---------------------------------------------- From 1b06948fb0e5541a57be993579173b31c464c227 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 26 Jul 2024 13:46:21 +0800 Subject: [PATCH 023/152] library: os, syscall --- internal/lib/os/wait_wait6.go | 32 ++++++++ internal/lib/os/wait_waitid.go | 55 ++++++++++++++ internal/lib/syscall/forkpipe2.go | 101 ++++++++++++++++++++++++++ internal/lib/syscall/syscall_linux.go | 59 +++++++++++++++ 4 files changed, 247 insertions(+) create mode 100644 internal/lib/os/wait_wait6.go create mode 100644 internal/lib/os/wait_waitid.go create mode 100644 internal/lib/syscall/forkpipe2.go diff --git a/internal/lib/os/wait_wait6.go b/internal/lib/os/wait_wait6.go new file mode 100644 index 00000000..994b8a60 --- /dev/null +++ b/internal/lib/os/wait_wait6.go @@ -0,0 +1,32 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build dragonfly || freebsd || netbsd + +package os + +import ( + "syscall" +) + +// blockUntilWaitable attempts to block until a call to p.Wait will +// succeed immediately, and reports whether it has done so. +// It does not actually call p.Wait. +func (p *Process) blockUntilWaitable() (bool, error) { + var errno syscall.Errno + for { + _, errno = wait6(_P_PID, p.Pid, syscall.WEXITED|syscall.WNOWAIT) + if errno != syscall.EINTR { + break + } + } + // TODO(xsw): + // runtime.KeepAlive(p) + if errno == syscall.ENOSYS { + return false, nil + } else if errno != 0 { + return false, NewSyscallError("wait6", errno) + } + return true, nil +} diff --git a/internal/lib/os/wait_waitid.go b/internal/lib/os/wait_waitid.go new file mode 100644 index 00000000..c14aba38 --- /dev/null +++ b/internal/lib/os/wait_waitid.go @@ -0,0 +1,55 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// We used to used this code for Darwin, but according to issue #19314 +// waitid returns if the process is stopped, even when using WEXITED. + +//go:build linux + +package os + +import ( + _ "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/syscall" +) + +const _P_PID = 1 + +// int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options); +// +//go:linkname waitid C.waitid +func waitid(idtype, id uintptr, infop *[16]uint64, options c.Int) c.Int + +// blockUntilWaitable attempts to block until a call to p.Wait will +// succeed immediately, and reports whether it has done so. +// It does not actually call p.Wait. +func (p *Process) blockUntilWaitable() (bool, error) { + // The waitid system call expects a pointer to a siginfo_t, + // which is 128 bytes on all Linux systems. + // On darwin/amd64, it requires 104 bytes. + // We don't care about the values it returns. + var siginfo [16]uint64 + psig := &siginfo[0] + var e syscall.Errno + for { + e = syscall.Errno(waitid(_P_PID, uintptr(p.Pid), psig, syscall.WEXITED|syscall.WNOWAIT, 0, 0)) + if e != syscall.EINTR { + break + } + } + // TODO(xsw): + // runtime.KeepAlive(p) + if e != 0 { + // waitid has been available since Linux 2.6.9, but + // reportedly is not available in Ubuntu on Windows. + // See issue 16610. + if e == syscall.ENOSYS { + return false, nil + } + return false, NewSyscallError("waitid", e) + } + return true, nil +} diff --git a/internal/lib/syscall/forkpipe2.go b/internal/lib/syscall/forkpipe2.go new file mode 100644 index 00000000..9a93b258 --- /dev/null +++ b/internal/lib/syscall/forkpipe2.go @@ -0,0 +1,101 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build dragonfly || freebsd || linux || netbsd || openbsd || solaris + +package syscall + +import "sync" + +// forkExecPipe atomically opens a pipe with O_CLOEXEC set on both file +// descriptors. +func forkExecPipe(p []int) error { + return Pipe2(p, O_CLOEXEC) +} + +var ( + // Guard the forking variable. + forkingLock sync.Mutex + // Number of goroutines currently forking, and thus the + // number of goroutines holding a conceptual write lock + // on ForkLock. + forking int +) + +// TODO(xsw): +// hasWaitingReaders reports whether any goroutine is waiting +// to acquire a read lock on rw. It is defined in the sync package. +func hasWaitingReaders(rw *sync.RWMutex) bool { + panic("todo: syscall.hasWaitingReaders in sync package") +} + +// acquireForkLock acquires a write lock on ForkLock. +// ForkLock is exported and we've promised that during a fork +// we will call ForkLock.Lock, so that no other threads create +// new fds that are not yet close-on-exec before we fork. +// But that forces all fork calls to be serialized, which is bad. +// But we haven't promised that serialization, and it is essentially +// undetectable by other users of ForkLock, which is good. +// Avoid the serialization by ensuring that ForkLock is locked +// at the first fork and unlocked when there are no more forks. +func acquireForkLock() { + forkingLock.Lock() + defer forkingLock.Unlock() + + if forking == 0 { + // There is no current write lock on ForkLock. + ForkLock.Lock() + forking++ + return + } + + // ForkLock is currently locked for writing. + + if hasWaitingReaders(&ForkLock) { + // ForkLock is locked for writing, and at least one + // goroutine is waiting to read from it. + // To avoid lock starvation, allow readers to proceed. + // The simple way to do this is for us to acquire a + // read lock. That will block us until all current + // conceptual write locks are released. + // + // Note that this case is unusual on modern systems + // with O_CLOEXEC and SOCK_CLOEXEC. On those systems + // the standard library should never take a read + // lock on ForkLock. + + forkingLock.Unlock() + + ForkLock.RLock() + ForkLock.RUnlock() + + forkingLock.Lock() + + // Readers got a chance, so now take the write lock. + + if forking == 0 { + ForkLock.Lock() + } + } + + forking++ +} + +// releaseForkLock releases the conceptual write lock on ForkLock +// acquired by acquireForkLock. +func releaseForkLock() { + forkingLock.Lock() + defer forkingLock.Unlock() + + if forking <= 0 { + panic("syscall.releaseForkLock: negative count") + } + + forking-- + + if forking == 0 { + // No more conceptual write locks. + ForkLock.Unlock() + } +} diff --git a/internal/lib/syscall/syscall_linux.go b/internal/lib/syscall/syscall_linux.go index a3e05ff2..cad706d3 100644 --- a/internal/lib/syscall/syscall_linux.go +++ b/internal/lib/syscall/syscall_linux.go @@ -11,6 +11,65 @@ package syscall +type WaitStatus uint32 + +// Wait status is 7 bits at bottom, either 0 (exited), +// 0x7F (stopped), or a signal number that caused an exit. +// The 0x80 bit is whether there was a core dump. +// An extra number (exit code, signal causing a stop) +// is in the high bits. At least that's the idea. +// There are various irregularities. For example, the +// "continued" status is 0xFFFF, distinguishing itself +// from stopped via the core dump bit. + +const ( + mask = 0x7F + core = 0x80 + exited = 0x00 + stopped = 0x7F + shift = 8 +) + +func (w WaitStatus) Exited() bool { return w&mask == exited } + +func (w WaitStatus) Signaled() bool { return w&mask != stopped && w&mask != exited } + +func (w WaitStatus) Stopped() bool { return w&0xFF == stopped } + +func (w WaitStatus) Continued() bool { return w == 0xFFFF } + +func (w WaitStatus) CoreDump() bool { return w.Signaled() && w&core != 0 } + +func (w WaitStatus) ExitStatus() int { + if !w.Exited() { + return -1 + } + return int(w>>shift) & 0xFF +} + +func (w WaitStatus) Signal() Signal { + if !w.Signaled() { + return -1 + } + return Signal(w & mask) +} + +func (w WaitStatus) StopSignal() Signal { + if !w.Stopped() { + return -1 + } + return Signal(w>>shift) & 0xFF +} + +/* TODO(xsw): +func (w WaitStatus) TrapCause() int { + if w.StopSignal() != SIGTRAP { + return -1 + } + return int(w>>shift) >> 8 +} +*/ + func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) { panic("todo: syscall.Faccessat") } From d3e84cbc4c0d35b7b93bc9bdde19932ac244e969 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 26 Jul 2024 13:47:22 +0800 Subject: [PATCH 024/152] ci --- .github/workflows/go.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index ecc8b535..1c1abb91 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -14,8 +14,8 @@ jobs: test: strategy: matrix: - # os: [macos-latest, ubuntu-latest] - os: [macos-latest] + os: [macos-latest, ubuntu-latest] + # os: [macos-latest] llvm: [18] runs-on: ${{ matrix.os }} steps: From 384e887fdb1c38dbc57fd08edd3472e3510be24b Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 26 Jul 2024 14:35:58 +0800 Subject: [PATCH 025/152] syscall (linux): waitid, pipe2 --- internal/lib/os/wait_waitid.go | 4 +-- internal/lib/syscall/forkpipe2.go | 8 ++++-- internal/lib/syscall/syscall_linux.go | 37 +++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/internal/lib/os/wait_waitid.go b/internal/lib/os/wait_waitid.go index c14aba38..0c89c81b 100644 --- a/internal/lib/os/wait_waitid.go +++ b/internal/lib/os/wait_waitid.go @@ -21,7 +21,7 @@ const _P_PID = 1 // int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options); // //go:linkname waitid C.waitid -func waitid(idtype, id uintptr, infop *[16]uint64, options c.Int) c.Int +func waitid(idtype, id uintptr, infop *uint64, options c.Int) c.Int // blockUntilWaitable attempts to block until a call to p.Wait will // succeed immediately, and reports whether it has done so. @@ -35,7 +35,7 @@ func (p *Process) blockUntilWaitable() (bool, error) { psig := &siginfo[0] var e syscall.Errno for { - e = syscall.Errno(waitid(_P_PID, uintptr(p.Pid), psig, syscall.WEXITED|syscall.WNOWAIT, 0, 0)) + e = syscall.Errno(waitid(_P_PID, uintptr(p.Pid), psig, syscall.WEXITED|syscall.WNOWAIT)) if e != syscall.EINTR { break } diff --git a/internal/lib/syscall/forkpipe2.go b/internal/lib/syscall/forkpipe2.go index 9a93b258..b5cd1d30 100644 --- a/internal/lib/syscall/forkpipe2.go +++ b/internal/lib/syscall/forkpipe2.go @@ -6,12 +6,16 @@ package syscall -import "sync" +import ( + "sync" + + "github.com/goplus/llgo/c/syscall" +) // forkExecPipe atomically opens a pipe with O_CLOEXEC set on both file // descriptors. func forkExecPipe(p []int) error { - return Pipe2(p, O_CLOEXEC) + return Pipe2(p, syscall.O_CLOEXEC) } var ( diff --git a/internal/lib/syscall/syscall_linux.go b/internal/lib/syscall/syscall_linux.go index cad706d3..3b643db2 100644 --- a/internal/lib/syscall/syscall_linux.go +++ b/internal/lib/syscall/syscall_linux.go @@ -11,6 +11,14 @@ package syscall +import ( + _ "unsafe" + + "github.com/goplus/llgo/c" +) + +// ----------------------------------------------------------------------------- + type WaitStatus uint32 // Wait status is 7 bits at bottom, either 0 (exited), @@ -70,6 +78,35 @@ func (w WaitStatus) TrapCause() int { } */ +// ----------------------------------------------------------------------------- + +// int pipe2(int pipefd[2], int flags); +// +//go:linkname pipe2 C.pipe2 +func pipe2(pipefd *[2]c.Int, flags c.Int) c.Int + +func Pipe(p []int) error { + return Pipe2(p, 0) +} + +func Pipe2(p []int, flags int) error { + if len(p) != 2 { + return EINVAL + } + var pp [2]c.Int + ret := pipe2(&pp, c.Int(flags)) + if ret == 0 { + p[0] = int(pp[0]) + p[1] = int(pp[1]) + return nil + } + return Errno(ret) +} + +// ----------------------------------------------------------------------------- + func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) { panic("todo: syscall.Faccessat") } + +// ----------------------------------------------------------------------------- From a3b23e348a13066cf013c2092b36cd244fea6d00 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 26 Jul 2024 14:49:21 +0800 Subject: [PATCH 026/152] library syscall (linux): Wait4 --- internal/lib/os/wait_waitid.go | 2 +- internal/lib/syscall/exec_libc.go | 6 ++++-- internal/lib/syscall/exec_libc2.go | 2 -- internal/lib/syscall/exec_linux.go | 6 ++++-- internal/lib/syscall/syscall_linux.go | 16 +++++++++++----- 5 files changed, 20 insertions(+), 12 deletions(-) diff --git a/internal/lib/os/wait_waitid.go b/internal/lib/os/wait_waitid.go index 0c89c81b..301254b3 100644 --- a/internal/lib/os/wait_waitid.go +++ b/internal/lib/os/wait_waitid.go @@ -10,10 +10,10 @@ package os import ( + "syscall" _ "unsafe" "github.com/goplus/llgo/c" - "github.com/goplus/llgo/c/syscall" ) const _P_PID = 1 diff --git a/internal/lib/syscall/exec_libc.go b/internal/lib/syscall/exec_libc.go index 44557867..1c38a19a 100644 --- a/internal/lib/syscall/exec_libc.go +++ b/internal/lib/syscall/exec_libc.go @@ -11,6 +11,8 @@ package syscall import ( "runtime" "unsafe" + + "github.com/goplus/llgo/c" ) type SysProcAttr struct { @@ -77,8 +79,8 @@ func init() { // split the stack, or acquire mutexes). We can't call RawSyscall // because it's not safe even for BSD-subsystem calls. // -//go:norace -func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) { +// func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) { +func forkAndExecInChild(argv0 *c.Char, argv, envv **c.Char, chroot, dir *c.Char, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) { // Declare all variables at top in case any // declarations require heap allocation (e.g., err1). var ( diff --git a/internal/lib/syscall/exec_libc2.go b/internal/lib/syscall/exec_libc2.go index 61b2c3b3..4c45b80b 100644 --- a/internal/lib/syscall/exec_libc2.go +++ b/internal/lib/syscall/exec_libc2.go @@ -52,8 +52,6 @@ func runtime_AfterForkInChild() // For the same reason compiler does not race instrument it. // The calls to rawSyscall are okay because they are assembly // functions that do not grow the stack. -// -//go:norace func forkAndExecInChild(argv0 *c.Char, argv, envv **c.Char, chroot, dir *c.Char, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err1 Errno) { // Declare all variables at top in case any // declarations require heap allocation (e.g., err1). diff --git a/internal/lib/syscall/exec_linux.go b/internal/lib/syscall/exec_linux.go index c66e820f..fb93fce3 100644 --- a/internal/lib/syscall/exec_linux.go +++ b/internal/lib/syscall/exec_linux.go @@ -6,6 +6,8 @@ package syscall +import "github.com/goplus/llgo/c" + // Linux unshare/clone/clone2/clone3 flags, architecture-independent, // copied from linux/sched.h. const ( @@ -119,8 +121,8 @@ func runtime_AfterForkInChild() // The calls to RawSyscall are okay because they are assembly // functions that do not grow the stack. // -//go:norace -func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) { +// func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) { +func forkAndExecInChild(argv0 *c.Char, argv, envv **c.Char, chroot, dir *c.Char, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) { /* TODO(xsw): // Set up and fork. This returns immediately in the parent or // if there's an error. diff --git a/internal/lib/syscall/syscall_linux.go b/internal/lib/syscall/syscall_linux.go index 3b643db2..7c4c2a59 100644 --- a/internal/lib/syscall/syscall_linux.go +++ b/internal/lib/syscall/syscall_linux.go @@ -15,6 +15,7 @@ import ( _ "unsafe" "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/syscall" ) // ----------------------------------------------------------------------------- @@ -78,6 +79,15 @@ func (w WaitStatus) TrapCause() int { } */ +func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error) { + var status c.Int + wpid, err = wait4(pid, &status, options, rusage) + if wstatus != nil { + *wstatus = WaitStatus(status) + } + return +} + // ----------------------------------------------------------------------------- // int pipe2(int pipefd[2], int flags); @@ -85,13 +95,9 @@ func (w WaitStatus) TrapCause() int { //go:linkname pipe2 C.pipe2 func pipe2(pipefd *[2]c.Int, flags c.Int) c.Int -func Pipe(p []int) error { - return Pipe2(p, 0) -} - func Pipe2(p []int, flags int) error { if len(p) != 2 { - return EINVAL + return Errno(syscall.EINVAL) } var pp [2]c.Int ret := pipe2(&pp, c.Int(flags)) From 49fabf23a8a8eb4775ac034e1cf4cbcaf81ef878 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 26 Jul 2024 14:51:17 +0800 Subject: [PATCH 027/152] x --- internal/lib/syscall/syscall_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/lib/syscall/syscall_linux.go b/internal/lib/syscall/syscall_linux.go index 7c4c2a59..17d9fe95 100644 --- a/internal/lib/syscall/syscall_linux.go +++ b/internal/lib/syscall/syscall_linux.go @@ -79,7 +79,7 @@ func (w WaitStatus) TrapCause() int { } */ -func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error) { +func Wait4(pid int, wstatus *WaitStatus, options int, rusage *syscall.Rusage) (wpid int, err error) { var status c.Int wpid, err = wait4(pid, &status, options, rusage) if wstatus != nil { From 666be94a71aa8c8a36ace9a8802f396ee9f1d799 Mon Sep 17 00:00:00 2001 From: fuxiaohei Date: Fri, 26 Jul 2024 14:53:27 +0800 Subject: [PATCH 028/152] fix describe mismatch in pthread.h --- c/pthread/sync/sync.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/c/pthread/sync/sync.go b/c/pthread/sync/sync.go index 33679a54..8c9ce5db 100644 --- a/c/pthread/sync/sync.go +++ b/c/pthread/sync/sync.go @@ -76,14 +76,18 @@ func (m *Mutex) Init(attr *MutexAttr) c.Int { return 0 } // llgo:link (*Mutex).Destroy C.pthread_mutex_destroy func (m *Mutex) Destroy() {} -// llgo:link (*Mutex).Lock C.pthread_mutex_lock -func (m *Mutex) Lock() {} +func (m *Mutex) Lock() { m.lockInternal() } + +// llgo:link (*Mutex).lockInternal C.pthread_mutex_lock +func (m *Mutex) lockInternal() c.Int { return 0 } // llgo:link (*Mutex).TryLock C.pthread_mutex_trylock func (m *Mutex) TryLock() c.Int { return 0 } -// llgo:link (*Mutex).Unlock C.pthread_mutex_unlock -func (m *Mutex) Unlock() {} +func (m *Mutex) Unlock() { m.unlockInternal() } + +// llgo:link (*Mutex).unlockInternal C.pthread_mutex_unlock +func (m *Mutex) unlockInternal() c.Int { return 0 } // ----------------------------------------------------------------------------- From 66a89a7d54c01f4f00f8a7cdfe355e483c1ab76b Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 26 Jul 2024 14:55:36 +0800 Subject: [PATCH 029/152] c/libuv: disable EFTYPE --- c/libuv/error.go | 152 +++++++++++++++++++++++------------------------ 1 file changed, 76 insertions(+), 76 deletions(-) diff --git a/c/libuv/error.go b/c/libuv/error.go index 309604e4..13f0029f 100644 --- a/c/libuv/error.go +++ b/c/libuv/error.go @@ -9,85 +9,85 @@ import ( ) const ( - E2BIG Errno = Errno(syscall.E2BIG) - EACCES Errno = Errno(syscall.EACCES) - EADDRINUSE Errno = Errno(syscall.EADDRINUSE) - EADDRNOTAVAIL Errno = Errno(syscall.EADDRNOTAVAIL) - EAFNOSUPPORT Errno = Errno(syscall.EAFNOSUPPORT) - EAGAIN Errno = Errno(syscall.EAGAIN) - EALREADY Errno = Errno(syscall.EALREADY) - EBADF Errno = Errno(syscall.EBADF) - EBUSY Errno = Errno(syscall.EBUSY) - ECANCELED Errno = Errno(syscall.ECANCELED) - ECONNABORTED Errno = Errno(syscall.ECONNABORTED) - ECONNREFUSED Errno = Errno(syscall.ECONNREFUSED) - ECONNRESET Errno = Errno(syscall.ECONNRESET) - EDESTADDRREQ Errno = Errno(syscall.EDESTADDRREQ) - EEXIST Errno = Errno(syscall.EEXIST) - EFAULT Errno = Errno(syscall.EFAULT) - EFBIG Errno = Errno(syscall.EFBIG) - EHOSTUNREACH Errno = Errno(syscall.EHOSTUNREACH) - EINTR Errno = Errno(syscall.EINTR) - EINVAL Errno = Errno(syscall.EINVAL) - EIO Errno = Errno(syscall.EIO) - EISCONN Errno = Errno(syscall.EISCONN) - EISDIR Errno = Errno(syscall.EISDIR) - ELOOP Errno = Errno(syscall.ELOOP) - EMFILE Errno = Errno(syscall.EMFILE) - EMSGSIZE Errno = Errno(syscall.EMSGSIZE) - ENAMETOOLONG Errno = Errno(syscall.ENAMETOOLONG) - ENETDOWN Errno = Errno(syscall.ENETDOWN) - ENETUNREACH Errno = Errno(syscall.ENETUNREACH) - ENFILE Errno = Errno(syscall.ENFILE) - ENOBUFS Errno = Errno(syscall.ENOBUFS) - ENODEV Errno = Errno(syscall.ENODEV) - ENOENT Errno = Errno(syscall.ENOENT) - ENOMEM Errno = Errno(syscall.ENOMEM) - ENOPROTOOPT Errno = Errno(syscall.ENOPROTOOPT) - ENOSPC Errno = Errno(syscall.ENOSPC) - ENOSYS Errno = Errno(syscall.ENOSYS) - ENOTCONN Errno = Errno(syscall.ENOTCONN) - ENOTDIR Errno = Errno(syscall.ENOTDIR) - ENOTEMPTY Errno = Errno(syscall.ENOTEMPTY) - ENOTSOCK Errno = Errno(syscall.ENOTSOCK) - ENOTSUP Errno = Errno(syscall.ENOTSUP) - EOVERFLOW Errno = Errno(syscall.EOVERFLOW) - EPERM Errno = Errno(syscall.EPERM) - EPIPE Errno = Errno(syscall.EPIPE) - EPROTO Errno = Errno(syscall.EPROTO) - EPROTONOSUPPORT Errno = Errno(syscall.EPROTONOSUPPORT) - EPROTOTYPE Errno = Errno(syscall.EPROTOTYPE) - ERANGE Errno = Errno(syscall.ERANGE) - EROFS Errno = Errno(syscall.EROFS) - ESHUTDOWN Errno = Errno(syscall.ESHUTDOWN) - ESPIPE Errno = Errno(syscall.ESPIPE) - ESRCH Errno = Errno(syscall.ESRCH) - ETIMEDOUT Errno = Errno(syscall.ETIMEDOUT) - ETXTBSY Errno = Errno(syscall.ETXTBSY) - EXDEV Errno = Errno(syscall.EXDEV) - ENXIO Errno = Errno(syscall.ENXIO) - EMLINK Errno = Errno(syscall.EMLINK) - EHOSTDOWN Errno = Errno(syscall.EHOSTDOWN) - ENOTTY Errno = Errno(syscall.ENOTTY) - EFTYPE Errno = Errno(syscall.EFTYPE) - EILSEQ Errno = Errno(syscall.EILSEQ) - ESOCKTNOSUPPORT Errno = Errno(syscall.ESOCKTNOSUPPORT) + E2BIG = Errno(syscall.E2BIG) + EACCES = Errno(syscall.EACCES) + EADDRINUSE = Errno(syscall.EADDRINUSE) + EADDRNOTAVAIL = Errno(syscall.EADDRNOTAVAIL) + EAFNOSUPPORT = Errno(syscall.EAFNOSUPPORT) + EAGAIN = Errno(syscall.EAGAIN) + EALREADY = Errno(syscall.EALREADY) + EBADF = Errno(syscall.EBADF) + EBUSY = Errno(syscall.EBUSY) + ECANCELED = Errno(syscall.ECANCELED) + ECONNABORTED = Errno(syscall.ECONNABORTED) + ECONNREFUSED = Errno(syscall.ECONNREFUSED) + ECONNRESET = Errno(syscall.ECONNRESET) + EDESTADDRREQ = Errno(syscall.EDESTADDRREQ) + EEXIST = Errno(syscall.EEXIST) + EFAULT = Errno(syscall.EFAULT) + EFBIG = Errno(syscall.EFBIG) + EHOSTUNREACH = Errno(syscall.EHOSTUNREACH) + EINTR = Errno(syscall.EINTR) + EINVAL = Errno(syscall.EINVAL) + EIO = Errno(syscall.EIO) + EISCONN = Errno(syscall.EISCONN) + EISDIR = Errno(syscall.EISDIR) + ELOOP = Errno(syscall.ELOOP) + EMFILE = Errno(syscall.EMFILE) + EMSGSIZE = Errno(syscall.EMSGSIZE) + ENAMETOOLONG = Errno(syscall.ENAMETOOLONG) + ENETDOWN = Errno(syscall.ENETDOWN) + ENETUNREACH = Errno(syscall.ENETUNREACH) + ENFILE = Errno(syscall.ENFILE) + ENOBUFS = Errno(syscall.ENOBUFS) + ENODEV = Errno(syscall.ENODEV) + ENOENT = Errno(syscall.ENOENT) + ENOMEM = Errno(syscall.ENOMEM) + ENOPROTOOPT = Errno(syscall.ENOPROTOOPT) + ENOSPC = Errno(syscall.ENOSPC) + ENOSYS = Errno(syscall.ENOSYS) + ENOTCONN = Errno(syscall.ENOTCONN) + ENOTDIR = Errno(syscall.ENOTDIR) + ENOTEMPTY = Errno(syscall.ENOTEMPTY) + ENOTSOCK = Errno(syscall.ENOTSOCK) + ENOTSUP = Errno(syscall.ENOTSUP) + EOVERFLOW = Errno(syscall.EOVERFLOW) + EPERM = Errno(syscall.EPERM) + EPIPE = Errno(syscall.EPIPE) + EPROTO = Errno(syscall.EPROTO) + EPROTONOSUPPORT = Errno(syscall.EPROTONOSUPPORT) + EPROTOTYPE = Errno(syscall.EPROTOTYPE) + ERANGE = Errno(syscall.ERANGE) + EROFS = Errno(syscall.EROFS) + ESHUTDOWN = Errno(syscall.ESHUTDOWN) + ESPIPE = Errno(syscall.ESPIPE) + ESRCH = Errno(syscall.ESRCH) + ETIMEDOUT = Errno(syscall.ETIMEDOUT) + ETXTBSY = Errno(syscall.ETXTBSY) + EXDEV = Errno(syscall.EXDEV) + ENXIO = Errno(syscall.ENXIO) + EMLINK = Errno(syscall.EMLINK) + EHOSTDOWN = Errno(syscall.EHOSTDOWN) + ENOTTY = Errno(syscall.ENOTTY) + //EFTYPE = Errno(syscall.EFTYPE) + EILSEQ = Errno(syscall.EILSEQ) + ESOCKTNOSUPPORT = Errno(syscall.ESOCKTNOSUPPORT) ) const ( - EAI_ADDRFAMILY Errno = Errno(net.EAI_ADDRFAMILY) - EAI_AGAIN Errno = Errno(net.EAI_AGAIN) - EAI_BADFLAGS Errno = Errno(net.EAI_BADFLAGS) - EAI_BADHINTS Errno = Errno(net.EAI_BADHINTS) - EAI_FAIL Errno = Errno(net.EAI_FAIL) - EAI_FAMILY Errno = Errno(net.EAI_FAMILY) - EAI_MEMORY Errno = Errno(net.EAI_MEMORY) - EAI_NODATA Errno = Errno(net.EAI_NODATA) - EAI_NONAME Errno = Errno(net.EAI_NONAME) - EAI_OVERFLOW Errno = Errno(net.EAI_OVERFLOW) - EAI_PROTOCOL Errno = Errno(net.EAI_PROTOCOL) - EAI_SERVICE Errno = Errno(net.EAI_SERVICE) - EAI_SOCKTYPE Errno = Errno(net.EAI_SOCKTYPE) + EAI_ADDRFAMILY = Errno(net.EAI_ADDRFAMILY) + EAI_AGAIN = Errno(net.EAI_AGAIN) + EAI_BADFLAGS = Errno(net.EAI_BADFLAGS) + EAI_BADHINTS = Errno(net.EAI_BADHINTS) + EAI_FAIL = Errno(net.EAI_FAIL) + EAI_FAMILY = Errno(net.EAI_FAMILY) + EAI_MEMORY = Errno(net.EAI_MEMORY) + EAI_NODATA = Errno(net.EAI_NODATA) + EAI_NONAME = Errno(net.EAI_NONAME) + EAI_OVERFLOW = Errno(net.EAI_OVERFLOW) + EAI_PROTOCOL = Errno(net.EAI_PROTOCOL) + EAI_SERVICE = Errno(net.EAI_SERVICE) + EAI_SOCKTYPE = Errno(net.EAI_SOCKTYPE) ) const ( From e764a2298d3060710bd713cb26e41c87e03042f9 Mon Sep 17 00:00:00 2001 From: morpingsss Date: Fri, 26 Jul 2024 16:14:41 +0800 Subject: [PATCH 030/152] docs(llgo/doc): documents fix --- chore/llcppg/design.md | 126 +++++++++++----------- doc/How-to-support-a-C&C++-Library.md | 145 ++++++++++++-------------- 2 files changed, 127 insertions(+), 144 deletions(-) diff --git a/chore/llcppg/design.md b/chore/llcppg/design.md index 7cecf46a..23523654 100644 --- a/chore/llcppg/design.md +++ b/chore/llcppg/design.md @@ -74,30 +74,31 @@ gogensig - # read AST from stdin ### Process -1. The Parsing Module reads `config.json` to obtain dynamic libraries, header files, and the package name. After parsing, it writes the generated `common_symbol_info.json` path into `config.json`. -2. The Function Declaration Generation Module reads `config.json` to get the package name, header files, and the previously generated `common_symbol_info.json`. After parsing, it generates the function prototype `func_prototype.json`. -3. Reads the previously generated `func_prototype.json`, stores it as a structure, and uses gogen to generate code based on the structure. +1. The Parsing Module reads `llcppg.cfg` to obtain dynamic libraries, header files, and the package name. After parsing, it writes the generated `llcppg.symb.json` path into `llcppg.cfg`. +2. The Function Declaration Generation Module reads `llcppg.cfg` to get the package name, header files, and the previously generated `llcppg.symb.json`. After parsing, it generates the function prototype `llcppg.function.json`. +3. Reads the previously generated `llcppg.information.json`, stores it as a structure, and uses gogen to generate code based on the structure. ## Parsing Module ### Input -Obtains the paths to header files and dynamic library files by reading the JSON file `config.json`. +Obtains the paths to header files and dynamic library files by reading the JSON file `llcppg.cfg`. ```json { - "PackageName": "inireader", - "HeaderFiles": [ - "/path/to/header/INIReader.h", - "/path/to/header/AnotherHeader.h" + "name": "inih", + "cflags": "$(pkg-config --cflags INIReader)", + "include": [ + "INIReader.h", + "AnotherHeaderFile.h" ], - "DLLFile": "/path/to/lib/libINIReader.dylib", - "JSONFile": "/path/to/json/config.json" + "libs": "$(pkg-config --libs INIReader)", + "trimPrefixes": ["Ini", "INI"] } ``` ```bash -./generate_symbol_table /path/to/config.json +llcppsymg config-file ``` ### Implementation Steps @@ -105,15 +106,15 @@ Obtains the paths to header files and dynamic library files by reading the JSON 1. Parse dylib and store: ```go -// common.go +// types.go type CPPSymbol struct { - Address string - Type string - Name string + Symbol string `json:"symbol"` + Type string `json:"type"` + Name string `json:"name"` } // parser_dylib.go -func ParseDylibSymbols(dylibPath string) ([]common.CPPSymbol, error) +func parseDylibSymbols(lib string) ([]common.CPPSymbol, error) ``` 2. Parse header files and store: @@ -121,48 +122,46 @@ func ParseDylibSymbols(dylibPath string) ([]common.CPPSymbol, error) ```go // common.go type ASTInformation struct { - Namespace string - Class string - Name string - BaseClasses []string - ReturnType string - Location string - Parameters []Parameter - Symbol string + Namespace string `json:"namespace"` + Class string `json:"class"` + Name string `json:"name"` + BaseClasses []string `json:"baseClasses"` + ReturnType string `json:"returnType"` + Location string `json:"location"` + Parameters []Parameter `json:"parameters"` + Symbol string `json:"symbol"` } type Parameter struct { - Name string - Type string + Name string `json:"name"` + Type string `json:"type"` } // parser_ast.go -func ParseHeaderFile(files []string) ([]common.ASTInformation, error) +func parseHeaderFile(config types.Config) ([]common.ASTInformation, error) ``` 3. Cross-reference data from the first two steps to get the final output ```go // common.go -type CommonSymbolInfo struct { - FunctionName string - Symbol string - Location string - UserFunctionName string +type SymbolInfo struct { + Mangle string `json:"mangle"` // C++ Symbol + CPP string `json:"c++"` // C++ function name + Go string `json:"go"` // Go function name } // common_symbols.go -func GetCommonSymbols(dylibSymbols []common.CPPSymbol, astInfoList []common.ASTInformation) []common.CommonSymbolInfo +func getCommonSymbols(dylibSymbols []common.CPPSymbol, astInfoList []common.ASTInformation) []common.SymbolInfo { ``` -4. Generate `common_symbol_info.json` file and store the JSON file path into `config.json` +4. Generate `llcppg.symb.json` file and store the JSON file path into `llcppg.cfg` ```go -// generator.go -func GenerateJSON([]CommonSymbolInfo) +func generateJSON([]CommonSymbolInfo) ``` -5. Example `common_symbol_info.json` file +5. Example `llcppg.symb.json` file ```json { @@ -177,14 +176,14 @@ func GenerateJSON([]CommonSymbolInfo) ### Input -No input required, directly reads the `config.json` file +No input required, directly reads the `llcppg.cfg` file ### Implementation Steps 1. Execute the executable ```bash -./generate_func_decl /path/to/config.json +llcppsigfetch config-file ``` 2. Parse header files @@ -192,18 +191,19 @@ No input required, directly reads the `config.json` file ```go // common.go type ASTInformation struct { - Namespace string - Class string - Name string - BaseClasses []string - ReturnType string - Location string - Parameters []Parameter + Namespace string `json:"namespace"` + Class string `json:"class"` + Name string `json:"name"` + BaseClasses []string `json:"baseClasses"` + ReturnType string `json:"returnType"` + Location string `json:"location"` + Parameters []Parameter `json:"parameters"` + Symbol string `json:"symbol"` } type Parameter struct { - Name string - Type string + Name string `json:"name"` + Type string `json:"type"` } // parser_ast.go @@ -218,19 +218,19 @@ func ParseHeaderFile(filePath string) ([]common.ASTInformation, error) ```json { - "FunctionName": "A::B::C", - "Symbol": "_ZN9INIReaderC1ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE", - "Location": "a.h", - "ReturnType" : "int", - "UserFunctionName": "CFromA", - "Parameters" : [ + "functionName": "A::B::C", + "symbol": "_ZN9INIReaderC1ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE", + "location": "a.h", + "returnType": "int", + "userFunctionName": "CFromA", + "parameters": [ { - "arg1" : "int" + "arg1": "int" }, { - "arg2" : "*char" + "arg2": "*char" } - ] + ] } ``` @@ -245,7 +245,7 @@ No input required, directly reads `func_prototype.json` file 1. Execute the executable ```bash -./generate_code /path/to/func_prototype.json +gogensig ast-file ``` 2. Parse JSON file @@ -253,11 +253,11 @@ No input required, directly reads `func_prototype.json` file ```go // common.go type HeaderFileInfo struct { - FunctionName string - Symbol string - Location string - UserFunctionName string - Parameters map[string]string + FunctionName string `json:"functionName"` + Symbol string `json:"symbol"` + Location string `json:"location"` + UserFunctionName string `json:"userFunctionName"` + Parameters map[string]string `json:"parameters"` } // parse_json.go diff --git a/doc/How-to-support-a-C&C++-Library.md b/doc/How-to-support-a-C&C++-Library.md index 6c462b2d..fc2e71d0 100644 --- a/doc/How-to-support-a-C&C++-Library.md +++ b/doc/How-to-support-a-C&C++-Library.md @@ -1,5 +1,3 @@ -# How to support a C/C++ Library - # Support a C Library ## Install a C Library @@ -14,7 +12,7 @@ brew install inih 1. On macOS, use `nm -gU libbar.dylib` to parse C-style symbols -```jsx +```bash 0000000000003e55 T _ini_parse ``` @@ -26,14 +24,14 @@ int ini_parse(const char* filename, ini_handler handler, void* user); 3. Create the corresponding Go file - ```c - inih/ - ├── _demo - ├── inih_demo - ├──inih_demo.go - └── inih.go +```c +inih/ +├── _demo + ├── inih_demo + ├──inih_demo.go +└── inih.go +``` - ``` 4. In `inih.go`, use LLGoPackage to specify the location of the third-party library so that llgo can link to the third-party library. Both `pkg-config --libs inih` and `linih` are used to specify the location of the third-party library. ```go @@ -50,42 +48,42 @@ const ( 5. Write the corresponding function in `inih.go` - Note that the basic C function type mapping to Go function type can be found at [https://github.com/goplus/llgo/blob/main/doc/Type-Mapping-between-C-and-Go.md](https://github.com/goplus/llgo/blob/main/doc/Type-Mapping-between-C-and-Go.md). Some types requiring special handling are listed at the end of this document for reference. +Note that the basic C function type mapping to Go function type can be found at [https://github.com/goplus/llgo/blob/main/doc/Type-Mapping-between-C-and-Go.md](https://github.com/goplus/llgo/blob/main/doc/Type-Mapping-between-C-and-Go.md). Some types requiring special handling are listed at the end of this document for reference. - ```go - //go:linkname Parse C.ini_parse - func Parse(filename *c.Char, handler func(user c.Pointer, section *c.Char, name *c.Char, value *c.Char) c.Int, user c.Pointer) c.Int - ``` +```go +//go:linkname Parse C.ini_parse +func Parse(filename *c.Char, handler func(user c.Pointer, section *c.Char, name *c.Char, value *c.Char) c.Int, user c.Pointer) c.Int +``` 6. Write the function call in `inih_demo.go` - ```go - package main +```go +package main - import ( - "github.com/goplus/llgo/c" - "github.com/goplus/llgo/cpp/inih" - ) +import ( +"github.com/goplus/llgo/c" +"github.com/goplus/llgo/cpp/inih" +) - func main() { - filename := c.Str("path/to/yourIniFile") +func main() { +filename := c.Str("path/to/yourIniFile") - if inih.Parse(filename, func(user c.Pointer, section *c.Char, name *c.Char, value *c.Char) c.Int { - println("section:", c.GoString(section), "name:", c.GoString(name), "value:", c.GoString(value)) - return 1 - }, nil) < 0 { - println("Error parsing config file") - return - } - } - - ``` +if inih.Parse(filename, func(user c.Pointer, section *c.Char, name *c.Char, value *c.Char) c.Int { + println("section:", c.GoString(section), "name:", c.GoString(name), "value:", c.GoString(value)) + return 1 + }, nil) < 0 { + println("Error parsing config file") + return + } +} +``` + 7. Use llgo to run the demo - ```bash - cd inih/_demo/inih_demo - llgo run . - ``` +```bash +cd inih/_demo/inih_demo +llgo run . +``` ## Handling Special Types @@ -106,15 +104,18 @@ typedef enum { BLEND_CUSTOM_SEPARATE // Blend textures using custom rgb/alpha separate src/dst factors (use rlSetBlendFactorsSeparate()) } BlendMode; */ + +type BlendMode c.Int + const ( - BLEND_ALPHA BlendMode = iota // Blend textures considering alpha (default) - BLEND_ADDITIVE // Blend textures adding colors - BLEND_MULTIPLIED // Blend textures multiplying colors - BLEND_ADD_COLORS // Blend textures adding colors (alternative) - BLEND_SUBTRACT_COLORS // Blend textures subtracting colors (alternative) - BLEND_ALPHA_PREMULTIPLY // Blend premultiplied textures considering alpha - BLEND_CUSTOM // Blend textures using custom src/dst factors (use rlSetBlendFactors()) - BLEND_CUSTOM_SEPARATE // Blend textures using custom rgb/alpha separate src/dst factors (use rlSetBlendFactorsSeparate()) + BLEND_ALPHA BlendMode = iota // Blend textures considering alpha (default) + BLEND_ADDITIVE // Blend textures adding colors + BLEND_MULTIPLIED // Blend textures multiplying colors + BLEND_ADD_COLORS // Blend textures adding colors (alternative) + BLEND_SUBTRACT_COLORS // Blend textures subtracting colors (alternative) + BLEND_ALPHA_PREMULTIPLY // Blend premultiplied textures considering alpha + BLEND_CUSTOM // Blend textures using custom src/dst factors (use rlSetBlendFactors()) + BLEND_CUSTOM_SEPARATE // Blend textures using custom rgb/alpha separate src/dst factors (use rlSetBlendFactorsSeparate()) ) ``` @@ -133,14 +134,14 @@ typedef struct Vector4 { */ type Vector4 struct { - X float32 // Vector x component - Y float32 // Vector y component - Z float32 // Vector z component - W float32 // Vector w component + X float32 // Vector x component + Y float32 // Vector y component + Z float32 // Vector z component + W float32 // Vector w component } // If class member variables don't need to be exposed, like llgo/c/cjson, wrap functions that use these member variables as methods of the class. Example: -// + /* typedef struct cJSON { @@ -167,11 +168,10 @@ typedef struct cJSON // llgo:type C type JSON struct { - Unused [0]byte + Unused [0]byte } // llgo:link (*JSON).AddItem C.cJSON_AddItemToArray func (o *JSON) AddItem(item *JSON) c.Int { return 0 } - ``` For the size of Unused, if the methods bound to the structure do not need to create objects, i.e., the receiver of the Go methods bound to this structure is of pointer type, you can declare `Unused [0]byte`. Otherwise, you need to write a simple C file using the `sizeof` operator to calculate the size of the structure. Assuming the structure size is 38 bytes, then declare `Unused [38]byte`. @@ -209,14 +209,13 @@ import ( ) func main() { - strings := make([]*c.Char, 4) - strings[0] = c.Str("hello") - strings[1] = c.Str("world") - strings[2] = c.Str("ni") - strings[3] = c.Str("hao") - ptrtest.PrintStrings(unsafe.SliceData(strings), c.Int(4)) + strings := make([]*c.Char, 4) + strings[0] = c.Str("hello") + strings[1] = c.Str("world") + strings[2] = c.Str("ni") + strings[3] = c.Str("hao") + ptrtest.PrintStrings(unsafe.SliceData(strings), c.Int(4)) } - ``` # LLGO for C++ Third-Party Libraries @@ -241,7 +240,6 @@ inih/ ├── _wrap/cpp_wrap.cpp (optional) └── inih.go └── reader.go - ``` ## Writing Go Files to Link Library Functions @@ -255,28 +253,24 @@ Ordinary functions can be directly linked using the corresponding symbol in the ```bash nm -gU $(brew --prefix inih)/lib/libINIReader.dylib > output.txt c++filt symbol.txt - ``` Function prototype ```cpp int ParseError() const; - ``` Example of `symbol.txt` ```bash 0000000000002992 T INIReader::ParseError() const - ``` Example of `output.txt` ```bash 0000000000002992 T __ZNK9INIReader10ParseErrorEv - ``` Find the offset of the function you want to use in `symbol.txt`, then go back to `output.txt` and find the symbol corresponding to that offset. @@ -287,7 +281,6 @@ For functions, generally use `go:linkname` to link. Here, refer to the migration // The inih library currently does not involve ordinary functions, this is for demonstration purposes only and is not needed for migrating inih //go:linkname ParseError C.__ZNK9INIReader10ParseErrorEv func ParseError() c.Int - ``` ### Migrating Classes @@ -299,7 +292,6 @@ func ParseError() c.Int type Reader struct { Unused [32]byte } - ``` - Constructor @@ -323,18 +315,16 @@ func ParseError() c.Int // llgo:link (*Reader).InitFromBuffer C._ZN9INIReaderC1EPKcm func (r *Reader) InitFromBuffer(buffer *c.Char, bufferSize uintptr) {} - ``` - Constructor is not explicitly declared in the class (cannot find the corresponding symbol in the dynamic library) If the destructor is not explicitly declared in the source code, the compiler will automatically generate a default destructor. Use `extern "C"` to wrap it in cppWrap.cpp: - ```jsx + ```c extern "C" void INIReaderInit(INIReader* r) { r->INIReader(); } - ``` Link in Go: @@ -342,7 +332,6 @@ func ParseError() c.Int ```go // llgo:link (*Reader).INIReaderInit C.INIReaderInit func (r *Reader) INIReaderInit() {} - ``` - Class Methods @@ -353,7 +342,6 @@ func ParseError() c.Int func (r *Reader) GetInteger(section *std.String, name *std.String, defaultValue c.Long) c.Long { return 0 } - ``` Template or inline methods of the class will be introduced in the next section. @@ -362,9 +350,8 @@ func ParseError() c.Int Similar to the constructor process, after creating the class, use `defer` to call it explicitly: ```go - reader := inih.NewReader(c.Str(buf), uintptr(len(buf))) - defer reader.Dispose() - + reader := inih.NewReader(c.Str(buf), uintptr(len(buf))) + defer reader.Dispose() ``` ### Templates and Inlines @@ -376,7 +363,6 @@ Templates or inlines do not generate symbols in dynamic libraries (dylib) (defau extern "C" void stdStringInitFromCStrLen(std::string* s, const char* cstr, size_t len) { new(s) std::string(cstr, len); } - ``` Then use LLGoFiles to link in Go: the writing of standard library's `LLGoFiles` and `LLGoPackage` is slightly different from third-party libraries. Using `std::string` and `spdlog` library as examples, inih does not involve this step: @@ -393,7 +379,6 @@ func (s *String) InitFromCStrLen(cstr *c.Char, n uintptr) {} const ( LLGoFiles = "$(pkg-config --cflags spdlog): cppWrap/cppWrap.cpp" LLGoPackage = "link: $(pkg-config --libs spdlog); -lspdlog -pthread -lfmt") - ``` ## Writing and Running the Demo @@ -409,9 +394,8 @@ import ( func demoFromBuffer() { buf := `[settings] -username=admin -timeout=100 -` + username=admin + timeout=100` reader := inih.NewReader(c.Str(buf), uintptr(len(buf))) defer reader.Dispose() @@ -441,7 +425,6 @@ func main() { demoFromBuffer() demoFromFile() } - ``` -Use `llgo run reader_demo.go` to run in the directory where the demo is written. +Use `llgo run .` to run in the directory where the demo is written. From 2227f83b1d924a6efa5daca19c27e741a5545d5e Mon Sep 17 00:00:00 2001 From: morpingsss Date: Fri, 26 Jul 2024 16:18:39 +0800 Subject: [PATCH 031/152] docs(llgo/chore/llcppg): Modify the name of the json generated by llcppsigfetch --- chore/llcppg/design.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chore/llcppg/design.md b/chore/llcppg/design.md index 23523654..9c6eda54 100644 --- a/chore/llcppg/design.md +++ b/chore/llcppg/design.md @@ -210,7 +210,7 @@ type Parameter struct { func ParseHeaderFile(filePath string) ([]common.ASTInformation, error) ``` -3. Generate the final JSON mapping file `func_prototype.json` +3. Generate the final JSON mapping file `llcppg.information.json` ```go func GenerateJSONFile(info []common.ASTInformation) @@ -238,7 +238,7 @@ func ParseHeaderFile(filePath string) ([]common.ASTInformation, error) ### Input -No input required, directly reads `func_prototype.json` file +No input required, directly reads `llcppg.information.json` file ### Implementation Steps From ae505111351c15af8839422cfb251c17115e3246 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 26 Jul 2024 16:31:53 +0800 Subject: [PATCH 032/152] cjson: FreeCStr --- c/cjson/_demo/mkjson/mkjson.go | 5 ++++- c/cjson/cjson.go | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/c/cjson/_demo/mkjson/mkjson.go b/c/cjson/_demo/mkjson/mkjson.go index e91db71f..c98c9311 100644 --- a/c/cjson/_demo/mkjson/mkjson.go +++ b/c/cjson/_demo/mkjson/mkjson.go @@ -22,6 +22,9 @@ func main() { mod.SetItem(c.Str("items"), syms) - c.Printf(c.Str("%s\n"), mod.CStr()) + cstr := mod.CStr() + c.Printf(c.Str("%s\n"), cstr) + cjson.FreeCStr(cstr) + mod.Delete() } diff --git a/c/cjson/cjson.go b/c/cjson/cjson.go index 56936469..21c0d1b0 100644 --- a/c/cjson/cjson.go +++ b/c/cjson/cjson.go @@ -133,3 +133,9 @@ func (o *JSON) PrintUnformatted() *c.Char { return nil } // // llgo:link (*JSON).PrintBuffered C.cJSON_PrintBuffered func (o *JSON) PrintBuffered(prebuffer c.Int, fmt c.Int) *c.Char { return nil } + +//go:linkname Free C.cJSON_free +func Free(ptr unsafe.Pointer) + +//go:linkname FreeCStr C.cJSON_free +func FreeCStr(*c.Char) From e027872f50f67ba3833ad72fa38df3df4a34ab64 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 26 Jul 2024 16:43:51 +0800 Subject: [PATCH 033/152] cjson.ParseString demo --- c/cjson/_demo/mkjson/mkjson.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/c/cjson/_demo/mkjson/mkjson.go b/c/cjson/_demo/mkjson/mkjson.go index c98c9311..6361d48b 100644 --- a/c/cjson/_demo/mkjson/mkjson.go +++ b/c/cjson/_demo/mkjson/mkjson.go @@ -23,6 +23,19 @@ func main() { mod.SetItem(c.Str("items"), syms) cstr := mod.CStr() + str := c.GoString(cstr) + c.Printf(c.Str("%s\n"), cstr) + cjson.FreeCStr(cstr) + + mod.Delete() + + cjsonLoad(str) +} + +func cjsonLoad(str string) { + mod := cjson.ParseString(str) + + cstr := mod.Print() c.Printf(c.Str("%s\n"), cstr) cjson.FreeCStr(cstr) From c5d18d90463fecfad58f0b2244e35868243604d0 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 26 Jul 2024 16:49:23 +0800 Subject: [PATCH 034/152] pipedemo: todo --- _cmptest/_pipedemo/pipe.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 _cmptest/_pipedemo/pipe.go diff --git a/_cmptest/_pipedemo/pipe.go b/_cmptest/_pipedemo/pipe.go new file mode 100644 index 00000000..1d60a08c --- /dev/null +++ b/_cmptest/_pipedemo/pipe.go @@ -0,0 +1,26 @@ +package main + +import ( + "fmt" + "io" +) + +func main() { + data := []byte("This is some data that needs to be stored in Body.") + pr, pw := io.Pipe() + go func() { + defer pw.Close() + if _, err := pw.Write(data); err != nil { + fmt.Println("Error writing to pipe:", err) + return + } + }() + defer pr.Close() + + readData, err := io.ReadAll(pr) + if err != nil { + fmt.Println("Error reading from Body:", err) + return + } + fmt.Println("Body:", string(readData)) +} From bc584aa56e0681a19d8a466e27b33c9915246701 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 26 Jul 2024 18:01:55 +0800 Subject: [PATCH 035/152] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7dff6402..ac2d79b6 100644 --- a/README.md +++ b/README.md @@ -214,6 +214,7 @@ The currently supported libraries include: * [c/bdwgc](https://pkg.go.dev/github.com/goplus/llgo/c/bdwgc) * [c/cjson](https://pkg.go.dev/github.com/goplus/llgo/c/cjson) * [c/clang](https://pkg.go.dev/github.com/goplus/llgo/c/clang) +* [c/libuv](https://pkg.go.dev/github.com/goplus/llgo/c/libuv) * [c/llama2](https://pkg.go.dev/github.com/goplus/llgo/c/llama2) * [c/lua](https://pkg.go.dev/github.com/goplus/llgo/c/lua) * [c/neco](https://pkg.go.dev/github.com/goplus/llgo/c/neco) From 6923f0df2a2f4f9312d45e918e1ede5bd4a34866 Mon Sep 17 00:00:00 2001 From: hackerchai Date: Fri, 26 Jul 2024 19:01:53 +0800 Subject: [PATCH 036/152] fix(c/libuv): fix return type FsType Signed-off-by: hackerchai --- c/libuv/fs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c/libuv/fs.go b/c/libuv/fs.go index 42afbe8a..227d4a8c 100644 --- a/c/libuv/fs.go +++ b/c/libuv/fs.go @@ -112,7 +112,7 @@ type FsPollCb func(handle *FsPoll, status c.Int, events c.Int) /* Fs related function and method */ //go:linkname FsGetType C.uv_fs_get_type -func FsGetType(req *Fs) *FsType +func FsGetType(req *Fs) FsType //go:linkname FsGetPath C.uv_fs_get_path func FsGetPath(req *Fs) *c.Char From bc93bda1b7e26d8e02fa91e3ec5919efffb7f22a Mon Sep 17 00:00:00 2001 From: hackerchai Date: Fri, 26 Jul 2024 19:04:49 +0800 Subject: [PATCH 037/152] fix(c/libuv): rename UvFile into File & remove unused File struct Signed-off-by: hackerchai --- c/libuv/_demo/async_fs/async_fs.go | 4 ++-- c/libuv/fs.go | 27 +++++++++++---------------- c/libuv/libuv.go | 2 +- 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/c/libuv/_demo/async_fs/async_fs.go b/c/libuv/_demo/async_fs/async_fs.go index 6f715974..5c8b42e6 100644 --- a/c/libuv/_demo/async_fs/async_fs.go +++ b/c/libuv/_demo/async_fs/async_fs.go @@ -47,7 +47,7 @@ func onOpen(req *libuv.Fs) { // Init buffer iov = libuv.InitBuf((*c.Char)(unsafe.Pointer(&buffer[0])), c.Uint(unsafe.Sizeof(buffer))) // Read the file - readRes := libuv.FsRead(loop, &readReq, libuv.UvFile(libuv.FsGetResult(req)), &iov, 1, -1, onRead) + readRes := libuv.FsRead(loop, &readReq, libuv.File(libuv.FsGetResult(req)), &iov, 1, -1, onRead) if readRes != 0 { c.Printf(c.Str("Error in FsRead: %s (code: %d)\n"), libuv.Strerror(libuv.Errno(readRes)), readRes) libuv.LoopClose(loop) @@ -63,7 +63,7 @@ func onRead(req *libuv.Fs) { } else if libuv.FsGetResult(req) == 0 { c.Printf(c.Str("EOF\n")) // Close the file - closeRes := libuv.FsClose(loop, &closeReq, libuv.UvFile(libuv.FsGetResult(&openReq)), onClose) + closeRes := libuv.FsClose(loop, &closeReq, libuv.File(libuv.FsGetResult(&openReq)), onClose) if closeRes != 0 { // Print the content c.Printf(c.Str("Error in FsClose: %s (code: %d)\n"), libuv.Strerror(libuv.Errno(closeRes)), closeRes) diff --git a/c/libuv/fs.go b/c/libuv/fs.go index 227d4a8c..62a3c193 100644 --- a/c/libuv/fs.go +++ b/c/libuv/fs.go @@ -62,7 +62,7 @@ type FsType c.Int type DirentType c.Int -type UvFile c.Int +type File c.Int // ---------------------------------------------- @@ -85,11 +85,6 @@ type Dirent struct { Type DirentType } -type File struct { - Loop *Loop - Req *Fs -} - type Stat struct { Unused [0]byte } @@ -139,13 +134,13 @@ func DefaultLoop() *Loop func FsOpen(loop *Loop, req *Fs, path *c.Char, flags c.Int, mode c.Int, cb FsCb) c.Int //go:linkname FsClose C.uv_fs_close -func FsClose(loop *Loop, req *Fs, file UvFile, cb FsCb) c.Int +func FsClose(loop *Loop, req *Fs, file File, cb FsCb) c.Int //go:linkname FsRead C.uv_fs_read -func FsRead(loop *Loop, req *Fs, file UvFile, bufs *Buf, nbufs c.Uint, offset c.LongLong, cb FsCb) c.Int +func FsRead(loop *Loop, req *Fs, file File, bufs *Buf, nbufs c.Uint, offset c.LongLong, cb FsCb) c.Int //go:linkname FsWrite C.uv_fs_write -func FsWrite(loop *Loop, req *Fs, file UvFile, bufs *Buf, nbufs c.Uint, offset c.LongLong, cb FsCb) c.Int +func FsWrite(loop *Loop, req *Fs, file File, bufs *Buf, nbufs c.Uint, offset c.LongLong, cb FsCb) c.Int //go:linkname FsUnlink C.uv_fs_unlink func FsUnlink(loop *Loop, req *Fs, path *c.Char, cb FsCb) c.Int @@ -166,19 +161,19 @@ func FsRmdir(loop *Loop, req *Fs, path *c.Char, cb FsCb) c.Int func FsStat(loop *Loop, req *Fs, path *c.Char, cb FsCb) c.Int //go:linkname FsFstat C.uv_fs_fstat -func FsFstat(loop *Loop, req *Fs, file UvFile, cb FsCb) c.Int +func FsFstat(loop *Loop, req *Fs, file File, cb FsCb) c.Int //go:linkname FsRename C.uv_fs_rename func FsRename(loop *Loop, req *Fs, path *c.Char, newPath *c.Char, cb FsCb) c.Int //go:linkname FsFsync C.uv_fs_fsync -func FsFsync(loop *Loop, req *Fs, file UvFile, cb FsCb) c.Int +func FsFsync(loop *Loop, req *Fs, file File, cb FsCb) c.Int //go:linkname FsFdatasync C.uv_fs_fdatasync -func FsFdatasync(loop *Loop, req *Fs, file UvFile, cb FsCb) c.Int +func FsFdatasync(loop *Loop, req *Fs, file File, cb FsCb) c.Int //go:linkname FsFtruncate C.uv_fs_ftruncate -func FsFtruncate(loop *Loop, req *Fs, file UvFile, offset c.LongLong, cb FsCb) c.Int +func FsFtruncate(loop *Loop, req *Fs, file File, offset c.LongLong, cb FsCb) c.Int //go:linkname FsSendfile C.uv_fs_sendfile func FsSendfile(loop *Loop, req *Fs, outFd c.Int, inFd c.Int, inOffset c.LongLong, length c.Int, cb FsCb) c.Int @@ -190,13 +185,13 @@ func FsAccess(loop *Loop, req *Fs, path *c.Char, flags c.Int, cb FsCb) c.Int func FsChmod(loop *Loop, req *Fs, path *c.Char, mode c.Int, cb FsCb) c.Int //go:linkname FsFchmod C.uv_fs_fchmod -func FsFchmod(loop *Loop, req *Fs, file UvFile, mode c.Int, cb FsCb) c.Int +func FsFchmod(loop *Loop, req *Fs, file File, mode c.Int, cb FsCb) c.Int //go:linkname FsUtime C.uv_fs_utime func FsUtime(loop *Loop, req *Fs, path *c.Char, atime c.Int, mtime c.Int, cb FsCb) c.Int //go:linkname FsFutime C.uv_fs_futime -func FsFutime(loop *Loop, req *Fs, file UvFile, atime c.Int, mtime c.Int, cb FsCb) c.Int +func FsFutime(loop *Loop, req *Fs, file File, atime c.Int, mtime c.Int, cb FsCb) c.Int //go:linkname FsLutime C.uv_fs_lutime func FsLutime(loop *Loop, req *Fs, path *c.Char, atime c.Int, mtime c.Int, cb FsCb) c.Int @@ -238,7 +233,7 @@ func FsStatfs(loop *Loop, req *Fs, path *c.Char, cb FsCb) c.Int func FsChown(loop *Loop, req *Fs, path *c.Char, uid c.Int, gid c.Int, cb FsCb) c.Int //go:linkname FsFchown C.uv_fs_fchown -func FsFchown(loop *Loop, req *Fs, file UvFile, uid c.Int, gid c.Int, cb FsCb) c.Int +func FsFchown(loop *Loop, req *Fs, file File, uid c.Int, gid c.Int, cb FsCb) c.Int //go:linkname FsLchown C.uv_fs_lchown func FsLchown(loop *Loop, req *Fs, path *c.Char, uid c.Int, gid c.Int, cb FsCb) c.Int diff --git a/c/libuv/libuv.go b/c/libuv/libuv.go index ff1c7c78..47ae2573 100644 --- a/c/libuv/libuv.go +++ b/c/libuv/libuv.go @@ -297,7 +297,7 @@ func (handle *Handle) Fileno(fd *OsFd) c.Int { } //go:linkname UvPipe C.uv_pipe -func UvPipe(fds [2]UvFile, readFlags c.Int, writeFlags c.Int) c.Int { +func UvPipe(fds [2]File, readFlags c.Int, writeFlags c.Int) c.Int { return 0 } From 4eaf69dfa73a0653fb61bb32f0a0b7513cb55323 Mon Sep 17 00:00:00 2001 From: visualfc Date: Sat, 27 Jul 2024 06:58:40 +0800 Subject: [PATCH 038/152] internal/runtime: fix endSelect --- internal/runtime/z_chan.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/internal/runtime/z_chan.go b/internal/runtime/z_chan.go index 950c6096..fca39e59 100644 --- a/internal/runtime/z_chan.go +++ b/internal/runtime/z_chan.go @@ -239,6 +239,12 @@ func (p *selectOp) init() { p.sem = false } +func (p *selectOp) end() { + p.mutex.Destroy() + p.cond.Destroy() + p.next = nil +} + func (p *selectOp) notify() { p.mutex.Lock() p.sem = true @@ -299,6 +305,7 @@ func Select(ops ...ChanOp) (isel int, recvOK bool) { for _, op := range ops { endSelect(op.C, selOp) } + selOp.end() return } @@ -317,7 +324,6 @@ func endSelect(c *Chan, selOp *selectOp) { } *pp = selOp.next c.mutex.Unlock() - selOp.next = nil } // ----------------------------------------------------------------------------- From e69306a2ba01c3f9b992f979b7d6a0f6952a73f2 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sat, 27 Jul 2024 09:11:38 +0800 Subject: [PATCH 039/152] c/pthread/sync: use go:linkname for internal func --- c/pthread/sync/sync.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/c/pthread/sync/sync.go b/c/pthread/sync/sync.go index 8c9ce5db..233a5e98 100644 --- a/c/pthread/sync/sync.go +++ b/c/pthread/sync/sync.go @@ -76,18 +76,18 @@ func (m *Mutex) Init(attr *MutexAttr) c.Int { return 0 } // llgo:link (*Mutex).Destroy C.pthread_mutex_destroy func (m *Mutex) Destroy() {} -func (m *Mutex) Lock() { m.lockInternal() } - -// llgo:link (*Mutex).lockInternal C.pthread_mutex_lock -func (m *Mutex) lockInternal() c.Int { return 0 } - // llgo:link (*Mutex).TryLock C.pthread_mutex_trylock func (m *Mutex) TryLock() c.Int { return 0 } -func (m *Mutex) Unlock() { m.unlockInternal() } +func (m *Mutex) Lock() { lockInternal(m) } -// llgo:link (*Mutex).unlockInternal C.pthread_mutex_unlock -func (m *Mutex) unlockInternal() c.Int { return 0 } +func (m *Mutex) Unlock() { unlockInternal(m) } + +//go:linkname lockInternal C.pthread_mutex_lock +func lockInternal(m *Mutex) c.Int + +//go:linkname unlockInternal C.pthread_mutex_unlock +func unlockInternal(m *Mutex) c.Int // ----------------------------------------------------------------------------- From 2cd99943212841a9e976b2e2ae5588073003e537 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sat, 27 Jul 2024 09:28:09 +0800 Subject: [PATCH 040/152] C.wrap_pthread_mutex_lock --- c/pthread/sync/_pthd/pthd.c | 12 ++++++++++++ c/pthread/sync/sync.go | 12 ++++-------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/c/pthread/sync/_pthd/pthd.c b/c/pthread/sync/_pthd/pthd.c index e8275a54..f7bc7eb9 100644 --- a/c/pthread/sync/_pthd/pthd.c +++ b/c/pthread/sync/_pthd/pthd.c @@ -5,3 +5,15 @@ pthread_once_t llgoSyncOnceInitVal = PTHREAD_ONCE_INIT; // ----------------------------------------------------------------------------- + +// wrap return type to void +void wrap_pthread_mutex_lock(pthread_mutex_t *mutex) { + pthread_mutex_lock(mutex); +} + +// wrap return type to void +void wrap_pthread_mutex_unlock(pthread_mutex_t *mutex) { + pthread_mutex_unlock(mutex); +} + +// ----------------------------------------------------------------------------- diff --git a/c/pthread/sync/sync.go b/c/pthread/sync/sync.go index 233a5e98..b9e45d48 100644 --- a/c/pthread/sync/sync.go +++ b/c/pthread/sync/sync.go @@ -79,15 +79,11 @@ func (m *Mutex) Destroy() {} // llgo:link (*Mutex).TryLock C.pthread_mutex_trylock func (m *Mutex) TryLock() c.Int { return 0 } -func (m *Mutex) Lock() { lockInternal(m) } +// llgo:link (*Mutex).Lock C.wrap_pthread_mutex_lock +func (m *Mutex) Lock() {} -func (m *Mutex) Unlock() { unlockInternal(m) } - -//go:linkname lockInternal C.pthread_mutex_lock -func lockInternal(m *Mutex) c.Int - -//go:linkname unlockInternal C.pthread_mutex_unlock -func unlockInternal(m *Mutex) c.Int +// llgo:link (*Mutex).Unlock C.wrap_pthread_mutex_unlock +func (m *Mutex) Unlock() {} // ----------------------------------------------------------------------------- From aa55f4dceb5e3c95e04f1e0c427c2a85c5a999d7 Mon Sep 17 00:00:00 2001 From: visualfc Date: Sat, 27 Jul 2024 12:32:33 +0800 Subject: [PATCH 041/152] internal/build: add io.pipe for overlay --- internal/build/build.go | 4 - internal/build/overlay.go | 216 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 216 insertions(+), 4 deletions(-) create mode 100644 internal/build/overlay.go diff --git a/internal/build/build.go b/internal/build/build.go index a45a0251..1eafcb81 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -766,10 +766,6 @@ var hasAltPkg = map[string]none{ "runtime": {}, } -var overlayFiles = map[string]string{ - "math/exp_amd64.go": "package math;", -} - func check(err error) { if err != nil { panic(err) diff --git a/internal/build/overlay.go b/internal/build/overlay.go new file mode 100644 index 00000000..f8ad3039 --- /dev/null +++ b/internal/build/overlay.go @@ -0,0 +1,216 @@ +package build + +var overlayFiles = map[string]string{ + "math/exp_amd64.go": "package math;", + "io/pipe.go": data_io_pipe, +} + +// TODO fixed chan buffer and remove it. +var data_io_pipe = ` +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Pipe adapter to connect code expecting an io.Reader +// with code expecting an io.Writer. + +package io + +import ( + "errors" + "sync" +) + +// onceError is an object that will only store an error once. +type onceError struct { + sync.Mutex // guards following + err error +} + +func (a *onceError) Store(err error) { + a.Lock() + defer a.Unlock() + if a.err != nil { + return + } + a.err = err +} +func (a *onceError) Load() error { + a.Lock() + defer a.Unlock() + return a.err +} + +// ErrClosedPipe is the error used for read or write operations on a closed pipe. +var ErrClosedPipe = errors.New("io: read/write on closed pipe") + +// A pipe is the shared pipe structure underlying PipeReader and PipeWriter. +type pipe struct { + wrMu sync.Mutex // Serializes Write operations + wrCh chan []byte + rdCh chan int + + once sync.Once // Protects closing done + done chan struct{} + rerr onceError + werr onceError +} + +func (p *pipe) read(b []byte) (n int, err error) { + select { + case <-p.done: + return 0, p.readCloseError() + default: + } + + select { + case bw := <-p.wrCh: + nr := copy(b, bw) + p.rdCh <- nr + return nr, nil + case <-p.done: + return 0, p.readCloseError() + } +} + +func (p *pipe) closeRead(err error) error { + if err == nil { + err = ErrClosedPipe + } + p.rerr.Store(err) + p.once.Do(func() { close(p.done) }) + return nil +} + +func (p *pipe) write(b []byte) (n int, err error) { + select { + case <-p.done: + return 0, p.writeCloseError() + default: + p.wrMu.Lock() + defer p.wrMu.Unlock() + } + + for once := true; once || len(b) > 0; once = false { + select { + case p.wrCh <- b: + nw := <-p.rdCh + b = b[nw:] + n += nw + case <-p.done: + return n, p.writeCloseError() + } + } + return n, nil +} + +func (p *pipe) closeWrite(err error) error { + if err == nil { + err = EOF + } + p.werr.Store(err) + p.once.Do(func() { close(p.done) }) + return nil +} + +// readCloseError is considered internal to the pipe type. +func (p *pipe) readCloseError() error { + rerr := p.rerr.Load() + if werr := p.werr.Load(); rerr == nil && werr != nil { + return werr + } + return ErrClosedPipe +} + +// writeCloseError is considered internal to the pipe type. +func (p *pipe) writeCloseError() error { + werr := p.werr.Load() + if rerr := p.rerr.Load(); werr == nil && rerr != nil { + return rerr + } + return ErrClosedPipe +} + +// A PipeReader is the read half of a pipe. +type PipeReader struct { + p *pipe +} + +// Read implements the standard Read interface: +// it reads data from the pipe, blocking until a writer +// arrives or the write end is closed. +// If the write end is closed with an error, that error is +// returned as err; otherwise err is EOF. +func (r *PipeReader) Read(data []byte) (n int, err error) { + return r.p.read(data) +} + +// Close closes the reader; subsequent writes to the +// write half of the pipe will return the error ErrClosedPipe. +func (r *PipeReader) Close() error { + return r.CloseWithError(nil) +} + +// CloseWithError closes the reader; subsequent writes +// to the write half of the pipe will return the error err. +// +// CloseWithError never overwrites the previous error if it exists +// and always returns nil. +func (r *PipeReader) CloseWithError(err error) error { + return r.p.closeRead(err) +} + +// A PipeWriter is the write half of a pipe. +type PipeWriter struct { + p *pipe +} + +// Write implements the standard Write interface: +// it writes data to the pipe, blocking until one or more readers +// have consumed all the data or the read end is closed. +// If the read end is closed with an error, that err is +// returned as err; otherwise err is ErrClosedPipe. +func (w *PipeWriter) Write(data []byte) (n int, err error) { + return w.p.write(data) +} + +// Close closes the writer; subsequent reads from the +// read half of the pipe will return no bytes and EOF. +func (w *PipeWriter) Close() error { + return w.CloseWithError(nil) +} + +// CloseWithError closes the writer; subsequent reads from the +// read half of the pipe will return no bytes and the error err, +// or EOF if err is nil. +// +// CloseWithError never overwrites the previous error if it exists +// and always returns nil. +func (w *PipeWriter) CloseWithError(err error) error { + return w.p.closeWrite(err) +} + +// Pipe creates a synchronous in-memory pipe. +// It can be used to connect code expecting an io.Reader +// with code expecting an io.Writer. +// +// Reads and Writes on the pipe are matched one to one +// except when multiple Reads are needed to consume a single Write. +// That is, each Write to the PipeWriter blocks until it has satisfied +// one or more Reads from the PipeReader that fully consume +// the written data. +// The data is copied directly from the Write to the corresponding +// Read (or Reads); there is no internal buffering. +// +// It is safe to call Read and Write in parallel with each other or with Close. +// Parallel calls to Read and parallel calls to Write are also safe: +// the individual calls will be gated sequentially. +func Pipe() (*PipeReader, *PipeWriter) { + p := &pipe{ + wrCh: make(chan []byte, 1), + rdCh: make(chan int, 1), + done: make(chan struct{}, 1), + } + return &PipeReader{p}, &PipeWriter{p} +} +` From bf773df099dea18528a5227fd68d0b83a3aa21bd Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sat, 27 Jul 2024 16:03:22 +0800 Subject: [PATCH 042/152] _cmptest: pipedemo --- _cmptest/{_pipedemo => pipedemo}/pipe.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename _cmptest/{_pipedemo => pipedemo}/pipe.go (100%) diff --git a/_cmptest/_pipedemo/pipe.go b/_cmptest/pipedemo/pipe.go similarity index 100% rename from _cmptest/_pipedemo/pipe.go rename to _cmptest/pipedemo/pipe.go From 4d14fbb1e741ad4e070f68c8e307677f3e0314be Mon Sep 17 00:00:00 2001 From: visualfc Date: Sat, 27 Jul 2024 21:18:33 +0800 Subject: [PATCH 043/152] cl: after init check hasPatch --- cl/compile.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/cl/compile.go b/cl/compile.go index cd3acbfd..91694861 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -25,6 +25,7 @@ import ( "log" "os" "sort" + "strings" "github.com/goplus/llgo/cl/blocks" "github.com/goplus/llgo/internal/typepatch" @@ -288,9 +289,14 @@ func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, n int, do instrs = instrs[:last] } else { // TODO(xsw): confirm pyMod don't need to call AfterInit - p.inits = append(p.inits, func() { + after := func() { pkg.AfterInit(b, ret) - }) + } + if strings.HasSuffix(fn.Name(), "$hasPatch") { + p.inits[len(p.inits)-1] = after + } else { + p.inits = append(p.inits, after) + } } } else if doMainInit { argc := pkg.NewVar("__llgo_argc", types.NewPointer(types.Typ[types.Int32]), llssa.InC) From 9d9e998d498055c129ec57daf76cb6ce73a609e8 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sun, 28 Jul 2024 09:29:08 +0800 Subject: [PATCH 044/152] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ac2d79b6..4f94d58a 100644 --- a/README.md +++ b/README.md @@ -243,7 +243,7 @@ All Go syntax (not including `cgo`) is already supported. Here are some examples * [goroutine](_demo/goroutine/goroutine.go): goroutine demo -## Defer +### Defer LLGo `defer` does not support usage in loops. This is not a bug but a feature, because we think that using `defer` in a loop is a very unrecommended practice. From c63a1978cb873df0a9505a8fbffe524a44fb46b0 Mon Sep 17 00:00:00 2001 From: visualfc Date: Sun, 28 Jul 2024 11:29:22 +0800 Subject: [PATCH 045/152] cl: afterInit skip pkgHasPatch --- cl/compile.go | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/cl/compile.go b/cl/compile.go index 91694861..e50eb478 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -25,7 +25,6 @@ import ( "log" "os" "sort" - "strings" "github.com/goplus/llgo/cl/blocks" "github.com/goplus/llgo/internal/typepatch" @@ -287,16 +286,11 @@ func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, n int, do if pyModInit = p.pyMod != ""; pyModInit { last = len(instrs) - 1 instrs = instrs[:last] - } else { + } else if p.state != pkgHasPatch { // TODO(xsw): confirm pyMod don't need to call AfterInit - after := func() { + p.inits = append(p.inits, func() { pkg.AfterInit(b, ret) - } - if strings.HasSuffix(fn.Name(), "$hasPatch") { - p.inits[len(p.inits)-1] = after - } else { - p.inits = append(p.inits, after) - } + }) } } else if doMainInit { argc := pkg.NewVar("__llgo_argc", types.NewPointer(types.Typ[types.Int32]), llssa.InC) From 2986426251ce3504ff6a7738ef8b530a0e042d53 Mon Sep 17 00:00:00 2001 From: visualfc Date: Sun, 28 Jul 2024 16:52:03 +0800 Subject: [PATCH 046/152] cl: fix pkg init hasPatch --- cl/compile.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cl/compile.go b/cl/compile.go index e50eb478..b4000fbc 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -195,6 +195,8 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun isInit := (f.Name() == "init" && sig.Recv() == nil) if isInit && state == pkgHasPatch { name = initFnNameOfHasPatch(name) + block := f.Blocks[0].Instrs[1].(*ssa.If).Block() + block.Succs[0], block.Succs[1] = block.Succs[1], block.Succs[0] } fn := pkg.FuncOf(name) From 6c7db7ad23833ffab0f3ad6a15759aa68d133e09 Mon Sep 17 00:00:00 2001 From: visualfc Date: Sun, 28 Jul 2024 16:52:42 +0800 Subject: [PATCH 047/152] build: patch io.pipe --- cl/compile.go | 1 + internal/build/build.go | 1 + internal/build/overlay.go | 211 -------------------------------------- internal/lib/io/pipe.go | 207 +++++++++++++++++++++++++++++++++++++ 4 files changed, 209 insertions(+), 211 deletions(-) create mode 100644 internal/lib/io/pipe.go diff --git a/cl/compile.go b/cl/compile.go index b4000fbc..29f2dc54 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -195,6 +195,7 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun isInit := (f.Name() == "init" && sig.Recv() == nil) if isInit && state == pkgHasPatch { name = initFnNameOfHasPatch(name) + // pkg.init$guard has been set, change ssa.if-cond block := f.Blocks[0].Instrs[1].(*ssa.If).Block() block.Succs[0], block.Succs[1] = block.Succs[1], block.Succs[0] } diff --git a/internal/build/build.go b/internal/build/build.go index 1eafcb81..ba27fc65 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -764,6 +764,7 @@ var hasAltPkg = map[string]none{ "os": {}, "os/exec": {}, "runtime": {}, + "io": {}, } func check(err error) { diff --git a/internal/build/overlay.go b/internal/build/overlay.go index f8ad3039..19f39f24 100644 --- a/internal/build/overlay.go +++ b/internal/build/overlay.go @@ -2,215 +2,4 @@ package build var overlayFiles = map[string]string{ "math/exp_amd64.go": "package math;", - "io/pipe.go": data_io_pipe, } - -// TODO fixed chan buffer and remove it. -var data_io_pipe = ` -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Pipe adapter to connect code expecting an io.Reader -// with code expecting an io.Writer. - -package io - -import ( - "errors" - "sync" -) - -// onceError is an object that will only store an error once. -type onceError struct { - sync.Mutex // guards following - err error -} - -func (a *onceError) Store(err error) { - a.Lock() - defer a.Unlock() - if a.err != nil { - return - } - a.err = err -} -func (a *onceError) Load() error { - a.Lock() - defer a.Unlock() - return a.err -} - -// ErrClosedPipe is the error used for read or write operations on a closed pipe. -var ErrClosedPipe = errors.New("io: read/write on closed pipe") - -// A pipe is the shared pipe structure underlying PipeReader and PipeWriter. -type pipe struct { - wrMu sync.Mutex // Serializes Write operations - wrCh chan []byte - rdCh chan int - - once sync.Once // Protects closing done - done chan struct{} - rerr onceError - werr onceError -} - -func (p *pipe) read(b []byte) (n int, err error) { - select { - case <-p.done: - return 0, p.readCloseError() - default: - } - - select { - case bw := <-p.wrCh: - nr := copy(b, bw) - p.rdCh <- nr - return nr, nil - case <-p.done: - return 0, p.readCloseError() - } -} - -func (p *pipe) closeRead(err error) error { - if err == nil { - err = ErrClosedPipe - } - p.rerr.Store(err) - p.once.Do(func() { close(p.done) }) - return nil -} - -func (p *pipe) write(b []byte) (n int, err error) { - select { - case <-p.done: - return 0, p.writeCloseError() - default: - p.wrMu.Lock() - defer p.wrMu.Unlock() - } - - for once := true; once || len(b) > 0; once = false { - select { - case p.wrCh <- b: - nw := <-p.rdCh - b = b[nw:] - n += nw - case <-p.done: - return n, p.writeCloseError() - } - } - return n, nil -} - -func (p *pipe) closeWrite(err error) error { - if err == nil { - err = EOF - } - p.werr.Store(err) - p.once.Do(func() { close(p.done) }) - return nil -} - -// readCloseError is considered internal to the pipe type. -func (p *pipe) readCloseError() error { - rerr := p.rerr.Load() - if werr := p.werr.Load(); rerr == nil && werr != nil { - return werr - } - return ErrClosedPipe -} - -// writeCloseError is considered internal to the pipe type. -func (p *pipe) writeCloseError() error { - werr := p.werr.Load() - if rerr := p.rerr.Load(); werr == nil && rerr != nil { - return rerr - } - return ErrClosedPipe -} - -// A PipeReader is the read half of a pipe. -type PipeReader struct { - p *pipe -} - -// Read implements the standard Read interface: -// it reads data from the pipe, blocking until a writer -// arrives or the write end is closed. -// If the write end is closed with an error, that error is -// returned as err; otherwise err is EOF. -func (r *PipeReader) Read(data []byte) (n int, err error) { - return r.p.read(data) -} - -// Close closes the reader; subsequent writes to the -// write half of the pipe will return the error ErrClosedPipe. -func (r *PipeReader) Close() error { - return r.CloseWithError(nil) -} - -// CloseWithError closes the reader; subsequent writes -// to the write half of the pipe will return the error err. -// -// CloseWithError never overwrites the previous error if it exists -// and always returns nil. -func (r *PipeReader) CloseWithError(err error) error { - return r.p.closeRead(err) -} - -// A PipeWriter is the write half of a pipe. -type PipeWriter struct { - p *pipe -} - -// Write implements the standard Write interface: -// it writes data to the pipe, blocking until one or more readers -// have consumed all the data or the read end is closed. -// If the read end is closed with an error, that err is -// returned as err; otherwise err is ErrClosedPipe. -func (w *PipeWriter) Write(data []byte) (n int, err error) { - return w.p.write(data) -} - -// Close closes the writer; subsequent reads from the -// read half of the pipe will return no bytes and EOF. -func (w *PipeWriter) Close() error { - return w.CloseWithError(nil) -} - -// CloseWithError closes the writer; subsequent reads from the -// read half of the pipe will return no bytes and the error err, -// or EOF if err is nil. -// -// CloseWithError never overwrites the previous error if it exists -// and always returns nil. -func (w *PipeWriter) CloseWithError(err error) error { - return w.p.closeWrite(err) -} - -// Pipe creates a synchronous in-memory pipe. -// It can be used to connect code expecting an io.Reader -// with code expecting an io.Writer. -// -// Reads and Writes on the pipe are matched one to one -// except when multiple Reads are needed to consume a single Write. -// That is, each Write to the PipeWriter blocks until it has satisfied -// one or more Reads from the PipeReader that fully consume -// the written data. -// The data is copied directly from the Write to the corresponding -// Read (or Reads); there is no internal buffering. -// -// It is safe to call Read and Write in parallel with each other or with Close. -// Parallel calls to Read and parallel calls to Write are also safe: -// the individual calls will be gated sequentially. -func Pipe() (*PipeReader, *PipeWriter) { - p := &pipe{ - wrCh: make(chan []byte, 1), - rdCh: make(chan int, 1), - done: make(chan struct{}, 1), - } - return &PipeReader{p}, &PipeWriter{p} -} -` diff --git a/internal/lib/io/pipe.go b/internal/lib/io/pipe.go new file mode 100644 index 00000000..b3ca0570 --- /dev/null +++ b/internal/lib/io/pipe.go @@ -0,0 +1,207 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Pipe adapter to connect code expecting an io.Reader +// with code expecting an io.Writer. + +package io + +import ( + "errors" + "io" + "sync" +) + +// onceError is an object that will only store an error once. +type onceError struct { + sync.Mutex // guards following + err error +} + +func (a *onceError) Store(err error) { + a.Lock() + defer a.Unlock() + if a.err != nil { + return + } + a.err = err +} +func (a *onceError) Load() error { + a.Lock() + defer a.Unlock() + return a.err +} + +// ErrClosedPipe is the error used for read or write operations on a closed pipe. +var ErrClosedPipe = errors.New("io: read/write on closed pipe") + +// A pipe is the shared pipe structure underlying PipeReader and PipeWriter. +type pipe struct { + wrMu sync.Mutex // Serializes Write operations + wrCh chan []byte + rdCh chan int + + once sync.Once // Protects closing done + done chan struct{} + rerr onceError + werr onceError +} + +func (p *pipe) read(b []byte) (n int, err error) { + select { + case <-p.done: + return 0, p.readCloseError() + default: + } + + select { + case bw := <-p.wrCh: + nr := copy(b, bw) + p.rdCh <- nr + return nr, nil + case <-p.done: + return 0, p.readCloseError() + } +} + +func (p *pipe) closeRead(err error) error { + if err == nil { + err = ErrClosedPipe + } + p.rerr.Store(err) + p.once.Do(func() { close(p.done) }) + return nil +} + +func (p *pipe) write(b []byte) (n int, err error) { + select { + case <-p.done: + return 0, p.writeCloseError() + default: + p.wrMu.Lock() + defer p.wrMu.Unlock() + } + + for once := true; once || len(b) > 0; once = false { + select { + case p.wrCh <- b: + nw := <-p.rdCh + b = b[nw:] + n += nw + case <-p.done: + return n, p.writeCloseError() + } + } + return n, nil +} + +func (p *pipe) closeWrite(err error) error { + if err == nil { + err = io.EOF + } + p.werr.Store(err) + p.once.Do(func() { close(p.done) }) + return nil +} + +// readCloseError is considered internal to the pipe type. +func (p *pipe) readCloseError() error { + rerr := p.rerr.Load() + if werr := p.werr.Load(); rerr == nil && werr != nil { + return werr + } + return ErrClosedPipe +} + +// writeCloseError is considered internal to the pipe type. +func (p *pipe) writeCloseError() error { + werr := p.werr.Load() + if rerr := p.rerr.Load(); werr == nil && rerr != nil { + return rerr + } + return ErrClosedPipe +} + +// A PipeReader is the read half of a pipe. +type PipeReader struct { + p *pipe +} + +// Read implements the standard Read interface: +// it reads data from the pipe, blocking until a writer +// arrives or the write end is closed. +// If the write end is closed with an error, that error is +// returned as err; otherwise err is EOF. +func (r *PipeReader) Read(data []byte) (n int, err error) { + return r.p.read(data) +} + +// Close closes the reader; subsequent writes to the +// write half of the pipe will return the error ErrClosedPipe. +func (r *PipeReader) Close() error { + return r.CloseWithError(nil) +} + +// CloseWithError closes the reader; subsequent writes +// to the write half of the pipe will return the error err. +// +// CloseWithError never overwrites the previous error if it exists +// and always returns nil. +func (r *PipeReader) CloseWithError(err error) error { + return r.p.closeRead(err) +} + +// A PipeWriter is the write half of a pipe. +type PipeWriter struct { + p *pipe +} + +// Write implements the standard Write interface: +// it writes data to the pipe, blocking until one or more readers +// have consumed all the data or the read end is closed. +// If the read end is closed with an error, that err is +// returned as err; otherwise err is ErrClosedPipe. +func (w *PipeWriter) Write(data []byte) (n int, err error) { + return w.p.write(data) +} + +// Close closes the writer; subsequent reads from the +// read half of the pipe will return no bytes and EOF. +func (w *PipeWriter) Close() error { + return w.CloseWithError(nil) +} + +// CloseWithError closes the writer; subsequent reads from the +// read half of the pipe will return no bytes and the error err, +// or EOF if err is nil. +// +// CloseWithError never overwrites the previous error if it exists +// and always returns nil. +func (w *PipeWriter) CloseWithError(err error) error { + return w.p.closeWrite(err) +} + +// Pipe creates a synchronous in-memory pipe. +// It can be used to connect code expecting an io.Reader +// with code expecting an io.Writer. +// +// Reads and Writes on the pipe are matched one to one +// except when multiple Reads are needed to consume a single Write. +// That is, each Write to the PipeWriter blocks until it has satisfied +// one or more Reads from the PipeReader that fully consume +// the written data. +// The data is copied directly from the Write to the corresponding +// Read (or Reads); there is no internal buffering. +// +// It is safe to call Read and Write in parallel with each other or with Close. +// Parallel calls to Read and parallel calls to Write are also safe: +// the individual calls will be gated sequentially. +func Pipe() (*PipeReader, *PipeWriter) { + p := &pipe{ + wrCh: make(chan []byte, 1), + rdCh: make(chan int, 1), + done: make(chan struct{}, 1), + } + return &PipeReader{p}, &PipeWriter{p} +} From 389750d7d8496b1d863ffe53e252536ec06046ae Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sun, 28 Jul 2024 18:51:07 +0800 Subject: [PATCH 048/152] ssa: panicBlk (todo) --- internal/runtime/z_rt.go | 9 +++-- ssa/eh.go | 83 ++++++++++++++++++++++++---------------- 2 files changed, 55 insertions(+), 37 deletions(-) diff --git a/internal/runtime/z_rt.go b/internal/runtime/z_rt.go index c100ade5..fa43c104 100644 --- a/internal/runtime/z_rt.go +++ b/internal/runtime/z_rt.go @@ -30,6 +30,7 @@ type Defer struct { Addr unsafe.Pointer // sigjmpbuf Bits uintptr Link *Defer + Reth unsafe.Pointer // block address after Rethrow Rund unsafe.Pointer // block address after RunDefers } @@ -82,10 +83,6 @@ func init() { // ----------------------------------------------------------------------------- -func unpackEface(i any) *eface { - return (*eface)(unsafe.Pointer(&i)) -} - // TracePanic prints panic message. func TracePanic(v any) { print("panic: ") @@ -94,6 +91,10 @@ func TracePanic(v any) { } /* +func unpackEface(i any) *eface { + return (*eface)(unsafe.Pointer(&i)) +} + func stringTracef(fp c.FilePtr, format *c.Char, s String) { cs := c.Alloca(uintptr(s.len) + 1) c.Fprintf(fp, format, CStrCopy(cs, s)) diff --git a/ssa/eh.go b/ssa/eh.go index dfbfcb89..28f8ea71 100644 --- a/ssa/eh.go +++ b/ssa/eh.go @@ -86,14 +86,17 @@ func (p Function) deferInitBuilder() (b Builder, next BasicBlock) { } type aDefer struct { - nextBit int // next defer bit - key Expr // pthread TLS key - data Expr // pointer to runtime.Defer - bitsPtr Expr // pointer to defer bits - rundPtr Expr // pointer to RunDefers index - procBlk BasicBlock // deferProc block - runsNext []BasicBlock // next blocks of RunDefers - stmts []func(bits Expr) + nextBit int // next defer bit + key Expr // pthread TLS key + data Expr // pointer to runtime.Defer + bitsPtr Expr // pointer to defer bits + rethPtr Expr // next block of Rethrow + rundPtr Expr // next block of RunDefers + procBlk BasicBlock // deferProc block + panicBlk BasicBlock // panic block (runDefers and rethrow) + rethsNext []BasicBlock // next blocks of Rethrow + rundsNext []BasicBlock // next blocks of RunDefers + stmts []func(bits Expr) } func (p Package) keyInit(name string) { @@ -124,53 +127,60 @@ const ( // 0: addr sigjmpbuf // 1: bits uintptr // 2: link *Defer - // 3: rund voidptr + // 3: reth voidptr: block address after Rethrow + // 4: rund voidptr: block address after RunDefers deferSigjmpbuf = iota deferBits deferLink - deferRund + deferRethrow + deferRunDefers ) func (b Builder) getDefer(kind DoAction) *aDefer { self := b.Func if self.defer_ == nil { // TODO(xsw): check if in pkg.init - var next, rundBlk BasicBlock + var next, panicBlk BasicBlock if kind != DeferAlways { b, next = self.deferInitBuilder() } + prog := b.Prog + blks := self.MakeBlocks(2) + procBlk, rethrowBlk := blks[0], blks[1] + key := b.deferKey() zero := prog.Val(uintptr(0)) link := Expr{b.pthreadGetspecific(key).impl, prog.DeferPtr()} jb := b.AllocaSigjmpBuf() - ptr := b.aggregateAlloca(prog.Defer(), jb.impl, zero.impl, link.impl) + ptr := b.aggregateAlloca(prog.Defer(), jb.impl, zero.impl, link.impl, procBlk.Addr().impl) deferData := Expr{ptr, prog.DeferPtr()} b.pthreadSetspecific(key, deferData) - blks := self.MakeBlocks(2) - procBlk, rethrowBlk := blks[0], blks[1] bitsPtr := b.FieldAddr(deferData, deferBits) - rundPtr := b.FieldAddr(deferData, deferRund) - self.defer_ = &aDefer{ - key: key, - data: deferData, - bitsPtr: bitsPtr, - rundPtr: rundPtr, - procBlk: procBlk, - runsNext: []BasicBlock{rethrowBlk}, - } + rethPtr := b.FieldAddr(deferData, deferRethrow) + rundPtr := b.FieldAddr(deferData, deferRunDefers) + czero := prog.IntVal(0, prog.CInt()) retval := b.Sigsetjmp(jb, czero) if kind != DeferAlways { - rundBlk = self.MakeBlock() + panicBlk = self.MakeBlock() } else { blks = self.MakeBlocks(2) - next, rundBlk = blks[0], blks[1] + next, panicBlk = blks[0], blks[1] + } + b.If(b.BinOp(token.EQL, retval, czero), next, panicBlk) + + self.defer_ = &aDefer{ + key: key, + data: deferData, + bitsPtr: bitsPtr, + rethPtr: rethPtr, + rundPtr: rundPtr, + procBlk: procBlk, + panicBlk: panicBlk, + rethsNext: []BasicBlock{procBlk}, + rundsNext: []BasicBlock{rethrowBlk}, } - b.If(b.BinOp(token.EQL, retval, czero), next, rundBlk) - b.SetBlockEx(rundBlk, AtEnd, false) // exec runDefers and rethrow - b.Store(rundPtr, rethrowBlk.Addr()) - b.Jump(procBlk) b.SetBlockEx(rethrowBlk, AtEnd, false) // rethrow b.Call(b.Pkg.rtFunc("Rethrow"), link) @@ -229,7 +239,7 @@ func (b Builder) Defer(kind DoAction, fn Expr, args ...Expr) { func (b Builder) RunDefers() { self := b.getDefer(DeferInCond) blk := b.Func.MakeBlock() - self.runsNext = append(self.runsNext, blk) + self.rundsNext = append(self.rundsNext, blk) b.Store(self.rundPtr, blk.Addr()) b.Jump(self.procBlk) @@ -243,11 +253,14 @@ func (p Function) endDefer(b Builder) { if self == nil { return } - nexts := self.runsNext + procBlk := self.procBlk + panicBlk := self.panicBlk + rundPtr := self.rundPtr + nexts := self.rundsNext if len(nexts) == 0 { return } - b.SetBlockEx(self.procBlk, AtEnd, true) + b.SetBlockEx(procBlk, AtEnd, true) bits := b.Load(self.bitsPtr) stmts := self.stmts for i := len(stmts) - 1; i >= 0; i-- { @@ -256,7 +269,11 @@ func (p Function) endDefer(b Builder) { link := b.getField(b.Load(self.data), deferLink) b.pthreadSetspecific(self.key, link) - b.IndirectJump(b.Load(self.rundPtr), nexts) + b.IndirectJump(b.Load(rundPtr), nexts) + + b.SetBlockEx(panicBlk, AtEnd, false) // exec runDefers and rethrow + b.Store(rundPtr, nexts[0].Addr()) // nexts[0] is rethrowBlk + b.Jump(procBlk) } // ----------------------------------------------------------------------------- From fa0ce2a14c530f2b04be6b1172b28f72ae18da10 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sun, 28 Jul 2024 18:57:46 +0800 Subject: [PATCH 049/152] ssa: panicBlk use IndirectJump --- ssa/eh.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ssa/eh.go b/ssa/eh.go index 28f8ea71..269dd2a0 100644 --- a/ssa/eh.go +++ b/ssa/eh.go @@ -255,6 +255,7 @@ func (p Function) endDefer(b Builder) { } procBlk := self.procBlk panicBlk := self.panicBlk + rethPtr := self.rethPtr rundPtr := self.rundPtr nexts := self.rundsNext if len(nexts) == 0 { @@ -273,7 +274,7 @@ func (p Function) endDefer(b Builder) { b.SetBlockEx(panicBlk, AtEnd, false) // exec runDefers and rethrow b.Store(rundPtr, nexts[0].Addr()) // nexts[0] is rethrowBlk - b.Jump(procBlk) + b.IndirectJump(b.Load(rethPtr), self.rethsNext) } // ----------------------------------------------------------------------------- From 35ba69a175292e04055056a987b538870d6cb06e Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Sun, 28 Jul 2024 09:46:45 +0800 Subject: [PATCH 050/152] c/lua:metatable --- c/lua/_demo/metatable/metatable.go | 125 +++++++++++++++++++++++++++++ c/lua/lua.go | 124 +++++++++++++++------------- 2 files changed, 192 insertions(+), 57 deletions(-) create mode 100644 c/lua/_demo/metatable/metatable.go diff --git a/c/lua/_demo/metatable/metatable.go b/c/lua/_demo/metatable/metatable.go new file mode 100644 index 00000000..424fd5ed --- /dev/null +++ b/c/lua/_demo/metatable/metatable.go @@ -0,0 +1,125 @@ +package main + +import ( + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/lua" +) + +func toString(L *lua.State) c.Int { + L.Pushstring(c.Str("Hello from metatable!")) + return 1 +} + +func printStack(L *lua.State, message string) { + top := L.Gettop() + c.Printf(c.Str("%s - Stack size: %d\n"), c.AllocaCStr(message), c.Int(top)) + for i := c.Int(1); i <= top; i++ { + t := L.Type(i) + switch t { + case c.Int(lua.STRING): + c.Printf(c.Str(" %d: string: %s\n"), c.Int(i), L.Tostring(i)) + case c.Int(lua.BOOLEAN): + c.Printf(c.Str(" %d: boolean: %v\n"), c.Int(i), L.Toboolean(i)) + case c.Int(lua.NUMBER): + c.Printf(c.Str(" %d: number: %f\n"), c.Int(i), L.Tonumber(i)) + default: + c.Printf(c.Str(" %d: %s\n"), c.Int(i), L.Typename(t)) + } + } +} + +func main() { + L := lua.Newstate() + defer L.Close() + + L.Openlibs() + + L.Newtable() + printStack(L, "After creating main table") + + L.Newtable() + printStack(L, "After creating metatable") + + L.Pushcfunction(toString) + printStack(L, "After Push CFunction") + + L.Setfield(-2, c.Str("__tostring")) + printStack(L, "After setting __tostring") + + if L.Setmetatable(-2) == 0 { + c.Printf(c.Str("Failed to set metatable\n")) + } + printStack(L, "After setting metatable") + + L.Setglobal(c.Str("obj")) + printStack(L, "After setting global obj") + + testcode := c.Str(` + if obj == nil then + print('obj is not defined') + else + local mt = getmetatable(obj) + if not mt then + print('Metatable not set') + elseif not mt.__tostring then + print('__tostring not set in metatable') + else + print(mt.__tostring(obj)) + end + end + `) + + if L.Dostring(testcode) != lua.OK { + c.Printf(c.Str("Error: %s\n"), L.Tostring(-1)) + } + + L.Getglobal(c.Str("obj")) + if L.Getmetatable(-1) != 0 { + c.Printf(c.Str("Metatable get success\n")) + L.Pushstring(c.Str("__tostring")) + L.Gettable(-2) + if L.Isfunction(-1) { + c.Printf(c.Str("__tostring function found in metatable\n")) + if L.Iscfunction(-1) != 0 { + c.Printf(c.Str("__tostring is a C function\n")) + cfunc := L.Tocfunction(-1) + if cfunc != nil { + c.Printf(c.Str("Successfully retrieved __tostring C function pointer\n")) + L.Pushcfunction(cfunc) + if L.Call(0, 1) == lua.OK { + result := L.Tostring(-1) + c.Printf(c.Str("Result of calling __tostring: %s\n"), result) + } + } + } + } else { + c.Printf(c.Str("__tostring function not found in metatable\n")) + } + } else { + c.Printf(c.Str("No metatable found using GetTable\n")) + } +} + +/* Expected output: +After creating main table - Stack size: 1 + 1: table +After creating metatable - Stack size: 2 + 1: table + 2: table +After Push CFunction - Stack size: 3 + 1: table + 2: table + 3: function +After setting __tostring - Stack size: 2 + 1: table + 2: table +After setting metatable - Stack size: 1 + 1: table +After setting global obj - Stack size: 0 +Hello from metatable! +Metatable get success +__tostring function found in metatable +__tostring is a C function +Successfully retrieved __tostring C function pointer +Result of calling __tostring: Hello from metatable! +*/ diff --git a/c/lua/lua.go b/c/lua/lua.go index 1812f74a..0dad8493 100644 --- a/c/lua/lua.go +++ b/c/lua/lua.go @@ -41,17 +41,17 @@ type State struct { // ** basic types // */ const ( - NONE = int(-1) - NIL = int(0) - BOOLEAN = int(1) - LIGHTUSERDATA = int(2) - NUMBER = int(3) - STRING = int(4) - TABLE = int(5) - FUNCTION = int(6) - USERDATA = int(7) - THREAD = int(8) - UMTYPES = int(9) + NONE c.Int = -1 + NIL c.Int = 0 + BOOLEAN c.Int = 1 + LIGHTUSERDATA c.Int = 2 + NUMBER c.Int = 3 + STRING c.Int = 4 + TABLE c.Int = 5 + FUNCTION c.Int = 6 + USERDATA c.Int = 7 + THREAD c.Int = 8 + UMTYPES c.Int = 9 ) // /* minimum Lua stack available to a C function */ @@ -78,13 +78,15 @@ type Integer = c.Int type Unsigned = c.Uint // /* type for continuation-function contexts */ -// TODO(zzy): Context may not be c.Int -type KContext c.Int +type KContext = c.Pointer // /* // ** Type for C functions registered with Lua // */ +// llgo:type C +type CFunction func(L *State) c.Int + // /* // ** Type for continuation functions // */ @@ -185,9 +187,6 @@ func (L *State) Xmove(to *State, n c.Int) {} // /* // ** access functions (stack -> C) // */ -// LUA_API int (lua_isinteger) (State *L, int idx); -// llgo:link (*State).Isinteger C.lua_isinteger -func (L *State) Isinteger(idx c.Int) c.Int { return 0 } // llgo:link (*State).Isnumber C.lua_isnumber func (L *State) Isnumber(idx c.Int) c.Int { return 0 } @@ -195,11 +194,17 @@ func (L *State) Isnumber(idx c.Int) c.Int { return 0 } // llgo:link (*State).Isstring C.lua_isstring func (L *State) Isstring(idx c.Int) c.Int { return 0 } -// TODO(zzy):add to demo +// llgo:link (*State).Iscfunction C.lua_iscfunction +func (L *State) Iscfunction(idx c.Int) c.Int { return 0 } + +// llgo:link (*State).Isinteger C.lua_isinteger +func (L *State) Isinteger(idx c.Int) c.Int { return 0 } + +// LUA_API int (lua_isuserdata) (State *L, int idx); + // llgo:link (*State).Type C.lua_type func (L *State) Type(idx c.Int) c.Int { return 0 } -// TODO(zzy) // llgo:link (*State).Typename C.lua_typename func (L *State) Typename(tp c.Int) *c.Char { return nil } @@ -215,11 +220,11 @@ func (L *State) Toboolean(idx c.Int) bool { return false } // llgo:link (*State).Tolstring C.lua_tolstring func (L *State) Tolstring(idx c.Int, len *c.Ulong) *c.Char { return nil } -// LUA_API int (lua_iscfunction) (State *L, int idx); -// LUA_API int (lua_isuserdata) (State *L, int idx); - // LUA_API lua_Unsigned (lua_rawlen) (State *L, int idx); -// LUA_API lua_CFunction (lua_tocfunction) (State *L, int idx); + +// llgo:link (*State).Tocfunction C.lua_tocfunction +func (L *State) Tocfunction(idx c.Int) CFunction { return nil } + // LUA_API void *(lua_touserdata) (State *L, int idx); // LUA_API State *(lua_tothread) (State *L, int idx); // LUA_API const void *(lua_topointer) (State *L, int idx); @@ -240,24 +245,21 @@ func (L *State) Pushnumber(n Number) {} // llgo:link (*State).Pushinteger C.lua_pushinteger func (L *State) Pushinteger(n Integer) {} -// llgo:link (*State).Pushstring C.lua_pushstring -func (L *State) Pushstring(s *c.Char) *c.Char { - return nil -} - // llgo:link (*State).Pushlstring C.lua_pushlstring -func (L *State) Pushlstring(s *c.Char, len c.Ulong) *c.Char { - return nil -} +func (L *State) Pushlstring(s *c.Char, len c.Ulong) *c.Char { return nil } + +// llgo:link (*State).Pushstring C.lua_pushstring +func (L *State) Pushstring(s *c.Char) *c.Char { return nil } // llgo:link (*State).Pushfstring C.lua_pushfstring func (L *State) Pushfstring(format *c.Char, __llgo_va_list ...any) *c.Char { return nil } +// llgo:link (*State).Pushcclosure C.lua_pushcclosure +func (L *State) Pushcclosure(fn CFunction, n c.Int) {} + // llgo:link (*State).Pushboolean C.lua_pushboolean func (L *State) Pushboolean(b c.Int) {} -//const char *(lua_pushvfstring) (State *L, const char *fmt,va_list argp); -//void (lua_pushcclosure) (State *L, lua_CFunction fn, int n); //void (lua_pushlightuserdata) (State *L, void *p); //int (lua_pushthread) (State *L); @@ -274,23 +276,25 @@ func (L *State) Gettable(idx c.Int) c.Int { return 0 } // llgo:link (*State).Getfield C.lua_getfield func (L *State) Getfield(idx c.Int, k *c.Char) c.Int { return 0 } -// llgo:link (*State).Createtable C.lua_createtable -func (L *State) Createtable(narr c.Int, nrec c.Int) {} - // LUA_API int (lua_geti) (State *L, int idx, lua_Integer n); // LUA_API int (lua_rawget) (State *L, int idx); // LUA_API int (lua_rawgeti) (State *L, int idx, lua_Integer n); // LUA_API int (lua_rawgetp) (State *L, int idx, const void *p); +// llgo:link (*State).Createtable C.lua_createtable +func (L *State) Createtable(narr c.Int, nrec c.Int) {} + // LUA_API void *(lua_newuserdatauv) (State *L, size_t sz, int nuvalue); -// LUA_API int (lua_getmetatable) (State *L, int objindex); + +// llgo:link (*State).Getmetatable C.lua_getmetatable +func (L *State) Getmetatable(objindex c.Int) c.Int { return 0 } + // LUA_API int (lua_getiuservalue) (State *L, int idx, int n); // /* // ** set functions (stack -> Lua) // */ -// TODO(zzy):add to demo // llgo:link (*State).Setglobal C.lua_setglobal func (L *State) Setglobal(name *c.Char) {} @@ -304,25 +308,34 @@ func (L *State) Setfield(idx c.Int, k *c.Char) {} //void (lua_rawset) (State *L, int idx); //void (lua_rawseti) (State *L, int idx, lua_Integer n); //void (lua_rawsetp) (State *L, int idx, const void *p); -//int (lua_setmetatable) (State *L, int objindex); + +// llgo:link (*State).Setmetatable C.lua_setmetatable +func (L *State) Setmetatable(objindex c.Int) c.Int { return 0 } + //int (lua_setiuservalue) (State *L, int idx, int n); // /* // ** 'load' and 'call' functions (load and run Lua code) // */ +// llgo:link (*State).Callk C.lua_callk +func (L *State) Callk(nargs c.Int, nresults c.Int, ctx KContext, k KFunction) c.Int { + return 0 +} + +func (L *State) Call(nargs c.Int, nresults c.Int) c.Int { + return L.Callk(nargs, nresults, nil, nil) +} + // llgo:link (*State).Pcallk C.lua_pcallk -func (L *State) Pcallk(nargs c.Int, nresults c.Int, errfunc c.Int, ctx KContext, k *KFunction) c.Int { +func (L *State) Pcallk(nargs c.Int, nresults c.Int, errfunc c.Int, ctx KContext, k KFunction) c.Int { return 0 } func (L *State) Pcall(nargs c.Int, nresults c.Int, errfunc c.Int) c.Int { - return L.Pcallk(nargs, nresults, errfunc, KContext(c.Int(0)), nil) + return L.Pcallk(nargs, nresults, errfunc, nil, nil) } -// void (lua_callk) (State *L, int nargs, int nresults, lua_KContext ctx, lua_KFunction k); -// #define lua_call(L,n,r) lua_callk(L, (n), (r), 0, NULL) - // int (lua_load) (State *L, lua_Reader reader, void *dt, const char *chunkname, const char *mode); // int (lua_dump) (State *L, lua_Writer writer, void *data, int strip); @@ -396,11 +409,17 @@ func (L *State) Next(idx c.Int) c.Int { return 0 } // ** =============================================================== // */ -func (L *State) Tonumber(idx c.Int) Number { return L.Tonumberx(idx, nil) } -func (L *State) Tostring(idx c.Int) *c.Char { return L.Tolstring(idx, nil) } -func (L *State) Tointeger(idx c.Int) Integer { return L.Tointegerx(idx, nil) } -func (L *State) Pop(n c.Int) { L.Settop(-(n) - 1) } -func (L *State) Newtable() { L.Createtable(0, 0) } +// #define lua_getextraspace(L) ((void *)((char *)(L) - LUA_EXTRASPACE)) + +func (L *State) Tonumber(idx c.Int) Number { return L.Tonumberx(idx, nil) } +func (L *State) Tostring(idx c.Int) *c.Char { return L.Tolstring(idx, nil) } +func (L *State) Tointeger(idx c.Int) Integer { return L.Tointegerx(idx, nil) } +func (L *State) Pop(n c.Int) { L.Settop(-(n) - 1) } +func (L *State) Newtable() { L.Createtable(0, 0) } + +// #define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) +func (L *State) Pushcfunction(f CFunction) { L.Pushcclosure(f, 0) } + func (L *State) Isfunction(n c.Int) bool { return L.Type(n) == c.Int(FUNCTION) } func (L *State) Istable(n c.Int) bool { return L.Type(n) == c.Int(TABLE) } func (L *State) Islightuserdata(n c.Int) bool { return L.Type(n) == c.Int(LIGHTUSERDATA) } @@ -410,20 +429,11 @@ func (L *State) Isthread(n c.Int) bool { return L.Type(n) == c.Int(THREAD func (L *State) Isnone(n c.Int) bool { return L.Type(n) == c.Int(NONE) } func (L *State) Isnoneornil(n c.Int) bool { return L.Type(n) <= 0 } -// #define lua_getextraspace(L) ((void *)((char *)(L) - LUA_EXTRASPACE)) - -// #define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) - -// #define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0) - // #define lua_pushliteral(L, s) lua_pushstring(L, "" s) - // #define lua_pushglobaltable(L) ((void)lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)) // #define lua_insert(L,idx) lua_rotate(L, (idx), 1) - // #define lua_remove(L,idx) (lua_rotate(L, (idx), -1), lua_pop(L, 1)) - // #define lua_replace(L,idx) (lua_copy(L, -1, (idx)), lua_pop(L, 1)) // /* }============================================================== */ From 5007d49c83b8c4f563fc2af3e4a16280ea18066b Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sun, 28 Jul 2024 19:53:04 +0800 Subject: [PATCH 051/152] ssa: endDefer --- ssa/eh.go | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/ssa/eh.go b/ssa/eh.go index 269dd2a0..2e996101 100644 --- a/ssa/eh.go +++ b/ssa/eh.go @@ -94,7 +94,6 @@ type aDefer struct { rundPtr Expr // next block of RunDefers procBlk BasicBlock // deferProc block panicBlk BasicBlock // panic block (runDefers and rethrow) - rethsNext []BasicBlock // next blocks of Rethrow rundsNext []BasicBlock // next blocks of RunDefers stmts []func(bits Expr) } @@ -178,7 +177,6 @@ func (b Builder) getDefer(kind DoAction) *aDefer { rundPtr: rundPtr, procBlk: procBlk, panicBlk: panicBlk, - rethsNext: []BasicBlock{procBlk}, rundsNext: []BasicBlock{rethrowBlk}, } @@ -253,28 +251,42 @@ func (p Function) endDefer(b Builder) { if self == nil { return } - procBlk := self.procBlk - panicBlk := self.panicBlk - rethPtr := self.rethPtr - rundPtr := self.rundPtr nexts := self.rundsNext if len(nexts) == 0 { return } - b.SetBlockEx(procBlk, AtEnd, true) - bits := b.Load(self.bitsPtr) - stmts := self.stmts - for i := len(stmts) - 1; i >= 0; i-- { - stmts[i](bits) - } + rethrowBlk := nexts[0] + procBlk := self.procBlk + panicBlk := self.panicBlk + rethPtr := self.rethPtr + rundPtr := self.rundPtr + bitsPtr := self.bitsPtr + + stmts := self.stmts + n := len(stmts) + rethsNext := make([]BasicBlock, n+1) + blks := p.MakeBlocks(n - 1) + copy(rethsNext[1:], blks) + rethsNext[0] = rethrowBlk + rethsNext[n] = procBlk + + for i := n - 1; i >= 0; i-- { + rethNext := rethsNext[i] + b.SetBlockEx(rethsNext[i+1], AtEnd, true) + b.Store(rethPtr, rethNext.Addr()) + stmts[i](b.Load(bitsPtr)) + if i != 0 { + b.Jump(rethNext) + } + } link := b.getField(b.Load(self.data), deferLink) b.pthreadSetspecific(self.key, link) b.IndirectJump(b.Load(rundPtr), nexts) - b.SetBlockEx(panicBlk, AtEnd, false) // exec runDefers and rethrow - b.Store(rundPtr, nexts[0].Addr()) // nexts[0] is rethrowBlk - b.IndirectJump(b.Load(rethPtr), self.rethsNext) + b.SetBlockEx(panicBlk, AtEnd, false) // panicBlk: exec runDefers and rethrow + b.Store(rundPtr, rethrowBlk.Addr()) + b.IndirectJump(b.Load(rethPtr), rethsNext) } // ----------------------------------------------------------------------------- From 0d468e0df43224ceb3d5ff4baea7f17a610b9539 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sun, 28 Jul 2024 20:00:53 +0800 Subject: [PATCH 052/152] cl/_testgo: defer5 --- cl/_testgo/defer5/in.go | 17 +++++++++++++++++ cl/_testgo/defer5/out.ll | 1 + 2 files changed, 18 insertions(+) create mode 100644 cl/_testgo/defer5/in.go create mode 100644 cl/_testgo/defer5/out.ll diff --git a/cl/_testgo/defer5/in.go b/cl/_testgo/defer5/in.go new file mode 100644 index 00000000..114e7fb9 --- /dev/null +++ b/cl/_testgo/defer5/in.go @@ -0,0 +1,17 @@ +package main + +func main() { + defer println("A") + defer func() { + if e := recover(); e != nil { + println("in defer 1") + panic("panic in defer 1") + } + }() + defer func() { + println("in defer 2") + panic("panic in defer 2") + }() + defer println("B") + panic("panic in main") +} diff --git a/cl/_testgo/defer5/out.ll b/cl/_testgo/defer5/out.ll new file mode 100644 index 00000000..1c8a0e79 --- /dev/null +++ b/cl/_testgo/defer5/out.ll @@ -0,0 +1 @@ +; \ No newline at end of file From 072f606784d5a9657e2413f4a49da757040c62b1 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Sun, 28 Jul 2024 20:15:11 +0800 Subject: [PATCH 053/152] c/lua:coroutine --- c/lua/_demo/coroutine-cfunc/coroutine.go | 42 ++++++++++++++++++++++++ c/lua/_demo/coroutine/coroutine.go | 22 ++++++++++++- c/lua/lua.go | 7 ++-- 3 files changed, 66 insertions(+), 5 deletions(-) create mode 100644 c/lua/_demo/coroutine-cfunc/coroutine.go diff --git a/c/lua/_demo/coroutine-cfunc/coroutine.go b/c/lua/_demo/coroutine-cfunc/coroutine.go new file mode 100644 index 00000000..676a5bfc --- /dev/null +++ b/c/lua/_demo/coroutine-cfunc/coroutine.go @@ -0,0 +1,42 @@ +package main + +import ( + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/lua" +) + +func coroutineFunc(L *lua.State) c.Int { + c.Printf(c.Str("Coroutine started\n")) + L.Yield(0) // Pause the coroutine + c.Printf(c.Str("Coroutine resumed\n")) + return 0 +} + +func main() { + L := lua.Newstate() + defer L.Close() + + L.Openlibs() + co := L.Newthread() + L.Pushcfunction(coroutineFunc) + L.Xmove(co, 1) + + var nres c.Int + + c.Printf(c.Str("Resuming coroutine for the first time\n")) + result := co.Resume(nil, 0, &nres) + if result == lua.YIELD { + c.Printf(c.Str("Coroutine yielded\n")) + result = co.Resume(nil, 0, &nres) + if result == 0 { + c.Printf(c.Str("Coroutine finished\n")) + } + } +} + +/* Expected output: +Resuming coroutine for the first time +Coroutine started +Coroutine yielded +Coroutine finished +*/ diff --git a/c/lua/_demo/coroutine/coroutine.go b/c/lua/_demo/coroutine/coroutine.go index feee0f66..ed05d98b 100644 --- a/c/lua/_demo/coroutine/coroutine.go +++ b/c/lua/_demo/coroutine/coroutine.go @@ -36,8 +36,8 @@ func main() { // Resume coroutine and handle yields for { status = co.Resume(nil, 0, &nres) - c.Printf(c.Str("Resuming coroutine %d...\n"), status) if status == lua.YIELD { + c.Printf(c.Str("Resuming coroutine %d...\n"), status) yieldValue := co.Tointeger(-1) c.Printf(c.Str("Yield value: %d\n"), yieldValue) co.Pop(1) // Clean up the stack @@ -58,3 +58,23 @@ func main() { finalStatus := co.Status() c.Printf(c.Str("Final status of coroutine: %d\n"), finalStatus) } + +/* Expected output: +Resuming coroutine... +Resuming coroutine 1... +Yield value: 1 +Coroutine is yieldable. +Resuming coroutine 1... +Yield value: 2 +Coroutine is yieldable. +Resuming coroutine 1... +Yield value: 3 +Coroutine is yieldable. +Resuming coroutine 1... +Yield value: 4 +Coroutine is yieldable. +Resuming coroutine 1... +Yield value: 5 +Coroutine is yieldable. +Final status of coroutine: 0 +*/ diff --git a/c/lua/lua.go b/c/lua/lua.go index 0dad8493..86f3a532 100644 --- a/c/lua/lua.go +++ b/c/lua/lua.go @@ -146,7 +146,6 @@ type KFunction func(L *State, status c.Int, ctx KContext) c.Int func (L *State) Close() {} // State *(lua_newstate) (lua_Alloc f, void *ud); -// State *(lua_newthread) (State *L); // llgo:link (*State).Newthread C.lua_newthread func (L *State) Newthread() *State { return nil } @@ -353,9 +352,9 @@ func (L *State) Status() c.Int { return 0 } // llgo:link (*State).Isyieldable C.lua_isyieldable func (L *State) Isyieldable() c.Int { return 0 } -// TODO(zzy) -// int (lua_yieldk) (State *L, int nresults, lua_KContext ctx, lua_KFunction k); -// #define lua_yield(L,n) lua_yieldk(L, (n), 0, NULL) +// llgo:link (*State).Yieldk C.lua_yieldk +func (L *State) Yieldk(nresults c.Int, ctx KContext, k KFunction) c.Int { return 0 } +func (L *State) Yield(nresults c.Int) c.Int { return L.Yieldk(nresults, nil, nil) } // /* // ** Warning-related functions From 54964629fea1c67aa134898aa778d6bbb36e6101 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sun, 28 Jul 2024 20:31:48 +0800 Subject: [PATCH 054/152] runtime.Panic: mv error/Stringer to printany --- internal/runtime/z_error.go | 4 ++++ internal/runtime/z_rt.go | 10 ---------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/internal/runtime/z_error.go b/internal/runtime/z_error.go index eb05f07c..69cdb953 100644 --- a/internal/runtime/z_error.go +++ b/internal/runtime/z_error.go @@ -96,6 +96,10 @@ func printany(i any) { print(v) case string: print(v) + case error: + print(v.Error()) + case interface{ String() string }: + print(v.String()) default: printanycustomtype(i) } diff --git a/internal/runtime/z_rt.go b/internal/runtime/z_rt.go index fa43c104..c11ebb6c 100644 --- a/internal/runtime/z_rt.go +++ b/internal/runtime/z_rt.go @@ -47,12 +47,6 @@ func Recover() (ret any) { // Panic panics with a value. func Panic(v any) { - switch e := v.(type) { - case error: - v = e.Error() - case interface{ String() string }: - v = e.String() - } ptr := c.Malloc(unsafe.Sizeof(v)) *(*any)(ptr) = v excepKey.Set(ptr) @@ -91,10 +85,6 @@ func TracePanic(v any) { } /* -func unpackEface(i any) *eface { - return (*eface)(unsafe.Pointer(&i)) -} - func stringTracef(fp c.FilePtr, format *c.Char, s String) { cs := c.Alloca(uintptr(s.len) + 1) c.Fprintf(fp, format, CStrCopy(cs, s)) From 3881db0b4eab465009bd9974196bf5ae11aad62b Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sun, 28 Jul 2024 21:57:32 +0800 Subject: [PATCH 055/152] compileFuncDecl todo: change ssa.If to ssa.Jump for func init --- cl/compile.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cl/compile.go b/cl/compile.go index 29f2dc54..113dfc1b 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -195,7 +195,7 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun isInit := (f.Name() == "init" && sig.Recv() == nil) if isInit && state == pkgHasPatch { name = initFnNameOfHasPatch(name) - // pkg.init$guard has been set, change ssa.if-cond + // TODO(xsw): pkg.init$guard has been set, change ssa.If to ssa.Jump block := f.Blocks[0].Instrs[1].(*ssa.If).Block() block.Succs[0], block.Succs[1] = block.Succs[1], block.Succs[0] } @@ -306,7 +306,7 @@ func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, n int, do b.Call(pkg.FuncOf("main.init").Expr) } for i, instr := range instrs { - if i == 1 && doModInit && p.state == pkgInPatch { + if i == 1 && doModInit && p.state == pkgInPatch { // in patch package but no pkgFNoOldInit initFnNameOld := initFnNameOfHasPatch(p.fn.Name()) fnOld := pkg.NewFunc(initFnNameOld, llssa.NoArgsNoRet, llssa.InC) b.Call(fnOld.Expr) From 907f326788701c23c135da30b6c5f3250257fe69 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sun, 28 Jul 2024 22:19:40 +0800 Subject: [PATCH 056/152] library: os.File.Close --- internal/lib/os/file_posix.go | 11 ++++------- internal/lib/os/file_unix.go | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/internal/lib/os/file_posix.go b/internal/lib/os/file_posix.go index 03f1806c..684ff5be 100644 --- a/internal/lib/os/file_posix.go +++ b/internal/lib/os/file_posix.go @@ -16,13 +16,10 @@ import ( // be canceled and return immediately with an ErrClosed error. // Close will return an error if it has already been called. func (f *File) Close() error { - /* - if f == nil { - return ErrInvalid - } - return f.file.close() - */ - panic("todo: os.(*File).Close") + if f == nil { + return ErrInvalid + } + return f.close() } // pread reads len(b) bytes from the File starting at byte offset off. diff --git a/internal/lib/os/file_unix.go b/internal/lib/os/file_unix.go index 918890a0..fc75f0c1 100644 --- a/internal/lib/os/file_unix.go +++ b/internal/lib/os/file_unix.go @@ -250,6 +250,27 @@ func openFileNolog(name string, flag int, perm FileMode) (*File, error) { return f, nil } +func (file *File) close() error { + return syscall.Close(int(file.fd)) + /* TODO(xsw): + if file.dirinfo != nil { + file.dirinfo.close() + file.dirinfo = nil + } + var err error + if e := file.pfd.Close(); e != nil { + if e == poll.ErrFileClosing { + e = ErrClosed + } + err = &PathError{Op: "close", Path: file.name, Err: e} + } + + // no need for a finalizer anymore + runtime.SetFinalizer(file, nil) + return err + */ +} + func tempDir() string { dir := Getenv("TMPDIR") if dir == "" { From 09b6b9259cd321867b562432a98258762d27ae7e Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sun, 28 Jul 2024 22:22:28 +0800 Subject: [PATCH 057/152] library: exec.Cmd.environ --- internal/lib/os/exec/exec.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/internal/lib/os/exec/exec.go b/internal/lib/os/exec/exec.go index c54ecc3e..8f9059dc 100644 --- a/internal/lib/os/exec/exec.go +++ b/internal/lib/os/exec/exec.go @@ -29,6 +29,8 @@ import ( "strings" "syscall" "time" + + "github.com/goplus/llgo/internal/lib/internal/syscall/execenv" ) // Error is returned by LookPath when it fails to classify a file as an @@ -1089,7 +1091,6 @@ func minInt(a, b int) int { // would be run as it is currently configured. If an error occurs in computing // the environment, it is returned alongside the best-effort copy. func (c *Cmd) environ() ([]string, error) { - /* TODO(xsw): var err error env := c.Env @@ -1128,8 +1129,6 @@ func (c *Cmd) environ() ([]string, error) { err = dedupErr } return addCriticalEnv(env), err - */ - panic("todo: exec.Cmd.environ") } // Environ returns a copy of the environment in which the command would be run From 3a8642b1e0d523dca0711c8e6e3616a0365e0da1 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sun, 28 Jul 2024 22:27:26 +0800 Subject: [PATCH 058/152] syscall: forkAndExecInChild --- _cmptest/{_osexec => osexec}/exec.go | 0 internal/lib/syscall/exec_libc2.go | 11 +++-------- 2 files changed, 3 insertions(+), 8 deletions(-) rename _cmptest/{_osexec => osexec}/exec.go (100%) diff --git a/_cmptest/_osexec/exec.go b/_cmptest/osexec/exec.go similarity index 100% rename from _cmptest/_osexec/exec.go rename to _cmptest/osexec/exec.go diff --git a/internal/lib/syscall/exec_libc2.go b/internal/lib/syscall/exec_libc2.go index 4c45b80b..02237240 100644 --- a/internal/lib/syscall/exec_libc2.go +++ b/internal/lib/syscall/exec_libc2.go @@ -261,22 +261,17 @@ func forkAndExecInChild(argv0 *c.Char, argv, envv **c.Char, chroot, dir *c.Char, if fd[i] == i { // dup2(i, i) won't clear close-on-exec flag on Linux, // probably not elsewhere either. - ret := os.Fcntl(c.Int(fd[i]), syscall.F_SETFD, 0) - if ret != 0 { + if ret := os.Fcntl(c.Int(fd[i]), syscall.F_SETFD, 0); ret != 0 { err1 = Errno(ret) goto childerror } continue } - /* TODO(xsw): // The new fd is created NOT close-on-exec, - // which is exactly what we want. - _, _, err1 = rawSyscall(abi.FuncPCABI0(libc_dup2_trampoline), uintptr(fd[i]), uintptr(i), 0) - if err1 != 0 { + if ret := os.Dup2(c.Int(fd[i]), c.Int(i)); ret != 0 { + err1 = Errno(ret) goto childerror } - */ - panic("todo: syscall.forkAndExecInChild - dup2") } // By convention, we don't close-on-exec the fds we are From bae40c82b94bc8debc4d8667de7c62a99b0380da Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sun, 28 Jul 2024 22:51:35 +0800 Subject: [PATCH 059/152] llgo/xtool/nm/nmindex --- chore/nmindex/nmindex.go | 6 +++--- xtool/nm/{ => nmindex}/index.go | 12 +++++++----- xtool/nm/{ => nmindex}/query.go | 8 +++++--- 3 files changed, 15 insertions(+), 11 deletions(-) rename xtool/nm/{ => nmindex}/index.go (90%) rename xtool/nm/{ => nmindex}/query.go (96%) diff --git a/chore/nmindex/nmindex.go b/chore/nmindex/nmindex.go index 43d34118..fc575fc2 100644 --- a/chore/nmindex/nmindex.go +++ b/chore/nmindex/nmindex.go @@ -21,7 +21,7 @@ import ( "os" "github.com/goplus/llgo/xtool/env/llvm" - "github.com/goplus/llgo/xtool/nm" + "github.com/goplus/llgo/xtool/nm/nmindex" ) func main() { @@ -58,7 +58,7 @@ func makeIndex() { idxDir := indexDir() os.MkdirAll(idxDir, 0755) - b := nm.NewIndexBuilder(env.Nm()) + b := nmindex.NewIndexBuilder(env.Nm()) libDirs := []string{ usrLib(false), usrLib(true), @@ -78,7 +78,7 @@ func query(q string) { q = "_" + q } } - files, err := nm.Query(indexDir(), q) + files, err := nmindex.Query(indexDir(), q) check(err) for _, f := range files { fmt.Printf("%s:\n", f.ArFile) diff --git a/xtool/nm/index.go b/xtool/nm/nmindex/index.go similarity index 90% rename from xtool/nm/index.go rename to xtool/nm/nmindex/index.go index 912f5806..5f325444 100644 --- a/xtool/nm/index.go +++ b/xtool/nm/nmindex/index.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package nm +package nmindex import ( "bytes" @@ -24,13 +24,15 @@ import ( "os" "path/filepath" "strings" + + "github.com/goplus/llgo/xtool/nm" ) type IndexBuilder struct { - nm *Cmd + nm *nm.Cmd } -func NewIndexBuilder(nm *Cmd) *IndexBuilder { +func NewIndexBuilder(nm *nm.Cmd) *IndexBuilder { return &IndexBuilder{nm} } @@ -95,12 +97,12 @@ func (p *IndexBuilder) IndexFile(arFile, outFile string) (err error) { } for _, sym := range item.Symbols { switch sym.Type { - case Text, Data, BSS, Rodata, 'S', 'C', 'W', 'A': + case nm.Text, nm.Data, nm.BSS, nm.Rodata, 'S', 'C', 'W', 'A': b.WriteByte(byte(sym.Type)) b.WriteByte(' ') b.WriteString(sym.Name) b.WriteByte('\n') - case Undefined, LocalText, LocalData, LocalBSS, LocalASym, 'I', 'i', 'a', 'w': + case nm.Undefined, nm.LocalText, nm.LocalData, nm.LocalBSS, nm.LocalASym, 'I', 'i', 'a', 'w': /* if sym.Type != Undefined && strings.Contains(sym.Name, "fprintf") { log.Printf("skip symbol type %c: %s\n", sym.Type, sym.Name) diff --git a/xtool/nm/query.go b/xtool/nm/nmindex/query.go similarity index 96% rename from xtool/nm/query.go rename to xtool/nm/nmindex/query.go index f39c19bc..d86c4d52 100644 --- a/xtool/nm/query.go +++ b/xtool/nm/nmindex/query.go @@ -14,19 +14,21 @@ * limitations under the License. */ -package nm +package nmindex import ( "bufio" "os" "strings" + + "github.com/goplus/llgo/xtool/nm" ) // MatchedItem represents a matched item type MatchedItem struct { ObjFile string Symbol string - Type SymbolType + Type nm.SymbolType } // MatchedFile represents a matched file @@ -88,7 +90,7 @@ func queryIndex(files []*MatchedFile, idxFile, query string) []*MatchedFile { items = append(items, &MatchedItem{ ObjFile: objFile, Symbol: sym, - Type: SymbolType(typ), + Type: nm.SymbolType(typ), }) } if len(items) > 0 { From a9ece9e49d41dc8c467607a9795b2d7c9c414b4a Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sun, 28 Jul 2024 23:09:21 +0800 Subject: [PATCH 060/152] library: os.Pipe --- internal/lib/os/os.go | 3 --- internal/lib/os/pipe2_unix.go | 22 ++++++++++++++++++++++ internal/lib/os/pipe_unix.go | 28 ++++++++++++++++++++++++++++ internal/lib/os/pipe_wasm.go | 16 ++++++++++++++++ 4 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 internal/lib/os/pipe2_unix.go create mode 100644 internal/lib/os/pipe_unix.go create mode 100644 internal/lib/os/pipe_wasm.go diff --git a/internal/lib/os/os.go b/internal/lib/os/os.go index 9350272c..591f2ced 100644 --- a/internal/lib/os/os.go +++ b/internal/lib/os/os.go @@ -274,9 +274,6 @@ func Mkdir(name string, perm FileMode) error { // func MkdirAll(path string, perm FileMode) error // func MkdirTemp(dir, pattern string) (string, error) // func NewSyscallError(syscall string, err error) error -func Pipe() (r *File, w *File, err error) { - panic("todo: os.Pipe") -} // func ReadFile(name string) ([]byte, error) diff --git a/internal/lib/os/pipe2_unix.go b/internal/lib/os/pipe2_unix.go new file mode 100644 index 00000000..2d293fdb --- /dev/null +++ b/internal/lib/os/pipe2_unix.go @@ -0,0 +1,22 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build dragonfly || freebsd || linux || netbsd || openbsd || solaris + +package os + +import "syscall" + +// Pipe returns a connected pair of Files; reads from r return bytes written to w. +// It returns the files and an error, if any. +func Pipe() (r *File, w *File, err error) { + var p [2]int + + e := syscall.Pipe2(p[0:], syscall.O_CLOEXEC) + if e != nil { + return nil, nil, NewSyscallError("pipe2", e) + } + + return newFile(p[0], "|0", kindPipe), newFile(p[1], "|1", kindPipe), nil +} diff --git a/internal/lib/os/pipe_unix.go b/internal/lib/os/pipe_unix.go new file mode 100644 index 00000000..2eb11a04 --- /dev/null +++ b/internal/lib/os/pipe_unix.go @@ -0,0 +1,28 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build aix || darwin + +package os + +import "syscall" + +// Pipe returns a connected pair of Files; reads from r return bytes written to w. +// It returns the files and an error, if any. +func Pipe() (r *File, w *File, err error) { + var p [2]int + + // See ../syscall/exec.go for description of lock. + syscall.ForkLock.RLock() + e := syscall.Pipe(p[0:]) + if e != nil { + syscall.ForkLock.RUnlock() + return nil, nil, NewSyscallError("pipe", e) + } + syscall.CloseOnExec(p[0]) + syscall.CloseOnExec(p[1]) + syscall.ForkLock.RUnlock() + + return newFile(p[0], "|0", kindPipe), newFile(p[1], "|1", kindPipe), nil +} diff --git a/internal/lib/os/pipe_wasm.go b/internal/lib/os/pipe_wasm.go new file mode 100644 index 00000000..87a29b1f --- /dev/null +++ b/internal/lib/os/pipe_wasm.go @@ -0,0 +1,16 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build wasm + +package os + +import "syscall" + +// Pipe returns a connected pair of Files; reads from r return bytes written to w. +// It returns the files and an error, if any. +func Pipe() (r *File, w *File, err error) { + // Neither GOOS=js nor GOOS=wasip1 have pipes. + return nil, nil, NewSyscallError("pipe", syscall.ENOSYS) +} From 7d045842dd2d743ba5b52d8e2efbcab036c5c581 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sun, 28 Jul 2024 23:15:20 +0800 Subject: [PATCH 061/152] library: syscall.CloseOnExec --- internal/lib/syscall/exec_unix.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/internal/lib/syscall/exec_unix.go b/internal/lib/syscall/exec_unix.go index d979d891..21a12178 100644 --- a/internal/lib/syscall/exec_unix.go +++ b/internal/lib/syscall/exec_unix.go @@ -68,10 +68,7 @@ import ( var ForkLock sync.RWMutex func CloseOnExec(fd int) { - /* TODO(xsw): - fcntl(fd, F_SETFD, FD_CLOEXEC) - */ - panic("todo: syscall.CloseOnExec") + os.Fcntl(c.Int(fd), syscall.F_SETFD, syscall.FD_CLOEXEC) } func SetNonblock(fd int, nonblocking bool) (err error) { From a5dff9fb15e97408a6b950b4534eaf44a2e152cb Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sun, 28 Jul 2024 23:53:22 +0800 Subject: [PATCH 062/152] library: os.ProcessState.String --- cl/import.go | 72 +++++++++++++++--------------- internal/build/build.go | 1 + internal/lib/internal/itoa/itoa.go | 34 ++++++++++++++ internal/lib/os/exec_posix.go | 5 +-- internal/lib/os/str.go | 39 ++++++++++++++++ 5 files changed, 113 insertions(+), 38 deletions(-) create mode 100644 internal/lib/internal/itoa/itoa.go create mode 100644 internal/lib/os/str.go diff --git a/cl/import.go b/cl/import.go index 5d9b8b25..43d5aa0e 100644 --- a/cl/import.go +++ b/cl/import.go @@ -500,41 +500,6 @@ func (p *context) ensureLoaded(pkgTypes *types.Package) *types.Package { return pkgTypes } -func pkgKindByPath(pkgPath string) int { - switch pkgPath { - case "runtime/cgo", "unsafe": - return PkgDeclOnly - } - return PkgNormal -} - -func replaceGoName(v string, pos int) string { - switch v[:pos] { - case "runtime": - return "github.com/goplus/llgo/internal/runtime" + v[pos:] - } - return v -} - -func ignoreName(name string) bool { - /* TODO(xsw): confirm this is not needed more - if name == "unsafe.init" { - return true - } - */ - const internal = "internal/" - return (strings.HasPrefix(name, internal) && !supportedInternal(name[len(internal):])) || - strings.HasPrefix(name, "crypto/") || strings.HasPrefix(name, "runtime/") || - strings.HasPrefix(name, "arena.") || strings.HasPrefix(name, "maps.") || - strings.HasPrefix(name, "plugin.") -} - -func supportedInternal(name string) bool { - return strings.HasPrefix(name, "abi.") || strings.HasPrefix(name, "bytealg.") || - strings.HasPrefix(name, "oserror.") || strings.HasPrefix(name, "reflectlite.") || - strings.HasPrefix(name, "syscall/unix.") || strings.HasPrefix(name, "syscall/execenv.") -} - // ----------------------------------------------------------------------------- const ( @@ -600,3 +565,40 @@ func toBackground(bg string) llssa.Background { } // ----------------------------------------------------------------------------- + +func pkgKindByPath(pkgPath string) int { + switch pkgPath { + case "runtime/cgo", "unsafe": + return PkgDeclOnly + } + return PkgNormal +} + +func replaceGoName(v string, pos int) string { + switch v[:pos] { + case "runtime": + return "github.com/goplus/llgo/internal/runtime" + v[pos:] + } + return v +} + +func ignoreName(name string) bool { + /* TODO(xsw): confirm this is not needed more + if name == "unsafe.init" { + return true + } + */ + const internal = "internal/" + return (strings.HasPrefix(name, internal) && !supportedInternal(name[len(internal):])) || + strings.HasPrefix(name, "crypto/") || strings.HasPrefix(name, "runtime/") || + strings.HasPrefix(name, "arena.") || strings.HasPrefix(name, "maps.") || + strings.HasPrefix(name, "plugin.") +} + +func supportedInternal(name string) bool { + return strings.HasPrefix(name, "abi.") || strings.HasPrefix(name, "bytealg.") || + strings.HasPrefix(name, "itoa.") || strings.HasPrefix(name, "oserror.") || strings.HasPrefix(name, "reflectlite.") || + strings.HasPrefix(name, "syscall/unix.") || strings.HasPrefix(name, "syscall/execenv.") +} + +// ----------------------------------------------------------------------------- diff --git a/internal/build/build.go b/internal/build/build.go index ba27fc65..6de1e0c4 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -750,6 +750,7 @@ var hasAltPkg = map[string]none{ "fmt": {}, "internal/abi": {}, "internal/bytealg": {}, + "internal/itoa": {}, "internal/oserror": {}, "internal/reflectlite": {}, "internal/syscall/execenv": {}, diff --git a/internal/lib/internal/itoa/itoa.go b/internal/lib/internal/itoa/itoa.go new file mode 100644 index 00000000..32b15359 --- /dev/null +++ b/internal/lib/internal/itoa/itoa.go @@ -0,0 +1,34 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Simple conversions to avoid depending on strconv. + +// llgo:skipall +package itoa + +// Itoa converts val to a decimal string. +func Itoa(val int) string { + if val < 0 { + return "-" + Uitoa(uint(-val)) + } + return Uitoa(uint(val)) +} + +// Uitoa converts val to a decimal string. +func Uitoa(val uint) string { + if val == 0 { // avoid string allocation + return "0" + } + var buf [20]byte // big enough for 64bit value base 10 + i := len(buf) - 1 + for val >= 10 { + q := val / 10 + buf[i] = byte('0' + val - q*10) + i-- + val = q + } + // val < 10 + buf[i] = byte('0' + val) + return string(buf[i:]) +} diff --git a/internal/lib/os/exec_posix.go b/internal/lib/os/exec_posix.go index 1c698988..54462588 100644 --- a/internal/lib/os/exec_posix.go +++ b/internal/lib/os/exec_posix.go @@ -7,8 +7,10 @@ package os import ( + "runtime" "syscall" + "github.com/goplus/llgo/internal/lib/internal/itoa" "github.com/goplus/llgo/internal/lib/internal/syscall/execenv" ) @@ -96,7 +98,6 @@ func (p *ProcessState) sysUsage() any { } func (p *ProcessState) String() string { - /* TODO(xsw): if p == nil { return "" } @@ -124,8 +125,6 @@ func (p *ProcessState) String() string { res += " (core dumped)" } return res - */ - panic("todo: os.ProcessState.String") } // ExitCode returns the exit code of the exited process, or -1 diff --git a/internal/lib/os/str.go b/internal/lib/os/str.go new file mode 100644 index 00000000..242c945c --- /dev/null +++ b/internal/lib/os/str.go @@ -0,0 +1,39 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Simple conversions to avoid depending on strconv. + +package os + +// itox converts val (an int) to a hexadecimal string. +func itox(val int) string { + if val < 0 { + return "-" + uitox(uint(-val)) + } + return uitox(uint(val)) +} + +const hex = "0123456789abcdef" + +// uitox converts val (a uint) to a hexadecimal string. +func uitox(val uint) string { + if val == 0 { // avoid string allocation + return "0x0" + } + var buf [20]byte // big enough for 64bit value base 16 + 0x + i := len(buf) - 1 + for val >= 16 { + q := val / 16 + buf[i] = hex[val%16] + i-- + val = q + } + // val < 16 + buf[i] = hex[val%16] + i-- + buf[i] = 'x' + i-- + buf[i] = '0' + return string(buf[i:]) +} From 4df478316c775c162988d778aafef6eab714f1a7 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Mon, 29 Jul 2024 00:02:30 +0800 Subject: [PATCH 063/152] library: fmt.(*pp).doPrint; syscall.forkAndExecInChild --- _cmptest/osexec/exec.go | 4 ++++ internal/lib/fmt/print.go | 21 +++++++++------------ internal/lib/syscall/exec_libc2.go | 9 ++++----- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/_cmptest/osexec/exec.go b/_cmptest/osexec/exec.go index ec983c4b..31eb2ec1 100644 --- a/_cmptest/osexec/exec.go +++ b/_cmptest/osexec/exec.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "os" "os/exec" "runtime" @@ -15,4 +16,7 @@ func main() { cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Run() + + output, _ := exec.Command(ls).Output() + fmt.Print(string(output)) } diff --git a/internal/lib/fmt/print.go b/internal/lib/fmt/print.go index 88786f0a..450d5a0d 100644 --- a/internal/lib/fmt/print.go +++ b/internal/lib/fmt/print.go @@ -1218,19 +1218,16 @@ formatLoop: } func (p *pp) doPrint(a []any) { - /* - prevString := false - for argNum, arg := range a { - isString := arg != nil && reflect.TypeOf(arg).Kind() == reflect.String - // Add a space between two non-string arguments. - if argNum > 0 && !isString && !prevString { - p.buf.writeByte(' ') - } - p.printArg(arg, 'v') - prevString = isString + prevString := false + for argNum, arg := range a { + isString := arg != nil && reflect.TypeOf(arg).Kind() == reflect.String + // Add a space between two non-string arguments. + if argNum > 0 && !isString && !prevString { + p.buf.writeByte(' ') } - */ - panic("todo: fmt.(*pp).doPrint") + p.printArg(arg, 'v') + prevString = isString + } } // doPrintln is like doPrint but always adds a space between arguments diff --git a/internal/lib/syscall/exec_libc2.go b/internal/lib/syscall/exec_libc2.go index 02237240..12bcfcbc 100644 --- a/internal/lib/syscall/exec_libc2.go +++ b/internal/lib/syscall/exec_libc2.go @@ -7,6 +7,8 @@ package syscall import ( + "unsafe" + "github.com/goplus/llgo/c" "github.com/goplus/llgo/c/os" "github.com/goplus/llgo/c/syscall" @@ -322,12 +324,9 @@ func forkAndExecInChild(argv0 *c.Char, argv, envv **c.Char, chroot, dir *c.Char, */ childerror: - /* TODO(xsw): // send error code on pipe - rawSyscall(abi.FuncPCABI0(libc_write_trampoline), uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1)) + os.Write(c.Int(pipe), unsafe.Pointer(&err1), unsafe.Sizeof(err1)) for { - rawSyscall(abi.FuncPCABI0(libc_exit_trampoline), 253, 0, 0) + os.Exit(253) } - */ - panic("todo: syscall.forkAndExecInChild - childerror") } From 708de50887afd2fc4889b98f0d860608ed07933e Mon Sep 17 00:00:00 2001 From: xushiwei Date: Mon, 29 Jul 2024 00:14:01 +0800 Subject: [PATCH 064/152] syscall.forkAndExecInChild fix: os.Fcntl/Dup2 retval --- internal/lib/syscall/exec_libc2.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/internal/lib/syscall/exec_libc2.go b/internal/lib/syscall/exec_libc2.go index 12bcfcbc..3a5dede4 100644 --- a/internal/lib/syscall/exec_libc2.go +++ b/internal/lib/syscall/exec_libc2.go @@ -263,15 +263,15 @@ func forkAndExecInChild(argv0 *c.Char, argv, envv **c.Char, chroot, dir *c.Char, if fd[i] == i { // dup2(i, i) won't clear close-on-exec flag on Linux, // probably not elsewhere either. - if ret := os.Fcntl(c.Int(fd[i]), syscall.F_SETFD, 0); ret != 0 { - err1 = Errno(ret) + if ret := os.Fcntl(c.Int(fd[i]), syscall.F_SETFD, 0); ret < 0 { + err1 = Errno(os.Errno) goto childerror } continue } // The new fd is created NOT close-on-exec, - if ret := os.Dup2(c.Int(fd[i]), c.Int(i)); ret != 0 { - err1 = Errno(ret) + if ret := os.Dup2(c.Int(fd[i]), c.Int(i)); ret < 0 { + err1 = Errno(os.Errno) goto childerror } } @@ -281,10 +281,7 @@ func forkAndExecInChild(argv0 *c.Char, argv, envv **c.Char, chroot, dir *c.Char, // Programs that know they inherit fds >= 3 will need // to set them close-on-exec. for i = len(fd); i < 3; i++ { - /* TODO(xsw): - rawSyscall(abi.FuncPCABI0(libc_close_trampoline), uintptr(i), 0, 0) - */ - panic("todo: syscall.forkAndExecInChild - for i") + os.Close(c.Int(i)) } // Detach fd 0 from tty From 8a13fc7cd9844dbba2868d8f918fd9adf6a9c633 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Mon, 29 Jul 2024 00:17:53 +0800 Subject: [PATCH 065/152] osexec: llvm bindir --- _cmptest/osexec/exec.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/_cmptest/osexec/exec.go b/_cmptest/osexec/exec.go index 31eb2ec1..0ec5c09e 100644 --- a/_cmptest/osexec/exec.go +++ b/_cmptest/osexec/exec.go @@ -5,6 +5,8 @@ import ( "os" "os/exec" "runtime" + + "github.com/goplus/llgo/xtool/env/llvm" ) func main() { @@ -17,6 +19,6 @@ func main() { cmd.Stderr = os.Stderr cmd.Run() - output, _ := exec.Command(ls).Output() - fmt.Print(string(output)) + dir := llvm.New("").BinDir() + fmt.Println(dir) } From 4cd162911890604bf209ab830a24de0dbdca4ea7 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Mon, 29 Jul 2024 01:26:56 +0800 Subject: [PATCH 066/152] reflect.Value: Uint fix --- _cmptest/printfdemo/demo.go | 28 ++++++++++++++++++++++++ internal/lib/reflect/value.go | 41 +++++++++++++++++++++++------------ 2 files changed, 55 insertions(+), 14 deletions(-) create mode 100644 _cmptest/printfdemo/demo.go diff --git a/_cmptest/printfdemo/demo.go b/_cmptest/printfdemo/demo.go new file mode 100644 index 00000000..7a20726f --- /dev/null +++ b/_cmptest/printfdemo/demo.go @@ -0,0 +1,28 @@ +/* + * 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 main + +import ( + "fmt" + + "github.com/goplus/llgo/xtool/nm" +) + +func main() { + sym := nm.Symbol{Name: "abc", Type: nm.Text} + fmt.Printf("%016x %c %s\n", sym.Addr, sym.Type, sym.Name) +} diff --git a/internal/lib/reflect/value.go b/internal/lib/reflect/value.go index c2155266..f2d8756f 100644 --- a/internal/lib/reflect/value.go +++ b/internal/lib/reflect/value.go @@ -1579,26 +1579,39 @@ func (v Value) CanUint() bool { // Uint returns v's underlying value, as a uint64. // It panics if v's Kind is not Uint, Uintptr, Uint8, Uint16, Uint32, or Uint64. func (v Value) Uint() uint64 { + f := v.flag k := v.kind() p := v.ptr - switch k { - case Uint: - return uint64(*(*uint)(p)) - case Uint8: - return uint64(*(*uint8)(p)) - case Uint16: - return uint64(*(*uint16)(p)) - case Uint32: - return uint64(*(*uint32)(p)) - case Uint64: - return *(*uint64)(p) - case Uintptr: - return uint64(*(*uintptr)(p)) + if f&flagAddr != 0 { + switch k { + case Uint: + return uint64(*(*uint)(p)) + case Uint8: + return uint64(*(*uint8)(p)) + case Uint16: + return uint64(*(*uint16)(p)) + case Uint32: + return uint64(*(*uint32)(p)) + case Uint64: + return *(*uint64)(p) + case Uintptr: + return uint64(*(*uintptr)(p)) + } + } else if unsafe.Sizeof(uintptr(0)) == 8 { + if k >= Uint && k <= Uintptr { + return uint64(uintptr(p)) + } + } else { + if k >= Uint && k <= Uint32 { + return uint64(uintptr(p)) + } + if k == Uint64 || k == Uintptr { + return *(*uint64)(p) + } } panic(&ValueError{"reflect.Value.Uint", v.kind()}) } -//go:nocheckptr // This prevents inlining Value.UnsafeAddr when -d=checkptr is enabled, // which ensures cmd/compile can recognize unsafe.Pointer(v.UnsafeAddr()) // and make an exception. From 53c2558d2688eb089f73fbee16fead495191ec17 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Mon, 29 Jul 2024 01:56:39 +0800 Subject: [PATCH 067/152] cmptest: jsondemo (todo) --- _cmptest/_jsondemo/json.go | 16 ++++++++++++++++ _cmptest/_timeout/timer.go | 2 +- _cmptest/printfdemo/demo.go | 16 ---------------- 3 files changed, 17 insertions(+), 17 deletions(-) create mode 100644 _cmptest/_jsondemo/json.go diff --git a/_cmptest/_jsondemo/json.go b/_cmptest/_jsondemo/json.go new file mode 100644 index 00000000..6f39c82e --- /dev/null +++ b/_cmptest/_jsondemo/json.go @@ -0,0 +1,16 @@ +package main + +import ( + "encoding/json" + "fmt" + "unsafe" +) + +func main() { + s := `{"name":"math","items":[{"name":"sqrt","sig":"(x, /)"},{"name":"pi"}]}` + data := unsafe.Slice(unsafe.StringData(s), len(s)) + var v any + json.Unmarshal(data, &v) + b, _ := json.MarshalIndent(v, "", " ") + fmt.Println(string(b)) +} diff --git a/_cmptest/_timeout/timer.go b/_cmptest/_timeout/timer.go index bc060890..a830f85e 100644 --- a/_cmptest/_timeout/timer.go +++ b/_cmptest/_timeout/timer.go @@ -13,7 +13,7 @@ func main() { select { case m := <-c: handle(m) - case <-time.After(10 * time.Second): + case <-time.After(time.Second / 10): fmt.Println("timed out") } } diff --git a/_cmptest/printfdemo/demo.go b/_cmptest/printfdemo/demo.go index 7a20726f..36a2afeb 100644 --- a/_cmptest/printfdemo/demo.go +++ b/_cmptest/printfdemo/demo.go @@ -1,19 +1,3 @@ -/* - * 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 main import ( From e0892fcebbe6da2a302b4cca7f99067f5253c18f Mon Sep 17 00:00:00 2001 From: xushiwei Date: Mon, 29 Jul 2024 08:38:56 +0800 Subject: [PATCH 068/152] library: path, path/filepath --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4f94d58a..50338c10 100644 --- a/README.md +++ b/README.md @@ -279,6 +279,8 @@ Here are the Go packages that can be imported correctly: * [sort](https://pkg.go.dev/sort) * [strconv](https://pkg.go.dev/strconv) * [strings](https://pkg.go.dev/strings) +* [path](https://pkg.go.dev/path) +* [path/filepath](https://pkg.go.dev/path/filepath) * [sync/atomic](https://pkg.go.dev/sync/atomic) * [sync](https://pkg.go.dev/sync) (partially) * [syscall](https://pkg.go.dev/syscall) (partially) @@ -292,7 +294,7 @@ Here are the Go packages that can be imported correctly: ## Dependencies -- [Go 1.20+](https://go.dev) (build only) +- [Go 1.20+](https://go.dev) - [LLVM 18](https://llvm.org) - [LLD 18](https://lld.llvm.org) - [Clang 18](https://clang.llvm.org) From 8b6b039c13d6e69346e4e864684c2687f0d48a61 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Mon, 29 Jul 2024 08:55:27 +0800 Subject: [PATCH 069/152] cpp/std: support nogc --- cpp/std/std_gc.go | 39 +++++++++++++++++++++++++++++++++++++++ cpp/std/std_nogc.go | 35 +++++++++++++++++++++++++++++++++++ cpp/std/string.go | 11 +---------- 3 files changed, 75 insertions(+), 10 deletions(-) create mode 100644 cpp/std/std_gc.go create mode 100644 cpp/std/std_nogc.go diff --git a/cpp/std/std_gc.go b/cpp/std/std_gc.go new file mode 100644 index 00000000..21902954 --- /dev/null +++ b/cpp/std/std_gc.go @@ -0,0 +1,39 @@ +//go:build !nogc +// +build !nogc + +/* + * 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 std + +import ( + "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/bdwgc" +) + +// ----------------------------------------------------------------------------- + +func allocString() *String { + ptr := bdwgc.Malloc(unsafe.Sizeof(String{})) + bdwgc.RegisterFinalizer(ptr, func(obj, data c.Pointer) { + (*String)(obj).Dispose() + }, nil, nil, nil) + return (*String)(ptr) +} + +// ----------------------------------------------------------------------------- diff --git a/cpp/std/std_nogc.go b/cpp/std/std_nogc.go new file mode 100644 index 00000000..20f8c880 --- /dev/null +++ b/cpp/std/std_nogc.go @@ -0,0 +1,35 @@ +//go:build nogc +// +build nogc + +/* + * 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 std + +import ( + "unsafe" + + "github.com/goplus/llgo/c" +) + +// ----------------------------------------------------------------------------- + +func allocString() *String { + ptr := c.Malloc(unsafe.Sizeof(String{})) + return (*String)(ptr) +} + +// ----------------------------------------------------------------------------- diff --git a/cpp/std/string.go b/cpp/std/string.go index fa04524f..5427a51a 100644 --- a/cpp/std/string.go +++ b/cpp/std/string.go @@ -20,7 +20,6 @@ import ( "unsafe" "github.com/goplus/llgo/c" - "github.com/goplus/llgo/c/bdwgc" ) // ----------------------------------------------------------------------------- @@ -32,7 +31,7 @@ type StringView = string // String represents a C++ std::string object. type String struct { - Unused [24]byte + Unused [3]uintptr } // llgo:link (*String).InitEmpty C.stdStringInitEmpty @@ -52,14 +51,6 @@ func (s *String) Dispose() {} // ----------------------------------------------------------------------------- -func allocString() *String { - ptr := bdwgc.Malloc(unsafe.Sizeof(String{})) - bdwgc.RegisterFinalizer(ptr, func(obj, data c.Pointer) { - (*String)(obj).Dispose() - }, nil, nil, nil) - return (*String)(ptr) -} - // NewString creates a C++ std::string object. func NewString(v string) *String { ret := allocString() From b1cc732cb2f852fb644ec0b4d37dcf43ad969401 Mon Sep 17 00:00:00 2001 From: hackerchai Date: Mon, 29 Jul 2024 09:59:46 +0800 Subject: [PATCH 070/152] refactor(c/libuv): Rename UvPipe & remove unused structs Signed-off-by: hackerchai --- c/libuv/libuv.go | 36 ++---------------------------------- 1 file changed, 2 insertions(+), 34 deletions(-) diff --git a/c/libuv/libuv.go b/c/libuv/libuv.go index 47ae2573..9465f00e 100644 --- a/c/libuv/libuv.go +++ b/c/libuv/libuv.go @@ -101,46 +101,14 @@ type Handle struct { Unused [96]byte } -type Dir struct { - Unused [0]byte -} - type Stream struct { Unused [264]byte } -type Pipe struct { - Unused [0]byte -} - -type Tty struct { - Unused [0]byte -} - type Poll struct { Unused [0]byte } -type Prepare struct { - Unused [0]byte -} - -type Check struct { - Unused [0]byte -} - -type Idle struct { - Unused [0]byte -} - -type Async struct { - Unused [0]byte -} - -type Process struct { - Unused [0]byte -} - /* Request types. */ type Req struct { @@ -296,8 +264,8 @@ func (handle *Handle) Fileno(fd *OsFd) c.Int { return 0 } -//go:linkname UvPipe C.uv_pipe -func UvPipe(fds [2]File, readFlags c.Int, writeFlags c.Int) c.Int { +//go:linkname Pipe C.uv_pipe +func Pipe(fds [2]File, readFlags c.Int, writeFlags c.Int) c.Int { return 0 } From cbd891785ebfcf058c1ed4ca09096130f312fab4 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Mon, 29 Jul 2024 11:06:33 +0800 Subject: [PATCH 071/152] library: os.Expand --- _cmptest/envexpand/expand.go | 20 ++++++++++++++++++++ internal/lib/os/env.go | 2 -- 2 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 _cmptest/envexpand/expand.go diff --git a/_cmptest/envexpand/expand.go b/_cmptest/envexpand/expand.go new file mode 100644 index 00000000..0c4baaf0 --- /dev/null +++ b/_cmptest/envexpand/expand.go @@ -0,0 +1,20 @@ +package main + +import ( + "fmt" + "os" +) + +func main() { + mapper := func(placeholderName string) string { + switch placeholderName { + case "DAY_PART": + return "morning" + case "NAME": + return "Gopher" + } + return "" + } + + fmt.Println(os.Expand("Good ${DAY_PART}, $NAME!", mapper)) +} diff --git a/internal/lib/os/env.go b/internal/lib/os/env.go index 7c0f3174..550dac19 100644 --- a/internal/lib/os/env.go +++ b/internal/lib/os/env.go @@ -10,7 +10,6 @@ import ( "syscall" ) -/* TODO(xsw): // Expand replaces ${var} or $var in the string based on the mapping function. // For example, os.ExpandEnv(s) is equivalent to os.Expand(s, os.Getenv). func Expand(s string, mapping func(string) string) string { @@ -94,7 +93,6 @@ func getShellName(s string) (string, int) { } return s[:i], i } -*/ // Getenv retrieves the value of the environment variable named by the key. // It returns the value, which will be empty if the variable is not present. From e7d72b6f5303dbd0184980f00fd26ea9503bf816 Mon Sep 17 00:00:00 2001 From: Aofei Sheng Date: Mon, 29 Jul 2024 08:58:13 +0800 Subject: [PATCH 072/152] build: fix `sigsetjmp` issues and ensure required libs on Linux 1. Handle `sigsetjmp` platform differences: - Separate `sigsetjmp` linkage to platform-specific files. - Use `__sigsetjmp` on Linux to handle `sigsetjmp` being a macro. - Maintain original implementation for Darwin. 2. Ensure linking of required libs: - Explicitly link against fundamental libs (e.g., libm, libatomic). - Address the fact that typical Linux linkers don't link these by default. --- c/setjmp/setjmp.go | 3 --- c/setjmp/setjmp_linux.go | 12 ++++++++++++ c/setjmp/setjmp_other.go | 12 ++++++++++++ internal/build/build.go | 2 ++ 4 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 c/setjmp/setjmp_linux.go create mode 100644 c/setjmp/setjmp_other.go diff --git a/c/setjmp/setjmp.go b/c/setjmp/setjmp.go index 89c6745a..c4bccb18 100644 --- a/c/setjmp/setjmp.go +++ b/c/setjmp/setjmp.go @@ -44,9 +44,6 @@ func Longjmp(env *JmpBuf, val c.Int) // ----------------------------------------------------------------------------- -//go:linkname Sigsetjmp C.sigsetjmp -func Sigsetjmp(env *SigjmpBuf, savemask c.Int) c.Int - //go:linkname Siglongjmp C.siglongjmp func Siglongjmp(env *SigjmpBuf, val c.Int) diff --git a/c/setjmp/setjmp_linux.go b/c/setjmp/setjmp_linux.go new file mode 100644 index 00000000..17bad916 --- /dev/null +++ b/c/setjmp/setjmp_linux.go @@ -0,0 +1,12 @@ +//go:build linux + +package setjmp + +import ( + _ "unsafe" + + "github.com/goplus/llgo/c" +) + +//go:linkname Sigsetjmp C.__sigsetjmp +func Sigsetjmp(env *SigjmpBuf, savemask c.Int) c.Int diff --git a/c/setjmp/setjmp_other.go b/c/setjmp/setjmp_other.go new file mode 100644 index 00000000..a83d513d --- /dev/null +++ b/c/setjmp/setjmp_other.go @@ -0,0 +1,12 @@ +//go:build !linux + +package setjmp + +import ( + _ "unsafe" + + "github.com/goplus/llgo/c" +) + +//go:linkname Sigsetjmp C.sigsetjmp +func Sigsetjmp(env *SigjmpBuf, savemask c.Int) c.Int diff --git a/internal/build/build.go b/internal/build/build.go index 6de1e0c4..aa3b8b89 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -356,6 +356,8 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, llFiles "-rpath", "$ORIGIN", "-rpath", "$ORIGIN/../lib", "-Xlinker", "--gc-sections", + "-lm", + "-latomic", "-lpthread", // libpthread is built-in since glibc 2.34 (2021-08-01); we need to support earlier versions. ) } From e0e3664fdb9ab1a79c45ccf116560f35509e8832 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Mon, 29 Jul 2024 11:44:49 +0800 Subject: [PATCH 073/152] cmptest: regexdemo (todo) --- _cmptest/_regexdemo/regex.go | 11 +++++++++++ cmd/internal/version/version.go | 2 +- {xtool => x}/env/build.go | 0 {xtool => x}/env/version.go | 0 xtool/env/env.go | 3 --- 5 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 _cmptest/_regexdemo/regex.go rename {xtool => x}/env/build.go (100%) rename {xtool => x}/env/version.go (100%) diff --git a/_cmptest/_regexdemo/regex.go b/_cmptest/_regexdemo/regex.go new file mode 100644 index 00000000..7153a2e9 --- /dev/null +++ b/_cmptest/_regexdemo/regex.go @@ -0,0 +1,11 @@ +package main + +import ( + "fmt" + + "github.com/goplus/llgo/xtool/env" +) + +func main() { + fmt.Println(env.ExpandEnv("$(pkg-config --libs bdw-gc)")) +} diff --git a/cmd/internal/version/version.go b/cmd/internal/version/version.go index f6fc6eb4..1a6adffc 100644 --- a/cmd/internal/version/version.go +++ b/cmd/internal/version/version.go @@ -21,7 +21,7 @@ import ( "runtime" "github.com/goplus/llgo/cmd/internal/base" - "github.com/goplus/llgo/xtool/env" + "github.com/goplus/llgo/x/env" ) // llgo version diff --git a/xtool/env/build.go b/x/env/build.go similarity index 100% rename from xtool/env/build.go rename to x/env/build.go diff --git a/xtool/env/version.go b/x/env/version.go similarity index 100% rename from xtool/env/version.go rename to x/env/version.go diff --git a/xtool/env/env.go b/xtool/env/env.go index 062c043f..4285deb3 100644 --- a/xtool/env/env.go +++ b/xtool/env/env.go @@ -36,11 +36,8 @@ func ExpandEnv(s string) string { func expandEnvWithCmd(s string) string { expanded := reSubcmd.ReplaceAllStringFunc(s, func(m string) string { subcmd := strings.TrimSpace(m[2 : len(m)-1]) - args := parseSubcmd(subcmd) - cmd := args[0] - if cmd != "pkg-config" && cmd != "llvm-config" { fmt.Fprintf(os.Stderr, "expand cmd only support pkg-config and llvm-config: '%s'\n", subcmd) return "" From 82aa181540305b7aadd592fec562413aa64e06fb Mon Sep 17 00:00:00 2001 From: Aofei Sheng Date: Mon, 29 Jul 2024 14:14:43 +0800 Subject: [PATCH 074/152] installation: require Python 3.12 --- .github/workflows/go.yml | 15 ++++++++------- README.md | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 1c1abb91..e373b9d2 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -14,7 +14,7 @@ jobs: test: strategy: matrix: - os: [macos-latest, ubuntu-latest] + os: [macos-latest, ubuntu-24.04] # os: [macos-latest] llvm: [18] runs-on: ${{ matrix.os }} @@ -36,15 +36,16 @@ jobs: echo "deb http://apt.llvm.org/$(lsb_release -cs)/ llvm-toolchain-$(lsb_release -cs)-${{ matrix.llvm }} main" | sudo tee /etc/apt/sources.list.d/llvm.list wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - sudo apt-get update - sudo apt-get install -y llvm-${{ matrix.llvm }}-dev clang-${{ matrix.llvm }} lld-${{ matrix.llvm }} pkg-config libgc-dev libcjson-dev libsqlite3-dev python3.11-dev + sudo apt-get install -y llvm-${{ matrix.llvm }}-dev clang-${{ matrix.llvm }} lld-${{ matrix.llvm }} pkg-config libgc-dev libcjson-dev libsqlite3-dev python3.12-dev + echo /usr/lib/llvm-${{ matrix.llvm }}/bin >> $GITHUB_PATH - + - name: Clang information run: | echo $PATH which clang clang --version - + - name: Set up Go uses: actions/setup-go@v5 with: @@ -63,19 +64,19 @@ jobs: - name: Install run: go install ./... - + - name: LLGO tests if: matrix.os != 'ubuntu-latest' run: | echo "Test result on ${{ matrix.os }} with LLVM ${{ matrix.llvm }}" > result.md LLGOROOT=$PWD bash .github/workflows/test_llgo.sh - + - name: Test _demo and _pydemo run: | set +e LLGOROOT=$PWD bash .github/workflows/test_demo.sh exit 0 - + - name: Show test result run: cat result.md diff --git a/README.md b/README.md index 50338c10..2f03e518 100644 --- a/README.md +++ b/README.md @@ -302,7 +302,7 @@ Here are the Go packages that can be imported correctly: - [bdwgc/libgc 8.0+](https://www.hboehm.info/gc/) - [cJSON 1.7+](https://github.com/DaveGamble/cJSON) (optional, for [github.com/goplus/llgo/c/cjson](https://pkg.go.dev/github.com/goplus/llgo/c/cjson)) - [SQLite 3](https://www.sqlite.org) (optional, for [github.com/goplus/llgo/c/sqlite](https://pkg.go.dev/github.com/goplus/llgo/c/sqlite)) -- [Python 3.11+](https://www.python.org) (optional, for [github.com/goplus/llgo/py](https://pkg.go.dev/github.com/goplus/llgo/py)) +- [Python 3.12+](https://www.python.org) (optional, for [github.com/goplus/llgo/py](https://pkg.go.dev/github.com/goplus/llgo/py)) ## How to install From 2d5e991eaf983db39ed27e2bc2e213b78888ee38 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Mon, 29 Jul 2024 16:31:59 +0800 Subject: [PATCH 075/152] library: bytes, regexp, regexp/syntax --- README.md | 5 ++++- _cmptest/{_regexdemo => regexdemo}/regex.go | 0 internal/lib/sync/pool.go | 6 +++++- 3 files changed, 9 insertions(+), 2 deletions(-) rename _cmptest/{_regexdemo => regexdemo}/regex.go (100%) diff --git a/README.md b/README.md index 50338c10..157df89f 100644 --- a/README.md +++ b/README.md @@ -277,8 +277,9 @@ Here are the Go packages that can be imported correctly: * [log](https://pkg.go.dev/log) * [flag](https://pkg.go.dev/flag) * [sort](https://pkg.go.dev/sort) -* [strconv](https://pkg.go.dev/strconv) +* [bytes](https://pkg.go.dev/bytes) * [strings](https://pkg.go.dev/strings) +* [strconv](https://pkg.go.dev/strconv) * [path](https://pkg.go.dev/path) * [path/filepath](https://pkg.go.dev/path/filepath) * [sync/atomic](https://pkg.go.dev/sync/atomic) @@ -290,6 +291,8 @@ Here are the Go packages that can be imported correctly: * [fmt](https://pkg.go.dev/fmt) (partially) * [reflect](https://pkg.go.dev/reflect) (partially) * [time](https://pkg.go.dev/time) (partially) +* [regexp](https://pkg.go.dev/regexp) +* [regexp/syntax](https://pkg.go.dev/regexp/syntax) ## Dependencies diff --git a/_cmptest/_regexdemo/regex.go b/_cmptest/regexdemo/regex.go similarity index 100% rename from _cmptest/_regexdemo/regex.go rename to _cmptest/regexdemo/regex.go diff --git a/internal/lib/sync/pool.go b/internal/lib/sync/pool.go index 98d5a11e..6d247ea9 100644 --- a/internal/lib/sync/pool.go +++ b/internal/lib/sync/pool.go @@ -24,6 +24,10 @@ type Pool struct { func (p *Pool) Put(x any) { } +// TODO(xsw): func (p *Pool) Get() any { - return p.New() + if p.New != nil { + return p.New() + } + return nil } From 3b5e8edd37580caa0f428f56f29acfbae50077e1 Mon Sep 17 00:00:00 2001 From: Aofei Sheng Date: Mon, 29 Jul 2024 11:46:08 +0800 Subject: [PATCH 076/152] ssa: fix `Builder.Sigsetjmp` for Linux This follows up on #597. --- internal/build/build.go | 8 +++++++- ssa/eh.go | 6 +++++- ssa/package.go | 6 +++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/internal/build/build.go b/internal/build/build.go index aa3b8b89..8854fb36 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -20,6 +20,7 @@ import ( "debug/macho" "fmt" "go/ast" + "go/build" "go/constant" "go/token" "go/types" @@ -135,7 +136,12 @@ func Do(args []string, conf *Config) { llssa.Initialize(llssa.InitAll) - prog := llssa.NewProgram(nil) + target := &llssa.Target{ + GOOS: build.Default.GOOS, + GOARCH: build.Default.GOARCH, + } + + prog := llssa.NewProgram(target) sizes := prog.TypeSizes dedup := packages.NewDeduper() dedup.SetPreload(func(pkg *types.Package, files []*ast.File) { diff --git a/ssa/eh.go b/ssa/eh.go index 2e996101..a7f42a59 100644 --- a/ssa/eh.go +++ b/ssa/eh.go @@ -63,7 +63,11 @@ func (b Builder) AllocaSigjmpBuf() Expr { } func (b Builder) Sigsetjmp(jb, savemask Expr) Expr { - fn := b.Pkg.cFunc("sigsetjmp", b.Prog.tySigsetjmp()) + fname := "sigsetjmp" + if b.Prog.target.GOOS == "linux" { + fname = "__sigsetjmp" + } + fn := b.Pkg.cFunc(fname, b.Prog.tySigsetjmp()) return b.Call(fn, jb, savemask) } diff --git a/ssa/package.go b/ssa/package.go index 382ef365..c519cc14 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -19,6 +19,7 @@ package ssa import ( "go/token" "go/types" + "runtime" "strconv" "unsafe" @@ -203,7 +204,10 @@ type Program = *aProgram // NewProgram creates a new program. func NewProgram(target *Target) Program { if target == nil { - target = &Target{} + target = &Target{ + GOOS: runtime.GOOS, + GOARCH: runtime.GOARCH, + } } ctx := llvm.NewContext() td := target.targetData() // TODO(xsw): target config From f16f16c15edc33822fa1d5f2f8a131542c0734c0 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Mon, 29 Jul 2024 19:50:23 +0800 Subject: [PATCH 077/152] Update How-to-support-a-C&C++-Library.md --- doc/How-to-support-a-C&C++-Library.md | 35 +++++++++++++++------------ 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/doc/How-to-support-a-C&C++-Library.md b/doc/How-to-support-a-C&C++-Library.md index fc2e71d0..b0b9bca6 100644 --- a/doc/How-to-support-a-C&C++-Library.md +++ b/doc/How-to-support-a-C&C++-Library.md @@ -1,6 +1,9 @@ -# Support a C Library +How to support a C/C++ Library +===== -## Install a C Library +## Support a C Library + +### Install a C Library We recommend using a package manager (such as brew, apt-get, winget, etc.) to install a C library. For example: @@ -8,7 +11,7 @@ We recommend using a package manager (such as brew, apt-get, winget, etc.) to in brew install inih ``` -## Writing Go Files to Link Library Functions +### Writing Go Files to Link Library Functions 1. On macOS, use `nm -gU libbar.dylib` to parse C-style symbols @@ -85,9 +88,9 @@ cd inih/_demo/inih_demo llgo run . ``` -## Handling Special Types +### Handling Special Types -### Handling Enum Values in C +#### Handling Enum Values in C Use const to implement enum values @@ -119,7 +122,7 @@ const ( ) ``` -### Handling Structs in C +#### Handling Structs in C ```go // If you need to use class member variables, like llgo/c/raylib @@ -176,7 +179,7 @@ func (o *JSON) AddItem(item *JSON) c.Int { return 0 } For the size of Unused, if the methods bound to the structure do not need to create objects, i.e., the receiver of the Go methods bound to this structure is of pointer type, you can declare `Unused [0]byte`. Otherwise, you need to write a simple C file using the `sizeof` operator to calculate the size of the structure. Assuming the structure size is 38 bytes, then declare `Unused [38]byte`. -### Handling Function Pointers in C +#### Handling Function Pointers in C ```go // Convert function pointers to Go style and then declare function pointer types using aliases @@ -186,7 +189,7 @@ type Comp func(a c.Int) ``` -### Handling char ** Type in C +#### Handling char ** Type in C Handle char ** as `[]*c.Char` @@ -218,15 +221,15 @@ func main() { } ``` -# LLGO for C++ Third-Party Libraries +## LLGO for C++ Third-Party Libraries Using the C++ part of the inih library as an example -## Installation +### Installation Same as installing C libraries -## File Structure +### File Structure After migrating the C part of the inih library, just continue creating files in the same directory. @@ -242,9 +245,9 @@ inih/ └── reader.go ``` -## Writing Go Files to Link Library Functions +### Writing Go Files to Link Library Functions -### Migrating Ordinary Functions +#### Migrating Ordinary Functions Since the inih library does not have C++ style ordinary functions, we'll use an ordinary method of a class as an example. The specific process is the same. @@ -283,7 +286,7 @@ For functions, generally use `go:linkname` to link. Here, refer to the migration func ParseError() c.Int ``` -### Migrating Classes +#### Migrating Classes - Use a struct to map the class. The writing method is the same as migrating a struct in the C library migration: @@ -354,7 +357,7 @@ func ParseError() c.Int defer reader.Dispose() ``` -### Templates and Inlines +#### Templates and Inlines Templates or inlines do not generate symbols in dynamic libraries (dylib) (default constructors and destructors). To ensure that you can use C style symbols to link template or inline functions, create a C++ file and wrap it with `extern "C"`, then bind the functions directly in Go. @@ -381,7 +384,7 @@ LLGoFiles = "$(pkg-config --cflags spdlog): cppWrap/cppWrap.cpp" LLGoPackage = "link: $(pkg-config --libs spdlog); -lspdlog -pthread -lfmt") ``` -## Writing and Running the Demo +### Writing and Running the Demo ```go package main From dc6aa66f9a229e31bc5c156b0f91ec5715205d96 Mon Sep 17 00:00:00 2001 From: visualfc Date: Mon, 29 Jul 2024 13:53:56 +0800 Subject: [PATCH 078/152] ssa: closure changeType check convert --- cl/_testgo/closure/out.ll | 24 ++-- cl/_testgo/invoke/out.ll | 10 +- cl/_testrt/closureconv/in.go | 63 ++++++++++ cl/_testrt/closureconv/out.ll | 220 ++++++++++++++++++++++++++++++++++ ssa/expr.go | 28 ++++- ssa/package.go | 2 +- 6 files changed, 328 insertions(+), 19 deletions(-) create mode 100644 cl/_testrt/closureconv/in.go create mode 100644 cl/_testrt/closureconv/out.ll diff --git a/cl/_testgo/closure/out.ll b/cl/_testgo/closure/out.ll index b387030b..652eefd1 100644 --- a/cl/_testgo/closure/out.ll +++ b/cl/_testgo/closure/out.ll @@ -2,6 +2,7 @@ source_filename = "main" %"github.com/goplus/llgo/internal/runtime.String" = type { ptr, i64 } +%main.T = type { ptr, ptr } @"main.init$guard" = global i1 false, align 1 @__llgo_argc = global i32 0, align 4 @@ -37,12 +38,12 @@ _llgo_0: store i64 3, ptr %5, align 4 %6 = load %"github.com/goplus/llgo/internal/runtime.String", ptr %3, align 8 store %"github.com/goplus/llgo/internal/runtime.String" %6, ptr %2, align 8 - %7 = alloca { ptr, ptr }, align 8 - %8 = getelementptr inbounds { ptr, ptr }, ptr %7, i32 0, i32 0 + %7 = alloca %main.T, align 8 + %8 = getelementptr inbounds %main.T, ptr %7, i32 0, i32 0 store ptr @"__llgo_stub.main.main$1", ptr %8, align 8 - %9 = getelementptr inbounds { ptr, ptr }, ptr %7, i32 0, i32 1 + %9 = getelementptr inbounds %main.T, ptr %7, i32 0, i32 1 store ptr null, ptr %9, align 8 - %10 = load { ptr, ptr }, ptr %7, align 8 + %10 = load %main.T, ptr %7, align 8 %11 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 8) %12 = getelementptr inbounds { ptr }, ptr %11, i32 0, i32 0 store ptr %2, ptr %12, align 8 @@ -52,12 +53,15 @@ _llgo_0: %15 = getelementptr inbounds { ptr, ptr }, ptr %13, i32 0, i32 1 store ptr %11, ptr %15, align 8 %16 = load { ptr, ptr }, ptr %13, align 8 - %17 = extractvalue { ptr, ptr } %10, 1 - %18 = extractvalue { ptr, ptr } %10, 0 - call void %18(ptr %17, i64 100) - %19 = extractvalue { ptr, ptr } %16, 1 - %20 = extractvalue { ptr, ptr } %16, 0 - call void %20(ptr %19, i64 200) + %17 = alloca %main.T, align 8 + store { ptr, ptr } %16, ptr %17, align 8 + %18 = load %main.T, ptr %17, align 8 + %19 = extractvalue %main.T %10, 1 + %20 = extractvalue %main.T %10, 0 + call void %20(ptr %19, i64 100) + %21 = extractvalue %main.T %18, 1 + %22 = extractvalue %main.T %18, 0 + call void %22(ptr %21, i64 200) ret i32 0 } diff --git a/cl/_testgo/invoke/out.ll b/cl/_testgo/invoke/out.ll index 03a2ae86..b1222dc0 100644 --- a/cl/_testgo/invoke/out.ll +++ b/cl/_testgo/invoke/out.ll @@ -304,13 +304,13 @@ _llgo_0: %14 = getelementptr inbounds %main.T5, ptr %13, i32 0, i32 0 store i64 300, ptr %14, align 4 %15 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 16) - %16 = alloca { ptr, ptr }, align 8 - %17 = getelementptr inbounds { ptr, ptr }, ptr %16, i32 0, i32 0 + %16 = alloca %main.T6, align 8 + %17 = getelementptr inbounds %main.T6, ptr %16, i32 0, i32 0 store ptr @"__llgo_stub.main.main$1", ptr %17, align 8 - %18 = getelementptr inbounds { ptr, ptr }, ptr %16, i32 0, i32 1 + %18 = getelementptr inbounds %main.T6, ptr %16, i32 0, i32 1 store ptr null, ptr %18, align 8 - %19 = load { ptr, ptr }, ptr %16, align 8 - store { ptr, ptr } %19, ptr %15, align 8 + %19 = load %main.T6, ptr %16, align 8 + store %main.T6 %19, ptr %15, align 8 %20 = load %main.T, ptr %2, align 8 %21 = load ptr, ptr @_llgo_main.T, align 8 %22 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 16) diff --git a/cl/_testrt/closureconv/in.go b/cl/_testrt/closureconv/in.go new file mode 100644 index 00000000..0b80ae37 --- /dev/null +++ b/cl/_testrt/closureconv/in.go @@ -0,0 +1,63 @@ +package main + +type Func func(a int, b int) int +type Func2 func(a int, b int) int + +type Call struct { + fn Func + n int +} + +func (c *Call) add(a int, b int) int { + return a + b + c.n +} + +func add(a int, b int) int { + return a + b +} + +func demo1(n int) Func { + m := &Call{n: n} + m.fn = m.add + return m.fn +} + +func demo2() Func { + m := &Call{} + return m.add +} + +func demo3() Func { + return add +} + +func demo4() Func { + return func(a, b int) int { return a + b } +} + +func demo5(n int) Func { + return func(a, b int) int { return a + b + n } +} + +func main() { + n1 := demo1(1)(99, 200) + println(n1) + + n2 := demo2()(100, 200) + println(n2) + + n3 := demo3()(100, 200) + println(n3) + + n4 := demo4()(100, 200) + println(n4) + + n5 := demo5(1)(99, 200) + println(n5) + + var fn func(a int, b int) int = demo5(1) + println(fn(99, 200)) + + var fn2 Func2 = (Func2)(demo5(1)) + println(fn2(99, 200)) +} diff --git a/cl/_testrt/closureconv/out.ll b/cl/_testrt/closureconv/out.ll new file mode 100644 index 00000000..f69440d5 --- /dev/null +++ b/cl/_testrt/closureconv/out.ll @@ -0,0 +1,220 @@ +; ModuleID = 'main' +source_filename = "main" + +%main.Call = type { %main.Func, i64 } +%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 i64 @"main.(*Call).add"(ptr %0, i64 %1, i64 %2) { +_llgo_0: + %3 = add i64 %1, %2 + %4 = getelementptr inbounds %main.Call, ptr %0, i32 0, i32 1 + %5 = load i64, ptr %4, align 4 + %6 = add i64 %3, %5 + ret i64 %6 +} + +define i64 @main.add(i64 %0, i64 %1) { +_llgo_0: + %2 = add i64 %0, %1 + ret i64 %2 +} + +define %main.Func @main.demo1(i64 %0) { +_llgo_0: + %1 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 24) + %2 = getelementptr inbounds %main.Call, ptr %1, i32 0, i32 1 + store i64 %0, ptr %2, align 4 + %3 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 8) + %4 = getelementptr inbounds { ptr }, ptr %3, i32 0, i32 0 + store ptr %1, ptr %4, align 8 + %5 = alloca { ptr, ptr }, align 8 + %6 = getelementptr inbounds { ptr, ptr }, ptr %5, i32 0, i32 0 + store ptr @"main.add$bound", ptr %6, align 8 + %7 = getelementptr inbounds { ptr, ptr }, ptr %5, i32 0, i32 1 + store ptr %3, ptr %7, align 8 + %8 = load { ptr, ptr }, ptr %5, align 8 + %9 = getelementptr inbounds %main.Call, ptr %1, i32 0, i32 0 + %10 = alloca %main.Func, align 8 + store { ptr, ptr } %8, ptr %10, align 8 + %11 = load %main.Func, ptr %10, align 8 + store %main.Func %11, ptr %9, align 8 + %12 = getelementptr inbounds %main.Call, ptr %1, i32 0, i32 0 + %13 = load %main.Func, ptr %12, align 8 + ret %main.Func %13 +} + +define %main.Func @main.demo2() { +_llgo_0: + %0 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 24) + %1 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 8) + %2 = getelementptr inbounds { ptr }, ptr %1, i32 0, i32 0 + store ptr %0, ptr %2, align 8 + %3 = alloca { ptr, ptr }, align 8 + %4 = getelementptr inbounds { ptr, ptr }, ptr %3, i32 0, i32 0 + store ptr @"main.add$bound", ptr %4, align 8 + %5 = getelementptr inbounds { ptr, ptr }, ptr %3, i32 0, i32 1 + store ptr %1, ptr %5, align 8 + %6 = load { ptr, ptr }, ptr %3, align 8 + %7 = alloca %main.Func, align 8 + store { ptr, ptr } %6, ptr %7, align 8 + %8 = load %main.Func, ptr %7, align 8 + ret %main.Func %8 +} + +define %main.Func @main.demo3() { +_llgo_0: + %0 = alloca %main.Func, align 8 + %1 = getelementptr inbounds %main.Func, ptr %0, i32 0, i32 0 + store ptr @__llgo_stub.main.add, ptr %1, align 8 + %2 = getelementptr inbounds %main.Func, ptr %0, i32 0, i32 1 + store ptr null, ptr %2, align 8 + %3 = load %main.Func, ptr %0, align 8 + ret %main.Func %3 +} + +define %main.Func @main.demo4() { +_llgo_0: + %0 = alloca %main.Func, align 8 + %1 = getelementptr inbounds %main.Func, ptr %0, i32 0, i32 0 + store ptr @"__llgo_stub.main.demo4$1", ptr %1, align 8 + %2 = getelementptr inbounds %main.Func, ptr %0, i32 0, i32 1 + store ptr null, ptr %2, align 8 + %3 = load %main.Func, ptr %0, align 8 + ret %main.Func %3 +} + +define i64 @"main.demo4$1"(i64 %0, i64 %1) { +_llgo_0: + %2 = add i64 %0, %1 + ret i64 %2 +} + +define %main.Func @main.demo5(i64 %0) { +_llgo_0: + %1 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64 8) + store i64 %0, ptr %1, align 4 + %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.demo5$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 = alloca %main.Func, align 8 + store { ptr, ptr } %7, ptr %8, align 8 + %9 = load %main.Func, ptr %8, align 8 + ret %main.Func %9 +} + +define i64 @"main.demo5$1"(ptr %0, i64 %1, i64 %2) { +_llgo_0: + %3 = add i64 %1, %2 + %4 = load { ptr }, ptr %0, align 8 + %5 = extractvalue { ptr } %4, 0 + %6 = load i64, ptr %5, align 4 + %7 = add i64 %3, %6 + ret i64 %7 +} + +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 = call %main.Func @main.demo1(i64 1) + %3 = extractvalue %main.Func %2, 1 + %4 = extractvalue %main.Func %2, 0 + %5 = call i64 %4(ptr %3, i64 99, i64 200) + call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %5) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + %6 = call %main.Func @main.demo2() + %7 = extractvalue %main.Func %6, 1 + %8 = extractvalue %main.Func %6, 0 + %9 = call i64 %8(ptr %7, i64 100, i64 200) + call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %9) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + %10 = call %main.Func @main.demo3() + %11 = extractvalue %main.Func %10, 1 + %12 = extractvalue %main.Func %10, 0 + %13 = call i64 %12(ptr %11, i64 100, i64 200) + call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %13) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + %14 = call %main.Func @main.demo4() + %15 = extractvalue %main.Func %14, 1 + %16 = extractvalue %main.Func %14, 0 + %17 = call i64 %16(ptr %15, i64 100, i64 200) + call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %17) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + %18 = call %main.Func @main.demo5(i64 1) + %19 = extractvalue %main.Func %18, 1 + %20 = extractvalue %main.Func %18, 0 + %21 = call i64 %20(ptr %19, i64 99, i64 200) + call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %21) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + %22 = call %main.Func @main.demo5(i64 1) + %23 = alloca { ptr, ptr }, align 8 + store %main.Func %22, ptr %23, align 8 + %24 = load { ptr, ptr }, ptr %23, align 8 + %25 = extractvalue { ptr, ptr } %24, 1 + %26 = extractvalue { ptr, ptr } %24, 0 + %27 = call i64 %26(ptr %25, i64 99, i64 200) + call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %27) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + %28 = call %main.Func @main.demo5(i64 1) + %29 = extractvalue %main.Func %28, 1 + %30 = extractvalue %main.Func %28, 0 + %31 = call i64 %30(ptr %29, i64 99, i64 200) + call void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64 %31) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + ret i32 0 +} + +declare ptr @"github.com/goplus/llgo/internal/runtime.AllocZ"(i64) + +define i64 @"main.add$bound"(ptr %0, i64 %1, i64 %2) { +_llgo_0: + %3 = load { ptr }, ptr %0, align 8 + %4 = extractvalue { ptr } %3, 0 + %5 = call i64 @"main.(*Call).add"(ptr %4, i64 %1, i64 %2) + ret i64 %5 +} + +declare ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64) + +define linkonce i64 @__llgo_stub.main.add(ptr %0, i64 %1, i64 %2) { +_llgo_0: + %3 = tail call i64 @main.add(i64 %1, i64 %2) + ret i64 %3 +} + +define linkonce i64 @"__llgo_stub.main.demo4$1"(ptr %0, i64 %1, i64 %2) { +_llgo_0: + %3 = tail call i64 @"main.demo4$1"(i64 %1, i64 %2) + ret i64 %3 +} + +declare void @"github.com/goplus/llgo/internal/runtime.init"() + +declare void @"github.com/goplus/llgo/internal/runtime.PrintInt"(i64) + +declare void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8) diff --git a/ssa/expr.go b/ssa/expr.go index 46dd202f..9400958a 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -694,8 +694,30 @@ func (b Builder) ChangeType(t Type, x Expr) (ret Expr) { if debugInstr { log.Printf("ChangeType %v, %v\n", t.RawType(), x.impl) } - if t.kind == vkClosure && x.kind == vkFuncDecl { - ret.impl = checkExpr(x, t.raw.Type.Underlying(), b).impl + if t.kind == vkClosure { + switch x.kind { + case vkFuncDecl: + ret.impl = checkExpr(x, t.raw.Type, b).impl + case vkClosure: + convType := func() Expr { + r := Expr{llvm.CreateAlloca(b.impl, t.ll), b.Prog.Pointer(t)} + b.Store(r, x) + return b.Load(r) + } + switch t.RawType().(type) { + case *types.Named: + if _, ok := x.RawType().(*types.Struct); ok { + return convType() + } + case *types.Struct: + if _, ok := x.RawType().(*types.Named); ok { + return convType() + } + } + fallthrough + default: + ret.impl = x.impl + } } else { ret.impl = x.impl } @@ -1172,7 +1194,7 @@ func (b Builder) PrintEx(ln bool, args ...Expr) (ret Expr) { // ----------------------------------------------------------------------------- func checkExpr(v Expr, t types.Type, b Builder) Expr { - if t, ok := t.(*types.Struct); ok && isClosure(t) { + if st, ok := t.Underlying().(*types.Struct); ok && isClosure(st) { if v.kind != vkClosure { return b.Pkg.closureStub(b, t, v) } diff --git a/ssa/package.go b/ssa/package.go index c519cc14..02b0e360 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -608,7 +608,7 @@ const ( closureStub = "__llgo_stub." ) -func (p Package) closureStub(b Builder, t *types.Struct, v Expr) Expr { +func (p Package) closureStub(b Builder, t types.Type, v Expr) Expr { name := v.impl.Name() prog := b.Prog nilVal := prog.Nil(prog.VoidPtr()).impl From afdf31a66c666870edcd407841451719e4a41df2 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Tue, 30 Jul 2024 00:28:04 +0800 Subject: [PATCH 079/152] library: encoding/{binary, base64} --- README.md | 2 ++ _cmptest/base64demo/base64.go | 18 ++++++++++++++++++ ssa/expr.go | 1 + 3 files changed, 21 insertions(+) create mode 100644 _cmptest/base64demo/base64.go diff --git a/README.md b/README.md index fed235f6..a1432878 100644 --- a/README.md +++ b/README.md @@ -291,6 +291,8 @@ Here are the Go packages that can be imported correctly: * [fmt](https://pkg.go.dev/fmt) (partially) * [reflect](https://pkg.go.dev/reflect) (partially) * [time](https://pkg.go.dev/time) (partially) +* [encoding/binary](https://pkg.go.dev/encoding/binary) +* [encoding/base64](https://pkg.go.dev/encoding/base64) * [regexp](https://pkg.go.dev/regexp) * [regexp/syntax](https://pkg.go.dev/regexp/syntax) diff --git a/_cmptest/base64demo/base64.go b/_cmptest/base64demo/base64.go new file mode 100644 index 00000000..22728ba9 --- /dev/null +++ b/_cmptest/base64demo/base64.go @@ -0,0 +1,18 @@ +package main + +import ( + "encoding/base64" + "fmt" +) + +func main() { + msg := "Hello, 世界" + encoded := base64.StdEncoding.EncodeToString([]byte(msg)) + fmt.Println(encoded) + decoded, err := base64.StdEncoding.DecodeString(encoded) + if err != nil { + fmt.Println("decode error:", err) + return + } + fmt.Println(string(decoded)) +} diff --git a/ssa/expr.go b/ssa/expr.go index 9400958a..263ebb7a 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -699,6 +699,7 @@ func (b Builder) ChangeType(t Type, x Expr) (ret Expr) { case vkFuncDecl: ret.impl = checkExpr(x, t.raw.Type, b).impl case vkClosure: + // TODO(xsw): change type should be a noop instruction convType := func() Expr { r := Expr{llvm.CreateAlloca(b.impl, t.ll), b.Prog.Pointer(t)} b.Store(r, x) From ad1a42d6a5c3369b7eded41be6c2f6239b9440a2 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Tue, 30 Jul 2024 00:30:58 +0800 Subject: [PATCH 080/152] library: encoding/base32 --- README.md | 1 + _cmptest/base64demo/base64.go | 20 +++++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a1432878..a8ff2825 100644 --- a/README.md +++ b/README.md @@ -292,6 +292,7 @@ Here are the Go packages that can be imported correctly: * [reflect](https://pkg.go.dev/reflect) (partially) * [time](https://pkg.go.dev/time) (partially) * [encoding/binary](https://pkg.go.dev/encoding/binary) +* [encoding/base32](https://pkg.go.dev/encoding/base32) * [encoding/base64](https://pkg.go.dev/encoding/base64) * [regexp](https://pkg.go.dev/regexp) * [regexp/syntax](https://pkg.go.dev/regexp/syntax) diff --git a/_cmptest/base64demo/base64.go b/_cmptest/base64demo/base64.go index 22728ba9..e80a6f42 100644 --- a/_cmptest/base64demo/base64.go +++ b/_cmptest/base64demo/base64.go @@ -1,11 +1,12 @@ package main import ( + "encoding/base32" "encoding/base64" "fmt" ) -func main() { +func base64Demo() { msg := "Hello, 世界" encoded := base64.StdEncoding.EncodeToString([]byte(msg)) fmt.Println(encoded) @@ -16,3 +17,20 @@ func main() { } fmt.Println(string(decoded)) } + +func base32Demo() { + str := "JBSWY3DPFQQHO33SNRSCC===" + dst := make([]byte, base32.StdEncoding.DecodedLen(len(str))) + n, err := base32.StdEncoding.Decode(dst, []byte(str)) + if err != nil { + fmt.Println("decode error:", err) + return + } + dst = dst[:n] + fmt.Printf("%q\n", dst) +} + +func main() { + base64Demo() + base32Demo() +} From 4607079ca9b76aec76a60eb3a5d686c32ca04448 Mon Sep 17 00:00:00 2001 From: AN Long Date: Tue, 30 Jul 2024 00:33:21 +0800 Subject: [PATCH 081/152] feat(python): Add some basic dict methods --- py/dict.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/py/dict.go b/py/dict.go index 8f8923b4..817d3ee8 100644 --- a/py/dict.go +++ b/py/dict.go @@ -42,4 +42,22 @@ func (d *Object) DictValues() *Object { return nil } // llgo:link (*Object).DictItems C.PyDict_Items func (d *Object) DictItems() *Object { return nil } +// Insert val into the dictionary d with a key of key. key must be hashable; +// if it isn’t, return -1 and TypeError will be set. Return 0 on success or +// -1 on failure. +// +// llgo:link (*Object).DictSetItem C.PyDict_SetItem +func (d *Object) DictSetItem(key *Object, val *Object) *Object { return nil } + +// Return the object from dictionary d which has a key key. Return nil if the +// key key is not present, but without setting an exception. +// +// llgo:link (*Object).DictGetItem C.PyDict_GetItem +func (d *Object) DictGetItem(key *Object) *Object { return nil } + +// Return the number of items in the dictionary. +// +// llgo:link (*Object).DictSize C.PyDict_Size +func (d *Object) DictSize() uintptr { return 0 } + // ----------------------------------------------------------------------------- From 679e2d0f6baa8b7bc8b6742e93810e7e4a92e5d1 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Tue, 30 Jul 2024 00:33:44 +0800 Subject: [PATCH 082/152] library: encoding/hex --- README.md | 1 + _cmptest/base64demo/base64.go | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/README.md b/README.md index a8ff2825..5762e3dd 100644 --- a/README.md +++ b/README.md @@ -292,6 +292,7 @@ Here are the Go packages that can be imported correctly: * [reflect](https://pkg.go.dev/reflect) (partially) * [time](https://pkg.go.dev/time) (partially) * [encoding/binary](https://pkg.go.dev/encoding/binary) +* [encoding/hex](https://pkg.go.dev/encoding/hex) * [encoding/base32](https://pkg.go.dev/encoding/base32) * [encoding/base64](https://pkg.go.dev/encoding/base64) * [regexp](https://pkg.go.dev/regexp) diff --git a/_cmptest/base64demo/base64.go b/_cmptest/base64demo/base64.go index e80a6f42..0fbd1fa4 100644 --- a/_cmptest/base64demo/base64.go +++ b/_cmptest/base64demo/base64.go @@ -3,7 +3,9 @@ package main import ( "encoding/base32" "encoding/base64" + "encoding/hex" "fmt" + "log" ) func base64Demo() { @@ -30,7 +32,17 @@ func base32Demo() { fmt.Printf("%q\n", dst) } +func hexDemo() { + const s = "48656c6c6f20476f7068657221" + decoded, err := hex.DecodeString(s) + if err != nil { + log.Fatal(err) + } + fmt.Printf("%s\n", decoded) + +} func main() { base64Demo() base32Demo() + hexDemo() } From cc37097164607637ffc4677790cceb56d5e7afe1 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Tue, 30 Jul 2024 00:44:03 +0800 Subject: [PATCH 083/152] library: bufio, encoding/csv --- README.md | 2 ++ _cmptest/csvdemo/csv.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 _cmptest/csvdemo/csv.go diff --git a/README.md b/README.md index 5762e3dd..f9c3fcf4 100644 --- a/README.md +++ b/README.md @@ -278,6 +278,7 @@ Here are the Go packages that can be imported correctly: * [flag](https://pkg.go.dev/flag) * [sort](https://pkg.go.dev/sort) * [bytes](https://pkg.go.dev/bytes) +* [bufio](https://pkg.go.dev/bufio) * [strings](https://pkg.go.dev/strings) * [strconv](https://pkg.go.dev/strconv) * [path](https://pkg.go.dev/path) @@ -295,6 +296,7 @@ Here are the Go packages that can be imported correctly: * [encoding/hex](https://pkg.go.dev/encoding/hex) * [encoding/base32](https://pkg.go.dev/encoding/base32) * [encoding/base64](https://pkg.go.dev/encoding/base64) +* [encoding/csv](https://pkg.go.dev/encoding/csv) * [regexp](https://pkg.go.dev/regexp) * [regexp/syntax](https://pkg.go.dev/regexp/syntax) diff --git a/_cmptest/csvdemo/csv.go b/_cmptest/csvdemo/csv.go new file mode 100644 index 00000000..d4beec09 --- /dev/null +++ b/_cmptest/csvdemo/csv.go @@ -0,0 +1,30 @@ +package main + +import ( + "encoding/csv" + "fmt" + "io" + "log" + "strings" +) + +func main() { + in := `first_name,last_name,username +"Rob","Pike",rob +Ken,Thompson,ken +"Robert","Griesemer","gri" +` + r := csv.NewReader(strings.NewReader(in)) + + for { + record, err := r.Read() + if err == io.EOF { + break + } + if err != nil { + log.Fatal(err) + } + + fmt.Println(record) + } +} From 42352d9f5756c3a55a8e6db458561778ed654d44 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Tue, 30 Jul 2024 01:35:49 +0800 Subject: [PATCH 084/152] library: c/openssl --- _cmptest/_md5demo/md5.go | 14 +++++++ _demo/cmd5demo/md5.go | 18 +++++++++ c/openssl/md5.go | 85 ++++++++++++++++++++++++++++++++++++++++ c/openssl/openssl.go | 23 +++++++++++ 4 files changed, 140 insertions(+) create mode 100644 _cmptest/_md5demo/md5.go create mode 100644 _demo/cmd5demo/md5.go create mode 100644 c/openssl/md5.go create mode 100644 c/openssl/openssl.go diff --git a/_cmptest/_md5demo/md5.go b/_cmptest/_md5demo/md5.go new file mode 100644 index 00000000..dbee9e66 --- /dev/null +++ b/_cmptest/_md5demo/md5.go @@ -0,0 +1,14 @@ +package main + +import ( + "crypto/md5" + "fmt" + "io" +) + +func main() { + h := md5.New() + io.WriteString(h, "The fog is getting thicker!") + io.WriteString(h, "And Leon's getting laaarger!") + fmt.Printf("%x", h.Sum(nil)) +} diff --git a/_demo/cmd5demo/md5.go b/_demo/cmd5demo/md5.go new file mode 100644 index 00000000..1c08a192 --- /dev/null +++ b/_demo/cmd5demo/md5.go @@ -0,0 +1,18 @@ +package main + +import ( + "fmt" + "unsafe" + + "github.com/goplus/llgo/c/openssl" +) + +func main() { + var md5 openssl.MD5_CTX + var h = make([]byte, openssl.MD5_LBLOCK) + md5.Init() + md5.UpdateString("The fog is getting thicker!") + md5.UpdateString("And Leon's getting laaarger!") + md5.Final(unsafe.Pointer(unsafe.SliceData(h))) + fmt.Printf("%x", h) +} diff --git a/c/openssl/md5.go b/c/openssl/md5.go new file mode 100644 index 00000000..b828da9f --- /dev/null +++ b/c/openssl/md5.go @@ -0,0 +1,85 @@ +/* + * 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 openssl + +import ( + "unsafe" + + "github.com/goplus/llgo/c" +) + +// ----------------------------------------------------------------------------- + +const ( + MD5_CBLOCK = 64 + MD5_LBLOCK = MD5_CBLOCK / 4 +) + +// ----------------------------------------------------------------------------- + +type MD5_LONG = c.Uint + +type MD5_CTX struct { + A, B, C, D MD5_LONG + Nl, Nh MD5_LONG + Data [MD5_LBLOCK]MD5_LONG + Num c.Uint +} + +// OSSL_DEPRECATEDIN_3_0 int MD5_Init(MD5_CTX *c); +// +// llgo:link (*MD5_CTX).Init C.MD5_Init +func (c *MD5_CTX) Init() c.Int { return 0 } + +// OSSL_DEPRECATEDIN_3_0 int MD5_Update(MD5_CTX *c, const void *data, size_t len); +// +// llgo:link (*MD5_CTX).Update C.MD5_Update +func (c *MD5_CTX) Update(data unsafe.Pointer, n uintptr) c.Int { return 0 } + +func (c *MD5_CTX) UpdateBytes(data []byte) c.Int { + return c.Update(unsafe.Pointer(unsafe.SliceData(data)), uintptr(len(data))) +} + +func (c *MD5_CTX) UpdateString(data string) c.Int { + return c.Update(unsafe.Pointer(unsafe.StringData(data)), uintptr(len(data))) +} + +// OSSL_DEPRECATEDIN_3_0 int MD5_Final(unsigned char *md, MD5_CTX *c); +// +//go:linkname md5Final C.MD5_Final +func md5Final(md unsafe.Pointer, c *MD5_CTX) c.Int + +func (c *MD5_CTX) Final(md unsafe.Pointer) c.Int { + return md5Final(md, c) +} + +// OSSL_DEPRECATEDIN_3_0 unsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md); +// +//go:linkname MD5 C.MD5 +func MD5(data unsafe.Pointer, n uintptr, md unsafe.Pointer) unsafe.Pointer + +func MD5Bytes(data []byte, md unsafe.Pointer) unsafe.Pointer { + return MD5(unsafe.Pointer(unsafe.SliceData(data)), uintptr(len(data)), md) +} + +func MD5String(data string, md unsafe.Pointer) unsafe.Pointer { + return MD5(unsafe.Pointer(unsafe.StringData(data)), uintptr(len(data)), md) +} + +// OSSL_DEPRECATEDIN_3_0 void MD5_Transform(MD5_CTX *c, const unsigned char *b); + +// ----------------------------------------------------------------------------- diff --git a/c/openssl/openssl.go b/c/openssl/openssl.go new file mode 100644 index 00000000..cde02ca5 --- /dev/null +++ b/c/openssl/openssl.go @@ -0,0 +1,23 @@ +/* + * 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 openssl + +const ( + LLGoPackage = "link: $(pkg-config --libs openssl); -lssl -lcrypto" +) + +// ----------------------------------------------------------------------------- From d480bb3ecd144471916b9838e4270906c1e649b8 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Tue, 30 Jul 2024 02:07:19 +0800 Subject: [PATCH 085/152] library: crypto/md5 --- README.md | 1 + _cmptest/{_md5demo => md5demo}/md5.go | 0 _demo/cmd5demo/md5.go | 2 +- c/openssl/md5.go | 10 ++-- cl/import.go | 5 +- internal/build/build.go | 1 + internal/lib/crypto/md5/md5.go | 66 +++++++++++++++++++++++++++ 7 files changed, 76 insertions(+), 9 deletions(-) rename _cmptest/{_md5demo => md5demo}/md5.go (100%) create mode 100644 internal/lib/crypto/md5/md5.go diff --git a/README.md b/README.md index f9c3fcf4..a6963999 100644 --- a/README.md +++ b/README.md @@ -297,6 +297,7 @@ Here are the Go packages that can be imported correctly: * [encoding/base32](https://pkg.go.dev/encoding/base32) * [encoding/base64](https://pkg.go.dev/encoding/base64) * [encoding/csv](https://pkg.go.dev/encoding/csv) +* [crypto/md5](https://pkg.go.dev/crypto/md5) * [regexp](https://pkg.go.dev/regexp) * [regexp/syntax](https://pkg.go.dev/regexp/syntax) diff --git a/_cmptest/_md5demo/md5.go b/_cmptest/md5demo/md5.go similarity index 100% rename from _cmptest/_md5demo/md5.go rename to _cmptest/md5demo/md5.go diff --git a/_demo/cmd5demo/md5.go b/_demo/cmd5demo/md5.go index 1c08a192..26db4527 100644 --- a/_demo/cmd5demo/md5.go +++ b/_demo/cmd5demo/md5.go @@ -13,6 +13,6 @@ func main() { md5.Init() md5.UpdateString("The fog is getting thicker!") md5.UpdateString("And Leon's getting laaarger!") - md5.Final(unsafe.Pointer(unsafe.SliceData(h))) + md5.Final(unsafe.SliceData(h)) fmt.Printf("%x", h) } diff --git a/c/openssl/md5.go b/c/openssl/md5.go index b828da9f..99aac0c3 100644 --- a/c/openssl/md5.go +++ b/c/openssl/md5.go @@ -61,22 +61,22 @@ func (c *MD5_CTX) UpdateString(data string) c.Int { // OSSL_DEPRECATEDIN_3_0 int MD5_Final(unsigned char *md, MD5_CTX *c); // //go:linkname md5Final C.MD5_Final -func md5Final(md unsafe.Pointer, c *MD5_CTX) c.Int +func md5Final(md *byte, c *MD5_CTX) c.Int -func (c *MD5_CTX) Final(md unsafe.Pointer) c.Int { +func (c *MD5_CTX) Final(md *byte) c.Int { return md5Final(md, c) } // OSSL_DEPRECATEDIN_3_0 unsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md); // //go:linkname MD5 C.MD5 -func MD5(data unsafe.Pointer, n uintptr, md unsafe.Pointer) unsafe.Pointer +func MD5(data unsafe.Pointer, n uintptr, md *byte) *byte -func MD5Bytes(data []byte, md unsafe.Pointer) unsafe.Pointer { +func MD5Bytes(data []byte, md *byte) *byte { return MD5(unsafe.Pointer(unsafe.SliceData(data)), uintptr(len(data)), md) } -func MD5String(data string, md unsafe.Pointer) unsafe.Pointer { +func MD5String(data string, md *byte) *byte { return MD5(unsafe.Pointer(unsafe.StringData(data)), uintptr(len(data)), md) } diff --git a/cl/import.go b/cl/import.go index 43d5aa0e..6729b7cf 100644 --- a/cl/import.go +++ b/cl/import.go @@ -590,9 +590,8 @@ func ignoreName(name string) bool { */ const internal = "internal/" return (strings.HasPrefix(name, internal) && !supportedInternal(name[len(internal):])) || - strings.HasPrefix(name, "crypto/") || strings.HasPrefix(name, "runtime/") || - strings.HasPrefix(name, "arena.") || strings.HasPrefix(name, "maps.") || - strings.HasPrefix(name, "plugin.") + strings.HasPrefix(name, "runtime/") || strings.HasPrefix(name, "arena.") || + strings.HasPrefix(name, "maps.") || strings.HasPrefix(name, "plugin.") } func supportedInternal(name string) bool { diff --git a/internal/build/build.go b/internal/build/build.go index 8854fb36..ce75c02a 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -755,6 +755,7 @@ func findDylibDep(exe, lib string) string { type none struct{} var hasAltPkg = map[string]none{ + "crypto/md5": {}, "fmt": {}, "internal/abi": {}, "internal/bytealg": {}, diff --git a/internal/lib/crypto/md5/md5.go b/internal/lib/crypto/md5/md5.go new file mode 100644 index 00000000..9baf0419 --- /dev/null +++ b/internal/lib/crypto/md5/md5.go @@ -0,0 +1,66 @@ +/* + * 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 md5 + +// llgo:skipall +import ( + "hash" + "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/openssl" +) + +// The blocksize of MD5 in bytes. +const BlockSize = 64 + +// The size of an MD5 checksum in bytes. +const Size = 16 + +type digest struct { + ctx openssl.MD5_CTX +} + +func (d *digest) Size() int { return Size } + +func (d *digest) BlockSize() int { return BlockSize } + +func (d *digest) Reset() { + d.ctx.Init() +} + +func (d *digest) Write(p []byte) (nn int, err error) { + d.ctx.UpdateBytes(p) + return len(p), nil +} + +func (d *digest) Sum(in []byte) []byte { + hash := (*[Size]byte)(c.Alloca(Size)) + d.ctx.Final((*byte)(unsafe.Pointer(hash))) + return append(in, hash[:]...) +} + +func New() hash.Hash { + d := new(digest) + d.ctx.Init() + return d +} + +func Sum(data []byte) (ret [Size]byte) { + openssl.MD5Bytes(data, &ret[0]) + return +} From 68fa3dadb03a0bdfcccc07520409c35ac8dd9aa4 Mon Sep 17 00:00:00 2001 From: Aofei Sheng Date: Tue, 30 Jul 2024 08:43:51 +0800 Subject: [PATCH 086/152] build: replicate macOS `-dead_strip` optimization on Linux Added `-fdata-sections` and `-ffunction-sections` compiler flags to work with `--gc-sections` on Linux. This combination achieves similar dead code elimination as macOS's `-dead_strip`, reducing binary size and resolving undefined symbol issues. Ensures consistent optimization across macOS and Linux builds. --- internal/build/build.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/build/build.go b/internal/build/build.go index ce75c02a..0d9d546d 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -361,6 +361,8 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, llFiles args, "-rpath", "$ORIGIN", "-rpath", "$ORIGIN/../lib", + "-fdata-sections", + "-ffunction-sections", "-Xlinker", "--gc-sections", "-lm", "-latomic", From f008a65d911e59e99ba2bbe50de443043b4b7b33 Mon Sep 17 00:00:00 2001 From: Aofei Sheng Date: Tue, 30 Jul 2024 14:10:56 +0800 Subject: [PATCH 087/152] deps: require OpenSSL 3.0+ --- .github/workflows/go.yml | 24 +++++++++++------------- README.md | 5 +++-- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index e373b9d2..7d43725a 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -14,31 +14,29 @@ jobs: test: strategy: matrix: - os: [macos-latest, ubuntu-24.04] - # os: [macos-latest] + os: + - macos-latest + - ubuntu-24.04 llvm: [18] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - - name: Update Homebrew - # needed as long as LLVM 18 is still fresh - if: matrix.llvm == 18 && startsWith(matrix.os, 'macos') - run: brew update - - name: Install LLVM ${{ matrix.llvm }} and bdw-gc + - name: Install dependencies if: startsWith(matrix.os, 'macos') run: | - HOMEBREW_NO_AUTO_UPDATE=1 brew install llvm@${{ matrix.llvm }} bdw-gc - echo `brew --prefix llvm@${{ matrix.llvm }}`/bin >> $GITHUB_PATH - - name: Install LLVM ${{ matrix.llvm }} and libgc-dev + brew update + brew install llvm@${{ matrix.llvm }} pkg-config bdw-gc openssl cjson sqlite python@3.12 + echo "$(brew --prefix llvm@${{ matrix.llvm }})/bin" >> $GITHUB_PATH + + - name: Install dependencies if: startsWith(matrix.os, 'ubuntu') run: | echo "deb http://apt.llvm.org/$(lsb_release -cs)/ llvm-toolchain-$(lsb_release -cs)-${{ matrix.llvm }} main" | sudo tee /etc/apt/sources.list.d/llvm.list wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - sudo apt-get update - sudo apt-get install -y llvm-${{ matrix.llvm }}-dev clang-${{ matrix.llvm }} lld-${{ matrix.llvm }} pkg-config libgc-dev libcjson-dev libsqlite3-dev python3.12-dev - - echo /usr/lib/llvm-${{ matrix.llvm }}/bin >> $GITHUB_PATH + sudo apt-get install -y llvm-${{ matrix.llvm }}-dev clang-${{ matrix.llvm }} lld-${{ matrix.llvm }} pkg-config libgc-dev libssl-dev libcjson-dev libsqlite3-dev python3.12-dev + echo "/usr/lib/llvm-${{ matrix.llvm }}/bin" >> $GITHUB_PATH - name: Clang information run: | diff --git a/README.md b/README.md index a6963999..b52b82d6 100644 --- a/README.md +++ b/README.md @@ -310,6 +310,7 @@ Here are the Go packages that can be imported correctly: - [Clang 18](https://clang.llvm.org) - [pkg-config 0.29+](https://www.freedesktop.org/wiki/Software/pkg-config/) - [bdwgc/libgc 8.0+](https://www.hboehm.info/gc/) +- [OpenSSL 3.0+](https://www.openssl.org/) - [cJSON 1.7+](https://github.com/DaveGamble/cJSON) (optional, for [github.com/goplus/llgo/c/cjson](https://pkg.go.dev/github.com/goplus/llgo/c/cjson)) - [SQLite 3](https://www.sqlite.org) (optional, for [github.com/goplus/llgo/c/sqlite](https://pkg.go.dev/github.com/goplus/llgo/c/sqlite)) - [Python 3.12+](https://www.python.org) (optional, for [github.com/goplus/llgo/py](https://pkg.go.dev/github.com/goplus/llgo/py)) @@ -322,7 +323,7 @@ Follow these steps to generate the `llgo` command (its usage is the same as the ```sh brew update # execute if needed -brew install llvm@18 pkg-config libgc +brew install llvm@18 pkg-config bdw-gc openssl brew install cjson sqlite python@3.12 # optional export PATH=$(brew --prefix llvm@18)/bin:$PATH # you may want to add this to your shell RC file, e.g. ~/.zshrc go install -v github.com/goplus/llgo/cmd/llgo@latest @@ -334,7 +335,7 @@ go install -v github.com/goplus/llgo/cmd/llgo@latest echo "deb http://apt.llvm.org/$(lsb_release -cs)/ llvm-toolchain-$(lsb_release -cs)-18 main" | sudo tee /etc/apt/sources.list.d/llvm.list wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - sudo apt-get update # execute if needed -sudo apt-get install -y llvm-18-dev clang-18 lld-18 pkg-config libgc-dev +sudo apt-get install -y llvm-18-dev clang-18 lld-18 pkg-config libgc-dev libssl-dev sudo apt-get install -y libcjson-dev libsqlite3-dev python3.12-dev # optional export PATH=/usr/lib/llvm-18/bin:$PATH # you may want to add this to your shell RC file, e.g. ~/.bashrc go install -v github.com/goplus/llgo/cmd/llgo@latest From 0a884df74f164e7a39ebf02c1734e2cb18a261db Mon Sep 17 00:00:00 2001 From: Aofei Sheng Date: Tue, 30 Jul 2024 17:09:41 +0800 Subject: [PATCH 088/152] ssa: add support for `min` and `max` built-in functions --- ssa/expr.go | 30 +++++++++++++++++++++++++++--- ssa/ssa_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/ssa/expr.go b/ssa/expr.go index 263ebb7a..4ad0b6c0 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -383,7 +383,7 @@ var boolPredOpToLLVM = []llvm.IntPredicate{ token.NEQ - predOpBase: llvm.IntNE, } -// EQL NEQ LSS LEQ GTR GEQ == != < <= < >= +// EQL NEQ LSS LEQ GTR GEQ == != < <= > >= func isPredOp(op token.Token) bool { return op >= predOpBase && op <= predOpLast } @@ -392,7 +392,7 @@ func isPredOp(op token.Token) bool { // op can be: // ADD SUB MUL QUO REM + - * / % // AND OR XOR SHL SHR AND_NOT & | ^ << >> &^ -// EQL NEQ LSS LEQ GTR GEQ == != < <= < >= +// EQL NEQ LSS LEQ GTR GEQ == != < <= > >= func (b Builder) BinOp(op token.Token, x, y Expr) Expr { if debugInstr { log.Printf("BinOp %d, %v, %v\n", op, x.impl, y.impl) @@ -490,7 +490,7 @@ func (b Builder) BinOp(op token.Token, x, y Expr) Expr { llop := logicOpToLLVM[op-logicOpBase] return Expr{llvm.CreateBinOp(b.impl, llop, x.impl, y.impl), x.Type} } - case isPredOp(op): // op: == != < <= < >= + case isPredOp(op): // op: == != < <= > >= prog := b.Prog tret := prog.Bool() kind := x.kind @@ -1014,6 +1014,22 @@ func (b Builder) Do(da DoAction, fn Expr, args ...Expr) (ret Expr) { return } +// compareSelect performs a series of comparisons and selections based on the +// given comparison op. It's used to implement operations like min and max. +// +// The function iterates through the provided expressions, comparing each with +// the current result using the specified comparison op. It selects the +// appropriate value based on the comparison. +func (b Builder) compareSelect(op token.Token, x Expr, y ...Expr) Expr { + ret := x + for _, v := range y { + cond := b.BinOp(op, ret, v) + sel := llvm.CreateSelect(b.impl, cond.impl, ret.impl, v.impl) + ret = Expr{sel, ret.Type} + } + return ret +} + // A Builtin represents a specific use of a built-in function, e.g. len. // // Builtins are immutable values. Builtins do not have addresses. @@ -1124,6 +1140,14 @@ func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) { b.Call(b.Pkg.rtFunc("MapClear"), t, m) return } + case "min": + if len(args) > 0 { + return b.compareSelect(token.LSS, args[0], args[1:]...) + } + case "max": + if len(args) > 0 { + return b.compareSelect(token.GTR, args[0], args[1:]...) + } } panic("todo: " + fn) } diff --git a/ssa/ssa_test.go b/ssa/ssa_test.go index 81cd739f..4946869d 100644 --- a/ssa/ssa_test.go +++ b/ssa/ssa_test.go @@ -521,3 +521,34 @@ func TestBasicType(t *testing.T) { } } } + +func TestCompareSelect(t *testing.T) { + prog := NewProgram(nil) + pkg := prog.NewPackage("bar", "foo/bar") + + params := types.NewTuple( + types.NewVar(0, nil, "a", types.Typ[types.Int]), + types.NewVar(0, nil, "b", types.Typ[types.Int]), + types.NewVar(0, nil, "c", types.Typ[types.Int]), + ) + rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int])) + sig := types.NewSignatureType(nil, nil, nil, params, rets, false) + fn := pkg.NewFunc("fn", sig, InGo) + + b := fn.MakeBody(1) + result := b.compareSelect(token.GTR, fn.Param(0), fn.Param(1), fn.Param(2)) + b.Return(result) + + assertPkg(t, pkg, `; ModuleID = 'foo/bar' +source_filename = "foo/bar" + +define i64 @fn(i64 %0, i64 %1, i64 %2) { +_llgo_0: + %3 = icmp sgt i64 %0, %1 + %4 = select i1 %3, i64 %0, i64 %1 + %5 = icmp sgt i64 %4, %2 + %6 = select i1 %5, i64 %4, i64 %2 + ret i64 %6 +} +`) +} From c0e84043c9adbb3348dea211ab8211ce58627900 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Tue, 30 Jul 2024 17:14:59 +0800 Subject: [PATCH 089/152] library: os.ReadFile --- _cmptest/readfiledemo/readf.go | 25 +++++++++++++++++++++++++ c/syscall/syscall.go | 7 ++++++- internal/lib/os/stat_darwin.go | 3 ++- internal/lib/os/stat_unix.go | 9 +++++---- internal/lib/os/types_unix.go | 3 ++- 5 files changed, 40 insertions(+), 7 deletions(-) create mode 100644 _cmptest/readfiledemo/readf.go diff --git a/_cmptest/readfiledemo/readf.go b/_cmptest/readfiledemo/readf.go new file mode 100644 index 00000000..7f6c7bb4 --- /dev/null +++ b/_cmptest/readfiledemo/readf.go @@ -0,0 +1,25 @@ +package main + +import ( + "fmt" + "os" +) + +func main() { + fileName := "err.log" + os.WriteFile(fileName, []byte("123"), 0644) + + _, err := os.Stat(fileName) + if os.IsNotExist(err) { + fmt.Fprintf(os.Stderr, "File %s not found\n", fileName) + return + } + + data, err := os.ReadFile(fileName) + if err != nil { + fmt.Fprintf(os.Stderr, "ReadFile: %v\n", err) + return + } + + fmt.Printf("%s\n", data) +} diff --git a/c/syscall/syscall.go b/c/syscall/syscall.go index 69b266ec..e9a08e06 100644 --- a/c/syscall/syscall.go +++ b/c/syscall/syscall.go @@ -17,7 +17,7 @@ package syscall const ( - LLGoPackage = "decl" + LLGoPackage = "noinit" ) type Errno = uintptr @@ -25,3 +25,8 @@ type Errno = uintptr // A Signal is a number describing a process signal. // It implements the os.Signal interface. type Signal = int + +// Unix returns the time stored in ts as seconds plus nanoseconds. +func (ts *Timespec) Unix() (sec int64, nsec int64) { + return int64(ts.Sec), int64(ts.Nsec) +} diff --git a/internal/lib/os/stat_darwin.go b/internal/lib/os/stat_darwin.go index 0725f6bb..2f5dc575 100644 --- a/internal/lib/os/stat_darwin.go +++ b/internal/lib/os/stat_darwin.go @@ -5,8 +5,9 @@ package os import ( - "syscall" "time" + + "github.com/goplus/llgo/c/syscall" ) func fillFileStatFromSys(fs *fileStat, name string) { diff --git a/internal/lib/os/stat_unix.go b/internal/lib/os/stat_unix.go index 04ae7304..c0840d31 100644 --- a/internal/lib/os/stat_unix.go +++ b/internal/lib/os/stat_unix.go @@ -6,12 +6,15 @@ package os -import "syscall" +import ( + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/os" + "github.com/goplus/llgo/internal/lib/syscall" +) // Stat returns the FileInfo structure describing file. // If there is an error, it will be of type *PathError. func (f *File) Stat() (FileInfo, error) { - /* TODO(xsw): if f == nil { return nil, ErrInvalid } @@ -22,8 +25,6 @@ func (f *File) Stat() (FileInfo, error) { } fillFileStatFromSys(&fs, f.name) return &fs, nil - */ - panic("todo: os.File.Stat") } // statNolog stats a file with no test logging. diff --git a/internal/lib/os/types_unix.go b/internal/lib/os/types_unix.go index 1b90a5a1..602eee4c 100644 --- a/internal/lib/os/types_unix.go +++ b/internal/lib/os/types_unix.go @@ -7,8 +7,9 @@ package os import ( - "syscall" "time" + + "github.com/goplus/llgo/internal/lib/syscall" ) // A fileStat is the implementation of FileInfo returned by Stat and Lstat. From a96d6a814825b20863b25bacf480e4f4d1d963af Mon Sep 17 00:00:00 2001 From: morpingsss Date: Fri, 26 Jul 2024 10:31:20 +0800 Subject: [PATCH 090/152] feat(llgo/xtools): add llcppsymg.go --- chore/_xtool/llcppsymg/common/common.go | 29 +++ chore/_xtool/llcppsymg/llcppg.cfg | 10 + chore/_xtool/llcppsymg/llcppsymg.go | 249 +++++++++++++++++++++++- 3 files changed, 278 insertions(+), 10 deletions(-) create mode 100644 chore/_xtool/llcppsymg/common/common.go create mode 100644 chore/_xtool/llcppsymg/llcppg.cfg diff --git a/chore/_xtool/llcppsymg/common/common.go b/chore/_xtool/llcppsymg/common/common.go new file mode 100644 index 00000000..35fed532 --- /dev/null +++ b/chore/_xtool/llcppsymg/common/common.go @@ -0,0 +1,29 @@ +package common + +type CPPSymbol struct { + Symbol string `json:"symbol"` + Type string `json:"type"` + Name string `json:"name"` +} + +type ASTInformation struct { + Namespace string `json:"namespace"` + Class string `json:"class"` + Name string `json:"name"` + BaseClasses []string `json:"baseClasses"` + ReturnType string `json:"returnType"` + Location string `json:"location"` + Parameters []Parameter `json:"parameters"` + Symbol string `json:"symbol"` +} + +type Parameter struct { + Name string `json:"name"` + Type string `json:"type"` +} + +type SymbolInfo struct { + Mangle string `json:"mangle"` // C++ Symbol + CPP string `json:"c++"` // C++ function name + Go string `json:"go"` // Go function name +} diff --git a/chore/_xtool/llcppsymg/llcppg.cfg b/chore/_xtool/llcppsymg/llcppg.cfg new file mode 100644 index 00000000..b5335e2c --- /dev/null +++ b/chore/_xtool/llcppsymg/llcppg.cfg @@ -0,0 +1,10 @@ +{ + "name": "inih", + "cflags": "$(pkg-config --cflags INIReader)", + "include": [ + "INIReader.h", + "AnotherHeaderFile.h" + ], + "libs": "$(pkg-config --libs INIReader)", + "trimPrefixes": ["Ini", "INI"] +} \ No newline at end of file diff --git a/chore/_xtool/llcppsymg/llcppsymg.go b/chore/_xtool/llcppsymg/llcppsymg.go index ea60dffe..db4bc1e4 100644 --- a/chore/_xtool/llcppsymg/llcppsymg.go +++ b/chore/_xtool/llcppsymg/llcppsymg.go @@ -17,12 +17,20 @@ package main import ( + "bufio" + "bytes" + "encoding/json" + "errors" "fmt" + "github.com/goplus/llgo/chore/_xtool/llcppsymg/common" + "github.com/goplus/llgo/chore/llcppg/types" "io" "os" - - "github.com/goplus/llgo/c" - "github.com/goplus/llgo/c/cjson" + "os/exec" + "path/filepath" + "regexp" + "strconv" + "strings" ) func main() { @@ -40,14 +48,26 @@ func main() { } check(err) - conf := cjson.ParseBytes(data) - if conf == nil { - fmt.Fprintln(os.Stderr, "Failed to parse config file:", cfgFile) - os.Exit(1) - } - defer conf.Delete() + var config types.Config + err = json.Unmarshal(data, &config) + check(err) + + symbols, err := parseDylibSymbols(config.Libs) + check(err) + + files, err := parseHeaderFile(config) + check(err) + + symbolInfo := getCommonSymbols(symbols, files) + + jsonData, err := json.MarshalIndent(symbolInfo, "", " ") + check(err) + + // 写入文件 + fileName := "llcppg.symb.json" + err = os.WriteFile(fileName, jsonData, 0644) // 使用 0644 权限 + check(err) - c.Printf(c.Str("%s"), conf.Print()) } func check(err error) { @@ -55,3 +75,212 @@ func check(err error) { panic(err) } } + +func parseDylibSymbols(lib string) ([]common.CPPSymbol, error) { + dylibPath, _ := generateDylibPath(lib) + nmCmd := exec.Command("nm", "-gU", dylibPath) + nmOutput, err := nmCmd.Output() + if err != nil { + return nil, errors.New("failed to execute nm command") + } + + symbols := parseNmOutput(nmOutput) + + for i, sym := range symbols { + decodedName, err := decodeSymbolName(sym.Name) + if err != nil { + return nil, err + } + symbols[i].Name = decodedName + } + + return symbols, nil +} + +func generateDylibPath(lib string) (string, error) { + // 执行pkg-config命令 + output := expandEnv(lib) + // 解析输出 + libPath := "" + libName := "" + for _, part := range strings.Fields(string(output)) { + if strings.HasPrefix(part, "-L") { + libPath = part[2:] // 去掉-L前缀 + } else if strings.HasPrefix(part, "-l") { + libName = part[2:] // 去掉-l前缀 + } + } + + if libPath == "" || libName == "" { + return "", fmt.Errorf("failed to parse pkg-config output: %s", output) + } + + // 构造dylib路径 + dylibPath := filepath.Join(libPath, "lib"+libName+".dylib") + return dylibPath, nil +} + +func parseNmOutput(output []byte) []common.CPPSymbol { + scanner := bufio.NewScanner(bytes.NewReader(output)) + var symbols []common.CPPSymbol + + for scanner.Scan() { + line := scanner.Text() + fields := strings.Fields(line) + if len(fields) < 3 { + continue + } + symbolName := fields[2] + // Check if the symbol name starts with an underscore and remove it if present + if strings.HasPrefix(symbolName, "_") { + symbolName = symbolName[1:] + } + symbols = append(symbols, common.CPPSymbol{ + Symbol: symbolName, + Type: fields[1], + Name: fields[2], + }) + } + + return symbols +} + +func decodeSymbolName(symbolName string) (string, error) { + cppfiltCmd := exec.Command("c++filt", symbolName) + cppfiltOutput, err := cppfiltCmd.Output() + if err != nil { + return "", errors.New("failed to execute c++filt command") + } + + decodedName := strings.TrimSpace(string(cppfiltOutput)) + // 将特定的模板类型转换为 std::string + decodedName = strings.ReplaceAll(decodedName, "std::__1::basic_string, std::__1::allocator > const", "std::string") + return decodedName, nil +} + +// parseHeaderFile +func parseHeaderFile(config types.Config) ([]common.ASTInformation, error) { + files := generateHeaderFilePath(config.CFlags, config.Include) + headerFileCmd := exec.Command("llcppinfofetch", files...) + headerFileOutput, err := headerFileCmd.Output() + if err != nil { + return nil, errors.New("failed to execute header file command") + } + t := make([]common.ASTInformation, 0) + err = json.Unmarshal(headerFileOutput, &t) + if err != nil { + return nil, err + } + return t, nil +} + +func generateHeaderFilePath(cflags string, files []string) []string { + // 执行pkg-config命令 + prefixPath := expandEnv(cflags) + if strings.HasPrefix(prefixPath, "-I") { + prefixPath = prefixPath[2:] + } + // 去掉首尾空白字符(包括换行符) + prefixPath = strings.TrimSpace(prefixPath) + var includePaths []string + for _, file := range files { + includePaths = append(includePaths, filepath.Join(prefixPath, "/"+file)) + } + return includePaths +} + +func getCommonSymbols(dylibSymbols []common.CPPSymbol, astInfoList []common.ASTInformation) []common.SymbolInfo { + var commonSymbols []common.SymbolInfo + functionNameMap := make(map[string]int) + + for _, astInfo := range astInfoList { + for _, dylibSym := range dylibSymbols { + if dylibSym.Symbol == astInfo.Symbol { + cppName := generateCPPName(astInfo) + functionNameMap[cppName]++ + symbolInfo := common.SymbolInfo{ + Mangle: dylibSym.Symbol, + CPP: cppName, + Go: generateMangle(astInfo, functionNameMap[cppName]), + } + commonSymbols = append(commonSymbols, symbolInfo) + break + } + } + } + + return commonSymbols +} + +func generateCPPName(astInfo common.ASTInformation) string { + cppName := astInfo.Name + if astInfo.Class != "" { + cppName = astInfo.Class + "::" + astInfo.Name + } + return cppName +} + +func generateMangle(astInfo common.ASTInformation, count int) string { + res := "" + if astInfo.Class != "" { + if astInfo.Class == astInfo.Name { + res = "(*" + astInfo.Class + ")." + "Init" + if count > 1 { + res += "__" + strconv.Itoa(count-1) + } + } else if astInfo.Name == "~"+astInfo.Class { + res = "(*" + astInfo.Class + ")." + "Dispose" + if count > 1 { + res += "__" + strconv.Itoa(count-1) + } + } else { + res = "(*" + astInfo.Class + ")." + astInfo.Name + "__" + string(rune(count)) + } + } else { + res = astInfo.Name + if count > 0 { + res += "__" + strconv.Itoa(count-1) + } + } + return res +} + +var ( + reSubcmd = regexp.MustCompile(`\$\([^)]+\)`) + reFlag = regexp.MustCompile(`[^ \t\n]+`) +) + +func expandEnv(s string) string { + return expandEnvWithCmd(s) +} + +func expandEnvWithCmd(s string) string { + expanded := reSubcmd.ReplaceAllStringFunc(s, func(m string) string { + subcmd := strings.TrimSpace(s[2 : len(s)-1]) + + args := parseSubcmd(subcmd) + + cmd := args[0] + + if cmd != "pkg-config" && cmd != "llvm-config" { + fmt.Fprintf(os.Stderr, "expand cmd only support pkg-config and llvm-config: '%s'\n", subcmd) + return "" + } + + var out []byte + var err error + out, err = exec.Command(cmd, args[1:]...).Output() + + if err != nil { + // TODO(kindy): log in verbose mode + return "" + } + + return string(out) + }) + return os.Expand(expanded, os.Getenv) +} + +func parseSubcmd(s string) []string { + return reFlag.FindAllString(s, -1) +} From f391ccb8e8872f8761fe61b5a9d3b24e675bc96a Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Fri, 26 Jul 2024 13:45:08 +0800 Subject: [PATCH 091/152] c/clang/symg/header:get func info --- chore/_xtool/llcppinfofetch/llcppinfofetch.go | 220 ++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 chore/_xtool/llcppinfofetch/llcppinfofetch.go diff --git a/chore/_xtool/llcppinfofetch/llcppinfofetch.go b/chore/_xtool/llcppinfofetch/llcppinfofetch.go new file mode 100644 index 00000000..333dd7dd --- /dev/null +++ b/chore/_xtool/llcppinfofetch/llcppinfofetch.go @@ -0,0 +1,220 @@ +package main + +import ( + "fmt" + "os" + "strconv" + "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/cjson" + "github.com/goplus/llgo/c/clang" +) + +type ASTInformation struct { + Namespace string `json:"namespace"` + Class string `json:"class"` + Name string `json:"name"` + // BaseClasses []string `json:"baseClasses"` + ReturnType string `json:"returnType"` + Location string `json:"location"` + Parameters []Parameter `json:"parameters"` + Symbol string `json:"symbol"` +} + +type Parameter struct { + Name string `json:"name"` + Type string `json:"type"` +} + +type Context struct { + namespaceName string + className string + astInfo []ASTInformation +} + +func newContext() *Context { + return &Context{ + astInfo: make([]ASTInformation, 0), + } +} + +func (c *Context) setNamespaceName(name string) { + c.namespaceName = name +} + +func (c *Context) setClassName(name string) { + c.className = name +} + +var context = newContext() + +func collectFuncInfo(cursor clang.Cursor) ASTInformation { + info := ASTInformation{ + Namespace: context.namespaceName, + Class: context.className, + } + + loc := cursor.Location() + var file clang.File + var line, column c.Uint + + loc.SpellingLocation(&file, &line, &column, nil) + filename := file.FileName() + + info.Location = c.GoString(filename.CStr()) + ":" + strconv.Itoa(int(line)) + ":" + strconv.Itoa(int(column)) + + // c.Printf(c.Str("%s:%d:%d\n"), filename.CStr(), line, column) + + cursorStr := cursor.String() + symbol := cursor.Mangling() + + info.Name = c.GoString(cursorStr.CStr()) + info.Symbol = c.GoString(symbol.CStr()) + + defer symbol.Dispose() + defer cursorStr.Dispose() + defer filename.Dispose() + + if context.namespaceName != "" { + info.Namespace = context.namespaceName + } + if context.className != "" { + info.Class = context.className + } + + if cursor.Kind == clang.CXXMethod || cursor.Kind == clang.FunctionDecl { + // c.Printf(c.Str("symbol:%s\n"), symbol.CStr()) + + typeStr := cursor.ResultType().String() + defer typeStr.Dispose() + info.ReturnType = c.GoString(typeStr.CStr()) + // c.Printf(c.Str("Parameters(%d): ( "), cursor.NumArguments()) + + info.Parameters = make([]Parameter, cursor.NumArguments()) + for i := 0; i < int(cursor.NumArguments()); i++ { + argCurSor := cursor.Argument(c.Uint(i)) + argType := argCurSor.Type().String() + argName := argCurSor.String() + info.Parameters[i] = Parameter{ + Name: c.GoString(argName.CStr()), + Type: c.GoString(argType.CStr()), + } + + argType.Dispose() + argName.Dispose() + } + + // fmt.Println("location", info.Location) + // fmt.Println("symbol:", info.Symbol) + // fmt.Println("name:", info.Name) + // if info.Namespace != "" { + // fmt.Println("namespace:", info.Namespace) + // } + // if info.Class != "" { + // fmt.Println("class:", info.Class) + // } + // fmt.Println("return type:", info.ReturnType) + + // if len(info.Parameters) != 0 { + // fmt.Println("Parameters:(") + // for _, param := range info.Parameters { + // fmt.Println(" ", param.Name, ":", param.Type) + // } + // fmt.Println(")") + // } + // println("--------------------------------") + } + + return info +} + +func visit(cursor, parent clang.Cursor, clientData c.Pointer) clang.ChildVisitResult { + if cursor.Kind == clang.Namespace { + nameStr := cursor.String() + context.setNamespaceName(c.GoString(nameStr.CStr())) + clang.VisitChildren(cursor, visit, nil) + context.setNamespaceName("") + } else if cursor.Kind == clang.ClassDecl { + nameStr := cursor.String() + context.setClassName(c.GoString(nameStr.CStr())) + clang.VisitChildren(cursor, visit, nil) + context.setClassName("") + } else if cursor.Kind == clang.CXXMethod || cursor.Kind == clang.FunctionDecl { + info := collectFuncInfo(cursor) + context.astInfo = append(context.astInfo, info) + } + + return clang.ChildVisit_Continue +} + +func parse(filename *c.Char) []ASTInformation { + index := clang.CreateIndex(0, 0) + args := make([]*c.Char, 3) + args[0] = c.Str("-x") + args[1] = c.Str("c++") + args[2] = c.Str("-std=c++11") + unit := index.ParseTranslationUnit( + filename, + unsafe.SliceData(args), 3, + nil, 0, + clang.TranslationUnit_None, + ) + + if unit == nil { + println("Unable to parse translation unit. Quitting.") + c.Exit(1) + } + + cursor := unit.Cursor() + + clang.VisitChildren(cursor, visit, nil) + + unit.Dispose() + index.Dispose() + + return context.astInfo +} +func printJson(infos []ASTInformation) { + root := cjson.Array() + + for _, info := range infos { + item := cjson.Object() + item.SetItem(c.Str("namespace"), cjson.String(c.AllocaCStr(info.Namespace))) + item.SetItem(c.Str("class"), cjson.String(c.AllocaCStr(info.Class))) + item.SetItem(c.Str("name"), cjson.String(c.AllocaCStr(info.Name))) + item.SetItem(c.Str("returnType"), cjson.String(c.AllocaCStr(info.ReturnType))) + item.SetItem(c.Str("location"), cjson.String(c.AllocaCStr(info.Location))) + item.SetItem(c.Str("symbol"), cjson.String(c.AllocaCStr(info.Symbol))) + + params := cjson.Array() + for _, param := range info.Parameters { + paramObj := cjson.Object() + paramObj.SetItem(c.Str("name"), cjson.String(c.AllocaCStr(param.Name))) + paramObj.SetItem(c.Str("type"), cjson.String(c.AllocaCStr(param.Type))) + params.AddItem(paramObj) + } + item.SetItem(c.Str("parameters"), params) + + root.AddItem(item) + } + c.Printf(c.Str("%s\n"), root.Print()) +} + +func main() { + // for i := c.Int(0); i < c.Argc; i++ { + // c.Printf(c.Str("%s\n"), c.Index(c.Argv, i)) + // } + // c.Printf(c.Str("c.Index %s\n"), c.Index(c.Argv, 1)) + // c.Printf(c.Str("c.Index %s\n"), *c.Advance(c.Argv, 1)) + + if c.Argc != 2 { + fmt.Fprintln(os.Stderr, "Usage: \n") + return + } else { + // sourceFile := *c.Advance(c.Argv, 1) + printJson(parse(c.Index(c.Argv, 1))) + // fmt.Println("Json end") + } + +} From c53484f92ed98c3d526404d526820c3abdae106e Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Fri, 26 Jul 2024 14:37:26 +0800 Subject: [PATCH 092/152] c/clang/symg:normal gen json --- chore/_xtool/llcppinfofetch/llcppinfofetch.go | 7 +++++++ chore/_xtool/llcppsymg/llcppg.cfg | 3 +-- chore/_xtool/llcppsymg/llcppsymg.go | 17 +++++++++++++---- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/chore/_xtool/llcppinfofetch/llcppinfofetch.go b/chore/_xtool/llcppinfofetch/llcppinfofetch.go index 333dd7dd..a7b2c9c4 100644 --- a/chore/_xtool/llcppinfofetch/llcppinfofetch.go +++ b/chore/_xtool/llcppinfofetch/llcppinfofetch.go @@ -70,7 +70,13 @@ func collectFuncInfo(cursor clang.Cursor) ASTInformation { symbol := cursor.Mangling() info.Name = c.GoString(cursorStr.CStr()) + info.Symbol = c.GoString(symbol.CStr()) + if len(info.Symbol) >= 1 { + if info.Symbol[0] == '_' { + info.Symbol = info.Symbol[1:] + } + } defer symbol.Dispose() defer cursorStr.Dispose() @@ -212,6 +218,7 @@ func main() { fmt.Fprintln(os.Stderr, "Usage: \n") return } else { + // sourceFile := *c.Advance(c.Argv, 1) printJson(parse(c.Index(c.Argv, 1))) // fmt.Println("Json end") diff --git a/chore/_xtool/llcppsymg/llcppg.cfg b/chore/_xtool/llcppsymg/llcppg.cfg index b5335e2c..163284b3 100644 --- a/chore/_xtool/llcppsymg/llcppg.cfg +++ b/chore/_xtool/llcppsymg/llcppg.cfg @@ -2,8 +2,7 @@ "name": "inih", "cflags": "$(pkg-config --cflags INIReader)", "include": [ - "INIReader.h", - "AnotherHeaderFile.h" + "INIReader.h" ], "libs": "$(pkg-config --libs INIReader)", "trimPrefixes": ["Ini", "INI"] diff --git a/chore/_xtool/llcppsymg/llcppsymg.go b/chore/_xtool/llcppsymg/llcppsymg.go index db4bc1e4..dafd21d8 100644 --- a/chore/_xtool/llcppsymg/llcppsymg.go +++ b/chore/_xtool/llcppsymg/llcppsymg.go @@ -22,8 +22,6 @@ import ( "encoding/json" "errors" "fmt" - "github.com/goplus/llgo/chore/_xtool/llcppsymg/common" - "github.com/goplus/llgo/chore/llcppg/types" "io" "os" "os/exec" @@ -31,6 +29,9 @@ import ( "regexp" "strconv" "strings" + + "github.com/goplus/llgo/chore/_xtool/llcppsymg/common" + "github.com/goplus/llgo/chore/llcppg/types" ) func main() { @@ -161,11 +162,16 @@ func decodeSymbolName(symbolName string) (string, error) { // parseHeaderFile func parseHeaderFile(config types.Config) ([]common.ASTInformation, error) { files := generateHeaderFilePath(config.CFlags, config.Include) + fmt.Println(files) headerFileCmd := exec.Command("llcppinfofetch", files...) + + fmt.Println("Executing command:", headerFileCmd.String()) + headerFileOutput, err := headerFileCmd.Output() if err != nil { return nil, errors.New("failed to execute header file command") } + fmt.Println("headerFileOutput:", string(headerFileOutput), len(headerFileOutput)) t := make([]common.ASTInformation, 0) err = json.Unmarshal(headerFileOutput, &t) if err != nil { @@ -234,11 +240,14 @@ func generateMangle(astInfo common.ASTInformation, count int) string { res += "__" + strconv.Itoa(count-1) } } else { - res = "(*" + astInfo.Class + ")." + astInfo.Name + "__" + string(rune(count)) + res = "(*" + astInfo.Class + ")." + astInfo.Name + if count > 1 { + res += "__" + strconv.Itoa(count-1) + } } } else { res = astInfo.Name - if count > 0 { + if count > 1 { res += "__" + strconv.Itoa(count-1) } } From ce36a25ba39ef29c7c6ede27612c0394322ed17e Mon Sep 17 00:00:00 2001 From: morpingsss Date: Fri, 26 Jul 2024 15:00:06 +0800 Subject: [PATCH 093/152] feat(llgo/xtools): add TrimPrefix --- chore/_xtool/llcppinfofetch/llcppinfofetch.go | 33 +++++-------------- chore/_xtool/llcppsymg/llcppsymg.go | 28 +++++++++++----- 2 files changed, 29 insertions(+), 32 deletions(-) diff --git a/chore/_xtool/llcppinfofetch/llcppinfofetch.go b/chore/_xtool/llcppinfofetch/llcppinfofetch.go index a7b2c9c4..177d2400 100644 --- a/chore/_xtool/llcppinfofetch/llcppinfofetch.go +++ b/chore/_xtool/llcppinfofetch/llcppinfofetch.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "github.com/goplus/llgo/chore/_xtool/llcppsymg/common" "os" "strconv" "unsafe" @@ -11,31 +12,15 @@ import ( "github.com/goplus/llgo/c/clang" ) -type ASTInformation struct { - Namespace string `json:"namespace"` - Class string `json:"class"` - Name string `json:"name"` - // BaseClasses []string `json:"baseClasses"` - ReturnType string `json:"returnType"` - Location string `json:"location"` - Parameters []Parameter `json:"parameters"` - Symbol string `json:"symbol"` -} - -type Parameter struct { - Name string `json:"name"` - Type string `json:"type"` -} - type Context struct { namespaceName string className string - astInfo []ASTInformation + astInfo []common.ASTInformation } func newContext() *Context { return &Context{ - astInfo: make([]ASTInformation, 0), + astInfo: make([]common.ASTInformation, 0), } } @@ -49,8 +34,8 @@ func (c *Context) setClassName(name string) { var context = newContext() -func collectFuncInfo(cursor clang.Cursor) ASTInformation { - info := ASTInformation{ +func collectFuncInfo(cursor clang.Cursor) common.ASTInformation { + info := common.ASTInformation{ Namespace: context.namespaceName, Class: context.className, } @@ -97,12 +82,12 @@ func collectFuncInfo(cursor clang.Cursor) ASTInformation { info.ReturnType = c.GoString(typeStr.CStr()) // c.Printf(c.Str("Parameters(%d): ( "), cursor.NumArguments()) - info.Parameters = make([]Parameter, cursor.NumArguments()) + info.Parameters = make([]common.Parameter, cursor.NumArguments()) for i := 0; i < int(cursor.NumArguments()); i++ { argCurSor := cursor.Argument(c.Uint(i)) argType := argCurSor.Type().String() argName := argCurSor.String() - info.Parameters[i] = Parameter{ + info.Parameters[i] = common.Parameter{ Name: c.GoString(argName.CStr()), Type: c.GoString(argType.CStr()), } @@ -154,7 +139,7 @@ func visit(cursor, parent clang.Cursor, clientData c.Pointer) clang.ChildVisitRe return clang.ChildVisit_Continue } -func parse(filename *c.Char) []ASTInformation { +func parse(filename *c.Char) []common.ASTInformation { index := clang.CreateIndex(0, 0) args := make([]*c.Char, 3) args[0] = c.Str("-x") @@ -181,7 +166,7 @@ func parse(filename *c.Char) []ASTInformation { return context.astInfo } -func printJson(infos []ASTInformation) { +func printJson(infos []common.ASTInformation) { root := cjson.Array() for _, info := range infos { diff --git a/chore/_xtool/llcppsymg/llcppsymg.go b/chore/_xtool/llcppsymg/llcppsymg.go index dafd21d8..54e6b445 100644 --- a/chore/_xtool/llcppsymg/llcppsymg.go +++ b/chore/_xtool/llcppsymg/llcppsymg.go @@ -35,10 +35,10 @@ import ( ) func main() { - cfgFile := "llcppg.cfg" - if len(os.Args) > 1 { - cfgFile = os.Args[1] - } + cfgFile := "/Users/lixianyu/go/src/llgo/chore/_xtool/llcppsymg/llcppg.cfg" + //if len(os.Args) > 1 { + // cfgFile = os.Args[1] + //} var data []byte var err error @@ -59,7 +59,7 @@ func main() { files, err := parseHeaderFile(config) check(err) - symbolInfo := getCommonSymbols(symbols, files) + symbolInfo := getCommonSymbols(symbols, files, config.TrimPrefixes) jsonData, err := json.MarshalIndent(symbolInfo, "", " ") check(err) @@ -195,7 +195,7 @@ func generateHeaderFilePath(cflags string, files []string) []string { return includePaths } -func getCommonSymbols(dylibSymbols []common.CPPSymbol, astInfoList []common.ASTInformation) []common.SymbolInfo { +func getCommonSymbols(dylibSymbols []common.CPPSymbol, astInfoList []common.ASTInformation, prefix []string) []common.SymbolInfo { var commonSymbols []common.SymbolInfo functionNameMap := make(map[string]int) @@ -207,7 +207,7 @@ func getCommonSymbols(dylibSymbols []common.CPPSymbol, astInfoList []common.ASTI symbolInfo := common.SymbolInfo{ Mangle: dylibSym.Symbol, CPP: cppName, - Go: generateMangle(astInfo, functionNameMap[cppName]), + Go: generateMangle(astInfo, functionNameMap[cppName], prefix), } commonSymbols = append(commonSymbols, symbolInfo) break @@ -226,7 +226,10 @@ func generateCPPName(astInfo common.ASTInformation) string { return cppName } -func generateMangle(astInfo common.ASTInformation, count int) string { +func generateMangle(astInfo common.ASTInformation, count int, prefixes []string) string { + // 去除前缀 + astInfo.Class = removePrefix(astInfo.Class, prefixes) + astInfo.Name = removePrefix(astInfo.Name, prefixes) res := "" if astInfo.Class != "" { if astInfo.Class == astInfo.Name { @@ -254,6 +257,15 @@ func generateMangle(astInfo common.ASTInformation, count int) string { return res } +func removePrefix(str string, prefixes []string) string { + for _, prefix := range prefixes { + if strings.HasPrefix(str, prefix) { + return strings.TrimPrefix(str, prefix) + } + } + return str +} + var ( reSubcmd = regexp.MustCompile(`\$\([^)]+\)`) reFlag = regexp.MustCompile(`[^ \t\n]+`) From 2c7f0e85833c1eb79f3df0606bfed3927477a35d Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Fri, 26 Jul 2024 15:14:19 +0800 Subject: [PATCH 094/152] c/clang/symg:get constructor & destructor --- chore/_xtool/llcppinfofetch/llcppinfofetch.go | 69 +++++-------------- chore/_xtool/llcppsymg/llcppsymg.go | 2 +- 2 files changed, 19 insertions(+), 52 deletions(-) diff --git a/chore/_xtool/llcppinfofetch/llcppinfofetch.go b/chore/_xtool/llcppinfofetch/llcppinfofetch.go index 177d2400..c8e918b1 100644 --- a/chore/_xtool/llcppinfofetch/llcppinfofetch.go +++ b/chore/_xtool/llcppinfofetch/llcppinfofetch.go @@ -2,11 +2,12 @@ package main import ( "fmt" - "github.com/goplus/llgo/chore/_xtool/llcppsymg/common" "os" "strconv" "unsafe" + "github.com/goplus/llgo/chore/_xtool/llcppsymg/common" + "github.com/goplus/llgo/c" "github.com/goplus/llgo/c/cjson" "github.com/goplus/llgo/c/clang" @@ -74,47 +75,22 @@ func collectFuncInfo(cursor clang.Cursor) common.ASTInformation { info.Class = context.className } - if cursor.Kind == clang.CXXMethod || cursor.Kind == clang.FunctionDecl { - // c.Printf(c.Str("symbol:%s\n"), symbol.CStr()) + typeStr := cursor.ResultType().String() + defer typeStr.Dispose() + info.ReturnType = c.GoString(typeStr.CStr()) - typeStr := cursor.ResultType().String() - defer typeStr.Dispose() - info.ReturnType = c.GoString(typeStr.CStr()) - // c.Printf(c.Str("Parameters(%d): ( "), cursor.NumArguments()) - - info.Parameters = make([]common.Parameter, cursor.NumArguments()) - for i := 0; i < int(cursor.NumArguments()); i++ { - argCurSor := cursor.Argument(c.Uint(i)) - argType := argCurSor.Type().String() - argName := argCurSor.String() - info.Parameters[i] = common.Parameter{ - Name: c.GoString(argName.CStr()), - Type: c.GoString(argType.CStr()), - } - - argType.Dispose() - argName.Dispose() + info.Parameters = make([]common.Parameter, cursor.NumArguments()) + for i := 0; i < int(cursor.NumArguments()); i++ { + argCurSor := cursor.Argument(c.Uint(i)) + argType := argCurSor.Type().String() + argName := argCurSor.String() + info.Parameters[i] = common.Parameter{ + Name: c.GoString(argName.CStr()), + Type: c.GoString(argType.CStr()), } - // fmt.Println("location", info.Location) - // fmt.Println("symbol:", info.Symbol) - // fmt.Println("name:", info.Name) - // if info.Namespace != "" { - // fmt.Println("namespace:", info.Namespace) - // } - // if info.Class != "" { - // fmt.Println("class:", info.Class) - // } - // fmt.Println("return type:", info.ReturnType) - - // if len(info.Parameters) != 0 { - // fmt.Println("Parameters:(") - // for _, param := range info.Parameters { - // fmt.Println(" ", param.Name, ":", param.Type) - // } - // fmt.Println(")") - // } - // println("--------------------------------") + argType.Dispose() + argName.Dispose() } return info @@ -131,7 +107,7 @@ func visit(cursor, parent clang.Cursor, clientData c.Pointer) clang.ChildVisitRe context.setClassName(c.GoString(nameStr.CStr())) clang.VisitChildren(cursor, visit, nil) context.setClassName("") - } else if cursor.Kind == clang.CXXMethod || cursor.Kind == clang.FunctionDecl { + } else if cursor.Kind == clang.CXXMethod || cursor.Kind == clang.FunctionDecl || cursor.Kind == clang.Constructor || cursor.Kind == clang.Destructor { info := collectFuncInfo(cursor) context.astInfo = append(context.astInfo, info) } @@ -193,20 +169,11 @@ func printJson(infos []common.ASTInformation) { } func main() { - // for i := c.Int(0); i < c.Argc; i++ { - // c.Printf(c.Str("%s\n"), c.Index(c.Argv, i)) - // } - // c.Printf(c.Str("c.Index %s\n"), c.Index(c.Argv, 1)) - // c.Printf(c.Str("c.Index %s\n"), *c.Advance(c.Argv, 1)) - - if c.Argc != 2 { + if c.Argc < 2 { fmt.Fprintln(os.Stderr, "Usage: \n") return } else { - - // sourceFile := *c.Advance(c.Argv, 1) + // todo(zzy): receive files printJson(parse(c.Index(c.Argv, 1))) - // fmt.Println("Json end") } - } diff --git a/chore/_xtool/llcppsymg/llcppsymg.go b/chore/_xtool/llcppsymg/llcppsymg.go index 54e6b445..6547ebf6 100644 --- a/chore/_xtool/llcppsymg/llcppsymg.go +++ b/chore/_xtool/llcppsymg/llcppsymg.go @@ -35,7 +35,7 @@ import ( ) func main() { - cfgFile := "/Users/lixianyu/go/src/llgo/chore/_xtool/llcppsymg/llcppg.cfg" + cfgFile := "llcppg.cfg" //if len(os.Args) > 1 { // cfgFile = os.Args[1] //} From 3bd609ee41ffe95464c3b95f73a852e8ac6f2dfb Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Fri, 26 Jul 2024 15:56:47 +0800 Subject: [PATCH 095/152] c/clang/symg:filter unuse file --- chore/_xtool/llcppinfofetch/llcppinfofetch.go | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/chore/_xtool/llcppinfofetch/llcppinfofetch.go b/chore/_xtool/llcppinfofetch/llcppinfofetch.go index c8e918b1..d2f57ae3 100644 --- a/chore/_xtool/llcppinfofetch/llcppinfofetch.go +++ b/chore/_xtool/llcppinfofetch/llcppinfofetch.go @@ -17,6 +17,7 @@ type Context struct { namespaceName string className string astInfo []common.ASTInformation + filename *c.Char } func newContext() *Context { @@ -33,25 +34,19 @@ func (c *Context) setClassName(name string) { c.className = name } +func (c *Context) setFilename(filename *c.Char) { + c.filename = filename +} + var context = newContext() func collectFuncInfo(cursor clang.Cursor) common.ASTInformation { + info := common.ASTInformation{ Namespace: context.namespaceName, Class: context.className, } - loc := cursor.Location() - var file clang.File - var line, column c.Uint - - loc.SpellingLocation(&file, &line, &column, nil) - filename := file.FileName() - - info.Location = c.GoString(filename.CStr()) + ":" + strconv.Itoa(int(line)) + ":" + strconv.Itoa(int(column)) - - // c.Printf(c.Str("%s:%d:%d\n"), filename.CStr(), line, column) - cursorStr := cursor.String() symbol := cursor.Mangling() @@ -66,7 +61,6 @@ func collectFuncInfo(cursor clang.Cursor) common.ASTInformation { defer symbol.Dispose() defer cursorStr.Dispose() - defer filename.Dispose() if context.namespaceName != "" { info.Namespace = context.namespaceName @@ -108,8 +102,21 @@ func visit(cursor, parent clang.Cursor, clientData c.Pointer) clang.ChildVisitRe clang.VisitChildren(cursor, visit, nil) context.setClassName("") } else if cursor.Kind == clang.CXXMethod || cursor.Kind == clang.FunctionDecl || cursor.Kind == clang.Constructor || cursor.Kind == clang.Destructor { - info := collectFuncInfo(cursor) - context.astInfo = append(context.astInfo, info) + loc := cursor.Location() + var file clang.File + var line, column c.Uint + + loc.SpellingLocation(&file, &line, &column, nil) + filename := file.FileName() + + if c.Strcmp(filename.CStr(), context.filename) == 0 { + info := collectFuncInfo(cursor) + info.Location = c.GoString(filename.CStr()) + ":" + strconv.Itoa(int(line)) + ":" + strconv.Itoa(int(column)) + context.astInfo = append(context.astInfo, info) + } + + defer filename.Dispose() + } return clang.ChildVisit_Continue @@ -120,6 +127,7 @@ func parse(filename *c.Char) []common.ASTInformation { args := make([]*c.Char, 3) args[0] = c.Str("-x") args[1] = c.Str("c++") + args[1] = c.Str("c++") args[2] = c.Str("-std=c++11") unit := index.ParseTranslationUnit( filename, @@ -134,6 +142,7 @@ func parse(filename *c.Char) []common.ASTInformation { } cursor := unit.Cursor() + context.setFilename(filename) clang.VisitChildren(cursor, visit, nil) From 9a46301d46651832d0acdde0f8748d5f14b3041d Mon Sep 17 00:00:00 2001 From: morpingsss Date: Fri, 26 Jul 2024 16:13:20 +0800 Subject: [PATCH 096/152] feat(llgo/xtools): delete chinese --- chore/_xtool/llcppsymg/llcppsymg.go | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/chore/_xtool/llcppsymg/llcppsymg.go b/chore/_xtool/llcppsymg/llcppsymg.go index 6547ebf6..0597e6a6 100644 --- a/chore/_xtool/llcppsymg/llcppsymg.go +++ b/chore/_xtool/llcppsymg/llcppsymg.go @@ -36,9 +36,9 @@ import ( func main() { cfgFile := "llcppg.cfg" - //if len(os.Args) > 1 { - // cfgFile = os.Args[1] - //} + if len(os.Args) > 1 { + cfgFile = os.Args[1] + } var data []byte var err error @@ -66,7 +66,7 @@ func main() { // 写入文件 fileName := "llcppg.symb.json" - err = os.WriteFile(fileName, jsonData, 0644) // 使用 0644 权限 + err = os.WriteFile(fileName, jsonData, 0644) check(err) } @@ -99,16 +99,14 @@ func parseDylibSymbols(lib string) ([]common.CPPSymbol, error) { } func generateDylibPath(lib string) (string, error) { - // 执行pkg-config命令 output := expandEnv(lib) - // 解析输出 libPath := "" libName := "" for _, part := range strings.Fields(string(output)) { if strings.HasPrefix(part, "-L") { - libPath = part[2:] // 去掉-L前缀 + libPath = part[2:] } else if strings.HasPrefix(part, "-l") { - libName = part[2:] // 去掉-l前缀 + libName = part[2:] } } @@ -154,7 +152,6 @@ func decodeSymbolName(symbolName string) (string, error) { } decodedName := strings.TrimSpace(string(cppfiltOutput)) - // 将特定的模板类型转换为 std::string decodedName = strings.ReplaceAll(decodedName, "std::__1::basic_string, std::__1::allocator > const", "std::string") return decodedName, nil } @@ -181,12 +178,11 @@ func parseHeaderFile(config types.Config) ([]common.ASTInformation, error) { } func generateHeaderFilePath(cflags string, files []string) []string { - // 执行pkg-config命令 prefixPath := expandEnv(cflags) if strings.HasPrefix(prefixPath, "-I") { prefixPath = prefixPath[2:] } - // 去掉首尾空白字符(包括换行符) + prefixPath = strings.TrimSpace(prefixPath) var includePaths []string for _, file := range files { From f0d17b13f288c4c1e0f601bfa4932455ecde8411 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Fri, 26 Jul 2024 17:09:26 +0800 Subject: [PATCH 097/152] c/clang/symg:multiple header file --- chore/_xtool/llcppinfofetch/llcppinfofetch.go | 55 ++++++++++--------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/chore/_xtool/llcppinfofetch/llcppinfofetch.go b/chore/_xtool/llcppinfofetch/llcppinfofetch.go index d2f57ae3..7dd09777 100644 --- a/chore/_xtool/llcppinfofetch/llcppinfofetch.go +++ b/chore/_xtool/llcppinfofetch/llcppinfofetch.go @@ -17,7 +17,7 @@ type Context struct { namespaceName string className string astInfo []common.ASTInformation - filename *c.Char + currentFile *c.Char } func newContext() *Context { @@ -34,8 +34,8 @@ func (c *Context) setClassName(name string) { c.className = name } -func (c *Context) setFilename(filename *c.Char) { - c.filename = filename +func (c *Context) setCurrentFile(filename *c.Char) { + c.currentFile = filename } var context = newContext() @@ -109,7 +109,7 @@ func visit(cursor, parent clang.Cursor, clientData c.Pointer) clang.ChildVisitRe loc.SpellingLocation(&file, &line, &column, nil) filename := file.FileName() - if c.Strcmp(filename.CStr(), context.filename) == 0 { + if c.Strcmp(filename.CStr(), context.currentFile) == 0 { info := collectFuncInfo(cursor) info.Location = c.GoString(filename.CStr()) + ":" + strconv.Itoa(int(line)) + ":" + strconv.Itoa(int(column)) context.astInfo = append(context.astInfo, info) @@ -122,31 +122,34 @@ func visit(cursor, parent clang.Cursor, clientData c.Pointer) clang.ChildVisitRe return clang.ChildVisit_Continue } -func parse(filename *c.Char) []common.ASTInformation { +func parse(filenames []*c.Char) []common.ASTInformation { index := clang.CreateIndex(0, 0) args := make([]*c.Char, 3) args[0] = c.Str("-x") args[1] = c.Str("c++") - args[1] = c.Str("c++") args[2] = c.Str("-std=c++11") - unit := index.ParseTranslationUnit( - filename, - unsafe.SliceData(args), 3, - nil, 0, - clang.TranslationUnit_None, - ) - if unit == nil { - println("Unable to parse translation unit. Quitting.") - c.Exit(1) + for _, filename := range filenames { + unit := index.ParseTranslationUnit( + filename, + unsafe.SliceData(args), 3, + nil, 0, + clang.TranslationUnit_None, + ) + + if unit == nil { + fmt.Printf("Unable to parse translation unit for file: %s. Skipping.\n", c.GoString(filename)) + continue + } + + cursor := unit.Cursor() + context.setCurrentFile(filename) + + clang.VisitChildren(cursor, visit, nil) + + unit.Dispose() } - cursor := unit.Cursor() - context.setFilename(filename) - - clang.VisitChildren(cursor, visit, nil) - - unit.Dispose() index.Dispose() return context.astInfo @@ -176,13 +179,15 @@ func printJson(infos []common.ASTInformation) { } c.Printf(c.Str("%s\n"), root.Print()) } - func main() { if c.Argc < 2 { - fmt.Fprintln(os.Stderr, "Usage: \n") + fmt.Fprintln(os.Stderr, "Usage: [ ...]\n") return } else { - // todo(zzy): receive files - printJson(parse(c.Index(c.Argv, 1))) + filenames := make([]*c.Char, c.Argc-1) + for i := 1; i < int(c.Argc); i++ { + filenames[i-1] = c.Index(c.Argv, c.Int(i)) + } + printJson(parse(filenames)) } } From 0d3180271bbf6164755edd8ed9994337cb20c5f8 Mon Sep 17 00:00:00 2001 From: morpingsss Date: Mon, 29 Jul 2024 10:52:10 +0800 Subject: [PATCH 098/152] feat(llgo/chore/llcppg): add JSONPath --- chore/_xtool/llcppsymg/llcppsymg.go | 10 +++++++++- chore/llcppg/types/types.go | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/chore/_xtool/llcppsymg/llcppsymg.go b/chore/_xtool/llcppsymg/llcppsymg.go index 0597e6a6..03614a9b 100644 --- a/chore/_xtool/llcppsymg/llcppsymg.go +++ b/chore/_xtool/llcppsymg/llcppsymg.go @@ -64,11 +64,19 @@ func main() { jsonData, err := json.MarshalIndent(symbolInfo, "", " ") check(err) - // 写入文件 fileName := "llcppg.symb.json" err = os.WriteFile(fileName, jsonData, 0644) check(err) + absJSONPath, err := filepath.Abs(fileName) + check(err) + + config.JSONPath = absJSONPath + updatedCfgData, err := json.MarshalIndent(config, "", " ") + check(err) + + err = os.WriteFile(cfgFile, updatedCfgData, 0644) + check(err) } func check(err error) { diff --git a/chore/llcppg/types/types.go b/chore/llcppg/types/types.go index 2e39bbdd..91ac3a5b 100644 --- a/chore/llcppg/types/types.go +++ b/chore/llcppg/types/types.go @@ -23,4 +23,5 @@ type Config struct { Libs string `json:"libs"` Include []string `json:"include"` TrimPrefixes []string `json:"trimPrefixes"` + JSONPath string `json:"jsonPath"` } From 1e3aef5b94cbb520ae480f68a97fd3affe283c70 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Mon, 29 Jul 2024 10:57:04 +0800 Subject: [PATCH 099/152] c/clang/symg:remove chinese --- chore/_xtool/llcppsymg/llcppsymg.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/chore/_xtool/llcppsymg/llcppsymg.go b/chore/_xtool/llcppsymg/llcppsymg.go index 03614a9b..f0907a50 100644 --- a/chore/_xtool/llcppsymg/llcppsymg.go +++ b/chore/_xtool/llcppsymg/llcppsymg.go @@ -122,7 +122,6 @@ func generateDylibPath(lib string) (string, error) { return "", fmt.Errorf("failed to parse pkg-config output: %s", output) } - // 构造dylib路径 dylibPath := filepath.Join(libPath, "lib"+libName+".dylib") return dylibPath, nil } @@ -231,7 +230,6 @@ func generateCPPName(astInfo common.ASTInformation) string { } func generateMangle(astInfo common.ASTInformation, count int, prefixes []string) string { - // 去除前缀 astInfo.Class = removePrefix(astInfo.Class, prefixes) astInfo.Name = removePrefix(astInfo.Name, prefixes) res := "" From c8532a548ccbd564534ed33cfac82fd82fb860d0 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Mon, 29 Jul 2024 11:21:36 +0800 Subject: [PATCH 100/152] c/clang/symg:abstract common type --- chore/_xtool/llcppinfofetch/llcppinfofetch.go | 18 +++++----- chore/_xtool/llcppsymg/common/common.go | 29 ---------------- chore/_xtool/llcppsymg/llcppsymg.go | 33 ++++++++----------- chore/llcppg/types/types.go | 28 ++++++++++++++++ 4 files changed, 50 insertions(+), 58 deletions(-) delete mode 100644 chore/_xtool/llcppsymg/common/common.go diff --git a/chore/_xtool/llcppinfofetch/llcppinfofetch.go b/chore/_xtool/llcppinfofetch/llcppinfofetch.go index 7dd09777..374aa252 100644 --- a/chore/_xtool/llcppinfofetch/llcppinfofetch.go +++ b/chore/_xtool/llcppinfofetch/llcppinfofetch.go @@ -6,7 +6,7 @@ import ( "strconv" "unsafe" - "github.com/goplus/llgo/chore/_xtool/llcppsymg/common" + "github.com/goplus/llgo/chore/llcppg/types" "github.com/goplus/llgo/c" "github.com/goplus/llgo/c/cjson" @@ -16,13 +16,13 @@ import ( type Context struct { namespaceName string className string - astInfo []common.ASTInformation + astInfo []types.ASTInformation currentFile *c.Char } func newContext() *Context { return &Context{ - astInfo: make([]common.ASTInformation, 0), + astInfo: make([]types.ASTInformation, 0), } } @@ -40,9 +40,9 @@ func (c *Context) setCurrentFile(filename *c.Char) { var context = newContext() -func collectFuncInfo(cursor clang.Cursor) common.ASTInformation { +func collectFuncInfo(cursor clang.Cursor) types.ASTInformation { - info := common.ASTInformation{ + info := types.ASTInformation{ Namespace: context.namespaceName, Class: context.className, } @@ -73,12 +73,12 @@ func collectFuncInfo(cursor clang.Cursor) common.ASTInformation { defer typeStr.Dispose() info.ReturnType = c.GoString(typeStr.CStr()) - info.Parameters = make([]common.Parameter, cursor.NumArguments()) + info.Parameters = make([]types.Parameter, cursor.NumArguments()) for i := 0; i < int(cursor.NumArguments()); i++ { argCurSor := cursor.Argument(c.Uint(i)) argType := argCurSor.Type().String() argName := argCurSor.String() - info.Parameters[i] = common.Parameter{ + info.Parameters[i] = types.Parameter{ Name: c.GoString(argName.CStr()), Type: c.GoString(argType.CStr()), } @@ -122,7 +122,7 @@ func visit(cursor, parent clang.Cursor, clientData c.Pointer) clang.ChildVisitRe return clang.ChildVisit_Continue } -func parse(filenames []*c.Char) []common.ASTInformation { +func parse(filenames []*c.Char) []types.ASTInformation { index := clang.CreateIndex(0, 0) args := make([]*c.Char, 3) args[0] = c.Str("-x") @@ -154,7 +154,7 @@ func parse(filenames []*c.Char) []common.ASTInformation { return context.astInfo } -func printJson(infos []common.ASTInformation) { +func printJson(infos []types.ASTInformation) { root := cjson.Array() for _, info := range infos { diff --git a/chore/_xtool/llcppsymg/common/common.go b/chore/_xtool/llcppsymg/common/common.go deleted file mode 100644 index 35fed532..00000000 --- a/chore/_xtool/llcppsymg/common/common.go +++ /dev/null @@ -1,29 +0,0 @@ -package common - -type CPPSymbol struct { - Symbol string `json:"symbol"` - Type string `json:"type"` - Name string `json:"name"` -} - -type ASTInformation struct { - Namespace string `json:"namespace"` - Class string `json:"class"` - Name string `json:"name"` - BaseClasses []string `json:"baseClasses"` - ReturnType string `json:"returnType"` - Location string `json:"location"` - Parameters []Parameter `json:"parameters"` - Symbol string `json:"symbol"` -} - -type Parameter struct { - Name string `json:"name"` - Type string `json:"type"` -} - -type SymbolInfo struct { - Mangle string `json:"mangle"` // C++ Symbol - CPP string `json:"c++"` // C++ function name - Go string `json:"go"` // Go function name -} diff --git a/chore/_xtool/llcppsymg/llcppsymg.go b/chore/_xtool/llcppsymg/llcppsymg.go index f0907a50..da1be9bc 100644 --- a/chore/_xtool/llcppsymg/llcppsymg.go +++ b/chore/_xtool/llcppsymg/llcppsymg.go @@ -30,7 +30,6 @@ import ( "strconv" "strings" - "github.com/goplus/llgo/chore/_xtool/llcppsymg/common" "github.com/goplus/llgo/chore/llcppg/types" ) @@ -85,7 +84,7 @@ func check(err error) { } } -func parseDylibSymbols(lib string) ([]common.CPPSymbol, error) { +func parseDylibSymbols(lib string) ([]types.CPPSymbol, error) { dylibPath, _ := generateDylibPath(lib) nmCmd := exec.Command("nm", "-gU", dylibPath) nmOutput, err := nmCmd.Output() @@ -126,9 +125,9 @@ func generateDylibPath(lib string) (string, error) { return dylibPath, nil } -func parseNmOutput(output []byte) []common.CPPSymbol { +func parseNmOutput(output []byte) []types.CPPSymbol { scanner := bufio.NewScanner(bytes.NewReader(output)) - var symbols []common.CPPSymbol + var symbols []types.CPPSymbol for scanner.Scan() { line := scanner.Text() @@ -138,10 +137,8 @@ func parseNmOutput(output []byte) []common.CPPSymbol { } symbolName := fields[2] // Check if the symbol name starts with an underscore and remove it if present - if strings.HasPrefix(symbolName, "_") { - symbolName = symbolName[1:] - } - symbols = append(symbols, common.CPPSymbol{ + symbolName = strings.TrimPrefix(symbolName, "_") + symbols = append(symbols, types.CPPSymbol{ Symbol: symbolName, Type: fields[1], Name: fields[2], @@ -164,7 +161,7 @@ func decodeSymbolName(symbolName string) (string, error) { } // parseHeaderFile -func parseHeaderFile(config types.Config) ([]common.ASTInformation, error) { +func parseHeaderFile(config types.Config) ([]types.ASTInformation, error) { files := generateHeaderFilePath(config.CFlags, config.Include) fmt.Println(files) headerFileCmd := exec.Command("llcppinfofetch", files...) @@ -176,7 +173,7 @@ func parseHeaderFile(config types.Config) ([]common.ASTInformation, error) { return nil, errors.New("failed to execute header file command") } fmt.Println("headerFileOutput:", string(headerFileOutput), len(headerFileOutput)) - t := make([]common.ASTInformation, 0) + t := make([]types.ASTInformation, 0) err = json.Unmarshal(headerFileOutput, &t) if err != nil { return nil, err @@ -186,11 +183,7 @@ func parseHeaderFile(config types.Config) ([]common.ASTInformation, error) { func generateHeaderFilePath(cflags string, files []string) []string { prefixPath := expandEnv(cflags) - if strings.HasPrefix(prefixPath, "-I") { - prefixPath = prefixPath[2:] - } - - prefixPath = strings.TrimSpace(prefixPath) + prefixPath = strings.TrimPrefix(prefixPath, "-I") var includePaths []string for _, file := range files { includePaths = append(includePaths, filepath.Join(prefixPath, "/"+file)) @@ -198,8 +191,8 @@ func generateHeaderFilePath(cflags string, files []string) []string { return includePaths } -func getCommonSymbols(dylibSymbols []common.CPPSymbol, astInfoList []common.ASTInformation, prefix []string) []common.SymbolInfo { - var commonSymbols []common.SymbolInfo +func getCommonSymbols(dylibSymbols []types.CPPSymbol, astInfoList []types.ASTInformation, prefix []string) []types.SymbolInfo { + var commonSymbols []types.SymbolInfo functionNameMap := make(map[string]int) for _, astInfo := range astInfoList { @@ -207,7 +200,7 @@ func getCommonSymbols(dylibSymbols []common.CPPSymbol, astInfoList []common.ASTI if dylibSym.Symbol == astInfo.Symbol { cppName := generateCPPName(astInfo) functionNameMap[cppName]++ - symbolInfo := common.SymbolInfo{ + symbolInfo := types.SymbolInfo{ Mangle: dylibSym.Symbol, CPP: cppName, Go: generateMangle(astInfo, functionNameMap[cppName], prefix), @@ -221,7 +214,7 @@ func getCommonSymbols(dylibSymbols []common.CPPSymbol, astInfoList []common.ASTI return commonSymbols } -func generateCPPName(astInfo common.ASTInformation) string { +func generateCPPName(astInfo types.ASTInformation) string { cppName := astInfo.Name if astInfo.Class != "" { cppName = astInfo.Class + "::" + astInfo.Name @@ -229,7 +222,7 @@ func generateCPPName(astInfo common.ASTInformation) string { return cppName } -func generateMangle(astInfo common.ASTInformation, count int, prefixes []string) string { +func generateMangle(astInfo types.ASTInformation, count int, prefixes []string) string { astInfo.Class = removePrefix(astInfo.Class, prefixes) astInfo.Name = removePrefix(astInfo.Name, prefixes) res := "" diff --git a/chore/llcppg/types/types.go b/chore/llcppg/types/types.go index 91ac3a5b..e71885ef 100644 --- a/chore/llcppg/types/types.go +++ b/chore/llcppg/types/types.go @@ -25,3 +25,31 @@ type Config struct { TrimPrefixes []string `json:"trimPrefixes"` JSONPath string `json:"jsonPath"` } + +type CPPSymbol struct { + Symbol string `json:"symbol"` + Type string `json:"type"` + Name string `json:"name"` +} + +type ASTInformation struct { + Namespace string `json:"namespace"` + Class string `json:"class"` + Name string `json:"name"` + BaseClasses []string `json:"baseClasses"` + ReturnType string `json:"returnType"` + Location string `json:"location"` + Parameters []Parameter `json:"parameters"` + Symbol string `json:"symbol"` +} + +type Parameter struct { + Name string `json:"name"` + Type string `json:"type"` +} + +type SymbolInfo struct { + Mangle string `json:"mangle"` // C++ Symbol + CPP string `json:"c++"` // C++ function name + Go string `json:"go"` // Go function name +} From f427c0532d463ea491bc722d57aabdfb27bf8782 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Mon, 29 Jul 2024 15:09:54 +0800 Subject: [PATCH 101/152] c/clang/symg:use cjson get conf --- c/cjson/cjson.go | 14 ++++++ chore/_xtool/llcppsymg/llcppsymg.go | 75 +++++++++++------------------ 2 files changed, 42 insertions(+), 47 deletions(-) diff --git a/c/cjson/cjson.go b/c/cjson/cjson.go index 21c0d1b0..51edb48e 100644 --- a/c/cjson/cjson.go +++ b/c/cjson/cjson.go @@ -134,6 +134,20 @@ func (o *JSON) PrintUnformatted() *c.Char { return nil } // llgo:link (*JSON).PrintBuffered C.cJSON_PrintBuffered func (o *JSON) PrintBuffered(prebuffer c.Int, fmt c.Int) *c.Char { return nil } +// llgo:link (*JSON).GetObjectItemCaseSensitive C.cJSON_GetObjectItemCaseSensitive +func (o *JSON) GetObjectItemCaseSensitive(key *c.Char) *JSON { return nil } + +func (o *JSON) GetItem(key string) *JSON { return o.GetObjectItemCaseSensitive(c.AllocaCStr(key)) } + +// llgo:link (*JSON).GetArraySize C.cJSON_GetArraySize +func (o *JSON) GetArraySize() c.Int { return 0 } + +// llgo:link (*JSON).GetArrayItem C.cJSON_GetArrayItem +func (o *JSON) GetArrayItem(index c.Int) *JSON { return nil } + +// llgo:link (*JSON).GetStringValue C.cJSON_GetStringValue +func (o *JSON) GetStringValue() *c.Char { return nil } + //go:linkname Free C.cJSON_free func Free(ptr unsafe.Pointer) diff --git a/chore/_xtool/llcppsymg/llcppsymg.go b/chore/_xtool/llcppsymg/llcppsymg.go index da1be9bc..cfc05998 100644 --- a/chore/_xtool/llcppsymg/llcppsymg.go +++ b/chore/_xtool/llcppsymg/llcppsymg.go @@ -26,10 +26,11 @@ import ( "os" "os/exec" "path/filepath" - "regexp" "strconv" "strings" + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/cjson" "github.com/goplus/llgo/chore/llcppg/types" ) @@ -48,9 +49,10 @@ func main() { } check(err) - var config types.Config - err = json.Unmarshal(data, &config) - check(err) + config, err := getConf(data) + if err != nil { + fmt.Fprintln(os.Stderr, "Failed to parse config file:", cfgFile) + } symbols, err := parseDylibSymbols(config.Libs) check(err) @@ -84,6 +86,26 @@ func check(err error) { } } +func getConf(data []byte) (config types.Config, err error) { + conf := cjson.ParseBytes(data) + defer conf.Delete() + if conf == nil { + return config, errors.New("failed to execute nm command") + } + config.Name = c.GoString(conf.GetItem("name").GetStringValue()) + config.CFlags = c.GoString(conf.GetItem("cflags").GetStringValue()) + config.Libs = c.GoString(conf.GetItem("libs").GetStringValue()) + config.Include = make([]string, conf.GetItem("include").GetArraySize()) + for i := range config.Include { + config.Include[i] = c.GoString(conf.GetItem("include").GetArrayItem(c.Int(i)).GetStringValue()) + } + config.TrimPrefixes = make([]string, conf.GetItem("trimPrefixes").GetArraySize()) + for i := range config.TrimPrefixes { + config.TrimPrefixes[i] = c.GoString(conf.GetItem("trimPrefixes").GetArrayItem(c.Int(i)).GetStringValue()) + } + return +} + func parseDylibSymbols(lib string) ([]types.CPPSymbol, error) { dylibPath, _ := generateDylibPath(lib) nmCmd := exec.Command("nm", "-gU", dylibPath) @@ -106,7 +128,7 @@ func parseDylibSymbols(lib string) ([]types.CPPSymbol, error) { } func generateDylibPath(lib string) (string, error) { - output := expandEnv(lib) + output := lib libPath := "" libName := "" for _, part := range strings.Fields(string(output)) { @@ -160,7 +182,6 @@ func decodeSymbolName(symbolName string) (string, error) { return decodedName, nil } -// parseHeaderFile func parseHeaderFile(config types.Config) ([]types.ASTInformation, error) { files := generateHeaderFilePath(config.CFlags, config.Include) fmt.Println(files) @@ -182,7 +203,7 @@ func parseHeaderFile(config types.Config) ([]types.ASTInformation, error) { } func generateHeaderFilePath(cflags string, files []string) []string { - prefixPath := expandEnv(cflags) + prefixPath := cflags prefixPath = strings.TrimPrefix(prefixPath, "-I") var includePaths []string for _, file := range files { @@ -260,43 +281,3 @@ func removePrefix(str string, prefixes []string) string { } return str } - -var ( - reSubcmd = regexp.MustCompile(`\$\([^)]+\)`) - reFlag = regexp.MustCompile(`[^ \t\n]+`) -) - -func expandEnv(s string) string { - return expandEnvWithCmd(s) -} - -func expandEnvWithCmd(s string) string { - expanded := reSubcmd.ReplaceAllStringFunc(s, func(m string) string { - subcmd := strings.TrimSpace(s[2 : len(s)-1]) - - args := parseSubcmd(subcmd) - - cmd := args[0] - - if cmd != "pkg-config" && cmd != "llvm-config" { - fmt.Fprintf(os.Stderr, "expand cmd only support pkg-config and llvm-config: '%s'\n", subcmd) - return "" - } - - var out []byte - var err error - out, err = exec.Command(cmd, args[1:]...).Output() - - if err != nil { - // TODO(kindy): log in verbose mode - return "" - } - - return string(out) - }) - return os.Expand(expanded, os.Getenv) -} - -func parseSubcmd(s string) []string { - return reFlag.FindAllString(s, -1) -} From 01d0338851a3c3cbeaa6fc8ab3856c81bccf0d95 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Mon, 29 Jul 2024 17:21:32 +0800 Subject: [PATCH 102/152] c/clang/symg:use llvm to demangle name --- chore/_xtool/llcppsymg/llcppsymg.go | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/chore/_xtool/llcppsymg/llcppsymg.go b/chore/_xtool/llcppsymg/llcppsymg.go index cfc05998..00486d13 100644 --- a/chore/_xtool/llcppsymg/llcppsymg.go +++ b/chore/_xtool/llcppsymg/llcppsymg.go @@ -32,6 +32,7 @@ import ( "github.com/goplus/llgo/c" "github.com/goplus/llgo/c/cjson" "github.com/goplus/llgo/chore/llcppg/types" + "github.com/goplus/llgo/cpp/llvm" ) func main() { @@ -53,8 +54,8 @@ func main() { if err != nil { fmt.Fprintln(os.Stderr, "Failed to parse config file:", cfgFile) } - symbols, err := parseDylibSymbols(config.Libs) + check(err) files, err := parseHeaderFile(config) @@ -109,7 +110,7 @@ func getConf(data []byte) (config types.Config, err error) { func parseDylibSymbols(lib string) ([]types.CPPSymbol, error) { dylibPath, _ := generateDylibPath(lib) nmCmd := exec.Command("nm", "-gU", dylibPath) - nmOutput, err := nmCmd.Output() + nmOutput, err := nmCmd.Output() // maybe lock if err != nil { return nil, errors.New("failed to execute nm command") } @@ -171,13 +172,9 @@ func parseNmOutput(output []byte) []types.CPPSymbol { } func decodeSymbolName(symbolName string) (string, error) { - cppfiltCmd := exec.Command("c++filt", symbolName) - cppfiltOutput, err := cppfiltCmd.Output() - if err != nil { - return "", errors.New("failed to execute c++filt command") - } - - decodedName := strings.TrimSpace(string(cppfiltOutput)) + llvm.ItaniumDemangle(symbolName, true) + demangleName := c.GoString(llvm.ItaniumDemangle(symbolName, true)) + decodedName := strings.TrimSpace(string(demangleName)) decodedName = strings.ReplaceAll(decodedName, "std::__1::basic_string, std::__1::allocator > const", "std::string") return decodedName, nil } From 84ca145663cfd93ae0ae118c0d949bee2083f849 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Mon, 29 Jul 2024 17:57:38 +0800 Subject: [PATCH 103/152] c/clang/symg:merge llcppinfofetch to llcppsymg --- chore/_xtool/llcppsymg/llcppsymg.go | 28 +----- .../parse/parse.go} | 91 +++++++++---------- 2 files changed, 46 insertions(+), 73 deletions(-) rename chore/_xtool/{llcppinfofetch/llcppinfofetch.go => llcppsymg/parse/parse.go} (65%) diff --git a/chore/_xtool/llcppsymg/llcppsymg.go b/chore/_xtool/llcppsymg/llcppsymg.go index 00486d13..33aabe67 100644 --- a/chore/_xtool/llcppsymg/llcppsymg.go +++ b/chore/_xtool/llcppsymg/llcppsymg.go @@ -31,6 +31,7 @@ import ( "github.com/goplus/llgo/c" "github.com/goplus/llgo/c/cjson" + "github.com/goplus/llgo/chore/_xtool/llcppsymg/parse" "github.com/goplus/llgo/chore/llcppg/types" "github.com/goplus/llgo/cpp/llvm" ) @@ -58,10 +59,11 @@ func main() { check(err) - files, err := parseHeaderFile(config) + filepaths := generateHeaderFilePath(config.CFlags, config.Include) + astInfos, err := parse.ParseHeaderFile(filepaths) check(err) - symbolInfo := getCommonSymbols(symbols, files, config.TrimPrefixes) + symbolInfo := getCommonSymbols(symbols, astInfos, config.TrimPrefixes) jsonData, err := json.MarshalIndent(symbolInfo, "", " ") check(err) @@ -91,7 +93,7 @@ func getConf(data []byte) (config types.Config, err error) { conf := cjson.ParseBytes(data) defer conf.Delete() if conf == nil { - return config, errors.New("failed to execute nm command") + return config, errors.New("failed to parse config") } config.Name = c.GoString(conf.GetItem("name").GetStringValue()) config.CFlags = c.GoString(conf.GetItem("cflags").GetStringValue()) @@ -179,26 +181,6 @@ func decodeSymbolName(symbolName string) (string, error) { return decodedName, nil } -func parseHeaderFile(config types.Config) ([]types.ASTInformation, error) { - files := generateHeaderFilePath(config.CFlags, config.Include) - fmt.Println(files) - headerFileCmd := exec.Command("llcppinfofetch", files...) - - fmt.Println("Executing command:", headerFileCmd.String()) - - headerFileOutput, err := headerFileCmd.Output() - if err != nil { - return nil, errors.New("failed to execute header file command") - } - fmt.Println("headerFileOutput:", string(headerFileOutput), len(headerFileOutput)) - t := make([]types.ASTInformation, 0) - err = json.Unmarshal(headerFileOutput, &t) - if err != nil { - return nil, err - } - return t, nil -} - func generateHeaderFilePath(cflags string, files []string) []string { prefixPath := cflags prefixPath = strings.TrimPrefix(prefixPath, "-I") diff --git a/chore/_xtool/llcppinfofetch/llcppinfofetch.go b/chore/_xtool/llcppsymg/parse/parse.go similarity index 65% rename from chore/_xtool/llcppinfofetch/llcppinfofetch.go rename to chore/_xtool/llcppsymg/parse/parse.go index 374aa252..092c93fd 100644 --- a/chore/_xtool/llcppinfofetch/llcppinfofetch.go +++ b/chore/_xtool/llcppsymg/parse/parse.go @@ -1,23 +1,20 @@ -package main +package parse import ( - "fmt" - "os" + "errors" "strconv" "unsafe" - "github.com/goplus/llgo/chore/llcppg/types" - "github.com/goplus/llgo/c" - "github.com/goplus/llgo/c/cjson" "github.com/goplus/llgo/c/clang" + "github.com/goplus/llgo/chore/llcppg/types" ) type Context struct { namespaceName string className string astInfo []types.ASTInformation - currentFile *c.Char + currentFile string } func newContext() *Context { @@ -34,7 +31,7 @@ func (c *Context) setClassName(name string) { c.className = name } -func (c *Context) setCurrentFile(filename *c.Char) { +func (c *Context) setCurrentFile(filename string) { c.currentFile = filename } @@ -109,37 +106,50 @@ func visit(cursor, parent clang.Cursor, clientData c.Pointer) clang.ChildVisitRe loc.SpellingLocation(&file, &line, &column, nil) filename := file.FileName() - if c.Strcmp(filename.CStr(), context.currentFile) == 0 { + if c.Strcmp(filename.CStr(), c.AllocaCStr(context.currentFile)) == 0 { info := collectFuncInfo(cursor) info.Location = c.GoString(filename.CStr()) + ":" + strconv.Itoa(int(line)) + ":" + strconv.Itoa(int(column)) context.astInfo = append(context.astInfo, info) } defer filename.Dispose() - } return clang.ChildVisit_Continue } -func parse(filenames []*c.Char) []types.ASTInformation { +// func main() { +// if c.Argc < 2 { +// fmt.Fprintln(os.Stderr, "Usage: [ ...]\n") +// return +// } else { +// filenames := make([]*c.Char, c.Argc-1) +// for i := 1; i < int(c.Argc); i++ { +// filenames[i-1] = c.Index(c.Argv, c.Int(i)) +// } +// printJson(parse(filenames)) +// } +// } + +func ParseHeaderFile(filepaths []string) ([]types.ASTInformation, error) { + index := clang.CreateIndex(0, 0) args := make([]*c.Char, 3) args[0] = c.Str("-x") args[1] = c.Str("c++") args[2] = c.Str("-std=c++11") + context = newContext() - for _, filename := range filenames { + for _, filename := range filepaths { unit := index.ParseTranslationUnit( - filename, + c.AllocaCStr(filename), unsafe.SliceData(args), 3, nil, 0, clang.TranslationUnit_None, ) if unit == nil { - fmt.Printf("Unable to parse translation unit for file: %s. Skipping.\n", c.GoString(filename)) - continue + return nil, errors.New("Unable to parse translation unit for file " + filename) } cursor := unit.Cursor() @@ -152,42 +162,23 @@ func parse(filenames []*c.Char) []types.ASTInformation { index.Dispose() - return context.astInfo -} -func printJson(infos []types.ASTInformation) { - root := cjson.Array() + return context.astInfo, nil - for _, info := range infos { - item := cjson.Object() - item.SetItem(c.Str("namespace"), cjson.String(c.AllocaCStr(info.Namespace))) - item.SetItem(c.Str("class"), cjson.String(c.AllocaCStr(info.Class))) - item.SetItem(c.Str("name"), cjson.String(c.AllocaCStr(info.Name))) - item.SetItem(c.Str("returnType"), cjson.String(c.AllocaCStr(info.ReturnType))) - item.SetItem(c.Str("location"), cjson.String(c.AllocaCStr(info.Location))) - item.SetItem(c.Str("symbol"), cjson.String(c.AllocaCStr(info.Symbol))) + // files := generateHeaderFilePath(config.CFlags, config.Include) + // fmt.Println(files) + // headerFileCmd := exec.Command("llcppinfofetch", files...) - params := cjson.Array() - for _, param := range info.Parameters { - paramObj := cjson.Object() - paramObj.SetItem(c.Str("name"), cjson.String(c.AllocaCStr(param.Name))) - paramObj.SetItem(c.Str("type"), cjson.String(c.AllocaCStr(param.Type))) - params.AddItem(paramObj) - } - item.SetItem(c.Str("parameters"), params) + // fmt.Println("Executing command:", headerFileCmd.String()) - root.AddItem(item) - } - c.Printf(c.Str("%s\n"), root.Print()) -} -func main() { - if c.Argc < 2 { - fmt.Fprintln(os.Stderr, "Usage: [ ...]\n") - return - } else { - filenames := make([]*c.Char, c.Argc-1) - for i := 1; i < int(c.Argc); i++ { - filenames[i-1] = c.Index(c.Argv, c.Int(i)) - } - printJson(parse(filenames)) - } + // headerFileOutput, err := headerFileCmd.Output() + // if err != nil { + // return nil, errors.New("failed to execute header file command") + // } + // fmt.Println("headerFileOutput:", string(headerFileOutput), len(headerFileOutput)) + // t := make([]types.ASTInformation, 0) + // err = json.Unmarshal(headerFileOutput, &t) + // if err != nil { + // return nil, err + // } + // return t, nil } From aca3a0522264237d1dfe99de56a4935986b3431f Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Mon, 29 Jul 2024 20:36:04 +0800 Subject: [PATCH 104/152] c/clang/symg:abstract json item fetch --- c/cjson/cjson.go | 2 -- chore/_xtool/llcppsymg/llcppsymg.go | 34 +++++++++++++++++++++-------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/c/cjson/cjson.go b/c/cjson/cjson.go index 51edb48e..af969e2d 100644 --- a/c/cjson/cjson.go +++ b/c/cjson/cjson.go @@ -137,8 +137,6 @@ func (o *JSON) PrintBuffered(prebuffer c.Int, fmt c.Int) *c.Char { return nil } // llgo:link (*JSON).GetObjectItemCaseSensitive C.cJSON_GetObjectItemCaseSensitive func (o *JSON) GetObjectItemCaseSensitive(key *c.Char) *JSON { return nil } -func (o *JSON) GetItem(key string) *JSON { return o.GetObjectItemCaseSensitive(c.AllocaCStr(key)) } - // llgo:link (*JSON).GetArraySize C.cJSON_GetArraySize func (o *JSON) GetArraySize() c.Int { return 0 } diff --git a/chore/_xtool/llcppsymg/llcppsymg.go b/chore/_xtool/llcppsymg/llcppsymg.go index 33aabe67..0296e0fa 100644 --- a/chore/_xtool/llcppsymg/llcppsymg.go +++ b/chore/_xtool/llcppsymg/llcppsymg.go @@ -52,6 +52,8 @@ func main() { check(err) config, err := getConf(data) + check(err) + if err != nil { fmt.Fprintln(os.Stderr, "Failed to parse config file:", cfgFile) } @@ -95,16 +97,30 @@ func getConf(data []byte) (config types.Config, err error) { if conf == nil { return config, errors.New("failed to parse config") } - config.Name = c.GoString(conf.GetItem("name").GetStringValue()) - config.CFlags = c.GoString(conf.GetItem("cflags").GetStringValue()) - config.Libs = c.GoString(conf.GetItem("libs").GetStringValue()) - config.Include = make([]string, conf.GetItem("include").GetArraySize()) - for i := range config.Include { - config.Include[i] = c.GoString(conf.GetItem("include").GetArrayItem(c.Int(i)).GetStringValue()) + config.Name = getStringItem(conf, "name", "") + config.CFlags = getStringItem(conf, "cflags", "") + config.Libs = getStringItem(conf, "libs", "") + config.Include = getStringArrayItem(conf, "include") + config.TrimPrefixes = getStringArrayItem(conf, "trimPrefixes") + return +} + +func getStringItem(obj *cjson.JSON, key string, defval string) (value string) { + item := obj.GetObjectItemCaseSensitive(c.AllocaCStr(key)) + if item == nil { + return defval } - config.TrimPrefixes = make([]string, conf.GetItem("trimPrefixes").GetArraySize()) - for i := range config.TrimPrefixes { - config.TrimPrefixes[i] = c.GoString(conf.GetItem("trimPrefixes").GetArrayItem(c.Int(i)).GetStringValue()) + return c.GoString(item.GetStringValue()) +} + +func getStringArrayItem(obj *cjson.JSON, key string) (value []string) { + item := obj.GetObjectItemCaseSensitive(c.AllocaCStr(key)) + if item == nil { + return + } + value = make([]string, item.GetArraySize()) + for i := range value { + value[i] = c.GoString(item.GetArrayItem(c.Int(i)).GetStringValue()) } return } From 87382aad4da77e53abd38866408a39319f765c6d Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Mon, 29 Jul 2024 22:10:30 +0800 Subject: [PATCH 105/152] c/clang/symg:use unsafe.String to avoid memory copy --- chore/_xtool/llcppsymg/llcppsymg.go | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/chore/_xtool/llcppsymg/llcppsymg.go b/chore/_xtool/llcppsymg/llcppsymg.go index 0296e0fa..f63c5711 100644 --- a/chore/_xtool/llcppsymg/llcppsymg.go +++ b/chore/_xtool/llcppsymg/llcppsymg.go @@ -28,6 +28,7 @@ import ( "path/filepath" "strconv" "strings" + "unsafe" "github.com/goplus/llgo/c" "github.com/goplus/llgo/c/cjson" @@ -91,18 +92,24 @@ func check(err error) { } } -func getConf(data []byte) (config types.Config, err error) { +func getConf(data []byte) (*types.Config, error) { conf := cjson.ParseBytes(data) - defer conf.Delete() if conf == nil { - return config, errors.New("failed to parse config") + return nil, errors.New("failed to parse config") } - config.Name = getStringItem(conf, "name", "") - config.CFlags = getStringItem(conf, "cflags", "") - config.Libs = getStringItem(conf, "libs", "") - config.Include = getStringArrayItem(conf, "include") - config.TrimPrefixes = getStringArrayItem(conf, "trimPrefixes") - return + config := &types.Config{ + Name: getStringItem(conf, "name", ""), + CFlags: getStringItem(conf, "cflags", ""), + Libs: getStringItem(conf, "libs", ""), + Include: getStringArrayItem(conf, "include"), + TrimPrefixes: getStringArrayItem(conf, "trimPrefixes"), + } + return config, nil +} + +func getString(obj *cjson.JSON) (value string) { + str := obj.GetStringValue() + return unsafe.String((*byte)(unsafe.Pointer(str)), c.Strlen(str)) } func getStringItem(obj *cjson.JSON, key string, defval string) (value string) { @@ -110,7 +117,7 @@ func getStringItem(obj *cjson.JSON, key string, defval string) (value string) { if item == nil { return defval } - return c.GoString(item.GetStringValue()) + return getString(item) } func getStringArrayItem(obj *cjson.JSON, key string) (value []string) { @@ -120,7 +127,7 @@ func getStringArrayItem(obj *cjson.JSON, key string) (value []string) { } value = make([]string, item.GetArraySize()) for i := range value { - value[i] = c.GoString(item.GetArrayItem(c.Int(i)).GetStringValue()) + value[i] = getString(item.GetArrayItem(c.Int(i))) } return } From 8d840e694d8cc2856a1b38873e4edde7fd27ba9c Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Mon, 29 Jul 2024 22:11:44 +0800 Subject: [PATCH 106/152] c/clang/symg:remove unuse comment --- chore/_xtool/llcppsymg/parse/parse.go | 31 --------------------------- 1 file changed, 31 deletions(-) diff --git a/chore/_xtool/llcppsymg/parse/parse.go b/chore/_xtool/llcppsymg/parse/parse.go index 092c93fd..5a15d26a 100644 --- a/chore/_xtool/llcppsymg/parse/parse.go +++ b/chore/_xtool/llcppsymg/parse/parse.go @@ -118,19 +118,6 @@ func visit(cursor, parent clang.Cursor, clientData c.Pointer) clang.ChildVisitRe return clang.ChildVisit_Continue } -// func main() { -// if c.Argc < 2 { -// fmt.Fprintln(os.Stderr, "Usage: [ ...]\n") -// return -// } else { -// filenames := make([]*c.Char, c.Argc-1) -// for i := 1; i < int(c.Argc); i++ { -// filenames[i-1] = c.Index(c.Argv, c.Int(i)) -// } -// printJson(parse(filenames)) -// } -// } - func ParseHeaderFile(filepaths []string) ([]types.ASTInformation, error) { index := clang.CreateIndex(0, 0) @@ -163,22 +150,4 @@ func ParseHeaderFile(filepaths []string) ([]types.ASTInformation, error) { index.Dispose() return context.astInfo, nil - - // files := generateHeaderFilePath(config.CFlags, config.Include) - // fmt.Println(files) - // headerFileCmd := exec.Command("llcppinfofetch", files...) - - // fmt.Println("Executing command:", headerFileCmd.String()) - - // headerFileOutput, err := headerFileCmd.Output() - // if err != nil { - // return nil, errors.New("failed to execute header file command") - // } - // fmt.Println("headerFileOutput:", string(headerFileOutput), len(headerFileOutput)) - // t := make([]types.ASTInformation, 0) - // err = json.Unmarshal(headerFileOutput, &t) - // if err != nil { - // return nil, err - // } - // return t, nil } From 121923016873cde4a7ce62651b34d81c665507b6 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Tue, 30 Jul 2024 00:05:23 +0800 Subject: [PATCH 107/152] c/clang/symg:genSymbolTableFile --- chore/_xtool/llcppsymg/llcppsymg.go | 45 +++++++++++++++++++---------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/chore/_xtool/llcppsymg/llcppsymg.go b/chore/_xtool/llcppsymg/llcppsymg.go index f63c5711..af1a8538 100644 --- a/chore/_xtool/llcppsymg/llcppsymg.go +++ b/chore/_xtool/llcppsymg/llcppsymg.go @@ -19,7 +19,6 @@ package main import ( "bufio" "bytes" - "encoding/json" "errors" "fmt" "io" @@ -68,21 +67,7 @@ func main() { symbolInfo := getCommonSymbols(symbols, astInfos, config.TrimPrefixes) - jsonData, err := json.MarshalIndent(symbolInfo, "", " ") - check(err) - - fileName := "llcppg.symb.json" - err = os.WriteFile(fileName, jsonData, 0644) - check(err) - - absJSONPath, err := filepath.Abs(fileName) - check(err) - - config.JSONPath = absJSONPath - updatedCfgData, err := json.MarshalIndent(config, "", " ") - check(err) - - err = os.WriteFile(cfgFile, updatedCfgData, 0644) + err = genSymbolTableFile(symbolInfo) check(err) } @@ -283,3 +268,31 @@ func removePrefix(str string, prefixes []string) string { } return str } + +func genSymbolTableFile(symbolInfos []types.SymbolInfo) error { + root := cjson.Array() + defer root.Delete() + + for _, symbol := range symbolInfos { + item := cjson.Object() + item.SetItem(c.Str("mangle"), cjson.String(c.AllocaCStr(symbol.Mangle))) + item.SetItem(c.Str("c++"), cjson.String(c.AllocaCStr(symbol.CPP))) + item.SetItem(c.Str("go"), cjson.String(c.AllocaCStr(symbol.Go))) + root.AddItem(item) + } + + fileName := "llcppg.symb.json" + cStr := root.Print() + if cStr == nil { + return errors.New("symbol table is empty") + } + defer c.Free(unsafe.Pointer(cStr)) + + data := unsafe.Slice((*byte)(unsafe.Pointer(cStr)), c.Strlen(cStr)) + + if err := os.WriteFile(fileName, data, 0644); err != nil { + return errors.New("failed to write symbol table file") + } + + return nil +} From 67b10d8d38a9c2517b1fcab37d814093d2d59d07 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Tue, 30 Jul 2024 09:33:05 +0800 Subject: [PATCH 108/152] c/clang/symg:refine config usage --- chore/_xtool/llcppsymg/llcppsymg.go | 26 ++++++++++++++++---------- chore/llcppg/types/types.go | 9 +++++++++ 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/chore/_xtool/llcppsymg/llcppsymg.go b/chore/_xtool/llcppsymg/llcppsymg.go index af1a8538..8d356a63 100644 --- a/chore/_xtool/llcppsymg/llcppsymg.go +++ b/chore/_xtool/llcppsymg/llcppsymg.go @@ -53,6 +53,7 @@ func main() { config, err := getConf(data) check(err) + defer config.Delete() if err != nil { fmt.Fprintln(os.Stderr, "Failed to parse config file:", cfgFile) @@ -77,19 +78,24 @@ func check(err error) { } } -func getConf(data []byte) (*types.Config, error) { - conf := cjson.ParseBytes(data) - if conf == nil { - return nil, errors.New("failed to parse config") +func getConf(data []byte) (types.Conf, error) { + parsedConf := cjson.ParseBytes(data) + if parsedConf == nil { + return types.Conf{}, errors.New("failed to parse config") } + config := &types.Config{ - Name: getStringItem(conf, "name", ""), - CFlags: getStringItem(conf, "cflags", ""), - Libs: getStringItem(conf, "libs", ""), - Include: getStringArrayItem(conf, "include"), - TrimPrefixes: getStringArrayItem(conf, "trimPrefixes"), + Name: getStringItem(parsedConf, "name", ""), + CFlags: getStringItem(parsedConf, "cflags", ""), + Libs: getStringItem(parsedConf, "libs", ""), + Include: getStringArrayItem(parsedConf, "include"), + TrimPrefixes: getStringArrayItem(parsedConf, "trimPrefixes"), } - return config, nil + + return types.Conf{ + JSON: parsedConf, + Config: config, + }, nil } func getString(obj *cjson.JSON) (value string) { diff --git a/chore/llcppg/types/types.go b/chore/llcppg/types/types.go index e71885ef..bd516458 100644 --- a/chore/llcppg/types/types.go +++ b/chore/llcppg/types/types.go @@ -16,6 +16,10 @@ package types +import ( + "github.com/goplus/llgo/c/cjson" +) + // Config represents a configuration for the llcppg tool. type Config struct { Name string `json:"name"` @@ -26,6 +30,11 @@ type Config struct { JSONPath string `json:"jsonPath"` } +type Conf struct { + *cjson.JSON + *Config +} + type CPPSymbol struct { Symbol string `json:"symbol"` Type string `json:"type"` From 14face336e1596461eb3549d2b86bfd301a8b5bc Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Tue, 30 Jul 2024 10:33:52 +0800 Subject: [PATCH 109/152] c/clang/symg:remove JSONPath --- chore/llcppg/types/types.go | 1 - 1 file changed, 1 deletion(-) diff --git a/chore/llcppg/types/types.go b/chore/llcppg/types/types.go index bd516458..ece5c0e3 100644 --- a/chore/llcppg/types/types.go +++ b/chore/llcppg/types/types.go @@ -27,7 +27,6 @@ type Config struct { Libs string `json:"libs"` Include []string `json:"include"` TrimPrefixes []string `json:"trimPrefixes"` - JSONPath string `json:"jsonPath"` } type Conf struct { From 099c80e04b3caecbc64c7c30e1599bb7c3670189 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Tue, 30 Jul 2024 15:07:33 +0800 Subject: [PATCH 110/152] c/clang/symg: use xtool/nm to parse symbol --- chore/_xtool/llcppsymg/llcppsymg.go | 78 ++++++++++++++--------------- chore/llcppg/types/types.go | 6 +-- 2 files changed, 40 insertions(+), 44 deletions(-) diff --git a/chore/_xtool/llcppsymg/llcppsymg.go b/chore/_xtool/llcppsymg/llcppsymg.go index 8d356a63..a3d1bede 100644 --- a/chore/_xtool/llcppsymg/llcppsymg.go +++ b/chore/_xtool/llcppsymg/llcppsymg.go @@ -17,13 +17,10 @@ package main import ( - "bufio" - "bytes" "errors" "fmt" "io" "os" - "os/exec" "path/filepath" "strconv" "strings" @@ -34,6 +31,7 @@ import ( "github.com/goplus/llgo/chore/_xtool/llcppsymg/parse" "github.com/goplus/llgo/chore/llcppg/types" "github.com/goplus/llgo/cpp/llvm" + "github.com/goplus/llgo/xtool/nm" ) func main() { @@ -124,21 +122,26 @@ func getStringArrayItem(obj *cjson.JSON, key string) (value []string) { } func parseDylibSymbols(lib string) ([]types.CPPSymbol, error) { - dylibPath, _ := generateDylibPath(lib) - nmCmd := exec.Command("nm", "-gU", dylibPath) - nmOutput, err := nmCmd.Output() // maybe lock + dylibPath, err := generateDylibPath(lib) if err != nil { - return nil, errors.New("failed to execute nm command") + return nil, errors.New("failed to generate dylib path") } - symbols := parseNmOutput(nmOutput) + files, err := nm.New("").List(dylibPath) + if err != nil { + return nil, errors.New("failed to list symbols in dylib") + } - for i, sym := range symbols { - decodedName, err := decodeSymbolName(sym.Name) - if err != nil { - return nil, err + var symbols []types.CPPSymbol + + for _, file := range files { + for _, sym := range file.Symbols { + demangleName := decodeSymbolName(sym.Name) + symbols = append(symbols, types.CPPSymbol{ + Symbol: sym, + DemangleName: demangleName, + }) } - symbols[i].Name = decodedName } return symbols, nil @@ -164,35 +167,28 @@ func generateDylibPath(lib string) (string, error) { return dylibPath, nil } -func parseNmOutput(output []byte) []types.CPPSymbol { - scanner := bufio.NewScanner(bytes.NewReader(output)) - var symbols []types.CPPSymbol - - for scanner.Scan() { - line := scanner.Text() - fields := strings.Fields(line) - if len(fields) < 3 { - continue - } - symbolName := fields[2] - // Check if the symbol name starts with an underscore and remove it if present - symbolName = strings.TrimPrefix(symbolName, "_") - symbols = append(symbols, types.CPPSymbol{ - Symbol: symbolName, - Type: fields[1], - Name: fields[2], - }) +func decodeSymbolName(symbolName string) string { + if symbolName == "" { + return "" } - return symbols -} + demangled := llvm.ItaniumDemangle(symbolName, true) + if demangled == nil { + return symbolName + } + defer c.Free(unsafe.Pointer(demangled)) -func decodeSymbolName(symbolName string) (string, error) { - llvm.ItaniumDemangle(symbolName, true) - demangleName := c.GoString(llvm.ItaniumDemangle(symbolName, true)) - decodedName := strings.TrimSpace(string(demangleName)) - decodedName = strings.ReplaceAll(decodedName, "std::__1::basic_string, std::__1::allocator > const", "std::string") - return decodedName, nil + demangleName := c.GoString(demangled) + if demangleName == "" { + return symbolName + } + + decodedName := strings.TrimSpace(demangleName) + decodedName = strings.ReplaceAll(decodedName, + "std::__1::basic_string, std::__1::allocator > const", + "std::string") + + return decodedName } func generateHeaderFilePath(cflags string, files []string) []string { @@ -211,11 +207,11 @@ func getCommonSymbols(dylibSymbols []types.CPPSymbol, astInfoList []types.ASTInf for _, astInfo := range astInfoList { for _, dylibSym := range dylibSymbols { - if dylibSym.Symbol == astInfo.Symbol { + if strings.TrimPrefix(dylibSym.Name, "_") == astInfo.Symbol { cppName := generateCPPName(astInfo) functionNameMap[cppName]++ symbolInfo := types.SymbolInfo{ - Mangle: dylibSym.Symbol, + Mangle: strings.TrimPrefix(dylibSym.Name, "_"), CPP: cppName, Go: generateMangle(astInfo, functionNameMap[cppName], prefix), } diff --git a/chore/llcppg/types/types.go b/chore/llcppg/types/types.go index ece5c0e3..6ca9d833 100644 --- a/chore/llcppg/types/types.go +++ b/chore/llcppg/types/types.go @@ -18,6 +18,7 @@ package types import ( "github.com/goplus/llgo/c/cjson" + "github.com/goplus/llgo/xtool/nm" ) // Config represents a configuration for the llcppg tool. @@ -35,9 +36,8 @@ type Conf struct { } type CPPSymbol struct { - Symbol string `json:"symbol"` - Type string `json:"type"` - Name string `json:"name"` + DemangleName string + *nm.Symbol } type ASTInformation struct { From f93d3381e08c12408da25c0ab4939f34cc73583c Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Tue, 30 Jul 2024 15:17:37 +0800 Subject: [PATCH 111/152] c/clang/symg:move get conf func --- chore/_xtool/llcppsymg/config/config.go | 60 +++++++++++++++++++++++++ chore/_xtool/llcppsymg/llcppsymg.go | 56 +++-------------------- chore/llcppg/types/types.go | 6 --- 3 files changed, 66 insertions(+), 56 deletions(-) create mode 100644 chore/_xtool/llcppsymg/config/config.go diff --git a/chore/_xtool/llcppsymg/config/config.go b/chore/_xtool/llcppsymg/config/config.go new file mode 100644 index 00000000..3545e25b --- /dev/null +++ b/chore/_xtool/llcppsymg/config/config.go @@ -0,0 +1,60 @@ +package config + +import ( + "errors" + "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/cjson" + "github.com/goplus/llgo/chore/llcppg/types" +) + +type Conf struct { + *cjson.JSON + *types.Config +} + +func GetConf(data []byte) (Conf, error) { + parsedConf := cjson.ParseBytes(data) + if parsedConf == nil { + return Conf{}, errors.New("failed to parse config") + } + + config := &types.Config{ + Name: GetStringItem(parsedConf, "name", ""), + CFlags: GetStringItem(parsedConf, "cflags", ""), + Libs: GetStringItem(parsedConf, "libs", ""), + Include: GetStringArrayItem(parsedConf, "include"), + TrimPrefixes: GetStringArrayItem(parsedConf, "trimPrefixes"), + } + + return Conf{ + JSON: parsedConf, + Config: config, + }, nil +} + +func GetString(obj *cjson.JSON) (value string) { + str := obj.GetStringValue() + return unsafe.String((*byte)(unsafe.Pointer(str)), c.Strlen(str)) +} + +func GetStringItem(obj *cjson.JSON, key string, defval string) (value string) { + item := obj.GetObjectItemCaseSensitive(c.AllocaCStr(key)) + if item == nil { + return defval + } + return GetString(item) +} + +func GetStringArrayItem(obj *cjson.JSON, key string) (value []string) { + item := obj.GetObjectItemCaseSensitive(c.AllocaCStr(key)) + if item == nil { + return + } + value = make([]string, item.GetArraySize()) + for i := range value { + value[i] = GetString(item.GetArrayItem(c.Int(i))) + } + return +} diff --git a/chore/_xtool/llcppsymg/llcppsymg.go b/chore/_xtool/llcppsymg/llcppsymg.go index a3d1bede..6edde02f 100644 --- a/chore/_xtool/llcppsymg/llcppsymg.go +++ b/chore/_xtool/llcppsymg/llcppsymg.go @@ -28,6 +28,7 @@ import ( "github.com/goplus/llgo/c" "github.com/goplus/llgo/c/cjson" + "github.com/goplus/llgo/chore/_xtool/llcppsymg/config" "github.com/goplus/llgo/chore/_xtool/llcppsymg/parse" "github.com/goplus/llgo/chore/llcppg/types" "github.com/goplus/llgo/cpp/llvm" @@ -49,22 +50,22 @@ func main() { } check(err) - config, err := getConf(data) + conf, err := config.GetConf(data) check(err) - defer config.Delete() + defer conf.Delete() if err != nil { fmt.Fprintln(os.Stderr, "Failed to parse config file:", cfgFile) } - symbols, err := parseDylibSymbols(config.Libs) + symbols, err := parseDylibSymbols(conf.Libs) check(err) - filepaths := generateHeaderFilePath(config.CFlags, config.Include) + filepaths := generateHeaderFilePath(conf.CFlags, conf.Include) astInfos, err := parse.ParseHeaderFile(filepaths) check(err) - symbolInfo := getCommonSymbols(symbols, astInfos, config.TrimPrefixes) + symbolInfo := getCommonSymbols(symbols, astInfos, conf.TrimPrefixes) err = genSymbolTableFile(symbolInfo) check(err) @@ -76,51 +77,6 @@ func check(err error) { } } -func getConf(data []byte) (types.Conf, error) { - parsedConf := cjson.ParseBytes(data) - if parsedConf == nil { - return types.Conf{}, errors.New("failed to parse config") - } - - config := &types.Config{ - Name: getStringItem(parsedConf, "name", ""), - CFlags: getStringItem(parsedConf, "cflags", ""), - Libs: getStringItem(parsedConf, "libs", ""), - Include: getStringArrayItem(parsedConf, "include"), - TrimPrefixes: getStringArrayItem(parsedConf, "trimPrefixes"), - } - - return types.Conf{ - JSON: parsedConf, - Config: config, - }, nil -} - -func getString(obj *cjson.JSON) (value string) { - str := obj.GetStringValue() - return unsafe.String((*byte)(unsafe.Pointer(str)), c.Strlen(str)) -} - -func getStringItem(obj *cjson.JSON, key string, defval string) (value string) { - item := obj.GetObjectItemCaseSensitive(c.AllocaCStr(key)) - if item == nil { - return defval - } - return getString(item) -} - -func getStringArrayItem(obj *cjson.JSON, key string) (value []string) { - item := obj.GetObjectItemCaseSensitive(c.AllocaCStr(key)) - if item == nil { - return - } - value = make([]string, item.GetArraySize()) - for i := range value { - value[i] = getString(item.GetArrayItem(c.Int(i))) - } - return -} - func parseDylibSymbols(lib string) ([]types.CPPSymbol, error) { dylibPath, err := generateDylibPath(lib) if err != nil { diff --git a/chore/llcppg/types/types.go b/chore/llcppg/types/types.go index 6ca9d833..9d700881 100644 --- a/chore/llcppg/types/types.go +++ b/chore/llcppg/types/types.go @@ -17,7 +17,6 @@ package types import ( - "github.com/goplus/llgo/c/cjson" "github.com/goplus/llgo/xtool/nm" ) @@ -30,11 +29,6 @@ type Config struct { TrimPrefixes []string `json:"trimPrefixes"` } -type Conf struct { - *cjson.JSON - *Config -} - type CPPSymbol struct { DemangleName string *nm.Symbol From 63f4c73ef083f08133de6198bea9626ef42121d9 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Tue, 30 Jul 2024 15:27:03 +0800 Subject: [PATCH 112/152] c/clang/symg:remove example config file --- chore/_xtool/llcppsymg/llcppg.cfg | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 chore/_xtool/llcppsymg/llcppg.cfg diff --git a/chore/_xtool/llcppsymg/llcppg.cfg b/chore/_xtool/llcppsymg/llcppg.cfg deleted file mode 100644 index 163284b3..00000000 --- a/chore/_xtool/llcppsymg/llcppg.cfg +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "inih", - "cflags": "$(pkg-config --cflags INIReader)", - "include": [ - "INIReader.h" - ], - "libs": "$(pkg-config --libs INIReader)", - "trimPrefixes": ["Ini", "INI"] -} \ No newline at end of file From aae7af21947a7b3bf3d726baeb9c56a9b93e363e Mon Sep 17 00:00:00 2001 From: xushiwei Date: Tue, 30 Jul 2024 18:26:33 +0800 Subject: [PATCH 113/152] library: hash, hash/crc64 --- README.md | 2 ++ _cmptest/crcdemo/crc.go | 11 +++++++++++ ssa/expr.go | 2 +- 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 _cmptest/crcdemo/crc.go diff --git a/README.md b/README.md index b52b82d6..9ea97453 100644 --- a/README.md +++ b/README.md @@ -297,6 +297,8 @@ Here are the Go packages that can be imported correctly: * [encoding/base32](https://pkg.go.dev/encoding/base32) * [encoding/base64](https://pkg.go.dev/encoding/base64) * [encoding/csv](https://pkg.go.dev/encoding/csv) +* [hash](https://pkg.go.dev/hash) +* [hash/crc64](https://pkg.go.dev/hash/crc64) * [crypto/md5](https://pkg.go.dev/crypto/md5) * [regexp](https://pkg.go.dev/regexp) * [regexp/syntax](https://pkg.go.dev/regexp/syntax) diff --git a/_cmptest/crcdemo/crc.go b/_cmptest/crcdemo/crc.go new file mode 100644 index 00000000..872c4b23 --- /dev/null +++ b/_cmptest/crcdemo/crc.go @@ -0,0 +1,11 @@ +package main + +import ( + "fmt" + "hash/crc64" +) + +func main() { + crc := crc64.MakeTable(crc64.ECMA) + fmt.Printf("%016x\n", crc64.Checksum([]byte("Hello world"), crc)) +} diff --git a/ssa/expr.go b/ssa/expr.go index 263ebb7a..d1b158dd 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -562,7 +562,7 @@ func (b Builder) BinOp(op token.Token, x, y Expr) Expr { return Expr{llvm.CreateICmp(b.impl, pred, x.impl, y.impl), tret} } case vkArray: - typ := x.raw.Type.(*types.Array) + typ := x.raw.Type.Underlying().(*types.Array) elem := b.Prog.Elem(x.Type) ret := prog.BoolVal(true) for i, n := 0, int(typ.Len()); i < n; i++ { From 9ae4b930830813fed6004414cb07bdce38b73e33 Mon Sep 17 00:00:00 2001 From: tsingbx Date: Tue, 30 Jul 2024 18:32:27 +0800 Subject: [PATCH 114/152] add openssl sha1,sha256,sha512 --- _demo/cshademo/sha.go | 29 ++++++++++++ c/openssl/sha.go | 31 +++++++++++++ c/openssl/sha1.go | 63 ++++++++++++++++++++++++++ c/openssl/sha256.go | 103 ++++++++++++++++++++++++++++++++++++++++++ c/openssl/sha512.go | 101 +++++++++++++++++++++++++++++++++++++++++ ssa/expr.go | 10 ++++ 6 files changed, 337 insertions(+) create mode 100644 _demo/cshademo/sha.go create mode 100644 c/openssl/sha.go create mode 100644 c/openssl/sha1.go create mode 100644 c/openssl/sha256.go create mode 100644 c/openssl/sha512.go diff --git a/_demo/cshademo/sha.go b/_demo/cshademo/sha.go new file mode 100644 index 00000000..ad58d8f8 --- /dev/null +++ b/_demo/cshademo/sha.go @@ -0,0 +1,29 @@ +package main + +import ( + "fmt" + "unsafe" + + "github.com/goplus/llgo/c/openssl" +) + +func main() { + + var sha1 openssl.SHA_CTX + sha1.Init() + sha1.UpdateString("His money is twice tainted:") + sha1.UpdateString(" 'taint yours and 'taint mine.") + + h := make([]byte, openssl.SHA_DIGEST_LENGTH) + sha1.Final(unsafe.SliceData(h)) + fmt.Printf("%x\n", h) + + var sha256 openssl.SHA256_CTX + sha256.Init() + sha256.UpdateString("His money is twice tainted:") + sha256.UpdateString(" 'taint yours and 'taint mine.") + + hh := make([]byte, openssl.SHA256_DIGEST_LENGTH) + sha256.Final(unsafe.SliceData(hh)) + fmt.Printf("%x\n", hh) +} diff --git a/c/openssl/sha.go b/c/openssl/sha.go new file mode 100644 index 00000000..3cf7d891 --- /dev/null +++ b/c/openssl/sha.go @@ -0,0 +1,31 @@ +package openssl + +import "github.com/goplus/llgo/c" + +const ( + SHA_DIGEST_LENGTH = 20 + SHA_LBLOCK = 16 + SHA_CBLOCK = (SHA_LBLOCK * 4) + SHA_LAST_BLOCK = (SHA_CBLOCK - 8) + + SHA256_CBLOCK = (SHA_LBLOCK * 4) + SHA256_192_DIGEST_LENGTH = 24 + SHA224_DIGEST_LENGTH = 28 + SHA256_DIGEST_LENGTH = 32 + SHA384_DIGEST_LENGTH = 48 + SHA512_DIGEST_LENGTH = 64 + SHA512_CBLOCK = (SHA_LBLOCK * 8) +) + +/* +# if (defined(_WIN32) || defined(_WIN64)) && !defined(__MINGW32__) +# define SHA_LONG64 unsigned __int64 +# elif defined(__arch64__) +# define SHA_LONG64 unsigned long +# else +# define SHA_LONG64 unsigned long long +# endif +*/ +type SHA_LONG64 = c.UlongLong + +type SHA_LONG = c.Uint diff --git a/c/openssl/sha1.go b/c/openssl/sha1.go new file mode 100644 index 00000000..6ca7f6b2 --- /dev/null +++ b/c/openssl/sha1.go @@ -0,0 +1,63 @@ +package openssl + +import ( + "unsafe" + + "github.com/goplus/llgo/c" +) + +type SHA_CTX struct { + H0, H1, H2, H3, H4 SHA_LONG + Nl, Nh SHA_LONG + Data [SHA_LBLOCK]SHA_LONG + Num c.Uint +} + +// OSSL_DEPRECATEDIN_3_0 int SHA1_Init(SHA_CTX *c); +// +// llgo:link (*SHA_CTX).Init C.SHA1_Init +func (c *SHA_CTX) Init() c.Int { return 0 } + +// OSSL_DEPRECATEDIN_3_0 int SHA1_Update(SHA_CTX *c, const void *data, size_t len); +// +// llgo:link (*SHA_CTX).Update C.SHA1_Update +func (c *SHA_CTX) Update(data unsafe.Pointer, n uintptr) c.Int { return 0 } + +func (c *SHA_CTX) UpdateBytes(data []byte) c.Int { + return c.Update(unsafe.Pointer(unsafe.SliceData(data)), uintptr(len(data))) +} + +func (c *SHA_CTX) UpdateString(data string) c.Int { + return c.Update(unsafe.Pointer(unsafe.StringData(data)), uintptr(len(data))) +} + +// OSSL_DEPRECATEDIN_3_0 int SHA1_Final(unsigned char *md, SHA_CTX *c); +// +//go:linkname sha1Final C.SHA1_Final +func sha1Final(md *byte, c *SHA_CTX) c.Int + +func (c *SHA_CTX) Final(md *byte) c.Int { + return sha1Final(md, c) +} + +// OSSL_DEPRECATEDIN_3_0 void SHA1_Transform(SHA_CTX *c, const unsigned char *data); +// +//go:linkname sha1Transform C.SHA1_Transform +func sha1Transform(c *SHA_CTX, data *byte) + +func (c *SHA_CTX) Transform(data *byte) { + sha1Transform(c, data) +} + +// unsigned char *SHA1(const unsigned char *d, size_t n, unsigned char *md); +// +//go:linkname SHA1 C.SHA1 +func SHA1(data unsafe.Pointer, n uintptr, md *byte) *byte + +func SHA1Bytes(data []byte, md *byte) *byte { + return SHA1(unsafe.Pointer(unsafe.SliceData(data)), uintptr(len(data)), md) +} + +func SHA1String(data string, md *byte) *byte { + return SHA1(unsafe.Pointer(unsafe.StringData(data)), uintptr(len(data)), md) +} diff --git a/c/openssl/sha256.go b/c/openssl/sha256.go new file mode 100644 index 00000000..d18e3a2d --- /dev/null +++ b/c/openssl/sha256.go @@ -0,0 +1,103 @@ +package openssl + +import ( + "unsafe" + + "github.com/goplus/llgo/c" +) + +type SHA256_CTX struct { + H [8]SHA_LONG + Nl, Nh SHA_LONG + Data [SHA_LBLOCK]SHA_LONG + Num, MdLen c.Uint +} + +// OSSL_DEPRECATEDIN_3_0 int SHA224_Init(SHA256_CTX *c); +// +// llgo:link (*SHA256_CTX).Init224 C.SHA224_Init +func (c *SHA256_CTX) Init224() c.Int { return 0 } + +// OSSL_DEPRECATEDIN_3_0 int SHA224_Update(SHA256_CTX *c, const void *data, size_t len); +// +// llgo:link (*SHA256_CTX).Update224 C.SHA224_Update +func (c *SHA256_CTX) Update224(data unsafe.Pointer, n uintptr) c.Int { return 0 } + +func (c *SHA256_CTX) Update224Bytes(data []byte) c.Int { + return c.Update224(unsafe.Pointer(unsafe.SliceData(data)), uintptr(len(data))) +} + +func (c *SHA256_CTX) Update224String(data string) c.Int { + return c.Update224(unsafe.Pointer(unsafe.StringData(data)), uintptr(len(data))) +} + +// OSSL_DEPRECATEDIN_3_0 int SHA224_Final(unsigned char *md, SHA256_CTX *c); +// +//go:linkname sha224Final C.SHA224_Final +func sha224Final(md *byte, c *SHA256_CTX) c.Int + +func (c *SHA256_CTX) Final224(md *byte) c.Int { + return sha224Final(md, c) +} + +// OSSL_DEPRECATEDIN_3_0 int SHA256_Init(SHA256_CTX *c); +// +// llgo:link (*SHA256_CTX).Init C.SHA256_Init +func (c *SHA256_CTX) Init() c.Int { return 0 } + +// OSSL_DEPRECATEDIN_3_0 int SHA256_Update(SHA256_CTX *c, const void *data, size_t len); +// +// llgo:link (*SHA256_CTX).Update C.SHA256_Update +func (c *SHA256_CTX) Update(data unsafe.Pointer, n uintptr) c.Int { return 0 } + +func (c *SHA256_CTX) UpdateBytes(data []byte) c.Int { + return c.Update(unsafe.Pointer(unsafe.SliceData(data)), uintptr(len(data))) +} + +func (c *SHA256_CTX) UpdateString(data string) c.Int { + return c.Update(unsafe.Pointer(unsafe.StringData(data)), uintptr(len(data))) +} + +// OSSL_DEPRECATEDIN_3_0 int SHA256_Final(unsigned char *md, SHA256_CTX *c); +// +//go:linkname sha256Final C.SHA256_Final +func sha256Final(md *byte, c *SHA256_CTX) c.Int + +func (c *SHA256_CTX) Final(md *byte) c.Int { + return sha256Final(md, c) +} + +// OSSL_DEPRECATEDIN_3_0 void SHA256_Transform(SHA256_CTX *c, const unsigned char *data); +// +//go:linkname sha256Transform C.SHA256_Transform +func sha256Transform(c *SHA256_CTX, data *byte) + +func (c *SHA256_CTX) Transform(data *byte) { + sha256Transform(c, data) +} + +// unsigned char *SHA224(const unsigned char *d, size_t n, unsigned char *md); +// +//go:linkname SHA224 C.SHA224 +func SHA224(data unsafe.Pointer, n uintptr, md *byte) *byte + +func SHA224Bytes(data []byte, md *byte) *byte { + return SHA224(unsafe.Pointer(unsafe.SliceData(data)), uintptr(len(data)), md) +} + +func SHA224String(data string, md *byte) *byte { + return SHA224(unsafe.Pointer(unsafe.StringData(data)), uintptr(len(data)), md) +} + +// unsigned char *SHA256(const unsigned char *d, size_t n, unsigned char *md); +// +//go:linkname SHA256 C.SHA256 +func SHA256(data unsafe.Pointer, n uintptr, md *byte) *byte + +func SHA256Bytes(data []byte, md *byte) *byte { + return SHA256(unsafe.Pointer(unsafe.SliceData(data)), uintptr(len(data)), md) +} + +func SHA256String(data string, md *byte) *byte { + return SHA256(unsafe.Pointer(unsafe.StringData(data)), uintptr(len(data)), md) +} diff --git a/c/openssl/sha512.go b/c/openssl/sha512.go new file mode 100644 index 00000000..6add73b7 --- /dev/null +++ b/c/openssl/sha512.go @@ -0,0 +1,101 @@ +package openssl + +import ( + "unsafe" + + "github.com/goplus/llgo/c" +) + +type SHA512_CTX struct { + H [8]SHA_LONG64 + N1, Nh SHA_LONG64 + D [SHA_LBLOCK]SHA_LONG64 + Num, MdLen c.Uint +} + +// OSSL_DEPRECATEDIN_3_0 int SHA384_Init(SHA512_CTX *c); +// +// llgo:link (*SHA512_CTX).Init384 C.SHA384_Init +func (c *SHA512_CTX) Init384() c.Int { return 0 } + +// OSSL_DEPRECATEDIN_3_0 int SHA384_Update(SHA512_CTX *c, const void *data, size_t len); +// +// llgo:link (*SHA512_CTX).Update384 C.SHA384_Update +func (c *SHA512_CTX) Update384(data unsafe.Pointer, n uintptr) c.Int { return 0 } + +func (c *SHA512_CTX) Update384Bytes(data []byte) c.Int { + return c.Update384(unsafe.Pointer(unsafe.SliceData(data)), uintptr(len(data))) +} + +func (c *SHA512_CTX) Update384String(data string) c.Int { + return c.Update384(unsafe.Pointer(unsafe.StringData(data)), uintptr(len(data))) +} + +// OSSL_DEPRECATEDIN_3_0 int SHA384_Final(unsigned char *md, SHA512_CTX *c); +// +//go:linkname sha384Final C.SHA384_Final +func sha384Final(md *byte, c *SHA512_CTX) c.Int + +func (c *SHA512_CTX) Final384(md *byte) c.Int { + return sha384Final(md, c) +} + +// OSSL_DEPRECATEDIN_3_0 int SHA512_Init(SHA512_CTX *c); +// +// llgo:link (*SHA512_CTX).Init C.SHA512_Init +func (c *SHA512_CTX) Init() c.Int { return 0 } + +// OSSL_DEPRECATEDIN_3_0 int SHA512_Update(SHA512_CTX *c, const void *data, size_t len); +// +// llgo:link (*SHA512_CTX).Update C.SHA512_Update +func (c *SHA512_CTX) Update(data unsafe.Pointer, n uintptr) c.Int { return 0 } +func (c *SHA512_CTX) UpdateBytes(data []byte) c.Int { + return c.Update(unsafe.Pointer(unsafe.SliceData(data)), uintptr(len(data))) +} +func (c *SHA512_CTX) UpdateString(data string) c.Int { + return c.Update(unsafe.Pointer(unsafe.StringData(data)), uintptr(len(data))) +} + +// OSSL_DEPRECATEDIN_3_0 int SHA512_Final(unsigned char *md, SHA512_CTX *c); +// +//go:linkname sha512Final C.SHA512_Final +func sha512Final(md *byte, c *SHA512_CTX) c.Int + +func (c *SHA512_CTX) Final(md *byte) c.Int { + return sha512Final(md, c) +} + +// OSSL_DEPRECATEDIN_3_0 void SHA512_Transform(SHA512_CTX *c, const unsigned char *data); +// +//go:linkname sha512Transform C.SHA512_Transform +func sha512Transform(c *SHA512_CTX, data *byte) + +func (c *SHA512_CTX) Transform(data *byte) { + sha512Transform(c, data) +} + +// unsigned char *SHA384(const unsigned char *d, size_t n, unsigned char *md); +// +//go:linkname SHA384 C.SHA384 +func SHA384(data unsafe.Pointer, n uintptr, md *byte) *byte + +func SHA384Bytes(data []byte, md *byte) *byte { + return SHA384(unsafe.Pointer(unsafe.SliceData(data)), uintptr(len(data)), md) +} + +func SHA384String(data string, md *byte) *byte { + return SHA384(unsafe.Pointer(unsafe.StringData(data)), uintptr(len(data)), md) +} + +// unsigned char *SHA512(const unsigned char *d, size_t n, unsigned char *md); +// +//go:linkname SHA512 C.SHA512 +func SHA512(data unsafe.Pointer, n uintptr, md *byte) *byte + +func SHA512Bytes(data []byte, md *byte) *byte { + return SHA512(unsafe.Pointer(unsafe.SliceData(data)), uintptr(len(data)), md) +} + +func SHA512String(data string, md *byte) *byte { + return SHA512(unsafe.Pointer(unsafe.StringData(data)), uintptr(len(data)), md) +} diff --git a/ssa/expr.go b/ssa/expr.go index 263ebb7a..f9adbd7d 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -1022,6 +1022,10 @@ func (b Builder) Do(da DoAction, fn Expr, args ...Expr) (ret Expr) { // Go spec (excluding "make" and "new"). func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) { switch fn { + case "max": + fallthrough + case "min": + return b.max(args...) case "len": if len(args) == 1 { arg := args[0] @@ -1128,6 +1132,12 @@ func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) { panic("todo: " + fn) } +// max return the max value for the args +func (b Builder) max(args ...Expr) (ret Expr) { + //todo + return args[len(args)-1] +} + // Println prints the arguments to stderr, followed by a newline. func (b Builder) Println(args ...Expr) (ret Expr) { return b.PrintEx(true, args...) From 3212f059ff938758bbacbbb8eb6c38c743b51451 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Tue, 30 Jul 2024 18:53:55 +0800 Subject: [PATCH 115/152] c/zlib: crc32/adler32 --- c/zlib/zlib.go | 152 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) diff --git a/c/zlib/zlib.go b/c/zlib/zlib.go index 1cd6111d..1cd65e94 100644 --- a/c/zlib/zlib.go +++ b/c/zlib/zlib.go @@ -76,17 +76,169 @@ const ( DEFLATED = 8 ) +// ----------------------------------------------------------------------------- + +/* +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); + +compressBound() returns an upper bound on the compressed size after +compress() or compress2() on sourceLen bytes. It would be used before a +compress() or compress2() call to allocate the destination buffer. +*/ //go:linkname CompressBound C.compressBound func CompressBound(sourceLen c.Ulong) c.Ulong +/* +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); + +Compresses the source buffer into the destination buffer. sourceLen is +the byte length of the source buffer. Upon entry, destLen is the total size +of the destination buffer, which must be at least the value returned by +compressBound(sourceLen). Upon exit, destLen is the actual size of the +compressed data. compress() is equivalent to compress2() with a level +parameter of Z_DEFAULT_COMPRESSION. + +compress returns Z_OK if success, Z_MEM_ERROR if there was not +enough memory, Z_BUF_ERROR if there was not enough room in the output +buffer. +*/ //go:linkname Compress C.compress func Compress(dest *byte, destLen *c.Ulong, source *byte, sourceLen c.Ulong) c.Int +/* +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, int level)); + +Compresses the source buffer into the destination buffer. The level +parameter has the same meaning as in deflateInit. sourceLen is the byte +length of the source buffer. Upon entry, destLen is the total size of the +destination buffer, which must be at least the value returned by +compressBound(sourceLen). Upon exit, destLen is the actual size of the +compressed data. + +compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough +memory, Z_BUF_ERROR if there was not enough room in the output buffer, +Z_STREAM_ERROR if the level parameter is invalid. +*/ //go:linkname Compress2 C.compress2 func Compress2(dest *byte, destLen *c.Ulong, source *byte, sourceLen c.Ulong, level c.Int) c.Int +/* +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); + +Decompresses the source buffer into the destination buffer. sourceLen is +the byte length of the source buffer. Upon entry, destLen is the total size +of the destination buffer, which must be large enough to hold the entire +uncompressed data. (The size of the uncompressed data must have been saved +previously by the compressor and transmitted to the decompressor by some +mechanism outside the scope of this compression library.) Upon exit, destLen +is the actual size of the uncompressed data. + +uncompress returns Z_OK if success, Z_MEM_ERROR if there was not +enough memory, Z_BUF_ERROR if there was not enough room in the output +buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In +the case where there is not enough room, uncompress() will fill the output +buffer with the uncompressed data up to that point. +*/ //go:linkname Uncompress C.uncompress func Uncompress(dest *byte, destLen *c.Ulong, source *byte, sourceLen c.Ulong) c.Int +/* +ZEXTERN int ZEXPORT uncompress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong *sourceLen)); + +Same as uncompress, except that sourceLen is a pointer, where the +length of the source is *sourceLen. On return, *sourceLen is the number of +source bytes consumed. +*/ //go:linkname Uncompress2 C.uncompress2 func Uncompress2(dest *byte, destLen *c.Ulong, source *byte, sourceLen *c.Ulong) c.Int + +// ----------------------------------------------------------------------------- + +/* +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); + +Update a running CRC-32 with the bytes buf[0..len-1] and return the +updated CRC-32. If buf is Z_NULL, this function returns the required +initial value for the crc. Pre- and post-conditioning (one's complement) is +performed within this function so it shouldn't be done by the application. + +Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ +//go:linkname Crc32 C.crc32 +func Crc32(crc c.Ulong, buf *byte, len c.Uint) c.Ulong + +/* +ZEXTERN uLong ZEXPORT crc32_z OF((uLong adler, const Bytef *buf, z_size_t len)); + +Same as crc32(), but with a size_t length. +*/ +//go:linkname Crc32Z C.crc32_z +func Crc32Z(crc c.Ulong, buf *byte, len uintptr) c.Ulong + +/* +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + +Combine two CRC-32 check values into one. For two sequences of bytes, +seq1 and seq2 with lengths len1 and len2, CRC-32 check values were +calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 +check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and +len2. +*/ +//go:linkname Crc32Combine C.crc32_combine +func Crc32Combine(crc1 c.Ulong, crc2 c.Ulong, len2 int64) c.Ulong + +/* +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); + +Update a running Adler-32 checksum with the bytes buf[0..len-1] and +return the updated checksum. If buf is Z_NULL, this function returns the +required initial value for the checksum. + +An Adler-32 checksum is almost as reliable as a CRC-32 but can be computed +much faster. + +Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ +//go:linkname Adler32 C.adler32 +func Adler32(adler c.Ulong, buf *byte, len c.Uint) c.Ulong + +/* +ZEXTERN uLong ZEXPORT adler32_z OF((uLong adler, const Bytef *buf, z_size_t len)); + +Same as adler32(), but with a size_t length. +*/ +//go:linkname Adler32Z C.adler32_z +func Adler32Z(adler c.Ulong, buf *byte, len uintptr) c.Ulong + +/* +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, z_off_t len2)); + +Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 +and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for +each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of +seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note +that the z_off_t type (like off_t) is a signed integer. If len2 is +negative, the result has no meaning or utility. +*/ +//go:linkname Adler32Combine C.adler32_combine +func Adler32Combine(adler1 c.Ulong, adler2 c.Ulong, len2 int64) c.Ulong + +// ----------------------------------------------------------------------------- From cd32d6debec35850bc4a18be23814461a0907515 Mon Sep 17 00:00:00 2001 From: tsingbx Date: Tue, 30 Jul 2024 18:32:27 +0800 Subject: [PATCH 116/152] add openssl sha1,sha256,sha512 --- _demo/cshademo/sha.go | 29 ++++++++++++ c/openssl/sha.go | 31 +++++++++++++ c/openssl/sha1.go | 63 ++++++++++++++++++++++++++ c/openssl/sha256.go | 103 ++++++++++++++++++++++++++++++++++++++++++ c/openssl/sha512.go | 101 +++++++++++++++++++++++++++++++++++++++++ ssa/expr.go | 10 ++++ 6 files changed, 337 insertions(+) create mode 100644 _demo/cshademo/sha.go create mode 100644 c/openssl/sha.go create mode 100644 c/openssl/sha1.go create mode 100644 c/openssl/sha256.go create mode 100644 c/openssl/sha512.go diff --git a/_demo/cshademo/sha.go b/_demo/cshademo/sha.go new file mode 100644 index 00000000..ad58d8f8 --- /dev/null +++ b/_demo/cshademo/sha.go @@ -0,0 +1,29 @@ +package main + +import ( + "fmt" + "unsafe" + + "github.com/goplus/llgo/c/openssl" +) + +func main() { + + var sha1 openssl.SHA_CTX + sha1.Init() + sha1.UpdateString("His money is twice tainted:") + sha1.UpdateString(" 'taint yours and 'taint mine.") + + h := make([]byte, openssl.SHA_DIGEST_LENGTH) + sha1.Final(unsafe.SliceData(h)) + fmt.Printf("%x\n", h) + + var sha256 openssl.SHA256_CTX + sha256.Init() + sha256.UpdateString("His money is twice tainted:") + sha256.UpdateString(" 'taint yours and 'taint mine.") + + hh := make([]byte, openssl.SHA256_DIGEST_LENGTH) + sha256.Final(unsafe.SliceData(hh)) + fmt.Printf("%x\n", hh) +} diff --git a/c/openssl/sha.go b/c/openssl/sha.go new file mode 100644 index 00000000..3cf7d891 --- /dev/null +++ b/c/openssl/sha.go @@ -0,0 +1,31 @@ +package openssl + +import "github.com/goplus/llgo/c" + +const ( + SHA_DIGEST_LENGTH = 20 + SHA_LBLOCK = 16 + SHA_CBLOCK = (SHA_LBLOCK * 4) + SHA_LAST_BLOCK = (SHA_CBLOCK - 8) + + SHA256_CBLOCK = (SHA_LBLOCK * 4) + SHA256_192_DIGEST_LENGTH = 24 + SHA224_DIGEST_LENGTH = 28 + SHA256_DIGEST_LENGTH = 32 + SHA384_DIGEST_LENGTH = 48 + SHA512_DIGEST_LENGTH = 64 + SHA512_CBLOCK = (SHA_LBLOCK * 8) +) + +/* +# if (defined(_WIN32) || defined(_WIN64)) && !defined(__MINGW32__) +# define SHA_LONG64 unsigned __int64 +# elif defined(__arch64__) +# define SHA_LONG64 unsigned long +# else +# define SHA_LONG64 unsigned long long +# endif +*/ +type SHA_LONG64 = c.UlongLong + +type SHA_LONG = c.Uint diff --git a/c/openssl/sha1.go b/c/openssl/sha1.go new file mode 100644 index 00000000..6ca7f6b2 --- /dev/null +++ b/c/openssl/sha1.go @@ -0,0 +1,63 @@ +package openssl + +import ( + "unsafe" + + "github.com/goplus/llgo/c" +) + +type SHA_CTX struct { + H0, H1, H2, H3, H4 SHA_LONG + Nl, Nh SHA_LONG + Data [SHA_LBLOCK]SHA_LONG + Num c.Uint +} + +// OSSL_DEPRECATEDIN_3_0 int SHA1_Init(SHA_CTX *c); +// +// llgo:link (*SHA_CTX).Init C.SHA1_Init +func (c *SHA_CTX) Init() c.Int { return 0 } + +// OSSL_DEPRECATEDIN_3_0 int SHA1_Update(SHA_CTX *c, const void *data, size_t len); +// +// llgo:link (*SHA_CTX).Update C.SHA1_Update +func (c *SHA_CTX) Update(data unsafe.Pointer, n uintptr) c.Int { return 0 } + +func (c *SHA_CTX) UpdateBytes(data []byte) c.Int { + return c.Update(unsafe.Pointer(unsafe.SliceData(data)), uintptr(len(data))) +} + +func (c *SHA_CTX) UpdateString(data string) c.Int { + return c.Update(unsafe.Pointer(unsafe.StringData(data)), uintptr(len(data))) +} + +// OSSL_DEPRECATEDIN_3_0 int SHA1_Final(unsigned char *md, SHA_CTX *c); +// +//go:linkname sha1Final C.SHA1_Final +func sha1Final(md *byte, c *SHA_CTX) c.Int + +func (c *SHA_CTX) Final(md *byte) c.Int { + return sha1Final(md, c) +} + +// OSSL_DEPRECATEDIN_3_0 void SHA1_Transform(SHA_CTX *c, const unsigned char *data); +// +//go:linkname sha1Transform C.SHA1_Transform +func sha1Transform(c *SHA_CTX, data *byte) + +func (c *SHA_CTX) Transform(data *byte) { + sha1Transform(c, data) +} + +// unsigned char *SHA1(const unsigned char *d, size_t n, unsigned char *md); +// +//go:linkname SHA1 C.SHA1 +func SHA1(data unsafe.Pointer, n uintptr, md *byte) *byte + +func SHA1Bytes(data []byte, md *byte) *byte { + return SHA1(unsafe.Pointer(unsafe.SliceData(data)), uintptr(len(data)), md) +} + +func SHA1String(data string, md *byte) *byte { + return SHA1(unsafe.Pointer(unsafe.StringData(data)), uintptr(len(data)), md) +} diff --git a/c/openssl/sha256.go b/c/openssl/sha256.go new file mode 100644 index 00000000..d18e3a2d --- /dev/null +++ b/c/openssl/sha256.go @@ -0,0 +1,103 @@ +package openssl + +import ( + "unsafe" + + "github.com/goplus/llgo/c" +) + +type SHA256_CTX struct { + H [8]SHA_LONG + Nl, Nh SHA_LONG + Data [SHA_LBLOCK]SHA_LONG + Num, MdLen c.Uint +} + +// OSSL_DEPRECATEDIN_3_0 int SHA224_Init(SHA256_CTX *c); +// +// llgo:link (*SHA256_CTX).Init224 C.SHA224_Init +func (c *SHA256_CTX) Init224() c.Int { return 0 } + +// OSSL_DEPRECATEDIN_3_0 int SHA224_Update(SHA256_CTX *c, const void *data, size_t len); +// +// llgo:link (*SHA256_CTX).Update224 C.SHA224_Update +func (c *SHA256_CTX) Update224(data unsafe.Pointer, n uintptr) c.Int { return 0 } + +func (c *SHA256_CTX) Update224Bytes(data []byte) c.Int { + return c.Update224(unsafe.Pointer(unsafe.SliceData(data)), uintptr(len(data))) +} + +func (c *SHA256_CTX) Update224String(data string) c.Int { + return c.Update224(unsafe.Pointer(unsafe.StringData(data)), uintptr(len(data))) +} + +// OSSL_DEPRECATEDIN_3_0 int SHA224_Final(unsigned char *md, SHA256_CTX *c); +// +//go:linkname sha224Final C.SHA224_Final +func sha224Final(md *byte, c *SHA256_CTX) c.Int + +func (c *SHA256_CTX) Final224(md *byte) c.Int { + return sha224Final(md, c) +} + +// OSSL_DEPRECATEDIN_3_0 int SHA256_Init(SHA256_CTX *c); +// +// llgo:link (*SHA256_CTX).Init C.SHA256_Init +func (c *SHA256_CTX) Init() c.Int { return 0 } + +// OSSL_DEPRECATEDIN_3_0 int SHA256_Update(SHA256_CTX *c, const void *data, size_t len); +// +// llgo:link (*SHA256_CTX).Update C.SHA256_Update +func (c *SHA256_CTX) Update(data unsafe.Pointer, n uintptr) c.Int { return 0 } + +func (c *SHA256_CTX) UpdateBytes(data []byte) c.Int { + return c.Update(unsafe.Pointer(unsafe.SliceData(data)), uintptr(len(data))) +} + +func (c *SHA256_CTX) UpdateString(data string) c.Int { + return c.Update(unsafe.Pointer(unsafe.StringData(data)), uintptr(len(data))) +} + +// OSSL_DEPRECATEDIN_3_0 int SHA256_Final(unsigned char *md, SHA256_CTX *c); +// +//go:linkname sha256Final C.SHA256_Final +func sha256Final(md *byte, c *SHA256_CTX) c.Int + +func (c *SHA256_CTX) Final(md *byte) c.Int { + return sha256Final(md, c) +} + +// OSSL_DEPRECATEDIN_3_0 void SHA256_Transform(SHA256_CTX *c, const unsigned char *data); +// +//go:linkname sha256Transform C.SHA256_Transform +func sha256Transform(c *SHA256_CTX, data *byte) + +func (c *SHA256_CTX) Transform(data *byte) { + sha256Transform(c, data) +} + +// unsigned char *SHA224(const unsigned char *d, size_t n, unsigned char *md); +// +//go:linkname SHA224 C.SHA224 +func SHA224(data unsafe.Pointer, n uintptr, md *byte) *byte + +func SHA224Bytes(data []byte, md *byte) *byte { + return SHA224(unsafe.Pointer(unsafe.SliceData(data)), uintptr(len(data)), md) +} + +func SHA224String(data string, md *byte) *byte { + return SHA224(unsafe.Pointer(unsafe.StringData(data)), uintptr(len(data)), md) +} + +// unsigned char *SHA256(const unsigned char *d, size_t n, unsigned char *md); +// +//go:linkname SHA256 C.SHA256 +func SHA256(data unsafe.Pointer, n uintptr, md *byte) *byte + +func SHA256Bytes(data []byte, md *byte) *byte { + return SHA256(unsafe.Pointer(unsafe.SliceData(data)), uintptr(len(data)), md) +} + +func SHA256String(data string, md *byte) *byte { + return SHA256(unsafe.Pointer(unsafe.StringData(data)), uintptr(len(data)), md) +} diff --git a/c/openssl/sha512.go b/c/openssl/sha512.go new file mode 100644 index 00000000..6add73b7 --- /dev/null +++ b/c/openssl/sha512.go @@ -0,0 +1,101 @@ +package openssl + +import ( + "unsafe" + + "github.com/goplus/llgo/c" +) + +type SHA512_CTX struct { + H [8]SHA_LONG64 + N1, Nh SHA_LONG64 + D [SHA_LBLOCK]SHA_LONG64 + Num, MdLen c.Uint +} + +// OSSL_DEPRECATEDIN_3_0 int SHA384_Init(SHA512_CTX *c); +// +// llgo:link (*SHA512_CTX).Init384 C.SHA384_Init +func (c *SHA512_CTX) Init384() c.Int { return 0 } + +// OSSL_DEPRECATEDIN_3_0 int SHA384_Update(SHA512_CTX *c, const void *data, size_t len); +// +// llgo:link (*SHA512_CTX).Update384 C.SHA384_Update +func (c *SHA512_CTX) Update384(data unsafe.Pointer, n uintptr) c.Int { return 0 } + +func (c *SHA512_CTX) Update384Bytes(data []byte) c.Int { + return c.Update384(unsafe.Pointer(unsafe.SliceData(data)), uintptr(len(data))) +} + +func (c *SHA512_CTX) Update384String(data string) c.Int { + return c.Update384(unsafe.Pointer(unsafe.StringData(data)), uintptr(len(data))) +} + +// OSSL_DEPRECATEDIN_3_0 int SHA384_Final(unsigned char *md, SHA512_CTX *c); +// +//go:linkname sha384Final C.SHA384_Final +func sha384Final(md *byte, c *SHA512_CTX) c.Int + +func (c *SHA512_CTX) Final384(md *byte) c.Int { + return sha384Final(md, c) +} + +// OSSL_DEPRECATEDIN_3_0 int SHA512_Init(SHA512_CTX *c); +// +// llgo:link (*SHA512_CTX).Init C.SHA512_Init +func (c *SHA512_CTX) Init() c.Int { return 0 } + +// OSSL_DEPRECATEDIN_3_0 int SHA512_Update(SHA512_CTX *c, const void *data, size_t len); +// +// llgo:link (*SHA512_CTX).Update C.SHA512_Update +func (c *SHA512_CTX) Update(data unsafe.Pointer, n uintptr) c.Int { return 0 } +func (c *SHA512_CTX) UpdateBytes(data []byte) c.Int { + return c.Update(unsafe.Pointer(unsafe.SliceData(data)), uintptr(len(data))) +} +func (c *SHA512_CTX) UpdateString(data string) c.Int { + return c.Update(unsafe.Pointer(unsafe.StringData(data)), uintptr(len(data))) +} + +// OSSL_DEPRECATEDIN_3_0 int SHA512_Final(unsigned char *md, SHA512_CTX *c); +// +//go:linkname sha512Final C.SHA512_Final +func sha512Final(md *byte, c *SHA512_CTX) c.Int + +func (c *SHA512_CTX) Final(md *byte) c.Int { + return sha512Final(md, c) +} + +// OSSL_DEPRECATEDIN_3_0 void SHA512_Transform(SHA512_CTX *c, const unsigned char *data); +// +//go:linkname sha512Transform C.SHA512_Transform +func sha512Transform(c *SHA512_CTX, data *byte) + +func (c *SHA512_CTX) Transform(data *byte) { + sha512Transform(c, data) +} + +// unsigned char *SHA384(const unsigned char *d, size_t n, unsigned char *md); +// +//go:linkname SHA384 C.SHA384 +func SHA384(data unsafe.Pointer, n uintptr, md *byte) *byte + +func SHA384Bytes(data []byte, md *byte) *byte { + return SHA384(unsafe.Pointer(unsafe.SliceData(data)), uintptr(len(data)), md) +} + +func SHA384String(data string, md *byte) *byte { + return SHA384(unsafe.Pointer(unsafe.StringData(data)), uintptr(len(data)), md) +} + +// unsigned char *SHA512(const unsigned char *d, size_t n, unsigned char *md); +// +//go:linkname SHA512 C.SHA512 +func SHA512(data unsafe.Pointer, n uintptr, md *byte) *byte + +func SHA512Bytes(data []byte, md *byte) *byte { + return SHA512(unsafe.Pointer(unsafe.SliceData(data)), uintptr(len(data)), md) +} + +func SHA512String(data string, md *byte) *byte { + return SHA512(unsafe.Pointer(unsafe.StringData(data)), uintptr(len(data)), md) +} diff --git a/ssa/expr.go b/ssa/expr.go index 263ebb7a..f9adbd7d 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -1022,6 +1022,10 @@ func (b Builder) Do(da DoAction, fn Expr, args ...Expr) (ret Expr) { // Go spec (excluding "make" and "new"). func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) { switch fn { + case "max": + fallthrough + case "min": + return b.max(args...) case "len": if len(args) == 1 { arg := args[0] @@ -1128,6 +1132,12 @@ func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) { panic("todo: " + fn) } +// max return the max value for the args +func (b Builder) max(args ...Expr) (ret Expr) { + //todo + return args[len(args)-1] +} + // Println prints the arguments to stderr, followed by a newline. func (b Builder) Println(args ...Expr) (ret Expr) { return b.PrintEx(true, args...) From 1eaf124d4e48251d041cdb16618a01bfb887a729 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Tue, 30 Jul 2024 19:36:36 +0800 Subject: [PATCH 117/152] library: hash/{crc32, adler32} --- README.md | 2 + _cmptest/crcdemo/crc.go | 19 +++++- c/zlib/_demo/crc32demo/crc.go | 11 +++ c/zlib/zlib.go | 18 ++++- internal/build/build.go | 1 + internal/lib/hash/crc32/crc32.go | 112 +++++++++++++++++++++++++++++++ 6 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 c/zlib/_demo/crc32demo/crc.go create mode 100644 internal/lib/hash/crc32/crc32.go diff --git a/README.md b/README.md index 9ea97453..86793dcf 100644 --- a/README.md +++ b/README.md @@ -298,6 +298,8 @@ Here are the Go packages that can be imported correctly: * [encoding/base64](https://pkg.go.dev/encoding/base64) * [encoding/csv](https://pkg.go.dev/encoding/csv) * [hash](https://pkg.go.dev/hash) +* [hash/adler32](https://pkg.go.dev/hash/adler32) +* [hash/crc32](https://pkg.go.dev/hash/crc32) (partially) * [hash/crc64](https://pkg.go.dev/hash/crc64) * [crypto/md5](https://pkg.go.dev/crypto/md5) * [regexp](https://pkg.go.dev/regexp) diff --git a/_cmptest/crcdemo/crc.go b/_cmptest/crcdemo/crc.go index 872c4b23..b2d6eaae 100644 --- a/_cmptest/crcdemo/crc.go +++ b/_cmptest/crcdemo/crc.go @@ -2,10 +2,27 @@ package main import ( "fmt" + "hash/adler32" + "hash/crc32" "hash/crc64" ) -func main() { +func crc64Demo() { crc := crc64.MakeTable(crc64.ECMA) fmt.Printf("%016x\n", crc64.Checksum([]byte("Hello world"), crc)) } + +func crc32Demo() { + crc32q := crc32.MakeTable(crc32.IEEE) + fmt.Printf("%08x\n", crc32.Checksum([]byte("Hello world"), crc32q)) +} + +func adler32Demo() { + fmt.Printf("%08x\n", adler32.Checksum([]byte("Hello world"))) +} + +func main() { + adler32Demo() + crc32Demo() + crc64Demo() +} diff --git a/c/zlib/_demo/crc32demo/crc.go b/c/zlib/_demo/crc32demo/crc.go new file mode 100644 index 00000000..0d0c6843 --- /dev/null +++ b/c/zlib/_demo/crc32demo/crc.go @@ -0,0 +1,11 @@ +package main + +import ( + "fmt" + + "github.com/goplus/llgo/c/zlib" +) + +func main() { + fmt.Printf("%08x\n", zlib.Crc32ZString(0, "Hello world")) +} diff --git a/c/zlib/zlib.go b/c/zlib/zlib.go index 1cd65e94..0aa1aaad 100644 --- a/c/zlib/zlib.go +++ b/c/zlib/zlib.go @@ -17,7 +17,7 @@ package zlib import ( - _ "unsafe" + "unsafe" "github.com/goplus/llgo/c" ) @@ -186,6 +186,14 @@ Same as crc32(), but with a size_t length. //go:linkname Crc32Z C.crc32_z func Crc32Z(crc c.Ulong, buf *byte, len uintptr) c.Ulong +func Crc32ZBytes(crc c.Ulong, buf []byte) c.Ulong { + return Crc32Z(crc, unsafe.SliceData(buf), uintptr(len(buf))) +} + +func Crc32ZString(crc c.Ulong, buf string) c.Ulong { + return Crc32Z(crc, unsafe.StringData(buf), uintptr(len(buf))) +} + /* ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); @@ -228,6 +236,14 @@ Same as adler32(), but with a size_t length. //go:linkname Adler32Z C.adler32_z func Adler32Z(adler c.Ulong, buf *byte, len uintptr) c.Ulong +func Adler32ZBytes(adler c.Ulong, buf []byte) c.Ulong { + return Adler32Z(adler, unsafe.SliceData(buf), uintptr(len(buf))) +} + +func Adler32ZString(adler c.Ulong, buf string) c.Ulong { + return Adler32Z(adler, unsafe.StringData(buf), uintptr(len(buf))) +} + /* ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, z_off_t len2)); diff --git a/internal/build/build.go b/internal/build/build.go index 0d9d546d..8b1485d5 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -759,6 +759,7 @@ type none struct{} var hasAltPkg = map[string]none{ "crypto/md5": {}, "fmt": {}, + "hash/crc32": {}, "internal/abi": {}, "internal/bytealg": {}, "internal/itoa": {}, diff --git a/internal/lib/hash/crc32/crc32.go b/internal/lib/hash/crc32/crc32.go new file mode 100644 index 00000000..9acfcc2c --- /dev/null +++ b/internal/lib/hash/crc32/crc32.go @@ -0,0 +1,112 @@ +/* + * 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 crc32 + +// llgo:skipall +import ( + "hash" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/zlib" +) + +// The size of a CRC-32 checksum in bytes. +const Size = 4 + +// Predefined polynomials. +const ( + // IEEE is by far and away the most common CRC-32 polynomial. + // Used by ethernet (IEEE 802.3), v.42, fddi, gzip, zip, png, ... + IEEE = 0xedb88320 + + // Castagnoli's polynomial, used in iSCSI. + // Has better error detection characteristics than IEEE. + // https://dx.doi.org/10.1109/26.231911 + Castagnoli = 0x82f63b78 + + // Koopman's polynomial. + // Also has better error detection characteristics than IEEE. + // https://dx.doi.org/10.1109/DSN.2002.1028931 + Koopman = 0xeb31d82e +) + +// Table is a 256-word table representing the polynomial for efficient processing. +type Table [256]uint32 + +// IEEETable is the table for the IEEE polynomial. +var IEEETable *Table = new(Table) + +// MakeTable returns a Table constructed from the specified polynomial. +// The contents of this Table must not be modified. +func MakeTable(poly uint32) *Table { + if poly == IEEE { + return IEEETable + } + panic("todo: hash/crc32.MakeTable") +} + +type digest uint32 + +func (d *digest) Size() int { return Size } + +func (d *digest) BlockSize() int { return 1 } + +func (d *digest) Reset() { *d = 0 } + +func (d *digest) Write(p []byte) (n int, err error) { + *d = digest(zlib.Crc32ZBytes(c.Ulong(*d), p)) + return len(p), nil +} + +func (d *digest) Sum32() uint32 { return uint32(*d) } + +func (d *digest) Sum(in []byte) []byte { + s := d.Sum32() + return append(in, byte(s>>24), byte(s>>16), byte(s>>8), byte(s)) +} + +func New(tab *Table) hash.Hash32 { + if tab == IEEETable { + return new(digest) + } + panic("todo: hash/crc32.New") +} + +// NewIEEE creates a new hash.Hash32 computing the CRC-32 checksum using +// the IEEE polynomial. Its Sum method will lay the value out in +// big-endian byte order. The returned Hash32 also implements +// encoding.BinaryMarshaler and encoding.BinaryUnmarshaler to marshal +// and unmarshal the internal state of the hash. +func NewIEEE() hash.Hash32 { return New(IEEETable) } + +// Update returns the result of adding the bytes in p to the crc. +func Update(crc uint32, tab *Table, p []byte) uint32 { + if tab == IEEETable { + return uint32(zlib.Crc32ZBytes(c.Ulong(crc), p)) + } + panic("todo: hash/crc32.Update") +} + +// Checksum returns the CRC-32 checksum of data +// using the polynomial represented by the Table. +func Checksum(data []byte, tab *Table) uint32 { return Update(0, tab, data) } + +// ChecksumIEEE returns the CRC-32 checksum of data +// using the IEEE polynomial. +func ChecksumIEEE(data []byte) uint32 { + return uint32(zlib.Crc32ZBytes(0, data)) +} From 9afe26f1d62b05d0676bc5d55dc9ec5bdd8ea227 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Tue, 30 Jul 2024 20:50:22 +0800 Subject: [PATCH 118/152] c/clang/symg:keep user edit symbol --- chore/_xtool/llcppsymg/llcppsymg.go | 53 +++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/chore/_xtool/llcppsymg/llcppsymg.go b/chore/_xtool/llcppsymg/llcppsymg.go index 6edde02f..93c2194d 100644 --- a/chore/_xtool/llcppsymg/llcppsymg.go +++ b/chore/_xtool/llcppsymg/llcppsymg.go @@ -228,6 +228,23 @@ func removePrefix(str string, prefixes []string) string { } func genSymbolTableFile(symbolInfos []types.SymbolInfo) error { + // keep open follow code block can run successfully + for i := range symbolInfos { + println("symbol", symbolInfos[i].Go) + } + + fileName := "llcppg.symb.json" + existingSymbols, err := readExistingSymbolTable(fileName) + if err != nil { + return err + } + + for i := range symbolInfos { + if existingSymbol, exists := existingSymbols[symbolInfos[i].Mangle]; exists { + symbolInfos[i].Go = existingSymbol.Go + } + } + root := cjson.Array() defer root.Delete() @@ -239,7 +256,6 @@ func genSymbolTableFile(symbolInfos []types.SymbolInfo) error { root.AddItem(item) } - fileName := "llcppg.symb.json" cStr := root.Print() if cStr == nil { return errors.New("symbol table is empty") @@ -251,6 +267,39 @@ func genSymbolTableFile(symbolInfos []types.SymbolInfo) error { if err := os.WriteFile(fileName, data, 0644); err != nil { return errors.New("failed to write symbol table file") } - return nil } +func readExistingSymbolTable(fileName string) (map[string]types.SymbolInfo, error) { + existingSymbols := make(map[string]types.SymbolInfo) + + if _, err := os.Stat(fileName); err != nil { + return existingSymbols, nil + } + + data, err := os.ReadFile(fileName) + if err != nil { + return nil, errors.New("failed to read symbol table file") + } + + parsedJSON := cjson.ParseBytes(data) + if parsedJSON == nil { + return nil, errors.New("failed to parse JSON") + } + + arraySize := parsedJSON.GetArraySize() + + for i := 0; i < int(arraySize); i++ { + item := parsedJSON.GetArrayItem(c.Int(i)) + if item == nil { + continue + } + symbol := types.SymbolInfo{ + Mangle: config.GetStringItem(item, "mangle", ""), + CPP: config.GetStringItem(item, "c++", ""), + Go: config.GetStringItem(item, "go", ""), + } + existingSymbols[symbol.Mangle] = symbol + } + + return existingSymbols, nil +} From 6e24792b9ba1384cb4d683bdf83df83e9a408ff4 Mon Sep 17 00:00:00 2001 From: tsingbx Date: Tue, 30 Jul 2024 21:06:17 +0800 Subject: [PATCH 119/152] Remove redundant code and add examples --- _demo/cshademo/sha.go | 59 +++++++++++++++++++++++++++++++++++-------- c/openssl/sha1.go | 8 ++---- c/openssl/sha256.go | 8 ++---- c/openssl/sha512.go | 8 ++---- ssa/expr.go | 10 -------- 5 files changed, 55 insertions(+), 38 deletions(-) diff --git a/_demo/cshademo/sha.go b/_demo/cshademo/sha.go index ad58d8f8..37d2ad2b 100644 --- a/_demo/cshademo/sha.go +++ b/_demo/cshademo/sha.go @@ -8,22 +8,61 @@ import ( ) func main() { + str := "His money is twice tainted:" var sha1 openssl.SHA_CTX sha1.Init() - sha1.UpdateString("His money is twice tainted:") - sha1.UpdateString(" 'taint yours and 'taint mine.") + sha1.UpdateString(str) - h := make([]byte, openssl.SHA_DIGEST_LENGTH) - sha1.Final(unsafe.SliceData(h)) - fmt.Printf("%x\n", h) + h1 := make([]byte, openssl.SHA_DIGEST_LENGTH) + sha1.Final(unsafe.SliceData(h1)) + fmt.Printf("%x\n", h1) + + h2 := make([]byte, openssl.SHA_DIGEST_LENGTH) + openssl.SHA1String(str, unsafe.SliceData(h2)) + fmt.Printf("%x\n", h2) var sha256 openssl.SHA256_CTX sha256.Init() - sha256.UpdateString("His money is twice tainted:") - sha256.UpdateString(" 'taint yours and 'taint mine.") + sha256.UpdateString(str) + h3 := make([]byte, openssl.SHA256_DIGEST_LENGTH) + sha256.Final(unsafe.SliceData(h3)) + fmt.Printf("%x\n", h3) - hh := make([]byte, openssl.SHA256_DIGEST_LENGTH) - sha256.Final(unsafe.SliceData(hh)) - fmt.Printf("%x\n", hh) + h4 := make([]byte, openssl.SHA256_DIGEST_LENGTH) + openssl.SHA256String(str, unsafe.SliceData(h4)) + fmt.Printf("%x\n", h4) + + var sha512 openssl.SHA512_CTX + sha512.Init() + sha512.UpdateString("His money is twice tainted:") + + h5 := make([]byte, openssl.SHA512_DIGEST_LENGTH) + sha512.Final(unsafe.SliceData(h5)) + fmt.Printf("%x\n", h5) + + h6 := make([]byte, openssl.SHA512_DIGEST_LENGTH) + openssl.SHA512String(str, unsafe.SliceData(h6)) + fmt.Printf("%x\n", h6) + + var sha224 openssl.SHA256_CTX + sha224.Init224() + sha224.Update224String(str) + + h7 := make([]byte, openssl.SHA224_DIGEST_LENGTH) + sha224.Final224(unsafe.SliceData(h7)) + fmt.Printf("%x\n", h7) + h8 := make([]byte, openssl.SHA224_DIGEST_LENGTH) + openssl.SHA224String(str, unsafe.SliceData(h8)) + fmt.Printf("%x\n", h8) + + var sha384 openssl.SHA512_CTX + sha384.Init384() + sha384.Update384String(str) + h9 := make([]byte, openssl.SHA384_DIGEST_LENGTH) + sha384.Final384(unsafe.SliceData(h9)) + fmt.Printf("%x\n", h9) + h10 := make([]byte, openssl.SHA384_DIGEST_LENGTH) + openssl.SHA384String(str, unsafe.SliceData(h10)) + fmt.Printf("%x\n", h10) } diff --git a/c/openssl/sha1.go b/c/openssl/sha1.go index 6ca7f6b2..96da515d 100644 --- a/c/openssl/sha1.go +++ b/c/openssl/sha1.go @@ -42,12 +42,8 @@ func (c *SHA_CTX) Final(md *byte) c.Int { // OSSL_DEPRECATEDIN_3_0 void SHA1_Transform(SHA_CTX *c, const unsigned char *data); // -//go:linkname sha1Transform C.SHA1_Transform -func sha1Transform(c *SHA_CTX, data *byte) - -func (c *SHA_CTX) Transform(data *byte) { - sha1Transform(c, data) -} +// llgo:link (*SHA_CTX).Transform C.SHA1_Transform +func (c *SHA_CTX) Transform(data *byte) {} // unsigned char *SHA1(const unsigned char *d, size_t n, unsigned char *md); // diff --git a/c/openssl/sha256.go b/c/openssl/sha256.go index d18e3a2d..0c41ddc5 100644 --- a/c/openssl/sha256.go +++ b/c/openssl/sha256.go @@ -69,12 +69,8 @@ func (c *SHA256_CTX) Final(md *byte) c.Int { // OSSL_DEPRECATEDIN_3_0 void SHA256_Transform(SHA256_CTX *c, const unsigned char *data); // -//go:linkname sha256Transform C.SHA256_Transform -func sha256Transform(c *SHA256_CTX, data *byte) - -func (c *SHA256_CTX) Transform(data *byte) { - sha256Transform(c, data) -} +// llgo:link (*SHA256_CTX).Transform C.SHA256_Transform +func (c *SHA256_CTX) Transform(data *byte) {} // unsigned char *SHA224(const unsigned char *d, size_t n, unsigned char *md); // diff --git a/c/openssl/sha512.go b/c/openssl/sha512.go index 6add73b7..1e5c5b5b 100644 --- a/c/openssl/sha512.go +++ b/c/openssl/sha512.go @@ -67,12 +67,8 @@ func (c *SHA512_CTX) Final(md *byte) c.Int { // OSSL_DEPRECATEDIN_3_0 void SHA512_Transform(SHA512_CTX *c, const unsigned char *data); // -//go:linkname sha512Transform C.SHA512_Transform -func sha512Transform(c *SHA512_CTX, data *byte) - -func (c *SHA512_CTX) Transform(data *byte) { - sha512Transform(c, data) -} +// llgo:link (*SHA512_CTX).Transform C.SHA512_Transform +func (c *SHA512_CTX) Transform(data *byte) {} // unsigned char *SHA384(const unsigned char *d, size_t n, unsigned char *md); // diff --git a/ssa/expr.go b/ssa/expr.go index f9adbd7d..263ebb7a 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -1022,10 +1022,6 @@ func (b Builder) Do(da DoAction, fn Expr, args ...Expr) (ret Expr) { // Go spec (excluding "make" and "new"). func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) { switch fn { - case "max": - fallthrough - case "min": - return b.max(args...) case "len": if len(args) == 1 { arg := args[0] @@ -1132,12 +1128,6 @@ func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) { panic("todo: " + fn) } -// max return the max value for the args -func (b Builder) max(args ...Expr) (ret Expr) { - //todo - return args[len(args)-1] -} - // Println prints the arguments to stderr, followed by a newline. func (b Builder) Println(args ...Expr) (ret Expr) { return b.PrintEx(true, args...) From 95f64761ca424ee04e31ef2d2bf23e8a5813aa67 Mon Sep 17 00:00:00 2001 From: tsingbx Date: Tue, 30 Jul 2024 21:24:03 +0800 Subject: [PATCH 120/152] add SHA224_CTX, SHA384_CTX --- _demo/cshademo/sha.go | 16 ++++++++-------- c/openssl/sha256.go | 22 ++++++++++++---------- c/openssl/sha512.go | 22 ++++++++++++---------- 3 files changed, 32 insertions(+), 28 deletions(-) diff --git a/_demo/cshademo/sha.go b/_demo/cshademo/sha.go index 37d2ad2b..ee2577cb 100644 --- a/_demo/cshademo/sha.go +++ b/_demo/cshademo/sha.go @@ -45,22 +45,22 @@ func main() { openssl.SHA512String(str, unsafe.SliceData(h6)) fmt.Printf("%x\n", h6) - var sha224 openssl.SHA256_CTX - sha224.Init224() - sha224.Update224String(str) + var sha224 openssl.SHA224_CTX + sha224.Init() + sha224.UpdateString(str) h7 := make([]byte, openssl.SHA224_DIGEST_LENGTH) - sha224.Final224(unsafe.SliceData(h7)) + sha224.Final(unsafe.SliceData(h7)) fmt.Printf("%x\n", h7) h8 := make([]byte, openssl.SHA224_DIGEST_LENGTH) openssl.SHA224String(str, unsafe.SliceData(h8)) fmt.Printf("%x\n", h8) - var sha384 openssl.SHA512_CTX - sha384.Init384() - sha384.Update384String(str) + var sha384 openssl.SHA384_CTX + sha384.Init() + sha384.UpdateString(str) h9 := make([]byte, openssl.SHA384_DIGEST_LENGTH) - sha384.Final384(unsafe.SliceData(h9)) + sha384.Final(unsafe.SliceData(h9)) fmt.Printf("%x\n", h9) h10 := make([]byte, openssl.SHA384_DIGEST_LENGTH) openssl.SHA384String(str, unsafe.SliceData(h10)) diff --git a/c/openssl/sha256.go b/c/openssl/sha256.go index 0c41ddc5..2934208d 100644 --- a/c/openssl/sha256.go +++ b/c/openssl/sha256.go @@ -13,30 +13,32 @@ type SHA256_CTX struct { Num, MdLen c.Uint } +type SHA224_CTX SHA256_CTX + // OSSL_DEPRECATEDIN_3_0 int SHA224_Init(SHA256_CTX *c); // -// llgo:link (*SHA256_CTX).Init224 C.SHA224_Init -func (c *SHA256_CTX) Init224() c.Int { return 0 } +// llgo:link (*SHA224_CTX).Init C.SHA224_Init +func (c *SHA224_CTX) Init() c.Int { return 0 } // OSSL_DEPRECATEDIN_3_0 int SHA224_Update(SHA256_CTX *c, const void *data, size_t len); // -// llgo:link (*SHA256_CTX).Update224 C.SHA224_Update -func (c *SHA256_CTX) Update224(data unsafe.Pointer, n uintptr) c.Int { return 0 } +// llgo:link (*SHA224_CTX).Update C.SHA224_Update +func (c *SHA224_CTX) Update(data unsafe.Pointer, n uintptr) c.Int { return 0 } -func (c *SHA256_CTX) Update224Bytes(data []byte) c.Int { - return c.Update224(unsafe.Pointer(unsafe.SliceData(data)), uintptr(len(data))) +func (c *SHA224_CTX) UpdateBytes(data []byte) c.Int { + return c.Update(unsafe.Pointer(unsafe.SliceData(data)), uintptr(len(data))) } -func (c *SHA256_CTX) Update224String(data string) c.Int { - return c.Update224(unsafe.Pointer(unsafe.StringData(data)), uintptr(len(data))) +func (c *SHA224_CTX) UpdateString(data string) c.Int { + return c.Update(unsafe.Pointer(unsafe.StringData(data)), uintptr(len(data))) } // OSSL_DEPRECATEDIN_3_0 int SHA224_Final(unsigned char *md, SHA256_CTX *c); // //go:linkname sha224Final C.SHA224_Final -func sha224Final(md *byte, c *SHA256_CTX) c.Int +func sha224Final(md *byte, c *SHA224_CTX) c.Int -func (c *SHA256_CTX) Final224(md *byte) c.Int { +func (c *SHA224_CTX) Final(md *byte) c.Int { return sha224Final(md, c) } diff --git a/c/openssl/sha512.go b/c/openssl/sha512.go index 1e5c5b5b..85431af8 100644 --- a/c/openssl/sha512.go +++ b/c/openssl/sha512.go @@ -13,30 +13,32 @@ type SHA512_CTX struct { Num, MdLen c.Uint } +type SHA384_CTX SHA512_CTX + // OSSL_DEPRECATEDIN_3_0 int SHA384_Init(SHA512_CTX *c); // -// llgo:link (*SHA512_CTX).Init384 C.SHA384_Init -func (c *SHA512_CTX) Init384() c.Int { return 0 } +// llgo:link (*SHA384_CTX).Init C.SHA384_Init +func (c *SHA384_CTX) Init() c.Int { return 0 } // OSSL_DEPRECATEDIN_3_0 int SHA384_Update(SHA512_CTX *c, const void *data, size_t len); // -// llgo:link (*SHA512_CTX).Update384 C.SHA384_Update -func (c *SHA512_CTX) Update384(data unsafe.Pointer, n uintptr) c.Int { return 0 } +// llgo:link (*SHA384_CTX).Update C.SHA384_Update +func (c *SHA384_CTX) Update(data unsafe.Pointer, n uintptr) c.Int { return 0 } -func (c *SHA512_CTX) Update384Bytes(data []byte) c.Int { - return c.Update384(unsafe.Pointer(unsafe.SliceData(data)), uintptr(len(data))) +func (c *SHA384_CTX) UpdateBytes(data []byte) c.Int { + return c.Update(unsafe.Pointer(unsafe.SliceData(data)), uintptr(len(data))) } -func (c *SHA512_CTX) Update384String(data string) c.Int { - return c.Update384(unsafe.Pointer(unsafe.StringData(data)), uintptr(len(data))) +func (c *SHA384_CTX) UpdateString(data string) c.Int { + return c.Update(unsafe.Pointer(unsafe.StringData(data)), uintptr(len(data))) } // OSSL_DEPRECATEDIN_3_0 int SHA384_Final(unsigned char *md, SHA512_CTX *c); // //go:linkname sha384Final C.SHA384_Final -func sha384Final(md *byte, c *SHA512_CTX) c.Int +func sha384Final(md *byte, c *SHA384_CTX) c.Int -func (c *SHA512_CTX) Final384(md *byte) c.Int { +func (c *SHA384_CTX) Final(md *byte) c.Int { return sha384Final(md, c) } From cacd52f81da231adae88d1ef520a4a23cfeb288a Mon Sep 17 00:00:00 2001 From: xushiwei Date: Tue, 30 Jul 2024 21:41:26 +0800 Subject: [PATCH 121/152] library: math/rand --- README.md | 1 + _cmptest/_bigsqrt2/sqrt2.go | 47 +++ _demo/crand/rand.go | 26 ++ _demo/llama2-c/run.go | 3 +- _demo/rand/rand.go | 15 - _demo/randdemo/rand.go | 12 + c/c.go | 3 - internal/build/build.go | 1 + internal/lib/math/rand/exp.go | 221 +++++++++++++ internal/lib/math/rand/normal.go | 156 +++++++++ internal/lib/math/rand/rand.go | 547 +++++++++++++++++++++++++++++++ internal/lib/math/rand/rng.go | 252 ++++++++++++++ internal/lib/math/rand/zipf.go | 77 +++++ 13 files changed, 1342 insertions(+), 19 deletions(-) create mode 100644 _cmptest/_bigsqrt2/sqrt2.go create mode 100644 _demo/crand/rand.go delete mode 100644 _demo/rand/rand.go create mode 100644 _demo/randdemo/rand.go create mode 100644 internal/lib/math/rand/exp.go create mode 100644 internal/lib/math/rand/normal.go create mode 100644 internal/lib/math/rand/rand.go create mode 100644 internal/lib/math/rand/rng.go create mode 100644 internal/lib/math/rand/zipf.go diff --git a/README.md b/README.md index 86793dcf..f4b54e4a 100644 --- a/README.md +++ b/README.md @@ -270,6 +270,7 @@ Here are the Go packages that can be imported correctly: * [math](https://pkg.go.dev/math) * [math/bits](https://pkg.go.dev/math/bits) * [math/cmplx](https://pkg.go.dev/math/cmplx) +* [math/rand](https://pkg.go.dev/math/rand) * [errors](https://pkg.go.dev/errors) * [context](https://pkg.go.dev/context) * [io](https://pkg.go.dev/io) diff --git a/_cmptest/_bigsqrt2/sqrt2.go b/_cmptest/_bigsqrt2/sqrt2.go new file mode 100644 index 00000000..156bb800 --- /dev/null +++ b/_cmptest/_bigsqrt2/sqrt2.go @@ -0,0 +1,47 @@ +package main + +import ( + "fmt" + "math" + "math/big" +) + +func main() { + // We'll do computations with 200 bits of precision in the mantissa. + const prec = 200 + + // Compute the square root of 2 using Newton's Method. We start with + // an initial estimate for sqrt(2), and then iterate: + // x_{n+1} = 1/2 * ( x_n + (2.0 / x_n) ) + + // Since Newton's Method doubles the number of correct digits at each + // iteration, we need at least log_2(prec) steps. + steps := int(math.Log2(prec)) + + // Initialize values we need for the computation. + two := new(big.Float).SetPrec(prec).SetInt64(2) + half := new(big.Float).SetPrec(prec).SetFloat64(0.5) + + // Use 1 as the initial estimate. + x := new(big.Float).SetPrec(prec).SetInt64(1) + + // We use t as a temporary variable. There's no need to set its precision + // since big.Float values with unset (== 0) precision automatically assume + // the largest precision of the arguments when used as the result (receiver) + // of a big.Float operation. + t := new(big.Float) + + // Iterate. + for i := 0; i <= steps; i++ { + t.Quo(two, x) // t = 2.0 / x_n + t.Add(x, t) // t = x_n + (2.0 / x_n) + x.Mul(half, t) // x_{n+1} = 0.5 * t + } + + // We can use the usual fmt.Printf verbs since big.Float implements fmt.Formatter + fmt.Printf("sqrt(2) = %.50f\n", x) + + // Print the error between 2 and x*x. + t.Mul(x, x) // t = x*x + fmt.Printf("error = %e\n", t.Sub(two, t)) +} diff --git a/_demo/crand/rand.go b/_demo/crand/rand.go new file mode 100644 index 00000000..00df6fd2 --- /dev/null +++ b/_demo/crand/rand.go @@ -0,0 +1,26 @@ +package main + +import ( + "fmt" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/math/rand" + "github.com/goplus/llgo/c/time" +) + +func fastrand64() uint64 { + v1 := uint64(rand.Random()) + v2 := uint64(rand.Random()) + return v1 ^ (v2 << 32) +} + +func main() { + rand.Srand(c.Uint(time.Time(nil))) + fmt.Printf("%x\n", rand.Rand()) + fmt.Printf("%x\n", rand.Rand()) + + rand.Srandom(c.Uint(time.Time(nil))) + fmt.Printf("%x\n", rand.Random()) + fmt.Printf("%x\n", rand.Random()) + fmt.Printf("%x\n", fastrand64()) +} diff --git a/_demo/llama2-c/run.go b/_demo/llama2-c/run.go index d7b2102a..29077c10 100644 --- a/_demo/llama2-c/run.go +++ b/_demo/llama2-c/run.go @@ -3,6 +3,7 @@ package main import ( "github.com/goplus/llgo/c" "github.com/goplus/llgo/c/llama2" + "github.com/goplus/llgo/c/time" ) func main() { @@ -11,7 +12,7 @@ func main() { var tokenizerPath *c.Char = c.Str("tokenizer.bin") var temperature, topp c.Float = 1.0, 0.9 var steps c.Int = 256 - var rngSeed uint64 = uint64(c.Time(nil)) + var rngSeed uint64 = uint64(time.Time(nil)) loop: // parse command line arguments for { diff --git a/_demo/rand/rand.go b/_demo/rand/rand.go deleted file mode 100644 index 106398a9..00000000 --- a/_demo/rand/rand.go +++ /dev/null @@ -1,15 +0,0 @@ -package main - -import ( - "github.com/goplus/llgo/c" - "github.com/goplus/llgo/c/math/rand" -) - -func main() { - var s c.Uint = 6 - rand.Srand(s) - rr := rand.RandR(&s) - r := rand.Rand() - println("r:", r) - println("rr:", rr) -} diff --git a/_demo/randdemo/rand.go b/_demo/randdemo/rand.go new file mode 100644 index 00000000..23ce9ebe --- /dev/null +++ b/_demo/randdemo/rand.go @@ -0,0 +1,12 @@ +package main + +import ( + "fmt" + "math/rand" +) + +func main() { + fmt.Println(rand.Intn(100)) + fmt.Println(rand.Intn(100)) + fmt.Println(rand.Intn(100)) +} diff --git a/c/c.go b/c/c.go index 2b8eada1..f72670b9 100644 --- a/c/c.go +++ b/c/c.go @@ -227,9 +227,6 @@ func Perror(s *Char) // ----------------------------------------------------------------------------- -//go:linkname Time C.time -func Time(*int32) int32 - //go:linkname Usleep C.usleep func Usleep(useconds Uint) Int diff --git a/internal/build/build.go b/internal/build/build.go index 8b1485d5..8f487d8e 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -769,6 +769,7 @@ var hasAltPkg = map[string]none{ "internal/syscall/unix": {}, "math": {}, "math/cmplx": {}, + "math/rand": {}, "reflect": {}, "sync": {}, "sync/atomic": {}, diff --git a/internal/lib/math/rand/exp.go b/internal/lib/math/rand/exp.go new file mode 100644 index 00000000..c1162c19 --- /dev/null +++ b/internal/lib/math/rand/exp.go @@ -0,0 +1,221 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rand + +import ( + "math" +) + +/* + * Exponential distribution + * + * See "The Ziggurat Method for Generating Random Variables" + * (Marsaglia & Tsang, 2000) + * https://www.jstatsoft.org/v05/i08/paper [pdf] + */ + +const ( + re = 7.69711747013104972 +) + +// ExpFloat64 returns an exponentially distributed float64 in the range +// (0, +math.MaxFloat64] with an exponential distribution whose rate parameter +// (lambda) is 1 and whose mean is 1/lambda (1). +// To produce a distribution with a different rate parameter, +// callers can adjust the output using: +// +// sample = ExpFloat64() / desiredRateParameter +func (r *Rand) ExpFloat64() float64 { + for { + j := r.Uint32() + i := j & 0xFF + x := float64(j) * float64(we[i]) + if j < ke[i] { + return x + } + if i == 0 { + return re - math.Log(r.Float64()) + } + if fe[i]+float32(r.Float64())*(fe[i-1]-fe[i]) < float32(math.Exp(-x)) { + return x + } + } +} + +var ke = [256]uint32{ + 0xe290a139, 0x0, 0x9beadebc, 0xc377ac71, 0xd4ddb990, + 0xde893fb8, 0xe4a8e87c, 0xe8dff16a, 0xebf2deab, 0xee49a6e8, + 0xf0204efd, 0xf19bdb8e, 0xf2d458bb, 0xf3da104b, 0xf4b86d78, + 0xf577ad8a, 0xf61de83d, 0xf6afb784, 0xf730a573, 0xf7a37651, + 0xf80a5bb6, 0xf867189d, 0xf8bb1b4f, 0xf9079062, 0xf94d70ca, + 0xf98d8c7d, 0xf9c8928a, 0xf9ff175b, 0xfa319996, 0xfa6085f8, + 0xfa8c3a62, 0xfab5084e, 0xfadb36c8, 0xfaff0410, 0xfb20a6ea, + 0xfb404fb4, 0xfb5e2951, 0xfb7a59e9, 0xfb95038c, 0xfbae44ba, + 0xfbc638d8, 0xfbdcf892, 0xfbf29a30, 0xfc0731df, 0xfc1ad1ed, + 0xfc2d8b02, 0xfc3f6c4d, 0xfc5083ac, 0xfc60ddd1, 0xfc708662, + 0xfc7f8810, 0xfc8decb4, 0xfc9bbd62, 0xfca9027c, 0xfcb5c3c3, + 0xfcc20864, 0xfccdd70a, 0xfcd935e3, 0xfce42ab0, 0xfceebace, + 0xfcf8eb3b, 0xfd02c0a0, 0xfd0c3f59, 0xfd156b7b, 0xfd1e48d6, + 0xfd26daff, 0xfd2f2552, 0xfd372af7, 0xfd3eeee5, 0xfd4673e7, + 0xfd4dbc9e, 0xfd54cb85, 0xfd5ba2f2, 0xfd62451b, 0xfd68b415, + 0xfd6ef1da, 0xfd750047, 0xfd7ae120, 0xfd809612, 0xfd8620b4, + 0xfd8b8285, 0xfd90bcf5, 0xfd95d15e, 0xfd9ac10b, 0xfd9f8d36, + 0xfda43708, 0xfda8bf9e, 0xfdad2806, 0xfdb17141, 0xfdb59c46, + 0xfdb9a9fd, 0xfdbd9b46, 0xfdc170f6, 0xfdc52bd8, 0xfdc8ccac, + 0xfdcc542d, 0xfdcfc30b, 0xfdd319ef, 0xfdd6597a, 0xfdd98245, + 0xfddc94e5, 0xfddf91e6, 0xfde279ce, 0xfde54d1f, 0xfde80c52, + 0xfdeab7de, 0xfded5034, 0xfdefd5be, 0xfdf248e3, 0xfdf4aa06, + 0xfdf6f984, 0xfdf937b6, 0xfdfb64f4, 0xfdfd818d, 0xfdff8dd0, + 0xfe018a08, 0xfe03767a, 0xfe05536c, 0xfe07211c, 0xfe08dfc9, + 0xfe0a8fab, 0xfe0c30fb, 0xfe0dc3ec, 0xfe0f48b1, 0xfe10bf76, + 0xfe122869, 0xfe1383b4, 0xfe14d17c, 0xfe1611e7, 0xfe174516, + 0xfe186b2a, 0xfe19843e, 0xfe1a9070, 0xfe1b8fd6, 0xfe1c8289, + 0xfe1d689b, 0xfe1e4220, 0xfe1f0f26, 0xfe1fcfbc, 0xfe2083ed, + 0xfe212bc3, 0xfe21c745, 0xfe225678, 0xfe22d95f, 0xfe234ffb, + 0xfe23ba4a, 0xfe241849, 0xfe2469f2, 0xfe24af3c, 0xfe24e81e, + 0xfe25148b, 0xfe253474, 0xfe2547c7, 0xfe254e70, 0xfe25485a, + 0xfe25356a, 0xfe251586, 0xfe24e88f, 0xfe24ae64, 0xfe2466e1, + 0xfe2411df, 0xfe23af34, 0xfe233eb4, 0xfe22c02c, 0xfe22336b, + 0xfe219838, 0xfe20ee58, 0xfe20358c, 0xfe1f6d92, 0xfe1e9621, + 0xfe1daef0, 0xfe1cb7ac, 0xfe1bb002, 0xfe1a9798, 0xfe196e0d, + 0xfe1832fd, 0xfe16e5fe, 0xfe15869d, 0xfe141464, 0xfe128ed3, + 0xfe10f565, 0xfe0f478c, 0xfe0d84b1, 0xfe0bac36, 0xfe09bd73, + 0xfe07b7b5, 0xfe059a40, 0xfe03644c, 0xfe011504, 0xfdfeab88, + 0xfdfc26e9, 0xfdf98629, 0xfdf6c83b, 0xfdf3ec01, 0xfdf0f04a, + 0xfdedd3d1, 0xfdea953d, 0xfde7331e, 0xfde3abe9, 0xfddffdfb, + 0xfddc2791, 0xfdd826cd, 0xfdd3f9a8, 0xfdcf9dfc, 0xfdcb1176, + 0xfdc65198, 0xfdc15bb3, 0xfdbc2ce2, 0xfdb6c206, 0xfdb117be, + 0xfdab2a63, 0xfda4f5fd, 0xfd9e7640, 0xfd97a67a, 0xfd908192, + 0xfd8901f2, 0xfd812182, 0xfd78d98e, 0xfd7022bb, 0xfd66f4ed, + 0xfd5d4732, 0xfd530f9c, 0xfd48432b, 0xfd3cd59a, 0xfd30b936, + 0xfd23dea4, 0xfd16349e, 0xfd07a7a3, 0xfcf8219b, 0xfce7895b, + 0xfcd5c220, 0xfcc2aadb, 0xfcae1d5e, 0xfc97ed4e, 0xfc7fe6d4, + 0xfc65ccf3, 0xfc495762, 0xfc2a2fc8, 0xfc07ee19, 0xfbe213c1, + 0xfbb8051a, 0xfb890078, 0xfb5411a5, 0xfb180005, 0xfad33482, + 0xfa839276, 0xfa263b32, 0xf9b72d1c, 0xf930a1a2, 0xf889f023, + 0xf7b577d2, 0xf69c650c, 0xf51530f0, 0xf2cb0e3c, 0xeeefb15d, + 0xe6da6ecf, +} +var we = [256]float32{ + 2.0249555e-09, 1.486674e-11, 2.4409617e-11, 3.1968806e-11, + 3.844677e-11, 4.4228204e-11, 4.9516443e-11, 5.443359e-11, + 5.905944e-11, 6.344942e-11, 6.7643814e-11, 7.1672945e-11, + 7.556032e-11, 7.932458e-11, 8.298079e-11, 8.654132e-11, + 9.0016515e-11, 9.3415074e-11, 9.674443e-11, 1.0001099e-10, + 1.03220314e-10, 1.06377254e-10, 1.09486115e-10, 1.1255068e-10, + 1.1557435e-10, 1.1856015e-10, 1.2151083e-10, 1.2442886e-10, + 1.2731648e-10, 1.3017575e-10, 1.3300853e-10, 1.3581657e-10, + 1.3860142e-10, 1.4136457e-10, 1.4410738e-10, 1.4683108e-10, + 1.4953687e-10, 1.5222583e-10, 1.54899e-10, 1.5755733e-10, + 1.6020171e-10, 1.6283301e-10, 1.6545203e-10, 1.6805951e-10, + 1.7065617e-10, 1.732427e-10, 1.7581973e-10, 1.7838787e-10, + 1.8094774e-10, 1.8349985e-10, 1.8604476e-10, 1.8858298e-10, + 1.9111498e-10, 1.9364126e-10, 1.9616223e-10, 1.9867835e-10, + 2.0119004e-10, 2.0369768e-10, 2.0620168e-10, 2.087024e-10, + 2.1120022e-10, 2.136955e-10, 2.1618855e-10, 2.1867974e-10, + 2.2116936e-10, 2.2365775e-10, 2.261452e-10, 2.2863202e-10, + 2.311185e-10, 2.3360494e-10, 2.360916e-10, 2.3857874e-10, + 2.4106667e-10, 2.4355562e-10, 2.4604588e-10, 2.485377e-10, + 2.5103128e-10, 2.5352695e-10, 2.560249e-10, 2.585254e-10, + 2.6102867e-10, 2.6353494e-10, 2.6604446e-10, 2.6855745e-10, + 2.7107416e-10, 2.7359479e-10, 2.761196e-10, 2.7864877e-10, + 2.8118255e-10, 2.8372119e-10, 2.8626485e-10, 2.888138e-10, + 2.9136826e-10, 2.939284e-10, 2.9649452e-10, 2.9906677e-10, + 3.016454e-10, 3.0423064e-10, 3.0682268e-10, 3.0942177e-10, + 3.1202813e-10, 3.1464195e-10, 3.1726352e-10, 3.19893e-10, + 3.2253064e-10, 3.251767e-10, 3.2783135e-10, 3.3049485e-10, + 3.3316744e-10, 3.3584938e-10, 3.3854083e-10, 3.4124212e-10, + 3.4395342e-10, 3.46675e-10, 3.4940711e-10, 3.5215003e-10, + 3.5490397e-10, 3.5766917e-10, 3.6044595e-10, 3.6323455e-10, + 3.660352e-10, 3.6884823e-10, 3.7167386e-10, 3.745124e-10, + 3.773641e-10, 3.802293e-10, 3.8310827e-10, 3.860013e-10, + 3.8890866e-10, 3.918307e-10, 3.9476775e-10, 3.9772008e-10, + 4.0068804e-10, 4.0367196e-10, 4.0667217e-10, 4.09689e-10, + 4.1272286e-10, 4.1577405e-10, 4.1884296e-10, 4.2192994e-10, + 4.250354e-10, 4.281597e-10, 4.313033e-10, 4.3446652e-10, + 4.3764986e-10, 4.408537e-10, 4.4407847e-10, 4.4732465e-10, + 4.5059267e-10, 4.5388301e-10, 4.571962e-10, 4.6053267e-10, + 4.6389292e-10, 4.6727755e-10, 4.70687e-10, 4.741219e-10, + 4.7758275e-10, 4.810702e-10, 4.845848e-10, 4.8812715e-10, + 4.9169796e-10, 4.9529775e-10, 4.989273e-10, 5.0258725e-10, + 5.0627835e-10, 5.100013e-10, 5.1375687e-10, 5.1754584e-10, + 5.21369e-10, 5.2522725e-10, 5.2912136e-10, 5.330522e-10, + 5.370208e-10, 5.4102806e-10, 5.45075e-10, 5.491625e-10, + 5.532918e-10, 5.5746385e-10, 5.616799e-10, 5.6594107e-10, + 5.7024857e-10, 5.746037e-10, 5.7900773e-10, 5.834621e-10, + 5.8796823e-10, 5.925276e-10, 5.971417e-10, 6.018122e-10, + 6.065408e-10, 6.113292e-10, 6.1617933e-10, 6.2109295e-10, + 6.260722e-10, 6.3111916e-10, 6.3623595e-10, 6.4142497e-10, + 6.4668854e-10, 6.5202926e-10, 6.5744976e-10, 6.6295286e-10, + 6.6854156e-10, 6.742188e-10, 6.79988e-10, 6.858526e-10, + 6.9181616e-10, 6.978826e-10, 7.04056e-10, 7.103407e-10, + 7.167412e-10, 7.2326256e-10, 7.2990985e-10, 7.366886e-10, + 7.4360473e-10, 7.5066453e-10, 7.5787476e-10, 7.6524265e-10, + 7.7277595e-10, 7.80483e-10, 7.883728e-10, 7.9645507e-10, + 8.047402e-10, 8.1323964e-10, 8.219657e-10, 8.309319e-10, + 8.401528e-10, 8.496445e-10, 8.594247e-10, 8.6951274e-10, + 8.799301e-10, 8.9070046e-10, 9.018503e-10, 9.134092e-10, + 9.254101e-10, 9.378904e-10, 9.508923e-10, 9.644638e-10, + 9.786603e-10, 9.935448e-10, 1.0091913e-09, 1.025686e-09, + 1.0431306e-09, 1.0616465e-09, 1.08138e-09, 1.1025096e-09, + 1.1252564e-09, 1.1498986e-09, 1.1767932e-09, 1.206409e-09, + 1.2393786e-09, 1.276585e-09, 1.3193139e-09, 1.3695435e-09, + 1.4305498e-09, 1.508365e-09, 1.6160854e-09, 1.7921248e-09, +} +var fe = [256]float32{ + 1, 0.9381437, 0.90046996, 0.87170434, 0.8477855, 0.8269933, + 0.8084217, 0.7915276, 0.77595687, 0.7614634, 0.7478686, + 0.7350381, 0.72286767, 0.71127474, 0.70019263, 0.6895665, + 0.67935055, 0.6695063, 0.66000086, 0.65080583, 0.6418967, + 0.63325197, 0.6248527, 0.6166822, 0.60872537, 0.60096896, + 0.5934009, 0.58601034, 0.5787874, 0.57172304, 0.5648092, + 0.5580383, 0.5514034, 0.5448982, 0.5385169, 0.53225386, + 0.5261042, 0.52006316, 0.5141264, 0.50828975, 0.5025495, + 0.496902, 0.49134386, 0.485872, 0.48048335, 0.4751752, + 0.46994483, 0.46478975, 0.45970762, 0.45469615, 0.44975325, + 0.44487688, 0.44006512, 0.43531612, 0.43062815, 0.42599955, + 0.42142874, 0.4169142, 0.41245446, 0.40804818, 0.403694, + 0.3993907, 0.39513698, 0.39093173, 0.38677382, 0.38266218, + 0.37859577, 0.37457356, 0.37059465, 0.3666581, 0.362763, + 0.35890847, 0.35509375, 0.351318, 0.3475805, 0.34388044, + 0.34021714, 0.3365899, 0.33299807, 0.32944095, 0.32591796, + 0.3224285, 0.3189719, 0.31554767, 0.31215525, 0.30879408, + 0.3054636, 0.3021634, 0.29889292, 0.2956517, 0.29243928, + 0.28925523, 0.28609908, 0.28297043, 0.27986884, 0.27679393, + 0.2737453, 0.2707226, 0.2677254, 0.26475343, 0.26180625, + 0.25888354, 0.25598502, 0.2531103, 0.25025907, 0.24743107, + 0.24462597, 0.24184346, 0.23908329, 0.23634516, 0.23362878, + 0.23093392, 0.2282603, 0.22560766, 0.22297576, 0.22036438, + 0.21777324, 0.21520215, 0.21265087, 0.21011916, 0.20760682, + 0.20511365, 0.20263945, 0.20018397, 0.19774707, 0.19532852, + 0.19292815, 0.19054577, 0.1881812, 0.18583426, 0.18350479, + 0.1811926, 0.17889754, 0.17661946, 0.17435817, 0.17211354, + 0.1698854, 0.16767362, 0.16547804, 0.16329853, 0.16113494, + 0.15898713, 0.15685499, 0.15473837, 0.15263714, 0.15055119, + 0.14848037, 0.14642459, 0.14438373, 0.14235765, 0.14034624, + 0.13834943, 0.13636707, 0.13439907, 0.13244532, 0.13050574, + 0.1285802, 0.12666863, 0.12477092, 0.12288698, 0.12101672, + 0.119160056, 0.1173169, 0.115487166, 0.11367077, 0.11186763, + 0.11007768, 0.10830083, 0.10653701, 0.10478614, 0.10304816, + 0.101323, 0.09961058, 0.09791085, 0.09622374, 0.09454919, + 0.09288713, 0.091237515, 0.08960028, 0.087975375, 0.08636274, + 0.08476233, 0.083174095, 0.081597984, 0.08003395, 0.07848195, + 0.076941945, 0.07541389, 0.07389775, 0.072393484, 0.07090106, + 0.069420435, 0.06795159, 0.066494495, 0.06504912, 0.063615434, + 0.062193416, 0.060783047, 0.059384305, 0.057997175, + 0.05662164, 0.05525769, 0.053905312, 0.052564494, 0.051235236, + 0.049917534, 0.048611384, 0.047316793, 0.046033762, 0.0447623, + 0.043502413, 0.042254124, 0.041017443, 0.039792392, + 0.038578995, 0.037377283, 0.036187284, 0.035009038, + 0.033842582, 0.032687962, 0.031545233, 0.030414443, 0.02929566, + 0.02818895, 0.027094385, 0.026012046, 0.024942026, 0.023884421, + 0.022839336, 0.021806888, 0.020787204, 0.019780423, 0.0187867, + 0.0178062, 0.016839107, 0.015885621, 0.014945968, 0.014020392, + 0.013109165, 0.012212592, 0.011331013, 0.01046481, 0.009614414, + 0.008780315, 0.007963077, 0.0071633533, 0.006381906, + 0.0056196423, 0.0048776558, 0.004157295, 0.0034602648, + 0.0027887989, 0.0021459677, 0.0015362998, 0.0009672693, + 0.00045413437, +} diff --git a/internal/lib/math/rand/normal.go b/internal/lib/math/rand/normal.go new file mode 100644 index 00000000..6654479a --- /dev/null +++ b/internal/lib/math/rand/normal.go @@ -0,0 +1,156 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rand + +import ( + "math" +) + +/* + * Normal distribution + * + * See "The Ziggurat Method for Generating Random Variables" + * (Marsaglia & Tsang, 2000) + * http://www.jstatsoft.org/v05/i08/paper [pdf] + */ + +const ( + rn = 3.442619855899 +) + +func absInt32(i int32) uint32 { + if i < 0 { + return uint32(-i) + } + return uint32(i) +} + +// NormFloat64 returns a normally distributed float64 in +// the range -math.MaxFloat64 through +math.MaxFloat64 inclusive, +// with standard normal distribution (mean = 0, stddev = 1). +// To produce a different normal distribution, callers can +// adjust the output using: +// +// sample = NormFloat64() * desiredStdDev + desiredMean +func (r *Rand) NormFloat64() float64 { + for { + j := int32(r.Uint32()) // Possibly negative + i := j & 0x7F + x := float64(j) * float64(wn[i]) + if absInt32(j) < kn[i] { + // This case should be hit better than 99% of the time. + return x + } + + if i == 0 { + // This extra work is only required for the base strip. + for { + x = -math.Log(r.Float64()) * (1.0 / rn) + y := -math.Log(r.Float64()) + if y+y >= x*x { + break + } + } + if j > 0 { + return rn + x + } + return -rn - x + } + if fn[i]+float32(r.Float64())*(fn[i-1]-fn[i]) < float32(math.Exp(-.5*x*x)) { + return x + } + } +} + +var kn = [128]uint32{ + 0x76ad2212, 0x0, 0x600f1b53, 0x6ce447a6, 0x725b46a2, + 0x7560051d, 0x774921eb, 0x789a25bd, 0x799045c3, 0x7a4bce5d, + 0x7adf629f, 0x7b5682a6, 0x7bb8a8c6, 0x7c0ae722, 0x7c50cce7, + 0x7c8cec5b, 0x7cc12cd6, 0x7ceefed2, 0x7d177e0b, 0x7d3b8883, + 0x7d5bce6c, 0x7d78dd64, 0x7d932886, 0x7dab0e57, 0x7dc0dd30, + 0x7dd4d688, 0x7de73185, 0x7df81cea, 0x7e07c0a3, 0x7e163efa, + 0x7e23b587, 0x7e303dfd, 0x7e3beec2, 0x7e46db77, 0x7e51155d, + 0x7e5aabb3, 0x7e63abf7, 0x7e6c222c, 0x7e741906, 0x7e7b9a18, + 0x7e82adfa, 0x7e895c63, 0x7e8fac4b, 0x7e95a3fb, 0x7e9b4924, + 0x7ea0a0ef, 0x7ea5b00d, 0x7eaa7ac3, 0x7eaf04f3, 0x7eb3522a, + 0x7eb765a5, 0x7ebb4259, 0x7ebeeafd, 0x7ec2620a, 0x7ec5a9c4, + 0x7ec8c441, 0x7ecbb365, 0x7ece78ed, 0x7ed11671, 0x7ed38d62, + 0x7ed5df12, 0x7ed80cb4, 0x7eda175c, 0x7edc0005, 0x7eddc78e, + 0x7edf6ebf, 0x7ee0f647, 0x7ee25ebe, 0x7ee3a8a9, 0x7ee4d473, + 0x7ee5e276, 0x7ee6d2f5, 0x7ee7a620, 0x7ee85c10, 0x7ee8f4cd, + 0x7ee97047, 0x7ee9ce59, 0x7eea0eca, 0x7eea3147, 0x7eea3568, + 0x7eea1aab, 0x7ee9e071, 0x7ee98602, 0x7ee90a88, 0x7ee86d08, + 0x7ee7ac6a, 0x7ee6c769, 0x7ee5bc9c, 0x7ee48a67, 0x7ee32efc, + 0x7ee1a857, 0x7edff42f, 0x7ede0ffa, 0x7edbf8d9, 0x7ed9ab94, + 0x7ed7248d, 0x7ed45fae, 0x7ed1585c, 0x7ece095f, 0x7eca6ccb, + 0x7ec67be2, 0x7ec22eee, 0x7ebd7d1a, 0x7eb85c35, 0x7eb2c075, + 0x7eac9c20, 0x7ea5df27, 0x7e9e769f, 0x7e964c16, 0x7e8d44ba, + 0x7e834033, 0x7e781728, 0x7e6b9933, 0x7e5d8a1a, 0x7e4d9ded, + 0x7e3b737a, 0x7e268c2f, 0x7e0e3ff5, 0x7df1aa5d, 0x7dcf8c72, + 0x7da61a1e, 0x7d72a0fb, 0x7d30e097, 0x7cd9b4ab, 0x7c600f1a, + 0x7ba90bdc, 0x7a722176, 0x77d664e5, +} +var wn = [128]float32{ + 1.7290405e-09, 1.2680929e-10, 1.6897518e-10, 1.9862688e-10, + 2.2232431e-10, 2.4244937e-10, 2.601613e-10, 2.7611988e-10, + 2.9073963e-10, 3.042997e-10, 3.1699796e-10, 3.289802e-10, + 3.4035738e-10, 3.5121603e-10, 3.616251e-10, 3.7164058e-10, + 3.8130857e-10, 3.9066758e-10, 3.9975012e-10, 4.08584e-10, + 4.1719309e-10, 4.2559822e-10, 4.338176e-10, 4.418672e-10, + 4.497613e-10, 4.5751258e-10, 4.651324e-10, 4.7263105e-10, + 4.8001775e-10, 4.87301e-10, 4.944885e-10, 5.015873e-10, + 5.0860405e-10, 5.155446e-10, 5.2241467e-10, 5.2921934e-10, + 5.359635e-10, 5.426517e-10, 5.4928817e-10, 5.5587696e-10, + 5.624219e-10, 5.6892646e-10, 5.753941e-10, 5.818282e-10, + 5.882317e-10, 5.946077e-10, 6.00959e-10, 6.072884e-10, + 6.135985e-10, 6.19892e-10, 6.2617134e-10, 6.3243905e-10, + 6.386974e-10, 6.449488e-10, 6.511956e-10, 6.5744005e-10, + 6.6368433e-10, 6.699307e-10, 6.7618144e-10, 6.824387e-10, + 6.8870465e-10, 6.949815e-10, 7.012715e-10, 7.075768e-10, + 7.1389966e-10, 7.202424e-10, 7.266073e-10, 7.329966e-10, + 7.394128e-10, 7.4585826e-10, 7.5233547e-10, 7.58847e-10, + 7.653954e-10, 7.719835e-10, 7.7861395e-10, 7.852897e-10, + 7.920138e-10, 7.987892e-10, 8.0561924e-10, 8.125073e-10, + 8.194569e-10, 8.2647167e-10, 8.3355556e-10, 8.407127e-10, + 8.479473e-10, 8.55264e-10, 8.6266755e-10, 8.7016316e-10, + 8.777562e-10, 8.8545243e-10, 8.932582e-10, 9.0117996e-10, + 9.09225e-10, 9.174008e-10, 9.2571584e-10, 9.341788e-10, + 9.427997e-10, 9.515889e-10, 9.605579e-10, 9.697193e-10, + 9.790869e-10, 9.88676e-10, 9.985036e-10, 1.0085882e-09, + 1.0189509e-09, 1.0296151e-09, 1.0406069e-09, 1.0519566e-09, + 1.063698e-09, 1.0758702e-09, 1.0885183e-09, 1.1016947e-09, + 1.1154611e-09, 1.1298902e-09, 1.1450696e-09, 1.1611052e-09, + 1.1781276e-09, 1.1962995e-09, 1.2158287e-09, 1.2369856e-09, + 1.2601323e-09, 1.2857697e-09, 1.3146202e-09, 1.347784e-09, + 1.3870636e-09, 1.4357403e-09, 1.5008659e-09, 1.6030948e-09, +} +var fn = [128]float32{ + 1, 0.9635997, 0.9362827, 0.9130436, 0.89228165, 0.87324303, + 0.8555006, 0.8387836, 0.8229072, 0.8077383, 0.793177, + 0.7791461, 0.7655842, 0.7524416, 0.73967725, 0.7272569, + 0.7151515, 0.7033361, 0.69178915, 0.68049186, 0.6694277, + 0.658582, 0.6479418, 0.63749546, 0.6272325, 0.6171434, + 0.6072195, 0.5974532, 0.58783704, 0.5783647, 0.56903, + 0.5598274, 0.5507518, 0.54179835, 0.5329627, 0.52424055, + 0.5156282, 0.50712204, 0.49871865, 0.49041483, 0.48220766, + 0.4740943, 0.46607214, 0.4581387, 0.45029163, 0.44252872, + 0.43484783, 0.427247, 0.41972435, 0.41227803, 0.40490642, + 0.39760786, 0.3903808, 0.3832238, 0.37613547, 0.36911446, + 0.3621595, 0.35526937, 0.34844297, 0.34167916, 0.33497685, + 0.3283351, 0.3217529, 0.3152294, 0.30876362, 0.30235484, + 0.29600215, 0.28970486, 0.2834622, 0.2772735, 0.27113807, + 0.2650553, 0.25902456, 0.2530453, 0.24711695, 0.241239, + 0.23541094, 0.22963232, 0.2239027, 0.21822165, 0.21258877, + 0.20700371, 0.20146611, 0.19597565, 0.19053204, 0.18513499, + 0.17978427, 0.17447963, 0.1692209, 0.16400786, 0.15884037, + 0.15371831, 0.14864157, 0.14361008, 0.13862377, 0.13368265, + 0.12878671, 0.12393598, 0.119130544, 0.11437051, 0.10965602, + 0.104987256, 0.10036444, 0.095787846, 0.0912578, 0.08677467, + 0.0823389, 0.077950984, 0.073611505, 0.06932112, 0.06508058, + 0.06089077, 0.056752663, 0.0526674, 0.048636295, 0.044660863, + 0.040742867, 0.03688439, 0.033087887, 0.029356318, + 0.025693292, 0.022103304, 0.018592102, 0.015167298, + 0.011839478, 0.008624485, 0.005548995, 0.0026696292, +} diff --git a/internal/lib/math/rand/rand.go b/internal/lib/math/rand/rand.go new file mode 100644 index 00000000..d5e80f89 --- /dev/null +++ b/internal/lib/math/rand/rand.go @@ -0,0 +1,547 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package rand implements pseudo-random number generators suitable for tasks +// such as simulation, but it should not be used for security-sensitive work. +// +// Random numbers are generated by a [Source], usually wrapped in a [Rand]. +// Both types should be used by a single goroutine at a time: sharing among +// multiple goroutines requires some kind of synchronization. +// +// Top-level functions, such as [Float64] and [Int], +// are safe for concurrent use by multiple goroutines. +// +// This package's outputs might be easily predictable regardless of how it's +// seeded. For random numbers suitable for security-sensitive work, see the +// crypto/rand package. +package rand + +// llgo:skipall +import ( + "sync" + "sync/atomic" + _ "unsafe" // for go:linkname + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/math/rand" + "github.com/goplus/llgo/c/time" +) + +// A Source represents a source of uniformly-distributed +// pseudo-random int64 values in the range [0, 1<<63). +// +// A Source is not safe for concurrent use by multiple goroutines. +type Source interface { + Int63() int64 + Seed(seed int64) +} + +// A Source64 is a Source that can also generate +// uniformly-distributed pseudo-random uint64 values in +// the range [0, 1<<64) directly. +// If a Rand r's underlying Source s implements Source64, +// then r.Uint64 returns the result of one call to s.Uint64 +// instead of making two calls to s.Int63. +type Source64 interface { + Source + Uint64() uint64 +} + +// NewSource returns a new pseudo-random Source seeded with the given value. +// Unlike the default Source used by top-level functions, this source is not +// safe for concurrent use by multiple goroutines. +// The returned Source implements Source64. +func NewSource(seed int64) Source { + return newSource(seed) +} + +func newSource(seed int64) *rngSource { + var rng rngSource + rng.Seed(seed) + return &rng +} + +// A Rand is a source of random numbers. +type Rand struct { + src Source + s64 Source64 // non-nil if src is source64 + + // readVal contains remainder of 63-bit integer used for bytes + // generation during most recent Read call. + // It is saved so next Read call can start where the previous + // one finished. + readVal int64 + // readPos indicates the number of low-order bytes of readVal + // that are still valid. + readPos int8 +} + +// New returns a new Rand that uses random values from src +// to generate other random values. +func New(src Source) *Rand { + s64, _ := src.(Source64) + return &Rand{src: src, s64: s64} +} + +// Seed uses the provided seed value to initialize the generator to a deterministic state. +// Seed should not be called concurrently with any other Rand method. +func (r *Rand) Seed(seed int64) { + if lk, ok := r.src.(*lockedSource); ok { + lk.seedPos(seed, &r.readPos) + return + } + + r.src.Seed(seed) + r.readPos = 0 +} + +// Int63 returns a non-negative pseudo-random 63-bit integer as an int64. +func (r *Rand) Int63() int64 { return r.src.Int63() } + +// Uint32 returns a pseudo-random 32-bit value as a uint32. +func (r *Rand) Uint32() uint32 { return uint32(r.Int63() >> 31) } + +// Uint64 returns a pseudo-random 64-bit value as a uint64. +func (r *Rand) Uint64() uint64 { + if r.s64 != nil { + return r.s64.Uint64() + } + return uint64(r.Int63())>>31 | uint64(r.Int63())<<32 +} + +// Int31 returns a non-negative pseudo-random 31-bit integer as an int32. +func (r *Rand) Int31() int32 { return int32(r.Int63() >> 32) } + +// Int returns a non-negative pseudo-random int. +func (r *Rand) Int() int { + u := uint(r.Int63()) + return int(u << 1 >> 1) // clear sign bit if int == int32 +} + +// Int63n returns, as an int64, a non-negative pseudo-random number in the half-open interval [0,n). +// It panics if n <= 0. +func (r *Rand) Int63n(n int64) int64 { + if n <= 0 { + panic("invalid argument to Int63n") + } + if n&(n-1) == 0 { // n is power of two, can mask + return r.Int63() & (n - 1) + } + max := int64((1 << 63) - 1 - (1<<63)%uint64(n)) + v := r.Int63() + for v > max { + v = r.Int63() + } + return v % n +} + +// Int31n returns, as an int32, a non-negative pseudo-random number in the half-open interval [0,n). +// It panics if n <= 0. +func (r *Rand) Int31n(n int32) int32 { + if n <= 0 { + panic("invalid argument to Int31n") + } + if n&(n-1) == 0 { // n is power of two, can mask + return r.Int31() & (n - 1) + } + max := int32((1 << 31) - 1 - (1<<31)%uint32(n)) + v := r.Int31() + for v > max { + v = r.Int31() + } + return v % n +} + +// int31n returns, as an int32, a non-negative pseudo-random number in the half-open interval [0,n). +// n must be > 0, but int31n does not check this; the caller must ensure it. +// int31n exists because Int31n is inefficient, but Go 1 compatibility +// requires that the stream of values produced by math/rand remain unchanged. +// int31n can thus only be used internally, by newly introduced APIs. +// +// For implementation details, see: +// https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction +// https://lemire.me/blog/2016/06/30/fast-random-shuffling +func (r *Rand) int31n(n int32) int32 { + v := r.Uint32() + prod := uint64(v) * uint64(n) + low := uint32(prod) + if low < uint32(n) { + thresh := uint32(-n) % uint32(n) + for low < thresh { + v = r.Uint32() + prod = uint64(v) * uint64(n) + low = uint32(prod) + } + } + return int32(prod >> 32) +} + +// Intn returns, as an int, a non-negative pseudo-random number in the half-open interval [0,n). +// It panics if n <= 0. +func (r *Rand) Intn(n int) int { + if n <= 0 { + panic("invalid argument to Intn") + } + if n <= 1<<31-1 { + return int(r.Int31n(int32(n))) + } + return int(r.Int63n(int64(n))) +} + +// Float64 returns, as a float64, a pseudo-random number in the half-open interval [0.0,1.0). +func (r *Rand) Float64() float64 { + // A clearer, simpler implementation would be: + // return float64(r.Int63n(1<<53)) / (1<<53) + // However, Go 1 shipped with + // return float64(r.Int63()) / (1 << 63) + // and we want to preserve that value stream. + // + // There is one bug in the value stream: r.Int63() may be so close + // to 1<<63 that the division rounds up to 1.0, and we've guaranteed + // that the result is always less than 1.0. + // + // We tried to fix this by mapping 1.0 back to 0.0, but since float64 + // values near 0 are much denser than near 1, mapping 1 to 0 caused + // a theoretically significant overshoot in the probability of returning 0. + // Instead of that, if we round up to 1, just try again. + // Getting 1 only happens 1/2⁵³ of the time, so most clients + // will not observe it anyway. +again: + f := float64(r.Int63()) / (1 << 63) + if f == 1 { + goto again // resample; this branch is taken O(never) + } + return f +} + +// Float32 returns, as a float32, a pseudo-random number in the half-open interval [0.0,1.0). +func (r *Rand) Float32() float32 { + // Same rationale as in Float64: we want to preserve the Go 1 value + // stream except we want to fix it not to return 1.0 + // This only happens 1/2²⁴ of the time (plus the 1/2⁵³ of the time in Float64). +again: + f := float32(r.Float64()) + if f == 1 { + goto again // resample; this branch is taken O(very rarely) + } + return f +} + +// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers +// in the half-open interval [0,n). +func (r *Rand) Perm(n int) []int { + m := make([]int, n) + // In the following loop, the iteration when i=0 always swaps m[0] with m[0]. + // A change to remove this useless iteration is to assign 1 to i in the init + // statement. But Perm also effects r. Making this change will affect + // the final state of r. So this change can't be made for compatibility + // reasons for Go 1. + for i := 0; i < n; i++ { + j := r.Intn(i + 1) + m[i] = m[j] + m[j] = i + } + return m +} + +// Shuffle pseudo-randomizes the order of elements. +// n is the number of elements. Shuffle panics if n < 0. +// swap swaps the elements with indexes i and j. +func (r *Rand) Shuffle(n int, swap func(i, j int)) { + if n < 0 { + panic("invalid argument to Shuffle") + } + + // Fisher-Yates shuffle: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle + // Shuffle really ought not be called with n that doesn't fit in 32 bits. + // Not only will it take a very long time, but with 2³¹! possible permutations, + // there's no way that any PRNG can have a big enough internal state to + // generate even a minuscule percentage of the possible permutations. + // Nevertheless, the right API signature accepts an int n, so handle it as best we can. + i := n - 1 + for ; i > 1<<31-1-1; i-- { + j := int(r.Int63n(int64(i + 1))) + swap(i, j) + } + for ; i > 0; i-- { + j := int(r.int31n(int32(i + 1))) + swap(i, j) + } +} + +// Read generates len(p) random bytes and writes them into p. It +// always returns len(p) and a nil error. +// Read should not be called concurrently with any other Rand method. +func (r *Rand) Read(p []byte) (n int, err error) { + switch src := r.src.(type) { + case *lockedSource: + return src.read(p, &r.readVal, &r.readPos) + case *fastSource: + return src.read(p, &r.readVal, &r.readPos) + } + return read(p, r.src, &r.readVal, &r.readPos) +} + +func read(p []byte, src Source, readVal *int64, readPos *int8) (n int, err error) { + pos := *readPos + val := *readVal + rng, _ := src.(*rngSource) + for n = 0; n < len(p); n++ { + if pos == 0 { + if rng != nil { + val = rng.Int63() + } else { + val = src.Int63() + } + pos = 7 + } + p[n] = byte(val) + val >>= 8 + pos-- + } + *readPos = pos + *readVal = val + return +} + +/* + * Top-level convenience functions + */ + +// globalRandGenerator is the source of random numbers for the top-level +// convenience functions. When possible it uses the runtime fastrand64 +// function to avoid locking. This is not possible if the user called Seed, +// either explicitly or implicitly via GODEBUG=randautoseed=0. +var globalRandGenerator atomic.Pointer[Rand] + +// globalRand returns the generator to use for the top-level convenience +// functions. +func globalRand() *Rand { + if r := globalRandGenerator.Load(); r != nil { + return r + } + + // This is the first call. Initialize based on GODEBUG. + var r = &Rand{ + src: &fastSource{}, + s64: &fastSource{}, + } + if !globalRandGenerator.CompareAndSwap(nil, r) { + // Two different goroutines called some top-level + // function at the same time. While the results in + // that case are unpredictable, if we just use r here, + // and we are using a seed, we will most likely return + // the same value for both calls. That doesn't seem ideal. + // Just use the first one to get in. + return globalRandGenerator.Load() + } + return r +} + +func fastrand64() uint64 { + v1 := uint64(rand.Random()) + v2 := uint64(rand.Random()) + return v1 ^ (v2 << 32) +} + +func init() { + rand.Srandom(c.Uint(time.Time(nil))) +} + +// fastSource is an implementation of Source64 that uses the runtime +// fastrand functions. +type fastSource struct { + // The mutex is used to avoid race conditions in Read. + mu sync.Mutex +} + +func (*fastSource) Int63() int64 { + return int64(fastrand64() & rngMask) +} + +func (*fastSource) Seed(int64) { + panic("internal error: call to fastSource.Seed") +} + +func (*fastSource) Uint64() uint64 { + return fastrand64() +} + +func (fs *fastSource) read(p []byte, readVal *int64, readPos *int8) (n int, err error) { + fs.mu.Lock() + n, err = read(p, fs, readVal, readPos) + fs.mu.Unlock() + return +} + +// Seed uses the provided seed value to initialize the default Source to a +// deterministic state. Seed values that have the same remainder when +// divided by 2³¹-1 generate the same pseudo-random sequence. +// Seed, unlike the Rand.Seed method, is safe for concurrent use. +// +// If Seed is not called, the generator is seeded randomly at program startup. +// +// Prior to Go 1.20, the generator was seeded like Seed(1) at program startup. +// To force the old behavior, call Seed(1) at program startup. +// Alternately, set GODEBUG=randautoseed=0 in the environment +// before making any calls to functions in this package. +// +// Deprecated: As of Go 1.20 there is no reason to call Seed with +// a random value. Programs that call Seed with a known value to get +// a specific sequence of results should use New(NewSource(seed)) to +// obtain a local random generator. +func Seed(seed int64) { + orig := globalRandGenerator.Load() + + // If we are already using a lockedSource, we can just re-seed it. + if orig != nil { + if _, ok := orig.src.(*lockedSource); ok { + orig.Seed(seed) + return + } + } + + // Otherwise either + // 1) orig == nil, which is the normal case when Seed is the first + // top-level function to be called, or + // 2) orig is already a fastSource, in which case we need to change + // to a lockedSource. + // Either way we do the same thing. + + r := New(new(lockedSource)) + r.Seed(seed) + + if !globalRandGenerator.CompareAndSwap(orig, r) { + // Something changed underfoot. Retry to be safe. + Seed(seed) + } +} + +// Int63 returns a non-negative pseudo-random 63-bit integer as an int64 +// from the default Source. +func Int63() int64 { return globalRand().Int63() } + +// Uint32 returns a pseudo-random 32-bit value as a uint32 +// from the default Source. +func Uint32() uint32 { return globalRand().Uint32() } + +// Uint64 returns a pseudo-random 64-bit value as a uint64 +// from the default Source. +func Uint64() uint64 { return globalRand().Uint64() } + +// Int31 returns a non-negative pseudo-random 31-bit integer as an int32 +// from the default Source. +func Int31() int32 { return globalRand().Int31() } + +// Int returns a non-negative pseudo-random int from the default Source. +func Int() int { return globalRand().Int() } + +// Int63n returns, as an int64, a non-negative pseudo-random number in the half-open interval [0,n) +// from the default Source. +// It panics if n <= 0. +func Int63n(n int64) int64 { return globalRand().Int63n(n) } + +// Int31n returns, as an int32, a non-negative pseudo-random number in the half-open interval [0,n) +// from the default Source. +// It panics if n <= 0. +func Int31n(n int32) int32 { return globalRand().Int31n(n) } + +// Intn returns, as an int, a non-negative pseudo-random number in the half-open interval [0,n) +// from the default Source. +// It panics if n <= 0. +func Intn(n int) int { return globalRand().Intn(n) } + +// Float64 returns, as a float64, a pseudo-random number in the half-open interval [0.0,1.0) +// from the default Source. +func Float64() float64 { return globalRand().Float64() } + +// Float32 returns, as a float32, a pseudo-random number in the half-open interval [0.0,1.0) +// from the default Source. +func Float32() float32 { return globalRand().Float32() } + +// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers +// in the half-open interval [0,n) from the default Source. +func Perm(n int) []int { return globalRand().Perm(n) } + +// Shuffle pseudo-randomizes the order of elements using the default Source. +// n is the number of elements. Shuffle panics if n < 0. +// swap swaps the elements with indexes i and j. +func Shuffle(n int, swap func(i, j int)) { globalRand().Shuffle(n, swap) } + +// Read generates len(p) random bytes from the default Source and +// writes them into p. It always returns len(p) and a nil error. +// Read, unlike the Rand.Read method, is safe for concurrent use. +// +// Deprecated: For almost all use cases, crypto/rand.Read is more appropriate. +func Read(p []byte) (n int, err error) { return globalRand().Read(p) } + +// NormFloat64 returns a normally distributed float64 in the range +// [-math.MaxFloat64, +math.MaxFloat64] with +// standard normal distribution (mean = 0, stddev = 1) +// from the default Source. +// To produce a different normal distribution, callers can +// adjust the output using: +// +// sample = NormFloat64() * desiredStdDev + desiredMean +func NormFloat64() float64 { return globalRand().NormFloat64() } + +// ExpFloat64 returns an exponentially distributed float64 in the range +// (0, +math.MaxFloat64] with an exponential distribution whose rate parameter +// (lambda) is 1 and whose mean is 1/lambda (1) from the default Source. +// To produce a distribution with a different rate parameter, +// callers can adjust the output using: +// +// sample = ExpFloat64() / desiredRateParameter +func ExpFloat64() float64 { return globalRand().ExpFloat64() } + +type lockedSource struct { + lk sync.Mutex + s *rngSource +} + +func (r *lockedSource) Int63() (n int64) { + r.lk.Lock() + n = r.s.Int63() + r.lk.Unlock() + return +} + +func (r *lockedSource) Uint64() (n uint64) { + r.lk.Lock() + n = r.s.Uint64() + r.lk.Unlock() + return +} + +func (r *lockedSource) Seed(seed int64) { + r.lk.Lock() + r.seed(seed) + r.lk.Unlock() +} + +// seedPos implements Seed for a lockedSource without a race condition. +func (r *lockedSource) seedPos(seed int64, readPos *int8) { + r.lk.Lock() + r.seed(seed) + *readPos = 0 + r.lk.Unlock() +} + +// seed seeds the underlying source. +// The caller must have locked r.lk. +func (r *lockedSource) seed(seed int64) { + if r.s == nil { + r.s = newSource(seed) + } else { + r.s.Seed(seed) + } +} + +// read implements Read for a lockedSource without a race condition. +func (r *lockedSource) read(p []byte, readVal *int64, readPos *int8) (n int, err error) { + r.lk.Lock() + n, err = read(p, r.s, readVal, readPos) + r.lk.Unlock() + return +} diff --git a/internal/lib/math/rand/rng.go b/internal/lib/math/rand/rng.go new file mode 100644 index 00000000..1e4a9e01 --- /dev/null +++ b/internal/lib/math/rand/rng.go @@ -0,0 +1,252 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rand + +/* + * Uniform distribution + * + * algorithm by + * DP Mitchell and JA Reeds + */ + +const ( + rngLen = 607 + rngTap = 273 + rngMax = 1 << 63 + rngMask = rngMax - 1 + int32max = (1 << 31) - 1 +) + +var ( + // rngCooked used for seeding. See gen_cooked.go for details. + rngCooked [rngLen]int64 = [...]int64{ + -4181792142133755926, -4576982950128230565, 1395769623340756751, 5333664234075297259, + -6347679516498800754, 9033628115061424579, 7143218595135194537, 4812947590706362721, + 7937252194349799378, 5307299880338848416, 8209348851763925077, -7107630437535961764, + 4593015457530856296, 8140875735541888011, -5903942795589686782, -603556388664454774, + -7496297993371156308, 113108499721038619, 4569519971459345583, -4160538177779461077, + -6835753265595711384, -6507240692498089696, 6559392774825876886, 7650093201692370310, + 7684323884043752161, -8965504200858744418, -2629915517445760644, 271327514973697897, + -6433985589514657524, 1065192797246149621, 3344507881999356393, -4763574095074709175, + 7465081662728599889, 1014950805555097187, -4773931307508785033, -5742262670416273165, + 2418672789110888383, 5796562887576294778, 4484266064449540171, 3738982361971787048, + -4699774852342421385, 10530508058128498, -589538253572429690, -6598062107225984180, + 8660405965245884302, 10162832508971942, -2682657355892958417, 7031802312784620857, + 6240911277345944669, 831864355460801054, -1218937899312622917, 2116287251661052151, + 2202309800992166967, 9161020366945053561, 4069299552407763864, 4936383537992622449, + 457351505131524928, -8881176990926596454, -6375600354038175299, -7155351920868399290, + 4368649989588021065, 887231587095185257, -3659780529968199312, -2407146836602825512, + 5616972787034086048, -751562733459939242, 1686575021641186857, -5177887698780513806, + -4979215821652996885, -1375154703071198421, 5632136521049761902, -8390088894796940536, + -193645528485698615, -5979788902190688516, -4907000935050298721, -285522056888777828, + -2776431630044341707, 1679342092332374735, 6050638460742422078, -2229851317345194226, + -1582494184340482199, 5881353426285907985, 812786550756860885, 4541845584483343330, + -6497901820577766722, 4980675660146853729, -4012602956251539747, -329088717864244987, + -2896929232104691526, 1495812843684243920, -2153620458055647789, 7370257291860230865, + -2466442761497833547, 4706794511633873654, -1398851569026877145, 8549875090542453214, + -9189721207376179652, -7894453601103453165, 7297902601803624459, 1011190183918857495, + -6985347000036920864, 5147159997473910359, -8326859945294252826, 2659470849286379941, + 6097729358393448602, -7491646050550022124, -5117116194870963097, -896216826133240300, + -745860416168701406, 5803876044675762232, -787954255994554146, -3234519180203704564, + -4507534739750823898, -1657200065590290694, 505808562678895611, -4153273856159712438, + -8381261370078904295, 572156825025677802, 1791881013492340891, 3393267094866038768, + -5444650186382539299, 2352769483186201278, -7930912453007408350, -325464993179687389, + -3441562999710612272, -6489413242825283295, 5092019688680754699, -227247482082248967, + 4234737173186232084, 5027558287275472836, 4635198586344772304, -536033143587636457, + 5907508150730407386, -8438615781380831356, 972392927514829904, -3801314342046600696, + -4064951393885491917, -174840358296132583, 2407211146698877100, -1640089820333676239, + 3940796514530962282, -5882197405809569433, 3095313889586102949, -1818050141166537098, + 5832080132947175283, 7890064875145919662, 8184139210799583195, -8073512175445549678, + -7758774793014564506, -4581724029666783935, 3516491885471466898, -8267083515063118116, + 6657089965014657519, 5220884358887979358, 1796677326474620641, 5340761970648932916, + 1147977171614181568, 5066037465548252321, 2574765911837859848, 1085848279845204775, + -5873264506986385449, 6116438694366558490, 2107701075971293812, -7420077970933506541, + 2469478054175558874, -1855128755834809824, -5431463669011098282, -9038325065738319171, + -6966276280341336160, 7217693971077460129, -8314322083775271549, 7196649268545224266, + -3585711691453906209, -5267827091426810625, 8057528650917418961, -5084103596553648165, + -2601445448341207749, -7850010900052094367, 6527366231383600011, 3507654575162700890, + 9202058512774729859, 1954818376891585542, -2582991129724600103, 8299563319178235687, + -5321504681635821435, 7046310742295574065, -2376176645520785576, -7650733936335907755, + 8850422670118399721, 3631909142291992901, 5158881091950831288, -6340413719511654215, + 4763258931815816403, 6280052734341785344, -4979582628649810958, 2043464728020827976, + -2678071570832690343, 4562580375758598164, 5495451168795427352, -7485059175264624713, + 553004618757816492, 6895160632757959823, -989748114590090637, 7139506338801360852, + -672480814466784139, 5535668688139305547, 2430933853350256242, -3821430778991574732, + -1063731997747047009, -3065878205254005442, 7632066283658143750, 6308328381617103346, + 3681878764086140361, 3289686137190109749, 6587997200611086848, 244714774258135476, + -5143583659437639708, 8090302575944624335, 2945117363431356361, -8359047641006034763, + 3009039260312620700, -793344576772241777, 401084700045993341, -1968749590416080887, + 4707864159563588614, -3583123505891281857, -3240864324164777915, -5908273794572565703, + -3719524458082857382, -5281400669679581926, 8118566580304798074, 3839261274019871296, + 7062410411742090847, -8481991033874568140, 6027994129690250817, -6725542042704711878, + -2971981702428546974, -7854441788951256975, 8809096399316380241, 6492004350391900708, + 2462145737463489636, -8818543617934476634, -5070345602623085213, -8961586321599299868, + -3758656652254704451, -8630661632476012791, 6764129236657751224, -709716318315418359, + -3403028373052861600, -8838073512170985897, -3999237033416576341, -2920240395515973663, + -2073249475545404416, 368107899140673753, -6108185202296464250, -6307735683270494757, + 4782583894627718279, 6718292300699989587, 8387085186914375220, 3387513132024756289, + 4654329375432538231, -292704475491394206, -3848998599978456535, 7623042350483453954, + 7725442901813263321, 9186225467561587250, -5132344747257272453, -6865740430362196008, + 2530936820058611833, 1636551876240043639, -3658707362519810009, 1452244145334316253, + -7161729655835084979, -7943791770359481772, 9108481583171221009, -3200093350120725999, + 5007630032676973346, 2153168792952589781, 6720334534964750538, -3181825545719981703, + 3433922409283786309, 2285479922797300912, 3110614940896576130, -2856812446131932915, + -3804580617188639299, 7163298419643543757, 4891138053923696990, 580618510277907015, + 1684034065251686769, 4429514767357295841, -8893025458299325803, -8103734041042601133, + 7177515271653460134, 4589042248470800257, -1530083407795771245, 143607045258444228, + 246994305896273627, -8356954712051676521, 6473547110565816071, 3092379936208876896, + 2058427839513754051, -4089587328327907870, 8785882556301281247, -3074039370013608197, + -637529855400303673, 6137678347805511274, -7152924852417805802, 5708223427705576541, + -3223714144396531304, 4358391411789012426, 325123008708389849, 6837621693887290924, + 4843721905315627004, -3212720814705499393, -3825019837890901156, 4602025990114250980, + 1044646352569048800, 9106614159853161675, -8394115921626182539, -4304087667751778808, + 2681532557646850893, 3681559472488511871, -3915372517896561773, -2889241648411946534, + -6564663803938238204, -8060058171802589521, 581945337509520675, 3648778920718647903, + -4799698790548231394, -7602572252857820065, 220828013409515943, -1072987336855386047, + 4287360518296753003, -4633371852008891965, 5513660857261085186, -2258542936462001533, + -8744380348503999773, 8746140185685648781, 228500091334420247, 1356187007457302238, + 3019253992034194581, 3152601605678500003, -8793219284148773595, 5559581553696971176, + 4916432985369275664, -8559797105120221417, -5802598197927043732, 2868348622579915573, + -7224052902810357288, -5894682518218493085, 2587672709781371173, -7706116723325376475, + 3092343956317362483, -5561119517847711700, 972445599196498113, -1558506600978816441, + 1708913533482282562, -2305554874185907314, -6005743014309462908, -6653329009633068701, + -483583197311151195, 2488075924621352812, -4529369641467339140, -4663743555056261452, + 2997203966153298104, 1282559373026354493, 240113143146674385, 8665713329246516443, + 628141331766346752, -4651421219668005332, -7750560848702540400, 7596648026010355826, + -3132152619100351065, 7834161864828164065, 7103445518877254909, 4390861237357459201, + -4780718172614204074, -319889632007444440, 622261699494173647, -3186110786557562560, + -8718967088789066690, -1948156510637662747, -8212195255998774408, -7028621931231314745, + 2623071828615234808, -4066058308780939700, -5484966924888173764, -6683604512778046238, + -6756087640505506466, 5256026990536851868, 7841086888628396109, 6640857538655893162, + -8021284697816458310, -7109857044414059830, -1689021141511844405, -4298087301956291063, + -4077748265377282003, -998231156719803476, 2719520354384050532, 9132346697815513771, + 4332154495710163773, -2085582442760428892, 6994721091344268833, -2556143461985726874, + -8567931991128098309, 59934747298466858, -3098398008776739403, -265597256199410390, + 2332206071942466437, -7522315324568406181, 3154897383618636503, -7585605855467168281, + -6762850759087199275, 197309393502684135, -8579694182469508493, 2543179307861934850, + 4350769010207485119, -4468719947444108136, -7207776534213261296, -1224312577878317200, + 4287946071480840813, 8362686366770308971, 6486469209321732151, -5605644191012979782, + -1669018511020473564, 4450022655153542367, -7618176296641240059, -3896357471549267421, + -4596796223304447488, -6531150016257070659, -8982326463137525940, -4125325062227681798, + -1306489741394045544, -8338554946557245229, 5329160409530630596, 7790979528857726136, + 4955070238059373407, -4304834761432101506, -6215295852904371179, 3007769226071157901, + -6753025801236972788, 8928702772696731736, 7856187920214445904, -4748497451462800923, + 7900176660600710914, -7082800908938549136, -6797926979589575837, -6737316883512927978, + 4186670094382025798, 1883939007446035042, -414705992779907823, 3734134241178479257, + 4065968871360089196, 6953124200385847784, -7917685222115876751, -7585632937840318161, + -5567246375906782599, -5256612402221608788, 3106378204088556331, -2894472214076325998, + 4565385105440252958, 1979884289539493806, -6891578849933910383, 3783206694208922581, + 8464961209802336085, 2843963751609577687, 3030678195484896323, -4429654462759003204, + 4459239494808162889, 402587895800087237, 8057891408711167515, 4541888170938985079, + 1042662272908816815, -3666068979732206850, 2647678726283249984, 2144477441549833761, + -3417019821499388721, -2105601033380872185, 5916597177708541638, -8760774321402454447, + 8833658097025758785, 5970273481425315300, 563813119381731307, -6455022486202078793, + 1598828206250873866, -4016978389451217698, -2988328551145513985, -6071154634840136312, + 8469693267274066490, 125672920241807416, -3912292412830714870, -2559617104544284221, + -486523741806024092, -4735332261862713930, 5923302823487327109, -9082480245771672572, + -1808429243461201518, 7990420780896957397, 4317817392807076702, 3625184369705367340, + -6482649271566653105, -3480272027152017464, -3225473396345736649, -368878695502291645, + -3981164001421868007, -8522033136963788610, 7609280429197514109, 3020985755112334161, + -2572049329799262942, 2635195723621160615, 5144520864246028816, -8188285521126945980, + 1567242097116389047, 8172389260191636581, -2885551685425483535, -7060359469858316883, + -6480181133964513127, -7317004403633452381, 6011544915663598137, 5932255307352610768, + 2241128460406315459, -8327867140638080220, 3094483003111372717, 4583857460292963101, + 9079887171656594975, -384082854924064405, -3460631649611717935, 4225072055348026230, + -7385151438465742745, 3801620336801580414, -399845416774701952, -7446754431269675473, + 7899055018877642622, 5421679761463003041, 5521102963086275121, -4975092593295409910, + 8735487530905098534, -7462844945281082830, -2080886987197029914, -1000715163927557685, + -4253840471931071485, -5828896094657903328, 6424174453260338141, 359248545074932887, + -5949720754023045210, -2426265837057637212, 3030918217665093212, -9077771202237461772, + -3186796180789149575, 740416251634527158, -2142944401404840226, 6951781370868335478, + 399922722363687927, -8928469722407522623, -1378421100515597285, -8343051178220066766, + -3030716356046100229, -8811767350470065420, 9026808440365124461, 6440783557497587732, + 4615674634722404292, 539897290441580544, 2096238225866883852, 8751955639408182687, + -7316147128802486205, 7381039757301768559, 6157238513393239656, -1473377804940618233, + 8629571604380892756, 5280433031239081479, 7101611890139813254, 2479018537985767835, + 7169176924412769570, -1281305539061572506, -7865612307799218120, 2278447439451174845, + 3625338785743880657, 6477479539006708521, 8976185375579272206, -3712000482142939688, + 1326024180520890843, 7537449876596048829, 5464680203499696154, 3189671183162196045, + 6346751753565857109, -8982212049534145501, -6127578587196093755, -245039190118465649, + -6320577374581628592, 7208698530190629697, 7276901792339343736, -7490986807540332668, + 4133292154170828382, 2918308698224194548, -7703910638917631350, -3929437324238184044, + -4300543082831323144, -6344160503358350167, 5896236396443472108, -758328221503023383, + -1894351639983151068, -307900319840287220, -6278469401177312761, -2171292963361310674, + 8382142935188824023, 9103922860780351547, 4152330101494654406, + } +) + +type rngSource struct { + tap int // index into vec + feed int // index into vec + vec [rngLen]int64 // current feedback register +} + +// seed rng x[n+1] = 48271 * x[n] mod (2**31 - 1) +func seedrand(x int32) int32 { + const ( + A = 48271 + Q = 44488 + R = 3399 + ) + + hi := x / Q + lo := x % Q + x = A*lo - R*hi + if x < 0 { + x += int32max + } + return x +} + +// Seed uses the provided seed value to initialize the generator to a deterministic state. +func (rng *rngSource) Seed(seed int64) { + rng.tap = 0 + rng.feed = rngLen - rngTap + + seed = seed % int32max + if seed < 0 { + seed += int32max + } + if seed == 0 { + seed = 89482311 + } + + x := int32(seed) + for i := -20; i < rngLen; i++ { + x = seedrand(x) + if i >= 0 { + var u int64 + u = int64(x) << 40 + x = seedrand(x) + u ^= int64(x) << 20 + x = seedrand(x) + u ^= int64(x) + u ^= rngCooked[i] + rng.vec[i] = u + } + } +} + +// Int63 returns a non-negative pseudo-random 63-bit integer as an int64. +func (rng *rngSource) Int63() int64 { + return int64(rng.Uint64() & rngMask) +} + +// Uint64 returns a non-negative pseudo-random 64-bit integer as a uint64. +func (rng *rngSource) Uint64() uint64 { + rng.tap-- + if rng.tap < 0 { + rng.tap += rngLen + } + + rng.feed-- + if rng.feed < 0 { + rng.feed += rngLen + } + + x := rng.vec[rng.feed] + rng.vec[rng.tap] + rng.vec[rng.feed] = x + return uint64(x) +} diff --git a/internal/lib/math/rand/zipf.go b/internal/lib/math/rand/zipf.go new file mode 100644 index 00000000..f04c814e --- /dev/null +++ b/internal/lib/math/rand/zipf.go @@ -0,0 +1,77 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// W.Hormann, G.Derflinger: +// "Rejection-Inversion to Generate Variates +// from Monotone Discrete Distributions" +// http://eeyore.wu-wien.ac.at/papers/96-04-04.wh-der.ps.gz + +package rand + +import "math" + +// A Zipf generates Zipf distributed variates. +type Zipf struct { + r *Rand + imax float64 + v float64 + q float64 + s float64 + oneminusQ float64 + oneminusQinv float64 + hxm float64 + hx0minusHxm float64 +} + +func (z *Zipf) h(x float64) float64 { + return math.Exp(z.oneminusQ*math.Log(z.v+x)) * z.oneminusQinv +} + +func (z *Zipf) hinv(x float64) float64 { + return math.Exp(z.oneminusQinv*math.Log(z.oneminusQ*x)) - z.v +} + +// NewZipf returns a Zipf variate generator. +// The generator generates values k ∈ [0, imax] +// such that P(k) is proportional to (v + k) ** (-s). +// Requirements: s > 1 and v >= 1. +func NewZipf(r *Rand, s float64, v float64, imax uint64) *Zipf { + z := new(Zipf) + if s <= 1.0 || v < 1 { + return nil + } + z.r = r + z.imax = float64(imax) + z.v = v + z.q = s + z.oneminusQ = 1.0 - z.q + z.oneminusQinv = 1.0 / z.oneminusQ + z.hxm = z.h(z.imax + 0.5) + z.hx0minusHxm = z.h(0.5) - math.Exp(math.Log(z.v)*(-z.q)) - z.hxm + z.s = 1 - z.hinv(z.h(1.5)-math.Exp(-z.q*math.Log(z.v+1.0))) + return z +} + +// Uint64 returns a value drawn from the Zipf distribution described +// by the Zipf object. +func (z *Zipf) Uint64() uint64 { + if z == nil { + panic("rand: nil Zipf") + } + k := 0.0 + + for { + r := z.r.Float64() // r on [0,1] + ur := z.hxm + r*z.hx0minusHxm + x := z.hinv(ur) + k = math.Floor(x + 0.5) + if k-x <= z.s { + break + } + if ur >= z.h(k+0.5)-math.Exp(-math.Log(k+z.v)*z.q) { + break + } + } + return uint64(k) +} From ee3d11cb5314db1bcd1040b3db00aa556bbc3504 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Tue, 30 Jul 2024 21:56:19 +0800 Subject: [PATCH 122/152] README: c/openssl --- README.md | 1 + c/openssl/sha.go | 31 ------------------------------- c/openssl/sha1.go | 35 +++++++++++++++++++++++++++++++++++ c/openssl/sha256.go | 16 ++++++++++++++++ c/openssl/sha512.go | 16 ++++++++++++++++ 5 files changed, 68 insertions(+), 31 deletions(-) delete mode 100644 c/openssl/sha.go diff --git a/README.md b/README.md index f4b54e4a..514e7d10 100644 --- a/README.md +++ b/README.md @@ -218,6 +218,7 @@ The currently supported libraries include: * [c/llama2](https://pkg.go.dev/github.com/goplus/llgo/c/llama2) * [c/lua](https://pkg.go.dev/github.com/goplus/llgo/c/lua) * [c/neco](https://pkg.go.dev/github.com/goplus/llgo/c/neco) +* [c/openssl](https://pkg.go.dev/github.com/goplus/llgo/c/openssl) * [c/raylib](https://pkg.go.dev/github.com/goplus/llgo/c/raylib) * [c/sqlite](https://pkg.go.dev/github.com/goplus/llgo/c/sqlite) * [c/zlib](https://pkg.go.dev/github.com/goplus/llgo/c/zlib) diff --git a/c/openssl/sha.go b/c/openssl/sha.go deleted file mode 100644 index 3cf7d891..00000000 --- a/c/openssl/sha.go +++ /dev/null @@ -1,31 +0,0 @@ -package openssl - -import "github.com/goplus/llgo/c" - -const ( - SHA_DIGEST_LENGTH = 20 - SHA_LBLOCK = 16 - SHA_CBLOCK = (SHA_LBLOCK * 4) - SHA_LAST_BLOCK = (SHA_CBLOCK - 8) - - SHA256_CBLOCK = (SHA_LBLOCK * 4) - SHA256_192_DIGEST_LENGTH = 24 - SHA224_DIGEST_LENGTH = 28 - SHA256_DIGEST_LENGTH = 32 - SHA384_DIGEST_LENGTH = 48 - SHA512_DIGEST_LENGTH = 64 - SHA512_CBLOCK = (SHA_LBLOCK * 8) -) - -/* -# if (defined(_WIN32) || defined(_WIN64)) && !defined(__MINGW32__) -# define SHA_LONG64 unsigned __int64 -# elif defined(__arch64__) -# define SHA_LONG64 unsigned long -# else -# define SHA_LONG64 unsigned long long -# endif -*/ -type SHA_LONG64 = c.UlongLong - -type SHA_LONG = c.Uint diff --git a/c/openssl/sha1.go b/c/openssl/sha1.go index 96da515d..4ad88ca9 100644 --- a/c/openssl/sha1.go +++ b/c/openssl/sha1.go @@ -1,3 +1,19 @@ +/* + * 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 openssl import ( @@ -6,6 +22,25 @@ import ( "github.com/goplus/llgo/c" ) +const ( + SHA_DIGEST_LENGTH = 20 + SHA_LBLOCK = 16 + SHA_CBLOCK = (SHA_LBLOCK * 4) + SHA_LAST_BLOCK = (SHA_CBLOCK - 8) + + SHA256_CBLOCK = (SHA_LBLOCK * 4) + SHA256_192_DIGEST_LENGTH = 24 + SHA224_DIGEST_LENGTH = 28 + SHA256_DIGEST_LENGTH = 32 + SHA384_DIGEST_LENGTH = 48 + SHA512_DIGEST_LENGTH = 64 + SHA512_CBLOCK = (SHA_LBLOCK * 8) +) + +type SHA_LONG64 = c.UlongLong + +type SHA_LONG = c.Uint + type SHA_CTX struct { H0, H1, H2, H3, H4 SHA_LONG Nl, Nh SHA_LONG diff --git a/c/openssl/sha256.go b/c/openssl/sha256.go index 2934208d..f7bd9f82 100644 --- a/c/openssl/sha256.go +++ b/c/openssl/sha256.go @@ -1,3 +1,19 @@ +/* + * 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 openssl import ( diff --git a/c/openssl/sha512.go b/c/openssl/sha512.go index 85431af8..ec2e0739 100644 --- a/c/openssl/sha512.go +++ b/c/openssl/sha512.go @@ -1,3 +1,19 @@ +/* + * 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 openssl import ( From a3ff845a1481ee4faf1bb0c38e29d70d3dd1aa45 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Wed, 31 Jul 2024 01:27:08 +0800 Subject: [PATCH 123/152] c/openssl: rand --- _cmptest/_randcrypt/rand.go | 18 +++++ {_demo => c/openssl/_demo}/cmd5demo/md5.go | 0 c/openssl/_demo/cranddemo/rand.go | 17 +++++ {_demo => c/openssl/_demo}/cshademo/sha.go | 0 c/openssl/openssl.go | 2 + c/openssl/rand.go | 80 ++++++++++++++++++++++ 6 files changed, 117 insertions(+) create mode 100644 _cmptest/_randcrypt/rand.go rename {_demo => c/openssl/_demo}/cmd5demo/md5.go (100%) create mode 100644 c/openssl/_demo/cranddemo/rand.go rename {_demo => c/openssl/_demo}/cshademo/sha.go (100%) create mode 100644 c/openssl/rand.go diff --git a/_cmptest/_randcrypt/rand.go b/_cmptest/_randcrypt/rand.go new file mode 100644 index 00000000..c8d52e63 --- /dev/null +++ b/_cmptest/_randcrypt/rand.go @@ -0,0 +1,18 @@ +package main + +import ( + "crypto/rand" + "fmt" +) + +func main() { + c := 10 + b := make([]byte, c) + _, err := rand.Read(b) + if err != nil { + fmt.Println("error:", err) + return + } + // The slice should now contain random bytes instead of only zeroes. + fmt.Printf("%x\n", b) +} diff --git a/_demo/cmd5demo/md5.go b/c/openssl/_demo/cmd5demo/md5.go similarity index 100% rename from _demo/cmd5demo/md5.go rename to c/openssl/_demo/cmd5demo/md5.go diff --git a/c/openssl/_demo/cranddemo/rand.go b/c/openssl/_demo/cranddemo/rand.go new file mode 100644 index 00000000..4fc3a803 --- /dev/null +++ b/c/openssl/_demo/cranddemo/rand.go @@ -0,0 +1,17 @@ +package main + +import ( + "fmt" + + "github.com/goplus/llgo/c/openssl" +) + +func main() { + b := make([]byte, 10) + + openssl.RANDBytes(b) + fmt.Printf("%x\n", b) + + openssl.RANDPrivBytes(b) + fmt.Printf("%x\n", b) +} diff --git a/_demo/cshademo/sha.go b/c/openssl/_demo/cshademo/sha.go similarity index 100% rename from _demo/cshademo/sha.go rename to c/openssl/_demo/cshademo/sha.go diff --git a/c/openssl/openssl.go b/c/openssl/openssl.go index cde02ca5..491f61fe 100644 --- a/c/openssl/openssl.go +++ b/c/openssl/openssl.go @@ -16,6 +16,8 @@ package openssl +// ----------------------------------------------------------------------------- + const ( LLGoPackage = "link: $(pkg-config --libs openssl); -lssl -lcrypto" ) diff --git a/c/openssl/rand.go b/c/openssl/rand.go new file mode 100644 index 00000000..f9ad6338 --- /dev/null +++ b/c/openssl/rand.go @@ -0,0 +1,80 @@ +/* + * 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 openssl + +import ( + "unsafe" + + "github.com/goplus/llgo/c" +) + +// ----------------------------------------------------------------------------- + +// int RAND_bytes(unsigned char *buf, int num); +// +//go:linkname RANDBufferWithLen C.RAND_bytes +func RANDBufferWithLen(buf *byte, num c.Int) c.Int + +func RANDBytes(buf []byte) c.Int { + return RANDBufferWithLen(unsafe.SliceData(buf), c.Int(len(buf))) +} + +// int RAND_priv_bytes(unsigned char *buf, int num); +// +//go:linkname RANDPrivBufferWithLen C.RAND_priv_bytes +func RANDPrivBufferWithLen(buf *byte, num c.Int) c.Int + +func RANDPrivBytes(buf []byte) c.Int { + return RANDPrivBufferWithLen(unsafe.SliceData(buf), c.Int(len(buf))) +} + +// void RAND_seed(const void *buf, int num); +// +//go:linkname RANDSeed C.RAND_seed +func RANDSeed(buf unsafe.Pointer, num c.Int) + +// void RAND_keep_random_devices_open(int keep); +// +//go:linkname RANDKeepRandomDevicesOpen C.RAND_keep_random_devices_open +func RANDKeepRandomDevicesOpen(keep c.Int) + +// int RAND_load_file(const char *file, long max_bytes); +// +//go:linkname RANDLoadFile C.RAND_load_file +func RANDLoadFile(file *c.Char, maxBytes c.Long) c.Int + +// int RAND_write_file(const char *file); +// +//go:linkname RANDWriteFile C.RAND_write_file +func RANDWriteFile(file *c.Char) c.Int + +// const char *RAND_file_name(char *file, size_t num); +// +//go:linkname RANDFileName C.RAND_file_name +func RANDFileName(file *c.Char, num uintptr) *c.Char + +// int RAND_status(void); +// +//go:linkname RANDStatus C.RAND_status +func RANDStatus() c.Int + +// int RAND_poll(void); +// +//go:linkname RANDPoll C.RAND_poll +func RANDPoll() c.Int + +// ----------------------------------------------------------------------------- From 45b4315842312d5f5697bac81744fe782259d7c9 Mon Sep 17 00:00:00 2001 From: Aofei Sheng Date: Wed, 31 Jul 2024 08:56:03 +0800 Subject: [PATCH 124/152] deps: require zlib 1.2+ --- .github/workflows/go.yml | 42 +++++++++++++++++++++++++--------- .github/workflows/test_llgo.sh | 2 -- README.md | 15 +++++------- 3 files changed, 37 insertions(+), 22 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 7d43725a..9a412391 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -18,7 +18,7 @@ jobs: - macos-latest - ubuntu-24.04 llvm: [18] - runs-on: ${{ matrix.os }} + runs-on: ${{matrix.os}} steps: - uses: actions/checkout@v4 @@ -26,17 +26,37 @@ jobs: if: startsWith(matrix.os, 'macos') run: | brew update - brew install llvm@${{ matrix.llvm }} pkg-config bdw-gc openssl cjson sqlite python@3.12 - echo "$(brew --prefix llvm@${{ matrix.llvm }})/bin" >> $GITHUB_PATH + brew install llvm@${{matrix.llvm}} pkg-config bdw-gc openssl + echo "$(brew --prefix llvm@${{matrix.llvm}})/bin" >> $GITHUB_PATH + + # Install optional deps for demos. + # + # NOTE: Keep this list updated as new deps are introduced. + opt_deps=( + cjson # for github.com/goplus/llgo/c/cjson + sqlite # for github.com/goplus/llgo/c/sqlite + python@3.12 # for github.com/goplus/llgo/py + ) + brew install "${opt_deps[@]}" - name: Install dependencies if: startsWith(matrix.os, 'ubuntu') run: | - echo "deb http://apt.llvm.org/$(lsb_release -cs)/ llvm-toolchain-$(lsb_release -cs)-${{ matrix.llvm }} main" | sudo tee /etc/apt/sources.list.d/llvm.list + echo "deb http://apt.llvm.org/$(lsb_release -cs)/ llvm-toolchain-$(lsb_release -cs)-${{matrix.llvm}} main" | sudo tee /etc/apt/sources.list.d/llvm.list wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - sudo apt-get update - sudo apt-get install -y llvm-${{ matrix.llvm }}-dev clang-${{ matrix.llvm }} lld-${{ matrix.llvm }} pkg-config libgc-dev libssl-dev libcjson-dev libsqlite3-dev python3.12-dev - echo "/usr/lib/llvm-${{ matrix.llvm }}/bin" >> $GITHUB_PATH + sudo apt-get install -y llvm-${{matrix.llvm}}-dev clang-${{matrix.llvm}} lld-${{matrix.llvm}} pkg-config libgc-dev libssl-dev zlib1g-dev + echo "/usr/lib/llvm-${{matrix.llvm}}/bin" >> $GITHUB_PATH + + # Install optional deps for demos. + # + # NOTE: Keep this list updated as new deps are introduced. + opt_deps=( + libcjson-dev # for github.com/goplus/llgo/c/cjson + libsqlite3-dev # for github.com/goplus/llgo/c/sqlite + python3.12-dev # for github.com/goplus/llgo/py + ) + sudo apt-get install -y "${opt_deps[@]}" - name: Clang information run: | @@ -66,13 +86,13 @@ jobs: - name: LLGO tests if: matrix.os != 'ubuntu-latest' run: | - echo "Test result on ${{ matrix.os }} with LLVM ${{ matrix.llvm }}" > result.md - LLGOROOT=$PWD bash .github/workflows/test_llgo.sh + echo "Test result on ${{matrix.os}} with LLVM ${{matrix.llvm}}" > result.md + bash .github/workflows/test_llgo.sh - name: Test _demo and _pydemo run: | set +e - LLGOROOT=$PWD bash .github/workflows/test_demo.sh + bash .github/workflows/test_demo.sh exit 0 - name: Show test result @@ -83,10 +103,10 @@ jobs: if: false with: filePath: result.md - comment_tag: test-result-on-${{ matrix.os }}-with-llvm-${{ matrix.llvm }} + comment_tag: test-result-on-${{matrix.os}}-with-llvm-${{matrix.llvm}} - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4 with: - token: ${{ secrets.CODECOV_TOKEN }} + token: ${{secrets.CODECOV_TOKEN}} slug: goplus/llgo diff --git a/.github/workflows/test_llgo.sh b/.github/workflows/test_llgo.sh index 0ecff3a1..a1b97fe3 100644 --- a/.github/workflows/test_llgo.sh +++ b/.github/workflows/test_llgo.sh @@ -1,8 +1,6 @@ #!/bin/bash set -e -export LLGOROOT=$PWD - testcmd=/tmp/test llgo build -o $testcmd ./c/bdwgc/_test cases=$($testcmd) diff --git a/README.md b/README.md index 514e7d10..21838002 100644 --- a/README.md +++ b/README.md @@ -317,8 +317,7 @@ Here are the Go packages that can be imported correctly: - [pkg-config 0.29+](https://www.freedesktop.org/wiki/Software/pkg-config/) - [bdwgc/libgc 8.0+](https://www.hboehm.info/gc/) - [OpenSSL 3.0+](https://www.openssl.org/) -- [cJSON 1.7+](https://github.com/DaveGamble/cJSON) (optional, for [github.com/goplus/llgo/c/cjson](https://pkg.go.dev/github.com/goplus/llgo/c/cjson)) -- [SQLite 3](https://www.sqlite.org) (optional, for [github.com/goplus/llgo/c/sqlite](https://pkg.go.dev/github.com/goplus/llgo/c/sqlite)) +- [zlib 1.2+](https://www.zlib.net) - [Python 3.12+](https://www.python.org) (optional, for [github.com/goplus/llgo/py](https://pkg.go.dev/github.com/goplus/llgo/py)) ## How to install @@ -328,10 +327,9 @@ Follow these steps to generate the `llgo` command (its usage is the same as the ### on macOS ```sh -brew update # execute if needed +brew update brew install llvm@18 pkg-config bdw-gc openssl -brew install cjson sqlite python@3.12 # optional -export PATH=$(brew --prefix llvm@18)/bin:$PATH # you may want to add this to your shell RC file, e.g. ~/.zshrc +brew install python@3.12 # optional go install -v github.com/goplus/llgo/cmd/llgo@latest ``` @@ -340,10 +338,9 @@ go install -v github.com/goplus/llgo/cmd/llgo@latest ```sh echo "deb http://apt.llvm.org/$(lsb_release -cs)/ llvm-toolchain-$(lsb_release -cs)-18 main" | sudo tee /etc/apt/sources.list.d/llvm.list wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - -sudo apt-get update # execute if needed -sudo apt-get install -y llvm-18-dev clang-18 lld-18 pkg-config libgc-dev libssl-dev -sudo apt-get install -y libcjson-dev libsqlite3-dev python3.12-dev # optional -export PATH=/usr/lib/llvm-18/bin:$PATH # you may want to add this to your shell RC file, e.g. ~/.bashrc +sudo apt-get update +sudo apt-get install -y llvm-18-dev clang-18 lld-18 pkg-config libgc-dev libssl-dev zlib1g-dev +sudo apt-get install -y python3.12-dev # optional go install -v github.com/goplus/llgo/cmd/llgo@latest ``` From af6e4abe8458114c2d5c7e7f77a44947b546f6bc Mon Sep 17 00:00:00 2001 From: xushiwei Date: Wed, 31 Jul 2024 10:26:18 +0800 Subject: [PATCH 125/152] library: crypto/rand --- .../_randcrypt => _demo/randcrypt}/rand.go | 0 c/openssl/err.go | 156 ++++++++++++++++++ internal/build/build.go | 1 + internal/lib/crypto/rand/rand.go | 105 ++++++++++++ internal/lib/crypto/rand/util.go | 98 +++++++++++ 5 files changed, 360 insertions(+) rename {_cmptest/_randcrypt => _demo/randcrypt}/rand.go (100%) create mode 100644 c/openssl/err.go create mode 100644 internal/lib/crypto/rand/rand.go create mode 100644 internal/lib/crypto/rand/util.go diff --git a/_cmptest/_randcrypt/rand.go b/_demo/randcrypt/rand.go similarity index 100% rename from _cmptest/_randcrypt/rand.go rename to _demo/randcrypt/rand.go diff --git a/c/openssl/err.go b/c/openssl/err.go new file mode 100644 index 00000000..02fb7969 --- /dev/null +++ b/c/openssl/err.go @@ -0,0 +1,156 @@ +/* + * 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 openssl + +import ( + "unsafe" + + "github.com/goplus/llgo/c" +) + +// ----------------------------------------------------------------------------- + +/*- + * The error code packs differently depending on if it records a system + * error or an OpenSSL error. + * + * A system error packs like this (we follow POSIX and only allow positive + * numbers that fit in an |int|): + * + * +-+-------------------------------------------------------------+ + * |1| system error number | + * +-+-------------------------------------------------------------+ + * + * An OpenSSL error packs like this: + * + * <---------------------------- 32 bits --------------------------> + * <--- 8 bits ---><------------------ 23 bits -----------------> + * +-+---------------+---------------------------------------------+ + * |0| library | reason | + * +-+---------------+---------------------------------------------+ + * + * A few of the reason bits are reserved as flags with special meaning: + * + * <5 bits-<>--------- 19 bits -----------------> + * +-------+-+-----------------------------------+ + * | rflags| | reason | + * +-------+-+-----------------------------------+ + * ^ + * | + * ERR_RFLAG_FATAL = ERR_R_FATAL + * + * The reason flags are part of the overall reason code for practical + * reasons, as they provide an easy way to place different types of + * reason codes in different numeric ranges. + * + * The currently known reason flags are: + * + * ERR_RFLAG_FATAL Flags that the reason code is considered fatal. + * For backward compatibility reasons, this flag + * is also the code for ERR_R_FATAL (that reason + * code served the dual purpose of flag and reason + * code in one in pre-3.0 OpenSSL). + * ERR_RFLAG_COMMON Flags that the reason code is common to all + * libraries. All ERR_R_ macros must use this flag, + * and no other _R_ macro is allowed to use it. + */ +type Errno c.Ulong + +// ERR_get_error returns the earliest error code from the thread's error queue and +// removes the entry. This function can be called repeatedly until there are no more +// error codes to return. +// +// unsigned long ERR_get_error(void); +// +//go:linkname ERRGetError C.ERR_get_error +func ERRGetError() Errno + +// ERR_get_error_all() is the same as ERR_get_error(), but on success it additionally +// stores the filename, line number and function where the error occurred in *file, +// *line and *func, and also extra text and flags in *data, *flags. If any of those +// parameters are NULL, it will not be changed. +// +// unsigned long ERR_get_error_all( +// const char **file, int *line, const char **func, const char **data, int *flags); +// +//go:linkname ERRGetErrorAll C.ERR_get_error_all +func ERRGetErrorAll( + file **c.Char, line *c.Int, function **c.Char, data **c.Char, flags *c.Int) Errno + +// unsigned long ERR_peek_error(void); +// +//go:linkname ERRPeekError C.ERR_peek_error +func ERRPeekError() Errno + +// unsigned long ERR_peek_error_all( +// const char **file, int *line, const char **func, const char **data, int *flags); +// +//go:linkname ERRPeekErrorAll C.ERR_peek_error_all +func ERRPeekErrorAll( + file **c.Char, line *c.Int, function **c.Char, data **c.Char, flags *c.Int) Errno + +// unsigned long ERR_peek_last_error(void); +// +//go:linkname ERRPeekLastError C.ERR_peek_last_error +func ERRPeekLastError() Errno + +// unsigned long ERR_peek_last_error_all( +// const char **file, int *line, const char **func, const char **data, int *flags); +// +//go:linkname ERRPeekLastErrorAll C.ERR_peek_last_error_all +func ERRPeekLastErrorAll( + file **c.Char, line *c.Int, function **c.Char, data **c.Char, flags *c.Int) Errno + +// void ERR_clear_error(void); +// +//go:linkname ERRClearError C.ERR_clear_error +func ERRClearError() + +// ERR_error_string() generates a human-readable string representing the error code e, +// and places it at buf. buf must be at least 256 bytes long. +// +// char *ERR_error_string(unsigned long e, char *buf); +// +//go:linkname ERRErrorString C.ERR_error_string +func ERRErrorString(e Errno, buf *c.Char) *c.Char + +// ERR_lib_error_string() and ERR_reason_error_string() return the library name and +// reason string respectively. +// +// const char *ERR_lib_error_string(unsigned long e); +// +//go:linkname ERRLibErrorString C.ERR_lib_error_string +func ERRLibErrorString(e Errno) *c.Char + +// const char *ERR_reason_error_string(unsigned long e); +// +//go:linkname ERRReasonErrorString C.ERR_reason_error_string +func ERRReasonErrorString(e Errno) *c.Char + +// void ERR_print_errors_cb(int (*cb) (const char *str, size_t len, void *u), void *u); +// +// [pid]:error:[error code]:[library name]:[function name]:[reason string]:[filename]:[line]:[optional text message] +// +//go:linkname ERRPrintErrorsCb C.ERR_print_errors_cb +func ERRPrintErrorsCb(cb func(str *c.Char, len uintptr, u unsafe.Pointer) c.Int, u unsafe.Pointer) + +// void ERR_print_errors_fp(FILE *fp); +// +//go:linkname ERRPrintErrorsFp C.ERR_print_errors_fp +func ERRPrintErrorsFp(fp c.FilePtr) + +// ----------------------------------------------------------------------------- diff --git a/internal/build/build.go b/internal/build/build.go index 8f487d8e..d32c39e8 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -758,6 +758,7 @@ type none struct{} var hasAltPkg = map[string]none{ "crypto/md5": {}, + "crypto/rand": {}, "fmt": {}, "hash/crc32": {}, "internal/abi": {}, diff --git a/internal/lib/crypto/rand/rand.go b/internal/lib/crypto/rand/rand.go new file mode 100644 index 00000000..091614ca --- /dev/null +++ b/internal/lib/crypto/rand/rand.go @@ -0,0 +1,105 @@ +/* + * 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 rand + +// llgo:skipall +import ( + "io" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/openssl" + "github.com/qiniu/x/errors" +) + +type rndReader struct{} + +func (rndReader) Read(p []byte) (n int, err error) { + return Read(p) +} + +// Reader is a global, shared instance of a cryptographically +// secure random number generator. +var Reader io.Reader = rndReader{} + +type opensslError struct { + file *c.Char + line c.Int + flags c.Int + function *c.Char + data *c.Char + err openssl.Errno +} + +// [error code]:[library name]:[function name]:[reason string]:[[filename:line]]: [text message] +func (p *opensslError) Error() string { + const bufsize = 1024 + buf := (*c.Char)(c.Alloca(bufsize)) + lib := openssl.ERRLibErrorString(p.err) + reason := openssl.ERRReasonErrorString(p.err) + n := uintptr(c.Snprintf( + buf, bufsize, + c.Str("%d:%s:%s:%s:[%s:%d]: "), + p.err, lib, p.function, reason, p.file, p.line)) + n += c.Strlen(openssl.ERRErrorString(p.err, c.Advance(buf, n))) + return c.GoString(buf, n) +} + +func getError() *opensslError { + ret := new(opensslError) + err := openssl.ERRGetErrorAll(&ret.file, &ret.line, &ret.function, &ret.data, &ret.flags) + if err == 0 { + return nil + } + return ret +} + +func getErrors() error { + var errs errors.List + for openssl.ERRPeekError() != 0 { + errs.Add(getError()) + } + return errs.ToError() +} + +// Read is a helper function that calls Reader.Read using io.ReadFull. +// On return, n == len(b) if and only if err == nil. +func Read(b []byte) (n int, err error) { + if openssl.RANDBytes(b) != 0 { + return len(b), nil + } + return 0, getErrors() +} + +/* TODO(xsw): +// batched returns a function that calls f to populate a []byte by chunking it +// into subslices of, at most, readMax bytes. +func batched(f func([]byte) error, readMax int) func([]byte) error { + return func(out []byte) error { + for len(out) > 0 { + read := len(out) + if read > readMax { + read = readMax + } + if err := f(out[:read]); err != nil { + return err + } + out = out[read:] + } + return nil + } +} +*/ diff --git a/internal/lib/crypto/rand/util.go b/internal/lib/crypto/rand/util.go new file mode 100644 index 00000000..51e10c8f --- /dev/null +++ b/internal/lib/crypto/rand/util.go @@ -0,0 +1,98 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rand + +/* TODO(xsw): +import ( + "errors" + "io" + "math/big" +) + +// Prime returns a number of the given bit length that is prime with high probability. +// Prime will return error for any error returned by rand.Read or if bits < 2. +func Prime(rand io.Reader, bits int) (*big.Int, error) { + if bits < 2 { + return nil, errors.New("crypto/rand: prime size must be at least 2-bit") + } + + b := uint(bits % 8) + if b == 0 { + b = 8 + } + + bytes := make([]byte, (bits+7)/8) + p := new(big.Int) + + for { + if _, err := io.ReadFull(rand, bytes); err != nil { + return nil, err + } + + // Clear bits in the first byte to make sure the candidate has a size <= bits. + bytes[0] &= uint8(int(1<= 2 { + bytes[0] |= 3 << (b - 2) + } else { + // Here b==1, because b cannot be zero. + bytes[0] |= 1 + if len(bytes) > 1 { + bytes[1] |= 0x80 + } + } + // Make the value odd since an even number this large certainly isn't prime. + bytes[len(bytes)-1] |= 1 + + p.SetBytes(bytes) + if p.ProbablyPrime(20) { + return p, nil + } + } +} + +// Int returns a uniform random value in [0, max). It panics if max <= 0. +func Int(rand io.Reader, max *big.Int) (n *big.Int, err error) { + if max.Sign() <= 0 { + panic("crypto/rand: argument to Int is <= 0") + } + n = new(big.Int) + n.Sub(max, n.SetUint64(1)) + // bitLen is the maximum bit length needed to encode a value < max. + bitLen := n.BitLen() + if bitLen == 0 { + // the only valid result is 0 + return + } + // k is the maximum byte length needed to encode a value < max. + k := (bitLen + 7) / 8 + // b is the number of bits in the most significant byte of max-1. + b := uint(bitLen % 8) + if b == 0 { + b = 8 + } + + bytes := make([]byte, k) + + for { + _, err = io.ReadFull(rand, bytes) + if err != nil { + return nil, err + } + + // Clear bits in the first byte to increase the probability + // that the candidate is < max. + bytes[0] &= uint8(int(1< Date: Wed, 31 Jul 2024 10:30:04 +0800 Subject: [PATCH 126/152] README: crypto/rand --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 514e7d10..f004731b 100644 --- a/README.md +++ b/README.md @@ -304,6 +304,7 @@ Here are the Go packages that can be imported correctly: * [hash/crc32](https://pkg.go.dev/hash/crc32) (partially) * [hash/crc64](https://pkg.go.dev/hash/crc64) * [crypto/md5](https://pkg.go.dev/crypto/md5) +* [crypto/rand](https://pkg.go.dev/crypto/rand) (partially) * [regexp](https://pkg.go.dev/regexp) * [regexp/syntax](https://pkg.go.dev/regexp/syntax) From 4c70651b81ff8eeae2ccf14980e45f83b8582db9 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Wed, 31 Jul 2024 11:33:15 +0800 Subject: [PATCH 127/152] library: go/{token, scanner} --- README.md | 3 +++ _cmptest/goscandemo/scan.go | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 _cmptest/goscandemo/scan.go diff --git a/README.md b/README.md index f004731b..12e39ca0 100644 --- a/README.md +++ b/README.md @@ -294,6 +294,7 @@ Here are the Go packages that can be imported correctly: * [fmt](https://pkg.go.dev/fmt) (partially) * [reflect](https://pkg.go.dev/reflect) (partially) * [time](https://pkg.go.dev/time) (partially) +* [encoding](https://pkg.go.dev/encoding) * [encoding/binary](https://pkg.go.dev/encoding/binary) * [encoding/hex](https://pkg.go.dev/encoding/hex) * [encoding/base32](https://pkg.go.dev/encoding/base32) @@ -307,6 +308,8 @@ Here are the Go packages that can be imported correctly: * [crypto/rand](https://pkg.go.dev/crypto/rand) (partially) * [regexp](https://pkg.go.dev/regexp) * [regexp/syntax](https://pkg.go.dev/regexp/syntax) +* [go/token](https://pkg.go.dev/go/token) +* [go/scanner](https://pkg.go.dev/go/scanner) ## Dependencies diff --git a/_cmptest/goscandemo/scan.go b/_cmptest/goscandemo/scan.go new file mode 100644 index 00000000..76cdc261 --- /dev/null +++ b/_cmptest/goscandemo/scan.go @@ -0,0 +1,27 @@ +package main + +import ( + "fmt" + "go/scanner" + "go/token" +) + +func main() { + // src is the input that we want to tokenize. + src := []byte("cos(x) + 1i*sin(x) // Euler") + + // Initialize the scanner. + var s scanner.Scanner + fset := token.NewFileSet() // positions are relative to fset + file := fset.AddFile("", fset.Base(), len(src)) // register input "file" + s.Init(file, src, nil /* no error handler */, scanner.ScanComments) + + // Repeated calls to Scan yield the token sequence found in the input. + for { + pos, tok, lit := s.Scan() + if tok == token.EOF { + break + } + fmt.Printf("%s\t%s\t%q\n", fset.Position(pos), tok, lit) + } +} From 5082ba7102222c818743431b3ee72efa1fdd0aad Mon Sep 17 00:00:00 2001 From: xushiwei Date: Wed, 31 Jul 2024 12:29:09 +0800 Subject: [PATCH 128/152] library: go/parser (todo) --- _cmptest/_goparsedemo/parse.go | 34 + internal/build/_overlay/go/parser/resolver.go | 612 ++++++++++++++++++ internal/build/overlay.go | 10 +- ssa/eh.go | 2 +- 4 files changed, 656 insertions(+), 2 deletions(-) create mode 100644 _cmptest/_goparsedemo/parse.go create mode 100644 internal/build/_overlay/go/parser/resolver.go diff --git a/_cmptest/_goparsedemo/parse.go b/_cmptest/_goparsedemo/parse.go new file mode 100644 index 00000000..bba0347c --- /dev/null +++ b/_cmptest/_goparsedemo/parse.go @@ -0,0 +1,34 @@ +package main + +import ( + "fmt" + "go/parser" + "go/token" +) + +func main() { + fset := token.NewFileSet() // positions are relative to fset + + src := `package foo + +import ( + "fmt" + "time" +) + +func bar() { + fmt.Println(time.Now()) +}` + + // Parse src but stop after processing the imports. + f, err := parser.ParseFile(fset, "", src, parser.ImportsOnly) + if err != nil { + fmt.Println(err) + return + } + + // Print the imports from the file's AST. + for _, s := range f.Imports { + fmt.Println(s.Path.Value) + } +} diff --git a/internal/build/_overlay/go/parser/resolver.go b/internal/build/_overlay/go/parser/resolver.go new file mode 100644 index 00000000..a7338135 --- /dev/null +++ b/internal/build/_overlay/go/parser/resolver.go @@ -0,0 +1,612 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package parser + +import ( + "fmt" + "go/ast" + "go/token" + "strings" +) + +const debugResolve = false + +// resolveFile walks the given file to resolve identifiers within the file +// scope, updating ast.Ident.Obj fields with declaration information. +// +// If declErr is non-nil, it is used to report declaration errors during +// resolution. tok is used to format position in error messages. +func resolveFile(file *ast.File, handle *token.File, declErr func(token.Pos, string)) { + pkgScope := ast.NewScope(nil) + r := &resolver{ + handle: handle, + declErr: declErr, + topScope: pkgScope, + pkgScope: pkgScope, + depth: 1, + } + + for _, decl := range file.Decls { + ast.Walk(r, decl) + } + + r.closeScope() + assert(r.topScope == nil, "unbalanced scopes") + assert(r.labelScope == nil, "unbalanced label scopes") + + // resolve global identifiers within the same file + i := 0 + for _, ident := range r.unresolved { + // i <= index for current ident + assert(ident.Obj == unresolved, "object already resolved") + ident.Obj = r.pkgScope.Lookup(ident.Name) // also removes unresolved sentinel + if ident.Obj == nil { + r.unresolved[i] = ident + i++ + } else if debugResolve { + pos := ident.Obj.Decl.(interface{ Pos() token.Pos }).Pos() + r.trace("resolved %s@%v to package object %v", ident.Name, ident.Pos(), pos) + } + } + file.Scope = r.pkgScope + file.Unresolved = r.unresolved[0:i] +} + +const maxScopeDepth int = 1e3 + +type resolver struct { + handle *token.File + declErr func(token.Pos, string) + + // Ordinary identifier scopes + pkgScope *ast.Scope // pkgScope.Outer == nil + topScope *ast.Scope // top-most scope; may be pkgScope + unresolved []*ast.Ident // unresolved identifiers + depth int // scope depth + + // Label scopes + // (maintained by open/close LabelScope) + labelScope *ast.Scope // label scope for current function + targetStack [][]*ast.Ident // stack of unresolved labels +} + +func (r *resolver) trace(format string, args ...any) { + fmt.Println(strings.Repeat(". ", r.depth) + r.sprintf(format, args...)) +} + +func (r *resolver) sprintf(format string, args ...any) string { + for i, arg := range args { + switch arg := arg.(type) { + case token.Pos: + args[i] = r.handle.Position(arg) + } + } + return fmt.Sprintf(format, args...) +} + +func (r *resolver) openScope(pos token.Pos) { + r.depth++ + if r.depth > maxScopeDepth { + panic(bailout{pos: pos, msg: "exceeded max scope depth during object resolution"}) + } + if debugResolve { + r.trace("opening scope @%v", pos) + } + r.topScope = ast.NewScope(r.topScope) +} + +func (r *resolver) closeScope() { + r.depth-- + if debugResolve { + r.trace("closing scope") + } + r.topScope = r.topScope.Outer +} + +func (r *resolver) openLabelScope() { + r.labelScope = ast.NewScope(r.labelScope) + r.targetStack = append(r.targetStack, nil) +} + +func (r *resolver) closeLabelScope() { + // resolve labels + n := len(r.targetStack) - 1 + scope := r.labelScope + for _, ident := range r.targetStack[n] { + ident.Obj = scope.Lookup(ident.Name) + if ident.Obj == nil && r.declErr != nil { + r.declErr(ident.Pos(), fmt.Sprintf("label %s undefined", ident.Name)) + } + } + // pop label scope + r.targetStack = r.targetStack[0:n] + r.labelScope = r.labelScope.Outer +} + +func (r *resolver) declare(decl, data any, scope *ast.Scope, kind ast.ObjKind, idents ...*ast.Ident) { + for _, ident := range idents { + if ident.Obj != nil { + panic(fmt.Sprintf("%v: identifier %s already declared or resolved", ident.Pos(), ident.Name)) + } + obj := ast.NewObj(kind, ident.Name) + // remember the corresponding declaration for redeclaration + // errors and global variable resolution/typechecking phase + obj.Decl = decl + obj.Data = data + // Identifiers (for receiver type parameters) are written to the scope, but + // never set as the resolved object. See issue #50956. + if _, ok := decl.(*ast.Ident); !ok { + ident.Obj = obj + } + if ident.Name != "_" { + if debugResolve { + r.trace("declaring %s@%v", ident.Name, ident.Pos()) + } + if alt := scope.Insert(obj); alt != nil && r.declErr != nil { + prevDecl := "" + if pos := alt.Pos(); pos.IsValid() { + prevDecl = r.sprintf("\n\tprevious declaration at %v", pos) + } + r.declErr(ident.Pos(), fmt.Sprintf("%s redeclared in this block%s", ident.Name, prevDecl)) + } + } + } +} + +func (r *resolver) shortVarDecl(decl *ast.AssignStmt) { + // Go spec: A short variable declaration may redeclare variables + // provided they were originally declared in the same block with + // the same type, and at least one of the non-blank variables is new. + n := 0 // number of new variables + for _, x := range decl.Lhs { + if ident, isIdent := x.(*ast.Ident); isIdent { + assert(ident.Obj == nil, "identifier already declared or resolved") + obj := ast.NewObj(ast.Var, ident.Name) + // remember corresponding assignment for other tools + obj.Decl = decl + ident.Obj = obj + if ident.Name != "_" { + if debugResolve { + r.trace("declaring %s@%v", ident.Name, ident.Pos()) + } + if alt := r.topScope.Insert(obj); alt != nil { + ident.Obj = alt // redeclaration + } else { + n++ // new declaration + } + } + } + } + if n == 0 && r.declErr != nil { + r.declErr(decl.Lhs[0].Pos(), "no new variables on left side of :=") + } +} + +// The unresolved object is a sentinel to mark identifiers that have been added +// to the list of unresolved identifiers. The sentinel is only used for verifying +// internal consistency. +var unresolved = new(ast.Object) + +// If x is an identifier, resolve attempts to resolve x by looking up +// the object it denotes. If no object is found and collectUnresolved is +// set, x is marked as unresolved and collected in the list of unresolved +// identifiers. +func (r *resolver) resolve(ident *ast.Ident, collectUnresolved bool) { + if ident.Obj != nil { + panic(r.sprintf("%v: identifier %s already declared or resolved", ident.Pos(), ident.Name)) + } + // '_' should never refer to existing declarations, because it has special + // handling in the spec. + if ident.Name == "_" { + return + } + for s := r.topScope; s != nil; s = s.Outer { + if obj := s.Lookup(ident.Name); obj != nil { + if debugResolve { + r.trace("resolved %v:%s to %v", ident.Pos(), ident.Name, obj) + } + assert(obj.Name != "", "obj with no name") + // Identifiers (for receiver type parameters) are written to the scope, + // but never set as the resolved object. See issue #50956. + if _, ok := obj.Decl.(*ast.Ident); !ok { + ident.Obj = obj + } + return + } + } + // all local scopes are known, so any unresolved identifier + // must be found either in the file scope, package scope + // (perhaps in another file), or universe scope --- collect + // them so that they can be resolved later + if collectUnresolved { + ident.Obj = unresolved + r.unresolved = append(r.unresolved, ident) + } +} + +func (r *resolver) walkExprs(list []ast.Expr) { + for _, node := range list { + ast.Walk(r, node) + } +} + +func (r *resolver) walkLHS(list []ast.Expr) { + for _, expr := range list { + expr := unparen(expr) + if _, ok := expr.(*ast.Ident); !ok && expr != nil { + ast.Walk(r, expr) + } + } +} + +func (r *resolver) walkStmts(list []ast.Stmt) { + for _, stmt := range list { + ast.Walk(r, stmt) + } +} + +func (r *resolver) Visit(node ast.Node) ast.Visitor { + if debugResolve && node != nil { + r.trace("node %T@%v", node, node.Pos()) + } + + switch n := node.(type) { + + // Expressions. + case *ast.Ident: + r.resolve(n, true) + + case *ast.FuncLit: + r.openScope(n.Pos()) + defer r.closeScope() + r.walkFuncType(n.Type) + r.walkBody(n.Body) + + case *ast.SelectorExpr: + ast.Walk(r, n.X) + // Note: don't try to resolve n.Sel, as we don't support qualified + // resolution. + + case *ast.StructType: + r.openScope(n.Pos()) + defer r.closeScope() + r.walkFieldList(n.Fields, ast.Var) + + case *ast.FuncType: + r.openScope(n.Pos()) + defer r.closeScope() + r.walkFuncType(n) + + case *ast.CompositeLit: + if n.Type != nil { + ast.Walk(r, n.Type) + } + for _, e := range n.Elts { + if kv, _ := e.(*ast.KeyValueExpr); kv != nil { + // See issue #45160: try to resolve composite lit keys, but don't + // collect them as unresolved if resolution failed. This replicates + // existing behavior when resolving during parsing. + if ident, _ := kv.Key.(*ast.Ident); ident != nil { + r.resolve(ident, false) + } else { + ast.Walk(r, kv.Key) + } + ast.Walk(r, kv.Value) + } else { + ast.Walk(r, e) + } + } + + case *ast.InterfaceType: + r.openScope(n.Pos()) + defer r.closeScope() + r.walkFieldList(n.Methods, ast.Fun) + + // Statements + case *ast.LabeledStmt: + r.declare(n, nil, r.labelScope, ast.Lbl, n.Label) + ast.Walk(r, n.Stmt) + + case *ast.AssignStmt: + r.walkExprs(n.Rhs) + if n.Tok == token.DEFINE { + r.shortVarDecl(n) + } else { + r.walkExprs(n.Lhs) + } + + case *ast.BranchStmt: + // add to list of unresolved targets + if n.Tok != token.FALLTHROUGH && n.Label != nil { + depth := len(r.targetStack) - 1 + r.targetStack[depth] = append(r.targetStack[depth], n.Label) + } + + case *ast.BlockStmt: + r.openScope(n.Pos()) + defer r.closeScope() + r.walkStmts(n.List) + + case *ast.IfStmt: + r.openScope(n.Pos()) + defer r.closeScope() + if n.Init != nil { + ast.Walk(r, n.Init) + } + ast.Walk(r, n.Cond) + ast.Walk(r, n.Body) + if n.Else != nil { + ast.Walk(r, n.Else) + } + + case *ast.CaseClause: + r.walkExprs(n.List) + r.openScope(n.Pos()) + defer r.closeScope() + r.walkStmts(n.Body) + + case *ast.SwitchStmt: + r.openScope(n.Pos()) + defer r.closeScope() + if n.Init != nil { + ast.Walk(r, n.Init) + } + if n.Tag != nil { + // The scope below reproduces some unnecessary behavior of the parser, + // opening an extra scope in case this is a type switch. It's not needed + // for expression switches. + // TODO: remove this once we've matched the parser resolution exactly. + if n.Init != nil { + r.openScope(n.Tag.Pos()) + defer r.closeScope() + } + ast.Walk(r, n.Tag) + } + if n.Body != nil { + r.walkStmts(n.Body.List) + } + + case *ast.TypeSwitchStmt: + if n.Init != nil { + r.openScope(n.Pos()) + defer r.closeScope() + ast.Walk(r, n.Init) + } + r.openScope(n.Assign.Pos()) + defer r.closeScope() + ast.Walk(r, n.Assign) + // s.Body consists only of case clauses, so does not get its own + // scope. + if n.Body != nil { + r.walkStmts(n.Body.List) + } + + case *ast.CommClause: + r.openScope(n.Pos()) + defer r.closeScope() + if n.Comm != nil { + ast.Walk(r, n.Comm) + } + r.walkStmts(n.Body) + + case *ast.SelectStmt: + // as for switch statements, select statement bodies don't get their own + // scope. + if n.Body != nil { + r.walkStmts(n.Body.List) + } + + case *ast.ForStmt: + r.openScope(n.Pos()) + defer r.closeScope() + if n.Init != nil { + ast.Walk(r, n.Init) + } + if n.Cond != nil { + ast.Walk(r, n.Cond) + } + if n.Post != nil { + ast.Walk(r, n.Post) + } + ast.Walk(r, n.Body) + + case *ast.RangeStmt: + r.openScope(n.Pos()) + defer r.closeScope() + ast.Walk(r, n.X) + var lhs []ast.Expr + if n.Key != nil { + lhs = append(lhs, n.Key) + } + if n.Value != nil { + lhs = append(lhs, n.Value) + } + if len(lhs) > 0 { + if n.Tok == token.DEFINE { + // Note: we can't exactly match the behavior of object resolution + // during the parsing pass here, as it uses the position of the RANGE + // token for the RHS OpPos. That information is not contained within + // the AST. + as := &ast.AssignStmt{ + Lhs: lhs, + Tok: token.DEFINE, + TokPos: n.TokPos, + Rhs: []ast.Expr{&ast.UnaryExpr{Op: token.RANGE, X: n.X}}, + } + // TODO(rFindley): this walkLHS reproduced the parser resolution, but + // is it necessary? By comparison, for a normal AssignStmt we don't + // walk the LHS in case there is an invalid identifier list. + r.walkLHS(lhs) + r.shortVarDecl(as) + } else { + r.walkExprs(lhs) + } + } + ast.Walk(r, n.Body) + + // Declarations + case *ast.GenDecl: + switch n.Tok { + case token.CONST, token.VAR: + for i, spec := range n.Specs { + spec := spec.(*ast.ValueSpec) + kind := ast.Con + if n.Tok == token.VAR { + kind = ast.Var + } + r.walkExprs(spec.Values) + if spec.Type != nil { + ast.Walk(r, spec.Type) + } + r.declare(spec, i, r.topScope, kind, spec.Names...) + } + case token.TYPE: + for _, spec := range n.Specs { + spec := spec.(*ast.TypeSpec) + // Go spec: The scope of a type identifier declared inside a function begins + // at the identifier in the TypeSpec and ends at the end of the innermost + // containing block. + r.declare(spec, nil, r.topScope, ast.Typ, spec.Name) + if spec.TypeParams != nil { + r.openScope(spec.Pos()) + r.walkTParams(spec.TypeParams) + r.closeScope() + } + ast.Walk(r, spec.Type) + } + } + + case *ast.FuncDecl: + // Open the function scope. + r.openScope(n.Pos()) + defer r.closeScope() + + r.walkRecv(n.Recv) + + // Type parameters are walked normally: they can reference each other, and + // can be referenced by normal parameters. + if n.Type.TypeParams != nil { + r.walkTParams(n.Type.TypeParams) + // TODO(rFindley): need to address receiver type parameters. + } + + // Resolve and declare parameters in a specific order to get duplicate + // declaration errors in the correct location. + r.resolveList(n.Type.Params) + r.resolveList(n.Type.Results) + r.declareList(n.Recv, ast.Var) + r.declareList(n.Type.Params, ast.Var) + r.declareList(n.Type.Results, ast.Var) + + r.walkBody(n.Body) + if n.Recv == nil && n.Name.Name != "init" { + r.declare(n, nil, r.pkgScope, ast.Fun, n.Name) + } + + default: + return r + } + + return nil +} + +func (r *resolver) walkFuncType(typ *ast.FuncType) { + // typ.TypeParams must be walked separately for FuncDecls. + r.resolveList(typ.Params) + r.resolveList(typ.Results) + r.declareList(typ.Params, ast.Var) + r.declareList(typ.Results, ast.Var) +} + +func (r *resolver) resolveList(list *ast.FieldList) { + if list == nil { + return + } + for _, f := range list.List { + if f.Type != nil { + ast.Walk(r, f.Type) + } + } +} + +func (r *resolver) declareList(list *ast.FieldList, kind ast.ObjKind) { + if list == nil { + return + } + for _, f := range list.List { + r.declare(f, nil, r.topScope, kind, f.Names...) + } +} + +func (r *resolver) walkRecv(recv *ast.FieldList) { + // If our receiver has receiver type parameters, we must declare them before + // trying to resolve the rest of the receiver, and avoid re-resolving the + // type parameter identifiers. + if recv == nil || len(recv.List) == 0 { + return // nothing to do + } + typ := recv.List[0].Type + if ptr, ok := typ.(*ast.StarExpr); ok { + typ = ptr.X + } + + var declareExprs []ast.Expr // exprs to declare + var resolveExprs []ast.Expr // exprs to resolve + switch typ := typ.(type) { + case *ast.IndexExpr: + declareExprs = []ast.Expr{typ.Index} + resolveExprs = append(resolveExprs, typ.X) + case *ast.IndexListExpr: + declareExprs = typ.Indices + resolveExprs = append(resolveExprs, typ.X) + default: + resolveExprs = append(resolveExprs, typ) + } + for _, expr := range declareExprs { + if id, _ := expr.(*ast.Ident); id != nil { + r.declare(expr, nil, r.topScope, ast.Typ, id) + } else { + // The receiver type parameter expression is invalid, but try to resolve + // it anyway for consistency. + resolveExprs = append(resolveExprs, expr) + } + } + for _, expr := range resolveExprs { + if expr != nil { + ast.Walk(r, expr) + } + } + // The receiver is invalid, but try to resolve it anyway for consistency. + for _, f := range recv.List[1:] { + if f.Type != nil { + ast.Walk(r, f.Type) + } + } +} + +func (r *resolver) walkFieldList(list *ast.FieldList, kind ast.ObjKind) { + if list == nil { + return + } + r.resolveList(list) + r.declareList(list, kind) +} + +// walkTParams is like walkFieldList, but declares type parameters eagerly so +// that they may be resolved in the constraint expressions held in the field +// Type. +func (r *resolver) walkTParams(list *ast.FieldList) { + r.declareList(list, ast.Typ) + r.resolveList(list) +} + +func (r *resolver) walkBody(body *ast.BlockStmt) { + if body == nil { + return + } + r.openLabelScope() + defer r.closeLabelScope() + r.walkStmts(body.List) +} diff --git a/internal/build/overlay.go b/internal/build/overlay.go index 19f39f24..90724cc0 100644 --- a/internal/build/overlay.go +++ b/internal/build/overlay.go @@ -1,5 +1,13 @@ package build +import ( + _ "embed" +) + +//go:embed _overlay/go/parser/resolver.go +var go_parser_resolver string + var overlayFiles = map[string]string{ - "math/exp_amd64.go": "package math;", + "math/exp_amd64.go": "package math;", + "go/parser/resolver.go": go_parser_resolver, } diff --git a/ssa/eh.go b/ssa/eh.go index a7f42a59..7b097005 100644 --- a/ssa/eh.go +++ b/ssa/eh.go @@ -221,7 +221,7 @@ func (b Builder) Defer(kind DoAction, fn Expr, args ...Expr) { case DeferAlways: // nothing to do default: - panic("todo: DeferInLoop is not supported") + panic("todo: DeferInLoop is not supported - " + b.Func.Name()) } self.stmts = append(self.stmts, func(bits Expr) { switch kind { From a4ec6cce96ef34337f1e5db2a07c4ffc100cd259 Mon Sep 17 00:00:00 2001 From: Aofei Sheng Date: Wed, 31 Jul 2024 10:49:07 +0800 Subject: [PATCH 129/152] ci: install further optional dependencies for demos Achieved 100% pass rate for demo tests, at least on macOS. --- .github/workflows/go.yml | 23 +++++++++++++++-------- .github/workflows/test_demo.sh | 3 ++- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 9a412391..1de5cc8e 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -58,6 +58,15 @@ jobs: ) sudo apt-get install -y "${opt_deps[@]}" + - name: Install further optional dependencies for demos + run: | + wget -P ./_demo/llama2-c https://huggingface.co/karpathy/tinyllamas/resolve/main/stories15M.bin + py_deps=( + numpy # for github.com/goplus/llgo/py/numpy + torch # for github.com/goplus/llgo/py/torch + ) + pip3 install --break-system-packages "${py_deps[@]}" + - name: Clang information run: | echo $PATH @@ -73,27 +82,25 @@ jobs: run: go build -v ./... - name: Test - if: matrix.os != 'macos-latest' + if: ${{!startsWith(matrix.os, 'macos')}} run: go test -v ./... - name: Test with coverage - if: matrix.os == 'macos-latest' + if: startsWith(matrix.os, 'macos') run: go test -v -coverprofile="coverage.txt" -covermode=atomic ./... - name: Install run: go install ./... - name: LLGO tests - if: matrix.os != 'ubuntu-latest' + if: ${{!startsWith(matrix.os, 'ubuntu')}} run: | echo "Test result on ${{matrix.os}} with LLVM ${{matrix.llvm}}" > result.md bash .github/workflows/test_llgo.sh - - name: Test _demo and _pydemo - run: | - set +e - bash .github/workflows/test_demo.sh - exit 0 + - name: Test demos + continue-on-error: true + run: bash .github/workflows/test_demo.sh - name: Show test result run: cat result.md diff --git a/.github/workflows/test_demo.sh b/.github/workflows/test_demo.sh index c707b00e..8e947f6a 100644 --- a/.github/workflows/test_demo.sh +++ b/.github/workflows/test_demo.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -e # llgo run subdirectories under _demo and _pydemo total=0 @@ -8,7 +9,7 @@ for d in ./_demo/* ./_pydemo/*; do total=$((total+1)) if [ -d "$d" ]; then echo "Testing $d" - if ! llgo run "$d"; then + if ! (cd "$d" && llgo run .); then echo "FAIL" failed=$((failed+1)) failed_cases="$failed_cases\n* :x: $d" From 2119e52f55ea0d3d245483489a2f69490ba92648 Mon Sep 17 00:00:00 2001 From: Aofei Sheng Date: Wed, 31 Jul 2024 13:27:54 +0800 Subject: [PATCH 130/152] fix(.goreleaser.yaml): correct ldflags for build version and time --- .goreleaser.yaml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 9dc465b9..bd4efc18 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -20,8 +20,8 @@ builds: flags: - -tags=darwin,amd64,byollvm ldflags: - - -X github.com/goplus/llgo/xtool/env.buildVersion=v{{.Version}} - - -X github.com/goplus/llgo/xtool/env.buildDate={{.Date}} + - -X github.com/goplus/llgo/x/env.buildVersion=v{{.Version}} + - -X github.com/goplus/llgo/x/env.buildTime={{.Date}} - -X github.com/goplus/llgo/xtool/env/llvm.ldLLVMConfigBin=/usr/local/opt/llvm@18/bin/llvm-config env: - CC=o64-clang @@ -36,8 +36,8 @@ builds: flags: - -tags=darwin,arm64,byollvm ldflags: - - -X github.com/goplus/llgo/xtool/env.buildVersion=v{{.Version}} - - -X github.com/goplus/llgo/xtool/env.buildDate={{.Date}} + - -X github.com/goplus/llgo/x/env.buildVersion=v{{.Version}} + - -X github.com/goplus/llgo/x/env.buildTime={{.Date}} - -X github.com/goplus/llgo/xtool/env/llvm.ldLLVMConfigBin=/opt/homebrew/opt/llvm@18/bin/llvm-config env: - CC=oa64-clang @@ -52,8 +52,8 @@ builds: flags: - -tags=linux,amd64,byollvm ldflags: - - -X github.com/goplus/llgo/xtool/env.buildVersion=v{{.Version}} - - -X github.com/goplus/llgo/xtool/env.buildDate={{.Date}} + - -X github.com/goplus/llgo/x/env.buildVersion=v{{.Version}} + - -X github.com/goplus/llgo/x/env.buildTime={{.Date}} - -X github.com/goplus/llgo/xtool/env/llvm.ldLLVMConfigBin=/usr/lib/llvm-18/bin/llvm-config env: - CC=x86_64-linux-gnu-gcc @@ -68,8 +68,8 @@ builds: flags: - -tags=linux,arm64,byollvm ldflags: - - -X github.com/goplus/llgo/xtool/env.buildVersion=v{{.Version}} - - -X github.com/goplus/llgo/xtool/env.buildDate={{.Date}} + - -X github.com/goplus/llgo/x/env.buildVersion=v{{.Version}} + - -X github.com/goplus/llgo/x/env.buildTime={{.Date}} - -X github.com/goplus/llgo/xtool/env/llvm.ldLLVMConfigBin=/usr/lib/llvm-18/bin/llvm-config env: - CC=aarch64-linux-gnu-gcc From 2d7958f726f0347dcede1c891ea6e99707f9221b Mon Sep 17 00:00:00 2001 From: tsingbx Date: Wed, 31 Jul 2024 13:56:42 +0800 Subject: [PATCH 131/152] add crypto sha1, sha256, sha512 --- _cmptest/sha1demo/sha1.go | 11 ++++ _cmptest/sha256demo/sha256.go | 11 ++++ _cmptest/sha512demo/sha512.go | 11 ++++ internal/build/build.go | 3 + internal/lib/crypto/sha1/sha1.go | 53 ++++++++++++++++ internal/lib/crypto/sha256/sha224.go | 31 ++++++++++ internal/lib/crypto/sha256/sha256.go | 69 +++++++++++++++++++++ internal/lib/crypto/sha512/sha384.go | 31 ++++++++++ internal/lib/crypto/sha512/sha512.go | 90 ++++++++++++++++++++++++++++ 9 files changed, 310 insertions(+) create mode 100644 _cmptest/sha1demo/sha1.go create mode 100644 _cmptest/sha256demo/sha256.go create mode 100644 _cmptest/sha512demo/sha512.go create mode 100644 internal/lib/crypto/sha1/sha1.go create mode 100644 internal/lib/crypto/sha256/sha224.go create mode 100644 internal/lib/crypto/sha256/sha256.go create mode 100644 internal/lib/crypto/sha512/sha384.go create mode 100644 internal/lib/crypto/sha512/sha512.go diff --git a/_cmptest/sha1demo/sha1.go b/_cmptest/sha1demo/sha1.go new file mode 100644 index 00000000..e2f0d0ed --- /dev/null +++ b/_cmptest/sha1demo/sha1.go @@ -0,0 +1,11 @@ +package main + +import ( + "crypto/sha1" + "fmt" +) + +func main() { + data := []byte("This page intentionally left blank.") + fmt.Printf("% x", sha1.Sum(data)) +} diff --git a/_cmptest/sha256demo/sha256.go b/_cmptest/sha256demo/sha256.go new file mode 100644 index 00000000..41e2eab7 --- /dev/null +++ b/_cmptest/sha256demo/sha256.go @@ -0,0 +1,11 @@ +package main + +import ( + "crypto/sha256" + "fmt" +) + +func main() { + sum := sha256.Sum256([]byte("hello world\n")) + fmt.Printf("%x", sum) +} diff --git a/_cmptest/sha512demo/sha512.go b/_cmptest/sha512demo/sha512.go new file mode 100644 index 00000000..eeff921f --- /dev/null +++ b/_cmptest/sha512demo/sha512.go @@ -0,0 +1,11 @@ +package main + +import ( + "crypto/sha512" + "fmt" +) + +func main() { + sum := sha512.Sum512([]byte("hello world\n")) + fmt.Printf("%x", sum) +} diff --git a/internal/build/build.go b/internal/build/build.go index d32c39e8..7e732be9 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -758,6 +758,9 @@ type none struct{} var hasAltPkg = map[string]none{ "crypto/md5": {}, + "crypto/sha1": {}, + "crypto/sha256": {}, + "crypto/sha512": {}, "crypto/rand": {}, "fmt": {}, "hash/crc32": {}, diff --git a/internal/lib/crypto/sha1/sha1.go b/internal/lib/crypto/sha1/sha1.go new file mode 100644 index 00000000..0779e917 --- /dev/null +++ b/internal/lib/crypto/sha1/sha1.go @@ -0,0 +1,53 @@ +package sha1 + +// llgo:skipall + +import ( + "hash" + "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/openssl" +) + +// The blocksize of SHA-1 in bytes. +const BlockSize = 64 + +// The size of a SHA-1 checksum in bytes. +const Size = 20 + +type digest struct { + ctx openssl.SHA_CTX +} + +func (d *digest) Size() int { return Size } + +func (d *digest) BlockSize() int { return BlockSize } + +func (d *digest) Reset() { + d.ctx.Init() +} + +func (d *digest) Write(p []byte) (nn int, err error) { + d.ctx.UpdateBytes(p) + return len(p), nil +} + +func (d *digest) Sum(in []byte) []byte { + hash := (*[Size]byte)(c.Alloca(Size)) + d.ctx.Final((*byte)(unsafe.Pointer(hash))) + return append(in, hash[:]...) +} + +// New returns a new hash.Hash computing the SHA1 checksum. +func New() hash.Hash { + d := new(digest) + d.ctx.Init() + return d +} + +// Sum returns the SHA-1 checksum of the data. +func Sum(data []byte) (ret [Size]byte) { + openssl.SHA1Bytes(data, &ret[0]) + return +} diff --git a/internal/lib/crypto/sha256/sha224.go b/internal/lib/crypto/sha256/sha224.go new file mode 100644 index 00000000..2a9d5a70 --- /dev/null +++ b/internal/lib/crypto/sha256/sha224.go @@ -0,0 +1,31 @@ +package sha256 + +import ( + "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/openssl" +) + +type digest224 struct { + ctx openssl.SHA224_CTX +} + +func (d *digest224) Size() int { return Size224 } + +func (d *digest224) BlockSize() int { return BlockSize } + +func (d *digest224) Reset() { + d.ctx.Init() +} + +func (d *digest224) Write(p []byte) (nn int, err error) { + d.ctx.UpdateBytes(p) + return len(p), nil +} + +func (d *digest224) Sum(in []byte) []byte { + hash := (*[Size]byte)(c.Alloca(Size)) + d.ctx.Final((*byte)(unsafe.Pointer(hash))) + return append(in, hash[:]...) +} diff --git a/internal/lib/crypto/sha256/sha256.go b/internal/lib/crypto/sha256/sha256.go new file mode 100644 index 00000000..b96dfd55 --- /dev/null +++ b/internal/lib/crypto/sha256/sha256.go @@ -0,0 +1,69 @@ +package sha256 + +// llgo:skipall + +import ( + "hash" + "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/openssl" +) + +// The blocksize of SHA256 and SHA224 in bytes. +const BlockSize = 64 + +// The size of a SHA256 checksum in bytes. +const Size = 32 + +// The size of a SHA224 checksum in bytes. +const Size224 = 28 + +type digest256 struct { + ctx openssl.SHA256_CTX +} + +func (d *digest256) Size() int { return Size } + +func (d *digest256) BlockSize() int { return BlockSize } + +func (d *digest256) Reset() { + d.ctx.Init() +} + +func (d *digest256) Write(p []byte) (nn int, err error) { + d.ctx.UpdateBytes(p) + return len(p), nil +} + +func (d *digest256) Sum(in []byte) []byte { + hash := (*[Size]byte)(c.Alloca(Size)) + d.ctx.Final((*byte)(unsafe.Pointer(hash))) + return append(in, hash[:]...) +} + +// New returns a new hash.Hash computing the SHA256 checksum. +func New() hash.Hash { + d := new(digest256) + d.ctx.Init() + return d +} + +// New224 returns a new hash.Hash computing the SHA224 checksum. +func New224() hash.Hash { + d := new(digest224) + d.ctx.Init() + return d +} + +// Sum224 returns the SHA224 checksum of the data. +func Sum224(data []byte) (ret [Size224]byte) { + openssl.SHA224Bytes(data, &ret[0]) + return +} + +// Sum256 returns the SHA256 checksum of the data. +func Sum256(data []byte) (ret [Size]byte) { + openssl.SHA256Bytes(data, &ret[0]) + return +} diff --git a/internal/lib/crypto/sha512/sha384.go b/internal/lib/crypto/sha512/sha384.go new file mode 100644 index 00000000..ab69e872 --- /dev/null +++ b/internal/lib/crypto/sha512/sha384.go @@ -0,0 +1,31 @@ +package sha512 + +import ( + "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/openssl" +) + +type digest384 struct { + ctx openssl.SHA384_CTX +} + +func (d *digest384) Size() int { return Size384 } + +func (d *digest384) BlockSize() int { return BlockSize } + +func (d *digest384) Reset() { + d.ctx.Init() +} + +func (d *digest384) Write(p []byte) (nn int, err error) { + d.ctx.UpdateBytes(p) + return len(p), nil +} + +func (d *digest384) Sum(in []byte) []byte { + hash := (*[Size]byte)(c.Alloca(Size)) + d.ctx.Final((*byte)(unsafe.Pointer(hash))) + return append(in, hash[:]...) +} diff --git a/internal/lib/crypto/sha512/sha512.go b/internal/lib/crypto/sha512/sha512.go new file mode 100644 index 00000000..c566a64a --- /dev/null +++ b/internal/lib/crypto/sha512/sha512.go @@ -0,0 +1,90 @@ +package sha512 + +// llgo:skipall + +import ( + "hash" + "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/openssl" +) + +const ( + // Size is the size, in bytes, of a SHA-512 checksum. + Size = 64 + + // Size224 is the size, in bytes, of a SHA-512/224 checksum. + Size224 = 28 + + // Size256 is the size, in bytes, of a SHA-512/256 checksum. + Size256 = 32 + + // Size384 is the size, in bytes, of a SHA-384 checksum. + Size384 = 48 + + // BlockSize is the block size, in bytes, of the SHA-512/224, + // SHA-512/256, SHA-384 and SHA-512 hash functions. + BlockSize = 128 +) + +type digest512 struct { + ctx openssl.SHA512_CTX +} + +func (d *digest512) Size() int { return Size } + +func (d *digest512) BlockSize() int { return BlockSize } + +func (d *digest512) Reset() { + d.ctx.Init() +} + +func (d *digest512) Write(p []byte) (nn int, err error) { + d.ctx.UpdateBytes(p) + return len(p), nil +} + +func (d *digest512) Sum(in []byte) []byte { + hash := (*[Size]byte)(c.Alloca(Size)) + d.ctx.Final((*byte)(unsafe.Pointer(hash))) + return append(in, hash[:]...) +} + +func New() hash.Hash { + d := new(digest512) + d.ctx.Init() + return d +} + +func New384() hash.Hash { + d := new(digest384) + d.ctx.Init() + return d +} + +func Sum384(data []byte) (ret [Size384]byte) { + openssl.SHA384Bytes(data, &ret[0]) + return +} + +func Sum512(data []byte) (ret [Size]byte) { + openssl.SHA512Bytes(data, &ret[0]) + return +} + +func New512_224() hash.Hash { + panic("todo: New512_224") +} + +func New512_256() hash.Hash { + panic("todo: New512_256") +} + +func Sum512_224(data []byte) [Size224]byte { + panic("todo: Sum512_224") +} + +func Sum512_256(data []byte) [Size256]byte { + panic("todo: Sum512_256") +} From f67b15b9268e57c32616fdb06d29c0ab8d7196ba Mon Sep 17 00:00:00 2001 From: tsingbx Date: Wed, 31 Jul 2024 14:28:15 +0800 Subject: [PATCH 132/152] fix test fail --- _cmptest/sha1demo/sha1.go | 7 +++++-- internal/lib/crypto/sha1/sha1.go | 1 - 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/_cmptest/sha1demo/sha1.go b/_cmptest/sha1demo/sha1.go index e2f0d0ed..dbd813c7 100644 --- a/_cmptest/sha1demo/sha1.go +++ b/_cmptest/sha1demo/sha1.go @@ -3,9 +3,12 @@ package main import ( "crypto/sha1" "fmt" + "io" ) func main() { - data := []byte("This page intentionally left blank.") - fmt.Printf("% x", sha1.Sum(data)) + h := sha1.New() + io.WriteString(h, "The fog is getting thicker!") + io.WriteString(h, "And Leon's getting laaarger!") + fmt.Printf("%x", h.Sum(nil)) } diff --git a/internal/lib/crypto/sha1/sha1.go b/internal/lib/crypto/sha1/sha1.go index 0779e917..93637a51 100644 --- a/internal/lib/crypto/sha1/sha1.go +++ b/internal/lib/crypto/sha1/sha1.go @@ -1,7 +1,6 @@ package sha1 // llgo:skipall - import ( "hash" "unsafe" From 8882d75132cd85757049dc57dd9353cba3831661 Mon Sep 17 00:00:00 2001 From: tsingbx Date: Wed, 31 Jul 2024 14:36:42 +0800 Subject: [PATCH 133/152] fix test error --- _cmptest/sha256demo/sha256.go | 7 +++++-- _cmptest/sha512demo/sha512.go | 7 +++++-- internal/lib/crypto/sha256/sha256.go | 6 +++++- internal/lib/crypto/sha512/sha512.go | 6 +++++- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/_cmptest/sha256demo/sha256.go b/_cmptest/sha256demo/sha256.go index 41e2eab7..1ef0652d 100644 --- a/_cmptest/sha256demo/sha256.go +++ b/_cmptest/sha256demo/sha256.go @@ -3,9 +3,12 @@ package main import ( "crypto/sha256" "fmt" + "io" ) func main() { - sum := sha256.Sum256([]byte("hello world\n")) - fmt.Printf("%x", sum) + h := sha256.New() + io.WriteString(h, "The fog is getting thicker!") + io.WriteString(h, "And Leon's getting laaarger!") + fmt.Printf("%x", h.Sum(nil)) } diff --git a/_cmptest/sha512demo/sha512.go b/_cmptest/sha512demo/sha512.go index eeff921f..f2bf3791 100644 --- a/_cmptest/sha512demo/sha512.go +++ b/_cmptest/sha512demo/sha512.go @@ -3,9 +3,12 @@ package main import ( "crypto/sha512" "fmt" + "io" ) func main() { - sum := sha512.Sum512([]byte("hello world\n")) - fmt.Printf("%x", sum) + h := sha512.New() + io.WriteString(h, "The fog is getting thicker!") + io.WriteString(h, "And Leon's getting laaarger!") + fmt.Printf("%x", h.Sum(nil)) } diff --git a/internal/lib/crypto/sha256/sha256.go b/internal/lib/crypto/sha256/sha256.go index b96dfd55..d55b3d97 100644 --- a/internal/lib/crypto/sha256/sha256.go +++ b/internal/lib/crypto/sha256/sha256.go @@ -1,7 +1,6 @@ package sha256 // llgo:skipall - import ( "hash" "unsafe" @@ -67,3 +66,8 @@ func Sum256(data []byte) (ret [Size]byte) { openssl.SHA256Bytes(data, &ret[0]) return } + +func Sum(data []byte) (ret [Size]byte) { + openssl.SHA256Bytes(data, &ret[0]) + return +} diff --git a/internal/lib/crypto/sha512/sha512.go b/internal/lib/crypto/sha512/sha512.go index c566a64a..53ff4700 100644 --- a/internal/lib/crypto/sha512/sha512.go +++ b/internal/lib/crypto/sha512/sha512.go @@ -1,7 +1,6 @@ package sha512 // llgo:skipall - import ( "hash" "unsafe" @@ -73,6 +72,11 @@ func Sum512(data []byte) (ret [Size]byte) { return } +func Sum(data []byte) (ret [Size]byte) { + openssl.SHA512Bytes(data, &ret[0]) + return +} + func New512_224() hash.Hash { panic("todo: New512_224") } From c9a7dab419aa8c68fd8cdde7dc0d16ac9e20595a Mon Sep 17 00:00:00 2001 From: tsingbx Date: Wed, 31 Jul 2024 14:56:03 +0800 Subject: [PATCH 134/152] delete sum --- internal/lib/crypto/sha256/sha256.go | 5 ----- internal/lib/crypto/sha512/sha512.go | 5 ----- 2 files changed, 10 deletions(-) diff --git a/internal/lib/crypto/sha256/sha256.go b/internal/lib/crypto/sha256/sha256.go index d55b3d97..480325be 100644 --- a/internal/lib/crypto/sha256/sha256.go +++ b/internal/lib/crypto/sha256/sha256.go @@ -66,8 +66,3 @@ func Sum256(data []byte) (ret [Size]byte) { openssl.SHA256Bytes(data, &ret[0]) return } - -func Sum(data []byte) (ret [Size]byte) { - openssl.SHA256Bytes(data, &ret[0]) - return -} diff --git a/internal/lib/crypto/sha512/sha512.go b/internal/lib/crypto/sha512/sha512.go index 53ff4700..0cdf6ec9 100644 --- a/internal/lib/crypto/sha512/sha512.go +++ b/internal/lib/crypto/sha512/sha512.go @@ -72,11 +72,6 @@ func Sum512(data []byte) (ret [Size]byte) { return } -func Sum(data []byte) (ret [Size]byte) { - openssl.SHA512Bytes(data, &ret[0]) - return -} - func New512_224() hash.Hash { panic("todo: New512_224") } From 4fdfafa17f738035385c989d2af59128d2663804 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Wed, 31 Jul 2024 12:01:29 +0800 Subject: [PATCH 135/152] doc/c:update destructor usage --- doc/How-to-support-a-C&C++-Library.md | 29 ++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/doc/How-to-support-a-C&C++-Library.md b/doc/How-to-support-a-C&C++-Library.md index b0b9bca6..5c77e4a9 100644 --- a/doc/How-to-support-a-C&C++-Library.md +++ b/doc/How-to-support-a-C&C++-Library.md @@ -336,6 +336,28 @@ func ParseError() c.Int // llgo:link (*Reader).INIReaderInit C.INIReaderInit func (r *Reader) INIReaderInit() {} ``` +- Destructor + + Similar to constructors, destructors are typically hidden, so they also need to be wrapped. The wrapping method for destructors differs from that of constructors as follows: + In the C++ wrapper file (e.g., cppWrap.cpp): + ```c + void INIReaderDispose(INIReader* r) { + r->~INIReader(); + } + ``` + This wrapper function explicitly calls the object's destructor. By using extern "C", we ensure that this function can be called by C code, allowing Go to link to it. + In the Go file: + ```go + // llgo:link (*Reader).Dispose C.INIReaderDispose + func (r *Reader) Dispose() {} + ``` + Here we link the Go Dispose method to the C++ wrapped INIReaderDispose function. + In actual usage: + ```go + reader := inih.NewReader(c.Str(buf), uintptr(len(buf))) + defer reader.Dispose() + ``` + We use defer to ensure that the Dispose method is called when the reader object goes out of scope, thus properly releasing resources. - Class Methods For general methods of the class, directly use `llgo:link` to link: @@ -348,14 +370,7 @@ func ParseError() c.Int ``` Template or inline methods of the class will be introduced in the next section. -- Destructor - Similar to the constructor process, after creating the class, use `defer` to call it explicitly: - - ```go - reader := inih.NewReader(c.Str(buf), uintptr(len(buf))) - defer reader.Dispose() - ``` #### Templates and Inlines From a4e9233231c49ea2462cb49cd2635ac90d326fde Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Wed, 31 Jul 2024 15:13:30 +0800 Subject: [PATCH 136/152] doc/c:fix incorrect usage in construtors --- doc/How-to-support-a-C&C++-Library.md | 57 +++++++++++---------------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/doc/How-to-support-a-C&C++-Library.md b/doc/How-to-support-a-C&C++-Library.md index 5c77e4a9..e97fa254 100644 --- a/doc/How-to-support-a-C&C++-Library.md +++ b/doc/How-to-support-a-C&C++-Library.md @@ -27,7 +27,7 @@ int ini_parse(const char* filename, ini_handler handler, void* user); 3. Create the corresponding Go file -```c +```bash inih/ ├── _demo ├── inih_demo @@ -300,50 +300,39 @@ func ParseError() c.Int - Constructor is explicitly declared in the class (can find the corresponding symbol in the dynamic library): - Bind to the `InitFromBuffer` method of the struct and call it in the `NewReaderFile` function to initialize the class and return the class for Go to use. + ```cpp + class INIReader { + public: + // Construct INIReader and parse given filename. + INI_API explicit INIReader(const std::string &filename); + } + ``` + Bind to the `InitFromFile` method of the struct and call it in the `NewReaderFile` function to initialize the class and return the class for Go to use. + The following long string starting with `_ZN9INI` is the corresponding function prototype in the symbol table for `INIReader(const std::string &filename)` ```go + // llgo:link (*Reader).InitFromFile C._ZN9INIReaderC1ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE + func (r *Reader) InitFromFile(fileName *std.String) {} + // NewReaderFile creates a new INIReader instance. func NewReaderFile(fileName *std.String) (ret Reader) { ret.InitFromFile(fileName) return } - /* - class INIReader - { - public: - explicit INIReader(const char *buffer, size_t buffer_size); - } - */ - - // llgo:link (*Reader).InitFromBuffer C._ZN9INIReaderC1EPKcm - func (r *Reader) InitFromBuffer(buffer *c.Char, bufferSize uintptr) {} ``` - Constructor is not explicitly declared in the class (cannot find the corresponding symbol in the dynamic library) - - If the destructor is not explicitly declared in the source code, the compiler will automatically generate a default destructor. Use `extern "C"` to wrap it in cppWrap.cpp: - - ```c - extern "C" void INIReaderInit(INIReader* r) - { - r->INIReader(); - } - ``` - - Link in Go: - - ```go - // llgo:link (*Reader).INIReaderInit C.INIReaderInit - func (r *Reader) INIReaderInit() {} - ``` + + In typical implementations of the inih library, directly invoking implicit constructors to instantiate reader objects is not recommended. For detailed examples of how bindings effectively handle non-exported symbols, please refer to the [Templates and Inlines](#templates-and-inlines) section. - Destructor - Similar to constructors, destructors are typically hidden, so they also need to be wrapped. The wrapping method for destructors differs from that of constructors as follows: + Destructors are typically not directly accessible in the dynamic library, so they need to be wrapped. The wrapping method for destructors differs from that of constructors as follows: In the C++ wrapper file (e.g., cppWrap.cpp): - ```c - void INIReaderDispose(INIReader* r) { - r->~INIReader(); - } + ```cpp + extern "C" { + void INIReaderDispose(INIReader* r) { + r->~INIReader(); + } + } // extern "C" ``` This wrapper function explicitly calls the object's destructor. By using extern "C", we ensure that this function can be called by C code, allowing Go to link to it. In the Go file: @@ -376,7 +365,7 @@ func ParseError() c.Int Templates or inlines do not generate symbols in dynamic libraries (dylib) (default constructors and destructors). To ensure that you can use C style symbols to link template or inline functions, create a C++ file and wrap it with `extern "C"`, then bind the functions directly in Go. -```c +```cpp // Using std::string as an example, not needed for migrating inih extern "C" void stdStringInitFromCStrLen(std::string* s, const char* cstr, size_t len) { new(s) std::string(cstr, len); From 27677f86e47b4a6693ba142ad4e6769820253a91 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Wed, 31 Jul 2024 18:55:46 +0800 Subject: [PATCH 137/152] library: crypto/{sha1, sha256, sha512} --- README.md | 3 +++ internal/lib/crypto/md5/md5.go | 5 +++++ internal/lib/crypto/sha1/sha1.go | 5 +++++ internal/lib/crypto/sha256/sha224.go | 17 ++++++++++++++ internal/lib/crypto/sha256/sha256.go | 22 +++++-------------- internal/lib/crypto/sha512/sha384.go | 12 ++++++++++ internal/lib/crypto/sha512/sha512.go | 33 +++++++++++++--------------- 7 files changed, 63 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 10d12960..4766ac6d 100644 --- a/README.md +++ b/README.md @@ -305,6 +305,9 @@ Here are the Go packages that can be imported correctly: * [hash/crc32](https://pkg.go.dev/hash/crc32) (partially) * [hash/crc64](https://pkg.go.dev/hash/crc64) * [crypto/md5](https://pkg.go.dev/crypto/md5) +* [crypto/sha1](https://pkg.go.dev/crypto/sha1) +* [crypto/sha256](https://pkg.go.dev/crypto/sha256) +* [crypto/sha512](https://pkg.go.dev/crypto/sha512) (partially) * [crypto/rand](https://pkg.go.dev/crypto/rand) (partially) * [regexp](https://pkg.go.dev/regexp) * [regexp/syntax](https://pkg.go.dev/regexp/syntax) diff --git a/internal/lib/crypto/md5/md5.go b/internal/lib/crypto/md5/md5.go index 9baf0419..7865000e 100644 --- a/internal/lib/crypto/md5/md5.go +++ b/internal/lib/crypto/md5/md5.go @@ -18,6 +18,7 @@ package md5 // llgo:skipall import ( + "crypto" "hash" "unsafe" @@ -25,6 +26,10 @@ import ( "github.com/goplus/llgo/c/openssl" ) +func init() { + crypto.RegisterHash(crypto.MD5, New) +} + // The blocksize of MD5 in bytes. const BlockSize = 64 diff --git a/internal/lib/crypto/sha1/sha1.go b/internal/lib/crypto/sha1/sha1.go index 93637a51..d804ef10 100644 --- a/internal/lib/crypto/sha1/sha1.go +++ b/internal/lib/crypto/sha1/sha1.go @@ -2,6 +2,7 @@ package sha1 // llgo:skipall import ( + "crypto" "hash" "unsafe" @@ -9,6 +10,10 @@ import ( "github.com/goplus/llgo/c/openssl" ) +func init() { + crypto.RegisterHash(crypto.SHA1, New) +} + // The blocksize of SHA-1 in bytes. const BlockSize = 64 diff --git a/internal/lib/crypto/sha256/sha224.go b/internal/lib/crypto/sha256/sha224.go index 2a9d5a70..79d9d0da 100644 --- a/internal/lib/crypto/sha256/sha224.go +++ b/internal/lib/crypto/sha256/sha224.go @@ -1,12 +1,16 @@ package sha256 import ( + "hash" "unsafe" "github.com/goplus/llgo/c" "github.com/goplus/llgo/c/openssl" ) +// The size of a SHA224 checksum in bytes. +const Size224 = 28 + type digest224 struct { ctx openssl.SHA224_CTX } @@ -29,3 +33,16 @@ func (d *digest224) Sum(in []byte) []byte { d.ctx.Final((*byte)(unsafe.Pointer(hash))) return append(in, hash[:]...) } + +// New224 returns a new hash.Hash computing the SHA224 checksum. +func New224() hash.Hash { + d := new(digest224) + d.ctx.Init() + return d +} + +// Sum224 returns the SHA224 checksum of the data. +func Sum224(data []byte) (ret [Size224]byte) { + openssl.SHA224Bytes(data, &ret[0]) + return +} diff --git a/internal/lib/crypto/sha256/sha256.go b/internal/lib/crypto/sha256/sha256.go index 480325be..4e4dcb00 100644 --- a/internal/lib/crypto/sha256/sha256.go +++ b/internal/lib/crypto/sha256/sha256.go @@ -2,6 +2,7 @@ package sha256 // llgo:skipall import ( + "crypto" "hash" "unsafe" @@ -9,15 +10,17 @@ import ( "github.com/goplus/llgo/c/openssl" ) +func init() { + crypto.RegisterHash(crypto.SHA224, New224) + crypto.RegisterHash(crypto.SHA256, New) +} + // The blocksize of SHA256 and SHA224 in bytes. const BlockSize = 64 // The size of a SHA256 checksum in bytes. const Size = 32 -// The size of a SHA224 checksum in bytes. -const Size224 = 28 - type digest256 struct { ctx openssl.SHA256_CTX } @@ -48,19 +51,6 @@ func New() hash.Hash { return d } -// New224 returns a new hash.Hash computing the SHA224 checksum. -func New224() hash.Hash { - d := new(digest224) - d.ctx.Init() - return d -} - -// Sum224 returns the SHA224 checksum of the data. -func Sum224(data []byte) (ret [Size224]byte) { - openssl.SHA224Bytes(data, &ret[0]) - return -} - // Sum256 returns the SHA256 checksum of the data. func Sum256(data []byte) (ret [Size]byte) { openssl.SHA256Bytes(data, &ret[0]) diff --git a/internal/lib/crypto/sha512/sha384.go b/internal/lib/crypto/sha512/sha384.go index ab69e872..ecfd18d7 100644 --- a/internal/lib/crypto/sha512/sha384.go +++ b/internal/lib/crypto/sha512/sha384.go @@ -1,6 +1,7 @@ package sha512 import ( + "hash" "unsafe" "github.com/goplus/llgo/c" @@ -29,3 +30,14 @@ func (d *digest384) Sum(in []byte) []byte { d.ctx.Final((*byte)(unsafe.Pointer(hash))) return append(in, hash[:]...) } + +func New384() hash.Hash { + d := new(digest384) + d.ctx.Init() + return d +} + +func Sum384(data []byte) (ret [Size384]byte) { + openssl.SHA384Bytes(data, &ret[0]) + return +} diff --git a/internal/lib/crypto/sha512/sha512.go b/internal/lib/crypto/sha512/sha512.go index 0cdf6ec9..57790485 100644 --- a/internal/lib/crypto/sha512/sha512.go +++ b/internal/lib/crypto/sha512/sha512.go @@ -2,6 +2,7 @@ package sha512 // llgo:skipall import ( + "crypto" "hash" "unsafe" @@ -9,6 +10,13 @@ import ( "github.com/goplus/llgo/c/openssl" ) +func init() { + crypto.RegisterHash(crypto.SHA384, New384) + crypto.RegisterHash(crypto.SHA512, New) + crypto.RegisterHash(crypto.SHA512_224, New512_224) + crypto.RegisterHash(crypto.SHA512_256, New512_256) +} + const ( // Size is the size, in bytes, of a SHA-512 checksum. Size = 64 @@ -56,34 +64,23 @@ func New() hash.Hash { return d } -func New384() hash.Hash { - d := new(digest384) - d.ctx.Init() - return d -} - -func Sum384(data []byte) (ret [Size384]byte) { - openssl.SHA384Bytes(data, &ret[0]) - return -} - func Sum512(data []byte) (ret [Size]byte) { openssl.SHA512Bytes(data, &ret[0]) return } func New512_224() hash.Hash { - panic("todo: New512_224") -} - -func New512_256() hash.Hash { - panic("todo: New512_256") + panic("todo: crypto/sha512.New512_224") } func Sum512_224(data []byte) [Size224]byte { - panic("todo: Sum512_224") + panic("todo: crypto/sha512.Sum512_224") +} + +func New512_256() hash.Hash { + panic("todo: crypto/sha512.New512_256") } func Sum512_256(data []byte) [Size256]byte { - panic("todo: Sum512_256") + panic("todo: crypto/sha512.Sum512_256") } From 85509c777d3f95e52f3a783f33b9e794201f89b0 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Wed, 31 Jul 2024 18:59:25 +0800 Subject: [PATCH 138/152] library: crypto --- README.md | 1 + _cmptest/md5demo/md5.go | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4766ac6d..878879a3 100644 --- a/README.md +++ b/README.md @@ -304,6 +304,7 @@ Here are the Go packages that can be imported correctly: * [hash/adler32](https://pkg.go.dev/hash/adler32) * [hash/crc32](https://pkg.go.dev/hash/crc32) (partially) * [hash/crc64](https://pkg.go.dev/hash/crc64) +* [crypto](https://pkg.go.dev/crypto) * [crypto/md5](https://pkg.go.dev/crypto/md5) * [crypto/sha1](https://pkg.go.dev/crypto/sha1) * [crypto/sha256](https://pkg.go.dev/crypto/sha256) diff --git a/_cmptest/md5demo/md5.go b/_cmptest/md5demo/md5.go index dbee9e66..ebe57948 100644 --- a/_cmptest/md5demo/md5.go +++ b/_cmptest/md5demo/md5.go @@ -1,6 +1,7 @@ package main import ( + "crypto" "crypto/md5" "fmt" "io" @@ -10,5 +11,10 @@ func main() { h := md5.New() io.WriteString(h, "The fog is getting thicker!") io.WriteString(h, "And Leon's getting laaarger!") - fmt.Printf("%x", h.Sum(nil)) + fmt.Printf("%x\n", h.Sum(nil)) + + h = crypto.MD5.New() + io.WriteString(h, "The fog is getting thicker!") + io.WriteString(h, "And Leon's getting laaarger!") + fmt.Printf("%x\n", h.Sum(nil)) } From 79d8b00b279a80819326d0564cd729a6aef9fb43 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Wed, 31 Jul 2024 22:34:13 +0800 Subject: [PATCH 139/152] c/openssl: bignum, rsa --- c/openssl/_demo/cbigintdemo/fib.go | 42 ++++ c/openssl/bn.go | 306 +++++++++++++++++++++++++++++ c/openssl/openssl.go | 12 ++ c/openssl/rsa.go | 60 ++++++ 4 files changed, 420 insertions(+) create mode 100644 c/openssl/_demo/cbigintdemo/fib.go create mode 100644 c/openssl/bn.go create mode 100644 c/openssl/rsa.go diff --git a/c/openssl/_demo/cbigintdemo/fib.go b/c/openssl/_demo/cbigintdemo/fib.go new file mode 100644 index 00000000..ce93c218 --- /dev/null +++ b/c/openssl/_demo/cbigintdemo/fib.go @@ -0,0 +1,42 @@ +package main + +import ( + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/openssl" +) + +func newInt(n openssl.BN_ULONG) *openssl.BIGNUM { + ret := openssl.BNNew() + ret.SetWord(n) + return ret +} + +func main() { + ctx := openssl.BN_CTXNew() + defer ctx.Free() + + // Initialize two big ints with the first two numbers in the sequence. + a := newInt(0) + b := newInt(1) + defer a.Free() + defer b.Free() + + // Initialize limit as 10^99, the smallest integer with 100 digits. + v10, v99 := newInt(10), newInt(99) + defer v10.Free() + defer v99.Free() + + limit := openssl.BNNew() + defer limit.Free() + + limit.Exp(v10, v99, ctx) + + // Loop while a is smaller than 1e100. + for a.Cmp(limit) < 0 { + // Compute the next Fibonacci number, storing it in a. + a.Add(a, b) + // Swap a and b so that b is the next number in the sequence. + a, b = b, a + } + c.Printf(c.Str("%s\n"), a.CStr()) // 100-digit Fibonacci number +} diff --git a/c/openssl/bn.go b/c/openssl/bn.go new file mode 100644 index 00000000..4b0d483d --- /dev/null +++ b/c/openssl/bn.go @@ -0,0 +1,306 @@ +/* + * 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 openssl + +import ( + _ "unsafe" + + "github.com/goplus/llgo/c" +) + +type BN_ULONG = uint64 + +// ----------------------------------------------------------------------------- + +type BN_CTX struct { + Unused [0]byte +} + +// BN_CTX *BN_CTX_new(void); +// +//go:linkname BN_CTXNew C.BN_CTX_new +func BN_CTXNew() *BN_CTX + +// BN_CTX *BN_CTX_secure_new(void); +// +//go:linkname BN_CTXSecureNew C.BN_CTX_secure_new +func BN_CTXSecureNew() *BN_CTX + +// BN_CTX *BN_CTX_new_ex(OSSL_LIB_CTX *ctx); +// BN_CTX *BN_CTX_secure_new_ex(OSSL_LIB_CTX *ctx); + +// void BN_CTX_free(BN_CTX *c); +// +// llgo:link (*BN_CTX).Free C.BN_CTX_free +func (*BN_CTX) Free() {} + +// void BN_CTX_start(BN_CTX *ctx); +// BIGNUM *BN_CTX_get(BN_CTX *ctx); +// void BN_CTX_end(BN_CTX *ctx); + +// ----------------------------------------------------------------------------- + +type BIGNUM struct { + Unused [0]byte +} + +// BIGNUM *BN_new(void); +// +//go:linkname BNNew C.BN_new +func BNNew() *BIGNUM + +// BIGNUM *BN_secure_new(void); +// +//go:linkname BNSecureNew C.BN_secure_new +func BNSecureNew() *BIGNUM + +// void BN_free(BIGNUM *a); +// +// llgo:link (*BIGNUM).Free C.BN_free +func (*BIGNUM) Free() {} + +// void BN_clear_free(BIGNUM *a); +// +// llgo:link (*BIGNUM).ClearFree C.BN_clear_free +func (*BIGNUM) ClearFree() {} + +// void BN_clear(BIGNUM *a); +// +// llgo:link (*BIGNUM).Clear C.BN_clear +func (*BIGNUM) Clear() {} + +// BIGNUM *BN_dup(const BIGNUM *a); +// +// llgo:link (*BIGNUM).Dup C.BN_dup +func (*BIGNUM) Dup() *BIGNUM { return nil } + +// BIGNUM *BN_copy(BIGNUM *a, const BIGNUM *b); +// +// llgo:link (*BIGNUM).Copy C.BN_copy +func (*BIGNUM) Copy(b *BIGNUM) *BIGNUM { return nil } + +// void BN_swap(BIGNUM *a, BIGNUM *b); +// +// llgo:link (*BIGNUM).Swap C.BN_swap +func (*BIGNUM) Swap(b *BIGNUM) {} + +// int BN_set_word(BIGNUM *a, BN_ULONG w); +// +// llgo:link (*BIGNUM).SetWord C.BN_set_word +func (*BIGNUM) SetWord(w BN_ULONG) c.Int { return 0 } + +// BN_ULONG BN_get_word(const BIGNUM *a); +// +// llgo:link (*BIGNUM).GetWord C.BN_get_word +func (*BIGNUM) GetWord() BN_ULONG { return 0 } + +// BN_ULONG BN_mod_word(const BIGNUM *a, BN_ULONG w); +// +// llgo:link (*BIGNUM).ModWord C.BN_mod_word +func (*BIGNUM) ModWord(w BN_ULONG) BN_ULONG { return 0 } + +// BN_ULONG BN_div_word(BIGNUM *a, BN_ULONG w); +// +// llgo:link (*BIGNUM).DivWord C.BN_div_word +func (*BIGNUM) DivWord(w BN_ULONG) BN_ULONG { return 0 } + +// int BN_mul_word(BIGNUM *a, BN_ULONG w); +// +// llgo:link (*BIGNUM).MulWord C.BN_mul_word +func (*BIGNUM) MulWord(w BN_ULONG) c.Int { return 0 } + +// int BN_add_word(BIGNUM *a, BN_ULONG w); +// +// llgo:link (*BIGNUM).AddWord C.BN_add_word +func (*BIGNUM) AddWord(w BN_ULONG) c.Int { return 0 } + +// int BN_sub_word(BIGNUM *a, BN_ULONG w); +// +// llgo:link (*BIGNUM).SubWord C.BN_sub_word +func (*BIGNUM) SubWord(w BN_ULONG) c.Int { return 0 } + +// char *BN_bn2hex(const BIGNUM *a); +// +// llgo:link (*BIGNUM).Bn2hex C.BN_bn2hex +func (*BIGNUM) Bn2hex() *c.Char { return nil } + +// char *BN_bn2dec(const BIGNUM *a); +// +// llgo:link (*BIGNUM).Bn2dec C.BN_bn2dec +func (*BIGNUM) Bn2dec() *c.Char { return nil } + +// llgo:link (*BIGNUM).CStr C.BN_bn2dec +func (*BIGNUM) CStr() *c.Char { return nil } + +// int BN_hex2bn(BIGNUM **a, const char *str); +// +//go:linkname BNHex2bn C.BN_hex2bn +func BNHex2bn(a **BIGNUM, str *c.Char) c.Int + +// int BN_dec2bn(BIGNUM **a, const char *str); +// +//go:linkname BNDec2bn C.BN_dec2bn +func BNDec2bn(a **BIGNUM, str *c.Char) c.Int + +// int BN_asc2bn(BIGNUM **a, const char *str); +// +//go:linkname BNAsc2bn C.BN_asc2bn +func BNAsc2bn(a **BIGNUM, str *c.Char) c.Int + +/* +BIGNUM *BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret); +BIGNUM *BN_signed_bin2bn(const unsigned char *s, int len, BIGNUM *ret); +int BN_bn2bin(const BIGNUM *a, unsigned char *to); +int BN_bn2binpad(const BIGNUM *a, unsigned char *to, int tolen); +int BN_signed_bn2bin(const BIGNUM *a, unsigned char *to, int tolen); +BIGNUM *BN_lebin2bn(const unsigned char *s, int len, BIGNUM *ret); +BIGNUM *BN_signed_lebin2bn(const unsigned char *s, int len, BIGNUM *ret); +int BN_bn2lebinpad(const BIGNUM *a, unsigned char *to, int tolen); +int BN_signed_bn2lebin(const BIGNUM *a, unsigned char *to, int tolen); +BIGNUM *BN_native2bn(const unsigned char *s, int len, BIGNUM *ret); +BIGNUM *BN_signed_native2bn(const unsigned char *s, int len, BIGNUM *ret); +int BN_bn2nativepad(const BIGNUM *a, unsigned char *to, int tolen); +int BN_signed_bn2native(const BIGNUM *a, unsigned char *to, int tolen); +BIGNUM *BN_mpi2bn(const unsigned char *s, int len, BIGNUM *ret); +int BN_bn2mpi(const BIGNUM *a, unsigned char *to); +*/ + +// int BN_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b); +// +// llgo:link (*BIGNUM).Sub C.BN_sub +func (*BIGNUM) Sub(a, b *BIGNUM) c.Int { return 0 } + +// int BN_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b); +// +// llgo:link (*BIGNUM).Add C.BN_add +func (*BIGNUM) Add(a, b *BIGNUM) c.Int { return 0 } + +// int BN_usub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b); +// +// llgo:link (*BIGNUM).Usub C.BN_usub +func (*BIGNUM) Usub(a, b *BIGNUM) c.Int { return 0 } + +// int BN_uadd(BIGNUM *r, const BIGNUM *a, const BIGNUM *b); +// +// llgo:link (*BIGNUM).Uadd C.BN_uadd +func (*BIGNUM) Uadd(a, b *BIGNUM) c.Int { return 0 } + +// int BN_mul(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx); +// +// llgo:link (*BIGNUM).Mul C.BN_mul +func (*BIGNUM) Mul(r, a, b *BIGNUM, ctx *BN_CTX) c.Int { return 0 } + +// int BN_sqr(BIGNUM *r, const BIGNUM *a, BN_CTX *ctx); +// +// llgo:link (*BIGNUM).Sqr C.BN_sqr +func (*BIGNUM) Sqr(r, a *BIGNUM, ctx *BN_CTX) c.Int { return 0 } + +/** BN_set_negative sets sign of a BIGNUM + * \param b pointer to the BIGNUM object + * \param n 0 if the BIGNUM b should be positive and a value != 0 otherwise + */ +// void BN_set_negative(BIGNUM *b, int n); +// +// llgo:link (*BIGNUM).SetNegative C.BN_set_negative +func (*BIGNUM) SetNegative(n c.Int) {} + +/** BN_is_negative returns 1 if the BIGNUM is negative + * \param b pointer to the BIGNUM object + * \return 1 if a < 0 and 0 otherwise + */ +// int BN_is_negative(const BIGNUM *b); +// +// llgo:link (*BIGNUM).IsNegative C.BN_is_negative +func (*BIGNUM) IsNegative() c.Int { return 0 } + +// int BN_div(BIGNUM *dv, BIGNUM *rem, const BIGNUM *m, const BIGNUM *d, BN_CTX *ctx); +// +// llgo:link (*BIGNUM).Div C.BN_div +func (*BIGNUM) Div(rem, m, d *BIGNUM, ctx *BN_CTX) c.Int { return 0 } + +// int BN_nnmod(BIGNUM *r, const BIGNUM *m, const BIGNUM *d, BN_CTX *ctx); +// +// llgo:link (*BIGNUM).Nnmod C.BN_nnmod +func (*BIGNUM) Nnmod(r, m, d *BIGNUM, ctx *BN_CTX) c.Int { return 0 } + +// int BN_cmp(const BIGNUM *a, const BIGNUM *b); +// +// llgo:link (*BIGNUM).Cmp C.BN_cmp +func (*BIGNUM) Cmp(b *BIGNUM) c.Int { return 0 } + +// int BN_ucmp(const BIGNUM *a, const BIGNUM *b); +// +// llgo:link (*BIGNUM).Ucmp C.BN_ucmp +func (*BIGNUM) Ucmp(b *BIGNUM) c.Int { return 0 } + +// int BN_is_bit_set(const BIGNUM *a, int n); +// +// llgo:link (*BIGNUM).IsBitSet C.BN_is_bit_set +func (*BIGNUM) IsBitSet(n c.Int) c.Int { return 0 } + +// int BN_set_bit(BIGNUM *a, int n); +// +// llgo:link (*BIGNUM).SetBit C.BN_set_bit +func (*BIGNUM) SetBit(n c.Int) c.Int { return 0 } + +// int BN_clear_bit(BIGNUM *a, int n); +// +// llgo:link (*BIGNUM).ClearBit C.BN_clear_bit +func (*BIGNUM) ClearBit(n c.Int) c.Int { return 0 } + +// int BN_lshift(BIGNUM *r, const BIGNUM *a, int n); +// +// llgo:link (*BIGNUM).Lshift C.BN_lshift +func (*BIGNUM) Lshift(a *BIGNUM, n c.Int) c.Int { return 0 } + +// int BN_lshift1(BIGNUM *r, const BIGNUM *a); +// +// llgo:link (*BIGNUM).Lshift1 C.BN_lshift1 +func (*BIGNUM) Lshift1(a *BIGNUM) c.Int { return 0 } + +// int BN_rshift(BIGNUM *r, const BIGNUM *a, int n); +// +// llgo:link (*BIGNUM).Rshift C.BN_rshift +func (*BIGNUM) Rshift(a *BIGNUM, n c.Int) c.Int { return 0 } + +// int BN_rshift1(BIGNUM *r, const BIGNUM *a); +// +// llgo:link (*BIGNUM).Rshift1 C.BN_rshift1 +func (*BIGNUM) Rshift1(a *BIGNUM) c.Int { return 0 } + +// int BN_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, BN_CTX *ctx); +// +// llgo:link (*BIGNUM).Exp C.BN_exp +func (*BIGNUM) Exp(a, p *BIGNUM, ctx *BN_CTX) c.Int { return 0 } + +// int BN_gcd(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx); +// +// llgo:link (*BIGNUM).Gcd C.BN_gcd +func (*BIGNUM) Gcd(a, b *BIGNUM, ctx *BN_CTX) c.Int { return 0 } + +// int BN_are_coprime(const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx); +// +// llgo:link (*BIGNUM).AreCoprime C.BN_are_coprime +func (*BIGNUM) AreCoprime(b *BIGNUM, ctx *BN_CTX) c.Int { return 0 } + +// ----------------------------------------------------------------------------- + +type BN_GENCB struct { + Unused [0]byte +} + +// ----------------------------------------------------------------------------- diff --git a/c/openssl/openssl.go b/c/openssl/openssl.go index 491f61fe..0762808a 100644 --- a/c/openssl/openssl.go +++ b/c/openssl/openssl.go @@ -16,10 +16,22 @@ package openssl +import ( + "unsafe" + + "github.com/goplus/llgo/c" +) + // ----------------------------------------------------------------------------- const ( LLGoPackage = "link: $(pkg-config --libs openssl); -lssl -lcrypto" ) +//go:linkname Free C.OPENSSL_free +func Free(ptr unsafe.Pointer) + +//go:linkname FreeCStr C.OPENSSL_free +func FreeCStr(ptr *c.Char) + // ----------------------------------------------------------------------------- diff --git a/c/openssl/rsa.go b/c/openssl/rsa.go new file mode 100644 index 00000000..378ba424 --- /dev/null +++ b/c/openssl/rsa.go @@ -0,0 +1,60 @@ +/* + * 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 openssl + +import ( + "unsafe" + + "github.com/goplus/llgo/c" +) + +// ----------------------------------------------------------------------------- + +type RSA struct { + Unused [0]byte +} + +// OSSL_DEPRECATEDIN_3_0 RSA *RSA_new(void); +// +//go:linkname RSANew C.RSA_new +func RSANew() *RSA + +// OSSL_DEPRECATEDIN_3_0 RSA *RSA_new_method(ENGINE *engine); + +// OSSL_DEPRECATEDIN_3_0 int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb); +// +// llgo:link (*RSA).GenerateKeyEx C.RSA_generate_key_ex +func (*RSA) GenerateKeyEx(bits c.Int, e *BIGNUM, cb *BN_GENCB) c.Int { return 0 } + +// OSSL_DEPRECATEDIN_3_0 int RSA_generate_multi_prime_key(RSA *rsa, int bits, int primes, BIGNUM *e, BN_GENCB *cb); +// +// llgo:link (*RSA).GenerateMultiPrimeKey C.RSA_generate_multi_prime_key +func (*RSA) GenerateMultiPrimeKey(bits, primes c.Int, e *BIGNUM, cb *BN_GENCB) c.Int { return 0 } + +// OSSL_DEPRECATEDIN_3_0 int RSA_sign( +// int type, const unsigned char *m, unsigned int m_length, +// unsigned char *sigret, unsigned int *siglen, RSA *rsa); +// +//go:linkname RSASign C.RSA_sign +func RSASign(typ c.Int, msg unsafe.Pointer, mlen c.Uint, sigret *byte, siglen *c.Uint, rsa *RSA) c.Int + +// OSSL_DEPRECATEDIN_3_0 int RSA_verify(int type, const unsigned char *m, +// unsigned int m_length, +// const unsigned char *sigbuf, +// unsigned int siglen, RSA *rsa); + +// ----------------------------------------------------------------------------- From 4e3b65188d0b65f419435bff41ef7c3ba1655333 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Wed, 31 Jul 2024 20:34:04 +0800 Subject: [PATCH 140/152] doc/c:update implicit destructors description --- doc/How-to-support-a-C&C++-Library.md | 44 +++++++++++++-------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/doc/How-to-support-a-C&C++-Library.md b/doc/How-to-support-a-C&C++-Library.md index e97fa254..e691cb42 100644 --- a/doc/How-to-support-a-C&C++-Library.md +++ b/doc/How-to-support-a-C&C++-Library.md @@ -296,6 +296,16 @@ func ParseError() c.Int Unused [32]byte } ``` +- Class Methods + + For general methods of the class, directly use `llgo:link` to link: + + ```go + // llgo:link (*Reader).GetInteger C._ZNK9INIReader10GetIntegerERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEES8_l + func (r *Reader) GetInteger(section *std.String, name *std.String, defaultValue c.Long) c.Long { + return 0 + } + ``` - Constructor - Constructor is explicitly declared in the class (can find the corresponding symbol in the dynamic library): @@ -323,43 +333,33 @@ func ParseError() c.Int - Constructor is not explicitly declared in the class (cannot find the corresponding symbol in the dynamic library) In typical implementations of the inih library, directly invoking implicit constructors to instantiate reader objects is not recommended. For detailed examples of how bindings effectively handle non-exported symbols, please refer to the [Templates and Inlines](#templates-and-inlines) section. + - Destructor - Destructors are typically not directly accessible in the dynamic library, so they need to be wrapped. The wrapping method for destructors differs from that of constructors as follows: - In the C++ wrapper file (e.g., cppWrap.cpp): - ```cpp + The accessibility of destructors in dynamic libraries depends on their definition method. Destructors that are explicitly declared in header files and implemented as non-inline functions in .cpp files typically appear in the dynamic library's symbol table. These destructors can be directly linked, consistent with the linking method of general class methods (see "Class Methods" section). + + For destructors that do not meet these conditions (such as those explicitly declared in header files but implemented inline in .cpp files) and consequently do not appear in the dynamic library's symbol table, a wrapper layer implementation is required. This wrapper in the C++ wrapper file (e.g., cppWrap.cpp) looks like: + ```cpp extern "C" { void INIReaderDispose(INIReader* r) { r->~INIReader(); } } // extern "C" - ``` + ``` This wrapper function explicitly calls the object's destructor. By using extern "C", we ensure that this function can be called by C code, allowing Go to link to it. In the Go file: - ```go + ```go // llgo:link (*Reader).Dispose C.INIReaderDispose func (r *Reader) Dispose() {} - ``` + ``` Here we link the Go Dispose method to the C++ wrapped INIReaderDispose function. In actual usage: - ```go + We use defer to ensure that the Dispose method is called when the reader object goes out of scope, thus properly releasing resources. + ```go reader := inih.NewReader(c.Str(buf), uintptr(len(buf))) defer reader.Dispose() - ``` - We use defer to ensure that the Dispose method is called when the reader object goes out of scope, thus properly releasing resources. -- Class Methods - - For general methods of the class, directly use `llgo:link` to link: - - ```go - // llgo:link (*Reader).GetInteger C._ZNK9INIReader10GetIntegerERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEES8_l - func (r *Reader) GetInteger(section *std.String, name *std.String, defaultValue c.Long) c.Long { - return 0 - } - ``` - - Template or inline methods of the class will be introduced in the next section. - + ``` + This situation is analogous to the handling of inline functions and templates described in the following section. #### Templates and Inlines From 2e4b1d8c2b4b5fed38323bf0760f11c156ae6bcb Mon Sep 17 00:00:00 2001 From: xushiwei Date: Thu, 1 Aug 2024 00:32:21 +0800 Subject: [PATCH 141/152] library: math/big.Int (mini-impl for _cmptest/bigintdemo) --- _cmptest/bigintdemo/fib.go | 25 ++ c/openssl/_demo/cbigintdemo/fib.go | 4 +- c/openssl/_wrap/openssl.c | 5 + c/openssl/bn.go | 35 +++ c/openssl/openssl.go | 5 +- internal/build/build.go | 1 + internal/lib/math/big/int.go | 395 +++++++++++++++++++++++++++++ internal/lib/math/big/intconv.go | 70 +++++ 8 files changed, 537 insertions(+), 3 deletions(-) create mode 100644 _cmptest/bigintdemo/fib.go create mode 100644 c/openssl/_wrap/openssl.c create mode 100644 internal/lib/math/big/int.go create mode 100644 internal/lib/math/big/intconv.go diff --git a/_cmptest/bigintdemo/fib.go b/_cmptest/bigintdemo/fib.go new file mode 100644 index 00000000..9876c2e0 --- /dev/null +++ b/_cmptest/bigintdemo/fib.go @@ -0,0 +1,25 @@ +package main + +import ( + "fmt" + "math/big" +) + +func main() { + // Initialize two big ints with the first two numbers in the sequence. + a := big.NewInt(0) + b := big.NewInt(1) + + // Initialize limit as 10^99, the smallest integer with 100 digits. + var limit big.Int + limit.Exp(big.NewInt(10), big.NewInt(99), nil) + + // Loop while a is smaller than 1e100. + for a.Cmp(&limit) < 0 { + // Compute the next Fibonacci number, storing it in a. + a.Add(a, b) + // Swap a and b so that b is the next number in the sequence. + a, b = b, a + } + fmt.Println(a) // 100-digit Fibonacci number +} diff --git a/c/openssl/_demo/cbigintdemo/fib.go b/c/openssl/_demo/cbigintdemo/fib.go index ce93c218..0d7648a9 100644 --- a/c/openssl/_demo/cbigintdemo/fib.go +++ b/c/openssl/_demo/cbigintdemo/fib.go @@ -38,5 +38,7 @@ func main() { // Swap a and b so that b is the next number in the sequence. a, b = b, a } - c.Printf(c.Str("%s\n"), a.CStr()) // 100-digit Fibonacci number + cstr := a.CStr() + c.Printf(c.Str("%s\n"), cstr) // 100-digit Fibonacci number + openssl.FreeCStr(cstr) } diff --git a/c/openssl/_wrap/openssl.c b/c/openssl/_wrap/openssl.c new file mode 100644 index 00000000..7cc5bbdb --- /dev/null +++ b/c/openssl/_wrap/openssl.c @@ -0,0 +1,5 @@ +#include + +void opensslFree(void *ptr) { + OPENSSL_free(ptr); +} diff --git a/c/openssl/bn.go b/c/openssl/bn.go index 4b0d483d..4a765b1c 100644 --- a/c/openssl/bn.go +++ b/c/openssl/bn.go @@ -98,6 +98,36 @@ func (*BIGNUM) Copy(b *BIGNUM) *BIGNUM { return nil } // llgo:link (*BIGNUM).Swap C.BN_swap func (*BIGNUM) Swap(b *BIGNUM) {} +// int BN_is_zero(const BIGNUM *a); +// +// llgo:link (*BIGNUM).IsZero C.BN_is_zero +func (*BIGNUM) IsZero() c.Int { return 0 } + +// void BN_zero_ex(BIGNUM *a); +// +// llgo:link (*BIGNUM).SetZero C.BN_zero_ex +func (*BIGNUM) SetZero() {} + +// int BN_is_one(const BIGNUM *a); +// +// llgo:link (*BIGNUM).IsOne C.BN_is_one +func (*BIGNUM) IsOne() c.Int { return 0 } + +// int BN_is_odd(const BIGNUM *a); +// +// llgo:link (*BIGNUM).IsOdd C.BN_is_odd +func (*BIGNUM) IsOdd() c.Int { return 0 } + +// int BN_abs_is_word(const BIGNUM *a, const BN_ULONG w); +// +// llgo:link (*BIGNUM).AbsIsWord C.BN_abs_is_word +func (*BIGNUM) AbsIsWord(w BN_ULONG) c.Int { return 0 } + +// int BN_is_word(const BIGNUM *a, const BN_ULONG w); +// +// llgo:link (*BIGNUM).IsWord C.BN_is_word +func (*BIGNUM) IsWord(w BN_ULONG) c.Int { return 0 } + // int BN_set_word(BIGNUM *a, BN_ULONG w); // // llgo:link (*BIGNUM).SetWord C.BN_set_word @@ -287,6 +317,11 @@ func (*BIGNUM) Rshift1(a *BIGNUM) c.Int { return 0 } // llgo:link (*BIGNUM).Exp C.BN_exp func (*BIGNUM) Exp(a, p *BIGNUM, ctx *BN_CTX) c.Int { return 0 } +// int BN_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m, BN_CTX *ctx); +// +// llgo:link (*BIGNUM).ModExp C.BN_mod_exp +func (*BIGNUM) ModExp(a, p, m *BIGNUM, ctx *BN_CTX) c.Int { return 0 } + // int BN_gcd(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx); // // llgo:link (*BIGNUM).Gcd C.BN_gcd diff --git a/c/openssl/openssl.go b/c/openssl/openssl.go index 0762808a..5002e851 100644 --- a/c/openssl/openssl.go +++ b/c/openssl/openssl.go @@ -25,13 +25,14 @@ import ( // ----------------------------------------------------------------------------- const ( + LLGoFiles = "$(pkg-config --cflags openssl): _wrap/openssl.c" LLGoPackage = "link: $(pkg-config --libs openssl); -lssl -lcrypto" ) -//go:linkname Free C.OPENSSL_free +//go:linkname Free C.opensslFree func Free(ptr unsafe.Pointer) -//go:linkname FreeCStr C.OPENSSL_free +//go:linkname FreeCStr C.opensslFree func FreeCStr(ptr *c.Char) // ----------------------------------------------------------------------------- diff --git a/internal/build/build.go b/internal/build/build.go index 7e732be9..668a307c 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -772,6 +772,7 @@ var hasAltPkg = map[string]none{ "internal/syscall/execenv": {}, "internal/syscall/unix": {}, "math": {}, + "math/big": {}, "math/cmplx": {}, "math/rand": {}, "reflect": {}, diff --git a/internal/lib/math/big/int.go b/internal/lib/math/big/int.go new file mode 100644 index 00000000..e970a174 --- /dev/null +++ b/internal/lib/math/big/int.go @@ -0,0 +1,395 @@ +/* + * 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 big + +// llgo:skipall +import ( + "github.com/goplus/llgo/c/openssl" +) + +// A Word represents a single digit of a multi-precision unsigned integer. +type Word openssl.BN_ULONG + +// ----------------------------------------------------------------------------- + +// TODO(xsw): share ctx +func ctxGet() *openssl.BN_CTX { + return openssl.BN_CTXNew() +} + +func ctxPut(ctx *openssl.BN_CTX) { + ctx.Free() +} + +// ----------------------------------------------------------------------------- + +type Int openssl.BIGNUM + +// Sign returns: +// +// -1 if x < 0 +// 0 if x == 0 +// +1 if x > 0 +func (x *Int) Sign() int { + a := (*openssl.BIGNUM)(x) + if a.IsNegative() != 0 { + return -1 + } else if a.IsZero() != 0 { + return 0 + } + return 1 +} + +// SetInt64 sets z to x and returns z. +func (z *Int) SetInt64(x int64) *Int { + a := (*openssl.BIGNUM)(z) + if x < 0 { + a.SetWord(openssl.BN_ULONG(-x)) + a.SetNegative(1) + } else { + a.SetWord(openssl.BN_ULONG(x)) + } + return z +} + +// SetUint64 sets z to x and returns z. +func (z *Int) SetUint64(x uint64) *Int { + (*openssl.BIGNUM)(z).SetWord(openssl.BN_ULONG(x)) + return z +} + +// NewInt allocates and returns a new Int set to x. +func NewInt(x int64) *Int { + z := (*Int)(openssl.BNNew()) + return z.SetInt64(x) +} + +/* +// Set sets z to x and returns z. +func (z *Int) Set(x *Int) *Int { +} + +// Bits provides raw (unchecked but fast) access to x by returning its +// absolute value as a little-endian Word slice. The result and x share +// the same underlying array. +// Bits is intended to support implementation of missing low-level Int +// functionality outside this package; it should be avoided otherwise. +func (x *Int) Bits() []Word { +} + +// SetBits provides raw (unchecked but fast) access to z by setting its +// value to abs, interpreted as a little-endian Word slice, and returning +// z. The result and abs share the same underlying array. +// SetBits is intended to support implementation of missing low-level Int +// functionality outside this package; it should be avoided otherwise. +func (z *Int) SetBits(abs []Word) *Int { +} + +// Abs sets z to |x| (the absolute value of x) and returns z. +func (z *Int) Abs(x *Int) *Int { +} + +// Neg sets z to -x and returns z. +func (z *Int) Neg(x *Int) *Int { +} +*/ + +// Add sets z to the sum x+y and returns z. +func (z *Int) Add(x, y *Int) *Int { + (*openssl.BIGNUM)(z).Add((*openssl.BIGNUM)(x), (*openssl.BIGNUM)(y)) + return z +} + +// Sub sets z to the difference x-y and returns z. +func (z *Int) Sub(x, y *Int) *Int { + (*openssl.BIGNUM)(z).Sub((*openssl.BIGNUM)(x), (*openssl.BIGNUM)(y)) + return z +} + +/* +// Mul sets z to the product x*y and returns z. +func (z *Int) Mul(x, y *Int) *Int { +} + +// MulRange sets z to the product of all integers +// in the range [a, b] inclusively and returns z. +// If a > b (empty range), the result is 1. +func (z *Int) MulRange(a, b int64) *Int { +} + +// Binomial sets z to the binomial coefficient C(n, k) and returns z. +func (z *Int) Binomial(n, k int64) *Int { +} + +// Quo sets z to the quotient x/y for y != 0 and returns z. +// If y == 0, a division-by-zero run-time panic occurs. +// Quo implements truncated division (like Go); see QuoRem for more details. +func (z *Int) Quo(x, y *Int) *Int { +} + +// Rem sets z to the remainder x%y for y != 0 and returns z. +// If y == 0, a division-by-zero run-time panic occurs. +// Rem implements truncated modulus (like Go); see QuoRem for more details. +func (z *Int) Rem(x, y *Int) *Int { +} + +// QuoRem sets z to the quotient x/y and r to the remainder x%y +// and returns the pair (z, r) for y != 0. +// If y == 0, a division-by-zero run-time panic occurs. +// +// QuoRem implements T-division and modulus (like Go): +// +// q = x/y with the result truncated to zero +// r = x - y*q +// +// (See Daan Leijen, “Division and Modulus for Computer Scientists”.) +// See DivMod for Euclidean division and modulus (unlike Go). +func (z *Int) QuoRem(x, y, r *Int) (*Int, *Int) { +} + +// Div sets z to the quotient x/y for y != 0 and returns z. +// If y == 0, a division-by-zero run-time panic occurs. +// Div implements Euclidean division (unlike Go); see DivMod for more details. +func (z *Int) Div(x, y *Int) *Int { +} + +// Mod sets z to the modulus x%y for y != 0 and returns z. +// If y == 0, a division-by-zero run-time panic occurs. +// Mod implements Euclidean modulus (unlike Go); see DivMod for more details. +func (z *Int) Mod(x, y *Int) *Int { +} + +// DivMod sets z to the quotient x div y and m to the modulus x mod y +// and returns the pair (z, m) for y != 0. +// If y == 0, a division-by-zero run-time panic occurs. +// +// DivMod implements Euclidean division and modulus (unlike Go): +// +// q = x div y such that +// m = x - y*q with 0 <= m < |y| +// +// (See Raymond T. Boute, “The Euclidean definition of the functions +// div and mod”. ACM Transactions on Programming Languages and +// Systems (TOPLAS), 14(2):127-144, New York, NY, USA, 4/1992. +// ACM press.) +// See QuoRem for T-division and modulus (like Go). +func (z *Int) DivMod(x, y, m *Int) (*Int, *Int) { +} +*/ + +// Cmp compares x and y and returns: +// +// -1 if x < y +// 0 if x == y +// +1 if x > y +func (x *Int) Cmp(y *Int) (r int) { + return int((*openssl.BIGNUM)(x).Cmp((*openssl.BIGNUM)(y))) +} + +// CmpAbs compares the absolute values of x and y and returns: +// +// -1 if |x| < |y| +// 0 if |x| == |y| +// +1 if |x| > |y| +func (x *Int) CmpAbs(y *Int) int { + return int((*openssl.BIGNUM)(x).Ucmp((*openssl.BIGNUM)(y))) +} + +/* +// Int64 returns the int64 representation of x. +// If x cannot be represented in an int64, the result is undefined. +func (x *Int) Int64() int64 { +} + +// Uint64 returns the uint64 representation of x. +// If x cannot be represented in a uint64, the result is undefined. +func (x *Int) Uint64() uint64 { +} + +// IsInt64 reports whether x can be represented as an int64. +func (x *Int) IsInt64() bool { +} + +// IsUint64 reports whether x can be represented as a uint64. +func (x *Int) IsUint64() bool { +} + +// Float64 returns the float64 value nearest x, +// and an indication of any rounding that occurred. +// TODO(xsw): +// func (x *Int) Float64() (float64, Accuracy) + +// SetString sets z to the value of s, interpreted in the given base, +// and returns z and a boolean indicating success. The entire string +// (not just a prefix) must be valid for success. If SetString fails, +// the value of z is undefined but the returned value is nil. +// +// The base argument must be 0 or a value between 2 and MaxBase. +// For base 0, the number prefix determines the actual base: A prefix of +// “0b” or “0B” selects base 2, “0”, “0o” or “0O” selects base 8, +// and “0x” or “0X” selects base 16. Otherwise, the selected base is 10 +// and no prefix is accepted. +// +// For bases <= 36, lower and upper case letters are considered the same: +// The letters 'a' to 'z' and 'A' to 'Z' represent digit values 10 to 35. +// For bases > 36, the upper case letters 'A' to 'Z' represent the digit +// values 36 to 61. +// +// For base 0, an underscore character “_” may appear between a base +// prefix and an adjacent digit, and between successive digits; such +// underscores do not change the value of the number. +// Incorrect placement of underscores is reported as an error if there +// are no other errors. If base != 0, underscores are not recognized +// and act like any other character that is not a valid digit. +func (z *Int) SetString(s string, base int) (*Int, bool) { +} + +// SetBytes interprets buf as the bytes of a big-endian unsigned +// integer, sets z to that value, and returns z. +func (z *Int) SetBytes(buf []byte) *Int { +} + +// Bytes returns the absolute value of x as a big-endian byte slice. +// +// To use a fixed length slice, or a preallocated one, use FillBytes. +func (x *Int) Bytes() []byte { +} + +// FillBytes sets buf to the absolute value of x, storing it as a zero-extended +// big-endian byte slice, and returns buf. +// +// If the absolute value of x doesn't fit in buf, FillBytes will panic. +func (x *Int) FillBytes(buf []byte) []byte { +} + +// BitLen returns the length of the absolute value of x in bits. +// The bit length of 0 is 0. +func (x *Int) BitLen() int { +} + +// TrailingZeroBits returns the number of consecutive least significant zero +// bits of |x|. +func (x *Int) TrailingZeroBits() uint { +} +*/ + +// Exp sets z = x**y mod |m| (i.e. the sign of m is ignored), and returns z. +// If m == nil or m == 0, z = x**y unless y <= 0 then z = 1. If m != 0, y < 0, +// and x and m are not relatively prime, z is unchanged and nil is returned. +// +// Modular exponentiation of inputs of a particular size is not a +// cryptographically constant-time operation. +func (z *Int) Exp(x, y, m *Int) *Int { + ctx := ctxGet() + mbn := (*openssl.BIGNUM)(m) + if mbn == nil || mbn.IsZero() != 0 { + (*openssl.BIGNUM)(z).Exp((*openssl.BIGNUM)(x), (*openssl.BIGNUM)(y), ctx) + } else { + (*openssl.BIGNUM)(z).ModExp((*openssl.BIGNUM)(x), (*openssl.BIGNUM)(y), mbn, ctx) + } + ctxPut(ctx) + return z +} + +/* +// GCD sets z to the greatest common divisor of a and b and returns z. +// If x or y are not nil, GCD sets their value such that z = a*x + b*y. +// +// a and b may be positive, zero or negative. (Before Go 1.14 both had +// to be > 0.) Regardless of the signs of a and b, z is always >= 0. +// +// If a == b == 0, GCD sets z = x = y = 0. +// +// If a == 0 and b != 0, GCD sets z = |b|, x = 0, y = sign(b) * 1. +// +// If a != 0 and b == 0, GCD sets z = |a|, x = sign(a) * 1, y = 0. +func (z *Int) GCD(x, y, a, b *Int) *Int { +} + +// Rand sets z to a pseudo-random number in [0, n) and returns z. +// +// As this uses the math/rand package, it must not be used for +// security-sensitive work. Use crypto/rand.Int instead. +func (z *Int) Rand(rnd *rand.Rand, n *Int) *Int { +} + +// ModInverse sets z to the multiplicative inverse of g in the ring ℤ/nℤ +// and returns z. If g and n are not relatively prime, g has no multiplicative +// inverse in the ring ℤ/nℤ. In this case, z is unchanged and the return value +// is nil. If n == 0, a division-by-zero run-time panic occurs. +func (z *Int) ModInverse(g, n *Int) *Int { +} + +// Jacobi returns the Jacobi symbol (x/y), either +1, -1, or 0. +// The y argument must be an odd integer. +func Jacobi(x, y *Int) int { +} + +// ModSqrt sets z to a square root of x mod p if such a square root exists, and +// returns z. The modulus p must be an odd prime. If x is not a square mod p, +// ModSqrt leaves z unchanged and returns nil. This function panics if p is +// not an odd integer, its behavior is undefined if p is odd but not prime. +func (z *Int) ModSqrt(x, p *Int) *Int { +} + +// Lsh sets z = x << n and returns z. +func (z *Int) Lsh(x *Int, n uint) *Int { +} + +// Rsh sets z = x >> n and returns z. +func (z *Int) Rsh(x *Int, n uint) *Int { +} + +// Bit returns the value of the i'th bit of x. That is, it +// returns (x>>i)&1. The bit index i must be >= 0. +func (x *Int) Bit(i int) uint { +} + +// SetBit sets z to x, with x's i'th bit set to b (0 or 1). +// That is, if b is 1 SetBit sets z = x | (1 << i); +// if b is 0 SetBit sets z = x &^ (1 << i). If b is not 0 or 1, +// SetBit will panic. +func (z *Int) SetBit(x *Int, i int, b uint) *Int { +} + +// And sets z = x & y and returns z. +func (z *Int) And(x, y *Int) *Int { +} + +// AndNot sets z = x &^ y and returns z. +func (z *Int) AndNot(x, y *Int) *Int { +} + +// Or sets z = x | y and returns z. +func (z *Int) Or(x, y *Int) *Int { +} + +// Xor sets z = x ^ y and returns z. +func (z *Int) Xor(x, y *Int) *Int { +} + +// Not sets z = ^x and returns z. +func (z *Int) Not(x *Int) *Int { +} + +// Sqrt sets z to ⌊√x⌋, the largest integer such that z² ≤ x, and returns z. +// It panics if x is negative. +func (z *Int) Sqrt(x *Int) *Int { +} +*/ + +// ----------------------------------------------------------------------------- diff --git a/internal/lib/math/big/intconv.go b/internal/lib/math/big/intconv.go new file mode 100644 index 00000000..4b946be3 --- /dev/null +++ b/internal/lib/math/big/intconv.go @@ -0,0 +1,70 @@ +/* + * 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 big + +import ( + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/openssl" +) + +/* +// Text returns the string representation of x in the given base. +// Base must be between 2 and 62, inclusive. The result uses the +// lower-case letters 'a' to 'z' for digit values 10 to 35, and +// the upper-case letters 'A' to 'Z' for digit values 36 to 61. +// No prefix (such as "0x") is added to the string. If x is a nil +// pointer it returns "". +func (x *Int) Text(base int) string { +} + +// Append appends the string representation of x, as generated by +// x.Text(base), to buf and returns the extended buffer. +func (x *Int) Append(buf []byte, base int) []byte { +} +*/ + +// String returns the decimal representation of x as generated by +// x.Text(10). +func (x *Int) String() string { + // TODO(xsw): can optimize it? + cstr := (*openssl.BIGNUM)(x).CStr() + ret := c.GoString(cstr) + openssl.FreeCStr(cstr) + return ret +} + +/* +// Format implements fmt.Formatter. It accepts the formats +// 'b' (binary), 'o' (octal with 0 prefix), 'O' (octal with 0o prefix), +// 'd' (decimal), 'x' (lowercase hexadecimal), and +// 'X' (uppercase hexadecimal). +// Also supported are the full suite of package fmt's format +// flags for integral types, including '+' and ' ' for sign +// control, '#' for leading zero in octal and for hexadecimal, +// a leading "0x" or "0X" for "%#x" and "%#X" respectively, +// specification of minimum digits precision, output field +// width, space or zero padding, and '-' for left or right +// justification. +func (x *Int) Format(s fmt.State, ch rune) { +} + +// Scan is a support routine for fmt.Scanner; it sets z to the value of +// the scanned number. It accepts the formats 'b' (binary), 'o' (octal), +// 'd' (decimal), 'x' (lowercase hexadecimal), and 'X' (uppercase hexadecimal). +func (z *Int) Scan(s fmt.ScanState, ch rune) error { +} +*/ From 69f8d1b7176dceb7f8e20d87a1b57b10a48abfd3 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Thu, 1 Aug 2024 08:46:37 +0800 Subject: [PATCH 142/152] README: math/big --- README.md | 1 + internal/lib/math/big/int.go | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 878879a3..0c990e7a 100644 --- a/README.md +++ b/README.md @@ -269,6 +269,7 @@ Here are the Go packages that can be imported correctly: * [unicode/utf8](https://pkg.go.dev/unicode/utf8) * [unicode/utf16](https://pkg.go.dev/unicode/utf16) * [math](https://pkg.go.dev/math) +* [math/big](https://pkg.go.dev/math/big) (partially) * [math/bits](https://pkg.go.dev/math/bits) * [math/cmplx](https://pkg.go.dev/math/cmplx) * [math/rand](https://pkg.go.dev/math/rand) diff --git a/internal/lib/math/big/int.go b/internal/lib/math/big/int.go index e970a174..f568938b 100644 --- a/internal/lib/math/big/int.go +++ b/internal/lib/math/big/int.go @@ -62,13 +62,16 @@ func (z *Int) SetInt64(x int64) *Int { a.SetNegative(1) } else { a.SetWord(openssl.BN_ULONG(x)) + a.SetNegative(0) } return z } // SetUint64 sets z to x and returns z. func (z *Int) SetUint64(x uint64) *Int { - (*openssl.BIGNUM)(z).SetWord(openssl.BN_ULONG(x)) + a := (*openssl.BIGNUM)(z) + a.SetWord(openssl.BN_ULONG(x)) + a.SetNegative(0) return z } From 47a05d0ea21a622e89571145a69d30f9a4264cb3 Mon Sep 17 00:00:00 2001 From: luoliwoshang <2643523683@qq.com> Date: Thu, 1 Aug 2024 09:53:40 +0800 Subject: [PATCH 143/152] doc/c:refine symbol visibility description --- doc/How-to-support-a-C&C++-Library.md | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/doc/How-to-support-a-C&C++-Library.md b/doc/How-to-support-a-C&C++-Library.md index e691cb42..14ac8bd6 100644 --- a/doc/How-to-support-a-C&C++-Library.md +++ b/doc/How-to-support-a-C&C++-Library.md @@ -1,5 +1,21 @@ How to support a C/C++ Library ===== +## Symbol Visibility +When llgo needs to link C or C++ libraries, symbol visibility is a crucial concept. It determines how C/C++ functions and methods are linked and utilized within llgo. Symbol visibility significantly impacts the handling of symbols in llgo bindings: visible symbols can typically be directly linked, while invisible symbols require wrapper functions. + +The accessibility of symbols, particularly destructors, in dynamic libraries depends on their definition method. For instance, destructors that are explicitly declared in header files and implemented as non-inline functions in .cpp files typically appear in the dynamic library's symbol table, making them visible and directly linkable. + +**Visible Symbols:** These are symbols that can be found in the dynamic library. They typically represent public API functions and methods. + +* Example: A class constructor explicitly declared in the header and implemented in a .cpp file. + +* Example: A non-inline destructor declared in the header and implemented in a .cpp file. + +**Invisible Symbols:** These are symbols that cannot be found in the dynamic library. This may include inline functions, templates, or certain constructors and destructors. + +* Example: A default constructor not explicitly declared. + +* Example: An inline destructor or a compiler-generated default destructor. ## Support a C Library @@ -308,7 +324,7 @@ func ParseError() c.Int ``` - Constructor - - Constructor is explicitly declared in the class (can find the corresponding symbol in the dynamic library): + - Explicitly Constructor: ```cpp class INIReader { @@ -330,15 +346,14 @@ func ParseError() c.Int return } ``` - - Constructor is not explicitly declared in the class (cannot find the corresponding symbol in the dynamic library) + - Implicitly Constructor In typical implementations of the inih library, directly invoking implicit constructors to instantiate reader objects is not recommended. For detailed examples of how bindings effectively handle non-exported symbols, please refer to the [Templates and Inlines](#templates-and-inlines) section. - Destructor - The accessibility of destructors in dynamic libraries depends on their definition method. Destructors that are explicitly declared in header files and implemented as non-inline functions in .cpp files typically appear in the dynamic library's symbol table. These destructors can be directly linked, consistent with the linking method of general class methods (see "Class Methods" section). - - For destructors that do not meet these conditions (such as those explicitly declared in header files but implemented inline in .cpp files) and consequently do not appear in the dynamic library's symbol table, a wrapper layer implementation is required. This wrapper in the C++ wrapper file (e.g., cppWrap.cpp) looks like: + Explicitly declared and non-inline destructors can be directly linked, consistent with the linking method of general class methods (see "Class Methods" section). For destructors that do not appear in the dynamic library's symbol table, a wrapper layer implementation is required. This wrapper in the C++ wrapper file (e.g., cppWrap.cpp) looks like: + ```cpp extern "C" { void INIReaderDispose(INIReader* r) { From ceac95c81aa8ab08530b03c3d8ac5d5e081c82be Mon Sep 17 00:00:00 2001 From: hackerchai Date: Tue, 30 Jul 2024 15:18:54 +0800 Subject: [PATCH 144/152] fix(c/libuv): Add libuv fs struct new func Signed-off-by: hackerchai --- c/libuv/_wrap/fs.c | 6 ++++++ c/libuv/fs.go | 3 +++ c/libuv/libuv.go | 1 + 3 files changed, 10 insertions(+) create mode 100644 c/libuv/_wrap/fs.c diff --git a/c/libuv/_wrap/fs.c b/c/libuv/_wrap/fs.c new file mode 100644 index 00000000..6728e4ee --- /dev/null +++ b/c/libuv/_wrap/fs.c @@ -0,0 +1,6 @@ +#include + +uv_fs_t uv_fs_new() { + uv_fs_t req; + return req; +} \ No newline at end of file diff --git a/c/libuv/fs.go b/c/libuv/fs.go index 62a3c193..b29c815b 100644 --- a/c/libuv/fs.go +++ b/c/libuv/fs.go @@ -106,6 +106,9 @@ type FsPollCb func(handle *FsPoll, status c.Int, events c.Int) /* Fs related function and method */ +//go:linkname FsNew C.uv_fs_new +func FsNew() Fs + //go:linkname FsGetType C.uv_fs_get_type func FsGetType(req *Fs) FsType diff --git a/c/libuv/libuv.go b/c/libuv/libuv.go index 9465f00e..b11d6ad9 100644 --- a/c/libuv/libuv.go +++ b/c/libuv/libuv.go @@ -9,6 +9,7 @@ import ( const ( LLGoPackage = "link: $(pkg-config --libs libuv); -luv" + LLGoFiles = "$(pkg-config --cflags libuv): _wrap/fs.c" ) // ---------------------------------------------- From acd09d24d541f7a5f43fcefa59010dac6327c664 Mon Sep 17 00:00:00 2001 From: hackerchai Date: Tue, 30 Jul 2024 15:19:31 +0800 Subject: [PATCH 145/152] fix(c/libuv): Fix async_fs demo return 255 error & pointer not allocated error Signed-off-by: hackerchai fix(c/libuv): Mv LLGoFiles declaration Signed-off-by: hackerchai fix(c/libuv/demo): Fix return 255 error & pointer not allocated error Signed-off-by: hackerchai refactor(c/libuv): Rewrite FsNew() logic Signed-off-by: hackerchai --- c/libuv/_demo/async_fs/async_fs.go | 63 +++++++++++++++++++++--------- c/libuv/_wrap/fs.c | 7 ++-- c/libuv/fs.go | 8 +++- c/libuv/libuv.go | 1 - 4 files changed, 55 insertions(+), 24 deletions(-) diff --git a/c/libuv/_demo/async_fs/async_fs.go b/c/libuv/_demo/async_fs/async_fs.go index 5c8b42e6..760cbe63 100644 --- a/c/libuv/_demo/async_fs/async_fs.go +++ b/c/libuv/_demo/async_fs/async_fs.go @@ -12,12 +12,12 @@ const BUFFER_SIZE = 1024 var ( loop *libuv.Loop - openReq libuv.Fs - readReq libuv.Fs - closeReq libuv.Fs + openReq *libuv.Fs + closeReq *libuv.Fs buffer [BUFFER_SIZE]c.Char iov libuv.Buf + file libuv.File ) func main() { @@ -27,11 +27,23 @@ func main() { // Initialize the loop loop = libuv.DefaultLoop() + // Initialize the requests + openReq = libuv.FsNew() + closeReq = libuv.FsNew() + if openReq == nil || closeReq == nil { + c.Fprintf(c.Stderr, c.Str("Error in FsNew\n")) + return + } + // Open the file - libuv.FsOpen(loop, &openReq, c.Str("example.txt"), os.O_RDONLY, 0, onOpen) + libuv.FsOpen(loop, openReq, c.Str("example.txt"), os.O_RDONLY, 0, onOpen) // Run the loop - libuv.Run(loop, libuv.RUN_DEFAULT) + result := libuv.Run(loop, libuv.RUN_DEFAULT) + + if result != 0 { + c.Fprintf(c.Stderr, c.Str("Error in Run: %s\n"), libuv.Strerror(libuv.Errno(result))) + } // Cleanup defer cleanup() @@ -40,32 +52,45 @@ func main() { func onOpen(req *libuv.Fs) { // Check for errors if libuv.FsGetResult(req) < 0 { - c.Fprintf(c.Stderr, c.Str("Error opening file: %s\n"), libuv.Strerror(libuv.Errno(libuv.LoopClose(loop)))) + c.Fprintf(c.Stderr, c.Str("Error opening file: %s\n"), libuv.Strerror(libuv.Errno(libuv.FsGetResult(req)))) libuv.LoopClose(loop) return } + + // Store the file descriptor + file = libuv.File(libuv.FsGetResult(req)) + // Init buffer iov = libuv.InitBuf((*c.Char)(unsafe.Pointer(&buffer[0])), c.Uint(unsafe.Sizeof(buffer))) + // Read the file - readRes := libuv.FsRead(loop, &readReq, libuv.File(libuv.FsGetResult(req)), &iov, 1, -1, onRead) + readFile() + +} + +func readFile() { + // Initialize the request + readReq := libuv.FsNew() + + // Read the file + readRes := libuv.FsRead(loop, readReq, file, &iov, 1, -1, onRead) if readRes != 0 { c.Printf(c.Str("Error in FsRead: %s (code: %d)\n"), libuv.Strerror(libuv.Errno(readRes)), readRes) + libuv.FsReqCleanup(readReq) libuv.LoopClose(loop) - return } } func onRead(req *libuv.Fs) { + // Cleanup the request + defer libuv.FsReqCleanup(req) // Check for errors if libuv.FsGetResult(req) < 0 { c.Fprintf(c.Stderr, c.Str("Read error: %s\n"), libuv.Strerror(libuv.Errno(libuv.FsGetResult(req)))) - libuv.LoopClose(loop) } else if libuv.FsGetResult(req) == 0 { - c.Printf(c.Str("EOF\n")) // Close the file - closeRes := libuv.FsClose(loop, &closeReq, libuv.File(libuv.FsGetResult(&openReq)), onClose) + closeRes := libuv.FsClose(loop, closeReq, libuv.File(libuv.FsGetResult(openReq)), onClose) if closeRes != 0 { - // Print the content c.Printf(c.Str("Error in FsClose: %s (code: %d)\n"), libuv.Strerror(libuv.Errno(closeRes)), closeRes) libuv.LoopClose(loop) return @@ -73,7 +98,8 @@ func onRead(req *libuv.Fs) { } else { c.Printf(c.Str("Read %d bytes\n"), libuv.FsGetResult(req)) c.Printf(c.Str("Read content: %.*s\n"), libuv.FsGetResult(req), (*c.Char)(unsafe.Pointer(&buffer[0]))) - libuv.LoopClose(loop) + // Read the file again + readFile() } } @@ -84,14 +110,15 @@ func onClose(req *libuv.Fs) { } else { c.Printf(c.Str("\nFile closed successfully.\n")) } - libuv.LoopClose(loop) } func cleanup() { // Cleanup the requests - libuv.FsReqCleanup(&openReq) - libuv.FsReqCleanup(&readReq) - libuv.FsReqCleanup(&closeReq) + libuv.FsReqCleanup(openReq) + libuv.FsReqCleanup(closeReq) // Close the loop - libuv.LoopClose(loop) + result := libuv.LoopClose(loop) + if result != 0 { + c.Fprintf(c.Stderr, c.Str("Error in LoopClose: %s\n"), libuv.Strerror(libuv.Errno(result))) + } } diff --git a/c/libuv/_wrap/fs.c b/c/libuv/_wrap/fs.c index 6728e4ee..e3469edc 100644 --- a/c/libuv/_wrap/fs.c +++ b/c/libuv/_wrap/fs.c @@ -1,6 +1,7 @@ +#include #include -uv_fs_t uv_fs_new() { - uv_fs_t req; - return req; +uv_fs_t* uv_fs_new() { + uv_fs_t* req = malloc(sizeof(uv_fs_t)); + return req; } \ No newline at end of file diff --git a/c/libuv/fs.go b/c/libuv/fs.go index b29c815b..1c4dfc25 100644 --- a/c/libuv/fs.go +++ b/c/libuv/fs.go @@ -6,6 +6,10 @@ import ( "github.com/goplus/llgo/c" ) +const ( + LLGoFiles = "$(pkg-config --cflags libuv): _wrap/fs.c" +) + const ( FS_UNKNOWN FsType = -1 FS_CUSTOM FsType = 0 @@ -69,7 +73,7 @@ type File c.Int /* Handle types. */ type Fs struct { - Unused [0]byte + Unused [440]byte } type FsEvent struct { @@ -107,7 +111,7 @@ type FsPollCb func(handle *FsPoll, status c.Int, events c.Int) /* Fs related function and method */ //go:linkname FsNew C.uv_fs_new -func FsNew() Fs +func FsNew() *Fs //go:linkname FsGetType C.uv_fs_get_type func FsGetType(req *Fs) FsType diff --git a/c/libuv/libuv.go b/c/libuv/libuv.go index b11d6ad9..9465f00e 100644 --- a/c/libuv/libuv.go +++ b/c/libuv/libuv.go @@ -9,7 +9,6 @@ import ( const ( LLGoPackage = "link: $(pkg-config --libs libuv); -luv" - LLGoFiles = "$(pkg-config --cflags libuv): _wrap/fs.c" ) // ---------------------------------------------- From f253e4fabec7106573b43c44425b012bc3890cf6 Mon Sep 17 00:00:00 2001 From: hackerchai Date: Thu, 1 Aug 2024 10:27:59 +0800 Subject: [PATCH 146/152] Revert "fix(c/libuv): Add libuv fs struct new func" This reverts commit 5fdb0a9634b5ecc29ddd50b6e5cce9938bcb7934. # Conflicts: # c/libuv/_wrap/fs.c # c/libuv/fs.go --- c/libuv/_wrap/fs.c | 7 ------- c/libuv/fs.go | 7 ------- 2 files changed, 14 deletions(-) delete mode 100644 c/libuv/_wrap/fs.c diff --git a/c/libuv/_wrap/fs.c b/c/libuv/_wrap/fs.c deleted file mode 100644 index e3469edc..00000000 --- a/c/libuv/_wrap/fs.c +++ /dev/null @@ -1,7 +0,0 @@ -#include -#include - -uv_fs_t* uv_fs_new() { - uv_fs_t* req = malloc(sizeof(uv_fs_t)); - return req; -} \ No newline at end of file diff --git a/c/libuv/fs.go b/c/libuv/fs.go index 1c4dfc25..9bb8f57f 100644 --- a/c/libuv/fs.go +++ b/c/libuv/fs.go @@ -6,10 +6,6 @@ import ( "github.com/goplus/llgo/c" ) -const ( - LLGoFiles = "$(pkg-config --cflags libuv): _wrap/fs.c" -) - const ( FS_UNKNOWN FsType = -1 FS_CUSTOM FsType = 0 @@ -110,9 +106,6 @@ type FsPollCb func(handle *FsPoll, status c.Int, events c.Int) /* Fs related function and method */ -//go:linkname FsNew C.uv_fs_new -func FsNew() *Fs - //go:linkname FsGetType C.uv_fs_get_type func FsGetType(req *Fs) FsType From 5dd5494f93ea3bfb6f001b8618e3b010e944108b Mon Sep 17 00:00:00 2001 From: hackerchai Date: Thu, 1 Aug 2024 10:52:59 +0800 Subject: [PATCH 147/152] refactor(c/libuv): Adapt libuv.Fs struct --- c/libuv/_demo/async_fs/async_fs.go | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/c/libuv/_demo/async_fs/async_fs.go b/c/libuv/_demo/async_fs/async_fs.go index 760cbe63..14b50ecd 100644 --- a/c/libuv/_demo/async_fs/async_fs.go +++ b/c/libuv/_demo/async_fs/async_fs.go @@ -12,8 +12,8 @@ const BUFFER_SIZE = 1024 var ( loop *libuv.Loop - openReq *libuv.Fs - closeReq *libuv.Fs + openReq libuv.Fs + closeReq libuv.Fs buffer [BUFFER_SIZE]c.Char iov libuv.Buf @@ -27,16 +27,8 @@ func main() { // Initialize the loop loop = libuv.DefaultLoop() - // Initialize the requests - openReq = libuv.FsNew() - closeReq = libuv.FsNew() - if openReq == nil || closeReq == nil { - c.Fprintf(c.Stderr, c.Str("Error in FsNew\n")) - return - } - // Open the file - libuv.FsOpen(loop, openReq, c.Str("example.txt"), os.O_RDONLY, 0, onOpen) + libuv.FsOpen(loop, &openReq, c.Str("example.txt"), os.O_RDONLY, 0, onOpen) // Run the loop result := libuv.Run(loop, libuv.RUN_DEFAULT) @@ -69,14 +61,14 @@ func onOpen(req *libuv.Fs) { } func readFile() { - // Initialize the request - readReq := libuv.FsNew() + // Initialize the request every time + var readReq libuv.Fs // Read the file - readRes := libuv.FsRead(loop, readReq, file, &iov, 1, -1, onRead) + readRes := libuv.FsRead(loop, &readReq, file, &iov, 1, -1, onRead) if readRes != 0 { c.Printf(c.Str("Error in FsRead: %s (code: %d)\n"), libuv.Strerror(libuv.Errno(readRes)), readRes) - libuv.FsReqCleanup(readReq) + libuv.FsReqCleanup(&readReq) libuv.LoopClose(loop) } } @@ -89,7 +81,7 @@ func onRead(req *libuv.Fs) { c.Fprintf(c.Stderr, c.Str("Read error: %s\n"), libuv.Strerror(libuv.Errno(libuv.FsGetResult(req)))) } else if libuv.FsGetResult(req) == 0 { // Close the file - closeRes := libuv.FsClose(loop, closeReq, libuv.File(libuv.FsGetResult(openReq)), onClose) + closeRes := libuv.FsClose(loop, &closeReq, libuv.File(libuv.FsGetResult(&openReq)), onClose) if closeRes != 0 { c.Printf(c.Str("Error in FsClose: %s (code: %d)\n"), libuv.Strerror(libuv.Errno(closeRes)), closeRes) libuv.LoopClose(loop) @@ -114,8 +106,8 @@ func onClose(req *libuv.Fs) { func cleanup() { // Cleanup the requests - libuv.FsReqCleanup(openReq) - libuv.FsReqCleanup(closeReq) + libuv.FsReqCleanup(&openReq) + libuv.FsReqCleanup(&closeReq) // Close the loop result := libuv.LoopClose(loop) if result != 0 { From 688d15342756b66508aa0659f2bf920c0b087c45 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Thu, 1 Aug 2024 22:55:17 +0800 Subject: [PATCH 148/152] c/openssl: bio, pem, rsa --- c/openssl/bio.go | 61 ++++++++++++ c/openssl/pem.go | 37 +++++++ c/openssl/rsa.go | 247 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 344 insertions(+), 1 deletion(-) create mode 100644 c/openssl/bio.go create mode 100644 c/openssl/pem.go diff --git a/c/openssl/bio.go b/c/openssl/bio.go new file mode 100644 index 00000000..f03fcb83 --- /dev/null +++ b/c/openssl/bio.go @@ -0,0 +1,61 @@ +/* + * 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 openssl + +import ( + "unsafe" + + "github.com/goplus/llgo/c" +) + +// ----------------------------------------------------------------------------- + +type BIO struct { + Unused [0]byte +} + +// BIO *BIO_new_mem_buf(const void *buf, int len); +// +//go:linkname BIONewMemBuf C.BIO_new_mem_buf +func BIONewMemBuf(buf unsafe.Pointer, len c.Int) *BIO + +// int BIO_free(BIO *a); +// +// llgo:link (*BIO).Free C.BIO_free +func (*BIO) Free() c.Int { return 0 } + +// int BIO_up_ref(BIO *a); +// +// llgo:link (*BIO).UpRef C.BIO_up_ref +func (*BIO) UpRef() c.Int { return 0 } + +// int BIO_read_ex(BIO *b, void *data, size_t dlen, size_t *readbytes); +// +// llgo:link (*BIO).ReadEx C.BIO_read_ex +func (*BIO) ReadEx(data unsafe.Pointer, dlen uintptr, readbytes *uintptr) c.Int { return 0 } + +// int BIO_write(BIO *b, const void *data, int dlen); +// +// llgo:link (*BIO).Write C.BIO_write +func (*BIO) Write(data unsafe.Pointer, dlen c.Int) c.Int { return 0 } + +// int BIO_write_ex(BIO *b, const void *data, size_t dlen, size_t *written); +// +// llgo:link (*BIO).WriteEx C.BIO_write_ex +func (*BIO) WriteEx(data unsafe.Pointer, dlen uintptr, written *uintptr) c.Int { return 0 } + +// ----------------------------------------------------------------------------- diff --git a/c/openssl/pem.go b/c/openssl/pem.go new file mode 100644 index 00000000..fb2f360b --- /dev/null +++ b/c/openssl/pem.go @@ -0,0 +1,37 @@ +/* + * 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 openssl + +import ( + "unsafe" + + "github.com/goplus/llgo/c" +) + +// ----------------------------------------------------------------------------- + +// typedef int (*pem_password_cb)(char *buf, int size, int rwflag, void *userdata); +// +// llgo:type C +type PemPasswordCb func(buf *c.Char, size, rwflag c.Int, userdata unsafe.Pointer) c.Int + +// RSA *PEM_read_bio_RSAPrivateKey(BIO *bp, RSA **x, pem_password_cb *cb, void *u); +// +//go:linkname PEMReadBioRSAPrivateKey C.PEM_read_bio_RSAPrivateKey +func PEMReadBioRSAPrivateKey(bp *BIO, x **RSA, cb PemPasswordCb, u unsafe.Pointer) *RSA + +// ----------------------------------------------------------------------------- diff --git a/c/openssl/rsa.go b/c/openssl/rsa.go index 378ba424..23c60f28 100644 --- a/c/openssl/rsa.go +++ b/c/openssl/rsa.go @@ -24,6 +24,157 @@ import ( // ----------------------------------------------------------------------------- +type RSA_METHOD struct { + Unused [0]byte +} + +// OSSL_DEPRECATEDIN_3_0 RSA_METHOD *RSA_meth_new(const char *name, int flags); +// +//go:linkname RSAMethNew C.RSA_meth_new +func RSAMethNew(name *byte, flags c.Int) *RSA_METHOD + +// OSSL_DEPRECATEDIN_3_0 void RSA_meth_free(RSA_METHOD *meth); +// +// llgo:link (*RSA_METHOD).Free C.RSA_meth_free +func (*RSA_METHOD) Free() {} + +// OSSL_DEPRECATEDIN_3_0 +// int RSA_meth_set_init(RSA_METHOD *rsa, int (*init) (RSA *rsa)); +// +// llgo:link (*RSA_METHOD).SetInit C.RSA_meth_set_init +func (*RSA_METHOD) SetInit(init func(rsa *RSA) c.Int) c.Int { return 0 } + +// OSSL_DEPRECATEDIN_3_0 +// int RSA_meth_set_finish(RSA_METHOD *rsa, int (*finish) (RSA *rsa)); +// +// llgo:link (*RSA_METHOD).SetFinish C.RSA_meth_set_finish +func (*RSA_METHOD) SetFinish(finish func(rsa *RSA) c.Int) c.Int { return 0 } + +/* +OSSL_DEPRECATEDIN_3_0 +int RSA_meth_set_mod_exp(RSA_METHOD *rsa, + int (*mod_exp) (BIGNUM *r0, const BIGNUM *i, RSA *rsa, + BN_CTX *ctx)); +*/ +// llgo:link (*RSA_METHOD).SetModExp C.RSA_meth_set_mod_exp +func (*RSA_METHOD) SetModExp(modExp func( + r0 *BIGNUM, i *BIGNUM, rsa *RSA, ctx *BN_CTX) c.Int) c.Int { + return 0 +} + +/* +OSSL_DEPRECATEDIN_3_0 +int RSA_meth_set_bn_mod_exp(RSA_METHOD *rsa, + int (*bn_mod_exp) (BIGNUM *r, + const BIGNUM *a, + const BIGNUM *p, + const BIGNUM *m, + BN_CTX *ctx, + BN_MONT_CTX *m_ctx)); +//-llgo:link (*RSA_METHOD).SetBnModExp C.RSA_meth_set_bn_mod_exp +func (*RSA_METHOD) SetBnModExp(bnModExp func( + r *BIGNUM, a *BIGNUM, p *BIGNUM, m *BIGNUM, ctx *BN_CTX, mCtx *BN_MONT_CTX) c.Int) c.Int { + return 0 +} +*/ + +/* +OSSL_DEPRECATEDIN_3_0 +int RSA_meth_set_pub_enc(RSA_METHOD *rsa, + int (*pub_enc) (int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, + int padding)); +*/ +// llgo:link (*RSA_METHOD).SetPubEnc C.RSA_meth_set_pub_enc +func (*RSA_METHOD) SetPubEnc(pubEnc func( + flen c.Int, from *byte, to *byte, rsa *RSA, padding c.Int) c.Int) c.Int { + return 0 +} + +/* +OSSL_DEPRECATEDIN_3_0 +int RSA_meth_set_pub_dec(RSA_METHOD *rsa, + int (*pub_dec) (int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, + int padding)); +*/ +// llgo:link (*RSA_METHOD).SetPubDec C.RSA_meth_set_pub_dec +func (*RSA_METHOD) SetPubDec(pubDec func( + flen c.Int, from *byte, to *byte, rsa *RSA, padding c.Int) c.Int) c.Int { + return 0 +} + +/* +OSSL_DEPRECATEDIN_3_0 +int RSA_meth_set_priv_enc(RSA_METHOD *rsa, + int (*priv_enc) (int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, + int padding)); +*/ +// llgo:link (*RSA_METHOD).SetPrivEnc C.RSA_meth_set_priv_enc +func (*RSA_METHOD) SetPrivEnc(privEnc func( + flen c.Int, from *byte, to *byte, rsa *RSA, padding c.Int) c.Int) c.Int { + return 0 +} + +/* +OSSL_DEPRECATEDIN_3_0 +int RSA_meth_set_priv_dec(RSA_METHOD *rsa, + int (*priv_dec) (int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, + int padding)); +*/ +// llgo:link (*RSA_METHOD).SetPrivDec C.RSA_meth_set_priv_dec +func (*RSA_METHOD) SetPrivDec(privDec func( + flen c.Int, from *byte, to *byte, rsa *RSA, padding c.Int) c.Int) c.Int { + return 0 +} + +/* +OSSL_DEPRECATEDIN_3_0 +int RSA_meth_set_sign(RSA_METHOD *rsa, + int (*sign) (int type, const unsigned char *m, + unsigned int m_length, + unsigned char *sigret, unsigned int *siglen, + const RSA *rsa)); +*/ +// llgo:link (*RSA_METHOD).SetSign C.RSA_meth_set_sign +func (*RSA_METHOD) SetSign(sign func( + typ c.Int, msg *byte, mlen c.Uint, + sigret *byte, siglen *c.Uint, rsa *RSA) c.Int) c.Int { + return 0 +} + +/* +OSSL_DEPRECATEDIN_3_0 +int RSA_meth_set_verify(RSA_METHOD *rsa, + int (*verify) (int dtype, const unsigned char *m, + unsigned int m_length, + const unsigned char *sigbuf, + unsigned int siglen, const RSA *rsa)); +*/ +// llgo:link (*RSA_METHOD).SetVerify C.RSA_meth_set_verify +func (*RSA_METHOD) SetVerify(verify func( + dtype c.Int, msg *byte, mlen c.Uint, + sigbuf *byte, siglen c.Uint, rsa *RSA) c.Int) c.Int { + return 0 +} + +/* +OSSL_DEPRECATEDIN_3_0 +int RSA_meth_set_keygen(RSA_METHOD *rsa, + int (*keygen) (RSA *rsa, int bits, BIGNUM *e, + BN_GENCB *cb)); + +OSSL_DEPRECATEDIN_3_0 +int RSA_meth_set_multi_prime_keygen(RSA_METHOD *meth, + int (*keygen) (RSA *rsa, int bits, + int primes, BIGNUM *e, + BN_GENCB *cb)); +*/ + +// ----------------------------------------------------------------------------- + type RSA struct { Unused [0]byte } @@ -35,6 +186,72 @@ func RSANew() *RSA // OSSL_DEPRECATEDIN_3_0 RSA *RSA_new_method(ENGINE *engine); +// OSSL_DEPRECATEDIN_3_0 void RSA_free(RSA *r); +// +// llgo:link (*RSA).Free C.RSA_free +func (*RSA) Free() {} + +// "up" the RSA object's reference count +// OSSL_DEPRECATEDIN_3_0 int RSA_up_ref(RSA *r); +// +// llgo:link (*RSA).UpRef C.RSA_up_ref +func (*RSA) UpRef() c.Int { return 0 } + +// OSSL_DEPRECATEDIN_3_0 int RSA_bits(const RSA *rsa); +// +// llgo:link (*RSA).Bits C.RSA_bits +func (*RSA) Bits() c.Int { return 0 } + +// OSSL_DEPRECATEDIN_3_0 int RSA_size(const RSA *rsa); +// +// llgo:link (*RSA).Size C.RSA_size +func (*RSA) Size() c.Int { return 0 } + +// OSSL_DEPRECATEDIN_3_0 int RSA_security_bits(const RSA *rsa); +// +// llgo:link (*RSA).SecurityBits C.RSA_security_bits +func (*RSA) SecurityBits() c.Int { return 0 } + +// OSSL_DEPRECATEDIN_3_0 int RSA_flags(const RSA *r); +// +// llgo:link (*RSA).Flags C.RSA_flags +func (*RSA) Flags() c.Int { return 0 } + +// OSSL_DEPRECATEDIN_3_0 void RSA_set_flags(RSA *r, int flags); +// +// llgo:link (*RSA).SetFlags C.RSA_set_flags +func (*RSA) SetFlags(flags c.Int) {} + +// OSSL_DEPRECATEDIN_3_0 void RSA_clear_flags(RSA *r, int flags); +// +// llgo:link (*RSA).ClearFlags C.RSA_clear_flags +func (*RSA) ClearFlags(flags c.Int) {} + +// OSSL_DEPRECATEDIN_3_0 int RSA_test_flags(const RSA *r, int flags); +// +// llgo:link (*RSA).TestFlags C.RSA_test_flags +func (*RSA) TestFlags(flags c.Int) c.Int { return 0 } + +// OSSL_DEPRECATEDIN_3_0 int RSA_get_version(RSA *r); +// +// llgo:link (*RSA).GetVersion C.RSA_get_version +func (*RSA) GetVersion() c.Int { return 0 } + +// OSSL_DEPRECATEDIN_3_0 int RSA_set_ex_data(RSA *r, int idx, void *arg); +// +// llgo:link (*RSA).SetExData C.RSA_set_ex_data +func (*RSA) SetExData(idx c.Int, arg unsafe.Pointer) c.Int { return 0 } + +// OSSL_DEPRECATEDIN_3_0 void *RSA_get_ex_data(const RSA *r, int idx); +// +// llgo:link (*RSA).GetExData C.RSA_get_ex_data +func (*RSA) GetExData(idx c.Int) unsafe.Pointer { return nil } + +// OSSL_DEPRECATEDIN_3_0 int RSA_set_method(RSA *rsa, const RSA_METHOD *meth); +// +// llgo:link (*RSA).SetMethod C.RSA_set_method +func (*RSA) SetMethod(meth *RSA_METHOD) c.Int { return 0 } + // OSSL_DEPRECATEDIN_3_0 int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb); // // llgo:link (*RSA).GenerateKeyEx C.RSA_generate_key_ex @@ -45,12 +262,40 @@ func (*RSA) GenerateKeyEx(bits c.Int, e *BIGNUM, cb *BN_GENCB) c.Int { return 0 // llgo:link (*RSA).GenerateMultiPrimeKey C.RSA_generate_multi_prime_key func (*RSA) GenerateMultiPrimeKey(bits, primes c.Int, e *BIGNUM, cb *BN_GENCB) c.Int { return 0 } +/* +// next 4 return -1 on error + +OSSL_DEPRECATEDIN_3_0 +int RSA_public_encrypt(int flen, const unsigned char *from, unsigned char *to, + RSA *rsa, int padding); +OSSL_DEPRECATEDIN_3_0 +int RSA_private_encrypt(int flen, const unsigned char *from, unsigned char *to, + RSA *rsa, int padding); +OSSL_DEPRECATEDIN_3_0 +int RSA_public_decrypt(int flen, const unsigned char *from, unsigned char *to, + RSA *rsa, int padding); +OSSL_DEPRECATEDIN_3_0 +int RSA_private_decrypt(int flen, const unsigned char *from, unsigned char *to, + RSA *rsa, int padding); +*/ +//go:linkname RSAPublicEncrypt C.RSA_public_encrypt +func RSAPublicEncrypt(flen c.Int, from *byte, to *byte, rsa *RSA, padding c.Int) c.Int + +//go:linkname RSAPrivateEncrypt C.RSA_private_encrypt +func RSAPrivateEncrypt(flen c.Int, from *byte, to *byte, rsa *RSA, padding c.Int) c.Int + +//go:linkname RSAPublicDecrypt C.RSA_public_decrypt +func RSAPublicDecrypt(flen c.Int, from *byte, to *byte, rsa *RSA, padding c.Int) c.Int + +//go:linkname RSAPrivateDecrypt C.RSA_private_decrypt +func RSAPrivateDecrypt(flen c.Int, from *byte, to *byte, rsa *RSA, padding c.Int) c.Int + // OSSL_DEPRECATEDIN_3_0 int RSA_sign( // int type, const unsigned char *m, unsigned int m_length, // unsigned char *sigret, unsigned int *siglen, RSA *rsa); // //go:linkname RSASign C.RSA_sign -func RSASign(typ c.Int, msg unsafe.Pointer, mlen c.Uint, sigret *byte, siglen *c.Uint, rsa *RSA) c.Int +func RSASign(typ c.Int, msg *byte, mlen c.Uint, sigret *byte, siglen *c.Uint, rsa *RSA) c.Int // OSSL_DEPRECATEDIN_3_0 int RSA_verify(int type, const unsigned char *m, // unsigned int m_length, From 5f92c3b3fc610f9868aeefdcbd7aa63ef7f7ec80 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 2 Aug 2024 14:26:13 +0800 Subject: [PATCH 149/152] library: io/ioutil --- _cmptest/ioutildemo/ioutil.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 _cmptest/ioutildemo/ioutil.go diff --git a/_cmptest/ioutildemo/ioutil.go b/_cmptest/ioutildemo/ioutil.go new file mode 100644 index 00000000..723068e7 --- /dev/null +++ b/_cmptest/ioutildemo/ioutil.go @@ -0,0 +1,19 @@ +package main + +import ( + "fmt" + "io/ioutil" + "log" + "strings" +) + +func main() { + r := strings.NewReader("Go is a general-purpose language designed with systems programming in mind.") + + b, err := ioutil.ReadAll(r) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("%s\n", b) +} From c1868464630d4dd55583727eee2a619c0f89e868 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 2 Aug 2024 14:27:10 +0800 Subject: [PATCH 150/152] README: io/ioutil --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0c990e7a..22db1411 100644 --- a/README.md +++ b/README.md @@ -277,6 +277,7 @@ Here are the Go packages that can be imported correctly: * [context](https://pkg.go.dev/context) * [io](https://pkg.go.dev/io) * [io/fs](https://pkg.go.dev/io/fs) +* [io/ioutil](https://pkg.go.dev/io/ioutil) * [log](https://pkg.go.dev/log) * [flag](https://pkg.go.dev/flag) * [sort](https://pkg.go.dev/sort) From b3e1b6fdbf8979d0fb33c921d222488da2801abe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E8=8B=B1=E6=9D=B0?= <2635879218@qq.com> Date: Fri, 2 Aug 2024 17:54:19 +0800 Subject: [PATCH 151/152] refactor(c-libuv): Added TODO(uid) comment & adjusted the position of Handle, Stream, Req, Write, Connect --- c/libuv/_demo/echo_server/echo_server.go | 6 +- c/libuv/libuv.go | 218 --------------------- c/libuv/net.go | 230 ++++++++++++++++++++++- c/libuv/timer.go | 1 + 4 files changed, 232 insertions(+), 223 deletions(-) diff --git a/c/libuv/_demo/echo_server/echo_server.go b/c/libuv/_demo/echo_server/echo_server.go index ee7faafc..9d961200 100644 --- a/c/libuv/_demo/echo_server/echo_server.go +++ b/c/libuv/_demo/echo_server/echo_server.go @@ -11,8 +11,6 @@ import ( var DEFAULT_PORT c.Int = 8080 var DEFAULT_BACKLOG c.Int = 128 -var loop *libuv.Loop - type WriteReq struct { Req libuv.Write Buf libuv.Buf @@ -20,7 +18,7 @@ type WriteReq struct { func main() { // Initialize the default event loop - loop = libuv.DefaultLoop() + var loop = libuv.DefaultLoop() // Initialize a TCP server var server libuv.Tcp @@ -105,7 +103,7 @@ func OnNewConnection(server *libuv.Stream, status c.Int) { } // Initialize the client TCP handle. - if libuv.InitTcp(loop, client) < 0 { + if libuv.InitTcp(libuv.DefaultLoop(), client) < 0 { c.Fprintf(c.Stderr, c.Str("Failed to initialize client\n")) c.Free(c.Pointer(client)) return diff --git a/c/libuv/libuv.go b/c/libuv/libuv.go index 9465f00e..b567bedc 100644 --- a/c/libuv/libuv.go +++ b/c/libuv/libuv.go @@ -97,44 +97,16 @@ type Loop struct { Unused [0]byte } -type Handle struct { - Unused [96]byte -} - -type Stream struct { - Unused [264]byte -} - type Poll struct { Unused [0]byte } /* Request types. */ -type Req struct { - Unused [0]byte -} - -type GetAddrInfo struct { - Unused [0]byte -} - -type GetNameInfo struct { - Unused [0]byte -} - type Shutdown struct { Unused [0]byte } -type Write struct { - Unused [192]byte -} - -type Connect struct { - Unused [0]byte -} - type Buf struct { Base *c.Char Len uintptr @@ -157,21 +129,12 @@ type FreeFunc func(ptr c.Pointer) // llgo:type C type AllocCb func(handle *Handle, suggestedSize uintptr, buf *Buf) -// llgo:type C -type ReadCb func(stream *Stream, nread c.Long, buf *Buf) - -// llgo:type C -type WriteCb func(req *Write, status c.Int) - // llgo:type C type GetaddrinfoCb func(req *GetAddrInfo, status c.Int, res *net.AddrInfo) // llgo:type C type GetnameinfoCb func(req *GetNameInfo, status c.Int, hostname *c.Char, service *c.Char) -// llgo:type C -type ConnectionCb func(server *Stream, status c.Int) - // llgo:type C type ShutdownCb func(req *Shutdown, status c.Int) @@ -204,170 +167,6 @@ func (shutdown *Shutdown) Shutdown(stream *Stream, shutdownCb ShutdownCb) c.Int // ---------------------------------------------- -/* Handle related function and method */ - -// llgo:link (*Handle).Ref C.uv_ref -func (handle *Handle) Ref() {} - -// llgo:link (*Handle).Unref C.uv_unref -func (handle *Handle) Unref() {} - -// llgo:link (*Handle).HasRef C.uv_has_ref -func (handle *Handle) HasRef() c.Int { - return 0 -} - -//go:linkname HandleSize C.uv_handle_size -func HandleSize(handleType HandleType) uintptr - -// llgo:link (*Handle).GetType C.uv_handle_get_type -func (handle *Handle) GetType() HandleType { - return 0 -} - -//go:linkname HandleTypeName C.uv_handle_type_name -func HandleTypeName(handleType HandleType) *c.Char - -// llgo:link (*Handle).GetData C.uv_handle_get_data -func (handle *Handle) GetData() c.Pointer { - return nil -} - -// llgo:link (*Handle).GetLoop C.uv_handle_get_loop -func (handle *Handle) GetLoop() *Loop { - return nil -} - -// llgo:link (*Handle).SetData C.uv_handle_set_data -func (handle *Handle) SetData(data c.Pointer) {} - -// llgo:link (*Handle).IsActive C.uv_is_active -func (handle *Handle) IsActive() c.Int { - return 0 -} - -// llgo:link (*Handle).Close C.uv_close -func (handle *Handle) Close(closeCb CloseCb) {} - -// llgo:link (*Handle).SendBufferSize C.uv_send_buffer_size -func (handle *Handle) SendBufferSize(value *c.Int) c.Int { - return 0 -} - -// llgo:link (*Handle).RecvBufferSize C.uv_recv_buffer_size -func (handle *Handle) RecvBufferSize(value *c.Int) c.Int { - return 0 -} - -// llgo:link (*Handle).Fileno C.uv_fileno -func (handle *Handle) Fileno(fd *OsFd) c.Int { - return 0 -} - -//go:linkname Pipe C.uv_pipe -func Pipe(fds [2]File, readFlags c.Int, writeFlags c.Int) c.Int { - return 0 -} - -//go:linkname Socketpair C.uv_socketpair -func Socketpair(_type c.Int, protocol c.Int, socketVector [2]OsSock, flag0 c.Int, flag1 c.Int) c.Int { - return 0 -} - -// llgo:link (*Handle).IsClosing C.uv_is_closing -func (handle *Handle) IsClosing() c.Int { - return 0 -} - -// ---------------------------------------------- - -/* Req related function and method */ - -//go:linkname ReqSize C.uv_req_size -func ReqSize(reqType ReqType) uintptr - -// llgo:link (*Req).GetData C.uv_req_get_data -func (req *Req) GetData() c.Pointer { - return nil -} - -// llgo:link (*Req).SetData C.uv_handle_set_data -func (req *Req) SetData(data c.Pointer) {} - -// llgo:link (*Req).GetType C.uv_req_get_type -func (req *Req) GetType() ReqType { - return 0 -} - -//go:linkname TypeName C.uv_req_type_name -func TypeName(reqType ReqType) *c.Char - -// ---------------------------------------------- - -/* Stream related function and method */ - -// llgo:link (*Stream).GetWriteQueueSize C.uv_stream_get_write_queue_size -func (stream *Stream) GetWriteQueueSize() uintptr { - return 0 -} - -// llgo:link (*Stream).Listen C.uv_listen -func (stream *Stream) Listen(backlog c.Int, connectionCb ConnectionCb) c.Int { - return 0 -} - -// llgo:link (*Stream).Accept C.uv_accept -func (server *Stream) Accept(client *Stream) c.Int { - return 0 -} - -// llgo:link (*Stream).StartRead C.uv_read_start -func (stream *Stream) StartRead(allocCb AllocCb, readCb ReadCb) c.Int { - return 0 -} - -// llgo:link (*Stream).StopRead C.uv_read_stop -func (stream *Stream) StopRead() c.Int { - return 0 -} - -// llgo:link (*Write).Write C.uv_write -func (req *Write) Write(stream *Stream, bufs *Buf, nbufs c.Uint, writeCb WriteCb) c.Int { - return 0 -} - -// llgo:link (*Write).Write2 C.uv_write2 -func (req *Write) Write2(stream *Stream, bufs *Buf, nbufs c.Uint, sendStream *Stream, writeCb WriteCb) c.Int { - return 0 -} - -// llgo:link (*Stream).TryWrite C.uv_try_write -func (stream *Stream) TryWrite(bufs *Buf, nbufs c.Uint) c.Int { - return 0 -} - -// llgo:link (*Stream).TryWrite2 C.uv_try_write2 -func (stream *Stream) TryWrite2(bufs *Buf, nbufs c.Uint, sendStream *Stream) c.Int { - return 0 -} - -// llgo:link (*Stream).IsReadable C.uv_is_readable -func (stream *Stream) IsReadable() c.Int { - return 0 -} - -// llgo:link (*Stream).IsWritable C.uv_is_writable -func (stream *Stream) IsWritable() c.Int { - return 0 -} - -// llgo:link (*Stream).SetBlocking C.uv_stream_set_blocking -func (stream *Stream) SetBlocking(blocking c.Int) c.Int { - return 0 -} - -// ---------------------------------------------- - /* Loop related functions and method. */ //go:linkname LoopSize C.uv_loop_size @@ -437,20 +236,3 @@ func PollStop(handle *Poll) c.Int //go:linkname PollInitSocket C.uv_poll_init_socket func PollInitSocket(loop *Loop, handle *Poll, socket c.Int) c.Int - -// ---------------------------------------------- - -/* Getaddrinfo related function and method */ - -//go:linkname Getaddrinfo C.uv_getaddrinfo -func Getaddrinfo(loop *Loop, req *GetAddrInfo, getaddrinfoCb GetaddrinfoCb, node *c.Char, service *c.Char, hints *net.AddrInfo) c.Int - -//go:linkname Freeaddrinfo C.uv_freeaddrinfo -func Freeaddrinfo(addrInfo *net.AddrInfo) - -// ---------------------------------------------- - -/* Getnameinfo related function and method */ - -//go:linkname Getnameinfo C.uv_getnameinfo -func Getnameinfo(loop *Loop, req *GetNameInfo, getnameinfoCb GetnameinfoCb, addr *net.SockAddr, flags c.Int) c.Int diff --git a/c/libuv/net.go b/c/libuv/net.go index 28cf10b6..2463666c 100644 --- a/c/libuv/net.go +++ b/c/libuv/net.go @@ -65,8 +65,22 @@ type UdpFlags c.Int /* Handle types. */ +// TODO(spongehah): Handle +type Handle struct { + Data c.Pointer + Unused [88]byte +} + +// TODO(spongehah): Stream +type Stream struct { + Data c.Pointer + Unused [256]byte +} + +// TODO(spongehah): Tcp type Tcp struct { - Unused [264]byte + Data c.Pointer + Unused [256]byte } type Udp struct { @@ -75,10 +89,34 @@ type Udp struct { /* Request types. */ +type Req struct { + Unused [0]byte +} + type UdpSend struct { Unused [0]byte } +// TODO(spongehah): Write +type Write struct { + Data c.Pointer + Unused [184]byte +} + +// TODO(spongehah): Connect +type Connect struct { + Data c.Pointer + Unused [88]byte +} + +type GetAddrInfo struct { + Unused [0]byte +} + +type GetNameInfo struct { + Unused [0]byte +} + // ---------------------------------------------- /* Function type */ @@ -95,6 +133,179 @@ type UdpSendCb func(req *UdpSend, status c.Int) // llgo:type C type UdpRecvCb func(handle *Udp, nread c.Long, buf *Buf, addr *net.SockAddr, flags c.Uint) +// llgo:type C +type ReadCb func(stream *Stream, nread c.Long, buf *Buf) + +// llgo:type C +type WriteCb func(req *Write, status c.Int) + +// llgo:type C +type ConnectionCb func(server *Stream, status c.Int) + +// ---------------------------------------------- + +/* Handle related function and method */ + +// llgo:link (*Handle).Ref C.uv_ref +func (handle *Handle) Ref() {} + +// llgo:link (*Handle).Unref C.uv_unref +func (handle *Handle) Unref() {} + +// llgo:link (*Handle).HasRef C.uv_has_ref +func (handle *Handle) HasRef() c.Int { + return 0 +} + +//go:linkname HandleSize C.uv_handle_size +func HandleSize(handleType HandleType) uintptr + +// llgo:link (*Handle).GetType C.uv_handle_get_type +func (handle *Handle) GetType() HandleType { + return 0 +} + +//go:linkname HandleTypeName C.uv_handle_type_name +func HandleTypeName(handleType HandleType) *c.Char + +// llgo:link (*Handle).GetData C.uv_handle_get_data +func (handle *Handle) GetData() c.Pointer { + return nil +} + +// llgo:link (*Handle).GetLoop C.uv_handle_get_loop +func (handle *Handle) GetLoop() *Loop { + return nil +} + +// llgo:link (*Handle).SetData C.uv_handle_set_data +func (handle *Handle) SetData(data c.Pointer) {} + +// llgo:link (*Handle).IsActive C.uv_is_active +func (handle *Handle) IsActive() c.Int { + return 0 +} + +// llgo:link (*Handle).Close C.uv_close +func (handle *Handle) Close(closeCb CloseCb) {} + +// llgo:link (*Handle).SendBufferSize C.uv_send_buffer_size +func (handle *Handle) SendBufferSize(value *c.Int) c.Int { + return 0 +} + +// llgo:link (*Handle).RecvBufferSize C.uv_recv_buffer_size +func (handle *Handle) RecvBufferSize(value *c.Int) c.Int { + return 0 +} + +// llgo:link (*Handle).Fileno C.uv_fileno +func (handle *Handle) Fileno(fd *OsFd) c.Int { + return 0 +} + +//go:linkname Pipe C.uv_pipe +func Pipe(fds [2]File, readFlags c.Int, writeFlags c.Int) c.Int { + return 0 +} + +//go:linkname Socketpair C.uv_socketpair +func Socketpair(_type c.Int, protocol c.Int, socketVector [2]OsSock, flag0 c.Int, flag1 c.Int) c.Int { + return 0 +} + +// llgo:link (*Handle).IsClosing C.uv_is_closing +func (handle *Handle) IsClosing() c.Int { + return 0 +} + +// ---------------------------------------------- + +/* Req related function and method */ + +//go:linkname ReqSize C.uv_req_size +func ReqSize(reqType ReqType) uintptr + +// llgo:link (*Req).GetData C.uv_req_get_data +func (req *Req) GetData() c.Pointer { + return nil +} + +// llgo:link (*Req).SetData C.uv_handle_set_data +func (req *Req) SetData(data c.Pointer) {} + +// llgo:link (*Req).GetType C.uv_req_get_type +func (req *Req) GetType() ReqType { + return 0 +} + +//go:linkname TypeName C.uv_req_type_name +func TypeName(reqType ReqType) *c.Char + +// ---------------------------------------------- + +/* Stream related function and method */ + +// llgo:link (*Stream).GetWriteQueueSize C.uv_stream_get_write_queue_size +func (stream *Stream) GetWriteQueueSize() uintptr { + return 0 +} + +// llgo:link (*Stream).Listen C.uv_listen +func (stream *Stream) Listen(backlog c.Int, connectionCb ConnectionCb) c.Int { + return 0 +} + +// llgo:link (*Stream).Accept C.uv_accept +func (server *Stream) Accept(client *Stream) c.Int { + return 0 +} + +// llgo:link (*Stream).StartRead C.uv_read_start +func (stream *Stream) StartRead(allocCb AllocCb, readCb ReadCb) c.Int { + return 0 +} + +// llgo:link (*Stream).StopRead C.uv_read_stop +func (stream *Stream) StopRead() c.Int { + return 0 +} + +// llgo:link (*Write).Write C.uv_write +func (req *Write) Write(stream *Stream, bufs *Buf, nbufs c.Uint, writeCb WriteCb) c.Int { + return 0 +} + +// llgo:link (*Write).Write2 C.uv_write2 +func (req *Write) Write2(stream *Stream, bufs *Buf, nbufs c.Uint, sendStream *Stream, writeCb WriteCb) c.Int { + return 0 +} + +// llgo:link (*Stream).TryWrite C.uv_try_write +func (stream *Stream) TryWrite(bufs *Buf, nbufs c.Uint) c.Int { + return 0 +} + +// llgo:link (*Stream).TryWrite2 C.uv_try_write2 +func (stream *Stream) TryWrite2(bufs *Buf, nbufs c.Uint, sendStream *Stream) c.Int { + return 0 +} + +// llgo:link (*Stream).IsReadable C.uv_is_readable +func (stream *Stream) IsReadable() c.Int { + return 0 +} + +// llgo:link (*Stream).IsWritable C.uv_is_writable +func (stream *Stream) IsWritable() c.Int { + return 0 +} + +// llgo:link (*Stream).SetBlocking C.uv_stream_set_blocking +func (stream *Stream) SetBlocking(blocking c.Int) c.Int { + return 0 +} + // ---------------------------------------------- /* Tcp related function and method */ @@ -273,3 +484,20 @@ func InetNtop(af c.Int, src c.Pointer, dst *c.Char, size uintptr) c.Int //go:linkname InetPton C.uv_inet_pton func InetPton(af c.Int, src *c.Char, dst c.Pointer) c.Int + +// ---------------------------------------------- + +/* Getaddrinfo related function and method */ + +//go:linkname Getaddrinfo C.uv_getaddrinfo +func Getaddrinfo(loop *Loop, req *GetAddrInfo, getaddrinfoCb GetaddrinfoCb, node *c.Char, service *c.Char, hints *net.AddrInfo) c.Int + +//go:linkname Freeaddrinfo C.uv_freeaddrinfo +func Freeaddrinfo(addrInfo *net.AddrInfo) + +// ---------------------------------------------- + +/* Getnameinfo related function and method */ + +//go:linkname Getnameinfo C.uv_getnameinfo +func Getnameinfo(loop *Loop, req *GetNameInfo, getnameinfoCb GetnameinfoCb, addr *net.SockAddr, flags c.Int) c.Int diff --git a/c/libuv/timer.go b/c/libuv/timer.go index 98970402..0c31cb95 100644 --- a/c/libuv/timer.go +++ b/c/libuv/timer.go @@ -10,6 +10,7 @@ import ( /* Handle types. */ +// TODO(spongehah): Timer type Timer struct { Unused [0]byte } From d1f33a6c4c7e70cbe447b6dcee834f44968a1e1d Mon Sep 17 00:00:00 2001 From: Aofei Sheng Date: Sat, 3 Aug 2024 09:04:34 +0800 Subject: [PATCH 152/152] perf(lib/sync): avoid using `defer` --- internal/lib/sync/sync.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/lib/sync/sync.go b/internal/lib/sync/sync.go index 415b6dec..69075e0c 100644 --- a/internal/lib/sync/sync.go +++ b/internal/lib/sync/sync.go @@ -98,11 +98,11 @@ type Once struct { func (o *Once) Do(f func()) { if !o.done { o.m.Lock() - defer o.m.Unlock() if !o.done { o.done = true f() } + o.m.Unlock() } }