Add JS_TryGetProperty (#337)

* Optimize `JS_GetPropertyInt64` and `JS_TryGetPropertyInt64`

- add `js_get_fast_array_element()` to special case arrays and typed arrays
- use `js_get_fast_array_element()` in `JS_GetPropertyValue()`,
  `JS_TryGetPropertyInt64()` and `JS_GetPropertyInt64()`.
- simplify `js_array_at()`
This commit is contained in:
Charlie Gordon 2024-04-03 05:10:08 +02:00 committed by GitHub
parent 569b238ec4
commit c15ef1f8dc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

181
quickjs.c
View file

@ -7936,6 +7936,61 @@ JSAtom JS_ValueToAtom(JSContext *ctx, JSValue val)
return atom; return atom;
} }
static BOOL js_get_fast_array_element(JSContext *ctx, JSObject *p,
uint32_t idx, JSValue *pval)
{
switch(p->class_id) {
case JS_CLASS_ARRAY:
case JS_CLASS_ARGUMENTS:
if (unlikely(idx >= p->u.array.count)) return FALSE;
*pval = js_dup(p->u.array.u.values[idx]);
return TRUE;
case JS_CLASS_INT8_ARRAY:
if (unlikely(idx >= p->u.array.count)) return FALSE;
*pval = js_int32(p->u.array.u.int8_ptr[idx]);
return TRUE;
case JS_CLASS_UINT8C_ARRAY:
case JS_CLASS_UINT8_ARRAY:
if (unlikely(idx >= p->u.array.count)) return FALSE;
*pval = js_int32(p->u.array.u.uint8_ptr[idx]);
return TRUE;
case JS_CLASS_INT16_ARRAY:
if (unlikely(idx >= p->u.array.count)) return FALSE;
*pval = js_int32(p->u.array.u.int16_ptr[idx]);
return TRUE;
case JS_CLASS_UINT16_ARRAY:
if (unlikely(idx >= p->u.array.count)) return FALSE;
*pval = js_int32(p->u.array.u.uint16_ptr[idx]);
return TRUE;
case JS_CLASS_INT32_ARRAY:
if (unlikely(idx >= p->u.array.count)) return FALSE;
*pval = js_int32(p->u.array.u.int32_ptr[idx]);
return TRUE;
case JS_CLASS_UINT32_ARRAY:
if (unlikely(idx >= p->u.array.count)) return FALSE;
*pval = js_uint32(p->u.array.u.uint32_ptr[idx]);
return TRUE;
case JS_CLASS_BIG_INT64_ARRAY:
if (unlikely(idx >= p->u.array.count)) return FALSE;
*pval = JS_NewBigInt64(ctx, p->u.array.u.int64_ptr[idx]);
return TRUE;
case JS_CLASS_BIG_UINT64_ARRAY:
if (unlikely(idx >= p->u.array.count)) return FALSE;
*pval = JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]);
return TRUE;
case JS_CLASS_FLOAT32_ARRAY:
if (unlikely(idx >= p->u.array.count)) return FALSE;
*pval = js_float64(p->u.array.u.float_ptr[idx]);
return TRUE;
case JS_CLASS_FLOAT64_ARRAY:
if (unlikely(idx >= p->u.array.count)) return FALSE;
*pval = js_float64(p->u.array.u.double_ptr[idx]);
return TRUE;
default:
return FALSE;
}
}
static JSValue JS_GetPropertyValue(JSContext *ctx, JSValue this_obj, static JSValue JS_GetPropertyValue(JSContext *ctx, JSValue this_obj,
JSValue prop) JSValue prop)
{ {
@ -7944,52 +7999,13 @@ static JSValue JS_GetPropertyValue(JSContext *ctx, JSValue this_obj,
if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT && if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT &&
JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) { JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) {
JSObject *p; JSObject *p = JS_VALUE_GET_OBJ(this_obj);
uint32_t idx; uint32_t idx = JS_VALUE_GET_INT(prop);
/* fast path for array access */ JSValue val;
p = JS_VALUE_GET_OBJ(this_obj); /* fast path for array and typed array access */
idx = JS_VALUE_GET_INT(prop); if (js_get_fast_array_element(ctx, p, idx, &val))
switch(p->class_id) { return val;
case JS_CLASS_ARRAY:
case JS_CLASS_ARGUMENTS:
if (unlikely(idx >= p->u.array.count)) goto slow_path;
return js_dup(p->u.array.u.values[idx]);
case JS_CLASS_INT8_ARRAY:
if (unlikely(idx >= p->u.array.count)) goto slow_path;
return js_int32(p->u.array.u.int8_ptr[idx]);
case JS_CLASS_UINT8C_ARRAY:
case JS_CLASS_UINT8_ARRAY:
if (unlikely(idx >= p->u.array.count)) goto slow_path;
return js_int32(p->u.array.u.uint8_ptr[idx]);
case JS_CLASS_INT16_ARRAY:
if (unlikely(idx >= p->u.array.count)) goto slow_path;
return js_int32(p->u.array.u.int16_ptr[idx]);
case JS_CLASS_UINT16_ARRAY:
if (unlikely(idx >= p->u.array.count)) goto slow_path;
return js_int32(p->u.array.u.uint16_ptr[idx]);
case JS_CLASS_INT32_ARRAY:
if (unlikely(idx >= p->u.array.count)) goto slow_path;
return js_int32(p->u.array.u.int32_ptr[idx]);
case JS_CLASS_UINT32_ARRAY:
if (unlikely(idx >= p->u.array.count)) goto slow_path;
return js_uint32(p->u.array.u.uint32_ptr[idx]);
case JS_CLASS_BIG_INT64_ARRAY:
if (unlikely(idx >= p->u.array.count)) goto slow_path;
return JS_NewBigInt64(ctx, p->u.array.u.int64_ptr[idx]);
case JS_CLASS_BIG_UINT64_ARRAY:
if (unlikely(idx >= p->u.array.count)) goto slow_path;
return JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]);
case JS_CLASS_FLOAT32_ARRAY:
if (unlikely(idx >= p->u.array.count)) goto slow_path;
return js_float64(p->u.array.u.float_ptr[idx]);
case JS_CLASS_FLOAT64_ARRAY:
if (unlikely(idx >= p->u.array.count)) goto slow_path;
return js_float64(p->u.array.u.double_ptr[idx]);
default:
goto slow_path;
} }
} else {
slow_path:
atom = JS_ValueToAtom(ctx, prop); atom = JS_ValueToAtom(ctx, prop);
JS_FreeValue(ctx, prop); JS_FreeValue(ctx, prop);
if (unlikely(atom == JS_ATOM_NULL)) if (unlikely(atom == JS_ATOM_NULL))
@ -7998,46 +8014,45 @@ static JSValue JS_GetPropertyValue(JSContext *ctx, JSValue this_obj,
JS_FreeAtom(ctx, atom); JS_FreeAtom(ctx, atom);
return ret; return ret;
} }
}
JSValue JS_GetPropertyUint32(JSContext *ctx, JSValue this_obj, JSValue JS_GetPropertyUint32(JSContext *ctx, JSValue this_obj,
uint32_t idx) uint32_t idx)
{ {
return JS_GetPropertyValue(ctx, this_obj, js_uint32(idx)); return JS_GetPropertyInt64(ctx, this_obj, idx);
} }
/* Check if an object has a generalized numeric property. Return value: /* Check if an object has a generalized numeric property. Return value:
-1 for exception, -1 for exception, *pval set to JS_EXCEPTION
TRUE if property exists, stored into *pval, TRUE if property exists, stored into *pval,
FALSE if proprty does not exist. FALSE if property does not exist. *pval set to JS_UNDEFINED.
*/ */
static int JS_TryGetPropertyInt64(JSContext *ctx, JSValue obj, int64_t idx, JSValue *pval) static int JS_TryGetPropertyInt64(JSContext *ctx, JSValue obj, int64_t idx, JSValue *pval)
{ {
JSValue val = JS_UNDEFINED; JSValue val;
JSAtom prop; JSAtom prop;
int present; int present;
if (likely((uint64_t)idx <= JS_ATOM_MAX_INT)) { if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT &&
/* fast path */ (uint64_t)idx <= INT32_MAX)) {
present = JS_HasProperty(ctx, obj, __JS_AtomFromUInt32(idx)); /* fast path for array and typed array access */
if (present > 0) { JSObject *p = JS_VALUE_GET_OBJ(obj);
val = JS_GetPropertyValue(ctx, obj, js_int32(idx)); if (js_get_fast_array_element(ctx, p, idx, pval))
if (unlikely(JS_IsException(val))) return TRUE;
present = -1;
} }
} else { val = JS_EXCEPTION;
prop = JS_NewAtomInt64(ctx, idx);
present = -1; present = -1;
prop = JS_NewAtomInt64(ctx, idx);
if (likely(prop != JS_ATOM_NULL)) { if (likely(prop != JS_ATOM_NULL)) {
present = JS_HasProperty(ctx, obj, prop); present = JS_HasProperty(ctx, obj, prop);
if (present > 0) { if (present > 0) {
val = JS_GetProperty(ctx, obj, prop); val = JS_GetProperty(ctx, obj, prop);
if (unlikely(JS_IsException(val))) if (unlikely(JS_IsException(val)))
present = -1; present = -1;
} else if (present == FALSE) {
val = JS_UNDEFINED;
} }
JS_FreeAtom(ctx, prop); JS_FreeAtom(ctx, prop);
} }
}
*pval = val; *pval = val;
return present; return present;
} }
@ -8047,9 +8062,12 @@ JSValue JS_GetPropertyInt64(JSContext *ctx, JSValue obj, int64_t idx)
JSAtom prop; JSAtom prop;
JSValue val; JSValue val;
if ((uint64_t)idx <= INT32_MAX) { if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT &&
/* fast path for fast arrays */ (uint64_t)idx <= INT32_MAX)) {
return JS_GetPropertyValue(ctx, obj, js_int32(idx)); /* fast path for array and typed array access */
JSObject *p = JS_VALUE_GET_OBJ(obj);
if (js_get_fast_array_element(ctx, p, idx, &val))
return val;
} }
prop = JS_NewAtomInt64(ctx, idx); prop = JS_NewAtomInt64(ctx, idx);
if (prop == JS_ATOM_NULL) if (prop == JS_ATOM_NULL)
@ -34907,6 +34925,7 @@ static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d,
JSValue desc) JSValue desc)
{ {
JSValue val, getter, setter; JSValue val, getter, setter;
int present;
int flags; int flags;
if (!JS_IsObject(desc)) { if (!JS_IsObject(desc)) {
@ -34917,7 +34936,10 @@ static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d,
val = JS_UNDEFINED; val = JS_UNDEFINED;
getter = JS_UNDEFINED; getter = JS_UNDEFINED;
setter = JS_UNDEFINED; setter = JS_UNDEFINED;
if (JS_HasProperty(ctx, desc, JS_ATOM_enumerable)) { present = JS_HasProperty(ctx, desc, JS_ATOM_enumerable);
if (present < 0)
goto fail;
if (present) {
JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_enumerable); JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_enumerable);
if (JS_IsException(prop)) if (JS_IsException(prop))
goto fail; goto fail;
@ -34925,7 +34947,10 @@ static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d,
if (JS_ToBoolFree(ctx, prop)) if (JS_ToBoolFree(ctx, prop))
flags |= JS_PROP_ENUMERABLE; flags |= JS_PROP_ENUMERABLE;
} }
if (JS_HasProperty(ctx, desc, JS_ATOM_configurable)) { present = JS_HasProperty(ctx, desc, JS_ATOM_configurable);
if (present < 0)
goto fail;
if (present) {
JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_configurable); JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_configurable);
if (JS_IsException(prop)) if (JS_IsException(prop))
goto fail; goto fail;
@ -34933,13 +34958,19 @@ static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d,
if (JS_ToBoolFree(ctx, prop)) if (JS_ToBoolFree(ctx, prop))
flags |= JS_PROP_CONFIGURABLE; flags |= JS_PROP_CONFIGURABLE;
} }
if (JS_HasProperty(ctx, desc, JS_ATOM_value)) { present = JS_HasProperty(ctx, desc, JS_ATOM_value);
if (present < 0)
goto fail;
if (present) {
flags |= JS_PROP_HAS_VALUE; flags |= JS_PROP_HAS_VALUE;
val = JS_GetProperty(ctx, desc, JS_ATOM_value); val = JS_GetProperty(ctx, desc, JS_ATOM_value);
if (JS_IsException(val)) if (JS_IsException(val))
goto fail; goto fail;
} }
if (JS_HasProperty(ctx, desc, JS_ATOM_writable)) { present = JS_HasProperty(ctx, desc, JS_ATOM_writable);
if (present < 0)
goto fail;
if (present) {
JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_writable); JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_writable);
if (JS_IsException(prop)) if (JS_IsException(prop))
goto fail; goto fail;
@ -34947,7 +34978,10 @@ static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d,
if (JS_ToBoolFree(ctx, prop)) if (JS_ToBoolFree(ctx, prop))
flags |= JS_PROP_WRITABLE; flags |= JS_PROP_WRITABLE;
} }
if (JS_HasProperty(ctx, desc, JS_ATOM_get)) { present = JS_HasProperty(ctx, desc, JS_ATOM_get);
if (present < 0)
goto fail;
if (present) {
flags |= JS_PROP_HAS_GET; flags |= JS_PROP_HAS_GET;
getter = JS_GetProperty(ctx, desc, JS_ATOM_get); getter = JS_GetProperty(ctx, desc, JS_ATOM_get);
if (JS_IsException(getter) || if (JS_IsException(getter) ||
@ -34956,7 +34990,10 @@ static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d,
goto fail; goto fail;
} }
} }
if (JS_HasProperty(ctx, desc, JS_ATOM_set)) { present = JS_HasProperty(ctx, desc, JS_ATOM_set);
if (present < 0)
goto fail;
if (present) {
flags |= JS_PROP_HAS_SET; flags |= JS_PROP_HAS_SET;
setter = JS_GetProperty(ctx, desc, JS_ATOM_set); setter = JS_GetProperty(ctx, desc, JS_ATOM_set);
if (JS_IsException(setter) || if (JS_IsException(setter) ||
@ -36995,10 +37032,8 @@ static JSValue js_array_at(JSContext *ctx, JSValue this_val,
if (idx < 0 || idx >= len) { if (idx < 0 || idx >= len) {
ret = JS_UNDEFINED; ret = JS_UNDEFINED;
} else if (js_get_fast_array(ctx, obj, &arrp, &count) && count == len) { } else {
ret = js_dup(arrp[idx]); ret = JS_GetPropertyInt64(ctx, obj, idx);
} else if (!JS_TryGetPropertyInt64(ctx, obj, idx, &ret)) {
ret = JS_UNDEFINED;
} }
exception: exception: