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