Bug 773850 - Refactor method guarding to be able to work for methods that must be able to accept a |this| which is a proxy. r=luke

--HG--
extra : rebase_source : 1a015ffd3faa9fa6c82426c94058bce026602f8c
This commit is contained in:
Jeff Walden 2012-07-03 17:44:22 -07:00
parent 1f7d331861
commit 1cf3018b6e
39 changed files with 1896 additions and 1373 deletions

View File

@ -104,9 +104,13 @@ static const uint32_t XBLPROTO_SLOT = 0;
static const uint32_t FIELD_SLOT = 1;
static bool
ObjectHasISupportsPrivate(JS::Handle<JSObject*> obj)
ValueHasISupportsPrivate(const JS::Value &v)
{
JSClass* clasp = ::JS_GetClass(obj);
if (!v.isObject()) {
return false;
}
JSClass* clasp = ::JS_GetClass(&v.toObject());
const uint32_t HAS_PRIVATE_NSISUPPORTS =
JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS;
return (clasp->flags & HAS_PRIVATE_NSISUPPORTS) == HAS_PRIVATE_NSISUPPORTS;
@ -127,7 +131,7 @@ InstallXBLField(JSContext* cx,
//
// FieldAccessorGuard already determined whether |thisObj| was acceptable as
// |this| in terms of not throwing a TypeError. Assert this for good measure.
MOZ_ASSERT(ObjectHasISupportsPrivate(thisObj));
MOZ_ASSERT(ValueHasISupportsPrivate(JS::ObjectValue(*thisObj)));
// But there are some cases where we must accept |thisObj| but not install a
// property on it, or otherwise touch it. Hence this split of |this|-vetting
@ -210,91 +214,23 @@ InstallXBLField(JSContext* cx,
return false;
}
// Determine whether the |this| passed to this method is valid for an XBL field
// access (which is to say, an object with an nsISupports private), taking into
// account proxies and/or wrappers around |this|. There are three possible
// outcomes from calling this method:
//
// 1. An error was hit, and this method returned false. In this case, the
// caller should propagate it.
// 2. The |this| passed in was directly acceptable for XBL field access. This
// method returned true and set *thisObj to a |this| that can be used for
// field definition (i.e. that passes ObjectHasISupportsPrivate). In this
// case, the caller should install the field on *thisObj.
// 3. The |this| passed in was a proxy/wrapper around an object usable for
// XBL field access. The method recursively (and successfully) invoked the
// native on the unwrapped |this|, then it returned true and set *thisObj
// to null. In this case, the caller should itself return true.
//
// Thus this method implements the JS_CallNonGenericMethodOnProxy idiom in
// jsapi.h.
//
// Note that a |this| valid for field access is *not* necessarily one on which
// the field value will be installed. It's just one where calling the specified
// method on it won't unconditionally throw a TypeError. Confusing? Perhaps,
// but it's compatible with what we did before we implemented XBL fields using
// this technique.
inline bool
FieldAccessorGuard(JSContext *cx, unsigned argc, JS::Value *vp, JSNative native, JSObject **thisObj)
static bool
FieldGetterImpl(JSContext *cx, JS::CallArgs args)
{
JS::Rooted<JSObject*> obj(cx, JS_THIS_OBJECT(cx, vp));
if (!obj) {
xpc::Throw(cx, NS_ERROR_UNEXPECTED);
return false;
}
const JS::Value &thisv = args.thisv();
MOZ_ASSERT(ValueHasISupportsPrivate(thisv));
if (ObjectHasISupportsPrivate(obj)) {
*thisObj = obj;
return true;
}
// |this| wasn't an unwrapped object passing the has-private-nsISupports test.
// So try to unwrap |this| and recursively call the native on it.
//
// This |protoClass| gunk is needed for the JS engine to report an error if an
// object of the wrong class was passed as |this|, so that it can complain
// that it expected an object of type |protoClass|. It would be better if the
// error didn't try to specify the expected class of objects -- particularly
// because there's no one class of objects -- but it's what the API wants, so
// pass a class that's marginally correct as an answer.
JSClass* protoClass;
{
JS::Rooted<JSObject*> callee(cx, &JS_CALLEE(cx, vp).toObject());
JS::Rooted<JSObject*> xblProto(cx);
xblProto = &js::GetFunctionNativeReserved(callee, XBLPROTO_SLOT).toObject();
JSAutoEnterCompartment ac;
if (!ac.enter(cx, xblProto)) {
return false;
}
protoClass = ::JS_GetClass(xblProto);
}
*thisObj = NULL;
return JS_CallNonGenericMethodOnProxy(cx, argc, vp, native, protoClass);
}
static JSBool
FieldGetter(JSContext *cx, unsigned argc, JS::Value *vp)
{
JS::Rooted<JSObject*> thisObj(cx);
if (!FieldAccessorGuard(cx, argc, vp, FieldGetter, thisObj.address())) {
return false;
}
if (!thisObj) {
return true; // FieldGetter was recursively invoked on an unwrapped |this|
}
JS::Rooted<JSObject*> thisObj(cx, &thisv.toObject());
bool installed = false;
JS::Rooted<JSObject*> callee(cx, &JS_CALLEE(cx, vp).toObject());
JS::Rooted<JSObject*> callee(cx, &args.calleev().toObject());
JS::Rooted<jsid> id(cx);
if (!InstallXBLField(cx, callee, thisObj, id.address(), &installed)) {
return false;
}
if (!installed) {
JS_SET_RVAL(cx, vp, JS::UndefinedValue());
args.rval() = JS::UndefinedValue();
return true;
}
@ -302,33 +238,46 @@ FieldGetter(JSContext *cx, unsigned argc, JS::Value *vp)
if (!JS_GetPropertyById(cx, thisObj, id, v.address())) {
return false;
}
JS_SET_RVAL(cx, vp, v);
args.rval() = v;
return true;
}
static JSBool
FieldSetter(JSContext *cx, unsigned argc, JS::Value *vp)
FieldGetter(JSContext *cx, unsigned argc, JS::Value *vp)
{
JS::Rooted<JSObject*> thisObj(cx);
if (!FieldAccessorGuard(cx, argc, vp, FieldSetter, thisObj.address())) {
return false;
}
if (!thisObj) {
return true; // FieldSetter was recursively invoked on an unwrapped |this|
}
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
return JS::CallNonGenericMethod(cx, ValueHasISupportsPrivate, FieldGetterImpl,
args);
}
static bool
FieldSetterImpl(JSContext *cx, JS::CallArgs args)
{
const JS::Value &thisv = args.thisv();
MOZ_ASSERT(ValueHasISupportsPrivate(thisv));
JS::Rooted<JSObject*> thisObj(cx, &thisv.toObject());
bool installed = false;
JS::Rooted<JSObject*> callee(cx, &JS_CALLEE(cx, vp).toObject());
JS::Rooted<JSObject*> callee(cx, &args.calleev().toObject());
JS::Rooted<jsid> id(cx);
if (!InstallXBLField(cx, callee, thisObj, id.address(), &installed)) {
return false;
}
JS::Rooted<JS::Value> v(cx,
argc > 0 ? JS_ARGV(cx, vp)[0] : JS::UndefinedValue());
args.length() > 0 ? args[0] : JS::UndefinedValue());
return JS_SetPropertyById(cx, thisObj, id, v.address());
}
static JSBool
FieldSetter(JSContext *cx, unsigned argc, JS::Value *vp)
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
return JS::CallNonGenericMethod(cx, ValueHasISupportsPrivate, FieldSetterImpl,
args);
}
static JSBool
XBLResolve(JSContext *cx, JSHandleObject obj, JSHandleId id, unsigned flags,
JSMutableHandleObject objp)

View File

@ -115,7 +115,6 @@ CPPSRCS = \
ScopeObject.cpp \
Debugger.cpp \
GlobalObject.cpp \
MethodGuard.cpp \
ObjectImpl.cpp \
Stack.cpp \
String.cpp \

View File

@ -15,7 +15,6 @@
#include "gc/Marking.h"
#include "vm/GlobalObject.h"
#include "vm/MethodGuard.h"
#include "vm/Stack.h"
#include "jsobjinlines.h"
@ -227,42 +226,49 @@ MapObject::construct(JSContext *cx, unsigned argc, Value *vp)
return true;
}
#define UNPACK_THIS(T, native, cx, argc, vp, args, data) \
CallArgs args = CallArgsFromVp(argc, vp); \
if (!args.thisv().isObject() || \
!args.thisv().toObject().hasClass(&T::class_)) \
{ \
return js::HandleNonGenericMethodClassMismatch(cx, args, native, \
&T::class_); \
} \
if (!args.thisv().toObject().getPrivate()) { \
ReportIncompatibleMethod(cx, args, &T::class_); \
return false; \
} \
T::Data &data = *static_cast<T &>(args.thisv().toObject()).getData(); \
(void) data
#define THIS_MAP(native, cx, argc, vp, args, map) \
UNPACK_THIS(MapObject, native, cx, argc, vp, args, map)
bool
MapObject::is(const Value &v)
{
return v.isObject() && v.toObject().hasClass(&class_) && v.toObject().getPrivate();
}
#define ARG0_KEY(cx, args, key) \
HashableValue key; \
if (args.length() > 0 && !key.setValue(cx, args[0])) \
return false
JSBool
MapObject::size(JSContext *cx, unsigned argc, Value *vp)
ValueMap &
MapObject::extract(CallReceiver call)
{
THIS_MAP(size, cx, argc, vp, args, map);
JS_ASSERT(call.thisv().isObject());
JS_ASSERT(call.thisv().toObject().hasClass(&MapObject::class_));
return *static_cast<MapObject&>(call.thisv().toObject()).getData();
}
bool
MapObject::size_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(MapObject::is(args.thisv()));
ValueMap &map = extract(args);
JS_STATIC_ASSERT(sizeof map.count() <= sizeof(uint32_t));
args.rval().setNumber(map.count());
return true;
}
JSBool
MapObject::get(JSContext *cx, unsigned argc, Value *vp)
MapObject::size(JSContext *cx, unsigned argc, Value *vp)
{
THIS_MAP(get, cx, argc, vp, args, map);
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, is, size_impl, args);
}
bool
MapObject::get_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(MapObject::is(args.thisv()));
ValueMap &map = extract(args);
ARG0_KEY(cx, args, key);
if (ValueMap::Ptr p = map.lookup(key))
@ -273,18 +279,36 @@ MapObject::get(JSContext *cx, unsigned argc, Value *vp)
}
JSBool
MapObject::has(JSContext *cx, unsigned argc, Value *vp)
MapObject::get(JSContext *cx, unsigned argc, Value *vp)
{
THIS_MAP(has, cx, argc, vp, args, map);
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, is, get_impl, args);
}
bool
MapObject::has_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(MapObject::is(args.thisv()));
ValueMap &map = extract(args);
ARG0_KEY(cx, args, key);
args.rval().setBoolean(map.lookup(key));
return true;
}
JSBool
MapObject::set(JSContext *cx, unsigned argc, Value *vp)
MapObject::has(JSContext *cx, unsigned argc, Value *vp)
{
THIS_MAP(set, cx, argc, vp, args, map);
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, is, has_impl, args);
}
bool
MapObject::set_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(MapObject::is(args.thisv()));
ValueMap &map = extract(args);
ARG0_KEY(cx, args, key);
if (!map.put(key, args.length() > 1 ? args[1] : UndefinedValue())) {
js_ReportOutOfMemory(cx);
@ -295,9 +319,18 @@ MapObject::set(JSContext *cx, unsigned argc, Value *vp)
}
JSBool
MapObject::delete_(JSContext *cx, unsigned argc, Value *vp)
MapObject::set(JSContext *cx, unsigned argc, Value *vp)
{
THIS_MAP(delete_, cx, argc, vp, args, map);
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, is, set_impl, args);
}
bool
MapObject::delete_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(MapObject::is(args.thisv()));
ValueMap &map = extract(args);
ARG0_KEY(cx, args, key);
ValueMap::Ptr p = map.lookup(key);
bool found = p.found();
@ -307,6 +340,13 @@ MapObject::delete_(JSContext *cx, unsigned argc, Value *vp)
return true;
}
JSBool
MapObject::delete_(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, is, delete_impl, args);
}
JSObject *
js_InitMapClass(JSContext *cx, JSObject *obj)
{
@ -404,31 +444,62 @@ SetObject::construct(JSContext *cx, unsigned argc, Value *vp)
return true;
}
#define THIS_SET(native, cx, argc, vp, args, set) \
UNPACK_THIS(SetObject, native, cx, argc, vp, args, set)
JSBool
SetObject::size(JSContext *cx, unsigned argc, Value *vp)
bool
SetObject::is(const Value &v)
{
THIS_SET(size, cx, argc, vp, args, set);
return v.isObject() && v.toObject().hasClass(&class_) && v.toObject().getPrivate();
}
ValueSet &
SetObject::extract(CallReceiver call)
{
JS_ASSERT(call.thisv().isObject());
JS_ASSERT(call.thisv().toObject().hasClass(&SetObject::class_));
return *static_cast<SetObject&>(call.thisv().toObject()).getData();
}
bool
SetObject::size_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(is(args.thisv()));
ValueSet &set = extract(args);
JS_STATIC_ASSERT(sizeof set.count() <= sizeof(uint32_t));
args.rval().setNumber(set.count());
return true;
}
JSBool
SetObject::has(JSContext *cx, unsigned argc, Value *vp)
SetObject::size(JSContext *cx, unsigned argc, Value *vp)
{
THIS_SET(has, cx, argc, vp, args, set);
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, is, size_impl, args);
}
bool
SetObject::has_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(is(args.thisv()));
ValueSet &set = extract(args);
ARG0_KEY(cx, args, key);
args.rval().setBoolean(set.has(key));
return true;
}
JSBool
SetObject::add(JSContext *cx, unsigned argc, Value *vp)
SetObject::has(JSContext *cx, unsigned argc, Value *vp)
{
THIS_SET(add, cx, argc, vp, args, set);
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, is, has_impl, args);
}
bool
SetObject::add_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(is(args.thisv()));
ValueSet &set = extract(args);
ARG0_KEY(cx, args, key);
if (!set.put(key)) {
js_ReportOutOfMemory(cx);
@ -439,9 +510,18 @@ SetObject::add(JSContext *cx, unsigned argc, Value *vp)
}
JSBool
SetObject::delete_(JSContext *cx, unsigned argc, Value *vp)
SetObject::add(JSContext *cx, unsigned argc, Value *vp)
{
THIS_SET(delete_, cx, argc, vp, args, set);
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, is, add_impl, args);
}
bool
SetObject::delete_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(is(args.thisv()));
ValueSet &set = extract(args);
ARG0_KEY(cx, args, key);
ValueSet::Ptr p = set.lookup(key);
bool found = p.found();
@ -451,6 +531,13 @@ SetObject::delete_(JSContext *cx, unsigned argc, Value *vp)
return true;
}
JSBool
SetObject::delete_(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, is, delete_impl, args);
}
JSObject *
js_InitSetClass(JSContext *cx, JSObject *obj)
{

View File

@ -74,16 +74,24 @@ class MapObject : public JSObject {
static JSObject *initClass(JSContext *cx, JSObject *obj);
static Class class_;
private:
typedef ValueMap Data;
static JSFunctionSpec methods[];
ValueMap *getData() { return static_cast<ValueMap *>(getPrivate()); }
static ValueMap & extract(CallReceiver call);
static void mark(JSTracer *trc, JSObject *obj);
static void finalize(FreeOp *fop, JSObject *obj);
static JSBool construct(JSContext *cx, unsigned argc, Value *vp);
static bool is(const Value &v);
static bool size_impl(JSContext *cx, CallArgs args);
static JSBool size(JSContext *cx, unsigned argc, Value *vp);
static bool get_impl(JSContext *cx, CallArgs args);
static JSBool get(JSContext *cx, unsigned argc, Value *vp);
static bool has_impl(JSContext *cx, CallArgs args);
static JSBool has(JSContext *cx, unsigned argc, Value *vp);
static bool set_impl(JSContext *cx, CallArgs args);
static JSBool set(JSContext *cx, unsigned argc, Value *vp);
static bool delete_impl(JSContext *cx, CallArgs args);
static JSBool delete_(JSContext *cx, unsigned argc, Value *vp);
};
@ -92,15 +100,22 @@ class SetObject : public JSObject {
static JSObject *initClass(JSContext *cx, JSObject *obj);
static Class class_;
private:
typedef ValueSet Data;
static JSFunctionSpec methods[];
ValueSet *getData() { return static_cast<ValueSet *>(getPrivate()); }
static ValueSet & extract(CallReceiver call);
static void mark(JSTracer *trc, JSObject *obj);
static void finalize(FreeOp *fop, JSObject *obj);
static JSBool construct(JSContext *cx, unsigned argc, Value *vp);
static bool is(const Value &v);
static bool size_impl(JSContext *cx, CallArgs args);
static JSBool size(JSContext *cx, unsigned argc, Value *vp);
static bool has_impl(JSContext *cx, CallArgs args);
static JSBool has(JSContext *cx, unsigned argc, Value *vp);
static bool add_impl(JSContext *cx, CallArgs args);
static JSBool add(JSContext *cx, unsigned argc, Value *vp);
static bool delete_impl(JSContext *cx, CallArgs args);
static JSBool delete_(JSContext *cx, unsigned argc, Value *vp);
};

View File

@ -11,7 +11,6 @@
#include "builtin/RegExp.h"
#include "vm/MethodGuard-inl.h"
#include "vm/RegExpObject-inl.h"
#include "vm/RegExpStatics-inl.h"
@ -293,19 +292,25 @@ CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder, CallArgs args)
return true;
}
static bool
IsRegExp(const Value &v)
{
return v.isObject() && v.toObject().hasClass(&RegExpClass);
}
static bool
regexp_compile_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(IsRegExp(args.thisv()));
RegExpObjectBuilder builder(cx, &args.thisv().toObject().asRegExp());
return CompileRegExpObject(cx, builder, args);
}
static JSBool
regexp_compile(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, regexp_compile, &RegExpClass, &thisObj))
return false;
if (!thisObj)
return true;
RegExpObjectBuilder builder(cx, &thisObj->asRegExp());
return CompileRegExpObject(cx, builder, args);
return CallNonGenericMethod(cx, IsRegExp, regexp_compile_impl, args);
}
static JSBool
@ -332,23 +337,24 @@ regexp_construct(JSContext *cx, unsigned argc, Value *vp)
return CompileRegExpObject(cx, builder, args);
}
static bool
regexp_toString_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(IsRegExp(args.thisv()));
JSString *str = args.thisv().toObject().asRegExp().toString(cx);
if (!str)
return false;
args.rval() = StringValue(str);
return true;
}
static JSBool
regexp_toString(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, regexp_toString, &RegExpClass, &thisObj))
return false;
if (!thisObj)
return true;
JSString *str = thisObj->asRegExp().toString(cx);
if (!str)
return false;
*vp = StringValue(str);
return true;
return CallNonGenericMethod(cx, IsRegExp, regexp_toString_impl, args);
}
static JSFunctionSpec regexp_methods[] = {
@ -541,18 +547,10 @@ GetSharedForGreedyStar(JSContext *cx, JSAtom *source, RegExpFlag flags, RegExpGu
* |execType| to perform this optimization.
*/
static bool
ExecuteRegExp(JSContext *cx, Native native, unsigned argc, Value *vp)
ExecuteRegExp(JSContext *cx, RegExpExecType execType, CallArgs args)
{
CallArgs args = CallArgsFromVp(argc, vp);
/* Step 1. */
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, native, &RegExpClass, &thisObj))
return false;
if (!thisObj)
return true;
Rooted<RegExpObject*> reobj(cx, &thisObj->asRegExp());
/* Step 1 was performed by CallNonGenericMethod. */
Rooted<RegExpObject*> reobj(cx, &args.thisv().toObject().asRegExp());
RegExpGuard re;
if (StartsWithGreedyStar(reobj->getSource())) {
@ -598,7 +596,6 @@ ExecuteRegExp(JSContext *cx, Native native, unsigned argc, Value *vp)
}
/* Steps 8-21. */
RegExpExecType execType = (native == regexp_test) ? RegExpTest : RegExpExec;
size_t lastIndexInt(i);
if (!ExecuteRegExp(cx, res, *re, linearInput, chars, length, &lastIndexInt, execType,
&args.rval())) {
@ -617,19 +614,33 @@ ExecuteRegExp(JSContext *cx, Native native, unsigned argc, Value *vp)
}
/* ES5 15.10.6.2. */
static bool
regexp_exec_impl(JSContext *cx, CallArgs args)
{
return ExecuteRegExp(cx, RegExpExec, args);
}
JSBool
js::regexp_exec(JSContext *cx, unsigned argc, Value *vp)
{
return ExecuteRegExp(cx, regexp_exec, argc, vp);
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsRegExp, regexp_exec_impl, args);
}
/* ES5 15.10.6.3. */
static bool
regexp_test_impl(JSContext *cx, CallArgs args)
{
if (!ExecuteRegExp(cx, RegExpTest, args))
return false;
if (!args.rval().isTrue())
args.rval().setBoolean(false);
return true;
}
JSBool
js::regexp_test(JSContext *cx, unsigned argc, Value *vp)
{
if (!ExecuteRegExp(cx, regexp_test, argc, vp))
return false;
if (!vp->isTrue())
vp->setBoolean(false);
return true;
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsRegExp, regexp_test_impl, args);
}

