| /* -*- 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 "jsproxy.h" |
| |
| #include <string.h> |
| |
| #include "jsapi.h" |
| #include "jscntxt.h" |
| #include "jsfun.h" |
| #include "jsgc.h" |
| #include "jsprvtd.h" |
| |
| #include "gc/Marking.h" |
| |
| #include "jsatominlines.h" |
| #include "jsinferinlines.h" |
| #include "jsobjinlines.h" |
| |
| #include "vm/RegExpObject-inl.h" |
| |
| using namespace js; |
| using namespace js::gc; |
| using mozilla::ArrayLength; |
| |
| static inline HeapSlot & |
| GetCall(JSObject *proxy) |
| { |
| JS_ASSERT(IsFunctionProxy(proxy)); |
| return proxy->getSlotRef(JSSLOT_PROXY_CALL); |
| } |
| |
| static inline Value |
| GetConstruct(JSObject *proxy) |
| { |
| if (proxy->slotSpan() <= JSSLOT_PROXY_CONSTRUCT) |
| return UndefinedValue(); |
| return proxy->getSlot(JSSLOT_PROXY_CONSTRUCT); |
| } |
| |
| static inline HeapSlot & |
| GetFunctionProxyConstruct(JSObject *proxy) |
| { |
| JS_ASSERT(IsFunctionProxy(proxy)); |
| JS_ASSERT(proxy->slotSpan() > JSSLOT_PROXY_CONSTRUCT); |
| return proxy->getSlotRef(JSSLOT_PROXY_CONSTRUCT); |
| } |
| |
| void |
| js::AutoEnterPolicy::reportError(JSContext *cx, jsid id) |
| { |
| if (JSID_IS_VOID(id)) { |
| JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
| JSMSG_OBJECT_ACCESS_DENIED); |
| } else { |
| JSString *str = IdToString(cx, id); |
| const jschar *prop = str ? str->getCharsZ(cx) : NULL; |
| JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, |
| JSMSG_PROPERTY_ACCESS_DENIED, prop); |
| } |
| } |
| |
| #ifdef DEBUG |
| void |
| js::AutoEnterPolicy::recordEnter(JSContext *cx, HandleObject proxy, HandleId id) |
| { |
| if (allowed()) { |
| context = cx; |
| enteredProxy.construct(proxy); |
| enteredId.construct(id); |
| prev = cx->runtime()->enteredPolicy; |
| cx->runtime()->enteredPolicy = this; |
| } |
| } |
| |
| void |
| js::AutoEnterPolicy::recordLeave() |
| { |
| if (!enteredProxy.empty()) { |
| JS_ASSERT(context->runtime()->enteredPolicy == this); |
| context->runtime()->enteredPolicy = prev; |
| } |
| } |
| |
| JS_FRIEND_API(void) |
| js::assertEnteredPolicy(JSContext *cx, JSObject *proxy, jsid id) |
| { |
| MOZ_ASSERT(proxy->isProxy()); |
| MOZ_ASSERT(cx->runtime()->enteredPolicy); |
| MOZ_ASSERT(cx->runtime()->enteredPolicy->enteredProxy.ref().get() == proxy); |
| MOZ_ASSERT(cx->runtime()->enteredPolicy->enteredId.ref().get() == id); |
| } |
| #endif |
| |
| BaseProxyHandler::BaseProxyHandler(void *family) |
| : mFamily(family), |
| mHasPrototype(false), |
| mHasPolicy(false) |
| { |
| } |
| |
| BaseProxyHandler::~BaseProxyHandler() |
| { |
| } |
| |
| bool |
| BaseProxyHandler::enter(JSContext *cx, HandleObject wrapper, HandleId id, Action act, |
| bool *bp) |
| { |
| *bp = true; |
| return true; |
| } |
| |
| bool |
| BaseProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) |
| { |
| assertEnteredPolicy(cx, proxy, id); |
| AutoPropertyDescriptorRooter desc(cx); |
| if (!getPropertyDescriptor(cx, proxy, id, &desc, 0)) |
| return false; |
| *bp = !!desc.obj; |
| return true; |
| } |
| |
| bool |
| BaseProxyHandler::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) |
| { |
| assertEnteredPolicy(cx, proxy, id); |
| AutoPropertyDescriptorRooter desc(cx); |
| if (!getOwnPropertyDescriptor(cx, proxy, id, &desc, 0)) |
| return false; |
| *bp = !!desc.obj; |
| return true; |
| } |
| |
| bool |
| BaseProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver, |
| HandleId id, MutableHandleValue vp) |
| { |
| assertEnteredPolicy(cx, proxy, id); |
| |
| AutoPropertyDescriptorRooter desc(cx); |
| if (!getPropertyDescriptor(cx, proxy, id, &desc, 0)) |
| return false; |
| if (!desc.obj) { |
| vp.setUndefined(); |
| return true; |
| } |
| if (!desc.getter || |
| (!(desc.attrs & JSPROP_GETTER) && desc.getter == JS_PropertyStub)) { |
| vp.set(desc.value); |
| return true; |
| } |
| if (desc.attrs & JSPROP_GETTER) |
| return InvokeGetterOrSetter(cx, receiver, CastAsObjectJsval(desc.getter), 0, NULL, |
| vp.address()); |
| if (!(desc.attrs & JSPROP_SHARED)) |
| vp.set(desc.value); |
| else |
| vp.setUndefined(); |
| |
| if (desc.attrs & JSPROP_SHORTID) { |
| RootedId id(cx, INT_TO_JSID(desc.shortid)); |
| return CallJSPropertyOp(cx, desc.getter, receiver, id, vp); |
| } |
| return CallJSPropertyOp(cx, desc.getter, receiver, id, vp); |
| } |
| |
| bool |
| BaseProxyHandler::getElementIfPresent(JSContext *cx, HandleObject proxy, HandleObject receiver, |
| uint32_t index, MutableHandleValue vp, bool *present) |
| { |
| RootedId id(cx); |
| if (!IndexToId(cx, index, &id)) |
| return false; |
| |
| assertEnteredPolicy(cx, proxy, id); |
| |
| if (!has(cx, proxy, id, present)) |
| return false; |
| |
| if (!*present) { |
| Debug_SetValueRangeToCrashOnTouch(vp.address(), 1); |
| return true; |
| } |
| |
| return get(cx, proxy, receiver, id, vp); |
| } |
| |
| bool |
| BaseProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver, |
| HandleId id, bool strict, MutableHandleValue vp) |
| { |
| assertEnteredPolicy(cx, proxy, id); |
| |
| AutoPropertyDescriptorRooter desc(cx); |
| if (!getOwnPropertyDescriptor(cx, proxy, id, &desc, JSRESOLVE_ASSIGNING)) |
| return false; |
| /* The control-flow here differs from ::get() because of the fall-through case below. */ |
| if (desc.obj) { |
| // Check for read-only properties. |
| if (desc.attrs & JSPROP_READONLY) |
| return strict ? Throw(cx, id, JSMSG_CANT_REDEFINE_PROP) : true; |
| if (!desc.setter) { |
| // Be wary of the odd explicit undefined setter case possible through |
| // Object.defineProperty. |
| if (!(desc.attrs & JSPROP_SETTER)) |
| desc.setter = JS_StrictPropertyStub; |
| } else if ((desc.attrs & JSPROP_SETTER) || desc.setter != JS_StrictPropertyStub) { |
| if (!CallSetter(cx, receiver, id, desc.setter, desc.attrs, desc.shortid, strict, vp)) |
| return false; |
| if (!proxy->isProxy() || GetProxyHandler(proxy) != this) |
| return true; |
| if (desc.attrs & JSPROP_SHARED) |
| return true; |
| } |
| if (!desc.getter) { |
| // Same as above for the null setter case. |
| if (!(desc.attrs & JSPROP_GETTER)) |
| desc.getter = JS_PropertyStub; |
| } |
| desc.value = vp.get(); |
| return defineProperty(cx, receiver, id, &desc); |
| } |
| if (!getPropertyDescriptor(cx, proxy, id, &desc, JSRESOLVE_ASSIGNING)) |
| return false; |
| if (desc.obj) { |
| // Check for read-only properties. |
| if (desc.attrs & JSPROP_READONLY) |
| return strict ? Throw(cx, id, JSMSG_CANT_REDEFINE_PROP) : true; |
| if (!desc.setter) { |
| // Be wary of the odd explicit undefined setter case possible through |
| // Object.defineProperty. |
| if (!(desc.attrs & JSPROP_SETTER)) |
| desc.setter = JS_StrictPropertyStub; |
| } else if ((desc.attrs & JSPROP_SETTER) || desc.setter != JS_StrictPropertyStub) { |
| if (!CallSetter(cx, receiver, id, desc.setter, desc.attrs, desc.shortid, strict, vp)) |
| return false; |
| if (!proxy->isProxy() || GetProxyHandler(proxy) != this) |
| return true; |
| if (desc.attrs & JSPROP_SHARED) |
| return true; |
| } |
| if (!desc.getter) { |
| // Same as above for the null setter case. |
| if (!(desc.attrs & JSPROP_GETTER)) |
| desc.getter = JS_PropertyStub; |
| } |
| desc.value = vp.get(); |
| return defineProperty(cx, receiver, id, &desc); |
| } |
| |
| desc.obj = receiver; |
| desc.value = vp.get(); |
| desc.attrs = JSPROP_ENUMERATE; |
| desc.shortid = 0; |
| desc.getter = NULL; |
| desc.setter = NULL; // Pick up the class getter/setter. |
| return defineProperty(cx, receiver, id, &desc); |
| } |
| |
| bool |
| BaseProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) |
| { |
| assertEnteredPolicy(cx, proxy, JSID_VOID); |
| JS_ASSERT(props.length() == 0); |
| |
| if (!getOwnPropertyNames(cx, proxy, props)) |
| return false; |
| |
| /* Select only the enumerable properties through in-place iteration. */ |
| AutoPropertyDescriptorRooter desc(cx); |
| RootedId id(cx); |
| size_t i = 0; |
| for (size_t j = 0, len = props.length(); j < len; j++) { |
| JS_ASSERT(i <= j); |
| id = props[j]; |
| AutoWaivePolicy policy(cx, proxy, id); |
| if (!getOwnPropertyDescriptor(cx, proxy, id, &desc, 0)) |
| return false; |
| if (desc.obj && (desc.attrs & JSPROP_ENUMERATE)) |
| props[i++] = id; |
| } |
| |
| JS_ASSERT(i <= props.length()); |
| props.resize(i); |
| |
| return true; |
| } |
| |
| bool |
| BaseProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleValue vp) |
| { |
| assertEnteredPolicy(cx, proxy, JSID_VOID); |
| |
| AutoIdVector props(cx); |
| if ((flags & JSITER_OWNONLY) |
| ? !keys(cx, proxy, props) |
| : !enumerate(cx, proxy, props)) { |
| return false; |
| } |
| |
| return EnumeratedIdVectorToIterator(cx, proxy, flags, props, vp); |
| } |
| |
| bool |
| BaseProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args) |
| { |
| JS_NOT_REACHED("callable proxies should implement call trap"); |
| return false; |
| } |
| |
| bool |
| BaseProxyHandler::construct(JSContext *cx, HandleObject proxy, const CallArgs &args) |
| { |
| JS_NOT_REACHED("callable proxies should implement construct trap"); |
| return false; |
| } |
| |
| const char * |
| BaseProxyHandler::className(JSContext *cx, HandleObject proxy) |
| { |
| return IsFunctionProxy(proxy) ? "Function" : "Object"; |
| } |
| |
| JSString * |
| BaseProxyHandler::fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) |
| { |
| JS_NOT_REACHED("callable proxies should implement fun_toString trap"); |
| return NULL; |
| } |
| |
| bool |
| BaseProxyHandler::regexp_toShared(JSContext *cx, HandleObject proxy, |
| RegExpGuard *g) |
| { |
| JS_NOT_REACHED("This should have been a wrapped regexp"); |
| return false; |
| } |
| |
| bool |
| BaseProxyHandler::defaultValue(JSContext *cx, HandleObject proxy, JSType hint, |
| MutableHandleValue vp) |
| { |
| return DefaultValue(cx, proxy, hint, vp); |
| } |
| |
| bool |
| BaseProxyHandler::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args) |
| { |
| ReportIncompatible(cx, args); |
| return false; |
| } |
| |
| bool |
| BaseProxyHandler::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp) |
| { |
| assertEnteredPolicy(cx, proxy, JSID_VOID); |
| RootedValue val(cx, ObjectValue(*proxy.get())); |
| js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, |
| JSDVG_SEARCH_STACK, val, NullPtr()); |
| return false; |
| } |
| |
| bool |
| BaseProxyHandler::objectClassIs(HandleObject proxy, ESClassValue classValue, JSContext *cx) |
| { |
| return false; |
| } |
| |
| void |
| BaseProxyHandler::finalize(JSFreeOp *fop, JSObject *proxy) |
| { |
| } |
| |
| JSObject * |
| BaseProxyHandler::weakmapKeyDelegate(JSObject *proxy) |
| { |
| return NULL; |
| } |
| |
| bool |
| BaseProxyHandler::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop) |
| { |
| // The default implementation here just uses proto of the proxy object. |
| protop.set(proxy->getTaggedProto().toObjectOrNull()); |
| return true; |
| } |
| |
| |
| bool |
| DirectProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, |
| PropertyDescriptor *desc, unsigned flags) |
| { |
| assertEnteredPolicy(cx, proxy, id); |
| JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype. |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| return JS_GetPropertyDescriptorById(cx, target, id, 0, desc); |
| } |
| |
| static bool |
| GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id, unsigned flags, |
| JSPropertyDescriptor *desc) |
| { |
| // If obj is a proxy, we can do better than just guessing. This is |
| // important for certain types of wrappers that wrap other wrappers. |
| if (obj->isProxy()) |
| return Proxy::getOwnPropertyDescriptor(cx, obj, id, desc, flags); |
| |
| if (!JS_GetPropertyDescriptorById(cx, obj, id, flags, desc)) |
| return false; |
| if (desc->obj != obj) |
| desc->obj = NULL; |
| return true; |
| } |
| |
| bool |
| DirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, |
| HandleId id, PropertyDescriptor *desc, unsigned flags) |
| { |
| assertEnteredPolicy(cx, proxy, id); |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| return GetOwnPropertyDescriptor(cx, target, id, 0, desc); |
| } |
| |
| bool |
| DirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id, |
| PropertyDescriptor *desc) |
| { |
| assertEnteredPolicy(cx, proxy, id); |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| RootedValue v(cx, desc->value); |
| return CheckDefineProperty(cx, target, id, v, desc->getter, desc->setter, desc->attrs) && |
| JS_DefinePropertyById(cx, target, id, v, desc->getter, desc->setter, desc->attrs); |
| } |
| |
| bool |
| DirectProxyHandler::getOwnPropertyNames(JSContext *cx, HandleObject proxy, |
| AutoIdVector &props) |
| { |
| assertEnteredPolicy(cx, proxy, JSID_VOID); |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| return GetPropertyNames(cx, target, JSITER_OWNONLY | JSITER_HIDDEN, &props); |
| } |
| |
| bool |
| DirectProxyHandler::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) |
| { |
| assertEnteredPolicy(cx, proxy, id); |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| RootedValue v(cx); |
| if (!JS_DeletePropertyById2(cx, target, id, v.address())) |
| return false; |
| JSBool b; |
| if (!JS_ValueToBoolean(cx, v, &b)) |
| return false; |
| *bp = !!b; |
| return true; |
| } |
| |
| bool |
| DirectProxyHandler::enumerate(JSContext *cx, HandleObject proxy, |
| AutoIdVector &props) |
| { |
| assertEnteredPolicy(cx, proxy, JSID_VOID); |
| JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype. |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| return GetPropertyNames(cx, target, 0, &props); |
| } |
| |
| bool |
| DirectProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args) |
| { |
| assertEnteredPolicy(cx, proxy, JSID_VOID); |
| RootedValue target(cx, GetProxyPrivate(proxy)); |
| return Invoke(cx, args.thisv(), target, args.length(), args.array(), args.rval().address()); |
| } |
| |
| bool |
| DirectProxyHandler::construct(JSContext *cx, HandleObject proxy, const CallArgs &args) |
| { |
| assertEnteredPolicy(cx, proxy, JSID_VOID); |
| RootedValue target(cx, GetProxyPrivate(proxy)); |
| return InvokeConstructor(cx, target, args.length(), args.array(), args.rval().address()); |
| } |
| |
| bool |
| DirectProxyHandler::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, |
| CallArgs args) |
| { |
| args.setThis(ObjectValue(*GetProxyTargetObject(&args.thisv().toObject()))); |
| if (!test(args.thisv())) { |
| ReportIncompatible(cx, args); |
| return false; |
| } |
| |
| return CallNativeImpl(cx, impl, args); |
| } |
| |
| bool |
| DirectProxyHandler::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, |
| bool *bp) |
| { |
| assertEnteredPolicy(cx, proxy, JSID_VOID); |
| JSBool b; |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| if (!JS_HasInstance(cx, target, v, &b)) |
| return false; |
| *bp = !!b; |
| return true; |
| } |
| |
| |
| bool |
| DirectProxyHandler::objectClassIs(HandleObject proxy, ESClassValue classValue, |
| JSContext *cx) |
| { |
| RootedObject obj(cx, GetProxyTargetObject(proxy)); |
| return ObjectClassIs(obj, classValue, cx); |
| } |
| |
| const char * |
| DirectProxyHandler::className(JSContext *cx, HandleObject proxy) |
| { |
| assertEnteredPolicy(cx, proxy, JSID_VOID); |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| return JSObject::className(cx, target); |
| } |
| |
| JSString * |
| DirectProxyHandler::fun_toString(JSContext *cx, HandleObject proxy, |
| unsigned indent) |
| { |
| assertEnteredPolicy(cx, proxy, JSID_VOID); |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| return fun_toStringHelper(cx, target, indent); |
| } |
| |
| bool |
| DirectProxyHandler::regexp_toShared(JSContext *cx, HandleObject proxy, |
| RegExpGuard *g) |
| { |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| return RegExpToShared(cx, target, g); |
| } |
| |
| bool |
| DirectProxyHandler::defaultValue(JSContext *cx, HandleObject proxy, JSType hint, |
| MutableHandleValue vp) |
| { |
| vp.set(ObjectValue(*GetProxyTargetObject(proxy))); |
| if (hint == JSTYPE_VOID) |
| return ToPrimitive(cx, vp); |
| return ToPrimitive(cx, hint, vp); |
| } |
| |
| JSObject * |
| DirectProxyHandler::weakmapKeyDelegate(JSObject *proxy) |
| { |
| return UncheckedUnwrap(proxy); |
| } |
| |
| DirectProxyHandler::DirectProxyHandler(void *family) |
| : BaseProxyHandler(family) |
| { |
| } |
| |
| bool |
| DirectProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) |
| { |
| assertEnteredPolicy(cx, proxy, id); |
| JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype. |
| JSBool found; |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| if (!JS_HasPropertyById(cx, target, id, &found)) |
| return false; |
| *bp = !!found; |
| return true; |
| } |
| |
| bool |
| DirectProxyHandler::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) |
| { |
| assertEnteredPolicy(cx, proxy, id); |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| AutoPropertyDescriptorRooter desc(cx); |
| if (!JS_GetPropertyDescriptorById(cx, target, id, 0, &desc)) |
| return false; |
| *bp = (desc.obj == target); |
| return true; |
| } |
| |
| bool |
| DirectProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver, |
| HandleId id, MutableHandleValue vp) |
| { |
| assertEnteredPolicy(cx, proxy, id); |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| return JSObject::getGeneric(cx, target, receiver, id, vp); |
| } |
| |
| bool |
| DirectProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver, |
| HandleId id, bool strict, MutableHandleValue vp) |
| { |
| assertEnteredPolicy(cx, proxy, id); |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| return JSObject::setGeneric(cx, target, receiver, id, vp, strict); |
| } |
| |
| bool |
| DirectProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) |
| { |
| assertEnteredPolicy(cx, proxy, JSID_VOID); |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| return GetPropertyNames(cx, target, JSITER_OWNONLY, &props); |
| } |
| |
| bool |
| DirectProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags, |
| MutableHandleValue vp) |
| { |
| assertEnteredPolicy(cx, proxy, JSID_VOID); |
| JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype. |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| return GetIterator(cx, target, flags, vp); |
| } |
| |
| bool |
| DirectProxyHandler::isExtensible(JSObject *proxy) |
| { |
| return GetProxyTargetObject(proxy)->isExtensible(); |
| } |
| |
| bool |
| DirectProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy) |
| { |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| return JSObject::preventExtensions(cx, target); |
| } |
| |
| static bool |
| GetFundamentalTrap(JSContext *cx, HandleObject handler, HandlePropertyName name, |
| MutableHandleValue fvalp) |
| { |
| JS_CHECK_RECURSION(cx, return false); |
| |
| return JSObject::getProperty(cx, handler, handler, name, fvalp); |
| } |
| |
| static bool |
| GetDerivedTrap(JSContext *cx, HandleObject handler, HandlePropertyName name, |
| MutableHandleValue fvalp) |
| { |
| JS_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 JSObject::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.address()); |
| } |
| |
| static bool |
| Trap1(JSContext *cx, HandleObject handler, HandleValue fval, HandleId id, MutableHandleValue rval) |
| { |
| rval.set(IdToValue(id)); // Re-use out-param to avoid Rooted overhead. |
| JSString *str = ToString<CanGC>(cx, rval); |
| if (!str) |
| return false; |
| rval.setString(str); |
| 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_); |
| rval.set(IdToValue(id)); // Re-use out-param to avoid Rooted overhead. |
| JSString *str = ToString<CanGC>(cx, rval); |
| if (!str) |
| return false; |
| rval.setString(str); |
| Value argv[2] = { rval.get(), v }; |
| AutoValueArray ava(cx, argv, 2); |
| return Trap(cx, handler, fval, 2, argv, rval); |
| } |
| |
| static bool |
| ParsePropertyDescriptorObject(JSContext *cx, HandleObject obj, const Value &v, |
| PropertyDescriptor *desc, bool complete = false) |
| { |
| AutoPropDescArrayRooter descs(cx); |
| PropDesc *d = descs.append(); |
| if (!d || !d->initialize(cx, v)) |
| return false; |
| if (complete) |
| d->complete(); |
| desc->obj = obj; |
| desc->value = d->hasValue() ? d->value() : UndefinedValue(); |
| JS_ASSERT(!(d->attributes() & JSPROP_SHORTID)); |
| desc->attrs = d->attributes(); |
| desc->getter = d->getter(); |
| desc->setter = d->setter(); |
| desc->shortid = 0; |
| return true; |
| } |
| |
| static bool |
| IndicatePropertyNotFound(PropertyDescriptor *desc) |
| { |
| desc->obj = NULL; |
| return true; |
| } |
| |
| static bool |
| ValueToBool(const Value &v, bool *bp) |
| { |
| *bp = ToBoolean(v); |
| return true; |
| } |
| |
| static bool |
| ArrayToIdVector(JSContext *cx, const Value &array, AutoIdVector &props) |
| { |
| JS_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 (!JS_CHECK_OPERATION_LIMIT(cx)) |
| return false; |
| if (!JSObject::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; |
| } |
| |
| /* Derived class for all scripted indirect proxy handlers. */ |
| class ScriptedIndirectProxyHandler : public BaseProxyHandler |
| { |
| public: |
| ScriptedIndirectProxyHandler(); |
| virtual ~ScriptedIndirectProxyHandler(); |
| |
| /* ES5 Harmony fundamental proxy traps. */ |
| virtual bool preventExtensions(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE; |
| virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, |
| PropertyDescriptor *desc, unsigned flags) MOZ_OVERRIDE; |
| virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, |
| PropertyDescriptor *desc, unsigned flags) MOZ_OVERRIDE; |
| virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id, |
| PropertyDescriptor *desc) MOZ_OVERRIDE; |
| virtual bool getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props); |
| virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) MOZ_OVERRIDE; |
| virtual bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) MOZ_OVERRIDE; |
| |
| /* ES5 Harmony derived proxy traps. */ |
| virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) MOZ_OVERRIDE; |
| virtual bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) MOZ_OVERRIDE; |
| virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, |
| MutableHandleValue vp) MOZ_OVERRIDE; |
| virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, |
| bool strict, MutableHandleValue vp) MOZ_OVERRIDE; |
| virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) MOZ_OVERRIDE; |
| virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, |
| MutableHandleValue vp) MOZ_OVERRIDE; |
| |
| /* Spidermonkey extensions. */ |
| virtual bool isExtensible(JSObject *proxy) MOZ_OVERRIDE; |
| virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE; |
| virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE; |
| virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, |
| CallArgs args) MOZ_OVERRIDE; |
| virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) MOZ_OVERRIDE; |
| virtual bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, |
| MutableHandleValue vp) MOZ_OVERRIDE; |
| |
| static ScriptedIndirectProxyHandler singleton; |
| }; |
| |
| static int sScriptedIndirectProxyHandlerFamily = 0; |
| |
| ScriptedIndirectProxyHandler::ScriptedIndirectProxyHandler() |
| : BaseProxyHandler(&sScriptedIndirectProxyHandlerFamily) |
| { |
| } |
| |
| ScriptedIndirectProxyHandler::~ScriptedIndirectProxyHandler() |
| { |
| } |
| |
| bool |
| ScriptedIndirectProxyHandler::isExtensible(JSObject *proxy) |
| { |
| // Scripted indirect proxies don't support extensibility changes. |
| return true; |
| } |
| |
| bool |
| ScriptedIndirectProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy) |
| { |
| // See above. |
| JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CHANGE_EXTENSIBILITY); |
| return false; |
| } |
| |
| static bool |
| ReturnedValueMustNotBePrimitive(JSContext *cx, HandleObject proxy, JSAtom *atom, const Value &v) |
| { |
| if (v.isPrimitive()) { |
| JSAutoByteString bytes; |
| if (js_AtomToPrintableString(cx, atom, &bytes)) { |
| RootedValue val(cx, ObjectOrNullValue(proxy)); |
| js_ReportValueError2(cx, JSMSG_BAD_TRAP_RETURN_VALUE, |
| JSDVG_SEARCH_STACK, val, NullPtr(), bytes.ptr()); |
| } |
| return false; |
| } |
| return true; |
| } |
| |
| static JSObject * |
| GetIndirectProxyHandlerObject(JSObject *proxy) |
| { |
| return GetProxyPrivate(proxy).toObjectOrNull(); |
| } |
| |
| bool |
| ScriptedIndirectProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, |
| PropertyDescriptor *desc, unsigned flags) |
| { |
| 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.get().isUndefined() && IndicatePropertyNotFound(desc)) || |
| (ReturnedValueMustNotBePrimitive(cx, proxy, cx->names().getPropertyDescriptor, value) && |
| ParsePropertyDescriptorObject(cx, proxy, value, desc))); |
| } |
| |
| bool |
| ScriptedIndirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, |
| PropertyDescriptor *desc, unsigned flags) |
| { |
| 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.get().isUndefined() && IndicatePropertyNotFound(desc)) || |
| (ReturnedValueMustNotBePrimitive(cx, proxy, cx->names().getPropertyDescriptor, value) && |
| ParsePropertyDescriptorObject(cx, proxy, value, desc))); |
| } |
| |
| bool |
| ScriptedIndirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id, |
| PropertyDescriptor *desc) |
| { |
| RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); |
| RootedValue fval(cx), value(cx); |
| return GetFundamentalTrap(cx, handler, cx->names().defineProperty, &fval) && |
| NewPropertyDescriptorObject(cx, desc, &value) && |
| Trap2(cx, handler, fval, id, value, &value); |
| } |
| |
| bool |
| ScriptedIndirectProxyHandler::getOwnPropertyNames(JSContext *cx, HandleObject proxy, |
| AutoIdVector &props) |
| { |
| RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); |
| RootedValue fval(cx), value(cx); |
| return GetFundamentalTrap(cx, handler, cx->names().getOwnPropertyNames, &fval) && |
| Trap(cx, handler, fval, 0, NULL, &value) && |
| ArrayToIdVector(cx, value, props); |
| } |
| |
| bool |
| ScriptedIndirectProxyHandler::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) |
| { |
| RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); |
| RootedValue fval(cx), value(cx); |
| return GetFundamentalTrap(cx, handler, cx->names().delete_, &fval) && |
| Trap1(cx, handler, fval, id, &value) && |
| ValueToBool(value, bp); |
| } |
| |
| bool |
| ScriptedIndirectProxyHandler::enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) |
| { |
| RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); |
| RootedValue fval(cx), value(cx); |
| return GetFundamentalTrap(cx, handler, cx->names().enumerate, &fval) && |
| Trap(cx, handler, fval, 0, NULL, &value) && |
| ArrayToIdVector(cx, value, props); |
| } |
| |
| bool |
| ScriptedIndirectProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) |
| { |
| RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); |
| RootedValue fval(cx), value(cx); |
| if (!GetDerivedTrap(cx, handler, cx->names().has, &fval)) |
| return false; |
| if (!js_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) |
| { |
| RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); |
| RootedValue fval(cx), value(cx); |
| if (!GetDerivedTrap(cx, handler, cx->names().hasOwn, &fval)) |
| return false; |
| if (!js_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) |
| { |
| RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); |
| RootedValue idv(cx, IdToValue(id)); |
| JSString *str = ToString<CanGC>(cx, idv); |
| if (!str) |
| return false; |
| RootedValue value(cx, StringValue(str)); |
| Value argv[] = { ObjectOrNullValue(receiver), value }; |
| AutoValueArray ava(cx, argv, 2); |
| RootedValue fval(cx); |
| if (!GetDerivedTrap(cx, handler, cx->names().get, &fval)) |
| return false; |
| if (!js_IsCallable(fval)) |
| return BaseProxyHandler::get(cx, proxy, receiver, id, vp); |
| return Trap(cx, handler, fval, 2, argv, vp); |
| } |
| |
| bool |
| ScriptedIndirectProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver, |
| HandleId id, bool strict, MutableHandleValue vp) |
| { |
| RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); |
| RootedValue idv(cx, IdToValue(id)); |
| JSString *str = ToString<CanGC>(cx, idv); |
| if (!str) |
| return false; |
| RootedValue value(cx, StringValue(str)); |
| Value argv[] = { ObjectOrNullValue(receiver), value, vp.get() }; |
| AutoValueArray ava(cx, argv, 3); |
| RootedValue fval(cx); |
| if (!GetDerivedTrap(cx, handler, cx->names().set, &fval)) |
| return false; |
| if (!js_IsCallable(fval)) |
| return BaseProxyHandler::set(cx, proxy, receiver, id, strict, vp); |
| return Trap(cx, handler, fval, 3, argv, &value); |
| } |
| |
| bool |
| ScriptedIndirectProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) |
| { |
| RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); |
| RootedValue value(cx); |
| if (!GetDerivedTrap(cx, handler, cx->names().keys, &value)) |
| return false; |
| if (!js_IsCallable(value)) |
| return BaseProxyHandler::keys(cx, proxy, props); |
| return Trap(cx, handler, value, 0, NULL, &value) && |
| ArrayToIdVector(cx, value, props); |
| } |
| |
| bool |
| ScriptedIndirectProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags, |
| MutableHandleValue vp) |
| { |
| RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); |
| RootedValue value(cx); |
| if (!GetDerivedTrap(cx, handler, cx->names().iterate, &value)) |
| return false; |
| if (!js_IsCallable(value)) |
| return BaseProxyHandler::iterate(cx, proxy, flags, vp); |
| return Trap(cx, handler, value, 0, NULL, vp) && |
| ReturnedValueMustNotBePrimitive(cx, proxy, cx->names().iterate, vp); |
| } |
| |
| bool |
| ScriptedIndirectProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args) |
| { |
| assertEnteredPolicy(cx, proxy, JSID_VOID); |
| RootedValue call(cx, GetCall(proxy)); |
| return Invoke(cx, args.thisv(), call, args.length(), args.array(), args.rval().address()); |
| } |
| |
| bool |
| ScriptedIndirectProxyHandler::construct(JSContext *cx, HandleObject proxy, const CallArgs &args) |
| { |
| assertEnteredPolicy(cx, proxy, JSID_VOID); |
| RootedValue fval(cx, GetConstruct(proxy)); |
| if (fval.isUndefined()) |
| fval = GetCall(proxy); |
| return InvokeConstructor(cx, fval, args.length(), args.array(), args.rval().address()); |
| } |
| |
| bool |
| ScriptedIndirectProxyHandler::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, |
| CallArgs args) |
| { |
| return BaseProxyHandler::nativeCall(cx, test, impl, args); |
| } |
| |
| JSString * |
| ScriptedIndirectProxyHandler::fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) |
| { |
| assertEnteredPolicy(cx, proxy, JSID_VOID); |
| Value fval = GetCall(proxy); |
| if (IsFunctionProxy(proxy) && |
| (fval.isPrimitive() || !fval.toObject().is<JSFunction>())) { |
| JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
| JSMSG_INCOMPATIBLE_PROTO, |
| js_Function_str, js_toString_str, |
| "object"); |
| return NULL; |
| } |
| RootedObject obj(cx, &fval.toObject()); |
| return fun_toStringHelper(cx, obj, indent); |
| } |
| |
| bool |
| ScriptedIndirectProxyHandler::defaultValue(JSContext *cx, HandleObject proxy, JSType hint, |
| MutableHandleValue vp) |
| { |
| /* |
| * This function is only here to prevent bug 757063. It will be removed when |
| * the direct proxy refactor is complete. |
| */ |
| return BaseProxyHandler::defaultValue(cx, proxy, hint, vp); |
| } |
| |
| ScriptedIndirectProxyHandler ScriptedIndirectProxyHandler::singleton; |
| |
| static JSObject * |
| GetDirectProxyHandlerObject(JSObject *proxy) |
| { |
| return GetProxyExtra(proxy, 0).toObjectOrNull(); |
| } |
| |
| /* Derived class for all scripted direct proxy handlers. */ |
| class ScriptedDirectProxyHandler : public DirectProxyHandler { |
| public: |
| ScriptedDirectProxyHandler(); |
| virtual ~ScriptedDirectProxyHandler(); |
| |
| /* ES5 Harmony fundamental proxy traps. */ |
| virtual bool preventExtensions(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE; |
| virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, |
| PropertyDescriptor *desc, unsigned flags) MOZ_OVERRIDE; |
| virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, |
| PropertyDescriptor *desc, unsigned flags) MOZ_OVERRIDE; |
| virtual bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id, |
| PropertyDescriptor *desc) MOZ_OVERRIDE; |
| virtual bool getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props); |
| virtual bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) MOZ_OVERRIDE; |
| virtual bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) MOZ_OVERRIDE; |
| |
| /* ES5 Harmony derived proxy traps. */ |
| virtual bool has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) MOZ_OVERRIDE; |
| virtual bool hasOwn(JSContext *cx,HandleObject proxy, HandleId id, bool *bp) MOZ_OVERRIDE; |
| virtual bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, |
| MutableHandleValue vp) MOZ_OVERRIDE; |
| virtual bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, |
| bool strict, MutableHandleValue vp) MOZ_OVERRIDE; |
| virtual bool keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) MOZ_OVERRIDE; |
| virtual bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, |
| MutableHandleValue vp) MOZ_OVERRIDE; |
| |
| virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE; |
| virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE; |
| |
| static ScriptedDirectProxyHandler singleton; |
| }; |
| |
| static int sScriptedDirectProxyHandlerFamily = 0; |
| |
| // Aux.2 FromGenericPropertyDescriptor(Desc) |
| static bool |
| FromGenericPropertyDescriptor(JSContext *cx, PropDesc *desc, MutableHandleValue rval) |
| { |
| // Aux.2 step 1 |
| if (desc->isUndefined()) { |
| rval.setUndefined(); |
| return true; |
| } |
| |
| // steps 3-9 |
| if (!desc->makeObject(cx)) |
| return false; |
| rval.set(desc->pd()); |
| return true; |
| } |
| |
| /* |
| * Aux.3 NormalizePropertyDescriptor(Attributes) |
| * |
| * NOTE: to minimize code duplication, the code for this function is shared with |
| * that for Aux.4 NormalizeAndCompletePropertyDescriptor (see below). The |
| * argument complete is used to distinguish between the two. |
| */ |
| static bool |
| NormalizePropertyDescriptor(JSContext *cx, MutableHandleValue vp, bool complete = false) |
| { |
| // Aux.4 step 1 |
| if (complete && vp.isUndefined()) |
| return true; |
| |
| // Aux.3 steps 1-2 / Aux.4 steps 2-3 |
| AutoPropDescArrayRooter descs(cx); |
| PropDesc *desc = descs.append(); |
| if (!desc || !desc->initialize(cx, vp.get())) |
| return false; |
| if (complete) |
| desc->complete(); |
| JS_ASSERT(!vp.isPrimitive()); // due to desc->initialize |
| RootedObject attributes(cx, &vp.toObject()); |
| |
| /* |
| * Aux.3 step 3 / Aux.4 step 4 |
| * |
| * NOTE: Aux.4 step 4 actually specifies FromPropertyDescriptor here. |
| * However, the way FromPropertyDescriptor is implemented (PropDesc:: |
| * makeObject) is actually closer to FromGenericPropertyDescriptor, |
| * and is in fact used to implement the latter, so we might as well call it |
| * directly. |
| */ |
| if (!FromGenericPropertyDescriptor(cx, desc, vp)) |
| return false; |
| if (vp.isUndefined()) |
| return true; |
| RootedObject descObj(cx, &vp.toObject()); |
| |
| // Aux.3 steps 4-5 / Aux.4 steps 5-6 |
| AutoIdVector props(cx); |
| if (!GetPropertyNames(cx, attributes, 0, &props)) |
| return false; |
| size_t n = props.length(); |
| for (size_t i = 0; i < n; ++i) { |
| RootedId id(cx, props[i]); |
| if (JSID_IS_ATOM(id)) { |
| JSAtom *atom = JSID_TO_ATOM(id); |
| const JSAtomState &atomState = cx->runtime()->atomState; |
| if (atom == atomState.value || atom == atomState.writable || |
| atom == atomState.get || atom == atomState.set || |
| atom == atomState.enumerable || atom == atomState.configurable) |
| { |
| continue; |
| } |
| } |
| |
| RootedValue v(cx); |
| if (!JSObject::getGeneric(cx, descObj, attributes, id, &v)) |
| return false; |
| if (!JSObject::defineGeneric(cx, descObj, id, v, NULL, NULL, JSPROP_ENUMERATE)) |
| return false; |
| } |
| return true; |
| } |
| |
| // Aux.4 NormalizeAndCompletePropertyDescriptor(Attributes) |
| static inline bool |
| NormalizeAndCompletePropertyDescriptor(JSContext *cx, MutableHandleValue vp) |
| { |
| return NormalizePropertyDescriptor(cx, vp, true); |
| } |
| |
| static inline bool |
| IsDataDescriptor(const PropertyDescriptor &desc) |
| { |
| return desc.obj && !(desc.attrs & (JSPROP_GETTER | JSPROP_SETTER)); |
| } |
| |
| static inline bool |
| IsAccessorDescriptor(const PropertyDescriptor &desc) |
| { |
| return desc.obj && desc.attrs & (JSPROP_GETTER | JSPROP_SETTER); |
| } |
| |
| // Aux.5 ValidateProperty(O, P, Desc) |
| static bool |
| ValidateProperty(JSContext *cx, HandleObject obj, HandleId id, PropDesc *desc, bool *bp) |
| { |
| // step 1 |
| AutoPropertyDescriptorRooter current(cx); |
| if (!GetOwnPropertyDescriptor(cx, obj, id, 0, ¤t)) |
| return false; |
| |
| /* |
| * steps 2-4 are redundant since ValidateProperty is never called unless |
| * target.[[HasOwn]](P) is true |
| */ |
| JS_ASSERT(current.obj); |
| |
| // step 5 |
| if (!desc->hasValue() && !desc->hasWritable() && !desc->hasGet() && !desc->hasSet() && |
| !desc->hasEnumerable() && !desc->hasConfigurable()) |
| { |
| *bp = true; |
| return true; |
| } |
| |
| // step 6 |
| if ((!desc->hasWritable() || desc->writable() == !(current.attrs & JSPROP_READONLY)) && |
| (!desc->hasGet() || desc->getter() == current.getter) && |
| (!desc->hasSet() || desc->setter() == current.setter) && |
| (!desc->hasEnumerable() || desc->enumerable() == bool(current.attrs & JSPROP_ENUMERATE)) && |
| (!desc->hasConfigurable() || desc->configurable() == !(current.attrs & JSPROP_PERMANENT))) |
| { |
| if (!desc->hasValue()) { |
| *bp = true; |
| return true; |
| } |
| bool same = false; |
| if (!SameValue(cx, desc->value(), current.value, &same)) |
| return false; |
| if (same) { |
| *bp = true; |
| return true; |
| } |
| } |
| |
| // step 7 |
| if (current.attrs & JSPROP_PERMANENT) { |
| if (desc->hasConfigurable() && desc->configurable()) { |
| *bp = false; |
| return true; |
| } |
| |
| if (desc->hasEnumerable() && |
| desc->enumerable() != bool(current.attrs & JSPROP_ENUMERATE)) |
| { |
| *bp = false; |
| return true; |
| } |
| } |
| |
| // step 8 |
| if (desc->isGenericDescriptor()) { |
| *bp = true; |
| return true; |
| } |
| |
| // step 9 |
| if (IsDataDescriptor(current) != desc->isDataDescriptor()) { |
| *bp = !(current.attrs & JSPROP_PERMANENT); |
| return true; |
| } |
| |
| // step 10 |
| if (IsDataDescriptor(current)) { |
| JS_ASSERT(desc->isDataDescriptor()); // by step 9 |
| if ((current.attrs & JSPROP_PERMANENT) && (current.attrs & JSPROP_READONLY)) { |
| if (desc->hasWritable() && desc->writable()) { |
| *bp = false; |
| return true; |
| } |
| |
| if (desc->hasValue()) { |
| bool same; |
| if (!SameValue(cx, desc->value(), current.value, &same)) |
| return false; |
| if (!same) { |
| *bp = false; |
| return true; |
| } |
| } |
| } |
| |
| *bp = true; |
| return true; |
| } |
| |
| // steps 11-12 |
| JS_ASSERT(IsAccessorDescriptor(current)); // by step 10 |
| JS_ASSERT(desc->isAccessorDescriptor()); // by step 9 |
| *bp = (!(current.attrs & JSPROP_PERMANENT) || |
| ((!desc->hasSet() || desc->setter() == current.setter) && |
| (!desc->hasGet() || desc->getter() == current.getter))); |
| return true; |
| } |
| |
| // Aux.6 IsSealed(O, P) |
| static bool |
| IsSealed(JSContext* cx, HandleObject obj, HandleId id, bool *bp) |
| { |
| // step 1 |
| AutoPropertyDescriptorRooter desc(cx); |
| if (!GetOwnPropertyDescriptor(cx, obj, id, 0, &desc)) |
| return false; |
| |
| // steps 2-3 |
| *bp = desc.obj && (desc.attrs & JSPROP_PERMANENT); |
| return true; |
| } |
| |
| static bool |
| HasOwn(JSContext *cx, HandleObject obj, HandleId id, bool *bp) |
| { |
| AutoPropertyDescriptorRooter desc(cx); |
| if (!JS_GetPropertyDescriptorById(cx, obj, id, 0, &desc)) |
| return false; |
| *bp = (desc.obj == obj); |
| return true; |
| } |
| |
| static bool |
| IdToValue(JSContext *cx, HandleId id, MutableHandleValue value) |
| { |
| value.set(IdToValue(id)); // Re-use out-param to avoid Rooted overhead. |
| JSString *name = ToString<CanGC>(cx, value); |
| if (!name) |
| return false; |
| value.set(StringValue(name)); |
| return true; |
| } |
| |
| // TrapGetOwnProperty(O, P) |
| static bool |
| TrapGetOwnProperty(JSContext *cx, HandleObject proxy, HandleId id, MutableHandleValue rval) |
| { |
| // step 1 |
| RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); |
| |
| // step 2 |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| |
| // step 3 |
| RootedValue trap(cx); |
| if (!JSObject::getProperty(cx, handler, handler, cx->names().getOwnPropertyDescriptor, &trap)) |
| return false; |
| |
| // step 4 |
| if (trap.isUndefined()) { |
| AutoPropertyDescriptorRooter desc(cx); |
| if (!GetOwnPropertyDescriptor(cx, target, id, &desc)) |
| return false; |
| return NewPropertyDescriptorObject(cx, &desc, rval); |
| } |
| |
| // step 5 |
| RootedValue value(cx); |
| if (!IdToValue(cx, id, &value)) |
| return false; |
| Value argv[] = { |
| ObjectValue(*target), |
| value |
| }; |
| RootedValue trapResult(cx); |
| if (!Invoke(cx, ObjectValue(*handler), trap, 2, argv, trapResult.address())) |
| return false; |
| |
| // step 6 |
| if (!NormalizeAndCompletePropertyDescriptor(cx, &trapResult)) |
| return false; |
| |
| // step 7 |
| if (trapResult.isUndefined()) { |
| bool sealed; |
| if (!IsSealed(cx, target, id, &sealed)) |
| return false; |
| if (sealed) { |
| JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_REPORT_NC_AS_NE); |
| return false; |
| } |
| |
| if (!target->isExtensible()) { |
| bool found; |
| if (!HasOwn(cx, target, id, &found)) |
| return false; |
| if (found) { |
| JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_REPORT_E_AS_NE); |
| return false; |
| } |
| } |
| |
| rval.set(UndefinedValue()); |
| return true; |
| } |
| |
| // step 8 |
| bool isFixed; |
| if (!HasOwn(cx, target, id, &isFixed)) |
| return false; |
| |
| // step 9 |
| if (target->isExtensible() && !isFixed) { |
| JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_REPORT_NEW); |
| return false; |
| } |
| |
| AutoPropDescArrayRooter descs(cx); |
| PropDesc *desc = descs.append(); |
| if (!desc || !desc->initialize(cx, trapResult)) |
| return false; |
| |
| /* step 10 */ |
| if (isFixed) { |
| bool valid; |
| if (!ValidateProperty(cx, target, id, desc, &valid)) |
| return false; |
| |
| if (!valid) { |
| JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_REPORT_INVALID); |
| return false; |
| } |
| } |
| |
| // step 11 |
| if (!desc->configurable() && !isFixed) { |
| JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_REPORT_NE_AS_NC); |
| return false; |
| } |
| |
| // step 12 |
| rval.set(trapResult); |
| return true; |
| } |
| |
| // TrapDefineOwnProperty(O, P, DescObj, Throw) |
| static bool |
| TrapDefineOwnProperty(JSContext *cx, HandleObject proxy, HandleId id, MutableHandleValue vp) |
| { |
| // step 1 |
| RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); |
| |
| // step 2 |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| |
| // step 3 |
| RootedValue trap(cx); |
| if (!JSObject::getProperty(cx, handler, handler, cx->names().defineProperty, &trap)) |
| return false; |
| |
| // step 4 |
| if (trap.isUndefined()) { |
| AutoPropertyDescriptorRooter desc(cx); |
| if (!ParsePropertyDescriptorObject(cx, proxy, vp, &desc)) |
| return false; |
| return JS_DefinePropertyById(cx, target, id, desc.value, desc.getter, desc.setter, |
| desc.attrs); |
| } |
| |
| // step 5 |
| RootedValue normalizedDesc(cx, vp); |
| if (!NormalizePropertyDescriptor(cx, &normalizedDesc)) |
| return false; |
| |
| // step 6 |
| RootedValue value(cx); |
| if (!IdToValue(cx, id, &value)) |
| return false; |
| Value argv[] = { |
| ObjectValue(*target), |
| value, |
| normalizedDesc |
| }; |
| RootedValue trapResult(cx); |
| if (!Invoke(cx, ObjectValue(*handler), trap, 3, argv, trapResult.address())) |
| return false; |
| |
| // steps 7-8 |
| if (ToBoolean(trapResult)) { |
| bool isFixed; |
| if (!HasOwn(cx, target, id, &isFixed)) |
| return false; |
| |
| if (!target->isExtensible() && !isFixed) { |
| JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_DEFINE_NEW); |
| return false; |
| } |
| |
| AutoPropDescArrayRooter descs(cx); |
| PropDesc *desc = descs.append(); |
| if (!desc || !desc->initialize(cx, normalizedDesc)) |
| return false; |
| |
| if (isFixed) { |
| bool valid; |
| if (!ValidateProperty(cx, target, id, desc, &valid)) |
| return false; |
| if (!valid) { |
| JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_DEFINE_INVALID); |
| return false; |
| } |
| } |
| |
| if (!desc->configurable() && !isFixed) { |
| JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_DEFINE_NE_AS_NC); |
| return false; |
| } |
| |
| vp.set(BooleanValue(true)); |
| return true; |
| } |
| |
| // step 9 |
| // FIXME: API does not include a Throw parameter |
| vp.set(BooleanValue(false)); |
| return true; |
| } |
| |
| static inline void |
| ReportInvalidTrapResult(JSContext *cx, JSObject *proxy, JSAtom *atom) |
| { |
| RootedValue v(cx, ObjectOrNullValue(proxy)); |
| JSAutoByteString bytes; |
| if (!js_AtomToPrintableString(cx, atom, &bytes)) |
| return; |
| js_ReportValueError2(cx, JSMSG_INVALID_TRAP_RESULT, JSDVG_IGNORE_STACK, v, |
| NullPtr(), bytes.ptr()); |
| } |
| |
| // This function is shared between getOwnPropertyNames, enumerate, and keys |
| static bool |
| ArrayToIdVector(JSContext *cx, HandleObject proxy, HandleObject target, HandleValue v, |
| AutoIdVector &props, unsigned flags, JSAtom *trapName_) |
| { |
| JS_ASSERT(v.isObject()); |
| RootedObject array(cx, &v.toObject()); |
| RootedAtom trapName(cx, trapName_); |
| |
| // steps g-h |
| uint32_t n; |
| if (!GetLengthProperty(cx, array, &n)) |
| return false; |
| |
| // steps i-k |
| for (uint32_t i = 0; i < n; ++i) { |
| // step i |
| RootedValue v(cx); |
| if (!JSObject::getElement(cx, array, array, i, &v)) |
| return false; |
| |
| // step ii |
| RootedId id(cx); |
| if (!ValueToId<CanGC>(cx, v, &id)) |
| return false; |
| |
| // step iii |
| for (uint32_t j = 0; j < i; ++j) { |
| if (props[j] == id) { |
| ReportInvalidTrapResult(cx, proxy, trapName); |
| return false; |
| } |
| } |
| |
| // step iv |
| bool isFixed; |
| if (!HasOwn(cx, target, id, &isFixed)) |
| return false; |
| |
| // step v |
| if (!target->isExtensible() && !isFixed) { |
| JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_REPORT_NEW); |
| return false; |
| } |
| |
| // step vi |
| if (!props.append(id)) |
| return false; |
| } |
| |
| // step l |
| AutoIdVector ownProps(cx); |
| if (!GetPropertyNames(cx, target, flags, &ownProps)) |
| return false; |
| |
| // step m |
| for (size_t i = 0; i < ownProps.length(); ++i) { |
| RootedId id(cx, ownProps[i]); |
| |
| bool found = false; |
| for (size_t j = 0; j < props.length(); ++j) { |
| if (props[j] == id) { |
| found = true; |
| break; |
| } |
| } |
| if (found) |
| continue; |
| |
| // step i |
| bool sealed; |
| if (!IsSealed(cx, target, id, &sealed)) |
| return false; |
| if (sealed) { |
| JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_SKIP_NC); |
| return false; |
| } |
| |
| // step ii |
| bool isFixed; |
| if (!HasOwn(cx, target, id, &isFixed)) |
| return false; |
| |
| // step iii |
| if (!target->isExtensible() && isFixed) { |
| JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_REPORT_E_AS_NE); |
| return false; |
| } |
| } |
| |
| // step n |
| return true; |
| } |
| |
| ScriptedDirectProxyHandler::ScriptedDirectProxyHandler() |
| : DirectProxyHandler(&sScriptedDirectProxyHandlerFamily) |
| { |
| } |
| |
| ScriptedDirectProxyHandler::~ScriptedDirectProxyHandler() |
| { |
| } |
| |
| bool |
| ScriptedDirectProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy) |
| { |
| // step a |
| RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); |
| |
| // step b |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| |
| // step c |
| RootedValue trap(cx); |
| if (!JSObject::getProperty(cx, handler, handler, cx->names().preventExtensions, &trap)) |
| return false; |
| |
| // step d |
| if (trap.isUndefined()) |
| return DirectProxyHandler::preventExtensions(cx, proxy); |
| |
| // step e |
| Value argv[] = { |
| ObjectValue(*target) |
| }; |
| RootedValue trapResult(cx); |
| if (!Invoke(cx, ObjectValue(*handler), trap, 1, argv, trapResult.address())) |
| return false; |
| |
| // step f |
| bool success = ToBoolean(trapResult); |
| if (success) { |
| // step g |
| if (target->isExtensible()) { |
| JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_REPORT_AS_NON_EXTENSIBLE); |
| return false; |
| } |
| return true; |
| } |
| |
| // step h |
| JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CHANGE_EXTENSIBILITY); |
| return false; |
| } |
| |
| // FIXME: Move to Proxy::getPropertyDescriptor once ScriptedIndirectProxy is removed |
| bool |
| ScriptedDirectProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, |
| PropertyDescriptor *desc, unsigned flags) |
| { |
| JS_CHECK_RECURSION(cx, return false); |
| |
| if (!GetOwnPropertyDescriptor(cx, proxy, id, desc)) |
| return false; |
| if (desc->obj) |
| return true; |
| RootedObject proto(cx); |
| if (!JSObject::getProto(cx, proxy, &proto)) |
| return false; |
| if (!proto) { |
| JS_ASSERT(!desc->obj); |
| return true; |
| } |
| return JS_GetPropertyDescriptorById(cx, proto, id, 0, desc); |
| } |
| |
| bool |
| ScriptedDirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, |
| PropertyDescriptor *desc, unsigned flags) |
| { |
| // step 1 |
| RootedValue v(cx); |
| if (!TrapGetOwnProperty(cx, proxy, id, &v)) |
| return false; |
| |
| // step 2 |
| if (v.isUndefined()) { |
| desc->obj = NULL; |
| return true; |
| } |
| |
| // steps 3-4 |
| return ParsePropertyDescriptorObject(cx, proxy, v, desc, true); |
| } |
| |
| bool |
| ScriptedDirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id, |
| PropertyDescriptor *desc) |
| { |
| // step 1 |
| AutoPropDescArrayRooter descs(cx); |
| PropDesc *d = descs.append(); |
| d->initFromPropertyDescriptor(*desc); |
| RootedValue v(cx); |
| if (!FromGenericPropertyDescriptor(cx, d, &v)) |
| return false; |
| |
| // step 2 |
| return TrapDefineOwnProperty(cx, proxy, id, &v); |
| } |
| |
| bool |
| ScriptedDirectProxyHandler::getOwnPropertyNames(JSContext *cx, HandleObject proxy, |
| AutoIdVector &props) |
| { |
| // step a |
| RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); |
| |
| // step b |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| |
| // step c |
| RootedValue trap(cx); |
| if (!JSObject::getProperty(cx, handler, handler, cx->names().getOwnPropertyNames, &trap)) |
| return false; |
| |
| // step d |
| if (trap.isUndefined()) |
| return DirectProxyHandler::getOwnPropertyNames(cx, proxy, props); |
| |
| // step e |
| Value argv[] = { |
| ObjectValue(*target) |
| }; |
| RootedValue trapResult(cx); |
| if (!Invoke(cx, ObjectValue(*handler), trap, 1, argv, trapResult.address())) |
| return false; |
| |
| // step f |
| if (trapResult.isPrimitive()) { |
| ReportInvalidTrapResult(cx, proxy, cx->names().getOwnPropertyNames); |
| return false; |
| } |
| |
| // steps g to n are shared |
| return ArrayToIdVector(cx, proxy, target, trapResult, props, JSITER_OWNONLY | JSITER_HIDDEN, |
| cx->names().getOwnPropertyNames); |
| } |
| |
| // Proxy.[[Delete]](P, Throw) |
| bool |
| ScriptedDirectProxyHandler::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) |
| { |
| // step 1 |
| RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); |
| |
| // step 2 |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| |
| // step 3 |
| RootedValue trap(cx); |
| if (!JSObject::getProperty(cx, handler, handler, cx->names().deleteProperty, &trap)) |
| return false; |
| |
| // step 4 |
| if (trap.isUndefined()) |
| return DirectProxyHandler::delete_(cx, proxy, id, bp); |
| |
| // step 5 |
| RootedValue value(cx); |
| if (!IdToValue(cx, id, &value)) |
| return false; |
| Value argv[] = { |
| ObjectValue(*target), |
| value |
| }; |
| RootedValue trapResult(cx); |
| if (!Invoke(cx, ObjectValue(*handler), trap, 2, argv, trapResult.address())) |
| return false; |
| |
| // step 6-7 |
| if (ToBoolean(trapResult)) { |
| bool sealed; |
| if (!IsSealed(cx, target, id, &sealed)) |
| return false; |
| if (sealed) { |
| RootedValue v(cx, IdToValue(id)); |
| js_ReportValueError(cx, JSMSG_CANT_DELETE, JSDVG_IGNORE_STACK, v, NullPtr()); |
| return false; |
| } |
| |
| *bp = true; |
| return true; |
| } |
| |
| // step 8 |
| // FIXME: API does not include a Throw parameter |
| *bp = false; |
| return true; |
| } |
| |
| // 12.6.4 The for-in Statement, step 6 |
| bool |
| ScriptedDirectProxyHandler::enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) |
| { |
| // step a |
| RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); |
| |
| // step b |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| |
| // step c |
| RootedValue trap(cx); |
| if (!JSObject::getProperty(cx, handler, handler, cx->names().enumerate, &trap)) |
| return false; |
| |
| // step d |
| if (trap.isUndefined()) |
| return DirectProxyHandler::enumerate(cx, proxy, props); |
| |
| // step e |
| Value argv[] = { |
| ObjectOrNullValue(target) |
| }; |
| RootedValue trapResult(cx); |
| if (!Invoke(cx, ObjectValue(*handler), trap, 1, argv, trapResult.address())) |
| return false; |
| |
| // step f |
| if (trapResult.isPrimitive()) { |
| JSAutoByteString bytes; |
| if (!js_AtomToPrintableString(cx, cx->names().enumerate, &bytes)) |
| return false; |
| RootedValue v(cx, ObjectOrNullValue(proxy)); |
| js_ReportValueError2(cx, JSMSG_INVALID_TRAP_RESULT, JSDVG_SEARCH_STACK, |
| v, NullPtr(), bytes.ptr()); |
| return false; |
| } |
| |
| // steps g-m are shared |
| // FIXME: the trap should return an iterator object, see bug 783826 |
| return ArrayToIdVector(cx, proxy, target, trapResult, props, 0, cx->names().enumerate); |
| } |
| |
| // Proxy.[[HasProperty]](P) |
| bool |
| ScriptedDirectProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) |
| { |
| // step 1 |
| RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); |
| |
| // step 2 |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| |
| // step 3 |
| RootedValue trap(cx); |
| if (!JSObject::getProperty(cx, handler, handler, cx->names().has, &trap)) |
| return false; |
| |
| // step 4 |
| if (trap.isUndefined()) |
| return DirectProxyHandler::has(cx, proxy, id, bp); |
| |
| // step 5 |
| RootedValue value(cx); |
| if (!IdToValue(cx, id, &value)) |
| return false; |
| Value argv[] = { |
| ObjectOrNullValue(target), |
| value |
| }; |
| RootedValue trapResult(cx); |
| if (!Invoke(cx, ObjectValue(*handler), trap, 2, argv, trapResult.address())) |
| return false; |
| |
| // step 6 |
| bool success = ToBoolean(trapResult);; |
| |
| // step 7 |
| if (!success) { |
| bool sealed; |
| if (!IsSealed(cx, target, id, &sealed)) |
| return false; |
| if (sealed) { |
| JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_REPORT_NC_AS_NE); |
| return false; |
| } |
| |
| if (!target->isExtensible()) { |
| bool isFixed; |
| if (!HasOwn(cx, target, id, &isFixed)) |
| return false; |
| if (isFixed) { |
| JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_REPORT_E_AS_NE); |
| return false; |
| } |
| } |
| } |
| |
| // step 8 |
| *bp = success; |
| return true; |
| } |
| |
| // Proxy.[[HasOwnProperty]](P) |
| bool |
| ScriptedDirectProxyHandler::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) |
| { |
| // step 1 |
| RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); |
| |
| // step 2 |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| |
| // step 3 |
| RootedValue trap(cx); |
| if (!JSObject::getProperty(cx, handler, handler, cx->names().hasOwn, &trap)) |
| return false; |
| |
| // step 4 |
| if (trap.isUndefined()) |
| return DirectProxyHandler::hasOwn(cx, proxy, id, bp); |
| |
| // step 5 |
| RootedValue value(cx); |
| if (!IdToValue(cx, id, &value)) |
| return false; |
| Value argv[] = { |
| ObjectOrNullValue(target), |
| value |
| }; |
| RootedValue trapResult(cx); |
| if (!Invoke(cx, ObjectValue(*handler), trap, 2, argv, trapResult.address())) |
| return false; |
| |
| // step 6 |
| bool success = ToBoolean(trapResult); |
| |
| // steps 7-8 |
| if (!success) { |
| bool sealed; |
| if (!IsSealed(cx, target, id, &sealed)) |
| return false; |
| if (sealed) { |
| JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_REPORT_NC_AS_NE); |
| return false; |
| } |
| |
| if (!target->isExtensible()) { |
| bool isFixed; |
| if (!HasOwn(cx, target, id, &isFixed)) |
| return false; |
| if (isFixed) { |
| JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_REPORT_E_AS_NE); |
| return false; |
| } |
| } |
| } else if (!target->isExtensible()) { |
| bool isFixed; |
| if (!HasOwn(cx, target, id, &isFixed)) |
| return false; |
| if (!isFixed) { |
| JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_REPORT_NEW); |
| return false; |
| } |
| } |
| |
| // step 9 |
| *bp = !!success; |
| return true; |
| } |
| |
| // Proxy.[[GetP]](P, Receiver) |
| bool |
| ScriptedDirectProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver, |
| HandleId id, MutableHandleValue vp) |
| { |
| // step 1 |
| RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); |
| |
| // step 2 |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| |
| // step 3 |
| RootedValue trap(cx); |
| if (!JSObject::getProperty(cx, handler, handler, cx->names().get, &trap)) |
| return false; |
| |
| // step 4 |
| if (trap.isUndefined()) |
| return DirectProxyHandler::get(cx, proxy, receiver, id, vp); |
| |
| // step 5 |
| RootedValue value(cx); |
| if (!IdToValue(cx, id, &value)) |
| return false; |
| Value argv[] = { |
| ObjectOrNullValue(target), |
| value, |
| ObjectOrNullValue(receiver) |
| }; |
| RootedValue trapResult(cx); |
| if (!Invoke(cx, ObjectValue(*handler), trap, 3, argv, trapResult.address())) |
| return false; |
| |
| // step 6 |
| AutoPropertyDescriptorRooter desc(cx); |
| if (!GetOwnPropertyDescriptor(cx, target, id, &desc)) |
| return false; |
| |
| // step 7 |
| if (desc.obj) { |
| if (IsDataDescriptor(desc) && |
| (desc.attrs & JSPROP_PERMANENT) && |
| (desc.attrs & JSPROP_READONLY)) |
| { |
| bool same; |
| if (!SameValue(cx, vp, desc.value, &same)) |
| return false; |
| if (!same) { |
| JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MUST_REPORT_SAME_VALUE); |
| return false; |
| } |
| } |
| |
| if (IsAccessorDescriptor(desc) && |
| (desc.attrs & JSPROP_PERMANENT) && |
| !(desc.attrs & JSPROP_GETTER)) |
| { |
| if (!trapResult.isUndefined()) { |
| JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MUST_REPORT_UNDEFINED); |
| return false; |
| } |
| } |
| } |
| |
| // step 8 |
| vp.set(trapResult); |
| return true; |
| } |
| |
| // Proxy.[[SetP]](P, V, Receiver) |
| bool |
| ScriptedDirectProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver, |
| HandleId id, bool strict, MutableHandleValue vp) |
| { |
| // step 1 |
| RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); |
| |
| // step 2 |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| |
| // step 3 |
| RootedValue trap(cx); |
| if (!JSObject::getProperty(cx, handler, handler, cx->names().set, &trap)) |
| return false; |
| |
| // step 4 |
| if (trap.isUndefined()) |
| return DirectProxyHandler::set(cx, proxy, receiver, id, strict, vp); |
| |
| // step 5 |
| RootedValue value(cx); |
| if (!IdToValue(cx, id, &value)) |
| return false; |
| Value argv[] = { |
| ObjectOrNullValue(target), |
| value, |
| vp.get(), |
| ObjectValue(*receiver) |
| }; |
| RootedValue trapResult(cx); |
| if (!Invoke(cx, ObjectValue(*handler), trap, 4, argv, trapResult.address())) |
| return false; |
| |
| // step 6 |
| bool success = ToBoolean(trapResult); |
| |
| // step 7 |
| if (success) { |
| AutoPropertyDescriptorRooter desc(cx); |
| if (!GetOwnPropertyDescriptor(cx, target, id, &desc)) |
| return false; |
| |
| if (desc.obj) { |
| if (IsDataDescriptor(desc) && (desc.attrs & JSPROP_PERMANENT) && |
| (desc.attrs & JSPROP_READONLY)) { |
| bool same; |
| if (!SameValue(cx, vp, desc.value, &same)) |
| return false; |
| if (!same) { |
| JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_SET_NW_NC); |
| return false; |
| } |
| } |
| |
| if (IsAccessorDescriptor(desc) && (desc.attrs & JSPROP_PERMANENT)) { |
| if (!(desc.attrs & JSPROP_SETTER)) { |
| JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_SET_WO_SETTER); |
| return false; |
| } |
| } |
| } |
| } |
| |
| // step 8 |
| vp.set(BooleanValue(success)); |
| return true; |
| } |
| |
| // 15.2.3.14 Object.keys (O), step 2 |
| bool |
| ScriptedDirectProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) |
| { |
| // step a |
| RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); |
| |
| // step b |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| |
| // step c |
| RootedValue trap(cx); |
| if (!JSObject::getProperty(cx, handler, handler, cx->names().keys, &trap)) |
| return false; |
| |
| // step d |
| if (trap.isUndefined()) |
| return DirectProxyHandler::keys(cx, proxy, props); |
| |
| // step e |
| Value argv[] = { |
| ObjectOrNullValue(target) |
| }; |
| RootedValue trapResult(cx); |
| if (!Invoke(cx, ObjectValue(*handler), trap, 1, argv, trapResult.address())) |
| return false; |
| |
| // step f |
| if (trapResult.isPrimitive()) { |
| JSAutoByteString bytes; |
| if (!js_AtomToPrintableString(cx, cx->names().keys, &bytes)) |
| return false; |
| RootedValue v(cx, ObjectOrNullValue(proxy)); |
| js_ReportValueError2(cx, JSMSG_INVALID_TRAP_RESULT, JSDVG_IGNORE_STACK, |
| v, NullPtr(), bytes.ptr()); |
| return false; |
| } |
| |
| // steps g-n are shared |
| return ArrayToIdVector(cx, proxy, target, trapResult, props, JSITER_OWNONLY, cx->names().keys); |
| } |
| |
| bool |
| ScriptedDirectProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags, |
| MutableHandleValue vp) |
| { |
| // FIXME: Provide a proper implementation for this trap, see bug 787004 |
| return DirectProxyHandler::iterate(cx, proxy, flags, vp); |
| } |
| |
| bool |
| ScriptedDirectProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args) |
| { |
| // step 1 |
| RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); |
| |
| // step 2 |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| |
| /* |
| * NB: Remember to throw a TypeError here if we change NewProxyObject so that this trap can get |
| * called for non-callable objects |
| */ |
| |
| // step 3 |
| RootedObject argsArray(cx, NewDenseCopiedArray(cx, args.length(), args.array())); |
| if (!argsArray) |
| return false; |
| |
| // step 4 |
| RootedValue trap(cx); |
| if (!JSObject::getProperty(cx, handler, handler, cx->names().apply, &trap)) |
| return false; |
| |
| // step 5 |
| if (trap.isUndefined()) |
| return DirectProxyHandler::call(cx, proxy, args); |
| |
| // step 6 |
| Value argv[] = { |
| ObjectValue(*target), |
| args.thisv(), |
| ObjectValue(*argsArray) |
| }; |
| RootedValue thisValue(cx, ObjectValue(*handler)); |
| return Invoke(cx, thisValue, trap, ArrayLength(argv), argv, args.rval().address()); |
| } |
| |
| bool |
| ScriptedDirectProxyHandler::construct(JSContext *cx, HandleObject proxy, const CallArgs &args) |
| { |
| // step 1 |
| RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); |
| |
| // step 2 |
| RootedObject target(cx, GetProxyTargetObject(proxy)); |
| |
| /* |
| * NB: Remember to throw a TypeError here if we change NewProxyObject so that this trap can get |
| * called for non-callable objects |
| */ |
| |
| // step 3 |
| RootedObject argsArray(cx, NewDenseCopiedArray(cx, args.length(), args.array())); |
| if (!argsArray) |
| return false; |
| |
| // step 4 |
| RootedValue trap(cx); |
| if (!JSObject::getProperty(cx, handler, handler, cx->names().construct, &trap)) |
| return false; |
| |
| // step 5 |
| if (trap.isUndefined()) |
| return DirectProxyHandler::construct(cx, proxy, args); |
| |
| // step 6 |
| Value constructArgv[] = { |
| ObjectValue(*target), |
| ObjectValue(*argsArray) |
| }; |
| RootedValue thisValue(cx, ObjectValue(*handler)); |
| return Invoke(cx, thisValue, trap, ArrayLength(constructArgv), constructArgv, |
| args.rval().address()); |
| } |
| |
| ScriptedDirectProxyHandler ScriptedDirectProxyHandler::singleton; |
| |
| #define INVOKE_ON_PROTOTYPE(cx, handler, proxy, protoCall) \ |
| JS_BEGIN_MACRO \ |
| RootedObject proto(cx); \ |
| if (!handler->getPrototypeOf(cx, proxy, &proto)) \ |
| return false; \ |
| if (!proto) \ |
| return true; \ |
| assertSameCompartment(cx, proxy, proto); \ |
| return protoCall; \ |
| JS_END_MACRO \ |
| |
| bool |
| Proxy::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, |
| PropertyDescriptor *desc, unsigned flags) |
| { |
| JS_CHECK_RECURSION(cx, return false); |
| BaseProxyHandler *handler = GetProxyHandler(proxy); |
| desc->obj = NULL; // default result if we refuse to perform this action |
| AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true); |
| if (!policy.allowed()) |
| return policy.returnValue(); |
| if (!handler->hasPrototype()) |
| return handler->getPropertyDescriptor(cx, proxy, id, desc, flags); |
| if (!handler->getOwnPropertyDescriptor(cx, proxy, id, desc, flags)) |
| return false; |
| if (desc->obj) |
| return true; |
| INVOKE_ON_PROTOTYPE(cx, handler, proxy, |
| JS_GetPropertyDescriptorById(cx, proto, id, 0, desc)); |
| } |
| |
| bool |
| Proxy::getPropertyDescriptor(JSContext *cx, HandleObject proxy, unsigned flags, |
| HandleId id, MutableHandleValue vp) |
| { |
| JS_CHECK_RECURSION(cx, return false); |
| |
| AutoPropertyDescriptorRooter desc(cx); |
| if (!Proxy::getPropertyDescriptor(cx, proxy, id, &desc, flags)) |
| return false; |
| return NewPropertyDescriptorObject(cx, &desc, vp); |
| } |
| |
| bool |
| Proxy::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, |
| PropertyDescriptor *desc, unsigned flags) |
| { |
| JS_CHECK_RECURSION(cx, return false); |
| |
| BaseProxyHandler *handler = GetProxyHandler(proxy); |
| desc->obj = NULL; // default result if we refuse to perform this action |
| AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true); |
| if (!policy.allowed()) |
| return policy.returnValue(); |
| return handler->getOwnPropertyDescriptor(cx, proxy, id, desc, flags); |
| } |
| |
| bool |
| Proxy::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, unsigned flags, HandleId id, |
| MutableHandleValue vp) |
| { |
| JS_CHECK_RECURSION(cx, return false); |
| |
| AutoPropertyDescriptorRooter desc(cx); |
| if (!Proxy::getOwnPropertyDescriptor(cx, proxy, id, &desc, flags)) |
| return false; |
| return NewPropertyDescriptorObject(cx, &desc, vp); |
| } |
| |
| bool |
| Proxy::defineProperty(JSContext *cx, HandleObject proxy, HandleId id, PropertyDescriptor *desc) |
| { |
| JS_CHECK_RECURSION(cx, return false); |
| BaseProxyHandler *handler = GetProxyHandler(proxy); |
| AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true); |
| if (!policy.allowed()) |
| return policy.returnValue(); |
| return GetProxyHandler(proxy)->defineProperty(cx, proxy, id, desc); |
| } |
| |
| bool |
| Proxy::defineProperty(JSContext *cx, HandleObject proxy, HandleId id, HandleValue v) |
| { |
| JS_CHECK_RECURSION(cx, return false); |
| AutoPropertyDescriptorRooter desc(cx); |
| return ParsePropertyDescriptorObject(cx, proxy, v, &desc) && |
| Proxy::defineProperty(cx, proxy, id, &desc); |
| } |
| |
| bool |
| Proxy::getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props) |
| { |
| JS_CHECK_RECURSION(cx, return false); |
| BaseProxyHandler *handler = GetProxyHandler(proxy); |
| AutoEnterPolicy policy(cx, handler, proxy, JS::JSID_VOIDHANDLE, BaseProxyHandler::GET, true); |
| if (!policy.allowed()) |
| return policy.returnValue(); |
| return GetProxyHandler(proxy)->getOwnPropertyNames(cx, proxy, props); |
| } |
| |
| bool |
| Proxy::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) |
| { |
| JS_CHECK_RECURSION(cx, return false); |
| BaseProxyHandler *handler = GetProxyHandler(proxy); |
| *bp = true; // default result if we refuse to perform this action |
| AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true); |
| if (!policy.allowed()) |
| return policy.returnValue(); |
| return GetProxyHandler(proxy)->delete_(cx, proxy, id, bp); |
| } |
| |
| JS_FRIEND_API(bool) |
| js::AppendUnique(JSContext *cx, AutoIdVector &base, AutoIdVector &others) |
| { |
| AutoIdVector uniqueOthers(cx); |
| if (!uniqueOthers.reserve(others.length())) |
| return false; |
| for (size_t i = 0; i < others.length(); ++i) { |
| bool unique = true; |
| for (size_t j = 0; j < base.length(); ++j) { |
| if (others[i] == base[j]) { |
| unique = false; |
| break; |
| } |
| } |
| if (unique) |
| uniqueOthers.append(others[i]); |
| } |
| return base.append(uniqueOthers); |
| } |
| |
| bool |
| Proxy::enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) |
| { |
| JS_CHECK_RECURSION(cx, return false); |
| BaseProxyHandler *handler = GetProxyHandler(proxy); |
| AutoEnterPolicy policy(cx, handler, proxy, JS::JSID_VOIDHANDLE, BaseProxyHandler::GET, true); |
| if (!policy.allowed()) |
| return policy.returnValue(); |
| if (!handler->hasPrototype()) |
| return GetProxyHandler(proxy)->enumerate(cx, proxy, props); |
| if (!handler->keys(cx, proxy, props)) |
| return false; |
| AutoIdVector protoProps(cx); |
| INVOKE_ON_PROTOTYPE(cx, handler, proxy, |
| GetPropertyNames(cx, proto, 0, &protoProps) && |
| AppendUnique(cx, props, protoProps)); |
| } |
| |
| bool |
| Proxy::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) |
| { |
| JS_CHECK_RECURSION(cx, return false); |
| BaseProxyHandler *handler = GetProxyHandler(proxy); |
| *bp = false; // default result if we refuse to perform this action |
| AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true); |
| if (!policy.allowed()) |
| return policy.returnValue(); |
| if (!handler->hasPrototype()) |
| return handler->has(cx, proxy, id, bp); |
| if (!handler->hasOwn(cx, proxy, id, bp)) |
| return false; |
| if (*bp) |
| return true; |
| JSBool Bp; |
| INVOKE_ON_PROTOTYPE(cx, handler, proxy, |
| JS_HasPropertyById(cx, proto, id, &Bp) && |
| ((*bp = Bp) || true)); |
| } |
| |
| bool |
| Proxy::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) |
| { |
| JS_CHECK_RECURSION(cx, return false); |
| BaseProxyHandler *handler = GetProxyHandler(proxy); |
| *bp = false; // default result if we refuse to perform this action |
| AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true); |
| if (!policy.allowed()) |
| return policy.returnValue(); |
| return handler->hasOwn(cx, proxy, id, bp); |
| } |
| |
| bool |
| Proxy::get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, |
| MutableHandleValue vp) |
| { |
| JS_CHECK_RECURSION(cx, return false); |
| BaseProxyHandler *handler = GetProxyHandler(proxy); |
| vp.setUndefined(); // default result if we refuse to perform this action |
| AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true); |
| if (!policy.allowed()) |
| return policy.returnValue(); |
| bool own; |
| if (!handler->hasPrototype()) { |
| own = true; |
| } else { |
| if (!handler->hasOwn(cx, proxy, id, &own)) |
| return false; |
| } |
| if (own) |
| return handler->get(cx, proxy, receiver, id, vp); |
| INVOKE_ON_PROTOTYPE(cx, handler, proxy, JSObject::getGeneric(cx, proto, receiver, id, vp)); |
| } |
| |
| bool |
| Proxy::getElementIfPresent(JSContext *cx, HandleObject proxy, HandleObject receiver, uint32_t index, |
| MutableHandleValue vp, bool *present) |
| { |
| JS_CHECK_RECURSION(cx, return false); |
| |
| RootedId id(cx); |
| if (!IndexToId(cx, index, &id)) |
| return false; |
| |
| BaseProxyHandler *handler = GetProxyHandler(proxy); |
| AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true); |
| if (!policy.allowed()) |
| return policy.returnValue(); |
| |
| if (!handler->hasPrototype()) { |
| return handler->getElementIfPresent(cx, proxy, receiver, index, |
| vp, present); |
| } |
| |
| bool hasOwn; |
| if (!handler->hasOwn(cx, proxy, id, &hasOwn)) |
| return false; |
| |
| if (hasOwn) { |
| *present = true; |
| return GetProxyHandler(proxy)->get(cx, proxy, receiver, id, vp); |
| } |
| |
| *present = false; |
| INVOKE_ON_PROTOTYPE(cx, handler, proxy, |
| JSObject::getElementIfPresent(cx, proto, receiver, index, vp, present)); |
| } |
| |
| bool |
| Proxy::set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, bool strict, |
| MutableHandleValue vp) |
| { |
| JS_CHECK_RECURSION(cx, return false); |
| BaseProxyHandler *handler = GetProxyHandler(proxy); |
| AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true); |
| if (!policy.allowed()) |
| return policy.returnValue(); |
| if (handler->hasPrototype()) { |
| // If we're using a prototype, we still want to use the proxy trap unless |
| // we have a non-own property with a setter. |
| bool hasOwn; |
| if (!handler->hasOwn(cx, proxy, id, &hasOwn)) |
| return false; |
| if (!hasOwn) { |
| RootedObject proto(cx); |
| if (!handler->getPrototypeOf(cx, proxy, &proto)) |
| return false; |
| if (proto) { |
| AutoPropertyDescriptorRooter desc(cx); |
| if (!JS_GetPropertyDescriptorById(cx, proto, id, 0, &desc)) |
| return false; |
| if (desc.obj && desc.setter) |
| return JSObject::setGeneric(cx, proto, receiver, id, vp, strict); |
| } |
| } |
| } |
| return handler->set(cx, proxy, receiver, id, strict, vp); |
| } |
| |
| bool |
| Proxy::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props) |
| { |
| JS_CHECK_RECURSION(cx, return false); |
| BaseProxyHandler *handler = GetProxyHandler(proxy); |
| AutoEnterPolicy policy(cx, handler, proxy, JS::JSID_VOIDHANDLE, BaseProxyHandler::GET, true); |
| if (!policy.allowed()) |
| return policy.returnValue(); |
| return handler->keys(cx, proxy, props); |
| } |
| |
| bool |
| Proxy::iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleValue vp) |
| { |
| JS_CHECK_RECURSION(cx, return false); |
| BaseProxyHandler *handler = GetProxyHandler(proxy); |
| vp.setUndefined(); // default result if we refuse to perform this action |
| if (!handler->hasPrototype()) { |
| AutoEnterPolicy policy(cx, handler, proxy, JS::JSID_VOIDHANDLE, |
| BaseProxyHandler::GET, true); |
| // If the policy denies access but wants us to return true, we need |
| // to hand a valid (empty) iterator object to the caller. |
| if (!policy.allowed()) { |
| AutoIdVector props(cx); |
| return policy.returnValue() && |
| EnumeratedIdVectorToIterator(cx, proxy, flags, props, vp); |
| } |
| return handler->iterate(cx, proxy, flags, vp); |
| } |
| AutoIdVector props(cx); |
| // The other Proxy::foo methods do the prototype-aware work for us here. |
| if ((flags & JSITER_OWNONLY) |
| ? !Proxy::keys(cx, proxy, props) |
| : !Proxy::enumerate(cx, proxy, props)) { |
| return false; |
| } |
| return EnumeratedIdVectorToIterator(cx, proxy, flags, props, vp); |
| } |
| |
| bool |
| Proxy::isExtensible(JSObject *proxy) |
| { |
| return GetProxyHandler(proxy)->isExtensible(proxy); |
| } |
| |
| bool |
| Proxy::preventExtensions(JSContext *cx, HandleObject proxy) |
| { |
| JS_CHECK_RECURSION(cx, return false); |
| BaseProxyHandler *handler = GetProxyHandler(proxy); |
| return handler->preventExtensions(cx, proxy); |
| } |
| |
| bool |
| Proxy::call(JSContext *cx, HandleObject proxy, const CallArgs &args) |
| { |
| JS_CHECK_RECURSION(cx, return false); |
| BaseProxyHandler *handler = GetProxyHandler(proxy); |
| |
| // Because vp[0] is JS_CALLEE on the way in and JS_RVAL on the way out, we |
| // can only set our default value once we're sure that we're not calling the |
| // trap. |
| AutoEnterPolicy policy(cx, handler, proxy, JS::JSID_VOIDHANDLE, |
| BaseProxyHandler::CALL, true); |
| if (!policy.allowed()) { |
| args.rval().setUndefined(); |
| return policy.returnValue(); |
| } |
| |
| return handler->call(cx, proxy, args); |
| } |
| |
| bool |
| Proxy::construct(JSContext *cx, HandleObject proxy, const CallArgs &args) |
| { |
| JS_CHECK_RECURSION(cx, return false); |
| BaseProxyHandler *handler = GetProxyHandler(proxy); |
| |
| // Because vp[0] is JS_CALLEE on the way in and JS_RVAL on the way out, we |
| // can only set our default value once we're sure that we're not calling the |
| // trap. |
| AutoEnterPolicy policy(cx, handler, proxy, JS::JSID_VOIDHANDLE, |
| BaseProxyHandler::CALL, true); |
| if (!policy.allowed()) { |
| args.rval().setUndefined(); |
| return policy.returnValue(); |
| } |
| |
| return handler->construct(cx, proxy, args); |
| } |
| |
| bool |
| Proxy::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args) |
| { |
| JS_CHECK_RECURSION(cx, return false); |
| RootedObject proxy(cx, &args.thisv().toObject()); |
| // Note - we don't enter a policy here because our security architecture |
| // guards against nativeCall by overriding the trap itself in the right |
| // circumstances. |
| return GetProxyHandler(proxy)->nativeCall(cx, test, impl, args); |
| } |
| |
| bool |
| Proxy::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp) |
| { |
| JS_CHECK_RECURSION(cx, return false); |
| BaseProxyHandler *handler = GetProxyHandler(proxy); |
| *bp = false; // default result if we refuse to perform this action |
| AutoEnterPolicy policy(cx, handler, proxy, JS::JSID_VOIDHANDLE, BaseProxyHandler::GET, true); |
| if (!policy.allowed()) |
| return policy.returnValue(); |
| return GetProxyHandler(proxy)->hasInstance(cx, proxy, v, bp); |
| } |
| |
| bool |
| Proxy::objectClassIs(HandleObject proxy, ESClassValue classValue, JSContext *cx) |
| { |
| JS_CHECK_RECURSION(cx, return false); |
| return GetProxyHandler(proxy)->objectClassIs(proxy, classValue, cx); |
| } |
| |
| const char * |
| Proxy::className(JSContext *cx, HandleObject proxy) |
| { |
| // Check for unbounded recursion, but don't signal an error; className |
| // needs to be infallible. |
| int stackDummy; |
| if (!JS_CHECK_STACK_SIZE(cx->mainThread().nativeStackLimit, &stackDummy)) |
| return "too much recursion"; |
| |
| BaseProxyHandler *handler = GetProxyHandler(proxy); |
| AutoEnterPolicy policy(cx, handler, proxy, JS::JSID_VOIDHANDLE, |
| BaseProxyHandler::GET, /* mayThrow = */ false); |
| // Do the safe thing if the policy rejects. |
| if (!policy.allowed()) { |
| return handler->BaseProxyHandler::className(cx, proxy); |
| } |
| return handler->className(cx, proxy); |
| } |
| |
| JSString * |
| Proxy::fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) |
| { |
| JS_CHECK_RECURSION(cx, return NULL); |
| BaseProxyHandler *handler = GetProxyHandler(proxy); |
| AutoEnterPolicy policy(cx, handler, proxy, JS::JSID_VOIDHANDLE, |
| BaseProxyHandler::GET, /* mayThrow = */ false); |
| // Do the safe thing if the policy rejects. |
| if (!policy.allowed()) { |
| if (proxy->isCallable()) |
| return JS_NewStringCopyZ(cx, "function () {\n [native code]\n}"); |
| ReportIsNotFunction(cx, ObjectValue(*proxy)); |
| return NULL; |
| } |
| return handler->fun_toString(cx, proxy, indent); |
| } |
| |
| bool |
| Proxy::regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) |
| { |
| JS_CHECK_RECURSION(cx, return false); |
| return GetProxyHandler(proxy)->regexp_toShared(cx, proxy, g); |
| } |
| |
| bool |
| Proxy::defaultValue(JSContext *cx, HandleObject proxy, JSType hint |