Skip to content

Commit aa4d2cf

Browse files
Introduce immutable objects
Allow to 'freeze' objects which will raise a runtime error on trying to modify them. Immutability flags is applied to SQObject, which is a handle referencing an actual container, just like const reference in C++. So the object possibly can be mutated by the external code (again like in C++) The immutability is propagated to nested fields during get operation, so if table t={w={x=123}} is immutable, t.w is also immutable. A new _flags field was added added to SQObject. When SQInteger is 64-bit (which in case of Dagor engine is true both on 32 and 64 bit platform) it doesn't affect size of SQObject struct. On 32 bit platform with 32-bit SQInteger it currently increases struct size from 8 to 12 bytes. If needed _flags may be packed together with _type field (since together they need 28+1 bits), or alternatively NaN tagging may be used. Squirrel API added: freeze(object) - make a immutable object handle getobjflags(object) - return object flags Also SQOBJ_FLAG_IMMUTABLE was added to const table
1 parent 2f70d87 commit aa4d2cf

9 files changed

Lines changed: 188 additions & 28 deletions

File tree

include/sqconfig.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11

2+
#if !defined(_MSC_VER) || defined(__clang__) || (_MSC_VER >= 1800)
3+
#define USE_STDINT
4+
#endif
5+
6+
#ifdef USE_STDINT
7+
#include <stdint.h>
8+
#endif
29
#ifdef _SQ64
310

411
#ifdef _MSC_VER

include/squirrel.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ struct SQOuter;
8282
#define SQOBJECT_DELEGABLE 0x02000000
8383
#define SQOBJECT_CANBEFALSE 0x01000000
8484

85+
#define SQOBJ_FLAG_IMMUTABLE 0x01
86+
8587
#define SQ_MATCHTYPEMASKSTRING (-99999)
8688

8789
#define _RT_MASK 0x00FFFFFF
@@ -127,6 +129,8 @@ typedef enum tagSQObjectType{
127129
OT_OUTER = (_RT_OUTER|SQOBJECT_REF_COUNTED) //internal usage only
128130
}SQObjectType;
129131

132+
typedef uint8_t SQObjectFlags;
133+
130134
#define ISREFCOUNTED(t) ((t)&SQOBJECT_REF_COUNTED)
131135

132136

@@ -157,6 +161,7 @@ typedef union tagSQObjectValue
157161
typedef struct tagSQObject
158162
{
159163
SQObjectType _type;
164+
SQObjectFlags _flags;
160165
SQObjectValue _unVal;
161166
}SQObject;
162167

@@ -325,6 +330,7 @@ SQUIRREL_API SQRESULT sq_setfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedI
325330
SQUIRREL_API SQRESULT sq_next(HSQUIRRELVM v,SQInteger idx);
326331
SQUIRREL_API SQRESULT sq_getweakrefval(HSQUIRRELVM v,SQInteger idx);
327332
SQUIRREL_API SQRESULT sq_clear(HSQUIRRELVM v,SQInteger idx);
333+
SQUIRREL_API SQRESULT sq_freeze(HSQUIRRELVM v, SQInteger idx);
328334

329335
/*calls*/
330336
SQUIRREL_API SQRESULT sq_call(HSQUIRRELVM v,SQInteger params,SQBool retval,SQBool invoke_err_handler);
@@ -397,6 +403,7 @@ SQUIRREL_API void sq_setnativedebughook(HSQUIRRELVM v,SQDEBUGHOOK hook);
397403
#define sq_isbool(o) ((o)._type==OT_BOOL)
398404
#define sq_isweakref(o) ((o)._type==OT_WEAKREF)
399405
#define sq_type(o) ((o)._type)
406+
#define sq_objflags(o) ((o)._flags)
400407

401408

402409
#define SQ_OK (0)