View File

@ -20,16 +20,25 @@ static JSClass CustomClass = {
static const uint32_t CUSTOM_SLOT = 0;
static bool
IsCustomClass(const Value &v)
{
return v.isObject() && JS_GetClass(&v.toObject()) == &CustomClass;
}
static bool
CustomMethodImpl(JSContext *cx, CallArgs args)
{
JS_ASSERT(IsCustomClass(args.thisv()));
args.rval() = JS_GetReservedSlot(&args.thisv().toObject(), CUSTOM_SLOT);
return true;
}
static JSBool
CustomMethod(JSContext *cx, unsigned argc, Value *vp)
{
const Value &thisv = JS_THIS_VALUE(cx, vp);
JSObject *thisObj;
if (!thisv.isObject() || JS_GetClass((thisObj = &thisv.toObject())) != &CustomClass)
return JS_CallNonGenericMethodOnProxy(cx, argc, vp, CustomMethod, &CustomClass);
JS_SET_RVAL(cx, vp, JS_GetReservedSlot(thisObj, CUSTOM_SLOT));
return true;
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsCustomClass, CustomMethodImpl, args);
}
BEGIN_TEST(test_CallNonGenericMethodOnProxy)

View File

@ -61,7 +61,6 @@
#include "gc/Marking.h"
#include "gc/Memory.h"
#include "js/MemoryMetrics.h"
#include "vm/MethodGuard.h"
#include "vm/NumericConversions.h"
#include "vm/StringBuffer.h"
#include "vm/Xdr.h"
@ -88,6 +87,24 @@ using namespace js;
using namespace js::gc;
using namespace js::types;
bool
JS::detail::CallMethodIfWrapped(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
CallArgs args)
{
const Value &thisv = args.thisv();
JS_ASSERT(!test(thisv));
if (thisv.isObject()) {
JSObject &thisObj = args.thisv().toObject();
if (thisObj.isProxy())
return Proxy::nativeCall(cx, test, impl, args);
}
ReportIncompatible(cx, args);
return false;
}
/*
* This class is a version-establishing barrier at the head of a VM entry or
* re-entry. It ensures that:
@ -4874,14 +4891,6 @@ JS_DefineFunctionById(JSContext *cx, JSObject *obj_, jsid id_, JSNative call,
return js_DefineFunction(cx, obj, id, call, nargs, attrs);
}
extern JS_PUBLIC_API(JSBool)
JS_CallNonGenericMethodOnProxy(JSContext *cx, unsigned argc, jsval *vp, JSNative native,
JSClass *clasp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return HandleNonGenericMethodClassMismatch(cx, args, native, Valueify(clasp));
}
struct AutoLastFrameCheck {
AutoLastFrameCheck(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM)
: cx(cx) {

View File

@ -1443,6 +1443,93 @@ CallArgsFromSp(unsigned argc, Value *sp)
return CallArgsFromArgv(argc, sp - argc);
}
/* Returns true if |v| is considered an acceptable this-value. */
typedef bool (*IsAcceptableThis)(const Value &v);
/*
* Implements the guts of a method; guaranteed to be provided an acceptable
* this-value, as determined by a corresponding IsAcceptableThis method.
*/
typedef bool (*NativeImpl)(JSContext *cx, CallArgs args);
namespace detail {
/* DON'T CALL THIS DIRECTLY. It's for use only by CallNonGenericMethod! */
extern JS_PUBLIC_API(bool)
CallMethodIfWrapped(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args);
} /* namespace detail */
/*
* Methods usually act upon |this| objects only from a single global object and
* compartment. Sometimes, however, a method must act upon |this| values from
* multiple global objects or compartments. In such cases the |this| value a
* method might see will be wrapped, such that various access to the object --
* to its class, its private data, its reserved slots, and so on -- will not
* work properly without entering that object's compartment. This method
* implements a solution to this problem.
*
* To implement a method that accepts |this| values from multiple compartments,
* define two functions. The first function matches the IsAcceptableThis type
* and indicates whether the provided value is an acceptable |this| for the
* method; it must be a pure function only of its argument.
*
* static JSClass AnswerClass = { ... };
*
* static bool
* IsAnswerObject(const Value &v)
* {
* if (!v.isObject())
* return false;
* return JS_GetClass(&v.toObject()) == &AnswerClass;
* }
*
* The second function implements the NativeImpl signature and defines the
* behavior of the method when it is provided an acceptable |this| value.
* Aside from some typing niceties -- see the CallArgs interface for details --
* its interface is the same as that of JSNative.
*
* static bool
* answer_getAnswer_impl(JSContext *cx, JS::CallArgs args)
* {
* args.rval().setInt32(42);
* return true;
* }
*
* The implementation function is guaranteed to be called *only* with a |this|
* value which is considered acceptable.
*
* Now to implement the actual method, write a JSNative that calls the method
* declared below, passing the appropriate arguments.
*
* static JSBool
* answer_getAnswer(JSContext *cx, unsigned argc, JS::Value *vp)
* {
* JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
* return JS::CallNonGenericMethod(cx, IsAnswerObject,
answer_getAnswer_impl, args);
* }
*
* JS::CallNonGenericMethod will test whether |args.thisv()| is acceptable. If
* it is, it will call the provided implementation function, which will return
* a value and indicate success. If it is not, it will attempt to unwrap
* |this| and call the implementation function on the unwrapped |this|. If
* that succeeds, all well and good. If it doesn't succeed, a TypeError will
* be thrown.
*
* Note: JS::CallNonGenericMethod will only work correctly if it's called in
* tail position in a JSNative. Do not call it from any other place.
*/
JS_ALWAYS_INLINE bool
CallNonGenericMethod(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args)
{
const Value &thisv = args.thisv();
if (test(thisv))
return impl(cx, args);
return detail::CallMethodIfWrapped(cx, test, impl, args);
}
} /* namespace JS */
/************************************************************************/
@ -3955,7 +4042,7 @@ struct JSClass {
* with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was
* prevously allowed, but is now an ES5 violation and thus unsupported.
*/
#define JSCLASS_GLOBAL_SLOT_COUNT (JSProto_LIMIT * 3 + 8)
#define JSCLASS_GLOBAL_SLOT_COUNT (JSProto_LIMIT * 3 + 19)
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
#define JSCLASS_GLOBAL_FLAGS \
@ -4704,66 +4791,6 @@ JS_DefineFunctionById(JSContext *cx, JSObject *obj, jsid id, JSNative call,
extern JS_PUBLIC_API(JSObject *)
JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent);
/*
* Methods usually act upon |this| objects only from a single global object and
* compartment. Sometimes, however, a method must act upon |this| values from
* multiple global objects or compartments. In such cases the |this| value a
* method might see will be wrapped, such that various access to the object --
* to its class, its private data, its reserved slots, and so on -- will not
* work properly without entering that object's compartment. This method
* implements a solution to this problem.
*
* When called, this method attempts to unwrap |this| and call |native| on the
* underlying object with the provided arguments, entering |this|'s compartment
* in the process. It is critical that |this|-checking occur right at the
* start of |native| so that reentrant invocation is idempotent! If the call
* fails because |this| isn't a proxy to another object, a TypeError is thrown.
*
* The following example demonstrates the most common way this method might be
* used, to accept objects having only a particular class but which might be
* found in another compartment/global object or might be a proxy of some sort:
*
* static JSClass MyClass = { "MyClass", JSCLASS_HAS_PRIVATE, ... };
*
* inline bool
* RequireMyClassThis(JSContext *cx, unsigned argc, JSObject **thisObj)
* {
* const Value &thisv = JS_THIS_VALUE(cx, vp);
* if (!thisv.isObject()) {
* JS_ReportError(cx, "this must be an object");
* return false;
* }
*
* JSObject *obj = &thisv.toObject();
* if (JS_GetClass(obj) == &MyClass) {
* *thisObj = obj;
* return true;
* }
*
* *thisObj = NULL; // prevent infinite recursion into calling method
* return JS_CallNonGenericMethodOnProxy(cx, argc, vp, method, &MyClass);
* }
*
* static JSBool
* Method(JSContext *cx, unsigned argc, jsval *vp)
* {
* if (!RequireMyClassThis(cx, argc, vp, &thisObj))
* return false;
* if (!thisObj)
* return true; // method invocation was performed by nested call
*
* // thisObj definitely has MyClass: implement the guts of the method.
* void *priv = JS_GetPrivate(thisObj);
* ...
* }
*
* This method doesn't do any checking of its own, except to throw a TypeError
* if the |this| in the arguments isn't a proxy that can be unwrapped for the
* recursive call. The client is responsible for performing all type-checks!
*/
extern JS_PUBLIC_API(JSBool)
JS_CallNonGenericMethodOnProxy(JSContext *cx, unsigned argc, jsval *vp, JSNative native, JSClass *clasp);
/*
* Given a buffer, return JS_FALSE if the buffer might become a valid
* javascript statement with the addition of more lines. Otherwise return

View File

@ -95,7 +95,6 @@
#include "gc/Marking.h"
#include "vm/ArgumentsObject.h"
#include "vm/MethodGuard.h"
#include "vm/NumericConversions.h"
#include "vm/StringBuffer.h"
@ -1443,17 +1442,18 @@ class ArraySharpDetector
}
};
static JSBool
array_toSource(JSContext *cx, unsigned argc, Value *vp)
static bool
IsArray(const Value &v)
{
JS_CHECK_RECURSION(cx, return false);
return v.isObject() && v.toObject().isArray();
}
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject obj(cx, ToObject(cx, &args.thisv()));
if (!obj)
return false;
if (!obj->isArray())
return HandleNonGenericMethodClassMismatch(cx, args, array_toSource, &ArrayClass);
static bool
array_toSource_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(IsArray(args.thisv()));
Rooted<JSObject*> obj(cx, &args.thisv().toObject());
ArraySharpDetector detector(cx);
if (!detector.init(obj))
@ -1516,6 +1516,14 @@ array_toSource(JSContext *cx, unsigned argc, Value *vp)
args.rval().setString(str);
return true;
}
static JSBool
array_toSource(JSContext *cx, unsigned argc, Value *vp)
{
JS_CHECK_RECURSION(cx, return false);
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsArray, array_toSource_impl, args);
}
#endif
class AutoArrayCycleDetector

View File

@ -30,7 +30,7 @@
#include "jsobjinlines.h"
#include "vm/BooleanObject-inl.h"
#include "vm/MethodGuard-inl.h"
#include "vm/GlobalObject-inl.h"
using namespace js;
using namespace js::types;
@ -46,15 +46,20 @@ Class js::BooleanClass = {
JS_ConvertStub
};
#if JS_HAS_TOSOURCE
static JSBool
bool_toSource(JSContext *cx, unsigned argc, Value *vp)
static bool
IsBoolean(const Value &v)
{
CallArgs args = CallArgsFromVp(argc, vp);
return v.isBoolean() || (v.isObject() && v.toObject().hasClass(&BooleanClass));
}
bool b, ok;
if (!BoxedPrimitiveMethodGuard(cx, args, bool_toSource, &b, &ok))
return ok;
#if JS_HAS_TOSOURCE
static bool
bool_toSource_impl(JSContext *cx, CallArgs args)
{
const Value &thisv = args.thisv();
JS_ASSERT(IsBoolean(thisv));
bool b = thisv.isBoolean() ? thisv.toBoolean() : thisv.toObject().asBoolean().unbox();
StringBuffer sb(cx);
if (!sb.append("(new Boolean(") || !BooleanToStringBuffer(cx, b, sb) || !sb.append("))"))
@ -66,18 +71,41 @@ bool_toSource(JSContext *cx, unsigned argc, Value *vp)
args.rval().setString(str);
return true;
}
static JSBool
bool_toSource(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsBoolean, bool_toSource_impl, args);
}
#endif
static bool
bool_toString_impl(JSContext *cx, CallArgs args)
{
const Value &thisv = args.thisv();
JS_ASSERT(IsBoolean(thisv));
bool b = thisv.isBoolean() ? thisv.toBoolean() : thisv.toObject().asBoolean().unbox();
args.rval().setString(cx->runtime->atomState.booleanAtoms[b ? 1 : 0]);
return true;
}
static JSBool
bool_toString(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsBoolean, bool_toString_impl, args);
}
bool b, ok;
if (!BoxedPrimitiveMethodGuard<bool>(cx, args, bool_toString, &b, &ok))
return ok;
static bool
bool_valueOf_impl(JSContext *cx, CallArgs args)
{
const Value &thisv = args.thisv();
JS_ASSERT(IsBoolean(thisv));
args.rval().setString(cx->runtime->atomState.booleanAtoms[b ? 1 : 0]);
bool b = thisv.isBoolean() ? thisv.toBoolean() : thisv.toObject().asBoolean().unbox();
args.rval().setBoolean(b);
return true;
}
@ -85,13 +113,7 @@ static JSBool
bool_valueOf(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
bool b, ok;
if (!BoxedPrimitiveMethodGuard(cx, args, bool_valueOf, &b, &ok))
return ok;
args.rval().setBoolean(b);
return true;
return CallNonGenericMethod(cx, IsBoolean, bool_valueOf_impl, args);
}
static JSFunctionSpec boolean_methods[] = {
@ -99,7 +121,6 @@ static JSFunctionSpec boolean_methods[] = {
JS_FN(js_toSource_str, bool_toSource, 0, 0),
#endif
JS_FN(js_toString_str, bool_toString, 0, 0),
JS_FN(js_valueOf_str, bool_valueOf, 0, 0),
JS_FS_END
};
@ -143,6 +164,19 @@ js_InitBooleanClass(JSContext *cx, JSObject *obj)
if (!DefinePropertiesAndBrand(cx, booleanProto, NULL, boolean_methods))
return NULL;
Rooted<PropertyName*> valueOfName(cx, cx->runtime->atomState.valueOfAtom);
Rooted<JSFunction*> valueOf(cx,
js_NewFunction(cx, NULL, bool_valueOf, 0, 0, global, valueOfName));
if (!valueOf)
return NULL;
if (!booleanProto->defineProperty(cx, valueOfName, ObjectValue(*valueOf),
JS_PropertyStub, JS_StrictPropertyStub, 0))
{
return NULL;
}
global->setBooleanValueOf(valueOf);
if (!DefineConstructorAndPrototype(cx, global, JSProto_Boolean, ctor, booleanProto))
return NULL;
@ -160,21 +194,12 @@ namespace js {
bool
BooleanGetPrimitiveValueSlow(JSContext *cx, JSObject &obj, Value *vp)
{
JS_ASSERT(ObjectClassIs(obj, ESClass_Boolean, cx));
JS_ASSERT(obj.isProxy());
/*
* To respect the proxy abstraction, we can't simply unwrap and call
* getPrimitiveThis on the wrapped object. All we know is that obj says
* its [[Class]] is "Boolean". Boolean.prototype.valueOf is specified to
* return the [[PrimitiveValue]] internal property, so call that instead.
*/
InvokeArgsGuard ag;
if (!cx->stack.pushInvokeArgs(cx, 0, &ag))
return false;
ag.calleev().setUndefined();
ag.calleev() = cx->compartment->maybeGlobal()->booleanValueOf();
ag.thisv().setObject(obj);
if (!GetProxyHandler(&obj)->nativeCall(cx, &obj, &BooleanClass, bool_valueOf, ag))
if (!Invoke(cx, ag))
return false;
*vp = ag.rval();
return true;

View File

@ -376,7 +376,7 @@ JS_ALWAYS_INLINE bool
CallJSNative(JSContext *cx, Native native, const CallArgs &args)
{
#ifdef DEBUG
JSBool alreadyThrowing = cx->isExceptionPending();
bool alreadyThrowing = cx->isExceptionPending();
#endif
assertSameCompartment(cx, args);
bool ok = native(cx, args.length(), args.base());
@ -387,6 +387,22 @@ CallJSNative(JSContext *cx, Native native, const CallArgs &args)
return ok;
}
STATIC_PRECONDITION_ASSUME(ubound(args.argv_) >= argc)
JS_ALWAYS_INLINE bool
CallNativeImpl(JSContext *cx, NativeImpl impl, const CallArgs &args)
{
#ifdef DEBUG
bool alreadyThrowing = cx->isExceptionPending();
#endif
assertSameCompartment(cx, args);
bool ok = impl(cx, args);
if (ok) {
assertSameCompartment(cx, args.rval());
JS_ASSERT_IF(!alreadyThrowing, !cx->isExceptionPending());
}
return ok;
}
extern JSBool CallOrConstructBoundFunction(JSContext *, unsigned, js::Value *);
STATIC_PRECONDITION(ubound(args.argv_) >= argc)

File diff suppressed because it is too large Load Diff

View File

@ -39,7 +39,6 @@
#include "frontend/TokenStream.h"
#include "gc/Marking.h"
#include "vm/Debugger.h"
#include "vm/MethodGuard.h"
#include "vm/ScopeObject.h"
#include "vm/Xdr.h"
@ -1348,3 +1347,45 @@ js_DefineFunction(JSContext *cx, HandleObject obj, HandleId id, Native native,
return fun;
}
void
js::ReportIncompatibleMethod(JSContext *cx, CallReceiver call, Class *clasp)
{
Value &thisv = call.thisv();
#ifdef DEBUG
if (thisv.isObject()) {
JS_ASSERT(thisv.toObject().getClass() != clasp ||
!thisv.toObject().getProto() ||
thisv.toObject().getProto()->getClass() != clasp);
} else if (thisv.isString()) {
JS_ASSERT(clasp != &StringClass);
} else if (thisv.isNumber()) {
JS_ASSERT(clasp != &NumberClass);
} else if (thisv.isBoolean()) {
JS_ASSERT(clasp != &BooleanClass);
} else {
JS_ASSERT(thisv.isUndefined() || thisv.isNull());
}
#endif
if (JSFunction *fun = ReportIfNotFunction(cx, call.calleev())) {
JSAutoByteString funNameBytes;
if (const char *funName = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
clasp->name, funName, InformalValueTypeName(thisv));
}
}
}
void
js::ReportIncompatible(JSContext *cx, CallReceiver call)
{
if (JSFunction *fun = ReportIfNotFunction(cx, call.calleev())) {
JSAutoByteString funNameBytes;
if (const char *funName = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_METHOD,
funName, "method", InformalValueTypeName(call.thisv()));
}
}
}

