Issue #2083 - Part 2: Remove use of RegExpGuard.

Based on Mozilla bug 1345177.
There were more uses of RegExpGuard in js/src/builtin/RegExp.cpp not found in
 the original bug's patch.
This commit is contained in:
Job Bautista 2023-01-26 15:13:53 +08:00 committed by roytam1
commit d163d08cb2
21 changed files with 92 additions and 73 deletions

View file

@ -128,7 +128,8 @@ class CPOWProxyHandler : public BaseProxyHandler
virtual bool isArray(JSContext* cx, HandleObject obj,
IsArrayAnswer* answer) const override;
virtual const char* className(JSContext* cx, HandleObject proxy) const override;
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
MutableHandle<RegExpShared*> shared) const override;
virtual void finalize(JSFreeOp* fop, JSObject* proxy) const override;
virtual void objectMoved(JSObject* proxy, const JSObject* old) const override;
virtual bool isCallable(JSObject* obj) const override;
@ -854,13 +855,14 @@ WrapperOwner::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* is
}
bool
CPOWProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const
CPOWProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy,
MutableHandle<RegExpShared*> shared) const
{
FORWARD(regexp_toShared, (cx, proxy, g));
FORWARD(regexp_toShared, (cx, proxy, shared));
}
bool
WrapperOwner::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g)
WrapperOwner::regexp_toShared(JSContext* cx, HandleObject proxy, MutableHandle<RegExpShared*> shared)
{
ObjectId objId = idOf(proxy);
@ -880,7 +882,7 @@ WrapperOwner::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g)
if (!regexp)
return false;
return js::RegExpToSharedNonInline(cx, regexp, g);
return js::RegExpToSharedNonInline(cx, regexp, shared);
}
void

View file

@ -59,7 +59,8 @@ class WrapperOwner : public virtual JavaScriptShared
bool getPrototypeIfOrdinary(JSContext* cx, JS::HandleObject proxy, bool* isOrdinary,
JS::MutableHandleObject protop);
bool regexp_toShared(JSContext* cx, JS::HandleObject proxy, js::RegExpGuard* g);
bool regexp_toShared(JSContext* cx, JS::HandleObject proxy,
js::MutableHandle<js::RegExpShared*> shared);
nsresult instanceOf(JSObject* obj, const nsID* id, bool* bp);

View file

@ -31,7 +31,7 @@ using JS::PrivateValue;
using JS::PropertyDescriptor;
using JS::Value;
using RegExpGuard = JS::Rooted<RegExpShared*>;
class RegExpShared;
class JS_FRIEND_API(Wrapper);
@ -328,7 +328,8 @@ class JS_FRIEND_API(BaseProxyHandler)
virtual bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const;
virtual const char* className(JSContext* cx, HandleObject proxy) const;
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, bool isToSource) const;
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const;
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
MutableHandle<js::RegExpShared*> shared) const;
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const;
virtual void trace(JSTracer* trc, JSObject* proxy) const;
virtual void finalize(JSFreeOp* fop, JSObject* proxy) const;

View file

