Bug 548702 - Temporary value-rooting scheme should be C++- and RAII-based, not macro-based. r=igor

This commit is contained in:
Jeff Walden
2010-01-11 11:52:21 -06:00
parent 9a0812f239
commit e1d797671d
50 changed files with 1384 additions and 1389 deletions

View File

@@ -70,10 +70,14 @@
#include "jsstaticcheck.h"
#include "jsvector.h"
#include "jscntxtinlines.h"
#ifdef DEBUG
#include <string.h> /* for #ifdef DEBUG memset calls */
#endif
using namespace js;
/*
* NOTES
* - in the js shell, you must use the -x command line option, or call
@@ -866,60 +870,10 @@ attr_identity(const void *a, const void *b)
return qname_identity(xmla->name, xmlb->name);
}
struct JSXMLArrayCursor
{
JSXMLArray *array;
uint32 index;
JSXMLArrayCursor *next;
JSXMLArrayCursor **prevp;
void *root;
JSXMLArrayCursor(JSXMLArray *array)
: array(array), index(0), next(array->cursors), prevp(&array->cursors),
root(NULL)
{
if (next)
next->prevp = &next;
array->cursors = this;
}
~JSXMLArrayCursor() { disconnect(); }
void disconnect() {
if (!array)
return;
if (next)
next->prevp = prevp;
*prevp = next;
array = NULL;
}
void *getNext() {
if (!array || index >= array->length)
return NULL;
return root = array->vector[index++];
}
void *getCurrent() {
if (!array || index >= array->length)
return NULL;
return root = array->vector[index];
}
};
static void
XMLArrayCursorTrace(JSTracer *trc, JSXMLArrayCursor *cursor)
{
void *root;
#ifdef DEBUG
size_t index = 0;
#endif
for (; cursor; cursor = cursor->next) {
root = cursor->root;
JS_SET_TRACING_INDEX(trc, "cursor_root", index++);
js_CallValueTracerIfGCThing(trc, (jsval)root);
}
cursor->trace(trc);
}
/* NB: called with null cx from the GC, via xml_trace => XMLArrayTrim. */
@@ -3848,12 +3802,10 @@ GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
JSObject *kidobj, *listobj;
JSObject *nameqn;
jsid funid;
jsval roots[2];
JSTempValueRooter tvr;
xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL);
if (!xml)
return JS_TRUE;
return true;
if (js_IdIsIndex(id, &index)) {
if (xml->xml_class != JSXML_CLASS_LIST) {
@@ -3870,18 +3822,18 @@ GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
if (!kid) {
*vp = JSVAL_VOID;
return JS_TRUE;
return true;
}
kidobj = js_GetXMLObject(cx, kid);
if (!kidobj)
return JS_FALSE;
return false;
*vp = OBJECT_TO_JSVAL(kidobj);
} else {
*vp = JSVAL_VOID;
}
}
return JS_TRUE;
return true;
}
/*
@@ -3889,37 +3841,34 @@ GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
*/
nameqn = ToXMLName(cx, id, &funid);
if (!nameqn)
return JS_FALSE;
return false;
if (funid)
return GetXMLFunction(cx, obj, funid, vp);
roots[0] = OBJECT_TO_JSVAL(nameqn);
JS_PUSH_TEMP_ROOT(cx, 1, roots, &tvr);
jsval roots[2] = { OBJECT_TO_JSVAL(nameqn), JSVAL_NULL };
AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(roots), roots);
listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
if (listobj) {
roots[1] = OBJECT_TO_JSVAL(listobj);
tvr.count++;
if (!listobj)
return false;
list = (JSXML *) listobj->getPrivate();
if (!GetNamedProperty(cx, xml, nameqn, list)) {
listobj = NULL;
} else {
/*
* Erratum: ECMA-357 9.1.1.1 misses that [[Append]] sets the
* given list's [[TargetProperty]] to the property that is being
* appended. This means that any use of the internal [[Get]]
* property returns a list which, when used by e.g. [[Insert]]
* duplicates the last element matched by id. See bug 336921.
*/
list->xml_target = xml;
list->xml_targetprop = nameqn;
*vp = OBJECT_TO_JSVAL(listobj);
}
}
roots[1] = OBJECT_TO_JSVAL(listobj);
JS_POP_TEMP_ROOT(cx, &tvr);
return listobj != NULL;
list = (JSXML *) listobj->getPrivate();
if (!GetNamedProperty(cx, xml, nameqn, list))
return false;
/*
* Erratum: ECMA-357 9.1.1.1 misses that [[Append]] sets the
* given list's [[TargetProperty]] to the property that is being
* appended. This means that any use of the internal [[Get]]
* property returns a list which, when used by e.g. [[Insert]]
* duplicates the last element matched by id. See bug 336921.
*/
list->xml_target = xml;
list->xml_targetprop = nameqn;
*vp = OBJECT_TO_JSVAL(listobj);
return true;
}
static JSXML *
@@ -3963,8 +3912,6 @@ PutProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSBool ok, primitiveAssign;
enum { OBJ_ROOT, ID_ROOT, VAL_ROOT };
jsval roots[3];
JSTempValueRooter tvr;
JSXML *xml, *vxml, *rxml, *kid, *attr, *parent, *copy, *kid2, *match;
JSObject *vobj, *nameobj, *attrobj, *parentobj, *kidobj, *copyobj;
JSObject *targetprop, *nameqn, *attrqn;
@@ -3993,11 +3940,13 @@ PutProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
ok = js_EnterLocalRootScope(cx);
if (!ok)
return JS_FALSE;
MUST_FLOW_THROUGH("out");
jsval roots[3];
roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj);
roots[ID_ROOT] = id;
roots[VAL_ROOT] = *vp;
JS_PUSH_TEMP_ROOT(cx, 3, roots, &tvr);
AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(roots), roots);
if (js_IdIsIndex(id, &index)) {
if (xml->xml_class != JSXML_CLASS_LIST) {
@@ -4584,7 +4533,6 @@ PutProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
}
out:
JS_POP_TEMP_ROOT(cx, &tvr);
js_LeaveLocalRootScope(cx);
return ok;
@@ -4716,38 +4664,34 @@ HasFunctionProperty(JSContext *cx, JSObject *obj, jsid funid, JSBool *found)
JSObject *pobj;
JSProperty *prop;
JSXML *xml;
JSTempValueRooter tvr;
JSBool ok;
JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_XMLClass);
if (!js_LookupProperty(cx, obj, funid, &pobj, &prop))
return JS_FALSE;
return false;
if (prop) {
pobj->dropProperty(cx, prop);
} else {
xml = (JSXML *) obj->getPrivate();
if (HasSimpleContent(xml)) {
AutoObjectRooter tvr(cx);
/*
* Search in String.prototype to set found whenever
* GetXMLFunction returns existing function.
*/
JS_PUSH_TEMP_ROOT_OBJECT(cx, NULL, &tvr);
ok = js_GetClassPrototype(cx, NULL, JSProto_String,
&tvr.u.object);
JS_ASSERT(tvr.u.object);
if (ok) {
ok = js_LookupProperty(cx, tvr.u.object, funid, &pobj, &prop);
if (ok && prop)
pobj->dropProperty(cx, prop);
}
JS_POP_TEMP_ROOT(cx, &tvr);
if (!ok)
return JS_FALSE;
if (!js_GetClassPrototype(cx, NULL, JSProto_String, tvr.addr()))
return false;
JS_ASSERT(tvr.object());
if (!js_LookupProperty(cx, tvr.object(), funid, &pobj, &prop))
return false;
if (prop)
pobj->dropProperty(cx, prop);
}
}
*found = (prop != NULL);
return JS_TRUE;
return true;
}
/* ECMA-357 9.1.1.6 XML [[HasProperty]] and 9.2.1.5 XMLList [[HasProperty]]. */
@@ -5014,18 +4958,22 @@ xml_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
switch (enum_op) {
case JSENUMERATE_INIT:
if (length == 0) {
cursor = NULL;
*statep = JSVAL_ZERO;
} else {
cursor = cx->create<JSXMLArrayCursor>(&xml->xml_kids);
if (!cursor)
return JS_FALSE;
*statep = PRIVATE_TO_JSVAL(cursor);
}
*statep = PRIVATE_TO_JSVAL(cursor);
if (idp)
*idp = INT_TO_JSID(length);
break;
case JSENUMERATE_NEXT:
if (*statep == JSVAL_ZERO) {
*statep = JSVAL_NULL;
break;
}
cursor = (JSXMLArrayCursor *) JSVAL_TO_PRIVATE(*statep);
if (cursor && cursor->array && (index = cursor->index) < length) {
*idp = INT_TO_JSID(index);
@@ -5035,9 +4983,11 @@ xml_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
/* FALL THROUGH */
case JSENUMERATE_DESTROY:
cursor = (JSXMLArrayCursor *) JSVAL_TO_PRIVATE(*statep);
if (cursor)
cx->destroy(cursor);
if (*statep != JSVAL_ZERO) {
cursor = (JSXMLArrayCursor *) JSVAL_TO_PRIVATE(*statep);
if (cursor)
cx->destroy(cursor);
}
*statep = JSVAL_NULL;
break;
}
@@ -5126,7 +5076,7 @@ js_GetXMLMethod(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
* As our callers have a bad habit of passing a pointer to an unrooted
* local value as vp, we use a proper root here.
*/
JSAutoTempValueRooter tvr(cx);
AutoValueRooter tvr(cx);
JSBool ok = GetXMLFunction(cx, obj, id, tvr.addr());
*vp = tvr.value();
return ok;
@@ -5149,20 +5099,22 @@ js_EnumerateXMLValues(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
switch (enum_op) {
case JSENUMERATE_INIT:
if (length == 0) {
cursor = NULL;
*statep = JSVAL_ZERO;
} else {
cursor = cx->create<JSXMLArrayCursor>(&xml->xml_kids);
if (!cursor)
return JS_FALSE;
*statep = PRIVATE_TO_JSVAL(cursor);
}
*statep = PRIVATE_TO_JSVAL(cursor);
if (idp)
*idp = INT_TO_JSID(length);
if (vp)
*vp = JSVAL_VOID;
JS_ASSERT(!idp);
JS_ASSERT(!vp);
break;
case JSENUMERATE_NEXT:
if (*statep == JSVAL_ZERO) {
*statep = JSVAL_NULL;
break;
}
cursor = (JSXMLArrayCursor *) JSVAL_TO_PRIVATE(*statep);
if (cursor && cursor->array && (index = cursor->index) < length) {
while (!(kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML))) {
@@ -5181,10 +5133,12 @@ js_EnumerateXMLValues(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
/* FALL THROUGH */
case JSENUMERATE_DESTROY:
cursor = (JSXMLArrayCursor *) JSVAL_TO_PRIVATE(*statep);
if (cursor) {
destroy:
cx->destroy(cursor);
if (*statep != JSVAL_ZERO) {
cursor = (JSXMLArrayCursor *) JSVAL_TO_PRIVATE(*statep);
if (cursor) {
destroy:
cx->destroy(cursor);
}
}
*statep = JSVAL_NULL;
break;
@@ -5477,7 +5431,7 @@ xml_attributes(JSContext *cx, uintN argc, jsval *vp)
return JS_FALSE;
name = OBJECT_TO_JSVAL(qn);
JSAutoTempValueRooter tvr(cx, name);
AutoValueRooter tvr(cx, name);
return GetProperty(cx, JS_THIS_OBJECT(cx, vp), name, vp);
}
@@ -5883,81 +5837,6 @@ xml_hasSimpleContent(JSContext *cx, uintN argc, jsval *vp)
return JS_TRUE;
}
typedef struct JSTempRootedNSArray {
JSTempValueRooter tvr;
JSXMLArray array;
jsval value; /* extra root for temporaries */
} JSTempRootedNSArray;
static void
TraceObjectVector(JSTracer *trc, JSObject **vec, uint32 len)
{
uint32 i;
JSObject *obj;
for (i = 0; i < len; i++) {
obj = vec[i];
if (obj) {
JS_SET_TRACING_INDEX(trc, "vector", i);
js_CallGCMarker(trc, obj, JSTRACE_OBJECT);
}
}
}
static void
trace_temp_ns_array(JSTracer *trc, JSTempValueRooter *tvr)
{
JSTempRootedNSArray *tmp = (JSTempRootedNSArray *)tvr;
TraceObjectVector(trc,
(JSObject **) tmp->array.vector,
tmp->array.length);
XMLArrayCursorTrace(trc, tmp->array.cursors);
JS_CALL_VALUE_TRACER(trc, tmp->value, "temp_ns_array_value");
}
static void
InitTempNSArray(JSContext *cx, JSTempRootedNSArray *tmp)
{
XMLArrayInit(cx, &tmp->array, 0);
tmp->value = JSVAL_NULL;
JS_PUSH_TEMP_ROOT_TRACE(cx, trace_temp_ns_array, &tmp->tvr);
}
static void
FinishTempNSArray(JSContext *cx, JSTempRootedNSArray *tmp)
{
JS_ASSERT(tmp->tvr.u.trace == trace_temp_ns_array);
JS_POP_TEMP_ROOT(cx, &tmp->tvr);
XMLArrayFinish(cx, &tmp->array);
}
/*
* Populate a new JS array with elements of JSTempRootedNSArray.array and
* place the result into rval. rval must point to a rooted location.
*/
static JSBool
TempNSArrayToJSArray(JSContext *cx, JSTempRootedNSArray *tmp, jsval *rval)
{
JSObject *arrayobj;
uint32 i, n;
JSObject *ns;
arrayobj = js_NewArrayObject(cx, 0, NULL);
if (!arrayobj)
return JS_FALSE;
*rval = OBJECT_TO_JSVAL(arrayobj);
for (i = 0, n = tmp->array.length; i < n; i++) {
ns = XMLARRAY_MEMBER(&tmp->array, i, JSObject);
if (!ns)
continue;
tmp->value = OBJECT_TO_JSVAL(ns);
if (!arrayobj->setProperty(cx, INT_TO_JSID(i), &tmp->value))
return JS_FALSE;
}
return JS_TRUE;
}
static JSBool
FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray *nsarray)
{
@@ -5999,19 +5878,48 @@ FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray *nsarray)
return JS_TRUE;
}
class AutoNamespaceArray : public js::AutoNamespaces {
public:
AutoNamespaceArray(JSContext *cx)
: js::AutoNamespaces(cx)
{
XMLArrayInit(cx, &array, 0);
}
~AutoNamespaceArray() {
XMLArrayFinish(context, &array);
}
/*
* Populate a new JS array with elements of array and place the result into
* rval. rval must point to a rooted location.
*/
bool toJSArray(jsval *rval) {
JSObject *arrayobj = js_NewArrayObject(context, 0, NULL);
if (!arrayobj)
return JS_FALSE;
*rval = OBJECT_TO_JSVAL(arrayobj);
AutoValueRooter tvr(context);
for (uint32 i = 0, n = array.length; i < n; i++) {
JSObject *ns = XMLARRAY_MEMBER(&array, i, JSObject);
if (!ns)
continue;
*tvr.addr() = OBJECT_TO_JSVAL(ns);
if (!arrayobj->setProperty(context, INT_TO_JSID(i), tvr.addr()))
return JS_FALSE;
}
return JS_TRUE;
}
};
static JSBool
xml_inScopeNamespaces(JSContext *cx, uintN argc, jsval *vp)
{
JSTempRootedNSArray namespaces;
JSBool ok;
NON_LIST_XML_METHOD_PROLOG;
InitTempNSArray(cx, &namespaces);
ok = FindInScopeNamespaces(cx, xml, &namespaces.array) &&
TempNSArrayToJSArray(cx, &namespaces, vp);
FinishTempNSArray(cx, &namespaces);
return ok;
AutoNamespaceArray namespaces(cx);
return FindInScopeNamespaces(cx, xml, &namespaces.array) && namespaces.toJSArray(vp);
}
static JSBool
@@ -6111,15 +6019,13 @@ static JSBool
xml_namespace(JSContext *cx, uintN argc, jsval *vp)
{
JSString *prefix, *nsprefix;
JSTempRootedNSArray inScopeNSes;
JSBool ok;
jsuint i, length;
JSObject *ns;
NON_LIST_XML_METHOD_PROLOG;
if (argc == 0 && !JSXML_HAS_NAME(xml)) {
*vp = JSVAL_NULL;
return JS_TRUE;
return true;
}
if (argc == 0) {
@@ -6127,22 +6033,18 @@ xml_namespace(JSContext *cx, uintN argc, jsval *vp)
} else {
prefix = js_ValueToString(cx, vp[2]);
if (!prefix)
return JS_FALSE;
return false;
vp[2] = STRING_TO_JSVAL(prefix); /* local root */
}
InitTempNSArray(cx, &inScopeNSes);
MUST_FLOW_THROUGH("out");
ok = FindInScopeNamespaces(cx, xml, &inScopeNSes.array);
if (!ok)
goto out;
AutoNamespaceArray inScopeNSes(cx);
if (!FindInScopeNamespaces(cx, xml, &inScopeNSes.array))
return false;
if (!prefix) {
ns = GetNamespace(cx, xml->name, &inScopeNSes.array);
if (!ns) {
ok = JS_FALSE;
goto out;
}
if (!ns)
return false;
} else {
ns = NULL;
for (i = 0, length = inScopeNSes.array.length; i < length; i++) {
@@ -6157,64 +6059,44 @@ xml_namespace(JSContext *cx, uintN argc, jsval *vp)
}
*vp = (!ns) ? JSVAL_VOID : OBJECT_TO_JSVAL(ns);
out:
FinishTempNSArray(cx, &inScopeNSes);
return JS_TRUE;
return true;
}
static JSBool
xml_namespaceDeclarations(JSContext *cx, uintN argc, jsval *vp)
{
JSBool ok;
JSTempRootedNSArray ancestors, declared;
JSXML *yml;
uint32 i, n;
JSObject *ns;
NON_LIST_XML_METHOD_PROLOG;
if (JSXML_HAS_VALUE(xml))
return JS_TRUE;
return true;
/* From here, control flow must goto out to finish these arrays. */
ok = JS_TRUE;
InitTempNSArray(cx, &ancestors);
InitTempNSArray(cx, &declared);
yml = xml;
AutoNamespaceArray ancestors(cx);
AutoNamespaceArray declared(cx);
JSXML *yml = xml;
while ((yml = yml->parent) != NULL) {
JS_ASSERT(yml->xml_class == JSXML_CLASS_ELEMENT);
for (i = 0, n = yml->xml_namespaces.length; i < n; i++) {
ns = XMLARRAY_MEMBER(&yml->xml_namespaces, i, JSObject);
if (ns &&
!XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) {
ok = XMLARRAY_APPEND(cx, &ancestors.array, ns);
if (!ok)
goto out;
for (uint32 i = 0, n = yml->xml_namespaces.length; i < n; i++) {
JSObject *ns = XMLARRAY_MEMBER(&yml->xml_namespaces, i, JSObject);
if (ns && !XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) {
if (!XMLARRAY_APPEND(cx, &ancestors.array, ns))
return false;
}
}
}
for (i = 0, n = xml->xml_namespaces.length; i < n; i++) {
ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
for (uint32 i = 0, n = xml->xml_namespaces.length; i < n; i++) {
JSObject *ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSObject);
if (!ns)
continue;
if (!IsDeclared(ns))
continue;
if (!XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) {
ok = XMLARRAY_APPEND(cx, &declared.array, ns);
if (!ok)
goto out;
if (!XMLARRAY_APPEND(cx, &declared.array, ns))
return false;
}
}
ok = TempNSArrayToJSArray(cx, &declared, vp);
out:
/* Finishing must be in reverse order of initialization to follow LIFO. */
FinishTempNSArray(cx, &declared);
FinishTempNSArray(cx, &ancestors);
return ok;
return declared.toJSArray(vp);
}
static const char js_attribute_str[] = "attribute";
@@ -7247,9 +7129,9 @@ js_TraceXML(JSTracer *trc, JSXML *xml)
if (xml->xml_targetprop)
JS_CALL_OBJECT_TRACER(trc, xml->xml_targetprop, "targetprop");
} else {
TraceObjectVector(trc,
(JSObject **) xml->xml_namespaces.vector,
xml->xml_namespaces.length);
js::TraceObjectVector(trc,
(JSObject **) xml->xml_namespaces.vector,
xml->xml_namespaces.length);
XMLArrayCursorTrace(trc, xml->xml_namespaces.cursors);
if (IS_GC_MARKING_TRACER(trc))
XMLArrayTrim(&xml->xml_namespaces);
@@ -7282,17 +7164,12 @@ js_FinalizeXML(JSContext *cx, JSXML *xml)
JSObject *
js_NewXMLObject(JSContext *cx, JSXMLClass xml_class)
{
JSXML *xml;
JSObject *obj;
JSTempValueRooter tvr;
xml = js_NewXML(cx, xml_class);
JSXML *xml = js_NewXML(cx, xml_class);
if (!xml)
return NULL;
JS_PUSH_TEMP_ROOT_XML(cx, xml, &tvr);
obj = js_GetXMLObject(cx, xml);
JS_POP_TEMP_ROOT(cx, &tvr);
return obj;
AutoXMLRooter root(cx, xml);
return js_GetXMLObject(cx, xml);
}
static JSObject *
@@ -7809,48 +7686,35 @@ js_FindXMLProperty(JSContext *cx, jsval nameval, JSObject **objp, jsid *idp)
static JSBool
GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
JSObject *target;
JSXML *xml;
JSTempValueRooter tvr;
JSBool ok;
JS_ASSERT(OBJECT_IS_XML(cx, obj));
MUST_FLOW_THROUGH("out");
JS_PUSH_TEMP_ROOT_OBJECT(cx, NULL, &tvr);
/*
* See comments before xml_lookupProperty about the need for the proto
* chain lookup.
*/
target = obj;
JSObject *target = obj;
AutoObjectRooter tvr(cx);
for (;;) {
ok = js_GetProperty(cx, target, id, vp);
if (!ok)
goto out;
if (VALUE_IS_FUNCTION(cx, *vp)) {
ok = JS_TRUE;
goto out;
}
if (!js_GetProperty(cx, target, id, vp))
return false;
if (VALUE_IS_FUNCTION(cx, *vp))
return true;
target = target->getProto();
if (target == NULL)
break;
tvr.u.object = target;
tvr.setObject(target);
}
xml = (JSXML *) obj->getPrivate();
if (HasSimpleContent(xml)) {
/* Search in String.prototype to implement 11.2.2.1 Step 3(f). */
ok = js_GetClassPrototype(cx, NULL, JSProto_String, &tvr.u.object);
if (!ok)
goto out;
JS_ASSERT(tvr.u.object);
ok = tvr.u.object->getProperty(cx, id, vp);
}
JSXML *xml = (JSXML *) obj->getPrivate();
if (!HasSimpleContent(xml))
return true;
out:
JS_POP_TEMP_ROOT(cx, &tvr);
return ok;
/* Search in String.prototype to implement 11.2.2.1 Step 3(f). */
if (!js_GetClassPrototype(cx, NULL, JSProto_String, tvr.addr()))
return false;
JS_ASSERT(tvr.object());
return tvr.object()->getProperty(cx, id, vp);
}
static JSXML *