#include "js/Proxy.h"
#include "vm/ProxyObject.h"
#include "vm/JSContext-inl.h"
#include "vm/JSObject-inl.h"
using namespace js;
using JS::IsArrayAnswer;
bool BaseProxyHandler::enter(JSContext* cx, HandleObject wrapper, HandleId id,
Action act, bool mayThrow, bool* bp) const {
*bp = true;
return true;
}
bool BaseProxyHandler::has(JSContext* cx, HandleObject proxy, HandleId id,
bool* bp) const {
assertEnteredPolicy(cx, proxy, id, GET);
if (!hasOwn(cx, proxy, id, bp)) {
return false;
}
if (*bp) {
return true;
}
RootedObject proto(cx);
if (!GetPrototype(cx, proxy, &proto)) {
return false;
}
if (proto) {
return HasProperty(cx, proto, id, bp);
}
*bp = false;
return true;
}
bool BaseProxyHandler::hasOwn(JSContext* cx, HandleObject proxy, HandleId id,
bool* bp) const {
assertEnteredPolicy(cx, proxy, id, GET);
Rooted<PropertyDescriptor> desc(cx);
if (!getOwnPropertyDescriptor(cx, proxy, id, &desc)) {
return false;
}
*bp = !!desc.object();
return true;
}
bool BaseProxyHandler::get(JSContext* cx, HandleObject proxy,
HandleValue receiver, HandleId id,
MutableHandleValue vp) const {
assertEnteredPolicy(cx, proxy, id, GET);
Rooted<PropertyDescriptor> desc(cx);
if (!getOwnPropertyDescriptor(cx, proxy, id, &desc)) {
return false;
}
desc.assertCompleteIfFound();
if (!desc.object()) {
RootedObject proto(cx);
if (!GetPrototype(cx, proxy, &proto)) {
return false;
}
if (!proto) {
vp.setUndefined();
return true;
}
return GetProperty(cx, proto, receiver, id, vp);
}
if (desc.isDataDescriptor()) {
vp.set(desc.value());
return true;
}
MOZ_ASSERT(desc.isAccessorDescriptor());
RootedObject getter(cx, desc.getterObject());
if (!getter) {
vp.setUndefined();
return true;
}
RootedValue getterFunc(cx, ObjectValue(*getter));
return CallGetter(cx, receiver, getterFunc, vp);
}
bool BaseProxyHandler::set(JSContext* cx, HandleObject proxy, HandleId id,
HandleValue v, HandleValue receiver,
ObjectOpResult& result) const {
assertEnteredPolicy(cx, proxy, id, SET);
Rooted<PropertyDescriptor> ownDesc(cx);
if (!getOwnPropertyDescriptor(cx, proxy, id, &ownDesc)) {
return false;
}
ownDesc.assertCompleteIfFound();
return SetPropertyIgnoringNamedGetter(cx, proxy, id, v, receiver, ownDesc,
result);
}
bool js::SetPropertyIgnoringNamedGetter(JSContext* cx, HandleObject obj,
HandleId id, HandleValue v,
HandleValue receiver,
Handle<PropertyDescriptor> ownDesc_,
ObjectOpResult& result) {
Rooted<PropertyDescriptor> ownDesc(cx, ownDesc_);
if (!ownDesc.object()) {
RootedObject proto(cx);
if (!GetPrototype(cx, obj, &proto)) {
return false;
}
if (proto) {
return SetProperty(cx, proto, id, v, receiver, result);
}
ownDesc.setDataDescriptor(UndefinedHandleValue, JSPROP_ENUMERATE);
}
if (ownDesc.isDataDescriptor()) {
if (!ownDesc.writable()) {
return result.fail(JSMSG_READ_ONLY);
}
if (!receiver.isObject()) {
return result.fail(JSMSG_SET_NON_OBJECT_RECEIVER);
}
RootedObject receiverObj(cx, &receiver.toObject());
if (SetterOp setter = ownDesc.setter()) {
return CallJSSetterOp(cx, setter, receiverObj, id, v, result);
}
Rooted<PropertyDescriptor> existingDescriptor(cx);
if (!GetOwnPropertyDescriptor(cx, receiverObj, id, &existingDescriptor)) {
return false;
}
if (existingDescriptor.object()) {
if (existingDescriptor.isAccessorDescriptor()) {
return result.fail(JSMSG_OVERWRITING_ACCESSOR);
}
if (!existingDescriptor.writable()) {
return result.fail(JSMSG_READ_ONLY);
}
}
unsigned attrs = existingDescriptor.object()
? JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_READONLY |
JSPROP_IGNORE_PERMANENT
: JSPROP_ENUMERATE;
return DefineDataProperty(cx, receiverObj, id, v, attrs, result);
}
MOZ_ASSERT(ownDesc.isAccessorDescriptor());
RootedObject setter(cx);
if (ownDesc.hasSetterObject()) {
setter = ownDesc.setterObject();
}
if (!setter) {
return result.fail(JSMSG_GETTER_ONLY);
}
RootedValue setterValue(cx, ObjectValue(*setter));
if (!CallSetter(cx, receiver, setterValue, v)) {
return false;
}
return result.succeed();
}
bool BaseProxyHandler::getOwnEnumerablePropertyKeys(JSContext* cx,
HandleObject proxy,
AutoIdVector& props) const {
assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
MOZ_ASSERT(props.length() == 0);
if (!ownPropertyKeys(cx, proxy, props)) {
return false;
}
RootedId id(cx);
size_t i = 0;
for (size_t j = 0, len = props.length(); j < len; j++) {
MOZ_ASSERT(i <= j);
id = props[j];
if (JSID_IS_SYMBOL(id)) {
continue;
}
AutoWaivePolicy policy(cx, proxy, id, BaseProxyHandler::GET);
Rooted<PropertyDescriptor> desc(cx);
if (!getOwnPropertyDescriptor(cx, proxy, id, &desc)) {
return false;
}
desc.assertCompleteIfFound();
if (desc.object() && desc.enumerable()) {
props[i++].set(id);
}
}
MOZ_ASSERT(i <= props.length());
if (!props.resize(i)) {
return false;
}
return true;
}
bool BaseProxyHandler::enumerate(JSContext* cx, HandleObject proxy,
AutoIdVector& props) const {
assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
MOZ_ASSERT(props.empty());
return GetPropertyKeys(cx, proxy, 0, &props);
}
bool BaseProxyHandler::call(JSContext* cx, HandleObject proxy,
const CallArgs& args) const {
MOZ_CRASH("callable proxies should implement call trap");
}
bool BaseProxyHandler::construct(JSContext* cx, HandleObject proxy,
const CallArgs& args) const {
MOZ_CRASH("callable proxies should implement construct trap");
}
const char* BaseProxyHandler::className(JSContext* cx,
HandleObject proxy) const {
return proxy->isCallable() ? "Function" : "Object";
}
JSString* BaseProxyHandler::fun_toString(JSContext* cx, HandleObject proxy,
bool isToSource) const {
if (proxy->isCallable()) {
return JS_NewStringCopyZ(cx, "function () {\n [native code]\n}");
}
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_INCOMPATIBLE_PROTO, js_Function_str,
js_toString_str, "object");
return nullptr;
}
RegExpShared* BaseProxyHandler::regexp_toShared(JSContext* cx,
HandleObject proxy) const {
MOZ_CRASH("This should have been a wrapped regexp");
}
bool BaseProxyHandler::boxedValue_unbox(JSContext* cx, HandleObject proxy,
MutableHandleValue vp) const {
vp.setUndefined();
return true;
}
bool BaseProxyHandler::nativeCall(JSContext* cx, IsAcceptableThis test,
NativeImpl impl, const CallArgs& args) const {
ReportIncompatible(cx, args);
return false;
}
bool BaseProxyHandler::hasInstance(JSContext* cx, HandleObject proxy,
MutableHandleValue v, bool* bp) const {
assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
RootedValue val(cx, ObjectValue(*proxy.get()));
ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, JSDVG_SEARCH_STACK, val,
nullptr);
return false;
}
bool BaseProxyHandler::getBuiltinClass(JSContext* cx, HandleObject proxy,
ESClass* cls) const {
*cls = ESClass::Other;
return true;
}
bool BaseProxyHandler::isArray(JSContext* cx, HandleObject proxy,
IsArrayAnswer* answer) const {
*answer = IsArrayAnswer::NotArray;
return true;
}
void BaseProxyHandler::trace(JSTracer* trc, JSObject* proxy) const {}
void BaseProxyHandler::finalize(JSFreeOp* fop, JSObject* proxy) const {}
size_t BaseProxyHandler::objectMoved(JSObject* proxy, JSObject* old) const {
return 0;
}
bool BaseProxyHandler::getPrototype(JSContext* cx, HandleObject proxy,
MutableHandleObject protop) const {
MOZ_CRASH("must override getPrototype with dynamic prototype");
}
bool BaseProxyHandler::setPrototype(JSContext* cx, HandleObject proxy,
HandleObject proto,
ObjectOpResult& result) const {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_CANT_SET_PROTO_OF, "incompatible Proxy");
return false;
}
bool BaseProxyHandler::setImmutablePrototype(JSContext* cx, HandleObject proxy,
bool* succeeded) const {
*succeeded = false;
return true;
}
bool BaseProxyHandler::getElements(JSContext* cx, HandleObject proxy,
uint32_t begin, uint32_t end,
ElementAdder* adder) const {
assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
return js::GetElementsWithAdder(cx, proxy, proxy, begin, end, adder);
}
bool BaseProxyHandler::isCallable(JSObject* obj) const { return false; }
bool BaseProxyHandler::isConstructor(JSObject* obj) const { return false; }