Skip to content

Commit 1b13fa6

Browse files
author
Fabrice Bellard
committed
added more C callbacks for exotic objects (#324)
1 parent 7645ce5 commit 1b13fa6

File tree

2 files changed

+67
-41
lines changed

2 files changed

+67
-41
lines changed

quickjs.c

+56-41
Original file line numberDiff line numberDiff line change
@@ -1149,13 +1149,8 @@ static JSProperty *add_property(JSContext *ctx,
11491149
static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val);
11501150
JSValue JS_ThrowOutOfMemory(JSContext *ctx);
11511151
static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx);
1152-
static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj);
1153-
static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj,
1154-
JSValueConst proto_val, BOOL throw_flag);
11551152

11561153
static int js_resolve_proxy(JSContext *ctx, JSValueConst *pval, int throw_exception);
1157-
static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj);
1158-
static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj);
11591154
static int JS_CreateProperty(JSContext *ctx, JSObject *p,
11601155
JSAtom prop, JSValueConst val,
11611156
JSValueConst getter, JSValueConst setter,
@@ -7194,7 +7189,8 @@ static inline __exception int js_poll_interrupts(JSContext *ctx)
71947189
}
71957190
}
71967191

7197-
/* return -1 (exception) or TRUE/FALSE */
7192+
/* Return -1 (exception) or TRUE/FALSE. 'throw_flag' = FALSE indicates
7193+
that it is called from Reflect.setPrototypeOf(). */
71987194
static int JS_SetPrototypeInternal(JSContext *ctx, JSValueConst obj,
71997195
JSValueConst proto_val,
72007196
BOOL throw_flag)
@@ -7225,8 +7221,20 @@ static int JS_SetPrototypeInternal(JSContext *ctx, JSValueConst obj,
72257221
if (throw_flag && JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
72267222
return TRUE;
72277223

7228-
if (unlikely(p->class_id == JS_CLASS_PROXY))
7229-
return js_proxy_setPrototypeOf(ctx, obj, proto_val, throw_flag);
7224+
if (unlikely(p->is_exotic)) {
7225+
const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
7226+
int ret;
7227+
if (em && em->set_prototype) {
7228+
ret = em->set_prototype(ctx, obj, proto_val);
7229+
if (ret == 0 && throw_flag) {
7230+
JS_ThrowTypeError(ctx, "proxy: bad prototype");
7231+
return -1;
7232+
} else {
7233+
return ret;
7234+
}
7235+
}
7236+
}
7237+
72307238
sh = p->shape;
72317239
if (sh->proto == proto)
72327240
return TRUE;
@@ -7303,22 +7311,24 @@ static JSValueConst JS_GetPrototypePrimitive(JSContext *ctx, JSValueConst val)
73037311
return val;
73047312
}
73057313

7306-
/* Return an Object, JS_NULL or JS_EXCEPTION in case of Proxy object. */
7314+
/* Return an Object, JS_NULL or JS_EXCEPTION in case of exotic object. */
73077315
JSValue JS_GetPrototype(JSContext *ctx, JSValueConst obj)
73087316
{
73097317
JSValue val;
73107318
if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
73117319
JSObject *p;
73127320
p = JS_VALUE_GET_OBJ(obj);
7313-
if (unlikely(p->class_id == JS_CLASS_PROXY)) {
7314-
val = js_proxy_getPrototypeOf(ctx, obj);
7315-
} else {
7316-
p = p->shape->proto;
7317-
if (!p)
7318-
val = JS_NULL;
7319-
else
7320-
val = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
7321+
if (unlikely(p->is_exotic)) {
7322+
const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
7323+
if (em && em->get_prototype) {
7324+
return em->get_prototype(ctx, obj);
7325+
}
73217326
}
7327+
p = p->shape->proto;
7328+
if (!p)
7329+
val = JS_NULL;
7330+
else
7331+
val = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
73227332
} else {
73237333
val = JS_DupValue(ctx, JS_GetPrototypePrimitive(ctx, obj));
73247334
}
@@ -7365,8 +7375,8 @@ static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val,
73657375
for(;;) {
73667376
proto1 = p->shape->proto;
73677377
if (!proto1) {
7368-
/* slow case if proxy in the prototype chain */
7369-
if (unlikely(p->class_id == JS_CLASS_PROXY)) {
7378+
/* slow case if exotic object in the prototype chain */
7379+
if (unlikely(p->is_exotic && !p->fast_array)) {
73707380
JSValue obj1;
73717381
obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, (JSObject *)p));
73727382
for(;;) {
@@ -8170,30 +8180,37 @@ int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc,
81708180
return JS_GetOwnPropertyInternal(ctx, desc, JS_VALUE_GET_OBJ(obj), prop);
81718181
}
81728182

8173-
/* return -1 if exception (Proxy object only) or TRUE/FALSE */
8183+
/* return -1 if exception (exotic object only) or TRUE/FALSE */
81748184
int JS_IsExtensible(JSContext *ctx, JSValueConst obj)
81758185
{
81768186
JSObject *p;
81778187

81788188
if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
81798189
return FALSE;
81808190
p = JS_VALUE_GET_OBJ(obj);
8181-
if (unlikely(p->class_id == JS_CLASS_PROXY))
8182-
return js_proxy_isExtensible(ctx, obj);
8183-
else
8184-
return p->extensible;
8191+
if (unlikely(p->is_exotic)) {
8192+
const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
8193+
if (em && em->is_extensible) {
8194+
return em->is_extensible(ctx, obj);
8195+
}
8196+
}
8197+
return p->extensible;
81858198
}
81868199

8187-
/* return -1 if exception (Proxy object only) or TRUE/FALSE */
8200+
/* return -1 if exception (exotic object only) or TRUE/FALSE */
81888201
int JS_PreventExtensions(JSContext *ctx, JSValueConst obj)
81898202
{
81908203
JSObject *p;
81918204

81928205
if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
81938206
return FALSE;
81948207
p = JS_VALUE_GET_OBJ(obj);
8195-
if (unlikely(p->class_id == JS_CLASS_PROXY))
8196-
return js_proxy_preventExtensions(ctx, obj);
8208+
if (unlikely(p->is_exotic)) {
8209+
const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
8210+
if (em && em->prevent_extensions) {
8211+
return em->prevent_extensions(ctx, obj);
8212+
}
8213+
}
81978214
p->extensible = FALSE;
81988215
return TRUE;
81998216
}
@@ -46040,7 +46057,7 @@ static JSProxyData *get_proxy_method(JSContext *ctx, JSValue *pmethod,
4604046057
return s;
4604146058
}
4604246059

46043-
static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj)
46060+
static JSValue js_proxy_get_prototype(JSContext *ctx, JSValueConst obj)
4604446061
{
4604546062
JSProxyData *s;
4604646063
JSValue method, ret, proto1;
@@ -46081,8 +46098,8 @@ static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj)
4608146098
return ret;
4608246099
}
4608346100

46084-
static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj,
46085-
JSValueConst proto_val, BOOL throw_flag)
46101+
static int js_proxy_set_prototype(JSContext *ctx, JSValueConst obj,
46102+
JSValueConst proto_val)
4608646103
{
4608746104
JSProxyData *s;
4608846105
JSValue method, ret, proto1;
@@ -46094,21 +46111,15 @@ static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj,
4609446111
if (!s)
4609546112
return -1;
4609646113
if (JS_IsUndefined(method))
46097-
return JS_SetPrototypeInternal(ctx, s->target, proto_val, throw_flag);
46114+
return JS_SetPrototypeInternal(ctx, s->target, proto_val, FALSE);
4609846115
args[0] = s->target;
4609946116
args[1] = proto_val;
4610046117
ret = JS_CallFree(ctx, method, s->handler, 2, args);
4610146118
if (JS_IsException(ret))
4610246119
return -1;
4610346120
res = JS_ToBoolFree(ctx, ret);
46104-
if (!res) {
46105-
if (throw_flag) {
46106-
JS_ThrowTypeError(ctx, "proxy: bad prototype");
46107-
return -1;
46108-
} else {
46109-
return FALSE;
46110-
}
46111-
}
46121+
if (!res)
46122+
return FALSE;
4611246123
res2 = JS_IsExtensible(ctx, s->target);
4611346124
if (res2 < 0)
4611446125
return -1;
@@ -46126,7 +46137,7 @@ static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj,
4612646137
return TRUE;
4612746138
}
4612846139

46129-
static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj)
46140+
static int js_proxy_is_extensible(JSContext *ctx, JSValueConst obj)
4613046141
{
4613146142
JSProxyData *s;
4613246143
JSValue method, ret;
@@ -46152,7 +46163,7 @@ static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj)
4615246163
return res;
4615346164
}
4615446165

