Bug 688069 - fix String.prototype.{replace,match,search,split} for transparently wrapped RegExp arguments (r=cdleary)

--HG--
extra : rebase_source : 52e43bfbd134f8afcff15b354fcb777d9b0e71ba
This commit is contained in:
Luke Wagner 2012-02-01 13:36:48 -08:00
Родитель 19a5502f89
Коммит 0c3a78bf21
14 изменённых файлов: 87 добавлений и 44 удалений

Просмотреть файл

@ -230,7 +230,7 @@ CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder, CallArgs args)
} }
Value sourceValue = args[0]; Value sourceValue = args[0];
if (ValueIsRegExp(sourceValue)) { if (IsObjectWithClass(sourceValue, ESClass_RegExp, cx)) {
/* /*
* If we get passed in a |RegExpObject| source we return a new * If we get passed in a |RegExpObject| source we return a new
* object with the same source/flags. * object with the same source/flags.
@ -243,9 +243,15 @@ CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder, CallArgs args)
return false; return false;
} }
RegExpObject *reobj = builder.build(&sourceObj.asRegExp()); RegExpShared *shared = RegExpToShared(cx, sourceObj);
if (!shared)
return false;
shared->incref(cx);
RegExpObject *reobj = builder.build(AlreadyIncRefed<RegExpShared>(shared));
if (!reobj) if (!reobj)
return false; return false;
args.rval() = ObjectValue(*reobj); args.rval() = ObjectValue(*reobj);
return true; return true;
} }
@ -314,7 +320,7 @@ regexp_construct(JSContext *cx, uintN argc, Value *vp)
* Otherwise, delegate to the standard constructor. * Otherwise, delegate to the standard constructor.
* See ECMAv5 15.10.3.1. * See ECMAv5 15.10.3.1.
*/ */
if (args.length() >= 1 && ValueIsRegExp(args[0]) && if (args.length() >= 1 && IsObjectWithClass(args[0], ESClass_RegExp, cx) &&
(args.length() == 1 || args[1].isUndefined())) (args.length() == 1 || args[1].isUndefined()))
{ {
args.rval() = args[0]; args.rval() = args[0];

Просмотреть файл

@ -983,7 +983,7 @@ static bool
EmitIndex32(JSContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce) EmitIndex32(JSContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce)
{ {
const size_t len = 1 + UINT32_INDEX_LEN; const size_t len = 1 + UINT32_INDEX_LEN;
JS_ASSERT(js_CodeSpec[op].length == len); JS_ASSERT(size_t(js_CodeSpec[op].length) == len);
ptrdiff_t offset = EmitCheck(cx, bce, len); ptrdiff_t offset = EmitCheck(cx, bce, len);
if (offset < 0) if (offset < 0)
return false; return false;

Просмотреть файл

@ -27,7 +27,6 @@ function test(str, f) {
f(g1.eval("new Object()")); f(g1.eval("new Object()"));
} catch (e) { } catch (e) {
assertEq(Object.prototype.toString.call(e), "[object Error]"); assertEq(Object.prototype.toString.call(e), "[object Error]");
assertEq(e.name, "TypeError");
threw = true; threw = true;
} }
assertEq(threw, true); assertEq(threw, true);
@ -36,7 +35,6 @@ function test(str, f) {
f(g2.eval("new Object()")); f(g2.eval("new Object()"));
} catch (e) { } catch (e) {
assertEq(Object.prototype.toString.call(e), "[object Error]"); assertEq(Object.prototype.toString.call(e), "[object Error]");
assertEq(e.name, "TypeError");
threw = true; threw = true;
} }
assertEq(threw, true); assertEq(threw, true);
@ -45,7 +43,6 @@ function test(str, f) {
f(proxy1); f(proxy1);
} catch (e) { } catch (e) {
assertEq(Object.prototype.toString.call(e), "[object Error]"); assertEq(Object.prototype.toString.call(e), "[object Error]");
assertEq(e.name, "TypeError");
threw = true; threw = true;
} }
assertEq(threw, true); assertEq(threw, true);
@ -54,7 +51,6 @@ function test(str, f) {
f(proxy2); f(proxy2);
} catch (e) { } catch (e) {
assertEq(Object.prototype.toString.call(e), "[object Error]"); assertEq(Object.prototype.toString.call(e), "[object Error]");
assertEq(e.name, "TypeError");
threw = true; threw = true;
} }
assertEq(threw, true); assertEq(threw, true);
@ -78,6 +74,13 @@ test("new RegExp('1')", function(r) RegExp.prototype.toString.call(r));
test("new RegExp('1')", function(r) RegExp.prototype.exec.call(r, '1').toString()); test("new RegExp('1')", function(r) RegExp.prototype.exec.call(r, '1').toString());
test("new RegExp('1')", function(r) RegExp.prototype.test.call(r, '1')); test("new RegExp('1')", function(r) RegExp.prototype.test.call(r, '1'));
test("new RegExp('1')", function(r) RegExp.prototype.compile.call(r, '1').toString()); test("new RegExp('1')", function(r) RegExp.prototype.compile.call(r, '1').toString());
test("new RegExp('1')", function(r) assertEq("a1".search(r), 1));
test("new RegExp('1')", function(r) assertEq("a1".match(r)[0], '1'));
test("new RegExp('1')", function(r) assertEq("a1".replace(r, 'A'), 'aA'));
test("new RegExp('1')", function(r) assertEq(String("a1b".split(r)), "a,b"));
test("new RegExp('1')", function(r) assertEq(r, RegExp(r)));
test("new RegExp('1')", function(r) assertEq(String(new RegExp(r)), String(r)));
test("new RegExp('1')", function(r) assertEq(String(/x/.compile(r)), String(r)));
test("new WeakMap()", function(w) WeakMap.prototype.has.call(w, {})); test("new WeakMap()", function(w) WeakMap.prototype.has.call(w, {}));
test("new WeakMap()", function(w) WeakMap.prototype.get.call(w, {})); test("new WeakMap()", function(w) WeakMap.prototype.get.call(w, {}));
test("new WeakMap()", function(w) WeakMap.prototype.delete.call(w, {})); test("new WeakMap()", function(w) WeakMap.prototype.delete.call(w, {}));

Просмотреть файл

@ -3567,9 +3567,7 @@ static JSBool
array_isArray(JSContext *cx, uintN argc, Value *vp) array_isArray(JSContext *cx, uintN argc, Value *vp)
{ {
CallArgs args = CallArgsFromVp(argc, vp); CallArgs args = CallArgsFromVp(argc, vp);
bool isArray = args.length() > 0 && bool isArray = args.length() > 0 && IsObjectWithClass(args[0], ESClass_Array, cx);
args[0].isObject() &&
ObjectClassIs(args[0].toObject(), ESClass_Array, cx);
args.rval().setBoolean(isArray); args.rval().setBoolean(isArray);
return true; return true;
} }

Просмотреть файл

@ -407,7 +407,9 @@ Valueify(const JSClass *c)
* Enumeration describing possible values of the [[Class]] internal property * Enumeration describing possible values of the [[Class]] internal property
* value of objects. * value of objects.
*/ */
enum ESClassValue { ESClass_Array, ESClass_Number, ESClass_String, ESClass_Boolean }; enum ESClassValue {
ESClass_Array, ESClass_Number, ESClass_String, ESClass_Boolean, ESClass_RegExp
};
/* /*
* Return whether the given object has the given [[Class]] internal property * Return whether the given object has the given [[Class]] internal property
@ -418,6 +420,10 @@ enum ESClassValue { ESClass_Array, ESClass_Number, ESClass_String, ESClass_Boole
inline bool inline bool
ObjectClassIs(JSObject &obj, ESClassValue classValue, JSContext *cx); ObjectClassIs(JSObject &obj, ESClassValue classValue, JSContext *cx);
/* Just a helper that checks v.isObject before calling ObjectClassIs. */
inline bool
IsObjectWithClass(const Value &v, ESClassValue classValue, JSContext *cx);
} /* namespace js */ } /* namespace js */
#endif /* __cplusplus */ #endif /* __cplusplus */

