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

207
quickjs.c
View file

@ -7936,6 +7936,61 @@ JSAtom JS_ValueToAtom(JSContext *ctx, JSValue val)
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,
JSValue prop)
{
@ -7944,99 +7999,59 @@ static JSValue JS_GetPropertyValue(JSContext *ctx, JSValue this_obj,
if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT &&
JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) {
JSObject *p;
uint32_t idx;
/* fast path for array access */
p = JS_VALUE_GET_OBJ(this_obj);
idx = JS_VALUE_GET_INT(prop);
switch(p->class_id) {
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);
JS_FreeValue(ctx, prop);
if (unlikely(atom == JS_ATOM_NULL))
return JS_EXCEPTION;
ret = JS_GetProperty(ctx, this_obj, atom);
JS_FreeAtom(ctx, atom);
return ret;
JSObject *p = JS_VALUE_GET_OBJ(this_obj);
uint32_t idx = JS_VALUE_GET_INT(prop);
JSValue val;
/* fast path for array and typed array access */
if (js_get_fast_array_element(ctx, p, idx, &val))
return val;
}
atom = JS_ValueToAtom(ctx, prop);
JS_FreeValue(ctx, prop);
if (unlikely(atom == JS_ATOM_NULL))
return JS_EXCEPTION;
ret = JS_GetProperty(ctx, this_obj, atom);
JS_FreeAtom(ctx, atom);
return ret;
}
JSValue JS_GetPropertyUint32(JSContext *ctx, JSValue this_obj,
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:
-1 for exception,
-1 for exception, *pval set to JS_EXCEPTION
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)
{
JSValue val = JS_UNDEFINED;
JSValue val;
JSAtom prop;
int present;
if (likely((uint64_t)idx <= JS_ATOM_MAX_INT)) {
/* fast path */
present = JS_HasProperty(ctx, obj, __JS_AtomFromUInt32(idx));
if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT &&
(uint64_t)idx <= INT32_MAX)) {
/* fast path for array and typed array access */
JSObject *p = JS_VALUE_GET_OBJ(obj);
if (js_get_fast_array_element(ctx, p, idx, pval))
return TRUE;
}
val = JS_EXCEPTION;
present = -1;
prop = JS_NewAtomInt64(ctx, idx);
if (likely(prop != JS_ATOM_NULL)) {
present = JS_HasProperty(ctx, obj, prop);
if (present > 0) {
val = JS_GetPropertyValue(ctx, obj, js_int32(idx));
val = JS_GetProperty(ctx, obj, prop);
if (unlikely(JS_IsException(val)))
present = -1;
} else if (present == FALSE) {
val = JS_UNDEFINED;
}
} else {
prop = JS_NewAtomInt64(ctx, idx);
present = -1;
if (likely(prop != JS_ATOM_NULL)) {
present = JS_HasProperty(ctx, obj, prop);
if (present > 0) {
val = JS_GetProperty(ctx, obj, prop);
if (unlikely(JS_IsException(val)))
present = -1;
}
JS_FreeAtom(ctx, prop);
}
JS_FreeAtom(ctx, prop);
}
*pval = val;
return present;
@ -8047,9 +8062,12 @@ JSValue JS_GetPropertyInt64(JSContext *ctx, JSValue obj, int64_t idx)
JSAtom prop;
JSValue val;
if ((uint64_t)idx <= INT32_MAX) {
/* fast path for fast arrays */
return JS_GetPropertyValue(ctx, obj, js_int32(idx));
if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT &&
(uint64_t)idx <= INT32_MAX)) {
/* 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);
if (prop == JS_ATOM_NULL)
@ -34907,6 +34925,7 @@ static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d,
JSValue desc)
{
JSValue val, getter, setter;
int present;
int flags;
if (!JS_IsObject(desc)) {
@ -34917,7 +34936,10 @@ static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d,
val = JS_UNDEFINED;
getter = 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);
if (JS_IsException(prop))
goto fail;
@ -34925,7 +34947,10 @@ static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d,
if (JS_ToBoolFree(ctx, prop))
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);
if (JS_IsException(prop))
goto fail;
@ -34933,13 +34958,19 @@ static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d,
if (JS_ToBoolFree(ctx, prop))
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;
val = JS_GetProperty(ctx, desc, JS_ATOM_value);
if (JS_IsException(val))
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);
if (JS_IsException(prop))
goto fail;
@ -34947,7 +34978,10 @@ static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d,
if (JS_ToBoolFree(ctx, prop))
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;
getter = JS_GetProperty(ctx, desc, JS_ATOM_get);
if (JS_IsException(getter) ||
@ -34956,7 +34990,10 @@ static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d,
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;
setter = JS_GetProperty(ctx, desc, JS_ATOM_set);
if (JS_IsException(setter) ||
@ -36995,10 +37032,8 @@ static JSValue js_array_at(JSContext *ctx, JSValue this_val,
if (idx < 0 || idx >= len) {
ret = JS_UNDEFINED;
} else if (js_get_fast_array(ctx, obj, &arrp, &count) && count == len) {
ret = js_dup(arrp[idx]);
} else if (!JS_TryGetPropertyInt64(ctx, obj, idx, &ret)) {
ret = JS_UNDEFINED;
} else {
ret = JS_GetPropertyInt64(ctx, obj, idx);
}
exception: