diff --git a/dom/permission/tests/file_framework.js b/dom/permission/tests/file_framework.js index dddb278ead6..e05d479946a 100644 --- a/dom/permission/tests/file_framework.js +++ b/dom/permission/tests/file_framework.js @@ -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; diff --git a/js/public/Class.h b/js/public/Class.h index 32277167a4e..894d3d3152f 100644 --- a/js/public/Class.h +++ b/js/public/Class.h @@ -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; diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp index 9e51f43740d..28052d6bf75 100644 --- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -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 */ } diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index ad756814e76..8465e36bff6 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -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; } diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index b27ffd52b56..52efb84f3d8 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -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 desc, unsigned flags) @@ -2720,6 +2748,18 @@ Proxy::unwatch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id) return proxy->as().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().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 */ } diff --git a/js/src/jsproxy.h b/js/src/jsproxy.h index dd17ae1842a..3360abf449f 100644 --- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -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, diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index c591eebbd2f..c821bfdd7a9 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -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, } diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index cacdc05cb9d..74339e88347 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -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 */ \ } \ } diff --git a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp index e65c0fbd76e..d890b151e9b 100644 --- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp +++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp @@ -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, } diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 264f09a5c40..b2f9d0b0714 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -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, \ }