@ -187,7 +187,7 @@ js::ExecuteRegExpLegacy(JSContext* cx, RegExpStatics* res, Handle<RegExpObject*>
HandleLinearString input, size_t* lastIndex, bool test,
MutableHandleValue rval)
{
RegExpGuard shared(cx);
RootedRegExpShared shared(cx);
if (!RegExpObject::getShared(cx, reobj, &shared))
return false;
@ -275,7 +275,7 @@ RegExpInitializeIgnoringLastIndex(JSContext* cx, Handle<RegExpObject*> obj,
if (sharedUse == UseRegExpShared) {
/* Steps 7-8. */
RegExpGuard re(cx);
RootedRegExpShared re(cx);
if (!cx->compartment()->regExps.get(cx, pattern, flags, &re))
return false;
@ -381,7 +381,7 @@ regexp_compile_impl(JSContext* cx, const CallArgs& args)
RegExpFlag flags;
{
// Step 3b.
RegExpGuard g(cx);
RootedRegExpShared g(cx);
if (!RegExpToShared(cx, patternObj, &g))
return false;
@ -476,7 +476,7 @@ js::regexp_construct(JSContext* cx, unsigned argc, Value* vp)
RegExpFlag flags;
{
// Step 4.a.
RegExpGuard g(cx);
RootedRegExpShared g(cx);
if (!RegExpToShared(cx, patternObj, &g))
return false;
sourceAtom = g->getSource();
@ -603,7 +603,7 @@ js::regexp_clone(JSContext* cx, unsigned argc, Value* vp)
RootedAtom sourceAtom(cx);
RegExpFlag flags;
{
RegExpGuard g(cx);
RootedRegExpShared g(cx);
if (!RegExpToShared(cx, from, &g))
return false;
sourceAtom = g->getSource();
@ -985,7 +985,7 @@ ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
/* Steps 1-2 performed by the caller. */
Rooted<RegExpObject*> reobj(cx, &regexp->as<RegExpObject>());
RegExpGuard re(cx);
RootedRegExpShared re(cx);
if (!RegExpObject::getShared(cx, reobj, &re))
return RegExpRunStatus_Error;
@ -1070,7 +1070,7 @@ RegExpMatcherImpl(JSContext* cx, HandleObject regexp, HandleString string,
/* Steps 16-25 */
Rooted<RegExpObject*> reobj(cx, &regexp->as<RegExpObject>());
RegExpGuard shared(cx);
RootedRegExpShared shared(cx);
if (!RegExpObject::getShared(cx, reobj, &shared))
return false;
return CreateRegExpMatchResult(cx, *shared, string, matches, rval);
@ -1117,7 +1117,7 @@ js::RegExpMatcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
// successful only if the pairs have actually been filled in.
if (maybeMatches && maybeMatches->pairsRaw()[0] >= 0) {
Rooted<RegExpObject*> reobj(cx, &regexp->as<RegExpObject>());
RegExpGuard shared(cx);
RootedRegExpShared shared(cx);
if (!RegExpObject::getShared(cx, reobj, &shared))
return false;
return CreateRegExpMatchResult(cx, *shared, input, *maybeMatches, output);

View file

@ -6141,7 +6141,7 @@ JS_GetRegExpFlags(JSContext* cx, HandleObject obj)
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
RegExpGuard shared(cx);
RootedRegExpShared shared(cx);
if (!RegExpToShared(cx, obj, &shared))
return false;
return shared->getFlags();
@ -6153,7 +6153,7 @@ JS_GetRegExpSource(JSContext* cx, HandleObject obj)
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
RegExpGuard shared(cx);
RootedRegExpShared shared(cx);
if (!RegExpToShared(cx, obj, &shared))
return nullptr;
return shared->getSource();

View file

@ -1114,9 +1114,9 @@ extern JS_FRIEND_API(unsigned)
GetEnterCompartmentDepth(JSContext* cx);
#endif
using RegExpGuard = JS::Rooted<RegExpShared*>;
extern JS_FRIEND_API(bool)
RegExpToSharedNonInline(JSContext* cx, JS::HandleObject regexp, RegExpGuard* shared);
RegExpToSharedNonInline(JSContext* cx, JS::HandleObject regexp,
JS::MutableHandle<RegExpShared*> shared);
/* Implemented in jswrapper.cpp. */
typedef enum NukeReferencesToWindow {

View file

@ -117,7 +117,7 @@ class JS_FRIEND_API(Wrapper) : public BaseProxyHandler
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy,
bool isToSource) const override;
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
RegExpGuard* g) const override;
MutableHandle<RegExpShared*> shared) const override;
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy,
MutableHandleValue vp) const override;
virtual bool isCallable(JSObject* obj) const override;
@ -211,7 +211,8 @@ class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper
virtual const char* className(JSContext* cx, HandleObject proxy) const override;
virtual JSString* fun_toString(JSContext* cx, HandleObject wrapper,
bool isToSource) const override;
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
MutableHandle<RegExpShared*> shared) const override;
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
// Allocate CrossCompartmentWrappers in the nursery.
@ -312,7 +313,8 @@ class JS_FRIEND_API(SecurityWrapper) : public Base
const CallArgs& args) const override;
virtual bool getBuiltinClass(JSContext* cx, HandleObject wrapper, ESClass* cls) const override;
virtual bool isArray(JSContext* cx, HandleObject wrapper, JS::IsArrayAnswer* answer) const override;
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
MutableHandle<RegExpShared*> shared) const override;
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
/*

View file

@ -327,7 +327,7 @@ BaseProxyHandler::fun_toString(JSContext* cx, HandleObject proxy, bool isToSourc
bool
BaseProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy,
RegExpGuard* g) const
MutableHandleRegExpShared shared) const
{
MOZ_CRASH("This should have been a wrapped regexp");
}

View file

@ -437,9 +437,10 @@ CrossCompartmentWrapper::fun_toString(JSContext* cx, HandleObject wrapper, bool
}
bool
CrossCompartmentWrapper::regexp_toShared(JSContext* cx, HandleObject wrapper, RegExpGuard* g) const
CrossCompartmentWrapper::regexp_toShared(JSContext* cx, HandleObject wrapper,
MutableHandleRegExpShared shared) const
{
RegExpGuard re(cx);
RootedRegExpShared re(cx);
{
AutoCompartment call(cx, wrappedObject(wrapper));
if (!Wrapper::regexp_toShared(cx, wrapper, &re))
@ -447,7 +448,7 @@ CrossCompartmentWrapper::regexp_toShared(JSContext* cx, HandleObject wrapper, Re
}
// Get an equivalent RegExpShared associated with the current compartment.
return cx->compartment()->regExps.get(cx, re->getSource(), re->getFlags(), g);
return cx->compartment()->regExps.get(cx, re->getSource(), re->getFlags(), shared);
}
bool

View file

@ -142,7 +142,8 @@ DeadObjectProxy::fun_toString(JSContext* cx, HandleObject proxy, bool isToSource
}
bool
DeadObjectProxy::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const
DeadObjectProxy::regexp_toShared(JSContext* cx, HandleObject proxy,
MutableHandle<RegExpShared*> shared) const
{
ReportDead(cx);
return false;

View file

@ -49,7 +49,8 @@ class DeadObjectProxy : public BaseProxyHandler
virtual const char* className(JSContext* cx, HandleObject proxy) const override;
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy,
bool isToSource) const override;
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
MutableHandle<RegExpShared*> shared) const override;
virtual bool isCallable(JSObject* obj) const override;
virtual bool isConstructor(JSObject* obj) const override;

View file

@ -488,10 +488,10 @@ Proxy::fun_toString(JSContext* cx, HandleObject proxy, bool isToSource)
}
bool
Proxy::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g)
Proxy::regexp_toShared(JSContext* cx, HandleObject proxy, MutableHandleRegExpShared shared)
{
JS_CHECK_RECURSION(cx, return false);
return proxy->as<ProxyObject>().handler()->regexp_toShared(cx, proxy, g);
return proxy->as<ProxyObject>().handler()->regexp_toShared(cx, proxy, shared);
}
bool

View file

@ -59,7 +59,8 @@ class Proxy
static bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer);
static const char* className(JSContext* cx, HandleObject proxy);
static JSString* fun_toString(JSContext* cx, HandleObject proxy, bool isToSource);
static bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g);
static bool regexp_toShared(JSContext* cx, HandleObject proxy,
MutableHandle<RegExpShared*> shared);
static bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp);
static bool getElements(JSContext* cx, HandleObject obj, uint32_t begin, uint32_t end,

View file

@ -1265,7 +1265,8 @@ ScriptedProxyHandler::fun_toString(JSContext* cx, HandleObject proxy, bool isToS
}
bool
ScriptedProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const
ScriptedProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy,
MutableHandleRegExpShared shared) const
{
MOZ_CRASH("Should not end up in ScriptedProxyHandler::regexp_toShared");
return false;

View file

@ -69,7 +69,7 @@ class ScriptedProxyHandler : public BaseProxyHandler
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy,
bool isToSource) const override;
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
RegExpGuard* g) const override;
MutableHandle<RegExpShared*> shared) const override;
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy,
MutableHandleValue vp) const override;

View file

@ -93,9 +93,10 @@ SecurityWrapper<Base>::isArray(JSContext* cx, HandleObject obj, JS::IsArrayAnswe
template <class Base>
bool
SecurityWrapper<Base>::regexp_toShared(JSContext* cx, HandleObject obj, RegExpGuard* g) const
SecurityWrapper<Base>::regexp_toShared(JSContext* cx, HandleObject obj,
MutableHandle<RegExpShared*> shared) const
{
return Base::regexp_toShared(cx, obj, g);
return Base::regexp_toShared(cx, obj, shared);
}
template <class Base>

View file

@ -267,10 +267,10 @@ Wrapper::fun_toString(JSContext* cx, HandleObject proxy, bool isToSource) const
}
bool
Wrapper::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const
Wrapper::regexp_toShared(JSContext* cx, HandleObject proxy, MutableHandleRegExpShared shared) const
{
RootedObject target(cx, proxy->as<ProxyObject>().target());
return RegExpToShared(cx, target, g);
return RegExpToShared(cx, target, shared);
}
bool

View file

@ -122,14 +122,15 @@ VectorMatchPairs::allocOrExpandArray(size_t pairCount)
/* RegExpObject */
/* static */ bool
RegExpObject::getShared(JSContext* cx, Handle<RegExpObject*> regexp, RegExpGuard* g)
RegExpObject::getShared(JSContext* cx, Handle<RegExpObject*> regexp,
MutableHandleRegExpShared shared)
{
if (regexp->hasShared()) {
g->set(regexp->sharedRef());
shared.set(regexp->sharedRef());
return true;
}
return createShared(cx, regexp, g);
return createShared(cx, regexp, shared);
}
/* static */ bool
@ -278,13 +279,14 @@ RegExpObject::create(ExclusiveContext* cx, HandleAtom source, RegExpFlag flags,
}
/* static */ bool
RegExpObject::createShared(JSContext* cx, Handle<RegExpObject*> regexp, RegExpGuard* g)
RegExpObject::createShared(JSContext* cx, Handle<RegExpObject*> regexp,
MutableHandleRegExpShared shared)
{
MOZ_ASSERT(!regexp->hasShared());
if (!cx->compartment()->regExps.get(cx, regexp->getSource(), regexp->getFlags(), g))
if (!cx->compartment()->regExps.get(cx, regexp->getSource(), regexp->getFlags(), shared))
return false;
regexp->setShared(**g);
regexp->setShared(*shared);
return true;
}
@ -891,11 +893,11 @@ RegExpShared::dumpBytecode(JSContext* cx, bool match_only, HandleLinearString in
RegExpObject::dumpBytecode(JSContext* cx, Handle<RegExpObject*> regexp,
bool match_only, HandleLinearString input)
{
RegExpGuard g(cx);
if (!getShared(cx, regexp, &g))
RootedRegExpShared shared(cx);
if (!getShared(cx, regexp, &shared))
return false;
return g->dumpBytecode(cx, match_only, input);
return shared->dumpBytecode(cx, match_only, input);
}
#endif
@ -1351,11 +1353,12 @@ RegExpCompartment::sweep(JSRuntime* rt)
}
bool
RegExpCompartment::get(JSContext* cx, JSAtom* source, RegExpFlag flags, RegExpGuard* g)
RegExpCompartment::get(JSContext* cx, JSAtom* source, RegExpFlag flags,
MutableHandleRegExpShared result)
{
DependentAddPtr<Set> p(cx, set_.get(), Key(source, flags));
if (p) {
g->set(*p);
result.set(*p);
return true;
}
@ -1370,18 +1373,19 @@ RegExpCompartment::get(JSContext* cx, JSAtom* source, RegExpFlag flags, RegExpGu
return false;
}
g->set(shared);
result.set(shared);
return true;
}
bool
RegExpCompartment::get(JSContext* cx, HandleAtom atom, JSString* opt, RegExpGuard* g)
RegExpCompartment::get(JSContext* cx, HandleAtom atom, JSString* opt,
MutableHandleRegExpShared shared)
{
RegExpFlag flags = RegExpFlag(0);
if (opt && !ParseRegExpFlags(cx, opt, &flags))
return false;
return get(cx, atom, flags, g);
return get(cx, atom, flags, shared);
}
size_t
@ -1410,12 +1414,12 @@ js::CloneRegExpObject(JSContext* cx, JSObject* obj_)
Rooted<JSAtom*> source(cx, regex->getSource());
RegExpGuard g(cx);
if (!RegExpObject::getShared(cx, regex, &g))
RootedRegExpShared shared(cx);
if (!RegExpObject::getShared(cx, regex, &shared))
return nullptr;
clone->initAndZeroLastIndex(source, g->getFlags(), cx);
clone->setShared(*g);
clone->initAndZeroLastIndex(source, shared->getFlags(), cx);
clone->setShared(*shared);
return clone;
}
@ -1554,9 +1558,9 @@ js::CloneScriptRegExpObject(JSContext* cx, RegExpObject& reobj)
}
JS_FRIEND_API(bool)
js::RegExpToSharedNonInline(JSContext* cx, HandleObject obj, js::RegExpGuard* g)
js::RegExpToSharedNonInline(JSContext* cx, HandleObject obj, MutableHandleRegExpShared shared)
{
return RegExpToShared(cx, obj, g);
return RegExpToShared(cx, obj, shared);
}
JS::ubi::Node::Size

