mirror of
https://github.com/odin-lang/Odin.git
synced 2025-12-29 17:34:34 +00:00
1357 lines
30 KiB
Odin
1357 lines
30 KiB
Odin
package reflect
|
|
|
|
import "core:runtime"
|
|
import "core:mem"
|
|
import "core:intrinsics"
|
|
_ :: intrinsics;
|
|
|
|
Type_Info :: runtime.Type_Info;
|
|
|
|
Type_Info_Named :: runtime.Type_Info_Named;
|
|
Type_Info_Integer :: runtime.Type_Info_Integer;
|
|
Type_Info_Rune :: runtime.Type_Info_Rune;
|
|
Type_Info_Float :: runtime.Type_Info_Float;
|
|
Type_Info_Complex :: runtime.Type_Info_Complex;
|
|
Type_Info_Quaternion :: runtime.Type_Info_Quaternion;
|
|
Type_Info_String :: runtime.Type_Info_String;
|
|
Type_Info_Boolean :: runtime.Type_Info_Boolean;
|
|
Type_Info_Any :: runtime.Type_Info_Any;
|
|
Type_Info_Type_Id :: runtime.Type_Info_Type_Id;
|
|
Type_Info_Pointer :: runtime.Type_Info_Pointer;
|
|
Type_Info_Multi_Pointer :: runtime.Type_Info_Multi_Pointer;
|
|
Type_Info_Procedure :: runtime.Type_Info_Procedure;
|
|
Type_Info_Array :: runtime.Type_Info_Array;
|
|
Type_Info_Enumerated_Array :: runtime.Type_Info_Enumerated_Array;
|
|
Type_Info_Dynamic_Array :: runtime.Type_Info_Dynamic_Array;
|
|
Type_Info_Slice :: runtime.Type_Info_Slice;
|
|
Type_Info_Tuple :: runtime.Type_Info_Tuple;
|
|
Type_Info_Struct :: runtime.Type_Info_Struct;
|
|
Type_Info_Union :: runtime.Type_Info_Union;
|
|
Type_Info_Enum :: runtime.Type_Info_Enum;
|
|
Type_Info_Map :: runtime.Type_Info_Map;
|
|
Type_Info_Bit_Set :: runtime.Type_Info_Bit_Set;
|
|
Type_Info_Simd_Vector :: runtime.Type_Info_Simd_Vector;
|
|
Type_Info_Relative_Pointer :: runtime.Type_Info_Relative_Pointer;
|
|
Type_Info_Relative_Slice :: runtime.Type_Info_Relative_Slice;
|
|
|
|
Type_Info_Enum_Value :: runtime.Type_Info_Enum_Value;
|
|
|
|
|
|
Type_Kind :: enum {
|
|
Invalid,
|
|
|
|
Named,
|
|
Integer,
|
|
Rune,
|
|
Float,
|
|
Complex,
|
|
Quaternion,
|
|
String,
|
|
Boolean,
|
|
Any,
|
|
Type_Id,
|
|
Pointer,
|
|
Multi_Pointer,
|
|
Procedure,
|
|
Array,
|
|
Enumerated_Array,
|
|
Dynamic_Array,
|
|
Slice,
|
|
Tuple,
|
|
Struct,
|
|
Union,
|
|
Enum,
|
|
Map,
|
|
Bit_Set,
|
|
Simd_Vector,
|
|
Relative_Pointer,
|
|
Relative_Slice,
|
|
}
|
|
|
|
|
|
type_kind :: proc(T: typeid) -> Type_Kind {
|
|
ti := type_info_of(T);
|
|
if ti != nil {
|
|
switch _ in ti.variant {
|
|
case Type_Info_Named: return .Named;
|
|
case Type_Info_Integer: return .Integer;
|
|
case Type_Info_Rune: return .Rune;
|
|
case Type_Info_Float: return .Float;
|
|
case Type_Info_Complex: return .Complex;
|
|
case Type_Info_Quaternion: return .Quaternion;
|
|
case Type_Info_String: return .String;
|
|
case Type_Info_Boolean: return .Boolean;
|
|
case Type_Info_Any: return .Any;
|
|
case Type_Info_Type_Id: return .Type_Id;
|
|
case Type_Info_Pointer: return .Pointer;
|
|
case Type_Info_Multi_Pointer: return .Multi_Pointer;
|
|
case Type_Info_Procedure: return .Procedure;
|
|
case Type_Info_Array: return .Array;
|
|
case Type_Info_Enumerated_Array: return .Enumerated_Array;
|
|
case Type_Info_Dynamic_Array: return .Dynamic_Array;
|
|
case Type_Info_Slice: return .Slice;
|
|
case Type_Info_Tuple: return .Tuple;
|
|
case Type_Info_Struct: return .Struct;
|
|
case Type_Info_Union: return .Union;
|
|
case Type_Info_Enum: return .Enum;
|
|
case Type_Info_Map: return .Map;
|
|
case Type_Info_Bit_Set: return .Bit_Set;
|
|
case Type_Info_Simd_Vector: return .Simd_Vector;
|
|
case Type_Info_Relative_Pointer: return .Relative_Pointer;
|
|
case Type_Info_Relative_Slice: return .Relative_Slice;
|
|
}
|
|
|
|
}
|
|
return .Invalid;
|
|
}
|
|
|
|
// TODO(bill): Better name
|
|
underlying_type_kind :: proc(T: typeid) -> Type_Kind {
|
|
return type_kind(runtime.typeid_base(T));
|
|
}
|
|
|
|
// TODO(bill): Better name
|
|
backing_type_kind :: proc(T: typeid) -> Type_Kind {
|
|
return type_kind(runtime.typeid_core(T));
|
|
}
|
|
|
|
|
|
type_info_base :: proc(info: ^Type_Info) -> ^Type_Info {
|
|
if info == nil { return nil; }
|
|
|
|
base := info;
|
|
loop: for {
|
|
#partial switch i in base.variant {
|
|
case Type_Info_Named: base = i.base;
|
|
case: break loop;
|
|
}
|
|
}
|
|
return base;
|
|
}
|
|
|
|
|
|
type_info_core :: proc(info: ^Type_Info) -> ^Type_Info {
|
|
if info == nil { return nil; }
|
|
|
|
base := info;
|
|
loop: for {
|
|
#partial switch i in base.variant {
|
|
case Type_Info_Named: base = i.base;
|
|
case Type_Info_Enum: base = i.base;
|
|
case: break loop;
|
|
}
|
|
}
|
|
return base;
|
|
}
|
|
type_info_base_without_enum :: type_info_core;
|
|
|
|
|
|
typeid_base :: proc(id: typeid) -> typeid {
|
|
ti := type_info_of(id);
|
|
ti = type_info_base(ti);
|
|
return ti.id;
|
|
}
|
|
typeid_core :: proc(id: typeid) -> typeid {
|
|
ti := type_info_base_without_enum(type_info_of(id));
|
|
return ti.id;
|
|
}
|
|
typeid_base_without_enum :: typeid_core;
|
|
|
|
typeid_elem :: proc(id: typeid) -> typeid {
|
|
ti := type_info_of(id);
|
|
if ti == nil { return nil; }
|
|
|
|
bits := 8*ti.size;
|
|
|
|
#partial switch v in ti.variant {
|
|
case Type_Info_Complex:
|
|
switch bits {
|
|
case 64: return f32;
|
|
case 128: return f64;
|
|
}
|
|
case Type_Info_Quaternion:
|
|
switch bits {
|
|
case 128: return f32;
|
|
case 256: return f64;
|
|
}
|
|
case Type_Info_Pointer: return v.elem.id;
|
|
case Type_Info_Multi_Pointer: return v.elem.id;
|
|
case Type_Info_Array: return v.elem.id;
|
|
case Type_Info_Enumerated_Array: return v.elem.id;
|
|
case Type_Info_Slice: return v.elem.id;
|
|
case Type_Info_Dynamic_Array: return v.elem.id;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
|
|
size_of_typeid :: proc(T: typeid) -> int {
|
|
if ti := type_info_of(T); ti != nil {
|
|
return ti.size;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
align_of_typeid :: proc(T: typeid) -> int {
|
|
if ti := type_info_of(T); ti != nil {
|
|
return ti.align;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
as_bytes :: proc(v: any) -> []byte {
|
|
if v != nil {
|
|
sz := size_of_typeid(v.id);
|
|
return mem.slice_ptr((^byte)(v.data), sz);
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
any_data :: #force_inline proc(v: any) -> (data: rawptr, id: typeid) {
|
|
return v.data, v.id;
|
|
}
|
|
|
|
is_nil :: proc(v: any) -> bool {
|
|
if v == nil {
|
|
return true;
|
|
}
|
|
data := as_bytes(v);
|
|
if data != nil {
|
|
return true;
|
|
}
|
|
for v in data {
|
|
if v != 0 {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
length :: proc(val: any) -> int {
|
|
if val == nil { return 0; }
|
|
|
|
#partial switch a in type_info_of(val.id).variant {
|
|
case Type_Info_Named:
|
|
return length({val.data, a.base.id});
|
|
|
|
case Type_Info_Pointer:
|
|
return length({val.data, a.elem.id});
|
|
|
|
case Type_Info_Array:
|
|
return a.count;
|
|
|
|
case Type_Info_Enumerated_Array:
|
|
return a.count;
|
|
|
|
case Type_Info_Slice:
|
|
return (^mem.Raw_Slice)(val.data).len;
|
|
|
|
case Type_Info_Dynamic_Array:
|
|
return (^mem.Raw_Dynamic_Array)(val.data).len;
|
|
|
|
case Type_Info_Map:
|
|
return (^mem.Raw_Map)(val.data).entries.len;
|
|
|
|
case Type_Info_String:
|
|
if a.is_cstring {
|
|
return len((^cstring)(val.data)^);
|
|
} else {
|
|
return (^mem.Raw_String)(val.data).len;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
capacity :: proc(val: any) -> int {
|
|
if val == nil { return 0; }
|
|
|
|
#partial switch a in type_info_of(val.id).variant {
|
|
case Type_Info_Named:
|
|
return capacity({val.data, a.base.id});
|
|
|
|
case Type_Info_Pointer:
|
|
return capacity({val.data, a.elem.id});
|
|
|
|
case Type_Info_Array:
|
|
return a.count;
|
|
|
|
case Type_Info_Enumerated_Array:
|
|
return a.count;
|
|
|
|
case Type_Info_Dynamic_Array:
|
|
return (^mem.Raw_Dynamic_Array)(val.data).cap;
|
|
|
|
case Type_Info_Map:
|
|
return (^mem.Raw_Map)(val.data).entries.cap;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
index :: proc(val: any, i: int, loc := #caller_location) -> any {
|
|
if val == nil { return nil; }
|
|
|
|
#partial switch a in type_info_of(val.id).variant {
|
|
case Type_Info_Named:
|
|
return index({val.data, a.base.id}, i, loc);
|
|
|
|
case Type_Info_Pointer:
|
|
ptr := (^rawptr)(val.data)^;
|
|
if ptr == nil {
|
|
return nil;
|
|
}
|
|
return index({ptr, a.elem.id}, i, loc);
|
|
|
|
case Type_Info_Multi_Pointer:
|
|
ptr := (^rawptr)(val.data)^;
|
|
if ptr == nil {
|
|
return nil;
|
|
}
|
|
return index({ptr, a.elem.id}, i, loc);
|
|
|
|
case Type_Info_Array:
|
|
runtime.bounds_check_error_loc(loc, i, a.count);
|
|
offset := uintptr(a.elem.size * i);
|
|
data := rawptr(uintptr(val.data) + offset);
|
|
return any{data, a.elem.id};
|
|
|
|
case Type_Info_Enumerated_Array:
|
|
runtime.bounds_check_error_loc(loc, i, a.count);
|
|
offset := uintptr(a.elem.size * i);
|
|
data := rawptr(uintptr(val.data) + offset);
|
|
return any{data, a.elem.id};
|
|
|
|
case Type_Info_Slice:
|
|
raw := (^mem.Raw_Slice)(val.data);
|
|
runtime.bounds_check_error_loc(loc, i, raw.len);
|
|
offset := uintptr(a.elem.size * i);
|
|
data := rawptr(uintptr(raw.data) + offset);
|
|
return any{data, a.elem.id};
|
|
|
|
case Type_Info_Dynamic_Array:
|
|
raw := (^mem.Raw_Dynamic_Array)(val.data);
|
|
runtime.bounds_check_error_loc(loc, i, raw.len);
|
|
offset := uintptr(a.elem.size * i);
|
|
data := rawptr(uintptr(raw.data) + offset);
|
|
return any{data, a.elem.id};
|
|
|
|
case Type_Info_String:
|
|
if a.is_cstring { return nil; }
|
|
|
|
raw := (^mem.Raw_String)(val.data);
|
|
runtime.bounds_check_error_loc(loc, i, raw.len);
|
|
offset := uintptr(size_of(u8) * i);
|
|
data := rawptr(uintptr(raw.data) + offset);
|
|
return any{data, typeid_of(u8)};
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
|
// Each key is a non-empty string which contains no control characters other than space, quotes, and colon.
|
|
Struct_Tag :: distinct string;
|
|
|
|
Struct_Field :: struct {
|
|
name: string,
|
|
type: ^Type_Info,
|
|
tag: Struct_Tag,
|
|
offset: uintptr,
|
|
is_using: bool,
|
|
}
|
|
|
|
struct_field_at :: proc(T: typeid, i: int) -> (field: Struct_Field) {
|
|
ti := runtime.type_info_base(type_info_of(T));
|
|
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
|
if 0 <= i && i < len(s.names) {
|
|
field.name = s.names[i];
|
|
field.type = s.types[i];
|
|
field.tag = Struct_Tag(s.tags[i]);
|
|
field.offset = s.offsets[i];
|
|
field.is_using = s.usings[i];
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
struct_field_by_name :: proc(T: typeid, name: string) -> (field: Struct_Field) {
|
|
ti := runtime.type_info_base(type_info_of(T));
|
|
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
|
for fname, i in s.names {
|
|
if fname == name {
|
|
field.name = s.names[i];
|
|
field.type = s.types[i];
|
|
field.tag = Struct_Tag(s.tags[i]);
|
|
field.offset = s.offsets[i];
|
|
field.is_using = s.usings[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
struct_field_value_by_name :: proc(a: any, field: string, allow_using := false) -> any {
|
|
if a == nil { return nil; }
|
|
|
|
ti := runtime.type_info_base(type_info_of(a.id));
|
|
|
|
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
|
for name, i in s.names {
|
|
if name == field {
|
|
return any{
|
|
rawptr(uintptr(a.data) + s.offsets[i]),
|
|
s.types[i].id,
|
|
};
|
|
}
|
|
|
|
if allow_using && s.usings[i] {
|
|
f := any{
|
|
rawptr(uintptr(a.data) + s.offsets[i]),
|
|
s.types[i].id,
|
|
};
|
|
|
|
if res := struct_field_value_by_name(f, field, allow_using); res != nil {
|
|
return res;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
|
|
|
|
struct_field_names :: proc(T: typeid) -> []string {
|
|
ti := runtime.type_info_base(type_info_of(T));
|
|
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
|
return s.names;
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
struct_field_types :: proc(T: typeid) -> []^Type_Info {
|
|
ti := runtime.type_info_base(type_info_of(T));
|
|
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
|
return s.types;
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
|
|
struct_field_tags :: proc(T: typeid) -> []Struct_Tag {
|
|
ti := runtime.type_info_base(type_info_of(T));
|
|
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
|
return transmute([]Struct_Tag)s.tags;
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
struct_field_offsets :: proc(T: typeid) -> []uintptr {
|
|
ti := runtime.type_info_base(type_info_of(T));
|
|
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
|
return s.offsets;
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
struct_fields_zipped :: proc(T: typeid) -> (fields: #soa[]Struct_Field) {
|
|
ti := runtime.type_info_base(type_info_of(T));
|
|
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
|
return soa_zip(
|
|
name = s.names,
|
|
type = s.types,
|
|
tag = transmute([]Struct_Tag)s.tags,
|
|
offset = s.offsets,
|
|
is_using = s.usings,
|
|
);
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
|
|
|
|
struct_tag_get :: proc(tag: Struct_Tag, key: string) -> (value: Struct_Tag) {
|
|
value, _ = struct_tag_lookup(tag, key);
|
|
return;
|
|
}
|
|
|
|
struct_tag_lookup :: proc(tag: Struct_Tag, key: string) -> (value: Struct_Tag, ok: bool) {
|
|
for t := tag; t != ""; /**/ {
|
|
i := 0;
|
|
for i < len(t) && t[i] == ' ' { // Skip whitespace
|
|
i += 1;
|
|
}
|
|
t = t[i:];
|
|
if len(t) == 0 {
|
|
break;
|
|
}
|
|
|
|
i = 0;
|
|
loop: for i < len(t) {
|
|
switch t[i] {
|
|
case ':', '"':
|
|
break loop;
|
|
case 0x00 ..< ' ', 0x7f ..= 0x9f: // break if control character is found
|
|
break loop;
|
|
}
|
|
i += 1;
|
|
}
|
|
|
|
if i == 0 {
|
|
break;
|
|
}
|
|
if i+1 >= len(t) {
|
|
break;
|
|
}
|
|
|
|
if t[i] != ':' || t[i+1] != '"' {
|
|
break;
|
|
}
|
|
name := string(t[:i]);
|
|
t = t[i+1:];
|
|
|
|
i = 1;
|
|
for i < len(t) && t[i] != '"' { // find closing quote
|
|
if t[i] == '\\' {
|
|
i += 1; // Skip escaped characters
|
|
}
|
|
i += 1;
|
|
}
|
|
|
|
if i >= len(t) {
|
|
break;
|
|
}
|
|
|
|
val := string(t[:i+1]);
|
|
t = t[i+1:];
|
|
|
|
if key == name {
|
|
return Struct_Tag(val[1:i]), true;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
enum_string :: proc(a: any) -> string {
|
|
if a == nil { return ""; }
|
|
ti := runtime.type_info_base(type_info_of(a.id));
|
|
if e, ok := ti.variant.(runtime.Type_Info_Enum); ok {
|
|
v, _ := as_i64(a);
|
|
for value, i in e.values {
|
|
if value == Type_Info_Enum_Value(v) {
|
|
return e.names[i];
|
|
}
|
|
}
|
|
} else {
|
|
panic("expected an enum to reflect.enum_string");
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
// Given a enum type and a value name, get the enum value.
|
|
enum_from_name :: proc($Enum_Type: typeid, name: string) -> (value: Enum_Type, ok: bool) {
|
|
ti := type_info_base(type_info_of(Enum_Type));
|
|
if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok {
|
|
for value_name, i in eti.names {
|
|
if value_name != name {
|
|
continue;
|
|
}
|
|
v := eti.values[i];
|
|
value = Enum_Type(v);
|
|
ok = true;
|
|
return;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
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));
|
|
if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok {
|
|
for value_name, i in eti.names {
|
|
if value_name != name {
|
|
continue;
|
|
}
|
|
value = eti.values[i];
|
|
ok = true;
|
|
return;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
enum_field_names :: proc(Enum_Type: typeid) -> []string {
|
|
ti := runtime.type_info_base(type_info_of(Enum_Type));
|
|
if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok {
|
|
return eti.names;
|
|
}
|
|
return nil;
|
|
}
|
|
enum_field_values :: proc(Enum_Type: typeid) -> []Type_Info_Enum_Value {
|
|
ti := runtime.type_info_base(type_info_of(Enum_Type));
|
|
if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok {
|
|
return eti.values;
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
Enum_Field :: struct {
|
|
name: string,
|
|
value: Type_Info_Enum_Value,
|
|
}
|
|
|
|
enum_fields_zipped :: proc(Enum_Type: typeid) -> (fields: #soa[]Enum_Field) {
|
|
ti := runtime.type_info_base(type_info_of(Enum_Type));
|
|
if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok {
|
|
return soa_zip(name=eti.names, value=eti.values);
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
|
|
|
|
union_variant_type_info :: proc(a: any) -> ^Type_Info {
|
|
id := union_variant_typeid(a);
|
|
return type_info_of(id);
|
|
}
|
|
|
|
type_info_union_is_pure_maybe :: proc(info: runtime.Type_Info_Union) -> bool {
|
|
return info.maybe && len(info.variants) == 1 && is_pointer(info.variants[0]);
|
|
}
|
|
|
|
union_variant_typeid :: proc(a: any) -> typeid {
|
|
if a == nil { return nil; }
|
|
|
|
ti := runtime.type_info_base(type_info_of(a.id));
|
|
if info, ok := ti.variant.(runtime.Type_Info_Union); ok {
|
|
if type_info_union_is_pure_maybe(info) {
|
|
if a.data != nil {
|
|
return info.variants[0].id;
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
tag_ptr := uintptr(a.data) + info.tag_offset;
|
|
tag_any := any{rawptr(tag_ptr), info.tag_type.id};
|
|
|
|
tag: i64 = ---;
|
|
switch i in tag_any {
|
|
case u8: tag = i64(i);
|
|
case i8: tag = i64(i);
|
|
case u16: tag = i64(i);
|
|
case i16: tag = i64(i);
|
|
case u32: tag = i64(i);
|
|
case i32: tag = i64(i);
|
|
case u64: tag = i64(i);
|
|
case i64: tag = i;
|
|
case: unimplemented();
|
|
}
|
|
|
|
if a.data != nil && tag != 0 {
|
|
i := tag if info.no_nil else tag-1;
|
|
return info.variants[i].id;
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
panic("expected a union to reflect.union_variant_typeid");
|
|
|
|
}
|
|
|
|
get_union_variant_raw_tag :: proc(a: any) -> i64 {
|
|
if a == nil { return -1; }
|
|
|
|
ti := runtime.type_info_base(type_info_of(a.id));
|
|
if info, ok := ti.variant.(runtime.Type_Info_Union); ok {
|
|
if type_info_union_is_pure_maybe(info) {
|
|
return 1 if a.data != nil else 0;
|
|
}
|
|
|
|
tag_ptr := uintptr(a.data) + info.tag_offset;
|
|
tag_any := any{rawptr(tag_ptr), info.tag_type.id};
|
|
|
|
tag: i64 = ---;
|
|
switch i in tag_any {
|
|
case u8: tag = i64(i);
|
|
case i8: tag = i64(i);
|
|
case u16: tag = i64(i);
|
|
case i16: tag = i64(i);
|
|
case u32: tag = i64(i);
|
|
case i32: tag = i64(i);
|
|
case u64: tag = i64(i);
|
|
case i64: tag = i;
|
|
case: unimplemented();
|
|
}
|
|
|
|
return tag;
|
|
}
|
|
panic("expected a union to reflect.get_union_variant_raw_tag");
|
|
}
|
|
|
|
|
|
set_union_variant_raw_tag :: proc(a: any, tag: i64) {
|
|
if a == nil { return; }
|
|
|
|
ti := runtime.type_info_base(type_info_of(a.id));
|
|
if info, ok := ti.variant.(runtime.Type_Info_Union); ok {
|
|
if type_info_union_is_pure_maybe(info) {
|
|
// Cannot do anything
|
|
return;
|
|
}
|
|
|
|
tag_ptr := uintptr(a.data) + info.tag_offset;
|
|
tag_any := any{rawptr(tag_ptr), info.tag_type.id};
|
|
|
|
switch i in &tag_any {
|
|
case u8: i = u8(tag);
|
|
case i8: i = i8(tag);
|
|
case u16: i = u16(tag);
|
|
case i16: i = i16(tag);
|
|
case u32: i = u32(tag);
|
|
case i32: i = i32(tag);
|
|
case u64: i = u64(tag);
|
|
case i64: i = tag;
|
|
case: unimplemented();
|
|
}
|
|
|
|
return;
|
|
}
|
|
panic("expected a union to reflect.set_union_variant_raw_tag");
|
|
}
|
|
|
|
set_union_variant_typeid :: proc(a: any, id: typeid) {
|
|
if a == nil { return; }
|
|
|
|
ti := runtime.type_info_base(type_info_of(a.id));
|
|
if info, ok := ti.variant.(runtime.Type_Info_Union); ok {
|
|
if type_info_union_is_pure_maybe(info) {
|
|
// Cannot do anything
|
|
return;
|
|
}
|
|
|
|
if id == nil && !info.no_nil {
|
|
set_union_variant_raw_tag(a, 0);
|
|
return;
|
|
}
|
|
|
|
for variant, i in info.variants {
|
|
if variant.id == id {
|
|
tag := i64(i);
|
|
if !info.no_nil {
|
|
tag += 1;
|
|
}
|
|
set_union_variant_raw_tag(a, tag);
|
|
return;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
panic("expected a union to reflect.set_union_variant_typeid");
|
|
}
|
|
|
|
set_union_variant_type_info :: proc(a: any, tag_ti: ^Type_Info) {
|
|
if a == nil { return; }
|
|
|
|
ti := runtime.type_info_base(type_info_of(a.id));
|
|
if info, ok := ti.variant.(runtime.Type_Info_Union); ok {
|
|
if type_info_union_is_pure_maybe(info) {
|
|
// Cannot do anything
|
|
return;
|
|
}
|
|
|
|
if tag_ti == nil && !info.no_nil {
|
|
set_union_variant_raw_tag(a, 0);
|
|
return;
|
|
}
|
|
|
|
for variant, i in info.variants {
|
|
if variant == tag_ti {
|
|
tag := i64(i);
|
|
if !info.no_nil {
|
|
tag += 1;
|
|
}
|
|
set_union_variant_raw_tag(a, tag);
|
|
return;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
panic("expected a union to reflect.set_union_variant_type_info");
|
|
}
|
|
|
|
|
|
as_bool :: proc(a: any) -> (value: bool, 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_Boolean:
|
|
valid = true;
|
|
switch v in a {
|
|
case bool: value = v;
|
|
case b8: value = bool(v);
|
|
case b16: value = bool(v);
|
|
case b32: value = bool(v);
|
|
case b64: value = bool(v);
|
|
case: valid = false;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
as_int :: proc(a: any) -> (value: int, valid: bool) {
|
|
v: i64;
|
|
v, valid = as_i64(a);
|
|
value = int(v);
|
|
return;
|
|
}
|
|
|
|
as_uint :: proc(a: any) -> (value: uint, valid: bool) {
|
|
v: u64;
|
|
v, valid = as_u64(a);
|
|
value = uint(v);
|
|
return;
|
|
}
|
|
|
|
as_i64 :: proc(a: any) -> (value: i64, 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_Integer:
|
|
valid = true;
|
|
switch v in a {
|
|
case i8: value = i64(v);
|
|
case i16: value = i64(v);
|
|
case i32: value = i64(v);
|
|
case i64: value = v;
|
|
case i128: value = i64(v);
|
|
case int: value = i64(v);
|
|
|
|
case u8: value = i64(v);
|
|
case u16: value = i64(v);
|
|
case u32: value = i64(v);
|
|
case u64: value = i64(v);
|
|
case u128: value = i64(v);
|
|
case uint: value = i64(v);
|
|
case uintptr: value = i64(v);
|
|
|
|
case u16le: value = i64(v);
|
|
case u32le: value = i64(v);
|
|
case u64le: value = i64(v);
|
|
case u128le: value = i64(v);
|
|
|
|
case i16le: value = i64(v);
|
|
case i32le: value = i64(v);
|
|
case i64le: value = i64(v);
|
|
case i128le: value = i64(v);
|
|
|
|
case u16be: value = i64(v);
|
|
case u32be: value = i64(v);
|
|
case u64be: value = i64(v);
|
|
case u128be: value = i64(v);
|
|
|
|
case i16be: value = i64(v);
|
|
case i32be: value = i64(v);
|
|
case i64be: value = i64(v);
|
|
case i128be: value = i64(v);
|
|
case: valid = false;
|
|
}
|
|
|
|
case Type_Info_Rune:
|
|
r := a.(rune);
|
|
value = i64(r);
|
|
valid = true;
|
|
|
|
case Type_Info_Float:
|
|
valid = true;
|
|
switch v in a {
|
|
case f32: value = i64(v);
|
|
case f64: value = i64(v);
|
|
case f32le: value = i64(v);
|
|
case f64le: value = i64(v);
|
|
case f32be: value = i64(v);
|
|
case f64be: value = i64(v);
|
|
case: valid = false;
|
|
}
|
|
|
|
case Type_Info_Boolean:
|
|
valid = true;
|
|
switch v in a {
|
|
case bool: value = i64(v);
|
|
case b8: value = i64(v);
|
|
case b16: value = i64(v);
|
|
case b32: value = i64(v);
|
|
case b64: value = i64(v);
|
|
case: valid = false;
|
|
}
|
|
|
|
case Type_Info_Complex:
|
|
switch v in a {
|
|
case complex64:
|
|
if imag(v) == 0 {
|
|
value = i64(real(v));
|
|
valid = true;
|
|
}
|
|
case complex128:
|
|
if imag(v) == 0 {
|
|
value = i64(real(v));
|
|
valid = true;
|
|
}
|
|
}
|
|
|
|
case Type_Info_Quaternion:
|
|
switch v in a {
|
|
case quaternion128:
|
|
if imag(v) == 0 && jmag(v) == 0 && kmag(v) == 0 {
|
|
value = i64(real(v));
|
|
valid = true;
|
|
}
|
|
case quaternion256:
|
|
if imag(v) == 0 && jmag(v) == 0 && kmag(v) == 0 {
|
|
value = i64(real(v));
|
|
valid = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
as_u64 :: proc(a: any) -> (value: u64, 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_Integer:
|
|
valid = true;
|
|
switch v in a {
|
|
case i8: value = u64(v);
|
|
case i16: value = u64(v);
|
|
case i32: value = u64(v);
|
|
case i64: value = u64(v);
|
|
case i128: value = u64(v);
|
|
case int: value = u64(v);
|
|
|
|
case u8: value = u64(v);
|
|
case u16: value = u64(v);
|
|
case u32: value = u64(v);
|
|
case u64: value = (v);
|
|
case u128: value = u64(v);
|
|
case uint: value = u64(v);
|
|
case uintptr:value = u64(v);
|
|
|
|
case u16le: value = u64(v);
|
|
case u32le: value = u64(v);
|
|
case u64le: value = u64(v);
|
|
case u128le: value = u64(v);
|
|
|
|
case i16le: value = u64(v);
|
|
case i32le: value = u64(v);
|
|
case i64le: value = u64(v);
|
|
case i128le: value = u64(v);
|
|
|
|
case u16be: value = u64(v);
|
|
case u32be: value = u64(v);
|
|
case u64be: value = u64(v);
|
|
case u128be: value = u64(v);
|
|
|
|
case i16be: value = u64(v);
|
|
case i32be: value = u64(v);
|
|
case i64be: value = u64(v);
|
|
case i128be: value = u64(v);
|
|
case: valid = false;
|
|
}
|
|
|
|
case Type_Info_Rune:
|
|
r := a.(rune);
|
|
value = u64(r);
|
|
valid = true;
|
|
|
|
case Type_Info_Float:
|
|
valid = true;
|
|
switch v in a {
|
|
case f32: value = u64(v);
|
|
case f64: value = u64(v);
|
|
case f32le: value = u64(v);
|
|
case f64le: value = u64(v);
|
|
case f32be: value = u64(v);
|
|
case f64be: value = u64(v);
|
|
case: valid = false;
|
|
}
|
|
|
|
case Type_Info_Boolean:
|
|
valid = true;
|
|
switch v in a {
|
|
case bool: value = u64(v);
|
|
case b8: value = u64(v);
|
|
case b16: value = u64(v);
|
|
case b32: value = u64(v);
|
|
case b64: value = u64(v);
|
|
case: valid = false;
|
|
}
|
|
|
|
case Type_Info_Complex:
|
|
switch v in a {
|
|
case complex64:
|
|
if imag(v) == 0 {
|
|
value = u64(real(v));
|
|
valid = true;
|
|
}
|
|
case complex128:
|
|
if imag(v) == 0 {
|
|
value = u64(real(v));
|
|
valid = true;
|
|
}
|
|
}
|
|
|
|
case Type_Info_Quaternion:
|
|
switch v in a {
|
|
case quaternion128:
|
|
if imag(v) == 0 && jmag(v) == 0 && kmag(v) == 0 {
|
|
value = u64(real(v));
|
|
valid = true;
|
|
}
|
|
case quaternion256:
|
|
if imag(v) == 0 && jmag(v) == 0 && kmag(v) == 0 {
|
|
value = u64(real(v));
|
|
valid = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
as_f64 :: proc(a: any) -> (value: f64, 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_Integer:
|
|
valid = true;
|
|
switch v in a {
|
|
case i8: value = f64(v);
|
|
case i16: value = f64(v);
|
|
case i32: value = f64(v);
|
|
case i64: value = f64(v);
|
|
case i128: value = f64(v);
|
|
|
|
case u8: value = f64(v);
|
|
case u16: value = f64(v);
|
|
case u32: value = f64(v);
|
|
case u64: value = f64(v);
|
|
case u128: value = f64(v);
|
|
|
|
case u16le: value = f64(v);
|
|
case u32le: value = f64(v);
|
|
case u64le: value = f64(v);
|
|
case u128le:value = f64(v);
|
|
|
|
case i16le: value = f64(v);
|
|
case i32le: value = f64(v);
|
|
case i64le: value = f64(v);
|
|
case i128le:value = f64(v);
|
|
|
|
case u16be: value = f64(v);
|
|
case u32be: value = f64(v);
|
|
case u64be: value = f64(v);
|
|
case u128be:value = f64(v);
|
|
|
|
case i16be: value = f64(v);
|
|
case i32be: value = f64(v);
|
|
case i64be: value = f64(v);
|
|
case i128be:value = f64(v);
|
|
case: valid = false;
|
|
}
|
|
|
|
case Type_Info_Rune:
|
|
r := a.(rune);
|
|
value = f64(i32(r));
|
|
valid = true;
|
|
|
|
case Type_Info_Float:
|
|
valid = true;
|
|
switch v in a {
|
|
case f32: value = f64(v);
|
|
case f64: value = (v);
|
|
case f32le: value = f64(v);
|
|
case f64le: value = f64(v);
|
|
case f32be: value = f64(v);
|
|
case f64be: value = f64(v);
|
|
case: valid = false;
|
|
}
|
|
|
|
case Type_Info_Boolean:
|
|
valid = true;
|
|
switch v in a {
|
|
case bool: value = f64(i32(v));
|
|
case b8: value = f64(i32(v));
|
|
case b16: value = f64(i32(v));
|
|
case b32: value = f64(i32(v));
|
|
case b64: value = f64(i32(v));
|
|
case: valid = false;
|
|
}
|
|
|
|
case Type_Info_Complex:
|
|
switch v in a {
|
|
case complex64:
|
|
if imag(v) == 0 {
|
|
value = f64(real(v));
|
|
valid = true;
|
|
}
|
|
case complex128:
|
|
if imag(v) == 0 {
|
|
value = real(v);
|
|
valid = true;
|
|
}
|
|
}
|
|
|
|
case Type_Info_Quaternion:
|
|
switch v in a {
|
|
case quaternion128:
|
|
if imag(v) == 0 && jmag(v) == 0 && kmag(v) == 0 {
|
|
value = f64(real(v));
|
|
valid = true;
|
|
}
|
|
case quaternion256:
|
|
if imag(v) == 0 && jmag(v) == 0 && kmag(v) == 0 {
|
|
value = real(v);
|
|
valid = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
as_string :: proc(a: any) -> (value: string, 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 string: value = v;
|
|
case cstring: value = string(v);
|
|
case: valid = false;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
relative_pointer_to_absolute :: proc(a: any) -> rawptr {
|
|
if a == nil { return nil; }
|
|
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_Relative_Pointer:
|
|
return relative_pointer_to_absolute_raw(a.data, info.base_integer.id);
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
|
|
relative_pointer_to_absolute_raw :: proc(data: rawptr, base_integer_id: typeid) -> rawptr {
|
|
_handle :: proc(ptr: ^$T) -> rawptr where intrinsics.type_is_integer(T) {
|
|
if ptr^ == 0 {
|
|
return nil;
|
|
}
|
|
when intrinsics.type_is_unsigned(T) {
|
|
return rawptr(uintptr(ptr) + uintptr(ptr^));
|
|
} else {
|
|
return rawptr(uintptr(ptr) + uintptr(i64(ptr^)));
|
|
}
|
|
}
|
|
|
|
ptr_any := any{data, base_integer_id};
|
|
ptr: rawptr;
|
|
switch i in &ptr_any {
|
|
case u8: ptr = _handle(&i);
|
|
case u16: ptr = _handle(&i);
|
|
case u32: ptr = _handle(&i);
|
|
case u64: ptr = _handle(&i);
|
|
case i8: ptr = _handle(&i);
|
|
case i16: ptr = _handle(&i);
|
|
case i32: ptr = _handle(&i);
|
|
case i64: ptr = _handle(&i);
|
|
case u16le: ptr = _handle(&i);
|
|
case u32le: ptr = _handle(&i);
|
|
case u64le: ptr = _handle(&i);
|
|
case i16le: ptr = _handle(&i);
|
|
case i32le: ptr = _handle(&i);
|
|
case i64le: ptr = _handle(&i);
|
|
case u16be: ptr = _handle(&i);
|
|
case u32be: ptr = _handle(&i);
|
|
case u64be: ptr = _handle(&i);
|
|
case i16be: ptr = _handle(&i);
|
|
case i32be: ptr = _handle(&i);
|
|
case i64be: ptr = _handle(&i);
|
|
}
|
|
return ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
as_pointer :: proc(a: any) -> (value: rawptr, 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_Pointer:
|
|
valid = true;
|
|
value = a.data;
|
|
|
|
case Type_Info_String:
|
|
valid = true;
|
|
switch v in a {
|
|
case cstring: value = rawptr(v);
|
|
case: valid = false;
|
|
}
|
|
|
|
case Type_Info_Relative_Pointer:
|
|
valid = true;
|
|
value = relative_pointer_to_absolute_raw(a.data, info.base_integer.id);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
as_raw_data :: proc(a: any) -> (value: rawptr, 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 string: value = raw_data(v);
|
|
case cstring: value = rawptr(v); // just in case
|
|
case: valid = false;
|
|
}
|
|
|
|
case Type_Info_Array:
|
|
valid = true;
|
|
value = a.data;
|
|
|
|
case Type_Info_Slice:
|
|
valid = true;
|
|
value = (^mem.Raw_Slice)(a.data).data;
|
|
|
|
case Type_Info_Dynamic_Array:
|
|
valid = true;
|
|
value = (^mem.Raw_Dynamic_Array)(a.data).data;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
not_equal :: proc(a, b: any) -> bool {
|
|
return !equal(a, b);
|
|
}
|
|
equal :: proc(a, b: any) -> bool {
|
|
if a == nil && b == nil {
|
|
return true;
|
|
}
|
|
|
|
if a.id != b.id {
|
|
return false;
|
|
}
|
|
|
|
if a.data == b.data {
|
|
return true;
|
|
}
|
|
|
|
t := type_info_of(a.id);
|
|
if .Comparable not_in t.flags {
|
|
return false;
|
|
}
|
|
|
|
if t.size == 0 {
|
|
return true;
|
|
}
|
|
|
|
if .Simple_Compare in t.flags {
|
|
return mem.compare_byte_ptrs((^byte)(a.data), (^byte)(b.data), t.size) == 0;
|
|
}
|
|
|
|
t = runtime.type_info_core(t);
|
|
|
|
#partial switch v in t.variant {
|
|
case Type_Info_String:
|
|
if v.is_cstring {
|
|
x := string((^cstring)(a.data)^);
|
|
y := string((^cstring)(b.data)^);
|
|
return x == y;
|
|
} else {
|
|
x := (^string)(a.data)^;
|
|
y := (^string)(b.data)^;
|
|
return x == y;
|
|
}
|
|
|
|
case Type_Info_Array:
|
|
for i in 0..<v.count {
|
|
x := rawptr(uintptr(a.data) + uintptr(v.elem_size*i));
|
|
y := rawptr(uintptr(b.data) + uintptr(v.elem_size*i));
|
|
if !equal(any{x, v.elem.id}, any{y, v.elem.id}) {
|
|
return false;
|
|
}
|
|
}
|
|
case Type_Info_Enumerated_Array:
|
|
for i in 0..<v.count {
|
|
x := rawptr(uintptr(a.data) + uintptr(v.elem_size*i));
|
|
y := rawptr(uintptr(b.data) + uintptr(v.elem_size*i));
|
|
if !equal(any{x, v.elem.id}, any{y, v.elem.id}) {
|
|
return false;
|
|
}
|
|
}
|
|
case Type_Info_Struct:
|
|
if v.equal != nil {
|
|
return v.equal(a.data, b.data);
|
|
} else {
|
|
for offset, i in v.offsets {
|
|
x := rawptr(uintptr(a.data) + offset);
|
|
y := rawptr(uintptr(b.data) + offset);
|
|
id := v.types[i].id;
|
|
if !equal(any{x, id}, any{y, id}) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
*/
|