Bug 697343 - Introduce a slice hook to allow optimizing Array.prototype.slice for Proxies etc. r=jandem,bz

This commit is contained in:
Tom Schuster 2013-12-05 20:07:24 +01:00
parent b053653c80
commit 7313632237
10 changed files with 96 additions and 17 deletions

View File

@ -169,7 +169,11 @@ function expandPermissions(aPerms) {
aPerms.forEach(function(el) {
var access = permTable[el].access ? "readwrite" : null;
var expanded = SpecialPowers.unwrap(expand(el, access));
perms = perms.concat(expanded.slice(0));
// COW arrays don't behave array-like enough, to allow
// using expanded.slice(0) here.
for (let i = 0; i < expanded.length; i++) {
perms.push(expanded[i]);
}
});
return perms;

View File

@ -380,6 +380,10 @@ typedef bool
typedef bool
(* UnwatchOp)(JSContext *cx, JS::HandleObject obj, JS::HandleId id);
typedef bool
(* SliceOp)(JSContext *cx, JS::HandleObject obj, uint32_t begin, uint32_t end,
JS::HandleObject result); // result is actually preallocted.
typedef JSObject *
(* ObjectOp)(JSContext *cx, JS::HandleObject obj);
typedef void
@ -468,6 +472,7 @@ struct ObjectOps
DeleteSpecialOp deleteSpecial;
WatchOp watch;
UnwatchOp unwatch;
SliceOp slice; // Optimized slice, can be null.
JSNewEnumerateOp enumerate;
ObjectOp thisObject;

View File

@ -2461,6 +2461,7 @@ const Class TypedObject::class_ = {
TypedDatum::obj_deleteElement,
TypedDatum::obj_deleteSpecial,
nullptr, nullptr, // watch/unwatch
nullptr, // slice
TypedDatum::obj_enumerate,
nullptr, /* thisObject */
}
@ -2552,6 +2553,7 @@ const Class TypedHandle::class_ = {
TypedDatum::obj_deleteElement,
TypedDatum::obj_deleteSpecial,
nullptr, nullptr, // watch/unwatch
nullptr, // slice
TypedDatum::obj_enumerate,
nullptr, /* thisObject */
}

View File

@ -2673,20 +2673,18 @@ js::array_concat(JSContext *cx, unsigned argc, Value *vp)
static bool
array_slice(JSContext *cx, unsigned argc, Value *vp)
{
uint32_t length, begin, end, slot;
bool hole;
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject obj(cx, ToObject(cx, args.thisv()));
if (!obj)
return false;
uint32_t length;
if (!GetLengthProperty(cx, obj, &length))
return false;
begin = 0;
end = length;
uint32_t begin = 0;
uint32_t end = length;
if (args.length() > 0) {
double d;
if (!ToInteger(cx, args[0], &d))
@ -2734,13 +2732,23 @@ array_slice(JSContext *cx, unsigned argc, Value *vp)
return true;
}
if (js::SliceOp op = obj->getOps()->slice) {
if (!op(cx, obj, begin, end, narr))
return false;
args.rval().setObject(*narr);
return true;
}
RootedValue value(cx);
for (slot = begin; slot < end; slot++) {
for (uint32_t slot = begin; slot < end; slot++) {
bool hole;
if (!JS_CHECK_OPERATION_LIMIT(cx) ||
!GetElement(cx, obj, slot, &hole, &value)) {
!GetElement(cx, obj, slot, &hole, &value))
{
return false;
}
if (!hole && !SetArrayElement(cx, narr, slot - begin, value))
if (!hole && !JSObject::defineElement(cx, narr, slot - begin, value))
return false;
}

View File

@ -364,6 +364,34 @@ BaseProxyHandler::unwatch(JSContext *cx, HandleObject proxy, HandleId id)
return true;
}
bool
BaseProxyHandler::slice(JSContext *cx, HandleObject proxy, uint32_t begin, uint32_t end,
HandleObject result)
{
assertEnteredPolicy(cx, proxy, JSID_VOID);
RootedId id(cx);
RootedValue value(cx);
for (uint32_t index = begin; index < end; index++) {
if (!IndexToId(cx, index, id.address()))
return false;
bool present;
if (!Proxy::has(cx, proxy, id, &present))
return false;
if (present) {
if (!Proxy::get(cx, proxy, proxy, id, &value))
return false;
if (!JSObject::defineElement(cx, result, index - begin, value))
return false;
}
}
return true;
}
bool
DirectProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
MutableHandle<PropertyDescriptor> desc, unsigned flags)
@ -2720,6 +2748,18 @@ Proxy::unwatch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id)
return proxy->as<ProxyObject>().handler()->unwatch(cx, proxy, id);
}
/* static */ bool
Proxy::slice(JSContext *cx, HandleObject proxy, uint32_t begin, uint32_t end,
HandleObject result)
{
JS_CHECK_RECURSION(cx, return false);
BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::GET, true);
if (!policy.allowed())
return policy.returnValue();
return handler->slice(cx, proxy, begin, end, result);
}
static JSObject *
proxy_innerObject(JSContext *cx, HandleObject obj)
{
@ -3013,17 +3053,24 @@ proxy_Construct(JSContext *cx, unsigned argc, Value *vp)
}
static bool
proxy_Watch(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable)
proxy_Watch(JSContext *cx, HandleObject obj, HandleId id, HandleObject callable)
{
return Proxy::watch(cx, obj, id, callable);
}
static bool
proxy_Unwatch(JSContext *cx, JS::HandleObject obj, JS::HandleId id)
proxy_Unwatch(JSContext *cx, HandleObject obj, HandleId id)
{
return Proxy::unwatch(cx, obj, id);
}
static bool
proxy_Slice(JSContext *cx, HandleObject proxy, uint32_t begin, uint32_t end,
HandleObject result)
{
return Proxy::slice(cx, proxy, begin, end, result);
}
#define PROXY_CLASS_EXT \
{ \
nullptr, /* outerObject */ \
@ -3076,6 +3123,7 @@ proxy_Unwatch(JSContext *cx, JS::HandleObject obj, JS::HandleId id)
proxy_DeleteElement, \
proxy_DeleteSpecial, \
proxy_Watch, proxy_Unwatch, \
proxy_Slice, \
nullptr, /* enumerate */ \
nullptr, /* thisObject */ \
} \
@ -3133,6 +3181,7 @@ const Class js::OuterWindowProxyObject::class_ = {
proxy_DeleteElement,
proxy_DeleteSpecial,
proxy_Watch, proxy_Unwatch,
proxy_Slice,
nullptr, /* enumerate */
nullptr, /* thisObject */
}

