mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 22:32:46 +00:00
Bug 1170372 - Use unboxed arrays for Array() and other functions keyed to allocation sites, r=jandem.
This commit is contained in:
parent
d225788d50
commit
a6cbfd3be7
@ -10989,7 +10989,7 @@ class CGDOMJSProxyHandler_getElements(ClassMethod):
|
||||
'jsvalRef': 'temp',
|
||||
'jsvalHandle': '&temp',
|
||||
'obj': 'proxy',
|
||||
'successCode': ("adder->append(cx, temp);\n"
|
||||
'successCode': ("if (!adder->append(cx, temp)) return false;\n"
|
||||
"continue;\n")
|
||||
}
|
||||
get = CGProxyIndexedGetter(self.descriptor, templateValues, False, False).define()
|
||||
|
@ -406,7 +406,7 @@ class JS_FRIEND_API(ElementAdder)
|
||||
|
||||
GetBehavior getBehavior() const { return getBehavior_; }
|
||||
|
||||
void append(JSContext* cx, JS::HandleValue v);
|
||||
bool append(JSContext* cx, JS::HandleValue v);
|
||||
void appendHole();
|
||||
};
|
||||
|
||||
|
@ -253,8 +253,7 @@ function ArrayMap(callbackfn/*, thisArg*/) {
|
||||
if (k in O) {
|
||||
/* Step c.i-iii. */
|
||||
var mappedValue = callFunction(callbackfn, T, O[k], k, O);
|
||||
// UnsafePutElements doesn't invoke setters, so we can use it here.
|
||||
UnsafePutElements(A, k, mappedValue);
|
||||
_DefineDataProperty(A, k, mappedValue);
|
||||
}
|
||||
}
|
||||
|
||||
@ -718,9 +717,6 @@ function ArrayFrom(items, mapfn=undefined, thisArg=undefined) {
|
||||
ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(1, mapfn));
|
||||
var T = thisArg;
|
||||
|
||||
// All elements defined by this algorithm have the same attrs:
|
||||
var attrs = ATTR_CONFIGURABLE | ATTR_ENUMERABLE | ATTR_WRITABLE;
|
||||
|
||||
// Steps 4-5.
|
||||
var usingIterator = GetMethod(items, std_iterator);
|
||||
|
||||
@ -757,7 +753,7 @@ function ArrayFrom(items, mapfn=undefined, thisArg=undefined) {
|
||||
var mappedValue = mapping ? callFunction(mapfn, thisArg, nextValue, k) : nextValue;
|
||||
|
||||
// Steps 6.g.ix-xi.
|
||||
_DefineDataProperty(A, k++, mappedValue, attrs);
|
||||
_DefineDataProperty(A, k++, mappedValue);
|
||||
}
|
||||
}
|
||||
|
||||
@ -782,7 +778,7 @@ function ArrayFrom(items, mapfn=undefined, thisArg=undefined) {
|
||||
var mappedValue = mapping ? callFunction(mapfn, thisArg, kValue, k) : kValue;
|
||||
|
||||
// Steps 16.f-g.
|
||||
_DefineDataProperty(A, k, mappedValue, attrs);
|
||||
_DefineDataProperty(A, k, mappedValue);
|
||||
}
|
||||
|
||||
// Steps 17-18.
|
||||
|
@ -929,15 +929,6 @@ function GetNumberOption(options, property, minimum, maximum, fallback) {
|
||||
/********** Property access for Intl objects **********/
|
||||
|
||||
|
||||
/**
|
||||
* Set a normal public property p of o to value v, but use Object.defineProperty
|
||||
* to avoid interference from setters on Object.prototype.
|
||||
*/
|
||||
function defineProperty(o, p, v) {
|
||||
_DefineDataProperty(o, p, v, ATTR_ENUMERABLE | ATTR_CONFIGURABLE | ATTR_WRITABLE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Weak map used to track the initialize-as-Intl status (and, if an object has
|
||||
* been so initialized, the Intl-specific internal properties) of all objects.
|
||||
@ -1458,7 +1449,7 @@ function Intl_Collator_resolvedOptions() {
|
||||
for (var i = 0; i < relevantExtensionKeys.length; i++) {
|
||||
var key = relevantExtensionKeys[i];
|
||||
var property = (key === "co") ? "collation" : collatorKeyMappings[key].property;
|
||||
defineProperty(result, property, internals[property]);
|
||||
_DefineDataProperty(result, property, internals[property]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -1901,7 +1892,7 @@ function Intl_NumberFormat_resolvedOptions() {
|
||||
for (var i = 0; i < optionalProperties.length; i++) {
|
||||
var p = optionalProperties[i];
|
||||
if (callFunction(std_Object_hasOwnProperty, internals, p))
|
||||
defineProperty(result, p, internals[p]);
|
||||
_DefineDataProperty(result, p, internals[p]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -2366,17 +2357,17 @@ function ToDateTimeOptions(options, required, defaults) {
|
||||
// the Throw parameter, while Object.defineProperty uses true. For the
|
||||
// calls here, the difference doesn't matter because we're adding
|
||||
// properties to a new object.
|
||||
defineProperty(options, "year", "numeric");
|
||||
defineProperty(options, "month", "numeric");
|
||||
defineProperty(options, "day", "numeric");
|
||||
_DefineDataProperty(options, "year", "numeric");
|
||||
_DefineDataProperty(options, "month", "numeric");
|
||||
_DefineDataProperty(options, "day", "numeric");
|
||||
}
|
||||
|
||||
// Step 8.
|
||||
if (needDefaults && (defaults === "time" || defaults === "all")) {
|
||||
// See comment for step 7.
|
||||
defineProperty(options, "hour", "numeric");
|
||||
defineProperty(options, "minute", "numeric");
|
||||
defineProperty(options, "second", "numeric");
|
||||
_DefineDataProperty(options, "hour", "numeric");
|
||||
_DefineDataProperty(options, "minute", "numeric");
|
||||
_DefineDataProperty(options, "second", "numeric");
|
||||
}
|
||||
|
||||
// Step 9.
|
||||
@ -2680,11 +2671,11 @@ function resolveICUPattern(pattern, result) {
|
||||
// skip other pattern characters and literal text
|
||||
}
|
||||
if (callFunction(std_Object_hasOwnProperty, icuPatternCharToComponent, c))
|
||||
defineProperty(result, icuPatternCharToComponent[c], value);
|
||||
_DefineDataProperty(result, icuPatternCharToComponent[c], value);
|
||||
if (c === "h" || c === "K")
|
||||
defineProperty(result, "hour12", true);
|
||||
_DefineDataProperty(result, "hour12", true);
|
||||
else if (c === "H" || c === "k")
|
||||
defineProperty(result, "hour12", false);
|
||||
_DefineDataProperty(result, "hour12", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,9 +14,7 @@
|
||||
AssertionFailed: false,
|
||||
MakeConstructible: false, DecompileArg: false,
|
||||
RuntimeDefaultLocale: false,
|
||||
ParallelDo: false, ParallelSlices: false, NewDenseArray: false,
|
||||
UnsafePutElements: false,
|
||||
ParallelTestsShouldPass: false,
|
||||
NewDenseArray: false,
|
||||
Dump: false,
|
||||
callFunction: false,
|
||||
TO_UINT32: false,
|
||||
|
@ -9306,8 +9306,8 @@ TryAttachFunCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script,
|
||||
}
|
||||
|
||||
static bool
|
||||
GetTemplateObjectForNative(JSContext* cx, HandleScript script, jsbytecode* pc,
|
||||
Native native, const CallArgs& args, MutableHandleObject res)
|
||||
GetTemplateObjectForNative(JSContext* cx, Native native, const CallArgs& args,
|
||||
MutableHandleObject res)
|
||||
{
|
||||
// Check for natives to which template objects can be attached. This is
|
||||
// done to provide templates to Ion for inlining these natives later on.
|
||||
@ -9321,27 +9321,16 @@ GetTemplateObjectForNative(JSContext* cx, HandleScript script, jsbytecode* pc,
|
||||
count = args.length();
|
||||
else if (args.length() == 1 && args[0].isInt32() && args[0].toInt32() >= 0)
|
||||
count = args[0].toInt32();
|
||||
res.set(NewDenseUnallocatedArray(cx, count, nullptr, TenuredObject));
|
||||
if (!res)
|
||||
return false;
|
||||
|
||||
ObjectGroup* group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array);
|
||||
if (!group)
|
||||
return false;
|
||||
res->setGroup(group);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (native == intrinsic_NewDenseArray) {
|
||||
res.set(NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject));
|
||||
if (!res)
|
||||
return false;
|
||||
|
||||
ObjectGroup* group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array);
|
||||
if (!group)
|
||||
return false;
|
||||
res->setGroup(group);
|
||||
return true;
|
||||
if (count <= ArrayObject::EagerAllocationMaxLength) {
|
||||
// With this and other array templates, set forceAnalyze so that we
|
||||
// don't end up with a template whose structure might change later.
|
||||
res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, count, TenuredObject,
|
||||
/* forceAnalyze = */ true));
|
||||
if (!res)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (native == js::array_concat || native == js::array_slice) {
|
||||
@ -9354,14 +9343,10 @@ GetTemplateObjectForNative(JSContext* cx, HandleScript script, jsbytecode* pc,
|
||||
}
|
||||
|
||||
if (native == js::str_split && args.length() == 1 && args[0].isString()) {
|
||||
res.set(NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject));
|
||||
res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, 0, TenuredObject,
|
||||
/* forceAnalyze = */ true));
|
||||
if (!res)
|
||||
return false;
|
||||
|
||||
ObjectGroup* group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array);
|
||||
if (!group)
|
||||
return false;
|
||||
res->setGroup(group);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -9655,7 +9640,7 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb
|
||||
RootedObject templateObject(cx);
|
||||
if (MOZ_LIKELY(!isSpread)) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (!GetTemplateObjectForNative(cx, script, pc, fun->native(), args, &templateObject))
|
||||
if (!GetTemplateObjectForNative(cx, fun->native(), args, &templateObject))
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -9677,24 +9662,16 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb
|
||||
}
|
||||
|
||||
static bool
|
||||
CopyArray(JSContext* cx, HandleArrayObject obj, MutableHandleValue result)
|
||||
CopyArray(JSContext* cx, HandleObject obj, MutableHandleValue result)
|
||||
{
|
||||
MOZ_ASSERT(obj->is<ArrayObject>());
|
||||
uint32_t length = obj->as<ArrayObject>().length();
|
||||
MOZ_ASSERT(obj->getDenseInitializedLength() == length);
|
||||
|
||||
RootedObjectGroup group(cx, obj->getGroup(cx));
|
||||
if (!group)
|
||||
uint32_t length = GetAnyBoxedOrUnboxedArrayLength(obj);
|
||||
JSObject* nobj = NewFullyAllocatedArrayTryReuseGroup(cx, obj, length, TenuredObject,
|
||||
/* forceAnalyze = */ true);
|
||||
if (!nobj)
|
||||
return false;
|
||||
CopyAnyBoxedOrUnboxedDenseElements(cx, nobj, obj, 0, 0, length);
|
||||
|
||||
RootedArrayObject newObj(cx, NewDenseFullyAllocatedArray(cx, length, nullptr, TenuredObject));
|
||||
if (!newObj)
|
||||
return false;
|
||||
|
||||
newObj->setGroup(group);
|
||||
newObj->setDenseInitializedLength(length);
|
||||
newObj->initDenseElements(0, obj->getDenseElements(), length);
|
||||
result.setObject(*newObj);
|
||||
result.setObject(*nobj);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -9722,7 +9699,7 @@ TryAttachStringSplit(JSContext* cx, ICCall_Fallback* stub, HandleScript script,
|
||||
|
||||
RootedString thisString(cx, thisv.toString());
|
||||
RootedString argString(cx, args[0].toString());
|
||||
RootedArrayObject obj(cx, &res.toObject().as<ArrayObject>());
|
||||
RootedObject obj(cx, &res.toObject());
|
||||
RootedValue arr(cx);
|
||||
|
||||
// Copy the array before storing in stub.
|
||||
@ -9730,14 +9707,17 @@ TryAttachStringSplit(JSContext* cx, ICCall_Fallback* stub, HandleScript script,
|
||||
return false;
|
||||
|
||||
// Atomize all elements of the array.
|
||||
RootedArrayObject arrObj(cx, &arr.toObject().as<ArrayObject>());
|
||||
uint32_t initLength = arrObj->length();
|
||||
RootedObject arrObj(cx, &arr.toObject());
|
||||
uint32_t initLength = GetAnyBoxedOrUnboxedArrayLength(arrObj);
|
||||
for (uint32_t i = 0; i < initLength; i++) {
|
||||
JSAtom* str = js::AtomizeString(cx, arrObj->getDenseElement(i).toString());
|
||||
JSAtom* str = js::AtomizeString(cx, GetAnyBoxedOrUnboxedDenseElement(arrObj, i).toString());
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
arrObj->setDenseElement(i, StringValue(str));
|
||||
if (!SetAnyBoxedOrUnboxedDenseElement(cx, arrObj, i, StringValue(str))) {
|
||||
// The value could not be stored to an unboxed dense element.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ICCall_StringSplit::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
|
||||
@ -10595,7 +10575,7 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler& masm)
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef bool (*CopyArrayFn)(JSContext*, HandleArrayObject, MutableHandleValue);
|
||||
typedef bool (*CopyArrayFn)(JSContext*, HandleObject, MutableHandleValue);
|
||||
static const VMFunction CopyArrayInfo = FunctionInfo<CopyArrayFn>(CopyArray);
|
||||
|
||||
bool
|
||||
|
@ -609,6 +609,11 @@ class ICNewArray_Fallback : public ICFallbackStub
|
||||
HeapPtrObjectGroup& templateGroup() {
|
||||
return templateGroup_;
|
||||
}
|
||||
|
||||
void setTemplateGroup(ObjectGroup* group) {
|
||||
templateObject_ = nullptr;
|
||||
templateGroup_ = group;
|
||||
}
|
||||
};
|
||||
|
||||
class ICNewObject_Fallback : public ICFallbackStub
|
||||
@ -4914,10 +4919,10 @@ class ICCall_StringSplit : public ICMonitoredStub
|
||||
uint32_t pcOffset_;
|
||||
HeapPtrString expectedThis_;
|
||||
HeapPtrString expectedArg_;
|
||||
HeapPtrArrayObject templateObject_;
|
||||
HeapPtrObject templateObject_;
|
||||
|
||||
ICCall_StringSplit(JitCode* stubCode, ICStub* firstMonitorStub, uint32_t pcOffset, JSString* thisString,
|
||||
JSString* argString, ArrayObject* templateObject)
|
||||
JSString* argString, JSObject* templateObject)
|
||||
: ICMonitoredStub(ICStub::Call_StringSplit, stubCode, firstMonitorStub),
|
||||
pcOffset_(pcOffset), expectedThis_(thisString), expectedArg_(argString),
|
||||
templateObject_(templateObject)
|
||||
@ -4944,7 +4949,7 @@ class ICCall_StringSplit : public ICMonitoredStub
|
||||
return expectedArg_;
|
||||
}
|
||||
|
||||
HeapPtrArrayObject& templateObject() {
|
||||
HeapPtrObject& templateObject() {
|
||||
return templateObject_;
|
||||
}
|
||||
|
||||
@ -4954,7 +4959,7 @@ class ICCall_StringSplit : public ICMonitoredStub
|
||||
uint32_t pcOffset_;
|
||||
RootedString expectedThis_;
|
||||
RootedString expectedArg_;
|
||||
RootedArrayObject templateObject_;
|
||||
RootedObject templateObject_;
|
||||
|
||||
bool generateStubCode(MacroAssembler& masm);
|
||||
|
||||
@ -4971,7 +4976,7 @@ class ICCall_StringSplit : public ICMonitoredStub
|
||||
pcOffset_(pcOffset),
|
||||
expectedThis_(cx, thisString),
|
||||
expectedArg_(cx, argString),
|
||||
templateObject_(cx, &templateObject.toObject().as<ArrayObject>())
|
||||
templateObject_(cx, &templateObject.toObject())
|
||||
{ }
|
||||
|
||||
ICStub* getStub(ICStubSpace* space) {
|
||||
|
@ -521,7 +521,7 @@ BaselineInspector::getTemplateObjectForNative(jsbytecode* pc, Native native)
|
||||
|
||||
bool
|
||||
BaselineInspector::isOptimizableCallStringSplit(jsbytecode* pc, JSString** stringOut, JSString** stringArg,
|
||||
NativeObject** objOut)
|
||||
JSObject** objOut)
|
||||
{
|
||||
if (!hasBaselineScript())
|
||||
return false;
|
||||
@ -733,6 +733,7 @@ BaselineInspector::expectedPropertyAccessInputType(jsbytecode* pc)
|
||||
return MIRType_Value;
|
||||
|
||||
case ICStub::GetProp_ArrayLength:
|
||||
case ICStub::GetProp_UnboxedArrayLength:
|
||||
case ICStub::GetProp_Native:
|
||||
case ICStub::GetProp_NativeDoesNotExist:
|
||||
case ICStub::GetProp_NativePrototype:
|
||||
@ -750,6 +751,7 @@ BaselineInspector::expectedPropertyAccessInputType(jsbytecode* pc)
|
||||
case ICStub::GetElem_String:
|
||||
case ICStub::GetElem_Dense:
|
||||
case ICStub::GetElem_TypedArray:
|
||||
case ICStub::GetElem_UnboxedArray:
|
||||
stubType = MIRType_Object;
|
||||
break;
|
||||
|
||||
|
@ -113,7 +113,7 @@ class BaselineInspector
|
||||
bool hasSeenNonStringIterMore(jsbytecode* pc);
|
||||
|
||||
bool isOptimizableCallStringSplit(jsbytecode* pc, JSString** stringOut, JSString** stringArg,
|
||||
NativeObject** objOut);
|
||||
JSObject** objOut);
|
||||
JSObject* getTemplateObject(jsbytecode* pc);
|
||||
JSObject* getTemplateObjectForNative(jsbytecode* pc, Native native);
|
||||
JSObject* getTemplateObjectForClassHook(jsbytecode* pc, const Class* clasp);
|
||||
|
@ -4154,9 +4154,20 @@ typedef JSObject* (*NewArrayOperationFn)(JSContext*, HandleScript, jsbytecode*,
|
||||
static const VMFunction NewArrayOperationInfo =
|
||||
FunctionInfo<NewArrayOperationFn>(NewArrayOperation);
|
||||
|
||||
typedef ArrayObject* (*NewDenseArrayFn)(ExclusiveContext*, uint32_t, HandleObjectGroup,
|
||||
AllocatingBehaviour, bool);
|
||||
static const VMFunction NewDenseArrayInfo = FunctionInfo<NewDenseArrayFn>(NewDenseArray);
|
||||
static JSObject*
|
||||
NewArrayWithGroup(JSContext* cx, uint32_t length, HandleObjectGroup group,
|
||||
bool convertDoubleElements)
|
||||
{
|
||||
JSObject* res = NewFullyAllocatedArrayTryUseGroup(cx, group, length);
|
||||
if (!res)
|
||||
return nullptr;
|
||||
if (convertDoubleElements)
|
||||
res->as<ArrayObject>().setShouldConvertDoubleElements();
|
||||
return res;
|
||||
}
|
||||
|
||||
typedef JSObject* (*NewArrayWithGroupFn)(JSContext*, uint32_t, HandleObjectGroup, bool);
|
||||
static const VMFunction NewArrayWithGroupInfo = FunctionInfo<NewArrayWithGroupFn>(NewArrayWithGroup);
|
||||
|
||||
void
|
||||
CodeGenerator::visitNewArrayCallVM(LNewArray* lir)
|
||||
@ -4168,13 +4179,12 @@ CodeGenerator::visitNewArrayCallVM(LNewArray* lir)
|
||||
|
||||
JSObject* templateObject = lir->mir()->templateObject();
|
||||
|
||||
if (templateObject && !templateObject->is<UnboxedArrayObject>()) {
|
||||
if (templateObject) {
|
||||
pushArg(Imm32(lir->mir()->convertDoubleElements()));
|
||||
pushArg(Imm32(lir->mir()->allocatingBehaviour()));
|
||||
pushArg(ImmGCPtr(templateObject->group()));
|
||||
pushArg(Imm32(lir->mir()->count()));
|
||||
|
||||
callVM(NewDenseArrayInfo, lir);
|
||||
callVM(NewArrayWithGroupInfo, lir);
|
||||
} else {
|
||||
pushArg(Imm32(GenericObject));
|
||||
pushArg(Imm32(lir->mir()->count()));
|
||||
@ -4297,7 +4307,7 @@ CodeGenerator::visitNewArrayCopyOnWrite(LNewArrayCopyOnWrite* lir)
|
||||
masm.bind(ool->rejoin());
|
||||
}
|
||||
|
||||
typedef ArrayObject* (*ArrayConstructorOneArgFn)(JSContext*, HandleObjectGroup, int32_t length);
|
||||
typedef JSObject* (*ArrayConstructorOneArgFn)(JSContext*, HandleObjectGroup, int32_t length);
|
||||
static const VMFunction ArrayConstructorOneArgInfo =
|
||||
FunctionInfo<ArrayConstructorOneArgFn>(ArrayConstructorOneArg);
|
||||
|
||||
@ -4308,32 +4318,47 @@ CodeGenerator::visitNewArrayDynamicLength(LNewArrayDynamicLength* lir)
|
||||
Register objReg = ToRegister(lir->output());
|
||||
Register tempReg = ToRegister(lir->temp());
|
||||
|
||||
ArrayObject* templateObject = lir->mir()->templateObject();
|
||||
JSObject* templateObject = lir->mir()->templateObject();
|
||||
gc::InitialHeap initialHeap = lir->mir()->initialHeap();
|
||||
|
||||
OutOfLineCode* ool = oolCallVM(ArrayConstructorOneArgInfo, lir,
|
||||
(ArgList(), ImmGCPtr(templateObject->group()), lengthReg),
|
||||
StoreRegisterTo(objReg));
|
||||
|
||||
size_t numSlots = gc::GetGCKindSlots(templateObject->asTenured().getAllocKind());
|
||||
size_t inlineLength = numSlots >= ObjectElements::VALUES_PER_HEADER
|
||||
? numSlots - ObjectElements::VALUES_PER_HEADER
|
||||
: 0;
|
||||
bool canInline = true;
|
||||
size_t inlineLength = 0;
|
||||
if (templateObject->is<ArrayObject>()) {
|
||||
if (templateObject->as<ArrayObject>().hasFixedElements()) {
|
||||
size_t numSlots = gc::GetGCKindSlots(templateObject->asTenured().getAllocKind());
|
||||
inlineLength = numSlots - ObjectElements::VALUES_PER_HEADER;
|
||||
} else {
|
||||
canInline = false;
|
||||
}
|
||||
} else {
|
||||
if (templateObject->as<UnboxedArrayObject>().hasInlineElements()) {
|
||||
size_t nbytes =
|
||||
templateObject->tenuredSizeOfThis() - UnboxedArrayObject::offsetOfInlineElements();
|
||||
inlineLength = nbytes / templateObject->as<UnboxedArrayObject>().elementSize();
|
||||
} else {
|
||||
canInline = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to do the allocation inline if the template object is big enough
|
||||
// for the length in lengthReg. If the length is bigger we could still
|
||||
// use the template object and not allocate the elements, but it's more
|
||||
// efficient to do a single big allocation than (repeatedly) reallocating
|
||||
// the array later on when filling it.
|
||||
if (!templateObject->isSingleton() && templateObject->length() <= inlineLength)
|
||||
masm.branch32(Assembler::Above, lengthReg, Imm32(templateObject->length()), ool->entry());
|
||||
else
|
||||
if (canInline) {
|
||||
// Try to do the allocation inline if the template object is big enough
|
||||
// for the length in lengthReg. If the length is bigger we could still
|
||||
// use the template object and not allocate the elements, but it's more
|
||||
// efficient to do a single big allocation than (repeatedly) reallocating
|
||||
// the array later on when filling it.
|
||||
masm.branch32(Assembler::Above, lengthReg, Imm32(inlineLength), ool->entry());
|
||||
|
||||
masm.createGCObject(objReg, tempReg, templateObject, initialHeap, ool->entry());
|
||||
|
||||
size_t lengthOffset = NativeObject::offsetOfFixedElements() + ObjectElements::offsetOfLength();
|
||||
masm.store32(lengthReg, Address(objReg, lengthOffset));
|
||||
} else {
|
||||
masm.jump(ool->entry());
|
||||
|
||||
masm.createGCObject(objReg, tempReg, templateObject, initialHeap, ool->entry());
|
||||
|
||||
size_t lengthOffset = NativeObject::offsetOfFixedElements() + ObjectElements::offsetOfLength();
|
||||
masm.store32(lengthReg, Address(objReg, lengthOffset));
|
||||
}
|
||||
|
||||
masm.bind(ool->rejoin());
|
||||
}
|
||||
@ -6398,6 +6423,25 @@ CodeGenerator::visitIncrementUnboxedArrayInitializedLength(LIncrementUnboxedArra
|
||||
masm.add32(Imm32(1), Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()));
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitSetUnboxedArrayInitializedLength(LSetUnboxedArrayInitializedLength* lir)
|
||||
{
|
||||
Register obj = ToRegister(lir->object());
|
||||
Int32Key key = ToInt32Key(lir->length());
|
||||
Register temp = ToRegister(lir->temp());
|
||||
|
||||
Address initLengthAddr(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength());
|
||||
masm.load32(initLengthAddr, temp);
|
||||
masm.and32(Imm32(UnboxedArrayObject::CapacityMask), temp);
|
||||
|
||||
if (key.isRegister())
|
||||
masm.or32(key.reg(), temp);
|
||||
else
|
||||
masm.or32(Imm32(key.constant()), temp);
|
||||
|
||||
masm.store32(temp, initLengthAddr);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitNotO(LNotO* lir)
|
||||
{
|
||||
@ -9360,7 +9404,7 @@ CodeGenerator::visitInArray(LInArray* lir)
|
||||
}
|
||||
|
||||
masm.branch32(Assembler::BelowOrEqual, initLength, Imm32(index), failedInitLength);
|
||||
if (mir->needsHoleCheck()) {
|
||||
if (mir->needsHoleCheck() && mir->unboxedType() == JSVAL_TYPE_MAGIC) {
|
||||
NativeObject::elementsSizeMustNotOverflow();
|
||||
Address address = Address(elements, index * sizeof(Value));
|
||||
masm.branchTestMagic(Assembler::Equal, address, &falseBranch);
|
||||
@ -9373,7 +9417,7 @@ CodeGenerator::visitInArray(LInArray* lir)
|
||||
failedInitLength = &negativeIntCheck;
|
||||
|
||||
masm.branch32(Assembler::BelowOrEqual, initLength, index, failedInitLength);
|
||||
if (mir->needsHoleCheck()) {
|
||||
if (mir->needsHoleCheck() && mir->unboxedType() == JSVAL_TYPE_MAGIC) {
|
||||
BaseIndex address = BaseIndex(elements, ToRegister(lir->index()), TimesEight);
|
||||
masm.branchTestMagic(Assembler::Equal, address, &falseBranch);
|
||||
}
|
||||
|
@ -198,6 +198,7 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
void visitUnboxedArrayLength(LUnboxedArrayLength* lir);
|
||||
void visitUnboxedArrayInitializedLength(LUnboxedArrayInitializedLength* lir);
|
||||
void visitIncrementUnboxedArrayInitializedLength(LIncrementUnboxedArrayInitializedLength* lir);
|
||||
void visitSetUnboxedArrayInitializedLength(LSetUnboxedArrayInitializedLength* lir);
|
||||
void visitNotO(LNotO* ins);
|
||||
void visitNotV(LNotV* ins);
|
||||
void visitBoundsCheck(LBoundsCheck* lir);
|
||||
|
@ -6504,8 +6504,7 @@ IonBuilder::jsop_newarray(uint32_t count)
|
||||
}
|
||||
current->add(templateConst);
|
||||
|
||||
MNewArray* ins = MNewArray::New(alloc(), constraints(), count, templateConst,
|
||||
heap, NewArray_FullyAllocating, pc);
|
||||
MNewArray* ins = MNewArray::New(alloc(), constraints(), count, templateConst, heap, pc);
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
|
||||
@ -6623,7 +6622,15 @@ IonBuilder::jsop_initelem_array()
|
||||
return resumeAfter(store);
|
||||
}
|
||||
|
||||
MConstant* id = MConstant::New(alloc(), Int32Value(GET_UINT24(pc)));
|
||||
return initializeArrayElement(obj, GET_UINT24(pc), value, unboxedType, /* addResumePoint = */ true);
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::initializeArrayElement(MDefinition* obj, size_t index, MDefinition* value,
|
||||
JSValueType unboxedType,
|
||||
bool addResumePointAndIncrementInitializedLength)
|
||||
{
|
||||
MConstant* id = MConstant::New(alloc(), Int32Value(index));
|
||||
current->add(id);
|
||||
|
||||
// Get the elements vector.
|
||||
@ -6634,11 +6641,13 @@ IonBuilder::jsop_initelem_array()
|
||||
// Note: storeUnboxedValue takes care of any post barriers on the value.
|
||||
storeUnboxedValue(obj, elements, 0, id, unboxedType, value, /* preBarrier = */ false);
|
||||
|
||||
MInstruction* increment = MIncrementUnboxedArrayInitializedLength::New(alloc(), obj);
|
||||
current->add(increment);
|
||||
if (addResumePointAndIncrementInitializedLength) {
|
||||
MInstruction* increment = MIncrementUnboxedArrayInitializedLength::New(alloc(), obj);
|
||||
current->add(increment);
|
||||
|
||||
if (!resumeAfter(increment))
|
||||
return false;
|
||||
if (!resumeAfter(increment))
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (NeedsPostBarrier(info(), value))
|
||||
current->add(MPostWriteBarrier::New(alloc(), obj, value));
|
||||
@ -6654,14 +6663,16 @@ IonBuilder::jsop_initelem_array()
|
||||
/* needsHoleCheck = */ false);
|
||||
current->add(store);
|
||||
|
||||
// Update the initialized length. (The template object for this array has
|
||||
// the array's ultimate length, so the length field is already correct: no
|
||||
// updating needed.)
|
||||
MSetInitializedLength* initLength = MSetInitializedLength::New(alloc(), elements, id);
|
||||
current->add(initLength);
|
||||
if (addResumePointAndIncrementInitializedLength) {
|
||||
// Update the initialized length. (The template object for this
|
||||
// array has the array's ultimate length, so the length field is
|
||||
// already correct: no updating needed.)
|
||||
MSetInitializedLength* initLength = MSetInitializedLength::New(alloc(), elements, id);
|
||||
current->add(initLength);
|
||||
|
||||
if (!resumeAfter(initLength))
|
||||
return false;
|
||||
if (!resumeAfter(initLength))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -8530,12 +8541,7 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType
|
||||
// Note: to help GVN, use the original MElements instruction and not
|
||||
// MConvertElementsToDoubles as operand. This is fine because converting
|
||||
// elements to double does not change the initialized length.
|
||||
MInstruction* initLength;
|
||||
if (unboxedType != JSVAL_TYPE_MAGIC)
|
||||
initLength = MUnboxedArrayInitializedLength::New(alloc(), obj);
|
||||
else
|
||||
initLength = MInitializedLength::New(alloc(), elements);
|
||||
current->add(initLength);
|
||||
MInstruction* initLength = initializedLength(obj, elements, unboxedType);
|
||||
|
||||
// If we can load the element as a definite double, make sure to check that
|
||||
// the array has been converted to homogenous doubles first.
|
||||
@ -8825,9 +8831,13 @@ IonBuilder::jsop_setelem()
|
||||
if (!setElemTryTypedArray(&emitted, object, index, value) || emitted)
|
||||
return emitted;
|
||||
|
||||
trackOptimizationAttempt(TrackedStrategy::SetElem_Dense);
|
||||
if (!setElemTryDense(&emitted, object, index, value) || emitted)
|
||||
return emitted;
|
||||
{
|
||||
trackOptimizationAttempt(TrackedStrategy::SetElem_Dense);
|
||||
SetElemICInspector icInspect(inspector->setElemICInspector(pc));
|
||||
bool writeHole = icInspect.sawOOBDenseWrite();
|
||||
if (!setElemTryDense(&emitted, object, index, value, writeHole) || emitted)
|
||||
return emitted;
|
||||
}
|
||||
|
||||
trackOptimizationAttempt(TrackedStrategy::SetElem_Arguments);
|
||||
if (!setElemTryArguments(&emitted, object, index, value) || emitted)
|
||||
@ -9020,7 +9030,7 @@ IonBuilder::setElemTryTypedArray(bool* emitted, MDefinition* object,
|
||||
}
|
||||
|
||||
// Emit typed setelem variant.
|
||||
if (!jsop_setelem_typed(arrayType, SetElem_Normal, object, index, value))
|
||||
if (!jsop_setelem_typed(arrayType, object, index, value))
|
||||
return false;
|
||||
|
||||
trackOptimizationSuccess();
|
||||
@ -9030,7 +9040,7 @@ IonBuilder::setElemTryTypedArray(bool* emitted, MDefinition* object,
|
||||
|
||||
bool
|
||||
IonBuilder::setElemTryDense(bool* emitted, MDefinition* object,
|
||||
MDefinition* index, MDefinition* value)
|
||||
MDefinition* index, MDefinition* value, bool writeHole)
|
||||
{
|
||||
MOZ_ASSERT(*emitted == false);
|
||||
|
||||
@ -9073,7 +9083,7 @@ IonBuilder::setElemTryDense(bool* emitted, MDefinition* object,
|
||||
}
|
||||
|
||||
// Emit dense setelem variant.
|
||||
if (!jsop_setelem_dense(conversion, SetElem_Normal, object, index, value, unboxedType))
|
||||
if (!jsop_setelem_dense(conversion, object, index, value, unboxedType, writeHole))
|
||||
return false;
|
||||
|
||||
trackOptimizationSuccess();
|
||||
@ -9159,9 +9169,8 @@ IonBuilder::setElemTryCache(bool* emitted, MDefinition* object,
|
||||
|
||||
bool
|
||||
IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
|
||||
SetElemSafety safety,
|
||||
MDefinition* obj, MDefinition* id, MDefinition* value,
|
||||
JSValueType unboxedType)
|
||||
JSValueType unboxedType, bool writeHole)
|
||||
{
|
||||
MIRType elementType = MIRType_None;
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC)
|
||||
@ -9213,20 +9222,12 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
|
||||
MOZ_CRASH("Unknown double conversion");
|
||||
}
|
||||
|
||||
bool writeHole = false;
|
||||
if (safety == SetElem_Normal) {
|
||||
SetElemICInspector icInspect(inspector->setElemICInspector(pc));
|
||||
writeHole = icInspect.sawOOBDenseWrite();
|
||||
}
|
||||
|
||||
// Use MStoreElementHole if this SETELEM has written to out-of-bounds
|
||||
// indexes in the past. Otherwise, use MStoreElement so that we can hoist
|
||||
// the initialized length and bounds check.
|
||||
MInstruction* store;
|
||||
MStoreElementCommon *common = nullptr;
|
||||
if (writeHole && writeOutOfBounds) {
|
||||
MOZ_ASSERT(safety == SetElem_Normal);
|
||||
|
||||
MStoreElementHole* ins = MStoreElementHole::New(alloc(), obj, elements, id, newValue, unboxedType);
|
||||
store = ins;
|
||||
common = ins;
|
||||
@ -9234,20 +9235,10 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
|
||||
current->add(ins);
|
||||
current->push(value);
|
||||
} else {
|
||||
MInstruction* initLength;
|
||||
if (unboxedType != JSVAL_TYPE_MAGIC)
|
||||
initLength = MUnboxedArrayInitializedLength::New(alloc(), obj);
|
||||
else
|
||||
initLength = MInitializedLength::New(alloc(), elements);
|
||||
current->add(initLength);
|
||||
MInstruction* initLength = initializedLength(obj, elements, unboxedType);
|
||||
|
||||
bool needsHoleCheck;
|
||||
if (safety == SetElem_Normal) {
|
||||
id = addBoundsCheck(id, initLength);
|
||||
needsHoleCheck = !packed && !writeOutOfBounds;
|
||||
} else {
|
||||
needsHoleCheck = false;
|
||||
}
|
||||
id = addBoundsCheck(id, initLength);
|
||||
bool needsHoleCheck = !packed && !writeOutOfBounds;
|
||||
|
||||
if (unboxedType != JSVAL_TYPE_MAGIC) {
|
||||
store = storeUnboxedValue(obj, elements, 0, id, unboxedType, newValue);
|
||||
@ -9259,8 +9250,7 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
|
||||
current->add(store);
|
||||
}
|
||||
|
||||
if (safety == SetElem_Normal)
|
||||
current->push(value);
|
||||
current->push(value);
|
||||
}
|
||||
|
||||
if (!resumeAfter(store))
|
||||
@ -9280,16 +9270,11 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
|
||||
|
||||
|
||||
bool
|
||||
IonBuilder::jsop_setelem_typed(Scalar::Type arrayType, SetElemSafety safety,
|
||||
IonBuilder::jsop_setelem_typed(Scalar::Type arrayType,
|
||||
MDefinition* obj, MDefinition* id, MDefinition* value)
|
||||
{
|
||||
bool expectOOB;
|
||||
if (safety == SetElem_Normal) {
|
||||
SetElemICInspector icInspect(inspector->setElemICInspector(pc));
|
||||
expectOOB = icInspect.sawOOBTypedArrayWrite();
|
||||
} else {
|
||||
expectOOB = false;
|
||||
}
|
||||
SetElemICInspector icInspect(inspector->setElemICInspector(pc));
|
||||
bool expectOOB = icInspect.sawOOBTypedArrayWrite();
|
||||
|
||||
if (expectOOB)
|
||||
spew("Emitting OOB TypedArray SetElem");
|
||||
@ -9302,9 +9287,7 @@ IonBuilder::jsop_setelem_typed(Scalar::Type arrayType, SetElemSafety safety,
|
||||
// Get length, bounds-check, then get elements, and add all instructions.
|
||||
MInstruction* length;
|
||||
MInstruction* elements;
|
||||
BoundsChecking checking = (!expectOOB && safety == SetElem_Normal)
|
||||
? DoBoundsCheck
|
||||
: SkipBoundsCheck;
|
||||
BoundsChecking checking = expectOOB ? SkipBoundsCheck : DoBoundsCheck;
|
||||
addTypedArrayLengthAndData(obj, checking, &id, &length, &elements);
|
||||
|
||||
// Clamp value to [0, 255] for Uint8ClampedArray.
|
||||
@ -9325,31 +9308,11 @@ IonBuilder::jsop_setelem_typed(Scalar::Type arrayType, SetElemSafety safety,
|
||||
}
|
||||
|
||||
current->add(ins);
|
||||
|
||||
if (safety == SetElem_Normal)
|
||||
current->push(value);
|
||||
current->push(value);
|
||||
|
||||
return resumeAfter(ins);
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::jsop_setelem_typed_object(Scalar::Type arrayType, SetElemSafety safety,
|
||||
MDefinition* object, MDefinition* index, MDefinition* value)
|
||||
{
|
||||
MOZ_ASSERT(safety == SetElem_Unsafe); // Can be fixed, but there's been no reason to as of yet
|
||||
|
||||
MInstruction* intIndex = MToInt32::New(alloc(), index);
|
||||
current->add(intIndex);
|
||||
|
||||
size_t elemSize = ScalarTypeDescr::alignment(arrayType);
|
||||
|
||||
LinearSum byteOffset(alloc());
|
||||
if (!byteOffset.add(intIndex, elemSize))
|
||||
setForceAbort();
|
||||
|
||||
return storeScalarTypedObjectValue(object, byteOffset, arrayType, value);
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::jsop_length()
|
||||
{
|
||||
@ -9511,8 +9474,7 @@ IonBuilder::jsop_rest()
|
||||
current->add(templateConst);
|
||||
|
||||
MNewArray* array = MNewArray::New(alloc(), constraints(), numRest, templateConst,
|
||||
templateObject->group()->initialHeap(constraints()),
|
||||
NewArray_FullyAllocating, pc);
|
||||
templateObject->group()->initialHeap(constraints()), pc);
|
||||
current->add(array);
|
||||
|
||||
if (numRest == 0) {
|
||||
@ -12396,12 +12358,21 @@ IonBuilder::jsop_in()
|
||||
MDefinition* obj = current->peek(-1);
|
||||
MDefinition* id = current->peek(-2);
|
||||
|
||||
if (!shouldAbortOnPreliminaryGroups(obj) &&
|
||||
ElementAccessIsDenseNative(constraints(), obj, id) &&
|
||||
!ElementAccessHasExtraIndexedProperty(constraints(), obj))
|
||||
{
|
||||
return jsop_in_dense();
|
||||
}
|
||||
do {
|
||||
if (shouldAbortOnPreliminaryGroups(obj))
|
||||
break;
|
||||
|
||||
JSValueType unboxedType = UnboxedArrayElementType(constraints(), obj, id);
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC) {
|
||||
if (!ElementAccessIsDenseNative(constraints(), obj, id))
|
||||
break;
|
||||
}
|
||||
|
||||
if (ElementAccessHasExtraIndexedProperty(constraints(), obj))
|
||||
break;
|
||||
|
||||
return jsop_in_dense(unboxedType);
|
||||
} while (false);
|
||||
|
||||
current->pop();
|
||||
current->pop();
|
||||
@ -12414,7 +12385,7 @@ IonBuilder::jsop_in()
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::jsop_in_dense()
|
||||
IonBuilder::jsop_in_dense(JSValueType unboxedType)
|
||||
{
|
||||
MDefinition* obj = current->pop();
|
||||
MDefinition* id = current->pop();
|
||||
@ -12427,11 +12398,10 @@ IonBuilder::jsop_in_dense()
|
||||
id = idInt32;
|
||||
|
||||
// Get the elements vector.
|
||||
MElements* elements = MElements::New(alloc(), obj);
|
||||
MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC);
|
||||
current->add(elements);
|
||||
|
||||
MInitializedLength* initLength = MInitializedLength::New(alloc(), elements);
|
||||
current->add(initLength);
|
||||
MInstruction* initLength = initializedLength(obj, elements, unboxedType);
|
||||
|
||||
// If there are no holes, speculate the InArray check will not fail.
|
||||
if (!needsHoleCheck && !failedBoundsCheck_) {
|
||||
@ -12440,7 +12410,8 @@ IonBuilder::jsop_in_dense()
|
||||
}
|
||||
|
||||
// Check if id < initLength and elem[id] not a hole.
|
||||
MInArray* ins = MInArray::New(alloc(), elements, id, initLength, obj, needsHoleCheck);
|
||||
MInArray* ins = MInArray::New(alloc(), elements, id, initLength, obj, needsHoleCheck,
|
||||
unboxedType);
|
||||
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
@ -13053,6 +13024,37 @@ IonBuilder::constantInt(int32_t i)
|
||||
return constant(Int32Value(i));
|
||||
}
|
||||
|
||||
MInstruction*
|
||||
IonBuilder::initializedLength(MDefinition* obj, MDefinition* elements, JSValueType unboxedType)
|
||||
{
|
||||
MInstruction* res;
|
||||
if (unboxedType != JSVAL_TYPE_MAGIC)
|
||||
res = MUnboxedArrayInitializedLength::New(alloc(), obj);
|
||||
else
|
||||
res = MInitializedLength::New(alloc(), elements);
|
||||
current->add(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
MInstruction*
|
||||
IonBuilder::setInitializedLength(MDefinition* obj, JSValueType unboxedType, size_t count)
|
||||
{
|
||||
MOZ_ASSERT(count);
|
||||
|
||||
MInstruction* res;
|
||||
if (unboxedType != JSVAL_TYPE_MAGIC) {
|
||||
res = MSetUnboxedArrayInitializedLength::New(alloc(), obj, constant(Int32Value(count)));
|
||||
} else {
|
||||
// MSetInitializedLength takes the index of the last element, rather
|
||||
// than the count itself.
|
||||
MInstruction* elements = MElements::New(alloc(), obj, /* unboxed = */ false);
|
||||
current->add(elements);
|
||||
res = MSetInitializedLength::New(alloc(), elements, constant(Int32Value(count - 1)));
|
||||
}
|
||||
current->add(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
MDefinition*
|
||||
IonBuilder::getCallee()
|
||||
{
|
||||
|
@ -46,16 +46,6 @@ class IonBuilder
|
||||
ControlStatus_None // No control flow.
|
||||
};
|
||||
|
||||
enum SetElemSafety {
|
||||
// Normal write like a[b] = c.
|
||||
SetElem_Normal,
|
||||
|
||||
// Write due to UnsafePutElements:
|
||||
// - assumed to be in bounds,
|
||||
// - not checked for data races
|
||||
SetElem_Unsafe,
|
||||
};
|
||||
|
||||
struct DeferredEdge : public TempObject
|
||||
{
|
||||
MBasicBlock* block;
|
||||
@ -352,6 +342,9 @@ class IonBuilder
|
||||
|
||||
MConstant* constant(const Value& v);
|
||||
MConstant* constantInt(int32_t i);
|
||||
MInstruction* initializedLength(MDefinition* obj, MDefinition* elements,
|
||||
JSValueType unboxedType);
|
||||
MInstruction* setInitializedLength(MDefinition* obj, JSValueType unboxedType, size_t count);
|
||||
|
||||
// Improve the type information at tests
|
||||
bool improveTypesAtTest(MDefinition* ins, bool trueBranch, MTest* test);
|
||||
@ -548,7 +541,7 @@ class IonBuilder
|
||||
bool setElemTryTypedStatic(bool* emitted, MDefinition* object,
|
||||
MDefinition* index, MDefinition* value);
|
||||
bool setElemTryDense(bool* emitted, MDefinition* object,
|
||||
MDefinition* index, MDefinition* value);
|
||||
MDefinition* index, MDefinition* value, bool writeHole);
|
||||
bool setElemTryArguments(bool* emitted, MDefinition* object,
|
||||
MDefinition* index, MDefinition* value);
|
||||
bool setElemTryCache(bool* emitted, MDefinition* object,
|
||||
@ -566,6 +559,9 @@ class IonBuilder
|
||||
MDefinition* value,
|
||||
TypedObjectPrediction elemTypeReprs,
|
||||
int32_t elemSize);
|
||||
bool initializeArrayElement(MDefinition* obj, size_t index, MDefinition* value,
|
||||
JSValueType unboxedType,
|
||||
bool addResumePointAndIncrementInitializedLength);
|
||||
|
||||
// jsop_getelem() helpers.
|
||||
bool getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index);
|
||||
@ -661,15 +657,10 @@ class IonBuilder
|
||||
bool jsop_getelem_typed(MDefinition* obj, MDefinition* index, ScalarTypeDescr::Type arrayType);
|
||||
bool jsop_setelem();
|
||||
bool jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
|
||||
SetElemSafety safety,
|
||||
MDefinition* object, MDefinition* index, MDefinition* value,
|
||||
JSValueType unboxedType);
|
||||
JSValueType unboxedType, bool writeHole);
|
||||
bool jsop_setelem_typed(ScalarTypeDescr::Type arrayType,
|
||||
SetElemSafety safety,
|
||||
MDefinition* object, MDefinition* index, MDefinition* value);
|
||||
bool jsop_setelem_typed_object(ScalarTypeDescr::Type arrayType,
|
||||
SetElemSafety safety,
|
||||
MDefinition* object, MDefinition* index, MDefinition* value);
|
||||
bool jsop_length();
|
||||
bool jsop_length_fastPath();
|
||||
bool jsop_arguments();
|
||||
@ -702,7 +693,7 @@ class IonBuilder
|
||||
bool jsop_isnoiter();
|
||||
bool jsop_iterend();
|
||||
bool jsop_in();
|
||||
bool jsop_in_dense();
|
||||
bool jsop_in_dense(JSValueType unboxedType);
|
||||
bool jsop_instanceof();
|
||||
bool jsop_getaliasedvar(ScopeCoordinate sc);
|
||||
bool jsop_setaliasedvar(ScopeCoordinate sc);
|
||||
@ -781,8 +772,9 @@ class IonBuilder
|
||||
InliningStatus inlineRegExpExec(CallInfo& callInfo);
|
||||
InliningStatus inlineRegExpTest(CallInfo& callInfo);
|
||||
|
||||
// Object natives.
|
||||
// Object natives and intrinsics.
|
||||
InliningStatus inlineObjectCreate(CallInfo& callInfo);
|
||||
InliningStatus inlineDefineDataProperty(CallInfo& callInfo);
|
||||
|
||||
// Atomics natives.
|
||||
InliningStatus inlineAtomicsCompareExchange(CallInfo& callInfo);
|
||||
@ -791,14 +783,6 @@ class IonBuilder
|
||||
InliningStatus inlineAtomicsFence(CallInfo& callInfo);
|
||||
InliningStatus inlineAtomicsBinop(CallInfo& callInfo, JSFunction* target);
|
||||
|
||||
// Array intrinsics.
|
||||
InliningStatus inlineUnsafePutElements(CallInfo& callInfo);
|
||||
bool inlineUnsafeSetDenseArrayElement(CallInfo& callInfo, uint32_t base);
|
||||
bool inlineUnsafeSetTypedArrayElement(CallInfo& callInfo, uint32_t base,
|
||||
ScalarTypeDescr::Type arrayType);
|
||||
bool inlineUnsafeSetTypedObjectArrayElement(CallInfo& callInfo, uint32_t base,
|
||||
ScalarTypeDescr::Type arrayType);
|
||||
|
||||
// Slot intrinsics.
|
||||
InliningStatus inlineUnsafeSetReservedSlot(CallInfo& callInfo);
|
||||
InliningStatus inlineUnsafeGetReservedSlot(CallInfo& callInfo,
|
||||
@ -815,8 +799,6 @@ class IonBuilder
|
||||
// TypedObject intrinsics and natives.
|
||||
InliningStatus inlineObjectIsTypeDescr(CallInfo& callInfo);
|
||||
InliningStatus inlineSetTypedObjectOffset(CallInfo& callInfo);
|
||||
bool elementAccessIsTypedObjectArrayOfScalarType(MDefinition* obj, MDefinition* id,
|
||||
ScalarTypeDescr::Type* arrayType);
|
||||
InliningStatus inlineConstructTypedObject(CallInfo& callInfo, TypeDescr* target);
|
||||
|
||||
// SIMD intrinsics and natives.
|
||||
|
@ -4198,6 +4198,30 @@ class LIncrementUnboxedArrayInitializedLength : public LInstructionHelper<0, 1,
|
||||
}
|
||||
};
|
||||
|
||||
class LSetUnboxedArrayInitializedLength : public LInstructionHelper<0, 2, 1>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(SetUnboxedArrayInitializedLength)
|
||||
|
||||
explicit LSetUnboxedArrayInitializedLength(const LAllocation& object,
|
||||
const LAllocation& length,
|
||||
const LDefinition& temp) {
|
||||
setOperand(0, object);
|
||||
setOperand(1, length);
|
||||
setTemp(0, temp);
|
||||
}
|
||||
|
||||
const LAllocation* object() {
|
||||
return getOperand(0);
|
||||
}
|
||||
const LAllocation* length() {
|
||||
return getOperand(1);
|
||||
}
|
||||
const LDefinition* temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
};
|
||||
|
||||
// Load the length from an elements header.
|
||||
class LArrayLength : public LInstructionHelper<1, 1, 0>
|
||||
{
|
||||
|
@ -217,6 +217,7 @@
|
||||
_(UnboxedArrayLength) \
|
||||
_(UnboxedArrayInitializedLength) \
|
||||
_(IncrementUnboxedArrayInitializedLength) \
|
||||
_(SetUnboxedArrayInitializedLength) \
|
||||
_(BoundsCheck) \
|
||||
_(BoundsCheckRange) \
|
||||
_(BoundsCheckLower) \
|
||||
|
@ -2518,6 +2518,14 @@ LIRGenerator::visitIncrementUnboxedArrayInitializedLength(MIncrementUnboxedArray
|
||||
add(new(alloc()) LIncrementUnboxedArrayInitializedLength(useRegister(ins->object())), ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitSetUnboxedArrayInitializedLength(MSetUnboxedArrayInitializedLength* ins)
|
||||
{
|
||||
add(new(alloc()) LSetUnboxedArrayInitializedLength(useRegister(ins->object()),
|
||||
useRegisterOrConstant(ins->length()),
|
||||
temp()), ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitNot(MNot* ins)
|
||||
{
|
||||
|
@ -190,6 +190,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
||||
void visitUnboxedArrayLength(MUnboxedArrayLength* ins);
|
||||
void visitUnboxedArrayInitializedLength(MUnboxedArrayInitializedLength* ins);
|
||||
void visitIncrementUnboxedArrayInitializedLength(MIncrementUnboxedArrayInitializedLength* ins);
|
||||
void visitSetUnboxedArrayInitializedLength(MSetUnboxedArrayInitializedLength* ins);
|
||||
void visitNot(MNot* ins);
|
||||
void visitBoundsCheck(MBoundsCheck* ins);
|
||||
void visitBoundsCheckLower(MBoundsCheckLower* ins);
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include "vm/NativeObject-inl.h"
|
||||
#include "vm/StringObject-inl.h"
|
||||
#include "vm/UnboxedObject-inl.h"
|
||||
|
||||
using mozilla::ArrayLength;
|
||||
|
||||
@ -184,10 +185,8 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
|
||||
// Object natives.
|
||||
if (native == obj_create)
|
||||
return inlineObjectCreate(callInfo);
|
||||
|
||||
// Array intrinsics.
|
||||
if (native == intrinsic_UnsafePutElements)
|
||||
return inlineUnsafePutElements(callInfo);
|
||||
if (native == intrinsic_DefineDataProperty)
|
||||
return inlineDefineDataProperty(callInfo);
|
||||
|
||||
// Slot intrinsics.
|
||||
if (native == intrinsic_UnsafeSetReservedSlot)
|
||||
@ -524,21 +523,18 @@ IonBuilder::InliningStatus
|
||||
IonBuilder::inlineArray(CallInfo& callInfo)
|
||||
{
|
||||
uint32_t initLength = 0;
|
||||
AllocatingBehaviour allocating = NewArray_Unallocating;
|
||||
|
||||
JSObject* templateObject = inspector->getTemplateObjectForNative(pc, ArrayConstructor);
|
||||
if (!templateObject) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeNoTemplateObj);
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
ArrayObject* templateArray = &templateObject->as<ArrayObject>();
|
||||
|
||||
// Multiple arguments imply array initialization, not just construction.
|
||||
if (callInfo.argc() >= 2) {
|
||||
initLength = callInfo.argc();
|
||||
allocating = NewArray_FullyAllocating;
|
||||
|
||||
TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(templateArray);
|
||||
TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(templateObject);
|
||||
if (!key->unknownProperties()) {
|
||||
HeapTypeSetKey elemTypes = key->property(JSID_VOID);
|
||||
|
||||
@ -561,8 +557,8 @@ IonBuilder::inlineArray(CallInfo& callInfo)
|
||||
if (!arg->isConstantValue()) {
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
MNewArrayDynamicLength* ins =
|
||||
MNewArrayDynamicLength::New(alloc(), constraints(), templateArray,
|
||||
templateArray->group()->initialHeap(constraints()),
|
||||
MNewArrayDynamicLength::New(alloc(), constraints(), templateObject,
|
||||
templateObject->group()->initialHeap(constraints()),
|
||||
arg);
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
@ -580,63 +576,34 @@ IonBuilder::inlineArray(CallInfo& callInfo)
|
||||
// Make sure initLength matches the template object's length. This is
|
||||
// not guaranteed to be the case, for instance if we're inlining the
|
||||
// MConstant may come from an outer script.
|
||||
if (initLength != templateArray->as<ArrayObject>().length())
|
||||
if (initLength != GetAnyBoxedOrUnboxedArrayLength(templateObject))
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
// Don't inline large allocations.
|
||||
if (initLength > ArrayObject::EagerAllocationMaxLength)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
allocating = NewArray_FullyAllocating;
|
||||
}
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
|
||||
MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateArray);
|
||||
MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
|
||||
current->add(templateConst);
|
||||
|
||||
MNewArray* ins = MNewArray::New(alloc(), constraints(), initLength, templateConst,
|
||||
templateArray->group()->initialHeap(constraints()),
|
||||
allocating, pc);
|
||||
templateObject->group()->initialHeap(constraints()), pc);
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
|
||||
if (callInfo.argc() >= 2) {
|
||||
// Get the elements vector.
|
||||
MElements* elements = MElements::New(alloc(), ins);
|
||||
current->add(elements);
|
||||
|
||||
// Store all values, no need to initialize the length after each as
|
||||
// jsop_initelem_array is doing because we do not expect to bailout
|
||||
// because the memory is supposed to be allocated by now.
|
||||
MConstant* id = nullptr;
|
||||
JSValueType unboxedType = GetBoxedOrUnboxedType(templateObject);
|
||||
for (uint32_t i = 0; i < initLength; i++) {
|
||||
id = MConstant::New(alloc(), Int32Value(i));
|
||||
current->add(id);
|
||||
|
||||
MDefinition* value = callInfo.getArg(i);
|
||||
if (ins->convertDoubleElements()) {
|
||||
MInstruction* valueDouble = MToDouble::New(alloc(), value);
|
||||
current->add(valueDouble);
|
||||
value = valueDouble;
|
||||
}
|
||||
|
||||
// There is normally no need for a post barrier on these writes
|
||||
// because the new array will be in the nursery. However, this
|
||||
// assumption is volated if we specifically requested pre-tenuring.
|
||||
if (ins->initialHeap() == gc::TenuredHeap)
|
||||
current->add(MPostWriteBarrier::New(alloc(), ins, value));
|
||||
|
||||
MStoreElement* store = MStoreElement::New(alloc(), elements, id, value,
|
||||
/* needsHoleCheck = */ false);
|
||||
current->add(store);
|
||||
if (!initializeArrayElement(ins, i, value, unboxedType, /* addResumePoint = */ false))
|
||||
return InliningStatus_Error;
|
||||
}
|
||||
|
||||
// Update the length.
|
||||
MSetInitializedLength* length = MSetInitializedLength::New(alloc(), elements, id);
|
||||
current->add(length);
|
||||
|
||||
if (!resumeAfter(length))
|
||||
MInstruction* setLength = setInitializedLength(ins, unboxedType, initLength);
|
||||
if (!resumeAfter(setLength))
|
||||
return InliningStatus_Error;
|
||||
}
|
||||
|
||||
@ -1671,14 +1638,13 @@ IonBuilder::inlineConstantStringSplit(CallInfo& callInfo)
|
||||
// Check if exist a template object in stub.
|
||||
JSString* stringThis = nullptr;
|
||||
JSString* stringArg = nullptr;
|
||||
NativeObject* templateObject = nullptr;
|
||||
JSObject* templateObject = nullptr;
|
||||
if (!inspector->isOptimizableCallStringSplit(pc, &stringThis, &stringArg, &templateObject))
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
MOZ_ASSERT(stringThis);
|
||||
MOZ_ASSERT(stringArg);
|
||||
MOZ_ASSERT(templateObject);
|
||||
MOZ_ASSERT(templateObject->is<ArrayObject>());
|
||||
|
||||
if (strval->toString() != stringThis)
|
||||
return InliningStatus_NotInlined;
|
||||
@ -1698,13 +1664,13 @@ IonBuilder::inlineConstantStringSplit(CallInfo& callInfo)
|
||||
if (!key.maybeTypes()->hasType(TypeSet::StringType()))
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
uint32_t initLength = templateObject->as<ArrayObject>().length();
|
||||
if (templateObject->getDenseInitializedLength() != initLength)
|
||||
uint32_t initLength = GetAnyBoxedOrUnboxedArrayLength(templateObject);
|
||||
if (GetAnyBoxedOrUnboxedInitializedLength(templateObject) != initLength)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
Vector<MConstant*, 0, SystemAllocPolicy> arrayValues;
|
||||
for (uint32_t i = 0; i < initLength; i++) {
|
||||
Value str = templateObject->getDenseElement(i);
|
||||
Value str = GetAnyBoxedOrUnboxedDenseElement(templateObject, i);
|
||||
MOZ_ASSERT(str.toString()->isAtom());
|
||||
MConstant* value = MConstant::New(alloc(), str, constraints());
|
||||
if (!TypeSetIncludes(key.maybeTypes(), value->type(), value->resultTypeSet()))
|
||||
@ -1724,8 +1690,7 @@ IonBuilder::inlineConstantStringSplit(CallInfo& callInfo)
|
||||
current->add(templateConst);
|
||||
|
||||
MNewArray* ins = MNewArray::New(alloc(), constraints(), initLength, templateConst,
|
||||
templateObject->group()->initialHeap(constraints()),
|
||||
NewArray_FullyAllocating, pc);
|
||||
templateObject->group()->initialHeap(constraints()), pc);
|
||||
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
@ -1736,37 +1701,21 @@ IonBuilder::inlineConstantStringSplit(CallInfo& callInfo)
|
||||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
// Get the elements vector.
|
||||
MElements* elements = MElements::New(alloc(), ins);
|
||||
current->add(elements);
|
||||
JSValueType unboxedType = GetBoxedOrUnboxedType(templateObject);
|
||||
|
||||
// Store all values, no need to initialize the length after each as
|
||||
// jsop_initelem_array is doing because we do not expect to bailout
|
||||
// because the memory is supposed to be allocated by now.
|
||||
MConstant* id = nullptr;
|
||||
for (uint32_t i = 0; i < initLength; i++) {
|
||||
id = MConstant::New(alloc(), Int32Value(i));
|
||||
current->add(id);
|
||||
|
||||
MConstant* value = arrayValues[i];
|
||||
current->add(value);
|
||||
|
||||
MStoreElement* store = MStoreElement::New(alloc(), elements, id, value,
|
||||
/* needsHoleCheck = */ false);
|
||||
current->add(store);
|
||||
|
||||
// There is normally no need for a post barrier on these writes
|
||||
// because the new array will be in the nursery. However, this
|
||||
// assumption is volated if we specifically requested pre-tenuring.
|
||||
if (ins->initialHeap() == gc::TenuredHeap)
|
||||
current->add(MPostWriteBarrier::New(alloc(), ins, value));
|
||||
if (!initializeArrayElement(ins, i, value, unboxedType, /* addResumePoint = */ false))
|
||||
return InliningStatus_Error;
|
||||
}
|
||||
|
||||
// Update the length.
|
||||
MSetInitializedLength* length = MSetInitializedLength::New(alloc(), elements, id);
|
||||
current->add(length);
|
||||
|
||||
if (!resumeAfter(length))
|
||||
MInstruction* setLength = setInitializedLength(ins, unboxedType, initLength);
|
||||
if (!resumeAfter(setLength))
|
||||
return InliningStatus_Error;
|
||||
|
||||
return InliningStatus_Inlined;
|
||||
@ -1792,7 +1741,6 @@ IonBuilder::inlineStringSplit(CallInfo& callInfo)
|
||||
JSObject* templateObject = inspector->getTemplateObjectForNative(pc, js::str_split);
|
||||
if (!templateObject)
|
||||
return InliningStatus_NotInlined;
|
||||
MOZ_ASSERT(templateObject->is<ArrayObject>());
|
||||
|
||||
TypeSet::ObjectKey* retKey = TypeSet::ObjectKey::get(templateObject);
|
||||
if (retKey->unknownProperties())
|
||||
@ -2147,174 +2095,36 @@ IonBuilder::inlineObjectCreate(CallInfo& callInfo)
|
||||
}
|
||||
|
||||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineUnsafePutElements(CallInfo& callInfo)
|
||||
IonBuilder::inlineDefineDataProperty(CallInfo& callInfo)
|
||||
{
|
||||
uint32_t argc = callInfo.argc();
|
||||
if (argc < 3 || (argc % 3) != 0 || callInfo.constructing()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||
MOZ_ASSERT(!callInfo.constructing());
|
||||
|
||||
// Only handle definitions of plain data properties.
|
||||
if (callInfo.argc() != 3)
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
/* Important:
|
||||
*
|
||||
* Here we inline each of the stores resulting from a call to
|
||||
* UnsafePutElements(). It is essential that these stores occur
|
||||
* atomically and cannot be interrupted by a stack or recursion
|
||||
* check. If this is not true, race conditions can occur.
|
||||
*/
|
||||
MDefinition* obj = callInfo.getArg(0);
|
||||
MDefinition* id = callInfo.getArg(1);
|
||||
MDefinition* value = callInfo.getArg(2);
|
||||
|
||||
for (uint32_t base = 0; base < argc; base += 3) {
|
||||
uint32_t arri = base + 0;
|
||||
uint32_t idxi = base + 1;
|
||||
uint32_t elemi = base + 2;
|
||||
if (ElementAccessHasExtraIndexedProperty(constraints(), obj))
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
MDefinition* obj = callInfo.getArg(arri);
|
||||
MDefinition* id = callInfo.getArg(idxi);
|
||||
MDefinition* elem = callInfo.getArg(elemi);
|
||||
// setElemTryDense will push the value as the result of the define instead
|
||||
// of |undefined|, but this is fine if the rval is ignored (as it should be
|
||||
// in self hosted code.)
|
||||
MOZ_ASSERT(*GetNextPc(pc) == JSOP_POP);
|
||||
|
||||
bool isDenseNative = ElementAccessIsDenseNative(constraints(), obj, id);
|
||||
|
||||
bool writeNeedsBarrier = false;
|
||||
if (isDenseNative) {
|
||||
writeNeedsBarrier = PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
|
||||
&obj, nullptr, &elem,
|
||||
/* canModify = */ false);
|
||||
}
|
||||
|
||||
// We can only inline setelem on dense arrays that do not need type
|
||||
// barriers and on typed arrays and on typed object arrays.
|
||||
Scalar::Type arrayType;
|
||||
if ((!isDenseNative || writeNeedsBarrier) &&
|
||||
!ElementAccessIsAnyTypedArray(constraints(), obj, id, &arrayType) &&
|
||||
!elementAccessIsTypedObjectArrayOfScalarType(obj, id, &arrayType))
|
||||
{
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
}
|
||||
bool emitted = false;
|
||||
if (!setElemTryDense(&emitted, obj, id, value, /* writeHole = */ true))
|
||||
return InliningStatus_Error;
|
||||
if (!emitted)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
|
||||
// Push the result first so that the stack depth matches up for
|
||||
// the potential bailouts that will occur in the stores below.
|
||||
MConstant* udef = MConstant::New(alloc(), UndefinedValue());
|
||||
current->add(udef);
|
||||
current->push(udef);
|
||||
|
||||
for (uint32_t base = 0; base < argc; base += 3) {
|
||||
uint32_t arri = base + 0;
|
||||
uint32_t idxi = base + 1;
|
||||
|
||||
MDefinition* obj = callInfo.getArg(arri);
|
||||
MDefinition* id = callInfo.getArg(idxi);
|
||||
|
||||
if (ElementAccessIsDenseNative(constraints(), obj, id)) {
|
||||
if (!inlineUnsafeSetDenseArrayElement(callInfo, base))
|
||||
return InliningStatus_Error;
|
||||
continue;
|
||||
}
|
||||
|
||||
Scalar::Type arrayType;
|
||||
if (ElementAccessIsAnyTypedArray(constraints(), obj, id, &arrayType)) {
|
||||
if (!inlineUnsafeSetTypedArrayElement(callInfo, base, arrayType))
|
||||
return InliningStatus_Error;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (elementAccessIsTypedObjectArrayOfScalarType(obj, id, &arrayType)) {
|
||||
if (!inlineUnsafeSetTypedObjectArrayElement(callInfo, base, arrayType))
|
||||
return InliningStatus_Error;
|
||||
continue;
|
||||
}
|
||||
|
||||
MOZ_CRASH("Element access not dense array nor typed array");
|
||||
}
|
||||
|
||||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::elementAccessIsTypedObjectArrayOfScalarType(MDefinition* obj, MDefinition* id,
|
||||
Scalar::Type* arrayType)
|
||||
{
|
||||
if (obj->type() != MIRType_Object) // lookupTypeDescrSet() tests for TypedObject
|
||||
return false;
|
||||
|
||||
if (id->type() != MIRType_Int32 && id->type() != MIRType_Double)
|
||||
return false;
|
||||
|
||||
TypedObjectPrediction prediction = typedObjectPrediction(obj);
|
||||
if (prediction.isUseless() || !prediction.ofArrayKind())
|
||||
return false;
|
||||
|
||||
TypedObjectPrediction elemPrediction = prediction.arrayElementType();
|
||||
if (elemPrediction.isUseless() || elemPrediction.kind() != type::Scalar)
|
||||
return false;
|
||||
|
||||
*arrayType = elemPrediction.scalarType();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::inlineUnsafeSetDenseArrayElement(CallInfo& callInfo, uint32_t base)
|
||||
{
|
||||
// Note: we do not check the conditions that are asserted as true
|
||||
// in intrinsic_UnsafePutElements():
|
||||
// - arr is a dense array
|
||||
// - idx < initialized length
|
||||
// Furthermore, note that inlineUnsafePutElements ensures the type of the
|
||||
// value is reflected in the JSID_VOID property of the array.
|
||||
|
||||
MDefinition* obj = callInfo.getArg(base + 0);
|
||||
MDefinition* id = callInfo.getArg(base + 1);
|
||||
MDefinition* elem = callInfo.getArg(base + 2);
|
||||
|
||||
TemporaryTypeSet::DoubleConversion conversion =
|
||||
obj->resultTypeSet()->convertDoubleElements(constraints());
|
||||
if (!jsop_setelem_dense(conversion, SetElem_Unsafe, obj, id, elem, JSVAL_TYPE_MAGIC))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::inlineUnsafeSetTypedArrayElement(CallInfo& callInfo,
|
||||
uint32_t base,
|
||||
Scalar::Type arrayType)
|
||||
{
|
||||
// Note: we do not check the conditions that are asserted as true
|
||||
// in intrinsic_UnsafePutElements():
|
||||
// - arr is a typed array
|
||||
// - idx < length
|
||||
|
||||
MDefinition* obj = callInfo.getArg(base + 0);
|
||||
MDefinition* id = callInfo.getArg(base + 1);
|
||||
MDefinition* elem = callInfo.getArg(base + 2);
|
||||
|
||||
if (!jsop_setelem_typed(arrayType, SetElem_Unsafe, obj, id, elem))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::inlineUnsafeSetTypedObjectArrayElement(CallInfo& callInfo,
|
||||
uint32_t base,
|
||||
Scalar::Type arrayType)
|
||||
{
|
||||
// Note: we do not check the conditions that are asserted as true
|
||||
// in intrinsic_UnsafePutElements():
|
||||
// - arr is a typed array
|
||||
// - idx < length
|
||||
|
||||
MDefinition* obj = callInfo.getArg(base + 0);
|
||||
MDefinition* id = callInfo.getArg(base + 1);
|
||||
MDefinition* elem = callInfo.getArg(base + 2);
|
||||
|
||||
if (!jsop_setelem_typed_object(arrayType, SetElem_Unsafe, obj, id, elem))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
IonBuilder::InliningStatus
|
||||
IonBuilder::inlineHasClass(CallInfo& callInfo,
|
||||
const Class* clasp1, const Class* clasp2,
|
||||
|
@ -4024,11 +4024,10 @@ MArrayState::Copy(TempAllocator& alloc, MArrayState* state)
|
||||
}
|
||||
|
||||
MNewArray::MNewArray(CompilerConstraintList* constraints, uint32_t count, MConstant* templateConst,
|
||||
gc::InitialHeap initialHeap, AllocatingBehaviour allocating, jsbytecode* pc)
|
||||
gc::InitialHeap initialHeap, jsbytecode* pc)
|
||||
: MUnaryInstruction(templateConst),
|
||||
count_(count),
|
||||
initialHeap_(initialHeap),
|
||||
allocating_(allocating),
|
||||
convertDoubleElements_(false),
|
||||
pc_(pc)
|
||||
{
|
||||
@ -4047,11 +4046,6 @@ MNewArray::shouldUseVM() const
|
||||
if (!templateObject())
|
||||
return true;
|
||||
|
||||
// Allocate space using the VMCall when mir hints it needs to get allocated
|
||||
// immediately, but only when data doesn't fit the available array slots.
|
||||
if (allocatingBehaviour() == NewArray_Unallocating)
|
||||
return false;
|
||||
|
||||
if (templateObject()->is<UnboxedArrayObject>()) {
|
||||
MOZ_ASSERT(templateObject()->as<UnboxedArrayObject>().capacity() >= count());
|
||||
return !templateObject()->as<UnboxedArrayObject>().hasInlineElements();
|
||||
|
@ -2915,8 +2915,6 @@ class MNewArray
|
||||
|
||||
// Heap where the array should be allocated.
|
||||
gc::InitialHeap initialHeap_;
|
||||
// Allocate space at initialization or not
|
||||
AllocatingBehaviour allocating_;
|
||||
|
||||
// Whether values written to this array should be converted to double first.
|
||||
bool convertDoubleElements_;
|
||||
@ -2924,18 +2922,16 @@ class MNewArray
|
||||
jsbytecode* pc_;
|
||||
|
||||
MNewArray(CompilerConstraintList* constraints, uint32_t count, MConstant* templateConst,
|
||||
gc::InitialHeap initialHeap, AllocatingBehaviour allocating, jsbytecode* pc);
|
||||
gc::InitialHeap initialHeap, jsbytecode* pc);
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(NewArray)
|
||||
|
||||
static MNewArray* New(TempAllocator& alloc, CompilerConstraintList* constraints,
|
||||
uint32_t count, MConstant* templateConst,
|
||||
gc::InitialHeap initialHeap, AllocatingBehaviour allocating,
|
||||
jsbytecode* pc)
|
||||
gc::InitialHeap initialHeap, jsbytecode* pc)
|
||||
{
|
||||
return new(alloc) MNewArray(constraints, count, templateConst,
|
||||
initialHeap, allocating, pc);
|
||||
return new(alloc) MNewArray(constraints, count, templateConst, initialHeap, pc);
|
||||
}
|
||||
|
||||
uint32_t count() const {
|
||||
@ -2950,10 +2946,6 @@ class MNewArray
|
||||
return initialHeap_;
|
||||
}
|
||||
|
||||
AllocatingBehaviour allocatingBehaviour() const {
|
||||
return allocating_;
|
||||
}
|
||||
|
||||
jsbytecode* pc() const {
|
||||
return pc_;
|
||||
}
|
||||
@ -3027,10 +3019,10 @@ class MNewArrayDynamicLength
|
||||
: public MUnaryInstruction,
|
||||
public IntPolicy<0>::Data
|
||||
{
|
||||
AlwaysTenured<ArrayObject*> templateObject_;
|
||||
AlwaysTenuredObject templateObject_;
|
||||
gc::InitialHeap initialHeap_;
|
||||
|
||||
MNewArrayDynamicLength(CompilerConstraintList* constraints, ArrayObject* templateObject,
|
||||
MNewArrayDynamicLength(CompilerConstraintList* constraints, JSObject* templateObject,
|
||||
gc::InitialHeap initialHeap, MDefinition* length)
|
||||
: MUnaryInstruction(length),
|
||||
templateObject_(templateObject),
|
||||
@ -3046,7 +3038,7 @@ class MNewArrayDynamicLength
|
||||
INSTRUCTION_HEADER(NewArrayDynamicLength)
|
||||
|
||||
static MNewArrayDynamicLength* New(TempAllocator& alloc, CompilerConstraintList* constraints,
|
||||
ArrayObject* templateObject, gc::InitialHeap initialHeap,
|
||||
JSObject* templateObject, gc::InitialHeap initialHeap,
|
||||
MDefinition* length)
|
||||
{
|
||||
return new(alloc) MNewArrayDynamicLength(constraints, templateObject, initialHeap, length);
|
||||
@ -3055,7 +3047,7 @@ class MNewArrayDynamicLength
|
||||
MDefinition* length() const {
|
||||
return getOperand(0);
|
||||
}
|
||||
ArrayObject* templateObject() const {
|
||||
JSObject* templateObject() const {
|
||||
return templateObject_;
|
||||
}
|
||||
gc::InitialHeap initialHeap() const {
|
||||
@ -7995,6 +7987,36 @@ class MIncrementUnboxedArrayInitializedLength
|
||||
ALLOW_CLONE(MIncrementUnboxedArrayInitializedLength)
|
||||
};
|
||||
|
||||
// Set the initialized length of an unboxed array object.
|
||||
class MSetUnboxedArrayInitializedLength
|
||||
: public MBinaryInstruction,
|
||||
public SingleObjectPolicy::Data
|
||||
{
|
||||
explicit MSetUnboxedArrayInitializedLength(MDefinition* obj, MDefinition* length)
|
||||
: MBinaryInstruction(obj, length)
|
||||
{}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(SetUnboxedArrayInitializedLength)
|
||||
|
||||
static MSetUnboxedArrayInitializedLength* New(TempAllocator& alloc, MDefinition* obj,
|
||||
MDefinition* length) {
|
||||
return new(alloc) MSetUnboxedArrayInitializedLength(obj, length);
|
||||
}
|
||||
|
||||
MDefinition* object() const {
|
||||
return getOperand(0);
|
||||
}
|
||||
MDefinition* length() const {
|
||||
return getOperand(1);
|
||||
}
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::Store(AliasSet::ObjectFields);
|
||||
}
|
||||
|
||||
ALLOW_CLONE(MSetUnboxedArrayInitializedLength)
|
||||
};
|
||||
|
||||
// Load the array length from an elements header.
|
||||
class MArrayLength
|
||||
: public MUnaryInstruction,
|
||||
@ -11891,13 +11913,15 @@ class MInArray
|
||||
{
|
||||
bool needsHoleCheck_;
|
||||
bool needsNegativeIntCheck_;
|
||||
JSValueType unboxedType_;
|
||||
|
||||
MInArray(MDefinition* elements, MDefinition* index,
|
||||
MDefinition* initLength, MDefinition* object,
|
||||
bool needsHoleCheck)
|
||||
bool needsHoleCheck, JSValueType unboxedType)
|
||||
: MQuaternaryInstruction(elements, index, initLength, object),
|
||||
needsHoleCheck_(needsHoleCheck),
|
||||
needsNegativeIntCheck_(true)
|
||||
needsNegativeIntCheck_(true),
|
||||
unboxedType_(unboxedType)
|
||||
{
|
||||
setResultType(MIRType_Boolean);
|
||||
setMovable();
|
||||
@ -11911,9 +11935,10 @@ class MInArray
|
||||
|
||||
static MInArray* New(TempAllocator& alloc, MDefinition* elements, MDefinition* index,
|
||||
MDefinition* initLength, MDefinition* object,
|
||||
bool needsHoleCheck)
|
||||
bool needsHoleCheck, JSValueType unboxedType)
|
||||
{
|
||||
return new(alloc) MInArray(elements, index, initLength, object, needsHoleCheck);
|
||||
return new(alloc) MInArray(elements, index, initLength, object, needsHoleCheck,
|
||||
unboxedType);
|
||||
}
|
||||
|
||||
MDefinition* elements() const {
|
||||
@ -11934,6 +11959,9 @@ class MInArray
|
||||
bool needsNegativeIntCheck() const {
|
||||
return needsNegativeIntCheck_;
|
||||
}
|
||||
JSValueType unboxedType() const {
|
||||
return unboxedType_;
|
||||
}
|
||||
void collectRangeInfoPreTrunc() override;
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::Load(AliasSet::Element);
|
||||
@ -11946,6 +11974,8 @@ class MInArray
|
||||
return false;
|
||||
if (needsNegativeIntCheck() != other->needsNegativeIntCheck())
|
||||
return false;
|
||||
if (unboxedType() != other->unboxedType())
|
||||
return false;
|
||||
return congruentIfOperandsEqual(other);
|
||||
}
|
||||
};
|
||||
|
@ -182,6 +182,7 @@ namespace jit {
|
||||
_(UnboxedArrayLength) \
|
||||
_(UnboxedArrayInitializedLength) \
|
||||
_(IncrementUnboxedArrayInitializedLength) \
|
||||
_(SetUnboxedArrayInitializedLength) \
|
||||
_(Not) \
|
||||
_(BoundsCheck) \
|
||||
_(BoundsCheckLower) \
|
||||
|
@ -1197,14 +1197,12 @@ MNewArray::writeRecoverData(CompactBufferWriter& writer) const
|
||||
MOZ_ASSERT(canRecoverOnBailout());
|
||||
writer.writeUnsigned(uint32_t(RInstruction::Recover_NewArray));
|
||||
writer.writeUnsigned(count());
|
||||
writer.writeByte(uint8_t(allocatingBehaviour()));
|
||||
return true;
|
||||
}
|
||||
|
||||
RNewArray::RNewArray(CompactBufferReader& reader)
|
||||
{
|
||||
count_ = reader.readUnsigned();
|
||||
allocatingBehaviour_ = AllocatingBehaviour(reader.readByte());
|
||||
}
|
||||
|
||||
bool
|
||||
@ -1212,9 +1210,9 @@ RNewArray::recover(JSContext* cx, SnapshotIterator& iter) const
|
||||
{
|
||||
RootedObject templateObject(cx, &iter.read().toObject());
|
||||
RootedValue result(cx);
|
||||
RootedObjectGroup group(cx);
|
||||
RootedObjectGroup group(cx, templateObject->group());
|
||||
|
||||
JSObject* resultObject = NewDenseArray(cx, count_, group, allocatingBehaviour_);
|
||||
JSObject* resultObject = NewFullyAllocatedArrayTryUseGroup(cx, group, count_);
|
||||
if (!resultObject)
|
||||
return false;
|
||||
|
||||
|
@ -635,7 +635,6 @@ class RNewArray final : public RInstruction
|
||||
{
|
||||
private:
|
||||
uint32_t count_;
|
||||
AllocatingBehaviour allocatingBehaviour_;
|
||||
|
||||
public:
|
||||
RINSTRUCTION_HEADER_(NewArray)
|
||||
|
@ -605,13 +605,6 @@ IsArrayEscaped(MInstruction* ins)
|
||||
MOZ_ASSERT(ins->isNewArray());
|
||||
uint32_t count = ins->toNewArray()->count();
|
||||
|
||||
// The array is probably too large to be represented efficiently with
|
||||
// MArrayState, and we do not want to make huge allocations during bailouts.
|
||||
if (ins->toNewArray()->allocatingBehaviour() == NewArray_Unallocating) {
|
||||
JitSpewDef(JitSpew_Escape, "Array is not allocated\n", ins);
|
||||
return true;
|
||||
}
|
||||
|
||||
JSObject* obj = ins->toNewArray()->templateObject();
|
||||
if (!obj || obj->is<UnboxedArrayObject>())
|
||||
return true;
|
||||
|
@ -2005,6 +2005,12 @@ MacroAssemblerARMCompat::or32(Imm32 imm, Register dest)
|
||||
ma_orr(imm, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARMCompat::or32(Register src, Register dest)
|
||||
{
|
||||
ma_orr(src, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARMCompat::xorPtr(Imm32 imm, Register dest)
|
||||
{
|
||||
|
@ -1277,6 +1277,7 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
||||
void and32(Imm32 imm, Register dest);
|
||||
void and32(Imm32 imm, const Address& dest);
|
||||
void and32(const Address& src, Register dest);
|
||||
void or32(Register src, Register dest);
|
||||
void or32(Imm32 imm, Register dest);
|
||||
void or32(Imm32 imm, const Address& dest);
|
||||
void xorPtr(Imm32 imm, Register dest);
|
||||
|
@ -1673,6 +1673,12 @@ MacroAssemblerMIPSCompat::or32(Imm32 imm, const Address& dest)
|
||||
store32(SecondScratchReg, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::or32(Register src, Register dest)
|
||||
{
|
||||
ma_or(dest, src);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::xor32(Imm32 imm, Register dest)
|
||||
{
|
||||
|
@ -1207,6 +1207,7 @@ public:
|
||||
void and32(const Address& src, Register dest);
|
||||
void or32(Imm32 imm, Register dest);
|
||||
void or32(Imm32 imm, const Address& dest);
|
||||
void or32(Register src, Register dest);
|
||||
void xor32(Imm32 imm, Register dest);
|
||||
void xorPtr(Imm32 imm, Register dest);
|
||||
void xorPtr(Register src, Register dest);
|
||||
|
@ -243,14 +243,25 @@ GetElement(JSContext* cx, HandleObject obj, IndexType index, bool* hole, Mutable
|
||||
return GetElement(cx, obj, obj, index, hole, vp);
|
||||
}
|
||||
|
||||
void
|
||||
bool
|
||||
ElementAdder::append(JSContext* cx, HandleValue v)
|
||||
{
|
||||
MOZ_ASSERT(index_ < length_);
|
||||
if (resObj_)
|
||||
resObj_->as<NativeObject>().setDenseElementWithType(cx, index_++, v);
|
||||
else
|
||||
vp_[index_++] = v;
|
||||
if (resObj_) {
|
||||
DenseElementResult result = SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, resObj_, index_,
|
||||
v.address(), 1,
|
||||
UpdateTypes);
|
||||
if (result == DenseElementResult::Failure)
|
||||
return false;
|
||||
if (result == DenseElementResult::Incomplete) {
|
||||
if (!DefineElement(cx, resObj_, index_, v))
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
vp_[index_] = v;
|
||||
}
|
||||
index_++;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
@ -258,12 +269,9 @@ ElementAdder::appendHole()
|
||||
{
|
||||
MOZ_ASSERT(getBehavior_ == ElementAdder::CheckHasElemPreserveHoles);
|
||||
MOZ_ASSERT(index_ < length_);
|
||||
if (resObj_) {
|
||||
MOZ_ASSERT(resObj_->as<NativeObject>().getDenseElement(index_).isMagic(JS_ELEMENTS_HOLE));
|
||||
index_++;
|
||||
} else {
|
||||
vp_[index_++].setMagic(JS_ELEMENTS_HOLE);
|
||||
}
|
||||
if (!resObj_)
|
||||
vp_[index_].setMagic(JS_ELEMENTS_HOLE);
|
||||
index_++;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -287,7 +295,8 @@ js::GetElementsWithAdder(JSContext* cx, HandleObject obj, HandleObject receiver,
|
||||
if (!GetElement(cx, obj, receiver, i, &val))
|
||||
return false;
|
||||
}
|
||||
adder->append(cx, val);
|
||||
if (!adder->append(cx, val))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -2281,84 +2290,6 @@ js::array_unshift(JSContext* cx, unsigned argc, Value* vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Return a new array with the default prototype and specified allocated
|
||||
// capacity and length. If possible, try to reuse the group of the input
|
||||
// object. The resulting array will have the same boxed/unboxed elements
|
||||
// representation as the input object, and will either reuse the input
|
||||
// object's group or will have unknown property types.
|
||||
JSObject*
|
||||
js::NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length,
|
||||
NewObjectKind newKind, bool forceAnalyze)
|
||||
{
|
||||
if (!obj->is<ArrayObject>() && !obj->is<UnboxedArrayObject>())
|
||||
return NewDenseFullyAllocatedArray(cx, length, nullptr, newKind);
|
||||
|
||||
if (obj->getProto() != cx->global()->maybeGetArrayPrototype())
|
||||
return NewDenseFullyAllocatedArray(cx, length, nullptr, newKind);
|
||||
|
||||
RootedObjectGroup group(cx, obj->getGroup(cx));
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
if (group->maybePreliminaryObjects())
|
||||
group->maybePreliminaryObjects()->maybeAnalyze(cx, group, forceAnalyze);
|
||||
|
||||
if (group->shouldPreTenure() || group->maybePreliminaryObjects())
|
||||
newKind = TenuredObject;
|
||||
|
||||
if (group->maybeUnboxedLayout()) {
|
||||
if (length > UnboxedArrayObject::MaximumCapacity)
|
||||
return NewDenseFullyAllocatedArray(cx, length, nullptr, newKind);
|
||||
|
||||
return UnboxedArrayObject::create(cx, group, length, newKind);
|
||||
}
|
||||
|
||||
ArrayObject* res = NewDenseFullyAllocatedArray(cx, length, nullptr, newKind);
|
||||
if (!res)
|
||||
return nullptr;
|
||||
|
||||
res->setGroup(group);
|
||||
|
||||
if (PreliminaryObjectArray* preliminaryObjects = group->maybePreliminaryObjects())
|
||||
preliminaryObjects->registerNewObject(res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// As above, except this might not allocate space up to |length| and will
|
||||
// definitely return a normal boxed array, instead of an unboxed array. This
|
||||
// should be used when the result might need sparse elements.
|
||||
static inline ArrayObject*
|
||||
NewPartlyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length)
|
||||
{
|
||||
if (!obj->is<ArrayObject>() && !obj->is<UnboxedArrayObject>())
|
||||
return NewDensePartlyAllocatedArray(cx, length);
|
||||
|
||||
if (obj->getProto() != cx->global()->maybeGetArrayPrototype())
|
||||
return NewDensePartlyAllocatedArray(cx, length);
|
||||
|
||||
RootedObjectGroup group(cx, obj->getGroup(cx));
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
if (group->maybePreliminaryObjects())
|
||||
group->maybePreliminaryObjects()->maybeAnalyze(cx, group);
|
||||
|
||||
NewObjectKind newKind = GenericObject;
|
||||
if (group->shouldPreTenure() || group->maybePreliminaryObjects())
|
||||
newKind = TenuredObject;
|
||||
|
||||
if (group->maybeUnboxedLayout())
|
||||
return NewDensePartlyAllocatedArray(cx, length, nullptr, newKind);
|
||||
|
||||
ArrayObject* res = NewDensePartlyAllocatedArray(cx, length, nullptr, newKind);
|
||||
if (!res)
|
||||
return nullptr;
|
||||
|
||||
res->setGroup(group);
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if this is a dense or unboxed array whose |count| properties
|
||||
* starting from |startingIndex| may be accessed (get, set, delete) directly
|
||||
@ -2960,28 +2891,17 @@ js::array_slice(JSContext* cx, unsigned argc, Value* vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedArrayObject narr(cx, NewPartlyAllocatedArrayTryReuseGroup(cx, obj, end - begin));
|
||||
RootedObject narr(cx, NewPartlyAllocatedArrayTryReuseGroup(cx, obj, end - begin));
|
||||
if (!narr)
|
||||
return false;
|
||||
|
||||
if (js::GetElementsOp op = obj->getOps()->getElements) {
|
||||
// Ensure that we have dense elements, so that ElementAdder::append can
|
||||
// use setDenseElementWithType.
|
||||
DenseElementResult result = narr->ensureDenseElements(cx, 0, end - begin);
|
||||
if (result == DenseElementResult::Failure)
|
||||
ElementAdder adder(cx, narr, end - begin, ElementAdder::CheckHasElemPreserveHoles);
|
||||
if (!op(cx, obj, begin, end, &adder))
|
||||
return false;
|
||||
|
||||
if (result == DenseElementResult::Success) {
|
||||
ElementAdder adder(cx, narr, end - begin, ElementAdder::CheckHasElemPreserveHoles);
|
||||
if (!op(cx, obj, begin, end, &adder))
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*narr);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fallthrough
|
||||
MOZ_ASSERT(result == DenseElementResult::Incomplete);
|
||||
args.rval().setObject(*narr);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj->isNative() && obj->isIndexed() && end - begin > 1000) {
|
||||
@ -3076,13 +2996,9 @@ array_filter(JSContext* cx, unsigned argc, Value* vp)
|
||||
RootedValue thisv(cx, args.length() >= 2 ? args[1] : UndefinedValue());
|
||||
|
||||
/* Step 6. */
|
||||
RootedObject arr(cx, NewDenseFullyAllocatedArray(cx, 0));
|
||||
RootedObject arr(cx, NewFullyAllocatedArrayForCallingAllocationSite(cx, 0));
|
||||
if (!arr)
|
||||
return false;
|
||||
ObjectGroup* newGroup = ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array);
|
||||
if (!newGroup)
|
||||
return false;
|
||||
arr->setGroup(newGroup);
|
||||
|
||||
/* Step 7. */
|
||||
uint32_t k = 0;
|
||||
@ -3157,19 +3073,11 @@ IsArrayConstructor(const Value& v)
|
||||
}
|
||||
|
||||
static bool
|
||||
ArrayFromCallArgs(JSContext* cx, HandleObjectGroup group, CallArgs& args)
|
||||
ArrayFromCallArgs(JSContext* cx, CallArgs& args)
|
||||
{
|
||||
JSObject* obj = NewDenseFullyAllocatedArray(cx, args.length());
|
||||
JSObject* obj = NewCopiedArrayForCallingAllocationSite(cx, args.array(), args.length());
|
||||
if (!obj)
|
||||
return false;
|
||||
obj->setGroup(group);
|
||||
|
||||
DenseElementResult result =
|
||||
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, 0, args.array(), args.length(),
|
||||
UpdateTypes);
|
||||
if (result == DenseElementResult::Failure)
|
||||
return false;
|
||||
MOZ_ASSERT(result == DenseElementResult::Success);
|
||||
|
||||
args.rval().setObject(*obj);
|
||||
return true;
|
||||
@ -3183,10 +3091,7 @@ array_of(JSContext* cx, unsigned argc, Value* vp)
|
||||
if (IsArrayConstructor(args.thisv()) || !IsConstructor(args.thisv())) {
|
||||
// IsArrayConstructor(this) will usually be true in practice. This is
|
||||
// the most common path.
|
||||
RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array));
|
||||
if (!group)
|
||||
return false;
|
||||
return ArrayFromCallArgs(cx, group, args);
|
||||
return ArrayFromCallArgs(cx, args);
|
||||
}
|
||||
|
||||
// Step 4.
|
||||
@ -3289,16 +3194,12 @@ bool
|
||||
js::ArrayConstructor(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
|
||||
if (args.isConstructing())
|
||||
MOZ_ASSERT(args.newTarget().toObject().as<JSFunction>().native() == js::ArrayConstructor);
|
||||
|
||||
RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array));
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
if (args.length() != 1 || !args[0].isNumber())
|
||||
return ArrayFromCallArgs(cx, group, args);
|
||||
return ArrayFromCallArgs(cx, args);
|
||||
|
||||
uint32_t length;
|
||||
if (args[0].isInt32()) {
|
||||
@ -3317,14 +3218,7 @@ js::ArrayConstructor(JSContext* cx, unsigned argc, Value* vp)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate up to |EagerAllocationMaxLength| dense elements eagerly, to
|
||||
* avoid reallocating elements when filling the array.
|
||||
*/
|
||||
AllocatingBehaviour allocating = (length <= ArrayObject::EagerAllocationMaxLength)
|
||||
? NewArray_FullyAllocating
|
||||
: NewArray_PartlyAllocating;
|
||||
RootedObject obj(cx, NewDenseArray(cx, length, group, allocating));
|
||||
JSObject* obj = NewPartlyAllocatedArrayForCallingAllocationSite(cx, length);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
@ -3332,7 +3226,7 @@ js::ArrayConstructor(JSContext* cx, unsigned argc, Value* vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
ArrayObject*
|
||||
JSObject*
|
||||
js::ArrayConstructorOneArg(JSContext* cx, HandleObjectGroup group, int32_t lengthInt)
|
||||
{
|
||||
if (lengthInt < 0) {
|
||||
@ -3341,10 +3235,7 @@ js::ArrayConstructorOneArg(JSContext* cx, HandleObjectGroup group, int32_t lengt
|
||||
}
|
||||
|
||||
uint32_t length = uint32_t(lengthInt);
|
||||
AllocatingBehaviour allocating = (length <= ArrayObject::EagerAllocationMaxLength)
|
||||
? NewArray_FullyAllocating
|
||||
: NewArray_PartlyAllocating;
|
||||
return NewDenseArray(cx, length, group, allocating);
|
||||
return NewPartlyAllocatedArrayTryUseGroup(cx, group, length);
|
||||
}
|
||||
|
||||
static JSObject*
|
||||
@ -3532,7 +3423,7 @@ js::NewDenseFullyAllocatedArray(ExclusiveContext* cx, uint32_t length,
|
||||
HandleObject proto /* = nullptr */,
|
||||
NewObjectKind newKind /* = GenericObject */)
|
||||
{
|
||||
return NewArray<NativeObject::NELEMENTS_LIMIT>(cx, length, proto, newKind);
|
||||
return NewArray<UINT32_MAX>(cx, length, proto, newKind);
|
||||
}
|
||||
|
||||
ArrayObject * JS_FASTCALL
|
||||
@ -3551,47 +3442,13 @@ js::NewDenseUnallocatedArray(ExclusiveContext* cx, uint32_t length,
|
||||
return NewArray<0>(cx, length, proto, newKind);
|
||||
}
|
||||
|
||||
ArrayObject*
|
||||
js::NewDenseArray(ExclusiveContext* cx, uint32_t length, HandleObjectGroup group,
|
||||
AllocatingBehaviour allocating, bool convertDoubleElements)
|
||||
{
|
||||
NewObjectKind newKind = !group ? SingletonObject : GenericObject;
|
||||
if (group && group->shouldPreTenure())
|
||||
newKind = TenuredObject;
|
||||
|
||||
ArrayObject* arr;
|
||||
if (allocating == NewArray_Unallocating) {
|
||||
arr = NewDenseUnallocatedArray(cx, length, nullptr, newKind);
|
||||
} else if (allocating == NewArray_PartlyAllocating) {
|
||||
arr = NewDensePartlyAllocatedArray(cx, length, nullptr, newKind);
|
||||
} else {
|
||||
MOZ_ASSERT(allocating == NewArray_FullyAllocating);
|
||||
arr = NewDenseFullyAllocatedArray(cx, length, nullptr, newKind);
|
||||
}
|
||||
if (!arr)
|
||||
return nullptr;
|
||||
|
||||
if (group)
|
||||
arr->setGroup(group);
|
||||
|
||||
if (convertDoubleElements)
|
||||
arr->setShouldConvertDoubleElements();
|
||||
|
||||
// If the length calculation overflowed, make sure that is marked for the
|
||||
// new group.
|
||||
if (arr->length() > INT32_MAX)
|
||||
arr->setLength(cx, arr->length());
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
ArrayObject*
|
||||
js::NewDenseCopiedArray(JSContext* cx, uint32_t length, HandleArrayObject src,
|
||||
uint32_t elementOffset, HandleObject proto /* = nullptr */)
|
||||
{
|
||||
MOZ_ASSERT(!src->isIndexed());
|
||||
|
||||
ArrayObject* arr = NewArray<NativeObject::NELEMENTS_LIMIT>(cx, length, proto);
|
||||
ArrayObject* arr = NewArray<UINT32_MAX>(cx, length, proto);
|
||||
if (!arr)
|
||||
return nullptr;
|
||||
|
||||
@ -3610,7 +3467,7 @@ js::NewDenseCopiedArray(JSContext* cx, uint32_t length, const Value* values,
|
||||
HandleObject proto /* = nullptr */,
|
||||
NewObjectKind newKind /* = GenericObject */)
|
||||
{
|
||||
ArrayObject* arr = NewArray<NativeObject::NELEMENTS_LIMIT>(cx, length, proto);
|
||||
ArrayObject* arr = NewArray<UINT32_MAX>(cx, length, proto);
|
||||
if (!arr)
|
||||
return nullptr;
|
||||
|
||||
@ -3636,7 +3493,7 @@ js::NewDenseFullyAllocatedArrayWithTemplate(JSContext* cx, uint32_t length, JSOb
|
||||
|
||||
gc::InitialHeap heap = GetInitialHeap(GenericObject, &ArrayObject::class_);
|
||||
Rooted<ArrayObject*> arr(cx, ArrayObject::createArray(cx, allocKind,
|
||||
heap, shape, group, length));
|
||||
heap, shape, group, length));
|
||||
if (!arr)
|
||||
return nullptr;
|
||||
|
||||
@ -3661,6 +3518,150 @@ js::NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc
|
||||
return arr;
|
||||
}
|
||||
|
||||
// Return a new boxed or unboxed array with the specified length and allocated
|
||||
// capacity (up to maxLength), using the specified group if possible.
|
||||
template <uint32_t maxLength>
|
||||
static inline JSObject*
|
||||
NewArrayTryUseGroup(JSContext* cx, HandleObjectGroup group, size_t length,
|
||||
NewObjectKind newKind = GenericObject, bool forceAnalyze = false)
|
||||
{
|
||||
MOZ_ASSERT(newKind != SingletonObject);
|
||||
|
||||
if (group->maybePreliminaryObjects())
|
||||
group->maybePreliminaryObjects()->maybeAnalyze(cx, group, forceAnalyze);
|
||||
|
||||
if (group->shouldPreTenure() || group->maybePreliminaryObjects())
|
||||
newKind = TenuredObject;
|
||||
|
||||
if (group->maybeUnboxedLayout()) {
|
||||
if (length > UnboxedArrayObject::MaximumCapacity)
|
||||
return NewArray<maxLength>(cx, length, nullptr, newKind);
|
||||
|
||||
return UnboxedArrayObject::create(cx, group, length, newKind, maxLength);
|
||||
}
|
||||
|
||||
ArrayObject* res = NewArray<maxLength>(cx, length, nullptr, newKind);
|
||||
if (!res)
|
||||
return nullptr;
|
||||
|
||||
res->setGroup(group);
|
||||
|
||||
// If the length calculation overflowed, make sure that is marked for the
|
||||
// new group.
|
||||
if (res->length() > INT32_MAX)
|
||||
res->setLength(cx, res->length());
|
||||
|
||||
if (PreliminaryObjectArray* preliminaryObjects = group->maybePreliminaryObjects())
|
||||
preliminaryObjects->registerNewObject(res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::NewFullyAllocatedArrayTryUseGroup(JSContext* cx, HandleObjectGroup group, size_t length)
|
||||
{
|
||||
return NewArrayTryUseGroup<UINT32_MAX>(cx, group, length);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::NewPartlyAllocatedArrayTryUseGroup(JSContext* cx, HandleObjectGroup group, size_t length)
|
||||
{
|
||||
return NewArrayTryUseGroup<ArrayObject::EagerAllocationMaxLength>(cx, group, length);
|
||||
}
|
||||
|
||||
// Return a new array with the default prototype and specified allocated
|
||||
// capacity and length. If possible, try to reuse the group of the input
|
||||
// object. The resulting array will either reuse the input object's group or
|
||||
// will have unknown property types. Additionally, the result will have the
|
||||
// same boxed/unboxed elements representation as the input object, unless
|
||||
// |length| is larger than the input object's initialized length (in which case
|
||||
// UnboxedArrayObject::MaximumCapacity might be exceeded).
|
||||
template <uint32_t maxLength>
|
||||
static inline JSObject*
|
||||
NewArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length,
|
||||
NewObjectKind newKind = GenericObject, bool forceAnalyze = false)
|
||||
{
|
||||
if (!obj->is<ArrayObject>() && !obj->is<UnboxedArrayObject>())
|
||||
return NewArray<maxLength>(cx, length, nullptr, newKind);
|
||||
|
||||
if (obj->getProto() != cx->global()->maybeGetArrayPrototype())
|
||||
return NewArray<maxLength>(cx, length, nullptr, newKind);
|
||||
|
||||
RootedObjectGroup group(cx, obj->getGroup(cx));
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
return NewArrayTryUseGroup<maxLength>(cx, group, length, newKind, forceAnalyze);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length,
|
||||
NewObjectKind newKind, bool forceAnalyze)
|
||||
{
|
||||
return NewArrayTryReuseGroup<UINT32_MAX>(cx, obj, length, newKind, forceAnalyze);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::NewPartlyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length)
|
||||
{
|
||||
return NewArrayTryReuseGroup<ArrayObject::EagerAllocationMaxLength>(cx, obj, length);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::NewFullyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length,
|
||||
NewObjectKind newKind, bool forceAnalyze)
|
||||
{
|
||||
RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array));
|
||||
if (!group)
|
||||
return nullptr;
|
||||
return NewArrayTryUseGroup<UINT32_MAX>(cx, group, length, newKind, forceAnalyze);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length)
|
||||
{
|
||||
RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array));
|
||||
if (!group)
|
||||
return nullptr;
|
||||
return NewArrayTryUseGroup<ArrayObject::EagerAllocationMaxLength>(cx, group, length);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::NewCopiedArrayTryUseGroup(JSContext* cx, HandleObjectGroup group, const Value* vp, size_t length)
|
||||
{
|
||||
JSObject* obj = NewFullyAllocatedArrayTryUseGroup(cx, group, length);
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
||||
DenseElementResult result =
|
||||
SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, 0, vp, length, UpdateTypes);
|
||||
if (result == DenseElementResult::Failure)
|
||||
return nullptr;
|
||||
if (result == DenseElementResult::Success)
|
||||
return obj;
|
||||
|
||||
MOZ_ASSERT(obj->is<UnboxedArrayObject>());
|
||||
if (!UnboxedArrayObject::convertToNative(cx, obj))
|
||||
return nullptr;
|
||||
|
||||
result = SetOrExtendBoxedOrUnboxedDenseElements<JSVAL_TYPE_MAGIC>(cx, obj, 0, vp, length,
|
||||
UpdateTypes);
|
||||
MOZ_ASSERT(result != DenseElementResult::Incomplete);
|
||||
if (result == DenseElementResult::Failure)
|
||||
return nullptr;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length)
|
||||
{
|
||||
RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array));
|
||||
if (!group)
|
||||
return nullptr;
|
||||
return NewCopiedArrayTryUseGroup(cx, group, vp, length);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool
|
||||
js::ArrayInfo(JSContext* cx, unsigned argc, Value* vp)
|
||||
|
@ -12,6 +12,8 @@
|
||||
#include "jsobj.h"
|
||||
#include "jspubtd.h"
|
||||
|
||||
#include "vm/ArrayObject.h"
|
||||
|
||||
namespace js {
|
||||
/* 2^32-2, inclusive */
|
||||
const uint32_t MAX_ARRAY_INDEX = 4294967294u;
|
||||
@ -35,7 +37,7 @@ IdIsIndex(jsid id, uint32_t* indexp)
|
||||
extern JSObject*
|
||||
InitArrayClass(JSContext* cx, js::HandleObject obj);
|
||||
|
||||
class ArrayObject;
|
||||
// The methods below only create dense boxed arrays.
|
||||
|
||||
/* Create a dense array with no capacity allocated, length set to 0. */
|
||||
extern ArrayObject * JS_FASTCALL
|
||||
@ -63,20 +65,6 @@ extern ArrayObject * JS_FASTCALL
|
||||
NewDenseFullyAllocatedArray(ExclusiveContext* cx, uint32_t length, HandleObject proto = nullptr,
|
||||
NewObjectKind newKind = GenericObject);
|
||||
|
||||
enum AllocatingBehaviour {
|
||||
NewArray_Unallocating,
|
||||
NewArray_PartlyAllocating,
|
||||
NewArray_FullyAllocating
|
||||
};
|
||||
|
||||
/*
|
||||
* Create a dense array with a set length, but only allocates space for the
|
||||
* contents if the length is not excessive.
|
||||
*/
|
||||
extern ArrayObject*
|
||||
NewDenseArray(ExclusiveContext* cx, uint32_t length, HandleObjectGroup group,
|
||||
AllocatingBehaviour allocating, bool convertDoubleElements = false);
|
||||
|
||||
/* Create a dense array with a copy of the dense array elements in src. */
|
||||
extern ArrayObject*
|
||||
NewDenseCopiedArray(JSContext* cx, uint32_t length, HandleArrayObject src,
|
||||
@ -95,12 +83,36 @@ NewDenseFullyAllocatedArrayWithTemplate(JSContext* cx, uint32_t length, JSObject
|
||||
extern JSObject*
|
||||
NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc::InitialHeap heap);
|
||||
|
||||
/* Create a dense or unboxed array, using the same group as |obj| if possible. */
|
||||
// The methods below can create either boxed or unboxed arrays.
|
||||
|
||||
extern JSObject*
|
||||
NewFullyAllocatedArrayTryUseGroup(JSContext* cx, HandleObjectGroup group, size_t length);
|
||||
|
||||
extern JSObject*
|
||||
NewPartlyAllocatedArrayTryUseGroup(JSContext* cx, HandleObjectGroup group, size_t length);
|
||||
|
||||
extern JSObject*
|
||||
NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length,
|
||||
NewObjectKind newKind = GenericObject,
|
||||
bool forceAnalyze = false);
|
||||
|
||||
extern JSObject*
|
||||
NewPartlyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length);
|
||||
|
||||
extern JSObject*
|
||||
NewFullyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length,
|
||||
NewObjectKind newKind = GenericObject,
|
||||
bool forceAnalyze = false);
|
||||
|
||||
extern JSObject*
|
||||
NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length);
|
||||
|
||||
extern JSObject*
|
||||
NewCopiedArrayTryUseGroup(JSContext* cx, HandleObjectGroup group, const Value* vp, size_t length);
|
||||
|
||||
extern JSObject*
|
||||
NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length);
|
||||
|
||||
/*
|
||||
* Determines whether a write to the given element on |obj| should fail because
|
||||
* |obj| is an Array with a non-writable length, and writing that element would
|
||||
@ -193,7 +205,7 @@ array_slice_dense(JSContext* cx, HandleObject obj, int32_t begin, int32_t end, H
|
||||
extern bool
|
||||
NewbornArrayPush(JSContext* cx, HandleObject obj, const Value& v);
|
||||
|
||||
extern ArrayObject*
|
||||
extern JSObject*
|
||||
ArrayConstructorOneArg(JSContext* cx, HandleObjectGroup group, int32_t lengthInt);
|
||||
|
||||
#ifdef DEBUG
|
||||
|
@ -794,7 +794,6 @@ bool intrinsic_NewDenseArray(JSContext* cx, unsigned argc, Value* vp);
|
||||
bool intrinsic_IsConstructing(JSContext* cx, unsigned argc, Value* vp);
|
||||
bool intrinsic_SubstringKernel(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
bool intrinsic_UnsafePutElements(JSContext* cx, unsigned argc, Value* vp);
|
||||
bool intrinsic_DefineDataProperty(JSContext* cx, unsigned argc, Value* vp);
|
||||
bool intrinsic_UnsafeSetReservedSlot(JSContext* cx, unsigned argc, Value* vp);
|
||||
bool intrinsic_UnsafeGetReservedSlot(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
@ -663,18 +663,15 @@ js::StandardDefineProperty(JSContext* cx, HandleObject obj, HandleId id,
|
||||
if (IsAnyTypedArray(obj))
|
||||
return DefinePropertyOnTypedArray(cx, obj, id, desc, result);
|
||||
|
||||
if (!MaybeConvertUnboxedObjectToNative(cx, obj))
|
||||
return false;
|
||||
|
||||
if (obj->getOps()->lookupProperty) {
|
||||
if (obj->is<ProxyObject>()) {
|
||||
Rooted<PropertyDescriptor> pd(cx, desc);
|
||||
pd.object().set(obj);
|
||||
return Proxy::defineProperty(cx, obj, id, pd, result);
|
||||
}
|
||||
return result.fail(JSMSG_OBJECT_NOT_EXTENSIBLE);
|
||||
if (obj->is<ProxyObject>()) {
|
||||
Rooted<PropertyDescriptor> pd(cx, desc);
|
||||
pd.object().set(obj);
|
||||
return Proxy::defineProperty(cx, obj, id, pd, result);
|
||||
}
|
||||
|
||||
if (obj->getOps()->defineProperty)
|
||||
return obj->getOps()->defineProperty(cx, obj, id, desc, result);
|
||||
|
||||
return DefinePropertyOnObject(cx, obj.as<NativeObject>(), id, desc, result);
|
||||
}
|
||||
|
||||
|
@ -3612,7 +3612,7 @@ class SplitMatchResult {
|
||||
} /* anonymous namespace */
|
||||
|
||||
template<class Matcher>
|
||||
static ArrayObject*
|
||||
static JSObject*
|
||||
SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, const Matcher& splitMatch,
|
||||
HandleObjectGroup group)
|
||||
{
|
||||
@ -3635,10 +3635,10 @@ SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, const Matcher
|
||||
* assertEq(b.length, 0);
|
||||
*/
|
||||
if (!result.isFailure())
|
||||
return NewDenseEmptyArray(cx);
|
||||
return NewFullyAllocatedArrayTryUseGroup(cx, group, 0);
|
||||
|
||||
RootedValue v(cx, StringValue(str));
|
||||
return NewDenseCopiedArray(cx, 1, v.address());
|
||||
return NewCopiedArrayTryUseGroup(cx, group, v.address(), 1);
|
||||
}
|
||||
|
||||
/* Step 12. */
|
||||
@ -3696,7 +3696,7 @@ SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, const Matcher
|
||||
|
||||
/* Step 13(c)(iii)(4). */
|
||||
if (splits.length() == limit)
|
||||
return NewDenseCopiedArray(cx, splits.length(), splits.begin());
|
||||
return NewCopiedArrayTryUseGroup(cx, group, splits.begin(), splits.length());
|
||||
|
||||
/* Step 13(c)(iii)(5). */
|
||||
lastEndIndex = endIndex;
|
||||
@ -3717,15 +3717,13 @@ SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, const Matcher
|
||||
if (!sub || !splits.append(StringValue(sub)))
|
||||
return nullptr;
|
||||
} else {
|
||||
/* Only string entries have been accounted for so far. */
|
||||
AddTypePropertyId(cx, group, nullptr, JSID_VOID, UndefinedValue());
|
||||
if (!splits.append(UndefinedValue()))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Step 13(c)(iii)(7)(d). */
|
||||
if (splits.length() == limit)
|
||||
return NewDenseCopiedArray(cx, splits.length(), splits.begin());
|
||||
return NewCopiedArrayTryUseGroup(cx, group, splits.begin(), splits.length());
|
||||
}
|
||||
}
|
||||
|
||||
@ -3739,12 +3737,12 @@ SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, const Matcher
|
||||
return nullptr;
|
||||
|
||||
/* Step 16. */
|
||||
return NewDenseCopiedArray(cx, splits.length(), splits.begin());
|
||||
return NewCopiedArrayTryUseGroup(cx, group, splits.begin(), splits.length());
|
||||
}
|
||||
|
||||
// Fast-path for splitting a string into a character array via split("").
|
||||
static ArrayObject*
|
||||
CharSplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit)
|
||||
static JSObject*
|
||||
CharSplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, HandleObjectGroup group)
|
||||
{
|
||||
size_t strLength = str->length();
|
||||
if (strLength == 0)
|
||||
@ -3764,7 +3762,7 @@ CharSplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit)
|
||||
splits.infallibleAppend(StringValue(sub));
|
||||
}
|
||||
|
||||
return NewDenseCopiedArray(cx, splits.length(), splits.begin());
|
||||
return NewCopiedArrayTryUseGroup(cx, group, splits.begin(), splits.length());
|
||||
}
|
||||
|
||||
namespace {
|
||||
@ -3849,7 +3847,6 @@ js::str_split(JSContext* cx, unsigned argc, Value* vp)
|
||||
RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array));
|
||||
if (!group)
|
||||
return false;
|
||||
AddTypePropertyId(cx, group, nullptr, JSID_VOID, TypeSet::StringType());
|
||||
|
||||
/* Step 5: Use the second argument as the split limit, if given. */
|
||||
uint32_t limit;
|
||||
@ -3880,10 +3877,9 @@ js::str_split(JSContext* cx, unsigned argc, Value* vp)
|
||||
|
||||
/* Step 9. */
|
||||
if (limit == 0) {
|
||||
JSObject* aobj = NewDenseEmptyArray(cx);
|
||||
JSObject* aobj = NewFullyAllocatedArrayTryUseGroup(cx, group, 0);
|
||||
if (!aobj)
|
||||
return false;
|
||||
aobj->setGroup(group);
|
||||
args.rval().setObject(*aobj);
|
||||
return true;
|
||||
}
|
||||
@ -3891,10 +3887,9 @@ js::str_split(JSContext* cx, unsigned argc, Value* vp)
|
||||
/* Step 10. */
|
||||
if (!sepDefined) {
|
||||
RootedValue v(cx, StringValue(str));
|
||||
JSObject* aobj = NewDenseCopiedArray(cx, 1, v.address());
|
||||
JSObject* aobj = NewCopiedArrayTryUseGroup(cx, group, v.address(), 1);
|
||||
if (!aobj)
|
||||
return false;
|
||||
aobj->setGroup(group);
|
||||
args.rval().setObject(*aobj);
|
||||
return true;
|
||||
}
|
||||
@ -3906,7 +3901,7 @@ js::str_split(JSContext* cx, unsigned argc, Value* vp)
|
||||
RootedObject aobj(cx);
|
||||
if (!re.initialized()) {
|
||||
if (sepstr->length() == 0) {
|
||||
aobj = CharSplitHelper(cx, linearStr, limit);
|
||||
aobj = CharSplitHelper(cx, linearStr, limit, group);
|
||||
} else {
|
||||
SplitStringMatcher matcher(cx, sepstr);
|
||||
aobj = SplitHelper(cx, linearStr, limit, matcher, group);
|
||||
@ -3922,7 +3917,6 @@ js::str_split(JSContext* cx, unsigned argc, Value* vp)
|
||||
return false;
|
||||
|
||||
/* Step 16. */
|
||||
aobj->setGroup(group);
|
||||
args.rval().setObject(*aobj);
|
||||
return true;
|
||||
}
|
||||
@ -3940,19 +3934,11 @@ js::str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, H
|
||||
|
||||
uint32_t limit = UINT32_MAX;
|
||||
|
||||
RootedObject aobj(cx);
|
||||
if (linearSep->length() == 0) {
|
||||
aobj = CharSplitHelper(cx, linearStr, limit);
|
||||
} else {
|
||||
SplitStringMatcher matcher(cx, linearSep);
|
||||
aobj = SplitHelper(cx, linearStr, limit, matcher, group);
|
||||
}
|
||||
if (linearSep->length() == 0)
|
||||
return CharSplitHelper(cx, linearStr, limit, group);
|
||||
|
||||
if (!aobj)
|
||||
return nullptr;
|
||||
|
||||
aobj->setGroup(group);
|
||||
return aobj;
|
||||
SplitStringMatcher matcher(cx, linearSep);
|
||||
return SplitHelper(cx, linearStr, limit, matcher, group);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1174,7 +1174,10 @@ ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* script, jsbytecode* pc
|
||||
}
|
||||
}
|
||||
|
||||
if (JSOp(*pc) == JSOP_NEWARRAY && cx->runtime()->options().unboxedArrays()) {
|
||||
if (kind == JSProto_Array &&
|
||||
(JSOp(*pc) == JSOP_NEWARRAY || IsCallPC(pc)) &&
|
||||
cx->runtime()->options().unboxedArrays())
|
||||
{
|
||||
PreliminaryObjectArrayWithTemplate* preliminaryObjects =
|
||||
cx->new_<PreliminaryObjectArrayWithTemplate>(nullptr);
|
||||
if (preliminaryObjects)
|
||||
|
@ -300,81 +300,11 @@ js::intrinsic_NewDenseArray(JSContext* cx, unsigned argc, Value* vp)
|
||||
uint32_t length = args[0].toInt32();
|
||||
|
||||
// Make a new buffer and initialize it up to length.
|
||||
RootedArrayObject buffer(cx, NewDenseFullyAllocatedArray(cx, length));
|
||||
RootedObject buffer(cx, NewFullyAllocatedArrayForCallingAllocationSite(cx, length));
|
||||
if (!buffer)
|
||||
return false;
|
||||
|
||||
ObjectGroup* newgroup = ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array);
|
||||
if (!newgroup)
|
||||
return false;
|
||||
buffer->setGroup(newgroup);
|
||||
|
||||
DenseElementResult edr = buffer->ensureDenseElements(cx, length, 0);
|
||||
switch (edr) {
|
||||
case DenseElementResult::Success:
|
||||
args.rval().setObject(*buffer);
|
||||
return true;
|
||||
|
||||
case DenseElementResult::Incomplete: // shouldn't happen!
|
||||
MOZ_ASSERT(!"%EnsureDenseArrayElements() would yield sparse array");
|
||||
JS_ReportError(cx, "%EnsureDenseArrayElements() would yield sparse array");
|
||||
break;
|
||||
|
||||
case DenseElementResult::Failure:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* UnsafePutElements(arr0, idx0, elem0, ..., arrN, idxN, elemN): For each set of
|
||||
* (arr, idx, elem) arguments that are passed, performs the assignment
|
||||
* |arr[idx] = elem|. |arr| must be either a dense array or a typed array.
|
||||
*
|
||||
* If |arr| is a dense array, the index must be an int32 less than the
|
||||
* initialized length of |arr|. Use |%EnsureDenseResultArrayElements|
|
||||
* to ensure that the initialized length is long enough.
|
||||
*
|
||||
* If |arr| is a typed array, the index must be an int32 less than the
|
||||
* length of |arr|.
|
||||
*/
|
||||
bool
|
||||
js::intrinsic_UnsafePutElements(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if ((args.length() % 3) != 0) {
|
||||
JS_ReportError(cx, "Incorrect number of arguments, not divisible by 3");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t base = 0; base < args.length(); base += 3) {
|
||||
uint32_t arri = base;
|
||||
uint32_t idxi = base+1;
|
||||
uint32_t elemi = base+2;
|
||||
|
||||
MOZ_ASSERT(args[arri].isObject());
|
||||
MOZ_ASSERT(args[arri].toObject().isNative() || IsTypedObjectArray(args[arri].toObject()));
|
||||
MOZ_ASSERT(args[idxi].isInt32());
|
||||
|
||||
RootedObject arrobj(cx, &args[arri].toObject());
|
||||
uint32_t idx = args[idxi].toInt32();
|
||||
|
||||
if (IsAnyTypedArray(arrobj.get()) || arrobj->is<TypedObject>()) {
|
||||
MOZ_ASSERT_IF(IsAnyTypedArray(arrobj.get()), idx < AnyTypedArrayLength(arrobj.get()));
|
||||
MOZ_ASSERT_IF(arrobj->is<TypedObject>(), idx < uint32_t(arrobj->as<TypedObject>().length()));
|
||||
// XXX: Always non-strict.
|
||||
ObjectOpResult ignored;
|
||||
RootedValue receiver(cx, ObjectValue(*arrobj));
|
||||
if (!SetElement(cx, arrobj, idx, args[elemi], receiver, ignored))
|
||||
return false;
|
||||
} else {
|
||||
MOZ_ASSERT(idx < arrobj->as<ArrayObject>().getDenseInitializedLength());
|
||||
arrobj->as<ArrayObject>().setDenseElementWithType(cx, idx, args[elemi]);
|
||||
}
|
||||
}
|
||||
|
||||
args.rval().setUndefined();
|
||||
args.rval().setObject(*buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -383,38 +313,47 @@ js::intrinsic_DefineDataProperty(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
MOZ_ASSERT(args.length() == 4);
|
||||
MOZ_ASSERT(args.length() >= 3);
|
||||
MOZ_ASSERT(args[0].isObject());
|
||||
MOZ_ASSERT(args[3].isInt32());
|
||||
|
||||
RootedObject obj(cx, &args[0].toObject());
|
||||
RootedId id(cx);
|
||||
if (!ValueToId<CanGC>(cx, args[1], &id))
|
||||
return false;
|
||||
RootedValue value(cx, args[2]);
|
||||
unsigned attributes = args[3].toInt32();
|
||||
|
||||
unsigned attrs = 0;
|
||||
if (args.length() >= 4) {
|
||||
unsigned attributes = args[3].toInt32();
|
||||
|
||||
MOZ_ASSERT(bool(attributes & ATTR_ENUMERABLE) != bool(attributes & ATTR_NONENUMERABLE),
|
||||
"_DefineDataProperty must receive either ATTR_ENUMERABLE xor ATTR_NONENUMERABLE");
|
||||
if (attributes & ATTR_ENUMERABLE)
|
||||
attrs |= JSPROP_ENUMERATE;
|
||||
|
||||
MOZ_ASSERT(bool(attributes & ATTR_CONFIGURABLE) != bool(attributes & ATTR_NONCONFIGURABLE),
|
||||
"_DefineDataProperty must receive either ATTR_CONFIGURABLE xor "
|
||||
"ATTR_NONCONFIGURABLE");
|
||||
if (attributes & ATTR_NONCONFIGURABLE)
|
||||
attrs |= JSPROP_PERMANENT;
|
||||
|
||||
MOZ_ASSERT(bool(attributes & ATTR_WRITABLE) != bool(attributes & ATTR_NONWRITABLE),
|
||||
"_DefineDataProperty must receive either ATTR_WRITABLE xor ATTR_NONWRITABLE");
|
||||
if (attributes & ATTR_NONWRITABLE)
|
||||
attrs |= JSPROP_READONLY;
|
||||
} else {
|
||||
// If the fourth argument is unspecified, the attributes are for a
|
||||
// plain data property.
|
||||
attrs = JSPROP_ENUMERATE;
|
||||
}
|
||||
|
||||
Rooted<PropertyDescriptor> desc(cx);
|
||||
unsigned attrs = 0;
|
||||
|
||||
MOZ_ASSERT(bool(attributes & ATTR_ENUMERABLE) != bool(attributes & ATTR_NONENUMERABLE),
|
||||
"_DefineDataProperty must receive either ATTR_ENUMERABLE xor ATTR_NONENUMERABLE");
|
||||
if (attributes & ATTR_ENUMERABLE)
|
||||
attrs |= JSPROP_ENUMERATE;
|
||||
|
||||
MOZ_ASSERT(bool(attributes & ATTR_CONFIGURABLE) != bool(attributes & ATTR_NONCONFIGURABLE),
|
||||
"_DefineDataProperty must receive either ATTR_CONFIGURABLE xor "
|
||||
"ATTR_NONCONFIGURABLE");
|
||||
if (attributes & ATTR_NONCONFIGURABLE)
|
||||
attrs |= JSPROP_PERMANENT;
|
||||
|
||||
MOZ_ASSERT(bool(attributes & ATTR_WRITABLE) != bool(attributes & ATTR_NONWRITABLE),
|
||||
"_DefineDataProperty must receive either ATTR_WRITABLE xor ATTR_NONWRITABLE");
|
||||
if (attributes & ATTR_NONWRITABLE)
|
||||
attrs |= JSPROP_READONLY;
|
||||
|
||||
desc.setDataDescriptor(value, attrs);
|
||||
return StandardDefineProperty(cx, obj, id, desc);
|
||||
if (!StandardDefineProperty(cx, obj, id, desc))
|
||||
return false;
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -1374,7 +1313,6 @@ static const JSFunctionSpec intrinsic_functions[] = {
|
||||
JS_FN("RuntimeDefaultLocale", intrinsic_RuntimeDefaultLocale, 0,0),
|
||||
JS_FN("SubstringKernel", intrinsic_SubstringKernel, 3,0),
|
||||
|
||||
JS_FN("UnsafePutElements", intrinsic_UnsafePutElements, 3,0),
|
||||
JS_FN("_DefineDataProperty", intrinsic_DefineDataProperty, 4,0),
|
||||
JS_FN("UnsafeSetReservedSlot", intrinsic_UnsafeSetReservedSlot, 3,0),
|
||||
JS_FN("UnsafeGetReservedSlot", intrinsic_UnsafeGetReservedSlot, 2,0),
|
||||
|
@ -320,10 +320,28 @@ SetAnyBoxedOrUnboxedArrayLength(JSContext* cx, JSObject* obj, size_t length)
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
SetAnyBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const Value& value)
|
||||
{
|
||||
if (obj->isNative()) {
|
||||
obj->as<NativeObject>().setDenseElementWithType(cx, index, value);
|
||||
return true;
|
||||
}
|
||||
return obj->as<UnboxedArrayObject>().setElement(cx, index, value);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Template methods for NativeObject and UnboxedArrayObject accesses.
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
static inline JSValueType
|
||||
GetBoxedOrUnboxedType(JSObject* obj)
|
||||
{
|
||||
if (obj->isNative())
|
||||
return JSVAL_TYPE_MAGIC;
|
||||
return obj->as<UnboxedArrayObject>().elementType();
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
static inline bool
|
||||
HasBoxedOrUnboxedDenseElements(JSObject* obj)
|
||||
@ -387,6 +405,17 @@ SetBoxedOrUnboxedDenseElementNoTypeChange(JSObject* obj, size_t index, const Val
|
||||
obj->as<UnboxedArrayObject>().setElementNoTypeChangeSpecific<Type>(index, value);
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
static inline bool
|
||||
SetBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const Value& value)
|
||||
{
|
||||
if (Type == JSVAL_TYPE_MAGIC) {
|
||||
obj->as<NativeObject>().setDenseElementWithType(cx, index, value);
|
||||
return true;
|
||||
}
|
||||
return obj->as<UnboxedArrayObject>().setElementSpecific<Type>(cx, index, value);
|
||||
}
|
||||
|
||||
template <JSValueType Type>
|
||||
static inline bool
|
||||
EnsureBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count)
|
||||
@ -666,6 +695,9 @@ CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
|
||||
void
|
||||
SetAnyBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen);
|
||||
|
||||
bool
|
||||
EnsureAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count);
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif // vm_UnboxedObject_inl_h
|
||||
|
@ -416,6 +416,8 @@ UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group)
|
||||
|
||||
RootedObjectGroup replacementGroup(cx);
|
||||
|
||||
const Class* clasp = layout.isArray() ? &ArrayObject::class_ : &PlainObject::class_;
|
||||
|
||||
// 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
|
||||
@ -447,33 +449,33 @@ UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group)
|
||||
// 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()) {
|
||||
MOZ_ASSERT(!layout.isArray());
|
||||
|
||||
RootedScript script(cx, layout.allocationScript());
|
||||
jsbytecode* pc = layout.allocationPc();
|
||||
|
||||
replacementGroup = ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, proto);
|
||||
replacementGroup = ObjectGroupCompartment::makeGroup(cx, clasp, 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,
|
||||
JSProtoKey key = layout.isArray() ? JSProto_Array : JSProto_Object;
|
||||
cx->compartment()->objectGroups.replaceAllocationSiteGroup(script, pc, key,
|
||||
replacementGroup);
|
||||
|
||||
// Clear any baseline information at this opcode.
|
||||
// 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);
|
||||
fallback->toNewObject_Fallback()->setTemplateObject(nullptr);
|
||||
if (fallback->isNewObject_Fallback())
|
||||
fallback->toNewObject_Fallback()->setTemplateObject(nullptr);
|
||||
else if (fallback->isNewArray_Fallback())
|
||||
fallback->toNewArray_Fallback()->setTemplateGroup(replacementGroup);
|
||||
}
|
||||
}
|
||||
|
||||
const Class* clasp = layout.isArray() ? &ArrayObject::class_ : &PlainObject::class_;
|
||||
size_t nfixed = layout.isArray() ? 0 : gc::GetGCKindSlots(layout.getAllocKind());
|
||||
|
||||
if (layout.isArray()) {
|
||||
@ -734,7 +736,7 @@ UnboxedPlainObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId
|
||||
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 true;
|
||||
return result.succeed();
|
||||
}
|
||||
|
||||
// Trying to incompatibly redefine an existing property requires the
|
||||
@ -742,8 +744,7 @@ UnboxedPlainObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId
|
||||
if (!convertToNative(cx, obj))
|
||||
return false;
|
||||
|
||||
return DefineProperty(cx, obj, id, desc, result) &&
|
||||
result.checkStrict(cx, obj, id);
|
||||
return DefineProperty(cx, obj, id, desc, result);
|
||||
}
|
||||
|
||||
// Define the property on the expando object.
|
||||
@ -1001,13 +1002,14 @@ UnboxedArrayObject::convertToNative(JSContext* cx, JSObject* obj)
|
||||
|
||||
/* static */ UnboxedArrayObject*
|
||||
UnboxedArrayObject::create(ExclusiveContext* cx, HandleObjectGroup group, uint32_t length,
|
||||
NewObjectKind newKind)
|
||||
NewObjectKind newKind, uint32_t maxLength)
|
||||
{
|
||||
MOZ_ASSERT(length <= MaximumCapacity);
|
||||
|
||||
MOZ_ASSERT(group->clasp() == &class_);
|
||||
uint32_t elementSize = UnboxedTypeSize(group->unboxedLayout().elementType());
|
||||
uint32_t nbytes = offsetOfInlineElements() + elementSize * length;
|
||||
uint32_t capacity = Min(length, maxLength);
|
||||
uint32_t nbytes = offsetOfInlineElements() + elementSize * capacity;
|
||||
|
||||
UnboxedArrayObject* res;
|
||||
if (nbytes <= JSObject::MAX_BYTE_SIZE) {
|
||||
@ -1015,7 +1017,7 @@ UnboxedArrayObject::create(ExclusiveContext* cx, HandleObjectGroup group, uint32
|
||||
|
||||
// If there was no provided length information, pick an allocation kind
|
||||
// to accommodate small arrays (as is done for normal native arrays).
|
||||
if (length == 0)
|
||||
if (capacity == 0)
|
||||
allocKind = gc::AllocKind::OBJECT8;
|
||||
|
||||
res = NewObjectWithGroup<UnboxedArrayObject>(cx, group, allocKind, newKind);
|
||||
@ -1023,14 +1025,20 @@ UnboxedArrayObject::create(ExclusiveContext* cx, HandleObjectGroup group, uint32
|
||||
return nullptr;
|
||||
res->setInlineElements();
|
||||
|
||||
size_t capacity = (GetGCKindBytes(allocKind) - offsetOfInlineElements()) / elementSize;
|
||||
res->setCapacityIndex(exactCapacityIndex(capacity));
|
||||
size_t actualCapacity = (GetGCKindBytes(allocKind) - offsetOfInlineElements()) / elementSize;
|
||||
MOZ_ASSERT(actualCapacity >= capacity);
|
||||
res->setCapacityIndex(exactCapacityIndex(actualCapacity));
|
||||
} else {
|
||||
res = NewObjectWithGroup<UnboxedArrayObject>(cx, group, gc::AllocKind::OBJECT0, newKind);
|
||||
if (!res)
|
||||
return nullptr;
|
||||
|
||||
res->elements_ = AllocateObjectBuffer<uint8_t>(cx, res, length * elementSize);
|
||||
uint32_t capacityIndex = (capacity == length)
|
||||
? CapacityMatchesLengthIndex
|
||||
: chooseCapacityIndex(capacity, length);
|
||||
uint32_t actualCapacity = computeCapacity(capacityIndex, length);
|
||||
|
||||
res->elements_ = AllocateObjectBuffer<uint8_t>(cx, res, actualCapacity * elementSize);
|
||||
if (!res->elements_) {
|
||||
// Make the object safe for GC.
|
||||
res->setInlineElements();
|
||||
@ -1038,7 +1046,7 @@ UnboxedArrayObject::create(ExclusiveContext* cx, HandleObjectGroup group, uint32
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
res->setCapacityIndex(CapacityMatchesLengthIndex);
|
||||
res->setCapacityIndex(capacityIndex);
|
||||
}
|
||||
|
||||
res->setLength(cx, length);
|
||||
|
@ -416,7 +416,8 @@ class UnboxedArrayObject : public JSObject
|
||||
|
||||
static bool convertToNative(JSContext* cx, JSObject* obj);
|
||||
static UnboxedArrayObject* create(ExclusiveContext* cx, HandleObjectGroup group,
|
||||
uint32_t length, NewObjectKind newKind);
|
||||
uint32_t length, NewObjectKind newKind,
|
||||
uint32_t maxLength = MaximumCapacity);
|
||||
|
||||
void fillAfterConvert(ExclusiveContext* cx,
|
||||
const AutoValueVector& values, size_t* valueCursor);
|
||||
|
Loading…
Reference in New Issue
Block a user