View File

@ -264,6 +264,21 @@ XDRInterpretedFunction(XDRState<mode> *xdr, HandleObject enclosingScope,
extern JSObject *
CloneInterpretedFunction(JSContext *cx, HandleObject enclosingScope, HandleFunction fun);
/*
* Report an error that call.thisv is not compatible with the specified class,
* assuming that the method (clasp->name).prototype.<name of callee function>
* is what was called.
*/
extern void
ReportIncompatibleMethod(JSContext *cx, CallReceiver call, Class *clasp);
/*
* Report an error that call.thisv is not an acceptable this for the callee
* function.
*/
extern void
ReportIncompatible(JSContext *cx, CallReceiver call);
} /* namespace js */
extern JSBool

View File

@ -43,7 +43,6 @@
#include "jsinferinlines.h"
#include "jsobjinlines.h"
#include "vm/MethodGuard-inl.h"
#include "vm/Stack-inl.h"
#include "vm/String-inl.h"
@ -819,16 +818,18 @@ js_ThrowStopIteration(JSContext *cx)
return JS_FALSE;
}
static JSBool
iterator_next(JSContext *cx, unsigned argc, Value *vp)
static bool
IsIterator(const Value &v)
{
CallArgs args = CallArgsFromVp(argc, vp);
return v.isObject() && v.toObject().hasClass(&IteratorClass);
}
RootedObject thisObj(cx);
if (!NonGenericMethodGuard(cx, args, iterator_next, &IteratorClass, thisObj.address()))
return false;
if (!thisObj)
return true;
static bool
iterator_next_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(IsIterator(args.thisv()));
Rooted<JSObject*> thisObj(cx, &args.thisv().toObject());
if (!js_IteratorMore(cx, thisObj, &args.rval()))
return false;
@ -841,6 +842,13 @@ iterator_next(JSContext *cx, unsigned argc, Value *vp)
return js_IteratorNext(cx, thisObj, &args.rval());
}
static JSBool
iterator_next(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsIterator, iterator_next_impl, args);
}
#define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT)
static JSFunctionSpec iterator_methods[] = {
@ -1598,16 +1606,18 @@ CloseGenerator(JSContext *cx, JSObject *obj)
return SendToGenerator(cx, JSGENOP_CLOSE, obj, gen, UndefinedValue());
}
static JSBool
generator_send(JSContext *cx, unsigned argc, Value *vp)
static bool
IsGenerator(const Value &v)
{
CallArgs args = CallArgsFromVp(argc, vp);
return v.isObject() && v.toObject().hasClass(&GeneratorClass);
}
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, generator_send, &GeneratorClass, &thisObj))
return false;
if (!thisObj)
return true;
static bool
generator_send_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(IsGenerator(args.thisv()));
Rooted<JSObject*> thisObj(cx, &args.thisv().toObject());
JSGenerator *gen = (JSGenerator *) thisObj->getPrivate();
if (!gen || gen->state == JSGEN_CLOSED) {
@ -1633,15 +1643,18 @@ generator_send(JSContext *cx, unsigned argc, Value *vp)
}
static JSBool
generator_next(JSContext *cx, unsigned argc, Value *vp)
generator_send(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsGenerator, generator_send_impl, args);
}
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, generator_next, &GeneratorClass, &thisObj))
return false;
if (!thisObj)
return true;
static bool
generator_next_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(IsGenerator(args.thisv()));
Rooted<JSObject*> thisObj(cx, &args.thisv().toObject());
JSGenerator *gen = (JSGenerator *) thisObj->getPrivate();
if (!gen || gen->state == JSGEN_CLOSED) {
@ -1657,15 +1670,18 @@ generator_next(JSContext *cx, unsigned argc, Value *vp)
}
static JSBool
generator_throw(JSContext *cx, unsigned argc, Value *vp)
generator_next(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsGenerator, generator_next_impl, args);
}
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, generator_throw, &GeneratorClass, &thisObj))
return false;
if (!thisObj)
return true;
static bool
generator_throw_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(IsGenerator(args.thisv()));
Rooted<JSObject*> thisObj(cx, &args.thisv().toObject());
JSGenerator *gen = (JSGenerator *) thisObj->getPrivate();
if (!gen || gen->state == JSGEN_CLOSED) {
@ -1686,15 +1702,18 @@ generator_throw(JSContext *cx, unsigned argc, Value *vp)
}
static JSBool
generator_close(JSContext *cx, unsigned argc, Value *vp)
generator_throw(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsGenerator, generator_throw_impl, args);
}
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, generator_close, &GeneratorClass, &thisObj))
return false;
if (!thisObj)
return true;
static bool
generator_close_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(IsGenerator(args.thisv()));
Rooted<JSObject*> thisObj(cx, &args.thisv().toObject());
JSGenerator *gen = (JSGenerator *) thisObj->getPrivate();
if (!gen || gen->state == JSGEN_CLOSED) {
@ -1716,6 +1735,13 @@ generator_close(JSContext *cx, unsigned argc, Value *vp)
return true;
}
static JSBool
generator_close(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsGenerator, generator_close_impl, args);
}
static JSFunctionSpec generator_methods[] = {
JS_FN(js_next_str, generator_next, 0,JSPROP_ROPERM),
JS_FN(js_send_str, generator_send, 1,JSPROP_ROPERM),

View File

@ -44,7 +44,6 @@
#include "jslibmath.h"
#include "vm/GlobalObject.h"
#include "vm/MethodGuard.h"
#include "vm/NumericConversions.h"
#include "vm/StringBuffer.h"
@ -53,7 +52,6 @@
#include "jsnuminlines.h"
#include "jsobjinlines.h"
#include "vm/MethodGuard-inl.h"
#include "vm/NumberObject-inl.h"
#include "vm/String-inl.h"
@ -465,19 +463,29 @@ Number(JSContext *cx, unsigned argc, Value *vp)
return true;
}
#if JS_HAS_TOSOURCE
static JSBool
num_toSource(JSContext *cx, unsigned argc, Value *vp)
static bool
IsNumber(const Value &v)
{
CallArgs args = CallArgsFromVp(argc, vp);
return v.isNumber() || (v.isObject() && v.toObject().hasClass(&NumberClass));
}
double d;
bool ok;
if (!BoxedPrimitiveMethodGuard(cx, args, num_toSource, &d, &ok))
return ok;
inline double
Extract(const Value &v)
{
if (v.isNumber())
return v.toNumber();
return v.toObject().asNumber().unbox();
}
#if JS_HAS_TOSOURCE
static bool
num_toSource_impl(JSContext *cx, CallArgs args)
{
double d = Extract(args.thisv());
StringBuffer sb(cx);
if (!sb.append("(new Number(") || !NumberValueToStringBuffer(cx, NumberValue(d), sb) ||
if (!sb.append("(new Number(") ||
!NumberValueToStringBuffer(cx, NumberValue(d), sb) ||
!sb.append("))"))
{
return false;
@ -489,6 +497,13 @@ num_toSource(JSContext *cx, unsigned argc, Value *vp)
args.rval().setString(str);
return true;
}
static JSBool
num_toSource(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsNumber, num_toSource_impl, args);
}
#endif
ToCStringBuf::ToCStringBuf() :dbuf(NULL)
@ -578,15 +593,12 @@ IntToCString(ToCStringBuf *cbuf, int i, int base = 10)
static JSString * JS_FASTCALL
js_NumberToStringWithBase(JSContext *cx, double d, int base);
static JSBool
num_toString(JSContext *cx, unsigned argc, Value *vp)
static bool
num_toString_impl(JSContext *cx, CallArgs args)
{
CallArgs args = CallArgsFromVp(argc, vp);
JS_ASSERT(IsNumber(args.thisv()));
double d;
bool ok;
if (!BoxedPrimitiveMethodGuard(cx, args, num_toString, &d, &ok))
return ok;
double d = Extract(args.thisv());
int32_t base = 10;
if (args.hasDefined(0)) {
@ -611,14 +623,18 @@ num_toString(JSContext *cx, unsigned argc, Value *vp)
}
static JSBool
num_toLocaleString(JSContext *cx, unsigned argc, Value *vp)
num_toString(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsNumber, num_toString_impl, args);
}
double d;
bool ok;
if (!BoxedPrimitiveMethodGuard(cx, args, num_toLocaleString, &d, &ok))
return ok;
static bool
num_toLocaleString_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(IsNumber(args.thisv()));
double d = Extract(args.thisv());
Rooted<JSString*> str(cx, js_NumberToStringWithBase(cx, d, 10));
if (!str) {
@ -739,18 +755,26 @@ num_toLocaleString(JSContext *cx, unsigned argc, Value *vp)
return true;
}
static JSBool
num_toLocaleString(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsNumber, num_toLocaleString_impl, args);
}
static bool
num_valueOf_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(IsNumber(args.thisv()));
args.rval().setNumber(Extract(args.thisv()));
return true;
}
JSBool
js_num_valueOf(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
double d;
bool ok;
if (!BoxedPrimitiveMethodGuard(cx, args, js_num_valueOf, &d, &ok))
return ok;
args.rval().setNumber(d);
return true;
return CallNonGenericMethod(cx, IsNumber, num_valueOf_impl, args);
}
@ -794,15 +818,10 @@ DToStrResult(JSContext *cx, double d, JSDToStrMode mode, int precision, CallArgs
* In the following three implementations, we allow a larger range of precision
* than ECMA requires; this is permitted by ECMA-262.
*/
static JSBool
num_toFixed(JSContext *cx, unsigned argc, Value *vp)
static bool
num_toFixed_impl(JSContext *cx, CallArgs args)
{
CallArgs args = CallArgsFromVp(argc, vp);
double d;
bool ok;
if (!BoxedPrimitiveMethodGuard(cx, args, num_toFixed, &d, &ok))
return ok;
JS_ASSERT(IsNumber(args.thisv()));
int precision;
if (args.length() == 0) {
@ -812,18 +831,20 @@ num_toFixed(JSContext *cx, unsigned argc, Value *vp)
return false;
}
return DToStrResult(cx, d, DTOSTR_FIXED, precision, args);
return DToStrResult(cx, Extract(args.thisv()), DTOSTR_FIXED, precision, args);
}
static JSBool
num_toExponential(JSContext *cx, unsigned argc, Value *vp)
num_toFixed(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsNumber, num_toFixed_impl, args);
}
double d;
bool ok;
if (!BoxedPrimitiveMethodGuard(cx, args, num_toExponential, &d, &ok))
return ok;
static bool
num_toExponential_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(IsNumber(args.thisv()));
JSDToStrMode mode;
int precision;
@ -836,18 +857,22 @@ num_toExponential(JSContext *cx, unsigned argc, Value *vp)
return false;
}
return DToStrResult(cx, d, mode, precision + 1, args);
return DToStrResult(cx, Extract(args.thisv()), mode, precision + 1, args);
}
static JSBool
num_toPrecision(JSContext *cx, unsigned argc, Value *vp)
num_toExponential(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsNumber, num_toExponential_impl, args);
}
double d;
bool ok;
if (!BoxedPrimitiveMethodGuard(cx, args, num_toPrecision, &d, &ok))
return ok;
static bool
num_toPrecision_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(IsNumber(args.thisv()));
double d = Extract(args.thisv());
if (!args.hasDefined(0)) {
JSString *str = js_NumberToStringWithBase(cx, d, 10);
@ -873,6 +898,13 @@ num_toPrecision(JSContext *cx, unsigned argc, Value *vp)
return DToStrResult(cx, d, mode, precision, args);
}
static JSBool
num_toPrecision(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsNumber, num_toPrecision_impl, args);
}
static JSFunctionSpec number_methods[] = {
#if JS_HAS_TOSOURCE
JS_FN(js_toSource_str, num_toSource, 0, 0),

View File

@ -60,7 +60,9 @@
#include "jsscopeinlines.h"
#include "jsscriptinlines.h"
#include "vm/MethodGuard-inl.h"
#include "vm/BooleanObject-inl.h"
#include "vm/NumberObject-inl.h"
#include "vm/StringObject-inl.h"
#include "jsautooplen.h"

View File

@ -902,20 +902,28 @@ static JSFunctionSpec json_static_methods[] = {
JSObject *
js_InitJSONClass(JSContext *cx, JSObject *obj_)
{
RootedObject obj(cx, obj_);
Rooted<GlobalObject*> global(cx, &obj_->asGlobal());
RootedObject JSON(cx, NewObjectWithClassProto(cx, &JSONClass, NULL, obj));
/*
* JSON requires that Boolean.prototype.valueOf be created and stashed in a
* reserved slot on the global object; see js::BooleanGetPrimitiveValueSlow
* called from PreprocessValue above.
*/
if (!global->getOrCreateBooleanPrototype(cx))
return NULL;
RootedObject JSON(cx, NewObjectWithClassProto(cx, &JSONClass, NULL, global));
if (!JSON || !JSON->setSingletonType(cx))
return NULL;
if (!JS_DefineProperty(cx, obj, js_JSON_str, OBJECT_TO_JSVAL(JSON),
if (!JS_DefineProperty(cx, global, js_JSON_str, OBJECT_TO_JSVAL(JSON),
JS_PropertyStub, JS_StrictPropertyStub, 0))
return NULL;
if (!JS_DefineFunctions(cx, JSON, json_static_methods))
return NULL;
MarkStandardClassInitializedNoProto(obj, &JSONClass);
MarkStandardClassInitializedNoProto(global, &JSONClass);
return JSON;
}

View File

@ -8,21 +8,21 @@
#include <string.h>
#include "jsapi.h"
#include "jscntxt.h"
#include "jsfun.h"
#include "jsgc.h"
#include "jsprvtd.h"
#include "jsnum.h"
#include "jsobjinlines.h"
#include "jsproxy.h"
#include "jsscope.h"
#include "gc/Marking.h"
#include "vm/MethodGuard.h"
#include "vm/RegExpObject-inl.h"
#include "jsatominlines.h"
#include "jsinferinlines.h"
#include "jsobjinlines.h"
#include "vm/RegExpObject-inl.h"
using namespace js;
using namespace js::gc;
@ -318,10 +318,10 @@ BaseProxyHandler::iteratorNext(JSContext *cx, JSObject *proxy, Value *vp)
}
bool
BaseProxyHandler::nativeCall(JSContext *cx, JSObject *proxy, Class *clasp, Native native, CallArgs args)
BaseProxyHandler::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args)
{
JS_ASSERT(OperationInProgress(cx, proxy));
ReportIncompatibleMethod(cx, args, clasp);
JS_ASSERT(OperationInProgress(cx, &args.thisv().toObject()));
ReportIncompatible(cx, args);
return false;
}
@ -457,11 +457,16 @@ IndirectProxyHandler::construct(JSContext *cx, JSObject *proxy, unsigned argc,
}
bool
IndirectProxyHandler::nativeCall(JSContext *cx, JSObject *proxy, Class *clasp,
Native native, CallArgs args)
IndirectProxyHandler::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
CallArgs args)
{
args.thisv() = ObjectValue(*GetProxyTargetObject(proxy));
return CallJSNative(cx, native, args);
args.thisv() = ObjectValue(*GetProxyTargetObject(&args.thisv().toObject()));
if (!test(args.thisv())) {
ReportIncompatible(cx, args);
return false;
}
return CallNativeImpl(cx, impl, args);
}
bool
@ -1152,11 +1157,12 @@ Proxy::construct(JSContext *cx, JSObject *proxy, unsigned argc, Value *argv, Val
}
bool
Proxy::nativeCall(JSContext *cx, JSObject *proxy, Class *clasp, Native native, CallArgs args)
Proxy::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args)
{
JS_CHECK_RECURSION(cx, return false);
Rooted<JSObject*> proxy(cx, &args.thisv().toObject());
AutoPendingProxyOperation pending(cx, proxy);
return GetProxyHandler(proxy)->nativeCall(cx, proxy, clasp, native, args);
return GetProxyHandler(proxy)->nativeCall(cx, test, impl, args);
}
bool

