diff --git a/src/debugger/godot4/variables/variant_decoder.ts b/src/debugger/godot4/variables/variant_decoder.ts index 6826a34..8a5b792 100644 --- a/src/debugger/godot4/variables/variant_decoder.ts +++ b/src/debugger/godot4/variables/variant_decoder.ts @@ -24,9 +24,8 @@ import { ENCODE_FLAG_64, ENCODE_FLAG_OBJECT_AS_ID, ENCODE_FLAG_TYPED_ARRAY_MASK, - ENCODE_FLAG_TYPED_ARRAY_BUILTIN, - ENCODE_FLAG_TYPED_ARRAY_CLASS_NAME, - ENCODE_FLAG_TYPED_ARRAY_SCRIPT, + ENCODE_FLAG_TYPED_DICT_MASK, + ContainerTypeFlags, RID, Callable, Signal, @@ -145,15 +144,9 @@ export class VariantDecoder { case GDScriptTypes.SIGNAL: return this.decode_Signal(model); case GDScriptTypes.DICTIONARY: - return this.decode_Dictionary(model); + return this.decode_Dictionary(model, type); case GDScriptTypes.ARRAY: - if (type & ENCODE_FLAG_TYPED_ARRAY_MASK) { - const with_script_name = (type & ENCODE_FLAG_TYPED_ARRAY_CLASS_NAME) != 0; - const with_script_path = (type & ENCODE_FLAG_TYPED_ARRAY_SCRIPT) != 0; - return this.decode_TypedArray(model, with_script_name || with_script_path); - } else { - return this.decode_Array(model); - } + return this.decode_Array(model, type); case GDScriptTypes.PACKED_BYTE_ARRAY: return this.decode_PackedByteArray(model); case GDScriptTypes.PACKED_INT32_ARRAY: @@ -215,6 +208,15 @@ export class VariantDecoder { return output; } + private decode_ContainerTypeFlag(model: BufferModel, type: GDScriptTypes, bitOffset: number) { + const shiftedType = (type >> bitOffset) & 0b11; + if (shiftedType === ContainerTypeFlags.BUILTIN) { + return this.decode_UInt32(model); + } else { + return this.decode_String(model); + } + } + private decode_AABBf(model: BufferModel) { return new AABB(this.decode_Vector3f(model), this.decode_Vector3f(model)); } @@ -223,25 +225,16 @@ export class VariantDecoder { return new AABB(this.decode_Vector3d(model), this.decode_Vector3d(model)); } - private decode_Array(model: BufferModel) { - const output: Array = []; + private decode_Array(model: BufferModel, type: GDScriptTypes) { + const output = []; - const count = this.decode_UInt32(model); - - for (let i = 0; i < count; i++) { - const value = this.decode_variant(model); - output.push(value); + let arrayType = null; + if (type & ENCODE_FLAG_TYPED_ARRAY_MASK) { + arrayType = this.decode_ContainerTypeFlag(model, type, 16); } - - return output; - } - - private decode_TypedArray(model: BufferModel, with_scripts: boolean) { - const output: Array = []; - // TODO: the type information is currently discarded // it needs to be decoded and then packed into the output somehow - const type = with_scripts ? this.decode_String(model) : this.decode_UInt32(model); + const count = this.decode_UInt32(model); for (let i = 0; i < count; i++) { @@ -275,8 +268,20 @@ export class VariantDecoder { return new Color(rgb.x, rgb.y, rgb.z, a); } - private decode_Dictionary(model: BufferModel) { - const output = new Map(); + private decode_Dictionary(model: BufferModel, type: GDScriptTypes) { + const output = new Map(); + + let keyType = null; + let valueType = null; + if (type & ENCODE_FLAG_TYPED_DICT_MASK) { + keyType = this.decode_ContainerTypeFlag(model, type, 16); + valueType = this.decode_ContainerTypeFlag(model, type, 18); + + // console.log("type:", (type >> 16) & 0b11, "keyType:", keyType); + // console.log("type:", type >> 18, "valueType:", valueType); + } + // TODO: the type information is currently discarded + // it needs to be decoded and then packed into the output somehow const count = this.decode_UInt32(model); for (let i = 0; i < count; i++) { diff --git a/src/debugger/godot4/variables/variants.ts b/src/debugger/godot4/variables/variants.ts index 3cc4d7d..e222085 100644 --- a/src/debugger/godot4/variables/variants.ts +++ b/src/debugger/godot4/variables/variants.ts @@ -54,10 +54,14 @@ export enum GDScriptTypes { export const ENCODE_FLAG_64 = 1 << 16; export const ENCODE_FLAG_OBJECT_AS_ID = 1 << 16; -export const ENCODE_FLAG_TYPED_ARRAY_MASK = 3 << 16; -export const ENCODE_FLAG_TYPED_ARRAY_BUILTIN = 1 << 16; -export const ENCODE_FLAG_TYPED_ARRAY_CLASS_NAME = 2 << 16; -export const ENCODE_FLAG_TYPED_ARRAY_SCRIPT = 3 << 16; +export const ENCODE_FLAG_TYPED_ARRAY_MASK = 0b11 << 16; +export const ENCODE_FLAG_TYPED_DICT_MASK = 0b1111 << 16; + +export enum ContainerTypeFlags { + BUILTIN = 1, + CLASS_NAME = 2, + SCRIPT = 3, +} export interface BufferModel { buffer: Buffer; diff --git a/src/debugger/inspector_provider.ts b/src/debugger/inspector_provider.ts index c2e4e63..5125bc9 100644 --- a/src/debugger/inspector_provider.ts +++ b/src/debugger/inspector_provider.ts @@ -1,23 +1,13 @@ -import { - TreeDataProvider, - EventEmitter, - Event, - ProviderResult, - TreeItem, - TreeItemCollapsibleState, -} from "vscode"; +import { TreeDataProvider, EventEmitter, Event, ProviderResult, TreeItem, TreeItemCollapsibleState } from "vscode"; import { GodotVariable, RawObject, ObjectId } from "./debug_runtime"; export class InspectorProvider implements TreeDataProvider { - private _on_did_change_tree_data: EventEmitter< + private _on_did_change_tree_data: EventEmitter = new EventEmitter< RemoteProperty | undefined - > = new EventEmitter(); + >(); private tree: RemoteProperty | undefined; - public readonly onDidChangeTreeData: Event | undefined = this - ._on_did_change_tree_data.event; - - constructor() {} + public readonly onDidChangeTreeData: Event | undefined = this._on_did_change_tree_data.event; public clean_up() { if (this.tree) { @@ -26,12 +16,7 @@ export class InspectorProvider implements TreeDataProvider { } } - public fill_tree( - element_name: string, - class_name: string, - object_id: number, - variable: GodotVariable - ) { + public fill_tree(element_name: string, class_name: string, object_id: number, variable: GodotVariable) { this.tree = this.parse_variable(variable, object_id); this.tree.label = element_name; this.tree.collapsibleState = TreeItemCollapsibleState.Expanded; @@ -39,9 +24,7 @@ export class InspectorProvider implements TreeDataProvider { this._on_did_change_tree_data.fire(undefined); } - public getChildren( - element?: RemoteProperty - ): ProviderResult { + public getChildren(element?: RemoteProperty): ProviderResult { if (!this.tree) { return Promise.resolve([]); } @@ -57,15 +40,11 @@ export class InspectorProvider implements TreeDataProvider { return element; } - public get_changed_value( - parents: RemoteProperty[], - property: RemoteProperty, - new_parsed_value: any - ) { + public get_changed_value(parents: RemoteProperty[], property: RemoteProperty, new_parsed_value: any) { const idx = parents.length - 1; const value = parents[idx].value; if (Array.isArray(value)) { - const idx = parseInt(property.label); + const idx = Number.parseInt(property.label); if (idx < value.length) { value[idx] = new_parsed_value; } @@ -104,13 +83,9 @@ export class InspectorProvider implements TreeDataProvider { if (Number.isInteger(value)) { rendered_value = `${value}`; } else { - rendered_value = `${parseFloat(value.toFixed(5))}`; + rendered_value = `${Number.parseFloat(value.toFixed(5))}`; } - } else if ( - typeof value === "bigint" || - typeof value === "boolean" || - typeof value === "string" - ) { + } else if (typeof value === "bigint" || typeof value === "boolean" || typeof value === "string") { rendered_value = `${value}`; } else if (typeof value === "undefined") { rendered_value = "null"; @@ -131,25 +106,21 @@ export class InspectorProvider implements TreeDataProvider { let child_props: RemoteProperty[] = []; if (value) { - const sub_variables = - typeof value["sub_values"] === "function" && - value instanceof ObjectId === false - ? value.sub_values() - : Array.isArray(value) - ? value.map((va, i) => { - return { name: `${i}`, value: va }; - }) - : value instanceof Map - ? Array.from(value.keys()).map((va) => { - const name = - typeof va["rendered_value"] === "function" - ? va.rendered_value() - : `${va}`; - const map_value = value.get(va); + let sub_variables = []; + if (typeof value.sub_values === "function" && value instanceof ObjectId === false) { + sub_variables = value.sub_values(); + } else if (Array.isArray(value)) { + sub_variables = value.map((va, i) => { + return { name: `${i}`, value: va }; + }); + } else if (value instanceof Map) { + sub_variables = Array.from(value.keys()).map((va) => { + const name = typeof va.rendered_value === "function" ? va.rendered_value() : `${va}`; + const map_value = value.get(va); + return { name: name, value: map_value }; + }); + } - return { name: name, value: map_value }; - }) - : []; child_props = sub_variables?.map((va) => { return this.parse_variable(va, object_id); }); @@ -160,14 +131,12 @@ export class InspectorProvider implements TreeDataProvider { value, object_id, child_props, - child_props.length === 0 - ? TreeItemCollapsibleState.None - : TreeItemCollapsibleState.Collapsed + child_props.length === 0 ? TreeItemCollapsibleState.None : TreeItemCollapsibleState.Collapsed, ); out_prop.description = rendered_value; - out_prop.properties.forEach((prop) => { + for (const prop of out_prop.properties) { prop.parent = out_prop; - }); + } out_prop.description = rendered_value; if (value instanceof ObjectId) { @@ -180,11 +149,10 @@ export class InspectorProvider implements TreeDataProvider { typeof value === "string" ) { out_prop.contextValue = "editable_value"; - } else if ( - Array.isArray(value) || - (value instanceof Map && value instanceof RawObject === false) - ) { - out_prop.properties.forEach((prop) => (prop.changes_parent = true)); + } else if (Array.isArray(value) || (value instanceof Map && value instanceof RawObject === false)) { + for (const prop of out_prop.properties) { + prop.parent = out_prop; + } } return out_prop; @@ -200,7 +168,7 @@ export class RemoteProperty extends TreeItem { public value: any, public object_id: number, public properties: RemoteProperty[], - public collapsibleState?: TreeItemCollapsibleState + public collapsibleState?: TreeItemCollapsibleState, ) { super(label, collapsibleState); }