View file

@ -32,10 +32,8 @@
*
* To save memory, a RegExpShared is not created for a RegExpObject until it is
* needed for execution. When a RegExpShared needs to be created, it is looked
* up in a per-compartment table to allow reuse between objects. Lastly, on
* GC, every RegExpShared (that is not active on the callstack) is discarded.
* Because of the last point, any code using a RegExpShared (viz., by executing
* a regexp) must indicate the RegExpShared is active via RegExpGuard.
* up in a per-compartment table to allow reuse between objects. Lastly, on GC,
* every RegExpShared that is not in active use is discarded.
*/
namespace js {
@ -251,6 +249,10 @@ class RegExpShared : public gc::TenuredCell
#endif
};
using RootedRegExpShared = JS::Rooted<RegExpShared*>;
using HandleRegExpShared = JS::Handle<RegExpShared*>;
using MutableHandleRegExpShared = JS::MutableHandle<RegExpShared*>;
class RegExpCompartment
{
struct Key {
@ -321,10 +323,11 @@ class RegExpCompartment
bool empty() { return set_.empty(); }
bool get(JSContext* cx, JSAtom* source, RegExpFlag flags, RegExpGuard* g);
bool get(JSContext* cx, JSAtom* source, RegExpFlag flags, MutableHandleRegExpShared shared);
/* Like 'get', but compile 'maybeOpt' (if non-null). */
bool get(JSContext* cx, HandleAtom source, JSString* maybeOpt, RegExpGuard* g);
bool get(JSContext* cx, HandleAtom source, JSString* maybeOpt,
MutableHandleRegExpShared shared);
/* Get or create template object used to base the result of .exec() on. */
ArrayObject* getOrCreateMatchResultTemplateObject(JSContext* cx) {
@ -447,7 +450,7 @@ class RegExpObject : public NativeObject
static bool isOriginalFlagGetter(JSNative native, RegExpFlag* mask);
static MOZ_MUST_USE bool getShared(JSContext* cx, Handle<RegExpObject*> regexp,
RegExpGuard* g);
MutableHandleRegExpShared shared);
bool hasShared() {
return !!sharedRef();
@ -479,7 +482,7 @@ class RegExpObject : public NativeObject
* Side effect: sets the private field.
*/
static MOZ_MUST_USE bool createShared(JSContext* cx, Handle<RegExpObject*> regexp,
RegExpGuard* g);
MutableHandleRegExpShared shared);
ReadBarriered<RegExpShared*>& sharedRef() {
auto& ref = NativeObject::privateRef(PRIVATE_SLOT);
@ -501,12 +504,12 @@ ParseRegExpFlags(JSContext* cx, JSString* flagStr, RegExpFlag* flagsOut);
/* Assuming GetBuiltinClass(obj) is ESClass::RegExp, return a RegExpShared for obj. */
inline bool
RegExpToShared(JSContext* cx, HandleObject obj, RegExpGuard* g)
RegExpToShared(JSContext* cx, HandleObject obj, MutableHandleRegExpShared shared)
{
if (obj->is<RegExpObject>())
return RegExpObject::getShared(cx, obj.as<RegExpObject>(), g);
return RegExpObject::getShared(cx, obj.as<RegExpObject>(), shared);
return Proxy::regexp_toShared(cx, obj, g);
return Proxy::regexp_toShared(cx, obj, shared);
}
template<XDRMode mode>

View file

@ -80,8 +80,8 @@ RegExpStatics::executeLazy(JSContext* cx)
MOZ_ASSERT(lazyIndex != size_t(-1));
/* Retrieve or create the RegExpShared in this compartment. */
RegExpGuard g(cx);
if (!cx->compartment()->regExps.get(cx, lazySource, lazyFlags, &g))
RootedRegExpShared shared(cx);
if (!cx->compartment()->regExps.get(cx, lazySource, lazyFlags, &shared))
return false;
/*
@ -91,7 +91,7 @@ RegExpStatics::executeLazy(JSContext* cx)
/* Execute the full regular expression. */
RootedLinearString input(cx, matchesInput);
RegExpRunStatus status = g->execute(cx, input, lazyIndex, &this->matches, nullptr);
RegExpRunStatus status = shared->execute(cx, input, lazyIndex, &this->matches, nullptr);
if (status == RegExpRunStatus_Error)
return false;

View file

@ -1402,7 +1402,7 @@ JSStructuredCloneWriter::startWrite(HandleValue v)
return false;
if (cls == ESClass::RegExp) {
RegExpGuard re(context());
RootedRegExpShared re(context());
if (!RegExpToShared(context(), obj, &re))
return false;
return out.writePair(SCTAG_REGEXP_OBJECT, re->getFlags()) &&