mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-04 13:07:52 +00:00
Bug 309894: non-recursive XML-filtering implementation. r,a1.9=brendan
This commit is contained in:
parent
6a640d9c5f
commit
dd6b5cd3cf
@ -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")
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)))
|
||||
|
@ -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)) {
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
232
js/src/jsxml.c
232
js/src/jsxml.c
@ -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 *
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user