Bug 792220 - Remove lookupProperty to prevent interpreter reentrance. r=jandem

This commit is contained in:
Nicolas B. Pierron 2012-10-03 15:13:40 -07:00
Родитель 4842d8e772
Коммит 2d50daf191
14 изменённых файлов: 178 добавлений и 68 удалений

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

@ -561,17 +561,12 @@ GetSharedForGreedyStar(JSContext *cx, JSAtom *source, RegExpFlag flags, RegExpGu
return cx->compartment->regExps.getHack(cx, source, hackedSource, flags, g);
}
/*
* ES5 15.10.6.2 (and 15.10.6.3, which calls 15.10.6.2).
*
* RegExp.prototype.test doesn't need to create a results array, and we use
* |execType| to perform this optimization.
*/
static bool
ExecuteRegExp(JSContext *cx, RegExpExecType execType, CallArgs args)
bool
js::ExecuteRegExp(JSContext *cx, RegExpExecType execType, HandleObject regexp,
HandleString string, MutableHandleValue rval)
{
/* Step 1 was performed by CallNonGenericMethod. */
Rooted<RegExpObject*> reobj(cx, &args.thisv().toObject().asRegExp());
/* Step 1 (b) was performed by CallNonGenericMethod. */
Rooted<RegExpObject*> reobj(cx, &regexp->asRegExp());
RegExpGuard re;
if (StartsWithGreedyStar(reobj->getSource())) {
@ -584,13 +579,8 @@ ExecuteRegExp(JSContext *cx, RegExpExecType execType, CallArgs args)
RegExpStatics *res = cx->regExpStatics();
/* Step 2. */
JSString *input = ToString(cx, (args.length() > 0) ? args[0] : UndefinedValue());
if (!input)
return false;
/* Step 3. */
Rooted<JSLinearString*> linearInput(cx, input->ensureLinear(cx));
Rooted<JSLinearString*> linearInput(cx, string->ensureLinear(cx));
if (!linearInput)
return false;
@ -612,20 +602,20 @@ ExecuteRegExp(JSContext *cx, RegExpExecType execType, CallArgs args)
/* Step 9a. */
if (i < 0 || i > length) {
reobj->zeroLastIndex();
args.rval().setNull();
rval.setNull();
return true;
}
/* Steps 8-21. */
size_t lastIndexInt(i);
if (!ExecuteRegExp(cx, res, *re, linearInput, chars, length, &lastIndexInt, execType,
args.rval().address())) {
if (!ExecuteRegExp(cx, res, *re, linearInput.get(), chars, length, &lastIndexInt, execType,
rval.address())) {
return false;
}
/* Step 11 (with sticky extension). */
if (re->global() || (!args.rval().isNull() && re->sticky())) {
if (args.rval().isNull())
if (re->global() || (!rval.isNull() && re->sticky())) {
if (rval.isNull())
reobj->zeroLastIndex();
else
reobj->setLastIndex(lastIndexInt);
@ -634,6 +624,26 @@ ExecuteRegExp(JSContext *cx, RegExpExecType execType, CallArgs args)
return true;
}
/*
* ES5 15.10.6.2 (and 15.10.6.3, which calls 15.10.6.2).
*
* RegExp.prototype.test doesn't need to create a results array, and we use
* |execType| to perform this optimization.
*/
static bool
ExecuteRegExp(JSContext *cx, RegExpExecType execType, CallArgs args)
{
/* Step 1 (a) was performed by CallNonGenericMethod. */
RootedObject regexp(cx, &args.thisv().toObject());
/* Step 2. */
RootedString string(cx, ToString(cx, (args.length() > 0) ? args[0] : UndefinedValue()));
if (!string)
return false;
return ExecuteRegExp(cx, execType, regexp, string, args.rval());
}
/* ES5 15.10.6.2. */
static bool
regexp_exec_impl(JSContext *cx, CallArgs args)

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

@ -35,6 +35,10 @@ ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpShared &shared,
JSLinearString *input, const jschar *chars, size_t length,
size_t *lastIndex, RegExpExecType type, Value *rval);
bool
ExecuteRegExp(JSContext *cx, RegExpExecType execType, HandleObject regexp,
HandleString string, MutableHandleValue rval);
extern JSBool
regexp_exec(JSContext *cx, unsigned argc, Value *vp);

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

@ -238,6 +238,30 @@ CodeGenerator::visitRegExp(LRegExp *lir)
return callVM(CloneRegExpObjectInfo, lir);
}
bool
CodeGenerator::visitRegExpTest(LRegExpTest *lir)
{
typedef bool (*pf)(JSContext *cx, RegExpExecType type, HandleObject regexp,
HandleString string, MutableHandleValue rval);
static const VMFunction ExecuteRegExpInfo = FunctionInfo<pf>(ExecuteRegExp);
pushArg(ToRegister(lir->string()));
pushArg(ToRegister(lir->regexp()));
pushArg(Imm32(RegExpTest));
if (!callVM(ExecuteRegExpInfo, lir))
return false;
Register output = ToRegister(lir->output());
Label notBool, end;
masm.branchTestBoolean(Assembler::NotEqual, JSReturnOperand, &notBool);
masm.unboxBoolean(JSReturnOperand, output);
masm.jump(&end);
masm.bind(&notBool);
masm.mov(Imm32(0), output);
masm.bind(&end);
return true;
}
bool
CodeGenerator::visitLambdaForSingleton(LLambdaForSingleton *lir)
{

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

@ -62,6 +62,7 @@ class CodeGenerator : public CodeGeneratorSpecific
bool visitIntToString(LIntToString *lir);
bool visitInteger(LInteger *lir);
bool visitRegExp(LRegExp *lir);
bool visitRegExpTest(LRegExpTest *lir);
bool visitLambda(LLambda *lir);
bool visitLambdaForSingleton(LLambdaForSingleton *lir);
bool visitPointer(LPointer *lir);

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

@ -3686,41 +3686,6 @@ IonBuilder::jsop_funapply(uint32 argc)
return pushTypeBarrier(apply, types, barrier);
}
// Get the builtin RegExp.prototype.test function.
static bool
GetBuiltinRegExpTest(JSContext *cx, JSScript *script, JSFunction **result)
{
JS_ASSERT(*result == NULL);
// Get the builtin RegExp.prototype object.
RootedObject proto(cx, script->global().getOrCreateRegExpPrototype(cx));
if (!proto)
return false;
// Get the |test| property. Note that we use lookupProperty, not getProperty,
// to avoid calling a getter.
RootedShape shape(cx);
RootedObject holder(cx);
if (!JSObject::lookupProperty(cx, proto, cx->names().test, &holder, &shape))
return false;
if (proto != holder || !shape || !shape->hasDefaultGetter() || !shape->hasSlot())
return true;
// The RegExp.prototype.test property is writable, so we have to ensure
// we got the builtin function.
Value val = holder->getSlot(shape->slot());
if (!val.isObject())
return true;
JSObject *obj = &val.toObject();
if (!obj->isFunction() || obj->toFunction()->maybeNative() != regexp_test)
return true;
*result = obj->toFunction();
return true;
}
bool
IonBuilder::jsop_call(uint32 argc, bool constructing)
{
@ -3750,20 +3715,9 @@ IonBuilder::jsop_call(uint32 argc, bool constructing)
}
RootedFunction target(cx, NULL);
if (numTargets == 1) {
if (numTargets == 1)
target = targets[0]->toFunction();
// Call RegExp.test instead of RegExp.exec if the result will not be used
// or will only be used to test for existence.
if (target->maybeNative() == regexp_exec && !CallResultEscapes(pc)) {
JSFunction *newTarget = NULL;
if (!GetBuiltinRegExpTest(cx, script_, &newTarget))
return false;
if (newTarget)
target = newTarget;
}
}
return makeCallBarrier(target, argc, constructing, types, barrier);
}

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

@ -387,6 +387,9 @@ class IonBuilder : public MIRGenerator
InliningStatus inlineStrFromCharCode(uint32 argc, bool constructing);
InliningStatus inlineStrCharAt(uint32 argc, bool constructing);
// RegExp natives.
InliningStatus inlineRegExpTest(uint32 argc, bool constructing);
InliningStatus inlineNativeCall(JSNative native, uint32 argc, bool constructing);
bool jsop_call_inline(HandleFunction callee, uint32 argc, bool constructing,

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

@ -1726,6 +1726,29 @@ class LRegExp : public LCallInstructionHelper<1, 0, 0>
}
};
class LRegExpTest : public LCallInstructionHelper<1, 2, 0>
{
public:
LIR_HEADER(RegExpTest);
LRegExpTest(const LAllocation &regexp, const LAllocation &string)
{
setOperand(0, regexp);
setOperand(1, string);
}
const LAllocation *regexp() {
return getOperand(0);
}
const LAllocation *string() {
return getOperand(1);
}
const MRegExpTest *mir() const {
return mir_->toRegExpTest();
}
};
class LLambdaForSingleton : public LCallInstructionHelper<1, 1, 0>
{
public:

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

@ -94,6 +94,7 @@
_(OsrValue) \
_(OsrScopeChain) \
_(RegExp) \
_(RegExpTest) \
_(Lambda) \
_(LambdaForSingleton) \
_(ImplicitThis) \

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

@ -1112,6 +1112,17 @@ LIRGenerator::visitRegExp(MRegExp *ins)
return defineVMReturn(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitRegExpTest(MRegExpTest *ins)
{
JS_ASSERT(ins->regexp()->type() == MIRType_Object);
JS_ASSERT(ins->string()->type() == MIRType_String);
LRegExpTest *lir = new LRegExpTest(useRegisterAtStart(ins->regexp()),
useRegisterAtStart(ins->string()));
return defineVMReturn(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitLambda(MLambda *ins)
{

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

@ -127,6 +127,7 @@ class LIRGenerator : public LIRGeneratorSpecific
bool visitTruncateToInt32(MTruncateToInt32 *truncate);
bool visitToString(MToString *convert);
bool visitRegExp(MRegExp *ins);
bool visitRegExpTest(MRegExpTest *ins);
bool visitLambda(MLambda *ins);
bool visitImplicitThis(MImplicitThis *ins);
bool visitSlots(MSlots *ins);

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

@ -64,6 +64,12 @@ IonBuilder::inlineNativeCall(JSNative native, uint32 argc, bool constructing)
if (native == js_str_charAt)
return inlineStrCharAt(argc, constructing);
// RegExp natives.
if (native == regexp_exec && !CallResultEscapes(pc))
return inlineRegExpTest(argc, constructing);
if (native == regexp_test)
return inlineRegExpTest(argc, constructing);
return InliningStatus_NotInlined;
}
@ -680,5 +686,33 @@ IonBuilder::inlineStrCharAt(uint32 argc, bool constructing)
return InliningStatus_Inlined;
}
IonBuilder::InliningStatus
IonBuilder::inlineRegExpTest(uint32 argc, bool constructing)
{
if (argc != 1 || constructing)
return InliningStatus_NotInlined;
// TI can infer a NULL return type of regexp_test with eager compilation.
if (CallResultEscapes(pc) && getInlineReturnType() != MIRType_Boolean)
return InliningStatus_NotInlined;
if (getInlineArgType(argc, 0) != MIRType_Object)
return InliningStatus_NotInlined;
if (getInlineArgType(argc, 1) != MIRType_String)
return InliningStatus_NotInlined;
MDefinitionVector argv;
if (!discardCall(argc, argv, current))
return InliningStatus_Error;
MInstruction *match = MRegExpTest::New(argv[0], argv[1]);
current->add(match);
current->push(match);
if (!resumeAfter(match))
return InliningStatus_Error;
return InliningStatus_Inlined;
}
} // namespace ion
} // namespace js

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

@ -2970,6 +2970,37 @@ class MRegExp : public MNullaryInstruction
}
};
class MRegExpTest
: public MBinaryInstruction,
public MixPolicy<ObjectPolicy<1>, StringPolicy >
{
private:
MRegExpTest(MDefinition *regexp, MDefinition *string)
: MBinaryInstruction(string, regexp)
{
setResultType(MIRType_Boolean);
}
public:
INSTRUCTION_HEADER(RegExpTest)
static MRegExpTest *New(MDefinition *regexp, MDefinition *string) {
return new MRegExpTest(regexp, string);
}
TypePolicy *typePolicy() {
return this;
}
MDefinition *regexp() const {
return getOperand(1);
}
MDefinition *string() const {
return getOperand(0);
}
};
class MLambda
: public MUnaryInstruction,
public SingleObjectPolicy

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

@ -75,6 +75,7 @@ namespace ion {
_(Start) \
_(OsrEntry) \
_(RegExp) \
_(RegExpTest) \
_(Lambda) \
_(ImplicitThis) \
_(Slots) \

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

@ -0,0 +1,12 @@
var p = Proxy.create({
has : function(id) {}
});
Object.prototype.__proto__ = p;
function f() {
if (/a/.exec("a"))
return 1;
return 0;
}
delete RegExp.prototype.test;
f();