зеркало из https://github.com/mozilla/gecko-dev.git
Bug 792220 - Remove lookupProperty to prevent interpreter reentrance. r=jandem
This commit is contained in:
Родитель
4842d8e772
Коммит
2d50daf191
|
@ -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, ®exp->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, ¬Bool);
|
||||
masm.unboxBoolean(JSReturnOperand, output);
|
||||
masm.jump(&end);
|
||||
masm.bind(¬Bool);
|
||||
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 ®exp, 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();
|
||||
|
Загрузка…
Ссылка в новой задаче