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];
if (ValueIsRegExp(sourceValue)) {
if (IsObjectWithClass(sourceValue, ESClass_RegExp, cx)) {
/*
* If we get passed in a |RegExpObject| source we return a new
* object with the same source/flags.
@ -243,9 +243,15 @@ CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder, CallArgs args)
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)
return false;
args.rval() = ObjectValue(*reobj);
return true;
}
@ -314,7 +320,7 @@ regexp_construct(JSContext *cx, uintN argc, Value *vp)
* Otherwise, delegate to the standard constructor.
* 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.rval() = args[0];

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

@ -983,7 +983,7 @@ static bool
EmitIndex32(JSContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce)
{
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);
if (offset < 0)
return false;

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

@ -27,7 +27,6 @@ function test(str, f) {
f(g1.eval("new Object()"));
} catch (e) {
assertEq(Object.prototype.toString.call(e), "[object Error]");
assertEq(e.name, "TypeError");
threw = true;
}
assertEq(threw, true);
@ -36,7 +35,6 @@ function test(str, f) {
f(g2.eval("new Object()"));
} catch (e) {
assertEq(Object.prototype.toString.call(e), "[object Error]");
assertEq(e.name, "TypeError");
threw = true;
}
assertEq(threw, true);
@ -45,7 +43,6 @@ function test(str, f) {
f(proxy1);
} catch (e) {
assertEq(Object.prototype.toString.call(e), "[object Error]");
assertEq(e.name, "TypeError");
threw = true;
}
assertEq(threw, true);
@ -54,7 +51,6 @@ function test(str, f) {
f(proxy2);
} catch (e) {
assertEq(Object.prototype.toString.call(e), "[object Error]");
assertEq(e.name, "TypeError");
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.test.call(r, '1'));
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.get.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)
{
CallArgs args = CallArgsFromVp(argc, vp);
bool isArray = args.length() > 0 &&
args[0].isObject() &&
ObjectClassIs(args[0].toObject(), ESClass_Array, cx);
bool isArray = args.length() > 0 && IsObjectWithClass(args[0], ESClass_Array, cx);
args.rval().setBoolean(isArray);
return true;
}

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

@ -407,7 +407,9 @@ Valueify(const JSClass *c)
* Enumeration describing possible values of the [[Class]] internal property
* 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
@ -418,6 +420,10 @@ enum ESClassValue { ESClass_Array, ESClass_Number, ESClass_String, ESClass_Boole
inline bool
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 */
#endif /* __cplusplus */

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

@ -1953,11 +1953,20 @@ ObjectClassIs(JSObject &obj, ESClassValue classValue, JSContext *cx)
case ESClass_Number: return obj.isNumber();
case ESClass_String: return obj.isString();
case ESClass_Boolean: return obj.isBoolean();
case ESClass_RegExp: return obj.isRegExp();
}
JS_NOT_REACHED("bad classValue");
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
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);
}
RegExpShared *
ProxyHandler::regexp_toShared(JSContext *cx, JSObject *proxy)
{
JS_NOT_REACHED("This should have been a wrapped regexp");
return (RegExpShared *)NULL;
}
bool
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);
}
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
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 JSString *obj_toString(JSContext *cx, JSObject *proxy);
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 void finalize(JSContext *cx, 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 JSString *obj_toString(JSContext *cx, JSObject *proxy);
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);
};

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

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

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

@ -54,10 +54,10 @@
#endif
#include "jscompartment.h"
#include "vm/RegExpObject.h"
#include "jsobjinlines.h"
#include "vm/RegExpObject-inl.h"
using namespace js;
using namespace js::gc;
@ -334,6 +334,12 @@ Wrapper::fun_toString(JSContext *cx, JSObject *wrapper, uintN indent)
return str;
}
RegExpShared *
Wrapper::regexp_toShared(JSContext *cx, JSObject *wrapper)
{
return wrappedObject(wrapper)->asRegExp().getShared(cx);
}
bool
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);
}
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<CrossCompartmentWrapper>;

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

@ -94,6 +94,7 @@ class JS_FRIEND_API(Wrapper) : public ProxyHandler
virtual bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx) 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 RegExpShared *regexp_toShared(JSContext *cx, JSObject *proxy) MOZ_OVERRIDE;
virtual bool defaultValue(JSContext *cx, JSObject *wrapper, JSType hint, Value *vp) 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 objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx) MOZ_OVERRIDE;
virtual RegExpShared *regexp_toShared(JSContext *cx, JSObject *proxy) MOZ_OVERRIDE;
};
typedef SecurityWrapper<Wrapper> SameCompartmentSecurityWrapper;

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

@ -59,12 +59,6 @@ JSObject::asRegExp()
namespace js {
inline bool
ValueIsRegExp(const Value &v)
{
return !v.isPrimitive() && v.toObject().isRegExp();
}
inline bool
IsRegExpMetaChar(jschar c)
{
@ -506,6 +500,15 @@ RegExpShared::decref(JSContext *cx)
#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 */
#endif

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

@ -137,18 +137,6 @@ RegExpObjectBuilder::build(JSLinearString *source, RegExpFlag flags)
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 *
RegExpObjectBuilder::clone(RegExpObject *other, RegExpObject *proto)
{

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

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