diff --git a/mozilla/js/src/Makefile.in b/mozilla/js/src/Makefile.in index 2709dbdbc63..7e6469fc697 100644 --- a/mozilla/js/src/Makefile.in +++ b/mozilla/js/src/Makefile.in @@ -95,6 +95,7 @@ CSRCS = \ jsstr.c \ jsutil.c \ jsxdrapi.c \ + jsxml.c \ prmjtime.c \ $(NULL) @@ -136,11 +137,12 @@ EXPORTS = \ jsscan.h \ jsscope.h \ jsscript.h \ + jsstddef.h \ jsstr.h \ jstypes.h \ jsutil.h \ jsxdrapi.h \ - jsstddef.h \ + jsxml.h \ $(NULL) ifeq ($(OS_ARCH),WINNT) diff --git a/mozilla/js/src/Makefile.ref b/mozilla/js/src/Makefile.ref index e6f00826bb3..12b40fcdf3a 100644 --- a/mozilla/js/src/Makefile.ref +++ b/mozilla/js/src/Makefile.ref @@ -179,6 +179,7 @@ JS_HFILES = \ jsscript.h \ jsstr.h \ jsxdrapi.h \ + jsxml.h \ $(NULL) API_HFILES = \ @@ -238,6 +239,7 @@ JS_CFILES = \ jsstr.c \ jsutil.c \ jsxdrapi.c \ + jsxml.c \ prmjtime.c \ $(NULL) diff --git a/mozilla/js/src/js.c b/mozilla/js/src/js.c index 77c778b9277..a67d57a57ea 100644 --- a/mozilla/js/src/js.c +++ b/mozilla/js/src/js.c @@ -587,6 +587,7 @@ static struct { {"strict", JSOPTION_STRICT}, {"werror", JSOPTION_WERROR}, {"atline", JSOPTION_ATLINE}, + {"xml", JSOPTION_XML}, {0, 0} }; @@ -1267,11 +1268,11 @@ DumpStats(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) atom = js_Atomize(cx, bytes, JS_GetStringLength(str), 0); if (!atom) return JS_FALSE; - if (!js_FindProperty(cx, (jsid)atom, &obj, &obj2, &prop)) + if (!js_FindProperty(cx, ATOM_TO_JSID(atom), &obj, &obj2, &prop)) return JS_FALSE; if (prop) { OBJ_DROP_PROPERTY(cx, obj2, prop); - if (!OBJ_GET_PROPERTY(cx, obj, (jsid)atom, &value)) + if (!OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &value)) return JS_FALSE; } if (!prop || !JSVAL_IS_OBJECT(value)) { @@ -1309,16 +1310,16 @@ DoExport(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) atom = js_ValueToStringAtom(cx, argv[1]); if (!atom) return JS_FALSE; - if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &obj2, &prop)) + if (!OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &obj2, &prop)) return JS_FALSE; if (!prop) { ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL, JSPROP_EXPORTED, NULL); } else { - ok = OBJ_GET_ATTRIBUTES(cx, obj, (jsid)atom, prop, &attrs); + ok = OBJ_GET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, &attrs); if (ok) { attrs |= JSPROP_EXPORTED; - ok = OBJ_SET_ATTRIBUTES(cx, obj, (jsid)atom, prop, &attrs); + ok = OBJ_SET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, &attrs); } OBJ_DROP_PROPERTY(cx, obj2, prop); } diff --git a/mozilla/js/src/js.msg b/mozilla/js/src/js.msg index dc6c534f7cc..389237da770 100644 --- a/mozilla/js/src/js.msg +++ b/mozilla/js/src/js.msg @@ -249,3 +249,24 @@ MSG_DEF(JSMSG_RESERVED_SLOT_RANGE, 166, 0, JSEXN_RANGEERR, "reserved slot ind MSG_DEF(JSMSG_CANT_DECODE_PRINCIPALS, 167, 0, JSEXN_INTERNALERR, "can't decode JSPrincipals") MSG_DEF(JSMSG_CANT_SEAL_OBJECT, 168, 1, JSEXN_ERR, "can't seal {0} objects") MSG_DEF(JSMSG_CANT_UNSEAL_OBJECT, 169, 1, JSEXN_ERR, "can't unseal {0} objects") +MSG_DEF(JSMSG_BAD_XML_MARKUP, 170, 0, JSEXN_SYNTAXERR, "invalid XML markup") +MSG_DEF(JSMSG_BAD_XML_CHARACTER, 171, 0, JSEXN_SYNTAXERR, "illegal XML character") +MSG_DEF(JSMSG_BAD_DEFAULT_XML_NAMESPACE,172,0,JSEXN_SYNTAXERR, "invalid default XML namespace") +MSG_DEF(JSMSG_BAD_XML_NAME_SYNTAX, 173, 0, JSEXN_SYNTAXERR, "invalid XML name") +MSG_DEF(JSMSG_BRACKET_AFTER_ATTR_EXPR,174, 0, JSEXN_SYNTAXERR, "missing ] after attribute expression") +MSG_DEF(JSMSG_NAME_AFTER_DBLDOT, 175, 0, JSEXN_SYNTAXERR, "missing name after .. operator") +MSG_DEF(JSMSG_CURLY_IN_XML_EXPR, 176, 0, JSEXN_SYNTAXERR, "missing } in XML expression") +MSG_DEF(JSMSG_BAD_XML_NAMESPACE, 177, 0, JSEXN_TYPEERR, "invalid XML namespace") +MSG_DEF(JSMSG_BAD_XML_ATTRIBUTE_NAME, 178, 1, JSEXN_TYPEERR, "invalid XML attribute name {0}") +MSG_DEF(JSMSG_BAD_XML_NAME, 179, 1, JSEXN_TYPEERR, "invalid XML name {0}") +MSG_DEF(JSMSG_BAD_XML_CONVERSION, 180, 1, JSEXN_TYPEERR, "can't convert {0} to XML") +MSG_DEF(JSMSG_BAD_XMLLIST_CONVERSION, 181, 1, JSEXN_TYPEERR, "can't convert {0} to XMLList") +MSG_DEF(JSMSG_IS_NOT_XML_OBJECT, 182, 1, JSEXN_TYPEERR, "{0} is not an XML object") +MSG_DEF(JSMSG_NO_ASSIGN_IN_XML_ATTR, 183, 0, JSEXN_SYNTAXERR, "missing = in XML attribute") +MSG_DEF(JSMSG_BAD_XML_ATTR_VALUE, 184, 0, JSEXN_SYNTAXERR, "invalid XML attribute value") +MSG_DEF(JSMSG_XML_TAG_NAME_MISMATCH, 185, 0, JSEXN_SYNTAXERR, "XML tag name mismatch") +MSG_DEF(JSMSG_BAD_XML_TAG_SYNTAX, 186, 0, JSEXN_SYNTAXERR, "invalid XML tag syntax") +MSG_DEF(JSMSG_BAD_XML_LIST_SYNTAX, 187, 0, JSEXN_SYNTAXERR, "invalid XML list syntax") +MSG_DEF(JSMSG_INCOMPATIBLE_METHOD, 188, 3, JSEXN_TYPEERR, "{0} {1} called on incompatible {2}") +MSG_DEF(JSMSG_CANT_SET_XML_ATTRS, 189, 0, JSEXN_INTERNALERR, "can't set XML property attributes") +MSG_DEF(JSMSG_END_OF_XML_SOURCE, 190, 0, JSEXN_SYNTAXERR, "unexpected end of XML source") diff --git a/mozilla/js/src/jsapi.c b/mozilla/js/src/jsapi.c index a616ac36d09..72667269293 100644 --- a/mozilla/js/src/jsapi.c +++ b/mozilla/js/src/jsapi.c @@ -81,6 +81,10 @@ #include "jsfile.h" #endif +#if JS_HAS_XML_SUPPORT +#include "jsxml.h" +#endif + #ifdef HAVE_VA_LIST_AS_ARRAY #define JS_ADDRESSOF_VA_LIST(ap) (ap) #else @@ -598,27 +602,36 @@ JS_TypeOfValue(JSContext *cx, jsval v) CHECK_REQUEST(cx); if (JSVAL_IS_OBJECT(v)) { - /* XXX JSVAL_IS_OBJECT(v) is true for null too! Can we change ECMA? */ + type = JSTYPE_OBJECT; /* XXXbe JSTYPE_NULL for JS2 */ obj = JSVAL_TO_OBJECT(v); - if (obj && - (ops = obj->map->ops, - ops == &js_ObjectOps - ? (clasp = OBJ_GET_CLASS(cx, obj), - clasp->call || clasp == &js_FunctionClass) - : ops->call != NULL)) { - type = JSTYPE_FUNCTION; - } else { -#ifdef NARCISSUS - if (obj) { - /* XXX suppress errors/exceptions */ - OBJ_GET_PROPERTY(cx, obj, - (jsid)cx->runtime->atomState.callAtom, - &v); - if (JSVAL_IS_FUNCTION(cx, v)) - return JSTYPE_FUNCTION; - } + if (obj) { + ops = obj->map->ops; +#if JS_HAS_XML_SUPPORT + if (ops == &js_XMLObjectOps.base) { + type = JSTYPE_XML; + } else #endif - type = JSTYPE_OBJECT; + { + clasp = OBJ_GET_CLASS(cx, obj); + if ((ops == &js_ObjectOps) + ? (clasp->call + ? clasp == &js_RegExpClass + : clasp == &js_FunctionClass) + : ops->call != NULL) { + type = JSTYPE_FUNCTION; + } else { +#ifdef NARCISSUS + if (!OBJ_GET_PROPERTY(cx, obj, + ATOM_TO_JSID(cx->runtime->atomState + .callAtom), + &v)) { + JS_ClearPendingException(cx); + } else if (JSVAL_IS_FUNCTION(cx, v)) { + type = JSTYPE_FUNCTION; + } +#endif + } + } } } else if (JSVAL_IS_NUMBER(v)) { type = JSTYPE_NUMBER; @@ -1093,13 +1106,13 @@ InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj) if (resolving) { rt = cx->runtime; key.obj = obj; - key.id = (jsid) rt->atomState.FunctionAtom; + key.id = ATOM_TO_JSID(rt->atomState.FunctionAtom); entry = (JSResolvingEntry *) JS_DHashTableOperate(table, &key, JS_DHASH_ADD); if (entry && entry->key.obj && (entry->flags & JSRESFLAG_LOOKUP)) { /* Already resolving Function, record Object too. */ JS_ASSERT(entry->key.obj == obj); - key.id = (jsid) rt->atomState.ObjectAtom; + key.id = ATOM_TO_JSID(rt->atomState.ObjectAtom); entry = (JSResolvingEntry *) JS_DHashTableOperate(table, &key, JS_DHASH_ADD); } @@ -1145,8 +1158,8 @@ JS_InitStandardClasses(JSContext *cx, JSObject *obj) { /* Define a top-level property 'undefined' with the undefined value. */ JSAtom *atom = cx->runtime->atomState.typeAtoms[JSTYPE_VOID]; - if (!OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, JSVAL_VOID, NULL, NULL, - JSPROP_PERMANENT, NULL)) { + if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID, + NULL, NULL, JSPROP_PERMANENT, NULL)) { return JS_FALSE; } } @@ -1174,6 +1187,9 @@ JS_InitStandardClasses(JSContext *cx, JSObject *obj) #if JS_HAS_ERROR_EXCEPTIONS js_InitExceptionClasses(cx, obj) && #endif +#if JS_HAS_XML_SUPPORT + js_InitXMLClasses(cx, obj) && +#endif #if JS_HAS_FILE_OBJECT js_InitFileClass(cx, obj, JS_TRUE) && #endif @@ -1282,6 +1298,14 @@ static JSStdName standard_class_names[] = { {js_InitExceptionClasses, LAZILY_PINNED_ATOM(URIError)}, #endif +#if JS_HAS_XML_SUPPORT + {js_InitXMLClass, LAZILY_PINNED_ATOM(isXMLName)}, + {js_InitNamespaceClass, LAZILY_PINNED_ATOM(Namespace)}, + {js_InitQNameClass, LAZILY_PINNED_ATOM(QName)}, + {js_InitXMLClass, LAZILY_PINNED_ATOM(XML)}, + {js_InitXMLClass, LAZILY_PINNED_ATOM(XMLList)}, +#endif + {NULL, 0, NULL} }; @@ -1341,8 +1365,8 @@ JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id, atom = rt->atomState.typeAtoms[JSTYPE_VOID]; if (idstr == ATOM_TO_STRING(atom)) { *resolved = JS_TRUE; - return OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, JSVAL_VOID, NULL, NULL, - JSPROP_PERMANENT, NULL); + return OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID, + NULL, NULL, JSPROP_PERMANENT, NULL); } #endif @@ -1400,7 +1424,7 @@ HasOwnProperty(JSContext *cx, JSObject *obj, JSAtom *atom, JSBool *ownp) JSObject *pobj; JSProperty *prop; - if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, &prop)) + if (!OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) return JS_FALSE; if (prop) OBJ_DROP_PROPERTY(cx, pobj, prop); @@ -1425,8 +1449,8 @@ JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj) if (!HasOwnProperty(cx, obj, atom, &found)) return JS_FALSE; if (!found && - !OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, JSVAL_VOID, NULL, NULL, - JSPROP_PERMANENT, NULL)) { + !OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID, + NULL, NULL, JSPROP_PERMANENT, NULL)) { return JS_FALSE; } #endif @@ -1855,7 +1879,7 @@ JS_ValueToId(JSContext *cx, jsval v, jsid *idp) atom = js_ValueToStringAtom(cx, v); if (!atom) return JS_FALSE; - *idp = (jsid)atom; + *idp = ATOM_TO_JSID(atom); } return JS_TRUE; } @@ -1909,9 +1933,9 @@ JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, { JSAtom *atom; JSObject *proto, *ctor; + jsval cval, rval; JSBool named; JSFunction *fun; - jsval junk; CHECK_REQUEST(cx); atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0); @@ -1925,7 +1949,8 @@ JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, if (!constructor) { /* Lacking a constructor, name the prototype (e.g., Math). */ - named = OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, OBJECT_TO_JSVAL(proto), + named = OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), + OBJECT_TO_JSVAL(proto), NULL, NULL, 0, NULL); if (!named) goto bad; @@ -1944,8 +1969,22 @@ JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, */ fun->clasp = clasp; - /* Connect constructor and prototype by named properties. */ + /* + * Optionally construct the prototype object, before the class has + * been fully initialized. Allow the ctor to replace proto with a + * different object, as is done for operator new -- and as at least + * XML support requires. + */ ctor = fun->object; + if (clasp->flags & JSCLASS_CONSTRUCT_PROTOTYPE) { + cval = OBJECT_TO_JSVAL(ctor); + if (!js_InternalConstruct(cx, proto, cval, 0, NULL, &rval)) + goto bad; + if (!JSVAL_IS_PRIMITIVE(rval) && JSVAL_TO_OBJECT(rval) != proto) + proto = JSVAL_TO_OBJECT(rval); + } + + /* Connect constructor and prototype by named properties. */ if (!js_SetClassPrototype(cx, ctor, proto, JSPROP_READONLY | JSPROP_PERMANENT)) { goto bad; @@ -1972,7 +2011,7 @@ JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, bad: if (named) - (void) OBJ_DELETE_PROPERTY(cx, obj, (jsid)atom, &junk); + (void) OBJ_DELETE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &rval); cx->newborn[GCX_OBJECT] = NULL; return NULL; } @@ -2091,7 +2130,7 @@ JS_GetConstructor(JSContext *cx, JSObject *proto) CHECK_REQUEST(cx); if (!OBJ_GET_PROPERTY(cx, proto, - (jsid)cx->runtime->atomState.constructorAtom, + ATOM_TO_JSID(cx->runtime->atomState.constructorAtom), &cval)) { return NULL; } @@ -2106,8 +2145,8 @@ JS_GetConstructor(JSContext *cx, JSObject *proto) JS_PUBLIC_API(JSBool) JS_GetObjectId(JSContext *cx, JSObject *obj, jsid *idp) { - JS_ASSERT(((jsid)obj & JSVAL_TAGMASK) == 0); - *idp = (jsid) obj | JSVAL_INT; + JS_ASSERT(((jsid)obj & JSID_TAGMASK) == 0); + *idp = OBJECT_TO_JSID(obj); return JS_TRUE; } @@ -2211,14 +2250,14 @@ DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value, JSAtom *atom; if (attrs & JSPROP_INDEX) { - id = INT_TO_JSVAL((jsint)name); + id = INT_TO_JSID((jsint)name); atom = NULL; attrs &= ~JSPROP_INDEX; } else { atom = js_Atomize(cx, name, strlen(name), 0); if (!atom) return JS_FALSE; - id = (jsid)atom; + id = ATOM_TO_JSID(atom); } if (flags != 0 && OBJ_IS_NATIVE(obj)) { return js_DefineNativeProperty(cx, obj, id, value, getter, setter, @@ -2242,12 +2281,12 @@ DefineUCProperty(JSContext *cx, JSObject *obj, if (!atom) return JS_FALSE; if (flags != 0 && OBJ_IS_NATIVE(obj)) { - return js_DefineNativeProperty(cx, obj, (jsid)atom, value, + return js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value, getter, setter, attrs, flags, tinyid, NULL); } - return OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, value, getter, setter, - attrs, NULL); + return OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), value, + getter, setter, attrs, NULL); } JS_PUBLIC_API(JSObject *) @@ -2336,7 +2375,7 @@ LookupProperty(JSContext *cx, JSObject *obj, const char *name, JSObject **objp, atom = js_Atomize(cx, name, strlen(name), 0); if (!atom) return JS_FALSE; - return OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, objp, propp); + return OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), objp, propp); } static JSBool @@ -2349,7 +2388,7 @@ LookupUCProperty(JSContext *cx, JSObject *obj, atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); if (!atom) return JS_FALSE; - return OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, objp, propp); + return OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), objp, propp); } JS_PUBLIC_API(JSBool) @@ -2380,7 +2419,7 @@ JS_AliasProperty(JSContext *cx, JSObject *obj, const char *name, ok = JS_FALSE; } else { sprop = (JSScopeProperty *)prop; - ok = (js_AddNativeProperty(cx, obj, (jsid)atom, + ok = (js_AddNativeProperty(cx, obj, ATOM_TO_JSID(atom), sprop->getter, sprop->setter, sprop->slot, sprop->attrs, sprop->flags | SPROP_IS_ALIAS, sprop->shortid) @@ -2424,7 +2463,7 @@ GetPropertyAttributes(JSContext *cx, JSObject *obj, JSAtom *atom, if (!atom) return JS_FALSE; - if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &obj2, &prop)) + if (!OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &obj2, &prop)) return JS_FALSE; if (!prop || obj != obj2) { *foundp = JS_FALSE; @@ -2434,7 +2473,7 @@ GetPropertyAttributes(JSContext *cx, JSObject *obj, JSAtom *atom, } *foundp = JS_TRUE; - ok = OBJ_GET_ATTRIBUTES(cx, obj, (jsid)atom, prop, attrsp); + ok = OBJ_GET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, attrsp); OBJ_DROP_PROPERTY(cx, obj, prop); return ok; } @@ -2449,7 +2488,7 @@ SetPropertyAttributes(JSContext *cx, JSObject *obj, JSAtom *atom, if (!atom) return JS_FALSE; - if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &obj2, &prop)) + if (!OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &obj2, &prop)) return JS_FALSE; if (!prop || obj != obj2) { *foundp = JS_FALSE; @@ -2459,7 +2498,7 @@ SetPropertyAttributes(JSContext *cx, JSObject *obj, JSAtom *atom, } *foundp = JS_TRUE; - ok = OBJ_SET_ATTRIBUTES(cx, obj, (jsid)atom, prop, &attrs); + ok = OBJ_SET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, &attrs); OBJ_DROP_PROPERTY(cx, obj, prop); return ok; } @@ -2530,12 +2569,9 @@ JS_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, const char *name, if (!atom) return JS_FALSE; ok = OBJ_IS_NATIVE(obj) - ? js_LookupPropertyWithFlags(cx, obj, (jsid)atom, flags, &obj2, &prop -#if defined JS_THREADSAFE && defined DEBUG - , __FILE__, __LINE__ -#endif - ) - : OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &obj2, &prop); + ? js_LookupPropertyWithFlags(cx, obj, ATOM_TO_JSID(atom), flags, + &obj2, &prop) + : OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &obj2, &prop); if (ok) *vp = LookupResult(cx, obj, obj2, prop); return ok; @@ -2550,7 +2586,7 @@ JS_GetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp) atom = js_Atomize(cx, name, strlen(name), 0); if (!atom) return JS_FALSE; - return OBJ_GET_PROPERTY(cx, obj, (jsid)atom, vp); + return OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); } JS_PUBLIC_API(JSBool) @@ -2562,7 +2598,7 @@ JS_SetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp) atom = js_Atomize(cx, name, strlen(name), 0); if (!atom) return JS_FALSE; - return OBJ_SET_PROPERTY(cx, obj, (jsid)atom, vp); + return OBJ_SET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); } JS_PUBLIC_API(JSBool) @@ -2584,7 +2620,7 @@ JS_DeleteProperty2(JSContext *cx, JSObject *obj, const char *name, atom = js_Atomize(cx, name, strlen(name), 0); if (!atom) return JS_FALSE; - return OBJ_DELETE_PROPERTY(cx, obj, (jsid)atom, rval); + return OBJ_DELETE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), rval); } JS_PUBLIC_API(JSBool) @@ -2678,7 +2714,7 @@ JS_GetUCProperty(JSContext *cx, JSObject *obj, atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); if (!atom) return JS_FALSE; - return OBJ_GET_PROPERTY(cx, obj, (jsid)atom, vp); + return OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); } JS_PUBLIC_API(JSBool) @@ -2692,7 +2728,7 @@ JS_SetUCProperty(JSContext *cx, JSObject *obj, atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); if (!atom) return JS_FALSE; - return OBJ_SET_PROPERTY(cx, obj, (jsid)atom, vp); + return OBJ_SET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); } JS_PUBLIC_API(JSBool) @@ -2706,7 +2742,7 @@ JS_DeleteUCProperty2(JSContext *cx, JSObject *obj, atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); if (!atom) return JS_FALSE; - return OBJ_DELETE_PROPERTY(cx, obj, (jsid)atom, rval); + return OBJ_DELETE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), rval); } JS_PUBLIC_API(JSObject *) @@ -2749,7 +2785,7 @@ JS_DefineElement(JSContext *cx, JSObject *obj, jsint index, jsval value, JSPropertyOp getter, JSPropertyOp setter, uintN attrs) { CHECK_REQUEST(cx); - return OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSVAL(index), value, + return OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSID(index), value, getter, setter, attrs, NULL); } @@ -2777,7 +2813,7 @@ JS_AliasElement(JSContext *cx, JSObject *obj, const char *name, jsint alias) return JS_FALSE; } sprop = (JSScopeProperty *)prop; - ok = (js_AddNativeProperty(cx, obj, INT_TO_JSVAL(alias), + ok = (js_AddNativeProperty(cx, obj, INT_TO_JSID(alias), sprop->getter, sprop->setter, sprop->slot, sprop->attrs, sprop->flags | SPROP_IS_ALIAS, sprop->shortid) @@ -2794,7 +2830,7 @@ JS_HasElement(JSContext *cx, JSObject *obj, jsint index, JSBool *foundp) JSProperty *prop; CHECK_REQUEST(cx); - ok = OBJ_LOOKUP_PROPERTY(cx, obj, INT_TO_JSVAL(index), &obj2, &prop); + ok = OBJ_LOOKUP_PROPERTY(cx, obj, INT_TO_JSID(index), &obj2, &prop); if (ok) { *foundp = (prop != NULL); if (prop) @@ -2811,7 +2847,7 @@ JS_LookupElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp) JSProperty *prop; CHECK_REQUEST(cx); - ok = OBJ_LOOKUP_PROPERTY(cx, obj, INT_TO_JSVAL(index), &obj2, &prop); + ok = OBJ_LOOKUP_PROPERTY(cx, obj, INT_TO_JSID(index), &obj2, &prop); if (ok) *vp = LookupResult(cx, obj, obj2, prop); return ok; @@ -2821,14 +2857,14 @@ JS_PUBLIC_API(JSBool) JS_GetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp) { CHECK_REQUEST(cx); - return OBJ_GET_PROPERTY(cx, obj, INT_TO_JSVAL(index), vp); + return OBJ_GET_PROPERTY(cx, obj, INT_TO_JSID(index), vp); } JS_PUBLIC_API(JSBool) JS_SetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp) { CHECK_REQUEST(cx); - return OBJ_SET_PROPERTY(cx, obj, INT_TO_JSVAL(index), vp); + return OBJ_SET_PROPERTY(cx, obj, INT_TO_JSID(index), vp); } JS_PUBLIC_API(JSBool) @@ -2844,7 +2880,7 @@ JS_PUBLIC_API(JSBool) JS_DeleteElement2(JSContext *cx, JSObject *obj, jsint index, jsval *rval) { CHECK_REQUEST(cx); - return OBJ_DELETE_PROPERTY(cx, obj, INT_TO_JSVAL(index), rval); + return OBJ_DELETE_PROPERTY(cx, obj, INT_TO_JSID(index), rval); } JS_PUBLIC_API(void) @@ -3449,7 +3485,7 @@ JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj, argAtom = js_Atomize(cx, argnames[i], strlen(argnames[i]), 0); if (!argAtom) break; - if (!js_AddNativeProperty(cx, fun->object, (jsid)argAtom, + if (!js_AddNativeProperty(cx, fun->object, ATOM_TO_JSID(argAtom), js_GetArgument, js_SetArgument, SPROP_INVALID_SLOT, JSPROP_ENUMERATE | JSPROP_PERMANENT | @@ -3468,7 +3504,7 @@ JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj, goto out; } if (obj && funAtom) { - if (!OBJ_DEFINE_PROPERTY(cx, obj, (jsid)funAtom, + if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(funAtom), OBJECT_TO_JSVAL(fun->object), NULL, NULL, 0, NULL)) { return NULL; @@ -3688,12 +3724,21 @@ JS_PUBLIC_API(JSBool) JS_CallFunctionName(JSContext *cx, JSObject *obj, const char *name, uintN argc, jsval *argv, jsval *rval) { + JSBool ok; jsval fval; CHECK_REQUEST(cx); - if (!JS_GetProperty(cx, obj, name, &fval)) - return JS_FALSE; - if (!js_InternalCall(cx, obj, fval, argc, argv, rval)) { +#if JS_HAS_XML_SUPPORT + if (OBJECT_IS_XML(cx, obj)) { + ok = js_InternalCallMethod(cx, obj, name, argc, argv, rval); + } else +#endif + { + if (!JS_GetProperty(cx, obj, name, &fval)) + return JS_FALSE; + ok = js_InternalCall(cx, obj, fval, argc, argv, rval); + } + if (!ok) { #if JS_HAS_EXCEPTIONS if (!cx->fp) js_ReportUncaughtException(cx); diff --git a/mozilla/js/src/jsapi.h b/mozilla/js/src/jsapi.h index 8f491ffec36..2ed8893f479 100644 --- a/mozilla/js/src/jsapi.h +++ b/mozilla/js/src/jsapi.h @@ -446,6 +446,11 @@ JS_StringToVersion(const char *string); option supported for the XUL preprocessor and kindred beasts. */ +#define JSOPTION_XML JS_BIT(6) /* EMCAScript for XML support: + parse as a token, + not backward compatible with + the comment-hiding hack used + in HTML script tags. */ extern JS_PUBLIC_API(uint32) JS_GetOptions(JSContext *cx); @@ -707,7 +712,7 @@ extern JS_PUBLIC_API(JSBool) JS_IsAboutToBeFinalized(JSContext *cx, void *thing); /* - * Add an external string finalizer, one created by JS_NewExternalString (see + * Add a finalizer for external strings created by JS_NewExternalString (see * below) using a type-code returned from this function, and that understands * how to free or release the memory pointed at by JS_GetStringChars(str). * @@ -801,6 +806,9 @@ struct JSClass { object in prototype chain passed in via *objp in/out parameter */ +#define JSCLASS_CONSTRUCT_PROTOTYPE (1<<6) /* call constructor on class + prototype */ +#define JSCLASS_W3C_XML_INFO_ITEM (1<<7) /* W3C XML Information Item */ /* * To reserve slots fetched and stored via JS_Get/SetReservedSlot, bitwise-or @@ -850,6 +858,14 @@ struct JSObjectOps { JSSetRequiredSlotOp setRequiredSlot; }; +struct JSXMLObjectOps { + JSObjectOps base; + JSCallMethodOp callMethod; + JSEnumerateValuesOp enumerateValues; + JSEqualityOp equality; + JSConcatenateOp concatenate; +}; + /* * Classes that expose JSObjectOps via a non-null getObjectOps class hook may * derive a property structure from this struct, return a pointer to it from @@ -878,6 +894,37 @@ JS_ValueToId(JSContext *cx, jsval v, jsid *idp); extern JS_PUBLIC_API(JSBool) JS_IdToValue(JSContext *cx, jsid id, jsval *vp); +/* + * The magic XML namespace id is int-tagged, but not a valid integer jsval. + * Global object classes in embeddings that enable JS_HAS_XML_SUPPORT (E4X) + * should handle this id specially before converting id via JSVAL_TO_INT. + * XXXbe fix this to avoid preemption/collision + */ +#define JS_DEFAULT_XML_NAMESPACE_ID INT_TO_JSVAL(JSVAL_INT_MIN) + +#define JSID_ATOM 0x0 +#define JSID_INT 0x1 +#define JSID_OBJECT 0x2 +#define JSID_TAGMASK 0x3 +#define JSID_TAG(id) ((id) & JSID_TAGMASK) +#define JSID_SETTAG(id,t) ((id) | (t)) +#define JSID_CLRTAG(id) ((id) & ~(jsid)JSID_TAGMASK) + +#define JSID_IS_ATOM(id) (JSID_TAG(id) == JSID_ATOM) +#define JSID_TO_ATOM(id) ((JSAtom *)(id)) +#define ATOM_TO_JSID(atom) ((jsid)(atom)) + +#define JSID_IS_INT(id) ((id) & JSID_INT) +#define JSID_TO_INT(id) ((jsint)(id) >> 1) +#define INT_TO_JSID(i) (((jsint)(i) << 1) | JSID_INT) + +#define JSID_IS_OBJECT(id) (JSID_TAG(id) == JSID_OBJECT) +#define JSID_TO_OBJECT(id) ((JSObject *) JSID_CLRTAG(id)) +#define OBJECT_TO_JSID(obj) ((jsid)(obj) | JSID_OBJECT) + +/* + * JSNewResolveOp flag bits. + */ #define JSRESOLVE_QUALIFIED 0x01 /* resolve a qualified property id */ #define JSRESOLVE_ASSIGNING 0x02 /* resolve on the left of assignment */ #define JSRESOLVE_DETECTING 0x04 /* 'if (o.p)...' or '(o.p) ?...:...' */ diff --git a/mozilla/js/src/jsarray.c b/mozilla/js/src/jsarray.c index a05a6ee3c0c..d0b708d8e6b 100644 --- a/mozilla/js/src/jsarray.c +++ b/mozilla/js/src/jsarray.c @@ -63,7 +63,7 @@ #define MAXSTR "4294967295" /* - * Determine if the id represents an array index. + * Determine if the id represents an array index or an XML property index. * * An id is an array index according to ECMA by (15.4): * @@ -78,8 +78,8 @@ * that calling a standard conversion routine might allow strings such as * "08" or "4.0" as array indices, which they are not. */ -static JSBool -IdIsIndex(jsid id, jsuint *indexp) +JSBool +js_IdIsIndex(jsval id, jsuint *indexp) { JSString *str; jschar *cp; @@ -93,7 +93,10 @@ IdIsIndex(jsid id, jsuint *indexp) return JS_TRUE; } - /* It must be a string. */ + /* NB: id should be a string, but jsxml.c may call us with an object id. */ + if (!JSVAL_IS_STRING(id)) + return JS_FALSE; + str = JSVAL_TO_STRING(id); cp = JSSTRING_CHARS(str); if (JS7_ISDEC(*cp) && JSSTRING_LENGTH(str) < sizeof(MAXSTR)) { @@ -108,9 +111,8 @@ IdIsIndex(jsid id, jsuint *indexp) cp++; } } - /* Make sure all characters were consumed and that it couldn't - * have overflowed. - */ + + /* Ensure that all characters were consumed and we didn't overflow. */ if (*cp == 0 && (oldIndex < (MAXINDEX / 10) || (oldIndex == (MAXINDEX / 10) && c < (MAXINDEX % 10)))) @@ -138,7 +140,7 @@ ValueIsLength(JSContext *cx, jsval v, jsuint *lengthp) *lengthp = (jsuint) i; return JS_TRUE; } - + if (!js_ValueToNumber(cx, v, &d)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH); @@ -164,7 +166,7 @@ js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) jsint i; jsval v; - id = (jsid) cx->runtime->atomState.lengthAtom; + id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); if (!OBJ_GET_PROPERTY(cx, obj, id, &v)) return JS_FALSE; @@ -197,7 +199,7 @@ IndexToId(JSContext *cx, jsuint length, jsid *idp) JSAtom *atom; if (length <= JSVAL_INT_MAX) { - *idp = (jsid) INT_TO_JSVAL(length); + *idp = INT_TO_JSID(length); } else { str = js_NumberToString(cx, (jsdouble)length); if (!str) @@ -205,7 +207,7 @@ IndexToId(JSContext *cx, jsuint length, jsid *idp) atom = js_AtomizeString(cx, str, 0); if (!atom) return JS_FALSE; - *idp = (jsid)atom; + *idp = ATOM_TO_JSID(atom); } return JS_TRUE; @@ -219,7 +221,7 @@ js_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length) if (!IndexToValue(cx, length, &v)) return JS_FALSE; - id = (jsid) cx->runtime->atomState.lengthAtom; + id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); return OBJ_SET_PROPERTY(cx, obj, id, &v); } @@ -232,7 +234,7 @@ js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) jsval v; older = JS_SetErrorReporter(cx, NULL); - id = (jsid) cx->runtime->atomState.lengthAtom; + id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); ok = OBJ_GET_PROPERTY(cx, obj, id, &v); JS_SetErrorReporter(cx, older); if (!ok) @@ -278,7 +280,7 @@ array_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { jsuint index, length; - if (!(IdIsIndex(id, &index))) + if (!js_IdIsIndex(id, &index)) return JS_TRUE; if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; @@ -404,7 +406,8 @@ array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize, if (!ok) goto done; - if (!literalize && (JSVAL_IS_VOID(v) || JSVAL_IS_NULL(v))) { + if ((!literalize || cx->version == JSVERSION_1_2) && + (JSVAL_IS_VOID(v) || JSVAL_IS_NULL(v))) { str = cx->runtime->emptyString; } else { if (localeString) { @@ -549,7 +552,7 @@ InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, jsval *vector) if (!IndexToValue(cx, length, &v)) return JS_FALSE; - id = (jsid) cx->runtime->atomState.lengthAtom; + id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); if (!OBJ_DEFINE_PROPERTY(cx, obj, id, v, array_length_getter, array_length_setter, JSPROP_PERMANENT, @@ -666,7 +669,7 @@ HeapSortHelper(JSBool building, HSortArgs *hsa, size_t lo, size_t hi) a = (char *)vec + (hi - 1) * elsize; b = (char *)vec2 + j * elsize; - /* + /* * During sorting phase b points to a member of heap that cannot be * bigger then biggest of vec[0] and vec[1], and cmp(a, b, arg) <= 0 * always holds. @@ -704,7 +707,7 @@ HeapSortHelper(JSBool building, HSortArgs *hsa, size_t lo, size_t hi) } JSBool -js_HeapSort(void *vec, size_t nel, size_t elsize, JSComparator cmp, void *arg) +js_HeapSort(void *vec, size_t nel, size_t elsize, JSComparator cmp, void *arg) { void *pivot; HSortArgs hsa; @@ -877,7 +880,7 @@ array_sort(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) goto out; /* We know JSVAL_IS_STRING yields 0 or 1, so avoid a branch via &=. */ - all_strings &= JSVAL_IS_STRING(vec[i]); + all_strings &= JSVAL_IS_STRING(vec[i]); } ca.context = cx; @@ -1242,7 +1245,8 @@ array_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) aobj = JSVAL_TO_OBJECT(v); if (aobj && OBJ_GET_CLASS(cx, aobj) == &js_ArrayClass) { if (!OBJ_GET_PROPERTY(cx, aobj, - (jsid)cx->runtime->atomState.lengthAtom, + ATOM_TO_JSID(cx->runtime->atomState + .lengthAtom), &v)) { return JS_FALSE; } diff --git a/mozilla/js/src/jsarray.h b/mozilla/js/src/jsarray.h index cbb2aedf1aa..207d7a03bd4 100644 --- a/mozilla/js/src/jsarray.h +++ b/mozilla/js/src/jsarray.h @@ -47,6 +47,9 @@ JS_BEGIN_EXTERN_C +extern JSBool +js_IdIsIndex(jsval id, jsuint *indexp); + extern JSClass js_ArrayClass; extern JSObject * diff --git a/mozilla/js/src/jsatom.c b/mozilla/js/src/jsatom.c index ede2350b6ec..99a030279f1 100644 --- a/mozilla/js/src/jsatom.c +++ b/mozilla/js/src/jsatom.c @@ -59,16 +59,7 @@ JS_FRIEND_API(const char *) js_AtomToPrintableString(JSContext *cx, JSAtom *atom) { - JSString *str; - const char *bytes; - - str = js_QuoteString(cx, ATOM_TO_STRING(atom), 0); - if (!str) - return NULL; - bytes = js_GetStringBytes(str); - if (!bytes) - JS_ReportOutOfMemory(cx); - return bytes; + return js_ValueToPrintableString(cx, ATOM_KEY(atom)); } extern const char js_Error_str[]; /* trivial, from jsexn.h */ @@ -84,6 +75,8 @@ const char *js_type_str[] = { "string", "number", "boolean", + "null", + "xml", }; const char *js_boolean_str[] = { @@ -111,6 +104,7 @@ const char js_caller_str[] = "caller"; const char js_class_prototype_str[] = "prototype"; const char js_constructor_str[] = "constructor"; const char js_count_str[] = "__count__"; +const char js_etago_str[] = ""; +const char js_qualifier_str[] = "::"; const char js_setter_str[] = "setter"; const char js_set_str[] = "set"; +const char js_space_str[] = " "; +const char js_stago_str[] = "<"; +const char js_star_str[] = "*"; +const char js_starQualifier_str[] = "*::"; +const char js_tagc_str[] = ">"; const char js_toSource_str[] = "toSource"; const char js_toString_str[] = "toString"; const char js_toLocaleString_str[] = "toLocaleString"; const char js_valueOf_str[] = "valueOf"; +const char js_xml_str[] = "xml"; #ifdef NARCISSUS const char js_call_str[] = "__call__"; @@ -307,6 +310,7 @@ js_InitPinnedAtoms(JSContext *cx, JSAtomState *state) FROB(classPrototypeAtom, js_class_prototype_str); FROB(constructorAtom, js_constructor_str); FROB(countAtom, js_count_str); + FROB(etagoAtom, js_etago_str); FROB(evalAtom, js_eval_str); FROB(getAtom, js_get_str); FROB(getterAtom, js_getter_str); @@ -314,15 +318,24 @@ js_InitPinnedAtoms(JSContext *cx, JSAtomState *state) FROB(inputAtom, js_input_str); FROB(lengthAtom, js_length_str); FROB(nameAtom, js_name_str); + FROB(namespaceAtom, js_namespace_str); FROB(noSuchMethodAtom, js_noSuchMethod_str); FROB(parentAtom, js_parent_str); FROB(protoAtom, js_proto_str); + FROB(ptagcAtom, js_ptagc_str); + FROB(qualifierAtom, js_qualifier_str); FROB(setAtom, js_set_str); FROB(setterAtom, js_setter_str); + FROB(spaceAtom, js_space_str); + FROB(stagoAtom, js_stago_str); + FROB(starAtom, js_star_str); + FROB(starQualifierAtom, js_starQualifier_str); + FROB(tagcAtom, js_tagc_str); FROB(toSourceAtom, js_toSource_str); FROB(toStringAtom, js_toString_str); FROB(toLocaleStringAtom, js_toLocaleString_str); FROB(valueOfAtom, js_valueOf_str); + FROB(xmlAtom, js_xml_str); #ifdef NARCISSUS FROB(callAtom, js_call_str); diff --git a/mozilla/js/src/jsatom.h b/mozilla/js/src/jsatom.h index 6f486c3371f..eb45e8ae3c8 100644 --- a/mozilla/js/src/jsatom.h +++ b/mozilla/js/src/jsatom.h @@ -180,6 +180,7 @@ struct JSAtomState { JSAtom *classPrototypeAtom; JSAtom *constructorAtom; JSAtom *countAtom; + JSAtom *etagoAtom; JSAtom *evalAtom; JSAtom *getAtom; JSAtom *getterAtom; @@ -187,15 +188,24 @@ struct JSAtomState { JSAtom *inputAtom; JSAtom *lengthAtom; JSAtom *nameAtom; + JSAtom *namespaceAtom; JSAtom *noSuchMethodAtom; JSAtom *parentAtom; JSAtom *protoAtom; + JSAtom *ptagcAtom; + JSAtom *qualifierAtom; JSAtom *setAtom; JSAtom *setterAtom; + JSAtom *spaceAtom; + JSAtom *stagoAtom; + JSAtom *starAtom; + JSAtom *starQualifierAtom; + JSAtom *tagcAtom; JSAtom *toLocaleStringAtom; JSAtom *toSourceAtom; JSAtom *toStringAtom; JSAtom *valueOfAtom; + JSAtom *xmlAtom; /* Less frequently used atoms, pinned lazily by JS_ResolveStandardClass. */ struct { @@ -203,11 +213,16 @@ struct JSAtomState { JSAtom *InfinityAtom; JSAtom *InternalErrorAtom; JSAtom *NaNAtom; + JSAtom *NamespaceAtom; + JSAtom *QNameAtom; JSAtom *RangeErrorAtom; JSAtom *ReferenceErrorAtom; JSAtom *SyntaxErrorAtom; JSAtom *TypeErrorAtom; JSAtom *URIErrorAtom; + JSAtom *XMLAtom; + JSAtom *XMLListAtom; + JSAtom *anynameAtom; JSAtom *decodeURIAtom; JSAtom *decodeURIComponentAtom; JSAtom *defineGetterAtom; @@ -219,6 +234,7 @@ struct JSAtomState { JSAtom *isFiniteAtom; JSAtom *isNaNAtom; JSAtom *isPrototypeOfAtom; + JSAtom *isXMLNameAtom; JSAtom *lookupGetterAtom; JSAtom *lookupSetterAtom; JSAtom *parseFloatAtom; @@ -267,6 +283,7 @@ extern const char js_caller_str[]; extern const char js_class_prototype_str[]; extern const char js_constructor_str[]; extern const char js_count_str[]; +extern const char js_etago_str[]; extern const char js_eval_str[]; extern const char js_getter_str[]; extern const char js_get_str[]; @@ -274,15 +291,24 @@ extern const char js_index_str[]; extern const char js_input_str[]; extern const char js_length_str[]; extern const char js_name_str[]; +extern const char js_namespace_str[]; extern const char js_noSuchMethod_str[]; extern const char js_parent_str[]; extern const char js_proto_str[]; +extern const char js_ptagc_str[]; +extern const char js_qualifier_str[]; extern const char js_setter_str[]; extern const char js_set_str[]; +extern const char js_space_str[]; +extern const char js_stago_str[]; +extern const char js_star_str[]; +extern const char js_starQualifier_str[]; +extern const char js_tagc_str[]; extern const char js_toSource_str[]; extern const char js_toString_str[]; extern const char js_toLocaleString_str[]; extern const char js_valueOf_str[]; +extern const char js_xml_str[]; #ifdef NARCISSUS extern const char js_call_str[]; diff --git a/mozilla/js/src/jsbool.c b/mozilla/js/src/jsbool.c index 75d4ee05af0..8de23955763 100644 --- a/mozilla/js/src/jsbool.c +++ b/mozilla/js/src/jsbool.c @@ -54,7 +54,7 @@ #include "jsobj.h" #include "jsstr.h" -static JSClass boolean_class = { +JSClass js_BooleanClass = { "Boolean", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, @@ -73,13 +73,13 @@ bool_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, char buf[32]; JSString *str; - if (!JS_InstanceOf(cx, obj, &boolean_class, argv)) + if (!JS_InstanceOf(cx, obj, &js_BooleanClass, argv)) return JS_FALSE; v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); if (!JSVAL_IS_BOOLEAN(v)) return js_obj_toSource(cx, obj, argc, argv, rval); JS_snprintf(buf, sizeof buf, "(new %s(%s))", - boolean_class.name, + js_BooleanClass.name, js_boolean_str[JSVAL_TO_BOOLEAN(v) ? 1 : 0]); str = JS_NewStringCopyZ(cx, buf); if (!str) @@ -97,7 +97,7 @@ bool_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, JSAtom *atom; JSString *str; - if (!JS_InstanceOf(cx, obj, &boolean_class, argv)) + if (!JS_InstanceOf(cx, obj, &js_BooleanClass, argv)) return JS_FALSE; v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); if (!JSVAL_IS_BOOLEAN(v)) @@ -113,7 +113,7 @@ bool_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, static JSBool bool_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - if (!JS_InstanceOf(cx, obj, &boolean_class, argv)) + if (!JS_InstanceOf(cx, obj, &js_BooleanClass, argv)) return JS_FALSE; *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); return JS_TRUE; @@ -159,7 +159,7 @@ js_InitBooleanClass(JSContext *cx, JSObject *obj) { JSObject *proto; - proto = JS_InitClass(cx, obj, NULL, &boolean_class, Boolean, 1, + proto = JS_InitClass(cx, obj, NULL, &js_BooleanClass, Boolean, 1, NULL, boolean_methods, NULL, NULL); if (!proto) return NULL; @@ -172,7 +172,7 @@ js_BooleanToObject(JSContext *cx, JSBool b) { JSObject *obj; - obj = js_NewObject(cx, &boolean_class, NULL, NULL); + obj = js_NewObject(cx, &js_BooleanClass, NULL, NULL); if (!obj) return NULL; OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, BOOLEAN_TO_JSVAL(b)); diff --git a/mozilla/js/src/jsbool.h b/mozilla/js/src/jsbool.h index c07910f59c0..552bdef1adb 100644 --- a/mozilla/js/src/jsbool.h +++ b/mozilla/js/src/jsbool.h @@ -45,6 +45,8 @@ JS_BEGIN_EXTERN_C +extern JSClass js_BooleanClass; + extern JSObject * js_InitBooleanClass(JSContext *cx, JSObject *obj); diff --git a/mozilla/js/src/jscntxt.h b/mozilla/js/src/jscntxt.h index 630d6a63f1e..c3140e4c7f0 100644 --- a/mozilla/js/src/jscntxt.h +++ b/mozilla/js/src/jscntxt.h @@ -450,6 +450,7 @@ struct JSContext { #define JS_HAS_WERROR_OPTION(cx) ((cx)->options & JSOPTION_WERROR) #define JS_HAS_COMPILE_N_GO_OPTION(cx) ((cx)->options & JSOPTION_COMPILE_N_GO) #define JS_HAS_ATLINE_OPTION(cx) ((cx)->options & JSOPTION_ATLINE) +#define JS_HAS_XML_OPTION(cx) ((cx)->options & JSOPTION_XML) extern JSContext * js_NewContext(JSRuntime *rt, size_t stackChunkSize); diff --git a/mozilla/js/src/jsconfig.h b/mozilla/js/src/jsconfig.h index 44a64d87027..9d4d47c8da6 100644 --- a/mozilla/js/src/jsconfig.h +++ b/mozilla/js/src/jsconfig.h @@ -133,6 +133,7 @@ #define JS_HAS_FUN_EXPR_STMT 0 /* has function expression statement */ #define JS_HAS_LVALUE_RETURN 1 /* has o.item(i) = j; for native item */ #define JS_HAS_NO_SUCH_METHOD 0 /* has o.__noSuchMethod__ handler */ +#define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */ #elif JS_VERSION == 100 @@ -191,6 +192,7 @@ #define JS_HAS_FUN_EXPR_STMT 0 /* has function expression statement */ #define JS_HAS_LVALUE_RETURN 0 /* has o.item(i) = j; for native item */ #define JS_HAS_NO_SUCH_METHOD 0 /* has o.__noSuchMethod__ handler */ +#define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */ #elif JS_VERSION == 110 @@ -249,6 +251,7 @@ #define JS_HAS_FUN_EXPR_STMT 0 /* has function expression statement */ #define JS_HAS_LVALUE_RETURN 0 /* has o.item(i) = j; for native item */ #define JS_HAS_NO_SUCH_METHOD 0 /* has o.__noSuchMethod__ handler */ +#define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */ #elif JS_VERSION == 120 @@ -307,6 +310,7 @@ #define JS_HAS_FUN_EXPR_STMT 0 /* has function expression statement */ #define JS_HAS_LVALUE_RETURN 0 /* has o.item(i) = j; for native item */ #define JS_HAS_NO_SUCH_METHOD 0 /* has o.__noSuchMethod__ handler */ +#define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */ #elif JS_VERSION == 130 @@ -365,6 +369,7 @@ #define JS_HAS_FUN_EXPR_STMT 0 /* has function expression statement */ #define JS_HAS_LVALUE_RETURN 0 /* has o.item(i) = j; for native item */ #define JS_HAS_NO_SUCH_METHOD 0 /* has o.__noSuchMethod__ handler */ +#define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */ #elif JS_VERSION == 140 @@ -423,6 +428,7 @@ #define JS_HAS_FUN_EXPR_STMT 0 /* has function expression statement */ #define JS_HAS_LVALUE_RETURN 0 /* has o.item(i) = j; for native item */ #define JS_HAS_NO_SUCH_METHOD 0 /* has o.__noSuchMethod__ handler */ +#define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */ #elif JS_VERSION == 150 @@ -481,6 +487,7 @@ #define JS_HAS_FUN_EXPR_STMT 1 /* has function expression statement */ #define JS_HAS_LVALUE_RETURN 1 /* has o.item(i) = j; for native item */ #define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */ +#define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */ #else diff --git a/mozilla/js/src/jscpucfg.c b/mozilla/js/src/jscpucfg.c index aa6c04c4d55..737505ad00e 100644 --- a/mozilla/js/src/jscpucfg.c +++ b/mozilla/js/src/jscpucfg.c @@ -222,10 +222,10 @@ int main(int argc, char **argv) int big_endian = 0, little_endian = 0, ntests = 0; if (sizeof(short) == 2) { - /* force |volatile| here to get rid of any compiler optimisations - * (var in register etc.) which may be appiled to |auto| vars - + /* force |volatile| here to get rid of any compiler optimisations + * (var in register etc.) which may be appiled to |auto| vars - * even those in |union|s... - * (|static| is used to get the same functionality for compilers + * (|static| is used to get the same functionality for compilers * which do not honor |volatile|...). */ volatile static union { @@ -287,7 +287,7 @@ int main(int argc, char **argv) printf("#undef IS_BIG_ENDIAN\n\n"); } else { fprintf(stderr, "%s: unknown byte order" - "(big_endian=%d, little_endian=%d, ntests=%d)!\n", + "(big_endian=%d, little_endian=%d, ntests=%d)!\n", argv[0], big_endian, little_endian, ntests); return EXIT_FAILURE; } diff --git a/mozilla/js/src/jsdate.c b/mozilla/js/src/jsdate.c index 4d85b1ef73f..1b2838a6eff 100644 --- a/mozilla/js/src/jsdate.c +++ b/mozilla/js/src/jsdate.c @@ -1648,7 +1648,7 @@ date_toLocaleHelper(JSContext *cx, JSObject *obj, uintN argc, if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode) return cx->localeCallbacks->localeToUnicode(cx, buf, rval); - + str = JS_NewStringCopyZ(cx, buf); if (!str) return JS_FALSE; @@ -1877,7 +1877,7 @@ Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JSString *str; jsdouble d; - /* Date called as function */ + /* Date called as function. */ if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { int64 us, ms, us2ms; jsdouble msec_time; @@ -1893,7 +1893,7 @@ Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) return date_format(cx, msec_time, FORMATSPEC_FULL, rval); } - /* Date called as constructor */ + /* Date called as constructor. */ if (argc == 0) { int64 us, ms, us2ms; jsdouble msec_time; diff --git a/mozilla/js/src/jsdbgapi.c b/mozilla/js/src/jsdbgapi.c index 8713a86b99f..5a851445af6 100644 --- a/mozilla/js/src/jsdbgapi.c +++ b/mozilla/js/src/jsdbgapi.c @@ -408,12 +408,14 @@ js_WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, JSPropertyOp setter) if (!(attrs & JSPROP_SETTER)) return &js_watch_set; /* & to silence schoolmarmish MSVC */ - if (!JSVAL_IS_INT(id)) { - atom = (JSAtom *)id; - } else { - atom = js_AtomizeInt(cx, JSVAL_TO_INT(id), 0); + if (JSID_IS_ATOM(id)) { + atom = JSID_TO_ATOM(id); + } else if (JSID_IS_INT(id)) { + atom = js_AtomizeInt(cx, JSID_TO_INT(id), 0); if (!atom) return NULL; + } else { + atom = NULL; } wrapper = js_NewFunction(cx, NULL, js_watch_set_wrapper, 1, 0, OBJ_GET_PARENT(cx, (JSObject *)setter), @@ -444,13 +446,13 @@ JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id, } if (JSVAL_IS_INT(id)) { - propid = (jsid)id; + propid = INT_TO_JSID(JSVAL_TO_INT(id)); atom = NULL; } else { atom = js_ValueToStringAtom(cx, id); if (!atom) return JS_FALSE; - propid = (jsid)atom; + propid = ATOM_TO_JSID(atom); } if (!js_LookupProperty(cx, obj, propid, &pobj, &prop)) diff --git a/mozilla/js/src/jsdtoa.c b/mozilla/js/src/jsdtoa.c index 9f729fa8ab3..1a9cb54a14b 100644 --- a/mozilla/js/src/jsdtoa.c +++ b/mozilla/js/src/jsdtoa.c @@ -235,7 +235,7 @@ static double private_mem[PRIVATE_mem], *pmem_next = private_mem; * (which does not exist on 10.1 either). */ #if defined(MACOS_DEPLOYMENT_TARGET) && (MACOS_DEPLOYMENT_TARGET < 100200) -#undef FLT_ROUNDS +#undef FLT_ROUNDS #define FLT_ROUNDS 1 #endif #endif /* Bad_float_h */ @@ -2039,7 +2039,7 @@ static int32 quorem(Bigint *b, Bigint *S) /* Always emits at least one digit. */ /* If biasUp is set, then rounding in modes 2 and 3 will round away from zero - * when the number is exactly halfway between two representable values. For example, + * when the number is exactly halfway between two representable values. For example, * rounding 2.5 to zero digits after the decimal point will return 3 and not 2. * 2.49 will still round to 2, and 2.51 will still round to 3. */ /* bufsize should be at least 20 for modes 0 and 1. For the other modes, @@ -2118,11 +2118,11 @@ js_dtoa(double d, int mode, JSBool biasUp, int ndigits, } return JS_TRUE; } - + b = NULL; /* initialize for abort protection */ S = NULL; mlo = mhi = NULL; - + if (!d) { no_digits: *decpt = 1; @@ -2829,7 +2829,7 @@ JS_dtostr(char *buffer, size_t bufferSize, JSDToStrMode mode, int precision, dou } while (numEnd != p); *numEnd = '\0'; } - + if (exponentialNotation) { /* Insert a decimal point if more than one significand digit */ if (nDigits != 1) { @@ -2899,7 +2899,7 @@ divrem(Bigint *b, uint32 divisor) ULong dividend = remainder << 16 | a >> 16; ULong quotientHi = dividend / divisor; ULong quotientLo; - + remainder = dividend - quotientHi*divisor; JS_ASSERT(quotientHi <= 0xFFFF && remainder < divisor); dividend = remainder << 16 | (a & 0xFFFF); @@ -2954,7 +2954,7 @@ JS_dtobasestr(int base, double d) /* Locking for Balloc's shared buffers */ ACQUIRE_DTOA_LOCK(); - + /* Output the integer part of d with the digits in reverse order. */ pInt = p; di = fd_floor(d); @@ -2995,7 +2995,7 @@ JS_dtobasestr(int base, double d) *pInt++ = *q; *q-- = ch; } - + df = d - di; if (df != 0.0) { /* We have a fraction. */ @@ -3003,7 +3003,7 @@ JS_dtobasestr(int base, double d) Bigint *b, *s, *mlo, *mhi; b = s = mlo = mhi = NULL; - + *p++ = '.'; b = d2b(df, &e, &bbits); if (!b) { @@ -3017,7 +3017,7 @@ JS_dtobasestr(int base, double d) } JS_ASSERT(e < 0); /* At this point df = b * 2^e. e must be less than zero because 0 < df < 1. */ - + s2 = -(int32)(word0(d) >> Exp_shift1 & Exp_mask>>Exp_shift1); #ifndef Sudden_Underflow if (!s2) diff --git a/mozilla/js/src/jsemit.c b/mozilla/js/src/jsemit.c index f78c8ea1936..25792143cc7 100644 --- a/mozilla/js/src/jsemit.c +++ b/mozilla/js/src/jsemit.c @@ -155,12 +155,15 @@ UpdateDepth(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t target) if (nuses < 0) nuses = 2 + GET_ARGC(pc); /* stack: fun, this, [argc arguments] */ cg->stackDepth -= nuses; + JS_ASSERT(cg->stackDepth >= 0); if (cg->stackDepth < 0) { char numBuf[12]; JS_snprintf(numBuf, sizeof numBuf, "%d", target); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_STACK_UNDERFLOW, - cg->filename ? cg->filename : "stdin", numBuf); + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, + js_GetErrorMessage, NULL, + JSMSG_STACK_UNDERFLOW, + cg->filename ? cg->filename : "stdin", + numBuf); } cg->stackDepth += cs->ndefs; if ((uintN)cg->stackDepth > cg->maxStackDepth) @@ -1473,7 +1476,7 @@ js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, * nor can prop be deleted. */ prop = NULL; - ok = OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, &prop); + ok = OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop); if (ok) { if (pobj == obj && (fp->flags & (JSFRAME_EVAL | JSFRAME_COMPILE_N_GO))) { @@ -1483,9 +1486,10 @@ js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, * variable object. Therefore we can get constant values * from our variable object here. */ - ok = OBJ_GET_ATTRIBUTES(cx, obj, (jsid)atom, prop, &attrs); + ok = OBJ_GET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, + &attrs); if (ok && !(~attrs & (JSPROP_READONLY | JSPROP_PERMANENT))) - ok = OBJ_GET_PROPERTY(cx, obj, (jsid)atom, vp); + ok = OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); } if (prop) OBJ_DROP_PROPERTY(cx, pobj, prop); @@ -1730,7 +1734,7 @@ LookupArgOrVar(JSContext *cx, JSTreeContext *tc, JSParseNode *pn) * NB: We know that JSOP_DELNAME on an argument or variable evaluates * to false, due to JSPROP_PERMANENT. */ - if (!js_LookupProperty(cx, obj, (jsid)atom, &pobj, &prop)) + if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) return JS_FALSE; sprop = (JSScopeProperty *) prop; if (sprop) { @@ -2716,7 +2720,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) cg->principals)) { return JS_FALSE; } - cg2->treeContext.flags = pn->pn_flags | TCF_IN_FUNCTION; + cg2->treeContext.flags = (uint16) (pn->pn_flags | TCF_IN_FUNCTION); cg2->treeContext.tryCount = pn->pn_tryCount; cg2->parent = cg; fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(pn->pn_funAtom)); @@ -2768,7 +2772,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) uintN slot; obj = OBJ_GET_PARENT(cx, fun->object); - if (!js_LookupProperty(cx, obj, (jsid)fun->atom, &pobj, + if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(fun->atom), &pobj, &prop)) { return JS_FALSE; } @@ -3698,6 +3702,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) break; case TOK_LC: +#if JS_HAS_XML_SUPPORT + if (pn->pn_arity == PN_UNARY) { + if (!js_EmitTree(cx, cg, pn->pn_kid)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_XMLEXPR) < 0) + return JS_FALSE; + break; + } +#endif + + JS_ASSERT(pn->pn_arity == PN_LIST); js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BLOCK, top); for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { if (!js_EmitTree(cx, cg, pn2)) @@ -3739,6 +3754,11 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) } break; + case TOK_DEFAULT: + if (js_Emit1(cx, cg, pn->pn_op) < 0) + return JS_FALSE; + break; + case TOK_COLON: /* Emit an annotated nop so we know to decompile a label. */ atom = pn->pn_atom; @@ -3837,12 +3857,27 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) return JS_FALSE; if (!js_EmitTree(cx, cg, pn2->pn_right)) return JS_FALSE; +#if JS_HAS_XML_SUPPORT + if (TOKEN_TYPE_IS_XML(pn2->pn_right->pn_type) && + js_Emit1(cx, cg, JSOP_BINDXMLPROP) < 0) { + return JS_FALSE; + } +#endif break; #if JS_HAS_LVALUE_RETURN case TOK_LP: if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; break; +#endif +#if JS_HAS_XML_SUPPORT + case TOK_UNARYOP: + JS_ASSERT(pn2->pn_op == JSOP_SETXMLNAME); + if (!js_EmitTree(cx, cg, pn2->pn_kid)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_BINDXMLNAME) < 0) + return JS_FALSE; + break; #endif default: JS_ASSERT(0); @@ -3876,6 +3911,9 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) case TOK_LB: #if JS_HAS_LVALUE_RETURN case TOK_LP: +#endif +#if JS_HAS_XML_SUPPORT + case TOK_UNARYOP: #endif if (js_Emit1(cx, cg, JSOP_DUP2) < 0) return JS_FALSE; @@ -3919,6 +3957,12 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (js_Emit1(cx, cg, JSOP_SETELEM) < 0) return JS_FALSE; break; +#if JS_HAS_XML_SUPPORT + case TOK_UNARYOP: + if (js_Emit1(cx, cg, JSOP_SETXMLNAME) < 0) + return JS_FALSE; + break; +#endif default:; } break; @@ -4035,6 +4079,9 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) return JS_FALSE; } } else { +#if JS_HAS_XML_SUPPORT + case TOK_DBLCOLON: +#endif /* Binary operators that evaluate both operands unconditionally. */ if (!js_EmitTree(cx, cg, pn->pn_left)) return JS_FALSE; @@ -4047,12 +4094,24 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) #if JS_HAS_EXCEPTIONS case TOK_THROW: +#endif +#if JS_HAS_XML_SUPPORT + case TOK_AT: #endif case TOK_UNARYOP: /* Unary op, including unary +/-. */ - if (!js_EmitTree(cx, cg, pn->pn_kid)) + pn2 = pn->pn_kid; + if (!js_EmitTree(cx, cg, pn2)) return JS_FALSE; - if (js_Emit1(cx, cg, pn->pn_op) < 0) + op = pn->pn_op; +#if JS_HAS_XML_SUPPORT + if (op == JSOP_XMLNAME && + js_NewSrcNote2(cx, cg, SRC_PCBASE, + CG_OFFSET(cg) - pn2->pn_offset) < 0) { + return JS_FALSE; + } +#endif + if (js_Emit1(cx, cg, op) < 0) return JS_FALSE; break; @@ -4101,6 +4160,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (js_Emit1(cx, cg, op) < 0) return JS_FALSE; break; +#endif +#if JS_HAS_XML_SUPPORT + case TOK_UNARYOP: + JS_ASSERT(pn2->pn_op == JSOP_SETXMLNAME); + if (!js_EmitTree(cx, cg, pn2->pn_kid)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_BINDXMLNAME) < 0) + return JS_FALSE; + if (js_Emit1(cx, cg, op) < 0) + return JS_FALSE; + break; #endif default: JS_ASSERT(0); @@ -4150,6 +4220,21 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) } break; +#if JS_HAS_XML_SUPPORT + case TOK_FILTER: + if (!js_EmitTree(cx, cg, pn->pn_left)) + return JS_FALSE; + jmp = js_Emit3(cx, cg, pn->pn_op, 0, 0); + if (jmp < 0) + return JS_FALSE; + if (!js_EmitTree(cx, cg, pn->pn_right)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_POPV) < 0) + return JS_FALSE; + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); + break; +#endif + case TOK_DOT: /* * Pop a stack operand, convert it to object, get a property named by @@ -4162,6 +4247,9 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) break; case TOK_LB: +#if JS_HAS_XML_SUPPORT + case TOK_DBLDOT: +#endif /* * Pop two operands, convert the left one to object and the right one * to property name (atom or tagged int), get the named property, and @@ -4177,23 +4265,50 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) * Emit function call or operator new (constructor call) code. * First, emit code for the left operand to evaluate the callable or * constructable object expression. + * + * For E4X, if calling rather than constructing (TOK_LP), and if this + * expression is a member reference, select JSOP_CALLMETHOD instead of + * JSOP_CALL. ECMA-357 requires this to handle XML methods, which are + * not found via normal property lookup. */ pn2 = pn->pn_head; - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; + if (JS_HAS_XML_OPTION(cx) && pn2->pn_type == TOK_DOT) { + pn->pn_op = JSOP_CALLMETHOD; + if (!js_EmitTree(cx, cg, pn2->pn_expr)) + return JS_FALSE; + ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList); + if (!ale) + return JS_FALSE; + atomIndex = ALE_INDEX(ale); + pn2->pn_offset = top; + + /* + * Dup the object whose method is being called, so the stack has + * the normal [callee, this, args...] structure. + */ + if (js_Emit1(cx, cg, JSOP_DUP) < 0) + return JS_FALSE; + } else { + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + + /* + * Push the virtual machine's "obj" register, which was set by a + * name, property, or element get (or set) bytecode. + */ + if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0) + return JS_FALSE; + +#ifdef __GNUC__ + atomIndex = 0; /* quell GCC overwarning */ +#endif + } /* Remember start of callable-object bytecode for decompilation hint. */ - off = pn2->pn_offset; + off = top; /* - * Push the virtual machine's "obj" register, which was set by a name, - * property, or element get (or set) bytecode. - */ - if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0) - return JS_FALSE; - - /* - * Emit code for each argument in order, then emit the JSOP_*CALL or + * Emit code for each argument in order, then emit the JSOP_*CALL* or * JSOP_NEW bytecode with a two-byte immediate telling how many args * were pushed on the operand stack. */ @@ -4203,9 +4318,21 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) } if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - off) < 0) return JS_FALSE; + argc = pn->pn_count - 1; - if (js_Emit3(cx, cg, pn->pn_op, ARGC_HI(argc), ARGC_LO(argc)) < 0) - return JS_FALSE; + op = pn->pn_op; + if (op == JSOP_CALLMETHOD) { + off = js_EmitN(cx, cg, op, ARGNO_LEN+ATOM_INDEX_LEN); + if (off < 0) + return JS_FALSE; + pc = CG_CODE(cg, off); + SET_ARGNO(pc, argc); + pc += ARGNO_LEN; + SET_ATOM_INDEX(pc, atomIndex); + } else { + if (js_Emit3(cx, cg, op, ARGC_HI(argc), ARGC_LO(argc)) < 0) + return JS_FALSE; + } break; #if JS_HAS_INITIALIZERS @@ -4383,6 +4510,13 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) break; } /* FALL THROUGH */ + +#if JS_HAS_XML_SUPPORT + case TOK_XMLATTR: + case TOK_XMLTEXT: + case TOK_XMLCDATA: + case TOK_XMLCOMMENT: +#endif case TOK_STRING: case TOK_OBJECT: /* @@ -4403,6 +4537,9 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) ok = EmitNumberOp(cx, pn->pn_dval, cg); break; +#if JS_HAS_XML_SUPPORT + case TOK_ANYNAME: +#endif case TOK_PRIMARY: if (js_Emit1(cx, cg, pn->pn_op) < 0) return JS_FALSE; @@ -4415,6 +4552,116 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) break; #endif /* JS_HAS_DEBUGGER_KEYWORD */ +#if JS_HAS_XML_SUPPORT + case TOK_XMLELEM: + case TOK_XMLLIST: + if (pn->pn_op == JSOP_XMLOBJECT) { + ok = EmitAtomOp(cx, pn, pn->pn_op, cg); + break; + } + + JS_ASSERT(pn->pn_count != 0); + for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + if (pn2 != pn->pn_head && js_Emit1(cx, cg, JSOP_ADD) < 0) + return JS_FALSE; + } + if ((pn->pn_extra & PNX_XMLROOT) && js_Emit1(cx, cg, pn->pn_op) < 0) + return JS_FALSE; + break; + + case TOK_XMLPTAGC: + if (pn->pn_op == JSOP_XMLOBJECT) { + ok = EmitAtomOp(cx, pn, pn->pn_op, cg); + break; + } + /* FALL THROUGH */ + + case TOK_XMLSTAGO: + case TOK_XMLETAGO: + { + uint32 i; + + ale = js_IndexAtom(cx, + (pn->pn_type == TOK_XMLETAGO) + ? cx->runtime->atomState.etagoAtom + : cx->runtime->atomState.stagoAtom, + &cg->atomList); + if (!ale) + return JS_FALSE; + EMIT_ATOM_INDEX_OP(JSOP_STRING, ALE_INDEX(ale)); + + JS_ASSERT(pn->pn_count != 0); + pn2 = pn->pn_head; + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + if (js_Emit1(cx, cg, JSOP_ADD) < 0) + return JS_FALSE; + + for (pn2 = pn2->pn_next, i = 0; pn2; pn2 = pn2->pn_next, i++) { + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + if ((i & 1) && pn2->pn_type == TOK_LC) { + if (js_Emit1(cx, cg, JSOP_TOATTRVAL) < 0) + return JS_FALSE; + } + if (js_Emit1(cx, cg, + (i & 1) ? JSOP_ADDATTRVAL : JSOP_ADDATTRNAME) < 0) { + return JS_FALSE; + } + } + + ale = js_IndexAtom(cx, + (pn->pn_type == TOK_XMLPTAGC) + ? cx->runtime->atomState.ptagcAtom + : cx->runtime->atomState.tagcAtom, + &cg->atomList); + if (!ale) + return JS_FALSE; + EMIT_ATOM_INDEX_OP(JSOP_STRING, ALE_INDEX(ale)); + if (js_Emit1(cx, cg, JSOP_ADD) < 0) + return JS_FALSE; + + if ((pn->pn_extra & PNX_XMLROOT) && js_Emit1(cx, cg, pn->pn_op) < 0) + return JS_FALSE; + break; + } + + case TOK_XMLNAME: + if (pn->pn_arity == PN_LIST) { + JS_ASSERT(pn->pn_count != 0); + for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { + if (!js_EmitTree(cx, cg, pn2)) + return JS_FALSE; + if (pn2 != pn->pn_head && js_Emit1(cx, cg, JSOP_ADD) < 0) + return JS_FALSE; + } + } else { + JS_ASSERT(pn->pn_arity == PN_NULLARY); + ok = EmitAtomOp(cx, pn, pn->pn_op, cg); + } + break; + + case TOK_XMLPI: + off = js_EmitN(cx, cg, JSOP_XMLPI, 2 * ATOM_INDEX_LEN); + if (off < 0) + return JS_FALSE; + pc = CG_CODE(cg, off); + ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList); + if (!ale) + return JS_FALSE; + atomIndex = ALE_INDEX(ale); + SET_ATOM_INDEX(pc, atomIndex); + pc += ATOM_INDEX_LEN; + ale = js_IndexAtom(cx, pn->pn_atom2, &cg->atomList); + if (!ale) + return JS_FALSE; + atomIndex = ALE_INDEX(ale); + SET_ATOM_INDEX(pc, atomIndex); + break; +#endif /* JS_HAS_XML_SUPPORT */ + default: JS_ASSERT(0); } diff --git a/mozilla/js/src/jsexn.c b/mozilla/js/src/jsexn.c index 6d3a182db18..95be295f64d 100644 --- a/mozilla/js/src/jsexn.c +++ b/mozilla/js/src/jsexn.c @@ -549,7 +549,8 @@ Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) * prototype ourselves. */ ok = OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(argv[-2]), - (jsid)cx->runtime->atomState.classPrototypeAtom, + ATOM_TO_JSID(cx->runtime->atomState + .classPrototypeAtom), &pval); if (!ok) goto out; @@ -623,8 +624,11 @@ exn_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) jschar *chars, *cp; size_t name_length, message_length, length; - if (!OBJ_GET_PROPERTY(cx, obj, (jsid)cx->runtime->atomState.nameAtom, &v)) + if (!OBJ_GET_PROPERTY(cx, obj, + ATOM_TO_JSID(cx->runtime->atomState.nameAtom), + &v)) { return JS_FALSE; + } name = JSVAL_IS_STRING(v) ? JSVAL_TO_STRING(v) : cx->runtime->emptyString; if (!JS_GetProperty(cx, obj, js_message_str, &v)) @@ -675,8 +679,11 @@ exn_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) size_t lineno_length, name_length, message_length, filename_length, length; jschar *chars, *cp; - if (!OBJ_GET_PROPERTY(cx, obj, (jsid)cx->runtime->atomState.nameAtom, &v)) + if (!OBJ_GET_PROPERTY(cx, obj, + ATOM_TO_JSID(cx->runtime->atomState.nameAtom), + &v)) { return JS_FALSE; + } name = js_ValueToString(cx, v); if (!name) return JS_FALSE; diff --git a/mozilla/js/src/jsfun.c b/mozilla/js/src/jsfun.c index bbb1e1b6132..96cdfc96509 100644 --- a/mozilla/js/src/jsfun.c +++ b/mozilla/js/src/jsfun.c @@ -96,7 +96,8 @@ js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp) if (TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) { JS_ASSERT(fp->callobj); return OBJ_GET_PROPERTY(cx, fp->callobj, - (jsid) cx->runtime->atomState.argumentsAtom, + ATOM_TO_JSID(cx->runtime->atomState + .argumentsAtom), vp); } argsobj = js_GetArgsObject(cx, fp); @@ -182,7 +183,8 @@ js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, if (TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) { JS_ASSERT(fp->callobj); if (!OBJ_GET_PROPERTY(cx, fp->callobj, - (jsid) cx->runtime->atomState.argumentsAtom, + ATOM_TO_JSID(cx->runtime->atomState + .argumentsAtom), &val)) { return JS_FALSE; } @@ -199,15 +201,15 @@ js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, *objp = NULL; *vp = JSVAL_VOID; - if (JSVAL_IS_INT(id)) { - slot = (uintN) JSVAL_TO_INT(id); + if (JSID_IS_INT(id)) { + slot = (uintN) JSID_TO_INT(id); if (slot < MAXARGS(fp)) { if (fp->argsobj && ArgWasDeleted(cx, fp, slot)) return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp); *vp = fp->argv[slot]; } } else { - if (id == (jsid) cx->runtime->atomState.lengthAtom) { + if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) { if (fp->argsobj && TEST_OVERRIDE_BIT(fp, ARGS_LENGTH)) return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp); *vp = INT_TO_JSVAL((jsint) fp->argc); @@ -271,10 +273,14 @@ js_PutArgsObject(JSContext *cx, JSStackFrame *fp) * before fp goes away. */ rt = cx->runtime; - ok &= js_GetProperty(cx, argsobj, (jsid)rt->atomState.calleeAtom, &rval); - ok &= js_SetProperty(cx, argsobj, (jsid)rt->atomState.calleeAtom, &rval); - ok &= js_GetProperty(cx, argsobj, (jsid)rt->atomState.lengthAtom, &rval); - ok &= js_SetProperty(cx, argsobj, (jsid)rt->atomState.lengthAtom, &rval); + ok &= js_GetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.calleeAtom), + &rval); + ok &= js_SetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.calleeAtom), + &rval); + ok &= js_GetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.lengthAtom), + &rval); + ok &= js_SetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.lengthAtom), + &rval); /* * Clear the private pointer to fp, which is about to go away (js_Invoke). @@ -400,7 +406,8 @@ args_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, slot = JSVAL_TO_INT(id); if (slot < MAXARGS(fp) && !ArgWasDeleted(cx, fp, slot)) { /* XXX ECMA specs DontEnum, contrary to other array-like objects */ - if (!js_DefineProperty(cx, obj, (jsid) id, fp->argv[slot], + if (!js_DefineProperty(cx, obj, INT_TO_JSID(JSVAL_TO_INT(id)), + fp->argv[slot], args_getProperty, args_setProperty, JSVERSION_IS_ECMA(cx->version) ? 0 @@ -432,7 +439,7 @@ args_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, } if (atom && !TEST_OVERRIDE_BIT(fp, tinyid)) { - if (!js_DefineNativeProperty(cx, obj, (jsid) atom, value, + if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value, args_getProperty, args_setProperty, 0, SPROP_HAS_SHORTID, tinyid, NULL)) { return JS_FALSE; @@ -465,14 +472,16 @@ args_enumerate(JSContext *cx, JSObject *obj) * and creates direct properties of obj, but that it may fail to resolve * length or callee if overridden. */ - if (!js_LookupProperty(cx, obj, (jsid) cx->runtime->atomState.lengthAtom, + if (!js_LookupProperty(cx, obj, + ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), &pobj, &prop)) { return JS_FALSE; } if (prop) OBJ_DROP_PROPERTY(cx, pobj, prop); - if (!js_LookupProperty(cx, obj, (jsid) cx->runtime->atomState.calleeAtom, + if (!js_LookupProperty(cx, obj, + ATOM_TO_JSID(cx->runtime->atomState.calleeAtom), &pobj, &prop)) { return JS_FALSE; } @@ -481,10 +490,8 @@ args_enumerate(JSContext *cx, JSObject *obj) nargs = MAXARGS(fp); for (slot = 0; slot < nargs; slot++) { - if (!js_LookupProperty(cx, obj, (jsid) INT_TO_JSVAL((jsint)slot), - &pobj, &prop)) { + if (!js_LookupProperty(cx, obj, INT_TO_JSID((jsint)slot), &pobj, &prop)) return JS_FALSE; - } if (prop) OBJ_DROP_PROPERTY(cx, pobj, prop); } @@ -573,7 +580,7 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp) * Get the arguments object to snapshot fp's actual argument values. */ if (fp->argsobj) { - argsid = (jsid) cx->runtime->atomState.argumentsAtom; + argsid = ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom); ok &= js_GetProperty(cx, callobj, argsid, &aval); ok &= js_SetProperty(cx, callobj, argsid, &aval); ok &= js_PutArgsObject(cx, fp); @@ -782,7 +789,7 @@ call_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, atom = js_AtomizeString(cx, str, 0); if (!atom) return JS_FALSE; - if (!js_LookupProperty(cx, funobj, (jsid)atom, &obj2, &prop)) + if (!js_LookupProperty(cx, funobj, ATOM_TO_JSID(atom), &obj2, &prop)) return JS_FALSE; sprop = (JSScopeProperty *) prop; @@ -981,8 +988,10 @@ fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, * Clone of a function: make its prototype property value have the * same class as the clone-parent's prototype. */ - if (!OBJ_GET_PROPERTY(cx, fun->object, (jsid)prototypeAtom, &pval)) + if (!OBJ_GET_PROPERTY(cx, fun->object, ATOM_TO_JSID(prototypeAtom), + &pval)) { return JS_FALSE; + } if (JSVAL_IS_OBJECT(pval)) parentProto = JSVAL_TO_OBJECT(pval); } @@ -1200,11 +1209,12 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp) return JS_FALSE; /* Flag duplicate argument if atom is bound in fun->object. */ - dupflag = SCOPE_GET_PROPERTY(OBJ_SCOPE(fun->object), (jsid)atom) + dupflag = SCOPE_GET_PROPERTY(OBJ_SCOPE(fun->object), + ATOM_TO_JSID(atom)) ? SPROP_IS_DUPLICATE : 0; - if (!js_AddNativeProperty(cx, fun->object, (jsid)atom, + if (!js_AddNativeProperty(cx, fun->object, ATOM_TO_JSID(atom), getter, setter, SPROP_INVALID_SLOT, attrs | JSPROP_SHARED, SPROP_HAS_SHORTID | dupflag, @@ -1257,7 +1267,8 @@ fun_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) JSString *str; if (!OBJ_GET_PROPERTY(cx, obj, - (jsid)cx->runtime->atomState.classPrototypeAtom, + ATOM_TO_JSID(cx->runtime->atomState + .classPrototypeAtom), &pval)) { return JS_FALSE; } @@ -1562,7 +1573,7 @@ js_IsIdentifier(JSString *str) return JS_FALSE; s = JSSTRING_CHARS(str); c = *s; - if (!JS_ISIDENT_START(c)) + if (!JS_ISIDSTART(c)) return JS_FALSE; for (n--; n != 0; n--) { c = *++s; @@ -1724,8 +1735,10 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) * we're assured at this point that it's a valid identifier. */ atom = CURRENT_TOKEN(ts).t_atom; - if (!js_LookupProperty(cx, obj, (jsid)atom, &obj2, &prop)) + if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &obj2, + &prop)) { goto bad_formal; + } sprop = (JSScopeProperty *) prop; dupflag = 0; if (sprop) { @@ -1754,7 +1767,7 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) goto bad_formal; sprop = NULL; } - if (!js_AddNativeProperty(cx, fun->object, (jsid)atom, + if (!js_AddNativeProperty(cx, fun->object, ATOM_TO_JSID(atom), js_GetArgument, js_SetArgument, SPROP_INVALID_SLOT, JSPROP_ENUMERATE | JSPROP_PERMANENT | @@ -1948,8 +1961,10 @@ js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native, fun = js_NewFunction(cx, NULL, native, nargs, attrs, obj, atom); if (!fun) return NULL; - if (!OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, OBJECT_TO_JSVAL(fun->object), - NULL, NULL, attrs & ~JSFUN_FLAGS_MASK, NULL)) { + if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), + OBJECT_TO_JSVAL(fun->object), + NULL, NULL, + attrs & ~JSFUN_FLAGS_MASK, NULL)) { return NULL; } return fun; diff --git a/mozilla/js/src/jsfun.h b/mozilla/js/src/jsfun.h index eb79161a90f..e5c8ead7f6e 100644 --- a/mozilla/js/src/jsfun.h +++ b/mozilla/js/src/jsfun.h @@ -78,7 +78,7 @@ extern JS_FRIEND_DATA(JSClass) js_FunctionClass; * NB: jsapi.h and jsobj.h must be included before any call to this macro. */ #define JSVAL_IS_FUNCTION(cx, v) \ - (JSVAL_IS_OBJECT(v) && JSVAL_TO_OBJECT(v) && \ + (!JSVAL_IS_PRIMITIVE(v) && \ OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_FunctionClass) extern JSBool diff --git a/mozilla/js/src/jsgc.c b/mozilla/js/src/jsgc.c index 754f4ae6d1f..65d37098879 100644 --- a/mozilla/js/src/jsgc.c +++ b/mozilla/js/src/jsgc.c @@ -1267,6 +1267,9 @@ restart: GC_MARK(cx, fp->scopeChain, "scope chain", NULL); if (fp->sharpArray) GC_MARK(cx, fp->sharpArray, "sharp array", NULL); + + if (fp->xmlNamespace) + GC_MARK(cx, fp->xmlNamespace, "xmlNamespace", NULL); } while ((fp = fp->down) != NULL); } diff --git a/mozilla/js/src/jsgc.h b/mozilla/js/src/jsgc.h index a4813d16d57..9ca6876df5a 100644 --- a/mozilla/js/src/jsgc.h +++ b/mozilla/js/src/jsgc.h @@ -131,7 +131,7 @@ js_LockGCThingRT(JSRuntime *rt, void *thing); extern JSBool js_UnlockGCThingRT(JSRuntime *rt, void *thing); -extern JSBool +extern JSBool js_IsAboutToBeFinalized(JSContext *cx, void *thing); extern void diff --git a/mozilla/js/src/jsinterp.c b/mozilla/js/src/jsinterp.c index 6269f4c87f3..ae09fad82b9 100644 --- a/mozilla/js/src/jsinterp.c +++ b/mozilla/js/src/jsinterp.c @@ -75,6 +75,10 @@ #include "jsjit.h" #endif +#if JS_HAS_XML_SUPPORT +#include "jsxml.h" +#endif + #ifdef DEBUG #define ASSERT_CACHE_IS_EMPTY(cache) \ JS_BEGIN_MACRO \ @@ -470,7 +474,7 @@ SetFunctionSlot(JSContext *cx, JSObject *obj, JSPropertyOp setter, jsid id, JSString *str; JSBool ok; - slot = (uintN) JSVAL_TO_INT(id); + slot = (uintN) JSID_TO_INT(id); if (OBJ_GET_CLASS(cx, obj) != &js_FunctionClass) { /* * Given a non-function object obj that has a function object in its @@ -1042,7 +1046,8 @@ js_Invoke(JSContext *cx, uintN argc, uintN flags) thisp = frame.thisp; ok = OBJ_GET_PROPERTY(cx, thisp, - (jsid)cx->runtime->atomState.noSuchMethodAtom, + ATOM_TO_JSID(cx->runtime->atomState + .noSuchMethodAtom), &v); if (!ok) goto out2; @@ -1180,6 +1185,7 @@ have_fun: frame.sharpDepth = 0; frame.sharpArray = NULL; frame.dormantNext = NULL; + frame.xmlNamespace = NULL; /* Compute the 'this' parameter and store it in frame as frame.thisp. */ ok = ComputeThis(cx, thisp, &frame); @@ -1347,14 +1353,43 @@ bad: goto out2; } +static JSBool +CallMethod(JSContext *cx, JSObject *obj, jsid id, uintN argc, jsval *vp) +{ + JSStackFrame *fp, frame; + JSXMLObjectOps *ops; + JSBool ok; + + memset(&frame, 0, sizeof(JSStackFrame)); + frame.argc = argc; + frame.argv = vp + 2; + frame.rval = JSVAL_VOID; + fp = cx->fp; + frame.varobj = fp->varobj; + frame.scopeChain = fp->scopeChain; + frame.down = fp; + frame.sp = fp->sp; + if (!ComputeThis(cx, JSVAL_TO_OBJECT(vp[1]), &frame)) + return JS_FALSE; + cx->fp = &frame; + ops = (JSXMLObjectOps *) obj->map->ops; + ok = ops->callMethod(cx, obj, id, argc, frame.argv, &frame.rval); + cx->fp = fp; + *vp = frame.rval; + fp->sp = vp + 1; + return ok; +} + JSBool js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags, uintN argc, jsval *argv, jsval *rval) { JSStackFrame *fp, *oldfp, frame; - jsval *oldsp, *sp; + jsval *oldsp, *sp, *vp; void *mark; uintN i; + const char *name; + JSAtom *atom; JSBool ok; fp = oldfp = cx->fp; @@ -1373,14 +1408,28 @@ js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags, PUSH(OBJECT_TO_JSVAL(obj)); for (i = 0; i < argc; i++) PUSH(argv[i]); - fp->sp = sp; - ok = js_Invoke(cx, argc, flags | JSINVOKE_INTERNAL); + SAVE_SP(fp); + + if (flags & JSINVOKE_CALL_METHOD) { + name = (const char *) fval; + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) { + ok = JS_FALSE; + } else { + vp = sp - (2 + argc); + *vp = OBJECT_TO_JSVAL(obj); + ok = CallMethod(cx, obj, ATOM_TO_JSID(atom), argc, vp); + } + } else { + ok = js_Invoke(cx, argc, flags | JSINVOKE_INTERNAL); + } + if (ok) { RESTORE_SP(fp); *rval = POP_OPND(); } - js_FreeStack(cx, mark); + out: fp->sp = oldsp; if (oldfp != fp) @@ -1478,6 +1527,7 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script, frame.sharpDepth = 0; frame.flags = flags; frame.dormantNext = NULL; + frame.xmlNamespace = NULL; /* * Here we wrap the call to js_Interpret with code to (conditionally) @@ -1697,7 +1747,7 @@ js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs, : isFunction ? js_function_str : js_var_str; - name = js_AtomToPrintableString(cx, (JSAtom *)id); + name = js_AtomToPrintableString(cx, JSID_TO_ATOM(id)); if (!name) goto bad; return JS_ReportErrorFlagsAndNumber(cx, report, @@ -1870,7 +1920,7 @@ js_Interpret(JSContext *cx, jsval *result) SAVE_SP(fp); for (n = -nuses; n < 0; n++) { str = js_DecompileValueGenerator(cx, n, sp[n], NULL); - if (str != NULL) { + if (str) { fprintf(tracefp, "%s %s", (n == -nuses) ? " inputs:" : ",", JS_GetStringBytes(str)); @@ -2121,12 +2171,20 @@ js_Interpret(JSContext *cx, jsval *result) STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); break; +/* + * If the index value at sp[n] is not an int that fits in a jsval, it could + * be an object (an XML QName, AttributeName, or AnyName). Otherwise convert + * it to a string atom it. + * + * NB: This macro knows that int jsval and int jsid are tagged identically. + */ #define FETCH_ELEMENT_ID(n, id) \ JS_BEGIN_MACRO \ - /* If the index is not a jsint, atomize it. */ \ id = (jsid) FETCH_OPND(n); \ if (JSVAL_IS_INT(id)) { \ atom = NULL; \ + } else if (JSVAL_IS_OBJECT(id)) { \ + id = OBJECT_TO_JSID(JSVAL_TO_OBJECT(id)); \ } else { \ SAVE_SP(fp); \ atom = js_ValueToStringAtom(cx, (jsval)id); \ @@ -2134,7 +2192,7 @@ js_Interpret(JSContext *cx, jsval *result) ok = JS_FALSE; \ goto out; \ } \ - id = (jsid)atom; \ + id = ATOM_TO_JSID(atom); \ } \ JS_END_MACRO @@ -2176,13 +2234,13 @@ js_Interpret(JSContext *cx, jsval *result) */ lval = FETCH_OPND(-1); atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); i = -2; goto do_forinloop; case JSOP_FORNAME: atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); /* * ECMA 12.6.3 says to eval the LHS after looking for properties @@ -2423,18 +2481,37 @@ js_Interpret(JSContext *cx, jsval *result) goto out; \ JS_END_MACRO +#if JS_HAS_XML_SUPPORT +#define CHECK_OBJECT_ID(n, id) \ + JS_BEGIN_MACRO \ + if (op != JSOP_SETELEM && JSID_IS_OBJECT(id)) { \ + SAVE_SP(fp); \ + ok = js_BindXMLProperty(cx, FETCH_OPND(n-1), \ + OBJECT_TO_JSVAL(JSID_TO_OBJECT(id))); \ + if (!ok) \ + goto out; \ + } \ + JS_END_MACRO +#else +#define CHECK_OBJECT_ID(n, id) /* nothing */ +#endif + #define ELEMENT_OP(n, call) \ JS_BEGIN_MACRO \ FETCH_ELEMENT_ID(n, id); \ + CHECK_OBJECT_ID(n, id); \ PROPERTY_OP(n-1, call); \ JS_END_MACRO /* * Direct callers, i.e. those who do not wrap CACHED_GET and CACHED_SET calls * in PROPERTY_OP or ELEMENT_OP macro calls must SAVE_SP(fp); beforehand, just - * in case a getter or setter function is invoked. + * in case a getter or setter function is invoked. CACHED_GET and CACHED_SET + * use cx, obj, id, and rval from their caller's lexical environment. */ -#define CACHED_GET(call) \ +#define CACHED_GET(call) CACHED_GET_VP(call, &rval) + +#define CACHED_GET_VP(call,vp) \ JS_BEGIN_MACRO \ if (!OBJ_IS_NATIVE(obj)) { \ ok = call; \ @@ -2444,14 +2521,14 @@ js_Interpret(JSContext *cx, jsval *result) if (sprop) { \ JSScope *scope_ = OBJ_SCOPE(obj); \ slot = (uintN)sprop->slot; \ - rval = (slot != SPROP_INVALID_SLOT) \ - ? LOCKED_OBJ_GET_SLOT(obj, slot) \ - : JSVAL_VOID; \ + *(vp) = (slot != SPROP_INVALID_SLOT) \ + ? LOCKED_OBJ_GET_SLOT(obj, slot) \ + : JSVAL_VOID; \ JS_UNLOCK_SCOPE(cx, scope_); \ - ok = SPROP_GET(cx, sprop, obj, obj, &rval); \ + ok = SPROP_GET(cx, sprop, obj, obj, vp); \ JS_LOCK_SCOPE(cx, scope_); \ if (ok && SPROP_HAS_VALID_SLOT(sprop, scope_)) \ - LOCKED_OBJ_SET_SLOT(obj, slot, rval); \ + LOCKED_OBJ_SET_SLOT(obj, slot, *(vp)); \ JS_UNLOCK_SCOPE(cx, scope_); \ } else { \ JS_UNLOCK_OBJ(cx, obj); \ @@ -2492,7 +2569,8 @@ js_Interpret(JSContext *cx, jsval *result) obj = fp->varobj; atom = GET_ATOM(cx, script, pc); rval = FETCH_OPND(-1); - ok = OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, rval, NULL, NULL, + ok = OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), rval, + NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY, NULL); @@ -2504,7 +2582,7 @@ js_Interpret(JSContext *cx, jsval *result) case JSOP_BINDNAME: atom = GET_ATOM(cx, script, pc); SAVE_SP(fp); - obj = js_FindIdentifierBase(cx, (jsid)atom); + obj = js_FindIdentifierBase(cx, ATOM_TO_JSID(atom)); if (!obj) { ok = JS_FALSE; goto out; @@ -2514,7 +2592,7 @@ js_Interpret(JSContext *cx, jsval *result) case JSOP_SETNAME: atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); rval = FETCH_OPND(-1); lval = FETCH_OPND(-2); JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval)); @@ -2918,7 +2996,8 @@ js_Interpret(JSContext *cx, jsval *result) } else { /* Get the constructor prototype object for this function. */ ok = OBJ_GET_PROPERTY(cx, obj2, - (jsid)rt->atomState.classPrototypeAtom, + ATOM_TO_JSID(rt->atomState + .classPrototypeAtom), &rval); if (!ok) goto out; @@ -2957,12 +3036,9 @@ js_Interpret(JSContext *cx, jsval *result) break; } /* native [[Construct]] returning primitive is error */ - str = js_ValueToString(cx, rval); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_NEW_RESULT, - JS_GetStringBytes(str)); - } + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_NEW_RESULT, + js_ValueToPrintableString(cx, rval)); ok = JS_FALSE; goto out; } @@ -2972,7 +3048,7 @@ js_Interpret(JSContext *cx, jsval *result) case JSOP_DELNAME: atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); SAVE_SP(fp); ok = js_FindProperty(cx, id, &obj, &obj2, &prop); @@ -2992,7 +3068,7 @@ js_Interpret(JSContext *cx, jsval *result) case JSOP_DELPROP: atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); PROPERTY_OP(-1, ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval)); STORE_OPND(-1, rval); break; @@ -3021,7 +3097,7 @@ js_Interpret(JSContext *cx, jsval *result) case JSOP_NAMEINC: case JSOP_NAMEDEC: atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); SAVE_SP(fp); ok = js_FindProperty(cx, id, &obj, &obj2, &prop); @@ -3039,7 +3115,7 @@ js_Interpret(JSContext *cx, jsval *result) case JSOP_PROPINC: case JSOP_PROPDEC: atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); lval = POP_OPND(); goto do_incop; @@ -3048,6 +3124,7 @@ js_Interpret(JSContext *cx, jsval *result) case JSOP_ELEMINC: case JSOP_ELEMDEC: POP_ELEMENT_ID(id); + CHECK_OBJECT_ID(0, id); lval = POP_OPND(); do_incop: @@ -3194,7 +3271,7 @@ js_Interpret(JSContext *cx, jsval *result) case JSOP_GETPROP: /* Get an immediate atom naming the property. */ atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); PROPERTY_OP(-1, CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval))); STORE_OPND(-1, rval); break; @@ -3205,7 +3282,7 @@ js_Interpret(JSContext *cx, jsval *result) /* Get an immediate atom naming the property. */ atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); PROPERTY_OP(-2, CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval))); sp--; STORE_OPND(-1, rval); @@ -3227,6 +3304,7 @@ js_Interpret(JSContext *cx, jsval *result) case JSOP_ENUMELEM: /* Funky: the value to set is under the [obj, id] pair. */ FETCH_ELEMENT_ID(-1, id); + CHECK_OBJECT_ID(-1, id); lval = FETCH_OPND(-2); VALUE_TO_OBJECT(cx, lval, obj); rval = FETCH_OPND(-3); @@ -3255,13 +3333,43 @@ js_Interpret(JSContext *cx, jsval *result) PUSH_OPND(OBJECT_TO_JSVAL(obj)); break; +#if JS_HAS_XML_SUPPORT + case JSOP_CALLMETHOD: + argc = GET_ARGC(pc); + vp = sp - (argc + 2); + lval = *vp; + VALUE_TO_OBJECT(cx, lval, obj); + pc2 = pc + ARGNO_LEN; + atom = GET_ATOM(cx, script, pc2); + id = ATOM_TO_JSID(atom); + + SAVE_SP(fp); + if (OBJECT_IS_XML(cx, obj)) { + ok = CallMethod(cx, obj, id, argc, vp); + if (!ok) + goto out; + RESTORE_SP(fp); + obj = NULL; + break; + } + + CACHED_GET_VP(OBJ_GET_PROPERTY(cx, obj, id, vp), vp); + if (!ok) + goto out; + vp[1] = OBJECT_TO_JSVAL(obj); + goto try_inline_call; +#endif + case JSOP_CALL: case JSOP_EVAL: argc = GET_ARGC(pc); vp = sp - (argc + 2); - lval = *vp; SAVE_SP(fp); +#if JS_HAS_XML_SUPPORT + try_inline_call: +#endif + lval = *vp; if (JSVAL_IS_FUNCTION(cx, lval) && (obj = JSVAL_TO_OBJECT(lval), fun = (JSFunction *) JS_GetPrivate(cx, obj), @@ -3425,7 +3533,7 @@ js_Interpret(JSContext *cx, jsval *result) case JSOP_NAME: atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); SAVE_SP(fp); ok = js_FindProperty(cx, id, &obj, &obj2, &prop); @@ -3835,7 +3943,7 @@ js_Interpret(JSContext *cx, jsval *result) case JSOP_EXPORTNAME: atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); obj = fp->varobj; ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); if (!ok) @@ -3856,7 +3964,7 @@ js_Interpret(JSContext *cx, jsval *result) break; case JSOP_IMPORTALL: - id = (jsid)JSVAL_VOID; + id = (jsid) JSVAL_VOID; PROPERTY_OP(-1, ok = ImportProperty(cx, obj, id)); sp--; break; @@ -3864,7 +3972,7 @@ js_Interpret(JSContext *cx, jsval *result) case JSOP_IMPORTPROP: /* Get an immediate atom naming the property. */ atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); PROPERTY_OP(-1, ok = ImportProperty(cx, obj, id)); sp--; break; @@ -3910,7 +4018,7 @@ js_Interpret(JSContext *cx, jsval *result) break; case JSOP_ARGSUB: - id = (jsid) INT_TO_JSVAL(GET_ARGNO(pc)); + id = INT_TO_JSID(GET_ARGNO(pc)); SAVE_SP(fp); ok = js_GetArgsProperty(cx, fp, id, &obj, &rval); if (!ok) @@ -3932,7 +4040,7 @@ js_Interpret(JSContext *cx, jsval *result) #undef LAZY_ARGS_THISP case JSOP_ARGCNT: - id = (jsid) rt->atomState.lengthAtom; + id = ATOM_TO_JSID(rt->atomState.lengthAtom); SAVE_SP(fp); ok = js_GetArgsProperty(cx, fp, id, &obj, &rval); if (!ok) @@ -3999,7 +4107,7 @@ js_Interpret(JSContext *cx, jsval *result) * as JSOP_SETNAME does, where [obj] is due to JSOP_BINDNAME. */ atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); SAVE_SP(fp); CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); if (!ok) @@ -4027,7 +4135,7 @@ js_Interpret(JSContext *cx, jsval *result) attrs |= JSPROP_READONLY; /* Lookup id in order to check for redeclaration problems. */ - id = (jsid)atom; + id = ATOM_TO_JSID(atom); ok = js_CheckRedeclaration(cx, obj, id, attrs, &obj2, &prop); if (!ok) goto out; @@ -4079,7 +4187,7 @@ js_Interpret(JSContext *cx, jsval *result) atom = js_GetAtom(cx, &script->atomMap, atomIndex); obj = ATOM_TO_OBJECT(atom); fun = (JSFunction *) JS_GetPrivate(cx, obj); - id = (jsid) fun->atom; + id = ATOM_TO_JSID(fun->atom); /* * We must be at top-level (either outermost block that forms a @@ -4262,7 +4370,7 @@ js_Interpret(JSContext *cx, jsval *result) attrs = fun->flags & (JSFUN_GETTER | JSFUN_SETTER); if (attrs) attrs |= JSPROP_SHARED; - ok = OBJ_DEFINE_PROPERTY(cx, parent, (jsid)fun->atom, + ok = OBJ_DEFINE_PROPERTY(cx, parent, ATOM_TO_JSID(fun->atom), attrs ? JSVAL_VOID : OBJECT_TO_JSVAL(obj), (attrs & JSFUN_GETTER) ? (JSPropertyOp) obj @@ -4329,7 +4437,7 @@ js_Interpret(JSContext *cx, jsval *result) if (attrs) attrs |= JSPROP_SHARED; parent = fp->varobj; - ok = OBJ_DEFINE_PROPERTY(cx, parent, (jsid)fun->atom, + ok = OBJ_DEFINE_PROPERTY(cx, parent, ATOM_TO_JSID(fun->atom), attrs ? JSVAL_VOID : OBJECT_TO_JSVAL(obj), (attrs & JSFUN_GETTER) ? (JSPropertyOp) obj @@ -4369,7 +4477,7 @@ js_Interpret(JSContext *cx, jsval *result) case JSOP_SETNAME: case JSOP_SETPROP: atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); i = -1; rval = FETCH_OPND(i); goto gs_pop_lval; @@ -4389,7 +4497,7 @@ js_Interpret(JSContext *cx, jsval *result) i = -1; rval = FETCH_OPND(i); atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); goto gs_get_lval; case JSOP_INITELEM: @@ -4477,7 +4585,7 @@ js_Interpret(JSContext *cx, jsval *result) /* Get the immediate property name into id. */ atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); i = -1; goto do_init; @@ -4515,7 +4623,7 @@ js_Interpret(JSContext *cx, jsval *result) fp->sharpArray = obj; } i = (jsint) GET_ATOM_INDEX(pc); - id = (jsid) INT_TO_JSVAL(i); + id = INT_TO_JSID(i); rval = FETCH_OPND(-1); if (JSVAL_IS_PRIMITIVE(rval)) { char numBuf[12]; @@ -4532,7 +4640,7 @@ js_Interpret(JSContext *cx, jsval *result) case JSOP_USESHARP: i = (jsint) GET_ATOM_INDEX(pc); - id = (jsid) INT_TO_JSVAL(i); + id = INT_TO_JSID(i); obj = fp->sharpArray; if (!obj) { rval = JSVAL_VOID; @@ -4605,7 +4713,7 @@ js_Interpret(JSContext *cx, jsval *result) /* Get the immediate catch variable name into id. */ atom = GET_ATOM(cx, script, pc); - id = (jsid)atom; + id = ATOM_TO_JSID(atom); /* Find the object being initialized at top of stack. */ lval = FETCH_OPND(-1); @@ -4679,6 +4787,306 @@ js_Interpret(JSContext *cx, jsval *result) } #endif /* JS_HAS_DEBUGGER_KEYWORD */ +#if JS_HAS_XML_SUPPORT + case JSOP_DEFXMLNS: + rval = POP(); + ok = js_SetDefaultXMLNamespace(cx, rval); + if (!ok) + goto out; + break; + + case JSOP_ANYNAME: + ok = js_GetAnyName(cx, &rval); + if (!ok) + goto out; + PUSH_OPND(rval); + break; + + case JSOP_QNAMEPART: + atom = GET_ATOM(cx, script, pc); + PUSH_OPND(ATOM_KEY(atom)); + break; + + case JSOP_QNAME: + rval = FETCH_OPND(-1); + lval = FETCH_OPND(-2); + SAVE_SP(fp); + obj = js_ConstructQNameObject(cx, lval, rval); + if (!obj) { + ok = JS_FALSE; + goto out; + } + sp--; + STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); + break; + + case JSOP_TOATTRNAME: + rval = FETCH_OPND(-1); + ok = js_ToAttributeName(cx, &rval); + if (!ok) + goto out; + STORE_OPND(-1, rval); + break; + + case JSOP_TOATTRVAL: + rval = FETCH_OPND(-1); + JS_ASSERT(JSVAL_IS_STRING(rval)); + str = js_EscapeAttributeValue(cx, JSVAL_TO_STRING(rval)); + if (!str) { + ok = JS_FALSE; + goto out; + } + STORE_OPND(-1, STRING_TO_JSVAL(str)); + break; + + case JSOP_ADDATTRNAME: + case JSOP_ADDATTRVAL: + rval = FETCH_OPND(-1); + lval = FETCH_OPND(-2); + str = JSVAL_TO_STRING(lval); + str2 = JSVAL_TO_STRING(rval); + str = js_AddAttributePart(cx, op == JSOP_ADDATTRNAME, str, str2); + if (!str) { + ok = JS_FALSE; + goto out; + } + sp--; + STORE_OPND(-1, STRING_TO_JSVAL(str)); + break; + + case JSOP_BINDXMLPROP: + rval = FETCH_OPND(-1); + lval = FETCH_OPND(-2); + SAVE_SP(fp); + ok = js_BindXMLProperty(cx, lval, rval); + if (!ok) + goto out; + break; + + case JSOP_BINDXMLNAME: + lval = FETCH_OPND(-1); + ok = js_FindXMLProperty(cx, lval, &obj, &rval); + if (!ok) + goto out; + if (JSVAL_IS_VOID(rval)) { + const char *printable = js_ValueToPrintableString(cx, lval); + if (printable) + js_ReportIsNotDefined(cx, printable); + ok = JS_FALSE; + goto out; + } + STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); + PUSH_OPND(rval); + break; + + case JSOP_SETXMLNAME: + obj = JSVAL_TO_OBJECT(FETCH_OPND(-3)); + lval = FETCH_OPND(-2); + rval = FETCH_OPND(-1); + ok = js_SetXMLProperty(cx, obj, lval, &rval); + if (!ok) + goto out; + sp -= 2; + STORE_OPND(-1, rval); + break; + + case JSOP_XMLNAME: + lval = FETCH_OPND(-1); + ok = js_FindXMLProperty(cx, lval, &obj, &rval); + if (!ok) + goto out; + if (!JSVAL_IS_VOID(rval)) { + ok = js_GetXMLProperty(cx, obj, rval, &rval); + if (!ok) + goto out; + } + STORE_OPND(-1, rval); + break; + + case JSOP_DESCENDANTS: + lval = FETCH_OPND(-2); + VALUE_TO_OBJECT(cx, lval, obj); + rval = FETCH_OPND(-1); + ok = js_GetXMLDescendants(cx, obj, rval, &rval); + if (!ok) + goto out; + sp--; + STORE_OPND(-1, rval); + break; + + case JSOP_FILTER: + lval = FETCH_OPND(-1); + VALUE_TO_OBJECT(cx, lval, obj); + len = GET_JUMP_OFFSET(pc); + SAVE_SP(fp); + ok = js_FilterXMLList(cx, obj, pc + cs->length, len - cs->length, + &rval); + if (!ok) + goto out; + STORE_OPND(-1, rval); + break; + + case JSOP_TOXML: + rval = FETCH_OPND(-1); + obj = js_ValueToXMLObject(cx, rval); + if (!obj) { + ok = JS_FALSE; + goto out; + } + STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); + break; + + case JSOP_TOXMLLIST: + rval = FETCH_OPND(-1); + obj = js_ValueToXMLListObject(cx, rval); + if (!obj) { + ok = JS_FALSE; + goto out; + } + STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); + break; + + case JSOP_XMLEXPR: + rval = FETCH_OPND(-1); + str = js_ValueToString(cx, rval); + if (!str) { + ok = JS_FALSE; + goto out; + } + STORE_OPND(-1, STRING_TO_JSVAL(str)); + break; + + case JSOP_XMLOBJECT: + atom = GET_ATOM(cx, script, pc); + obj = js_CloneXMLObject(cx, ATOM_TO_OBJECT(atom)); + if (!obj) { + ok = JS_FALSE; + goto out; + } + PUSH_OPND(OBJECT_TO_JSVAL(obj)); + obj = NULL; + break; + + case JSOP_XMLCDATA: + { + jschar *buf; + size_t len, off; + + /* XXXbe temporary: should construct an XML object */ + atom = GET_ATOM(cx, script, pc); + str = ATOM_TO_STRING(atom); + len = JSSTRING_LENGTH(str); + buf = (jschar *) JS_malloc(cx, (9 + len + 3 + 1) * sizeof(jschar)); + if (!buf) { + ok = JS_FALSE; + goto out; + } + buf[0] = (jschar) '<'; + buf[1] = (jschar) '!'; + buf[2] = (jschar) '['; + buf[3] = (jschar) 'C'; + buf[4] = (jschar) 'D'; + buf[5] = (jschar) 'A'; + buf[6] = (jschar) 'T'; + buf[7] = (jschar) 'A'; + buf[8] = (jschar) '['; + off = 9; + js_strncpy(buf + off, JSSTRING_CHARS(str), len); + off += len; + buf[off++] = (jschar) ']'; + buf[off++] = (jschar) ']'; + buf[off++] = (jschar) '>'; + buf[off] = 0; + str = js_NewString(cx, buf, off, 0); + if (!str) { + JS_free(cx, buf); + ok = JS_FALSE; + goto out; + } + rval = STRING_TO_JSVAL(str); + PUSH_OPND(rval); + break; + } + + case JSOP_XMLCOMMENT: + { + jschar *buf; + size_t len, off; + + /* XXXbe temporary: should construct an XML object */ + atom = GET_ATOM(cx, script, pc); + str = ATOM_TO_STRING(atom); + len = JSSTRING_LENGTH(str); + buf = (jschar *) JS_malloc(cx, (4 + len + 3 + 1) * sizeof(jschar)); + if (!buf) { + ok = JS_FALSE; + goto out; + } + buf[0] = (jschar) '<'; + buf[1] = (jschar) '!'; + buf[2] = (jschar) '-'; + buf[3] = (jschar) '-'; + off = 4; + js_strncpy(buf + off, JSSTRING_CHARS(str), len); + off += len; + buf[off++] = (jschar) '-'; + buf[off++] = (jschar) '-'; + buf[off++] = (jschar) '>'; + buf[off] = 0; + str = js_NewString(cx, buf, off, 0); + if (!str) { + JS_free(cx, buf); + ok = JS_FALSE; + goto out; + } + rval = STRING_TO_JSVAL(str); + PUSH_OPND(rval); + break; + } + + case JSOP_XMLPI: + { + JSAtom *atom2; + jschar *buf; + size_t len, len2, off; + + /* XXXbe temporary: should construct an XML object */ + atom = GET_ATOM(cx, script, pc); + str = ATOM_TO_STRING(atom); + len = JSSTRING_LENGTH(str); + atom2 = GET_ATOM(cx, script, pc + ATOM_INDEX_LEN); + str2 = ATOM_TO_STRING(atom2); + len2 = JSSTRING_LENGTH(str2); + buf = (jschar *) JS_malloc(cx, + (2 + len + 1 + len2 + 2 + 1) + * sizeof(jschar)); + if (!buf) { + ok = JS_FALSE; + goto out; + } + buf[0] = (jschar) '<'; + buf[1] = (jschar) '?'; + off = 2; + js_strncpy(buf + off, JSSTRING_CHARS(str), len); + off += len; + buf[off++] = (jschar) ' '; + js_strncpy(buf + off, JSSTRING_CHARS(str2), len2); + off += len2; + buf[off++] = (jschar) '?'; + buf[off++] = (jschar) '>'; + buf[off] = 0; + str = js_NewString(cx, buf, off, 0); + if (!str) { + JS_free(cx, buf); + ok = JS_FALSE; + goto out; + } + rval = STRING_TO_JSVAL(str); + PUSH_OPND(rval); + break; + } +#endif /* JS_HAS_XML_SUPPORT */ + default: { char numBuf[12]; JS_snprintf(numBuf, sizeof numBuf, "%d", op); diff --git a/mozilla/js/src/jsinterp.h b/mozilla/js/src/jsinterp.h index 81f16d76022..846c3487157 100644 --- a/mozilla/js/src/jsinterp.h +++ b/mozilla/js/src/jsinterp.h @@ -72,6 +72,7 @@ struct JSStackFrame { JSObject *sharpArray; /* scope for #n= initializer vars */ uint32 flags; /* frame flags -- see below */ JSStackFrame *dormantNext; /* next dormant frame chain */ + JSObject *xmlNamespace; /* null or default xml namespace in E4X */ }; typedef struct JSInlineFrame { @@ -93,7 +94,7 @@ typedef struct JSInlineFrame { #define JSFRAME_SPECIAL 0x30 /* special evaluation frame flags */ #define JSFRAME_COMPILING 0x40 /* frame is being used by compiler */ #define JSFRAME_COMPILE_N_GO 0x80 /* compiler-and-go mode, can optimize name - references based on scope chain */ + references based on scope chain */ #define JSFRAME_OVERRIDE_SHIFT 24 /* override bit-set params; see jsfun.c */ #define JSFRAME_OVERRIDE_BITS 8 @@ -256,7 +257,8 @@ extern void js_DumpCallTable(JSContext *cx); /* * NB: js_Invoke requires that cx is currently running JS (i.e., that cx->fp - * is non-null). + * is non-null), and that the callee, |this| parameter, and actual arguments + * are already pushed on the stack under cx->fp->sp. */ extern JS_FRIEND_API(JSBool) js_Invoke(JSContext *cx, uintN argc, uintN flags); @@ -267,6 +269,7 @@ js_Invoke(JSContext *cx, uintN argc, uintN flags); #define JSINVOKE_CONSTRUCT JSFRAME_CONSTRUCTING #define JSINVOKE_INTERNAL JSFRAME_INTERNAL #define JSINVOKE_SKIP_CALLER JSFRAME_SKIP_CALLER +#define JSINVOKE_CALL_METHOD 0x8 /* * "Internal" calls may come from C or C++ code using a JSContext on which no @@ -275,6 +278,10 @@ js_Invoke(JSContext *cx, uintN argc, uintN flags); #define js_InternalCall(cx,obj,fval,argc,argv,rval) \ js_InternalInvoke(cx, obj, fval, 0, argc, argv, rval) +#define js_InternalCallMethod(cx,obj,name,argc,argv,rval) \ + js_InternalInvoke(cx, obj, (jsval) name, JSINVOKE_CALL_METHOD, \ + argc, argv, rval) + #define js_InternalConstruct(cx,obj,fval,argc,argv,rval) \ js_InternalInvoke(cx, obj, fval, JSINVOKE_CONSTRUCT, argc, argv, rval) diff --git a/mozilla/js/src/jslibmath.h b/mozilla/js/src/jslibmath.h index 7cbcf767abe..6ed874bd6dc 100644 --- a/mozilla/js/src/jslibmath.h +++ b/mozilla/js/src/jslibmath.h @@ -119,7 +119,7 @@ #define __P(p) () #endif -#if defined _WIN32 || defined SUNOS4 +#if defined _WIN32 || defined SUNOS4 #define fd_acos acos #define fd_asin asin @@ -268,7 +268,7 @@ extern double fd_atan __P((double)); extern double fd_cos __P((double)); extern double fd_sin __P((double)); extern double fd_tan __P((double)); - + extern double fd_exp __P((double)); extern double fd_log __P((double)); extern double fd_sqrt __P((double)); diff --git a/mozilla/js/src/jslock.h b/mozilla/js/src/jslock.h index 9ece59c9b98..0ab71927b2d 100644 --- a/mozilla/js/src/jslock.h +++ b/mozilla/js/src/jslock.h @@ -106,28 +106,9 @@ typedef struct JSFatLockTable { * an #include cycle between jslock.h and jsscope.h: moderate-sized XXX here, * to be fixed by moving JS_LOCK_SCOPE to jsscope.h, JS_LOCK_OBJ to jsobj.h, * and so on. - * - * We also need jsscope.h #ifdef DEBUG for SET_OBJ_INFO and SET_SCOPE_INFO, - * but we do not want any nested includes that depend on DEBUG. Those lead - * to build bustage when someone makes a change that depends in a subtle way - * on jsscope.h being included directly or indirectly, but does not test by - * building optimized as well as DEBUG. */ #include "jsscope.h" -#ifdef DEBUG - -#define SET_OBJ_INFO(obj_,file_,line_) \ - SET_SCOPE_INFO(OBJ_SCOPE(obj_),file_,line_) - -#define SET_SCOPE_INFO(scope_,file_,line_) \ - ((scope_)->ownercx ? (void)0 : \ - (JS_ASSERT((0 < (scope_)->u.count && (scope_)->u.count <= 4) || \ - SCOPE_IS_SEALED(scope_)), \ - (void)((scope_)->file[(scope_)->u.count-1] = (file_), \ - (scope_)->line[(scope_)->u.count-1] = (line_)))) -#endif /* DEBUG */ - #define JS_LOCK_RUNTIME(rt) js_LockRuntime(rt) #define JS_UNLOCK_RUNTIME(rt) js_UnlockRuntime(rt) @@ -140,16 +121,14 @@ typedef struct JSFatLockTable { */ #define JS_LOCK_OBJ(cx,obj) ((OBJ_SCOPE(obj)->ownercx == (cx)) \ ? (void)0 \ - : (js_LockObj(cx, obj), \ - SET_OBJ_INFO(obj,__FILE__,__LINE__))) + : (js_LockObj(cx, obj))) #define JS_UNLOCK_OBJ(cx,obj) ((OBJ_SCOPE(obj)->ownercx == (cx)) \ ? (void)0 : js_UnlockObj(cx, obj)) -#define JS_LOCK_SCOPE(cx,scope) ((scope)->ownercx == (cx) ? (void)0 : \ - (js_LockScope(cx, scope), \ - SET_SCOPE_INFO(scope,__FILE__,__LINE__))) -#define JS_UNLOCK_SCOPE(cx,scope) ((scope)->ownercx == (cx) ? (void)0 : \ - js_UnlockScope(cx, scope)) +#define JS_LOCK_SCOPE(cx,scope) ((scope)->ownercx == (cx) ? (void)0 \ + : js_LockScope(cx, scope)) +#define JS_UNLOCK_SCOPE(cx,scope) ((scope)->ownercx == (cx) ? (void)0 \ + : js_UnlockScope(cx, scope)) #define JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope) \ js_TransferScopeLock(cx, scope, newscope) @@ -279,11 +258,4 @@ extern JS_INLINE void js_Unlock(JSThinLock *tl, jsword me); #define JS_LOCK(P,CX) JS_LOCK0(P,(CX)->thread) #define JS_UNLOCK(P,CX) JS_UNLOCK0(P,(CX)->thread) -#ifndef SET_OBJ_INFO -#define SET_OBJ_INFO(obj,f,l) ((void)0) -#endif -#ifndef SET_SCOPE_INFO -#define SET_SCOPE_INFO(scope,f,l) ((void)0) -#endif - #endif /* jslock_h___ */ diff --git a/mozilla/js/src/jslong.h b/mozilla/js/src/jslong.h index bde8bfbbf17..059cf00bb22 100644 --- a/mozilla/js/src/jslong.h +++ b/mozilla/js/src/jslong.h @@ -185,7 +185,7 @@ extern JS_PUBLIC_API(JSInt64) JSLL_Zero(void); /*********************************************************************** ** MACROS: JSLL_UDIVMOD ** DESCRIPTION: -** Produce both a quotient and a remainder given an unsigned +** Produce both a quotient and a remainder given an unsigned ** INPUTS: JSUint64 a: The dividend of the operation ** JSUint64 b: The quotient of the operation ** OUTPUTS: JSUint64 *qp: pointer to quotient diff --git a/mozilla/js/src/jsmath.c b/mozilla/js/src/jsmath.c index 9c6fcec0e43..af37d80b0e3 100644 --- a/mozilla/js/src/jsmath.c +++ b/mozilla/js/src/jsmath.c @@ -132,7 +132,7 @@ math_asin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) #ifdef XP_MAC if (x == 0) return js_NewNumberValue(cx, x, rval); -#endif +#endif z = fd_asin(x); return js_NewNumberValue(cx, z, rval); } @@ -147,7 +147,7 @@ math_atan(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) #ifdef XP_MAC if (x == 0) return js_NewNumberValue(cx, x, rval); -#endif +#endif z = fd_atan(x); return js_NewNumberValue(cx, z, rval); } @@ -465,7 +465,7 @@ JSObject * js_InitMathClass(JSContext *cx, JSObject *obj) { JSObject *Math; - + Math = JS_DefineObject(cx, obj, "Math", &math_class, NULL, 0); if (!Math) return NULL; diff --git a/mozilla/js/src/jsmath.h b/mozilla/js/src/jsmath.h index 7a6b21657bc..b67dba1deb7 100644 --- a/mozilla/js/src/jsmath.h +++ b/mozilla/js/src/jsmath.h @@ -10,17 +10,17 @@ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. - * + * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. - * + * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998-1999 * the Initial Developer. All Rights Reserved. - * + * * Contributor(s): - * + * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), diff --git a/mozilla/js/src/jsnum.c b/mozilla/js/src/jsnum.c index 9de1d72db72..952c7080d16 100644 --- a/mozilla/js/src/jsnum.c +++ b/mozilla/js/src/jsnum.c @@ -161,7 +161,7 @@ static JSFunctionSpec number_functions[] = { {0,0,0,0,0} }; -static JSClass number_class = { +JSClass js_NumberClass = { "Number", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, @@ -201,7 +201,7 @@ num_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) char buf[64]; JSString *str; - if (!JS_InstanceOf(cx, obj, &number_class, argv)) + if (!JS_InstanceOf(cx, obj, &js_NumberClass, argv)) return JS_FALSE; v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); JS_ASSERT(JSVAL_IS_NUMBER(v)); @@ -211,7 +211,7 @@ num_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JS_ReportOutOfMemory(cx); return JS_FALSE; } - JS_snprintf(buf, sizeof buf, "(new %s(%s))", number_class.name, numStr); + JS_snprintf(buf, sizeof buf, "(new %s(%s))", js_NumberClass.name, numStr); str = JS_NewStringCopyZ(cx, buf); if (!str) return JS_FALSE; @@ -256,7 +256,7 @@ num_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) jsint base; JSString *str; - if (!JS_InstanceOf(cx, obj, &number_class, argv)) + if (!JS_InstanceOf(cx, obj, &js_NumberClass, argv)) return JS_FALSE; v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); JS_ASSERT(JSVAL_IS_NUMBER(v)); @@ -385,7 +385,7 @@ num_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, static JSBool num_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - if (!JS_InstanceOf(cx, obj, &number_class, argv)) + if (!JS_InstanceOf(cx, obj, &js_NumberClass, argv)) return JS_FALSE; *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); return JS_TRUE; @@ -404,7 +404,7 @@ num_to(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval, JSDTo JSString *str; char buf[DTOSTR_VARIABLE_BUFFER_SIZE(MAX_PRECISION+1)], *numStr; /* Use MAX_PRECISION+1 because precisionOffset can be 1 */ - if (!JS_InstanceOf(cx, obj, &number_class, argv)) + if (!JS_InstanceOf(cx, obj, &js_NumberClass, argv)) return JS_FALSE; v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); JS_ASSERT(JSVAL_IS_NUMBER(v)); @@ -605,7 +605,7 @@ js_InitNumberClass(JSContext *cx, JSObject *obj) if (!JS_DefineFunctions(cx, obj, number_functions)) return NULL; - proto = JS_InitClass(cx, obj, NULL, &number_class, Number, 1, + proto = JS_InitClass(cx, obj, NULL, &js_NumberClass, Number, 1, NULL, number_methods, NULL, NULL); if (!proto || !(ctor = JS_GetConstructor(cx, proto))) return NULL; @@ -679,7 +679,7 @@ js_NumberToObject(JSContext *cx, jsdouble d) JSObject *obj; jsval v; - obj = js_NewObject(cx, &number_class, NULL, NULL); + obj = js_NewObject(cx, &js_NumberClass, NULL, NULL); if (!obj) return NULL; if (!js_NewNumberValue(cx, d, &v)) { diff --git a/mozilla/js/src/jsnum.h b/mozilla/js/src/jsnum.h index 28f40e6ecb8..1f041f2c3ce 100644 --- a/mozilla/js/src/jsnum.h +++ b/mozilla/js/src/jsnum.h @@ -144,6 +144,8 @@ extern void js_FinishRuntimeNumberState(JSContext *cx); /* Initialize the Number class, returning its prototype object. */ +extern JSClass js_NumberClass; + extern JSObject * js_InitNumberClass(JSContext *cx, JSObject *obj); diff --git a/mozilla/js/src/jsobj.c b/mozilla/js/src/jsobj.c index 847040c615f..388584a97dd 100644 --- a/mozilla/js/src/jsobj.c +++ b/mozilla/js/src/jsobj.c @@ -83,11 +83,7 @@ js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop); JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = { js_NewObjectMap, js_DestroyObjectMap, -#if defined JS_THREADSAFE && defined DEBUG - _js_LookupProperty, js_DefineProperty, -#else js_LookupProperty, js_DefineProperty, -#endif js_GetProperty, js_SetProperty, js_GetAttributes, js_SetAttributes, js_DeleteProperty, js_DefaultValue, @@ -157,10 +153,10 @@ obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp) slot = (uint32) JSVAL_TO_INT(id); if (id == INT_TO_JSVAL(JSSLOT_PROTO)) { - id = (jsid)cx->runtime->atomState.protoAtom; + id = ATOM_TO_JSID(cx->runtime->atomState.protoAtom); mode = JSACC_PROTO; } else { - id = (jsid)cx->runtime->atomState.parentAtom; + id = ATOM_TO_JSID(cx->runtime->atomState.parentAtom); mode = JSACC_PARENT; } if (!OBJ_CHECK_ACCESS(cx, obj, id, mode, vp, &attrs)) @@ -789,7 +785,7 @@ js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, goto error; /* Convert id to a jsval and then to a string. */ - atom = JSVAL_IS_INT(id) ? NULL : (JSAtom *)id; + atom = JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : NULL; id = ID_TO_VALUE(id); idstr = js_ValueToString(cx, id); if (!idstr) { @@ -805,7 +801,7 @@ js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, */ if (atom ? (ATOM_KEYWORD(atom) || !js_IsIdentifier(idstr)) - : JSVAL_TO_INT(id) < 0) { + : (JSID_IS_OBJECT(id) || JSID_TO_INT(id) < 0)) { idstr = js_QuoteString(cx, idstr, (jschar)'\''); if (!idstr) { ok = JS_FALSE; @@ -1200,9 +1196,9 @@ obj_unwatch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) */ /* Proposed ECMA 15.2.4.5. */ -static JSBool -obj_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) +JSBool +js_obj_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) { jsid id; JSObject *obj2; @@ -1433,7 +1429,7 @@ static JSFunctionSpec object_methods[] = { {js_unwatch_str, obj_unwatch, 1,0,0}, #endif #if JS_HAS_NEW_OBJ_METHODS - {js_hasOwnProperty_str, obj_hasOwnProperty, 1,0,0}, + {js_hasOwnProperty_str, js_obj_hasOwnProperty,1,0,0}, {js_isPrototypeOf_str, obj_isPrototypeOf, 1,0,0}, {js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0,0}, #endif @@ -1474,11 +1470,7 @@ Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) */ static JSBool with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, - JSProperty **propp -#if defined JS_THREADSAFE && defined DEBUG - , const char *file, uintN line -#endif - ) + JSProperty **propp) { JSObject *proto; JSScopeProperty *sprop; @@ -1696,11 +1688,13 @@ js_InitObjectClass(JSContext *cx, JSObject *obj) #endif /* ECMA (15.1.2.1) says 'eval' is also a property of the global object. */ - if (!OBJ_GET_PROPERTY(cx, proto, (jsid)cx->runtime->atomState.evalAtom, + if (!OBJ_GET_PROPERTY(cx, proto, + ATOM_TO_JSID(cx->runtime->atomState.evalAtom), &eval)) { return NULL; } - if (!OBJ_DEFINE_PROPERTY(cx, obj, (jsid)cx->runtime->atomState.evalAtom, + if (!OBJ_DEFINE_PROPERTY(cx, obj, + ATOM_TO_JSID(cx->runtime->atomState.evalAtom), eval, NULL, NULL, 0, NULL)) { return NULL; } @@ -1760,11 +1754,10 @@ GetClassPrototype(JSContext *cx, JSObject *scope, const char *name, JSObject * js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) { - JSObject *obj, *ctor; + JSObject *obj; JSObjectOps *ops; JSObjectMap *map; JSClass *protoclasp; - jsval cval; uint32 nslots, i; jsval *newslots; @@ -1802,16 +1795,12 @@ js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) (JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT))) && protoclasp->reserveSlots == clasp->reserveSlots))) { - /* Default parent to the parent of the prototype's constructor. */ - if (!parent) { - if (!OBJ_GET_PROPERTY(cx, proto, - (jsid)cx->runtime->atomState.constructorAtom, - &cval)) { - goto bad; - } - if (JSVAL_IS_OBJECT(cval) && (ctor = JSVAL_TO_OBJECT(cval)) != NULL) - parent = OBJ_GET_PARENT(cx, ctor); - } + /* + * Default parent to the parent of the prototype, which was set from + * the parent of the prototype's constructor. + */ + if (!parent) + parent = OBJ_GET_PARENT(cx, proto); /* Share the given prototype's map. */ obj->map = js_HoldObjectMap(cx, map); @@ -1864,8 +1853,8 @@ bad: return NULL; } -static JSBool -FindConstructor(JSContext *cx, JSObject *start, const char *name, jsval *vp) +JSBool +js_FindConstructor(JSContext *cx, JSObject *start, const char *name, jsval *vp) { JSAtom *atom; JSObject *obj, *pobj; @@ -1891,12 +1880,8 @@ FindConstructor(JSContext *cx, JSObject *start, const char *name, jsval *vp) } JS_ASSERT(OBJ_IS_NATIVE(obj)); - if (!js_LookupPropertyWithFlags(cx, obj, (jsid)atom, JSRESOLVE_CLASSNAME, - &pobj, &prop -#if defined JS_THREADSAFE && defined DEBUG - , __FILE__, __LINE__ -#endif - )) { + if (!js_LookupPropertyWithFlags(cx, obj, ATOM_TO_JSID(atom), + JSRESOLVE_CLASSNAME, &pobj, &prop)) { return JS_FALSE; } if (!prop) { @@ -1919,7 +1904,7 @@ js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, jsval cval, rval; JSObject *obj, *ctor; - if (!FindConstructor(cx, parent, clasp->name, &cval)) + if (!js_FindConstructor(cx, parent, clasp->name, &cval)) return NULL; if (JSVAL_IS_PRIMITIVE(cval)) { js_ReportIsNotFunction(cx, &cval, JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK); @@ -1935,7 +1920,8 @@ js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, parent = OBJ_GET_PARENT(cx, ctor); if (!proto) { if (!OBJ_GET_PROPERTY(cx, ctor, - (jsid)cx->runtime->atomState.classPrototypeAtom, + ATOM_TO_JSID(cx->runtime->atomState + .classPrototypeAtom), &rval)) { return NULL; } @@ -2074,8 +2060,8 @@ js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot) #define CHECK_FOR_FUNNY_INDEX(id) \ JS_BEGIN_MACRO \ - if (!JSVAL_IS_INT(id)) { \ - JSAtom *atom_ = (JSAtom *)id; \ + if (JSID_IS_ATOM(id)) { \ + JSAtom *atom_ = JSID_TO_ATOM(id); \ JSString *str_ = ATOM_TO_STRING(atom_); \ const jschar *cp_ = str_->chars; \ JSBool negative_ = (*cp_ == '-'); \ @@ -2110,7 +2096,7 @@ CheckForFunnyIndex(jsid id, const jschar *cp, JSBool negative) c <= (JSVAL_INT_MAX % 10)))) { if (negative) index = 0 - index; - id = INT_TO_JSVAL((jsint)index); + id = INT_TO_JSID((jsint)index); } return id; } @@ -2334,13 +2320,16 @@ Detecting(JSContext *cx, jsbytecode *pc) return JS_FALSE; } +JS_FRIEND_API(JSBool) +js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + JSProperty **propp) +{ + return js_LookupPropertyWithFlags(cx, obj, id, 0, objp, propp); +} + JSBool js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, - JSObject **objp, JSProperty **propp -#if defined JS_THREADSAFE && defined DEBUG - , const char *file, uintN line -#endif - ) + JSObject **objp, JSProperty **propp) { JSObject *start, *obj2, *proto; JSScope *scope; @@ -2366,7 +2355,6 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, start = obj; for (;;) { JS_LOCK_OBJ(cx, obj); - SET_OBJ_INFO(obj, file, line); scope = OBJ_SCOPE(obj); if (scope->object == obj) { sprop = SCOPE_GET_PROPERTY(scope, id); @@ -2435,7 +2423,6 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, goto cleanup; JS_LOCK_OBJ(cx, obj); - SET_OBJ_INFO(obj, file, line); if (obj2) { /* Resolved: juggle locks and lookup id again. */ if (obj2 != obj) { @@ -2478,7 +2465,6 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, if (!ok) goto cleanup; JS_LOCK_OBJ(cx, obj); - SET_OBJ_INFO(obj, file, line); scope = OBJ_SCOPE(obj); JS_ASSERT(MAP_IS_NATIVE(&scope->map)); if (scope->object == obj) @@ -2515,22 +2501,6 @@ out: return JS_TRUE; } -#if defined JS_THREADSAFE && defined DEBUG -JS_FRIEND_API(JSBool) -_js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, - JSProperty **propp, const char *file, uintN line) -{ - return js_LookupPropertyWithFlags(cx, obj, id, 0, objp, propp, file, line); -} -#else -JS_FRIEND_API(JSBool) -js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, - JSProperty **propp) -{ - return js_LookupPropertyWithFlags(cx, obj, id, 0, objp, propp); -} -#endif - JS_FRIEND_API(JSBool) js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp, JSProperty **propp) @@ -2641,7 +2611,7 @@ js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) #if JS_BUG_NULL_INDEX_PROPS /* Indexed properties defaulted to null in old versions. */ - default_val = (JSVAL_IS_INT(id) && JSVAL_TO_INT(id) >= 0) + default_val = (JSID_IS_INT(id) && JSID_TO_INT(id) >= 0) ? JSVAL_NULL : JSVAL_VOID; #else @@ -3068,8 +3038,9 @@ js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) * method, and calling that method returned failure. */ if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL, - &v)) + &v)) { return JS_FALSE; + } if (!JSVAL_IS_PRIMITIVE(v)) { if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v)) @@ -3382,7 +3353,8 @@ GetCurrentExecutionContext(JSContext *cx, JSObject *obj, jsval *rval) while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) obj = tmp; if (!OBJ_GET_PROPERTY(cx, obj, - (jsid)cx->runtime->atomState.ExecutionContextAtom, + ATOM_TO_JSID(cx->runtime->atomState + .ExecutionContextAtom), &xcval)) { return JS_FALSE; } @@ -3391,7 +3363,7 @@ GetCurrentExecutionContext(JSContext *cx, JSObject *obj, jsval *rval) return JS_FALSE; } if (!OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(xcval), - (jsid)cx->runtime->atomState.currentAtom, + ATOM_TO_JSID(cx->runtime->atomState.currentAtom), rval)) { return JS_FALSE; } @@ -3413,7 +3385,7 @@ js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) callee = JSVAL_TO_OBJECT(argv[-2]); if (!OBJ_GET_PROPERTY(cx, callee, - (jsid)cx->runtime->atomState.callAtom, + ATOM_TO_JSID(cx->runtime->atomState.callAtom), &fval)) { return JS_FALSE; } @@ -3455,7 +3427,8 @@ js_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, callee = JSVAL_TO_OBJECT(argv[-2]); if (!OBJ_GET_PROPERTY(cx, callee, - (jsid)cx->runtime->atomState.constructAtom, + ATOM_TO_JSID(cx->runtime->atomState + .constructAtom), &cval)) { return JS_FALSE; } @@ -3494,7 +3467,8 @@ js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) jsval fval, rval; if (!OBJ_GET_PROPERTY(cx, obj, - (jsid)cx->runtime->atomState.hasInstanceAtom, + ATOM_TO_JSID(cx->runtime->atomState + .hasInstanceAtom), &fval)) { return JS_FALSE; } @@ -3539,12 +3513,13 @@ GetClassPrototype(JSContext *cx, JSObject *scope, const char *name, jsval v; JSObject *ctor; - if (!FindConstructor(cx, scope, name, &v)) + if (!js_FindConstructor(cx, scope, name, &v)) return JS_FALSE; if (JSVAL_IS_FUNCTION(cx, v)) { ctor = JSVAL_TO_OBJECT(v); if (!OBJ_GET_PROPERTY(cx, ctor, - (jsid)cx->runtime->atomState.classPrototypeAtom, + ATOM_TO_JSID(cx->runtime->atomState + .classPrototypeAtom), &v)) { return JS_FALSE; } @@ -3564,8 +3539,10 @@ js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto, * DontDelete. */ if (!OBJ_DEFINE_PROPERTY(cx, ctor, - (jsid)cx->runtime->atomState.classPrototypeAtom, - OBJECT_TO_JSVAL(proto), NULL, NULL, + ATOM_TO_JSID(cx->runtime->atomState + .classPrototypeAtom), + OBJECT_TO_JSVAL(proto), + JS_PropertyStub, JS_PropertyStub, attrs, NULL)) { return JS_FALSE; } @@ -3575,8 +3552,10 @@ js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto, * for a user-defined function f, is DontEnum. */ return OBJ_DEFINE_PROPERTY(cx, proto, - (jsid)cx->runtime->atomState.constructorAtom, - OBJECT_TO_JSVAL(ctor), NULL, NULL, + ATOM_TO_JSID(cx->runtime->atomState + .constructorAtom), + OBJECT_TO_JSVAL(ctor), + JS_PropertyStub, JS_PropertyStub, 0, NULL); } @@ -3658,7 +3637,7 @@ js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom, * behave properly. */ older = JS_SetErrorReporter(cx, NULL); - if (!OBJ_GET_PROPERTY(cx, obj, (jsid)atom, &fval)) { + if (!OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &fval)) { JS_ClearPendingException(cx); ok = JS_TRUE; } else if (!JSVAL_IS_PRIMITIVE(fval)) { diff --git a/mozilla/js/src/jsobj.h b/mozilla/js/src/jsobj.h index 9ac5857e097..6a7850ab6ec 100644 --- a/mozilla/js/src/jsobj.h +++ b/mozilla/js/src/jsobj.h @@ -61,13 +61,8 @@ struct JSObjectMap { }; /* Shorthand macros for frequently-made calls. */ -#if defined JS_THREADSAFE && defined DEBUG -#define OBJ_LOOKUP_PROPERTY(cx,obj,id,objp,propp) \ - (obj)->map->ops->lookupProperty(cx,obj,id,objp,propp,__FILE__,__LINE__) -#else #define OBJ_LOOKUP_PROPERTY(cx,obj,id,objp,propp) \ (obj)->map->ops->lookupProperty(cx,obj,id,objp,propp) -#endif #define OBJ_DEFINE_PROPERTY(cx,obj,id,value,getter,setter,attrs,propp) \ (obj)->map->ops->defineProperty(cx,obj,id,value,getter,setter,attrs,propp) #define OBJ_GET_PROPERTY(cx,obj,id,vp) \ @@ -261,6 +256,10 @@ extern JSBool js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); +extern JSBool +js_obj_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + extern JSObject * js_InitObjectClass(JSContext *cx, JSObject *obj); @@ -295,6 +294,9 @@ js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj); extern JSObject * js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent); +extern JSBool +js_FindConstructor(JSContext *cx, JSObject *start, const char *name, jsval *vp); + extern JSObject * js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent, uintN argc, jsval *argv); @@ -351,29 +353,16 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, * *objp and *propp null. Therefore all callers who receive a non-null *propp * must later call OBJ_DROP_PROPERTY(cx, *objp, *propp). */ -#if defined JS_THREADSAFE && defined DEBUG -extern JS_FRIEND_API(JSBool) -_js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, - JSProperty **propp, const char *file, uintN line); - -#define js_LookupProperty(cx,obj,id,objp,propp) \ - _js_LookupProperty(cx,obj,id,objp,propp,__FILE__,__LINE__) -#else extern JS_FRIEND_API(JSBool) js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, JSProperty **propp); -#endif /* * Specialized subroutine that allows caller to preset JSRESOLVE_* flags. */ extern JSBool js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, - JSObject **objp, JSProperty **propp -#if defined JS_THREADSAFE && defined DEBUG - , const char *file, uintN line -#endif - ); + JSObject **objp, JSProperty **propp); extern JS_FRIEND_API(JSBool) js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp, diff --git a/mozilla/js/src/jsopcode.c b/mozilla/js/src/jsopcode.c index e751103a0fd..cc09b4cd536 100644 --- a/mozilla/js/src/jsopcode.c +++ b/mozilla/js/src/jsopcode.c @@ -82,6 +82,7 @@ const char js_null_str[] = "null"; const char js_this_str[] = "this"; const char js_false_str[] = "false"; const char js_true_str[] = "true"; +const char js_default_str[] = "default"; const char *js_incop_str[] = {"++", "--"}; @@ -111,7 +112,7 @@ static ptrdiff_t GetJumpOffset(jsbytecode *pc, jsbytecode *pc2) { uint32 type; - + type = (js_CodeSpec[*pc].format & JOF_TYPEMASK); if (JOF_TYPE_IS_EXTENDED_JUMP(type)) return GET_JUMPX_OFFSET(pc2); @@ -148,9 +149,9 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, const JSCodeSpec *cs; ptrdiff_t len, off, jmplen; uint32 type; + jsbytecode *stop; JSAtom *atom; JSString *str; - char *cstr; op = (JSOp)*pc; if (op >= JSOP_LIMIT) { @@ -184,16 +185,16 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, fprintf(fp, " %u (%d)", loc + off, off); break; + case JOF_CONST2: case JOF_CONST: - atom = GET_ATOM(cx, script, pc); - str = js_ValueToSource(cx, ATOM_KEY(atom)); - if (!str) - return 0; - cstr = js_DeflateString(cx, JSSTRING_CHARS(str), JSSTRING_LENGTH(str)); - if (!cstr) - return 0; - fprintf(fp, " %s", cstr); - JS_free(cx, cstr); + stop = pc + len - 1; + do { + atom = GET_ATOM(cx, script, pc); + str = js_ValueToSource(cx, ATOM_KEY(atom)); + if (!str) + return 0; + fprintf(fp, " %s", JS_GetStringBytes(str)); + } while ((pc += ATOM_INDEX_LEN) < stop); break; case JOF_UINT16: @@ -249,12 +250,7 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, str = js_ValueToSource(cx, ATOM_KEY(atom)); if (!str) return 0; - cstr = js_DeflateString(cx, JSSTRING_CHARS(str), - JSSTRING_LENGTH(str)); - if (!cstr) - return 0; - fprintf(fp, "\n\t%s: %d", cstr, off); - JS_free(cx, cstr); + fprintf(fp, "\n\t%s: %d", JS_GetStringBytes(str), off); npairs--; } len = 1 + pc2 - pc; @@ -271,18 +267,14 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, break; #if JS_HAS_LEXICAL_CLOSURE - case JOF_DEFLOCALVAR: + case JOF_INDEXCONST: fprintf(fp, " %u", GET_VARNO(pc)); pc += VARNO_LEN; atom = GET_ATOM(cx, script, pc); str = js_ValueToSource(cx, ATOM_KEY(atom)); if (!str) return 0; - cstr = js_DeflateString(cx, JSSTRING_CHARS(str), JSSTRING_LENGTH(str)); - if (!cstr) - return 0; - fprintf(fp, " %s", cstr); - JS_free(cx, cstr); + fprintf(fp, " %s", JS_GetStringBytes(str)); break; #endif @@ -701,7 +693,7 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, diff = table[0].offset - defaultOffset; if (diff > 0) { jp->indent += 2; - js_printf(jp, "\tdefault:\n"); + js_printf(jp, "\t%s:\n", js_default_str); jp->indent += 2; if (!Decompile(ss, pc + defaultOffset, diff)) return JS_FALSE; @@ -768,7 +760,7 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, off = defaultOffset; } jp->indent -= 2; - js_printf(jp, "\tdefault:\n"); + js_printf(jp, "\t%s:\n", js_default_str); jp->indent += 2; } if (!Decompile(ss, pc + off, off2 - off)) @@ -779,7 +771,7 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, if (defaultOffset == switchLength) { jp->indent += 2; - js_printf(jp, "\tdefault:;\n"); + js_printf(jp, "\t%s:;\n", js_default_str); jp->indent -= 2; } js_printf(jp, "\t}\n"); @@ -1497,7 +1489,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) RETRACT(&ss->sprinter, xval); js_printf(jp, *lval ? ".%s" : "%s", xval); } else if (xval) { - js_printf(jp, "[%s]", xval); + js_printf(jp, + (js_CodeSpec[lastop].format & JOF_XMLNAME) + ? ".%s%s" + : "[%s]", + xval); } rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-1]); js_printf(jp, " in %s) {\n", rval); @@ -1607,6 +1603,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_NEW: case JSOP_CALL: + case JSOP_CALLMETHOD: case JSOP_EVAL: #if JS_HAS_LVALUE_RETURN case JSOP_SETCALL: @@ -1641,6 +1638,15 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) if (saveop == JSOP_NEW) { todo = Sprint(&ss->sprinter, "%s %s%s", js_new_str, argv[0], lval); + } else if (saveop == JSOP_CALLMETHOD) { + atom = GET_ATOM(cx, jp->script, pc + ARGNO_LEN); + xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); + if (!xval) { + todo = -1; + } else { + todo = Sprint(&ss->sprinter, "%s.%s%s", + argv[0], xval, lval); + } } else { todo = Sprint(&ss->sprinter, "%s%s", argv[0], lval); @@ -1694,7 +1700,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_DELELEM: xval = POP_STR(); lval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s %s[%s]", + todo = Sprint(&ss->sprinter, + (js_CodeSpec[lastop].format & JOF_XMLNAME) + ? "%s.%s%s" + : "%s[%s]%s", js_delete_str, lval, xval); break; @@ -1743,9 +1752,17 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_DECELEM: xval = POP_STR(); lval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s%s[%s]", - js_incop_str[!(cs->format & JOF_INC)], - lval, xval); + if (*xval != '\0') { + todo = Sprint(&ss->sprinter, + (js_CodeSpec[lastop].format & JOF_XMLNAME) + ? "%s%s.%s" + : "%s%s[%s]", + js_incop_str[!(cs->format & JOF_INC)], + lval, xval); + } else { + todo = Sprint(&ss->sprinter, "%s%s", + js_incop_str[!(cs->format & JOF_INC)], lval); + } break; case JSOP_ARGINC: @@ -1787,9 +1804,17 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_ELEMDEC: xval = POP_STR(); lval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s[%s]%s", - lval, xval, - js_incop_str[!(cs->format & JOF_INC)]); + if (*xval != '\0') { + todo = Sprint(&ss->sprinter, + (js_CodeSpec[lastop].format & JOF_XMLNAME) + ? "%s.%s%s" + : "%s[%s]%s", + lval, xval, + js_incop_str[!(cs->format & JOF_INC)]); + } else { + todo = Sprint(&ss->sprinter, "%s%s", + lval, js_incop_str[!(cs->format & JOF_INC)]); + } break; case JSOP_GETPROP2: @@ -1825,10 +1850,15 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) xval = POP_STR(); op = JSOP_GETELEM; lval = POP_STR(); - if (*xval == '\0') + if (*xval == '\0') { todo = Sprint(&ss->sprinter, "%s", lval); - else - todo = Sprint(&ss->sprinter, "%s[%s]", lval, xval); + } else { + todo = Sprint(&ss->sprinter, + (js_CodeSpec[lastop].format & JOF_XMLNAME) + ? "%s.%s" + : "%s[%s]", + lval, xval); + } break; case JSOP_SETELEM: @@ -1840,7 +1870,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) if (*xval == '\0') goto do_setlval; sn = js_GetSrcNote(jp->script, pc - 1); - todo = Sprint(&ss->sprinter, "%s[%s] %s= %s", + todo = Sprint(&ss->sprinter, + (js_CodeSpec[lastop].format & JOF_XMLNAME) + ? "%s.%s %s= %s" + : "%s[%s] %s= %s", lval, xval, (sn && SN_TYPE(sn) == SRC_ASSIGNOP) ? js_CodeSpec[lastop].token @@ -2186,6 +2219,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) break; case JSOP_IMPORTPROP: + do_importprop: GET_ATOM_QUOTE_AND_FMT("\timport %s[%s]\n", "\timport %s.%s\n", rval); lval = POP_STR(); @@ -2196,6 +2230,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_IMPORTELEM: xval = POP_STR(); op = JSOP_GETELEM; + if (js_CodeSpec[lastop].format & JOF_XMLNAME) + goto do_importprop; lval = POP_STR(); js_printf(jp, "\timport %s[%s]\n", lval, xval); todo = -2; @@ -2319,6 +2355,110 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) break; #endif /* JS_HAS_DEBUGGER_KEYWORD */ +#if JS_HAS_XML_SUPPORT + case JSOP_DEFXMLNS: + rval = POP_STR(); + js_printf(jp, "\t%s %s %s %s;\n", + js_default_str, js_xml_str, js_namespace_str, rval); + todo = -2; + break; + + case JSOP_ANYNAME: + todo = SprintPut(&ss->sprinter, "*", 1); + break; + + case JSOP_QNAMEPART: + atom = GET_ATOM(cx, jp->script, pc); + goto do_name; + + case JSOP_QNAME: + rval = POP_STR(); + lval = POP_STR(); + todo = Sprint(&ss->sprinter, "%s::%s", lval, rval); + break; + + case JSOP_TOATTRNAME: + rval = POP_STR(); + todo = Sprint(&ss->sprinter, "@%s", rval); + break; + + case JSOP_TOATTRVAL: + break; + + case JSOP_ADDATTRNAME: + rval = POP_STR(); + lval = POP_STR(); + todo = Sprint(&ss->sprinter, "%s %s", lval, rval); + break; + + case JSOP_ADDATTRVAL: + rval = POP_STR(); + lval = POP_STR(); + todo = Sprint(&ss->sprinter, "%s=\"%s\"", lval, rval); + break; + + case JSOP_BINDXMLNAME: + /* Leave the name stacked and push a dummy string. */ + todo = Sprint(&ss->sprinter, ""); + break; + + case JSOP_SETXMLNAME: + /* Pop the r.h.s., the dummy string, and the name. */ + rval = POP_STR(); + (void) PopOff(ss, op); + lval = POP_STR(); + goto do_setlval; + + case JSOP_XMLNAME: + case JSOP_XMLEXPR: + case JSOP_FILTER: + case JSOP_TOXML: + case JSOP_TOXMLLIST: + /* Conversion ops do nothing in the decompiler. */ + todo = -2; + break; + + case JSOP_DESCENDANTS: + rval = POP_STR(); + lval = POP_STR(); + todo = Sprint(&ss->sprinter, "%s..%s", lval, rval); + break; + + case JSOP_XMLOBJECT: + atom = GET_ATOM(cx, jp->script, pc); + todo = Sprint(&ss->sprinter, "", + ATOM_TO_OBJECT(atom)); + break; + + case JSOP_XMLCDATA: + todo = SprintPut(&ss->sprinter, "script, pc); + if (!QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0)) + return JS_FALSE; + SprintPut(&ss->sprinter, "]]>", 3); + break; + + case JSOP_XMLCOMMENT: + todo = SprintPut(&ss->sprinter, "", 3); + break; + + case JSOP_XMLPI: + todo = SprintPut(&ss->sprinter, "script, pc); + if (!QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0)) + return JS_FALSE; + SprintPut(&ss->sprinter, " ", 1); + atom = GET_ATOM(cx, jp->script, pc + ATOM_INDEX_LEN); + if (!QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0)) + return JS_FALSE; + SprintPut(&ss->sprinter, "?>", 2); + break; +#endif /* JS_HAS_XML_SUPPORT */ + default: todo = -2; break; @@ -2657,8 +2797,11 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v, begin = pc; } else { sn = js_GetSrcNote(script, pc); - if (!sn || SN_TYPE(sn) != SRC_PCBASE) + if (!sn || SN_TYPE(sn) != SRC_PCBASE) { + if (cs->token) + return JS_NewStringCopyZ(cx, cs->token); goto do_fallback; + } begin = pc - js_GetSrcNoteOffset(sn, 0); } end = pc + cs->length; diff --git a/mozilla/js/src/jsopcode.h b/mozilla/js/src/jsopcode.h index 01a6d46ac79..7500aa74c66 100644 --- a/mozilla/js/src/jsopcode.h +++ b/mozilla/js/src/jsopcode.h @@ -70,10 +70,11 @@ typedef enum JSOp { #define JOF_LOOKUPSWITCH 5 /* lookup switch */ #define JOF_QARG 6 /* quickened get/set function argument ops */ #define JOF_QVAR 7 /* quickened get/set local variable ops */ -#define JOF_DEFLOCALVAR 8 /* define local var with initial value */ +#define JOF_INDEXCONST 8 /* arg or var index + constant pool index */ #define JOF_JUMPX 9 /* signed 32-bit jump offset immediate */ #define JOF_TABLESWITCHX 10 /* extended (32-bit offset) table switch */ #define JOF_LOOKUPSWITCHX 11 /* extended (32-bit offset) lookup switch */ +#define JOF_CONST2 12 /* 2 unsigned 16-bit constant pool indexes */ #define JOF_TYPEMASK 0x000f /* mask for above immediate types */ #define JOF_NAME 0x0010 /* name operation */ #define JOF_PROP 0x0020 /* obj.prop operation */ @@ -93,6 +94,7 @@ typedef enum JSOp { #define JOF_BACKPATCH 0x4000 /* backpatch placeholder during codegen */ #define JOF_LEFTASSOC 0x8000 /* left-associative operator */ #define JOF_DECLARING 0x10000 /* var, const, or function declaration op */ +#define JOF_XMLNAME 0x20000 /* XML name: *, a::b, @a, @a::b, etc. */ #define JOF_TYPE_IS_EXTENDED_JUMP(t) \ ((unsigned)((t) - JOF_JUMPX) <= (unsigned)(JOF_LOOKUPSWITCHX - JOF_JUMPX)) diff --git a/mozilla/js/src/jsopcode.tbl b/mozilla/js/src/jsopcode.tbl index c4996f349ec..5aa1d5f2c6a 100644 --- a/mozilla/js/src/jsopcode.tbl +++ b/mozilla/js/src/jsopcode.tbl @@ -309,7 +309,7 @@ OPDEF(JSOP_ARGCNT, 137,"argcnt", NULL, 1, 0, 1, 12, JOF_BYTE) * The local variable's slot number is the first immediate two-byte operand. * The function object's atom index is the second immediate operand. */ -OPDEF(JSOP_DEFLOCALFUN, 138,"deflocalfun",NULL, 5, 0, 0, 0, JOF_DEFLOCALVAR|JOF_DECLARING) +OPDEF(JSOP_DEFLOCALFUN, 138,"deflocalfun",NULL, 5, 0, 0, 0, JOF_INDEXCONST|JOF_DECLARING) /* Extended jumps. */ OPDEF(JSOP_GOTOX, 139,"gotox", NULL, 5, 0, 0, 0, JOF_JUMPX) @@ -340,5 +340,29 @@ OPDEF(JSOP_DECGVAR, 157,"decgvar", NULL, 3, 0, 1, 10, JOF_CONST| OPDEF(JSOP_GVARINC, 158,"gvarinc", NULL, 3, 0, 1, 10, JOF_CONST|JOF_NAME|JOF_INC|JOF_POST) OPDEF(JSOP_GVARDEC, 159,"gvardec", NULL, 3, 0, 1, 10, JOF_CONST|JOF_NAME|JOF_DEC|JOF_POST) -/* Regular expression literal requiring special fork-on-exec handling. */ +/* Regular expression literal requiring special "fork on exec" handling. */ OPDEF(JSOP_REGEXP, 160,"regexp", NULL, 3, 0, 1, 12, JOF_CONST) + +/* XML (ECMA-357, a.k.a. "E4X") support. */ +OPDEF(JSOP_DEFXMLNS, 161,"defxmlns", NULL, 1, 1, 0, 0, JOF_BYTE) +OPDEF(JSOP_ANYNAME, 162,"anyname", NULL, 1, 0, 1, 12, JOF_BYTE|JOF_XMLNAME) +OPDEF(JSOP_QNAMEPART, 163,"qnamepart", NULL, 3, 0, 1, 12, JOF_CONST|JOF_NAME) +OPDEF(JSOP_QNAME, 164,"qname", NULL, 1, 2, 1, 12, JOF_BYTE|JOF_XMLNAME) +OPDEF(JSOP_TOATTRNAME, 165,"toattrname", NULL, 1, 1, 1, 12, JOF_BYTE|JOF_XMLNAME) +OPDEF(JSOP_TOATTRVAL, 166,"toattrval", NULL, 1, 1, 1, 12, JOF_BYTE) +OPDEF(JSOP_ADDATTRNAME, 167,"addattrname",NULL, 1, 2, 1, 8, JOF_BYTE) +OPDEF(JSOP_ADDATTRVAL, 168,"addattrval", NULL, 1, 2, 1, 8, JOF_BYTE) +OPDEF(JSOP_BINDXMLPROP, 169,"bindxmlprop",NULL, 1, 2, 2, 0, JOF_BYTE) +OPDEF(JSOP_BINDXMLNAME, 170,"bindxmlname",NULL, 1, 1, 2, 0, JOF_BYTE|JOF_XMLNAME|JOF_SET|JOF_ASSIGNING) +OPDEF(JSOP_SETXMLNAME, 171,"setxmlname", NULL, 1, 3, 1, 1, JOF_BYTE|JOF_XMLNAME|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) +OPDEF(JSOP_XMLNAME, 172,"xmlname", NULL, 1, 1, 1, 12, JOF_BYTE|JOF_XMLNAME) +OPDEF(JSOP_DESCENDANTS, 173,"descendants",NULL, 1, 2, 1, 11, JOF_BYTE) +OPDEF(JSOP_FILTER, 174,"filter", NULL, 3, 1, 1, 11, JOF_JUMP) +OPDEF(JSOP_TOXML, 175,"toxml", NULL, 1, 1, 1, 12, JOF_BYTE) +OPDEF(JSOP_TOXMLLIST, 176,"toxmllist", NULL, 1, 1, 1, 12, JOF_BYTE) +OPDEF(JSOP_XMLEXPR, 177,"xmlexpr", NULL, 1, 1, 1, 12, JOF_BYTE) +OPDEF(JSOP_XMLOBJECT, 178,"xmlobject", NULL, 3, 0, 1, 12, JOF_CONST) +OPDEF(JSOP_XMLCDATA, 179,"xmlcdata", NULL, 3, 0, 1, 12, JOF_CONST) +OPDEF(JSOP_XMLCOMMENT, 180,"xmlcomment", NULL, 3, 0, 1, 12, JOF_CONST) +OPDEF(JSOP_XMLPI, 181,"xmlpi", NULL, 5, 0, 1, 12, JOF_CONST2) +OPDEF(JSOP_CALLMETHOD, 182,"callmethod", NULL, 5, -1, 1, 11, JOF_INDEXCONST) diff --git a/mozilla/js/src/jsparse.c b/mozilla/js/src/jsparse.c index 6b3f7600c5e..cd334e4c7ec 100644 --- a/mozilla/js/src/jsparse.c +++ b/mozilla/js/src/jsparse.c @@ -48,9 +48,7 @@ * compile-time expressions. Finally, it calls js_EmitTree (see jsemit.h) to * generate bytecode. * - * This parser attempts no error recovery. The dense JSTokenType enumeration - * was designed with error recovery built on 64-bit first and follow bitsets - * in mind, however. + * This parser attempts no error recovery. */ #include "jsstddef.h" #include @@ -76,11 +74,15 @@ #include "jsscript.h" #include "jsstr.h" +#if JS_HAS_XML_SUPPORT +#include "jsxml.h" +#endif + /* * JS parsers, from lowest to highest precedence. * - * Each parser takes a context and a token stream, and emits bytecode using - * a code generator. + * Each parser takes a context, a token stream, and a tree context struct. + * Each returns a parse node tree or null on error. */ typedef JSParseNode * @@ -142,17 +144,21 @@ static uint32 maxparsenodes = 0; static uint32 recyclednodes = 0; #endif -static void +static JSParseNode * RecycleTree(JSParseNode *pn, JSTreeContext *tc) { + JSParseNode *next; + if (!pn) - return; + return NULL; JS_ASSERT(pn != tc->nodeList); /* catch back-to-back dup recycles */ + next = pn->pn_next; pn->pn_next = tc->nodeList; tc->nodeList = pn; #ifdef METER_PARSENODES recyclednodes++; #endif + return next; } static JSParseNode * @@ -202,6 +208,13 @@ NewOrRecycledNode(JSContext *cx, JSTreeContext *tc) break; } } +#ifdef METER_PARSENODES + if (pn) { + parsenodes++; + if (parsenodes - recyclednodes > maxparsenodes) + maxparsenodes = parsenodes - recyclednodes; + } +#endif return pn; } @@ -209,7 +222,7 @@ NewOrRecycledNode(JSContext *cx, JSTreeContext *tc) * Allocate a JSParseNode from cx's temporary arena. */ static JSParseNode * -NewParseNode(JSContext *cx, JSToken *tok, JSParseNodeArity arity, +NewParseNode(JSContext *cx, JSToken *tp, JSParseNodeArity arity, JSTreeContext *tc) { JSParseNode *pn; @@ -217,16 +230,11 @@ NewParseNode(JSContext *cx, JSToken *tok, JSParseNodeArity arity, pn = NewOrRecycledNode(cx, tc); if (!pn) return NULL; - pn->pn_type = tok->type; - pn->pn_pos = tok->pos; + pn->pn_type = tp->type; + pn->pn_pos = tp->pos; pn->pn_op = JSOP_NOP; pn->pn_arity = arity; pn->pn_next = NULL; -#ifdef METER_PARSENODES - parsenodes++; - if (parsenodes - recyclednodes > maxparsenodes) - maxparsenodes = parsenodes - recyclednodes; -#endif return pn; } @@ -252,7 +260,6 @@ NewBinary(JSContext *cx, JSTokenType tt, left->pn_arity = PN_LIST; PN_INIT_LIST_1(left, pn1); PN_APPEND(left, pn2); - left->pn_extra = 0; if (tt == TOK_PLUS) { if (pn1->pn_type == TOK_STRING) left->pn_extra |= PNX_STRCAT; @@ -302,11 +309,6 @@ NewBinary(JSContext *cx, JSTokenType tt, pn->pn_left = left; pn->pn_right = right; pn->pn_next = NULL; -#ifdef METER_PARSENODES - parsenodes++; - if (parsenodes - recyclednodes > maxparsenodes) - maxparsenodes = parsenodes - recyclednodes; -#endif return pn; } @@ -800,12 +802,14 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, */ JS_ASSERT(OBJ_GET_CLASS(cx, varobj) == &js_FunctionClass); JS_ASSERT(fp->fun == (JSFunction *) JS_GetPrivate(cx, varobj)); - if (!js_LookupProperty(cx, varobj, (jsid)funAtom, &pobj, &prop)) + if (!js_LookupProperty(cx, varobj, ATOM_TO_JSID(funAtom), + &pobj, &prop)) { return NULL; + } if (prop) OBJ_DROP_PROPERTY(cx, pobj, prop); if (!prop || pobj != varobj) { - if (!js_DefineNativeProperty(cx, varobj, (jsid)funAtom, + if (!js_DefineNativeProperty(cx, varobj, ATOM_TO_JSID(funAtom), JSVAL_VOID, js_GetLocalVariable, js_SetLocalVariable, @@ -836,8 +840,8 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, MUST_MATCH_TOKEN(TOK_NAME, JSMSG_MISSING_FORMAL); argAtom = CURRENT_TOKEN(ts).t_atom; pobj = NULL; - if (!js_LookupProperty(cx, fun->object, (jsid)argAtom, &pobj, - &prop)) { + if (!js_LookupProperty(cx, fun->object, ATOM_TO_JSID(argAtom), + &pobj, &prop)) { return NULL; } dupflag = 0; @@ -867,7 +871,7 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, return NULL; prop = NULL; } - if (!js_AddNativeProperty(cx, fun->object, (jsid)argAtom, + if (!js_AddNativeProperty(cx, fun->object, ATOM_TO_JSID(argAtom), js_GetArgument, js_SetArgument, SPROP_INVALID_SLOT, JSPROP_ENUMERATE | JSPROP_PERMANENT | @@ -1916,6 +1920,30 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) break; #endif /* JS_HAS_DEBUGGER_KEYWORD */ +#if JS_HAS_XML_SUPPORT + case TOK_DEFAULT: + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + if (!pn) + return NULL; + if (!js_MatchToken(cx, ts, TOK_NAME) || + CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.xmlAtom || + !js_MatchToken(cx, ts, TOK_NAME) || + CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.namespaceAtom || + !js_MatchToken(cx, ts, TOK_ASSIGN) || + CURRENT_TOKEN(ts).t_op != JSOP_NOP) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_BAD_DEFAULT_XML_NAMESPACE); + return NULL; + } + pn2 = Expr(cx, ts, tc); + if (!pn2) + return NULL; + pn->pn_op = JSOP_DEFXMLNS; + pn->pn_pos.end = pn2->pn_pos.end; + pn->pn_kid = pn2; + break; +#endif + case TOK_ERROR: return NULL; @@ -2010,7 +2038,6 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) if (!pn) return NULL; pn->pn_op = CURRENT_TOKEN(ts).t_op; - pn->pn_extra = 0; /* assume no JSOP_POP needed */ PN_INIT_LIST(pn); /* @@ -2100,7 +2127,7 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) if (!fun) { prop = NULL; /* don't lookup global variables at compile time */ } else { - if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, &prop)) + if (!OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) return NULL; } if (prop && pobj == obj && OBJ_IS_NATIVE(pobj)) { @@ -2182,7 +2209,7 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) atom != cx->runtime->atomState.argumentsAtom && fp->scopeChain == obj && !js_InWithStatement(tc)) { - if (!js_AddNativeProperty(cx, obj, (jsid)atom, + if (!js_AddNativeProperty(cx, obj, ATOM_TO_JSID(atom), currentGetter, currentSetter, SPROP_INVALID_SLOT, pn2->pn_attrs | JSPROP_SHARED, @@ -2289,8 +2316,19 @@ AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) break; #if JS_HAS_LVALUE_RETURN case TOK_LP: - pn2->pn_op = JSOP_SETCALL; - break; + if (pn2->pn_op == JSOP_CALL) { + pn2->pn_op = JSOP_SETCALL; + break; + } + /* FALL THROUGH */ +#endif +#if JS_HAS_XML_SUPPORT + case TOK_UNARYOP: + if (pn2->pn_op == JSOP_XMLNAME) { + pn2->pn_op = JSOP_SETXMLNAME; + break; + } + /* FALL THROUGH */ #endif default: js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, @@ -2519,6 +2557,9 @@ SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid, kid->pn_type != TOK_DOT && #if JS_HAS_LVALUE_RETURN (kid->pn_type != TOK_LP || kid->pn_op != JSOP_CALL) && +#endif +#if JS_HAS_XML_SUPPORT + (kid->pn_type != TOK_UNARYOP || kid->pn_op != JSOP_XMLNAME) && #endif kid->pn_type != TOK_LB) { js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, @@ -2558,7 +2599,15 @@ SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, #if JS_HAS_LVALUE_RETURN case TOK_LP: + JS_ASSERT(kid->pn_op == JSOP_CALL); kid->pn_op = JSOP_SETCALL; + /* FALL THROUGH */ +#endif +#if JS_HAS_XML_SUPPORT + case TOK_UNARYOP: + if (kid->pn_op == JSOP_XMLNAME) + kid->pn_op = JSOP_SETXMLNAME; + /* FALL THROUGH */ #endif case TOK_LB: op = (tt == TOK_INC) @@ -2723,6 +2772,19 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, pn = PrimaryExpr(cx, ts, tc); if (!pn) return NULL; + + if (pn->pn_type == TOK_AT || pn->pn_type == TOK_DBLCOLON) { + pn2 = NewOrRecycledNode(cx, tc); + if (!pn2) + return NULL; + pn2->pn_type = TOK_UNARYOP; + pn2->pn_pos = pn->pn_pos; + pn2->pn_op = JSOP_XMLNAME; + pn2->pn_arity = PN_UNARY; + pn2->pn_kid = pn; + pn2->pn_next = NULL; + pn = pn2; + } } while ((tt = js_GetToken(cx, ts)) > TOK_EOF) { @@ -2730,12 +2792,64 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc); if (!pn2) return NULL; +#if JS_HAS_XML_SUPPORT + pn3 = PrimaryExpr(cx, ts, tc); + if (!pn3) + return NULL; + tt = pn3->pn_type; + if (tt == TOK_NAME) { + pn2->pn_op = JSOP_GETPROP; + pn2->pn_expr = pn; + pn2->pn_atom = pn3->pn_atom; + RecycleTree(pn3, tc); + } else { + if (TOKEN_TYPE_IS_XML(tt)) { + pn2->pn_type = TOK_LB; + pn2->pn_op = JSOP_GETELEM; + } else if (tt == TOK_RP) { + pn2->pn_type = TOK_FILTER; + pn2->pn_op = JSOP_FILTER; + } else { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_NAME_AFTER_DOT); + return NULL; + } + pn2->pn_arity = PN_BINARY; + pn2->pn_left = pn; + pn2->pn_right = pn3; + } +#else MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT); - pn2->pn_pos.begin = pn->pn_pos.begin; - pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; pn2->pn_op = JSOP_GETPROP; pn2->pn_expr = pn; pn2->pn_atom = CURRENT_TOKEN(ts).t_atom; +#endif + pn2->pn_pos.begin = pn->pn_pos.begin; + pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; +#if JS_HAS_XML_SUPPORT + } else if (tt == TOK_DBLDOT) { + pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc); + if (!pn2) + return NULL; + pn3 = PrimaryExpr(cx, ts, tc); + if (!pn3) + return NULL; + tt = pn3->pn_type; + if (tt == TOK_NAME) { + pn3->pn_type = TOK_STRING; + pn3->pn_arity = PN_NULLARY; + pn3->pn_op = JSOP_STRING; + } else if (!TOKEN_TYPE_IS_XML(tt)) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_NAME_AFTER_DOT); + return NULL; + } + pn2->pn_op = JSOP_DESCENDANTS; + pn2->pn_left = pn; + pn2->pn_right = pn3; + pn2->pn_pos.begin = pn->pn_pos.begin; + pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; +#endif } else if (tt == TOK_LB) { pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc); if (!pn2) @@ -2796,6 +2910,647 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, return pn; } +static JSParseNode * +BracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + uintN oldflags; + JSParseNode *pn; + +#if JS_HAS_IN_OPERATOR + /* + * Always accept the 'in' operator in a parenthesized expression, + * where it's unambiguous, even if we might be parsing the init of a + * for statement. + */ + oldflags = tc->flags; + tc->flags &= ~TCF_IN_FOR_INIT; +#endif + pn = Expr(cx, ts, tc); +#if JS_HAS_IN_OPERATOR + tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS); +#endif + return pn; +} + +#if JS_HAS_XML_SUPPORT + +static JSParseNode * +EndBracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn; + + pn = BracketedExpr(cx, ts, tc); + if (!pn) + return NULL; + + MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ATTR_EXPR); + return pn; +} + +/* + * From the ECMA-357 grammar in 11.1.1 and 11.1.2: + * + * AttributeIdentifier: + * @ PropertySelector + * @ QualifiedIdentifier + * @ [ Expression ] + * + * PropertySelector: + * Identifier + * * + * + * QualifiedIdentifier: + * PropertySelector :: PropertySelector + * PropertySelector :: [ Expression ] + * + * We adapt AttributeIdentifier and QualifiedIdentier to be LL(1), like so: + * + * AttributeIdentifier: + * @ QualifiedIdentifier + * @ [ Expression ] + * + * PropertySelector: + * Identifier + * * + * + * QualifiedIdentifier: + * PropertySelector :: PropertySelector + * PropertySelector :: [ Expression ] + * PropertySelector + * + * Since PrimaryExpression: Identifier in ECMA-262 and we want the semantics + * for that rule to result in a name node, but extend the grammar to include + * PrimaryExpression: QualifiedIdentifier, we factor further: + * + * QualifiedIdentifier: + * PropertySelector QualifiedSuffix + * + * QualifiedSuffix: + * :: PropertySelector + * :: [ Expression ] + * /nothing/ + * + * And use this production instead of PrimaryExpression: QualifiedIdentifier: + * + * PrimaryExpression: + * Identifier QualifiedSuffix + * + * We hoists the :: match into callers of QualifiedSuffix, in order to tweak + * PropertySelector vs. Identifier pn_arity, pn_op, and other members. + */ +static JSParseNode * +PropertySelector(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn; + + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); + if (!pn) + return NULL; + if (pn->pn_type == TOK_STAR) { + pn->pn_type = TOK_ANYNAME; + pn->pn_op = JSOP_ANYNAME; + pn->pn_atom = cx->runtime->atomState.starAtom; + } else { + pn->pn_op = JSOP_QNAMEPART; + pn->pn_atom = CURRENT_TOKEN(ts).t_atom; + pn->pn_arity = PN_NAME; + pn->pn_expr = NULL; + pn->pn_slot = -1; + pn->pn_attrs = 0; + } + return pn; +} + +static JSParseNode * +QualifiedSuffix(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, + JSTreeContext *tc) +{ + JSParseNode *pn2, *pn3; + JSTokenType tt; + + JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_DBLCOLON); + pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc); + if (!pn2) + return NULL; + pn2->pn_op = JSOP_QNAME; + pn2->pn_left = pn; + tt = js_GetToken(cx, ts); + if (tt == TOK_STAR || tt == TOK_NAME) { + pn3 = PropertySelector(cx, ts, tc); + } else if (TOK_LB) { + pn3 = EndBracketedExpr(cx, ts, tc); + } else { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_SYNTAX_ERROR); + return NULL; + } + if (!pn3) + return NULL; + pn2->pn_right = pn3; + return pn2; +} + +static JSParseNode * +QualifiedIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn; + + pn = PropertySelector(cx, ts, tc); + if (!pn) + return NULL; + if (js_MatchToken(cx, ts, TOK_DBLCOLON)) + pn = QualifiedSuffix(cx, ts, pn, tc); + return pn; +} + +static JSParseNode * +AttributeIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn, *pn2; + JSTokenType tt; + + JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_AT); + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + if (!pn) + return NULL; + pn->pn_op = JSOP_TOATTRNAME; + tt = js_GetToken(cx, ts); + if (tt == TOK_STAR || tt == TOK_NAME) { + pn2 = QualifiedIdentifier(cx, ts, tc); + } else if (tt == TOK_LB) { + pn2 = EndBracketedExpr(cx, ts, tc); + } else { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_SYNTAX_ERROR); + return NULL; + } + if (!pn2) + return NULL; + pn->pn_kid = pn2; + return pn; +} + +/* + * Make a TOK_LC unary node whose pn_kid is an expression. + */ +static JSParseNode * +XMLExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn, *pn2; + uintN oldflags; + + JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LC); + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); + if (!pn) + return NULL; + + /* + * Turn off XML tag mode, but don't restore it after parsing this braced + * expression. Instead, simply restore ts's old flags. This is required + * because XMLExpr is called both from within a tag, and from within text + * contained in an element, but outside of any start, end, or point tag. + */ + oldflags = ts->flags; + ts->flags = oldflags & ~TSF_XMLTAGMODE; + pn2 = Expr(cx, ts, tc); + if (!pn2) + return NULL; + + MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_XML_EXPR); + ts->flags = oldflags; + pn->pn_kid = pn2; + return pn; +} + +/* + * Make a terminal node for oneof TOK_XMLNAME, TOK_XMLATTR, TOK_XMLTEXT, + * TOK_XMLCDATA, TOK_XMLCOMMENT, or TOK_XMLPI. + */ +static JSParseNode * +XMLAtomNode(JSContext *cx, JSToken *tp, JSTreeContext *tc) +{ + JSParseNode *pn; + + pn = NewParseNode(cx, tp, PN_NULLARY, tc); + if (!pn) + return JS_FALSE; + pn->pn_op = tp->t_op; + pn->pn_atom = tp->t_atom; + return pn; +} + +/* + * Parse the productions: + * + * XMLNameExpr: + * XMLName XMLNameExpr? + * { Expr } XMLNameExpr? + * + * Return a PN_LIST, PN_UNARY, or PN_NULLARY according as XMLNameExpr produces + * a list of names and/or expressions, a single expression, or a single name. + * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME; if PN_UNARY, pn_type + * will be TOK_LC. + */ +static JSParseNode * +XMLNameExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) +{ + JSParseNode *pn, *pn2, *list; + JSToken *tp; + JSTokenType tt; + + pn = list = NULL; + do { + tp = &CURRENT_TOKEN(ts); + tt = tp->type; + if (tt == TOK_LC) { + pn2 = XMLExpr(cx, ts, tc); + if (!pn2) + return NULL; + } else { + JS_ASSERT(tt == TOK_XMLNAME); + pn2 = XMLAtomNode(cx, tp, tc); + if (!pn2) + return NULL; + } + + if (!pn) { + pn = pn2; + } else { + if (!list) { + list = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + if (!list) + return NULL; + list->pn_type = TOK_XMLNAME; + list->pn_pos.begin = pn->pn_pos.begin; + PN_INIT_LIST_1(list, pn); + list->pn_extra = PNX_CANTFOLD; + pn = list; + } + pn->pn_pos.end = pn2->pn_pos.end; + PN_APPEND(pn, pn2); + } + } while ((tt = js_GetToken(cx, ts)) == TOK_XMLNAME || tt == TOK_LC); + + js_UngetToken(ts); + return pn; +} + +/* + * Macro to test whether an XMLNameExpr or XMLTagContent node can be folded + * at compile time into a JSXML tree. + */ +#define XML_FOLDABLE(pn) ((pn)->pn_arity == PN_LIST \ + ? ((pn)->pn_extra & PNX_CANTFOLD) == 0 \ + : (pn)->pn_type != TOK_LC) + +/* + * Parse the productions: + * + * XMLTagContent: + * XMLNameExpr + * XMLTagContent S XMLNameExpr S? = S? XMLAttr + * XMLTagContent S XMLNameExpr S? = S? { Expr } + * + * Return a PN_LIST, PN_UNARY, or PN_NULLARY according to how XMLTagContent + * produces a list of name and attribute values and/or braced expressions, a + * single expression, or a single name. + * + * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME for the case where + * XMLTagContent: XMLNameExpr. If pn_type is not TOK_XMLNAME but pn_arity is + * PN_LIST, pn_type will be tagtype. If PN_UNARY, pn_type will be TOK_LC and + * we parsed exactly one expression. + */ +static JSParseNode * +XMLTagContent(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, + JSTokenType tagtype, JSAtom **namep) +{ + JSParseNode *pn, *pn2, *list; + JSTokenType tt; + + pn = XMLNameExpr(cx, ts, tc); + if (!pn) + return NULL; + *namep = (pn->pn_arity == PN_NULLARY) ? pn->pn_atom : NULL; + list = NULL; + + while (js_MatchToken(cx, ts, TOK_XMLSPACE)) { + tt = js_GetToken(cx, ts); + if (tt != TOK_XMLNAME && tt != TOK_LC) { + js_UngetToken(ts); + break; + } + + pn2 = XMLNameExpr(cx, ts, tc); + if (!pn2) + return NULL; + if (!list) { + list = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + if (!list) + return NULL; + list->pn_type = tagtype; + list->pn_pos.begin = pn->pn_pos.begin; + PN_INIT_LIST_1(list, pn); + pn = list; + } + PN_APPEND(pn, pn2); + if (!XML_FOLDABLE(pn2)) + pn->pn_extra |= PNX_CANTFOLD; + + js_MatchToken(cx, ts, TOK_XMLSPACE); + MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_NO_ASSIGN_IN_XML_ATTR); + js_MatchToken(cx, ts, TOK_XMLSPACE); + + tt = js_GetToken(cx, ts); + if (tt == TOK_XMLATTR) { + pn2 = XMLAtomNode(cx, &CURRENT_TOKEN(ts), tc); + } else if (tt == TOK_LC) { + pn2 = XMLExpr(cx, ts, tc); + pn->pn_extra |= PNX_CANTFOLD; + } else { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_BAD_XML_ATTR_VALUE); + return NULL; + } + if (!pn2) + return NULL; + pn->pn_pos.end = pn2->pn_pos.end; + PN_APPEND(pn, pn2); + } + + return pn; +} + +#define XML_CHECK_FOR_ERROR_AND_EOF(tt,result) \ + JS_BEGIN_MACRO \ + if ((tt) <= TOK_EOF) { \ + if ((tt) == TOK_EOF) { \ + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, \ + JSMSG_END_OF_XML_SOURCE); \ + } \ + return result; \ + } \ + JS_END_MACRO + +static JSParseNode * +XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, + JSBool allowList); + +/* + * Consume XML element tag content, including the TOK_XMLETAGO (flags &= ~TSF_XMLTAGMODE; + for (;;) { + ts->flags |= TSF_XMLTEXTMODE; + tt = js_GetToken(cx, ts); + ts->flags &= ~TSF_XMLTEXTMODE; + XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE); + + JS_ASSERT(tt == TOK_XMLTEXT); + textAtom = CURRENT_TOKEN(ts).t_atom; + if (textAtom) { + /* Non-zero-length XML text scanned. */ + pn2 = XMLAtomNode(cx, &CURRENT_TOKEN(ts), tc); + if (!pn2) + return JS_FALSE; + pn->pn_pos.end = pn2->pn_pos.end; + PN_APPEND(pn, pn2); + } + + ts->flags |= TSF_OPERAND; + tt = js_GetToken(cx, ts); + ts->flags &= ~TSF_OPERAND; + XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE); + if (tt == TOK_XMLETAGO) + break; + + if (tt == TOK_LC) { + pn2 = XMLExpr(cx, ts, tc); + pn->pn_extra |= PNX_CANTFOLD; + } else if (tt == TOK_XMLSTAGO) { + pn2 = XMLElementOrList(cx, ts, tc, JS_FALSE); + if (pn2) { + pn2->pn_extra &= ~PNX_XMLROOT; + pn->pn_extra |= pn2->pn_extra; + } + } else { + JS_ASSERT(tt == TOK_XMLCDATA || tt == TOK_XMLCOMMENT || + tt == TOK_XMLPI); + pn2 = XMLAtomNode(cx, &CURRENT_TOKEN(ts), tc); + } + if (!pn2) + return JS_FALSE; + pn->pn_pos.end = pn2->pn_pos.end; + PN_APPEND(pn, pn2); + } + + JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLETAGO); + ts->flags |= TSF_XMLTAGMODE; + return JS_TRUE; +} + +/* + * Return a PN_LIST node containing an XML or XMLList Initialiser. + */ +static JSParseNode * +XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, + JSBool allowList) +{ + JSParseNode *pn, *pn2, *list; + JSBool hadSpace; + JSTokenType tt; + JSAtom *startAtom, *endAtom; + + JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLSTAGO); + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + if (!pn) + return NULL; + + ts->flags |= TSF_XMLTAGMODE; + hadSpace = js_MatchToken(cx, ts, TOK_XMLSPACE); + tt = js_GetToken(cx, ts); + if (tt == TOK_ERROR) + return NULL; + + if (tt == TOK_XMLNAME || tt == TOK_LC) { + /* + * XMLElement. Append the tag and its contents, if any, to pn. + */ + pn2 = XMLTagContent(cx, ts, tc, TOK_XMLSTAGO, &startAtom); + if (!pn2) + return NULL; + js_MatchToken(cx, ts, TOK_XMLSPACE); + + tt = js_GetToken(cx, ts); + if (tt == TOK_XMLPTAGC) { + /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */ + if (pn2->pn_type == TOK_XMLSTAGO) { + PN_INIT_LIST(pn); + RecycleTree(pn, tc); + pn = pn2; + } else { + JS_ASSERT(pn2->pn_type == TOK_XMLNAME); + PN_INIT_LIST_1(pn, pn2); + if (!XML_FOLDABLE(pn2)) + pn->pn_extra |= PNX_CANTFOLD; + } + pn->pn_type = TOK_XMLPTAGC; + pn->pn_extra |= PNX_XMLROOT; + } else { + /* We had better have a tag-close (>) at this point. */ + if (tt != TOK_XMLTAGC) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_BAD_XML_TAG_SYNTAX); + return NULL; + } + + /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */ + if (pn2->pn_type != TOK_XMLSTAGO) { + PN_INIT_LIST_1(pn, pn2); + if (!XML_FOLDABLE(pn2)) + pn->pn_extra |= PNX_CANTFOLD; + pn2 = pn; + pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + if (!pn) + return NULL; + } + + /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */ + pn->pn_type = TOK_XMLELEM; + PN_INIT_LIST_1(pn, pn2); + if (!XML_FOLDABLE(pn2)) + pn->pn_extra |= PNX_CANTFOLD; + pn->pn_extra |= PNX_XMLROOT; + + /* Get element contents and delimiting end-tag-open sequence. */ + if (!XMLElementContent(cx, ts, pn, tc)) + return NULL; + + js_MatchToken(cx, ts, TOK_XMLSPACE); + tt = js_GetToken(cx, ts); + XML_CHECK_FOR_ERROR_AND_EOF(tt, NULL); + if (tt != TOK_XMLNAME && tt != TOK_LC) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_BAD_XML_TAG_SYNTAX); + return NULL; + } + + /* Parse end tag; check mismatch at compile-time if we can. */ + pn2 = XMLTagContent(cx, ts, tc, TOK_XMLETAGO, &endAtom); + if (!pn2) + return NULL; + if (pn2->pn_type == TOK_XMLETAGO) { + /* Oops, end tag has attributes! */ + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_BAD_XML_TAG_SYNTAX); + return NULL; + } + if (endAtom && startAtom && endAtom != startAtom) { + /* End vs. start tag name mismatch. */ + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_XML_TAG_NAME_MISMATCH); + return NULL; + } + + /* Make a TOK_XMLETAGO list with pn2 as its single child. */ + JS_ASSERT(pn2->pn_type == TOK_XMLNAME || pn2->pn_type == TOK_LC); + list = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc); + if (!list) + return NULL; + list->pn_type = TOK_XMLETAGO; + PN_INIT_LIST_1(list, pn2); + PN_APPEND(pn, list); + if (!XML_FOLDABLE(pn2)) { + list->pn_extra |= PNX_CANTFOLD; + pn->pn_extra |= PNX_CANTFOLD; + } + + js_MatchToken(cx, ts, TOK_XMLSPACE); + MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_TAG_SYNTAX); + } + + /* Set pn_op now that pn has been updated to its final value. */ + pn->pn_op = JSOP_TOXML; + } else if (!hadSpace && allowList && tt == TOK_XMLTAGC) { + /* XMLList Initialiser. */ + pn->pn_type = TOK_XMLLIST; + pn->pn_op = JSOP_TOXMLLIST; + PN_INIT_LIST(pn); + pn->pn_extra |= PNX_XMLROOT; + if (!XMLElementContent(cx, ts, pn, tc)) + return NULL; + + MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_LIST_SYNTAX); + } else { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_BAD_XML_NAME_SYNTAX); + return NULL; + } + + pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; + ts->flags &= ~TSF_XMLTAGMODE; + return pn; +} + +JS_FRIEND_API(JSParseNode *) +js_ParseXMLTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, + JSBool allowList) +{ + JSStackFrame *fp, frame; + JSParseNode *pn; + JSTreeContext tc; + JSTokenType tt; + + /* + * Push a compiler frame if we have no frames, or if the top frame is a + * lightweight function activation, or if its scope chain doesn't match + * the one passed to us. + */ + fp = cx->fp; + if (!fp || !fp->varobj || fp->scopeChain != chain) { + memset(&frame, 0, sizeof frame); + frame.varobj = frame.scopeChain = chain; + if (cx->options & JSOPTION_VAROBJFIX) { + while ((chain = JS_GetParent(cx, chain)) != NULL) + frame.varobj = chain; + } + frame.down = fp; + cx->fp = &frame; + } + + JS_KEEP_ATOMS(cx->runtime); + TREE_CONTEXT_INIT(&tc); + + /* Set XML-only mode to turn off special treatment of {expr} in XML. */ + ts->flags |= TSF_OPERAND | TSF_XMLONLYMODE; + tt = js_GetToken(cx, ts); + ts->flags &= ~TSF_OPERAND; + + if (tt != TOK_XMLSTAGO) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_BAD_XML_MARKUP); + pn = NULL; + } else { + pn = XMLElementOrList(cx, ts, &tc, allowList); + } + + ts->flags &= ~TSF_XMLONLYMODE; + TREE_CONTEXT_FINISH(&tc); + JS_UNKEEP_ATOMS(cx->runtime); + cx->fp = fp; + return pn; +} + +#endif /* JS_HAS_XMLSUPPORT */ + static JSParseNode * PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { @@ -2854,7 +3609,6 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) if (!pn) return NULL; pn->pn_type = TOK_RB; - pn->pn_extra = 0; #if JS_HAS_SHARP_VARS if (defsharp) { @@ -3025,26 +3779,10 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) #endif /* JS_HAS_INITIALIZERS */ case TOK_LP: - { -#if JS_HAS_IN_OPERATOR - uintN oldflags; -#endif pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc); if (!pn) return NULL; -#if JS_HAS_IN_OPERATOR - /* - * Always accept the 'in' operator in a parenthesized expression, - * where it's unambiguous, even if we might be parsing the init of a - * for statement. - */ - oldflags = tc->flags; - tc->flags &= ~TCF_IN_FOR_INIT; -#endif - pn2 = Expr(cx, ts, tc); -#if JS_HAS_IN_OPERATOR - tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS); -#endif + pn2 = BracketedExpr(cx, ts, tc); if (!pn2) return NULL; @@ -3053,26 +3791,69 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; pn->pn_kid = pn2; break; - } + +#if JS_HAS_XML_SUPPORT + case TOK_STAR: + pn = QualifiedIdentifier(cx, ts, tc); + if (!pn) + return NULL; + notsharp = JS_TRUE; + break; + + case TOK_AT: + pn = AttributeIdentifier(cx, ts, tc); + if (!pn) + return NULL; + notsharp = JS_TRUE; + break; + + case TOK_XMLSTAGO: + pn = XMLElementOrList(cx, ts, tc, JS_TRUE); + if (!pn) + return NULL; + notsharp = JS_TRUE; /* XXXbe could be sharp? */ + break; +#endif /* JS_HAS_XML_SUPPORT */ case TOK_STRING: #if JS_HAS_SHARP_VARS notsharp = JS_TRUE; -#endif /* FALL THROUGH */ +#endif + +#if JS_HAS_XML_SUPPORT + case TOK_XMLCDATA: + case TOK_XMLCOMMENT: + case TOK_XMLPI: +#endif case TOK_NAME: case TOK_OBJECT: pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc); if (!pn) return NULL; - pn->pn_op = CURRENT_TOKEN(ts).t_op; pn->pn_atom = CURRENT_TOKEN(ts).t_atom; +#if JS_HAS_XML_SUPPORT + if (tt == TOK_XMLPI) + pn->pn_atom2 = CURRENT_TOKEN(ts).t_atom2; + else +#endif + pn->pn_op = CURRENT_TOKEN(ts).t_op; if (tt == TOK_NAME) { pn->pn_arity = PN_NAME; pn->pn_expr = NULL; pn->pn_slot = -1; pn->pn_attrs = 0; +#if JS_HAS_XML_SUPPORT + if (js_MatchToken(cx, ts, TOK_DBLCOLON)) { + pn->pn_op = JSOP_QNAMEPART; + pn = QualifiedSuffix(cx, ts, pn, tc); + if (!pn) + return NULL; + break; + } +#endif + /* Unqualified __parent__ and __proto__ uses require activations. */ if (pn->pn_atom == cx->runtime->atomState.parentAtom || pn->pn_atom == cx->runtime->atomState.protoAtom) { @@ -3329,6 +4110,104 @@ FoldBinaryNumeric(JSContext *cx, JSOp op, JSParseNode *pn1, JSParseNode *pn2, return JS_TRUE; } +#if JS_HAS_XML_SUPPORT + +static JSBool +FoldXMLConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) +{ + JSTokenType tt; + JSParseNode **pnp, *pn1, *pn2; + JSString *accum, *str; + uint32 i; + + tt = pn->pn_type; + pnp = &pn->pn_head; + pn1 = *pnp; + if (tt == TOK_XMLELEM || tt == TOK_XMLLIST) { + accum = NULL; + } else { + accum = ATOM_TO_STRING((pn->pn_type == TOK_XMLETAGO) + ? cx->runtime->atomState.etagoAtom + : cx->runtime->atomState.stagoAtom); + } + + for (pn2 = pn1, i = 0; pn2; pn2 = pn2->pn_next, i++) { + /* The parser already rejected end-tags with attributes. */ + JS_ASSERT(tt != TOK_XMLETAGO || i == 0); + + switch (pn2->pn_type) { + case TOK_XMLNAME: + case TOK_XMLATTR: + case TOK_XMLTEXT: + case TOK_XMLCDATA: + case TOK_XMLCOMMENT: + case TOK_STRING: + str = ATOM_TO_STRING(pn2->pn_atom); + break; + + default: + JS_ASSERT(*pnp == pn1); + if (accum) { + while (pn1 != pn2) { + pn1 = RecycleTree(pn1, tc); + --pn->pn_count; + } + pn1->pn_type = TOK_XMLTEXT; + pn1->pn_op = JSOP_STRING; + pn1->pn_atom = js_AtomizeString(cx, accum, 0); + if (!pn1->pn_atom) + return JS_FALSE; + *pnp = pn1; + } + pnp = &pn2->pn_next; + pn1 = *pnp; + accum = NULL; + continue; + } + + if (accum) { + str = ((tt == TOK_XMLPTAGC || tt == TOK_XMLSTAGO) && i != 0) + ? js_AddAttributePart(cx, i & 1, accum, str) + : js_ConcatStrings(cx, accum, str); + if (!str) + return JS_FALSE; + } + accum = str; + } + + if (accum) { + if (tt == TOK_XMLPTAGC) + str = ATOM_TO_STRING(cx->runtime->atomState.ptagcAtom); + else if (tt == TOK_XMLSTAGO || tt == TOK_XMLETAGO) + str = ATOM_TO_STRING(cx->runtime->atomState.tagcAtom); + else + str = NULL; + if (str) { + accum = js_ConcatStrings(cx, accum, str); + if (!accum) + return JS_FALSE; + } + + JS_ASSERT(*pnp == pn1); + while (pn1->pn_next) { + pn1 = RecycleTree(pn1, tc); + --pn->pn_count; + } + pn1->pn_type = TOK_XMLTEXT; + pn1->pn_op = JSOP_STRING; + pn1->pn_atom = js_AtomizeString(cx, accum, 0); + if (!pn1->pn_atom) + return JS_FALSE; + *pnp = pn1; + } + + if (pn->pn_count == 1) + PN_MOVE_NODE(pn, pn1); + return JS_TRUE; +} + +#endif /* JS_HAS_XML_SUPPORT */ + JSBool js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) { @@ -3347,6 +4226,47 @@ js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) break; case PN_LIST: +#if JS_HAS_XML_SUPPORT + switch (pn->pn_type) { + case TOK_XMLELEM: + case TOK_XMLLIST: + case TOK_XMLPTAGC: + /* + * Try to fold this XML parse tree once, from the top down, into + * a JSXML tree with just one object wrapping the tree root. + */ + if ((pn->pn_extra & PNX_CANTFOLD) == 0) { + JSXML *xml; + JSObject *obj; + JSAtom *atom; + + xml = js_ParseNodeToXML(cx, pn, XML_COPY_ON_WRITE); + if (!xml) + return JS_FALSE; + obj = js_GetXMLObject(cx, xml); + if (!obj) { + js_DestroyXML(cx, xml); + return JS_FALSE; + } + atom = js_AtomizeObject(cx, obj, 0); + if (!atom) + return JS_FALSE; + pn->pn_op = JSOP_XMLOBJECT; + pn->pn_arity = PN_NULLARY; + pn->pn_atom = atom; + return JS_TRUE; + } + + /* + * Can't fold from parse node to XML tree -- try folding strings + * as much as possible. + */ + break; + + default:; + } +#endif + /* Save the list head in pn1 for later use. */ for (pn1 = pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { if (!js_FoldConstants(cx, pn2, tc)) @@ -3485,13 +4405,11 @@ js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) } /* Fill the buffer, advancing chars and recycling kids as we go. */ - for (pn2 = pn1; pn2; pn2 = pn3) { + for (pn2 = pn1; pn2; pn2 = RecycleTree(pn2, tc)) { str2 = ATOM_TO_STRING(pn2->pn_atom); length2 = str2->length; js_strncpy(chars, str2->chars, length2); chars += length2; - pn3 = pn2->pn_next; - RecycleTree(pn2, tc); } *chars = 0; @@ -3628,6 +4546,25 @@ js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) } break; +#if JS_HAS_XML_SUPPORT + case TOK_XMLELEM: + case TOK_XMLLIST: + case TOK_XMLPTAGC: + case TOK_XMLSTAGO: + case TOK_XMLETAGO: + if (!FoldXMLConstants(cx, pn, tc)) + return JS_FALSE; + break; + + case TOK_XMLNAME: + if (pn->pn_arity == PN_LIST) { + JS_ASSERT(pn->pn_count != 0); + if (!FoldXMLConstants(cx, pn, tc)) + return JS_FALSE; + } + break; +#endif + default:; } diff --git a/mozilla/js/src/jsparse.h b/mozilla/js/src/jsparse.h index 9ce12b3916c..56f47fcc203 100644 --- a/mozilla/js/src/jsparse.h +++ b/mozilla/js/src/jsparse.h @@ -155,8 +155,8 @@ JS_BEGIN_EXTERN_C * pn_count: 1 + N (where N is number of args) * ctor is a MEMBER expr * TOK_DELETE unary pn_kid: MEMBER expr - * TOK_DOT name pn_expr: MEMBER expr to left of . - * pn_atom: name to right of . + * TOK_DOT, name pn_expr: MEMBER expr to left of . + * TOK_DBLDOT pn_atom: name to right of . * TOK_LB binary pn_left: MEMBER expr to left of [ * pn_right: expr between [ and ] * TOK_LP list pn_head: list of call, arg1, arg2, ... argN @@ -182,6 +182,54 @@ JS_BEGIN_EXTERN_C * with pn_slot >= 0 and pn_attrs telling const-ness * TOK_NUMBER dval pn_dval: double value of numeric literal * TOK_PRIMARY nullary pn_op: JSOp bytecode + * TOK_AT unary pn_op: JSOP_TOATTRNAME; pn_kid attribute id/expr + * TOK_DBLCOLON binary pn_op: JSOP_QNAME + * pn_left: TOK_STAR or TOK_NAME name node + * pn_right: TOK_STAR or TOK_NAME name node, or + * expr between [ and ] + * TOK_XMLELEM list XML element node + * pn_head: start tag, content1, ... contentN, end tag + * pn_count: 2 + N where N is number of content nodes + * N may be > x.length() if {expr} embedded + * TOK_XMLLIST list XML list node + * pn_head: content1, ... contentN + * TOK_XMLSTAGO, list XML start, end, and point tag contents + * TOK_XMLETAGC, pn_head: tag name or {expr}, ... XML attrs ... + * TOK_XMLPTAGO + * TOK_XMLNAME nullary pn_atom: XML name, with no {expr} embedded + * TOK_XMLNAME list pn_head: tag name or {expr}, ... name or {expr} + * TOK_XMLATTR, nullary pn_atom: attribute value string; pn_op: JSOP_STRING + * TOK_XMLCDATA, + * TOK_XMLCOMMENT + * TOK_XMLPI nullary pn_atom: XML processing instruction target + * pn_atom2: XML PI content, or null if no content + * TOK_XMLTEXT nullary pn_atom: marked-up text, or null if empty string + * TOK_LC unary {expr} in XML tag or content; pn_kid is expr + * + * So an XML tag with no {expr} and three attributes is a list with the form: + * + * (tagname attrname1 attrvalue1 attrname2 attrvalue2 attrname2 attrvalue3) + * + * An XML tag with embedded expressions like so: + * + * + * + * would have the form: + * + * ((name1 {expr1}) (name2 {expr2} name3) {expr3}) + * + * where () bracket a list with elements separated by spaces, and {expr} is a + * TOK_LC unary node with expr as its kid. + * + * Thus, the attribute name/value pairs occupy successive odd and even list + * locations, where pn_head is the TOK_XMLNAME node at list location 0. The + * parser builds the same sort of structures for elements: + * + * Hi there!How are you?{x + y} + * + * translates to: + * + * ((a x {x}) 'Hi there!' ((b y {y}) 'How are you?') ((answer) {x + y})) */ typedef enum JSParseNodeArity { PN_FUNC = -3, @@ -232,6 +280,10 @@ struct JSParseNode { jsint slot; /* -1 or arg or local var slot */ uintN attrs; /* attributes if local var or const */ } name; + struct { + JSAtom *atom; /* first atom in pair */ + JSAtom *atom2; /* second atom in pair or null */ + } apair; jsdouble dval; /* aligned numeric literal value */ } pn_u; JSParseNode *pn_next; /* to align dval and pn_u on RISCs */ @@ -258,6 +310,7 @@ struct JSParseNode { #define pn_slot pn_u.name.slot #define pn_attrs pn_u.name.attrs #define pn_dval pn_u.dval +#define pn_atom2 pn_u.apair.atom2 /* PN_LIST pn_extra flags. */ #define PNX_STRCAT 0x01 /* TOK_PLUS list has string term */ @@ -266,6 +319,7 @@ struct JSParseNode { #define PNX_FORINVAR 0x08 /* TOK_VAR is left kid of TOK_IN node, which is left kid of TOK_FOR */ #define PNX_ENDCOMMA 0x10 /* array literal has comma at end */ +#define PNX_XMLROOT 0x20 /* top-most node in XML literal tree */ /* * Move pn2 into pn, preserving pn->pn_pos and pn->pn_offset and handing off @@ -304,7 +358,7 @@ struct JSParseNode { JS_BEGIN_MACRO \ (list)->pn_head = NULL; \ (list)->pn_tail = &(list)->pn_head; \ - (list)->pn_count = 0; \ + (list)->pn_count = (list)->pn_extra = 0; \ JS_END_MACRO #define PN_INIT_LIST_1(list, pn) \ @@ -312,6 +366,7 @@ struct JSParseNode { (list)->pn_head = (pn); \ (list)->pn_tail = &(pn)->pn_next; \ (list)->pn_count = 1; \ + (list)->pn_extra = 0; \ JS_END_MACRO #define PN_APPEND(list, pn) \ @@ -340,6 +395,12 @@ js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun); extern JSBool js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc); +#if JS_HAS_XML_SUPPORT +JS_FRIEND_API(JSParseNode *) +js_ParseXMLTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, + JSBool allowList); +#endif + JS_END_EXTERN_C #endif /* jsparse_h___ */ diff --git a/mozilla/js/src/jsprvtd.h b/mozilla/js/src/jsprvtd.h index f5f1e77f601..34a701dfd93 100644 --- a/mozilla/js/src/jsprvtd.h +++ b/mozilla/js/src/jsprvtd.h @@ -92,6 +92,7 @@ typedef struct JSScopeOps JSScopeOps; typedef struct JSScopeProperty JSScopeProperty; typedef struct JSStackFrame JSStackFrame; typedef struct JSStackHeader JSStackHeader; +typedef struct JSStringBuffer JSStringBuffer; typedef struct JSSubString JSSubString; /* "Friend" types used by jscntxt.h and jsdbgapi.h. */ @@ -104,8 +105,8 @@ typedef enum JSTrapStatus { } JSTrapStatus; typedef JSTrapStatus -(* JS_DLL_CALLBACK JSTrapHandler)(JSContext *cx, JSScript *script, jsbytecode *pc, - jsval *rval, void *closure); +(* JS_DLL_CALLBACK JSTrapHandler)(JSContext *cx, JSScript *script, + jsbytecode *pc, jsval *rval, void *closure); typedef JSBool (* JS_DLL_CALLBACK JSWatchPointHandler)(JSContext *cx, JSObject *obj, jsval id, @@ -115,56 +116,53 @@ typedef JSBool typedef void (* JS_DLL_CALLBACK JSNewScriptHook)(JSContext *cx, const char *filename, /* URL of script */ - uintN lineno, /* line script starts */ + uintN lineno, /* first line */ JSScript *script, JSFunction *fun, void *callerdata); /* called just before script destruction */ typedef void -(* JS_DLL_CALLBACK JSDestroyScriptHook)(JSContext *cx, - JSScript *script, +(* JS_DLL_CALLBACK JSDestroyScriptHook)(JSContext *cx, + JSScript *script, void *callerdata); typedef void -(* JS_DLL_CALLBACK JSSourceHandler)(const char *filename, uintN lineno, - jschar *str, size_t length, +(* JS_DLL_CALLBACK JSSourceHandler)(const char *filename, uintN lineno, + jschar *str, size_t length, void **listenerTSData, void *closure); /* -* This hook captures high level script execution and function calls -* (JS or native). -* It is used by JS_SetExecuteHook to hook top level scripts and by -* JS_SetCallHook to hook function calls. -* It will get called twice per script or function call: -* just before execution begins and just after it finishes. In both cases -* the 'current' frame is that of the executing code. -* -* The 'before' param is JS_TRUE for the hook invocation before the execution -* and JS_FALSE for the invocation after the code has run. -* -* The 'ok' param is significant only on the post execution invocation to -* signify whether or not the code completed 'normally'. -* -* The 'closure' param is as passed to JS_SetExecuteHook or JS_SetCallHook -* for the 'before'invocation, but is whatever value is returned from that -* invocation for the 'after' invocation. Thus, the hook implementor *could* -* allocate a stucture in the 'before' invocation and return a pointer -* to that structure. The pointer would then be handed to the hook for -* the 'after' invocation. Alternately, the 'before' could just return the -* same value as in 'closure' to cause the 'after' invocation to be called -* with the same 'closure' value as the 'before'. -* -* Returning NULL in the 'before' hook will cause the 'after' hook to -* NOT be called. -*/ - + * This hook captures high level script execution and function calls (JS or + * native). It is used by JS_SetExecuteHook to hook top level scripts and by + * JS_SetCallHook to hook function calls. It will get called twice per script + * or function call: just before execution begins and just after it finishes. + * In both cases the 'current' frame is that of the executing code. + * + * The 'before' param is JS_TRUE for the hook invocation before the execution + * and JS_FALSE for the invocation after the code has run. + * + * The 'ok' param is significant only on the post execution invocation to + * signify whether or not the code completed 'normally'. + * + * The 'closure' param is as passed to JS_SetExecuteHook or JS_SetCallHook + * for the 'before'invocation, but is whatever value is returned from that + * invocation for the 'after' invocation. Thus, the hook implementor *could* + * allocate a stucture in the 'before' invocation and return a pointer to that + * structure. The pointer would then be handed to the hook for the 'after' + * invocation. Alternately, the 'before' could just return the same value as + * in 'closure' to cause the 'after' invocation to be called with the same + * 'closure' value as the 'before'. + * + * Returning NULL in the 'before' hook will cause the 'after' hook *not* to + * be called. + */ typedef void * (* JS_DLL_CALLBACK JSInterpreterHook)(JSContext *cx, JSStackFrame *fp, JSBool before, JSBool *ok, void *closure); typedef void -(* JS_DLL_CALLBACK JSObjectHook)(JSContext *cx, JSObject *obj, JSBool isNew, +(* JS_DLL_CALLBACK JSObjectHook)(JSContext *cx, JSObject *obj, JSBool isNew, void *closure); typedef JSBool diff --git a/mozilla/js/src/jspubtd.h b/mozilla/js/src/jspubtd.h index 5f19a15ce71..bf2bb718911 100644 --- a/mozilla/js/src/jspubtd.h +++ b/mozilla/js/src/jspubtd.h @@ -84,6 +84,8 @@ typedef enum JSType { JSTYPE_STRING, /* string */ JSTYPE_NUMBER, /* number */ JSTYPE_BOOLEAN, /* boolean */ + JSTYPE_NULL, /* null */ + JSTYPE_XML, /* xml object */ JSTYPE_LIMIT } JSType; @@ -123,6 +125,7 @@ typedef struct JSPropertySpec JSPropertySpec; typedef struct JSObject JSObject; typedef struct JSObjectMap JSObjectMap; typedef struct JSObjectOps JSObjectOps; +typedef struct JSXMLObjectOps JSXMLObjectOps; typedef struct JSRuntime JSRuntime; typedef struct JSRuntime JSTaskState; /* XXX deprecated name */ typedef struct JSScript JSScript; @@ -263,12 +266,17 @@ typedef void * Thus JSClass (which pre-dates JSObjectOps in the API) provides a low-level * interface to class-specific code and data, while JSObjectOps allows for a * higher level of operation, which does not use the object's class except to - * find the class's JSObjectOps struct, by calling clasp->getObjectOps. + * find the class's JSObjectOps struct, by calling clasp->getObjectOps, and to + * finalize the object. * * If this seems backwards, that's because it is! API compatibility requires * a JSClass *clasp parameter to JS_NewObject, etc. Most host objects do not * need to implement the larger JSObjectOps, and can share the common JSScope * code and data used by the native (js_ObjectOps, see jsobj.c) ops. + * + * Further extension to preserve API compatibility: if this function returns + * a pointer to JSXMLObjectOps.base, not to JSObjectOps, then the engine calls + * extended hooks needed for E4X. */ typedef JSObjectOps * (* JS_DLL_CALLBACK JSGetObjectOps)(JSContext *cx, JSClass *clasp); @@ -387,11 +395,7 @@ typedef void */ typedef JSBool (* JS_DLL_CALLBACK JSLookupPropOp)(JSContext *cx, JSObject *obj, jsid id, - JSObject **objp, JSProperty **propp -#if defined JS_THREADSAFE && defined DEBUG - , const char *file, uintN line -#endif - ); + JSObject **objp, JSProperty **propp); /* * Define obj[id], a direct property of obj named id, having the given initial @@ -491,6 +495,23 @@ typedef JSBool (* JS_DLL_CALLBACK JSSetRequiredSlotOp)(JSContext *cx, JSObject *obj, uint32 slot, jsval v); +typedef JSBool +(* JS_DLL_CALLBACK JSCallMethodOp)(JSContext *cx, JSObject *obj, jsid id, + uintN argc, jsval *argv, jsval *rval); + +typedef JSBool +(* JS_DLL_CALLBACK JSEnumerateValuesOp)(JSContext *cx, JSObject *obj, + JSIterateOp enum_op, + jsval *statep, jsval *vp); + +typedef JSBool +(* JS_DLL_CALLBACK JSEqualityOp)(JSContext *cx, JSObject *obj, jsval v, + jsval *vp); + +typedef JSBool +(* JS_DLL_CALLBACK JSConcatenateOp)(JSContext *cx, JSObject *obj, jsval v, + jsval *vp); + /* Typedef for native functions called by the JS VM. */ typedef JSBool diff --git a/mozilla/js/src/jsregexp.c b/mozilla/js/src/jsregexp.c index 4d909d4e284..ffd9ec14876 100644 --- a/mozilla/js/src/jsregexp.c +++ b/mozilla/js/src/jsregexp.c @@ -2998,7 +2998,7 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, ok = JS_FALSE; goto out; } - DEFVAL(STRING_TO_JSVAL(matchstr), INT_TO_JSVAL(0)); + DEFVAL(STRING_TO_JSVAL(matchstr), INT_TO_JSID(0)); } res = &cx->regExpStatics; @@ -3048,7 +3048,7 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, if (test) continue; if (parsub->index == -1) { - ok = js_DefineProperty(cx, obj, INT_TO_JSVAL(num + 1), + ok = js_DefineProperty(cx, obj, INT_TO_JSID(num + 1), JSVAL_VOID, NULL, NULL, JSPROP_ENUMERATE, NULL); } else { @@ -3060,7 +3060,7 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, ok = JS_FALSE; goto out; } - ok = js_DefineProperty(cx, obj, INT_TO_JSVAL(num + 1), + ok = js_DefineProperty(cx, obj, INT_TO_JSID(num + 1), STRING_TO_JSVAL(parstr), NULL, NULL, JSPROP_ENUMERATE, NULL); } @@ -3084,9 +3084,9 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, * order (so they come after the elements). */ DEFVAL(INT_TO_JSVAL(start + gData.skipped), - (jsid)cx->runtime->atomState.indexAtom); + ATOM_TO_JSID(cx->runtime->atomState.indexAtom)); DEFVAL(STRING_TO_JSVAL(str), - (jsid)cx->runtime->atomState.inputAtom); + ATOM_TO_JSID(cx->runtime->atomState.inputAtom)); } #undef DEFVAL diff --git a/mozilla/js/src/jsregexp.h b/mozilla/js/src/jsregexp.h index 998352df522..a1f340e2a6c 100644 --- a/mozilla/js/src/jsregexp.h +++ b/mozilla/js/src/jsregexp.h @@ -65,9 +65,9 @@ struct JSRegExpStatics { /* * This struct holds a bitmap representation of a class from a regexp. - * There's a list of these referenced by the classList field in the JSRegExp + * There's a list of these referenced by the classList field in the JSRegExp * struct below. The initial state has startIndex set to the offset in the - * original regexp source of the beginning of the class contents. The first + * original regexp source of the beginning of the class contents. The first * use of the class converts the source representation into a bitmap. * */ diff --git a/mozilla/js/src/jsscan.c b/mozilla/js/src/jsscan.c index 995c1e660a6..6874c5536a2 100644 --- a/mozilla/js/src/jsscan.c +++ b/mozilla/js/src/jsscan.c @@ -67,6 +67,10 @@ #include "jsregexp.h" #include "jsscan.h" +#if JS_HAS_XML_SUPPORT +#include "jsxml.h" +#endif + #define RESERVE_JAVA_KEYWORDS #define RESERVE_ECMA_KEYWORDS @@ -209,6 +213,42 @@ js_NewTokenStream(JSContext *cx, const jschar *base, size_t length, return ts; } +#define TBMIN 64 + +static JSBool +GrowTokenBuf(JSStringBuffer *sb, size_t newlength) +{ + JSContext *cx; + jschar *base; + ptrdiff_t offset, length; + size_t tbsize; + JSArenaPool *pool; + + cx = sb->data; + base = sb->base; + offset = PTRDIFF(sb->ptr, base, jschar); + pool = &cx->tempPool; + if (!base) { + tbsize = TBMIN * sizeof(jschar); + length = TBMIN - 1; + JS_ARENA_ALLOCATE_CAST(base, jschar *, pool, tbsize); + } else { + length = PTRDIFF(sb->limit, base, jschar); + tbsize = (length + 1) * sizeof(jschar); + length += length + 1; + JS_ARENA_GROW_CAST(base, jschar *, pool, tbsize, tbsize); + } + if (!base) { + JS_ReportOutOfMemory(cx); + sb->base = STRING_BUFFER_ERROR_BASE; + return JS_FALSE; + } + sb->base = base; + sb->limit = base + length; + sb->ptr = base + offset; + return JS_TRUE; +} + JS_FRIEND_API(JSTokenStream *) js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length) { @@ -227,6 +267,8 @@ js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length) ts->userbuf.base = (jschar *)base; ts->userbuf.limit = (jschar *)base + length; ts->userbuf.ptr = (jschar *)base; + ts->tokenbuf.grow = GrowTokenBuf; + ts->tokenbuf.data = cx; ts->listener = cx->runtime->sourceHandler; ts->listenerData = cx->runtime->sourceHandlerData; return ts; @@ -646,6 +688,120 @@ js_ReportCompileErrorNumber(JSContext *cx, JSTokenStream *ts, return warning; } +static JSBool +GrowStringBuffer(JSStringBuffer *sb, size_t newlength) +{ + ptrdiff_t offset; + jschar *bp; + + offset = PTRDIFF(sb->ptr, sb->base, jschar); + newlength += offset; + bp = (jschar *) realloc(sb->base, (newlength + 1) * sizeof(jschar)); + if (!bp) { + free(sb->base); + sb->base = STRING_BUFFER_ERROR_BASE; + return JS_FALSE; + } + sb->base = bp; + sb->ptr = bp + offset; + sb->limit = bp + newlength; + return JS_TRUE; +} + +static void +FreeStringBuffer(JSStringBuffer *sb) +{ + if (sb->base) + free(sb->base); +} + +void +js_InitStringBuffer(JSStringBuffer *sb) +{ + sb->base = sb->limit = sb->ptr = NULL; + sb->grow = GrowStringBuffer; + sb->free = FreeStringBuffer; + sb->data = NULL; +} + +void +js_FinishStringBuffer(JSStringBuffer *sb) +{ + sb->free(sb); +} + +#define ENSURE_STRING_BUFFER(sb,n) \ + ((sb)->ptr + (n) <= (sb)->limit || sb->grow(sb, n)) + +void +js_AppendChar(JSStringBuffer *sb, jschar c) +{ + jschar *bp; + + if (!STRING_BUFFER_OK(sb)) + return; + if (!ENSURE_STRING_BUFFER(sb, 1)) + return; + bp = sb->ptr; + *bp++ = c; + *bp = 0; + sb->ptr = bp; +} + +#if JS_HAS_XML_SUPPORT + +void +js_RepeatChar(JSStringBuffer *sb, jschar c, uintN count) +{ + jschar *bp; + + if (!STRING_BUFFER_OK(sb) || count == 0) + return; + if (!ENSURE_STRING_BUFFER(sb, count)) + return; + for (bp = sb->ptr; count; --count) + *bp++ = c; + *bp = 0; + sb->ptr = bp; +} + +void +js_AppendCString(JSStringBuffer *sb, const char *asciiz) +{ + size_t length; + jschar *bp; + + if (!STRING_BUFFER_OK(sb)) + return; + length = strlen(asciiz); + if (!ENSURE_STRING_BUFFER(sb, length)) + return; + for (bp = sb->ptr; length; --length) + *bp++ = (jschar) *asciiz++; + *bp = 0; + sb->ptr = bp; +} + +void +js_AppendJSString(JSStringBuffer *sb, JSString *str) +{ + size_t length; + jschar *bp; + + if (!STRING_BUFFER_OK(sb)) + return; + length = JSSTRING_LENGTH(str); + if (!ENSURE_STRING_BUFFER(sb, length)) + return; + bp = sb->ptr; + js_strncpy(bp, JSSTRING_CHARS(str), length); + bp += length; + *bp = 0; + sb->ptr = bp; +} + +#endif /* JS_HAS_XML_SUPPORT */ + JSTokenType js_PeekToken(JSContext *cx, JSTokenStream *ts) { @@ -673,48 +829,6 @@ js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts) return tt; } -#define TBMIN 64 - -static JSBool -GrowTokenBuf(JSContext *cx, JSTokenBuf *tb) -{ - jschar *base; - ptrdiff_t offset, length; - size_t tbsize; - JSArenaPool *pool; - - base = tb->base; - offset = PTRDIFF(tb->ptr, base, jschar); - pool = &cx->tempPool; - if (!base) { - tbsize = TBMIN * sizeof(jschar); - length = TBMIN; - JS_ARENA_ALLOCATE_CAST(base, jschar *, pool, tbsize); - } else { - length = PTRDIFF(tb->limit, base, jschar); - tbsize = length * sizeof(jschar); - length <<= 1; - JS_ARENA_GROW_CAST(base, jschar *, pool, tbsize, tbsize); - } - if (!base) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - tb->base = base; - tb->limit = base + length; - tb->ptr = base + offset; - return JS_TRUE; -} - -static JSBool -AddToTokenBuf(JSContext *cx, JSTokenBuf *tb, jschar c) -{ - if (tb->ptr == tb->limit && !GrowTokenBuf(cx, tb)) - return JS_FALSE; - *tb->ptr++ = c; - return JS_TRUE; -} - /* * We have encountered a '\': check for a Unicode escape sequence after it, * returning the character code value if we found a Unicode escape sequence. @@ -772,11 +886,7 @@ js_GetToken(JSContext *cx, JSTokenStream *ts) TOKENBUF_LENGTH(), \ 0)) -#define ADD_TO_TOKENBUF(c) \ - JS_BEGIN_MACRO \ - if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c)) \ - goto error; \ - JS_END_MACRO +#define ADD_TO_TOKENBUF(c) js_AppendChar(&ts->tokenbuf, (jschar) (c)) /* If there was a fatal error, keep returning TOK_ERROR. */ if (ts->flags & TSF_ERROR) @@ -784,6 +894,7 @@ js_GetToken(JSContext *cx, JSTokenStream *ts) /* Check for a pushed-back token resulting from mismatching lookahead. */ while (ts->lookahead != 0) { + JS_ASSERT(!(ts->flags & TSF_XMLTEXTMODE)); ts->lookahead--; ts->cursor = (ts->cursor + 1) & NTOKENS_MASK; tt = CURRENT_TOKEN(ts).type; @@ -791,6 +902,141 @@ js_GetToken(JSContext *cx, JSTokenStream *ts) return tt; } +#if JS_HAS_XML_SUPPORT + if (ts->flags & TSF_XMLTEXTMODE) { + tp = NewToken(ts); + INIT_TOKENBUF(); + qc = (ts->flags & TSF_XMLONLYMODE) ? '<' : '{'; + while ((c = GetChar(ts)) != qc && c != '<') { + if (c == EOF) { + tt = TOK_EOF; + goto out; + } + + if (c == '&') { + /* XXXbe handle built-in XML 1.0 entities */ + } + + ADD_TO_TOKENBUF(c); + } + UngetChar(ts, c); + + if (TOKENBUF_LENGTH() == 0) { + atom = NULL; + } else { + atom = TOKENBUF_TO_ATOM(); + if (!atom) + goto error; + } + tp->pos.end.lineno = (uint16)ts->lineno; + tp->t_op = JSOP_STRING; + tp->t_atom = atom; + tt = TOK_XMLTEXT; + goto out; + } + + if (ts->flags & TSF_XMLTAGMODE) { + tp = NewToken(ts); + c = GetChar(ts); + if (JS_ISXMLSPACE(c)) { + do { + c = GetChar(ts); + } while (JS_ISXMLSPACE(c)); + UngetChar(ts, c); + tt = TOK_XMLSPACE; + goto out; + } + + if (c == EOF) { + tt = TOK_EOF; + goto out; + } + + INIT_TOKENBUF(); + if (JS_ISXMLNAMESTART(c)) { + ADD_TO_TOKENBUF(c); + while ((c = GetChar(ts)) != EOF && JS_ISXMLNAME(c)) + ADD_TO_TOKENBUF(c); + + UngetChar(ts, c); + atom = TOKENBUF_TO_ATOM(); + if (!atom) + goto error; + tp->t_op = JSOP_STRING; + tp->t_atom = atom; + tt = TOK_XMLNAME; + goto out; + } + + switch (c) { + case '{': + if (ts->flags & TSF_XMLONLYMODE) + goto bad_xml_char; + tt = TOK_LC; + goto out; + + case '=': + tt = TOK_ASSIGN; + goto out; + + case '"': + case '\'': + qc = c; + while ((c = GetChar(ts)) != qc) { + if (c == EOF) { + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_UNTERMINATED_STRING); + goto error; + } + + /* + * XML attribute values are double-quoted when pretty-printed + * so escape " if it is expressed directly in a single-quoted + * attribute value. + */ + if (c == '"') { + JS_ASSERT(qc == '\''); + js_AppendCString(&ts->tokenbuf, js_quot_entity_str); + continue; + } + + if (c == '&') { + /* XXXbe handle built-in XML 1.0 entities */ + continue; + } + + ADD_TO_TOKENBUF(c); + } + atom = TOKENBUF_TO_ATOM(); + if (!atom) + goto error; + tp->pos.end.lineno = (uint16)ts->lineno; + tp->t_op = JSOP_STRING; + tp->t_atom = atom; + tt = TOK_XMLATTR; + goto out; + + case '>': + tt = TOK_XMLTAGC; + goto out; + + case '/': + if (MatchChar(ts, '>')) { + tt = TOK_XMLPTAGC; + goto out; + } + /* FALL THROUGH */ + + bad_xml_char: + default: + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_BAD_XML_CHARACTER); + goto error; + } + /* NOTREACHED */ + } +#endif /* JS_HAS_XML_SUPPORT */ + retry: do { c = GetChar(ts); @@ -811,10 +1057,10 @@ retry: ts->flags |= TSF_DIRTYLINE; hadUnicodeEscape = JS_FALSE; - if (JS_ISIDENT_START(c) || + if (JS_ISIDSTART(c) || (c == '\\' && (c = GetUnicodeEscape(ts), - hadUnicodeEscape = JS_ISIDENT_START(c)))) { + hadUnicodeEscape = JS_ISIDSTART(c)))) { INIT_TOKENBUF(); for (;;) { ADD_TO_TOKENBUF(c); @@ -1033,10 +1279,21 @@ retry: case '?': tt = TOK_HOOK; break; case '.': - tt = TOK_DOT; +#if JS_HAS_XML_SUPPORT + if (MatchChar(ts, c)) + tt = TOK_DBLDOT; + else +#endif + tt = TOK_DOT; break; case ':': +#if JS_HAS_XML_SUPPORT + if (JS_HAS_XML_OPTION(cx) && MatchChar(ts, c)) { + tt = TOK_DBLCOLON; + break; + } +#endif /* * Default so compiler can modify to JSOP_GETTER if 'p getter: v' in an * object initializer, likewise for setter. @@ -1104,7 +1361,126 @@ retry: } break; +#if JS_HAS_XML_SUPPORT + case '@': + if (JS_HAS_XML_OPTION(cx)) { + tt = TOK_AT; + break; + } + goto badchar; +#endif + case '<': +#if JS_HAS_XML_SUPPORT + if (JS_HAS_XML_OPTION(cx) && (ts->flags & TSF_OPERAND)) { + /* Check for XML comment or CDATA section. */ + if (MatchChar(ts, '!')) { + INIT_TOKENBUF(); + + /* Scan XML comment. */ + if (MatchChar(ts, '-')) { + if (!MatchChar(ts, '-')) + goto bad_xml_markup; + while ((c = GetChar(ts)) != '-' || !MatchChar(ts, '-')) { + if (c == EOF) + goto bad_xml_markup; + ADD_TO_TOKENBUF(c); + } + tt = TOK_XMLCOMMENT; + tp->t_op = JSOP_XMLCOMMENT; + goto finish_xml_markup; + } + + /* Scan CDATA section. */ + if (MatchChar(ts, '[')) { + jschar cp[6]; + if (PeekChars(ts, 6, cp) && + cp[0] == 'C' && + cp[1] == 'D' && + cp[2] == 'A' && + cp[3] == 'T' && + cp[4] == 'A' && + cp[5] == '[') { + SkipChars(ts, 6); + while ((c = GetChar(ts)) != ']' || + !MatchChar(ts, ']')) { + if (c == EOF) + goto bad_xml_markup; + ADD_TO_TOKENBUF(c); + } + tt = TOK_XMLCDATA; + tp->t_op = JSOP_XMLCDATA; + goto finish_xml_markup; + } + goto bad_xml_markup; + } + } + + /* Check for processing instruction. */ + if (MatchChar(ts, '?')) { + JSBool inTarget = JS_TRUE; + size_t targetLength = 0; + ptrdiff_t contentIndex = -1; + + INIT_TOKENBUF(); + while ((c = GetChar(ts)) != '?' || PeekChar(ts) != '>') { + if (c == EOF) + goto bad_xml_markup; + if (inTarget) { + if (JS_ISXMLSPACE(c)) { + if (TOKENBUF_LENGTH() == 0) + goto bad_xml_markup; + inTarget = JS_FALSE; + } else { + if (!((TOKENBUF_LENGTH() == 0) + ? JS_ISXMLNAMESTART(c) + : JS_ISXMLNAME(c))) { + goto bad_xml_markup; + } + ++targetLength; + } + } else { + if (contentIndex < 0 && !JS_ISXMLSPACE(c)) + contentIndex = TOKENBUF_LENGTH(); + } + ADD_TO_TOKENBUF(c); + } + if (contentIndex < 0) { + tp->t_atom2 = NULL; + } else { + atom = js_AtomizeChars(cx, + &TOKENBUF_CHAR(contentIndex), + TOKENBUF_LENGTH() - contentIndex, + 0); + if (!atom) + goto error; + tp->t_atom2 = atom; + TRIM_TOKENBUF(targetLength); + } + tt = TOK_XMLPI; + + finish_xml_markup: + if (!MatchChar(ts, '>')) + goto bad_xml_markup; + atom = TOKENBUF_TO_ATOM(); + if (!atom) + goto error; + tp->t_atom = atom; + tp->pos.end.lineno = (uint16)ts->lineno; + goto out; + } + + /* An XML start-of-tag character. */ + tt = MatchChar(ts, '/') ? TOK_XMLETAGO : TOK_XMLSTAGO; + goto out; + + bad_xml_markup: + js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, + JSMSG_BAD_XML_MARKUP); + goto error; + } +#endif /* JS_HAS_XML_SUPPORT */ + /* NB: treat HTML begin-comment as comment-till-end-of-line */ if (MatchChar(ts, '!')) { if (MatchChar(ts, '-')) { @@ -1372,10 +1748,12 @@ skipline: goto badchar; break; } - - badchar: #endif /* JS_HAS_SHARP_VARS */ +#if JS_HAS_SHARP_VARS || JS_HAS_XML_SUPPORT + badchar: +#endif + default: js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_ILLEGAL_CHARACTER); @@ -1383,6 +1761,8 @@ skipline: } out: + if (!STRING_BUFFER_OK(&ts->tokenbuf)) + tt = TOK_ERROR; JS_ASSERT(tt < TOK_LIMIT); tp->pos.end.index = ts->linepos + (ts->linebuf.ptr - ts->linebuf.base) - diff --git a/mozilla/js/src/jsscan.h b/mozilla/js/src/jsscan.h index 121b35df31c..67fb5c9a374 100644 --- a/mozilla/js/src/jsscan.h +++ b/mozilla/js/src/jsscan.h @@ -107,6 +107,24 @@ typedef enum JSTokenType { TOK_THROW = 58, /* throw keyword */ TOK_INSTANCEOF = 59, /* instanceof keyword */ TOK_DEBUGGER = 60, /* debugger keyword */ + TOK_XMLSTAGO = 61, /* XML start tag open (<) */ + TOK_XMLETAGO = 62, /* XML end tag open () */ + TOK_XMLTAGC = 64, /* XML start or end tag close (>) */ + TOK_XMLNAME = 65, /* XML start-tag non-final fragment */ + TOK_XMLATTR = 66, /* XML quoted attribute value */ + TOK_XMLSPACE = 67, /* XML whitespace within a tag */ + TOK_XMLTEXT = 68, /* XML text */ + TOK_XMLCOMMENT = 69, /* XML comment */ + TOK_XMLCDATA = 70, /* XML CDATA section */ + TOK_XMLPI = 71, /* XML processing instruction */ + TOK_AT = 72, /* XML attribute op (@) */ + TOK_DBLCOLON = 73, /* namespace qualified name op (::) */ + TOK_ANYNAME = 74, /* XML AnyName singleton (*) */ + TOK_DBLDOT = 75, /* XML descendant op (..) */ + TOK_FILTER = 76, /* XML filtering predicate op (.()) */ + TOK_XMLELEM = 77, /* XML element node type (no token) */ + TOK_XMLLIST = 78, /* XML list node type (no token) */ TOK_RESERVED, /* reserved keywords */ TOK_LIMIT /* domain size */ } JSTokenType; @@ -114,6 +132,42 @@ typedef enum JSTokenType { #define IS_PRIMARY_TOKEN(tt) \ ((uintN)((tt) - TOK_NAME) <= (uintN)(TOK_PRIMARY - TOK_NAME)) +#define TOKEN_TYPE_IS_XML(tt) \ + (tt == TOK_AT || tt == TOK_DBLCOLON || tt == TOK_ANYNAME) + +struct JSStringBuffer { + jschar *base; + jschar *limit; /* length limit for quick bounds check */ + jschar *ptr; /* slot for next non-NUL char to store */ + size_t length; + size_t offset; + JSBool (*grow)(JSStringBuffer *sb, size_t newlength); + void (*free)(JSStringBuffer *sb); + void *data; +}; + +#define STRING_BUFFER_ERROR_BASE ((jschar *) 1) +#define STRING_BUFFER_OK(sb) ((sb)->base != STRING_BUFFER_ERROR_BASE) +#define STRING_BUFFER_OFFSET(sb) ((sb)->ptr -(sb)->base) + +extern void +js_InitStringBuffer(JSStringBuffer *sb); + +extern void +js_FinishStringBuffer(JSStringBuffer *sb); + +extern void +js_AppendChar(JSStringBuffer *sb, jschar c); + +extern void +js_RepeatChar(JSStringBuffer *sb, jschar c, uintN count); + +extern void +js_AppendCString(JSStringBuffer *sb, const char *asciiz); + +extern void +js_AppendJSString(JSStringBuffer *sb, JSString *str); + struct JSTokenPtr { uint16 index; /* index of char in physical line */ uint16 lineno; /* physical line number */ @@ -129,16 +183,21 @@ struct JSToken { JSTokenPos pos; /* token position in file */ jschar *ptr; /* beginning of token in line buffer */ union { - struct { + struct { /* non-numeric literal */ JSOp op; /* operator, for minimal parser */ JSAtom *atom; /* atom table entry */ } s; + struct { /* atom pair, for XML PIs */ + JSAtom *atom2; /* auxiliary atom table entry */ + JSAtom *atom; /* main atom table entry */ + } p; jsdouble dval; /* floating point number */ } u; }; #define t_op u.s.op #define t_atom u.s.atom +#define t_atom2 u.p.atom2 #define t_dval u.dval typedef struct JSTokenBuf { @@ -164,7 +223,7 @@ struct JSTokenStream { ptrdiff_t linepos; /* linebuf offset in physical line */ JSTokenBuf linebuf; /* line buffer for diagnostics */ JSTokenBuf userbuf; /* user input buffer if !file */ - JSTokenBuf tokenbuf; /* current token string buffer */ + JSStringBuffer tokenbuf; /* current token string buffer */ const char *filename; /* input filename or null */ FILE *file; /* stdio stream if reading from file */ JSPrincipals *principals; /* principals associated with source */ @@ -187,6 +246,9 @@ struct JSTokenStream { #define TSF_CRFLAG 0x40 /* linebuf would have ended with \r */ #define TSF_DIRTYLINE 0x80 /* non-whitespace since start of line */ #define TSF_OWNFILENAME 0x100 /* ts->filename is malloc'd */ +#define TSF_XMLTAGMODE 0x200 /* scanning within an XML tag in E4X */ +#define TSF_XMLTEXTMODE 0x400 /* scanning XMLText terminal from E4X */ +#define TSF_XMLONLYMODE 0x800 /* don't scan {expr} within text/tag */ /* Unicode separators that are treated as line terminators, in addition to \n, \r */ #define LINE_SEPARATOR 0x2028 diff --git a/mozilla/js/src/jsscope.h b/mozilla/js/src/jsscope.h index 8ac7ef073bc..c7c189969b0 100644 --- a/mozilla/js/src/jsscope.h +++ b/mozilla/js/src/jsscope.h @@ -99,7 +99,7 @@ * that order; and to finish the fork, we'd add a node labeled Z with the path * X->Z, if it doesn't exist. This could lead to lots of extra nodes, and to * O(n^2) growth when deleting lots of properties. - * + * * Rather, for O(1) growth all around, we should share the path X->Y->Z among * scopes having those three properties added in that order, and among scopes * having only X->Z where Y was deleted. All such scopes have a lastProp that @@ -345,10 +345,12 @@ js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp, extern void js_DestroyScope(JSContext *cx, JSScope *scope); -#define ID_TO_VALUE(id) (((id) & JSVAL_INT) ? id : ATOM_KEY((JSAtom *)(id))) -#define HASH_ID(id) (((id) & JSVAL_INT) \ - ? (jsatomid) JSVAL_TO_INT(id) \ - : ((JSAtom *)id)->number) +#define ID_TO_VALUE(id) (JSID_IS_ATOM(id) ? ATOM_KEY(JSID_TO_ATOM(id)) : \ + JSID_IS_OBJECT(id) ? (jsval)JSID_CLRTAG(id) : \ + (jsval)(id)) +#define HASH_ID(id) (JSID_IS_ATOM(id) ? JSID_TO_ATOM(id)->number : \ + JSID_IS_OBJECT(id) ? (jsatomid) JSID_CLRTAG(id) : \ + (jsatomid) JSID_TO_INT(id)) extern JS_FRIEND_API(JSScopeProperty **) js_SearchScope(JSScope *scope, jsid id, JSBool adding); diff --git a/mozilla/js/src/jsscript.c b/mozilla/js/src/jsscript.c index 7e5aefc306c..c4c42a6e623 100644 --- a/mozilla/js/src/jsscript.c +++ b/mozilla/js/src/jsscript.c @@ -919,7 +919,7 @@ js_free_sftbl_entry(void *priv, JSHashEntry *he, uintN flag) free(he); } -static JSHashAllocOps table_alloc_ops = { +static JSHashAllocOps sftbl_alloc_ops = { js_alloc_table_space, js_free_table_space, js_alloc_sftbl_entry, js_free_sftbl_entry }; @@ -930,25 +930,18 @@ js_InitRuntimeScriptState(JSContext *cx) JSRuntime *rt = cx->runtime; #ifdef JS_THREADSAFE - /* Must come through here once in primordial thread to init safely! */ - if (!rt->scriptFilenameTableLock) { - rt->scriptFilenameTableLock = JS_NEW_LOCK(); - if (!rt->scriptFilenameTableLock) - return JS_FALSE; - } + JS_ASSERT(!rt->scriptFilenameTableLock); + rt->scriptFilenameTableLock = JS_NEW_LOCK(); + if (!rt->scriptFilenameTableLock) + return JS_FALSE; #endif + JS_ASSERT(!rt->scriptFilenameTable); + rt->scriptFilenameTable = + JS_NewHashTable(16, JS_HashString, js_compare_strings, NULL, + &sftbl_alloc_ops, NULL); if (!rt->scriptFilenameTable) { - JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock); - if (!rt->scriptFilenameTable) { - rt->scriptFilenameTable = - JS_NewHashTable(16, JS_HashString, js_compare_strings, NULL, - &table_alloc_ops, NULL); - } - JS_RELEASE_LOCK(rt->scriptFilenameTableLock); - if (!rt->scriptFilenameTable) { - js_FinishRuntimeScriptState(cx); /* free lock if threadsafe */ - return JS_FALSE; - } + js_FinishRuntimeScriptState(cx); /* free lock if threadsafe */ + return JS_FALSE; } return JS_TRUE; } diff --git a/mozilla/js/src/jsstr.c b/mozilla/js/src/jsstr.c index e143ab8dfca..a68fb9e04b2 100644 --- a/mozilla/js/src/jsstr.c +++ b/mozilla/js/src/jsstr.c @@ -167,7 +167,7 @@ js_ConcatStrings(JSContext *cx, JSString *left, JSString *right) s = (jschar *) JS_realloc(cx, ls, (ln + rn + 1) * sizeof(jschar)); if (!s) return NULL; - + /* Take care: right could depend on left! */ lrdist = (size_t)(rs - ls); if (lrdist < ln) @@ -537,7 +537,7 @@ str_enumerate(JSContext *cx, JSObject *obj) str1 = js_NewDependentString(cx, str, i, 1, 0); if (!str1) return JS_FALSE; - if (!OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSVAL(i), + if (!OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSID(i), STRING_TO_JSVAL(str1), NULL, NULL, STRING_ELEMENT_ATTRS, NULL)) { return JS_FALSE; @@ -563,7 +563,7 @@ str_resolve(JSContext *cx, JSObject *obj, jsval id) str1 = js_NewDependentString(cx, str, (size_t)slot, 1, 0); if (!str1) return JS_FALSE; - if (!OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSVAL(slot), + if (!OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSID(slot), STRING_TO_JSVAL(str1), NULL, NULL, STRING_ELEMENT_ATTRS, NULL)) { return JS_FALSE; @@ -572,7 +572,7 @@ str_resolve(JSContext *cx, JSObject *obj, jsval id) return JS_TRUE; } -static JSClass string_class = { +JSClass js_StringClass = { js_String_str, JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, str_getProperty, JS_PropertyStub, @@ -610,7 +610,7 @@ str_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) char buf[16]; jschar *s, *t; - if (!JS_InstanceOf(cx, obj, &string_class, argv)) + if (!JS_InstanceOf(cx, obj, &js_StringClass, argv)) return JS_FALSE; v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); if (!JSVAL_IS_STRING(v)) @@ -618,7 +618,7 @@ str_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) str = js_QuoteString(cx, JSVAL_TO_STRING(v), '"'); if (!str) return JS_FALSE; - j = JS_snprintf(buf, sizeof buf, "(new %s(", string_class.name); + j = JS_snprintf(buf, sizeof buf, "(new %s(", js_StringClass.name); s = JSSTRING_CHARS(str); k = JSSTRING_LENGTH(str); n = j + k + 2; @@ -648,7 +648,7 @@ str_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval v; - if (!JS_InstanceOf(cx, obj, &string_class, argv)) + if (!JS_InstanceOf(cx, obj, &js_StringClass, argv)) return JS_FALSE; v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); if (!JSVAL_IS_STRING(v)) @@ -660,7 +660,7 @@ str_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) static JSBool str_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - if (!JS_InstanceOf(cx, obj, &string_class, argv)) + if (!JS_InstanceOf(cx, obj, &js_StringClass, argv)) return JS_FALSE; *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); return JS_TRUE; @@ -1172,6 +1172,7 @@ match_or_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, * would normally be returned in *rval. */ JS_ASSERT(*cx->fp->down->pc == JSOP_CALL || + *cx->fp->down->pc == JSOP_CALLMETHOD || *cx->fp->down->pc == JSOP_NEW); JS_ASSERT(js_CodeSpec[*cx->fp->down->pc].length == 3); switch (cx->fp->down->pc[3]) { @@ -1228,7 +1229,7 @@ match_glob(JSContext *cx, jsint count, GlobData *data) if (!matchstr) return JS_FALSE; v = STRING_TO_JSVAL(matchstr); - return js_SetProperty(cx, arrayobj, INT_TO_JSVAL(count), &v); + return js_SetProperty(cx, arrayobj, INT_TO_JSID(count), &v); } static JSBool @@ -2415,7 +2416,7 @@ js_InitStringClass(JSContext *cx, JSObject *obj) if (!JS_DefineFunctions(cx, obj, string_functions)) return NULL; - proto = JS_InitClass(cx, obj, NULL, &string_class, String, 1, + proto = JS_InitClass(cx, obj, NULL, &js_StringClass, String, 1, string_props, string_methods, NULL, string_static_methods); if (!proto) @@ -2635,13 +2636,31 @@ js_StringToObject(JSContext *cx, JSString *str) { JSObject *obj; - obj = js_NewObject(cx, &string_class, NULL, NULL); + obj = js_NewObject(cx, &js_StringClass, NULL, NULL); if (!obj) return NULL; OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, STRING_TO_JSVAL(str)); return obj; } +const char * +js_ValueToPrintableString(JSContext *cx, jsval v) +{ + JSString *str; + const char *bytes; + + str = js_ValueToString(cx, v); + if (!str) + return NULL; + str = js_QuoteString(cx, str, 0); + if (!str) + return NULL; + bytes = js_GetStringBytes(str); + if (!bytes) + JS_ReportOutOfMemory(cx); + return bytes; +} + JSString * js_ValueToString(JSContext *cx, jsval v) { @@ -2936,7 +2955,9 @@ js_GetStringBytes(JSString *str) * character code, then masking with 0x1F, then adding 10 * will produce the desired numeric value * 5 bits digit offset - * 4 bits reserved for future use + * 1 bit XML 1.0 name start character + * 1 bit XML 1.0 name character + * 2 bits reserved for future use * 5 bits character type */ @@ -4066,119 +4087,119 @@ const uint32 js_A[] = { 0x00000016, /* 6 Pe */ 0x00000019, /* 7 Sm */ 0x00000014, /* 8 Pd */ -0x00036009, /* 9 Nd, identifier part, decimal 16 */ -0x0827FE01, /* 10 Lu, hasLower (add 32), identifier start, supradecimal 31 */ +0x00036089, /* 9 Nd, identifier part, decimal 16 */ +0x0827FF81, /* 10 Lu, hasLower (add 32), identifier start, supradecimal 31 */ 0x0000001B, /* 11 Sk */ 0x00050017, /* 12 Pc, underscore */ -0x0817FE02, /* 13 Ll, hasUpper (subtract 32), identifier start, supradecimal 31 */ +0x0817FF82, /* 13 Ll, hasUpper (subtract 32), identifier start, supradecimal 31 */ 0x0000000C, /* 14 Zs */ 0x0000001C, /* 15 So */ -0x00070002, /* 16 Ll, identifier start */ +0x00070182, /* 16 Ll, identifier start */ 0x0000600B, /* 17 No, decimal 16 */ 0x0000500B, /* 18 No, decimal 8 */ 0x0000800B, /* 19 No, strange */ -0x08270001, /* 20 Lu, hasLower (add 32), identifier start */ -0x08170002, /* 21 Ll, hasUpper (subtract 32), identifier start */ -0xE1D70002, /* 22 Ll, hasUpper (subtract -121), identifier start */ -0x00670001, /* 23 Lu, hasLower (add 1), identifier start */ -0x00570002, /* 24 Ll, hasUpper (subtract 1), identifier start */ -0xCE670001, /* 25 Lu, hasLower (add -199), identifier start */ -0x3A170002, /* 26 Ll, hasUpper (subtract 232), identifier start */ -0xE1E70001, /* 27 Lu, hasLower (add -121), identifier start */ -0x4B170002, /* 28 Ll, hasUpper (subtract 300), identifier start */ -0x34A70001, /* 29 Lu, hasLower (add 210), identifier start */ -0x33A70001, /* 30 Lu, hasLower (add 206), identifier start */ -0x33670001, /* 31 Lu, hasLower (add 205), identifier start */ -0x32A70001, /* 32 Lu, hasLower (add 202), identifier start */ -0x32E70001, /* 33 Lu, hasLower (add 203), identifier start */ -0x33E70001, /* 34 Lu, hasLower (add 207), identifier start */ -0x34E70001, /* 35 Lu, hasLower (add 211), identifier start */ -0x34670001, /* 36 Lu, hasLower (add 209), identifier start */ -0x35670001, /* 37 Lu, hasLower (add 213), identifier start */ -0x00070001, /* 38 Lu, identifier start */ -0x36A70001, /* 39 Lu, hasLower (add 218), identifier start */ -0x00070005, /* 40 Lo, identifier start */ -0x36670001, /* 41 Lu, hasLower (add 217), identifier start */ -0x36E70001, /* 42 Lu, hasLower (add 219), identifier start */ -0x00AF0001, /* 43 Lu, hasLower (add 2), hasTitle, identifier start */ -0x007F0003, /* 44 Lt, hasUpper (subtract 1), hasLower (add 1), hasTitle, identifier start */ -0x009F0002, /* 45 Ll, hasUpper (subtract 2), hasTitle, identifier start */ +0x08270181, /* 20 Lu, hasLower (add 32), identifier start */ +0x08170182, /* 21 Ll, hasUpper (subtract 32), identifier start */ +0xE1D70182, /* 22 Ll, hasUpper (subtract -121), identifier start */ +0x00670181, /* 23 Lu, hasLower (add 1), identifier start */ +0x00570182, /* 24 Ll, hasUpper (subtract 1), identifier start */ +0xCE670181, /* 25 Lu, hasLower (add -199), identifier start */ +0x3A170182, /* 26 Ll, hasUpper (subtract 232), identifier start */ +0xE1E70181, /* 27 Lu, hasLower (add -121), identifier start */ +0x4B170182, /* 28 Ll, hasUpper (subtract 300), identifier start */ +0x34A70181, /* 29 Lu, hasLower (add 210), identifier start */ +0x33A70181, /* 30 Lu, hasLower (add 206), identifier start */ +0x33670181, /* 31 Lu, hasLower (add 205), identifier start */ +0x32A70181, /* 32 Lu, hasLower (add 202), identifier start */ +0x32E70181, /* 33 Lu, hasLower (add 203), identifier start */ +0x33E70181, /* 34 Lu, hasLower (add 207), identifier start */ +0x34E70181, /* 35 Lu, hasLower (add 211), identifier start */ +0x34670181, /* 36 Lu, hasLower (add 209), identifier start */ +0x35670181, /* 37 Lu, hasLower (add 213), identifier start */ +0x00070181, /* 38 Lu, identifier start */ +0x36A70181, /* 39 Lu, hasLower (add 218), identifier start */ +0x00070185, /* 40 Lo, identifier start */ +0x36670181, /* 41 Lu, hasLower (add 217), identifier start */ +0x36E70181, /* 42 Lu, hasLower (add 219), identifier start */ +0x00AF0181, /* 43 Lu, hasLower (add 2), hasTitle, identifier start */ +0x007F0183, /* 44 Lt, hasUpper (subtract 1), hasLower (add 1), hasTitle, identifier start */ +0x009F0182, /* 45 Ll, hasUpper (subtract 2), hasTitle, identifier start */ 0x00000000, /* 46 unassigned */ -0x34970002, /* 47 Ll, hasUpper (subtract 210), identifier start */ -0x33970002, /* 48 Ll, hasUpper (subtract 206), identifier start */ -0x33570002, /* 49 Ll, hasUpper (subtract 205), identifier start */ -0x32970002, /* 50 Ll, hasUpper (subtract 202), identifier start */ -0x32D70002, /* 51 Ll, hasUpper (subtract 203), identifier start */ -0x33D70002, /* 52 Ll, hasUpper (subtract 207), identifier start */ -0x34570002, /* 53 Ll, hasUpper (subtract 209), identifier start */ -0x34D70002, /* 54 Ll, hasUpper (subtract 211), identifier start */ -0x35570002, /* 55 Ll, hasUpper (subtract 213), identifier start */ -0x36970002, /* 56 Ll, hasUpper (subtract 218), identifier start */ -0x36570002, /* 57 Ll, hasUpper (subtract 217), identifier start */ -0x36D70002, /* 58 Ll, hasUpper (subtract 219), identifier start */ -0x00070004, /* 59 Lm, identifier start */ -0x00030006, /* 60 Mn, identifier part */ -0x09A70001, /* 61 Lu, hasLower (add 38), identifier start */ -0x09670001, /* 62 Lu, hasLower (add 37), identifier start */ -0x10270001, /* 63 Lu, hasLower (add 64), identifier start */ -0x0FE70001, /* 64 Lu, hasLower (add 63), identifier start */ -0x09970002, /* 65 Ll, hasUpper (subtract 38), identifier start */ -0x09570002, /* 66 Ll, hasUpper (subtract 37), identifier start */ -0x10170002, /* 67 Ll, hasUpper (subtract 64), identifier start */ -0x0FD70002, /* 68 Ll, hasUpper (subtract 63), identifier start */ -0x0F970002, /* 69 Ll, hasUpper (subtract 62), identifier start */ -0x0E570002, /* 70 Ll, hasUpper (subtract 57), identifier start */ -0x0BD70002, /* 71 Ll, hasUpper (subtract 47), identifier start */ -0x0D970002, /* 72 Ll, hasUpper (subtract 54), identifier start */ -0x15970002, /* 73 Ll, hasUpper (subtract 86), identifier start */ -0x14170002, /* 74 Ll, hasUpper (subtract 80), identifier start */ -0x14270001, /* 75 Lu, hasLower (add 80), identifier start */ -0x0C270001, /* 76 Lu, hasLower (add 48), identifier start */ -0x0C170002, /* 77 Ll, hasUpper (subtract 48), identifier start */ -0x00034009, /* 78 Nd, identifier part, decimal 0 */ -0x00000007, /* 79 Me */ -0x00030008, /* 80 Mc, identifier part */ -0x00037409, /* 81 Nd, identifier part, decimal 26 */ +0x34970182, /* 47 Ll, hasUpper (subtract 210), identifier start */ +0x33970182, /* 48 Ll, hasUpper (subtract 206), identifier start */ +0x33570182, /* 49 Ll, hasUpper (subtract 205), identifier start */ +0x32970182, /* 50 Ll, hasUpper (subtract 202), identifier start */ +0x32D70182, /* 51 Ll, hasUpper (subtract 203), identifier start */ +0x33D70182, /* 52 Ll, hasUpper (subtract 207), identifier start */ +0x34570182, /* 53 Ll, hasUpper (subtract 209), identifier start */ +0x34D70182, /* 54 Ll, hasUpper (subtract 211), identifier start */ +0x35570182, /* 55 Ll, hasUpper (subtract 213), identifier start */ +0x36970182, /* 56 Ll, hasUpper (subtract 218), identifier start */ +0x36570182, /* 57 Ll, hasUpper (subtract 217), identifier start */ +0x36D70182, /* 58 Ll, hasUpper (subtract 219), identifier start */ +0x00070084, /* 59 Lm, identifier start */ +0x00030086, /* 60 Mn, identifier part */ +0x09A70181, /* 61 Lu, hasLower (add 38), identifier start */ +0x09670181, /* 62 Lu, hasLower (add 37), identifier start */ +0x10270181, /* 63 Lu, hasLower (add 64), identifier start */ +0x0FE70181, /* 64 Lu, hasLower (add 63), identifier start */ +0x09970182, /* 65 Ll, hasUpper (subtract 38), identifier start */ +0x09570182, /* 66 Ll, hasUpper (subtract 37), identifier start */ +0x10170182, /* 67 Ll, hasUpper (subtract 64), identifier start */ +0x0FD70182, /* 68 Ll, hasUpper (subtract 63), identifier start */ +0x0F970182, /* 69 Ll, hasUpper (subtract 62), identifier start */ +0x0E570182, /* 70 Ll, hasUpper (subtract 57), identifier start */ +0x0BD70182, /* 71 Ll, hasUpper (subtract 47), identifier start */ +0x0D970182, /* 72 Ll, hasUpper (subtract 54), identifier start */ +0x15970182, /* 73 Ll, hasUpper (subtract 86), identifier start */ +0x14170182, /* 74 Ll, hasUpper (subtract 80), identifier start */ +0x14270181, /* 75 Lu, hasLower (add 80), identifier start */ +0x0C270181, /* 76 Lu, hasLower (add 48), identifier start */ +0x0C170182, /* 77 Ll, hasUpper (subtract 48), identifier start */ +0x00034089, /* 78 Nd, identifier part, decimal 0 */ +0x00000087, /* 79 Me */ +0x00030088, /* 80 Mc, identifier part */ +0x00037489, /* 81 Nd, identifier part, decimal 26 */ 0x00005A0B, /* 82 No, decimal 13 */ 0x00006E0B, /* 83 No, decimal 23 */ 0x0000740B, /* 84 No, decimal 26 */ 0x0000000B, /* 85 No */ -0xFE170002, /* 86 Ll, hasUpper (subtract -8), identifier start */ -0xFE270001, /* 87 Lu, hasLower (add -8), identifier start */ -0xED970002, /* 88 Ll, hasUpper (subtract -74), identifier start */ -0xEA970002, /* 89 Ll, hasUpper (subtract -86), identifier start */ -0xE7170002, /* 90 Ll, hasUpper (subtract -100), identifier start */ -0xE0170002, /* 91 Ll, hasUpper (subtract -128), identifier start */ -0xE4170002, /* 92 Ll, hasUpper (subtract -112), identifier start */ -0xE0970002, /* 93 Ll, hasUpper (subtract -126), identifier start */ -0xFDD70002, /* 94 Ll, hasUpper (subtract -9), identifier start */ -0xEDA70001, /* 95 Lu, hasLower (add -74), identifier start */ -0xFDE70001, /* 96 Lu, hasLower (add -9), identifier start */ -0xEAA70001, /* 97 Lu, hasLower (add -86), identifier start */ -0xE7270001, /* 98 Lu, hasLower (add -100), identifier start */ -0xFE570002, /* 99 Ll, hasUpper (subtract -7), identifier start */ -0xE4270001, /* 100 Lu, hasLower (add -112), identifier start */ -0xFE670001, /* 101 Lu, hasLower (add -7), identifier start */ -0xE0270001, /* 102 Lu, hasLower (add -128), identifier start */ -0xE0A70001, /* 103 Lu, hasLower (add -126), identifier start */ +0xFE170182, /* 86 Ll, hasUpper (subtract -8), identifier start */ +0xFE270181, /* 87 Lu, hasLower (add -8), identifier start */ +0xED970182, /* 88 Ll, hasUpper (subtract -74), identifier start */ +0xEA970182, /* 89 Ll, hasUpper (subtract -86), identifier start */ +0xE7170182, /* 90 Ll, hasUpper (subtract -100), identifier start */ +0xE0170182, /* 91 Ll, hasUpper (subtract -128), identifier start */ +0xE4170182, /* 92 Ll, hasUpper (subtract -112), identifier start */ +0xE0970182, /* 93 Ll, hasUpper (subtract -126), identifier start */ +0xFDD70182, /* 94 Ll, hasUpper (subtract -9), identifier start */ +0xEDA70181, /* 95 Lu, hasLower (add -74), identifier start */ +0xFDE70181, /* 96 Lu, hasLower (add -9), identifier start */ +0xEAA70181, /* 97 Lu, hasLower (add -86), identifier start */ +0xE7270181, /* 98 Lu, hasLower (add -100), identifier start */ +0xFE570182, /* 99 Ll, hasUpper (subtract -7), identifier start */ +0xE4270181, /* 100 Lu, hasLower (add -112), identifier start */ +0xFE670181, /* 101 Lu, hasLower (add -7), identifier start */ +0xE0270181, /* 102 Lu, hasLower (add -128), identifier start */ +0xE0A70181, /* 103 Lu, hasLower (add -126), identifier start */ 0x00010010, /* 104 Cf, ignorable */ 0x0004000D, /* 105 Zl, whitespace */ 0x0004000E, /* 106 Zp, whitespace */ 0x0000400B, /* 107 No, decimal 0 */ 0x0000440B, /* 108 No, decimal 2 */ -0x0427420A, /* 109 Nl, hasLower (add 16), identifier start, decimal 1 */ -0x0427800A, /* 110 Nl, hasLower (add 16), identifier start, strange */ -0x0417620A, /* 111 Nl, hasUpper (subtract 16), identifier start, decimal 17 */ -0x0417800A, /* 112 Nl, hasUpper (subtract 16), identifier start, strange */ -0x0007800A, /* 113 Nl, identifier start, strange */ +0x0427438A, /* 109 Nl, hasLower (add 16), identifier start, decimal 1 */ +0x0427818A, /* 110 Nl, hasLower (add 16), identifier start, strange */ +0x0417638A, /* 111 Nl, hasUpper (subtract 16), identifier start, decimal 17 */ +0x0417818A, /* 112 Nl, hasUpper (subtract 16), identifier start, strange */ +0x0007818A, /* 113 Nl, identifier start, strange */ 0x0000420B, /* 114 No, decimal 1 */ 0x0000720B, /* 115 No, decimal 25 */ 0x06A0001C, /* 116 So, hasLower (add 26) */ 0x0690001C, /* 117 So, hasUpper (subtract 26) */ 0x00006C0B, /* 118 No, decimal 22 */ 0x0000560B, /* 119 No, decimal 11 */ -0x0007720A, /* 120 Nl, identifier start, decimal 25 */ -0x0007400A, /* 121 Nl, identifier start, decimal 0 */ +0x0007738A, /* 120 Nl, identifier start, decimal 25 */ +0x0007418A, /* 121 Nl, identifier start, decimal 0 */ 0x00000013, /* 122 Cs */ 0x00000012 /* 123 Co */ }; @@ -4493,7 +4514,7 @@ Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length) JS_ASSERT((*utf8Buffer & 0xC0) == 0x80); ucs4Char = ucs4Char<<6 | (*utf8Buffer++ & 0x3F); } - if (ucs4Char < minucs4Char || + if (ucs4Char < minucs4Char || ucs4Char == 0xFFFE || ucs4Char == 0xFFFF) { ucs4Char = 0xFFFD; } diff --git a/mozilla/js/src/jsstr.h b/mozilla/js/src/jsstr.h index 202d0d9ea7c..58a6c54ea72 100644 --- a/mozilla/js/src/jsstr.h +++ b/mozilla/js/src/jsstr.h @@ -225,19 +225,19 @@ typedef enum JSCharType { >> JS_CTYPE(c)) & 1) /* A unicode letter, suitable for use in an identifier. */ -#define JS_ISUC_LETTER(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ - (1 << JSCT_LOWERCASE_LETTER) | \ - (1 << JSCT_TITLECASE_LETTER) | \ - (1 << JSCT_MODIFIER_LETTER) | \ - (1 << JSCT_OTHER_LETTER) | \ - (1 << JSCT_LETTER_NUMBER)) \ - >> JS_CTYPE(c)) & 1) +#define JS_ISLETTER(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ + (1 << JSCT_LOWERCASE_LETTER) | \ + (1 << JSCT_TITLECASE_LETTER) | \ + (1 << JSCT_MODIFIER_LETTER) | \ + (1 << JSCT_OTHER_LETTER) | \ + (1 << JSCT_LETTER_NUMBER)) \ + >> JS_CTYPE(c)) & 1) /* * 'IdentifierPart' from ECMA grammar, is Unicode letter or combining mark or * digit or connector punctuation. */ -#define JS_ISID_PART(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ +#define JS_ISIDPART(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ (1 << JSCT_LOWERCASE_LETTER) | \ (1 << JSCT_TITLECASE_LETTER) | \ (1 << JSCT_MODIFIER_LETTER) | \ @@ -259,12 +259,20 @@ typedef enum JSCharType { */ #define JS_ISWORD(c) ((c) < 128 && (isalnum(c) || (c) == '_')) -/* XXXbe unify on A/X/Y tbls, avoid ctype.h? */ -#define JS_ISIDENT_START(c) (JS_ISUC_LETTER(c) || (c) == '_' || (c) == '$') -#define JS_ISIDENT(c) (JS_ISID_PART(c) || (c) == '_' || (c) == '$') +#define JS_ISIDSTART(c) (JS_ISLETTER(c) || (c) == '_' || (c) == '$') +#define JS_ISIDENT(c) (JS_ISIDPART(c) || (c) == '_' || (c) == '$') + +#define JS_ISXMLSPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || \ + (c) == '\n') +#define JS_ISXMLNSSTART(c) ((JS_CCODE(c) & 0x00000100) || (c) == '_') +#define JS_ISXMLNS(c) ((JS_CCODE(c) & 0x00000080) || (c) == '.' || \ + (c) == '-' || (c) == '_') +#define JS_ISXMLNAMESTART(c) (JS_ISXMLNSSTART(c) || (c) == ':') +#define JS_ISXMLNAME(c) (JS_ISXMLNS(c) || (c) == ':') #define JS_ISDIGIT(c) (JS_CTYPE(c) == JSCT_DECIMAL_DIGIT_NUMBER) +/* XXXbe unify on A/X/Y tbls, avoid ctype.h? */ /* XXXbe fs, etc. ? */ #define JS_ISSPACE(c) ((JS_CCODE(c) & 0x00070000) == 0x00040000) #define JS_ISPRINT(c) ((c) < 128 && isprint(c)) @@ -279,8 +287,6 @@ typedef enum JSCharType { ? (c) + ((int32)JS_CCODE(c) >> 22) \ : (c))) -#define JS_TOCTRL(c) ((c) ^ 64) /* XXX unsafe! requires uppercase c */ - /* Shorthands for ASCII (7-bit) decimal and hex conversion. */ #define JS7_ISDEC(c) ((c) < 128 && isdigit(c)) #define JS7_UNDEC(c) ((c) - '0') @@ -306,6 +312,8 @@ extern void js_FinishRuntimeStringState(JSContext *cx); /* Initialize the String class, returning its prototype object. */ +extern JSClass js_StringClass; + extern JSObject * js_InitStringClass(JSContext *cx, JSObject *obj); @@ -344,6 +352,12 @@ js_FinalizeStringRT(JSRuntime *rt, JSString *str); extern JSObject * js_StringToObject(JSContext *cx, JSString *str); +/* + * Convert a value to a printable C string. + */ +extern const char * +js_ValueToPrintableString(JSContext *cx, jsval v); + /* * Convert a value to a string, returning null after reporting an error, * otherwise returning a new string reference. diff --git a/mozilla/js/src/jstypes.h b/mozilla/js/src/jstypes.h index b5c80961bfb..6ce8675b52c 100644 --- a/mozilla/js/src/jstypes.h +++ b/mozilla/js/src/jstypes.h @@ -231,7 +231,7 @@ JS_BEGIN_EXTERN_C ** JSInt8 ** DESCRIPTION: ** The int8 types are known to be 8 bits each. There is no type that -** is equivalent to a plain "char". +** is equivalent to a plain "char". ************************************************************************/ #if JS_BYTES_PER_BYTE == 1 typedef unsigned char JSUint8; @@ -244,7 +244,7 @@ typedef signed char JSInt8; ** TYPES: JSUint16 ** JSInt16 ** DESCRIPTION: -** The int16 types are known to be 16 bits each. +** The int16 types are known to be 16 bits each. ************************************************************************/ #if JS_BYTES_PER_SHORT == 2 typedef unsigned short JSUint16; @@ -257,7 +257,7 @@ typedef short JSInt16; ** TYPES: JSUint32 ** JSInt32 ** DESCRIPTION: -** The int32 types are known to be 32 bits each. +** The int32 types are known to be 32 bits each. ************************************************************************/ #if JS_BYTES_PER_INT == 4 typedef unsigned int JSUint32; @@ -315,7 +315,7 @@ typedef JSInt64 JSUint64; ** The JSIntn types are most appropriate for automatic variables. They are ** guaranteed to be at least 16 bits, though various architectures may ** define them to be wider (e.g., 32 or even 64 bits). These types are -** never valid for fields of a structure. +** never valid for fields of a structure. ************************************************************************/ #if JS_BYTES_PER_INT >= 2 typedef int JSIntn; @@ -327,14 +327,14 @@ typedef unsigned int JSUintn; /************************************************************************ ** TYPES: JSFloat64 ** DESCRIPTION: -** NSPR's floating point type is always 64 bits. +** NSPR's floating point type is always 64 bits. ************************************************************************/ typedef double JSFloat64; /************************************************************************ ** TYPES: JSSize ** DESCRIPTION: -** A type for representing the size of objects. +** A type for representing the size of objects. ************************************************************************/ typedef size_t JSSize; @@ -342,7 +342,7 @@ typedef size_t JSSize; ** TYPES: JSPtrDiff ** DESCRIPTION: ** A type for pointer difference. Variables of this type are suitable -** for storing a pointer or pointer sutraction. +** for storing a pointer or pointer sutraction. ************************************************************************/ typedef ptrdiff_t JSPtrdiff; @@ -350,7 +350,7 @@ typedef ptrdiff_t JSPtrdiff; ** TYPES: JSUptrdiff ** DESCRIPTION: ** A type for pointer difference. Variables of this type are suitable -** for storing a pointer or pointer sutraction. +** for storing a pointer or pointer sutraction. ************************************************************************/ typedef unsigned long JSUptrdiff; @@ -360,7 +360,7 @@ typedef unsigned long JSUptrdiff; ** Use JSBool for variables and parameter types. Use JS_FALSE and JS_TRUE ** for clarity of target type in assignments and actual arguments. Use ** 'if (bool)', 'while (!bool)', '(bool) ? x : y' etc., to test booleans -** just as you would C int-valued conditions. +** just as you would C int-valued conditions. ************************************************************************/ typedef JSIntn JSBool; #define JS_TRUE (JSIntn)1 diff --git a/mozilla/js/src/jsxdrapi.c b/mozilla/js/src/jsxdrapi.c index e956072f9a3..0c9aeceedf0 100644 --- a/mozilla/js/src/jsxdrapi.c +++ b/mozilla/js/src/jsxdrapi.c @@ -498,7 +498,7 @@ JS_PUBLIC_API(JSBool) JS_XDRValue(JSXDRState *xdr, jsval *vp) { uint32 type; - + if (xdr->mode == JSXDR_ENCODE) { if (JSVAL_IS_NULL(*vp)) type = JSVAL_XDRNULL; diff --git a/mozilla/js/src/jsxml.c b/mozilla/js/src/jsxml.c new file mode 100644 index 00000000000..c813a8fd868 --- /dev/null +++ b/mozilla/js/src/jsxml.c @@ -0,0 +1,5538 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey E4X code, released August, 2004. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "jsstddef.h" +#include "jsconfig.h" + +#if JS_HAS_XML_SUPPORT + +#include +#include +#include +#include "jstypes.h" +#include "jsbit.h" +#include "jsprf.h" +#include "jsutil.h" +#include "jsapi.h" +#include "jsarray.h" +#include "jsatom.h" +#include "jsbool.h" +#include "jscntxt.h" +#include "jsfun.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsopcode.h" +#include "jsparse.h" +#include "jsscan.h" +#include "jsscope.h" +#include "jsscript.h" +#include "jsstr.h" +#include "jsxml.h" + +#ifdef DEBUG +#include /* for #ifdef DEBUG memset calls */ +#endif + +/* + * NOTES + * - in the js shell, you must options('xml') to start using XML literals + * + * TODO + * - XML_NYI + * - equality + * - concatenation + * - for each in loops + * - Improve code in js_Interpret for CDATA, COMMENT, and PI + * - JSCLASS_W3C_XML_INFO_ITEM support -- live two-way binding to Gecko's DOM! + * - JS_DEFAULT_XML_NAMESPACE_ID is bogus, see jsapi.h XXXbe comment + * - Scan basic XML entities: < > & " + * - clean up GC_MARK_DEBUG to include private reference paths? + * - JSCLASS_NO_DEFAULT_NEW_OBJ or some such to avoid wasted obj alloc? + * - JS_TypeOfValue sure could use a cleaner interface to "types" + * - see also XXXbe + */ + +/* + * Random utilities and global functions. + */ +const char js_isXMLName_str[] = "isXMLName"; +const char js_Namespace_str[] = "Namespace"; +const char js_QName_str[] = "QName"; +const char js_XML_str[] = "XML"; +const char js_XMLList_str[] = "XMLList"; +const char js_localName_str[] = "localName"; +const char js_prefix_str[] = "prefix"; +const char js_toXMLString_str[] = "toXMLString"; +const char js_uri_str[] = "uri"; + +const char js_amp_entity_str[] = "&"; +const char js_gt_entity_str[] = ">"; +const char js_lt_entity_str[] = "<"; +const char js_quot_entity_str[] = """; + +#define IS_EMPTY(str) (JSSTRING_LENGTH(str) == 0) +#define IS_STAR(str) (JSSTRING_LENGTH(str) == 1 && *JSSTRING_CHARS(str) == '*') + +static JSBool +xml_isXMLName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + *rval = BOOLEAN_TO_JSVAL(js_IsXMLName(cx, argv[0])); + return JS_TRUE; +} + +/* + * Namespace class and library functions. + */ +static JSBool +namespace_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +static void +namespace_finalize(JSContext *cx, JSObject *obj) +{ + JSXMLNamespace *ns; + + ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj); + if (ns) { + JS_ASSERT(ns->object); + ns->object = NULL; + js_DestroyXMLNamespace(cx, ns); + } +} + +static void +namespace_mark_private(JSContext *cx, JSXMLNamespace *ns, void *arg) +{ + JS_MarkGCThing(cx, ns->prefix, js_prefix_str, arg); + JS_MarkGCThing(cx, ns->uri, js_uri_str, arg); +} + +static void +namespace_mark_vector(JSContext *cx, JSXMLNamespace **vec, uint32 len, + void *arg) +{ + uint32 i; + JSXMLNamespace *ns; + + for (i = 0; i < len; i++) { + ns = vec[i]; + if (ns->object) { +#ifdef GC_MARK_DEBUG + char buf[100]; + + JS_snprintf(buf, sizeof buf, "%s=%s", + ns->prefix ? JS_GetStringBytes(ns->prefix) : "", + JS_GetStringBytes(ns->uri)); +#else + const char *buf = NULL; +#endif + JS_MarkGCThing(cx, ns->object, buf, arg); + } else { + namespace_mark_private(cx, ns, arg); + } + } +} + +static uint32 +namespace_mark(JSContext *cx, JSObject *obj, void *arg) +{ + JSXMLNamespace *ns; + + ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj); + namespace_mark_private(cx, ns, arg); + return 0; +} + +static JSClass namespace_class = { + "Namespace", JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE, + JS_PropertyStub, JS_PropertyStub, namespace_getProperty, NULL, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, namespace_finalize, + NULL, NULL, NULL, NULL, + NULL, NULL, namespace_mark, NULL +}; + +enum namespace_tinyid { + NAMESPACE_PREFIX = -1, + NAMESPACE_URI = -2 +}; + +#define NAMESPACE_ATTRS (JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED) + +static JSPropertySpec namespace_props[] = { + {js_prefix_str, NAMESPACE_PREFIX, NAMESPACE_ATTRS, 0, 0}, + {js_uri_str, NAMESPACE_URI, NAMESPACE_ATTRS, 0, 0}, + {0,0,0,0,0} +}; + +static JSBool +namespace_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSXMLNamespace *ns; + + if (!JSVAL_IS_INT(id)) + return JS_TRUE; + + ns = (JSXMLNamespace *) + JS_GetInstancePrivate(cx, obj, &namespace_class, NULL); + if (!ns) + return JS_TRUE; + + switch (JSVAL_TO_INT(id)) { + case NAMESPACE_PREFIX: + *vp = ns->prefix ? STRING_TO_JSVAL(ns->prefix) : JSVAL_VOID; + break; + case NAMESPACE_URI: + *vp = STRING_TO_JSVAL(ns->uri); + break; + } + return JS_TRUE; +} + +static JSBool +namespace_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXMLNamespace *ns; + + ns = (JSXMLNamespace *) + JS_GetInstancePrivate(cx, obj, &namespace_class, argv); + if (!ns) + return JS_FALSE; + + *rval = STRING_TO_JSVAL(ns->uri); + return JS_TRUE; +} + +static JSFunctionSpec namespace_methods[] = { + {js_toString_str, namespace_toString, 0,0,0}, + {0,0,0,0,0} +}; + +JSXMLNamespace * +js_NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri) +{ + JSXMLNamespace *ns; + + ns = (JSXMLNamespace *) JS_malloc(cx, sizeof(JSXMLNamespace)); + if (!ns) + return NULL; + ns->object = NULL; + ns->prefix = prefix; + ns->uri = uri; + return ns; +} + +void +js_DestroyXMLNamespace(JSContext *cx, JSXMLNamespace *ns) +{ + if (!ns->object) + JS_free(cx, ns); +} + +JSObject * +js_NewXMLNamespaceObject(JSContext *cx, JSString *prefix, JSString *uri) +{ + JSXMLNamespace *ns; + JSObject *obj; + + ns = js_NewXMLNamespace(cx, prefix, uri); + if (!ns) + return NULL; + obj = js_GetXMLNamespaceObject(cx, ns); + if (!obj) + js_DestroyXMLNamespace(cx, ns); + return obj; +} + +JSObject * +js_GetXMLNamespaceObject(JSContext *cx, JSXMLNamespace *ns) +{ + JSObject *obj; + + obj = ns->object; + if (obj) { + JS_ASSERT(JS_GetPrivate(cx, obj) == ns); + return obj; + } + obj = js_NewObject(cx, &namespace_class, NULL, NULL); + if (!obj || !JS_SetPrivate(cx, obj, ns)) { + cx->newborn[GCX_OBJECT] = NULL; + return NULL; + } + ns->object = obj; + return obj; +} + +/* + * QName class and library functions. + */ +static JSBool +qname_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +static void +qname_finalize(JSContext *cx, JSObject *obj) +{ + JSXMLQName *qn; + + qn = (JSXMLQName *) JS_GetPrivate(cx, obj); + if (qn) { + JS_ASSERT(qn->object); + qn->object = NULL; + js_DestroyXMLQName(cx, qn); + } +} + +static void +qname_mark_private(JSContext *cx, JSXMLQName *qn, void *arg) +{ + JS_MarkGCThing(cx, qn->uri, js_uri_str, arg); + JS_MarkGCThing(cx, qn->prefix, js_prefix_str, arg); + JS_MarkGCThing(cx, qn->localName, js_localName_str, arg); +} + +static uint32 +qname_mark(JSContext *cx, JSObject *obj, void *arg) +{ + JSXMLQName *qn; + + qn = (JSXMLQName *) JS_GetPrivate(cx, obj); + qname_mark_private(cx, qn, arg); + return 0; +} + +static JSClass qname_class = { + "QName", JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE, + JS_PropertyStub, JS_PropertyStub, qname_getProperty, NULL, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, qname_finalize, + NULL, NULL, NULL, NULL, + NULL, NULL, qname_mark, NULL +}; + +/* + * Uninitialized classes for AttributeName and AnyName, which are like QName, + * except that they're never constructed and they have no getters. + */ +static JSClass attribute_name_class = { + "AttributeName", JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, qname_finalize, + NULL, NULL, NULL, NULL, + NULL, NULL, qname_mark, NULL +}; + +static JSClass any_name_class = { + "AnyName", JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, qname_finalize, + NULL, NULL, NULL, NULL, + NULL, NULL, qname_mark, NULL +}; + +enum qname_tinyid { + QNAME_URI = -1, + QNAME_LOCALNAME = -2 +}; + +#define QNAME_ATTRS (JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED) + +static JSPropertySpec qname_props[] = { + {js_uri_str, QNAME_URI, QNAME_ATTRS, 0, 0}, + {js_localName_str, QNAME_LOCALNAME, QNAME_ATTRS, 0, 0}, + {0,0,0,0,0} +}; + +static JSBool +qname_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSXMLQName *qn; + + if (!JSVAL_IS_INT(id)) + return JS_TRUE; + + qn = (JSXMLQName *) JS_GetInstancePrivate(cx, obj, &qname_class, NULL); + if (!qn) + return JS_TRUE; + + switch (JSVAL_TO_INT(id)) { + case QNAME_URI: + *vp = qn->uri ? STRING_TO_JSVAL(qn->uri) : JSVAL_NULL; + break; + case QNAME_LOCALNAME: + *vp = STRING_TO_JSVAL(qn->localName); + break; + } + return JS_TRUE; +} + +static JSBool +qname_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXMLQName *qn; + JSString *str, *qualstr; + + qn = (JSXMLQName *) JS_GetInstancePrivate(cx, obj, &qname_class, argv); + if (!qn) + return JS_FALSE; + + if (!qn->uri) { + /* No uri means wildcard qualifier. */ + str = ATOM_TO_STRING(cx->runtime->atomState.starQualifierAtom); + } else if (IS_EMPTY(qn->uri)) { + /* Empty string for uri means localName is in no namespace. */ + str = cx->runtime->emptyString; + } else { + qualstr = ATOM_TO_STRING(cx->runtime->atomState.qualifierAtom); + str = js_ConcatStrings(cx, qn->uri, qualstr); + if (!str) + return JS_FALSE; + } + str = js_ConcatStrings(cx, str, qn->localName); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSFunctionSpec qname_methods[] = { + {js_toString_str, qname_toString, 0,0,0}, + {0,0,0,0,0} +}; + +JSXMLQName * +js_NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, + JSString *localName) +{ + JSXMLQName *qn; + + qn = (JSXMLQName *) JS_malloc(cx, sizeof(JSXMLQName)); + if (!qn) + return NULL; + qn->object = NULL; + qn->uri = uri; + qn->prefix = prefix; + qn->localName = localName; + return qn; +} + +void +js_DestroyXMLQName(JSContext *cx, JSXMLQName *qn) +{ + if (!qn->object) + JS_free(cx, qn); +} + +JSObject * +js_NewXMLQNameObject(JSContext *cx, JSString *uri, JSString *prefix, + JSString *localName) +{ + JSXMLQName *qn; + JSObject *obj; + + qn = js_NewXMLQName(cx, uri, prefix, localName); + if (!qn) + return NULL; + obj = js_GetXMLQNameObject(cx, qn); + if (!obj) + js_DestroyXMLQName(cx, qn); + return obj; +} + +JSObject * +js_GetXMLQNameObject(JSContext *cx, JSXMLQName *qn) +{ + JSObject *obj; + + obj = qn->object; + if (obj) { + JS_ASSERT(JS_GetPrivate(cx, obj) == qn); + return obj; + } + obj = js_NewObject(cx, &qname_class, NULL, NULL); + if (!obj || !JS_SetPrivate(cx, obj, qn)) { + cx->newborn[GCX_OBJECT] = NULL; + return NULL; + } + qn->object = obj; + return obj; +} + +JSObject * +js_ConstructQNameObject(JSContext *cx, jsval nsval, jsval lnval) +{ + jsval argv[2]; + + /* + * ECMA-357 11.1.2, + * The _QualifiedIdentifier : PropertySelector :: PropertySelector_ + * production, step 2. + */ + if (!JSVAL_IS_PRIMITIVE(nsval) && + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nsval)) == &any_name_class) { + nsval = JSVAL_NULL; + } + + argv[0] = nsval; + argv[1] = lnval; + return js_ConstructObject(cx, &qname_class, NULL, NULL, 2, argv); +} + +static JSBool +IsXMLName(const jschar *cp, size_t n) +{ + JSBool rv; + jschar c; + + rv = JS_FALSE; + if (n != 0 && JS_ISXMLNSSTART(*cp)) { + while (--n != 0) { + c = *++cp; + if (!JS_ISXMLNS(c)) + return rv; + } + rv = JS_TRUE; + } + return rv; +} + +JSBool +js_IsXMLName(JSContext *cx, jsval v) +{ + JSBool rv; + JSErrorReporter older; + JSXMLQName *qn; + JSString *name; + + rv = JS_FALSE; + older = JS_SetErrorReporter(cx, NULL); + + /* + * Inline specialization of the QName constructor called with v passed as + * the only argument, to compute the localName for the constructed qname, + * without actually allocating the object or computing its uri and prefix. + * See ECMA-357 13.1.2.1 step 1 and 13.3.2. + */ + if (!JSVAL_IS_PRIMITIVE(v) && + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &qname_class) { + qn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); + name = qn->localName; + } else { + name = js_ValueToString(cx, v); + if (!name) + JS_ClearPendingException(cx); + } + + if (name) + rv = IsXMLName(JSSTRING_CHARS(name), JSSTRING_LENGTH(name)); + + JS_SetErrorReporter(cx, older); + return rv; +} + +static JSBool +Namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval urival, prefixval; + JSObject *uriobj; + JSBool isNamespace, isQName; + JSClass *clasp; + JSString *empty, *prefix; + JSXMLNamespace *ns, *ns2; + JSXMLQName *qn; + + urival = argv[argc > 1]; + isNamespace = isQName = JS_FALSE; + if (!JSVAL_IS_PRIMITIVE(urival)) { + uriobj = JSVAL_TO_OBJECT(urival); + clasp = OBJ_GET_CLASS(cx, uriobj); + isNamespace = (clasp == &namespace_class); + isQName = (clasp == &qname_class); + } +#ifdef __GNUC__ /* suppress bogus gcc warnings */ + else uriobj = NULL; +#endif + + if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { + /* Namespace called as function. */ + if (argc == 1 && isNamespace) { + /* Namespace called with one Namespace argument is identity. */ + *rval = urival; + return JS_TRUE; + } + + /* Create and return a new QName object exactly as if constructed. */ + obj = js_NewObject(cx, &namespace_class, NULL, NULL); + if (!obj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + } + + /* + * Create and connect private data to rooted obj early, so we don't have + * to worry about rooting string newborns hanging off of the private data + * further below. + */ + empty = cx->runtime->emptyString; + ns = js_NewXMLNamespace(cx, empty, empty); + if (!ns) + return JS_FALSE; + if (!JS_SetPrivate(cx, obj, ns)) { + js_DestroyXMLNamespace(cx, ns); + return JS_FALSE; + } + ns->object = obj; + + if (argc == 1) { + if (isNamespace) { + ns2 = (JSXMLNamespace *) JS_GetPrivate(cx, uriobj); + ns->uri = ns2->uri; + ns->prefix = ns2->prefix; + } else if (isQName && + (qn = (JSXMLQName *) JS_GetPrivate(cx, uriobj))->uri) { + ns->uri = qn->uri; + ns->prefix = qn->prefix; + } else { + ns->uri = js_ValueToString(cx, urival); + if (!ns->uri) + return JS_FALSE; + + /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */ + if (!IS_EMPTY(ns->uri)) + ns->prefix = NULL; + } + } else if (argc == 2) { + if (isQName && + (qn = (JSXMLQName *) JS_GetPrivate(cx, uriobj))->uri) { + ns->uri = qn->uri; + } else { + ns->uri = js_ValueToString(cx, urival); + if (!ns->uri) + return JS_FALSE; + } + + prefixval = argv[0]; + if (IS_EMPTY(ns->uri)) { + if (!JSVAL_IS_VOID(prefixval)) { + prefix = js_ValueToString(cx, prefixval); + if (!prefix) + return JS_FALSE; + if (!IS_EMPTY(prefix)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_NAMESPACE); + return JS_FALSE; + } + } + } else if (JSVAL_IS_VOID(prefixval) || !js_IsXMLName(cx, prefixval)) { + /* NULL here represents *undefined* in ECMA-357 13.2.2 4(d) etc. */ + ns->prefix = NULL; + } else { + prefix = js_ValueToString(cx, prefixval); + if (!prefix) + return JS_FALSE; + ns->prefix = prefix; + } + } + + return JS_TRUE; +} + +static JSBool +QName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval nameval, nsval; + JSBool isQName, isNamespace; + JSXMLQName *qn; + JSString *uri, *prefix, *name; + JSObject *nsobj; + JSClass *clasp; + JSXMLNamespace *ns; + + nameval = argv[argc > 1]; + isQName = !JSVAL_IS_PRIMITIVE(nameval) && + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nameval)) == &qname_class; + + if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { + /* QName called as function. */ + if (argc == 1 && isQName) { + /* QName called with one QName argument is identity. */ + *rval = nameval; + return JS_TRUE; + } + + /* Create and return a new QName object exactly as if constructed. */ + obj = js_NewObject(cx, &qname_class, NULL, NULL); + if (!obj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(obj); + } + + if (isQName) { + /* If namespace is not specified and name is a QName, clone it. */ + qn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nameval)); + if (argc == 1) { + uri = qn->uri; + prefix = qn->prefix; + name = qn->localName; + goto out; + } + + /* Namespace and qname were passed -- use the qname's localName. */ + nameval = STRING_TO_JSVAL(qn->localName); + } + + if (argc == 0) { + name = cx->runtime->emptyString; + } else { + name = js_ValueToString(cx, nameval); + if (!name) + return JS_FALSE; + + /* Use argv[1] as a local root for name, even if it was not passed. */ + argv[1] = STRING_TO_JSVAL(name); + } + + nsval = argv[0]; + if (argc == 1 || JSVAL_IS_VOID(nsval)) { + if (IS_STAR(name)) { + nsval = JSVAL_NULL; + } else { + if (!js_GetDefaultXMLNamespace(cx, &nsval)) + return JS_FALSE; + } + } + + if (JSVAL_IS_NULL(nsval)) { + uri = prefix = NULL; + } else { + /* + * Inline specialization of the Namespace constructor called with + * nsval passed as the only argument, to compute the uri and prefix + * for the constructed namespace, without actually allocating the + * object or computing other members. See ECMA-357 13.3.2 6(a) and + * 13.2.2. + */ + isNamespace = isQName = JS_FALSE; + if (!JSVAL_IS_PRIMITIVE(nsval)) { + nsobj = JSVAL_TO_OBJECT(nsval); + clasp = OBJ_GET_CLASS(cx, nsobj); + isNamespace = (clasp == &namespace_class); + isQName = (clasp == &qname_class); + } +#ifdef __GNUC__ /* suppress bogus gcc warnings */ + else nsobj = NULL; +#endif + + if (isNamespace) { + ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); + uri = ns->uri; + prefix = ns->prefix; + } else if (isQName && + (qn = (JSXMLQName *) JS_GetPrivate(cx, nsobj))->uri) { + uri = qn->uri; + prefix = qn->prefix; + } else { + uri = js_ValueToString(cx, nsval); + if (!uri) + return JS_FALSE; + + /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */ + prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL; + } + } + +out: + qn = js_NewXMLQName(cx, uri, prefix, name); + if (!qn) + return JS_FALSE; + if (!JS_SetPrivate(cx, obj, qn)) { + js_DestroyXMLQName(cx, qn); + return JS_FALSE; + } + qn->object = obj; + return JS_TRUE; +} + +/* + * XMLArray library functions. + */ +static JSBool +namespace_identity(const void *a, const void *b) +{ + const JSXMLNamespace *nsa = (const JSXMLNamespace *) a; + const JSXMLNamespace *nsb = (const JSXMLNamespace *) b; + + if (nsa->prefix && nsb->prefix && + js_CompareStrings(nsa->prefix, nsb->prefix)) { + return JS_FALSE; + } + if (nsa->prefix || nsb->prefix) + return JS_FALSE; + return !js_CompareStrings(nsa->uri, nsb->uri); +} + +static JSBool +attr_identity(const void *a, const void *b) +{ + const JSXML *xmla = (const JSXML *) a; + const JSXML *xmlb = (const JSXML *) b; + JSXMLQName *qna = xmla->name; + JSXMLQName *qnb = xmlb->name; + + JS_ASSERT(qna && qnb); + if (!qna->uri ^ !qnb->uri) + return JS_FALSE; + if (qna->uri && js_CompareStrings(qna->uri, qnb->uri)) + return JS_FALSE; + return !js_CompareStrings(qna->localName, qnb->localName); +} + +JSBool +XMLArrayInit(JSContext *cx, JSXMLArray *set, uint32 length) +{ + set->count = 0; + set->length = length; + if (length == 0) { + set->vector = NULL; + } else { + if ((size_t)length > ~(size_t)0 / sizeof(void *) || + !(set->vector = (void **) calloc(length, sizeof(void *)))) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + } + return JS_TRUE; +} + +typedef void +(* JS_DLL_CALLBACK JSXMLArrayElemDtor)(JSContext *cx, void *elt); + +void +XMLArrayFinish(JSContext *cx, JSXMLArray *set, JSXMLArrayElemDtor dtor) +{ + uint32 i, n; + + for (i = 0, n = set->count; i < n; i++) + dtor(cx, set->vector[i]); + JS_free(cx, set->vector); +#ifdef DEBUG + memset(set, 0xd5, sizeof *set); +#endif +} + +#define NOT_FOUND ((uint32) -1) + +uint32 +XMLArrayHasMember(JSXMLArray *set, void *elt, JSIdentityOp identity) +{ + void **vector; + uint32 i, n; + + /* The identity op must not reallocate set->vector. */ + vector = set->vector; + if (identity) { + for (i = 0, n = set->count; i < n; i++) { + if (identity(vector[i], elt)) + return i; + } + } else { + for (i = 0, n = set->count; i < n; i++) { + if (vector[i] == elt) + return i; + } + } + return NOT_FOUND; +} + +/* + * Grow set vector length by powers of two till LINEAR_THRESHOLD, after which + * we grow it by LINEAR_INCREMENT. Both must be powers of two, and threshold + * should be greater than increment. + */ +#define LINEAR_THRESHOLD 256 +#define LINEAR_INCREMENT 32 + +JSBool +XMLArrayInsert(JSContext *cx, JSXMLArray *set, uint32 index, void *elt) +{ + uint32 length, i; + int log2; + + if (index >= set->count) { + if (index >= set->length) { + length = index + 1; + if (index >= LINEAR_THRESHOLD) { + length = JS_ROUNDUP(length, LINEAR_INCREMENT); + } else { + JS_CEILING_LOG2(log2, length); + length = JS_BIT(log2); + } + if ((size_t)length > ~(size_t)0 / sizeof(void *) || + !(set->vector = (void **) + realloc(set->vector, + length * sizeof(void *)))) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + set->length = length; + for (i = set->count; i < index; i++) + set->vector[i] = NULL; + } + set->count = index + 1; + } + + set->vector[index] = elt; + return JS_TRUE; +} + +void * +XMLArrayDelete(JSContext *cx, JSXMLArray *set, uint32 index, JSBool compress) +{ + uint32 count; + void **vector, *elt; + + count = set->count; + if (index >= count) + return NULL; + + vector = set->vector; + elt = vector[index]; + if (compress) { + while (++index < count) + vector[index-1] = vector[index]; + set->count = count - 1; + } else { + vector[index] = NULL; + } + return elt; +} + +#define XMLARRAY_INIT(x,a,l) XMLArrayInit(x, a, l) +#define XMLARRAY_FINISH(x,a,d) XMLArrayFinish(x, a, (JSXMLArrayElemDtor)d) +#define XMLARRAY_HAS_MEMBER(a,e,i) XMLArrayHasMember(a, (void *)(e), i) +#define XMLARRAY_MEMBER(a,i,t) ((t *) (a)->vector[i]) +#define XMLARRAY_SET_MEMBER(a,i,e) ((a)->vector[i] = (void *)(e)) +#define XMLARRAY_INSERT(x,a,i,e) XMLArrayInsert(x, a, i, (void *)(e)) +#define XMLARRAY_APPEND(x,a,e) XMLArrayInsert(x, a, (a)->count, (void*)(e)) +#define XMLARRAY_DELETE(x,a,i,c,t) ((t *) XMLArrayDelete(x, a, i, c)) + +/* + * XML helper, object-ops, and library functions. We start with the helpers, + * in ECMA-357 order, but merging XML (9.1) and XMLList (9.2) helpers. + */ +static JSXML * +ParseXMLSource(JSContext *cx, JSString *src) +{ + jsval nsval; + JSXMLNamespace *ns; + size_t urilen, srclen, length, offset; + jschar *chars; + void *mark; + JSTokenStream *ts; + JSParseNode *pn; + JSXML *xml; + + static const char prefix[] = ""; + static const char suffix[] = ""; + +#define constrlen(constr) (sizeof(constr) - 1) + + if (!js_GetDefaultXMLNamespace(cx, &nsval)) + return NULL; + ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval)); + + urilen = JSSTRING_LENGTH(ns->uri); + srclen = JSSTRING_LENGTH(src); + length = constrlen(prefix) + urilen + constrlen(middle) + srclen + + constrlen(suffix); + + chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); + if (!chars) + return NULL; + + js_InflateStringToBuffer(chars, prefix, constrlen(prefix)); + offset = constrlen(prefix); + js_strncpy(chars + offset, JSSTRING_CHARS(ns->uri), urilen); + offset += urilen; + js_InflateStringToBuffer(chars + offset, middle, constrlen(middle)); + offset += constrlen(middle); + js_strncpy(chars + offset, JSSTRING_CHARS(src), srclen); + offset += srclen; + js_InflateStringToBuffer(chars + offset, suffix, constrlen(suffix)); + + mark = JS_ARENA_MARK(&cx->tempPool); + ts = js_NewBufferTokenStream(cx, chars, length); + if (!ts) + return NULL; + + pn = js_ParseXMLTokenStream(cx, cx->fp->scopeChain, ts, JS_FALSE); + if (!pn) { + xml = NULL; + } else { + xml = js_ParseNodeToXML(cx, pn, 0); + if (!xml) + return JS_FALSE; + } + + JS_ARENA_RELEASE(&cx->tempPool, mark); + JS_free(cx, chars); + return xml; + +#undef constrlen +} + +static JSXML * +ToXML(JSContext *cx, jsval v) +{ + JSObject *obj; + JSClass *clasp; + JSXML *xml; + JSString *str; + uint32 length; + + if (JSVAL_IS_PRIMITIVE(v)) { + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) + goto bad; + } else { + obj = JSVAL_TO_OBJECT(v); + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp == &js_XMLClass) { + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (xml->xml_class == JSXML_CLASS_LIST) { + if (xml->xml_kids.count != 1) + goto bad; + xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST); + } + return xml; + } + + if (clasp->flags & JSCLASS_W3C_XML_INFO_ITEM) { + } + + if (clasp != &js_StringClass && + clasp != &js_NumberClass && + clasp != &js_BooleanClass) { + goto bad; + } + } + + str = js_ValueToString(cx, v); + if (!str) + return NULL; + if (IS_EMPTY(str)) { + length = 0; + } else { + xml = ParseXMLSource(cx, str); + if (!xml) + return NULL; + length = JSXML_LENGTH(xml); + } + + if (length == 0) { + obj = js_NewXMLObject(cx, JSXML_CLASS_TEXT); + if (!obj) + return NULL; + xml = (JSXML *) JS_GetPrivate(cx, obj); + xml->xml_value = cx->runtime->emptyString; + } else if (length == 1) { + xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + if (!js_GetXMLObject(cx, xml)) + return NULL; + xml->parent = NULL; + } else { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SYNTAX_ERROR); + return NULL; + } + return xml; + +bad: + str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_CONVERSION, + JS_GetStringBytes(str)); + } + return NULL; +} + +static JSBool +Append(JSContext *cx, JSXML *list, JSXML *kid); + +static JSXML * +ToXMLList(JSContext *cx, jsval v) +{ + JSObject *obj, *listobj; + JSClass *clasp; + JSXML *xml, *list, *kid; + JSString *str; + uint32 i, length; + + if (JSVAL_IS_PRIMITIVE(v)) { + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) + goto bad; + } else { + obj = JSVAL_TO_OBJECT(v); + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp == &js_XMLClass) { + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (xml->xml_class != JSXML_CLASS_LIST) { + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + return NULL; + list = (JSXML *) JS_GetPrivate(cx, listobj); + if (!Append(cx, list, xml)) + return NULL; + return list; + } + return xml; + } + + if (clasp->flags & JSCLASS_W3C_XML_INFO_ITEM) { + JS_ASSERT(0); + } + + if (clasp != &js_StringClass && + clasp != &js_NumberClass && + clasp != &js_BooleanClass) { + goto bad; + } + } + + str = js_ValueToString(cx, v); + if (!str) + return NULL; + if (IS_EMPTY(str)) { + length = 0; +#ifdef __GNUC__ /* suppress bogus gcc warnings */ + xml = NULL; +#endif + } else { + xml = ParseXMLSource(cx, str); + if (!xml) + return NULL; + length = JSXML_LENGTH(xml); + } + + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + return NULL; + list = (JSXML *) JS_GetPrivate(cx, listobj); + for (i = 0; i < length; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + kid->parent = NULL; + if (!Append(cx, list, kid)) + return NULL; + } + return list; + +bad: + str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XMLLIST_CONVERSION, + JS_GetStringBytes(str)); + } + return NULL; +} + +/* + * ECMA-357 10.2.1 17(d-g) pulled out into a common subroutine that appends + * equals, a double quote, an attribute value, and a closing double quote. + */ +static void +AppendAttributeValue(JSStringBuffer *sb, JSString *valstr) +{ + js_AppendCString(sb, "=\""); + js_AppendJSString(sb, valstr); + js_AppendChar(sb, '"'); +} + +/* + * ECMA-357 10.2.1.1 EscapeElementValue helper method. + * This function takes ownership of sb->base, if sb is non-null, in all cases. + */ +static JSString * +EscapeElementValue(JSContext *cx, JSStringBuffer *sb, JSString *str) +{ + size_t length, newlength; + const jschar *cp, *start, *end; + jschar c; + + length = newlength = JSSTRING_LENGTH(str); + for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) { + c = *cp; + if (c == '<' || c == '>') + newlength += 3; + else if (c == '&') + newlength += 4; + } + if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) { + JSStringBuffer localSB; + if (!sb) { + sb = &localSB; + js_InitStringBuffer(sb); + } + if (!sb->grow(sb, STRING_BUFFER_OFFSET(sb) + newlength)) { + JS_ReportOutOfMemory(cx); + return NULL; + } + for (cp = start; cp < end; cp++) { + c = *cp; + if (c == '<') + js_AppendCString(sb, js_lt_entity_str); + else if (c == '>') + js_AppendCString(sb, js_gt_entity_str); + else if (c == '&') + js_AppendCString(sb, js_amp_entity_str); + else + js_AppendChar(sb, c); + } + JS_ASSERT(STRING_BUFFER_OK(sb)); + str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb), 0); + if (!str) + js_FinishStringBuffer(sb); + } + return str; +} + +/* + * ECMA-357 10.2.1.2 EscapeAttributeValue helper method. + * This function takes ownership of sb->base, if sb is non-null, in all cases. + */ +static JSString * +EscapeAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *str) +{ + size_t length, newlength; + const jschar *cp, *start, *end; + jschar c; + + length = newlength = JSSTRING_LENGTH(str); + for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) { + c = *cp; + if (c == '"') + newlength += 5; + else if (c == '<') + newlength += 3; + else if (c == '&' || c == '\n' || c == '\r' || c == '\t') + newlength += 4; + } + if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) { + JSStringBuffer localSB; + if (!sb) { + sb = &localSB; + js_InitStringBuffer(sb); + } + if (!sb->grow(sb, STRING_BUFFER_OFFSET(sb) + newlength)) { + JS_ReportOutOfMemory(cx); + return NULL; + } + for (cp = start; cp < end; cp++) { + c = *cp; + if (c == '"') + js_AppendCString(sb, js_quot_entity_str); + else if (c == '<') + js_AppendCString(sb, js_lt_entity_str); + else if (c == '&') + js_AppendCString(sb, js_amp_entity_str); + else if (c == '\n') + js_AppendCString(sb, " "); + else if (c == '\r') + js_AppendCString(sb, " "); + else if (c == '\t') + js_AppendCString(sb, " "); + else + js_AppendChar(sb, c); + } + JS_ASSERT(STRING_BUFFER_OK(sb)); + str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb), 0); + if (!str) + js_FinishStringBuffer(sb); + } + return str; +} + +static const char js_ignoreComments_str[] = "ignoreComments"; +static const char js_ignoreProcessingInstructions_str[] + = "ignoreProcessingInstructions"; +static const char js_ignoreWhitespace_str[] = "ignoreWhitespace"; +static const char js_prettyPrinting_str[] = "prettyPrinting"; +static const char js_prettyIndent_str[] = "prettyIndent"; + +static JSBool +GetXMLSetting(JSContext *cx, const char *name, jsval *vp) +{ + jsval v; + + if (!js_FindConstructor(cx, NULL, js_XML_str, &v)) + return JS_FALSE; + if (!JSVAL_IS_FUNCTION(cx, v)) { + *vp = JSVAL_VOID; + return JS_TRUE; + } + return JS_GetProperty(cx, JSVAL_TO_OBJECT(v), name, vp); +} + +static JSString * +ChompXMLWhitespace(JSContext *cx, JSString *str) +{ + size_t length, newlength, offset; + const jschar *cp, *start, *end; + jschar c; + + length = JSSTRING_LENGTH(str); + for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) { + c = *cp; + if (!JS_ISXMLSPACE(c)) + break; + } + while (end > cp) { + c = end[-1]; + if (!JS_ISXMLSPACE(c)) + break; + --end; + } + newlength = PTRDIFF(end, cp, jschar); + if (newlength == length) + return str; + offset = PTRDIFF(cp, start, jschar); + return js_NewDependentString(cx, str, offset, newlength, 0); +} + +/* 13.3.5.4 [[GetNamespace]]([InScopeNamespaces]) */ +static JSXMLNamespace * +GetNamespace(JSContext *cx, JSXMLQName *qn, JSXMLArray *inScopeNSes) +{ + JSXMLNamespace *match, *ns; + uint32 i, n; + + JS_ASSERT(qn->uri); + if (!qn->uri) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_NAMESPACE); + return NULL; + } + + /* Look for a matching namespace in inScopeNSes, if provided. */ + match = NULL; + if (inScopeNSes) { + for (i = 0, n = inScopeNSes->count; i < n; i++) { + ns = XMLARRAY_MEMBER(inScopeNSes, i, JSXMLNamespace); + if (!js_CompareStrings(ns->uri, qn->uri) && + (ns->prefix == qn->prefix || + (ns->prefix && qn->prefix && + !js_CompareStrings(ns->prefix, qn->prefix)))) { + match = ns; + break; + } + } + } + + /* If we didn't match, make a new namespace from qn. */ + if (!match) + match = js_NewXMLNamespace(cx, qn->prefix, qn->uri); + + /* Share ns reference by converting it to GC'd allocation. */ + if (match && !js_GetXMLNamespaceObject(cx, match)) + return NULL; + return match; +} + +static JSString * +GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls) +{ + const jschar *cp, *start, *end; + size_t length, newlength, offset; + uint32 i, n, m, serial; + jschar *bp, *dp; + JSBool done; + JSXMLNamespace *ns; + JSString *prefix; + + /* + * Try peeling off the last filename suffix or pathname component till + * we have a valid XML name. This heuristic will prefer "xul" given + * ".../there.is.only.xul", "xbl" given ".../xbl", and "xbl2" given any + * likely URI of the form ".../xbl2/2005". + */ + start = JSSTRING_CHARS(uri); + cp = end = start + JSSTRING_LENGTH(uri); + while (--cp >= start) { + if (*cp == '.' || *cp == '/') { + ++cp; + if (IsXMLName(cp, PTRDIFF(end, cp, jschar))) + break; + end = --cp; + } + } + length = PTRDIFF(end, cp, jschar); + + /* + * Now search through decls looking for a collision. If we collide with + * an existing prefix, start tacking on a hyphen and a serial number. + */ + serial = 0; + bp = NULL; +#ifdef __GNUC__ /* suppress bogus gcc warnings */ + newlength = 0; +#endif + do { + done = JS_TRUE; + for (i = 0, n = decls->count; i < n; i++) { + ns = XMLARRAY_MEMBER(decls, i, JSXMLNamespace); + if (ns->prefix && + JSSTRING_LENGTH(ns->prefix) == length && + !memcmp(JSSTRING_CHARS(ns->prefix), cp, length)) { + if (!bp) { + newlength = length + 2 + (size_t) log10(n); + bp = (jschar *) + JS_malloc(cx, (newlength + 1) * sizeof(jschar)); + if (!bp) + return NULL; + js_strncpy(bp, cp, length); + } + + ++serial; + JS_ASSERT(serial < n); + dp = bp + length + 2 + (size_t) log10(serial); + *dp = 0; + for (m = serial; m != 0; m /= 10) + *--dp = '0' + m % 10; + *--dp = '-'; + JS_ASSERT(dp == cp + length); + + done = JS_FALSE; + break; + } + } + } while (!done); + + if (!bp) { + offset = PTRDIFF(cp, start, jschar); + prefix = js_NewDependentString(cx, uri, offset, length, 0); + } else { + prefix = js_NewString(cx, bp, newlength, 0); + if (!prefix) + JS_free(cx, bp); + } + return prefix; +} + +/* ECMA-357 10.2.1 and 10.2.2 */ +static JSString * +XMLToXMLString(JSContext *cx, JSXML *xml, JSXMLArray *ancestorNSes, + uintN indentLevel) +{ + jsval v; + JSBool pretty, indentKids; + JSStringBuffer sb; + JSString *str, *name, *prefix, *kidstr; + size_t length, newlength, namelength; + jschar *bp, *base; + uint32 i, n; + JSXMLArray empty, decls, ancdecls; + JSXMLNamespace *ns, *ns2; + uintN nextIndentLevel; + JSXML *attr, *kid; + + static const jschar comment_prefix_ucNstr[] = {'<', '!', '-', '-'}; + static const jschar comment_suffix_ucNstr[] = {'-', '-', '>'}; + static const jschar pi_prefix_ucNstr[] = {'<', '?'}; + static const jschar pi_suffix_ucNstr[] = {'?', '>'}; + + if (!GetXMLSetting(cx, js_prettyPrinting_str, &v)) + return NULL; + if (!js_ValueToBoolean(cx, v, &pretty)) + return NULL; + + js_InitStringBuffer(&sb); + if (pretty) + js_RepeatChar(&sb, ' ', indentLevel); + str = NULL; + + /* XXXbe Erratum: shouldn't CDATA be handled specially? */ + switch (xml->xml_class) { + case JSXML_CLASS_TEXT: + /* Step 4. */ + if (pretty) { + str = ChompXMLWhitespace(cx, xml->xml_value); + if (!str) + return NULL; + } else { + str = xml->xml_value; + } + return EscapeElementValue(cx, &sb, str); + + case JSXML_CLASS_ATTRIBUTE: + /* Step 5. */ + return EscapeAttributeValue(cx, &sb, xml->xml_value); + + case JSXML_CLASS_COMMENT: + /* Step 6. */ + str = xml->xml_value; + length = JSSTRING_LENGTH(str); + newlength = STRING_BUFFER_OFFSET(&sb) + 4 + length + 3; + bp = base = (jschar *) + JS_realloc(cx, sb.base, (newlength + 1) * sizeof(jschar)); + if (!bp) { + js_FinishStringBuffer(&sb); + return NULL; + } + + bp += STRING_BUFFER_OFFSET(&sb); + js_strncpy(bp, comment_prefix_ucNstr, 4); + bp += 4; + js_strncpy(bp, JSSTRING_CHARS(str), length); + bp += length; + js_strncpy(bp, comment_suffix_ucNstr, 3); + bp[3] = 0; + str = js_NewString(cx, base, newlength, 0); + if (!str) + free(base); + return str; + + case JSXML_CLASS_PROCESSING_INSTRUCTION: + /* Step 7. */ + name = xml->name->localName; + namelength = JSSTRING_LENGTH(name); + str = xml->xml_value; + length = JSSTRING_LENGTH(str); + newlength = STRING_BUFFER_OFFSET(&sb) + 2 + namelength + 1 + length + 2; + bp = base = (jschar *) + JS_realloc(cx, sb.base, (newlength + 1) * sizeof(jschar)); + if (!bp) { + js_FinishStringBuffer(&sb); + return NULL; + } + + bp += STRING_BUFFER_OFFSET(&sb); + js_strncpy(bp, pi_prefix_ucNstr, 2); + bp += 2; + js_strncpy(bp, JSSTRING_CHARS(name), namelength); + bp += namelength; + *bp++ = (jschar) ' '; + js_strncpy(bp, JSSTRING_CHARS(str), length); + bp += length; + js_strncpy(bp, pi_suffix_ucNstr, 2); + bp[2] = 0; + str = js_NewString(cx, base, newlength, 0); + if (!str) + free(base); + return str; + + case JSXML_CLASS_LIST: + /* ECMA-357 10.2.2. */ + for (i = 0, n = xml->xml_kids.count; i < n; i++) { + if (pretty && i != 0) + js_AppendChar(&sb, '\n'); + + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + kidstr = XMLToXMLString(cx, kid, ancestorNSes, indentLevel); + if (!kidstr) + goto list_out; + + js_AppendJSString(&sb, kidstr); + } + + if (!sb.base) { + if (!STRING_BUFFER_OK(&sb)) { + JS_ReportOutOfMemory(cx); + return NULL; + } + return cx->runtime->emptyString; + } + + str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb), 0); + list_out: + if (!str) + js_FinishStringBuffer(&sb); + return str; + + default:; + } + + /* ECMA-357 10.2.1 step 8 onward: handle ToXMLString on an XML element. */ + if (!ancestorNSes) { + XMLARRAY_INIT(cx, &empty, 0); + ancestorNSes = ∅ + } + XMLARRAY_INIT(cx, &decls, 0); + ancdecls.length = 0; + + /* Clone in-scope namespaces not in ancestorNSes into decls. */ + for (i = 0, n = xml->xml_namespaces.count; i < n; i++) { + ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); + if (!XMLARRAY_HAS_MEMBER(ancestorNSes, ns, namespace_identity)) { + /* NOTE: may want to exclude unused namespaces here. */ + ns2 = js_NewXMLNamespace(cx, ns->prefix, ns->uri); + if (!ns2) + goto out; + if (!XMLARRAY_APPEND(cx, &decls, ns2)) { + js_DestroyXMLNamespace(cx, ns2); + goto out; + } + } + } + + /* + * Union ancestorNSes and decls into ancdecls. Note that ancdecls does + * not own its member references. In the spec, ancdecls has no name, but + * is always written out as (AncestorNamespaces U namespaceDeclarations). + */ + if (!XMLARRAY_INIT(cx, &ancdecls, ancestorNSes->count + decls.count)) + goto out; + for (i = 0, n = ancestorNSes->count; i < n; i++) { + ns2 = XMLARRAY_MEMBER(ancestorNSes, i, JSXMLNamespace); + JS_ASSERT(!XMLARRAY_HAS_MEMBER(&decls, ns2, namespace_identity)); + if (!XMLARRAY_APPEND(cx, &ancdecls, ns2)) + goto out; + } + for (i = 0, n = decls.count; i < n; i++) { + ns2 = XMLARRAY_MEMBER(&decls, i, JSXMLNamespace); + JS_ASSERT(!XMLARRAY_HAS_MEMBER(&ancdecls, ns2, namespace_identity)); + if (!XMLARRAY_APPEND(cx, &ancdecls, ns2)) + goto out; + } + + /* Step 11, except we don't clone ns unless its prefix is undefined. */ + ns = GetNamespace(cx, xml->name, &ancdecls); + if (!ns) + goto out; + + /* Step 12 (NULL means *undefined* here), plus the deferred ns cloning. */ + if (!ns->prefix) { + /* + * Create a namespace prefix that isn't used by any member of decls. + * Assign the new prefix to a copy of ns. + */ + prefix = GeneratePrefix(cx, ns->uri, &ancdecls); + if (!prefix) + goto out; + ns = js_NewXMLNamespace(cx, prefix, ns->uri); + if (!ns) + goto out; + if (!XMLARRAY_APPEND(cx, &decls, ns)) { + js_DestroyXMLNamespace(cx, ns); + goto out; + } + } + + /* Format the element or point-tag into sb. */ + js_AppendChar(&sb, '<'); + + if (!IS_EMPTY(ns->prefix)) { + js_AppendJSString(&sb, ns->prefix); + js_AppendChar(&sb, ':'); + } + js_AppendJSString(&sb, xml->name->localName); + + /* + * Step 16 makes a union to avoid writing two loops in step 17, to share + * common attribute value appending spec-code. We prefer two loops for + * faster code and less data overhead. + */ + + /* Step 17(c): append XML namespace declarations. */ + for (i = 0, n = decls.count; i < n; i++) { + ns2 = XMLARRAY_MEMBER(&decls, i, JSXMLNamespace); + js_AppendCString(&sb, " xmlns"); + + /* 17(c)(ii): NULL means *undefined* here. */ + if (!ns2->prefix) { + prefix = GeneratePrefix(cx, ns2->uri, &ancdecls); + if (!prefix) + goto out; + ns2->prefix = prefix; + } + + /* 17(c)(iii). */ + if (!IS_EMPTY(ns2->prefix)) { + js_AppendChar(&sb, ':'); + js_AppendJSString(&sb, ns2->prefix); + } + + /* 17(d-g). */ + AppendAttributeValue(&sb, ns2->uri); + } + + /* Step 17(b): append attributes. */ + for (i = 0, n = xml->xml_attrs.count; i < n; i++) { + attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); + js_AppendChar(&sb, ' '); + ns2 = GetNamespace(cx, attr->name, ancestorNSes); + if (!ns2) + goto out; + + /* 17(b)(ii): NULL means *undefined* here. */ + if (!ns2->prefix) { + prefix = GeneratePrefix(cx, ns2->uri, &ancdecls); + if (!prefix) + goto out; + + /* Again, we avoid copying ns2 until we know it's prefix-less. */ + ns2 = js_NewXMLNamespace(cx, prefix, ns2->uri); + if (!ns2) + goto out; + if (!XMLARRAY_APPEND(cx, &decls, ns2)) { + js_DestroyXMLNamespace(cx, ns2); + goto out; + } + } + + /* 17(b)(iii). */ + if (!IS_EMPTY(ns2->prefix)) { + js_AppendJSString(&sb, ns2->prefix); + js_AppendChar(&sb, ':'); + } + + /* 17(b)(iv). */ + js_AppendJSString(&sb, attr->name->localName); + + /* 17(d-g). */ + AppendAttributeValue(&sb, attr->xml_value); + } + + /* Step 18: handle point tags. */ + n = xml->xml_kids.count; + if (n == 0) { + js_AppendCString(&sb, "/>"); + } else { + /* Steps 19 through 25: handle element tag content, and the end-tag. */ + js_AppendChar(&sb, '>'); + indentKids = n > 1 || + (n == 1 && + XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML)->xml_class + != JSXML_CLASS_TEXT); + + if (pretty && indentKids) { + if (!GetXMLSetting(cx, js_prettyIndent_str, &v)) + return NULL; + if (!js_ValueToECMAUint32(cx, v, &i)) + return NULL; + nextIndentLevel = indentLevel + i; + } else { + nextIndentLevel = 0; + } + + for (i = 0; i < n; i++) { + if (pretty && indentKids) + js_AppendChar(&sb, '\n'); + + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + kidstr = XMLToXMLString(cx, kid, &ancdecls, nextIndentLevel); + if (!kidstr) + goto out; + + js_AppendJSString(&sb, kidstr); + } + + if (pretty && indentKids) { + js_AppendChar(&sb, '\n'); + js_RepeatChar(&sb, ' ', indentLevel); + } + js_AppendCString(&sb, "prefix)) { + js_AppendJSString(&sb, ns->prefix); + js_AppendChar(&sb, ':'); + } + + /* Step 27. */ + js_AppendJSString(&sb, xml->name->localName); + js_AppendChar(&sb, '>'); + } + + if (!STRING_BUFFER_OK(&sb)) { + JS_ReportOutOfMemory(cx); + goto out; + } + + str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb), 0); +out: + if (!str) + js_FinishStringBuffer(&sb); + XMLARRAY_FINISH(cx, &decls, js_DestroyXMLNamespace); + if (ancdecls.length != 0) + XMLARRAY_FINISH(cx, &ancdecls, NULL); + return str; +} + +/* ECMA-357 10.2 */ +static JSString * +ToXMLString(JSContext *cx, jsval v, JSXMLArray *ancestorNSes, uintN indentLevel) +{ + JSObject *obj; + JSString *str; + JSXML *xml; + + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_CONVERSION, + js_type_str[JSVAL_IS_NULL(v) + ? JSTYPE_NULL + : JSTYPE_VOID]); + return NULL; + } + + if (JSVAL_IS_BOOLEAN(v) || JSVAL_IS_NUMBER(v)) + return js_ValueToString(cx, v); + + if (JSVAL_IS_STRING(v)) + return EscapeElementValue(cx, NULL, JSVAL_TO_STRING(v)); + + obj = JSVAL_TO_OBJECT(v); + if (!OBJECT_IS_XML(cx, obj)) { + if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_STRING, &v)) + return NULL; + str = js_ValueToString(cx, v); + if (!str) + return NULL; + return EscapeElementValue(cx, NULL, str); + } + + /* Handle non-element cases in this switch, returning from each case. */ + xml = (JSXML *) JS_GetPrivate(cx, obj); + return XMLToXMLString(cx, xml, ancestorNSes, indentLevel); +} + +static JSXMLQName * +ToAttributeName(JSContext *cx, jsval v) +{ + JSString *name, *uri, *prefix; + JSObject *obj; + JSClass *clasp; + JSXMLQName *qn; + + if (JSVAL_IS_STRING(v)) { + name = JSVAL_TO_STRING(v); + uri = prefix = cx->runtime->emptyString; + } else if (!JSVAL_IS_PRIMITIVE(v)) { + obj = JSVAL_TO_OBJECT(v); + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp == &attribute_name_class) + return (JSXMLQName *) JS_GetPrivate(cx, obj); + if (clasp == &qname_class) { + qn = (JSXMLQName *) JS_GetPrivate(cx, obj); + uri = qn->uri; + prefix = qn->prefix; + name = qn->localName; + } else { + if (clasp == &any_name_class) { + name = ATOM_TO_STRING(cx->runtime->atomState.starAtom); + } else { + name = js_ValueToString(cx, v); + if (!name) + return NULL; + } + uri = prefix = cx->runtime->emptyString; + } + } else { + name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); + if (name) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_ATTRIBUTE_NAME, + JS_GetStringBytes(name)); + } + return NULL; + } + + qn = js_NewXMLQName(cx, uri, prefix, name); + if (!qn) + return NULL; + obj = js_NewObject(cx, &attribute_name_class, NULL, NULL); + if (!obj || !JS_SetPrivate(cx, obj, qn)) { + js_DestroyXMLQName(cx, qn); + cx->newborn[GCX_OBJECT] = NULL; + return NULL; + } + qn->object = obj; + return qn; +} + +static JSXMLQName * +ToXMLName(JSContext *cx, jsval v) +{ + JSString *name; + JSObject *obj; + JSClass *clasp; + uint32 index; + + if (JSVAL_IS_STRING(v)) { + name = JSVAL_TO_STRING(v); + } else if (!JSVAL_IS_PRIMITIVE(v)) { + obj = JSVAL_TO_OBJECT(v); + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp == &attribute_name_class || clasp == &qname_class) + goto out; + if (clasp == &any_name_class) { + name = ATOM_TO_STRING(cx->runtime->atomState.starAtom); + } else { + name = js_ValueToString(cx, v); + if (!name) + return NULL; + } + } else { + name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); + if (name) + goto badname; + return NULL; + } + + /* + * ECMA-357 10.6.1 step 1 seems to be incorrect. The spec says: + * + * 1. If ToString(ToNumber(P)) == ToString(P), throw a TypeError exception + * + * First, _P_ should be _s_, to refer to the given string. + * + * Second, why does ToXMLName applied to the string type throw TypeError + * only for numeric literals without any leading or trailing whitespace? + * + * If the idea is to reject uint32 property names, then the check needs to + * be stricter, to exclude hexadecimal and floating point literals. + */ + if (js_IdIsIndex(STRING_TO_JSVAL(name), &index)) + goto badname; + + if (*JSSTRING_CHARS(name) == '@') { + name = js_NewDependentString(cx, name, 1, JSSTRING_LENGTH(name) - 1, 0); + if (!name) + return NULL; + return ToAttributeName(cx, STRING_TO_JSVAL(name)); + } + + v = STRING_TO_JSVAL(name); + obj = js_ConstructObject(cx, &qname_class, NULL, NULL, 1, &v); + if (!obj) + return NULL; +out: + return (JSXMLQName *) JS_GetPrivate(cx, obj); + +badname: + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_NAME, + js_ValueToPrintableString(cx, STRING_TO_JSVAL(name))); + return NULL; +} + +/* ECMA-357 9.1.1.13 XML [[AddInScopeNamespace]]. */ +static JSBool +AddInScopeNamespace(JSContext *cx, JSXML *xml, JSXMLNamespace *ns) +{ + JSXMLNamespace *match, *ns2; + uint32 i, n, m; + + if (xml->xml_class != JSXML_CLASS_ELEMENT) + return JS_TRUE; + + /* NULL means *undefined* here -- see ECMA-357 9.1.1.13 step 2. */ + if (!ns->prefix) { + match = NULL; + for (i = 0, n = xml->xml_namespaces.count; i < n; i++) { + ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); + if (!js_CompareStrings(ns2->uri, ns->uri)) { + match = ns2; + break; + } + } + if (!match && !XMLARRAY_INSERT(cx, &xml->xml_namespaces, n, ns)) + return JS_FALSE; + } else { + if (IS_EMPTY(ns->prefix) && IS_EMPTY(xml->name->uri)) + return JS_TRUE; + match = NULL; +#ifdef __GNUC__ /* suppress bogus gcc warnings */ + m = NOT_FOUND; +#endif + for (i = 0, n = xml->xml_namespaces.count; i < n; i++) { + ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); + if (!js_CompareStrings(ns2->prefix, ns->prefix)) { + match = ns2; + m = i; + break; + } + } + if (match && js_CompareStrings(match->uri, ns->uri)) { + ns2 = XMLARRAY_DELETE(cx, &xml->xml_namespaces, m, JS_TRUE, + JSXMLNamespace); + JS_ASSERT(ns2 == match); + match->prefix = NULL; + if (!AddInScopeNamespace(cx, xml, match)) { + js_DestroyXMLNamespace(cx, match); + return JS_FALSE; + } + } + if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) + return JS_FALSE; + } + + /* OPTION: enforce that descendants have superset namespaces. */ + return JS_TRUE; +} + +/* ECMA-357 9.2.1.6 XMLList [[Append]]. */ +static JSBool +Append(JSContext *cx, JSXML *list, JSXML *kid) +{ + uint32 i, j, n; + JSXML *grandkid; + JSBool ok, locked; + + JS_ASSERT(list->xml_class == JSXML_CLASS_LIST); + i = list->xml_kids.count; + n = 1; + if (kid->xml_class == JSXML_CLASS_LIST) { + if (kid->object) { + JS_LOCK_OBJ(cx, kid->object); + locked = JS_TRUE; + } else { + locked = JS_FALSE; + } + list->xml_target = kid->xml_target; + list->xml_targetprop = kid->xml_targetprop; + n = JSXML_LENGTH(kid); + ok = JS_TRUE; + for (j = 0; j < n; j++) { + grandkid = XMLARRAY_MEMBER(&kid->xml_kids, j, JSXML); + ok = js_GetXMLObject(cx, grandkid) != NULL; + if (!ok) + break; + ok = XMLARRAY_INSERT(cx, &list->xml_kids, i + j, grandkid); + if (!ok) + break; + } + if (locked) + JS_UNLOCK_OBJ(cx, kid->object); + return ok; + } + + list->xml_target = kid->parent; + if (kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) + list->xml_targetprop = NULL; + else + list->xml_targetprop = kid->name; + if (!js_GetXMLObject(cx, kid)) + return JS_FALSE; + if (!XMLARRAY_INSERT(cx, &list->xml_kids, i, kid)) + return JS_FALSE; + return JS_TRUE; +} + +/* ECMA-357 9.1.1.7 XML [[DeepCopy]] and 9.2.1.7 XMLList [[DeepCopy]]. */ +static JSXML * +DeepCopyInLRS(JSContext *cx, JSXML *xml); + +static JSXML * +DeepCopy(JSContext *cx, JSXML *xml) +{ + JSXML *copy; + + /* Our caller may not be protecting newborns with a local root scope. */ + if (!JS_EnterLocalRootScope(cx)) + return NULL; + copy = DeepCopyInLRS(cx, xml); + JS_LeaveLocalRootScope(cx); + return copy; +} + +/* + * (i) We must be in a local root scope (InLRS). + * (ii) parent must have a rooted object. + */ +static JSBool +DeepCopySetInLRS(JSContext *cx, JSXMLArray *from, JSXMLArray *to, JSXML *parent) +{ + uint32 i, n; + JSXML *kid, *kid2; + + JS_ASSERT(cx->localRootStack); + JS_ASSERT(parent->object); + + for (i = 0, n = from->count; i < n; i++) { + kid = XMLARRAY_MEMBER(from, i, JSXML); + kid2 = DeepCopyInLRS(cx, kid); + if (!kid2) + return JS_FALSE; + if (!XMLARRAY_INSERT(cx, to, i, kid2)) + return JS_FALSE; + kid2->parent = parent; + JS_ForgetLocalRoot(cx, kid2->object); + } + return JS_TRUE; +} + +static JSXML * +DeepCopyInLRS(JSContext *cx, JSXML *xml) +{ + JSObject *copyobj; + JSXML *copy; + JSBool ok; + uint32 i, n; + JSXMLNamespace *ns, *ns2; + + /* Our caller must be protecting newborn objects. */ + JS_ASSERT(cx->localRootStack); + + copyobj = js_NewXMLObject(cx, xml->xml_class); + if (!copyobj) + return NULL; + + if (xml->object) + OBJ_SET_PROTO(cx, copyobj, OBJ_GET_PROTO(cx, xml->object)); + copy = (JSXML *) JS_GetPrivate(cx, copyobj); + copy->name = xml->name; + + if (JSXML_HAS_VALUE(xml)) { + copy->xml_value = xml->xml_value; + ok = JS_TRUE; + } else { + ok = DeepCopySetInLRS(cx, &xml->xml_kids, ©->xml_kids, copy); + if (!ok) + goto out; + + if (xml->xml_class == JSXML_CLASS_LIST) { + copy->xml_target = xml->xml_target; + copy->xml_targetprop = xml->xml_targetprop; + } else { + for (i = 0, n = xml->xml_namespaces.count; i < n; i++) { + ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); + ns2 = js_NewXMLNamespace(cx, ns->prefix, ns->uri); + if (!ns2) { + ok = JS_FALSE; + goto out; + } + ok = XMLARRAY_INSERT(cx, ©->xml_namespaces, i, ns2); + if (!ok) { + js_DestroyXMLNamespace(cx, ns2); + goto out; + } + } + + ok = DeepCopySetInLRS(cx, &xml->xml_attrs, ©->xml_attrs, copy); + if (!ok) + goto out; + } + } + +out: + if (!ok) { + copy->object = NULL; + js_DestroyXML(cx, copy); + return NULL; + } + return copy; +} + +static void +ReportBadXMLName(JSContext *cx, jsval id) +{ + JSString *name; + + name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, id, NULL); + if (name) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_XML_NAME, + JS_GetStringBytes(name)); + } +} + +/* ECMA-357 9.1.1.4 XML [[DeleteByIndex]]. */ +static JSBool +DeleteByIndex(JSContext *cx, JSXML *xml, jsval id, jsval *vp) +{ + uint32 index; + JSXML *kid; + + if (!js_IdIsIndex(id, &index)) { + ReportBadXMLName(cx, id); + return JS_FALSE; + } + + if (JSXML_HAS_KIDS(xml) && index < xml->xml_kids.count) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); + kid->parent = NULL; + XMLArrayDelete(cx, &xml->xml_kids, index, JS_TRUE); + } + + *vp = JSVAL_TRUE; + return JS_TRUE; +} + +static JSBool +MatchAttrName(JSXMLQName *nameqn, JSXMLQName *attrqn) +{ + return (IS_STAR(nameqn->localName) || + !js_CompareStrings(attrqn->localName, nameqn->localName)) && + (!nameqn->uri || + !js_CompareStrings(attrqn->uri, nameqn->uri)); +} + +static JSBool +MatchElemName(JSXMLQName *nameqn, JSXML *elem) +{ + return (IS_STAR(nameqn->localName) || + (elem->xml_class == JSXML_CLASS_ELEMENT && + !js_CompareStrings(elem->name->localName, nameqn->localName))) && + (!nameqn->uri || + (elem->xml_class == JSXML_CLASS_ELEMENT && + !js_CompareStrings(elem->name->uri, nameqn->uri))); +} + +/* ECMA-357 9.1.1.8 XML [[Descendants]] and 9.2.1.8 XMLList [[Descendants]]. */ +static JSBool +DescendantsHelper(JSContext *cx, JSXML *xml, JSXMLQName *nameqn, JSXML *list) +{ + uint32 i, n; + JSXML *attr, *kid; + + if (OBJ_GET_CLASS(cx, nameqn->object) == &attribute_name_class) { + for (i = 0, n = xml->xml_attrs.count; i < n; i++) { + attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); + if (MatchAttrName(nameqn, attr->name)) { + if (!Append(cx, list, attr)) + return JS_FALSE; + } + } + } + + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (MatchElemName(nameqn, kid)) { + if (!Append(cx, list, kid)) + return JS_FALSE; + } + if (!DescendantsHelper(cx, kid, nameqn, list)) + return JS_FALSE; + } + return JS_TRUE; +} + +static JSXML * +Descendants(JSContext *cx, JSXML *xml, jsval id) +{ + JSXMLQName *nameqn; + JSObject *listobj; + JSXML *list; + + nameqn = ToXMLName(cx, id); + if (!nameqn) + return NULL; + + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + return NULL; + list = (JSXML *) JS_GetPrivate(cx, listobj); + + /* + * Protect nameqn's object and strings from GC by linking list to it + * temporarily. The cx->newborn[GCX_OBJECT] GC root protects listobj, + * which protects list. No further object allocations occur beneath + * the DescendantsHelper call here. + */ + list->name = nameqn; + if (!DescendantsHelper(cx, xml, nameqn, list)) { + js_DestroyXML(cx, list); + return NULL; + } + list->name = NULL; + return list; +} + +/* Recursive helper for Equals. */ +static JSBool +XMLEquals(JSContext *cx, JSXML *xml, JSXML *vxml) +{ + JSXMLQName *qn, *vqn; + uint32 i, j, n; + JSXML **xvec, **vvec, *attr, *vattr; + + if (xml->xml_class != vxml->xml_class) + return JS_FALSE; + + qn = xml->name; + vqn = vxml->name; + if (qn) { + if (!vqn) + return JS_FALSE; + if (js_CompareStrings(qn->localName, vqn->localName)) + return JS_FALSE; + if (js_CompareStrings(qn->uri, vqn->uri)) + return JS_FALSE; + } else { + if (vqn) + return JS_FALSE; + } + + if (JSXML_HAS_VALUE(xml)) { + if (js_CompareStrings(xml->xml_value, vxml->xml_value)) + return JS_FALSE; + } else { + n = xml->xml_kids.count; + if (n != vxml->xml_kids.count) + return JS_FALSE; + xvec = (JSXML **) xml->xml_kids.vector; + vvec = (JSXML **) vxml->xml_kids.vector; + for (i = 0; i < n; i++) { + if (!XMLEquals(cx, xvec[i], vvec[i])) + return JS_FALSE; + } + + if (xml->xml_class == JSXML_CLASS_ELEMENT) { + n = xml->xml_attrs.count; + if (n != vxml->xml_attrs.count) + return JS_FALSE; + for (i = 0; i < n; i++) { + attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); + j = XMLARRAY_HAS_MEMBER(&vxml->xml_attrs, attr, attr_identity); + if (j == NOT_FOUND) + return JS_FALSE; + vattr = XMLARRAY_MEMBER(&vxml->xml_attrs, j, JSXML); + if (js_CompareStrings(attr->xml_value, vattr->xml_value)) + return JS_FALSE; + } + } + } + + return JS_TRUE; +} + +/* ECMA-357 9.1.1.9 XML [[Equals]] and 9.2.1.9 XMLList [[Equals]]. */ +static JSBool +Equals(JSContext *cx, JSXML *xml, jsval v) +{ + JSObject *vobj; + JSXML *vxml; + + if (JSVAL_IS_PRIMITIVE(v)) + return JS_FALSE; + + vobj = JSVAL_TO_OBJECT(v); + if (OBJ_GET_CLASS(cx, vobj) != &js_XMLClass) + return JS_FALSE; + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + return XMLEquals(cx, xml, vxml); +} + +static JSBool +Replace(JSContext *cx, JSXML *xml, jsval id, jsval v); + +/* ECMA-357 9.1.1.11 XML [[Insert]]. */ +static JSBool +Insert(JSContext *cx, JSXML *xml, jsval id, jsval v) +{ + uint32 i, j, n; + JSXML *vxml, *kid; + JSObject *vobj; + + if (!JSXML_HAS_KIDS(xml)) + return JS_TRUE; + + if (!js_IdIsIndex(id, &i)) { + ReportBadXMLName(cx, id); + return JS_FALSE; + } + + n = 1; + vxml = NULL; + if (!JSVAL_IS_PRIMITIVE(v)) { + vobj = JSVAL_TO_OBJECT(v); + if (OBJ_GET_CLASS(cx, vobj) == &js_XMLClass) { + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + if (vxml->xml_class == JSXML_CLASS_LIST) + n = vxml->xml_kids.count; + } + } + + if (n == 0) + return JS_TRUE; + + j = xml->xml_kids.count; + JS_ASSERT(j >= i); + while (j != i) { + --j; + if (!XMLARRAY_INSERT(cx, &xml->xml_kids, j + n, + xml->xml_kids.vector[j])) { + return JS_FALSE; + } + } + + if (vxml && vxml->xml_class == JSXML_CLASS_LIST) { + for (j = 0; j < n; j++) { + kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML); + if (!js_GetXMLObject(cx, kid)) + return JS_FALSE; + kid->parent = xml; + XMLARRAY_SET_MEMBER(&xml->xml_kids, i + j, kid); + + /* OPTION: enforce that descendants have superset namespaces. */ + } + } else { + if (!Replace(cx, xml, id, v)) + return JS_FALSE; + } + return JS_TRUE; +} + +static JSBool +IndexToIdVal(JSContext *cx, uint32 index, jsval *idvp) +{ + JSString *str; + + if (index <= JSVAL_INT_MAX) { + *idvp = INT_TO_JSVAL(index); + } else { + str = js_NumberToString(cx, (jsdouble) index); + if (!str) + return JS_FALSE; + *idvp = STRING_TO_JSVAL(str); + } + return JS_TRUE; +} + +/* ECMA-357 9.1.1.12 XML [[Replace]]. */ +static JSBool +Replace(JSContext *cx, JSXML *xml, jsval id, jsval v) +{ + uint32 i, n; + JSXML *vxml, *kid; + JSObject *vobj; + jsval junk; + JSString *str; + + if (!JSXML_HAS_KIDS(xml)) + return JS_TRUE; + + if (!js_IdIsIndex(id, &i)) { + ReportBadXMLName(cx, id); + return JS_FALSE; + } + + /* + * 9.1.1.12 + * [[Replace]] handles _i >= x.[[Length]]_ by incrementing _x.[[Length]_. + * It should therefore constrain callers to pass in _i <= x.[[Length]]_. + */ + n = xml->xml_kids.count; + JS_ASSERT(i <= n); + if (i >= n) { + if (!IndexToIdVal(cx, n, &id)) + return JS_FALSE; + if (!XMLARRAY_INSERT(cx, &xml->xml_kids, n, NULL)) + return JS_FALSE; + i = n; + } + + vxml = NULL; + if (!JSVAL_IS_PRIMITIVE(v)) { + vobj = JSVAL_TO_OBJECT(v); + if (OBJ_GET_CLASS(cx, vobj) == &js_XMLClass) + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + } + + switch (vxml ? vxml->xml_class : JSXML_CLASS_LIMIT) { + case JSXML_CLASS_ELEMENT: + case JSXML_CLASS_COMMENT: + case JSXML_CLASS_PROCESSING_INSTRUCTION: + case JSXML_CLASS_TEXT: + vxml->parent = xml; + if (i < n) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + kid->parent = NULL; + } + XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml); + + /* OPTION: enforce that descendants have superset namespaces. */ + break; + + case JSXML_CLASS_LIST: + if (!DeleteByIndex(cx, xml, id, &junk)) + return JS_FALSE; + if (!Insert(cx, xml, id, v)) + return JS_FALSE; + break; + + default: + str = js_ValueToString(cx, v); + if (!str) + return JS_FALSE; + + vxml = js_NewXML(cx, JSXML_CLASS_TEXT); + if (!vxml) + return JS_FALSE; + vxml->parent = xml; + vxml->xml_value = str; + + if (i < n) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + kid->parent = NULL; + } + XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml); + break; + } + + return JS_TRUE; +} + +/* ECMA-357 9.1.1.10 XML [[ResolveValue]], 9.2.1.10 XMLList [[ResolveValue]]. */ +static JSBool +ResolveValue(JSContext *cx, JSXML *list, JSXML **result) +{ + JSXML *target, *base; + JSXMLQName *targetprop; + jsval id, tv; + + /* Our caller must be protecting newborn objects. */ + JS_ASSERT(cx->localRootStack); + + if (list->xml_class != JSXML_CLASS_LIST || list->xml_kids.count != 0) { + if (!js_GetXMLObject(cx, list)) + return JS_FALSE; + *result = list; + return JS_TRUE; + } + + target = list->xml_target; + targetprop = list->xml_targetprop; + if (!target || + !targetprop || + OBJ_GET_CLASS(cx, targetprop->object) == &attribute_name_class || + IS_STAR(targetprop->localName)) { + *result = NULL; + return JS_TRUE; + } + + if (!ResolveValue(cx, target, &base)) + return JS_FALSE; + if (!base) { + *result = NULL; + return JS_TRUE; + } + if (!js_GetXMLObject(cx, base)) + return JS_FALSE; + + id = OBJECT_TO_JSVAL(targetprop->object); + if (!js_XMLClass.getProperty(cx, base->object, id, &tv)) + return JS_FALSE; + target = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(tv)); + + if (JSXML_LENGTH(target) == 0) { + if (base->xml_class == JSXML_CLASS_LIST && JSXML_LENGTH(base) > 1) { + *result = NULL; + return JS_TRUE; + } + tv = STRING_TO_JSVAL(cx->runtime->emptyString); + if (!js_XMLClass.setProperty(cx, base->object, id, &tv)) + return JS_FALSE; + if (!js_XMLClass.getProperty(cx, base->object, id, &tv)) + return JS_FALSE; + target = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(tv)); + } + + *result = target; + return JS_TRUE; +} + +/* ECMA-357 9.1.1.3 XML [[Delete]], 9.2.1.3 XML [[Delete]]. */ +static JSBool +DeleteProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSXML *xml, *kid, *parent, *attr; + JSBool isIndex; + uint32 index, deleteCount; + JSXMLQName *nameqn; + JSObject *nameobj; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + isIndex = js_IdIsIndex(id, &index); + + if (xml->xml_class == JSXML_CLASS_LIST) { + /* ECMA-357 9.2.1.3. */ + if (isIndex && index < xml->xml_kids.count) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); + parent = kid->parent; + if (parent) { + JS_ASSERT(parent->object); + JS_ASSERT(JSXML_HAS_KIDS(parent)); + + if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) { + nameqn = kid->name; + nameobj = js_GetXMLQNameObject(cx, nameqn); + if (!nameobj) + return JS_FALSE; + + id = OBJECT_TO_JSVAL(nameobj); + if (!DeleteProperty(cx, parent->object, id, vp)) + return JS_FALSE; + } else { + index = XMLARRAY_HAS_MEMBER(&parent->xml_kids, kid, NULL); + JS_ASSERT(index != NOT_FOUND); + if (!IndexToIdVal(cx, index, &id)) + return JS_FALSE; + if (!DeleteByIndex(cx, parent, id, vp)) + return JS_FALSE; + } + } + + XMLArrayDelete(cx, &xml->xml_kids, index, JS_TRUE); + } + } else { + /* ECMA-357 9.1.1.3. */ + if (isIndex) { + /* See NOTE in spec: this variation is reserved for future use. */ + ReportBadXMLName(cx, id); + return JS_FALSE; + } + + nameqn = ToXMLName(cx, id); + if (!nameqn) + return JS_FALSE; + nameobj = nameqn->object; + + if (OBJ_GET_CLASS(cx, nameobj) == &attribute_name_class) { + if (xml->xml_class == JSXML_CLASS_ELEMENT && + (index = xml->xml_attrs.count) != 0) { + deleteCount = 0; + do { + --index; + attr = XMLARRAY_MEMBER(&xml->xml_attrs, index, JSXML); + if (MatchAttrName(nameqn, attr->name)) { + attr->parent = NULL; + XMLArrayDelete(cx, &xml->xml_attrs, index, JS_FALSE); + ++deleteCount; + } + } while (index != 0); + xml->xml_attrs.count -= deleteCount; + } + } else { + if (JSXML_HAS_KIDS(xml) && (index = xml->xml_kids.count) != 0) { + deleteCount = 0; + do { + --index; + kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); + if (MatchElemName(nameqn, kid)) { + kid->parent = NULL; + XMLArrayDelete(cx, &xml->xml_kids, index, JS_FALSE); + ++deleteCount; + } + } while (index != 0); + xml->xml_kids.count -= deleteCount; + } + } + } + + *vp = JSVAL_TRUE; + return JS_TRUE; +} + +/* ECMA-357 9.1.1.1 XML [[Get]] and 9.2.1.1 XMLList [[Get]]. */ +static JSBool +GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSXML *xml, *list, *attr, *kid; + uint32 index, i, n; + JSXMLQName *nameqn; + JSObject *listobj, *kidobj, *nameobj; + JSBool ok; + jsval kidval; + + xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); + if (!xml) + return JS_TRUE; + +retry: + if (xml->xml_class == JSXML_CLASS_LIST) { + /* ECMA-357 9.2.1.1 starts here. */ + if (js_IdIsIndex(id, &index)) + return js_ObjectClass.getProperty(cx, obj, id, vp); + + nameqn = ToXMLName(cx, id); + if (!nameqn) + return JS_FALSE; + + /* + * Recursion through GetProperty may allocate more list objects, so + * we make use of local root scopes here. Each new allocation will + * push the newborn onto the local root stack. + */ + ok = JS_EnterLocalRootScope(cx); + if (!ok) + return JS_FALSE; + + /* + * NB: nameqn is already protected from GC by cx->newborn[GCX_OBJECT] + * until listobj is created. After that, a local root keeps listobj + * alive, and listobj's private keeps nameqn alive via targetprop. + */ + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) { + ok = JS_FALSE; + } else { + list = (JSXML *) JS_GetPrivate(cx, listobj); + list->xml_target = xml; + list->xml_targetprop = nameqn; + + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid->xml_class == JSXML_CLASS_ELEMENT) { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) { + ok = JS_FALSE; + break; + } + ok = GetProperty(cx, kidobj, id, &kidval); + if (!ok) + break; + kidobj = JSVAL_TO_OBJECT(kidval); + kid = (JSXML *) JS_GetPrivate(cx, kidobj); + if (JSXML_LENGTH(kid) > 0) { + ok = Append(cx, list, kid); + if (!ok) + break; + } + } + } + } + + JS_LeaveLocalRootScope(cx); + if (!ok) + return JS_FALSE; + } else { + /* ECMA-357 9.1.1.1 starts here. */ + if (js_IdIsIndex(id, &index)) { + xml = ToXMLList(cx, OBJECT_TO_JSVAL(obj)); + if (!xml) + return JS_FALSE; + obj = xml->object; + goto retry; + } + + nameqn = ToXMLName(cx, id); + if (!nameqn) + return JS_FALSE; + nameobj = nameqn->object; + + /* + * See comment in JSXML_CLASS_LIST block above on newborn rooting. + * Unlike the LIST case, we make no recursive calls below, nor do we + * allocate any other object. So we don't need to root listobj or + * enter a local root scope. + */ + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + return JS_FALSE; + list = (JSXML *) JS_GetPrivate(cx, listobj); + list->xml_target = xml; + list->xml_targetprop = nameqn; + + if (OBJ_GET_CLASS(cx, nameobj) == &attribute_name_class) { + for (i = 0, n = xml->xml_attrs.count; i < n; i++) { + attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); + if (MatchAttrName(nameqn, attr->name)) { + if (!Append(cx, list, attr)) + return JS_FALSE; + } + } + } else { + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (MatchElemName(nameqn, kid)) { + if (!Append(cx, list, kid)) + return JS_FALSE; + } + } + } + } + + *vp = OBJECT_TO_JSVAL(listobj); + return JS_TRUE; +} + +static JSString * +KidToString(JSContext *cx, JSXML *xml, uint32 index) +{ + JSXML *kid; + JSObject *kidobj; + + kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + return NULL; + return js_ValueToString(cx, OBJECT_TO_JSVAL(kidobj)); +} + +/* ECMA-357 9.1.1.2 XML [[Put]] and 9.2.1.2 XMLList [[Put]]. */ +static JSBool +PutProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSBool ok, primitiveAssign; + JSXML *xml, *vxml, *rxml, *kid, *attr, *parent, *copy, *kid2, *match; + JSObject *vobj, *nameobj, *attrobj, *parentobj, *kidobj, *copyobj; + JSXMLQName *targetprop, *nameqn, *attrqn; + uint32 index, i, j, k, n, q; + jsval attrval, nsval, junk; + JSString *left, *right, *space; + JSXMLNamespace *ns; + + xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); + if (!xml) + return JS_TRUE; + if ((xml->flags & XML_COPY_ON_WRITE) && xml->object != obj) { + /* XXXbe waste an XML object here. */ + xml = DeepCopy(cx, xml); + if (!xml || !JS_SetPrivate(cx, obj, xml)) + return JS_FALSE; + xml->object = obj; + } + + /* Precompute vxml for 9.2.1.2 2(c)(vii)(2-3) and 2(d) and 9.1.1.2 1. */ + vxml = NULL; + if (!JSVAL_IS_PRIMITIVE(*vp)) { + vobj = JSVAL_TO_OBJECT(*vp); + if (OBJ_GET_CLASS(cx, vobj) == &js_XMLClass) + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + } + + /* Control flow after here must exit via label out. */ + ok = JS_EnterLocalRootScope(cx); + if (!ok) + return JS_FALSE; + + if (xml->xml_class == JSXML_CLASS_LIST) { + /* ECMA-357 9.2.1.2. */ + if (js_IdIsIndex(id, &index)) { + /* Step 1 sets i to the property index. */ + i = index; + + /* 2(a-b). */ + if (xml->xml_target) { + ok = ResolveValue(cx, xml->xml_target, &rxml); + if (!ok) + goto out; + if (!rxml) + goto out; + JS_ASSERT(rxml->object); + } else { + rxml = NULL; + } + + /* 2(c). */ + if (index >= xml->xml_kids.count) { + /* 2(c)(i). */ + if (rxml && rxml->xml_class == JSXML_CLASS_LIST) { + if (rxml->xml_kids.count != 1) + goto out; + rxml = XMLARRAY_MEMBER(&rxml->xml_kids, 0, JSXML); + ok = js_GetXMLObject(cx, rxml) != NULL; + if (!ok) + goto out; + } + + /* 2(c)(ii) is distributed below as several js_NewXML calls. */ + targetprop = xml->xml_targetprop; + if (!targetprop || IS_STAR(targetprop->localName)) { + /* 2(c)(iv)(1-2), out of order w.r.t. 2(c)(iii). */ + kid = js_NewXML(cx, JSXML_CLASS_TEXT); + if (!kid) + goto bad; + } else { + nameobj = targetprop->object; + if (nameobj && + OBJ_GET_CLASS(cx, nameobj) == &attribute_name_class) { + /* + * 2(c)(iii)(1-3). + * Note that rxml can't be null here, because target + * and targetprop are non-null. + */ + ok = GetProperty(cx, rxml->object, id, &attrval); + if (!ok) + goto out; + attrobj = JSVAL_TO_OBJECT(attrval); + attr = (JSXML *) JS_GetPrivate(cx, attrobj); + if (JSXML_LENGTH(attr) != 0) + goto out; + + kid = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE); + } else { + /* 2(c)(v). */ + kid = js_NewXML(cx, JSXML_CLASS_ELEMENT); + } + if (!kid) + goto bad; + + /* An important bit of 2(c)(ii). */ + kid->name = targetprop; + } + + /* Final important bit of 2(c)(ii). */ + kid->parent = rxml; + + /* 2(c)(vi-vii). */ + i = xml->xml_kids.count; + if (kid->xml_class != JSXML_CLASS_ATTRIBUTE) { + /* + * 2(c)(vii)(1) tests whether _y.[[Parent]]_ is not null. + * y.[[Parent]] is here called kid->parent, which we know + * from 2(c)(ii) is _r_, here called rxml. So let's just + * test that! Erratum, the spec should be simpler here. + */ + if (rxml) { + j = n = rxml->xml_kids.count - 1; + if (i != 0) { + for (j = 0; j < n; j++) { + if (rxml->xml_kids.vector[j] == + xml->xml_kids.vector[i-1]) { + break; + } + } + } + + ok = XMLARRAY_INSERT(cx, &rxml->xml_kids, j+1, kid); + if (!ok) { + js_DestroyXML(cx, kid); + goto out; + } + } + + /* + * 2(c)(vii)(2-3). + * Erratum: [[PropertyName]] in 2(c)(vii)(3) must be a + * typo for [[TargetProperty]]. + */ + if (vxml) { + kid->name = (vxml->xml_class == JSXML_CLASS_LIST) + ? vxml->xml_targetprop + : vxml->name; + } + } + + /* 2(c)(viii). */ + ok = Append(cx, xml, kid); + if (!ok) { + js_DestroyXML(cx, kid); + goto out; + } + } + + /* 2(d). */ + if (!vxml || + vxml->xml_class == JSXML_CLASS_TEXT || + vxml->xml_class == JSXML_CLASS_ATTRIBUTE) { + ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); + if (!ok) + goto out; + } + + /* 2(e). */ + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + parent = kid->parent; + if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) { + nameobj = js_GetXMLQNameObject(cx, kid->name); + if (!nameobj) + goto bad; + id = OBJECT_TO_JSVAL(nameobj); + + /* 2(e)(i). */ + parentobj = parent->object; + ok = PutProperty(cx, parentobj, id, vp); + if (!ok) + goto out; + + /* 2(e)(ii). */ + ok = GetProperty(cx, parentobj, id, vp); + if (!ok) + goto out; + attr = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(*vp)); + + /* 2(e)(iii). */ + xml->xml_kids.vector[i] = attr->xml_kids.vector[0]; + } + + /* 2(f). */ + else if (vxml && vxml->xml_class == JSXML_CLASS_LIST) { + /* 2(f)(i) Create a shallow copy _c_ of _V_. */ + copyobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!copyobj) + goto bad; + copy = (JSXML *) JS_GetPrivate(cx, copyobj); + for (k = 0, n = vxml->xml_kids.count; k < n; k++) { + kid2 = XMLARRAY_MEMBER(&vxml->xml_kids, k, JSXML); + ok = js_GetXMLObject(cx, kid2) != NULL; + if (!ok) + goto out; + ok = XMLARRAY_INSERT(cx, ©->xml_kids, k, kid2); + if (!ok) + goto out; + } + + if (parent) { + q = XMLARRAY_HAS_MEMBER(&parent->xml_kids, kid, NULL); + JS_ASSERT(q != NOT_FOUND); + + ok = IndexToIdVal(cx, q, &id); + if (!ok) + goto out; + ok = Replace(cx, parent, id, OBJECT_TO_JSVAL(copyobj)); + if (!ok) + goto out; + + /* Erratum: isn't this loop useless? */ + for (j = 0, n = copy->xml_kids.count; j < n; j++) { + kid2 = XMLARRAY_MEMBER(&parent->xml_kids, q + j, JSXML); + ok = js_GetXMLObject(cx, kid2) != NULL; + if (!ok) + goto out; + XMLARRAY_SET_MEMBER(©->xml_kids, j, kid2); + } + } + + /* 2(f)(iv, vi). */ + j = xml->xml_kids.count; + k = copy->xml_kids.count; + while (j != i) { + --j; + kid2 = XMLARRAY_MEMBER(&xml->xml_kids, j, JSXML); + ok = XMLARRAY_INSERT(cx, &xml->xml_kids, j + k, kid2); + if (!ok) + goto out; + } + + /* 2(f)(v). */ + for (j = 0; j < k; j++) { + kid2 = XMLARRAY_MEMBER(©->xml_kids, j, JSXML); + XMLARRAY_SET_MEMBER(&xml->xml_kids, i + j, kid2); + } + } + + /* 2(g). */ + else if (vxml || JSXML_HAS_VALUE(kid)) { + if (parent) { + q = XMLARRAY_HAS_MEMBER(&parent->xml_kids, kid, NULL); + JS_ASSERT(q != NOT_FOUND); + + ok = IndexToIdVal(cx, q, &id); + if (!ok) + goto out; + ok = Replace(cx, parent, id, *vp); + if (!ok) + goto out; + + vxml = XMLARRAY_MEMBER(&parent->xml_kids, q, JSXML); + *vp = OBJECT_TO_JSVAL(vxml->object); + } + + /* + * 2(g)(iii). + * Erratum: _V_ may not be of type XML, but all index-named + * properties _x[i]_ in an XMLList _x_ must be of type XML, + * according to 9.2.1.1 Overview and other places in the spec. + * + * Thanks to 2(d), we know _V_ (*vp here) is either a string + * or an XML/XMLList object. If *vp is a string, call ToXML + * on it to satisfy the constraint. + */ + if (!vxml) { + JS_ASSERT(JSVAL_IS_STRING(*vp)); + vxml = ToXML(cx, *vp); + if (!vxml) + goto bad; + } + XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml); + } + + /* 2(h). */ + else { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + goto bad; + id = ATOM_KEY(cx->runtime->atomState.starAtom); + ok = PutProperty(cx, kidobj, id, vp); + if (!ok) + goto out; + } + } else { + /* 3. */ + n = JSXML_LENGTH(xml); + if (n <= 1) { + if (n == 0) { + ok = ResolveValue(cx, xml, &rxml); + if (!ok) + goto out; + if (!rxml || JSXML_LENGTH(rxml) != 1) + goto out; + ok = Append(cx, xml, rxml); + if (!ok) + goto out; + } + JS_ASSERT(JSXML_LENGTH(xml) == 1); + kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + goto bad; + ok = PutProperty(cx, kidobj, id, vp); + if (!ok) + goto out; + } + } + } else { + /* + * ECMA-357 9.1.1.2. + * Erratum: move steps 3 and 4 to before 1 and 2, to avoid wasted + * effort in ToString or [[DeepCopy]]. + */ + if (js_IdIsIndex(id, &index)) { + /* See NOTE in spec: this variation is reserved for future use. */ + ReportBadXMLName(cx, id); + goto bad; + } + + if (JSXML_HAS_VALUE(xml)) + goto out; + + if (!vxml || + vxml->xml_class == JSXML_CLASS_TEXT || + vxml->xml_class == JSXML_CLASS_ATTRIBUTE) { + ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); + if (!ok) + goto out; + } else { + rxml = DeepCopyInLRS(cx, vxml); + if (!rxml) + goto bad; + vxml = rxml; + *vp = OBJECT_TO_JSVAL(vxml->object); + } + + nameqn = ToXMLName(cx, id); + if (!nameqn) + goto bad; + nameobj = nameqn->object; + + /* + * 6. + * Erratum: why is this done here, so early? use is way later.... + */ + ok = js_GetDefaultXMLNamespace(cx, &nsval); + if (!ok) + goto out; + + if (OBJ_GET_CLASS(cx, nameobj) == &attribute_name_class) { + /* 7(a). */ + if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj))) + goto out; + + /* 7(b-c). */ + if (vxml && vxml->xml_class == JSXML_CLASS_LIST) { + n = vxml->xml_kids.count; + if (n == 0) { + *vp = STRING_TO_JSVAL(cx->runtime->emptyString); + } else { + left = KidToString(cx, vxml, 0); + if (!left) + goto bad; + + space = ATOM_TO_STRING(cx->runtime->atomState.spaceAtom); + for (i = 1; i < n; i++) { + left = js_ConcatStrings(cx, left, space); + if (!left) + goto bad; + right = KidToString(cx, vxml, i); + if (!right) + goto bad; + left = js_ConcatStrings(cx, left, right); + if (!left) + goto bad; + } + + *vp = STRING_TO_JSVAL(left); + } + } else { + ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); + if (!ok) + goto out; + } + + /* 7(d-e). */ + match = NULL; + for (i = 0, n = xml->xml_attrs.count; i < n; i++) { + attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); + attrqn = attr->name; + if (!js_CompareStrings(attrqn->localName, nameqn->localName) && + (!nameqn->uri || + !js_CompareStrings(attrqn->uri, nameqn->uri))) { + if (!match) { + match = attr; + } else { + nameobj = js_GetXMLQNameObject(cx, attrqn); + if (!nameobj) + goto bad; + + id = OBJECT_TO_JSVAL(nameobj); + ok = DeleteProperty(cx, obj, id, &junk); + if (!ok) + goto out; + + /* XXXbe O(n^2) badness here! */ + --i; + } + } + } + + /* 7(f). */ + attr = match; + if (!attr) { + /* 7(f)(i-ii). */ + if (!nameqn->uri) { + left = right = cx->runtime->emptyString; + } else { + left = nameqn->uri; + right = nameqn->prefix; + } + nameqn = js_NewXMLQName(cx, left, right, nameqn->localName); + if (!nameqn) + goto bad; + + /* 7(f)(iii). */ + attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE); + if (!attr) + goto bad; + attr->parent = xml; + attr->name = nameqn; + + /* 7(f)(iv). */ + ok = XMLARRAY_INSERT(cx, &xml->xml_attrs, n, attr); + if (!ok) { + js_DestroyXML(cx, attr); + goto out; + } + + /* 7(f)(v-vi). */ + ns = GetNamespace(cx, nameqn, NULL); + if (!ns) + goto bad; + ok = AddInScopeNamespace(cx, xml, ns); + if (!ok) + goto out; + } + + /* 7(g). */ + attr->xml_value = JSVAL_TO_STRING(*vp); + goto out; + } + + /* 8-9. */ + if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)) && + !IS_STAR(nameqn->localName)) { + goto out; + } + + /* 10-11. */ + id = JSVAL_VOID; + primitiveAssign = !vxml && !IS_STAR(nameqn->localName); + + /* 12. */ + k = n = xml->xml_kids.count; + while (k != 0) { + --k; + kid = XMLARRAY_MEMBER(&xml->xml_kids, k, JSXML); + if (MatchElemName(nameqn, kid)) { + if (!JSVAL_IS_VOID(id)) { + ok = DeleteByIndex(cx, xml, id, &junk); + if (!ok) + goto out; + } + ok = IndexToIdVal(cx, k, &id); + if (!ok) + goto out; + } + } + + /* 13. */ + if (JSVAL_IS_VOID(id)) { + /* 13(a). */ + ok = IndexToIdVal(cx, n, &id); + if (!ok) + goto out; + + /* 13(b). */ + if (primitiveAssign) { + if (nameqn->uri) { + ns = (JSXMLNamespace *) + JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval)); + left = ns->uri; + right = ns->prefix; + } else { + left = nameqn->uri; + right = nameqn->prefix; + } + nameqn = js_NewXMLQName(cx, left, right, nameqn->localName); + if (!nameqn) + goto bad; + + /* 13(b)(iii). */ + vobj = js_NewXMLObject(cx, JSXML_CLASS_ELEMENT); + if (!vxml) + goto bad; + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + vxml->parent = xml; + vxml->name = nameqn; + + /* 13(b)(iv-vi). */ + ns = GetNamespace(cx, nameqn, NULL); + if (!ns) + goto bad; + ok = Replace(cx, xml, id, OBJECT_TO_JSVAL(vobj)); + if (!ok) + goto out; + ok = AddInScopeNamespace(cx, vxml, ns); + if (!ok) + goto out; + } + } + + /* 14. */ + if (primitiveAssign) { + js_IdIsIndex(id, &index); + kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); + if (JSXML_HAS_KIDS(kid)) { + XMLARRAY_FINISH(cx, &kid->xml_kids, js_DestroyXML); + XMLARRAY_INIT(cx, &kid->xml_kids, 1); + } + + /* 14(b-c). */ + ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); + if (!ok) + goto out; + if (!IS_EMPTY(JSVAL_TO_STRING(*vp))) + ok = Replace(cx, kid, JSVAL_ZERO, *vp); + } else { + /* 15(a). */ + ok = Replace(cx, xml, id, *vp); + } + } + +out: + JS_LeaveLocalRootScope(cx); + return ok; +bad: + ok = JS_FALSE; + goto out; +} + +/* ECMA-357 9.1.1.6 XML [[HasProperty]] and 9.2.1.5 XMLList [[HasProperty]]. */ +static JSBool +HasProperty(JSContext *cx, JSObject *obj, jsval id, JSBool *foundp) +{ + JSXML *xml; + uint32 i, n; + JSXML *kid, *attr; + JSObject *kidobj; + JSXMLQName *qn; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (xml->xml_class == JSXML_CLASS_LIST) { + n = JSXML_LENGTH(xml); + if (js_IdIsIndex(id, &i)) { + *foundp = i < n; + return JS_TRUE; + } + + for (i = 0; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid->xml_class == JSXML_CLASS_ELEMENT) { + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj || !HasProperty(cx, kidobj, id, foundp)) + return JS_FALSE; + if (*foundp) + return JS_TRUE; + } + } + } else if (xml->xml_class == JSXML_CLASS_ELEMENT) { + if (js_IdIsIndex(id, &i)) { + *foundp = (i == 0); + return JS_TRUE; + } + + qn = ToXMLName(cx, id); + if (!qn) + return JS_FALSE; + + if (OBJ_GET_CLASS(cx, qn->object) == &attribute_name_class) { + for (i = 0, n = xml->xml_attrs.count; i < n; i++) { + attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); + *foundp = MatchAttrName(qn, attr->name); + if (*foundp) + return JS_TRUE; + } + } else { + for (i = 0, n = xml->xml_kids.count; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + *foundp = MatchElemName(qn, kid); + if (*foundp) + return JS_TRUE; + } + } + } + + *foundp = JS_FALSE; + return JS_TRUE; +} + +static void +xml_finalize(JSContext *cx, JSObject *obj) +{ + JSXML *xml; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (xml) { + if (xml->object == obj) + xml->object = NULL; + js_DestroyXML(cx, xml); + } +} + +static void +xml_mark_private(JSContext *cx, JSXML *xml, void *arg); + +static void +xml_mark_vector(JSContext *cx, JSXML **vec, uint32 len, void *arg) +{ + uint32 i; + JSXML *elt; + + for (i = 0; i < len; i++) { + elt = vec[i]; + if (elt->object) { +#ifdef GC_MARK_DEBUG + char buf[100]; + JSXMLQName *qn = elt->name; + + JS_snprintf(buf, sizeof buf, "%s::%s", + qn->uri ? JS_GetStringBytes(qn->uri) : "*", + JS_GetStringBytes(qn->localName)); +#else + const char *buf = NULL; +#endif + JS_MarkGCThing(cx, elt->object, buf, arg); + } else { + xml_mark_private(cx, elt, arg); + } + } +} + +static void +xml_mark_private(JSContext *cx, JSXML *xml, void *arg) +{ + JSXMLQName *qn; + + qn = xml->name; + if (qn) { + if (qn->object) + JS_MarkGCThing(cx, qn->object, "name", arg); + else + qname_mark_private(cx, qn, arg); + } + + if (JSXML_HAS_VALUE(xml)) { + JS_MarkGCThing(cx, xml->xml_value, "value", arg); + } else { + xml_mark_vector(cx, + (JSXML **) xml->xml_kids.vector, + xml->xml_kids.count, + arg); + + if (xml->xml_class == JSXML_CLASS_LIST) { + if (xml->xml_target) + xml_mark_private(cx, xml->xml_target, arg); + if (xml->xml_targetprop) + qname_mark_private(cx, xml->xml_targetprop, arg); + } else { + namespace_mark_vector(cx, + (JSXMLNamespace **) + xml->xml_namespaces.vector, + xml->xml_namespaces.count, + arg); + xml_mark_vector(cx, + (JSXML **) xml->xml_attrs.vector, + xml->xml_attrs.count, + arg); + } + } +} + +/* + * js_XMLObjectOps.newObjectMap == js_NewObjectMap, so XML objects appear to + * be native. Therefore, xml_lookupProperty must return a valid JSProperty + * pointer parameter via *propp to signify "property found". + * + * We use an all-zeroes static here to return a unique non-null address via + * *propp. The layer that calls into JSObjectOps never inspects *propp, + * although it could expect that the id member match the sought-after jsid. + * Nevertheless, we use all-zeroes for now, pending a more sweeping cleanup + * of JSObjectOps. + */ +static JSScopeProperty dummy_sprop; + +static JSBool +xml_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, + JSProperty **propp) +{ + JSBool ok, found; + + JS_LOCK_OBJ_VOID(cx, obj, + ok = HasProperty(cx, obj, ID_TO_VALUE(id), &found)); + if (!ok) + return JS_FALSE; + *objp = obj; + *propp = found ? (JSProperty *) &dummy_sprop : NULL; + return JS_TRUE; +} + +static JSBool +xml_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs, + JSProperty **propp) +{ + JSBool ok; + + if (JSVAL_IS_FUNCTION(cx, value) || getter || setter || + (attrs & JSPROP_ENUMERATE) == 0 || + (attrs & (JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED))) { + ok = js_DefineProperty(cx, obj, id, value, getter, setter, attrs, + propp); + } else { + JS_LOCK_OBJ_VOID(cx, obj, + ok = PutProperty(cx, obj, ID_TO_VALUE(id), &value)); + if (!ok) + return JS_FALSE; + if (propp) + *propp = (JSProperty *) JS_TRUE; + } + return ok; +} + +static JSBool +xml_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + JSBool ok; + + if (id == JS_DEFAULT_XML_NAMESPACE_ID) { + *vp = JSVAL_VOID; + return JS_TRUE; + } + + JS_LOCK_OBJ_VOID(cx, obj, ok = GetProperty(cx, obj, ID_TO_VALUE(id), vp)); + return ok; +} + +static JSBool +xml_setProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + JSBool ok; + + JS_LOCK_OBJ_VOID(cx, obj, ok = PutProperty(cx, obj, ID_TO_VALUE(id), vp)); + return ok; +} + +static JSBool +FoundProperty(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + JSBool *foundp) +{ + JSBool ok; + + if (prop == (JSProperty *) JS_TRUE) { + ok = *foundp = JS_TRUE; + } else { + JS_LOCK_OBJ_VOID(cx, obj, + ok = HasProperty(cx, obj, ID_TO_VALUE(id), foundp)); + } + return ok; +} + +static JSBool +xml_getAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + uintN *attrsp) +{ + JSBool found; + + if (!FoundProperty(cx, obj, id, prop, &found)) + return JS_FALSE; + *attrsp = found ? JSPROP_ENUMERATE : 0; + return JS_TRUE; +} + +static JSBool +xml_setAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, + uintN *attrsp) +{ + JSBool found; + + if (!FoundProperty(cx, obj, id, prop, &found)) + return JS_FALSE; + if (found) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_CANT_SET_XML_ATTRS); + } + return !found; +} + +static JSBool +xml_deleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval) +{ + JSBool ok; + + JS_LOCK_OBJ_VOID(cx, obj, + ok = DeleteProperty(cx, obj, ID_TO_VALUE(id), rval)); + return ok; +} + +static JSBool +xml_defaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) +{ + return JS_CallFunctionName(cx, obj, js_toString_str, 0, NULL, vp); +} + +static JSBool +xml_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, + jsval *statep, jsid *idp) +{ + return JS_TRUE; +} + +static JSBool +xml_checkAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, + jsval *vp, uintN *attrsp) +{ + if (!cx->runtime->checkObjectAccess) + return JS_TRUE; + return cx->runtime->checkObjectAccess(cx, obj, ID_TO_VALUE(id), mode, vp); +} + +static JSBool +xml_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + return JS_TRUE; +} + +static uint32 +xml_mark(JSContext *cx, JSObject *obj, void *arg) +{ + JSXML *xml; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + xml_mark_private(cx, xml, arg); + return 0; +} + +static void +xml_clear(JSContext *cx, JSObject *obj) +{ +} + +static JSBool +xml_callMethod(JSContext *cx, JSObject *obj, jsid id, uintN argc, jsval *argv, + jsval *rval) +{ + JSStackFrame *fp; + jsval fval; + JSXML *xml, *kid; + + if (!JS_InstanceOf(cx, obj, &js_XMLClass, argv)) + return JS_FALSE; + fp = cx->fp; + +retry: + /* 11.2.2.1 Step 3(d). */ + if (!js_GetProperty(cx, OBJ_GET_PROTO(cx, obj), id, &fval)) + return JS_FALSE; + + if (JSVAL_IS_VOID(fval)) { + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (xml->xml_class == JSXML_CLASS_LIST) { + JSScope *scope; + + JS_LOCK_OBJ(cx, obj); + scope = OBJ_SCOPE(obj); + if (xml->xml_kids.count == 1) { + xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + obj = js_GetXMLObject(cx, xml); + JS_UNLOCK_SCOPE(cx, scope); + if (!obj) + return JS_FALSE; + fp->argv[-1] = OBJECT_TO_JSVAL(obj); + fp->thisp = obj; + goto retry; + } + JS_UNLOCK_SCOPE(cx, scope); + } + + if (xml->xml_class != JSXML_CLASS_LIST) { + JSBool simple; + uint32 i, n; + JSString *str; + + switch (xml->xml_class) { + case JSXML_CLASS_COMMENT: + case JSXML_CLASS_PROCESSING_INSTRUCTION: + simple = JS_FALSE; + break; + default: + simple = JS_TRUE; + JS_LOCK_OBJ(cx, obj); + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid->xml_class == JSXML_CLASS_ELEMENT) { + simple = JS_FALSE; + break; + } + } + JS_UNLOCK_OBJ(cx, obj); + break; + } + if (simple) { + str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); + if (!str || !js_ValueToObject(cx, STRING_TO_JSVAL(str), &obj)) + return JS_FALSE; + fp->argv[-1] = OBJECT_TO_JSVAL(obj); + fp->thisp = obj; + goto retry; + } + } + } + + fp->argv[-2] = fval; + if (!js_Invoke(cx, fp->argc, JSFRAME_SKIP_CALLER)) + return JS_FALSE; + *rval = fp->sp[-1]; + return JS_TRUE; +} + +static JSBool +xml_enumerateValues(JSContext *cx, JSObject *obj, JSIterateOp enum_op, + jsval *statep, jsval *vp) +{ + return JS_TRUE; +} + +static JSBool +xml_equality(JSContext *cx, JSObject *obj, jsval v, jsval *vp) +{ + return JS_TRUE; +} + +static JSBool +xml_concatenate(JSContext *cx, JSObject *obj, jsval v, jsval *vp) +{ + return JS_TRUE; +} + +JSXMLObjectOps js_XMLObjectOps = { + { + /* Use js_NewObjectMap so XML objects satisfy OBJ_IS_NATIVE tests. */ + js_NewObjectMap, js_DestroyObjectMap, + xml_lookupProperty, xml_defineProperty, + xml_getProperty, xml_setProperty, + xml_getAttributes, xml_setAttributes, + xml_deleteProperty, xml_defaultValue, + xml_enumerate, xml_checkAccess, + NULL, NULL, + NULL, NULL, + NULL, xml_hasInstance, + js_SetProtoOrParent, js_SetProtoOrParent, + xml_mark, xml_clear, + NULL, NULL + }, + xml_callMethod, xml_enumerateValues, + xml_equality, xml_concatenate +}; + +static JSObjectOps * +xml_getObjectOps(JSContext *cx, JSClass *clasp) +{ + return &js_XMLObjectOps.base; +} + +JSClass js_XMLClass = { + js_XML_str, + JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, xml_finalize, + xml_getObjectOps, NULL, NULL, NULL, + NULL, NULL, NULL, NULL +}; + +static JSBool +xml_addNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + JSObject *nsobj; + JSXMLNamespace *ns; + JSBool ok; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + nsobj = js_ConstructObject(cx, &namespace_class, NULL, NULL, 1, argv); + if (!nsobj) + return JS_FALSE; + ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); + JS_LOCK_OBJ_VOID(cx, obj, ok = AddInScopeNamespace(cx, xml, ns)); + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSBool +xml_appendChild(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsval name, v; + JSBool ok; + JSObject *vobj; + JSXML *vxml; + + if (!js_GetAnyName(cx, &name)) + return JS_FALSE; + + JS_LOCK_OBJ(cx, obj); + ok = GetProperty(cx, obj, name, &v); + if (!ok) + goto out; + JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); + vobj = JSVAL_TO_OBJECT(v); + JS_ASSERT(OBJECT_IS_XML(cx, vobj)); + vxml = (JSXML *) JS_GetPrivate(cx, vobj); + JS_ASSERT(vxml->xml_class == JSXML_CLASS_LIST); + + ok = IndexToIdVal(cx, vxml->xml_kids.count, &name); + if (!ok) + goto out; + ok = PutProperty(cx, JSVAL_TO_OBJECT(v), name, &argv[0]); +out: + JS_UNLOCK_OBJ(cx, obj); + *rval = OBJECT_TO_JSVAL(obj); + return ok; +} + +/* XML and XMLList */ +static JSBool +xml_attribute(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXMLQName *qn; + jsval name; + JSBool ok; + + qn = ToAttributeName(cx, argv[0]); + if (!qn) + return JS_FALSE; + name = OBJECT_TO_JSVAL(qn->object); + JS_LOCK_OBJ_VOID(cx, obj, ok = GetProperty(cx, obj, name, rval)); + return ok; +} + +/* XML and XMLList */ +static JSBool +xml_attributes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsval name; + JSXMLQName *qn; + JSBool ok; + + name = ATOM_KEY(cx->runtime->atomState.starAtom); + qn = ToAttributeName(cx, name); + if (!qn) + return JS_FALSE; + name = OBJECT_TO_JSVAL(qn->object); + JS_LOCK_OBJ_VOID(cx, obj, ok = GetProperty(cx, obj, name, rval)); + return ok; +} + +/* XML and XMLList */ +static JSBool +xml_child_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval name, + jsval *rval) +{ + uint32 index; + JSBool ok; + JSXML *kid; + JSObject *kidobj; + + /* ECMA-357 13.4.4.6 */ + JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST); + + if (js_IdIsIndex(name, &index)) { + ok = JS_TRUE; + JS_LOCK_OBJ(cx, obj); + if (index >= JSXML_LENGTH(xml)) { + *rval = JSVAL_VOID; + } else { + kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + ok = JS_FALSE; + else + *rval = OBJECT_TO_JSVAL(kidobj); + } + JS_UNLOCK_OBJ(cx, obj); + return ok; + } + + JS_LOCK_OBJ_VOID(cx, obj, ok = GetProperty(cx, obj, name, rval)); + return ok; +} + +static JSBool +xml_child(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSXML *xml, *list, *kid, *vxml; + jsval name, v; + uint32 i, n; + JSObject *listobj, *kidobj; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + name = argv[0]; + if (xml->xml_class == JSXML_CLASS_LIST) { + /* ECMA-357 13.5.4.4 */ + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + return JS_FALSE; + + *rval = OBJECT_TO_JSVAL(listobj); + list = (JSXML *) JS_GetPrivate(cx, listobj); + list->xml_target = xml; + + for (i = 0, n = xml->xml_kids.count; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + return JS_FALSE; + if (!xml_child_helper(cx, kidobj, kid, name, &v)) + return JS_FALSE; + + JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); + vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); + if (JSXML_LENGTH(vxml) != 0 && !Append(cx, list, vxml)) + return JS_FALSE; + } + return JS_TRUE; + } + + return xml_child_helper(cx, obj, xml, name, rval); +} + +static JSBool +xml_childIndex(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *parent; + JSBool locked; + uint32 i, n; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + parent = xml->parent; + if (!parent || xml->xml_class == JSXML_CLASS_ATTRIBUTE) { + *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); + return JS_TRUE; + } + if (parent->object) { + JS_LOCK_OBJ(cx, parent->object); + locked = JS_TRUE; + } else { + locked = JS_FALSE; + } + for (i = 0, n = JSXML_LENGTH(parent); i < n; i++) { + if (XMLARRAY_MEMBER(&parent->xml_kids, i, JSXML) == xml) + break; + } + if (locked) + JS_UNLOCK_OBJ(cx, parent->object); + JS_ASSERT(i < n); + return js_NewNumberValue(cx, i, rval); +} + +/* XML and XMLList */ +static JSBool +xml_children(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsval name; + JSBool ok; + + name = ATOM_KEY(cx->runtime->atomState.starAtom); + JS_LOCK_OBJ_VOID(cx, obj, ok = GetProperty(cx, obj, name, rval)); + return ok; +} + +/* XML and XMLList */ +static JSBool +xml_comments(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *list, *kid, *vxml; + JSObject *listobj, *kidobj; + JSBool ok; + uint32 i, n; + jsval v; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + return JS_FALSE; + + *rval = OBJECT_TO_JSVAL(listobj); + list = (JSXML *) JS_GetPrivate(cx, listobj); + list->xml_target = xml; + + ok = JS_TRUE; + JS_LOCK_OBJ(cx, obj); + + if (xml->xml_class == JSXML_CLASS_LIST) { + /* 13.5.4.6 Step 2. */ + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid->xml_class == JSXML_CLASS_ELEMENT) { + ok = JS_EnterLocalRootScope(cx); + if (!ok) + break; + kidobj = js_GetXMLObject(cx, kid); + ok = kidobj + ? xml_comments(cx, kidobj, argc, argv, &v) + : JS_FALSE; + JS_LeaveLocalRootScope(cx); + if (!ok) + break; + vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); + if (JSXML_LENGTH(vxml) != 0) { + ok = Append(cx, list, vxml); + if (!ok) + break; + } + } + } + } else { + /* 13.4.4.9 Step 2. */ + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid->xml_class == JSXML_CLASS_COMMENT) { + ok = Append(cx, list, kid); + if (!ok) + break; + } + } + } + + JS_UNLOCK_OBJ(cx, obj); + return ok; +} + +/* XML and XMLList */ +static JSBool +xml_contains(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + JSBool ok; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + JS_LOCK_OBJ_VOID(cx, obj, ok = Equals(cx, xml, argv[0])); + return ok; +} + +/* XML and XMLList */ +static JSBool +xml_copy(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSXML *xml, *copy; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + JS_LOCK_OBJ_VOID(cx, obj, copy = DeepCopy(cx, xml)); + if (!copy) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(copy->object); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_descendants(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *list; + jsval name; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0]; + JS_LOCK_OBJ_VOID(cx, obj, list = Descendants(cx, xml, name)); + if (!list) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(list->object); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_elements(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *list, *kid, *vxml; + jsval name, v; + JSXMLQName *nameqn; + JSObject *listobj, *kidobj; + JSBool ok; + uint32 i, n; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0]; + nameqn = ToXMLName(cx, name); + if (!nameqn) + return JS_FALSE; + + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(listobj); + list = (JSXML *) JS_GetPrivate(cx, listobj); + list->xml_target = xml; + list->xml_targetprop = nameqn; + + ok = JS_TRUE; + JS_LOCK_OBJ(cx, obj); + + if (xml->xml_class == JSXML_CLASS_LIST) { + /* 13.5.4.6 */ + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid->xml_class == JSXML_CLASS_ELEMENT) { + ok = JS_EnterLocalRootScope(cx); + if (!ok) + break; + kidobj = js_GetXMLObject(cx, kid); + ok = kidobj + ? xml_elements(cx, kidobj, argc, argv, &v) + : JS_FALSE; + JS_LeaveLocalRootScope(cx); + if (!ok) + break; + vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); + if (JSXML_LENGTH(vxml) != 0) { + ok = Append(cx, list, vxml); + if (!ok) + break; + } + } + } + } else { + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid->xml_class == JSXML_CLASS_ELEMENT && + MatchElemName(nameqn, kid)) { + ok = Append(cx, list, kid); + if (!ok) + break; + } + } + } + + JS_UNLOCK_OBJ(cx, obj); + return ok; +} + +/* XML and XMLList */ +static JSBool +xml_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsval name; + JSBool ok, found; + + name = argv[0]; + JS_LOCK_OBJ_VOID(cx, obj, ok = HasProperty(cx, obj, name, &found)); + if (!ok) + return JS_FALSE; + if (!found) + return js_obj_hasOwnProperty(cx, obj, argc, argv, rval); + *rval = JSVAL_TRUE; + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_hasComplexContent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *kid; + JSObject *kidobj; + uint32 i, n; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + switch (xml->xml_class) { + case JSXML_CLASS_ATTRIBUTE: + case JSXML_CLASS_COMMENT: + case JSXML_CLASS_PROCESSING_INSTRUCTION: + case JSXML_CLASS_TEXT: + *rval = JSVAL_FALSE; + break; + case JSXML_CLASS_LIST: + JS_LOCK_OBJ(cx, obj); + if (xml->xml_kids.count == 0) { + *rval = JSVAL_TRUE; + } else if (xml->xml_kids.count == 1) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + kidobj = js_GetXMLObject(cx, kid); + JS_UNLOCK_OBJ(cx, obj); + if (!kidobj) + return JS_FALSE; + return xml_hasComplexContent(cx, kidobj, argc, argv, rval); + } + /* FALL THROUGH */ + default: + *rval = JSVAL_FALSE; + for (i = 0, n = xml->xml_kids.count; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid->xml_class == JSXML_CLASS_ELEMENT) { + *rval = JSVAL_TRUE; + break; + } + } + JS_UNLOCK_OBJ(cx, obj); + break; + } + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_hasSimpleContent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *kid; + JSObject *kidobj; + uint32 i, n; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + switch (xml->xml_class) { + case JSXML_CLASS_COMMENT: + case JSXML_CLASS_PROCESSING_INSTRUCTION: + *rval = JSVAL_FALSE; + break; + case JSXML_CLASS_LIST: + JS_LOCK_OBJ(cx, obj); + if (xml->xml_kids.count == 0) { + *rval = JSVAL_TRUE; + } else if (xml->xml_kids.count == 1) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); + kidobj = js_GetXMLObject(cx, kid); + JS_UNLOCK_OBJ(cx, obj); + if (!kidobj) + return JS_FALSE; + return xml_hasSimpleContent(cx, kidobj, argc, argv, rval); + } + /* FALL THROUGH */ + default: + *rval = JSVAL_TRUE; + for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid->xml_class == JSXML_CLASS_ELEMENT) { + *rval = JSVAL_FALSE; + break; + } + } + JS_UNLOCK_OBJ(cx, obj); + break; + } + return JS_TRUE; +} + +static JSBool +xml_inScopeNamespaces(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSObject *arrayobj, *nsobj; + JSXML *xml; + JSBool ok, locked; + uint32 length, i, j, n; + JSXMLNamespace *ns, *ns2; + jsval v; + + arrayobj = js_NewArrayObject(cx, 0, NULL); + if (!arrayobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(arrayobj); + length = 0; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + ok = JS_TRUE; + do { + if (xml->object) { + JS_LOCK_OBJ(cx, obj); + locked = JS_TRUE; + } else { + locked = JS_FALSE; + } + for (i = 0, n = xml->xml_namespaces.count; i < n; i++) { + ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); + + for (j = 0; j < length; j++) { + ok = JS_GetElement(cx, arrayobj, j, &v); + if (!ok) + goto break2; + nsobj = JSVAL_TO_OBJECT(v); + ns2 = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); + if ((ns2->prefix && ns->prefix) + ? !js_CompareStrings(ns2->prefix, ns->prefix) + : !js_CompareStrings(ns2->uri, ns->uri)) { + break; + } + } + + if (j == length) { + nsobj = js_GetXMLNamespaceObject(cx, ns); + if (!nsobj) { + ok = JS_FALSE; + break; + } + v = OBJECT_TO_JSVAL(nsobj); + ok = JS_SetElement(cx, arrayobj, length, &v); + if (!ok) + break; + ++length; + } + } + + break2: + xml = xml->parent; + if (locked) + JS_UNLOCK_OBJ(cx, obj); + } while (xml && ok); + return ok; +} + +#define XML_NYI(name) \ + JS_BEGIN_MACRO \ + JS_ReportError(cx, #name " is not yet implemented"); \ + return JS_FALSE; \ + JS_END_MACRO + +static JSBool +xml_insertChildAfter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + XML_NYI(insertChildAfter); +} + +static JSBool +xml_insertChildBefore(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + XML_NYI(insertChildBefore); +} + +/* XML and XMLList */ +static JSBool +xml_length(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSXML *xml; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (xml->xml_class != JSXML_CLASS_LIST) { + *rval = JSVAL_ONE; + } else { + if (!js_NewNumberValue(cx, xml->xml_kids.count, rval)) + return JS_FALSE; + } + return JS_TRUE; +} + +static JSBool +xml_localName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + *rval = xml->name ? STRING_TO_JSVAL(xml->name->localName) : JSVAL_NULL; + return JS_TRUE; +} + +static JSBool +xml_name(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSXML *xml; + JSObject *nameobj; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (!xml->name) { + *rval = JSVAL_NULL; + } else { + JS_LOCK_OBJ_VOID(cx, obj, + nameobj = js_GetXMLQNameObject(cx, xml->name)); + if (!nameobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(nameobj); + } + return JS_TRUE; +} + +static JSBool +xml_namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + XML_NYI(namespace); +} + +static JSBool +xml_namespaceDeclarations(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + XML_NYI(namespaceDeclarations); +} + +static JSBool +xml_nodeKind(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + XML_NYI(nodeKind); +} + +/* XML and XMLList */ +static JSBool +xml_normalize(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + XML_NYI(normalize); +} + +/* XML and XMLList */ +static JSBool +xml_parent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + XML_NYI(parent); +} + +/* XML and XMLList */ +static JSBool +xml_processingInstructions(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ + XML_NYI(processingInstructions); +} + +static JSBool +xml_prependChild(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + XML_NYI(prependChild); +} + +/* XML and XMLList */ +static JSBool +xml_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + XML_NYI(propertyIsEnumerable); +} + +static JSBool +xml_removeNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + XML_NYI(removeNamespace); +} + +static JSBool +xml_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + XML_NYI(replace); +} + +static JSBool +xml_setChildren(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + XML_NYI(setChildren); +} + +static JSBool +xml_setLocalName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + XML_NYI(setLocalName); +} + +static JSBool +xml_setName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + XML_NYI(setName); +} + +static JSBool +xml_setNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + XML_NYI(setNamespace); +} + +/* XML and XMLList */ +static JSBool +xml_text(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + XML_NYI(text); +} + +/* XML and XMLList */ +static JSBool +xml_toXMLString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSString *str; + + str = ToXMLString(cx, OBJECT_TO_JSVAL(obj), NULL, 0); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSXML *xml, *kid; + JSString *str; + uint32 i, n; + + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (xml->xml_class == JSXML_CLASS_ATTRIBUTE || + xml->xml_class == JSXML_CLASS_TEXT) { + str = xml->xml_value; + } else { + if (!xml_hasSimpleContent(cx, obj, argc, argv, rval)) + return JS_FALSE; + if (*rval == JSVAL_FALSE) + return xml_toXMLString(cx, obj, argc, argv, rval); + + str = cx->runtime->emptyString; + for (i = 0, n = xml->xml_kids.count; i < n; i++) { + kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); + if (kid->xml_class != JSXML_CLASS_COMMENT && + kid->xml_class != JSXML_CLASS_PROCESSING_INSTRUCTION) { + str = js_ConcatStrings(cx, str, kid->xml_value); + if (!str) + return JS_FALSE; + } + } + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +/* XML and XMLList */ +static JSBool +xml_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + *rval = OBJECT_TO_JSVAL(obj); + return JS_TRUE; +} + +static JSFunctionSpec xml_methods[] = { + {"addNamespace", xml_addNamespace, 1,0,0}, + {"appendChild", xml_appendChild, 1,0,0}, + {"attribute", xml_attribute, 1,0,0}, + {"attributes", xml_attributes, 0,0,0}, + {"child", xml_child, 1,0,0}, + {"childIndex", xml_childIndex, 0,0,0}, + {"children", xml_children, 0,0,0}, + {"comments", xml_comments, 0,0,0}, + {"contains", xml_contains, 1,0,0}, + {"copy", xml_copy, 0,0,0}, + {"descendants", xml_descendants, 1,0,0}, + {"elements", xml_elements, 1,0,0}, + {"hasOwnProperty", xml_hasOwnProperty, 1,0,0}, + {"hasComplexContent", xml_hasComplexContent, 1,0,0}, + {"hasSimpleContent", xml_hasSimpleContent, 1,0,0}, + {"inScopeNamespaces", xml_inScopeNamespaces, 0,0,0}, + {"insertChildAfter", xml_insertChildAfter, 2,0,0}, + {"insertChildBefore", xml_insertChildBefore, 2,0,0}, + {js_length_str, xml_length, 0,0,0}, + {js_localName_str, xml_localName, 0,0,0}, + {"name", xml_name, 0,0,0}, + {js_namespace_str, xml_namespace, 1,0,0}, + {"namespaceDeclarations", xml_namespaceDeclarations, 0,0,0}, + {"nodeKind", xml_nodeKind, 0,0,0}, + {"normalize", xml_normalize, 0,0,0}, + {"parent", xml_parent, 0,0,0}, + {"processingInstructions",xml_processingInstructions,1,0,0}, + {"prependChild", xml_prependChild, 1,0,0}, + {"propertyIsEnumerable", xml_propertyIsEnumerable, 1,0,0}, + {"removeNamespace", xml_removeNamespace, 1,0,0}, + {"replace", xml_replace, 2,0,0}, + {"setChildren", xml_setChildren, 1,0,0}, + {"setLocalName", xml_setLocalName, 1,0,0}, + {"setName", xml_setName, 1,0,0}, + {"setNamespace", xml_setNamespace, 1,0,0}, + {"text", xml_text, 0,0,0}, + {js_toString_str, xml_toString, 0,0,0}, + {js_toXMLString_str, xml_toXMLString, 0,0,0}, + {js_valueOf_str, xml_valueOf, 0,0,0}, + {0,0,0,0,0} +}; + +/* + * NB: These tinyids must + * (a) not collide with the generic negative tinyids at the top of jsfun.c; + * (b) index their corresponding xml_static_props array elements. + * Don't change 'em! + */ +enum xml_static_tinyid { + XML_IGNORE_COMMENTS, + XML_IGNORE_PROCESSING_INSTRUCTIONS, + XML_IGNORE_WHITESPACE, + XML_PRETTY_PRINTING, + XML_PRETTY_INDENT +}; + +static JSPropertySpec xml_static_props[] = { + {js_ignoreComments_str, XML_IGNORE_COMMENTS, JSPROP_PERMANENT, 0, 0}, + {js_ignoreProcessingInstructions_str, + XML_IGNORE_PROCESSING_INSTRUCTIONS, JSPROP_PERMANENT, 0, 0}, + {js_ignoreWhitespace_str, XML_IGNORE_WHITESPACE, JSPROP_PERMANENT, 0, 0}, + {js_prettyPrinting_str, XML_PRETTY_PRINTING, JSPROP_PERMANENT, 0, 0}, + {js_prettyIndent_str, XML_PRETTY_INDENT, JSPROP_PERMANENT, 0, 0}, + {0,0,0,0,0} +}; + +static JSBool +CopyXMLSettings(JSContext *cx, JSObject *from, JSObject *to) +{ + int i; + const char *name; + jsval v; + + for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) { + name = xml_static_props[i].name; + if (!JS_GetProperty(cx, from, name, &v)) + return JS_FALSE; + if (JSVAL_IS_BOOLEAN(v) && !JS_SetProperty(cx, to, name, &v)) + return JS_FALSE; + } + + name = xml_static_props[i].name; + if (!JS_GetProperty(cx, from, name, &v)) + return JS_FALSE; + if (JSVAL_IS_NUMBER(v) && !JS_SetProperty(cx, to, name, &v)) + return JS_FALSE; + return JS_TRUE; +} + +static JSBool +SetDefaultXMLSettings(JSContext *cx, JSObject *obj) +{ + int i; + jsval v; + + for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) { + v = JSVAL_TRUE; + if (!JS_SetProperty(cx, obj, xml_static_props[i].name, &v)) + return JS_FALSE; + } + v = INT_TO_JSVAL(2); + return JS_SetProperty(cx, obj, xml_static_props[i].name, &v); +} + +static JSBool +xml_settings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + JSObject *settings; + + settings = JS_NewObject(cx, NULL, NULL, NULL); + if (!settings) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(settings); + return CopyXMLSettings(cx, obj, settings); +} + +static JSBool +xml_setSettings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + jsval v; + JSObject *settings; + + v = argv[0]; + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) + return SetDefaultXMLSettings(cx, obj); + + if (JSVAL_IS_PRIMITIVE(v)) + return JS_TRUE; + settings = JSVAL_TO_OBJECT(v); + return CopyXMLSettings(cx, settings, obj); +} + +static JSBool +xml_defaultSettings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSObject *settings; + + settings = JS_NewObject(cx, NULL, NULL, NULL); + if (!settings) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(settings); + return SetDefaultXMLSettings(cx, settings); +} + +static JSFunctionSpec xml_static_methods[] = { + {"settings", xml_settings, 0,0,0}, + {"setSettings", xml_setSettings, 1,0,0}, + {"defaultSettings", xml_defaultSettings, 0,0,0}, + {0,0,0,0,0} +}; + +static JSBool +XML(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval v; + JSXML *xml, *copy; + JSObject *xobj, *vobj; + JSClass *clasp; + + v = argv[0]; + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) + v = STRING_TO_JSVAL(cx->runtime->emptyString); + + xml = ToXML(cx, v); + if (!xml) + return JS_FALSE; + xobj = xml->object; + *rval = OBJECT_TO_JSVAL(xobj); + + if ((cx->fp->flags & JSFRAME_CONSTRUCTING) && !JSVAL_IS_PRIMITIVE(v)) { + vobj = JSVAL_TO_OBJECT(v); + clasp = OBJ_GET_CLASS(cx, vobj); + if (clasp == &js_XMLClass || + (clasp->flags & JSCLASS_W3C_XML_INFO_ITEM)) { + copy = DeepCopy(cx, xml); + if (!copy) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(copy->object); + return JS_TRUE; + } + } + return JS_TRUE; +} + +static JSBool +XMLList(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + jsval v; + JSObject *vobj, *listobj; + JSXML *xml, *list; + + v = argv[0]; + if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) + v = STRING_TO_JSVAL(cx->runtime->emptyString); + + if ((cx->fp->flags & JSFRAME_CONSTRUCTING) && !JSVAL_IS_PRIMITIVE(v)) { + vobj = JSVAL_TO_OBJECT(v); + if (OBJ_GET_CLASS(cx, vobj) == &js_XMLClass) { + xml = (JSXML *) JS_GetPrivate(cx, vobj); + if (xml->xml_class == JSXML_CLASS_LIST) { + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(listobj); + + list = (JSXML *) JS_GetPrivate(cx, listobj); + if (!Append(cx, list, xml)) + return JS_FALSE; + return JS_TRUE; + } + } + } + + list = ToXMLList(cx, v); + if (!list) + return JS_FALSE; + *rval = OBJECT_TO_JSVAL(list->object); + return JS_TRUE; +} + +#define JSXML_LIST_SIZE sizeof(JSXML) +#define JSXML_ELEMENT_SIZE sizeof(JSXML) +#define JSXML_SIMPLEX_SIZE (offsetof(JSXML, u) + sizeof(JSString *)) + +static size_t sizeof_JSXML[JSXML_CLASS_LIMIT] = { + JSXML_LIST_SIZE, /* JSXML_CLASS_LIST */ + JSXML_ELEMENT_SIZE, /* JSXML_CLASS_ELEMENT */ + JSXML_SIMPLEX_SIZE, /* JSXML_CLASS_TEXT */ + JSXML_SIMPLEX_SIZE, /* JSXML_CLASS_ATTRIBUTE */ + JSXML_SIMPLEX_SIZE, /* JSXML_CLASS_COMMENT */ + JSXML_SIMPLEX_SIZE /* JSXML_CLASS_PROCESSING_INSTRUCTION */ +}; + +JSXML * +js_NewXML(JSContext *cx, JSXMLClass xml_class) +{ + JSXML *xml; + + xml = (JSXML *) JS_malloc(cx, sizeof_JSXML[xml_class]); + if (!xml) + return NULL; + + xml->object = NULL; + xml->parent = NULL; + xml->name = NULL; + xml->nrefs = 1; + xml->flags = 0; + xml->xml_class = xml_class; + if (JSXML_CLASS_HAS_VALUE(xml_class)) { + xml->xml_value = cx->runtime->emptyString; + } else { + XMLARRAY_INIT(cx, &xml->xml_kids, 0); + if (xml_class == JSXML_CLASS_LIST) { + xml->xml_target = NULL; + xml->xml_targetprop = NULL; + } else { + XMLARRAY_INIT(cx, &xml->xml_namespaces, 0); + XMLARRAY_INIT(cx, &xml->xml_attrs, 0); + } + } + return xml; +} + +void +js_DestroyXML(JSContext *cx, JSXML *xml) +{ + if (xml->object || JS_ATOMIC_DECREMENT(&xml->nrefs) != 0) + return; + if (xml->name) + js_DestroyXMLQName(cx, xml->name); + if (JSXML_HAS_KIDS(xml)) { + XMLARRAY_FINISH(cx, &xml->xml_kids, js_DestroyXML); + if (xml->xml_class != JSXML_CLASS_LIST) { + XMLARRAY_FINISH(cx, &xml->xml_namespaces, js_DestroyXMLNamespace); + XMLARRAY_FINISH(cx, &xml->xml_attrs, js_DestroyXML); + } + } +} + +static JSXMLQName * +StringToQName(JSContext *cx, JSString *str) +{ + jsval nsval; + JSXMLNamespace *ns; + + JS_ASSERT(JSSTRING_LENGTH(str) != 0 && *JSSTRING_CHARS(str) != '@'); + JS_ASSERT(JSSTRING_LENGTH(str) != 1 || *JSSTRING_CHARS(str) != '*'); + + if (!js_GetDefaultXMLNamespace(cx, &nsval)) + return NULL; + JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval)); + ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval)); + return js_NewXMLQName(cx, ns->uri, ns->prefix, str); +} + +JSXML * +js_ParseNodeToXML(JSContext *cx, JSParseNode *pn, uint32 flags) +{ + JSXML *xml, *kid, *attr; + JSParseNode *pn2; + uint32 i; + JSXMLQName *qn; + JSString *str; + JSXMLClass xml_class; + + xml = NULL; + switch (pn->pn_type) { + case TOK_XMLELEM: + pn2 = pn->pn_head; + xml = js_ParseNodeToXML(cx, pn2, flags); + if (!xml) + return NULL; + + for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) { + if (!pn2->pn_next) { + /* Don't append the end tag! */ + JS_ASSERT(pn2->pn_type == TOK_XMLETAGO); + break; + } + + kid = js_ParseNodeToXML(cx, pn2, flags); + if (!kid || !XMLARRAY_INSERT(cx, &xml->xml_kids, i, kid)) { + if (kid) + js_DestroyXML(cx, kid); + goto fail; + } + kid->parent = xml; + } + break; + + case TOK_XMLLIST: + xml = js_NewXML(cx, JSXML_CLASS_LIST); + if (!xml) + return NULL; + for (i = 0, pn2 = pn->pn_head; pn2; i++, pn2 = pn2->pn_next) { + kid = js_ParseNodeToXML(cx, pn2, flags); + if (!kid || !XMLARRAY_INSERT(cx, &xml->xml_kids, i, kid)) { + if (kid) + js_DestroyXML(cx, kid); + goto fail; + } + } + xml->flags = flags; + break; + + case TOK_XMLSTAGO: + case TOK_XMLPTAGC: + pn2 = pn->pn_head; + xml = js_ParseNodeToXML(cx, pn2, flags); + if (!xml) + return NULL; + + for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) { + if (pn2->pn_type != TOK_XMLNAME || pn2->pn_arity != PN_NULLARY) + goto syntax; + qn = StringToQName(cx, ATOM_TO_STRING(pn2->pn_atom)); + if (!qn) + goto fail; + + pn2 = pn2->pn_next; + JS_ASSERT(pn2); + if (pn2->pn_type != TOK_XMLATTR) { + js_DestroyXMLQName(cx, qn); + goto syntax; + } + attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE); + if (!attr || !XMLARRAY_INSERT(cx, &xml->xml_attrs, i, attr)) { + if (attr) + js_DestroyXML(cx, attr); + js_DestroyXMLQName(cx, qn); + goto fail; + } + + attr->parent = xml; + attr->name = qn; + attr->flags = flags; + attr->xml_value = ATOM_TO_STRING(pn2->pn_atom); + } + break; + + case TOK_XMLNAME: + if (pn->pn_arity == PN_LIST) + goto syntax; + JS_ASSERT(pn->pn_arity == PN_NULLARY); + qn = StringToQName(cx, ATOM_TO_STRING(pn->pn_atom)); + if (!qn) + return NULL; + + xml = js_NewXML(cx, JSXML_CLASS_ELEMENT); + if (!xml) { + js_DestroyXMLQName(cx, qn); + return NULL; + } + xml->name = qn; + xml->flags = flags; + break; + + case TOK_XMLTEXT: + case TOK_XMLCDATA: + case TOK_XMLCOMMENT: + case TOK_XMLPI: + qn = NULL; + str = ATOM_TO_STRING(pn->pn_atom); + if (pn->pn_type == TOK_XMLCOMMENT) { + xml_class = JSXML_CLASS_COMMENT; + } else if (pn->pn_type == TOK_XMLPI) { + qn = StringToQName(cx, str); + if (!qn) + return NULL; + str = pn->pn_atom2 + ? ATOM_TO_STRING(pn->pn_atom2) + : cx->runtime->emptyString; + xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION; + } else { + /* CDATA section content, or element text. */ + xml_class = JSXML_CLASS_TEXT; + } + + xml = js_NewXML(cx, xml_class); + if (!xml) { + if (qn) + js_DestroyXMLQName(cx, qn); + return NULL; + } + xml->name = qn; + xml->flags = flags; + xml->xml_value = str; + break; + + default: + goto syntax; + } + return xml; + +syntax: + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_XML_MARKUP); +fail: + if (xml) + js_DestroyXML(cx, xml); + return NULL; +} + +JSObject * +js_NewXMLObject(JSContext *cx, JSXMLClass xml_class) +{ + JSXML *xml; + JSObject *obj; + + xml = js_NewXML(cx, xml_class); + if (!xml) + return NULL; + obj = js_GetXMLObject(cx, xml); + if (!obj) + js_DestroyXML(cx, xml); + return obj; +} + +JSObject * +js_GetXMLObject(JSContext *cx, JSXML *xml) +{ + JSObject *obj; + + obj = xml->object; + if (obj) { + JS_ASSERT(JS_GetPrivate(cx, obj) == xml); + return obj; + } + + /* + * A JSXML cannot be shared among threads unless it has an object. + * A JSXML cannot be given an object unless: + * (a) it has no parent; or + * (b) its parent has no object (therefore is thread-private); or + * (c) its parent's object is locked. + */ + JS_ASSERT(!xml->parent || + !xml->parent->object || + JS_IS_OBJ_LOCKED(cx, xml->parent->object)); + + obj = js_NewObject(cx, &js_XMLClass, NULL, NULL); + if (!obj || !JS_SetPrivate(cx, obj, xml)) { + cx->newborn[GCX_OBJECT] = NULL; + return NULL; + } + xml->object = obj; + return obj; +} + +JSObject * +js_InitNamespaceClass(JSContext *cx, JSObject *obj) +{ + return JS_InitClass(cx, obj, NULL, &namespace_class, Namespace, 2, + namespace_props, namespace_methods, NULL, NULL); +} + +JSObject * +js_InitQNameClass(JSContext *cx, JSObject *obj) +{ + return JS_InitClass(cx, obj, NULL, &qname_class, QName, 2, + qname_props, qname_methods, NULL, NULL); +} + +JSObject * +js_InitXMLClass(JSContext *cx, JSObject *obj) +{ + JSObject *proto, *pobj, *ctor; + JSXML *xml; + JSProperty *prop; + JSScopeProperty *sprop; + jsval cval, argv[1], junk; + + /* Define the isXMLName function. */ + if (!JS_DefineFunction(cx, obj, js_isXMLName_str, xml_isXMLName, 1, 0)) + return NULL; + + /* Define the XML class constructor and prototype. */ + proto = JS_InitClass(cx, obj, NULL, &js_XMLClass, XML, 1, + NULL, xml_methods, + xml_static_props, xml_static_methods); + if (!proto) + return NULL; + xml = js_NewXML(cx, JSXML_CLASS_TEXT); + if (!xml || !JS_SetPrivate(cx, proto, xml)) + return NULL; + xml->object = proto; + + /* + * Prepare to set default settings on the XML constructor we just made. + * NB: We can't use JS_GetConstructor, because it calls OBJ_GET_PROPERTY, + * which is xml_getProperty, which creates a new XMLList every time! We + * must instead call js_LookupProperty directly. + */ + if (!js_LookupProperty(cx, proto, + ATOM_TO_JSID(cx->runtime->atomState.constructorAtom), + &pobj, &prop)) { + return NULL; + } + JS_ASSERT(prop); + sprop = (JSScopeProperty *) prop; + JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))); + cval = OBJ_GET_SLOT(cx, pobj, sprop->slot); + OBJ_DROP_PROPERTY(cx, pobj, prop); + JS_ASSERT(JSVAL_IS_FUNCTION(cx, cval)); + + /* Set default settings. */ + ctor = JSVAL_TO_OBJECT(cval); + argv[0] = JSVAL_VOID; + if (!xml_setSettings(cx, ctor, 0, argv, &junk)) + return NULL; + + /* Define the XMLList function. */ + if (!JS_DefineFunction(cx, obj, js_XMLList_str, XMLList, 1, 0)) + return NULL; + return proto; +} + +JSObject * +js_InitXMLClasses(JSContext *cx, JSObject *obj) +{ + if (!js_InitNamespaceClass(cx, obj) || !js_InitQNameClass(cx, obj)) + return NULL; + return js_InitXMLClass(cx, obj); +} + +/* + * Note the asymmetry between js_GetDefaultXMLNamespace and js_SetDefaultXML- + * Namespace. Get searches fp->scopeChain for JS_DEFAULT_XML_NAMESPACE_ID, + * while Set sets JS_DEFAULT_XML_NAMESPACE_ID in fp->varobj (unless fp is a + * lightweight function activation). There's no requirement that fp->varobj + * lie directly on fp->scopeChain, although it should be reachable using the + * prototype chain from a scope object (cf. JSOPTION_VAROBJFIX in jsapi.h). + * + * If Get can't find JS_DEFAULT_XML_NAMESPACE_ID along the scope chain, it + * creates a default namespace via 'new Namespace()'. In contrast, Set uses + * its v argument as the uri of a new Namespace, with "" as the prefix. See + * ECMA-357 12.1 and 12.1.1. Note that if Set is called with a Namespace n, + * the default XML namespace will be set to ("", n.uri). So the uri string + * is really the only usefully stored value of the default namespace. + */ +JSBool +js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp) +{ + JSStackFrame *fp; + JSObject *nsobj, *obj, *tmp; + jsval v; + + fp = cx->fp; + nsobj = fp->xmlNamespace; + if (nsobj) { + *vp = OBJECT_TO_JSVAL(nsobj); + return JS_TRUE; + } + + obj = NULL; + for (tmp = fp->scopeChain; tmp; tmp = OBJ_GET_PARENT(cx, obj)) { + obj = tmp; + if (!OBJ_GET_PROPERTY(cx, obj, JS_DEFAULT_XML_NAMESPACE_ID, &v)) + return JS_FALSE; + if (!JSVAL_IS_PRIMITIVE(v)) { + fp->xmlNamespace = JSVAL_TO_OBJECT(v); + *vp = v; + return JS_TRUE; + } + } + + nsobj = js_ConstructObject(cx, &namespace_class, NULL, obj, 0, NULL); + if (!nsobj) + return JS_FALSE; + v = OBJECT_TO_JSVAL(nsobj); + if (obj && !OBJ_SET_PROPERTY(cx, obj, JS_DEFAULT_XML_NAMESPACE_ID, &v)) + return JS_FALSE; + fp->xmlNamespace = nsobj; + *vp = v; + return JS_TRUE; +} + +JSBool +js_SetDefaultXMLNamespace(JSContext *cx, jsval v) +{ + jsval argv[2]; + JSObject *nsobj, *varobj; + JSStackFrame *fp; + + argv[0] = STRING_TO_JSVAL(cx->runtime->emptyString); + argv[1] = v; + nsobj = js_ConstructObject(cx, &namespace_class, NULL, NULL, 2, argv); + if (!nsobj) + return JS_FALSE; + v = OBJECT_TO_JSVAL(nsobj); + + fp = cx->fp; + varobj = fp->varobj; + if (varobj) { + if (!OBJ_SET_PROPERTY(cx, varobj, JS_DEFAULT_XML_NAMESPACE_ID, &v)) + return JS_FALSE; + } else { + JS_ASSERT(fp->fun && !(fp->fun->flags & JSFUN_HEAVYWEIGHT)); + } + fp->xmlNamespace = JSVAL_TO_OBJECT(v); + return JS_TRUE; +} + +JSBool +js_ToAttributeName(JSContext *cx, jsval *vp) +{ + JSXMLQName *qn; + + qn = ToAttributeName(cx, *vp); + if (!qn) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(qn->object); + return JS_TRUE; +} + +JSString * +js_EscapeAttributeValue(JSContext *cx, JSString *str) +{ + return EscapeAttributeValue(cx, NULL, str); +} + +JSString * +js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, JSString *str2) +{ + size_t len, len2, newlen; + jschar *chars; + + JS_ASSERT(!JSSTRING_IS_DEPENDENT(str) && + (*js_GetGCThingFlags(str) & GCF_MUTABLE)); + len = str->length; + len2 = JSSTRING_LENGTH(str2); + newlen = (isName) ? len + 1 + len2 : len + 2 + len2 + 1; + chars = (jschar *)JS_realloc(cx, str->chars, (newlen + 1) * sizeof(jschar)); + if (!chars) + return NULL; + str->chars = chars; + str->length = newlen; + chars += len; + if (isName) { + *chars++ = ' '; + js_strncpy(chars, JSSTRING_CHARS(str2), len2); + chars += len2; + } else { + *chars++ = '='; + *chars++ = '"'; + js_strncpy(chars, JSSTRING_CHARS(str2), len2); + chars += len2; + *chars++ = '"'; + } + *chars = 0; + return str; +} + +JSBool +js_GetAnyName(JSContext *cx, jsval *vp) +{ + JSRuntime *rt; + JSAtom *atom; + JSXMLQName *qn; + JSObject *obj; + + rt = cx->runtime; + atom = rt->atomState.lazy.anynameAtom; + if (!atom) { + qn = js_NewXMLQName(cx, rt->emptyString, rt->emptyString, + ATOM_TO_STRING(rt->atomState.starAtom)); + if (!qn) + return JS_FALSE; + + obj = js_NewObject(cx, &any_name_class, NULL, NULL); + if (!obj || !JS_SetPrivate(cx, obj, qn)) { + cx->newborn[GCX_OBJECT] = NULL; + return JS_FALSE; + } + qn->object = obj; + + atom = js_AtomizeObject(cx, obj, ATOM_PINNED); + if (!atom) + return JS_FALSE; + rt->atomState.lazy.anynameAtom = atom; + } + *vp = ATOM_KEY(atom); + return JS_TRUE; +} + +JSBool +js_BindXMLProperty(JSContext *cx, jsval lval, jsval rval) +{ + JSString *str; + uint32 index; + + if (!JSVAL_IS_PRIMITIVE(lval) && + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(lval)) == &js_XMLClass) { + /* + * Text fairly buried in 11.6.1 says that a numeric property id used + * with an XML object is reserved and should throw a TypeError. + */ + if (!js_IdIsIndex(rval, &index)) + return JS_TRUE; + lval = rval; + } + + str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, lval, NULL); + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_IS_NOT_XML_OBJECT, + JS_GetStringBytes(str)); + } + return JS_FALSE; +} + +JSBool +js_FindXMLProperty(JSContext *cx, jsval name, JSObject **objp, jsval *namep) +{ + JSXMLQName *qn; + jsid id; + JSObject *obj, *pobj, *lastobj; + JSProperty *prop; + + qn = ToXMLName(cx, name); + if (!qn) + return JS_FALSE; + id = OBJECT_TO_JSID(qn->object); + + obj = cx->fp->scopeChain; + do { + if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) + return JS_FALSE; + if (prop) { + OBJ_DROP_PROPERTY(cx, pobj, prop); + + if (OBJECT_IS_XML(cx, pobj)) { + *objp = pobj; + *namep = ID_TO_VALUE(id); + return JS_TRUE; + } + } + + lastobj = obj; + } while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL); + + *objp = lastobj; + *namep = JSVAL_VOID; + return JS_TRUE; +} + +JSBool +js_GetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp) +{ + JSBool ok; + + JS_LOCK_OBJ_VOID(cx, obj, ok = GetProperty(cx, obj, name, vp)); + return ok; +} + +JSBool +js_SetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp) +{ + JSBool ok; + + JS_LOCK_OBJ_VOID(cx, obj, ok = PutProperty(cx, obj, name, vp)); + return ok; +} + +static JSXML * +GetPrivate(JSContext *cx, JSObject *obj, const char *method) +{ + JSXML *xml; + + xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); + if (!xml) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_INCOMPATIBLE_METHOD, + js_XML_str, method, OBJ_GET_CLASS(cx, obj)->name); + } + return xml; +} + +JSBool +js_GetXMLDescendants(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSXML *xml, *list; + + xml = GetPrivate(cx, obj, "descendants internal method"); + if (!xml) + return JS_FALSE; + + list = Descendants(cx, xml, id); + if (!list) + return JS_FALSE; + *vp = OBJECT_TO_JSVAL(list->object); + return JS_TRUE; +} + +JSBool +js_FilterXMLList(JSContext *cx, JSObject *obj, jsbytecode *pc, uint32 len, + jsval *vp) +{ + JSBool ok, inLRS, match; + JSStackFrame *fp, frame; + JSObject *listobj, *resobj, *withobj, *kidobj; + JSXML *xml, *list, *result, *kid; + JSScript script; + uint32 i, n; + + ok = JS_TRUE; + inLRS = JS_FALSE; + fp = cx->fp; + frame = *fp; + frame.down = fp; + frame.flags |= JSFRAME_EVAL; + cx->fp = &frame; + + xml = GetPrivate(cx, obj, "filtering predicate operator"); + if (!xml) + goto bad; + + if (xml->xml_class == JSXML_CLASS_LIST) { + list = xml; + } else { + ok = JS_EnterLocalRootScope(cx); + if (!ok) + goto out; + inLRS = JS_TRUE; + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!listobj) + goto bad; + list = (JSXML *) JS_GetPrivate(cx, listobj); + ok = Append(cx, list, xml); + if (!ok) + goto out; + } + + /* Root resobj via frame.rval, whether or not we are inLRS. */ + resobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (!resobj) + goto bad; + result = (JSXML *) JS_GetPrivate(cx, resobj); + frame.rval = OBJECT_TO_JSVAL(resobj); + + /* Hoist the scope chain update out of the loop over kids. */ + withobj = js_NewObject(cx, &js_WithClass, NULL, fp->scopeChain); + if (!withobj) + goto bad; + frame.scopeChain = withobj; + + script = *fp->script; + script.code = script.main = pc; + script.length = len; + frame.script = &script; + + for (i = 0, n = list->xml_kids.count; i < n; i++) { + kid = XMLARRAY_MEMBER(&list->xml_kids, i, JSXML); + kidobj = js_GetXMLObject(cx, kid); + if (!kidobj) + goto bad; + OBJ_SET_PROTO(cx, withobj, kidobj); + ok = js_Interpret(cx, vp); + if (!ok) + goto out; + ok = js_ValueToBoolean(cx, *vp, &match); + if (!ok) + goto out; + if (match) { + ok = Append(cx, result, kid); + if (!ok) + goto out; + } + } + + *vp = OBJECT_TO_JSVAL(resobj); + +out: + if (inLRS) + JS_LeaveLocalRootScope(cx); + cx->fp = fp; + return ok; +bad: + ok = JS_FALSE; + goto out; +} + +JSObject * +js_ValueToXMLObject(JSContext *cx, jsval v) +{ + JSXML *xml; + + xml = ToXML(cx, v); + if (!xml) + return NULL; + return js_GetXMLObject(cx, xml); +} + +JSObject * +js_ValueToXMLListObject(JSContext *cx, jsval v) +{ + JSXML *xml; + + xml = ToXMLList(cx, v); + if (!xml) + return NULL; + return js_GetXMLObject(cx, xml); +} + +JSObject * +js_CloneXMLObject(JSContext *cx, JSObject *obj) +{ + JSObject *newobj; + JSXML *xml; + + newobj = js_NewObject(cx, &js_XMLClass, NULL, NULL); + xml = (JSXML *) JS_GetPrivate(cx, obj); + if (!newobj || !JS_SetPrivate(cx, newobj, xml)) { + cx->newborn[GCX_OBJECT] = NULL; + return NULL; + } + JS_ATOMIC_INCREMENT(&xml->nrefs); + return newobj; +} + +#endif /* JS_HAS_XML_SUPPORT */ diff --git a/mozilla/js/src/jsxml.h b/mozilla/js/src/jsxml.h new file mode 100644 index 00000000000..6762be0ad8c --- /dev/null +++ b/mozilla/js/src/jsxml.h @@ -0,0 +1,256 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is SpiderMonkey E4X code, released August, 2004. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef jsxml_h___ +#define jsxml_h___ + +#include "jsstddef.h" +#include "jspubtd.h" + +/* XXXbe move to jsprvtd.h or jspubtd.h */ +typedef struct JSXML JSXML; +typedef struct JSXMLNamespace JSXMLNamespace; +typedef struct JSXMLQName JSXMLQName; +typedef struct JSXMLArray JSXMLArray; + +extern const char js_isXMLName_str[]; +extern const char js_Namespace_str[]; +extern const char js_QName_str[]; +extern const char js_XML_str[]; +extern const char js_XMLList_str[]; + +extern const char js_amp_entity_str[]; +extern const char js_gt_entity_str[]; +extern const char js_lt_entity_str[]; +extern const char js_quot_entity_str[]; + +struct JSXMLNamespace { + JSObject *object; + JSString *prefix; + JSString *uri; +}; + +extern JSXMLNamespace * +js_NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri); + +extern void +js_DestroyXMLNamespace(JSContext *cx, JSXMLNamespace *ns); + +extern JSObject * +js_NewXMLNamespaceObject(JSContext *cx, JSString *prefix, JSString *uri); + +extern JSObject * +js_GetXMLNamespaceObject(JSContext *cx, JSXMLNamespace *ns); + +struct JSXMLQName { + JSObject *object; + JSString *uri; + JSString *prefix; + JSString *localName; +}; + +extern JSXMLQName * +js_NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, + JSString *localName); + +extern void +js_DestroyXMLQName(JSContext *cx, JSXMLQName *qn); + +extern JSObject * +js_NewXMLQNameObject(JSContext *cx, JSString *uri, JSString *prefix, + JSString *localName); + +extern JSObject * +js_GetXMLQNameObject(JSContext *cx, JSXMLQName *qn); + +extern JSObject * +js_ConstructQNameObject(JSContext *cx, jsval nsval, jsval lnval); + +typedef JSBool +(* JS_DLL_CALLBACK JSIdentityOp)(const void *a, const void *b); + +struct JSXMLArray { + uint32 count; + uint32 length; + void **vector; +}; + +typedef enum JSXMLClass { + JSXML_CLASS_LIST, + JSXML_CLASS_ELEMENT, + JSXML_CLASS_TEXT, + JSXML_CLASS_ATTRIBUTE, + JSXML_CLASS_COMMENT, + JSXML_CLASS_PROCESSING_INSTRUCTION, + JSXML_CLASS_LIMIT +} JSXMLClass; + +#define JSXML_CLASS_HAS_KIDS(xml_class) ((xml_class) < JSXML_CLASS_TEXT) +#define JSXML_CLASS_HAS_VALUE(xml_class) ((xml_class) >= JSXML_CLASS_TEXT) + +struct JSXML { + JSObject *object; + JSXML *parent; + JSXMLQName *name; + jsrefcount nrefs; /* references from JSObjects */ + uint32 flags; + JSXMLClass xml_class; + union { + struct { + JSXMLArray kids; /* NB: must come first */ + JSXML *target; + JSXMLQName *targetprop; + } list; + struct { + JSXMLArray kids; /* NB: must come first */ + JSXMLArray namespaces; + JSXMLArray attrs; + } elem; + JSString *value; + } u; +}; + +#define xml_kids u.list.kids +#define xml_target u.list.target +#define xml_targetprop u.list.targetprop +#define xml_namespaces u.elem.namespaces +#define xml_attrs u.elem.attrs +#define xml_value u.value + +#define JSXML_HAS_KIDS(xml) JSXML_CLASS_HAS_KIDS((xml)->xml_class) +#define JSXML_HAS_VALUE(xml) JSXML_CLASS_HAS_VALUE((xml)->xml_class) +#define JSXML_LENGTH(xml) (JSXML_CLASS_HAS_KIDS((xml)->xml_class) \ + ? (xml)->xml_kids.count \ + : 0) + +/* JSXML flag definitions. */ +#define XML_COPY_ON_WRITE 0x1 + +extern JSXML * +js_NewXML(JSContext *cx, JSXMLClass xml_class); + +extern void +js_DestroyXML(JSContext *cx, JSXML *xml); + +extern JSXML * +js_ParseNodeToXML(JSContext *cx, JSParseNode *pn, uint32 flags); + +extern JSObject * +js_NewXMLObject(JSContext *cx, JSXMLClass xml_class); + +extern JSObject * +js_GetXMLObject(JSContext *cx, JSXML *xml); + +extern JS_FRIEND_DATA(JSXMLObjectOps) js_XMLObjectOps; +extern JS_FRIEND_DATA(JSClass) js_XMLClass; + +/* + * NB: jsapi.h and jsobj.h must be included before any call to this macro. + */ +#define OBJECT_IS_XML(cx,obj) ((obj)->map->ops == &js_XMLObjectOps.base) +#define JSVAL_IS_XML(cx,v) (!JSVAL_IS_PRIMITIVE(v) && \ + OBJECT_IS_XML(cx, JSVAL_TO_OBJECT(v))) + +extern JSObject * +js_InitNamespaceClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitQNameClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitXMLClass(JSContext *cx, JSObject *obj); + +extern JSObject * +js_InitXMLClasses(JSContext *cx, JSObject *obj); + +extern JSBool +js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp); + +extern JSBool +js_SetDefaultXMLNamespace(JSContext *cx, jsval v); + +/* + * Return true if v is a XML QName object, or if it converts to a string that + * contains a valid XML qualified name (one containing no :), false otherwise. + * NB: This function is an infallible predicate, it hides exceptions. + */ +extern JSBool +js_IsXMLName(JSContext *cx, jsval v); + +extern JSBool +js_ToAttributeName(JSContext *cx, jsval *vp); + +extern JSString * +js_EscapeAttributeValue(JSContext *cx, JSString *str); + +extern JSString * +js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, + JSString *str2); + +extern JSBool +js_GetAnyName(JSContext *cx, jsval *vp); + +extern JSBool +js_BindXMLProperty(JSContext *cx, jsval lval, jsval rval); + +extern JSBool +js_FindXMLProperty(JSContext *cx, jsval name, JSObject **objp, jsval *namep); + +extern JSBool +js_GetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp); + +extern JSBool +js_SetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp); + +extern JSBool +js_GetXMLDescendants(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +extern JSBool +js_FilterXMLList(JSContext *cx, JSObject *obj, jsbytecode *pc, uint32 len, + jsval *vp); + +extern JSObject * +js_ValueToXMLObject(JSContext *cx, jsval v); + +extern JSObject * +js_ValueToXMLListObject(JSContext *cx, jsval v); + +extern JSObject * +js_CloneXMLObject(JSContext *cx, JSObject *obj); + +#endif /* jsxml_h___ */