squirrel/sqapi.cpp

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ SQRESULT sq_compile(HSQUIRRELVM v,SQLEXREADFUNC read,SQUserPointer p,const SQCha
142142
#ifndef NO_COMPILER
143143
if(Compile(v, read, p, bindings, sourcename, o, raiseerror?true:false, _ss(v)->_debuginfo)) {
144144
v->Push(SQClosure::Create(_ss(v), _funcproto(o),
145-
_table(v->_roottable)->GetWeakRef(_ss(v)->_alloc_ctx, OT_TABLE)));
145+
_table(v->_roottable)->GetWeakRef(_ss(v)->_alloc_ctx, OT_TABLE, 0)));
146146
return SQ_OK;
147147
}
148148
return SQ_ERROR;
@@ -482,7 +482,7 @@ SQRESULT sq_bindenv(HSQUIRRELVM v,SQInteger idx)
482482
!sq_isclass(env) &&
483483
!sq_isinstance(env))
484484
return sq_throwerror(v,_SC("invalid environment"));
485-
SQWeakRef *w = _refcounted(env)->GetWeakRef(_ss(v)->_alloc_ctx, sq_type(env));
485+
SQWeakRef *w = _refcounted(env)->GetWeakRef(_ss(v)->_alloc_ctx, sq_type(env), env._flags);
486486
SQObjectPtr ret;
487487
if(sq_isclosure(o)) {
488488
SQClosure *c = _closure(o)->Clone();
@@ -529,7 +529,7 @@ SQRESULT sq_setclosureroot(HSQUIRRELVM v,SQInteger idx)
529529
SQObject o = stack_get(v, -1);
530530
if(!sq_isclosure(c)) return sq_throwerror(v, _SC("closure expected"));
531531
if(sq_istable(o)) {
532-
_closure(c)->SetRoot(_table(o)->GetWeakRef(_ss(v)->_alloc_ctx, OT_TABLE));
532+
_closure(c)->SetRoot(_table(o)->GetWeakRef(_ss(v)->_alloc_ctx, OT_TABLE, o._flags));
533533
v->Pop();
534534
return SQ_OK;
535535
}
@@ -934,6 +934,12 @@ SQRESULT sq_rawset(HSQUIRRELVM v,SQInteger idx)
934934
v->Pop(2);
935935
return sq_throwerror(v, _SC("null key"));
936936
}
937+
938+
if (self._flags & SQOBJ_FLAG_IMMUTABLE) {
939+
v->Raise_Error(_SC("trying to modify immutable '%s'"),GetTypeName(self));
940+
return SQ_ERROR;
941+
}
942+
937943
switch(sq_type(self)) {
938944
case OT_TABLE:
939945
_table(self)->NewSlot(key, v->GetUp(-1));
@@ -1028,6 +1034,12 @@ SQRESULT sq_rawdeleteslot(HSQUIRRELVM v,SQInteger idx,SQBool pushval)
10281034
sq_aux_paramscheck(v, 2);
10291035
SQObjectPtr *self;
10301036
_GETSAFE_OBJ(v, idx, OT_TABLE,self);
1037+
1038+
if (self->_flags & SQOBJ_FLAG_IMMUTABLE) {
1039+
v->Raise_Error(_SC("trying to modify immutable '%s'"),GetTypeName(*self));
1040+
return SQ_ERROR;
1041+
}
1042+
10311043
SQObjectPtr &key = v->GetUp(-1);
10321044
SQObjectPtr t;
10331045
if(_table(*self)->Get(key,t)) {
@@ -1168,7 +1180,7 @@ void sq_pushobject(HSQUIRRELVM v,HSQOBJECT obj)
11681180

11691181
void sq_resetobject(HSQOBJECT *po)
11701182
{
1171-
po->_unVal.raw=0;po->_type=OT_NULL;
1183+
po->_unVal.raw=0;po->_type=OT_NULL;po->_flags=0;
11721184
}
11731185

11741186
SQRESULT sq_throwerror(HSQUIRRELVM v,const SQChar *err)
@@ -1532,7 +1544,7 @@ void sq_weakref(HSQUIRRELVM v,SQInteger idx)
15321544
{
15331545
SQObject &o=stack_get(v,idx);
15341546
if(ISREFCOUNTED(sq_type(o))) {
1535-
v->Push(_refcounted(o)->GetWeakRef(_ss(v)->_alloc_ctx, sq_type(o)));
1547+
v->Push(_refcounted(o)->GetWeakRef(_ss(v)->_alloc_ctx, sq_type(o), o._flags));
15361548
return;
15371549
}
15381550
v->Push(o);
@@ -1611,6 +1623,21 @@ void sq_move(HSQUIRRELVM dest,HSQUIRRELVM src,SQInteger idx)
16111623
dest->Push(stack_get(src,idx));
16121624
}
16131625

1626+
SQRESULT sq_freeze(HSQUIRRELVM v, SQInteger idx)
1627+
{
1628+
SQObjectPtr &o = stack_get(v, idx);
1629+
SQObjectType tp = sq_type(o);
1630+
if (tp != OT_ARRAY && tp != OT_TABLE && tp != OT_INSTANCE && tp != OT_CLASS && tp != OT_USERDATA) {
1631+
v->Raise_Error(_SC("Cannot freeze %s"), IdType2Name(tp));
1632+
return SQ_ERROR;
1633+
}
1634+
1635+
SQObjectPtr dst = o;
1636+
dst._flags |= SQOBJ_FLAG_IMMUTABLE;
1637+
v->Push(dst);
1638+
return SQ_OK;
1639+
}
1640+
16141641
void sq_setprintfunc(HSQUIRRELVM v, SQPRINTFUNCTION printfunc,SQPRINTFUNCTION errfunc)
16151642
{
16161643
_ss(v)->_printfunc = printfunc;

squirrel/sqbaselib.cpp

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ static SQInteger delegable_getfuncinfos(HSQUIRRELVM v);
2222
static SQInteger class_getfuncinfos(HSQUIRRELVM v);
2323

2424

25+
#define SQ_CHECK_IMMUTABLE_SELF \
26+
if (sq_objflags(stack_get(v,1)) & SQOBJ_FLAG_IMMUTABLE) \
27+
return sq_throwerror(v, _SC("Cannot modify immutable object"));
28+
29+
#define SQ_CHECK_IMMUTABLE_OBJ(o) \
30+
if (sq_objflags(o) & SQOBJ_FLAG_IMMUTABLE) \
31+
return sq_throwerror(v, _SC("Cannot modify immutable object"));
32+
33+
2534
static bool str2num(const SQChar *s,SQObjectPtr &res,SQInteger base)
2635
{
2736
SQChar *end;
@@ -373,6 +382,17 @@ static SQInteger base_clamp(HSQUIRRELVM v)
373382
return 1;
374383
}
375384

385+
static SQInteger base_freeze(HSQUIRRELVM v)
386+
{
387+
return SQ_SUCCEEDED(sq_freeze(v, 2)) ? 1 : SQ_ERROR;
388+
}
389+
390+
static SQInteger base_getobjflags(HSQUIRRELVM v)
391+
{
392+
sq_pushinteger(v, sq_objflags(stack_get(v, 2)));
393+
return 1;
394+
}
395+
376396

377397
static const SQRegFunction base_funcs[]={
378398
//generic
@@ -400,6 +420,8 @@ static const SQRegFunction base_funcs[]={
400420
{_SC("min"),base_min_max<-1>,-3,_SC(".nnnnnnnn")},
401421
{_SC("max"),base_min_max<1>,-3,_SC(".nnnnnnnn")},
402422
{_SC("clamp"),base_clamp,4,_SC(".nnn")},
423+
{_SC("freeze"),base_freeze,2,NULL},
424+
{_SC("getobjflags"), base_getobjflags,2,NULL},
403425
{NULL,(SQFUNCTION)0,0,NULL}
404426
};
405427

@@ -509,6 +531,7 @@ static SQInteger obj_delegate_weakref(HSQUIRRELVM v)
509531

510532
static SQInteger obj_clear(HSQUIRRELVM v)
511533
{
534+
SQ_CHECK_IMMUTABLE_SELF;
512535
return SQ_SUCCEEDED(sq_clear(v,-1)) ? 1 : SQ_ERROR;
513536
}
514537

@@ -524,6 +547,8 @@ static SQInteger number_delegate_tochar(HSQUIRRELVM v)
524547

525548
static SQInteger container_update(HSQUIRRELVM v)
526549
{
550+
SQ_CHECK_IMMUTABLE_SELF;
551+
527552
SQInteger top = sq_gettop(v);
528553
for (int arg=2; arg<=top; ++arg) {
529554
SQObjectType argType = sq_gettype(v, arg);
@@ -545,9 +570,10 @@ static SQInteger container_update(HSQUIRRELVM v)
545570

546571
static SQInteger container_merge(HSQUIRRELVM v)
547572
{
573+
SQObject &o = stack_get(v,1);
574+
548575
SQInteger top = sq_gettop(v);
549-
SQObjectType tp = sq_gettype(v, 1);
550-
switch (tp) {
576+
switch (sq_type(o)) {
551577
case OT_TABLE:
552578
sq_newtableex(v, sq_getsize(v, 1));
553579
break;
@@ -673,6 +699,8 @@ static SQInteger container_findvalue(HSQUIRRELVM v)
673699

674700
static SQInteger table_rawdelete(HSQUIRRELVM v)
675701
{
702+
SQ_CHECK_IMMUTABLE_SELF;
703+
676704
if(SQ_FAILED(sq_rawdeleteslot(v,1,SQTrue)))
677705
return SQ_ERROR;
678706
return 1;
@@ -691,6 +719,8 @@ static SQInteger container_rawexists(HSQUIRRELVM v)
691719

692720
static SQInteger container_rawset(HSQUIRRELVM v)
693721
{
722+
SQ_CHECK_IMMUTABLE_SELF;
723+
694724
return SQ_SUCCEEDED(sq_rawset(v,-3)) ? 1 : SQ_ERROR;
695725
}
696726

@@ -700,6 +730,7 @@ static SQInteger container_rawget(HSQUIRRELVM v)
700730
return SQ_SUCCEEDED(sq_rawget(v,-2))?1:SQ_ERROR;
701731
}
702732

733+
703734
static SQInteger table_setdelegate(HSQUIRRELVM v)
704735
{
705736
if(SQ_FAILED(sq_setdelegate(v,-2)))
@@ -942,6 +973,8 @@ const SQRegFunction SQSharedState::_table_default_delegate_funcz[]={
942973

943974
static SQInteger array_append(HSQUIRRELVM v)
944975
{
976+
SQ_CHECK_IMMUTABLE_SELF;
977+
945978
SQArray *arr = _array(stack_get(v, 1));
946979
SQInteger nitems = sq_gettop(v)-1;
947980
SQInteger offs = arr->Size();
@@ -955,6 +988,8 @@ static SQInteger array_append(HSQUIRRELVM v)
955988

956989
static SQInteger array_extend(HSQUIRRELVM v)
957990
{
991+
SQ_CHECK_IMMUTABLE_SELF;
992+
958993
SQArray *arr = _array(stack_get(v, 1));
959994
SQInteger n = sq_gettop(v)-1;
960995
for (SQInteger i=0; i<n; ++i) {
@@ -969,11 +1004,15 @@ static SQInteger array_extend(HSQUIRRELVM v)
9691004

9701005
static SQInteger array_reverse(HSQUIRRELVM v)
9711006
{
1007+
SQ_CHECK_IMMUTABLE_SELF;
1008+
9721009
return SQ_SUCCEEDED(sq_arrayreverse(v,-1)) ? 1 : SQ_ERROR;
9731010
}
9741011

9751012
static SQInteger array_pop(HSQUIRRELVM v)
9761013
{
1014+
SQ_CHECK_IMMUTABLE_SELF;
1015+
9771016
return SQ_SUCCEEDED(sq_arraypop(v,1,SQTrue))?1:SQ_ERROR;
9781017
}
9791018

@@ -990,6 +1029,9 @@ static SQInteger array_top(HSQUIRRELVM v)
9901029
static SQInteger array_insert(HSQUIRRELVM v)
9911030
{
9921031
SQObject &o=stack_get(v,1);
1032+
1033+
SQ_CHECK_IMMUTABLE_OBJ(o);
1034+
9931035
SQObject &idx=stack_get(v,2);
9941036
SQObject &val=stack_get(v,3);
9951037
if(!_array(o)->Insert(tointeger(idx),val))
@@ -1001,6 +1043,8 @@ static SQInteger array_insert(HSQUIRRELVM v)
10011043
static SQInteger array_remove(HSQUIRRELVM v)
10021044
{
10031045
SQObject &o = stack_get(v, 1);
1046+
SQ_CHECK_IMMUTABLE_OBJ(o);
1047+
10041048
SQObject &idx = stack_get(v, 2);
10051049
if(!sq_isnumeric(idx)) return sq_throwerror(v, _SC("wrong type"));
10061050
SQObjectPtr val;
@@ -1015,6 +1059,8 @@ static SQInteger array_remove(HSQUIRRELVM v)
10151059
static SQInteger array_resize(HSQUIRRELVM v)
10161060
{
10171061
SQObject &o = stack_get(v, 1);
1062+
SQ_CHECK_IMMUTABLE_OBJ(o);
1063+
10181064
SQObject &nsize = stack_get(v, 2);
10191065
SQObjectPtr fill;
10201066
if(sq_isnumeric(nsize)) {
@@ -1079,6 +1125,8 @@ static SQInteger array_map(HSQUIRRELVM v)
10791125
static SQInteger array_apply(HSQUIRRELVM v)
10801126
{
10811127
SQObject &o = stack_get(v,1);
1128+
SQ_CHECK_IMMUTABLE_OBJ(o);
1129+
10821130
if(SQ_FAILED(__map_array(_array(o),_array(o),v,false)))
10831131
return SQ_ERROR;
10841132
sq_pop(v,1);
@@ -1299,6 +1347,8 @@ static SQInteger array_sort(HSQUIRRELVM v)
12991347
{
13001348
SQInteger func = -1;
13011349
SQObjectPtr &o = stack_get(v,1);
1350+
SQ_CHECK_IMMUTABLE_OBJ(o);
1351+
13021352
if(_array(o)->Size() > 1) {
13031353
if(sq_gettop(v) == 2) func = 2;
13041354
if(!_hsort(v, o, 0, _array(o)->Size()-1, func))
@@ -1346,6 +1396,8 @@ static SQInteger array_slice(HSQUIRRELVM v)
13461396

13471397
static SQInteger array_replace(HSQUIRRELVM v)
13481398
{
1399+
SQ_CHECK_IMMUTABLE_SELF;
1400+
13491401
SQArray *dst = _array(stack_get(v, 1));
13501402
SQArray *src = _array(stack_get(v, 2));
13511403
dst->_values.copy(src->_values);
@@ -1380,7 +1432,6 @@ const SQRegFunction SQSharedState::_array_default_delegate_funcz[]={
13801432
{_SC("findvalue"),container_findvalue,-2, _SC("ac.")},
13811433
{_SC("totable"),pairs_to_table,1, _SC("a") },
13821434
{_SC("replace"),array_replace,2, _SC("aa")},
1383-
13841435
{NULL,(SQFUNCTION)0,0,NULL}
13851436
};
13861437

0 commit comments

Comments
 (0)