feat: add containsKey method to Map and validate key types in subscript operations

Implement `containsKey()` native method on Map that returns a boolean indicating whether the given key exists in the map. Add `validateKey()` helper to reject non-value-type keys (objects, lists, fibers, etc.) with a clear runtime error. Refactor `wrenMapGet()` signature to return success/failure via bool and output the value through an out-parameter, enabling both `containsKey()` and the existing subscript operator to share the same key validation and lookup logic. Update `map_subscript` to return `null` instead of crashing when a key is not found. Add test cases for `containsKey` success/failure, and runtime error tests for invalid key types in subscript getter, setter, and containsKey.
This commit is contained in:
Bob Nystrom
2015-01-25 20:39:19 +00:00
parent 1d195f13ea
commit a83205ef89
8 changed files with 65 additions and 10 deletions
+33 -3
View File
@@ -230,6 +230,21 @@ static int validateIndexValue(WrenVM* vm, Value* args, int count, double value,
return -1;
}
// Validates that [key] is a valid object for use as a map key. Returns true if
// it is. If not, reports an error and returns false.
static bool validateKey(WrenVM* vm, Value* args, int index)
{
Value arg = args[index];
if (IS_BOOL(arg) || IS_CLASS(arg) || IS_NULL(arg) ||
IS_NUM(arg) || IS_RANGE(arg) || IS_STRING(arg))
{
return true;
}
args[0] = OBJ_VAL(wrenNewString(vm, "Key must be a value type.", 25));
return false;
}
// Validates that the argument at [argIndex] is an integer within `[0, count)`.
// Also allows negative indices which map backwards from the end. Returns the
// valid positive index value. If invalid, reports an error and returns -1.
@@ -719,13 +734,19 @@ DEF_NATIVE(map_instantiate)
DEF_NATIVE(map_subscript)
{
// TODO: Validate key type.
RETURN_VAL(wrenMapGet(AS_MAP(args[0]), args[1]));
if (!validateKey(vm, args, 1)) return PRIM_ERROR;
Value value;
if (wrenMapGet(AS_MAP(args[0]), args[1], &value)) RETURN_VAL(value);
// Not found.
RETURN_NULL;
}
DEF_NATIVE(map_subscriptSetter)
{
// TODO: Validate key type.
if (!validateKey(vm, args, 1)) return PRIM_ERROR;
wrenMapSet(vm, AS_MAP(args[0]), args[1], args[2]);
RETURN_VAL(args[2]);
}
@@ -740,6 +761,14 @@ DEF_NATIVE(map_clear)
RETURN_NULL;
}
DEF_NATIVE(map_containsKey)
{
if (!validateKey(vm, args, 1)) return PRIM_ERROR;
Value dummy;
RETURN_BOOL(wrenMapGet(AS_MAP(args[0]), args[1], &dummy));
}
DEF_NATIVE(map_count)
{
RETURN_NUM(AS_MAP(args[0])->count);
@@ -1468,6 +1497,7 @@ void wrenInitializeCore(WrenVM* vm)
NATIVE(vm->mapClass, "[ ]", map_subscript);
NATIVE(vm->mapClass, "[ ]=", map_subscriptSetter);
NATIVE(vm->mapClass, "clear", map_clear);
NATIVE(vm->mapClass, "containsKey ", map_containsKey);
NATIVE(vm->mapClass, "count", map_count);
NATIVE(vm->mapClass, "iterate_ ", map_iterate);
NATIVE(vm->mapClass, "keyIteratorValue_ ", map_keyIteratorValue);