mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-28 18:17:29 +09:00
Merge remote-tracking branch 'origin/master' into custom
This commit is contained in:
commit
911c58f97f
53 changed files with 100 additions and 3419 deletions
|
|
@ -9192,7 +9192,7 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!emitArray(args, argc))
|
if (!emitArray(args, argc, JSOP_SPREADCALLARRAY))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (optCodeEmitted) {
|
if (optCodeEmitted) {
|
||||||
|
|
@ -9683,11 +9683,11 @@ BytecodeEmitter::emitArrayLiteral(ParseNode* pn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return emitArray(pn->pn_head, pn->pn_count);
|
return emitArray(pn->pn_head, pn->pn_count, JSOP_NEWARRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count)
|
BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count, JSOp op)
|
||||||
{
|
{
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -9698,6 +9698,7 @@ BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count)
|
||||||
* to avoid dup'ing and popping the array as each element is added, as
|
* to avoid dup'ing and popping the array as each element is added, as
|
||||||
* JSOP_SETELEM/JSOP_SETPROP would do.
|
* JSOP_SETELEM/JSOP_SETPROP would do.
|
||||||
*/
|
*/
|
||||||
|
MOZ_ASSERT(op == JSOP_NEWARRAY || op == JSOP_SPREADCALLARRAY);
|
||||||
|
|
||||||
uint32_t nspread = 0;
|
uint32_t nspread = 0;
|
||||||
for (ParseNode* elt = pn; elt; elt = elt->pn_next) {
|
for (ParseNode* elt = pn; elt; elt = elt->pn_next) {
|
||||||
|
|
@ -9718,7 +9719,7 @@ BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count)
|
||||||
|
|
||||||
// For arrays with spread, this is a very pessimistic allocation, the
|
// For arrays with spread, this is a very pessimistic allocation, the
|
||||||
// minimum possible final size.
|
// minimum possible final size.
|
||||||
if (!emitUint32Operand(JSOP_NEWARRAY, count - nspread)) // ARRAY
|
if (!emitUint32Operand(op, count - nspread)) // ARRAY
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ParseNode* pn2 = pn;
|
ParseNode* pn2 = pn;
|
||||||
|
|
|
||||||
|
|
@ -521,7 +521,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
||||||
MOZ_MUST_USE bool emitAtomOp(ParseNode* pn, JSOp op);
|
MOZ_MUST_USE bool emitAtomOp(ParseNode* pn, JSOp op);
|
||||||
|
|
||||||
MOZ_MUST_USE bool emitArrayLiteral(ParseNode* pn);
|
MOZ_MUST_USE bool emitArrayLiteral(ParseNode* pn);
|
||||||
MOZ_MUST_USE bool emitArray(ParseNode* pn, uint32_t count);
|
MOZ_MUST_USE bool emitArray(ParseNode* pn, uint32_t count, JSOp op);
|
||||||
MOZ_MUST_USE bool emitArrayComp(ParseNode* pn);
|
MOZ_MUST_USE bool emitArrayComp(ParseNode* pn);
|
||||||
|
|
||||||
MOZ_MUST_USE bool emitInternedScopeOp(uint32_t index, JSOp op);
|
MOZ_MUST_USE bool emitInternedScopeOp(uint32_t index, JSOp op);
|
||||||
|
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
|
|
||||||
function Foo(a, b) {
|
|
||||||
this.a = a;
|
|
||||||
this.b = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
function invalidate_foo() {
|
|
||||||
var a = [];
|
|
||||||
var counter = 0;
|
|
||||||
for (var i = 0; i < 50; i++)
|
|
||||||
a.push(new Foo(i, i + 1));
|
|
||||||
Object.defineProperty(Foo.prototype, "a", {configurable: true, set: function() { counter++; }});
|
|
||||||
for (var i = 0; i < 50; i++)
|
|
||||||
a.push(new Foo(i, i + 1));
|
|
||||||
delete Foo.prototype.a;
|
|
||||||
var total = 0;
|
|
||||||
for (var i = 0; i < a.length; i++) {
|
|
||||||
assertEq('a' in a[i], i < 50);
|
|
||||||
total += a[i].b;
|
|
||||||
}
|
|
||||||
assertEq(total, 2550);
|
|
||||||
assertEq(counter, 50);
|
|
||||||
}
|
|
||||||
invalidate_foo();
|
|
||||||
|
|
||||||
function Bar(a, b, fn) {
|
|
||||||
this.a = a;
|
|
||||||
if (b == 30)
|
|
||||||
Object.defineProperty(Bar.prototype, "b", {configurable: true, set: fn});
|
|
||||||
this.b = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
function invalidate_bar() {
|
|
||||||
var a = [];
|
|
||||||
var counter = 0;
|
|
||||||
function fn() { counter++; }
|
|
||||||
for (var i = 0; i < 50; i++)
|
|
||||||
a.push(new Bar(i, i + 1, fn));
|
|
||||||
delete Bar.prototype.b;
|
|
||||||
var total = 0;
|
|
||||||
for (var i = 0; i < a.length; i++) {
|
|
||||||
assertEq('a' in a[i], true);
|
|
||||||
assertEq('b' in a[i], i < 29);
|
|
||||||
total += a[i].a;
|
|
||||||
}
|
|
||||||
assertEq(total, 1225);
|
|
||||||
assertEq(counter, 21);
|
|
||||||
}
|
|
||||||
invalidate_bar();
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
|
|
||||||
// Test various ways of converting an unboxed object to native.
|
|
||||||
|
|
||||||
function Foo(a, b) {
|
|
||||||
this.a = a;
|
|
||||||
this.b = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
var proxyObj = {
|
|
||||||
get: function(recipient, name) {
|
|
||||||
return recipient[name] + 2;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function f() {
|
|
||||||
var a = [];
|
|
||||||
for (var i = 0; i < 50; i++)
|
|
||||||
a.push(new Foo(i, i + 1));
|
|
||||||
|
|
||||||
var prop = "a";
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
for (; i < 5; i++)
|
|
||||||
a[i].c = i;
|
|
||||||
for (; i < 10; i++)
|
|
||||||
Object.defineProperty(a[i], 'c', {value: i});
|
|
||||||
for (; i < 15; i++)
|
|
||||||
a[i] = new Proxy(a[i], proxyObj);
|
|
||||||
for (; i < 20; i++)
|
|
||||||
a[i].a = 3.5;
|
|
||||||
for (; i < 25; i++)
|
|
||||||
delete a[i].b;
|
|
||||||
for (; i < 30; i++)
|
|
||||||
a[prop] = 4;
|
|
||||||
|
|
||||||
var total = 0;
|
|
||||||
for (i = 0; i < a.length; i++) {
|
|
||||||
if ('a' in a[i])
|
|
||||||
total += a[i].a;
|
|
||||||
if ('b' in a[i])
|
|
||||||
total += a[i].b;
|
|
||||||
if ('c' in a[i])
|
|
||||||
total += a[i].c;
|
|
||||||
}
|
|
||||||
assertEq(total, 2382.5);
|
|
||||||
}
|
|
||||||
f();
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
|
|
||||||
function f() {
|
|
||||||
var propNames = ["a","b","c","d","e","f","g","h","i","j","x","y"];
|
|
||||||
var arr = [];
|
|
||||||
for (var i=0; i<64; i++)
|
|
||||||
arr.push({x:1, y:2});
|
|
||||||
for (var i=0; i<64; i++) {
|
|
||||||
// Make sure there are expandos with dynamic slots for each object.
|
|
||||||
for (var j = 0; j < propNames.length; j++)
|
|
||||||
arr[i][propNames[j]] = j;
|
|
||||||
}
|
|
||||||
var res = 0;
|
|
||||||
for (var i=0; i<100000; i++) {
|
|
||||||
var o = arr[i % 64];
|
|
||||||
var p = propNames[i % propNames.length];
|
|
||||||
res += o[p];
|
|
||||||
}
|
|
||||||
assertEq(res, 549984);
|
|
||||||
}
|
|
||||||
f();
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
|
|
||||||
// Use the correct receiver when non-native objects are prototypes of other objects.
|
|
||||||
|
|
||||||
function Thing(a, b) {
|
|
||||||
this.a = a;
|
|
||||||
this.b = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
function foo() {
|
|
||||||
var array = [];
|
|
||||||
for (var i = 0; i < 10000; i++)
|
|
||||||
array.push(new Thing(i, i + 1));
|
|
||||||
|
|
||||||
var proto = new Thing(1, 2);
|
|
||||||
var obj = Object.create(proto);
|
|
||||||
|
|
||||||
Object.defineProperty(Thing.prototype, "c", {set:function() { this.d = 0; }});
|
|
||||||
obj.c = 3;
|
|
||||||
assertEq(obj.c, undefined);
|
|
||||||
assertEq(obj.d, 0);
|
|
||||||
assertEq(obj.hasOwnProperty("d"), true);
|
|
||||||
assertEq(proto.d, undefined);
|
|
||||||
assertEq(proto.hasOwnProperty("d"), false);
|
|
||||||
|
|
||||||
obj.a = 3;
|
|
||||||
assertEq(obj.a, 3);
|
|
||||||
assertEq(proto.a, 1);
|
|
||||||
assertEq(obj.hasOwnProperty("a"), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
foo();
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
function O() {
|
|
||||||
this.x = 1;
|
|
||||||
this.y = 2;
|
|
||||||
}
|
|
||||||
function testUnboxed() {
|
|
||||||
var arr = [];
|
|
||||||
for (var i=0; i<100; i++)
|
|
||||||
arr.push(new O);
|
|
||||||
|
|
||||||
var o = arr[arr.length-1];
|
|
||||||
o[0] = 0;
|
|
||||||
o[2] = 2;
|
|
||||||
var sym = Symbol();
|
|
||||||
o[sym] = 1;
|
|
||||||
o.z = 3;
|
|
||||||
Object.defineProperty(o, '3', {value:1,enumerable:false,configurable:false,writable:false});
|
|
||||||
o[4] = 4;
|
|
||||||
|
|
||||||
var props = Reflect.ownKeys(o);
|
|
||||||
assertEq(props[props.length-1], sym);
|
|
||||||
|
|
||||||
assertEq(Object.getOwnPropertyNames(o).join(""), "0234xyz");
|
|
||||||
}
|
|
||||||
testUnboxed();
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
|
|
||||||
var a = [];
|
|
||||||
for (var i = 0; i < 2000; i++)
|
|
||||||
a.push({f:i});
|
|
||||||
|
|
||||||
function f() {
|
|
||||||
var total = 0;
|
|
||||||
for (var i = 0; i < a.length; i++)
|
|
||||||
total += a[i].f;
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
assertEq(f(), 1999000);
|
|
||||||
|
|
||||||
var sub = Object.create(a[0]);
|
|
||||||
|
|
||||||
assertEq(f(), 1999000);
|
|
||||||
|
|
@ -16,7 +16,7 @@ using namespace js;
|
||||||
using namespace js::jit;
|
using namespace js::jit;
|
||||||
|
|
||||||
// OperandLocation represents the location of an OperandId. The operand is
|
// OperandLocation represents the location of an OperandId. The operand is
|
||||||
// either in a register or on the stack, and is either boxed or unboxed.
|
// either in a register or on the stack.
|
||||||
class OperandLocation
|
class OperandLocation
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -814,36 +814,6 @@ BaselineCacheIRCompiler::emitGuardSpecificObject()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
BaselineCacheIRCompiler::emitGuardNoUnboxedExpando()
|
|
||||||
{
|
|
||||||
Register obj = allocator.useRegister(masm, reader.objOperandId());
|
|
||||||
|
|
||||||
FailurePath* failure;
|
|
||||||
if (!addFailurePath(&failure))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Address expandoAddr(obj, UnboxedPlainObject::offsetOfExpando());
|
|
||||||
masm.branchPtr(Assembler::NotEqual, expandoAddr, ImmWord(0), failure->label());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
BaselineCacheIRCompiler::emitGuardAndLoadUnboxedExpando()
|
|
||||||
{
|
|
||||||
Register obj = allocator.useRegister(masm, reader.objOperandId());
|
|
||||||
Register output = allocator.defineRegister(masm, reader.objOperandId());
|
|
||||||
|
|
||||||
FailurePath* failure;
|
|
||||||
if (!addFailurePath(&failure))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Address expandoAddr(obj, UnboxedPlainObject::offsetOfExpando());
|
|
||||||
masm.loadPtr(expandoAddr, output);
|
|
||||||
masm.branchTestPtr(Assembler::Zero, output, output, failure->label());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
BaselineCacheIRCompiler::emitLoadFixedSlotResult()
|
BaselineCacheIRCompiler::emitLoadFixedSlotResult()
|
||||||
{
|
{
|
||||||
|
|
@ -870,26 +840,6 @@ BaselineCacheIRCompiler::emitLoadDynamicSlotResult()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
BaselineCacheIRCompiler::emitLoadUnboxedPropertyResult()
|
|
||||||
{
|
|
||||||
Register obj = allocator.useRegister(masm, reader.objOperandId());
|
|
||||||
AutoScratchRegister scratch(allocator, masm);
|
|
||||||
|
|
||||||
JSValueType fieldType = reader.valueType();
|
|
||||||
|
|
||||||
Address fieldOffset(stubAddress(reader.stubOffset()));
|
|
||||||
masm.load32(fieldOffset, scratch);
|
|
||||||
masm.loadUnboxedProperty(BaseIndex(obj, scratch, TimesOne), fieldType, R0);
|
|
||||||
|
|
||||||
if (fieldType == JSVAL_TYPE_OBJECT)
|
|
||||||
emitEnterTypeMonitorIC();
|
|
||||||
else
|
|
||||||
emitReturnFromIC();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
BaselineCacheIRCompiler::emitGuardNoDetachedTypedObjects()
|
BaselineCacheIRCompiler::emitGuardNoDetachedTypedObjects()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2049,6 +2049,12 @@ BaselineCompiler::emit_JSOP_NEWARRAY()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
BaselineCompiler::emit_JSOP_SPREADCALLARRAY()
|
||||||
|
{
|
||||||
|
return emit_JSOP_NEWARRAY();
|
||||||
|
}
|
||||||
|
|
||||||
typedef ArrayObject* (*NewArrayCopyOnWriteFn)(JSContext*, HandleArrayObject, gc::InitialHeap);
|
typedef ArrayObject* (*NewArrayCopyOnWriteFn)(JSContext*, HandleArrayObject, gc::InitialHeap);
|
||||||
const VMFunction jit::NewArrayCopyOnWriteInfo =
|
const VMFunction jit::NewArrayCopyOnWriteInfo =
|
||||||
FunctionInfo<NewArrayCopyOnWriteFn>(js::NewDenseCopyOnWriteArray, "NewDenseCopyOnWriteArray");
|
FunctionInfo<NewArrayCopyOnWriteFn>(js::NewDenseCopyOnWriteArray, "NewDenseCopyOnWriteArray");
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,7 @@ namespace jit {
|
||||||
_(JSOP_BITNOT) \
|
_(JSOP_BITNOT) \
|
||||||
_(JSOP_NEG) \
|
_(JSOP_NEG) \
|
||||||
_(JSOP_NEWARRAY) \
|
_(JSOP_NEWARRAY) \
|
||||||
|
_(JSOP_SPREADCALLARRAY) \
|
||||||
_(JSOP_NEWARRAY_COPYONWRITE) \
|
_(JSOP_NEWARRAY_COPYONWRITE) \
|
||||||
_(JSOP_INITELEM_ARRAY) \
|
_(JSOP_INITELEM_ARRAY) \
|
||||||
_(JSOP_NEWOBJECT) \
|
_(JSOP_NEWOBJECT) \
|
||||||
|
|
|
||||||
|
|
@ -42,8 +42,8 @@
|
||||||
#include "jit/shared/Lowering-shared-inl.h"
|
#include "jit/shared/Lowering-shared-inl.h"
|
||||||
#include "vm/EnvironmentObject-inl.h"
|
#include "vm/EnvironmentObject-inl.h"
|
||||||
#include "vm/Interpreter-inl.h"
|
#include "vm/Interpreter-inl.h"
|
||||||
|
#include "vm/NativeObject-inl.h"
|
||||||
#include "vm/StringObject-inl.h"
|
#include "vm/StringObject-inl.h"
|
||||||
#include "vm/UnboxedObject-inl.h"
|
|
||||||
|
|
||||||
using mozilla::DebugOnly;
|
using mozilla::DebugOnly;
|
||||||
|
|
||||||
|
|
@ -732,11 +732,6 @@ LastPropertyForSetProp(JSObject* obj)
|
||||||
if (obj->isNative())
|
if (obj->isNative())
|
||||||
return obj->as<NativeObject>().lastProperty();
|
return obj->as<NativeObject>().lastProperty();
|
||||||
|
|
||||||
if (obj->is<UnboxedPlainObject>()) {
|
|
||||||
UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
|
|
||||||
return expando ? expando->lastProperty() : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1153,56 +1148,6 @@ TryAttachNativeOrUnboxedGetValueElemStub(JSContext* cx, HandleScript script, jsb
|
||||||
|
|
||||||
ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
|
ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
|
||||||
|
|
||||||
if (obj->is<UnboxedPlainObject>() && holder == obj) {
|
|
||||||
const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
|
|
||||||
|
|
||||||
// Once unboxed objects support symbol-keys, we need to change the following accordingly
|
|
||||||
MOZ_ASSERT_IF(!keyVal.isString(), !property);
|
|
||||||
|
|
||||||
if (property) {
|
|
||||||
if (!cx->runtime()->jitSupportsFloatingPoint)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
RootedPropertyName name(cx, JSID_TO_ATOM(id)->asPropertyName());
|
|
||||||
ICGetElemNativeCompiler<PropertyName*> compiler(cx, ICStub::GetElem_UnboxedPropertyName,
|
|
||||||
monitorStub, obj, holder,
|
|
||||||
name,
|
|
||||||
ICGetElemNativeStub::UnboxedProperty,
|
|
||||||
needsAtomize, property->offset +
|
|
||||||
UnboxedPlainObject::offsetOfData(),
|
|
||||||
property->type);
|
|
||||||
ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
|
|
||||||
if (!newStub)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
stub->addNewStub(newStub);
|
|
||||||
*attached = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Shape* shape = obj->as<UnboxedPlainObject>().maybeExpando()->lookup(cx, id);
|
|
||||||
if (!shape->hasDefaultGetter() || !shape->hasSlot())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
bool isFixedSlot;
|
|
||||||
uint32_t offset;
|
|
||||||
GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
|
|
||||||
|
|
||||||
ICGetElemNativeStub::AccessType acctype =
|
|
||||||
isFixedSlot ? ICGetElemNativeStub::FixedSlot
|
|
||||||
: ICGetElemNativeStub::DynamicSlot;
|
|
||||||
ICGetElemNativeCompiler<T> compiler(cx, getGetElemStubKind<T>(ICStub::GetElem_NativeSlotName),
|
|
||||||
monitorStub, obj, holder, key,
|
|
||||||
acctype, needsAtomize, offset);
|
|
||||||
ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
|
|
||||||
if (!newStub)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
stub->addNewStub(newStub);
|
|
||||||
*attached = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!holder->isNative())
|
if (!holder->isNative())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
|
@ -1450,7 +1395,7 @@ TryAttachGetElemStub(JSContext* cx, JSScript* script, jsbytecode* pc, ICGetElem_
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for NativeObject[id] and UnboxedPlainObject[id] shape-optimizable accesses.
|
// Check for NativeObject[id] and UnboxedPlainObject[id] shape-optimizable accesses.
|
||||||
if (obj->isNative() || obj->is<UnboxedPlainObject>()) {
|
if (obj->isNative()) {
|
||||||
RootedScript rootedScript(cx, script);
|
RootedScript rootedScript(cx, script);
|
||||||
if (rhs.isString()) {
|
if (rhs.isString()) {
|
||||||
if (!TryAttachNativeOrUnboxedGetValueElemStub<PropertyName*>(cx, rootedScript, pc, stub,
|
if (!TryAttachNativeOrUnboxedGetValueElemStub<PropertyName*>(cx, rootedScript, pc, stub,
|
||||||
|
|
@ -1862,14 +1807,6 @@ ICGetElemNativeCompiler<T>::generateStubCode(MacroAssembler& masm)
|
||||||
Register holderReg;
|
Register holderReg;
|
||||||
if (obj_ == holder_) {
|
if (obj_ == holder_) {
|
||||||
holderReg = objReg;
|
holderReg = objReg;
|
||||||
|
|
||||||
if (obj_->is<UnboxedPlainObject>() && acctype_ != ICGetElemNativeStub::UnboxedProperty) {
|
|
||||||
// The property will be loaded off the unboxed expando.
|
|
||||||
masm.push(R1.scratchReg());
|
|
||||||
popR1 = true;
|
|
||||||
holderReg = R1.scratchReg();
|
|
||||||
masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Shape guard holder.
|
// Shape guard holder.
|
||||||
if (regs.empty()) {
|
if (regs.empty()) {
|
||||||
|
|
@ -1920,13 +1857,6 @@ ICGetElemNativeCompiler<T>::generateStubCode(MacroAssembler& masm)
|
||||||
if (popR1)
|
if (popR1)
|
||||||
masm.addToStackPtr(ImmWord(sizeof(size_t)));
|
masm.addToStackPtr(ImmWord(sizeof(size_t)));
|
||||||
|
|
||||||
} else if (acctype_ == ICGetElemNativeStub::UnboxedProperty) {
|
|
||||||
masm.load32(Address(ICStubReg, ICGetElemNativeSlotStub<T>::offsetOfOffset()),
|
|
||||||
scratchReg);
|
|
||||||
masm.loadUnboxedProperty(BaseIndex(objReg, scratchReg, TimesOne), unboxedType_,
|
|
||||||
TypedOrValueRegister(R0));
|
|
||||||
if (popR1)
|
|
||||||
masm.addToStackPtr(ImmWord(sizeof(size_t)));
|
|
||||||
} else {
|
} else {
|
||||||
MOZ_ASSERT(acctype_ == ICGetElemNativeStub::NativeGetter ||
|
MOZ_ASSERT(acctype_ == ICGetElemNativeStub::NativeGetter ||
|
||||||
acctype_ == ICGetElemNativeStub::ScriptedGetter);
|
acctype_ == ICGetElemNativeStub::ScriptedGetter);
|
||||||
|
|
@ -2673,18 +2603,6 @@ BaselineScript::noteArrayWriteHole(uint32_t pcOffset)
|
||||||
// SetElem_DenseOrUnboxedArray
|
// SetElem_DenseOrUnboxedArray
|
||||||
//
|
//
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void
|
|
||||||
EmitUnboxedPreBarrierForBaseline(MacroAssembler &masm, T address, JSValueType type)
|
|
||||||
{
|
|
||||||
if (type == JSVAL_TYPE_OBJECT)
|
|
||||||
EmitPreBarrier(masm, address, MIRType::Object);
|
|
||||||
else if (type == JSVAL_TYPE_STRING)
|
|
||||||
EmitPreBarrier(masm, address, MIRType::String);
|
|
||||||
else
|
|
||||||
MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(type));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICSetElem_DenseOrUnboxedArray::Compiler::generateStubCode(MacroAssembler& masm)
|
ICSetElem_DenseOrUnboxedArray::Compiler::generateStubCode(MacroAssembler& masm)
|
||||||
{
|
{
|
||||||
|
|
@ -4131,18 +4049,7 @@ TryAttachSetValuePropStub(JSContext* cx, HandleScript script, jsbytecode* pc, IC
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!obj->isNative()) {
|
if (!obj->isNative()) {
|
||||||
if (obj->is<UnboxedPlainObject>()) {
|
return true;
|
||||||
UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
|
|
||||||
if (expando) {
|
|
||||||
shape = expando->lookup(cx, name);
|
|
||||||
if (!shape)
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t chainDepth;
|
size_t chainDepth;
|
||||||
|
|
@ -4292,40 +4199,6 @@ TryAttachSetAccessorPropStub(JSContext* cx, HandleScript script, jsbytecode* pc,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
TryAttachUnboxedSetPropStub(JSContext* cx, HandleScript script,
|
|
||||||
ICSetProp_Fallback* stub, HandleId id,
|
|
||||||
HandleObject obj, HandleValue rhs, bool* attached)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(!*attached);
|
|
||||||
|
|
||||||
if (!cx->runtime()->jitSupportsFloatingPoint)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (!obj->is<UnboxedPlainObject>())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
|
|
||||||
if (!property)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
ICSetProp_Unboxed::Compiler compiler(cx, obj->group(),
|
|
||||||
property->offset + UnboxedPlainObject::offsetOfData(),
|
|
||||||
property->type);
|
|
||||||
ICUpdatedStub* newStub = compiler.getStub(compiler.getStubSpace(script));
|
|
||||||
if (!newStub)
|
|
||||||
return false;
|
|
||||||
if (compiler.needsUpdateStubs() && !newStub->addUpdateStubForValue(cx, script, obj, id, rhs))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
stub->addNewStub(newStub);
|
|
||||||
|
|
||||||
StripPreliminaryObjectStubs(cx, stub);
|
|
||||||
|
|
||||||
*attached = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
TryAttachTypedObjectSetPropStub(JSContext* cx, HandleScript script,
|
TryAttachTypedObjectSetPropStub(JSContext* cx, HandleScript script,
|
||||||
ICSetProp_Fallback* stub, HandleId id,
|
ICSetProp_Fallback* stub, HandleId id,
|
||||||
|
|
@ -4409,12 +4282,6 @@ DoSetPropFallback(JSContext* cx, BaselineFrame* frame, ICSetProp_Fallback* stub_
|
||||||
return false;
|
return false;
|
||||||
RootedReceiverGuard oldGuard(cx, ReceiverGuard(obj));
|
RootedReceiverGuard oldGuard(cx, ReceiverGuard(obj));
|
||||||
|
|
||||||
if (obj->is<UnboxedPlainObject>()) {
|
|
||||||
MOZ_ASSERT(!oldShape);
|
|
||||||
if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
|
|
||||||
oldShape = expando->lastProperty();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool attached = false;
|
bool attached = false;
|
||||||
// There are some reasons we can fail to attach a stub that are temporary.
|
// There are some reasons we can fail to attach a stub that are temporary.
|
||||||
// We want to avoid calling noteUnoptimizableAccess() if the reason we
|
// We want to avoid calling noteUnoptimizableAccess() if the reason we
|
||||||
|
|
@ -4485,15 +4352,6 @@ DoSetPropFallback(JSContext* cx, BaselineFrame* frame, ICSetProp_Fallback* stub_
|
||||||
if (attached)
|
if (attached)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!attached &&
|
|
||||||
lhs.isObject() &&
|
|
||||||
!TryAttachUnboxedSetPropStub(cx, script, stub, id, obj, rhs, &attached))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (attached)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (!attached &&
|
if (!attached &&
|
||||||
lhs.isObject() &&
|
lhs.isObject() &&
|
||||||
!TryAttachTypedObjectSetPropStub(cx, script, stub, id, obj, rhs, &attached))
|
!TryAttachTypedObjectSetPropStub(cx, script, stub, id, obj, rhs, &attached))
|
||||||
|
|
@ -4578,20 +4436,7 @@ GuardGroupAndShapeMaybeUnboxedExpando(MacroAssembler& masm, JSObject* obj,
|
||||||
|
|
||||||
// Guard against shape or expando shape.
|
// Guard against shape or expando shape.
|
||||||
masm.loadPtr(Address(ICStubReg, offsetOfShape), scratch);
|
masm.loadPtr(Address(ICStubReg, offsetOfShape), scratch);
|
||||||
if (obj->is<UnboxedPlainObject>()) {
|
masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure);
|
||||||
Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando());
|
|
||||||
masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failure);
|
|
||||||
Label done;
|
|
||||||
masm.push(object);
|
|
||||||
masm.loadPtr(expandoAddress, object);
|
|
||||||
masm.branchTestObjShape(Assembler::Equal, object, scratch, &done);
|
|
||||||
masm.pop(object);
|
|
||||||
masm.jump(failure);
|
|
||||||
masm.bind(&done);
|
|
||||||
masm.pop(object);
|
|
||||||
} else {
|
|
||||||
masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
@ -4630,13 +4475,7 @@ ICSetProp_Native::Compiler::generateStubCode(MacroAssembler& masm)
|
||||||
regs.takeUnchecked(objReg);
|
regs.takeUnchecked(objReg);
|
||||||
|
|
||||||
Register holderReg;
|
Register holderReg;
|
||||||
if (obj_->is<UnboxedPlainObject>()) {
|
if (isFixedSlot_) {
|
||||||
// We are loading off the expando object, so use that for the holder.
|
|
||||||
holderReg = regs.takeAny();
|
|
||||||
masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg);
|
|
||||||
if (!isFixedSlot_)
|
|
||||||
masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), holderReg);
|
|
||||||
} else if (isFixedSlot_) {
|
|
||||||
holderReg = objReg;
|
holderReg = objReg;
|
||||||
} else {
|
} else {
|
||||||
holderReg = regs.takeAny();
|
holderReg = regs.takeAny();
|
||||||
|
|
@ -4773,31 +4612,17 @@ ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler& masm)
|
||||||
regs.add(R0);
|
regs.add(R0);
|
||||||
regs.takeUnchecked(objReg);
|
regs.takeUnchecked(objReg);
|
||||||
|
|
||||||
if (obj_->is<UnboxedPlainObject>()) {
|
// Write the object's new shape.
|
||||||
holderReg = regs.takeAny();
|
Address shapeAddr(objReg, ShapedObject::offsetOfShape());
|
||||||
masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg);
|
EmitPreBarrier(masm, shapeAddr, MIRType::Shape);
|
||||||
|
masm.loadPtr(Address(ICStubReg, ICSetProp_NativeAdd::offsetOfNewShape()), scratch);
|
||||||
|
masm.storePtr(scratch, shapeAddr);
|
||||||
|
|
||||||
// Write the expando object's new shape.
|
if (isFixedSlot_) {
|
||||||
Address shapeAddr(holderReg, ShapedObject::offsetOfShape());
|
holderReg = objReg;
|
||||||
EmitPreBarrier(masm, shapeAddr, MIRType::Shape);
|
|
||||||
masm.loadPtr(Address(ICStubReg, ICSetProp_NativeAdd::offsetOfNewShape()), scratch);
|
|
||||||
masm.storePtr(scratch, shapeAddr);
|
|
||||||
|
|
||||||
if (!isFixedSlot_)
|
|
||||||
masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), holderReg);
|
|
||||||
} else {
|
} else {
|
||||||
// Write the object's new shape.
|
holderReg = regs.takeAny();
|
||||||
Address shapeAddr(objReg, ShapedObject::offsetOfShape());
|
masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), holderReg);
|
||||||
EmitPreBarrier(masm, shapeAddr, MIRType::Shape);
|
|
||||||
masm.loadPtr(Address(ICStubReg, ICSetProp_NativeAdd::offsetOfNewShape()), scratch);
|
|
||||||
masm.storePtr(scratch, shapeAddr);
|
|
||||||
|
|
||||||
if (isFixedSlot_) {
|
|
||||||
holderReg = objReg;
|
|
||||||
} else {
|
|
||||||
holderReg = regs.takeAny();
|
|
||||||
masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), holderReg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform the store. No write barrier required since this is a new
|
// Perform the store. No write barrier required since this is a new
|
||||||
|
|
@ -4828,70 +4653,6 @@ ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler& masm)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
ICSetProp_Unboxed::Compiler::generateStubCode(MacroAssembler& masm)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(engine_ == Engine::Baseline);
|
|
||||||
|
|
||||||
Label failure;
|
|
||||||
|
|
||||||
// Guard input is an object.
|
|
||||||
masm.branchTestObject(Assembler::NotEqual, R0, &failure);
|
|
||||||
|
|
||||||
AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
|
|
||||||
Register scratch = regs.takeAny();
|
|
||||||
|
|
||||||
// Unbox and group guard.
|
|
||||||
Register object = masm.extractObject(R0, ExtractTemp0);
|
|
||||||
masm.loadPtr(Address(ICStubReg, ICSetProp_Unboxed::offsetOfGroup()), scratch);
|
|
||||||
masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfGroup()), scratch,
|
|
||||||
&failure);
|
|
||||||
|
|
||||||
if (needsUpdateStubs()) {
|
|
||||||
// Stow both R0 and R1 (object and value).
|
|
||||||
EmitStowICValues(masm, 2);
|
|
||||||
|
|
||||||
// Move RHS into R0 for TypeUpdate check.
|
|
||||||
masm.moveValue(R1, R0);
|
|
||||||
|
|
||||||
// Call the type update stub.
|
|
||||||
if (!callTypeUpdateIC(masm, sizeof(Value)))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Unstow R0 and R1 (object and key)
|
|
||||||
EmitUnstowICValues(masm, 2);
|
|
||||||
|
|
||||||
// The TypeUpdate IC may have smashed object. Rederive it.
|
|
||||||
masm.unboxObject(R0, object);
|
|
||||||
|
|
||||||
// Trigger post barriers here on the values being written. Fields which
|
|
||||||
// objects can be written to also need update stubs.
|
|
||||||
LiveGeneralRegisterSet saveRegs;
|
|
||||||
saveRegs.add(R0);
|
|
||||||
saveRegs.add(R1);
|
|
||||||
saveRegs.addUnchecked(object);
|
|
||||||
saveRegs.add(ICStubReg);
|
|
||||||
emitPostWriteBarrierSlot(masm, object, R1, scratch, saveRegs);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute the address being written to.
|
|
||||||
masm.load32(Address(ICStubReg, ICSetProp_Unboxed::offsetOfFieldOffset()), scratch);
|
|
||||||
BaseIndex address(object, scratch, TimesOne);
|
|
||||||
|
|
||||||
EmitUnboxedPreBarrierForBaseline(masm, address, fieldType_);
|
|
||||||
masm.storeUnboxedProperty(address, fieldType_,
|
|
||||||
ConstantOrRegister(TypedOrValueRegister(R1)), &failure);
|
|
||||||
|
|
||||||
// The RHS has to be in R0.
|
|
||||||
masm.moveValue(R1, R0);
|
|
||||||
|
|
||||||
EmitReturnFromIC(masm);
|
|
||||||
|
|
||||||
masm.bind(&failure);
|
|
||||||
EmitStubGuardFailure(masm);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ICSetProp_TypedObject::Compiler::generateStubCode(MacroAssembler& masm)
|
ICSetProp_TypedObject::Compiler::generateStubCode(MacroAssembler& masm)
|
||||||
{
|
{
|
||||||
|
|
@ -5652,7 +5413,7 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb
|
||||||
if (!thisObject)
|
if (!thisObject)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (thisObject->is<PlainObject>() || thisObject->is<UnboxedPlainObject>())
|
if (thisObject->is<PlainObject>())
|
||||||
templateObject = thisObject;
|
templateObject = thisObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@
|
||||||
#include "jit/SharedICRegisters.h"
|
#include "jit/SharedICRegisters.h"
|
||||||
#include "js/GCVector.h"
|
#include "js/GCVector.h"
|
||||||
#include "vm/ArrayObject.h"
|
#include "vm/ArrayObject.h"
|
||||||
#include "vm/UnboxedObject.h"
|
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
namespace jit {
|
namespace jit {
|
||||||
|
|
@ -1823,8 +1822,7 @@ class ICSetProp_Native : public ICUpdatedStub
|
||||||
virtual int32_t getKey() const {
|
virtual int32_t getKey() const {
|
||||||
return static_cast<int32_t>(engine_) |
|
return static_cast<int32_t>(engine_) |
|
||||||
(static_cast<int32_t>(kind) << 1) |
|
(static_cast<int32_t>(kind) << 1) |
|
||||||
(static_cast<int32_t>(isFixedSlot_) << 17) |
|
(static_cast<int32_t>(isFixedSlot_) << 17);
|
||||||
(static_cast<int32_t>(obj_->is<UnboxedPlainObject>()) << 18);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
|
||||||
|
|
@ -1929,7 +1927,6 @@ class ICSetPropNativeAddCompiler : public ICStubCompiler
|
||||||
return static_cast<int32_t>(engine_) |
|
return static_cast<int32_t>(engine_) |
|
||||||
(static_cast<int32_t>(kind) << 1) |
|
(static_cast<int32_t>(kind) << 1) |
|
||||||
(static_cast<int32_t>(isFixedSlot_) << 17) |
|
(static_cast<int32_t>(isFixedSlot_) << 17) |
|
||||||
(static_cast<int32_t>(obj_->is<UnboxedPlainObject>()) << 18) |
|
|
||||||
(static_cast<int32_t>(protoChainDepth_) << 19);
|
(static_cast<int32_t>(protoChainDepth_) << 19);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1954,10 +1951,7 @@ class ICSetPropNativeAddCompiler : public ICStubCompiler
|
||||||
newGroup = nullptr;
|
newGroup = nullptr;
|
||||||
|
|
||||||
RootedShape newShape(cx);
|
RootedShape newShape(cx);
|
||||||
if (obj_->isNative())
|
newShape = obj_->as<NativeObject>().lastProperty();
|
||||||
newShape = obj_->as<NativeObject>().lastProperty();
|
|
||||||
else
|
|
||||||
newShape = obj_->as<UnboxedPlainObject>().maybeExpando()->lastProperty();
|
|
||||||
|
|
||||||
return newStub<ICSetProp_NativeAddImpl<ProtoChainDepth>>(
|
return newStub<ICSetProp_NativeAddImpl<ProtoChainDepth>>(
|
||||||
space, getStubCode(), oldGroup_, shapes, newShape, newGroup, offset_);
|
space, getStubCode(), oldGroup_, shapes, newShape, newGroup, offset_);
|
||||||
|
|
|
||||||
|
|
@ -104,19 +104,11 @@ AddReceiver(const ReceiverGuard& receiver,
|
||||||
static bool
|
static bool
|
||||||
GetCacheIRReceiverForNativeReadSlot(ICCacheIR_Monitored* stub, ReceiverGuard* receiver)
|
GetCacheIRReceiverForNativeReadSlot(ICCacheIR_Monitored* stub, ReceiverGuard* receiver)
|
||||||
{
|
{
|
||||||
// We match either:
|
// We match:
|
||||||
//
|
//
|
||||||
// GuardIsObject 0
|
// GuardIsObject 0
|
||||||
// GuardShape 0
|
// GuardShape 0
|
||||||
// LoadFixedSlotResult 0 or LoadDynamicSlotResult 0
|
// LoadFixedSlotResult 0 or LoadDynamicSlotResult 0
|
||||||
//
|
|
||||||
// or
|
|
||||||
//
|
|
||||||
// GuardIsObject 0
|
|
||||||
// GuardGroup 0
|
|
||||||
// 1: GuardAndLoadUnboxedExpando 0
|
|
||||||
// GuardShape 1
|
|
||||||
// LoadFixedSlotResult 1 or LoadDynamicSlotResult 1
|
|
||||||
|
|
||||||
*receiver = ReceiverGuard();
|
*receiver = ReceiverGuard();
|
||||||
CacheIRReader reader(stub->stubInfo());
|
CacheIRReader reader(stub->stubInfo());
|
||||||
|
|
@ -125,14 +117,6 @@ GetCacheIRReceiverForNativeReadSlot(ICCacheIR_Monitored* stub, ReceiverGuard* re
|
||||||
if (!reader.matchOp(CacheOp::GuardIsObject, objId))
|
if (!reader.matchOp(CacheOp::GuardIsObject, objId))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (reader.matchOp(CacheOp::GuardGroup, objId)) {
|
|
||||||
receiver->group = stub->stubInfo()->getStubField<ObjectGroup*>(stub, reader.stubOffset());
|
|
||||||
|
|
||||||
if (!reader.matchOp(CacheOp::GuardAndLoadUnboxedExpando, objId))
|
|
||||||
return false;
|
|
||||||
objId = reader.objOperandId();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reader.matchOp(CacheOp::GuardShape, objId)) {
|
if (reader.matchOp(CacheOp::GuardShape, objId)) {
|
||||||
receiver->shape = stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
|
receiver->shape = stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
|
||||||
return reader.matchOpEither(CacheOp::LoadFixedSlotResult, CacheOp::LoadDynamicSlotResult);
|
return reader.matchOpEither(CacheOp::LoadFixedSlotResult, CacheOp::LoadDynamicSlotResult);
|
||||||
|
|
@ -141,29 +125,6 @@ GetCacheIRReceiverForNativeReadSlot(ICCacheIR_Monitored* stub, ReceiverGuard* re
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
GetCacheIRReceiverForUnboxedProperty(ICCacheIR_Monitored* stub, ReceiverGuard* receiver)
|
|
||||||
{
|
|
||||||
// We match:
|
|
||||||
//
|
|
||||||
// GuardIsObject 0
|
|
||||||
// GuardGroup 0
|
|
||||||
// LoadUnboxedPropertyResult 0 ..
|
|
||||||
|
|
||||||
*receiver = ReceiverGuard();
|
|
||||||
CacheIRReader reader(stub->stubInfo());
|
|
||||||
|
|
||||||
ObjOperandId objId = ObjOperandId(0);
|
|
||||||
if (!reader.matchOp(CacheOp::GuardIsObject, objId))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!reader.matchOp(CacheOp::GuardGroup, objId))
|
|
||||||
return false;
|
|
||||||
receiver->group = stub->stubInfo()->getStubField<ObjectGroup*>(stub, reader.stubOffset());
|
|
||||||
|
|
||||||
return reader.matchOp(CacheOp::LoadUnboxedPropertyResult, objId);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers)
|
BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers)
|
||||||
{
|
{
|
||||||
|
|
@ -182,8 +143,7 @@ BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receiv
|
||||||
while (stub->next()) {
|
while (stub->next()) {
|
||||||
ReceiverGuard receiver;
|
ReceiverGuard receiver;
|
||||||
if (stub->isCacheIR_Monitored()) {
|
if (stub->isCacheIR_Monitored()) {
|
||||||
if (!GetCacheIRReceiverForNativeReadSlot(stub->toCacheIR_Monitored(), &receiver) &&
|
if (!GetCacheIRReceiverForNativeReadSlot(stub->toCacheIR_Monitored(), &receiver))
|
||||||
!GetCacheIRReceiverForUnboxedProperty(stub->toCacheIR_Monitored(), &receiver))
|
|
||||||
{
|
{
|
||||||
receivers.clear();
|
receivers.clear();
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -191,8 +151,6 @@ BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receiv
|
||||||
} else if (stub->isSetProp_Native()) {
|
} else if (stub->isSetProp_Native()) {
|
||||||
receiver = ReceiverGuard(stub->toSetProp_Native()->group(),
|
receiver = ReceiverGuard(stub->toSetProp_Native()->group(),
|
||||||
stub->toSetProp_Native()->shape());
|
stub->toSetProp_Native()->shape());
|
||||||
} else if (stub->isSetProp_Unboxed()) {
|
|
||||||
receiver = ReceiverGuard(stub->toSetProp_Unboxed()->group(), nullptr);
|
|
||||||
} else {
|
} else {
|
||||||
receivers.clear();
|
receivers.clear();
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,7 @@
|
||||||
#include "jit/IonCaches.h"
|
#include "jit/IonCaches.h"
|
||||||
|
|
||||||
#include "jsobjinlines.h"
|
#include "jsobjinlines.h"
|
||||||
|
#include "vm/NativeObject-inl.h"
|
||||||
#include "vm/UnboxedObject-inl.h"
|
|
||||||
|
|
||||||
using namespace js;
|
using namespace js;
|
||||||
using namespace js::jit;
|
using namespace js::jit;
|
||||||
|
|
@ -60,10 +59,6 @@ GetPropIRGenerator::tryAttachStub(Maybe<CacheIRWriter>& writer)
|
||||||
return false;
|
return false;
|
||||||
if (!emitted_ && !tryAttachNative(*writer, obj, objId))
|
if (!emitted_ && !tryAttachNative(*writer, obj, objId))
|
||||||
return false;
|
return false;
|
||||||
if (!emitted_ && !tryAttachUnboxed(*writer, obj, objId))
|
|
||||||
return false;
|
|
||||||
if (!emitted_ && !tryAttachUnboxedExpando(*writer, obj, objId))
|
|
||||||
return false;
|
|
||||||
if (!emitted_ && !tryAttachTypedObject(*writer, obj, objId))
|
if (!emitted_ && !tryAttachTypedObject(*writer, obj, objId))
|
||||||
return false;
|
return false;
|
||||||
if (!emitted_ && !tryAttachModuleNamespace(*writer, obj, objId))
|
if (!emitted_ && !tryAttachModuleNamespace(*writer, obj, objId))
|
||||||
|
|
@ -163,19 +158,9 @@ GeneratePrototypeGuards(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
TestMatchingReceiver(CacheIRWriter& writer, JSObject* obj, Shape* shape, ObjOperandId objId,
|
TestMatchingReceiver(CacheIRWriter& writer, JSObject* obj, Shape* shape, ObjOperandId objId)
|
||||||
Maybe<ObjOperandId>* expandoId)
|
|
||||||
{
|
{
|
||||||
if (obj->is<UnboxedPlainObject>()) {
|
if (obj->is<TypedObject>()) {
|
||||||
writer.guardGroup(objId, obj->group());
|
|
||||||
|
|
||||||
if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
|
|
||||||
expandoId->emplace(writer.guardAndLoadUnboxedExpando(objId));
|
|
||||||
writer.guardShape(expandoId->ref(), expando->lastProperty());
|
|
||||||
} else {
|
|
||||||
writer.guardNoUnboxedExpando(objId);
|
|
||||||
}
|
|
||||||
} else if (obj->is<TypedObject>()) {
|
|
||||||
writer.guardGroup(objId, obj->group());
|
writer.guardGroup(objId, obj->group());
|
||||||
} else {
|
} else {
|
||||||
Shape* shape = obj->maybeShape();
|
Shape* shape = obj->maybeShape();
|
||||||
|
|
@ -188,8 +173,7 @@ static void
|
||||||
EmitReadSlotResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
|
EmitReadSlotResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
|
||||||
Shape* shape, ObjOperandId objId)
|
Shape* shape, ObjOperandId objId)
|
||||||
{
|
{
|
||||||
Maybe<ObjOperandId> expandoId;
|
TestMatchingReceiver(writer, obj, shape, objId);
|
||||||
TestMatchingReceiver(writer, obj, shape, objId, &expandoId);
|
|
||||||
|
|
||||||
ObjOperandId holderId;
|
ObjOperandId holderId;
|
||||||
if (obj != holder) {
|
if (obj != holder) {
|
||||||
|
|
@ -212,9 +196,6 @@ EmitReadSlotResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
|
||||||
lastObjId = protoId;
|
lastObjId = protoId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (obj->is<UnboxedPlainObject>()) {
|
|
||||||
holder = obj->as<UnboxedPlainObject>().maybeExpando();
|
|
||||||
holderId = *expandoId;
|
|
||||||
} else {
|
} else {
|
||||||
holderId = objId;
|
holderId = objId;
|
||||||
}
|
}
|
||||||
|
|
@ -265,51 +246,6 @@ GetPropIRGenerator::tryAttachNative(CacheIRWriter& writer, HandleObject obj, Obj
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
GetPropIRGenerator::tryAttachUnboxed(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(!emitted_);
|
|
||||||
|
|
||||||
if (!obj->is<UnboxedPlainObject>())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(name_);
|
|
||||||
if (!property)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (!cx_->runtime()->jitSupportsFloatingPoint)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
writer.guardGroup(objId, obj->group());
|
|
||||||
writer.loadUnboxedPropertyResult(objId, property->type,
|
|
||||||
UnboxedPlainObject::offsetOfData() + property->offset);
|
|
||||||
emitted_ = true;
|
|
||||||
preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
GetPropIRGenerator::tryAttachUnboxedExpando(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(!emitted_);
|
|
||||||
|
|
||||||
if (!obj->is<UnboxedPlainObject>())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
|
|
||||||
if (!expando)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
Shape* shape = expando->lookup(cx_, NameToId(name_));
|
|
||||||
if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
emitted_ = true;
|
|
||||||
|
|
||||||
EmitReadSlotResult(writer, obj, obj, shape, objId);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
GetPropIRGenerator::tryAttachTypedObject(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId)
|
GetPropIRGenerator::tryAttachTypedObject(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -87,13 +87,10 @@ class ObjOperandId : public OperandId
|
||||||
_(GuardClass) \
|
_(GuardClass) \
|
||||||
_(GuardSpecificObject) \
|
_(GuardSpecificObject) \
|
||||||
_(GuardNoDetachedTypedObjects) \
|
_(GuardNoDetachedTypedObjects) \
|
||||||
_(GuardNoUnboxedExpando) \
|
|
||||||
_(GuardAndLoadUnboxedExpando) \
|
|
||||||
_(LoadObject) \
|
_(LoadObject) \
|
||||||
_(LoadProto) \
|
_(LoadProto) \
|
||||||
_(LoadFixedSlotResult) \
|
_(LoadFixedSlotResult) \
|
||||||
_(LoadDynamicSlotResult) \
|
_(LoadDynamicSlotResult) \
|
||||||
_(LoadUnboxedPropertyResult) \
|
|
||||||
_(LoadTypedObjectResult) \
|
_(LoadTypedObjectResult) \
|
||||||
_(LoadInt32ArrayLengthResult) \
|
_(LoadInt32ArrayLengthResult) \
|
||||||
_(LoadArgumentsObjectLengthResult) \
|
_(LoadArgumentsObjectLengthResult) \
|
||||||
|
|
@ -274,15 +271,6 @@ class MOZ_RAII CacheIRWriter
|
||||||
void guardNoDetachedTypedObjects() {
|
void guardNoDetachedTypedObjects() {
|
||||||
writeOp(CacheOp::GuardNoDetachedTypedObjects);
|
writeOp(CacheOp::GuardNoDetachedTypedObjects);
|
||||||
}
|
}
|
||||||
void guardNoUnboxedExpando(ObjOperandId obj) {
|
|
||||||
writeOpWithOperandId(CacheOp::GuardNoUnboxedExpando, obj);
|
|
||||||
}
|
|
||||||
ObjOperandId guardAndLoadUnboxedExpando(ObjOperandId obj) {
|
|
||||||
ObjOperandId res(nextOperandId_++);
|
|
||||||
writeOpWithOperandId(CacheOp::GuardAndLoadUnboxedExpando, obj);
|
|
||||||
writeOperandId(res);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjOperandId loadObject(JSObject* obj) {
|
ObjOperandId loadObject(JSObject* obj) {
|
||||||
ObjOperandId res(nextOperandId_++);
|
ObjOperandId res(nextOperandId_++);
|
||||||
|
|
@ -308,11 +296,6 @@ class MOZ_RAII CacheIRWriter
|
||||||
writeOpWithOperandId(CacheOp::LoadDynamicSlotResult, obj);
|
writeOpWithOperandId(CacheOp::LoadDynamicSlotResult, obj);
|
||||||
addStubWord(offset, StubField::GCType::NoGCThing);
|
addStubWord(offset, StubField::GCType::NoGCThing);
|
||||||
}
|
}
|
||||||
void loadUnboxedPropertyResult(ObjOperandId obj, JSValueType type, size_t offset) {
|
|
||||||
writeOpWithOperandId(CacheOp::LoadUnboxedPropertyResult, obj);
|
|
||||||
buffer_.writeByte(uint32_t(type));
|
|
||||||
addStubWord(offset, StubField::GCType::NoGCThing);
|
|
||||||
}
|
|
||||||
void loadTypedObjectResult(ObjOperandId obj, uint32_t offset, TypedThingLayout layout,
|
void loadTypedObjectResult(ObjOperandId obj, uint32_t offset, TypedThingLayout layout,
|
||||||
uint32_t typeDescr) {
|
uint32_t typeDescr) {
|
||||||
MOZ_ASSERT(uint32_t(layout) <= UINT8_MAX);
|
MOZ_ASSERT(uint32_t(layout) <= UINT8_MAX);
|
||||||
|
|
@ -406,9 +389,6 @@ class MOZ_RAII GetPropIRGenerator
|
||||||
PreliminaryObjectAction preliminaryObjectAction_;
|
PreliminaryObjectAction preliminaryObjectAction_;
|
||||||
|
|
||||||
MOZ_MUST_USE bool tryAttachNative(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId);
|
MOZ_MUST_USE bool tryAttachNative(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId);
|
||||||
MOZ_MUST_USE bool tryAttachUnboxed(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId);
|
|
||||||
MOZ_MUST_USE bool tryAttachUnboxedExpando(CacheIRWriter& writer, HandleObject obj,
|
|
||||||
ObjOperandId objId);
|
|
||||||
MOZ_MUST_USE bool tryAttachTypedObject(CacheIRWriter& writer, HandleObject obj,
|
MOZ_MUST_USE bool tryAttachTypedObject(CacheIRWriter& writer, HandleObject obj,
|
||||||
ObjOperandId objId);
|
ObjOperandId objId);
|
||||||
MOZ_MUST_USE bool tryAttachObjectLength(CacheIRWriter& writer, HandleObject obj,
|
MOZ_MUST_USE bool tryAttachObjectLength(CacheIRWriter& writer, HandleObject obj,
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
#include "builtin/Eval.h"
|
#include "builtin/Eval.h"
|
||||||
#include "builtin/TypedObject.h"
|
#include "builtin/TypedObject.h"
|
||||||
#include "gc/Nursery.h"
|
#include "gc/Nursery.h"
|
||||||
|
#include "gc/StoreBuffer-inl.h"
|
||||||
#include "irregexp/NativeRegExpMacroAssembler.h"
|
#include "irregexp/NativeRegExpMacroAssembler.h"
|
||||||
#include "jit/AtomicOperations.h"
|
#include "jit/AtomicOperations.h"
|
||||||
#include "jit/BaselineCompiler.h"
|
#include "jit/BaselineCompiler.h"
|
||||||
|
|
@ -3027,7 +3028,7 @@ CodeGenerator::visitStoreSlotV(LStoreSlotV* lir)
|
||||||
|
|
||||||
static void
|
static void
|
||||||
GuardReceiver(MacroAssembler& masm, const ReceiverGuard& guard,
|
GuardReceiver(MacroAssembler& masm, const ReceiverGuard& guard,
|
||||||
Register obj, Register scratch, Label* miss, bool checkNullExpando)
|
Register obj, Register scratch, Label* miss)
|
||||||
{
|
{
|
||||||
if (guard.group) {
|
if (guard.group) {
|
||||||
masm.branchTestObjGroup(Assembler::NotEqual, obj, guard.group, miss);
|
masm.branchTestObjGroup(Assembler::NotEqual, obj, guard.group, miss);
|
||||||
|
|
@ -3049,13 +3050,11 @@ CodeGenerator::emitGetPropertyPolymorphic(LInstruction* ins, Register obj, Regis
|
||||||
|
|
||||||
Label next;
|
Label next;
|
||||||
masm.comment("GuardReceiver");
|
masm.comment("GuardReceiver");
|
||||||
GuardReceiver(masm, receiver, obj, scratch, &next, /* checkNullExpando = */ false);
|
GuardReceiver(masm, receiver, obj, scratch, &next);
|
||||||
|
|
||||||
if (receiver.shape) {
|
if (receiver.shape) {
|
||||||
masm.comment("loadTypedOrValue");
|
masm.comment("loadTypedOrValue");
|
||||||
// If this is an unboxed expando access, GuardReceiver loaded the
|
Register target = obj;
|
||||||
// expando object into scratch.
|
|
||||||
Register target = receiver.group ? scratch : obj;
|
|
||||||
|
|
||||||
Shape* shape = mir->shape(i);
|
Shape* shape = mir->shape(i);
|
||||||
if (shape->slot() < shape->numFixedSlots()) {
|
if (shape->slot() < shape->numFixedSlots()) {
|
||||||
|
|
@ -3121,12 +3120,10 @@ CodeGenerator::emitSetPropertyPolymorphic(LInstruction* ins, Register obj, Regis
|
||||||
ReceiverGuard receiver = mir->receiver(i);
|
ReceiverGuard receiver = mir->receiver(i);
|
||||||
|
|
||||||
Label next;
|
Label next;
|
||||||
GuardReceiver(masm, receiver, obj, scratch, &next, /* checkNullExpando = */ false);
|
GuardReceiver(masm, receiver, obj, scratch, &next);
|
||||||
|
|
||||||
if (receiver.shape) {
|
if (receiver.shape) {
|
||||||
// If this is an unboxed expando access, GuardReceiver loaded the
|
Register target = obj;
|
||||||
// expando object into scratch.
|
|
||||||
Register target = receiver.group ? scratch : obj;
|
|
||||||
|
|
||||||
Shape* shape = mir->shape(i);
|
Shape* shape = mir->shape(i);
|
||||||
if (shape->slot() < shape->numFixedSlots()) {
|
if (shape->slot() < shape->numFixedSlots()) {
|
||||||
|
|
@ -3292,7 +3289,7 @@ CodeGenerator::visitGuardReceiverPolymorphic(LGuardReceiverPolymorphic* lir)
|
||||||
const ReceiverGuard& receiver = mir->receiver(i);
|
const ReceiverGuard& receiver = mir->receiver(i);
|
||||||
|
|
||||||
Label next;
|
Label next;
|
||||||
GuardReceiver(masm, receiver, obj, temp, &next, /* checkNullExpando = */ true);
|
GuardReceiver(masm, receiver, obj, temp, &next);
|
||||||
|
|
||||||
if (i == mir->numReceivers() - 1) {
|
if (i == mir->numReceivers() - 1) {
|
||||||
bailoutFrom(&next, lir->snapshot());
|
bailoutFrom(&next, lir->snapshot());
|
||||||
|
|
@ -8382,11 +8379,6 @@ CodeGenerator::visitStoreUnboxedPointer(LStoreUnboxedPointer* lir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef bool (*ConvertUnboxedObjectToNativeFn)(JSContext*, JSObject*);
|
|
||||||
static const VMFunction ConvertUnboxedPlainObjectToNativeInfo =
|
|
||||||
FunctionInfo<ConvertUnboxedObjectToNativeFn>(UnboxedPlainObject::convertToNative,
|
|
||||||
"UnboxedPlainObject::convertToNative");
|
|
||||||
|
|
||||||
typedef bool (*ArrayPopShiftFn)(JSContext*, HandleObject, MutableHandleValue);
|
typedef bool (*ArrayPopShiftFn)(JSContext*, HandleObject, MutableHandleValue);
|
||||||
static const VMFunction ArrayPopDenseInfo =
|
static const VMFunction ArrayPopDenseInfo =
|
||||||
FunctionInfo<ArrayPopShiftFn>(jit::ArrayPopDense, "ArrayPopDense");
|
FunctionInfo<ArrayPopShiftFn>(jit::ArrayPopDense, "ArrayPopDense");
|
||||||
|
|
@ -8679,11 +8671,11 @@ CodeGenerator::visitIteratorStartO(LIteratorStartO* lir)
|
||||||
masm.loadPtr(Address(niTemp, offsetof(NativeIterator, guard_array)), temp2);
|
masm.loadPtr(Address(niTemp, offsetof(NativeIterator, guard_array)), temp2);
|
||||||
|
|
||||||
// Compare object with the first receiver guard. The last iterator can only
|
// Compare object with the first receiver guard. The last iterator can only
|
||||||
// match for native objects and unboxed objects.
|
// match for native objects.
|
||||||
{
|
{
|
||||||
Address groupAddr(temp2, offsetof(ReceiverGuard, group));
|
Address groupAddr(temp2, offsetof(ReceiverGuard, group));
|
||||||
Address shapeAddr(temp2, offsetof(ReceiverGuard, shape));
|
Address shapeAddr(temp2, offsetof(ReceiverGuard, shape));
|
||||||
Label guardDone, shapeMismatch, noExpando;
|
Label guardDone, shapeMismatch;
|
||||||
masm.loadObjShape(obj, temp1);
|
masm.loadObjShape(obj, temp1);
|
||||||
masm.branchPtr(Assembler::NotEqual, shapeAddr, temp1, &shapeMismatch);
|
masm.branchPtr(Assembler::NotEqual, shapeAddr, temp1, &shapeMismatch);
|
||||||
|
|
||||||
|
|
@ -8695,12 +8687,6 @@ CodeGenerator::visitIteratorStartO(LIteratorStartO* lir)
|
||||||
masm.bind(&shapeMismatch);
|
masm.bind(&shapeMismatch);
|
||||||
masm.loadObjGroup(obj, temp1);
|
masm.loadObjGroup(obj, temp1);
|
||||||
masm.branchPtr(Assembler::NotEqual, groupAddr, temp1, ool->entry());
|
masm.branchPtr(Assembler::NotEqual, groupAddr, temp1, ool->entry());
|
||||||
masm.loadPtr(Address(obj, UnboxedPlainObject::offsetOfExpando()), temp1);
|
|
||||||
masm.branchTestPtr(Assembler::Zero, temp1, temp1, &noExpando);
|
|
||||||
branchIfNotEmptyObjectElements(temp1, ool->entry());
|
|
||||||
masm.loadObjShape(temp1, temp1);
|
|
||||||
masm.bind(&noExpando);
|
|
||||||
masm.branchPtr(Assembler::NotEqual, shapeAddr, temp1, ool->entry());
|
|
||||||
masm.bind(&guardDone);
|
masm.bind(&guardDone);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2218,8 +2218,6 @@ IonBuilder::inspectOpcode(JSOp op)
|
||||||
// update that stale value.
|
// update that stale value.
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
// Any unused opcodes and JSOP_LIMIT will end up here without having
|
|
||||||
// to explicitly specify
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -10925,63 +10923,6 @@ IonBuilder::getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_
|
||||||
return slot;
|
return slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t
|
|
||||||
IonBuilder::getUnboxedOffset(TemporaryTypeSet* types, PropertyName* name, JSValueType* punboxedType)
|
|
||||||
{
|
|
||||||
if (!types || types->unknownObject() || !types->objectOrSentinel()) {
|
|
||||||
trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
|
|
||||||
return UINT32_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t offset = UINT32_MAX;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < types->getObjectCount(); i++) {
|
|
||||||
TypeSet::ObjectKey* key = types->getObject(i);
|
|
||||||
if (!key)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (key->unknownProperties()) {
|
|
||||||
trackOptimizationOutcome(TrackedOutcome::UnknownProperties);
|
|
||||||
return UINT32_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key->isSingleton()) {
|
|
||||||
trackOptimizationOutcome(TrackedOutcome::Singleton);
|
|
||||||
return UINT32_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
UnboxedLayout* layout = key->group()->maybeUnboxedLayout();
|
|
||||||
if (!layout) {
|
|
||||||
trackOptimizationOutcome(TrackedOutcome::NotUnboxed);
|
|
||||||
return UINT32_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
const UnboxedLayout::Property* property = layout->lookup(name);
|
|
||||||
if (!property) {
|
|
||||||
trackOptimizationOutcome(TrackedOutcome::StructNoField);
|
|
||||||
return UINT32_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (layout->nativeGroup()) {
|
|
||||||
trackOptimizationOutcome(TrackedOutcome::UnboxedConvertedToNative);
|
|
||||||
return UINT32_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (offset == UINT32_MAX) {
|
|
||||||
offset = property->offset;
|
|
||||||
*punboxedType = property->type;
|
|
||||||
} else if (offset != property->offset) {
|
|
||||||
trackOptimizationOutcome(TrackedOutcome::InconsistentFieldOffset);
|
|
||||||
return UINT32_MAX;
|
|
||||||
} else if (*punboxedType != property->type) {
|
|
||||||
trackOptimizationOutcome(TrackedOutcome::InconsistentFieldType);
|
|
||||||
return UINT32_MAX;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
IonBuilder::jsop_runonce()
|
IonBuilder::jsop_runonce()
|
||||||
{
|
{
|
||||||
|
|
@ -11948,72 +11889,6 @@ IonBuilder::getPropTryModuleNamespace(bool* emitted, MDefinition* obj, PropertyN
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
MInstruction*
|
|
||||||
IonBuilder::loadUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType,
|
|
||||||
BarrierKind barrier, TemporaryTypeSet* types)
|
|
||||||
{
|
|
||||||
// loadUnboxedValue is designed to load any value as if it were contained in
|
|
||||||
// an array. Thus a property offset is converted to an index, when the
|
|
||||||
// object is reinterpreted as an array of properties of the same size.
|
|
||||||
size_t index = offset / UnboxedTypeSize(unboxedType);
|
|
||||||
MInstruction* indexConstant = MConstant::New(alloc(), Int32Value(index));
|
|
||||||
current->add(indexConstant);
|
|
||||||
|
|
||||||
return loadUnboxedValue(obj, UnboxedPlainObject::offsetOfData(),
|
|
||||||
indexConstant, unboxedType, barrier, types);
|
|
||||||
}
|
|
||||||
|
|
||||||
MInstruction*
|
|
||||||
IonBuilder::loadUnboxedValue(MDefinition* elements, size_t elementsOffset,
|
|
||||||
MDefinition* index, JSValueType unboxedType,
|
|
||||||
BarrierKind barrier, TemporaryTypeSet* types)
|
|
||||||
{
|
|
||||||
MInstruction* load;
|
|
||||||
switch (unboxedType) {
|
|
||||||
case JSVAL_TYPE_BOOLEAN:
|
|
||||||
load = MLoadUnboxedScalar::New(alloc(), elements, index, Scalar::Uint8,
|
|
||||||
DoesNotRequireMemoryBarrier, elementsOffset);
|
|
||||||
load->setResultType(MIRType::Boolean);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JSVAL_TYPE_INT32:
|
|
||||||
load = MLoadUnboxedScalar::New(alloc(), elements, index, Scalar::Int32,
|
|
||||||
DoesNotRequireMemoryBarrier, elementsOffset);
|
|
||||||
load->setResultType(MIRType::Int32);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JSVAL_TYPE_DOUBLE:
|
|
||||||
load = MLoadUnboxedScalar::New(alloc(), elements, index, Scalar::Float64,
|
|
||||||
DoesNotRequireMemoryBarrier, elementsOffset,
|
|
||||||
/* canonicalizeDoubles = */ false);
|
|
||||||
load->setResultType(MIRType::Double);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JSVAL_TYPE_STRING:
|
|
||||||
load = MLoadUnboxedString::New(alloc(), elements, index, elementsOffset);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JSVAL_TYPE_OBJECT: {
|
|
||||||
MLoadUnboxedObjectOrNull::NullBehavior nullBehavior;
|
|
||||||
if (types->hasType(TypeSet::NullType()))
|
|
||||||
nullBehavior = MLoadUnboxedObjectOrNull::HandleNull;
|
|
||||||
else if (barrier != BarrierKind::NoBarrier)
|
|
||||||
nullBehavior = MLoadUnboxedObjectOrNull::BailOnNull;
|
|
||||||
else
|
|
||||||
nullBehavior = MLoadUnboxedObjectOrNull::NullNotPossible;
|
|
||||||
load = MLoadUnboxedObjectOrNull::New(alloc(), elements, index, nullBehavior,
|
|
||||||
elementsOffset);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
MOZ_CRASH();
|
|
||||||
}
|
|
||||||
|
|
||||||
current->add(load);
|
|
||||||
return load;
|
|
||||||
}
|
|
||||||
|
|
||||||
MDefinition*
|
MDefinition*
|
||||||
IonBuilder::addShapeGuardsForGetterSetter(MDefinition* obj, JSObject* holder, Shape* holderShape,
|
IonBuilder::addShapeGuardsForGetterSetter(MDefinition* obj, JSObject* holder, Shape* holderShape,
|
||||||
const BaselineInspector::ReceiverVector& receivers,
|
const BaselineInspector::ReceiverVector& receivers,
|
||||||
|
|
@ -12835,66 +12710,6 @@ IonBuilder::setPropTryDefiniteSlot(bool* emitted, MDefinition* obj,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
MInstruction*
|
|
||||||
IonBuilder::storeUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType,
|
|
||||||
MDefinition* value)
|
|
||||||
{
|
|
||||||
size_t scaledOffsetConstant = offset / UnboxedTypeSize(unboxedType);
|
|
||||||
MInstruction* scaledOffset = MConstant::New(alloc(), Int32Value(scaledOffsetConstant));
|
|
||||||
current->add(scaledOffset);
|
|
||||||
|
|
||||||
return storeUnboxedValue(obj, obj, UnboxedPlainObject::offsetOfData(),
|
|
||||||
scaledOffset, unboxedType, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
MInstruction*
|
|
||||||
IonBuilder::storeUnboxedValue(MDefinition* obj, MDefinition* elements, int32_t elementsOffset,
|
|
||||||
MDefinition* scaledOffset, JSValueType unboxedType,
|
|
||||||
MDefinition* value, bool preBarrier /* = true */)
|
|
||||||
{
|
|
||||||
MInstruction* store;
|
|
||||||
switch (unboxedType) {
|
|
||||||
case JSVAL_TYPE_BOOLEAN:
|
|
||||||
store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Uint8,
|
|
||||||
MStoreUnboxedScalar::DontTruncateInput,
|
|
||||||
DoesNotRequireMemoryBarrier, elementsOffset);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JSVAL_TYPE_INT32:
|
|
||||||
store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Int32,
|
|
||||||
MStoreUnboxedScalar::DontTruncateInput,
|
|
||||||
DoesNotRequireMemoryBarrier, elementsOffset);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JSVAL_TYPE_DOUBLE:
|
|
||||||
store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Float64,
|
|
||||||
MStoreUnboxedScalar::DontTruncateInput,
|
|
||||||
DoesNotRequireMemoryBarrier, elementsOffset);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JSVAL_TYPE_STRING:
|
|
||||||
store = MStoreUnboxedString::New(alloc(), elements, scaledOffset, value,
|
|
||||||
elementsOffset, preBarrier);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JSVAL_TYPE_OBJECT:
|
|
||||||
MOZ_ASSERT(value->type() == MIRType::Object ||
|
|
||||||
value->type() == MIRType::Null ||
|
|
||||||
value->type() == MIRType::Value);
|
|
||||||
MOZ_ASSERT(!value->mightBeType(MIRType::Undefined),
|
|
||||||
"MToObjectOrNull slow path is invalid for unboxed objects");
|
|
||||||
store = MStoreUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, value, obj,
|
|
||||||
elementsOffset, preBarrier);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
MOZ_CRASH();
|
|
||||||
}
|
|
||||||
|
|
||||||
current->add(store);
|
|
||||||
return store;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
IonBuilder::setPropTryInlineAccess(bool* emitted, MDefinition* obj,
|
IonBuilder::setPropTryInlineAccess(bool* emitted, MDefinition* obj,
|
||||||
PropertyName* name, MDefinition* value,
|
PropertyName* name, MDefinition* value,
|
||||||
|
|
|
||||||
|
|
@ -1048,19 +1048,6 @@ class IonBuilder
|
||||||
ResultWithOOM<bool> testNotDefinedProperty(MDefinition* obj, jsid id);
|
ResultWithOOM<bool> testNotDefinedProperty(MDefinition* obj, jsid id);
|
||||||
|
|
||||||
uint32_t getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_t* pnfixed);
|
uint32_t getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_t* pnfixed);
|
||||||
uint32_t getUnboxedOffset(TemporaryTypeSet* types, PropertyName* name,
|
|
||||||
JSValueType* punboxedType);
|
|
||||||
MInstruction* loadUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType,
|
|
||||||
BarrierKind barrier, TemporaryTypeSet* types);
|
|
||||||
MInstruction* loadUnboxedValue(MDefinition* elements, size_t elementsOffset,
|
|
||||||
MDefinition* scaledOffset, JSValueType unboxedType,
|
|
||||||
BarrierKind barrier, TemporaryTypeSet* types);
|
|
||||||
MInstruction* storeUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType,
|
|
||||||
MDefinition* value);
|
|
||||||
MInstruction* storeUnboxedValue(MDefinition* obj,
|
|
||||||
MDefinition* elements, int32_t elementsOffset,
|
|
||||||
MDefinition* scaledOffset, JSValueType unboxedType,
|
|
||||||
MDefinition* value, bool preBarrier = true);
|
|
||||||
MOZ_MUST_USE bool checkPreliminaryGroups(MDefinition *obj);
|
MOZ_MUST_USE bool checkPreliminaryGroups(MDefinition *obj);
|
||||||
MOZ_MUST_USE bool freezePropTypeSets(TemporaryTypeSet* types,
|
MOZ_MUST_USE bool freezePropTypeSets(TemporaryTypeSet* types,
|
||||||
JSObject* foundProto, PropertyName* name);
|
JSObject* foundProto, PropertyName* name);
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@
|
||||||
#include "jit/shared/Lowering-shared-inl.h"
|
#include "jit/shared/Lowering-shared-inl.h"
|
||||||
#include "vm/Interpreter-inl.h"
|
#include "vm/Interpreter-inl.h"
|
||||||
#include "vm/Shape-inl.h"
|
#include "vm/Shape-inl.h"
|
||||||
#include "vm/UnboxedObject-inl.h"
|
|
||||||
|
|
||||||
using namespace js;
|
using namespace js;
|
||||||
using namespace js::jit;
|
using namespace js::jit;
|
||||||
|
|
@ -620,26 +619,7 @@ TestMatchingReceiver(MacroAssembler& masm, IonCache::StubAttacher& attacher,
|
||||||
Register object, JSObject* obj, Label* failure,
|
Register object, JSObject* obj, Label* failure,
|
||||||
bool alwaysCheckGroup = false)
|
bool alwaysCheckGroup = false)
|
||||||
{
|
{
|
||||||
if (obj->is<UnboxedPlainObject>()) {
|
if (obj->is<TypedObject>()) {
|
||||||
MOZ_ASSERT(failure);
|
|
||||||
|
|
||||||
masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failure);
|
|
||||||
Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando());
|
|
||||||
if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
|
|
||||||
masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failure);
|
|
||||||
Label success;
|
|
||||||
masm.push(object);
|
|
||||||
masm.loadPtr(expandoAddress, object);
|
|
||||||
masm.branchTestObjShape(Assembler::Equal, object, expando->lastProperty(),
|
|
||||||
&success);
|
|
||||||
masm.pop(object);
|
|
||||||
masm.jump(failure);
|
|
||||||
masm.bind(&success);
|
|
||||||
masm.pop(object);
|
|
||||||
} else {
|
|
||||||
masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), failure);
|
|
||||||
}
|
|
||||||
} else if (obj->is<TypedObject>()) {
|
|
||||||
attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
|
attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
|
||||||
Address(object, JSObject::offsetOfGroup()),
|
Address(object, JSObject::offsetOfGroup()),
|
||||||
ImmGCPtr(obj->group()), failure);
|
ImmGCPtr(obj->group()), failure);
|
||||||
|
|
@ -756,7 +736,6 @@ GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm,
|
||||||
// jump directly. Otherwise, jump to the end of the stub, so there's a
|
// jump directly. Otherwise, jump to the end of the stub, so there's a
|
||||||
// common point to patch.
|
// common point to patch.
|
||||||
bool multipleFailureJumps = (obj != holder)
|
bool multipleFailureJumps = (obj != holder)
|
||||||
|| obj->is<UnboxedPlainObject>()
|
|
||||||
|| (checkTDZ && output.hasValue())
|
|| (checkTDZ && output.hasValue())
|
||||||
|| (failures != nullptr && failures->used());
|
|| (failures != nullptr && failures->used());
|
||||||
|
|
||||||
|
|
@ -775,7 +754,6 @@ GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm,
|
||||||
Register scratchReg = Register::FromCode(0); // Quell compiler warning.
|
Register scratchReg = Register::FromCode(0); // Quell compiler warning.
|
||||||
|
|
||||||
if (obj != holder ||
|
if (obj != holder ||
|
||||||
obj->is<UnboxedPlainObject>() ||
|
|
||||||
!holder->as<NativeObject>().isFixedSlot(shape->slot()))
|
!holder->as<NativeObject>().isFixedSlot(shape->slot()))
|
||||||
{
|
{
|
||||||
if (output.hasValue()) {
|
if (output.hasValue()) {
|
||||||
|
|
@ -836,10 +814,6 @@ GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm,
|
||||||
|
|
||||||
holderReg = InvalidReg;
|
holderReg = InvalidReg;
|
||||||
}
|
}
|
||||||
} else if (obj->is<UnboxedPlainObject>()) {
|
|
||||||
holder = obj->as<UnboxedPlainObject>().maybeExpando();
|
|
||||||
holderReg = scratchReg;
|
|
||||||
masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), holderReg);
|
|
||||||
} else {
|
} else {
|
||||||
holderReg = object;
|
holderReg = object;
|
||||||
}
|
}
|
||||||
|
|
@ -867,30 +841,6 @@ GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm,
|
||||||
attacher.jumpNextStub(masm);
|
attacher.jumpNextStub(masm);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
GenerateReadUnboxed(JSContext* cx, IonScript* ion, MacroAssembler& masm,
|
|
||||||
IonCache::StubAttacher& attacher, JSObject* obj,
|
|
||||||
const UnboxedLayout::Property* property,
|
|
||||||
Register object, TypedOrValueRegister output,
|
|
||||||
Label* failures = nullptr)
|
|
||||||
{
|
|
||||||
// Guard on the group of the object.
|
|
||||||
attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
|
|
||||||
Address(object, JSObject::offsetOfGroup()),
|
|
||||||
ImmGCPtr(obj->group()), failures);
|
|
||||||
|
|
||||||
Address address(object, UnboxedPlainObject::offsetOfData() + property->offset);
|
|
||||||
|
|
||||||
masm.loadUnboxedProperty(address, property->type, output);
|
|
||||||
|
|
||||||
attacher.jumpRejoin(masm);
|
|
||||||
|
|
||||||
if (failures) {
|
|
||||||
masm.bind(failures);
|
|
||||||
attacher.jumpNextStub(masm);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
EmitGetterCall(JSContext* cx, MacroAssembler& masm,
|
EmitGetterCall(JSContext* cx, MacroAssembler& masm,
|
||||||
IonCache::StubAttacher& attacher, JSObject* obj,
|
IonCache::StubAttacher& attacher, JSObject* obj,
|
||||||
|
|
@ -1497,67 +1447,6 @@ GetPropertyIC::tryAttachNative(JSContext* cx, HandleScript outerScript, IonScrip
|
||||||
return linkAndAttachStub(cx, masm, attacher, ion, attachKind, outcome);
|
return linkAndAttachStub(cx, masm, attacher, ion, attachKind, outcome);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
GetPropertyIC::tryAttachUnboxed(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
|
||||||
HandleObject obj, HandleId id, void* returnAddr, bool* emitted)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(canAttachStub());
|
|
||||||
MOZ_ASSERT(!*emitted);
|
|
||||||
MOZ_ASSERT(outerScript->ionScript() == ion);
|
|
||||||
|
|
||||||
if (!obj->is<UnboxedPlainObject>())
|
|
||||||
return true;
|
|
||||||
const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
|
|
||||||
if (!property)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
*emitted = true;
|
|
||||||
|
|
||||||
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
|
|
||||||
|
|
||||||
Label failures;
|
|
||||||
emitIdGuard(masm, id, &failures);
|
|
||||||
Label* maybeFailures = failures.used() ? &failures : nullptr;
|
|
||||||
|
|
||||||
StubAttacher attacher(*this);
|
|
||||||
GenerateReadUnboxed(cx, ion, masm, attacher, obj, property, object(), output(), maybeFailures);
|
|
||||||
return linkAndAttachStub(cx, masm, attacher, ion, "read unboxed",
|
|
||||||
JS::TrackedOutcome::ICGetPropStub_UnboxedRead);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
GetPropertyIC::tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
|
||||||
HandleObject obj, HandleId id, void* returnAddr, bool* emitted)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(canAttachStub());
|
|
||||||
MOZ_ASSERT(!*emitted);
|
|
||||||
MOZ_ASSERT(outerScript->ionScript() == ion);
|
|
||||||
|
|
||||||
if (!obj->is<UnboxedPlainObject>())
|
|
||||||
return true;
|
|
||||||
Rooted<UnboxedExpandoObject*> expando(cx, obj->as<UnboxedPlainObject>().maybeExpando());
|
|
||||||
if (!expando)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
Shape* shape = expando->lookup(cx, id);
|
|
||||||
if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
*emitted = true;
|
|
||||||
|
|
||||||
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
|
|
||||||
|
|
||||||
Label failures;
|
|
||||||
emitIdGuard(masm, id, &failures);
|
|
||||||
Label* maybeFailures = failures.used() ? &failures : nullptr;
|
|
||||||
|
|
||||||
StubAttacher attacher(*this);
|
|
||||||
GenerateReadSlot(cx, ion, masm, attacher, DontCheckTDZ, obj, obj,
|
|
||||||
shape, object(), output(), maybeFailures);
|
|
||||||
return linkAndAttachStub(cx, masm, attacher, ion, "read unboxed expando",
|
|
||||||
JS::TrackedOutcome::ICGetPropStub_UnboxedReadExpando);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
GetPropertyIC::tryAttachTypedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
GetPropertyIC::tryAttachTypedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||||
HandleObject obj, HandleId id, bool* emitted)
|
HandleObject obj, HandleId id, bool* emitted)
|
||||||
|
|
@ -2127,12 +2016,6 @@ GetPropertyIC::tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript*
|
||||||
if (!*emitted && !tryAttachNative(cx, outerScript, ion, obj, id, returnAddr, emitted))
|
if (!*emitted && !tryAttachNative(cx, outerScript, ion, obj, id, returnAddr, emitted))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!*emitted && !tryAttachUnboxed(cx, outerScript, ion, obj, id, returnAddr, emitted))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!*emitted && !tryAttachUnboxedExpando(cx, outerScript, ion, obj, id, returnAddr, emitted))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!*emitted && !tryAttachTypedArrayLength(cx, outerScript, ion, obj, id, emitted))
|
if (!*emitted && !tryAttachTypedArrayLength(cx, outerScript, ion, obj, id, emitted))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -2311,12 +2194,6 @@ GenerateSetSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att
|
||||||
|
|
||||||
NativeObject::slotsSizeMustNotOverflow();
|
NativeObject::slotsSizeMustNotOverflow();
|
||||||
|
|
||||||
if (obj->is<UnboxedPlainObject>()) {
|
|
||||||
obj = obj->as<UnboxedPlainObject>().maybeExpando();
|
|
||||||
masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), tempReg);
|
|
||||||
object = tempReg;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obj->as<NativeObject>().isFixedSlot(shape->slot())) {
|
if (obj->as<NativeObject>().isFixedSlot(shape->slot())) {
|
||||||
Address addr(object, NativeObject::getFixedSlotOffset(shape->slot()));
|
Address addr(object, NativeObject::getFixedSlotOffset(shape->slot()));
|
||||||
|
|
||||||
|
|
@ -2954,23 +2831,13 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att
|
||||||
masm.branchTestObjGroup(Assembler::NotEqual, object, oldGroup, failures);
|
masm.branchTestObjGroup(Assembler::NotEqual, object, oldGroup, failures);
|
||||||
if (obj->maybeShape()) {
|
if (obj->maybeShape()) {
|
||||||
masm.branchTestObjShape(Assembler::NotEqual, object, oldShape, failures);
|
masm.branchTestObjShape(Assembler::NotEqual, object, oldShape, failures);
|
||||||
} else {
|
|
||||||
MOZ_ASSERT(obj->is<UnboxedPlainObject>());
|
|
||||||
|
|
||||||
Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando());
|
|
||||||
masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failures);
|
|
||||||
|
|
||||||
masm.loadPtr(expandoAddress, tempReg);
|
|
||||||
masm.branchTestObjShape(Assembler::NotEqual, tempReg, oldShape, failures);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Shape* newShape = obj->maybeShape();
|
Shape* newShape = obj->maybeShape();
|
||||||
if (!newShape)
|
|
||||||
newShape = obj->as<UnboxedPlainObject>().maybeExpando()->lastProperty();
|
|
||||||
|
|
||||||
// Guard that the incoming value is in the type set for the property
|
// Guard that the incoming value is in the type set for the property
|
||||||
// if a type barrier is required.
|
// if a type barrier is required.
|
||||||
if (checkTypeset)
|
if (newShape && checkTypeset)
|
||||||
CheckTypeSetForWrite(masm, obj, newShape->propid(), tempReg, value, failures);
|
CheckTypeSetForWrite(masm, obj, newShape->propid(), tempReg, value, failures);
|
||||||
|
|
||||||
// Guard shapes along prototype chain.
|
// Guard shapes along prototype chain.
|
||||||
|
|
@ -2991,9 +2858,7 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call a stub to (re)allocate dynamic slots, if necessary.
|
// Call a stub to (re)allocate dynamic slots, if necessary.
|
||||||
uint32_t newNumDynamicSlots = obj->is<UnboxedPlainObject>()
|
uint32_t newNumDynamicSlots = obj->as<NativeObject>().numDynamicSlots();
|
||||||
? obj->as<UnboxedPlainObject>().maybeExpando()->numDynamicSlots()
|
|
||||||
: obj->as<NativeObject>().numDynamicSlots();
|
|
||||||
if (NativeObject::dynamicSlotsCount(oldShape) != newNumDynamicSlots) {
|
if (NativeObject::dynamicSlotsCount(oldShape) != newNumDynamicSlots) {
|
||||||
AllocatableRegisterSet regs(RegisterSet::Volatile());
|
AllocatableRegisterSet regs(RegisterSet::Volatile());
|
||||||
LiveRegisterSet save(regs.asLiveSet());
|
LiveRegisterSet save(regs.asLiveSet());
|
||||||
|
|
@ -3004,12 +2869,6 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att
|
||||||
Register temp1 = regs.takeAnyGeneral();
|
Register temp1 = regs.takeAnyGeneral();
|
||||||
Register temp2 = regs.takeAnyGeneral();
|
Register temp2 = regs.takeAnyGeneral();
|
||||||
|
|
||||||
if (obj->is<UnboxedPlainObject>()) {
|
|
||||||
// Pass the expando object to the stub.
|
|
||||||
masm.Push(object);
|
|
||||||
masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), object);
|
|
||||||
}
|
|
||||||
|
|
||||||
masm.setupUnalignedABICall(temp1);
|
masm.setupUnalignedABICall(temp1);
|
||||||
masm.loadJSContext(temp1);
|
masm.loadJSContext(temp1);
|
||||||
masm.passABIArg(temp1);
|
masm.passABIArg(temp1);
|
||||||
|
|
@ -3026,27 +2885,16 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att
|
||||||
masm.jump(&allocDone);
|
masm.jump(&allocDone);
|
||||||
|
|
||||||
masm.bind(&allocFailed);
|
masm.bind(&allocFailed);
|
||||||
if (obj->is<UnboxedPlainObject>())
|
|
||||||
masm.Pop(object);
|
|
||||||
masm.PopRegsInMask(save);
|
masm.PopRegsInMask(save);
|
||||||
masm.jump(failures);
|
masm.jump(failures);
|
||||||
|
|
||||||
masm.bind(&allocDone);
|
masm.bind(&allocDone);
|
||||||
masm.setFramePushed(framePushedAfterCall);
|
masm.setFramePushed(framePushedAfterCall);
|
||||||
if (obj->is<UnboxedPlainObject>())
|
|
||||||
masm.Pop(object);
|
|
||||||
masm.PopRegsInMask(save);
|
masm.PopRegsInMask(save);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool popObject = false;
|
bool popObject = false;
|
||||||
|
|
||||||
if (obj->is<UnboxedPlainObject>()) {
|
|
||||||
masm.push(object);
|
|
||||||
popObject = true;
|
|
||||||
obj = obj->as<UnboxedPlainObject>().maybeExpando();
|
|
||||||
masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), object);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the object or expando object's new shape.
|
// Write the object or expando object's new shape.
|
||||||
Address shapeAddr(object, ShapedObject::offsetOfShape());
|
Address shapeAddr(object, ShapedObject::offsetOfShape());
|
||||||
if (cx->zone()->needsIncrementalBarrier())
|
if (cx->zone()->needsIncrementalBarrier())
|
||||||
|
|
@ -3054,8 +2902,6 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att
|
||||||
masm.storePtr(ImmGCPtr(newShape), shapeAddr);
|
masm.storePtr(ImmGCPtr(newShape), shapeAddr);
|
||||||
|
|
||||||
if (oldGroup != obj->group()) {
|
if (oldGroup != obj->group()) {
|
||||||
MOZ_ASSERT(!obj->is<UnboxedPlainObject>());
|
|
||||||
|
|
||||||
// Changing object's group from a partially to fully initialized group,
|
// Changing object's group from a partially to fully initialized group,
|
||||||
// per the acquired properties analysis. Only change the group if the
|
// per the acquired properties analysis. Only change the group if the
|
||||||
// old group still has a newScript.
|
// old group still has a newScript.
|
||||||
|
|
@ -3298,141 +3144,6 @@ CanAttachNativeSetProp(JSContext* cx, HandleObject obj, HandleId id, const Const
|
||||||
return SetPropertyIC::CanAttachNone;
|
return SetPropertyIC::CanAttachNone;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
GenerateSetUnboxed(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher,
|
|
||||||
JSObject* obj, jsid id, uint32_t unboxedOffset, JSValueType unboxedType,
|
|
||||||
Register object, Register tempReg, const ConstantOrRegister& value,
|
|
||||||
bool checkTypeset, Label* failures)
|
|
||||||
{
|
|
||||||
// Guard on the type of the object.
|
|
||||||
masm.branchPtr(Assembler::NotEqual,
|
|
||||||
Address(object, JSObject::offsetOfGroup()),
|
|
||||||
ImmGCPtr(obj->group()), failures);
|
|
||||||
|
|
||||||
if (checkTypeset)
|
|
||||||
CheckTypeSetForWrite(masm, obj, id, tempReg, value, failures);
|
|
||||||
|
|
||||||
Address address(object, UnboxedPlainObject::offsetOfData() + unboxedOffset);
|
|
||||||
|
|
||||||
if (cx->zone()->needsIncrementalBarrier()) {
|
|
||||||
if (unboxedType == JSVAL_TYPE_OBJECT)
|
|
||||||
masm.callPreBarrier(address, MIRType::Object);
|
|
||||||
else if (unboxedType == JSVAL_TYPE_STRING)
|
|
||||||
masm.callPreBarrier(address, MIRType::String);
|
|
||||||
else
|
|
||||||
MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(unboxedType));
|
|
||||||
}
|
|
||||||
|
|
||||||
masm.storeUnboxedProperty(address, unboxedType, value, failures);
|
|
||||||
|
|
||||||
attacher.jumpRejoin(masm);
|
|
||||||
|
|
||||||
masm.bind(failures);
|
|
||||||
attacher.jumpNextStub(masm);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
CanAttachSetUnboxed(JSContext* cx, HandleObject obj, HandleId id, const ConstantOrRegister& val,
|
|
||||||
bool needsTypeBarrier, bool* checkTypeset,
|
|
||||||
uint32_t* unboxedOffset, JSValueType* unboxedType)
|
|
||||||
{
|
|
||||||
if (!obj->is<UnboxedPlainObject>())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
|
|
||||||
if (property) {
|
|
||||||
*checkTypeset = false;
|
|
||||||
if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset))
|
|
||||||
return false;
|
|
||||||
*unboxedOffset = property->offset;
|
|
||||||
*unboxedType = property->type;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
CanAttachSetUnboxedExpando(JSContext* cx, HandleObject obj, HandleId id,
|
|
||||||
const ConstantOrRegister& val,
|
|
||||||
bool needsTypeBarrier, bool* checkTypeset, Shape** pshape)
|
|
||||||
{
|
|
||||||
if (!obj->is<UnboxedPlainObject>())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Rooted<UnboxedExpandoObject*> expando(cx, obj->as<UnboxedPlainObject>().maybeExpando());
|
|
||||||
if (!expando)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Shape* shape = expando->lookupPure(id);
|
|
||||||
if (!shape || !shape->hasDefaultSetter() || !shape->hasSlot() || !shape->writable())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
*checkTypeset = false;
|
|
||||||
if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
*pshape = shape;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
CanAttachAddUnboxedExpando(JSContext* cx, HandleObject obj, HandleShape oldShape,
|
|
||||||
HandleId id, const ConstantOrRegister& val,
|
|
||||||
bool needsTypeBarrier, bool* checkTypeset)
|
|
||||||
{
|
|
||||||
if (!obj->is<UnboxedPlainObject>())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Rooted<UnboxedExpandoObject*> expando(cx, obj->as<UnboxedPlainObject>().maybeExpando());
|
|
||||||
if (!expando || expando->inDictionaryMode())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Shape* newShape = expando->lastProperty();
|
|
||||||
if (newShape->isEmptyShape() || newShape->propid() != id || newShape->previous() != oldShape)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
MOZ_ASSERT(newShape->hasDefaultSetter() && newShape->hasSlot() && newShape->writable());
|
|
||||||
|
|
||||||
if (PrototypeChainShadowsPropertyAdd(cx, obj, id))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
*checkTypeset = false;
|
|
||||||
if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
SetPropertyIC::tryAttachUnboxed(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
|
||||||
HandleObject obj, HandleId id, bool* emitted)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(!*emitted);
|
|
||||||
|
|
||||||
bool checkTypeset = false;
|
|
||||||
uint32_t unboxedOffset;
|
|
||||||
JSValueType unboxedType;
|
|
||||||
if (!CanAttachSetUnboxed(cx, obj, id, value(), needsTypeBarrier(), &checkTypeset,
|
|
||||||
&unboxedOffset, &unboxedType))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
*emitted = true;
|
|
||||||
|
|
||||||
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
|
|
||||||
StubAttacher attacher(*this);
|
|
||||||
|
|
||||||
Label failures;
|
|
||||||
emitIdGuard(masm, id, &failures);
|
|
||||||
|
|
||||||
GenerateSetUnboxed(cx, masm, attacher, obj, id, unboxedOffset, unboxedType,
|
|
||||||
object(), temp(), value(), checkTypeset, &failures);
|
|
||||||
return linkAndAttachStub(cx, masm, attacher, ion, "set_unboxed",
|
|
||||||
JS::TrackedOutcome::ICSetPropStub_SetUnboxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
SetPropertyIC::tryAttachProxy(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
SetPropertyIC::tryAttachProxy(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||||
HandleObject obj, HandleId id, bool* emitted)
|
HandleObject obj, HandleId id, bool* emitted)
|
||||||
|
|
@ -3513,26 +3224,6 @@ SetPropertyIC::tryAttachNative(JSContext* cx, HandleScript outerScript, IonScrip
|
||||||
MOZ_CRASH("Unreachable");
|
MOZ_CRASH("Unreachable");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
SetPropertyIC::tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
|
||||||
HandleObject obj, HandleId id, bool* emitted)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(!*emitted);
|
|
||||||
|
|
||||||
RootedShape shape(cx);
|
|
||||||
bool checkTypeset = false;
|
|
||||||
if (!CanAttachSetUnboxedExpando(cx, obj, id, value(), needsTypeBarrier(),
|
|
||||||
&checkTypeset, shape.address()))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!attachSetSlot(cx, outerScript, ion, obj, shape, checkTypeset))
|
|
||||||
return false;
|
|
||||||
*emitted = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
SetPropertyIC::tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
SetPropertyIC::tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
||||||
HandleObject obj, HandleValue idval, HandleValue value,
|
HandleObject obj, HandleValue idval, HandleValue value,
|
||||||
|
|
@ -3558,12 +3249,6 @@ SetPropertyIC::tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript*
|
||||||
|
|
||||||
if (!*emitted && !tryAttachNative(cx, outerScript, ion, obj, id, emitted, tryNativeAddSlot))
|
if (!*emitted && !tryAttachNative(cx, outerScript, ion, obj, id, emitted, tryNativeAddSlot))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!*emitted && !tryAttachUnboxed(cx, outerScript, ion, obj, id, emitted))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!*emitted && !tryAttachUnboxedExpando(cx, outerScript, ion, obj, id, emitted))
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (idval.isInt32()) {
|
if (idval.isInt32()) {
|
||||||
|
|
@ -3615,16 +3300,6 @@ SetPropertyIC::tryAttachAddSlot(JSContext* cx, HandleScript outerScript, IonScri
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkTypeset = false;
|
|
||||||
if (CanAttachAddUnboxedExpando(cx, obj, oldShape, id, value(), needsTypeBarrier(),
|
|
||||||
&checkTypeset))
|
|
||||||
{
|
|
||||||
if (!attachAddSlot(cx, outerScript, ion, obj, id, oldShape, oldGroup, checkTypeset))
|
|
||||||
return false;
|
|
||||||
*emitted = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3646,11 +3321,6 @@ SetPropertyIC::update(JSContext* cx, HandleScript outerScript, size_t cacheIndex
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
oldShape = obj->maybeShape();
|
oldShape = obj->maybeShape();
|
||||||
if (obj->is<UnboxedPlainObject>()) {
|
|
||||||
MOZ_ASSERT(!oldShape);
|
|
||||||
if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
|
|
||||||
oldShape = expando->lastProperty();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RootedId id(cx);
|
RootedId id(cx);
|
||||||
|
|
|
||||||
|
|
@ -529,18 +529,6 @@ class GetPropertyIC : public IonCache
|
||||||
HandleObject obj, HandleId id, void* returnAddr,
|
HandleObject obj, HandleId id, void* returnAddr,
|
||||||
bool* emitted);
|
bool* emitted);
|
||||||
|
|
||||||
MOZ_MUST_USE bool tryAttachUnboxed(JSContext* cx, HandleScript outerScript, IonScript* ion,
|
|
||||||
HandleObject obj, HandleId id, void* returnAddr,
|
|
||||||
bool* emitted);
|
|
||||||
|
|
||||||
MOZ_MUST_USE bool tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript,
|
|
||||||
IonScript* ion, HandleObject obj, HandleId id,
|
|
||||||
void* returnAddr, bool* emitted);
|
|
||||||
|
|
||||||
MOZ_MUST_USE bool tryAttachUnboxedArrayLength(JSContext* cx, HandleScript outerScript,
|
|
||||||
IonScript* ion, HandleObject obj, HandleId id,
|
|
||||||
void* returnAddr, bool* emitted);
|
|
||||||
|
|
||||||
MOZ_MUST_USE bool tryAttachTypedArrayLength(JSContext* cx, HandleScript outerScript,
|
MOZ_MUST_USE bool tryAttachTypedArrayLength(JSContext* cx, HandleScript outerScript,
|
||||||
IonScript* ion, HandleObject obj, HandleId id,
|
IonScript* ion, HandleObject obj, HandleId id,
|
||||||
bool* emitted);
|
bool* emitted);
|
||||||
|
|
|
||||||
|
|
@ -221,9 +221,6 @@ DefaultJitOptions::DefaultJitOptions()
|
||||||
Warn(forcedRegisterAllocatorEnv, env);
|
Warn(forcedRegisterAllocatorEnv, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toggles whether unboxed plain objects can be created by the VM.
|
|
||||||
SET_DEFAULT(disableUnboxedObjects, true);
|
|
||||||
|
|
||||||
// Test whether Atomics are allowed in asm.js code.
|
// Test whether Atomics are allowed in asm.js code.
|
||||||
SET_DEFAULT(asmJSAtomicsEnable, false);
|
SET_DEFAULT(asmJSAtomicsEnable, false);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -91,9 +91,6 @@ struct DefaultJitOptions
|
||||||
mozilla::Maybe<uint32_t> forcedDefaultIonSmallFunctionWarmUpThreshold;
|
mozilla::Maybe<uint32_t> forcedDefaultIonSmallFunctionWarmUpThreshold;
|
||||||
mozilla::Maybe<IonRegisterAllocator> forcedRegisterAllocator;
|
mozilla::Maybe<IonRegisterAllocator> forcedRegisterAllocator;
|
||||||
|
|
||||||
// The options below affect the rest of the VM, and not just the JIT.
|
|
||||||
bool disableUnboxedObjects;
|
|
||||||
|
|
||||||
DefaultJitOptions();
|
DefaultJitOptions();
|
||||||
bool isSmallFunction(JSScript* script) const;
|
bool isSmallFunction(JSScript* script) const;
|
||||||
void setEagerCompilation();
|
void setEagerCompilation();
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@
|
||||||
#include "jit/shared/Lowering-shared-inl.h"
|
#include "jit/shared/Lowering-shared-inl.h"
|
||||||
#include "vm/NativeObject-inl.h"
|
#include "vm/NativeObject-inl.h"
|
||||||
#include "vm/StringObject-inl.h"
|
#include "vm/StringObject-inl.h"
|
||||||
#include "vm/UnboxedObject-inl.h"
|
|
||||||
|
|
||||||
using mozilla::ArrayLength;
|
using mozilla::ArrayLength;
|
||||||
using mozilla::AssertedCast;
|
using mozilla::AssertedCast;
|
||||||
|
|
|
||||||
|
|
@ -4817,15 +4817,14 @@ MCreateThisWithTemplate::canRecoverOnBailout() const
|
||||||
|
|
||||||
MObjectState::MObjectState(MObjectState* state)
|
MObjectState::MObjectState(MObjectState* state)
|
||||||
: numSlots_(state->numSlots_),
|
: numSlots_(state->numSlots_),
|
||||||
numFixedSlots_(state->numFixedSlots_),
|
numFixedSlots_(state->numFixedSlots_)
|
||||||
operandIndex_(state->operandIndex_)
|
|
||||||
{
|
{
|
||||||
// This instruction is only used as a summary for bailout paths.
|
// This instruction is only used as a summary for bailout paths.
|
||||||
setResultType(MIRType::Object);
|
setResultType(MIRType::Object);
|
||||||
setRecoveredOnBailout();
|
setRecoveredOnBailout();
|
||||||
}
|
}
|
||||||
|
|
||||||
MObjectState::MObjectState(JSObject *templateObject, OperandIndexMap* operandIndex)
|
MObjectState::MObjectState(JSObject* templateObject)
|
||||||
{
|
{
|
||||||
// This instruction is only used as a summary for bailout paths.
|
// This instruction is only used as a summary for bailout paths.
|
||||||
setResultType(MIRType::Object);
|
setResultType(MIRType::Object);
|
||||||
|
|
@ -4836,8 +4835,6 @@ MObjectState::MObjectState(JSObject *templateObject, OperandIndexMap* operandInd
|
||||||
NativeObject* nativeObject = &templateObject->as<NativeObject>();
|
NativeObject* nativeObject = &templateObject->as<NativeObject>();
|
||||||
numSlots_ = nativeObject->slotSpan();
|
numSlots_ = nativeObject->slotSpan();
|
||||||
numFixedSlots_ = nativeObject->numFixedSlots();
|
numFixedSlots_ = nativeObject->numFixedSlots();
|
||||||
|
|
||||||
operandIndex_ = operandIndex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JSObject*
|
JSObject*
|
||||||
|
|
@ -4897,7 +4894,7 @@ MObjectState::New(TempAllocator& alloc, MDefinition* obj)
|
||||||
JSObject* templateObject = templateObjectOf(obj);
|
JSObject* templateObject = templateObjectOf(obj);
|
||||||
MOZ_ASSERT(templateObject, "Unexpected object creation.");
|
MOZ_ASSERT(templateObject, "Unexpected object creation.");
|
||||||
|
|
||||||
MObjectState* res = new(alloc) MObjectState(templateObject, nullptr);
|
MObjectState* res = new(alloc) MObjectState(templateObject);
|
||||||
if (!res || !res->init(alloc, obj))
|
if (!res || !res->init(alloc, obj))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
return res;
|
return res;
|
||||||
|
|
|
||||||
|
|
@ -375,7 +375,7 @@ class AliasSet {
|
||||||
Element = 1 << 1, // A Value member of obj->elements or
|
Element = 1 << 1, // A Value member of obj->elements or
|
||||||
// a typed object.
|
// a typed object.
|
||||||
UnboxedElement = 1 << 2, // An unboxed scalar or reference member of
|
UnboxedElement = 1 << 2, // An unboxed scalar or reference member of
|
||||||
// typed object or unboxed object.
|
// typed object.
|
||||||
DynamicSlot = 1 << 3, // A Value member of obj->slots.
|
DynamicSlot = 1 << 3, // A Value member of obj->slots.
|
||||||
FixedSlot = 1 << 4, // A Value member of obj->fixedSlots().
|
FixedSlot = 1 << 4, // A Value member of obj->fixedSlots().
|
||||||
DOMProperty = 1 << 5, // A DOM property
|
DOMProperty = 1 << 5, // A DOM property
|
||||||
|
|
@ -3758,14 +3758,9 @@ class MObjectState
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
uint32_t numSlots_;
|
uint32_t numSlots_;
|
||||||
uint32_t numFixedSlots_; // valid if isUnboxed() == false.
|
uint32_t numFixedSlots_;
|
||||||
OperandIndexMap* operandIndex_; // valid if isUnboxed() == true.
|
|
||||||
|
|
||||||
bool isUnboxed() const {
|
MObjectState(JSObject *templateObject);
|
||||||
return operandIndex_ != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
MObjectState(JSObject *templateObject, OperandIndexMap* operandIndex);
|
|
||||||
explicit MObjectState(MObjectState* state);
|
explicit MObjectState(MObjectState* state);
|
||||||
|
|
||||||
MOZ_MUST_USE bool init(TempAllocator& alloc, MDefinition* obj);
|
MOZ_MUST_USE bool init(TempAllocator& alloc, MDefinition* obj);
|
||||||
|
|
@ -3826,18 +3821,6 @@ class MObjectState
|
||||||
setSlot(slot + numFixedSlots(), def);
|
setSlot(slot + numFixedSlots(), def);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interface reserved for unboxed objects.
|
|
||||||
bool hasOffset(uint32_t offset) const {
|
|
||||||
MOZ_ASSERT(isUnboxed());
|
|
||||||
return offset < operandIndex_->map.length() && operandIndex_->map[offset] != 0;
|
|
||||||
}
|
|
||||||
MDefinition* getOffset(uint32_t offset) const {
|
|
||||||
return getOperand(operandIndex_->map[offset]);
|
|
||||||
}
|
|
||||||
void setOffset(uint32_t offset, MDefinition* def) {
|
|
||||||
replaceOperand(operandIndex_->map[offset], def);
|
|
||||||
}
|
|
||||||
|
|
||||||
MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
|
MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
|
||||||
bool canRecoverOnBailout() const override {
|
bool canRecoverOnBailout() const override {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -126,20 +126,14 @@ MacroAssembler::guardTypeSetMightBeIncomplete(TypeSet* types, Register obj, Regi
|
||||||
{
|
{
|
||||||
// Type set guards might miss when an object's group changes. In this case
|
// Type set guards might miss when an object's group changes. In this case
|
||||||
// either its old group's properties will become unknown, or it will change
|
// either its old group's properties will become unknown, or it will change
|
||||||
// to a native object with an original unboxed group. Jump to label if this
|
// to a native object. Jump to label if this might have happened for the
|
||||||
// might have happened for the input object.
|
// input object.
|
||||||
|
|
||||||
if (types->unknownObject()) {
|
if (types->unknownObject()) {
|
||||||
jump(label);
|
jump(label);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
loadPtr(Address(obj, JSObject::offsetOfGroup()), scratch);
|
|
||||||
load32(Address(scratch, ObjectGroup::offsetOfFlags()), scratch);
|
|
||||||
and32(Imm32(OBJECT_FLAG_ADDENDUM_MASK), scratch);
|
|
||||||
branch32(Assembler::Equal,
|
|
||||||
scratch, Imm32(ObjectGroup::addendumOriginalUnboxedGroupValue()), label);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < types->getObjectCount(); i++) {
|
for (size_t i = 0; i < types->getObjectCount(); i++) {
|
||||||
if (JSObject* singleton = types->getSingletonNoBarrier(i)) {
|
if (JSObject* singleton = types->getSingletonNoBarrier(i)) {
|
||||||
movePtr(ImmGCPtr(singleton), scratch);
|
movePtr(ImmGCPtr(singleton), scratch);
|
||||||
|
|
@ -468,243 +462,6 @@ template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const A
|
||||||
template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const BaseIndex& src, const ValueOperand& dest,
|
template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const BaseIndex& src, const ValueOperand& dest,
|
||||||
bool allowDouble, Register temp, Label* fail);
|
bool allowDouble, Register temp, Label* fail);
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void
|
|
||||||
MacroAssembler::loadUnboxedProperty(T address, JSValueType type, TypedOrValueRegister output)
|
|
||||||
{
|
|
||||||
switch (type) {
|
|
||||||
case JSVAL_TYPE_INT32: {
|
|
||||||
// Handle loading an int32 into a double reg.
|
|
||||||
if (output.type() == MIRType::Double) {
|
|
||||||
convertInt32ToDouble(address, output.typedReg().fpu());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
MOZ_FALLTHROUGH;
|
|
||||||
}
|
|
||||||
|
|
||||||
case JSVAL_TYPE_BOOLEAN:
|
|
||||||
case JSVAL_TYPE_STRING: {
|
|
||||||
Register outReg;
|
|
||||||
if (output.hasValue()) {
|
|
||||||
outReg = output.valueReg().scratchReg();
|
|
||||||
} else {
|
|
||||||
MOZ_ASSERT(output.type() == MIRTypeFromValueType(type));
|
|
||||||
outReg = output.typedReg().gpr();
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case JSVAL_TYPE_BOOLEAN:
|
|
||||||
load8ZeroExtend(address, outReg);
|
|
||||||
break;
|
|
||||||
case JSVAL_TYPE_INT32:
|
|
||||||
load32(address, outReg);
|
|
||||||
break;
|
|
||||||
case JSVAL_TYPE_STRING:
|
|
||||||
loadPtr(address, outReg);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
MOZ_CRASH();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (output.hasValue())
|
|
||||||
tagValue(type, outReg, output.valueReg());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case JSVAL_TYPE_OBJECT:
|
|
||||||
if (output.hasValue()) {
|
|
||||||
Register scratch = output.valueReg().scratchReg();
|
|
||||||
loadPtr(address, scratch);
|
|
||||||
|
|
||||||
Label notNull, done;
|
|
||||||
branchPtr(Assembler::NotEqual, scratch, ImmWord(0), ¬Null);
|
|
||||||
|
|
||||||
moveValue(NullValue(), output.valueReg());
|
|
||||||
jump(&done);
|
|
||||||
|
|
||||||
bind(¬Null);
|
|
||||||
tagValue(JSVAL_TYPE_OBJECT, scratch, output.valueReg());
|
|
||||||
|
|
||||||
bind(&done);
|
|
||||||
} else {
|
|
||||||
// Reading null can't be possible here, as otherwise the result
|
|
||||||
// would be a value (either because null has been read before or
|
|
||||||
// because there is a barrier).
|
|
||||||
Register reg = output.typedReg().gpr();
|
|
||||||
loadPtr(address, reg);
|
|
||||||
#ifdef DEBUG
|
|
||||||
Label ok;
|
|
||||||
branchTestPtr(Assembler::NonZero, reg, reg, &ok);
|
|
||||||
assumeUnreachable("Null not possible");
|
|
||||||
bind(&ok);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JSVAL_TYPE_DOUBLE:
|
|
||||||
// Note: doubles in unboxed objects are not accessed through other
|
|
||||||
// views and do not need canonicalization.
|
|
||||||
if (output.hasValue())
|
|
||||||
loadValue(address, output.valueReg());
|
|
||||||
else
|
|
||||||
loadDouble(address, output.typedReg().fpu());
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
MOZ_CRASH();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template void
|
|
||||||
MacroAssembler::loadUnboxedProperty(Address address, JSValueType type,
|
|
||||||
TypedOrValueRegister output);
|
|
||||||
|
|
||||||
template void
|
|
||||||
MacroAssembler::loadUnboxedProperty(BaseIndex address, JSValueType type,
|
|
||||||
TypedOrValueRegister output);
|
|
||||||
|
|
||||||
static void
|
|
||||||
StoreUnboxedFailure(MacroAssembler& masm, Label* failure)
|
|
||||||
{
|
|
||||||
// Storing a value to an unboxed property is a fallible operation and
|
|
||||||
// the caller must provide a failure label if a particular unboxed store
|
|
||||||
// might fail. Sometimes, however, a store that cannot succeed (such as
|
|
||||||
// storing a string to an int32 property) will be marked as infallible.
|
|
||||||
// This can only happen if the code involved is unreachable.
|
|
||||||
if (failure)
|
|
||||||
masm.jump(failure);
|
|
||||||
else
|
|
||||||
masm.assumeUnreachable("Incompatible write to unboxed property");
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void
|
|
||||||
MacroAssembler::storeUnboxedProperty(T address, JSValueType type,
|
|
||||||
const ConstantOrRegister& value, Label* failure)
|
|
||||||
{
|
|
||||||
switch (type) {
|
|
||||||
case JSVAL_TYPE_BOOLEAN:
|
|
||||||
if (value.constant()) {
|
|
||||||
if (value.value().isBoolean())
|
|
||||||
store8(Imm32(value.value().toBoolean()), address);
|
|
||||||
else
|
|
||||||
StoreUnboxedFailure(*this, failure);
|
|
||||||
} else if (value.reg().hasTyped()) {
|
|
||||||
if (value.reg().type() == MIRType::Boolean)
|
|
||||||
store8(value.reg().typedReg().gpr(), address);
|
|
||||||
else
|
|
||||||
StoreUnboxedFailure(*this, failure);
|
|
||||||
} else {
|
|
||||||
if (failure)
|
|
||||||
branchTestBoolean(Assembler::NotEqual, value.reg().valueReg(), failure);
|
|
||||||
storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ 1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JSVAL_TYPE_INT32:
|
|
||||||
if (value.constant()) {
|
|
||||||
if (value.value().isInt32())
|
|
||||||
store32(Imm32(value.value().toInt32()), address);
|
|
||||||
else
|
|
||||||
StoreUnboxedFailure(*this, failure);
|
|
||||||
} else if (value.reg().hasTyped()) {
|
|
||||||
if (value.reg().type() == MIRType::Int32)
|
|
||||||
store32(value.reg().typedReg().gpr(), address);
|
|
||||||
else
|
|
||||||
StoreUnboxedFailure(*this, failure);
|
|
||||||
} else {
|
|
||||||
if (failure)
|
|
||||||
branchTestInt32(Assembler::NotEqual, value.reg().valueReg(), failure);
|
|
||||||
storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ 4);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JSVAL_TYPE_DOUBLE:
|
|
||||||
if (value.constant()) {
|
|
||||||
if (value.value().isNumber()) {
|
|
||||||
loadConstantDouble(value.value().toNumber(), ScratchDoubleReg);
|
|
||||||
storeDouble(ScratchDoubleReg, address);
|
|
||||||
} else {
|
|
||||||
StoreUnboxedFailure(*this, failure);
|
|
||||||
}
|
|
||||||
} else if (value.reg().hasTyped()) {
|
|
||||||
if (value.reg().type() == MIRType::Int32) {
|
|
||||||
convertInt32ToDouble(value.reg().typedReg().gpr(), ScratchDoubleReg);
|
|
||||||
storeDouble(ScratchDoubleReg, address);
|
|
||||||
} else if (value.reg().type() == MIRType::Double) {
|
|
||||||
storeDouble(value.reg().typedReg().fpu(), address);
|
|
||||||
} else {
|
|
||||||
StoreUnboxedFailure(*this, failure);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ValueOperand reg = value.reg().valueReg();
|
|
||||||
Label notInt32, end;
|
|
||||||
branchTestInt32(Assembler::NotEqual, reg, ¬Int32);
|
|
||||||
int32ValueToDouble(reg, ScratchDoubleReg);
|
|
||||||
storeDouble(ScratchDoubleReg, address);
|
|
||||||
jump(&end);
|
|
||||||
bind(¬Int32);
|
|
||||||
if (failure)
|
|
||||||
branchTestDouble(Assembler::NotEqual, reg, failure);
|
|
||||||
storeValue(reg, address);
|
|
||||||
bind(&end);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JSVAL_TYPE_OBJECT:
|
|
||||||
if (value.constant()) {
|
|
||||||
if (value.value().isObjectOrNull())
|
|
||||||
storePtr(ImmGCPtr(value.value().toObjectOrNull()), address);
|
|
||||||
else
|
|
||||||
StoreUnboxedFailure(*this, failure);
|
|
||||||
} else if (value.reg().hasTyped()) {
|
|
||||||
MOZ_ASSERT(value.reg().type() != MIRType::Null);
|
|
||||||
if (value.reg().type() == MIRType::Object)
|
|
||||||
storePtr(value.reg().typedReg().gpr(), address);
|
|
||||||
else
|
|
||||||
StoreUnboxedFailure(*this, failure);
|
|
||||||
} else {
|
|
||||||
if (failure) {
|
|
||||||
Label ok;
|
|
||||||
branchTestNull(Assembler::Equal, value.reg().valueReg(), &ok);
|
|
||||||
branchTestObject(Assembler::NotEqual, value.reg().valueReg(), failure);
|
|
||||||
bind(&ok);
|
|
||||||
}
|
|
||||||
storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ sizeof(uintptr_t));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JSVAL_TYPE_STRING:
|
|
||||||
if (value.constant()) {
|
|
||||||
if (value.value().isString())
|
|
||||||
storePtr(ImmGCPtr(value.value().toString()), address);
|
|
||||||
else
|
|
||||||
StoreUnboxedFailure(*this, failure);
|
|
||||||
} else if (value.reg().hasTyped()) {
|
|
||||||
if (value.reg().type() == MIRType::String)
|
|
||||||
storePtr(value.reg().typedReg().gpr(), address);
|
|
||||||
else
|
|
||||||
StoreUnboxedFailure(*this, failure);
|
|
||||||
} else {
|
|
||||||
if (failure)
|
|
||||||
branchTestString(Assembler::NotEqual, value.reg().valueReg(), failure);
|
|
||||||
storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ sizeof(uintptr_t));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
MOZ_CRASH();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template void
|
|
||||||
MacroAssembler::storeUnboxedProperty(Address address, JSValueType type,
|
|
||||||
const ConstantOrRegister& value, Label* failure);
|
|
||||||
|
|
||||||
template void
|
|
||||||
MacroAssembler::storeUnboxedProperty(BaseIndex address, JSValueType type,
|
|
||||||
const ConstantOrRegister& value, Label* failure);
|
|
||||||
|
|
||||||
// Inlined version of gc::CheckAllocatorState that checks the bare essentials
|
// Inlined version of gc::CheckAllocatorState that checks the bare essentials
|
||||||
// and bails for anything that cannot be handled with our jit allocators.
|
// and bails for anything that cannot be handled with our jit allocators.
|
||||||
void
|
void
|
||||||
|
|
@ -1252,10 +1009,6 @@ MacroAssembler::initGCThing(Register obj, Register temp, JSObject* templateObj,
|
||||||
nbytes = (nbytes < sizeof(uintptr_t)) ? 0 : nbytes - sizeof(uintptr_t);
|
nbytes = (nbytes < sizeof(uintptr_t)) ? 0 : nbytes - sizeof(uintptr_t);
|
||||||
offset += sizeof(uintptr_t);
|
offset += sizeof(uintptr_t);
|
||||||
}
|
}
|
||||||
} else if (templateObj->is<UnboxedPlainObject>()) {
|
|
||||||
storePtr(ImmWord(0), Address(obj, UnboxedPlainObject::offsetOfExpando()));
|
|
||||||
if (initContents)
|
|
||||||
initUnboxedObjectContents(obj, &templateObj->as<UnboxedPlainObject>());
|
|
||||||
} else {
|
} else {
|
||||||
MOZ_CRASH("Unknown object");
|
MOZ_CRASH("Unknown object");
|
||||||
}
|
}
|
||||||
|
|
@ -1276,29 +1029,6 @@ MacroAssembler::initGCThing(Register obj, Register temp, JSObject* templateObj,
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
MacroAssembler::initUnboxedObjectContents(Register object, UnboxedPlainObject* templateObject)
|
|
||||||
{
|
|
||||||
const UnboxedLayout& layout = templateObject->layoutDontCheckGeneration();
|
|
||||||
|
|
||||||
// Initialize reference fields of the object, per UnboxedPlainObject::create.
|
|
||||||
if (const int32_t* list = layout.traceList()) {
|
|
||||||
while (*list != -1) {
|
|
||||||
storePtr(ImmGCPtr(GetJitContext()->runtime->names().empty),
|
|
||||||
Address(object, UnboxedPlainObject::offsetOfData() + *list));
|
|
||||||
list++;
|
|
||||||
}
|
|
||||||
list++;
|
|
||||||
while (*list != -1) {
|
|
||||||
storePtr(ImmWord(0),
|
|
||||||
Address(object, UnboxedPlainObject::offsetOfData() + *list));
|
|
||||||
list++;
|
|
||||||
}
|
|
||||||
// Unboxed objects don't have Values to initialize.
|
|
||||||
MOZ_ASSERT(*(list + 1) == -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::compareStrings(JSOp op, Register left, Register right, Register result,
|
MacroAssembler::compareStrings(JSOp op, Register left, Register right, Register result,
|
||||||
Label* fail)
|
Label* fail)
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,6 @@
|
||||||
#include "vm/ProxyObject.h"
|
#include "vm/ProxyObject.h"
|
||||||
#include "vm/Shape.h"
|
#include "vm/Shape.h"
|
||||||
#include "vm/TypedArrayObject.h"
|
#include "vm/TypedArrayObject.h"
|
||||||
#include "vm/UnboxedObject.h"
|
|
||||||
|
|
||||||
using mozilla::FloatingPoint;
|
using mozilla::FloatingPoint;
|
||||||
|
|
||||||
|
|
@ -1626,17 +1625,6 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||||
void storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value, const Address& dest,
|
void storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value, const Address& dest,
|
||||||
unsigned numElems = 0);
|
unsigned numElems = 0);
|
||||||
|
|
||||||
// Load a property from an UnboxedPlainObject.
|
|
||||||
template <typename T>
|
|
||||||
void loadUnboxedProperty(T address, JSValueType type, TypedOrValueRegister output);
|
|
||||||
|
|
||||||
// Store a property to an UnboxedPlainObject, without triggering barriers.
|
|
||||||
// If failure is null, the value definitely has a type suitable for storing
|
|
||||||
// in the property.
|
|
||||||
template <typename T>
|
|
||||||
void storeUnboxedProperty(T address, JSValueType type,
|
|
||||||
const ConstantOrRegister& value, Label* failure);
|
|
||||||
|
|
||||||
Register extractString(const Address& address, Register scratch) {
|
Register extractString(const Address& address, Register scratch) {
|
||||||
return extractObject(address, scratch);
|
return extractObject(address, scratch);
|
||||||
}
|
}
|
||||||
|
|
@ -1713,8 +1701,6 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||||
LiveRegisterSet liveRegs, Label* fail,
|
LiveRegisterSet liveRegs, Label* fail,
|
||||||
TypedArrayObject* templateObj, TypedArrayLength lengthKind);
|
TypedArrayObject* templateObj, TypedArrayLength lengthKind);
|
||||||
|
|
||||||
void initUnboxedObjectContents(Register object, UnboxedPlainObject* templateObject);
|
|
||||||
|
|
||||||
void newGCString(Register result, Register temp, Label* fail);
|
void newGCString(Register result, Register temp, Label* fail);
|
||||||
void newGCFatInlineString(Register result, Register temp, Label* fail);
|
void newGCFatInlineString(Register result, Register temp, Label* fail);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,9 @@
|
||||||
#include "jit/JitcodeMap.h"
|
#include "jit/JitcodeMap.h"
|
||||||
#include "jit/JitSpewer.h"
|
#include "jit/JitSpewer.h"
|
||||||
#include "js/TrackedOptimizationInfo.h"
|
#include "js/TrackedOptimizationInfo.h"
|
||||||
#include "vm/UnboxedObject.h"
|
|
||||||
|
|
||||||
#include "vm/ObjectGroup-inl.h"
|
#include "vm/ObjectGroup-inl.h"
|
||||||
#include "vm/TypeInference-inl.h"
|
#include "vm/TypeInference-inl.h"
|
||||||
#include "vm/UnboxedObject-inl.h"
|
|
||||||
|
|
||||||
using namespace js;
|
using namespace js;
|
||||||
using namespace js::jit;
|
using namespace js::jit;
|
||||||
|
|
@ -846,8 +844,6 @@ MaybeConstructorFromType(TypeSet::Type ty)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
ObjectGroup* obj = ty.group();
|
ObjectGroup* obj = ty.group();
|
||||||
TypeNewScript* newScript = obj->newScript();
|
TypeNewScript* newScript = obj->newScript();
|
||||||
if (!newScript && obj->maybeUnboxedLayout())
|
|
||||||
newScript = obj->unboxedLayout().newScript();
|
|
||||||
return newScript ? newScript->function() : nullptr;
|
return newScript ? newScript->function() : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@
|
||||||
|
|
||||||
#include "vm/Interpreter-inl.h"
|
#include "vm/Interpreter-inl.h"
|
||||||
#include "vm/NativeObject-inl.h"
|
#include "vm/NativeObject-inl.h"
|
||||||
#include "vm/UnboxedObject-inl.h"
|
|
||||||
|
|
||||||
using namespace js;
|
using namespace js;
|
||||||
using namespace js::jit;
|
using namespace js::jit;
|
||||||
|
|
@ -1540,37 +1539,12 @@ RObjectState::recover(JSContext* cx, SnapshotIterator& iter) const
|
||||||
RootedObject object(cx, &iter.read().toObject());
|
RootedObject object(cx, &iter.read().toObject());
|
||||||
RootedValue val(cx);
|
RootedValue val(cx);
|
||||||
|
|
||||||
if (object->is<UnboxedPlainObject>()) {
|
RootedNativeObject nativeObject(cx, &object->as<NativeObject>());
|
||||||
const UnboxedLayout& layout = object->as<UnboxedPlainObject>().layout();
|
MOZ_ASSERT(nativeObject->slotSpan() == numSlots());
|
||||||
|
|
||||||
RootedId id(cx);
|
for (size_t i = 0; i < numSlots(); i++) {
|
||||||
RootedValue receiver(cx, ObjectValue(*object));
|
val = iter.read();
|
||||||
const UnboxedLayout::PropertyVector& properties = layout.properties();
|
nativeObject->setSlot(i, val);
|
||||||
for (size_t i = 0; i < properties.length(); i++) {
|
|
||||||
val = iter.read();
|
|
||||||
|
|
||||||
// This is the default placeholder value of MObjectState, when no
|
|
||||||
// properties are defined yet.
|
|
||||||
if (val.isUndefined())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
id = NameToId(properties[i].name);
|
|
||||||
ObjectOpResult result;
|
|
||||||
|
|
||||||
// SetProperty can only fail due to OOM.
|
|
||||||
if (!SetProperty(cx, object, id, val, receiver, result))
|
|
||||||
return false;
|
|
||||||
if (!result)
|
|
||||||
return result.reportError(cx, object, id);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
RootedNativeObject nativeObject(cx, &object->as<NativeObject>());
|
|
||||||
MOZ_ASSERT(nativeObject->slotSpan() == numSlots());
|
|
||||||
|
|
||||||
for (size_t i = 0; i < numSlots(); i++) {
|
|
||||||
val = iter.read();
|
|
||||||
nativeObject->setSlot(i, val);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val.setObject(*object);
|
val.setObject(*object);
|
||||||
|
|
|
||||||
|
|
@ -285,10 +285,6 @@ class ObjectMemoryView : public MDefinitionVisitorDefaultNoop
|
||||||
void visitGuardShape(MGuardShape* ins);
|
void visitGuardShape(MGuardShape* ins);
|
||||||
void visitFunctionEnvironment(MFunctionEnvironment* ins);
|
void visitFunctionEnvironment(MFunctionEnvironment* ins);
|
||||||
void visitLambda(MLambda* ins);
|
void visitLambda(MLambda* ins);
|
||||||
|
|
||||||
private:
|
|
||||||
void storeOffset(MInstruction* ins, size_t offset, MDefinition* value);
|
|
||||||
void loadOffset(MInstruction* ins, size_t offset);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const char* ObjectMemoryView::phaseName = "Scalar Replacement of Object";
|
const char* ObjectMemoryView::phaseName = "Scalar Replacement of Object";
|
||||||
|
|
@ -630,35 +626,6 @@ ObjectMemoryView::visitLambda(MLambda* ins)
|
||||||
ins->setIncompleteObject();
|
ins->setIncompleteObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
ObjectMemoryView::storeOffset(MInstruction* ins, size_t offset, MDefinition* value)
|
|
||||||
{
|
|
||||||
// Clone the state and update the slot value.
|
|
||||||
MOZ_ASSERT(state_->hasOffset(offset));
|
|
||||||
state_ = BlockState::Copy(alloc_, state_);
|
|
||||||
if (!state_) {
|
|
||||||
oom_ = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
state_->setOffset(offset, value);
|
|
||||||
ins->block()->insertBefore(ins, state_);
|
|
||||||
|
|
||||||
// Remove original instruction.
|
|
||||||
ins->block()->discard(ins);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ObjectMemoryView::loadOffset(MInstruction* ins, size_t offset)
|
|
||||||
{
|
|
||||||
// Replace load by the slot value.
|
|
||||||
MOZ_ASSERT(state_->hasOffset(offset));
|
|
||||||
ins->replaceAllUsesWith(state_->getOffset(offset));
|
|
||||||
|
|
||||||
// Remove original instruction.
|
|
||||||
ins->block()->discard(ins);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
IndexOf(MDefinition* ins, int32_t* res)
|
IndexOf(MDefinition* ins, int32_t* res)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2244,8 +2244,7 @@ IsCacheableProtoChain(JSObject* obj, JSObject* holder, bool isDOMProxy)
|
||||||
if (!isDOMProxy && !obj->isNative()) {
|
if (!isDOMProxy && !obj->isNative()) {
|
||||||
if (obj == holder)
|
if (obj == holder)
|
||||||
return false;
|
return false;
|
||||||
if (!obj->is<UnboxedPlainObject>() &&
|
if (!obj->is<TypedObject>())
|
||||||
!obj->is<TypedObject>())
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -2573,9 +2572,6 @@ CheckHasNoSuchProperty(JSContext* cx, JSObject* obj, PropertyName* name,
|
||||||
} else if (curObj != obj) {
|
} else if (curObj != obj) {
|
||||||
// Non-native objects are only handled as the original receiver.
|
// Non-native objects are only handled as the original receiver.
|
||||||
return false;
|
return false;
|
||||||
} else if (curObj->is<UnboxedPlainObject>()) {
|
|
||||||
if (curObj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, NameToId(name)))
|
|
||||||
return false;
|
|
||||||
} else if (curObj->is<TypedObject>()) {
|
} else if (curObj->is<TypedObject>()) {
|
||||||
if (curObj->as<TypedObject>().typeDescr().hasProperty(cx->names(), NameToId(name)))
|
if (curObj->as<TypedObject>().typeDescr().hasProperty(cx->names(), NameToId(name)))
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -2840,34 +2836,15 @@ GuardReceiverObject(MacroAssembler& masm, ReceiverGuard guard,
|
||||||
{
|
{
|
||||||
Address groupAddress(ICStubReg, receiverGuardOffset + HeapReceiverGuard::offsetOfGroup());
|
Address groupAddress(ICStubReg, receiverGuardOffset + HeapReceiverGuard::offsetOfGroup());
|
||||||
Address shapeAddress(ICStubReg, receiverGuardOffset + HeapReceiverGuard::offsetOfShape());
|
Address shapeAddress(ICStubReg, receiverGuardOffset + HeapReceiverGuard::offsetOfShape());
|
||||||
Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando());
|
|
||||||
|
|
||||||
if (guard.group) {
|
if (guard.group) {
|
||||||
masm.loadPtr(groupAddress, scratch);
|
masm.loadPtr(groupAddress, scratch);
|
||||||
masm.branchTestObjGroup(Assembler::NotEqual, object, scratch, failure);
|
masm.branchTestObjGroup(Assembler::NotEqual, object, scratch, failure);
|
||||||
|
|
||||||
if (guard.group->clasp() == &UnboxedPlainObject::class_ && !guard.shape) {
|
|
||||||
// Guard the unboxed object has no expando object.
|
|
||||||
masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), failure);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (guard.shape) {
|
if (guard.shape) {
|
||||||
masm.loadPtr(shapeAddress, scratch);
|
masm.loadPtr(shapeAddress, scratch);
|
||||||
if (guard.group && guard.group->clasp() == &UnboxedPlainObject::class_) {
|
masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure);
|
||||||
// Guard the unboxed object has a matching expando object.
|
|
||||||
masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failure);
|
|
||||||
Label done;
|
|
||||||
masm.push(object);
|
|
||||||
masm.loadPtr(expandoAddress, object);
|
|
||||||
masm.branchTestObjShape(Assembler::Equal, object, scratch, &done);
|
|
||||||
masm.pop(object);
|
|
||||||
masm.jump(failure);
|
|
||||||
masm.bind(&done);
|
|
||||||
masm.pop(object);
|
|
||||||
} else {
|
|
||||||
masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4251,8 +4228,7 @@ DoNewObject(JSContext* cx, void* payload, ICNewObject_Fallback* stub, MutableHan
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!stub->invalid() &&
|
if (!stub->invalid() &&
|
||||||
(templateObject->is<UnboxedPlainObject>() ||
|
!templateObject->as<PlainObject>().hasDynamicSlots())
|
||||||
!templateObject->as<PlainObject>().hasDynamicSlots()))
|
|
||||||
{
|
{
|
||||||
JitCode* code = GenerateNewObjectWithTemplateCode(cx, templateObject);
|
JitCode* code = GenerateNewObjectWithTemplateCode(cx, templateObject);
|
||||||
if (!code)
|
if (!code)
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
#include "vm/NativeObject-inl.h"
|
#include "vm/NativeObject-inl.h"
|
||||||
#include "vm/StringObject-inl.h"
|
#include "vm/StringObject-inl.h"
|
||||||
#include "vm/TypeInference-inl.h"
|
#include "vm/TypeInference-inl.h"
|
||||||
#include "vm/UnboxedObject-inl.h"
|
#include "gc/StoreBuffer-inl.h"
|
||||||
|
|
||||||
using namespace js;
|
using namespace js;
|
||||||
using namespace js::jit;
|
using namespace js::jit;
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,6 @@
|
||||||
#include "vm/Caches-inl.h"
|
#include "vm/Caches-inl.h"
|
||||||
#include "vm/Interpreter-inl.h"
|
#include "vm/Interpreter-inl.h"
|
||||||
#include "vm/NativeObject-inl.h"
|
#include "vm/NativeObject-inl.h"
|
||||||
#include "vm/UnboxedObject-inl.h"
|
|
||||||
|
|
||||||
using namespace js;
|
using namespace js;
|
||||||
using namespace js::gc;
|
using namespace js::gc;
|
||||||
|
|
|
||||||
|
|
@ -529,9 +529,6 @@ struct JSCompartment
|
||||||
// table manages references from such typed objects to their buffers.
|
// table manages references from such typed objects to their buffers.
|
||||||
js::ObjectWeakMap* lazyArrayBuffers;
|
js::ObjectWeakMap* lazyArrayBuffers;
|
||||||
|
|
||||||
// All unboxed layouts in the compartment.
|
|
||||||
mozilla::LinkedList<js::UnboxedLayout> unboxedLayouts;
|
|
||||||
|
|
||||||
// WebAssembly state for the compartment.
|
// WebAssembly state for the compartment.
|
||||||
js::wasm::Compartment wasm;
|
js::wasm::Compartment wasm;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -269,7 +269,7 @@ js::GetBuiltinClass(JSContext* cx, HandleObject obj, ESClass* cls)
|
||||||
if (MOZ_UNLIKELY(obj->is<ProxyObject>()))
|
if (MOZ_UNLIKELY(obj->is<ProxyObject>()))
|
||||||
return Proxy::getBuiltinClass(cx, obj, cls);
|
return Proxy::getBuiltinClass(cx, obj, cls);
|
||||||
|
|
||||||
if (obj->is<PlainObject>() || obj->is<UnboxedPlainObject>())
|
if (obj->is<PlainObject>())
|
||||||
*cls = ESClass::Object;
|
*cls = ESClass::Object;
|
||||||
else if (obj->is<ArrayObject>())
|
else if (obj->is<ArrayObject>())
|
||||||
*cls = ESClass::Array;
|
*cls = ESClass::Array;
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@
|
||||||
#include "frontend/BytecodeCompiler.h"
|
#include "frontend/BytecodeCompiler.h"
|
||||||
#include "gc/Marking.h"
|
#include "gc/Marking.h"
|
||||||
#include "gc/Policy.h"
|
#include "gc/Policy.h"
|
||||||
|
#include "gc/StoreBuffer-inl.h"
|
||||||
#include "jit/BaselineJIT.h"
|
#include "jit/BaselineJIT.h"
|
||||||
#include "js/MemoryMetrics.h"
|
#include "js/MemoryMetrics.h"
|
||||||
#include "js/Proxy.h"
|
#include "js/Proxy.h"
|
||||||
|
|
@ -54,7 +55,6 @@
|
||||||
#include "vm/RegExpStaticsObject.h"
|
#include "vm/RegExpStaticsObject.h"
|
||||||
#include "vm/Shape.h"
|
#include "vm/Shape.h"
|
||||||
#include "vm/TypedArrayCommon.h"
|
#include "vm/TypedArrayCommon.h"
|
||||||
#include "vm/UnboxedObject-inl.h"
|
|
||||||
|
|
||||||
#include "jsatominlines.h"
|
#include "jsatominlines.h"
|
||||||
#include "jsboolinlines.h"
|
#include "jsboolinlines.h"
|
||||||
|
|
|
||||||
|
|
@ -32,19 +32,6 @@
|
||||||
#include "vm/ShapedObject-inl.h"
|
#include "vm/ShapedObject-inl.h"
|
||||||
#include "vm/TypeInference-inl.h"
|
#include "vm/TypeInference-inl.h"
|
||||||
|
|
||||||
namespace js {
|
|
||||||
|
|
||||||
// This is needed here for ensureShape() below.
|
|
||||||
inline bool
|
|
||||||
MaybeConvertUnboxedObjectToNative(ExclusiveContext* cx, JSObject* obj)
|
|
||||||
{
|
|
||||||
if (obj->is<UnboxedPlainObject>())
|
|
||||||
return UnboxedPlainObject::convertToNative(cx->asJSContext(), obj);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace js
|
|
||||||
|
|
||||||
inline js::Shape*
|
inline js::Shape*
|
||||||
JSObject::maybeShape() const
|
JSObject::maybeShape() const
|
||||||
{
|
{
|
||||||
|
|
@ -57,8 +44,6 @@ JSObject::maybeShape() const
|
||||||
inline js::Shape*
|
inline js::Shape*
|
||||||
JSObject::ensureShape(js::ExclusiveContext* cx)
|
JSObject::ensureShape(js::ExclusiveContext* cx)
|
||||||
{
|
{
|
||||||
if (!js::MaybeConvertUnboxedObjectToNative(cx, this))
|
|
||||||
return nullptr;
|
|
||||||
js::Shape* shape = maybeShape();
|
js::Shape* shape = maybeShape();
|
||||||
MOZ_ASSERT(shape);
|
MOZ_ASSERT(shape);
|
||||||
return shape;
|
return shape;
|
||||||
|
|
|
||||||
|
|
@ -355,7 +355,6 @@ UNIFIED_SOURCES += [
|
||||||
'vm/UbiNode.cpp',
|
'vm/UbiNode.cpp',
|
||||||
'vm/UbiNodeCensus.cpp',
|
'vm/UbiNodeCensus.cpp',
|
||||||
'vm/UbiNodeShortestPaths.cpp',
|
'vm/UbiNodeShortestPaths.cpp',
|
||||||
'vm/UnboxedObject.cpp',
|
|
||||||
'vm/Unicode.cpp',
|
'vm/Unicode.cpp',
|
||||||
'vm/Value.cpp',
|
'vm/Value.cpp',
|
||||||
'vm/WeakMapPtr.cpp',
|
'vm/WeakMapPtr.cpp',
|
||||||
|
|
|
||||||
|
|
@ -7536,7 +7536,6 @@ SetWorkerContextOptions(JSContext* cx)
|
||||||
.setWasm(enableWasm)
|
.setWasm(enableWasm)
|
||||||
.setWasmAlwaysBaseline(enableWasmAlwaysBaseline)
|
.setWasmAlwaysBaseline(enableWasmAlwaysBaseline)
|
||||||
.setNativeRegExp(enableNativeRegExp)
|
.setNativeRegExp(enableNativeRegExp)
|
||||||
.setUnboxedArrays(enableUnboxedArrays)
|
|
||||||
.setArrayProtoValues(enableArrayProtoValues);
|
.setArrayProtoValues(enableArrayProtoValues);
|
||||||
cx->setOffthreadIonCompilationEnabled(offthreadCompilation);
|
cx->setOffthreadIonCompilationEnabled(offthreadCompilation);
|
||||||
cx->profilingScripts = enableCodeCoverage || enableDisassemblyDumps;
|
cx->profilingScripts = enableCodeCoverage || enableDisassemblyDumps;
|
||||||
|
|
@ -7706,7 +7705,6 @@ main(int argc, char** argv, char** envp)
|
||||||
|| !op.addBoolOption('\0', "no-asmjs", "Disable asm.js compilation")
|
|| !op.addBoolOption('\0', "no-asmjs", "Disable asm.js compilation")
|
||||||
|| !op.addBoolOption('\0', "no-wasm", "Disable WebAssembly compilation")
|
|| !op.addBoolOption('\0', "no-wasm", "Disable WebAssembly compilation")
|
||||||
|| !op.addBoolOption('\0', "no-native-regexp", "Disable native regexp compilation")
|
|| !op.addBoolOption('\0', "no-native-regexp", "Disable native regexp compilation")
|
||||||
|| !op.addBoolOption('\0', "no-unboxed-objects", "Disable creating unboxed plain objects")
|
|
||||||
|| !op.addBoolOption('\0', "wasm-always-baseline", "Enable wasm baseline compiler when possible")
|
|| !op.addBoolOption('\0', "wasm-always-baseline", "Enable wasm baseline compiler when possible")
|
||||||
|| !op.addBoolOption('\0', "wasm-check-bce", "Always generate wasm bounds check, even redundant ones.")
|
|| !op.addBoolOption('\0', "wasm-check-bce", "Always generate wasm bounds check, even redundant ones.")
|
||||||
|| !op.addBoolOption('\0', "no-array-proto-values", "Remove Array.prototype.values")
|
|| !op.addBoolOption('\0', "no-array-proto-values", "Remove Array.prototype.values")
|
||||||
|
|
|
||||||
|
|
@ -1916,7 +1916,6 @@ CASE(EnableInterruptsPseudoOpcode)
|
||||||
/* Various 1-byte no-ops. */
|
/* Various 1-byte no-ops. */
|
||||||
CASE(JSOP_NOP)
|
CASE(JSOP_NOP)
|
||||||
CASE(JSOP_NOP_DESTRUCTURING)
|
CASE(JSOP_NOP_DESTRUCTURING)
|
||||||
CASE(JSOP_UNUSED126)
|
|
||||||
CASE(JSOP_UNUSED192)
|
CASE(JSOP_UNUSED192)
|
||||||
CASE(JSOP_UNUSED209)
|
CASE(JSOP_UNUSED209)
|
||||||
CASE(JSOP_UNUSED210)
|
CASE(JSOP_UNUSED210)
|
||||||
|
|
@ -3637,6 +3636,7 @@ CASE(JSOP_NEWINIT)
|
||||||
END_CASE(JSOP_NEWINIT)
|
END_CASE(JSOP_NEWINIT)
|
||||||
|
|
||||||
CASE(JSOP_NEWARRAY)
|
CASE(JSOP_NEWARRAY)
|
||||||
|
CASE(JSOP_SPREADCALLARRAY)
|
||||||
{
|
{
|
||||||
uint32_t length = GET_UINT32(REGS.pc);
|
uint32_t length = GET_UINT32(REGS.pc);
|
||||||
JSObject* obj = NewArrayOperation(cx, script, REGS.pc, length);
|
JSObject* obj = NewArrayOperation(cx, script, REGS.pc, length);
|
||||||
|
|
@ -4970,11 +4970,6 @@ js::NewObjectOperationWithTemplate(JSContext* cx, HandleObject templateObject)
|
||||||
|
|
||||||
NewObjectKind newKind = templateObject->group()->shouldPreTenure() ? TenuredObject : GenericObject;
|
NewObjectKind newKind = templateObject->group()->shouldPreTenure() ? TenuredObject : GenericObject;
|
||||||
|
|
||||||
if (templateObject->group()->maybeUnboxedLayout()) {
|
|
||||||
RootedObjectGroup group(cx, templateObject->group());
|
|
||||||
return UnboxedPlainObject::create(cx, group, newKind);
|
|
||||||
}
|
|
||||||
|
|
||||||
JSObject* obj = CopyInitializerObject(cx, templateObject.as<PlainObject>(), newKind);
|
JSObject* obj = CopyInitializerObject(cx, templateObject.as<PlainObject>(), newKind);
|
||||||
if (!obj)
|
if (!obj)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
||||||
|
|
@ -390,33 +390,6 @@ NativeObject::setLastPropertyMakeNonNative(Shape* shape)
|
||||||
shape_ = shape;
|
shape_ = shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
NativeObject::setLastPropertyMakeNative(ExclusiveContext* cx, Shape* shape)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(getClass()->isNative());
|
|
||||||
MOZ_ASSERT(shape->getObjectClass()->isNative());
|
|
||||||
MOZ_ASSERT(!shape->inDictionary());
|
|
||||||
|
|
||||||
// This method is used to convert unboxed objects into native objects. In
|
|
||||||
// this case, the shape_ field was previously used to store other data and
|
|
||||||
// this should be treated as an initialization.
|
|
||||||
shape_.init(shape);
|
|
||||||
|
|
||||||
slots_ = nullptr;
|
|
||||||
elements_ = emptyObjectElements;
|
|
||||||
|
|
||||||
size_t oldSpan = shape->numFixedSlots();
|
|
||||||
size_t newSpan = shape->slotSpan();
|
|
||||||
|
|
||||||
initializeSlotRange(0, oldSpan);
|
|
||||||
|
|
||||||
// A failure at this point will leave the object as a mutant, and we
|
|
||||||
// can't recover.
|
|
||||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
|
||||||
if (oldSpan != newSpan && !updateSlotsForSpan(cx, oldSpan, newSpan))
|
|
||||||
oomUnsafe.crash("NativeObject::setLastPropertyMakeNative");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
NativeObject::setSlotSpan(ExclusiveContext* cx, uint32_t span)
|
NativeObject::setSlotSpan(ExclusiveContext* cx, uint32_t span)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -470,11 +470,6 @@ class NativeObject : public ShapedObject
|
||||||
// that are (temporarily) inconsistent.
|
// that are (temporarily) inconsistent.
|
||||||
void setLastPropertyMakeNonNative(Shape* shape);
|
void setLastPropertyMakeNonNative(Shape* shape);
|
||||||
|
|
||||||
// As for setLastProperty(), but changes the class associated with the
|
|
||||||
// object to a native one. The object's type has already been changed, and
|
|
||||||
// this brings the shape into sync with it.
|
|
||||||
void setLastPropertyMakeNative(ExclusiveContext* cx, Shape* shape);
|
|
||||||
|
|
||||||
// Newly-created TypedArrays that map a SharedArrayBuffer are
|
// Newly-created TypedArrays that map a SharedArrayBuffer are
|
||||||
// marked as shared by giving them an ObjectElements that has the
|
// marked as shared by giving them an ObjectElements that has the
|
||||||
// ObjectElements::SHARED_MEMORY flag set.
|
// ObjectElements::SHARED_MEMORY flag set.
|
||||||
|
|
|
||||||
|
|
@ -108,20 +108,6 @@ ObjectGroup::maybePreliminaryObjects()
|
||||||
return maybePreliminaryObjectsDontCheckGeneration();
|
return maybePreliminaryObjectsDontCheckGeneration();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline UnboxedLayout*
|
|
||||||
ObjectGroup::maybeUnboxedLayout()
|
|
||||||
{
|
|
||||||
maybeSweep(nullptr);
|
|
||||||
return maybeUnboxedLayoutDontCheckGeneration();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline UnboxedLayout&
|
|
||||||
ObjectGroup::unboxedLayout()
|
|
||||||
{
|
|
||||||
maybeSweep(nullptr);
|
|
||||||
return unboxedLayoutDontCheckGeneration();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace js
|
} // namespace js
|
||||||
|
|
||||||
#endif /* vm_ObjectGroup_inl_h */
|
#endif /* vm_ObjectGroup_inl_h */
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,10 @@
|
||||||
#include "vm/ArrayObject.h"
|
#include "vm/ArrayObject.h"
|
||||||
#include "vm/Shape.h"
|
#include "vm/Shape.h"
|
||||||
#include "vm/TaggedProto.h"
|
#include "vm/TaggedProto.h"
|
||||||
#include "vm/UnboxedObject.h"
|
|
||||||
|
|
||||||
#include "jsobjinlines.h"
|
#include "jsobjinlines.h"
|
||||||
|
|
||||||
#include "vm/UnboxedObject-inl.h"
|
#include "vm/NativeObject-inl.h"
|
||||||
|
|
||||||
using namespace js;
|
using namespace js;
|
||||||
|
|
||||||
|
|
@ -56,7 +55,6 @@ ObjectGroup::finalize(FreeOp* fop)
|
||||||
if (newScriptDontCheckGeneration())
|
if (newScriptDontCheckGeneration())
|
||||||
newScriptDontCheckGeneration()->clear();
|
newScriptDontCheckGeneration()->clear();
|
||||||
fop->delete_(newScriptDontCheckGeneration());
|
fop->delete_(newScriptDontCheckGeneration());
|
||||||
fop->delete_(maybeUnboxedLayoutDontCheckGeneration());
|
|
||||||
if (maybePreliminaryObjectsDontCheckGeneration())
|
if (maybePreliminaryObjectsDontCheckGeneration())
|
||||||
maybePreliminaryObjectsDontCheckGeneration()->clear();
|
maybePreliminaryObjectsDontCheckGeneration()->clear();
|
||||||
fop->delete_(maybePreliminaryObjectsDontCheckGeneration());
|
fop->delete_(maybePreliminaryObjectsDontCheckGeneration());
|
||||||
|
|
@ -83,8 +81,6 @@ ObjectGroup::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
if (TypeNewScript* newScript = newScriptDontCheckGeneration())
|
if (TypeNewScript* newScript = newScriptDontCheckGeneration())
|
||||||
n += newScript->sizeOfIncludingThis(mallocSizeOf);
|
n += newScript->sizeOfIncludingThis(mallocSizeOf);
|
||||||
if (UnboxedLayout* layout = maybeUnboxedLayoutDontCheckGeneration())
|
|
||||||
n += layout->sizeOfIncludingThis(mallocSizeOf);
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -530,8 +526,7 @@ ObjectGroup::defaultNewGroup(ExclusiveContext* cx, const Class* clasp,
|
||||||
if (p) {
|
if (p) {
|
||||||
ObjectGroup* group = p->group;
|
ObjectGroup* group = p->group;
|
||||||
MOZ_ASSERT_IF(clasp, group->clasp() == clasp);
|
MOZ_ASSERT_IF(clasp, group->clasp() == clasp);
|
||||||
MOZ_ASSERT_IF(!clasp, group->clasp() == &PlainObject::class_ ||
|
MOZ_ASSERT_IF(!clasp, group->clasp() == &PlainObject::class_);
|
||||||
group->clasp() == &UnboxedPlainObject::class_);
|
|
||||||
MOZ_ASSERT(group->proto() == proto);
|
MOZ_ASSERT(group->proto() == proto);
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
@ -971,46 +966,6 @@ js::CombinePlainObjectPropertyTypes(ExclusiveContext* cx, JSObject* newObj,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (newObj->is<UnboxedPlainObject>()) {
|
|
||||||
const UnboxedLayout& layout = newObj->as<UnboxedPlainObject>().layout();
|
|
||||||
const int32_t* traceList = layout.traceList();
|
|
||||||
if (!traceList)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
uint8_t* newData = newObj->as<UnboxedPlainObject>().data();
|
|
||||||
uint8_t* oldData = oldObj->as<UnboxedPlainObject>().data();
|
|
||||||
|
|
||||||
for (; *traceList != -1; traceList++) {}
|
|
||||||
traceList++;
|
|
||||||
for (; *traceList != -1; traceList++) {
|
|
||||||
JSObject* newInnerObj = *reinterpret_cast<JSObject**>(newData + *traceList);
|
|
||||||
JSObject* oldInnerObj = *reinterpret_cast<JSObject**>(oldData + *traceList);
|
|
||||||
|
|
||||||
if (!newInnerObj || !oldInnerObj || SameGroup(oldInnerObj, newInnerObj))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!GiveObjectGroup(cx, newInnerObj, oldInnerObj))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (SameGroup(oldInnerObj, newInnerObj))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!GiveObjectGroup(cx, oldInnerObj, newInnerObj))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (SameGroup(oldInnerObj, newInnerObj)) {
|
|
||||||
for (size_t i = 1; i < ncompare; i++) {
|
|
||||||
if (compare[i].isObject() && SameGroup(&compare[i].toObject(), newObj)) {
|
|
||||||
uint8_t* otherData = compare[i].toObject().as<UnboxedPlainObject>().data();
|
|
||||||
JSObject* otherInnerObj = *reinterpret_cast<JSObject**>(otherData + *traceList);
|
|
||||||
if (otherInnerObj && !SameGroup(otherInnerObj, newInnerObj)) {
|
|
||||||
if (!GiveObjectGroup(cx, otherInnerObj, newInnerObj))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -1234,12 +1189,6 @@ ObjectGroup::newPlainObject(ExclusiveContext* cx, IdValuePair* properties, size_
|
||||||
|
|
||||||
RootedObjectGroup group(cx, p->value().group);
|
RootedObjectGroup group(cx, p->value().group);
|
||||||
|
|
||||||
// Watch for existing groups which now use an unboxed layout.
|
|
||||||
if (group->maybeUnboxedLayout()) {
|
|
||||||
MOZ_ASSERT(group->unboxedLayout().properties().length() == nproperties);
|
|
||||||
return UnboxedPlainObject::createWithProperties(cx, group, newKind, properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update property types according to the properties we are about to add.
|
// Update property types according to the properties we are about to add.
|
||||||
// Do this before we do anything which can GC, which might move or remove
|
// Do this before we do anything which can GC, which might move or remove
|
||||||
// this table entry.
|
// this table entry.
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@
|
||||||
namespace js {
|
namespace js {
|
||||||
|
|
||||||
class TypeDescr;
|
class TypeDescr;
|
||||||
class UnboxedLayout;
|
|
||||||
|
|
||||||
class PreliminaryObjectArrayWithTemplate;
|
class PreliminaryObjectArrayWithTemplate;
|
||||||
class TypeNewScript;
|
class TypeNewScript;
|
||||||
|
|
@ -154,16 +153,6 @@ class ObjectGroup : public gc::TenuredCell
|
||||||
// For some plain objects, the addendum stores a PreliminaryObjectArrayWithTemplate.
|
// For some plain objects, the addendum stores a PreliminaryObjectArrayWithTemplate.
|
||||||
Addendum_PreliminaryObjects,
|
Addendum_PreliminaryObjects,
|
||||||
|
|
||||||
// When objects in this group have an unboxed representation, the
|
|
||||||
// addendum stores an UnboxedLayout (which might have a TypeNewScript
|
|
||||||
// as well, if the group is also constructed using 'new').
|
|
||||||
Addendum_UnboxedLayout,
|
|
||||||
|
|
||||||
// If this group is used by objects that have been converted from an
|
|
||||||
// unboxed representation and/or have the same allocation kind as such
|
|
||||||
// objects, the addendum points to that unboxed group.
|
|
||||||
Addendum_OriginalUnboxedGroup,
|
|
||||||
|
|
||||||
// When used by typed objects, the addendum stores a TypeDescr.
|
// When used by typed objects, the addendum stores a TypeDescr.
|
||||||
Addendum_TypeDescr
|
Addendum_TypeDescr
|
||||||
};
|
};
|
||||||
|
|
@ -185,7 +174,6 @@ class ObjectGroup : public gc::TenuredCell
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeNewScript* anyNewScript();
|
|
||||||
void detachNewScript(bool writeBarrier, ObjectGroup* replacement);
|
void detachNewScript(bool writeBarrier, ObjectGroup* replacement);
|
||||||
|
|
||||||
ObjectGroupFlags flagsDontCheckGeneration() const {
|
ObjectGroupFlags flagsDontCheckGeneration() const {
|
||||||
|
|
@ -225,34 +213,6 @@ class ObjectGroup : public gc::TenuredCell
|
||||||
maybePreliminaryObjectsDontCheckGeneration();
|
maybePreliminaryObjectsDontCheckGeneration();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline UnboxedLayout* maybeUnboxedLayout();
|
|
||||||
inline UnboxedLayout& unboxedLayout();
|
|
||||||
|
|
||||||
UnboxedLayout* maybeUnboxedLayoutDontCheckGeneration() const {
|
|
||||||
if (addendumKind() == Addendum_UnboxedLayout)
|
|
||||||
return reinterpret_cast<UnboxedLayout*>(addendum_);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
UnboxedLayout& unboxedLayoutDontCheckGeneration() const {
|
|
||||||
MOZ_ASSERT(addendumKind() == Addendum_UnboxedLayout);
|
|
||||||
return *maybeUnboxedLayoutDontCheckGeneration();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setUnboxedLayout(UnboxedLayout* layout) {
|
|
||||||
setAddendum(Addendum_UnboxedLayout, layout);
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectGroup* maybeOriginalUnboxedGroup() const {
|
|
||||||
if (addendumKind() == Addendum_OriginalUnboxedGroup)
|
|
||||||
return reinterpret_cast<ObjectGroup*>(addendum_);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setOriginalUnboxedGroup(ObjectGroup* group) {
|
|
||||||
setAddendum(Addendum_OriginalUnboxedGroup, group);
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeDescr* maybeTypeDescr() {
|
TypeDescr* maybeTypeDescr() {
|
||||||
// Note: there is no need to sweep when accessing the type descriptor
|
// Note: there is no need to sweep when accessing the type descriptor
|
||||||
// of an object, as it is strongly held and immutable.
|
// of an object, as it is strongly held and immutable.
|
||||||
|
|
@ -313,9 +273,8 @@ class ObjectGroup : public gc::TenuredCell
|
||||||
* that can be read out of that property in actual JS objects. In native
|
* that can be read out of that property in actual JS objects. In native
|
||||||
* objects, property types account for plain data properties (those with a
|
* objects, property types account for plain data properties (those with a
|
||||||
* slot and no getter or setter hook) and dense elements. In typed objects
|
* slot and no getter or setter hook) and dense elements. In typed objects
|
||||||
* and unboxed objects, property types account for object and value
|
* property types account for object and value properties and elements in
|
||||||
* properties and elements in the object, and expando properties in unboxed
|
* the object.
|
||||||
* objects.
|
|
||||||
*
|
*
|
||||||
* For accesses on these properties, the correspondence is as follows:
|
* For accesses on these properties, the correspondence is as follows:
|
||||||
*
|
*
|
||||||
|
|
@ -338,10 +297,9 @@ class ObjectGroup : public gc::TenuredCell
|
||||||
* 2. Array lengths are special cased by the compiler and VM and are not
|
* 2. Array lengths are special cased by the compiler and VM and are not
|
||||||
* reflected in property types.
|
* reflected in property types.
|
||||||
*
|
*
|
||||||
* 3. In typed objects (but not unboxed objects), the initial values of
|
* 3. In typed objects, the initial values of properties (null pointers and
|
||||||
* properties (null pointers and undefined values) are not reflected in
|
* undefined values) are not reflected in the property types. These
|
||||||
* the property types. These values are always possible when reading the
|
* values are always possible when reading the property.
|
||||||
* property.
|
|
||||||
*
|
*
|
||||||
* We establish these by using write barriers on calls to setProperty and
|
* We establish these by using write barriers on calls to setProperty and
|
||||||
* defineProperty which are on native properties, and on any jitcode which
|
* defineProperty which are on native properties, and on any jitcode which
|
||||||
|
|
@ -455,12 +413,6 @@ class ObjectGroup : public gc::TenuredCell
|
||||||
return &flags_;
|
return &flags_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the bit pattern stored in an object's addendum when it has an
|
|
||||||
// original unboxed group.
|
|
||||||
static inline int32_t addendumOriginalUnboxedGroupValue() {
|
|
||||||
return Addendum_OriginalUnboxedGroup << OBJECT_FLAG_ADDENDUM_SHIFT;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline uint32_t basePropertyCount();
|
inline uint32_t basePropertyCount();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -511,8 +463,8 @@ class ObjectGroup : public gc::TenuredCell
|
||||||
NewObjectKind newKind,
|
NewObjectKind newKind,
|
||||||
NewArrayKind arrayKind = NewArrayKind::Normal);
|
NewArrayKind arrayKind = NewArrayKind::Normal);
|
||||||
|
|
||||||
// Create a PlainObject or UnboxedPlainObject with the specified properties
|
// Create a PlainObject with the specified properties and a group specialized
|
||||||
// and a group specialized for those properties.
|
// for those properties.
|
||||||
static JSObject* newPlainObject(ExclusiveContext* cx,
|
static JSObject* newPlainObject(ExclusiveContext* cx,
|
||||||
IdValuePair* properties, size_t nproperties,
|
IdValuePair* properties, size_t nproperties,
|
||||||
NewObjectKind newKind);
|
NewObjectKind newKind);
|
||||||
|
|
|
||||||
|
|
@ -1281,7 +1281,17 @@
|
||||||
* Stack: receiver, obj, propval => obj[propval]
|
* Stack: receiver, obj, propval => obj[propval]
|
||||||
*/ \
|
*/ \
|
||||||
macro(JSOP_GETELEM_SUPER, 125, "getelem-super", NULL, 1, 3, 1, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC) \
|
macro(JSOP_GETELEM_SUPER, 125, "getelem-super", NULL, 1, 3, 1, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC) \
|
||||||
macro(JSOP_UNUSED126, 126, "unused126", NULL, 5, 0, 1, JOF_UINT32) \
|
/*
|
||||||
|
* Pushes newly created array for a spread call onto the stack. This has
|
||||||
|
* the same semantics as JSOP_NEWARRAY, but is distinguished to avoid
|
||||||
|
* using unboxed arrays in spread calls, which would make compiling spread
|
||||||
|
* calls in baseline more complex.
|
||||||
|
* Category: Literals
|
||||||
|
* Type: Array
|
||||||
|
* Operands: uint32_t length
|
||||||
|
* Stack: => obj
|
||||||
|
*/ \
|
||||||
|
macro(JSOP_SPREADCALLARRAY, 126, "spreadcallarray", NULL, 5, 0, 1, JOF_UINT32) \
|
||||||
\
|
\
|
||||||
/*
|
/*
|
||||||
* Defines the given function on the current scope.
|
* Defines the given function on the current scope.
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,7 @@ ReceiverGuard::ReceiverGuard(JSObject* obj)
|
||||||
: group(nullptr), shape(nullptr)
|
: group(nullptr), shape(nullptr)
|
||||||
{
|
{
|
||||||
if (obj) {
|
if (obj) {
|
||||||
if (obj->is<UnboxedPlainObject>()) {
|
if (obj->is<TypedObject>()) {
|
||||||
group = obj->group();
|
|
||||||
if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
|
|
||||||
shape = expando->lastProperty();
|
|
||||||
} else if (obj->is<TypedObject>()) {
|
|
||||||
group = obj->group();
|
group = obj->group();
|
||||||
} else {
|
} else {
|
||||||
shape = obj->maybeShape();
|
shape = obj->maybeShape();
|
||||||
|
|
@ -32,9 +28,7 @@ ReceiverGuard::ReceiverGuard(ObjectGroup* group, Shape* shape)
|
||||||
{
|
{
|
||||||
if (group) {
|
if (group) {
|
||||||
const Class* clasp = group->clasp();
|
const Class* clasp = group->clasp();
|
||||||
if (clasp == &UnboxedPlainObject::class_) {
|
if (IsTypedObjectClass(clasp)) {
|
||||||
// Keep both group and shape.
|
|
||||||
} else if (IsTypedObjectClass(clasp)) {
|
|
||||||
this->shape = nullptr;
|
this->shape = nullptr;
|
||||||
} else {
|
} else {
|
||||||
this->group = nullptr;
|
this->group = nullptr;
|
||||||
|
|
@ -45,10 +39,6 @@ ReceiverGuard::ReceiverGuard(ObjectGroup* group, Shape* shape)
|
||||||
/* static */ int32_t
|
/* static */ int32_t
|
||||||
HeapReceiverGuard::keyBits(JSObject* obj)
|
HeapReceiverGuard::keyBits(JSObject* obj)
|
||||||
{
|
{
|
||||||
if (obj->is<UnboxedPlainObject>()) {
|
|
||||||
// Both the group and shape need to be guarded for unboxed plain objects.
|
|
||||||
return obj->as<UnboxedPlainObject>().maybeExpando() ? 0 : 1;
|
|
||||||
}
|
|
||||||
if (obj->is<TypedObject>()) {
|
if (obj->is<TypedObject>()) {
|
||||||
// Only the group needs to be guarded for typed objects.
|
// Only the group needs to be guarded for typed objects.
|
||||||
return 2;
|
return 2;
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@
|
||||||
#include "vm/SharedArrayObject.h"
|
#include "vm/SharedArrayObject.h"
|
||||||
#include "vm/StringObject.h"
|
#include "vm/StringObject.h"
|
||||||
#include "vm/TypedArrayObject.h"
|
#include "vm/TypedArrayObject.h"
|
||||||
#include "vm/UnboxedObject.h"
|
|
||||||
|
|
||||||
#include "jscntxtinlines.h"
|
#include "jscntxtinlines.h"
|
||||||
|
|
||||||
|
|
@ -285,10 +284,6 @@ TypeIdString(jsid id)
|
||||||
*/
|
*/
|
||||||
struct AutoEnterAnalysis
|
struct AutoEnterAnalysis
|
||||||
{
|
{
|
||||||
// For use when initializing an UnboxedLayout. The UniquePtr's destructor
|
|
||||||
// must run when GC is not suppressed.
|
|
||||||
UniquePtr<UnboxedLayout> unboxedLayoutToCleanUp;
|
|
||||||
|
|
||||||
// Prevent GC activity in the middle of analysis.
|
// Prevent GC activity in the middle of analysis.
|
||||||
gc::AutoSuppressGC suppressGC;
|
gc::AutoSuppressGC suppressGC;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@
|
||||||
#include "vm/Opcodes.h"
|
#include "vm/Opcodes.h"
|
||||||
#include "vm/Shape.h"
|
#include "vm/Shape.h"
|
||||||
#include "vm/Time.h"
|
#include "vm/Time.h"
|
||||||
#include "vm/UnboxedObject.h"
|
|
||||||
|
|
||||||
#include "jsatominlines.h"
|
#include "jsatominlines.h"
|
||||||
#include "jsscriptinlines.h"
|
#include "jsscriptinlines.h"
|
||||||
|
|
@ -297,9 +296,6 @@ js::ObjectGroupHasProperty(JSContext* cx, ObjectGroup* group, jsid id, const Val
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
JSObject* obj = &value.toObject();
|
|
||||||
if (!obj->hasLazyGroup() && obj->group()->maybeOriginalUnboxedGroup())
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!types->hasType(type)) {
|
if (!types->hasType(type)) {
|
||||||
|
|
@ -1944,33 +1940,6 @@ class ConstraintDataFreezeObjectForTypedArrayData
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Constraint which triggers recompilation if an unboxed object in some group
|
|
||||||
// is converted to a native object.
|
|
||||||
class ConstraintDataFreezeObjectForUnboxedConvertedToNative
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ConstraintDataFreezeObjectForUnboxedConvertedToNative()
|
|
||||||
{}
|
|
||||||
|
|
||||||
const char* kind() { return "freezeObjectForUnboxedConvertedToNative"; }
|
|
||||||
|
|
||||||
bool invalidateOnNewType(TypeSet::Type type) { return false; }
|
|
||||||
bool invalidateOnNewPropertyState(TypeSet* property) { return false; }
|
|
||||||
bool invalidateOnNewObjectState(ObjectGroup* group) {
|
|
||||||
return group->unboxedLayout().nativeGroup() != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool constraintHolds(JSContext* cx,
|
|
||||||
const HeapTypeSetKey& property, TemporaryTypeSet* expected)
|
|
||||||
{
|
|
||||||
return !invalidateOnNewObjectState(property.object()->maybeGroup());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool shouldSweep() { return false; }
|
|
||||||
|
|
||||||
JSCompartment* maybeCompartment() { return nullptr; }
|
|
||||||
};
|
|
||||||
|
|
||||||
} /* anonymous namespace */
|
} /* anonymous namespace */
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -2505,8 +2474,6 @@ TemporaryTypeSet::propertyNeedsBarrier(CompilerConstraintList* constraints, jsid
|
||||||
bool
|
bool
|
||||||
js::ClassCanHaveExtraProperties(const Class* clasp)
|
js::ClassCanHaveExtraProperties(const Class* clasp)
|
||||||
{
|
{
|
||||||
if (clasp == &UnboxedPlainObject::class_)
|
|
||||||
return false;
|
|
||||||
return clasp->getResolve()
|
return clasp->getResolve()
|
||||||
|| clasp->getOpsLookupProperty()
|
|| clasp->getOpsLookupProperty()
|
||||||
|| clasp->getOpsGetProperty()
|
|| clasp->getOpsGetProperty()
|
||||||
|
|
@ -2805,15 +2772,6 @@ js::AddTypePropertyId(ExclusiveContext* cx, ObjectGroup* group, JSObject* obj, j
|
||||||
// from acquiring the fully initialized group.
|
// from acquiring the fully initialized group.
|
||||||
if (group->newScript() && group->newScript()->initializedGroup())
|
if (group->newScript() && group->newScript()->initializedGroup())
|
||||||
AddTypePropertyId(cx, group->newScript()->initializedGroup(), nullptr, id, type);
|
AddTypePropertyId(cx, group->newScript()->initializedGroup(), nullptr, id, type);
|
||||||
|
|
||||||
// Maintain equivalent type information for unboxed object groups and their
|
|
||||||
// corresponding native group. Since type sets might contain the unboxed
|
|
||||||
// group but not the native group, this ensures optimizations based on the
|
|
||||||
// unboxed group are valid for the native group.
|
|
||||||
if (group->maybeUnboxedLayout() && group->maybeUnboxedLayout()->nativeGroup())
|
|
||||||
AddTypePropertyId(cx, group->maybeUnboxedLayout()->nativeGroup(), nullptr, id, type);
|
|
||||||
if (ObjectGroup* unboxedGroup = group->maybeOriginalUnboxedGroup())
|
|
||||||
AddTypePropertyId(cx, unboxedGroup, nullptr, id, type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -2885,12 +2843,6 @@ ObjectGroup::setFlags(ExclusiveContext* cx, ObjectGroupFlags flags)
|
||||||
// acquired properties analysis.
|
// acquired properties analysis.
|
||||||
if (newScript() && newScript()->initializedGroup())
|
if (newScript() && newScript()->initializedGroup())
|
||||||
newScript()->initializedGroup()->setFlags(cx, flags);
|
newScript()->initializedGroup()->setFlags(cx, flags);
|
||||||
|
|
||||||
// Propagate flag changes between unboxed and corresponding native groups.
|
|
||||||
if (maybeUnboxedLayout() && maybeUnboxedLayout()->nativeGroup())
|
|
||||||
maybeUnboxedLayout()->nativeGroup()->setFlags(cx, flags);
|
|
||||||
if (ObjectGroup* unboxedGroup = maybeOriginalUnboxedGroup())
|
|
||||||
unboxedGroup->setFlags(cx, flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -2923,23 +2875,6 @@ ObjectGroup::markUnknown(ExclusiveContext* cx)
|
||||||
prop->types.setNonDataProperty(cx);
|
prop->types.setNonDataProperty(cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ObjectGroup* unboxedGroup = maybeOriginalUnboxedGroup())
|
|
||||||
MarkObjectGroupUnknownProperties(cx, unboxedGroup);
|
|
||||||
if (maybeUnboxedLayout() && maybeUnboxedLayout()->nativeGroup())
|
|
||||||
MarkObjectGroupUnknownProperties(cx, maybeUnboxedLayout()->nativeGroup());
|
|
||||||
if (ObjectGroup* unboxedGroup = maybeOriginalUnboxedGroup())
|
|
||||||
MarkObjectGroupUnknownProperties(cx, unboxedGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeNewScript*
|
|
||||||
ObjectGroup::anyNewScript()
|
|
||||||
{
|
|
||||||
if (newScript())
|
|
||||||
return newScript();
|
|
||||||
if (maybeUnboxedLayout())
|
|
||||||
return unboxedLayout().newScript();
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -2949,7 +2884,7 @@ ObjectGroup::detachNewScript(bool writeBarrier, ObjectGroup* replacement)
|
||||||
// analyzed, remove it from the newObjectGroups table so that it will not be
|
// analyzed, remove it from the newObjectGroups table so that it will not be
|
||||||
// produced by calling 'new' on the associated function anymore.
|
// produced by calling 'new' on the associated function anymore.
|
||||||
// The TypeNewScript is not actually destroyed.
|
// The TypeNewScript is not actually destroyed.
|
||||||
TypeNewScript* newScript = anyNewScript();
|
TypeNewScript* newScript = this->newScript();
|
||||||
MOZ_ASSERT(newScript);
|
MOZ_ASSERT(newScript);
|
||||||
|
|
||||||
if (newScript->analyzed()) {
|
if (newScript->analyzed()) {
|
||||||
|
|
@ -2968,10 +2903,7 @@ ObjectGroup::detachNewScript(bool writeBarrier, ObjectGroup* replacement)
|
||||||
MOZ_ASSERT(!replacement);
|
MOZ_ASSERT(!replacement);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->newScript())
|
setAddendum(Addendum_None, nullptr, writeBarrier);
|
||||||
setAddendum(Addendum_None, nullptr, writeBarrier);
|
|
||||||
else
|
|
||||||
unboxedLayout().setNewScript(nullptr, writeBarrier);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -2982,7 +2914,7 @@ ObjectGroup::maybeClearNewScriptOnOOM()
|
||||||
if (!isMarked())
|
if (!isMarked())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
TypeNewScript* newScript = anyNewScript();
|
TypeNewScript* newScript = this->newScript();
|
||||||
if (!newScript)
|
if (!newScript)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -2997,7 +2929,7 @@ ObjectGroup::maybeClearNewScriptOnOOM()
|
||||||
void
|
void
|
||||||
ObjectGroup::clearNewScript(ExclusiveContext* cx, ObjectGroup* replacement /* = nullptr*/)
|
ObjectGroup::clearNewScript(ExclusiveContext* cx, ObjectGroup* replacement /* = nullptr*/)
|
||||||
{
|
{
|
||||||
TypeNewScript* newScript = anyNewScript();
|
TypeNewScript* newScript = this->newScript();
|
||||||
if (!newScript)
|
if (!newScript)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -3451,22 +3383,6 @@ PreliminaryObjectArray::sweep()
|
||||||
for (size_t i = 0; i < COUNT; i++) {
|
for (size_t i = 0; i < COUNT; i++) {
|
||||||
JSObject** ptr = &objects[i];
|
JSObject** ptr = &objects[i];
|
||||||
if (*ptr && IsAboutToBeFinalizedUnbarriered(ptr)) {
|
if (*ptr && IsAboutToBeFinalizedUnbarriered(ptr)) {
|
||||||
// Before we clear this reference, change the object's group to the
|
|
||||||
// Object.prototype group. This is done to ensure JSObject::finalize
|
|
||||||
// sees a NativeObject Class even if we change the current group's
|
|
||||||
// Class to one of the unboxed object classes in the meantime. If
|
|
||||||
// the compartment's global is dead, we don't do anything as the
|
|
||||||
// group's Class is not going to change in that case.
|
|
||||||
JSObject* obj = *ptr;
|
|
||||||
GlobalObject* global = obj->compartment()->unsafeUnbarrieredMaybeGlobal();
|
|
||||||
if (global && !obj->isSingleton()) {
|
|
||||||
JSObject* objectProto = GetBuiltinPrototypePure(global, JSProto_Object);
|
|
||||||
obj->setGroup(objectProto->groupRaw());
|
|
||||||
MOZ_ASSERT(obj->is<NativeObject>());
|
|
||||||
MOZ_ASSERT(obj->getClass() == objectProto->getClass());
|
|
||||||
MOZ_ASSERT(!obj->getClass()->hasFinalize());
|
|
||||||
}
|
|
||||||
|
|
||||||
*ptr = nullptr;
|
*ptr = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3566,16 +3482,11 @@ PreliminaryObjectArrayWithTemplate::maybeAnalyze(ExclusiveContext* cx, ObjectGro
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (group->maybeUnboxedLayout())
|
// Since the preliminary objects still reflect the template object's
|
||||||
return;
|
// properties, and all objects in the future will be created with those
|
||||||
|
// properties, the properties can be marked as definitive for objects in
|
||||||
if (shape()) {
|
// the group.
|
||||||
// We weren't able to use an unboxed layout, but since the preliminary
|
group->addDefiniteProperties(cx, shape());
|
||||||
// objects still reflect the template object's properties, and all
|
|
||||||
// objects in the future will be created with those properties, the
|
|
||||||
// properties can be marked as definite for objects in the group.
|
|
||||||
group->addDefiniteProperties(cx, shape());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
@ -3589,7 +3500,6 @@ TypeNewScript::make(JSContext* cx, ObjectGroup* group, JSFunction* fun)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(cx->zone()->types.activeAnalysis);
|
MOZ_ASSERT(cx->zone()->types.activeAnalysis);
|
||||||
MOZ_ASSERT(!group->newScript());
|
MOZ_ASSERT(!group->newScript());
|
||||||
MOZ_ASSERT(!group->maybeUnboxedLayout());
|
|
||||||
|
|
||||||
// rollbackPartiallyInitializedObjects expects function_ to be
|
// rollbackPartiallyInitializedObjects expects function_ to be
|
||||||
// canonicalized.
|
// canonicalized.
|
||||||
|
|
@ -3852,27 +3762,6 @@ TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate,
|
||||||
js_delete(preliminaryObjects);
|
js_delete(preliminaryObjects);
|
||||||
preliminaryObjects = nullptr;
|
preliminaryObjects = nullptr;
|
||||||
|
|
||||||
if (group->maybeUnboxedLayout()) {
|
|
||||||
// An unboxed layout was constructed for the group, and this has already
|
|
||||||
// been hooked into it.
|
|
||||||
MOZ_ASSERT(group->unboxedLayout().newScript() == this);
|
|
||||||
destroyNewScript.group = nullptr;
|
|
||||||
|
|
||||||
// Clear out the template object, which is not used for TypeNewScripts
|
|
||||||
// with an unboxed layout. Currently it is a mutant object with a
|
|
||||||
// non-native group and native shape, so make it safe for GC by changing
|
|
||||||
// its group to the default for its prototype.
|
|
||||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
|
||||||
ObjectGroup* plainGroup = ObjectGroup::defaultNewGroup(cx, &PlainObject::class_,
|
|
||||||
group->proto());
|
|
||||||
if (!plainGroup)
|
|
||||||
oomUnsafe.crash("TypeNewScript::maybeAnalyze");
|
|
||||||
templateObject_->setGroup(plainGroup);
|
|
||||||
templateObject_ = nullptr;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prefixShape->slotSpan() == templateObject()->slotSpan()) {
|
if (prefixShape->slotSpan() == templateObject()->slotSpan()) {
|
||||||
// The definite properties analysis found exactly the properties that
|
// The definite properties analysis found exactly the properties that
|
||||||
// are held in common by the preliminary objects. No further analysis
|
// are held in common by the preliminary objects. No further analysis
|
||||||
|
|
@ -3968,12 +3857,6 @@ TypeNewScript::rollbackPartiallyInitializedObjects(JSContext* cx, ObjectGroup* g
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (thisv.toObject().is<UnboxedPlainObject>()) {
|
|
||||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
|
||||||
if (!UnboxedPlainObject::convertToNative(cx, &thisv.toObject()))
|
|
||||||
oomUnsafe.crash("rollbackPartiallyInitializedObjects");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Found a matching frame.
|
// Found a matching frame.
|
||||||
RootedPlainObject obj(cx, &thisv.toObject().as<PlainObject>());
|
RootedPlainObject obj(cx, &thisv.toObject().as<PlainObject>());
|
||||||
|
|
||||||
|
|
@ -4167,12 +4050,6 @@ ConstraintTypeSet::sweep(Zone* zone, AutoClearTypeInferenceStateOnOOM& oom)
|
||||||
// Object sets containing objects with unknown properties might
|
// Object sets containing objects with unknown properties might
|
||||||
// not be complete. Mark the type set as unknown, which it will
|
// not be complete. Mark the type set as unknown, which it will
|
||||||
// be treated as during Ion compilation.
|
// be treated as during Ion compilation.
|
||||||
//
|
|
||||||
// Note that we don't have to do this when the type set might
|
|
||||||
// be missing the native group corresponding to an unboxed
|
|
||||||
// object group. In this case, the native group points to the
|
|
||||||
// unboxed object group via its addendum, so as long as objects
|
|
||||||
// with either group exist, neither group will be finalized.
|
|
||||||
flags |= TYPE_FLAG_ANYOBJECT;
|
flags |= TYPE_FLAG_ANYOBJECT;
|
||||||
clearObjects();
|
clearObjects();
|
||||||
objectCount = 0;
|
objectCount = 0;
|
||||||
|
|
@ -4256,21 +4133,6 @@ ObjectGroup::sweep(AutoClearTypeInferenceStateOnOOM* oom)
|
||||||
Maybe<AutoClearTypeInferenceStateOnOOM> fallbackOOM;
|
Maybe<AutoClearTypeInferenceStateOnOOM> fallbackOOM;
|
||||||
EnsureHasAutoClearTypeInferenceStateOnOOM(oom, zone(), fallbackOOM);
|
EnsureHasAutoClearTypeInferenceStateOnOOM(oom, zone(), fallbackOOM);
|
||||||
|
|
||||||
if (maybeUnboxedLayout()) {
|
|
||||||
// Remove unboxed layouts that are about to be finalized from the
|
|
||||||
// compartment wide list while we are still on the main thread.
|
|
||||||
ObjectGroup* group = this;
|
|
||||||
if (IsAboutToBeFinalizedUnbarriered(&group))
|
|
||||||
unboxedLayout().detachFromCompartment();
|
|
||||||
|
|
||||||
if (unboxedLayout().newScript())
|
|
||||||
unboxedLayout().newScript()->sweep();
|
|
||||||
|
|
||||||
// Discard constructor code to avoid holding onto ExecutablePools.
|
|
||||||
if (zone()->isGCCompacting())
|
|
||||||
unboxedLayout().setConstructorCode(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (maybePreliminaryObjects())
|
if (maybePreliminaryObjects())
|
||||||
maybePreliminaryObjects()->sweep();
|
maybePreliminaryObjects()->sweep();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,177 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
||||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
#ifndef vm_UnboxedObject_inl_h
|
|
||||||
#define vm_UnboxedObject_inl_h
|
|
||||||
|
|
||||||
#include "vm/UnboxedObject.h"
|
|
||||||
|
|
||||||
#include "gc/StoreBuffer-inl.h"
|
|
||||||
#include "vm/ArrayObject-inl.h"
|
|
||||||
#include "vm/NativeObject-inl.h"
|
|
||||||
|
|
||||||
namespace js {
|
|
||||||
|
|
||||||
static inline Value
|
|
||||||
GetUnboxedValue(uint8_t* p, JSValueType type, bool maybeUninitialized)
|
|
||||||
{
|
|
||||||
switch (type) {
|
|
||||||
case JSVAL_TYPE_BOOLEAN:
|
|
||||||
return BooleanValue(*p != 0);
|
|
||||||
|
|
||||||
case JSVAL_TYPE_INT32:
|
|
||||||
return Int32Value(*reinterpret_cast<int32_t*>(p));
|
|
||||||
|
|
||||||
case JSVAL_TYPE_DOUBLE: {
|
|
||||||
// During unboxed plain object creation, non-GC thing properties are
|
|
||||||
// left uninitialized. This is normally fine, since the properties will
|
|
||||||
// be filled in shortly, but if they are read before that happens we
|
|
||||||
// need to make sure that doubles are canonical.
|
|
||||||
double d = *reinterpret_cast<double*>(p);
|
|
||||||
if (maybeUninitialized)
|
|
||||||
return DoubleValue(JS::CanonicalizeNaN(d));
|
|
||||||
return DoubleValue(d);
|
|
||||||
}
|
|
||||||
|
|
||||||
case JSVAL_TYPE_STRING:
|
|
||||||
return StringValue(*reinterpret_cast<JSString**>(p));
|
|
||||||
|
|
||||||
case JSVAL_TYPE_OBJECT:
|
|
||||||
return ObjectOrNullValue(*reinterpret_cast<JSObject**>(p));
|
|
||||||
|
|
||||||
default:
|
|
||||||
MOZ_CRASH("Invalid type for unboxed value");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
SetUnboxedValueNoTypeChange(JSObject* unboxedObject,
|
|
||||||
uint8_t* p, JSValueType type, const Value& v,
|
|
||||||
bool preBarrier)
|
|
||||||
{
|
|
||||||
switch (type) {
|
|
||||||
case JSVAL_TYPE_BOOLEAN:
|
|
||||||
*p = v.toBoolean();
|
|
||||||
return;
|
|
||||||
|
|
||||||
case JSVAL_TYPE_INT32:
|
|
||||||
*reinterpret_cast<int32_t*>(p) = v.toInt32();
|
|
||||||
return;
|
|
||||||
|
|
||||||
case JSVAL_TYPE_DOUBLE:
|
|
||||||
*reinterpret_cast<double*>(p) = v.toNumber();
|
|
||||||
return;
|
|
||||||
|
|
||||||
case JSVAL_TYPE_STRING: {
|
|
||||||
MOZ_ASSERT(!IsInsideNursery(v.toString()));
|
|
||||||
JSString** np = reinterpret_cast<JSString**>(p);
|
|
||||||
if (preBarrier)
|
|
||||||
JSString::writeBarrierPre(*np);
|
|
||||||
*np = v.toString();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
case JSVAL_TYPE_OBJECT: {
|
|
||||||
JSObject** np = reinterpret_cast<JSObject**>(p);
|
|
||||||
|
|
||||||
// Manually trigger post barriers on the whole object. If we treat
|
|
||||||
// the pointer as a HeapPtrObject we will get confused later if the
|
|
||||||
// object is converted to its native representation.
|
|
||||||
JSObject* obj = v.toObjectOrNull();
|
|
||||||
if (IsInsideNursery(obj) && !IsInsideNursery(unboxedObject)) {
|
|
||||||
JSRuntime* rt = unboxedObject->runtimeFromMainThread();
|
|
||||||
rt->gc.storeBuffer.putWholeCell(unboxedObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (preBarrier)
|
|
||||||
JSObject::writeBarrierPre(*np);
|
|
||||||
*np = obj;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
MOZ_CRASH("Invalid type for unboxed value");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool
|
|
||||||
SetUnboxedValue(ExclusiveContext* cx, JSObject* unboxedObject, jsid id,
|
|
||||||
uint8_t* p, JSValueType type, const Value& v, bool preBarrier)
|
|
||||||
{
|
|
||||||
switch (type) {
|
|
||||||
case JSVAL_TYPE_BOOLEAN:
|
|
||||||
if (v.isBoolean()) {
|
|
||||||
*p = v.toBoolean();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
|
|
||||||
case JSVAL_TYPE_INT32:
|
|
||||||
if (v.isInt32()) {
|
|
||||||
*reinterpret_cast<int32_t*>(p) = v.toInt32();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
|
|
||||||
case JSVAL_TYPE_DOUBLE:
|
|
||||||
if (v.isNumber()) {
|
|
||||||
*reinterpret_cast<double*>(p) = v.toNumber();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
|
|
||||||
case JSVAL_TYPE_STRING:
|
|
||||||
if (v.isString()) {
|
|
||||||
MOZ_ASSERT(!IsInsideNursery(v.toString()));
|
|
||||||
JSString** np = reinterpret_cast<JSString**>(p);
|
|
||||||
if (preBarrier)
|
|
||||||
JSString::writeBarrierPre(*np);
|
|
||||||
*np = v.toString();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
|
|
||||||
case JSVAL_TYPE_OBJECT:
|
|
||||||
if (v.isObjectOrNull()) {
|
|
||||||
JSObject** np = reinterpret_cast<JSObject**>(p);
|
|
||||||
|
|
||||||
// Update property types when writing object properties. Types for
|
|
||||||
// other properties were captured when the unboxed layout was
|
|
||||||
// created.
|
|
||||||
AddTypePropertyId(cx, unboxedObject, id, v);
|
|
||||||
|
|
||||||
// As above, trigger post barriers on the whole object.
|
|
||||||
JSObject* obj = v.toObjectOrNull();
|
|
||||||
if (IsInsideNursery(v.toObjectOrNull()) && !IsInsideNursery(unboxedObject)) {
|
|
||||||
JSRuntime* rt = unboxedObject->runtimeFromMainThread();
|
|
||||||
rt->gc.storeBuffer.putWholeCell(unboxedObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (preBarrier)
|
|
||||||
JSObject::writeBarrierPre(*np);
|
|
||||||
*np = obj;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
|
|
||||||
default:
|
|
||||||
MOZ_CRASH("Invalid type for unboxed value");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
// UnboxedPlainObject
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
inline const UnboxedLayout&
|
|
||||||
UnboxedPlainObject::layout() const
|
|
||||||
{
|
|
||||||
return group()->unboxedLayout();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace js
|
|
||||||
|
|
||||||
#endif // vm_UnboxedObject_inl_h
|
|
||||||
|
|
@ -1,956 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
||||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
#include "vm/UnboxedObject-inl.h"
|
|
||||||
|
|
||||||
#include "jit/BaselineIC.h"
|
|
||||||
#include "jit/ExecutableAllocator.h"
|
|
||||||
#include "jit/JitCommon.h"
|
|
||||||
#include "jit/Linker.h"
|
|
||||||
|
|
||||||
#include "jsobjinlines.h"
|
|
||||||
|
|
||||||
#include "gc/Nursery-inl.h"
|
|
||||||
#include "jit/MacroAssembler-inl.h"
|
|
||||||
#include "vm/Shape-inl.h"
|
|
||||||
|
|
||||||
using mozilla::ArrayLength;
|
|
||||||
using mozilla::DebugOnly;
|
|
||||||
using mozilla::PodCopy;
|
|
||||||
|
|
||||||
using namespace js;
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
// UnboxedLayout
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void
|
|
||||||
UnboxedLayout::trace(JSTracer* trc)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < properties_.length(); i++)
|
|
||||||
TraceManuallyBarrieredEdge(trc, &properties_[i].name, "unboxed_layout_name");
|
|
||||||
|
|
||||||
if (newScript())
|
|
||||||
newScript()->trace(trc);
|
|
||||||
|
|
||||||
TraceNullableEdge(trc, &nativeGroup_, "unboxed_layout_nativeGroup");
|
|
||||||
TraceNullableEdge(trc, &nativeShape_, "unboxed_layout_nativeShape");
|
|
||||||
TraceNullableEdge(trc, &allocationScript_, "unboxed_layout_allocationScript");
|
|
||||||
TraceNullableEdge(trc, &replacementGroup_, "unboxed_layout_replacementGroup");
|
|
||||||
TraceNullableEdge(trc, &constructorCode_, "unboxed_layout_constructorCode");
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t
|
|
||||||
UnboxedLayout::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
|
|
||||||
{
|
|
||||||
return mallocSizeOf(this)
|
|
||||||
+ properties_.sizeOfExcludingThis(mallocSizeOf)
|
|
||||||
+ (newScript() ? newScript()->sizeOfIncludingThis(mallocSizeOf) : 0)
|
|
||||||
+ mallocSizeOf(traceList());
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
UnboxedLayout::setNewScript(TypeNewScript* newScript, bool writeBarrier /* = true */)
|
|
||||||
{
|
|
||||||
if (newScript_ && writeBarrier)
|
|
||||||
TypeNewScript::writeBarrierPre(newScript_);
|
|
||||||
newScript_ = newScript;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constructor code returns a 0x1 value to indicate the constructor code should
|
|
||||||
// be cleared.
|
|
||||||
static const uintptr_t CLEAR_CONSTRUCTOR_CODE_TOKEN = 0x1;
|
|
||||||
|
|
||||||
/* static */ bool
|
|
||||||
UnboxedLayout::makeConstructorCode(JSContext* cx, HandleObjectGroup group)
|
|
||||||
{
|
|
||||||
gc::AutoSuppressGC suppress(cx);
|
|
||||||
|
|
||||||
using namespace jit;
|
|
||||||
|
|
||||||
if (!cx->compartment()->ensureJitCompartmentExists(cx))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
UnboxedLayout& layout = group->unboxedLayout();
|
|
||||||
MOZ_ASSERT(!layout.constructorCode());
|
|
||||||
|
|
||||||
UnboxedPlainObject* templateObject = UnboxedPlainObject::create(cx, group, TenuredObject);
|
|
||||||
if (!templateObject)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
JitContext jitContext(cx, nullptr);
|
|
||||||
|
|
||||||
MacroAssembler masm;
|
|
||||||
|
|
||||||
Register propertiesReg, newKindReg;
|
|
||||||
#ifdef JS_CODEGEN_X86
|
|
||||||
propertiesReg = eax;
|
|
||||||
newKindReg = ecx;
|
|
||||||
masm.loadPtr(Address(masm.getStackPointer(), sizeof(void*)), propertiesReg);
|
|
||||||
masm.loadPtr(Address(masm.getStackPointer(), 2 * sizeof(void*)), newKindReg);
|
|
||||||
#else
|
|
||||||
propertiesReg = IntArgReg0;
|
|
||||||
newKindReg = IntArgReg1;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef JS_CODEGEN_ARM64
|
|
||||||
// ARM64 communicates stack address via sp, but uses a pseudo-sp for addressing.
|
|
||||||
masm.initStackPtr();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
MOZ_ASSERT(propertiesReg.volatile_());
|
|
||||||
MOZ_ASSERT(newKindReg.volatile_());
|
|
||||||
|
|
||||||
AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
|
|
||||||
regs.take(propertiesReg);
|
|
||||||
regs.take(newKindReg);
|
|
||||||
Register object = regs.takeAny(), scratch1 = regs.takeAny(), scratch2 = regs.takeAny();
|
|
||||||
|
|
||||||
LiveGeneralRegisterSet savedNonVolatileRegisters = SavedNonVolatileRegisters(regs);
|
|
||||||
masm.PushRegsInMask(savedNonVolatileRegisters);
|
|
||||||
|
|
||||||
// The scratch double register might be used by MacroAssembler methods.
|
|
||||||
if (ScratchDoubleReg.volatile_())
|
|
||||||
masm.push(ScratchDoubleReg);
|
|
||||||
|
|
||||||
Label failure, tenuredObject, allocated;
|
|
||||||
masm.branch32(Assembler::NotEqual, newKindReg, Imm32(GenericObject), &tenuredObject);
|
|
||||||
masm.branchTest32(Assembler::NonZero, AbsoluteAddress(group->addressOfFlags()),
|
|
||||||
Imm32(OBJECT_FLAG_PRE_TENURE), &tenuredObject);
|
|
||||||
|
|
||||||
// Allocate an object in the nursery
|
|
||||||
masm.createGCObject(object, scratch1, templateObject, gc::DefaultHeap, &failure,
|
|
||||||
/* initFixedSlots = */ false);
|
|
||||||
|
|
||||||
masm.jump(&allocated);
|
|
||||||
masm.bind(&tenuredObject);
|
|
||||||
|
|
||||||
// Allocate an object in the tenured heap.
|
|
||||||
masm.createGCObject(object, scratch1, templateObject, gc::TenuredHeap, &failure,
|
|
||||||
/* initFixedSlots = */ false);
|
|
||||||
|
|
||||||
// If any of the properties being stored are in the nursery, add a store
|
|
||||||
// buffer entry for the new object.
|
|
||||||
Label postBarrier;
|
|
||||||
for (size_t i = 0; i < layout.properties().length(); i++) {
|
|
||||||
const UnboxedLayout::Property& property = layout.properties()[i];
|
|
||||||
if (property.type == JSVAL_TYPE_OBJECT) {
|
|
||||||
Address valueAddress(propertiesReg, i * sizeof(IdValuePair) + offsetof(IdValuePair, value));
|
|
||||||
Label notObject;
|
|
||||||
masm.branchTestObject(Assembler::NotEqual, valueAddress, ¬Object);
|
|
||||||
Register valueObject = masm.extractObject(valueAddress, scratch1);
|
|
||||||
masm.branchPtrInNurseryChunk(Assembler::Equal, valueObject, scratch2, &postBarrier);
|
|
||||||
masm.bind(¬Object);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
masm.jump(&allocated);
|
|
||||||
masm.bind(&postBarrier);
|
|
||||||
|
|
||||||
LiveGeneralRegisterSet liveVolatileRegisters;
|
|
||||||
liveVolatileRegisters.add(propertiesReg);
|
|
||||||
if (object.volatile_())
|
|
||||||
liveVolatileRegisters.add(object);
|
|
||||||
masm.PushRegsInMask(liveVolatileRegisters);
|
|
||||||
|
|
||||||
masm.mov(ImmPtr(cx->runtime()), scratch1);
|
|
||||||
masm.setupUnalignedABICall(scratch2);
|
|
||||||
masm.passABIArg(scratch1);
|
|
||||||
masm.passABIArg(object);
|
|
||||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, PostWriteBarrier));
|
|
||||||
|
|
||||||
masm.PopRegsInMask(liveVolatileRegisters);
|
|
||||||
|
|
||||||
masm.bind(&allocated);
|
|
||||||
|
|
||||||
ValueOperand valueOperand;
|
|
||||||
#ifdef JS_NUNBOX32
|
|
||||||
valueOperand = ValueOperand(scratch1, scratch2);
|
|
||||||
#else
|
|
||||||
valueOperand = ValueOperand(scratch1);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Label failureStoreOther, failureStoreObject;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < layout.properties().length(); i++) {
|
|
||||||
const UnboxedLayout::Property& property = layout.properties()[i];
|
|
||||||
Address valueAddress(propertiesReg, i * sizeof(IdValuePair) + offsetof(IdValuePair, value));
|
|
||||||
Address targetAddress(object, UnboxedPlainObject::offsetOfData() + property.offset);
|
|
||||||
|
|
||||||
masm.loadValue(valueAddress, valueOperand);
|
|
||||||
|
|
||||||
if (property.type == JSVAL_TYPE_OBJECT) {
|
|
||||||
HeapTypeSet* types = group->maybeGetProperty(IdToTypeId(NameToId(property.name)));
|
|
||||||
|
|
||||||
Label notObject;
|
|
||||||
masm.branchTestObject(Assembler::NotEqual, valueOperand,
|
|
||||||
types->mightBeMIRType(MIRType::Null) ? ¬Object : &failureStoreObject);
|
|
||||||
|
|
||||||
Register payloadReg = masm.extractObject(valueOperand, scratch1);
|
|
||||||
|
|
||||||
if (!types->hasType(TypeSet::AnyObjectType())) {
|
|
||||||
Register scratch = (payloadReg == scratch1) ? scratch2 : scratch1;
|
|
||||||
masm.guardObjectType(payloadReg, types, scratch, &failureStoreObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
masm.storeUnboxedProperty(targetAddress, JSVAL_TYPE_OBJECT,
|
|
||||||
TypedOrValueRegister(MIRType::Object,
|
|
||||||
AnyRegister(payloadReg)), nullptr);
|
|
||||||
|
|
||||||
if (notObject.used()) {
|
|
||||||
Label done;
|
|
||||||
masm.jump(&done);
|
|
||||||
masm.bind(¬Object);
|
|
||||||
masm.branchTestNull(Assembler::NotEqual, valueOperand, &failureStoreOther);
|
|
||||||
masm.storeUnboxedProperty(targetAddress, JSVAL_TYPE_OBJECT, NullValue(), nullptr);
|
|
||||||
masm.bind(&done);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
masm.storeUnboxedProperty(targetAddress, property.type,
|
|
||||||
ConstantOrRegister(valueOperand), &failureStoreOther);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Label done;
|
|
||||||
masm.bind(&done);
|
|
||||||
|
|
||||||
if (object != ReturnReg)
|
|
||||||
masm.movePtr(object, ReturnReg);
|
|
||||||
|
|
||||||
// Restore non-volatile registers which were saved on entry.
|
|
||||||
if (ScratchDoubleReg.volatile_())
|
|
||||||
masm.pop(ScratchDoubleReg);
|
|
||||||
masm.PopRegsInMask(savedNonVolatileRegisters);
|
|
||||||
|
|
||||||
masm.abiret();
|
|
||||||
|
|
||||||
masm.bind(&failureStoreOther);
|
|
||||||
|
|
||||||
// There was a failure while storing a value which cannot be stored at all
|
|
||||||
// in the unboxed object. Initialize the object so it is safe for GC and
|
|
||||||
// return null.
|
|
||||||
masm.initUnboxedObjectContents(object, templateObject);
|
|
||||||
|
|
||||||
masm.bind(&failure);
|
|
||||||
|
|
||||||
masm.movePtr(ImmWord(0), object);
|
|
||||||
masm.jump(&done);
|
|
||||||
|
|
||||||
masm.bind(&failureStoreObject);
|
|
||||||
|
|
||||||
// There was a failure while storing a value to an object slot of the
|
|
||||||
// unboxed object. If the value is storable, the failure occurred due to
|
|
||||||
// incomplete type information in the object, so return a token to trigger
|
|
||||||
// regeneration of the jitcode after a new object is created in the VM.
|
|
||||||
{
|
|
||||||
Label isObject;
|
|
||||||
masm.branchTestObject(Assembler::Equal, valueOperand, &isObject);
|
|
||||||
masm.branchTestNull(Assembler::NotEqual, valueOperand, &failureStoreOther);
|
|
||||||
masm.bind(&isObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the object so it is safe for GC.
|
|
||||||
masm.initUnboxedObjectContents(object, templateObject);
|
|
||||||
|
|
||||||
masm.movePtr(ImmWord(CLEAR_CONSTRUCTOR_CODE_TOKEN), object);
|
|
||||||
masm.jump(&done);
|
|
||||||
|
|
||||||
Linker linker(masm);
|
|
||||||
AutoFlushICache afc("UnboxedObject");
|
|
||||||
JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
|
|
||||||
if (!code)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
layout.setConstructorCode(code);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
UnboxedLayout::detachFromCompartment()
|
|
||||||
{
|
|
||||||
if (isInList())
|
|
||||||
remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
// UnboxedPlainObject
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool
|
|
||||||
UnboxedPlainObject::setValue(ExclusiveContext* cx, const UnboxedLayout::Property& property,
|
|
||||||
const Value& v)
|
|
||||||
{
|
|
||||||
uint8_t* p = &data_[property.offset];
|
|
||||||
return SetUnboxedValue(cx, this, NameToId(property.name), p, property.type, v,
|
|
||||||
/* preBarrier = */ true);
|
|
||||||
}
|
|
||||||
|
|
||||||
Value
|
|
||||||
UnboxedPlainObject::getValue(const UnboxedLayout::Property& property,
|
|
||||||
bool maybeUninitialized /* = false */)
|
|
||||||
{
|
|
||||||
uint8_t* p = &data_[property.offset];
|
|
||||||
return GetUnboxedValue(p, property.type, maybeUninitialized);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
UnboxedPlainObject::trace(JSTracer* trc, JSObject* obj)
|
|
||||||
{
|
|
||||||
if (obj->as<UnboxedPlainObject>().expando_) {
|
|
||||||
TraceManuallyBarrieredEdge(trc,
|
|
||||||
reinterpret_cast<NativeObject**>(&obj->as<UnboxedPlainObject>().expando_),
|
|
||||||
"unboxed_expando");
|
|
||||||
}
|
|
||||||
|
|
||||||
const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layoutDontCheckGeneration();
|
|
||||||
const int32_t* list = layout.traceList();
|
|
||||||
if (!list)
|
|
||||||
return;
|
|
||||||
|
|
||||||
uint8_t* data = obj->as<UnboxedPlainObject>().data();
|
|
||||||
while (*list != -1) {
|
|
||||||
GCPtrString* heap = reinterpret_cast<GCPtrString*>(data + *list);
|
|
||||||
TraceEdge(trc, heap, "unboxed_string");
|
|
||||||
list++;
|
|
||||||
}
|
|
||||||
list++;
|
|
||||||
while (*list != -1) {
|
|
||||||
GCPtrObject* heap = reinterpret_cast<GCPtrObject*>(data + *list);
|
|
||||||
TraceNullableEdge(trc, heap, "unboxed_object");
|
|
||||||
list++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unboxed objects don't have Values to trace.
|
|
||||||
MOZ_ASSERT(*(list + 1) == -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */ UnboxedExpandoObject*
|
|
||||||
UnboxedPlainObject::ensureExpando(JSContext* cx, Handle<UnboxedPlainObject*> obj)
|
|
||||||
{
|
|
||||||
if (obj->expando_)
|
|
||||||
return obj->expando_;
|
|
||||||
|
|
||||||
UnboxedExpandoObject* expando =
|
|
||||||
NewObjectWithGivenProto<UnboxedExpandoObject>(cx, nullptr, gc::AllocKind::OBJECT4);
|
|
||||||
if (!expando)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
// Don't track property types for expando objects. This allows Baseline
|
|
||||||
// and Ion AddSlot ICs to guard on the unboxed group without guarding on
|
|
||||||
// the expando group.
|
|
||||||
MarkObjectGroupUnknownProperties(cx, expando->group());
|
|
||||||
|
|
||||||
// If the expando is tenured then the original object must also be tenured.
|
|
||||||
// Otherwise barriers triggered on the original object for writes to the
|
|
||||||
// expando (as can happen in the JIT) won't see the tenured->nursery edge.
|
|
||||||
// See WholeCellEdges::mark.
|
|
||||||
MOZ_ASSERT_IF(!IsInsideNursery(expando), !IsInsideNursery(obj));
|
|
||||||
|
|
||||||
// As with setValue(), we need to manually trigger post barriers on the
|
|
||||||
// whole object. If we treat the field as a GCPtrObject and later
|
|
||||||
// convert the object to its native representation, we will end up with a
|
|
||||||
// corrupted store buffer entry.
|
|
||||||
if (IsInsideNursery(expando) && !IsInsideNursery(obj))
|
|
||||||
cx->runtime()->gc.storeBuffer.putWholeCell(obj);
|
|
||||||
|
|
||||||
obj->expando_ = expando;
|
|
||||||
return expando;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
UnboxedPlainObject::containsUnboxedOrExpandoProperty(ExclusiveContext* cx, jsid id) const
|
|
||||||
{
|
|
||||||
if (layout().lookup(id))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (maybeExpando() && maybeExpando()->containsShapeOrElement(cx, id))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
PropagatePropertyTypes(JSContext* cx, jsid id, ObjectGroup* oldGroup, ObjectGroup* newGroup)
|
|
||||||
{
|
|
||||||
HeapTypeSet* typeProperty = oldGroup->maybeGetProperty(id);
|
|
||||||
TypeSet::TypeList types;
|
|
||||||
if (!typeProperty->enumerateTypes(&types)) {
|
|
||||||
ReportOutOfMemory(cx);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (size_t j = 0; j < types.length(); j++)
|
|
||||||
AddTypePropertyId(cx, newGroup, nullptr, id, types[j]);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static PlainObject*
|
|
||||||
MakeReplacementTemplateObject(JSContext* cx, HandleObjectGroup group, const UnboxedLayout &layout)
|
|
||||||
{
|
|
||||||
PlainObject* obj = NewObjectWithGroup<PlainObject>(cx, group, layout.getAllocKind(),
|
|
||||||
TenuredObject);
|
|
||||||
if (!obj)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < layout.properties().length(); i++) {
|
|
||||||
const UnboxedLayout::Property& property = layout.properties()[i];
|
|
||||||
if (!obj->addDataProperty(cx, NameToId(property.name), i, JSPROP_ENUMERATE))
|
|
||||||
return nullptr;
|
|
||||||
MOZ_ASSERT(obj->slotSpan() == i + 1);
|
|
||||||
MOZ_ASSERT(!obj->inDictionaryMode());
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */ bool
|
|
||||||
UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group)
|
|
||||||
{
|
|
||||||
AutoEnterAnalysis enter(cx);
|
|
||||||
|
|
||||||
UnboxedLayout& layout = group->unboxedLayout();
|
|
||||||
Rooted<TaggedProto> proto(cx, group->proto());
|
|
||||||
|
|
||||||
MOZ_ASSERT(!layout.nativeGroup());
|
|
||||||
|
|
||||||
RootedObjectGroup replacementGroup(cx);
|
|
||||||
|
|
||||||
// Immediately clear any new script on the group. This is done by replacing
|
|
||||||
// the existing new script with one for a replacement default new group.
|
|
||||||
// This is done so that the size of the replacment group's objects is the
|
|
||||||
// same as that for the unboxed group, so that we do not see polymorphic
|
|
||||||
// slot accesses later on for sites that see converted objects from this
|
|
||||||
// group and objects that were allocated using the replacement new group.
|
|
||||||
if (layout.newScript()) {
|
|
||||||
replacementGroup = ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, proto);
|
|
||||||
if (!replacementGroup)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
PlainObject* templateObject = MakeReplacementTemplateObject(cx, replacementGroup, layout);
|
|
||||||
if (!templateObject)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
TypeNewScript* replacementNewScript =
|
|
||||||
TypeNewScript::makeNativeVersion(cx, layout.newScript(), templateObject);
|
|
||||||
if (!replacementNewScript)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
replacementGroup->setNewScript(replacementNewScript);
|
|
||||||
gc::TraceTypeNewScript(replacementGroup);
|
|
||||||
|
|
||||||
group->clearNewScript(cx, replacementGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Similarly, if this group is keyed to an allocation site, replace its
|
|
||||||
// entry with a new group that has no unboxed layout.
|
|
||||||
if (layout.allocationScript()) {
|
|
||||||
RootedScript script(cx, layout.allocationScript());
|
|
||||||
jsbytecode* pc = layout.allocationPc();
|
|
||||||
|
|
||||||
replacementGroup = ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, proto);
|
|
||||||
if (!replacementGroup)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
PlainObject* templateObject = &script->getObject(pc)->as<PlainObject>();
|
|
||||||
replacementGroup->addDefiniteProperties(cx, templateObject->lastProperty());
|
|
||||||
|
|
||||||
cx->compartment()->objectGroups.replaceAllocationSiteGroup(script, pc, JSProto_Object,
|
|
||||||
replacementGroup);
|
|
||||||
|
|
||||||
// Clear any baseline information at this opcode which might use the old group.
|
|
||||||
if (script->hasBaselineScript()) {
|
|
||||||
jit::ICEntry& entry = script->baselineScript()->icEntryFromPCOffset(script->pcToOffset(pc));
|
|
||||||
jit::ICFallbackStub* fallback = entry.fallbackStub();
|
|
||||||
for (jit::ICStubIterator iter = fallback->beginChain(); !iter.atEnd(); iter++)
|
|
||||||
iter.unlink(cx);
|
|
||||||
if (fallback->isNewObject_Fallback())
|
|
||||||
fallback->toNewObject_Fallback()->setTemplateObject(nullptr);
|
|
||||||
else if (fallback->isNewArray_Fallback())
|
|
||||||
fallback->toNewArray_Fallback()->setTemplateGroup(replacementGroup);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t nfixed = gc::GetGCKindSlots(layout.getAllocKind());
|
|
||||||
|
|
||||||
RootedShape shape(cx, EmptyShape::getInitialShape(cx, &PlainObject::class_, proto, nfixed, 0));
|
|
||||||
if (!shape)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Add shapes for each property, if this is for a plain object.
|
|
||||||
for (size_t i = 0; i < layout.properties().length(); i++) {
|
|
||||||
const UnboxedLayout::Property& property = layout.properties()[i];
|
|
||||||
|
|
||||||
Rooted<StackShape> child(cx, StackShape(shape->base()->unowned(), NameToId(property.name),
|
|
||||||
i, JSPROP_ENUMERATE, 0));
|
|
||||||
shape = cx->zone()->propertyTree.getChild(cx, shape, child);
|
|
||||||
if (!shape)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectGroup* nativeGroup =
|
|
||||||
ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, proto,
|
|
||||||
group->flags() & OBJECT_FLAG_DYNAMIC_MASK);
|
|
||||||
if (!nativeGroup)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// No sense propagating if we don't know what we started with.
|
|
||||||
if (!group->unknownProperties()) {
|
|
||||||
// Propagate all property types from the old group to the new group.
|
|
||||||
for (size_t i = 0; i < layout.properties().length(); i++) {
|
|
||||||
const UnboxedLayout::Property& property = layout.properties()[i];
|
|
||||||
jsid id = NameToId(property.name);
|
|
||||||
if (!PropagatePropertyTypes(cx, id, group, nativeGroup))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// If we are OOM we may not be able to propagate properties.
|
|
||||||
if (nativeGroup->unknownProperties())
|
|
||||||
break;
|
|
||||||
|
|
||||||
HeapTypeSet* nativeProperty = nativeGroup->maybeGetProperty(id);
|
|
||||||
if (nativeProperty && nativeProperty->canSetDefinite(i))
|
|
||||||
nativeProperty->setDefinite(i);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If we skip, though, the new group had better agree.
|
|
||||||
MOZ_ASSERT(nativeGroup->unknownProperties());
|
|
||||||
}
|
|
||||||
|
|
||||||
layout.nativeGroup_ = nativeGroup;
|
|
||||||
layout.nativeShape_ = shape;
|
|
||||||
layout.replacementGroup_ = replacementGroup;
|
|
||||||
|
|
||||||
nativeGroup->setOriginalUnboxedGroup(group);
|
|
||||||
|
|
||||||
group->markStateChange(cx);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */ bool
|
|
||||||
UnboxedPlainObject::convertToNative(JSContext* cx, JSObject* obj)
|
|
||||||
{
|
|
||||||
const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout();
|
|
||||||
UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
|
|
||||||
|
|
||||||
if (!layout.nativeGroup()) {
|
|
||||||
if (!UnboxedLayout::makeNativeGroup(cx, obj->group()))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// makeNativeGroup can reentrantly invoke this method.
|
|
||||||
if (obj->is<PlainObject>())
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoValueVector values(cx);
|
|
||||||
for (size_t i = 0; i < layout.properties().length(); i++) {
|
|
||||||
// We might be reading properties off the object which have not been
|
|
||||||
// initialized yet. Make sure any double values we read here are
|
|
||||||
// canonicalized.
|
|
||||||
if (!values.append(obj->as<UnboxedPlainObject>().getValue(layout.properties()[i], true)))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We are eliminating the expando edge with the conversion, so trigger a
|
|
||||||
// pre barrier.
|
|
||||||
JSObject::writeBarrierPre(expando);
|
|
||||||
|
|
||||||
// Additionally trigger a post barrier on the expando itself. Whole cell
|
|
||||||
// store buffer entries can be added on the original unboxed object for
|
|
||||||
// writes to the expando (see WholeCellEdges::trace), so after conversion
|
|
||||||
// we need to make sure the expando itself will still be traced.
|
|
||||||
if (expando && !IsInsideNursery(expando))
|
|
||||||
cx->runtime()->gc.storeBuffer.putWholeCell(expando);
|
|
||||||
|
|
||||||
obj->setGroup(layout.nativeGroup());
|
|
||||||
obj->as<PlainObject>().setLastPropertyMakeNative(cx, layout.nativeShape());
|
|
||||||
|
|
||||||
for (size_t i = 0; i < values.length(); i++)
|
|
||||||
obj->as<PlainObject>().initSlotUnchecked(i, values[i]);
|
|
||||||
|
|
||||||
if (expando) {
|
|
||||||
// Add properties from the expando object to the object, in order.
|
|
||||||
// Suppress GC here, so that callers don't need to worry about this
|
|
||||||
// method collecting. The stuff below can only fail due to OOM, in
|
|
||||||
// which case the object will not have been completely filled back in.
|
|
||||||
gc::AutoSuppressGC suppress(cx);
|
|
||||||
|
|
||||||
Vector<jsid> ids(cx);
|
|
||||||
for (Shape::Range<NoGC> r(expando->lastProperty()); !r.empty(); r.popFront()) {
|
|
||||||
if (!ids.append(r.front().propid()))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < expando->getDenseInitializedLength(); i++) {
|
|
||||||
if (!expando->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE)) {
|
|
||||||
if (!ids.append(INT_TO_JSID(i)))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
::Reverse(ids.begin(), ids.end());
|
|
||||||
|
|
||||||
RootedPlainObject nobj(cx, &obj->as<PlainObject>());
|
|
||||||
Rooted<UnboxedExpandoObject*> nexpando(cx, expando);
|
|
||||||
RootedId id(cx);
|
|
||||||
Rooted<PropertyDescriptor> desc(cx);
|
|
||||||
for (size_t i = 0; i < ids.length(); i++) {
|
|
||||||
id = ids[i];
|
|
||||||
if (!GetOwnPropertyDescriptor(cx, nexpando, id, &desc))
|
|
||||||
return false;
|
|
||||||
ObjectOpResult result;
|
|
||||||
if (!DefineProperty(cx, nobj, id, desc, result))
|
|
||||||
return false;
|
|
||||||
MOZ_ASSERT(result.ok());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */
|
|
||||||
UnboxedPlainObject*
|
|
||||||
UnboxedPlainObject::create(ExclusiveContext* cx, HandleObjectGroup group, NewObjectKind newKind)
|
|
||||||
{
|
|
||||||
AutoSetNewObjectMetadata metadata(cx);
|
|
||||||
|
|
||||||
MOZ_ASSERT(group->clasp() == &class_);
|
|
||||||
gc::AllocKind allocKind = group->unboxedLayout().getAllocKind();
|
|
||||||
|
|
||||||
UnboxedPlainObject* res =
|
|
||||||
NewObjectWithGroup<UnboxedPlainObject>(cx, group, allocKind, newKind);
|
|
||||||
if (!res)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
// Overwrite the dummy shape which was written to the object's expando field.
|
|
||||||
res->initExpando();
|
|
||||||
|
|
||||||
// Initialize reference fields of the object. All fields in the object will
|
|
||||||
// be overwritten shortly, but references need to be safe for the GC.
|
|
||||||
const int32_t* list = res->layout().traceList();
|
|
||||||
if (list) {
|
|
||||||
uint8_t* data = res->data();
|
|
||||||
while (*list != -1) {
|
|
||||||
GCPtrString* heap = reinterpret_cast<GCPtrString*>(data + *list);
|
|
||||||
heap->init(cx->names().empty);
|
|
||||||
list++;
|
|
||||||
}
|
|
||||||
list++;
|
|
||||||
while (*list != -1) {
|
|
||||||
GCPtrObject* heap = reinterpret_cast<GCPtrObject*>(data + *list);
|
|
||||||
heap->init(nullptr);
|
|
||||||
list++;
|
|
||||||
}
|
|
||||||
// Unboxed objects don't have Values to initialize.
|
|
||||||
MOZ_ASSERT(*(list + 1) == -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */ JSObject*
|
|
||||||
UnboxedPlainObject::createWithProperties(ExclusiveContext* cx, HandleObjectGroup group,
|
|
||||||
NewObjectKind newKind, IdValuePair* properties)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(newKind == GenericObject || newKind == TenuredObject);
|
|
||||||
|
|
||||||
UnboxedLayout& layout = group->unboxedLayout();
|
|
||||||
|
|
||||||
if (layout.constructorCode()) {
|
|
||||||
MOZ_ASSERT(cx->isJSContext());
|
|
||||||
|
|
||||||
typedef JSObject* (*ConstructorCodeSignature)(IdValuePair*, NewObjectKind);
|
|
||||||
ConstructorCodeSignature function =
|
|
||||||
reinterpret_cast<ConstructorCodeSignature>(layout.constructorCode()->raw());
|
|
||||||
|
|
||||||
JSObject* obj;
|
|
||||||
{
|
|
||||||
JS::AutoSuppressGCAnalysis nogc;
|
|
||||||
obj = reinterpret_cast<JSObject*>(CALL_GENERATED_2(function, properties, newKind));
|
|
||||||
}
|
|
||||||
if (obj > reinterpret_cast<JSObject*>(CLEAR_CONSTRUCTOR_CODE_TOKEN))
|
|
||||||
return obj;
|
|
||||||
|
|
||||||
if (obj == reinterpret_cast<JSObject*>(CLEAR_CONSTRUCTOR_CODE_TOKEN))
|
|
||||||
layout.setConstructorCode(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
UnboxedPlainObject* obj = UnboxedPlainObject::create(cx, group, newKind);
|
|
||||||
if (!obj)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < layout.properties().length(); i++) {
|
|
||||||
if (!obj->setValue(cx, layout.properties()[i], properties[i].value))
|
|
||||||
return NewPlainObjectWithProperties(cx, properties, layout.properties().length(), newKind);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef JS_CODEGEN_NONE
|
|
||||||
if (cx->isJSContext() &&
|
|
||||||
!group->unknownProperties() &&
|
|
||||||
!layout.constructorCode() &&
|
|
||||||
cx->asJSContext()->runtime()->jitSupportsFloatingPoint &&
|
|
||||||
jit::CanLikelyAllocateMoreExecutableMemory())
|
|
||||||
{
|
|
||||||
if (!UnboxedLayout::makeConstructorCode(cx->asJSContext(), group))
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */ bool
|
|
||||||
UnboxedPlainObject::obj_lookupProperty(JSContext* cx, HandleObject obj,
|
|
||||||
HandleId id, MutableHandleObject objp,
|
|
||||||
MutableHandleShape propp)
|
|
||||||
{
|
|
||||||
if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
|
|
||||||
MarkNonNativePropertyFound<CanGC>(propp);
|
|
||||||
objp.set(obj);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
RootedObject proto(cx, obj->staticPrototype());
|
|
||||||
if (!proto) {
|
|
||||||
objp.set(nullptr);
|
|
||||||
propp.set(nullptr);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return LookupProperty(cx, proto, id, objp, propp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */ bool
|
|
||||||
UnboxedPlainObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
|
|
||||||
Handle<PropertyDescriptor> desc,
|
|
||||||
ObjectOpResult& result)
|
|
||||||
{
|
|
||||||
const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout();
|
|
||||||
|
|
||||||
if (const UnboxedLayout::Property* property = layout.lookup(id)) {
|
|
||||||
if (!desc.getter() && !desc.setter() && desc.attributes() == JSPROP_ENUMERATE) {
|
|
||||||
// This define is equivalent to setting an existing property.
|
|
||||||
if (obj->as<UnboxedPlainObject>().setValue(cx, *property, desc.value()))
|
|
||||||
return result.succeed();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trying to incompatibly redefine an existing property requires the
|
|
||||||
// object to be converted to a native object.
|
|
||||||
if (!convertToNative(cx, obj))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return DefineProperty(cx, obj, id, desc, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define the property on the expando object.
|
|
||||||
Rooted<UnboxedExpandoObject*> expando(cx, ensureExpando(cx, obj.as<UnboxedPlainObject>()));
|
|
||||||
if (!expando)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Update property types on the unboxed object as well.
|
|
||||||
AddTypePropertyId(cx, obj, id, desc.value());
|
|
||||||
|
|
||||||
return DefineProperty(cx, expando, id, desc, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */ bool
|
|
||||||
UnboxedPlainObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
|
|
||||||
{
|
|
||||||
if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
|
|
||||||
*foundp = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
RootedObject proto(cx, obj->staticPrototype());
|
|
||||||
if (!proto) {
|
|
||||||
*foundp = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return HasProperty(cx, proto, id, foundp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */ bool
|
|
||||||
UnboxedPlainObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
|
|
||||||
HandleId id, MutableHandleValue vp)
|
|
||||||
{
|
|
||||||
const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout();
|
|
||||||
|
|
||||||
if (const UnboxedLayout::Property* property = layout.lookup(id)) {
|
|
||||||
vp.set(obj->as<UnboxedPlainObject>().getValue(*property));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
|
|
||||||
if (expando->containsShapeOrElement(cx, id)) {
|
|
||||||
RootedObject nexpando(cx, expando);
|
|
||||||
return GetProperty(cx, nexpando, receiver, id, vp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RootedObject proto(cx, obj->staticPrototype());
|
|
||||||
if (!proto) {
|
|
||||||
vp.setUndefined();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetProperty(cx, proto, receiver, id, vp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */ bool
|
|
||||||
UnboxedPlainObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
|
|
||||||
HandleValue receiver, ObjectOpResult& result)
|
|
||||||
{
|
|
||||||
const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout();
|
|
||||||
|
|
||||||
if (const UnboxedLayout::Property* property = layout.lookup(id)) {
|
|
||||||
if (receiver.isObject() && obj == &receiver.toObject()) {
|
|
||||||
if (obj->as<UnboxedPlainObject>().setValue(cx, *property, v))
|
|
||||||
return result.succeed();
|
|
||||||
|
|
||||||
if (!convertToNative(cx, obj))
|
|
||||||
return false;
|
|
||||||
return SetProperty(cx, obj, id, v, receiver, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
return SetPropertyByDefining(cx, id, v, receiver, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
|
|
||||||
if (expando->containsShapeOrElement(cx, id)) {
|
|
||||||
// Update property types on the unboxed object as well.
|
|
||||||
AddTypePropertyId(cx, obj, id, v);
|
|
||||||
|
|
||||||
RootedObject nexpando(cx, expando);
|
|
||||||
return SetProperty(cx, nexpando, id, v, receiver, result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return SetPropertyOnProto(cx, obj, id, v, receiver, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */ bool
|
|
||||||
UnboxedPlainObject::obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
|
|
||||||
MutableHandle<PropertyDescriptor> desc)
|
|
||||||
{
|
|
||||||
const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout();
|
|
||||||
|
|
||||||
if (const UnboxedLayout::Property* property = layout.lookup(id)) {
|
|
||||||
desc.value().set(obj->as<UnboxedPlainObject>().getValue(*property));
|
|
||||||
desc.setAttributes(JSPROP_ENUMERATE);
|
|
||||||
desc.object().set(obj);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
|
|
||||||
if (expando->containsShapeOrElement(cx, id)) {
|
|
||||||
RootedObject nexpando(cx, expando);
|
|
||||||
if (!GetOwnPropertyDescriptor(cx, nexpando, id, desc))
|
|
||||||
return false;
|
|
||||||
if (desc.object() == nexpando)
|
|
||||||
desc.object().set(obj);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
desc.object().set(nullptr);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */ bool
|
|
||||||
UnboxedPlainObject::obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
|
|
||||||
ObjectOpResult& result)
|
|
||||||
{
|
|
||||||
if (!convertToNative(cx, obj))
|
|
||||||
return false;
|
|
||||||
return DeleteProperty(cx, obj, id, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */ bool
|
|
||||||
UnboxedPlainObject::obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable)
|
|
||||||
{
|
|
||||||
if (!convertToNative(cx, obj))
|
|
||||||
return false;
|
|
||||||
return WatchProperty(cx, obj, id, callable);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */ bool
|
|
||||||
UnboxedPlainObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
|
|
||||||
bool enumerableOnly)
|
|
||||||
{
|
|
||||||
// Ignore expando properties here, they are special-cased by the property
|
|
||||||
// enumeration code.
|
|
||||||
|
|
||||||
const UnboxedLayout::PropertyVector& unboxed = obj->as<UnboxedPlainObject>().layout().properties();
|
|
||||||
for (size_t i = 0; i < unboxed.length(); i++) {
|
|
||||||
if (!properties.append(NameToId(unboxed[i].name)))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Class UnboxedExpandoObject::class_ = {
|
|
||||||
"UnboxedExpandoObject",
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
static const ClassOps UnboxedPlainObjectClassOps = {
|
|
||||||
nullptr, /* addProperty */
|
|
||||||
nullptr, /* delProperty */
|
|
||||||
nullptr, /* getProperty */
|
|
||||||
nullptr, /* setProperty */
|
|
||||||
nullptr, /* enumerate */
|
|
||||||
nullptr, /* resolve */
|
|
||||||
nullptr, /* mayResolve */
|
|
||||||
nullptr, /* finalize */
|
|
||||||
nullptr, /* call */
|
|
||||||
nullptr, /* hasInstance */
|
|
||||||
nullptr, /* construct */
|
|
||||||
UnboxedPlainObject::trace,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const ObjectOps UnboxedPlainObjectObjectOps = {
|
|
||||||
UnboxedPlainObject::obj_lookupProperty,
|
|
||||||
UnboxedPlainObject::obj_defineProperty,
|
|
||||||
UnboxedPlainObject::obj_hasProperty,
|
|
||||||
UnboxedPlainObject::obj_getProperty,
|
|
||||||
UnboxedPlainObject::obj_setProperty,
|
|
||||||
UnboxedPlainObject::obj_getOwnPropertyDescriptor,
|
|
||||||
UnboxedPlainObject::obj_deleteProperty,
|
|
||||||
UnboxedPlainObject::obj_watch,
|
|
||||||
nullptr, /* No unwatch needed, as watch() converts the object to native */
|
|
||||||
nullptr, /* getElements */
|
|
||||||
UnboxedPlainObject::obj_enumerate,
|
|
||||||
nullptr /* funToString */
|
|
||||||
};
|
|
||||||
|
|
||||||
const Class UnboxedPlainObject::class_ = {
|
|
||||||
js_Object_str,
|
|
||||||
Class::NON_NATIVE |
|
|
||||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
|
|
||||||
JSCLASS_DELAY_METADATA_BUILDER,
|
|
||||||
&UnboxedPlainObjectClassOps,
|
|
||||||
JS_NULL_CLASS_SPEC,
|
|
||||||
JS_NULL_CLASS_EXT,
|
|
||||||
&UnboxedPlainObjectObjectOps
|
|
||||||
};
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
// API
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static inline Value
|
|
||||||
NextValue(Handle<GCVector<Value>> values, size_t* valueCursor)
|
|
||||||
{
|
|
||||||
return values[(*valueCursor)++];
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
UnboxedPlainObject::fillAfterConvert(ExclusiveContext* cx,
|
|
||||||
Handle<GCVector<Value>> values, size_t* valueCursor)
|
|
||||||
{
|
|
||||||
initExpando();
|
|
||||||
memset(data(), 0, layout().size());
|
|
||||||
for (size_t i = 0; i < layout().properties().length(); i++)
|
|
||||||
JS_ALWAYS_TRUE(setValue(cx, layout().properties()[i], NextValue(values, valueCursor)));
|
|
||||||
}
|
|
||||||
|
|
@ -1,319 +0,0 @@
|
||||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
||||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
#ifndef vm_UnboxedObject_h
|
|
||||||
#define vm_UnboxedObject_h
|
|
||||||
|
|
||||||
#include "jsgc.h"
|
|
||||||
#include "jsobj.h"
|
|
||||||
|
|
||||||
#include "vm/Runtime.h"
|
|
||||||
#include "vm/TypeInference.h"
|
|
||||||
|
|
||||||
namespace js {
|
|
||||||
|
|
||||||
// Memory required for an unboxed value of a given type. Returns zero for types
|
|
||||||
// which can't be used for unboxed objects.
|
|
||||||
static inline size_t
|
|
||||||
UnboxedTypeSize(JSValueType type)
|
|
||||||
{
|
|
||||||
switch (type) {
|
|
||||||
case JSVAL_TYPE_BOOLEAN: return 1;
|
|
||||||
case JSVAL_TYPE_INT32: return 4;
|
|
||||||
case JSVAL_TYPE_DOUBLE: return 8;
|
|
||||||
case JSVAL_TYPE_STRING: return sizeof(void*);
|
|
||||||
case JSVAL_TYPE_OBJECT: return sizeof(void*);
|
|
||||||
default: return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool
|
|
||||||
UnboxedTypeNeedsPreBarrier(JSValueType type)
|
|
||||||
{
|
|
||||||
return type == JSVAL_TYPE_STRING || type == JSVAL_TYPE_OBJECT;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool
|
|
||||||
UnboxedTypeNeedsPostBarrier(JSValueType type)
|
|
||||||
{
|
|
||||||
return type == JSVAL_TYPE_OBJECT;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Class tracking information specific to unboxed objects.
|
|
||||||
class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
struct Property {
|
|
||||||
PropertyName* name;
|
|
||||||
uint32_t offset;
|
|
||||||
JSValueType type;
|
|
||||||
|
|
||||||
Property()
|
|
||||||
: name(nullptr), offset(UINT32_MAX), type(JSVAL_TYPE_MAGIC)
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef Vector<Property, 0, SystemAllocPolicy> PropertyVector;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// If objects in this group have ever been converted to native objects,
|
|
||||||
// these store the corresponding native group and initial shape for such
|
|
||||||
// objects. Type information for this object is reflected in nativeGroup.
|
|
||||||
GCPtrObjectGroup nativeGroup_;
|
|
||||||
GCPtrShape nativeShape_;
|
|
||||||
|
|
||||||
// Any script/pc which the associated group is created for.
|
|
||||||
GCPtrScript allocationScript_;
|
|
||||||
jsbytecode* allocationPc_;
|
|
||||||
|
|
||||||
// If nativeGroup is set and this object originally had a TypeNewScript or
|
|
||||||
// was keyed to an allocation site, this points to the group which replaced
|
|
||||||
// this one. This link is only needed to keep the replacement group from
|
|
||||||
// being GC'ed. If it were GC'ed and a new one regenerated later, that new
|
|
||||||
// group might have a different allocation kind from this group.
|
|
||||||
GCPtrObjectGroup replacementGroup_;
|
|
||||||
|
|
||||||
// The following members are only used for unboxed plain objects.
|
|
||||||
|
|
||||||
// All properties on objects with this layout, in enumeration order.
|
|
||||||
PropertyVector properties_;
|
|
||||||
|
|
||||||
// Byte size of the data for objects with this layout.
|
|
||||||
size_t size_;
|
|
||||||
|
|
||||||
// Any 'new' script information associated with this layout.
|
|
||||||
TypeNewScript* newScript_;
|
|
||||||
|
|
||||||
// List for use in tracing objects with this layout. This has the same
|
|
||||||
// structure as the trace list on a TypeDescr.
|
|
||||||
int32_t* traceList_;
|
|
||||||
|
|
||||||
// If this layout has been used to construct script or JSON constant
|
|
||||||
// objects, this code might be filled in to more quickly fill in objects
|
|
||||||
// from an array of values.
|
|
||||||
GCPtrJitCode constructorCode_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
UnboxedLayout()
|
|
||||||
: nativeGroup_(nullptr), nativeShape_(nullptr),
|
|
||||||
allocationScript_(nullptr), allocationPc_(nullptr), replacementGroup_(nullptr),
|
|
||||||
size_(0), newScript_(nullptr), traceList_(nullptr), constructorCode_(nullptr)
|
|
||||||
{}
|
|
||||||
|
|
||||||
bool initProperties(const PropertyVector& properties, size_t size) {
|
|
||||||
size_ = size;
|
|
||||||
return properties_.appendAll(properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
~UnboxedLayout() {
|
|
||||||
if (newScript_)
|
|
||||||
newScript_->clear();
|
|
||||||
js_delete(newScript_);
|
|
||||||
js_free(traceList_);
|
|
||||||
|
|
||||||
nativeGroup_.init(nullptr);
|
|
||||||
nativeShape_.init(nullptr);
|
|
||||||
replacementGroup_.init(nullptr);
|
|
||||||
constructorCode_.init(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void detachFromCompartment();
|
|
||||||
|
|
||||||
const PropertyVector& properties() const {
|
|
||||||
return properties_;
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeNewScript* newScript() const {
|
|
||||||
return newScript_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setNewScript(TypeNewScript* newScript, bool writeBarrier = true);
|
|
||||||
|
|
||||||
JSScript* allocationScript() const {
|
|
||||||
return allocationScript_;
|
|
||||||
}
|
|
||||||
|
|
||||||
jsbytecode* allocationPc() const {
|
|
||||||
return allocationPc_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setAllocationSite(JSScript* script, jsbytecode* pc) {
|
|
||||||
allocationScript_ = script;
|
|
||||||
allocationPc_ = pc;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int32_t* traceList() const {
|
|
||||||
return traceList_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setTraceList(int32_t* traceList) {
|
|
||||||
traceList_ = traceList;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Property* lookup(JSAtom* atom) const {
|
|
||||||
for (size_t i = 0; i < properties_.length(); i++) {
|
|
||||||
if (properties_[i].name == atom)
|
|
||||||
return &properties_[i];
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Property* lookup(jsid id) const {
|
|
||||||
if (JSID_IS_STRING(id))
|
|
||||||
return lookup(JSID_TO_ATOM(id));
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t size() const {
|
|
||||||
return size_;
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectGroup* nativeGroup() const {
|
|
||||||
return nativeGroup_;
|
|
||||||
}
|
|
||||||
|
|
||||||
Shape* nativeShape() const {
|
|
||||||
return nativeShape_;
|
|
||||||
}
|
|
||||||
|
|
||||||
jit::JitCode* constructorCode() const {
|
|
||||||
return constructorCode_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setConstructorCode(jit::JitCode* code) {
|
|
||||||
constructorCode_ = code;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline gc::AllocKind getAllocKind() const;
|
|
||||||
|
|
||||||
void trace(JSTracer* trc);
|
|
||||||
|
|
||||||
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
|
||||||
|
|
||||||
static bool makeNativeGroup(JSContext* cx, ObjectGroup* group);
|
|
||||||
static bool makeConstructorCode(JSContext* cx, HandleObjectGroup group);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Class for expando objects holding extra properties given to an unboxed plain
|
|
||||||
// object. These objects behave identically to normal native plain objects, and
|
|
||||||
// have a separate Class to distinguish them for memory usage reporting.
|
|
||||||
class UnboxedExpandoObject : public NativeObject
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static const Class class_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Class for a plain object using an unboxed representation. The physical
|
|
||||||
// layout of these objects is identical to that of an InlineTypedObject, though
|
|
||||||
// these objects use an UnboxedLayout instead of a TypeDescr to keep track of
|
|
||||||
// how their properties are stored.
|
|
||||||
class UnboxedPlainObject : public JSObject
|
|
||||||
{
|
|
||||||
// Optional object which stores extra properties on this object. This is
|
|
||||||
// not automatically barriered to avoid problems if the object is converted
|
|
||||||
// to a native. See ensureExpando().
|
|
||||||
UnboxedExpandoObject* expando_;
|
|
||||||
|
|
||||||
// Start of the inline data, which immediately follows the group and extra properties.
|
|
||||||
uint8_t data_[1];
|
|
||||||
|
|
||||||
public:
|
|
||||||
static const Class class_;
|
|
||||||
|
|
||||||
static bool obj_lookupProperty(JSContext* cx, HandleObject obj,
|
|
||||||
HandleId id, MutableHandleObject objp,
|
|
||||||
MutableHandleShape propp);
|
|
||||||
|
|
||||||
static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
|
|
||||||
Handle<PropertyDescriptor> desc,
|
|
||||||
ObjectOpResult& result);
|
|
||||||
|
|
||||||
static bool obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp);
|
|
||||||
|
|
||||||
static bool obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
|
|
||||||
HandleId id, MutableHandleValue vp);
|
|
||||||
|
|
||||||
static bool obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
|
|
||||||
HandleValue receiver, ObjectOpResult& result);
|
|
||||||
|
|
||||||
static bool obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
|
|
||||||
MutableHandle<PropertyDescriptor> desc);
|
|
||||||
|
|
||||||
static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
|
|
||||||
ObjectOpResult& result);
|
|
||||||
|
|
||||||
static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
|
|
||||||
bool enumerableOnly);
|
|
||||||
static bool obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable);
|
|
||||||
|
|
||||||
inline const UnboxedLayout& layout() const;
|
|
||||||
|
|
||||||
const UnboxedLayout& layoutDontCheckGeneration() const {
|
|
||||||
return group()->unboxedLayoutDontCheckGeneration();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t* data() {
|
|
||||||
return &data_[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
UnboxedExpandoObject* maybeExpando() const {
|
|
||||||
return expando_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void initExpando() {
|
|
||||||
expando_ = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For use during GC.
|
|
||||||
JSObject** addressOfExpando() {
|
|
||||||
return reinterpret_cast<JSObject**>(&expando_);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool containsUnboxedOrExpandoProperty(ExclusiveContext* cx, jsid id) const;
|
|
||||||
|
|
||||||
static UnboxedExpandoObject* ensureExpando(JSContext* cx, Handle<UnboxedPlainObject*> obj);
|
|
||||||
|
|
||||||
bool setValue(ExclusiveContext* cx, const UnboxedLayout::Property& property, const Value& v);
|
|
||||||
Value getValue(const UnboxedLayout::Property& property, bool maybeUninitialized = false);
|
|
||||||
|
|
||||||
static bool convertToNative(JSContext* cx, JSObject* obj);
|
|
||||||
static UnboxedPlainObject* create(ExclusiveContext* cx, HandleObjectGroup group,
|
|
||||||
NewObjectKind newKind);
|
|
||||||
static JSObject* createWithProperties(ExclusiveContext* cx, HandleObjectGroup group,
|
|
||||||
NewObjectKind newKind, IdValuePair* properties);
|
|
||||||
|
|
||||||
void fillAfterConvert(ExclusiveContext* cx,
|
|
||||||
Handle<GCVector<Value>> values, size_t* valueCursor);
|
|
||||||
|
|
||||||
static void trace(JSTracer* trc, JSObject* object);
|
|
||||||
|
|
||||||
static size_t offsetOfExpando() {
|
|
||||||
return offsetof(UnboxedPlainObject, expando_);
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t offsetOfData() {
|
|
||||||
return offsetof(UnboxedPlainObject, data_[0]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline gc::AllocKind
|
|
||||||
UnboxedLayout::getAllocKind() const
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(size());
|
|
||||||
return gc::GetGCObjectKindForBytes(UnboxedPlainObject::offsetOfData() + size());
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace js
|
|
||||||
|
|
||||||
namespace JS {
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct DeletePolicy<js::UnboxedLayout> : public js::GCManagedDeletePolicy<js::UnboxedLayout>
|
|
||||||
{};
|
|
||||||
|
|
||||||
} /* namespace JS */
|
|
||||||
|
|
||||||
#endif /* vm_UnboxedObject_h */
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue