Bug 309894: non-recursive XML-filtering implementation. r,a1.9=brendan

This commit is contained in:
igor@mir2.org 2008-02-13 06:32:31 -08:00
parent 6a640d9c5f
commit dd6b5cd3cf
10 changed files with 299 additions and 154 deletions

View File

@ -296,7 +296,7 @@ MSG_DEF(JSMSG_KEYWORD_NOT_NS, 213, 0, JSEXN_SYNTAXERR, "keyword is used
MSG_DEF(JSMSG_BAD_GENERATOR_YIELD, 214, 1, JSEXN_TYPEERR, "yield from closing generator {0}")
MSG_DEF(JSMSG_BAD_GENERATOR_SYNTAX, 215, 1, JSEXN_SYNTAXERR, "{0} expression must be parenthesized")
MSG_DEF(JSMSG_ARRAY_COMP_LEFTSIDE, 216, 0, JSEXN_SYNTAXERR, "invalid array comprehension left-hand side")
MSG_DEF(JSMSG_YIELD_FROM_FILTER, 217, 0, JSEXN_INTERNALERR, "yield not yet supported from filtering predicate")
MSG_DEF(JSMSG_NON_XML_FILTER, 217, 1, JSEXN_TYPEERR, "XML filter is applied to non-XML value {0}")
MSG_DEF(JSMSG_EMPTY_ARRAY_REDUCE, 218, 0, JSEXN_TYPEERR, "reduce of empty array with no initial value")
MSG_DEF(JSMSG_NON_LIST_XML_METHOD, 219, 2, JSEXN_TYPEERR, "cannot call {0} method on an XML list with {1} elements")
MSG_DEF(JSMSG_BAD_DELETE_OPERAND, 220, 0, JSEXN_SYNTAXERR, "invalid delete operand")

View File

@ -262,6 +262,7 @@ static const char *statementName[] = {
"label statement", /* LABEL */
"if statement", /* IF */
"else statement", /* ELSE */
"destructuring body", /* BODY */
"switch statement", /* SWITCH */
"block", /* BLOCK */
js_with_statement_str, /* WITH */
@ -275,6 +276,8 @@ static const char *statementName[] = {
"while loop", /* WHILE_LOOP */
};
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(statementName) == STMT_LIMIT);
static const char *
StatementName(JSCodeGenerator *cg)
{
@ -5862,11 +5865,12 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
jmp = js_Emit3(cx, cg, JSOP_FILTER, 0, 0);
if (jmp < 0)
return JS_FALSE;
top = CG_OFFSET(cg);
if (!js_EmitTree(cx, cg, pn->pn_right))
return JS_FALSE;
if (js_Emit1(cx, cg, JSOP_ENDFILTER) < 0)
return JS_FALSE;
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);
if (EmitJump(cx, cg, JSOP_ENDFILTER, top - CG_OFFSET(cg)) < 0)
return JS_FALSE;
break;
#endif

View File

@ -78,7 +78,8 @@ typedef enum JSStmtType {
STMT_DO_LOOP, /* do/while loop statement */
STMT_FOR_LOOP, /* for loop statement */
STMT_FOR_IN_LOOP, /* for/in loop statement */
STMT_WHILE_LOOP /* while loop statement */
STMT_WHILE_LOOP, /* while loop statement */
STMT_LIMIT
} JSStmtType;
#define STMT_TYPE_IN_RANGE(t,b,e) ((uint)((t) - (b)) <= (uintN)((e) - (b)))

View File

@ -2028,6 +2028,61 @@ InternNonIntElementId(JSContext *cx, JSObject *obj, jsval idval, jsid *idp)
return js_ValueToStringId(cx, idval, idp);
}
/*
* Enter the new with scope using an object at sp[-1] and associate the depth
* of the with block with sp + stackIndex.
*/
static JSBool
EnterWith(JSContext *cx, jsint stackIndex)
{
JSStackFrame *fp;
jsval *sp;
JSObject *obj, *parent, *withobj;
fp = cx->fp;
sp = fp->sp;
JS_ASSERT(stackIndex < 0);
JS_ASSERT(fp->spbase <= sp + stackIndex);
if (!JSVAL_IS_PRIMITIVE(sp[-1])) {
obj = JSVAL_TO_OBJECT(sp[-1]);
} else {
obj = js_ValueToNonNullObject(cx, sp[-1]);
if (!obj)
return JS_FALSE;
sp[-1] = OBJECT_TO_JSVAL(obj);
}
parent = js_GetScopeChain(cx, fp);
if (!parent)
return JS_FALSE;
OBJ_TO_INNER_OBJECT(cx, obj);
if (!obj)
return JS_FALSE;
withobj = js_NewWithObject(cx, obj, parent,
sp + stackIndex - fp->spbase);
if (!withobj)
return JS_FALSE;
fp->scopeChain = withobj;
js_DisablePropertyCache(cx);
return JS_TRUE;
}
static void
LeaveWith(JSContext *cx)
{
JSObject *withobj;
withobj = cx->fp->scopeChain;
JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass);
cx->fp->scopeChain = OBJ_GET_PARENT(cx, withobj);
JS_SetPrivate(cx, withobj, NULL);
js_EnablePropertyCache(cx);
}
/*
* Threaded interpretation via computed goto appears to be well-supported by
* GCC 3 and higher. IBM's C compiler when run with the right options (e.g.,
@ -2231,7 +2286,7 @@ js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result)
uintN argc, attrs, flags, slot;
jsval *vp, lval, rval, ltmp, rtmp;
jsid id;
JSObject *withobj, *iterobj;
JSObject *iterobj;
JSProperty *prop;
JSScopeProperty *sprop;
JSString *str, *str2;
@ -2594,30 +2649,27 @@ interrupt:
BEGIN_CASE(JSOP_ENTERWITH)
SAVE_SP_AND_PC(fp);
FETCH_OBJECT(cx, -1, rval, obj);
OBJ_TO_INNER_OBJECT(cx, obj);
if (!obj || !(obj2 = js_GetScopeChain(cx, fp))) {
ok = JS_FALSE;
ok = EnterWith(cx, -1);
if (!ok)
goto out;
}
withobj = js_NewWithObject(cx, obj, obj2, sp - fp->spbase - 1);
if (!withobj) {
ok = JS_FALSE;
goto out;
}
fp->scopeChain = withobj;
STORE_OPND(-1, OBJECT_TO_JSVAL(withobj));
js_DisablePropertyCache(cx);
/*
* We must ensure that different "with" blocks have different
* stack depth associated with them. This allows the try handler
* search to properly recover the scope chain. Thus we must keep
* the stack at least at the current level.
*
* We set sp[-1] to the current "with" object to help asserting
* the enter/leave balance in [leavewith].
*/
sp[-1] = OBJECT_TO_JSVAL(fp->scopeChain);
END_CASE(JSOP_ENTERWITH)
BEGIN_CASE(JSOP_LEAVEWITH)
rval = POP_OPND();
JS_ASSERT(JSVAL_IS_OBJECT(rval));
withobj = JSVAL_TO_OBJECT(rval);
JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass);
fp->scopeChain = OBJ_GET_PARENT(cx, withobj);
JS_SetPrivate(cx, withobj, NULL);
js_EnablePropertyCache(cx);
JS_ASSERT(sp[-1] == OBJECT_TO_JSVAL(fp->scopeChain));
sp--;
SAVE_SP_AND_PC(fp);
LeaveWith(cx);
END_CASE(JSOP_LEAVEWITH)
BEGIN_CASE(JSOP_SETRVAL)
@ -6080,19 +6132,43 @@ interrupt:
END_CASE(JSOP_DESCENDANTS)
BEGIN_CASE(JSOP_FILTER)
/*
* We push the hole value before jumping to [enditer] so we can
* detect the first iteration and direct js_StepXMLListFilter to
* initialize filter's state.
*/
PUSH_OPND(JSVAL_HOLE);
len = GET_JUMP_OFFSET(pc);
SAVE_SP_AND_PC(fp);
FETCH_OBJECT(cx, -1, lval, obj);
ok = js_FilterXMLList(cx, obj, pc + js_CodeSpec[op].length, &rval);
if (!ok)
goto out;
JS_ASSERT(fp->sp == sp);
STORE_OPND(-1, rval);
JS_ASSERT(len > 0);
END_VARLEN_CASE
BEGIN_CASE(JSOP_ENDFILTER)
*result = POP_OPND();
goto out;
SAVE_SP_AND_PC(fp);
cond = (sp[-1] != JSVAL_HOLE);
if (cond) {
/* Exit the "with" block left from the previous iteration. */
LeaveWith(cx);
}
ok = js_StepXMLListFilter(cx, cond);
if (!ok)
goto out;
if (sp[-1] != JSVAL_NULL) {
/*
* Decrease sp after EnterWith returns as we use sp[-1] there
* to root temporaries.
*/
JS_ASSERT(VALUE_IS_XML(cx, sp[-1]));
ok = EnterWith(cx, -2);
if (!ok)
goto out;
sp--;
len = GET_JUMP_OFFSET(pc);
JS_ASSERT(len < 0);
CHECK_BRANCH(len);
DO_NEXT_OP(len);
}
sp--;
END_CASE(JSOP_ENDFILTER);
EMPTY_CASE(JSOP_STARTXML)
EMPTY_CASE(JSOP_STARTXMLEXPR)
@ -6338,7 +6414,7 @@ interrupt:
*/
SAVE_SP_AND_PC(fp);
ok = js_CloseIterator(cx, sp[-1]);
--sp;
sp--;
if (!ok)
goto out;
END_CASE(JSOP_ENDITER)
@ -6358,13 +6434,6 @@ interrupt:
BEGIN_CASE(JSOP_YIELD)
ASSERT_NOT_THROWING(cx);
if (fp->flags & JSFRAME_FILTERING) {
/* FIXME: bug 309894 -- fix to eliminate this error. */
JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL,
JSMSG_YIELD_FROM_FILTER);
ok = JS_FALSE;
goto out;
}
if (FRAME_TO_GENERATOR(fp)->state == JSGEN_CLOSING) {
js_ReportValueError(cx, JSMSG_BAD_GENERATOR_YIELD,
JSDVG_SEARCH_STACK, fp->argv[-2], NULL);
@ -6406,7 +6475,7 @@ interrupt:
ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
if (!ok)
goto out;
--sp;
sp--;
END_CASE(JSOP_ARRAYPUSH)
#endif /* JS_HAS_GENERATORS */
@ -6514,31 +6583,8 @@ interrupt:
out:
JS_ASSERT((size_t)(pc - script->code) < script->length);
if (!ok && cx->throwing && !(fp->flags & JSFRAME_FILTERING)) {
/*
* An exception has been raised and we are not in an XML filtering
* predicate expression. The latter check is necessary to avoid
* catching exceptions within the filtering predicate, such as this
* example taken from tests/e4x/Regress/regress-301596.js:
*
* try {
* <xml/>.(@a == 1);
* throw 5;
* } catch (e) {
* }
*
* The inner interpreter activation executing the predicate bytecode
* will throw "reference to undefined XML name @a" (or 5, in older
* versions that followed the first edition of ECMA-357 and evaluated
* unbound identifiers to undefined), and the exception must not be
* caught until control unwinds to the outer interpreter activation.
*
* Otherwise, the wrong stack depth will be restored by JSOP_SETSP,
* and the catch will move into the filtering predicate expression,
* leading to double catch execution if it rethrows.
*
* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=309894
*/
if (!ok && cx->throwing) {
/* An exception has been raised. */
JSTrapHandler handler;
JSTryNote *tn, *tnlimit;
uint32 offset;
@ -6618,7 +6664,8 @@ out:
fp->blockChain = obj;
JS_ASSERT(ok);
for (obj = fp->scopeChain; ; obj = OBJ_GET_PARENT(cx, obj)) {
for (;;) {
obj = fp->scopeChain;
clasp = OBJ_GET_CLASS(cx, obj);
if (clasp != &js_WithClass && clasp != &js_BlockClass)
break;
@ -6629,14 +6676,12 @@ out:
if (clasp == &js_BlockClass) {
/* Don't fail until after we've updated all stacks. */
ok &= js_PutBlockObject(cx, obj);
fp->scopeChain = OBJ_GET_PARENT(cx, obj);
} else {
JS_ASSERT(clasp == &js_WithClass);
JS_SetPrivate(cx, obj, NULL);
js_EnablePropertyCache(cx);
LeaveWith(cx);
}
}
fp->scopeChain = obj;
sp = fp->spbase + i;
/*
@ -6731,8 +6776,7 @@ out:
* (a) an inline call in the same js_Interpret;
* (b) an "out of line" call made through js_Invoke;
* (c) a js_Execute activation;
* (d) a generator (SendToGenerator, jsiter.c);
* (e) js_FilterXMLList.
* (d) a generator (SendToGenerator, jsiter.c).
*/
if (!ok) {
for (obj = fp->scopeChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {

View File

@ -106,11 +106,10 @@ typedef struct JSInlineFrame {
#define JSFRAME_EVAL 0x10 /* frame for obj_eval */
#define JSFRAME_SCRIPT_OBJECT 0x20 /* compiling source for a Script object */
#define JSFRAME_YIELDING 0x40 /* js_Interpret dispatched JSOP_YIELD */
#define JSFRAME_FILTERING 0x80 /* XML filtering predicate expression */
#define JSFRAME_ITERATOR 0x100 /* trying to get an iterator for for-in */
#define JSFRAME_POP_BLOCKS 0x200 /* scope chain contains blocks to pop */
#define JSFRAME_GENERATOR 0x400 /* frame belongs to generator-iterator */
#define JSFRAME_ROOTED_ARGV 0x800 /* frame.argv is rooted by the caller */
#define JSFRAME_ITERATOR 0x80 /* trying to get an iterator for for-in */
#define JSFRAME_POP_BLOCKS 0x100 /* scope chain contains blocks to pop */
#define JSFRAME_GENERATOR 0x200 /* frame belongs to generator-iterator */
#define JSFRAME_ROOTED_ARGV 0x400 /* frame.argv is rooted by the caller */
#define JSFRAME_OVERRIDE_SHIFT 24 /* override bit-set params; see jsfun.c */
#define JSFRAME_OVERRIDE_BITS 8

View File

@ -397,7 +397,7 @@ OPDEF(JSOP_SETXMLNAME, 171,"setxmlname", NULL, 1, 3, 1, 3, JOF_BYTE|J
OPDEF(JSOP_XMLNAME, 172,"xmlname", NULL, 1, 1, 1, 19, JOF_BYTE)
OPDEF(JSOP_DESCENDANTS, 173,"descendants",NULL, 1, 2, 1, 18, JOF_BYTE)
OPDEF(JSOP_FILTER, 174,"filter", NULL, 3, 1, 1, 0, JOF_JUMP)
OPDEF(JSOP_ENDFILTER, 175,"endfilter", NULL, 1, 1, 0, 18, JOF_BYTE)
OPDEF(JSOP_ENDFILTER, 175,"endfilter", NULL, 3, 2, 1, 18, JOF_JUMP)
OPDEF(JSOP_TOXML, 176,"toxml", NULL, 1, 1, 1, 19, JOF_BYTE)
OPDEF(JSOP_TOXMLLIST, 177,"toxmllist", NULL, 1, 1, 1, 19, JOF_BYTE)
OPDEF(JSOP_XMLTAGEXPR, 178,"xmltagexpr", NULL, 1, 1, 1, 0, JOF_BYTE)

View File

@ -50,12 +50,14 @@
# define QNAME_INIT js_InitQNameClass
# define ANYNAME_INIT js_InitAnyNameClass
# define ATTRIBUTE_INIT js_InitAttributeNameClass
# define XMLFILTER_INIT js_InitXMLFilterClass
#else
# define XML_INIT js_InitNullClass
# define NAMESPACE_INIT js_InitNullClass
# define QNAME_INIT js_InitNullClass
# define ANYNAME_INIT js_InitNullClass
# define ATTRIBUTE_INIT js_InitNullClass
# define XMLFILTER_INIT js_InitNullClass
#endif
#if JS_HAS_GENERATORS
@ -106,6 +108,7 @@ JS_PROTO(StopIteration, 27, js_InitIteratorClasses)
JS_PROTO(UnusedProto28, 28, js_InitNullClass)
JS_PROTO(File, 29, FILE_INIT)
JS_PROTO(Block, 30, js_InitBlockClass)
JS_PROTO(XMLFilter, 31, XMLFILTER_INIT)
#undef SCRIPT_INIT
#undef XML_INIT

View File

@ -202,7 +202,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id);
* before deserialization of bytecode. If the saved version does not match
* the current version, abort deserialization and invalidate the file.
*/
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 18)
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 19)
/*
* Library-private functions.

View File

@ -7717,6 +7717,8 @@ js_InitXMLClasses(JSContext *cx, JSObject *obj)
return NULL;
if (!js_InitAnyNameClass(cx, obj))
return NULL;
if (!js_InitXMLFilterClass(cx, obj))
return NULL;
return js_InitXMLClass(cx, obj);
}
@ -8188,87 +8190,175 @@ js_DeleteXMLListElements(JSContext *cx, JSObject *listobj)
return JS_TRUE;
}
JSBool
js_FilterXMLList(JSContext *cx, JSObject *obj, jsbytecode *pc, jsval *vp)
typedef struct JSXMLFilter {
JSXML *list;
JSXML *result;
JSXML *kid;
JSXMLArrayCursor cursor;
} JSXMLFilter;
static void
xmlfilter_trace(JSTracer *trc, JSObject *obj)
{
JSBool ok;
JSStackFrame *fp;
uint32 flags;
JSObject *scobj, *listobj, *resobj, *withobj, *kidobj;
JSXML *xml, *list, *result, *kid;
JSXMLArrayCursor cursor;
JSXMLFilter *filter;
ok = js_EnterLocalRootScope(cx);
if (!ok)
return JS_FALSE;
filter = (JSXMLFilter *) JS_GetPrivate(trc->context, obj);
if (!filter)
return;
/* All control flow after this point must exit via label out or bad. */
*vp = JSVAL_NULL;
fp = cx->fp;
flags = fp->flags;
fp->flags = flags | JSFRAME_FILTERING;
scobj = js_GetScopeChain(cx, fp);
withobj = NULL;
if (!scobj)
goto bad;
xml = GetPrivate(cx, obj, "filtering predicate operator");
if (!xml)
goto bad;
JS_ASSERT(filter->list);
JS_CALL_TRACER(trc, filter->list, JSTRACE_XML, "list");
if (filter->result)
JS_CALL_TRACER(trc, filter->result, JSTRACE_XML, "result");
if (filter->kid)
JS_CALL_TRACER(trc, filter->kid, JSTRACE_XML, "kid");
if (xml->xml_class == JSXML_CLASS_LIST) {
list = xml;
/*
* We do not need to trace the cursor as that would be done when
* tracing the filter->list.
*/
}
static void
xmlfilter_finalize(JSContext *cx, JSObject *obj)
{
JSXMLFilter *filter;
filter = (JSXMLFilter *) JS_GetPrivate(cx, obj);
if (!filter)
return;
XMLArrayCursorFinish(&filter->cursor);
JS_free(cx, filter);
}
JSClass js_XMLFilterClass = {
"XMLFilter",
JSCLASS_HAS_PRIVATE |
JSCLASS_IS_ANONYMOUS |
JSCLASS_MARK_IS_TRACE |
JSCLASS_HAS_CACHED_PROTO(JSProto_XMLFilter),
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, xmlfilter_finalize,
NULL, NULL, NULL, NULL,
NULL, NULL, JS_CLASS_TRACE(xmlfilter_trace), NULL
};
JSObject *
js_InitXMLFilterClass(JSContext *cx, JSObject *obj)
{
JSObject *proto;
proto = JS_InitClass(cx, obj, NULL, &js_XMLFilterClass, NULL, 0, NULL,
NULL, NULL, NULL);
if (!proto)
return NULL;
OBJ_SET_PROTO(cx, proto, NULL);
return proto;
}
JSBool
js_StepXMLListFilter(JSContext *cx, JSBool initialized)
{
jsval *sp;
JSObject *obj, *filterobj, *resobj, *kidobj;
JSXML *xml, *list;
JSXMLFilter *filter;
sp = cx->fp->sp;
if (!initialized) {
/*
* We haven't iterated yet, so initialize the filter based on the
* value stored in sp[-2].
*/
if (!VALUE_IS_XML(cx, sp[-2])) {
js_ReportValueError(cx, JSMSG_NON_XML_FILTER, -2, sp[-2], NULL);
return JS_FALSE;
}
obj = JSVAL_TO_OBJECT(sp[-2]);
xml = (JSXML *) JS_GetPrivate(cx, obj);
if (xml->xml_class == JSXML_CLASS_LIST) {
list = xml;
} else {
obj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
if (!obj)
return JS_FALSE;
/*
* Root just-created obj. sp[-2] cannot be used yet for rooting
* as it may be the only root holding xml.
*/
sp[-1] = OBJECT_TO_JSVAL(obj);
list = (JSXML *) JS_GetPrivate(cx, obj);
if (!Append(cx, list, xml))
return JS_FALSE;
}
filterobj = js_NewObject(cx, &js_XMLFilterClass, NULL, NULL);
if (!filterobj)
return JS_FALSE;
filter = JS_malloc(cx, sizeof *filter);
if (!filter)
return JS_FALSE;
/*
* Init all filter fields before JS_SetPrivate exposes it to
* xmlfilter_trace or xmlfilter_finalize.
*/
filter->list = list;
filter->result = NULL;
filter->kid = NULL;
XMLArrayCursorInit(&filter->cursor, &list->xml_kids);
JS_SetPrivate(cx, filterobj, filter);
/* Store filterobj to use in the later iterations. */
sp[-2] = OBJECT_TO_JSVAL(filterobj);
resobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
if (!resobj)
return JS_FALSE;
/* This also roots resobj. */
filter->result = (JSXML *) JS_GetPrivate(cx, resobj);
} else {
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;
/* We have iterated at least once. */
JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[-2]));
JS_ASSERT(OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(sp[-2])) ==
&js_XMLFilterClass);
filter = (JSXMLFilter *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(sp[-2]));
JS_ASSERT(filter->kid);
/* Check if the filter expression wants to append the element. */
if (js_ValueToBoolean(sp[-1]) &&
!Append(cx, filter->result, filter->kid)) {
return JS_FALSE;
}
}
resobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
if (!resobj)
goto bad;
result = (JSXML *) JS_GetPrivate(cx, resobj);
/* Hoist the scope chain update out of the loop over kids. */
withobj = js_NewWithObject(cx, NULL, scobj, -1);
if (!withobj)
goto bad;
fp->scopeChain = withobj;
XMLArrayCursorInit(&cursor, &list->xml_kids);
while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) {
kidobj = js_GetXMLObject(cx, kid);
/* Do the iteration. */
filter->kid = (JSXML *) XMLArrayCursorNext(&filter->cursor);
if (!filter->kid) {
/*
* Do not defer finishing the cursor until the next GC cycle to avoid
* accumulation of dead cursors associated with filter->list.
*/
XMLArrayCursorFinish(&filter->cursor);
JS_ASSERT(filter->result->object);
sp[-2] = OBJECT_TO_JSVAL(filter->result->object);
kidobj = NULL;
} else {
kidobj = js_GetXMLObject(cx, filter->kid);
if (!kidobj)
break;
OBJ_SET_PROTO(cx, withobj, kidobj);
ok = js_Interpret(cx, pc, vp);
if (ok && js_ValueToBoolean(*vp))
ok = Append(cx, result, kid);
if (!ok)
break;
return JS_FALSE;
}
XMLArrayCursorFinish(&cursor);
if (!ok)
goto out;
if (kid)
goto bad;
*vp = OBJECT_TO_JSVAL(resobj);
out:
fp->flags = flags | (fp->flags & JSFRAME_POP_BLOCKS);
if (withobj) {
fp->scopeChain = scobj;
JS_SetPrivate(cx, withobj, NULL);
}
js_LeaveLocalRootScopeWithResult(cx, *vp);
return ok;
bad:
ok = JS_FALSE;
goto out;
/* Null as kidobj at sp[-1] signals filter termination. */
sp[-1] = OBJECT_TO_JSVAL(kidobj);
return JS_TRUE;
}
JSObject *

View File

@ -229,6 +229,7 @@ extern JS_FRIEND_DATA(JSExtendedClass) js_NamespaceClass;
extern JS_FRIEND_DATA(JSExtendedClass) js_QNameClass;
extern JS_FRIEND_DATA(JSClass) js_AttributeNameClass;
extern JS_FRIEND_DATA(JSClass) js_AnyNameClass;
extern JSClass js_XMLFilterClass;
/*
* Macros to test whether an object or a value is of type "xml" (per typeof).
@ -315,8 +316,11 @@ js_GetXMLDescendants(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
extern JSBool
js_DeleteXMLListElements(JSContext *cx, JSObject *listobj);
extern JSObject *
js_InitXMLFilterClass(JSContext *cx, JSObject* obj);
extern JSBool
js_FilterXMLList(JSContext *cx, JSObject *obj, jsbytecode *pc, jsval *vp);
js_StepXMLListFilter(JSContext *cx, JSBool initialized);
extern JSObject *
js_ValueToXMLObject(JSContext *cx, jsval v);