ole32: Only fail on missing Implemented Categories key if we actually have categories...
[wine] / dlls / jscript / regexp.c
1 /*
2  * Copyright 2008 Jacek Caban for CodeWeavers
3  *
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.
8  *
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.
13  *
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
17  */
18
19 /*
20  * Code in this file is based on files:
21  * js/src/jsregexp.h
22  * js/src/jsregexp.c
23  * from Mozilla project, released under LGPL 2.1 or later.
24  *
25  * The Original Code is Mozilla Communicator client code, released
26  * March 31, 1998.
27  *
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.
32  */
33
34 #include <assert.h>
35 #include <math.h>
36
37 #include "jscript.h"
38
39 #include "wine/debug.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
42
43 #define JSREG_FOLD      0x01    /* fold uppercase to lowercase */
44 #define JSREG_GLOB      0x02    /* global exec, creates array of matches */
45 #define JSREG_MULTILINE 0x04    /* treat ^ and $ as begin and end of line */
46 #define JSREG_STICKY    0x08    /* only match starting at lastIndex */
47
48 typedef BYTE JSPackedBool;
49 typedef BYTE jsbytecode;
50
51 /*
52  * This struct holds a bitmap representation of a class from a regexp.
53  * There's a list of these referenced by the classList field in the JSRegExp
54  * struct below. The initial state has startIndex set to the offset in the
55  * original regexp source of the beginning of the class contents. The first
56  * use of the class converts the source representation into a bitmap.
57  *
58  */
59 typedef struct RECharSet {
60     JSPackedBool    converted;
61     JSPackedBool    sense;
62     WORD            length;
63     union {
64         BYTE        *bits;
65         struct {
66             size_t  startIndex;
67             size_t  length;
68         } src;
69     } u;
70 } RECharSet;
71
72 typedef struct {
73     WORD         flags;         /* flags, see jsapi.h's JSREG_* defines */
74     size_t       parenCount;    /* number of parenthesized submatches */
75     size_t       classCount;    /* count [...] bitmaps */
76     RECharSet    *classList;    /* list of [...] bitmaps */
77     BSTR         source;        /* locked source string, sans // */
78     jsbytecode   program[1];    /* regular expression bytecode */
79 } JSRegExp;
80
81 typedef struct {
82     DispatchEx dispex;
83
84     JSRegExp *jsregexp;
85     BSTR str;
86     INT last_index;
87     VARIANT last_index_var;
88 } RegExpInstance;
89
90 static const WCHAR sourceW[] = {'s','o','u','r','c','e',0};
91 static const WCHAR globalW[] = {'g','l','o','b','a','l',0};
92 static const WCHAR ignoreCaseW[] = {'i','g','n','o','r','e','C','a','s','e',0};
93 static const WCHAR multilineW[] = {'m','u','l','t','i','l','i','n','e',0};
94 static const WCHAR lastIndexW[] = {'l','a','s','t','I','n','d','e','x',0};
95 static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0};
96 static const WCHAR execW[] = {'e','x','e','c',0};
97 static const WCHAR testW[] = {'t','e','s','t',0};
98
99 static const WCHAR emptyW[] = {0};
100
101 /* FIXME: Better error handling */
102 #define ReportRegExpError(a,b,c)
103 #define ReportRegExpErrorHelper(a,b,c,d)
104 #define JS_ReportErrorNumber(a,b,c,d)
105 #define JS_ReportErrorFlagsAndNumber(a,b,c,d,e,f)
106 #define js_ReportOutOfScriptQuota(a)
107 #define JS_ReportOutOfMemory(a)
108 #define JS_COUNT_OPERATION(a,b)
109
110 #define JSMSG_MIN_TOO_BIG 47
111 #define JSMSG_MAX_TOO_BIG 48
112 #define JSMSG_OUT_OF_ORDER 49
113 #define JSMSG_OUT_OF_MEMORY 137
114
115 #define LINE_SEPARATOR  0x2028
116 #define PARA_SEPARATOR  0x2029
117
118 #define RE_IS_LETTER(c)     (((c >= 'A') && (c <= 'Z')) ||                    \
119                              ((c >= 'a') && (c <= 'z')) )
120 #define RE_IS_LINE_TERM(c)  ((c == '\n') || (c == '\r') ||                    \
121                              (c == LINE_SEPARATOR) || (c == PARA_SEPARATOR))
122
123 #define JS_ISWORD(c)    ((c) < 128 && (isalnum(c) || (c) == '_'))
124
125 #define JS7_ISDEC(c)    ((((unsigned)(c)) - '0') <= 9)
126 #define JS7_UNDEC(c)    ((c) - '0')
127
128 typedef enum REOp {
129     REOP_EMPTY,
130     REOP_BOL,
131     REOP_EOL,
132     REOP_WBDRY,
133     REOP_WNONBDRY,
134     REOP_DOT,
135     REOP_DIGIT,
136     REOP_NONDIGIT,
137     REOP_ALNUM,
138     REOP_NONALNUM,
139     REOP_SPACE,
140     REOP_NONSPACE,
141     REOP_BACKREF,
142     REOP_FLAT,
143     REOP_FLAT1,
144     REOP_FLATi,
145     REOP_FLAT1i,
146     REOP_UCFLAT1,
147     REOP_UCFLAT1i,
148     REOP_UCFLAT,
149     REOP_UCFLATi,
150     REOP_CLASS,
151     REOP_NCLASS,
152     REOP_ALT,
153     REOP_QUANT,
154     REOP_STAR,
155     REOP_PLUS,
156     REOP_OPT,
157     REOP_LPAREN,
158     REOP_RPAREN,
159     REOP_JUMP,
160     REOP_DOTSTAR,
161     REOP_LPARENNON,
162     REOP_ASSERT,
163     REOP_ASSERT_NOT,
164     REOP_ASSERTTEST,
165     REOP_ASSERTNOTTEST,
166     REOP_MINIMALSTAR,
167     REOP_MINIMALPLUS,
168     REOP_MINIMALOPT,
169     REOP_MINIMALQUANT,
170     REOP_ENDCHILD,
171     REOP_REPEAT,
172     REOP_MINIMALREPEAT,
173     REOP_ALTPREREQ,
174     REOP_ALTPREREQ2,
175     REOP_ENDALT,
176     REOP_CONCAT,
177     REOP_END,
178     REOP_LIMIT /* META: no operator >= to this */
179 } REOp;
180
181 #define REOP_IS_SIMPLE(op)  ((op) <= REOP_NCLASS)
182
183 static const char *reop_names[] = {
184     "empty",
185     "bol",
186     "eol",
187     "wbdry",
188     "wnonbdry",
189     "dot",
190     "digit",
191     "nondigit",
192     "alnum",
193     "nonalnum",
194     "space",
195     "nonspace",
196     "backref",
197     "flat",
198     "flat1",
199     "flati",
200     "flat1i",
201     "ucflat1",
202     "ucflat1i",
203     "ucflat",
204     "ucflati",
205     "class",
206     "nclass",
207     "alt",
208     "quant",
209     "star",
210     "plus",
211     "opt",
212     "lparen",
213     "rparen",
214     "jump",
215     "dotstar",
216     "lparennon",
217     "assert",
218     "assert_not",
219     "asserttest",
220     "assertnottest",
221     "minimalstar",
222     "minimalplus",
223     "minimalopt",
224     "minimalquant",
225     "endchild",
226     "repeat",
227     "minimalrepeat",
228     "altprereq",
229     "alrprereq2",
230     "endalt",
231     "concat",
232     "end",
233     NULL
234 };
235
236 typedef struct RECapture {
237     ptrdiff_t index;           /* start of contents, -1 for empty  */
238     size_t length;             /* length of capture */
239 } RECapture;
240
241 typedef struct REMatchState {
242     const WCHAR *cp;
243     RECapture parens[1];      /* first of 're->parenCount' captures,
244                                  allocated at end of this struct */
245 } REMatchState;
246
247 typedef struct REProgState {
248     jsbytecode *continue_pc;        /* current continuation data */
249     jsbytecode continue_op;
250     ptrdiff_t index;                /* progress in text */
251     size_t parenSoFar;              /* highest indexed paren started */
252     union {
253         struct {
254             UINT min;               /* current quantifier limits */
255             UINT max;
256         } quantifier;
257         struct {
258             size_t top;             /* backtrack stack state */
259             size_t sz;
260         } assertion;
261     } u;
262 } REProgState;
263
264 typedef struct REBackTrackData {
265     size_t sz;                      /* size of previous stack entry */
266     jsbytecode *backtrack_pc;       /* where to backtrack to */
267     jsbytecode backtrack_op;
268     const WCHAR *cp;                /* index in text of match at backtrack */
269     size_t parenIndex;              /* start index of saved paren contents */
270     size_t parenCount;              /* # of saved paren contents */
271     size_t saveStateStackTop;       /* number of parent states */
272     /* saved parent states follow */
273     /* saved paren contents follow */
274 } REBackTrackData;
275
276 #define INITIAL_STATESTACK  100
277 #define INITIAL_BACKTRACK   8000
278
279 typedef struct REGlobalData {
280     script_ctx_t *cx;
281     JSRegExp *regexp;               /* the RE in execution */
282     BOOL ok;                        /* runtime error (out_of_memory only?) */
283     size_t start;                   /* offset to start at */
284     ptrdiff_t skipped;              /* chars skipped anchoring this r.e. */
285     const WCHAR    *cpbegin;        /* text base address */
286     const WCHAR    *cpend;          /* text limit address */
287
288     REProgState *stateStack;        /* stack of state of current parents */
289     size_t stateStackTop;
290     size_t stateStackLimit;
291
292     REBackTrackData *backTrackStack;/* stack of matched-so-far positions */
293     REBackTrackData *backTrackSP;
294     size_t backTrackStackSize;
295     size_t cursz;                   /* size of current stack entry */
296     size_t backTrackCount;          /* how many times we've backtracked */
297     size_t backTrackLimit;          /* upper limit on backtrack states */
298
299     jsheap_t *pool;                 /* It's faster to use one malloc'd pool
300                                        than to malloc/free the three items
301                                        that are allocated from this pool */
302 } REGlobalData;
303
304 typedef struct RENode RENode;
305 struct RENode {
306     REOp            op;         /* r.e. op bytecode */
307     RENode          *next;      /* next in concatenation order */
308     void            *kid;       /* first operand */
309     union {
310         void        *kid2;      /* second operand */
311         INT         num;        /* could be a number */
312         size_t      parenIndex; /* or a parenthesis index */
313         struct {                /* or a quantifier range */
314             UINT  min;
315             UINT  max;
316             JSPackedBool greedy;
317         } range;
318         struct {                /* or a character class */
319             size_t  startIndex;
320             size_t  kidlen;     /* length of string at kid, in jschars */
321             size_t  index;      /* index into class list */
322             WORD  bmsize;       /* bitmap size, based on max char code */
323             JSPackedBool sense;
324         } ucclass;
325         struct {                /* or a literal sequence */
326             WCHAR   chr;        /* of one character */
327             size_t  length;     /* or many (via the kid) */
328         } flat;
329         struct {
330             RENode  *kid2;      /* second operand from ALT */
331             WCHAR   ch1;        /* match char for ALTPREREQ */
332             WCHAR   ch2;        /* ditto, or class index for ALTPREREQ2 */
333         } altprereq;
334     } u;
335 };
336
337 #define CLASS_CACHE_SIZE    4
338
339 typedef struct CompilerState {
340     script_ctx_t    *context;
341     const WCHAR     *cpbegin;
342     const WCHAR     *cpend;
343     const WCHAR     *cp;
344     size_t          parenCount;
345     size_t          classCount;   /* number of [] encountered */
346     size_t          treeDepth;    /* maximum depth of parse tree */
347     size_t          progLength;   /* estimated bytecode length */
348     RENode          *result;
349     size_t          classBitmapsMem; /* memory to hold all class bitmaps */
350     struct {
351         const WCHAR *start;         /* small cache of class strings */
352         size_t length;              /* since they're often the same */
353         size_t index;
354     } classCache[CLASS_CACHE_SIZE];
355     WORD          flags;
356 } CompilerState;
357
358 typedef struct EmitStateStackEntry {
359     jsbytecode      *altHead;       /* start of REOP_ALT* opcode */
360     jsbytecode      *nextAltFixup;  /* fixup pointer to next-alt offset */
361     jsbytecode      *nextTermFixup; /* fixup ptr. to REOP_JUMP offset */
362     jsbytecode      *endTermFixup;  /* fixup ptr. to REOPT_ALTPREREQ* offset */
363     RENode          *continueNode;  /* original REOP_ALT* node being stacked */
364     jsbytecode      continueOp;     /* REOP_JUMP or REOP_ENDALT continuation */
365     JSPackedBool    jumpToJumpFlag; /* true if we've patched jump-to-jump to
366                                        avoid 16-bit unsigned offset overflow */
367 } EmitStateStackEntry;
368
369 /*
370  * Immediate operand sizes and getter/setters.  Unlike the ones in jsopcode.h,
371  * the getters and setters take the pc of the offset, not of the opcode before
372  * the offset.
373  */
374 #define ARG_LEN             2
375 #define GET_ARG(pc)         ((WORD)(((pc)[0] << 8) | (pc)[1]))
376 #define SET_ARG(pc, arg)    ((pc)[0] = (jsbytecode) ((arg) >> 8),       \
377                              (pc)[1] = (jsbytecode) (arg))
378
379 #define OFFSET_LEN          ARG_LEN
380 #define OFFSET_MAX          ((1 << (ARG_LEN * 8)) - 1)
381 #define GET_OFFSET(pc)      GET_ARG(pc)
382
383 static BOOL ParseRegExp(CompilerState*);
384
385 /*
386  * Maximum supported tree depth is maximum size of EmitStateStackEntry stack.
387  * For sanity, we limit it to 2^24 bytes.
388  */
389 #define TREE_DEPTH_MAX  ((1 << 24) / sizeof(EmitStateStackEntry))
390
391 /*
392  * The maximum memory that can be allocated for class bitmaps.
393  * For sanity, we limit it to 2^24 bytes.
394  */
395 #define CLASS_BITMAPS_MEM_LIMIT (1 << 24)
396
397 /*
398  * Functions to get size and write/read bytecode that represent small indexes
399  * compactly.
400  * Each byte in the code represent 7-bit chunk of the index. 8th bit when set
401  * indicates that the following byte brings more bits to the index. Otherwise
402  * this is the last byte in the index bytecode representing highest index bits.
403  */
404 static size_t
405 GetCompactIndexWidth(size_t index)
406 {
407     size_t width;
408
409     for (width = 1; (index >>= 7) != 0; ++width) { }
410     return width;
411 }
412
413 static inline jsbytecode *
414 WriteCompactIndex(jsbytecode *pc, size_t index)
415 {
416     size_t next;
417
418     while ((next = index >> 7) != 0) {
419         *pc++ = (jsbytecode)(index | 0x80);
420         index = next;
421     }
422     *pc++ = (jsbytecode)index;
423     return pc;
424 }
425
426 static inline jsbytecode *
427 ReadCompactIndex(jsbytecode *pc, size_t *result)
428 {
429     size_t nextByte;
430
431     nextByte = *pc++;
432     if ((nextByte & 0x80) == 0) {
433         /*
434          * Short-circuit the most common case when compact index <= 127.
435          */
436         *result = nextByte;
437     } else {
438         size_t shift = 7;
439         *result = 0x7F & nextByte;
440         do {
441             nextByte = *pc++;
442             *result |= (nextByte & 0x7F) << shift;
443             shift += 7;
444         } while ((nextByte & 0x80) != 0);
445     }
446     return pc;
447 }
448
449 /* Construct and initialize an RENode, returning NULL for out-of-memory */
450 static RENode *
451 NewRENode(CompilerState *state, REOp op)
452 {
453     RENode *ren;
454
455     ren = jsheap_alloc(&state->context->tmp_heap, sizeof(*ren));
456     if (!ren) {
457         /* js_ReportOutOfScriptQuota(cx); */
458         return NULL;
459     }
460     ren->op = op;
461     ren->next = NULL;
462     ren->kid = NULL;
463     return ren;
464 }
465
466 /*
467  * Validates and converts hex ascii value.
468  */
469 static BOOL
470 isASCIIHexDigit(WCHAR c, UINT *digit)
471 {
472     UINT cv = c;
473
474     if (cv < '0')
475         return FALSE;
476     if (cv <= '9') {
477         *digit = cv - '0';
478         return TRUE;
479     }
480     cv |= 0x20;
481     if (cv >= 'a' && cv <= 'f') {
482         *digit = cv - 'a' + 10;
483         return TRUE;
484     }
485     return FALSE;
486 }
487
488 typedef struct {
489     REOp op;
490     const WCHAR *errPos;
491     size_t parenIndex;
492 } REOpData;
493
494 #define JUMP_OFFSET_HI(off)     ((jsbytecode)((off) >> 8))
495 #define JUMP_OFFSET_LO(off)     ((jsbytecode)(off))
496
497 static BOOL
498 SetForwardJumpOffset(jsbytecode *jump, jsbytecode *target)
499 {
500     ptrdiff_t offset = target - jump;
501
502     /* Check that target really points forward. */
503     assert(offset >= 2);
504     if ((size_t)offset > OFFSET_MAX)
505         return FALSE;
506
507     jump[0] = JUMP_OFFSET_HI(offset);
508     jump[1] = JUMP_OFFSET_LO(offset);
509     return TRUE;
510 }
511
512 /*
513  * Generate bytecode for the tree rooted at t using an explicit stack instead
514  * of recursion.
515  */
516 static jsbytecode *
517 EmitREBytecode(CompilerState *state, JSRegExp *re, size_t treeDepth,
518                jsbytecode *pc, RENode *t)
519 {
520     EmitStateStackEntry *emitStateSP, *emitStateStack;
521     RECharSet *charSet;
522     REOp op;
523
524     if (treeDepth == 0) {
525         emitStateStack = NULL;
526     } else {
527         emitStateStack = heap_alloc(sizeof(EmitStateStackEntry) * treeDepth);
528         if (!emitStateStack)
529             return NULL;
530     }
531     emitStateSP = emitStateStack;
532     op = t->op;
533     assert(op < REOP_LIMIT);
534
535     for (;;) {
536         *pc++ = op;
537         switch (op) {
538           case REOP_EMPTY:
539             --pc;
540             break;
541
542           case REOP_ALTPREREQ2:
543           case REOP_ALTPREREQ:
544             assert(emitStateSP);
545             emitStateSP->altHead = pc - 1;
546             emitStateSP->endTermFixup = pc;
547             pc += OFFSET_LEN;
548             SET_ARG(pc, t->u.altprereq.ch1);
549             pc += ARG_LEN;
550             SET_ARG(pc, t->u.altprereq.ch2);
551             pc += ARG_LEN;
552
553             emitStateSP->nextAltFixup = pc;    /* offset to next alternate */
554             pc += OFFSET_LEN;
555
556             emitStateSP->continueNode = t;
557             emitStateSP->continueOp = REOP_JUMP;
558             emitStateSP->jumpToJumpFlag = FALSE;
559             ++emitStateSP;
560             assert((size_t)(emitStateSP - emitStateStack) <= treeDepth);
561             t = t->kid;
562             op = t->op;
563             assert(op < REOP_LIMIT);
564             continue;
565
566           case REOP_JUMP:
567             emitStateSP->nextTermFixup = pc;    /* offset to following term */
568             pc += OFFSET_LEN;
569             if (!SetForwardJumpOffset(emitStateSP->nextAltFixup, pc))
570                 goto jump_too_big;
571             emitStateSP->continueOp = REOP_ENDALT;
572             ++emitStateSP;
573             assert((size_t)(emitStateSP - emitStateStack) <= treeDepth);
574             t = t->u.kid2;
575             op = t->op;
576             assert(op < REOP_LIMIT);
577             continue;
578
579           case REOP_ENDALT:
580             /*
581              * If we already patched emitStateSP->nextTermFixup to jump to
582              * a nearer jump, to avoid 16-bit immediate offset overflow, we
583              * are done here.
584              */
585             if (emitStateSP->jumpToJumpFlag)
586                 break;
587
588             /*
589              * Fix up the REOP_JUMP offset to go to the op after REOP_ENDALT.
590              * REOP_ENDALT is executed only on successful match of the last
591              * alternate in a group.
592              */
593             if (!SetForwardJumpOffset(emitStateSP->nextTermFixup, pc))
594                 goto jump_too_big;
595             if (t->op != REOP_ALT) {
596                 if (!SetForwardJumpOffset(emitStateSP->endTermFixup, pc))
597                     goto jump_too_big;
598             }
599
600             /*
601              * If the program is bigger than the REOP_JUMP offset range, then
602              * we must check for alternates before this one that are part of
603              * the same group, and fix up their jump offsets to target jumps
604              * close enough to fit in a 16-bit unsigned offset immediate.
605              */
606             if ((size_t)(pc - re->program) > OFFSET_MAX &&
607                 emitStateSP > emitStateStack) {
608                 EmitStateStackEntry *esp, *esp2;
609                 jsbytecode *alt, *jump;
610                 ptrdiff_t span, header;
611
612                 esp2 = emitStateSP;
613                 alt = esp2->altHead;
614                 for (esp = esp2 - 1; esp >= emitStateStack; --esp) {
615                     if (esp->continueOp == REOP_ENDALT &&
616                         !esp->jumpToJumpFlag &&
617                         esp->nextTermFixup + OFFSET_LEN == alt &&
618                         (size_t)(pc - ((esp->continueNode->op != REOP_ALT)
619                                        ? esp->endTermFixup
620                                        : esp->nextTermFixup)) > OFFSET_MAX) {
621                         alt = esp->altHead;
622                         jump = esp->nextTermFixup;
623
624                         /*
625                          * The span must be 1 less than the distance from
626                          * jump offset to jump offset, so we actually jump
627                          * to a REOP_JUMP bytecode, not to its offset!
628                          */
629                         for (;;) {
630                             assert(jump < esp2->nextTermFixup);
631                             span = esp2->nextTermFixup - jump - 1;
632                             if ((size_t)span <= OFFSET_MAX)
633                                 break;
634                             do {
635                                 if (--esp2 == esp)
636                                     goto jump_too_big;
637                             } while (esp2->continueOp != REOP_ENDALT);
638                         }
639
640                         jump[0] = JUMP_OFFSET_HI(span);
641                         jump[1] = JUMP_OFFSET_LO(span);
642
643                         if (esp->continueNode->op != REOP_ALT) {
644                             /*
645                              * We must patch the offset at esp->endTermFixup
646                              * as well, for the REOP_ALTPREREQ{,2} opcodes.
647                              * If we're unlucky and endTermFixup is more than
648                              * OFFSET_MAX bytes from its target, we cheat by
649                              * jumping 6 bytes to the jump whose offset is at
650                              * esp->nextTermFixup, which has the same target.
651                              */
652                             jump = esp->endTermFixup;
653                             header = esp->nextTermFixup - jump;
654                             span += header;
655                             if ((size_t)span > OFFSET_MAX)
656                                 span = header;
657
658                             jump[0] = JUMP_OFFSET_HI(span);
659                             jump[1] = JUMP_OFFSET_LO(span);
660                         }
661
662                         esp->jumpToJumpFlag = TRUE;
663                     }
664                 }
665             }
666             break;
667
668           case REOP_ALT:
669             assert(emitStateSP);
670             emitStateSP->altHead = pc - 1;
671             emitStateSP->nextAltFixup = pc;     /* offset to next alternate */
672             pc += OFFSET_LEN;
673             emitStateSP->continueNode = t;
674             emitStateSP->continueOp = REOP_JUMP;
675             emitStateSP->jumpToJumpFlag = FALSE;
676             ++emitStateSP;
677             assert((size_t)(emitStateSP - emitStateStack) <= treeDepth);
678             t = t->kid;
679             op = t->op;
680             assert(op < REOP_LIMIT);
681             continue;
682
683           case REOP_FLAT:
684             /*
685              * Coalesce FLATs if possible and if it would not increase bytecode
686              * beyond preallocated limit. The latter happens only when bytecode
687              * size for coalesced string with offset p and length 2 exceeds 6
688              * bytes preallocated for 2 single char nodes, i.e. when
689              * 1 + GetCompactIndexWidth(p) + GetCompactIndexWidth(2) > 6 or
690              * GetCompactIndexWidth(p) > 4.
691              * Since when GetCompactIndexWidth(p) <= 4 coalescing of 3 or more
692              * nodes strictly decreases bytecode size, the check has to be
693              * done only for the first coalescing.
694              */
695             if (t->kid &&
696                 GetCompactIndexWidth((WCHAR*)t->kid - state->cpbegin) <= 4)
697             {
698                 while (t->next &&
699                        t->next->op == REOP_FLAT &&
700                        (WCHAR*)t->kid + t->u.flat.length ==
701                        t->next->kid) {
702                     t->u.flat.length += t->next->u.flat.length;
703                     t->next = t->next->next;
704                 }
705             }
706             if (t->kid && t->u.flat.length > 1) {
707                 pc[-1] = (state->flags & JSREG_FOLD) ? REOP_FLATi : REOP_FLAT;
708                 pc = WriteCompactIndex(pc, (WCHAR*)t->kid - state->cpbegin);
709                 pc = WriteCompactIndex(pc, t->u.flat.length);
710             } else if (t->u.flat.chr < 256) {
711                 pc[-1] = (state->flags & JSREG_FOLD) ? REOP_FLAT1i : REOP_FLAT1;
712                 *pc++ = (jsbytecode) t->u.flat.chr;
713             } else {
714                 pc[-1] = (state->flags & JSREG_FOLD)
715                          ? REOP_UCFLAT1i
716                          : REOP_UCFLAT1;
717                 SET_ARG(pc, t->u.flat.chr);
718                 pc += ARG_LEN;
719             }
720             break;
721
722           case REOP_LPAREN:
723             assert(emitStateSP);
724             pc = WriteCompactIndex(pc, t->u.parenIndex);
725             emitStateSP->continueNode = t;
726             emitStateSP->continueOp = REOP_RPAREN;
727             ++emitStateSP;
728             assert((size_t)(emitStateSP - emitStateStack) <= treeDepth);
729             t = t->kid;
730             op = t->op;
731             continue;
732
733           case REOP_RPAREN:
734             pc = WriteCompactIndex(pc, t->u.parenIndex);
735             break;
736
737           case REOP_BACKREF:
738             pc = WriteCompactIndex(pc, t->u.parenIndex);
739             break;
740
741           case REOP_ASSERT:
742             assert(emitStateSP);
743             emitStateSP->nextTermFixup = pc;
744             pc += OFFSET_LEN;
745             emitStateSP->continueNode = t;
746             emitStateSP->continueOp = REOP_ASSERTTEST;
747             ++emitStateSP;
748             assert((size_t)(emitStateSP - emitStateStack) <= treeDepth);
749             t = t->kid;
750             op = t->op;
751             continue;
752
753           case REOP_ASSERTTEST:
754           case REOP_ASSERTNOTTEST:
755             if (!SetForwardJumpOffset(emitStateSP->nextTermFixup, pc))
756                 goto jump_too_big;
757             break;
758
759           case REOP_ASSERT_NOT:
760             assert(emitStateSP);
761             emitStateSP->nextTermFixup = pc;
762             pc += OFFSET_LEN;
763             emitStateSP->continueNode = t;
764             emitStateSP->continueOp = REOP_ASSERTNOTTEST;
765             ++emitStateSP;
766             assert((size_t)(emitStateSP - emitStateStack) <= treeDepth);
767             t = t->kid;
768             op = t->op;
769             continue;
770
771           case REOP_QUANT:
772             assert(emitStateSP);
773             if (t->u.range.min == 0 && t->u.range.max == (UINT)-1) {
774                 pc[-1] = (t->u.range.greedy) ? REOP_STAR : REOP_MINIMALSTAR;
775             } else if (t->u.range.min == 0 && t->u.range.max == 1) {
776                 pc[-1] = (t->u.range.greedy) ? REOP_OPT : REOP_MINIMALOPT;
777             } else if (t->u.range.min == 1 && t->u.range.max == (UINT) -1) {
778                 pc[-1] = (t->u.range.greedy) ? REOP_PLUS : REOP_MINIMALPLUS;
779             } else {
780                 if (!t->u.range.greedy)
781                     pc[-1] = REOP_MINIMALQUANT;
782                 pc = WriteCompactIndex(pc, t->u.range.min);
783                 /*
784                  * Write max + 1 to avoid using size_t(max) + 1 bytes
785                  * for (UINT)-1 sentinel.
786                  */
787                 pc = WriteCompactIndex(pc, t->u.range.max + 1);
788             }
789             emitStateSP->nextTermFixup = pc;
790             pc += OFFSET_LEN;
791             emitStateSP->continueNode = t;
792             emitStateSP->continueOp = REOP_ENDCHILD;
793             ++emitStateSP;
794             assert((size_t)(emitStateSP - emitStateStack) <= treeDepth);
795             t = t->kid;
796             op = t->op;
797             continue;
798
799           case REOP_ENDCHILD:
800             if (!SetForwardJumpOffset(emitStateSP->nextTermFixup, pc))
801                 goto jump_too_big;
802             break;
803
804           case REOP_CLASS:
805             if (!t->u.ucclass.sense)
806                 pc[-1] = REOP_NCLASS;
807             pc = WriteCompactIndex(pc, t->u.ucclass.index);
808             charSet = &re->classList[t->u.ucclass.index];
809             charSet->converted = FALSE;
810             charSet->length = t->u.ucclass.bmsize;
811             charSet->u.src.startIndex = t->u.ucclass.startIndex;
812             charSet->u.src.length = t->u.ucclass.kidlen;
813             charSet->sense = t->u.ucclass.sense;
814             break;
815
816           default:
817             break;
818         }
819
820         t = t->next;
821         if (t) {
822             op = t->op;
823         } else {
824             if (emitStateSP == emitStateStack)
825                 break;
826             --emitStateSP;
827             t = emitStateSP->continueNode;
828             op = (REOp) emitStateSP->continueOp;
829         }
830     }
831
832   cleanup:
833     heap_free(emitStateStack);
834     return pc;
835
836   jump_too_big:
837     ReportRegExpError(state, JSREPORT_ERROR, JSMSG_REGEXP_TOO_COMPLEX);
838     pc = NULL;
839     goto cleanup;
840 }
841
842 /*
843  * Process the op against the two top operands, reducing them to a single
844  * operand in the penultimate slot. Update progLength and treeDepth.
845  */
846 static BOOL
847 ProcessOp(CompilerState *state, REOpData *opData, RENode **operandStack,
848           INT operandSP)
849 {
850     RENode *result;
851
852     switch (opData->op) {
853       case REOP_ALT:
854         result = NewRENode(state, REOP_ALT);
855         if (!result)
856             return FALSE;
857         result->kid = operandStack[operandSP - 2];
858         result->u.kid2 = operandStack[operandSP - 1];
859         operandStack[operandSP - 2] = result;
860
861         if (state->treeDepth == TREE_DEPTH_MAX) {
862             ReportRegExpError(state, JSREPORT_ERROR, JSMSG_REGEXP_TOO_COMPLEX);
863             return FALSE;
864         }
865         ++state->treeDepth;
866
867         /*
868          * Look at both alternates to see if there's a FLAT or a CLASS at
869          * the start of each. If so, use a prerequisite match.
870          */
871         if (((RENode *) result->kid)->op == REOP_FLAT &&
872             ((RENode *) result->u.kid2)->op == REOP_FLAT &&
873             (state->flags & JSREG_FOLD) == 0) {
874             result->op = REOP_ALTPREREQ;
875             result->u.altprereq.ch1 = ((RENode *) result->kid)->u.flat.chr;
876             result->u.altprereq.ch2 = ((RENode *) result->u.kid2)->u.flat.chr;
877             /* ALTPREREQ, <end>, uch1, uch2, <next>, ...,
878                                             JUMP, <end> ... ENDALT */
879             state->progLength += 13;
880         }
881         else
882         if (((RENode *) result->kid)->op == REOP_CLASS &&
883             ((RENode *) result->kid)->u.ucclass.index < 256 &&
884             ((RENode *) result->u.kid2)->op == REOP_FLAT &&
885             (state->flags & JSREG_FOLD) == 0) {
886             result->op = REOP_ALTPREREQ2;
887             result->u.altprereq.ch1 = ((RENode *) result->u.kid2)->u.flat.chr;
888             result->u.altprereq.ch2 = ((RENode *) result->kid)->u.ucclass.index;
889             /* ALTPREREQ2, <end>, uch1, uch2, <next>, ...,
890                                             JUMP, <end> ... ENDALT */
891             state->progLength += 13;
892         }
893         else
894         if (((RENode *) result->kid)->op == REOP_FLAT &&
895             ((RENode *) result->u.kid2)->op == REOP_CLASS &&
896             ((RENode *) result->u.kid2)->u.ucclass.index < 256 &&
897             (state->flags & JSREG_FOLD) == 0) {
898             result->op = REOP_ALTPREREQ2;
899             result->u.altprereq.ch1 = ((RENode *) result->kid)->u.flat.chr;
900             result->u.altprereq.ch2 =
901                 ((RENode *) result->u.kid2)->u.ucclass.index;
902             /* ALTPREREQ2, <end>, uch1, uch2, <next>, ...,
903                                           JUMP, <end> ... ENDALT */
904             state->progLength += 13;
905         }
906         else {
907             /* ALT, <next>, ..., JUMP, <end> ... ENDALT */
908             state->progLength += 7;
909         }
910         break;
911
912       case REOP_CONCAT:
913         result = operandStack[operandSP - 2];
914         while (result->next)
915             result = result->next;
916         result->next = operandStack[operandSP - 1];
917         break;
918
919       case REOP_ASSERT:
920       case REOP_ASSERT_NOT:
921       case REOP_LPARENNON:
922       case REOP_LPAREN:
923         /* These should have been processed by a close paren. */
924         ReportRegExpErrorHelper(state, JSREPORT_ERROR, JSMSG_MISSING_PAREN,
925                                 opData->errPos);
926         return FALSE;
927
928       default:;
929     }
930     return TRUE;
931 }
932
933 /*
934  * Hack two bits in CompilerState.flags, for use within FindParenCount to flag
935  * its being on the stack, and to propagate errors to its callers.
936  */
937 #define JSREG_FIND_PAREN_COUNT  0x8000
938 #define JSREG_FIND_PAREN_ERROR  0x4000
939
940 /*
941  * Magic return value from FindParenCount and GetDecimalValue, to indicate
942  * overflow beyond GetDecimalValue's max parameter, or a computed maximum if
943  * its findMax parameter is non-null.
944  */
945 #define OVERFLOW_VALUE          ((UINT)-1)
946
947 static UINT
948 FindParenCount(CompilerState *state)
949 {
950     CompilerState temp;
951     int i;
952
953     if (state->flags & JSREG_FIND_PAREN_COUNT)
954         return OVERFLOW_VALUE;
955
956     /*
957      * Copy state into temp, flag it so we never report an invalid backref,
958      * and reset its members to parse the entire regexp.  This is obviously
959      * suboptimal, but GetDecimalValue calls us only if a backref appears to
960      * refer to a forward parenthetical, which is rare.
961      */
962     temp = *state;
963     temp.flags |= JSREG_FIND_PAREN_COUNT;
964     temp.cp = temp.cpbegin;
965     temp.parenCount = 0;
966     temp.classCount = 0;
967     temp.progLength = 0;
968     temp.treeDepth = 0;
969     temp.classBitmapsMem = 0;
970     for (i = 0; i < CLASS_CACHE_SIZE; i++)
971         temp.classCache[i].start = NULL;
972
973     if (!ParseRegExp(&temp)) {
974         state->flags |= JSREG_FIND_PAREN_ERROR;
975         return OVERFLOW_VALUE;
976     }
977     return temp.parenCount;
978 }
979
980 /*
981  * Extract and return a decimal value at state->cp.  The initial character c
982  * has already been read.  Return OVERFLOW_VALUE if the result exceeds max.
983  * Callers who pass a non-null findMax should test JSREG_FIND_PAREN_ERROR in
984  * state->flags to discover whether an error occurred under findMax.
985  */
986 static UINT
987 GetDecimalValue(WCHAR c, UINT max, UINT (*findMax)(CompilerState *state),
988                 CompilerState *state)
989 {
990     UINT value = JS7_UNDEC(c);
991     BOOL overflow = (value > max && (!findMax || value > findMax(state)));
992
993     /* The following restriction allows simpler overflow checks. */
994     assert(max <= ((UINT)-1 - 9) / 10);
995     while (state->cp < state->cpend) {
996         c = *state->cp;
997         if (!JS7_ISDEC(c))
998             break;
999         value = 10 * value + JS7_UNDEC(c);
1000         if (!overflow && value > max && (!findMax || value > findMax(state)))
1001             overflow = TRUE;
1002         ++state->cp;
1003     }
1004     return overflow ? OVERFLOW_VALUE : value;
1005 }
1006
1007 /*
1008  * Calculate the total size of the bitmap required for a class expression.
1009  */
1010 static BOOL
1011 CalculateBitmapSize(CompilerState *state, RENode *target, const WCHAR *src,
1012                     const WCHAR *end)
1013 {
1014     UINT max = 0;
1015     BOOL inRange = FALSE;
1016     WCHAR c, rangeStart = 0;
1017     UINT n, digit, nDigits, i;
1018
1019     target->u.ucclass.bmsize = 0;
1020     target->u.ucclass.sense = TRUE;
1021
1022     if (src == end)
1023         return TRUE;
1024
1025     if (*src == '^') {
1026         ++src;
1027         target->u.ucclass.sense = FALSE;
1028     }
1029
1030     while (src != end) {
1031         BOOL canStartRange = TRUE;
1032         UINT localMax = 0;
1033
1034         switch (*src) {
1035           case '\\':
1036             ++src;
1037             c = *src++;
1038             switch (c) {
1039               case 'b':
1040                 localMax = 0x8;
1041                 break;
1042               case 'f':
1043                 localMax = 0xC;
1044                 break;
1045               case 'n':
1046                 localMax = 0xA;
1047                 break;
1048               case 'r':
1049                 localMax = 0xD;
1050                 break;
1051               case 't':
1052                 localMax = 0x9;
1053                 break;
1054               case 'v':
1055                 localMax = 0xB;
1056                 break;
1057               case 'c':
1058                 if (src < end && RE_IS_LETTER(*src)) {
1059                     localMax = (UINT) (*src++) & 0x1F;
1060                 } else {
1061                     --src;
1062                     localMax = '\\';
1063                 }
1064                 break;
1065               case 'x':
1066                 nDigits = 2;
1067                 goto lexHex;
1068               case 'u':
1069                 nDigits = 4;
1070 lexHex:
1071                 n = 0;
1072                 for (i = 0; (i < nDigits) && (src < end); i++) {
1073                     c = *src++;
1074                     if (!isASCIIHexDigit(c, &digit)) {
1075                         /*
1076                          * Back off to accepting the original
1077                          *'\' as a literal.
1078                          */
1079                         src -= i + 1;
1080                         n = '\\';
1081                         break;
1082                     }
1083                     n = (n << 4) | digit;
1084                 }
1085                 localMax = n;
1086                 break;
1087               case 'd':
1088                 canStartRange = FALSE;
1089                 if (inRange) {
1090                     JS_ReportErrorNumber(state->context,
1091                                          js_GetErrorMessage, NULL,
1092                                          JSMSG_BAD_CLASS_RANGE);
1093                     return FALSE;
1094                 }
1095                 localMax = '9';
1096                 break;
1097               case 'D':
1098               case 's':
1099               case 'S':
1100               case 'w':
1101               case 'W':
1102                 canStartRange = FALSE;
1103                 if (inRange) {
1104                     JS_ReportErrorNumber(state->context,
1105                                          js_GetErrorMessage, NULL,
1106                                          JSMSG_BAD_CLASS_RANGE);
1107                     return FALSE;
1108                 }
1109                 max = 65535;
1110
1111                 /*
1112                  * If this is the start of a range, ensure that it's less than
1113                  * the end.
1114                  */
1115                 localMax = 0;
1116                 break;
1117               case '0':
1118               case '1':
1119               case '2':
1120               case '3':
1121               case '4':
1122               case '5':
1123               case '6':
1124               case '7':
1125                 /*
1126                  *  This is a non-ECMA extension - decimal escapes (in this
1127                  *  case, octal!) are supposed to be an error inside class
1128                  *  ranges, but supported here for backwards compatibility.
1129                  *
1130                  */
1131                 n = JS7_UNDEC(c);
1132                 c = *src;
1133                 if ('0' <= c && c <= '7') {
1134                     src++;
1135                     n = 8 * n + JS7_UNDEC(c);
1136                     c = *src;
1137                     if ('0' <= c && c <= '7') {
1138                         src++;
1139                         i = 8 * n + JS7_UNDEC(c);
1140                         if (i <= 0377)
1141                             n = i;
1142                         else
1143                             src--;
1144                     }
1145                 }
1146                 localMax = n;
1147                 break;
1148
1149               default:
1150                 localMax = c;
1151                 break;
1152             }
1153             break;
1154           default:
1155             localMax = *src++;
1156             break;
1157         }
1158
1159         if (inRange) {
1160             /* Throw a SyntaxError here, per ECMA-262, 15.10.2.15. */
1161             if (rangeStart > localMax) {
1162                 JS_ReportErrorNumber(state->context,
1163                                      js_GetErrorMessage, NULL,
1164                                      JSMSG_BAD_CLASS_RANGE);
1165                 return FALSE;
1166             }
1167             inRange = FALSE;
1168         } else {
1169             if (canStartRange && src < end - 1) {
1170                 if (*src == '-') {
1171                     ++src;
1172                     inRange = TRUE;
1173                     rangeStart = (WCHAR)localMax;
1174                     continue;
1175                 }
1176             }
1177             if (state->flags & JSREG_FOLD)
1178                 rangeStart = localMax;   /* one run of the uc/dc loop below */
1179         }
1180
1181         if (state->flags & JSREG_FOLD) {
1182             WCHAR maxch = localMax;
1183
1184             for (i = rangeStart; i <= localMax; i++) {
1185                 WCHAR uch, dch;
1186
1187                 uch = toupperW(i);
1188                 dch = tolowerW(i);
1189                 if(maxch < uch)
1190                     maxch = uch;
1191                 if(maxch < dch)
1192                     maxch = dch;
1193             }
1194             localMax = maxch;
1195         }
1196
1197         if (localMax > max)
1198             max = localMax;
1199     }
1200     target->u.ucclass.bmsize = max;
1201     return TRUE;
1202 }
1203
1204 static INT
1205 ParseMinMaxQuantifier(CompilerState *state, BOOL ignoreValues)
1206 {
1207     UINT min, max;
1208     WCHAR c;
1209     const WCHAR *errp = state->cp++;
1210
1211     c = *state->cp;
1212     if (JS7_ISDEC(c)) {
1213         ++state->cp;
1214         min = GetDecimalValue(c, 0xFFFF, NULL, state);
1215         c = *state->cp;
1216
1217         if (!ignoreValues && min == OVERFLOW_VALUE)
1218             return JSMSG_MIN_TOO_BIG;
1219
1220         if (c == ',') {
1221             c = *++state->cp;
1222             if (JS7_ISDEC(c)) {
1223                 ++state->cp;
1224                 max = GetDecimalValue(c, 0xFFFF, NULL, state);
1225                 c = *state->cp;
1226                 if (!ignoreValues && max == OVERFLOW_VALUE)
1227                     return JSMSG_MAX_TOO_BIG;
1228                 if (!ignoreValues && min > max)
1229                     return JSMSG_OUT_OF_ORDER;
1230             } else {
1231                 max = (UINT)-1;
1232             }
1233         } else {
1234             max = min;
1235         }
1236         if (c == '}') {
1237             state->result = NewRENode(state, REOP_QUANT);
1238             if (!state->result)
1239                 return JSMSG_OUT_OF_MEMORY;
1240             state->result->u.range.min = min;
1241             state->result->u.range.max = max;
1242             /*
1243              * QUANT, <min>, <max>, <next> ... <ENDCHILD>
1244              * where <max> is written as compact(max+1) to make
1245              * (UINT)-1 sentinel to occupy 1 byte, not width_of(max)+1.
1246              */
1247             state->progLength += (1 + GetCompactIndexWidth(min)
1248                                   + GetCompactIndexWidth(max + 1)
1249                                   +3);
1250             return 0;
1251         }
1252     }
1253
1254     state->cp = errp;
1255     return -1;
1256 }
1257
1258 static BOOL
1259 ParseQuantifier(CompilerState *state)
1260 {
1261     RENode *term;
1262     term = state->result;
1263     if (state->cp < state->cpend) {
1264         switch (*state->cp) {
1265           case '+':
1266             state->result = NewRENode(state, REOP_QUANT);
1267             if (!state->result)
1268                 return FALSE;
1269             state->result->u.range.min = 1;
1270             state->result->u.range.max = (UINT)-1;
1271             /* <PLUS>, <next> ... <ENDCHILD> */
1272             state->progLength += 4;
1273             goto quantifier;
1274           case '*':
1275             state->result = NewRENode(state, REOP_QUANT);
1276             if (!state->result)
1277                 return FALSE;
1278             state->result->u.range.min = 0;
1279             state->result->u.range.max = (UINT)-1;
1280             /* <STAR>, <next> ... <ENDCHILD> */
1281             state->progLength += 4;
1282             goto quantifier;
1283           case '?':
1284             state->result = NewRENode(state, REOP_QUANT);
1285             if (!state->result)
1286                 return FALSE;
1287             state->result->u.range.min = 0;
1288             state->result->u.range.max = 1;
1289             /* <OPT>, <next> ... <ENDCHILD> */
1290             state->progLength += 4;
1291             goto quantifier;
1292           case '{':       /* balance '}' */
1293           {
1294             INT err;
1295
1296             err = ParseMinMaxQuantifier(state, FALSE);
1297             if (err == 0)
1298                 goto quantifier;
1299             if (err == -1)
1300                 return TRUE;
1301
1302             ReportRegExpErrorHelper(state, JSREPORT_ERROR, err, errp);
1303             return FALSE;
1304           }
1305           default:;
1306         }
1307     }
1308     return TRUE;
1309
1310 quantifier:
1311     if (state->treeDepth == TREE_DEPTH_MAX) {
1312         ReportRegExpError(state, JSREPORT_ERROR, JSMSG_REGEXP_TOO_COMPLEX);
1313         return FALSE;
1314     }
1315
1316     ++state->treeDepth;
1317     ++state->cp;
1318     state->result->kid = term;
1319     if (state->cp < state->cpend && *state->cp == '?') {
1320         ++state->cp;
1321         state->result->u.range.greedy = FALSE;
1322     } else {
1323         state->result->u.range.greedy = TRUE;
1324     }
1325     return TRUE;
1326 }
1327
1328 /*
1329  *  item:       assertion               An item is either an assertion or
1330  *              quantatom               a quantified atom.
1331  *
1332  *  assertion:  '^'                     Assertions match beginning of string
1333  *                                      (or line if the class static property
1334  *                                      RegExp.multiline is true).
1335  *              '$'                     End of string (or line if the class
1336  *                                      static property RegExp.multiline is
1337  *                                      true).
1338  *              '\b'                    Word boundary (between \w and \W).
1339  *              '\B'                    Word non-boundary.
1340  *
1341  *  quantatom:  atom                    An unquantified atom.
1342  *              quantatom '{' n ',' m '}'
1343  *                                      Atom must occur between n and m times.
1344  *              quantatom '{' n ',' '}' Atom must occur at least n times.
1345  *              quantatom '{' n '}'     Atom must occur exactly n times.
1346  *              quantatom '*'           Zero or more times (same as {0,}).
1347  *              quantatom '+'           One or more times (same as {1,}).
1348  *              quantatom '?'           Zero or one time (same as {0,1}).
1349  *
1350  *              any of which can be optionally followed by '?' for ungreedy
1351  *
1352  *  atom:       '(' regexp ')'          A parenthesized regexp (what matched
1353  *                                      can be addressed using a backreference,
1354  *                                      see '\' n below).
1355  *              '.'                     Matches any char except '\n'.
1356  *              '[' classlist ']'       A character class.
1357  *              '[' '^' classlist ']'   A negated character class.
1358  *              '\f'                    Form Feed.
1359  *              '\n'                    Newline (Line Feed).
1360  *              '\r'                    Carriage Return.
1361  *              '\t'                    Horizontal Tab.
1362  *              '\v'                    Vertical Tab.
1363  *              '\d'                    A digit (same as [0-9]).
1364  *              '\D'                    A non-digit.
1365  *              '\w'                    A word character, [0-9a-z_A-Z].
1366  *              '\W'                    A non-word character.
1367  *              '\s'                    A whitespace character, [ \b\f\n\r\t\v].
1368  *              '\S'                    A non-whitespace character.
1369  *              '\' n                   A backreference to the nth (n decimal
1370  *                                      and positive) parenthesized expression.
1371  *              '\' octal               An octal escape sequence (octal must be
1372  *                                      two or three digits long, unless it is
1373  *                                      0 for the null character).
1374  *              '\x' hex                A hex escape (hex must be two digits).
1375  *              '\u' unicode            A unicode escape (must be four digits).
1376  *              '\c' ctrl               A control character, ctrl is a letter.
1377  *              '\' literalatomchar     Any character except one of the above
1378  *                                      that follow '\' in an atom.
1379  *              otheratomchar           Any character not first among the other
1380  *                                      atom right-hand sides.
1381  */
1382 static BOOL
1383 ParseTerm(CompilerState *state)
1384 {
1385     WCHAR c = *state->cp++;
1386     UINT nDigits;
1387     UINT num, tmp, n, i;
1388     const WCHAR *termStart;
1389
1390     switch (c) {
1391     /* assertions and atoms */
1392       case '^':
1393         state->result = NewRENode(state, REOP_BOL);
1394         if (!state->result)
1395             return FALSE;
1396         state->progLength++;
1397         return TRUE;
1398       case '$':
1399         state->result = NewRENode(state, REOP_EOL);
1400         if (!state->result)
1401             return FALSE;
1402         state->progLength++;
1403         return TRUE;
1404       case '\\':
1405         if (state->cp >= state->cpend) {
1406             /* a trailing '\' is an error */
1407             ReportRegExpError(state, JSREPORT_ERROR, JSMSG_TRAILING_SLASH);
1408             return FALSE;
1409         }
1410         c = *state->cp++;
1411         switch (c) {
1412         /* assertion escapes */
1413           case 'b' :
1414             state->result = NewRENode(state, REOP_WBDRY);
1415             if (!state->result)
1416                 return FALSE;
1417             state->progLength++;
1418             return TRUE;
1419           case 'B':
1420             state->result = NewRENode(state, REOP_WNONBDRY);
1421             if (!state->result)
1422                 return FALSE;
1423             state->progLength++;
1424             return TRUE;
1425           /* Decimal escape */
1426           case '0':
1427               /* Give a strict warning. See also the note below. */
1428               WARN("non-octal digit in an escape sequence that doesn't match a back-reference\n");
1429      doOctal:
1430             num = 0;
1431             while (state->cp < state->cpend) {
1432                 c = *state->cp;
1433                 if (c < '0' || '7' < c)
1434                     break;
1435                 state->cp++;
1436                 tmp = 8 * num + (UINT)JS7_UNDEC(c);
1437                 if (tmp > 0377)
1438                     break;
1439                 num = tmp;
1440             }
1441             c = (WCHAR)num;
1442     doFlat:
1443             state->result = NewRENode(state, REOP_FLAT);
1444             if (!state->result)
1445                 return FALSE;
1446             state->result->u.flat.chr = c;
1447             state->result->u.flat.length = 1;
1448             state->progLength += 3;
1449             break;
1450           case '1':
1451           case '2':
1452           case '3':
1453           case '4':
1454           case '5':
1455           case '6':
1456           case '7':
1457           case '8':
1458           case '9':
1459             termStart = state->cp - 1;
1460             num = GetDecimalValue(c, state->parenCount, FindParenCount, state);
1461             if (state->flags & JSREG_FIND_PAREN_ERROR)
1462                 return FALSE;
1463             if (num == OVERFLOW_VALUE) {
1464                 /* Give a strict mode warning. */
1465                 WARN("back-reference exceeds number of capturing parentheses\n");
1466
1467                 /*
1468                  * Note: ECMA 262, 15.10.2.9 says that we should throw a syntax
1469                  * error here. However, for compatibility with IE, we treat the
1470                  * whole backref as flat if the first character in it is not a
1471                  * valid octal character, and as an octal escape otherwise.
1472                  */
1473                 state->cp = termStart;
1474                 if (c >= '8') {
1475                     /* Treat this as flat. termStart - 1 is the \. */
1476                     c = '\\';
1477                     goto asFlat;
1478                 }
1479
1480                 /* Treat this as an octal escape. */
1481                 goto doOctal;
1482             }
1483             assert(1 <= num && num <= 0x10000);
1484             state->result = NewRENode(state, REOP_BACKREF);
1485             if (!state->result)
1486                 return FALSE;
1487             state->result->u.parenIndex = num - 1;
1488             state->progLength
1489                 += 1 + GetCompactIndexWidth(state->result->u.parenIndex);
1490             break;
1491           /* Control escape */
1492           case 'f':
1493             c = 0xC;
1494             goto doFlat;
1495           case 'n':
1496             c = 0xA;
1497             goto doFlat;
1498           case 'r':
1499             c = 0xD;
1500             goto doFlat;
1501           case 't':
1502             c = 0x9;
1503             goto doFlat;
1504           case 'v':
1505             c = 0xB;
1506             goto doFlat;
1507           /* Control letter */
1508           case 'c':
1509             if (state->cp < state->cpend && RE_IS_LETTER(*state->cp)) {
1510                 c = (WCHAR) (*state->cp++ & 0x1F);
1511             } else {
1512                 /* back off to accepting the original '\' as a literal */
1513                 --state->cp;
1514                 c = '\\';
1515             }
1516             goto doFlat;
1517           /* HexEscapeSequence */
1518           case 'x':
1519             nDigits = 2;
1520             goto lexHex;
1521           /* UnicodeEscapeSequence */
1522           case 'u':
1523             nDigits = 4;
1524 lexHex:
1525             n = 0;
1526             for (i = 0; i < nDigits && state->cp < state->cpend; i++) {
1527                 UINT digit;
1528                 c = *state->cp++;
1529                 if (!isASCIIHexDigit(c, &digit)) {
1530                     /*
1531                      * Back off to accepting the original 'u' or 'x' as a
1532                      * literal.
1533                      */
1534                     state->cp -= i + 2;
1535                     n = *state->cp++;
1536                     break;
1537                 }
1538                 n = (n << 4) | digit;
1539             }
1540             c = (WCHAR) n;
1541             goto doFlat;
1542           /* Character class escapes */
1543           case 'd':
1544             state->result = NewRENode(state, REOP_DIGIT);
1545 doSimple:
1546             if (!state->result)
1547                 return FALSE;
1548             state->progLength++;
1549             break;
1550           case 'D':
1551             state->result = NewRENode(state, REOP_NONDIGIT);
1552             goto doSimple;
1553           case 's':
1554             state->result = NewRENode(state, REOP_SPACE);
1555             goto doSimple;
1556           case 'S':
1557             state->result = NewRENode(state, REOP_NONSPACE);
1558             goto doSimple;
1559           case 'w':
1560             state->result = NewRENode(state, REOP_ALNUM);
1561             goto doSimple;
1562           case 'W':
1563             state->result = NewRENode(state, REOP_NONALNUM);
1564             goto doSimple;
1565           /* IdentityEscape */
1566           default:
1567             state->result = NewRENode(state, REOP_FLAT);
1568             if (!state->result)
1569                 return FALSE;
1570             state->result->u.flat.chr = c;
1571             state->result->u.flat.length = 1;
1572             state->result->kid = (void *) (state->cp - 1);
1573             state->progLength += 3;
1574             break;
1575         }
1576         break;
1577       case '[':
1578         state->result = NewRENode(state, REOP_CLASS);
1579         if (!state->result)
1580             return FALSE;
1581         termStart = state->cp;
1582         state->result->u.ucclass.startIndex = termStart - state->cpbegin;
1583         for (;;) {
1584             if (state->cp == state->cpend) {
1585                 ReportRegExpErrorHelper(state, JSREPORT_ERROR,
1586                                         JSMSG_UNTERM_CLASS, termStart);
1587
1588                 return FALSE;
1589             }
1590             if (*state->cp == '\\') {
1591                 state->cp++;
1592                 if (state->cp != state->cpend)
1593                     state->cp++;
1594                 continue;
1595             }
1596             if (*state->cp == ']') {
1597                 state->result->u.ucclass.kidlen = state->cp - termStart;
1598                 break;
1599             }
1600             state->cp++;
1601         }
1602         for (i = 0; i < CLASS_CACHE_SIZE; i++) {
1603             if (!state->classCache[i].start) {
1604                 state->classCache[i].start = termStart;
1605                 state->classCache[i].length = state->result->u.ucclass.kidlen;
1606                 state->classCache[i].index = state->classCount;
1607                 break;
1608             }
1609             if (state->classCache[i].length ==
1610                 state->result->u.ucclass.kidlen) {
1611                 for (n = 0; ; n++) {
1612                     if (n == state->classCache[i].length) {
1613                         state->result->u.ucclass.index
1614                             = state->classCache[i].index;
1615                         goto claim;
1616                     }
1617                     if (state->classCache[i].start[n] != termStart[n])
1618                         break;
1619                 }
1620             }
1621         }
1622         state->result->u.ucclass.index = state->classCount++;
1623
1624     claim:
1625         /*
1626          * Call CalculateBitmapSize now as we want any errors it finds
1627          * to be reported during the parse phase, not at execution.
1628          */
1629         if (!CalculateBitmapSize(state, state->result, termStart, state->cp++))
1630             return FALSE;
1631         /*
1632          * Update classBitmapsMem with number of bytes to hold bmsize bits,
1633          * which is (bitsCount + 7) / 8 or (highest_bit + 1 + 7) / 8
1634          * or highest_bit / 8 + 1 where highest_bit is u.ucclass.bmsize.
1635          */
1636         n = (state->result->u.ucclass.bmsize >> 3) + 1;
1637         if (n > CLASS_BITMAPS_MEM_LIMIT - state->classBitmapsMem) {
1638             ReportRegExpError(state, JSREPORT_ERROR, JSMSG_REGEXP_TOO_COMPLEX);
1639             return FALSE;
1640         }
1641         state->classBitmapsMem += n;
1642         /* CLASS, <index> */
1643         state->progLength
1644             += 1 + GetCompactIndexWidth(state->result->u.ucclass.index);
1645         break;
1646
1647       case '.':
1648         state->result = NewRENode(state, REOP_DOT);
1649         goto doSimple;
1650
1651       case '{':
1652       {
1653         const WCHAR *errp = state->cp--;
1654         INT err;
1655
1656         err = ParseMinMaxQuantifier(state, TRUE);
1657         state->cp = errp;
1658
1659         if (err < 0)
1660             goto asFlat;
1661
1662         /* FALL THROUGH */
1663       }
1664       case '*':
1665       case '+':
1666       case '?':
1667         ReportRegExpErrorHelper(state, JSREPORT_ERROR,
1668                                 JSMSG_BAD_QUANTIFIER, state->cp - 1);
1669         return FALSE;
1670       default:
1671 asFlat:
1672         state->result = NewRENode(state, REOP_FLAT);
1673         if (!state->result)
1674             return FALSE;
1675         state->result->u.flat.chr = c;
1676         state->result->u.flat.length = 1;
1677         state->result->kid = (void *) (state->cp - 1);
1678         state->progLength += 3;
1679         break;
1680     }
1681     return ParseQuantifier(state);
1682 }
1683
1684 /*
1685  * Top-down regular expression grammar, based closely on Perl4.
1686  *
1687  *  regexp:     altern                  A regular expression is one or more
1688  *              altern '|' regexp       alternatives separated by vertical bar.
1689  */
1690 #define INITIAL_STACK_SIZE  128
1691
1692 static BOOL
1693 ParseRegExp(CompilerState *state)
1694 {
1695     size_t parenIndex;
1696     RENode *operand;
1697     REOpData *operatorStack;
1698     RENode **operandStack;
1699     REOp op;
1700     INT i;
1701     BOOL result = FALSE;
1702
1703     INT operatorSP = 0, operatorStackSize = INITIAL_STACK_SIZE;
1704     INT operandSP = 0, operandStackSize = INITIAL_STACK_SIZE;
1705
1706     /* Watch out for empty regexp */
1707     if (state->cp == state->cpend) {
1708         state->result = NewRENode(state, REOP_EMPTY);
1709         return (state->result != NULL);
1710     }
1711
1712     operatorStack = heap_alloc(sizeof(REOpData) * operatorStackSize);
1713     if (!operatorStack)
1714         return FALSE;
1715
1716     operandStack = heap_alloc(sizeof(RENode *) * operandStackSize);
1717     if (!operandStack)
1718         goto out;
1719
1720     for (;;) {
1721         parenIndex = state->parenCount;
1722         if (state->cp == state->cpend) {
1723             /*
1724              * If we are at the end of the regexp and we're short one or more
1725              * operands, the regexp must have the form /x|/ or some such, with
1726              * left parentheses making us short more than one operand.
1727              */
1728             if (operatorSP >= operandSP) {
1729                 operand = NewRENode(state, REOP_EMPTY);
1730                 if (!operand)
1731                     goto out;
1732                 goto pushOperand;
1733             }
1734         } else {
1735             switch (*state->cp) {
1736               case '(':
1737                 ++state->cp;
1738                 if (state->cp + 1 < state->cpend &&
1739                     *state->cp == '?' &&
1740                     (state->cp[1] == '=' ||
1741                      state->cp[1] == '!' ||
1742                      state->cp[1] == ':')) {
1743                     switch (state->cp[1]) {
1744                       case '=':
1745                         op = REOP_ASSERT;
1746                         /* ASSERT, <next>, ... ASSERTTEST */
1747                         state->progLength += 4;
1748                         break;
1749                       case '!':
1750                         op = REOP_ASSERT_NOT;
1751                         /* ASSERTNOT, <next>, ... ASSERTNOTTEST */
1752                         state->progLength += 4;
1753                         break;
1754                       default:
1755                         op = REOP_LPARENNON;
1756                         break;
1757                     }
1758                     state->cp += 2;
1759                 } else {
1760                     op = REOP_LPAREN;
1761                     /* LPAREN, <index>, ... RPAREN, <index> */
1762                     state->progLength
1763                         += 2 * (1 + GetCompactIndexWidth(parenIndex));
1764                     state->parenCount++;
1765                     if (state->parenCount == 65535) {
1766                         ReportRegExpError(state, JSREPORT_ERROR,
1767                                           JSMSG_TOO_MANY_PARENS);
1768                         goto out;
1769                     }
1770                 }
1771                 goto pushOperator;
1772
1773               case ')':
1774                 /*
1775                  * If there's no stacked open parenthesis, throw syntax error.
1776                  */
1777                 for (i = operatorSP - 1; ; i--) {
1778                     if (i < 0) {
1779                         ReportRegExpError(state, JSREPORT_ERROR,
1780                                           JSMSG_UNMATCHED_RIGHT_PAREN);
1781                         goto out;
1782                     }
1783                     if (operatorStack[i].op == REOP_ASSERT ||
1784                         operatorStack[i].op == REOP_ASSERT_NOT ||
1785                         operatorStack[i].op == REOP_LPARENNON ||
1786                         operatorStack[i].op == REOP_LPAREN) {
1787                         break;
1788                     }
1789                 }
1790                 /* FALL THROUGH */
1791
1792               case '|':
1793                 /* Expected an operand before these, so make an empty one */
1794                 operand = NewRENode(state, REOP_EMPTY);
1795                 if (!operand)
1796                     goto out;
1797                 goto pushOperand;
1798
1799               default:
1800                 if (!ParseTerm(state))
1801                     goto out;
1802                 operand = state->result;
1803 pushOperand:
1804                 if (operandSP == operandStackSize) {
1805                     RENode **tmp;
1806                     operandStackSize += operandStackSize;
1807                     tmp = heap_realloc(operandStack, sizeof(RENode *) * operandStackSize);
1808                     if (!tmp)
1809                         goto out;
1810                     operandStack = tmp;
1811                 }
1812                 operandStack[operandSP++] = operand;
1813                 break;
1814             }
1815         }
1816
1817         /* At the end; process remaining operators. */
1818 restartOperator:
1819         if (state->cp == state->cpend) {
1820             while (operatorSP) {
1821                 --operatorSP;
1822                 if (!ProcessOp(state, &operatorStack[operatorSP],
1823                                operandStack, operandSP))
1824                     goto out;
1825                 --operandSP;
1826             }
1827             assert(operandSP == 1);
1828             state->result = operandStack[0];
1829             result = TRUE;
1830             goto out;
1831         }
1832
1833         switch (*state->cp) {
1834           case '|':
1835             /* Process any stacked 'concat' operators */
1836             ++state->cp;
1837             while (operatorSP &&
1838                    operatorStack[operatorSP - 1].op == REOP_CONCAT) {
1839                 --operatorSP;
1840                 if (!ProcessOp(state, &operatorStack[operatorSP],
1841                                operandStack, operandSP)) {
1842                     goto out;
1843                 }
1844                 --operandSP;
1845             }
1846             op = REOP_ALT;
1847             goto pushOperator;
1848
1849           case ')':
1850             /*
1851              * If there's no stacked open parenthesis, throw syntax error.
1852              */
1853             for (i = operatorSP - 1; ; i--) {
1854                 if (i < 0) {
1855                     ReportRegExpError(state, JSREPORT_ERROR,
1856                                       JSMSG_UNMATCHED_RIGHT_PAREN);
1857                     goto out;
1858                 }
1859                 if (operatorStack[i].op == REOP_ASSERT ||
1860                     operatorStack[i].op == REOP_ASSERT_NOT ||
1861                     operatorStack[i].op == REOP_LPARENNON ||
1862                     operatorStack[i].op == REOP_LPAREN) {
1863                     break;
1864                 }
1865             }
1866             ++state->cp;
1867
1868             /* Process everything on the stack until the open parenthesis. */
1869             for (;;) {
1870                 assert(operatorSP);
1871                 --operatorSP;
1872                 switch (operatorStack[operatorSP].op) {
1873                   case REOP_ASSERT:
1874                   case REOP_ASSERT_NOT:
1875                   case REOP_LPAREN:
1876                     operand = NewRENode(state, operatorStack[operatorSP].op);
1877                     if (!operand)
1878                         goto out;
1879                     operand->u.parenIndex =
1880                         operatorStack[operatorSP].parenIndex;
1881                     assert(operandSP);
1882                     operand->kid = operandStack[operandSP - 1];
1883                     operandStack[operandSP - 1] = operand;
1884                     if (state->treeDepth == TREE_DEPTH_MAX) {
1885                         ReportRegExpError(state, JSREPORT_ERROR,
1886                                           JSMSG_REGEXP_TOO_COMPLEX);
1887                         goto out;
1888                     }
1889                     ++state->treeDepth;
1890                     /* FALL THROUGH */
1891
1892                   case REOP_LPARENNON:
1893                     state->result = operandStack[operandSP - 1];
1894                     if (!ParseQuantifier(state))
1895                         goto out;
1896                     operandStack[operandSP - 1] = state->result;
1897                     goto restartOperator;
1898                   default:
1899                     if (!ProcessOp(state, &operatorStack[operatorSP],
1900                                    operandStack, operandSP))
1901                         goto out;
1902                     --operandSP;
1903                     break;
1904                 }
1905             }
1906             break;
1907
1908           case '{':
1909           {
1910             const WCHAR *errp = state->cp;
1911
1912             if (ParseMinMaxQuantifier(state, TRUE) < 0) {
1913                 /*
1914                  * This didn't even scan correctly as a quantifier, so we should
1915                  * treat it as flat.
1916                  */
1917                 op = REOP_CONCAT;
1918                 goto pushOperator;
1919             }
1920
1921             state->cp = errp;
1922             /* FALL THROUGH */
1923           }
1924
1925           case '+':
1926           case '*':
1927           case '?':
1928             ReportRegExpErrorHelper(state, JSREPORT_ERROR, JSMSG_BAD_QUANTIFIER,
1929                                     state->cp);
1930             result = FALSE;
1931             goto out;
1932
1933           default:
1934             /* Anything else is the start of the next term. */
1935             op = REOP_CONCAT;
1936 pushOperator:
1937             if (operatorSP == operatorStackSize) {
1938                 REOpData *tmp;
1939                 operatorStackSize += operatorStackSize;
1940                 tmp = heap_realloc(operatorStack, sizeof(REOpData) * operatorStackSize);
1941                 if (!tmp)
1942                     goto out;
1943                 operatorStack = tmp;
1944             }
1945             operatorStack[operatorSP].op = op;
1946             operatorStack[operatorSP].errPos = state->cp;
1947             operatorStack[operatorSP++].parenIndex = parenIndex;
1948             break;
1949         }
1950     }
1951 out:
1952     heap_free(operatorStack);
1953     heap_free(operandStack);
1954     return result;
1955 }
1956
1957 /*
1958  * Save the current state of the match - the position in the input
1959  * text as well as the position in the bytecode. The state of any
1960  * parent expressions is also saved (preceding state).
1961  * Contents of parenCount parentheses from parenIndex are also saved.
1962  */
1963 static REBackTrackData *
1964 PushBackTrackState(REGlobalData *gData, REOp op,
1965                    jsbytecode *target, REMatchState *x, const WCHAR *cp,
1966                    size_t parenIndex, size_t parenCount)
1967 {
1968     size_t i;
1969     REBackTrackData *result =
1970         (REBackTrackData *) ((char *)gData->backTrackSP + gData->cursz);
1971
1972     size_t sz = sizeof(REBackTrackData) +
1973                 gData->stateStackTop * sizeof(REProgState) +
1974                 parenCount * sizeof(RECapture);
1975
1976     ptrdiff_t btsize = gData->backTrackStackSize;
1977     ptrdiff_t btincr = ((char *)result + sz) -
1978                        ((char *)gData->backTrackStack + btsize);
1979
1980     TRACE("\tBT_Push: %lu,%lu\n", (unsigned long) parenIndex, (unsigned long) parenCount);
1981
1982     JS_COUNT_OPERATION(gData->cx, JSOW_JUMP * (1 + parenCount));
1983     if (btincr > 0) {
1984         ptrdiff_t offset = (char *)result - (char *)gData->backTrackStack;
1985
1986         JS_COUNT_OPERATION(gData->cx, JSOW_ALLOCATION);
1987         btincr = ((btincr+btsize-1)/btsize)*btsize;
1988         gData->backTrackStack = jsheap_grow(gData->pool, gData->backTrackStack, btsize, btincr);
1989         if (!gData->backTrackStack) {
1990             js_ReportOutOfScriptQuota(gData->cx);
1991             gData->ok = FALSE;
1992             return NULL;
1993         }
1994         gData->backTrackStackSize = btsize + btincr;
1995         result = (REBackTrackData *) ((char *)gData->backTrackStack + offset);
1996     }
1997     gData->backTrackSP = result;
1998     result->sz = gData->cursz;
1999     gData->cursz = sz;
2000
2001     result->backtrack_op = op;
2002     result->backtrack_pc = target;
2003     result->cp = cp;
2004     result->parenCount = parenCount;
2005     result->parenIndex = parenIndex;
2006
2007     result->saveStateStackTop = gData->stateStackTop;
2008     assert(gData->stateStackTop);
2009     memcpy(result + 1, gData->stateStack,
2010            sizeof(REProgState) * result->saveStateStackTop);
2011
2012     if (parenCount != 0) {
2013         memcpy((char *)(result + 1) +
2014                sizeof(REProgState) * result->saveStateStackTop,
2015                &x->parens[parenIndex],
2016                sizeof(RECapture) * parenCount);
2017         for (i = 0; i != parenCount; i++)
2018             x->parens[parenIndex + i].index = -1;
2019     }
2020
2021     return result;
2022 }
2023
2024 static inline REMatchState *
2025 FlatNIMatcher(REGlobalData *gData, REMatchState *x, WCHAR *matchChars,
2026               size_t length)
2027 {
2028     size_t i;
2029     assert(gData->cpend >= x->cp);
2030     if (length > (size_t)(gData->cpend - x->cp))
2031         return NULL;
2032     for (i = 0; i != length; i++) {
2033         if (toupperW(matchChars[i]) != toupperW(x->cp[i]))
2034             return NULL;
2035     }
2036     x->cp += length;
2037     return x;
2038 }
2039
2040 /*
2041  * 1. Evaluate DecimalEscape to obtain an EscapeValue E.
2042  * 2. If E is not a character then go to step 6.
2043  * 3. Let ch be E's character.
2044  * 4. Let A be a one-element RECharSet containing the character ch.
2045  * 5. Call CharacterSetMatcher(A, false) and return its Matcher result.
2046  * 6. E must be an integer. Let n be that integer.
2047  * 7. If n=0 or n>NCapturingParens then throw a SyntaxError exception.
2048  * 8. Return an internal Matcher closure that takes two arguments, a State x
2049  *    and a Continuation c, and performs the following:
2050  *     1. Let cap be x's captures internal array.
2051  *     2. Let s be cap[n].
2052  *     3. If s is undefined, then call c(x) and return its result.
2053  *     4. Let e be x's endIndex.
2054  *     5. Let len be s's length.
2055  *     6. Let f be e+len.
2056  *     7. If f>InputLength, return failure.
2057  *     8. If there exists an integer i between 0 (inclusive) and len (exclusive)
2058  *        such that Canonicalize(s[i]) is not the same character as
2059  *        Canonicalize(Input [e+i]), then return failure.
2060  *     9. Let y be the State (f, cap).
2061  *     10. Call c(y) and return its result.
2062  */
2063 static REMatchState *
2064 BackrefMatcher(REGlobalData *gData, REMatchState *x, size_t parenIndex)
2065 {
2066     size_t len, i;
2067     const WCHAR *parenContent;
2068     RECapture *cap = &x->parens[parenIndex];
2069
2070     if (cap->index == -1)
2071         return x;
2072
2073     len = cap->length;
2074     if (x->cp + len > gData->cpend)
2075         return NULL;
2076
2077     parenContent = &gData->cpbegin[cap->index];
2078     if (gData->regexp->flags & JSREG_FOLD) {
2079         for (i = 0; i < len; i++) {
2080             if (toupperW(parenContent[i]) != toupperW(x->cp[i]))
2081                 return NULL;
2082         }
2083     } else {
2084         for (i = 0; i < len; i++) {
2085             if (parenContent[i] != x->cp[i])
2086                 return NULL;
2087         }
2088     }
2089     x->cp += len;
2090     return x;
2091 }
2092
2093 /* Add a single character to the RECharSet */
2094 static void
2095 AddCharacterToCharSet(RECharSet *cs, WCHAR c)
2096 {
2097     UINT byteIndex = (UINT)(c >> 3);
2098     assert(c <= cs->length);
2099     cs->u.bits[byteIndex] |= 1 << (c & 0x7);
2100 }
2101
2102
2103 /* Add a character range, c1 to c2 (inclusive) to the RECharSet */
2104 static void
2105 AddCharacterRangeToCharSet(RECharSet *cs, UINT c1, UINT c2)
2106 {
2107     UINT i;
2108
2109     UINT byteIndex1 = c1 >> 3;
2110     UINT byteIndex2 = c2 >> 3;
2111
2112     assert(c2 <= cs->length && c1 <= c2);
2113
2114     c1 &= 0x7;
2115     c2 &= 0x7;
2116
2117     if (byteIndex1 == byteIndex2) {
2118         cs->u.bits[byteIndex1] |= ((BYTE)0xFF >> (7 - (c2 - c1))) << c1;
2119     } else {
2120         cs->u.bits[byteIndex1] |= 0xFF << c1;
2121         for (i = byteIndex1 + 1; i < byteIndex2; i++)
2122             cs->u.bits[i] = 0xFF;
2123         cs->u.bits[byteIndex2] |= (BYTE)0xFF >> (7 - c2);
2124     }
2125 }
2126
2127 /* Compile the source of the class into a RECharSet */
2128 static BOOL
2129 ProcessCharSet(REGlobalData *gData, RECharSet *charSet)
2130 {
2131     const WCHAR *src, *end;
2132     BOOL inRange = FALSE;
2133     WCHAR rangeStart = 0;
2134     UINT byteLength, n;
2135     WCHAR c, thisCh;
2136     INT nDigits, i;
2137
2138     assert(!charSet->converted);
2139     /*
2140      * Assert that startIndex and length points to chars inside [] inside
2141      * source string.
2142      */
2143     assert(1 <= charSet->u.src.startIndex);
2144     assert(charSet->u.src.startIndex
2145               < SysStringLen(gData->regexp->source));
2146     assert(charSet->u.src.length <= SysStringLen(gData->regexp->source)
2147                                        - 1 - charSet->u.src.startIndex);
2148
2149     charSet->converted = TRUE;
2150     src = gData->regexp->source + charSet->u.src.startIndex;
2151
2152     end = src + charSet->u.src.length;
2153
2154     assert(src[-1] == '[' && end[0] == ']');
2155
2156     byteLength = (charSet->length >> 3) + 1;
2157     charSet->u.bits = heap_alloc(byteLength);
2158     if (!charSet->u.bits) {
2159         JS_ReportOutOfMemory(gData->cx);
2160         gData->ok = FALSE;
2161         return FALSE;
2162     }
2163     memset(charSet->u.bits, 0, byteLength);
2164
2165     if (src == end)
2166         return TRUE;
2167
2168     if (*src == '^') {
2169         assert(charSet->sense == FALSE);
2170         ++src;
2171     } else {
2172         assert(charSet->sense == TRUE);
2173     }
2174
2175     while (src != end) {
2176         switch (*src) {
2177           case '\\':
2178             ++src;
2179             c = *src++;
2180             switch (c) {
2181               case 'b':
2182                 thisCh = 0x8;
2183                 break;
2184               case 'f':
2185                 thisCh = 0xC;
2186                 break;
2187               case 'n':
2188                 thisCh = 0xA;
2189                 break;
2190               case 'r':
2191                 thisCh = 0xD;
2192                 break;
2193               case 't':
2194                 thisCh = 0x9;
2195                 break;
2196               case 'v':
2197                 thisCh = 0xB;
2198                 break;
2199               case 'c':
2200                 if (src < end && JS_ISWORD(*src)) {
2201                     thisCh = (WCHAR)(*src++ & 0x1F);
2202                 } else {
2203                     --src;
2204                     thisCh = '\\';
2205                 }
2206                 break;
2207               case 'x':
2208                 nDigits = 2;
2209                 goto lexHex;
2210               case 'u':
2211                 nDigits = 4;
2212             lexHex:
2213                 n = 0;
2214                 for (i = 0; (i < nDigits) && (src < end); i++) {
2215                     UINT digit;
2216                     c = *src++;
2217                     if (!isASCIIHexDigit(c, &digit)) {
2218                         /*
2219                          * Back off to accepting the original '\'
2220                          * as a literal
2221                          */
2222                         src -= i + 1;
2223                         n = '\\';
2224                         break;
2225                     }
2226                     n = (n << 4) | digit;
2227                 }
2228                 thisCh = (WCHAR)n;
2229                 break;
2230               case '0':
2231               case '1':
2232               case '2':
2233               case '3':
2234               case '4':
2235               case '5':
2236               case '6':
2237               case '7':
2238                 /*
2239                  *  This is a non-ECMA extension - decimal escapes (in this
2240                  *  case, octal!) are supposed to be an error inside class
2241                  *  ranges, but supported here for backwards compatibility.
2242                  */
2243                 n = JS7_UNDEC(c);
2244                 c = *src;
2245                 if ('0' <= c && c <= '7') {
2246                     src++;
2247                     n = 8 * n + JS7_UNDEC(c);
2248                     c = *src;
2249                     if ('0' <= c && c <= '7') {
2250                         src++;
2251                         i = 8 * n + JS7_UNDEC(c);
2252                         if (i <= 0377)
2253                             n = i;
2254                         else
2255                             src--;
2256                     }
2257                 }
2258                 thisCh = (WCHAR)n;
2259                 break;
2260
2261               case 'd':
2262                 AddCharacterRangeToCharSet(charSet, '0', '9');
2263                 continue;   /* don't need range processing */
2264               case 'D':
2265                 AddCharacterRangeToCharSet(charSet, 0, '0' - 1);
2266                 AddCharacterRangeToCharSet(charSet,
2267                                            (WCHAR)('9' + 1),
2268                                            (WCHAR)charSet->length);
2269                 continue;
2270               case 's':
2271                 for (i = (INT)charSet->length; i >= 0; i--)
2272                     if (isspaceW(i))
2273                         AddCharacterToCharSet(charSet, (WCHAR)i);
2274                 continue;
2275               case 'S':
2276                 for (i = (INT)charSet->length; i >= 0; i--)
2277                     if (!isspaceW(i))
2278                         AddCharacterToCharSet(charSet, (WCHAR)i);
2279                 continue;
2280               case 'w':
2281                 for (i = (INT)charSet->length; i >= 0; i--)
2282                     if (JS_ISWORD(i))
2283                         AddCharacterToCharSet(charSet, (WCHAR)i);
2284                 continue;
2285               case 'W':
2286                 for (i = (INT)charSet->length; i >= 0; i--)
2287                     if (!JS_ISWORD(i))
2288                         AddCharacterToCharSet(charSet, (WCHAR)i);
2289                 continue;
2290               default:
2291                 thisCh = c;
2292                 break;
2293
2294             }
2295             break;
2296
2297           default:
2298             thisCh = *src++;
2299             break;
2300
2301         }
2302         if (inRange) {
2303             if (gData->regexp->flags & JSREG_FOLD) {
2304                 int i;
2305
2306                 assert(rangeStart <= thisCh);
2307                 for (i = rangeStart; i <= thisCh; i++) {
2308                     WCHAR uch, dch;
2309
2310                     AddCharacterToCharSet(charSet, i);
2311                     uch = toupperW(i);
2312                     dch = tolowerW(i);
2313                     if (i != uch)
2314                         AddCharacterToCharSet(charSet, uch);
2315                     if (i != dch)
2316                         AddCharacterToCharSet(charSet, dch);
2317                 }
2318             } else {
2319                 AddCharacterRangeToCharSet(charSet, rangeStart, thisCh);
2320             }
2321             inRange = FALSE;
2322         } else {
2323             if (gData->regexp->flags & JSREG_FOLD) {
2324                 AddCharacterToCharSet(charSet, toupperW(thisCh));
2325                 AddCharacterToCharSet(charSet, tolowerW(thisCh));
2326             } else {
2327                 AddCharacterToCharSet(charSet, thisCh);
2328             }
2329             if (src < end - 1) {
2330                 if (*src == '-') {
2331                     ++src;
2332                     inRange = TRUE;
2333                     rangeStart = thisCh;
2334                 }
2335             }
2336         }
2337     }
2338     return TRUE;
2339 }
2340
2341 static BOOL
2342 ReallocStateStack(REGlobalData *gData)
2343 {
2344     size_t limit = gData->stateStackLimit;
2345     size_t sz = sizeof(REProgState) * limit;
2346
2347     gData->stateStack = jsheap_grow(gData->pool, gData->stateStack, sz, sz);
2348     if (!gData->stateStack) {
2349         js_ReportOutOfScriptQuota(gData->cx);
2350         gData->ok = FALSE;
2351         return FALSE;
2352     }
2353     gData->stateStackLimit = limit + limit;
2354     return TRUE;
2355 }
2356
2357 #define PUSH_STATE_STACK(data)                                                \
2358     do {                                                                      \
2359         ++(data)->stateStackTop;                                              \
2360         if ((data)->stateStackTop == (data)->stateStackLimit &&               \
2361             !ReallocStateStack((data))) {                                     \
2362             return NULL;                                                      \
2363         }                                                                     \
2364     }while(0)
2365
2366 /*
2367  * Apply the current op against the given input to see if it's going to match
2368  * or fail. Return false if we don't get a match, true if we do. If updatecp is
2369  * true, then update the current state's cp. Always update startpc to the next
2370  * op.
2371  */
2372 static inline REMatchState *
2373 SimpleMatch(REGlobalData *gData, REMatchState *x, REOp op,
2374             jsbytecode **startpc, BOOL updatecp)
2375 {
2376     REMatchState *result = NULL;
2377     WCHAR matchCh;
2378     size_t parenIndex;
2379     size_t offset, length, index;
2380     jsbytecode *pc = *startpc;  /* pc has already been incremented past op */
2381     WCHAR *source;
2382     const WCHAR *startcp = x->cp;
2383     WCHAR ch;
2384     RECharSet *charSet;
2385
2386     const char *opname = reop_names[op];
2387     TRACE("\n%06d: %*s%s\n", (int)(pc - gData->regexp->program),
2388           (int)gData->stateStackTop * 2, "", opname);
2389
2390     switch (op) {
2391       case REOP_EMPTY:
2392         result = x;
2393         break;
2394       case REOP_BOL:
2395         if (x->cp != gData->cpbegin) {
2396             if (/*!gData->cx->regExpStatics.multiline &&  FIXME !!! */
2397                 !(gData->regexp->flags & JSREG_MULTILINE)) {
2398                 break;
2399             }
2400             if (!RE_IS_LINE_TERM(x->cp[-1]))
2401                 break;
2402         }
2403         result = x;
2404         break;
2405       case REOP_EOL:
2406         if (x->cp != gData->cpend) {
2407             if (/*!gData->cx->regExpStatics.multiline &&*/
2408                 !(gData->regexp->flags & JSREG_MULTILINE)) {
2409                 break;
2410             }
2411             if (!RE_IS_LINE_TERM(*x->cp))
2412                 break;
2413         }
2414         result = x;
2415         break;
2416       case REOP_WBDRY:
2417         if ((x->cp == gData->cpbegin || !JS_ISWORD(x->cp[-1])) ^
2418             !(x->cp != gData->cpend && JS_ISWORD(*x->cp))) {
2419             result = x;
2420         }
2421         break;
2422       case REOP_WNONBDRY:
2423         if ((x->cp == gData->cpbegin || !JS_ISWORD(x->cp[-1])) ^
2424             (x->cp != gData->cpend && JS_ISWORD(*x->cp))) {
2425             result = x;
2426         }
2427         break;
2428       case REOP_DOT:
2429         if (x->cp != gData->cpend && !RE_IS_LINE_TERM(*x->cp)) {
2430             result = x;
2431             result->cp++;
2432         }
2433         break;
2434       case REOP_DIGIT:
2435         if (x->cp != gData->cpend && JS7_ISDEC(*x->cp)) {
2436             result = x;
2437             result->cp++;
2438         }
2439         break;
2440       case REOP_NONDIGIT:
2441         if (x->cp != gData->cpend && !JS7_ISDEC(*x->cp)) {
2442             result = x;
2443             result->cp++;
2444         }
2445         break;
2446       case REOP_ALNUM:
2447         if (x->cp != gData->cpend && JS_ISWORD(*x->cp)) {
2448             result = x;
2449             result->cp++;
2450         }
2451         break;
2452       case REOP_NONALNUM:
2453         if (x->cp != gData->cpend && !JS_ISWORD(*x->cp)) {
2454             result = x;
2455             result->cp++;
2456         }
2457         break;
2458       case REOP_SPACE:
2459         if (x->cp != gData->cpend && isspaceW(*x->cp)) {
2460             result = x;
2461             result->cp++;
2462         }
2463         break;
2464       case REOP_NONSPACE:
2465         if (x->cp != gData->cpend && !isspaceW(*x->cp)) {
2466             result = x;
2467             result->cp++;
2468         }
2469         break;
2470       case REOP_BACKREF:
2471         pc = ReadCompactIndex(pc, &parenIndex);
2472         assert(parenIndex < gData->regexp->parenCount);
2473         result = BackrefMatcher(gData, x, parenIndex);
2474         break;
2475       case REOP_FLAT:
2476         pc = ReadCompactIndex(pc, &offset);
2477         assert(offset < SysStringLen(gData->regexp->source));
2478         pc = ReadCompactIndex(pc, &length);
2479         assert(1 <= length);
2480         assert(length <= SysStringLen(gData->regexp->source) - offset);
2481         if (length <= (size_t)(gData->cpend - x->cp)) {
2482             source = gData->regexp->source + offset;
2483             TRACE("%s\n", debugstr_wn(source, length));
2484             for (index = 0; index != length; index++) {
2485                 if (source[index] != x->cp[index])
2486                     return NULL;
2487             }
2488             x->cp += length;
2489             result = x;
2490         }
2491         break;
2492       case REOP_FLAT1:
2493         matchCh = *pc++;
2494         TRACE(" '%c' == '%c'\n", (char)matchCh, (char)*x->cp);
2495         if (x->cp != gData->cpend && *x->cp == matchCh) {
2496             result = x;
2497             result->cp++;
2498         }
2499         break;
2500       case REOP_FLATi:
2501         pc = ReadCompactIndex(pc, &offset);
2502         assert(offset < SysStringLen(gData->regexp->source));
2503         pc = ReadCompactIndex(pc, &length);
2504         assert(1 <= length);
2505         assert(length <= SysStringLen(gData->regexp->source) - offset);
2506         source = gData->regexp->source;
2507         result = FlatNIMatcher(gData, x, source + offset, length);
2508         break;
2509       case REOP_FLAT1i:
2510         matchCh = *pc++;
2511         if (x->cp != gData->cpend && toupperW(*x->cp) == toupperW(matchCh)) {
2512             result = x;
2513             result->cp++;
2514         }
2515         break;
2516       case REOP_UCFLAT1:
2517         matchCh = GET_ARG(pc);
2518         TRACE(" '%c' == '%c'\n", (char)matchCh, (char)*x->cp);
2519         pc += ARG_LEN;
2520         if (x->cp != gData->cpend && *x->cp == matchCh) {
2521             result = x;
2522             result->cp++;
2523         }
2524         break;
2525       case REOP_UCFLAT1i:
2526         matchCh = GET_ARG(pc);
2527         pc += ARG_LEN;
2528         if (x->cp != gData->cpend && toupperW(*x->cp) == toupperW(matchCh)) {
2529             result = x;
2530             result->cp++;
2531         }
2532         break;
2533       case REOP_CLASS:
2534         pc = ReadCompactIndex(pc, &index);
2535         assert(index < gData->regexp->classCount);
2536         if (x->cp != gData->cpend) {
2537             charSet = &gData->regexp->classList[index];
2538             assert(charSet->converted);
2539             ch = *x->cp;
2540             index = ch >> 3;
2541             if (charSet->length != 0 &&
2542                 ch <= charSet->length &&
2543                 (charSet->u.bits[index] & (1 << (ch & 0x7)))) {
2544                 result = x;
2545                 result->cp++;
2546             }
2547         }
2548         break;
2549       case REOP_NCLASS:
2550         pc = ReadCompactIndex(pc, &index);
2551         assert(index < gData->regexp->classCount);
2552         if (x->cp != gData->cpend) {
2553             charSet = &gData->regexp->classList[index];
2554             assert(charSet->converted);
2555             ch = *x->cp;
2556             index = ch >> 3;
2557             if (charSet->length == 0 ||
2558                 ch > charSet->length ||
2559                 !(charSet->u.bits[index] & (1 << (ch & 0x7)))) {
2560                 result = x;
2561                 result->cp++;
2562             }
2563         }
2564         break;
2565
2566       default:
2567         assert(FALSE);
2568     }
2569     if (result) {
2570         if (!updatecp)
2571             x->cp = startcp;
2572         *startpc = pc;
2573         TRACE(" *\n");
2574         return result;
2575     }
2576     x->cp = startcp;
2577     return NULL;
2578 }
2579
2580 static inline REMatchState *
2581 ExecuteREBytecode(REGlobalData *gData, REMatchState *x)
2582 {
2583     REMatchState *result = NULL;
2584     REBackTrackData *backTrackData;
2585     jsbytecode *nextpc, *testpc;
2586     REOp nextop;
2587     RECapture *cap;
2588     REProgState *curState;
2589     const WCHAR *startcp;
2590     size_t parenIndex, k;
2591     size_t parenSoFar = 0;
2592
2593     WCHAR matchCh1, matchCh2;
2594     RECharSet *charSet;
2595
2596     BOOL anchor;
2597     jsbytecode *pc = gData->regexp->program;
2598     REOp op = (REOp) *pc++;
2599
2600     /*
2601      * If the first node is a simple match, step the index into the string
2602      * until that match is made, or fail if it can't be found at all.
2603      */
2604     if (REOP_IS_SIMPLE(op) && !(gData->regexp->flags & JSREG_STICKY)) {
2605         anchor = FALSE;
2606         while (x->cp <= gData->cpend) {
2607             nextpc = pc;    /* reset back to start each time */
2608             result = SimpleMatch(gData, x, op, &nextpc, TRUE);
2609             if (result) {
2610                 anchor = TRUE;
2611                 x = result;
2612                 pc = nextpc;    /* accept skip to next opcode */
2613                 op = (REOp) *pc++;
2614                 assert(op < REOP_LIMIT);
2615                 break;
2616             }
2617             gData->skipped++;
2618             x->cp++;
2619         }
2620         if (!anchor)
2621             goto bad;
2622     }
2623
2624     for (;;) {
2625         const char *opname = reop_names[op];
2626         TRACE("\n%06d: %*s%s\n", (int)(pc - gData->regexp->program),
2627               (int)gData->stateStackTop * 2, "", opname);
2628
2629         if (REOP_IS_SIMPLE(op)) {
2630             result = SimpleMatch(gData, x, op, &pc, TRUE);
2631         } else {
2632             curState = &gData->stateStack[gData->stateStackTop];
2633             switch (op) {
2634               case REOP_END:
2635                 goto good;
2636               case REOP_ALTPREREQ2:
2637                 nextpc = pc + GET_OFFSET(pc);   /* start of next op */
2638                 pc += ARG_LEN;
2639                 matchCh2 = GET_ARG(pc);
2640                 pc += ARG_LEN;
2641                 k = GET_ARG(pc);
2642                 pc += ARG_LEN;
2643
2644                 if (x->cp != gData->cpend) {
2645                     if (*x->cp == matchCh2)
2646                         goto doAlt;
2647
2648                     charSet = &gData->regexp->classList[k];
2649                     if (!charSet->converted && !ProcessCharSet(gData, charSet))
2650                         goto bad;
2651                     matchCh1 = *x->cp;
2652                     k = matchCh1 >> 3;
2653                     if ((charSet->length == 0 ||
2654                          matchCh1 > charSet->length ||
2655                          !(charSet->u.bits[k] & (1 << (matchCh1 & 0x7)))) ^
2656                         charSet->sense) {
2657                         goto doAlt;
2658                     }
2659                 }
2660                 result = NULL;
2661                 break;
2662
2663               case REOP_ALTPREREQ:
2664                 nextpc = pc + GET_OFFSET(pc);   /* start of next op */
2665                 pc += ARG_LEN;
2666                 matchCh1 = GET_ARG(pc);
2667                 pc += ARG_LEN;
2668                 matchCh2 = GET_ARG(pc);
2669                 pc += ARG_LEN;
2670                 if (x->cp == gData->cpend ||
2671                     (*x->cp != matchCh1 && *x->cp != matchCh2)) {
2672                     result = NULL;
2673                     break;
2674                 }
2675                 /* else false thru... */
2676
2677               case REOP_ALT:
2678               doAlt:
2679                 nextpc = pc + GET_OFFSET(pc);   /* start of next alternate */
2680                 pc += ARG_LEN;                  /* start of this alternate */
2681                 curState->parenSoFar = parenSoFar;
2682                 PUSH_STATE_STACK(gData);
2683                 op = (REOp) *pc++;
2684                 startcp = x->cp;
2685                 if (REOP_IS_SIMPLE(op)) {
2686                     if (!SimpleMatch(gData, x, op, &pc, TRUE)) {
2687                         op = (REOp) *nextpc++;
2688                         pc = nextpc;
2689                         continue;
2690                     }
2691                     result = x;
2692                     op = (REOp) *pc++;
2693                 }
2694                 nextop = (REOp) *nextpc++;
2695                 if (!PushBackTrackState(gData, nextop, nextpc, x, startcp, 0, 0))
2696                     goto bad;
2697                 continue;
2698
2699               /*
2700                * Occurs at (successful) end of REOP_ALT,
2701                */
2702               case REOP_JUMP:
2703                 /*
2704                  * If we have not gotten a result here, it is because of an
2705                  * empty match.  Do the same thing REOP_EMPTY would do.
2706                  */
2707                 if (!result)
2708                     result = x;
2709
2710                 --gData->stateStackTop;
2711                 pc += GET_OFFSET(pc);
2712                 op = (REOp) *pc++;
2713                 continue;
2714
2715               /*
2716                * Occurs at last (successful) end of REOP_ALT,
2717                */
2718               case REOP_ENDALT:
2719                 /*
2720                  * If we have not gotten a result here, it is because of an
2721                  * empty match.  Do the same thing REOP_EMPTY would do.
2722                  */
2723                 if (!result)
2724                     result = x;
2725
2726                 --gData->stateStackTop;
2727                 op = (REOp) *pc++;
2728                 continue;
2729
2730               case REOP_LPAREN:
2731                 pc = ReadCompactIndex(pc, &parenIndex);
2732                 TRACE("[ %lu ]\n", (unsigned long) parenIndex);
2733                 assert(parenIndex < gData->regexp->parenCount);
2734                 if (parenIndex + 1 > parenSoFar)
2735                     parenSoFar = parenIndex + 1;
2736                 x->parens[parenIndex].index = x->cp - gData->cpbegin;
2737                 x->parens[parenIndex].length = 0;
2738                 op = (REOp) *pc++;
2739                 continue;
2740
2741               case REOP_RPAREN:
2742               {
2743                 ptrdiff_t delta;
2744
2745                 pc = ReadCompactIndex(pc, &parenIndex);
2746                 assert(parenIndex < gData->regexp->parenCount);
2747                 cap = &x->parens[parenIndex];
2748                 delta = x->cp - (gData->cpbegin + cap->index);
2749                 cap->length = (delta < 0) ? 0 : (size_t) delta;
2750                 op = (REOp) *pc++;
2751
2752                 if (!result)
2753                     result = x;
2754                 continue;
2755               }
2756               case REOP_ASSERT:
2757                 nextpc = pc + GET_OFFSET(pc);  /* start of term after ASSERT */
2758                 pc += ARG_LEN;                 /* start of ASSERT child */
2759                 op = (REOp) *pc++;
2760                 testpc = pc;
2761                 if (REOP_IS_SIMPLE(op) &&
2762                     !SimpleMatch(gData, x, op, &testpc, FALSE)) {
2763                     result = NULL;
2764                     break;
2765                 }
2766                 curState->u.assertion.top =
2767                     (char *)gData->backTrackSP - (char *)gData->backTrackStack;
2768                 curState->u.assertion.sz = gData->cursz;
2769                 curState->index = x->cp - gData->cpbegin;
2770                 curState->parenSoFar = parenSoFar;
2771                 PUSH_STATE_STACK(gData);
2772                 if (!PushBackTrackState(gData, REOP_ASSERTTEST,
2773                                         nextpc, x, x->cp, 0, 0)) {
2774                     goto bad;
2775                 }
2776                 continue;
2777
2778               case REOP_ASSERT_NOT:
2779                 nextpc = pc + GET_OFFSET(pc);
2780                 pc += ARG_LEN;
2781                 op = (REOp) *pc++;
2782                 testpc = pc;
2783                 if (REOP_IS_SIMPLE(op) /* Note - fail to fail! */ &&
2784                     SimpleMatch(gData, x, op, &testpc, FALSE) &&
2785                     *testpc == REOP_ASSERTNOTTEST) {
2786                     result = NULL;
2787                     break;
2788                 }
2789                 curState->u.assertion.top
2790                     = (char *)gData->backTrackSP -
2791                       (char *)gData->backTrackStack;
2792                 curState->u.assertion.sz = gData->cursz;
2793                 curState->index = x->cp - gData->cpbegin;
2794                 curState->parenSoFar = parenSoFar;
2795                 PUSH_STATE_STACK(gData);
2796                 if (!PushBackTrackState(gData, REOP_ASSERTNOTTEST,
2797                                         nextpc, x, x->cp, 0, 0)) {
2798                     goto bad;
2799                 }
2800                 continue;
2801
2802               case REOP_ASSERTTEST:
2803                 --gData->stateStackTop;
2804                 --curState;
2805                 x->cp = gData->cpbegin + curState->index;
2806                 gData->backTrackSP =
2807                     (REBackTrackData *) ((char *)gData->backTrackStack +
2808                                          curState->u.assertion.top);
2809                 gData->cursz = curState->u.assertion.sz;
2810                 if (result)
2811                     result = x;
2812                 break;
2813
2814               case REOP_ASSERTNOTTEST:
2815                 --gData->stateStackTop;
2816                 --curState;
2817                 x->cp = gData->cpbegin + curState->index;
2818                 gData->backTrackSP =
2819                     (REBackTrackData *) ((char *)gData->backTrackStack +
2820                                          curState->u.assertion.top);
2821                 gData->cursz = curState->u.assertion.sz;
2822                 result = (!result) ? x : NULL;
2823                 break;
2824               case REOP_STAR:
2825                 curState->u.quantifier.min = 0;
2826                 curState->u.quantifier.max = (UINT)-1;
2827                 goto quantcommon;
2828               case REOP_PLUS:
2829                 curState->u.quantifier.min = 1;
2830                 curState->u.quantifier.max = (UINT)-1;
2831                 goto quantcommon;
2832               case REOP_OPT:
2833                 curState->u.quantifier.min = 0;
2834                 curState->u.quantifier.max = 1;
2835                 goto quantcommon;
2836               case REOP_QUANT:
2837                 pc = ReadCompactIndex(pc, &k);
2838                 curState->u.quantifier.min = k;
2839                 pc = ReadCompactIndex(pc, &k);
2840                 /* max is k - 1 to use one byte for (UINT)-1 sentinel. */
2841                 curState->u.quantifier.max = k - 1;
2842                 assert(curState->u.quantifier.min <= curState->u.quantifier.max);
2843               quantcommon:
2844                 if (curState->u.quantifier.max == 0) {
2845                     pc = pc + GET_OFFSET(pc);
2846                     op = (REOp) *pc++;
2847                     result = x;
2848                     continue;
2849                 }
2850                 /* Step over <next> */
2851                 nextpc = pc + ARG_LEN;
2852                 op = (REOp) *nextpc++;
2853                 startcp = x->cp;
2854                 if (REOP_IS_SIMPLE(op)) {
2855                     if (!SimpleMatch(gData, x, op, &nextpc, TRUE)) {
2856                         if (curState->u.quantifier.min == 0)
2857                             result = x;
2858                         else
2859                             result = NULL;
2860                         pc = pc + GET_OFFSET(pc);
2861                         break;
2862                     }
2863                     op = (REOp) *nextpc++;
2864                     result = x;
2865                 }
2866                 curState->index = startcp - gData->cpbegin;
2867                 curState->continue_op = REOP_REPEAT;
2868                 curState->continue_pc = pc;
2869                 curState->parenSoFar = parenSoFar;
2870                 PUSH_STATE_STACK(gData);
2871                 if (curState->u.quantifier.min == 0 &&
2872                     !PushBackTrackState(gData, REOP_REPEAT, pc, x, startcp,
2873                                         0, 0)) {
2874                     goto bad;
2875                 }
2876                 pc = nextpc;
2877                 continue;
2878
2879               case REOP_ENDCHILD: /* marks the end of a quantifier child */
2880                 pc = curState[-1].continue_pc;
2881                 op = (REOp) curState[-1].continue_op;
2882
2883                 if (!result)
2884                     result = x;
2885                 continue;
2886
2887               case REOP_REPEAT:
2888                 --curState;
2889                 do {
2890                     --gData->stateStackTop;
2891                     if (!result) {
2892                         /* Failed, see if we have enough children. */
2893                         if (curState->u.quantifier.min == 0)
2894                             goto repeatDone;
2895                         goto break_switch;
2896                     }
2897                     if (curState->u.quantifier.min == 0 &&
2898                         x->cp == gData->cpbegin + curState->index) {
2899                         /* matched an empty string, that'll get us nowhere */
2900                         result = NULL;
2901                         goto break_switch;
2902                     }
2903                     if (curState->u.quantifier.min != 0)
2904                         curState->u.quantifier.min--;
2905                     if (curState->u.quantifier.max != (UINT) -1)
2906                         curState->u.quantifier.max--;
2907                     if (curState->u.quantifier.max == 0)
2908                         goto repeatDone;
2909                     nextpc = pc + ARG_LEN;
2910                     nextop = (REOp) *nextpc;
2911                     startcp = x->cp;
2912                     if (REOP_IS_SIMPLE(nextop)) {
2913                         nextpc++;
2914                         if (!SimpleMatch(gData, x, nextop, &nextpc, TRUE)) {
2915                             if (curState->u.quantifier.min == 0)
2916                                 goto repeatDone;
2917                             result = NULL;
2918                             goto break_switch;
2919                         }
2920                         result = x;
2921                     }
2922                     curState->index = startcp - gData->cpbegin;
2923                     PUSH_STATE_STACK(gData);
2924                     if (curState->u.quantifier.min == 0 &&
2925                         !PushBackTrackState(gData, REOP_REPEAT,
2926                                             pc, x, startcp,
2927                                             curState->parenSoFar,
2928                                             parenSoFar -
2929                                             curState->parenSoFar)) {
2930                         goto bad;
2931                     }
2932                 } while (*nextpc == REOP_ENDCHILD);
2933                 pc = nextpc;
2934                 op = (REOp) *pc++;
2935                 parenSoFar = curState->parenSoFar;
2936                 continue;
2937
2938               repeatDone:
2939                 result = x;
2940                 pc += GET_OFFSET(pc);
2941                 goto break_switch;
2942
2943               case REOP_MINIMALSTAR:
2944                 curState->u.quantifier.min = 0;
2945                 curState->u.quantifier.max = (UINT)-1;
2946                 goto minimalquantcommon;
2947               case REOP_MINIMALPLUS:
2948                 curState->u.quantifier.min = 1;
2949                 curState->u.quantifier.max = (UINT)-1;
2950                 goto minimalquantcommon;
2951               case REOP_MINIMALOPT:
2952                 curState->u.quantifier.min = 0;
2953                 curState->u.quantifier.max = 1;
2954                 goto minimalquantcommon;
2955               case REOP_MINIMALQUANT:
2956                 pc = ReadCompactIndex(pc, &k);
2957                 curState->u.quantifier.min = k;
2958                 pc = ReadCompactIndex(pc, &k);
2959                 /* See REOP_QUANT comments about k - 1. */
2960                 curState->u.quantifier.max = k - 1;
2961                 assert(curState->u.quantifier.min
2962                           <= curState->u.quantifier.max);
2963               minimalquantcommon:
2964                 curState->index = x->cp - gData->cpbegin;
2965                 curState->parenSoFar = parenSoFar;
2966                 PUSH_STATE_STACK(gData);
2967                 if (curState->u.quantifier.min != 0) {
2968                     curState->continue_op = REOP_MINIMALREPEAT;
2969                     curState->continue_pc = pc;
2970                     /* step over <next> */
2971                     pc += OFFSET_LEN;
2972                     op = (REOp) *pc++;
2973                 } else {
2974                     if (!PushBackTrackState(gData, REOP_MINIMALREPEAT,
2975                                             pc, x, x->cp, 0, 0)) {
2976                         goto bad;
2977                     }
2978                     --gData->stateStackTop;
2979                     pc = pc + GET_OFFSET(pc);
2980                     op = (REOp) *pc++;
2981                 }
2982                 continue;
2983
2984               case REOP_MINIMALREPEAT:
2985                 --gData->stateStackTop;
2986                 --curState;
2987
2988                 TRACE("{%d,%d}\n", curState->u.quantifier.min, curState->u.quantifier.max);
2989 #define PREPARE_REPEAT()                                                      \
2990     do {                                                                      \
2991         curState->index = x->cp - gData->cpbegin;                             \
2992         curState->continue_op = REOP_MINIMALREPEAT;                           \
2993         curState->continue_pc = pc;                                           \
2994         pc += ARG_LEN;                                                        \
2995         for (k = curState->parenSoFar; k < parenSoFar; k++)                   \
2996             x->parens[k].index = -1;                                          \
2997         PUSH_STATE_STACK(gData);                                              \
2998         op = (REOp) *pc++;                                                    \
2999         assert(op < REOP_LIMIT);                                              \
3000     }while(0)
3001
3002                 if (!result) {
3003                     TRACE(" -\n");
3004                     /*
3005                      * Non-greedy failure - try to consume another child.
3006                      */
3007                     if (curState->u.quantifier.max == (UINT) -1 ||
3008                         curState->u.quantifier.max > 0) {
3009                         PREPARE_REPEAT();
3010                         continue;
3011                     }
3012                     /* Don't need to adjust pc since we're going to pop. */
3013                     break;
3014                 }
3015                 if (curState->u.quantifier.min == 0 &&
3016                     x->cp == gData->cpbegin + curState->index) {
3017                     /* Matched an empty string, that'll get us nowhere. */
3018                     result = NULL;
3019                     break;
3020                 }
3021                 if (curState->u.quantifier.min != 0)
3022                     curState->u.quantifier.min--;
3023                 if (curState->u.quantifier.max != (UINT) -1)
3024                     curState->u.quantifier.max--;
3025                 if (curState->u.quantifier.min != 0) {
3026                     PREPARE_REPEAT();
3027                     continue;
3028                 }
3029                 curState->index = x->cp - gData->cpbegin;
3030                 curState->parenSoFar = parenSoFar;
3031                 PUSH_STATE_STACK(gData);
3032                 if (!PushBackTrackState(gData, REOP_MINIMALREPEAT,
3033                                         pc, x, x->cp,
3034                                         curState->parenSoFar,
3035                                         parenSoFar - curState->parenSoFar)) {
3036                     goto bad;
3037                 }
3038                 --gData->stateStackTop;
3039                 pc = pc + GET_OFFSET(pc);
3040                 op = (REOp) *pc++;
3041                 assert(op < REOP_LIMIT);
3042                 continue;
3043               default:
3044                 assert(FALSE);
3045                 result = NULL;
3046             }
3047           break_switch:;
3048         }
3049
3050         /*
3051          *  If the match failed and there's a backtrack option, take it.
3052          *  Otherwise this is a complete and utter failure.
3053          */
3054         if (!result) {
3055             if (gData->cursz == 0)
3056                 return NULL;
3057
3058             /* Potentially detect explosive regex here. */
3059             gData->backTrackCount++;
3060             if (gData->backTrackLimit &&
3061                 gData->backTrackCount >= gData->backTrackLimit) {
3062                 JS_ReportErrorNumber(gData->cx, js_GetErrorMessage, NULL,
3063                                      JSMSG_REGEXP_TOO_COMPLEX);
3064                 gData->ok = FALSE;
3065                 return NULL;
3066             }
3067
3068             backTrackData = gData->backTrackSP;
3069             gData->cursz = backTrackData->sz;
3070             gData->backTrackSP =
3071                 (REBackTrackData *) ((char *)backTrackData - backTrackData->sz);
3072             x->cp = backTrackData->cp;
3073             pc = backTrackData->backtrack_pc;
3074             op = (REOp) backTrackData->backtrack_op;
3075             assert(op < REOP_LIMIT);
3076             gData->stateStackTop = backTrackData->saveStateStackTop;
3077             assert(gData->stateStackTop);
3078
3079             memcpy(gData->stateStack, backTrackData + 1,
3080                    sizeof(REProgState) * backTrackData->saveStateStackTop);
3081             curState = &gData->stateStack[gData->stateStackTop - 1];
3082
3083             if (backTrackData->parenCount) {
3084                 memcpy(&x->parens[backTrackData->parenIndex],
3085                        (char *)(backTrackData + 1) +
3086                        sizeof(REProgState) * backTrackData->saveStateStackTop,
3087                        sizeof(RECapture) * backTrackData->parenCount);
3088                 parenSoFar = backTrackData->parenIndex + backTrackData->parenCount;
3089             } else {
3090                 for (k = curState->parenSoFar; k < parenSoFar; k++)
3091                     x->parens[k].index = -1;
3092                 parenSoFar = curState->parenSoFar;
3093             }
3094
3095             TRACE("\tBT_Pop: %ld,%ld\n",
3096                      (unsigned long) backTrackData->parenIndex,
3097                      (unsigned long) backTrackData->parenCount);
3098             continue;
3099         }
3100         x = result;
3101
3102         /*
3103          *  Continue with the expression.
3104          */
3105         op = (REOp)*pc++;
3106         assert(op < REOP_LIMIT);
3107     }
3108
3109 bad:
3110     TRACE("\n");
3111     return NULL;
3112
3113 good:
3114     TRACE("\n");
3115     return x;
3116 }
3117
3118 static REMatchState *MatchRegExp(REGlobalData *gData, REMatchState *x)
3119 {
3120     REMatchState *result;
3121     const WCHAR *cp = x->cp;
3122     const WCHAR *cp2;
3123     UINT j;
3124
3125     /*
3126      * Have to include the position beyond the last character
3127      * in order to detect end-of-input/line condition.
3128      */
3129     for (cp2 = cp; cp2 <= gData->cpend; cp2++) {
3130         gData->skipped = cp2 - cp;
3131         x->cp = cp2;
3132         for (j = 0; j < gData->regexp->parenCount; j++)
3133             x->parens[j].index = -1;
3134         result = ExecuteREBytecode(gData, x);
3135         if (!gData->ok || result || (gData->regexp->flags & JSREG_STICKY))
3136             return result;
3137         gData->backTrackSP = gData->backTrackStack;
3138         gData->cursz = 0;
3139         gData->stateStackTop = 0;
3140         cp2 = cp + gData->skipped;
3141     }
3142     return NULL;
3143 }
3144
3145 #define MIN_BACKTRACK_LIMIT 400000
3146
3147 static REMatchState *InitMatch(script_ctx_t *cx, REGlobalData *gData, JSRegExp *re, size_t length)
3148 {
3149     REMatchState *result;
3150     UINT i;
3151
3152     gData->backTrackStackSize = INITIAL_BACKTRACK;
3153     gData->backTrackStack = jsheap_alloc(gData->pool, INITIAL_BACKTRACK);
3154     if (!gData->backTrackStack)
3155         goto bad;
3156
3157     gData->backTrackSP = gData->backTrackStack;
3158     gData->cursz = 0;
3159     gData->backTrackCount = 0;
3160     gData->backTrackLimit = 0;
3161
3162     gData->stateStackLimit = INITIAL_STATESTACK;
3163     gData->stateStack = jsheap_alloc(gData->pool, sizeof(REProgState) * INITIAL_STATESTACK);
3164     if (!gData->stateStack)
3165         goto bad;
3166
3167     gData->stateStackTop = 0;
3168     gData->cx = cx;
3169     gData->regexp = re;
3170     gData->ok = TRUE;
3171
3172     result = jsheap_alloc(gData->pool, offsetof(REMatchState, parens) + re->parenCount * sizeof(RECapture));
3173     if (!result)
3174         goto bad;
3175
3176     for (i = 0; i < re->classCount; i++) {
3177         if (!re->classList[i].converted &&
3178             !ProcessCharSet(gData, &re->classList[i])) {
3179             return NULL;
3180         }
3181     }
3182
3183     return result;
3184
3185 bad:
3186     js_ReportOutOfScriptQuota(cx);
3187     gData->ok = FALSE;
3188     return NULL;
3189 }
3190
3191 static void
3192 js_DestroyRegExp(JSRegExp *re)
3193 {
3194     if (re->classList) {
3195         UINT i;
3196         for (i = 0; i < re->classCount; i++) {
3197             if (re->classList[i].converted)
3198                 heap_free(re->classList[i].u.bits);
3199             re->classList[i].u.bits = NULL;
3200         }
3201         heap_free(re->classList);
3202     }
3203     heap_free(re);
3204 }
3205
3206 static JSRegExp *
3207 js_NewRegExp(script_ctx_t *cx, BSTR str, UINT flags, BOOL flat)
3208 {
3209     JSRegExp *re;
3210     jsheap_t *mark;
3211     CompilerState state;
3212     size_t resize;
3213     jsbytecode *endPC;
3214     UINT i;
3215     size_t len;
3216
3217     re = NULL;
3218     mark = jsheap_mark(&cx->tmp_heap);
3219     len = SysStringLen(str);
3220
3221     state.context = cx;
3222     state.cp = str;
3223     if (!state.cp)
3224         goto out;
3225     state.cpbegin = state.cp;
3226     state.cpend = state.cp + len;
3227     state.flags = flags;
3228     state.parenCount = 0;
3229     state.classCount = 0;
3230     state.progLength = 0;
3231     state.treeDepth = 0;
3232     state.classBitmapsMem = 0;
3233     for (i = 0; i < CLASS_CACHE_SIZE; i++)
3234         state.classCache[i].start = NULL;
3235
3236     if (len != 0 && flat) {
3237         state.result = NewRENode(&state, REOP_FLAT);
3238         if (!state.result)
3239             goto out;
3240         state.result->u.flat.chr = *state.cpbegin;
3241         state.result->u.flat.length = len;
3242         state.result->kid = (void *) state.cpbegin;
3243         /* Flat bytecode: REOP_FLAT compact(string_offset) compact(len). */
3244         state.progLength += 1 + GetCompactIndexWidth(0)
3245                           + GetCompactIndexWidth(len);
3246     } else {
3247         if (!ParseRegExp(&state))
3248             goto out;
3249     }
3250     resize = offsetof(JSRegExp, program) + state.progLength + 1;
3251     re = heap_alloc(resize);
3252     if (!re)
3253         goto out;
3254
3255     assert(state.classBitmapsMem <= CLASS_BITMAPS_MEM_LIMIT);
3256     re->classCount = state.classCount;
3257     if (re->classCount) {
3258         re->classList = heap_alloc(re->classCount * sizeof(RECharSet));
3259         if (!re->classList) {
3260             js_DestroyRegExp(re);
3261             re = NULL;
3262             goto out;
3263         }
3264         for (i = 0; i < re->classCount; i++)
3265             re->classList[i].converted = FALSE;
3266     } else {
3267         re->classList = NULL;
3268     }
3269     endPC = EmitREBytecode(&state, re, state.treeDepth, re->program, state.result);
3270     if (!endPC) {
3271         js_DestroyRegExp(re);
3272         re = NULL;
3273         goto out;
3274     }
3275     *endPC++ = REOP_END;
3276     /*
3277      * Check whether size was overestimated and shrink using realloc.
3278      * This is safe since no pointers to newly parsed regexp or its parts
3279      * besides re exist here.
3280      */
3281     if ((size_t)(endPC - re->program) != state.progLength + 1) {
3282         JSRegExp *tmp;
3283         assert((size_t)(endPC - re->program) < state.progLength + 1);
3284         resize = offsetof(JSRegExp, program) + (endPC - re->program);
3285         tmp = heap_realloc(re, resize);
3286         if (tmp)
3287             re = tmp;
3288     }
3289
3290     re->flags = flags;
3291     re->parenCount = state.parenCount;
3292     re->source = str;
3293
3294 out:
3295     jsheap_clear(mark);
3296     return re;
3297 }
3298
3299 static inline RegExpInstance *regexp_from_vdisp(vdisp_t *vdisp)
3300 {
3301     return (RegExpInstance*)vdisp->u.jsdisp;
3302 }
3303
3304 static void set_last_index(RegExpInstance *This, DWORD last_index)
3305 {
3306     This->last_index = last_index;
3307     VariantClear(&This->last_index_var);
3308     num_set_val(&This->last_index_var, last_index);
3309 }
3310
3311 static HRESULT do_regexp_match_next(script_ctx_t *ctx, RegExpInstance *regexp, DWORD rem_flags,
3312         const WCHAR *str, DWORD len, const WCHAR **cp, match_result_t **parens, DWORD *parens_size,
3313         DWORD *parens_cnt, match_result_t *ret)
3314 {
3315     REMatchState *x, *result;
3316     REGlobalData gData;
3317     DWORD matchlen;
3318
3319     gData.cpbegin = *cp;
3320     gData.cpend = str + len;
3321     gData.start = *cp-str;
3322     gData.skipped = 0;
3323     gData.pool = &ctx->tmp_heap;
3324
3325     x = InitMatch(NULL, &gData, regexp->jsregexp, gData.cpend - gData.cpbegin);
3326     if(!x) {
3327         WARN("InitMatch failed\n");
3328         return E_FAIL;
3329     }
3330
3331     x->cp = *cp;
3332     result = MatchRegExp(&gData, x);
3333     if(!gData.ok) {
3334         WARN("MatchRegExp failed\n");
3335         return E_FAIL;
3336     }
3337
3338     if(!result) {
3339         if(rem_flags & REM_RESET_INDEX)
3340             set_last_index(regexp, 0);
3341         return S_FALSE;
3342     }
3343
3344     if(parens) {
3345         DWORD i;
3346
3347         if(regexp->jsregexp->parenCount > *parens_size) {
3348             match_result_t *new_parens;
3349
3350             if(*parens)
3351                 new_parens = heap_realloc(*parens, sizeof(match_result_t)*regexp->jsregexp->parenCount);
3352             else
3353                 new_parens = heap_alloc(sizeof(match_result_t)*regexp->jsregexp->parenCount);
3354             if(!new_parens)
3355                 return E_OUTOFMEMORY;
3356
3357             *parens = new_parens;
3358         }
3359
3360         *parens_cnt = regexp->jsregexp->parenCount;
3361
3362         for(i=0; i < regexp->jsregexp->parenCount; i++) {
3363             if(result->parens[i].index == -1) {
3364                 (*parens)[i].str = NULL;
3365                 (*parens)[i].len = 0;
3366             }else {
3367                 (*parens)[i].str = *cp + result->parens[i].index;
3368                 (*parens)[i].len = result->parens[i].length;
3369             }
3370         }
3371     }
3372
3373     matchlen = (result->cp-*cp) - gData.skipped;
3374     *cp = result->cp;
3375     ret->str = result->cp-matchlen;
3376     ret->len = matchlen;
3377     set_last_index(regexp, result->cp-str);
3378
3379     return S_OK;
3380 }
3381
3382 HRESULT regexp_match_next(script_ctx_t *ctx, DispatchEx *dispex, DWORD rem_flags, const WCHAR *str,
3383         DWORD len, const WCHAR **cp, match_result_t **parens, DWORD *parens_size, DWORD *parens_cnt,
3384         match_result_t *ret)
3385 {
3386     RegExpInstance *regexp = (RegExpInstance*)dispex;
3387     jsheap_t *mark;
3388     HRESULT hres;
3389
3390     if((rem_flags & REM_CHECK_GLOBAL) && !(regexp->jsregexp->flags & JSREG_GLOB))
3391         return S_FALSE;
3392
3393     mark = jsheap_mark(&ctx->tmp_heap);
3394
3395     hres = do_regexp_match_next(ctx, regexp, rem_flags, str, len, cp, parens, parens_size, parens_cnt, ret);
3396
3397     jsheap_clear(mark);
3398     return hres;
3399 }
3400
3401 HRESULT regexp_match(script_ctx_t *ctx, DispatchEx *dispex, const WCHAR *str, DWORD len, BOOL gflag,
3402         match_result_t **match_result, DWORD *result_cnt)
3403 {
3404     RegExpInstance *This = (RegExpInstance*)dispex;
3405     match_result_t *ret = NULL, cres;
3406     const WCHAR *cp = str;
3407     DWORD i=0, ret_size = 0;
3408     jsheap_t *mark;
3409     HRESULT hres;
3410
3411     mark = jsheap_mark(&ctx->tmp_heap);
3412
3413     while(1) {
3414         hres = do_regexp_match_next(ctx, This, 0, str, len, &cp, NULL, NULL, NULL, &cres);
3415         if(hres == S_FALSE) {
3416             hres = S_OK;
3417             break;
3418         }
3419
3420         if(FAILED(hres))
3421             break;
3422
3423         if(ret_size == i) {
3424             if(ret)
3425                 ret = heap_realloc(ret, (ret_size <<= 1) * sizeof(match_result_t));
3426             else
3427                 ret = heap_alloc((ret_size=4) * sizeof(match_result_t));
3428             if(!ret) {
3429                 hres = E_OUTOFMEMORY;
3430                 break;
3431             }
3432         }
3433
3434         ret[i++] = cres;
3435
3436         if(!gflag && !(This->jsregexp->flags & JSREG_GLOB)) {
3437             hres = S_OK;
3438             break;
3439         }
3440     }
3441
3442     jsheap_clear(mark);
3443     if(FAILED(hres)) {
3444         heap_free(ret);
3445         return hres;
3446     }
3447
3448     *match_result = ret;
3449     *result_cnt = i;
3450     return S_OK;
3451 }
3452
3453 static HRESULT RegExp_source(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
3454         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
3455 {
3456     TRACE("\n");
3457
3458     switch(flags) {
3459     case DISPATCH_PROPERTYGET: {
3460         RegExpInstance *This = regexp_from_vdisp(jsthis);
3461
3462         V_VT(retv) = VT_BSTR;
3463         V_BSTR(retv) = SysAllocString(This->str);
3464         if(!V_BSTR(retv))
3465             return E_OUTOFMEMORY;
3466         break;
3467     }
3468     default:
3469         FIXME("Unimplemnted flags %x\n", flags);
3470         return E_NOTIMPL;
3471     }
3472
3473     return S_OK;
3474 }
3475
3476 static HRESULT RegExp_global(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
3477         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
3478 {
3479     FIXME("\n");
3480     return E_NOTIMPL;
3481 }
3482
3483 static HRESULT RegExp_ignoreCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
3484         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
3485 {
3486     FIXME("\n");
3487     return E_NOTIMPL;
3488 }
3489
3490 static HRESULT RegExp_multiline(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
3491         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
3492 {
3493     FIXME("\n");
3494     return E_NOTIMPL;
3495 }
3496
3497 static INT index_from_var(script_ctx_t *ctx, VARIANT *v)
3498 {
3499     jsexcept_t ei;
3500     VARIANT num;
3501     HRESULT hres;
3502
3503     memset(&ei, 0, sizeof(ei));
3504     hres = to_number(ctx, v, &ei, &num);
3505     if(FAILED(hres)) { /* FIXME: Move ignoring exceptions to to_promitive */
3506         VariantClear(&ei.var);
3507         return 0;
3508     }
3509
3510     if(V_VT(&num) == VT_R8) {
3511         DOUBLE d = floor(V_R8(&num));
3512         return (DOUBLE)(INT)d == d ? d : 0;
3513     }
3514
3515     return V_I4(&num);
3516 }
3517
3518 static HRESULT RegExp_lastIndex(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
3519         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
3520 {
3521     TRACE("\n");
3522
3523     switch(flags) {
3524     case DISPATCH_PROPERTYGET: {
3525         RegExpInstance *regexp = regexp_from_vdisp(jsthis);
3526
3527         V_VT(retv) = VT_EMPTY;
3528         return VariantCopy(retv, &regexp->last_index_var);
3529     }
3530     case DISPATCH_PROPERTYPUT: {
3531         RegExpInstance *regexp = regexp_from_vdisp(jsthis);
3532         VARIANT *arg;
3533         HRESULT hres;
3534
3535         arg = get_arg(dp,0);
3536         hres = VariantCopy(&regexp->last_index_var, arg);
3537         if(FAILED(hres))
3538             return hres;
3539
3540         regexp->last_index = index_from_var(ctx, arg);
3541         break;
3542     }
3543     default:
3544         FIXME("unimplemented flags: %x\n", flags);
3545         return E_NOTIMPL;
3546     }
3547
3548     return S_OK;
3549 }
3550
3551 static HRESULT RegExp_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
3552         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
3553 {
3554     FIXME("\n");
3555     return E_NOTIMPL;
3556 }
3557
3558 static HRESULT create_match_array(script_ctx_t *ctx, BSTR input, const match_result_t *result,
3559         const match_result_t *parens, DWORD parens_cnt, jsexcept_t *ei, IDispatch **ret)
3560 {
3561     DispatchEx *array;
3562     VARIANT var;
3563     int i;
3564     HRESULT hres = S_OK;
3565
3566     static const WCHAR indexW[] = {'i','n','d','e','x',0};
3567     static const WCHAR inputW[] = {'i','n','p','u','t',0};
3568     static const WCHAR zeroW[] = {'0',0};
3569
3570     hres = create_array(ctx, parens_cnt+1, &array);
3571     if(FAILED(hres))
3572         return hres;
3573
3574     for(i=0; i < parens_cnt; i++) {
3575         V_VT(&var) = VT_BSTR;
3576         V_BSTR(&var) = SysAllocStringLen(parens[i].str, parens[i].len);
3577         if(!V_BSTR(&var)) {
3578             hres = E_OUTOFMEMORY;
3579             break;
3580         }
3581
3582         hres = jsdisp_propput_idx(array, i+1, &var, ei, NULL/*FIXME*/);
3583         SysFreeString(V_BSTR(&var));
3584         if(FAILED(hres))
3585             break;
3586     }
3587
3588     while(SUCCEEDED(hres)) {
3589         V_VT(&var) = VT_I4;
3590         V_I4(&var) = result->str-input;
3591         hres = jsdisp_propput_name(array, indexW, &var, ei, NULL/*FIXME*/);
3592         if(FAILED(hres))
3593             break;
3594
3595         V_VT(&var) = VT_BSTR;
3596         V_BSTR(&var) = input;
3597         hres = jsdisp_propput_name(array, inputW, &var, ei, NULL/*FIXME*/);
3598         if(FAILED(hres))
3599             break;
3600
3601         V_BSTR(&var) = SysAllocStringLen(result->str, result->len);
3602         if(!V_BSTR(&var)) {
3603             hres = E_OUTOFMEMORY;
3604             break;
3605         }
3606         hres = jsdisp_propput_name(array, zeroW, &var, ei, NULL/*FIXME*/);
3607         SysFreeString(V_BSTR(&var));
3608         break;
3609     }
3610
3611     if(FAILED(hres)) {
3612         jsdisp_release(array);
3613         return hres;
3614     }
3615
3616     *ret = (IDispatch*)_IDispatchEx_(array);
3617     return S_OK;
3618 }
3619
3620 static HRESULT run_exec(script_ctx_t *ctx, vdisp_t *jsthis, VARIANT *arg, jsexcept_t *ei, BSTR *input,
3621         match_result_t *match, match_result_t **parens, DWORD *parens_cnt, VARIANT_BOOL *ret)
3622 {
3623     RegExpInstance *regexp;
3624     DWORD parens_size = 0, last_index = 0, length;
3625     const WCHAR *cp;
3626     BSTR string;
3627     HRESULT hres;
3628
3629     if(!is_vclass(jsthis, JSCLASS_REGEXP)) {
3630         FIXME("Not a RegExp\n");
3631         return E_NOTIMPL;
3632     }
3633
3634     regexp = regexp_from_vdisp(jsthis);
3635
3636     if(arg) {
3637         hres = to_string(ctx, arg, ei, &string);
3638         if(FAILED(hres))
3639             return hres;
3640         length = SysStringLen(string);
3641     }else {
3642         string = NULL;
3643         length = 0;
3644     }
3645
3646     if(regexp->jsregexp->flags & JSREG_GLOB) {
3647         if(regexp->last_index < 0) {
3648             SysFreeString(string);
3649             set_last_index(regexp, 0);
3650             *ret = VARIANT_FALSE;
3651             if(input) {
3652                 *input = NULL;
3653             }
3654             return S_OK;
3655         }
3656
3657         last_index = regexp->last_index;
3658     }
3659
3660     cp = string + last_index;
3661     hres = regexp_match_next(ctx, &regexp->dispex, REM_RESET_INDEX, string, length, &cp, parens,
3662             parens ? &parens_size : NULL, parens_cnt, match);
3663     if(FAILED(hres)) {
3664         SysFreeString(string);
3665         return hres;
3666     }
3667
3668     *ret = hres == S_OK ? VARIANT_TRUE : VARIANT_FALSE;
3669     if(input) {
3670         *input = string;
3671     }else {
3672         SysFreeString(string);
3673     }
3674     return S_OK;
3675 }
3676
3677 static HRESULT RegExp_exec(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
3678         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
3679 {
3680     match_result_t *parens = NULL, match;
3681     DWORD parens_cnt = 0;
3682     VARIANT_BOOL b;
3683     BSTR string;
3684     HRESULT hres;
3685
3686     TRACE("\n");
3687
3688     hres = run_exec(ctx, jsthis, arg_cnt(dp) ? get_arg(dp,0) : NULL, ei, &string, &match, &parens, &parens_cnt, &b);
3689     if(FAILED(hres))
3690         return hres;
3691
3692     if(retv) {
3693         if(b) {
3694             IDispatch *ret;
3695
3696             hres = create_match_array(ctx, string, &match, parens, parens_cnt, ei, &ret);
3697             if(SUCCEEDED(hres)) {
3698                 V_VT(retv) = VT_DISPATCH;
3699                 V_DISPATCH(retv) = ret;
3700             }
3701         }else {
3702             V_VT(retv) = VT_NULL;
3703         }
3704     }
3705
3706     heap_free(parens);
3707     SysFreeString(string);
3708     return hres;
3709 }
3710
3711 static HRESULT RegExp_test(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
3712         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
3713 {
3714     match_result_t match;
3715     VARIANT_BOOL b;
3716     HRESULT hres;
3717
3718     TRACE("\n");
3719
3720     hres = run_exec(ctx, jsthis, arg_cnt(dp) ? get_arg(dp,0) : NULL, ei, NULL, &match, NULL, NULL, &b);
3721     if(FAILED(hres))
3722         return hres;
3723
3724     if(retv) {
3725         V_VT(retv) = VT_BOOL;
3726         V_BOOL(retv) = b;
3727     }
3728     return S_OK;
3729 }
3730
3731 static HRESULT RegExp_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
3732         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
3733 {
3734     TRACE("\n");
3735
3736     switch(flags) {
3737     case INVOKE_FUNC:
3738         return throw_type_error(ctx, ei, IDS_NOT_FUNC, NULL);
3739     default:
3740         FIXME("unimplemented flags %x\n", flags);
3741         return E_NOTIMPL;
3742     }
3743
3744     return S_OK;
3745 }
3746
3747 static void RegExp_destructor(DispatchEx *dispex)
3748 {
3749     RegExpInstance *This = (RegExpInstance*)dispex;
3750
3751     if(This->jsregexp)
3752         js_DestroyRegExp(This->jsregexp);
3753     VariantClear(&This->last_index_var);
3754     SysFreeString(This->str);
3755     heap_free(This);
3756 }
3757
3758 static const builtin_prop_t RegExp_props[] = {
3759     {execW,                  RegExp_exec,                  PROPF_METHOD|1},
3760     {globalW,                RegExp_global,                0},
3761     {ignoreCaseW,            RegExp_ignoreCase,            0},
3762     {lastIndexW,             RegExp_lastIndex,             0},
3763     {multilineW,             RegExp_multiline,             0},
3764     {sourceW,                RegExp_source,                0},
3765     {testW,                  RegExp_test,                  PROPF_METHOD|1},
3766     {toStringW,              RegExp_toString,              PROPF_METHOD}
3767 };
3768
3769 static const builtin_info_t RegExp_info = {
3770     JSCLASS_REGEXP,
3771     {NULL, RegExp_value, 0},
3772     sizeof(RegExp_props)/sizeof(*RegExp_props),
3773     RegExp_props,
3774     RegExp_destructor,
3775     NULL
3776 };
3777
3778 static HRESULT alloc_regexp(script_ctx_t *ctx, DispatchEx *object_prototype, RegExpInstance **ret)
3779 {
3780     RegExpInstance *regexp;
3781     HRESULT hres;
3782
3783     regexp = heap_alloc_zero(sizeof(RegExpInstance));
3784     if(!regexp)
3785         return E_OUTOFMEMORY;
3786
3787     if(object_prototype)
3788         hres = init_dispex(&regexp->dispex, ctx, &RegExp_info, object_prototype);
3789     else
3790         hres = init_dispex_from_constr(&regexp->dispex, ctx, &RegExp_info, ctx->regexp_constr);
3791
3792     if(FAILED(hres)) {
3793         heap_free(regexp);
3794         return hres;
3795     }
3796
3797     *ret = regexp;
3798     return S_OK;
3799 }
3800
3801 HRESULT create_regexp(script_ctx_t *ctx, const WCHAR *exp, int len, DWORD flags, DispatchEx **ret)
3802 {
3803     RegExpInstance *regexp;
3804     HRESULT hres;
3805
3806     TRACE("%s %x\n", debugstr_w(exp), flags);
3807
3808     hres = alloc_regexp(ctx, NULL, &regexp);
3809     if(FAILED(hres))
3810         return hres;
3811
3812     if(len == -1)
3813         regexp->str = SysAllocString(exp);
3814     else
3815         regexp->str = SysAllocStringLen(exp, len);
3816     if(!regexp->str) {
3817         jsdisp_release(&regexp->dispex);
3818         return E_OUTOFMEMORY;
3819     }
3820
3821     regexp->jsregexp = js_NewRegExp(ctx, regexp->str, flags, FALSE);
3822     if(!regexp->jsregexp) {
3823         WARN("js_NewRegExp failed\n");
3824         jsdisp_release(&regexp->dispex);
3825         return E_FAIL;
3826     }
3827
3828     V_VT(&regexp->last_index_var) = VT_I4;
3829     V_I4(&regexp->last_index_var) = 0;
3830
3831     *ret = &regexp->dispex;
3832     return S_OK;
3833 }
3834
3835 HRESULT create_regexp_var(script_ctx_t *ctx, VARIANT *src_arg, VARIANT *flags_arg, DispatchEx **ret)
3836 {
3837     const WCHAR *opt = emptyW, *src;
3838     DWORD flags;
3839     HRESULT hres;
3840
3841     if(V_VT(src_arg) == VT_DISPATCH) {
3842         DispatchEx *obj;
3843
3844         obj = iface_to_jsdisp((IUnknown*)V_DISPATCH(src_arg));
3845         if(obj) {
3846             if(is_class(obj, JSCLASS_REGEXP)) {
3847                 RegExpInstance *regexp = (RegExpInstance*)obj;
3848
3849                 hres = create_regexp(ctx, regexp->str, -1, regexp->jsregexp->flags, ret);
3850                 jsdisp_release(obj);
3851                 return hres;
3852             }
3853
3854             jsdisp_release(obj);
3855         }
3856     }
3857
3858     if(V_VT(src_arg) != VT_BSTR) {
3859         FIXME("flags_arg = %s\n", debugstr_variant(flags_arg));
3860         return E_NOTIMPL;
3861     }
3862
3863     src = V_BSTR(src_arg);
3864
3865     if(flags_arg) {
3866         if(V_VT(flags_arg) != VT_BSTR) {
3867             FIXME("unimplemented for vt %d\n", V_VT(flags_arg));
3868             return E_NOTIMPL;
3869         }
3870
3871         opt = V_BSTR(flags_arg);
3872     }
3873
3874     hres = parse_regexp_flags(opt, strlenW(opt), &flags);
3875     if(FAILED(hres))
3876         return hres;
3877
3878     return create_regexp(ctx, src, -1, flags, ret);
3879 }
3880
3881 HRESULT regexp_string_match(script_ctx_t *ctx, DispatchEx *re, BSTR str,
3882         VARIANT *retv, jsexcept_t *ei)
3883 {
3884     RegExpInstance *regexp = (RegExpInstance*)re;
3885     match_result_t *match_result;
3886     DWORD match_cnt, i, length;
3887     DispatchEx *array;
3888     VARIANT var;
3889     HRESULT hres;
3890
3891     length = SysStringLen(str);
3892
3893     if(!(regexp->jsregexp->flags & JSREG_GLOB)) {
3894         match_result_t match, *parens = NULL;
3895         DWORD parens_cnt, parens_size = 0;
3896         const WCHAR *cp = str;
3897
3898         hres = regexp_match_next(ctx, &regexp->dispex, 0, str, length, &cp, &parens, &parens_size, &parens_cnt, &match);
3899         if(FAILED(hres))
3900             return hres;
3901
3902         if(retv) {
3903             if(hres == S_OK) {
3904                 IDispatch *ret;
3905
3906                 hres = create_match_array(ctx, str, &match, parens, parens_cnt, ei, &ret);
3907                 if(SUCCEEDED(hres)) {
3908                     V_VT(retv) = VT_DISPATCH;
3909                     V_DISPATCH(retv) = ret;
3910                 }
3911             }else {
3912                 V_VT(retv) = VT_NULL;
3913             }
3914         }
3915
3916         heap_free(parens);
3917         return S_OK;
3918     }
3919
3920     hres = regexp_match(ctx, &regexp->dispex, str, length, FALSE, &match_result, &match_cnt);
3921     if(FAILED(hres))
3922         return hres;
3923
3924     if(!match_cnt) {
3925         TRACE("no match\n");
3926
3927         if(retv)
3928             V_VT(retv) = VT_NULL;
3929         return S_OK;
3930     }
3931
3932     hres = create_array(ctx, match_cnt, &array);
3933     if(FAILED(hres))
3934         return hres;
3935
3936     V_VT(&var) = VT_BSTR;
3937
3938     for(i=0; i < match_cnt; i++) {
3939         V_BSTR(&var) = SysAllocStringLen(match_result[i].str, match_result[i].len);
3940         if(!V_BSTR(&var)) {
3941             hres = E_OUTOFMEMORY;
3942             break;
3943         }
3944
3945         hres = jsdisp_propput_idx(array, i, &var, ei, NULL/*FIXME*/);
3946         SysFreeString(V_BSTR(&var));
3947         if(FAILED(hres))
3948             break;
3949     }
3950
3951     heap_free(match_result);
3952
3953     if(SUCCEEDED(hres) && retv) {
3954         V_VT(retv) = VT_DISPATCH;
3955         V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(array);
3956     }else {
3957         jsdisp_release(array);
3958     }
3959     return hres;
3960 }
3961
3962 static HRESULT RegExpConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
3963         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
3964 {
3965     TRACE("\n");
3966
3967     switch(flags) {
3968     case DISPATCH_METHOD:
3969         if(arg_cnt(dp)) {
3970             VARIANT *arg = get_arg(dp,0);
3971             if(V_VT(arg) == VT_DISPATCH) {
3972                 DispatchEx *jsdisp = iface_to_jsdisp((IUnknown*)V_DISPATCH(arg));
3973                 if(jsdisp) {
3974                     if(is_class(jsdisp, JSCLASS_REGEXP)) {
3975                         if(arg_cnt(dp) > 1 && V_VT(get_arg(dp,1)) != VT_EMPTY) {
3976                             jsdisp_release(jsdisp);
3977                             return throw_regexp_error(ctx, ei, IDS_REGEXP_SYNTAX_ERROR, NULL);
3978                         }
3979
3980                         if(retv) {
3981                             V_VT(retv) = VT_DISPATCH;
3982                             V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(jsdisp);
3983                         }else {
3984                             jsdisp_release(jsdisp);
3985                         }
3986                         return S_OK;
3987                     }
3988                     jsdisp_release(jsdisp);
3989                 }
3990             }
3991         }
3992         /* fall through */
3993     case DISPATCH_CONSTRUCT: {
3994         DispatchEx *ret;
3995         HRESULT hres;
3996
3997         if(!arg_cnt(dp)) {
3998             FIXME("no args\n");
3999             return E_NOTIMPL;
4000         }
4001
4002         hres = create_regexp_var(ctx, get_arg(dp,0), arg_cnt(dp) > 1 ? get_arg(dp,1) : NULL, &ret);
4003         if(FAILED(hres))
4004             return hres;
4005
4006         if(retv) {
4007             V_VT(retv) = VT_DISPATCH;
4008             V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(ret);
4009         }else {
4010             jsdisp_release(ret);
4011         }
4012         return S_OK;
4013     }
4014     default:
4015         FIXME("unimplemented flags: %x\n", flags);
4016         return E_NOTIMPL;
4017     }
4018
4019     return S_OK;
4020 }
4021
4022 HRESULT create_regexp_constr(script_ctx_t *ctx, DispatchEx *object_prototype, DispatchEx **ret)
4023 {
4024     RegExpInstance *regexp;
4025     HRESULT hres;
4026
4027     static const WCHAR RegExpW[] = {'R','e','g','E','x','p',0};
4028
4029     hres = alloc_regexp(ctx, object_prototype, &regexp);
4030     if(FAILED(hres))
4031         return hres;
4032
4033     hres = create_builtin_function(ctx, RegExpConstr_value, RegExpW, NULL,
4034             PROPF_CONSTR|2, &regexp->dispex, ret);
4035
4036     jsdisp_release(&regexp->dispex);
4037     return hres;
4038 }
4039
4040 HRESULT parse_regexp_flags(const WCHAR *str, DWORD str_len, DWORD *ret)
4041 {
4042     const WCHAR *p;
4043     DWORD flags = 0;
4044
4045     for (p = str; p < str+str_len; p++) {
4046         switch (*p) {
4047         case 'g':
4048             flags |= JSREG_GLOB;
4049             break;
4050         case 'i':
4051             flags |= JSREG_FOLD;
4052             break;
4053         case 'm':
4054             flags |= JSREG_MULTILINE;
4055             break;
4056         case 'y':
4057             flags |= JSREG_STICKY;
4058             break;
4059         default:
4060             WARN("wrong flag %c\n", *p);
4061             return E_FAIL;
4062         }
4063     }
4064
4065     *ret = flags;
4066     return S_OK;
4067 }