Bug 877216. Add CallArgs-like structs for use in DOM specialized getters/setters/methods. r=waldo

This commit is contained in:
Boris Zbarsky 2013-05-30 17:47:00 -04:00
parent 897328ff5b
commit 5be706faaf
7 changed files with 204 additions and 77 deletions

View File

@ -5104,7 +5104,7 @@ class CGGenericMethod(CGAbstractBindingMethod):
return CGIndenter(CGGeneric(
"const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
"MOZ_ASSERT(info->type == JSJitInfo::Method);\n"
"JSJitMethodOp method = (JSJitMethodOp)info->op;\n"
"JSJitMethodOp method = info->method;\n"
"return method(cx, obj, self, argc, vp);"))
class CGSpecializedMethod(CGAbstractStaticMethod):
@ -5240,7 +5240,7 @@ class CGGenericGetter(CGAbstractBindingMethod):
return CGIndenter(CGGeneric(
"const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
"MOZ_ASSERT(info->type == JSJitInfo::Getter);\n"
"JSJitPropertyOp getter = info->op;\n"
"JSJitGetterOp getter = info->getter;\n"
"return getter(cx, obj, self, vp);"))
class CGSpecializedGetter(CGAbstractStaticMethod):
@ -5324,7 +5324,7 @@ class CGGenericSetter(CGAbstractBindingMethod):
"JS::Value* argv = JS_ARGV(cx, vp);\n"
"const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
"MOZ_ASSERT(info->type == JSJitInfo::Setter);\n"
"JSJitPropertyOp setter = info->op;\n"
"JSJitSetterOp setter = info->setter;\n"
"if (!setter(cx, obj, self, argv)) {\n"
" return false;\n"
"}\n"
@ -5431,7 +5431,7 @@ class CGMemberJITInfo(CGThing):
"")
return ("\n"
"static const JSJitInfo %s = {\n"
" %s,\n"
" { %s },\n"
" %s,\n"
" %s,\n"
" JSJitInfo::%s,\n"
@ -5445,7 +5445,9 @@ class CGMemberJITInfo(CGThing):
def define(self):
if self.member.isAttr():
getterinfo = ("%s_getterinfo" % self.member.identifier.name)
getter = ("(JSJitPropertyOp)get_%s" % self.member.identifier.name)
# We need the cast here because JSJitGetterOp has a "void* self"
# while we have the right type.
getter = ("(JSJitGetterOp)get_%s" % self.member.identifier.name)
getterinfal = "infallible" in self.descriptor.getExtendedAttributes(self.member, getter=True)
getterconst = self.member.getExtendedAttribute("Constant")
getterpure = getterconst or self.member.getExtendedAttribute("Pure")
@ -5457,7 +5459,9 @@ class CGMemberJITInfo(CGThing):
[self.member.type])
if not self.member.readonly or self.member.getExtendedAttribute("PutForwards") is not None:
setterinfo = ("%s_setterinfo" % self.member.identifier.name)
setter = ("(JSJitPropertyOp)set_%s" % self.member.identifier.name)
# Actually a JSJitSetterOp, but JSJitGetterOp is first in the
# union.
setter = ("(JSJitGetterOp)set_%s" % self.member.identifier.name)
# Setters are always fallible, since they have to do a typed unwrap.
result += self.defineJitInfo(setterinfo, setter, "Setter",
False, False, False,
@ -5466,8 +5470,8 @@ class CGMemberJITInfo(CGThing):
if self.member.isMethod():
methodinfo = ("%s_methodinfo" % self.member.identifier.name)
name = CppKeywords.checkMethodName(self.member.identifier.name)
# Actually a JSJitMethodOp, but JSJitPropertyOp by struct definition.
method = ("(JSJitPropertyOp)%s" % name)
# Actually a JSJitMethodOp, but JSJitGetterOp is first in the union.
method = ("(JSJitGetterOp)%s" % name)
# Methods are infallible if they are infallible, have no arguments
# to unwrap, and have a return type that's infallible to wrap up for

View File

@ -31,6 +31,7 @@
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/TypeTraits.h"
#include "jstypes.h"
@ -92,30 +93,50 @@ namespace JS {
* public interface are meant to be used by embedders! See inline comments to
* for details.
*/
class MOZ_STACK_CLASS CallReceiver
namespace detail {
enum UsedRval { IncludeUsedRval, NoUsedRval };
template<UsedRval WantUsedRval>
class MOZ_STACK_CLASS UsedRvalBase;
template<>
class MOZ_STACK_CLASS UsedRvalBase<IncludeUsedRval>
{
protected:
#ifdef DEBUG
mutable bool usedRval_;
void setUsedRval() const { usedRval_ = true; }
void clearUsedRval() const { usedRval_ = false; }
#else
};
template<>
class MOZ_STACK_CLASS UsedRvalBase<NoUsedRval>
{
protected:
void setUsedRval() const {}
void clearUsedRval() const {}
};
template<UsedRval WantUsedRval>
class MOZ_STACK_CLASS CallReceiverBase : public UsedRvalBase<
#ifdef DEBUG
WantUsedRval
#else
NoUsedRval
#endif
>
{
protected:
Value *argv_;
friend CallReceiver CallReceiverFromVp(Value *vp);
friend CallReceiver CallReceiverFromArgv(Value *argv);
public:
/*
* Returns the function being called, as an object. Must not be called
* after rval() has been used!
*/
JSObject &callee() const {
MOZ_ASSERT(!usedRval_);
MOZ_ASSERT(!this->usedRval_);
return argv_[-2].toObject();
}
@ -124,7 +145,7 @@ class MOZ_STACK_CLASS CallReceiver
* rval() has been used!
*/
HandleValue calleev() const {
MOZ_ASSERT(!usedRval_);
MOZ_ASSERT(!this->usedRval_);
return HandleValue::fromMarkedLocation(&argv_[-2]);
}
@ -160,7 +181,7 @@ class MOZ_STACK_CLASS CallReceiver
* fails.
*/
MutableHandleValue rval() const {
setUsedRval();
this->setUsedRval();
return MutableHandleValue::fromMarkedLocation(&argv_[-2]);
}
@ -171,7 +192,7 @@ class MOZ_STACK_CLASS CallReceiver
Value *base() const { return argv_ - 2; }
Value *spAfterCall() const {
setUsedRval();
this->setUsedRval();
return argv_ - 1;
}
@ -181,7 +202,7 @@ class MOZ_STACK_CLASS CallReceiver
// it. You probably don't want to use these!
void setCallee(Value aCalleev) const {
clearUsedRval();
this->clearUsedRval();
argv_[-2] = aCalleev;
}
@ -194,6 +215,15 @@ class MOZ_STACK_CLASS CallReceiver
}
};
} // namespace detail
class MOZ_STACK_CLASS CallReceiver : public detail::CallReceiverBase<detail::IncludeUsedRval>
{
private:
friend CallReceiver CallReceiverFromVp(Value *vp);
friend CallReceiver CallReceiverFromArgv(Value *argv);
};
MOZ_ALWAYS_INLINE CallReceiver
CallReceiverFromArgv(Value *argv)
{
@ -233,11 +263,63 @@ CallReceiverFromVp(Value *vp)
* public interface are meant to be used by embedders! See inline comments to
* for details.
*/
class MOZ_STACK_CLASS CallArgs : public CallReceiver
namespace detail {
template<UsedRval WantUsedRval>
class MOZ_STACK_CLASS CallArgsBase :
public mozilla::Conditional<WantUsedRval == detail::IncludeUsedRval,
CallReceiver,
CallReceiverBase<NoUsedRval> >::Type
{
protected:
unsigned argc_;
public:
/* Returns the number of arguments. */
unsigned length() const { return argc_; }
/* Returns the i-th zero-indexed argument. */
Value &operator[](unsigned i) const {
MOZ_ASSERT(i < argc_);
return this->argv_[i];
}
/* Returns a mutable handle for the i-th zero-indexed argument. */
MutableHandleValue handleAt(unsigned i) const {
MOZ_ASSERT(i < argc_);
return MutableHandleValue::fromMarkedLocation(&this->argv_[i]);
}
/*
* Returns the i-th zero-indexed argument, or |undefined| if there's no
* such argument.
*/
Value get(unsigned i) const {
return i < length() ? this->argv_[i] : UndefinedValue();
}
/*
* Returns true if the i-th zero-indexed argument is present and is not
* |undefined|.
*/
bool hasDefined(unsigned i) const {
return i < argc_ && !this->argv_[i].isUndefined();
}
public:
// These methods are publicly exposed, but we're less sure of the interface
// here than we'd like (because they're hackish and drop assertions). Try
// to avoid using these if you can.
Value *array() const { return this->argv_; }
Value *end() const { return this->argv_ + argc_; }
};
} // namespace detail
class MOZ_STACK_CLASS CallArgs : public detail::CallArgsBase<detail::IncludeUsedRval>
{
private:
friend CallArgs CallArgsFromVp(unsigned argc, Value *vp);
friend CallArgs CallArgsFromSp(unsigned argc, Value *sp);
@ -249,45 +331,6 @@ class MOZ_STACK_CLASS CallArgs : public CallReceiver
return args;
}
public:
/* Returns the number of arguments. */
unsigned length() const { return argc_; }
/* Returns the i-th zero-indexed argument. */
Value &operator[](unsigned i) const {
MOZ_ASSERT(i < argc_);
return argv_[i];
}
/* Returns a mutable handle for the i-th zero-indexed argument. */
MutableHandleValue handleAt(unsigned i) const {
MOZ_ASSERT(i < argc_);
return MutableHandleValue::fromMarkedLocation(&argv_[i]);
}
/*
* Returns the i-th zero-indexed argument, or |undefined| if there's no
* such argument.
*/
Value get(unsigned i) const {
return i < length() ? argv_[i] : UndefinedValue();
}
/*
* Returns true if the i-th zero-indexed argument is present and is not
* |undefined|.
*/
bool hasDefined(unsigned i) const {
return i < argc_ && !argv_[i].isUndefined();
}
public:
// These methods are publicly exposed, but we're less sure of the interface
// here than we'd like (because they're hackish and drop assertions). Try
// to avoid using these if you can.
Value *array() const { return argv_; }
Value *end() const { return argv_ + argc_; }
};
MOZ_ALWAYS_INLINE CallArgs

View File

@ -1453,7 +1453,7 @@ CodeGenerator::visitCallDOMNative(LCallDOMNative *call)
masm.passABIArg(argPrivate);
masm.passABIArg(argArgc);
masm.passABIArg(argVp);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target->jitInfo()->op));
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target->jitInfo()->method));
if (target->jitInfo()->isInfallible) {
masm.loadValue(Address(StackPointer, IonDOMMethodExitFrameLayout::offsetOfResult()),

View File

@ -7791,7 +7791,8 @@ IonBuilder::jsop_setprop(HandlePropertyName name)
// properties.
RootedFunction setter(cx, commonSetter);
if (isDOM && TestShouldDOMCall(cx, objTypes, setter, JSJitInfo::Setter)) {
MSetDOMProperty *set = MSetDOMProperty::New(setter->jitInfo()->op, obj, value);
JS_ASSERT(setter->jitInfo()->type == JSJitInfo::Setter);
MSetDOMProperty *set = MSetDOMProperty::New(setter->jitInfo()->setter, obj, value);
if (!set)
return false;

View File

@ -6586,9 +6586,9 @@ class MSetDOMProperty
: public MAryInstruction<2>,
public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >
{
const JSJitPropertyOp func_;
const JSJitSetterOp func_;
MSetDOMProperty(const JSJitPropertyOp func, MDefinition *obj, MDefinition *val)
MSetDOMProperty(const JSJitSetterOp func, MDefinition *obj, MDefinition *val)
: func_(func)
{
setOperand(0, obj);
@ -6598,12 +6598,12 @@ class MSetDOMProperty
public:
INSTRUCTION_HEADER(SetDOMProperty)
static MSetDOMProperty *New(const JSJitPropertyOp func, MDefinition *obj, MDefinition *val)
static MSetDOMProperty *New(const JSJitSetterOp func, MDefinition *obj, MDefinition *val)
{
return new MSetDOMProperty(func, obj, val);
}
const JSJitPropertyOp fun() {
const JSJitSetterOp fun() {
return func_;
}
@ -6631,6 +6631,7 @@ class MGetDOMProperty
: info_(jitinfo)
{
JS_ASSERT(jitinfo);
JS_ASSERT(jitinfo->type == JSJitInfo::Getter);
setOperand(0, obj);
@ -6657,8 +6658,8 @@ class MGetDOMProperty
return new MGetDOMProperty(info, obj, guard);
}
const JSJitPropertyOp fun() {
return info_->op;
const JSJitGetterOp fun() {
return info_->getter;
}
bool isInfallible() const {
return info_->isInfallible;

View File

@ -14,6 +14,8 @@
#include "jspubtd.h"
#include "jsprvtd.h"
#include "js/CallArgs.h"
/*
* This macro checks if the stack pointer has exceeded a given limit. If
* |tolerance| is non-zero, it returns true only if the stack pointer has
@ -1432,14 +1434,86 @@ JS_GetDataViewByteLength(JSObject *obj);
JS_FRIEND_API(void *)
JS_GetDataViewData(JSObject *obj);
/*
* A class, expected to be passed by value, which represents the CallArgs for a
* JSJitGetterOp.
*/
class JSJitGetterCallArgs : protected JS::MutableHandleValue
{
public:
explicit JSJitGetterCallArgs(const JS::CallArgs& args)
: JS::MutableHandleValue(args.rval())
{}
JS::MutableHandleValue rval() {
return *this;
}
};
/*
* A class, expected to be passed by value, which represents the CallArgs for a
* JSJitSetterOp.
*/
class JSJitSetterCallArgs : protected JS::MutableHandleValue
{
public:
explicit JSJitSetterCallArgs(const JS::CallArgs& args)
: JS::MutableHandleValue(args.handleAt(0))
{}
JS::MutableHandleValue handleAt(unsigned i) {
MOZ_ASSERT(i == 0);
return *this;
}
unsigned length() const { return 1; }
// Add get() or maybe hasDefined() as needed
};
/*
* A class, expected to be passed by reference, which represents the CallArgs
* for a JSJitMethodOp.
*/
class JSJitMethodCallArgs : protected JS::detail::CallArgsBase<JS::detail::NoUsedRval>
{
private:
typedef JS::detail::CallArgsBase<JS::detail::NoUsedRval> Base;
public:
explicit JSJitMethodCallArgs(const JS::CallArgs& args) {
argv_ = args.array();
argc_ = args.length();
}
JS::MutableHandleValue rval() const {
return Base::rval();
}
unsigned length() const { return Base::length(); }
JS::MutableHandleValue handleAt(unsigned i) const {
return Base::handleAt(i);
}
bool hasDefined(unsigned i) const {
return Base::hasDefined(i);
}
// Add get() as needed
};
/*
* This struct contains metadata passed from the DOM to the JS Engine for JIT
* optimizations on DOM property accessors. Eventually, this should be made
* available to general JSAPI users, but we are not currently ready to do so.
*/
typedef bool
(* JSJitPropertyOp)(JSContext *cx, JSHandleObject thisObj,
void *specializedThis, JS::Value *vp);
(* JSJitGetterOp)(JSContext *cx, JSHandleObject thisObj,
void *specializedThis, JS::Value *vp);
typedef bool
(* JSJitSetterOp)(JSContext *cx, JSHandleObject thisObj,
void *specializedThis, JS::Value *vp);
typedef bool
(* JSJitMethodOp)(JSContext *cx, JSHandleObject thisObj,
void *specializedThis, unsigned argc, JS::Value *vp);
@ -1451,7 +1525,11 @@ struct JSJitInfo {
Method
};
JSJitPropertyOp op;
union {
JSJitGetterOp getter;
JSJitSetterOp setter;
JSJitMethodOp method;
};
uint32_t protoID;
uint32_t depth;
OpType type;

View File

@ -4604,7 +4604,7 @@ dom_doFoo(JSContext* cx, JSHandleObject obj, void *self, unsigned argc, JS::Valu
}
const JSJitInfo dom_x_getterinfo = {
(JSJitPropertyOp)dom_get_x,
{ (JSJitGetterOp)dom_get_x },
0, /* protoID */
0, /* depth */
JSJitInfo::Getter,
@ -4613,7 +4613,7 @@ const JSJitInfo dom_x_getterinfo = {
};
const JSJitInfo dom_x_setterinfo = {
(JSJitPropertyOp)dom_set_x,
{ (JSJitGetterOp)dom_set_x },
0, /* protoID */
0, /* depth */
JSJitInfo::Setter,
@ -4622,7 +4622,7 @@ const JSJitInfo dom_x_setterinfo = {
};
const JSJitInfo doFoo_methodinfo = {
(JSJitPropertyOp)dom_doFoo,
{ (JSJitGetterOp)dom_doFoo },
0, /* protoID */
0, /* depth */
JSJitInfo::Method,
@ -4684,7 +4684,7 @@ dom_genericGetter(JSContext *cx, unsigned argc, JS::Value *vp)
const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));
MOZ_ASSERT(info->type == JSJitInfo::Getter);
JSJitPropertyOp getter = info->op;
JSJitGetterOp getter = info->getter;
return getter(cx, obj, val.toPrivate(), vp);
}
@ -4707,7 +4707,7 @@ dom_genericSetter(JSContext* cx, unsigned argc, JS::Value* vp)
const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));
MOZ_ASSERT(info->type == JSJitInfo::Setter);
JSJitPropertyOp setter = info->op;
JSJitSetterOp setter = info->setter;
if (!setter(cx, obj, val.toPrivate(), argv))
return false;
JS_SET_RVAL(cx, vp, UndefinedValue());
@ -4730,7 +4730,7 @@ dom_genericMethod(JSContext* cx, unsigned argc, JS::Value *vp)
const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));
MOZ_ASSERT(info->type == JSJitInfo::Method);
JSJitMethodOp method = (JSJitMethodOp)info->op;
JSJitMethodOp method = info->method;
return method(cx, obj, val.toPrivate(), argc, vp);
}