mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-13 01:21:38 -07:00
Merge remote-tracking branch 'upstream/master' into llvm-14-fixes
This commit is contained in:
@@ -124,13 +124,13 @@ jobs:
|
||||
build_macos:
|
||||
name: MacOS Build
|
||||
if: github.repository == 'odin-lang/Odin'
|
||||
runs-on: macos-13
|
||||
runs-on: macos-14 # Intel machine
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Download LLVM and setup PATH
|
||||
run: |
|
||||
brew update
|
||||
brew install llvm@20 dylibbundler lld
|
||||
brew install llvm@20 dylibbundler lld@20
|
||||
|
||||
- name: build odin
|
||||
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
|
||||
@@ -169,7 +169,7 @@ jobs:
|
||||
- name: Download LLVM and setup PATH
|
||||
run: |
|
||||
brew update
|
||||
brew install llvm@20 dylibbundler lld
|
||||
brew install llvm@20 dylibbundler lld@20
|
||||
|
||||
- name: build odin
|
||||
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
|
||||
|
||||
@@ -302,3 +302,9 @@ misc/featuregen/featuregen
|
||||
.cache/
|
||||
.clangd
|
||||
compile_commands.json
|
||||
|
||||
# Dev cmake helpers
|
||||
build/
|
||||
cmake-build*/
|
||||
CMakeLists.txt
|
||||
sandbox/
|
||||
|
||||
@@ -374,10 +374,11 @@ objc_selector :: struct{}
|
||||
objc_class :: struct{}
|
||||
objc_ivar :: struct{}
|
||||
|
||||
objc_id :: ^objc_object
|
||||
objc_SEL :: ^objc_selector
|
||||
objc_Class :: ^objc_class
|
||||
objc_Ivar :: ^objc_ivar
|
||||
objc_id :: ^objc_object
|
||||
objc_SEL :: ^objc_selector
|
||||
objc_Class :: ^objc_class
|
||||
objc_Ivar :: ^objc_ivar
|
||||
objc_instancetype :: distinct objc_id
|
||||
|
||||
objc_find_selector :: proc($name: string) -> objc_SEL ---
|
||||
objc_register_selector :: proc($name: string) -> objc_SEL ---
|
||||
@@ -385,6 +386,7 @@ objc_find_class :: proc($name: string) -> objc_Class ---
|
||||
objc_register_class :: proc($name: string) -> objc_Class ---
|
||||
objc_ivar_get :: proc(self: ^$T) -> ^$U ---
|
||||
objc_block :: proc(invoke: $T, ..any) -> ^Objc_Block(T) where type_is_proc(T) ---
|
||||
objc_super :: proc(obj: ^$T) -> ^$U where type_is_subtype_of(T, objc_object) && type_is_subtype_of(U, objc_object) ---
|
||||
|
||||
valgrind_client_request :: proc(default: uintptr, request: uintptr, a0, a1, a2, a3, a4: uintptr) -> uintptr ---
|
||||
|
||||
|
||||
@@ -636,6 +636,8 @@ _cleanup_runtime_contextless :: proc "contextless" () {
|
||||
/////////////////////////////
|
||||
|
||||
|
||||
// type_info_base returns the base-type of a `^Type_Info` stripping the `distinct`ness from the first level
|
||||
@(require_results)
|
||||
type_info_base :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
|
||||
if info == nil {
|
||||
return nil
|
||||
@@ -652,6 +654,10 @@ type_info_base :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
|
||||
}
|
||||
|
||||
|
||||
// type_info_core returns the core-type of a `^Type_Info` stripping the `distinct`ness from the first level AND/OR
|
||||
// returns the backing integer type of an enum or bit_set `^Type_Info`.
|
||||
// This is also aliased as `type_info_base_without_enum`
|
||||
@(require_results)
|
||||
type_info_core :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
|
||||
if info == nil {
|
||||
return nil
|
||||
@@ -668,6 +674,10 @@ type_info_core :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
// type_info_base_without_enum returns the core-type of a `^Type_Info` stripping the `distinct`ness from the first level AND/OR
|
||||
// returns the backing integer type of an enum or bit_set `^Type_Info`.
|
||||
// This is also aliased as `type_info_core`
|
||||
type_info_base_without_enum :: type_info_core
|
||||
|
||||
__type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info #no_bounds_check {
|
||||
@@ -684,15 +694,23 @@ __type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info #no_bounds_check
|
||||
}
|
||||
|
||||
when !ODIN_NO_RTTI {
|
||||
// typeid_base returns the base-type of a `typeid` stripping the `distinct`ness from the first level
|
||||
typeid_base :: proc "contextless" (id: typeid) -> typeid {
|
||||
ti := type_info_of(id)
|
||||
ti = type_info_base(ti)
|
||||
return ti.id
|
||||
}
|
||||
// typeid_core returns the core-type of a `typeid` stripping the `distinct`ness from the first level AND/OR
|
||||
// returns the backing integer type of an enum or bit_set `typeid`.
|
||||
// This is also aliased as `typeid_base_without_enum`
|
||||
typeid_core :: proc "contextless" (id: typeid) -> typeid {
|
||||
ti := type_info_core(type_info_of(id))
|
||||
return ti.id
|
||||
}
|
||||
|
||||
// typeid_base_without_enum returns the core-type of a `typeid` stripping the `distinct`ness from the first level AND/OR
|
||||
// returns the backing integer type of an enum or bit_set `typeid`.
|
||||
// This is also aliased as `typeid_core`
|
||||
typeid_base_without_enum :: typeid_core
|
||||
}
|
||||
|
||||
@@ -708,11 +726,15 @@ default_logger_proc :: proc(data: rawptr, level: Logger_Level, text: string, opt
|
||||
// Nothing
|
||||
}
|
||||
|
||||
// Returns the default logger used by `context.logger`
|
||||
@(require_results)
|
||||
default_logger :: proc() -> Logger {
|
||||
return Logger{default_logger_proc, nil, Logger_Level.Debug, nil}
|
||||
}
|
||||
|
||||
|
||||
// Returns the default `context`
|
||||
@(require_results)
|
||||
default_context :: proc "contextless" () -> Context {
|
||||
c: Context
|
||||
__init_context(&c)
|
||||
|
||||
@@ -62,6 +62,8 @@ when !NO_DEFAULT_TEMP_ALLOCATOR {
|
||||
}
|
||||
}
|
||||
|
||||
// Initializes the global temporary allocator used as the default `context.temp_allocator`.
|
||||
// This is ignored when `NO_DEFAULT_TEMP_ALLOCATOR` is true.
|
||||
@(builtin, disabled=NO_DEFAULT_TEMP_ALLOCATOR)
|
||||
init_global_temporary_allocator :: proc(size: int, backup_allocator := context.allocator) {
|
||||
when !NO_DEFAULT_TEMP_ALLOCATOR {
|
||||
@@ -564,6 +566,7 @@ _append_elem :: #force_no_inline proc(array: ^Raw_Dynamic_Array, size_of_elem, a
|
||||
return
|
||||
}
|
||||
|
||||
// `append_elem` appends an element to the end of a dynamic array.
|
||||
@builtin
|
||||
append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
when size_of(E) == 0 {
|
||||
@@ -575,6 +578,9 @@ append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller
|
||||
}
|
||||
}
|
||||
|
||||
// `non_zero_append_elem` appends an element to the end of a dynamic array, without zeroing any reserved memory
|
||||
//
|
||||
// Note: Prefer using the procedure group `non_zero_append
|
||||
@builtin
|
||||
non_zero_append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
when size_of(E) == 0 {
|
||||
@@ -613,6 +619,9 @@ _append_elems :: #force_no_inline proc(array: ^Raw_Dynamic_Array, size_of_elem,
|
||||
return arg_len, err
|
||||
}
|
||||
|
||||
// `append_elems` appends `args` to the end of a dynamic array.
|
||||
//
|
||||
// Note: Prefer using the procedure group `append`.
|
||||
@builtin
|
||||
append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
when size_of(E) == 0 {
|
||||
@@ -624,6 +633,9 @@ append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #ca
|
||||
}
|
||||
}
|
||||
|
||||
// `non_zero_append_elems` appends `args` to the end of a dynamic array, without zeroing any reserved memory
|
||||
//
|
||||
// Note: Prefer using the procedure group `non_zero_append
|
||||
@builtin
|
||||
non_zero_append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
when size_of(E) == 0 {
|
||||
@@ -640,10 +652,16 @@ _append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, should_ze
|
||||
return _append_elems((^Raw_Dynamic_Array)(array), 1, 1, should_zero, loc, raw_data(arg), len(arg))
|
||||
}
|
||||
|
||||
// `append_elem_string` appends a string to the end of a dynamic array of bytes
|
||||
//
|
||||
// Note: Prefer using the procedure group `append`.
|
||||
@builtin
|
||||
append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
return _append_elem_string(array, arg, true, loc)
|
||||
}
|
||||
// `non_zero_append_elem_string` appends a string to the end of a dynamic array of bytes, without zeroing any reserved memory
|
||||
//
|
||||
// Note: Prefer using the procedure group `non_zero_append`.
|
||||
@builtin
|
||||
non_zero_append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
return _append_elem_string(array, arg, false, loc)
|
||||
@@ -651,6 +669,8 @@ non_zero_append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, l
|
||||
|
||||
|
||||
// The append_string built-in procedure appends multiple strings to the end of a [dynamic]u8 like type
|
||||
//
|
||||
// Note: Prefer using the procedure group `append`.
|
||||
@builtin
|
||||
append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
n_arg: int
|
||||
@@ -665,7 +685,8 @@ append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_
|
||||
}
|
||||
|
||||
// The append built-in procedure appends elements to the end of a dynamic array
|
||||
@builtin append :: proc{
|
||||
@builtin
|
||||
append :: proc{
|
||||
append_elem,
|
||||
append_elems,
|
||||
append_elem_string,
|
||||
@@ -674,7 +695,8 @@ append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_
|
||||
append_soa_elems,
|
||||
}
|
||||
|
||||
@builtin non_zero_append :: proc{
|
||||
@builtin
|
||||
non_zero_append :: proc{
|
||||
non_zero_append_elem,
|
||||
non_zero_append_elems,
|
||||
non_zero_append_elem_string,
|
||||
@@ -684,6 +706,8 @@ append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_
|
||||
}
|
||||
|
||||
|
||||
// `append_nothing` appends an empty value to a dynamic array. It returns `1, nil` if successful, and `0, err` when it was not possible,
|
||||
// whatever `err` happens to be.
|
||||
@builtin
|
||||
append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
if array == nil {
|
||||
@@ -695,6 +719,7 @@ append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: i
|
||||
}
|
||||
|
||||
|
||||
// `inject_at_elem` injects an element in a dynamic array at a specified index and moves the previous elements after that index "across"
|
||||
@builtin
|
||||
inject_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||
when !ODIN_NO_BOUNDS_CHECK {
|
||||
@@ -716,6 +741,7 @@ inject_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcas
|
||||
return
|
||||
}
|
||||
|
||||
// `inject_at_elems` injects multiple elements in a dynamic array at a specified index and moves the previous elements after that index "across"
|
||||
@builtin
|
||||
inject_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||
when !ODIN_NO_BOUNDS_CHECK {
|
||||
@@ -742,6 +768,7 @@ inject_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadca
|
||||
return
|
||||
}
|
||||
|
||||
// `inject_at_elem_string` injects a string into a dynamic array at a specified index and moves the previous elements after that index "across"
|
||||
@builtin
|
||||
inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||
when !ODIN_NO_BOUNDS_CHECK {
|
||||
@@ -766,10 +793,13 @@ inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, ar
|
||||
return
|
||||
}
|
||||
|
||||
// `inject_at` injects something into a dynamic array at a specified index and moves the previous elements after that index "across"
|
||||
@builtin inject_at :: proc{inject_at_elem, inject_at_elems, inject_at_elem_string}
|
||||
|
||||
|
||||
|
||||
// `assign_at_elem` assigns a value at a given index. If the requested index is smaller than the current
|
||||
// size of the dynamic array, it will attempt to `resize` the a new length of `index+1` and then assign as `index`.
|
||||
@builtin
|
||||
assign_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||
if index < len(array) {
|
||||
@@ -784,6 +814,8 @@ assign_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, arg: E, loc
|
||||
}
|
||||
|
||||
|
||||
// `assign_at_elems` assigns a values at a given index. If the requested index is smaller than the current
|
||||
// size of the dynamic array, it will attempt to `resize` the a new length of `index+len(args)` and then assign as `index`.
|
||||
@builtin
|
||||
assign_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||
new_size := index + len(args)
|
||||
@@ -800,7 +832,8 @@ assign_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadca
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// `assign_at_elem_string` assigns a string at a given index. If the requested index is smaller than the current
|
||||
// size of the dynamic array, it will attempt to `resize` the a new length of `index+len(arg)` and then assign as `index`.
|
||||
@builtin
|
||||
assign_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||
new_size := index + len(arg)
|
||||
@@ -817,7 +850,14 @@ assign_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, ar
|
||||
return
|
||||
}
|
||||
|
||||
@builtin assign_at :: proc{assign_at_elem, assign_at_elems, assign_at_elem_string}
|
||||
// `assign_at` assigns a value at a given index. If the requested index is smaller than the current
|
||||
// size of the dynamic array, it will attempt to `resize` the a new length of `index+size_needed` and then assign as `index`.
|
||||
@builtin
|
||||
assign_at :: proc{
|
||||
assign_at_elem,
|
||||
assign_at_elems,
|
||||
assign_at_elem_string,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -834,6 +874,8 @@ clear_dynamic_array :: proc "contextless" (array: ^$T/[dynamic]$E) {
|
||||
|
||||
// `reserve_dynamic_array` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
|
||||
//
|
||||
// When a memory resize allocation is required, the memory will be asked to be zeroed (i.e. it calls `mem_resize`).
|
||||
//
|
||||
// Note: Prefer the procedure group `reserve`.
|
||||
_reserve_dynamic_array :: #force_no_inline proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, capacity: int, should_zero: bool, loc := #caller_location) -> Allocator_Error {
|
||||
if a == nil {
|
||||
@@ -868,11 +910,21 @@ _reserve_dynamic_array :: #force_no_inline proc(a: ^Raw_Dynamic_Array, size_of_e
|
||||
return nil
|
||||
}
|
||||
|
||||
// `reserve_dynamic_array` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
|
||||
//
|
||||
// When a memory resize allocation is required, the memory will be asked to be zeroed (i.e. it calls `mem_resize`).
|
||||
//
|
||||
// Note: Prefer the procedure group `reserve`.
|
||||
@builtin
|
||||
reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
|
||||
return _reserve_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), capacity, true, loc)
|
||||
}
|
||||
|
||||
// `non_zero_reserve_dynamic_array` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
|
||||
//
|
||||
// When a memory resize allocation is required, the memory will be asked to not be zeroed (i.e. it calls `non_zero_mem_resize`).
|
||||
//
|
||||
// Note: Prefer the procedure group `non_zero_reserve`.
|
||||
@builtin
|
||||
non_zero_reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
|
||||
return _reserve_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), capacity, false, loc)
|
||||
@@ -921,28 +973,33 @@ _resize_dynamic_array :: #force_no_inline proc(a: ^Raw_Dynamic_Array, size_of_el
|
||||
|
||||
// `resize_dynamic_array` will try to resize memory of a passed dynamic array or map to the requested element count (setting the `len`, and possibly `cap`).
|
||||
//
|
||||
// When a memory resize allocation is required, the memory will be asked to be zeroed (i.e. it calls `mem_resize`).
|
||||
//
|
||||
// Note: Prefer the procedure group `resize`
|
||||
@builtin
|
||||
resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error {
|
||||
return _resize_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), length, true, loc=loc)
|
||||
}
|
||||
|
||||
// `non_zero_resize_dynamic_array` will try to resize memory of a passed dynamic array or map to the requested element count (setting the `len`, and possibly `cap`).
|
||||
//
|
||||
// When a memory resize allocation is required, the memory will be asked to not be zeroed (i.e. it calls `non_zero_mem_resize`).
|
||||
//
|
||||
// Note: Prefer the procedure group `non_zero_resize`
|
||||
@builtin
|
||||
non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error {
|
||||
return _resize_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), length, false, loc=loc)
|
||||
}
|
||||
|
||||
/*
|
||||
Shrinks the capacity of a dynamic array down to the current length, or the given capacity.
|
||||
|
||||
If `new_cap` is negative, then `len(array)` is used.
|
||||
|
||||
Returns false if `cap(array) < new_cap`, or the allocator report failure.
|
||||
|
||||
If `len(array) < new_cap`, then `len(array)` will be left unchanged.
|
||||
|
||||
Note: Prefer the procedure group `shrink`
|
||||
*/
|
||||
// Shrinks the capacity of a dynamic array down to the current length, or the given capacity.
|
||||
//
|
||||
// If `new_cap` is negative, then `len(array)` is used.
|
||||
//
|
||||
// Returns false if `cap(array) < new_cap`, or the allocator report failure.
|
||||
//
|
||||
// If `len(array) < new_cap`, then `len(array)` will be left unchanged.
|
||||
//
|
||||
// Note: Prefer the procedure group `shrink`
|
||||
shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int new_cap := -1, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) {
|
||||
return _shrink_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), new_cap, loc)
|
||||
}
|
||||
@@ -1023,6 +1080,7 @@ map_entry :: proc(m: ^$T/map[$K]$V, key: K, loc := #caller_location) -> (key_ptr
|
||||
}
|
||||
|
||||
|
||||
// `card` returns the number of bits that are set in a bit_set—its cardinality
|
||||
@builtin
|
||||
card :: proc "contextless" (s: $S/bit_set[$E; $U]) -> int {
|
||||
return int(intrinsics.count_ones(transmute(intrinsics.type_bit_set_underlying_type(S))s))
|
||||
@@ -1030,6 +1088,10 @@ card :: proc "contextless" (s: $S/bit_set[$E; $U]) -> int {
|
||||
|
||||
|
||||
|
||||
// Evaluates the condition and panics the program iff the condition is false.
|
||||
// This uses the `context.assertion_failure_procedure` to assert.
|
||||
//
|
||||
// This routine will be ignored when `ODIN_DISABLE_ASSERT` is true.
|
||||
@builtin
|
||||
@(disabled=ODIN_DISABLE_ASSERT)
|
||||
assert :: proc(condition: bool, message := #caller_expression(condition), loc := #caller_location) {
|
||||
@@ -1050,9 +1112,9 @@ assert :: proc(condition: bool, message := #caller_expression(condition), loc :=
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluates the condition and aborts the program iff the condition is
|
||||
// false. This routine ignores `ODIN_DISABLE_ASSERT`, and will always
|
||||
// execute.
|
||||
// Evaluates the condition and panics the program iff the condition is false.
|
||||
// This uses the `context.assertion_failure_procedure` to assert.
|
||||
// This routine ignores `ODIN_DISABLE_ASSERT`, and will always execute.
|
||||
@builtin
|
||||
ensure :: proc(condition: bool, message := #caller_expression(condition), loc := #caller_location) {
|
||||
if !condition {
|
||||
@@ -1068,6 +1130,8 @@ ensure :: proc(condition: bool, message := #caller_expression(condition), loc :=
|
||||
}
|
||||
}
|
||||
|
||||
// Panics the program with a message.
|
||||
// This uses the `context.assertion_failure_procedure` to panic.
|
||||
@builtin
|
||||
panic :: proc(message: string, loc := #caller_location) -> ! {
|
||||
p := context.assertion_failure_proc
|
||||
@@ -1077,6 +1141,8 @@ panic :: proc(message: string, loc := #caller_location) -> ! {
|
||||
p("panic", message, loc)
|
||||
}
|
||||
|
||||
// Panics the program with a message to indicate something has yet to be implemented.
|
||||
// This uses the `context.assertion_failure_procedure` to assert.
|
||||
@builtin
|
||||
unimplemented :: proc(message := "", loc := #caller_location) -> ! {
|
||||
p := context.assertion_failure_proc
|
||||
@@ -1086,7 +1152,10 @@ unimplemented :: proc(message := "", loc := #caller_location) -> ! {
|
||||
p("not yet implemented", message, loc)
|
||||
}
|
||||
|
||||
|
||||
// Evaluates the condition and panics the program iff the condition is false.
|
||||
// This uses the `default_assertion_contextless_failure_proc` to assert.
|
||||
//
|
||||
// This routine will be ignored when `ODIN_DISABLE_ASSERT` is true.
|
||||
@builtin
|
||||
@(disabled=ODIN_DISABLE_ASSERT)
|
||||
assert_contextless :: proc "contextless" (condition: bool, message := #caller_expression(condition), loc := #caller_location) {
|
||||
@@ -1103,6 +1172,8 @@ assert_contextless :: proc "contextless" (condition: bool, message := #caller_ex
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluates the condition and panics the program iff the condition is false.
|
||||
// This uses the `default_assertion_contextless_failure_proc` to assert.
|
||||
@builtin
|
||||
ensure_contextless :: proc "contextless" (condition: bool, message := #caller_expression(condition), loc := #caller_location) {
|
||||
if !condition {
|
||||
@@ -1114,11 +1185,15 @@ ensure_contextless :: proc "contextless" (condition: bool, message := #caller_ex
|
||||
}
|
||||
}
|
||||
|
||||
// Panics the program with a message to indicate something has yet to be implemented.
|
||||
// This uses the `default_assertion_contextless_failure_proc` to assert.
|
||||
@builtin
|
||||
panic_contextless :: proc "contextless" (message: string, loc := #caller_location) -> ! {
|
||||
default_assertion_contextless_failure_proc("panic", message, loc)
|
||||
}
|
||||
|
||||
// Panics the program with a message.
|
||||
// This uses the `default_assertion_contextless_failure_proc` to assert.
|
||||
@builtin
|
||||
unimplemented_contextless :: proc "contextless" (message := "", loc := #caller_location) -> ! {
|
||||
default_assertion_contextless_failure_proc("not yet implemented", message, loc)
|
||||
|
||||
@@ -23,6 +23,14 @@ nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return nil, .None
|
||||
}
|
||||
|
||||
// nil_allocator returns an allocator which will return `nil` for any result.
|
||||
// * `.Alloc`, `.Alloc_Non_Zero`, `.Resize`, `.Resize_Non_Zeroed` will return `nil, .Out_Of_Memory`
|
||||
// * `.Free` will return `nil, .None`
|
||||
// * `.Free_All` will return `nil, .Mode_Not_Implemented`
|
||||
// * `.Query_Features`, `.Query_Info` will return `nil, .Mode_Not_Implemented`
|
||||
//
|
||||
// This is extremely useful for creating a dynamic array from a buffer which does not nothing
|
||||
// on a resize/reserve beyond the originally allocated memory.
|
||||
@(require_results)
|
||||
nil_allocator :: proc "contextless" () -> Allocator {
|
||||
return Allocator{
|
||||
@@ -73,6 +81,9 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// panic_allocator returns an allocator which will panic for any non-zero-sized allocation or `query_info`
|
||||
//
|
||||
// This is extremely useful for to check when something does a memory operation when it should not, and thus panic.
|
||||
@(require_results)
|
||||
panic_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
|
||||
@@ -4,6 +4,7 @@ DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE: int : #config(DEFAULT_TEMP_ALLOCATOR_BACKIN
|
||||
NO_DEFAULT_TEMP_ALLOCATOR: bool : ODIN_OS == .Freestanding || ODIN_DEFAULT_TO_NIL_ALLOCATOR
|
||||
|
||||
when NO_DEFAULT_TEMP_ALLOCATOR {
|
||||
// `Default_Temp_Allocator` is a `nil_allocator` when `NO_DEFAULT_TEMP_ALLOCATOR` is `true`.
|
||||
Default_Temp_Allocator :: struct {}
|
||||
|
||||
default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backing_allocator := context.allocator) {}
|
||||
@@ -20,6 +21,11 @@ when NO_DEFAULT_TEMP_ALLOCATOR {
|
||||
default_temp_allocator_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) {
|
||||
}
|
||||
} else {
|
||||
// `Default_Temp_Allocator` is an `Arena` based type of allocator. See `runtime.Arena` for its implementation.
|
||||
// The default `context.temp_allocator` is typically called with `free_all(context.temp_allocator)` once per "frame-loop"
|
||||
// to prevent it from "leaking" memory.
|
||||
//
|
||||
// Note: `Default_Temp_Allocator` is a `nil_allocator` when `NO_DEFAULT_TEMP_ALLOCATOR` is `true`.
|
||||
Default_Temp_Allocator :: struct {
|
||||
arena: Arena,
|
||||
}
|
||||
|
||||
@@ -15,16 +15,25 @@ objc_SEL :: ^intrinsics.objc_selector
|
||||
objc_Ivar :: ^intrinsics.objc_ivar
|
||||
objc_BOOL :: bool
|
||||
|
||||
objc_super :: struct {
|
||||
receiver: objc_id,
|
||||
super_class: objc_Class,
|
||||
}
|
||||
|
||||
objc_IMP :: proc "c" (object: objc_id, sel: objc_SEL, #c_vararg args: ..any) -> objc_id
|
||||
|
||||
foreign ObjC {
|
||||
sel_registerName :: proc "c" (name: cstring) -> objc_SEL ---
|
||||
|
||||
objc_msgSend :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) ---
|
||||
objc_msgSend_fpret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> f64 ---
|
||||
objc_msgSend_fp2ret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> complex128 ---
|
||||
objc_msgSend_stret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) ---
|
||||
objc_msgSend :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) ---
|
||||
objc_msgSend_fpret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> f64 ---
|
||||
objc_msgSend_fp2ret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> complex128 ---
|
||||
objc_msgSend_stret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) ---
|
||||
|
||||
// See: https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/objc-abi.h#L111
|
||||
objc_msgSendSuper2 :: proc "c" (super: rawptr, op: objc_SEL, #c_vararg args: ..any) -> objc_id ---
|
||||
objc_msgSendSuper2_stret :: proc "c" (super: ^objc_super, op: objc_SEL, #c_vararg args: ..any) ---
|
||||
|
||||
|
||||
objc_lookUpClass :: proc "c" (name: cstring) -> objc_Class ---
|
||||
objc_allocateClassPair :: proc "c" (superclass: objc_Class, name: cstring, extraBytes: uint) -> objc_Class ---
|
||||
@@ -33,6 +42,7 @@ foreign ObjC {
|
||||
class_addIvar :: proc "c" (cls: objc_Class, name: cstring, size: uint, alignment: u8, types: cstring) -> objc_BOOL ---
|
||||
class_getInstanceVariable :: proc "c" (cls : objc_Class, name: cstring) -> objc_Ivar ---
|
||||
class_getInstanceSize :: proc "c" (cls : objc_Class) -> uint ---
|
||||
class_getSuperclass :: proc "c" (cls : objc_Class) -> objc_Class ---
|
||||
ivar_getOffset :: proc "c" (v: objc_Ivar) -> uintptr ---
|
||||
object_getClass :: proc "c" (obj: objc_id) -> objc_Class ---
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ MIN_READ_BUFFER_SIZE :: 16
|
||||
@(private)
|
||||
DEFAULT_MAX_CONSECUTIVE_EMPTY_READS :: 128
|
||||
|
||||
// reader_init initializes using an `allocator`
|
||||
reader_init :: proc(b: ^Reader, rd: io.Reader, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator, loc := #caller_location) {
|
||||
size := size
|
||||
size = max(size, MIN_READ_BUFFER_SIZE)
|
||||
@@ -37,6 +38,7 @@ reader_init :: proc(b: ^Reader, rd: io.Reader, size: int = DEFAULT_BUF_SIZE, all
|
||||
b.buf = make([]byte, size, allocator, loc)
|
||||
}
|
||||
|
||||
// reader_init initializes using a user provided bytes buffer `buf`
|
||||
reader_init_with_buf :: proc(b: ^Reader, rd: io.Reader, buf: []byte) {
|
||||
reader_reset(b, rd)
|
||||
b.buf_allocator = {}
|
||||
@@ -49,10 +51,12 @@ reader_destroy :: proc(b: ^Reader) {
|
||||
b^ = {}
|
||||
}
|
||||
|
||||
// reader_size returns the number of bytes in the backing buffer
|
||||
reader_size :: proc(b: ^Reader) -> int {
|
||||
return len(b.buf)
|
||||
}
|
||||
|
||||
// reader_reset resets the read and write positions, and the error values
|
||||
reader_reset :: proc(b: ^Reader, r: io.Reader) {
|
||||
b.rd = r
|
||||
b.r, b.w = 0, 0
|
||||
|
||||
+21
-7
@@ -46,6 +46,7 @@ DEFAULT_MAX_SCAN_TOKEN_SIZE :: 1<<16
|
||||
@(private)
|
||||
_INIT_BUF_SIZE :: 4096
|
||||
|
||||
// Initializes a Scanner buffer an allocator `buf_allocator`
|
||||
scanner_init :: proc(s: ^Scanner, r: io.Reader, buf_allocator := context.allocator) -> ^Scanner {
|
||||
s.r = r
|
||||
s.split = scan_lines
|
||||
@@ -53,6 +54,8 @@ scanner_init :: proc(s: ^Scanner, r: io.Reader, buf_allocator := context.allocat
|
||||
s.buf.allocator = buf_allocator
|
||||
return s
|
||||
}
|
||||
|
||||
// Initializes a Scanner buffer a user provided bytes buffer `buf`
|
||||
scanner_init_with_buffer :: proc(s: ^Scanner, r: io.Reader, buf: []byte) -> ^Scanner {
|
||||
s.r = r
|
||||
s.split = scan_lines
|
||||
@@ -75,24 +78,27 @@ scanner_error :: proc(s: ^Scanner) -> Scanner_Error {
|
||||
return s._err
|
||||
}
|
||||
|
||||
// Returns the most recent token created by scanner_scan.
|
||||
// Returns the most recent token created by 'scan'.
|
||||
// The underlying array may point to data that may be overwritten
|
||||
// by another call to scanner_scan.
|
||||
// by another call to 'scan'.
|
||||
// Treat the returned value as if it is immutable.
|
||||
scanner_bytes :: proc(s: ^Scanner) -> []byte {
|
||||
return s.token
|
||||
}
|
||||
|
||||
// Returns the most recent token created by scanner_scan.
|
||||
// Returns the most recent token created by 'scan'.
|
||||
// The underlying array may point to data that may be overwritten
|
||||
// by another call to scanner_scan.
|
||||
// by another call to 'scan'.
|
||||
// Treat the returned value as if it is immutable.
|
||||
scanner_text :: proc(s: ^Scanner) -> string {
|
||||
return string(s.token)
|
||||
}
|
||||
|
||||
// scanner_scan advances the scanner
|
||||
scanner_scan :: proc(s: ^Scanner) -> bool {
|
||||
// scanner_scan is an alias of scan
|
||||
scanner_scan :: scan
|
||||
|
||||
// scan advances the Scanner
|
||||
scan :: proc(s: ^Scanner) -> bool {
|
||||
set_err :: proc(s: ^Scanner, err: Scanner_Error) {
|
||||
switch s._err {
|
||||
case nil, .EOF:
|
||||
@@ -229,6 +235,7 @@ scanner_scan :: proc(s: ^Scanner) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
// scan_bytes is a splitting procedure that returns each byte as a token
|
||||
scan_bytes :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
|
||||
if at_eof && len(data) == 0 {
|
||||
return
|
||||
@@ -236,6 +243,10 @@ scan_bytes :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte,
|
||||
return 1, data[0:1], nil, false
|
||||
}
|
||||
|
||||
// scan_runes is a splitting procedure that returns each UTF-8 encoded rune as a token.
|
||||
// The lsit of runes return is equivalent to that of iterating over a string in a 'for in' loop, meaning any
|
||||
// erroneous UTF-8 encodings will be returned as U+FFFD. Unfortunately this means it is impossible for the "client"
|
||||
// to know whether a U+FFFD is an expected replacement rune or an encoding of an error.
|
||||
scan_runes :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
|
||||
if at_eof && len(data) == 0 {
|
||||
return
|
||||
@@ -264,7 +275,8 @@ scan_runes :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte,
|
||||
token = ERROR_RUNE
|
||||
return
|
||||
}
|
||||
|
||||
// scan_words is a splitting procedure that returns each Unicode-space-separated word of text, excluding the surrounded spaces.
|
||||
// It will never return return an empty string.
|
||||
scan_words :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
|
||||
is_space :: proc "contextless" (r: rune) -> bool {
|
||||
switch r {
|
||||
@@ -312,6 +324,8 @@ scan_words :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte,
|
||||
return
|
||||
}
|
||||
|
||||
// scan_lines is a splitting procedure that returns each line of text stripping of any trailing newline and an optional preceding carriage return (\r?\n).
|
||||
// A new line is allowed to be empty.
|
||||
scan_lines :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
|
||||
trim_carriage_return :: proc "contextless" (data: []byte) -> []byte {
|
||||
if len(data) > 0 && data[len(data)-1] == '\r' {
|
||||
|
||||
@@ -19,6 +19,7 @@ Writer :: struct {
|
||||
|
||||
}
|
||||
|
||||
// Initialized a Writer with an `allocator`
|
||||
writer_init :: proc(b: ^Writer, wr: io.Writer, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator) {
|
||||
size := size
|
||||
size = max(size, MIN_READ_BUFFER_SIZE)
|
||||
@@ -27,6 +28,7 @@ writer_init :: proc(b: ^Writer, wr: io.Writer, size: int = DEFAULT_BUF_SIZE, all
|
||||
b.buf = make([]byte, size, allocator)
|
||||
}
|
||||
|
||||
// Initialized a Writer with a user provided buffer `buf`
|
||||
writer_init_with_buf :: proc(b: ^Writer, wr: io.Writer, buf: []byte) {
|
||||
writer_reset(b, wr)
|
||||
b.buf_allocator = {}
|
||||
|
||||
@@ -134,8 +134,13 @@ equal_fold :: proc(u, v: []byte) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO(bill): Unicode folding
|
||||
|
||||
r := unicode.simple_fold(sr)
|
||||
for r != sr && r < tr {
|
||||
r = unicode.simple_fold(sr)
|
||||
}
|
||||
if r == tr {
|
||||
continue loop
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -176,7 +176,11 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
return .Unsupported_Type
|
||||
|
||||
case runtime.Type_Info_Pointer:
|
||||
return .Unsupported_Type
|
||||
if v.id == typeid_of(Null) {
|
||||
io.write_string(w, "null") or_return
|
||||
} else {
|
||||
return .Unsupported_Type
|
||||
}
|
||||
|
||||
case runtime.Type_Info_Multi_Pointer:
|
||||
return .Unsupported_Type
|
||||
|
||||
@@ -164,25 +164,25 @@ orthogonal :: proc{vector2_orthogonal, vector3_orthogonal}
|
||||
|
||||
@(require_results)
|
||||
vector4_srgb_to_linear_f16 :: proc "contextless" (col: Vector4f16) -> Vector4f16 {
|
||||
r := math.pow(col.x, 2.2)
|
||||
g := math.pow(col.y, 2.2)
|
||||
b := math.pow(col.z, 2.2)
|
||||
r := math.pow((col.x + 0.055) / 1.055, 2.4) if col.x > 0.04045 else col.x / 12.92
|
||||
g := math.pow((col.y + 0.055) / 1.055, 2.4) if col.y > 0.04045 else col.y / 12.92
|
||||
b := math.pow((col.z + 0.055) / 1.055, 2.4) if col.z > 0.04045 else col.z / 12.92
|
||||
a := col.w
|
||||
return {r, g, b, a}
|
||||
}
|
||||
@(require_results)
|
||||
vector4_srgb_to_linear_f32 :: proc "contextless" (col: Vector4f32) -> Vector4f32 {
|
||||
r := math.pow(col.x, 2.2)
|
||||
g := math.pow(col.y, 2.2)
|
||||
b := math.pow(col.z, 2.2)
|
||||
r := math.pow((col.x + 0.055) / 1.055, 2.4) if col.x > 0.04045 else col.x / 12.92
|
||||
g := math.pow((col.y + 0.055) / 1.055, 2.4) if col.y > 0.04045 else col.y / 12.92
|
||||
b := math.pow((col.z + 0.055) / 1.055, 2.4) if col.z > 0.04045 else col.z / 12.92
|
||||
a := col.w
|
||||
return {r, g, b, a}
|
||||
}
|
||||
@(require_results)
|
||||
vector4_srgb_to_linear_f64 :: proc "contextless" (col: Vector4f64) -> Vector4f64 {
|
||||
r := math.pow(col.x, 2.2)
|
||||
g := math.pow(col.y, 2.2)
|
||||
b := math.pow(col.z, 2.2)
|
||||
r := math.pow((col.x + 0.055) / 1.055, 2.4) if col.x > 0.04045 else col.x / 12.92
|
||||
g := math.pow((col.y + 0.055) / 1.055, 2.4) if col.y > 0.04045 else col.y / 12.92
|
||||
b := math.pow((col.z + 0.055) / 1.055, 2.4) if col.z > 0.04045 else col.z / 12.92
|
||||
a := col.w
|
||||
return {r, g, b, a}
|
||||
}
|
||||
@@ -192,70 +192,55 @@ vector4_srgb_to_linear :: proc{
|
||||
vector4_srgb_to_linear_f64,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
vector3_srgb_to_linear_f16 :: proc "contextless" (col: Vector3f16) -> Vector3f16 {
|
||||
r := math.pow((col.x + 0.055) / 1.055, 2.4) if col.x > 0.04045 else col.x / 12.92
|
||||
g := math.pow((col.y + 0.055) / 1.055, 2.4) if col.y > 0.04045 else col.y / 12.92
|
||||
b := math.pow((col.z + 0.055) / 1.055, 2.4) if col.z > 0.04045 else col.z / 12.92
|
||||
return {r, g, b}
|
||||
}
|
||||
@(require_results)
|
||||
vector3_srgb_to_linear_f32 :: proc "contextless" (col: Vector3f32) -> Vector3f32 {
|
||||
r := math.pow((col.x + 0.055) / 1.055, 2.4) if col.x > 0.04045 else col.x / 12.92
|
||||
g := math.pow((col.y + 0.055) / 1.055, 2.4) if col.y > 0.04045 else col.y / 12.92
|
||||
b := math.pow((col.z + 0.055) / 1.055, 2.4) if col.z > 0.04045 else col.z / 12.92
|
||||
return {r, g, b}
|
||||
}
|
||||
@(require_results)
|
||||
vector3_srgb_to_linear_f64 :: proc "contextless" (col: Vector3f64) -> Vector3f64 {
|
||||
r := math.pow((col.x + 0.055) / 1.055, 2.4) if col.x > 0.04045 else col.x / 12.92
|
||||
g := math.pow((col.y + 0.055) / 1.055, 2.4) if col.y > 0.04045 else col.y / 12.92
|
||||
b := math.pow((col.z + 0.055) / 1.055, 2.4) if col.z > 0.04045 else col.z / 12.92
|
||||
return {r, g, b}
|
||||
}
|
||||
vector3_srgb_to_linear :: proc{
|
||||
vector3_srgb_to_linear_f16,
|
||||
vector3_srgb_to_linear_f32,
|
||||
vector3_srgb_to_linear_f64,
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
vector4_linear_to_srgb_f16 :: proc "contextless" (col: Vector4f16) -> Vector4f16 {
|
||||
a :: 2.51
|
||||
b :: 0.03
|
||||
c :: 2.43
|
||||
d :: 0.59
|
||||
e :: 0.14
|
||||
|
||||
x := col.x
|
||||
y := col.y
|
||||
z := col.z
|
||||
|
||||
x = (x * (a * x + b)) / (x * (c * x + d) + e)
|
||||
y = (y * (a * y + b)) / (y * (c * y + d) + e)
|
||||
z = (z * (a * z + b)) / (z * (c * z + d) + e)
|
||||
|
||||
x = math.pow(clamp(x, 0, 1), 1.0 / 2.2)
|
||||
y = math.pow(clamp(y, 0, 1), 1.0 / 2.2)
|
||||
z = math.pow(clamp(z, 0, 1), 1.0 / 2.2)
|
||||
x := 1.055 * math.pow(col.x, 1.0 / 2.4) - 0.055 if col.x > 0.0031308 else 12.92 * col.x
|
||||
y := 1.055 * math.pow(col.y, 1.0 / 2.4) - 0.055 if col.y > 0.0031308 else 12.92 * col.y
|
||||
z := 1.055 * math.pow(col.z, 1.0 / 2.4) - 0.055 if col.z > 0.0031308 else 12.92 * col.z
|
||||
|
||||
return {x, y, z, col.w}
|
||||
}
|
||||
@(require_results)
|
||||
vector4_linear_to_srgb_f32 :: proc "contextless" (col: Vector4f32) -> Vector4f32 {
|
||||
a :: 2.51
|
||||
b :: 0.03
|
||||
c :: 2.43
|
||||
d :: 0.59
|
||||
e :: 0.14
|
||||
|
||||
x := col.x
|
||||
y := col.y
|
||||
z := col.z
|
||||
|
||||
x = (x * (a * x + b)) / (x * (c * x + d) + e)
|
||||
y = (y * (a * y + b)) / (y * (c * y + d) + e)
|
||||
z = (z * (a * z + b)) / (z * (c * z + d) + e)
|
||||
|
||||
x = math.pow(clamp(x, 0, 1), 1.0 / 2.2)
|
||||
y = math.pow(clamp(y, 0, 1), 1.0 / 2.2)
|
||||
z = math.pow(clamp(z, 0, 1), 1.0 / 2.2)
|
||||
x := 1.055 * math.pow(col.x, 1.0 / 2.4) - 0.055 if col.x > 0.0031308 else 12.92 * col.x
|
||||
y := 1.055 * math.pow(col.y, 1.0 / 2.4) - 0.055 if col.y > 0.0031308 else 12.92 * col.y
|
||||
z := 1.055 * math.pow(col.z, 1.0 / 2.4) - 0.055 if col.z > 0.0031308 else 12.92 * col.z
|
||||
|
||||
return {x, y, z, col.w}
|
||||
}
|
||||
@(require_results)
|
||||
vector4_linear_to_srgb_f64 :: proc "contextless" (col: Vector4f64) -> Vector4f64 {
|
||||
a :: 2.51
|
||||
b :: 0.03
|
||||
c :: 2.43
|
||||
d :: 0.59
|
||||
e :: 0.14
|
||||
|
||||
x := col.x
|
||||
y := col.y
|
||||
z := col.z
|
||||
|
||||
x = (x * (a * x + b)) / (x * (c * x + d) + e)
|
||||
y = (y * (a * y + b)) / (y * (c * y + d) + e)
|
||||
z = (z * (a * z + b)) / (z * (c * z + d) + e)
|
||||
|
||||
x = math.pow(clamp(x, 0, 1), 1.0 / 2.2)
|
||||
y = math.pow(clamp(y, 0, 1), 1.0 / 2.2)
|
||||
z = math.pow(clamp(z, 0, 1), 1.0 / 2.2)
|
||||
x := 1.055 * math.pow(col.x, 1.0 / 2.4) - 0.055 if col.x > 0.0031308 else 12.92 * col.x
|
||||
y := 1.055 * math.pow(col.y, 1.0 / 2.4) - 0.055 if col.y > 0.0031308 else 12.92 * col.y
|
||||
z := 1.055 * math.pow(col.z, 1.0 / 2.4) - 0.055 if col.z > 0.0031308 else 12.92 * col.z
|
||||
|
||||
return {x, y, z, col.w}
|
||||
}
|
||||
@@ -265,6 +250,36 @@ vector4_linear_to_srgb :: proc{
|
||||
vector4_linear_to_srgb_f64,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
vector3_linear_to_srgb_f16 :: proc "contextless" (col: Vector3f16) -> Vector3f16 {
|
||||
x := 1.055 * math.pow(col.x, 1.0 / 2.4) - 0.055 if col.x > 0.0031308 else 12.92 * col.x
|
||||
y := 1.055 * math.pow(col.y, 1.0 / 2.4) - 0.055 if col.y > 0.0031308 else 12.92 * col.y
|
||||
z := 1.055 * math.pow(col.z, 1.0 / 2.4) - 0.055 if col.z > 0.0031308 else 12.92 * col.z
|
||||
|
||||
return {x, y, z}
|
||||
}
|
||||
@(require_results)
|
||||
vector3_linear_to_srgb_f32 :: proc "contextless" (col: Vector3f32) -> Vector3f32 {
|
||||
x := 1.055 * math.pow(col.x, 1.0 / 2.4) - 0.055 if col.x > 0.0031308 else 12.92 * col.x
|
||||
y := 1.055 * math.pow(col.y, 1.0 / 2.4) - 0.055 if col.y > 0.0031308 else 12.92 * col.y
|
||||
z := 1.055 * math.pow(col.z, 1.0 / 2.4) - 0.055 if col.z > 0.0031308 else 12.92 * col.z
|
||||
|
||||
return {x, y, z}
|
||||
}
|
||||
@(require_results)
|
||||
vector3_linear_to_srgb_f64 :: proc "contextless" (col: Vector3f64) -> Vector3f64 {
|
||||
x := 1.055 * math.pow(col.x, 1.0 / 2.4) - 0.055 if col.x > 0.0031308 else 12.92 * col.x
|
||||
y := 1.055 * math.pow(col.y, 1.0 / 2.4) - 0.055 if col.y > 0.0031308 else 12.92 * col.y
|
||||
z := 1.055 * math.pow(col.z, 1.0 / 2.4) - 0.055 if col.z > 0.0031308 else 12.92 * col.z
|
||||
|
||||
return {x, y, z}
|
||||
}
|
||||
vector3_linear_to_srgb :: proc{
|
||||
vector3_linear_to_srgb_f16,
|
||||
vector3_linear_to_srgb_f32,
|
||||
vector3_linear_to_srgb_f64,
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
vector4_hsl_to_rgb_f16 :: proc "contextless" (h, s, l: f16, a: f16 = 1) -> Vector4f16 {
|
||||
|
||||
+6
-1
@@ -402,7 +402,12 @@ remap :: proc "contextless" (old_value, old_min, old_max, new_min, new_max: $T)
|
||||
if old_range == 0 {
|
||||
return new_range / 2
|
||||
}
|
||||
return ((old_value - old_min) / old_range) * new_range + new_min
|
||||
|
||||
when intrinsics.type_is_integer(T) {
|
||||
return (((old_value - old_min)) * new_range) / old_range + new_min
|
||||
} else {
|
||||
return ((old_value - old_min) / old_range) * new_range + new_min
|
||||
}
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
|
||||
@@ -199,7 +199,7 @@ _get_full_path :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> (fullpath:
|
||||
buf: [32]u8
|
||||
copy(buf[:], PROC_FD_PATH)
|
||||
|
||||
strconv.itoa(buf[len(PROC_FD_PATH):], int(fd))
|
||||
strconv.write_int(buf[len(PROC_FD_PATH):], i64(fd), 10)
|
||||
|
||||
if fullpath, err = _read_link_cstr(cstring(&buf[0]), allocator); err != nil || fullpath[0] != '/' {
|
||||
delete(fullpath, allocator)
|
||||
|
||||
@@ -908,7 +908,7 @@ _dup :: proc(fd: Handle) -> (Handle, Error) {
|
||||
@(require_results)
|
||||
absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
|
||||
buf : [256]byte
|
||||
fd_str := strconv.itoa( buf[:], cast(int)fd )
|
||||
fd_str := strconv.write_int( buf[:], cast(i64)fd, 10 )
|
||||
|
||||
procfs_path := strings.concatenate( []string{ "/proc/self/fd/", fd_str } )
|
||||
defer delete(procfs_path)
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// Package reflect provides utility procedures and types to perform runtime type introspection/reflection (RTTI).
|
||||
//
|
||||
// WARNING! THIS IS ADVANCED BEHAVIOUR FOR ODIN! THIS SHOULD NOT BE USED BY BEGINNERS TO ODIN!
|
||||
//
|
||||
// This package is only to be used by individuals who know exactly how the RTTI works as well as EXACTLY how `any` works.
|
||||
// Especially since `any` can be unintuitive in its use to many, it can be dangerous to use. It is highly recommend that you **do not**
|
||||
// use `any` unless you know exactly what you are doing.
|
||||
//
|
||||
// RTTI is an extremely powerful tool which should only be used when absolutely necessary (such runtime-type-safe formatted printing).
|
||||
//
|
||||
// ## The Type System of Odin
|
||||
//
|
||||
// It is important to understand how the type systems works in Odin before using any RTTI. A good example of this is Odin's `distinct` type system.
|
||||
// In Odin, `distinct` types are represented by `Type_Info_Named`. A named struct is a `Type_Info_Named` which then points to a `Type_Info_Struct`.
|
||||
// This means you must use something like `type_info_base` to restrict the `Type_Info_Named` aspect and get the base-type directly. Doing a type-assertion
|
||||
// on the variant will not work as (incorrectly) expected without doing this.
|
||||
//
|
||||
// ## Advanced Information of How `any` Works
|
||||
//
|
||||
// An overview of how `any` works:
|
||||
//
|
||||
// An `any` type can reference any data type. It is functionally equivalent to `struct {data: rawptr, id: typeid}` with extra semantics on
|
||||
// how assignment and type assertion works.
|
||||
//
|
||||
// This is commonly used to construct runtime-type-safe printing, such as in `core:fmt`.
|
||||
// The use of `any` outside of this is heavily discourage and should be only used by people who FULLY understand its semantics.
|
||||
//
|
||||
// The `any` value is only valid as long as the underlying data is still valid. Passing a literal to an `any` will allocate the literal in
|
||||
// the current stack frame.
|
||||
//
|
||||
// Example:
|
||||
// x: int = 123
|
||||
// a: any = x
|
||||
// // equivalent to
|
||||
// a: any
|
||||
// a.data = &x
|
||||
// a.id = typeid_of(type_of(x))
|
||||
// // With literals
|
||||
// v: any = 123
|
||||
// // equivalent to
|
||||
// v: any
|
||||
// _tmp: int = 123
|
||||
// v.data = &_tmp
|
||||
// v.id = typeid_of(type_of(_tmp))
|
||||
//
|
||||
//
|
||||
// `any` is a topologically-dual to a `union` in terms of its usage. Both support assignments of differing types
|
||||
// (`any` being open to any type, `union` being closed to a specific set of types), type assertions (`x.(T)`), and `switch in`.
|
||||
// The main internal difference is how the memory is stored. `any` being open is a pointer+typeid, a `union`
|
||||
// is a blob+tag. A `union` does not need to store a `typeid` because it is a closed ABI-consistent set of variant types.
|
||||
package reflect
|
||||
@@ -2,6 +2,10 @@ package reflect
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
// An iterator to dynamically iterate across something that is array-like (or pointer-to-array-like)
|
||||
// Example:
|
||||
// it: int // used as a tracking value
|
||||
// for elem, idx in iterate_array(any_array_val, &it) { ... }
|
||||
@(require_results)
|
||||
iterate_array :: proc(val: any, it: ^int) -> (elem: any, index: int, ok: bool) {
|
||||
if val == nil || it == nil {
|
||||
@@ -45,6 +49,10 @@ iterate_array :: proc(val: any, it: ^int) -> (elem: any, index: int, ok: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
// An iterator to dynamically iterate across map (or pointer-to-map)
|
||||
// Example:
|
||||
// it: int // used as a tracking value
|
||||
// for key, val in iterate_map(any_map_val, &it) { ... }
|
||||
@(require_results)
|
||||
iterate_map :: proc(val: any, it: ^int) -> (key, value: any, ok: bool) {
|
||||
if val == nil || it == nil {
|
||||
|
||||
+231
-33
@@ -70,6 +70,7 @@ Type_Kind :: enum {
|
||||
}
|
||||
|
||||
|
||||
// type_kind returns a enum `Type_Kind` to state what kind of type a typeid is
|
||||
@(require_results)
|
||||
type_kind :: proc(T: typeid) -> Type_Kind {
|
||||
ti := type_info_of(T)
|
||||
@@ -108,31 +109,51 @@ type_kind :: proc(T: typeid) -> Type_Kind {
|
||||
return .Invalid
|
||||
}
|
||||
|
||||
// TODO(bill): Better name
|
||||
// Returns the `Type_Kind` of the base-type of a typeid.
|
||||
@(require_results)
|
||||
underlying_type_kind :: proc(T: typeid) -> Type_Kind {
|
||||
return type_kind(runtime.typeid_base(T))
|
||||
}
|
||||
|
||||
// TODO(bill): Better name
|
||||
// Returns the `Type_Kind` of the core-type of a typeid. See
|
||||
@(require_results)
|
||||
backing_type_kind :: proc(T: typeid) -> Type_Kind {
|
||||
return type_kind(runtime.typeid_core(T))
|
||||
}
|
||||
|
||||
|
||||
// type_info_base returns the base-type of a `^Type_Info` stripping the `distinct`ness from the first level
|
||||
type_info_base :: runtime.type_info_base
|
||||
|
||||
// type_info_core returns the core-type of a `^Type_Info` stripping the `distinct`ness from the first level AND/OR
|
||||
// returns the backing integer type of an enum or bit_set `^Type_Info`.
|
||||
// This is also aliased as `type_info_base_without_enum`
|
||||
type_info_core :: runtime.type_info_core
|
||||
|
||||
|
||||
// type_info_base_without_enum returns the core-type of a `^Type_Info` stripping the `distinct`ness from the first level AND/OR
|
||||
// returns the backing integer type of an enum or bit_set `^Type_Info`.
|
||||
// This is also aliased as `type_info_core`
|
||||
type_info_base_without_enum :: type_info_core
|
||||
|
||||
|
||||
when !ODIN_NO_RTTI {
|
||||
// typeid_base returns the base-type of a `typeid` stripping the `distinct`ness from the first level
|
||||
typeid_base :: runtime.typeid_base
|
||||
|
||||
// typeid_core returns the core-type of a `typeid` stripping the `distinct`ness from the first level AND/OR
|
||||
// returns the backing integer type of an enum or bit_set `typeid`.
|
||||
// This is also aliased as `typeid_base_without_enum`
|
||||
typeid_core :: runtime.typeid_core
|
||||
|
||||
// typeid_base_without_enum returns the core-type of a `typeid` stripping the `distinct`ness from the first level AND/OR
|
||||
// returns the backing integer type of an enum or bit_set `typeid`.
|
||||
// This is also aliased as `typeid_core`
|
||||
typeid_base_without_enum :: typeid_core
|
||||
}
|
||||
|
||||
|
||||
// any_base returns an `any` where the `typeid` has been replaced with the `base-type` equivalent
|
||||
@(require_results)
|
||||
any_base :: proc(v: any) -> any {
|
||||
v := v
|
||||
@@ -141,6 +162,8 @@ any_base :: proc(v: any) -> any {
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// any_core returns an `any` where the `typeid` has been replaced with the `core-type` equivalent
|
||||
@(require_results)
|
||||
any_core :: proc(v: any) -> any {
|
||||
v := v
|
||||
@@ -150,6 +173,20 @@ any_core :: proc(v: any) -> any {
|
||||
return v
|
||||
}
|
||||
|
||||
// typeid_elem returns a `typeid` of the element-type of a type if possible, otherwise it returns itself
|
||||
// complex32 -> f16
|
||||
// complex64 -> f32
|
||||
// complex128 -> f64
|
||||
// quaternion64 -> f16
|
||||
// quaternion128 -> f32
|
||||
// quaternion256 -> f64
|
||||
// ^T -> T
|
||||
// [^]T -> T
|
||||
// #soa^T -> T
|
||||
// [N]T -> T
|
||||
// []T -> T
|
||||
// [dynamic]T -> T
|
||||
// #simd[N]T -> T
|
||||
@(require_results)
|
||||
typeid_elem :: proc(id: typeid) -> typeid {
|
||||
ti := type_info_of(id)
|
||||
@@ -160,11 +197,13 @@ typeid_elem :: proc(id: typeid) -> typeid {
|
||||
#partial switch v in ti.variant {
|
||||
case Type_Info_Complex:
|
||||
switch bits {
|
||||
case 32: return f16
|
||||
case 64: return f32
|
||||
case 128: return f64
|
||||
}
|
||||
case Type_Info_Quaternion:
|
||||
switch bits {
|
||||
case 64: return f16
|
||||
case 128: return f32
|
||||
case 256: return f64
|
||||
}
|
||||
@@ -181,6 +220,7 @@ typeid_elem :: proc(id: typeid) -> typeid {
|
||||
}
|
||||
|
||||
|
||||
// returns the size of the type that the passed typeid represents
|
||||
@(require_results)
|
||||
size_of_typeid :: proc(T: typeid) -> int {
|
||||
if ti := type_info_of(T); ti != nil {
|
||||
@@ -189,6 +229,7 @@ size_of_typeid :: proc(T: typeid) -> int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// returns the alignment of the type that the passed typeid represents
|
||||
@(require_results)
|
||||
align_of_typeid :: proc(T: typeid) -> int {
|
||||
if ti := type_info_of(T); ti != nil {
|
||||
@@ -197,6 +238,7 @@ align_of_typeid :: proc(T: typeid) -> int {
|
||||
return 1
|
||||
}
|
||||
|
||||
// Reinterprets the data stored at `v` as a slice of bytes
|
||||
@(require_results)
|
||||
as_bytes :: proc(v: any) -> []byte {
|
||||
if v != nil {
|
||||
@@ -206,11 +248,13 @@ as_bytes :: proc(v: any) -> []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Splits the data stored in `any` into its two components: `data` and `id`
|
||||
@(require_results)
|
||||
any_data :: #force_inline proc(v: any) -> (data: rawptr, id: typeid) {
|
||||
return v.data, v.id
|
||||
}
|
||||
|
||||
// Returns true if the `any` value is either `nil` or the data stored at the address is all zeroed
|
||||
@(require_results)
|
||||
is_nil :: proc(v: any) -> bool {
|
||||
if v == nil {
|
||||
@@ -228,6 +272,16 @@ is_nil :: proc(v: any) -> bool {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
// Returns the length of the type that represents the `any` value, or returns 0 if not possible
|
||||
// len(^T) -> len(T)
|
||||
// len([N]T) -> N
|
||||
// len(#simd[N]T) -> N
|
||||
// len([]T)
|
||||
// len([dynamic]T)
|
||||
// len(map[K]V)
|
||||
// len(string) or len(cstring)
|
||||
// len(string16) or len(cstring16)
|
||||
@(require_results)
|
||||
length :: proc(val: any) -> int {
|
||||
if val == nil { return 0 }
|
||||
@@ -255,10 +309,19 @@ length :: proc(val: any) -> int {
|
||||
return runtime.map_len((^runtime.Raw_Map)(val.data)^)
|
||||
|
||||
case Type_Info_String:
|
||||
if a.is_cstring {
|
||||
return len((^cstring)(val.data)^)
|
||||
} else {
|
||||
return (^runtime.Raw_String)(val.data).len
|
||||
switch a.encoding {
|
||||
case .UTF_8:
|
||||
if a.is_cstring {
|
||||
return len((^cstring)(val.data)^)
|
||||
} else {
|
||||
return (^runtime.Raw_String)(val.data).len
|
||||
}
|
||||
case .UTF_16:
|
||||
if a.is_cstring {
|
||||
return len((^cstring16)(val.data)^)
|
||||
} else {
|
||||
return (^runtime.Raw_String16)(val.data).len
|
||||
}
|
||||
}
|
||||
|
||||
case Type_Info_Simd_Vector:
|
||||
@@ -268,6 +331,12 @@ length :: proc(val: any) -> int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Returns the capacity of the type that represents the `any` value, or returns 0 if not possible
|
||||
// cap(^T) -> cap(T)
|
||||
// cap([N]T) -> N
|
||||
// cap(#simd[N]T) -> N
|
||||
// cap([dynamic]T)
|
||||
// cap(map[K]V)
|
||||
@(require_results)
|
||||
capacity :: proc(val: any) -> int {
|
||||
if val == nil { return 0 }
|
||||
@@ -299,6 +368,7 @@ capacity :: proc(val: any) -> int {
|
||||
}
|
||||
|
||||
|
||||
// Dynamically indexes `any` as an indexable-type if possible. Returns `nil` if not possible
|
||||
@(require_results)
|
||||
index :: proc(val: any, i: int, loc := #caller_location) -> any {
|
||||
if val == nil { return nil }
|
||||
@@ -350,15 +420,25 @@ index :: proc(val: any, i: int, loc := #caller_location) -> any {
|
||||
case Type_Info_String:
|
||||
if a.is_cstring { return nil }
|
||||
|
||||
raw := (^runtime.Raw_String)(val.data)
|
||||
runtime.bounds_check_error_loc(loc, i, raw.len)
|
||||
offset := uintptr(size_of(u8) * i)
|
||||
data := rawptr(uintptr(raw.data) + offset)
|
||||
return any{data, typeid_of(u8)}
|
||||
switch a.encoding {
|
||||
case .UTF_8:
|
||||
raw := (^runtime.Raw_String)(val.data)
|
||||
runtime.bounds_check_error_loc(loc, i, raw.len)
|
||||
offset := uintptr(size_of(u8) * i)
|
||||
data := rawptr(uintptr(raw.data) + offset)
|
||||
return any{data, typeid_of(u8)}
|
||||
case .UTF_16:
|
||||
raw := (^runtime.Raw_String16)(val.data)
|
||||
runtime.bounds_check_error_loc(loc, i, raw.len)
|
||||
offset := uintptr(size_of(u16) * i)
|
||||
data := rawptr(uintptr(raw.data) + offset)
|
||||
return any{data, typeid_of(u16)}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Dereferences `any` if it represents a pointer-based value (`^T -> T`)
|
||||
@(require_results)
|
||||
deref :: proc(val: any) -> any {
|
||||
if val != nil {
|
||||
@@ -375,20 +455,22 @@ deref :: proc(val: any) -> any {
|
||||
|
||||
|
||||
|
||||
// Struct_Tag represents the type of the string of a struct field
|
||||
// `Struct_Tag` represents the type of the `string` of a struct field
|
||||
//
|
||||
// Through convention, tags are the concatenation of optionally space separationed key:"value" pairs.
|
||||
// Through convention, tags are the concatenation of optionally space-separated key:"value" pairs.
|
||||
// Each key is a non-empty string which contains no control characters other than space, quotes, and colon.
|
||||
Struct_Tag :: distinct string
|
||||
|
||||
// `Struct_Field` represents a information of a field of a struct
|
||||
Struct_Field :: struct {
|
||||
name: string,
|
||||
type: ^Type_Info,
|
||||
tag: Struct_Tag,
|
||||
offset: uintptr,
|
||||
offset: uintptr, // in bytes
|
||||
is_using: bool,
|
||||
}
|
||||
|
||||
// Returns a `Struct_Field` containing the information for a struct field of a typeid `T` at index `i`
|
||||
@(require_results)
|
||||
struct_field_at :: proc(T: typeid, i: int) -> (field: Struct_Field) {
|
||||
ti := runtime.type_info_base(type_info_of(T))
|
||||
@@ -404,6 +486,7 @@ struct_field_at :: proc(T: typeid, i: int) -> (field: Struct_Field) {
|
||||
return
|
||||
}
|
||||
|
||||
// Returns a `Struct_Field` containing the information for a struct field by `name` of a typeid `T`
|
||||
@(require_results)
|
||||
struct_field_by_name :: proc(T: typeid, name: string) -> (field: Struct_Field) {
|
||||
ti := runtime.type_info_base(type_info_of(T))
|
||||
@@ -422,6 +505,10 @@ struct_field_by_name :: proc(T: typeid, name: string) -> (field: Struct_Field) {
|
||||
return
|
||||
}
|
||||
|
||||
// Returns an `any` of a struct field specified by name
|
||||
// Example:
|
||||
// v := struct_field_value_by_name(the_struct, "field_name")
|
||||
// nested_value_through_using := struct_field_value_by_name(the_struct, "field_name", allow_using=true)
|
||||
@(require_results)
|
||||
struct_field_value_by_name :: proc(a: any, field: string, allow_using := false) -> any {
|
||||
if a == nil { return nil }
|
||||
@@ -452,6 +539,10 @@ struct_field_value_by_name :: proc(a: any, field: string, allow_using := false)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns an `any` of a struct field specified by a `Struct_Field`
|
||||
// Example:
|
||||
// field := struct_field_value_by_name(the_struct, "field_name")
|
||||
// value_by_field := struct_field_value(the_struct, field)
|
||||
@(require_results)
|
||||
struct_field_value :: proc(a: any, field: Struct_Field) -> any {
|
||||
if a == nil { return nil }
|
||||
@@ -461,6 +552,7 @@ struct_field_value :: proc(a: any, field: Struct_Field) -> any {
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a `[]string` of the names of the struct fields of type `T`
|
||||
@(require_results)
|
||||
struct_field_names :: proc(T: typeid) -> []string {
|
||||
ti := runtime.type_info_base(type_info_of(T))
|
||||
@@ -470,6 +562,7 @@ struct_field_names :: proc(T: typeid) -> []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns a `[]^Type_Info` of the types of the struct fields of type `T`
|
||||
@(require_results)
|
||||
struct_field_types :: proc(T: typeid) -> []^Type_Info {
|
||||
ti := runtime.type_info_base(type_info_of(T))
|
||||
@@ -480,6 +573,7 @@ struct_field_types :: proc(T: typeid) -> []^Type_Info {
|
||||
}
|
||||
|
||||
|
||||
// Returns a `[]Struct_Tag` of the tags of the struct fields of type `T`
|
||||
@(require_results)
|
||||
struct_field_tags :: proc(T: typeid) -> []Struct_Tag {
|
||||
ti := runtime.type_info_base(type_info_of(T))
|
||||
@@ -489,6 +583,7 @@ struct_field_tags :: proc(T: typeid) -> []Struct_Tag {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns a `[]uintptr` of the offsets in bytes of the struct fields of type `T`
|
||||
@(require_results)
|
||||
struct_field_offsets :: proc(T: typeid) -> []uintptr {
|
||||
ti := runtime.type_info_base(type_info_of(T))
|
||||
@@ -498,6 +593,7 @@ struct_field_offsets :: proc(T: typeid) -> []uintptr {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Struct_Field_Count_Method is the count method used by `struct_field_count` in order to find the number of fields
|
||||
Struct_Field_Count_Method :: enum {
|
||||
Top_Level,
|
||||
Using,
|
||||
@@ -556,6 +652,10 @@ struct_field_count :: proc(T: typeid, method := Struct_Field_Count_Method.Top_Le
|
||||
return
|
||||
}
|
||||
|
||||
// Returns the fields of a struct type `T` as an `#soa` slice.
|
||||
// This is useful to iterate over.
|
||||
// Example:
|
||||
// for field, i in reflect.struct_fields_zipped(Foo) { ... }
|
||||
@(require_results)
|
||||
struct_fields_zipped :: proc(T: typeid) -> (fields: #soa[]Struct_Field) {
|
||||
ti := runtime.type_info_base(type_info_of(T))
|
||||
@@ -572,13 +672,26 @@ struct_fields_zipped :: proc(T: typeid) -> (fields: #soa[]Struct_Field) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
// struct_tag_get returns the value associated with a key in the tag string.
|
||||
// If the key is present in the tag, the value (which might be empty) is returned. Otherwise an empty string is returned.
|
||||
// This is just a wrapper around `struct_tag_lookup` with the `ok` value being ignored.
|
||||
//
|
||||
// The convention for struct tags is usually of the form:
|
||||
//
|
||||
// `key:"value" another:"set" and:"whatever"`
|
||||
@(require_results)
|
||||
struct_tag_get :: proc(tag: Struct_Tag, key: string) -> (value: string) {
|
||||
v, _ := struct_tag_lookup(tag, key)
|
||||
return string(v)
|
||||
return v
|
||||
}
|
||||
|
||||
// struct_tag_lookup returns the value associated with a key in the tag string.
|
||||
// If the key is present in the tag, the value (which might be empty) is return. Otherwise an empty string is returned.
|
||||
// The `ok` value returns whether the value was explicit set in the tag string.
|
||||
//
|
||||
// The convention for struct tags is usually of the form:
|
||||
//
|
||||
// `key:"value" another:"set" and:"whatever"`
|
||||
@(require_results)
|
||||
struct_tag_lookup :: proc(tag: Struct_Tag, key: string) -> (value: string, ok: bool) {
|
||||
for t := tag; t != ""; /**/ {
|
||||
@@ -638,6 +751,7 @@ struct_tag_lookup :: proc(tag: Struct_Tag, key: string) -> (value: string, ok: b
|
||||
}
|
||||
|
||||
|
||||
// Returns the string representation of an enum value. It will panic if the value passed is not an enum.
|
||||
@(require_results)
|
||||
enum_string :: proc(a: any) -> string {
|
||||
if a == nil { return "" }
|
||||
@@ -656,7 +770,7 @@ enum_string :: proc(a: any) -> string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Given a enum type and a value name, get the enum value.
|
||||
// Given an enum type and a value name, get the enum value.
|
||||
@(require_results)
|
||||
enum_from_name :: proc($Enum_Type: typeid, name: string) -> (value: Enum_Type, ok: bool) {
|
||||
ti := type_info_base(type_info_of(Enum_Type))
|
||||
@@ -674,6 +788,7 @@ enum_from_name :: proc($Enum_Type: typeid, name: string) -> (value: Enum_Type, o
|
||||
return
|
||||
}
|
||||
|
||||
// enum_from_name_any returns the value of an enum field's name if found, returns `0, false` otherwise.
|
||||
@(require_results)
|
||||
enum_from_name_any :: proc(Enum_Type: typeid, name: string) -> (value: Type_Info_Enum_Value, ok: bool) {
|
||||
ti := runtime.type_info_base(type_info_of(Enum_Type))
|
||||
@@ -690,6 +805,7 @@ enum_from_name_any :: proc(Enum_Type: typeid, name: string) -> (value: Type_Info
|
||||
return
|
||||
}
|
||||
|
||||
// enum_name_from_value returns the name of enum field if a valid name using parametric polymorphism, otherwise returns `"", false`
|
||||
@(require_results)
|
||||
enum_name_from_value :: proc(value: $Enum_Type) -> (name: string, ok: bool) where intrinsics.type_is_enum(Enum_Type) {
|
||||
ti := type_info_base(type_info_of(Enum_Type))
|
||||
@@ -706,6 +822,7 @@ enum_name_from_value :: proc(value: $Enum_Type) -> (name: string, ok: bool) wher
|
||||
return
|
||||
}
|
||||
|
||||
// enum_name_from_value_any returns the name of enum field if a valid name using reflection, otherwise returns `"", false`
|
||||
@(require_results)
|
||||
enum_name_from_value_any :: proc(value: any) -> (name: string, ok: bool) {
|
||||
if value.id == nil {
|
||||
@@ -725,9 +842,7 @@ enum_name_from_value_any :: proc(value: any) -> (name: string, ok: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Returns whether the value given has a defined name in the enum type.
|
||||
*/
|
||||
// Returns whether the value given has a defined name in the enum type.
|
||||
@(require_results)
|
||||
enum_value_has_name :: proc(value: $T) -> bool where intrinsics.type_is_enum(T) {
|
||||
when len(T) == cap(T) {
|
||||
@@ -749,6 +864,7 @@ enum_value_has_name :: proc(value: $T) -> bool where intrinsics.type_is_enum(T)
|
||||
|
||||
|
||||
|
||||
// enum_field_names returns `[]string` of the names of the fields of type `Enum_Type`
|
||||
@(require_results)
|
||||
enum_field_names :: proc(Enum_Type: typeid) -> []string {
|
||||
ti := runtime.type_info_base(type_info_of(Enum_Type))
|
||||
@@ -757,6 +873,7 @@ enum_field_names :: proc(Enum_Type: typeid) -> []string {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// enum_field_values returns `[]Type_Info_Enum_Value` of the values of the fields of type `Enum_Type`
|
||||
@(require_results)
|
||||
enum_field_values :: proc(Enum_Type: typeid) -> []Type_Info_Enum_Value {
|
||||
ti := runtime.type_info_base(type_info_of(Enum_Type))
|
||||
@@ -766,11 +883,16 @@ enum_field_values :: proc(Enum_Type: typeid) -> []Type_Info_Enum_Value {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Represents an `Enum_Field` storing the `name` and `value`
|
||||
Enum_Field :: struct {
|
||||
name: string,
|
||||
value: Type_Info_Enum_Value,
|
||||
}
|
||||
|
||||
// Returns a #soa slice of the enum field information of type `Enum_Type`
|
||||
// This is useful to iterate over.
|
||||
// Example:
|
||||
// for field, i in reflect.enum_fields_zipped(Foo) { ... }
|
||||
@(require_results)
|
||||
enum_fields_zipped :: proc(Enum_Type: typeid) -> (fields: #soa[]Enum_Field) {
|
||||
ti := runtime.type_info_base(type_info_of(Enum_Type))
|
||||
@@ -782,17 +904,20 @@ enum_fields_zipped :: proc(Enum_Type: typeid) -> (fields: #soa[]Enum_Field) {
|
||||
|
||||
|
||||
|
||||
// Returns `^Type_Info` of a any-encoded union type. Panics if a union was not passed.
|
||||
@(require_results)
|
||||
union_variant_type_info :: proc(a: any) -> ^Type_Info {
|
||||
id := union_variant_typeid(a)
|
||||
return type_info_of(id)
|
||||
}
|
||||
|
||||
// Returns whether the `Type_Info_Union` store no tag (called a "pure maybe").
|
||||
@(require_results)
|
||||
type_info_union_is_pure_maybe :: proc(info: runtime.Type_Info_Union) -> bool {
|
||||
return len(info.variants) == 1 && is_pointer_internally(info.variants[0])
|
||||
}
|
||||
|
||||
// UNSAFE: Returns `typeid` of a any-encoded union type. Panics if a union was not passed.
|
||||
@(require_results)
|
||||
union_variant_typeid :: proc(a: any) -> typeid {
|
||||
if a == nil { return nil }
|
||||
@@ -833,6 +958,7 @@ union_variant_typeid :: proc(a: any) -> typeid {
|
||||
panic("expected a union to reflect.union_variant_typeid")
|
||||
}
|
||||
|
||||
// UNSAFE: Returns the underlying tag value of a union. Panics if a union was not passed.
|
||||
@(require_results)
|
||||
get_union_variant_raw_tag :: proc(a: any) -> i64 {
|
||||
if a == nil { return -1 }
|
||||
@@ -864,6 +990,7 @@ get_union_variant_raw_tag :: proc(a: any) -> i64 {
|
||||
panic("expected a union to reflect.get_union_variant_raw_tag")
|
||||
}
|
||||
|
||||
// Returns the underlying variant value of a union. Panics if a union was not passed.
|
||||
@(require_results)
|
||||
get_union_variant :: proc(a: any) -> any {
|
||||
if a == nil {
|
||||
@@ -876,6 +1003,14 @@ get_union_variant :: proc(a: any) -> any {
|
||||
return any{a.data, id}
|
||||
}
|
||||
|
||||
// Converts a pointer to a union, to a union containing the pointers to the variant types, and stores a pointer of the variant value in the new union
|
||||
//
|
||||
// Example:
|
||||
// val: union{i32, f32, string}
|
||||
// val = "123"
|
||||
// ptr: union{^i32, ^f32, ^string} = get_union_as_ptr_variants(&val)
|
||||
// sp := ptr.(^string)
|
||||
// assert(sp^ == "123")
|
||||
@(require_results)
|
||||
get_union_as_ptr_variants :: proc(val: ^$T) -> (res: intrinsics.type_convert_variants_to_pointers(T)) where intrinsics.type_is_union(T) {
|
||||
ptr := rawptr(val)
|
||||
@@ -886,7 +1021,7 @@ get_union_as_ptr_variants :: proc(val: ^$T) -> (res: intrinsics.type_convert_var
|
||||
}
|
||||
|
||||
|
||||
|
||||
// UNSAFE: Manually set the tag value of a union using an integer. Panics if a union was not passed.
|
||||
set_union_variant_raw_tag :: proc(a: any, tag: i64) {
|
||||
if a == nil { return }
|
||||
|
||||
@@ -917,6 +1052,7 @@ set_union_variant_raw_tag :: proc(a: any, tag: i64) {
|
||||
panic("expected a union to reflect.set_union_variant_raw_tag")
|
||||
}
|
||||
|
||||
// UNSAFE: Manually set the tag value of a union using a `typeid`. Panics if a union was not passed.
|
||||
set_union_variant_typeid :: proc(a: any, id: typeid) {
|
||||
if a == nil { return }
|
||||
|
||||
@@ -947,6 +1083,7 @@ set_union_variant_typeid :: proc(a: any, id: typeid) {
|
||||
panic("expected a union to reflect.set_union_variant_typeid")
|
||||
}
|
||||
|
||||
// UNSAFE: Manually set the tag value of a union using a `^Type_Info`. Panics if a union was not passed.
|
||||
set_union_variant_type_info :: proc(a: any, tag_ti: ^Type_Info) {
|
||||
if a == nil { return }
|
||||
|
||||
@@ -977,6 +1114,7 @@ set_union_variant_type_info :: proc(a: any, tag_ti: ^Type_Info) {
|
||||
panic("expected a union to reflect.set_union_variant_type_info")
|
||||
}
|
||||
|
||||
// UNSAFE: Manually set the variant value of a union using an `any`. Panics if a union was not passed.
|
||||
set_union_value :: proc(dst: any, value: any) -> bool {
|
||||
if dst == nil { return false }
|
||||
|
||||
@@ -1015,6 +1153,7 @@ set_union_value :: proc(dst: any, value: any) -> bool {
|
||||
panic("expected a union to reflect.set_union_variant_typeid")
|
||||
}
|
||||
|
||||
// UNSAFE: Checks to see if the data stored is a `bit_set` and is big endian. Panics if a `bit_set` was not passed.
|
||||
@(require_results)
|
||||
bit_set_is_big_endian :: proc(value: any, loc := #caller_location) -> bool {
|
||||
if value == nil { return ODIN_ENDIAN == .Big }
|
||||
@@ -1046,6 +1185,10 @@ Bit_Field :: struct {
|
||||
tag: Struct_Tag,
|
||||
}
|
||||
|
||||
// Returns the fields of a `bit_field` type `T` as an `#soa` slice.
|
||||
// This is useful to iterate over.
|
||||
// Example:
|
||||
// for field, i in reflect.bit_fields_zipped(Foo_Bit_Field) { ... }
|
||||
@(require_results)
|
||||
bit_fields_zipped :: proc(T: typeid) -> (fields: #soa[]Bit_Field) {
|
||||
ti := runtime.type_info_base(type_info_of(T))
|
||||
@@ -1061,6 +1204,7 @@ bit_fields_zipped :: proc(T: typeid) -> (fields: #soa[]Bit_Field) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// bit_field_names returns a `[]string` of the field names of a `bit_field` type `T`
|
||||
@(require_results)
|
||||
bit_field_names :: proc(T: typeid) -> []string {
|
||||
ti := runtime.type_info_base(type_info_of(T))
|
||||
@@ -1070,6 +1214,7 @@ bit_field_names :: proc(T: typeid) -> []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
// bit_field_types returns a `[]^Type_Info` of the field representation types of a `bit_field` type `T`, not the backing integer-bit-width types
|
||||
@(require_results)
|
||||
bit_field_types :: proc(T: typeid) -> []^Type_Info {
|
||||
ti := runtime.type_info_base(type_info_of(T))
|
||||
@@ -1079,6 +1224,7 @@ bit_field_types :: proc(T: typeid) -> []^Type_Info {
|
||||
return nil
|
||||
}
|
||||
|
||||
// bit_field_types returns a `[]uintptr` of the field bit-width-sizes of a `bit_field` type `T`
|
||||
@(require_results)
|
||||
bit_field_sizes :: proc(T: typeid) -> []uintptr {
|
||||
ti := runtime.type_info_base(type_info_of(T))
|
||||
@@ -1088,6 +1234,7 @@ bit_field_sizes :: proc(T: typeid) -> []uintptr {
|
||||
return nil
|
||||
}
|
||||
|
||||
// bit_field_types returns a `[]uintptr` of the field offsets in bits of a `bit_field` type `T`
|
||||
@(require_results)
|
||||
bit_field_offsets :: proc(T: typeid) -> []uintptr {
|
||||
ti := runtime.type_info_base(type_info_of(T))
|
||||
@@ -1097,6 +1244,7 @@ bit_field_offsets :: proc(T: typeid) -> []uintptr {
|
||||
return nil
|
||||
}
|
||||
|
||||
// bit_field_types returns a `[]Struct_Tag` of the field tags of a `bit_field` type `T`
|
||||
@(require_results)
|
||||
bit_field_tags :: proc(T: typeid) -> []Struct_Tag {
|
||||
ti := runtime.type_info_base(type_info_of(T))
|
||||
@@ -1106,6 +1254,7 @@ bit_field_tags :: proc(T: typeid) -> []Struct_Tag {
|
||||
return nil
|
||||
}
|
||||
|
||||
// as_bool attempts to convert an `any` to a `bool`.
|
||||
@(require_results)
|
||||
as_bool :: proc(a: any) -> (value: bool, valid: bool) {
|
||||
if a == nil { return }
|
||||
@@ -1129,6 +1278,7 @@ as_bool :: proc(a: any) -> (value: bool, valid: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
// as_int attempts to convert an `any` to a `int`.
|
||||
@(require_results)
|
||||
as_int :: proc(a: any) -> (value: int, valid: bool) {
|
||||
v: i64
|
||||
@@ -1137,6 +1287,7 @@ as_int :: proc(a: any) -> (value: int, valid: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
// as_uint attempts to convert an `any` to a `uint`.
|
||||
@(require_results)
|
||||
as_uint :: proc(a: any) -> (value: uint, valid: bool) {
|
||||
v: u64
|
||||
@@ -1145,6 +1296,7 @@ as_uint :: proc(a: any) -> (value: uint, valid: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
// as_i64 attempts to convert an `any` to a `i64`.
|
||||
@(require_results)
|
||||
as_i64 :: proc(a: any) -> (value: i64, valid: bool) {
|
||||
if a == nil { return }
|
||||
@@ -1253,6 +1405,7 @@ as_i64 :: proc(a: any) -> (value: i64, valid: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
// as_u64 attempts to convert an `any` to a `u64`.
|
||||
@(require_results)
|
||||
as_u64 :: proc(a: any) -> (value: u64, valid: bool) {
|
||||
if a == nil { return }
|
||||
@@ -1363,6 +1516,7 @@ as_u64 :: proc(a: any) -> (value: u64, valid: bool) {
|
||||
}
|
||||
|
||||
|
||||
// as_f64 attempts to convert an `any` to a `f64`.
|
||||
@(require_results)
|
||||
as_f64 :: proc(a: any) -> (value: f64, valid: bool) {
|
||||
if a == nil { return }
|
||||
@@ -1480,6 +1634,7 @@ as_f64 :: proc(a: any) -> (value: f64, valid: bool) {
|
||||
}
|
||||
|
||||
|
||||
// as_string attempts to convert an `any` to a `string`.
|
||||
@(require_results)
|
||||
as_string :: proc(a: any) -> (value: string, valid: bool) {
|
||||
if a == nil { return }
|
||||
@@ -1500,6 +1655,27 @@ as_string :: proc(a: any) -> (value: string, valid: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
// as_string16 attempts to convert an `any` to a `string16`.
|
||||
@(require_results)
|
||||
as_string16 :: proc(a: any) -> (value: string16, valid: bool) {
|
||||
if a == nil { return }
|
||||
a := a
|
||||
ti := runtime.type_info_core(type_info_of(a.id))
|
||||
a.id = ti.id
|
||||
|
||||
#partial switch info in ti.variant {
|
||||
case Type_Info_String:
|
||||
valid = true
|
||||
switch v in a {
|
||||
case string16: value = v
|
||||
case cstring16: value = string16(v)
|
||||
case: valid = false
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
relative_pointer_to_absolute_raw :: proc(data: rawptr, base_integer_id: typeid) -> rawptr {
|
||||
_handle :: proc(ptr: ^$T) -> rawptr where intrinsics.type_is_integer(T) {
|
||||
@@ -1543,6 +1719,8 @@ relative_pointer_to_absolute_raw :: proc(data: rawptr, base_integer_id: typeid)
|
||||
|
||||
|
||||
|
||||
// as_pointer attempts to convert an `any` to a `rawptr`.
|
||||
// This only works for `^T`, `[^]T`, `cstring`, `cstring16` based types
|
||||
@(require_results)
|
||||
as_pointer :: proc(a: any) -> (value: rawptr, valid: bool) {
|
||||
if a == nil { return }
|
||||
@@ -1551,14 +1729,15 @@ as_pointer :: proc(a: any) -> (value: rawptr, valid: bool) {
|
||||
a.id = ti.id
|
||||
|
||||
#partial switch info in ti.variant {
|
||||
case Type_Info_Pointer:
|
||||
case Type_Info_Pointer, Type_Info_Multi_Pointer:
|
||||
valid = true
|
||||
value = (^rawptr)(a.data)^
|
||||
|
||||
case Type_Info_String:
|
||||
valid = true
|
||||
switch v in a {
|
||||
case cstring: value = rawptr(v)
|
||||
case cstring: value = rawptr(v)
|
||||
case cstring16: value = rawptr(v)
|
||||
case: valid = false
|
||||
}
|
||||
}
|
||||
@@ -1567,6 +1746,7 @@ as_pointer :: proc(a: any) -> (value: rawptr, valid: bool) {
|
||||
}
|
||||
|
||||
|
||||
// Returns the equivalent of doing `raw_data(v)` where `v` is a non-any value
|
||||
@(require_results)
|
||||
as_raw_data :: proc(a: any) -> (value: rawptr, valid: bool) {
|
||||
if a == nil { return }
|
||||
@@ -1578,8 +1758,10 @@ as_raw_data :: proc(a: any) -> (value: rawptr, valid: bool) {
|
||||
case Type_Info_String:
|
||||
valid = true
|
||||
switch v in a {
|
||||
case string: value = raw_data(v)
|
||||
case cstring: value = rawptr(v) // just in case
|
||||
case string: value = raw_data(v)
|
||||
case cstring: value = rawptr(v) // just in case
|
||||
case string16: value = raw_data(v)
|
||||
case cstring16: value = rawptr(v) // just in case
|
||||
case: valid = false
|
||||
}
|
||||
|
||||
@@ -1604,10 +1786,13 @@ ne :: not_equal
|
||||
|
||||
DEFAULT_EQUAL_MAX_RECURSION_LEVEL :: 32
|
||||
|
||||
// Checks to see if two `any` values are not semantically equivalent
|
||||
@(require_results)
|
||||
not_equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_level := 0) -> bool {
|
||||
return !equal(a, b, including_indirect_array_recursion, recursion_level)
|
||||
}
|
||||
|
||||
// Checks to see if two `any` values are semantically equivalent
|
||||
@(require_results)
|
||||
equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_level := 0) -> bool {
|
||||
if a == nil && b == nil {
|
||||
@@ -1702,14 +1887,27 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
|
||||
return runtime.memory_compare(a.data, b.data, t.size) == 0
|
||||
|
||||
case Type_Info_String:
|
||||
if v.is_cstring {
|
||||
x := string((^cstring)(a.data)^)
|
||||
y := string((^cstring)(b.data)^)
|
||||
return x == y
|
||||
} else {
|
||||
x := (^string)(a.data)^
|
||||
y := (^string)(b.data)^
|
||||
return x == y
|
||||
switch v.encoding {
|
||||
case .UTF_8:
|
||||
if v.is_cstring {
|
||||
x := string((^cstring)(a.data)^)
|
||||
y := string((^cstring)(b.data)^)
|
||||
return x == y
|
||||
} else {
|
||||
x := (^string)(a.data)^
|
||||
y := (^string)(b.data)^
|
||||
return x == y
|
||||
}
|
||||
case .UTF_16:
|
||||
if v.is_cstring {
|
||||
x := string16((^cstring16)(a.data)^)
|
||||
y := string16((^cstring16)(b.data)^)
|
||||
return x == y
|
||||
} else {
|
||||
x := (^string16)(a.data)^
|
||||
y := (^string16)(b.data)^
|
||||
return x == y
|
||||
}
|
||||
}
|
||||
return true
|
||||
case Type_Info_Array:
|
||||
|
||||
@@ -3,6 +3,10 @@ package reflect
|
||||
import "core:io"
|
||||
import "core:strings"
|
||||
|
||||
|
||||
// Returns true when the `^Type_Info`s are semantically equivalent types
|
||||
// Note: The pointers being identical should be enough to check but this is done to make sure in certain cases where it is non-trivial
|
||||
// and each value wants to be checked directly.
|
||||
@(require_results)
|
||||
are_types_identical :: proc(a, b: ^Type_Info) -> bool {
|
||||
if a == b {
|
||||
@@ -187,6 +191,7 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Returns true if the base-type is a signed integer or just a float, false otherwise.
|
||||
@(require_results)
|
||||
is_signed :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
@@ -196,6 +201,7 @@ is_signed :: proc(info: ^Type_Info) -> bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
// Returns true if the base-type is an usigned integer, false otherwise.
|
||||
@(require_results)
|
||||
is_unsigned :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
@@ -206,6 +212,7 @@ is_unsigned :: proc(info: ^Type_Info) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Returns true when it is a 1-byte wide integer type, false otherwise.
|
||||
@(require_results)
|
||||
is_byte :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
@@ -216,78 +223,108 @@ is_byte :: proc(info: ^Type_Info) -> bool {
|
||||
}
|
||||
|
||||
|
||||
// Returns true the base-type is an integer of any kind, false otherwise.
|
||||
@(require_results)
|
||||
is_integer :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Integer)
|
||||
return ok
|
||||
}
|
||||
// Returns true the base-type is a rune, false otherwise.
|
||||
@(require_results)
|
||||
is_rune :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Rune)
|
||||
return ok
|
||||
}
|
||||
// Returns true the base-type is a float of any kind, false otherwise.
|
||||
@(require_results)
|
||||
is_float :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Float)
|
||||
return ok
|
||||
}
|
||||
// Returns true the base-type is a complex-type of any kind, false otherwise.
|
||||
@(require_results)
|
||||
is_complex :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Complex)
|
||||
return ok
|
||||
}
|
||||
// Returns true the base-type is a quaternions any kind, false otherwise.
|
||||
@(require_results)
|
||||
is_quaternion :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Quaternion)
|
||||
return ok
|
||||
}
|
||||
// Returns true the base-type is an `any`, false otherwise.
|
||||
@(require_results)
|
||||
is_any :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Any)
|
||||
return ok
|
||||
}
|
||||
|
||||
// Returns true the base-type is a string of any kind (string, cstring, string16, cstring16), false otherwise.
|
||||
@(require_results)
|
||||
is_string :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_String)
|
||||
return ok
|
||||
}
|
||||
// Returns true the base-type is a cstring of any kind (cstring, cstring16), false otherwise.
|
||||
@(require_results)
|
||||
is_cstring :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
v, ok := type_info_base(info).variant.(Type_Info_String)
|
||||
return ok && v.is_cstring
|
||||
}
|
||||
|
||||
// Returns true the base-type is a string of any kind (string16, cstring16), false otherwise.
|
||||
@(require_results)
|
||||
is_string16 :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
v, ok := type_info_base(info).variant.(Type_Info_String)
|
||||
return ok && v.encoding == .UTF_16
|
||||
}
|
||||
// Returns true the base-type is a cstring of any kind (cstring16), false otherwise.
|
||||
@(require_results)
|
||||
is_cstring16 :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
v, ok := type_info_base(info).variant.(Type_Info_String)
|
||||
return ok && v.is_cstring && v.encoding == .UTF_16
|
||||
}
|
||||
|
||||
// Returns true the base-type is a boolean of any kind, false otherwise.
|
||||
@(require_results)
|
||||
is_boolean :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Boolean)
|
||||
return ok
|
||||
}
|
||||
// Returns true the base-type is a pointer-type of any kind (^T or rawptr), false otherwise.
|
||||
@(require_results)
|
||||
is_pointer :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Pointer)
|
||||
return ok
|
||||
}
|
||||
// Returns true the base-type is a pointer-type of any kind ([^]T), false otherwise.
|
||||
@(require_results)
|
||||
is_multi_pointer :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Multi_Pointer)
|
||||
return ok
|
||||
}
|
||||
// Returns true the base-type is a pointer-type of any kind (#soa^T), false otherwise.
|
||||
@(require_results)
|
||||
is_soa_pointer :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Soa_Pointer)
|
||||
return ok
|
||||
}
|
||||
// Returns true when the type is a pointer-like type, false otherwise.
|
||||
@(require_results)
|
||||
is_pointer_internally :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
@@ -300,78 +337,91 @@ is_pointer_internally :: proc(info: ^Type_Info) -> bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
// Returns true when the type is a procedure type, false otherwise.
|
||||
@(require_results)
|
||||
is_procedure :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Procedure)
|
||||
return ok
|
||||
}
|
||||
// Returns true when the type is a fixed-array type ([N]T), false otherwise.
|
||||
@(require_results)
|
||||
is_array :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Array)
|
||||
return ok
|
||||
}
|
||||
// Returns true when the type is an enumerated-array type ([Enum]T), false otherwise.
|
||||
@(require_results)
|
||||
is_enumerated_array :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Enumerated_Array)
|
||||
return ok
|
||||
}
|
||||
// Returns true when the type is a dynamic-array type ([dynamic]T), false otherwise.
|
||||
@(require_results)
|
||||
is_dynamic_array :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Dynamic_Array)
|
||||
return ok
|
||||
}
|
||||
// Returns true when the type is a map type (map[K]V), false otherwise.
|
||||
@(require_results)
|
||||
is_dynamic_map :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Map)
|
||||
return ok
|
||||
}
|
||||
// Returns true when the type is a bit_set type, false otherwise.
|
||||
@(require_results)
|
||||
is_bit_set :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Bit_Set)
|
||||
return ok
|
||||
}
|
||||
// Returns true when the type is a slice type ([]T), false otherwise.
|
||||
@(require_results)
|
||||
is_slice :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Slice)
|
||||
return ok
|
||||
}
|
||||
// Returns true when the type represents a set of parameters for a procedure (inputs or outputs), false otherwise.
|
||||
@(require_results)
|
||||
is_parameters :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Parameters)
|
||||
return ok
|
||||
}
|
||||
// Returns true when the type is a struct type, `#raw_union` will be false. All other types will be false otherwise.
|
||||
@(require_results)
|
||||
is_struct :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
s, ok := type_info_base(info).variant.(Type_Info_Struct)
|
||||
return ok && .raw_union not_in s.flags
|
||||
}
|
||||
// Returns true when the type is a struct type with `#raw_union` applied, when `#raw_union` is not applied, the value will be false. All other types will be false otherwise.
|
||||
@(require_results)
|
||||
is_raw_union :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
s, ok := type_info_base(info).variant.(Type_Info_Struct)
|
||||
return ok && .raw_union in s.flags
|
||||
}
|
||||
// Returns true when the type is a union type (not `#raw_union`), false otherwise.
|
||||
@(require_results)
|
||||
is_union :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Union)
|
||||
return ok
|
||||
}
|
||||
// Returns true when the type is an enum type, false otherwise.
|
||||
@(require_results)
|
||||
is_enum :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Enum)
|
||||
return ok
|
||||
}
|
||||
// Returns true when the type is a #simd-array type (#simd[N]T), false otherwise.
|
||||
@(require_results)
|
||||
is_simd_vector :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
@@ -380,6 +430,9 @@ is_simd_vector :: proc(info: ^Type_Info) -> bool {
|
||||
}
|
||||
|
||||
|
||||
// Returns true when the core-type is represented with a platform-native endian type, and returns false otherwise.
|
||||
// This will also return false when the type is not an integer, pointer, or bit_set.
|
||||
// If the type is the same as the platform-native endian type (e.g. `u32le` on a little-endian system), this will return false.
|
||||
@(require_results)
|
||||
is_endian_platform :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false}
|
||||
@@ -399,6 +452,9 @@ is_endian_platform :: proc(info: ^Type_Info) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Returns true when the core-type is represented with a platform-native endian type or the same endianness as the system.
|
||||
// This will also return false when the type is not an integer, pointer, or bit_set.
|
||||
// If the type is the same as the platform-native endian type (e.g. `u32le` on a little-endian system), this will return true.
|
||||
@(require_results)
|
||||
is_endian_little :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false}
|
||||
@@ -421,6 +477,9 @@ is_endian_little :: proc(info: ^Type_Info) -> bool {
|
||||
return ODIN_ENDIAN == .Little
|
||||
}
|
||||
|
||||
// Returns true when the core-type is represented with a platform-native endian type or the same endianness as the system.
|
||||
// This will also return false when the type is not an integer, pointer, or bit_set.
|
||||
// If the type is the same as the platform-native endian type (e.g. `u32be` on a big-endian system), this will return true.
|
||||
@(require_results)
|
||||
is_endian_big :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false}
|
||||
@@ -446,27 +505,33 @@ is_endian_big :: proc(info: ^Type_Info) -> bool {
|
||||
|
||||
|
||||
|
||||
// Writes a typeid in standard (non-canonical) form to a `strings.Builder`
|
||||
write_typeid_builder :: proc(buf: ^strings.Builder, id: typeid, n_written: ^int = nil) -> (n: int, err: io.Error) {
|
||||
return write_type_writer(strings.to_writer(buf), type_info_of(id))
|
||||
}
|
||||
// Writes a typeid in standard (non-canonical) form to an `io.Writer`
|
||||
write_typeid_writer :: proc(writer: io.Writer, id: typeid, n_written: ^int = nil) -> (n: int, err: io.Error) {
|
||||
return write_type_writer(writer, type_info_of(id), n_written)
|
||||
}
|
||||
|
||||
// Writes a typeid in standard (non-canonical) form
|
||||
write_typeid :: proc{
|
||||
write_typeid_builder,
|
||||
write_typeid_writer,
|
||||
}
|
||||
|
||||
// Writes a `^Type_Info` in standard (non-canonical) form
|
||||
write_type :: proc{
|
||||
write_type_builder,
|
||||
write_type_writer,
|
||||
}
|
||||
|
||||
// Writes a `^Type_Info` in standard (non-canonical) form to a `strings.Builder`
|
||||
write_type_builder :: proc(buf: ^strings.Builder, ti: ^Type_Info) -> int {
|
||||
n, _ := write_type_writer(strings.to_writer(buf), ti)
|
||||
return n
|
||||
}
|
||||
// Writes a `^Type_Info` in standard (non-canonical) form to an `io.Writer`
|
||||
write_type_writer :: #force_no_inline proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) -> (n: int, err: io.Error) {
|
||||
defer if n_written != nil {
|
||||
n_written^ += n
|
||||
|
||||
@@ -36,3 +36,27 @@ append_u128 :: proc(buf: []byte, u: u128, base: int) -> string {
|
||||
append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string {
|
||||
return write_float(buf, f, fmt, prec, bit_size)
|
||||
}
|
||||
|
||||
// 2025-10-03 Deprecated C short names and implementations
|
||||
|
||||
@(deprecated="Use strconv.write_int() instead")
|
||||
itoa :: proc(buf: []byte, i: int) -> string {
|
||||
return write_int(buf, i64(i), 10)
|
||||
}
|
||||
|
||||
@(deprecated="Use strconv.parse_int() instead")
|
||||
atoi :: proc(s: string) -> int {
|
||||
v, _ := parse_int(s)
|
||||
return v
|
||||
}
|
||||
|
||||
@(deprecated="Use strconv.parse_f64() instead")
|
||||
atof :: proc(s: string) -> f64 {
|
||||
v, _ := parse_f64(s)
|
||||
return v
|
||||
}
|
||||
|
||||
@(deprecated="Use strconv.write_float() instead")
|
||||
ftoa :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string {
|
||||
return string(generic_ftoa(buf, f, fmt, prec, bit_size))
|
||||
}
|
||||
@@ -1547,85 +1547,8 @@ write_u128 :: proc(buf: []byte, u: u128, base: int) -> string {
|
||||
}
|
||||
|
||||
/*
|
||||
Converts an integer value to a string and stores it in the given buffer
|
||||
`ftoa` C name deprecated, use `write_float` instead (same procedure)
|
||||
|
||||
**Inputs**
|
||||
- buf: The buffer to store the resulting string
|
||||
- i: The integer value to be converted
|
||||
|
||||
Example:
|
||||
|
||||
import "core:fmt"
|
||||
import "core:strconv"
|
||||
itoa_example :: proc() {
|
||||
buf: [4]byte
|
||||
result := strconv.itoa(buf[:], 42)
|
||||
fmt.println(result, buf) // "42"
|
||||
}
|
||||
|
||||
Output:
|
||||
|
||||
42 [52, 50, 0, 0]
|
||||
|
||||
**Returns**
|
||||
- The resulting string after converting the integer value
|
||||
*/
|
||||
itoa :: proc(buf: []byte, i: int) -> string {
|
||||
return write_int(buf, i64(i), 10)
|
||||
}
|
||||
/*
|
||||
Converts a string to an integer value
|
||||
|
||||
**Inputs**
|
||||
- s: The string to be converted
|
||||
|
||||
Example:
|
||||
|
||||
import "core:fmt"
|
||||
import "core:strconv"
|
||||
atoi_example :: proc() {
|
||||
fmt.println(strconv.atoi("42"))
|
||||
}
|
||||
|
||||
Output:
|
||||
|
||||
42
|
||||
|
||||
**Returns**
|
||||
- The resulting integer value
|
||||
*/
|
||||
atoi :: proc(s: string) -> int {
|
||||
v, _ := parse_int(s)
|
||||
return v
|
||||
}
|
||||
/*
|
||||
Converts a string to a float64 value
|
||||
|
||||
**Inputs**
|
||||
- s: The string to be converted
|
||||
|
||||
Example:
|
||||
|
||||
import "core:fmt"
|
||||
import "core:strconv"
|
||||
atof_example :: proc() {
|
||||
fmt.printfln("%.3f", strconv.atof("3.14"))
|
||||
}
|
||||
|
||||
Output:
|
||||
|
||||
3.140
|
||||
|
||||
**Returns**
|
||||
- The resulting float64 value after converting the string
|
||||
*/
|
||||
atof :: proc(s: string) -> f64 {
|
||||
v, _ := parse_f64(s)
|
||||
return v
|
||||
}
|
||||
// Alias to `write_float`
|
||||
ftoa :: write_float
|
||||
/*
|
||||
Writes a float64 value as a string to the given buffer with the specified format and precision
|
||||
|
||||
**Inputs**
|
||||
|
||||
@@ -436,8 +436,13 @@ equal_fold :: proc(u, v: string) -> (res: bool) {
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO(bill): Unicode folding
|
||||
|
||||
r := unicode.simple_fold(sr)
|
||||
for r != sr && r < tr {
|
||||
r = unicode.simple_fold(sr)
|
||||
}
|
||||
if r == tr {
|
||||
continue loop
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -1937,7 +1937,7 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory) {
|
||||
if (buf_len > 0 && buf_ptr) {
|
||||
let n = Math.min(buf_len, str.length);
|
||||
str = str.substring(0, n);
|
||||
this.mem.loadBytes(buf_ptr, buf_len).set(new TextEncoder().encode(str))
|
||||
wasmMemoryInterface.loadBytes(buf_ptr, buf_len).set(new TextEncoder().encode(str))
|
||||
return n;
|
||||
}
|
||||
}
|
||||
@@ -2001,7 +2001,7 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory) {
|
||||
if (buf_len > 0 && buf_ptr) {
|
||||
let n = Math.min(buf_len, str.length);
|
||||
str = str.substring(0, n);
|
||||
this.mem.loadBytes(buf_ptr, buf_len).set(new TextEncoder().encode(str))
|
||||
wasmMemoryInterface.loadBytes(buf_ptr, buf_len).set(new TextEncoder().encode(str))
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
+115
-119
@@ -9,11 +9,12 @@ MAX_CAPTURES :: 32
|
||||
|
||||
Capture :: struct {
|
||||
init: int,
|
||||
len: int,
|
||||
len: int,
|
||||
}
|
||||
|
||||
Match :: struct {
|
||||
byte_start, byte_end: int,
|
||||
byte_start: int,
|
||||
byte_end: int,
|
||||
}
|
||||
|
||||
Error :: enum {
|
||||
@@ -27,18 +28,19 @@ Error :: enum {
|
||||
Match_Invalid,
|
||||
}
|
||||
|
||||
L_ESC :: '%'
|
||||
CAP_POSITION :: -2
|
||||
L_ESC :: '%'
|
||||
CAP_POSITION :: -2
|
||||
CAP_UNFINISHED :: -1
|
||||
INVALID :: -1
|
||||
INVALID :: -1
|
||||
|
||||
Match_State :: struct {
|
||||
src: string,
|
||||
src: string,
|
||||
pattern: string,
|
||||
level: int,
|
||||
level: int,
|
||||
capture: [MAX_CAPTURES]Capture,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
match_class :: proc(c: rune, cl: rune) -> (res: bool) {
|
||||
switch unicode.to_lower(cl) {
|
||||
case 'a': res = is_alpha(c)
|
||||
@@ -65,19 +67,23 @@ is_punct :: unicode.is_punct
|
||||
is_space :: unicode.is_space
|
||||
is_cntrl :: unicode.is_control
|
||||
|
||||
@(require_results)
|
||||
is_alnum :: proc(c: rune) -> bool {
|
||||
return unicode.is_alpha(c) || unicode.is_digit(c)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_graph :: proc(c: rune) -> bool {
|
||||
return (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || unicode.is_digit(c)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_xdigit :: proc(c: rune) -> bool {
|
||||
return (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || unicode.is_digit(c)
|
||||
}
|
||||
|
||||
// find the first utf8 charater and its size, return an error if the character is an error
|
||||
@(require_results)
|
||||
utf8_peek :: proc(bytes: string) -> (c: rune, size: int, err: Error) {
|
||||
c, size = utf8.decode_rune_in_string(bytes)
|
||||
|
||||
@@ -90,6 +96,7 @@ utf8_peek :: proc(bytes: string) -> (c: rune, size: int, err: Error) {
|
||||
|
||||
// find the first utf8 charater and its size and advance the index
|
||||
// return an error if the character is an error
|
||||
@(require_results)
|
||||
utf8_advance :: proc(bytes: string, index: ^int) -> (c: rune, err: Error) {
|
||||
size: int
|
||||
c, size = utf8.decode_rune_in_string(bytes[index^:])
|
||||
@@ -103,10 +110,12 @@ utf8_advance :: proc(bytes: string, index: ^int) -> (c: rune, err: Error) {
|
||||
}
|
||||
|
||||
// continuation byte?
|
||||
@(require_results)
|
||||
is_cont :: proc(b: byte) -> bool {
|
||||
return b & 0xc0 == 0x80
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
utf8_prev :: proc(bytes: string, a, b: int) -> int {
|
||||
b := b
|
||||
|
||||
@@ -117,6 +126,7 @@ utf8_prev :: proc(bytes: string, a, b: int) -> int {
|
||||
return a < b ? b - 1 : a
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
utf8_next :: proc(bytes: string, a: int) -> int {
|
||||
a := a
|
||||
b := len(bytes)
|
||||
@@ -128,6 +138,7 @@ utf8_next :: proc(bytes: string, a: int) -> int {
|
||||
return a < b ? a + 1 : b
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
check_capture :: proc(ms: ^Match_State, l: rune) -> (int, Error) {
|
||||
l := int(l - '1')
|
||||
|
||||
@@ -138,6 +149,7 @@ check_capture :: proc(ms: ^Match_State, l: rune) -> (int, Error) {
|
||||
return l, .OK
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
capture_to_close :: proc(ms: ^Match_State) -> (int, Error) {
|
||||
level := ms.level - 1
|
||||
|
||||
@@ -152,6 +164,7 @@ capture_to_close :: proc(ms: ^Match_State) -> (int, Error) {
|
||||
return 0, .Invalid_Pattern_Capture
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
class_end :: proc(ms: ^Match_State, p: int) -> (step: int, err: Error) {
|
||||
step = p
|
||||
ch := utf8_advance(ms.pattern, &step) or_return
|
||||
@@ -163,7 +176,7 @@ class_end :: proc(ms: ^Match_State, p: int) -> (step: int, err: Error) {
|
||||
return
|
||||
}
|
||||
|
||||
utf8_advance(ms.pattern, &step) or_return
|
||||
_ = utf8_advance(ms.pattern, &step) or_return
|
||||
|
||||
case '[':
|
||||
// fine with step by 1
|
||||
@@ -198,6 +211,7 @@ class_end :: proc(ms: ^Match_State, p: int) -> (step: int, err: Error) {
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
match_bracket_class :: proc(ms: ^Match_State, c: rune, p, ec: int) -> (sig: bool, err: Error) {
|
||||
sig = true
|
||||
p := p
|
||||
@@ -240,6 +254,7 @@ match_bracket_class :: proc(ms: ^Match_State, c: rune, p, ec: int) -> (sig: bool
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
single_match :: proc(ms: ^Match_State, s, p, ep: int) -> (matched: bool, schar_size: int, err: Error) {
|
||||
if s >= len(ms.src) {
|
||||
return
|
||||
@@ -254,13 +269,16 @@ single_match :: proc(ms: ^Match_State, s, p, ep: int) -> (matched: bool, schar_s
|
||||
case L_ESC:
|
||||
pchar_next, _ := utf8_peek(ms.pattern[p + psize:]) or_return
|
||||
matched = match_class(schar, pchar_next)
|
||||
case '[': matched = match_bracket_class(ms, schar, p, ep - 1) or_return
|
||||
case: matched = schar == pchar
|
||||
case '[':
|
||||
matched = match_bracket_class(ms, schar, p, ep - 1) or_return
|
||||
case:
|
||||
matched = schar == pchar
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
match_balance :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error) {
|
||||
if p >= len(ms.pattern) - 1 {
|
||||
return INVALID, .Invalid_Pattern_Capture
|
||||
@@ -300,13 +318,13 @@ match_balance :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error)
|
||||
return INVALID, .OK
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
max_expand :: proc(ms: ^Match_State, s, p, ep: int) -> (res: int, err: Error) {
|
||||
m := s
|
||||
|
||||
// count up matches
|
||||
for {
|
||||
matched, size := single_match(ms, m, p, ep) or_return
|
||||
|
||||
if !matched {
|
||||
break
|
||||
}
|
||||
@@ -316,7 +334,6 @@ max_expand :: proc(ms: ^Match_State, s, p, ep: int) -> (res: int, err: Error) {
|
||||
|
||||
for s <= m {
|
||||
result := match(ms, m, ep + 1) or_return
|
||||
|
||||
if result != INVALID {
|
||||
return result, .OK
|
||||
}
|
||||
@@ -331,6 +348,7 @@ max_expand :: proc(ms: ^Match_State, s, p, ep: int) -> (res: int, err: Error) {
|
||||
return INVALID, .OK
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
min_expand :: proc(ms: ^Match_State, s, p, ep: int) -> (res: int, err: Error) {
|
||||
s := s
|
||||
|
||||
@@ -339,19 +357,19 @@ min_expand :: proc(ms: ^Match_State, s, p, ep: int) -> (res: int, err: Error) {
|
||||
|
||||
if result != INVALID {
|
||||
return result, .OK
|
||||
} else {
|
||||
// TODO receive next step maybe?
|
||||
matched, rune_size := single_match(ms, s, p, ep) or_return
|
||||
}
|
||||
// TODO receive next step maybe?
|
||||
matched, rune_size := single_match(ms, s, p, ep) or_return
|
||||
|
||||
if matched {
|
||||
s += rune_size
|
||||
} else {
|
||||
return INVALID, .OK
|
||||
}
|
||||
if matched {
|
||||
s += rune_size
|
||||
} else {
|
||||
return INVALID, .OK
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
start_capture :: proc(ms: ^Match_State, s, p, what: int) -> (res: int, err: Error) {
|
||||
level := ms.level
|
||||
|
||||
@@ -366,6 +384,7 @@ start_capture :: proc(ms: ^Match_State, s, p, what: int) -> (res: int, err: Erro
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
end_capture :: proc(ms: ^Match_State, s, p: int) -> (res: int, err: Error) {
|
||||
l := capture_to_close(ms) or_return
|
||||
|
||||
@@ -379,6 +398,7 @@ end_capture :: proc(ms: ^Match_State, s, p: int) -> (res: int, err: Error) {
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
match_capture :: proc(ms: ^Match_State, s: int, char: rune) -> (res: int, err: Error) {
|
||||
index := check_capture(ms, char) or_return
|
||||
length := ms.capture[index].len
|
||||
@@ -390,6 +410,7 @@ match_capture :: proc(ms: ^Match_State, s: int, char: rune) -> (res: int, err: E
|
||||
return INVALID, .OK
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
match :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error) {
|
||||
s := s
|
||||
p := p
|
||||
@@ -431,7 +452,6 @@ match :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error) {
|
||||
// balanced string
|
||||
case 'b':
|
||||
s = match_balance(ms, s, p + 2) or_return
|
||||
|
||||
if s != INVALID {
|
||||
// eg after %b()
|
||||
return match(ms, s, p + 4)
|
||||
@@ -460,7 +480,7 @@ match :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error) {
|
||||
}
|
||||
|
||||
m1 := match_bracket_class(ms, previous, p, ep - 1) or_return
|
||||
m2 := match_bracket_class(ms, current, p, ep - 1) or_return
|
||||
m2 := match_bracket_class(ms, current, p, ep - 1) or_return
|
||||
|
||||
if !m1 && m2 {
|
||||
return match(ms, s, ep)
|
||||
@@ -486,6 +506,7 @@ match :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error) {
|
||||
return s, .OK
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
match_default :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error) {
|
||||
s := s
|
||||
ep := class_end(ms, p) or_return
|
||||
@@ -495,8 +516,10 @@ match_default :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error)
|
||||
epc := ep < len(ms.pattern) ? ms.pattern[ep] : 0
|
||||
|
||||
switch epc {
|
||||
case '*', '?', '-': return match(ms, s, ep + 1)
|
||||
case: s = INVALID
|
||||
case '*', '?', '-':
|
||||
return match(ms, s, ep + 1)
|
||||
case:
|
||||
s = INVALID
|
||||
}
|
||||
} else {
|
||||
epc := ep < len(ms.pattern) ? ms.pattern[ep] : 0
|
||||
@@ -505,22 +528,23 @@ match_default :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error)
|
||||
case '?':
|
||||
result := match(ms, s + ssize, ep + 1) or_return
|
||||
|
||||
if result != INVALID {
|
||||
s = result
|
||||
} else {
|
||||
if result == INVALID {
|
||||
return match(ms, s, ep + 1)
|
||||
}
|
||||
s = result
|
||||
|
||||
case '+': s = max_expand(ms, s + ssize, p, ep) or_return
|
||||
case '*': s = max_expand(ms, s, p, ep) or_return
|
||||
case '-': s = min_expand(ms, s, p, ep) or_return
|
||||
case: return match(ms, s + ssize, ep)
|
||||
case '*': s = max_expand(ms, s, p, ep) or_return
|
||||
case '-': s = min_expand(ms, s, p, ep) or_return
|
||||
case:
|
||||
return match(ms, s + ssize, ep)
|
||||
}
|
||||
}
|
||||
|
||||
return s, .OK
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
push_onecapture :: proc(ms: ^Match_State, i: int, s: int, e: int, matches: []Match) -> (err: Error) {
|
||||
if i >= ms.level {
|
||||
if i == 0 {
|
||||
@@ -533,22 +557,21 @@ push_onecapture :: proc(ms: ^Match_State, i: int, s: int, e: int, matches: []M
|
||||
length := ms.capture[i].len
|
||||
|
||||
switch length {
|
||||
case CAP_UNFINISHED: err = .Unfinished_Capture
|
||||
case CAP_POSITION: matches[i] = { init, init + 1 }
|
||||
case: matches[i] = { init, init + length }
|
||||
case CAP_UNFINISHED:
|
||||
err = .Unfinished_Capture
|
||||
case CAP_POSITION:
|
||||
matches[i] = { init, init + 1 }
|
||||
case:
|
||||
matches[i] = { init, init + length }
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
push_captures :: proc(
|
||||
ms: ^Match_State,
|
||||
s: int,
|
||||
e: int,
|
||||
matches: []Match,
|
||||
) -> (nlevels: int, err: Error) {
|
||||
nlevels = 1 if ms.level == 0 && s != -1 else ms.level
|
||||
@(require_results)
|
||||
push_captures :: proc(ms: ^Match_State, s, e: int, matches: []Match) -> (nlevels: int, err: Error) {
|
||||
nlevels = 1 if ms.level == 0 && s >= 0 else ms.level
|
||||
|
||||
for i in 0..<nlevels {
|
||||
push_onecapture(ms, i, s, e, matches) or_return
|
||||
@@ -559,6 +582,7 @@ push_captures :: proc(
|
||||
|
||||
// SPECIALS := "^$*+?.([%-"
|
||||
// all special characters inside a small ascii array
|
||||
@(rodata)
|
||||
SPECIALS_TABLE := [256]bool {
|
||||
'^' = true,
|
||||
'$' = true,
|
||||
@@ -573,6 +597,7 @@ SPECIALS_TABLE := [256]bool {
|
||||
}
|
||||
|
||||
// helper call to quick search for special characters
|
||||
@(require_results)
|
||||
index_special :: proc(text: string) -> int {
|
||||
for i in 0..<len(text) {
|
||||
if SPECIALS_TABLE[text[i]] {
|
||||
@@ -583,34 +608,34 @@ index_special :: proc(text: string) -> int {
|
||||
return -1
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
lmem_find :: proc(s1, s2: string) -> int {
|
||||
l1 := len(s1)
|
||||
l2 := len(s2)
|
||||
|
||||
if l2 == 0 {
|
||||
return 0
|
||||
} else if l2 > l1 {
|
||||
}
|
||||
if l2 > l1 {
|
||||
return -1
|
||||
} else {
|
||||
init := strings.index_byte(s1, s2[0])
|
||||
end := init + l2
|
||||
}
|
||||
|
||||
for end <= l1 && init != -1 {
|
||||
init += 1
|
||||
init := strings.index_byte(s1, s2[0])
|
||||
end := init + l2
|
||||
|
||||
if s1[init - 1:end] == s2 {
|
||||
return init - 1
|
||||
} else {
|
||||
next := strings.index_byte(s1[init:], s2[0])
|
||||
for end <= l1 && init >= 0 {
|
||||
init += 1
|
||||
|
||||
if next == -1 {
|
||||
return -1
|
||||
} else {
|
||||
init = init + next
|
||||
end = init + l2
|
||||
}
|
||||
}
|
||||
if s1[init - 1:end] == s2 {
|
||||
return init - 1
|
||||
}
|
||||
next := strings.index_byte(s1[init:], s2[0])
|
||||
|
||||
if next == -1 {
|
||||
return -1
|
||||
}
|
||||
init = init + next
|
||||
end = init + l2
|
||||
}
|
||||
|
||||
return -1
|
||||
@@ -618,36 +643,28 @@ lmem_find :: proc(s1, s2: string) -> int {
|
||||
|
||||
// find a pattern with in a haystack with an offset
|
||||
// allow_memfind will speed up simple searches
|
||||
find_aux :: proc(
|
||||
haystack: string,
|
||||
pattern: string,
|
||||
offset: int,
|
||||
allow_memfind: bool,
|
||||
matches: ^[MAX_CAPTURES]Match,
|
||||
) -> (captures: int, err: Error) {
|
||||
find_aux :: proc(haystack, pattern: string, offset: int, allow_memfind: bool, matches: ^[MAX_CAPTURES]Match) -> (captures: int, err: Error) {
|
||||
s := offset
|
||||
p := 0
|
||||
|
||||
specials_idx := index_special(pattern)
|
||||
if allow_memfind && specials_idx == -1 {
|
||||
if index := lmem_find(haystack[s:], pattern); index != -1 {
|
||||
if index := lmem_find(haystack[s:], pattern); index >= 0 {
|
||||
matches[0] = { index + s, index + s + len(pattern) }
|
||||
captures = 1
|
||||
return
|
||||
} else {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
pattern := pattern
|
||||
anchor: bool
|
||||
if len(pattern) > 0 && pattern[0] == '^' {
|
||||
anchor = true
|
||||
anchor = true
|
||||
pattern = pattern[1:]
|
||||
}
|
||||
|
||||
ms := Match_State {
|
||||
src = haystack,
|
||||
src = haystack,
|
||||
pattern = pattern,
|
||||
}
|
||||
|
||||
@@ -684,11 +701,8 @@ find_aux :: proc(
|
||||
// rest has to be used from captures
|
||||
// assumes captures is zeroed on first iteration
|
||||
// resets captures to zero on last iteration
|
||||
gmatch :: proc(
|
||||
haystack: ^string,
|
||||
pattern: string,
|
||||
captures: ^[MAX_CAPTURES]Match,
|
||||
) -> (res: string, ok: bool) {
|
||||
@(require_results)
|
||||
gmatch :: proc(haystack: ^string, pattern: string, captures: ^[MAX_CAPTURES]Match) -> (res: string, ok: bool) {
|
||||
haystack^ = haystack[captures[0].byte_end:]
|
||||
if len(haystack) > 0 {
|
||||
length, err := find_aux(haystack^, pattern, 0, false, captures)
|
||||
@@ -707,24 +721,17 @@ gmatch :: proc(
|
||||
}
|
||||
|
||||
// gsub with builder, replace patterns found with the replace content
|
||||
gsub_builder :: proc(
|
||||
builder: ^strings.Builder,
|
||||
haystack: string,
|
||||
pattern: string,
|
||||
replace: string,
|
||||
) -> string {
|
||||
@(require_results)
|
||||
gsub_builder :: proc(builder: ^strings.Builder, haystack, pattern, replace: string) -> string {
|
||||
// find matches
|
||||
captures: [MAX_CAPTURES]Match
|
||||
haystack := haystack
|
||||
|
||||
for {
|
||||
length, err := find_aux(haystack, pattern, 0, false, &captures)
|
||||
|
||||
// done
|
||||
if length == 0 {
|
||||
if length == 0 { // done
|
||||
break
|
||||
}
|
||||
|
||||
if err != .OK {
|
||||
return {}
|
||||
}
|
||||
@@ -746,21 +753,17 @@ gsub_builder :: proc(
|
||||
}
|
||||
|
||||
// uses temp builder to build initial string - then allocates the result
|
||||
gsub_allocator :: proc(
|
||||
haystack: string,
|
||||
pattern: string,
|
||||
replace: string,
|
||||
allocator := context.allocator,
|
||||
) -> string {
|
||||
@(require_results)
|
||||
gsub_allocator :: proc(haystack, pattern, replace: string, allocator := context.allocator) -> string {
|
||||
builder := strings.builder_make(0, 256, context.temp_allocator)
|
||||
return gsub_builder(&builder, haystack, pattern, replace)
|
||||
}
|
||||
|
||||
Gsub_Proc :: proc(
|
||||
// optional passed data
|
||||
data: rawptr,
|
||||
data: rawptr,
|
||||
// word match found
|
||||
word: string,
|
||||
word: string,
|
||||
// current haystack for found captures
|
||||
haystack: string,
|
||||
// found captures - empty for no captures
|
||||
@@ -768,20 +771,14 @@ Gsub_Proc :: proc(
|
||||
)
|
||||
|
||||
// call a procedure on every match in the haystack
|
||||
gsub_with :: proc(
|
||||
haystack: string,
|
||||
pattern: string,
|
||||
data: rawptr,
|
||||
call: Gsub_Proc,
|
||||
) {
|
||||
gsub_with :: proc(haystack, pattern: string, data: rawptr, call: Gsub_Proc) {
|
||||
// find matches
|
||||
captures: [MAX_CAPTURES]Match
|
||||
haystack := haystack
|
||||
|
||||
for {
|
||||
length := find_aux(haystack, pattern, 0, false, &captures) or_break
|
||||
// done
|
||||
if length == 0 {
|
||||
if length == 0 { // done
|
||||
break
|
||||
}
|
||||
|
||||
@@ -800,11 +797,8 @@ gsub :: proc { gsub_builder, gsub_allocator }
|
||||
// iterative find with zeroth capture only
|
||||
// assumes captures is zeroed on first iteration
|
||||
// resets captures to zero on last iteration
|
||||
gfind :: proc(
|
||||
haystack: ^string,
|
||||
pattern: string,
|
||||
captures: ^[MAX_CAPTURES]Match,
|
||||
) -> (res: string, ok: bool) {
|
||||
@(require_results)
|
||||
gfind :: proc(haystack: ^string, pattern: string, captures: ^[MAX_CAPTURES]Match) -> (res: string, ok: bool) {
|
||||
haystack^ = haystack[captures[0].byte_end:]
|
||||
if len(haystack) > 0 {
|
||||
length, err := find_aux(haystack^, pattern, 0, true, captures)
|
||||
@@ -822,10 +816,8 @@ gfind :: proc(
|
||||
}
|
||||
|
||||
// rebuilds a pattern into a case insensitive pattern
|
||||
pattern_case_insensitive_builder :: proc(
|
||||
builder: ^strings.Builder,
|
||||
pattern: string,
|
||||
) -> (res: string) {
|
||||
@(require_results)
|
||||
pattern_case_insensitive_builder :: proc(builder: ^strings.Builder, pattern: string) -> string {
|
||||
p := pattern
|
||||
last_percent: bool
|
||||
|
||||
@@ -849,11 +841,8 @@ pattern_case_insensitive_builder :: proc(
|
||||
return strings.to_string(builder^)
|
||||
}
|
||||
|
||||
pattern_case_insensitive_allocator :: proc(
|
||||
pattern: string,
|
||||
cap: int = 256,
|
||||
allocator := context.allocator,
|
||||
) -> (res: string) {
|
||||
@(require_results)
|
||||
pattern_case_insensitive_allocator :: proc(pattern: string, cap: int = 256, allocator := context.allocator) -> string {
|
||||
builder := strings.builder_make(0, cap, context.temp_allocator)
|
||||
return pattern_case_insensitive_builder(&builder, pattern)
|
||||
}
|
||||
@@ -877,6 +866,7 @@ Matcher :: struct {
|
||||
}
|
||||
|
||||
// init using haystack & pattern and an optional byte offset
|
||||
@(require_results)
|
||||
matcher_init :: proc(haystack, pattern: string, offset: int = 0) -> (res: Matcher) {
|
||||
res.haystack = haystack
|
||||
res.pattern = pattern
|
||||
@@ -886,13 +876,14 @@ matcher_init :: proc(haystack, pattern: string, offset: int = 0) -> (res: Matche
|
||||
}
|
||||
|
||||
// find the first match and return the byte start / end position in the string, true on success
|
||||
@(require_results)
|
||||
matcher_find :: proc(matcher: ^Matcher) -> (start, end: int, ok: bool) #no_bounds_check {
|
||||
matcher.captures_length, matcher.err = find_aux(
|
||||
matcher.haystack,
|
||||
matcher.pattern,
|
||||
matcher.offset,
|
||||
true,
|
||||
&matcher.captures,
|
||||
allow_memfind=true,
|
||||
matches=&matcher.captures,
|
||||
)
|
||||
ok = matcher.captures_length > 0 && matcher.err == .OK
|
||||
match := matcher.captures[0]
|
||||
@@ -902,13 +893,14 @@ matcher_find :: proc(matcher: ^Matcher) -> (start, end: int, ok: bool) #no_bound
|
||||
}
|
||||
|
||||
// find the first match and return the matched word, true on success
|
||||
@(require_results)
|
||||
matcher_match :: proc(matcher: ^Matcher) -> (word: string, ok: bool) #no_bounds_check {
|
||||
matcher.captures_length, matcher.err = find_aux(
|
||||
matcher.haystack,
|
||||
matcher.pattern,
|
||||
matcher.offset,
|
||||
false,
|
||||
&matcher.captures,
|
||||
allow_memfind=false,
|
||||
matches=&matcher.captures,
|
||||
)
|
||||
ok = matcher.captures_length > 0 && matcher.err == .OK
|
||||
match := matcher.captures[0]
|
||||
@@ -917,6 +909,7 @@ matcher_match :: proc(matcher: ^Matcher) -> (word: string, ok: bool) #no_bounds_
|
||||
}
|
||||
|
||||
// get the capture at the "correct" spot, as spot 0 is reserved for the first match
|
||||
@(require_results)
|
||||
matcher_capture :: proc(matcher: ^Matcher, index: int, loc := #caller_location) -> string #no_bounds_check {
|
||||
runtime.bounds_check_error_loc(loc, index + 1, MAX_CAPTURES - 1)
|
||||
cap := matcher.captures[index + 1]
|
||||
@@ -924,6 +917,7 @@ matcher_capture :: proc(matcher: ^Matcher, index: int, loc := #caller_location)
|
||||
}
|
||||
|
||||
// get the raw match out of the captures, skipping spot 0
|
||||
@(require_results)
|
||||
matcher_capture_raw :: proc(matcher: ^Matcher, index: int, loc := #caller_location) -> Match #no_bounds_check {
|
||||
runtime.bounds_check_error_loc(loc, index + 1, MAX_CAPTURES - 1)
|
||||
return matcher.captures[index + 1]
|
||||
@@ -933,6 +927,7 @@ matcher_capture_raw :: proc(matcher: ^Matcher, index: int, loc := #caller_locati
|
||||
matcher_gmatch :: matcher_match_iter
|
||||
|
||||
// iteratively match the haystack till it cant find any matches
|
||||
@(require_results)
|
||||
matcher_match_iter :: proc(matcher: ^Matcher) -> (res: string, index: int, ok: bool) {
|
||||
if len(matcher.iter) > 0 {
|
||||
matcher.captures_length, matcher.err = find_aux(
|
||||
@@ -962,6 +957,7 @@ matcher_match_iter :: proc(matcher: ^Matcher) -> (res: string, index: int, ok: b
|
||||
}
|
||||
|
||||
// get a slice of all valid captures above the first match
|
||||
@(require_results)
|
||||
matcher_captures_slice :: proc(matcher: ^Matcher) -> []Match {
|
||||
return matcher.captures[1:matcher.captures_length]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package unicode
|
||||
|
||||
// simple_fold iterates over the Unicode code points equivalent under the Unicode defined simple case folding.
|
||||
// simple_fold returns the smallest rune > r if one exists, or the smallest rune >= 0.
|
||||
// If no valid Unicode code point exists, r is returned.
|
||||
//
|
||||
// Example:
|
||||
// simple_fold('A') == 'a'
|
||||
// simple_fold('a') == 'A'
|
||||
// simple_fold('Z') == 'z'
|
||||
// simple_fold('z') == 'Z'
|
||||
// simple_fold('7') == '7'
|
||||
// simple_fold('k') == '\u212a' (Kelvin symbol, K)
|
||||
// simple_fold('\u212a') == 'k'
|
||||
// simple_fold(-3) == -3
|
||||
@(require_results)
|
||||
simple_fold :: proc(r: rune) -> rune {
|
||||
Fold_Pair :: struct {
|
||||
from: u16,
|
||||
to: u16,
|
||||
}
|
||||
|
||||
@(static, rodata)
|
||||
ASCII_FOLD := [MAX_ASCII + 1]u16{
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
|
||||
0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
|
||||
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
|
||||
0x0040, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
|
||||
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
|
||||
0x0060, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x212a, 0x004c, 0x004d, 0x004e, 0x004f,
|
||||
0x0050, 0x0051, 0x0052, 0x017f, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,
|
||||
}
|
||||
|
||||
@(static, rodata)
|
||||
CASE_ORBIT := [?]Fold_Pair{
|
||||
{0x004B, 0x006B}, {0x0053, 0x0073}, {0x006B, 0x212A}, {0x0073, 0x017F}, {0x00B5, 0x039C}, {0x00C5, 0x00E5}, {0x00DF, 0x1E9E},
|
||||
{0x00E5, 0x212B}, {0x0130, 0x0130}, {0x0131, 0x0131}, {0x017F, 0x0053}, {0x01C4, 0x01C5}, {0x01C5, 0x01C6}, {0x01C6, 0x01C4},
|
||||
{0x01C7, 0x01C8}, {0x01C8, 0x01C9}, {0x01C9, 0x01C7}, {0x01CA, 0x01CB}, {0x01CB, 0x01CC}, {0x01CC, 0x01CA}, {0x01F1, 0x01F2},
|
||||
{0x01F2, 0x01F3}, {0x01F3, 0x01F1}, {0x0345, 0x0399}, {0x0392, 0x03B2}, {0x0395, 0x03B5}, {0x0398, 0x03B8}, {0x0399, 0x03B9},
|
||||
{0x039A, 0x03BA}, {0x039C, 0x03BC}, {0x03A0, 0x03C0}, {0x03A1, 0x03C1}, {0x03A3, 0x03C2}, {0x03A6, 0x03C6}, {0x03A9, 0x03C9},
|
||||
{0x03B2, 0x03D0}, {0x03B5, 0x03F5}, {0x03B8, 0x03D1}, {0x03B9, 0x1FBE}, {0x03BA, 0x03F0}, {0x03BC, 0x00B5}, {0x03C0, 0x03D6},
|
||||
{0x03C1, 0x03F1}, {0x03C2, 0x03C3}, {0x03C3, 0x03A3}, {0x03C6, 0x03D5}, {0x03C9, 0x2126}, {0x03D0, 0x0392}, {0x03D1, 0x03F4},
|
||||
{0x03D5, 0x03A6}, {0x03D6, 0x03A0}, {0x03F0, 0x039A}, {0x03F1, 0x03A1}, {0x03F4, 0x0398}, {0x03F5, 0x0395}, {0x0412, 0x0432},
|
||||
{0x0414, 0x0434}, {0x041E, 0x043E}, {0x0421, 0x0441}, {0x0422, 0x0442}, {0x042A, 0x044A}, {0x0432, 0x1C80}, {0x0434, 0x1C81},
|
||||
{0x043E, 0x1C82}, {0x0441, 0x1C83}, {0x0442, 0x1C84}, {0x044A, 0x1C86}, {0x0462, 0x0463}, {0x0463, 0x1C87}, {0x1C80, 0x0412},
|
||||
{0x1C81, 0x0414}, {0x1C82, 0x041E}, {0x1C83, 0x0421}, {0x1C84, 0x1C85}, {0x1C85, 0x0422}, {0x1C86, 0x042A}, {0x1C87, 0x0462},
|
||||
{0x1C88, 0xA64A}, {0x1E60, 0x1E61}, {0x1E61, 0x1E9B}, {0x1E9B, 0x1E60}, {0x1E9E, 0x00DF}, {0x1FBE, 0x0345}, {0x2126, 0x03A9},
|
||||
{0x212A, 0x004B}, {0x212B, 0x00C5}, {0xA64A, 0xA64B}, {0xA64B, 0x1C88},
|
||||
}
|
||||
|
||||
if r < 0 || r > MAX_RUNE {
|
||||
return r
|
||||
}
|
||||
if int(r) < len(ASCII_FOLD) {
|
||||
return rune(ASCII_FOLD[r])
|
||||
}
|
||||
|
||||
lo, hi := 0, len(CASE_ORBIT)
|
||||
for lo < hi {
|
||||
m := int(uint(lo+hi) >> 1)
|
||||
if rune(CASE_ORBIT[m].from) < r {
|
||||
lo = m + 1
|
||||
} else {
|
||||
hi = m
|
||||
}
|
||||
}
|
||||
|
||||
if lo < len(CASE_ORBIT) && rune(CASE_ORBIT[lo].from) == r {
|
||||
return rune(CASE_ORBIT[lo].to)
|
||||
}
|
||||
|
||||
|
||||
l := to_lower(r)
|
||||
if l != r {
|
||||
return l
|
||||
}
|
||||
return to_upper(r)
|
||||
}
|
||||
+56
-3
@@ -210,7 +210,7 @@ gb_internal ObjcMsgKind get_objc_proc_kind(Type *return_type) {
|
||||
return ObjcMsg_normal;
|
||||
}
|
||||
|
||||
gb_internal void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_type, Slice<Type *> param_types) {
|
||||
void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_type, Slice<Type *> param_types) {
|
||||
ObjcMsgKind kind = get_objc_proc_kind(return_type);
|
||||
|
||||
Scope *scope = create_scope(c->info, nullptr);
|
||||
@@ -248,6 +248,12 @@ gb_internal void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_t
|
||||
try_to_add_package_dependency(c, "runtime", "objc_msgSend_fpret");
|
||||
try_to_add_package_dependency(c, "runtime", "objc_msgSend_fp2ret");
|
||||
try_to_add_package_dependency(c, "runtime", "objc_msgSend_stret");
|
||||
|
||||
Slice<Ast *> args = call->CallExpr.args;
|
||||
if (args.count > 0 && args[0]->tav.objc_super_target) {
|
||||
try_to_add_package_dependency(c, "runtime", "objc_msgSendSuper2");
|
||||
try_to_add_package_dependency(c, "runtime", "objc_msgSendSuper2_stret");
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal bool is_constant_string(CheckerContext *c, String const &builtin_name, Ast *expr, String *name_) {
|
||||
@@ -466,8 +472,8 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan
|
||||
|
||||
isize capture_arg_count = ce->args.count - 1;
|
||||
|
||||
// NOTE(harold): The first parameter is already checked at check_builtin_procedure().
|
||||
// Checking again would invalidate the Entity -> Value map for direct parameters if it's the handler proc.
|
||||
// NOTE(harold): The first argument is already checked at check_builtin_procedure().
|
||||
// Checking again would invalidate the Entity -> Value map for direct arguments if it's the handler proc.
|
||||
param_operands[0] = *operand;
|
||||
|
||||
for (isize i = 0; i < ce->args.count-1; i++) {
|
||||
@@ -680,6 +686,52 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan
|
||||
operand->mode = Addressing_Value;
|
||||
return true;
|
||||
} break;
|
||||
|
||||
case BuiltinProc_objc_super:
|
||||
{
|
||||
// Must be a pointer to an Objective-C object.
|
||||
Type *objc_obj = operand->type;
|
||||
if (!is_type_objc_ptr_to_object(objc_obj)) {
|
||||
gbString e = expr_to_string(operand->expr);
|
||||
gbString t = type_to_string(objc_obj);
|
||||
error(operand->expr, "'%.*s' expected a pointer to an Objective-C object, but got '%s' of type %s", LIT(builtin_name), e, t);
|
||||
gb_string_free(t);
|
||||
gb_string_free(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (operand->mode != Addressing_Value && operand->mode != Addressing_Variable) {
|
||||
gbString e = expr_to_string(operand->expr);
|
||||
gbString t = type_to_string(operand->type);
|
||||
error(operand->expr, "'%.*s' expression '%s', of type %s, must be a value or variable.", LIT(builtin_name), e, t);
|
||||
gb_string_free(t);
|
||||
gb_string_free(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
Type *obj_type = type_deref(objc_obj);
|
||||
GB_ASSERT(obj_type->kind == Type_Named);
|
||||
|
||||
// NOTE(harold) Track original type before transforming it to the superclass.
|
||||
// This is needed because objc_msgSendSuper2 must start its search on the subclass, not the superclass.
|
||||
call->tav.objc_super_target = obj_type;
|
||||
|
||||
// The superclass type must be known at compile time. We require this so that the selector method expressions
|
||||
// methods are resolved to the superclass's methods instead of the subclass's.
|
||||
Type *superclass = obj_type->Named.type_name->TypeName.objc_superclass;
|
||||
if (superclass == nullptr) {
|
||||
gbString t = type_to_string(obj_type);
|
||||
error(operand->expr, "'%.*s' target object '%.*s' does not have an Objective-C superclass. One must be set via the @(objc_superclass) attribute", LIT(builtin_name), t);
|
||||
gb_string_free(t);
|
||||
return false;
|
||||
}
|
||||
|
||||
GB_ASSERT(superclass->Named.type_name->TypeName.objc_class_name.len > 0);
|
||||
|
||||
operand->type = alloc_type_pointer(superclass);
|
||||
return true;
|
||||
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2515,6 +2567,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
case BuiltinProc_objc_register_class:
|
||||
case BuiltinProc_objc_ivar_get:
|
||||
case BuiltinProc_objc_block:
|
||||
case BuiltinProc_objc_super:
|
||||
return check_builtin_objc_procedure(c, operand, call, id, type_hint);
|
||||
|
||||
case BuiltinProc___entry_point:
|
||||
|
||||
+83
-45
@@ -587,9 +587,7 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr,
|
||||
super = named_type->Named.type_name->TypeName.objc_superclass;
|
||||
}
|
||||
} else {
|
||||
if (ac.objc_superclass != nullptr) {
|
||||
error(e->token, "@(objc_superclass) may only be applied when the @(obj_implement) attribute is also applied");
|
||||
} else if (ac.objc_ivar != nullptr) {
|
||||
if (ac.objc_ivar != nullptr) {
|
||||
error(e->token, "@(objc_ivar) may only be applied when the @(obj_implement) attribute is also applied");
|
||||
} else if (ac.objc_context_provider != nullptr) {
|
||||
error(e->token, "@(objc_context_provider) may only be applied when the @(obj_implement) attribute is also applied");
|
||||
@@ -1084,61 +1082,100 @@ gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeCon
|
||||
// Enable implementation by default if the class is an implementer too and
|
||||
// @objc_implement was not set to false explicitly in this proc.
|
||||
bool implement = tn->TypeName.objc_is_implementation;
|
||||
if( ac.objc_is_implementation && !tn->TypeName.objc_is_implementation ) {
|
||||
error(e->token, "Cannot apply @(objc_is_implement) to a procedure whose type does not also have @(objc_is_implement) set");
|
||||
}
|
||||
|
||||
if (ac.objc_is_disabled_implement) {
|
||||
implement = false;
|
||||
}
|
||||
|
||||
if (implement) {
|
||||
GB_ASSERT(e->kind == Entity_Procedure);
|
||||
String objc_selector = ac.objc_selector != "" ? ac.objc_selector : ac.objc_name;
|
||||
|
||||
if (e->kind == Entity_Procedure) {
|
||||
bool has_body = e->decl_info->proc_lit->ProcLit.body != nullptr;
|
||||
e->Procedure.is_objc_impl_or_import = implement || !has_body;
|
||||
e->Procedure.is_objc_class_method = ac.objc_is_class_method;
|
||||
e->Procedure.objc_selector_name = objc_selector;
|
||||
e->Procedure.objc_class = tn;
|
||||
|
||||
auto &proc = e->type->Proc;
|
||||
Type *first_param = proc.param_count > 0 ? proc.params->Tuple.variables[0]->type : t_untyped_nil;
|
||||
|
||||
if (!tn->TypeName.objc_is_implementation) {
|
||||
error(e->token, "@(objc_is_implement) attribute may only be applied to procedures whose class also have @(objc_is_implement) applied");
|
||||
} else if (!ac.objc_is_class_method && !(first_param->kind == Type_Pointer && internal_check_is_assignable_to(t, first_param->Pointer.elem))) {
|
||||
error(e->token, "Objective-C instance methods implementations require the first parameter to be a pointer to the class type set by @(objc_type)");
|
||||
} else if (proc.calling_convention == ProcCC_Odin && !tn->TypeName.objc_context_provider) {
|
||||
error(e->token, "Objective-C methods with Odin calling convention can only be used with classes that have @(objc_context_provider) set");
|
||||
} else if (ac.objc_is_class_method && proc.calling_convention != ProcCC_CDecl) {
|
||||
error(e->token, "Objective-C class methods (objc_is_class_method=true) that have @objc_is_implementation can only use \"c\" calling convention");
|
||||
} else if (proc.result_count > 1) {
|
||||
error(e->token, "Objective-C method implementations may return at most 1 value");
|
||||
} else {
|
||||
// Always export unconditionally
|
||||
// NOTE(harold): This means check_objc_methods() MUST be called before
|
||||
// e->Procedure.is_export is set in check_proc_decl()!
|
||||
if (ac.is_export) {
|
||||
error(e->token, "Explicit export not allowed when @(objc_implement) is set. It set exported implicitly");
|
||||
}
|
||||
if (ac.link_name != "") {
|
||||
error(e->token, "Explicit linkage not allowed when @(objc_implement) is set. It set to \"strong\" implicitly");
|
||||
}
|
||||
|
||||
ac.is_export = true;
|
||||
ac.linkage = STR_LIT("strong");
|
||||
|
||||
auto method = ObjcMethodData{ ac, e };
|
||||
method.ac.objc_selector = ac.objc_selector != "" ? ac.objc_selector : ac.objc_name;
|
||||
|
||||
CheckerInfo *info = ctx->info;
|
||||
mutex_lock(&info->objc_method_mutex);
|
||||
defer (mutex_unlock(&info->objc_method_mutex));
|
||||
|
||||
Array<ObjcMethodData>* method_list = map_get(&info->objc_method_implementations, t);
|
||||
if (method_list) {
|
||||
array_add(method_list, method);
|
||||
if (implement) {
|
||||
if( !has_body ) {
|
||||
error(e->token, "Procedures with @(objc_is_implement) must have a body");
|
||||
} else if (!tn->TypeName.objc_is_implementation) {
|
||||
error(e->token, "@(objc_is_implement) attribute may only be applied to procedures whose class also have @(objc_is_implement) applied");
|
||||
} else if (!ac.objc_is_class_method && !(first_param->kind == Type_Pointer && internal_check_is_assignable_to(t, first_param->Pointer.elem))) {
|
||||
error(e->token, "Objective-C instance methods implementations require the first parameter to be a pointer to the class type set by @(objc_type)");
|
||||
} else if (proc.calling_convention == ProcCC_Odin && !tn->TypeName.objc_context_provider) {
|
||||
error(e->token, "Objective-C methods with Odin calling convention can only be used with classes that have @(objc_context_provider) set");
|
||||
} else if (ac.objc_is_class_method && proc.calling_convention != ProcCC_CDecl) {
|
||||
error(e->token, "Objective-C class methods (objc_is_class_method=true) that have @objc_is_implementation can only use \"c\" calling convention");
|
||||
} else if (proc.result_count > 1) {
|
||||
error(e->token, "Objective-C method implementations may return at most 1 value");
|
||||
} else {
|
||||
auto list = array_make<ObjcMethodData>(permanent_allocator(), 1, 8);
|
||||
list[0] = method;
|
||||
// Always export unconditionally
|
||||
// NOTE(harold): This means check_objc_methods() MUST be called before
|
||||
// e->Procedure.is_export is set in check_proc_decl()!
|
||||
if (ac.is_export) {
|
||||
error(e->token, "Explicit export not allowed when @(objc_implement) is set. It set exported implicitly");
|
||||
}
|
||||
if (ac.link_name != "") {
|
||||
error(e->token, "Explicit linkage not allowed when @(objc_implement) is set. It set to \"strong\" implicitly");
|
||||
}
|
||||
|
||||
map_set(&info->objc_method_implementations, t, list);
|
||||
ac.is_export = true;
|
||||
ac.linkage = STR_LIT("strong");
|
||||
|
||||
auto method = ObjcMethodData{ ac, e };
|
||||
method.ac.objc_selector = objc_selector;
|
||||
|
||||
CheckerInfo *info = ctx->info;
|
||||
mutex_lock(&info->objc_method_mutex);
|
||||
defer (mutex_unlock(&info->objc_method_mutex));
|
||||
|
||||
Array<ObjcMethodData>* method_list = map_get(&info->objc_method_implementations, t);
|
||||
if (method_list) {
|
||||
array_add(method_list, method);
|
||||
} else {
|
||||
auto list = array_make<ObjcMethodData>(permanent_allocator(), 1, 8);
|
||||
list[0] = method;
|
||||
|
||||
map_set(&info->objc_method_implementations, t, list);
|
||||
}
|
||||
}
|
||||
} else if (!has_body) {
|
||||
if (ac.objc_selector == "The @(objc_selector) attribute is required for imported Objective-C methods.") {
|
||||
return;
|
||||
} else if (proc.calling_convention != ProcCC_CDecl) {
|
||||
error(e->token, "Imported Objective-C methods must use the \"c\" calling convention");
|
||||
return;
|
||||
} else if (tn->TypeName.objc_context_provider) {
|
||||
error(e->token, "Imported Objective-C class '%.*s' must not declare context providers.", tn->type->Named.name);
|
||||
return;
|
||||
} else if (tn->TypeName.objc_is_implementation) {
|
||||
error(e->token, "Imported Objective-C methods used in a class with @(objc_implement) is not allowed.");
|
||||
return;
|
||||
} else if (!ac.objc_is_class_method && !(first_param->kind == Type_Pointer && internal_check_is_assignable_to(t, first_param->Pointer.elem))) {
|
||||
error(e->token, "Objective-C instance methods require the first parameter to be a pointer to the class type set by @(objc_type)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (ac.objc_selector != "") {
|
||||
error(e->token, "@(objc_selector) may only be applied to procedures that are Objective-C implementations.");
|
||||
else if(ac.objc_selector != "") {
|
||||
error(e->token, "@(objc_selector) may only be applied to procedures that are Objective-C method implementations or are imported.");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
GB_ASSERT(e->kind == Entity_ProcGroup);
|
||||
if (tn->TypeName.objc_is_implementation) {
|
||||
error(e->token, "Objective-C procedure groups cannot use the @(objc_implement) attribute.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mutex_lock(&global_type_name_objc_metadata_mutex);
|
||||
defer (mutex_unlock(&global_type_name_objc_metadata_mutex));
|
||||
|
||||
@@ -1523,7 +1560,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
||||
if (!pt->is_polymorphic) {
|
||||
check_procedure_later(ctx->checker, ctx->file, e->token, d, proc_type, pl->body, pl->tags);
|
||||
}
|
||||
} else if (!is_foreign) {
|
||||
} else if (!is_foreign && !e->Procedure.is_objc_impl_or_import) {
|
||||
if (e->Procedure.is_export) {
|
||||
error(e->token, "Foreign export procedures must have a body");
|
||||
} else {
|
||||
@@ -1571,6 +1608,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
||||
// NOTE(bill): this must be delayed because the foreign import paths might not be evaluated yet until much later
|
||||
mpsc_enqueue(&ctx->info->foreign_decls_to_check, e);
|
||||
} else {
|
||||
// TODO(harold): Check if it's an objective-C foreign, if so, I don't think we need to check it.
|
||||
check_foreign_procedure(ctx, e, d);
|
||||
}
|
||||
} else {
|
||||
|
||||
+73
-8
@@ -1296,11 +1296,6 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
|
||||
error_line("\t Got: %s\n", s_got);
|
||||
gb_string_free(s_got);
|
||||
gb_string_free(s_expected);
|
||||
|
||||
Type *tx = x->Proc.params->Tuple.variables[0]->type;
|
||||
Type *ty = y->Proc.params->Tuple.variables[0]->type;
|
||||
gb_printf_err("%s kind:%.*s e:%p ot:%p\n", type_to_string(tx), LIT(type_strings[tx->kind]), tx->Named.type_name, tx->Named.type_name->TypeName.original_type_for_parapoly);
|
||||
gb_printf_err("%s kind:%.*s e:%p ot:%p\n", type_to_string(ty), LIT(type_strings[ty->kind]), ty->Named.type_name, ty->Named.type_name->TypeName.original_type_for_parapoly);
|
||||
} else {
|
||||
gbString s_expected = type_to_string(y);
|
||||
gbString s_got = type_to_string(x);
|
||||
@@ -6216,7 +6211,6 @@ gb_internal isize get_procedure_param_count_excluding_defaults(Type *pt, isize *
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7488,8 +7482,6 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
|
||||
Entity *e = proc_entities[valids[0].index];
|
||||
GB_ASSERT(e != nullptr);
|
||||
|
||||
Array<Operand> named_operands = {};
|
||||
|
||||
check_call_arguments_single(c, call, operand,
|
||||
e, e->type,
|
||||
positional_operands, named_operands,
|
||||
@@ -8151,6 +8143,73 @@ gb_internal ExprKind check_call_expr_as_type_cast(CheckerContext *c, Operand *op
|
||||
}
|
||||
|
||||
|
||||
void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_type, Slice<Type *> param_types);
|
||||
|
||||
gb_internal void check_objc_call_expr(CheckerContext *c, Operand *operand, Ast *call, Entity *proc_entity, Type *proc_type) {
|
||||
auto &proc = proc_type->Proc;
|
||||
Slice<Entity *> params = proc.params ? proc.params->Tuple.variables : Slice<Entity *>{};
|
||||
|
||||
Type *self_type = nullptr;
|
||||
isize params_start = 1;
|
||||
|
||||
ast_node(ce, CallExpr, call);
|
||||
|
||||
Type *return_type = proc.result_count == 0 ? nullptr : proc.results->Tuple.variables[0]->type;
|
||||
bool is_return_instancetype = return_type != nullptr && return_type == t_objc_instancetype;
|
||||
|
||||
if (params.count == 0 || !is_type_objc_ptr_to_object(params[0]->type)) {
|
||||
if (!proc_entity->Procedure.is_objc_class_method) {
|
||||
// Not a class method, invalid call
|
||||
error(call, "Invalid Objective-C call: The Objective-C method is not a class method but this first parameter is not an Objective-C object pointer.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_return_instancetype) {
|
||||
if (ce->proc->kind == Ast_SelectorExpr) {
|
||||
ast_node(se, SelectorExpr, ce->proc);
|
||||
|
||||
// NOTE(harold): These should have already been checked, right?
|
||||
GB_ASSERT(se->expr->tav.mode == Addressing_Type && se->expr->tav.type->kind == Type_Named);
|
||||
|
||||
return_type = alloc_type_pointer(se->expr->tav.type);
|
||||
} else {
|
||||
return_type = proc_entity->Procedure.objc_class->type;
|
||||
}
|
||||
}
|
||||
|
||||
self_type = t_objc_Class;
|
||||
params_start = 0;
|
||||
} else if (ce->args.count > 0) {
|
||||
GB_ASSERT(is_type_objc_ptr_to_object(params[0]->type));
|
||||
|
||||
if (ce->args[0]->tav.objc_super_target) {
|
||||
self_type = t_objc_super_ptr;
|
||||
} else {
|
||||
self_type = ce->args[0]->tav.type;
|
||||
}
|
||||
|
||||
if (is_return_instancetype) {
|
||||
// NOTE(harold): These should have already been checked, right?
|
||||
GB_ASSERT(ce->args[0]->tav.type && ce->args[0]->tav.type->kind == Type_Pointer && ce->args[0]->tav.type->Pointer.elem->kind == Type_Named);
|
||||
|
||||
return_type = ce->args[0]->tav.type;
|
||||
}
|
||||
}
|
||||
|
||||
auto param_types = slice_make<Type *>(permanent_allocator(), proc.param_count + 2 - params_start);
|
||||
param_types[0] = self_type;
|
||||
param_types[1] = t_objc_SEL;
|
||||
|
||||
for (isize i = params_start; i < params.count; i++) {
|
||||
param_types[i+2-params_start] = params[i]->type;
|
||||
}
|
||||
|
||||
if (is_return_instancetype) {
|
||||
operand->type = return_type;
|
||||
}
|
||||
|
||||
add_objc_proc_type(c, call, return_type, param_types);
|
||||
}
|
||||
|
||||
gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Slice<Ast *> const &args, ProcInlining inlining, Type *type_hint) {
|
||||
if (proc != nullptr &&
|
||||
@@ -8414,6 +8473,12 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
|
||||
}
|
||||
}
|
||||
|
||||
Entity *proc_entity = entity_from_expr(call->CallExpr.proc);
|
||||
bool is_objc_call = proc_entity && proc_entity->kind == Entity_Procedure && proc_entity->Procedure.is_objc_impl_or_import;
|
||||
if (is_objc_call) {
|
||||
check_objc_call_expr(c, operand, call, proc_entity, pt);
|
||||
}
|
||||
|
||||
return Expr_Expr;
|
||||
}
|
||||
|
||||
|
||||
+14
-1
@@ -1416,6 +1416,8 @@ gb_internal void init_universal(void) {
|
||||
t_objc_SEL = alloc_type_pointer(t_objc_selector);
|
||||
t_objc_Class = alloc_type_pointer(t_objc_class);
|
||||
t_objc_Ivar = alloc_type_pointer(t_objc_ivar);
|
||||
|
||||
t_objc_instancetype = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_instancetype"), t_objc_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1499,9 +1501,12 @@ gb_internal void destroy_checker_info(CheckerInfo *i) {
|
||||
|
||||
map_destroy(&i->objc_msgSend_types);
|
||||
string_set_destroy(&i->obcj_class_name_set);
|
||||
mpsc_destroy(&i->objc_class_implementations);
|
||||
map_destroy(&i->objc_method_implementations);
|
||||
|
||||
// NOTE(harold): Disabling this: It can cause the 'count == 0' assert to trigger
|
||||
// when there's checker errors and the queue is still full as it did not reach the generation stage.
|
||||
// mpsc_destroy(&i->objc_class_implementations);
|
||||
|
||||
string_map_destroy(&i->load_file_cache);
|
||||
string_map_destroy(&i->load_directory_cache);
|
||||
map_destroy(&i->load_directory_map);
|
||||
@@ -3386,12 +3391,20 @@ gb_internal void init_core_map_type(Checker *c) {
|
||||
t_raw_map_ptr = alloc_type_pointer(t_raw_map);
|
||||
}
|
||||
|
||||
gb_internal void init_core_objc_c(Checker *c) {
|
||||
if (build_context.metrics.os == TargetOs_darwin) {
|
||||
t_objc_super = find_core_type(c, str_lit("objc_super"));
|
||||
t_objc_super_ptr = alloc_type_pointer(t_objc_super);
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal void init_preload(Checker *c) {
|
||||
init_core_type_info(c);
|
||||
init_mem_allocator(c);
|
||||
init_core_context(c);
|
||||
init_core_source_code_location(c);
|
||||
init_core_map_type(c);
|
||||
init_core_objc_c(c);
|
||||
}
|
||||
|
||||
gb_internal ExactValue check_decl_attribute_value(CheckerContext *c, Ast *value) {
|
||||
|
||||
@@ -354,6 +354,7 @@ BuiltinProc__type_end,
|
||||
BuiltinProc_objc_register_class,
|
||||
BuiltinProc_objc_ivar_get,
|
||||
BuiltinProc_objc_block,
|
||||
BuiltinProc_objc_super,
|
||||
|
||||
BuiltinProc_constant_utf16_cstring,
|
||||
|
||||
@@ -715,7 +716,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
|
||||
{STR_LIT("objc_register_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
|
||||
{STR_LIT("objc_register_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
|
||||
{STR_LIT("objc_ivar_get"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
|
||||
{STR_LIT("objc_block"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
|
||||
{STR_LIT("objc_block"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("objc_super"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
{STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
|
||||
@@ -251,6 +251,8 @@ struct Entity {
|
||||
String link_name;
|
||||
String link_prefix;
|
||||
String link_suffix;
|
||||
String objc_selector_name;
|
||||
Entity *objc_class;
|
||||
DeferredProcedure deferred_procedure;
|
||||
|
||||
struct GenProcsData *gen_procs;
|
||||
@@ -266,6 +268,8 @@ struct Entity {
|
||||
bool is_anonymous : 1;
|
||||
bool no_sanitize_address : 1;
|
||||
bool no_sanitize_memory : 1;
|
||||
bool is_objc_impl_or_import : 1;
|
||||
bool is_objc_class_method : 1;
|
||||
} Procedure;
|
||||
struct {
|
||||
Array<Entity *> entities;
|
||||
|
||||
+117
-53
@@ -1417,8 +1417,21 @@ String lb_get_objc_type_encoding(Type *t, isize pointer_depth = 0) {
|
||||
return str_lit("?");
|
||||
case Type_Proc:
|
||||
return str_lit("?");
|
||||
case Type_BitSet:
|
||||
return lb_get_objc_type_encoding(t->BitSet.underlying, pointer_depth);
|
||||
case Type_BitSet: {
|
||||
Type *bitset_integer_type = t->BitSet.underlying;
|
||||
if (!bitset_integer_type) {
|
||||
switch (t->cached_size) {
|
||||
case 1: bitset_integer_type = t_u8; break;
|
||||
case 2: bitset_integer_type = t_u16; break;
|
||||
case 4: bitset_integer_type = t_u32; break;
|
||||
case 8: bitset_integer_type = t_u64; break;
|
||||
case 16: bitset_integer_type = t_u128; break;
|
||||
}
|
||||
}
|
||||
GB_ASSERT_MSG(bitset_integer_type, "Could not determine bit_set integer size for objc_type_encoding");
|
||||
|
||||
return lb_get_objc_type_encoding(bitset_integer_type, pointer_depth);
|
||||
}
|
||||
|
||||
case Type_SimdVector: {
|
||||
String type_str = lb_get_objc_type_encoding(t->SimdVector.elem, pointer_depth);
|
||||
@@ -1452,7 +1465,10 @@ String lb_get_objc_type_encoding(Type *t, isize pointer_depth = 0) {
|
||||
|
||||
struct lbObjCGlobalClass {
|
||||
lbObjCGlobal g;
|
||||
lbValue class_value; // Local registered class value
|
||||
union {
|
||||
lbValue class_value; // Local registered class value
|
||||
lbAddr class_global; // Global class pointer. Placeholder for class implementations which are registered in order of definition.
|
||||
};
|
||||
};
|
||||
|
||||
gb_internal void lb_register_objc_thing(
|
||||
@@ -1482,44 +1498,43 @@ gb_internal void lb_register_objc_thing(
|
||||
LLVMSetInitializer(v.value, LLVMConstNull(t));
|
||||
}
|
||||
|
||||
lbValue class_ptr = {};
|
||||
lbValue class_name = lb_const_value(m, t_cstring, exact_value_string(g.name));
|
||||
|
||||
// If this class requires an implementation, save it for registration below.
|
||||
if (g.class_impl_type != nullptr) {
|
||||
|
||||
// Make sure the superclass has been initialized before us
|
||||
lbValue superclass_value = lb_const_nil(m, t_objc_Class);
|
||||
|
||||
auto &tn = g.class_impl_type->Named.type_name->TypeName;
|
||||
Type *superclass = tn.objc_superclass;
|
||||
if (superclass != nullptr) {
|
||||
auto& superclass_global = string_map_must_get(&class_map, superclass->Named.type_name->TypeName.objc_class_name);
|
||||
lb_register_objc_thing(handled, m, args, class_impls, class_map, p, superclass_global.g, call);
|
||||
GB_ASSERT(superclass_global.class_value.value);
|
||||
|
||||
superclass_value = superclass_global.class_value;
|
||||
GB_ASSERT(superclass_global.class_global.addr.value);
|
||||
}
|
||||
|
||||
args.count = 3;
|
||||
args[0] = superclass_value;
|
||||
args[1] = class_name;
|
||||
args[2] = lb_const_int(m, t_uint, 0);
|
||||
class_ptr = lb_emit_runtime_call(p, "objc_allocateClassPair", args);
|
||||
lbObjCGlobalClass impl_global = {};
|
||||
impl_global.g = g;
|
||||
impl_global.class_global = addr;
|
||||
|
||||
array_add(&class_impls, lbObjCGlobalClass{g, class_ptr});
|
||||
array_add(&class_impls, impl_global);
|
||||
|
||||
lbObjCGlobalClass* class_global = string_map_get(&class_map, g.name);
|
||||
if (class_global != nullptr) {
|
||||
class_global->class_global = addr;
|
||||
}
|
||||
}
|
||||
else {
|
||||
lbValue class_ptr = {};
|
||||
lbValue class_name = lb_const_value(m, t_cstring, exact_value_string(g.name));
|
||||
|
||||
args.count = 1;
|
||||
args[0] = class_name;
|
||||
class_ptr = lb_emit_runtime_call(p, call, args);
|
||||
}
|
||||
|
||||
lb_addr_store(p, addr, class_ptr);
|
||||
lb_addr_store(p, addr, class_ptr);
|
||||
|
||||
lbObjCGlobalClass* class_global = string_map_get(&class_map, g.name);
|
||||
if (class_global != nullptr) {
|
||||
class_global->class_value = class_ptr;
|
||||
lbObjCGlobalClass* class_global = string_map_get(&class_map, g.name);
|
||||
if (class_global != nullptr) {
|
||||
class_global->class_value = class_ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1582,7 +1597,7 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
|
||||
string_map_init(&global_class_map, (usize)gen->objc_classes.count);
|
||||
defer (string_map_destroy(&global_class_map));
|
||||
|
||||
for (lbObjCGlobal g :referenced_classes) {
|
||||
for (lbObjCGlobal g : referenced_classes) {
|
||||
string_map_set(&global_class_map, g.name, lbObjCGlobalClass{g});
|
||||
}
|
||||
|
||||
@@ -1629,9 +1644,36 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
|
||||
|
||||
for (const auto &cd : class_impls) {
|
||||
auto &g = cd.g;
|
||||
Type *class_type = g.class_impl_type;
|
||||
|
||||
Type *class_type = g.class_impl_type;
|
||||
Type *class_ptr_type = alloc_type_pointer(class_type);
|
||||
lbValue class_value = cd.class_value;
|
||||
|
||||
// Begin class registration: create class pair and update global reference
|
||||
lbValue class_value = {};
|
||||
|
||||
{
|
||||
lbValue superclass_value = lb_const_nil(m, t_objc_Class);
|
||||
|
||||
auto& tn = class_type->Named.type_name->TypeName;
|
||||
Type *superclass = tn.objc_superclass;
|
||||
|
||||
if (superclass != nullptr) {
|
||||
auto& superclass_global = string_map_must_get(&global_class_map, superclass->Named.type_name->TypeName.objc_class_name);
|
||||
superclass_value = superclass_global.class_value;
|
||||
}
|
||||
|
||||
args.count = 3;
|
||||
args[0] = superclass_value;
|
||||
args[1] = lb_const_value(m, t_cstring, exact_value_string(g.name));
|
||||
args[2] = lb_const_int(m, t_uint, 0);
|
||||
class_value = lb_emit_runtime_call(p, "objc_allocateClassPair", args);
|
||||
|
||||
lbObjCGlobalClass &mapped_global = string_map_must_get(&global_class_map, tn.objc_class_name);
|
||||
lb_addr_store(p, mapped_global.class_global, class_value);
|
||||
|
||||
mapped_global.class_value = class_value;
|
||||
}
|
||||
|
||||
|
||||
Type *ivar_type = class_type->Named.type_name->TypeName.objc_ivar;
|
||||
|
||||
@@ -1651,7 +1693,6 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
|
||||
is_context_provider_ivar = ivar_type != nullptr && internal_check_is_assignable_to(contex_provider_self_named_type, ivar_type);
|
||||
}
|
||||
|
||||
|
||||
Array<ObjcMethodData> *methods = map_get(&m->info->objc_method_implementations, class_type);
|
||||
if (!methods) {
|
||||
continue;
|
||||
@@ -1710,17 +1751,21 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
|
||||
wrapper_results_tuple, method_type->Proc.result_count, false, ProcCC_CDecl);
|
||||
|
||||
lbProcedure *wrapper_proc = lb_create_dummy_procedure(m, proc_name, wrapper_proc_type);
|
||||
lb_add_attribute_to_proc(wrapper_proc->module, wrapper_proc->value, "nounwind");
|
||||
|
||||
lb_add_function_type_attributes(wrapper_proc->value, lb_get_function_type(m, wrapper_proc_type), ProcCC_CDecl);
|
||||
|
||||
// Emit the wrapper
|
||||
LLVMSetLinkage(wrapper_proc->value, LLVMExternalLinkage);
|
||||
// LLVMSetLinkage(wrapper_proc->value, LLVMInternalLinkage);
|
||||
LLVMSetDLLStorageClass(wrapper_proc->value, LLVMDLLExportStorageClass);
|
||||
lb_add_attribute_to_proc(wrapper_proc->module, wrapper_proc->value, "nounwind");
|
||||
|
||||
lb_begin_procedure_body(wrapper_proc);
|
||||
{
|
||||
LLVMValueRef context_addr = nullptr;
|
||||
if (method_type->Proc.calling_convention == ProcCC_Odin) {
|
||||
GB_ASSERT(context_provider);
|
||||
|
||||
// Emit the get odin context call
|
||||
|
||||
get_context_args[0] = lbValue {
|
||||
wrapper_proc->raw_input_parameters[0],
|
||||
contex_provider_self_ptr_type,
|
||||
@@ -1736,44 +1781,58 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
|
||||
get_context_args[0] = lb_handle_objc_ivar_for_objc_object_pointer(wrapper_proc, real_self);
|
||||
}
|
||||
|
||||
lbValue context = lb_emit_call(wrapper_proc, context_provider_proc_value, get_context_args);
|
||||
lbAddr context_addr = lb_addr(lb_address_from_load_or_generate_local(wrapper_proc, context));
|
||||
lb_push_context_onto_stack(wrapper_proc, context_addr);
|
||||
lbValue context = lb_emit_call(wrapper_proc, context_provider_proc_value, get_context_args);
|
||||
context_addr = lb_address_from_load(wrapper_proc, context).value;//lb_address_from_load_or_generate_local(wrapper_proc, context));
|
||||
// context_addr = LLVMGetOperand(context.value, 0);
|
||||
}
|
||||
|
||||
isize method_forward_arg_count = method_param_count + method_param_offset;
|
||||
isize method_forward_return_arg_offset = 0;
|
||||
auto raw_method_args = array_make<LLVMValueRef>(temporary_allocator(), 0, method_forward_arg_count+1);
|
||||
|
||||
auto method_call_args = array_make<lbValue>(temporary_allocator(), method_param_count + method_param_offset);
|
||||
lbValue method_proc_value = lb_find_procedure_value_from_entity(m, md.proc_entity);
|
||||
lbFunctionType* ft = lb_get_function_type(m, method_type);
|
||||
bool has_return = false;
|
||||
lbArgKind return_kind = {};
|
||||
|
||||
if (wrapper_results_tuple != nullptr) {
|
||||
has_return = true;
|
||||
return_kind = ft->ret.kind;
|
||||
|
||||
if (return_kind == lbArg_Indirect) {
|
||||
method_forward_return_arg_offset = 1;
|
||||
array_add(&raw_method_args, wrapper_proc->return_ptr.addr.value);
|
||||
}
|
||||
}
|
||||
|
||||
if (!md.ac.objc_is_class_method) {
|
||||
method_call_args[0] = lbValue {
|
||||
wrapper_proc->raw_input_parameters[0],
|
||||
class_ptr_type,
|
||||
};
|
||||
array_add(&raw_method_args, wrapper_proc->raw_input_parameters[method_forward_return_arg_offset]);
|
||||
}
|
||||
|
||||
for (isize i = 0; i < method_param_count; i++) {
|
||||
method_call_args[i+method_param_offset] = lbValue {
|
||||
wrapper_proc->raw_input_parameters[i+2],
|
||||
method_type->Proc.params->Tuple.variables[i+method_param_offset]->type,
|
||||
};
|
||||
array_add(&raw_method_args, wrapper_proc->raw_input_parameters[i+2+method_forward_return_arg_offset]);
|
||||
}
|
||||
|
||||
if (method_type->Proc.calling_convention == ProcCC_Odin) {
|
||||
array_add(&raw_method_args, context_addr);
|
||||
}
|
||||
lbValue method_proc_value = lb_find_procedure_value_from_entity(m, md.proc_entity);
|
||||
|
||||
// Call real procedure for method from here, passing the parameters expected, if any.
|
||||
lbValue return_value = lb_emit_call(wrapper_proc, method_proc_value, method_call_args);
|
||||
LLVMTypeRef fnp = lb_type_internal_for_procedures_raw(m, method_type);
|
||||
LLVMValueRef ret_val_raw = LLVMBuildCall2(wrapper_proc->builder, fnp, method_proc_value.value, raw_method_args.data, (unsigned)raw_method_args.count, "");
|
||||
|
||||
if (wrapper_results_tuple != nullptr) {
|
||||
auto &result_var = method_type->Proc.results->Tuple.variables[0];
|
||||
return_value = lb_emit_conv(wrapper_proc, return_value, result_var->type);
|
||||
lb_build_return_stmt_internal(wrapper_proc, return_value, result_var->token.pos);
|
||||
if (has_return && return_kind != lbArg_Indirect) {
|
||||
LLVMBuildRet(wrapper_proc->builder, ret_val_raw);
|
||||
}
|
||||
else {
|
||||
LLVMBuildRetVoid(wrapper_proc->builder);
|
||||
}
|
||||
}
|
||||
lb_end_procedure_body(wrapper_proc);
|
||||
|
||||
|
||||
// Add the method to the class
|
||||
String method_encoding = str_lit("v");
|
||||
// TODO (harold): Checker must ensure that objc_methods have a single return value or none!
|
||||
|
||||
GB_ASSERT(method_type->Proc.result_count <= 1);
|
||||
if (method_type->Proc.result_count != 0) {
|
||||
method_encoding = lb_get_objc_type_encoding(method_type->Proc.results->Tuple.variables[0]->type);
|
||||
@@ -1785,8 +1844,8 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
|
||||
method_encoding = concatenate_strings(temporary_allocator(), method_encoding, str_lit("#:"));
|
||||
}
|
||||
|
||||
for (isize i = method_param_offset; i < method_param_count; i++) {
|
||||
Type *param_type = method_type->Proc.params->Tuple.variables[i]->type;
|
||||
for (isize i = 0; i < method_param_count; i++) {
|
||||
Type *param_type = method_type->Proc.params->Tuple.variables[i + method_param_offset]->type;
|
||||
String param_encoding = lb_get_objc_type_encoding(param_type);
|
||||
|
||||
method_encoding = concatenate_strings(temporary_allocator(), method_encoding, param_encoding);
|
||||
@@ -1805,7 +1864,7 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
|
||||
args[2] = lbValue { wrapper_proc->value, wrapper_proc->type };
|
||||
args[3] = lb_const_value(m, t_cstring, exact_value_string(method_encoding));
|
||||
|
||||
// TODO(harold): Emit check BOOL result and panic if false.
|
||||
// TODO(harold): Emit check BOOL result and panic if false?
|
||||
lb_emit_runtime_call(p, "class_addMethod", args);
|
||||
|
||||
} // End methods
|
||||
@@ -1853,7 +1912,7 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
|
||||
// Defined in an external package, define it now in the main package
|
||||
LLVMTypeRef t = lb_type(m, t_int);
|
||||
|
||||
lbValue global{};
|
||||
lbValue global = {};
|
||||
global.value = LLVMAddGlobal(m->mod, t, g.global_name);
|
||||
global.type = t_int_ptr;
|
||||
|
||||
@@ -2193,6 +2252,11 @@ gb_internal void lb_create_global_procedures_and_types(lbGenerator *gen, Checker
|
||||
GB_ASSERT(m != nullptr);
|
||||
|
||||
if (e->kind == Entity_Procedure) {
|
||||
if (e->Procedure.is_foreign && e->Procedure.is_objc_impl_or_import) {
|
||||
// Do not generate declarations for foreign Objective-C methods. These are called indirectly through the Objective-C runtime.
|
||||
continue;
|
||||
}
|
||||
|
||||
array_add(&m->global_procedures_to_create, e);
|
||||
} else if (e->kind == Entity_TypeName) {
|
||||
array_add(&m->global_types_to_create, e);
|
||||
|
||||
@@ -206,7 +206,7 @@ struct lbModule {
|
||||
StringMap<lbAddr> objc_classes;
|
||||
StringMap<lbAddr> objc_selectors;
|
||||
StringMap<lbAddr> objc_ivars;
|
||||
isize objc_next_block_id; // Used to name objective-c blocks, per module
|
||||
isize objc_next_block_id; // Used to name objective-c blocks. Tracked per module.
|
||||
|
||||
PtrMap<u64/*type hash*/, lbAddr> map_cell_info_map; // address of runtime.Map_Info
|
||||
PtrMap<u64/*type hash*/, lbAddr> map_info_map; // address of runtime.Map_Cell_Info
|
||||
|
||||
@@ -3753,6 +3753,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
||||
case BuiltinProc_objc_register_class: return lb_handle_objc_register_class(p, expr);
|
||||
case BuiltinProc_objc_ivar_get: return lb_handle_objc_ivar_get(p, expr);
|
||||
case BuiltinProc_objc_block: return lb_handle_objc_block(p, expr);
|
||||
case BuiltinProc_objc_super: return lb_handle_objc_super(p, expr);
|
||||
|
||||
|
||||
case BuiltinProc_constant_utf16_cstring:
|
||||
@@ -4122,21 +4123,23 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
|
||||
}
|
||||
|
||||
Ast *proc_expr = unparen_expr(ce->proc);
|
||||
Entity *proc_entity = entity_of_node(proc_expr);
|
||||
|
||||
if (proc_mode == Addressing_Builtin) {
|
||||
Entity *e = entity_of_node(proc_expr);
|
||||
BuiltinProcId id = BuiltinProc_Invalid;
|
||||
if (e != nullptr) {
|
||||
id = cast(BuiltinProcId)e->Builtin.id;
|
||||
if (proc_entity != nullptr) {
|
||||
id = cast(BuiltinProcId)proc_entity->Builtin.id;
|
||||
} else {
|
||||
id = BuiltinProc_DIRECTIVE;
|
||||
}
|
||||
return lb_build_builtin_proc(p, expr, tv, id);
|
||||
}
|
||||
|
||||
bool is_objc_call = proc_entity && proc_entity->Procedure.is_objc_impl_or_import;
|
||||
|
||||
// NOTE(bill): Regular call
|
||||
lbValue value = {};
|
||||
|
||||
Entity *proc_entity = entity_of_node(proc_expr);
|
||||
if (proc_entity != nullptr) {
|
||||
if (proc_entity->flags & EntityFlag_Disabled) {
|
||||
GB_ASSERT(tv.type == nullptr);
|
||||
@@ -4170,11 +4173,13 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
|
||||
}
|
||||
}
|
||||
|
||||
if (value.value == nullptr) {
|
||||
if (is_objc_call) {
|
||||
value.type = proc_tv.type;
|
||||
} else if (value.value == nullptr) {
|
||||
value = lb_build_expr(p, proc_expr);
|
||||
}
|
||||
|
||||
GB_ASSERT(value.value != nullptr);
|
||||
GB_ASSERT(value.value != nullptr || is_objc_call);
|
||||
Type *proc_type_ = base_type(value.type);
|
||||
GB_ASSERT(proc_type_->kind == Type_Proc);
|
||||
TypeProc *pt = &proc_type_->Proc;
|
||||
@@ -4402,6 +4407,11 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
|
||||
|
||||
isize final_count = is_c_vararg ? args.count : pt->param_count;
|
||||
auto call_args = array_slice(args, 0, final_count);
|
||||
|
||||
if (is_objc_call) {
|
||||
return lb_handle_objc_auto_send(p, expr, slice(call_args, 0, call_args.count));
|
||||
}
|
||||
|
||||
return lb_emit_call(p, value, call_args, ce->inlining);
|
||||
}
|
||||
|
||||
|
||||
+203
-48
@@ -286,7 +286,14 @@ gb_internal lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t) {
|
||||
}
|
||||
}
|
||||
|
||||
bool is_simd_vector_bitcastable = false;
|
||||
if (is_type_simd_vector(src) && is_type_simd_vector(dst)) {
|
||||
if (!is_type_internally_pointer_like(src->SimdVector.elem) && !is_type_internally_pointer_like(dst->SimdVector.elem)) {
|
||||
is_simd_vector_bitcastable = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_simd_vector_bitcastable) {
|
||||
res.value = LLVMBuildBitCast(p->builder, value.value, lb_type(p->module, t), "");
|
||||
return res;
|
||||
} else if (is_type_array_like(src) && (is_type_simd_vector(dst) || is_type_integer_128bit(dst))) {
|
||||
@@ -2264,12 +2271,12 @@ gb_internal lbValue lb_handle_objc_ivar_get(lbProcedure *p, Ast *expr) {
|
||||
}
|
||||
|
||||
gb_internal void lb_create_objc_block_helper_procs(
|
||||
lbModule *m, LLVMTypeRef block_lit_type, isize capture_field_offset,
|
||||
lbModule *m, LLVMTypeRef block_lit_type, isize capture_field_offset, isize block_id,
|
||||
Slice<lbValue> capture_values, Slice<isize> objc_object_indices,
|
||||
lbProcedure *&out_copy_helper, lbProcedure *&out_dispose_helper
|
||||
) {
|
||||
gbString copy_helper_name = gb_string_append_fmt(gb_string_make(temporary_allocator(), ""), "__$objc_block_copy_helper_%lld", m->objc_next_block_id);
|
||||
gbString dispose_helper_name = gb_string_append_fmt(gb_string_make(temporary_allocator(), ""), "__$objc_block_dispose_helper_%lld", m->objc_next_block_id);
|
||||
gbString copy_helper_name = gb_string_append_fmt(gb_string_make(temporary_allocator(), ""), "__$%s::objc_block_copy_helper_%lld", m->module_name, block_id);
|
||||
gbString dispose_helper_name = gb_string_append_fmt(gb_string_make(temporary_allocator(), ""), "__$%s::objc_block_dispose_helper_%lld", m->module_name, block_id);
|
||||
|
||||
// copy: Block_Literal *dst, Block_Literal *src, i32 field_apropos
|
||||
// dispose: Block_Literal *src, i32 field_apropos
|
||||
@@ -2373,14 +2380,12 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
|
||||
/// https://www.newosxbook.com/src.php?tree=xnu&file=/libkern/libkern/Block_private.h
|
||||
/// https://github.com/llvm/llvm-project/blob/21f1f9558df3830ffa637def364e3c0cb0dbb3c0/compiler-rt/lib/BlocksRuntime/Block_private.h
|
||||
/// https://github.com/apple-oss-distributions/libclosure/blob/3668b0837f47be3cc1c404fb5e360f4ff178ca13/runtime.cpp
|
||||
|
||||
ast_node(ce, CallExpr, expr);
|
||||
GB_ASSERT(ce->args.count > 0);
|
||||
|
||||
lbModule *m = p->module;
|
||||
|
||||
m->objc_next_block_id += 1;
|
||||
|
||||
const isize block_id = m->objc_next_block_id++;
|
||||
const isize capture_arg_count = ce->args.count - 1;
|
||||
|
||||
Type *block_result_type = type_of_expr(expr);
|
||||
@@ -2425,7 +2430,7 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
|
||||
|
||||
// Create proc with the block signature
|
||||
// (takes a block literal pointer as the first parameter, followed by any expected ones from the user's proc)
|
||||
gbString block_invoker_name = gb_string_append_fmt(gb_string_make(permanent_allocator(), ""), "__$objc_block_invoker_%lld", m->objc_next_block_id);
|
||||
gbString block_invoker_name = gb_string_append_fmt(gb_string_make(permanent_allocator(), ""), "__$%s::objc_block_invoker_%lld", m->module_name, block_id);
|
||||
|
||||
// Add + 1 because the first parameter received is the block literal pointer itself
|
||||
auto invoker_args = array_make<Type *>(temporary_allocator(), block_forward_args + 1, block_forward_args + 1);
|
||||
@@ -2452,14 +2457,16 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
|
||||
|
||||
lbProcedure *invoker_proc = lb_create_dummy_procedure(m, make_string((u8*)block_invoker_name,
|
||||
gb_string_length(block_invoker_name)), invoker_proc_type);
|
||||
|
||||
LLVMSetLinkage(invoker_proc->value, LLVMPrivateLinkage);
|
||||
lb_add_function_type_attributes(invoker_proc->value, lb_get_function_type(m, invoker_proc_type), ProcCC_CDecl);
|
||||
|
||||
// Create the block descriptor and block literal
|
||||
gbString block_lit_type_name = gb_string_make(temporary_allocator(), "__$ObjC_Block_Literal_");
|
||||
block_lit_type_name = gb_string_append_fmt(block_lit_type_name, "%lld", m->objc_next_block_id);
|
||||
gbString block_lit_type_name = gb_string_make(temporary_allocator(), "");
|
||||
block_lit_type_name = gb_string_append_fmt(block_lit_type_name, "__$%s::ObjC_Block_Literal_%lld", m->module_name, block_id);
|
||||
|
||||
gbString block_desc_type_name = gb_string_make(temporary_allocator(), "__$ObjC_Block_Descriptor_");
|
||||
block_desc_type_name = gb_string_append_fmt(block_desc_type_name, "%lld", m->objc_next_block_id);
|
||||
gbString block_desc_type_name = gb_string_make(temporary_allocator(), "");
|
||||
block_desc_type_name = gb_string_append_fmt(block_desc_type_name, "__$%s::ObjC_Block_Descriptor_%lld", m->module_name,block_id);
|
||||
|
||||
LLVMTypeRef block_lit_type = {};
|
||||
LLVMTypeRef block_desc_type = {};
|
||||
@@ -2503,7 +2510,7 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
|
||||
|
||||
// Generate copy and dispose helper functions for captured params that are Objective-C objects (or a Block)
|
||||
if (has_objc_fields) {
|
||||
lb_create_objc_block_helper_procs(m, block_lit_type, capture_fields_offset,
|
||||
lb_create_objc_block_helper_procs(m, block_lit_type, capture_fields_offset, block_id,
|
||||
slice(captured_values, 0, captured_values.count),
|
||||
slice(objc_captures, 0, objc_captures.count),
|
||||
copy_helper, dispose_helper);
|
||||
@@ -2521,8 +2528,8 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
|
||||
}
|
||||
|
||||
// Create global block descriptor
|
||||
gbString desc_global_name = gb_string_make(temporary_allocator(), "__$objc_block_desc_");
|
||||
desc_global_name = gb_string_append_fmt(desc_global_name, "%lld", m->objc_next_block_id);
|
||||
gbString desc_global_name = gb_string_make(temporary_allocator(), "");
|
||||
desc_global_name = gb_string_append_fmt(desc_global_name, "__$%s::objc_block_desc_%lld", m->module_name, block_id);
|
||||
|
||||
LLVMValueRef p_descriptor = LLVMAddGlobal(m->mod, block_desc_type, desc_global_name);
|
||||
LLVMSetInitializer(p_descriptor, block_desc_initializer);
|
||||
@@ -2531,45 +2538,66 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
|
||||
/// Invoker body
|
||||
lb_begin_procedure_body(invoker_proc);
|
||||
{
|
||||
auto call_args = array_make<lbValue>(temporary_allocator(), user_proc.param_count, user_proc.param_count);
|
||||
// Reserve 2 extra arguments for: Indirect return values and context.
|
||||
auto call_args = array_make<LLVMValueRef>(temporary_allocator(), 0, user_proc.param_count + 2);
|
||||
|
||||
for (isize i = 1; i < invoker_proc->raw_input_parameters.count; i++) {
|
||||
lbValue arg = {};
|
||||
arg.type = invoker_args[i];
|
||||
arg.value = invoker_proc->raw_input_parameters[i],
|
||||
call_args[i-1] = arg;
|
||||
isize block_literal_arg_index = 0;
|
||||
|
||||
lbFunctionType* user_proc_ft = lb_get_function_type(m, user_proc_value.type);
|
||||
|
||||
lbArgKind return_kind = {};
|
||||
|
||||
GB_ASSERT(user_proc.result_count <= 1);
|
||||
if (user_proc.result_count > 0) {
|
||||
return_kind = user_proc_ft->ret.kind;
|
||||
|
||||
if (return_kind == lbArg_Indirect) {
|
||||
// Forward indirect return value
|
||||
array_add(&call_args, invoker_proc->raw_input_parameters[0]);
|
||||
block_literal_arg_index = 1;
|
||||
}
|
||||
}
|
||||
|
||||
LLVMValueRef block_literal = invoker_proc->raw_input_parameters[0];
|
||||
// Forward raw arguments
|
||||
for (isize i = block_literal_arg_index+1; i < invoker_proc->raw_input_parameters.count; i++) {
|
||||
array_add(&call_args, invoker_proc->raw_input_parameters[i]);
|
||||
}
|
||||
|
||||
LLVMValueRef block_literal = invoker_proc->raw_input_parameters[block_literal_arg_index];
|
||||
|
||||
// Copy capture parameters from the block literal
|
||||
isize capture_arg_in_user_proc_start_index = user_proc_ft->args.count - capture_arg_count;
|
||||
if (user_proc.calling_convention == ProcCC_Odin) {
|
||||
capture_arg_in_user_proc_start_index -= 1;
|
||||
}
|
||||
|
||||
for (isize i = 0; i < capture_arg_count; i++) {
|
||||
LLVMValueRef cap_value = LLVMBuildStructGEP2(invoker_proc->builder, block_lit_type, block_literal, unsigned(capture_fields_offset + i), "");
|
||||
|
||||
// Don't emit load if indirect. Pass the pointer as-is
|
||||
isize cap_arg_index_in_user_proc = capture_arg_in_user_proc_start_index + i;
|
||||
|
||||
if (user_proc_ft->args[cap_arg_index_in_user_proc].kind != lbArg_Indirect) {
|
||||
cap_value = OdinLLVMBuildLoad(invoker_proc, lb_type(invoker_proc->module, captured_values[i].type), cap_value);
|
||||
}
|
||||
|
||||
array_add(&call_args, cap_value);
|
||||
}
|
||||
|
||||
// Push context, if needed
|
||||
if (user_proc.calling_convention == ProcCC_Odin) {
|
||||
LLVMValueRef p_context = LLVMBuildStructGEP2(invoker_proc->builder, block_lit_type, block_literal, 5, "context");
|
||||
lbValue ctx_val = {};
|
||||
ctx_val.type = t_context_ptr;
|
||||
ctx_val.value = p_context;
|
||||
|
||||
lb_push_context_onto_stack(invoker_proc, lb_addr(ctx_val));
|
||||
array_add(&call_args, p_context);
|
||||
}
|
||||
|
||||
// Copy capture parameters from the block literal
|
||||
for (isize i = 0; i < capture_arg_count; i++) {
|
||||
LLVMValueRef cap_value = LLVMBuildStructGEP2(invoker_proc->builder, block_lit_type, block_literal, unsigned(capture_fields_offset + i), "");
|
||||
LLVMTypeRef fnp = lb_type_internal_for_procedures_raw(m, user_proc_value.type);
|
||||
LLVMValueRef ret_val = LLVMBuildCall2(invoker_proc->builder, fnp, user_proc_value.value, call_args.data, (unsigned)call_args.count, "");
|
||||
|
||||
lbValue cap_arg = {};
|
||||
cap_arg.value = cap_value;
|
||||
cap_arg.type = alloc_type_pointer(captured_values[i].type);
|
||||
|
||||
lbValue arg = lb_emit_load(invoker_proc, cap_arg);
|
||||
call_args[block_forward_args+i] = arg;
|
||||
if (user_proc.result_count > 0 && return_kind != lbArg_Indirect) {
|
||||
LLVMBuildRet(invoker_proc->builder, ret_val);
|
||||
}
|
||||
|
||||
lbValue result = lb_emit_call(invoker_proc, user_proc_value, call_args, proc_lit->ProcLit.inlining);
|
||||
|
||||
GB_ASSERT(user_proc.result_count <= 1);
|
||||
if (user_proc.result_count > 0) {
|
||||
GB_ASSERT(result.value != nullptr);
|
||||
LLVMBuildRet(p->builder, result.value);
|
||||
else {
|
||||
LLVMBuildRetVoid(invoker_proc->builder);
|
||||
}
|
||||
}
|
||||
lb_end_procedure_body(invoker_proc);
|
||||
@@ -2585,10 +2613,10 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
|
||||
}
|
||||
|
||||
gbString block_var_name = gb_string_make(temporary_allocator(), "__$objc_block_literal_");
|
||||
block_var_name = gb_string_append_fmt(block_var_name, "%lld", m->objc_next_block_id);
|
||||
block_var_name = gb_string_append_fmt(block_var_name, "%lld", block_id);
|
||||
|
||||
lbValue result = {};
|
||||
result.type = block_result_type;
|
||||
lbValue block_result = {};
|
||||
block_result.type = block_result_type;
|
||||
|
||||
lbValue isa_val = lb_find_runtime_value(m, is_global ? str_lit("_NSConcreteGlobalBlock") : str_lit("_NSConcreteStackBlock"));
|
||||
lbValue flags_val = lb_const_int(m, t_i32, (u64)raw_flags);
|
||||
@@ -2596,7 +2624,7 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
|
||||
|
||||
if (is_global) {
|
||||
LLVMValueRef p_block_lit = LLVMAddGlobal(m->mod, block_lit_type, block_var_name);
|
||||
result.value = p_block_lit;
|
||||
block_result.value = p_block_lit;
|
||||
|
||||
LLVMValueRef fields_values[5] = {
|
||||
isa_val.value, // isa
|
||||
@@ -2611,7 +2639,7 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
|
||||
|
||||
} else {
|
||||
LLVMValueRef p_block_lit = llvm_alloca(p, block_lit_type, lb_alignof(block_lit_type), block_var_name);
|
||||
result.value = p_block_lit;
|
||||
block_result.value = p_block_lit;
|
||||
|
||||
// Initialize it
|
||||
LLVMValueRef f_isa = LLVMBuildStructGEP2(p->builder, block_lit_type, p_block_lit, 0, "isa");
|
||||
@@ -2651,7 +2679,20 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return block_result;
|
||||
}
|
||||
|
||||
gb_internal lbValue lb_handle_objc_block_invoke(lbProcedure *p, Ast *expr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
gb_internal lbValue lb_handle_objc_super(lbProcedure *p, Ast *expr) {
|
||||
ast_node(ce, CallExpr, expr);
|
||||
GB_ASSERT(ce->args.count == 1);
|
||||
|
||||
// NOTE(harold): This doesn't actually do anything by itself,
|
||||
// it has an effect when used on the left side of a selector call expression.
|
||||
return lb_build_expr(p, ce->args[0]);
|
||||
}
|
||||
|
||||
gb_internal lbValue lb_handle_objc_find_selector(lbProcedure *p, Ast *expr) {
|
||||
@@ -2767,6 +2808,120 @@ gb_internal lbValue lb_handle_objc_send(lbProcedure *p, Ast *expr) {
|
||||
return lb_emit_call(p, the_proc, args);
|
||||
}
|
||||
|
||||
gb_internal lbValue lb_handle_objc_auto_send(lbProcedure *p, Ast *expr, Slice<lbValue> const arg_values) {
|
||||
ast_node(ce, CallExpr, expr);
|
||||
|
||||
lbModule *m = p->module;
|
||||
CheckerInfo *info = m->info;
|
||||
ObjcMsgData data = map_must_get(&info->objc_msgSend_types, expr);
|
||||
|
||||
Type *proc_type = data.proc_type;
|
||||
GB_ASSERT(proc_type != nullptr);
|
||||
|
||||
Entity *objc_method_ent = entity_of_node(ce->proc);
|
||||
GB_ASSERT(objc_method_ent != nullptr);
|
||||
GB_ASSERT(objc_method_ent->kind == Entity_Procedure);
|
||||
GB_ASSERT(objc_method_ent->Procedure.objc_selector_name.len > 0);
|
||||
|
||||
auto &proc = proc_type->Proc;
|
||||
GB_ASSERT(proc.param_count >= 2);
|
||||
|
||||
Type *objc_super_orig_type = nullptr;
|
||||
if (ce->args.count > 0) {
|
||||
objc_super_orig_type = unparen_expr(ce->args[0])->tav.objc_super_target;
|
||||
}
|
||||
|
||||
isize arg_offset = 1;
|
||||
lbValue id = {};
|
||||
if (!objc_method_ent->Procedure.is_objc_class_method) {
|
||||
GB_ASSERT(ce->args.count > 0);
|
||||
id = arg_values[0];
|
||||
|
||||
if (objc_super_orig_type) {
|
||||
GB_ASSERT(objc_super_orig_type->kind == Type_Named);
|
||||
|
||||
auto& tn = objc_super_orig_type->Named.type_name->TypeName;
|
||||
lbAddr p_supercls = lb_handle_objc_find_or_register_class(p, tn.objc_class_name, tn.objc_is_implementation ? objc_super_orig_type : nullptr);
|
||||
|
||||
lbValue supercls = lb_addr_load(p, p_supercls);
|
||||
lbAddr p_objc_super = lb_add_local_generated(p, t_objc_super, false);
|
||||
|
||||
lbValue f_id = lb_emit_struct_ep(p, p_objc_super.addr, 0);
|
||||
lbValue f_superclass = lb_emit_struct_ep(p, p_objc_super.addr, 1);
|
||||
|
||||
id = lb_emit_conv(p, id, t_objc_id);
|
||||
lb_emit_store(p, f_id, id);
|
||||
lb_emit_store(p, f_superclass, supercls);
|
||||
|
||||
id = p_objc_super.addr;
|
||||
}
|
||||
} else {
|
||||
Entity *objc_class = objc_method_ent->Procedure.objc_class;
|
||||
if (ce->proc->kind == Ast_SelectorExpr) {
|
||||
// NOTE (harold): If called via a selector expression (ex: Foo.alloc()), then we should use
|
||||
// the lhs-side to determine the class. This allows for class methods to be called
|
||||
// with the correct class as the target, even when the method is defined in a superclass.
|
||||
ast_node(se, SelectorExpr, ce->proc);
|
||||
GB_ASSERT(se->expr->tav.mode == Addressing_Type && se->expr->tav.type->kind == Type_Named);
|
||||
|
||||
objc_class = entity_from_expr(se->expr);
|
||||
|
||||
GB_ASSERT(objc_class);
|
||||
GB_ASSERT(objc_class->kind == Entity_TypeName);
|
||||
GB_ASSERT(objc_class->TypeName.objc_class_name != "");
|
||||
}
|
||||
|
||||
Type *class_impl_type = objc_class->TypeName.objc_is_implementation ? objc_class->type : nullptr;
|
||||
|
||||
id = lb_addr_load(p, lb_handle_objc_find_or_register_class(p, objc_class->TypeName.objc_class_name, class_impl_type));
|
||||
arg_offset = 0;
|
||||
}
|
||||
|
||||
lbValue sel = lb_addr_load(p, lb_handle_objc_find_or_register_selector(p, objc_method_ent->Procedure.objc_selector_name));
|
||||
|
||||
auto args = array_make<lbValue>(permanent_allocator(), 0, arg_values.count + 2 - arg_offset);
|
||||
|
||||
array_add(&args, id);
|
||||
array_add(&args, sel);
|
||||
|
||||
for (isize i = arg_offset; i < ce->args.count; i++) {
|
||||
array_add(&args, arg_values[i]);
|
||||
}
|
||||
|
||||
lbValue the_proc = {};
|
||||
|
||||
if (!objc_super_orig_type) {
|
||||
switch (data.kind) {
|
||||
default:
|
||||
GB_PANIC("unhandled ObjcMsgKind %u", data.kind);
|
||||
break;
|
||||
case ObjcMsg_normal: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend")); break;
|
||||
case ObjcMsg_fpret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_fpret")); break;
|
||||
case ObjcMsg_fp2ret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_fp2ret")); break;
|
||||
case ObjcMsg_stret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_stret")); break;
|
||||
}
|
||||
} else {
|
||||
switch (data.kind) {
|
||||
default:
|
||||
GB_PANIC("unhandled ObjcMsgKind %u", data.kind);
|
||||
break;
|
||||
case ObjcMsg_normal:
|
||||
case ObjcMsg_fpret:
|
||||
case ObjcMsg_fp2ret:
|
||||
the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSendSuper2"));
|
||||
break;
|
||||
case ObjcMsg_stret:
|
||||
the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSendSuper2_stret"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
the_proc = lb_emit_conv(p, the_proc, data.proc_type);
|
||||
|
||||
return lb_emit_call(p, the_proc, args);
|
||||
}
|
||||
|
||||
|
||||
gb_internal LLVMAtomicOrdering llvm_atomic_ordering_from_odin(ExactValue const &value) {
|
||||
GB_ASSERT(value.kind == ExactValue_Integer);
|
||||
i64 v = exact_value_to_i64(value);
|
||||
|
||||
+2
-1
@@ -48,7 +48,8 @@ gb_global String const addressing_mode_strings[] = {
|
||||
struct TypeAndValue {
|
||||
Type * type;
|
||||
AddressingMode mode;
|
||||
bool is_lhs; // Debug info
|
||||
bool is_lhs; // Debug info
|
||||
Type * objc_super_target; // Original type of the Obj-C object before being converted to the superclass' type by the objc_super() intrinsic.
|
||||
ExactValue value;
|
||||
};
|
||||
|
||||
|
||||
@@ -752,11 +752,14 @@ gb_global Type *t_objc_object = nullptr;
|
||||
gb_global Type *t_objc_selector = nullptr;
|
||||
gb_global Type *t_objc_class = nullptr;
|
||||
gb_global Type *t_objc_ivar = nullptr;
|
||||
gb_global Type *t_objc_super = nullptr; // Struct used in lieu of the 'self' instance when calling objc_msgSendSuper.
|
||||
gb_global Type *t_objc_super_ptr = nullptr;
|
||||
|
||||
gb_global Type *t_objc_id = nullptr;
|
||||
gb_global Type *t_objc_SEL = nullptr;
|
||||
gb_global Type *t_objc_Class = nullptr;
|
||||
gb_global Type *t_objc_Ivar = nullptr;
|
||||
gb_global Type *t_objc_instancetype = nullptr; // Special distinct variant of t_objc_id used mimic auto-typing of instancetype* in Objective-C
|
||||
|
||||
enum OdinAtomicMemoryOrder : i32 {
|
||||
OdinAtomicMemoryOrder_relaxed = 0, // unordered
|
||||
@@ -4735,6 +4738,14 @@ gb_internal bool is_type_objc_object(Type *t) {
|
||||
return internal_check_is_assignable_to(t, t_objc_object);
|
||||
}
|
||||
|
||||
gb_internal bool is_type_objc_ptr_to_object(Type *t) {
|
||||
// NOTE (harold): is_type_objc_object() returns true if it's a pointer to an object or the object itself.
|
||||
// This returns true ONLY if Type is a shallow pointer to an Objective-C object.
|
||||
|
||||
Type *elem = type_deref(t);
|
||||
return elem != t && elem->kind == Type_Named && is_type_objc_object(elem);
|
||||
}
|
||||
|
||||
gb_internal Type *get_struct_field_type(Type *t, isize index) {
|
||||
t = base_type(type_deref(t));
|
||||
GB_ASSERT(t->kind == Type_Struct);
|
||||
|
||||
Reference in New Issue
Block a user