Просмотреть файл

@ -1953,11 +1953,20 @@ ObjectClassIs(JSObject &obj, ESClassValue classValue, JSContext *cx)
case ESClass_Number: return obj.isNumber(); case ESClass_Number: return obj.isNumber();
case ESClass_String: return obj.isString(); case ESClass_String: return obj.isString();
case ESClass_Boolean: return obj.isBoolean(); case ESClass_Boolean: return obj.isBoolean();
case ESClass_RegExp: return obj.isRegExp();
} }
JS_NOT_REACHED("bad classValue"); JS_NOT_REACHED("bad classValue");
return false; return false;
} }
inline bool
IsObjectWithClass(const Value &v, ESClassValue classValue, JSContext *cx)
{
if (!v.isObject())
return false;
return ObjectClassIs(v.toObject(), classValue, cx);
}
static JS_ALWAYS_INLINE bool static JS_ALWAYS_INLINE bool
ValueIsSpecial(JSObject *obj, Value *propval, SpecialId *sidp, JSContext *cx) ValueIsSpecial(JSObject *obj, Value *propval, SpecialId *sidp, JSContext *cx)
{ {

Просмотреть файл

@ -301,6 +301,13 @@ ProxyHandler::fun_toString(JSContext *cx, JSObject *proxy, uintN indent)
return fun_toStringHelper(cx, &fval.toObject(), indent); return fun_toStringHelper(cx, &fval.toObject(), indent);
} }
RegExpShared *
ProxyHandler::regexp_toShared(JSContext *cx, JSObject *proxy)
{
JS_NOT_REACHED("This should have been a wrapped regexp");
return (RegExpShared *)NULL;
}
bool bool
ProxyHandler::defaultValue(JSContext *cx, JSObject *proxy, JSType hint, Value *vp) ProxyHandler::defaultValue(JSContext *cx, JSObject *proxy, JSType hint, Value *vp)
{ {
@ -946,6 +953,14 @@ Proxy::fun_toString(JSContext *cx, JSObject *proxy, uintN indent)
return GetProxyHandler(proxy)->fun_toString(cx, proxy, indent); return GetProxyHandler(proxy)->fun_toString(cx, proxy, indent);
} }
RegExpShared *
Proxy::regexp_toShared(JSContext *cx, JSObject *proxy)
{
JS_CHECK_RECURSION(cx, return NULL);
AutoPendingProxyOperation pending(cx, proxy);
return GetProxyHandler(proxy)->regexp_toShared(cx, proxy);
}
bool bool
Proxy::defaultValue(JSContext *cx, JSObject *proxy, JSType hint, Value *vp) Proxy::defaultValue(JSContext *cx, JSObject *proxy, JSType hint, Value *vp)
{ {

Просмотреть файл

@ -84,6 +84,7 @@ class JS_FRIEND_API(ProxyHandler) {
virtual bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx); virtual bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx);
virtual JSString *obj_toString(JSContext *cx, JSObject *proxy); virtual JSString *obj_toString(JSContext *cx, JSObject *proxy);
virtual JSString *fun_toString(JSContext *cx, JSObject *proxy, uintN indent); virtual JSString *fun_toString(JSContext *cx, JSObject *proxy, uintN indent);
virtual RegExpShared *regexp_toShared(JSContext *cx, JSObject *proxy);
virtual bool defaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp); virtual bool defaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp);
virtual void finalize(JSContext *cx, JSObject *proxy); virtual void finalize(JSContext *cx, JSObject *proxy);
virtual void trace(JSTracer *trc, JSObject *proxy); virtual void trace(JSTracer *trc, JSObject *proxy);
@ -137,6 +138,7 @@ class Proxy {
static bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx); static bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx);
static JSString *obj_toString(JSContext *cx, JSObject *proxy); static JSString *obj_toString(JSContext *cx, JSObject *proxy);
static JSString *fun_toString(JSContext *cx, JSObject *proxy, uintN indent); static JSString *fun_toString(JSContext *cx, JSObject *proxy, uintN indent);
static RegExpShared *regexp_toShared(JSContext *cx, JSObject *proxy);
static bool defaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp); static bool defaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp);
}; };