View File

@ -102,7 +102,7 @@ class JS_FRIEND_API(BaseProxyHandler) {
/* Spidermonkey extensions. */
virtual bool call(JSContext *cx, JSObject *proxy, unsigned argc, Value *vp);
virtual bool construct(JSContext *cx, JSObject *proxy, unsigned argc, Value *argv, Value *rval);
virtual bool nativeCall(JSContext *cx, JSObject *proxy, Class *clasp, Native native, CallArgs args);
virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args);
virtual bool hasInstance(JSContext *cx, JSObject *proxy, const Value *vp, bool *bp);
virtual JSType typeOf(JSContext *cx, JSObject *proxy);
virtual bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx);
@ -150,8 +150,8 @@ class JS_PUBLIC_API(IndirectProxyHandler) : public BaseProxyHandler {
Value *vp) MOZ_OVERRIDE;
virtual bool construct(JSContext *cx, JSObject *proxy, unsigned argc,
Value *argv, Value *rval) MOZ_OVERRIDE;
virtual bool nativeCall(JSContext *cx, JSObject *proxy, Class *clasp,
Native native, CallArgs args) MOZ_OVERRIDE;
virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
CallArgs args) MOZ_OVERRIDE;
virtual bool hasInstance(JSContext *cx, JSObject *proxy, const Value *vp,
bool *bp) MOZ_OVERRIDE;
virtual JSType typeOf(JSContext *cx, JSObject *proxy) MOZ_OVERRIDE;
@ -226,7 +226,7 @@ class Proxy {
/* Spidermonkey extensions. */
static bool call(JSContext *cx, JSObject *proxy, unsigned argc, Value *vp);
static bool construct(JSContext *cx, JSObject *proxy, unsigned argc, Value *argv, Value *rval);
static bool nativeCall(JSContext *cx, JSObject *proxy, Class *clasp, Native native, CallArgs args);
static bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args);
static bool hasInstance(JSContext *cx, JSObject *proxy, const Value *vp, bool *bp);
static JSType typeOf(JSContext *cx, JSObject *proxy);
static bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx);

View File

