2 * Copyright 2008 Jacek Caban for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Code in this file is based on files:
23 * from Mozilla project, released under LGPL 2.1 or later.
25 * The Original Code is Mozilla Communicator client code, released
28 * The Initial Developer of the Original Code is
29 * Netscape Communications Corporation.
30 * Portions created by the Initial Developer are Copyright (C) 1998
31 * the Initial Developer. All Rights Reserved.
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
42 #define JSREG_FOLD 0x01 /* fold uppercase to lowercase */
43 #define JSREG_GLOB 0x02 /* global exec, creates array of matches */
44 #define JSREG_MULTILINE 0x04 /* treat ^ and $ as begin and end of line */
45 #define JSREG_STICKY 0x08 /* only match starting at lastIndex */
47 typedef BYTE JSPackedBool;
48 typedef BYTE jsbytecode;
51 * This struct holds a bitmap representation of a class from a regexp.
52 * There's a list of these referenced by the classList field in the JSRegExp
53 * struct below. The initial state has startIndex set to the offset in the
54 * original regexp source of the beginning of the class contents. The first
55 * use of the class converts the source representation into a bitmap.
58 typedef struct RECharSet {
59 JSPackedBool converted;
72 WORD flags; /* flags, see jsapi.h's JSREG_* defines */
73 size_t parenCount; /* number of parenthesized submatches */
74 size_t classCount; /* count [...] bitmaps */
75 RECharSet *classList; /* list of [...] bitmaps */
76 BSTR source; /* locked source string, sans // */
77 jsbytecode program[1]; /* regular expression bytecode */
88 static const WCHAR sourceW[] = {'s','o','u','r','c','e',0};
89 static const WCHAR globalW[] = {'g','l','o','b','a','l',0};
90 static const WCHAR ignoreCaseW[] = {'i','g','n','o','r','e','C','a','s','e',0};
91 static const WCHAR multilineW[] = {'m','u','l','t','i','l','i','n','e',0};
92 static const WCHAR lastIndexW[] = {'l','a','s','t','I','n','d','e','x',0};
93 static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0};
94 static const WCHAR execW[] = {'e','x','e','c',0};
95 static const WCHAR testW[] = {'t','e','s','t',0};
97 static const WCHAR emptyW[] = {0};
99 /* FIXME: Better error handling */
100 #define ReportRegExpError(a,b,c)
101 #define ReportRegExpErrorHelper(a,b,c,d)
102 #define JS_ReportErrorNumber(a,b,c,d)
103 #define JS_ReportErrorFlagsAndNumber(a,b,c,d,e,f)
104 #define js_ReportOutOfScriptQuota(a)
105 #define JS_ReportOutOfMemory(a)
106 #define JS_COUNT_OPERATION(a,b)
108 #define JSMSG_MIN_TOO_BIG 47
109 #define JSMSG_MAX_TOO_BIG 48
110 #define JSMSG_OUT_OF_ORDER 49
111 #define JSMSG_OUT_OF_MEMORY 137
113 #define LINE_SEPARATOR 0x2028
114 #define PARA_SEPARATOR 0x2029
116 #define RE_IS_LETTER(c) (((c >= 'A') && (c <= 'Z')) || \
117 ((c >= 'a') && (c <= 'z')) )
118 #define RE_IS_LINE_TERM(c) ((c == '\n') || (c == '\r') || \
119 (c == LINE_SEPARATOR) || (c == PARA_SEPARATOR))
121 #define JS_ISWORD(c) ((c) < 128 && (isalnum(c) || (c) == '_'))
123 #define JS7_ISDEC(c) ((((unsigned)(c)) - '0') <= 9)
124 #define JS7_UNDEC(c) ((c) - '0')
176 REOP_LIMIT /* META: no operator >= to this */
179 #define REOP_IS_SIMPLE(op) ((op) <= REOP_NCLASS)
181 static const char *reop_names[] = {
234 typedef struct RECapture {
235 ptrdiff_t index; /* start of contents, -1 for empty */
236 size_t length; /* length of capture */
239 typedef struct REMatchState {
241 RECapture parens[1]; /* first of 're->parenCount' captures,
242 allocated at end of this struct */
245 typedef struct REProgState {
246 jsbytecode *continue_pc; /* current continuation data */
247 jsbytecode continue_op;
248 ptrdiff_t index; /* progress in text */
249 size_t parenSoFar; /* highest indexed paren started */
252 UINT min; /* current quantifier limits */
256 size_t top; /* backtrack stack state */
262 typedef struct REBackTrackData {
263 size_t sz; /* size of previous stack entry */
264 jsbytecode *backtrack_pc; /* where to backtrack to */
265 jsbytecode backtrack_op;
266 const WCHAR *cp; /* index in text of match at backtrack */
267 size_t parenIndex; /* start index of saved paren contents */
268 size_t parenCount; /* # of saved paren contents */
269 size_t saveStateStackTop; /* number of parent states */
270 /* saved parent states follow */
271 /* saved paren contents follow */
274 #define INITIAL_STATESTACK 100
275 #define INITIAL_BACKTRACK 8000
277 typedef struct REGlobalData {
279 JSRegExp *regexp; /* the RE in execution */
280 BOOL ok; /* runtime error (out_of_memory only?) */
281 size_t start; /* offset to start at */
282 ptrdiff_t skipped; /* chars skipped anchoring this r.e. */
283 const WCHAR *cpbegin; /* text base address */
284 const WCHAR *cpend; /* text limit address */
286 REProgState *stateStack; /* stack of state of current parents */
287 size_t stateStackTop;
288 size_t stateStackLimit;
290 REBackTrackData *backTrackStack;/* stack of matched-so-far positions */
291 REBackTrackData *backTrackSP;
292 size_t backTrackStackSize;
293 size_t cursz; /* size of current stack entry */
294 size_t backTrackCount; /* how many times we've backtracked */
295 size_t backTrackLimit; /* upper limit on backtrack states */
297 jsheap_t *pool; /* It's faster to use one malloc'd pool
298 than to malloc/free the three items
299 that are allocated from this pool */
302 typedef struct RENode RENode;
304 REOp op; /* r.e. op bytecode */
305 RENode *next; /* next in concatenation order */
306 void *kid; /* first operand */
308 void *kid2; /* second operand */
309 INT num; /* could be a number */
310 size_t parenIndex; /* or a parenthesis index */
311 struct { /* or a quantifier range */
316 struct { /* or a character class */
318 size_t kidlen; /* length of string at kid, in jschars */
319 size_t index; /* index into class list */
320 WORD bmsize; /* bitmap size, based on max char code */
323 struct { /* or a literal sequence */
324 WCHAR chr; /* of one character */
325 size_t length; /* or many (via the kid) */
328 RENode *kid2; /* second operand from ALT */
329 WCHAR ch1; /* match char for ALTPREREQ */
330 WCHAR ch2; /* ditto, or class index for ALTPREREQ2 */
335 #define CLASS_CACHE_SIZE 4
337 typedef struct CompilerState {
338 script_ctx_t *context;
339 const WCHAR *cpbegin;
343 size_t classCount; /* number of [] encountered */
344 size_t treeDepth; /* maximum depth of parse tree */
345 size_t progLength; /* estimated bytecode length */
347 size_t classBitmapsMem; /* memory to hold all class bitmaps */
349 const WCHAR *start; /* small cache of class strings */
350 size_t length; /* since they're often the same */
352 } classCache[CLASS_CACHE_SIZE];
356 typedef struct EmitStateStackEntry {
357 jsbytecode *altHead; /* start of REOP_ALT* opcode */
358 jsbytecode *nextAltFixup; /* fixup pointer to next-alt offset */
359 jsbytecode *nextTermFixup; /* fixup ptr. to REOP_JUMP offset */
360 jsbytecode *endTermFixup; /* fixup ptr. to REOPT_ALTPREREQ* offset */
361 RENode *continueNode; /* original REOP_ALT* node being stacked */
362 jsbytecode continueOp; /* REOP_JUMP or REOP_ENDALT continuation */
363 JSPackedBool jumpToJumpFlag; /* true if we've patched jump-to-jump to
364 avoid 16-bit unsigned offset overflow */
365 } EmitStateStackEntry;
368 * Immediate operand sizes and getter/setters. Unlike the ones in jsopcode.h,
369 * the getters and setters take the pc of the offset, not of the opcode before
373 #define GET_ARG(pc) ((WORD)(((pc)[0] << 8) | (pc)[1]))
374 #define SET_ARG(pc, arg) ((pc)[0] = (jsbytecode) ((arg) >> 8), \
375 (pc)[1] = (jsbytecode) (arg))
377 #define OFFSET_LEN ARG_LEN
378 #define OFFSET_MAX ((1 << (ARG_LEN * 8)) - 1)
379 #define GET_OFFSET(pc) GET_ARG(pc)
381 static BOOL ParseRegExp(CompilerState*);
384 * Maximum supported tree depth is maximum size of EmitStateStackEntry stack.
385 * For sanity, we limit it to 2^24 bytes.
387 #define TREE_DEPTH_MAX ((1 << 24) / sizeof(EmitStateStackEntry))
390 * The maximum memory that can be allocated for class bitmaps.
391 * For sanity, we limit it to 2^24 bytes.
393 #define CLASS_BITMAPS_MEM_LIMIT (1 << 24)
396 * Functions to get size and write/read bytecode that represent small indexes
398 * Each byte in the code represent 7-bit chunk of the index. 8th bit when set
399 * indicates that the following byte brings more bits to the index. Otherwise
400 * this is the last byte in the index bytecode representing highest index bits.
403 GetCompactIndexWidth(size_t index)
407 for (width = 1; (index >>= 7) != 0; ++width) { }
411 static inline jsbytecode *
412 WriteCompactIndex(jsbytecode *pc, size_t index)
416 while ((next = index >> 7) != 0) {
417 *pc++ = (jsbytecode)(index | 0x80);
420 *pc++ = (jsbytecode)index;
424 static inline jsbytecode *
425 ReadCompactIndex(jsbytecode *pc, size_t *result)
430 if ((nextByte & 0x80) == 0) {
432 * Short-circuit the most common case when compact index <= 127.
437 *result = 0x7F & nextByte;
440 *result |= (nextByte & 0x7F) << shift;
442 } while ((nextByte & 0x80) != 0);
447 /* Construct and initialize an RENode, returning NULL for out-of-memory */
449 NewRENode(CompilerState *state, REOp op)
453 ren = jsheap_alloc(&state->context->tmp_heap, sizeof(*ren));
455 /* js_ReportOutOfScriptQuota(cx); */
465 * Validates and converts hex ascii value.
468 isASCIIHexDigit(WCHAR c, UINT *digit)
479 if (cv >= 'a' && cv <= 'f') {
480 *digit = cv - 'a' + 10;
492 #define JUMP_OFFSET_HI(off) ((jsbytecode)((off) >> 8))
493 #define JUMP_OFFSET_LO(off) ((jsbytecode)(off))
496 SetForwardJumpOffset(jsbytecode *jump, jsbytecode *target)
498 ptrdiff_t offset = target - jump;
500 /* Check that target really points forward. */
502 if ((size_t)offset > OFFSET_MAX)
505 jump[0] = JUMP_OFFSET_HI(offset);
506 jump[1] = JUMP_OFFSET_LO(offset);
511 * Generate bytecode for the tree rooted at t using an explicit stack instead
515 EmitREBytecode(CompilerState *state, JSRegExp *re, size_t treeDepth,
516 jsbytecode *pc, RENode *t)
518 EmitStateStackEntry *emitStateSP, *emitStateStack;
522 if (treeDepth == 0) {
523 emitStateStack = NULL;
525 emitStateStack = heap_alloc(sizeof(EmitStateStackEntry) * treeDepth);
529 emitStateSP = emitStateStack;
531 assert(op < REOP_LIMIT);
540 case REOP_ALTPREREQ2:
543 emitStateSP->altHead = pc - 1;
544 emitStateSP->endTermFixup = pc;
546 SET_ARG(pc, t->u.altprereq.ch1);
548 SET_ARG(pc, t->u.altprereq.ch2);
551 emitStateSP->nextAltFixup = pc; /* offset to next alternate */
554 emitStateSP->continueNode = t;
555 emitStateSP->continueOp = REOP_JUMP;
556 emitStateSP->jumpToJumpFlag = FALSE;
558 assert((size_t)(emitStateSP - emitStateStack) <= treeDepth);
561 assert(op < REOP_LIMIT);
565 emitStateSP->nextTermFixup = pc; /* offset to following term */
567 if (!SetForwardJumpOffset(emitStateSP->nextAltFixup, pc))
569 emitStateSP->continueOp = REOP_ENDALT;
571 assert((size_t)(emitStateSP - emitStateStack) <= treeDepth);
574 assert(op < REOP_LIMIT);
579 * If we already patched emitStateSP->nextTermFixup to jump to
580 * a nearer jump, to avoid 16-bit immediate offset overflow, we
583 if (emitStateSP->jumpToJumpFlag)
587 * Fix up the REOP_JUMP offset to go to the op after REOP_ENDALT.
588 * REOP_ENDALT is executed only on successful match of the last
589 * alternate in a group.
591 if (!SetForwardJumpOffset(emitStateSP->nextTermFixup, pc))
593 if (t->op != REOP_ALT) {
594 if (!SetForwardJumpOffset(emitStateSP->endTermFixup, pc))
599 * If the program is bigger than the REOP_JUMP offset range, then
600 * we must check for alternates before this one that are part of
601 * the same group, and fix up their jump offsets to target jumps
602 * close enough to fit in a 16-bit unsigned offset immediate.
604 if ((size_t)(pc - re->program) > OFFSET_MAX &&
605 emitStateSP > emitStateStack) {
606 EmitStateStackEntry *esp, *esp2;
607 jsbytecode *alt, *jump;
608 ptrdiff_t span, header;
612 for (esp = esp2 - 1; esp >= emitStateStack; --esp) {
613 if (esp->continueOp == REOP_ENDALT &&
614 !esp->jumpToJumpFlag &&
615 esp->nextTermFixup + OFFSET_LEN == alt &&
616 (size_t)(pc - ((esp->continueNode->op != REOP_ALT)
618 : esp->nextTermFixup)) > OFFSET_MAX) {
620 jump = esp->nextTermFixup;
623 * The span must be 1 less than the distance from
624 * jump offset to jump offset, so we actually jump
625 * to a REOP_JUMP bytecode, not to its offset!
628 assert(jump < esp2->nextTermFixup);
629 span = esp2->nextTermFixup - jump - 1;
630 if ((size_t)span <= OFFSET_MAX)
635 } while (esp2->continueOp != REOP_ENDALT);
638 jump[0] = JUMP_OFFSET_HI(span);
639 jump[1] = JUMP_OFFSET_LO(span);
641 if (esp->continueNode->op != REOP_ALT) {
643 * We must patch the offset at esp->endTermFixup
644 * as well, for the REOP_ALTPREREQ{,2} opcodes.
645 * If we're unlucky and endTermFixup is more than
646 * OFFSET_MAX bytes from its target, we cheat by
647 * jumping 6 bytes to the jump whose offset is at
648 * esp->nextTermFixup, which has the same target.
650 jump = esp->endTermFixup;
651 header = esp->nextTermFixup - jump;
653 if ((size_t)span > OFFSET_MAX)
656 jump[0] = JUMP_OFFSET_HI(span);
657 jump[1] = JUMP_OFFSET_LO(span);
660 esp->jumpToJumpFlag = TRUE;
668 emitStateSP->altHead = pc - 1;
669 emitStateSP->nextAltFixup = pc; /* offset to next alternate */
671 emitStateSP->continueNode = t;
672 emitStateSP->continueOp = REOP_JUMP;
673 emitStateSP->jumpToJumpFlag = FALSE;
675 assert((size_t)(emitStateSP - emitStateStack) <= treeDepth);
678 assert(op < REOP_LIMIT);
683 * Coalesce FLATs if possible and if it would not increase bytecode
684 * beyond preallocated limit. The latter happens only when bytecode
685 * size for coalesced string with offset p and length 2 exceeds 6
686 * bytes preallocated for 2 single char nodes, i.e. when
687 * 1 + GetCompactIndexWidth(p) + GetCompactIndexWidth(2) > 6 or
688 * GetCompactIndexWidth(p) > 4.
689 * Since when GetCompactIndexWidth(p) <= 4 coalescing of 3 or more
690 * nodes strictly decreases bytecode size, the check has to be
691 * done only for the first coalescing.
694 GetCompactIndexWidth((WCHAR*)t->kid - state->cpbegin) <= 4)
697 t->next->op == REOP_FLAT &&
698 (WCHAR*)t->kid + t->u.flat.length ==
700 t->u.flat.length += t->next->u.flat.length;
701 t->next = t->next->next;
704 if (t->kid && t->u.flat.length > 1) {
705 pc[-1] = (state->flags & JSREG_FOLD) ? REOP_FLATi : REOP_FLAT;
706 pc = WriteCompactIndex(pc, (WCHAR*)t->kid - state->cpbegin);
707 pc = WriteCompactIndex(pc, t->u.flat.length);
708 } else if (t->u.flat.chr < 256) {
709 pc[-1] = (state->flags & JSREG_FOLD) ? REOP_FLAT1i : REOP_FLAT1;
710 *pc++ = (jsbytecode) t->u.flat.chr;
712 pc[-1] = (state->flags & JSREG_FOLD)
715 SET_ARG(pc, t->u.flat.chr);
722 pc = WriteCompactIndex(pc, t->u.parenIndex);
723 emitStateSP->continueNode = t;
724 emitStateSP->continueOp = REOP_RPAREN;
726 assert((size_t)(emitStateSP - emitStateStack) <= treeDepth);
732 pc = WriteCompactIndex(pc, t->u.parenIndex);
736 pc = WriteCompactIndex(pc, t->u.parenIndex);
741 emitStateSP->nextTermFixup = pc;
743 emitStateSP->continueNode = t;
744 emitStateSP->continueOp = REOP_ASSERTTEST;
746 assert((size_t)(emitStateSP - emitStateStack) <= treeDepth);
751 case REOP_ASSERTTEST:
752 case REOP_ASSERTNOTTEST:
753 if (!SetForwardJumpOffset(emitStateSP->nextTermFixup, pc))
757 case REOP_ASSERT_NOT:
759 emitStateSP->nextTermFixup = pc;
761 emitStateSP->continueNode = t;
762 emitStateSP->continueOp = REOP_ASSERTNOTTEST;
764 assert((size_t)(emitStateSP - emitStateStack) <= treeDepth);
771 if (t->u.range.min == 0 && t->u.range.max == (UINT)-1) {
772 pc[-1] = (t->u.range.greedy) ? REOP_STAR : REOP_MINIMALSTAR;
773 } else if (t->u.range.min == 0 && t->u.range.max == 1) {
774 pc[-1] = (t->u.range.greedy) ? REOP_OPT : REOP_MINIMALOPT;
775 } else if (t->u.range.min == 1 && t->u.range.max == (UINT) -1) {
776 pc[-1] = (t->u.range.greedy) ? REOP_PLUS : REOP_MINIMALPLUS;
778 if (!t->u.range.greedy)
779 pc[-1] = REOP_MINIMALQUANT;
780 pc = WriteCompactIndex(pc, t->u.range.min);
782 * Write max + 1 to avoid using size_t(max) + 1 bytes
783 * for (UINT)-1 sentinel.
785 pc = WriteCompactIndex(pc, t->u.range.max + 1);
787 emitStateSP->nextTermFixup = pc;
789 emitStateSP->continueNode = t;
790 emitStateSP->continueOp = REOP_ENDCHILD;
792 assert((size_t)(emitStateSP - emitStateStack) <= treeDepth);
798 if (!SetForwardJumpOffset(emitStateSP->nextTermFixup, pc))
803 if (!t->u.ucclass.sense)
804 pc[-1] = REOP_NCLASS;
805 pc = WriteCompactIndex(pc, t->u.ucclass.index);
806 charSet = &re->classList[t->u.ucclass.index];
807 charSet->converted = FALSE;
808 charSet->length = t->u.ucclass.bmsize;
809 charSet->u.src.startIndex = t->u.ucclass.startIndex;
810 charSet->u.src.length = t->u.ucclass.kidlen;
811 charSet->sense = t->u.ucclass.sense;
822 if (emitStateSP == emitStateStack)
825 t = emitStateSP->continueNode;
826 op = (REOp) emitStateSP->continueOp;
831 heap_free(emitStateStack);
835 ReportRegExpError(state, JSREPORT_ERROR, JSMSG_REGEXP_TOO_COMPLEX);
841 * Process the op against the two top operands, reducing them to a single
842 * operand in the penultimate slot. Update progLength and treeDepth.
845 ProcessOp(CompilerState *state, REOpData *opData, RENode **operandStack,
850 switch (opData->op) {
852 result = NewRENode(state, REOP_ALT);
855 result->kid = operandStack[operandSP - 2];
856 result->u.kid2 = operandStack[operandSP - 1];
857 operandStack[operandSP - 2] = result;
859 if (state->treeDepth == TREE_DEPTH_MAX) {
860 ReportRegExpError(state, JSREPORT_ERROR, JSMSG_REGEXP_TOO_COMPLEX);
866 * Look at both alternates to see if there's a FLAT or a CLASS at
867 * the start of each. If so, use a prerequisite match.
869 if (((RENode *) result->kid)->op == REOP_FLAT &&
870 ((RENode *) result->u.kid2)->op == REOP_FLAT &&
871 (state->flags & JSREG_FOLD) == 0) {
872 result->op = REOP_ALTPREREQ;
873 result->u.altprereq.ch1 = ((RENode *) result->kid)->u.flat.chr;
874 result->u.altprereq.ch2 = ((RENode *) result->u.kid2)->u.flat.chr;
875 /* ALTPREREQ, <end>, uch1, uch2, <next>, ...,
876 JUMP, <end> ... ENDALT */
877 state->progLength += 13;
880 if (((RENode *) result->kid)->op == REOP_CLASS &&
881 ((RENode *) result->kid)->u.ucclass.index < 256 &&
882 ((RENode *) result->u.kid2)->op == REOP_FLAT &&
883 (state->flags & JSREG_FOLD) == 0) {
884 result->op = REOP_ALTPREREQ2;
885 result->u.altprereq.ch1 = ((RENode *) result->u.kid2)->u.flat.chr;
886 result->u.altprereq.ch2 = ((RENode *) result->kid)->u.ucclass.index;
887 /* ALTPREREQ2, <end>, uch1, uch2, <next>, ...,
888 JUMP, <end> ... ENDALT */
889 state->progLength += 13;
892 if (((RENode *) result->kid)->op == REOP_FLAT &&
893 ((RENode *) result->u.kid2)->op == REOP_CLASS &&
894 ((RENode *) result->u.kid2)->u.ucclass.index < 256 &&
895 (state->flags & JSREG_FOLD) == 0) {
896 result->op = REOP_ALTPREREQ2;
897 result->u.altprereq.ch1 = ((RENode *) result->kid)->u.flat.chr;
898 result->u.altprereq.ch2 =
899 ((RENode *) result->u.kid2)->u.ucclass.index;
900 /* ALTPREREQ2, <end>, uch1, uch2, <next>, ...,
901 JUMP, <end> ... ENDALT */
902 state->progLength += 13;
905 /* ALT, <next>, ..., JUMP, <end> ... ENDALT */
906 state->progLength += 7;
911 result = operandStack[operandSP - 2];
913 result = result->next;
914 result->next = operandStack[operandSP - 1];
918 case REOP_ASSERT_NOT:
921 /* These should have been processed by a close paren. */
922 ReportRegExpErrorHelper(state, JSREPORT_ERROR, JSMSG_MISSING_PAREN,
932 * Hack two bits in CompilerState.flags, for use within FindParenCount to flag
933 * its being on the stack, and to propagate errors to its callers.
935 #define JSREG_FIND_PAREN_COUNT 0x8000
936 #define JSREG_FIND_PAREN_ERROR 0x4000
939 * Magic return value from FindParenCount and GetDecimalValue, to indicate
940 * overflow beyond GetDecimalValue's max parameter, or a computed maximum if
941 * its findMax parameter is non-null.
943 #define OVERFLOW_VALUE ((UINT)-1)
946 FindParenCount(CompilerState *state)
951 if (state->flags & JSREG_FIND_PAREN_COUNT)
952 return OVERFLOW_VALUE;
955 * Copy state into temp, flag it so we never report an invalid backref,
956 * and reset its members to parse the entire regexp. This is obviously
957 * suboptimal, but GetDecimalValue calls us only if a backref appears to
958 * refer to a forward parenthetical, which is rare.
961 temp.flags |= JSREG_FIND_PAREN_COUNT;
962 temp.cp = temp.cpbegin;
967 temp.classBitmapsMem = 0;
968 for (i = 0; i < CLASS_CACHE_SIZE; i++)
969 temp.classCache[i].start = NULL;
971 if (!ParseRegExp(&temp)) {
972 state->flags |= JSREG_FIND_PAREN_ERROR;
973 return OVERFLOW_VALUE;
975 return temp.parenCount;
979 * Extract and return a decimal value at state->cp. The initial character c
980 * has already been read. Return OVERFLOW_VALUE if the result exceeds max.
981 * Callers who pass a non-null findMax should test JSREG_FIND_PAREN_ERROR in
982 * state->flags to discover whether an error occurred under findMax.
985 GetDecimalValue(WCHAR c, UINT max, UINT (*findMax)(CompilerState *state),
986 CompilerState *state)
988 UINT value = JS7_UNDEC(c);
989 BOOL overflow = (value > max && (!findMax || value > findMax(state)));
991 /* The following restriction allows simpler overflow checks. */
992 assert(max <= ((UINT)-1 - 9) / 10);
993 while (state->cp < state->cpend) {
997 value = 10 * value + JS7_UNDEC(c);
998 if (!overflow && value > max && (!findMax || value > findMax(state)))
1002 return overflow ? OVERFLOW_VALUE : value;
1006 * Calculate the total size of the bitmap required for a class expression.
1009 CalculateBitmapSize(CompilerState *state, RENode *target, const WCHAR *src,
1013 BOOL inRange = FALSE;
1014 WCHAR c, rangeStart = 0;
1015 UINT n, digit, nDigits, i;
1017 target->u.ucclass.bmsize = 0;
1018 target->u.ucclass.sense = TRUE;
1025 target->u.ucclass.sense = FALSE;
1028 while (src != end) {
1029 BOOL canStartRange = TRUE;
1056 if (src < end && RE_IS_LETTER(*src)) {
1057 localMax = (UINT) (*src++) & 0x1F;
1070 for (i = 0; (i < nDigits) && (src < end); i++) {
1072 if (!isASCIIHexDigit(c, &digit)) {
1074 * Back off to accepting the original
1081 n = (n << 4) | digit;
1086 canStartRange = FALSE;
1088 JS_ReportErrorNumber(state->context,
1089 js_GetErrorMessage, NULL,
1090 JSMSG_BAD_CLASS_RANGE);
1100 canStartRange = FALSE;
1102 JS_ReportErrorNumber(state->context,
1103 js_GetErrorMessage, NULL,
1104 JSMSG_BAD_CLASS_RANGE);
1110 * If this is the start of a range, ensure that it's less than
1124 * This is a non-ECMA extension - decimal escapes (in this
1125 * case, octal!) are supposed to be an error inside class
1126 * ranges, but supported here for backwards compatibility.
1131 if ('0' <= c && c <= '7') {
1133 n = 8 * n + JS7_UNDEC(c);
1135 if ('0' <= c && c <= '7') {
1137 i = 8 * n + JS7_UNDEC(c);
1158 /* Throw a SyntaxError here, per ECMA-262, 15.10.2.15. */
1159 if (rangeStart > localMax) {
1160 JS_ReportErrorNumber(state->context,
1161 js_GetErrorMessage, NULL,
1162 JSMSG_BAD_CLASS_RANGE);
1167 if (canStartRange && src < end - 1) {
1171 rangeStart = (WCHAR)localMax;
1175 if (state->flags & JSREG_FOLD)
1176 rangeStart = localMax; /* one run of the uc/dc loop below */
1179 if (state->flags & JSREG_FOLD) {
1180 WCHAR maxch = localMax;
1182 for (i = rangeStart; i <= localMax; i++) {
1198 target->u.ucclass.bmsize = max;
1203 ParseMinMaxQuantifier(CompilerState *state, BOOL ignoreValues)
1207 const WCHAR *errp = state->cp++;
1212 min = GetDecimalValue(c, 0xFFFF, NULL, state);
1215 if (!ignoreValues && min == OVERFLOW_VALUE)
1216 return JSMSG_MIN_TOO_BIG;
1222 max = GetDecimalValue(c, 0xFFFF, NULL, state);
1224 if (!ignoreValues && max == OVERFLOW_VALUE)
1225 return JSMSG_MAX_TOO_BIG;
1226 if (!ignoreValues && min > max)
1227 return JSMSG_OUT_OF_ORDER;
1235 state->result = NewRENode(state, REOP_QUANT);
1237 return JSMSG_OUT_OF_MEMORY;
1238 state->result->u.range.min = min;
1239 state->result->u.range.max = max;
1241 * QUANT, <min>, <max>, <next> ... <ENDCHILD>
1242 * where <max> is written as compact(max+1) to make
1243 * (UINT)-1 sentinel to occupy 1 byte, not width_of(max)+1.
1245 state->progLength += (1 + GetCompactIndexWidth(min)
1246 + GetCompactIndexWidth(max + 1)
1257 ParseQuantifier(CompilerState *state)
1260 term = state->result;
1261 if (state->cp < state->cpend) {
1262 switch (*state->cp) {
1264 state->result = NewRENode(state, REOP_QUANT);
1267 state->result->u.range.min = 1;
1268 state->result->u.range.max = (UINT)-1;
1269 /* <PLUS>, <next> ... <ENDCHILD> */
1270 state->progLength += 4;
1273 state->result = NewRENode(state, REOP_QUANT);
1276 state->result->u.range.min = 0;
1277 state->result->u.range.max = (UINT)-1;
1278 /* <STAR>, <next> ... <ENDCHILD> */
1279 state->progLength += 4;
1282 state->result = NewRENode(state, REOP_QUANT);
1285 state->result->u.range.min = 0;
1286 state->result->u.range.max = 1;
1287 /* <OPT>, <next> ... <ENDCHILD> */
1288 state->progLength += 4;
1290 case '{': /* balance '}' */
1294 err = ParseMinMaxQuantifier(state, FALSE);
1300 ReportRegExpErrorHelper(state, JSREPORT_ERROR, err, errp);
1309 if (state->treeDepth == TREE_DEPTH_MAX) {
1310 ReportRegExpError(state, JSREPORT_ERROR, JSMSG_REGEXP_TOO_COMPLEX);
1316 state->result->kid = term;
1317 if (state->cp < state->cpend && *state->cp == '?') {
1319 state->result->u.range.greedy = FALSE;
1321 state->result->u.range.greedy = TRUE;
1327 * item: assertion An item is either an assertion or
1328 * quantatom a quantified atom.
1330 * assertion: '^' Assertions match beginning of string
1331 * (or line if the class static property
1332 * RegExp.multiline is true).
1333 * '$' End of string (or line if the class
1334 * static property RegExp.multiline is
1336 * '\b' Word boundary (between \w and \W).
1337 * '\B' Word non-boundary.
1339 * quantatom: atom An unquantified atom.
1340 * quantatom '{' n ',' m '}'
1341 * Atom must occur between n and m times.
1342 * quantatom '{' n ',' '}' Atom must occur at least n times.
1343 * quantatom '{' n '}' Atom must occur exactly n times.
1344 * quantatom '*' Zero or more times (same as {0,}).
1345 * quantatom '+' One or more times (same as {1,}).
1346 * quantatom '?' Zero or one time (same as {0,1}).
1348 * any of which can be optionally followed by '?' for ungreedy
1350 * atom: '(' regexp ')' A parenthesized regexp (what matched
1351 * can be addressed using a backreference,
1353 * '.' Matches any char except '\n'.
1354 * '[' classlist ']' A character class.
1355 * '[' '^' classlist ']' A negated character class.
1357 * '\n' Newline (Line Feed).
1358 * '\r' Carriage Return.
1359 * '\t' Horizontal Tab.
1360 * '\v' Vertical Tab.
1361 * '\d' A digit (same as [0-9]).
1363 * '\w' A word character, [0-9a-z_A-Z].
1364 * '\W' A non-word character.
1365 * '\s' A whitespace character, [ \b\f\n\r\t\v].
1366 * '\S' A non-whitespace character.
1367 * '\' n A backreference to the nth (n decimal
1368 * and positive) parenthesized expression.
1369 * '\' octal An octal escape sequence (octal must be
1370 * two or three digits long, unless it is
1371 * 0 for the null character).
1372 * '\x' hex A hex escape (hex must be two digits).
1373 * '\u' unicode A unicode escape (must be four digits).
1374 * '\c' ctrl A control character, ctrl is a letter.
1375 * '\' literalatomchar Any character except one of the above
1376 * that follow '\' in an atom.
1377 * otheratomchar Any character not first among the other
1378 * atom right-hand sides.
1381 ParseTerm(CompilerState *state)
1383 WCHAR c = *state->cp++;
1385 UINT num, tmp, n, i;
1386 const WCHAR *termStart;
1389 /* assertions and atoms */
1391 state->result = NewRENode(state, REOP_BOL);
1394 state->progLength++;
1397 state->result = NewRENode(state, REOP_EOL);
1400 state->progLength++;
1403 if (state->cp >= state->cpend) {
1404 /* a trailing '\' is an error */
1405 ReportRegExpError(state, JSREPORT_ERROR, JSMSG_TRAILING_SLASH);
1410 /* assertion escapes */
1412 state->result = NewRENode(state, REOP_WBDRY);
1415 state->progLength++;
1418 state->result = NewRENode(state, REOP_WNONBDRY);
1421 state->progLength++;
1423 /* Decimal escape */
1425 /* Give a strict warning. See also the note below. */
1426 WARN("non-octal digit in an escape sequence that doesn't match a back-reference\n");
1429 while (state->cp < state->cpend) {
1431 if (c < '0' || '7' < c)
1434 tmp = 8 * num + (UINT)JS7_UNDEC(c);
1441 state->result = NewRENode(state, REOP_FLAT);
1444 state->result->u.flat.chr = c;
1445 state->result->u.flat.length = 1;
1446 state->progLength += 3;
1457 termStart = state->cp - 1;
1458 num = GetDecimalValue(c, state->parenCount, FindParenCount, state);
1459 if (state->flags & JSREG_FIND_PAREN_ERROR)
1461 if (num == OVERFLOW_VALUE) {
1462 /* Give a strict mode warning. */
1463 WARN("back-reference exceeds number of capturing parentheses\n");
1466 * Note: ECMA 262, 15.10.2.9 says that we should throw a syntax
1467 * error here. However, for compatibility with IE, we treat the
1468 * whole backref as flat if the first character in it is not a
1469 * valid octal character, and as an octal escape otherwise.
1471 state->cp = termStart;
1473 /* Treat this as flat. termStart - 1 is the \. */
1478 /* Treat this as an octal escape. */
1481 assert(1 <= num && num <= 0x10000);
1482 state->result = NewRENode(state, REOP_BACKREF);
1485 state->result->u.parenIndex = num - 1;
1487 += 1 + GetCompactIndexWidth(state->result->u.parenIndex);
1489 /* Control escape */
1505 /* Control letter */
1507 if (state->cp < state->cpend && RE_IS_LETTER(*state->cp)) {
1508 c = (WCHAR) (*state->cp++ & 0x1F);
1510 /* back off to accepting the original '\' as a literal */
1515 /* HexEscapeSequence */
1519 /* UnicodeEscapeSequence */
1524 for (i = 0; i < nDigits && state->cp < state->cpend; i++) {
1527 if (!isASCIIHexDigit(c, &digit)) {
1529 * Back off to accepting the original 'u' or 'x' as a
1536 n = (n << 4) | digit;
1540 /* Character class escapes */
1542 state->result = NewRENode(state, REOP_DIGIT);
1546 state->progLength++;
1549 state->result = NewRENode(state, REOP_NONDIGIT);
1552 state->result = NewRENode(state, REOP_SPACE);
1555 state->result = NewRENode(state, REOP_NONSPACE);
1558 state->result = NewRENode(state, REOP_ALNUM);
1561 state->result = NewRENode(state, REOP_NONALNUM);
1563 /* IdentityEscape */
1565 state->result = NewRENode(state, REOP_FLAT);
1568 state->result->u.flat.chr = c;
1569 state->result->u.flat.length = 1;
1570 state->result->kid = (void *) (state->cp - 1);
1571 state->progLength += 3;
1576 state->result = NewRENode(state, REOP_CLASS);
1579 termStart = state->cp;
1580 state->result->u.ucclass.startIndex = termStart - state->cpbegin;
1582 if (state->cp == state->cpend) {
1583 ReportRegExpErrorHelper(state, JSREPORT_ERROR,
1584 JSMSG_UNTERM_CLASS, termStart);
1588 if (*state->cp == '\\') {
1590 if (state->cp != state->cpend)
1594 if (*state->cp == ']') {
1595 state->result->u.ucclass.kidlen = state->cp - termStart;
1600 for (i = 0; i < CLASS_CACHE_SIZE; i++) {
1601 if (!state->classCache[i].start) {
1602 state->classCache[i].start = termStart;
1603 state->classCache[i].length = state->result->u.ucclass.kidlen;
1604 state->classCache[i].index = state->classCount;
1607 if (state->classCache[i].length ==
1608 state->result->u.ucclass.kidlen) {
1609 for (n = 0; ; n++) {
1610 if (n == state->classCache[i].length) {
1611 state->result->u.ucclass.index
1612 = state->classCache[i].index;
1615 if (state->classCache[i].start[n] != termStart[n])
1620 state->result->u.ucclass.index = state->classCount++;
1624 * Call CalculateBitmapSize now as we want any errors it finds
1625 * to be reported during the parse phase, not at execution.
1627 if (!CalculateBitmapSize(state, state->result, termStart, state->cp++))
1630 * Update classBitmapsMem with number of bytes to hold bmsize bits,
1631 * which is (bitsCount + 7) / 8 or (highest_bit + 1 + 7) / 8
1632 * or highest_bit / 8 + 1 where highest_bit is u.ucclass.bmsize.
1634 n = (state->result->u.ucclass.bmsize >> 3) + 1;
1635 if (n > CLASS_BITMAPS_MEM_LIMIT - state->classBitmapsMem) {
1636 ReportRegExpError(state, JSREPORT_ERROR, JSMSG_REGEXP_TOO_COMPLEX);
1639 state->classBitmapsMem += n;
1640 /* CLASS, <index> */
1642 += 1 + GetCompactIndexWidth(state->result->u.ucclass.index);
1646 state->result = NewRENode(state, REOP_DOT);
1651 const WCHAR *errp = state->cp--;
1654 err = ParseMinMaxQuantifier(state, TRUE);
1665 ReportRegExpErrorHelper(state, JSREPORT_ERROR,
1666 JSMSG_BAD_QUANTIFIER, state->cp - 1);
1670 state->result = NewRENode(state, REOP_FLAT);
1673 state->result->u.flat.chr = c;
1674 state->result->u.flat.length = 1;
1675 state->result->kid = (void *) (state->cp - 1);
1676 state->progLength += 3;
1679 return ParseQuantifier(state);
1683 * Top-down regular expression grammar, based closely on Perl4.
1685 * regexp: altern A regular expression is one or more
1686 * altern '|' regexp alternatives separated by vertical bar.
1688 #define INITIAL_STACK_SIZE 128
1691 ParseRegExp(CompilerState *state)
1695 REOpData *operatorStack;
1696 RENode **operandStack;
1699 BOOL result = FALSE;
1701 INT operatorSP = 0, operatorStackSize = INITIAL_STACK_SIZE;
1702 INT operandSP = 0, operandStackSize = INITIAL_STACK_SIZE;
1704 /* Watch out for empty regexp */
1705 if (state->cp == state->cpend) {
1706 state->result = NewRENode(state, REOP_EMPTY);
1707 return (state->result != NULL);
1710 operatorStack = heap_alloc(sizeof(REOpData) * operatorStackSize);
1714 operandStack = heap_alloc(sizeof(RENode *) * operandStackSize);
1719 parenIndex = state->parenCount;
1720 if (state->cp == state->cpend) {
1722 * If we are at the end of the regexp and we're short one or more
1723 * operands, the regexp must have the form /x|/ or some such, with
1724 * left parentheses making us short more than one operand.
1726 if (operatorSP >= operandSP) {
1727 operand = NewRENode(state, REOP_EMPTY);
1733 switch (*state->cp) {
1736 if (state->cp + 1 < state->cpend &&
1737 *state->cp == '?' &&
1738 (state->cp[1] == '=' ||
1739 state->cp[1] == '!' ||
1740 state->cp[1] == ':')) {
1741 switch (state->cp[1]) {
1744 /* ASSERT, <next>, ... ASSERTTEST */
1745 state->progLength += 4;
1748 op = REOP_ASSERT_NOT;
1749 /* ASSERTNOT, <next>, ... ASSERTNOTTEST */
1750 state->progLength += 4;
1753 op = REOP_LPARENNON;
1759 /* LPAREN, <index>, ... RPAREN, <index> */
1761 += 2 * (1 + GetCompactIndexWidth(parenIndex));
1762 state->parenCount++;
1763 if (state->parenCount == 65535) {
1764 ReportRegExpError(state, JSREPORT_ERROR,
1765 JSMSG_TOO_MANY_PARENS);
1773 * If there's no stacked open parenthesis, throw syntax error.
1775 for (i = operatorSP - 1; ; i--) {
1777 ReportRegExpError(state, JSREPORT_ERROR,
1778 JSMSG_UNMATCHED_RIGHT_PAREN);
1781 if (operatorStack[i].op == REOP_ASSERT ||
1782 operatorStack[i].op == REOP_ASSERT_NOT ||
1783 operatorStack[i].op == REOP_LPARENNON ||
1784 operatorStack[i].op == REOP_LPAREN) {
1791 /* Expected an operand before these, so make an empty one */
1792 operand = NewRENode(state, REOP_EMPTY);
1798 if (!ParseTerm(state))
1800 operand = state->result;
1802 if (operandSP == operandStackSize) {
1804 operandStackSize += operandStackSize;
1805 tmp = heap_realloc(operandStack, sizeof(RENode *) * operandStackSize);
1810 operandStack[operandSP++] = operand;
1815 /* At the end; process remaining operators. */
1817 if (state->cp == state->cpend) {
1818 while (operatorSP) {
1820 if (!ProcessOp(state, &operatorStack[operatorSP],
1821 operandStack, operandSP))
1825 assert(operandSP == 1);
1826 state->result = operandStack[0];
1831 switch (*state->cp) {
1833 /* Process any stacked 'concat' operators */
1835 while (operatorSP &&
1836 operatorStack[operatorSP - 1].op == REOP_CONCAT) {
1838 if (!ProcessOp(state, &operatorStack[operatorSP],
1839 operandStack, operandSP)) {
1849 * If there's no stacked open parenthesis, throw syntax error.
1851 for (i = operatorSP - 1; ; i--) {
1853 ReportRegExpError(state, JSREPORT_ERROR,
1854 JSMSG_UNMATCHED_RIGHT_PAREN);
1857 if (operatorStack[i].op == REOP_ASSERT ||
1858 operatorStack[i].op == REOP_ASSERT_NOT ||
1859 operatorStack[i].op == REOP_LPARENNON ||
1860 operatorStack[i].op == REOP_LPAREN) {
1866 /* Process everything on the stack until the open parenthesis. */
1870 switch (operatorStack[operatorSP].op) {
1872 case REOP_ASSERT_NOT:
1874 operand = NewRENode(state, operatorStack[operatorSP].op);
1877 operand->u.parenIndex =
1878 operatorStack[operatorSP].parenIndex;
1880 operand->kid = operandStack[operandSP - 1];
1881 operandStack[operandSP - 1] = operand;
1882 if (state->treeDepth == TREE_DEPTH_MAX) {
1883 ReportRegExpError(state, JSREPORT_ERROR,
1884 JSMSG_REGEXP_TOO_COMPLEX);
1890 case REOP_LPARENNON:
1891 state->result = operandStack[operandSP - 1];
1892 if (!ParseQuantifier(state))
1894 operandStack[operandSP - 1] = state->result;
1895 goto restartOperator;
1897 if (!ProcessOp(state, &operatorStack[operatorSP],
1898 operandStack, operandSP))
1908 const WCHAR *errp = state->cp;
1910 if (ParseMinMaxQuantifier(state, TRUE) < 0) {
1912 * This didn't even scan correctly as a quantifier, so we should
1926 ReportRegExpErrorHelper(state, JSREPORT_ERROR, JSMSG_BAD_QUANTIFIER,
1932 /* Anything else is the start of the next term. */
1935 if (operatorSP == operatorStackSize) {
1937 operatorStackSize += operatorStackSize;
1938 tmp = heap_realloc(operatorStack, sizeof(REOpData) * operatorStackSize);
1941 operatorStack = tmp;
1943 operatorStack[operatorSP].op = op;
1944 operatorStack[operatorSP].errPos = state->cp;
1945 operatorStack[operatorSP++].parenIndex = parenIndex;
1950 heap_free(operatorStack);
1951 heap_free(operandStack);
1956 * Save the current state of the match - the position in the input
1957 * text as well as the position in the bytecode. The state of any
1958 * parent expressions is also saved (preceding state).
1959 * Contents of parenCount parentheses from parenIndex are also saved.
1961 static REBackTrackData *
1962 PushBackTrackState(REGlobalData *gData, REOp op,
1963 jsbytecode *target, REMatchState *x, const WCHAR *cp,
1964 size_t parenIndex, size_t parenCount)
1967 REBackTrackData *result =
1968 (REBackTrackData *) ((char *)gData->backTrackSP + gData->cursz);
1970 size_t sz = sizeof(REBackTrackData) +
1971 gData->stateStackTop * sizeof(REProgState) +
1972 parenCount * sizeof(RECapture);
1974 ptrdiff_t btsize = gData->backTrackStackSize;
1975 ptrdiff_t btincr = ((char *)result + sz) -
1976 ((char *)gData->backTrackStack + btsize);
1978 TRACE("\tBT_Push: %lu,%lu\n", (unsigned long) parenIndex, (unsigned long) parenCount);
1980 JS_COUNT_OPERATION(gData->cx, JSOW_JUMP * (1 + parenCount));
1982 ptrdiff_t offset = (char *)result - (char *)gData->backTrackStack;
1984 JS_COUNT_OPERATION(gData->cx, JSOW_ALLOCATION);
1985 btincr = ((btincr+btsize-1)/btsize)*btsize;
1986 gData->backTrackStack = jsheap_grow(gData->pool, gData->backTrackStack, btsize, btincr);
1987 if (!gData->backTrackStack) {
1988 js_ReportOutOfScriptQuota(gData->cx);
1992 gData->backTrackStackSize = btsize + btincr;
1993 result = (REBackTrackData *) ((char *)gData->backTrackStack + offset);
1995 gData->backTrackSP = result;
1996 result->sz = gData->cursz;
1999 result->backtrack_op = op;
2000 result->backtrack_pc = target;
2002 result->parenCount = parenCount;
2003 result->parenIndex = parenIndex;
2005 result->saveStateStackTop = gData->stateStackTop;
2006 assert(gData->stateStackTop);
2007 memcpy(result + 1, gData->stateStack,
2008 sizeof(REProgState) * result->saveStateStackTop);
2010 if (parenCount != 0) {
2011 memcpy((char *)(result + 1) +
2012 sizeof(REProgState) * result->saveStateStackTop,
2013 &x->parens[parenIndex],
2014 sizeof(RECapture) * parenCount);
2015 for (i = 0; i != parenCount; i++)
2016 x->parens[parenIndex + i].index = -1;
2022 static inline REMatchState *
2023 FlatNIMatcher(REGlobalData *gData, REMatchState *x, WCHAR *matchChars,
2027 assert(gData->cpend >= x->cp);
2028 if (length > (size_t)(gData->cpend - x->cp))
2030 for (i = 0; i != length; i++) {
2031 if (toupperW(matchChars[i]) != toupperW(x->cp[i]))
2039 * 1. Evaluate DecimalEscape to obtain an EscapeValue E.
2040 * 2. If E is not a character then go to step 6.
2041 * 3. Let ch be E's character.
2042 * 4. Let A be a one-element RECharSet containing the character ch.
2043 * 5. Call CharacterSetMatcher(A, false) and return its Matcher result.
2044 * 6. E must be an integer. Let n be that integer.
2045 * 7. If n=0 or n>NCapturingParens then throw a SyntaxError exception.
2046 * 8. Return an internal Matcher closure that takes two arguments, a State x
2047 * and a Continuation c, and performs the following:
2048 * 1. Let cap be x's captures internal array.
2049 * 2. Let s be cap[n].
2050 * 3. If s is undefined, then call c(x) and return its result.
2051 * 4. Let e be x's endIndex.
2052 * 5. Let len be s's length.
2053 * 6. Let f be e+len.
2054 * 7. If f>InputLength, return failure.
2055 * 8. If there exists an integer i between 0 (inclusive) and len (exclusive)
2056 * such that Canonicalize(s[i]) is not the same character as
2057 * Canonicalize(Input [e+i]), then return failure.
2058 * 9. Let y be the State (f, cap).
2059 * 10. Call c(y) and return its result.
2061 static REMatchState *
2062 BackrefMatcher(REGlobalData *gData, REMatchState *x, size_t parenIndex)
2065 const WCHAR *parenContent;
2066 RECapture *cap = &x->parens[parenIndex];
2068 if (cap->index == -1)
2072 if (x->cp + len > gData->cpend)
2075 parenContent = &gData->cpbegin[cap->index];
2076 if (gData->regexp->flags & JSREG_FOLD) {
2077 for (i = 0; i < len; i++) {
2078 if (toupperW(parenContent[i]) != toupperW(x->cp[i]))
2082 for (i = 0; i < len; i++) {
2083 if (parenContent[i] != x->cp[i])
2091 /* Add a single character to the RECharSet */
2093 AddCharacterToCharSet(RECharSet *cs, WCHAR c)
2095 UINT byteIndex = (UINT)(c >> 3);
2096 assert(c <= cs->length);
2097 cs->u.bits[byteIndex] |= 1 << (c & 0x7);
2101 /* Add a character range, c1 to c2 (inclusive) to the RECharSet */
2103 AddCharacterRangeToCharSet(RECharSet *cs, UINT c1, UINT c2)
2107 UINT byteIndex1 = c1 >> 3;
2108 UINT byteIndex2 = c2 >> 3;
2110 assert(c2 <= cs->length && c1 <= c2);
2115 if (byteIndex1 == byteIndex2) {
2116 cs->u.bits[byteIndex1] |= ((BYTE)0xFF >> (7 - (c2 - c1))) << c1;
2118 cs->u.bits[byteIndex1] |= 0xFF << c1;
2119 for (i = byteIndex1 + 1; i < byteIndex2; i++)
2120 cs->u.bits[i] = 0xFF;
2121 cs->u.bits[byteIndex2] |= (BYTE)0xFF >> (7 - c2);
2125 /* Compile the source of the class into a RECharSet */
2127 ProcessCharSet(REGlobalData *gData, RECharSet *charSet)
2129 const WCHAR *src, *end;
2130 BOOL inRange = FALSE;
2131 WCHAR rangeStart = 0;
2136 assert(!charSet->converted);
2138 * Assert that startIndex and length points to chars inside [] inside
2141 assert(1 <= charSet->u.src.startIndex);
2142 assert(charSet->u.src.startIndex
2143 < SysStringLen(gData->regexp->source));
2144 assert(charSet->u.src.length <= SysStringLen(gData->regexp->source)
2145 - 1 - charSet->u.src.startIndex);
2147 charSet->converted = TRUE;
2148 src = gData->regexp->source + charSet->u.src.startIndex;
2150 end = src + charSet->u.src.length;
2152 assert(src[-1] == '[' && end[0] == ']');
2154 byteLength = (charSet->length >> 3) + 1;
2155 charSet->u.bits = heap_alloc(byteLength);
2156 if (!charSet->u.bits) {
2157 JS_ReportOutOfMemory(gData->cx);
2161 memset(charSet->u.bits, 0, byteLength);
2167 assert(charSet->sense == FALSE);
2170 assert(charSet->sense == TRUE);
2173 while (src != end) {
2198 if (src < end && JS_ISWORD(*src)) {
2199 thisCh = (WCHAR)(*src++ & 0x1F);
2212 for (i = 0; (i < nDigits) && (src < end); i++) {
2215 if (!isASCIIHexDigit(c, &digit)) {
2217 * Back off to accepting the original '\'
2224 n = (n << 4) | digit;
2237 * This is a non-ECMA extension - decimal escapes (in this
2238 * case, octal!) are supposed to be an error inside class
2239 * ranges, but supported here for backwards compatibility.
2243 if ('0' <= c && c <= '7') {
2245 n = 8 * n + JS7_UNDEC(c);
2247 if ('0' <= c && c <= '7') {
2249 i = 8 * n + JS7_UNDEC(c);
2260 AddCharacterRangeToCharSet(charSet, '0', '9');
2261 continue; /* don't need range processing */
2263 AddCharacterRangeToCharSet(charSet, 0, '0' - 1);
2264 AddCharacterRangeToCharSet(charSet,
2266 (WCHAR)charSet->length);
2269 for (i = (INT)charSet->length; i >= 0; i--)
2271 AddCharacterToCharSet(charSet, (WCHAR)i);
2274 for (i = (INT)charSet->length; i >= 0; i--)
2276 AddCharacterToCharSet(charSet, (WCHAR)i);
2279 for (i = (INT)charSet->length; i >= 0; i--)
2281 AddCharacterToCharSet(charSet, (WCHAR)i);
2284 for (i = (INT)charSet->length; i >= 0; i--)
2286 AddCharacterToCharSet(charSet, (WCHAR)i);
2301 if (gData->regexp->flags & JSREG_FOLD) {
2304 assert(rangeStart <= thisCh);
2305 for (i = rangeStart; i <= thisCh; i++) {
2308 AddCharacterToCharSet(charSet, i);
2312 AddCharacterToCharSet(charSet, uch);
2314 AddCharacterToCharSet(charSet, dch);
2317 AddCharacterRangeToCharSet(charSet, rangeStart, thisCh);
2321 if (gData->regexp->flags & JSREG_FOLD) {
2322 AddCharacterToCharSet(charSet, toupperW(thisCh));
2323 AddCharacterToCharSet(charSet, tolowerW(thisCh));
2325 AddCharacterToCharSet(charSet, thisCh);
2327 if (src < end - 1) {
2331 rangeStart = thisCh;
2340 ReallocStateStack(REGlobalData *gData)
2342 size_t limit = gData->stateStackLimit;
2343 size_t sz = sizeof(REProgState) * limit;
2345 gData->stateStack = jsheap_grow(gData->pool, gData->stateStack, sz, sz);
2346 if (!gData->stateStack) {
2347 js_ReportOutOfScriptQuota(gData->cx);
2351 gData->stateStackLimit = limit + limit;
2355 #define PUSH_STATE_STACK(data) \
2357 ++(data)->stateStackTop; \
2358 if ((data)->stateStackTop == (data)->stateStackLimit && \
2359 !ReallocStateStack((data))) { \
2365 * Apply the current op against the given input to see if it's going to match
2366 * or fail. Return false if we don't get a match, true if we do. If updatecp is
2367 * true, then update the current state's cp. Always update startpc to the next
2370 static inline REMatchState *
2371 SimpleMatch(REGlobalData *gData, REMatchState *x, REOp op,
2372 jsbytecode **startpc, BOOL updatecp)
2374 REMatchState *result = NULL;
2377 size_t offset, length, index;
2378 jsbytecode *pc = *startpc; /* pc has already been incremented past op */
2380 const WCHAR *startcp = x->cp;
2384 const char *opname = reop_names[op];
2385 TRACE("\n%06d: %*s%s\n", pc - gData->regexp->program,
2386 (int)gData->stateStackTop * 2, "", opname);
2393 if (x->cp != gData->cpbegin) {
2394 if (/*!gData->cx->regExpStatics.multiline && FIXME !!! */
2395 !(gData->regexp->flags & JSREG_MULTILINE)) {
2398 if (!RE_IS_LINE_TERM(x->cp[-1]))
2404 if (x->cp != gData->cpend) {
2405 if (/*!gData->cx->regExpStatics.multiline &&*/
2406 !(gData->regexp->flags & JSREG_MULTILINE)) {
2409 if (!RE_IS_LINE_TERM(*x->cp))
2415 if ((x->cp == gData->cpbegin || !JS_ISWORD(x->cp[-1])) ^
2416 !(x->cp != gData->cpend && JS_ISWORD(*x->cp))) {
2421 if ((x->cp == gData->cpbegin || !JS_ISWORD(x->cp[-1])) ^
2422 (x->cp != gData->cpend && JS_ISWORD(*x->cp))) {
2427 if (x->cp != gData->cpend && !RE_IS_LINE_TERM(*x->cp)) {
2433 if (x->cp != gData->cpend && JS7_ISDEC(*x->cp)) {
2439 if (x->cp != gData->cpend && !JS7_ISDEC(*x->cp)) {
2445 if (x->cp != gData->cpend && JS_ISWORD(*x->cp)) {
2451 if (x->cp != gData->cpend && !JS_ISWORD(*x->cp)) {
2457 if (x->cp != gData->cpend && isspaceW(*x->cp)) {
2463 if (x->cp != gData->cpend && !isspaceW(*x->cp)) {
2469 pc = ReadCompactIndex(pc, &parenIndex);
2470 assert(parenIndex < gData->regexp->parenCount);
2471 result = BackrefMatcher(gData, x, parenIndex);
2474 pc = ReadCompactIndex(pc, &offset);
2475 assert(offset < SysStringLen(gData->regexp->source));
2476 pc = ReadCompactIndex(pc, &length);
2477 assert(1 <= length);
2478 assert(length <= SysStringLen(gData->regexp->source) - offset);
2479 if (length <= (size_t)(gData->cpend - x->cp)) {
2480 source = gData->regexp->source + offset;
2481 TRACE("%s\n", debugstr_wn(source, length));
2482 for (index = 0; index != length; index++) {
2483 if (source[index] != x->cp[index])
2492 TRACE(" '%c' == '%c'\n", (char)matchCh, (char)*x->cp);
2493 if (x->cp != gData->cpend && *x->cp == matchCh) {
2499 pc = ReadCompactIndex(pc, &offset);
2500 assert(offset < SysStringLen(gData->regexp->source));
2501 pc = ReadCompactIndex(pc, &length);
2502 assert(1 <= length);
2503 assert(length <= SysStringLen(gData->regexp->source) - offset);
2504 source = gData->regexp->source;
2505 result = FlatNIMatcher(gData, x, source + offset, length);
2509 if (x->cp != gData->cpend && toupperW(*x->cp) == toupperW(matchCh)) {
2515 matchCh = GET_ARG(pc);
2516 TRACE(" '%c' == '%c'\n", (char)matchCh, (char)*x->cp);
2518 if (x->cp != gData->cpend && *x->cp == matchCh) {
2524 matchCh = GET_ARG(pc);
2526 if (x->cp != gData->cpend && toupperW(*x->cp) == toupperW(matchCh)) {
2532 pc = ReadCompactIndex(pc, &index);
2533 assert(index < gData->regexp->classCount);
2534 if (x->cp != gData->cpend) {
2535 charSet = &gData->regexp->classList[index];
2536 assert(charSet->converted);
2539 if (charSet->length != 0 &&
2540 ch <= charSet->length &&
2541 (charSet->u.bits[index] & (1 << (ch & 0x7)))) {
2548 pc = ReadCompactIndex(pc, &index);
2549 assert(index < gData->regexp->classCount);
2550 if (x->cp != gData->cpend) {
2551 charSet = &gData->regexp->classList[index];
2552 assert(charSet->converted);
2555 if (charSet->length == 0 ||
2556 ch > charSet->length ||
2557 !(charSet->u.bits[index] & (1 << (ch & 0x7)))) {
2578 static inline REMatchState *
2579 ExecuteREBytecode(REGlobalData *gData, REMatchState *x)
2581 REMatchState *result = NULL;
2582 REBackTrackData *backTrackData;
2583 jsbytecode *nextpc, *testpc;
2586 REProgState *curState;
2587 const WCHAR *startcp;
2588 size_t parenIndex, k;
2589 size_t parenSoFar = 0;
2591 WCHAR matchCh1, matchCh2;
2595 jsbytecode *pc = gData->regexp->program;
2596 REOp op = (REOp) *pc++;
2599 * If the first node is a simple match, step the index into the string
2600 * until that match is made, or fail if it can't be found at all.
2602 if (REOP_IS_SIMPLE(op) && !(gData->regexp->flags & JSREG_STICKY)) {
2604 while (x->cp <= gData->cpend) {
2605 nextpc = pc; /* reset back to start each time */
2606 result = SimpleMatch(gData, x, op, &nextpc, TRUE);
2610 pc = nextpc; /* accept skip to next opcode */
2612 assert(op < REOP_LIMIT);
2623 const char *opname = reop_names[op];
2624 TRACE("\n%06d: %*s%s\n", pc - gData->regexp->program,
2625 (int)gData->stateStackTop * 2, "", opname);
2627 if (REOP_IS_SIMPLE(op)) {
2628 result = SimpleMatch(gData, x, op, &pc, TRUE);
2630 curState = &gData->stateStack[gData->stateStackTop];
2634 case REOP_ALTPREREQ2:
2635 nextpc = pc + GET_OFFSET(pc); /* start of next op */
2637 matchCh2 = GET_ARG(pc);
2642 if (x->cp != gData->cpend) {
2643 if (*x->cp == matchCh2)
2646 charSet = &gData->regexp->classList[k];
2647 if (!charSet->converted && !ProcessCharSet(gData, charSet))
2651 if ((charSet->length == 0 ||
2652 matchCh1 > charSet->length ||
2653 !(charSet->u.bits[k] & (1 << (matchCh1 & 0x7)))) ^
2661 case REOP_ALTPREREQ:
2662 nextpc = pc + GET_OFFSET(pc); /* start of next op */
2664 matchCh1 = GET_ARG(pc);
2666 matchCh2 = GET_ARG(pc);
2668 if (x->cp == gData->cpend ||
2669 (*x->cp != matchCh1 && *x->cp != matchCh2)) {
2673 /* else false thru... */
2677 nextpc = pc + GET_OFFSET(pc); /* start of next alternate */
2678 pc += ARG_LEN; /* start of this alternate */
2679 curState->parenSoFar = parenSoFar;
2680 PUSH_STATE_STACK(gData);
2683 if (REOP_IS_SIMPLE(op)) {
2684 if (!SimpleMatch(gData, x, op, &pc, TRUE)) {
2685 op = (REOp) *nextpc++;
2692 nextop = (REOp) *nextpc++;
2693 if (!PushBackTrackState(gData, nextop, nextpc, x, startcp, 0, 0))
2698 * Occurs at (successful) end of REOP_ALT,
2702 * If we have not gotten a result here, it is because of an
2703 * empty match. Do the same thing REOP_EMPTY would do.
2708 --gData->stateStackTop;
2709 pc += GET_OFFSET(pc);
2714 * Occurs at last (successful) end of REOP_ALT,
2718 * If we have not gotten a result here, it is because of an
2719 * empty match. Do the same thing REOP_EMPTY would do.
2724 --gData->stateStackTop;
2729 pc = ReadCompactIndex(pc, &parenIndex);
2730 TRACE("[ %lu ]\n", (unsigned long) parenIndex);
2731 assert(parenIndex < gData->regexp->parenCount);
2732 if (parenIndex + 1 > parenSoFar)
2733 parenSoFar = parenIndex + 1;
2734 x->parens[parenIndex].index = x->cp - gData->cpbegin;
2735 x->parens[parenIndex].length = 0;
2743 pc = ReadCompactIndex(pc, &parenIndex);
2744 assert(parenIndex < gData->regexp->parenCount);
2745 cap = &x->parens[parenIndex];
2746 delta = x->cp - (gData->cpbegin + cap->index);
2747 cap->length = (delta < 0) ? 0 : (size_t) delta;
2755 nextpc = pc + GET_OFFSET(pc); /* start of term after ASSERT */
2756 pc += ARG_LEN; /* start of ASSERT child */
2759 if (REOP_IS_SIMPLE(op) &&
2760 !SimpleMatch(gData, x, op, &testpc, FALSE)) {
2764 curState->u.assertion.top =
2765 (char *)gData->backTrackSP - (char *)gData->backTrackStack;
2766 curState->u.assertion.sz = gData->cursz;
2767 curState->index = x->cp - gData->cpbegin;
2768 curState->parenSoFar = parenSoFar;
2769 PUSH_STATE_STACK(gData);
2770 if (!PushBackTrackState(gData, REOP_ASSERTTEST,
2771 nextpc, x, x->cp, 0, 0)) {
2776 case REOP_ASSERT_NOT:
2777 nextpc = pc + GET_OFFSET(pc);
2781 if (REOP_IS_SIMPLE(op) /* Note - fail to fail! */ &&
2782 SimpleMatch(gData, x, op, &testpc, FALSE) &&
2783 *testpc == REOP_ASSERTNOTTEST) {
2787 curState->u.assertion.top
2788 = (char *)gData->backTrackSP -
2789 (char *)gData->backTrackStack;
2790 curState->u.assertion.sz = gData->cursz;
2791 curState->index = x->cp - gData->cpbegin;
2792 curState->parenSoFar = parenSoFar;
2793 PUSH_STATE_STACK(gData);
2794 if (!PushBackTrackState(gData, REOP_ASSERTNOTTEST,
2795 nextpc, x, x->cp, 0, 0)) {
2800 case REOP_ASSERTTEST:
2801 --gData->stateStackTop;
2803 x->cp = gData->cpbegin + curState->index;
2804 gData->backTrackSP =
2805 (REBackTrackData *) ((char *)gData->backTrackStack +
2806 curState->u.assertion.top);
2807 gData->cursz = curState->u.assertion.sz;
2812 case REOP_ASSERTNOTTEST:
2813 --gData->stateStackTop;
2815 x->cp = gData->cpbegin + curState->index;
2816 gData->backTrackSP =
2817 (REBackTrackData *) ((char *)gData->backTrackStack +
2818 curState->u.assertion.top);
2819 gData->cursz = curState->u.assertion.sz;
2820 result = (!result) ? x : NULL;
2823 curState->u.quantifier.min = 0;
2824 curState->u.quantifier.max = (UINT)-1;
2827 curState->u.quantifier.min = 1;
2828 curState->u.quantifier.max = (UINT)-1;
2831 curState->u.quantifier.min = 0;
2832 curState->u.quantifier.max = 1;
2835 pc = ReadCompactIndex(pc, &k);
2836 curState->u.quantifier.min = k;
2837 pc = ReadCompactIndex(pc, &k);
2838 /* max is k - 1 to use one byte for (UINT)-1 sentinel. */
2839 curState->u.quantifier.max = k - 1;
2840 assert(curState->u.quantifier.min <= curState->u.quantifier.max);
2842 if (curState->u.quantifier.max == 0) {
2843 pc = pc + GET_OFFSET(pc);
2848 /* Step over <next> */
2849 nextpc = pc + ARG_LEN;
2850 op = (REOp) *nextpc++;
2852 if (REOP_IS_SIMPLE(op)) {
2853 if (!SimpleMatch(gData, x, op, &nextpc, TRUE)) {
2854 if (curState->u.quantifier.min == 0)
2858 pc = pc + GET_OFFSET(pc);
2861 op = (REOp) *nextpc++;
2864 curState->index = startcp - gData->cpbegin;
2865 curState->continue_op = REOP_REPEAT;
2866 curState->continue_pc = pc;
2867 curState->parenSoFar = parenSoFar;
2868 PUSH_STATE_STACK(gData);
2869 if (curState->u.quantifier.min == 0 &&
2870 !PushBackTrackState(gData, REOP_REPEAT, pc, x, startcp,
2877 case REOP_ENDCHILD: /* marks the end of a quantifier child */
2878 pc = curState[-1].continue_pc;
2879 op = (REOp) curState[-1].continue_op;
2888 --gData->stateStackTop;
2890 /* Failed, see if we have enough children. */
2891 if (curState->u.quantifier.min == 0)
2895 if (curState->u.quantifier.min == 0 &&
2896 x->cp == gData->cpbegin + curState->index) {
2897 /* matched an empty string, that'll get us nowhere */
2901 if (curState->u.quantifier.min != 0)
2902 curState->u.quantifier.min--;
2903 if (curState->u.quantifier.max != (UINT) -1)
2904 curState->u.quantifier.max--;
2905 if (curState->u.quantifier.max == 0)
2907 nextpc = pc + ARG_LEN;
2908 nextop = (REOp) *nextpc;
2910 if (REOP_IS_SIMPLE(nextop)) {
2912 if (!SimpleMatch(gData, x, nextop, &nextpc, TRUE)) {
2913 if (curState->u.quantifier.min == 0)
2920 curState->index = startcp - gData->cpbegin;
2921 PUSH_STATE_STACK(gData);
2922 if (curState->u.quantifier.min == 0 &&
2923 !PushBackTrackState(gData, REOP_REPEAT,
2925 curState->parenSoFar,
2927 curState->parenSoFar)) {
2930 } while (*nextpc == REOP_ENDCHILD);
2933 parenSoFar = curState->parenSoFar;
2938 pc += GET_OFFSET(pc);
2941 case REOP_MINIMALSTAR:
2942 curState->u.quantifier.min = 0;
2943 curState->u.quantifier.max = (UINT)-1;
2944 goto minimalquantcommon;
2945 case REOP_MINIMALPLUS:
2946 curState->u.quantifier.min = 1;
2947 curState->u.quantifier.max = (UINT)-1;
2948 goto minimalquantcommon;
2949 case REOP_MINIMALOPT:
2950 curState->u.quantifier.min = 0;
2951 curState->u.quantifier.max = 1;
2952 goto minimalquantcommon;
2953 case REOP_MINIMALQUANT:
2954 pc = ReadCompactIndex(pc, &k);
2955 curState->u.quantifier.min = k;
2956 pc = ReadCompactIndex(pc, &k);
2957 /* See REOP_QUANT comments about k - 1. */
2958 curState->u.quantifier.max = k - 1;
2959 assert(curState->u.quantifier.min
2960 <= curState->u.quantifier.max);
2962 curState->index = x->cp - gData->cpbegin;
2963 curState->parenSoFar = parenSoFar;
2964 PUSH_STATE_STACK(gData);
2965 if (curState->u.quantifier.min != 0) {
2966 curState->continue_op = REOP_MINIMALREPEAT;
2967 curState->continue_pc = pc;
2968 /* step over <next> */
2972 if (!PushBackTrackState(gData, REOP_MINIMALREPEAT,
2973 pc, x, x->cp, 0, 0)) {
2976 --gData->stateStackTop;
2977 pc = pc + GET_OFFSET(pc);
2982 case REOP_MINIMALREPEAT:
2983 --gData->stateStackTop;
2986 TRACE("{%d,%d}\n", curState->u.quantifier.min, curState->u.quantifier.max);
2987 #define PREPARE_REPEAT() \
2989 curState->index = x->cp - gData->cpbegin; \
2990 curState->continue_op = REOP_MINIMALREPEAT; \
2991 curState->continue_pc = pc; \
2993 for (k = curState->parenSoFar; k < parenSoFar; k++) \
2994 x->parens[k].index = -1; \
2995 PUSH_STATE_STACK(gData); \
2996 op = (REOp) *pc++; \
2997 assert(op < REOP_LIMIT); \
3003 * Non-greedy failure - try to consume another child.
3005 if (curState->u.quantifier.max == (UINT) -1 ||
3006 curState->u.quantifier.max > 0) {
3010 /* Don't need to adjust pc since we're going to pop. */
3013 if (curState->u.quantifier.min == 0 &&
3014 x->cp == gData->cpbegin + curState->index) {
3015 /* Matched an empty string, that'll get us nowhere. */
3019 if (curState->u.quantifier.min != 0)
3020 curState->u.quantifier.min--;
3021 if (curState->u.quantifier.max != (UINT) -1)
3022 curState->u.quantifier.max--;
3023 if (curState->u.quantifier.min != 0) {
3027 curState->index = x->cp - gData->cpbegin;
3028 curState->parenSoFar = parenSoFar;
3029 PUSH_STATE_STACK(gData);
3030 if (!PushBackTrackState(gData, REOP_MINIMALREPEAT,
3032 curState->parenSoFar,
3033 parenSoFar - curState->parenSoFar)) {
3036 --gData->stateStackTop;
3037 pc = pc + GET_OFFSET(pc);
3039 assert(op < REOP_LIMIT);
3049 * If the match failed and there's a backtrack option, take it.
3050 * Otherwise this is a complete and utter failure.
3053 if (gData->cursz == 0)
3056 /* Potentially detect explosive regex here. */
3057 gData->backTrackCount++;
3058 if (gData->backTrackLimit &&
3059 gData->backTrackCount >= gData->backTrackLimit) {
3060 JS_ReportErrorNumber(gData->cx, js_GetErrorMessage, NULL,
3061 JSMSG_REGEXP_TOO_COMPLEX);
3066 backTrackData = gData->backTrackSP;
3067 gData->cursz = backTrackData->sz;
3068 gData->backTrackSP =
3069 (REBackTrackData *) ((char *)backTrackData - backTrackData->sz);
3070 x->cp = backTrackData->cp;
3071 pc = backTrackData->backtrack_pc;
3072 op = (REOp) backTrackData->backtrack_op;
3073 assert(op < REOP_LIMIT);
3074 gData->stateStackTop = backTrackData->saveStateStackTop;
3075 assert(gData->stateStackTop);
3077 memcpy(gData->stateStack, backTrackData + 1,
3078 sizeof(REProgState) * backTrackData->saveStateStackTop);
3079 curState = &gData->stateStack[gData->stateStackTop - 1];
3081 if (backTrackData->parenCount) {
3082 memcpy(&x->parens[backTrackData->parenIndex],
3083 (char *)(backTrackData + 1) +
3084 sizeof(REProgState) * backTrackData->saveStateStackTop,
3085 sizeof(RECapture) * backTrackData->parenCount);
3086 parenSoFar = backTrackData->parenIndex + backTrackData->parenCount;
3088 for (k = curState->parenSoFar; k < parenSoFar; k++)
3089 x->parens[k].index = -1;
3090 parenSoFar = curState->parenSoFar;
3093 TRACE("\tBT_Pop: %ld,%ld\n",
3094 (unsigned long) backTrackData->parenIndex,
3095 (unsigned long) backTrackData->parenCount);
3101 * Continue with the expression.
3104 assert(op < REOP_LIMIT);
3116 static REMatchState *MatchRegExp(REGlobalData *gData, REMatchState *x)
3118 REMatchState *result;
3119 const WCHAR *cp = x->cp;
3124 * Have to include the position beyond the last character
3125 * in order to detect end-of-input/line condition.
3127 for (cp2 = cp; cp2 <= gData->cpend; cp2++) {
3128 gData->skipped = cp2 - cp;
3130 for (j = 0; j < gData->regexp->parenCount; j++)
3131 x->parens[j].index = -1;
3132 result = ExecuteREBytecode(gData, x);
3133 if (!gData->ok || result || (gData->regexp->flags & JSREG_STICKY))
3135 gData->backTrackSP = gData->backTrackStack;
3137 gData->stateStackTop = 0;
3138 cp2 = cp + gData->skipped;
3143 #define MIN_BACKTRACK_LIMIT 400000
3145 static REMatchState *InitMatch(script_ctx_t *cx, REGlobalData *gData, JSRegExp *re, size_t length)
3147 REMatchState *result;
3150 gData->backTrackStackSize = INITIAL_BACKTRACK;
3151 gData->backTrackStack = jsheap_alloc(gData->pool, INITIAL_BACKTRACK);
3152 if (!gData->backTrackStack)
3155 gData->backTrackSP = gData->backTrackStack;
3157 gData->backTrackCount = 0;
3158 gData->backTrackLimit = 0;
3160 gData->stateStackLimit = INITIAL_STATESTACK;
3161 gData->stateStack = jsheap_alloc(gData->pool, sizeof(REProgState) * INITIAL_STATESTACK);
3162 if (!gData->stateStack)
3165 gData->stateStackTop = 0;
3170 result = jsheap_alloc(gData->pool, offsetof(REMatchState, parens) + re->parenCount * sizeof(RECapture));
3174 for (i = 0; i < re->classCount; i++) {
3175 if (!re->classList[i].converted &&
3176 !ProcessCharSet(gData, &re->classList[i])) {
3184 js_ReportOutOfScriptQuota(cx);
3190 js_DestroyRegExp(JSRegExp *re)
3192 if (re->classList) {
3194 for (i = 0; i < re->classCount; i++) {
3195 if (re->classList[i].converted)
3196 heap_free(re->classList[i].u.bits);
3197 re->classList[i].u.bits = NULL;
3199 heap_free(re->classList);
3205 js_NewRegExp(script_ctx_t *cx, BSTR str, UINT flags, BOOL flat)
3209 CompilerState state;
3216 mark = jsheap_mark(&cx->tmp_heap);
3217 len = SysStringLen(str);
3223 state.cpbegin = state.cp;
3224 state.cpend = state.cp + len;
3225 state.flags = flags;
3226 state.parenCount = 0;
3227 state.classCount = 0;
3228 state.progLength = 0;
3229 state.treeDepth = 0;
3230 state.classBitmapsMem = 0;
3231 for (i = 0; i < CLASS_CACHE_SIZE; i++)
3232 state.classCache[i].start = NULL;
3234 if (len != 0 && flat) {
3235 state.result = NewRENode(&state, REOP_FLAT);
3238 state.result->u.flat.chr = *state.cpbegin;
3239 state.result->u.flat.length = len;
3240 state.result->kid = (void *) state.cpbegin;
3241 /* Flat bytecode: REOP_FLAT compact(string_offset) compact(len). */
3242 state.progLength += 1 + GetCompactIndexWidth(0)
3243 + GetCompactIndexWidth(len);
3245 if (!ParseRegExp(&state))
3248 resize = offsetof(JSRegExp, program) + state.progLength + 1;
3249 re = heap_alloc(resize);
3253 assert(state.classBitmapsMem <= CLASS_BITMAPS_MEM_LIMIT);
3254 re->classCount = state.classCount;
3255 if (re->classCount) {
3256 re->classList = heap_alloc(re->classCount * sizeof(RECharSet));
3257 if (!re->classList) {
3258 js_DestroyRegExp(re);
3262 for (i = 0; i < re->classCount; i++)
3263 re->classList[i].converted = FALSE;
3265 re->classList = NULL;
3267 endPC = EmitREBytecode(&state, re, state.treeDepth, re->program, state.result);
3269 js_DestroyRegExp(re);
3273 *endPC++ = REOP_END;
3275 * Check whether size was overestimated and shrink using realloc.
3276 * This is safe since no pointers to newly parsed regexp or its parts
3277 * besides re exist here.
3279 if ((size_t)(endPC - re->program) != state.progLength + 1) {
3281 assert((size_t)(endPC - re->program) < state.progLength + 1);
3282 resize = offsetof(JSRegExp, program) + (endPC - re->program);
3283 tmp = heap_realloc(re, resize);
3289 re->parenCount = state.parenCount;
3297 static inline RegExpInstance *regexp_from_vdisp(vdisp_t *vdisp)
3299 return (RegExpInstance*)vdisp->u.jsdisp;
3302 static HRESULT do_regexp_match_next(script_ctx_t *ctx, RegExpInstance *regexp, const WCHAR *str, DWORD len,
3303 const WCHAR **cp, match_result_t **parens, DWORD *parens_size, DWORD *parens_cnt, match_result_t *ret)
3305 REMatchState *x, *result;
3309 gData.cpbegin = *cp;
3310 gData.cpend = str + len;
3311 gData.start = *cp-str;
3313 gData.pool = &ctx->tmp_heap;
3315 x = InitMatch(NULL, &gData, regexp->jsregexp, gData.cpend - gData.cpbegin);
3317 WARN("InitMatch failed\n");
3322 result = MatchRegExp(&gData, x);
3324 WARN("MatchRegExp failed\n");
3334 if(regexp->jsregexp->parenCount > *parens_size) {
3335 match_result_t *new_parens;
3338 new_parens = heap_realloc(*parens, sizeof(match_result_t)*regexp->jsregexp->parenCount);
3340 new_parens = heap_alloc(sizeof(match_result_t)*regexp->jsregexp->parenCount);
3342 return E_OUTOFMEMORY;
3344 *parens = new_parens;
3347 *parens_cnt = regexp->jsregexp->parenCount;
3349 for(i=0; i < regexp->jsregexp->parenCount; i++) {
3350 (*parens)[i].str = *cp + result->parens[i].index;
3351 (*parens)[i].len = result->parens[i].length;
3355 matchlen = (result->cp-*cp) - gData.skipped;
3357 ret->str = result->cp-matchlen;
3358 ret->len = matchlen;
3363 HRESULT regexp_match_next(script_ctx_t *ctx, DispatchEx *dispex, BOOL gcheck, const WCHAR *str, DWORD len,
3364 const WCHAR **cp, match_result_t **parens, DWORD *parens_size, DWORD *parens_cnt, match_result_t *ret)
3366 RegExpInstance *regexp = (RegExpInstance*)dispex;
3370 if(gcheck && !(regexp->jsregexp->flags & JSREG_GLOB))
3373 mark = jsheap_mark(&ctx->tmp_heap);
3375 hres = do_regexp_match_next(ctx, regexp, str, len, cp, parens, parens_size, parens_cnt, ret);
3381 HRESULT regexp_match(script_ctx_t *ctx, DispatchEx *dispex, const WCHAR *str, DWORD len, BOOL gflag,
3382 match_result_t **match_result, DWORD *result_cnt)
3384 RegExpInstance *This = (RegExpInstance*)dispex;
3385 match_result_t *ret = NULL, cres;
3386 const WCHAR *cp = str;
3387 DWORD i=0, ret_size = 0;
3391 mark = jsheap_mark(&ctx->tmp_heap);
3394 hres = do_regexp_match_next(ctx, This, str, len, &cp, NULL, NULL, NULL, &cres);
3395 if(hres == S_FALSE) {
3405 ret = heap_realloc(ret, (ret_size <<= 1) * sizeof(match_result_t));
3407 ret = heap_alloc((ret_size=4) * sizeof(match_result_t));
3409 hres = E_OUTOFMEMORY;
3416 if(!gflag && !(This->jsregexp->flags & JSREG_GLOB)) {
3428 *match_result = ret;
3433 static HRESULT RegExp_source(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
3434 VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
3439 case DISPATCH_PROPERTYGET: {
3440 RegExpInstance *This = regexp_from_vdisp(jsthis);
3442 V_VT(retv) = VT_BSTR;
3443 V_BSTR(retv) = SysAllocString(This->str);
3445 return E_OUTOFMEMORY;
3449 FIXME("Unimplemnted flags %x\n", flags);
3456 static HRESULT RegExp_global(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
3457 VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
3463 static HRESULT RegExp_ignoreCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
3464 VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
3470 static HRESULT RegExp_multiline(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
3471 VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
3477 static HRESULT RegExp_lastIndex(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
3478 VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
3483 case DISPATCH_PROPERTYGET: {
3484 RegExpInstance *regexp = regexp_from_vdisp(jsthis);
3486 V_I4(retv) = regexp->last_index;
3490 FIXME("unimplemented flags: %x\n", flags);
3497 static HRESULT RegExp_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
3498 VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
3504 static HRESULT create_match_array(script_ctx_t *ctx, BSTR input, const match_result_t *result,
3505 const match_result_t *parens, DWORD parens_cnt, jsexcept_t *ei, IDispatch **ret)
3510 HRESULT hres = S_OK;
3512 static const WCHAR indexW[] = {'i','n','d','e','x',0};
3513 static const WCHAR inputW[] = {'i','n','p','u','t',0};
3514 static const WCHAR zeroW[] = {'0',0};
3516 hres = create_array(ctx, parens_cnt+1, &array);
3520 for(i=0; i < parens_cnt; i++) {
3521 V_VT(&var) = VT_BSTR;
3522 V_BSTR(&var) = SysAllocStringLen(parens[i].str, parens[i].len);
3524 hres = E_OUTOFMEMORY;
3528 hres = jsdisp_propput_idx(array, i+1, &var, ei, NULL/*FIXME*/);
3529 SysFreeString(V_BSTR(&var));
3534 while(SUCCEEDED(hres)) {
3536 V_I4(&var) = result->str-input;
3537 hres = jsdisp_propput_name(array, indexW, &var, ei, NULL/*FIXME*/);
3541 V_VT(&var) = VT_BSTR;
3542 V_BSTR(&var) = input;
3543 hres = jsdisp_propput_name(array, inputW, &var, ei, NULL/*FIXME*/);
3547 V_BSTR(&var) = SysAllocStringLen(result->str, result->len);
3549 hres = E_OUTOFMEMORY;
3552 hres = jsdisp_propput_name(array, zeroW, &var, ei, NULL/*FIXME*/);
3553 SysFreeString(V_BSTR(&var));
3558 jsdisp_release(array);
3562 *ret = (IDispatch*)_IDispatchEx_(array);
3566 static HRESULT run_exec(script_ctx_t *ctx, vdisp_t *jsthis, VARIANT *arg, jsexcept_t *ei, BSTR *input,
3567 match_result_t *match, match_result_t **parens, DWORD *parens_cnt, VARIANT_BOOL *ret)
3569 RegExpInstance *regexp;
3570 DWORD parens_size = 0, last_index = 0, length;
3575 if(!is_vclass(jsthis, JSCLASS_REGEXP)) {
3576 FIXME("Not a RegExp\n");
3580 regexp = regexp_from_vdisp(jsthis);
3583 hres = to_string(ctx, arg, ei, &string);
3587 string = SysAllocStringLen(NULL, 0);
3589 return E_OUTOFMEMORY;
3592 length = SysStringLen(string);
3593 if(regexp->jsregexp->flags & JSREG_GLOB)
3594 last_index = regexp->last_index;
3596 cp = string + last_index;
3597 hres = regexp_match_next(ctx, ®exp->dispex, FALSE, string, length, &cp, parens, parens ? &parens_size : NULL,
3600 SysFreeString(string);
3605 regexp->last_index = cp-string;
3606 *ret = VARIANT_TRUE;
3608 regexp->last_index = 0;
3609 *ret = VARIANT_FALSE;
3617 static HRESULT RegExp_exec(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
3618 VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
3620 match_result_t *parens = NULL, match;
3621 DWORD parens_cnt = 0;
3628 hres = run_exec(ctx, jsthis, arg_cnt(dp) ? get_arg(dp,0) : NULL, ei, &string, &match, &parens, &parens_cnt, &b);
3636 hres = create_match_array(ctx, string, &match, parens, parens_cnt, ei, &ret);
3637 if(SUCCEEDED(hres)) {
3638 V_VT(retv) = VT_DISPATCH;
3639 V_DISPATCH(retv) = ret;
3642 V_VT(retv) = VT_NULL;
3647 SysFreeString(string);
3651 static HRESULT RegExp_test(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
3652 VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
3654 match_result_t match;
3660 hres = run_exec(ctx, jsthis, arg_cnt(dp) ? get_arg(dp,0) : NULL, ei, NULL, &match, NULL, NULL, &b);
3665 V_VT(retv) = VT_BOOL;
3671 static HRESULT RegExp_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
3672 VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
3678 return throw_type_error(ctx, ei, IDS_NOT_FUNC, NULL);
3680 FIXME("unimplemented flags %x\n", flags);
3687 static void RegExp_destructor(DispatchEx *dispex)
3689 RegExpInstance *This = (RegExpInstance*)dispex;
3692 js_DestroyRegExp(This->jsregexp);
3693 SysFreeString(This->str);
3697 static const builtin_prop_t RegExp_props[] = {
3698 {execW, RegExp_exec, PROPF_METHOD|1},
3699 {globalW, RegExp_global, 0},
3700 {ignoreCaseW, RegExp_ignoreCase, 0},
3701 {lastIndexW, RegExp_lastIndex, 0},
3702 {multilineW, RegExp_multiline, 0},
3703 {sourceW, RegExp_source, 0},
3704 {testW, RegExp_test, PROPF_METHOD|1},
3705 {toStringW, RegExp_toString, PROPF_METHOD}
3708 static const builtin_info_t RegExp_info = {
3710 {NULL, RegExp_value, 0},
3711 sizeof(RegExp_props)/sizeof(*RegExp_props),
3717 static HRESULT alloc_regexp(script_ctx_t *ctx, DispatchEx *object_prototype, RegExpInstance **ret)
3719 RegExpInstance *regexp;
3722 regexp = heap_alloc_zero(sizeof(RegExpInstance));
3724 return E_OUTOFMEMORY;
3726 if(object_prototype)
3727 hres = init_dispex(®exp->dispex, ctx, &RegExp_info, object_prototype);
3729 hres = init_dispex_from_constr(®exp->dispex, ctx, &RegExp_info, ctx->regexp_constr);
3740 HRESULT create_regexp(script_ctx_t *ctx, const WCHAR *exp, int len, DWORD flags, DispatchEx **ret)
3742 RegExpInstance *regexp;
3745 TRACE("%s %x\n", debugstr_w(exp), flags);
3747 hres = alloc_regexp(ctx, NULL, ®exp);
3752 regexp->str = SysAllocString(exp);
3754 regexp->str = SysAllocStringLen(exp, len);
3756 jsdisp_release(®exp->dispex);
3757 return E_OUTOFMEMORY;
3760 regexp->jsregexp = js_NewRegExp(ctx, regexp->str, flags, FALSE);
3761 if(!regexp->jsregexp) {
3762 WARN("js_NewRegExp failed\n");
3763 jsdisp_release(®exp->dispex);
3767 *ret = ®exp->dispex;
3771 static HRESULT regexp_constructor(script_ctx_t *ctx, DISPPARAMS *dp, VARIANT *retv)
3773 const WCHAR *opt = emptyW, *src;
3784 arg = get_arg(dp,0);
3785 if(V_VT(arg) == VT_DISPATCH) {
3788 obj = iface_to_jsdisp((IUnknown*)V_DISPATCH(arg));
3790 if(is_class(obj, JSCLASS_REGEXP)) {
3791 RegExpInstance *regexp = (RegExpInstance*)obj;
3793 hres = create_regexp(ctx, regexp->str, -1, regexp->jsregexp->flags, &ret);
3794 jsdisp_release(obj);
3798 V_VT(retv) = VT_DISPATCH;
3799 V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(ret);
3803 jsdisp_release(obj);
3807 if(V_VT(arg) != VT_BSTR) {
3808 FIXME("vt arg0 = %d\n", V_VT(arg));
3814 if(arg_cnt(dp) >= 2) {
3815 arg = get_arg(dp,1);
3816 if(V_VT(arg) != VT_BSTR) {
3817 FIXME("unimplemented for vt %d\n", V_VT(arg));
3824 hres = parse_regexp_flags(opt, strlenW(opt), &flags);
3828 hres = create_regexp(ctx, src, -1, flags, &ret);
3833 V_VT(retv) = VT_DISPATCH;
3834 V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(ret);
3836 jsdisp_release(ret);
3841 static HRESULT RegExpConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
3842 VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
3847 case DISPATCH_METHOD:
3849 VARIANT *arg = get_arg(dp,0);
3850 if(V_VT(arg) == VT_DISPATCH) {
3851 DispatchEx *jsdisp = iface_to_jsdisp((IUnknown*)V_DISPATCH(arg));
3853 if(is_class(jsdisp, JSCLASS_REGEXP)) {
3854 if(arg_cnt(dp) > 1 && V_VT(get_arg(dp,1)) != VT_EMPTY) {
3855 jsdisp_release(jsdisp);
3856 return throw_regexp_error(ctx, ei, IDS_REGEXP_SYNTAX_ERROR, NULL);
3860 V_VT(retv) = VT_DISPATCH;
3861 V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(jsdisp);
3863 jsdisp_release(jsdisp);
3867 jsdisp_release(jsdisp);
3872 case DISPATCH_CONSTRUCT:
3873 return regexp_constructor(ctx, dp, retv);
3875 FIXME("unimplemented flags: %x\n", flags);
3882 HRESULT create_regexp_constr(script_ctx_t *ctx, DispatchEx *object_prototype, DispatchEx **ret)
3884 RegExpInstance *regexp;
3887 static const WCHAR RegExpW[] = {'R','e','g','E','x','p',0};
3889 hres = alloc_regexp(ctx, object_prototype, ®exp);
3893 hres = create_builtin_function(ctx, RegExpConstr_value, RegExpW, NULL,
3894 PROPF_CONSTR|2, ®exp->dispex, ret);
3896 jsdisp_release(®exp->dispex);
3900 HRESULT parse_regexp_flags(const WCHAR *str, DWORD str_len, DWORD *ret)
3905 for (p = str; p < str+str_len; p++) {
3908 flags |= JSREG_GLOB;
3911 flags |= JSREG_FOLD;
3914 flags |= JSREG_MULTILINE;
3917 flags |= JSREG_STICKY;
3920 WARN("wrong flag %c\n", *p);