Browse Source

Merge branch 'master' of https://github.com/avanspector/Odin

avanspector 8 months ago
parent
commit
9868c8292b

+ 41 - 9
core/mem/tracking_allocator.odin

@@ -34,12 +34,18 @@ Tracking_Allocator_Bad_Free_Entry :: struct {
 	location: runtime.Source_Code_Location,
 	location: runtime.Source_Code_Location,
 }
 }
 
 
+/*
+Callback type for when tracking allocator runs into a bad free.
+*/
+Tracking_Allocator_Bad_Free_Callback :: proc(t: ^Tracking_Allocator, memory: rawptr, location: runtime.Source_Code_Location)
+
 /*
 /*
 Tracking allocator data.
 Tracking allocator data.
 */
 */
 Tracking_Allocator :: struct {
 Tracking_Allocator :: struct {
 	backing: Allocator,
 	backing: Allocator,
 	allocation_map: map[rawptr]Tracking_Allocator_Entry,
 	allocation_map: map[rawptr]Tracking_Allocator_Entry,
+	bad_free_callback: Tracking_Allocator_Bad_Free_Callback,
 	bad_free_array: [dynamic]Tracking_Allocator_Bad_Free_Entry,
 	bad_free_array: [dynamic]Tracking_Allocator_Bad_Free_Entry,
 	mutex: sync.Mutex,
 	mutex: sync.Mutex,
 	clear_on_free_all: bool,
 	clear_on_free_all: bool,
@@ -61,6 +67,7 @@ allocate the tracked data.
 tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Allocator, internals_allocator := context.allocator) {
 tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Allocator, internals_allocator := context.allocator) {
 	t.backing = backing_allocator
 	t.backing = backing_allocator
 	t.allocation_map.allocator = internals_allocator
 	t.allocation_map.allocator = internals_allocator
+	t.bad_free_callback = tracking_allocator_bad_free_callback_panic
 	t.bad_free_array.allocator = internals_allocator
 	t.bad_free_array.allocator = internals_allocator
 	if .Free_All in query_features(t.backing) {
 	if .Free_All in query_features(t.backing) {
 		t.clear_on_free_all = true
 		t.clear_on_free_all = true
@@ -109,6 +116,33 @@ tracking_allocator_reset :: proc(t: ^Tracking_Allocator) {
 	sync.mutex_unlock(&t.mutex)
 	sync.mutex_unlock(&t.mutex)
 }
 }
 
 
+/*
+Default behavior for a bad free: Crash with error message that says where the
+bad free happened.
+
+Override Tracking_Allocator.bad_free_callback to have something else happen. For
+example, you can use tracking_allocator_bad_free_callback_add_to_array to return
+the tracking allocator to the old behavior, where the bad_free_array was used.
+*/
+tracking_allocator_bad_free_callback_panic :: proc(t: ^Tracking_Allocator, memory: rawptr, location: runtime.Source_Code_Location) {
+	runtime.print_caller_location(location)
+	runtime.print_string(" Tracking allocator error: Bad free of pointer ")
+	runtime.print_uintptr(uintptr(memory))
+	runtime.print_string("\n")
+	runtime.trap()
+}
+
+/*
+Alternative behavior for a bad free: Store in `bad_free_array`. If you use this,
+then you must make sure to check Tracking_Allocator.bad_free_array at some point.
+*/
+tracking_allocator_bad_free_callback_add_to_array :: proc(t: ^Tracking_Allocator, memory: rawptr, location: runtime.Source_Code_Location) {
+	append(&t.bad_free_array, Tracking_Allocator_Bad_Free_Entry {
+		memory = memory,
+		location = location,
+	})
+}
+
 /*
 /*
 Tracking allocator.
 Tracking allocator.
 
 
@@ -116,8 +150,10 @@ The tracking allocator is an allocator wrapper that tracks memory allocations.
 This allocator stores all the allocations in a map. Whenever a pointer that's
 This allocator stores all the allocations in a map. Whenever a pointer that's
 not inside of the map is freed, the `bad_free_array` entry is added.
 not inside of the map is freed, the `bad_free_array` entry is added.
 
 
-An example of how to use the `Tracking_Allocator` to track subsequent allocations
-in your program and report leaks and bad frees:
+Here follows an example of how to use the `Tracking_Allocator` to track
+subsequent allocations in your program and report leaks. By default, the
+tracking allocator will crash on bad frees. You can override that behavior by
+overriding `track.bad_free_callback`.
 
 
 Example:
 Example:
 
 
@@ -137,9 +173,6 @@ Example:
 		for _, leak in track.allocation_map {
 		for _, leak in track.allocation_map {
 			fmt.printf("%v leaked %m\n", leak.location, leak.size)
 			fmt.printf("%v leaked %m\n", leak.location, leak.size)
 		}
 		}
-		for bad_free in track.bad_free_array {
-			fmt.printf("%v allocation %p was freed badly\n", bad_free.location, bad_free.memory)
-		}
 	}
 	}
 */
 */
 @(require_results)
 @(require_results)
@@ -191,10 +224,9 @@ tracking_allocator_proc :: proc(
 	}
 	}
 
 
 	if mode == .Free && old_memory != nil && old_memory not_in data.allocation_map {
 	if mode == .Free && old_memory != nil && old_memory not_in data.allocation_map {
-		append(&data.bad_free_array, Tracking_Allocator_Bad_Free_Entry{
-			memory = old_memory,
-			location = loc,
-		})
+		if data.bad_free_callback != nil {
+			data.bad_free_callback(data, old_memory, loc)
+		}
 	} else {
 	} else {
 		result = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc) or_return
 		result = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc) or_return
 	}
 	}