@ -52,7 +52,6 @@
#include "jsstrinlines.h"
#include "jsautooplen.h" // generated headers last
#include "vm/MethodGuard-inl.h"
#include "vm/RegExpObject-inl.h"
#include "vm/RegExpStatics-inl.h"
#include "vm/StringObject-inl.h"
@ -450,6 +449,12 @@ ThisToStringForStringProto(JSContext *cx, CallReceiver call)
return str;
}
static bool
IsString(const Value &v)
{
return v.isString() || (v.isObject() && v.toObject().hasClass(&StringClass));
}
#if JS_HAS_TOSOURCE
/*
@ -470,15 +475,14 @@ str_quote(JSContext *cx, unsigned argc, Value *vp)
return true;
}
static JSBool
str_toSource(JSContext *cx, unsigned argc, Value *vp)
static bool
str_toSource_impl(JSContext *cx, CallArgs args)
{
CallArgs args = CallArgsFromVp(argc, vp);
JS_ASSERT(IsString(args.thisv()));
JSString *str;
bool ok;
if (!BoxedPrimitiveMethodGuard(cx, args, str_toSource, &str, &ok))
return ok;
Rooted<JSString*> str(cx, ToString(cx, args.thisv()));
if (!str)
return false;
str = js_QuoteString(cx, str, '"');
if (!str)
@ -495,20 +499,31 @@ str_toSource(JSContext *cx, unsigned argc, Value *vp)
return true;
}
static JSBool
str_toSource(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsString, str_toSource_impl, args);
}
#endif /* JS_HAS_TOSOURCE */
static bool
str_toString_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(IsString(args.thisv()));
args.rval() = StringValue(args.thisv().isString()
? args.thisv().toString()
: args.thisv().toObject().asString().unbox());
return true;
}
JSBool
js_str_toString(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
JSString *str;
bool ok;
if (!BoxedPrimitiveMethodGuard(cx, args, js_str_toString, &str, &ok))
return ok;
args.rval() = StringValue(str);
return true;
return CallNonGenericMethod(cx, IsString, str_toString_impl, args);
}
/*

View File

@ -35,7 +35,7 @@
#include "jsobjinlines.h"
#include "jstypedarrayinlines.h"
#include "vm/MethodGuard-inl.h"
#include "vm/GlobalObject-inl.h"
using namespace mozilla;
using namespace js;
@ -117,32 +117,33 @@ getArrayBuffer(JSObject *obj)
return obj ? &obj->asArrayBuffer() : NULL;
}
JSBool
ArrayBufferObject::byteLengthGetter(JSContext *cx, unsigned argc, Value *vp)
static bool
IsArrayBuffer(const Value &v)
{
CallArgs args = CallArgsFromVp(argc, vp);
return v.isObject() && v.toObject().hasClass(&ArrayBufferClass);
}
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, byteLengthGetter, &ArrayBufferClass, &thisObj))
return false;
if (!thisObj)
return true;
JS_SET_RVAL(cx, vp, Int32Value(thisObj->asArrayBuffer().byteLength()));
bool
ArrayBufferObject::byteLengthGetterImpl(JSContext *cx, CallArgs args)
{
JS_ASSERT(IsArrayBuffer(args.thisv()));
args.rval() = Int32Value(args.thisv().toObject().asArrayBuffer().byteLength());
return true;
}
JSBool
ArrayBufferObject::fun_slice(JSContext *cx, unsigned argc, Value *vp)
ArrayBufferObject::byteLengthGetter(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsArrayBuffer, byteLengthGetterImpl, args);
}
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, fun_slice, &ArrayBufferClass, &thisObj))
return false;
if (!thisObj)
return true;
bool
ArrayBufferObject::fun_slice_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(IsArrayBuffer(args.thisv()));
Rooted<JSObject*> thisObj(cx, &args.thisv().toObject());
ArrayBufferObject &arrayBuffer = thisObj->asArrayBuffer();
// these are the default values
@ -169,6 +170,13 @@ ArrayBufferObject::fun_slice(JSContext *cx, unsigned argc, Value *vp)
return true;
}
JSBool
ArrayBufferObject::fun_slice(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsArrayBuffer, fun_slice_impl, args);
}
/*
* new ArrayBuffer(byteLength)
*/
@ -281,6 +289,37 @@ ArrayBufferObject::createSlice(JSContext *cx, ArrayBufferObject &arrayBuffer,
return create(cx, 0);
}
bool
ArrayBufferObject::createDataViewForThisImpl(JSContext *cx, CallArgs args)
{
JS_ASSERT(IsArrayBuffer(args.thisv()));
/*
* This method is only called for |DataView(alienBuf, ...)| which calls
* this as |createDataViewForThis.call(alienBuf, ..., DataView.prototype)|,
* ergo there must be at least two arguments.
*/
JS_ASSERT(args.length() >= 2);
Rooted<JSObject*> proto(cx, &args[args.length() - 1].toObject());
Rooted<JSObject*> buffer(cx, &args.thisv().toObject());
/*
* Pop off the passed-along prototype and delegate to normal DataView
* object construction.
*/
CallArgs frobbedArgs = CallArgsFromVp(args.length() - 1, args.base());
return DataViewObject::construct(cx, buffer, frobbedArgs, proto);
}
JSBool
ArrayBufferObject::createDataViewForThis(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsArrayBuffer, createDataViewForThisImpl, args);
}
void
ArrayBufferObject::obj_trace(JSTracer *trc, JSObject *obj)
{
@ -981,6 +1020,10 @@ class TypedArrayTemplate
return &TypedArray::classes[ArrayTypeID()];
}
static bool is(const Value &v) {
return v.isObject() && v.toObject().hasClass(fastClass());
}
static void
obj_trace(JSTracer *trc, JSObject *obj)
{
@ -1424,19 +1467,6 @@ class TypedArrayTemplate
return true;
}
static JSBool
fromBuffer(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject buffer(cx, &args[0].toObject());
RootedObject proto(cx, &args[3].toObject());
JSObject *obj = fromBuffer(cx, buffer, args[1].toInt32(), args[2].toInt32(), proto);
if (!obj)
return false;
vp->setObject(*obj);
return true;
}
static JSObject *
create(JSContext *cx, unsigned argc, Value *argv)
{
@ -1494,27 +1524,32 @@ class TypedArrayTemplate
return fromBuffer(cx, dataObj, byteOffset, length, proto);
}
static bool IsThisClass(const Value &v) {
return v.isObject() && v.toObject().hasClass(fastClass());
}
template<Value ValueGetter(JSObject *obj)>
static bool
GetterImpl(JSContext *cx, CallArgs args)
{
JS_ASSERT(IsThisClass(args.thisv()));
args.rval() = ValueGetter(&args.thisv().toObject());
return true;
}
// ValueGetter is a function that takes an unwrapped typed array object and
// returns a Value. Given such a function, Getter<> is a native that
// retrieves a given Value, probably from a slot on the object.
template<Value ValueGetter(JSObject *)>
template<Value ValueGetter(JSObject *obj)>
static JSBool
Getter(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, Getter<ValueGetter>, fastClass(), &thisObj))
return false;
if (!thisObj)
return true;
JS_SET_RVAL(cx, vp, ValueGetter(thisObj));
return true;
return CallNonGenericMethod(cx, IsThisClass, GetterImpl<ValueGetter>, args);
}
// Define an accessor for a read-only property that invokes a native getter
template<Value ValueGetter(JSObject *)>
template<Value ValueGetter(JSObject *obj)>
static bool
DefineGetter(JSContext *cx, PropertyName *name, HandleObject proto)
{
@ -1550,18 +1585,12 @@ class TypedArrayTemplate
}
/* subarray(start[, end]) */
static JSBool
fun_subarray(JSContext *cx, unsigned argc, Value *vp)
static bool
fun_subarray_impl(JSContext *cx, CallArgs args)
{
CallArgs args = CallArgsFromVp(argc, vp);
JS_ASSERT(IsThisClass(args.thisv()));
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, fun_subarray, fastClass(), &thisObj))
return false;
if (!thisObj)
return true;
JSObject *tarray = getTypedArray(thisObj);
JSObject *tarray = getTypedArray(&args.thisv().toObject());
if (!tarray)
return true;
@ -1589,17 +1618,18 @@ class TypedArrayTemplate
return true;
}
/* move(begin, end, dest) */
static JSBool
fun_move(JSContext *cx, unsigned argc, Value *vp)
fun_subarray(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsThisClass, fun_subarray_impl, args);
}
JSObject *obj;
if (!NonGenericMethodGuard(cx, args, fun_move, fastClass(), &obj))
return false;
if (!obj)
return true;
/* move(begin, end, dest) */
static bool
fun_move_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(IsThisClass(args.thisv()));
if (args.length() < 3) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TYPED_ARRAY_BAD_ARGS);
@ -1610,7 +1640,7 @@ class TypedArrayTemplate
uint32_t srcEnd;
uint32_t dest;
JSObject *tarray = getTypedArray(obj);
JSObject *tarray = getTypedArray(&args.thisv().toObject());
uint32_t length = TypedArray::length(tarray);
if (!ToClampedIndex(cx, args[0], length, &srcBegin) ||
!ToClampedIndex(cx, args[1], length, &srcEnd) ||
@ -1651,18 +1681,20 @@ class TypedArrayTemplate
return true;
}
/* set(array[, offset]) */
static JSBool
fun_set(JSContext *cx, unsigned argc, Value *vp)
fun_move(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsThisClass, fun_move_impl, args);
}
RootedObject thisObj(cx);
if (!NonGenericMethodGuard(cx, args, fun_set, fastClass(), thisObj.address()))
return false;
if (!thisObj)
return true;
/* set(array[, offset]) */
static bool
fun_set_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(IsThisClass(args.thisv()));
Rooted<JSObject*> thisObj(cx, &args.thisv().toObject());
RootedObject tarray(cx, getTypedArray(thisObj));
if (!tarray)
return true;
@ -1720,6 +1752,13 @@ class TypedArrayTemplate
return true;
}
static JSBool
fun_set(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsThisClass, fun_set_impl, args);
}
public:
static JSObject *
fromBuffer(JSContext *cx, HandleObject bufobj, int32_t byteOffsetInt, int32_t lengthInt,
@ -1752,23 +1791,31 @@ class TypedArrayTemplate
* set to the origin compartment's prototype object, not the
* target's (specifically, the actual view in the target
* compartment will use as its prototype a wrapper around the
* origin compartment's view.prototype object)
* origin compartment's view.prototype object).
*
* Rather than hack some crazy solution together, implement
* this all using a private helper function, created when
* ArrayBuffer was initialized and cached in the global. This
* reuses all the existing cross-compartment crazy so we don't
* have to do anything *uniquely* crazy here.
*/
JSObject *proto = GetProtoForClass(cx, fastClass());
Rooted<JSObject*> proto(cx, GetProtoForClass(cx, fastClass()));
if (!proto)
return NULL;
Value argv[] = { UndefinedValue(),
MagicValue(JS_IS_CONSTRUCTING),
ObjectValue(*bufobj),
Int32Value(byteOffsetInt),
Int32Value(lengthInt),
ObjectValue(*proto) };
uint32_t argc = sizeof(argv) / sizeof(argv[0]) - 2;
CallArgs args = CallArgsFromVp(argc, argv);
if (!Proxy::nativeCall(cx, bufobj, &ArrayBufferClass, fromBuffer, args))
InvokeArgsGuard ag;
if (!cx->stack.pushInvokeArgs(cx, 3, &ag))
return NULL;
return &args.rval().toObject();
ag.calleev() = cx->compartment->maybeGlobal()->createArrayFromBuffer<NativeType>();
ag.thisv() = ObjectValue(*bufobj);
ag[0] = Int32Value(byteOffsetInt);
ag[1] = Int32Value(lengthInt);
ag[2] = ObjectValue(*proto);
if (!Invoke(cx, ag))
return NULL;
return &ag.rval().toObject();
}
}
@ -2173,6 +2220,34 @@ class Uint8ClampedArray : public TypedArrayTemplate<uint8_clamped> {
static JSFunctionSpec jsfuncs[];
};
template<typename T>
bool
ArrayBufferObject::createTypedArrayFromBufferImpl(JSContext *cx, CallArgs args)
{
typedef TypedArrayTemplate<T> ArrayType;
JS_ASSERT(IsArrayBuffer(args.thisv()));
JS_ASSERT(args.length() == 3);
Rooted<JSObject*> buffer(cx, &args.thisv().toObject());
Rooted<JSObject*> proto(cx, &args[2].toObject());
Rooted<JSObject*> obj(cx);
obj = ArrayType::fromBuffer(cx, buffer, args[0].toInt32(), args[1].toInt32(), proto);
if (!obj)
return false;
args.rval() = ObjectValue(*obj);
return true;
}
template<typename T>
JSBool
ArrayBufferObject::createTypedArrayFromBuffer(JSContext *cx, unsigned argc, Value *vp)
{
typedef TypedArrayTemplate<T> ArrayType;
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsArrayBuffer, createTypedArrayFromBufferImpl<T>, args);
}
// this default implementation is only valid for integer types
// less than 32-bits in size.
template<typename NativeType>
@ -2294,21 +2369,6 @@ DataViewObject::construct(JSContext *cx, JSObject *bufobj, const CallArgs &args,
return true;
}
JSBool
DataViewObject::constructWithProto(JSContext *cx, unsigned argc, Value *vp)
{
// Pop the proto argument off the end
CallArgs args = CallArgsFromVp(argc, vp);
JSObject &proto = args[args.length() - 1].toObject();
// And now mimic class_constructor for everything else, but pass in the proto
args = CallArgsFromVp(argc - 1, vp);
RootedObject bufobj(cx);
if (!GetFirstArgumentAsObject(cx, args.length(), args.base(), "DataView constructor", &bufobj))
return false;
return construct(cx, bufobj, args, &proto);
}
JSBool
DataViewObject::class_constructor(JSContext *cx, unsigned argc, Value *vp)
{
@ -2319,23 +2379,21 @@ DataViewObject::class_constructor(JSContext *cx, unsigned argc, Value *vp)
return false;
if (bufobj->isWrapper() && UnwrapObject(bufobj)->isArrayBuffer()) {
JSObject *proto = GetProtoForClass(cx, &DataViewClass);
Rooted<GlobalObject*> global(cx, cx->compartment->maybeGlobal());
Rooted<JSObject*> proto(cx, global->getOrCreateDataViewPrototype(cx));
if (!proto)
return false;
Vector<Value, 6> argv(cx);
argv.resize(argc + 2 + 1);
memcpy(argv.begin(), args.base(), sizeof(Value) * (argc + 2));
argv[argc + 2].setObject(*proto);
argv[0].setUndefined(); // We want to use a different callee (avoid an assertion)
// Appease 'thisv' assertion in CrossCompartmentWrapper::nativeCall
argv[1].setMagic(JS_IS_CONSTRUCTING);
CallArgs proxyArgs = CallArgsFromVp(argc + 1, argv.begin());
if (!Proxy::nativeCall(cx, bufobj, &DataViewClass, constructWithProto, proxyArgs))
InvokeArgsGuard ag;
if (!cx->stack.pushInvokeArgs(cx, argc + 1, &ag))
return false;
args.rval() = proxyArgs.rval();
ag.calleev() = global->createDataViewForThis();
ag.thisv() = ObjectValue(*bufobj);
PodCopy(ag.array(), args.array(), args.length());
ag[argc] = ObjectValue(*proto);
if (!Invoke(cx, ag))
return false;
args.rval() = ag.rval();
return true;
}
@ -2509,18 +2567,12 @@ DataViewObject::write(JSContext *cx, Handle<DataViewObject*> obj,
return true;
}
JSBool
DataViewObject::fun_getInt8(JSContext *cx, unsigned argc, Value *vp)
bool
DataViewObject::getInt8Impl(JSContext *cx, CallArgs args)
{
CallArgs args = CallArgsFromVp(argc, vp);
JS_ASSERT(is(args.thisv()));
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, fun_getInt8, &DataViewClass, &thisObj))
return false;
if (!thisObj)
return true;
Rooted<DataViewObject*> thisView(cx, &thisObj->asDataView());
Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().asDataView());
int8_t val;
if (!read(cx, thisView, args, &val, "getInt8"))
@ -2530,17 +2582,18 @@ DataViewObject::fun_getInt8(JSContext *cx, unsigned argc, Value *vp)
}
JSBool
DataViewObject::fun_getUint8(JSContext *cx, unsigned argc, Value *vp)
DataViewObject::fun_getInt8(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, is, getInt8Impl, args);
}
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, fun_getUint8, &DataViewClass, &thisObj))
return false;
if (!thisObj)
return true;
bool
DataViewObject::getUint8Impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(is(args.thisv()));
Rooted<DataViewObject*> thisView(cx, &thisObj->asDataView());
Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().asDataView());
uint8_t val;
if (!read(cx, thisView, args, &val, "getUint8"))
@ -2550,17 +2603,18 @@ DataViewObject::fun_getUint8(JSContext *cx, unsigned argc, Value *vp)
}
JSBool
DataViewObject::fun_getInt16(JSContext *cx, unsigned argc, Value *vp)
DataViewObject::fun_getUint8(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, is, getUint8Impl, args);
}
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, fun_getInt16, &DataViewClass, &thisObj))
return false;
if (!thisObj)
return true;
bool
DataViewObject::getInt16Impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(is(args.thisv()));
Rooted<DataViewObject*> thisView(cx, &thisObj->asDataView());
Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().asDataView());
int16_t val;
if (!read(cx, thisView, args, &val, "getInt16"))
@ -2570,17 +2624,18 @@ DataViewObject::fun_getInt16(JSContext *cx, unsigned argc, Value *vp)
}
JSBool
DataViewObject::fun_getUint16(JSContext *cx, unsigned argc, Value *vp)
DataViewObject::fun_getInt16(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, is, getInt16Impl, args);
}
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, fun_getUint16, &DataViewClass, &thisObj))
return false;
if (!thisObj)
return true;
bool
DataViewObject::getUint16Impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(is(args.thisv()));
Rooted<DataViewObject*> thisView(cx, &thisObj->asDataView());
Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().asDataView());
uint16_t val;
if (!read(cx, thisView, args, &val, "getUint16"))
@ -2590,17 +2645,18 @@ DataViewObject::fun_getUint16(JSContext *cx, unsigned argc, Value *vp)
}
JSBool
DataViewObject::fun_getInt32(JSContext *cx, unsigned argc, Value *vp)
DataViewObject::fun_getUint16(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, is, getUint16Impl, args);
}
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, fun_getInt32, &DataViewClass, &thisObj))
return false;
if (!thisObj)
return true;
bool
DataViewObject::getInt32Impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(is(args.thisv()));
Rooted<DataViewObject*> thisView(cx, &thisObj->asDataView());
Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().asDataView());
int32_t val;
if (!read(cx, thisView, args, &val, "getInt32"))
@ -2610,17 +2666,18 @@ DataViewObject::fun_getInt32(JSContext *cx, unsigned argc, Value *vp)
}
JSBool
DataViewObject::fun_getUint32(JSContext *cx, unsigned argc, Value *vp)
DataViewObject::fun_getInt32(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, is, getInt32Impl, args);
}
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, fun_getUint32, &DataViewClass, &thisObj))
return false;
if (!thisObj)
return true;
bool
DataViewObject::getUint32Impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(is(args.thisv()));
Rooted<DataViewObject*> thisView(cx, &thisObj->asDataView());
Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().asDataView());
uint32_t val;
if (!read(cx, thisView, args, &val, "getUint32"))
@ -2630,17 +2687,18 @@ DataViewObject::fun_getUint32(JSContext *cx, unsigned argc, Value *vp)
}
JSBool
DataViewObject::fun_getFloat32(JSContext *cx, unsigned argc, Value *vp)
DataViewObject::fun_getUint32(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, is, getUint32Impl, args);
}
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, fun_getFloat32, &DataViewClass, &thisObj))
return false;
if (!thisObj)
return true;
bool
DataViewObject::getFloat32Impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(is(args.thisv()));
Rooted<DataViewObject*> thisView(cx, &thisObj->asDataView());
Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().asDataView());
float val;
if (!read(cx, thisView, args, &val, "getFloat32"))
@ -2651,17 +2709,18 @@ DataViewObject::fun_getFloat32(JSContext *cx, unsigned argc, Value *vp)
}
JSBool
DataViewObject::fun_getFloat64(JSContext *cx, unsigned argc, Value *vp)
DataViewObject::fun_getFloat32(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, is, getFloat32Impl, args);
}
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, fun_getFloat64, &DataViewClass, &thisObj))
return false;
if (!thisObj)
return true;
bool
DataViewObject::getFloat64Impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(is(args.thisv()));
Rooted<DataViewObject*> thisView(cx, &thisObj->asDataView());
Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().asDataView());
double val;
if (!read(cx, thisView, args, &val, "getFloat64"))
@ -2671,20 +2730,41 @@ DataViewObject::fun_getFloat64(JSContext *cx, unsigned argc, Value *vp)
return true;
}
JSBool
DataViewObject::fun_getFloat64(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, is, getFloat64Impl, args);
}
bool
DataViewObject::setInt8Impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(is(args.thisv()));
Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().asDataView());
if (!write<int8_t>(cx, thisView, args, "setInt8"))
return false;
args.rval().setUndefined();
return true;
}
JSBool
DataViewObject::fun_setInt8(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, is, setInt8Impl, args);
}
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, fun_setInt8, &DataViewClass, &thisObj))
return false;
if (!thisObj)
return true;
bool
DataViewObject::setUint8Impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(is(args.thisv()));
Rooted<DataViewObject*> thisView(cx, &thisObj->asDataView());
Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().asDataView());
if (!write<int8_t>(cx, thisView, args, "setInt8"))
if (!write<uint8_t>(cx, thisView, args, "setUint8"))
return false;
args.rval().setUndefined();
return true;
@ -2694,16 +2774,17 @@ JSBool
DataViewObject::fun_setUint8(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, is, setUint8Impl, args);
}
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, fun_setUint8, &DataViewClass, &thisObj))
return false;
if (!thisObj)
return true;
bool
DataViewObject::setInt16Impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(is(args.thisv()));
Rooted<DataViewObject*> thisView(cx, &thisObj->asDataView());
Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().asDataView());
if (!write<uint8_t>(cx, thisView, args, "setUint8"))
if (!write<int16_t>(cx, thisView, args, "setInt16"))
return false;
args.rval().setUndefined();
return true;
@ -2713,16 +2794,17 @@ JSBool
DataViewObject::fun_setInt16(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, is, setInt16Impl, args);
}
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, fun_setInt16, &DataViewClass, &thisObj))
return false;
if (!thisObj)
return true;
bool
DataViewObject::setUint16Impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(is(args.thisv()));
Rooted<DataViewObject*> thisView(cx, &thisObj->asDataView());
Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().asDataView());
if (!write<int16_t>(cx, thisView, args, "setInt16"))
if (!write<uint16_t>(cx, thisView, args, "setUint16"))
return false;
args.rval().setUndefined();
return true;
@ -2732,16 +2814,17 @@ JSBool
DataViewObject::fun_setUint16(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, is, setUint16Impl, args);
}
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, fun_setUint16, &DataViewClass, &thisObj))
return false;
if (!thisObj)
return true;
bool
DataViewObject::setInt32Impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(is(args.thisv()));
Rooted<DataViewObject*> thisView(cx, &thisObj->asDataView());
Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().asDataView());
if (!write<uint16_t>(cx, thisView, args, "setUint16"))
if (!write<int32_t>(cx, thisView, args, "setInt32"))
return false;
args.rval().setUndefined();
return true;
@ -2751,16 +2834,17 @@ JSBool
DataViewObject::fun_setInt32(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, is, setInt32Impl, args);
}
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, fun_setInt32, &DataViewClass, &thisObj))
return false;
if (!thisObj)
return true;
bool
DataViewObject::setUint32Impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(is(args.thisv()));
Rooted<DataViewObject*> thisView(cx, &thisObj->asDataView());
Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().asDataView());
if (!write<int32_t>(cx, thisView, args, "setInt32"))
if (!write<uint32_t>(cx, thisView, args, "setUint32"))
return false;
args.rval().setUndefined();
return true;
@ -2770,16 +2854,17 @@ JSBool
DataViewObject::fun_setUint32(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, is, setUint32Impl, args);
}
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, fun_setUint32, &DataViewClass, &thisObj))
return false;
if (!thisObj)
return true;
bool
DataViewObject::setFloat32Impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(is(args.thisv()));
Rooted<DataViewObject*> thisView(cx, &thisObj->asDataView());
Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().asDataView());
if (!write<uint32_t>(cx, thisView, args, "setUint32"))
if (!write<float>(cx, thisView, args, "setFloat32"))
return false;
args.rval().setUndefined();
return true;
@ -2789,16 +2874,17 @@ JSBool
DataViewObject::fun_setFloat32(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, is, setFloat32Impl, args);
}
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, fun_setFloat32, &DataViewClass, &thisObj))
return false;
if (!thisObj)
return true;
bool
DataViewObject::setFloat64Impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(is(args.thisv()));
Rooted<DataViewObject*> thisView(cx, &thisObj->asDataView());
Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().asDataView());
if (!write<float>(cx, thisView, args, "setFloat32"))
if (!write<double>(cx, thisView, args, "setFloat64"))
return false;
args.rval().setUndefined();
return true;
@ -2808,19 +2894,7 @@ JSBool
DataViewObject::fun_setFloat64(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, fun_setFloat64, &DataViewClass, &thisObj))
return false;
if (!thisObj)
return true;
Rooted<DataViewObject*> thisView(cx, &thisObj->asDataView());
if (!write<double>(cx, thisView, args, "setFloat64"))
return false;
args.rval().setUndefined();
return true;
return CallNonGenericMethod(cx, is, setFloat64Impl, args);
}
/***
@ -3073,9 +3147,19 @@ InitTypedArrayClass(JSContext *cx)
if (!JS_DefineFunctions(cx, proto, ArrayType::jsfuncs))
return NULL;
Rooted<JSFunction*> fun(cx);
fun =
js_NewFunction(cx, NULL,
ArrayBufferObject::createTypedArrayFromBuffer<typename ArrayType::ThisType>,
0, 0, global, NULL);
if (!fun)
return NULL;
if (!DefineConstructorAndPrototype(cx, global, ArrayType::key, ctor, proto))
return NULL;
global->setCreateArrayFromBuffer<typename ArrayType::ThisType>(fun);
return proto;
}
@ -3205,31 +3289,33 @@ JSFunctionSpec DataViewObject::jsfuncs[] = {
JS_FS_END
};
template<Value ValueGetter(DataViewObject &)>
static JSBool
DataViewGetter(JSContext *cx, unsigned argc, Value *vp)
template<Value ValueGetter(DataViewObject &view)>
bool
DataViewObject::getterImpl(JSContext *cx, CallArgs args)
{
CallArgs args = CallArgsFromVp(argc, vp);
JS_ASSERT(is(args.thisv()));
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, DataViewGetter<ValueGetter>, &DataViewClass, &thisObj))
return false;
if (!thisObj)
return true;
JS_SET_RVAL(cx, vp, ValueGetter(thisObj->asDataView()));
args.rval() = ValueGetter(args.thisv().toObject().asDataView());
return true;
}
template<Value ValueGetter(DataViewObject&)>
template<Value ValueGetter(DataViewObject &view)>
JSBool
DataViewObject::getter(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, is, getterImpl<ValueGetter>, args);
}
template<Value ValueGetter(DataViewObject &view)>
bool
DefineDataViewGetter(JSContext *cx, PropertyName *name, HandleObject proto)
DataViewObject::defineGetter(JSContext *cx, PropertyName *name, HandleObject proto)
{
RootedId id(cx, NameToId(name));
unsigned flags = JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_SHARED | JSPROP_GETTER;
Rooted<GlobalObject*> global(cx, cx->compartment->maybeGlobal());
JSObject *getter = js_NewFunction(cx, NULL, DataViewGetter<ValueGetter>, 0, 0, global, NULL);
JSObject *getter = js_NewFunction(cx, NULL, DataViewObject::getter<ValueGetter>, 0, 0, global, NULL);
if (!getter)
return false;
@ -3254,21 +3340,33 @@ DataViewObject::initClass(JSContext *cx)
if (!LinkConstructorAndPrototype(cx, ctor, proto))
return NULL;
if (!DefineDataViewGetter<bufferValue>(cx, cx->runtime->atomState.bufferAtom, proto))
if (!defineGetter<bufferValue>(cx, cx->runtime->atomState.bufferAtom, proto))
return NULL;
if (!DefineDataViewGetter<byteLengthValue>(cx, cx->runtime->atomState.byteLengthAtom, proto))
if (!defineGetter<byteLengthValue>(cx, cx->runtime->atomState.byteLengthAtom, proto))
return NULL;
if (!DefineDataViewGetter<byteOffsetValue>(cx, cx->runtime->atomState.byteOffsetAtom, proto))
if (!defineGetter<byteOffsetValue>(cx, cx->runtime->atomState.byteOffsetAtom, proto))
return NULL;
if (!JS_DefineFunctions(cx, proto, DataViewObject::jsfuncs))
return NULL;
/*
* Create a helper function to implement the craziness of
* |new DataView(new otherWindow.ArrayBuffer())|, and install it in the
* global for use by the DataView constructor.
*/
Rooted<JSFunction*> fun(cx);
fun = js_NewFunction(cx, NULL, ArrayBufferObject::createDataViewForThis, 0, 0, global, NULL);
if (!fun)
return NULL;
if (!DefineConstructorAndPrototype(cx, global, JSProto_DataView, ctor, proto))
return NULL;
global->setCreateDataViewForThis(fun);
return proto;
}
@ -3550,5 +3648,7 @@ JS_GetArrayBufferViewByteLength(JSObject *obj, JSContext *cx)
if (!(obj = CheckedUnwrap(cx, obj)))
return 0;
JS_ASSERT(obj->isTypedArray() || obj->isDataView());
return obj->isDataView() ? obj->asDataView().byteLength() : TypedArray::byteLengthValue(obj).toInt32();
return obj->isDataView()
? obj->asDataView().byteLength()
: TypedArray::byteLengthValue(obj).toInt32();
}

