From 70d3c9c7071053a8c497ef4d4fdc24bf95ec9f10 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Sat, 22 Feb 2014 07:30:49 -0500 Subject: [PATCH] Add proxy handler deriving from DirectWrapper and related methods, along with extended js::Class FFI. --- glue.rs | 60 +++++++------ js.rc | 75 ++++++++++++++++- jsapi.rs | 2 +- jsglue.cpp | 243 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 346 insertions(+), 34 deletions(-) diff --git a/glue.rs b/glue.rs index aa6709ada..f05a98f12 100644 --- a/glue.rs +++ b/glue.rs @@ -13,35 +13,35 @@ use jsval::JSVal; type c_bool = libc::c_int; pub struct ProxyTraps { - getPropertyDescriptor: extern "C" fn(*JSContext, *JSObject, jsid, c_bool, *mut JSPropertyDescriptor) -> c_bool, - getOwnPropertyDescriptor: extern "C" fn(*JSContext, *JSObject, jsid, JSBool, *mut JSPropertyDescriptor) -> JSBool, - defineProperty: extern "C" fn(*JSContext, *JSObject, jsid, *JSPropertyDescriptor) -> JSBool, - getOwnPropertyNames: *u8, - delete_: *u8, - enumerate: *u8, - - has: *u8, - hasOwn: extern "C" fn(*JSContext, *JSObject, jsid, *mut JSBool) -> JSBool, - get: extern "C" fn(*JSContext, *JSObject, *JSObject, jsid, *mut JSVal) -> JSBool, - set: *u8, - keys: *u8, - iterate: *u8, - - call: *u8, - construct: *u8, - nativeCall: *u8, - hasInstance: *u8, - typeOf: *u8, - objectClassIs: *u8, - obj_toString: extern "C" fn(*JSContext, *JSObject) -> *JSString, - fun_toString: *u8, + getPropertyDescriptor: Option c_bool>, + getOwnPropertyDescriptor: Option JSBool>, + defineProperty: Option JSBool>, + getOwnPropertyNames: *u8, //XXX need a representation for AutoIdVector& + delete_: Option JSBool>, + enumerate: *u8, //XXX need a representation for AutoIdVector& + + has: Option JSBool>, + hasOwn: Option JSBool>, + get: Option JSBool>, + set: Option JSBool>, + keys: *u8, //XXX need a representation for AutoIdVector& + iterate: Option JSBool>, + + call: Option JSBool>, + construct: Option JSBool>, + nativeCall: *u8, //XXX need a representation for IsAcceptableThis, NativeImpl, and CallArgs + hasInstance: Option JSBool>, + typeOf: Option uint>, //XXX JSType enum + objectClassIs: Option JSBool>, //XXX ESClassValue enum + obj_toString: Option *JSString>, + fun_toString: Option *JSString>, //regexp_toShared: *u8, - defaultValue: *u8, - iteratorNext: *u8, - finalize: extern "C" fn(*JSFreeOp, *JSObject), - getElementIfPresent: *u8, - getPrototypeOf: *u8, - trace: Option + defaultValue: Option JSBool>, //XXX JSType enum + iteratorNext: Option JSBool>, + finalize: Option, + getElementIfPresent: Option JSBool>, + getPrototypeOf: Option JSBool>, + trace: Option, } #[cfg(not(target_os = "android"))] @@ -71,9 +71,11 @@ pub fn SetFunctionNativeReserved(fun: *JSObject, which: libc::size_t, val: *JSVa pub fn GetFunctionNativeReserved(fun: *JSObject, which: libc::size_t) -> *JSVal; pub fn CreateProxyHandler(traps: *ProxyTraps, extra: *libc::c_void) -> *libc::c_void; +pub fn CreateWrapperProxyHandler(traps: *ProxyTraps) -> *libc::c_void; pub fn NewProxyObject(cx: *JSContext, handler: *libc::c_void, priv_: *JSVal, proto: *JSObject, parent: *JSObject, call: *JSObject, construct: *JSObject) -> *JSObject; +pub fn WrapperNew(cx: *JSContext, parent: *JSObject, handler: *libc::c_void) -> *JSObject; pub fn GetProxyExtra(obj: *JSObject, slot: c_uint) -> JSVal; pub fn GetProxyPrivate(obj: *JSObject) -> JSVal; @@ -105,4 +107,6 @@ pub fn GetProxyHandler(obj: *JSObject) -> *libc::c_void; pub fn InvokeGetOwnPropertyDescriptor(handler: *libc::c_void, cx: *JSContext, proxy: *JSObject, id: jsid, set: JSBool, desc: *mut JSPropertyDescriptor) -> JSBool; pub fn GetGlobalForObjectCrossCompartment(obj: *JSObject) -> *JSObject; pub fn ReportError(cx: *JSContext, error: *c_char); +pub fn IsWrapper(obj: *JSObject) -> JSBool; +pub fn UnwrapObject(obj: *JSObject, stopAtOuter: JSBool, flags: *libc::c_uint) -> *JSObject; } diff --git a/js.rc b/js.rc index e1dd53d9d..8b985c8a3 100644 --- a/js.rc +++ b/js.rc @@ -23,10 +23,11 @@ extern crate serialize; use std::cast; use std::ptr::null; use std::result::{Result, Ok, Err}; +use std::libc; use std::libc::c_uint; -use jsapi::{JSBool, JSContext, - JSObject, jsid, - JSProto_LIMIT}; +use jsapi::{JSBool, JSContext, JSPropertyOp, JSStrictPropertyOp, JSEnumerateOp, + JSObject, jsid, JSResolveOp, JSConvertOp, JSFinalizeOp, JSTraceOp, + JSProto_LIMIT, JSHandleObject, JSCheckAccessOp, JSNative, JSHasInstanceOp}; use jsapi::JS_ComputeThis; use std::libc::types::common::c99::uint32_t; use jsval::JSVal; @@ -154,3 +155,71 @@ pub unsafe fn JS_CALLEE(_cx: *JSContext, vp: *JSVal) -> JSVal { fn start(argc: int, argv: **u8) -> int { green::start(argc, argv, __test::main) } + +pub type JSObjectOp = extern "C" fn(*JSContext, JSHandleObject) -> *JSObject; + +pub struct Class { + name: *libc::c_char, + flags: uint32_t, + addProperty: Option, + delProperty: Option, + getProperty: Option, + setProperty: Option, + enumerate: Option, + resolve: Option, + convert: Option, + finalize: Option, + checkAccess: Option, + call: Option, + hasInstance: Option, + construct: Option, + trace: Option, + + ext: ClassExtension, + ops: ObjectOps, +} + +pub struct ClassExtension { + equality: *u8, + outerObject: Option, + innerObject: Option, + iteratorObject: *u8, + unused: *u8, + isWrappedNative: *u8, +} + +pub struct ObjectOps { + lookupGeneric: *u8, + lookupProperty: *u8, + lookupElement: *u8, + lookupSpecial: *u8, + defineGeneric: *u8, + defineProperty: *u8, + defineElement: *u8, + defineSpecial: *u8, + getGeneric: *u8, + getProperty: *u8, + getElement: *u8, + getElementIfPresent: *u8, + getSpecial: *u8, + setGeneric: *u8, + setProperty: *u8, + setElement: *u8, + setSpecial: *u8, + getGenericAttributes: *u8, + getPropertyAttributes: *u8, + getElementAttributes: *u8, + getSpecialAttributes: *u8, + setGenericAttributes: *u8, + setPropertyAttributes: *u8, + setElementAttributes: *u8, + setSpecialAttributes: *u8, + deleteProperty: *u8, + deleteElement: *u8, + deleteSpecial: *u8, + + enumerate: *u8, + typeOf: *u8, + thisObject: Option, + clear: *u8, +} diff --git a/jsapi.rs b/jsapi.rs index 8849d1fb7..79fc9d6c6 100644 --- a/jsapi.rs +++ b/jsapi.rs @@ -398,7 +398,7 @@ pub type JSWrapObjectCallback = *u8; pub type JSPreWrapCallback = *u8; -pub type JSSameCompartmentWrapObjectCallback = *u8; +pub type JSSameCompartmentWrapObjectCallback = extern "C" fn(*JSContext, *JSObject) -> *JSObject; pub type JSDestroyCompartmentCallback = *u8; diff --git a/jsglue.cpp b/jsglue.cpp index 79a8d61e6..4dbd3ed07 100644 --- a/jsglue.cpp +++ b/jsglue.cpp @@ -9,6 +9,7 @@ #include "jsfriendapi.h" #include "jsproxy.h" #include "jsclass.h" +#include "jswrapper.h" #include "assert.h" @@ -56,6 +57,219 @@ struct ProxyTraps { int HandlerFamily = js::JSSLOT_PROXY_EXTRA + 0 /*JSPROXYSLOT_EXPANDO*/; +class WrapperProxyHandler : public js::DirectWrapper +{ + ProxyTraps mTraps; + public: + WrapperProxyHandler(const ProxyTraps& aTraps) + : js::DirectWrapper(0), mTraps(aTraps) {} + + virtual bool isOuterWindow() { + return true; + } + + virtual bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, + bool set, JSPropertyDescriptor *desc) + { + return mTraps.getPropertyDescriptor ? + mTraps.getPropertyDescriptor(cx, proxy, id, set, desc) : + DirectWrapper::getPropertyDescriptor(cx, proxy, id, set, desc); + } + + virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, + jsid id, bool set, + JSPropertyDescriptor *desc) + { + return mTraps.getOwnPropertyDescriptor ? + mTraps.getOwnPropertyDescriptor(cx, proxy, id, set, desc) : + DirectWrapper::getOwnPropertyDescriptor(cx, proxy, id, set, desc); + } + + virtual bool defineProperty(JSContext *cx, JSObject *proxy, jsid id, + JSPropertyDescriptor *desc) + { + return mTraps.defineProperty ? + mTraps.defineProperty(cx, proxy, id, desc) : + DirectWrapper::defineProperty(cx, proxy, id, desc); + } + + virtual bool getOwnPropertyNames(JSContext *cx, JSObject *proxy, + JS::AutoIdVector &props) + { + return mTraps.getOwnPropertyNames ? + mTraps.getOwnPropertyNames(cx, proxy, props) : + DirectWrapper::getOwnPropertyNames(cx, proxy, props); + } + + virtual bool delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp) + { + return mTraps.delete_ ? + mTraps.delete_(cx, proxy, id, bp) : + DirectWrapper::delete_(cx, proxy, id, bp); + } + + virtual bool enumerate(JSContext *cx, JSObject *proxy, + JS::AutoIdVector &props) + { + return mTraps.enumerate ? + mTraps.enumerate(cx, proxy, props) : + DirectWrapper::enumerate(cx, proxy, props); + } + + /* ES5 Harmony derived proxy traps. */ + virtual bool has(JSContext *cx, JSObject *proxy, jsid id, bool *bp) + { + return mTraps.has ? + mTraps.has(cx, proxy, id, bp) : + DirectWrapper::has(cx, proxy, id, bp); + } + + virtual bool hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp) + { + return mTraps.hasOwn ? + mTraps.hasOwn(cx, proxy, id, bp) : + DirectWrapper::hasOwn(cx, proxy, id, bp); + } + + virtual bool get(JSContext *cx, JSObject *proxy, JSObject *receiver, + jsid id, JS::Value *vp) + { + return mTraps.get ? + mTraps.get(cx, proxy, receiver, id, vp) : + DirectWrapper::get(cx, proxy, receiver, id, vp); + } + + virtual bool set(JSContext *cx, JSObject *proxy, JSObject *receiver, + jsid id, bool strict, JS::Value *vp) + { + return mTraps.set ? + mTraps.set(cx, proxy, receiver, id, strict, vp) : + DirectWrapper::set(cx, proxy, receiver, id, strict, vp); + } + + virtual bool keys(JSContext *cx, JSObject *proxy, JS::AutoIdVector &props) + { + return mTraps.keys ? + mTraps.keys(cx, proxy, props) : + DirectWrapper::keys(cx, proxy, props); + } + + virtual bool iterate(JSContext *cx, JSObject *proxy, unsigned flags, + JS::Value *vp) + { + return mTraps.iterate ? + mTraps.iterate(cx, proxy, flags, vp) : + DirectWrapper::iterate(cx, proxy, flags, vp); + } + + /* Spidermonkey extensions. */ + virtual bool call(JSContext *cx, JSObject *proxy, unsigned argc, JS::Value *vp) + { + return mTraps.call ? + mTraps.call(cx, proxy, argc, vp) : + DirectWrapper::call(cx, proxy, argc, vp); + } + + virtual bool construct(JSContext *cx, JSObject *proxy, unsigned argc, JS::Value *argv, JS::Value *rval) + { + return mTraps.construct ? + mTraps.construct(cx, proxy, argc, argv, rval) : + DirectWrapper::construct(cx, proxy, argc, argv, rval); + } + + virtual bool nativeCall(JSContext *cx, JS::IsAcceptableThis test, JS::NativeImpl impl, JS::CallArgs args) + { + return mTraps.nativeCall ? + mTraps.nativeCall(cx, test, impl, args) : + DirectWrapper::nativeCall(cx, test, impl, args); + } + + virtual bool hasInstance(JSContext *cx, JSObject *proxy, const JS::Value *vp, bool *bp) + { + return mTraps.hasInstance ? + mTraps.hasInstance(cx, proxy, vp, bp) : + DirectWrapper::hasInstance(cx, proxy, vp, bp); + } + + virtual JSType typeOf(JSContext *cx, JSObject *proxy) + { + return mTraps.typeOf ? + mTraps.typeOf(cx, proxy) : + DirectWrapper::typeOf(cx, proxy); + } + + virtual bool objectClassIs(JSObject *obj, js::ESClassValue classValue, JSContext *cx) + { + return mTraps.objectClassIs ? + mTraps.objectClassIs(obj, classValue, cx) : + DirectWrapper::objectClassIs(obj, classValue, cx); + } + + virtual JSString *obj_toString(JSContext *cx, JSObject *proxy) + { + return mTraps.obj_toString ? + mTraps.obj_toString(cx, proxy) : + DirectWrapper::obj_toString(cx, proxy); + } + + virtual JSString *fun_toString(JSContext *cx, JSObject *proxy, unsigned indent) + { + return mTraps.fun_toString ? + mTraps.fun_toString(cx, proxy, indent) : + DirectWrapper::fun_toString(cx, proxy, indent); + } + + /*virtual bool regexp_toShared(JSContext *cx, JSObject *proxy, RegExpGuard *g) + { + return mTraps.regexp_toShared ? + mTraps.regexp_toShared(cx, proxy, g) : + DirectWrapper::regexp_toShared(cx, proxy, g); + }*/ + + virtual bool defaultValue(JSContext *cx, JSObject *obj, JSType hint, JS::Value *vp) + { + return mTraps.defaultValue ? + mTraps.defaultValue(cx, obj, hint, vp) : + DirectWrapper::defaultValue(cx, obj, hint, vp); + } + + virtual bool iteratorNext(JSContext *cx, JSObject *proxy, JS::Value *vp) + { + return mTraps.iteratorNext ? + mTraps.iteratorNext(cx, proxy, vp) : + DirectWrapper::iteratorNext(cx, proxy, vp); + } + + virtual void finalize(JSFreeOp *fop, JSObject *proxy) + { + return mTraps.finalize ? + mTraps.finalize(fop, proxy) : + DirectWrapper::finalize(fop, proxy); + } + + virtual bool getElementIfPresent(JSContext *cx, JSObject *obj, JSObject *receiver, + uint32_t index, JS::Value *vp, bool *present) + { + return mTraps.getElementIfPresent ? + mTraps.getElementIfPresent(cx, obj, receiver, index, vp, present) : + DirectWrapper::getElementIfPresent(cx, obj, receiver, index, vp, present); + } + + virtual bool getPrototypeOf(JSContext *cx, JSObject *proxy, JSObject **proto) + { + return mTraps.getPrototypeOf ? + mTraps.getPrototypeOf(cx, proxy, proto) : + DirectWrapper::getPrototypeOf(cx, proxy, proto); + } + + virtual void trace(JSTracer *trc, JSObject *proxy) + { + return mTraps.trace ? + mTraps.trace(trc, proxy) : + DirectWrapper::trace(trc, proxy); + } +}; + class ForwardingProxyHandler : public js::BaseProxyHandler { ProxyTraps mTraps; @@ -320,15 +534,28 @@ CreateProxyHandler(const ProxyTraps* aTraps, void* aExtra) return new ForwardingProxyHandler(*aTraps, aExtra); } +const void* +CreateWrapperProxyHandler(const ProxyTraps* aTraps) +{ + return new WrapperProxyHandler(*aTraps); +} + JSObject* -NewProxyObject(JSContext* aCx, void* aHandler, const js::Value* priv, +NewProxyObject(JSContext* aCx, void* aHandler, const js::Value* aPriv, JSObject* proto, JSObject* parent, JSObject* call, JSObject* construct) { - return js::NewProxyObject(aCx, (js::BaseProxyHandler*)aHandler, *priv, proto, + const js::Value &priv = aPriv ? *aPriv : JS::NullValue(); + return js::NewProxyObject(aCx, (js::BaseProxyHandler*)aHandler, priv, proto, parent, call, construct); } +JSObject* +WrapperNew(JSContext* aCx, JSObject* aParent, void* aHandler) +{ + return js::Wrapper::New(aCx, aParent, js::GetObjectProto(aParent), aParent, (js::Wrapper*)aHandler); +} + jsval GetProxyExtra(JSObject* obj, uint32_t slot) { @@ -463,4 +690,16 @@ ReportError(JSContext* aCx, const char* aError) JS_ReportError(aCx, aError); } +JSBool +IsWrapper(JSObject* obj) +{ + return js::IsWrapper(obj); +} + +JSObject* +UnwrapObject(JSObject* obj, JSBool stopAtOuter, unsigned* flags) +{ + return js::UnwrapObject(obj, stopAtOuter, flags); +} + } // extern "C"