Просмотреть файл

@ -1400,11 +1400,11 @@ class RegExpGuard
bool bool
init(CallArgs args, bool convertVoid = false) init(CallArgs args, bool convertVoid = false)
{ {
if (args.length() != 0 && ValueIsRegExp(args[0])) { if (args.length() != 0 && IsObjectWithClass(args[0], ESClass_RegExp, cx)) {
RegExpObject &reobj = args[0].toObject().asRegExp(); RegExpShared *shared = RegExpToShared(cx, args[0].toObject());
RegExpShared *shared = reobj.getShared(cx);
if (!shared) if (!shared)
return false; return false;
matcher.init(NeedsIncRef<RegExpShared>(shared)); matcher.init(NeedsIncRef<RegExpShared>(shared));
} else { } else {
if (convertVoid && (args.length() == 0 || args[0].isUndefined())) { if (convertVoid && (args.length() == 0 || args[0].isUndefined())) {
@ -2547,9 +2547,8 @@ js::str_split(JSContext *cx, uintN argc, Value *vp)
JSLinearString *sepstr = NULL; JSLinearString *sepstr = NULL;
bool sepUndefined = (args.length() == 0 || args[0].isUndefined()); bool sepUndefined = (args.length() == 0 || args[0].isUndefined());
if (!sepUndefined) { if (!sepUndefined) {
if (ValueIsRegExp(args[0])) { if (IsObjectWithClass(args[0], ESClass_RegExp, cx)) {
RegExpObject &reobj = args[0].toObject().asRegExp(); RegExpShared *shared = RegExpToShared(cx, args[0].toObject());
RegExpShared *shared = reobj.getShared(cx);
if (!shared) if (!shared)
return false; return false;
matcher.init(NeedsIncRef<RegExpShared>(shared)); matcher.init(NeedsIncRef<RegExpShared>(shared));

Просмотреть файл

@ -54,10 +54,10 @@
#endif #endif
#include "jscompartment.h" #include "jscompartment.h"
#include "vm/RegExpObject.h"
#include "jsobjinlines.h" #include "jsobjinlines.h"
#include "vm/RegExpObject-inl.h"
using namespace js; using namespace js;
using namespace js::gc; using namespace js::gc;
@ -334,6 +334,12 @@ Wrapper::fun_toString(JSContext *cx, JSObject *wrapper, uintN indent)
return str; return str;
} }
RegExpShared *
Wrapper::regexp_toShared(JSContext *cx, JSObject *wrapper)
{
return wrappedObject(wrapper)->asRegExp().getShared(cx);
}
bool bool
Wrapper::defaultValue(JSContext *cx, JSObject *wrapper, JSType hint, Value *vp) Wrapper::defaultValue(JSContext *cx, JSObject *wrapper, JSType hint, Value *vp)
{ {
@ -881,5 +887,13 @@ SecurityWrapper<Base>::objectClassIs(JSObject *obj, ESClassValue classValue, JSC
return Base::objectClassIs(obj, classValue, cx); return Base::objectClassIs(obj, classValue, cx);
} }
template <class Base>
RegExpShared *
SecurityWrapper<Base>::regexp_toShared(JSContext *cx, JSObject *obj)
{
return Base::regexp_toShared(cx, obj);
}
template class js::SecurityWrapper<Wrapper>; template class js::SecurityWrapper<Wrapper>;
template class js::SecurityWrapper<CrossCompartmentWrapper>; template class js::SecurityWrapper<CrossCompartmentWrapper>;

Просмотреть файл

@ -94,6 +94,7 @@ class JS_FRIEND_API(Wrapper) : public ProxyHandler
virtual bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx) MOZ_OVERRIDE; virtual bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx) MOZ_OVERRIDE;
virtual JSString *obj_toString(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE; virtual JSString *obj_toString(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE;
virtual JSString *fun_toString(JSContext *cx, JSObject *wrapper, uintN indent) MOZ_OVERRIDE; virtual JSString *fun_toString(JSContext *cx, JSObject *wrapper, uintN indent) MOZ_OVERRIDE;
virtual RegExpShared *regexp_toShared(JSContext *cx, JSObject *proxy) MOZ_OVERRIDE;
virtual bool defaultValue(JSContext *cx, JSObject *wrapper, JSType hint, Value *vp) MOZ_OVERRIDE; virtual bool defaultValue(JSContext *cx, JSObject *wrapper, JSType hint, Value *vp) MOZ_OVERRIDE;
virtual void trace(JSTracer *trc, JSObject *wrapper) MOZ_OVERRIDE; virtual void trace(JSTracer *trc, JSObject *wrapper) MOZ_OVERRIDE;
@ -178,6 +179,7 @@ class JS_FRIEND_API(SecurityWrapper) : public Base
virtual bool nativeCall(JSContext *cx, JSObject *wrapper, Class *clasp, Native native, CallArgs args) MOZ_OVERRIDE; virtual bool nativeCall(JSContext *cx, JSObject *wrapper, Class *clasp, Native native, CallArgs args) MOZ_OVERRIDE;
virtual bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx) MOZ_OVERRIDE; virtual bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx) MOZ_OVERRIDE;
virtual RegExpShared *regexp_toShared(JSContext *cx, JSObject *proxy) MOZ_OVERRIDE;
}; };
typedef SecurityWrapper<Wrapper> SameCompartmentSecurityWrapper; typedef SecurityWrapper<Wrapper> SameCompartmentSecurityWrapper;

Просмотреть файл

@ -59,12 +59,6 @@ JSObject::asRegExp()
namespace js { namespace js {
inline bool
ValueIsRegExp(const Value &v)
{
return !v.isPrimitive() && v.toObject().isRegExp();
}
inline bool inline bool
IsRegExpMetaChar(jschar c) IsRegExpMetaChar(jschar c)
{ {
@ -506,6 +500,15 @@ RegExpShared::decref(JSContext *cx)
#endif #endif
} }
inline RegExpShared *
RegExpToShared(JSContext *cx, JSObject &obj)
{
JS_ASSERT(ObjectClassIs(obj, ESClass_RegExp, cx));
if (obj.isRegExp())
return obj.asRegExp().getShared(cx);
return Proxy::regexp_toShared(cx, &obj);
}
} /* namespace js */ } /* namespace js */
#endif #endif

Просмотреть файл

@ -137,18 +137,6 @@ RegExpObjectBuilder::build(JSLinearString *source, RegExpFlag flags)
return reobj_->init(cx, source, flags) ? reobj_ : NULL; return reobj_->init(cx, source, flags) ? reobj_ : NULL;
} }
RegExpObject *
RegExpObjectBuilder::build(RegExpObject *other)
{
RegExpShared *shared = other->getShared(cx);
if (!shared)
return NULL;
/* Now, incref it for the RegExpObject being built. */
shared->incref(cx);
return build(AlreadyIncRefed<RegExpShared>(shared));
}
RegExpObject * RegExpObject *
RegExpObjectBuilder::clone(RegExpObject *other, RegExpObject *proto) RegExpObjectBuilder::clone(RegExpObject *other, RegExpObject *proto)
{ {

Просмотреть файл

@ -207,8 +207,6 @@ class RegExpObjectBuilder
bool getOrCreate(); bool getOrCreate();
bool getOrCreateClone(RegExpObject *proto); bool getOrCreateClone(RegExpObject *proto);
RegExpObject *build(AlreadyIncRefed<RegExpShared> shared);
friend class RegExpMatcher; friend class RegExpMatcher;
public: public:
@ -219,7 +217,7 @@ class RegExpObjectBuilder
RegExpObject *reobj() { return reobj_; } RegExpObject *reobj() { return reobj_; }
RegExpObject *build(JSLinearString *str, RegExpFlag flags); RegExpObject *build(JSLinearString *str, RegExpFlag flags);
RegExpObject *build(RegExpObject *other); RegExpObject *build(AlreadyIncRefed<RegExpShared> shared);
/* Perform a VM-internal clone. */ /* Perform a VM-internal clone. */
RegExpObject *clone(RegExpObject *other, RegExpObject *proto); RegExpObject *clone(RegExpObject *other, RegExpObject *proto);
@ -469,9 +467,6 @@ class RegExpMatcher
bool bool
ParseRegExpFlags(JSContext *cx, JSString *flagStr, RegExpFlag *flagsOut); ParseRegExpFlags(JSContext *cx, JSString *flagStr, RegExpFlag *flagsOut);
inline bool
ValueIsRegExp(const Value &v);
inline bool inline bool
IsRegExpMetaChar(jschar c); IsRegExpMetaChar(jschar c);
@ -481,6 +476,9 @@ CheckRegExpSyntax(JSContext *cx, JSLinearString *str)
return detail::RegExpCode::checkSyntax(cx, NULL, str); return detail::RegExpCode::checkSyntax(cx, NULL, str);
} }
inline RegExpShared *
RegExpToShared(JSContext *cx, JSObject &obj);
} /* namespace js */ } /* namespace js */
extern JS_FRIEND_API(JSObject *) JS_FASTCALL extern JS_FRIEND_API(JSObject *) JS_FASTCALL