+ 11 - 5
core/strings/intern.odin

@@ -89,6 +89,7 @@ intern_get_cstring :: proc(m: ^Intern, text: string) -> (str: cstring, err: runt
 	entry := _intern_get_entry(m, text) or_return
 	entry := _intern_get_entry(m, text) or_return
 	return cstring(&entry.str[0]), nil
 	return cstring(&entry.str[0]), nil
 }
 }
+
 /*
 /*
 Internal function to lookup whether the text string exists in the map, returns the entry
 Internal function to lookup whether the text string exists in the map, returns the entry
 Sets and allocates the entry if it wasn't set yet
 Sets and allocates the entry if it wasn't set yet
@@ -104,13 +105,15 @@ Returns:
 - err: An allocator error if one occured, `nil` otherwise
 - err: An allocator error if one occured, `nil` otherwise
 */
 */
 _intern_get_entry :: proc(m: ^Intern, text: string) -> (new_entry: ^Intern_Entry, err: runtime.Allocator_Error) #no_bounds_check {
 _intern_get_entry :: proc(m: ^Intern, text: string) -> (new_entry: ^Intern_Entry, err: runtime.Allocator_Error) #no_bounds_check {
-	if prev, ok := m.entries[text]; ok {
-		return prev, nil
-	}
 	if m.allocator.procedure == nil {
 	if m.allocator.procedure == nil {
 		m.allocator = context.allocator
 		m.allocator = context.allocator
 	}
 	}
 
 
+	key_ptr, val_ptr, inserted := map_entry(&m.entries, text) or_return
+	if !inserted {
+		return val_ptr^, nil
+	}
+
 	entry_size := int(offset_of(Intern_Entry, str)) + len(text) + 1
 	entry_size := int(offset_of(Intern_Entry, str)) + len(text) + 1
 	bytes := runtime.mem_alloc(entry_size, align_of(Intern_Entry), m.allocator) or_return
 	bytes := runtime.mem_alloc(entry_size, align_of(Intern_Entry), m.allocator) or_return
 	new_entry = (^Intern_Entry)(raw_data(bytes))
 	new_entry = (^Intern_Entry)(raw_data(bytes))
@@ -120,6 +123,9 @@ _intern_get_entry :: proc(m: ^Intern, text: string) -> (new_entry: ^Intern_Entry
 	new_entry.str[new_entry.len] = 0
 	new_entry.str[new_entry.len] = 0
 
 
 	key := string(new_entry.str[:new_entry.len])
 	key := string(new_entry.str[:new_entry.len])
-	m.entries[key] = new_entry
-	return new_entry, nil
+
+	key_ptr^ = key
+	val_ptr^ = new_entry
+
+	return
 }
 }

+ 4 - 1
core/sys/wasm/js/odin.js