46155-
static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj)
46166+
static int js_proxy_prevent_extensions(JSContext *ctx, JSValueConst obj)
4615646167
{
4615746168
JSProxyData *s;
4615846169
JSValue method, ret;
@@ -46860,6 +46871,10 @@ static const JSClassExoticMethods js_proxy_exotic_methods = {
4686046871
.has_property = js_proxy_has,
4686146872
.get_property = js_proxy_get,
4686246873
.set_property = js_proxy_set,
46874+
.get_prototype = js_proxy_get_prototype,
46875+
.set_prototype = js_proxy_set_prototype,
46876+
.is_extensible = js_proxy_is_extensible,
46877+
.prevent_extensions = js_proxy_prevent_extensions,
4686346878
};
4686446879

4686546880
static JSValue js_proxy_constructor(JSContext *ctx, JSValueConst this_val,

quickjs.h

+11
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,17 @@ typedef struct JSClassExoticMethods {
501501
/* return < 0 if exception or TRUE/FALSE */
502502
int (*set_property)(JSContext *ctx, JSValueConst obj, JSAtom atom,
503503
JSValueConst value, JSValueConst receiver, int flags);
504+
505+
/* To get a consistent object behavior when get_prototype != NULL,
506+
get_property, set_property and set_prototype must be != NULL
507+
and the object must be created with a JS_NULL prototype. */
508+
JSValue (*get_prototype)(JSContext *ctx, JSValueConst obj);
509+
/* return < 0 if exception or TRUE/FALSE */
510+
int (*set_prototype)(JSContext *ctx, JSValueConst obj, JSValueConst proto_val);
511+
/* return < 0 if exception or TRUE/FALSE */
512+
int (*is_extensible)(JSContext *ctx, JSValueConst obj);
513+
/* return < 0 if exception or TRUE/FALSE */
514+
int (*prevent_extensions)(JSContext *ctx, JSValueConst obj);
504515
} JSClassExoticMethods;
505516

506517
typedef void JSClassFinalizer(JSRuntime *rt, JSValue val);

0 commit comments

Comments
 (0)