View File

@ -170,6 +170,9 @@ class JS_FRIEND_API(BaseProxyHandler)
JS::HandleObject callable);
virtual bool unwatch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id);
virtual bool slice(JSContext *cx, HandleObject proxy, uint32_t begin, uint32_t end,
HandleObject result);
/* See comment for weakmapKeyDelegateOp in js/Class.h. */
virtual JSObject *weakmapKeyDelegate(JSObject *proxy);
virtual bool isScripted() { return false; }
@ -278,9 +281,11 @@ class Proxy
static bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp);
static bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop);
static bool watch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
JS::HandleObject callable);
static bool unwatch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id);
static bool watch(JSContext *cx, HandleObject proxy, HandleId id, HandleObject callable);
static bool unwatch(JSContext *cx, HandleObject proxy, HandleId id);
static bool slice(JSContext *cx, HandleObject obj, uint32_t begin, uint32_t end,
HandleObject result);
/* IC entry path for handling __noSuchMethod__ on access. */
static bool callProp(JSContext *cx, HandleObject proxy, HandleObject reveiver, HandleId id,

View File

@ -567,7 +567,8 @@ const Class WithObject::class_ = {
with_DeleteProperty,
with_DeleteElement,
with_DeleteSpecial,
nullptr, nullptr, /* watch/unwatch */
nullptr, nullptr, /* watch/unwatch */
nullptr, /* slice */
with_Enumerate,
with_ThisObject,
}

View File

@ -3460,8 +3460,9 @@ const Class ArrayBufferObject::class_ = {
ArrayBufferObject::obj_deleteElement,
ArrayBufferObject::obj_deleteSpecial,
nullptr, nullptr, /* watch/unwatch */
nullptr, /* slice */
ArrayBufferObject::obj_enumerate,
nullptr, /* thisObject */
nullptr, /* thisObject */
}
};
@ -3622,8 +3623,9 @@ IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float64, double, double)
_typedArray##Object::obj_deleteElement, \
_typedArray##Object::obj_deleteSpecial, \
nullptr, nullptr, /* watch/unwatch */ \
nullptr, /* slice */ \
_typedArray##Object::obj_enumerate, \
nullptr, /* thisObject */ \
nullptr, /* thisObject */ \
} \
}

View File

@ -731,6 +731,7 @@ const XPCWrappedNativeJSClass XPC_WN_NoHelper_JSClass = {
nullptr, // deleteElement
nullptr, // deleteSpecial
nullptr, nullptr, // watch/unwatch
nullptr, // slice
XPC_WN_JSOp_Enumerate,
XPC_WN_JSOp_ThisObject,
}

View File

@ -993,6 +993,7 @@ XPC_WN_JSOp_ThisObject(JSContext *cx, JS::HandleObject obj);
nullptr, /* deleteElement */ \
nullptr, /* deleteSpecial */ \
nullptr, nullptr, /* watch/unwatch */ \
nullptr, /* slice */ \
XPC_WN_JSOp_Enumerate, \
XPC_WN_JSOp_ThisObject, \
}
@ -1021,6 +1022,7 @@ XPC_WN_JSOp_ThisObject(JSContext *cx, JS::HandleObject obj);
nullptr, /* deleteElement */ \
nullptr, /* deleteSpecial */ \
nullptr, nullptr, /* watch/unwatch */ \
nullptr, /* slice */ \
XPC_WN_JSOp_Enumerate, \
XPC_WN_JSOp_ThisObject, \
}