@@ -110,7 +110,10 @@ class WasmMemoryInterface {
 	}
 	}
 
 
 	loadCstring(ptr) {
 	loadCstring(ptr) {
-		const start = this.loadPtr(ptr);
+		return this.loadCstringDirect(this.loadPtr(ptr));
+	}
+
+	loadCstringDirect(start) {
 		if (start == 0) {
 		if (start == 0) {
 			return null;
 			return null;
 		}
 		}

+ 1 - 0
core/sys/windows/comctl32.odin

@@ -6,4 +6,5 @@ foreign import "system:Comctl32.lib"
 @(default_calling_convention="system")
 @(default_calling_convention="system")
 foreign Comctl32 {
 foreign Comctl32 {
 	LoadIconWithScaleDown :: proc(hinst: HINSTANCE, pszName: PCWSTR, cx: c_int, cy: c_int, phico: ^HICON) -> HRESULT ---
 	LoadIconWithScaleDown :: proc(hinst: HINSTANCE, pszName: PCWSTR, cx: c_int, cy: c_int, phico: ^HICON) -> HRESULT ---
+	SetWindowSubclass :: proc(hwnd: HWND, pfnSubclass: SUBCLASSPROC, uIdSubclass: UINT_PTR, dwRefData: DWORD_PTR) ---
 }
 }

+ 1 - 1
core/sys/windows/dbghelp.odin

@@ -15,7 +15,7 @@ MINIDUMP_DIRECTORY :: struct {
 	Location:   MINIDUMP_LOCATION_DESCRIPTOR,
 	Location:   MINIDUMP_LOCATION_DESCRIPTOR,
 }
 }
 
 
-MINIDUMP_EXCEPTION_INFORMATION :: struct {
+MINIDUMP_EXCEPTION_INFORMATION :: struct #max_field_align(4) {
 	ThreadId:          DWORD,
 	ThreadId:          DWORD,
 	ExceptionPointers: ^EXCEPTION_POINTERS,
 	ExceptionPointers: ^EXCEPTION_POINTERS,
 	ClientPointers:    BOOL,
 	ClientPointers:    BOOL,

+ 2 - 0
core/sys/windows/types.odin

@@ -796,6 +796,8 @@ TIMERPROC :: #type proc "system" (HWND, UINT, UINT_PTR, DWORD)
 
 
 WNDPROC :: #type proc "system" (HWND, UINT, WPARAM, LPARAM) -> LRESULT
 WNDPROC :: #type proc "system" (HWND, UINT, WPARAM, LPARAM) -> LRESULT
 
 
+SUBCLASSPROC :: #type proc "system" (HWND, UINT, WPARAM, LPARAM, UINT_PTR, DWORD_PTR) -> LRESULT
+
 HOOKPROC :: #type proc "system" (code: c_int, wParam: WPARAM, lParam: LPARAM) -> LRESULT
 HOOKPROC :: #type proc "system" (code: c_int, wParam: WPARAM, lParam: LPARAM) -> LRESULT
 
 
 WINEVENTPROC :: #type proc "system" (
 WINEVENTPROC :: #type proc "system" (

+ 4 - 1
core/sys/windows/user32.odin

@@ -2,6 +2,7 @@
 package sys_windows
 package sys_windows
 
 
 import "base:intrinsics"
 import "base:intrinsics"
+import "core:c"
 foreign import user32 "system:User32.lib"
 foreign import user32 "system:User32.lib"
 
 
 @(default_calling_convention="system")
 @(default_calling_convention="system")
@@ -32,6 +33,8 @@ foreign user32 {
 	RegisterClassExW :: proc(^WNDCLASSEXW) -> ATOM ---
 	RegisterClassExW :: proc(^WNDCLASSEXW) -> ATOM ---
 	UnregisterClassW :: proc(lpClassName: LPCWSTR, hInstance: HINSTANCE) -> BOOL ---
 	UnregisterClassW :: proc(lpClassName: LPCWSTR, hInstance: HINSTANCE) -> BOOL ---
 
 
+	RegisterHotKey :: proc(hnwd: HWND, id: c.int, fsModifiers: UINT, vk: UINT) -> BOOL ---
+
 	CreateWindowExW :: proc(
 	CreateWindowExW :: proc(
 		dwExStyle: DWORD,
 		dwExStyle: DWORD,
 		lpClassName: LPCWSTR,
 		lpClassName: LPCWSTR,
@@ -553,7 +556,7 @@ MOUSE_ATTRIBUTES_CHANGED :: 0x04
 MOUSE_MOVE_NOCOALESCE :: 0x08
 MOUSE_MOVE_NOCOALESCE :: 0x08
 
 
 RI_MOUSE_BUTTON_1_DOWN :: 0x0001
 RI_MOUSE_BUTTON_1_DOWN :: 0x0001
-RI_MOUSE_LEFT_BUTTON_DOWNS :: RI_MOUSE_BUTTON_1_DOWN
+RI_MOUSE_LEFT_BUTTON_DOWN :: RI_MOUSE_BUTTON_1_DOWN
 RI_MOUSE_BUTTON_1_UP :: 0x0002
 RI_MOUSE_BUTTON_1_UP :: 0x0002
 RI_MOUSE_LEFT_BUTTON_UP :: RI_MOUSE_BUTTON_1_UP
 RI_MOUSE_LEFT_BUTTON_UP :: RI_MOUSE_BUTTON_1_UP
 RI_MOUSE_BUTTON_2_DOWN :: 0x0004
 RI_MOUSE_BUTTON_2_DOWN :: 0x0004

+ 210 - 0
core/sys/windows/xinput.odin

@@ -0,0 +1,210 @@
+#+build windows
+package sys_windows
+
+foreign import "system:xinput.lib"
+
+// Device types available in XINPUT_CAPABILITIES
+// Correspond to XINPUT_DEVTYPE_...
+XINPUT_DEVTYPE :: enum BYTE {
+	GAMEPAD = 0x01,
+}
+
+// Device subtypes available in XINPUT_CAPABILITIES
+// Correspond to XINPUT_DEVSUBTYPE_...
+XINPUT_DEVSUBTYPE :: enum BYTE {
+	UNKNOWN          = 0x00,
+	GAMEPAD          = 0x01,
+	WHEEL            = 0x02,
+	ARCADE_STICK     = 0x03,
+	FLIGHT_STICK     = 0x04,
+	DANCE_PAD        = 0x05,
+	GUITAR           = 0x06,
+	GUITAR_ALTERNATE = 0x07,
+	DRUM_KIT         = 0x08,
+	GUITAR_BASS      = 0x0B,
+	ARCADE_PAD       = 0x13,
+}
+
+// Flags for XINPUT_CAPABILITIES
+// Correspond to log2(XINPUT_CAPS_...)
+XINPUT_CAP :: enum WORD {
+	FFB_SUPPORTED   = 0,
+	WIRELESS        = 1,
+	VOICE_SUPPORTED = 2,
+	PMD_SUPPORTED   = 3,
+	NO_NAVIGATION   = 4,
+}
+XINPUT_CAPS :: distinct bit_set[XINPUT_CAP;WORD]
+
+// Constants for gamepad buttons
+// Correspond to log2(XINPUT_GAMEPAD_...)
+XINPUT_GAMEPAD_BUTTON_BIT :: enum WORD {
+	DPAD_UP        = 0,
+	DPAD_DOWN      = 1,
+	DPAD_LEFT      = 2,
+	DPAD_RIGHT     = 3,
+	START          = 4,
+	BACK           = 5,
+	LEFT_THUMB     = 6,
+	RIGHT_THUMB    = 7,
+	LEFT_SHOULDER  = 8,
+	RIGHT_SHOULDER = 9,
+	A              = 12,
+	B              = 13,
+	X              = 14,
+	Y              = 15,
+}
+XINPUT_GAMEPAD_BUTTON :: distinct bit_set[XINPUT_GAMEPAD_BUTTON_BIT;WORD]
+
+// Gamepad thresholds
+XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE: SHORT : 7849
+XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE: SHORT : 8689
+XINPUT_GAMEPAD_TRIGGER_THRESHOLD: SHORT : 30
+
+// Flags to pass to XInputGetCapabilities
+// Corresponds to log2(XINPUT_FLAG_...)
+XINPUT_FLAG_BIT :: enum WORD {
+	GAMEPAD = 0,
+}
+XINPUT_FLAG :: distinct bit_set[XINPUT_FLAG_BIT;DWORD]
+
+// Devices that support batteries
+// Corresponds to BATTERY_DEVTYPE_...
+BATTERY_DEVTYPE :: enum BYTE {
+	GAMEPAD = 0x00,
+	HEADSET = 0x01,
+}
+
+// Flags for battery status level
+// Correspond to BATTERY_TYPE_...
+BATTERY_TYPE :: enum BYTE {
+	DISCONNECTED = 0x00, // This device is not connected
+	WIRED        = 0x01, // Wired device, no battery
+	ALKALINE     = 0x02, // Alkaline battery source
+	NIMH         = 0x03, // Nickel Metal Hydride battery source
+	UNKNOWN      = 0xFF, // Cannot determine the battery type
+}
+
+// These are only valid for wireless, connected devices, with known battery types
+// The amount of use time remaining depends on the type of device.
+// Correspond to BATTERY_LEVEL_...
+BATTERY_LEVEL :: enum BYTE {
+	EMPTY  = 0x00,
+	LOW    = 0x01,
+	MEDIUM = 0x02,
+	FULL   = 0x03,
+}
+
+// User index definitions
+
+// Index of the gamer associated with the device
+XUSER :: enum DWORD {
+	One   = 0,
+	Two   = 1,
+	Three = 2,
+	Four  = 3,
+	Any   = 0x000000FF, // Can be only used with XInputGetKeystroke
+}
+
+XUSER_MAX_COUNT :: 4
+
+// Codes returned for the gamepad keystroke
+// Corresponds to VK_PAD_...
+VK_PAD :: enum WORD {
+	A                = 0x5800,
+	B                = 0x5801,
+	X                = 0x5802,
+	Y                = 0x5803,
+	RSHOULDER        = 0x5804,
+	LSHOULDER        = 0x5805,
+	LTRIGGER         = 0x5806,
+	RTRIGGER         = 0x5807,
+	DPAD_UP          = 0x5810,
+	DPAD_DOWN        = 0x5811,
+	DPAD_LEFT        = 0x5812,
+	DPAD_RIGHT       = 0x5813,
+	START            = 0x5814,
+	BACK             = 0x5815,
+	LTHUMB_PRESS     = 0x5816,
+	RTHUMB_PRESS     = 0x5817,
+	LTHUMB_UP        = 0x5820,
+	LTHUMB_DOWN      = 0x5821,
+	LTHUMB_RIGHT     = 0x5822,
+	LTHUMB_LEFT      = 0x5823,
+	LTHUMB_UPLEFT    = 0x5824,
+	LTHUMB_UPRIGHT   = 0x5825,
+	LTHUMB_DOWNRIGHT = 0x5826,
+	LTHUMB_DOWNLEFT  = 0x5827,
+	RTHUMB_UP        = 0x5830,
+	RTHUMB_DOWN      = 0x5831,
+	RTHUMB_RIGHT     = 0x5832,
+	RTHUMB_LEFT      = 0x5833,
+	RTHUMB_UPLEFT    = 0x5834,
+	RTHUMB_UPRIGHT   = 0x5835,
+	RTHUMB_DOWNRIGHT = 0x5836,
+	RTHUMB_DOWNLEFT  = 0x5837,
+}
+
+// Flags used in XINPUT_KEYSTROKE
+// Correspond to log2(XINPUT_KEYSTROKE_...)
+XINPUT_KEYSTROKE_BIT :: enum WORD {
+	KEYDOWN = 0,
+	KEYUP   = 1,
+	REPEAT  = 2,
+}
+XINPUT_KEYSTROKES :: distinct bit_set[XINPUT_KEYSTROKE_BIT;WORD]
+
+// Structures used by XInput APIs
+XINPUT_GAMEPAD :: struct {
+	wButtons:      XINPUT_GAMEPAD_BUTTON,
+	bLeftTrigger:  BYTE,
+	bRightTrigger: BYTE,
+	sThumbLX:      SHORT,
+	sThumbLY:      SHORT,
+	sThumbRX:      SHORT,
+	sThumbRY:      SHORT,
+}
+
+XINPUT_STATE :: struct {
+	dwPacketNumber: DWORD,
+	Gamepad:        XINPUT_GAMEPAD,
+}
+
+XINPUT_VIBRATION :: struct {
+	wLeftMotorSpeed:  WORD,
+	wRightMotorSpeed: WORD,
+}
+
+XINPUT_CAPABILITIES :: struct {
+	Type:      XINPUT_DEVTYPE,
+	SubType:   XINPUT_DEVSUBTYPE,
+	Flags:     XINPUT_CAPS,
+	Gamepad:   XINPUT_GAMEPAD,
+	Vibration: XINPUT_VIBRATION,
+}
+
+XINPUT_BATTERY_INFORMATION :: struct {
+	BatteryType:  BATTERY_TYPE,
+	BatteryLevel: BATTERY_LEVEL,
+}
+
+XINPUT_KEYSTROKE :: struct {
+	VirtualKey: VK_PAD,
+	Unicode:    WCHAR,
+	Flags:      XINPUT_KEYSTROKES,
+	UserIndex:  XUSER,
+	HidCode:    BYTE,
+}
+
+// XInput APIs
+@(default_calling_convention = "system")
+foreign xinput {
+	XInputGetState :: proc(user: XUSER, pState: ^XINPUT_STATE) -> System_Error ---
+	XInputSetState :: proc(user: XUSER, pVibration: ^XINPUT_VIBRATION) -> System_Error ---
+	XInputGetCapabilities :: proc(user: XUSER, dwFlags: XINPUT_FLAG, pCapabilities: ^XINPUT_CAPABILITIES) -> System_Error ---
+	XInputEnable :: proc(enable: BOOL) ---
+	XInputGetAudioDeviceIds :: proc(user: XUSER, pRenderDeviceId: LPWSTR, pRenderCount: ^UINT, pCaptureDeviceId: LPWSTR, pCaptureCount: ^UINT) -> System_Error ---
+	XInputGetBatteryInformation :: proc(user: XUSER, devType: BATTERY_DEVTYPE, pBatteryInformation: ^XINPUT_BATTERY_INFORMATION) -> System_Error ---
+	XInputGetKeystroke :: proc(user: XUSER, dwReserved: DWORD, pKeystroke: ^XINPUT_KEYSTROKE) -> System_Error ---
+	XInputGetDSoundAudioDeviceGuids :: proc(user: XUSER, pDSoundRenderGuid: ^GUID, pDSoundCaptureGuid: ^GUID) -> System_Error ---
+}

+ 1 - 0
core/testing/runner.odin

@@ -391,6 +391,7 @@ runner :: proc(internal_tests: []Internal_Test) -> bool {
 		fmt.assertf(alloc_error == nil, "Error allocating memory for task allocator #%i: %v", i, alloc_error)
 		fmt.assertf(alloc_error == nil, "Error allocating memory for task allocator #%i: %v", i, alloc_error)
 		when TRACKING_MEMORY {
 		when TRACKING_MEMORY {
 			mem.tracking_allocator_init(&task_memory_trackers[i], mem.rollback_stack_allocator(&task_allocators[i]))
 			mem.tracking_allocator_init(&task_memory_trackers[i], mem.rollback_stack_allocator(&task_allocators[i]))
+			task_memory_trackers[i].bad_free_callback = mem.tracking_allocator_bad_free_callback_add_to_array
 		}
 		}
 	}
 	}
 
 

+ 8 - 5
src/llvm_backend_debug.cpp

@@ -408,13 +408,18 @@ gb_internal LLVMMetadataRef lb_debug_union(lbModule *m, Type *type, String name,
 	lb_set_llvm_metadata(m, type, temp_forward_decl);
 	lb_set_llvm_metadata(m, type, temp_forward_decl);
 
 
 	isize index_offset = 1;
 	isize index_offset = 1;
+	isize variant_offset = 1;
 	if (is_type_union_maybe_pointer(bt)) {
 	if (is_type_union_maybe_pointer(bt)) {
 		index_offset = 0;
 		index_offset = 0;
+		variant_offset = 0;
+	} else if (bt->Union.kind == UnionType_no_nil) {
+		variant_offset = 0;
 	}
 	}
 
 
 	LLVMMetadataRef member_scope = lb_get_llvm_metadata(m, bt->Union.scope);
 	LLVMMetadataRef member_scope = lb_get_llvm_metadata(m, bt->Union.scope);
 	unsigned element_count = cast(unsigned)bt->Union.variants.count;
 	unsigned element_count = cast(unsigned)bt->Union.variants.count;
 	if (index_offset > 0) {
 	if (index_offset > 0) {
+		GB_ASSERT(index_offset == 1);
 		element_count += 1;
 		element_count += 1;
 	}
 	}
 
 
@@ -437,13 +442,11 @@ gb_internal LLVMMetadataRef lb_debug_union(lbModule *m, Type *type, String name,
 	for_array(j, bt->Union.variants) {
 	for_array(j, bt->Union.variants) {
 		Type *variant = bt->Union.variants[j];
 		Type *variant = bt->Union.variants[j];
 
 
-		unsigned field_index = cast(unsigned)(index_offset+j);
-
-		char name[16] = {};
-		gb_snprintf(name, gb_size_of(name), "v%u", field_index);
+		char name[32] = {};
+		gb_snprintf(name, gb_size_of(name), "v%td", variant_offset+j);
 		isize name_len = gb_strlen(name);
 		isize name_len = gb_strlen(name);
 
 
-		elements[field_index] = LLVMDIBuilderCreateMemberType(
+		elements[index_offset+j] = LLVMDIBuilderCreateMemberType(
 			m->debug_builder, member_scope,
 			m->debug_builder, member_scope,
 			name, name_len,
 			name, name_len,
 			file, line,
 			file, line,

+ 1 - 0
tests/core/container/test_core_rbtree.odin

@@ -11,6 +11,7 @@ import "core:log"
 test_rbtree_integer :: proc(t: ^testing.T, $Key: typeid, $Value: typeid) {
 test_rbtree_integer :: proc(t: ^testing.T, $Key: typeid, $Value: typeid) {
 	track: mem.Tracking_Allocator
 	track: mem.Tracking_Allocator
 	mem.tracking_allocator_init(&track, context.allocator)
 	mem.tracking_allocator_init(&track, context.allocator)
+	track.bad_free_callback = mem.tracking_allocator_bad_free_callback_add_to_array
 	defer mem.tracking_allocator_destroy(&track)
 	defer mem.tracking_allocator_destroy(&track)
 	context.allocator = mem.tracking_allocator(&track)
 	context.allocator = mem.tracking_allocator(&track)
 
 

+ 12 - 12
vendor/wgpu/wgpu.js

@@ -1181,7 +1181,7 @@ class WebGPUInterface {
 			 */
 			 */
 			wgpuBufferSetLabel: (bufferIdx, labelPtr) => {
 			wgpuBufferSetLabel: (bufferIdx, labelPtr) => {
 				const buffer = this.buffers.get(bufferIdx);
 				const buffer = this.buffers.get(bufferIdx);
-				buffer.buffer.label = this.mem.loadCstring(labelPtr);
+				buffer.buffer.label = this.mem.loadCstringDirect(labelPtr);
 			},
 			},
 
 
 			/**
 			/**
@@ -1370,7 +1370,7 @@ class WebGPUInterface {
 			 */
 			 */
 			wgpuCommandEncoderInsertDebugMarker: (commandEncoderIdx, markerLabelPtr) => {
 			wgpuCommandEncoderInsertDebugMarker: (commandEncoderIdx, markerLabelPtr) => {
 				const commandEncoder = this.commandEncoders.get(commandEncoderIdx);
 				const commandEncoder = this.commandEncoders.get(commandEncoderIdx);
-				commandEncoder.insertDebugMarker(this.mem.loadCstring(markerLabelPtr));
+				commandEncoder.insertDebugMarker(this.mem.loadCstringDirect(markerLabelPtr));
 			},
 			},
 
 
 			/**
 			/**
@@ -1387,7 +1387,7 @@ class WebGPUInterface {
 			 */
 			 */
 			wgpuCommandEncoderPushDebugGroup: (commandEncoderIdx, groupLabelPtr) => {
 			wgpuCommandEncoderPushDebugGroup: (commandEncoderIdx, groupLabelPtr) => {
 				const commandEncoder = this.commandEncoders.get(commandEncoderIdx);
 				const commandEncoder = this.commandEncoders.get(commandEncoderIdx);
-				commandEncoder.pushDebugGroup(this.mem.loadCstring(groupLabelPtr));
+				commandEncoder.pushDebugGroup(this.mem.loadCstringDirect(groupLabelPtr));
 			},
 			},
 
 
 			/**
 			/**
@@ -1459,7 +1459,7 @@ class WebGPUInterface {
 			 */
 			 */
 			wgpuComputePassEncoderInsertDebugMarker: (computePassEncoderIdx, markerLabelPtr) => {
 			wgpuComputePassEncoderInsertDebugMarker: (computePassEncoderIdx, markerLabelPtr) => {
 				const computePassEncoder = this.computePassEncoders.get(computePassEncoderIdx);
 				const computePassEncoder = this.computePassEncoders.get(computePassEncoderIdx);
-				computePassEncoder.insertDebugMarker(this.mem.loadCstring(markerLabelPtr));
+				computePassEncoder.insertDebugMarker(this.mem.loadCstringDirect(markerLabelPtr));
 			},
 			},
 
 
 			/**
 			/**
@@ -1476,7 +1476,7 @@ class WebGPUInterface {
 			 */
 			 */
 			wgpuComputePassEncoderPushDebugGroup: (computePassEncoderIdx, groupLabelPtr) => {
 			wgpuComputePassEncoderPushDebugGroup: (computePassEncoderIdx, groupLabelPtr) => {
 				const computePassEncoder = this.computePassEncoders.get(computePassEncoderIdx);
 				const computePassEncoder = this.computePassEncoders.get(computePassEncoderIdx);
-				computePassEncoder.pushDebugGroup(this.mem.loadCstring(groupLabelPtr));
+				computePassEncoder.pushDebugGroup(this.mem.loadCstringDirect(groupLabelPtr));
 			},
 			},
 
 
 			/**
 			/**
@@ -2216,7 +2216,7 @@ class WebGPUInterface {
 			wgpuRenderBundleEncoderInsertDebugMarker: (renderBundleEncoderIdx, markerLabelPtr) => {
 			wgpuRenderBundleEncoderInsertDebugMarker: (renderBundleEncoderIdx, markerLabelPtr) => {
 				const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx);
 				const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx);
 				this.assert(markerLabelPtr != 0);
 				this.assert(markerLabelPtr != 0);
-				const markerLabel = this.mem.loadCstring(markerLabelPtr);
+				const markerLabel = this.mem.loadCstringDirect(markerLabelPtr);
 				renderBundleEncoder.insertDebugMarker(markerLabel);
 				renderBundleEncoder.insertDebugMarker(markerLabel);
 			},
 			},
 
 
@@ -2235,7 +2235,7 @@ class WebGPUInterface {
 			wgpuRenderBundleEncoderPushDebugGroup: (renderBundleEncoderIdx, groupLabelPtr) => {
 			wgpuRenderBundleEncoderPushDebugGroup: (renderBundleEncoderIdx, groupLabelPtr) => {
 				const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx);
 				const renderBundleEncoder = this.renderBundleEncoders.get(renderBundleEncoderIdx);
 				this.assert(groupLabelPtr!= 0);
 				this.assert(groupLabelPtr!= 0);
-				const groupLabel = this.mem.loadCstring(groupLabelPtr);
+				const groupLabel = this.mem.loadCstringDirect(groupLabelPtr);
 				renderBundleEncoder.pushDebugGroup(groupLabel);
 				renderBundleEncoder.pushDebugGroup(groupLabel);
 			},
 			},
 
 
@@ -2407,7 +2407,7 @@ class WebGPUInterface {
 			 */
 			 */
 			wgpuRenderPassEncoderInsertDebugMarker: (renderPassEncoderIdx, markerLabelPtr) => {
 			wgpuRenderPassEncoderInsertDebugMarker: (renderPassEncoderIdx, markerLabelPtr) => {
 				const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx);
 				const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx);
-				const markerLabel = this.mem.loadCstring(markerLabelPtr);
+				const markerLabel = this.mem.loadCstringDirect(markerLabelPtr);
 				renderPassEncoder.insertDebugMarker(markerLabel);
 				renderPassEncoder.insertDebugMarker(markerLabel);
 			},
 			},
 
 
@@ -2425,7 +2425,7 @@ class WebGPUInterface {
 			 */
 			 */
 			wgpuRenderPassEncoderPushDebugGroup: (renderPassEncoderIdx, groupLabelPtr) => {
 			wgpuRenderPassEncoderPushDebugGroup: (renderPassEncoderIdx, groupLabelPtr) => {
 				const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx);
 				const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx);
-				const groupLabel = this.mem.loadCstring(groupLabelPtr);
+				const groupLabel = this.mem.loadCstringDirect(groupLabelPtr);
 				renderPassEncoder.pushDebugGroup(groupLabel);
 				renderPassEncoder.pushDebugGroup(groupLabel);
 			},
 			},
 
 
@@ -2881,11 +2881,11 @@ class WebGPUObjectManager {
 	}
 	}
 
 
 	/**
 	/**
-	 * @param {number} idx
+	 * @param {?number} idx
 	 * @returns {T}
 	 * @returns {T}
 	 */
 	 */
 	get(idx) {
 	get(idx) {
-		return this.objects[idx-1].object;
+		return this.objects[idx-1]?.object;
 	}
 	}
 
 
 	/** @param {number} idx */
 	/** @param {number} idx */
@@ -2908,7 +2908,7 @@ class WebGPUObjectManager {
 		if (withLabelSetter) {
 		if (withLabelSetter) {
 			inter[`wgpu${this.name}SetLabel`] = (idx, labelPtr) => {
 			inter[`wgpu${this.name}SetLabel`] = (idx, labelPtr) => {
 				const obj = this.get(idx);
 				const obj = this.get(idx);
-				obj.label = this.mem.loadCstring(labelPtr);
+				obj.label = this.mem.loadCstringDirect(labelPtr);
 			};
 			};
 		}
 		}
 		return inter;
 		return inter;