Add debugger support for Typed Dictionaries (#764)

Add decoding support for Typed Dictionaries
This commit is contained in:
David Kincaid
2025-01-03 21:40:31 -05:00
committed by GitHub
parent 996a7aefe6
commit 489db36e85
3 changed files with 73 additions and 96 deletions

View File

@@ -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<any> = [];
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<any> = [];
// 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<any, any>();
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++) {

View File

@@ -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;

View File

@@ -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<RemoteProperty> {
private _on_did_change_tree_data: EventEmitter<
private _on_did_change_tree_data: EventEmitter<RemoteProperty | undefined> = new EventEmitter<
RemoteProperty | undefined
> = new EventEmitter<RemoteProperty | undefined>();
>();
private tree: RemoteProperty | undefined;
public readonly onDidChangeTreeData: Event<RemoteProperty> | undefined = this
._on_did_change_tree_data.event;
constructor() {}
public readonly onDidChangeTreeData: Event<RemoteProperty> | undefined = this._on_did_change_tree_data.event;
public clean_up() {
if (this.tree) {
@@ -26,12 +16,7 @@ export class InspectorProvider implements TreeDataProvider<RemoteProperty> {
}
}
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<RemoteProperty> {
this._on_did_change_tree_data.fire(undefined);
}
public getChildren(
element?: RemoteProperty
): ProviderResult<RemoteProperty[]> {
public getChildren(element?: RemoteProperty): ProviderResult<RemoteProperty[]> {
if (!this.tree) {
return Promise.resolve([]);
}
@@ -57,15 +40,11 @@ export class InspectorProvider implements TreeDataProvider<RemoteProperty> {
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<RemoteProperty> {
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<RemoteProperty> {
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<RemoteProperty> {
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<RemoteProperty> {
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);
}