Пример #1
0
void
gjs_log_object_props(JSContext      *context,
                     JSObject       *obj,
                     GjsDebugTopic   topic,
                     const char     *prefix)
{
    JSObject *props_iter;
    jsid prop_id;

    JS_BeginRequest(context);

    /* We potentially create new strings, plus the property iterator,
     * that could get collected as we go through this process. So
     * create a local root scope.
     */
    JS_EnterLocalRootScope(context);

    props_iter = JS_NewPropertyIterator(context, obj);
    if (props_iter == NULL) {
        gjs_debug(GJS_DEBUG_ERROR,
                  "Failed to create property iterator for object props");
        goto done;
    }

    prop_id = JSVAL_VOID;
    if (!JS_NextProperty(context, props_iter, &prop_id))
        goto done;

    while (prop_id != JSVAL_VOID) {
        jsval nameval;
        const char *name;
        jsval propval;

        if (!JS_IdToValue(context, prop_id, &nameval))
            goto next;

        if (!gjs_get_string_id(nameval, &name))
            goto next;

        if (!gjs_object_get_property(context, obj, name, &propval))
            goto next;

        gjs_debug(topic,
                  "%s%s = '%s'",
                  prefix, name,
                  gjs_value_debug_string(context, propval));

    next:
        prop_id = JSVAL_VOID;
        if (!JS_NextProperty(context, props_iter, &prop_id))
            break;
    }

 done:
    JS_LeaveLocalRootScope(context);
    JS_EndRequest(context);
}
Пример #2
0
int
to_erl_object(ErlNifEnv* env, JSContext* cx, JSObject* obj, ERL_NIF_TERM* term)
{
    ERL_NIF_TERM* array = NULL;
    ERL_NIF_TERM list;
    ERL_NIF_TERM keyterm;
    ERL_NIF_TERM valterm;
    JSObject* iter;
    jsid idp;
    jsval val;
    int length;
    int index;
    int ret = ERROR;

    iter = JS_NewPropertyIterator(cx, obj);
    if(iter == NULL) goto done;

    length = 0;
    while(JS_NextProperty(cx, iter, &idp))
    {
        if(idp == JSID_VOID) break;
        length += 1;
    }
    
    array = enif_alloc(length * sizeof(ERL_NIF_TERM));
    if(array == NULL) goto done;
    
    iter = JS_NewPropertyIterator(cx, obj);
    if(iter == NULL) goto done;

    index = 0;
    while(JS_NextProperty(cx, iter, &idp))
    {
        if(idp == JSID_VOID)
        {
            list = enif_make_list_from_array(env, array, length);
            *term = enif_make_tuple1(env, list);
            ret = OK;
            goto done;
        }

        if(!JS_IdToValue(cx, idp, &val)) goto done;
        if(!to_erl_string(env, cx, val, &keyterm)) goto done;
        if(!JS_GetPropertyById(cx, obj, idp, &val)) goto done;
        if(!to_erl_intern(env, cx, val, &valterm)) goto done;
        
        array[index] = enif_make_tuple2(env, keyterm, valterm);
        index += 1;
    }

done:
    if(array != NULL) enif_free(array);
    return ret;
}
Пример #3
0
static JSBool
syck_enumerate(JSContext *cx, JSObject *obj, JSIterateOp op,
		  jsval *statep, jsid *idp)
{
    JSObject *iterator;
    JSBool ok = JS_FALSE;

if (_debug)
fprintf(stderr, "==> %s(%p,%p,%d,%p,%p)\n", __FUNCTION__, cx, obj, op, statep, idp);

    switch (op) {
    case JSENUMERATE_INIT:
	if ((iterator = JS_NewPropertyIterator(cx, obj)) == NULL)
	    goto exit;
	*statep = OBJECT_TO_JSVAL(iterator);
	if (idp)
	    *idp = JSVAL_ZERO;
	break;
    case JSENUMERATE_NEXT:
	iterator = (JSObject *) JSVAL_TO_OBJECT(*statep);
	if (!JS_NextProperty(cx, iterator, idp))
	    goto exit;
	if (*idp != JSVAL_VOID)
	    break;
	/*@fallthrough@*/
    case JSENUMERATE_DESTROY:
	/* Allow our iterator object to be GC'd. */
	*statep = JSVAL_NULL;
	break;
    }
    ok = JS_TRUE;
exit:
    return ok;
}
Пример #4
0
static PyObject *to_python_dict(JSContext *context, JSObject *obj) {
    JSObject *iter = JS_NewPropertyIterator(context, obj);
    if (iter) {
        PyObject *dict = PyDict_New();
        jsid propid;
        while (JS_NextProperty(context, iter, &propid) == JS_TRUE) {
            if (propid == JSID_VOID) {
                break;
            }
            jsval key, value;
            if (JS_IdToValue(context, propid, &key)) {
                PyObject *pykey = to_python_object(context, key);
                if (pykey != Py_None) {
                    if (JS_GetPropertyById(context, obj, propid, &value)) {
                        PyObject *pyvalue = to_python_object(context, value);
                        PyDict_SetItem(dict, pykey, pyvalue);
                        Py_DECREF(pykey);
                        Py_DECREF(pyvalue);
                    }
                }
            }
        }
        return dict;
    } else {
        Py_INCREF(Py_None);
        return Py_None;
    }
}
JSBool jsval_to_TProductInfo(JSContext *cx, jsval v, TProductInfo* ret)
{
    JSObject* tmp = JSVAL_TO_OBJECT(v);
    if (!tmp) {
        LOGD("jsval_to_TProductInfo: the jsval is not an object.");
        return JS_FALSE;
    }

    JSObject* it = JS_NewPropertyIterator(cx, tmp);

    while (true)
    {
        jsid idp;
        jsval key;
        if (! JS_NextProperty(cx, it, &idp) || ! JS_IdToValue(cx, idp, &key))
            return JS_FALSE; // error
        if (key == JSVAL_VOID)
            break; // end of iteration
        if (! JSVAL_IS_STRING(key))
            continue; // ignore integer properties
        jsval value;
        JS_GetPropertyById(cx, tmp, idp, &value);
        if (! JSVAL_IS_STRING(value))
            continue; // ignore integer properties

        JSStringWrapper strWrapper(JSVAL_TO_STRING(key), cx);
        JSStringWrapper strWrapper2(JSVAL_TO_STRING(value), cx);

        (*ret)[strWrapper.get()] = strWrapper2.get();
        LOGD("iterate object: key = %s, value = %s", strWrapper.get().c_str(), strWrapper2.get().c_str());
    }

    return JS_TRUE;
}
Пример #6
0
int
to_erl_object(emonk_buf_t* buf, JSContext* cx, JSObject* obj)
{
    JSObject* iter;
    JSString* key;
    jsid idp;
    jsval val;
    jschar* keyname;
    size_t keylen;
    int count = 0;
    int lengthpos;
    
    REQUEST(7);

    BUFPTR[0] = SMALL_TUPLE;
    BUFPTR[1] = (char) 1;
    BUFPTR[2] = LIST;
    
    // Remember the byte offset where length goes so we can write it
    // after enumerating the properties.
    lengthpos = buf->used + 3;
    buf->used += 7;
    
    iter = JS_NewPropertyIterator(cx, obj);
    if(iter == NULL) return ERROR;
    
    while(JS_NextProperty(cx, iter, &idp))
    {
        // Done iterating, write length and bail.
        if(idp == JSVAL_VOID)
        {
            count = htonl(count);
            memcpy(buf->buf+lengthpos, &count, 4);

            REQUEST(1);
            BUFPTR[0] = NIL;
            buf->used += 1;
            
            return OK;
        }

        REQUEST(2);
        BUFPTR[0] = SMALL_TUPLE;
        BUFPTR[1] = 2;
        buf->used += 2;

        if(!JS_IdToValue(cx, idp, &val)) return ERROR;
        if(!to_erl_string(buf, cx, val)) return ERROR;
        
        key = JS_ValueToString(cx, val);
        keyname = JS_GetStringChars(key);
        keylen = JS_GetStringLength(key);
        
        if(!JS_GetUCProperty(cx, obj, keyname, keylen, &val)) return ERROR;
        if(!to_erl_intern(buf, cx, val)) return ERROR;
        count += 1;
    }

    return ERROR;
}
bool jsval_to_FBInfo(JSContext *cx, jsval v, StringMap* ret)
{
	JSObject* tmp = JSVAL_TO_OBJECT(v);
	if (!tmp) {
		LOGD("jsval_to_TProductInfo: the jsval is not an object.");
		return false;
	}

	JSObject* it = JS_NewPropertyIterator(cx, tmp);

	while (true)
	{
		jsid idp;
		jsval key;
		if (! JS_NextProperty(cx, it, &idp) || ! JS_IdToValue(cx, idp, &key))
			return false; // error
		if (key == JSVAL_VOID)
			break; // end of iteration
		if (! JSVAL_IS_STRING(key))
			continue; // ignore integer properties
		JS::RootedValue value(cx);
		JS_GetPropertyById(cx, tmp, idp, &value);

//		if (! JSVAL_IS_STRING(value))
//			continue; // ignore integer properties
		if(JSVAL_IS_STRING(value))
		{

			JSStringWrapper strWrapper(JSVAL_TO_STRING(key), cx);
			JSStringWrapper strWrapper2(JSVAL_TO_STRING(value), cx);

			ret->insert(std::map<std::string, std::string>::value_type(strWrapper.get(), strWrapper2.get()));
		}
		else if(JSVAL_IS_NUMBER(value))
		{
			double number = 0.0;
			JS::ToNumber(cx, value, &number);

			std::stringstream ss;
			ss << number;

			JSStringWrapper strWrapper(JSVAL_TO_STRING(key), cx);
			//JSStringWrapper strWrapper2(JSVAL_TO_STRING(value), cx);

			ret->insert(std::map<std::string, std::string>::value_type(strWrapper.get(), ss.str()));
		}
		else if(JSVAL_IS_BOOLEAN(value))
		{
			bool boolVal = JS::ToBoolean(value);
			JSStringWrapper strWrapper(JSVAL_TO_STRING(key), cx);
			//JSStringWrapper strWrapper2(JSVAL_TO_STRING(value), cx);
			std::string boolstring = boolVal ? "true" : "false";
			ret->insert(std::map<std::string, std::string>::value_type(strWrapper.get(), boolstring));
		}
	}

	return true;
}
Пример #8
0
bool ScriptInterface::EnumeratePropertyNamesWithPrefix(JS::HandleValue objVal, const char* prefix, std::vector<std::string>& out)
{
	JSAutoRequest rq(m->m_cx);
	
	if (!objVal.isObjectOrNull())
	{
		LOGERROR("EnumeratePropertyNamesWithPrefix expected object type!");
		return false;
	}
		
	if(objVal.isNull())
		return true; // reached the end of the prototype chain
	
	JS::RootedObject obj(m->m_cx, &objVal.toObject());
	JS::RootedObject it(m->m_cx, JS_NewPropertyIterator(m->m_cx, obj));
	if (!it)
		return false;

	while (true)
	{
		JS::RootedId idp(m->m_cx);
		JS::RootedValue val(m->m_cx);
		if (! JS_NextProperty(m->m_cx, it, idp.address()) || ! JS_IdToValue(m->m_cx, idp, &val))
			return false;

		if (val.isUndefined())
			break; // end of iteration
		if (!val.isString())
			continue; // ignore integer properties

		JS::RootedString name(m->m_cx, val.toString());
		size_t len = strlen(prefix)+1;
		std::vector<char> buf(len);
		size_t prefixLen = strlen(prefix) * sizeof(char);
		JS_EncodeStringToBuffer(m->m_cx, name, &buf[0], prefixLen);
		buf[len-1]= '\0';
		if(0 == strcmp(&buf[0], prefix))
		{
			size_t len;
			const jschar* chars = JS_GetStringCharsAndLength(m->m_cx, name, &len);
			out.push_back(std::string(chars, chars+len));
		}
	}

	// Recurse up the prototype chain
	JS::RootedObject prototype(m->m_cx);
	if (JS_GetPrototype(m->m_cx, obj, &prototype))
	{
		JS::RootedValue prototypeVal(m->m_cx, JS::ObjectOrNullValue(prototype));
		if (! EnumeratePropertyNamesWithPrefix(prototypeVal, prefix, out))
			return false;
	}

	return true;
}
Пример #9
0
static void
load_module_elements(JSContext *context,
                     JSObject *in_object,
                     ImporterIterator *iter,
                     const char *init_path) {
    JSObject *module_obj;
    JSObject *jsiter;

    module_obj = load_module_init(context, in_object, init_path);

    if (module_obj != NULL) {
        jsid idp;

        jsiter = JS_NewPropertyIterator(context, module_obj);

        if (jsiter == NULL) {
            return;
        }

        if (!JS_NextProperty(context, jsiter, &idp)) {
            return;
        }

        while (!JSID_IS_VOID(idp)) {
            char *name;

            if (!gjs_get_string_id(context, idp, &name)) {
                continue;
            }

            /* Pass ownership of name */
            g_ptr_array_add(iter->elements, name);

            if (!JS_NextProperty(context, jsiter, &idp)) {
                break;
            }
        }
    }
}
Пример #10
0
static JSBool
rpmps_enumerate(JSContext *cx, JSObject *obj, JSIterateOp op,
		  jsval *statep, jsid *idp)
{

_ENUMERATE_DEBUG_ENTRY(_debug);

#ifdef	DYING
    switch (op) {
    case JSENUMERATE_INIT:
    case JSENUMERATE_INIT_ALL:
	if ((iterator = JS_NewPropertyIterator(cx, obj)) == NULL)
	    goto exit;
	*statep = OBJECT_TO_JSVAL(iterator);
	if (idp)
	    *idp = JSVAL_ZERO;
	break;
    case JSENUMERATE_NEXT:
	iterator = (JSObject *) JSVAL_TO_OBJECT(*statep);
	if (!JS_NextProperty(cx, iterator, idp))
	    goto exit;
	if (!JSID_IS_VOID(*idp))
	    break;
	/*@fallthrough@*/
    case JSENUMERATE_DESTROY:
	/* Allow our iterator object to be GC'd. */
	*statep = JSVAL_NULL;
	break;
    }
#else
    {	static const char hex[] = "0123456789abcdef";
	const char * s;
	char name[2];
	JSString * valstr;
	char value[5];
	for (s = "AaBbCc"; *s != '\0'; s++) {
	    name[0] = s[0]; name[1] = '\0';
	    value[0] = '0'; value[1] = 'x';
	    value[2] = hex[(name[0] >> 4) & 0xf];
	    value[3] = hex[(name[0]     ) & 0xf];
	    value[4] = '\0';
 	    if ((valstr = JS_NewStringCopyZ(cx, value)) == NULL
	     || !JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr),
				NULL, NULL, JSPROP_ENUMERATE))
		goto exit;
	}
    }
