Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to make object immutable. #1157

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/include/wren.h
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,19 @@ WREN_API void wrenEnsureSlots(WrenVM* vm, int numSlots);
// Gets the type of the object in [slot].
WREN_API WrenType wrenGetSlotType(WrenVM* vm, int slot);

// Try to freeze the object in [slot] for eternity.
WREN_API bool wrenFreezeSlot(WrenVM* vm, int slot);

// Test if the object in [slot] is frozen.
WREN_API bool wrenIsSlotFrozen(WrenVM* vm, int slot);

// Try to (un)freeze the object in [slot].
WREN_API bool wrenSetSlotFrozen(WrenVM* vm, int slot, bool isFrozen);

// Try to (un)freeze the object in [slot] using [secretSlot].
WREN_API bool wrenSetSlotFrozenWithSecret(WrenVM* vm, int slot, bool isFrozen,
int secretSlot);

// Reads a boolean value from [slot].
//
// It is an error to call this if the slot does not contain a boolean value.
Expand Down
59 changes: 59 additions & 0 deletions src/vm/wren_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,8 @@ DEF_PRIMITIVE(list_new)

DEF_PRIMITIVE(list_add)
{
VALIDATE_VALUE_IS_NOT_FROZEN(args[0]);

wrenValueBufferWrite(vm, &AS_LIST(args[0])->elements, args[1]);
RETURN_VAL(args[1]);
}
Expand All @@ -330,6 +332,8 @@ DEF_PRIMITIVE(list_add)
// minimize stack churn.
DEF_PRIMITIVE(list_addCore)
{
VALIDATE_VALUE_IS_NOT_FROZEN(args[0]);

wrenValueBufferWrite(vm, &AS_LIST(args[0])->elements, args[1]);

// Return the list.
Expand All @@ -338,6 +342,8 @@ DEF_PRIMITIVE(list_addCore)

DEF_PRIMITIVE(list_clear)
{
VALIDATE_VALUE_IS_NOT_FROZEN(args[0]);

wrenValueBufferClear(vm, &AS_LIST(args[0])->elements);
RETURN_NULL;
}
Expand All @@ -349,6 +355,8 @@ DEF_PRIMITIVE(list_count)

DEF_PRIMITIVE(list_insert)
{
VALIDATE_VALUE_IS_NOT_FROZEN(args[0]);

ObjList* list = AS_LIST(args[0]);

// count + 1 here so you can "insert" at the very end.
Expand Down Expand Up @@ -392,6 +400,8 @@ DEF_PRIMITIVE(list_iteratorValue)

DEF_PRIMITIVE(list_removeAt)
{
VALIDATE_VALUE_IS_NOT_FROZEN(args[0]);

ObjList* list = AS_LIST(args[0]);
uint32_t index = validateIndex(vm, args[1], list->elements.count, "Index");
if (index == UINT32_MAX) return false;
Expand All @@ -400,6 +410,8 @@ DEF_PRIMITIVE(list_removeAt)
}

DEF_PRIMITIVE(list_removeValue) {
VALIDATE_VALUE_IS_NOT_FROZEN(args[0]);

ObjList* list = AS_LIST(args[0]);
int index = wrenListIndexOf(vm, list, args[1]);
if(index == -1) RETURN_NULL;
Expand All @@ -414,6 +426,8 @@ DEF_PRIMITIVE(list_indexOf)

DEF_PRIMITIVE(list_swap)
{
VALIDATE_VALUE_IS_NOT_FROZEN(args[0]);

ObjList* list = AS_LIST(args[0]);
uint32_t indexA = validateIndex(vm, args[1], list->elements.count, "Index 0");
if (indexA == UINT32_MAX) return false;
Expand Down Expand Up @@ -461,6 +475,8 @@ DEF_PRIMITIVE(list_subscript)

DEF_PRIMITIVE(list_subscriptSetter)
{
VALIDATE_VALUE_IS_NOT_FROZEN(args[0]);

ObjList* list = AS_LIST(args[0]);
uint32_t index = validateIndex(vm, args[1], list->elements.count,
"Subscript");
Expand Down Expand Up @@ -488,6 +504,8 @@ DEF_PRIMITIVE(map_subscript)

DEF_PRIMITIVE(map_subscriptSetter)
{
VALIDATE_VALUE_IS_NOT_FROZEN(args[0]);

if (!validateKey(vm, args[1])) return false;

wrenMapSet(vm, AS_MAP(args[0]), args[1], args[2]);
Expand All @@ -499,6 +517,8 @@ DEF_PRIMITIVE(map_subscriptSetter)
// minimize stack churn.
DEF_PRIMITIVE(map_addCore)
{
VALIDATE_VALUE_IS_NOT_FROZEN(args[0]);

if (!validateKey(vm, args[1])) return false;

wrenMapSet(vm, AS_MAP(args[0]), args[1], args[2]);
Expand All @@ -509,6 +529,8 @@ DEF_PRIMITIVE(map_addCore)

DEF_PRIMITIVE(map_clear)
{
VALIDATE_VALUE_IS_NOT_FROZEN(args[0]);

wrenMapClear(vm, AS_MAP(args[0]));
RETURN_NULL;
}
Expand Down Expand Up @@ -560,6 +582,8 @@ DEF_PRIMITIVE(map_iterate)

DEF_PRIMITIVE(map_remove)
{
VALIDATE_VALUE_IS_NOT_FROZEN(args[0]);

if (!validateKey(vm, args[1])) return false;

RETURN_VAL(wrenMapRemoveKey(vm, AS_MAP(args[0]), args[1]));
Expand Down Expand Up @@ -885,6 +909,30 @@ DEF_PRIMITIVE(object_is)
RETURN_BOOL(false);
}

DEF_PRIMITIVE(object_freeze)
{
RETURN_BOOL(wrenFreeze(args[0]));
}

DEF_PRIMITIVE(object_isFrozen)
{
RETURN_BOOL(wrenIsFrozen(args[0]));
}

DEF_PRIMITIVE(object_setFrozen)
{
if (!validateBool(vm, args[1], "Value")) return false;

RETURN_BOOL(wrenSetFrozen(args[0], AS_BOOL(args[1])));
}

DEF_PRIMITIVE(object_setFrozenWithSecret)
{
if (!validateBool(vm, args[1], "Value")) return false;

RETURN_BOOL(wrenSetFrozenWithSecret(args[0], AS_BOOL(args[1]), args[2]));
}

DEF_PRIMITIVE(object_toString)
{
Obj* obj = AS_OBJ(args[0]);
Expand All @@ -897,6 +945,12 @@ DEF_PRIMITIVE(object_type)
RETURN_OBJ(wrenGetClass(vm, args[0]));
}

DEF_PRIMITIVE(object_validateIsNotFrozen)
{
VALIDATE_VALUE_IS_NOT_FROZEN(args[0]);
RETURN_VAL(args[0]);
}

DEF_PRIMITIVE(range_from)
{
RETURN_NUM(AS_RANGE(args[0])->from);
Expand Down Expand Up @@ -1247,9 +1301,14 @@ void wrenInitializeCore(WrenVM* vm)
PRIMITIVE(vm->objectClass, "!", object_not);
PRIMITIVE(vm->objectClass, "==(_)", object_eqeq);
PRIMITIVE(vm->objectClass, "!=(_)", object_bangeq);
PRIMITIVE(vm->objectClass, "freeze()", object_freeze);
PRIMITIVE(vm->objectClass, "is(_)", object_is);
PRIMITIVE(vm->objectClass, "isFrozen", object_isFrozen);
PRIMITIVE(vm->objectClass, "setFrozen(_)", object_setFrozen);
PRIMITIVE(vm->objectClass, "setFrozen(_,_)", object_setFrozenWithSecret);
PRIMITIVE(vm->objectClass, "toString", object_toString);
PRIMITIVE(vm->objectClass, "type", object_type);
PRIMITIVE(vm->objectClass, "validateIsNotFrozen()", object_validateIsNotFrozen);

// Now we can define Class, which is a subclass of Object.
vm->classClass = defineClass(vm, coreModule, "Class");
Expand Down
3 changes: 3 additions & 0 deletions src/vm/wren_core.wren
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,8 @@ class List is Sequence {
sort() { sort {|low, high| low < high } }

sort(comparer) {
validateIsNotFrozen()

if (!(comparer is Fn)) {
Fiber.abort("Comparer must be a function.")
}
Expand Down Expand Up @@ -409,6 +411,7 @@ class MapEntry {
construct new(key, value) {
_key = key
_value = value
freeze()
}

key { _key }
Expand Down
3 changes: 3 additions & 0 deletions src/vm/wren_core.wren.inc
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,8 @@ static const char* coreModuleSource =
" sort() { sort {|low, high| low < high } }\n"
"\n"
" sort(comparer) {\n"
" validateIsNotFrozen()\n"
"\n"
" if (!(comparer is Fn)) {\n"
" Fiber.abort(\"Comparer must be a function.\")\n"
" }\n"
Expand Down Expand Up @@ -411,6 +413,7 @@ static const char* coreModuleSource =
" construct new(key, value) {\n"
" _key = key\n"
" _value = value\n"
" freeze()\n"
" }\n"
"\n"
" key { _key }\n"
Expand Down
6 changes: 6 additions & 0 deletions src/vm/wren_primitive.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ static uint32_t validateIndexValue(WrenVM* vm, uint32_t count, double value,
return UINT32_MAX;
}

bool validateBool(WrenVM* vm, Value arg, const char* argName)
{
if (IS_BOOL(arg)) return true;
RETURN_ERROR_FMT("$ must be a boolean.", argName);
}

bool validateFn(WrenVM* vm, Value arg, const char* argName)
{
if (IS_CLOSURE(arg)) return true;
Expand Down
15 changes: 15 additions & 0 deletions src/vm/wren_primitive.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,21 @@
return false; \
} while (false)

#define ERROR_MSG_OBJECT_IS_FROZEN "Object is frozen"

#define VALIDATE_VALUE_IS_NOT_FROZEN(value) \
do \
{ \
if (wrenIsFrozen(value)) \
{ \
RETURN_ERROR(ERROR_MSG_OBJECT_IS_FROZEN); \
} \
} while (false)

// Validates that the given [arg] is a bool. Returns true if it is. If not,
// reports an error and returns false.
bool validateBool(WrenVM* vm, Value arg, const char* argName);

// Validates that the given [arg] is a function. Returns true if it is. If not,
// reports an error and returns false.
bool validateFn(WrenVM* vm, Value arg, const char* argName);
Expand Down
12 changes: 12 additions & 0 deletions src/vm/wren_value.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,19 @@ static void initObj(WrenVM* vm, Obj* obj, ObjType type, ObjClass* classObj)
{
obj->type = type;
obj->isDark = false;
obj->isFrozen = false;
obj->frozenSecret = FREEZE_SECRET_DEFAULT;
obj->classObj = classObj;
obj->next = vm->first;
vm->first = obj;
}

static void freezeObj(Obj* obj)
{
obj->isFrozen = true;
obj->frozenSecret = FREEZE_SECRET_ETERNAL;
}

ObjClass* wrenNewSingleClass(WrenVM* vm, int numFields, ObjString* name)
{
ObjClass* classObj = ALLOCATE(vm, ObjClass);
Expand Down Expand Up @@ -664,6 +672,7 @@ Value wrenNewRange(WrenVM* vm, double from, double to, bool isInclusive)
{
ObjRange* range = ALLOCATE(vm, ObjRange);
initObj(vm, &range->obj, OBJ_RANGE, vm->rangeClass);
freezeObj(&range->obj);
range->from = from;
range->to = to;
range->isInclusive = isInclusive;
Expand Down Expand Up @@ -716,6 +725,7 @@ Value wrenNewStringLength(WrenVM* vm, const char* text, size_t length)
ASSERT(length == 0 || text != NULL, "Unexpected NULL string.");

ObjString* string = allocateString(vm, length);
freezeObj(&string->obj);

// Copy the string (if given one).
if (length > 0 && text != NULL) memcpy(string->value, text, length);
Expand Down Expand Up @@ -983,6 +993,8 @@ void wrenGrayObj(WrenVM* vm, Obj* obj)
// It's been reached.
obj->isDark = true;

wrenGrayValue(vm, obj->frozenSecret);

// Add it to the gray list so it can be recursively explored for
// more marks later.
if (vm->grayCount >= vm->grayCapacity)
Expand Down
31 changes: 17 additions & 14 deletions src/vm/wren_value.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,21 +102,8 @@ typedef enum {
OBJ_UPVALUE
} ObjType;

typedef struct sObjClass ObjClass;

// Base struct for all heap-allocated objects.
typedef struct sObj Obj;
struct sObj
{
ObjType type;
bool isDark;

// The object's class.
ObjClass* classObj;

// The next object in the linked list of all currently allocated objects.
struct sObj* next;
};
typedef struct sObjClass ObjClass;

#if WREN_NAN_TAGGING

Expand Down Expand Up @@ -148,6 +135,22 @@ typedef struct

DECLARE_BUFFER(Value, Value);

// Base struct for all heap-allocated objects.
struct sObj
{
ObjType type;
bool isDark;

bool isFrozen;
Value frozenSecret;

// The object's class.
ObjClass* classObj;

// The next object in the linked list of all currently allocated objects.
struct sObj* next;
};

// A heap-allocated string object.
struct sObjString
{
Expand Down
Loading