зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1491736 - Split out type-set classes and related data types from vm/TypeInference.h into vm/TypeSet.h, and move TypeNewScript from vm/TypeInference.h to vm/TypeInference-inl.h, so code can use TypeSet types without needing JSFunction, Shape, and other super-complex types as well (via HeapPtr<T*> fields in TypeNewScript). r=jandem
--HG-- rename : js/src/vm/TypeInference.h => js/src/vm/TypeSet.h extra : rebase_source : 330bc27597e8a178129988d973ba4724450a43c6
This commit is contained in:
Родитель
aec3d8264b
Коммит
c2719f1ecf
|
@ -11,6 +11,7 @@
|
|||
#include "mozilla/TimeStamp.h"
|
||||
|
||||
#include "threading/ConditionVariable.h"
|
||||
#include "threading/ProtectedData.h" // js::ThreadData
|
||||
#include "vm/JSObject.h"
|
||||
#include "vm/MutexIDs.h"
|
||||
#include "vm/NativeObject.h"
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "js/GCHashTable.h"
|
||||
#include "vm/MallocProvider.h"
|
||||
#include "vm/Runtime.h"
|
||||
#include "vm/TypeInference.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "vm/EnvironmentObject-inl.h"
|
||||
#include "vm/JSContext-inl.h"
|
||||
#include "vm/JSObject-inl.h"
|
||||
#include "vm/TypeInference-inl.h"
|
||||
#include "vm/UnboxedObject-inl.h"
|
||||
|
||||
using namespace js;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "vm/BytecodeUtil-inl.h"
|
||||
#include "vm/JSObject-inl.h"
|
||||
#include "vm/JSScript-inl.h"
|
||||
#include "vm/TypeInference-inl.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
|
@ -4367,7 +4368,7 @@ static bool
|
|||
AnalyzePoppedThis(JSContext* cx, ObjectGroup* group,
|
||||
MDefinition* thisValue, MInstruction* ins, bool definitelyExecuted,
|
||||
HandlePlainObject baseobj,
|
||||
Vector<TypeNewScript::Initializer>* initializerList,
|
||||
Vector<TypeNewScriptInitializer>* initializerList,
|
||||
Vector<PropertyName*>* accessedProperties,
|
||||
bool* phandled)
|
||||
{
|
||||
|
@ -4436,16 +4437,16 @@ AnalyzePoppedThis(JSContext* cx, ObjectGroup* group,
|
|||
for (int i = callerResumePoints.length() - 1; i >= 0; i--) {
|
||||
MResumePoint* rp = callerResumePoints[i];
|
||||
JSScript* script = rp->block()->info().script();
|
||||
TypeNewScript::Initializer entry(TypeNewScript::Initializer::SETPROP_FRAME,
|
||||
script->pcToOffset(rp->pc()));
|
||||
TypeNewScriptInitializer entry(TypeNewScriptInitializer::SETPROP_FRAME,
|
||||
script->pcToOffset(rp->pc()));
|
||||
if (!initializerList->append(entry)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
JSScript* script = ins->block()->info().script();
|
||||
TypeNewScript::Initializer entry(TypeNewScript::Initializer::SETPROP,
|
||||
script->pcToOffset(setprop->resumePoint()->pc()));
|
||||
TypeNewScriptInitializer entry(TypeNewScriptInitializer::SETPROP,
|
||||
script->pcToOffset(setprop->resumePoint()->pc()));
|
||||
if (!initializerList->append(entry)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -4501,7 +4502,7 @@ CmpInstructions(const void* a, const void* b)
|
|||
bool
|
||||
jit::AnalyzeNewScriptDefiniteProperties(JSContext* cx, HandleFunction fun,
|
||||
ObjectGroup* group, HandlePlainObject baseobj,
|
||||
Vector<TypeNewScript::Initializer>* initializerList)
|
||||
Vector<TypeNewScriptInitializer>* initializerList)
|
||||
{
|
||||
MOZ_ASSERT(cx->zone()->types.activeAnalysis);
|
||||
|
||||
|
|
|
@ -203,7 +203,7 @@ ConvertLinearInequality(TempAllocator& alloc, MBasicBlock* block, const LinearSu
|
|||
MOZ_MUST_USE bool
|
||||
AnalyzeNewScriptDefiniteProperties(JSContext* cx, HandleFunction fun,
|
||||
ObjectGroup* group, HandlePlainObject baseobj,
|
||||
Vector<TypeNewScript::Initializer>* initializerList);
|
||||
Vector<TypeNewScriptInitializer>* initializerList);
|
||||
|
||||
MOZ_MUST_USE bool
|
||||
AnalyzeArgumentsUsage(JSContext* cx, JSScript* script);
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "vm/StringType.h"
|
||||
|
||||
#include "vm/JSObject-inl.h"
|
||||
#include "vm/ObjectGroup-inl.h"
|
||||
#include "vm/TypeInference-inl.h"
|
||||
|
||||
namespace js {
|
||||
|
|
|
@ -69,10 +69,12 @@
|
|||
#include "vm/JSFunction-inl.h"
|
||||
#include "vm/NativeObject-inl.h"
|
||||
#include "vm/NumberObject-inl.h"
|
||||
#include "vm/ObjectGroup-inl.h"
|
||||
#include "vm/Realm-inl.h"
|
||||
#include "vm/Shape-inl.h"
|
||||
#include "vm/StringObject-inl.h"
|
||||
#include "vm/TypedArrayObject-inl.h"
|
||||
#include "vm/TypeInference-inl.h"
|
||||
#include "vm/UnboxedObject-inl.h"
|
||||
|
||||
using namespace js;
|
||||
|
|
|
@ -49,6 +49,8 @@ namespace jit {
|
|||
|
||||
# define BASELINE_DISABLED_SCRIPT ((js::jit::BaselineScript*)0x1)
|
||||
|
||||
class AutoKeepTypeScripts;
|
||||
class AutoSweepTypeScript;
|
||||
class BreakpointSite;
|
||||
class Debugger;
|
||||
class LazyScript;
|
||||
|
@ -56,6 +58,7 @@ class ModuleObject;
|
|||
class RegExpObject;
|
||||
class SourceCompressionTask;
|
||||
class Shape;
|
||||
class TypeScript;
|
||||
|
||||
namespace frontend {
|
||||
struct BytecodeEmitter;
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "vm/EnvironmentObject-inl.h"
|
||||
#include "vm/JSObject-inl.h"
|
||||
#include "vm/Shape-inl.h"
|
||||
#include "vm/TypeInference-inl.h"
|
||||
#include "vm/UnboxedObject-inl.h"
|
||||
|
||||
using namespace js;
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "vm/TaggedProto.h"
|
||||
|
||||
#include "gc/Marking-inl.h"
|
||||
#include "vm/TypeInference-inl.h"
|
||||
#include "vm/UnboxedObject-inl.h"
|
||||
|
||||
using namespace js;
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#include "js/GCHashTable.h"
|
||||
#include "js/TypeDecls.h"
|
||||
#include "vm/TaggedProto.h"
|
||||
#include "vm/TypeInference.h"
|
||||
#include "vm/TypeSet.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
|
@ -25,7 +25,6 @@ class UnboxedLayout;
|
|||
|
||||
class PreliminaryObjectArrayWithTemplate;
|
||||
class TypeNewScript;
|
||||
class HeapTypeSet;
|
||||
class AutoClearTypeInferenceStateOnOOM;
|
||||
class AutoSweepObjectGroup;
|
||||
class CompilerConstraintList;
|
||||
|
@ -326,10 +325,7 @@ class ObjectGroup : public gc::TenuredCell
|
|||
setAddendum(Addendum_None, nullptr);
|
||||
}
|
||||
|
||||
bool hasUnanalyzedPreliminaryObjects() {
|
||||
return (newScriptDontCheckGeneration() && !newScriptDontCheckGeneration()->analyzed()) ||
|
||||
maybePreliminaryObjectsDontCheckGeneration();
|
||||
}
|
||||
inline bool hasUnanalyzedPreliminaryObjects();
|
||||
|
||||
inline UnboxedLayout* maybeUnboxedLayout(const AutoSweepObjectGroup& sweep);
|
||||
inline UnboxedLayout& unboxedLayout(const AutoSweepObjectGroup& sweep);
|
||||
|
|
|
@ -18,9 +18,14 @@
|
|||
#include "builtin/Symbol.h"
|
||||
#include "gc/GC.h"
|
||||
#include "jit/BaselineJIT.h"
|
||||
#include "js/HeapAPI.h"
|
||||
#include "vm/ArrayObject.h"
|
||||
#include "vm/BooleanObject.h"
|
||||
#include "vm/JSFunction.h"
|
||||
#include "vm/NativeObject.h"
|
||||
#include "vm/NumberObject.h"
|
||||
#include "vm/ObjectGroup.h"
|
||||
#include "vm/Shape.h"
|
||||
#include "vm/SharedArrayObject.h"
|
||||
#include "vm/StringObject.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
|
@ -268,6 +273,161 @@ TypeIdString(jsid id)
|
|||
#endif
|
||||
}
|
||||
|
||||
// New script properties analyses overview.
|
||||
//
|
||||
// When constructing objects using 'new' on a script, we attempt to determine
|
||||
// the properties which that object will eventually have. This is done via two
|
||||
// analyses. One of these, the definite properties analysis, is static, and the
|
||||
// other, the acquired properties analysis, is dynamic. As objects are
|
||||
// constructed using 'new' on some script to create objects of group G, our
|
||||
// analysis strategy is as follows:
|
||||
//
|
||||
// - When the first objects are created, no analysis is immediately performed.
|
||||
// Instead, all objects of group G are accumulated in an array.
|
||||
//
|
||||
// - After a certain number of such objects have been created, the definite
|
||||
// properties analysis is performed. This analyzes the body of the
|
||||
// constructor script and any other functions it calls to look for properties
|
||||
// which will definitely be added by the constructor in a particular order,
|
||||
// creating an object with shape S.
|
||||
//
|
||||
// - The properties in S are compared with the greatest common prefix P of the
|
||||
// shapes of the objects that have been created. If P has more properties
|
||||
// than S, the acquired properties analysis is performed.
|
||||
//
|
||||
// - The acquired properties analysis marks all properties in P as definite
|
||||
// in G, and creates a new group IG for objects which are partially
|
||||
// initialized. Objects of group IG are initially created with shape S, and if
|
||||
// they are later given shape P, their group can be changed to G.
|
||||
//
|
||||
// For objects which are rarely created, the definite properties analysis can
|
||||
// be triggered after only one or a few objects have been allocated, when code
|
||||
// being Ion compiled might access them. In this case type information in the
|
||||
// constructor might not be good enough for the definite properties analysis to
|
||||
// compute useful information, but the acquired properties analysis will still
|
||||
// be able to identify definite properties in this case.
|
||||
//
|
||||
// This layered approach is designed to maximize performance on easily
|
||||
// analyzable code, while still allowing us to determine definite properties
|
||||
// robustly when code consistently adds the same properties to objects, but in
|
||||
// complex ways which can't be understood statically.
|
||||
class TypeNewScript
|
||||
{
|
||||
private:
|
||||
// Scripted function which this information was computed for.
|
||||
HeapPtr<JSFunction*> function_ = {};
|
||||
|
||||
// Any preliminary objects with the type. The analyses are not performed
|
||||
// until this array is cleared.
|
||||
PreliminaryObjectArray* preliminaryObjects = nullptr;
|
||||
|
||||
// After the new script properties analyses have been performed, a template
|
||||
// object to use for newly constructed objects. The shape of this object
|
||||
// reflects all definite properties the object will have, and the
|
||||
// allocation kind to use. This is null if the new objects have an unboxed
|
||||
// layout, in which case the UnboxedLayout provides the initial structure
|
||||
// of the object.
|
||||
HeapPtr<PlainObject*> templateObject_ = {};
|
||||
|
||||
// Order in which definite properties become initialized. We need this in
|
||||
// case the definite properties are invalidated (such as by adding a setter
|
||||
// to an object on the prototype chain) while an object is in the middle of
|
||||
// being initialized, so we can walk the stack and fixup any objects which
|
||||
// look for in-progress objects which were prematurely set with an incorrect
|
||||
// shape. Property assignments in inner frames are preceded by a series of
|
||||
// SETPROP_FRAME entries specifying the stack down to the frame containing
|
||||
// the write.
|
||||
TypeNewScriptInitializer* initializerList = nullptr;
|
||||
|
||||
// If there are additional properties found by the acquired properties
|
||||
// analysis which were not found by the definite properties analysis, this
|
||||
// shape contains all such additional properties (plus the definite
|
||||
// properties). When an object of this group acquires this shape, it is
|
||||
// fully initialized and its group can be changed to initializedGroup.
|
||||
HeapPtr<Shape*> initializedShape_ = {};
|
||||
|
||||
// Group with definite properties set for all properties found by
|
||||
// both the definite and acquired properties analyses.
|
||||
HeapPtr<ObjectGroup*> initializedGroup_ = {};
|
||||
|
||||
public:
|
||||
TypeNewScript() = default;
|
||||
|
||||
~TypeNewScript() {
|
||||
js_delete(preliminaryObjects);
|
||||
js_free(initializerList);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
function_ = nullptr;
|
||||
templateObject_ = nullptr;
|
||||
initializedShape_ = nullptr;
|
||||
initializedGroup_ = nullptr;
|
||||
}
|
||||
|
||||
static void writeBarrierPre(TypeNewScript* newScript);
|
||||
|
||||
bool analyzed() const {
|
||||
return preliminaryObjects == nullptr;
|
||||
}
|
||||
|
||||
PlainObject* templateObject() const {
|
||||
return templateObject_;
|
||||
}
|
||||
|
||||
Shape* initializedShape() const {
|
||||
return initializedShape_;
|
||||
}
|
||||
|
||||
ObjectGroup* initializedGroup() const {
|
||||
return initializedGroup_;
|
||||
}
|
||||
|
||||
JSFunction* function() const {
|
||||
return function_;
|
||||
}
|
||||
|
||||
void trace(JSTracer* trc);
|
||||
void sweep();
|
||||
|
||||
void registerNewObject(PlainObject* res);
|
||||
bool maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate, bool force = false);
|
||||
|
||||
bool rollbackPartiallyInitializedObjects(JSContext* cx, ObjectGroup* group);
|
||||
|
||||
static bool make(JSContext* cx, ObjectGroup* group, JSFunction* fun);
|
||||
static TypeNewScript* makeNativeVersion(JSContext* cx, TypeNewScript* newScript,
|
||||
PlainObject* templateObject);
|
||||
|
||||
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
|
||||
|
||||
static size_t offsetOfPreliminaryObjects() {
|
||||
return offsetof(TypeNewScript, preliminaryObjects);
|
||||
}
|
||||
};
|
||||
|
||||
inline
|
||||
UnboxedLayout::~UnboxedLayout()
|
||||
{
|
||||
if (newScript_) {
|
||||
newScript_->clear();
|
||||
}
|
||||
js_delete(newScript_);
|
||||
js_free(traceList_);
|
||||
|
||||
nativeGroup_.init(nullptr);
|
||||
nativeShape_.init(nullptr);
|
||||
replacementGroup_.init(nullptr);
|
||||
constructorCode_.init(nullptr);
|
||||
}
|
||||
|
||||
inline bool
|
||||
ObjectGroup::hasUnanalyzedPreliminaryObjects()
|
||||
{
|
||||
return (newScriptDontCheckGeneration() && !newScriptDontCheckGeneration()->analyzed()) ||
|
||||
maybePreliminaryObjectsDontCheckGeneration();
|
||||
}
|
||||
|
||||
/*
|
||||
* Structure for type inference entry point functions. All functions which can
|
||||
* change type information must use this, and functions which depend on
|
||||
|
|
|
@ -4065,11 +4065,14 @@ TypeNewScript::makeNativeVersion(JSContext* cx, TypeNewScript* newScript,
|
|||
nativeNewScript->function_ = newScript->function();
|
||||
nativeNewScript->templateObject_ = templateObject;
|
||||
|
||||
Initializer* cursor = newScript->initializerList;
|
||||
while (cursor->kind != Initializer::DONE) { cursor++; }
|
||||
TypeNewScriptInitializer* cursor = newScript->initializerList;
|
||||
while (cursor->kind != TypeNewScriptInitializer::DONE) {
|
||||
cursor++;
|
||||
}
|
||||
|
||||
size_t initializerLength = cursor - newScript->initializerList + 1;
|
||||
|
||||
nativeNewScript->initializerList = cx->pod_calloc<Initializer>(initializerLength);
|
||||
nativeNewScript->initializerList = cx->pod_calloc<TypeNewScriptInitializer>(initializerLength);
|
||||
if (!nativeNewScript->initializerList) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -4232,7 +4235,7 @@ TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate,
|
|||
return false;
|
||||
}
|
||||
|
||||
Vector<Initializer> initializerVector(cx);
|
||||
Vector<TypeNewScriptInitializer> initializerVector(cx);
|
||||
|
||||
RootedPlainObject templateRoot(cx, templateObject());
|
||||
RootedFunction fun(cx, function());
|
||||
|
@ -4277,13 +4280,14 @@ TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate,
|
|||
}
|
||||
}
|
||||
|
||||
Initializer done(Initializer::DONE, 0);
|
||||
TypeNewScriptInitializer done(TypeNewScriptInitializer::DONE, 0);
|
||||
|
||||
if (!initializerVector.append(done)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
initializerList = group->zone()->pod_calloc<Initializer>(initializerVector.length());
|
||||
initializerList =
|
||||
group->zone()->pod_calloc<TypeNewScriptInitializer>(initializerVector.length());
|
||||
if (!initializerList) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
|
@ -4444,8 +4448,8 @@ TypeNewScript::rollbackPartiallyInitializedObjects(JSContext* cx, ObjectGroup* g
|
|||
// Index in pcOffsets of the frame currently being checked for a SETPROP.
|
||||
int setpropDepth = callDepth;
|
||||
|
||||
for (Initializer* init = initializerList;; init++) {
|
||||
if (init->kind == Initializer::SETPROP) {
|
||||
for (TypeNewScriptInitializer* init = initializerList; ; init++) {
|
||||
if (init->kind == TypeNewScriptInitializer::SETPROP) {
|
||||
if (!pastProperty && pcOffsets[setpropDepth] < init->offset) {
|
||||
// Have not yet reached this setprop.
|
||||
break;
|
||||
|
@ -4454,7 +4458,7 @@ TypeNewScript::rollbackPartiallyInitializedObjects(JSContext* cx, ObjectGroup* g
|
|||
numProperties++;
|
||||
pastProperty = false;
|
||||
setpropDepth = callDepth;
|
||||
} else if (init->kind == Initializer::SETPROP_FRAME) {
|
||||
} else if (init->kind == TypeNewScriptInitializer::SETPROP_FRAME) {
|
||||
if (!pastProperty) {
|
||||
if (pcOffsets[setpropDepth] < init->offset) {
|
||||
// Have not yet reached this inner call.
|
||||
|
@ -4471,7 +4475,7 @@ TypeNewScript::rollbackPartiallyInitializedObjects(JSContext* cx, ObjectGroup* g
|
|||
}
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(init->kind == Initializer::DONE);
|
||||
MOZ_ASSERT(init->kind == TypeNewScriptInitializer::DONE);
|
||||
finished = true;
|
||||
break;
|
||||
}
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,973 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* TypeSet classes and functions. */
|
||||
|
||||
#ifndef vm_TypeSet_h
|
||||
#define vm_TypeSet_h
|
||||
|
||||
#include "mozilla/Assertions.h" // MOZ_ASSERT
|
||||
#include "mozilla/Attributes.h" // MOZ_ALWAYS_INLINE
|
||||
#include "mozilla/Likely.h" // MOZ_UNLIKELY
|
||||
|
||||
#include <stdint.h> // intptr_t, uintptr_t, uint8_t, uint32_t
|
||||
#include <stdio.h> // FILE
|
||||
|
||||
#include "jstypes.h" // JS_BITS_PER_WORD
|
||||
#include "jsutil.h" // JS_CRASH_DIAGNOSTICS
|
||||
|
||||
#include "jit/IonTypes.h" // jit::MIRType
|
||||
#include "js/GCAnnotations.h" // JS_HAZ_GC_POINTER
|
||||
#include "js/TracingAPI.h" // JSTracer
|
||||
#include "js/TypeDecls.h" // IF_BIGINT
|
||||
#include "js/Utility.h" // UniqueChars
|
||||
#include "js/Value.h" // JSVAL_TYPE_*
|
||||
#include "js/Vector.h" // js::Vector
|
||||
#include "vm/TaggedProto.h" // js::TaggedProto
|
||||
|
||||
struct JSContext;
|
||||
struct jsid;
|
||||
class JSObject;
|
||||
|
||||
namespace JS {
|
||||
|
||||
class Compartment;
|
||||
class Realm;
|
||||
class Zone;
|
||||
|
||||
} // namespace JS
|
||||
|
||||
namespace js {
|
||||
|
||||
namespace jit {
|
||||
|
||||
struct IonScript;
|
||||
class TempAllocator;
|
||||
|
||||
} // namespace jit
|
||||
|
||||
class AutoClearTypeInferenceStateOnOOM;
|
||||
class AutoSweepBase;
|
||||
class AutoSweepObjectGroup;
|
||||
struct Class;
|
||||
class CompilerConstraintList;
|
||||
class HeapTypeSetKey;
|
||||
class LifoAlloc;
|
||||
class ObjectGroup;
|
||||
class SystemAllocPolicy;
|
||||
class TypeConstraint;
|
||||
class TypeNewScript;
|
||||
class TypeZone;
|
||||
|
||||
/*
|
||||
* Type inference memory management overview.
|
||||
*
|
||||
* Type information about the values observed within scripts and about the
|
||||
* contents of the heap is accumulated as the program executes. Compilation
|
||||
* accumulates constraints relating type information on the heap with the
|
||||
* compilations that should be invalidated when those types change. Type
|
||||
* information and constraints are allocated in the zone's typeLifoAlloc,
|
||||
* and on GC all data referring to live things is copied into a new allocator.
|
||||
* Thus, type set and constraints only hold weak references.
|
||||
*/
|
||||
|
||||
/* Flags and other state stored in TypeSet::flags */
|
||||
enum : uint32_t
|
||||
{
|
||||
TYPE_FLAG_UNDEFINED = 0x1,
|
||||
TYPE_FLAG_NULL = 0x2,
|
||||
TYPE_FLAG_BOOLEAN = 0x4,
|
||||
TYPE_FLAG_INT32 = 0x8,
|
||||
TYPE_FLAG_DOUBLE = 0x10,
|
||||
TYPE_FLAG_STRING = 0x20,
|
||||
TYPE_FLAG_SYMBOL = 0x40,
|
||||
#ifdef ENABLE_BIGINT
|
||||
TYPE_FLAG_BIGINT = 0x80,
|
||||
TYPE_FLAG_LAZYARGS = 0x100,
|
||||
TYPE_FLAG_ANYOBJECT = 0x200,
|
||||
#else
|
||||
TYPE_FLAG_LAZYARGS = 0x80,
|
||||
TYPE_FLAG_ANYOBJECT = 0x100,
|
||||
#endif
|
||||
|
||||
/* Mask containing all primitives */
|
||||
TYPE_FLAG_PRIMITIVE = TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL | TYPE_FLAG_BOOLEAN |
|
||||
TYPE_FLAG_INT32 | TYPE_FLAG_DOUBLE | TYPE_FLAG_STRING |
|
||||
TYPE_FLAG_SYMBOL |
|
||||
IF_BIGINT(TYPE_FLAG_BIGINT, 0),
|
||||
|
||||
/* Mask/shift for the number of objects in objectSet */
|
||||
#ifdef ENABLE_BIGINT
|
||||
TYPE_FLAG_OBJECT_COUNT_MASK = 0x3c00,
|
||||
TYPE_FLAG_OBJECT_COUNT_SHIFT = 10,
|
||||
#else
|
||||
TYPE_FLAG_OBJECT_COUNT_MASK = 0x3e00,
|
||||
TYPE_FLAG_OBJECT_COUNT_SHIFT = 9,
|
||||
#endif
|
||||
TYPE_FLAG_OBJECT_COUNT_LIMIT = 7,
|
||||
TYPE_FLAG_DOMOBJECT_COUNT_LIMIT =
|
||||
TYPE_FLAG_OBJECT_COUNT_MASK >> TYPE_FLAG_OBJECT_COUNT_SHIFT,
|
||||
|
||||
/* Whether the contents of this type set are totally unknown. */
|
||||
TYPE_FLAG_UNKNOWN = 0x00004000,
|
||||
|
||||
/* Mask of normal type flags on a type set. */
|
||||
TYPE_FLAG_BASE_MASK = TYPE_FLAG_PRIMITIVE | TYPE_FLAG_LAZYARGS | TYPE_FLAG_ANYOBJECT | TYPE_FLAG_UNKNOWN,
|
||||
|
||||
/* Additional flags for HeapTypeSet sets. */
|
||||
|
||||
/*
|
||||
* Whether the property has ever been deleted or reconfigured to behave
|
||||
* differently from a plain data property, other than making the property
|
||||
* non-writable.
|
||||
*/
|
||||
TYPE_FLAG_NON_DATA_PROPERTY = 0x00008000,
|
||||
|
||||
/* Whether the property has ever been made non-writable. */
|
||||
TYPE_FLAG_NON_WRITABLE_PROPERTY = 0x00010000,
|
||||
|
||||
/* Whether the property might not be constant. */
|
||||
TYPE_FLAG_NON_CONSTANT_PROPERTY = 0x00020000,
|
||||
|
||||
/*
|
||||
* Whether the property is definitely in a particular slot on all objects
|
||||
* from which it has not been deleted or reconfigured. For singletons
|
||||
* this may be a fixed or dynamic slot, and for other objects this will be
|
||||
* a fixed slot.
|
||||
*
|
||||
* If the property is definite, mask and shift storing the slot + 1.
|
||||
* Otherwise these bits are clear.
|
||||
*/
|
||||
TYPE_FLAG_DEFINITE_MASK = 0xfffc0000,
|
||||
TYPE_FLAG_DEFINITE_SHIFT = 18
|
||||
};
|
||||
using TypeFlags = uint32_t;
|
||||
|
||||
static_assert(TYPE_FLAG_PRIMITIVE < TYPE_FLAG_ANYOBJECT &&
|
||||
TYPE_FLAG_LAZYARGS < TYPE_FLAG_ANYOBJECT,
|
||||
"TYPE_FLAG_ANYOBJECT should be greater than primitive type flags");
|
||||
|
||||
/* Flags and other state stored in ObjectGroup::Flags */
|
||||
enum : uint32_t
|
||||
{
|
||||
/* Whether this group is associated with some allocation site. */
|
||||
OBJECT_FLAG_FROM_ALLOCATION_SITE = 0x1,
|
||||
|
||||
/* Whether this group is associated with a single object. */
|
||||
OBJECT_FLAG_SINGLETON = 0x2,
|
||||
|
||||
/*
|
||||
* Whether this group is used by objects whose singleton groups have not
|
||||
* been created yet.
|
||||
*/
|
||||
OBJECT_FLAG_LAZY_SINGLETON = 0x4,
|
||||
|
||||
/* Mask/shift for the number of properties in propertySet */
|
||||
OBJECT_FLAG_PROPERTY_COUNT_MASK = 0xfff8,
|
||||
OBJECT_FLAG_PROPERTY_COUNT_SHIFT = 3,
|
||||
OBJECT_FLAG_PROPERTY_COUNT_LIMIT =
|
||||
OBJECT_FLAG_PROPERTY_COUNT_MASK >> OBJECT_FLAG_PROPERTY_COUNT_SHIFT,
|
||||
|
||||
/* Whether any objects this represents may have sparse indexes. */
|
||||
OBJECT_FLAG_SPARSE_INDEXES = 0x00010000,
|
||||
|
||||
/* Whether any objects this represents may not have packed dense elements. */
|
||||
OBJECT_FLAG_NON_PACKED = 0x00020000,
|
||||
|
||||
/*
|
||||
* Whether any objects this represents may be arrays whose length does not
|
||||
* fit in an int32.
|
||||
*/
|
||||
OBJECT_FLAG_LENGTH_OVERFLOW = 0x00040000,
|
||||
|
||||
/* Whether any objects have been iterated over. */
|
||||
OBJECT_FLAG_ITERATED = 0x00080000,
|
||||
|
||||
/* Whether any object this represents may have non-extensible elements. */
|
||||
OBJECT_FLAG_NON_EXTENSIBLE_ELEMENTS = 0x00100000,
|
||||
|
||||
/*
|
||||
* For the function on a run-once script, whether the function has actually
|
||||
* run multiple times.
|
||||
*/
|
||||
OBJECT_FLAG_RUNONCE_INVALIDATED = 0x00200000,
|
||||
|
||||
/*
|
||||
* For a global object, whether any array buffers in this compartment with
|
||||
* typed object views have ever been detached.
|
||||
*/
|
||||
OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER = 0x00400000,
|
||||
|
||||
/*
|
||||
* Whether objects with this type should be allocated directly in the
|
||||
* tenured heap.
|
||||
*/
|
||||
OBJECT_FLAG_PRE_TENURE = 0x00800000,
|
||||
|
||||
/* Whether objects with this type might have copy on write elements. */
|
||||
OBJECT_FLAG_COPY_ON_WRITE = 0x01000000,
|
||||
|
||||
/* Whether this type has had its 'new' script cleared in the past. */
|
||||
OBJECT_FLAG_NEW_SCRIPT_CLEARED = 0x02000000,
|
||||
|
||||
/*
|
||||
* Whether all properties of this object are considered unknown.
|
||||
* If set, all other flags in DYNAMIC_MASK will also be set.
|
||||
*/
|
||||
OBJECT_FLAG_UNKNOWN_PROPERTIES = 0x04000000,
|
||||
|
||||
/* Flags which indicate dynamic properties of represented objects. */
|
||||
OBJECT_FLAG_DYNAMIC_MASK = 0x07ff0000,
|
||||
|
||||
// Mask/shift for the kind of addendum attached to this group.
|
||||
OBJECT_FLAG_ADDENDUM_MASK = 0x38000000,
|
||||
OBJECT_FLAG_ADDENDUM_SHIFT = 27,
|
||||
|
||||
// Mask/shift for this group's generation. If out of sync with the
|
||||
// TypeZone's generation, this group hasn't been swept yet.
|
||||
OBJECT_FLAG_GENERATION_MASK = 0x40000000,
|
||||
OBJECT_FLAG_GENERATION_SHIFT = 30,
|
||||
};
|
||||
using ObjectGroupFlags = uint32_t;
|
||||
|
||||
class StackTypeSet;
|
||||
class HeapTypeSet;
|
||||
class TemporaryTypeSet;
|
||||
|
||||
/*
|
||||
* [SMDOC] Type-Inference TypeSet
|
||||
*
|
||||
* Information about the set of types associated with an lvalue. There are
|
||||
* three kinds of type sets:
|
||||
*
|
||||
* - StackTypeSet are associated with TypeScripts, for arguments and values
|
||||
* observed at property reads. These are implicitly frozen on compilation
|
||||
* and only have constraints added to them which can trigger invalidation of
|
||||
* TypeNewScript information.
|
||||
*
|
||||
* - HeapTypeSet are associated with the properties of ObjectGroups. These
|
||||
* may have constraints added to them to trigger invalidation of either
|
||||
* compiled code or TypeNewScript information.
|
||||
*
|
||||
* - TemporaryTypeSet are created during compilation and do not outlive
|
||||
* that compilation.
|
||||
*
|
||||
* The contents of a type set completely describe the values that a particular
|
||||
* lvalue might have, except for the following cases:
|
||||
*
|
||||
* - If an object's prototype or class is dynamically mutated, its group will
|
||||
* change. Type sets containing the old group will not necessarily contain
|
||||
* the new group. When this occurs, the properties of the old and new group
|
||||
* will both be marked as unknown, which will prevent Ion from optimizing
|
||||
* based on the object's type information.
|
||||
*
|
||||
* - If an unboxed object is converted to a native object, its group will also
|
||||
* change and type sets containing the old group will not necessarily contain
|
||||
* the new group. Unlike the above case, this will not degrade property type
|
||||
* information, but Ion will no longer optimize unboxed objects with the old
|
||||
* group.
|
||||
*/
|
||||
class TypeSet
|
||||
{
|
||||
protected:
|
||||
/* Flags for this type set. */
|
||||
TypeFlags flags = 0;
|
||||
|
||||
public:
|
||||
class ObjectKey;
|
||||
|
||||
protected:
|
||||
/* Possible objects this type set can represent. */
|
||||
ObjectKey** objectSet = nullptr;
|
||||
|
||||
public:
|
||||
TypeSet() = default;
|
||||
|
||||
// Type set entry for either a JSObject with singleton type or a
|
||||
// non-singleton ObjectGroup.
|
||||
class ObjectKey
|
||||
{
|
||||
public:
|
||||
static intptr_t keyBits(ObjectKey* obj) { return (intptr_t) obj; }
|
||||
static ObjectKey* getKey(ObjectKey* obj) { return obj; }
|
||||
|
||||
static inline ObjectKey* get(JSObject* obj);
|
||||
static inline ObjectKey* get(ObjectGroup* group);
|
||||
|
||||
bool isGroup() {
|
||||
return (uintptr_t(this) & 1) == 0;
|
||||
}
|
||||
bool isSingleton() {
|
||||
return (uintptr_t(this) & 1) != 0;
|
||||
}
|
||||
|
||||
inline ObjectGroup* group();
|
||||
inline JSObject* singleton();
|
||||
|
||||
inline ObjectGroup* groupNoBarrier();
|
||||
inline JSObject* singletonNoBarrier();
|
||||
|
||||
const Class* clasp();
|
||||
TaggedProto proto();
|
||||
TypeNewScript* newScript();
|
||||
|
||||
bool unknownProperties();
|
||||
bool hasFlags(CompilerConstraintList* constraints, ObjectGroupFlags flags);
|
||||
bool hasStableClassAndProto(CompilerConstraintList* constraints);
|
||||
void watchStateChangeForTypedArrayData(CompilerConstraintList* constraints);
|
||||
void watchStateChangeForUnboxedConvertedToNative(CompilerConstraintList* constraints);
|
||||
HeapTypeSetKey property(jsid id);
|
||||
void ensureTrackedProperty(JSContext* cx, jsid id);
|
||||
|
||||
ObjectGroup* maybeGroup();
|
||||
|
||||
JS::Compartment* maybeCompartment();
|
||||
} JS_HAZ_GC_POINTER;
|
||||
|
||||
// Information about a single concrete type. We pack this into one word,
|
||||
// where small values are particular primitive or other singleton types and
|
||||
// larger values are either specific JS objects or object groups.
|
||||
class Type
|
||||
{
|
||||
friend class TypeSet;
|
||||
|
||||
uintptr_t data;
|
||||
explicit Type(uintptr_t data) : data(data) {}
|
||||
|
||||
public:
|
||||
|
||||
uintptr_t raw() const { return data; }
|
||||
|
||||
bool isPrimitive() const {
|
||||
return data < JSVAL_TYPE_OBJECT;
|
||||
}
|
||||
|
||||
bool isPrimitive(JSValueType type) const {
|
||||
MOZ_ASSERT(type < JSVAL_TYPE_OBJECT);
|
||||
return (uintptr_t) type == data;
|
||||
}
|
||||
|
||||
JSValueType primitive() const {
|
||||
MOZ_ASSERT(isPrimitive());
|
||||
return (JSValueType) data;
|
||||
}
|
||||
|
||||
bool isMagicArguments() const {
|
||||
return primitive() == JSVAL_TYPE_MAGIC;
|
||||
}
|
||||
|
||||
bool isSomeObject() const {
|
||||
return data == JSVAL_TYPE_OBJECT || data > JSVAL_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
bool isAnyObject() const {
|
||||
return data == JSVAL_TYPE_OBJECT;
|
||||
}
|
||||
|
||||
bool isUnknown() const {
|
||||
return data == JSVAL_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
/* Accessors for types that are either JSObject or ObjectGroup. */
|
||||
|
||||
bool isObject() const {
|
||||
MOZ_ASSERT(!isAnyObject() && !isUnknown());
|
||||
return data > JSVAL_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
bool isObjectUnchecked() const {
|
||||
return data > JSVAL_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
inline ObjectKey* objectKey() const;
|
||||
|
||||
/* Accessors for JSObject types */
|
||||
|
||||
bool isSingleton() const {
|
||||
return isObject() && !!(data & 1);
|
||||
}
|
||||
bool isSingletonUnchecked() const {
|
||||
return isObjectUnchecked() && !!(data & 1);
|
||||
}
|
||||
|
||||
inline JSObject* singleton() const;
|
||||
inline JSObject* singletonNoBarrier() const;
|
||||
|
||||
/* Accessors for ObjectGroup types */
|
||||
|
||||
bool isGroup() const {
|
||||
return isObject() && !(data & 1);
|
||||
}
|
||||
bool isGroupUnchecked() const {
|
||||
return isObjectUnchecked() && !(data & 1);
|
||||
}
|
||||
|
||||
inline ObjectGroup* group() const;
|
||||
inline ObjectGroup* groupNoBarrier() const;
|
||||
|
||||
inline void trace(JSTracer* trc);
|
||||
|
||||
JS::Compartment* maybeCompartment();
|
||||
|
||||
bool operator == (Type o) const { return data == o.data; }
|
||||
bool operator != (Type o) const { return data != o.data; }
|
||||
} JS_HAZ_GC_POINTER;
|
||||
|
||||
static inline Type UndefinedType() { return Type(JSVAL_TYPE_UNDEFINED); }
|
||||
static inline Type NullType() { return Type(JSVAL_TYPE_NULL); }
|
||||
static inline Type BooleanType() { return Type(JSVAL_TYPE_BOOLEAN); }
|
||||
static inline Type Int32Type() { return Type(JSVAL_TYPE_INT32); }
|
||||
static inline Type DoubleType() { return Type(JSVAL_TYPE_DOUBLE); }
|
||||
static inline Type StringType() { return Type(JSVAL_TYPE_STRING); }
|
||||
static inline Type SymbolType() { return Type(JSVAL_TYPE_SYMBOL); }
|
||||
#ifdef ENABLE_BIGINT
|
||||
static inline Type BigIntType() { return Type(JSVAL_TYPE_BIGINT); }
|
||||
#endif
|
||||
static inline Type MagicArgType() { return Type(JSVAL_TYPE_MAGIC); }
|
||||
static inline Type AnyObjectType() { return Type(JSVAL_TYPE_OBJECT); }
|
||||
static inline Type UnknownType() { return Type(JSVAL_TYPE_UNKNOWN); }
|
||||
|
||||
static inline Type PrimitiveType(JSValueType type) {
|
||||
MOZ_ASSERT(type < JSVAL_TYPE_UNKNOWN);
|
||||
return Type(type);
|
||||
}
|
||||
|
||||
static inline Type ObjectType(JSObject* obj);
|
||||
static inline Type ObjectType(ObjectGroup* group);
|
||||
static inline Type ObjectType(ObjectKey* key);
|
||||
|
||||
static const char* NonObjectTypeString(Type type);
|
||||
|
||||
static JS::UniqueChars TypeString(Type type);
|
||||
static JS::UniqueChars ObjectGroupString(ObjectGroup* group);
|
||||
|
||||
public:
|
||||
void print(FILE* fp = stderr);
|
||||
|
||||
/* Whether this set contains a specific type. */
|
||||
MOZ_ALWAYS_INLINE bool hasType(Type type) const;
|
||||
|
||||
TypeFlags baseFlags() const { return flags & TYPE_FLAG_BASE_MASK; }
|
||||
bool unknown() const { return !!(flags & TYPE_FLAG_UNKNOWN); }
|
||||
bool unknownObject() const { return !!(flags & (TYPE_FLAG_UNKNOWN | TYPE_FLAG_ANYOBJECT)); }
|
||||
bool empty() const { return !baseFlags() && !baseObjectCount(); }
|
||||
|
||||
bool hasAnyFlag(TypeFlags flags) const {
|
||||
MOZ_ASSERT((flags & TYPE_FLAG_BASE_MASK) == flags);
|
||||
return !!(baseFlags() & flags);
|
||||
}
|
||||
|
||||
bool nonDataProperty() const {
|
||||
return flags & TYPE_FLAG_NON_DATA_PROPERTY;
|
||||
}
|
||||
bool nonWritableProperty() const {
|
||||
return flags & TYPE_FLAG_NON_WRITABLE_PROPERTY;
|
||||
}
|
||||
bool nonConstantProperty() const {
|
||||
return flags & TYPE_FLAG_NON_CONSTANT_PROPERTY;
|
||||
}
|
||||
bool definiteProperty() const { return flags & TYPE_FLAG_DEFINITE_MASK; }
|
||||
unsigned definiteSlot() const {
|
||||
MOZ_ASSERT(definiteProperty());
|
||||
return (flags >> TYPE_FLAG_DEFINITE_SHIFT) - 1;
|
||||
}
|
||||
|
||||
/* Join two type sets into a new set. The result should not be modified further. */
|
||||
static TemporaryTypeSet* unionSets(TypeSet* a, TypeSet* b, LifoAlloc* alloc);
|
||||
/* Return the intersection of the 2 TypeSets. The result should not be modified further */
|
||||
static TemporaryTypeSet* intersectSets(TemporaryTypeSet* a, TemporaryTypeSet* b, LifoAlloc* alloc);
|
||||
/*
|
||||
* Returns a copy of TypeSet a excluding/removing the types in TypeSet b.
|
||||
* TypeSet b can only contain primitives or be any object. No support for
|
||||
* specific objects. The result should not be modified further.
|
||||
*/
|
||||
static TemporaryTypeSet* removeSet(TemporaryTypeSet* a, TemporaryTypeSet* b, LifoAlloc* alloc);
|
||||
|
||||
/* Add a type to this set using the specified allocator. */
|
||||
void addType(Type type, LifoAlloc* alloc);
|
||||
|
||||
/* Get a list of all types in this set. */
|
||||
using TypeList = Vector<Type, 1, SystemAllocPolicy>;
|
||||
template <class TypeListT> bool enumerateTypes(TypeListT* list) const;
|
||||
|
||||
/*
|
||||
* Iterate through the objects in this set. getObjectCount overapproximates
|
||||
* in the hash case (see SET_ARRAY_SIZE in TypeInference-inl.h), and
|
||||
* getObject may return nullptr.
|
||||
*/
|
||||
inline unsigned getObjectCount() const;
|
||||
inline bool hasGroup(unsigned i) const;
|
||||
inline bool hasSingleton(unsigned i) const;
|
||||
inline ObjectKey* getObject(unsigned i) const;
|
||||
inline JSObject* getSingleton(unsigned i) const;
|
||||
inline ObjectGroup* getGroup(unsigned i) const;
|
||||
|
||||
/*
|
||||
* Get the objects in the set without triggering barriers. This is
|
||||
* potentially unsafe and you should only call these methods if you know
|
||||
* what you're doing. For JIT compilation, use the following methods
|
||||
* instead:
|
||||
* - MacroAssembler::getSingletonAndDelayBarrier()
|
||||
* - MacroAssembler::getGroupAndDelayBarrier()
|
||||
*/
|
||||
inline JSObject* getSingletonNoBarrier(unsigned i) const;
|
||||
inline ObjectGroup* getGroupNoBarrier(unsigned i) const;
|
||||
|
||||
/* The Class of an object in this set. */
|
||||
inline const Class* getObjectClass(unsigned i) const;
|
||||
|
||||
bool canSetDefinite(unsigned slot) {
|
||||
// Note: the cast is required to work around an MSVC issue.
|
||||
return (slot + 1) <= (unsigned(TYPE_FLAG_DEFINITE_MASK) >> TYPE_FLAG_DEFINITE_SHIFT);
|
||||
}
|
||||
void setDefinite(unsigned slot) {
|
||||
MOZ_ASSERT(canSetDefinite(slot));
|
||||
flags |= ((slot + 1) << TYPE_FLAG_DEFINITE_SHIFT);
|
||||
MOZ_ASSERT(definiteSlot() == slot);
|
||||
}
|
||||
|
||||
/* Whether any values in this set might have the specified type. */
|
||||
bool mightBeMIRType(jit::MIRType type) const;
|
||||
|
||||
/*
|
||||
* Get whether this type set is known to be a subset of other.
|
||||
* This variant doesn't freeze constraints. That variant is called knownSubset
|
||||
*/
|
||||
bool isSubset(const TypeSet* other) const;
|
||||
|
||||
/*
|
||||
* Get whether the objects in this TypeSet are a subset of the objects
|
||||
* in other.
|
||||
*/
|
||||
bool objectsAreSubset(TypeSet* other);
|
||||
|
||||
/* Whether this TypeSet contains exactly the same types as other. */
|
||||
bool equals(const TypeSet* other) const {
|
||||
return this->isSubset(other) && other->isSubset(this);
|
||||
}
|
||||
|
||||
bool objectsIntersect(const TypeSet* other) const;
|
||||
|
||||
/* Forward all types in this set to the specified constraint. */
|
||||
bool addTypesToConstraint(JSContext* cx, TypeConstraint* constraint);
|
||||
|
||||
// Clone a type set into an arbitrary allocator.
|
||||
TemporaryTypeSet* clone(LifoAlloc* alloc) const;
|
||||
|
||||
// |*result| is not even partly initialized when this function is called:
|
||||
// this function placement-new's its contents into existence.
|
||||
bool cloneIntoUninitialized(LifoAlloc* alloc, TemporaryTypeSet* result) const;
|
||||
|
||||
// Create a new TemporaryTypeSet where undefined and/or null has been filtered out.
|
||||
TemporaryTypeSet* filter(LifoAlloc* alloc, bool filterUndefined, bool filterNull) const;
|
||||
// Create a new TemporaryTypeSet where the type has been set to object.
|
||||
TemporaryTypeSet* cloneObjectsOnly(LifoAlloc* alloc);
|
||||
TemporaryTypeSet* cloneWithoutObjects(LifoAlloc* alloc);
|
||||
|
||||
JS::Compartment* maybeCompartment();
|
||||
|
||||
// Trigger a read barrier on all the contents of a type set.
|
||||
static void readBarrier(const TypeSet* types);
|
||||
|
||||
protected:
|
||||
uint32_t baseObjectCount() const {
|
||||
return (flags & TYPE_FLAG_OBJECT_COUNT_MASK) >> TYPE_FLAG_OBJECT_COUNT_SHIFT;
|
||||
}
|
||||
inline void setBaseObjectCount(uint32_t count);
|
||||
|
||||
void clearObjects();
|
||||
|
||||
public:
|
||||
static inline Type GetValueType(const JS::Value& val);
|
||||
|
||||
static inline bool IsUntrackedValue(const JS::Value& val);
|
||||
|
||||
// Get the type of a possibly optimized out or uninitialized let value.
|
||||
// This generally only happens on unconditional type monitors on bailing
|
||||
// out of Ion, such as for argument and local types.
|
||||
static inline Type GetMaybeUntrackedValueType(const JS::Value& val);
|
||||
|
||||
static bool IsTypeMarked(JSRuntime* rt, Type* v);
|
||||
static bool IsTypeAboutToBeFinalized(Type* v);
|
||||
} JS_HAZ_GC_POINTER;
|
||||
|
||||
#if JS_BITS_PER_WORD == 32
|
||||
static const uintptr_t BaseTypeInferenceMagic = 0xa1a2b3b4;
|
||||
#else
|
||||
static const uintptr_t BaseTypeInferenceMagic = 0xa1a2b3b4c5c6d7d8;
|
||||
#endif
|
||||
static const uintptr_t TypeConstraintMagic = BaseTypeInferenceMagic + 1;
|
||||
static const uintptr_t ConstraintTypeSetMagic = BaseTypeInferenceMagic + 2;
|
||||
|
||||
#ifdef JS_CRASH_DIAGNOSTICS
|
||||
extern void
|
||||
ReportMagicWordFailure(uintptr_t actual, uintptr_t expected);
|
||||
extern void
|
||||
ReportMagicWordFailure(uintptr_t actual, uintptr_t expected, uintptr_t flags, uintptr_t objectSet);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* A constraint which listens to additions to a type set and propagates those
|
||||
* changes to other type sets.
|
||||
*/
|
||||
class TypeConstraint
|
||||
{
|
||||
private:
|
||||
#ifdef JS_CRASH_DIAGNOSTICS
|
||||
uintptr_t magic_ = TypeConstraintMagic;
|
||||
#endif
|
||||
|
||||
/* Next constraint listening to the same type set. */
|
||||
TypeConstraint* next_ = nullptr;
|
||||
|
||||
public:
|
||||
TypeConstraint() = default;
|
||||
|
||||
void checkMagic() const {
|
||||
#ifdef JS_CRASH_DIAGNOSTICS
|
||||
if (MOZ_UNLIKELY(magic_ != TypeConstraintMagic)) {
|
||||
ReportMagicWordFailure(magic_, TypeConstraintMagic);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TypeConstraint* next() const {
|
||||
checkMagic();
|
||||
if (next_) {
|
||||
next_->checkMagic();
|
||||
}
|
||||
return next_;
|
||||
}
|
||||
void setNext(TypeConstraint* next) {
|
||||
MOZ_ASSERT(!next_);
|
||||
checkMagic();
|
||||
if (next) {
|
||||
next->checkMagic();
|
||||
}
|
||||
next_ = next;
|
||||
}
|
||||
|
||||
/* Debugging name for this kind of constraint. */
|
||||
virtual const char* kind() = 0;
|
||||
|
||||
/* Register a new type for the set this constraint is listening to. */
|
||||
virtual void newType(JSContext* cx, TypeSet* source, TypeSet::Type type) = 0;
|
||||
|
||||
/*
|
||||
* For constraints attached to an object property's type set, mark the
|
||||
* property as having changed somehow.
|
||||
*/
|
||||
virtual void newPropertyState(JSContext* cx, TypeSet* source) {}
|
||||
|
||||
/*
|
||||
* For constraints attached to the JSID_EMPTY type set on an object,
|
||||
* indicate a change in one of the object's dynamic property flags or other
|
||||
* state.
|
||||
*/
|
||||
virtual void newObjectState(JSContext* cx, ObjectGroup* group) {}
|
||||
|
||||
/*
|
||||
* 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 bool sweep(TypeZone& zone, TypeConstraint** res) = 0;
|
||||
|
||||
/* The associated compartment, if any. */
|
||||
virtual JS::Compartment* maybeCompartment() = 0;
|
||||
};
|
||||
|
||||
/* Superclass common to stack and heap type sets. */
|
||||
class ConstraintTypeSet : public TypeSet
|
||||
{
|
||||
private:
|
||||
#ifdef JS_CRASH_DIAGNOSTICS
|
||||
uintptr_t magic_ = ConstraintTypeSetMagic;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
/* Chain of constraints which propagate changes out from this type set. */
|
||||
TypeConstraint* constraintList_ = nullptr;
|
||||
|
||||
public:
|
||||
ConstraintTypeSet() = default;
|
||||
|
||||
#ifdef JS_CRASH_DIAGNOSTICS
|
||||
void initMagic() {
|
||||
MOZ_ASSERT(!magic_);
|
||||
magic_ = ConstraintTypeSetMagic;
|
||||
}
|
||||
#endif
|
||||
|
||||
void checkMagic() const {
|
||||
#ifdef JS_CRASH_DIAGNOSTICS
|
||||
if (MOZ_UNLIKELY(magic_ != ConstraintTypeSetMagic)) {
|
||||
ReportMagicWordFailure(magic_, ConstraintTypeSetMagic, uintptr_t(flags), uintptr_t(objectSet));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// This takes a reference to AutoSweepBase to ensure we swept the owning
|
||||
// ObjectGroup or TypeScript.
|
||||
TypeConstraint* constraintList(const AutoSweepBase& sweep) const {
|
||||
checkMagic();
|
||||
if (constraintList_) {
|
||||
constraintList_->checkMagic();
|
||||
}
|
||||
return constraintList_;
|
||||
}
|
||||
void setConstraintList(TypeConstraint* constraint) {
|
||||
MOZ_ASSERT(!constraintList_);
|
||||
checkMagic();
|
||||
if (constraint) {
|
||||
constraint->checkMagic();
|
||||
}
|
||||
constraintList_ = constraint;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a type to this set, calling any constraint handlers if this is a new
|
||||
* possible type.
|
||||
*/
|
||||
void addType(const AutoSweepBase& sweep, JSContext* cx, Type type);
|
||||
|
||||
/* Generalize to any type. */
|
||||
void makeUnknown(const AutoSweepBase& sweep, JSContext* cx) {
|
||||
addType(sweep, cx, UnknownType());
|
||||
}
|
||||
|
||||
// Trigger a post barrier when writing to this set, if necessary.
|
||||
// addType(cx, type) takes care of this automatically.
|
||||
void postWriteBarrier(JSContext* cx, Type type);
|
||||
|
||||
/* Add a new constraint to this set. */
|
||||
bool addConstraint(JSContext* cx, TypeConstraint* constraint, bool callExisting = true);
|
||||
|
||||
inline void sweep(const AutoSweepBase& sweep, JS::Zone* zone,
|
||||
AutoClearTypeInferenceStateOnOOM& oom);
|
||||
inline void trace(JS::Zone* zone, JSTracer* trc);
|
||||
};
|
||||
|
||||
class StackTypeSet : public ConstraintTypeSet
|
||||
{
|
||||
public:
|
||||
};
|
||||
|
||||
class HeapTypeSet : public ConstraintTypeSet
|
||||
{
|
||||
inline void newPropertyState(const AutoSweepObjectGroup& sweep, JSContext* cx);
|
||||
|
||||
public:
|
||||
/* Mark this type set as representing a non-data property. */
|
||||
inline void setNonDataProperty(const AutoSweepObjectGroup& sweep, JSContext* cx);
|
||||
|
||||
/* Mark this type set as representing a non-writable property. */
|
||||
inline void setNonWritableProperty(const AutoSweepObjectGroup& sweep, JSContext* cx);
|
||||
|
||||
// Mark this type set as being non-constant.
|
||||
inline void setNonConstantProperty(const AutoSweepObjectGroup& sweep, JSContext* cx);
|
||||
};
|
||||
|
||||
enum class DOMObjectKind : uint8_t { Proxy, Native, Unknown };
|
||||
|
||||
class TemporaryTypeSet : public TypeSet
|
||||
{
|
||||
public:
|
||||
TemporaryTypeSet() {}
|
||||
TemporaryTypeSet(LifoAlloc* alloc, Type type);
|
||||
|
||||
TemporaryTypeSet(uint32_t flags, ObjectKey** objectSet) {
|
||||
this->flags = flags;
|
||||
this->objectSet = objectSet;
|
||||
}
|
||||
|
||||
TemporaryTypeSet(LifoAlloc* alloc, jit::MIRType type)
|
||||
: TemporaryTypeSet(alloc, PrimitiveType(ValueTypeFromMIRType(type)))
|
||||
{
|
||||
MOZ_ASSERT(type != jit::MIRType::Value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Constraints for JIT compilation.
|
||||
*
|
||||
* Methods for JIT compilation. These must be used when a script is
|
||||
* currently being compiled (see AutoEnterCompilation) and will add
|
||||
* constraints ensuring that if the return value change in the future due
|
||||
* to new type information, the script's jitcode will be discarded.
|
||||
*/
|
||||
|
||||
/* Get any type tag which all values in this set must have. */
|
||||
jit::MIRType getKnownMIRType();
|
||||
|
||||
bool isMagicArguments() { return getKnownMIRType() == jit::MIRType::MagicOptimizedArguments; }
|
||||
|
||||
/* Whether this value may be an object. */
|
||||
bool maybeObject() { return unknownObject() || baseObjectCount() > 0; }
|
||||
|
||||
/*
|
||||
* Whether this typeset represents a potentially sentineled object value:
|
||||
* the value may be an object or null or undefined.
|
||||
* Returns false if the value cannot ever be an object.
|
||||
*/
|
||||
bool objectOrSentinel() {
|
||||
TypeFlags flags = TYPE_FLAG_UNDEFINED | TYPE_FLAG_NULL | TYPE_FLAG_ANYOBJECT;
|
||||
if (baseFlags() & (~flags & TYPE_FLAG_BASE_MASK)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return hasAnyFlag(TYPE_FLAG_ANYOBJECT) || baseObjectCount() > 0;
|
||||
}
|
||||
|
||||
/* Whether the type set contains objects with any of a set of flags. */
|
||||
bool hasObjectFlags(CompilerConstraintList* constraints, ObjectGroupFlags flags);
|
||||
|
||||
/* Get the class shared by all objects in this set, or nullptr. */
|
||||
const Class* getKnownClass(CompilerConstraintList* constraints);
|
||||
|
||||
/*
|
||||
* Get the realm shared by all objects in this set, or nullptr. Returns
|
||||
* nullptr if the set contains proxies (because cross-compartment wrappers
|
||||
* don't have a single realm associated with them).
|
||||
*/
|
||||
JS::Realm* getKnownRealm(CompilerConstraintList* constraints);
|
||||
|
||||
/* Result returned from forAllClasses */
|
||||
enum ForAllResult
|
||||
{
|
||||
EMPTY = 1, // Set empty
|
||||
ALL_TRUE, // Set not empty and predicate returned true for all classes
|
||||
ALL_FALSE, // Set not empty and predicate returned false for all classes
|
||||
MIXED, // Set not empty and predicate returned false for some classes
|
||||
// and true for others, or set contains an unknown or non-object
|
||||
// type
|
||||
};
|
||||
|
||||
/* Apply func to the members of the set and return an appropriate result.
|
||||
* The iteration may end early if the result becomes known early.
|
||||
*/
|
||||
ForAllResult forAllClasses(CompilerConstraintList* constraints,
|
||||
bool (*func)(const Class* clasp));
|
||||
|
||||
/*
|
||||
* Returns true if all objects in this set have the same prototype, and
|
||||
* assigns this object to *proto. The proto can be nullptr.
|
||||
*/
|
||||
bool getCommonPrototype(CompilerConstraintList* constraints, JSObject** proto);
|
||||
|
||||
/* Whether the buffer mapped by a TypedArray is shared memory or not */
|
||||
enum TypedArraySharedness
|
||||
{
|
||||
UnknownSharedness = 1, // We can't determine sharedness
|
||||
KnownShared, // We know for sure the buffer is shared
|
||||
KnownUnshared // We know for sure the buffer is unshared
|
||||
};
|
||||
|
||||
/*
|
||||
* Get the typed array type of all objects in this set, or Scalar::MaxTypedArrayViewType.
|
||||
* If there is such a common type and sharedness is not nullptr then
|
||||
* *sharedness is set to what we know about the sharedness of the memory.
|
||||
*/
|
||||
Scalar::Type getTypedArrayType(CompilerConstraintList* constraints,
|
||||
TypedArraySharedness* sharedness = nullptr);
|
||||
|
||||
/* Whether all objects have JSCLASS_IS_DOMJSCLASS set. */
|
||||
bool isDOMClass(CompilerConstraintList* constraints, DOMObjectKind* kind);
|
||||
|
||||
/* Whether clasp->isCallable() is true for one or more objects in this set. */
|
||||
bool maybeCallable(CompilerConstraintList* constraints);
|
||||
|
||||
/* Whether clasp->emulatesUndefined() is true for one or more objects in this set. */
|
||||
bool maybeEmulatesUndefined(CompilerConstraintList* constraints);
|
||||
|
||||
/* Get the single value which can appear in this type set, otherwise nullptr. */
|
||||
JSObject* maybeSingleton();
|
||||
ObjectKey* maybeSingleObject();
|
||||
|
||||
/* Whether any objects in the type set needs a barrier on id. */
|
||||
bool propertyNeedsBarrier(CompilerConstraintList* constraints, jsid id);
|
||||
|
||||
/*
|
||||
* Whether this set contains all types in other, except (possibly) the
|
||||
* specified type.
|
||||
*/
|
||||
bool filtersType(const TemporaryTypeSet* other, Type type) const;
|
||||
|
||||
enum DoubleConversion
|
||||
{
|
||||
/* All types in the set should use eager double conversion. */
|
||||
AlwaysConvertToDoubles,
|
||||
|
||||
/* Some types in the set should use eager double conversion. */
|
||||
MaybeConvertToDoubles,
|
||||
|
||||
/* No types should use eager double conversion. */
|
||||
DontConvertToDoubles,
|
||||
|
||||
/* Some types should use eager double conversion, others cannot. */
|
||||
AmbiguousDoubleConversion
|
||||
};
|
||||
|
||||
/*
|
||||
* Whether known double optimizations are possible for element accesses on
|
||||
* objects in this type set.
|
||||
*/
|
||||
DoubleConversion convertDoubleElements(CompilerConstraintList* constraints);
|
||||
|
||||
private:
|
||||
void getTypedArraySharedness(CompilerConstraintList* constraints,
|
||||
TypedArraySharedness* sharedness);
|
||||
};
|
||||
|
||||
// Representation of a heap type property which may or may not be instantiated.
|
||||
// Heap properties for singleton types are instantiated lazily as they are used
|
||||
// by the compiler, but this is only done on the main thread. If we are
|
||||
// compiling off thread and use a property which has not yet been instantiated,
|
||||
// it will be treated as empty and non-configured and will be instantiated when
|
||||
// rejoining to the main thread. If it is in fact not empty, the compilation
|
||||
// will fail; to avoid this, we try to instantiate singleton property types
|
||||
// during generation of baseline caches.
|
||||
class HeapTypeSetKey
|
||||
{
|
||||
friend class TypeSet::ObjectKey;
|
||||
|
||||
// Object and property being accessed.
|
||||
TypeSet::ObjectKey* object_;
|
||||
jsid id_;
|
||||
|
||||
// If instantiated, the underlying heap type set.
|
||||
HeapTypeSet* maybeTypes_;
|
||||
|
||||
public:
|
||||
HeapTypeSetKey()
|
||||
: object_(nullptr), id_(JSID_EMPTY), maybeTypes_(nullptr)
|
||||
{}
|
||||
|
||||
TypeSet::ObjectKey* object() const { return object_; }
|
||||
jsid id() const { return id_; }
|
||||
|
||||
HeapTypeSet* maybeTypes() const {
|
||||
if (maybeTypes_) {
|
||||
maybeTypes_->checkMagic();
|
||||
}
|
||||
return maybeTypes_;
|
||||
}
|
||||
|
||||
bool instantiate(JSContext* cx);
|
||||
|
||||
void freeze(CompilerConstraintList* constraints);
|
||||
jit::MIRType knownMIRType(CompilerConstraintList* constraints);
|
||||
bool nonData(CompilerConstraintList* constraints);
|
||||
bool nonWritable(CompilerConstraintList* constraints);
|
||||
bool isOwnProperty(CompilerConstraintList* constraints, bool allowEmptyTypesForGlobal = false);
|
||||
bool knownSubset(CompilerConstraintList* constraints, const HeapTypeSetKey& other);
|
||||
JSObject* singleton(CompilerConstraintList* constraints);
|
||||
bool needsBarrier(CompilerConstraintList* constraints);
|
||||
bool constant(CompilerConstraintList* constraints, Value* valOut);
|
||||
bool couldBeConstant(CompilerConstraintList* constraints);
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* vm_TypeSet_h */
|
|
@ -9,8 +9,9 @@
|
|||
|
||||
#include "vm/UnboxedObject.h"
|
||||
|
||||
#include "vm/TypeInference.h"
|
||||
|
||||
#include "gc/StoreBuffer-inl.h"
|
||||
#include "vm/ArrayObject-inl.h"
|
||||
#include "vm/NativeObject-inl.h"
|
||||
|
||||
namespace js {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "jit/MacroAssembler-inl.h"
|
||||
#include "vm/JSObject-inl.h"
|
||||
#include "vm/Shape-inl.h"
|
||||
#include "vm/TypeInference-inl.h"
|
||||
|
||||
using mozilla::ArrayLength;
|
||||
using mozilla::PodCopy;
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
|
||||
namespace js {
|
||||
|
||||
struct AutoEnterAnalysis;
|
||||
class PreliminaryObjectArray;
|
||||
|
||||
// Memory required for an unboxed value of a given type. Returns zero for types
|
||||
// which can't be used for unboxed objects.
|
||||
static inline size_t
|
||||
|
@ -44,17 +47,16 @@ UnboxedTypeNeedsPostBarrier(JSValueType type)
|
|||
class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
|
||||
{
|
||||
public:
|
||||
struct Property {
|
||||
PropertyName* name;
|
||||
uint32_t offset;
|
||||
JSValueType type;
|
||||
struct Property
|
||||
{
|
||||
PropertyName* name = nullptr;
|
||||
uint32_t offset = UINT32_MAX;
|
||||
JSValueType type = JSVAL_TYPE_MAGIC;
|
||||
|
||||
Property()
|
||||
: name(nullptr), offset(UINT32_MAX), type(JSVAL_TYPE_MAGIC)
|
||||
{}
|
||||
Property() = default;
|
||||
};
|
||||
|
||||
typedef Vector<Property, 0, SystemAllocPolicy> PropertyVector;
|
||||
using PropertyVector = Vector<Property, 0, SystemAllocPolicy>;
|
||||
|
||||
private:
|
||||
JS::Zone* zone_;
|
||||
|
@ -62,19 +64,19 @@ class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
|
|||
// If objects in this group have ever been converted to native objects,
|
||||
// these store the corresponding native group and initial shape for such
|
||||
// objects. Type information for this object is reflected in nativeGroup.
|
||||
GCPtrObjectGroup nativeGroup_;
|
||||
GCPtrShape nativeShape_;
|
||||
GCPtrObjectGroup nativeGroup_ = {};
|
||||
GCPtrShape nativeShape_ = {};
|
||||
|
||||
// Any script/pc which the associated group is created for.
|
||||
GCPtrScript allocationScript_;
|
||||
jsbytecode* allocationPc_;
|
||||
GCPtrScript allocationScript_ = {};
|
||||
jsbytecode* allocationPc_ = {};
|
||||
|
||||
// If nativeGroup is set and this object originally had a TypeNewScript or
|
||||
// was keyed to an allocation site, this points to the group which replaced
|
||||
// this one. This link is only needed to keep the replacement group from
|
||||
// being GC'ed. If it were GC'ed and a new one regenerated later, that new
|
||||
// group might have a different allocation kind from this group.
|
||||
GCPtrObjectGroup replacementGroup_;
|
||||
GCPtrObjectGroup replacementGroup_ = {};
|
||||
|
||||
// The following members are only used for unboxed plain objects.
|
||||
|
||||
|
@ -82,27 +84,27 @@ class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
|
|||
PropertyVector properties_;
|
||||
|
||||
// Byte size of the data for objects with this layout.
|
||||
size_t size_;
|
||||
size_t size_ = 0;
|
||||
|
||||
// Any 'new' script information associated with this layout.
|
||||
TypeNewScript* newScript_;
|
||||
TypeNewScript* newScript_ = nullptr;
|
||||
|
||||
// List for use in tracing objects with this layout. This has the same
|
||||
// structure as the trace list on a TypeDescr.
|
||||
int32_t* traceList_;
|
||||
int32_t* traceList_ = nullptr;
|
||||
|
||||
// If this layout has been used to construct script or JSON constant
|
||||
// objects, this code might be filled in to more quickly fill in objects
|
||||
// from an array of values.
|
||||
GCPtrJitCode constructorCode_;
|
||||
GCPtrJitCode constructorCode_ = {};
|
||||
|
||||
public:
|
||||
explicit UnboxedLayout(JS::Zone* zone)
|
||||
: zone_(zone), nativeGroup_(nullptr), nativeShape_(nullptr),
|
||||
allocationScript_(nullptr), allocationPc_(nullptr), replacementGroup_(nullptr),
|
||||
size_(0), newScript_(nullptr), traceList_(nullptr), constructorCode_(nullptr)
|
||||
: zone_(zone)
|
||||
{}
|
||||
|
||||
inline ~UnboxedLayout();
|
||||
|
||||
JS::Zone* zone() const { return zone_; }
|
||||
|
||||
bool initProperties(const PropertyVector& properties, size_t size) {
|
||||
|
@ -110,19 +112,6 @@ class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
|
|||
return properties_.appendAll(properties);
|
||||
}
|
||||
|
||||
~UnboxedLayout() {
|
||||
if (newScript_) {
|
||||
newScript_->clear();
|
||||
}
|
||||
js_delete(newScript_);
|
||||
js_free(traceList_);
|
||||
|
||||
nativeGroup_.init(nullptr);
|
||||
nativeShape_.init(nullptr);
|
||||
replacementGroup_.init(nullptr);
|
||||
constructorCode_.init(nullptr);
|
||||
}
|
||||
|
||||
void detachFromRealm();
|
||||
|
||||
const PropertyVector& properties() const {
|
||||
|
|
Загрузка…
Ссылка в новой задаче