Bug 862531: Replace BaseProxyHandler::obj_toString with className. r=jorendorff

This commit is contained in:
Jim Blandy 2013-04-30 14:44:50 -07:00
parent 8249c1a522
commit e9b58eb4dc
23 changed files with 110 additions and 123 deletions

View File

@ -117,7 +117,7 @@ function test()
waitForWatchExpressions(function() {
info("Performing test2");
checkWatchExpressions(undefined,
{ type: "object", class: "Proxy" },
{ type: "object", class: "Window" },
undefined,
"sensational",
26);
@ -132,7 +132,7 @@ function test()
waitForWatchExpressions(function() {
info("Performing test3");
checkWatchExpressions({ type: "object", class: "Object" },
{ type: "object", class: "Proxy" },
{ type: "object", class: "Window" },
undefined,
"sensational",
26);
@ -147,7 +147,7 @@ function test()
waitForWatchExpressions(function() {
info("Performing test4");
checkWatchExpressions(5,
{ type: "object", class: "Proxy" },
{ type: "object", class: "Window" },
undefined,
"sensational",
27);
@ -163,7 +163,7 @@ function test()
waitForWatchExpressions(function() {
info("Performing test5");
checkWatchExpressions(5,
{ type: "object", class: "Proxy" },
{ type: "object", class: "Window" },
undefined,
"sensational",
27);
@ -179,7 +179,7 @@ function test()
waitForWatchExpressions(function() {
info("Performing test6");
checkWatchExpressions(5,
{ type: "object", class: "Proxy" },
{ type: "object", class: "Window" },
undefined,
"sensational",
27);
@ -195,7 +195,7 @@ function test()
waitForWatchExpressions(function() {
info("Performing test7");
checkWatchExpressions(5,
{ type: "object", class: "Proxy" },
{ type: "object", class: "Window" },
undefined,
"sensational",
27);
@ -211,7 +211,7 @@ function test()
waitForWatchExpressions(function() {
info("Performing test8");
checkWatchExpressions(5,
{ type: "object", class: "Proxy" },
{ type: "object", class: "Window" },
undefined,
"sensational",
27);

View File

@ -42,7 +42,7 @@ function testFrameParameters()
is(localNodes.length, 12,
"The localScope should contain all the created variable elements.");
is(localNodes[0].querySelector(".value").getAttribute("value"), "[object Proxy]",
is(localNodes[0].querySelector(".value").getAttribute("value"), "[object Window]",
"Should have the right property value for 'this'.");
is(localNodes[1].querySelector(".value").getAttribute("value"), "[object Object]",

View File

@ -43,7 +43,7 @@ function testFrameParameters()
is(localNodes.length + localNonEnums.length, 12,
"The localScope and localNonEnums should contain all the created variable elements.");
is(localNodes[0].querySelector(".value").getAttribute("value"), "[object Proxy]",
is(localNodes[0].querySelector(".value").getAttribute("value"), "[object Window]",
"Should have the right property value for 'this'.");
is(localNodes[8].querySelector(".value").getAttribute("value"), "[object Arguments]",
"Should have the right property value for 'arguments'.");
@ -117,7 +117,7 @@ function testFrameParameters()
window.clearInterval(intervalID);
is(thisNode.target.querySelector(".value")
.getAttribute("value"), "[object Proxy]",
.getAttribute("value"), "[object Window]",
"Should have the right property value for 'this'.");
is(thisNode.get("window").target.querySelector(".name")

View File

@ -50,23 +50,23 @@ function testFrameParameters()
is(frames.querySelectorAll(".dbg-stackframe").length, 3,
"Should have three frames.");
is(globalNodes[0].querySelector(".name").getAttribute("value"), "InstallTrigger",
"Should have the right property name for |InstallTrigger|.");
is(globalNodes[0].querySelector(".value").getAttribute("value"), "",
"Should have the right property value for |InstallTrigger|.");
is(globalNodes[1].querySelector(".name").getAttribute("value"), "SpecialPowers",
"Should have the right property name for |SpecialPowers|.");
is(globalNodes[1].querySelector(".value").getAttribute("value"), "[object Proxy]",
is(globalNodes[1].querySelector(".value").getAttribute("value"), "[object Object]",
"Should have the right property value for |SpecialPowers|.");
is(globalNodes[3].querySelector(".name").getAttribute("value"), "document",
"Should have the right property name for |document|.");
is(globalNodes[3].querySelector(".value").getAttribute("value"), "[object HTMLDocument]",
"Should have the right property value for |document|.");
let len = globalNodes.length - 1;
is(globalNodes[len].querySelector(".name").getAttribute("value"), "window",
"Should have the right property name for |window|.");
is(globalNodes[len].querySelector(".value").getAttribute("value"), "[object Proxy]",
is(globalNodes[len].querySelector(".value").getAttribute("value"), "[object Window]",
"Should have the right property value for |window|.");
resumeAndFinish();

View File

@ -61,17 +61,17 @@ function testWithFrame()
is(innerNodes[1].querySelector(".value").getAttribute("value"), "1",
"Should have the right property value for |one|.");
is(globalNodes[0].querySelector(".name").getAttribute("value"), "InstallTrigger",
"Should have the right property name for |InstallTrigger|.");
is(globalNodes[3].querySelector(".name").getAttribute("value"), "document",
"Should have the right property name for |document|.");
is(globalNodes[0].querySelector(".value").getAttribute("value"), "",
"Should have the right property value for |InstallTrigger|.");
is(globalNodes[3].querySelector(".value").getAttribute("value"), "[object HTMLDocument]",
"Should have the right property value for |document|.");
let len = globalNodes.length - 1;
is(globalNodes[len].querySelector(".name").getAttribute("value"), "window",
"Should have the right property name for |window|.");
is(globalNodes[len].querySelector(".value").getAttribute("value"), "[object Proxy]",
is(globalNodes[len].querySelector(".value").getAttribute("value"), "[object Window]",
"Should have the right property value for |window|.");
resumeAndFinish();

View File

@ -76,7 +76,7 @@ function testFrameEval() {
"Should have the right name for 'this'.");
is(scope.get("this").value.type, "object",
"Should have the right value type for 'this'.");
is(scope.get("this").value.class, "Proxy",
is(scope.get("this").value.class, "Window",
"Should have the right value type for 'this'.");
is(scope.get("ermahgerd")._isContentVisible, true,

View File

@ -552,8 +552,8 @@ public:
return false;
}
virtual JSString *obj_toString(JSContext *cx,
JS::Handle<JSObject*> wrapper) MOZ_OVERRIDE;
virtual const char *className(JSContext *cx,
JS::Handle<JSObject*> wrapper) MOZ_OVERRIDE;
virtual void finalize(JSFreeOp *fop, JSObject *proxy) MOZ_OVERRIDE;
// Fundamental traps
@ -646,12 +646,12 @@ nsOuterWindowProxy::preventExtensions(JSContext *cx,
return false;
}
JSString *
nsOuterWindowProxy::obj_toString(JSContext *cx, JS::Handle<JSObject*> proxy)
const char *
nsOuterWindowProxy::className(JSContext *cx, JS::Handle<JSObject*> proxy)
{
MOZ_ASSERT(js::IsProxy(proxy));
return JS_NewStringCopyZ(cx, "[object Window]");
return "Window";
}
void
@ -931,18 +931,18 @@ class nsChromeOuterWindowProxy : public nsOuterWindowProxy
public:
nsChromeOuterWindowProxy() : nsOuterWindowProxy() {}
virtual JSString *obj_toString(JSContext *cx, JS::Handle<JSObject*> wrapper);
virtual const char *className(JSContext *cx, JS::Handle<JSObject*> wrapper) MOZ_OVERRIDE;
static nsChromeOuterWindowProxy singleton;
};
JSString *
nsChromeOuterWindowProxy::obj_toString(JSContext *cx,
JS::Handle<JSObject*> proxy)
const char *
nsChromeOuterWindowProxy::className(JSContext *cx,
JS::Handle<JSObject*> proxy)
{
MOZ_ASSERT(js::IsProxy(proxy));
return JS_NewStringCopyZ(cx, "[object ChromeWindow]");
return "ChromeWindow";
}
nsChromeOuterWindowProxy

View File

@ -1373,7 +1373,7 @@ NativeToString(JSContext* cx, JSObject* wrapper, JSObject* object, const char* p
}
} else {
if (IsDOMProxy(obj)) {
str = js::GetProxyHandler(obj)->obj_toString(cx, obj);
str = JS_BasicObjectToString(cx, obj);
} else {
js::Class* clasp = js::GetObjectClass(obj);
if (IsDOMClass(clasp)) {

View File

@ -6868,13 +6868,14 @@ if (expando) {
vp.setUndefined();
return true;""" % (getIndexedOrExpando, getNamed)
class CGDOMJSProxyHandler_obj_toString(ClassMethod):
class CGDOMJSProxyHandler_className(ClassMethod):
def __init__(self, descriptor):
args = [Argument('JSContext*', 'cx'), Argument('JS::Handle<JSObject*>', 'proxy')]
ClassMethod.__init__(self, "obj_toString", "JSString*", args)
ClassMethod.__init__(self, "className", "const char*", args,
virtual=True, override=True)
self.descriptor = descriptor
def getBody(self):
return "return mozilla::dom::DOMProxyHandler::obj_toString(cx, \"%s\");" % self.descriptor.name
return 'return "%s";' % self.descriptor.name
class CGDOMJSProxyHandler_finalizeInBackground(ClassMethod):
def __init__(self, descriptor):
@ -6974,7 +6975,7 @@ class CGDOMJSProxyHandler(CGClass):
methods.extend([CGDOMJSProxyHandler_getOwnPropertyNames(descriptor),
CGDOMJSProxyHandler_hasOwn(descriptor),
CGDOMJSProxyHandler_get(descriptor),
CGDOMJSProxyHandler_obj_toString(descriptor),
CGDOMJSProxyHandler_className(descriptor),
CGDOMJSProxyHandler_finalizeInBackground(descriptor),
CGDOMJSProxyHandler_finalize(descriptor),
CGDOMJSProxyHandler_getElementIfPresent(descriptor),

View File

@ -205,34 +205,6 @@ DOMProxyHandler::has(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid
return ok;
}
// static
JSString*
DOMProxyHandler::obj_toString(JSContext* cx, const char* className)
{
size_t nchars = sizeof("[object ]") - 1 + strlen(className);
jschar* chars = static_cast<jschar*>(JS_malloc(cx, (nchars + 1) * sizeof(jschar)));
if (!chars) {
return NULL;
}
const char* prefix = "[object ";
nchars = 0;
while ((chars[nchars] = (jschar)*prefix) != 0) {
nchars++, prefix++;
}
while ((chars[nchars] = (jschar)*className) != 0) {
nchars++, className++;
}
chars[nchars++] = ']';
chars[nchars] = 0;
JSString* str = JS_NewUCString(cx, chars, nchars);
if (!str) {
JS_free(cx, chars);
}
return str;
}
bool
DOMProxyHandler::AppendNamedPropertyIds(JSContext* cx, JSObject* proxy,
nsTArray<nsString>& names,

View File

@ -46,7 +46,6 @@ public:
bool enumerate(JSContext* cx, JS::Handle<JSObject*> proxy, JS::AutoIdVector& props) MOZ_OVERRIDE;
bool has(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, bool* bp) MOZ_OVERRIDE;
bool isExtensible(JSObject *proxy) MOZ_OVERRIDE;
using js::BaseProxyHandler::obj_toString;
static JSObject* GetExpandoObject(JSObject* obj)
{
@ -60,8 +59,6 @@ public:
const DOMClass& mClass;
protected:
static JSString* obj_toString(JSContext* cx, const char* className);
// Append the property names in "names" that don't live on our proto
// chain to "props"
bool AppendNamedPropertyIds(JSContext* cx, JSObject* proxy,

View File

@ -287,13 +287,11 @@ obj_toSource(JSContext *cx, unsigned argc, Value *vp)
#endif /* JS_HAS_TOSOURCE */
JSString *
js::obj_toStringHelper(JSContext *cx, HandleObject obj)
JS_BasicObjectToString(JSContext *cx, HandleObject obj)
{
if (obj->isProxy())
return Proxy::obj_toString(cx, obj);
const char *className = JSObject::className(cx, obj);
StringBuffer sb(cx);
const char *className = obj->getClass()->name;
if (!sb.append("[object ") || !sb.appendInflated(className, strlen(className)) ||
!sb.append("]"))
{
@ -326,7 +324,7 @@ obj_toString(JSContext *cx, unsigned argc, Value *vp)
return false;
/* Steps 4-5. */
RawString str = js::obj_toStringHelper(cx, obj);
RawString str = JS_BasicObjectToString(cx, obj);
if (!str)
return false;
args.rval().setString(str);

View File

@ -18,9 +18,6 @@ extern const JSFunctionSpec object_static_methods[];
extern JSBool
obj_construct(JSContext *cx, unsigned argc, js::Value *vp);
extern JSString *
obj_toStringHelper(JSContext *cx, HandleObject obj);
} /* namespace js */
#endif

View File

@ -1,15 +1,26 @@
// Basic tests for Debugger.Object.prototype.class.
var g = newGlobal('new-compartment');
var g = newGlobal();
var dbg = new Debugger(g);
var hits = 0;
g.eval('function f() { debugger; }');
dbg.onDebuggerStatement = function (frame) {
var arr = frame.arguments;
assertEq(arr[0].class, "Object");
assertEq(arr[1].class, "Array");
assertEq(arr[2].class, "Function");
assertEq(arr[3].class, "Date");
assertEq(arr[4].class, "Proxy");
assertEq(arr[4].class, "Object");
assertEq(arr[5].class, "Function");
assertEq(arr[6].class, "Date");
hits++;
};
g.eval("(function () { debugger; })(Object.prototype, [], eval, new Date, Proxy.create({}));");
g.f(Object.prototype, [], eval, new Date,
Proxy.create({}), Proxy.createFunction({}, eval), new Proxy(new Date, {}));
assertEq(hits, 1);
// Debugger.Object.prototype.class should see through cross-compartment
// wrappers.
g.eval('f(Object.prototype, [], eval, new Date,\
Proxy.create({}), Proxy.createFunction({}, f), new Proxy(new Date, {}));');
assertEq(hits, 2);

View File

@ -1015,7 +1015,7 @@ array_toString(JSContext *cx, unsigned argc, Value *vp)
return false;
if (!js_IsCallable(join)) {
JSString *str = obj_toStringHelper(cx, obj);
JSString *str = JS_BasicObjectToString(cx, obj);
if (!str)
return false;
args.rval().setString(str);

View File

@ -117,6 +117,9 @@ JS_ObjectToOuterObject(JSContext *cx, JSObject *obj);
extern JS_FRIEND_API(JSObject *)
JS_CloneObject(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent);
extern JS_FRIEND_API(JSString *)
JS_BasicObjectToString(JSContext *cx, JSHandleObject obj);
extern JS_FRIEND_API(JSBool)
js_GetterOnlyPropertyStub(JSContext *cx, JSHandleObject obj, JSHandleId id, JSBool strict, JSMutableHandleValue vp);

View File

@ -1210,6 +1210,16 @@ JSObject::isSealedOrFrozen(JSContext *cx, HandleObject obj, ImmutabilityType it,
return true;
}
/* static */
const char *
JSObject::className(JSContext *cx, HandleObject obj)
{
if (obj->isProxy())
return Proxy::className(cx, obj);
return obj->getClass()->name;
}
/*
* Get the GC kind to use for scripted 'new' on the given class.
* FIXME bug 547327: estimate the size from the allocation site.

View File

@ -561,6 +561,9 @@ class JSObject : public js::ObjectImpl
return isSealedOrFrozen(cx, obj, FREEZE, resultp);
}
/* toString support. */
static const char *className(JSContext *cx, js::HandleObject obj);
/* Accessors for elements. */
struct MaybeContext {

View File

@ -320,12 +320,10 @@ BaseProxyHandler::construct(JSContext *cx, HandleObject proxy, const CallArgs &a
return false;
}
JSString *
BaseProxyHandler::obj_toString(JSContext *cx, HandleObject proxy)
const char *
BaseProxyHandler::className(JSContext *cx, HandleObject proxy)
{
return JS_NewStringCopyZ(cx, IsFunctionProxy(proxy)
? "[object Function]"
: "[object Object]");
return IsFunctionProxy(proxy) ? "Function" : "Object";
}
JSString *
@ -524,12 +522,12 @@ DirectProxyHandler::objectClassIs(HandleObject proxy, ESClassValue classValue,
return ObjectClassIs(obj, classValue, cx);
}
JSString *
DirectProxyHandler::obj_toString(JSContext *cx, HandleObject proxy)
const char *
DirectProxyHandler::className(JSContext *cx, HandleObject proxy)
{
assertEnteredPolicy(cx, proxy, JSID_VOID);
RootedObject target(cx, GetProxyTargetObject(proxy));
return obj_toStringHelper(cx, target);
return JSObject::className(cx, target);
}
JSString *
@ -2662,8 +2660,8 @@ Proxy::objectClassIs(HandleObject proxy, ESClassValue classValue, JSContext *cx)
return GetProxyHandler(proxy)->objectClassIs(proxy, classValue, cx);
}
JSString *
Proxy::obj_toString(JSContext *cx, HandleObject proxy)
const char *
Proxy::className(JSContext *cx, HandleObject proxy)
{
JS_CHECK_RECURSION(cx, return NULL);
BaseProxyHandler *handler = GetProxyHandler(proxy);
@ -2671,9 +2669,9 @@ Proxy::obj_toString(JSContext *cx, HandleObject proxy)
BaseProxyHandler::GET, /* mayThrow = */ false);
// Do the safe thing if the policy rejects.
if (!policy.allowed()) {
return handler->BaseProxyHandler::obj_toString(cx, proxy);
return handler->BaseProxyHandler::className(cx, proxy);
}
return handler->obj_toString(cx, proxy);
return handler->className(cx, proxy);
}
JSString *

View File

@ -135,7 +135,7 @@ class JS_FRIEND_API(BaseProxyHandler)
virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args);
virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp);
virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx);
virtual JSString *obj_toString(JSContext *cx, HandleObject proxy);
virtual const char *className(JSContext *cx, HandleObject proxy);
virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent);
virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g);
virtual bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp);
@ -199,7 +199,7 @@ class JS_PUBLIC_API(DirectProxyHandler) : public BaseProxyHandler
bool *bp) MOZ_OVERRIDE;
virtual bool objectClassIs(HandleObject obj, ESClassValue classValue,
JSContext *cx) MOZ_OVERRIDE;
virtual JSString *obj_toString(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE;
virtual const char *className(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE;
virtual JSString *fun_toString(JSContext *cx, HandleObject proxy,
unsigned indent) MOZ_OVERRIDE;
virtual bool regexp_toShared(JSContext *cx, HandleObject proxy,
@ -248,7 +248,7 @@ class Proxy
static bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args);
static bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp);
static bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx);
static JSString *obj_toString(JSContext *cx, HandleObject proxy);
static const char *className(JSContext *cx, HandleObject proxy);
static JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent);
static bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g);
static bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp);

View File

@ -526,19 +526,11 @@ CrossCompartmentWrapper::hasInstance(JSContext *cx, HandleObject wrapper, Mutabl
return Wrapper::hasInstance(cx, wrapper, v, bp);
}
JSString *
CrossCompartmentWrapper::obj_toString(JSContext *cx, HandleObject wrapper)
const char *
CrossCompartmentWrapper::className(JSContext *cx, HandleObject wrapper)
{
RootedString str(cx);
{
AutoCompartment call(cx, wrappedObject(wrapper));
str = Wrapper::obj_toString(cx, wrapper);
if (!str)
return NULL;
}
if (!cx->compartment->wrap(cx, str.address()))
return NULL;
return str;
AutoCompartment call(cx, wrappedObject(wrapper));
return Wrapper::className(cx, wrapper);
}
JSString *
@ -789,10 +781,10 @@ DeadObjectProxy::objectClassIs(HandleObject obj, ESClassValue classValue, JSCont
return false;
}
JSString *
DeadObjectProxy::obj_toString(JSContext *cx, HandleObject wrapper)
const char *
DeadObjectProxy::className(JSContext *cx, HandleObject wrapper)
{
return JS_NewStringCopyZ(cx, "[object DeadObject]");
return "DeadObject";
}
JSString *

View File

@ -112,7 +112,7 @@ class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper
CallArgs args) MOZ_OVERRIDE;
virtual bool hasInstance(JSContext *cx, HandleObject wrapper, MutableHandleValue v,
bool *bp) MOZ_OVERRIDE;
virtual JSString *obj_toString(JSContext *cx, HandleObject wrapper) MOZ_OVERRIDE;
virtual const char *className(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE;
virtual JSString *fun_toString(JSContext *cx, HandleObject wrapper,
unsigned indent) MOZ_OVERRIDE;
virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) MOZ_OVERRIDE;
@ -190,15 +190,20 @@ class JS_FRIEND_API(DeadObjectProxy) : public BaseProxyHandler
virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE;
virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
CallArgs args) MOZ_OVERRIDE;
virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp);
virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx);
virtual JSString *obj_toString(JSContext *cx, HandleObject proxy);
virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent);
virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g);
virtual bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp);
virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v,
bool *bp) MOZ_OVERRIDE;
virtual bool objectClassIs(HandleObject obj, ESClassValue classValue,
JSContext *cx) MOZ_OVERRIDE;
virtual const char *className(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE;
virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) MOZ_OVERRIDE;
virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) MOZ_OVERRIDE;
virtual bool defaultValue(JSContext *cx, HandleObject obj, JSType hint,
MutableHandleValue vp) MOZ_OVERRIDE;
virtual bool getElementIfPresent(JSContext *cx, HandleObject obj, HandleObject receiver,
uint32_t index, MutableHandleValue vp, bool *present);
virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop);
uint32_t index, MutableHandleValue vp,
bool *present) MOZ_OVERRIDE;
virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy,
MutableHandleObject protop) MOZ_OVERRIDE;
static DeadObjectProxy singleton;
};

View File

@ -4173,8 +4173,8 @@ static JSBool
DebuggerObject_getClass(JSContext *cx, unsigned argc, Value *vp)
{
THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, "get class", args, refobj);
const char *s = refobj->getClass()->name;
JSAtom *str = Atomize(cx, s, strlen(s));
const char *className = JSObject::className(cx, refobj);
JSAtom *str = Atomize(cx, className, strlen(className));
if (!str)
return false;
args.rval().setString(str);