View File

@ -27,6 +27,9 @@ namespace js {
*/
class ArrayBufferObject : public JSObject
{
static bool byteLengthGetterImpl(JSContext *cx, CallArgs args);
static bool fun_slice_impl(JSContext *cx, CallArgs args);
public:
static Class protoClass;
static JSFunctionSpec jsfuncs[];
@ -42,6 +45,17 @@ class ArrayBufferObject : public JSObject
static JSObject *createSlice(JSContext *cx, ArrayBufferObject &arrayBuffer,
uint32_t begin, uint32_t end);
static bool createDataViewForThisImpl(JSContext *cx, CallArgs args);
static JSBool createDataViewForThis(JSContext *cx, unsigned argc, Value *vp);
template<typename T>
static bool
createTypedArrayFromBufferImpl(JSContext *cx, CallArgs args);
template<typename T>
static JSBool
createTypedArrayFromBuffer(JSContext *cx, unsigned argc, Value *vp);
static void
obj_trace(JSTracer *trc, JSObject *obj);
@ -140,6 +154,7 @@ class ArrayBufferObject : public JSObject
* ArrayBuffer.prototype and neutered ArrayBuffers.
*/
inline bool hasData() const;
};
/*
@ -271,6 +286,20 @@ class DataViewObject : public JSObject
static const size_t BYTELENGTH_SLOT = 1;
static const size_t BUFFER_SLOT = 2;
static inline bool is(const Value &v);
template<Value ValueGetter(DataViewObject &view)>
static bool
getterImpl(JSContext *cx, CallArgs args);
template<Value ValueGetter(DataViewObject &view)>
static JSBool
getter(JSContext *cx, unsigned argc, Value *vp);
template<Value ValueGetter(DataViewObject &view)>
static bool
defineGetter(JSContext *cx, PropertyName *name, HandleObject proto);
public:
static const size_t RESERVED_SLOTS = 3;
@ -286,22 +315,54 @@ class DataViewObject : public JSObject
create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength,
Handle<ArrayBufferObject*> arrayBuffer, JSObject *proto);
static bool getInt8Impl(JSContext *cx, CallArgs args);
static JSBool fun_getInt8(JSContext *cx, unsigned argc, Value *vp);
static bool getUint8Impl(JSContext *cx, CallArgs args);
static JSBool fun_getUint8(JSContext *cx, unsigned argc, Value *vp);
static bool getInt16Impl(JSContext *cx, CallArgs args);
static JSBool fun_getInt16(JSContext *cx, unsigned argc, Value *vp);
static bool getUint16Impl(JSContext *cx, CallArgs args);
static JSBool fun_getUint16(JSContext *cx, unsigned argc, Value *vp);
static bool getInt32Impl(JSContext *cx, CallArgs args);
static JSBool fun_getInt32(JSContext *cx, unsigned argc, Value *vp);
static bool getUint32Impl(JSContext *cx, CallArgs args);
static JSBool fun_getUint32(JSContext *cx, unsigned argc, Value *vp);
static bool getFloat32Impl(JSContext *cx, CallArgs args);
static JSBool fun_getFloat32(JSContext *cx, unsigned argc, Value *vp);
static bool getFloat64Impl(JSContext *cx, CallArgs args);
static JSBool fun_getFloat64(JSContext *cx, unsigned argc, Value *vp);
static bool setInt8Impl(JSContext *cx, CallArgs args);
static JSBool fun_setInt8(JSContext *cx, unsigned argc, Value *vp);
static bool setUint8Impl(JSContext *cx, CallArgs args);
static JSBool fun_setUint8(JSContext *cx, unsigned argc, Value *vp);
static bool setInt16Impl(JSContext *cx, CallArgs args);
static JSBool fun_setInt16(JSContext *cx, unsigned argc, Value *vp);
static bool setUint16Impl(JSContext *cx, CallArgs args);
static JSBool fun_setUint16(JSContext *cx, unsigned argc, Value *vp);
static bool setInt32Impl(JSContext *cx, CallArgs args);
static JSBool fun_setInt32(JSContext *cx, unsigned argc, Value *vp);
static bool setUint32Impl(JSContext *cx, CallArgs args);
static JSBool fun_setUint32(JSContext *cx, unsigned argc, Value *vp);
static bool setFloat32Impl(JSContext *cx, CallArgs args);
static JSBool fun_setFloat32(JSContext *cx, unsigned argc, Value *vp);
static bool setFloat64Impl(JSContext *cx, CallArgs args);
static JSBool fun_setFloat64(JSContext *cx, unsigned argc, Value *vp);
inline uint32_t byteLength();
inline uint32_t byteOffset();
inline JSObject & arrayBuffer();

View File

@ -59,57 +59,67 @@ ClampIntForUint8Array(int32_t x)
}
inline Value
TypedArray::lengthValue(JSObject *obj) {
TypedArray::lengthValue(JSObject *obj)
{
JS_ASSERT(obj->isTypedArray());
return obj->getFixedSlot(FIELD_LENGTH);
}
inline uint32_t
TypedArray::length(JSObject *obj) {
TypedArray::length(JSObject *obj)
{
return lengthValue(obj).toInt32();
}
inline Value
TypedArray::byteOffsetValue(JSObject *obj) {
TypedArray::byteOffsetValue(JSObject *obj)
{
JS_ASSERT(obj->isTypedArray());
return obj->getFixedSlot(FIELD_BYTEOFFSET);
}
inline uint32_t
TypedArray::byteOffset(JSObject *obj) {
TypedArray::byteOffset(JSObject *obj)
{
return byteOffsetValue(obj).toInt32();
}
inline Value
TypedArray::byteLengthValue(JSObject *obj) {
TypedArray::byteLengthValue(JSObject *obj)
{
JS_ASSERT(obj->isTypedArray());
return obj->getFixedSlot(FIELD_BYTELENGTH);
}
inline uint32_t
TypedArray::byteLength(JSObject *obj) {
TypedArray::byteLength(JSObject *obj)
{
return byteLengthValue(obj).toInt32();
}
inline uint32_t
TypedArray::type(JSObject *obj) {
TypedArray::type(JSObject *obj)
{
JS_ASSERT(obj->isTypedArray());
return obj->getFixedSlot(FIELD_TYPE).toInt32();
}
inline Value
TypedArray::bufferValue(JSObject *obj) {
TypedArray::bufferValue(JSObject *obj)
{
JS_ASSERT(obj->isTypedArray());
return obj->getFixedSlot(FIELD_BUFFER);
}
inline ArrayBufferObject *
TypedArray::buffer(JSObject *obj) {
TypedArray::buffer(JSObject *obj)
{
return &bufferValue(obj).toObject().asArrayBuffer();
}
inline void *
TypedArray::viewData(JSObject *obj) {
TypedArray::viewData(JSObject *obj)
{
JS_ASSERT(obj->isTypedArray());
return (void *)obj->getPrivate(NUM_FIXED_SLOTS);
}
@ -141,6 +151,12 @@ TypedArray::slotWidth(JSObject *obj) {
return slotWidth(type(obj));
}
bool
DataViewObject::is(const Value &v)
{
return v.isObject() && v.toObject().hasClass(&DataViewClass);
}
inline DataViewObject *
DataViewObject::create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength,
Handle<ArrayBufferObject*> arrayBuffer, JSObject *proto)

View File

@ -19,8 +19,6 @@
#include "jsgcinlines.h"
#include "jsobjinlines.h"
#include "vm/MethodGuard-inl.h"
using namespace js;
namespace js {
@ -120,16 +118,16 @@ GetKeyArg(JSContext *cx, CallArgs &args)
return JS_UnwrapObject(&key);
}
static JSBool
WeakMap_has(JSContext *cx, unsigned argc, Value *vp)
static bool
IsWeakMap(const Value &v)
{
CallArgs args = CallArgsFromVp(argc, vp);
return v.isObject() && v.toObject().hasClass(&WeakMapClass);
}
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, WeakMap_has, &WeakMapClass, &thisObj))
return false;
if (!thisObj)
return true;
static bool
WeakMap_has_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(IsWeakMap(args.thisv()));
if (args.length() < 1) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
@ -140,10 +138,8 @@ WeakMap_has(JSContext *cx, unsigned argc, Value *vp)
if (!key)
return false;
ObjectValueMap *map = GetObjectMap(thisObj);
if (map) {
ObjectValueMap::Ptr ptr = map->lookup(key);
if (ptr) {
if (ObjectValueMap *map = GetObjectMap(&args.thisv().toObject())) {
if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
args.rval() = BooleanValue(true);
return true;
}
@ -154,15 +150,16 @@ WeakMap_has(JSContext *cx, unsigned argc, Value *vp)
}
static JSBool
WeakMap_get(JSContext *cx, unsigned argc, Value *vp)
WeakMap_has(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsWeakMap, WeakMap_has_impl, args);
}
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, WeakMap_get, &WeakMapClass, &thisObj))
return false;
if (!thisObj)
return true;
static bool
WeakMap_get_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(IsWeakMap(args.thisv()));
if (args.length() < 1) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
@ -173,10 +170,8 @@ WeakMap_get(JSContext *cx, unsigned argc, Value *vp)
if (!key)
return false;
ObjectValueMap *map = GetObjectMap(thisObj);
if (map) {
ObjectValueMap::Ptr ptr = map->lookup(key);
if (ptr) {
if (ObjectValueMap *map = GetObjectMap(&args.thisv().toObject())) {
if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
args.rval() = ptr->value;
return true;
}
@ -187,15 +182,16 @@ WeakMap_get(JSContext *cx, unsigned argc, Value *vp)
}
static JSBool
WeakMap_delete(JSContext *cx, unsigned argc, Value *vp)
WeakMap_get(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsWeakMap, WeakMap_get_impl, args);
}
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, WeakMap_delete, &WeakMapClass, &thisObj))
return false;
if (!thisObj)
return true;
static bool
WeakMap_delete_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(IsWeakMap(args.thisv()));
if (args.length() < 1) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
@ -206,10 +202,8 @@ WeakMap_delete(JSContext *cx, unsigned argc, Value *vp)
if (!key)
return false;
ObjectValueMap *map = GetObjectMap(thisObj);
if (map) {
ObjectValueMap::Ptr ptr = map->lookup(key);
if (ptr) {
if (ObjectValueMap *map = GetObjectMap(&args.thisv().toObject())) {
if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
map->remove(ptr);
args.rval() = BooleanValue(true);
return true;
@ -221,15 +215,16 @@ WeakMap_delete(JSContext *cx, unsigned argc, Value *vp)
}
static JSBool
WeakMap_set(JSContext *cx, unsigned argc, Value *vp)
WeakMap_delete(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsWeakMap, WeakMap_delete_impl, args);
}
JSObject *thisObj;
if (!NonGenericMethodGuard(cx, args, WeakMap_set, &WeakMapClass, &thisObj))
return false;
if (!thisObj)
return true;
static bool
WeakMap_set_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(IsWeakMap(args.thisv()));
if (args.length() < 1) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
@ -242,18 +237,22 @@ WeakMap_set(JSContext *cx, unsigned argc, Value *vp)
Value value = (args.length() > 1) ? args[1] : UndefinedValue();
Rooted<JSObject*> thisObj(cx, &args.thisv().toObject());
ObjectValueMap *map = GetObjectMap(thisObj);
if (!map) {
map = cx->new_<ObjectValueMap>(cx, thisObj);
map = cx->new_<ObjectValueMap>(cx, thisObj.get());
if (!map->init()) {
cx->delete_(map);
goto out_of_memory;
JS_ReportOutOfMemory(cx);
return false;
}
thisObj->setPrivate(map);
}
if (!map->put(key, value))
goto out_of_memory;
if (!map->put(key, value)) {
JS_ReportOutOfMemory(cx);
return false;
}
// Preserve wrapped native keys to prevent wrapper optimization.
if (key->getClass()->ext.isWrappedNative) {
@ -265,10 +264,13 @@ WeakMap_set(JSContext *cx, unsigned argc, Value *vp)
args.rval().setUndefined();
return true;
}
out_of_memory:
JS_ReportOutOfMemory(cx);
return false;
static JSBool
WeakMap_set(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsWeakMap, WeakMap_set_impl, args);
}
JS_FRIEND_API(JSBool)

View File

@ -304,10 +304,11 @@ DirectWrapper::construct(JSContext *cx, JSObject *wrapper, unsigned argc, Value
}
bool
DirectWrapper::nativeCall(JSContext *cx, JSObject *wrapper, Class *clasp, Native native, CallArgs args)
DirectWrapper::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args)
{
const jsid id = JSID_VOID;
CHECKED(IndirectProxyHandler::nativeCall(cx, wrapper, clasp, native, args), CALL);
Rooted<JSObject*> wrapper(cx, &args.thisv().toObject());
CHECKED(IndirectProxyHandler::nativeCall(cx, test, impl, args), CALL);
}
bool
@ -715,12 +716,14 @@ CrossCompartmentWrapper::construct(JSContext *cx, JSObject *wrapper_, unsigned a
}
bool
CrossCompartmentWrapper::nativeCall(JSContext *cx, JSObject *wrapper, Class *clasp, Native native, CallArgs srcArgs)
CrossCompartmentWrapper::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
CallArgs srcArgs)
{
JS_ASSERT(srcArgs.thisv().isMagic(JS_IS_CONSTRUCTING) || &srcArgs.thisv().toObject() == wrapper);
JS_ASSERT(!UnwrapObject(wrapper)->isCrossCompartmentWrapper());
Rooted<JSObject*> wrapper(cx, &srcArgs.thisv().toObject());
JS_ASSERT(srcArgs.thisv().isMagic(JS_IS_CONSTRUCTING) ||
!UnwrapObject(wrapper)->isCrossCompartmentWrapper());
JSObject *wrapped = wrappedObject(wrapper);
Rooted<JSObject*> wrapped(cx, wrappedObject(wrapper));
AutoCompartment call(cx, wrapped);
if (!call.enter())
return false;
@ -732,13 +735,13 @@ CrossCompartmentWrapper::nativeCall(JSContext *cx, JSObject *wrapper, Class *cla
Value *src = srcArgs.base();
Value *srcend = srcArgs.array() + srcArgs.length();
Value *dst = dstArgs.base();
for (; src != srcend; ++src, ++dst) {
for (; src < srcend; ++src, ++dst) {
*dst = *src;
if (!call.destination->wrap(cx, dst))
return false;
}
if (!CallJSNative(cx, native, dstArgs))
if (!CallNonGenericMethod(cx, test, impl, dstArgs))
return false;
srcArgs.rval() = dstArgs.rval();
@ -828,14 +831,14 @@ SecurityWrapper<Base>::SecurityWrapper(unsigned flags)
template <class Base>
bool
SecurityWrapper<Base>::nativeCall(JSContext *cx, JSObject *wrapper, Class *clasp, Native native,
SecurityWrapper<Base>::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
CallArgs args)
{
/*
* Let this through until compartment-per-global lets us have stronger
* invariants wrt document.domain (bug 714547).
*/
return Base::nativeCall(cx, wrapper, clasp, native, args);
return Base::nativeCall(cx, test, impl, args);
}
template <class Base>
@ -882,7 +885,8 @@ class JS_FRIEND_API(DeadObjectProxy) : public BaseProxyHandler
/* Spidermonkey extensions. */
virtual bool call(JSContext *cx, JSObject *proxy, unsigned argc, Value *vp);
virtual bool construct(JSContext *cx, JSObject *proxy, unsigned argc, Value *argv, Value *rval);
virtual bool nativeCall(JSContext *cx, JSObject *proxy, Class *clasp, Native native, CallArgs args);
virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
CallArgs args) MOZ_OVERRIDE;
virtual bool hasInstance(JSContext *cx, JSObject *proxy, const Value *vp, bool *bp);
virtual bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx);
virtual JSString *obj_toString(JSContext *cx, JSObject *proxy);
@ -967,8 +971,7 @@ DeadObjectProxy::construct(JSContext *cx, JSObject *wrapper, unsigned argc,
}
bool
DeadObjectProxy::nativeCall(JSContext *cx, JSObject *wrapper, Class *clasp,
Native native, CallArgs args)
DeadObjectProxy::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args)
{
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEAD_OBJECT);
return false;

