mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 15:48:51 +00:00
Merge remote-tracking branch 'upstream/master' into llvm-14-fixes
This commit is contained in:
@@ -124,13 +124,13 @@ jobs:
|
|||||||
build_macos:
|
build_macos:
|
||||||
name: MacOS Build
|
name: MacOS Build
|
||||||
if: github.repository == 'odin-lang/Odin'
|
if: github.repository == 'odin-lang/Odin'
|
||||||
runs-on: macos-13
|
runs-on: macos-14 # Intel machine
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Download LLVM and setup PATH
|
- name: Download LLVM and setup PATH
|
||||||
run: |
|
run: |
|
||||||
brew update
|
brew update
|
||||||
brew install llvm@20 dylibbundler lld
|
brew install llvm@20 dylibbundler lld@20
|
||||||
|
|
||||||
- name: build odin
|
- name: build odin
|
||||||
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
|
# 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
|
- name: Download LLVM and setup PATH
|
||||||
run: |
|
run: |
|
||||||
brew update
|
brew update
|
||||||
brew install llvm@20 dylibbundler lld
|
brew install llvm@20 dylibbundler lld@20
|
||||||
|
|
||||||
- name: build odin
|
- name: build odin
|
||||||
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
|
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
|
||||||
|
|||||||
@@ -302,3 +302,9 @@ misc/featuregen/featuregen
|
|||||||
.cache/
|
.cache/
|
||||||
.clangd
|
.clangd
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
|
|
||||||
|
# Dev cmake helpers
|
||||||
|
build/
|
||||||
|
cmake-build*/
|
||||||
|
CMakeLists.txt
|
||||||
|
sandbox/
|
||||||
|
|||||||
@@ -378,6 +378,7 @@ objc_id :: ^objc_object
|
|||||||
objc_SEL :: ^objc_selector
|
objc_SEL :: ^objc_selector
|
||||||
objc_Class :: ^objc_class
|
objc_Class :: ^objc_class
|
||||||
objc_Ivar :: ^objc_ivar
|
objc_Ivar :: ^objc_ivar
|
||||||
|
objc_instancetype :: distinct objc_id
|
||||||
|
|
||||||
objc_find_selector :: proc($name: string) -> objc_SEL ---
|
objc_find_selector :: proc($name: string) -> objc_SEL ---
|
||||||
objc_register_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_register_class :: proc($name: string) -> objc_Class ---
|
||||||
objc_ivar_get :: proc(self: ^$T) -> ^$U ---
|
objc_ivar_get :: proc(self: ^$T) -> ^$U ---
|
||||||
objc_block :: proc(invoke: $T, ..any) -> ^Objc_Block(T) where type_is_proc(T) ---
|
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 ---
|
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 {
|
type_info_base :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
|
||||||
if info == nil {
|
if info == nil {
|
||||||
return 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 {
|
type_info_core :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
|
||||||
if info == nil {
|
if info == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -668,6 +674,10 @@ type_info_core :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
|
|||||||
}
|
}
|
||||||
return base
|
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_base_without_enum :: type_info_core
|
||||||
|
|
||||||
__type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info #no_bounds_check {
|
__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 {
|
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 {
|
typeid_base :: proc "contextless" (id: typeid) -> typeid {
|
||||||
ti := type_info_of(id)
|
ti := type_info_of(id)
|
||||||
ti = type_info_base(ti)
|
ti = type_info_base(ti)
|
||||||
return ti.id
|
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 {
|
typeid_core :: proc "contextless" (id: typeid) -> typeid {
|
||||||
ti := type_info_core(type_info_of(id))
|
ti := type_info_core(type_info_of(id))
|
||||||
return ti.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
|
typeid_base_without_enum :: typeid_core
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -708,11 +726,15 @@ default_logger_proc :: proc(data: rawptr, level: Logger_Level, text: string, opt
|
|||||||
// Nothing
|
// Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the default logger used by `context.logger`
|
||||||
|
@(require_results)
|
||||||
default_logger :: proc() -> Logger {
|
default_logger :: proc() -> Logger {
|
||||||
return Logger{default_logger_proc, nil, Logger_Level.Debug, nil}
|
return Logger{default_logger_proc, nil, Logger_Level.Debug, nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Returns the default `context`
|
||||||
|
@(require_results)
|
||||||
default_context :: proc "contextless" () -> Context {
|
default_context :: proc "contextless" () -> Context {
|
||||||
c: Context
|
c: Context
|
||||||
__init_context(&c)
|
__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)
|
@(builtin, disabled=NO_DEFAULT_TEMP_ALLOCATOR)
|
||||||
init_global_temporary_allocator :: proc(size: int, backup_allocator := context.allocator) {
|
init_global_temporary_allocator :: proc(size: int, backup_allocator := context.allocator) {
|
||||||
when !NO_DEFAULT_TEMP_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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `append_elem` appends an element to the end of a dynamic array.
|
||||||
@builtin
|
@builtin
|
||||||
append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
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 {
|
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
|
@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 {
|
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 {
|
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
|
return arg_len, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `append_elems` appends `args` to the end of a dynamic array.
|
||||||
|
//
|
||||||
|
// Note: Prefer using the procedure group `append`.
|
||||||
@builtin
|
@builtin
|
||||||
append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
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 {
|
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
|
@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 {
|
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 {
|
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))
|
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
|
@builtin
|
||||||
append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
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)
|
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
|
@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 {
|
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)
|
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
|
// 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
|
@builtin
|
||||||
append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
n_arg: int
|
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
|
// The append built-in procedure appends elements to the end of a dynamic array
|
||||||
@builtin append :: proc{
|
@builtin
|
||||||
|
append :: proc{
|
||||||
append_elem,
|
append_elem,
|
||||||
append_elems,
|
append_elems,
|
||||||
append_elem_string,
|
append_elem_string,
|
||||||
@@ -674,7 +695,8 @@ append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_
|
|||||||
append_soa_elems,
|
append_soa_elems,
|
||||||
}
|
}
|
||||||
|
|
||||||
@builtin non_zero_append :: proc{
|
@builtin
|
||||||
|
non_zero_append :: proc{
|
||||||
non_zero_append_elem,
|
non_zero_append_elem,
|
||||||
non_zero_append_elems,
|
non_zero_append_elems,
|
||||||
non_zero_append_elem_string,
|
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
|
@builtin
|
||||||
append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
if array == nil {
|
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
|
@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 {
|
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 {
|
when !ODIN_NO_BOUNDS_CHECK {
|
||||||
@@ -716,6 +741,7 @@ inject_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcas
|
|||||||
return
|
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
|
@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 {
|
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 {
|
when !ODIN_NO_BOUNDS_CHECK {
|
||||||
@@ -742,6 +768,7 @@ inject_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadca
|
|||||||
return
|
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
|
@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 {
|
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 {
|
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
|
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}
|
@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
|
@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 {
|
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) {
|
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
|
@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 {
|
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)
|
new_size := index + len(args)
|
||||||
@@ -800,7 +832,8 @@ assign_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadca
|
|||||||
return
|
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
|
@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 {
|
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)
|
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
|
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`).
|
// `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`.
|
// 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 {
|
_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 {
|
if a == nil {
|
||||||
@@ -868,11 +910,21 @@ _reserve_dynamic_array :: #force_no_inline proc(a: ^Raw_Dynamic_Array, size_of_e
|
|||||||
return nil
|
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
|
@builtin
|
||||||
reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
|
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)
|
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
|
@builtin
|
||||||
non_zero_reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
|
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)
|
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`).
|
// `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`
|
// Note: Prefer the procedure group `resize`
|
||||||
@builtin
|
@builtin
|
||||||
resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error {
|
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)
|
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
|
@builtin
|
||||||
non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error {
|
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)
|
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.
|
||||||
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.
|
||||||
If `new_cap` is negative, then `len(array)` is used.
|
//
|
||||||
|
// Returns false if `cap(array) < new_cap`, or the allocator report failure.
|
||||||
Returns false if `cap(array) < new_cap`, or the allocator report failure.
|
//
|
||||||
|
// If `len(array) < new_cap`, then `len(array)` will be left unchanged.
|
||||||
If `len(array) < new_cap`, then `len(array)` will be left unchanged.
|
//
|
||||||
|
// Note: Prefer the procedure group `shrink`
|
||||||
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) {
|
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)
|
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
|
@builtin
|
||||||
card :: proc "contextless" (s: $S/bit_set[$E; $U]) -> int {
|
card :: proc "contextless" (s: $S/bit_set[$E; $U]) -> int {
|
||||||
return int(intrinsics.count_ones(transmute(intrinsics.type_bit_set_underlying_type(S))s))
|
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
|
@builtin
|
||||||
@(disabled=ODIN_DISABLE_ASSERT)
|
@(disabled=ODIN_DISABLE_ASSERT)
|
||||||
assert :: proc(condition: bool, message := #caller_expression(condition), loc := #caller_location) {
|
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
|
// Evaluates the condition and panics the program iff the condition is false.
|
||||||
// false. This routine ignores `ODIN_DISABLE_ASSERT`, and will always
|
// This uses the `context.assertion_failure_procedure` to assert.
|
||||||
// execute.
|
// This routine ignores `ODIN_DISABLE_ASSERT`, and will always execute.
|
||||||
@builtin
|
@builtin
|
||||||
ensure :: proc(condition: bool, message := #caller_expression(condition), loc := #caller_location) {
|
ensure :: proc(condition: bool, message := #caller_expression(condition), loc := #caller_location) {
|
||||||
if !condition {
|
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
|
@builtin
|
||||||
panic :: proc(message: string, loc := #caller_location) -> ! {
|
panic :: proc(message: string, loc := #caller_location) -> ! {
|
||||||
p := context.assertion_failure_proc
|
p := context.assertion_failure_proc
|
||||||
@@ -1077,6 +1141,8 @@ panic :: proc(message: string, loc := #caller_location) -> ! {
|
|||||||
p("panic", message, loc)
|
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
|
@builtin
|
||||||
unimplemented :: proc(message := "", loc := #caller_location) -> ! {
|
unimplemented :: proc(message := "", loc := #caller_location) -> ! {
|
||||||
p := context.assertion_failure_proc
|
p := context.assertion_failure_proc
|
||||||
@@ -1086,7 +1152,10 @@ unimplemented :: proc(message := "", loc := #caller_location) -> ! {
|
|||||||
p("not yet implemented", message, loc)
|
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
|
@builtin
|
||||||
@(disabled=ODIN_DISABLE_ASSERT)
|
@(disabled=ODIN_DISABLE_ASSERT)
|
||||||
assert_contextless :: proc "contextless" (condition: bool, message := #caller_expression(condition), loc := #caller_location) {
|
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
|
@builtin
|
||||||
ensure_contextless :: proc "contextless" (condition: bool, message := #caller_expression(condition), loc := #caller_location) {
|
ensure_contextless :: proc "contextless" (condition: bool, message := #caller_expression(condition), loc := #caller_location) {
|
||||||
if !condition {
|
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
|
@builtin
|
||||||
panic_contextless :: proc "contextless" (message: string, loc := #caller_location) -> ! {
|
panic_contextless :: proc "contextless" (message: string, loc := #caller_location) -> ! {
|
||||||
default_assertion_contextless_failure_proc("panic", message, loc)
|
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
|
@builtin
|
||||||
unimplemented_contextless :: proc "contextless" (message := "", loc := #caller_location) -> ! {
|
unimplemented_contextless :: proc "contextless" (message := "", loc := #caller_location) -> ! {
|
||||||
default_assertion_contextless_failure_proc("not yet implemented", message, loc)
|
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
|
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)
|
@(require_results)
|
||||||
nil_allocator :: proc "contextless" () -> Allocator {
|
nil_allocator :: proc "contextless" () -> Allocator {
|
||||||
return Allocator{
|
return Allocator{
|
||||||
@@ -73,6 +81,9 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
return nil, nil
|
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)
|
@(require_results)
|
||||||
panic_allocator :: proc() -> Allocator {
|
panic_allocator :: proc() -> Allocator {
|
||||||
return 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
|
NO_DEFAULT_TEMP_ALLOCATOR: bool : ODIN_OS == .Freestanding || ODIN_DEFAULT_TO_NIL_ALLOCATOR
|
||||||
|
|
||||||
when NO_DEFAULT_TEMP_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 :: struct {}
|
||||||
|
|
||||||
default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backing_allocator := context.allocator) {}
|
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) {
|
default_temp_allocator_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) {
|
||||||
}
|
}
|
||||||
} else {
|
} 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 {
|
Default_Temp_Allocator :: struct {
|
||||||
arena: Arena,
|
arena: Arena,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ objc_SEL :: ^intrinsics.objc_selector
|
|||||||
objc_Ivar :: ^intrinsics.objc_ivar
|
objc_Ivar :: ^intrinsics.objc_ivar
|
||||||
objc_BOOL :: bool
|
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
|
objc_IMP :: proc "c" (object: objc_id, sel: objc_SEL, #c_vararg args: ..any) -> objc_id
|
||||||
|
|
||||||
@@ -26,6 +30,11 @@ foreign ObjC {
|
|||||||
objc_msgSend_fp2ret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> complex128 ---
|
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_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_lookUpClass :: proc "c" (name: cstring) -> objc_Class ---
|
||||||
objc_allocateClassPair :: proc "c" (superclass: objc_Class, name: cstring, extraBytes: uint) -> objc_Class ---
|
objc_allocateClassPair :: proc "c" (superclass: objc_Class, name: cstring, extraBytes: uint) -> objc_Class ---
|
||||||
objc_registerClassPair :: proc "c" (cls : objc_Class) ---
|
objc_registerClassPair :: proc "c" (cls : 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_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_getInstanceVariable :: proc "c" (cls : objc_Class, name: cstring) -> objc_Ivar ---
|
||||||
class_getInstanceSize :: proc "c" (cls : objc_Class) -> uint ---
|
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 ---
|
ivar_getOffset :: proc "c" (v: objc_Ivar) -> uintptr ---
|
||||||
object_getClass :: proc "c" (obj: objc_id) -> objc_Class ---
|
object_getClass :: proc "c" (obj: objc_id) -> objc_Class ---
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ MIN_READ_BUFFER_SIZE :: 16
|
|||||||
@(private)
|
@(private)
|
||||||
DEFAULT_MAX_CONSECUTIVE_EMPTY_READS :: 128
|
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) {
|
reader_init :: proc(b: ^Reader, rd: io.Reader, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator, loc := #caller_location) {
|
||||||
size := size
|
size := size
|
||||||
size = max(size, MIN_READ_BUFFER_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)
|
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_init_with_buf :: proc(b: ^Reader, rd: io.Reader, buf: []byte) {
|
||||||
reader_reset(b, rd)
|
reader_reset(b, rd)
|
||||||
b.buf_allocator = {}
|
b.buf_allocator = {}
|
||||||
@@ -49,10 +51,12 @@ reader_destroy :: proc(b: ^Reader) {
|
|||||||
b^ = {}
|
b^ = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reader_size returns the number of bytes in the backing buffer
|
||||||
reader_size :: proc(b: ^Reader) -> int {
|
reader_size :: proc(b: ^Reader) -> int {
|
||||||
return len(b.buf)
|
return len(b.buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reader_reset resets the read and write positions, and the error values
|
||||||
reader_reset :: proc(b: ^Reader, r: io.Reader) {
|
reader_reset :: proc(b: ^Reader, r: io.Reader) {
|
||||||
b.rd = r
|
b.rd = r
|
||||||
b.r, b.w = 0, 0
|
b.r, b.w = 0, 0
|
||||||
|
|||||||
+21
-7
@@ -46,6 +46,7 @@ DEFAULT_MAX_SCAN_TOKEN_SIZE :: 1<<16
|
|||||||
@(private)
|
@(private)
|
||||||
_INIT_BUF_SIZE :: 4096
|
_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 {
|
scanner_init :: proc(s: ^Scanner, r: io.Reader, buf_allocator := context.allocator) -> ^Scanner {
|
||||||
s.r = r
|
s.r = r
|
||||||
s.split = scan_lines
|
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
|
s.buf.allocator = buf_allocator
|
||||||
return s
|
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 {
|
scanner_init_with_buffer :: proc(s: ^Scanner, r: io.Reader, buf: []byte) -> ^Scanner {
|
||||||
s.r = r
|
s.r = r
|
||||||
s.split = scan_lines
|
s.split = scan_lines
|
||||||
@@ -75,24 +78,27 @@ scanner_error :: proc(s: ^Scanner) -> Scanner_Error {
|
|||||||
return s._err
|
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
|
// 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.
|
// Treat the returned value as if it is immutable.
|
||||||
scanner_bytes :: proc(s: ^Scanner) -> []byte {
|
scanner_bytes :: proc(s: ^Scanner) -> []byte {
|
||||||
return s.token
|
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
|
// 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.
|
// Treat the returned value as if it is immutable.
|
||||||
scanner_text :: proc(s: ^Scanner) -> string {
|
scanner_text :: proc(s: ^Scanner) -> string {
|
||||||
return string(s.token)
|
return string(s.token)
|
||||||
}
|
}
|
||||||
|
|
||||||
// scanner_scan advances the scanner
|
// scanner_scan is an alias of scan
|
||||||
scanner_scan :: proc(s: ^Scanner) -> bool {
|
scanner_scan :: scan
|
||||||
|
|
||||||
|
// scan advances the Scanner
|
||||||
|
scan :: proc(s: ^Scanner) -> bool {
|
||||||
set_err :: proc(s: ^Scanner, err: Scanner_Error) {
|
set_err :: proc(s: ^Scanner, err: Scanner_Error) {
|
||||||
switch s._err {
|
switch s._err {
|
||||||
case nil, .EOF:
|
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) {
|
scan_bytes :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
|
||||||
if at_eof && len(data) == 0 {
|
if at_eof && len(data) == 0 {
|
||||||
return
|
return
|
||||||
@@ -236,6 +243,10 @@ scan_bytes :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte,
|
|||||||
return 1, data[0:1], nil, false
|
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) {
|
scan_runes :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
|
||||||
if at_eof && len(data) == 0 {
|
if at_eof && len(data) == 0 {
|
||||||
return
|
return
|
||||||
@@ -264,7 +275,8 @@ scan_runes :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte,
|
|||||||
token = ERROR_RUNE
|
token = ERROR_RUNE
|
||||||
return
|
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) {
|
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 {
|
is_space :: proc "contextless" (r: rune) -> bool {
|
||||||
switch r {
|
switch r {
|
||||||
@@ -312,6 +324,8 @@ scan_words :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte,
|
|||||||
return
|
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) {
|
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 {
|
trim_carriage_return :: proc "contextless" (data: []byte) -> []byte {
|
||||||
if len(data) > 0 && data[len(data)-1] == '\r' {
|
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) {
|
writer_init :: proc(b: ^Writer, wr: io.Writer, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator) {
|
||||||
size := size
|
size := size
|
||||||
size = max(size, MIN_READ_BUFFER_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)
|
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_init_with_buf :: proc(b: ^Writer, wr: io.Writer, buf: []byte) {
|
||||||
writer_reset(b, wr)
|
writer_reset(b, wr)
|
||||||
b.buf_allocator = {}
|
b.buf_allocator = {}
|
||||||
|
|||||||
@@ -134,8 +134,13 @@ equal_fold :: proc(u, v: []byte) -> bool {
|
|||||||
return false
|
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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -176,7 +176,11 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
|||||||
return .Unsupported_Type
|
return .Unsupported_Type
|
||||||
|
|
||||||
case runtime.Type_Info_Pointer:
|
case runtime.Type_Info_Pointer:
|
||||||
|
if v.id == typeid_of(Null) {
|
||||||
|
io.write_string(w, "null") or_return
|
||||||
|
} else {
|
||||||
return .Unsupported_Type
|
return .Unsupported_Type
|
||||||
|
}
|
||||||
|
|
||||||
case runtime.Type_Info_Multi_Pointer:
|
case runtime.Type_Info_Multi_Pointer:
|
||||||
return .Unsupported_Type
|
return .Unsupported_Type
|
||||||
|
|||||||
@@ -164,25 +164,25 @@ orthogonal :: proc{vector2_orthogonal, vector3_orthogonal}
|
|||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
vector4_srgb_to_linear_f16 :: proc "contextless" (col: Vector4f16) -> Vector4f16 {
|
vector4_srgb_to_linear_f16 :: proc "contextless" (col: Vector4f16) -> Vector4f16 {
|
||||||
r := math.pow(col.x, 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, 2.2)
|
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, 2.2)
|
b := math.pow((col.z + 0.055) / 1.055, 2.4) if col.z > 0.04045 else col.z / 12.92
|
||||||
a := col.w
|
a := col.w
|
||||||
return {r, g, b, a}
|
return {r, g, b, a}
|
||||||
}
|
}
|
||||||
@(require_results)
|
@(require_results)
|
||||||
vector4_srgb_to_linear_f32 :: proc "contextless" (col: Vector4f32) -> Vector4f32 {
|
vector4_srgb_to_linear_f32 :: proc "contextless" (col: Vector4f32) -> Vector4f32 {
|
||||||
r := math.pow(col.x, 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, 2.2)
|
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, 2.2)
|
b := math.pow((col.z + 0.055) / 1.055, 2.4) if col.z > 0.04045 else col.z / 12.92
|
||||||
a := col.w
|
a := col.w
|
||||||
return {r, g, b, a}
|
return {r, g, b, a}
|
||||||
}
|
}
|
||||||
@(require_results)
|
@(require_results)
|
||||||
vector4_srgb_to_linear_f64 :: proc "contextless" (col: Vector4f64) -> Vector4f64 {
|
vector4_srgb_to_linear_f64 :: proc "contextless" (col: Vector4f64) -> Vector4f64 {
|
||||||
r := math.pow(col.x, 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, 2.2)
|
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, 2.2)
|
b := math.pow((col.z + 0.055) / 1.055, 2.4) if col.z > 0.04045 else col.z / 12.92
|
||||||
a := col.w
|
a := col.w
|
||||||
return {r, g, b, a}
|
return {r, g, b, a}
|
||||||
}
|
}
|
||||||
@@ -192,70 +192,55 @@ vector4_srgb_to_linear :: proc{
|
|||||||
vector4_srgb_to_linear_f64,
|
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)
|
@(require_results)
|
||||||
vector4_linear_to_srgb_f16 :: proc "contextless" (col: Vector4f16) -> Vector4f16 {
|
vector4_linear_to_srgb_f16 :: proc "contextless" (col: Vector4f16) -> Vector4f16 {
|
||||||
a :: 2.51
|
x := 1.055 * math.pow(col.x, 1.0 / 2.4) - 0.055 if col.x > 0.0031308 else 12.92 * col.x
|
||||||
b :: 0.03
|
y := 1.055 * math.pow(col.y, 1.0 / 2.4) - 0.055 if col.y > 0.0031308 else 12.92 * col.y
|
||||||
c :: 2.43
|
z := 1.055 * math.pow(col.z, 1.0 / 2.4) - 0.055 if col.z > 0.0031308 else 12.92 * col.z
|
||||||
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)
|
|
||||||
|
|
||||||
return {x, y, z, col.w}
|
return {x, y, z, col.w}
|
||||||
}
|
}
|
||||||
@(require_results)
|
@(require_results)
|
||||||
vector4_linear_to_srgb_f32 :: proc "contextless" (col: Vector4f32) -> Vector4f32 {
|
vector4_linear_to_srgb_f32 :: proc "contextless" (col: Vector4f32) -> Vector4f32 {
|
||||||
a :: 2.51
|
x := 1.055 * math.pow(col.x, 1.0 / 2.4) - 0.055 if col.x > 0.0031308 else 12.92 * col.x
|
||||||
b :: 0.03
|
y := 1.055 * math.pow(col.y, 1.0 / 2.4) - 0.055 if col.y > 0.0031308 else 12.92 * col.y
|
||||||
c :: 2.43
|
z := 1.055 * math.pow(col.z, 1.0 / 2.4) - 0.055 if col.z > 0.0031308 else 12.92 * col.z
|
||||||
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)
|
|
||||||
|
|
||||||
return {x, y, z, col.w}
|
return {x, y, z, col.w}
|
||||||
}
|
}
|
||||||
@(require_results)
|
@(require_results)
|
||||||
vector4_linear_to_srgb_f64 :: proc "contextless" (col: Vector4f64) -> Vector4f64 {
|
vector4_linear_to_srgb_f64 :: proc "contextless" (col: Vector4f64) -> Vector4f64 {
|
||||||
a :: 2.51
|
x := 1.055 * math.pow(col.x, 1.0 / 2.4) - 0.055 if col.x > 0.0031308 else 12.92 * col.x
|
||||||
b :: 0.03
|
y := 1.055 * math.pow(col.y, 1.0 / 2.4) - 0.055 if col.y > 0.0031308 else 12.92 * col.y
|
||||||
c :: 2.43
|
z := 1.055 * math.pow(col.z, 1.0 / 2.4) - 0.055 if col.z > 0.0031308 else 12.92 * col.z
|
||||||
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)
|
|
||||||
|
|
||||||
return {x, y, z, col.w}
|
return {x, y, z, col.w}
|
||||||
}
|
}
|
||||||
@@ -265,6 +250,36 @@ vector4_linear_to_srgb :: proc{
|
|||||||
vector4_linear_to_srgb_f64,
|
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)
|
@(require_results)
|
||||||
vector4_hsl_to_rgb_f16 :: proc "contextless" (h, s, l: f16, a: f16 = 1) -> Vector4f16 {
|
vector4_hsl_to_rgb_f16 :: proc "contextless" (h, s, l: f16, a: f16 = 1) -> Vector4f16 {
|
||||||
|
|||||||
@@ -402,7 +402,12 @@ remap :: proc "contextless" (old_value, old_min, old_max, new_min, new_max: $T)
|
|||||||
if old_range == 0 {
|
if old_range == 0 {
|
||||||
return new_range / 2
|
return new_range / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
return ((old_value - old_min) / old_range) * new_range + new_min
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ _get_full_path :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> (fullpath:
|
|||||||
buf: [32]u8
|
buf: [32]u8
|
||||||
copy(buf[:], PROC_FD_PATH)
|
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] != '/' {
|
if fullpath, err = _read_link_cstr(cstring(&buf[0]), allocator); err != nil || fullpath[0] != '/' {
|
||||||
delete(fullpath, allocator)
|
delete(fullpath, allocator)
|
||||||
|
|||||||
@@ -908,7 +908,7 @@ _dup :: proc(fd: Handle) -> (Handle, Error) {
|
|||||||
@(require_results)
|
@(require_results)
|
||||||
absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
|
absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
|
||||||
buf : [256]byte
|
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 } )
|
procfs_path := strings.concatenate( []string{ "/proc/self/fd/", fd_str } )
|
||||||
defer delete(procfs_path)
|
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"
|
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)
|
@(require_results)
|
||||||
iterate_array :: proc(val: any, it: ^int) -> (elem: any, index: int, ok: bool) {
|
iterate_array :: proc(val: any, it: ^int) -> (elem: any, index: int, ok: bool) {
|
||||||
if val == nil || it == nil {
|
if val == nil || it == nil {
|
||||||
@@ -45,6 +49,10 @@ iterate_array :: proc(val: any, it: ^int) -> (elem: any, index: int, ok: bool) {
|
|||||||
return
|
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)
|
@(require_results)
|
||||||
iterate_map :: proc(val: any, it: ^int) -> (key, value: any, ok: bool) {
|
iterate_map :: proc(val: any, it: ^int) -> (key, value: any, ok: bool) {
|
||||||
if val == nil || it == nil {
|
if val == nil || it == nil {
|
||||||
|
|||||||
+211
-13
@@ -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)
|
@(require_results)
|
||||||
type_kind :: proc(T: typeid) -> Type_Kind {
|
type_kind :: proc(T: typeid) -> Type_Kind {
|
||||||
ti := type_info_of(T)
|
ti := type_info_of(T)
|
||||||
@@ -108,31 +109,51 @@ type_kind :: proc(T: typeid) -> Type_Kind {
|
|||||||
return .Invalid
|
return .Invalid
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(bill): Better name
|
// Returns the `Type_Kind` of the base-type of a typeid.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
underlying_type_kind :: proc(T: typeid) -> Type_Kind {
|
underlying_type_kind :: proc(T: typeid) -> Type_Kind {
|
||||||
return type_kind(runtime.typeid_base(T))
|
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)
|
@(require_results)
|
||||||
backing_type_kind :: proc(T: typeid) -> Type_Kind {
|
backing_type_kind :: proc(T: typeid) -> Type_Kind {
|
||||||
return type_kind(runtime.typeid_core(T))
|
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_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_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
|
type_info_base_without_enum :: type_info_core
|
||||||
|
|
||||||
|
|
||||||
when !ODIN_NO_RTTI {
|
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_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_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
|
typeid_base_without_enum :: typeid_core
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// any_base returns an `any` where the `typeid` has been replaced with the `base-type` equivalent
|
||||||
@(require_results)
|
@(require_results)
|
||||||
any_base :: proc(v: any) -> any {
|
any_base :: proc(v: any) -> any {
|
||||||
v := v
|
v := v
|
||||||
@@ -141,6 +162,8 @@ any_base :: proc(v: any) -> any {
|
|||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// any_core returns an `any` where the `typeid` has been replaced with the `core-type` equivalent
|
||||||
@(require_results)
|
@(require_results)
|
||||||
any_core :: proc(v: any) -> any {
|
any_core :: proc(v: any) -> any {
|
||||||
v := v
|
v := v
|
||||||
@@ -150,6 +173,20 @@ any_core :: proc(v: any) -> any {
|
|||||||
return v
|
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)
|
@(require_results)
|
||||||
typeid_elem :: proc(id: typeid) -> typeid {
|
typeid_elem :: proc(id: typeid) -> typeid {
|
||||||
ti := type_info_of(id)
|
ti := type_info_of(id)
|
||||||
@@ -160,11 +197,13 @@ typeid_elem :: proc(id: typeid) -> typeid {
|
|||||||
#partial switch v in ti.variant {
|
#partial switch v in ti.variant {
|
||||||
case Type_Info_Complex:
|
case Type_Info_Complex:
|
||||||
switch bits {
|
switch bits {
|
||||||
|
case 32: return f16
|
||||||
case 64: return f32
|
case 64: return f32
|
||||||
case 128: return f64
|
case 128: return f64
|
||||||
}
|
}
|
||||||
case Type_Info_Quaternion:
|
case Type_Info_Quaternion:
|
||||||
switch bits {
|
switch bits {
|
||||||
|
case 64: return f16
|
||||||
case 128: return f32
|
case 128: return f32
|
||||||
case 256: return f64
|
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)
|
@(require_results)
|
||||||
size_of_typeid :: proc(T: typeid) -> int {
|
size_of_typeid :: proc(T: typeid) -> int {
|
||||||
if ti := type_info_of(T); ti != nil {
|
if ti := type_info_of(T); ti != nil {
|
||||||
@@ -189,6 +229,7 @@ size_of_typeid :: proc(T: typeid) -> int {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returns the alignment of the type that the passed typeid represents
|
||||||
@(require_results)
|
@(require_results)
|
||||||
align_of_typeid :: proc(T: typeid) -> int {
|
align_of_typeid :: proc(T: typeid) -> int {
|
||||||
if ti := type_info_of(T); ti != nil {
|
if ti := type_info_of(T); ti != nil {
|
||||||
@@ -197,6 +238,7 @@ align_of_typeid :: proc(T: typeid) -> int {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reinterprets the data stored at `v` as a slice of bytes
|
||||||
@(require_results)
|
@(require_results)
|
||||||
as_bytes :: proc(v: any) -> []byte {
|
as_bytes :: proc(v: any) -> []byte {
|
||||||
if v != nil {
|
if v != nil {
|
||||||
@@ -206,11 +248,13 @@ as_bytes :: proc(v: any) -> []byte {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Splits the data stored in `any` into its two components: `data` and `id`
|
||||||
@(require_results)
|
@(require_results)
|
||||||
any_data :: #force_inline proc(v: any) -> (data: rawptr, id: typeid) {
|
any_data :: #force_inline proc(v: any) -> (data: rawptr, id: typeid) {
|
||||||
return v.data, v.id
|
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)
|
@(require_results)
|
||||||
is_nil :: proc(v: any) -> bool {
|
is_nil :: proc(v: any) -> bool {
|
||||||
if v == nil {
|
if v == nil {
|
||||||
@@ -228,6 +272,16 @@ is_nil :: proc(v: any) -> bool {
|
|||||||
return true
|
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)
|
@(require_results)
|
||||||
length :: proc(val: any) -> int {
|
length :: proc(val: any) -> int {
|
||||||
if val == nil { return 0 }
|
if val == nil { return 0 }
|
||||||
@@ -255,11 +309,20 @@ length :: proc(val: any) -> int {
|
|||||||
return runtime.map_len((^runtime.Raw_Map)(val.data)^)
|
return runtime.map_len((^runtime.Raw_Map)(val.data)^)
|
||||||
|
|
||||||
case Type_Info_String:
|
case Type_Info_String:
|
||||||
|
switch a.encoding {
|
||||||
|
case .UTF_8:
|
||||||
if a.is_cstring {
|
if a.is_cstring {
|
||||||
return len((^cstring)(val.data)^)
|
return len((^cstring)(val.data)^)
|
||||||
} else {
|
} else {
|
||||||
return (^runtime.Raw_String)(val.data).len
|
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:
|
case Type_Info_Simd_Vector:
|
||||||
return a.count
|
return a.count
|
||||||
@@ -268,6 +331,12 @@ length :: proc(val: any) -> int {
|
|||||||
return 0
|
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)
|
@(require_results)
|
||||||
capacity :: proc(val: any) -> int {
|
capacity :: proc(val: any) -> int {
|
||||||
if val == nil { return 0 }
|
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)
|
@(require_results)
|
||||||
index :: proc(val: any, i: int, loc := #caller_location) -> any {
|
index :: proc(val: any, i: int, loc := #caller_location) -> any {
|
||||||
if val == nil { return nil }
|
if val == nil { return nil }
|
||||||
@@ -350,15 +420,25 @@ index :: proc(val: any, i: int, loc := #caller_location) -> any {
|
|||||||
case Type_Info_String:
|
case Type_Info_String:
|
||||||
if a.is_cstring { return nil }
|
if a.is_cstring { return nil }
|
||||||
|
|
||||||
|
switch a.encoding {
|
||||||
|
case .UTF_8:
|
||||||
raw := (^runtime.Raw_String)(val.data)
|
raw := (^runtime.Raw_String)(val.data)
|
||||||
runtime.bounds_check_error_loc(loc, i, raw.len)
|
runtime.bounds_check_error_loc(loc, i, raw.len)
|
||||||
offset := uintptr(size_of(u8) * i)
|
offset := uintptr(size_of(u8) * i)
|
||||||
data := rawptr(uintptr(raw.data) + offset)
|
data := rawptr(uintptr(raw.data) + offset)
|
||||||
return any{data, typeid_of(u8)}
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dereferences `any` if it represents a pointer-based value (`^T -> T`)
|
||||||
@(require_results)
|
@(require_results)
|
||||||
deref :: proc(val: any) -> any {
|
deref :: proc(val: any) -> any {
|
||||||
if val != nil {
|
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.
|
// Each key is a non-empty string which contains no control characters other than space, quotes, and colon.
|
||||||
Struct_Tag :: distinct string
|
Struct_Tag :: distinct string
|
||||||
|
|
||||||
|
// `Struct_Field` represents a information of a field of a struct
|
||||||
Struct_Field :: struct {
|
Struct_Field :: struct {
|
||||||
name: string,
|
name: string,
|
||||||
type: ^Type_Info,
|
type: ^Type_Info,
|
||||||
tag: Struct_Tag,
|
tag: Struct_Tag,
|
||||||
offset: uintptr,
|
offset: uintptr, // in bytes
|
||||||
is_using: bool,
|
is_using: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns a `Struct_Field` containing the information for a struct field of a typeid `T` at index `i`
|
||||||
@(require_results)
|
@(require_results)
|
||||||
struct_field_at :: proc(T: typeid, i: int) -> (field: Struct_Field) {
|
struct_field_at :: proc(T: typeid, i: int) -> (field: Struct_Field) {
|
||||||
ti := runtime.type_info_base(type_info_of(T))
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns a `Struct_Field` containing the information for a struct field by `name` of a typeid `T`
|
||||||
@(require_results)
|
@(require_results)
|
||||||
struct_field_by_name :: proc(T: typeid, name: string) -> (field: Struct_Field) {
|
struct_field_by_name :: proc(T: typeid, name: string) -> (field: Struct_Field) {
|
||||||
ti := runtime.type_info_base(type_info_of(T))
|
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
|
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)
|
@(require_results)
|
||||||
struct_field_value_by_name :: proc(a: any, field: string, allow_using := false) -> any {
|
struct_field_value_by_name :: proc(a: any, field: string, allow_using := false) -> any {
|
||||||
if a == nil { return nil }
|
if a == nil { return nil }
|
||||||
@@ -452,6 +539,10 @@ struct_field_value_by_name :: proc(a: any, field: string, allow_using := false)
|
|||||||
return nil
|
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)
|
@(require_results)
|
||||||
struct_field_value :: proc(a: any, field: Struct_Field) -> any {
|
struct_field_value :: proc(a: any, field: Struct_Field) -> any {
|
||||||
if a == nil { return nil }
|
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)
|
@(require_results)
|
||||||
struct_field_names :: proc(T: typeid) -> []string {
|
struct_field_names :: proc(T: typeid) -> []string {
|
||||||
ti := runtime.type_info_base(type_info_of(T))
|
ti := runtime.type_info_base(type_info_of(T))
|
||||||
@@ -470,6 +562,7 @@ struct_field_names :: proc(T: typeid) -> []string {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns a `[]^Type_Info` of the types of the struct fields of type `T`
|
||||||
@(require_results)
|
@(require_results)
|
||||||
struct_field_types :: proc(T: typeid) -> []^Type_Info {
|
struct_field_types :: proc(T: typeid) -> []^Type_Info {
|
||||||
ti := runtime.type_info_base(type_info_of(T))
|
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)
|
@(require_results)
|
||||||
struct_field_tags :: proc(T: typeid) -> []Struct_Tag {
|
struct_field_tags :: proc(T: typeid) -> []Struct_Tag {
|
||||||
ti := runtime.type_info_base(type_info_of(T))
|
ti := runtime.type_info_base(type_info_of(T))
|
||||||
@@ -489,6 +583,7 @@ struct_field_tags :: proc(T: typeid) -> []Struct_Tag {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns a `[]uintptr` of the offsets in bytes of the struct fields of type `T`
|
||||||
@(require_results)
|
@(require_results)
|
||||||
struct_field_offsets :: proc(T: typeid) -> []uintptr {
|
struct_field_offsets :: proc(T: typeid) -> []uintptr {
|
||||||
ti := runtime.type_info_base(type_info_of(T))
|
ti := runtime.type_info_base(type_info_of(T))
|
||||||
@@ -498,6 +593,7 @@ struct_field_offsets :: proc(T: typeid) -> []uintptr {
|
|||||||
return nil
|
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 {
|
Struct_Field_Count_Method :: enum {
|
||||||
Top_Level,
|
Top_Level,
|
||||||
Using,
|
Using,
|
||||||
@@ -556,6 +652,10 @@ struct_field_count :: proc(T: typeid, method := Struct_Field_Count_Method.Top_Le
|
|||||||
return
|
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)
|
@(require_results)
|
||||||
struct_fields_zipped :: proc(T: typeid) -> (fields: #soa[]Struct_Field) {
|
struct_fields_zipped :: proc(T: typeid) -> (fields: #soa[]Struct_Field) {
|
||||||
ti := runtime.type_info_base(type_info_of(T))
|
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)
|
@(require_results)
|
||||||
struct_tag_get :: proc(tag: Struct_Tag, key: string) -> (value: string) {
|
struct_tag_get :: proc(tag: Struct_Tag, key: string) -> (value: string) {
|
||||||
v, _ := struct_tag_lookup(tag, key)
|
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)
|
@(require_results)
|
||||||
struct_tag_lookup :: proc(tag: Struct_Tag, key: string) -> (value: string, ok: bool) {
|
struct_tag_lookup :: proc(tag: Struct_Tag, key: string) -> (value: string, ok: bool) {
|
||||||
for t := tag; t != ""; /**/ {
|
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)
|
@(require_results)
|
||||||
enum_string :: proc(a: any) -> string {
|
enum_string :: proc(a: any) -> string {
|
||||||
if a == nil { return "" }
|
if a == nil { return "" }
|
||||||
@@ -656,7 +770,7 @@ enum_string :: proc(a: any) -> string {
|
|||||||
return ""
|
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)
|
@(require_results)
|
||||||
enum_from_name :: proc($Enum_Type: typeid, name: string) -> (value: Enum_Type, ok: bool) {
|
enum_from_name :: proc($Enum_Type: typeid, name: string) -> (value: Enum_Type, ok: bool) {
|
||||||
ti := type_info_base(type_info_of(Enum_Type))
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// enum_from_name_any returns the value of an enum field's name if found, returns `0, false` otherwise.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
enum_from_name_any :: proc(Enum_Type: typeid, name: string) -> (value: Type_Info_Enum_Value, ok: bool) {
|
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))
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// enum_name_from_value returns the name of enum field if a valid name using parametric polymorphism, otherwise returns `"", false`
|
||||||
@(require_results)
|
@(require_results)
|
||||||
enum_name_from_value :: proc(value: $Enum_Type) -> (name: string, ok: bool) where intrinsics.type_is_enum(Enum_Type) {
|
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))
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// enum_name_from_value_any returns the name of enum field if a valid name using reflection, otherwise returns `"", false`
|
||||||
@(require_results)
|
@(require_results)
|
||||||
enum_name_from_value_any :: proc(value: any) -> (name: string, ok: bool) {
|
enum_name_from_value_any :: proc(value: any) -> (name: string, ok: bool) {
|
||||||
if value.id == nil {
|
if value.id == nil {
|
||||||
@@ -725,9 +842,7 @@ enum_name_from_value_any :: proc(value: any) -> (name: string, ok: bool) {
|
|||||||
return
|
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)
|
@(require_results)
|
||||||
enum_value_has_name :: proc(value: $T) -> bool where intrinsics.type_is_enum(T) {
|
enum_value_has_name :: proc(value: $T) -> bool where intrinsics.type_is_enum(T) {
|
||||||
when len(T) == cap(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)
|
@(require_results)
|
||||||
enum_field_names :: proc(Enum_Type: typeid) -> []string {
|
enum_field_names :: proc(Enum_Type: typeid) -> []string {
|
||||||
ti := runtime.type_info_base(type_info_of(Enum_Type))
|
ti := runtime.type_info_base(type_info_of(Enum_Type))
|
||||||
@@ -757,6 +873,7 @@ enum_field_names :: proc(Enum_Type: typeid) -> []string {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
// enum_field_values returns `[]Type_Info_Enum_Value` of the values of the fields of type `Enum_Type`
|
||||||
@(require_results)
|
@(require_results)
|
||||||
enum_field_values :: proc(Enum_Type: typeid) -> []Type_Info_Enum_Value {
|
enum_field_values :: proc(Enum_Type: typeid) -> []Type_Info_Enum_Value {
|
||||||
ti := runtime.type_info_base(type_info_of(Enum_Type))
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Represents an `Enum_Field` storing the `name` and `value`
|
||||||
Enum_Field :: struct {
|
Enum_Field :: struct {
|
||||||
name: string,
|
name: string,
|
||||||
value: Type_Info_Enum_Value,
|
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)
|
@(require_results)
|
||||||
enum_fields_zipped :: proc(Enum_Type: typeid) -> (fields: #soa[]Enum_Field) {
|
enum_fields_zipped :: proc(Enum_Type: typeid) -> (fields: #soa[]Enum_Field) {
|
||||||
ti := runtime.type_info_base(type_info_of(Enum_Type))
|
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)
|
@(require_results)
|
||||||
union_variant_type_info :: proc(a: any) -> ^Type_Info {
|
union_variant_type_info :: proc(a: any) -> ^Type_Info {
|
||||||
id := union_variant_typeid(a)
|
id := union_variant_typeid(a)
|
||||||
return type_info_of(id)
|
return type_info_of(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns whether the `Type_Info_Union` store no tag (called a "pure maybe").
|
||||||
@(require_results)
|
@(require_results)
|
||||||
type_info_union_is_pure_maybe :: proc(info: runtime.Type_Info_Union) -> bool {
|
type_info_union_is_pure_maybe :: proc(info: runtime.Type_Info_Union) -> bool {
|
||||||
return len(info.variants) == 1 && is_pointer_internally(info.variants[0])
|
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)
|
@(require_results)
|
||||||
union_variant_typeid :: proc(a: any) -> typeid {
|
union_variant_typeid :: proc(a: any) -> typeid {
|
||||||
if a == nil { return nil }
|
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")
|
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)
|
@(require_results)
|
||||||
get_union_variant_raw_tag :: proc(a: any) -> i64 {
|
get_union_variant_raw_tag :: proc(a: any) -> i64 {
|
||||||
if a == nil { return -1 }
|
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")
|
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)
|
@(require_results)
|
||||||
get_union_variant :: proc(a: any) -> any {
|
get_union_variant :: proc(a: any) -> any {
|
||||||
if a == nil {
|
if a == nil {
|
||||||
@@ -876,6 +1003,14 @@ get_union_variant :: proc(a: any) -> any {
|
|||||||
return any{a.data, id}
|
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)
|
@(require_results)
|
||||||
get_union_as_ptr_variants :: proc(val: ^$T) -> (res: intrinsics.type_convert_variants_to_pointers(T)) where intrinsics.type_is_union(T) {
|
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)
|
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) {
|
set_union_variant_raw_tag :: proc(a: any, tag: i64) {
|
||||||
if a == nil { return }
|
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")
|
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) {
|
set_union_variant_typeid :: proc(a: any, id: typeid) {
|
||||||
if a == nil { return }
|
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")
|
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) {
|
set_union_variant_type_info :: proc(a: any, tag_ti: ^Type_Info) {
|
||||||
if a == nil { return }
|
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")
|
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 {
|
set_union_value :: proc(dst: any, value: any) -> bool {
|
||||||
if dst == nil { return false }
|
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")
|
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)
|
@(require_results)
|
||||||
bit_set_is_big_endian :: proc(value: any, loc := #caller_location) -> bool {
|
bit_set_is_big_endian :: proc(value: any, loc := #caller_location) -> bool {
|
||||||
if value == nil { return ODIN_ENDIAN == .Big }
|
if value == nil { return ODIN_ENDIAN == .Big }
|
||||||
@@ -1046,6 +1185,10 @@ Bit_Field :: struct {
|
|||||||
tag: Struct_Tag,
|
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)
|
@(require_results)
|
||||||
bit_fields_zipped :: proc(T: typeid) -> (fields: #soa[]Bit_Field) {
|
bit_fields_zipped :: proc(T: typeid) -> (fields: #soa[]Bit_Field) {
|
||||||
ti := runtime.type_info_base(type_info_of(T))
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bit_field_names returns a `[]string` of the field names of a `bit_field` type `T`
|
||||||
@(require_results)
|
@(require_results)
|
||||||
bit_field_names :: proc(T: typeid) -> []string {
|
bit_field_names :: proc(T: typeid) -> []string {
|
||||||
ti := runtime.type_info_base(type_info_of(T))
|
ti := runtime.type_info_base(type_info_of(T))
|
||||||
@@ -1070,6 +1214,7 @@ bit_field_names :: proc(T: typeid) -> []string {
|
|||||||
return nil
|
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)
|
@(require_results)
|
||||||
bit_field_types :: proc(T: typeid) -> []^Type_Info {
|
bit_field_types :: proc(T: typeid) -> []^Type_Info {
|
||||||
ti := runtime.type_info_base(type_info_of(T))
|
ti := runtime.type_info_base(type_info_of(T))
|
||||||
@@ -1079,6 +1224,7 @@ bit_field_types :: proc(T: typeid) -> []^Type_Info {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bit_field_types returns a `[]uintptr` of the field bit-width-sizes of a `bit_field` type `T`
|
||||||
@(require_results)
|
@(require_results)
|
||||||
bit_field_sizes :: proc(T: typeid) -> []uintptr {
|
bit_field_sizes :: proc(T: typeid) -> []uintptr {
|
||||||
ti := runtime.type_info_base(type_info_of(T))
|
ti := runtime.type_info_base(type_info_of(T))
|
||||||
@@ -1088,6 +1234,7 @@ bit_field_sizes :: proc(T: typeid) -> []uintptr {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bit_field_types returns a `[]uintptr` of the field offsets in bits of a `bit_field` type `T`
|
||||||
@(require_results)
|
@(require_results)
|
||||||
bit_field_offsets :: proc(T: typeid) -> []uintptr {
|
bit_field_offsets :: proc(T: typeid) -> []uintptr {
|
||||||
ti := runtime.type_info_base(type_info_of(T))
|
ti := runtime.type_info_base(type_info_of(T))
|
||||||
@@ -1097,6 +1244,7 @@ bit_field_offsets :: proc(T: typeid) -> []uintptr {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bit_field_types returns a `[]Struct_Tag` of the field tags of a `bit_field` type `T`
|
||||||
@(require_results)
|
@(require_results)
|
||||||
bit_field_tags :: proc(T: typeid) -> []Struct_Tag {
|
bit_field_tags :: proc(T: typeid) -> []Struct_Tag {
|
||||||
ti := runtime.type_info_base(type_info_of(T))
|
ti := runtime.type_info_base(type_info_of(T))
|
||||||
@@ -1106,6 +1254,7 @@ bit_field_tags :: proc(T: typeid) -> []Struct_Tag {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// as_bool attempts to convert an `any` to a `bool`.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
as_bool :: proc(a: any) -> (value: bool, valid: bool) {
|
as_bool :: proc(a: any) -> (value: bool, valid: bool) {
|
||||||
if a == nil { return }
|
if a == nil { return }
|
||||||
@@ -1129,6 +1278,7 @@ as_bool :: proc(a: any) -> (value: bool, valid: bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// as_int attempts to convert an `any` to a `int`.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
as_int :: proc(a: any) -> (value: int, valid: bool) {
|
as_int :: proc(a: any) -> (value: int, valid: bool) {
|
||||||
v: i64
|
v: i64
|
||||||
@@ -1137,6 +1287,7 @@ as_int :: proc(a: any) -> (value: int, valid: bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// as_uint attempts to convert an `any` to a `uint`.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
as_uint :: proc(a: any) -> (value: uint, valid: bool) {
|
as_uint :: proc(a: any) -> (value: uint, valid: bool) {
|
||||||
v: u64
|
v: u64
|
||||||
@@ -1145,6 +1296,7 @@ as_uint :: proc(a: any) -> (value: uint, valid: bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// as_i64 attempts to convert an `any` to a `i64`.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
as_i64 :: proc(a: any) -> (value: i64, valid: bool) {
|
as_i64 :: proc(a: any) -> (value: i64, valid: bool) {
|
||||||
if a == nil { return }
|
if a == nil { return }
|
||||||
@@ -1253,6 +1405,7 @@ as_i64 :: proc(a: any) -> (value: i64, valid: bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// as_u64 attempts to convert an `any` to a `u64`.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
as_u64 :: proc(a: any) -> (value: u64, valid: bool) {
|
as_u64 :: proc(a: any) -> (value: u64, valid: bool) {
|
||||||
if a == nil { return }
|
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)
|
@(require_results)
|
||||||
as_f64 :: proc(a: any) -> (value: f64, valid: bool) {
|
as_f64 :: proc(a: any) -> (value: f64, valid: bool) {
|
||||||
if a == nil { return }
|
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)
|
@(require_results)
|
||||||
as_string :: proc(a: any) -> (value: string, valid: bool) {
|
as_string :: proc(a: any) -> (value: string, valid: bool) {
|
||||||
if a == nil { return }
|
if a == nil { return }
|
||||||
@@ -1500,6 +1655,27 @@ as_string :: proc(a: any) -> (value: string, valid: bool) {
|
|||||||
return
|
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)
|
@(require_results)
|
||||||
relative_pointer_to_absolute_raw :: proc(data: rawptr, base_integer_id: typeid) -> rawptr {
|
relative_pointer_to_absolute_raw :: proc(data: rawptr, base_integer_id: typeid) -> rawptr {
|
||||||
_handle :: proc(ptr: ^$T) -> rawptr where intrinsics.type_is_integer(T) {
|
_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)
|
@(require_results)
|
||||||
as_pointer :: proc(a: any) -> (value: rawptr, valid: bool) {
|
as_pointer :: proc(a: any) -> (value: rawptr, valid: bool) {
|
||||||
if a == nil { return }
|
if a == nil { return }
|
||||||
@@ -1551,7 +1729,7 @@ as_pointer :: proc(a: any) -> (value: rawptr, valid: bool) {
|
|||||||
a.id = ti.id
|
a.id = ti.id
|
||||||
|
|
||||||
#partial switch info in ti.variant {
|
#partial switch info in ti.variant {
|
||||||
case Type_Info_Pointer:
|
case Type_Info_Pointer, Type_Info_Multi_Pointer:
|
||||||
valid = true
|
valid = true
|
||||||
value = (^rawptr)(a.data)^
|
value = (^rawptr)(a.data)^
|
||||||
|
|
||||||
@@ -1559,6 +1737,7 @@ as_pointer :: proc(a: any) -> (value: rawptr, valid: bool) {
|
|||||||
valid = true
|
valid = true
|
||||||
switch v in a {
|
switch v in a {
|
||||||
case cstring: value = rawptr(v)
|
case cstring: value = rawptr(v)
|
||||||
|
case cstring16: value = rawptr(v)
|
||||||
case: valid = false
|
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)
|
@(require_results)
|
||||||
as_raw_data :: proc(a: any) -> (value: rawptr, valid: bool) {
|
as_raw_data :: proc(a: any) -> (value: rawptr, valid: bool) {
|
||||||
if a == nil { return }
|
if a == nil { return }
|
||||||
@@ -1580,6 +1760,8 @@ as_raw_data :: proc(a: any) -> (value: rawptr, valid: bool) {
|
|||||||
switch v in a {
|
switch v in a {
|
||||||
case string: value = raw_data(v)
|
case string: value = raw_data(v)
|
||||||
case cstring: value = rawptr(v) // just in case
|
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
|
case: valid = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1604,10 +1786,13 @@ ne :: not_equal
|
|||||||
|
|
||||||
DEFAULT_EQUAL_MAX_RECURSION_LEVEL :: 32
|
DEFAULT_EQUAL_MAX_RECURSION_LEVEL :: 32
|
||||||
|
|
||||||
|
// Checks to see if two `any` values are not semantically equivalent
|
||||||
@(require_results)
|
@(require_results)
|
||||||
not_equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_level := 0) -> bool {
|
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)
|
return !equal(a, b, including_indirect_array_recursion, recursion_level)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checks to see if two `any` values are semantically equivalent
|
||||||
@(require_results)
|
@(require_results)
|
||||||
equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_level := 0) -> bool {
|
equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_level := 0) -> bool {
|
||||||
if a == nil && b == nil {
|
if a == nil && b == nil {
|
||||||
@@ -1702,6 +1887,8 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
|
|||||||
return runtime.memory_compare(a.data, b.data, t.size) == 0
|
return runtime.memory_compare(a.data, b.data, t.size) == 0
|
||||||
|
|
||||||
case Type_Info_String:
|
case Type_Info_String:
|
||||||
|
switch v.encoding {
|
||||||
|
case .UTF_8:
|
||||||
if v.is_cstring {
|
if v.is_cstring {
|
||||||
x := string((^cstring)(a.data)^)
|
x := string((^cstring)(a.data)^)
|
||||||
y := string((^cstring)(b.data)^)
|
y := string((^cstring)(b.data)^)
|
||||||
@@ -1711,6 +1898,17 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
|
|||||||
y := (^string)(b.data)^
|
y := (^string)(b.data)^
|
||||||
return x == y
|
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
|
return true
|
||||||
case Type_Info_Array:
|
case Type_Info_Array:
|
||||||
for i in 0..<v.count {
|
for i in 0..<v.count {
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ package reflect
|
|||||||
import "core:io"
|
import "core:io"
|
||||||
import "core:strings"
|
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)
|
@(require_results)
|
||||||
are_types_identical :: proc(a, b: ^Type_Info) -> bool {
|
are_types_identical :: proc(a, b: ^Type_Info) -> bool {
|
||||||
if a == b {
|
if a == b {
|
||||||
@@ -187,6 +191,7 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns true if the base-type is a signed integer or just a float, false otherwise.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
is_signed :: proc(info: ^Type_Info) -> bool {
|
is_signed :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
if info == nil { return false }
|
||||||
@@ -196,6 +201,7 @@ is_signed :: proc(info: ^Type_Info) -> bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
// Returns true if the base-type is an usigned integer, false otherwise.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
is_unsigned :: proc(info: ^Type_Info) -> bool {
|
is_unsigned :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
if info == nil { return false }
|
||||||
@@ -206,6 +212,7 @@ is_unsigned :: proc(info: ^Type_Info) -> bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns true when it is a 1-byte wide integer type, false otherwise.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
is_byte :: proc(info: ^Type_Info) -> bool {
|
is_byte :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
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)
|
@(require_results)
|
||||||
is_integer :: proc(info: ^Type_Info) -> bool {
|
is_integer :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
if info == nil { return false }
|
||||||
_, ok := type_info_base(info).variant.(Type_Info_Integer)
|
_, ok := type_info_base(info).variant.(Type_Info_Integer)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
// Returns true the base-type is a rune, false otherwise.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
is_rune :: proc(info: ^Type_Info) -> bool {
|
is_rune :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
if info == nil { return false }
|
||||||
_, ok := type_info_base(info).variant.(Type_Info_Rune)
|
_, ok := type_info_base(info).variant.(Type_Info_Rune)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
// Returns true the base-type is a float of any kind, false otherwise.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
is_float :: proc(info: ^Type_Info) -> bool {
|
is_float :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
if info == nil { return false }
|
||||||
_, ok := type_info_base(info).variant.(Type_Info_Float)
|
_, ok := type_info_base(info).variant.(Type_Info_Float)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
// Returns true the base-type is a complex-type of any kind, false otherwise.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
is_complex :: proc(info: ^Type_Info) -> bool {
|
is_complex :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
if info == nil { return false }
|
||||||
_, ok := type_info_base(info).variant.(Type_Info_Complex)
|
_, ok := type_info_base(info).variant.(Type_Info_Complex)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
// Returns true the base-type is a quaternions any kind, false otherwise.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
is_quaternion :: proc(info: ^Type_Info) -> bool {
|
is_quaternion :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
if info == nil { return false }
|
||||||
_, ok := type_info_base(info).variant.(Type_Info_Quaternion)
|
_, ok := type_info_base(info).variant.(Type_Info_Quaternion)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
// Returns true the base-type is an `any`, false otherwise.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
is_any :: proc(info: ^Type_Info) -> bool {
|
is_any :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
if info == nil { return false }
|
||||||
_, ok := type_info_base(info).variant.(Type_Info_Any)
|
_, ok := type_info_base(info).variant.(Type_Info_Any)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns true the base-type is a string of any kind (string, cstring, string16, cstring16), false otherwise.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
is_string :: proc(info: ^Type_Info) -> bool {
|
is_string :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
if info == nil { return false }
|
||||||
_, ok := type_info_base(info).variant.(Type_Info_String)
|
_, ok := type_info_base(info).variant.(Type_Info_String)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
// Returns true the base-type is a cstring of any kind (cstring, cstring16), false otherwise.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
is_cstring :: proc(info: ^Type_Info) -> bool {
|
is_cstring :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
if info == nil { return false }
|
||||||
v, ok := type_info_base(info).variant.(Type_Info_String)
|
v, ok := type_info_base(info).variant.(Type_Info_String)
|
||||||
return ok && v.is_cstring
|
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)
|
@(require_results)
|
||||||
is_boolean :: proc(info: ^Type_Info) -> bool {
|
is_boolean :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
if info == nil { return false }
|
||||||
_, ok := type_info_base(info).variant.(Type_Info_Boolean)
|
_, ok := type_info_base(info).variant.(Type_Info_Boolean)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
// Returns true the base-type is a pointer-type of any kind (^T or rawptr), false otherwise.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
is_pointer :: proc(info: ^Type_Info) -> bool {
|
is_pointer :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
if info == nil { return false }
|
||||||
_, ok := type_info_base(info).variant.(Type_Info_Pointer)
|
_, ok := type_info_base(info).variant.(Type_Info_Pointer)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
// Returns true the base-type is a pointer-type of any kind ([^]T), false otherwise.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
is_multi_pointer :: proc(info: ^Type_Info) -> bool {
|
is_multi_pointer :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
if info == nil { return false }
|
||||||
_, ok := type_info_base(info).variant.(Type_Info_Multi_Pointer)
|
_, ok := type_info_base(info).variant.(Type_Info_Multi_Pointer)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
// Returns true the base-type is a pointer-type of any kind (#soa^T), false otherwise.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
is_soa_pointer :: proc(info: ^Type_Info) -> bool {
|
is_soa_pointer :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
if info == nil { return false }
|
||||||
_, ok := type_info_base(info).variant.(Type_Info_Soa_Pointer)
|
_, ok := type_info_base(info).variant.(Type_Info_Soa_Pointer)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
// Returns true when the type is a pointer-like type, false otherwise.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
is_pointer_internally :: proc(info: ^Type_Info) -> bool {
|
is_pointer_internally :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
if info == nil { return false }
|
||||||
@@ -300,78 +337,91 @@ is_pointer_internally :: proc(info: ^Type_Info) -> bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
// Returns true when the type is a procedure type, false otherwise.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
is_procedure :: proc(info: ^Type_Info) -> bool {
|
is_procedure :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
if info == nil { return false }
|
||||||
_, ok := type_info_base(info).variant.(Type_Info_Procedure)
|
_, ok := type_info_base(info).variant.(Type_Info_Procedure)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
// Returns true when the type is a fixed-array type ([N]T), false otherwise.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
is_array :: proc(info: ^Type_Info) -> bool {
|
is_array :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
if info == nil { return false }
|
||||||
_, ok := type_info_base(info).variant.(Type_Info_Array)
|
_, ok := type_info_base(info).variant.(Type_Info_Array)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
// Returns true when the type is an enumerated-array type ([Enum]T), false otherwise.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
is_enumerated_array :: proc(info: ^Type_Info) -> bool {
|
is_enumerated_array :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
if info == nil { return false }
|
||||||
_, ok := type_info_base(info).variant.(Type_Info_Enumerated_Array)
|
_, ok := type_info_base(info).variant.(Type_Info_Enumerated_Array)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
// Returns true when the type is a dynamic-array type ([dynamic]T), false otherwise.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
is_dynamic_array :: proc(info: ^Type_Info) -> bool {
|
is_dynamic_array :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
if info == nil { return false }
|
||||||
_, ok := type_info_base(info).variant.(Type_Info_Dynamic_Array)
|
_, ok := type_info_base(info).variant.(Type_Info_Dynamic_Array)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
// Returns true when the type is a map type (map[K]V), false otherwise.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
is_dynamic_map :: proc(info: ^Type_Info) -> bool {
|
is_dynamic_map :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
if info == nil { return false }
|
||||||
_, ok := type_info_base(info).variant.(Type_Info_Map)
|
_, ok := type_info_base(info).variant.(Type_Info_Map)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
// Returns true when the type is a bit_set type, false otherwise.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
is_bit_set :: proc(info: ^Type_Info) -> bool {
|
is_bit_set :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
if info == nil { return false }
|
||||||
_, ok := type_info_base(info).variant.(Type_Info_Bit_Set)
|
_, ok := type_info_base(info).variant.(Type_Info_Bit_Set)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
// Returns true when the type is a slice type ([]T), false otherwise.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
is_slice :: proc(info: ^Type_Info) -> bool {
|
is_slice :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
if info == nil { return false }
|
||||||
_, ok := type_info_base(info).variant.(Type_Info_Slice)
|
_, ok := type_info_base(info).variant.(Type_Info_Slice)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
// Returns true when the type represents a set of parameters for a procedure (inputs or outputs), false otherwise.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
is_parameters :: proc(info: ^Type_Info) -> bool {
|
is_parameters :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
if info == nil { return false }
|
||||||
_, ok := type_info_base(info).variant.(Type_Info_Parameters)
|
_, ok := type_info_base(info).variant.(Type_Info_Parameters)
|
||||||
return ok
|
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)
|
@(require_results)
|
||||||
is_struct :: proc(info: ^Type_Info) -> bool {
|
is_struct :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
if info == nil { return false }
|
||||||
s, ok := type_info_base(info).variant.(Type_Info_Struct)
|
s, ok := type_info_base(info).variant.(Type_Info_Struct)
|
||||||
return ok && .raw_union not_in s.flags
|
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)
|
@(require_results)
|
||||||
is_raw_union :: proc(info: ^Type_Info) -> bool {
|
is_raw_union :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
if info == nil { return false }
|
||||||
s, ok := type_info_base(info).variant.(Type_Info_Struct)
|
s, ok := type_info_base(info).variant.(Type_Info_Struct)
|
||||||
return ok && .raw_union in s.flags
|
return ok && .raw_union in s.flags
|
||||||
}
|
}
|
||||||
|
// Returns true when the type is a union type (not `#raw_union`), false otherwise.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
is_union :: proc(info: ^Type_Info) -> bool {
|
is_union :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
if info == nil { return false }
|
||||||
_, ok := type_info_base(info).variant.(Type_Info_Union)
|
_, ok := type_info_base(info).variant.(Type_Info_Union)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
// Returns true when the type is an enum type, false otherwise.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
is_enum :: proc(info: ^Type_Info) -> bool {
|
is_enum :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
if info == nil { return false }
|
||||||
_, ok := type_info_base(info).variant.(Type_Info_Enum)
|
_, ok := type_info_base(info).variant.(Type_Info_Enum)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
// Returns true when the type is a #simd-array type (#simd[N]T), false otherwise.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
is_simd_vector :: proc(info: ^Type_Info) -> bool {
|
is_simd_vector :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false }
|
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)
|
@(require_results)
|
||||||
is_endian_platform :: proc(info: ^Type_Info) -> bool {
|
is_endian_platform :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false}
|
if info == nil { return false}
|
||||||
@@ -399,6 +452,9 @@ is_endian_platform :: proc(info: ^Type_Info) -> bool {
|
|||||||
return false
|
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)
|
@(require_results)
|
||||||
is_endian_little :: proc(info: ^Type_Info) -> bool {
|
is_endian_little :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false}
|
if info == nil { return false}
|
||||||
@@ -421,6 +477,9 @@ is_endian_little :: proc(info: ^Type_Info) -> bool {
|
|||||||
return ODIN_ENDIAN == .Little
|
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)
|
@(require_results)
|
||||||
is_endian_big :: proc(info: ^Type_Info) -> bool {
|
is_endian_big :: proc(info: ^Type_Info) -> bool {
|
||||||
if info == nil { return false}
|
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) {
|
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))
|
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) {
|
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)
|
return write_type_writer(writer, type_info_of(id), n_written)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Writes a typeid in standard (non-canonical) form
|
||||||
write_typeid :: proc{
|
write_typeid :: proc{
|
||||||
write_typeid_builder,
|
write_typeid_builder,
|
||||||
write_typeid_writer,
|
write_typeid_writer,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Writes a `^Type_Info` in standard (non-canonical) form
|
||||||
write_type :: proc{
|
write_type :: proc{
|
||||||
write_type_builder,
|
write_type_builder,
|
||||||
write_type_writer,
|
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 {
|
write_type_builder :: proc(buf: ^strings.Builder, ti: ^Type_Info) -> int {
|
||||||
n, _ := write_type_writer(strings.to_writer(buf), ti)
|
n, _ := write_type_writer(strings.to_writer(buf), ti)
|
||||||
return n
|
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) {
|
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 {
|
defer if n_written != nil {
|
||||||
n_written^ += n
|
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 {
|
append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string {
|
||||||
return write_float(buf, f, fmt, prec, bit_size)
|
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
|
Writes a float64 value as a string to the given buffer with the specified format and precision
|
||||||
|
|
||||||
**Inputs**
|
**Inputs**
|
||||||
|
|||||||
@@ -436,8 +436,13 @@ equal_fold :: proc(u, v: string) -> (res: bool) {
|
|||||||
return false
|
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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1937,7 +1937,7 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory) {
|
|||||||
if (buf_len > 0 && buf_ptr) {
|
if (buf_len > 0 && buf_ptr) {
|
||||||
let n = Math.min(buf_len, str.length);
|
let n = Math.min(buf_len, str.length);
|
||||||
str = str.substring(0, n);
|
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;
|
return n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2001,7 +2001,7 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory) {
|
|||||||
if (buf_len > 0 && buf_ptr) {
|
if (buf_len > 0 && buf_ptr) {
|
||||||
let n = Math.min(buf_len, str.length);
|
let n = Math.min(buf_len, str.length);
|
||||||
str = str.substring(0, n);
|
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;
|
return n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+87
-91
@@ -13,7 +13,8 @@ Capture :: struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Match :: struct {
|
Match :: struct {
|
||||||
byte_start, byte_end: int,
|
byte_start: int,
|
||||||
|
byte_end: int,
|
||||||
}
|
}
|
||||||
|
|
||||||
Error :: enum {
|
Error :: enum {
|
||||||
@@ -39,6 +40,7 @@ Match_State :: struct {
|
|||||||
capture: [MAX_CAPTURES]Capture,
|
capture: [MAX_CAPTURES]Capture,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
match_class :: proc(c: rune, cl: rune) -> (res: bool) {
|
match_class :: proc(c: rune, cl: rune) -> (res: bool) {
|
||||||
switch unicode.to_lower(cl) {
|
switch unicode.to_lower(cl) {
|
||||||
case 'a': res = is_alpha(c)
|
case 'a': res = is_alpha(c)
|
||||||
@@ -65,19 +67,23 @@ is_punct :: unicode.is_punct
|
|||||||
is_space :: unicode.is_space
|
is_space :: unicode.is_space
|
||||||
is_cntrl :: unicode.is_control
|
is_cntrl :: unicode.is_control
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
is_alnum :: proc(c: rune) -> bool {
|
is_alnum :: proc(c: rune) -> bool {
|
||||||
return unicode.is_alpha(c) || unicode.is_digit(c)
|
return unicode.is_alpha(c) || unicode.is_digit(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
is_graph :: proc(c: rune) -> bool {
|
is_graph :: proc(c: rune) -> bool {
|
||||||
return (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || unicode.is_digit(c)
|
return (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || unicode.is_digit(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
is_xdigit :: proc(c: rune) -> bool {
|
is_xdigit :: proc(c: rune) -> bool {
|
||||||
return (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || unicode.is_digit(c)
|
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
|
// 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) {
|
utf8_peek :: proc(bytes: string) -> (c: rune, size: int, err: Error) {
|
||||||
c, size = utf8.decode_rune_in_string(bytes)
|
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
|
// find the first utf8 charater and its size and advance the index
|
||||||
// return an error if the character is an error
|
// return an error if the character is an error
|
||||||
|
@(require_results)
|
||||||
utf8_advance :: proc(bytes: string, index: ^int) -> (c: rune, err: Error) {
|
utf8_advance :: proc(bytes: string, index: ^int) -> (c: rune, err: Error) {
|
||||||
size: int
|
size: int
|
||||||
c, size = utf8.decode_rune_in_string(bytes[index^:])
|
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?
|
// continuation byte?
|
||||||
|
@(require_results)
|
||||||
is_cont :: proc(b: byte) -> bool {
|
is_cont :: proc(b: byte) -> bool {
|
||||||
return b & 0xc0 == 0x80
|
return b & 0xc0 == 0x80
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
utf8_prev :: proc(bytes: string, a, b: int) -> int {
|
utf8_prev :: proc(bytes: string, a, b: int) -> int {
|
||||||
b := b
|
b := b
|
||||||
|
|
||||||
@@ -117,6 +126,7 @@ utf8_prev :: proc(bytes: string, a, b: int) -> int {
|
|||||||
return a < b ? b - 1 : a
|
return a < b ? b - 1 : a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
utf8_next :: proc(bytes: string, a: int) -> int {
|
utf8_next :: proc(bytes: string, a: int) -> int {
|
||||||
a := a
|
a := a
|
||||||
b := len(bytes)
|
b := len(bytes)
|
||||||
@@ -128,6 +138,7 @@ utf8_next :: proc(bytes: string, a: int) -> int {
|
|||||||
return a < b ? a + 1 : b
|
return a < b ? a + 1 : b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
check_capture :: proc(ms: ^Match_State, l: rune) -> (int, Error) {
|
check_capture :: proc(ms: ^Match_State, l: rune) -> (int, Error) {
|
||||||
l := int(l - '1')
|
l := int(l - '1')
|
||||||
|
|
||||||
@@ -138,6 +149,7 @@ check_capture :: proc(ms: ^Match_State, l: rune) -> (int, Error) {
|
|||||||
return l, .OK
|
return l, .OK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
capture_to_close :: proc(ms: ^Match_State) -> (int, Error) {
|
capture_to_close :: proc(ms: ^Match_State) -> (int, Error) {
|
||||||
level := ms.level - 1
|
level := ms.level - 1
|
||||||
|
|
||||||
@@ -152,6 +164,7 @@ capture_to_close :: proc(ms: ^Match_State) -> (int, Error) {
|
|||||||
return 0, .Invalid_Pattern_Capture
|
return 0, .Invalid_Pattern_Capture
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
class_end :: proc(ms: ^Match_State, p: int) -> (step: int, err: Error) {
|
class_end :: proc(ms: ^Match_State, p: int) -> (step: int, err: Error) {
|
||||||
step = p
|
step = p
|
||||||
ch := utf8_advance(ms.pattern, &step) or_return
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
utf8_advance(ms.pattern, &step) or_return
|
_ = utf8_advance(ms.pattern, &step) or_return
|
||||||
|
|
||||||
case '[':
|
case '[':
|
||||||
// fine with step by 1
|
// fine with step by 1
|
||||||
@@ -198,6 +211,7 @@ class_end :: proc(ms: ^Match_State, p: int) -> (step: int, err: Error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
match_bracket_class :: proc(ms: ^Match_State, c: rune, p, ec: int) -> (sig: bool, err: Error) {
|
match_bracket_class :: proc(ms: ^Match_State, c: rune, p, ec: int) -> (sig: bool, err: Error) {
|
||||||
sig = true
|
sig = true
|
||||||
p := p
|
p := p
|
||||||
@@ -240,6 +254,7 @@ match_bracket_class :: proc(ms: ^Match_State, c: rune, p, ec: int) -> (sig: bool
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
single_match :: proc(ms: ^Match_State, s, p, ep: int) -> (matched: bool, schar_size: int, err: Error) {
|
single_match :: proc(ms: ^Match_State, s, p, ep: int) -> (matched: bool, schar_size: int, err: Error) {
|
||||||
if s >= len(ms.src) {
|
if s >= len(ms.src) {
|
||||||
return
|
return
|
||||||
@@ -254,13 +269,16 @@ single_match :: proc(ms: ^Match_State, s, p, ep: int) -> (matched: bool, schar_s
|
|||||||
case L_ESC:
|
case L_ESC:
|
||||||
pchar_next, _ := utf8_peek(ms.pattern[p + psize:]) or_return
|
pchar_next, _ := utf8_peek(ms.pattern[p + psize:]) or_return
|
||||||
matched = match_class(schar, pchar_next)
|
matched = match_class(schar, pchar_next)
|
||||||
case '[': matched = match_bracket_class(ms, schar, p, ep - 1) or_return
|
case '[':
|
||||||
case: matched = schar == pchar
|
matched = match_bracket_class(ms, schar, p, ep - 1) or_return
|
||||||
|
case:
|
||||||
|
matched = schar == pchar
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
match_balance :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error) {
|
match_balance :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error) {
|
||||||
if p >= len(ms.pattern) - 1 {
|
if p >= len(ms.pattern) - 1 {
|
||||||
return INVALID, .Invalid_Pattern_Capture
|
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
|
return INVALID, .OK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
max_expand :: proc(ms: ^Match_State, s, p, ep: int) -> (res: int, err: Error) {
|
max_expand :: proc(ms: ^Match_State, s, p, ep: int) -> (res: int, err: Error) {
|
||||||
m := s
|
m := s
|
||||||
|
|
||||||
// count up matches
|
// count up matches
|
||||||
for {
|
for {
|
||||||
matched, size := single_match(ms, m, p, ep) or_return
|
matched, size := single_match(ms, m, p, ep) or_return
|
||||||
|
|
||||||
if !matched {
|
if !matched {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -316,7 +334,6 @@ max_expand :: proc(ms: ^Match_State, s, p, ep: int) -> (res: int, err: Error) {
|
|||||||
|
|
||||||
for s <= m {
|
for s <= m {
|
||||||
result := match(ms, m, ep + 1) or_return
|
result := match(ms, m, ep + 1) or_return
|
||||||
|
|
||||||
if result != INVALID {
|
if result != INVALID {
|
||||||
return result, .OK
|
return result, .OK
|
||||||
}
|
}
|
||||||
@@ -331,6 +348,7 @@ max_expand :: proc(ms: ^Match_State, s, p, ep: int) -> (res: int, err: Error) {
|
|||||||
return INVALID, .OK
|
return INVALID, .OK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
min_expand :: proc(ms: ^Match_State, s, p, ep: int) -> (res: int, err: Error) {
|
min_expand :: proc(ms: ^Match_State, s, p, ep: int) -> (res: int, err: Error) {
|
||||||
s := s
|
s := s
|
||||||
|
|
||||||
@@ -339,7 +357,7 @@ min_expand :: proc(ms: ^Match_State, s, p, ep: int) -> (res: int, err: Error) {
|
|||||||
|
|
||||||
if result != INVALID {
|
if result != INVALID {
|
||||||
return result, .OK
|
return result, .OK
|
||||||
} else {
|
}
|
||||||
// TODO receive next step maybe?
|
// TODO receive next step maybe?
|
||||||
matched, rune_size := single_match(ms, s, p, ep) or_return
|
matched, rune_size := single_match(ms, s, p, ep) or_return
|
||||||
|
|
||||||
@@ -349,9 +367,9 @@ min_expand :: proc(ms: ^Match_State, s, p, ep: int) -> (res: int, err: Error) {
|
|||||||
return INVALID, .OK
|
return INVALID, .OK
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
start_capture :: proc(ms: ^Match_State, s, p, what: int) -> (res: int, err: Error) {
|
start_capture :: proc(ms: ^Match_State, s, p, what: int) -> (res: int, err: Error) {
|
||||||
level := ms.level
|
level := ms.level
|
||||||
|
|
||||||
@@ -366,6 +384,7 @@ start_capture :: proc(ms: ^Match_State, s, p, what: int) -> (res: int, err: Erro
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
end_capture :: proc(ms: ^Match_State, s, p: int) -> (res: int, err: Error) {
|
end_capture :: proc(ms: ^Match_State, s, p: int) -> (res: int, err: Error) {
|
||||||
l := capture_to_close(ms) or_return
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
match_capture :: proc(ms: ^Match_State, s: int, char: rune) -> (res: int, err: Error) {
|
match_capture :: proc(ms: ^Match_State, s: int, char: rune) -> (res: int, err: Error) {
|
||||||
index := check_capture(ms, char) or_return
|
index := check_capture(ms, char) or_return
|
||||||
length := ms.capture[index].len
|
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
|
return INVALID, .OK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
match :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error) {
|
match :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error) {
|
||||||
s := s
|
s := s
|
||||||
p := p
|
p := p
|
||||||
@@ -431,7 +452,6 @@ match :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error) {
|
|||||||
// balanced string
|
// balanced string
|
||||||
case 'b':
|
case 'b':
|
||||||
s = match_balance(ms, s, p + 2) or_return
|
s = match_balance(ms, s, p + 2) or_return
|
||||||
|
|
||||||
if s != INVALID {
|
if s != INVALID {
|
||||||
// eg after %b()
|
// eg after %b()
|
||||||
return match(ms, s, p + 4)
|
return match(ms, s, p + 4)
|
||||||
@@ -486,6 +506,7 @@ match :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error) {
|
|||||||
return s, .OK
|
return s, .OK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
match_default :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error) {
|
match_default :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error) {
|
||||||
s := s
|
s := s
|
||||||
ep := class_end(ms, p) or_return
|
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
|
epc := ep < len(ms.pattern) ? ms.pattern[ep] : 0
|
||||||
|
|
||||||
switch epc {
|
switch epc {
|
||||||
case '*', '?', '-': return match(ms, s, ep + 1)
|
case '*', '?', '-':
|
||||||
case: s = INVALID
|
return match(ms, s, ep + 1)
|
||||||
|
case:
|
||||||
|
s = INVALID
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
epc := ep < len(ms.pattern) ? ms.pattern[ep] : 0
|
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 '?':
|
case '?':
|
||||||
result := match(ms, s + ssize, ep + 1) or_return
|
result := match(ms, s + ssize, ep + 1) or_return
|
||||||
|
|
||||||
if result != INVALID {
|
if result == INVALID {
|
||||||
s = result
|
|
||||||
} else {
|
|
||||||
return match(ms, s, ep + 1)
|
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 + ssize, p, ep) or_return
|
||||||
case '*': s = max_expand(ms, s, 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 '-': s = min_expand(ms, s, p, ep) or_return
|
||||||
case: return match(ms, s + ssize, ep)
|
case:
|
||||||
|
return match(ms, s + ssize, ep)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return s, .OK
|
return s, .OK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
push_onecapture :: proc(ms: ^Match_State, i: int, s: int, e: int, matches: []Match) -> (err: Error) {
|
push_onecapture :: proc(ms: ^Match_State, i: int, s: int, e: int, matches: []Match) -> (err: Error) {
|
||||||
if i >= ms.level {
|
if i >= ms.level {
|
||||||
if i == 0 {
|
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
|
length := ms.capture[i].len
|
||||||
|
|
||||||
switch length {
|
switch length {
|
||||||
case CAP_UNFINISHED: err = .Unfinished_Capture
|
case CAP_UNFINISHED:
|
||||||
case CAP_POSITION: matches[i] = { init, init + 1 }
|
err = .Unfinished_Capture
|
||||||
case: matches[i] = { init, init + length }
|
case CAP_POSITION:
|
||||||
|
matches[i] = { init, init + 1 }
|
||||||
|
case:
|
||||||
|
matches[i] = { init, init + length }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
push_captures :: proc(
|
@(require_results)
|
||||||
ms: ^Match_State,
|
push_captures :: proc(ms: ^Match_State, s, e: int, matches: []Match) -> (nlevels: int, err: Error) {
|
||||||
s: int,
|
nlevels = 1 if ms.level == 0 && s >= 0 else ms.level
|
||||||
e: int,
|
|
||||||
matches: []Match,
|
|
||||||
) -> (nlevels: int, err: Error) {
|
|
||||||
nlevels = 1 if ms.level == 0 && s != -1 else ms.level
|
|
||||||
|
|
||||||
for i in 0..<nlevels {
|
for i in 0..<nlevels {
|
||||||
push_onecapture(ms, i, s, e, matches) or_return
|
push_onecapture(ms, i, s, e, matches) or_return
|
||||||
@@ -559,6 +582,7 @@ push_captures :: proc(
|
|||||||
|
|
||||||
// SPECIALS := "^$*+?.([%-"
|
// SPECIALS := "^$*+?.([%-"
|
||||||
// all special characters inside a small ascii array
|
// all special characters inside a small ascii array
|
||||||
|
@(rodata)
|
||||||
SPECIALS_TABLE := [256]bool {
|
SPECIALS_TABLE := [256]bool {
|
||||||
'^' = true,
|
'^' = true,
|
||||||
'$' = true,
|
'$' = true,
|
||||||
@@ -573,6 +597,7 @@ SPECIALS_TABLE := [256]bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// helper call to quick search for special characters
|
// helper call to quick search for special characters
|
||||||
|
@(require_results)
|
||||||
index_special :: proc(text: string) -> int {
|
index_special :: proc(text: string) -> int {
|
||||||
for i in 0..<len(text) {
|
for i in 0..<len(text) {
|
||||||
if SPECIALS_TABLE[text[i]] {
|
if SPECIALS_TABLE[text[i]] {
|
||||||
@@ -583,60 +608,52 @@ index_special :: proc(text: string) -> int {
|
|||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
lmem_find :: proc(s1, s2: string) -> int {
|
lmem_find :: proc(s1, s2: string) -> int {
|
||||||
l1 := len(s1)
|
l1 := len(s1)
|
||||||
l2 := len(s2)
|
l2 := len(s2)
|
||||||
|
|
||||||
if l2 == 0 {
|
if l2 == 0 {
|
||||||
return 0
|
return 0
|
||||||
} else if l2 > l1 {
|
}
|
||||||
|
if l2 > l1 {
|
||||||
return -1
|
return -1
|
||||||
} else {
|
}
|
||||||
|
|
||||||
init := strings.index_byte(s1, s2[0])
|
init := strings.index_byte(s1, s2[0])
|
||||||
end := init + l2
|
end := init + l2
|
||||||
|
|
||||||
for end <= l1 && init != -1 {
|
for end <= l1 && init >= 0 {
|
||||||
init += 1
|
init += 1
|
||||||
|
|
||||||
if s1[init - 1:end] == s2 {
|
if s1[init - 1:end] == s2 {
|
||||||
return init - 1
|
return init - 1
|
||||||
} else {
|
}
|
||||||
next := strings.index_byte(s1[init:], s2[0])
|
next := strings.index_byte(s1[init:], s2[0])
|
||||||
|
|
||||||
if next == -1 {
|
if next == -1 {
|
||||||
return -1
|
return -1
|
||||||
} else {
|
}
|
||||||
init = init + next
|
init = init + next
|
||||||
end = init + l2
|
end = init + l2
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
// find a pattern with in a haystack with an offset
|
// find a pattern with in a haystack with an offset
|
||||||
// allow_memfind will speed up simple searches
|
// allow_memfind will speed up simple searches
|
||||||
find_aux :: proc(
|
find_aux :: proc(haystack, pattern: string, offset: int, allow_memfind: bool, matches: ^[MAX_CAPTURES]Match) -> (captures: int, err: Error) {
|
||||||
haystack: string,
|
|
||||||
pattern: string,
|
|
||||||
offset: int,
|
|
||||||
allow_memfind: bool,
|
|
||||||
matches: ^[MAX_CAPTURES]Match,
|
|
||||||
) -> (captures: int, err: Error) {
|
|
||||||
s := offset
|
s := offset
|
||||||
p := 0
|
p := 0
|
||||||
|
|
||||||
specials_idx := index_special(pattern)
|
specials_idx := index_special(pattern)
|
||||||
if allow_memfind && specials_idx == -1 {
|
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) }
|
matches[0] = { index + s, index + s + len(pattern) }
|
||||||
captures = 1
|
captures = 1
|
||||||
return
|
|
||||||
} else {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pattern := pattern
|
pattern := pattern
|
||||||
@@ -684,11 +701,8 @@ find_aux :: proc(
|
|||||||
// rest has to be used from captures
|
// rest has to be used from captures
|
||||||
// assumes captures is zeroed on first iteration
|
// assumes captures is zeroed on first iteration
|
||||||
// resets captures to zero on last iteration
|
// resets captures to zero on last iteration
|
||||||
gmatch :: proc(
|
@(require_results)
|
||||||
haystack: ^string,
|
gmatch :: proc(haystack: ^string, pattern: string, captures: ^[MAX_CAPTURES]Match) -> (res: string, ok: bool) {
|
||||||
pattern: string,
|
|
||||||
captures: ^[MAX_CAPTURES]Match,
|
|
||||||
) -> (res: string, ok: bool) {
|
|
||||||
haystack^ = haystack[captures[0].byte_end:]
|
haystack^ = haystack[captures[0].byte_end:]
|
||||||
if len(haystack) > 0 {
|
if len(haystack) > 0 {
|
||||||
length, err := find_aux(haystack^, pattern, 0, false, captures)
|
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 with builder, replace patterns found with the replace content
|
||||||
gsub_builder :: proc(
|
@(require_results)
|
||||||
builder: ^strings.Builder,
|
gsub_builder :: proc(builder: ^strings.Builder, haystack, pattern, replace: string) -> string {
|
||||||
haystack: string,
|
|
||||||
pattern: string,
|
|
||||||
replace: string,
|
|
||||||
) -> string {
|
|
||||||
// find matches
|
// find matches
|
||||||
captures: [MAX_CAPTURES]Match
|
captures: [MAX_CAPTURES]Match
|
||||||
haystack := haystack
|
haystack := haystack
|
||||||
|
|
||||||
for {
|
for {
|
||||||
length, err := find_aux(haystack, pattern, 0, false, &captures)
|
length, err := find_aux(haystack, pattern, 0, false, &captures)
|
||||||
|
if length == 0 { // done
|
||||||
// done
|
|
||||||
if length == 0 {
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != .OK {
|
if err != .OK {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
@@ -746,12 +753,8 @@ gsub_builder :: proc(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// uses temp builder to build initial string - then allocates the result
|
// uses temp builder to build initial string - then allocates the result
|
||||||
gsub_allocator :: proc(
|
@(require_results)
|
||||||
haystack: string,
|
gsub_allocator :: proc(haystack, pattern, replace: string, allocator := context.allocator) -> string {
|
||||||
pattern: string,
|
|
||||||
replace: string,
|
|
||||||
allocator := context.allocator,
|
|
||||||
) -> string {
|
|
||||||
builder := strings.builder_make(0, 256, context.temp_allocator)
|
builder := strings.builder_make(0, 256, context.temp_allocator)
|
||||||
return gsub_builder(&builder, haystack, pattern, replace)
|
return gsub_builder(&builder, haystack, pattern, replace)
|
||||||
}
|
}
|
||||||
@@ -768,20 +771,14 @@ Gsub_Proc :: proc(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// call a procedure on every match in the haystack
|
// call a procedure on every match in the haystack
|
||||||
gsub_with :: proc(
|
gsub_with :: proc(haystack, pattern: string, data: rawptr, call: Gsub_Proc) {
|
||||||
haystack: string,
|
|
||||||
pattern: string,
|
|
||||||
data: rawptr,
|
|
||||||
call: Gsub_Proc,
|
|
||||||
) {
|
|
||||||
// find matches
|
// find matches
|
||||||
captures: [MAX_CAPTURES]Match
|
captures: [MAX_CAPTURES]Match
|
||||||
haystack := haystack
|
haystack := haystack
|
||||||
|
|
||||||
for {
|
for {
|
||||||
length := find_aux(haystack, pattern, 0, false, &captures) or_break
|
length := find_aux(haystack, pattern, 0, false, &captures) or_break
|
||||||
// done
|
if length == 0 { // done
|
||||||
if length == 0 {
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -800,11 +797,8 @@ gsub :: proc { gsub_builder, gsub_allocator }
|
|||||||
// iterative find with zeroth capture only
|
// iterative find with zeroth capture only
|
||||||
// assumes captures is zeroed on first iteration
|
// assumes captures is zeroed on first iteration
|
||||||
// resets captures to zero on last iteration
|
// resets captures to zero on last iteration
|
||||||
gfind :: proc(
|
@(require_results)
|
||||||
haystack: ^string,
|
gfind :: proc(haystack: ^string, pattern: string, captures: ^[MAX_CAPTURES]Match) -> (res: string, ok: bool) {
|
||||||
pattern: string,
|
|
||||||
captures: ^[MAX_CAPTURES]Match,
|
|
||||||
) -> (res: string, ok: bool) {
|
|
||||||
haystack^ = haystack[captures[0].byte_end:]
|
haystack^ = haystack[captures[0].byte_end:]
|
||||||
if len(haystack) > 0 {
|
if len(haystack) > 0 {
|
||||||
length, err := find_aux(haystack^, pattern, 0, true, captures)
|
length, err := find_aux(haystack^, pattern, 0, true, captures)
|
||||||
@@ -822,10 +816,8 @@ gfind :: proc(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// rebuilds a pattern into a case insensitive pattern
|
// rebuilds a pattern into a case insensitive pattern
|
||||||
pattern_case_insensitive_builder :: proc(
|
@(require_results)
|
||||||
builder: ^strings.Builder,
|
pattern_case_insensitive_builder :: proc(builder: ^strings.Builder, pattern: string) -> string {
|
||||||
pattern: string,
|
|
||||||
) -> (res: string) {
|
|
||||||
p := pattern
|
p := pattern
|
||||||
last_percent: bool
|
last_percent: bool
|
||||||
|
|
||||||
@@ -849,11 +841,8 @@ pattern_case_insensitive_builder :: proc(
|
|||||||
return strings.to_string(builder^)
|
return strings.to_string(builder^)
|
||||||
}
|
}
|
||||||
|
|
||||||
pattern_case_insensitive_allocator :: proc(
|
@(require_results)
|
||||||
pattern: string,
|
pattern_case_insensitive_allocator :: proc(pattern: string, cap: int = 256, allocator := context.allocator) -> string {
|
||||||
cap: int = 256,
|
|
||||||
allocator := context.allocator,
|
|
||||||
) -> (res: string) {
|
|
||||||
builder := strings.builder_make(0, cap, context.temp_allocator)
|
builder := strings.builder_make(0, cap, context.temp_allocator)
|
||||||
return pattern_case_insensitive_builder(&builder, pattern)
|
return pattern_case_insensitive_builder(&builder, pattern)
|
||||||
}
|
}
|
||||||
@@ -877,6 +866,7 @@ Matcher :: struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// init using haystack & pattern and an optional byte offset
|
// init using haystack & pattern and an optional byte offset
|
||||||
|
@(require_results)
|
||||||
matcher_init :: proc(haystack, pattern: string, offset: int = 0) -> (res: Matcher) {
|
matcher_init :: proc(haystack, pattern: string, offset: int = 0) -> (res: Matcher) {
|
||||||
res.haystack = haystack
|
res.haystack = haystack
|
||||||
res.pattern = pattern
|
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
|
// 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_find :: proc(matcher: ^Matcher) -> (start, end: int, ok: bool) #no_bounds_check {
|
||||||
matcher.captures_length, matcher.err = find_aux(
|
matcher.captures_length, matcher.err = find_aux(
|
||||||
matcher.haystack,
|
matcher.haystack,
|
||||||
matcher.pattern,
|
matcher.pattern,
|
||||||
matcher.offset,
|
matcher.offset,
|
||||||
true,
|
allow_memfind=true,
|
||||||
&matcher.captures,
|
matches=&matcher.captures,
|
||||||
)
|
)
|
||||||
ok = matcher.captures_length > 0 && matcher.err == .OK
|
ok = matcher.captures_length > 0 && matcher.err == .OK
|
||||||
match := matcher.captures[0]
|
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
|
// 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_match :: proc(matcher: ^Matcher) -> (word: string, ok: bool) #no_bounds_check {
|
||||||
matcher.captures_length, matcher.err = find_aux(
|
matcher.captures_length, matcher.err = find_aux(
|
||||||
matcher.haystack,
|
matcher.haystack,
|
||||||
matcher.pattern,
|
matcher.pattern,
|
||||||
matcher.offset,
|
matcher.offset,
|
||||||
false,
|
allow_memfind=false,
|
||||||
&matcher.captures,
|
matches=&matcher.captures,
|
||||||
)
|
)
|
||||||
ok = matcher.captures_length > 0 && matcher.err == .OK
|
ok = matcher.captures_length > 0 && matcher.err == .OK
|
||||||
match := matcher.captures[0]
|
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
|
// 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 {
|
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)
|
runtime.bounds_check_error_loc(loc, index + 1, MAX_CAPTURES - 1)
|
||||||
cap := matcher.captures[index + 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
|
// 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 {
|
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)
|
runtime.bounds_check_error_loc(loc, index + 1, MAX_CAPTURES - 1)
|
||||||
return matcher.captures[index + 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
|
matcher_gmatch :: matcher_match_iter
|
||||||
|
|
||||||
// iteratively match the haystack till it cant find any matches
|
// iteratively match the haystack till it cant find any matches
|
||||||
|
@(require_results)
|
||||||
matcher_match_iter :: proc(matcher: ^Matcher) -> (res: string, index: int, ok: bool) {
|
matcher_match_iter :: proc(matcher: ^Matcher) -> (res: string, index: int, ok: bool) {
|
||||||
if len(matcher.iter) > 0 {
|
if len(matcher.iter) > 0 {
|
||||||
matcher.captures_length, matcher.err = find_aux(
|
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
|
// get a slice of all valid captures above the first match
|
||||||
|
@(require_results)
|
||||||
matcher_captures_slice :: proc(matcher: ^Matcher) -> []Match {
|
matcher_captures_slice :: proc(matcher: ^Matcher) -> []Match {
|
||||||
return matcher.captures[1:matcher.captures_length]
|
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;
|
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);
|
ObjcMsgKind kind = get_objc_proc_kind(return_type);
|
||||||
|
|
||||||
Scope *scope = create_scope(c->info, nullptr);
|
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_fpret");
|
||||||
try_to_add_package_dependency(c, "runtime", "objc_msgSend_fp2ret");
|
try_to_add_package_dependency(c, "runtime", "objc_msgSend_fp2ret");
|
||||||
try_to_add_package_dependency(c, "runtime", "objc_msgSend_stret");
|
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_) {
|
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;
|
isize capture_arg_count = ce->args.count - 1;
|
||||||
|
|
||||||
// NOTE(harold): The first parameter is already checked at check_builtin_procedure().
|
// NOTE(harold): The first argument is already checked at check_builtin_procedure().
|
||||||
// Checking again would invalidate the Entity -> Value map for direct parameters if it's the handler proc.
|
// Checking again would invalidate the Entity -> Value map for direct arguments if it's the handler proc.
|
||||||
param_operands[0] = *operand;
|
param_operands[0] = *operand;
|
||||||
|
|
||||||
for (isize i = 0; i < ce->args.count-1; i++) {
|
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;
|
operand->mode = Addressing_Value;
|
||||||
return true;
|
return true;
|
||||||
} break;
|
} 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_register_class:
|
||||||
case BuiltinProc_objc_ivar_get:
|
case BuiltinProc_objc_ivar_get:
|
||||||
case BuiltinProc_objc_block:
|
case BuiltinProc_objc_block:
|
||||||
|
case BuiltinProc_objc_super:
|
||||||
return check_builtin_objc_procedure(c, operand, call, id, type_hint);
|
return check_builtin_objc_procedure(c, operand, call, id, type_hint);
|
||||||
|
|
||||||
case BuiltinProc___entry_point:
|
case BuiltinProc___entry_point:
|
||||||
|
|||||||
+48
-10
@@ -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;
|
super = named_type->Named.type_name->TypeName.objc_superclass;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (ac.objc_superclass != nullptr) {
|
if (ac.objc_ivar != nullptr) {
|
||||||
error(e->token, "@(objc_superclass) may only be applied when the @(obj_implement) attribute is also applied");
|
|
||||||
} else if (ac.objc_ivar != nullptr) {
|
|
||||||
error(e->token, "@(objc_ivar) may only be applied when the @(obj_implement) attribute is also applied");
|
error(e->token, "@(objc_ivar) may only be applied when the @(obj_implement) attribute is also applied");
|
||||||
} else if (ac.objc_context_provider != nullptr) {
|
} 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");
|
error(e->token, "@(objc_context_provider) may only be applied when the @(obj_implement) attribute is also applied");
|
||||||
@@ -1084,17 +1082,30 @@ gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeCon
|
|||||||
// Enable implementation by default if the class is an implementer too and
|
// Enable implementation by default if the class is an implementer too and
|
||||||
// @objc_implement was not set to false explicitly in this proc.
|
// @objc_implement was not set to false explicitly in this proc.
|
||||||
bool implement = tn->TypeName.objc_is_implementation;
|
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) {
|
if (ac.objc_is_disabled_implement) {
|
||||||
implement = false;
|
implement = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (implement) {
|
String objc_selector = ac.objc_selector != "" ? ac.objc_selector : ac.objc_name;
|
||||||
GB_ASSERT(e->kind == Entity_Procedure);
|
|
||||||
|
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;
|
auto &proc = e->type->Proc;
|
||||||
Type *first_param = proc.param_count > 0 ? proc.params->Tuple.variables[0]->type : t_untyped_nil;
|
Type *first_param = proc.param_count > 0 ? proc.params->Tuple.variables[0]->type : t_untyped_nil;
|
||||||
|
|
||||||
if (!tn->TypeName.objc_is_implementation) {
|
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");
|
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))) {
|
} 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)");
|
error(e->token, "Objective-C instance methods implementations require the first parameter to be a pointer to the class type set by @(objc_type)");
|
||||||
@@ -1119,7 +1130,7 @@ gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeCon
|
|||||||
ac.linkage = STR_LIT("strong");
|
ac.linkage = STR_LIT("strong");
|
||||||
|
|
||||||
auto method = ObjcMethodData{ ac, e };
|
auto method = ObjcMethodData{ ac, e };
|
||||||
method.ac.objc_selector = ac.objc_selector != "" ? ac.objc_selector : ac.objc_name;
|
method.ac.objc_selector = objc_selector;
|
||||||
|
|
||||||
CheckerInfo *info = ctx->info;
|
CheckerInfo *info = ctx->info;
|
||||||
mutex_lock(&info->objc_method_mutex);
|
mutex_lock(&info->objc_method_mutex);
|
||||||
@@ -1135,9 +1146,35 @@ gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeCon
|
|||||||
map_set(&info->objc_method_implementations, t, list);
|
map_set(&info->objc_method_implementations, t, list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (ac.objc_selector != "") {
|
} else if (!has_body) {
|
||||||
error(e->token, "@(objc_selector) may only be applied to procedures that are Objective-C implementations.");
|
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 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);
|
mutex_lock(&global_type_name_objc_metadata_mutex);
|
||||||
defer (mutex_unlock(&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) {
|
if (!pt->is_polymorphic) {
|
||||||
check_procedure_later(ctx->checker, ctx->file, e->token, d, proc_type, pl->body, pl->tags);
|
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) {
|
if (e->Procedure.is_export) {
|
||||||
error(e->token, "Foreign export procedures must have a body");
|
error(e->token, "Foreign export procedures must have a body");
|
||||||
} else {
|
} 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
|
// 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);
|
mpsc_enqueue(&ctx->info->foreign_decls_to_check, e);
|
||||||
} else {
|
} 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);
|
check_foreign_procedure(ctx, e, d);
|
||||||
}
|
}
|
||||||
} else {
|
} 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);
|
error_line("\t Got: %s\n", s_got);
|
||||||
gb_string_free(s_got);
|
gb_string_free(s_got);
|
||||||
gb_string_free(s_expected);
|
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 {
|
} else {
|
||||||
gbString s_expected = type_to_string(y);
|
gbString s_expected = type_to_string(y);
|
||||||
gbString s_got = type_to_string(x);
|
gbString s_got = type_to_string(x);
|
||||||
@@ -6216,7 +6211,6 @@ gb_internal isize get_procedure_param_count_excluding_defaults(Type *pt, isize *
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7488,8 +7482,6 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
|
|||||||
Entity *e = proc_entities[valids[0].index];
|
Entity *e = proc_entities[valids[0].index];
|
||||||
GB_ASSERT(e != nullptr);
|
GB_ASSERT(e != nullptr);
|
||||||
|
|
||||||
Array<Operand> named_operands = {};
|
|
||||||
|
|
||||||
check_call_arguments_single(c, call, operand,
|
check_call_arguments_single(c, call, operand,
|
||||||
e, e->type,
|
e, e->type,
|
||||||
positional_operands, named_operands,
|
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) {
|
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 &&
|
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;
|
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_SEL = alloc_type_pointer(t_objc_selector);
|
||||||
t_objc_Class = alloc_type_pointer(t_objc_class);
|
t_objc_Class = alloc_type_pointer(t_objc_class);
|
||||||
t_objc_Ivar = alloc_type_pointer(t_objc_ivar);
|
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);
|
map_destroy(&i->objc_msgSend_types);
|
||||||
string_set_destroy(&i->obcj_class_name_set);
|
string_set_destroy(&i->obcj_class_name_set);
|
||||||
mpsc_destroy(&i->objc_class_implementations);
|
|
||||||
map_destroy(&i->objc_method_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_file_cache);
|
||||||
string_map_destroy(&i->load_directory_cache);
|
string_map_destroy(&i->load_directory_cache);
|
||||||
map_destroy(&i->load_directory_map);
|
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);
|
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) {
|
gb_internal void init_preload(Checker *c) {
|
||||||
init_core_type_info(c);
|
init_core_type_info(c);
|
||||||
init_mem_allocator(c);
|
init_mem_allocator(c);
|
||||||
init_core_context(c);
|
init_core_context(c);
|
||||||
init_core_source_code_location(c);
|
init_core_source_code_location(c);
|
||||||
init_core_map_type(c);
|
init_core_map_type(c);
|
||||||
|
init_core_objc_c(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
gb_internal ExactValue check_decl_attribute_value(CheckerContext *c, Ast *value) {
|
gb_internal ExactValue check_decl_attribute_value(CheckerContext *c, Ast *value) {
|
||||||
|
|||||||
@@ -354,6 +354,7 @@ BuiltinProc__type_end,
|
|||||||
BuiltinProc_objc_register_class,
|
BuiltinProc_objc_register_class,
|
||||||
BuiltinProc_objc_ivar_get,
|
BuiltinProc_objc_ivar_get,
|
||||||
BuiltinProc_objc_block,
|
BuiltinProc_objc_block,
|
||||||
|
BuiltinProc_objc_super,
|
||||||
|
|
||||||
BuiltinProc_constant_utf16_cstring,
|
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_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_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_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},
|
{STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||||
|
|
||||||
|
|||||||
@@ -251,6 +251,8 @@ struct Entity {
|
|||||||
String link_name;
|
String link_name;
|
||||||
String link_prefix;
|
String link_prefix;
|
||||||
String link_suffix;
|
String link_suffix;
|
||||||
|
String objc_selector_name;
|
||||||
|
Entity *objc_class;
|
||||||
DeferredProcedure deferred_procedure;
|
DeferredProcedure deferred_procedure;
|
||||||
|
|
||||||
struct GenProcsData *gen_procs;
|
struct GenProcsData *gen_procs;
|
||||||
@@ -266,6 +268,8 @@ struct Entity {
|
|||||||
bool is_anonymous : 1;
|
bool is_anonymous : 1;
|
||||||
bool no_sanitize_address : 1;
|
bool no_sanitize_address : 1;
|
||||||
bool no_sanitize_memory : 1;
|
bool no_sanitize_memory : 1;
|
||||||
|
bool is_objc_impl_or_import : 1;
|
||||||
|
bool is_objc_class_method : 1;
|
||||||
} Procedure;
|
} Procedure;
|
||||||
struct {
|
struct {
|
||||||
Array<Entity *> entities;
|
Array<Entity *> entities;
|
||||||
|
|||||||
+110
-46
@@ -1417,8 +1417,21 @@ String lb_get_objc_type_encoding(Type *t, isize pointer_depth = 0) {
|
|||||||
return str_lit("?");
|
return str_lit("?");
|
||||||
case Type_Proc:
|
case Type_Proc:
|
||||||
return str_lit("?");
|
return str_lit("?");
|
||||||
case Type_BitSet:
|
case Type_BitSet: {
|
||||||
return lb_get_objc_type_encoding(t->BitSet.underlying, pointer_depth);
|
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: {
|
case Type_SimdVector: {
|
||||||
String type_str = lb_get_objc_type_encoding(t->SimdVector.elem, pointer_depth);
|
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 {
|
struct lbObjCGlobalClass {
|
||||||
lbObjCGlobal g;
|
lbObjCGlobal g;
|
||||||
|
union {
|
||||||
lbValue class_value; // Local registered class value
|
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(
|
gb_internal void lb_register_objc_thing(
|
||||||
@@ -1482,38 +1498,36 @@ gb_internal void lb_register_objc_thing(
|
|||||||
LLVMSetInitializer(v.value, LLVMConstNull(t));
|
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 this class requires an implementation, save it for registration below.
|
||||||
if (g.class_impl_type != nullptr) {
|
if (g.class_impl_type != nullptr) {
|
||||||
|
|
||||||
// Make sure the superclass has been initialized before us
|
// 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;
|
auto &tn = g.class_impl_type->Named.type_name->TypeName;
|
||||||
Type *superclass = tn.objc_superclass;
|
Type *superclass = tn.objc_superclass;
|
||||||
if (superclass != nullptr) {
|
if (superclass != nullptr) {
|
||||||
auto& superclass_global = string_map_must_get(&class_map, superclass->Named.type_name->TypeName.objc_class_name);
|
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);
|
lb_register_objc_thing(handled, m, args, class_impls, class_map, p, superclass_global.g, call);
|
||||||
GB_ASSERT(superclass_global.class_value.value);
|
GB_ASSERT(superclass_global.class_global.addr.value);
|
||||||
|
|
||||||
superclass_value = superclass_global.class_value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
args.count = 3;
|
lbObjCGlobalClass impl_global = {};
|
||||||
args[0] = superclass_value;
|
impl_global.g = g;
|
||||||
args[1] = class_name;
|
impl_global.class_global = addr;
|
||||||
args[2] = lb_const_int(m, t_uint, 0);
|
|
||||||
class_ptr = lb_emit_runtime_call(p, "objc_allocateClassPair", args);
|
|
||||||
|
|
||||||
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 {
|
else {
|
||||||
|
lbValue class_ptr = {};
|
||||||
|
lbValue class_name = lb_const_value(m, t_cstring, exact_value_string(g.name));
|
||||||
|
|
||||||
args.count = 1;
|
args.count = 1;
|
||||||
args[0] = class_name;
|
args[0] = class_name;
|
||||||
class_ptr = lb_emit_runtime_call(p, call, args);
|
class_ptr = lb_emit_runtime_call(p, call, args);
|
||||||
}
|
|
||||||
|
|
||||||
lb_addr_store(p, addr, class_ptr);
|
lb_addr_store(p, addr, class_ptr);
|
||||||
|
|
||||||
@@ -1521,6 +1535,7 @@ gb_internal void lb_register_objc_thing(
|
|||||||
if (class_global != nullptr) {
|
if (class_global != nullptr) {
|
||||||
class_global->class_value = class_ptr;
|
class_global->class_value = class_ptr;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
|
gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
|
||||||
@@ -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);
|
string_map_init(&global_class_map, (usize)gen->objc_classes.count);
|
||||||
defer (string_map_destroy(&global_class_map));
|
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});
|
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) {
|
for (const auto &cd : class_impls) {
|
||||||
auto &g = cd.g;
|
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);
|
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;
|
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);
|
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);
|
Array<ObjcMethodData> *methods = map_get(&m->info->objc_method_implementations, class_type);
|
||||||
if (!methods) {
|
if (!methods) {
|
||||||
continue;
|
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);
|
wrapper_results_tuple, method_type->Proc.result_count, false, ProcCC_CDecl);
|
||||||
|
|
||||||
lbProcedure *wrapper_proc = lb_create_dummy_procedure(m, proc_name, wrapper_proc_type);
|
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
|
// 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);
|
lb_begin_procedure_body(wrapper_proc);
|
||||||
{
|
{
|
||||||
|
LLVMValueRef context_addr = nullptr;
|
||||||
if (method_type->Proc.calling_convention == ProcCC_Odin) {
|
if (method_type->Proc.calling_convention == ProcCC_Odin) {
|
||||||
GB_ASSERT(context_provider);
|
GB_ASSERT(context_provider);
|
||||||
|
|
||||||
// Emit the get odin context call
|
// Emit the get odin context call
|
||||||
|
|
||||||
get_context_args[0] = lbValue {
|
get_context_args[0] = lbValue {
|
||||||
wrapper_proc->raw_input_parameters[0],
|
wrapper_proc->raw_input_parameters[0],
|
||||||
contex_provider_self_ptr_type,
|
contex_provider_self_ptr_type,
|
||||||
@@ -1737,43 +1782,57 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lbValue context = lb_emit_call(wrapper_proc, context_provider_proc_value, get_context_args);
|
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));
|
context_addr = lb_address_from_load(wrapper_proc, context).value;//lb_address_from_load_or_generate_local(wrapper_proc, context));
|
||||||
lb_push_context_onto_stack(wrapper_proc, context_addr);
|
// 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) {
|
if (!md.ac.objc_is_class_method) {
|
||||||
method_call_args[0] = lbValue {
|
array_add(&raw_method_args, wrapper_proc->raw_input_parameters[method_forward_return_arg_offset]);
|
||||||
wrapper_proc->raw_input_parameters[0],
|
|
||||||
class_ptr_type,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (isize i = 0; i < method_param_count; i++) {
|
for (isize i = 0; i < method_param_count; i++) {
|
||||||
method_call_args[i+method_param_offset] = lbValue {
|
array_add(&raw_method_args, wrapper_proc->raw_input_parameters[i+2+method_forward_return_arg_offset]);
|
||||||
wrapper_proc->raw_input_parameters[i+2],
|
}
|
||||||
method_type->Proc.params->Tuple.variables[i+method_param_offset]->type,
|
|
||||||
};
|
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.
|
// 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) {
|
if (has_return && return_kind != lbArg_Indirect) {
|
||||||
auto &result_var = method_type->Proc.results->Tuple.variables[0];
|
LLVMBuildRet(wrapper_proc->builder, ret_val_raw);
|
||||||
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);
|
else {
|
||||||
|
LLVMBuildRetVoid(wrapper_proc->builder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lb_end_procedure_body(wrapper_proc);
|
lb_end_procedure_body(wrapper_proc);
|
||||||
|
|
||||||
|
|
||||||
// Add the method to the class
|
// Add the method to the class
|
||||||
String method_encoding = str_lit("v");
|
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);
|
GB_ASSERT(method_type->Proc.result_count <= 1);
|
||||||
if (method_type->Proc.result_count != 0) {
|
if (method_type->Proc.result_count != 0) {
|
||||||
method_encoding = lb_get_objc_type_encoding(method_type->Proc.results->Tuple.variables[0]->type);
|
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("#:"));
|
method_encoding = concatenate_strings(temporary_allocator(), method_encoding, str_lit("#:"));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (isize i = method_param_offset; i < method_param_count; i++) {
|
for (isize i = 0; i < method_param_count; i++) {
|
||||||
Type *param_type = method_type->Proc.params->Tuple.variables[i]->type;
|
Type *param_type = method_type->Proc.params->Tuple.variables[i + method_param_offset]->type;
|
||||||
String param_encoding = lb_get_objc_type_encoding(param_type);
|
String param_encoding = lb_get_objc_type_encoding(param_type);
|
||||||
|
|
||||||
method_encoding = concatenate_strings(temporary_allocator(), method_encoding, param_encoding);
|
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[2] = lbValue { wrapper_proc->value, wrapper_proc->type };
|
||||||
args[3] = lb_const_value(m, t_cstring, exact_value_string(method_encoding));
|
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);
|
lb_emit_runtime_call(p, "class_addMethod", args);
|
||||||
|
|
||||||
} // End methods
|
} // 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
|
// Defined in an external package, define it now in the main package
|
||||||
LLVMTypeRef t = lb_type(m, t_int);
|
LLVMTypeRef t = lb_type(m, t_int);
|
||||||
|
|
||||||
lbValue global{};
|
lbValue global = {};
|
||||||
global.value = LLVMAddGlobal(m->mod, t, g.global_name);
|
global.value = LLVMAddGlobal(m->mod, t, g.global_name);
|
||||||
global.type = t_int_ptr;
|
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);
|
GB_ASSERT(m != nullptr);
|
||||||
|
|
||||||
if (e->kind == Entity_Procedure) {
|
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);
|
array_add(&m->global_procedures_to_create, e);
|
||||||
} else if (e->kind == Entity_TypeName) {
|
} else if (e->kind == Entity_TypeName) {
|
||||||
array_add(&m->global_types_to_create, e);
|
array_add(&m->global_types_to_create, e);
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ struct lbModule {
|
|||||||
StringMap<lbAddr> objc_classes;
|
StringMap<lbAddr> objc_classes;
|
||||||
StringMap<lbAddr> objc_selectors;
|
StringMap<lbAddr> objc_selectors;
|
||||||
StringMap<lbAddr> objc_ivars;
|
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_cell_info_map; // address of runtime.Map_Info
|
||||||
PtrMap<u64/*type hash*/, lbAddr> map_info_map; // address of runtime.Map_Cell_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_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_ivar_get: return lb_handle_objc_ivar_get(p, expr);
|
||||||
case BuiltinProc_objc_block: return lb_handle_objc_block(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:
|
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);
|
Ast *proc_expr = unparen_expr(ce->proc);
|
||||||
|
Entity *proc_entity = entity_of_node(proc_expr);
|
||||||
|
|
||||||
if (proc_mode == Addressing_Builtin) {
|
if (proc_mode == Addressing_Builtin) {
|
||||||
Entity *e = entity_of_node(proc_expr);
|
|
||||||
BuiltinProcId id = BuiltinProc_Invalid;
|
BuiltinProcId id = BuiltinProc_Invalid;
|
||||||
if (e != nullptr) {
|
if (proc_entity != nullptr) {
|
||||||
id = cast(BuiltinProcId)e->Builtin.id;
|
id = cast(BuiltinProcId)proc_entity->Builtin.id;
|
||||||
} else {
|
} else {
|
||||||
id = BuiltinProc_DIRECTIVE;
|
id = BuiltinProc_DIRECTIVE;
|
||||||
}
|
}
|
||||||
return lb_build_builtin_proc(p, expr, tv, id);
|
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
|
// NOTE(bill): Regular call
|
||||||
lbValue value = {};
|
lbValue value = {};
|
||||||
|
|
||||||
Entity *proc_entity = entity_of_node(proc_expr);
|
|
||||||
if (proc_entity != nullptr) {
|
if (proc_entity != nullptr) {
|
||||||
if (proc_entity->flags & EntityFlag_Disabled) {
|
if (proc_entity->flags & EntityFlag_Disabled) {
|
||||||
GB_ASSERT(tv.type == nullptr);
|
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);
|
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);
|
Type *proc_type_ = base_type(value.type);
|
||||||
GB_ASSERT(proc_type_->kind == Type_Proc);
|
GB_ASSERT(proc_type_->kind == Type_Proc);
|
||||||
TypeProc *pt = &proc_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;
|
isize final_count = is_c_vararg ? args.count : pt->param_count;
|
||||||
auto call_args = array_slice(args, 0, final_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);
|
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_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), "");
|
res.value = LLVMBuildBitCast(p->builder, value.value, lb_type(p->module, t), "");
|
||||||
return res;
|
return res;
|
||||||
} else if (is_type_array_like(src) && (is_type_simd_vector(dst) || is_type_integer_128bit(dst))) {
|
} 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(
|
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,
|
Slice<lbValue> capture_values, Slice<isize> objc_object_indices,
|
||||||
lbProcedure *&out_copy_helper, lbProcedure *&out_dispose_helper
|
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 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(), ""), "__$objc_block_dispose_helper_%lld", m->objc_next_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
|
// copy: Block_Literal *dst, Block_Literal *src, i32 field_apropos
|
||||||
// dispose: 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://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/llvm/llvm-project/blob/21f1f9558df3830ffa637def364e3c0cb0dbb3c0/compiler-rt/lib/BlocksRuntime/Block_private.h
|
||||||
/// https://github.com/apple-oss-distributions/libclosure/blob/3668b0837f47be3cc1c404fb5e360f4ff178ca13/runtime.cpp
|
/// https://github.com/apple-oss-distributions/libclosure/blob/3668b0837f47be3cc1c404fb5e360f4ff178ca13/runtime.cpp
|
||||||
|
|
||||||
ast_node(ce, CallExpr, expr);
|
ast_node(ce, CallExpr, expr);
|
||||||
GB_ASSERT(ce->args.count > 0);
|
GB_ASSERT(ce->args.count > 0);
|
||||||
|
|
||||||
lbModule *m = p->module;
|
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;
|
const isize capture_arg_count = ce->args.count - 1;
|
||||||
|
|
||||||
Type *block_result_type = type_of_expr(expr);
|
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
|
// 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)
|
// (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
|
// 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);
|
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,
|
lbProcedure *invoker_proc = lb_create_dummy_procedure(m, make_string((u8*)block_invoker_name,
|
||||||
gb_string_length(block_invoker_name)), invoker_proc_type);
|
gb_string_length(block_invoker_name)), invoker_proc_type);
|
||||||
|
|
||||||
LLVMSetLinkage(invoker_proc->value, LLVMPrivateLinkage);
|
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
|
// Create the block descriptor and block literal
|
||||||
gbString block_lit_type_name = gb_string_make(temporary_allocator(), "__$ObjC_Block_Literal_");
|
gbString block_lit_type_name = gb_string_make(temporary_allocator(), "");
|
||||||
block_lit_type_name = gb_string_append_fmt(block_lit_type_name, "%lld", m->objc_next_block_id);
|
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_");
|
gbString block_desc_type_name = gb_string_make(temporary_allocator(), "");
|
||||||
block_desc_type_name = gb_string_append_fmt(block_desc_type_name, "%lld", m->objc_next_block_id);
|
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_lit_type = {};
|
||||||
LLVMTypeRef block_desc_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)
|
// Generate copy and dispose helper functions for captured params that are Objective-C objects (or a Block)
|
||||||
if (has_objc_fields) {
|
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(captured_values, 0, captured_values.count),
|
||||||
slice(objc_captures, 0, objc_captures.count),
|
slice(objc_captures, 0, objc_captures.count),
|
||||||
copy_helper, dispose_helper);
|
copy_helper, dispose_helper);
|
||||||
@@ -2521,8 +2528,8 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create global block descriptor
|
// Create global block descriptor
|
||||||
gbString desc_global_name = gb_string_make(temporary_allocator(), "__$objc_block_desc_");
|
gbString desc_global_name = gb_string_make(temporary_allocator(), "");
|
||||||
desc_global_name = gb_string_append_fmt(desc_global_name, "%lld", m->objc_next_block_id);
|
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);
|
LLVMValueRef p_descriptor = LLVMAddGlobal(m->mod, block_desc_type, desc_global_name);
|
||||||
LLVMSetInitializer(p_descriptor, block_desc_initializer);
|
LLVMSetInitializer(p_descriptor, block_desc_initializer);
|
||||||
@@ -2531,45 +2538,66 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
|
|||||||
/// Invoker body
|
/// Invoker body
|
||||||
lb_begin_procedure_body(invoker_proc);
|
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++) {
|
isize block_literal_arg_index = 0;
|
||||||
lbValue arg = {};
|
|
||||||
arg.type = invoker_args[i];
|
lbFunctionType* user_proc_ft = lb_get_function_type(m, user_proc_value.type);
|
||||||
arg.value = invoker_proc->raw_input_parameters[i],
|
|
||||||
call_args[i-1] = arg;
|
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
|
// Push context, if needed
|
||||||
if (user_proc.calling_convention == ProcCC_Odin) {
|
if (user_proc.calling_convention == ProcCC_Odin) {
|
||||||
LLVMValueRef p_context = LLVMBuildStructGEP2(invoker_proc->builder, block_lit_type, block_literal, 5, "context");
|
LLVMValueRef p_context = LLVMBuildStructGEP2(invoker_proc->builder, block_lit_type, block_literal, 5, "context");
|
||||||
lbValue ctx_val = {};
|
array_add(&call_args, p_context);
|
||||||
ctx_val.type = t_context_ptr;
|
|
||||||
ctx_val.value = p_context;
|
|
||||||
|
|
||||||
lb_push_context_onto_stack(invoker_proc, lb_addr(ctx_val));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy capture parameters from the block literal
|
LLVMTypeRef fnp = lb_type_internal_for_procedures_raw(m, user_proc_value.type);
|
||||||
for (isize i = 0; i < capture_arg_count; i++) {
|
LLVMValueRef ret_val = LLVMBuildCall2(invoker_proc->builder, fnp, user_proc_value.value, call_args.data, (unsigned)call_args.count, "");
|
||||||
LLVMValueRef cap_value = LLVMBuildStructGEP2(invoker_proc->builder, block_lit_type, block_literal, unsigned(capture_fields_offset + i), "");
|
|
||||||
|
|
||||||
lbValue cap_arg = {};
|
if (user_proc.result_count > 0 && return_kind != lbArg_Indirect) {
|
||||||
cap_arg.value = cap_value;
|
LLVMBuildRet(invoker_proc->builder, ret_val);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
lbValue result = lb_emit_call(invoker_proc, user_proc_value, call_args, proc_lit->ProcLit.inlining);
|
LLVMBuildRetVoid(invoker_proc->builder);
|
||||||
|
|
||||||
GB_ASSERT(user_proc.result_count <= 1);
|
|
||||||
if (user_proc.result_count > 0) {
|
|
||||||
GB_ASSERT(result.value != nullptr);
|
|
||||||
LLVMBuildRet(p->builder, result.value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lb_end_procedure_body(invoker_proc);
|
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_");
|
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 = {};
|
lbValue block_result = {};
|
||||||
result.type = block_result_type;
|
block_result.type = block_result_type;
|
||||||
|
|
||||||
lbValue isa_val = lb_find_runtime_value(m, is_global ? str_lit("_NSConcreteGlobalBlock") : str_lit("_NSConcreteStackBlock"));
|
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);
|
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) {
|
if (is_global) {
|
||||||
LLVMValueRef p_block_lit = LLVMAddGlobal(m->mod, block_lit_type, block_var_name);
|
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] = {
|
LLVMValueRef fields_values[5] = {
|
||||||
isa_val.value, // isa
|
isa_val.value, // isa
|
||||||
@@ -2611,7 +2639,7 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
LLVMValueRef p_block_lit = llvm_alloca(p, block_lit_type, lb_alignof(block_lit_type), block_var_name);
|
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
|
// Initialize it
|
||||||
LLVMValueRef f_isa = LLVMBuildStructGEP2(p->builder, block_lit_type, p_block_lit, 0, "isa");
|
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) {
|
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);
|
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_internal LLVMAtomicOrdering llvm_atomic_ordering_from_odin(ExactValue const &value) {
|
||||||
GB_ASSERT(value.kind == ExactValue_Integer);
|
GB_ASSERT(value.kind == ExactValue_Integer);
|
||||||
i64 v = exact_value_to_i64(value);
|
i64 v = exact_value_to_i64(value);
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ struct TypeAndValue {
|
|||||||
Type * type;
|
Type * type;
|
||||||
AddressingMode mode;
|
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;
|
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_selector = nullptr;
|
||||||
gb_global Type *t_objc_class = nullptr;
|
gb_global Type *t_objc_class = nullptr;
|
||||||
gb_global Type *t_objc_ivar = 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_id = nullptr;
|
||||||
gb_global Type *t_objc_SEL = nullptr;
|
gb_global Type *t_objc_SEL = nullptr;
|
||||||
gb_global Type *t_objc_Class = nullptr;
|
gb_global Type *t_objc_Class = nullptr;
|
||||||
gb_global Type *t_objc_Ivar = 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 {
|
enum OdinAtomicMemoryOrder : i32 {
|
||||||
OdinAtomicMemoryOrder_relaxed = 0, // unordered
|
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);
|
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) {
|
gb_internal Type *get_struct_field_type(Type *t, isize index) {
|
||||||
t = base_type(type_deref(t));
|
t = base_type(type_deref(t));
|
||||||
GB_ASSERT(t->kind == Type_Struct);
|
GB_ASSERT(t->kind == Type_Struct);
|
||||||
|
|||||||
Reference in New Issue
Block a user