зеркало из https://github.com/mozilla/gecko-dev.git
Bug 673188: Compile regexps lazily. (r=mrbkap)
This commit is contained in:
Родитель
07d0d35b47
Коммит
838539811c
|
@ -48,56 +48,170 @@
|
|||
using namespace js;
|
||||
using namespace js::types;
|
||||
|
||||
/*
|
||||
* Return:
|
||||
* - The original if no escaping need be performed.
|
||||
* - A new string if escaping need be performed.
|
||||
* - NULL on error.
|
||||
*/
|
||||
static JSString *
|
||||
EscapeNakedForwardSlashes(JSContext *cx, JSString *unescaped)
|
||||
class RegExpMatchBuilder
|
||||
{
|
||||
JSContext * const cx;
|
||||
JSObject * const array;
|
||||
|
||||
bool setProperty(JSAtom *name, Value v) {
|
||||
return !!js_DefineProperty(cx, array, ATOM_TO_JSID(name), &v,
|
||||
JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE);
|
||||
}
|
||||
|
||||
public:
|
||||
RegExpMatchBuilder(JSContext *cx, JSObject *array) : cx(cx), array(array) {}
|
||||
|
||||
bool append(uint32 index, Value v) {
|
||||
JS_ASSERT(!array->getOps()->getElement);
|
||||
return !!js_DefineElement(cx, array, index, &v, JS_PropertyStub, JS_StrictPropertyStub,
|
||||
JSPROP_ENUMERATE);
|
||||
}
|
||||
|
||||
bool setIndex(int index) {
|
||||
return setProperty(cx->runtime->atomState.indexAtom, Int32Value(index));
|
||||
}
|
||||
|
||||
bool setInput(JSString *str) {
|
||||
JS_ASSERT(str);
|
||||
return setProperty(cx->runtime->atomState.inputAtom, StringValue(str));
|
||||
}
|
||||
};
|
||||
|
||||
static bool
|
||||
CreateRegExpMatchResult(JSContext *cx, JSString *input, const jschar *chars, size_t length,
|
||||
MatchPairs *matchPairs, Value *rval)
|
||||
{
|
||||
/*
|
||||
* Create the (slow) result array for a match.
|
||||
*
|
||||
* Array contents:
|
||||
* 0: matched string
|
||||
* 1..pairCount-1: paren matches
|
||||
* input: input string
|
||||
* index: start index for the match
|
||||
*/
|
||||
JSObject *array = NewSlowEmptyArray(cx);
|
||||
if (!array)
|
||||
return false;
|
||||
|
||||
if (!input) {
|
||||
input = js_NewStringCopyN(cx, chars, length);
|
||||
if (!input)
|
||||
return false;
|
||||
}
|
||||
|
||||
RegExpMatchBuilder builder(cx, array);
|
||||
|
||||
for (size_t i = 0; i < matchPairs->pairCount(); ++i) {
|
||||
MatchPair pair = matchPairs->pair(i);
|
||||
|
||||
JSString *captured;
|
||||
if (pair.isUndefined()) {
|
||||
JS_ASSERT(i != 0); /* Since we had a match, first pair must be present. */
|
||||
if (!builder.append(i, UndefinedValue()))
|
||||
return false;
|
||||
} else {
|
||||
captured = js_NewDependentString(cx, input, pair.start, pair.length());
|
||||
if (!captured || !builder.append(i, StringValue(captured)))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!builder.setIndex(matchPairs->pair(0).start) || !builder.setInput(input))
|
||||
return false;
|
||||
|
||||
*rval = ObjectValue(*array);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool
|
||||
ExecuteRegExpImpl(JSContext *cx, RegExpStatics *res, T *re, JSLinearString *input,
|
||||
const jschar *chars, size_t length,
|
||||
size_t *lastIndex, RegExpExecType type, Value *rval)
|
||||
{
|
||||
LifoAllocScope allocScope(&cx->tempLifoAlloc());
|
||||
MatchPairs *matchPairs = NULL;
|
||||
RegExpRunStatus status = re->execute(cx, chars, length, lastIndex, allocScope, &matchPairs);
|
||||
|
||||
switch (status) {
|
||||
case RegExpRunStatus_Error:
|
||||
return false;
|
||||
case RegExpRunStatus_Success_NotFound:
|
||||
*rval = NullValue();
|
||||
return true;
|
||||
default:
|
||||
JS_ASSERT(status == RegExpRunStatus_Success);
|
||||
JS_ASSERT(matchPairs);
|
||||
}
|
||||
|
||||
if (res)
|
||||
res->updateFromMatchPairs(cx, input, matchPairs);
|
||||
|
||||
*lastIndex = matchPairs->pair(0).limit;
|
||||
|
||||
if (type == RegExpTest) {
|
||||
*rval = BooleanValue(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
return CreateRegExpMatchResult(cx, input, chars, length, matchPairs, rval);
|
||||
}
|
||||
|
||||
bool
|
||||
js::ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpPrivate *rep, JSLinearString *input,
|
||||
const jschar *chars, size_t length,
|
||||
size_t *lastIndex, RegExpExecType type, Value *rval)
|
||||
{
|
||||
return ExecuteRegExpImpl(cx, res, rep, input, chars, length, lastIndex, type, rval);
|
||||
}
|
||||
|
||||
bool
|
||||
js::ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpObject *reobj, JSLinearString *input,
|
||||
const jschar *chars, size_t length,
|
||||
size_t *lastIndex, RegExpExecType type, Value *rval)
|
||||
{
|
||||
return ExecuteRegExpImpl(cx, res, reobj, input, chars, length, lastIndex, type, rval);
|
||||
}
|
||||
|
||||
/* Note: returns the original if no escaping need be performed. */
|
||||
static JSLinearString *
|
||||
EscapeNakedForwardSlashes(JSContext *cx, JSLinearString *unescaped)
|
||||
{
|
||||
size_t oldLen = unescaped->length();
|
||||
const jschar *oldChars = unescaped->getChars(cx);
|
||||
if (!oldChars)
|
||||
return NULL;
|
||||
const jschar *oldChars = unescaped->chars();
|
||||
|
||||
JS::Anchor<JSString *> anchor(unescaped);
|
||||
|
||||
js::Vector<jschar, 128> newChars(cx);
|
||||
/* We may never need to use |sb|. Start using it lazily. */
|
||||
StringBuffer sb(cx);
|
||||
|
||||
for (const jschar *it = oldChars; it < oldChars + oldLen; ++it) {
|
||||
if (*it == '/' && (it == oldChars || it[-1] != '\\')) {
|
||||
if (!newChars.length()) {
|
||||
if (!newChars.reserve(oldLen + 1))
|
||||
/* There's a forward slash that needs escaping. */
|
||||
if (sb.empty()) {
|
||||
/* This is the first one we've seen, copy everything up to this point. */
|
||||
if (!sb.reserve(oldLen + 1))
|
||||
return NULL;
|
||||
newChars.infallibleAppend(oldChars, size_t(it - oldChars));
|
||||
sb.infallibleAppend(oldChars, size_t(it - oldChars));
|
||||
}
|
||||
if (!newChars.append('\\'))
|
||||
if (!sb.append('\\'))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!newChars.empty() && !newChars.append(*it))
|
||||
if (!sb.empty() && !sb.append(*it))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (newChars.empty())
|
||||
return unescaped;
|
||||
|
||||
size_t len = newChars.length();
|
||||
if (!newChars.append('\0'))
|
||||
return NULL;
|
||||
jschar *chars = newChars.extractRawBuffer();
|
||||
JSString *escaped = js_NewString(cx, chars, len);
|
||||
if (!escaped)
|
||||
cx->free_(chars);
|
||||
return escaped;
|
||||
return sb.empty() ? unescaped : sb.finishString();
|
||||
}
|
||||
|
||||
static bool
|
||||
ResetRegExpObjectWithStatics(JSContext *cx, RegExpObject *reobj,
|
||||
JSString *str, RegExpFlag flags = RegExpFlag(0))
|
||||
JSLinearString *str, RegExpFlag flags = RegExpFlag(0))
|
||||
{
|
||||
flags = RegExpFlag(flags | cx->regExpStatics()->getFlags());
|
||||
return ResetRegExpObject(cx, reobj, str, flags);
|
||||
return reobj->reset(cx, str, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -125,7 +239,7 @@ CompileRegExpObject(JSContext *cx, RegExpObject *obj, uintN argc, Value *argv, V
|
|||
if (ValueIsRegExp(sourceValue)) {
|
||||
/*
|
||||
* If we get passed in a |RegExpObject| source we return a new
|
||||
* object with the same |RegExpPrivate|.
|
||||
* object with the same source/flags.
|
||||
*
|
||||
* Note: the regexp static flags are not taken into consideration here.
|
||||
*/
|
||||
|
@ -135,23 +249,21 @@ CompileRegExpObject(JSContext *cx, RegExpObject *obj, uintN argc, Value *argv, V
|
|||
return false;
|
||||
}
|
||||
|
||||
RegExpPrivate *rep = sourceObj.asRegExp()->getPrivate();
|
||||
if (!rep)
|
||||
return false;
|
||||
|
||||
rep->incref(cx);
|
||||
if (!ResetRegExpObject(cx, obj, AlreadyIncRefed<RegExpPrivate>(rep)))
|
||||
if (!obj->reset(cx, sourceObj.asRegExp()))
|
||||
return false;
|
||||
*rval = ObjectValue(*obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
JSString *sourceStr;
|
||||
JSLinearString *sourceStr;
|
||||
if (sourceValue.isUndefined()) {
|
||||
sourceStr = cx->runtime->emptyString;
|
||||
} else {
|
||||
/* Coerce to string and compile. */
|
||||
sourceStr = js_ValueToString(cx, sourceValue);
|
||||
JSString *str = js_ValueToString(cx, sourceValue);
|
||||
if (!str)
|
||||
return false;
|
||||
sourceStr = str->ensureLinear(cx);
|
||||
if (!sourceStr)
|
||||
return false;
|
||||
}
|
||||
|
@ -166,10 +278,13 @@ CompileRegExpObject(JSContext *cx, RegExpObject *obj, uintN argc, Value *argv, V
|
|||
return false;
|
||||
}
|
||||
|
||||
JSString *escapedSourceStr = EscapeNakedForwardSlashes(cx, sourceStr);
|
||||
JSLinearString *escapedSourceStr = EscapeNakedForwardSlashes(cx, sourceStr);
|
||||
if (!escapedSourceStr)
|
||||
return false;
|
||||
|
||||
if (!RegExpPrivateCode::checkSyntax(cx, NULL, escapedSourceStr))
|
||||
return false;
|
||||
|
||||
if (!ResetRegExpObjectWithStatics(cx, obj, escapedSourceStr, flags))
|
||||
return false;
|
||||
*rval = ObjectValue(*obj);
|
||||
|
@ -187,9 +302,7 @@ regexp_compile(JSContext *cx, uintN argc, Value *vp)
|
|||
return ok;
|
||||
|
||||
RegExpObject *reobj = obj->asRegExp();
|
||||
ok = CompileRegExpObject(cx, reobj, args.length(), args.array(), &args.rval());
|
||||
JS_ASSERT_IF(ok, reobj->getPrivate());
|
||||
return ok;
|
||||
return CompileRegExpObject(cx, reobj, args.length(), args.array(), &args.rval());
|
||||
}
|
||||
|
||||
static JSBool
|
||||
|
@ -213,16 +326,10 @@ regexp_construct(JSContext *cx, uintN argc, Value *vp)
|
|||
if (!obj)
|
||||
return false;
|
||||
|
||||
PreInitRegExpObject pireo(obj);
|
||||
RegExpObject *reobj = pireo.get();
|
||||
|
||||
if (!CompileRegExpObject(cx, reobj, argc, argv, &JS_RVAL(cx, vp))) {
|
||||
pireo.fail();
|
||||
if (!CompileRegExpObject(cx, obj->asRegExp(), argc, argv, &JS_RVAL(cx, vp)))
|
||||
return false;
|
||||
}
|
||||
|
||||
pireo.succeed();
|
||||
*vp = ObjectValue(*reobj);
|
||||
*vp = ObjectValue(*obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -356,30 +463,9 @@ js_InitRegExpClass(JSContext *cx, JSObject *obj)
|
|||
if (!proto)
|
||||
return NULL;
|
||||
|
||||
{
|
||||
AlreadyIncRefed<RegExpPrivate> rep =
|
||||
RegExpPrivate::create(cx, cx->runtime->emptyString, RegExpFlag(0), NULL);
|
||||
if (!rep)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Associate the empty regular expression with |RegExp.prototype|, and define
|
||||
* the initial non-method properties of any regular expression instance.
|
||||
* These must be added before methods to preserve slot layout.
|
||||
*/
|
||||
#ifdef DEBUG
|
||||
assertSameCompartment(cx, proto, rep->compartment);
|
||||
#endif
|
||||
|
||||
PreInitRegExpObject pireo(proto);
|
||||
RegExpObject *reproto = pireo.get();
|
||||
if (!ResetRegExpObject(cx, reproto, rep)) {
|
||||
pireo.fail();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pireo.succeed();
|
||||
}
|
||||
RegExpObject *reproto = proto->asRegExp();
|
||||
if (!reproto->reset(cx, cx->runtime->emptyString, RegExpFlag(0)))
|
||||
return NULL;
|
||||
|
||||
if (!DefinePropertiesAndBrand(cx, proto, NULL, regexp_methods))
|
||||
return NULL;
|
||||
|
@ -419,7 +505,7 @@ js_InitRegExpClass(JSContext *cx, JSObject *obj)
|
|||
* RegExp.prototype.test doesn't need to create a results array, and we use
|
||||
* |execType| to perform this optimization.
|
||||
*/
|
||||
static JSBool
|
||||
static bool
|
||||
ExecuteRegExp(JSContext *cx, Native native, uintN argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
@ -431,23 +517,25 @@ ExecuteRegExp(JSContext *cx, Native native, uintN argc, Value *vp)
|
|||
return ok;
|
||||
|
||||
RegExpObject *reobj = obj->asRegExp();
|
||||
RegExpPrivate *re = reobj->getPrivate();
|
||||
if (!re)
|
||||
RegExpPrivate *rep = reobj->getOrCreatePrivate(cx);
|
||||
if (!rep)
|
||||
return true;
|
||||
|
||||
/*
|
||||
* Code execution under this call could swap out the guts of |reobj|, so we
|
||||
* have to take a defensive refcount here.
|
||||
*/
|
||||
AutoRefCount<RegExpPrivate> arc(cx, NeedsIncRef<RegExpPrivate>(re));
|
||||
AutoRefCount<RegExpPrivate> arc(cx, NeedsIncRef<RegExpPrivate>(rep));
|
||||
RegExpStatics *res = cx->regExpStatics();
|
||||
|
||||
/* Step 2. */
|
||||
JSString *input = js_ValueToString(cx, args.length() > 0 ? args[0] : UndefinedValue());
|
||||
JSString *input = js_ValueToString(cx, args.length() > 0 ? args[0] : UndefinedValue());
|
||||
if (!input)
|
||||
return false;
|
||||
|
||||
|
||||
/* Step 3. */
|
||||
JSLinearString *linearInput = input->ensureLinear(cx);
|
||||
const jschar *chars = linearInput->chars();
|
||||
size_t length = input->length();
|
||||
|
||||
/* Step 4. */
|
||||
|
@ -459,7 +547,7 @@ ExecuteRegExp(JSContext *cx, Native native, uintN argc, Value *vp)
|
|||
return false;
|
||||
|
||||
/* Steps 6-7 (with sticky extension). */
|
||||
if (!re->global() && !re->sticky())
|
||||
if (!rep->global() && !rep->sticky())
|
||||
i = 0;
|
||||
|
||||
/* Step 9a. */
|
||||
|
@ -470,12 +558,15 @@ ExecuteRegExp(JSContext *cx, Native native, uintN argc, Value *vp)
|
|||
}
|
||||
|
||||
/* Steps 8-21. */
|
||||
RegExpExecType execType = native == regexp_test ? RegExpTest : RegExpExec;
|
||||
size_t lastIndexInt(i);
|
||||
if (!re->execute(cx, res, input, &lastIndexInt, native == regexp_test, &args.rval()))
|
||||
if (!ExecuteRegExp(cx, res, rep, linearInput, chars, length, &lastIndexInt, execType,
|
||||
&args.rval())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Step 11 (with sticky extension). */
|
||||
if (re->global() || (!args.rval().isNull() && re->sticky())) {
|
||||
if (rep->global() || (!args.rval().isNull() && rep->sticky())) {
|
||||
if (args.rval().isNull())
|
||||
reobj->zeroLastIndex();
|
||||
else
|
||||
|
|
|
@ -53,6 +53,21 @@ js_InitRegExpClass(JSContext *cx, JSObject *obj);
|
|||
|
||||
namespace js {
|
||||
|
||||
/*
|
||||
* |res| may be null if the |RegExpStatics| are not to be updated.
|
||||
* |input| may be null if there is no |JSString| corresponding to
|
||||
* |chars| and |length|.
|
||||
*/
|
||||
bool
|
||||
ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpObject *reobj, JSLinearString *input,
|
||||
const jschar *chars, size_t length,
|
||||
size_t *lastIndex, RegExpExecType type, Value *rval);
|
||||
|
||||
bool
|
||||
ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpPrivate *rep, JSLinearString *input,
|
||||
const jschar *chars, size_t length,
|
||||
size_t *lastIndex, RegExpExecType type, Value *rval);
|
||||
|
||||
extern JSBool
|
||||
regexp_exec(JSContext *cx, uintN argc, Value *vp);
|
||||
|
||||
|
|
|
@ -324,6 +324,10 @@ class LifoAllocScope {
|
|||
lifoAlloc->release(mark);
|
||||
}
|
||||
|
||||
LifoAlloc &alloc() {
|
||||
return *lifoAlloc;
|
||||
}
|
||||
|
||||
void releaseEarly() {
|
||||
JS_ASSERT(shouldRelease);
|
||||
lifoAlloc->release(mark);
|
||||
|
|
|
@ -5961,16 +5961,9 @@ JS_ExecuteRegExp(JSContext *cx, JSObject *obj, JSObject *reobj, jschar *chars, s
|
|||
{
|
||||
CHECK_REQUEST(cx);
|
||||
|
||||
RegExpPrivate *rep = reobj->asRegExp()->getPrivate();
|
||||
if (!rep)
|
||||
return false;
|
||||
|
||||
JSString *str = js_NewStringCopyN(cx, chars, length);
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
RegExpStatics *res = obj->asGlobal()->getRegExpStatics();
|
||||
return rep->execute(cx, res, str, indexp, test, rval);
|
||||
return ExecuteRegExp(cx, res, reobj->asRegExp(), NULL, chars, length,
|
||||
indexp, test ? RegExpTest : RegExpExec, rval);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject *)
|
||||
|
@ -5998,15 +5991,8 @@ JS_ExecuteRegExpNoStatics(JSContext *cx, JSObject *obj, jschar *chars, size_t le
|
|||
{
|
||||
CHECK_REQUEST(cx);
|
||||
|
||||
RegExpPrivate *rep = obj->asRegExp()->getPrivate();
|
||||
if (!rep)
|
||||
return false;
|
||||
|
||||
JSString *str = js_NewStringCopyN(cx, chars, length);
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
return rep->executeNoStatics(cx, str, indexp, test, rval);
|
||||
return ExecuteRegExp(cx, NULL, obj->asRegExp(), NULL, chars, length, indexp,
|
||||
test ? RegExpTest : RegExpExec, rval);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
|
|
|
@ -128,8 +128,10 @@ namespace js {
|
|||
struct ArgumentsData;
|
||||
struct Class;
|
||||
|
||||
class RegExpObject;
|
||||
class RegExpPrivate;
|
||||
class RegExpStatics;
|
||||
class MatchPairs;
|
||||
|
||||
enum RegExpFlag
|
||||
{
|
||||
|
@ -139,6 +141,12 @@ enum RegExpFlag
|
|||
StickyFlag = JS_BIT(3)
|
||||
};
|
||||
|
||||
enum RegExpExecType
|
||||
{
|
||||
RegExpExec,
|
||||
RegExpTest
|
||||
};
|
||||
|
||||
class AutoStringRooter;
|
||||
class ExecuteArgsGuard;
|
||||
class InvokeFrameGuard;
|
||||
|
|
124
js/src/jsstr.cpp
124
js/src/jsstr.cpp
|
@ -72,6 +72,7 @@
|
|||
#include "jsstr.h"
|
||||
#include "jsversion.h"
|
||||
|
||||
#include "builtin/RegExp.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/RegExpObject.h"
|
||||
|
||||
|
@ -517,7 +518,7 @@ js_str_toString(JSContext *cx, uintN argc, Value *vp)
|
|||
/*
|
||||
* Java-like string native methods.
|
||||
*/
|
||||
|
||||
|
||||
JS_ALWAYS_INLINE bool
|
||||
ValueToIntegerRange(JSContext *cx, const Value &v, int32 *out)
|
||||
{
|
||||
|
@ -531,7 +532,7 @@ ValueToIntegerRange(JSContext *cx, const Value &v, int32 *out)
|
|||
*out = INT32_MAX;
|
||||
else if (d < INT32_MIN)
|
||||
*out = INT32_MIN;
|
||||
else
|
||||
else
|
||||
*out = int32(d);
|
||||
}
|
||||
|
||||
|
@ -1253,7 +1254,7 @@ class FlatMatch
|
|||
|
||||
public:
|
||||
FlatMatch() : patstr(NULL) {} /* Old GCC wants this initialization. */
|
||||
JSString *pattern() const { return patstr; }
|
||||
JSLinearString *pattern() const { return patstr; }
|
||||
size_t patternLength() const { return patlen; }
|
||||
|
||||
/*
|
||||
|
@ -1263,33 +1264,39 @@ class FlatMatch
|
|||
int32 match() const { return match_; }
|
||||
};
|
||||
|
||||
/* A regexp and optional associated object. */
|
||||
class RegExpPair
|
||||
{
|
||||
AutoRefCount<RegExpPrivate> rep_;
|
||||
JSObject *reobj_;
|
||||
RegExpObject *reobj_;
|
||||
|
||||
explicit RegExpPair(RegExpPair &);
|
||||
void operator=(const RegExpPair &);
|
||||
|
||||
public:
|
||||
explicit RegExpPair(JSContext *cx) : rep_(cx) {}
|
||||
|
||||
void reset(JSObject &obj) {
|
||||
reobj_ = &obj;
|
||||
RegExpPrivate *rep = reobj_->asRegExp()->getPrivate();
|
||||
JS_ASSERT(rep);
|
||||
bool resetWithObject(JSContext *cx, RegExpObject *reobj) {
|
||||
reobj_ = reobj;
|
||||
RegExpPrivate *rep = reobj_->asRegExp()->getOrCreatePrivate(cx);
|
||||
if (!rep)
|
||||
return false;
|
||||
rep_.reset(NeedsIncRef<RegExpPrivate>(rep));
|
||||
return true;
|
||||
}
|
||||
|
||||
void reset(AlreadyIncRefed<RegExpPrivate> rep) {
|
||||
void resetWithPrivate(AlreadyIncRefed<RegExpPrivate> rep) {
|
||||
reobj_ = NULL;
|
||||
rep_.reset(rep);
|
||||
}
|
||||
|
||||
/* Note: May be null. */
|
||||
JSObject *reobj() const { return reobj_; }
|
||||
bool hasRegExp() const { return !rep_.null(); }
|
||||
RegExpPrivate &re() const { JS_ASSERT(hasRegExp()); return *rep_; }
|
||||
bool null() const { return rep_.null(); }
|
||||
|
||||
RegExpObject *reobj() const { return reobj_; }
|
||||
|
||||
RegExpPrivate *getPrivate() const {
|
||||
JS_ASSERT(!null());
|
||||
return rep_.get();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -1314,7 +1321,7 @@ class RegExpGuard
|
|||
*/
|
||||
static const size_t MAX_FLAT_PAT_LEN = 256;
|
||||
|
||||
static JSString *flattenPattern(JSContext *cx, JSLinearString *patstr) {
|
||||
static JSLinearString *flattenPattern(JSContext *cx, JSLinearString *patstr) {
|
||||
StringBuffer sb(cx);
|
||||
if (!sb.reserve(patstr->length()))
|
||||
return NULL;
|
||||
|
@ -1343,13 +1350,13 @@ class RegExpGuard
|
|||
init(uintN argc, Value *vp, bool convertVoid = false)
|
||||
{
|
||||
if (argc != 0 && ValueIsRegExp(vp[2])) {
|
||||
rep.reset(vp[2].toObject());
|
||||
rep.resetWithObject(cx, vp[2].toObject().asRegExp());
|
||||
} else {
|
||||
if (convertVoid && (argc == 0 || vp[2].isUndefined())) {
|
||||
fm.patstr = cx->runtime->emptyString;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
fm.patstr = ArgToRootedString(cx, argc, vp, 0);
|
||||
if (!fm.patstr)
|
||||
return false;
|
||||
|
@ -1370,7 +1377,7 @@ class RegExpGuard
|
|||
tryFlatMatch(JSContext *cx, JSString *textstr, uintN optarg, uintN argc,
|
||||
bool checkMetaChars = true)
|
||||
{
|
||||
if (rep.hasRegExp())
|
||||
if (!rep.null())
|
||||
return NULL;
|
||||
|
||||
fm.pat = fm.patstr->chars();
|
||||
|
@ -1403,7 +1410,7 @@ class RegExpGuard
|
|||
const RegExpPair *
|
||||
normalizeRegExp(bool flat, uintN optarg, uintN argc, Value *vp)
|
||||
{
|
||||
if (rep.hasRegExp())
|
||||
if (!rep.null())
|
||||
return &rep;
|
||||
|
||||
/* Build RegExp from pattern string. */
|
||||
|
@ -1416,7 +1423,7 @@ class RegExpGuard
|
|||
opt = NULL;
|
||||
}
|
||||
|
||||
JSString *patstr;
|
||||
JSLinearString *patstr;
|
||||
if (flat) {
|
||||
patstr = flattenPattern(cx, fm.patstr);
|
||||
if (!patstr)
|
||||
|
@ -1426,23 +1433,23 @@ class RegExpGuard
|
|||
}
|
||||
JS_ASSERT(patstr);
|
||||
|
||||
AlreadyIncRefed<RegExpPrivate> re = RegExpPrivate::createFlagged(cx, patstr, opt, NULL);
|
||||
AlreadyIncRefed<RegExpPrivate> re = RegExpPrivate::create(cx, patstr, opt, NULL);
|
||||
if (!re)
|
||||
return NULL;
|
||||
rep.reset(re);
|
||||
rep.resetWithPrivate(re);
|
||||
return &rep;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
bool hasRegExpPair() const { return rep.hasRegExp(); }
|
||||
bool hasRegExpPair() const { return !rep.null(); }
|
||||
#endif
|
||||
};
|
||||
|
||||
/* js_ExecuteRegExp indicates success in two ways, based on the 'test' flag. */
|
||||
/* ExecuteRegExp indicates success in two ways, based on the 'test' flag. */
|
||||
static JS_ALWAYS_INLINE bool
|
||||
Matched(bool test, const Value &v)
|
||||
Matched(RegExpExecType type, const Value &v)
|
||||
{
|
||||
return test ? v.isTrue() : !v.isNull();
|
||||
return type == RegExpTest ? v.isTrue() : !v.isNull();
|
||||
}
|
||||
|
||||
typedef bool (*DoMatchCallback)(JSContext *cx, RegExpStatics *res, size_t count, void *data);
|
||||
|
@ -1463,19 +1470,26 @@ enum MatchControlFlags {
|
|||
|
||||
/* Factor out looping and matching logic. */
|
||||
static bool
|
||||
DoMatch(JSContext *cx, RegExpStatics *res, JSString *str, const RegExpPair &rep,
|
||||
DoMatch(JSContext *cx, RegExpStatics *res, JSString *str, const RegExpPair ®ExpPair,
|
||||
DoMatchCallback callback, void *data, MatchControlFlags flags, Value *rval)
|
||||
{
|
||||
RegExpPrivate &re = rep.re();
|
||||
if (re.global()) {
|
||||
RegExpPrivate *rep = regExpPair.getPrivate();
|
||||
JSLinearString *linearStr = str->ensureLinear(cx);
|
||||
if (!linearStr)
|
||||
return false;
|
||||
const jschar *chars = linearStr->chars();
|
||||
size_t length = linearStr->length();
|
||||
|
||||
if (rep->global()) {
|
||||
/* global matching ('g') */
|
||||
bool testGlobal = flags & TEST_GLOBAL_BIT;
|
||||
if (rep.reobj())
|
||||
rep.reobj()->asRegExp()->zeroLastIndex();
|
||||
RegExpExecType type = flags & TEST_GLOBAL_BIT ? RegExpTest : RegExpExec;
|
||||
if (RegExpObject *reobj = regExpPair.reobj())
|
||||
reobj->zeroLastIndex();
|
||||
|
||||
for (size_t count = 0, i = 0, length = str->length(); i <= length; ++count) {
|
||||
if (!re.execute(cx, res, str, &i, testGlobal, rval))
|
||||
if (!ExecuteRegExp(cx, res, rep, linearStr, chars, length, &i, type, rval))
|
||||
return false;
|
||||
if (!Matched(testGlobal, *rval))
|
||||
if (!Matched(type, *rval))
|
||||
break;
|
||||
if (!callback(cx, res, count, data))
|
||||
return false;
|
||||
|
@ -1484,12 +1498,12 @@ DoMatch(JSContext *cx, RegExpStatics *res, JSString *str, const RegExpPair &rep,
|
|||
}
|
||||
} else {
|
||||
/* single match */
|
||||
bool testSingle = !!(flags & TEST_SINGLE_BIT),
|
||||
callbackOnSingle = !!(flags & CALLBACK_ON_SINGLE_BIT);
|
||||
RegExpExecType type = (flags & TEST_SINGLE_BIT) ? RegExpTest : RegExpExec;
|
||||
bool callbackOnSingle = !!(flags & CALLBACK_ON_SINGLE_BIT);
|
||||
size_t i = 0;
|
||||
if (!re.execute(cx, res, str, &i, testSingle, rval))
|
||||
if (!ExecuteRegExp(cx, res, rep, linearStr, chars, length, &i, type, rval))
|
||||
return false;
|
||||
if (callbackOnSingle && Matched(testSingle, *rval) && !callback(cx, res, 0, data))
|
||||
if (callbackOnSingle && Matched(type, *rval) && !callback(cx, res, 0, data))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -1544,7 +1558,7 @@ js::str_match(JSContext *cx, uintN argc, Value *vp)
|
|||
JSString *str = ThisToStringForStringProto(cx, vp);
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
|
||||
RegExpGuard g(cx);
|
||||
if (!g.init(argc, vp, true))
|
||||
return false;
|
||||
|
@ -1564,7 +1578,7 @@ js::str_match(JSContext *cx, uintN argc, Value *vp)
|
|||
if (!DoMatch(cx, res, str, *rep, MatchCallback, arg, MATCH_ARGS, &rval))
|
||||
return false;
|
||||
|
||||
if (rep->re().global())
|
||||
if (rep->getPrivate()->global())
|
||||
vp->setObjectOrNull(array.object());
|
||||
else
|
||||
*vp = rval;
|
||||
|
@ -1587,14 +1601,20 @@ js::str_search(JSContext *cx, uintN argc, Value *vp)
|
|||
}
|
||||
if (cx->isExceptionPending()) /* from tryFlatMatch */
|
||||
return false;
|
||||
|
||||
|
||||
const RegExpPair *rep = g.normalizeRegExp(false, 1, argc, vp);
|
||||
if (!rep)
|
||||
return false;
|
||||
|
||||
JSLinearString *linearStr = str->ensureLinear(cx);
|
||||
if (!linearStr)
|
||||
return false;
|
||||
const jschar *chars = linearStr->chars();
|
||||
size_t length = linearStr->length();
|
||||
RegExpStatics *res = cx->regExpStatics();
|
||||
/* Per ECMAv5 15.5.4.12 (5) The last index property is ignored and left unchanged. */
|
||||
size_t i = 0;
|
||||
if (!rep->re().execute(cx, res, str, &i, true, vp))
|
||||
if (!ExecuteRegExp(cx, res, rep->getPrivate(), linearStr, chars, length, &i, RegExpTest, vp))
|
||||
return false;
|
||||
|
||||
if (vp->isTrue())
|
||||
|
@ -1657,7 +1677,7 @@ InterpretDollar(JSContext *cx, RegExpStatics *res, const jschar *dp, const jscha
|
|||
|
||||
JS_ASSERT(num <= res->parenCount());
|
||||
|
||||
/*
|
||||
/*
|
||||
* Note: we index to get the paren with the (1-indexed) pair
|
||||
* number, as opposed to a (0-indexed) paren number.
|
||||
*/
|
||||
|
@ -1815,7 +1835,7 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t
|
|||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* Precondition: |rdata.sb| already has necessary growth space reserved (as
|
||||
* derived from FindReplaceLength).
|
||||
*/
|
||||
|
@ -2212,7 +2232,7 @@ js::str_replace(JSContext *cx, uintN argc, Value *vp)
|
|||
if (rdata.lambda)
|
||||
return str_replace_flat_lambda(cx, argc, vp, rdata, *fm);
|
||||
|
||||
/*
|
||||
/*
|
||||
* Note: we could optimize the text.length == pattern.length case if we wanted,
|
||||
* even in the presence of dollar metachars.
|
||||
*/
|
||||
|
@ -2383,11 +2403,11 @@ SplitHelper(JSContext *cx, JSLinearString *str, uint32 limit, Matcher splitMatch
|
|||
*/
|
||||
class SplitRegExpMatcher {
|
||||
RegExpStatics *res;
|
||||
RegExpPrivate *re;
|
||||
RegExpPrivate *rep;
|
||||
|
||||
public:
|
||||
static const bool returnsCaptures = true;
|
||||
SplitRegExpMatcher(RegExpPrivate *re, RegExpStatics *res) : res(res), re(re) {}
|
||||
SplitRegExpMatcher(RegExpPrivate *rep, RegExpStatics *res) : res(res), rep(rep) {}
|
||||
|
||||
inline bool operator()(JSContext *cx, JSLinearString *str, size_t index,
|
||||
SplitMatchResult *result) {
|
||||
|
@ -2396,7 +2416,9 @@ class SplitRegExpMatcher {
|
|||
= UndefinedValue()
|
||||
#endif
|
||||
;
|
||||
if (!re->execute(cx, res, str, &index, true, &rval))
|
||||
const jschar *chars = str->chars();
|
||||
size_t length = str->length();
|
||||
if (!ExecuteRegExp(cx, res, rep, str, chars, length, &index, RegExpTest, &rval))
|
||||
return false;
|
||||
if (!rval.isTrue()) {
|
||||
result->setFailure();
|
||||
|
@ -2465,7 +2487,9 @@ js::str_split(JSContext *cx, uintN argc, Value *vp)
|
|||
bool sepUndefined = (argc == 0 || vp[2].isUndefined());
|
||||
if (!sepUndefined) {
|
||||
if (ValueIsRegExp(vp[2])) {
|
||||
re = vp[2].toObject().asRegExp()->getPrivate();
|
||||
re = vp[2].toObject().asRegExp()->getOrCreatePrivate(cx);
|
||||
if (!re)
|
||||
return false;
|
||||
} else {
|
||||
JSString *sep = js_ValueToString(cx, vp[2]);
|
||||
if (!sep)
|
||||
|
@ -2546,7 +2570,7 @@ str_substr(JSContext *cx, uintN argc, Value *vp)
|
|||
if (argc == 1 || vp[3].isUndefined()) {
|
||||
len = length - begin;
|
||||
} else {
|
||||
if (!ValueToIntegerRange(cx, vp[3], &len))
|
||||
if (!ValueToIntegerRange(cx, vp[3], &len))
|
||||
return false;
|
||||
|
||||
if (len <= 0) {
|
||||
|
|
|
@ -6593,9 +6593,9 @@ mjit::Compiler::jsop_regexp()
|
|||
return;
|
||||
}
|
||||
|
||||
RegExpPrivate *regexp = static_cast<RegExpObject *>(obj)->getPrivate();
|
||||
RegExpObject *reobj = obj->asRegExp();
|
||||
|
||||
DebugOnly<uint32> origFlags = regexp->getFlags();
|
||||
DebugOnly<uint32> origFlags = reobj->getFlags();
|
||||
DebugOnly<uint32> staticsFlags = res->getFlags();
|
||||
JS_ASSERT((origFlags & staticsFlags) == staticsFlags);
|
||||
|
||||
|
@ -6647,10 +6647,6 @@ mjit::Compiler::jsop_regexp()
|
|||
stubcc.masm.move(ImmPtr(obj), Registers::ArgReg1);
|
||||
OOL_STUBCALL(stubs::RegExp, REJOIN_FALLTHROUGH);
|
||||
|
||||
/* Bump the refcount on the wrapped RegExp. */
|
||||
size_t *refcount = regexp->addressOfRefCount();
|
||||
masm.add32(Imm32(1), AbsoluteAddress(refcount));
|
||||
|
||||
frame.pushTypedPayload(JSVAL_TYPE_OBJECT, result);
|
||||
|
||||
stubcc.rejoin(Changes(1));
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sw=4 et tw=99 ft=cpp:
|
||||
*
|
||||
* ***** 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 SpiderMonkey JavaScript code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Chris Leary <cdleary@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either 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 ***** */
|
||||
|
||||
#ifndef MatchPairs_h__
|
||||
#define MatchPairs_h__
|
||||
|
||||
/*
|
||||
* RegExp match results are succinctly represented by pairs of integer
|
||||
* indices delimiting (start, limit] segments of the input string.
|
||||
*
|
||||
* The pair count for a given RegExp match is the capturing parentheses
|
||||
* count plus one for the "0 capturing paren" whole text match.
|
||||
*/
|
||||
|
||||
namespace js {
|
||||
|
||||
struct MatchPair
|
||||
{
|
||||
int start;
|
||||
int limit;
|
||||
|
||||
MatchPair(int start, int limit) : start(start), limit(limit) {}
|
||||
|
||||
size_t length() const {
|
||||
JS_ASSERT(!isUndefined());
|
||||
return limit - start;
|
||||
}
|
||||
|
||||
bool isUndefined() const {
|
||||
return start == -1;
|
||||
}
|
||||
|
||||
void check() const {
|
||||
JS_ASSERT(limit >= start);
|
||||
JS_ASSERT_IF(!isUndefined(), start >= 0);
|
||||
}
|
||||
};
|
||||
|
||||
class MatchPairs
|
||||
{
|
||||
size_t pairCount_;
|
||||
int buffer_[1];
|
||||
|
||||
explicit MatchPairs(size_t pairCount) : pairCount_(pairCount) {
|
||||
initPairValues();
|
||||
}
|
||||
|
||||
void initPairValues() {
|
||||
for (int *it = buffer_; it < buffer_ + 2 * pairCount_; ++it)
|
||||
*it = -1;
|
||||
}
|
||||
|
||||
static size_t calculateSize(size_t backingPairCount) {
|
||||
return sizeof(MatchPairs) - sizeof(int) + sizeof(int) * backingPairCount;
|
||||
}
|
||||
|
||||
int *buffer() { return buffer_; }
|
||||
|
||||
friend class RegExpPrivate;
|
||||
|
||||
public:
|
||||
/*
|
||||
* |backingPairCount| is necessary because PCRE uses extra space
|
||||
* after the actual results in the buffer.
|
||||
*/
|
||||
static MatchPairs *create(LifoAlloc &alloc, size_t pairCount, size_t backingPairCount);
|
||||
|
||||
size_t pairCount() const { return pairCount_; }
|
||||
|
||||
MatchPair pair(size_t i) {
|
||||
JS_ASSERT(i < pairCount());
|
||||
return MatchPair(buffer_[2 * i], buffer_[2 * i + 1]);
|
||||
}
|
||||
|
||||
void displace(size_t amount) {
|
||||
if (!amount)
|
||||
return;
|
||||
|
||||
for (int *it = buffer_; it < buffer_ + 2 * pairCount_; ++it)
|
||||
*it = *it < 0 ? -1 : *it + amount;
|
||||
}
|
||||
|
||||
inline void checkAgainst(size_t length);
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif
|
|
@ -52,49 +52,11 @@ inline js::RegExpObject *
|
|||
JSObject::asRegExp()
|
||||
{
|
||||
JS_ASSERT(isRegExp());
|
||||
js::RegExpObject *reobj = static_cast<js::RegExpObject *>(this);
|
||||
JS_ASSERT(reobj->getPrivate());
|
||||
return reobj;
|
||||
return static_cast<js::RegExpObject *>(this);
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
/*
|
||||
* Maintains the post-initialization invariant of having a RegExpPrivate.
|
||||
*
|
||||
* N.B. If initialization fails, the |RegExpPrivate| will be null, so
|
||||
* finalization must consider that as a possibility.
|
||||
*/
|
||||
class PreInitRegExpObject
|
||||
{
|
||||
RegExpObject *reobj;
|
||||
DebugOnly<bool> gotResult;
|
||||
|
||||
public:
|
||||
explicit PreInitRegExpObject(JSObject *obj) {
|
||||
JS_ASSERT(obj->isRegExp());
|
||||
reobj = static_cast<RegExpObject *>(obj);
|
||||
gotResult = false;
|
||||
}
|
||||
|
||||
~PreInitRegExpObject() {
|
||||
JS_ASSERT(gotResult);
|
||||
}
|
||||
|
||||
RegExpObject *get() { return reobj; }
|
||||
|
||||
void succeed() {
|
||||
JS_ASSERT(!gotResult);
|
||||
JS_ASSERT(reobj->getPrivate());
|
||||
gotResult = true;
|
||||
}
|
||||
|
||||
void fail() {
|
||||
JS_ASSERT(!gotResult);
|
||||
gotResult = true;
|
||||
}
|
||||
};
|
||||
|
||||
inline bool
|
||||
ValueIsRegExp(const Value &v)
|
||||
{
|
||||
|
@ -125,67 +87,69 @@ HasRegExpMetaChars(const jschar *chars, size_t length)
|
|||
return false;
|
||||
}
|
||||
|
||||
inline bool
|
||||
ResetRegExpObject(JSContext *cx, RegExpObject *reobj, JSString *str, RegExpFlag flags)
|
||||
{
|
||||
AlreadyIncRefed<RegExpPrivate> rep = RegExpPrivate::create(cx, str, flags, NULL);
|
||||
if (!rep)
|
||||
return false;
|
||||
|
||||
return reobj->reset(cx, rep);
|
||||
}
|
||||
|
||||
inline bool
|
||||
ResetRegExpObject(JSContext *cx, RegExpObject *reobj, AlreadyIncRefed<RegExpPrivate> rep)
|
||||
{
|
||||
return reobj->reset(cx, rep);
|
||||
}
|
||||
|
||||
inline RegExpObject *
|
||||
RegExpObject::create(JSContext *cx, RegExpStatics *res, const jschar *chars, size_t length,
|
||||
RegExpFlag flags, TokenStream *ts)
|
||||
RegExpFlag flags, TokenStream *tokenStream)
|
||||
{
|
||||
RegExpFlag staticsFlags = res->getFlags();
|
||||
return createNoStatics(cx, chars, length, RegExpFlag(flags | staticsFlags), ts);
|
||||
return createNoStatics(cx, chars, length, RegExpFlag(flags | staticsFlags), tokenStream);
|
||||
}
|
||||
|
||||
inline RegExpObject *
|
||||
RegExpObject::createNoStatics(JSContext *cx, const jschar *chars, size_t length,
|
||||
RegExpFlag flags, TokenStream *ts)
|
||||
RegExpFlag flags, TokenStream *tokenStream)
|
||||
{
|
||||
JSString *str = js_NewStringCopyN(cx, chars, length);
|
||||
if (!str)
|
||||
JSLinearString *source = js_NewStringCopyN(cx, chars, length);
|
||||
if (!source)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* |NewBuiltinClassInstance| can GC before we store |re| in the
|
||||
* private field of the object. At that point the only reference to
|
||||
* the source string could be from the malloc-allocated GC-invisible
|
||||
* |re|. So we must anchor.
|
||||
*/
|
||||
JS::Anchor<JSString *> anchor(str);
|
||||
AlreadyIncRefed<RegExpPrivate> rep = RegExpPrivate::create(cx, str, flags, ts);
|
||||
if (!rep)
|
||||
/* |NewBuiltinClassInstance| can GC. */
|
||||
JS::Anchor<JSString *> anchor(source);
|
||||
|
||||
if (!RegExpPrivateCode::checkSyntax(cx, tokenStream, source))
|
||||
return NULL;
|
||||
|
||||
JSObject *obj = NewBuiltinClassInstance(cx, &RegExpClass);
|
||||
if (!obj)
|
||||
return NULL;
|
||||
|
||||
PreInitRegExpObject pireo(obj);
|
||||
RegExpObject *reobj = pireo.get();
|
||||
if (!ResetRegExpObject(cx, reobj, rep)) {
|
||||
rep->decref(cx);
|
||||
pireo.fail();
|
||||
return NULL;
|
||||
}
|
||||
RegExpObject *reobj = obj->asRegExp();
|
||||
return reobj->reset(cx, source, flags) ? reobj : NULL;
|
||||
}
|
||||
|
||||
pireo.succeed();
|
||||
return reobj;
|
||||
inline void
|
||||
RegExpObject::finalize(JSContext *cx)
|
||||
{
|
||||
if (RegExpPrivate *rep = getPrivate())
|
||||
rep->decref(cx);
|
||||
#ifdef DEBUG
|
||||
setPrivate((void *) 0x1); /* Non-null but still in the zero page. */
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool
|
||||
RegExpObject::reset(JSContext *cx, AlreadyIncRefed<RegExpPrivate> rep)
|
||||
{
|
||||
if (!reset(cx, rep->getSource(), rep->getFlags()))
|
||||
return false;
|
||||
|
||||
setPrivate(rep.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
RegExpObject::reset(JSContext *cx, RegExpObject *other)
|
||||
{
|
||||
if (RegExpPrivate *rep = other->getPrivate()) {
|
||||
rep->incref(cx);
|
||||
return reset(cx, AlreadyIncRefed<RegExpPrivate>(rep));
|
||||
}
|
||||
|
||||
return reset(cx, other->getSource(), other->getFlags());
|
||||
}
|
||||
|
||||
inline bool
|
||||
RegExpObject::reset(JSContext *cx, JSLinearString *source, RegExpFlag flags)
|
||||
{
|
||||
if (nativeEmpty()) {
|
||||
const js::Shape **shapep = &cx->compartment->initialRegExpShape;
|
||||
|
@ -208,104 +172,20 @@ RegExpObject::reset(JSContext *cx, AlreadyIncRefed<RegExpPrivate> rep)
|
|||
MULTILINE_FLAG_SLOT);
|
||||
JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->stickyAtom))->slot == STICKY_FLAG_SLOT);
|
||||
|
||||
setPrivate(rep.get());
|
||||
zeroLastIndex();
|
||||
setSource(rep->getSource());
|
||||
setGlobal(rep->global());
|
||||
setIgnoreCase(rep->ignoreCase());
|
||||
setMultiline(rep->multiline());
|
||||
setSticky(rep->sticky());
|
||||
setPrivate(NULL);
|
||||
setSource(source);
|
||||
setGlobal(flags & GlobalFlag);
|
||||
setIgnoreCase(flags & IgnoreCaseFlag);
|
||||
setMultiline(flags & MultilineFlag);
|
||||
setSticky(flags & StickyFlag);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* RegExpPrivate inlines. */
|
||||
|
||||
class RegExpMatchBuilder
|
||||
{
|
||||
JSContext * const cx;
|
||||
JSObject * const array;
|
||||
|
||||
bool setProperty(JSAtom *name, Value v) {
|
||||
return !!js_DefineProperty(cx, array, ATOM_TO_JSID(name), &v,
|
||||
JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE);
|
||||
}
|
||||
|
||||
public:
|
||||
RegExpMatchBuilder(JSContext *cx, JSObject *array) : cx(cx), array(array) {}
|
||||
|
||||
bool append(uint32 index, Value v) {
|
||||
JS_ASSERT(!array->getOps()->getElement);
|
||||
return !!js_DefineElement(cx, array, index, &v, JS_PropertyStub, JS_StrictPropertyStub,
|
||||
JSPROP_ENUMERATE);
|
||||
}
|
||||
|
||||
bool setIndex(int index) {
|
||||
return setProperty(cx->runtime->atomState.indexAtom, Int32Value(index));
|
||||
}
|
||||
|
||||
bool setInput(JSString *str) {
|
||||
JS_ASSERT(str);
|
||||
return setProperty(cx->runtime->atomState.inputAtom, StringValue(str));
|
||||
}
|
||||
};
|
||||
|
||||
inline void
|
||||
RegExpPrivate::checkMatchPairs(JSString *input, int *buf, size_t matchItemCount)
|
||||
{
|
||||
#if DEBUG
|
||||
size_t inputLength = input->length();
|
||||
for (size_t i = 0; i < matchItemCount; i += 2) {
|
||||
int start = buf[i];
|
||||
int limit = buf[i + 1];
|
||||
JS_ASSERT(limit >= start); /* Limit index must be larger than the start index. */
|
||||
if (start == -1)
|
||||
continue;
|
||||
JS_ASSERT(start >= 0);
|
||||
JS_ASSERT(size_t(limit) <= inputLength);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
inline JSObject *
|
||||
RegExpPrivate::createResult(JSContext *cx, JSString *input, int *buf, size_t matchItemCount)
|
||||
{
|
||||
/*
|
||||
* Create the result array for a match. Array contents:
|
||||
* 0: matched string
|
||||
* 1..pairCount-1: paren matches
|
||||
*/
|
||||
JSObject *array = NewSlowEmptyArray(cx);
|
||||
if (!array)
|
||||
return NULL;
|
||||
|
||||
RegExpMatchBuilder builder(cx, array);
|
||||
for (size_t i = 0; i < matchItemCount; i += 2) {
|
||||
int start = buf[i];
|
||||
int end = buf[i + 1];
|
||||
|
||||
JSString *captured;
|
||||
if (start >= 0) {
|
||||
JS_ASSERT(start <= end);
|
||||
JS_ASSERT(unsigned(end) <= input->length());
|
||||
captured = js_NewDependentString(cx, input, start, end - start);
|
||||
if (!captured || !builder.append(i / 2, StringValue(captured)))
|
||||
return NULL;
|
||||
} else {
|
||||
/* Missing parenthesized match. */
|
||||
JS_ASSERT(i != 0); /* Since we had a match, first pair must be present. */
|
||||
if (!builder.append(i / 2, UndefinedValue()))
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!builder.setIndex(buf[0]) || !builder.setInput(input))
|
||||
return NULL;
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
inline AlreadyIncRefed<RegExpPrivate>
|
||||
RegExpPrivate::create(JSContext *cx, JSString *source, RegExpFlag flags, TokenStream *ts)
|
||||
RegExpPrivate::create(JSContext *cx, JSLinearString *source, RegExpFlag flags, TokenStream *ts)
|
||||
{
|
||||
typedef AlreadyIncRefed<RegExpPrivate> RetType;
|
||||
|
||||
|
@ -416,8 +296,8 @@ RegExpPrivate::compile(JSContext *cx, TokenStream *ts)
|
|||
return code.compile(cx, *fakeySource, ts, &parenCount, getFlags());
|
||||
}
|
||||
|
||||
inline RegExpPrivateCode::ExecuteResult
|
||||
RegExpPrivateCode::execute(JSContext *cx, const jschar *chars, size_t start, size_t length,
|
||||
inline RegExpRunStatus
|
||||
RegExpPrivateCode::execute(JSContext *cx, const jschar *chars, size_t length, size_t start,
|
||||
int *output, size_t outputCount)
|
||||
{
|
||||
int result;
|
||||
|
@ -432,17 +312,17 @@ RegExpPrivateCode::execute(JSContext *cx, const jschar *chars, size_t start, siz
|
|||
#endif
|
||||
|
||||
if (result == -1)
|
||||
return Success_NotFound;
|
||||
return RegExpRunStatus_Success_NotFound;
|
||||
|
||||
#if !ENABLE_YARR_JIT
|
||||
if (result < 0) {
|
||||
reportPCREError(cx, result);
|
||||
return Error;
|
||||
return RegExpRunStatus_Error;
|
||||
}
|
||||
#endif
|
||||
|
||||
JS_ASSERT(result >= 0);
|
||||
return Success;
|
||||
return RegExpRunStatus_Success;
|
||||
}
|
||||
|
||||
inline void
|
||||
|
@ -464,6 +344,15 @@ RegExpPrivate::decref(JSContext *cx)
|
|||
cx->delete_(this);
|
||||
}
|
||||
|
||||
inline RegExpPrivate *
|
||||
RegExpObject::getOrCreatePrivate(JSContext *cx)
|
||||
{
|
||||
if (RegExpPrivate *rep = getPrivate())
|
||||
return rep;
|
||||
|
||||
return makePrivate(cx) ? getPrivate() : NULL;
|
||||
}
|
||||
|
||||
inline RegExpPrivate *
|
||||
RegExpObject::getPrivate() const
|
||||
{
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "jsscan.h"
|
||||
|
||||
#include "vm/RegExpStatics.h"
|
||||
#include "vm/MatchPairs.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
#include "jsstrinlines.h"
|
||||
|
@ -59,90 +60,99 @@ JS_STATIC_ASSERT(GlobalFlag == JSREG_GLOB);
|
|||
JS_STATIC_ASSERT(MultilineFlag == JSREG_MULTILINE);
|
||||
JS_STATIC_ASSERT(StickyFlag == JSREG_STICKY);
|
||||
|
||||
bool
|
||||
RegExpPrivate::executeInternal(JSContext *cx, RegExpStatics *res, JSString *inputstr,
|
||||
size_t *lastIndex, bool test, Value *rval)
|
||||
MatchPairs *
|
||||
MatchPairs::create(LifoAlloc &alloc, size_t pairCount, size_t backingPairCount)
|
||||
{
|
||||
const size_t pairCount = parenCount + 1;
|
||||
const size_t matchItemCount = pairCount * 2;
|
||||
const size_t bufCount = RegExpPrivateCode::getOutputSize(pairCount);
|
||||
void *mem = alloc.alloc(calculateSize(backingPairCount));
|
||||
if (!mem)
|
||||
return NULL;
|
||||
|
||||
LifoAllocScope las(&cx->tempLifoAlloc());
|
||||
int *buf = cx->tempLifoAlloc().newArray<int>(bufCount);
|
||||
if (!buf)
|
||||
return false;
|
||||
return new (mem) MatchPairs(pairCount);
|
||||
}
|
||||
|
||||
inline void
|
||||
MatchPairs::checkAgainst(size_t inputLength)
|
||||
{
|
||||
#if DEBUG
|
||||
for (size_t i = 0; i < pairCount(); ++i) {
|
||||
MatchPair p = pair(i);
|
||||
p.check();
|
||||
if (p.isUndefined())
|
||||
continue;
|
||||
JS_ASSERT(size_t(p.limit) <= inputLength);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
RegExpRunStatus
|
||||
RegExpPrivate::execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
|
||||
LifoAllocScope &allocScope, MatchPairs **output)
|
||||
{
|
||||
const size_t origLength = length;
|
||||
size_t backingPairCount = RegExpPrivateCode::getOutputSize(pairCount());
|
||||
|
||||
MatchPairs *matchPairs = MatchPairs::create(allocScope.alloc(), pairCount(), backingPairCount);
|
||||
if (!matchPairs)
|
||||
return RegExpRunStatus_Error;
|
||||
|
||||
/*
|
||||
* The JIT regexp procedure doesn't always initialize matchPair values.
|
||||
* Maybe we can make this faster by ensuring it does?
|
||||
* |displacement| emulates sticky mode by matching from this offset
|
||||
* into the char buffer and subtracting the delta off at the end.
|
||||
*/
|
||||
for (int *it = buf; it != buf + matchItemCount; ++it)
|
||||
*it = -1;
|
||||
|
||||
JSLinearString *input = inputstr->ensureLinear(cx);
|
||||
if (!input)
|
||||
return false;
|
||||
|
||||
JS::Anchor<JSString *> anchor(input);
|
||||
size_t len = input->length();
|
||||
const jschar *chars = input->chars();
|
||||
|
||||
/*
|
||||
* inputOffset emulates sticky mode by matching from this offset into the char buf and
|
||||
* subtracting the delta off at the end.
|
||||
*/
|
||||
size_t inputOffset = 0;
|
||||
size_t start = *lastIndex;
|
||||
size_t displacement = 0;
|
||||
|
||||
if (sticky()) {
|
||||
/* Sticky matches at the last index for the regexp object. */
|
||||
chars += *lastIndex;
|
||||
len -= *lastIndex;
|
||||
inputOffset = *lastIndex;
|
||||
displacement = *lastIndex;
|
||||
chars += displacement;
|
||||
length -= displacement;
|
||||
start = 0;
|
||||
}
|
||||
|
||||
size_t start = *lastIndex - inputOffset;
|
||||
RegExpPrivateCode::ExecuteResult result = code.execute(cx, chars, start, len, buf, bufCount);
|
||||
RegExpRunStatus status = code.execute(cx, chars, length, start,
|
||||
matchPairs->buffer(), backingPairCount);
|
||||
|
||||
switch (result) {
|
||||
case RegExpPrivateCode::Error:
|
||||
return false;
|
||||
case RegExpPrivateCode::Success_NotFound:
|
||||
*rval = NullValue();
|
||||
return true;
|
||||
switch (status) {
|
||||
case RegExpRunStatus_Error:
|
||||
return status;
|
||||
case RegExpRunStatus_Success_NotFound:
|
||||
*output = matchPairs;
|
||||
return status;
|
||||
default:
|
||||
JS_ASSERT(result == RegExpPrivateCode::Success);
|
||||
JS_ASSERT(status == RegExpRunStatus_Success);
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjust buf for the inputOffset. Use of sticky is rare and the matchItemCount is small, so
|
||||
* just do another pass.
|
||||
*/
|
||||
if (JS_UNLIKELY(inputOffset)) {
|
||||
for (size_t i = 0; i < matchItemCount; ++i)
|
||||
buf[i] = buf[i] < 0 ? -1 : buf[i] + inputOffset;
|
||||
}
|
||||
matchPairs->displace(displacement);
|
||||
matchPairs->checkAgainst(origLength);
|
||||
|
||||
/* Make sure the populated contents of |buf| are sane values against |input|. */
|
||||
checkMatchPairs(input, buf, matchItemCount);
|
||||
*lastIndex = matchPairs->pair(0).limit;
|
||||
*output = matchPairs;
|
||||
|
||||
if (res)
|
||||
res->updateFromMatch(cx, input, buf, matchItemCount);
|
||||
return RegExpRunStatus_Success;
|
||||
}
|
||||
|
||||
*lastIndex = buf[1];
|
||||
|
||||
if (test) {
|
||||
*rval = BooleanValue(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
JSObject *array = createResult(cx, input, buf, matchItemCount);
|
||||
if (!array)
|
||||
bool
|
||||
RegExpObject::makePrivate(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(!getPrivate());
|
||||
AlreadyIncRefed<RegExpPrivate> rep = RegExpPrivate::create(cx, getSource(), getFlags(), NULL);
|
||||
if (!rep)
|
||||
return false;
|
||||
|
||||
*rval = ObjectValue(*array);
|
||||
setPrivate(rep.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
RegExpRunStatus
|
||||
RegExpObject::execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
|
||||
LifoAllocScope &allocScope, MatchPairs **output)
|
||||
{
|
||||
if (!getPrivate() && !makePrivate(cx))
|
||||
return RegExpRunStatus_Error;
|
||||
|
||||
return getPrivate()->execute(cx, chars, length, lastIndex, allocScope, output);
|
||||
}
|
||||
|
||||
const Shape *
|
||||
RegExpObject::assignInitialShape(JSContext *cx)
|
||||
{
|
||||
|
@ -226,8 +236,7 @@ js_XDRRegExpObject(JSXDRState *xdr, JSObject **objp)
|
|||
static void
|
||||
regexp_finalize(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
if (RegExpPrivate *rep = static_cast<RegExpObject *>(obj)->getPrivate())
|
||||
rep->decref(cx);
|
||||
obj->asRegExp()->finalize(cx);
|
||||
}
|
||||
|
||||
Class js::RegExpClass = {
|
||||
|
@ -294,9 +303,9 @@ RegExpPrivateCode::reportPCREError(JSContext *cx, int error)
|
|||
return
|
||||
switch (error) {
|
||||
case -2: REPORT(JSMSG_REGEXP_TOO_COMPLEX);
|
||||
case 0: JS_NOT_REACHED("Precondition violation: an error must have occurred.");
|
||||
case 0: JS_NOT_REACHED("Precondition violation: an error must have occurred.");
|
||||
case 1: REPORT(JSMSG_TRAILING_SLASH);
|
||||
case 2: REPORT(JSMSG_TRAILING_SLASH);
|
||||
case 2: REPORT(JSMSG_TRAILING_SLASH);
|
||||
case 3: REPORT(JSMSG_REGEXP_TOO_COMPLEX);
|
||||
case 4: REPORT(JSMSG_BAD_QUANTIFIER);
|
||||
case 5: REPORT(JSMSG_BAD_QUANTIFIER);
|
||||
|
@ -317,6 +326,7 @@ RegExpPrivateCode::reportPCREError(JSContext *cx, int error)
|
|||
}
|
||||
#undef REPORT
|
||||
}
|
||||
|
||||
#endif /* ENABLE_YARR_JIT */
|
||||
|
||||
bool
|
||||
|
@ -357,7 +367,7 @@ js::ParseRegExpFlags(JSContext *cx, JSString *flagStr, RegExpFlag *flagsOut)
|
|||
}
|
||||
|
||||
AlreadyIncRefed<RegExpPrivate>
|
||||
RegExpPrivate::createFlagged(JSContext *cx, JSString *str, JSString *opt, TokenStream *ts)
|
||||
RegExpPrivate::create(JSContext *cx, JSLinearString *str, JSString *opt, TokenStream *ts)
|
||||
{
|
||||
if (!opt)
|
||||
return create(cx, str, RegExpFlag(0), ts);
|
||||
|
@ -370,7 +380,7 @@ RegExpPrivate::createFlagged(JSContext *cx, JSString *str, JSString *opt, TokenS
|
|||
}
|
||||
|
||||
RegExpObject *
|
||||
RegExpObject::clone(JSContext *cx, RegExpObject *obj, RegExpObject *proto)
|
||||
RegExpObject::clone(JSContext *cx, RegExpObject *reobj, RegExpObject *proto)
|
||||
{
|
||||
JSObject *clone = NewNativeClassInstance(cx, &RegExpClass, proto, proto->getParent());
|
||||
if (!clone)
|
||||
|
@ -380,41 +390,35 @@ RegExpObject::clone(JSContext *cx, RegExpObject *obj, RegExpObject *proto)
|
|||
* This clone functionality does not duplicate the JIT'd code blob,
|
||||
* which is necessary for cross-compartment cloning functionality.
|
||||
*/
|
||||
assertSameCompartment(cx, obj, clone);
|
||||
assertSameCompartment(cx, reobj, clone);
|
||||
|
||||
RegExpStatics *res = cx->regExpStatics();
|
||||
RegExpObject *reclone = clone->asRegExp();
|
||||
|
||||
/*
|
||||
/*
|
||||
* Check that the RegExpPrivate for the original is okay to use in
|
||||
* the clone -- if the RegExpStatics provides more flags we'll need
|
||||
* a different RegExpPrivate.
|
||||
* the clone -- if the |RegExpStatics| provides more flags we'll
|
||||
* need a different |RegExpPrivate|.
|
||||
*/
|
||||
AlreadyIncRefed<RegExpPrivate> rep(NULL);
|
||||
{
|
||||
RegExpFlag origFlags = obj->getFlags();
|
||||
RegExpFlag staticsFlags = res->getFlags();
|
||||
if ((origFlags & staticsFlags) != staticsFlags) {
|
||||
RegExpFlag newFlags = RegExpFlag(origFlags | staticsFlags);
|
||||
rep = RegExpPrivate::create(cx, obj->getSource(), newFlags, NULL);
|
||||
if (!rep)
|
||||
return NULL;
|
||||
} else {
|
||||
RegExpPrivate *toShare = obj->getPrivate();
|
||||
toShare->incref(cx);
|
||||
rep = AlreadyIncRefed<RegExpPrivate>(toShare);
|
||||
RegExpFlag origFlags = reobj->getFlags();
|
||||
RegExpFlag staticsFlags = res->getFlags();
|
||||
if ((origFlags & staticsFlags) != staticsFlags) {
|
||||
RegExpFlag newFlags = RegExpFlag(origFlags | staticsFlags);
|
||||
return reclone->reset(cx, reobj->getSource(), newFlags) ? reclone : NULL;
|
||||
}
|
||||
|
||||
RegExpPrivate *toShare = reobj->getPrivate();
|
||||
if (toShare) {
|
||||
toShare->incref(cx);
|
||||
if (!reclone->reset(cx, AlreadyIncRefed<RegExpPrivate>(toShare))) {
|
||||
toShare->decref(cx);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
if (!reclone->reset(cx, reobj))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JS_ASSERT(rep);
|
||||
|
||||
PreInitRegExpObject pireo(clone);
|
||||
RegExpObject *reclone = pireo.get();
|
||||
if (!ResetRegExpObject(cx, reclone, rep)) {
|
||||
pireo.fail();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pireo.succeed();
|
||||
return reclone;
|
||||
}
|
||||
|
||||
|
@ -435,11 +439,7 @@ JS_DEFINE_CALLINFO_3(extern, OBJECT, js_CloneRegExpObject, CONTEXT, OBJECT, OBJE
|
|||
JSFlatString *
|
||||
RegExpObject::toString(JSContext *cx) const
|
||||
{
|
||||
RegExpPrivate *rep = getPrivate();
|
||||
if (!rep)
|
||||
return cx->runtime->emptyString;
|
||||
|
||||
JSLinearString *src = rep->getSource();
|
||||
JSLinearString *src = getSource();
|
||||
StringBuffer sb(cx);
|
||||
if (size_t len = src->length()) {
|
||||
if (!sb.reserve(len + 2))
|
||||
|
@ -451,13 +451,13 @@ RegExpObject::toString(JSContext *cx) const
|
|||
if (!sb.append("/(?:)/"))
|
||||
return NULL;
|
||||
}
|
||||
if (rep->global() && !sb.append('g'))
|
||||
if (global() && !sb.append('g'))
|
||||
return NULL;
|
||||
if (rep->ignoreCase() && !sb.append('i'))
|
||||
if (ignoreCase() && !sb.append('i'))
|
||||
return NULL;
|
||||
if (rep->multiline() && !sb.append('m'))
|
||||
if (multiline() && !sb.append('m'))
|
||||
return NULL;
|
||||
if (rep->sticky() && !sb.append('y'))
|
||||
if (sticky() && !sb.append('y'))
|
||||
return NULL;
|
||||
|
||||
return sb.finishString();
|
||||
|
|
|
@ -49,13 +49,19 @@
|
|||
#include "yarr/Yarr.h"
|
||||
#if ENABLE_YARR_JIT
|
||||
#include "yarr/YarrJIT.h"
|
||||
#include "yarr/YarrSyntaxChecker.h"
|
||||
#else
|
||||
#include "yarr/pcre/pcre.h"
|
||||
#endif
|
||||
|
||||
namespace js {
|
||||
|
||||
class RegExpPrivate;
|
||||
enum RegExpRunStatus
|
||||
{
|
||||
RegExpRunStatus_Error,
|
||||
RegExpRunStatus_Success,
|
||||
RegExpRunStatus_Success_NotFound
|
||||
};
|
||||
|
||||
class RegExpObject : public ::JSObject
|
||||
{
|
||||
|
@ -87,6 +93,20 @@ class RegExpObject : public ::JSObject
|
|||
/* Note: fallible. */
|
||||
JSFlatString *toString(JSContext *cx) const;
|
||||
|
||||
/*
|
||||
* Run the regular expression over the input text.
|
||||
*
|
||||
* Results are placed in |output| as integer pairs. For eaxmple,
|
||||
* |output[0]| and |output[1]| represent the text indices that make
|
||||
* up the "0" (whole match) pair. Capturing parens will result in
|
||||
* more output.
|
||||
*
|
||||
* N.B. it's the responsibility of the caller to hook the |output|
|
||||
* into the |RegExpStatics| appropriately, if necessary.
|
||||
*/
|
||||
RegExpRunStatus execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
|
||||
LifoAllocScope &allocScope, MatchPairs **output);
|
||||
|
||||
/* Accessors. */
|
||||
|
||||
const Value &getLastIndex() const {
|
||||
|
@ -102,21 +122,20 @@ class RegExpObject : public ::JSObject
|
|||
setSlot(LAST_INDEX_SLOT, Int32Value(0));
|
||||
}
|
||||
|
||||
JSString *getSource() const {
|
||||
return getSlot(SOURCE_SLOT).toString();
|
||||
JSLinearString *getSource() const {
|
||||
return &getSlot(SOURCE_SLOT).toString()->asLinear();
|
||||
}
|
||||
void setSource(JSString *source) {
|
||||
void setSource(JSLinearString *source) {
|
||||
setSlot(SOURCE_SLOT, StringValue(source));
|
||||
}
|
||||
RegExpFlag getFlags() const {
|
||||
uintN flags = 0;
|
||||
flags |= getSlot(GLOBAL_FLAG_SLOT).toBoolean() ? GlobalFlag : 0;
|
||||
flags |= getSlot(IGNORE_CASE_FLAG_SLOT).toBoolean() ? IgnoreCaseFlag : 0;
|
||||
flags |= getSlot(MULTILINE_FLAG_SLOT).toBoolean() ? MultilineFlag : 0;
|
||||
flags |= getSlot(STICKY_FLAG_SLOT).toBoolean() ? StickyFlag : 0;
|
||||
flags |= global() ? GlobalFlag : 0;
|
||||
flags |= ignoreCase() ? IgnoreCaseFlag : 0;
|
||||
flags |= multiline() ? MultilineFlag : 0;
|
||||
flags |= sticky() ? StickyFlag : 0;
|
||||
return RegExpFlag(flags);
|
||||
}
|
||||
inline RegExpPrivate *getPrivate() const;
|
||||
|
||||
/* Flags. */
|
||||
|
||||
|
@ -124,12 +143,34 @@ class RegExpObject : public ::JSObject
|
|||
void setGlobal(bool enabled) { setSlot(GLOBAL_FLAG_SLOT, BooleanValue(enabled)); }
|
||||
void setMultiline(bool enabled) { setSlot(MULTILINE_FLAG_SLOT, BooleanValue(enabled)); }
|
||||
void setSticky(bool enabled) { setSlot(STICKY_FLAG_SLOT, BooleanValue(enabled)); }
|
||||
bool ignoreCase() const { return getSlot(IGNORE_CASE_FLAG_SLOT).toBoolean(); }
|
||||
bool global() const { return getSlot(GLOBAL_FLAG_SLOT).toBoolean(); }
|
||||
bool multiline() const { return getSlot(MULTILINE_FLAG_SLOT).toBoolean(); }
|
||||
bool sticky() const { return getSlot(STICKY_FLAG_SLOT).toBoolean(); }
|
||||
|
||||
/*
|
||||
* N.B. |RegExpObject|s can be mutated in place because of |RegExp.prototype.compile|, hence
|
||||
* |reset| for re-initialization.
|
||||
*/
|
||||
|
||||
inline bool reset(JSContext *cx, RegExpObject *other);
|
||||
inline bool reset(JSContext *cx, AlreadyIncRefed<RegExpPrivate> rep);
|
||||
inline bool reset(JSContext *cx, JSLinearString *source, RegExpFlag flags);
|
||||
|
||||
inline RegExpPrivate *getOrCreatePrivate(JSContext *cx);
|
||||
inline void finalize(JSContext *cx);
|
||||
|
||||
private:
|
||||
/* N.B. |RegExpObject|s can be mutated in place because of |RegExp.prototype.compile|. */
|
||||
inline bool reset(JSContext *cx, AlreadyIncRefed<RegExpPrivate> rep);
|
||||
/* The |RegExpPrivate| is lazily created at the time of use. */
|
||||
inline RegExpPrivate *getPrivate() const;
|
||||
|
||||
friend bool ResetRegExpObject(JSContext *, RegExpObject *, JSString *, RegExpFlag);
|
||||
/*
|
||||
* Precondition: the syntax for |source| has already been validated.
|
||||
* Side effect: sets the private field.
|
||||
*/
|
||||
bool makePrivate(JSContext *cx);
|
||||
|
||||
friend bool ResetRegExpObject(JSContext *, RegExpObject *, JSLinearString *, RegExpFlag);
|
||||
friend bool ResetRegExpObject(JSContext *, RegExpObject *, AlreadyIncRefed<RegExpPrivate>);
|
||||
|
||||
/*
|
||||
|
@ -143,13 +184,6 @@ class RegExpObject : public ::JSObject
|
|||
RegExpObject &operator=(const RegExpObject &reo);
|
||||
}; /* class RegExpObject */
|
||||
|
||||
inline bool
|
||||
ResetRegExpObject(JSContext *cx, RegExpObject *reobj, JSString *str, RegExpFlag flags);
|
||||
|
||||
/* N.B. On failure, caller must decref |rep|. */
|
||||
inline bool
|
||||
ResetRegExpObject(JSContext *cx, AlreadyIncRefed<RegExpPrivate> rep);
|
||||
|
||||
/* Abstracts away the gross |RegExpPrivate| backend details. */
|
||||
class RegExpPrivateCode
|
||||
{
|
||||
|
@ -189,20 +223,31 @@ class RegExpPrivateCode
|
|||
#endif
|
||||
}
|
||||
|
||||
static bool checkSyntax(JSContext *cx, TokenStream *tokenStream, JSLinearString *source) {
|
||||
#if ENABLE_YARR_JIT
|
||||
ErrorCode error = JSC::Yarr::checkSyntax(*source);
|
||||
if (error == JSC::Yarr::NoError)
|
||||
return true;
|
||||
|
||||
reportYarrError(cx, tokenStream, error);
|
||||
return false;
|
||||
#else
|
||||
# error "Syntax checking not implemented for !ENABLE_YARR_JIT"
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ENABLE_YARR_JIT
|
||||
static inline bool isJITRuntimeEnabled(JSContext *cx);
|
||||
void reportYarrError(JSContext *cx, TokenStream *ts, JSC::Yarr::ErrorCode error);
|
||||
static void reportYarrError(JSContext *cx, TokenStream *ts, JSC::Yarr::ErrorCode error);
|
||||
#else
|
||||
void reportPCREError(JSContext *cx, int error);
|
||||
static void reportPCREError(JSContext *cx, int error);
|
||||
#endif
|
||||
|
||||
inline bool compile(JSContext *cx, JSLinearString &pattern, TokenStream *ts, uintN *parenCount,
|
||||
RegExpFlag flags);
|
||||
|
||||
enum ExecuteResult { Error, Success, Success_NotFound };
|
||||
|
||||
inline ExecuteResult execute(JSContext *cx, const jschar *chars, size_t start, size_t length,
|
||||
int *output, size_t outputCount);
|
||||
inline RegExpRunStatus execute(JSContext *cx, const jschar *chars, size_t length, size_t start,
|
||||
int *output, size_t outputCount);
|
||||
|
||||
static size_t getOutputSize(size_t pairCount) {
|
||||
#if ENABLE_YARR_JIT
|
||||
|
@ -247,37 +292,16 @@ class RegExpPrivate
|
|||
|
||||
bool compile(JSContext *cx, TokenStream *ts);
|
||||
static inline void checkMatchPairs(JSString *input, int *buf, size_t matchItemCount);
|
||||
static JSObject *createResult(JSContext *cx, JSString *input, int *buf, size_t matchItemCount);
|
||||
bool executeInternal(JSContext *cx, RegExpStatics *res, JSString *input,
|
||||
size_t *lastIndex, bool test, Value *rval);
|
||||
|
||||
public:
|
||||
/*
|
||||
* Execute regexp on |input| at |*lastIndex|.
|
||||
*
|
||||
* On match: Update |*lastIndex| and RegExp class statics.
|
||||
* Return true if test is true. Place an array in |*rval| if test is false.
|
||||
* On mismatch: Make |*rval| null.
|
||||
*/
|
||||
bool execute(JSContext *cx, RegExpStatics *res, JSString *input, size_t *lastIndex, bool test,
|
||||
Value *rval) {
|
||||
JS_ASSERT(res);
|
||||
return executeInternal(cx, res, input, lastIndex, test, rval);
|
||||
}
|
||||
static AlreadyIncRefed<RegExpPrivate>
|
||||
create(JSContext *cx, JSLinearString *source, RegExpFlag flags, TokenStream *ts);
|
||||
|
||||
bool executeNoStatics(JSContext *cx, JSString *input, size_t *lastIndex, bool test,
|
||||
Value *rval) {
|
||||
return executeInternal(cx, NULL, input, lastIndex, test, rval);
|
||||
}
|
||||
static AlreadyIncRefed<RegExpPrivate>
|
||||
create(JSContext *cx, JSLinearString *source, JSString *flags, TokenStream *ts);
|
||||
|
||||
/* Factories */
|
||||
|
||||
static AlreadyIncRefed<RegExpPrivate> create(JSContext *cx, JSString *source, RegExpFlag flags,
|
||||
TokenStream *ts);
|
||||
|
||||
/* Would overload |create|, but |0| resolves ambiguously against pointer and uint. */
|
||||
static AlreadyIncRefed<RegExpPrivate> createFlagged(JSContext *cx, JSString *source,
|
||||
JSString *flags, TokenStream *ts);
|
||||
RegExpRunStatus execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
|
||||
LifoAllocScope &allocScope, MatchPairs **output);
|
||||
|
||||
/* Mutators */
|
||||
|
||||
|
@ -289,14 +313,18 @@ class RegExpPrivate
|
|||
|
||||
/* Accessors */
|
||||
|
||||
JSLinearString *getSource() const { return source; }
|
||||
size_t getParenCount() const { return parenCount; }
|
||||
RegExpFlag getFlags() const { return flags; }
|
||||
JSLinearString *getSource() const { return source; }
|
||||
size_t getParenCount() const { return parenCount; }
|
||||
|
||||
/* Accounts for the "0" (whole match) pair. */
|
||||
size_t pairCount() const { return parenCount + 1; }
|
||||
|
||||
RegExpFlag getFlags() const { return flags; }
|
||||
bool ignoreCase() const { return flags & IgnoreCaseFlag; }
|
||||
bool global() const { return flags & GlobalFlag; }
|
||||
bool multiline() const { return flags & MultilineFlag; }
|
||||
bool sticky() const { return flags & StickyFlag; }
|
||||
}; /* class RegExpPrivate */
|
||||
};
|
||||
|
||||
/*
|
||||
* Parse regexp flags. Report an error and return false if an invalid
|
||||
|
|
|
@ -45,12 +45,14 @@
|
|||
|
||||
#include "js/Vector.h"
|
||||
|
||||
#include "vm/MatchPairs.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
class RegExpStatics
|
||||
{
|
||||
typedef Vector<int, 20, SystemAllocPolicy> MatchPairs;
|
||||
MatchPairs matchPairs;
|
||||
typedef Vector<int, 20, SystemAllocPolicy> Pairs;
|
||||
Pairs matchPairs;
|
||||
/* The input that was used to produce matchPairs. */
|
||||
JSLinearString *matchPairsInput;
|
||||
/* The input last set on the statics. */
|
||||
|
@ -162,17 +164,20 @@ class RegExpStatics
|
|||
|
||||
/* Mutators. */
|
||||
|
||||
bool updateFromMatch(JSContext *cx, JSLinearString *input, int *buf, size_t matchItemCount) {
|
||||
bool updateFromMatchPairs(JSContext *cx, JSLinearString *input, MatchPairs *newPairs) {
|
||||
JS_ASSERT(input);
|
||||
aboutToWrite();
|
||||
pendingInput = input;
|
||||
|
||||
if (!matchPairs.resizeUninitialized(matchItemCount)) {
|
||||
if (!matchPairs.resizeUninitialized(2 * newPairs->pairCount())) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < matchItemCount; ++i)
|
||||
matchPairs[i] = buf[i];
|
||||
for (size_t i = 0; i < newPairs->pairCount(); ++i) {
|
||||
matchPairs[2 * i] = newPairs->pair(i).start;
|
||||
matchPairs[2 * i + 1] = newPairs->pair(i).limit;
|
||||
}
|
||||
|
||||
matchPairsInput = input;
|
||||
return true;
|
||||
|
|
Загрузка…
Ссылка в новой задаче