summaryrefslogtreecommitdiffstats
path: root/src/dom/js/jsregexp.c
diff options
context:
space:
mode:
authorAaron Spike <aaron@ekips.org>2006-04-12 13:20:54 +0000
committeracspike <acspike@users.sourceforge.net>2006-04-12 13:20:54 +0000
commitddfaffe08d23e4663fe759d67ae33fd67fc9ce5b (patch)
tree234f5425a7ef7058e69dc2ab77f89810e4f3ed91 /src/dom/js/jsregexp.c
parentfix 1466070 (diff)
downloadinkscape-ddfaffe08d23e4663fe759d67ae33fd67fc9ce5b.tar.gz
inkscape-ddfaffe08d23e4663fe759d67ae33fd67fc9ce5b.zip
Removed file/folder for ishmal
(bzr r478)
Diffstat (limited to 'src/dom/js/jsregexp.c')
-rw-r--r--src/dom/js/jsregexp.c3773
1 files changed, 0 insertions, 3773 deletions
diff --git a/src/dom/js/jsregexp.c b/src/dom/js/jsregexp.c
deleted file mode 100644
index 18ab92c35..000000000
--- a/src/dom/js/jsregexp.c
+++ /dev/null
@@ -1,3773 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- *
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla Communicator client code, released
- * March 31, 1998.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-/*
- * JS regular expressions, after Perl.
- */
-#include "jsstddef.h"
-#include <stdlib.h>
-#include <string.h>
-#include "jstypes.h"
-#include "jsarena.h" /* Added by JSIFY */
-#include "jsutil.h" /* Added by JSIFY */
-#include "jsapi.h"
-#include "jsarray.h"
-#include "jsatom.h"
-#include "jscntxt.h"
-#include "jsconfig.h"
-#include "jsfun.h"
-#include "jsgc.h"
-#include "jsinterp.h"
-#include "jslock.h"
-#include "jsnum.h"
-#include "jsobj.h"
-#include "jsopcode.h"
-#include "jsregexp.h"
-#include "jsscan.h"
-#include "jsstr.h"
-
-#ifdef XP_MAC
-#include <MacMemory.h>
-#endif
-
-#if JS_HAS_REGEXPS
-
-/* Note : contiguity of 'simple opcodes' is important for simpleMatch() */
-typedef enum REOp {
- REOP_EMPTY = 0, /* match rest of input against rest of r.e. */
- REOP_ALT = 1, /* alternative subexpressions in kid and next */
- REOP_SIMPLE_START = 2, /* start of 'simple opcodes' */
- REOP_BOL = 2, /* beginning of input (or line if multiline) */
- REOP_EOL = 3, /* end of input (or line if multiline) */
- REOP_WBDRY = 4, /* match "" at word boundary */
- REOP_WNONBDRY = 5, /* match "" at word non-boundary */
- REOP_DOT = 6, /* stands for any character */
- REOP_DIGIT = 7, /* match a digit char: [0-9] */
- REOP_NONDIGIT = 8, /* match a non-digit char: [^0-9] */
- REOP_ALNUM = 9, /* match an alphanumeric char: [0-9a-z_A-Z] */
- REOP_NONALNUM = 10, /* match a non-alphanumeric char: [^0-9a-z_A-Z] */
- REOP_SPACE = 11, /* match a whitespace char */
- REOP_NONSPACE = 12, /* match a non-whitespace char */
- REOP_BACKREF = 13, /* back-reference (e.g., \1) to a parenthetical */
- REOP_FLAT = 14, /* match a flat string */
- REOP_FLAT1 = 15, /* match a single char */
- REOP_FLATi = 16, /* case-independent REOP_FLAT */
- REOP_FLAT1i = 17, /* case-independent REOP_FLAT1 */
- REOP_UCFLAT1 = 18, /* single Unicode char */
- REOP_UCFLAT1i = 19, /* case-independent REOP_UCFLAT1 */
- REOP_UCFLAT = 20, /* flat Unicode string; len immediate counts chars */
- REOP_UCFLATi = 21, /* case-independent REOP_UCFLAT */
- REOP_CLASS = 22, /* character class with index */
- REOP_NCLASS = 23, /* negated character class with index */
- REOP_SIMPLE_END = 23, /* end of 'simple opcodes' */
- REOP_QUANT = 25, /* quantified atom: atom{1,2} */
- REOP_STAR = 26, /* zero or more occurrences of kid */
- REOP_PLUS = 27, /* one or more occurrences of kid */
- REOP_OPT = 28, /* optional subexpression in kid */
- REOP_LPAREN = 29, /* left paren bytecode: kid is u.num'th sub-regexp */
- REOP_RPAREN = 30, /* right paren bytecode */
- REOP_JUMP = 31, /* for deoptimized closure loops */
- REOP_DOTSTAR = 32, /* optimize .* to use a single opcode */
- REOP_ANCHOR = 33, /* like .* but skips left context to unanchored r.e. */
- REOP_EOLONLY = 34, /* $ not preceded by any pattern */
- REOP_BACKREFi = 37, /* case-independent REOP_BACKREF */
- REOP_LPARENNON = 41, /* non-capturing version of REOP_LPAREN */
- REOP_ASSERT = 43, /* zero width positive lookahead assertion */
- REOP_ASSERT_NOT = 44, /* zero width negative lookahead assertion */
- REOP_ASSERTTEST = 45, /* sentinel at end of assertion child */
- REOP_ASSERTNOTTEST = 46, /* sentinel at end of !assertion child */
- REOP_MINIMALSTAR = 47, /* non-greedy version of * */
- REOP_MINIMALPLUS = 48, /* non-greedy version of + */
- REOP_MINIMALOPT = 49, /* non-greedy version of ? */
- REOP_MINIMALQUANT = 50, /* non-greedy version of {} */
- REOP_ENDCHILD = 51, /* sentinel at end of quantifier child */
- REOP_REPEAT = 52, /* directs execution of greedy quantifier */
- REOP_MINIMALREPEAT = 53, /* directs execution of non-greedy quantifier */
- REOP_ALTPREREQ = 54, /* prerequisite for ALT, either of two chars */
- REOP_ALTPREREQ2 = 55, /* prerequisite for ALT, a char or a class */
- REOP_ENDALT = 56, /* end of final alternate */
- REOP_CONCAT = 57, /* concatenation of terms (parse time only) */
-
- REOP_END
-} REOp;
-
-#define REOP_IS_SIMPLE(op) (((op) >= REOP_SIMPLE_START) && ((op) <= REOP_SIMPLE_END))
-
-struct RENode {
- REOp op; /* r.e. op bytecode */
- RENode *next; /* next in concatenation order */
- void *kid; /* first operand */
- union {
- void *kid2; /* second operand */
- jsint num; /* could be a number */
- jsint parenIndex; /* or a parenthesis index */
- struct { /* or a quantifier range */
- uint16 min;
- uint16 max;
- JSBool greedy;
- } range;
- struct { /* or a character class */
- uint16 startIndex;
- uint16 kidlen; /* length of string at kid, in jschars */
- uint16 bmsize; /* bitmap size, based on max char code */
- uint16 index; /* index into class list */
- JSBool sense;
- } ucclass;
- struct { /* or a literal sequence */
- jschar chr; /* of one character */
- uint16 length; /* or many (via the kid) */
- } flat;
- struct {
- RENode *kid2; /* second operand from ALT */
- jschar ch1; /* match char for ALTPREREQ */
- jschar ch2; /* ditto, or class index for ALTPREREQ2 */
- } altprereq;
- } u;
-};
-
-#define RE_IS_LETTER(c) ( ((c >= 'A') && (c <= 'Z')) || \
- ((c >= 'a') && (c <= 'z')) )
-#define RE_IS_LINE_TERM(c) ( (c == '\n') || (c == '\r') || \
- (c == LINE_SEPARATOR) || (c == PARA_SEPARATOR))
-
-#define CLASS_CACHE_SIZE (4)
-typedef struct CompilerState {
- JSContext *context;
- JSTokenStream *tokenStream; /* For reporting errors */
- const jschar *cpbegin;
- const jschar *cpend;
- const jschar *cp;
- uintN flags;
- uint16 parenCount;
- uint16 classCount; /* number of [] encountered */
- size_t progLength; /* estimated bytecode length */
- uintN treeDepth; /* maximum depth of parse tree */
- RENode *result;
- struct {
- const jschar *start; /* small cache of class strings */
- uint16 length; /* since they're often the same */
- uint16 index;
- } classCache[CLASS_CACHE_SIZE];
-} CompilerState;
-
-typedef struct RECapture {
- int32 index; /* start of contents, -1 for empty */
- uint16 length; /* length of capture */
-} RECapture;
-
-typedef struct REMatchState {
- const jschar *cp;
- RECapture parens[1]; /* first of 're->parenCount' captures,
- * allocated at end of this struct.
- */
-} REMatchState;
-
-struct REBackTrackData;
-
-typedef struct REProgState {
- jsbytecode *continue_pc; /* current continuation data */
- jsbytecode continue_op;
- uint16 index; /* progress in text */
- uintN parenSoFar; /* highest indexed paren started */
- union {
- struct {
- uint16 min; /* current quantifier limits */
- uint16 max;
- } quantifier;
- struct {
- size_t top; /* backtrack stack state */
- size_t sz;
- } assertion;
- } u;
-} REProgState;
-
-typedef struct REBackTrackData {
- size_t sz; /* size of previous stack entry */
- jsbytecode *backtrack_pc; /* where to backtrack to */
- jsbytecode backtrack_op;
- const jschar *cp; /* index in text of match at backtrack */
- intN parenIndex; /* start index of saved paren contents */
- uint16 parenCount; /* # of saved paren contents */
- uint16 precedingStateTop; /* number of parent states */
- /* saved parent states follow */
- /* saved paren contents follow */
-} REBackTrackData;
-
-#define INITIAL_STATESTACK (100)
-#define INITIAL_BACKTRACK (8000)
-
-typedef struct REGlobalData {
- JSContext *cx;
- JSRegExp *regexp; /* the RE in execution */
- JSBool ok; /* runtime error (out_of_memory only?) */
- size_t start; /* offset to start at */
- ptrdiff_t skipped; /* chars skipped anchoring this r.e. */
- const jschar *cpbegin, *cpend; /* text base address and limit */
-
- REProgState *stateStack; /* stack of state of current parents */
- uint16 stateStackTop;
- uint16 maxStateStack;
-
- REBackTrackData *backTrackStack;/* stack of matched-so-far positions */
- REBackTrackData *backTrackSP;
- size_t maxBackTrack;
- size_t cursz; /* size of current stack entry */
-
- JSArenaPool pool; /* I don't understand but it's faster to
- * use this than to malloc/free the three
- * items that are allocated from this pool
- */
-
-} REGlobalData;
-
-
-/*
- * 1. If IgnoreCase is false, return ch.
- * 2. Let u be ch converted to upper case as if by calling
- * String.prototype.toUpperCase on the one-character string ch.
- * 3. If u does not consist of a single character, return ch.
- * 4. Let cu be u's character.
- * 5. If ch's code point value is greater than or equal to decimal 128 and cu's
- * code point value is less than decimal 128, then return ch.
- * 6. Return cu.
- */
-static jschar
-upcase(jschar ch)
-{
- jschar cu = JS_TOUPPER(ch);
- if ((ch >= 128) && (cu < 128)) return ch;
- return cu;
-}
-
-static jschar
-downcase(jschar ch)
-{
- jschar cl = JS_TOLOWER(ch);
- if ((cl >= 128) && (ch < 128)) return ch;
- return cl;
-}
-
-/* Construct and initialize an RENode, returning NULL for out-of-memory */
-static RENode *
-NewRENode(CompilerState *state, REOp op)
-{
- JSContext *cx;
- RENode *ren;
-
- cx = state->context;
- JS_ARENA_ALLOCATE_CAST(ren, RENode *, &cx->tempPool, sizeof *ren);
- if (!ren) {
- JS_ReportOutOfMemory(cx);
- return NULL;
- }
- ren->op = op;
- ren->next = NULL;
- ren->kid = NULL;
- return ren;
-}
-
-/*
- * Validates and converts hex ascii value.
- */
-static JSBool
-isASCIIHexDigit(jschar c, uintN *digit)
-{
- uintN cv = c;
-
- if (cv < '0')
- return JS_FALSE;
- if (cv <= '9') {
- *digit = cv - '0';
- return JS_TRUE;
- }
- cv |= 0x20;
- if (cv >= 'a' && cv <= 'f') {
- *digit = cv - 'a' + 10;
- return JS_TRUE;
- }
- return JS_FALSE;
-}
-
-
-typedef struct {
- REOp op;
- const jschar *errPos;
- uint16 parenIndex;
-} REOpData;
-
-
-/*
- * Process the op against the two top operands, reducing them to a single
- * operand in the penultimate slot. Update progLength and treeDepth.
- */
-static JSBool
-processOp(CompilerState *state, REOpData *opData, RENode **operandStack, intN operandSP)
-{
- RENode *result;
-
- switch (opData->op) {
- case REOP_ALT:
- result = NewRENode(state, REOP_ALT);
- if (!result)
- return JS_FALSE;
- result->kid = operandStack[operandSP - 2];
- result->u.kid2 = operandStack[operandSP - 1];
- operandStack[operandSP - 2] = result;
- /*
- * look at both alternates to see if there's a FLAT or a CLASS at
- * the start of each. If so, use a prerequisite match
- */
- ++state->treeDepth;
- if ((((RENode *)(result->kid))->op == REOP_FLAT)
- && (((RENode *)(result->u.kid2))->op == REOP_FLAT)
- && ((state->flags & JSREG_FOLD) == 0) ) {
- result->op = REOP_ALTPREREQ;
- result->u.altprereq.ch1
- = ((RENode *)(result->kid))->u.flat.chr;
- result->u.altprereq.ch2
- = ((RENode *)(result->u.kid2))->u.flat.chr;
- /* ALTPREREQ, <end>, uch1, uch2, <next>, ...,
- JUMP, <end> ... ENDALT */
- state->progLength += 13;
- }
- else
- if ((((RENode *)(result->kid))->op == REOP_CLASS)
- && (((RENode *)(result->kid))->u.ucclass.index < 256)
- && (((RENode *)(result->u.kid2))->op == REOP_FLAT)
- && ((state->flags & JSREG_FOLD) == 0) ) {
- result->op = REOP_ALTPREREQ2;
- result->u.altprereq.ch1
- = ((RENode *)(result->u.kid2))->u.flat.chr;
- result->u.altprereq.ch2
- = ((RENode *)(result->kid))->u.ucclass.index;
- /* ALTPREREQ2, <end>, uch1, uch2, <next>, ...,
- JUMP, <end> ... ENDALT */
- state->progLength += 13;
- }
- else
- if ((((RENode *)(result->kid))->op == REOP_FLAT)
- && (((RENode *)(result->u.kid2))->op == REOP_CLASS)
- && (((RENode *)(result->u.kid2))->u.ucclass.index < 256)
- && ((state->flags & JSREG_FOLD) == 0) ) {
- result->op = REOP_ALTPREREQ2;
- result->u.altprereq.ch1
- = ((RENode *)(result->kid))->u.flat.chr;
- result->u.altprereq.ch2
- = ((RENode *)(result->u.kid2))->u.ucclass.index;
- /* ALTPREREQ2, <end>, uch1, uch2, <next>, ...,
- JUMP, <end> ... ENDALT */
- state->progLength += 13;
- }
- else
- /* ALT, <next>, ..., JUMP, <end> ... ENDALT */
- state->progLength += 7;
- break;
- case REOP_CONCAT:
- result = operandStack[operandSP - 2];
- while (result->next)
- result = result->next;
- result->next = operandStack[operandSP - 1];
- break;
- case REOP_ASSERT:
- case REOP_ASSERT_NOT:
- case REOP_LPARENNON:
- case REOP_LPAREN:
- /* These should have been processed by a close paren. */
- js_ReportCompileErrorNumber(state->context, state->tokenStream,
- NULL, JSREPORT_ERROR,
- JSMSG_MISSING_PAREN, opData->errPos);
- return JS_FALSE;
- default:;
- }
- return JS_TRUE;
-}
-
-/*
- * Parser forward declarations.
- */
-static JSBool parseTerm(CompilerState *state);
-static JSBool parseQuantifier(CompilerState *state);
-
-/*
- * Top-down regular expression grammar, based closely on Perl4.
- *
- * regexp: altern A regular expression is one or more
- * altern '|' regexp alternatives separated by vertical bar.
- */
-
-#define INITIAL_STACK_SIZE (128)
-static JSBool
-parseRegExp(CompilerState *state)
-{
- uint16 parenIndex;
- RENode *operand;
- REOpData *operatorStack;
- RENode **operandStack;
- REOp op;
- intN i;
- JSBool result = JS_FALSE;
-
- intN operatorSP = 0, operatorStackSize = INITIAL_STACK_SIZE;
- intN operandSP = 0, operandStackSize = INITIAL_STACK_SIZE;
-
- /* Watch out for empty regexp */
- if (state->cp == state->cpend) {
- state->result = NewRENode(state, REOP_EMPTY);
- return (state->result != NULL);
- }
-
- operatorStack = (REOpData *)JS_malloc(state->context,
- sizeof(REOpData) * operatorStackSize);
- if (!operatorStack)
- return JS_FALSE;
-
- operandStack = (RENode **)JS_malloc(state->context,
- sizeof(RENode *) * operandStackSize);
- if (!operandStack)
- goto out;
-
-
- while (JS_TRUE) {
- if (state->cp == state->cpend) {
- /*
- * If we are at the end of the regexp and we're short an operand,
- * the regexp must have the form /x|/ or some such.
- */
- if (operatorSP == operandSP) {
- operand = NewRENode(state, REOP_EMPTY);
- if (!operand)
- goto out;
- goto pushOperand;
- }
- } else {
- switch (*state->cp) {
- /* balance '(' */
- case '(': /* balance ')' */
- ++state->cp;
- if ((state->cp < state->cpend) && (*state->cp == '?')
- && ( (state->cp[1] == '=')
- || (state->cp[1] == '!')
- || (state->cp[1] == ':') )) {
- ++state->cp;
- if (state->cp == state->cpend) {
- js_ReportCompileErrorNumber(state->context,
- state->tokenStream,
- NULL, JSREPORT_ERROR,
- JSMSG_MISSING_PAREN);
- goto out;
- }
- switch (*state->cp++) {
- case '=':
- op = REOP_ASSERT;
- /* ASSERT, <next>, ... ASSERTTEST */
- state->progLength += 4;
- break;
- case '!':
- op = REOP_ASSERT_NOT;
- /* ASSERTNOT, <next>, ... ASSERTNOTTEST */
- state->progLength += 4;
- break;
- case ':':
- op = REOP_LPARENNON;
- break;
- }
- parenIndex = state->parenCount;
- }
- else {
- op = REOP_LPAREN;
- /* LPAREN, <index>, ... RPAREN, <index> */
- state->progLength += 6;
- parenIndex = state->parenCount++;
- if (state->parenCount == 65535) {
- js_ReportCompileErrorNumber(state->context,
- state->tokenStream,
- NULL, JSREPORT_ERROR,
- JSMSG_TOO_MANY_PARENS);
- goto out;
- }
- }
- goto pushOperator;
- case ')':
- /* If there's not a stacked open parenthesis, throw
- * a syntax error.
- */
- for (i = operatorSP - 1; i >= 0; i--)
- if ((operatorStack[i].op == REOP_ASSERT)
- || (operatorStack[i].op == REOP_ASSERT_NOT)
- || (operatorStack[i].op == REOP_LPARENNON)
- || (operatorStack[i].op == REOP_LPAREN))
- break;
- if (i == -1) {
- js_ReportCompileErrorNumber(state->context,
- state->tokenStream,
- NULL, JSREPORT_ERROR,
- JSMSG_UNMATCHED_RIGHT_PAREN);
- goto out;
- }
- /* fall thru... */
- case '|':
- /* Expected an operand before these, so make an empty one */
- operand = NewRENode(state, REOP_EMPTY);
- if (!operand)
- goto out;
- goto pushOperand;
- default:
- if (!parseTerm(state))
- goto out;
- operand = state->result;
-pushOperand:
- if (operandSP == operandStackSize) {
- operandStackSize += operandStackSize;
- operandStack =
- (RENode **)JS_realloc(state->context, operandStack,
- sizeof(RENode *) * operandStackSize);
- if (!operandStack)
- goto out;
- }
- operandStack[operandSP++] = operand;
- break;
- }
- }
- /* At the end; process remaining operators */
-restartOperator:
- if (state->cp == state->cpend) {
- while (operatorSP) {
- --operatorSP;
- if (!processOp(state, &operatorStack[operatorSP],
- operandStack, operandSP))
- goto out;
- --operandSP;
- }
- JS_ASSERT(operandSP == 1);
- state->result = operandStack[0];
- result = JS_TRUE;
- goto out;
- }
- switch (*state->cp) {
- case '|':
- /* Process any stacked 'concat' operators */
- ++state->cp;
- while (operatorSP
- && (operatorStack[operatorSP - 1].op == REOP_CONCAT)) {
- --operatorSP;
- if (!processOp(state, &operatorStack[operatorSP],
- operandStack, operandSP))
- goto out;
- --operandSP;
- }
- op = REOP_ALT;
- goto pushOperator;
-
- case ')':
- /* If there's not a stacked open parenthesis,we
- * accept the close as a flat.
- */
- for (i = operatorSP - 1; i >= 0; i--)
- if ((operatorStack[i].op == REOP_ASSERT)
- || (operatorStack[i].op == REOP_ASSERT_NOT)
- || (operatorStack[i].op == REOP_LPARENNON)
- || (operatorStack[i].op == REOP_LPAREN))
- break;
- if (i == -1) {
- js_ReportCompileErrorNumber(state->context,
- state->tokenStream,
- NULL, JSREPORT_ERROR,
- JSMSG_UNMATCHED_RIGHT_PAREN);
- goto out;
- }
- ++state->cp;
- /* process everything on the stack until the open */
- while (JS_TRUE) {
- JS_ASSERT(operatorSP);
- --operatorSP;
- switch (operatorStack[operatorSP].op) {
- case REOP_ASSERT:
- case REOP_ASSERT_NOT:
- case REOP_LPAREN:
- operand = NewRENode(state, operatorStack[operatorSP].op);
- if (!operand)
- goto out;
- operand->u.parenIndex
- = operatorStack[operatorSP].parenIndex;
- JS_ASSERT(operandSP);
- operand->kid = operandStack[operandSP - 1];
- operandStack[operandSP - 1] = operand;
- ++state->treeDepth;
- /* fall thru... */
- case REOP_LPARENNON:
- state->result = operandStack[operandSP - 1];
- if (!parseQuantifier(state))
- goto out;
- operandStack[operandSP - 1] = state->result;
- goto restartOperator;
- default:
- if (!processOp(state, &operatorStack[operatorSP],
- operandStack, operandSP))
- goto out;
- --operandSP;
- break;
- }
- }
- break;
- default:
- /* Anything else is the start of the next term */
- op = REOP_CONCAT;
-pushOperator:
- if (operatorSP == operatorStackSize) {
- operatorStackSize += operatorStackSize;
- operatorStack =
- (REOpData *)JS_realloc(state->context, operatorStack,
- sizeof(REOpData) * operatorStackSize);
- if (!operatorStack)
- goto out;
- }
- operatorStack[operatorSP].op = op;
- operatorStack[operatorSP].errPos = state->cp;
- operatorStack[operatorSP++].parenIndex = parenIndex;
- break;
- }
- }
-out:
- if (operatorStack)
- JS_free(state->context, operatorStack);
- if (operandStack)
- JS_free(state->context, operandStack);
- return result;
-}
-
-/*
- * Extract and return a decimal value at state->cp, the
- * initial character 'c' has already been read.
- */
-static intN
-getDecimalValue(jschar c, CompilerState *state)
-{
- intN value = JS7_UNDEC(c);
- while (state->cp < state->cpend) {
- c = *state->cp;
- if (!JS7_ISDEC(c))
- break;
- value = (10 * value) + JS7_UNDEC(c);
- ++state->cp;
- }
- return value;
-}
-
-/*
- * Calculate the total size of the bitmap required for a class expression.
- */
-static JSBool
-calculateBitmapSize(CompilerState *state, RENode *target, const jschar *src,
- const jschar *end)
-{
- jschar rangeStart, c;
- uintN n, digit, nDigits, i;
- uintN max = 0;
- JSBool inRange = JS_FALSE;
-
- target->u.ucclass.bmsize = 0;
- target->u.ucclass.sense = JS_TRUE;
-
- if (src == end)
- return JS_TRUE;
-
- if (*src == '^') {
- ++src;
- target->u.ucclass.sense = JS_FALSE;
- }
-
- while (src != end) {
- uintN localMax = 0;
- switch (*src) {
- case '\\':
- ++src;
- c = *src++;
- switch (c) {
- case 'b':
- localMax = 0x8;
- break;
- case 'f':
- localMax = 0xC;
- break;
- case 'n':
- localMax = 0xA;
- break;
- case 'r':
- localMax = 0xD;
- break;
- case 't':
- localMax = 0x9;
- break;
- case 'v':
- localMax = 0xB;
- break;
- case 'c':
- if (((src + 1) < end) && RE_IS_LETTER(src[1]))
- localMax = (jschar)(*src++ & 0x1F);
- else
- localMax = '\\';
- break;
- case 'x':
- nDigits = 2;
- goto lexHex;
- case 'u':
- nDigits = 4;
-lexHex:
- n = 0;
- for (i = 0; (i < nDigits) && (src < end); i++) {
- c = *src++;
- if (!isASCIIHexDigit(c, &digit)) {
- /*
- * Back off to accepting the original
- *'\' as a literal.
- */
- src -= (i + 1);
- n = '\\';
- break;
- }
- n = (n << 4) | digit;
- }
- localMax = n;
- break;
- case 'd':
- if (inRange) {
- JS_ReportErrorNumber(state->context,
- js_GetErrorMessage, NULL,
- JSMSG_BAD_CLASS_RANGE);
- return JS_FALSE;
- }
- localMax = '9';
- break;
- case 'D':
- case 's':
- case 'S':
- case 'w':
- case 'W':
- if (inRange) {
- JS_ReportErrorNumber(state->context,
- js_GetErrorMessage, NULL,
- JSMSG_BAD_CLASS_RANGE);
- return JS_FALSE;
- }
- target->u.ucclass.bmsize = 65535;
- return JS_TRUE;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- /*
- * This is a non-ECMA extension - decimal escapes (in this
- * case, octal!) are supposed to be an error inside class
- * ranges, but supported here for backwards compatibility.
- *
- */
- n = JS7_UNDEC(c);
- c = *src;
- if ('0' <= c && c <= '7') {
- src++;
- n = 8 * n + JS7_UNDEC(c);
- c = *src;
- if ('0' <= c && c <= '7') {
- src++;
- i = 8 * n + JS7_UNDEC(c);
- if (i <= 0377)
- n = i;
- else
- src--;
- }
- }
- localMax = n;
- break;
-
- default:
- localMax = c;
- break;
- }
- break;
- default:
- localMax = *src++;
- break;
- }
- if (inRange) {
- if (rangeStart > localMax) {
- JS_ReportErrorNumber(state->context,
- js_GetErrorMessage, NULL,
- JSMSG_BAD_CLASS_RANGE);
- return JS_FALSE;
- }
- inRange = JS_FALSE;
- }
- else {
- if (src < (end - 1)) {
- if (*src == '-') {
- ++src;
- inRange = JS_TRUE;
- rangeStart = (jschar)localMax;
- continue;
- }
- }
- }
- if (state->flags & JSREG_FOLD) {
- c = JS_MAX(upcase((jschar)localMax), downcase((jschar)localMax));
- if (c > localMax)
- localMax = c;
- }
- if (localMax > max)
- max = localMax;
- }
- target->u.ucclass.bmsize = max;
- return JS_TRUE;
-}
-
-/*
- * item: assertion An item is either an assertion or
- * quantatom a quantified atom.
- *
- * assertion: '^' Assertions match beginning of string
- * (or line if the class static property
- * RegExp.multiline is true).
- * '$' End of string (or line if the class
- * static property RegExp.multiline is
- * true).
- * '\b' Word boundary (between \w and \W).
- * '\B' Word non-boundary.
- *
- * quantatom: atom An unquantified atom.
- * quantatom '{' n ',' m '}'
- * Atom must occur between n and m times.
- * quantatom '{' n ',' '}' Atom must occur at least n times.
- * quantatom '{' n '}' Atom must occur exactly n times.
- * quantatom '*' Zero or more times (same as {0,}).
- * quantatom '+' One or more times (same as {1,}).
- * quantatom '?' Zero or one time (same as {0,1}).
- *
- * any of which can be optionally followed by '?' for ungreedy
- *
- * atom: '(' regexp ')' A parenthesized regexp (what matched
- * can be addressed using a backreference,
- * see '\' n below).
- * '.' Matches any char except '\n'.
- * '[' classlist ']' A character class.
- * '[' '^' classlist ']' A negated character class.
- * '\f' Form Feed.
- * '\n' Newline (Line Feed).
- * '\r' Carriage Return.
- * '\t' Horizontal Tab.
- * '\v' Vertical Tab.
- * '\d' A digit (same as [0-9]).
- * '\D' A non-digit.
- * '\w' A word character, [0-9a-z_A-Z].
- * '\W' A non-word character.
- * '\s' A whitespace character, [ \b\f\n\r\t\v].
- * '\S' A non-whitespace character.
- * '\' n A backreference to the nth (n decimal
- * and positive) parenthesized expression.
- * '\' octal An octal escape sequence (octal must be
- * two or three digits long, unless it is
- * 0 for the null character).
- * '\x' hex A hex escape (hex must be two digits).
- * '\u' unicode A unicode escape (must be four digits).
- * '\c' ctrl A control character, ctrl is a letter.
- * '\' literalatomchar Any character except one of the above
- * that follow '\' in an atom.
- * otheratomchar Any character not first among the other
- * atom right-hand sides.
- */
-static JSBool
-parseTerm(CompilerState *state)
-{
- jschar c = *state->cp++;
- uintN nDigits;
- uintN num, tmp, n, i;
- const jschar *termStart;
- JSBool foundCachedCopy;
-
- switch (c) {
- /* assertions and atoms */
- case '^':
- state->result = NewRENode(state, REOP_BOL);
- if (!state->result)
- return JS_FALSE;
- state->progLength++;
- return JS_TRUE;
- case '$':
- state->result = NewRENode(state, REOP_EOL);
- if (!state->result)
- return JS_FALSE;
- state->progLength++;
- return JS_TRUE;
- case '\\':
- if (state->cp >= state->cpend) {
- /* a trailing '\' is an error */
- js_ReportCompileErrorNumber(state->context, state->tokenStream,
- NULL, JSREPORT_ERROR,
- JSMSG_TRAILING_SLASH);
- return JS_FALSE;
- }
- c = *state->cp++;
- switch (c) {
- /* assertion escapes */
- case 'b' :
- state->result = NewRENode(state, REOP_WBDRY);
- if (!state->result)
- return JS_FALSE;
- state->progLength++;
- return JS_TRUE;
- case 'B':
- state->result = NewRENode(state, REOP_WNONBDRY);
- if (!state->result)
- return JS_FALSE;
- state->progLength++;
- return JS_TRUE;
- /* Decimal escape */
- case '0':
- if (JS_HAS_STRICT_OPTION(state->context))
- c = 0;
- else {
- doOctal:
- num = 0;
- while (state->cp < state->cpend) {
- if ('0' <= (c = *state->cp) && c <= '7') {
- state->cp++;
- tmp = 8 * num + (uintN)JS7_UNDEC(c);
- if (tmp > 0377)
- break;
- num = tmp;
- }
- else
- break;
- }
- c = (jschar)(num);
- }
- doFlat:
- state->result = NewRENode(state, REOP_FLAT);
- if (!state->result)
- return JS_FALSE;
- state->result->u.flat.chr = c;
- state->result->u.flat.length = 1;
- state->progLength += 3;
- break;
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- termStart = state->cp - 1;
- num = (uintN)getDecimalValue(c, state);
- if (num > 9 &&
- num > state->parenCount &&
- !JS_HAS_STRICT_OPTION(state->context)) {
- state->cp = termStart;
- goto doOctal;
- }
- state->result = NewRENode(state, REOP_BACKREF);
- if (!state->result)
- return JS_FALSE;
- state->result->u.parenIndex = num - 1;
- state->progLength += 3;
- break;
- /* Control escape */
- case 'f':
- c = 0xC;
- goto doFlat;
- case 'n':
- c = 0xA;
- goto doFlat;
- case 'r':
- c = 0xD;
- goto doFlat;
- case 't':
- c = 0x9;
- goto doFlat;
- case 'v':
- c = 0xB;
- goto doFlat;
- /* Control letter */
- case 'c':
- if (((state->cp + 1) < state->cpend) &&
- RE_IS_LETTER(state->cp[1]))
- c = (jschar)(*state->cp++ & 0x1F);
- else {
- /* back off to accepting the original '\' as a literal */
- --state->cp;
- c = '\\';
- }
- goto doFlat;
- /* HexEscapeSequence */
- case 'x':
- nDigits = 2;
- goto lexHex;
- /* UnicodeEscapeSequence */
- case 'u':
- nDigits = 4;
-lexHex:
- n = 0;
- for (i = 0; (i < nDigits)
- && (state->cp < state->cpend); i++) {
- uintN digit;
- c = *state->cp++;
- if (!isASCIIHexDigit(c, &digit)) {
- /*
- * back off to accepting the original
- * 'u' or 'x' as a literal
- */
- state->cp -= (i + 2);
- n = *state->cp++;
- break;
- }
- n = (n << 4) | digit;
- }
- c = (jschar)(n);
- goto doFlat;
- /* Character class escapes */
- case 'd':
- state->result = NewRENode(state, REOP_DIGIT);
-doSimple:
- if (!state->result)
- return JS_FALSE;
- state->progLength++;
- break;
- case 'D':
- state->result = NewRENode(state, REOP_NONDIGIT);
- goto doSimple;
- case 's':
- state->result = NewRENode(state, REOP_SPACE);
- goto doSimple;
- case 'S':
- state->result = NewRENode(state, REOP_NONSPACE);
- goto doSimple;
- case 'w':
- state->result = NewRENode(state, REOP_ALNUM);
- goto doSimple;
- case 'W':
- state->result = NewRENode(state, REOP_NONALNUM);
- goto doSimple;
- /* IdentityEscape */
- default:
- state->result = NewRENode(state, REOP_FLAT);
- if (!state->result)
- return JS_FALSE;
- state->result->u.flat.chr = c;
- state->result->u.flat.length = 1;
- state->result->kid = (void *)(state->cp - 1);
- state->progLength += 3;
- break;
- }
- break;
- case '[':
- state->result = NewRENode(state, REOP_CLASS);
- if (!state->result)
- return JS_FALSE;
- termStart = state->cp;
- state->result->u.ucclass.startIndex = termStart - state->cpbegin;
- while (JS_TRUE) {
- if (state->cp == state->cpend) {
- js_ReportCompileErrorNumber(state->context, state->tokenStream,
- NULL, JSREPORT_ERROR,
- JSMSG_UNTERM_CLASS, termStart);
- return JS_FALSE;
- }
- if (*state->cp == '\\')
- state->cp++;
- else {
- if (*state->cp == ']') {
- state->result->u.ucclass.kidlen = state->cp - termStart;
- break;
- }
- }
- state->cp++;
- }
- foundCachedCopy = JS_FALSE;
- for (i = 0; i < CLASS_CACHE_SIZE; i++) {
- if (state->classCache[i].start) {
- if (state->classCache[i].length == state->result->u.ucclass.kidlen) {
- foundCachedCopy = JS_TRUE;
- for (n = 0; n < state->classCache[i].length; n++) {
- if (state->classCache[i].start[n] != termStart[n]) {
- foundCachedCopy = JS_FALSE;
- break;
- }
- }
- if (foundCachedCopy) {
- state->result->u.ucclass.index = state->classCache[i].index;
- break;
- }
- }
- }
- else {
- state->classCache[i].start = termStart;
- state->classCache[i].length = state->result->u.ucclass.kidlen;
- state->classCache[i].index = state->classCount;
- break;
- }
- }
- if (!foundCachedCopy)
- state->result->u.ucclass.index = state->classCount++;
- /*
- * Call calculateBitmapSize now as we want any errors it finds
- * to be reported during the parse phase, not at execution.
- */
- if (!calculateBitmapSize(state, state->result, termStart, state->cp++))
- return JS_FALSE;
- state->progLength += 3; /* CLASS, <index> */
- break;
-
- case '.':
- state->result = NewRENode(state, REOP_DOT);
- goto doSimple;
- case '*':
- case '+':
- case '?':
- js_ReportCompileErrorNumber(state->context, state->tokenStream,
- NULL, JSREPORT_ERROR,
- JSMSG_BAD_QUANTIFIER, state->cp - 1);
- return JS_FALSE;
-#if 0
- case '{': /* balance '}' */
- /* Treat left-curly in a non-quantifier context as an error only
- * if it's followed immediately by a decimal digit.
- * This is an Perl extension.
- */
- if ((state->cp != state->cpend) && JS7_ISDEC(*state->cp)) {
- js_ReportCompileErrorNumber(state->context, state->tokenStream,
- NULL, JSREPORT_ERROR,
- JSMSG_BAD_QUANTIFIER, state->cp - 1);
- return JS_FALSE;
- }
- /* fall thru... */
-#endif
- default:
- state->result = NewRENode(state, REOP_FLAT);
- if (!state->result)
- return JS_FALSE;
- state->result->u.flat.chr = c;
- state->result->u.flat.length = 1;
- state->result->kid = (void *)(state->cp - 1);
- state->progLength += 3;
- break;
- }
- return parseQuantifier(state);
-}
-
-static JSBool
-parseQuantifier(CompilerState *state)
-{
- RENode *term;
- term = state->result;
- if (state->cp < state->cpend) {
- switch (*state->cp) {
- case '+':
- state->result = NewRENode(state, REOP_QUANT);
- if (!state->result)
- return JS_FALSE;
- state->result->u.range.min = 1;
- state->result->u.range.max = -1;
- /* <PLUS>, <next> ... <ENDCHILD> */
- state->progLength += 4;
- goto quantifier;
- case '*':
- state->result = NewRENode(state, REOP_QUANT);
- if (!state->result)
- return JS_FALSE;
- state->result->u.range.min = 0;
- state->result->u.range.max = -1;
- /* <STAR>, <next> ... <ENDCHILD> */
- state->progLength += 4;
- goto quantifier;
- case '?':
- state->result = NewRENode(state, REOP_QUANT);
- if (!state->result)
- return JS_FALSE;
- state->result->u.range.min = 0;
- state->result->u.range.max = 1;
- /* <OPT>, <next> ... <ENDCHILD> */
- state->progLength += 4;
- goto quantifier;
- case '{': /* balance '}' */
- {
- intN err;
- intN min = 0;
- intN max = -1;
- jschar c;
- const jschar *errp = state->cp++;
-
- c = *state->cp;
- if (JS7_ISDEC(c)) {
- ++state->cp;
- min = getDecimalValue(c, state);
- c = *state->cp;
-
- if ((min + 1) >> 16) {
- err = JSMSG_MIN_TOO_BIG;
- goto quantError;
- }
- if (c == ',') {
- c = *++state->cp;
- if (JS7_ISDEC(c)) {
- ++state->cp;
- max = getDecimalValue(c, state);
- c = *state->cp;
- if ((max + 1) >> 16) {
- err = JSMSG_MAX_TOO_BIG;
- goto quantError;
- }
- if (min > max) {
- err = JSMSG_OUT_OF_ORDER;
- goto quantError;
- }
- }
- }
- else {
- max = min;
- }
- if (c == '}') {
- state->result = NewRENode(state, REOP_QUANT);
- if (!state->result)
- return JS_FALSE;
- state->result->u.range.min = min;
- state->result->u.range.max = max;
- /* QUANT, <min>, <max>, <next> ... <ENDCHILD> */
- state->progLength += 8;
- goto quantifier;
- }
- }
- state->cp = errp;
- return JS_TRUE;
-quantError:
- js_ReportCompileErrorNumber(state->context,
- state->tokenStream,
- NULL, JSREPORT_ERROR,
- err, errp);
- return JS_FALSE;
- }
- }
- }
- return JS_TRUE;
-
-quantifier:
- ++state->treeDepth;
- ++state->cp;
- state->result->kid = term;
- if ((state->cp < state->cpend) && (*state->cp == '?')) {
- ++state->cp;
- state->result->u.range.greedy = JS_FALSE;
- }
- else
- state->result->u.range.greedy = JS_TRUE;
- return JS_TRUE;
-}
-
-#define CHECK_OFFSET(diff) (JS_ASSERT(((diff) >= -32768) && ((diff) <= 32767)))
-#define SET_OFFSET(pc,off) ((pc)[0] = JUMP_OFFSET_HI(off), \
- (pc)[1] = JUMP_OFFSET_LO(off))
-#define GET_OFFSET(pc) ((int16)(((pc)[0] << 8) | (pc)[1]))
-#define OFFSET_LEN (2)
-#define GET_ARG(pc) GET_OFFSET(pc)
-#define SET_ARG(pc,arg) SET_OFFSET(pc,arg)
-#define ARG_LEN OFFSET_LEN
-
-/*
- * Recursively generate bytecode for the tree rooted at t. Iteratively.
- */
-
-typedef struct {
- RENode *nextAlt;
- jsbytecode *nextAltFixup, *nextTermFixup, *endTermFixup;
- RENode *continueNode;
- REOp continueOp;
-} EmitStateStackEntry;
-
-static jsbytecode *
-emitREBytecode(CompilerState *state, JSRegExp *re, intN treeDepth,
- jsbytecode *pc, RENode *t)
-{
- ptrdiff_t diff;
- RECharSet *charSet;
- EmitStateStackEntry *emitStateSP, *emitStateStack = NULL;
- REOp op;
-
- if (treeDepth) {
- emitStateStack =
- (EmitStateStackEntry *)JS_malloc(state->context,
- sizeof(EmitStateStackEntry)
- * treeDepth);
- if (!emitStateStack)
- return NULL;
- }
- emitStateSP = emitStateStack;
- op = t->op;
-
- while (JS_TRUE) {
- *pc++ = op;
- switch (op) {
- case REOP_EMPTY:
- --pc;
- break;
-
- case REOP_ALTPREREQ2:
- case REOP_ALTPREREQ:
- JS_ASSERT(emitStateSP);
- emitStateSP->endTermFixup = pc;
- pc += OFFSET_LEN;
- SET_ARG(pc, t->u.altprereq.ch1);
- pc += ARG_LEN;
- SET_ARG(pc, t->u.altprereq.ch2);
- pc += ARG_LEN;
-
- emitStateSP->nextAltFixup = pc; /* address of next alternate */
- pc += OFFSET_LEN;
-
- emitStateSP->continueNode = t;
- emitStateSP->continueOp = REOP_JUMP;
- ++emitStateSP;
- JS_ASSERT((emitStateSP - emitStateStack) <= treeDepth);
- t = (RENode *)(t->kid);
- op = t->op;
- continue;
-
- case REOP_JUMP:
- emitStateSP->nextTermFixup = pc; /* address of following term */
- pc += OFFSET_LEN;
- diff = pc - emitStateSP->nextAltFixup;
- CHECK_OFFSET(diff);
- SET_OFFSET(emitStateSP->nextAltFixup, diff);
- emitStateSP->continueOp = REOP_ENDALT;
- ++emitStateSP;
- JS_ASSERT((emitStateSP - emitStateStack) <= treeDepth);
- t = (RENode *)(t->u.kid2);
- op = t->op;
- continue;
-
- case REOP_ENDALT:
- diff = pc - emitStateSP->nextTermFixup;
- CHECK_OFFSET(diff);
- SET_OFFSET(emitStateSP->nextTermFixup, diff);
- if (t->op != REOP_ALT) {
- diff = pc - emitStateSP->endTermFixup;
- CHECK_OFFSET(diff);
- SET_OFFSET(emitStateSP->endTermFixup, diff);
- }
- break;
-
- case REOP_ALT:
- JS_ASSERT(emitStateSP);
- emitStateSP->nextAltFixup = pc; /* address of pointer to next alternate */
- pc += OFFSET_LEN;
- emitStateSP->continueNode = t;
- emitStateSP->continueOp = REOP_JUMP;
- ++emitStateSP;
- JS_ASSERT((emitStateSP - emitStateStack) <= treeDepth);
- t = (RENode *)(t->kid);
- op = t->op;
- continue;
-
- case REOP_FLAT:
- /*
- * Consecutize FLAT's if possible.
- */
- if (t->kid) {
- while (t->next && (t->next->op == REOP_FLAT)
- && (((jschar*)(t->kid) + t->u.flat.length)
- == (jschar*)(t->next->kid))) {
- t->u.flat.length += t->next->u.flat.length;
- t->next = t->next->next;
- }
- }
- if (t->kid && (t->u.flat.length > 1)) {
- if (state->flags & JSREG_FOLD)
- pc[-1] = REOP_FLATi;
- else
- pc[-1] = REOP_FLAT;
- SET_ARG(pc, (jschar *)(t->kid) - state->cpbegin);
- pc += ARG_LEN;
- SET_ARG(pc, t->u.flat.length);
- pc += ARG_LEN;
- }
- else {
- if (t->u.flat.chr < 256) {
- if (state->flags & JSREG_FOLD)
- pc[-1] = REOP_FLAT1i;
- else
- pc[-1] = REOP_FLAT1;
- *pc++ = (jsbytecode)(t->u.flat.chr);
- }
- else {
- if (state->flags & JSREG_FOLD)
- pc[-1] = REOP_UCFLAT1i;
- else
- pc[-1] = REOP_UCFLAT1;
- SET_ARG(pc, t->u.flat.chr);
- pc += ARG_LEN;
- }
- }
- break;
-
- case REOP_LPAREN:
- JS_ASSERT(emitStateSP);
- SET_ARG(pc, t->u.parenIndex);
- pc += ARG_LEN;
- emitStateSP->continueNode = t;
- emitStateSP->continueOp = REOP_RPAREN;
- ++emitStateSP;
- JS_ASSERT((emitStateSP - emitStateStack) <= treeDepth);
- t = (RENode *)(t->kid);
- op = t->op;
- continue;
- case REOP_RPAREN:
- SET_ARG(pc, t->u.parenIndex);
- pc += ARG_LEN;
- break;
-
- case REOP_BACKREF:
- SET_ARG(pc, t->u.parenIndex);
- pc += ARG_LEN;
- break;
- case REOP_ASSERT:
- JS_ASSERT(emitStateSP);
- emitStateSP->nextTermFixup = pc;
- pc += OFFSET_LEN;
- emitStateSP->continueNode = t;
- emitStateSP->continueOp = REOP_ASSERTTEST;
- ++emitStateSP;
- JS_ASSERT((emitStateSP - emitStateStack) <= treeDepth);
- t = (RENode *)(t->kid);
- op = t->op;
- continue;
- case REOP_ASSERTTEST:
- case REOP_ASSERTNOTTEST:
- diff = pc - emitStateSP->nextTermFixup;
- CHECK_OFFSET(diff);
- SET_OFFSET(emitStateSP->nextTermFixup, diff);
- break;
- case REOP_ASSERT_NOT:
- JS_ASSERT(emitStateSP);
- emitStateSP->nextTermFixup = pc;
- pc += OFFSET_LEN;
- emitStateSP->continueNode = t;
- emitStateSP->continueOp = REOP_ASSERTNOTTEST;
- ++emitStateSP;
- JS_ASSERT((emitStateSP - emitStateStack) <= treeDepth);
- t = (RENode *)(t->kid);
- op = t->op;
- continue;
- case REOP_QUANT:
- JS_ASSERT(emitStateSP);
- if ((t->u.range.min == 0) && (t->u.range.max == (uint16)(-1)))
- pc[-1] = (t->u.range.greedy) ? REOP_STAR : REOP_MINIMALSTAR;
- else
- if ((t->u.range.min == 0) && (t->u.range.max == 1))
- pc[-1] = (t->u.range.greedy) ? REOP_OPT : REOP_MINIMALOPT;
- else
- if ((t->u.range.min == 1) && (t->u.range.max == (uint16)(-1)))
- pc[-1] = (t->u.range.greedy) ? REOP_PLUS : REOP_MINIMALPLUS;
- else {
- if (!t->u.range.greedy) pc[-1] = REOP_MINIMALQUANT;
- SET_ARG(pc, t->u.range.min);
- pc += ARG_LEN;
- SET_ARG(pc, t->u.range.max);
- pc += ARG_LEN;
- }
- emitStateSP->nextTermFixup = pc;
- pc += OFFSET_LEN;
- emitStateSP->continueNode = t;
- emitStateSP->continueOp = REOP_ENDCHILD;
- ++emitStateSP;
- JS_ASSERT((emitStateSP - emitStateStack) <= treeDepth);
- t = (RENode *)(t->kid);
- op = t->op;
- continue;
- case REOP_ENDCHILD:
- diff = pc - emitStateSP->nextTermFixup;
- CHECK_OFFSET(diff);
- SET_OFFSET(emitStateSP->nextTermFixup, diff);
- break;
- case REOP_CLASS:
- if (!t->u.ucclass.sense)
- pc[-1] = REOP_NCLASS;
- SET_ARG(pc, t->u.ucclass.index);
- pc += ARG_LEN;
- charSet = &re->classList[t->u.ucclass.index];
- charSet->converted = JS_FALSE;
- charSet->length = t->u.ucclass.bmsize;
- charSet->u.src.startIndex = t->u.ucclass.startIndex;
- charSet->u.src.length = t->u.ucclass.kidlen;
- charSet->sense = t->u.ucclass.sense;
- break;
- default:
- break;
- }
- t = t->next;
- if (t == NULL) {
- if (emitStateSP == emitStateStack)
- break;
- --emitStateSP;
- t = emitStateSP->continueNode;
- op = emitStateSP->continueOp;
- }
- else
- op = t->op;
- }
- if (emitStateStack)
- JS_free(state->context, emitStateStack);
- return pc;
-}
-
-
-JSRegExp *
-js_NewRegExp(JSContext *cx, JSTokenStream *ts,
- JSString *str, uintN flags, JSBool flat)
-{
- JSRegExp *re;
- void *mark;
- CompilerState state;
- size_t resize;
- jsbytecode *endPC;
- uint32 i;
- size_t len;
-
- re = NULL;
- mark = JS_ARENA_MARK(&cx->tempPool);
-
- state.context = cx;
- state.tokenStream = ts;
- state.cpbegin = state.cp = JSSTRING_CHARS(str);
- state.cpend = state.cp + JSSTRING_LENGTH(str);
- state.flags = flags;
- state.parenCount = 0;
- state.classCount = 0;
- state.progLength = 0;
- state.treeDepth = 0;
- for (i = 0; i < CLASS_CACHE_SIZE; i++)
- state.classCache[i].start = NULL;
-
- len = JSSTRING_LENGTH(str);
-
- if (len != 0 && flat) {
- state.result = NewRENode(&state, REOP_FLAT);
- state.result->u.flat.chr = *state.cpbegin;
- state.result->u.flat.length = JSSTRING_LENGTH(str);
- state.result->kid = (void *)(state.cpbegin);
- state.progLength += 5;
- }
- else {
- if (!parseRegExp(&state))
- goto out;
- }
- resize = sizeof *re + state.progLength + 1;
- re = (JSRegExp *) JS_malloc(cx, JS_ROUNDUP(resize, sizeof(jsword)));
- if (!re)
- goto out;
-
- re->classCount = state.classCount;
- if (state.classCount) {
- re->classList = (RECharSet *)JS_malloc(cx, sizeof(RECharSet)
- * state.classCount);
- if (!re->classList)
- goto out;
- }
- else
- re->classList = NULL;
- endPC = emitREBytecode(&state, re, state.treeDepth, re->program, state.result);
- if (!endPC) {
- re = NULL;
- goto out;
- }
- *endPC++ = REOP_END;
- JS_ASSERT(endPC <= (re->program + (state.progLength + 1)));
-
- re->nrefs = 1;
- re->parenCount = state.parenCount;
- re->flags = flags;
- re->source = str;
-
-out:
- JS_ARENA_RELEASE(&cx->tempPool, mark);
- return re;
-}
-
-JSRegExp *
-js_NewRegExpOpt(JSContext *cx, JSTokenStream *ts,
- JSString *str, JSString *opt, JSBool flat)
-{
- uintN flags;
- jschar *s;
- size_t i, n;
- char charBuf[2];
-
- flags = 0;
- if (opt) {
- s = JSSTRING_CHARS(opt);
- for (i = 0, n = JSSTRING_LENGTH(opt); i < n; i++) {
- switch (s[i]) {
- case 'g':
- flags |= JSREG_GLOB;
- break;
- case 'i':
- flags |= JSREG_FOLD;
- break;
- case 'm':
- flags |= JSREG_MULTILINE;
- break;
- default:
- charBuf[0] = (char)s[i];
- charBuf[1] = '\0';
- js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
- JSMSG_BAD_FLAG, charBuf);
- return NULL;
- }
- }
- }
- return js_NewRegExp(cx, ts, str, flags, flat);
-}
-
-
-#define HOLD_REGEXP(cx, re) JS_ATOMIC_INCREMENT(&(re)->nrefs)
-#define DROP_REGEXP(cx, re) js_DestroyRegExp(cx, re)
-
-/*
- * Save the current state of the match - the position in the input
- * text as well as the position in the bytecode. The state of any
- * parent expressions is also saved (preceding state).
- * Contents of parenCount parentheses from parenIndex are also saved.
- */
-static REBackTrackData *
-pushBackTrackState(REGlobalData *gData, REOp op,
- jsbytecode *target, REMatchState *x, const jschar *cp,
- intN parenIndex, intN parenCount)
-{
- intN i;
- REBackTrackData *result
- = (REBackTrackData *)((char *)(gData->backTrackSP) + gData->cursz);
-
- size_t sz = sizeof(REBackTrackData)
- + gData->stateStackTop * sizeof(REProgState)
- + parenCount * sizeof(RECapture);
-
-
- if (((char *)result + sz)
- > (char *)gData->backTrackStack + gData->maxBackTrack) {
- ptrdiff_t offset = (char *)result - (char *)gData->backTrackStack;
- gData->backTrackStack
- = (REBackTrackData *)JS_ArenaGrow(&gData->pool,
- gData->backTrackStack,
- gData->maxBackTrack,
- gData->maxBackTrack);
- gData->maxBackTrack <<= 1;
- if (!gData->backTrackStack)
- return NULL;
- result = (REBackTrackData *)((char *)gData->backTrackStack + offset);
- }
- gData->backTrackSP = result;
- result->sz = gData->cursz;
- gData->cursz = sz;
-
- result->backtrack_op = op;
- result->backtrack_pc = target;
- result->cp = cp;
- result->parenCount = parenCount;
-
- result->precedingStateTop = gData->stateStackTop;
- JS_ASSERT(gData->stateStackTop);
- memcpy(result + 1, gData->stateStack,
- sizeof(REProgState) * result->precedingStateTop);
-
- if (parenCount != -1) {
- result->parenIndex = parenIndex;
- memcpy((char *)(result + 1)
- + sizeof(REProgState) * result->precedingStateTop,
- &x->parens[parenIndex],
- sizeof(RECapture) * parenCount);
- for (i = 0; i < parenCount; i++)
- x->parens[parenIndex + i].index = -1;
- }
-
- return result;
-}
-
-
-/*
- * Consecutive literal characters.
- */
-#if 0
-static REMatchState *
-flatNMatcher(REGlobalData *gData, REMatchState *x, jschar *matchChars,
- intN length)
-{
- intN i;
- if ((x->cp + length) > gData->cpend)
- return NULL;
- for (i = 0; i < length; i++) {
- if (matchChars[i] != x->cp[i])
- return NULL;
- }
- x->cp += length;
- return x;
-}
-#endif
-
-static REMatchState *
-flatNIMatcher(REGlobalData *gData, REMatchState *x, jschar *matchChars,
- intN length)
-{
- intN i;
- if ((x->cp + length) > gData->cpend)
- return NULL;
- for (i = 0; i < length; i++) {
- if (upcase(matchChars[i]) != upcase(x->cp[i]))
- return NULL;
- }
- x->cp += length;
- return x;
-}
-
-/*
- * 1. Evaluate DecimalEscape to obtain an EscapeValue E.
- * 2. If E is not a character then go to step 6.
- * 3. Let ch be E's character.
- * 4. Let A be a one-element RECharSet containing the character ch.
- * 5. Call CharacterSetMatcher(A, false) and return its Matcher result.
- * 6. E must be an integer. Let n be that integer.
- * 7. If n=0 or n>NCapturingParens then throw a SyntaxError exception.
- * 8. Return an internal Matcher closure that takes two arguments, a State x
- * and a Continuation c, and performs the following:
- * 1. Let cap be x's captures internal array.
- * 2. Let s be cap[n].
- * 3. If s is undefined, then call c(x) and return its result.
- * 4. Let e be x's endIndex.
- * 5. Let len be s's length.
- * 6. Let f be e+len.
- * 7. If f>InputLength, return failure.
- * 8. If there exists an integer i between 0 (inclusive) and len (exclusive)
- * such that Canonicalize(s[i]) is not the same character as
- * Canonicalize(Input [e+i]), then return failure.
- * 9. Let y be the State (f, cap).
- * 10. Call c(y) and return its result.
- */
-static REMatchState *
-backrefMatcher(REGlobalData *gData, REMatchState *x, uintN parenIndex)
-{
- uintN len;
- uintN i;
- const jschar *parenContent;
- RECapture *s = &x->parens[parenIndex];
- if (s->index == -1)
- return x;
-
- len = s->length;
- if ((x->cp + len) > gData->cpend)
- return NULL;
-
- parenContent = &gData->cpbegin[s->index];
- if (gData->regexp->flags & JSREG_FOLD) {
- for (i = 0; i < len; i++) {
- if (upcase(parenContent[i]) != upcase(x->cp[i]))
- return NULL;
- }
- }
- else {
- for (i = 0; i < len; i++) {
- if (parenContent[i] != x->cp[i])
- return NULL;
- }
- }
- x->cp += len;
- return x;
-}
-
-
-/* Add a single character to the RECharSet */
-static void
-addCharacterToCharSet(RECharSet *cs, jschar c)
-{
- uintN byteIndex = (uintN)(c / 8);
- JS_ASSERT(c <= cs->length);
- cs->u.bits[byteIndex] |= 1 << (c & 0x7);
-}
-
-
-/* Add a character range, c1 to c2 (inclusive) to the RECharSet */
-static void
-addCharacterRangeToCharSet(RECharSet *cs, jschar c1, jschar c2)
-{
- uintN i;
-
- uintN byteIndex1 = (uintN)(c1 / 8);
- uintN byteIndex2 = (uintN)(c2 / 8);
-
- JS_ASSERT((c2 <= cs->length) && (c1 <= c2));
-
- c1 &= 0x7;
- c2 &= 0x7;
-
- if (byteIndex1 == byteIndex2)
- cs->u.bits[byteIndex1] |= ((uint8)(0xFF) >> (7 - (c2 - c1))) << c1;
- else {
- cs->u.bits[byteIndex1] |= 0xFF << c1;
- for (i = byteIndex1 + 1; i < byteIndex2; i++)
- cs->u.bits[i] = 0xFF;
- cs->u.bits[byteIndex2] |= (uint8)(0xFF) >> (7 - c2);
- }
-}
-
-/* Compile the source of the class into a RECharSet */
-static JSBool
-processCharSet(REGlobalData *gData, RECharSet *charSet)
-{
- const jschar *src = JSSTRING_CHARS(gData->regexp->source)
- + charSet->u.src.startIndex;
- const jschar *end = src + charSet->u.src.length;
-
- jschar rangeStart, thisCh;
- uintN byteLength;
- jschar c;
- uintN n;
- intN nDigits;
- intN i;
- JSBool inRange = JS_FALSE;
-
- JS_ASSERT(!charSet->converted);
- charSet->converted = JS_TRUE;
-
- byteLength = (charSet->length / 8) + 1;
- charSet->u.bits = (uint8 *)JS_malloc(gData->cx, byteLength);
- if (!charSet->u.bits)
- return JS_FALSE;
- memset(charSet->u.bits, 0, byteLength);
-
- if (src == end)
- return JS_TRUE;
-
- if (*src == '^') {
- JS_ASSERT(charSet->sense == JS_FALSE);
- ++src;
- }
- else
- JS_ASSERT(charSet->sense == JS_TRUE);
-
-
- while (src != end) {
- switch (*src) {
- case '\\':
- ++src;
- c = *src++;
- switch (c) {
- case 'b':
- thisCh = 0x8;
- break;
- case 'f':
- thisCh = 0xC;
- break;
- case 'n':
- thisCh = 0xA;
- break;
- case 'r':
- thisCh = 0xD;
- break;
- case 't':
- thisCh = 0x9;
- break;
- case 'v':
- thisCh = 0xB;
- break;
- case 'c':
- if (((src + 1) < end) && JS_ISWORD(src[1]))
- thisCh = (jschar)(*src++ & 0x1F);
- else {
- --src;
- thisCh = '\\';
- }
- break;
- case 'x':
- nDigits = 2;
- goto lexHex;
- case 'u':
- nDigits = 4;
-lexHex:
- n = 0;
- for (i = 0; (i < nDigits) && (src < end); i++) {
- uintN digit;
- c = *src++;
- if (!isASCIIHexDigit(c, &digit)) {
- /*
- * Back off to accepting the original '\'
- * as a literal
- */
- src -= (i + 1);
- n = '\\';
- break;
- }
- n = (n << 4) | digit;
- }
- thisCh = (jschar)(n);
- break;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- /*
- * This is a non-ECMA extension - decimal escapes (in this
- * case, octal!) are supposed to be an error inside class
- * ranges, but supported here for backwards compatibility.
- *
- */
- n = JS7_UNDEC(c);
- c = *src;
- if ('0' <= c && c <= '7') {
- src++;
- n = 8 * n + JS7_UNDEC(c);
- c = *src;
- if ('0' <= c && c <= '7') {
- src++;
- i = 8 * n + JS7_UNDEC(c);
- if (i <= 0377)
- n = i;
- else
- src--;
- }
- }
- thisCh = (jschar)(n);
- break;
-
- case 'd':
- addCharacterRangeToCharSet(charSet, '0', '9');
- continue; /* don't need range processing */
- case 'D':
- addCharacterRangeToCharSet(charSet, 0, '0' - 1);
- addCharacterRangeToCharSet(charSet, (jschar)('9' + 1),
- (jschar)(charSet->length));
- continue;
- case 's':
- for (i = (intN)(charSet->length); i >= 0; i--)
- if (JS_ISSPACE(i))
- addCharacterToCharSet(charSet, (jschar)(i));
- continue;
- case 'S':
- for (i = (intN)(charSet->length); i >= 0; i--)
- if (!JS_ISSPACE(i))
- addCharacterToCharSet(charSet, (jschar)(i));
- continue;
- case 'w':
- for (i = (intN)(charSet->length); i >= 0; i--)
- if (JS_ISWORD(i))
- addCharacterToCharSet(charSet, (jschar)(i));
- continue;
- case 'W':
- for (i = (intN)(charSet->length); i >= 0; i--)
- if (!JS_ISWORD(i))
- addCharacterToCharSet(charSet, (jschar)(i));
- continue;
- default:
- thisCh = c;
- break;
-
- }
- break;
-
- default:
- thisCh = *src++;
- break;
-
- }
- if (inRange) {
- if (gData->regexp->flags & JSREG_FOLD) {
- addCharacterRangeToCharSet(charSet, upcase(rangeStart),
- upcase(thisCh));
- addCharacterRangeToCharSet(charSet, downcase(rangeStart),
- downcase(thisCh));
- } else {
- addCharacterRangeToCharSet(charSet, rangeStart, thisCh);
- }
- inRange = JS_FALSE;
- }
- else {
- if (gData->regexp->flags & JSREG_FOLD) {
- addCharacterToCharSet(charSet, upcase(thisCh));
- addCharacterToCharSet(charSet, downcase(thisCh));
- } else {
- addCharacterToCharSet(charSet, thisCh);
- }
- if (src < (end - 1)) {
- if (*src == '-') {
- ++src;
- inRange = JS_TRUE;
- rangeStart = thisCh;
- }
- }
- }
- }
- return JS_TRUE;
-}
-
-void
-js_DestroyRegExp(JSContext *cx, JSRegExp *re)
-{
- uintN i;
- if (JS_ATOMIC_DECREMENT(&re->nrefs) == 0) {
- if (re->classList) {
- for (i = 0; i < re->classCount; i++) {
- if (re->classList[i].converted)
- JS_free(cx, re->classList[i].u.bits);
- re->classList[i].u.bits = NULL;
- }
- JS_free(cx, re->classList);
- }
- JS_free(cx, re);
- }
-}
-
-static JSBool
-reallocStateStack(REGlobalData *gData)
-{
- size_t sz = sizeof(REProgState) * gData->maxStateStack;
- gData->maxStateStack <<= 1;
- gData->stateStack
- = (REProgState *)JS_ArenaGrow(&gData->pool, gData->stateStack, sz, sz);
- if (!gData->stateStack) {
- gData->ok = JS_FALSE;
- return JS_FALSE;
- }
- return JS_TRUE;
-}
-
-/*
-* Apply the current op against the given input to see if
-* it's going to match or fail. Return false if we don't
-* get a match, true if we do and update the state of the
-* input and pc if the update flag is true.
-*/
-static REMatchState *simpleMatch(REGlobalData *gData, REMatchState *x,
- REOp op, jsbytecode **startpc, JSBool update)
-{
- REMatchState *result = NULL;
- jschar matchCh;
- intN parenIndex;
- intN offset, length, index;
- jsbytecode *pc = *startpc; /* pc has already been incremented past op */
- jschar *source;
- const jschar *startcp = x->cp;
- jschar ch;
- RECharSet *charSet;
-
-
- switch (op) {
- default:
- JS_ASSERT(JS_FALSE);
- case REOP_BOL:
- if (x->cp != gData->cpbegin) {
- if (gData->cx->regExpStatics.multiline ||
- (gData->regexp->flags & JSREG_MULTILINE)) {
- if (!RE_IS_LINE_TERM(x->cp[-1]))
- break;
- }
- else
- break;
- }
- result = x;
- break;
- case REOP_EOL:
- if (x->cp != gData->cpend) {
- if (gData->cx->regExpStatics.multiline ||
- (gData->regexp->flags & JSREG_MULTILINE)) {
- if (!RE_IS_LINE_TERM(*x->cp))
- break;
- }
- else
- break;
- }
- result = x;
- break;
- case REOP_WBDRY:
- if ((x->cp == gData->cpbegin || !JS_ISWORD(x->cp[-1]))
- ^ !((x->cp != gData->cpend) && JS_ISWORD(*x->cp)))
- result = x;
- break;
- case REOP_WNONBDRY:
- if ((x->cp == gData->cpbegin || !JS_ISWORD(x->cp[-1]))
- ^ ((x->cp != gData->cpend) && JS_ISWORD(*x->cp)))
- result = x;
- break;
- case REOP_DOT:
- if (x->cp != gData->cpend && !RE_IS_LINE_TERM(*x->cp)) {
- result = x;
- result->cp++;
- }
- break;
- case REOP_DIGIT:
- if (x->cp != gData->cpend && JS_ISDIGIT(*x->cp)) {
- result = x;
- result->cp++;
- }
- break;
- case REOP_NONDIGIT:
- if (x->cp != gData->cpend && !JS_ISDIGIT(*x->cp)) {
- result = x;
- result->cp++;
- }
- break;
- case REOP_ALNUM:
- if (x->cp != gData->cpend && JS_ISWORD(*x->cp)) {
- result = x;
- result->cp++;
- }
- break;
- case REOP_NONALNUM:
- if (x->cp != gData->cpend && !JS_ISWORD(*x->cp)) {
- result = x;
- result->cp++;
- }
- break;
- case REOP_SPACE:
- if (x->cp != gData->cpend && JS_ISSPACE(*x->cp)) {
- result = x;
- result->cp++;
- }
- break;
- case REOP_NONSPACE:
- if (x->cp != gData->cpend && !JS_ISSPACE(*x->cp)) {
- result = x;
- result->cp++;
- }
- break;
- case REOP_BACKREF:
- parenIndex = GET_ARG(pc);
- pc += ARG_LEN;
- result = backrefMatcher(gData, x, parenIndex);
- break;
- case REOP_FLAT:
- offset = GET_ARG(pc);
- pc += ARG_LEN;
- length = GET_ARG(pc);
- pc += ARG_LEN;
- source = JSSTRING_CHARS(gData->regexp->source) + offset;
- if ((x->cp + length) <= gData->cpend) {
- for (index = 0; index < length; index++) {
- if (source[index] != x->cp[index])
- return NULL;
- }
- x->cp += length;
- result = x;
- }
- break;
- case REOP_FLAT1:
- matchCh = *pc++;
- if ((x->cp != gData->cpend) && (*x->cp == matchCh)) {
- result = x;
- result->cp++;
- }
- break;
- case REOP_FLATi:
- offset = GET_ARG(pc);
- pc += ARG_LEN;
- length = GET_ARG(pc);
- pc += ARG_LEN;
- source = JSSTRING_CHARS(gData->regexp->source);
- result = flatNIMatcher(gData, x, source + offset, length);
- break;
- case REOP_FLAT1i:
- matchCh = *pc++;
- if ((x->cp != gData->cpend) && (upcase(*x->cp) == upcase(matchCh))) {
- result = x;
- result->cp++;
- }
- break;
- case REOP_UCFLAT1:
- matchCh = GET_ARG(pc);
- pc += ARG_LEN;
- if ((x->cp != gData->cpend) && (*x->cp == matchCh)) {
- result = x;
- result->cp++;
- }
- break;
- case REOP_UCFLAT1i:
- matchCh = GET_ARG(pc);
- pc += ARG_LEN;
- if ((x->cp != gData->cpend) && (upcase(*x->cp) == upcase(matchCh))) {
- result = x;
- result->cp++;
- }
- break;
- case REOP_CLASS:
- index = GET_ARG(pc);
- pc += ARG_LEN;
- if (x->cp != gData->cpend) {
- charSet = &gData->regexp->classList[index];
- JS_ASSERT(charSet->converted);
- ch = *x->cp;
- index = ch / 8;
- if ((charSet->length != 0) &&
- ( (ch <= charSet->length)
- && ((charSet->u.bits[index] & (1 << (ch & 0x7))) != 0) )) {
- result = x;
- result->cp++;
- }
- }
- break;
- case REOP_NCLASS:
- index = GET_ARG(pc);
- pc += ARG_LEN;
- if (x->cp != gData->cpend) {
- charSet = &gData->regexp->classList[index];
- JS_ASSERT(charSet->converted);
- ch = *x->cp;
- index = ch / 8;
- if ((charSet->length == 0) ||
- ( (ch > charSet->length)
- || ((charSet->u.bits[index] & (1 << (ch & 0x7))) == 0) )) {
- result = x;
- result->cp++;
- }
- }
- break;
- }
- if (result != NULL) {
- if (update)
- *startpc = pc;
- else
- x->cp = startcp;
- return result;
- }
- x->cp = startcp;
- return NULL;
-}
-
-static REMatchState *
-executeREBytecode(REGlobalData *gData, REMatchState *x)
-{
- REMatchState *result;
- REBackTrackData *backTrackData;
- intN offset;
- jsbytecode *nextpc;
- REOp nextop;
- RECapture *cap;
- REProgState *curState;
- const jschar *startcp;
- uintN parenIndex, k;
- uintN parenSoFar = 0;
-
- jschar matchCh1, matchCh2;
- RECharSet *charSet;
-
- JSBool anchor;
- jsbytecode *pc = gData->regexp->program;
- REOp op = (REOp)(*pc++);
-
- /*
- * If the first node is a simple match, step the index into
- * the string until that match is made, or fail if it can't be
- * found at all.
- */
- if (REOP_IS_SIMPLE(op)) {
- anchor = JS_FALSE;
- while (x->cp <= gData->cpend) {
- nextpc = pc; /* reset back to start each time */
- result = simpleMatch(gData, x, op, &nextpc, JS_TRUE);
- if (result) {
- anchor = JS_TRUE;
- x = result;
- pc = nextpc; /* accept skip to next opcode */
- op = (REOp)(*pc++);
- break;
- }
- else {
- gData->skipped++;
- x->cp++;
- }
- }
- if (!anchor)
- return NULL;
- }
-
- while (JS_TRUE) {
- if (REOP_IS_SIMPLE(op))
- result = simpleMatch(gData, x, op, &pc, JS_TRUE);
- else {
- curState = &gData->stateStack[gData->stateStackTop];
- switch (op) {
- case REOP_EMPTY:
- result = x;
- break;
-
- case REOP_ALTPREREQ2:
- nextpc = pc + GET_OFFSET(pc); /* start of next op */
- pc += ARG_LEN;
- matchCh2 = GET_ARG(pc);
- pc += ARG_LEN;
- k = GET_ARG(pc);
- pc += ARG_LEN;
-
- if (x->cp != gData->cpend) {
- if (*x->cp == matchCh2)
- goto doAlt;
-
- charSet = &gData->regexp->classList[k];
- if (!charSet->converted)
- if (!processCharSet(gData, charSet))
- return NULL;
- matchCh1 = *x->cp;
- k = matchCh1 / 8;
- if ((charSet->length == 0 ||
- matchCh1 > charSet->length ||
- (charSet->u.bits[k] & (1 << (matchCh1 & 0x7))) == 0)
- ^ charSet->sense) {
- goto doAlt;
- }
- }
- result = NULL;
- break;
-
- case REOP_ALTPREREQ:
- nextpc = pc + GET_OFFSET(pc); /* start of next op */
- pc += ARG_LEN;
- matchCh1 = GET_ARG(pc);
- pc += ARG_LEN;
- matchCh2 = GET_ARG(pc);
- pc += ARG_LEN;
- if ((x->cp == gData->cpend)
- || ((*x->cp != matchCh1) && (*x->cp != matchCh2))) {
- result = NULL;
- break;
- }
- /* else false thru... */
-
- case REOP_ALT:
-doAlt:
- nextpc = pc + GET_OFFSET(pc); /* start of next alternate */
- pc += ARG_LEN; /* start of this alternate */
- curState->parenSoFar = parenSoFar;
- ++gData->stateStackTop;
- if (gData->stateStackTop == gData->maxStateStack)
- if (!reallocStateStack(gData))
- return NULL;
- op = (REOp)(*pc++);
- startcp = x->cp;
- if (REOP_IS_SIMPLE(op)) {
- if (!simpleMatch(gData, x, op, &pc, JS_TRUE)) {
- op = (REOp)(*nextpc++);
- pc = nextpc;
- continue;
- }
- else { /* accept the match and move on */
- result = x;
- op = (REOp)(*pc++);
- }
- }
- nextop = (REOp)(*nextpc++);
- if (!pushBackTrackState(gData, nextop, nextpc, x, startcp, 0, 0))
- return NULL;
- continue;
-
- /*
- * Occurs at (succesful) end of REOP_ALT,
- */
- case REOP_JUMP:
- --gData->stateStackTop;
- offset = GET_OFFSET(pc);
- pc += offset;
- op = (REOp)(*pc++);
- continue;
-
- /*
- * Occurs at last (succesful) end of REOP_ALT,
- */
- case REOP_ENDALT:
- --gData->stateStackTop;
- op = (REOp)(*pc++);
- continue;
-
- case REOP_LPAREN:
- parenIndex = GET_ARG(pc);
- if ((parenIndex + 1) > parenSoFar)
- parenSoFar = parenIndex + 1;
- pc += ARG_LEN;
- x->parens[parenIndex].index = x->cp - gData->cpbegin;
- x->parens[parenIndex].length = 0;
- op = (REOp)(*pc++);
- continue;
- case REOP_RPAREN:
- parenIndex = GET_ARG(pc);
- pc += ARG_LEN;
- cap = &x->parens[parenIndex];
- cap->length = x->cp - (gData->cpbegin + cap->index);
- op = (REOp)(*pc++);
- continue;
-
- case REOP_ASSERT:
- nextpc = pc + GET_OFFSET(pc); /* start of term after ASSERT */
- pc += ARG_LEN; /* start of ASSERT child */
- op = (REOp)(*pc++);
- if (REOP_IS_SIMPLE(op)
- && !simpleMatch(gData, x, op, &pc, JS_FALSE)) {
- result = NULL;
- break;
- }
- else {
- curState->u.assertion.top
- = (char *)gData->backTrackSP
- - (char *)gData->backTrackStack;
- curState->u.assertion.sz = gData->cursz;
- curState->index = x->cp - gData->cpbegin;
- curState->parenSoFar = parenSoFar;
- ++gData->stateStackTop;
- if (gData->stateStackTop == gData->maxStateStack)
- if (!reallocStateStack(gData))
- return NULL;
- if (!pushBackTrackState(gData, REOP_ASSERTTEST,
- nextpc, x, x->cp, 0, 0))
- return NULL;
- }
- continue;
- case REOP_ASSERT_NOT:
- nextpc = pc + GET_OFFSET(pc);
- pc += ARG_LEN;
- op = (REOp)(*pc++);
- if (REOP_IS_SIMPLE(op)
- /* Note - fail to fail! */
- && simpleMatch(gData, x, op, &pc, JS_FALSE)) {
- result = NULL;
- break;
- }
- else {
- curState->u.assertion.top
- = (char *)gData->backTrackSP
- - (char *)gData->backTrackStack;
- curState->u.assertion.sz = gData->cursz;
- curState->index = x->cp - gData->cpbegin;
- curState->parenSoFar = parenSoFar;
- ++gData->stateStackTop;
- if (gData->stateStackTop == gData->maxStateStack)
- if (!reallocStateStack(gData))
- return NULL;
- if (!pushBackTrackState(gData, REOP_ASSERTNOTTEST,
- nextpc, x, x->cp, 0, 0))
- return NULL;
- }
- continue;
- case REOP_ASSERTTEST:
- --gData->stateStackTop;
- --curState;
- x->cp = gData->cpbegin + curState->index;
- gData->backTrackSP
- = (REBackTrackData *)((char *)gData->backTrackStack
- + curState->u.assertion.top);
- gData->cursz = curState->u.assertion.sz;
- if (result != NULL)
- result = x;
- break;
- case REOP_ASSERTNOTTEST:
- --gData->stateStackTop;
- --curState;
- x->cp = gData->cpbegin + curState->index;
- gData->backTrackSP
- = (REBackTrackData *)((char *)gData->backTrackStack
- + curState->u.assertion.top);
- gData->cursz = curState->u.assertion.sz;
- if (result == NULL)
- result = x;
- else
- result = NULL;
- break;
-
- case REOP_END:
- if (x != NULL)
- return x;
- break;
-
- case REOP_STAR:
- curState->u.quantifier.min = 0;
- curState->u.quantifier.max = -1;
- goto quantcommon;
- case REOP_PLUS:
- curState->u.quantifier.min = 1;
- curState->u.quantifier.max = -1;
- goto quantcommon;
- case REOP_OPT:
- curState->u.quantifier.min = 0;
- curState->u.quantifier.max = 1;
- goto quantcommon;
- case REOP_QUANT:
- curState->u.quantifier.min = GET_ARG(pc);
- pc += ARG_LEN;
- curState->u.quantifier.max = GET_ARG(pc);
- pc += ARG_LEN;
-quantcommon:
- if (curState->u.quantifier.max == 0) {
- pc = pc + GET_OFFSET(pc);
- op = (REOp)(*pc++);
- result = x;
- continue;
- }
- /* Step over <next> */
- nextpc = pc + ARG_LEN;
- op = (REOp)(*nextpc++);
- startcp = x->cp;
- if (REOP_IS_SIMPLE(op)) {
- if (!simpleMatch(gData, x, op, &nextpc, JS_TRUE)) {
- if (curState->u.quantifier.min == 0)
- result = x;
- else
- result = NULL;
- pc = pc + GET_OFFSET(pc);
- break;
- }
- else {
- op = (REOp)(*nextpc++);
- result = x;
- }
- }
- curState->index = startcp - gData->cpbegin;
- curState->continue_op = REOP_REPEAT;
- curState->continue_pc = pc;
- curState->parenSoFar = parenSoFar;
- ++gData->stateStackTop;
- if (gData->stateStackTop == gData->maxStateStack)
- if (!reallocStateStack(gData))
- return NULL;
- if (curState->u.quantifier.min == 0)
- if (!pushBackTrackState(gData, REOP_REPEAT,
- pc, x, startcp, 0, 0))
- return NULL;
- pc = nextpc;
- continue;
-
- case REOP_ENDCHILD: /* marks the end of a quantifier child */
- pc = curState[-1].continue_pc;
- op = curState[-1].continue_op;
- continue;
-
- case REOP_REPEAT:
- --curState;
-repeatAgain:
- --gData->stateStackTop;
- if (result == NULL) {
- /*
- * There's been a failure, see if we have enough children.
- */
- if (curState->u.quantifier.min == 0) {
- result = x;
- goto repeatDone;
- }
- break;
- }
- else {
- if ((curState->u.quantifier.min == 0)
- && (x->cp == gData->cpbegin + curState->index)) {
- /* matched an empty string, that'll get us nowhere */
- result = NULL;
- break;
- }
- if (curState->u.quantifier.min != 0)
- curState->u.quantifier.min--;
- if (curState->u.quantifier.max != (uint16)(-1))
- curState->u.quantifier.max--;
- if (curState->u.quantifier.max == 0) {
- result = x;
- goto repeatDone;
- }
- nextpc = pc + ARG_LEN;
- nextop = (REOp)(*nextpc);
- startcp = x->cp;
- if (REOP_IS_SIMPLE(nextop)) {
- nextpc++;
- if (!simpleMatch(gData, x, nextop, &nextpc, JS_TRUE)) {
- if (curState->u.quantifier.min == 0) {
- result = x;
- goto repeatDone;
- }
- else
- result = NULL;
- break;
- }
- result = x;
- }
- curState->index = startcp - gData->cpbegin;
- ++gData->stateStackTop;
- if (gData->stateStackTop == gData->maxStateStack)
- if (!reallocStateStack(gData))
- return NULL;
- if (curState->u.quantifier.min == 0)
- if (!pushBackTrackState(gData, REOP_REPEAT,
- pc, x, startcp,
- curState->parenSoFar,
- parenSoFar
- - curState->parenSoFar))
- return NULL;
- if (*nextpc == REOP_ENDCHILD)
- goto repeatAgain;
- pc = nextpc;
- op = (REOp)(*pc++);
- parenSoFar = curState->parenSoFar;
- }
- continue;
-repeatDone:
- pc = pc + GET_OFFSET(pc);
- break;
-
-
- case REOP_MINIMALSTAR:
- curState->u.quantifier.min = 0;
- curState->u.quantifier.max = -1;
- goto minimalquantcommon;
- case REOP_MINIMALPLUS:
- curState->u.quantifier.min = 1;
- curState->u.quantifier.max = -1;
- goto minimalquantcommon;
- case REOP_MINIMALOPT:
- curState->u.quantifier.min = 0;
- curState->u.quantifier.max = 1;
- goto minimalquantcommon;
- case REOP_MINIMALQUANT:
- curState->u.quantifier.min = GET_ARG(pc);
- pc += ARG_LEN;
- curState->u.quantifier.max = GET_ARG(pc);
- pc += ARG_LEN;
-minimalquantcommon:
- curState->index = x->cp - gData->cpbegin;
- curState->parenSoFar = parenSoFar;
- ++gData->stateStackTop;
- if (gData->stateStackTop == gData->maxStateStack)
- if (!reallocStateStack(gData))
- return NULL;
- if (curState->u.quantifier.min != 0) {
- curState->continue_op = REOP_MINIMALREPEAT;
- curState->continue_pc = pc;
- /* step over <next> */
- pc += ARG_LEN;
- op = (REOp)(*pc++);
- }
- else {
- if (!pushBackTrackState(gData, REOP_MINIMALREPEAT,
- pc, x, x->cp, 0, 0))
- return NULL;
- --gData->stateStackTop;
- pc = pc + GET_OFFSET(pc);
- op = (REOp)(*pc++);
- }
- continue;
-
- case REOP_MINIMALREPEAT:
- --gData->stateStackTop;
- --curState;
-
- if (result == NULL) {
- /*
- * Non-greedy failure - try to consume another child.
- */
- if ((curState->u.quantifier.max == (uint16)(-1))
- || (curState->u.quantifier.max > 0)) {
- curState->index = x->cp - gData->cpbegin;
- curState->continue_op = REOP_MINIMALREPEAT;
- curState->continue_pc = pc;
- pc += ARG_LEN;
- for (k = curState->parenSoFar; k < parenSoFar; k++)
- x->parens[k].index = -1;
- ++gData->stateStackTop;
- if (gData->stateStackTop == gData->maxStateStack)
- if (!reallocStateStack(gData))
- return NULL;
- op = (REOp)(*pc++);
- continue;
- }
- else {
- /* Don't need to adjust pc since we're going to pop. */
- break;
- }
- }
- else {
- if ((curState->u.quantifier.min == 0)
- && (x->cp == gData->cpbegin + curState->index)) {
- /* Matched an empty string, that'll get us nowhere. */
- result = NULL;
- break;
- }
- if (curState->u.quantifier.min != 0)
- curState->u.quantifier.min--;
- if (curState->u.quantifier.max != (uint16)(-1))
- curState->u.quantifier.max--;
- if (curState->u.quantifier.min != 0) {
- curState->continue_op = REOP_MINIMALREPEAT;
- curState->continue_pc = pc;
- pc += ARG_LEN;
- for (k = curState->parenSoFar; k < parenSoFar; k++)
- x->parens[k].index = -1;
- curState->index = x->cp - gData->cpbegin;
- ++gData->stateStackTop;
- if (gData->stateStackTop == gData->maxStateStack)
- if (!reallocStateStack(gData))
- return NULL;
- op = (REOp)(*pc++);
- continue;
- }
- else {
- curState->index = x->cp - gData->cpbegin;
- curState->parenSoFar = parenSoFar;
- ++gData->stateStackTop;
- if (gData->stateStackTop == gData->maxStateStack)
- if (!reallocStateStack(gData))
- return NULL;
- if (!pushBackTrackState(gData, REOP_MINIMALREPEAT,
- pc, x, x->cp,
- curState->parenSoFar,
- parenSoFar
- - curState->parenSoFar))
- return NULL;
- --gData->stateStackTop;
- pc = pc + GET_OFFSET(pc);
- op = (REOp)(*pc++);
- continue;
- }
- }
-
- default:
- JS_ASSERT(JS_FALSE);
- result = NULL;
- }
- }
- /*
- * If the match failed and there's a backtrack option, take it.
- * Otherwise this is a complete and utter failure.
- */
- if (result == NULL) {
- if (gData->cursz > 0) {
- backTrackData = gData->backTrackSP;
- gData->cursz = backTrackData->sz;
- gData->backTrackSP
- = (REBackTrackData *)((char *)backTrackData
- - backTrackData->sz);
- x->cp = backTrackData->cp;
- pc = backTrackData->backtrack_pc;
- op = backTrackData->backtrack_op;
- gData->stateStackTop = backTrackData->precedingStateTop;
- JS_ASSERT(gData->stateStackTop);
-
- memcpy(gData->stateStack, backTrackData + 1,
- sizeof(REProgState) * backTrackData->precedingStateTop);
- curState = &gData->stateStack[gData->stateStackTop - 1];
-
- if (backTrackData->parenCount) {
- memcpy(&x->parens[backTrackData->parenIndex],
- (char *)(backTrackData + 1) + sizeof(REProgState) * backTrackData->precedingStateTop,
- sizeof(RECapture) * backTrackData->parenCount);
- parenSoFar = backTrackData->parenIndex + backTrackData->parenCount;
- }
- else {
- for (k = curState->parenSoFar; k < parenSoFar; k++)
- x->parens[k].index = -1;
- parenSoFar = curState->parenSoFar;
- }
- continue;
- }
- else
- return NULL;
- }
- else
- x = result;
-
- /*
- * Continue with the expression.
- */
- op = (REOp)*pc++;
- }
- return NULL;
-}
-
-static REMatchState *
-MatchRegExp(REGlobalData *gData, REMatchState *x)
-{
- REMatchState *result;
- const jschar *cp = x->cp;
- const jschar *cp2;
- uintN j;
-
- /*
- * Have to include the position beyond the last character
- * in order to detect end-of-input/line condition.
- */
- for (cp2 = cp; cp2 <= gData->cpend; cp2++) {
- gData->skipped = cp2 - cp;
- x->cp = cp2;
- for (j = 0; j < gData->regexp->parenCount; j++)
- x->parens[j].index = -1;
- result = executeREBytecode(gData, x);
- if (!gData->ok || result)
- return result;
- gData->backTrackSP = gData->backTrackStack;
- gData->cursz = 0;
- gData->stateStackTop = 0;
- cp2 = cp + gData->skipped;
- }
- return NULL;
-}
-
-
-static REMatchState *
-initMatch(JSContext *cx, REGlobalData *gData, JSRegExp *re)
-{
- REMatchState *result;
- uintN i;
-
- gData->maxBackTrack = INITIAL_BACKTRACK;
- JS_ARENA_ALLOCATE_CAST(gData->backTrackStack, REBackTrackData *,
- &gData->pool,
- INITIAL_BACKTRACK);
- if (!gData->backTrackStack)
- return NULL;
- gData->backTrackSP = gData->backTrackStack;
- gData->cursz = 0;
-
-
- gData->maxStateStack = INITIAL_STATESTACK;
- JS_ARENA_ALLOCATE_CAST(gData->stateStack, REProgState *,
- &gData->pool,
- sizeof(REProgState) * INITIAL_STATESTACK);
- if (!gData->stateStack)
- return NULL;
- gData->stateStackTop = 0;
-
- gData->cx = cx;
- gData->regexp = re;
- gData->ok = JS_TRUE;
-
- JS_ARENA_ALLOCATE_CAST(result, REMatchState *,
- &gData->pool,
- sizeof(REMatchState)
- + (re->parenCount - 1) * sizeof(RECapture));
- if (!result)
- return NULL;
-
- for (i = 0; i < re->classCount; i++)
- if (!re->classList[i].converted)
- if (!processCharSet(gData, &re->classList[i]))
- return NULL;
-
- return result;
-}
-
-JSBool
-js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp,
- JSBool test, jsval *rval)
-{
- REGlobalData gData;
- REMatchState *x, *result;
-
- const jschar *cp, *ep;
- size_t i, length, start;
- JSSubString *morepar;
- JSBool ok;
- JSRegExpStatics *res;
- ptrdiff_t matchlen;
- uintN num, morenum;
- JSString *parstr, *matchstr;
- JSObject *obj;
-
- RECapture *parsub;
-
- /*
- * It's safe to load from cp because JSStrings have a zero at the end,
- * and we never let cp get beyond cpend.
- */
- start = *indexp;
- length = JSSTRING_LENGTH(str);
- if (start > length)
- start = length;
- cp = JSSTRING_CHARS(str);
- gData.cpbegin = cp;
- gData.cpend = cp + length;
- cp += start;
- gData.start = start;
- gData.skipped = 0;
-
- JS_InitArenaPool(&gData.pool, "RegExpPool", 8096, 4);
- x = initMatch(cx, &gData, re);
- if (!x)
- return JS_FALSE;
- x->cp = cp;
-
- /*
- * Call the recursive matcher to do the real work. Return null on mismatch
- * whether testing or not. On match, return an extended Array object.
- */
- result = MatchRegExp(&gData, x);
- if (!(ok = gData.ok)) goto out;
- if (!result) {
- *rval = JSVAL_NULL;
- goto out;
- }
- cp = result->cp;
- i = PTRDIFF(cp, gData.cpbegin, jschar);
- *indexp = i;
- matchlen = i - (start + gData.skipped);
- ep = cp;
- cp -= matchlen;
-
- if (test) {
- /*
- * Testing for a match and updating cx->regExpStatics: don't allocate
- * an array object, do return true.
- */
- *rval = JSVAL_TRUE;
-
- /* Avoid warning. (gcc doesn't detect that obj is needed iff !test); */
- obj = NULL;
- } else {
- /*
- * The array returned on match has element 0 bound to the matched
- * string, elements 1 through state.parenCount bound to the paren
- * matches, an index property telling the length of the left context,
- * and an input property referring to the input string.
- */
- obj = js_NewArrayObject(cx, 0, NULL);
- if (!obj) {
- ok = JS_FALSE;
- goto out;
- }
- *rval = OBJECT_TO_JSVAL(obj);
-
-#define DEFVAL(val, id) { \
- ok = js_DefineProperty(cx, obj, id, val, \
- JS_PropertyStub, JS_PropertyStub, \
- JSPROP_ENUMERATE, NULL); \
- if (!ok) { \
- cx->newborn[GCX_OBJECT] = NULL; \
- cx->newborn[GCX_STRING] = NULL; \
- goto out; \
- } \
-}
-
- matchstr = js_NewStringCopyN(cx, cp, matchlen, 0);
- if (!matchstr) {
- cx->newborn[GCX_OBJECT] = NULL;
- ok = JS_FALSE;
- goto out;
- }
- DEFVAL(STRING_TO_JSVAL(matchstr), INT_TO_JSVAL(0));
- }
-
- res = &cx->regExpStatics;
- res->input = str;
- res->parenCount = re->parenCount;
- if (re->parenCount == 0)
- res->lastParen = js_EmptySubString;
- else {
- for (num = 0; num < re->parenCount; num++) {
- parsub = &result->parens[num];
- if (num < 9) {
- if (parsub->index == -1) {
- res->parens[num].chars = NULL;
- res->parens[num].length = 0;
- }
- else {
- res->parens[num].chars = gData.cpbegin + parsub->index;
- res->parens[num].length = parsub->length;
- }
- } else {
- morenum = num - 9;
- morepar = res->moreParens;
- if (!morepar) {
- res->moreLength = 10;
- morepar = (JSSubString*) JS_malloc(cx,
- 10 * sizeof(JSSubString));
- } else if (morenum >= res->moreLength) {
- res->moreLength += 10;
- morepar = (JSSubString*) JS_realloc(cx, morepar,
- res->moreLength * sizeof(JSSubString));
- }
- if (!morepar) {
- cx->newborn[GCX_OBJECT] = NULL;
- cx->newborn[GCX_STRING] = NULL;
- ok = JS_FALSE;
- goto out;
- }
- res->moreParens = morepar;
- if (parsub->index == -1) {
- morepar[morenum].chars = NULL;
- morepar[morenum].length = 0;
- }
- else {
- morepar[morenum].chars = gData.cpbegin + parsub->index;
- morepar[morenum].length = parsub->length;
- }
- }
- if (test)
- continue;
- if (parsub->index == -1)
- ok = js_DefineProperty(cx, obj, INT_TO_JSVAL(num + 1),
- JSVAL_VOID, NULL, NULL,
- JSPROP_ENUMERATE, NULL);
- else {
- parstr = js_NewStringCopyN(cx, gData.cpbegin + parsub->index,
- parsub->length, 0);
- if (!parstr) {
- cx->newborn[GCX_OBJECT] = NULL;
- cx->newborn[GCX_STRING] = NULL;
- ok = JS_FALSE;
- goto out;
- }
- ok = js_DefineProperty(cx, obj, INT_TO_JSVAL(num + 1),
- STRING_TO_JSVAL(parstr), NULL, NULL,
- JSPROP_ENUMERATE, NULL);
- }
- if (!ok) {
- cx->newborn[GCX_OBJECT] = NULL;
- cx->newborn[GCX_STRING] = NULL;
- goto out;
- }
- }
- if (parsub->index == -1) {
- res->lastParen.chars = NULL;
- res->lastParen.length = 0;
- }
- else {
- res->lastParen.chars = gData.cpbegin + parsub->index;
- res->lastParen.length = parsub->length;
- }
- }
-
- if (!test) {
- /*
- * Define the index and input properties last for better for/in loop
- * order (so they come after the elements).
- */
- DEFVAL(INT_TO_JSVAL(start + gData.skipped),
- (jsid)cx->runtime->atomState.indexAtom);
- DEFVAL(STRING_TO_JSVAL(str),
- (jsid)cx->runtime->atomState.inputAtom);
- }
-
-#undef DEFVAL
-
- res->lastMatch.chars = cp;
- res->lastMatch.length = matchlen;
- if (cx->version == JSVERSION_1_2) {
- /*
- * JS1.2 emulated Perl4.0.1.8 (patch level 36) for global regexps used
- * in scalar contexts, and unintentionally for the string.match "list"
- * psuedo-context. On "hi there bye", the following would result:
- *
- * Language while(/ /g){print("$`");} s/ /$`/g
- * perl4.036 "hi", "there" "hihitherehi therebye"
- * perl5 "hi", "hi there" "hihitherehi therebye"
- * js1.2 "hi", "there" "hihitheretherebye"
- */
- res->leftContext.chars = JSSTRING_CHARS(str) + start;
- res->leftContext.length = gData.skipped;
- } else {
- /*
- * For JS1.3 and ECMAv2, emulate Perl5 exactly:
- *
- * js1.3 "hi", "hi there" "hihitherehi therebye"
- */
- res->leftContext.chars = JSSTRING_CHARS(str);
- res->leftContext.length = start + gData.skipped;
- }
- res->rightContext.chars = ep;
- res->rightContext.length = gData.cpend - ep;
-
-out:
- JS_FinishArenaPool(&gData.pool);
- return ok;
-}
-
-/************************************************************************/
-
-enum regexp_tinyid {
- REGEXP_SOURCE = -1,
- REGEXP_GLOBAL = -2,
- REGEXP_IGNORE_CASE = -3,
- REGEXP_LAST_INDEX = -4,
- REGEXP_MULTILINE = -5
-};
-
-#define REGEXP_PROP_ATTRS (JSPROP_PERMANENT|JSPROP_SHARED)
-
-static JSPropertySpec regexp_props[] = {
- {"source", REGEXP_SOURCE, REGEXP_PROP_ATTRS | JSPROP_READONLY,0,0},
- {"global", REGEXP_GLOBAL, REGEXP_PROP_ATTRS | JSPROP_READONLY,0,0},
- {"ignoreCase", REGEXP_IGNORE_CASE, REGEXP_PROP_ATTRS | JSPROP_READONLY,0,0},
- {"lastIndex", REGEXP_LAST_INDEX, REGEXP_PROP_ATTRS,0,0},
- {"multiline", REGEXP_MULTILINE, REGEXP_PROP_ATTRS | JSPROP_READONLY,0,0},
- {0,0,0,0,0}
-};
-
-static JSBool
-regexp_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
-{
- jsint slot;
- JSRegExp *re;
-
- if (!JSVAL_IS_INT(id))
- return JS_TRUE;
- slot = JSVAL_TO_INT(id);
- if (slot == REGEXP_LAST_INDEX)
- return JS_GetReservedSlot(cx, obj, 0, vp);
-
- JS_LOCK_OBJ(cx, obj);
- re = (JSRegExp *) JS_GetInstancePrivate(cx, obj, &js_RegExpClass, NULL);
- if (re) {
- switch (slot) {
- case REGEXP_SOURCE:
- *vp = STRING_TO_JSVAL(re->source);
- break;
- case REGEXP_GLOBAL:
- *vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_GLOB) != 0);
- break;
- case REGEXP_IGNORE_CASE:
- *vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_FOLD) != 0);
- break;
- case REGEXP_MULTILINE:
- *vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_MULTILINE) != 0);
- break;
- }
- }
- JS_UNLOCK_OBJ(cx, obj);
- return JS_TRUE;
-}
-
-static JSBool
-regexp_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
-{
- JSBool ok;
- jsint slot;
- jsdouble lastIndex;
-
- ok = JS_TRUE;
- if (!JSVAL_IS_INT(id))
- return ok;
- slot = JSVAL_TO_INT(id);
- if (slot == REGEXP_LAST_INDEX) {
- if (!js_ValueToNumber(cx, *vp, &lastIndex))
- return JS_FALSE;
- lastIndex = js_DoubleToInteger(lastIndex);
- ok = js_NewNumberValue(cx, lastIndex, vp) &&
- JS_SetReservedSlot(cx, obj, 0, *vp);
- }
- return ok;
-}
-
-/*
- * RegExp class static properties and their Perl counterparts:
- *
- * RegExp.input $_
- * RegExp.multiline $*
- * RegExp.lastMatch $&
- * RegExp.lastParen $+
- * RegExp.leftContext $`
- * RegExp.rightContext $'
- */
-enum regexp_static_tinyid {
- REGEXP_STATIC_INPUT = -1,
- REGEXP_STATIC_MULTILINE = -2,
- REGEXP_STATIC_LAST_MATCH = -3,
- REGEXP_STATIC_LAST_PAREN = -4,
- REGEXP_STATIC_LEFT_CONTEXT = -5,
- REGEXP_STATIC_RIGHT_CONTEXT = -6
-};
-
-JSBool
-js_InitRegExpStatics(JSContext *cx, JSRegExpStatics *res)
-{
- JS_ClearRegExpStatics(cx);
- return js_AddRoot(cx, &res->input, "res->input");
-}
-
-void
-js_FreeRegExpStatics(JSContext *cx, JSRegExpStatics *res)
-{
- if (res->moreParens) {
- JS_free(cx, res->moreParens);
- res->moreParens = NULL;
- }
- js_RemoveRoot(cx->runtime, &res->input);
-}
-
-static JSBool
-regexp_static_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
-{
- jsint slot;
- JSRegExpStatics *res;
- JSString *str;
- JSSubString *sub;
-
- res = &cx->regExpStatics;
- if (!JSVAL_IS_INT(id))
- return JS_TRUE;
- slot = JSVAL_TO_INT(id);
- switch (slot) {
- case REGEXP_STATIC_INPUT:
- *vp = res->input ? STRING_TO_JSVAL(res->input)
- : JS_GetEmptyStringValue(cx);
- return JS_TRUE;
- case REGEXP_STATIC_MULTILINE:
- *vp = BOOLEAN_TO_JSVAL(res->multiline);
- return JS_TRUE;
- case REGEXP_STATIC_LAST_MATCH:
- sub = &res->lastMatch;
- break;
- case REGEXP_STATIC_LAST_PAREN:
- sub = &res->lastParen;
- break;
- case REGEXP_STATIC_LEFT_CONTEXT:
- sub = &res->leftContext;
- break;
- case REGEXP_STATIC_RIGHT_CONTEXT:
- sub = &res->rightContext;
- break;
- default:
- sub = REGEXP_PAREN_SUBSTRING(res, slot);
- break;
- }
- str = js_NewStringCopyN(cx, sub->chars, sub->length, 0);
- if (!str)
- return JS_FALSE;
- *vp = STRING_TO_JSVAL(str);
- return JS_TRUE;
-}
-
-static JSBool
-regexp_static_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
-{
- JSRegExpStatics *res;
-
- if (!JSVAL_IS_INT(id))
- return JS_TRUE;
- res = &cx->regExpStatics;
- /* XXX use if-else rather than switch to keep MSVC1.52 from crashing */
- if (JSVAL_TO_INT(id) == REGEXP_STATIC_INPUT) {
- if (!JSVAL_IS_STRING(*vp) &&
- !JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp)) {
- return JS_FALSE;
- }
- res->input = JSVAL_TO_STRING(*vp);
- } else if (JSVAL_TO_INT(id) == REGEXP_STATIC_MULTILINE) {
- if (!JSVAL_IS_BOOLEAN(*vp) &&
- !JS_ConvertValue(cx, *vp, JSTYPE_BOOLEAN, vp)) {
- return JS_FALSE;
- }
- res->multiline = JSVAL_TO_BOOLEAN(*vp);
- }
- return JS_TRUE;
-}
-
-static JSPropertySpec regexp_static_props[] = {
- {"input",
- REGEXP_STATIC_INPUT,
- JSPROP_ENUMERATE|JSPROP_SHARED,
- regexp_static_getProperty, regexp_static_setProperty},
- {"multiline",
- REGEXP_STATIC_MULTILINE,
- JSPROP_ENUMERATE|JSPROP_SHARED,
- regexp_static_getProperty, regexp_static_setProperty},
- {"lastMatch",
- REGEXP_STATIC_LAST_MATCH,
- JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED,
- regexp_static_getProperty, regexp_static_getProperty},
- {"lastParen",
- REGEXP_STATIC_LAST_PAREN,
- JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED,
- regexp_static_getProperty, regexp_static_getProperty},
- {"leftContext",
- REGEXP_STATIC_LEFT_CONTEXT,
- JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED,
- regexp_static_getProperty, regexp_static_getProperty},
- {"rightContext",
- REGEXP_STATIC_RIGHT_CONTEXT,
- JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED,
- regexp_static_getProperty, regexp_static_getProperty},
-
- /* XXX should have block scope and local $1, etc. */
- {"$1", 0, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED,
- regexp_static_getProperty, regexp_static_getProperty},
- {"$2", 1, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED,
- regexp_static_getProperty, regexp_static_getProperty},
- {"$3", 2, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED,
- regexp_static_getProperty, regexp_static_getProperty},
- {"$4", 3, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED,
- regexp_static_getProperty, regexp_static_getProperty},
- {"$5", 4, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED,
- regexp_static_getProperty, regexp_static_getProperty},
- {"$6", 5, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED,
- regexp_static_getProperty, regexp_static_getProperty},
- {"$7", 6, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED,
- regexp_static_getProperty, regexp_static_getProperty},
- {"$8", 7, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED,
- regexp_static_getProperty, regexp_static_getProperty},
- {"$9", 8, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED,
- regexp_static_getProperty, regexp_static_getProperty},
-
- {0,0,0,0,0}
-};
-
-static void
-regexp_finalize(JSContext *cx, JSObject *obj)
-{
- JSRegExp *re;
-
- re = (JSRegExp *) JS_GetPrivate(cx, obj);
- if (!re)
- return;
- js_DestroyRegExp(cx, re);
-}
-
-/* Forward static prototype. */
-static JSBool
-regexp_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval);
-
-static JSBool
-regexp_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- return regexp_exec(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, rval);
-}
-
-#if JS_HAS_XDR
-
-#include "jsxdrapi.h"
-
-static JSBool
-regexp_xdrObject(JSXDRState *xdr, JSObject **objp)
-{
- JSRegExp *re;
- JSString *source;
- uint8 flags;
-
- if (xdr->mode == JSXDR_ENCODE) {
- re = (JSRegExp *) JS_GetPrivate(xdr->cx, *objp);
- if (!re)
- return JS_FALSE;
- source = re->source;
- flags = (uint8) re->flags;
- }
- if (!JS_XDRString(xdr, &source) ||
- !JS_XDRUint8(xdr, &flags)) {
- return JS_FALSE;
- }
- if (xdr->mode == JSXDR_DECODE) {
- *objp = js_NewObject(xdr->cx, &js_RegExpClass, NULL, NULL);
- if (!*objp)
- return JS_FALSE;
- re = js_NewRegExp(xdr->cx, NULL, source, flags, JS_FALSE);
- if (!re)
- return JS_FALSE;
- if (!JS_SetPrivate(xdr->cx, *objp, re) ||
- !js_SetLastIndex(xdr->cx, *objp, 0)) {
- js_DestroyRegExp(xdr->cx, re);
- return JS_FALSE;
- }
- }
- return JS_TRUE;
-}
-
-#else /* !JS_HAS_XDR */
-
-#define regexp_xdrObject NULL
-
-#endif /* !JS_HAS_XDR */
-
-static uint32
-regexp_mark(JSContext *cx, JSObject *obj, void *arg)
-{
- JSRegExp *re = (JSRegExp *) JS_GetPrivate(cx, obj);
- if (re)
- JS_MarkGCThing(cx, re->source, "source", arg);
- return 0;
-}
-
-JSClass js_RegExpClass = {
- js_RegExp_str,
- JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1),
- JS_PropertyStub, JS_PropertyStub, regexp_getProperty, regexp_setProperty,
- JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, regexp_finalize,
- NULL, NULL, regexp_call, NULL,
- regexp_xdrObject, NULL, regexp_mark, 0
-};
-
-static const jschar empty_regexp_ucstr[] = {'(', '?', ':', ')', 0};
-
-static JSBool
-regexp_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- JSRegExp *re;
- const jschar *source;
- jschar *chars;
- size_t length, nflags;
- uintN flags;
- JSString *str;
-
- if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv))
- return JS_FALSE;
- JS_LOCK_OBJ(cx, obj);
- re = (JSRegExp *) JS_GetPrivate(cx, obj);
- if (!re) {
- JS_UNLOCK_OBJ(cx, obj);
- *rval = STRING_TO_JSVAL(cx->runtime->emptyString);
- return JS_TRUE;
- }
-
- source = JSSTRING_CHARS(re->source);
- length = JSSTRING_LENGTH(re->source);
- if (length == 0) {
- source = empty_regexp_ucstr;
- length = sizeof(empty_regexp_ucstr) / sizeof(jschar) - 1;
- }
- length += 2;
- nflags = 0;
- for (flags = re->flags; flags != 0; flags &= flags - 1)
- nflags++;
- chars = (jschar*) JS_malloc(cx, (length + nflags + 1) * sizeof(jschar));
- if (!chars) {
- JS_UNLOCK_OBJ(cx, obj);
- return JS_FALSE;
- }
-
- chars[0] = '/';
- js_strncpy(&chars[1], source, length - 2);
- chars[length-1] = '/';
- if (nflags) {
- if (re->flags & JSREG_GLOB)
- chars[length++] = 'g';
- if (re->flags & JSREG_FOLD)
- chars[length++] = 'i';
- if (re->flags & JSREG_MULTILINE)
- chars[length++] = 'm';
- }
- JS_UNLOCK_OBJ(cx, obj);
- chars[length] = 0;
-
- str = js_NewString(cx, chars, length, 0);
- if (!str) {
- JS_free(cx, chars);
- return JS_FALSE;
- }
- *rval = STRING_TO_JSVAL(str);
- return JS_TRUE;
-}
-
-static JSBool
-regexp_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- JSString *opt, *str;
- JSRegExp *oldre, *re;
- JSBool ok;
- JSObject *obj2;
-
- if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv))
- return JS_FALSE;
- opt = NULL;
- if (argc == 0) {
- str = cx->runtime->emptyString;
- } else {
- if (JSVAL_IS_OBJECT(argv[0])) {
- /*
- * If we get passed in a RegExp object we construct a new
- * RegExp that is a duplicate of it by re-compiling the
- * original source code. ECMA requires that it be an error
- * here if the flags are specified. (We must use the flags
- * from the original RegExp also).
- */
- obj2 = JSVAL_TO_OBJECT(argv[0]);
- if (obj2 && OBJ_GET_CLASS(cx, obj2) == &js_RegExpClass) {
- if (argc >= 2 && !JSVAL_IS_VOID(argv[1])) { /* 'flags' passed */
- JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
- JSMSG_NEWREGEXP_FLAGGED);
- return JS_FALSE;
- }
- JS_LOCK_OBJ(cx, obj2);
- re = (JSRegExp *) JS_GetPrivate(cx, obj2);
- if (!re) {
- JS_UNLOCK_OBJ(cx, obj2);
- return JS_FALSE;
- }
- re = js_NewRegExp(cx, NULL, re->source, re->flags, JS_FALSE);
- JS_UNLOCK_OBJ(cx, obj2);
- goto madeit;
- }
- }
- str = js_ValueToString(cx, argv[0]);
- if (!str)
- return JS_FALSE;
- argv[0] = STRING_TO_JSVAL(str);
- if (argc > 1) {
- if (JSVAL_IS_VOID(argv[1])) {
- opt = NULL;
- } else {
- opt = js_ValueToString(cx, argv[1]);
- if (!opt)
- return JS_FALSE;
- argv[1] = STRING_TO_JSVAL(opt);
- }
- }
- }
- re = js_NewRegExpOpt(cx, NULL, str, opt, JS_FALSE);
-madeit:
- if (!re)
- return JS_FALSE;
- JS_LOCK_OBJ(cx, obj);
- oldre = (JSRegExp *) JS_GetPrivate(cx, obj);
- ok = JS_SetPrivate(cx, obj, re) && js_SetLastIndex(cx, obj, 0);
- JS_UNLOCK_OBJ(cx, obj);
- if (!ok) {
- js_DestroyRegExp(cx, re);
- } else {
- if (oldre)
- js_DestroyRegExp(cx, oldre);
- *rval = OBJECT_TO_JSVAL(obj);
- }
- return ok;
-}
-
-static JSBool
-regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- JSBool test, jsval *rval)
-{
- JSBool ok;
- JSRegExp *re;
- jsdouble lastIndex;
- JSString *str;
- size_t i;
-
- ok = JS_InstanceOf(cx, obj, &js_RegExpClass, argv);
- if (!ok)
- return JS_FALSE;
- JS_LOCK_OBJ(cx, obj);
- re = (JSRegExp *) JS_GetPrivate(cx, obj);
- if (!re) {
- JS_UNLOCK_OBJ(cx, obj);
- return JS_TRUE;
- }
-
- /* NB: we must reach out: after this paragraph, in order to drop re. */
- HOLD_REGEXP(cx, re);
- if (re->flags & JSREG_GLOB) {
- ok = js_GetLastIndex(cx, obj, &lastIndex);
- } else {
- lastIndex = 0;
- }
- JS_UNLOCK_OBJ(cx, obj);
- if (!ok)
- goto out;
-
- /* Now that obj is unlocked, it's safe to (potentially) grab the GC lock. */
- if (argc == 0) {
- str = cx->regExpStatics.input;
- if (!str) {
- JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
- JSMSG_NO_INPUT,
- JS_GetStringBytes(re->source),
- (re->flags & JSREG_GLOB) ? "g" : "",
- (re->flags & JSREG_FOLD) ? "i" : "",
- (re->flags & JSREG_MULTILINE) ? "m" : "");
- ok = JS_FALSE;
- goto out;
- }
- } else {
- str = js_ValueToString(cx, argv[0]);
- if (!str)
- goto out;
- argv[0] = STRING_TO_JSVAL(str);
- }
-
- if (lastIndex < 0 || JSSTRING_LENGTH(str) < lastIndex) {
- ok = js_SetLastIndex(cx, obj, 0);
- *rval = JSVAL_NULL;
- } else {
- i = (size_t) lastIndex;
- ok = js_ExecuteRegExp(cx, re, str, &i, test, rval);
- if (ok && (re->flags & JSREG_GLOB))
- ok = js_SetLastIndex(cx, obj, (*rval == JSVAL_NULL) ? 0 : i);
- }
-
-out:
- DROP_REGEXP(cx, re);
- return ok;
-}
-
-static JSBool
-regexp_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- return regexp_exec_sub(cx, obj, argc, argv, JS_FALSE, rval);
-}
-
-static JSBool
-regexp_test(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- if (!regexp_exec_sub(cx, obj, argc, argv, JS_TRUE, rval))
- return JS_FALSE;
- if (*rval != JSVAL_TRUE)
- *rval = JSVAL_FALSE;
- return JS_TRUE;
-}
-
-static JSFunctionSpec regexp_methods[] = {
-#if JS_HAS_TOSOURCE
- {js_toSource_str, regexp_toString, 0,0,0},
-#endif
- {js_toString_str, regexp_toString, 0,0,0},
- {"compile", regexp_compile, 1,0,0},
- {"exec", regexp_exec, 0,0,0},
- {"test", regexp_test, 0,0,0},
- {0,0,0,0,0}
-};
-
-static JSBool
-RegExp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
- /*
- * If first arg is regexp and no flags are given, just return the arg.
- * (regexp_compile detects the regexp + flags case and throws a
- * TypeError.) See 10.15.3.1.
- */
- if ((argc < 2 || JSVAL_IS_VOID(argv[1])) && JSVAL_IS_OBJECT(argv[0]) &&
- OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[0])) == &js_RegExpClass) {
- *rval = argv[0];
- return JS_TRUE;
- }
-
- /* Otherwise, replace obj with a new RegExp object. */
- obj = js_NewObject(cx, &js_RegExpClass, NULL, NULL);
- if (!obj)
- return JS_FALSE;
- }
- return regexp_compile(cx, obj, argc, argv, rval);
-}
-
-JSObject *
-js_InitRegExpClass(JSContext *cx, JSObject *obj)
-{
- JSObject *proto, *ctor;
- jsval rval;
-
- proto = JS_InitClass(cx, obj, NULL, &js_RegExpClass, RegExp, 1,
- regexp_props, regexp_methods,
- regexp_static_props, NULL);
-
- if (!proto || !(ctor = JS_GetConstructor(cx, proto)))
- return NULL;
- if (!JS_AliasProperty(cx, ctor, "input", "$_") ||
- !JS_AliasProperty(cx, ctor, "multiline", "$*") ||
- !JS_AliasProperty(cx, ctor, "lastMatch", "$&") ||
- !JS_AliasProperty(cx, ctor, "lastParen", "$+") ||
- !JS_AliasProperty(cx, ctor, "leftContext", "$`") ||
- !JS_AliasProperty(cx, ctor, "rightContext", "$'")) {
- goto bad;
- }
-
- /* Give RegExp.prototype private data so it matches the empty string. */
- if (!regexp_compile(cx, proto, 0, NULL, &rval))
- goto bad;
- return proto;
-
-bad:
- JS_DeleteProperty(cx, obj, js_RegExpClass.name);
- return NULL;
-}
-
-JSObject *
-js_NewRegExpObject(JSContext *cx, JSTokenStream *ts,
- jschar *chars, size_t length, uintN flags)
-{
- JSString *str;
- JSObject *obj;
- JSRegExp *re;
-
- str = js_NewStringCopyN(cx, chars, length, 0);
- if (!str)
- return NULL;
- re = js_NewRegExp(cx, ts, str, flags, JS_FALSE);
- if (!re)
- return NULL;
- obj = js_NewObject(cx, &js_RegExpClass, NULL, NULL);
- if (!obj || !JS_SetPrivate(cx, obj, re) || !js_SetLastIndex(cx, obj, 0)) {
- js_DestroyRegExp(cx, re);
- return NULL;
- }
- return obj;
-}
-
-JSObject *
-js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *parent)
-{
- JSObject *clone;
- JSRegExp *re;
-
- JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_RegExpClass);
- clone = js_NewObject(cx, &js_RegExpClass, NULL, parent);
- if (!clone)
- return NULL;
- re = JS_GetPrivate(cx, obj);
- if (!JS_SetPrivate(cx, clone, re) || !js_SetLastIndex(cx, clone, 0)) {
- cx->newborn[GCX_OBJECT] = NULL;
- return NULL;
- }
- HOLD_REGEXP(cx, re);
- return clone;
-}
-
-JSBool
-js_GetLastIndex(JSContext *cx, JSObject *obj, jsdouble *lastIndex)
-{
- jsval v;
-
- return JS_GetReservedSlot(cx, obj, 0, &v) &&
- js_ValueToNumber(cx, v, lastIndex);
-}
-
-JSBool
-js_SetLastIndex(JSContext *cx, JSObject *obj, jsdouble lastIndex)
-{
- jsval v;
-
- return js_NewNumberValue(cx, lastIndex, &v) &&
- JS_SetReservedSlot(cx, obj, 0, v);
-}
-
-#endif /* JS_HAS_REGEXPS */