From: Brendan Hansen Date: Fri, 20 May 2022 00:27:57 +0000 (-0500) Subject: core library bugfixes; added cbindgen; packages are global now X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=ae5cee1ef8b1db581dad72e8e43094674fa55b86;p=onyx.git core library bugfixes; added cbindgen; packages are global now --- diff --git a/core/alloc.onyx b/core/alloc.onyx index b4984f69..8bbdcb7d 100644 --- a/core/alloc.onyx +++ b/core/alloc.onyx @@ -31,13 +31,11 @@ TEMPORARY_ALLOCATOR_SIZE :: 1 << 16; // 16Kb heap_allocator : Allocator; // The global temp allocator, set up upon program intialization. -#local +#local #thread_local temp_state : arena.ArenaState; temp_allocator : Allocator; -init :: () { - heap.init(); - +init_temp_allocator :: () { temp_state = arena.make(heap_allocator, TEMPORARY_ALLOCATOR_SIZE); temp_allocator = as_allocator(^temp_state); } diff --git a/core/alloc/arena.onyx b/core/alloc/arena.onyx index eaf8fdef..6224a442 100644 --- a/core/alloc/arena.onyx +++ b/core/alloc/arena.onyx @@ -118,8 +118,9 @@ free :: (arena: ^ArenaState) { clear :: (arena: ^ArenaState) { walker := arena.first_arena.next; while walker != null { + next := walker.next; raw_free(arena.backing_allocator, walker); - walker = walker.next; + walker = next; } arena.size = sizeof rawptr; diff --git a/core/onyx/cbindgen.onyx b/core/onyx/cbindgen.onyx new file mode 100644 index 00000000..01ec30ae --- /dev/null +++ b/core/onyx/cbindgen.onyx @@ -0,0 +1,371 @@ +package cbindgen + +// +// How to use Onyx's cbindgen +// +// 1. Use generate_c_binding(config) to create a C source file for a given foreign block. +// 2. Use compile_c_file() to compile the C source file into a native library. +// 3. Run the build file with the --generate-foreign-info flag enabled. +// +// Full example: +// #load "core/std" +// #load "./module" +// +// use core +// +// main :: () { +// path := module_path(#file); +// c_file_path := string.concat(path, "temporary.c"); +// success := cbindgen.generate_c_binding(.{ +// output_file = c_file_path, +// foreign_block = something.foreign_block, +// cast_map = .[], +// custom_implementations = .[], +// preamble = .[ +// """ +// #include "..." +// #define ... +// // ... +// """ +// ], +// }); +// +// if !success do os.exit(1); +// +// success = cbindgen.compile_c_file(c_file_path, string.concat(path, "library")); +// if !success do os.exit(1); +// +// os.remove_file(c_file_path); +// } +// + +#if #defined (runtime.Generated_Foreign_Info) { + +#if runtime.compiler_os == .Linux { + #if #defined (runtime.vars.CC) { + Linux_Compiler :: runtime.vars.CC + } else { + Linux_Compiler :: "/usr/bin/gcc" + } +} + + +use core +use simd + +Cast_Mapping :: struct { + type: type_expr; + name: str; +} + +Impl_Mapping :: struct { + name: str; + impl: str; +} + +Binding_Config :: struct { + foreign_block: runtime.info.foreign_block; + preamble: [] str; + output_file: str; + cast_map: [] Cast_Mapping; + custom_implementations: [] Impl_Mapping; + name_map: (str) -> str = null_proc; +} + +generate_c_binding :: (use binding_config: Binding_Config) -> bool { + wrote := false; + for file: os.with_file(output_file, .Write) { + writer := io.writer_make(file); + fb := runtime.info.get_foreign_block(foreign_block); + + write_file_introduction(^writer, preamble, fb.module_name); + + for fb.funcs { + for impl: custom_implementations { + if impl.name == it.name { + io.write(^writer, impl.impl); + io.write(^writer, "\n"); + continue continue; + } + } + + write_function_body(^writer, it, cast_map, name_map); + } + + write_library_block(^writer, fb.funcs); + wrote = true; + } + + return wrote; +} + +// +// Call as module_path(#file) +module_path :: (file_path: str) -> str { + out := file_path; + while out.count > 0 { + if out[out.count - 1] == #char "/" || out[out.count - 1] == #char "\\" do break; + out.count -= 1; + } + + return out; +} + +// +// This is not as platform independent as I would like. +// I don't know the best, most compatible way to compile DLLs on Windows. +// Maybe those DLL can be shipped with the library, as most Windows is x86_64. +// +compile_c_file :: ( + path: str, dest: str, + includes: [] str = .[], + libraries: [] str = .[], + flags := "") -> bool { + #if runtime.compiler_os == .Linux { + args: [..] str; + args << "-shared"; + args << "-fPIC"; + args << path; + + args << "-I"; + args << "/usr/share/onyx/include"; + + for includes { + args << "-I"; + args << it; + } + + if flags != "" { + for string.split(flags, #char " ") do args << it; + } + + args << "-o"; + args << tprintf("{}.so", dest); + + for libraries { + args << aprintf("-l{}", it); + } + + proc := os.process_spawn(Linux_Compiler, args); + defer os.process_destroy(^proc); + + proc_reader := io.reader_make(^proc); + output := io.read_all(^proc_reader); + defer memory.free_slice(^output); + + exit := os.process_wait(^proc); + if exit != .Success { + eprintf("Failed to compile {}\n", path); + eprintf("{}\n", output); + } + + return exit == .Success; + } + + #if runtime.compiler_os == .Windows { + return true; + } +} + +#local { + + write_file_introduction :: (writer: ^io.Writer, preamble: [] str, name: str) { + io.write_format(writer, "//\n"); + io.write_format(writer, "// THIS FILE WAS AUTOMATICALLY GENERATED.\n"); + io.write_format(writer, "//\n"); + + for text: preamble { + io.write_format(writer, "{}\n", text); + } + + io.write_format(writer, """ +#define ONYX_LIBRARY_NAME {} +#include "onyx_library.h" + +#define P(i, k) (params->data[i].of.k) + +""", name); + } + + write_function_body :: (writer, ff, cast_map, name_map) => { + use runtime.info; + + method_type := ff.type; + method_info := cast (^Type_Info_Function) get_type_info(method_type); + assert(method_info.kind == .Function, "Expected function type."); + + io.write_format(writer, "ONYX_DEF({}, (", ff.name); + i := 0; + for method_info.parameter_types { + io.write(writer, type_encoding(it)); + + if i != method_info.parameter_types.count - 1 { + io.write(writer, ", "); + } + i += 1; + } + + io.write(writer, "), ("); + io.write(writer, type_encoding(method_info.return_type)); + io.write(writer, ")) {\n"); + + method_name := ff.name; + if name_map != null_proc { + method_name = name_map(method_name); + } + + print_body(writer, method_name, method_info, cast_map); + + io.write(writer, "}\n\n"); + } + + write_library_block :: (writer, funcs) => { + io.write(writer, "\n\n"); + io.write(writer, "ONYX_LIBRARY {\n"); + for funcs { + io.write_format(writer, " ONYX_FUNC({})\n", it.name); + } + io.write(writer, " NULL\n"); + io.write(writer, "};"); + } + + print_body :: (writer, method_name, method_info, cast_map) => { + use runtime.info; + call := io.dynamic_string_stream_make(); + defer io.dynamic_string_stream_free(^call); + callw := io.writer_make(^call); + + param_num := 0; + for method_info.parameter_types { + if get_type_info(it).kind == .Slice { + io.write_format(^callw, "ONYX_PTR(P({}, i32)), P({}, i32)", param_num, param_num + 1); + param_num += 1; + + } elseif is_pointer(it) { + io.write_format(^callw, "ONYX_PTR(P({}, i32))", param_num); + + } else { + matched := false; + for^ m: cast_map { + if m.type == it { + io.write_format(^callw, "({}) P({}, {})", m.name, param_num, type_to_wasm_type(it)); + matched = true; + break; + } + } + + if !matched do io.write_format(^callw, "P({}, {})", param_num, type_to_wasm_type(it)); + } + + io.write_format(^callw, ", "); + param_num += 1; + } + + call_str := call->to_str(); + wasm_return_type := type_to_wasm_type(method_info.return_type); + switch wasm_return_type { + case "" do io.write_format(writer, " {}({});\n", method_name, call_str[0..call_str.count-2]); + case "i32" do io.write_format(writer, " results->data[0] = WASM_I32_VAL({}({}));\n", method_name, call_str[0..call_str.count-2]); + case "i64" do io.write_format(writer, " results->data[0] = WASM_I64_VAL({}({}));\n", method_name, call_str[0..call_str.count-2]); + case "f32" do io.write_format(writer, " results->data[0] = WASM_F32_VAL({}({}));\n", method_name, call_str[0..call_str.count-2]); + case "f64" do io.write_format(writer, " results->data[0] = WASM_F64_VAL({}({}));\n", method_name, call_str[0..call_str.count-2]); + } + + io.write_format(writer, " return NULL;\n"); + } + + type_to_wasm_type :: (t: type_expr) -> str { + use runtime.info; + + param_info := get_type_info(t); + switch param_info.kind { + case .Basic do switch t { + case bool do return "i32"; + case i8 do return "i32"; + case u8 do return "i32"; + case i16 do return "i32"; + case u16 do return "i32"; + case i32 do return "i32"; + case u32 do return "i32"; + case i64 do return "i64"; + case u64 do return "i64"; + + case f32 do return "f32"; + case f64 do return "f64"; + + case rawptr do return "i32"; // This will have to depend on the pointer size... + + case i8x16, i16x8, i32x4, i64x2, f32x4, f64x2, v128 do return "v128"; + + case type_expr do return "i32"; + } + + case .Pointer do return "i32"; // This will also have to depend on the pointer size... + case .Function do assert(false, "Passing functions between wasm and c is not yet supported."); + case .Array do return "i32"; + case .Slice do assert(false, "ASDFASDF"); + case .Enum do return type_to_wasm_type((cast(^Type_Info_Enum) param_info).backing_type); + case .Distinct do return type_to_wasm_type((cast(^Type_Info_Distinct) param_info).base_type); + + case .Struct { + s_info := cast(^Type_Info_Struct) param_info; + if s_info.members.count == 1 { + return type_to_wasm_type(s_info.members[0].type); + } + + assert(false, "Passing structures between wasm and c is not yet supported."); + } + } + + return ""; + } + + type_encoding :: (t: type_expr) -> str { + use runtime.info; + + param_info := get_type_info(t); + switch param_info.kind { + case .Basic do switch t { + case bool do return "WASM_I32"; + case i8 do return "WASM_I32"; + case u8 do return "WASM_I32"; + case i16 do return "WASM_I32"; + case u16 do return "WASM_I32"; + case i32 do return "WASM_I32"; + case u32 do return "WASM_I32"; + case i64 do return "WASM_I64"; + case u64 do return "WASM_I64"; + + case f32 do return "WASM_F32"; + case f64 do return "WASM_F64"; + + case rawptr do return "WASM_I32"; // This will have to depend on the pointer size... + + case i8x16, i16x8, i32x4, i64x2, f32x4, f64x2, v128 do return "WASM_V128"; + + case type_expr do return "WASM_I32"; + } + + case .Pointer do return "WASM_I32"; // This will also have to depend on the pointer size... + case .Function do assert(false, "Passing functions between wasm and c is not yet supported."); + case .Array do return "WASM_I32"; + case .Slice do return "WASM_I32,WASM_I32"; + case .Enum do return type_encoding((cast(^Type_Info_Enum) param_info).backing_type); + case .Distinct do return type_encoding((cast(^Type_Info_Distinct) param_info).base_type); + + case .Struct { + s_info := cast(^Type_Info_Struct) param_info; + if s_info.members.count == 1 { + return type_encoding(s_info.members[0].type); + return; + } + + assert(false, "Passing structures between wasm and c is not yet supported."); + } + } + + return ""; + } +} + +} diff --git a/core/onyx/fs.onyx b/core/onyx/fs.onyx index ea0124c0..9ba45011 100644 --- a/core/onyx/fs.onyx +++ b/core/onyx/fs.onyx @@ -36,6 +36,7 @@ __file_open :: (path: str, mode := os.OpenMode.Read) -> (FileData, os.FileError) __dir_open :: (path: str, dir: ^DirectoryData) -> bool --- __dir_close :: (dir: DirectoryData) -> void --- __dir_read :: (dir: DirectoryData, out_entry: ^os.DirectoryEntry) -> bool --- + __dir_create :: (path: str) -> bool --- __dir_remove :: (path: str) -> bool --- } diff --git a/core/os/dir.onyx b/core/os/dir.onyx index b97c7878..5594b588 100644 --- a/core/os/dir.onyx +++ b/core/os/dir.onyx @@ -40,6 +40,7 @@ dir_read :: (dir: Directory, out_entry: ^DirectoryEntry) -> bool { return fs.__dir_read(dir, out_entry); } +dir_create :: fs.__dir_create dir_exists :: fs.__file_exists dir_remove :: fs.__dir_remove -dir_rename :: fs.__file_rename \ No newline at end of file +dir_rename :: fs.__file_rename diff --git a/core/os/process.onyx b/core/os/process.onyx index e685849b..ab3e53be 100644 --- a/core/os/process.onyx +++ b/core/os/process.onyx @@ -16,8 +16,8 @@ Process :: struct { process_handle: Handle; } -process_spawn :: (path: str, args: [] str, non_blocking_io := false) -> Process { - handle := __process_spawn(path, args, non_blocking_io); +process_spawn :: (path: str, args: [] str, non_blocking_io := false, starting_directory := "") -> Process { + handle := __process_spawn(path, args, non_blocking_io, starting_directory); return .{ .{ ^process_stream_vtable }, @@ -81,7 +81,7 @@ process_destroy :: (use p: ^Process) => { } #foreign "onyx_runtime" { - __process_spawn :: (path: str, args: [] str, non_blocking_io: bool) -> Process.Handle --- + __process_spawn :: (path: str, args: [] str, non_blocking_io: bool, starting_directory: str) -> Process.Handle --- __process_read :: (handle: Process.Handle, buffer: [] u8) -> i32 --- __process_write :: (handle: Process.Handle, buffer: [] u8) -> i32 --- __process_kill :: (handle: Process.Handle) -> bool --- diff --git a/core/runtime/common.onyx b/core/runtime/common.onyx index f511b7af..1abf7617 100644 --- a/core/runtime/common.onyx +++ b/core/runtime/common.onyx @@ -28,7 +28,7 @@ __assert_handler :: (msg: str, site: CallSite) { __runtime_initialize :: () { __initialize_data_segments(); - alloc.init(); + alloc.heap.init(); __thread_initialize(); #if Multi_Threading_Enabled do thread.__initialize(); @@ -47,6 +47,8 @@ __thread_initialize :: macro () { memory.set(__tls_base, 0, __tls_size); } + alloc.init_temp_allocator(); + __initialize(^context); context.allocator = alloc.heap_allocator; context.temp_allocator = alloc.temp_allocator; diff --git a/core/runtime/info/proc_tags.onyx b/core/runtime/info/proc_tags.onyx index 3b311224..b1107255 100644 --- a/core/runtime/info/proc_tags.onyx +++ b/core/runtime/info/proc_tags.onyx @@ -15,7 +15,7 @@ get_tags_for_procedure :: (func: $T) -> [] any { if get_type_info(T).kind != .Function do return .[]; for tagged_procedures { - if it.func == func do return it.tags; + if (*cast(^T) ^it.func) == func do return it.tags; } return .[]; diff --git a/core/std.onyx b/core/std.onyx index c32ea1da..fe416ce7 100644 --- a/core/std.onyx +++ b/core/std.onyx @@ -55,6 +55,7 @@ package core #load "./onyx/fs" #load "./onyx/cptr" + #load "./onyx/cbindgen" } #if runtime.runtime == .Wasi { #load "./wasi/wasi" diff --git a/include/small_windows.h b/include/small_windows.h index fe2ba6fa..28cea0bd 100644 --- a/include/small_windows.h +++ b/include/small_windows.h @@ -419,6 +419,7 @@ GB_DLL_IMPORT BOOL WINAPI GetFileAttributesExW(wchar_t const *path, GET_FILEEX GB_DLL_IMPORT BOOL WINAPI CopyFileW(wchar_t const *old_f, wchar_t const *new_f, BOOL fail_if_exists); GB_DLL_IMPORT BOOL WINAPI MoveFileW(wchar_t const *old_f, wchar_t const *new_f); GB_DLL_IMPORT BOOL WINAPI DeleteFileA (char const *path); +GB_DLL_IMPORT BOOL WINAPI CreateDirectoryA(char const *path); GB_DLL_IMPORT BOOL WINAPI RemoveDirectoryA(char const *path); GB_DLL_IMPORT BOOL WINAPI MoveFileA (char const *old_path, char const *new_path); diff --git a/include/utils.h b/include/utils.h index 69a518d8..46ee92b6 100644 --- a/include/utils.h +++ b/include/utils.h @@ -11,7 +11,7 @@ extern bh_allocator global_heap_allocator; const char* onyx_ast_node_kind_string(AstKind kind); Package* package_lookup(char* package_name); -Package* package_lookup_or_create(char* package_name, Scope* parent_scope, bh_allocator alloc); +Package* package_lookup_or_create(char* package_name, Scope* parent_scope, bh_allocator alloc, OnyxFilePos pos); void package_track_use_package(Package* package, Entity* entity); void package_reinsert_use_packages(Package* package); diff --git a/scripts/c_binding.onyx b/scripts/c_binding.onyx deleted file mode 100644 index 95e41aef..00000000 --- a/scripts/c_binding.onyx +++ /dev/null @@ -1,251 +0,0 @@ -package c_binding - -use package core -use package simd -#local info :: package runtime.info - -#if !#defined((package runtime).Generated_Foreign_Info) { - #error "Please run this script with the '--generate-foreign-info' flag." -} - -Cast_Mapping :: struct { - type: type_expr; - name: str; -} - -Impl_Mapping :: struct { - name: str; - impl: str; -} - -Binding_Config :: struct { - foreign_block: info.foreign_block; - preamble: [] str; - output_file: str; - cast_map: [] Cast_Mapping; - custom_implementations: [] Impl_Mapping; - name_map: (str) -> str = null_proc; -} - -build_c_binding :: (use binding_config: Binding_Config) -> bool { - for file: os.with_file(output_file, .Write) { - writer := io.writer_make(file); - fb := info.get_foreign_block(foreign_block); - - write_file_introduction(^writer, preamble, fb.module_name); - - for fb.funcs { - for impl: custom_implementations { - if impl.name == it.name { - io.write(^writer, impl.impl); - io.write(^writer, "\n"); - continue continue; - } - } - - write_function_body(^writer, it, cast_map, name_map); - } - - write_library_block(^writer, fb.funcs); - } - - return true; -} - -write_file_introduction :: (writer: ^io.Writer, preamble: [] str, name: str) { - io.write_format(writer, "//\n"); - io.write_format(writer, "// THIS FILE WAS AUTOMATICALLY GENERATED.\n"); - io.write_format(writer, "//\n"); - - for text: preamble { - io.write_format(writer, "{}\n", text); - } - - io.write_format(writer, """ -#define ONYX_LIBRARY_NAME {} -#include "onyx_library.h" - -#define P(i, k) (params->data[i].of.k) - -""", name); -} - -write_function_body :: (writer, ff, cast_map, name_map) => { - use info; - - method_type := ff.type; - method_info := cast (^Type_Info_Function) get_type_info(method_type); - assert(method_info.kind == .Function, "Expected function type."); - - io.write_format(writer, "ONYX_DEF({}, (", ff.name); - i := 0; - for method_info.parameter_types { - io.write(writer, type_encoding(it)); - - if i != method_info.parameter_types.count - 1 { - io.write(writer, ", "); - } - i += 1; - } - - io.write(writer, "), ("); - io.write(writer, type_encoding(method_info.return_type)); - io.write(writer, ")) {\n"); - - method_name := ff.name; - if name_map != null_proc { - method_name = name_map(method_name); - } - - print_body(writer, method_name, method_info, cast_map); - - io.write(writer, "}\n\n"); -} - -write_library_block :: (writer, funcs) => { - io.write(writer, "\n\n"); - io.write(writer, "ONYX_LIBRARY {\n"); - for funcs { - io.write_format(writer, " ONYX_FUNC({})\n", it.name); - } - io.write(writer, " NULL\n"); - io.write(writer, "};"); -} - -print_body :: (writer, method_name, method_info, cast_map) => { - use info; - call := io.dynamic_string_stream_make(); - defer io.dynamic_string_stream_free(^call); - callw := io.writer_make(^call); - - param_num := 0; - for method_info.parameter_types { - if get_type_info(it).kind == .Slice { - io.write_format(^callw, "ONYX_PTR(P({}, i32)), P({}, i32)", param_num, param_num + 1); - param_num += 1; - - } elseif is_pointer(it) { - io.write_format(^callw, "ONYX_PTR(P({}, i32))", param_num); - - } else { - matched := false; - for^ m: cast_map { - if m.type == it { - io.write_format(^callw, "({}) P({}, {})", m.name, param_num, type_to_wasm_type(it)); - matched = true; - break; - } - } - - if !matched do io.write_format(^callw, "P({}, {})", param_num, type_to_wasm_type(it)); - } - - io.write_format(^callw, ", "); - param_num += 1; - } - - call_str := call->to_str(); - wasm_return_type := type_to_wasm_type(method_info.return_type); - switch wasm_return_type { - case "" do io.write_format(writer, " {}({});\n", method_name, call_str[0..call_str.count-2]); - case "i32" do io.write_format(writer, " results->data[0] = WASM_I32_VAL({}({}));\n", method_name, call_str[0..call_str.count-2]); - case "i64" do io.write_format(writer, " results->data[0] = WASM_I64_VAL({}({}));\n", method_name, call_str[0..call_str.count-2]); - case "f32" do io.write_format(writer, " results->data[0] = WASM_F32_VAL({}({}));\n", method_name, call_str[0..call_str.count-2]); - case "f64" do io.write_format(writer, " results->data[0] = WASM_F64_VAL({}({}));\n", method_name, call_str[0..call_str.count-2]); - } - - io.write_format(writer, " return NULL;\n"); -} - -type_to_wasm_type :: (t: type_expr) -> str { - use info; - - param_info := get_type_info(t); - switch param_info.kind { - case .Basic do switch t { - case bool do return "i32"; - case i8 do return "i32"; - case u8 do return "i32"; - case i16 do return "i32"; - case u16 do return "i32"; - case i32 do return "i32"; - case u32 do return "i32"; - case i64 do return "i64"; - case u64 do return "i64"; - - case f32 do return "f32"; - case f64 do return "f64"; - - case rawptr do return "i32"; // This will have to depend on the pointer size... - - case i8x16, i16x8, i32x4, i64x2, f32x4, f64x2, v128 do return "v128"; - - case type_expr do return "i32"; - } - - case .Pointer do return "i32"; // This will also have to depend on the pointer size... - case .Function do assert(false, "Passing functions between wasm and c is not yet supported."); - case .Array do return "i32"; - case .Slice do assert(false, "ASDFASDF"); - case .Enum do return type_to_wasm_type((cast(^Type_Info_Enum) param_info).backing_type); - case .Distinct do return type_to_wasm_type((cast(^Type_Info_Distinct) param_info).base_type); - - case .Struct { - s_info := cast(^Type_Info_Struct) param_info; - if s_info.members.count == 1 { - return type_to_wasm_type(s_info.members[0].type); - } - - assert(false, "Passing structures between wasm and c is not yet supported."); - } - } - - return ""; -} - -type_encoding :: (t: type_expr) -> str { - use info; - - param_info := get_type_info(t); - switch param_info.kind { - case .Basic do switch t { - case bool do return "WASM_I32"; - case i8 do return "WASM_I32"; - case u8 do return "WASM_I32"; - case i16 do return "WASM_I32"; - case u16 do return "WASM_I32"; - case i32 do return "WASM_I32"; - case u32 do return "WASM_I32"; - case i64 do return "WASM_I64"; - case u64 do return "WASM_I64"; - - case f32 do return "WASM_F32"; - case f64 do return "WASM_F64"; - - case rawptr do return "WASM_I32"; // This will have to depend on the pointer size... - - case i8x16, i16x8, i32x4, i64x2, f32x4, f64x2, v128 do return "WASM_V128"; - - case type_expr do return "WASM_I32"; - } - - case .Pointer do return "WASM_I32"; // This will also have to depend on the pointer size... - case .Function do assert(false, "Passing functions between wasm and c is not yet supported."); - case .Array do return "WASM_I32"; - case .Slice do return "WASM_I32,WASM_I32"; - case .Enum do return type_encoding((cast(^Type_Info_Enum) param_info).backing_type); - case .Distinct do return type_encoding((cast(^Type_Info_Distinct) param_info).base_type); - - case .Struct { - s_info := cast(^Type_Info_Struct) param_info; - if s_info.members.count == 1 { - return type_encoding(s_info.members[0].type); - return; - } - - assert(false, "Passing structures between wasm and c is not yet supported."); - } - } - - return ""; -} diff --git a/scripts/c_library.onyx b/scripts/c_library.onyx deleted file mode 100644 index 69cef4ee..00000000 --- a/scripts/c_library.onyx +++ /dev/null @@ -1,66 +0,0 @@ - -#load "core/std" -use package core - -runtime :: package runtime - -Linux_Compiler :: "/usr/bin/gcc" - - -// -// Call as module_path(#file) -module_path :: (file_path: str) -> str { - out := file_path; - while out.count > 0 { - if out[out.count - 1] == #char "/" || out[out.count - 1] == #char "\\" do break; - out.count -= 1; - } - - return out; -} - -compile_c_file :: (path: str, dest: str, includes: [] str = .[], libraries: [] str = .[], flags := "") -> bool { - printf("Compiling '{}' -> '{}'.\n", path, dest); - - #if runtime.compiler_os == .Linux { - args: [..] str; - args << "-shared"; - args << "-fPIC"; - args << path; - - args << "-I"; - args << "/usr/share/onyx/include"; - - for includes { - args << "-I"; - args << it; - } - - if flags != "" { - for string.split(flags, #char " ") do args << it; - } - - args << "-o"; - args << dest; - - for libraries { - args << aprintf("-l{}", it); - } - - proc := os.process_spawn(Linux_Compiler, args); - defer os.process_destroy(^proc); - - proc_reader := io.reader_make(^proc); - output := io.read_all(^proc_reader); - defer memory.free_slice(^output); - - exit := os.process_wait(^proc); - println(exit); - println(output); - return exit == .Success; - } - - #if runtime.compiler_os == .Windows { - return true; - } -} \ No newline at end of file diff --git a/scripts/onyx-pkg.onyx b/scripts/onyx-pkg.onyx index 3df59bd0..9928ccb4 100644 --- a/scripts/onyx-pkg.onyx +++ b/scripts/onyx-pkg.onyx @@ -3,8 +3,8 @@ Version :: SemVer.{0, 1, 1} -use package core -use package core.intrinsics.onyx {__initialize, __zero_value} +use core +use core.intrinsics.onyx {__initialize, __zero_value} global_arguments: struct { #tag "--config-file" @@ -29,8 +29,7 @@ main :: (args: [] cstr) { loaded_config_file := false; defer if loaded_config_file do store_config_file(); - info :: package runtime.info - command_procedures := info.get_procedures_with_tag(Command); + command_procedures := runtime.info.get_procedures_with_tag(Command); defer delete(^command_procedures); for command_procedures { if it.tag.command == command { @@ -65,8 +64,7 @@ run_help_command :: (args: [] cstr) { printf("onyx-pkg version {}\n", Version); printf("Package dependency resolver and synchronizer for Onyx.\n\nUsage:\n"); - info :: package runtime.info - command_procedures := info.get_procedures_with_tag(Command); + command_procedures := runtime.info.get_procedures_with_tag(Command); defer delete(^command_procedures); for command_procedures { printf("{}\n", it.tag.description); @@ -307,12 +305,50 @@ run_sync_command :: (args: [] cstr) { if it.type != .Directory do continue; if !array.contains(needed_dependency_folders, it->name()) { - os.remove_directory(tprintf("{}/{}", config.config.lib_source_directory, it->name())); + package_folder := tprintf("{}/{}", config.config.lib_source_directory, it->name()); + attempt_remove_native_library(package_folder); + os.remove_directory(package_folder); } } } } +#tag Command.{ "rebuild", "Rebuild native library for a package", "package-or-url", +""" +package-or-url Git repository name or package name on disk to remove. +""" +} +run_rebuild_command :: (args: [] cstr) { + if args.count < 1 { + eprintf("Expected package name."); + return; + } + + dep := string.as_str(args[0]); + + folder := dep; + if config.dependency_folders.folders->has(dep) { + folder = config.dependency_folders.folders[dep]; + } + + if rebuild_native_library(folder) { + printf("Successfully rebuilt {}.\n", dep); + } else { + printf("Failed to rebuild {}.\n", dep); + os.exit(1); + } +} + +#local { + #if runtime.compiler_os == .Linux { + native_library_suffix :: ".so" + } + #if runtime.compiler_os == .Windows { + native_library_suffix :: ".dll" + } +} + + install_package :: (pack: Package, downgrade_if_necessary := false) -> (bool, installed_folder: str) { if config.dependency_folders.folders->has(pack.repo) { folder_name := config.dependency_folders.folders[pack.repo]; @@ -342,8 +378,9 @@ install_package :: (pack: Package, downgrade_if_necessary := false) -> (bool, in } assert(config.dependency_folders.folders->has(pack.repo), ""); - folder_name := config.dependency_folders.folders[pack.repo]; - return true, folder_name; + folder_name := config.dependency_folders.folders[pack.repo]; + install_success := run_native_library_installation(folder_name); + return install_success, folder_name; } uninstall_package :: (pack: Package) -> bool { @@ -353,9 +390,34 @@ uninstall_package :: (pack: Package) -> bool { if os.file_exists(package_folder) { // Should this check if the version to be deleted is the one that is actually installed? + attempt_remove_native_library(package_folder); os.remove_directory(package_folder); } + + return true; } + + return false; +} + +attempt_remove_native_library :: (package_folder: str) -> bool { + inner_config: Config; + for os.with_file(tprintf("{}/onyx-pkg.ini", package_folder)) { + r := io.reader_make(it); + result, error := parse_ini_file(^r, ^inner_config); + + if result != .Success do return false; + if string.is_empty(inner_config.native_library.library) do return false; + + os.remove_file(tprintf("{}/{}{}", config.config.lib_bin_directory, inner_config.native_library.library, native_library_suffix)); + } + + return true; +} + +rebuild_native_library :: (folder: str) -> bool { + attempt_remove_native_library(tprintf("{}/{}", config.config.lib_source_directory, folder)); + return run_native_library_installation(folder); } get_installed_version_of_package :: (package_path: str) -> SemVer { @@ -372,6 +434,47 @@ get_installed_version_of_package :: (package_path: str) -> SemVer { return .{0, 0, 0}; } +run_native_library_installation :: (folder: str) -> bool { + inner_config: Config; + for os.with_file(tprintf("{}/{}/onyx-pkg.ini", config.config.lib_source_directory, folder)) { + r := io.reader_make(it); + result, error := parse_ini_file(^r, ^inner_config); + + if result != .Success do return false; + if string.is_empty(inner_config.native_library.build_cmd) do return true; + + args := string.split(inner_config.native_library.build_cmd, #char " ", context.temp_allocator); + cmd := args[0]; + args = args[1 .. args.count]; + + installed_dest := tprintf("{}/{}", config.config.lib_source_directory, folder); + build_proc := os.process_spawn(cmd, args, starting_directory=installed_dest); + build_result := os.process_wait(^build_proc); + + if build_result != .Success { + eprintf("Failed to build native library in {}.\n", folder); + return false; + } + + if !os.dir_exists(config.config.lib_bin_directory) { + if !os.dir_create(config.config.lib_bin_directory) { + eprintf("Failed to create native library directory, {}.\n", config.config.lib_bin_directory); + return false; + } + } + + source_path := tprintf("{}/{}{}", installed_dest, inner_config.native_library.library, native_library_suffix); + dest_path := tprintf("{}/{}{}", config.config.lib_bin_directory, inner_config.native_library.library, native_library_suffix); + success := os.rename_file(source_path, dest_path); + + if !success { + eprintf("Failed to move native library to final destination.\n {} -> {}\n", source_path, dest_path); + } + + return success; + } +} + #tag conv.Custom_Parse.{parse} #tag conv.Custom_Format.{format} @@ -425,9 +528,8 @@ Package :: struct { version: SemVer; } -#local runtime :: package runtime #if runtime.compiler_os == .Linux { - git_path :: "/usr/bin/git" + git_path :: "git" } #if runtime.compiler_os == .Windows { git_path :: "git.exe" @@ -452,9 +554,7 @@ Git :: struct { } } - if os.process_wait(^git_proc) == .Error { - eprintf("Encountered error running 'git' command."); - } + os.process_wait(^git_proc); return versions; } @@ -565,6 +665,12 @@ Config :: struct { } config: Config = .{}; + Native_Library :: struct { + build_cmd: str; + library: str; + } + native_library: Native_Library; + Dependencies :: struct { dependencies: Map(str, SemVer); @@ -676,7 +782,7 @@ store_config_file :: () -> bool { } parse_ini_file :: (r: ^io.Reader, output_ptr: any) -> (IniParseResult, IniParseError) { - info :: package runtime.info + info :: runtime.info line := 1; error :: macro (msg: str) { @@ -743,7 +849,6 @@ parse_ini_file :: (r: ^io.Reader, output_ptr: any) -> (IniParseResult, IniParseE field_name := r->read_until(#char "=", allocator=context.temp_allocator); assert(r->read_byte() == #char "=", "expected ="); - r->skip_whitespace(); field := info.get_struct_member(active_item_type, string.strip_whitespace(field_name)); target := cast(^u8) active_item_ptr + field.offset; @@ -755,8 +860,9 @@ parse_ini_file :: (r: ^io.Reader, output_ptr: any) -> (IniParseResult, IniParseE // If the type is a string, then the value can be the entire line // of text. No need to force the quotes. if field.type == str { - *cast(^str) target = string.alloc_copy(value_string) - |> string.strip_whitespace(); + *cast(^str) target = value_string + |> string.strip_whitespace() + |> string.alloc_copy(); } else { error(aprintf("Failed to parse value.")); @@ -771,7 +877,7 @@ parse_ini_file :: (r: ^io.Reader, output_ptr: any) -> (IniParseResult, IniParseE } write_ini_file :: (w: ^io.Writer, output: any) -> bool { - info :: package runtime.info + info :: runtime.info output_info := cast(^info.Type_Info_Struct) info.get_type_info(output.type); if output_info.kind != .Struct do return false; diff --git a/scripts/run_tests.onyx b/scripts/run_tests.onyx index 0df2023c..01020d8c 100644 --- a/scripts/run_tests.onyx +++ b/scripts/run_tests.onyx @@ -5,9 +5,8 @@ #load "core/std" -use package core -use package core.intrinsics.onyx { init } -#local runtime :: package runtime +use core +use core.intrinsics.onyx { init } Color :: enum { White; @@ -114,7 +113,7 @@ main :: (args) => { iter.parallel_for(cases, settings.threads, ^exec_context) { // Weird macros mean I have to forward external names - use package core + use core print_color :: print_color; args: [] str; diff --git a/src/builtins.c b/src/builtins.c index 7f246141..0c3520e1 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -374,7 +374,7 @@ void initialize_builtins(bh_allocator a) { if (bsym->package == NULL) symbol_builtin_introduce(context.global_scope, bsym->sym, bsym->node); else { - Package* p = package_lookup_or_create(bsym->package, context.global_scope, a); + Package* p = package_lookup_or_create(bsym->package, context.global_scope, a, context.global_scope->created_at); assert(p); symbol_builtin_introduce(p->scope, bsym->sym, bsym->node); @@ -382,7 +382,7 @@ void initialize_builtins(bh_allocator a) { bsym++; } - Package* p = package_lookup_or_create("builtin", context.global_scope, a); + Package* p = package_lookup_or_create("builtin", context.global_scope, a, context.global_scope->created_at); builtin_string_type = (AstType *) symbol_raw_resolve(p->scope, "str"); if (builtin_string_type == NULL) { @@ -480,7 +480,7 @@ void initalize_special_globals() { } void introduce_build_options(bh_allocator a) { - Package* p = package_lookup_or_create("runtime", context.global_scope, a); + Package* p = package_lookup_or_create("runtime", context.global_scope, a, context.global_scope->created_at); AstType* Runtime_Type = (AstType *) symbol_raw_resolve(p->scope, "Runtime"); if (Runtime_Type == NULL) { diff --git a/src/onyx.c b/src/onyx.c index 1275ae23..c73d01ef 100644 --- a/src/onyx.c +++ b/src/onyx.c @@ -235,7 +235,11 @@ static void context_init(CompileOptions* opts) { context.options = opts; context.cycle_detected = 0; - context.global_scope = scope_create(global_heap_allocator, NULL, (OnyxFilePos) { 0 }); + OnyxFilePos internal_location = { 0 }; + internal_location.filename = ""; + internal_location.line = 1; + internal_location.column = 1; + context.global_scope = scope_create(global_heap_allocator, NULL, internal_location); sh_new_arena(context.packages); // NOTE: This will be initialized upon the first call to entity_heap_insert. diff --git a/src/onyx_runtime.c b/src/onyx_runtime.c index 4144d082..804dfc97 100644 --- a/src/onyx_runtime.c +++ b/src/onyx_runtime.c @@ -34,6 +34,7 @@ ONYX_DEF(__file_open_impl, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) int path_len = params->data[1].of.i32; char path[512] = {0}; + path_len = bh_min(path_len, 511); strncpy(path, path_ptr, path_len); path[path_len] = 0; @@ -77,6 +78,7 @@ ONYX_DEF(__file_exists, (WASM_I32, WASM_I32), (WASM_I32)) { int path_len = params->data[1].of.i32; char path[512] = {0}; + path_len = bh_min(path_len, 511); strncpy(path, path_ptr, path_len); path[path_len] = 0; @@ -89,6 +91,7 @@ ONYX_DEF(__file_remove, (WASM_I32, WASM_I32), (WASM_I32)) { int path_len = params->data[1].of.i32; char path[512] = {0}; + path_len = bh_min(path_len, 511); strncpy(path, path_ptr, path_len); path[path_len] = 0; @@ -190,6 +193,7 @@ ONYX_DEF(__file_rename, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) { int old_path_len = params->data[1].of.i32; char old_path[512] = {0}; + old_path_len = bh_min(old_path_len, 511); strncpy(old_path, old_path_ptr, old_path_len); old_path[old_path_len] = 0; @@ -197,6 +201,7 @@ ONYX_DEF(__file_rename, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) { int new_path_len = params->data[3].of.i32; char new_path[512] = {0}; + new_path_len = bh_min(new_path_len, 511); strncpy(new_path, new_path_ptr, new_path_len); new_path[new_path_len] = 0; @@ -220,6 +225,7 @@ ONYX_DEF(__dir_open, (WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) { int path_len = params->data[1].of.i32; char path[512] = {0}; + path_len = bh_min(path_len, 511); strncpy(path, path_ptr, path_len); path[path_len] = 0; @@ -339,11 +345,32 @@ ONYX_DEF(__dir_close, (WASM_I64), ()) { return NULL; } +ONYX_DEF(__dir_create, (WASM_I32, WASM_I32), (WASM_I32)) { + char *path_ptr = ONYX_PTR(params->data[0].of.i32); + int path_len = params->data[1].of.i32; + + char path[512] = {0}; + path_len = bh_min(path_len, 511); + strncpy(path, path_ptr, path_len); + path[path_len] = 0; + +#ifdef _BH_WINDOWS + results->data[0] = WASM_I32_VAL(CreateDirectoryA(path, NULL)); + return NULL; +#endif + +#ifdef _BH_LINUX + results->data[0] = WASM_I32_VAL(mkdir(path, 0777) == 0); + return NULL; +#endif +} + ONYX_DEF(__dir_remove, (WASM_I32, WASM_I32), (WASM_I32)) { char *path_ptr = ONYX_PTR(params->data[0].of.i32); int path_len = params->data[1].of.i32; char path[512] = {0}; + path_len = bh_min(path_len, 511); strncpy(path, path_ptr, path_len); path[path_len] = 0; @@ -508,18 +535,27 @@ typedef struct OnyxProcess { #endif } OnyxProcess; -ONYX_DEF(__process_spawn, (WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I64)) { +ONYX_DEF(__process_spawn, (WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I64)) { char* process_str = ONYX_PTR(params->data[0].of.i32); i32 process_len = params->data[1].of.i32; i32 args_ptr = params->data[2].of.i32; i32 args_len = params->data[3].of.i32; b32 blocking_io = !params->data[4].of.i32; + char *cwd_str = ONYX_PTR(params->data[5].of.i32); + i32 cwd_len = params->data[6].of.i32; char process_path[1024]; process_len = bh_min(1023, process_len); memcpy(process_path, process_str, process_len); process_path[process_len] = '\0'; + char starting_dir[1024]; + if (cwd_len > 0) { + cwd_len = bh_min(1023, cwd_len); + memcpy(starting_dir, cwd_str, cwd_len); + starting_dir[cwd_len] = '\0'; + } + OnyxProcess *process = malloc(sizeof(OnyxProcess)); memset(process, 0, sizeof(*process)); process->magic_number = ONYX_PROCESS_MAGIC_NUMBER; @@ -568,7 +604,11 @@ ONYX_DEF(__process_spawn, (WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32), (W fcntl(1, F_SETFL, O_NONBLOCK); } - execv(process_path, process_args); + if (cwd_len > 0) { + chdir(starting_dir); // Switch current working directory. + } + + execvp(process_path, process_args); exit(-1); break; @@ -633,7 +673,12 @@ ONYX_DEF(__process_spawn, (WASM_I32, WASM_I32, WASM_I32, WASM_I32, WASM_I32), (W memset(&process->proc_info, 0, sizeof process->proc_info); - success = CreateProcessA(process_path, cmdLine, &saAttr, &saAttr, 1, 0, NULL, NULL, &startup, &process->proc_info); + char *working_dir = NULL; + if (cwd_len > 0) { + working_dir = starting_dir; + } + + success = CreateProcessA(process_path, cmdLine, &saAttr, &saAttr, 1, 0, NULL, working_dir, &startup, &process->proc_info); if (!success) { printf("FAILED TO CREATE PROCESS: %d\n", GetLastError()); wasm_val_init_ptr(&results->data[0], NULL); // Failed to run @LEAK @@ -1201,6 +1246,7 @@ ONYX_LIBRARY { ONYX_FUNC(__dir_open) ONYX_FUNC(__dir_read) ONYX_FUNC(__dir_close) + ONYX_FUNC(__dir_create) ONYX_FUNC(__dir_remove) ONYX_FUNC(__spawn_thread) diff --git a/src/parser.c b/src/parser.c index a68e574f..1b658e75 100644 --- a/src/parser.c +++ b/src/parser.c @@ -3309,7 +3309,7 @@ static AstPackage* parse_package_expression(OnyxParser* parser) { static Package* parse_file_package(OnyxParser* parser) { if (parser->curr->type != Token_Type_Keyword_Package) { - return package_lookup_or_create("main", context.global_scope, parser->allocator); + return package_lookup_or_create("main", context.global_scope, parser->allocator, parser->curr->pos); } AstPackage* package_node = parse_package_expression(parser); @@ -3323,7 +3323,7 @@ static Package* parse_file_package(OnyxParser* parser) { token_toggle_end(*symbol); strncat(aggregate_name, (*symbol)->text, 2047); - Package* newpackage = package_lookup_or_create(aggregate_name, context.global_scope, parser->allocator); + Package* newpackage = package_lookup_or_create(aggregate_name, context.global_scope, parser->allocator, package_node->token->pos); AstPackage* pnode = make_node(AstPackage, Ast_Kind_Package); pnode->token = *symbol; diff --git a/src/utils.c b/src/utils.c index 611ad8de..79114df6 100644 --- a/src/utils.c +++ b/src/utils.c @@ -26,7 +26,7 @@ Package* package_lookup(char* package_name) { } } -Package* package_lookup_or_create(char* package_name, Scope* parent_scope, bh_allocator alloc) { +Package* package_lookup_or_create(char* package_name, Scope* parent_scope, bh_allocator alloc, OnyxFilePos pos) { i32 index = shgeti(context.packages, package_name); if (index != -1) { return context.packages[index].value; @@ -45,6 +45,14 @@ Package* package_lookup_or_create(char* package_name, Scope* parent_scope, bh_al shput(context.packages, pac_name, package); + if (!charset_contains(pac_name, '.')) { + AstPackage* package_node = onyx_ast_node_new(alloc, sizeof(AstPackage), Ast_Kind_Package); + package_node->package_name = package->name; + package_node->package = package; + + symbol_raw_introduce(context.global_scope, pac_name, pos, (AstNode *) package_node); + } + return package; } } @@ -110,6 +118,11 @@ b32 symbol_raw_introduce(Scope* scope, char* name, OnyxFilePos pos, AstNode* sym AstNode *node = scope->symbols[index].value; if (node != symbol) { onyx_report_error(pos, Error_Critical, "Redeclaration of symbol '%s'.", name); + + if (node->token) { + onyx_report_error(node->token->pos, Error_Critical, "Previous declaration was here."); + } + return 0; } return 1; diff --git a/tests/struct_use_pointer_member b/tests/struct_use_pointer_member index d606442d..141715b4 100644 --- a/tests/struct_use_pointer_member +++ b/tests/struct_use_pointer_member @@ -1,2 +1,2 @@ Hello, I am Billy! -Go away!! 0xEDF4 +Go away!! (^Person) -> void diff --git a/tests/struct_use_pointer_member.onyx b/tests/struct_use_pointer_member.onyx index c2b8d445..14686b20 100644 --- a/tests/struct_use_pointer_member.onyx +++ b/tests/struct_use_pointer_member.onyx @@ -19,7 +19,7 @@ nice_person := Person_Vtable.{ mean_person := Person_Vtable.{ greet = (use p: ^Person) { - printf("Go away!! {}\n", ^greet); + printf("Go away!! {}\n", typeof greet); } } @@ -29,4 +29,4 @@ main :: (args) => { billy->greet(); charles->greet(); -} \ No newline at end of file +}