Bug 980630 - Remove type nuking, r=jandem.

This commit is contained in:
Brian Hackett 2014-03-08 10:57:38 -07:00
Родитель f745bb2ae1
Коммит 3899368c4e
16 изменённых файлов: 200 добавлений и 403 удалений

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

@ -152,9 +152,6 @@ struct AutoStopVerifyingBarriers
};
#endif /* JS_GC_ZEAL */
void
CrashAtUnhandlableOOM(const char *reason);
} /* namespace gc */
} /* namespace js */

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

@ -24,10 +24,11 @@
#include "js/Tracer.h"
namespace js {
namespace gc {
extern void
CrashAtUnhandlableOOM(const char *);
void
CrashAtUnhandlableOOM(const char *reason);
namespace gc {
/*
* BufferableRef represents an abstract reference for use in the generational

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

@ -882,12 +882,3 @@ js::gc::FinishVerifier(JSRuntime *rt)
}
#endif /* JS_GC_ZEAL */
void
js::gc::CrashAtUnhandlableOOM(const char *reason)
{
char msgbuf[1024];
JS_snprintf(msgbuf, sizeof(msgbuf), "[unhandlable oom] %s", reason);
MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
MOZ_CRASH();
}

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

@ -1,6 +0,0 @@
// |jit-test| allow-oom
if (typeof oomAfterAllocations == 'function') {
gczeal(4);
oomAfterAllocations(1);
var s = new Set;
}

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

@ -2065,7 +2065,8 @@ AnalyzePoppedThis(JSContext *cx, types::TypeObject *type,
block = rp->block(), rp = block->callerResumePoint())
{
JSScript *script = rp->block()->info().script();
types::AddClearDefiniteFunctionUsesInScript(cx, type, script, block->info().script());
if (!types::AddClearDefiniteFunctionUsesInScript(cx, type, script, block->info().script()))
return true;
if (!callerResumePoints.append(rp))
return false;
}

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

@ -617,8 +617,7 @@ IonBuilder::build()
}
#endif
if (!initParameters())
return false;
initParameters();
// Initialize local variables.
for (uint32_t i = 0; i < info().nlocals(); i++) {
@ -910,20 +909,18 @@ IonBuilder::rewriteParameters()
}
}
bool
void
IonBuilder::initParameters()
{
if (!info().funMaybeLazy())
return true;
return;
// If we are doing OSR on a frame which initially executed in the
// interpreter and didn't accumulate type information, try to use that OSR
// frame to determine possible initial types for 'this' and parameters.
if (thisTypes->empty() && baselineFrame_) {
if (!thisTypes->addType(baselineFrame_->thisType, alloc_->lifoAlloc()))
return false;
}
if (thisTypes->empty() && baselineFrame_)
thisTypes->addType(baselineFrame_->thisType, alloc_->lifoAlloc());
MParameter *param = MParameter::New(alloc(), MParameter::THIS_SLOT, thisTypes);
current->add(param);
@ -934,16 +931,13 @@ IonBuilder::initParameters()
if (types->empty() && baselineFrame_ &&
!script_->baselineScript()->modifiesArguments())
{
if (!types->addType(baselineFrame_->argTypes[i], alloc_->lifoAlloc()))
return false;
types->addType(baselineFrame_->argTypes[i], alloc_->lifoAlloc());
}
param = MParameter::New(alloc(), i, types);
current->add(param);
current->initSlot(info().argSlotUnchecked(i), param);
}
return true;
}
bool
@ -4971,14 +4965,12 @@ IonBuilder::jsop_call(uint32_t argc, bool constructing)
types::TemporaryTypeSet *observed = bytecodeTypes(pc);
if (observed->empty()) {
if (BytecodeFlowsToBitop(pc)) {
if (!observed->addType(types::Type::Int32Type(), alloc_->lifoAlloc()))
return false;
observed->addType(types::Type::Int32Type(), alloc_->lifoAlloc());
} else if (*GetNextPc(pc) == JSOP_POS) {
// Note: this is lame, overspecialized on the code patterns used
// by asm.js and should be replaced by a more general mechanism.
// See bug 870847.
if (!observed->addType(types::Type::DoubleType(), alloc_->lifoAlloc()))
return false;
observed->addType(types::Type::DoubleType(), alloc_->lifoAlloc());
}
}
@ -7091,8 +7083,7 @@ IonBuilder::jsop_getelem_dense(MDefinition *obj, MDefinition *index)
// Indexed call on an element of an array. Populate the observed types
// with any objects that could be in the array, to avoid extraneous
// type barriers.
if (!AddObjectsForPropertyRead(obj, nullptr, types))
return false;
AddObjectsForPropertyRead(obj, nullptr, types);
}
bool barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), obj, nullptr, types);
@ -9288,8 +9279,7 @@ IonBuilder::jsop_setarg(uint32_t arg)
}
if (!otherUses) {
JS_ASSERT(op->resultTypeSet() == &argTypes[arg]);
if (!argTypes[arg].addType(types::Type::UnknownType(), alloc_->lifoAlloc()))
return false;
argTypes[arg].addType(types::Type::UnknownType(), alloc_->lifoAlloc());
if (val->isMul()) {
val->setResultType(MIRType_Double);
val->toMul()->setSpecialization(MIRType_Double);

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

@ -327,7 +327,7 @@ class IonBuilder : public MIRGenerator
void insertRecompileCheck();
bool initParameters();
void initParameters();
void rewriteParameter(uint32_t slotIdx, MDefinition *param, int32_t argIndex);
void rewriteParameters();
bool initScopeChain(MDefinition *callee = nullptr);

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

@ -940,8 +940,7 @@ IonBuilder::inlineMathFRound(CallInfo &callInfo)
if (returned->empty()) {
// As there's only one possible returned type, just add it to the observed
// returned typeset
if (!returned->addType(types::Type::DoubleType(), alloc_->lifoAlloc()))
return InliningStatus_Error;
returned->addType(types::Type::DoubleType(), alloc_->lifoAlloc());
} else {
MIRType returnType = getInlineReturnType();
if (!IsNumberType(returnType))

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

@ -2765,10 +2765,8 @@ InlinePropertyTable::buildTypeSetForFunction(JSFunction *func) const
if (!types)
return nullptr;
for (size_t i = 0; i < numEntries(); i++) {
if (entries_[i]->func == func) {
if (!types->addType(types::Type::ObjectType(entries_[i]->typeObj), alloc))
return nullptr;
}
if (entries_[i]->func == func)
types->addType(types::Type::ObjectType(entries_[i]->typeObj), alloc);
}
return types;
}
@ -3172,7 +3170,7 @@ jit::PropertyReadIsIdempotent(types::CompilerConstraintList *constraints,
return true;
}
bool
void
jit::AddObjectsForPropertyRead(MDefinition *obj, PropertyName *name,
types::TemporaryTypeSet *observed)
{
@ -3182,16 +3180,20 @@ jit::AddObjectsForPropertyRead(MDefinition *obj, PropertyName *name,
LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc();
types::TemporaryTypeSet *types = obj->resultTypeSet();
if (!types || types->unknownObject())
return observed->addType(types::Type::AnyObjectType(), alloc);
if (!types || types->unknownObject()) {
observed->addType(types::Type::AnyObjectType(), alloc);
return;
}
for (size_t i = 0; i < types->getObjectCount(); i++) {
types::TypeObjectKey *object = types->getObject(i);
if (!object)
continue;
if (object->unknownProperties())
return observed->addType(types::Type::AnyObjectType(), alloc);
if (object->unknownProperties()) {
observed->addType(types::Type::AnyObjectType(), alloc);
return;
}
jsid id = name ? NameToId(name) : JSID_VOID;
types::HeapTypeSetKey property = object->property(id);
@ -3199,17 +3201,17 @@ jit::AddObjectsForPropertyRead(MDefinition *obj, PropertyName *name,
if (!types)
continue;
if (types->unknownObject())
return observed->addType(types::Type::AnyObjectType(), alloc);
if (types->unknownObject()) {
observed->addType(types::Type::AnyObjectType(), alloc);
return;
}
for (size_t i = 0; i < types->getObjectCount(); i++) {
types::TypeObjectKey *object = types->getObject(i);
if (object && !observed->addType(types::Type::ObjectType(object), alloc))
return false;
if (object)
observed->addType(types::Type::ObjectType(object), alloc);
}
}
return true;
}
static bool

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

@ -9866,7 +9866,7 @@ bool PropertyReadOnPrototypeNeedsTypeBarrier(types::CompilerConstraintList *cons
types::TemporaryTypeSet *observed);
bool PropertyReadIsIdempotent(types::CompilerConstraintList *constraints,
MDefinition *obj, PropertyName *name);
bool AddObjectsForPropertyRead(MDefinition *obj, PropertyName *name,
void AddObjectsForPropertyRead(MDefinition *obj, PropertyName *name,
types::TemporaryTypeSet *observed);
bool PropertyWriteNeedsTypeBarrier(TempAllocator &alloc, types::CompilerConstraintList *constraints,
MBasicBlock *current, MDefinition **pobj,

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

@ -2206,10 +2206,8 @@ ScriptAnalysis::needsArgsObj(JSContext *cx, SeenVector &seen, const SSAValue &v)
if (v == seen[i])
return false;
}
if (!seen.append(v)) {
cx->compartment()->types.setPendingNukeTypes(cx);
if (!seen.append(v))
return true;
}
SSAUseChain *use = useChain(v);
while (use) {

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

@ -1365,3 +1365,11 @@ void CompartmentChecker::check(AbstractFramePtr frame)
}
#endif
void
js::CrashAtUnhandlableOOM(const char *reason)
{
char msgbuf[1024];
JS_snprintf(msgbuf, sizeof(msgbuf), "[unhandlable oom] %s", reason);
MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
MOZ_CRASH();
}

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

@ -374,7 +374,7 @@ TypeSet::enumerateTypes(TypeList *list)
return true;
}
inline void
inline bool
TypeSet::addTypesToConstraint(JSContext *cx, TypeConstraint *constraint)
{
/*
@ -383,19 +383,20 @@ TypeSet::addTypesToConstraint(JSContext *cx, TypeConstraint *constraint)
*/
TypeList types;
if (!enumerateTypes(&types))
cx->compartment()->types.setPendingNukeTypes(cx);
return false;
for (unsigned i = 0; i < types.length(); i++)
constraint->newType(cx, this, types[i]);
return true;
}
void
ConstraintTypeSet::add(JSContext *cx, TypeConstraint *constraint, bool callExisting)
bool
ConstraintTypeSet::addConstraint(JSContext *cx, TypeConstraint *constraint, bool callExisting)
{
if (!constraint) {
/* OOM failure while constructing the constraint. */
cx->compartment()->types.setPendingNukeTypes(cx);
return;
return false;
}
JS_ASSERT(cx->compartment()->activeAnalysis);
@ -410,7 +411,8 @@ ConstraintTypeSet::add(JSContext *cx, TypeConstraint *constraint, bool callExist
constraintList = constraint;
if (callExisting)
addTypesToConstraint(cx, constraint);
return addTypesToConstraint(cx, constraint);
return true;
}
void
@ -502,14 +504,12 @@ TypeSet::unionSets(TypeSet *a, TypeSet *b, LifoAlloc *alloc)
if (!res->unknownObject()) {
for (size_t i = 0; i < a->getObjectCount() && !res->unknownObject(); i++) {
TypeObjectKey *key = a->getObject(i);
if (key && !res->addType(Type::ObjectType(key), alloc))
return nullptr;
if (TypeObjectKey *key = a->getObject(i))
res->addType(Type::ObjectType(key), alloc);
}
for (size_t i = 0; i < b->getObjectCount() && !res->unknownObject(); i++) {
TypeObjectKey *key = b->getObject(i);
if (key && !res->addType(Type::ObjectType(key), alloc))
return nullptr;
if (TypeObjectKey *key = b->getObject(i))
res->addType(Type::ObjectType(key), alloc);
}
}
@ -761,13 +761,11 @@ class TypeCompilerConstraint : public TypeConstraint
cx->zone()->types.addPendingRecompile(cx, compilation);
}
TypeConstraint *sweep(TypeZone &zone) {
bool sweep(TypeZone &zone, TypeConstraint **res) {
if (data.shouldSweep() || compilation.shouldSweep(zone))
return nullptr;
TypeConstraint *res = zone.typeLifoAlloc.new_<TypeCompilerConstraint<T> >(compilation, data);
if (!res)
zone.setPendingNukeTypes();
return res;
return false;
*res = zone.typeLifoAlloc.new_<TypeCompilerConstraint<T> >(compilation, data);
return true;
}
};
@ -784,9 +782,8 @@ CompilerConstraintInstance<T>::generateTypeConstraint(JSContext *cx, RecompileIn
if (!data.constraintHolds(cx, property, expected))
return false;
property.maybeTypes()->add(cx, cx->typeLifoAlloc().new_<TypeCompilerConstraint<T> >(recompileInfo, data),
/* callExisting = */ false);
return true;
return property.maybeTypes()->addConstraint(cx, cx->typeLifoAlloc().new_<TypeCompilerConstraint<T> >(recompileInfo, data),
/* callExisting = */ false);
}
} /* anonymous namespace */
@ -941,13 +938,11 @@ class TypeConstraintFreezeStack : public TypeConstraint
cx->zone()->types.addPendingRecompile(cx, script_);
}
TypeConstraint *sweep(TypeZone &zone) {
bool sweep(TypeZone &zone, TypeConstraint **res) {
if (IsScriptAboutToBeFinalized(&script_))
return nullptr;
TypeConstraint *res = zone.typeLifoAlloc.new_<TypeConstraintFreezeStack>(script_);
if (!res)
zone.setPendingNukeTypes();
return res;
return false;
*res = zone.typeLifoAlloc.new_<TypeConstraintFreezeStack>(script_);
return true;
}
};
@ -1017,8 +1012,10 @@ types::FinishCompilation(JSContext *cx, HandleScript script, ExecutionMode execu
size_t count = TypeScript::NumTypeSets(entry.script);
StackTypeSet *array = entry.script->types->typeArray();
for (size_t i = 0; i < count; i++)
array[i].add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeStack>(entry.script), false);
for (size_t i = 0; i < count; i++) {
if (!array[i].addConstraint(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeStack>(entry.script), false))
succeeded = false;
}
}
if (!succeeded || types.compilerOutputs->back().pendingInvalidation()) {
@ -1976,7 +1973,7 @@ TypeCompartment::addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey ke
if (!allocationSiteTable) {
allocationSiteTable = cx->new_<AllocationSiteTable>();
if (!allocationSiteTable || !allocationSiteTable->init()) {
cx->compartment()->types.setPendingNukeTypes(cx);
js_delete(allocationSiteTable);
return nullptr;
}
}
@ -2012,10 +2009,8 @@ TypeCompartment::addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey ke
Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
res = newTypeObject(cx, GetClassForProtoKey(key.kind), tagged, OBJECT_FLAG_FROM_ALLOCATION_SITE);
if (!res) {
cx->compartment()->types.setPendingNukeTypes(cx);
if (!res)
return nullptr;
}
key.script = keyScript;
}
@ -2031,10 +2026,8 @@ TypeCompartment::addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey ke
return nullptr;
}
if (!allocationSiteTable->add(p, key, res)) {
cx->compartment()->types.setPendingNukeTypes(cx);
if (!allocationSiteTable->add(p, key, res))
return nullptr;
}
return res;
}
@ -2209,60 +2202,6 @@ TypeZone::processPendingRecompiles(FreeOp *fop)
fop->delete_(pending);
}
void
TypeCompartment::setPendingNukeTypes(ExclusiveContext *cx)
{
TypeZone *zone = &compartment()->zone()->types;
if (!zone->pendingNukeTypes) {
if (cx->compartment())
js_ReportOutOfMemory(cx);
zone->pendingNukeTypes = true;
}
}
void
TypeZone::setPendingNukeTypes()
{
pendingNukeTypes = true;
}
void
TypeZone::nukeTypes(FreeOp *fop)
{
/*
* This is the usual response if we encounter an OOM while adding a type
* or resolving type constraints. Reset the compartment to not use type
* inference, and recompile all scripts.
*
* Because of the nature of constraint-based analysis (add constraints, and
* iterate them until reaching a fixpoint), we can't undo an add of a type set,
* and merely aborting the operation which triggered the add will not be
* sufficient for correct behavior as we will be leaving the types in an
* inconsistent state.
*/
JS_ASSERT(pendingNukeTypes);
if (pendingRecompiles) {
fop->free_(pendingRecompiles);
pendingRecompiles = nullptr;
}
inferenceEnabled = false;
#ifdef JS_ION
jit::InvalidateAll(fop, zone());
/* Throw away all JIT code in the compartment, but leave everything else alone. */
for (gc::CellIter i(zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
jit::FinishInvalidation(fop, script);
}
#endif /* JS_ION */
pendingNukeTypes = false;
}
void
TypeZone::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
{
@ -2270,23 +2209,19 @@ TypeZone::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
if (!co || !co->isValid() || co->pendingInvalidation())
return;
if (!pendingRecompiles) {
pendingRecompiles = cx->new_< Vector<RecompileInfo> >(cx);
if (!pendingRecompiles) {
cx->compartment()->types.setPendingNukeTypes(cx);
return;
}
}
if (!pendingRecompiles->append(info)) {
cx->compartment()->types.setPendingNukeTypes(cx);
return;
}
InferSpew(ISpewOps, "addPendingRecompile: %p:%s:%d",
co->script(), co->script()->filename(), co->script()->lineno());
co->setPendingInvalidation();
if (!pendingRecompiles) {
pendingRecompiles = cx->new_< Vector<RecompileInfo> >(cx);
if (!pendingRecompiles)
CrashAtUnhandlableOOM("Could not update pendingRecompiles");
}
if (!pendingRecompiles->append(info))
CrashAtUnhandlableOOM("Could not update pendingRecompiles");
}
void
@ -2325,32 +2260,18 @@ TypeCompartment::markSetsUnknown(JSContext *cx, TypeObject *target)
AutoEnterAnalysis enter(cx);
/*
* Mark both persistent and transient type sets which contain obj as having
* a generic object type. It is not sufficient to mark just the persistent
* sets, as analysis of individual opcodes can pull type objects from
* static information (like initializer objects at various offsets).
*
* We make a list of properties to update and fix them afterwards, as adding
* types can't be done while iterating over cells as it can potentially make
* new type objects as well or trigger GC.
*/
Vector<ConstraintTypeSet *> pending(cx);
/* Mark type sets which contain obj as having a generic object types. */
for (gc::CellIter i(cx->zone(), gc::FINALIZE_TYPE_OBJECT); !i.done(); i.next()) {
TypeObject *object = i.get<TypeObject>();
unsigned count = object->getPropertyCount();
for (unsigned i = 0; i < count; i++) {
Property *prop = object->getProperty(i);
if (prop && prop->types.hasType(Type::ObjectType(target))) {
if (!pending.append(&prop->types))
cx->compartment()->types.setPendingNukeTypes(cx);
}
if (prop && prop->types.hasType(Type::ObjectType(target)))
prop->types.addType(cx, Type::AnyObjectType());
}
}
for (unsigned i = 0; i < pending.length(); i++)
pending[i]->addType(cx, Type::AnyObjectType());
for (gc::CellIter i(cx->zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
RootedScript script(cx, i.get<JSScript>());
if (script->types) {
@ -2461,7 +2382,6 @@ TypeCompartment::setTypeToHomogenousArray(ExclusiveContext *cx,
arrayTypeTable = cx->new_<ArrayTypeTable>();
if (!arrayTypeTable || !arrayTypeTable->init()) {
arrayTypeTable = nullptr;
cx->compartment()->types.setPendingNukeTypes(cx);
return;
}
}
@ -2474,20 +2394,15 @@ TypeCompartment::setTypeToHomogenousArray(ExclusiveContext *cx,
/* Make a new type to use for future arrays with the same elements. */
RootedObject objProto(cx, obj->getProto());
TypeObject *objType = newTypeObject(cx, &ArrayObject::class_, objProto);
if (!objType) {
cx->compartment()->types.setPendingNukeTypes(cx);
if (!objType)
return;
}
obj->setType(objType);
if (!objType->unknownProperties())
objType->addPropertyType(cx, JSID_VOID, elementType);
key.proto = objProto;
if (!p.add(cx, *arrayTypeTable, key, objType)) {
cx->compartment()->types.setPendingNukeTypes(cx);
return;
}
(void) p.add(cx, *arrayTypeTable, key, objType);
}
}
@ -2625,8 +2540,8 @@ TypeCompartment::fixObjectType(ExclusiveContext *cx, JSObject *obj)
if (!objectTypeTable) {
objectTypeTable = cx->new_<ObjectTypeTable>();
if (!objectTypeTable || !objectTypeTable->init()) {
js_delete(objectTypeTable);
objectTypeTable = nullptr;
cx->compartment()->types.setPendingNukeTypes(cx);
return;
}
}
@ -2646,10 +2561,8 @@ TypeCompartment::fixObjectType(ExclusiveContext *cx, JSObject *obj)
return;
Vector<IdValuePair> properties(cx);
if (!properties.resize(obj->slotSpan())) {
cx->compartment()->types.setPendingNukeTypes(cx);
if (!properties.resize(obj->slotSpan()))
return;
}
Shape *shape = obj->lastProperty();
while (!shape->isEmptyShape()) {
@ -2674,25 +2587,19 @@ TypeCompartment::fixObjectType(ExclusiveContext *cx, JSObject *obj)
/* Make a new type to use for the object and similar future ones. */
Rooted<TaggedProto> objProto(cx, obj->getTaggedProto());
TypeObject *objType = newTypeObject(cx, &JSObject::class_, objProto);
if (!objType || !objType->addDefiniteProperties(cx, obj)) {
cx->compartment()->types.setPendingNukeTypes(cx);
if (!objType || !objType->addDefiniteProperties(cx, obj))
return;
}
if (obj->isIndexed())
objType->setFlags(cx, OBJECT_FLAG_SPARSE_INDEXES);
jsid *ids = cx->pod_calloc<jsid>(properties.length());
if (!ids) {
cx->compartment()->types.setPendingNukeTypes(cx);
ScopedJSFreePtr<jsid> ids(cx->pod_calloc<jsid>(properties.length()));
if (!ids)
return;
}
Type *types = cx->pod_calloc<Type>(properties.length());
if (!types) {
cx->compartment()->types.setPendingNukeTypes(cx);
ScopedJSFreePtr<Type> types(cx->pod_calloc<Type>(properties.length()));
if (!types)
return;
}
for (size_t i = 0; i < properties.length(); i++) {
ids[i] = properties[i].id;
@ -2712,13 +2619,13 @@ TypeCompartment::fixObjectType(ExclusiveContext *cx, JSObject *obj)
entry.shape = obj->lastProperty();
entry.types = types;
p = objectTypeTable->lookupForAdd(lookup);
if (!objectTypeTable->add(p, key, entry)) {
cx->compartment()->types.setPendingNukeTypes(cx);
return;
}
obj->setType(objType);
p = objectTypeTable->lookupForAdd(lookup);
if (objectTypeTable->add(p, key, entry)) {
ids.forget();
types.forget();
}
}
JSObject *
@ -2729,8 +2636,8 @@ TypeCompartment::newTypedObject(JSContext *cx, IdValuePair *properties, size_t n
if (!objectTypeTable) {
objectTypeTable = cx->new_<ObjectTypeTable>();
if (!objectTypeTable || !objectTypeTable->init()) {
js_delete(objectTypeTable);
objectTypeTable = nullptr;
cx->compartment()->types.setPendingNukeTypes(cx);
return nullptr;
}
}
@ -2807,8 +2714,7 @@ UpdatePropertyType(ExclusiveContext *cx, HeapTypeSet *types, JSObject *obj, Shap
if (shape->hasGetterValue() || shape->hasSetterValue()) {
types->setNonDataProperty(cx);
if (!types->TypeSet::addType(Type::UnknownType(), &cx->typeLifoAlloc()))
cx->compartment()->types.setPendingNukeTypes(cx);
types->TypeSet::addType(Type::UnknownType(), &cx->typeLifoAlloc());
} else if (shape->hasDefaultGetter() && shape->hasSlot()) {
if (!indexed && types->canSetDefinite(shape->slot()))
types->setDefinite(shape->slot());
@ -2822,8 +2728,7 @@ UpdatePropertyType(ExclusiveContext *cx, HeapTypeSet *types, JSObject *obj, Shap
*/
if (indexed || !value.isUndefined() || !CanHaveEmptyPropertyTypesForOwnProperty(obj)) {
Type type = GetValueType(value);
if (!types->TypeSet::addType(type, &cx->typeLifoAlloc()))
cx->compartment()->types.setPendingNukeTypes(cx);
types->TypeSet::addType(type, &cx->typeLifoAlloc());
}
}
}
@ -2833,10 +2738,8 @@ TypeObject::addProperty(ExclusiveContext *cx, jsid id, Property **pprop)
{
JS_ASSERT(!*pprop);
Property *base = cx->typeLifoAlloc().new_<Property>(id);
if (!base) {
cx->compartment()->types.setPendingNukeTypes(cx);
if (!base)
return false;
}
if (singleton() && singleton()->isNative()) {
/*
@ -2860,8 +2763,7 @@ TypeObject::addProperty(ExclusiveContext *cx, jsid id, Property **pprop)
const Value &value = singleton()->getDenseElement(i);
if (!value.isMagic(JS_ELEMENTS_HOLE)) {
Type type = GetValueType(value);
if (!base->types.TypeSet::addType(type, &cx->typeLifoAlloc()))
cx->compartment()->types.setPendingNukeTypes(cx);
base->types.TypeSet::addType(type, &cx->typeLifoAlloc());
}
}
} else if (!JSID_IS_EMPTY(id)) {
@ -2969,28 +2871,6 @@ TypeObject::addPropertyType(ExclusiveContext *cx, jsid id, const Value &value)
InlineAddTypeProperty(cx, this, id, GetValueType(value));
}
void
TypeObject::addPropertyType(ExclusiveContext *cx, const char *name, Type type)
{
jsid id = JSID_VOID;
if (name) {
JSAtom *atom = Atomize(cx, name, strlen(name));
if (!atom) {
AutoEnterAnalysis enter(cx);
cx->compartment()->types.setPendingNukeTypes(cx);
return;
}
id = AtomToId(atom);
}
InlineAddTypeProperty(cx, this, id, type);
}
void
TypeObject::addPropertyType(ExclusiveContext *cx, const char *name, const Value &value)
{
addPropertyType(cx, name, GetValueType(value));
}
void
TypeObject::markPropertyNonData(ExclusiveContext *cx, jsid id)
{
@ -3242,10 +3122,8 @@ TypeObject::clearNewScriptAddendum(ExclusiveContext *cx)
}
}
if (!finished) {
if (!JSObject::rollbackProperties(cx, obj, numProperties))
cx->compartment()->types.setPendingNukeTypes(cx);
}
if (!finished)
(void) JSObject::rollbackProperties(cx, obj, numProperties);
}
} else {
// Threads with an ExclusiveContext are not allowed to run scripts.
@ -3339,13 +3217,11 @@ class TypeConstraintClearDefiniteGetterSetter : public TypeConstraint
void newType(JSContext *cx, TypeSet *source, Type type) {}
TypeConstraint *sweep(TypeZone &zone) {
bool sweep(TypeZone &zone, TypeConstraint **res) {
if (IsTypeObjectAboutToBeFinalized(&object))
return nullptr;
TypeConstraint *res = zone.typeLifoAlloc.new_<TypeConstraintClearDefiniteGetterSetter>(object);
if (!res)
zone.setPendingNukeTypes();
return res;
return false;
*res = zone.typeLifoAlloc.new_<TypeConstraintClearDefiniteGetterSetter>(object);
return true;
}
};
@ -3365,7 +3241,8 @@ types::AddClearDefiniteGetterSetterForPrototypeChain(JSContext *cx, TypeObject *
HeapTypeSet *parentTypes = parentObject->getProperty(cx, id);
if (!parentTypes || parentTypes->nonDataProperty() || parentTypes->nonWritableProperty())
return false;
parentTypes->add(cx, cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteGetterSetter>(type));
if (!parentTypes->addConstraint(cx, cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteGetterSetter>(type)))
return false;
parent = parent->getProto();
}
return true;
@ -3394,17 +3271,15 @@ class TypeConstraintClearDefiniteSingle : public TypeConstraint
object->clearAddendum(cx);
}
TypeConstraint *sweep(TypeZone &zone) {
bool sweep(TypeZone &zone, TypeConstraint **res) {
if (IsTypeObjectAboutToBeFinalized(&object))
return nullptr;
TypeConstraint *res = zone.typeLifoAlloc.new_<TypeConstraintClearDefiniteSingle>(object);
if (!res)
zone.setPendingNukeTypes();
return res;
return false;
*res = zone.typeLifoAlloc.new_<TypeConstraintClearDefiniteSingle>(object);
return true;
}
};
void
bool
types::AddClearDefiniteFunctionUsesInScript(JSContext *cx, TypeObject *type,
JSScript *script, JSScript *calleeScript)
{
@ -3437,10 +3312,12 @@ types::AddClearDefiniteFunctionUsesInScript(JSContext *cx, TypeObject *type,
}
// This is a type set that might have been used when inlining
// |calleeScript| into |script|.
types->add(cx,
cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteSingle>(type));
if (!types->addConstraint(cx, cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteSingle>(type)))
return false;
}
}
return true;
}
/*
@ -3464,12 +3341,8 @@ CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun)
Vector<TypeNewScript::Initializer> initializerList(cx);
if (!jit::AnalyzeNewScriptProperties(cx, fun, type, baseobj, &initializerList)) {
cx->compartment()->types.setPendingNukeTypes(cx);
return;
}
if (baseobj->slotSpan() == 0 ||
if (!jit::AnalyzeNewScriptProperties(cx, fun, type, baseobj, &initializerList) ||
baseobj->slotSpan() == 0 ||
!!(type->flags() & OBJECT_FLAG_ADDENDUM_CLEARED))
{
if (type->hasNewScript())
@ -3509,7 +3382,6 @@ CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun)
!type->addDefiniteProperties(cx, baseobj) ||
!initializerList.append(done))
{
cx->compartment()->types.setPendingNukeTypes(cx);
return;
}
@ -3526,15 +3398,13 @@ CheckNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun)
#else
newScript = (TypeNewScript *) cx->calloc_(numBytes);
#endif
if (!newScript)
return;
new (newScript) TypeNewScript();
type->setAddendum(newScript);
if (!newScript) {
cx->compartment()->types.setPendingNukeTypes(cx);
return;
}
newScript->fun = fun;
newScript->templateObject = baseobj;
@ -3684,10 +3554,8 @@ JSScript::makeTypes(JSContext *cx)
unsigned count = TypeScript::NumTypeSets(this);
TypeScript *typeScript = (TypeScript *) cx->calloc_(sizeof(TypeScript) + (sizeof(StackTypeSet) * count));
if (!typeScript) {
cx->compartment()->types.setPendingNukeTypes(cx);
if (!typeScript)
return false;
}
new(typeScript) TypeScript();
@ -3836,6 +3704,7 @@ JSObject::makeLazyType(JSContext *cx, HandleObject obj)
{
JS_ASSERT(obj->hasLazyType());
JS_ASSERT(cx->compartment() == obj->compartment());
JS_ASSERT(cx->typeInferenceEnabled());
/* De-lazification of functions can GC, so we need to do it up here. */
if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpretedLazy()) {
@ -3859,17 +3728,8 @@ JSObject::makeLazyType(JSContext *cx, HandleObject obj)
Rooted<TaggedProto> proto(cx, obj->getTaggedProto());
TypeObject *type = cx->compartment()->types.newTypeObject(cx, obj->getClass(), proto, initialFlags);
if (!type) {
if (cx->typeInferenceEnabled())
cx->compartment()->types.setPendingNukeTypes(cx);
if (!type)
return nullptr;
}
if (!cx->typeInferenceEnabled()) {
/* This can only happen if types were previously nuked. */
obj->type_ = type;
return type;
}
AutoEnterAnalysis enter(cx);
@ -4048,22 +3908,22 @@ ExclusiveContext::getNewType(const Class *clasp, TaggedProto proto, JSFunction *
*/
if (obj->is<RegExpObject>()) {
AddTypeProperty(this, type, "source", types::Type::StringType());
AddTypeProperty(this, type, "global", types::Type::BooleanType());
AddTypeProperty(this, type, "ignoreCase", types::Type::BooleanType());
AddTypeProperty(this, type, "multiline", types::Type::BooleanType());
AddTypeProperty(this, type, "sticky", types::Type::BooleanType());
AddTypeProperty(this, type, "lastIndex", types::Type::Int32Type());
type->addPropertyType(this, NameToId(names().source), Type::StringType());
type->addPropertyType(this, NameToId(names().global), Type::BooleanType());
type->addPropertyType(this, NameToId(names().ignoreCase), Type::BooleanType());
type->addPropertyType(this, NameToId(names().multiline), Type::BooleanType());
type->addPropertyType(this, NameToId(names().sticky), Type::BooleanType());
type->addPropertyType(this, NameToId(names().lastIndex), Type::Int32Type());
}
if (obj->is<StringObject>())
AddTypeProperty(this, type, "length", Type::Int32Type());
type->addPropertyType(this, NameToId(names().length), Type::Int32Type());
if (obj->is<ErrorObject>()) {
AddTypeProperty(this, type, "fileName", types::Type::StringType());
AddTypeProperty(this, type, "lineNumber", types::Type::Int32Type());
AddTypeProperty(this, type, "columnNumber", types::Type::Int32Type());
AddTypeProperty(this, type, "stack", types::Type::StringType());
type->addPropertyType(this, NameToId(names().fileName), Type::StringType());
type->addPropertyType(this, NameToId(names().lineNumber), Type::Int32Type());
type->addPropertyType(this, NameToId(names().columnNumber), Type::Int32Type());
type->addPropertyType(this, NameToId(names().stack), Type::StringType());
}
}
@ -4152,10 +4012,9 @@ ConstraintTypeSet::sweep(Zone *zone)
TypeObjectKey **pentry =
HashSetInsert<TypeObjectKey *,TypeObjectKey,TypeObjectKey>
(zone->types.typeLifoAlloc, objectSet, objectCount, object);
if (pentry)
*pentry = object;
else
zone->types.setPendingNukeTypes();
if (!pentry)
CrashAtUnhandlableOOM("OOM in ConstraintTypeSet::sweep");
*pentry = object;
}
}
setBaseObjectCount(objectCount);
@ -4174,7 +4033,10 @@ ConstraintTypeSet::sweep(Zone *zone)
TypeConstraint *constraint = constraintList;
constraintList = nullptr;
while (constraint) {
if (TypeConstraint *copy = constraint->sweep(zone->types)) {
TypeConstraint *copy;
if (constraint->sweep(zone->types, &copy)) {
if (!copy)
CrashAtUnhandlableOOM("OOM in ConstraintTypeSet::sweep");
copy->next = constraintList;
constraintList = copy;
}
@ -4230,20 +4092,19 @@ TypeObject::sweep(FreeOp *fop)
*/
continue;
}
Property *newProp = typeLifoAlloc.new_<Property>(*prop);
if (newProp) {
Property **pentry =
HashSetInsert<jsid,Property,Property>
(typeLifoAlloc, propertySet, propertyCount, prop->id);
if (pentry) {
*pentry = newProp;
newProp->types.sweep(zone());
} else {
zone()->types.setPendingNukeTypes();
}
} else {
zone()->types.setPendingNukeTypes();
}
if (!newProp)
CrashAtUnhandlableOOM("OOM in TypeObject::sweep");
Property **pentry =
HashSetInsert<jsid,Property,Property>
(typeLifoAlloc, propertySet, propertyCount, prop->id);
if (!pentry)
CrashAtUnhandlableOOM("OOM in TypeObject::sweep");
*pentry = newProp;
newProp->types.sweep(zone());
}
}
setBasePropertyCount(propertyCount);
@ -4254,12 +4115,11 @@ TypeObject::sweep(FreeOp *fop)
clearProperties();
} else {
Property *newProp = typeLifoAlloc.new_<Property>(*prop);
if (newProp) {
propertySet = (Property **) newProp;
newProp->types.sweep(zone());
} else {
zone()->types.setPendingNukeTypes();
}
if (!newProp)
CrashAtUnhandlableOOM("OOM in TypeObject::sweep");
propertySet = (Property **) newProp;
newProp->types.sweep(zone());
}
}
@ -4449,7 +4309,6 @@ TypeZone::TypeZone(Zone *zone)
typeLifoAlloc(TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
compilerOutputs(nullptr),
pendingRecompiles(nullptr),
pendingNukeTypes(false),
inferenceEnabled(false)
{
}

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

@ -242,6 +242,10 @@ class Type
return data > JSVAL_TYPE_UNKNOWN;
}
bool isObjectUnchecked() const {
return data > JSVAL_TYPE_UNKNOWN;
}
inline TypeObjectKey *objectKey() const;
/* Accessors for JSObject types */
@ -335,7 +339,7 @@ public:
* If the data this constraint refers to is still live, copy it into the
* zone's new allocator. Type constraints only hold weak references.
*/
virtual TypeConstraint *sweep(TypeZone &zone) = 0;
virtual bool sweep(TypeZone &zone, TypeConstraint **res) = 0;
};
/* Flags and other state stored in TypeSet::flags */
@ -528,7 +532,7 @@ class TypeSet
static TemporaryTypeSet *unionSets(TypeSet *a, TypeSet *b, LifoAlloc *alloc);
/* Add a type to this set using the specified allocator. */
inline bool addType(Type type, LifoAlloc *alloc);
inline void addType(Type type, LifoAlloc *alloc);
/* Get a list of all types in this set. */
typedef Vector<Type, 1, SystemAllocPolicy> TypeList;
@ -543,7 +547,6 @@ class TypeSet
inline TypeObjectKey *getObject(unsigned i) const;
inline JSObject *getSingleObject(unsigned i) const;
inline TypeObject *getTypeObject(unsigned i) const;
inline bool getTypeOrSingleObject(JSContext *cx, unsigned i, TypeObject **obj) const;
/* The Class of an object in this set. */
inline const Class *getObjectClass(unsigned i) const;
@ -568,7 +571,7 @@ class TypeSet
bool isSubset(TypeSet *other);
/* Forward all types in this set to the specified constraint. */
void addTypesToConstraint(JSContext *cx, TypeConstraint *constraint);
bool addTypesToConstraint(JSContext *cx, TypeConstraint *constraint);
// Clone a type set into an arbitrary allocator.
TemporaryTypeSet *clone(LifoAlloc *alloc) const;
@ -599,7 +602,7 @@ class ConstraintTypeSet : public TypeSet
inline void addType(ExclusiveContext *cx, Type type);
/* Add a new constraint to this set. */
void add(JSContext *cx, TypeConstraint *constraint, bool callExisting = true);
bool addConstraint(JSContext *cx, TypeConstraint *constraint, bool callExisting = true);
inline void sweep(JS::Zone *zone);
};
@ -739,7 +742,7 @@ class TemporaryTypeSet : public TypeSet
bool
AddClearDefiniteGetterSetterForPrototypeChain(JSContext *cx, TypeObject *type, HandleId id);
void
bool
AddClearDefiniteFunctionUsesInScript(JSContext *cx, TypeObject *type,
JSScript *script, JSScript *calleeScript);
@ -1108,8 +1111,6 @@ struct TypeObject : gc::BarrieredCell<TypeObject>
void addPrototype(JSContext *cx, TypeObject *proto);
void addPropertyType(ExclusiveContext *cx, jsid id, Type type);
void addPropertyType(ExclusiveContext *cx, jsid id, const Value &value);
void addPropertyType(ExclusiveContext *cx, const char *name, Type type);
void addPropertyType(ExclusiveContext *cx, const char *name, const Value &value);
void markPropertyNonData(ExclusiveContext *cx, jsid id);
void markPropertyNonWritable(ExclusiveContext *cx, jsid id);
void markStateChange(ExclusiveContext *cx);
@ -1527,9 +1528,6 @@ struct TypeCompartment
/* Get or make an object for an allocation site, and add to the allocation site table. */
TypeObject *addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey key);
/* Mark all types as needing destruction once inference has 'finished'. */
void setPendingNukeTypes(ExclusiveContext *cx);
/* Mark any type set containing obj as having a generic object type. */
void markSetsUnknown(JSContext *cx, TypeObject *obj);
@ -1562,12 +1560,6 @@ struct TypeZone
/* Pending recompilations to perform before execution of JIT code can resume. */
Vector<RecompileInfo> *pendingRecompiles;
/*
* Bit set if all current types must be marked as unknown, and all scripts
* recompiled. Caused by OOM failure within inference operations.
*/
bool pendingNukeTypes;
/* Whether type inference is enabled in this compartment. */
bool inferenceEnabled;
@ -1579,16 +1571,11 @@ struct TypeZone
void sweep(FreeOp *fop, bool releaseTypes);
/* Mark all types as needing destruction once inference has 'finished'. */
void setPendingNukeTypes();
/* Mark a script as needing recompilation once inference has finished. */
void addPendingRecompile(JSContext *cx, const RecompileInfo &info);
void addPendingRecompile(JSContext *cx, JSScript *script);
void processPendingRecompiles(FreeOp *fop);
void nukeTypes(FreeOp *fop);
};
enum SpewChannel {

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

@ -247,9 +247,7 @@ struct AutoEnterAnalysis
*/
if (!compartment->activeAnalysis) {
TypeZone &types = compartment->zone()->types;
if (types.pendingNukeTypes)
types.nukeTypes(freeOp);
else if (types.pendingRecompiles)
if (types.pendingRecompiles)
types.processPendingRecompiles(freeOp);
}
}
@ -385,12 +383,11 @@ EnsureTrackPropertyTypes(JSContext *cx, JSObject *obj, jsid id)
if (obj->hasSingletonType()) {
AutoEnterAnalysis enter(cx);
if (obj->hasLazyType() && !obj->getType(cx)) {
cx->compartment()->types.setPendingNukeTypes(cx);
cx->clearPendingException();
CrashAtUnhandlableOOM("Could not allocate TypeObject in EnsureTrackPropertyTypes");
return;
}
if (!obj->type()->unknownProperties() && !obj->type()->getProperty(cx, id)) {
cx->compartment()->types.setPendingNukeTypes(cx);
obj->type()->markUnknown(cx);
return;
}
}
@ -450,17 +447,17 @@ AddTypePropertyId(ExclusiveContext *cx, JSObject *obj, jsid id, const Value &val
}
inline void
AddTypeProperty(ExclusiveContext *cx, TypeObject *obj, const char *name, Type type)
AddTypeProperty(ExclusiveContext *cx, TypeObject *obj, jsid id, Type type)
{
if (cx->typeInferenceEnabled() && !obj->unknownProperties())
obj->addPropertyType(cx, name, type);
obj->addPropertyType(cx, id, type);
}
inline void
AddTypeProperty(ExclusiveContext *cx, TypeObject *obj, const char *name, const Value &value)
AddTypeProperty(ExclusiveContext *cx, TypeObject *obj, jsid id, const Value &value)
{
if (cx->typeInferenceEnabled() && !obj->unknownProperties())
obj->addPropertyType(cx, name, value);
obj->addPropertyType(cx, id, value);
}
/* Set one or more dynamic flags on a type object. */
@ -1055,34 +1052,34 @@ TypeSet::clearObjects()
objectSet = nullptr;
}
bool
void
TypeSet::addType(Type type, LifoAlloc *alloc)
{
if (unknown())
return true;
return;
if (type.isUnknown()) {
flags |= TYPE_FLAG_BASE_MASK;
clearObjects();
JS_ASSERT(unknown());
return true;
return;
}
if (type.isPrimitive()) {
TypeFlags flag = PrimitiveTypeFlag(type.primitive());
if (flags & flag)
return true;
return;
/* If we add float to a type set it is also considered to contain int. */
if (flag == TYPE_FLAG_DOUBLE)
flag |= TYPE_FLAG_INT32;
flags |= flag;
return true;
return;
}
if (flags & TYPE_FLAG_ANYOBJECT)
return true;
return;
if (type.isAnyObject())
goto unknownObject;
@ -1092,9 +1089,9 @@ TypeSet::addType(Type type, LifoAlloc *alloc)
TypeObjectKey **pentry = HashSetInsert<TypeObjectKey *,TypeObjectKey,TypeObjectKey>
(*alloc, objectSet, objectCount, object);
if (!pentry)
return false;
goto unknownObject;
if (*pentry)
return true;
return;
*pentry = object;
setBaseObjectCount(objectCount);
@ -1116,8 +1113,6 @@ TypeSet::addType(Type type, LifoAlloc *alloc)
flags |= TYPE_FLAG_ANYOBJECT;
clearObjects();
}
return true;
}
inline void
@ -1128,10 +1123,10 @@ ConstraintTypeSet::addType(ExclusiveContext *cxArg, Type type)
if (hasType(type))
return;
if (!TypeSet::addType(type, &cxArg->typeLifoAlloc())) {
cxArg->compartment()->types.setPendingNukeTypes(cxArg);
return;
}
TypeSet::addType(type, &cxArg->typeLifoAlloc());
if (type.isObjectUnchecked() && unknownObject())
type = Type::AnyObjectType();
InferSpew(ISpewOps, "addType: %sT%p%s %s",
InferSpewColor(this), this, InferSpewColorReset(),
@ -1219,30 +1214,6 @@ TypeSet::getTypeObject(unsigned i) const
return (key && key->isTypeObject()) ? key->asTypeObject() : nullptr;
}
inline bool
TypeSet::getTypeOrSingleObject(JSContext *cx, unsigned i, TypeObject **result) const
{
JS_ASSERT(result);
JS_ASSERT(cx->compartment()->activeAnalysis);
*result = nullptr;
TypeObject *type = getTypeObject(i);
if (!type) {
JSObject *singleton = getSingleObject(i);
if (!singleton)
return true;
type = singleton->uninlinedGetType(cx);
if (!type) {
cx->compartment()->types.setPendingNukeTypes(cx);
return false;
}
}
*result = type;
return true;
}
inline const Class *
TypeSet::getObjectClass(unsigned i) const
{
@ -1302,7 +1273,7 @@ TypeObject::getProperty(ExclusiveContext *cx, jsid id)
Property **pprop = HashSetInsert<jsid,Property,Property>
(cx->typeLifoAlloc(), propertySet, propertyCount, id);
if (!pprop) {
cx->compartment()->types.setPendingNukeTypes(cx);
markUnknown(cx);
return nullptr;
}
@ -1310,8 +1281,7 @@ TypeObject::getProperty(ExclusiveContext *cx, jsid id)
setBasePropertyCount(propertyCount);
if (!addProperty(cx, id, pprop)) {
setBasePropertyCount(0);
propertySet = nullptr;
markUnknown(cx);
return nullptr;
}

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

@ -3209,7 +3209,7 @@ SplitHelper(JSContext *cx, Handle<JSLinearString*> str, uint32_t limit, const Ma
return nullptr;
} else {
/* Only string entries have been accounted for so far. */
AddTypeProperty(cx, type, nullptr, UndefinedValue());
AddTypeProperty(cx, type, JSID_VOID, UndefinedValue());
if (!splits.append(UndefinedValue()))
return nullptr;
}
@ -3344,7 +3344,7 @@ js::str_split(JSContext *cx, unsigned argc, Value *vp)
RootedTypeObject type(cx, GetTypeCallerInitObject(cx, JSProto_Array));
if (!type)
return false;
AddTypeProperty(cx, type, nullptr, Type::StringType());
AddTypeProperty(cx, type, JSID_VOID, Type::StringType());
/* Step 5: Use the second argument as the split limit, if given. */
uint32_t limit;