#endif
exit:
    return JS_TRUE;
}
bool jsval_to_std_map_string_string(JSContext *cx, jsval v, std::map<std::string, std::string>* ret)
{
    if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
    {
        return true;
    }
    
    JSObject* tmp = JSVAL_TO_OBJECT(v);
    if (!tmp) {
        CCLOG("%s", "jsval_to_ccvaluemap: the jsval is not an object.");
        return false;
    }
    
    JSObject* it = JS_NewPropertyIterator(cx, tmp);
    
    while (true)
    {
        jsid idp;
        jsval key;
        if (! JS_NextProperty(cx, it, &idp) || ! JS_IdToValue(cx, idp, &key)) {
            return false; // error
        }
        
        if (key == JSVAL_VOID) {
            break; // end of iteration
        }
        
        if (!JSVAL_IS_STRING(key)) {
            continue; // ignore integer properties
        }
        
        JSStringWrapper keyWrapper(JSVAL_TO_STRING(key), cx);
        
        JS::RootedValue value(cx);
        JS_GetPropertyById(cx, tmp, idp, &value);
        if (value.isString())
        {
            JSStringWrapper valueWapper(JSVAL_TO_STRING(value), cx);
            ret->insert(std::make_pair(keyWrapper.get(), valueWapper.get()));
        }
        else
        {
            CCASSERT(false, "not a string");
        }
    }
        
    return true;
}
Пример #12
0
	JSBool JsGlobal::event_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
		jsval *statep, jsid *idp)
	{
		static log4cplus::Logger log = log4cplus::Logger::getInstance("fsm.JsContext.enumerate");
		JSObject *iterator;
		//fsm::env::Js::ToString objString(cx,OBJECT_TO_JSVAL(obj));
		LOG4CPLUS_ERROR(log,"[_event] enumerate.");
		switch (enum_op) {
		case JSENUMERATE_INIT:
		case JSENUMERATE_INIT_ALL:
			LOG4CPLUS_TRACE(log, "enumerate [_event] Init properties.");

			iterator = JS_NewPropertyIterator(cx, obj);
			if (!iterator)
				return JS_FALSE;

			*statep = OBJECT_TO_JSVAL(iterator);
			if (idp)
				*idp = INT_TO_JSID(0);
			break;

		case JSENUMERATE_NEXT:
			/*if (its_enum_fail) {
				JS_ReportError(cx, "its enumeration failed");
				return JS_FALSE;
			}*/
			LOG4CPLUS_TRACE(log, "enumerate [_event] next properties.");
			iterator = (JSObject *) JSVAL_TO_OBJECT(*statep);
			if (!JS_NextProperty(cx, iterator, idp))
				return JS_FALSE;

			if (!JSID_IS_VOID(*idp))
				break;
			/* Fall through. */

		case JSENUMERATE_DESTROY:
			/* Allow our iterator object to be GC'd. */
			LOG4CPLUS_TRACE(log, "enumerate [_event] destroy properties.");
			*statep = JSVAL_NULL;
			break;
		}

		return JS_TRUE;
	}
Пример #13
0
bool ScriptInterface::EnumeratePropertyNamesWithPrefix(jsval obj, const char* prefix, std::vector<std::string>& out)
{
	utf16string prefix16 (prefix, prefix+strlen(prefix));

	if (! JSVAL_IS_OBJECT(obj))
		return false; // TODO: log error messages

	JSObject* it = JS_NewPropertyIterator(m->m_cx, JSVAL_TO_OBJECT(obj));
	if (!it)
		return false;

	while (true)
	{
		jsid idp;
		jsval val;
		if (! JS_NextProperty(m->m_cx, it, &idp) || ! JS_IdToValue(m->m_cx, idp, &val))
			return false;
		if (val == JSVAL_VOID)
			break; // end of iteration
		if (! JSVAL_IS_STRING(val))
			continue; // ignore integer properties

		JSString* name = JSVAL_TO_STRING(val);
		size_t len;
		const jschar* chars = JS_GetStringCharsAndLength(m->m_cx, name, &len);
		if (chars && len >= prefix16.size() && memcmp(chars, prefix16.c_str(), prefix16.size()*2) == 0)
			out.push_back(std::string(chars, chars+len)); // handles Unicode poorly
	}

	// Recurse up the prototype chain
	JSObject* prototype = JS_GetPrototype(m->m_cx, JSVAL_TO_OBJECT(obj));
	if (prototype)
	{
		if (! EnumeratePropertyNamesWithPrefix(OBJECT_TO_JSVAL(prototype), prefix, out))
			return false;
	}

	return true;
}
Пример #14
0
// Convert from a jsval to an AtNode
static AtSmartPtr<AtNode> ConvertNode(JSContext* cx, jsval node)
{
	AtSmartPtr<AtNode> obj (new AtNode());

	// Non-objects get converted into strings
	if (!JSVAL_IS_OBJECT(node))
	{
		JSString* str = JS_ValueToString(cx, node);
		if (!str)
			return obj; // error
		size_t valueLen;
		const jschar* valueChars = JS_GetStringCharsAndLength(cx, str, &valueLen);
		if (!valueChars)
			return obj; // error
		wxString valueWx(reinterpret_cast<const char*>(valueChars), wxMBConvUTF16(), valueLen*2);

		obj->value = valueWx.c_str();

		// Annotate numbers/booleans specially, to allow round-tripping
		if (JSVAL_IS_NUMBER(node))
		{
			obj->children.insert(AtNode::child_pairtype(
				"@number", AtSmartPtr<AtNode>(new AtNode())
			));
		}
		else if (JSVAL_IS_BOOLEAN(node))
		{
			obj->children.insert(AtNode::child_pairtype(
				"@boolean", AtSmartPtr<AtNode>(new AtNode())
			));
		}

		return obj;
	}

	JSObject* it = JS_NewPropertyIterator(cx, JSVAL_TO_OBJECT(node));
	if (!it)
		return obj; // error

	while (true)
	{
		jsid idp;
		jsval val;
		if (! JS_NextProperty(cx, it, &idp) || ! JS_IdToValue(cx, idp, &val))
			return obj; // error
		if (val == JSVAL_VOID)
			break; // end of iteration
		if (! JSVAL_IS_STRING(val))
			continue; // ignore integer properties

		JSString* name = JSVAL_TO_STRING(val);
		size_t len;
		const jschar* chars = JS_GetStringCharsAndLength(cx, name, &len);
		wxString nameWx(reinterpret_cast<const char*>(chars), wxMBConvUTF16(), len*2);
		std::string nameStr(nameWx.ToUTF8().data());

		jsval vp;
		if (!JS_GetPropertyById(cx, JSVAL_TO_OBJECT(node), idp, &vp))
			return obj; // error

		// Unwrap arrays into a special format like <$name><item>$i0</item><item>...
		// (This assumes arrays aren't nested)
		if (JSVAL_IS_OBJECT(vp) && JS_IsArrayObject(cx, JSVAL_TO_OBJECT(vp)))
		{
			AtSmartPtr<AtNode> child(new AtNode());
			child->children.insert(AtNode::child_pairtype(
				"@array", AtSmartPtr<AtNode>(new AtNode())
			));

			jsuint arrayLength;
			if (!JS_GetArrayLength(cx, JSVAL_TO_OBJECT(vp), &arrayLength))
				return obj; // error

			for (jsuint i = 0; i < arrayLength; ++i)
			{
				jsval val;
				if (!JS_GetElement(cx, JSVAL_TO_OBJECT(vp), i, &val))
					return obj; // error

				child->children.insert(AtNode::child_pairtype(
					"item", ConvertNode(cx, val)
				));
			}

			obj->children.insert(AtNode::child_pairtype(
				nameStr, child
			));
		}
		else
		{
			obj->children.insert(AtNode::child_pairtype(
				nameStr, ConvertNode(cx, vp)
			));
		}
	}

	return obj;
}
Пример #15
0
/* Initialize a newly created Boxed from an object that is a "hash" of
 * properties to set as fieds of the object. We don't require that every field
 * of the object be set.
 */
static JSBool
boxed_init_from_props(JSContext   *context,
                      JSObject    *obj,
                      Boxed       *priv,
                      jsval        props_value)
{
    JSObject *props;
    JSObject *iter;
    jsid prop_id;
    GHashTable *field_map;
    gboolean success;

    success = FALSE;

    if (!JSVAL_IS_OBJECT(props_value)) {
        gjs_throw(context, "argument should be a hash with fields to set");
        return JS_FALSE;
    }

    props = JSVAL_TO_OBJECT(props_value);

    iter = JS_NewPropertyIterator(context, props);
    if (iter == NULL) {
        gjs_throw(context, "Failed to create property iterator for fields hash");
        return JS_FALSE;
    }

    field_map = get_field_map(priv->info);

    prop_id = JSID_VOID;
    if (!JS_NextProperty(context, iter, &prop_id))
        goto out;

    while (!JSID_IS_VOID(prop_id)) {
        GIFieldInfo *field_info;
        char *name;
        jsval value;

        if (!gjs_get_string_id(context, prop_id, &name))
            goto out;

        field_info = g_hash_table_lookup(field_map, name);
        if (field_info == NULL) {
            gjs_throw(context, "No field %s on boxed type %s",
                      name, g_base_info_get_name((GIBaseInfo *)priv->info));
            g_free(name);
            goto out;
        }

        if (!gjs_object_require_property(context, props, "property list", name, &value)) {
            g_free(name);
            goto out;
        }
        g_free(name);

        if (!boxed_set_field_from_value(context, priv, field_info, value))
            goto out;

        prop_id = JSID_VOID;
        if (!JS_NextProperty(context, iter, &prop_id))
            goto out;
    }

    success = TRUE;

 out:
    g_hash_table_destroy(field_map);

    return success;
}
Пример #16
0
nsresult
leakmonJSObjectInfo::Init(leakmonObjectsInReportTable &aObjectsInReport)
{
	mIsInitialized = PR_TRUE;

	JSContext *cx = leakmonService::GetJSContext();
	NS_ENSURE_TRUE(cx, NS_ERROR_UNEXPECTED);

	JSAutoRequest ar(cx);

	if (!JSVAL_IS_PRIMITIVE(mJSValue)) {
		JSObject *obj = JSVAL_TO_OBJECT(mJSValue);

		// All of the objects in obj's prototype chain, and all
		// objects reachable from JS_NewPropertyIterator should
		// (I think?) be in the same compartment.
		JSAutoEnterCompartment ac;
		if (!ac.enter(cx, obj)) {
			return NS_ERROR_FAILURE;
		}

		JSObject *p;

		for (p = obj; p; p = JS_GetPrototype(cx, p)) {
			// Stack-scanning protects newly-created objects
			// (etor) from GC.  (And protecting |etor|
			// should in turn protect |id|.)

			// JS_NewPropertyIterator has the nice property that it
			// avoids JS_Enumerate on native objects (where it can
			// execute code) and uses the scope properties, but doesn't
			// require this code to use the unstable OBJ_IS_NATIVE API.
			JSObject *etor = JS_NewPropertyIterator(cx, p);
			if (!etor)
			    return NS_ERROR_OUT_OF_MEMORY;

			jsid id;
			while (JS_NextProperty(cx, etor, &id) && !JSID_IS_VOID(id)) {
				nsresult rv = AppendProperty(id, cx, aObjectsInReport);
				NS_ENSURE_SUCCESS(rv, rv);
			}
		}

		if (JS_ObjectIsFunction(cx, obj)) {
			JSFunction *fun = JS_ValueToFunction(cx, mJSValue);
			NS_ENSURE_TRUE(fun, NS_ERROR_UNEXPECTED);
			JSScript *script = JS_GetFunctionScript(cx, fun);
			if (script) { // null for native code
				const char *fname = JS_GetScriptFilename(cx, script);
				// XXX Do we know the encoding of this file name?
				mFileName = NS_ConvertUTF8toUTF16(fname);

				mLineStart = JS_GetScriptBaseLineNumber(cx, script);
				mLineEnd = mLineStart + JS_GetScriptLineExtent(cx, script) - 1;
			}
		}
	}

	ValueToString(cx, mJSValue, mString);

	return NS_OK;
}