64 |
#include "jsregexp.h" |
#include "jsregexp.h" |
65 |
#include "jsscan.h" |
#include "jsscan.h" |
66 |
#include "jsscope.h" |
#include "jsscope.h" |
67 |
|
#include "jsstaticcheck.h" |
68 |
#include "jsstr.h" |
#include "jsstr.h" |
69 |
|
|
70 |
#ifdef JS_TRACER |
#ifdef JS_TRACER |
71 |
#include "jstracer.h" |
#include "jstracer.h" |
72 |
using namespace avmplus; |
using namespace avmplus; |
73 |
using namespace nanojit; |
using namespace nanojit; |
|
|
|
|
/* |
|
|
* FIXME Duplicated with jstracer.cpp, doing it this way for now |
|
|
* to keep it private to files that need it. |
|
|
*/ |
|
|
#ifdef JS_JIT_SPEW |
|
|
static bool verbose_debug = getenv("TRACEMONKEY") && strstr(getenv("TRACEMONKEY"), "verbose"); |
|
|
#define debug_only_v(x) if (verbose_debug) { x; } |
|
|
#else |
|
|
#define debug_only_v(x) |
|
|
#endif |
|
74 |
#endif |
#endif |
75 |
|
|
76 |
typedef enum REOp { |
typedef enum REOp { |
1267 |
/* Treat this as an octal escape. */ |
/* Treat this as an octal escape. */ |
1268 |
goto doOctal; |
goto doOctal; |
1269 |
} |
} |
1270 |
JS_ASSERT(1 <= num && num <= 0x10000); |
|
1271 |
|
/* |
1272 |
|
* When FindParenCount calls the regex parser recursively (to find |
1273 |
|
* the number of backrefs) num can be arbitrary and the maximum |
1274 |
|
* supported number of backrefs does not bound it. |
1275 |
|
*/ |
1276 |
|
JS_ASSERT_IF(!(state->flags & JSREG_FIND_PAREN_COUNT), |
1277 |
|
1 <= num && num <= 0x10000); |
1278 |
state->result = NewRENode(state, REOP_BACKREF); |
state->result = NewRENode(state, REOP_BACKREF); |
1279 |
if (!state->result) |
if (!state->result) |
1280 |
return JS_FALSE; |
return JS_FALSE; |
1957 |
goto cleanup; |
goto cleanup; |
1958 |
} |
} |
1959 |
|
|
1960 |
|
static JSBool |
1961 |
|
CompileRegExpToAST(JSContext* cx, JSTokenStream* ts, |
1962 |
|
JSString* str, uintN flags, CompilerState& state) |
1963 |
|
{ |
1964 |
|
uintN i; |
1965 |
|
size_t len; |
1966 |
|
|
1967 |
|
len = JSSTRING_LENGTH(str); |
1968 |
|
|
1969 |
|
state.context = cx; |
1970 |
|
state.tokenStream = ts; |
1971 |
|
state.cp = js_UndependString(cx, str); |
1972 |
|
if (!state.cp) |
1973 |
|
return JS_FALSE; |
1974 |
|
state.cpbegin = state.cp; |
1975 |
|
state.cpend = state.cp + len; |
1976 |
|
state.flags = flags; |
1977 |
|
state.parenCount = 0; |
1978 |
|
state.classCount = 0; |
1979 |
|
state.progLength = 0; |
1980 |
|
state.treeDepth = 0; |
1981 |
|
state.classBitmapsMem = 0; |
1982 |
|
for (i = 0; i < CLASS_CACHE_SIZE; i++) |
1983 |
|
state.classCache[i].start = NULL; |
1984 |
|
|
1985 |
|
if (len != 0 && (flags & JSREG_FLAT)) { |
1986 |
|
state.result = NewRENode(&state, REOP_FLAT); |
1987 |
|
if (!state.result) |
1988 |
|
return JS_FALSE; |
1989 |
|
state.result->u.flat.chr = *state.cpbegin; |
1990 |
|
state.result->u.flat.length = len; |
1991 |
|
state.result->kid = (void *) state.cpbegin; |
1992 |
|
/* Flat bytecode: REOP_FLAT compact(string_offset) compact(len). */ |
1993 |
|
state.progLength += 1 + GetCompactIndexWidth(0) |
1994 |
|
+ GetCompactIndexWidth(len); |
1995 |
|
return JS_TRUE; |
1996 |
|
} |
1997 |
|
|
1998 |
|
return ParseRegExp(&state); |
1999 |
|
} |
2000 |
|
|
2001 |
#ifdef JS_TRACER |
#ifdef JS_TRACER |
2002 |
typedef List<LIns*, LIST_NonGCObjects> LInsList; |
typedef List<LIns*, LIST_NonGCObjects> LInsList; |
2003 |
|
|
2004 |
/* Dummy GC for nanojit placement new. */ |
/* Dummy GC for nanojit placement new. */ |
2005 |
static GC gc; |
static GC gc; |
2006 |
|
|
2007 |
|
static void* |
2008 |
|
HashRegExp(uint16 flags, jschar* s, size_t n) |
2009 |
|
{ |
2010 |
|
uint32 h; |
2011 |
|
|
2012 |
|
for (h = 0; n; s++, n--) |
2013 |
|
h = JS_ROTATE_LEFT32(h, 4) ^ *s; |
2014 |
|
return (void*)(h + flags); |
2015 |
|
} |
2016 |
|
|
2017 |
|
struct RESideExit : public SideExit { |
2018 |
|
size_t re_length; |
2019 |
|
uint16 re_flags; |
2020 |
|
jschar re_chars[1]; |
2021 |
|
}; |
2022 |
|
|
2023 |
|
/* Return the cached fragment for the given regexp, or NULL. */ |
2024 |
|
static Fragment* |
2025 |
|
LookupNativeRegExp(JSContext* cx, void* hash, uint16 re_flags, |
2026 |
|
jschar* re_chars, size_t re_length) |
2027 |
|
{ |
2028 |
|
Fragmento* fragmento = JS_TRACE_MONITOR(cx).reFragmento; |
2029 |
|
Fragment* fragment = fragmento->getLoop(hash); |
2030 |
|
while (fragment) { |
2031 |
|
if (fragment->lastIns) { |
2032 |
|
RESideExit* exit = (RESideExit*)fragment->lastIns->record()->exit; |
2033 |
|
if (exit->re_flags == re_flags && |
2034 |
|
exit->re_length == re_length && |
2035 |
|
!memcmp(exit->re_chars, re_chars, re_length * sizeof(jschar))) { |
2036 |
|
return fragment; |
2037 |
|
} |
2038 |
|
} |
2039 |
|
fragment = fragment->peer; |
2040 |
|
} |
2041 |
|
return NULL; |
2042 |
|
} |
2043 |
|
|
2044 |
|
static JSBool |
2045 |
|
ProcessCharSet(JSContext *cx, JSRegExp *re, RECharSet *charSet); |
2046 |
|
|
2047 |
class RegExpNativeCompiler { |
class RegExpNativeCompiler { |
2048 |
private: |
private: |
2049 |
JSRegExp* re; /* Careful: not fully initialized */ |
JSContext* cx; |
2050 |
CompilerState* cs; /* RegExp to compile */ |
JSRegExp* re; |
2051 |
|
CompilerState* cs; /* RegExp to compile */ |
2052 |
Fragment* fragment; |
Fragment* fragment; |
2053 |
LirWriter* lir; |
LirWriter* lir; |
2054 |
|
LirBufWriter* lirBufWriter; /* for skip */ |
2055 |
|
|
2056 |
LIns* state; |
LIns* state; |
2057 |
LIns* gdata; |
LIns* gdata; |
2058 |
LIns* cpend; |
LIns* cpend; |
2059 |
|
|
2060 |
JSBool isCaseInsensitive() const { return cs->flags & JSREG_FOLD; } |
JSBool isCaseInsensitive() const { return (cs->flags & JSREG_FOLD) != 0; } |
2061 |
|
|
2062 |
void targetCurrentPoint(LIns* ins) { ins->target(lir->ins0(LIR_label)); } |
JSBool targetCurrentPoint(LIns* ins) |
2063 |
|
{ |
2064 |
|
if (fragment->lirbuf->outOMem()) |
2065 |
|
return JS_FALSE; |
2066 |
|
ins->target(lir->ins0(LIR_label)); |
2067 |
|
return JS_TRUE; |
2068 |
|
} |
2069 |
|
|
2070 |
void targetCurrentPoint(LInsList& fails) |
JSBool targetCurrentPoint(LInsList& fails) |
2071 |
{ |
{ |
2072 |
|
if (fragment->lirbuf->outOMem()) |
2073 |
|
return JS_FALSE; |
2074 |
LIns* fail = lir->ins0(LIR_label); |
LIns* fail = lir->ins0(LIR_label); |
2075 |
for (size_t i = 0; i < fails.size(); ++i) { |
for (size_t i = 0; i < fails.size(); ++i) { |
2076 |
fails[i]->target(fail); |
fails[i]->target(fail); |
2077 |
} |
} |
2078 |
fails.clear(); |
fails.clear(); |
2079 |
|
return JS_TRUE; |
2080 |
} |
} |
2081 |
|
|
2082 |
/* |
/* |
2088 |
return pos; |
return pos; |
2089 |
} |
} |
2090 |
|
|
2091 |
LIns* compileFlatSingleChar(RENode* node, LIns* pos, LInsList& fails) |
LIns* compileFlatSingleChar(jschar ch, LIns* pos, LInsList& fails) |
2092 |
{ |
{ |
2093 |
/* |
/* |
2094 |
* Fast case-insensitive test for ASCII letters: convert text |
* Fast case-insensitive test for ASCII letters: convert text |
2095 |
* char to lower case by bit-or-ing in 32 and compare. |
* char to lower case by bit-or-ing in 32 and compare. |
2096 |
*/ |
*/ |
2097 |
JSBool useFastCI = JS_FALSE; |
JSBool useFastCI = JS_FALSE; |
|
jschar ch = node->u.flat.chr; /* char to test for */ |
|
2098 |
jschar ch2 = ch; /* 2nd char to test for if ci */ |
jschar ch2 = ch; /* 2nd char to test for if ci */ |
2099 |
if (cs->flags & JSREG_FOLD) { |
if (cs->flags & JSREG_FOLD) { |
2100 |
if (L'A' <= ch && ch <= L'Z' || L'a' <= ch || ch <= L'z') { |
if ((L'A' <= ch && ch <= L'Z') || (L'a' <= ch && ch <= L'z')) { |
2101 |
ch |= 32; |
ch |= 32; |
2102 |
ch2 = ch; |
ch2 = ch; |
2103 |
useFastCI = JS_TRUE; |
useFastCI = JS_TRUE; |
2118 |
} else { |
} else { |
2119 |
LIns* to_ok = lir->insBranch(LIR_jt, lir->ins2(LIR_eq, comp_ch, lir->insImm(ch)), 0); |
LIns* to_ok = lir->insBranch(LIR_jt, lir->ins2(LIR_eq, comp_ch, lir->insImm(ch)), 0); |
2120 |
fails.add(lir->insBranch(LIR_jf, lir->ins2(LIR_eq, comp_ch, lir->insImm(ch2)), 0)); |
fails.add(lir->insBranch(LIR_jf, lir->ins2(LIR_eq, comp_ch, lir->insImm(ch2)), 0)); |
2121 |
targetCurrentPoint(to_ok); |
if (!targetCurrentPoint(to_ok)) |
2122 |
|
return NULL; |
2123 |
} |
} |
2124 |
|
|
2125 |
return lir->ins2(LIR_piadd, pos, lir->insImm(2)); |
return lir->ins2(LIR_piadd, pos, lir->insImm(2)); |
2126 |
} |
} |
2127 |
|
|
2128 |
|
LIns* compileFlatDoubleChar(jschar ch1, jschar ch2, LIns* pos, |
2129 |
|
LInsList& fails) |
2130 |
|
{ |
2131 |
|
#ifdef IS_BIG_ENDIAN |
2132 |
|
uint32 word = (ch1 << 16) | ch2; |
2133 |
|
#else |
2134 |
|
uint32 word = (ch2 << 16) | ch1; |
2135 |
|
#endif |
2136 |
|
/* |
2137 |
|
* Fast case-insensitive test for ASCII letters: convert text |
2138 |
|
* char to lower case by bit-or-ing in 32 and compare. |
2139 |
|
*/ |
2140 |
|
JSBool useFastCI = JS_FALSE; |
2141 |
|
union { jschar c[2]; uint32 i; } mask; |
2142 |
|
if (cs->flags & JSREG_FOLD) { |
2143 |
|
JSBool mask1 = (L'A' <= ch1 && ch1 <= L'Z') || (L'a' <= ch1 && ch1 <= L'z'); |
2144 |
|
JSBool mask2 = (L'A' <= ch2 && ch2 <= L'Z') || (L'a' <= ch2 && ch2 <= L'z'); |
2145 |
|
if ((!mask1 && JS_TOLOWER(ch1) != ch1) || (!mask2 && JS_TOLOWER(ch2) != ch2)) { |
2146 |
|
pos = compileFlatSingleChar(ch1, pos, fails); |
2147 |
|
if (!pos) return NULL; |
2148 |
|
return compileFlatSingleChar(ch2, pos, fails); |
2149 |
|
} |
2150 |
|
|
2151 |
|
mask.c[0] = mask1 ? 0x0020 : 0x0; |
2152 |
|
mask.c[1] = mask2 ? 0x0020 : 0x0; |
2153 |
|
|
2154 |
|
if (mask.i) { |
2155 |
|
word |= mask.i; |
2156 |
|
useFastCI = JS_TRUE; |
2157 |
|
} |
2158 |
|
} |
2159 |
|
|
2160 |
|
LIns* to_fail = lir->insBranch(LIR_jf, lir->ins2(LIR_lt, pos, lir->ins2(LIR_sub, cpend, lir->insImm(2))), 0); |
2161 |
|
fails.add(to_fail); |
2162 |
|
LIns* text_word = lir->insLoad(LIR_ld, pos, lir->insImm(0)); |
2163 |
|
LIns* comp_word = useFastCI ? |
2164 |
|
lir->ins2(LIR_or, text_word, lir->insImm(mask.i)) : |
2165 |
|
text_word; |
2166 |
|
fails.add(lir->insBranch(LIR_jf, lir->ins2(LIR_eq, comp_word, lir->insImm(word)), 0)); |
2167 |
|
|
2168 |
|
return lir->ins2(LIR_piadd, pos, lir->insImm(4)); |
2169 |
|
} |
2170 |
|
|
2171 |
LIns* compileClass(RENode* node, LIns* pos, LInsList& fails) |
LIns* compileClass(RENode* node, LIns* pos, LInsList& fails) |
2172 |
{ |
{ |
2173 |
if (!node->u.ucclass.sense) |
if (!node->u.ucclass.sense) |
2174 |
return JS_FALSE; |
return JS_FALSE; |
2175 |
|
/* |
2176 |
|
* If we share generated native code, we need to make a copy |
2177 |
|
* of the bitmap because the original regexp's copy is destroyed |
2178 |
|
* when that regexp is. |
2179 |
|
*/ |
2180 |
|
RECharSet *charSet = &re->classList[node->u.ucclass.index]; |
2181 |
|
size_t bitmapLen = (charSet->length >> 3) + 1; |
2182 |
|
/* skip() can't hold large data blocks. */ |
2183 |
|
if (bitmapLen > 1024) |
2184 |
|
return NULL; |
2185 |
|
/* The following line allocates charSet.u.bits if successful. */ |
2186 |
|
if (!charSet->converted && !ProcessCharSet(cx, re, charSet)) |
2187 |
|
return NULL; |
2188 |
|
LIns* skip = lirBufWriter->skip(bitmapLen); |
2189 |
|
if (fragment->lirbuf->outOMem()) |
2190 |
|
return NULL; |
2191 |
|
void* bitmapData = skip->payload(); |
2192 |
|
memcpy(bitmapData, charSet->u.bits, bitmapLen); |
2193 |
|
|
|
RECharSet* charSet = InitNodeCharSet(re, node); |
|
2194 |
LIns* to_fail = lir->insBranch(LIR_jf, lir->ins2(LIR_lt, pos, cpend), 0); |
LIns* to_fail = lir->insBranch(LIR_jf, lir->ins2(LIR_lt, pos, cpend), 0); |
2195 |
fails.add(to_fail); |
fails.add(to_fail); |
2196 |
LIns* text_ch = lir->insLoad(LIR_ldcs, pos, lir->insImm(0)); |
LIns* text_ch = lir->insLoad(LIR_ldcs, pos, lir->insImm(0)); |
2197 |
fails.add(lir->insBranch(LIR_jf, lir->ins2(LIR_le, text_ch, lir->insImm(charSet->length)), 0)); |
fails.add(lir->insBranch(LIR_jf, lir->ins2(LIR_le, text_ch, lir->insImm(charSet->length)), 0)); |
2198 |
LIns* byteIndex = lir->ins2(LIR_rsh, text_ch, lir->insImm(3)); |
LIns* byteIndex = lir->ins2(LIR_rsh, text_ch, lir->insImm(3)); |
2199 |
LIns* bitmap = lir->insLoad(LIR_ld, lir->insImmPtr(charSet), (int) offsetof(RECharSet, u.bits)); |
LIns* bitmap = lir->insImmPtr(bitmapData); |
2200 |
LIns* byte = lir->insLoad(LIR_ldcb, lir->ins2(LIR_piadd, bitmap, byteIndex), (int) 0); |
LIns* byte = lir->insLoad(LIR_ldcb, lir->ins2(LIR_piadd, bitmap, byteIndex), (int) 0); |
2201 |
LIns* bitMask = lir->ins2(LIR_lsh, lir->insImm(1), |
LIns* bitMask = lir->ins2(LIR_lsh, lir->insImm(1), |
2202 |
lir->ins2(LIR_and, text_ch, lir->insImm(0x7))); |
lir->ins2(LIR_and, text_ch, lir->insImm(0x7))); |
2211 |
{ |
{ |
2212 |
LInsList kidFails(NULL); |
LInsList kidFails(NULL); |
2213 |
if (!compileNode((RENode *) node->kid, pos, kidFails)) |
if (!compileNode((RENode *) node->kid, pos, kidFails)) |
2214 |
return JS_FALSE; |
return NULL; |
2215 |
if (!compileNode(node->next, pos, kidFails)) |
if (!compileNode(node->next, pos, kidFails)) |
2216 |
return JS_FALSE; |
return NULL; |
2217 |
|
|
2218 |
targetCurrentPoint(kidFails); |
if (!targetCurrentPoint(kidFails)) |
2219 |
|
return NULL; |
2220 |
if (!compileNode(node->u.altprereq.kid2, pos, fails)) |
if (!compileNode(node->u.altprereq.kid2, pos, fails)) |
2221 |
return JS_FALSE; |
return NULL; |
2222 |
/* |
/* |
2223 |
* Disable compilation for any regexp where something follows an |
* Disable compilation for any regexp where something follows an |
2224 |
* alternation. To make this work, we need to redesign to either |
* alternation. To make this work, we need to redesign to either |
2227 |
* code. |
* code. |
2228 |
*/ |
*/ |
2229 |
if (node->next) |
if (node->next) |
2230 |
return JS_FALSE; |
return NULL; |
2231 |
return pos; |
return pos; |
2232 |
} |
} |
2233 |
|
|
2234 |
|
#if defined(AVMPLUS_ARM) || defined(AVMPLUS_SPARC) |
2235 |
|
/* We can't do this on ARM or SPARC, since it relies on doing a 32-bit load from |
2236 |
|
* a pointer which is only 2-byte aligned. |
2237 |
|
*/ |
2238 |
|
#undef USE_DOUBLE_CHAR_MATCH |
2239 |
|
#else |
2240 |
|
#define USE_DOUBLE_CHAR_MATCH |
2241 |
|
#endif |
2242 |
|
|
2243 |
JSBool compileNode(RENode* node, LIns* pos, LInsList& fails) |
JSBool compileNode(RENode* node, LIns* pos, LInsList& fails) |
2244 |
{ |
{ |
2245 |
for (; node; node = node->next) { |
for (; node; node = node->next) { |
2246 |
if (fragment->lirbuf->outOmem()) |
if (fragment->lirbuf->outOMem()) |
2247 |
return JS_FALSE; |
return JS_FALSE; |
2248 |
|
|
2249 |
switch (node->op) { |
switch (node->op) { |
2251 |
pos = compileEmpty(node, pos, fails); |
pos = compileEmpty(node, pos, fails); |
2252 |
break; |
break; |
2253 |
case REOP_FLAT: |
case REOP_FLAT: |
2254 |
if (node->u.flat.length != 1) |
#ifdef USE_DOUBLE_CHAR_MATCH |
2255 |
return JS_FALSE; |
if (node->u.flat.length == 1) { |
2256 |
pos = compileFlatSingleChar(node, pos, fails); |
if (node->next && node->next->op == REOP_FLAT && |
2257 |
|
node->next->u.flat.length == 1) { |
2258 |
|
pos = compileFlatDoubleChar(node->u.flat.chr, |
2259 |
|
node->next->u.flat.chr, |
2260 |
|
pos, fails); |
2261 |
|
node = node->next; |
2262 |
|
} else { |
2263 |
|
pos = compileFlatSingleChar(node->u.flat.chr, pos, fails); |
2264 |
|
} |
2265 |
|
} else { |
2266 |
|
size_t i; |
2267 |
|
for (i = 0; i < node->u.flat.length - 1; i += 2) { |
2268 |
|
if (fragment->lirbuf->outOMem()) |
2269 |
|
return JS_FALSE; |
2270 |
|
pos = compileFlatDoubleChar(((jschar*) node->kid)[i], |
2271 |
|
((jschar*) node->kid)[i+1], |
2272 |
|
pos, fails); |
2273 |
|
if (!pos) break; |
2274 |
|
} |
2275 |
|
if (pos && i == node->u.flat.length - 1) |
2276 |
|
pos = compileFlatSingleChar(((jschar*) node->kid)[i], pos, fails); |
2277 |
|
} |
2278 |
|
#else |
2279 |
|
if (node->u.flat.length == 1) { |
2280 |
|
pos = compileFlatSingleChar(node->u.flat.chr, pos, fails); |
2281 |
|
} else { |
2282 |
|
for (size_t i = 0; i < node->u.flat.length; i++) { |
2283 |
|
if (fragment->lirbuf->outOMem()) |
2284 |
|
return JS_FALSE; |
2285 |
|
pos = compileFlatSingleChar(((jschar*) node->kid)[i], pos, fails); |
2286 |
|
if (!pos) break; |
2287 |
|
} |
2288 |
|
} |
2289 |
|
#endif |
2290 |
break; |
break; |
2291 |
case REOP_ALT: |
case REOP_ALT: |
2292 |
case REOP_ALTPREREQ: |
case REOP_ALTPREREQ: |
2312 |
LInsList fails(NULL); |
LInsList fails(NULL); |
2313 |
if (!compileNode(root, start, fails)) |
if (!compileNode(root, start, fails)) |
2314 |
return JS_FALSE; |
return JS_FALSE; |
2315 |
targetCurrentPoint(fails); |
if (!targetCurrentPoint(fails)) |
2316 |
|
return JS_FALSE; |
2317 |
lir->ins1(LIR_ret, lir->insImm(0)); |
lir->ins1(LIR_ret, lir->insImm(0)); |
2318 |
return JS_TRUE; |
return JS_TRUE; |
2319 |
} |
} |
2327 |
if (!compileNode(root, start, fails)) |
if (!compileNode(root, start, fails)) |
2328 |
return JS_FALSE; |
return JS_FALSE; |
2329 |
|
|
2330 |
targetCurrentPoint(to_next); |
if (!targetCurrentPoint(to_next)) |
2331 |
|
return JS_FALSE; |
2332 |
lir->ins1(LIR_ret, lir->insImm(0)); |
lir->ins1(LIR_ret, lir->insImm(0)); |
2333 |
|
|
2334 |
targetCurrentPoint(fails); |
if (!targetCurrentPoint(fails)) |
2335 |
|
return JS_FALSE; |
2336 |
lir->insStorei(lir->ins2(LIR_piadd, start, lir->insImm(2)), gdata, |
lir->insStorei(lir->ins2(LIR_piadd, start, lir->insImm(2)), gdata, |
2337 |
(int) offsetof(REGlobalData, skipped)); |
(int) offsetof(REGlobalData, skipped)); |
2338 |
|
|
2342 |
inline LIns* |
inline LIns* |
2343 |
addName(LirBuffer* lirbuf, LIns* ins, const char* name) |
addName(LirBuffer* lirbuf, LIns* ins, const char* name) |
2344 |
{ |
{ |
2345 |
|
#ifdef NJ_VERBOSE |
2346 |
debug_only_v(lirbuf->names->addName(ins, name);) |
debug_only_v(lirbuf->names->addName(ins, name);) |
2347 |
|
#endif |
2348 |
return ins; |
return ins; |
2349 |
} |
} |
2350 |
|
|
2351 |
|
/* |
2352 |
|
* Insert the side exit and guard record for a compiled regexp. Most |
2353 |
|
* of the fields are not used. The important part is the regexp source |
2354 |
|
* and flags, which we use as the fragment lookup key. |
2355 |
|
*/ |
2356 |
|
GuardRecord* insertGuard(jschar* re_chars, size_t re_length) |
2357 |
|
{ |
2358 |
|
LIns* skip = lirBufWriter->skip(sizeof(GuardRecord) + |
2359 |
|
sizeof(RESideExit) + |
2360 |
|
(re_length-1) * sizeof(jschar)); |
2361 |
|
GuardRecord* guard = (GuardRecord *) skip->payload(); |
2362 |
|
memset(guard, 0, sizeof(*guard)); |
2363 |
|
RESideExit* exit = (RESideExit*)(guard+1); |
2364 |
|
guard->exit = exit; |
2365 |
|
guard->exit->target = fragment; |
2366 |
|
exit->re_flags = re->flags; |
2367 |
|
exit->re_length = re_length; |
2368 |
|
memcpy(exit->re_chars, re_chars, re_length * sizeof(jschar)); |
2369 |
|
fragment->lastIns = lir->insGuard(LIR_loop, lir->insImm(1), skip); |
2370 |
|
return guard; |
2371 |
|
} |
2372 |
|
|
2373 |
public: |
public: |
2374 |
RegExpNativeCompiler(JSRegExp *re, CompilerState *cs) |
RegExpNativeCompiler(JSRegExp* re, CompilerState* cs, Fragment* fragment) |
2375 |
: re(re), cs(cs), fragment(NULL) { } |
: re(re), cs(cs), fragment(fragment), lir(NULL), lirBufWriter(NULL) { } |
2376 |
|
|
2377 |
JSBool compile(JSContext* cx) |
JSBool compile(JSContext* cx) |
2378 |
{ |
{ |
2379 |
GuardRecord* guard; |
GuardRecord* guard = NULL; |
|
LIns* skip; |
|
2380 |
LIns* start; |
LIns* start; |
2381 |
bool oom = false; |
bool oom = false; |
2382 |
|
jschar* re_chars; |
2383 |
|
size_t re_length; |
2384 |
Fragmento* fragmento = JS_TRACE_MONITOR(cx).reFragmento; |
Fragmento* fragmento = JS_TRACE_MONITOR(cx).reFragmento; |
2385 |
fragment = fragmento->getLoop(re); |
|
2386 |
if (!fragment) { |
JSSTRING_CHARS_AND_LENGTH(re->source, re_chars, re_length); |
2387 |
fragment = fragmento->getAnchor(re); |
/* |
2388 |
fragment->lirbuf = new (&gc) LirBuffer(fragmento, NULL); |
* If the regexp is too long nanojit will assert when we |
2389 |
/* Scary: required to have the onDestroy method delete the lirbuf. */ |
* try to insert the guard record. |
2390 |
fragment->root = fragment; |
*/ |
2391 |
} |
if (re_length > 1024) |
2392 |
|
return JS_FALSE; |
2393 |
|
|
2394 |
|
this->cx = cx; |
2395 |
|
/* At this point we have an empty fragment. */ |
2396 |
LirBuffer* lirbuf = fragment->lirbuf; |
LirBuffer* lirbuf = fragment->lirbuf; |
2397 |
LirBufWriter* lirb; |
if (lirbuf->outOMem()) |
2398 |
if (lirbuf->outOmem()) goto fail2; |
goto fail; |
2399 |
/* FIXME Use bug 463260 smart pointer when available. */ |
/* FIXME Use bug 463260 smart pointer when available. */ |
2400 |
lir = lirb = new (&gc) LirBufWriter(lirbuf); |
lir = lirBufWriter = new (&gc) LirBufWriter(lirbuf); |
2401 |
|
|
2402 |
/* FIXME Use bug 463260 smart pointer when available. */ |
/* FIXME Use bug 463260 smart pointer when available. */ |
2403 |
|
#ifdef NJ_VERBOSE |
2404 |
debug_only_v(fragment->lirbuf->names = new (&gc) LirNameMap(&gc, NULL, fragmento->labels);) |
debug_only_v(fragment->lirbuf->names = new (&gc) LirNameMap(&gc, NULL, fragmento->labels);) |
2405 |
|
#endif |
2406 |
/* FIXME Use bug 463260 smart pointer when available. */ |
/* FIXME Use bug 463260 smart pointer when available. */ |
2407 |
|
#ifdef NJ_VERBOSE |
2408 |
debug_only_v(lir = new (&gc) VerboseWriter(&gc, lir, lirbuf->names);) |
debug_only_v(lir = new (&gc) VerboseWriter(&gc, lir, lirbuf->names);) |
2409 |
|
#endif |
2410 |
|
|
2411 |
lir->ins0(LIR_start); |
lir->ins0(LIR_start); |
2412 |
lirbuf->state = state = addName(lirbuf, lir->insParam(0, 0), "state"); |
lirbuf->state = state = addName(lirbuf, lir->insParam(0, 0), "state"); |
2415 |
cpend = addName(lirbuf, lir->insLoad(LIR_ldp, lirbuf->param1, offsetof(REGlobalData, cpend)), "cpend"); |
cpend = addName(lirbuf, lir->insLoad(LIR_ldp, lirbuf->param1, offsetof(REGlobalData, cpend)), "cpend"); |
2416 |
|
|
2417 |
if (cs->flags & JSREG_STICKY) { |
if (cs->flags & JSREG_STICKY) { |
2418 |
if (!compileSticky(cs->result, start)) goto fail; |
if (!compileSticky(cs->result, start)) |
2419 |
|
goto fail; |
2420 |
} else { |
} else { |
2421 |
if (!compileAnchoring(cs->result, start)) goto fail; |
if (!compileAnchoring(cs->result, start)) |
2422 |
|
goto fail; |
2423 |
} |
} |
2424 |
|
|
2425 |
/* Create fake guard record for loop edge. */ |
guard = insertGuard(re_chars, re_length); |
|
skip = lirb->skip(sizeof(GuardRecord) + sizeof(SideExit)); |
|
|
guard = (GuardRecord *) skip->payload(); |
|
|
memset(guard, 0, sizeof(*guard)); |
|
|
guard->exit = (SideExit *) guard+1; |
|
|
guard->exit->target = fragment; |
|
|
fragment->lastIns = lir->insGuard(LIR_loop, lir->insImm(1), skip); |
|
2426 |
|
|
2427 |
|
if (lirbuf->outOMem()) |
2428 |
|
goto fail; |
2429 |
::compile(fragmento->assm(), fragment); |
::compile(fragmento->assm(), fragment); |
2430 |
if (fragmento->assm()->error() != nanojit::None) { |
if (fragmento->assm()->error() != nanojit::None) { |
2431 |
oom = fragmento->assm()->error() == nanojit::OutOMem; |
oom = fragmento->assm()->error() == nanojit::OutOMem; |
2432 |
goto fail; |
goto fail; |
2433 |
} |
} |
2434 |
|
|
2435 |
delete lirb; |
delete lirBufWriter; |
2436 |
|
#ifdef NJ_VERBOSE |
2437 |
debug_only_v(delete lir;) |
debug_only_v(delete lir;) |
2438 |
|
#endif |
2439 |
return JS_TRUE; |
return JS_TRUE; |
2440 |
fail: |
fail: |
2441 |
delete lirb; |
if (lirbuf->outOMem() || oom || |
2442 |
debug_only_v(delete lir;) |
js_OverfullFragmento(&JS_TRACE_MONITOR(cx), fragmento)) { |
|
fail2: |
|
|
if (lirbuf->outOmem() || oom) |
|
2443 |
fragmento->clearFrags(); |
fragmento->clearFrags(); |
2444 |
|
lirbuf->rewind(); |
2445 |
|
} else { |
2446 |
|
if (!guard) insertGuard(re_chars, re_length); |
2447 |
|
fragment->blacklist(); |
2448 |
|
} |
2449 |
|
delete lirBufWriter; |
2450 |
|
#ifdef NJ_VERBOSE |
2451 |
|
debug_only_v(delete lir;) |
2452 |
|
#endif |
2453 |
return JS_FALSE; |
return JS_FALSE; |
2454 |
} |
} |
2455 |
}; |
}; |
2456 |
|
|
2457 |
|
/* |
2458 |
|
* Compile a regexp to native code in the given fragment. |
2459 |
|
*/ |
2460 |
static inline JSBool |
static inline JSBool |
2461 |
js_CompileRegExpToNative(JSContext *cx, JSRegExp *re, CompilerState *cs) |
CompileRegExpToNative(JSContext* cx, JSRegExp* re, Fragment* fragment) |
2462 |
|
{ |
2463 |
|
JSBool rv = JS_FALSE; |
2464 |
|
void* mark; |
2465 |
|
CompilerState state; |
2466 |
|
RegExpNativeCompiler rc(re, &state, fragment); |
2467 |
|
|
2468 |
|
JS_ASSERT(!fragment->code()); |
2469 |
|
JS_ASSERT(!fragment->isBlacklisted()); |
2470 |
|
|
2471 |
|
mark = JS_ARENA_MARK(&cx->tempPool); |
2472 |
|
if (!CompileRegExpToAST(cx, NULL, re->source, re->flags, state)) { |
2473 |
|
goto out; |
2474 |
|
} |
2475 |
|
rv = rc.compile(cx); |
2476 |
|
out: |
2477 |
|
JS_ARENA_RELEASE(&cx->tempPool, mark); |
2478 |
|
return rv; |
2479 |
|
} |
2480 |
|
|
2481 |
|
/* Function type for a compiled native regexp. */ |
2482 |
|
typedef REMatchState* (FASTCALL *NativeRegExp)(REMatchState*, REGlobalData*); |
2483 |
|
|
2484 |
|
/* |
2485 |
|
* Return a compiled native regexp if one already exists or can be created |
2486 |
|
* now, or NULL otherwise. |
2487 |
|
*/ |
2488 |
|
static NativeRegExp |
2489 |
|
GetNativeRegExp(JSContext* cx, JSRegExp* re) |
2490 |
{ |
{ |
2491 |
RegExpNativeCompiler rc(re, cs); |
Fragment *fragment; |
2492 |
return rc.compile(cx); |
jschar* re_chars; |
2493 |
|
size_t re_length; |
2494 |
|
Fragmento* fragmento = JS_TRACE_MONITOR(cx).reFragmento; |
2495 |
|
|
2496 |
|
JSSTRING_CHARS_AND_LENGTH(re->source, re_chars, re_length); |
2497 |
|
void* hash = HashRegExp(re->flags, re_chars, re_length); |
2498 |
|
fragment = LookupNativeRegExp(cx, hash, re->flags, re_chars, re_length); |
2499 |
|
if (fragment) { |
2500 |
|
if (fragment->code()) |
2501 |
|
goto ok; |
2502 |
|
if (fragment->isBlacklisted()) |
2503 |
|
return NULL; |
2504 |
|
} else { |
2505 |
|
fragment = fragmento->getAnchor(hash); |
2506 |
|
fragment->lirbuf = JS_TRACE_MONITOR(cx).reLirBuf; |
2507 |
|
fragment->root = fragment; |
2508 |
|
} |
2509 |
|
|
2510 |
|
if (!CompileRegExpToNative(cx, re, fragment)) |
2511 |
|
return NULL; |
2512 |
|
ok: |
2513 |
|
union { NIns *code; NativeRegExp func; } u; |
2514 |
|
u.code = fragment->code(); |
2515 |
|
return u.func; |
2516 |
} |
} |
2517 |
#endif |
#endif |
2518 |
|
|
2526 |
size_t resize; |
size_t resize; |
2527 |
jsbytecode *endPC; |
jsbytecode *endPC; |
2528 |
uintN i; |
uintN i; |
|
size_t len; |
|
2529 |
|
|
2530 |
re = NULL; |
re = NULL; |
2531 |
mark = JS_ARENA_MARK(&cx->tempPool); |
mark = JS_ARENA_MARK(&cx->tempPool); |
|
len = JSSTRING_LENGTH(str); |
|
2532 |
|
|
2533 |
state.context = cx; |
/* |
2534 |
state.tokenStream = ts; |
* Parsing the string as flat is now expressed internally using |
2535 |
state.cp = js_UndependString(cx, str); |
* a flag, so that we keep this information in the JSRegExp, but |
2536 |
if (!state.cp) |
* we keep the 'flat' parameter for now for compatibility. |
2537 |
|
*/ |
2538 |
|
if (flat) flags |= JSREG_FLAT; |
2539 |
|
if (!CompileRegExpToAST(cx, ts, str, flags, state)) |
2540 |
goto out; |
goto out; |
|
state.cpbegin = state.cp; |
|
|
state.cpend = state.cp + len; |
|
|
state.flags = flags; |
|
|
state.parenCount = 0; |
|
|
state.classCount = 0; |
|
|
state.progLength = 0; |
|
|
state.treeDepth = 0; |
|
|
state.classBitmapsMem = 0; |
|
|
for (i = 0; i < CLASS_CACHE_SIZE; i++) |
|
|
state.classCache[i].start = NULL; |
|
|
|
|
|
if (len != 0 && flat) { |
|
|
state.result = NewRENode(&state, REOP_FLAT); |
|
|
if (!state.result) |
|
|
goto out; |
|
|
state.result->u.flat.chr = *state.cpbegin; |
|
|
state.result->u.flat.length = len; |
|
|
state.result->kid = (void *) state.cpbegin; |
|
|
/* Flat bytecode: REOP_FLAT compact(string_offset) compact(len). */ |
|
|
state.progLength += 1 + GetCompactIndexWidth(0) |
|
|
+ GetCompactIndexWidth(len); |
|
|
} else { |
|
|
if (!ParseRegExp(&state)) |
|
|
goto out; |
|
|
} |
|
2541 |
|
|
2542 |
resize = offsetof(JSRegExp, program) + state.progLength + 1; |
resize = offsetof(JSRegExp, program) + state.progLength + 1; |
2543 |
re = (JSRegExp *) JS_malloc(cx, resize); |
re = (JSRegExp *) JS_malloc(cx, resize); |
2561 |
re->classList = NULL; |
re->classList = NULL; |
2562 |
} |
} |
2563 |
|
|
|
#ifdef JS_TRACER |
|
|
/* |
|
|
* Try compiling the native code version. For the time being we also |
|
|
* compile the bytecode version in case we evict the native code |
|
|
* version from the code cache. |
|
|
*/ |
|
|
if (TRACING_ENABLED(cx)) |
|
|
js_CompileRegExpToNative(cx, re, &state); |
|
|
#endif |
|
2564 |
/* Compile the bytecode version. */ |
/* Compile the bytecode version. */ |
2565 |
endPC = EmitREBytecode(&state, re, state.treeDepth, re->program, state.result); |
endPC = EmitREBytecode(&state, re, state.treeDepth, re->program, state.result); |
2566 |
if (!endPC) { |
if (!endPC) { |
2574 |
* This is safe since no pointers to newly parsed regexp or its parts |
* This is safe since no pointers to newly parsed regexp or its parts |
2575 |
* besides re exist here. |
* besides re exist here. |
2576 |
*/ |
*/ |
|
#if 0 |
|
|
/* |
|
|
* FIXME: Until bug 464866 is fixed, we can't move the re object so |
|
|
* don't shrink it for now. |
|
|
*/ |
|
2577 |
if ((size_t)(endPC - re->program) != state.progLength + 1) { |
if ((size_t)(endPC - re->program) != state.progLength + 1) { |
2578 |
JSRegExp *tmp; |
JSRegExp *tmp; |
2579 |
JS_ASSERT((size_t)(endPC - re->program) < state.progLength + 1); |
JS_ASSERT((size_t)(endPC - re->program) < state.progLength + 1); |
2582 |
if (tmp) |
if (tmp) |
2583 |
re = tmp; |
re = tmp; |
2584 |
} |
} |
|
#endif |
|
2585 |
|
|
2586 |
re->flags = flags; |
re->flags = flags; |
2587 |
re->parenCount = state.parenCount; |
re->parenCount = state.parenCount; |
2604 |
if (opt) { |
if (opt) { |
2605 |
JSSTRING_CHARS_AND_LENGTH(opt, s, n); |
JSSTRING_CHARS_AND_LENGTH(opt, s, n); |
2606 |
for (i = 0; i < n; i++) { |
for (i = 0; i < n; i++) { |
2607 |
|
#define HANDLE_FLAG(name) \ |
2608 |
|
JS_BEGIN_MACRO \ |
2609 |
|
if (flags & (name)) \ |
2610 |
|
goto bad_flag; \ |
2611 |
|
flags |= (name); \ |
2612 |
|
JS_END_MACRO |
2613 |
switch (s[i]) { |
switch (s[i]) { |
2614 |
case 'g': |
case 'g': |
2615 |
flags |= JSREG_GLOB; |
HANDLE_FLAG(JSREG_GLOB); |
2616 |
break; |
break; |
2617 |
case 'i': |
case 'i': |
2618 |
flags |= JSREG_FOLD; |
HANDLE_FLAG(JSREG_FOLD); |
2619 |
break; |
break; |
2620 |
case 'm': |
case 'm': |
2621 |
flags |= JSREG_MULTILINE; |
HANDLE_FLAG(JSREG_MULTILINE); |
2622 |
break; |
break; |
2623 |
case 'y': |
case 'y': |
2624 |
flags |= JSREG_STICKY; |
HANDLE_FLAG(JSREG_STICKY); |
2625 |
break; |
break; |
2626 |
default: |
default: |
2627 |
|
bad_flag: |
2628 |
charBuf[0] = (char)s[i]; |
charBuf[0] = (char)s[i]; |
2629 |
charBuf[1] = '\0'; |
charBuf[1] = '\0'; |
2630 |
JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, |
JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, |
2631 |
js_GetErrorMessage, NULL, |
js_GetErrorMessage, NULL, |
2632 |
JSMSG_BAD_FLAG, charBuf); |
JSMSG_BAD_REGEXP_FLAG, charBuf); |
2633 |
return NULL; |
return NULL; |
2634 |
} |
} |
2635 |
|
#undef HANDLE_FLAG |
2636 |
} |
} |
2637 |
} |
} |
2638 |
return js_NewRegExp(cx, NULL, str, flags, flat); |
return js_NewRegExp(cx, NULL, str, flags, flat); |
2664 |
re_debug("\tBT_Push: %lu,%lu", |
re_debug("\tBT_Push: %lu,%lu", |
2665 |
(unsigned long) parenIndex, (unsigned long) parenCount); |
(unsigned long) parenIndex, (unsigned long) parenCount); |
2666 |
|
|
|
JS_COUNT_OPERATION(gData->cx, JSOW_JUMP * (1 + parenCount)); |
|
2667 |
if (btincr > 0) { |
if (btincr > 0) { |
2668 |
ptrdiff_t offset = (char *)result - (char *)gData->backTrackStack; |
ptrdiff_t offset = (char *)result - (char *)gData->backTrackStack; |
2669 |
|
|
|
JS_COUNT_OPERATION(gData->cx, JSOW_ALLOCATION); |
|
2670 |
btincr = JS_ROUNDUP(btincr, btsize); |
btincr = JS_ROUNDUP(btincr, btsize); |
2671 |
JS_ARENA_GROW_CAST(gData->backTrackStack, REBackTrackData *, |
JS_ARENA_GROW_CAST(gData->backTrackStack, REBackTrackData *, |
2672 |
&gData->cx->regexpPool, btsize, btincr); |
&gData->cx->regexpPool, btsize, btincr); |
2892 |
|
|
2893 |
/* Compile the source of the class into a RECharSet */ |
/* Compile the source of the class into a RECharSet */ |
2894 |
static JSBool |
static JSBool |
2895 |
ProcessCharSet(REGlobalData *gData, RECharSet *charSet) |
ProcessCharSet(JSContext *cx, JSRegExp *re, RECharSet *charSet) |
2896 |
{ |
{ |
2897 |
const jschar *src, *end; |
const jschar *src, *end; |
2898 |
JSBool inRange = JS_FALSE; |
JSBool inRange = JS_FALSE; |
2908 |
*/ |
*/ |
2909 |
JS_ASSERT(1 <= charSet->u.src.startIndex); |
JS_ASSERT(1 <= charSet->u.src.startIndex); |
2910 |
JS_ASSERT(charSet->u.src.startIndex |
JS_ASSERT(charSet->u.src.startIndex |
2911 |
< JSSTRING_LENGTH(gData->regexp->source)); |
< JSSTRING_LENGTH(re->source)); |
2912 |
JS_ASSERT(charSet->u.src.length <= JSSTRING_LENGTH(gData->regexp->source) |
JS_ASSERT(charSet->u.src.length <= JSSTRING_LENGTH(re->source) |
2913 |
- 1 - charSet->u.src.startIndex); |
- 1 - charSet->u.src.startIndex); |
2914 |
|
|
2915 |
charSet->converted = JS_TRUE; |
charSet->converted = JS_TRUE; |
2916 |
src = JSSTRING_CHARS(gData->regexp->source) + charSet->u.src.startIndex; |
src = JSSTRING_CHARS(re->source) + charSet->u.src.startIndex; |
2917 |
end = src + charSet->u.src.length; |
end = src + charSet->u.src.length; |
2918 |
JS_ASSERT(src[-1] == '['); |
JS_ASSERT(src[-1] == '['); |
2919 |
JS_ASSERT(end[0] == ']'); |
JS_ASSERT(end[0] == ']'); |
2920 |
|
|
2921 |
byteLength = (charSet->length >> 3) + 1; |
byteLength = (charSet->length >> 3) + 1; |
2922 |
charSet->u.bits = (uint8 *)JS_malloc(gData->cx, byteLength); |
charSet->u.bits = (uint8 *)JS_malloc(cx, byteLength); |
2923 |
if (!charSet->u.bits) { |
if (!charSet->u.bits) { |
2924 |
JS_ReportOutOfMemory(gData->cx); |
JS_ReportOutOfMemory(cx); |
|
gData->ok = JS_FALSE; |
|
2925 |
return JS_FALSE; |
return JS_FALSE; |
2926 |
} |
} |
2927 |
memset(charSet->u.bits, 0, byteLength); |
memset(charSet->u.bits, 0, byteLength); |
3060 |
|
|
3061 |
} |
} |
3062 |
if (inRange) { |
if (inRange) { |
3063 |
if (gData->regexp->flags & JSREG_FOLD) { |
if (re->flags & JSREG_FOLD) { |
3064 |
int i; |
int i; |
3065 |
|
|
3066 |
JS_ASSERT(rangeStart <= thisCh); |
JS_ASSERT(rangeStart <= thisCh); |
3080 |
} |
} |
3081 |
inRange = JS_FALSE; |
inRange = JS_FALSE; |
3082 |
} else { |
} else { |
3083 |
if (gData->regexp->flags & JSREG_FOLD) { |
if (re->flags & JSREG_FOLD) { |
3084 |
AddCharacterToCharSet(charSet, upcase(thisCh)); |
AddCharacterToCharSet(charSet, upcase(thisCh)); |
3085 |
AddCharacterToCharSet(charSet, downcase(thisCh)); |
AddCharacterToCharSet(charSet, downcase(thisCh)); |
3086 |
} else { |
} else { |
3098 |
return JS_TRUE; |
return JS_TRUE; |
3099 |
} |
} |
3100 |
|
|
3101 |
|
static inline JSBool |
3102 |
|
MatcherProcessCharSet(REGlobalData *gData, RECharSet *charSet) { |
3103 |
|
JSBool rv = ProcessCharSet(gData->cx, gData->regexp, charSet); |
3104 |
|
if (!rv) gData->ok = JS_FALSE; |
3105 |
|
return rv; |
3106 |
|
} |
3107 |
|
|
3108 |
void |
void |
3109 |
js_DestroyRegExp(JSContext *cx, JSRegExp *re) |
js_DestroyRegExp(JSContext *cx, JSRegExp *re) |
3110 |
{ |
{ |
3111 |
if (JS_ATOMIC_DECREMENT(&re->nrefs) == 0) { |
if (JS_ATOMIC_DECREMENT(&re->nrefs) == 0) { |
|
#ifdef JS_TRACER |
|
|
/* Don't reuse this compiled code for some new regexp at same addr. */ |
|
|
Fragment* fragment = JS_TRACE_MONITOR(cx).reFragmento->getLoop(re); |
|
|
if (fragment) |
|
|
fragment->blacklist(); |
|
|
#endif |
|
3112 |
if (re->classList) { |
if (re->classList) { |
3113 |
uintN i; |
uintN i; |
3114 |
for (i = 0; i < re->classCount; i++) { |
for (i = 0; i < re->classCount; i++) { |
3433 |
goto doAlt; |
goto doAlt; |
3434 |
|
|
3435 |
charSet = &gData->regexp->classList[k]; |
charSet = &gData->regexp->classList[k]; |
3436 |
if (!charSet->converted && !ProcessCharSet(gData, charSet)) |
if (!charSet->converted && !MatcherProcessCharSet(gData, charSet)) |
3437 |
goto bad; |
goto bad; |
3438 |
matchCh1 = *x->cp; |
matchCh1 = *x->cp; |
3439 |
k = matchCh1 >> 3; |
k = matchCh1 >> 3; |
3843 |
if (!result) { |
if (!result) { |
3844 |
if (gData->cursz == 0) |
if (gData->cursz == 0) |
3845 |
return NULL; |
return NULL; |
3846 |
if (!JS_CHECK_OPERATION_LIMIT(gData->cx, JSOW_JUMP)) { |
if (!JS_CHECK_OPERATION_LIMIT(gData->cx)) { |
3847 |
gData->ok = JS_FALSE; |
gData->ok = JS_FALSE; |
3848 |
return NULL; |
return NULL; |
3849 |
} |
} |
3916 |
const jschar *cp2; |
const jschar *cp2; |
3917 |
uintN j; |
uintN j; |
3918 |
#ifdef JS_TRACER |
#ifdef JS_TRACER |
3919 |
Fragment *fragment; |
NativeRegExp native; |
3920 |
|
|
3921 |
/* Run with native regexp if possible. */ |
/* Run with native regexp if possible. */ |
3922 |
if (TRACING_ENABLED(gData->cx) && |
if (TRACING_ENABLED(gData->cx) && |
3923 |
((fragment = JS_TRACE_MONITOR(gData->cx).reFragmento->getLoop(gData->regexp)) != NULL) |
(native = GetNativeRegExp(gData->cx, gData->regexp))) { |
|
&& fragment->code() && !fragment->isBlacklisted()) { |
|
|
union { NIns *code; REMatchState* (FASTCALL *func)(void*, void*); } u; |
|
|
u.code = fragment->code(); |
|
|
REMatchState *lr; |
|
3924 |
gData->skipped = (ptrdiff_t) x->cp; |
gData->skipped = (ptrdiff_t) x->cp; |
3925 |
|
|
3926 |
debug_only_v(printf("entering REGEXP trace at %s:%u@%u, code: %p\n", |
#ifdef JS_JIT_SPEW |
3927 |
gData->cx->fp->script->filename, |
debug_only_v({ |
3928 |
js_FramePCToLineNumber(gData->cx, gData->cx->fp), |
VOUCH_DOES_NOT_REQUIRE_STACK(); |
3929 |
FramePCOffset(gData->cx->fp), |
JSStackFrame *caller = (JS_ON_TRACE(gData->cx)) |
3930 |
fragment->code());); |
? NULL |
3931 |
|
: js_GetScriptedCaller(gData->cx, NULL); |
3932 |
|
printf("entering REGEXP trace at %s:%u@%u, code: %p\n", |
3933 |
|
caller ? caller->script->filename : "<unknown>", |
3934 |
|
caller ? js_FramePCToLineNumber(gData->cx, caller) : 0, |
3935 |
|
caller ? FramePCOffset(caller) : 0, |
3936 |
|
JS_FUNC_TO_DATA_PTR(void *, native)); |
3937 |
|
}) |
3938 |
|
#endif |
3939 |
|
|
3940 |
#if defined(JS_NO_FASTCALL) && defined(NANOJIT_IA32) |
#if defined(JS_NO_FASTCALL) && defined(NANOJIT_IA32) |
3941 |
SIMULATE_FASTCALL(lr, x, gData, u.func); |
SIMULATE_FASTCALL(result, x, gData, native); |
3942 |
#else |
#else |
3943 |
lr = u.func(x, gData); |
result = native(x, gData); |
3944 |
#endif |
#endif |
3945 |
|
|
3946 |
debug_only_v(printf("leaving REGEXP trace\n")); |
debug_only_v(printf("leaving REGEXP trace\n")); |
3947 |
|
|
3948 |
gData->skipped = ((const jschar *) gData->skipped) - cp; |
gData->skipped = ((const jschar *) gData->skipped) - cp; |
3949 |
return lr; |
return result; |
3950 |
} |
} |
3951 |
#endif |
#endif |
3952 |
/* |
/* |
4015 |
|
|
4016 |
for (i = 0; i < re->classCount; i++) { |
for (i = 0; i < re->classCount; i++) { |
4017 |
if (!re->classList[i].converted && |
if (!re->classList[i].converted && |
4018 |
!ProcessCharSet(gData, &re->classList[i])) { |
!MatcherProcessCharSet(gData, &re->classList[i])) { |
4019 |
return NULL; |
return NULL; |
4020 |
} |
} |
4021 |
} |
} |
4355 |
REGEXP_STATIC_RIGHT_CONTEXT = -6 |
REGEXP_STATIC_RIGHT_CONTEXT = -6 |
4356 |
}; |
}; |
4357 |
|
|
4358 |
JSBool |
void |
4359 |
js_InitRegExpStatics(JSContext *cx, JSRegExpStatics *res) |
js_InitRegExpStatics(JSContext *cx) |
4360 |
{ |
{ |
4361 |
|
/* |
4362 |
|
* To avoid multiple allocations in InitMatch(), the arena size parameter |
4363 |
|
* should be at least as big as: |
4364 |
|
* INITIAL_BACKTRACK |
4365 |
|
* + (sizeof(REProgState) * INITIAL_STATESTACK) |
4366 |
|
* + (offsetof(REMatchState, parens) + avgParanSize * sizeof(RECapture)) |
4367 |
|
*/ |
4368 |
|
JS_INIT_ARENA_POOL(&cx->regexpPool, "regexp", |
4369 |
|
12 * 1024 - 40, /* FIXME: bug 421435 */ |
4370 |
|
sizeof(void *), &cx->scriptStackQuota); |
4371 |
|
|
4372 |
JS_ClearRegExpStatics(cx); |
JS_ClearRegExpStatics(cx); |
4373 |
return js_AddRoot(cx, &res->input, "res->input"); |
} |
4374 |
|
|
4375 |
|
JS_FRIEND_API(void) |
4376 |
|
js_SaveRegExpStatics(JSContext *cx, JSRegExpStatics *statics, |
4377 |
|
JSTempValueRooter *tvr) |
4378 |
|
{ |
4379 |
|
*statics = cx->regExpStatics; |
4380 |
|
JS_PUSH_TEMP_ROOT_STRING(cx, statics->input, tvr); |
4381 |
|
} |
4382 |
|
|
4383 |
|
JS_FRIEND_API(void) |
4384 |
|
js_RestoreRegExpStatics(JSContext *cx, JSRegExpStatics *statics, |
4385 |
|
JSTempValueRooter *tvr) |
4386 |
|
{ |
4387 |
|
cx->regExpStatics = *statics; |
4388 |
|
JS_POP_TEMP_ROOT(cx, tvr); |
4389 |
|
} |
4390 |
|
|
4391 |
|
void |
4392 |
|
js_TraceRegExpStatics(JSTracer *trc, JSContext *acx) |
4393 |
|
{ |
4394 |
|
JSRegExpStatics *res = &acx->regExpStatics; |
4395 |
|
|
4396 |
|
if (res->input) |
4397 |
|
JS_CALL_STRING_TRACER(trc, res->input, "res->input"); |
4398 |
} |
} |
4399 |
|
|
4400 |
void |
void |
4401 |
js_FreeRegExpStatics(JSContext *cx, JSRegExpStatics *res) |
js_FreeRegExpStatics(JSContext *cx) |
4402 |
{ |
{ |
4403 |
|
JSRegExpStatics *res = &cx->regExpStatics; |
4404 |
|
|
4405 |
if (res->moreParens) { |
if (res->moreParens) { |
4406 |
JS_free(cx, res->moreParens); |
JS_free(cx, res->moreParens); |
4407 |
res->moreParens = NULL; |
res->moreParens = NULL; |
4408 |
} |
} |
4409 |
js_RemoveRoot(cx->runtime, &res->input); |
JS_FinishArenaPool(&cx->regexpPool); |
4410 |
} |
} |
4411 |
|
|
4412 |
static JSBool |
static JSBool |
4555 |
|
|
4556 |
#include "jsxdrapi.h" |
#include "jsxdrapi.h" |
4557 |
|
|
4558 |
static JSBool |
JSBool |
4559 |
regexp_xdrObject(JSXDRState *xdr, JSObject **objp) |
js_XDRRegExpObject(JSXDRState *xdr, JSObject **objp) |
4560 |
{ |
{ |
4561 |
JSRegExp *re; |
JSRegExp *re; |
4562 |
JSString *source; |
JSString *source; |
4595 |
|
|
4596 |
#else /* !JS_HAS_XDR */ |
#else /* !JS_HAS_XDR */ |
4597 |
|
|
4598 |
#define regexp_xdrObject NULL |
#define js_XDRRegExpObject NULL |
4599 |
|
|
4600 |
#endif /* !JS_HAS_XDR */ |
#endif /* !JS_HAS_XDR */ |
4601 |
|
|
4619 |
JS_ConvertStub, regexp_finalize, |
JS_ConvertStub, regexp_finalize, |
4620 |
NULL, NULL, |
NULL, NULL, |
4621 |
regexp_call, NULL, |
regexp_call, NULL, |
4622 |
regexp_xdrObject, NULL, |
js_XDRRegExpObject, NULL, |
4623 |
JS_CLASS_TRACE(regexp_trace), 0 |
JS_CLASS_TRACE(regexp_trace), 0 |
4624 |
}; |
}; |
4625 |
|
|
4915 |
return JS_TRUE; |
return JS_TRUE; |
4916 |
} |
} |
4917 |
|
|
|
#ifdef JS_TRACER |
|
|
static jsint FASTCALL |
|
|
Regexp_p_test(JSContext* cx, JSObject* regexp, JSString* str) |
|
|
{ |
|
|
jsval vp[3] = { JSVAL_NULL, OBJECT_TO_JSVAL(regexp), STRING_TO_JSVAL(str) }; |
|
|
if (!regexp_exec_sub(cx, regexp, 1, vp + 2, JS_TRUE, vp)) |
|
|
return JSVAL_TO_BOOLEAN(JSVAL_VOID); |
|
|
return *vp == JSVAL_TRUE; |
|
|
} |
|
|
|
|
|
JS_DEFINE_TRCINFO_1(regexp_test, |
|
|
(3, (static, BOOL_FAIL, Regexp_p_test, CONTEXT, THIS, STRING, 1, 1))) |
|
|
|
|
|
#endif |
|
|
|
|
4918 |
static JSFunctionSpec regexp_methods[] = { |
static JSFunctionSpec regexp_methods[] = { |
4919 |
#if JS_HAS_TOSOURCE |
#if JS_HAS_TOSOURCE |
4920 |
JS_FN(js_toSource_str, regexp_toString, 0,0), |
JS_FN(js_toSource_str, regexp_toString, 0,0), |
4922 |
JS_FN(js_toString_str, regexp_toString, 0,0), |
JS_FN(js_toString_str, regexp_toString, 0,0), |
4923 |
JS_FN("compile", regexp_compile, 2,0), |
JS_FN("compile", regexp_compile, 2,0), |
4924 |
JS_FN("exec", regexp_exec, 1,0), |
JS_FN("exec", regexp_exec, 1,0), |
4925 |
JS_TN("test", regexp_test, 1,0, regexp_test_trcinfo), |
JS_FN("test", regexp_test, 1,0), |
4926 |
JS_FS_END |
JS_FS_END |
4927 |
}; |
}; |
4928 |
|
|
4929 |
static JSBool |
static JSBool |
4930 |
RegExp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) |
RegExp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) |
4931 |
{ |
{ |
4932 |
if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { |
if (!JS_IsConstructing(cx)) { |
4933 |
/* |
/* |
4934 |
* If first arg is regexp and no flags are given, just return the arg. |
* If first arg is regexp and no flags are given, just return the arg. |
4935 |
* (regexp_compile_sub detects the regexp + flags case and throws a |
* (regexp_compile_sub detects the regexp + flags case and throws a |
4959 |
JSObject * |
JSObject * |
4960 |
js_InitRegExpClass(JSContext *cx, JSObject *obj) |
js_InitRegExpClass(JSContext *cx, JSObject *obj) |
4961 |
{ |
{ |
4962 |
JSObject *proto, *ctor; |
JSObject *proto = js_InitClass(cx, obj, NULL, &js_RegExpClass, RegExp, 1, |
4963 |
jsval rval; |
regexp_props, regexp_methods, |
4964 |
|
regexp_static_props, NULL); |
4965 |
proto = JS_InitClass(cx, obj, NULL, &js_RegExpClass, RegExp, 1, |
if (!proto) |
4966 |
regexp_props, regexp_methods, |
return NULL; |
|
regexp_static_props, NULL); |
|
4967 |
|
|
4968 |
if (!proto || !(ctor = JS_GetConstructor(cx, proto))) |
JSObject *ctor = JS_GetConstructor(cx, proto); |
4969 |
|
if (!ctor) |
4970 |
return NULL; |
return NULL; |
4971 |
|
|
4972 |
|
/* Give RegExp.prototype private data so it matches the empty string. */ |
4973 |
|
jsval rval; |
4974 |
if (!JS_AliasProperty(cx, ctor, "input", "$_") || |
if (!JS_AliasProperty(cx, ctor, "input", "$_") || |
4975 |
!JS_AliasProperty(cx, ctor, "multiline", "$*") || |
!JS_AliasProperty(cx, ctor, "multiline", "$*") || |
4976 |
!JS_AliasProperty(cx, ctor, "lastMatch", "$&") || |
!JS_AliasProperty(cx, ctor, "lastMatch", "$&") || |
4977 |
!JS_AliasProperty(cx, ctor, "lastParen", "$+") || |
!JS_AliasProperty(cx, ctor, "lastParen", "$+") || |
4978 |
!JS_AliasProperty(cx, ctor, "leftContext", "$`") || |
!JS_AliasProperty(cx, ctor, "leftContext", "$`") || |
4979 |
!JS_AliasProperty(cx, ctor, "rightContext", "$'")) { |
!JS_AliasProperty(cx, ctor, "rightContext", "$'") || |
4980 |
goto bad; |
!regexp_compile_sub(cx, proto, 0, NULL, &rval)) { |
4981 |
|
return NULL; |
4982 |
} |
} |
4983 |
|
|
|
/* Give RegExp.prototype private data so it matches the empty string. */ |
|
|
if (!regexp_compile_sub(cx, proto, 0, NULL, &rval)) |
|
|
goto bad; |
|
4984 |
return proto; |
return proto; |
|
|
|
|
bad: |
|
|
JS_DeleteProperty(cx, obj, js_RegExpClass.name); |
|
|
return NULL; |
|
4985 |
} |
} |
4986 |
|
|
4987 |
JSObject * |
JSObject * |
4991 |
JSString *str; |
JSString *str; |
4992 |
JSObject *obj; |
JSObject *obj; |
4993 |
JSRegExp *re; |
JSRegExp *re; |
|
JSTempValueRooter tvr; |
|
4994 |
|
|
4995 |
str = js_NewStringCopyN(cx, chars, length); |
str = js_NewStringCopyN(cx, chars, length); |
4996 |
if (!str) |
if (!str) |
4997 |
return NULL; |
return NULL; |
4998 |
|
JSAutoTempValueRooter tvr(cx, str); |
4999 |
re = js_NewRegExp(cx, ts, str, flags, JS_FALSE); |
re = js_NewRegExp(cx, ts, str, flags, JS_FALSE); |
5000 |
if (!re) |
if (!re) |
5001 |
return NULL; |
return NULL; |
|
JS_PUSH_TEMP_ROOT_STRING(cx, str, &tvr); |
|
5002 |
obj = js_NewObject(cx, &js_RegExpClass, NULL, NULL, 0); |
obj = js_NewObject(cx, &js_RegExpClass, NULL, NULL, 0); |
5003 |
if (!obj || !JS_SetPrivate(cx, obj, re)) { |
if (!obj || !JS_SetPrivate(cx, obj, re)) { |
5004 |
js_DestroyRegExp(cx, re); |
js_DestroyRegExp(cx, re); |
5006 |
} |
} |
5007 |
if (obj && !js_SetLastIndex(cx, obj, 0)) |
if (obj && !js_SetLastIndex(cx, obj, 0)) |
5008 |
obj = NULL; |
obj = NULL; |
|
JS_POP_TEMP_ROOT(cx, &tvr); |
|
5009 |
return obj; |
return obj; |
5010 |
} |
} |
5011 |
|
|