Bug 1017109 - Add construct hook for CPOWs (r=mrbkap)

This commit is contained in:
Bill McCloskey 2014-06-30 17:50:58 -07:00
parent 3d9e496beb
commit 026f5d0e68
12 changed files with 68 additions and 26 deletions

View File

@ -45,6 +45,7 @@ function make_object()
o.s = "hello";
o.x = { i: 10 };
o.f = function () { return 99; };
o.ctor = function() { this.a = 3; }
// Doing anything with this Proxy will throw.
var throwing = new Proxy({}, new Proxy({}, {

View File

@ -48,6 +48,8 @@
ok(data.s === "hello", "string property");
ok(data.x.i === 10, "nested property");
ok(data.f() === 99, "function call");
let obj = new data.ctor();
ok(obj.a === 3, "constructor call");
ok(document.title === "Hello, Kitty", "document node");
data.i = 6;

View File

@ -82,10 +82,10 @@ class JavaScriptBase : public WrapperOwner, public WrapperAnswer, public Base
bool *result) {
return Answer::AnswerIsExtensible(objId, rs, result);
}
bool AnswerCall(const ObjectId &objId, const nsTArray<JSParam> &argv,
ReturnStatus *rs, JSVariant *result,
nsTArray<JSParam> *outparams) {
return Answer::AnswerCall(objId, argv, rs, result, outparams);
bool AnswerCallOrConstruct(const ObjectId &objId, const nsTArray<JSParam> &argv,
const bool &construct, ReturnStatus *rs, JSVariant *result,
nsTArray<JSParam> *outparams) {
return Answer::AnswerCallOrConstruct(objId, argv, construct, rs, result, outparams);
}
bool AnswerObjectClassIs(const ObjectId &objId, const uint32_t &classValue,
bool *result) {
@ -164,10 +164,10 @@ class JavaScriptBase : public WrapperOwner, public WrapperAnswer, public Base
bool *result) {
return Base::CallIsExtensible(objId, rs, result);
}
bool CallCall(const ObjectId &objId, const nsTArray<JSParam> &argv,
ReturnStatus *rs, JSVariant *result,
nsTArray<JSParam> *outparams) {
return Base::CallCall(objId, argv, rs, result, outparams);
bool CallCallOrConstruct(const ObjectId &objId, const nsTArray<JSParam> &argv,
const bool &construct, ReturnStatus *rs, JSVariant *result,
nsTArray<JSParam> *outparams) {
return Base::CallCallOrConstruct(objId, argv, construct, rs, result, outparams);
}
bool CallObjectClassIs(const ObjectId &objId, const uint32_t &classValue,
bool *result) {

View File

@ -36,7 +36,7 @@ both:
rpc Set(uint64_t objId, ObjectVariant receiver, nsString id, bool strict, JSVariant value) returns (ReturnStatus rs, JSVariant result);
rpc IsExtensible(uint64_t objId) returns (ReturnStatus rs, bool result);
rpc Call(uint64_t objId, JSParam[] argv) returns (ReturnStatus rs, JSVariant result, JSParam[] outparams);
rpc CallOrConstruct(uint64_t objId, JSParam[] argv, bool construct) returns (ReturnStatus rs, JSVariant result, JSParam[] outparams);
rpc ObjectClassIs(uint64_t objId, uint32_t classValue) returns (bool result);
rpc ClassName(uint64_t objId) returns (nsString name);

View File

@ -381,8 +381,12 @@ WrapperAnswer::AnswerIsExtensible(const ObjectId &objId, ReturnStatus *rs, bool
}
bool
WrapperAnswer::AnswerCall(const ObjectId &objId, const nsTArray<JSParam> &argv, ReturnStatus *rs,
JSVariant *result, nsTArray<JSParam> *outparams)
WrapperAnswer::AnswerCallOrConstruct(const ObjectId &objId,
const nsTArray<JSParam> &argv,
const bool &construct,
ReturnStatus *rs,
JSVariant *result,
nsTArray<JSParam> *outparams)
{
AutoSafeJSContext cx;
JSAutoRequest request(cx);
@ -434,7 +438,11 @@ WrapperAnswer::AnswerCall(const ObjectId &objId, const nsTArray<JSParam> &argv,
ContextOptionsRef(cx).setDontReportUncaught(true);
HandleValueArray args = HandleValueArray::subarray(vals, 2, vals.length() - 2);
bool success = JS::Call(cx, vals[1], vals[0], args, &rval);
bool success;
if (construct)
success = JS::Construct(cx, vals[0], args, &rval);
else
success = JS::Call(cx, vals[1], vals[0], args, &rval);
if (!success)
return fail(cx, rs);
}

View File

@ -45,9 +45,9 @@ class WrapperAnswer : public virtual JavaScriptShared
bool AnswerIsExtensible(const ObjectId &objId, ReturnStatus *rs,
bool *result);
bool AnswerCall(const ObjectId &objId, const nsTArray<JSParam> &argv,
ReturnStatus *rs, JSVariant *result,
nsTArray<JSParam> *outparams);
bool AnswerCallOrConstruct(const ObjectId &objId, const nsTArray<JSParam> &argv,
const bool &construct, ReturnStatus *rs, JSVariant *result,
nsTArray<JSParam> *outparams);
bool AnswerObjectClassIs(const ObjectId &objId, const uint32_t &classValue,
bool *result);
bool AnswerClassName(const ObjectId &objId, nsString *result);

View File

@ -80,6 +80,7 @@ class CPOWProxyHandler : public BaseProxyHandler
virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
virtual bool objectClassIs(HandleObject obj, js::ESClassValue classValue,
JSContext *cx) const MOZ_OVERRIDE;
virtual const char* className(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
@ -336,7 +337,7 @@ WrapperOwner::toString(JSContext *cx, HandleObject cpow, JS::CallArgs &args)
// Ask the other side to call its toString method. Update the callee so that
// it points to the CPOW and not to the synthesized CPOWToString function.
args.setCallee(ObjectValue(*cpow));
if (!call(cx, cpow, args))
if (!callOrConstruct(cx, cpow, args, false))
return false;
if (!args.rval().isString())
@ -482,11 +483,18 @@ WrapperOwner::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible)
bool
CPOWProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args) const
{
FORWARD(call, (cx, proxy, args));
FORWARD(callOrConstruct, (cx, proxy, args, false));
}
bool
WrapperOwner::call(JSContext *cx, HandleObject proxy, const CallArgs &args)
CPOWProxyHandler::construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const
{
FORWARD(callOrConstruct, (cx, proxy, args, true));
}
bool
WrapperOwner::callOrConstruct(JSContext *cx, HandleObject proxy, const CallArgs &args,
bool construct)
{
ObjectId objId = idOf(proxy);
@ -495,7 +503,12 @@ WrapperOwner::call(JSContext *cx, HandleObject proxy, const CallArgs &args)
RootedValue v(cx);
for (size_t i = 0; i < args.length() + 2; i++) {
v = args.base()[i];
// The |this| value for constructors is a magic value that we won't be
// able to convert, so skip it.
if (i == 1 && construct)
v = UndefinedValue();
else
v = args.base()[i];
if (v.isObject()) {
RootedObject obj(cx, &v.toObject());
if (xpc::IsOutObject(cx, obj)) {
@ -523,7 +536,7 @@ WrapperOwner::call(JSContext *cx, HandleObject proxy, const CallArgs &args)
JSVariant result;
ReturnStatus status;
InfallibleTArray<JSParam> outparams;
if (!CallCall(objId, vals, &status, &result, &outparams))
if (!CallCallOrConstruct(objId, vals, construct, &status, &result, &outparams))
return ipcfail(cx);
LOG_STACK();

View File

@ -55,7 +55,8 @@ class WrapperOwner : public virtual JavaScriptShared
// SpiderMonkey Extensions.
bool isExtensible(JSContext *cx, JS::HandleObject proxy, bool *extensible);
bool call(JSContext *cx, JS::HandleObject proxy, const JS::CallArgs &args);
bool callOrConstruct(JSContext *cx, JS::HandleObject proxy, const JS::CallArgs &args,
bool construct);
bool objectClassIs(JSContext *cx, JS::HandleObject obj, js::ESClassValue classValue);
const char* className(JSContext *cx, JS::HandleObject proxy);
@ -125,9 +126,9 @@ class WrapperOwner : public virtual JavaScriptShared
virtual bool CallIsExtensible(const ObjectId &objId, ReturnStatus *rs,
bool *result) = 0;
virtual bool CallCall(const ObjectId &objId, const nsTArray<JSParam> &argv,
ReturnStatus *rs, JSVariant *result,
nsTArray<JSParam> *outparams) = 0;
virtual bool CallCallOrConstruct(const ObjectId &objId, const nsTArray<JSParam> &argv,
const bool &construct, ReturnStatus *rs, JSVariant *result,
nsTArray<JSParam> *outparams) = 0;
virtual bool CallObjectClassIs(const ObjectId &objId, const uint32_t &classValue,
bool *result) = 0;
virtual bool CallClassName(const ObjectId &objId, nsString *result) = 0;

View File

@ -5126,6 +5126,18 @@ JS::Call(JSContext *cx, HandleValue thisv, HandleValue fval, const JS::HandleVal
return Invoke(cx, thisv, fval, args.length(), args.begin(), rval);
}
JS_PUBLIC_API(bool)
JS::Construct(JSContext *cx, HandleValue fval, const JS::HandleValueArray& args,
MutableHandleValue rval)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, fval, args);
AutoLastFrameCheck lfc(cx);
return InvokeConstructor(cx, fval, args.length(), args.begin(), rval.address());
}
JS_PUBLIC_API(JSObject *)
JS_New(JSContext *cx, HandleObject ctor, const JS::HandleValueArray& inputArgs)
{

View File

@ -3996,6 +3996,11 @@ Call(JSContext *cx, JS::HandleValue thisv, JS::HandleObject funObj, const JS::Ha
return Call(cx, thisv, fun, args, rval);
}
extern JS_PUBLIC_API(bool)
Construct(JSContext *cx, JS::HandleValue fun,
const JS::HandleValueArray& args,
MutableHandleValue rval);
} /* namespace JS */
/*

View File

@ -553,7 +553,7 @@ js::InvokeConstructor(JSContext *cx, CallArgs args)
}
bool
js::InvokeConstructor(JSContext *cx, Value fval, unsigned argc, Value *argv, Value *rval)
js::InvokeConstructor(JSContext *cx, Value fval, unsigned argc, const Value *argv, Value *rval)
{
InvokeArgs args(cx);
if (!args.init(argc))

View File

@ -149,7 +149,7 @@ InvokeConstructor(JSContext *cx, CallArgs args);
/* See the fval overload of Invoke. */
extern bool
InvokeConstructor(JSContext *cx, Value fval, unsigned argc, Value *argv, Value *rval);
InvokeConstructor(JSContext *cx, Value fval, unsigned argc, const Value *argv, Value *rval);
/*
* Executes a script with the given scopeChain/this. The 'type' indicates