зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1020609 - Implement Xrays to Arrays. r=bz
This commit is contained in:
Родитель
f93f11d656
Коммит
07bee3c907
|
@ -115,6 +115,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681
|
|||
|
||||
testObject();
|
||||
|
||||
testArray();
|
||||
|
||||
// We could also test DataView and Iterator here for completeness, but it's
|
||||
// more trouble than it's worth.
|
||||
|
||||
|
@ -145,6 +147,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681
|
|||
["constructor", "toSource", "toString", "toLocaleString", "valueOf", "watch",
|
||||
"unwatch", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable",
|
||||
"__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__"];
|
||||
gPrototypeProperties['Array'] =
|
||||
["length", "toSource", "toString", "toLocaleString", "join", "reverse", "sort", "push",
|
||||
"pop", "shift", "unshift", "splice", "concat", "slice", "lastIndexOf", "indexOf",
|
||||
"forEach", "map", "reduce", "reduceRight", "filter", "some", "every", "mapPar",
|
||||
"reducePar", "scanPar", "scatterPar", "filterPar", "find", "findIndex", "copyWithin",
|
||||
"fill", "@@iterator", "entries", "keys", "constructor"];
|
||||
|
||||
function filterOut(array, props) {
|
||||
return array.filter(p => props.indexOf(p) == -1);
|
||||
|
@ -225,14 +233,70 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681
|
|||
set getterSetterProp(x) { }, \
|
||||
callableProp: function() { }, \
|
||||
nonXrayableProp: new WeakMap() })');
|
||||
testTrickyObject(trickyObject);
|
||||
|
||||
}
|
||||
|
||||
function testArray() {
|
||||
// The |length| property is generally very weird, especially with respect
|
||||
// to its behavior on the prototype. Array.prototype is actually an Array
|
||||
// instance, and therefore has a vestigial .length. But we don't want to
|
||||
// show that over Xrays, and generally want .length to just appear as an
|
||||
// |own| data property. So we add it to the ignore list here, and check it
|
||||
// separately.
|
||||
let propsToSkip = ['length'];
|
||||
testXray('Array', new iwin.Array(20), new iwin.Array(), propsToSkip);
|
||||
|
||||
var trickyArray =
|
||||
iwin.eval("var trickyArray = []; \
|
||||
trickyArray.primitiveProp = 42; \
|
||||
trickyArray.objectProp = { foo: 2 }; \
|
||||
trickyArray.xoProp = top.location; \
|
||||
trickyArray.hasOwnProperty = 10; \
|
||||
Object.defineProperty(trickyArray, 'getterProp', { get: function() { return 2; }}); \
|
||||
Object.defineProperty(trickyArray, 'setterProp', { set: function(x) {}}); \
|
||||
Object.defineProperty(trickyArray, 'getterSetterProp', { get: function() { return 3; }, set: function(x) {}}); \
|
||||
trickyArray.callableProp = function() {}; \
|
||||
trickyArray.nonXrayableProp = new WeakMap(); \
|
||||
trickyArray;");
|
||||
|
||||
// Test indexed access.
|
||||
trickyArray.wrappedJSObject[9] = "some indexed property";
|
||||
is(trickyArray[9], "some indexed property", "indexed properties work correctly over Xrays");
|
||||
is(trickyArray.length, 10, "Length works correctly over Xrays");
|
||||
checkThrows(function() { "use strict"; delete trickyArray.length; }, /config/, "Can't delete non-configurable 'length' property");
|
||||
delete trickyArray[9];
|
||||
is(trickyArray[9], undefined, "Delete works correctly over Xrays");
|
||||
is(trickyArray.wrappedJSObject[9], undefined, "Delete works correctly over Xrays (viewed via waiver)");
|
||||
is(trickyArray.length, 10, "length doesn't change");
|
||||
trickyArray[11] = "some other indexed property";
|
||||
is(trickyArray.length, 12, "length now changes");
|
||||
is(trickyArray.wrappedJSObject[11], "some other indexed property");
|
||||
trickyArray.length = 0;
|
||||
is(trickyArray.length, 0, "Setting length works over Xray");
|
||||
is(trickyArray[11], undefined, "Setting length truncates over Xray");
|
||||
Object.defineProperty(trickyArray, 'length', { configurable: false, enumerable: false, writable: false, value: 0 });
|
||||
trickyArray[1] = "hi";
|
||||
is(trickyArray.length, 0, "Length remains non-writable");
|
||||
is(trickyArray[1], undefined, "Frozen length forbids new properties");
|
||||
|
||||
testTrickyObject(trickyArray);
|
||||
}
|
||||
|
||||
// Parts of this function are kind of specific to testing Object, but we factor
|
||||
// it out so that we can re-use the trickyObject stuff on Arrays.
|
||||
function testTrickyObject(trickyObject) {
|
||||
|
||||
// Make sure it looks right under the hood.
|
||||
is(trickyObject.wrappedJSObject.getterProp, 2, "Underlying object has getter");
|
||||
is(Cu.unwaiveXrays(trickyObject.wrappedJSObject.xoProp), top.location, "Underlying object has xo property");
|
||||
|
||||
// Test getOwnPropertyNames.
|
||||
var expectedNames = ['objectProp', 'primitiveProp'];
|
||||
if (trickyObject instanceof iwin.Array)
|
||||
expectedNames.push('length');
|
||||
is(Object.getOwnPropertyNames(trickyObject).sort().toSource(),
|
||||
['objectProp', 'primitiveProp'].toSource(), "getOwnPropertyNames should be filtered correctly");
|
||||
expectedNames.sort().toSource(), "getOwnPropertyNames should be filtered correctly");
|
||||
|
||||
// Test iteration and in-place modification. Beware of 'expando', which is the property
|
||||
// we placed on the xray proto.
|
||||
|
|
|
@ -122,14 +122,16 @@ WrapperFactory::DoubleWrap(JSContext *cx, HandleObject obj, unsigned flags)
|
|||
|
||||
// In general, we're trying to deprecate COWs incrementally as we introduce
|
||||
// Xrays to the corresponding object types. But switching off COWs for Object
|
||||
// instances would be too tumultuous at present, so we punt on that for later.
|
||||
// and Array instances would be too tumultuous at present, so we punt on that
|
||||
// for later.
|
||||
static bool
|
||||
ForceCOWBehavior(JSObject *obj)
|
||||
{
|
||||
if (IdentifyStandardInstanceOrPrototype(obj) == JSProto_Object) {
|
||||
JSProtoKey key = IdentifyStandardInstanceOrPrototype(obj);
|
||||
if (key == JSProto_Object || key == JSProto_Array) {
|
||||
MOZ_ASSERT(GetXrayType(obj) == XrayForJSObject,
|
||||
"We should use XrayWrappers for standard ES Object instances "
|
||||
"modulo this hack");
|
||||
"We should use XrayWrappers for standard ES Object and Array "
|
||||
"instances modulo this hack");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -46,6 +46,7 @@ IsJSXraySupported(JSProtoKey key)
|
|||
switch (key) {
|
||||
case JSProto_Date:
|
||||
case JSProto_Object:
|
||||
case JSProto_Array:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
@ -472,7 +473,7 @@ bool JSXrayTraits::getOwnPropertyFromTargetIfSafe(JSContext *cx,
|
|||
// forcibly override the behavior here for Arrays until bug 987163
|
||||
// lands.
|
||||
JSProtoKey key = IdentifyStandardInstanceOrPrototype(propObj);
|
||||
if (key != JSProto_Array && key != JSProto_Uint8ClampedArray &&
|
||||
if (key != JSProto_Uint8ClampedArray &&
|
||||
key != JSProto_Int8Array && key != JSProto_Uint8Array &&
|
||||
key != JSProto_Int16Array && key != JSProto_Uint16Array &&
|
||||
key != JSProto_Int32Array && key != JSProto_Uint32Array &&
|
||||
|
@ -519,10 +520,18 @@ JSXrayTraits::resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper,
|
|||
|
||||
RootedObject target(cx, getTargetObject(wrapper));
|
||||
if (!isPrototype(holder)) {
|
||||
// For object instances, we expose some properties from the underlying
|
||||
// object, but only after filtering them carefully.
|
||||
// For Object and Array instances, we expose some properties from the
|
||||
// underlying object, but only after filtering them carefully.
|
||||
//
|
||||
// Note that, as far as JS observables go, Arrays are just Objects with
|
||||
// a different prototype and a magic (own, non-configurable) |.length| that
|
||||
// serves as a non-tight upper bound on |own| indexed properties. So while
|
||||
// it's tempting to try to impose some sort of structure on what Arrays
|
||||
// "should" look like over Xrays, the underlying object is squishy enough
|
||||
// that it makes sense to just treat them like Objects for Xray purposes.
|
||||
switch (getProtoKey(holder)) {
|
||||
case JSProto_Object:
|
||||
case JSProto_Array:
|
||||
{
|
||||
JSAutoCompartment ac(cx, target);
|
||||
if (!getOwnPropertyFromTargetIfSafe(cx, target, wrapper, id, desc))
|
||||
|
@ -669,8 +678,10 @@ JSXrayTraits::delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp
|
|||
// If we're using Object Xrays, we allow callers to attempt to delete any
|
||||
// property from the underlying object that they are able to resolve. Note
|
||||
// that this deleting may fail if the property is non-configurable.
|
||||
bool isObjectInstance = getProtoKey(holder) == JSProto_Object && !isPrototype(holder);
|
||||
if (isObjectInstance) {
|
||||
JSProtoKey key = getProtoKey(holder);
|
||||
bool isObjectOrArrayInstance = (key == JSProto_Object || key == JSProto_Array) &&
|
||||
!isPrototype(holder);
|
||||
if (isObjectOrArrayInstance) {
|
||||
RootedObject target(cx, getTargetObject(wrapper));
|
||||
JSAutoCompartment ac(cx, target);
|
||||
Rooted<JSPropertyDescriptor> desc(cx);
|
||||
|
@ -695,34 +706,36 @@ JSXrayTraits::defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
|
|||
return false;
|
||||
|
||||
|
||||
// Object instances are special. For that case, we forward property
|
||||
// Object and Array instances are special. For those cases, we forward property
|
||||
// definitions to the underlying object if the following conditions are met:
|
||||
// * The property being defined is a value-prop.
|
||||
// * The property being defined is either a primitive or subsumed by the target.
|
||||
// * As seen from the Xray, any existing property that we would overwrite is an
|
||||
// |own| value-prop.
|
||||
//
|
||||
// To avoid confusion, we disallow expandos on Object instances, and
|
||||
// To avoid confusion, we disallow expandos on Object and Array instances, and
|
||||
// therefore raise an exception here if the above conditions aren't met.
|
||||
bool isObjectInstance = getProtoKey(holder) == JSProto_Object && !isPrototype(holder);
|
||||
if (isObjectInstance) {
|
||||
JSProtoKey key = getProtoKey(holder);
|
||||
bool isObjectOrArrayInstance = (key == JSProto_Object || key == JSProto_Array) &&
|
||||
!isPrototype(holder);
|
||||
if (isObjectOrArrayInstance) {
|
||||
RootedObject target(cx, getTargetObject(wrapper));
|
||||
if (desc.hasGetterOrSetter()) {
|
||||
JS_ReportError(cx, "Not allowed to define accessor property on [Object] XrayWrapper");
|
||||
JS_ReportError(cx, "Not allowed to define accessor property on [Object] or [Array] XrayWrapper");
|
||||
return false;
|
||||
}
|
||||
if (desc.value().isObject() &&
|
||||
!AccessCheck::subsumes(target, js::UncheckedUnwrap(&desc.value().toObject())))
|
||||
{
|
||||
JS_ReportError(cx, "Not allowed to define cross-origin object as property on [Object] XrayWrapper");
|
||||
JS_ReportError(cx, "Not allowed to define cross-origin object as property on [Object] or [Array] XrayWrapper");
|
||||
return false;
|
||||
}
|
||||
if (existingDesc.hasGetterOrSetter()) {
|
||||
JS_ReportError(cx, "Not allowed to overwrite accessor property on [Object] XrayWrapper");
|
||||
JS_ReportError(cx, "Not allowed to overwrite accessor property on [Object] or [Array] XrayWrapper");
|
||||
return false;
|
||||
}
|
||||
if (existingDesc.object() && existingDesc.object() != wrapper) {
|
||||
JS_ReportError(cx, "Not allowed to shadow non-own Xray-resolved property on [Object] XrayWrapper");
|
||||
JS_ReportError(cx, "Not allowed to shadow non-own Xray-resolved property on [Object] or [Array] XrayWrapper");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -750,10 +763,11 @@ JSXrayTraits::enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags
|
|||
return false;
|
||||
|
||||
if (!isPrototype(holder)) {
|
||||
// For object instances, we expose some properties from the underlying
|
||||
// For Object and Array instances, we expose some properties from the underlying
|
||||
// object, but only after filtering them carefully.
|
||||
switch (getProtoKey(holder)) {
|
||||
case JSProto_Object:
|
||||
case JSProto_Array:
|
||||
MOZ_ASSERT(props.empty());
|
||||
{
|
||||
JSAutoCompartment ac(cx, target);
|
||||
|
|
Загрузка…
Ссылка в новой задаче