View File

@ -202,7 +202,8 @@ class JS_FRIEND_API(DirectWrapper) : public Wrapper, public DirectProxyHandler
/* Spidermonkey extensions. */
virtual bool call(JSContext *cx, JSObject *wrapper, unsigned argc, Value *vp) MOZ_OVERRIDE;
virtual bool construct(JSContext *cx, JSObject *wrapper, unsigned argc, Value *argv, Value *rval) MOZ_OVERRIDE;
virtual bool nativeCall(JSContext *cx, JSObject *wrapper, Class *clasp, Native native, CallArgs args) MOZ_OVERRIDE;
virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
CallArgs args) MOZ_OVERRIDE;
virtual bool hasInstance(JSContext *cx, JSObject *wrapper, const Value *vp, bool *bp) MOZ_OVERRIDE;
virtual JSString *obj_toString(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE;
virtual JSString *fun_toString(JSContext *cx, JSObject *wrapper, unsigned indent) MOZ_OVERRIDE;
@ -243,7 +244,8 @@ class JS_FRIEND_API(CrossCompartmentWrapper) : public DirectWrapper
/* Spidermonkey extensions. */
virtual bool call(JSContext *cx, JSObject *wrapper, unsigned argc, Value *vp) MOZ_OVERRIDE;
virtual bool construct(JSContext *cx, JSObject *wrapper, unsigned argc, Value *argv, Value *rval) MOZ_OVERRIDE;
virtual bool nativeCall(JSContext *cx, JSObject *wrapper, Class *clasp, Native native, CallArgs args) MOZ_OVERRIDE;
virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
CallArgs args) MOZ_OVERRIDE;
virtual bool hasInstance(JSContext *cx, JSObject *wrapper, const Value *vp, bool *bp) MOZ_OVERRIDE;
virtual JSString *obj_toString(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE;
virtual JSString *fun_toString(JSContext *cx, JSObject *wrapper, unsigned indent) MOZ_OVERRIDE;
@ -268,7 +270,8 @@ class JS_FRIEND_API(SecurityWrapper) : public Base
public:
SecurityWrapper(unsigned flags);
virtual bool nativeCall(JSContext *cx, JSObject *wrapper, Class *clasp, Native native, CallArgs args) MOZ_OVERRIDE;
virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
CallArgs args) MOZ_OVERRIDE;
virtual bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx) MOZ_OVERRIDE;
virtual bool regexp_toShared(JSContext *cx, JSObject *proxy, RegExpGuard *g) MOZ_OVERRIDE;
};

View File

@ -41,7 +41,6 @@ size_t sE4XObjectsCreated = 0;
#include "frontend/TokenStream.h"
#include "gc/Marking.h"
#include "vm/GlobalObject.h"
#include "vm/MethodGuard.h"
#include "vm/StringBuffer.h"
#include "jsatominlines.h"

View File

@ -35,7 +35,7 @@ l = <><a>text</a></>;
actual = l.charAt(0);
TEST(4, expect, actual);
expect = 'TypeError: String.prototype.toString called on incompatible XML';
expect = "TypeError";
delete XML.prototype.function::toString;
var xml = <a>TEXT</a>;
@ -44,13 +44,13 @@ delete Object.prototype.toString;
try {
actual = xml.toString();
} catch(ex) {
actual = ex + '';
actual = ex.name;
} finally {
Object.prototype.toString = saveToString;
}
TEST(7, expect, actual);
expect = 'TypeError: String.prototype.toString called on incompatible XML';
expect = "TypeError";
try
{
var x = <a><name/></a>;
@ -59,7 +59,7 @@ try
}
catch(ex)
{
actual = ex + '';
actual = ex.name;
}
TEST(8, expect, actual);
@ -71,7 +71,7 @@ try
}
catch(ex)
{
actual = ex + '';
actual = ex.name;
}
TEST(9, expect, actual);

View File

@ -34,14 +34,14 @@ function test()
{
try
{
expect = 'TypeError: Array.prototype.toSource called on incompatible String';
actual = Array.prototype.toSource.call((new String('foo')));
Array.prototype.toSource.call(new String('foo'));
throw new Error("didn't throw");
}
catch(ex)
{
actual = ex + '';
assertEq(ex instanceof TypeError, true,
"wrong error thrown: expected TypeError, got " + ex);
}
assertEq(actual, expect, summary);
}
reportCompare(true, true, "Tests complete");

View File

@ -23,7 +23,7 @@ function allTests()
assertEq(d == DS, true);
var d2 = new Date(2010, 1, 1);
d2.valueOf = function() { return 17; };
d2.valueOf = function() { assertEq(arguments.length, 0); return 17; };
assertEq(d2 == DS, true);
var d3 = new Date(2010, 1, 1);
@ -36,7 +36,7 @@ function allTests()
assertEq(d == DS, true);
var d2 = new Date(2010, 1, 1);
d2.valueOf = function() { return 17; };
d2.valueOf = function() { assertEq(arguments.length, 0); return 17; };
assertEq(d2 == DS, true);
var d3 = new Date(2010, 1, 1);
@ -56,7 +56,7 @@ function allTests()
assertEq(d2 + 3, 9 + 3);
var d3 = new Date(2010, 1, 1);
d3.valueOf = function() { return 17; };
d3.valueOf = function() { assertEq(arguments.length, 0); return 17; };
assertEq(d3 + 5, DS + "5");
function testDateNumberAddition()
@ -69,7 +69,7 @@ function allTests()
assertEq(d2 + 3, 9 + 3);
var d3 = new Date(2010, 1, 1);
d3.valueOf = function() { return 17; };
d3.valueOf = function() { assertEq(arguments.length, 0); return 17; };
assertEq(d3 + 5, DS + "5");
}
testDateNumberAddition();
@ -85,7 +85,7 @@ function allTests()
assertEq(d2 + d2, 10);
var d3 = new Date(2010, 1, 1);
d3.valueOf = function() { return 8.5; };
d3.valueOf = function() { assertEq(arguments.length, 0); return 8.5; };
assertEq(d3 + d3, DS + DS);
function testDateDateAddition()
@ -98,7 +98,7 @@ function allTests()
assertEq(d2 + d2, 10);
var d3 = new Date(2010, 1, 1);
d3.valueOf = function() { return 8.5; };
d3.valueOf = function() { assertEq(arguments.length, 0); return 8.5; };
assertEq(d3 + d3, DS + DS);
}
testDateDateAddition();
@ -113,7 +113,7 @@ function allTests()
assertEq(obj[d], 17);
var d2 = new Date(2010, 1, 1);
d2.valueOf = function() { return 8; }
d2.valueOf = function() { assertEq(arguments.length, 0); return 8; }
assertEq(obj[d2], 17);
var d3 = new Date(2010, 1, 1);
@ -129,7 +129,7 @@ function allTests()
assertEq(obj[d], 17);
var d2 = new Date(2010, 1, 1);
d2.valueOf = function() { return 8; }
d2.valueOf = function() { assertEq(arguments.length, 0); return 8; }
assertEq(obj[d2], 17);
var d3 = new Date(2010, 1, 1);
@ -152,7 +152,7 @@ function allTests()
assertEq(d2 in { baz: 42 }, true);
var d3 = new Date(2010, 1, 1);
d3.valueOf = function() { return "quux"; };
d3.valueOf = function() { assertEq(arguments.length, 0); return "quux"; };
assertEq(d3 in obj, true);
function testInOperatorName()
@ -168,7 +168,7 @@ function allTests()
assertEq(d2 in { baz: 42 }, true);
var d3 = new Date(2010, 1, 1);
d3.valueOf = function() { return "quux"; };
d3.valueOf = function() { assertEq(arguments.length, 0); return "quux"; };
assertEq(d3 in obj, true);
}
testInOperatorName();

View File

@ -3,46 +3,35 @@
* 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/. */
//-----------------------------------------------------------------------------
var BUGNUMBER = 380933;
var summary = 'Do not assert with uneval object with setter with modified proto';
var actual = '';
var expect = '';
printBugNumber(BUGNUMBER);
printStatus (summary);
//-----------------------------------------------------------------------------
test();
//-----------------------------------------------------------------------------
var f = (function(){});
var y =
Object.defineProperty({}, "p",
{
get: f,
enumerable: true,
configurable: true
});
f.__proto__ = [];
function test()
try
{
enterFunc ('test');
printBugNumber(BUGNUMBER);
printStatus (summary);
var f = (function(){});
var y =
Object.defineProperty({}, "p",
{
get: f,
enumerable: true,
configurable: true
});
f.__proto__ = [];
expect = /TypeError: Array.prototype.toSource called on incompatible Function/;
try
{
uneval(y);
actual = 'No Error';
}
catch(ex)
{
actual = ex + '';
}
reportMatch(expect, actual, summary);
exitFunc ('test');
uneval(y);
throw new Error("didn't throw");
}
catch(ex)
{
assertEq(ex instanceof TypeError, true,
"wrong exception thrown: expected TypeError, got " + ex);
}
if (typeof reportCompare === "function")
reportCompare(true, true);
print("Tests complete");

View File

@ -10,7 +10,6 @@
#include "jsinterp.h"
#include "vm/GlobalObject.h"
#include "vm/MethodGuard.h"
#include "vm/Stack.h"
#include "vm/Xdr.h"

View File

