mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Add Debugger.Object.prototype.defineProperties and a few defineProperty tests.
This commit is contained in:
parent
01f08a3395
commit
ef3cb9f6dc
46
js/src/jit-test/tests/debug/Object-defineProperties-01.js
Normal file
46
js/src/jit-test/tests/debug/Object-defineProperties-01.js
Normal file
@ -0,0 +1,46 @@
|
||||
// Debug.Object.prototype.defineProperties.
|
||||
|
||||
var g = newGlobal('new-compartment');
|
||||
var dbg = new Debugger;
|
||||
var gw = dbg.addDebuggee(g);
|
||||
|
||||
var descProps = ['configurable', 'enumerable', 'writable', 'value', 'get', 'set'];
|
||||
function test(objexpr, descs) {
|
||||
g.eval("obj = (" + objexpr + ");");
|
||||
var gobjw = gw.getOwnPropertyDescriptor("obj").value;
|
||||
gobjw.defineProperties(descs);
|
||||
|
||||
var indirectEval = eval;
|
||||
var obj = indirectEval("(" + objexpr + ");");
|
||||
Object.defineProperties(obj, descs);
|
||||
|
||||
var ids = Object.keys(descs);
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
var actual = gobjw.getOwnPropertyDescriptor(ids[i]);
|
||||
var expected = Object.getOwnPropertyDescriptor(obj, ids[i]);
|
||||
assertEq(Object.getPrototypeOf(actual), Object.prototype);
|
||||
assertEq(actual.configurable, expected.configurable);
|
||||
assertEq(actual.enumerable, expected.enumerable);
|
||||
for (var j = 0; j < descProps; j++) {
|
||||
var prop = descProps[j];
|
||||
assertEq(prop in actual, prop in expected);
|
||||
assertEq(actual[prop], expected[prop]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test("{}", {});
|
||||
test("/abc/", {});
|
||||
|
||||
g.eval("var aglobal = newGlobal('same-compartment');");
|
||||
var aglobal = newGlobal('same-compartment');
|
||||
test("aglobal", {});
|
||||
|
||||
var adescs = {a: {enumerable: true, writable: true, value: 0}};
|
||||
test("{}", adescs);
|
||||
test("{a: 1}", adescs);
|
||||
|
||||
var arrdescs = [{value: 'a'}, {value: 'b'}, , {value: 'd'}];
|
||||
test("{}", arrdescs);
|
||||
test("[]", arrdescs);
|
||||
test("[0, 1, 2, 3]", arrdescs);
|
32
js/src/jit-test/tests/debug/Object-defineProperties-02.js
Normal file
32
js/src/jit-test/tests/debug/Object-defineProperties-02.js
Normal file
@ -0,0 +1,32 @@
|
||||
// Exceptions thrown by obj.defineProperties are copied into the debugger compartment.
|
||||
|
||||
var g = newGlobal('new-compartment');
|
||||
var dbg = new Debugger;
|
||||
var gw = dbg.addDebuggee(g);
|
||||
|
||||
function test(objexpr, descs) {
|
||||
var exca, excb;
|
||||
|
||||
g.eval("obj = (" + objexpr + ");");
|
||||
var gobjw = gw.getOwnPropertyDescriptor("obj").value;
|
||||
try {
|
||||
gobjw.defineProperties(descs);
|
||||
} catch (exc) {
|
||||
exca = exc;
|
||||
}
|
||||
|
||||
var indirectEval = eval;
|
||||
var obj = indirectEval("(" + objexpr + ");");
|
||||
try {
|
||||
Object.defineProperties(obj, descs);
|
||||
} catch (exc) {
|
||||
excb = exc;
|
||||
}
|
||||
|
||||
assertEq(Object.getPrototypeOf(exca), Object.getPrototypeOf(excb));
|
||||
assertEq(exca.message, excb.message);
|
||||
assertEq(typeof exca.fileName, "string");
|
||||
assertEq(typeof exca.stack, "string");
|
||||
}
|
||||
|
||||
test("Object.create(null, {p: {value: 1}})", {p: {value: 2}});
|
20
js/src/jit-test/tests/debug/Object-defineProperties-03.js
Normal file
20
js/src/jit-test/tests/debug/Object-defineProperties-03.js
Normal file
@ -0,0 +1,20 @@
|
||||
// obj.defineProperties can define accessor properties.
|
||||
|
||||
var g = newGlobal('new-compartment');
|
||||
var dbg = new Debugger;
|
||||
var gw = dbg.addDebuggee(g);
|
||||
g.value = undefined;
|
||||
g.eval("function gf() { return 12; }\n" +
|
||||
"function sf(v) { value = v; }\n");
|
||||
var gfw = gw.getOwnPropertyDescriptor("gf").value;
|
||||
var sfw = gw.getOwnPropertyDescriptor("sf").value;
|
||||
gw.defineProperties({x: {configurable: true, get: gfw, set: sfw}});
|
||||
assertEq(g.x, 12);
|
||||
g.x = 'ok';
|
||||
assertEq(g.value, 'ok');
|
||||
|
||||
var desc = g.Object.getOwnPropertyDescriptor(g, "x");
|
||||
assertEq(desc.configurable, true);
|
||||
assertEq(desc.enumerable, false);
|
||||
assertEq(desc.get, g.gf);
|
||||
assertEq(desc.set, g.sf);
|
@ -2,8 +2,6 @@
|
||||
// Also: when defineProperty throws, the exception is native to the debugger
|
||||
// compartment, not a wrapper.
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
var g = newGlobal('new-compartment');
|
||||
var dbg = new Debugger;
|
||||
var gw = dbg.addDebuggee(g);
|
||||
|
16
js/src/jit-test/tests/debug/Object-defineProperty-11.js
Normal file
16
js/src/jit-test/tests/debug/Object-defineProperty-11.js
Normal file
@ -0,0 +1,16 @@
|
||||
// obj.defineProperty works when obj's referent is a wrapper.
|
||||
|
||||
var x = {};
|
||||
var g = newGlobal('new-compartment');
|
||||
g.x = x;
|
||||
var dbg = new Debugger;
|
||||
var gw = dbg.addDebuggee(g);
|
||||
var xw = gw.getOwnPropertyDescriptor("x").value;
|
||||
xw.defineProperty("p", {configurable: true, enumerable: true, writable: true, value: gw});
|
||||
assertEq(x.p, g);
|
||||
|
||||
var desc = Object.getOwnPropertyDescriptor(x, "p");
|
||||
assertEq(desc.configurable, true);
|
||||
assertEq(desc.enumerable, true);
|
||||
assertEq(desc.writable, true);
|
||||
assertEq(desc.value, g);
|
18
js/src/jit-test/tests/debug/Object-defineProperty-12.js
Normal file
18
js/src/jit-test/tests/debug/Object-defineProperty-12.js
Normal file
@ -0,0 +1,18 @@
|
||||
// obj.defineProperty redefining an existing property leaves unspecified attributes unchanged.
|
||||
|
||||
var g = newGlobal('new-compartment');
|
||||
g.p = 1;
|
||||
var dbg = new Debugger;
|
||||
var gw = dbg.addDebuggee(g);
|
||||
|
||||
gw.defineProperty("p", {value: 2});
|
||||
assertEq(g.p, 2);
|
||||
|
||||
var desc = Object.getOwnPropertyDescriptor(g, "p");
|
||||
assertEq(desc.configurable, true);
|
||||
assertEq(desc.enumerable, true);
|
||||
assertEq(desc.writable, true);
|
||||
assertEq(desc.value, 2);
|
||||
|
||||
g.p = 3;
|
||||
assertEq(g.p, 3);
|
113
js/src/jsdbg.cpp
113
js/src/jsdbg.cpp
@ -43,7 +43,6 @@
|
||||
#include "jsapi.h"
|
||||
#include "jscntxt.h"
|
||||
#include "jsemit.h"
|
||||
#include "jsexn.h"
|
||||
#include "jsgcmark.h"
|
||||
#include "jsobj.h"
|
||||
#include "jstl.h"
|
||||
@ -2929,6 +2928,36 @@ CheckArgCompartment(JSContext *cx, JSObject *obj, const Value &v,
|
||||
return true;
|
||||
}
|
||||
|
||||
// Convert Debugger.Objects in decs to debuggee values.
|
||||
// Reject non-callable getters and setters.
|
||||
static bool
|
||||
UnwrapPropDesc(JSContext *cx, Debugger *dbg, JSObject *obj, PropDesc *desc)
|
||||
{
|
||||
return (!desc->hasValue || (dbg->unwrapDebuggeeValue(cx, &desc->value) &&
|
||||
CheckArgCompartment(cx, obj, desc->value, "defineProperty",
|
||||
"value"))) &&
|
||||
(!desc->hasGet || (dbg->unwrapDebuggeeValue(cx, &desc->get) &&
|
||||
CheckArgCompartment(cx, obj, desc->get, "defineProperty", "get") &&
|
||||
desc->checkGetter(cx))) &&
|
||||
(!desc->hasSet || (dbg->unwrapDebuggeeValue(cx, &desc->set) &&
|
||||
CheckArgCompartment(cx, obj, desc->set, "defineProperty", "set") &&
|
||||
desc->checkSetter(cx)));
|
||||
}
|
||||
|
||||
// Rewrap *idp and the fields of *desc for the current compartment. Also:
|
||||
// defining a property on a proxy requiers the pd field to contain a descriptor
|
||||
// object, so reconstitute desc->pd if needed.
|
||||
static bool
|
||||
WrapIdAndPropDesc(JSContext *cx, JSObject *obj, jsid *idp, PropDesc *desc)
|
||||
{
|
||||
JSCompartment *comp = cx->compartment;
|
||||
return comp->wrapId(cx, idp) &&
|
||||
comp->wrap(cx, &desc->value) &&
|
||||
comp->wrap(cx, &desc->get) &&
|
||||
comp->wrap(cx, &desc->set) &&
|
||||
(!obj->isProxy() || desc->makeObject(cx));
|
||||
}
|
||||
|
||||
static JSBool
|
||||
DebuggerObject_defineProperty(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
@ -2938,55 +2967,65 @@ DebuggerObject_defineProperty(JSContext *cx, uintN argc, Value *vp)
|
||||
if (!ValueToId(cx, argc >= 1 ? vp[2] : UndefinedValue(), &id))
|
||||
return JS_FALSE;
|
||||
|
||||
const Value &descval = argc >= 2 ? vp[3] : UndefinedValue();
|
||||
const Value &descval = argc >= 1 ? vp[3] : UndefinedValue();
|
||||
AutoPropDescArrayRooter descs(cx);
|
||||
PropDesc *desc = descs.append();
|
||||
if (!desc || !desc->initialize(cx, descval, false))
|
||||
return false;
|
||||
|
||||
desc->pd.setUndefined();
|
||||
if ((desc->hasValue && (!dbg->unwrapDebuggeeValue(cx, &desc->value) ||
|
||||
!CheckArgCompartment(cx, obj, desc->value, "defineProperty",
|
||||
"value"))) ||
|
||||
(desc->hasGet && (!dbg->unwrapDebuggeeValue(cx, &desc->get) ||
|
||||
!CheckArgCompartment(cx, obj, desc->get, "defineProperty", "get") ||
|
||||
!desc->checkGetter(cx))) ||
|
||||
(desc->hasSet && (!dbg->unwrapDebuggeeValue(cx, &desc->set) ||
|
||||
!CheckArgCompartment(cx, obj, desc->set, "defineProperty", "set") ||
|
||||
!desc->checkSetter(cx))))
|
||||
{
|
||||
if (!UnwrapPropDesc(cx, dbg, obj, desc))
|
||||
return false;
|
||||
|
||||
{
|
||||
AutoCompartment ac(cx, obj);
|
||||
if (!ac.enter() || !WrapIdAndPropDesc(cx, obj, &id, desc))
|
||||
return false;
|
||||
|
||||
ErrorCopier ec(ac, dbg->toJSObject());
|
||||
bool dummy;
|
||||
if (!DefineProperty(cx, obj, id, *desc, true, &dummy))
|
||||
return false;
|
||||
}
|
||||
|
||||
vp->setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
DebuggerObject_defineProperties(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
THIS_DEBUGOBJECT_OWNER_REFERENT(cx, vp, "get script", dbg, obj);
|
||||
REQUIRE_ARGC("Debugger.Object.defineProperties", 1);
|
||||
JSObject *props = ToObject(cx, &vp[2]);
|
||||
if (!props)
|
||||
return false;
|
||||
|
||||
AutoIdVector ids(cx);
|
||||
AutoPropDescArrayRooter descs(cx);
|
||||
if (!ReadPropertyDescriptors(cx, props, false, &ids, &descs))
|
||||
return false;
|
||||
size_t n = ids.length();
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
if (!UnwrapPropDesc(cx, dbg, obj, &descs[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
AutoCompartment ac(cx, obj);
|
||||
if (!ac.enter() ||
|
||||
!ac.destination->wrapId(cx, &id) ||
|
||||
!ac.destination->wrap(cx, &desc->value) ||
|
||||
!ac.destination->wrap(cx, &desc->get) ||
|
||||
!ac.destination->wrap(cx, &desc->set))
|
||||
{
|
||||
if (!ac.enter())
|
||||
return false;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
if (!WrapIdAndPropDesc(cx, obj, &ids[i], &descs[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Defining a property on a proxy requiers the pd field to contain
|
||||
// a descriptor object. Reconstitute it.
|
||||
if (obj->isProxy() && !desc->makeObject(cx))
|
||||
return false;
|
||||
|
||||
bool ignored;
|
||||
if (!DefineProperty(cx, obj, id, *desc, true, &ignored)) {
|
||||
if (cx->isExceptionPending()) {
|
||||
Value exc = cx->getPendingException();
|
||||
if (exc.isObject() && exc.toObject().isError()) {
|
||||
cx->clearPendingException();
|
||||
ac.leave();
|
||||
JSObject *copyobj = js_CopyErrorObject(cx, &exc.toObject(), dbg->toJSObject());
|
||||
if (copyobj)
|
||||
cx->setPendingException(ObjectValue(*copyobj));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
ErrorCopier ec(ac, dbg->toJSObject());
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
bool dummy;
|
||||
if (!DefineProperty(cx, obj, ids[i], descs[i], true, &dummy))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2994,6 +3033,7 @@ DebuggerObject_defineProperty(JSContext *cx, uintN argc, Value *vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
enum ApplyOrCallMode { ApplyMode, CallMode };
|
||||
|
||||
static JSBool
|
||||
@ -3085,6 +3125,7 @@ static JSFunctionSpec DebuggerObject_methods[] = {
|
||||
JS_FN("getOwnPropertyDescriptor", DebuggerObject_getOwnPropertyDescriptor, 1, 0),
|
||||
JS_FN("getOwnPropertyNames", DebuggerObject_getOwnPropertyNames, 0, 0),
|
||||
JS_FN("defineProperty", DebuggerObject_defineProperty, 2, 0),
|
||||
JS_FN("defineProperties", DebuggerObject_defineProperties, 1, 0),
|
||||
JS_FN("apply", DebuggerObject_apply, 0, 0),
|
||||
JS_FN("call", DebuggerObject_call, 0, 0),
|
||||
JS_FS_END
|
||||
|
@ -2499,30 +2499,42 @@ obj_defineProperty(JSContext* cx, uintN argc, Value* vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
bool
|
||||
ReadPropertyDescriptors(JSContext *cx, JSObject *props, bool checkAccessors,
|
||||
AutoIdVector *ids, AutoPropDescArrayRooter *descs)
|
||||
{
|
||||
if (!GetPropertyNames(cx, props, JSITER_OWNONLY, ids))
|
||||
return false;
|
||||
|
||||
for (size_t i = 0, len = ids->length(); i < len; i++) {
|
||||
jsid id = (*ids)[i];
|
||||
PropDesc* desc = descs->append();
|
||||
Value v;
|
||||
if (!desc || !props->getProperty(cx, id, &v) || !desc->initialize(cx, v, checkAccessors))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
static bool
|
||||
DefineProperties(JSContext *cx, JSObject *obj, JSObject *props)
|
||||
{
|
||||
AutoIdVector ids(cx);
|
||||
if (!GetPropertyNames(cx, props, JSITER_OWNONLY, &ids))
|
||||
AutoPropDescArrayRooter descs(cx);
|
||||
if (!ReadPropertyDescriptors(cx, props, true, &ids, &descs))
|
||||
return false;
|
||||
|
||||
AutoPropDescArrayRooter descs(cx);
|
||||
size_t len = ids.length();
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
jsid id = ids[i];
|
||||
PropDesc* desc = descs.append();
|
||||
Value v;
|
||||
if (!desc || !props->getProperty(cx, id, &v) || !desc->initialize(cx, v))
|
||||
return false;
|
||||
}
|
||||
bool dummy;
|
||||
for (size_t i = 0, len = ids.length(); i < len; i++) {
|
||||
if (!DefineProperty(cx, obj, ids[i], descs[i], true, &dummy))
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dummy;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (!DefineProperty(cx, obj, ids[i], descs[i], true, &dummy))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
extern JSBool
|
||||
|
@ -1646,6 +1646,14 @@ extern bool
|
||||
DefineProperty(JSContext *cx, JSObject *obj, const jsid &id, const PropDesc &desc, bool throwError,
|
||||
bool *rval);
|
||||
|
||||
/*
|
||||
* Read property descriptors from props, as for Object.defineProperties. See
|
||||
* ES5 15.2.3.7 steps 3-5.
|
||||
*/
|
||||
extern bool
|
||||
ReadPropertyDescriptors(JSContext *cx, JSObject *props, bool checkAccessors,
|
||||
AutoIdVector *ids, AutoPropDescArrayRooter *descs);
|
||||
|
||||
/*
|
||||
* Constant to pass to js_LookupPropertyWithFlags to infer bits from current
|
||||
* bytecode.
|
||||
|
@ -41,6 +41,7 @@
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jscntxt.h"
|
||||
#include "jsexn.h"
|
||||
#include "jsgc.h"
|
||||
#include "jsgcmark.h"
|
||||
#include "jsiter.h"
|
||||
@ -475,6 +476,24 @@ AutoCompartment::leave()
|
||||
entered = false;
|
||||
}
|
||||
|
||||
ErrorCopier::~ErrorCopier()
|
||||
{
|
||||
JSContext *cx = ac.context;
|
||||
if (cx->compartment == ac.destination &&
|
||||
ac.origin != ac.destination &&
|
||||
cx->isExceptionPending())
|
||||
{
|
||||
Value exc = cx->getPendingException();
|
||||
if (exc.isObject() && exc.toObject().isError()) {
|
||||
cx->clearPendingException();
|
||||
ac.leave();
|
||||
JSObject *copyobj = js_CopyErrorObject(cx, &exc.toObject(), scope);
|
||||
if (copyobj)
|
||||
cx->setPendingException(ObjectValue(*copyobj));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Cross compartment wrappers. */
|
||||
|
||||
JSCrossCompartmentWrapper::JSCrossCompartmentWrapper(uintN flags)
|
||||
|
@ -200,6 +200,22 @@ class AutoCompartment
|
||||
AutoCompartment & operator=(const AutoCompartment &);
|
||||
};
|
||||
|
||||
/*
|
||||
* Use this to change the behavior of an AutoCompartment slightly on error. If
|
||||
* the exception happens to be an Error object, copy it to the origin compartment
|
||||
* instead of wrapping it.
|
||||
*/
|
||||
class ErrorCopier {
|
||||
AutoCompartment ∾
|
||||
JSObject *scope;
|
||||
|
||||
public:
|
||||
ErrorCopier(AutoCompartment &ac, JSObject *scope) : ac(ac), scope(scope) {
|
||||
JS_ASSERT(scope->compartment() == ac.origin);
|
||||
}
|
||||
~ErrorCopier();
|
||||
};
|
||||
|
||||
extern JSObject *
|
||||
TransparentObjectWrapper(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSObject *parent,
|
||||
uintN flags);
|
||||
|
Loading…
Reference in New Issue
Block a user