Files
palemoon27/js/src/proxy/ScriptedIndirectProxyHandler.cpp
T
roytam1 a013252167 import changes from `dev' branch of rmottola/Arctic-Fox:
- Ensure input events account for APZ transforms on Windows. (bug 1143567 part 4, r=jmathies) (e9df7b3cb)
- Fix mouse event handling regression from bug 1143567. (bug 1142866, r=jmathies) (91b195614)
- Fix regression from bug 1143567 that broke context menus. (bug 1144827, r=jmathies, a=kwierso) (c852a6554)
- fix typo in applying bug 1143567 part 3 (b246c45a8)
- fix typo in applying bug 1143567 part 3 (6f1e6084f)
- Bug 1130089 - Use constexpr for JitStackValueAlignment. r=bbouvier (59793733b)
- pointer style (b3a4de187)
- Bug 1121613 - Move MacroAssemblerSpecific::Push to the generic MacroAssembler. r=jandem (54d0c7e82)
- Bug 1151382 - Fix Alignment mismatch for none jit. r=luke (05cce8627)
- fix typo (66dd065dd)
- pointer style (2fe56af71)
- remove MOZ_HAVE_CXX11_OVERRIDE and MOZ_HAVE_CXX11_FINAL - may break some older compilers, let's hope not anything currently used (a883df8d7)
- Bug 1135428 - OdinMonkey: remove compileAndGo restriction (r=bbouvier) Bug 1135428 - OdinMonkey: return nullptr, not false (r=warnings-as-errors CLOSED TREE) (378f1047e)
- Bug 1135428: Fix test to run with --no-asmjs as well (76b45cc55)
- pointer style (e85078674)
- Bug 1142784, part 1 - In NativeObject.cpp, move some Lookup functions out of the way and make a big swath of this file the [[DefineOwnProperty]] section. r=Waldo. (5452ceb3a)
- Bug 1142784, part 2 - Change ProxyHandler::defineProperty() to make the desc argument a non-mutable handle. r=Waldo. (26afaeb1e)
- Bug 1142784, part 3 - Change js::DefinePropertyOp and a few property-defining functions to use PropertyDescriptor rather than separate (value, attrs, getter, setter) arguments. r=Waldo. (0cf9f0f6d)
- more pointer style.... soon to be changed again, boring (88956061f)
- Bug 1146165 - Stop calling Proxy::set directly from Ion IC stub. EmitObjectOpResultCheck is retained because GenerateCallSetter still uses it in the JSSetterOp case. r=efaust. (28d368865)
- Bug 1138157 - Change ScriptedDirectProxyHandler to inherit from BaseProxyHandler. r=efaust (1ecb895cc)
- missing bit of Bug 1113369, part 4 - [[Set]] ObjectOpResult support. (ac03ea66d)
- pointer style (b966f0173)
- pointer style (883527058)
2020-05-30 12:48:57 +08:00

549 lines
19 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "proxy/ScriptedIndirectProxyHandler.h"
#include "jsapi.h"
#include "jscntxt.h"
#include "jscntxtinlines.h"
#include "jsobjinlines.h"
using namespace js;
static bool
GetFundamentalTrap(JSContext* cx, HandleObject handler, HandlePropertyName name,
MutableHandleValue fvalp)
{
JS_CHECK_RECURSION(cx, return false);
return GetProperty(cx, handler, handler, name, fvalp);
}
static bool
GetDerivedTrap(JSContext* cx, HandleObject handler, HandlePropertyName name,
MutableHandleValue fvalp)
{
MOZ_ASSERT(name == cx->names().has ||
name == cx->names().hasOwn ||
name == cx->names().get ||
name == cx->names().set ||
name == cx->names().keys ||
name == cx->names().iterate);
return GetProperty(cx, handler, handler, name, fvalp);
}
static bool
Trap(JSContext* cx, HandleObject handler, HandleValue fval, unsigned argc, Value* argv,
MutableHandleValue rval)
{
return Invoke(cx, ObjectValue(*handler), fval, argc, argv, rval);
}
static bool
Trap1(JSContext* cx, HandleObject handler, HandleValue fval, HandleId id, MutableHandleValue rval)
{
if (!IdToStringOrSymbol(cx, id, rval))
return false;
return Trap(cx, handler, fval, 1, rval.address(), rval);
}
static bool
Trap2(JSContext* cx, HandleObject handler, HandleValue fval, HandleId id, Value v_,
MutableHandleValue rval)
{
RootedValue v(cx, v_);
if (!IdToStringOrSymbol(cx, id, rval))
return false;
JS::AutoValueArray<2> argv(cx);
argv[0].set(rval);
argv[1].set(v);
return Trap(cx, handler, fval, 2, argv.begin(), rval);
}
static bool
IndicatePropertyNotFound(MutableHandle<PropertyDescriptor> desc)
{
desc.object().set(nullptr);
return true;
}
static bool
ValueToBool(HandleValue v, bool* bp)
{
*bp = ToBoolean(v);
return true;
}
static bool
ArrayToIdVector(JSContext* cx, const Value& array, AutoIdVector& props)
{
MOZ_ASSERT(props.length() == 0);
if (array.isPrimitive())
return true;
RootedObject obj(cx, &array.toObject());
uint32_t length;
if (!GetLengthProperty(cx, obj, &length))
return false;
RootedValue v(cx);
for (uint32_t n = 0; n < length; ++n) {
if (!CheckForInterrupt(cx))
return false;
if (!GetElement(cx, obj, obj, n, &v))
return false;
RootedId id(cx);
if (!ValueToId<CanGC>(cx, v, &id))
return false;
if (!props.append(id))
return false;
}
return true;
}
namespace {
/*
* Old-style indirect proxies allow callers to specify distinct scripted
* [[Call]] and [[Construct]] traps. We use an intermediate object so that we
* can stash this information in a single reserved slot on the proxy object.
*
* Note - Currently this is slightly unnecesary, because we actually have 2
* extra slots, neither of which are used for ScriptedIndirectProxy. But we're
* eventually moving towards eliminating one of those slots, and so we don't
* want to add a dependency here.
*/
static const Class CallConstructHolder = {
"CallConstructHolder",
JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS
};
} /* anonymous namespace */
// This variable exists solely to provide a unique address for use as an identifier.
const char ScriptedIndirectProxyHandler::family = 0;
bool
ScriptedIndirectProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy,
ObjectOpResult &result) const
{
// Scripted indirect proxies don't support extensibility changes.
return result.fail(JSMSG_CANT_CHANGE_EXTENSIBILITY);
}
bool
ScriptedIndirectProxyHandler::isExtensible(JSContext *cx, HandleObject proxy,
bool *extensible) const
{
// Scripted indirect proxies don't support extensibility changes.
*extensible = true;
return true;
}
static bool
ReturnedValueMustNotBePrimitive(JSContext* cx, HandleObject proxy, JSAtom* atom, const Value& v)
{
if (v.isPrimitive()) {
JSAutoByteString bytes;
if (AtomToPrintableString(cx, atom, &bytes)) {
RootedValue val(cx, ObjectOrNullValue(proxy));
ReportValueError2(cx, JSMSG_BAD_TRAP_RETURN_VALUE,
JSDVG_SEARCH_STACK, val, js::NullPtr(), bytes.ptr());
}
return false;
}
return true;
}
static JSObject*
GetIndirectProxyHandlerObject(JSObject* proxy)
{
return proxy->as<ProxyObject>().private_().toObjectOrNull();
}
bool
ScriptedIndirectProxyHandler::getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
MutableHandle<PropertyDescriptor> desc) const
{
RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
RootedValue fval(cx), value(cx);
return GetFundamentalTrap(cx, handler, cx->names().getPropertyDescriptor, &fval) &&
Trap1(cx, handler, fval, id, &value) &&
((value.isUndefined() && IndicatePropertyNotFound(desc)) ||
(ReturnedValueMustNotBePrimitive(cx, proxy, cx->names().getPropertyDescriptor, value) &&
ObjectToCompletePropertyDescriptor(cx, proxy, value, desc)));
}
bool
ScriptedIndirectProxyHandler::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
MutableHandle<PropertyDescriptor> desc) const
{
RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
RootedValue fval(cx), value(cx);
return GetFundamentalTrap(cx, handler, cx->names().getOwnPropertyDescriptor, &fval) &&
Trap1(cx, handler, fval, id, &value) &&
((value.isUndefined() && IndicatePropertyNotFound(desc)) ||
(ReturnedValueMustNotBePrimitive(cx, proxy, cx->names().getPropertyDescriptor, value) &&
ObjectToCompletePropertyDescriptor(cx, proxy, value, desc)));
}
bool
ScriptedIndirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
Handle<PropertyDescriptor> desc,
ObjectOpResult &result) const
{
RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
RootedValue fval(cx), value(cx);
return GetFundamentalTrap(cx, handler, cx->names().defineProperty, &fval) &&
FromPropertyDescriptor(cx, desc, &value) &&
Trap2(cx, handler, fval, id, value, &value) &&
result.succeed();
}
bool
ScriptedIndirectProxyHandler::ownPropertyKeys(JSContext *cx, HandleObject proxy,
AutoIdVector &props) const
{
RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
RootedValue fval(cx), value(cx);
return GetFundamentalTrap(cx, handler, cx->names().getOwnPropertyNames, &fval) &&
Trap(cx, handler, fval, 0, nullptr, &value) &&
ArrayToIdVector(cx, value, props);
}
bool
ScriptedIndirectProxyHandler::delete_(JSContext *cx, HandleObject proxy, HandleId id,
ObjectOpResult &result) const
{
RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
RootedValue fval(cx), value(cx);
if (!GetFundamentalTrap(cx, handler, cx->names().delete_, &fval))
return false;
if (!Trap1(cx, handler, fval, id, &value))
return false;
if (ToBoolean(value))
result.succeed();
else
result.fail(JSMSG_PROXY_DELETE_RETURNED_FALSE);
return true;
}
bool
ScriptedIndirectProxyHandler::enumerate(JSContext* cx, HandleObject proxy,
MutableHandleObject objp) const
{
// The hook that is called "enumerate" in the spec, used to be "iterate"
RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
RootedValue value(cx);
if (!GetDerivedTrap(cx, handler, cx->names().iterate, &value))
return false;
if (!IsCallable(value))
return BaseProxyHandler::enumerate(cx, proxy, objp);
RootedValue rval(cx);
if (!Trap(cx, handler, value, 0, nullptr, &rval))
return false;
if (!ReturnedValueMustNotBePrimitive(cx, proxy, cx->names().iterate, rval))
return false;
objp.set(&rval.toObject());
return true;
}
bool
ScriptedIndirectProxyHandler::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const
{
RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
RootedValue fval(cx), value(cx);
if (!GetDerivedTrap(cx, handler, cx->names().has, &fval))
return false;
if (!IsCallable(fval))
return BaseProxyHandler::has(cx, proxy, id, bp);
return Trap1(cx, handler, fval, id, &value) &&
ValueToBool(value, bp);
}
bool
ScriptedIndirectProxyHandler::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const
{
RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
RootedValue fval(cx), value(cx);
if (!GetDerivedTrap(cx, handler, cx->names().hasOwn, &fval))
return false;
if (!IsCallable(fval))
return BaseProxyHandler::hasOwn(cx, proxy, id, bp);
return Trap1(cx, handler, fval, id, &value) &&
ValueToBool(value, bp);
}
bool
ScriptedIndirectProxyHandler::get(JSContext* cx, HandleObject proxy, HandleObject receiver,
HandleId id, MutableHandleValue vp) const
{
RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
RootedValue idv(cx);
if (!IdToStringOrSymbol(cx, id, &idv))
return false;
JS::AutoValueArray<2> argv(cx);
argv[0].setObjectOrNull(receiver);
argv[1].set(idv);
RootedValue fval(cx);
if (!GetDerivedTrap(cx, handler, cx->names().get, &fval))
return false;
if (!IsCallable(fval))
return BaseProxyHandler::get(cx, proxy, receiver, id, vp);
return Trap(cx, handler, fval, 2, argv.begin(), vp);
}
bool
ScriptedIndirectProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver,
HandleId id, MutableHandleValue vp, ObjectOpResult &result) const
{
RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
RootedValue idv(cx);
if (!IdToStringOrSymbol(cx, id, &idv))
return false;
JS::AutoValueArray<3> argv(cx);
argv[0].setObjectOrNull(receiver);
argv[1].set(idv);
argv[2].set(vp);
RootedValue fval(cx);
if (!GetDerivedTrap(cx, handler, cx->names().set, &fval))
return false;
if (!IsCallable(fval))
return derivedSet(cx, proxy, receiver, id, vp, result);
if (!Trap(cx, handler, fval, 3, argv.begin(), &idv))
return false;
return result.succeed();
}
static bool
CallSetter(JSContext *cx, HandleObject obj, HandleId id, SetterOp op, unsigned attrs,
MutableHandleValue vp, ObjectOpResult &result)
{
if (attrs & JSPROP_SETTER) {
RootedValue opv(cx, CastAsObjectJsval(op));
if (!InvokeGetterOrSetter(cx, obj, opv, 1, vp.address(), vp))
return false;
return result.succeed();
}
if (attrs & JSPROP_GETTER)
return result.fail(JSMSG_GETTER_ONLY);
if (!op)
return result.succeed();
return CallJSSetterOp(cx, op, obj, id, vp, result);
}
bool
ScriptedIndirectProxyHandler::derivedSet(JSContext* cx, HandleObject proxy, HandleObject receiver,
HandleId id, MutableHandleValue vp,
ObjectOpResult &result) const
{
// Find an own or inherited property. The code here is strange for maximum
// backward compatibility with earlier code written before ES6 and before
// SetPropertyIgnoringNamedGetter.
//
// As of March 2015, testing/specialpowers/content/specialpowersAPI.js
// depends on the call to getPropertyDescriptor below, because it does
// support inherited setters but makes no attempt to provide a meaningful
// prototype chain.
Rooted<PropertyDescriptor> desc(cx);
if (!getOwnPropertyDescriptor(cx, proxy, id, &desc))
return false;
bool descIsOwn = desc.object() != nullptr;
if (!descIsOwn) {
if (!getPropertyDescriptor(cx, proxy, id, &desc))
return false;
}
MOZ_ASSERT_IF(descIsOwn, desc.object());
if (desc.object()) {
MOZ_ASSERT(desc.getter() != JS_PropertyStub);
MOZ_ASSERT(desc.setter() != JS_StrictPropertyStub);
// Check for read-only properties.
if (desc.isDataDescriptor() && !desc.writable())
return result.fail(descIsOwn ? JSMSG_READ_ONLY : JSMSG_CANT_REDEFINE_PROP);
if (desc.hasSetterObject() || desc.setter()) {
if (!CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), vp, result))
return false;
if (!result)
return true;
if (!proxy->is<ProxyObject>() ||
proxy->as<ProxyObject>().handler() != this ||
desc.isShared())
{
return result.succeed();
}
}
desc.value().set(vp.get());
if (descIsOwn) {
MOZ_ASSERT(desc.object() == proxy);
return this->defineProperty(cx, proxy, id, desc, result);
}
return DefineProperty(cx, receiver, id, desc.value(), desc.getter(), desc.setter(),
desc.attributes(), result);
}
desc.object().set(receiver);
desc.value().set(vp.get());
desc.setAttributes(JSPROP_ENUMERATE);
desc.setGetter(nullptr);
desc.setSetter(nullptr); // Pick up the class getter/setter.
return DefineProperty(cx, receiver, id, desc.value(), nullptr, nullptr, JSPROP_ENUMERATE,
result);
}
bool
ScriptedIndirectProxyHandler::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy,
AutoIdVector& props) const
{
RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
RootedValue value(cx);
if (!GetDerivedTrap(cx, handler, cx->names().keys, &value))
return false;
if (!IsCallable(value))
return BaseProxyHandler::getOwnEnumerablePropertyKeys(cx, proxy, props);
return Trap(cx, handler, value, 0, nullptr, &value) &&
ArrayToIdVector(cx, value, props);
}
bool
ScriptedIndirectProxyHandler::nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
const CallArgs& args) const
{
return BaseProxyHandler::nativeCall(cx, test, impl, args);
}
JSString*
ScriptedIndirectProxyHandler::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const
{
assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
if (!proxy->isCallable()) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
JSMSG_INCOMPATIBLE_PROTO,
js_Function_str, js_toString_str,
"object");
return nullptr;
}
RootedObject obj(cx, &proxy->as<ProxyObject>().extra(0).toObject().as<NativeObject>().getReservedSlot(0).toObject());
return fun_toStringHelper(cx, obj, indent);
}
const ScriptedIndirectProxyHandler ScriptedIndirectProxyHandler::singleton;
bool
CallableScriptedIndirectProxyHandler::call(JSContext* cx, HandleObject proxy, const CallArgs& args) const
{
assertEnteredPolicy(cx, proxy, JSID_VOID, CALL);
RootedObject ccHolder(cx, &proxy->as<ProxyObject>().extra(0).toObject());
MOZ_ASSERT(ccHolder->getClass() == &CallConstructHolder);
RootedValue call(cx, ccHolder->as<NativeObject>().getReservedSlot(0));
MOZ_ASSERT(call.isObject() && call.toObject().isCallable());
return Invoke(cx, args.thisv(), call, args.length(), args.array(), args.rval());
}
bool
CallableScriptedIndirectProxyHandler::construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const
{
assertEnteredPolicy(cx, proxy, JSID_VOID, CALL);
RootedObject ccHolder(cx, &proxy->as<ProxyObject>().extra(0).toObject());
MOZ_ASSERT(ccHolder->getClass() == &CallConstructHolder);
RootedValue construct(cx, ccHolder->as<NativeObject>().getReservedSlot(1));
MOZ_ASSERT(construct.isObject() && construct.toObject().isCallable());
return InvokeConstructor(cx, construct, args.length(), args.array(), args.rval());
}
const CallableScriptedIndirectProxyHandler CallableScriptedIndirectProxyHandler::singleton;
bool
js::proxy_create(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() < 1) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
"create", "0", "s");
return false;
}
JSObject* handler = NonNullObject(cx, args[0]);
if (!handler)
return false;
JSObject* proto;
if (args.get(1).isObject()) {
proto = &args[1].toObject();
} else {
MOZ_ASSERT(IsFunctionObject(&args.callee()));
proto = nullptr;
}
RootedValue priv(cx, ObjectValue(*handler));
JSObject* proxy = NewProxyObject(cx, &ScriptedIndirectProxyHandler::singleton,
priv, proto);
if (!proxy)
return false;
args.rval().setObject(*proxy);
return true;
}
bool
js::proxy_createFunction(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() < 2) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
"createFunction", "1", "");
return false;
}
RootedObject handler(cx, NonNullObject(cx, args[0]));
if (!handler)
return false;
RootedObject proto(cx, args.callee().global().getOrCreateFunctionPrototype(cx));
if (!proto)
return false;
RootedObject call(cx, ValueToCallable(cx, args[1], args.length() - 2));
if (!call)
return false;
RootedObject construct(cx, nullptr);
if (args.length() > 2) {
construct = ValueToCallable(cx, args[2], args.length() - 3);
if (!construct)
return false;
} else {
construct = call;
}
// Stash the call and construct traps on a holder object that we can stick
// in a slot on the proxy.
RootedObject ccHolder(cx, JS_NewObjectWithGivenProto(cx, Jsvalify(&CallConstructHolder),
js::NullPtr()));
if (!ccHolder)
return false;
ccHolder->as<NativeObject>().setReservedSlot(0, ObjectValue(*call));
ccHolder->as<NativeObject>().setReservedSlot(1, ObjectValue(*construct));
RootedValue priv(cx, ObjectValue(*handler));
JSObject* proxy =
NewProxyObject(cx, &CallableScriptedIndirectProxyHandler::singleton,
priv, proto);
if (!proxy)
return false;
proxy->as<ProxyObject>().setExtra(0, ObjectValue(*ccHolder));
args.rval().setObject(*proxy);
return true;
}