@ -59,6 +59,153 @@ GlobalObject::setOriginalEval(JSObject *evalobj)
setSlot(EVAL, ObjectValue(*evalobj));
}
void
GlobalObject::setCreateArrayFromBufferHelper(uint32_t slot, Handle<JSFunction*> fun)
{
JS_ASSERT(getSlotRef(slot).isUndefined());
setSlot(slot, ObjectValue(*fun));
}
void
GlobalObject::setBooleanValueOf(Handle<JSFunction*> valueOfFun)
{
JS_ASSERT(getSlotRef(BOOLEAN_VALUEOF).isUndefined());
setSlot(BOOLEAN_VALUEOF, ObjectValue(*valueOfFun));
}
void
GlobalObject::setCreateDataViewForThis(Handle<JSFunction*> fun)
{
JS_ASSERT(getSlotRef(CREATE_DATAVIEW_FOR_THIS).isUndefined());
setSlot(CREATE_DATAVIEW_FOR_THIS, ObjectValue(*fun));
}
template<>
inline void
GlobalObject::setCreateArrayFromBuffer<uint8_t>(Handle<JSFunction*> fun)
{
setCreateArrayFromBufferHelper(FROM_BUFFER_UINT8, fun);
}
template<>
inline void
GlobalObject::setCreateArrayFromBuffer<int8_t>(Handle<JSFunction*> fun)
{
setCreateArrayFromBufferHelper(FROM_BUFFER_INT8, fun);
}
template<>
inline void
GlobalObject::setCreateArrayFromBuffer<uint16_t>(Handle<JSFunction*> fun)
{
setCreateArrayFromBufferHelper(FROM_BUFFER_UINT16, fun);
}
template<>
inline void
GlobalObject::setCreateArrayFromBuffer<int16_t>(Handle<JSFunction*> fun)
{
setCreateArrayFromBufferHelper(FROM_BUFFER_INT16, fun);
}
template<>
inline void
GlobalObject::setCreateArrayFromBuffer<uint32_t>(Handle<JSFunction*> fun)
{
setCreateArrayFromBufferHelper(FROM_BUFFER_UINT32, fun);
}
template<>
inline void
GlobalObject::setCreateArrayFromBuffer<int32_t>(Handle<JSFunction*> fun)
{
setCreateArrayFromBufferHelper(FROM_BUFFER_INT32, fun);
}
template<>
inline void
GlobalObject::setCreateArrayFromBuffer<float>(Handle<JSFunction*> fun)
{
setCreateArrayFromBufferHelper(FROM_BUFFER_FLOAT32, fun);
}
template<>
inline void
GlobalObject::setCreateArrayFromBuffer<double>(Handle<JSFunction*> fun)
{
setCreateArrayFromBufferHelper(FROM_BUFFER_FLOAT64, fun);
}
template<>
inline void
GlobalObject::setCreateArrayFromBuffer<uint8_clamped>(Handle<JSFunction*> fun)
{
setCreateArrayFromBufferHelper(FROM_BUFFER_UINT8CLAMPED, fun);
}
template<>
inline Value
GlobalObject::createArrayFromBuffer<uint8_t>() const
{
return createArrayFromBufferHelper(FROM_BUFFER_UINT8);
}
template<>
inline Value
GlobalObject::createArrayFromBuffer<int8_t>() const
{
return createArrayFromBufferHelper(FROM_BUFFER_INT8);
}
template<>
inline Value
GlobalObject::createArrayFromBuffer<uint16_t>() const
{
return createArrayFromBufferHelper(FROM_BUFFER_UINT16);
}
template<>
inline Value
GlobalObject::createArrayFromBuffer<int16_t>() const
{
return createArrayFromBufferHelper(FROM_BUFFER_INT16);
}
template<>
inline Value
GlobalObject::createArrayFromBuffer<uint32_t>() const
{
return createArrayFromBufferHelper(FROM_BUFFER_UINT32);
}
template<>
inline Value
GlobalObject::createArrayFromBuffer<int32_t>() const
{
return createArrayFromBufferHelper(FROM_BUFFER_INT32);
}
template<>
inline Value
GlobalObject::createArrayFromBuffer<float>() const
{
return createArrayFromBufferHelper(FROM_BUFFER_FLOAT32);
}
template<>
inline Value
GlobalObject::createArrayFromBuffer<double>() const
{
return createArrayFromBufferHelper(FROM_BUFFER_FLOAT64);
}
template<>
inline Value
GlobalObject::createArrayFromBuffer<uint8_clamped>() const
{
return createArrayFromBufferHelper(FROM_BUFFER_UINT8CLAMPED);
}
} // namespace js
#endif

View File

@ -315,11 +315,13 @@ GlobalObject::clear(JSContext *cx)
setSlot(RUNTIME_CODEGEN_ENABLED, UndefinedValue());
/*
* Clear the original-eval and [[ThrowTypeError]] slots, in case throwing
* trying to execute a script for this global must reinitialize standard
* classes. See bug 470150.
* Clear all slots storing function values, in case throwing trying to
* execute a script for this global must reinitialize standard classes.
* See bug 470150.
*/
setSlot(BOOLEAN_VALUEOF, UndefinedValue());
setSlot(EVAL, UndefinedValue());
setSlot(CREATE_DATAVIEW_FOR_THIS, UndefinedValue());
setSlot(THROWTYPEERROR, UndefinedValue());
/*

View File

@ -69,14 +69,32 @@ class GlobalObject : public JSObject
*/
static const unsigned STANDARD_CLASS_SLOTS = JSProto_LIMIT * 3;
/* Various function values needed by the engine. */
static const unsigned BOOLEAN_VALUEOF = STANDARD_CLASS_SLOTS;
static const unsigned EVAL = BOOLEAN_VALUEOF + 1;
static const unsigned CREATE_DATAVIEW_FOR_THIS = EVAL + 1;
static const unsigned THROWTYPEERROR = CREATE_DATAVIEW_FOR_THIS + 1;
/*
* Instances of the internal createArrayFromBuffer function used by the
* typed array code, one per typed array element type.
*/
static const unsigned FROM_BUFFER_UINT8 = THROWTYPEERROR + 1;
static const unsigned FROM_BUFFER_INT8 = FROM_BUFFER_UINT8 + 1;
static const unsigned FROM_BUFFER_UINT16 = FROM_BUFFER_INT8 + 1;
static const unsigned FROM_BUFFER_INT16 = FROM_BUFFER_UINT16 + 1;
static const unsigned FROM_BUFFER_UINT32 = FROM_BUFFER_INT16 + 1;
static const unsigned FROM_BUFFER_INT32 = FROM_BUFFER_UINT32 + 1;
static const unsigned FROM_BUFFER_FLOAT32 = FROM_BUFFER_INT32 + 1;
static const unsigned FROM_BUFFER_FLOAT64 = FROM_BUFFER_FLOAT32 + 1;
static const unsigned FROM_BUFFER_UINT8CLAMPED = FROM_BUFFER_FLOAT64 + 1;
/* One-off properties stored after slots for built-ins. */
static const unsigned THROWTYPEERROR = STANDARD_CLASS_SLOTS;
static const unsigned GENERATOR_PROTO = THROWTYPEERROR + 1;
static const unsigned GENERATOR_PROTO = FROM_BUFFER_UINT8CLAMPED + 1;
static const unsigned REGEXP_STATICS = GENERATOR_PROTO + 1;
static const unsigned FUNCTION_NS = REGEXP_STATICS + 1;
static const unsigned RUNTIME_CODEGEN_ENABLED = FUNCTION_NS + 1;
static const unsigned EVAL = RUNTIME_CODEGEN_ENABLED + 1;
static const unsigned FLAGS = EVAL + 1;
static const unsigned FLAGS = RUNTIME_CODEGEN_ENABLED + 1;
static const unsigned DEBUGGERS = FLAGS + 1;
/* Total reserved-slot count for global objects. */
@ -110,7 +128,6 @@ class GlobalObject : public JSObject
inline void setFunctionClassDetails(JSFunction *ctor, JSObject *proto);
inline void setThrowTypeError(JSFunction *fun);
inline void setOriginalEval(JSObject *evalobj);
Value getConstructor(JSProtoKey key) const {
@ -157,6 +174,30 @@ class GlobalObject : public JSObject
bool errorClassesInitialized() const {
return classIsInitialized(JSProto_Error);
}
bool dataViewClassInitialized() const {
return classIsInitialized(JSProto_DataView);
}
bool typedArrayClassesInitialized() const {
// This alias exists only for clarity: in reality all the typed array
// classes constitute a (semi-)coherent whole.
return classIsInitialized(JSProto_DataView);
}
Value createArrayFromBufferHelper(uint32_t slot) const {
JS_ASSERT(typedArrayClassesInitialized());
JS_ASSERT(FROM_BUFFER_UINT8 <= slot && slot <= FROM_BUFFER_UINT8CLAMPED);
return getSlot(slot);
}
inline void setCreateArrayFromBufferHelper(uint32_t slot, Handle<JSFunction*> fun);
public:
/* XXX Privatize me! */
inline void setBooleanValueOf(Handle<JSFunction*> valueOfFun);
inline void setCreateDataViewForThis(Handle<JSFunction*> fun);
template<typename T>
inline void setCreateArrayFromBuffer(Handle<JSFunction*> fun);
public:
static GlobalObject *create(JSContext *cx, Class *clasp);
@ -277,6 +318,15 @@ class GlobalObject : public JSObject
return &self->getSlot(GENERATOR_PROTO).toObject();
}
JSObject *getOrCreateDataViewPrototype(JSContext *cx) {
if (dataViewClassInitialized())
return &getPrototype(JSProto_DataView).toObject();
Rooted<GlobalObject*> self(cx, this);
if (!js_InitTypedArrayClasses(cx, this))
return NULL;
return &self->getPrototype(JSProto_DataView).toObject();
}
inline RegExpStatics *getRegExpStatics() const;
JSObject *getThrowTypeError() const {
@ -284,6 +334,19 @@ class GlobalObject : public JSObject
return &getSlot(THROWTYPEERROR).toObject();
}
Value booleanValueOf() const {
JS_ASSERT(booleanClassInitialized());
return getSlot(BOOLEAN_VALUEOF);
}
Value createDataViewForThis() const {
JS_ASSERT(dataViewClassInitialized());
return getSlot(CREATE_DATAVIEW_FOR_THIS);
}
template<typename T>
inline Value createArrayFromBuffer() const;
void clear(JSContext *cx);
bool isCleared() const {

View File

@ -1,94 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et 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/. */
#ifndef MethodGuard_inl_h___
#define MethodGuard_inl_h___
#include "jsobj.h"
#include "MethodGuard.h"
#include "jsobjinlines.h"
#include "BooleanObject-inl.h"
#include "NumberObject-inl.h"
#include "StringObject-inl.h"
namespace js {
namespace detail {
template<typename T> class PrimitiveBehavior { };
template<>
class PrimitiveBehavior<bool> {
public:
static inline bool isType(const Value &v) { return v.isBoolean(); }
static inline bool extract(const Value &v) { return v.toBoolean(); }
static inline bool extract(JSObject &obj) { return obj.asBoolean().unbox(); }
static inline Class *getClass() { return &BooleanClass; }
};
template<>
class PrimitiveBehavior<double> {
public:
static inline bool isType(const Value &v) { return v.isNumber(); }
static inline double extract(const Value &v) { return v.toNumber(); }
static inline double extract(JSObject &obj) { return obj.asNumber().unbox(); }
static inline Class *getClass() { return &NumberClass; }
};
template<>
class PrimitiveBehavior<JSString *> {
public:
static inline bool isType(const Value &v) { return v.isString(); }
static inline JSString *extract(const Value &v) { return v.toString(); }
static inline JSString *extract(JSObject &obj) { return obj.asString().unbox(); }
static inline Class *getClass() { return &StringClass; }
};
} /* namespace detail */
inline bool
NonGenericMethodGuard(JSContext *cx, CallArgs args, Native native, Class *clasp, JSObject **thisObj)
{
const Value &thisv = args.thisv();
if (thisv.isObject()) {
JSObject &obj = thisv.toObject();
if (obj.getClass() == clasp) {
*thisObj = &obj;
return true;
}
}
*thisObj = NULL;
return HandleNonGenericMethodClassMismatch(cx, args, native, clasp);
}
template <typename T>
inline bool
BoxedPrimitiveMethodGuard(JSContext *cx, CallArgs args, Native native, T *v, bool *ok)
{
typedef detail::PrimitiveBehavior<T> Behavior;
const Value &thisv = args.thisv();
if (Behavior::isType(thisv)) {
*v = Behavior::extract(thisv);
return true;
}
JSObject *thisObj;
*ok = NonGenericMethodGuard(cx, args, native, Behavior::getClass(), &thisObj);
if (!*ok || !thisObj)
return false;
*v = Behavior::extract(*thisObj);
return true;
}
} /* namespace js */
#endif /* MethodGuard_inl_h___ */

View File

@ -1,59 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et 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 "MethodGuard.h"
#include "Stack.h"
#include "jsfuninlines.h"
#include "jsobjinlines.h"
using namespace js;
void
js::ReportIncompatibleMethod(JSContext *cx, CallReceiver call, Class *clasp)
{
Value &thisv = call.thisv();
#ifdef DEBUG
if (thisv.isObject()) {
JS_ASSERT(thisv.toObject().getClass() != clasp ||
!thisv.toObject().getProto() ||
thisv.toObject().getProto()->getClass() != clasp);
} else if (thisv.isString()) {
JS_ASSERT(clasp != &StringClass);
} else if (thisv.isNumber()) {
JS_ASSERT(clasp != &NumberClass);
} else if (thisv.isBoolean()) {
JS_ASSERT(clasp != &BooleanClass);
} else {
JS_ASSERT(thisv.isUndefined() || thisv.isNull());
}
#endif
if (JSFunction *fun = ReportIfNotFunction(cx, call.calleev())) {
JSAutoByteString funNameBytes;
if (const char *funName = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
clasp->name, funName, InformalValueTypeName(thisv));
}
}
}
bool
js::HandleNonGenericMethodClassMismatch(JSContext *cx, CallArgs args, Native native, Class *clasp)
{
if (args.thisv().isObject()) {
JSObject &thisObj = args.thisv().toObject();
if (thisObj.isProxy())
return Proxy::nativeCall(cx, &thisObj, clasp, native, args);
}
ReportIncompatibleMethod(cx, args, clasp);
return false;
}

View File

@ -1,89 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et 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/. */
/* Method prologue type-checking and unwrapping of the this parameter. */
#ifndef MethodGuard_h___
#define MethodGuard_h___
#include "jsobj.h"
namespace js {
/*
* Report an error if call.thisv is not compatible with the specified class.
*
* NB: most callers should be calling or NonGenericMethodGuard,
* HandleNonGenericMethodClassMismatch, or BoxedPrimitiveMethodGuard (so that
* transparent proxies are handled correctly). Thus, any caller of this
* function better have a good explanation for why proxies are being handled
* correctly (e.g., by IsCallable) or are not an issue (E4X).
*/
extern void
ReportIncompatibleMethod(JSContext *cx, CallReceiver call, Class *clasp);
/*
* A non-generic method is specified to report an error if args.thisv is not an
* object with a specific [[Class]] internal property (ES5 8.6.2).
* NonGenericMethodGuard performs this checking. Canonical usage is:
*
* CallArgs args = ...
* JSObject *thisObj;
* if (!NonGenericMethodGuard(cx, args, clasp, &thisObj))
* return false;
* if (!thisObj)
* return true;
*
* Specifically: if args.thisv is a proxy, NonGenericMethodGuard will call its
* ProxyHandler's nativeCall hook (which may recursively call args.callee in
* args.thisv's compartment). These are the possible post-conditions:
*
* 1. NonGenericMethodGuard returned false because it encountered an error:
* args.thisv wasn't an object, was an object of the wrong class, was a
* proxy to an object of the wrong class, or was a proxy to an object of
* the right class but the recursive call to args.callee failed. This case
* should be handled like any other failure: propagate it, or catch it and
* continue.
* 2. NonGenericMethodGuard returned true, and thisObj was nulled out. In
* this case args.thisv was a proxy to an object with the desired class,
* and recursive invocation of args.callee succeeded. This completes the
* invocation of args.callee, so return true.
* 3. NonGenericMethodGuard returned true, and thisObj was set to a non-null
* pointer. In this case args.thisv was exactly an object of the desired
* class, and not a proxy to one. Finish up the call using thisObj as the
* this object provided to the call, which will have clasp as its class.
*
* Be careful! This guard may reenter the native, so the guard must be placed
* before any effectful operations are performed.
*/
inline bool
NonGenericMethodGuard(JSContext *cx, CallArgs args, Native native, Class *clasp, JSObject **thisObj);
/*
* NonGenericMethodGuard tests args.thisv's class using 'clasp'. If more than
* one class is acceptable (viz., isDenseArray() || isSlowArray()), the caller
* may test the class and delegate to HandleNonGenericMethodClassMismatch to
* handle the proxy case and error reporting. The 'clasp' argument is only used
* for error reporting (clasp->name).
*/
extern bool
HandleNonGenericMethodClassMismatch(JSContext *cx, CallArgs args, Native native, Class *clasp);
/*
* Implement the extraction of a primitive from a value as needed for the
* toString, valueOf, and a few other methods of the boxed primitives classes
* Boolean, Number, and String (e.g., ES5 15.6.4.2). If 'true' is returned, the
* extracted primitive is stored in |*v|. If 'false' is returned, the caller
* must immediately 'return *ok'. For details, see NonGenericMethodGuard.
*/
template <typename T>
inline bool
BoxedPrimitiveMethodGuard(JSContext *cx, CallArgs args, Native native, T *v, bool *ok);
} /* namespace js */
#endif /* MethodGuard_h___ */