mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 928928 - Add missing property GetProp stub in BaselineJIT. r=nbp
This commit is contained in:
parent
5c5f844ec8
commit
82b1245b8d
78
js/src/jit-test/etc/generate-nosuchproperty-tests.js
Normal file
78
js/src/jit-test/etc/generate-nosuchproperty-tests.js
Normal file
@ -0,0 +1,78 @@
|
||||
|
||||
// This code generates the test cases jit-test/tests/baseline/no-such-property-getprop.js
|
||||
//
|
||||
// In particular, it generates the testChain_<N>_<I>() and runChain_<N>_<I>() functions
|
||||
// at the tail of the file.
|
||||
|
||||
var TEST_CASE_FUNCS = [];
|
||||
function runChain_NNNN_DDDD(obj) {
|
||||
var sum = 0;
|
||||
for (var i = 0; i < 100; i++)
|
||||
sum += obj.foo;
|
||||
return sum;
|
||||
}
|
||||
function testChain_NNNN_DDDD() {
|
||||
var obj = createTower(NNNN);
|
||||
assertEq(runChain_NNNN_DDDD(obj), NaN);
|
||||
updateChain(obj, DDDD, 'foo', 9);
|
||||
assertEq(runChain_NNNN_DDDD(obj), 900);
|
||||
}
|
||||
function generateTestCase(n, d) {
|
||||
var runFuncName = "runChain_" + n + "_" + d;
|
||||
var testFuncName = "testChain_" + n + "_" + d;
|
||||
TEST_CASE_FUNCS.push(testFuncName);
|
||||
|
||||
print("//// Test chain of length " + n + " with late-property-addition at depth " + d);
|
||||
print(uneval(runChain_NNNN_DDDD).replace(/NNNN/g, ''+n).replace(/DDDD/g, ''+d));
|
||||
print(uneval(testChain_NNNN_DDDD).replace(/NNNN/g, ''+n).replace(/DDDD/g, ''+d));
|
||||
print("");
|
||||
}
|
||||
|
||||
// Helper function to create an object with a proto-chain of length N.
|
||||
function createTower(n) {
|
||||
var result = Object.create(null);
|
||||
for (var i = 0; i < n; i++)
|
||||
result = Object.create(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
function updateChain(obj, depth, prop, value) {
|
||||
// Walk down the proto chain |depth| iterations and set |prop| to |value|.
|
||||
var cur = obj;
|
||||
for (var i = 0; i < depth; i++)
|
||||
cur = Object.getPrototypeOf(cur);
|
||||
|
||||
var desc = {value:value, writable:true, configurable:true, enumerable:true};
|
||||
Object.defineProperty(cur, prop, desc);
|
||||
}
|
||||
|
||||
print("/////////////////////////////////////////");
|
||||
print("// This is a generated file!");
|
||||
print("// See jit-tests/etc/generate-nosuchproperty-tests.js for the code");
|
||||
print("// that generated this code!");
|
||||
print("/////////////////////////////////////////");
|
||||
print("");
|
||||
print("/////////////////////////////////////////");
|
||||
print("// PRELUDE //");
|
||||
print("/////////////////////////////////////////");
|
||||
print("");
|
||||
print(createTower);
|
||||
print(updateChain);
|
||||
print("");
|
||||
print("/////////////////////////////////////////");
|
||||
print("// TEST CASES //");
|
||||
print("/////////////////////////////////////////");
|
||||
print("");
|
||||
for (var n = 0; n <= 10; n++) {
|
||||
for (var d = 0; d <= n; d++) {
|
||||
generateTestCase(n, d);
|
||||
}
|
||||
}
|
||||
|
||||
print("");
|
||||
print("/////////////////////////////////////////");
|
||||
print("// RUNNER //");
|
||||
print("/////////////////////////////////////////");
|
||||
print("");
|
||||
for (var i = 0; i < TEST_CASE_FUNCS.length; i++)
|
||||
print(TEST_CASE_FUNCS[i] + "();");
|
1025
js/src/jit-test/tests/baseline/no-such-property-getprop.js
Normal file
1025
js/src/jit-test/tests/baseline/no-such-property-getprop.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -335,6 +335,23 @@ ICStub::trace(JSTracer *trc)
|
||||
MarkShape(trc, &propStub->holderShape(), "baseline-getpropnativeproto-stub-holdershape");
|
||||
break;
|
||||
}
|
||||
case ICStub::GetProp_NativeDoesNotExist: {
|
||||
ICGetProp_NativeDoesNotExist *propStub = toGetProp_NativeDoesNotExist();
|
||||
JS_STATIC_ASSERT(ICGetProp_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH == 8);
|
||||
switch (propStub->protoChainDepth()) {
|
||||
case 0: propStub->toImpl<0>()->traceShapes(trc); break;
|
||||
case 1: propStub->toImpl<1>()->traceShapes(trc); break;
|
||||
case 2: propStub->toImpl<2>()->traceShapes(trc); break;
|
||||
case 3: propStub->toImpl<3>()->traceShapes(trc); break;
|
||||
case 4: propStub->toImpl<4>()->traceShapes(trc); break;
|
||||
case 5: propStub->toImpl<5>()->traceShapes(trc); break;
|
||||
case 6: propStub->toImpl<6>()->traceShapes(trc); break;
|
||||
case 7: propStub->toImpl<7>()->traceShapes(trc); break;
|
||||
case 8: propStub->toImpl<8>()->traceShapes(trc); break;
|
||||
default: MOZ_ASSUME_UNREACHABLE("Invalid proto stub.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ICStub::GetProp_CallDOMProxyNative:
|
||||
case ICStub::GetProp_CallDOMProxyWithGenerationNative: {
|
||||
ICGetPropCallDOMProxyNativeStub *propStub;
|
||||
@ -3340,6 +3357,35 @@ EffectlesslyLookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName n
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
CheckHasNoSuchProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
|
||||
MutableHandleObject lastProto, size_t *protoChainDepthOut)
|
||||
{
|
||||
MOZ_ASSERT(protoChainDepthOut != nullptr);
|
||||
|
||||
size_t depth = 0;
|
||||
RootedObject curObj(cx, obj);
|
||||
while (curObj) {
|
||||
if (!curObj->isNative())
|
||||
return false;
|
||||
|
||||
Shape *shape = curObj->nativeLookup(cx, NameToId(name));
|
||||
if (shape)
|
||||
return false;
|
||||
|
||||
JSObject *proto = curObj->getTaggedProto().toObjectOrNull();
|
||||
if (!proto)
|
||||
break;
|
||||
|
||||
curObj = proto;
|
||||
depth++;
|
||||
}
|
||||
|
||||
lastProto.set(curObj);
|
||||
*protoChainDepthOut = depth;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsCacheableProtoChain(JSObject *obj, JSObject *holder, bool isDOMProxy=false)
|
||||
{
|
||||
@ -6335,6 +6381,48 @@ TryAttachPrimitiveGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
TryAttachNativeDoesNotExistStub(JSContext *cx, HandleScript script, jsbytecode *pc,
|
||||
ICGetProp_Fallback *stub, HandlePropertyName name,
|
||||
HandleValue val, bool *attached)
|
||||
{
|
||||
MOZ_ASSERT(!*attached);
|
||||
|
||||
if (!val.isObject())
|
||||
return true;
|
||||
|
||||
RootedObject obj(cx, &val.toObject());
|
||||
|
||||
// Don't attach stubs for CALLPROP since those need NoSuchMethod handling.
|
||||
if (JSOp(*pc) == JSOP_CALLPROP)
|
||||
return true;
|
||||
|
||||
// Check if does-not-exist can be confirmed on property.
|
||||
RootedObject lastProto(cx);
|
||||
size_t protoChainDepth = SIZE_MAX;
|
||||
if (!CheckHasNoSuchProperty(cx, obj, name, &lastProto, &protoChainDepth))
|
||||
return true;
|
||||
MOZ_ASSERT(protoChainDepth < SIZE_MAX);
|
||||
|
||||
if (protoChainDepth > ICGetProp_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH)
|
||||
return true;
|
||||
|
||||
ICStub *monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
|
||||
RootedShape objShape(cx, obj->lastProperty());
|
||||
RootedShape lastShape(cx, lastProto->lastProperty());
|
||||
|
||||
// Confirmed no-such-property. Add stub.
|
||||
IonSpew(IonSpew_BaselineIC, " Generating GetProp_NativeDoesNotExist stub");
|
||||
ICGetPropNativeDoesNotExistCompiler compiler(cx, monitorStub, obj, protoChainDepth);
|
||||
ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!newStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(newStub);
|
||||
*attached = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
DoGetPropFallback(JSContext *cx, BaselineFrame *frame, ICGetProp_Fallback *stub_,
|
||||
MutableHandleValue val, MutableHandleValue res)
|
||||
@ -6423,6 +6511,14 @@ DoGetPropFallback(JSContext *cx, BaselineFrame *frame, ICGetProp_Fallback *stub_
|
||||
return true;
|
||||
}
|
||||
|
||||
if (res.isUndefined()) {
|
||||
// Try attaching property-not-found optimized stub for undefined results.
|
||||
if (!TryAttachNativeDoesNotExistStub(cx, script, pc, stub, name, val, &attached))
|
||||
return false;
|
||||
if (attached)
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_ASSERT(!attached);
|
||||
stub->noteUnoptimizableAccess();
|
||||
|
||||
@ -6680,6 +6776,87 @@ ICGetPropNativeCompiler::generateStubCode(MacroAssembler &masm)
|
||||
return true;
|
||||
}
|
||||
|
||||
ICStub *
|
||||
ICGetPropNativeDoesNotExistCompiler::getStub(ICStubSpace *space)
|
||||
{
|
||||
AutoShapeVector shapes(cx);
|
||||
if (!shapes.append(obj_->lastProperty()))
|
||||
return nullptr;
|
||||
|
||||
if (!GetProtoShapes(obj_, protoChainDepth_, &shapes))
|
||||
return nullptr;
|
||||
|
||||
JS_STATIC_ASSERT(ICGetProp_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH == 8);
|
||||
|
||||
ICStub *stub = nullptr;
|
||||
switch(protoChainDepth_) {
|
||||
case 0: stub = getStubSpecific<0>(space, &shapes); break;
|
||||
case 1: stub = getStubSpecific<1>(space, &shapes); break;
|
||||
case 2: stub = getStubSpecific<2>(space, &shapes); break;
|
||||
case 3: stub = getStubSpecific<3>(space, &shapes); break;
|
||||
case 4: stub = getStubSpecific<4>(space, &shapes); break;
|
||||
case 5: stub = getStubSpecific<5>(space, &shapes); break;
|
||||
case 6: stub = getStubSpecific<6>(space, &shapes); break;
|
||||
case 7: stub = getStubSpecific<7>(space, &shapes); break;
|
||||
case 8: stub = getStubSpecific<8>(space, &shapes); break;
|
||||
default: MOZ_ASSUME_UNREACHABLE("ProtoChainDepth too high.");
|
||||
}
|
||||
if (!stub)
|
||||
return nullptr;
|
||||
return stub;
|
||||
}
|
||||
|
||||
bool
|
||||
ICGetPropNativeDoesNotExistCompiler::generateStubCode(MacroAssembler &masm)
|
||||
{
|
||||
Label failure;
|
||||
|
||||
GeneralRegisterSet regs(availableGeneralRegs(1));
|
||||
Register scratch = regs.takeAny();
|
||||
|
||||
#ifdef DEBUG
|
||||
// Ensure that protoChainDepth_ matches the protoChainDepth stored on the stub.
|
||||
{
|
||||
Label ok;
|
||||
masm.load16ZeroExtend(Address(BaselineStubReg, ICStub::offsetOfExtra()), scratch);
|
||||
masm.branch32(Assembler::Equal, scratch, Imm32(protoChainDepth_), &ok);
|
||||
masm.assumeUnreachable("Non-matching proto chain depth on stub.");
|
||||
masm.bind(&ok);
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
||||
// Guard input is an object.
|
||||
masm.branchTestObject(Assembler::NotEqual, R0, &failure);
|
||||
|
||||
// Unbox and guard against old shape.
|
||||
Register objReg = masm.extractObject(R0, ExtractTemp0);
|
||||
masm.loadPtr(Address(BaselineStubReg, ICGetProp_NativeDoesNotExist::offsetOfShape(0)),
|
||||
scratch);
|
||||
masm.branchTestObjShape(Assembler::NotEqual, objReg, scratch, &failure);
|
||||
|
||||
Register protoReg = regs.takeAny();
|
||||
// Check the proto chain.
|
||||
for (size_t i = 0; i < protoChainDepth_; i++) {
|
||||
masm.loadObjProto(i == 0 ? objReg : protoReg, protoReg);
|
||||
masm.branchTestPtr(Assembler::Zero, protoReg, protoReg, &failure);
|
||||
size_t shapeOffset = ICGetProp_NativeDoesNotExistImpl<0>::offsetOfShape(i + 1);
|
||||
masm.loadPtr(Address(BaselineStubReg, shapeOffset), scratch);
|
||||
masm.branchTestObjShape(Assembler::NotEqual, protoReg, scratch, &failure);
|
||||
}
|
||||
|
||||
// Shape and type checks succeeded, ok to proceed.
|
||||
masm.moveValue(UndefinedValue(), R0);
|
||||
|
||||
// Normally for this op, the result would have to be monitored by TI.
|
||||
// However, since this stub ALWAYS returns UndefinedValue(), and we can be sure
|
||||
// that undefined is already registered with the type-set, this can be avoided.
|
||||
EmitReturnFromIC(masm);
|
||||
|
||||
masm.bind(&failure);
|
||||
EmitStubGuardFailure(masm);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ICGetProp_CallScripted::Compiler::generateStubCode(MacroAssembler &masm)
|
||||
{
|
||||
@ -10125,6 +10302,43 @@ ICGetProp_NativePrototype::Clone(JSContext *cx, ICStubSpace *space, ICStub *firs
|
||||
holder, holderShape);
|
||||
}
|
||||
|
||||
ICGetProp_NativeDoesNotExist::ICGetProp_NativeDoesNotExist(
|
||||
JitCode *stubCode, ICStub *firstMonitorStub, size_t protoChainDepth)
|
||||
: ICMonitoredStub(GetProp_NativeDoesNotExist, stubCode, firstMonitorStub)
|
||||
{
|
||||
MOZ_ASSERT(protoChainDepth <= MAX_PROTO_CHAIN_DEPTH);
|
||||
extra_ = protoChainDepth;
|
||||
}
|
||||
|
||||
/* static */ size_t
|
||||
ICGetProp_NativeDoesNotExist::offsetOfShape(size_t idx)
|
||||
{
|
||||
MOZ_ASSERT(ICGetProp_NativeDoesNotExistImpl<0>::offsetOfShape(idx) ==
|
||||
ICGetProp_NativeDoesNotExistImpl<
|
||||
ICGetProp_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH>::offsetOfShape(idx));
|
||||
return ICGetProp_NativeDoesNotExistImpl<0>::offsetOfShape(idx);
|
||||
}
|
||||
|
||||
template <size_t ProtoChainDepth>
|
||||
ICGetProp_NativeDoesNotExistImpl<ProtoChainDepth>::ICGetProp_NativeDoesNotExistImpl(
|
||||
JitCode *stubCode, ICStub *firstMonitorStub, const AutoShapeVector *shapes)
|
||||
: ICGetProp_NativeDoesNotExist(stubCode, firstMonitorStub, ProtoChainDepth)
|
||||
{
|
||||
MOZ_ASSERT(shapes->length() == NumShapes);
|
||||
for (size_t i = 0; i < NumShapes; i++)
|
||||
shapes_[i].init((*shapes)[i]);
|
||||
}
|
||||
|
||||
ICGetPropNativeDoesNotExistCompiler::ICGetPropNativeDoesNotExistCompiler(
|
||||
JSContext *cx, ICStub *firstMonitorStub, HandleObject obj, size_t protoChainDepth)
|
||||
: ICStubCompiler(cx, ICStub::GetProp_NativeDoesNotExist),
|
||||
firstMonitorStub_(firstMonitorStub),
|
||||
obj_(cx, obj),
|
||||
protoChainDepth_(protoChainDepth)
|
||||
{
|
||||
MOZ_ASSERT(protoChainDepth_ <= ICGetProp_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH);
|
||||
}
|
||||
|
||||
ICGetPropCallGetter::ICGetPropCallGetter(Kind kind, JitCode *stubCode, ICStub *firstMonitorStub,
|
||||
HandleObject holder, HandleShape holderShape, HandleFunction getter,
|
||||
uint32_t pcOffset)
|
||||
|
@ -414,6 +414,7 @@ class ICEntry
|
||||
_(GetProp_Primitive) \
|
||||
_(GetProp_StringLength) \
|
||||
_(GetProp_Native) \
|
||||
_(GetProp_NativeDoesNotExist) \
|
||||
_(GetProp_NativePrototype) \
|
||||
_(GetProp_CallScripted) \
|
||||
_(GetProp_CallNative) \
|
||||
@ -4412,6 +4413,104 @@ class ICGetPropNativeCompiler : public ICStubCompiler
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t ProtoChainDepth> class ICGetProp_NativeDoesNotExistImpl;
|
||||
|
||||
class ICGetProp_NativeDoesNotExist : public ICMonitoredStub
|
||||
{
|
||||
friend class ICStubSpace;
|
||||
public:
|
||||
static const size_t MAX_PROTO_CHAIN_DEPTH = 8;
|
||||
|
||||
protected:
|
||||
ICGetProp_NativeDoesNotExist(JitCode *stubCode, ICStub *firstMonitorStub,
|
||||
size_t protoChainDepth);
|
||||
|
||||
public:
|
||||
static inline ICGetProp_NativeDoesNotExist *New(ICStubSpace *space, JitCode *code,
|
||||
ICStub *firstMonitorStub,
|
||||
size_t protoChainDepth)
|
||||
{
|
||||
if (!code)
|
||||
return nullptr;
|
||||
return space->allocate<ICGetProp_NativeDoesNotExist>(code, firstMonitorStub,
|
||||
protoChainDepth);
|
||||
}
|
||||
|
||||
size_t protoChainDepth() const {
|
||||
MOZ_ASSERT(extra_ <= MAX_PROTO_CHAIN_DEPTH);
|
||||
return extra_;
|
||||
}
|
||||
|
||||
template <size_t ProtoChainDepth>
|
||||
ICGetProp_NativeDoesNotExistImpl<ProtoChainDepth> *toImpl() {
|
||||
MOZ_ASSERT(ProtoChainDepth == protoChainDepth());
|
||||
return static_cast<ICGetProp_NativeDoesNotExistImpl<ProtoChainDepth> *>(this);
|
||||
}
|
||||
|
||||
static size_t offsetOfShape(size_t idx);
|
||||
};
|
||||
|
||||
template <size_t ProtoChainDepth>
|
||||
class ICGetProp_NativeDoesNotExistImpl : public ICGetProp_NativeDoesNotExist
|
||||
{
|
||||
friend class ICStubSpace;
|
||||
public:
|
||||
static const size_t MAX_PROTO_CHAIN_DEPTH = 8;
|
||||
static const size_t NumShapes = ProtoChainDepth + 1;
|
||||
|
||||
private:
|
||||
mozilla::Array<HeapPtrShape, NumShapes> shapes_;
|
||||
|
||||
ICGetProp_NativeDoesNotExistImpl(JitCode *stubCode, ICStub *firstMonitorStub,
|
||||
const AutoShapeVector *shapes);
|
||||
|
||||
public:
|
||||
static inline ICGetProp_NativeDoesNotExistImpl<ProtoChainDepth> *New(
|
||||
ICStubSpace *space, JitCode *code, ICStub *firstMonitorStub,
|
||||
const AutoShapeVector *shapes)
|
||||
{
|
||||
if (!code)
|
||||
return nullptr;
|
||||
return space->allocate<ICGetProp_NativeDoesNotExistImpl<ProtoChainDepth>>(
|
||||
code, firstMonitorStub, shapes);
|
||||
}
|
||||
|
||||
void traceShapes(JSTracer *trc) {
|
||||
for (size_t i = 0; i < NumShapes; i++)
|
||||
MarkShape(trc, &shapes_[i], "baseline-getpropnativedoesnotexist-stub-shape");
|
||||
}
|
||||
|
||||
static size_t offsetOfShape(size_t idx) {
|
||||
return offsetof(ICGetProp_NativeDoesNotExistImpl, shapes_) + (idx * sizeof(HeapPtrShape));
|
||||
}
|
||||
};
|
||||
|
||||
class ICGetPropNativeDoesNotExistCompiler : public ICStubCompiler
|
||||
{
|
||||
ICStub *firstMonitorStub_;
|
||||
RootedObject obj_;
|
||||
size_t protoChainDepth_;
|
||||
|
||||
protected:
|
||||
virtual int32_t getKey() const {
|
||||
return static_cast<int32_t>(kind) | (static_cast<int32_t>(protoChainDepth_) << 16);
|
||||
}
|
||||
|
||||
bool generateStubCode(MacroAssembler &masm);
|
||||
|
||||
public:
|
||||
ICGetPropNativeDoesNotExistCompiler(JSContext *cx, ICStub *firstMonitorStub,
|
||||
HandleObject obj, size_t protoChainDepth);
|
||||
|
||||
template <size_t ProtoChainDepth>
|
||||
ICStub *getStubSpecific(ICStubSpace *space, const AutoShapeVector *shapes) {
|
||||
return ICGetProp_NativeDoesNotExistImpl<ProtoChainDepth>::New(space, getStubCode(),
|
||||
firstMonitorStub_, shapes);
|
||||
}
|
||||
|
||||
ICStub *getStub(ICStubSpace *space);
|
||||
};
|
||||
|
||||
class ICGetPropCallGetter : public ICMonitoredStub
|
||||
{
|
||||
friend class ICStubSpace;
|
||||
@ -5155,7 +5254,8 @@ class ICSetProp_NativeAddImpl : public ICSetProp_NativeAdd
|
||||
}
|
||||
};
|
||||
|
||||
class ICSetPropNativeAddCompiler : public ICStubCompiler {
|
||||
class ICSetPropNativeAddCompiler : public ICStubCompiler
|
||||
{
|
||||
RootedObject obj_;
|
||||
RootedShape oldShape_;
|
||||
size_t protoChainDepth_;
|
||||
|
Loading…
Reference in New Issue
Block a user