Skip to content

vm,types: introduce dict type with value-key support - #421

Open
jow- wants to merge 1 commit into
masterfrom
dict-support
Open

vm,types: introduce dict type with value-key support#421
jow- wants to merge 1 commit into
masterfrom
dict-support

Conversation

@jow-

@jow- jow- commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Summary

Introduce a new dict data structure type that extends regular ucode objects by allowing arbitrary value keys instead of being limited to string keys.

Motivation

Regular ucode objects use null-terminated strings as keys, which is the standard JSON/JavaScript object model. However, there are use cases where you need a map-like structure with non-string keys — e.g., using booleans, numbers, or even other objects as keys. The new dict type fills this gap.

Key Uniqueness Semantics

Key uniqueness follows uc_uniq() semantics:

  • Scalars (null, bool, int, double, string): compared by value equality
  • Non-scalars (arrays, objects, resources, closures): compared by pointer equality
  • NaN doubles: treated as equal for hashing purposes

This means two distinct array instances with identical contents are treated as different keys, while two integer values 42 and 42 are the same key.

Implementation Details

Dict Detection

Dicts reuse the existing UC_OBJECT type internally and are distinguished from regular objects by their hash table's equal_fn function pointer, set to the sentinel uc_dict_equal. This preserves ext_flag for is_constant semantics without requiring a new uc_type enum value.

Hash Table

The dict hash table uses a custom hash function (uc_dict_hash) and equality function (uc_dict_equal) that operate on uc_value_t* keys:

  • For scalars, the hash is computed from the value bytes
  • For non-scalars, the hash is computed from the pointer address
  • The entry destructor (ucv_free_dict_entry) releases references to both key and value uc_value_t objects

GC Integration

Dict keys are uc_value_t* pointers that must be traced during garbage collection. The ucv_gc_mark() function now checks ucv_is_dict() and marks both keys and values for dict objects.

Prototype Chain

Prototype chain lookup works across dict/object boundaries:

  • When looking up a key in a dict and it's not found, the prototype chain is walked
  • If the prototype is a regular object, the dict key is converted to a string for lookup
  • If the prototype is a dict, the value key is used directly

Spread Operator

  • Dict → Object: value keys are converted to strings via ucv_to_string()
  • Object → Dict: string keys become string-value keys
  • Dict → Dict: value keys are copied as-is
  • Array → Dict/Object: handled correctly in both directions

New API

dict() Constructor

let d = dict();                    // empty dict
let d = dict({ a: 1, b: 2 });      // from object
let d = dict([ 10, 20, 30 ]);      // from array (indices become int keys)
let d = dict(other_dict);          // from another dict

ucv_is_dict() Helper

Inline function in types.h for runtime dict detection.

ucv_dict_*() Functions

Function Description
ucv_dict_new() Create a new dict, optionally from a source
ucv_dict_get() Get a value by value key (with proto chain lookup)
ucv_dict_set() Set a value by value key
ucv_dict_delete() Delete a key from a dict
ucv_dict_length() Get the number of entries

ucv_dict_foreach() Macro

Iteration macro for traversing dict entries, yielding uc_value_t* keys and values.

Modified Builtins

The following stdlib functions now handle dicts transparently:

  • length() — returns entry count for dicts
  • keys() — returns actual value keys (not strings) for dicts
  • values() — returns values for dicts
  • exists() — checks key existence using value semantics for dicts

VM Integration

The VM instructions LOAD_VAL, PEEK_VAL, STORE_VAL, UPDATE_VAL, SOBJ, MOBJ, I_NEXT/I_NEXTKV, and DELETE are all updated to dispatch to dict-specific functions when ucv_is_dict() returns true.

Testing

Comprehensive test suite in tests/custom/03_stdlib/69_dict covering:

  • Empty dict creation
  • Initialization from objects, arrays, and dicts
  • All scalar key types (bool, int, float, null, string)
  • NaN key equality
  • Array/object key pointer equality semantics
  • Key updates, deletions
  • keys(), values(), exists(), length() integration
  • Spread operator (dict↔object, dict↔dict)
  • for...in iteration with value keys
  • Prototype chain (dict→object and dict→dict)
  • Increment/decrement operations
  • GC stress test (1000 dicts × 50 keys, valgrind-clean)

@jow-
jow- force-pushed the dict-support branch 6 times, most recently from 60e0b99 to f1c33e0 Compare July 25, 2026 21:24
Add a new dict type that extends ucode objects by allowing arbitrary
value keys (not limited to strings). Key uniqueness follows uc_uniq()
semantics:

  - Scalars (null, bool, int, double, string): compared by value
  - Non-scalars (arrays, objects, etc.): compared by pointer equality
  - NaN doubles are treated as equal

Dicts are distinguished from regular objects by their hash table
equal_fn function pointer, preserving ext_flag for is_constant semantics.

Provided functionality:
  - dict() stdlib constructor accepting optional source object/dict/array
  - keys() / values() returning actual value keys for dicts
  - for...in iteration yielding value keys
  - Spread operator support (dict->object converts keys to strings,
    object->dict preserves strings as string values)
  - Prototype chain lookup across dict/object boundaries
  - GC marking for dict value keys
  - JSON/stringification converting value keys to strings

Add stdlib test suite (tests/custom/03_stdlib/69_dict).

Signed-off-by: Jo-Philipp Wich <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant