[INFER] Condense type information during GC, bug 613221.

This commit is contained in:
Brian Hackett 2011-03-01 13:10:05 -08:00
parent ac00635e1e
commit 6010fa207c
27 changed files with 1112 additions and 1000 deletions

View File

@ -661,11 +661,6 @@ ifeq (,$(filter BeOS HP-UX WINNT WINCE OpenVMS OS2,$(OS_ARCH)))
EXTRA_LIBS += -lm
endif
# zlib is needed for recording executions in type inference
ifdef JS_TYPE_INFERENCE
EXTRA_LIBS += -lz
endif
# Prevent floating point errors caused by VC++ optimizations
ifeq ($(OS_ARCH)_$(GNU_CC),WINNT_)
ifeq (,$(filter-out 1200 1300 1310,$(_MSC_VER)))

View File

@ -0,0 +1,12 @@
function foo(n) {
for (var i = 0; i < n; i++) {}
return i;
}
assertEq(foo(1000), 1000);
gc();
eval("assertEq(foo(1000.5), 1001)");

View File

@ -0,0 +1,19 @@
var g = 10;
function bar(n) {
return g;
}
function foo(n, v) {
for (var i = 0; i < n; i++)
assertEq(bar(i), v);
}
foo(10, 10);
gc();
eval("g = 10.5");
foo(10, 10.5);

View File

@ -0,0 +1,6 @@
eval("var x = 10; function foo() { return x; }");
assertEq(foo(), 10);
gc();
assertEq(foo(), 10);

View File

@ -0,0 +1,8 @@
function foo(x, y) {
gc();
var z = x + y;
print(z);
}
foo(0x7ffffff0, 100);

View File

@ -61,26 +61,17 @@ void
TypeCompartment::init()
{
PodZero(this);
JS_InitArenaPool(&pool, "typeinfer", 512, 8, NULL);
#ifdef DEBUG
emptyObject.name_ = JSID_VOID;
#endif
emptyObject.unknownProperties = true;
emptyObject.pool = &pool;
}
TypeCompartment::~TypeCompartment()
{
JS_FinishArenaPool(&pool);
}
types::TypeObject *
TypeCompartment::newTypeObject(JSContext *cx, types::TypeScript *script, const char *name,
TypeCompartment::newTypeObject(JSContext *cx, JSScript *script, const char *name,
bool isFunction, JSObject *proto)
{
JSArenaPool &pool = script ? script->pool : this->pool;
#ifdef DEBUG
#if 1 /* Define to get unique printed names, including when there are multiple globals. */
static unsigned nameCount = 0;
@ -95,13 +86,16 @@ TypeCompartment::newTypeObject(JSContext *cx, types::TypeScript *script, const c
#endif
TypeObject *object;
if (isFunction)
object = ArenaNew<TypeFunction>(pool, &pool, id, proto);
else
object = ArenaNew<TypeObject>(pool, &pool, id, proto);
if (isFunction) {
object = (TypeFunction *) cx->calloc(sizeof(TypeFunction));
new(object) TypeFunction(id, proto);
} else {
object = (TypeObject *) cx->calloc(sizeof(TypeObject));
new(object) TypeObject(id, proto);
}
#ifdef JS_TYPE_INFERENCE
TypeObject *&objects = script ? script->objects : this->objects;
TypeObject *&objects = script ? script->typeObjects : this->objects;
object->next = objects;
objects = object;
#else
@ -128,7 +122,7 @@ TypeCompartment::newInitializerTypeObject(JSContext *cx, JSScript *script,
if (!js_GetClassPrototype(cx, script->getGlobal(), key, &proto, NULL))
return NULL;
TypeObject *res = newTypeObject(cx, script->types, name, false, proto);
TypeObject *res = newTypeObject(cx, script, name, false, proto);
if (isArray)
res->initializerArray = true;
else
@ -229,6 +223,7 @@ JSObject::makeNewType(JSContext *cx)
/////////////////////////////////////////////////////////////////////
namespace js {
namespace types {
void
types::TypeObject::trace(JSTracer *trc)
@ -254,53 +249,219 @@ types::TypeObject::trace(JSTracer *trc)
gc::MarkObject(trc, *proto, "type_proto");
}
void
types::TypeScript::trace(JSTracer *trc)
{
#ifdef JS_TYPE_INFERENCE
/* If a script is live, so are all type objects within it. */
types::TypeObject *object = objects;
while (object) {
if (!object->marked)
object->trace(trc);
object = object->next;
/*
* Condense any constraints on a type set which were generated during analysis
* of a script, and sweep all type objects and references to type objects
* which no longer exist.
*/
void
CondenseSweepTypeSet(JSContext *cx, HashSet<JSScript*> &condensed, TypeSet *types)
{
if (types->objectCount >= 2) {
bool removed = false;
unsigned objectCapacity = HashSetCapacity(types->objectCount);
for (unsigned i = 0; i < objectCapacity; i++) {
TypeObject *object = types->objectSet[i];
if (object && !object->marked) {
removed = true;
types->objectSet[i] = NULL;
}
}
if (removed) {
/* Reconstruct the type set to re-resolve hash collisions. */
TypeObject **oldArray = types->objectSet;
types->objectSet = NULL;
types->objectCount = 0;
for (unsigned i = 0; i < objectCapacity; i++) {
TypeObject *object = oldArray[i];
if (object) {
TypeObject *&entry = HashSetInsert<TypeObject *,TypeObject,TypeObjectKey>
(cx, types->objectSet, types->objectCount, object);
entry = object;
}
}
cx->free(oldArray);
}
} else if (types->objectCount == 1) {
TypeObject *object = (TypeObject*) types->objectSet;
if (!object->marked) {
types->objectSet = NULL;
types->objectCount = 0;
}
}
#endif
TypeConstraint *constraint = types->constraintList;
types->constraintList = NULL;
/*
* Keep track of all the scripts we have found or generated
* condensed constraints for, in the condensed table. We reuse the
* same table for each type set to avoid extra initialization cost,
* but the table is emptied after each set is processed.
*/
while (constraint) {
TypeConstraint *next = constraint->next;
TypeObject *object = constraint->baseSubset();
if (object) {
/*
* Constraint propagating data between objects. If the target
* is not being collected (these are weak references) then
* keep the constraint.
*/
if (object->marked) {
constraint->next = types->constraintList;
types->constraintList = constraint;
} else {
cx->free(constraint);
}
constraint = next;
continue;
}
/*
* Throw away constraints propagating types into scripts which
* are about to be destroyed. :FIXME: not handling eval-cache
* scripts right, which have already been destroyed and can
* lead to use of garbage pointers here.
*/
JSScript *script = constraint->script;
if (script->isCachedEval ||
(script->u.object && IsAboutToBeFinalized(cx, script->u.object)) ||
(script->fun && IsAboutToBeFinalized(cx, script->fun))) {
if (constraint->condensed())
cx->free(constraint);
constraint = next;
continue;
}
HashSet<JSScript*>::AddPtr p =
condensed.lookupForAdd(script);
if (!p) {
if (!condensed.add(p, script))
JS_NOT_REACHED("FIXME");
types->addCondensed(cx, script);
}
if (constraint->condensed())
cx->free(constraint);
constraint = next;
}
condensed.clear();
}
static inline void
SweepObjectList(JSContext *cx, types::TypeObject *objects)
void
CondenseTypeObjectList(JSContext *cx, TypeObject *objects)
{
types::TypeObject *object = objects;
HashSet<JSScript *> condensed(cx);
if (!condensed.init())
JS_NOT_REACHED("FIXME");
TypeObject *object = objects;
while (object) {
if (object->marked) {
object->marked = false;
} else {
object->proto = NULL;
if (object->emptyShapes) {
cx->free(object->emptyShapes);
object->emptyShapes = NULL;
if (object->propertyCount >= 2) {
unsigned capacity = HashSetCapacity(object->propertyCount);
for (unsigned i = 0; i < capacity; i++) {
Property *prop = object->propertySet[i];
if (prop) {
CondenseSweepTypeSet(cx, condensed, &prop->types);
CondenseSweepTypeSet(cx, condensed, &prop->ownTypes);
}
}
} else if (object->propertyCount == 1) {
Property *prop = (Property *) object->propertySet;
CondenseSweepTypeSet(cx, condensed, &prop->types);
CondenseSweepTypeSet(cx, condensed, &prop->ownTypes);
}
object = object->next;
}
}
static void
DestroyTypeSet(JSContext *cx, const TypeSet &types)
{
if (types.objectCount >= 2)
cx->free(types.objectSet);
}
static void
DestroyProperty(JSContext *cx, Property *prop)
{
DestroyTypeSet(cx, prop->types);
DestroyTypeSet(cx, prop->ownTypes);
cx->free(prop);
}
#endif /* JS_TYPE_INFERENCE */
void
types::TypeScript::sweep(JSContext *cx)
SweepTypeObjectList(JSContext *cx, TypeObject *&objects)
{
TypeObject **pobject = &objects;
while (*pobject) {
TypeObject *object = *pobject;
if (object->marked) {
object->marked = false;
pobject = &object->next;
} else {
if (object->emptyShapes)
cx->free(object->emptyShapes);
*pobject = object->next;
#ifdef JS_TYPE_INFERENCE
if (object->propertyCount >= 2) {
unsigned capacity = HashSetCapacity(object->propertyCount);
for (unsigned i = 0; i < capacity; i++) {
Property *prop = object->propertySet[i];
if (prop)
DestroyProperty(cx, prop);
}
cx->free(object->propertySet);
} else if (object->propertyCount == 1) {
Property *prop = (Property *) object->propertySet;
DestroyProperty(cx, prop);
}
#endif
cx->free(object);
}
}
}
} } /* namespace js::types */
void
JSScript::condenseTypes(JSContext *cx)
{
#ifdef JS_TYPE_INFERENCE
SweepObjectList(cx, objects);
js::types::CondenseTypeObjectList(cx, typeObjects);
if (varTypes) {
js::HashSet<JSScript *> condensed(cx);
if (!condensed.init())
JS_NOT_REACHED("FIXME");
unsigned num = 2 + nfixed + (fun ? fun->nargs : 0) + bindings.countUpvars();
for (unsigned i = 0; i < num; i++)
js::types::CondenseSweepTypeSet(cx, condensed, &varTypes[i]);
}
#endif
}
void
types::TypeCompartment::sweep(JSContext *cx)
JSScript::sweepTypes(JSContext *cx)
{
SweepObjectList(cx, objects);
}
#ifdef JS_TYPE_INFERENCE
SweepTypeObjectList(cx, typeObjects);
} /* namespace js */
if (types)
js::types::DestroyScriptTypes(cx, this);
#endif
}
namespace js {
namespace analyze {

View File

@ -4281,9 +4281,6 @@ JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent)
if (!FUN_FLAT_CLOSURE(fun))
return CloneFunctionObject(cx, fun, parent);
/* Throw away all inferred types about upvars in this script and its children. */
fun->script()->nukeUpvarTypes(cx);
/*
* A flat closure carries its own environment, so why clone it? In case
* someone wants to mutate its fixed slots or add ad-hoc properties. API
@ -4315,9 +4312,11 @@ JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent)
}
obj = obj->getParent();
}
if (!obj->getProperty(cx, r.front().id, clone->getFlatClosureUpvars() + i))
Value v;
if (!obj->getProperty(cx, r.front().id, &v))
return NULL;
fun->script()->typeSetUpvar(cx, i, v);
clone->getFlatClosureUpvars()[i] = v;
}
return clone;
@ -4413,7 +4412,7 @@ JS_TypeHandlerNew(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
TypeSet *prototypeTypes =
fun->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), true);
prototypeTypes->addNewObject(cx, fun, site->returnTypes);
prototypeTypes->addNewObject(cx, site->script, fun, site->returnTypes);
#endif
}
@ -4427,7 +4426,7 @@ JS_TypeHandlerThis(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *jssite)
if (site->isNew)
site->returnTypes->addType(cx, TYPE_UNKNOWN);
if (site->thisTypes)
site->thisTypes->addSubset(cx, site->pool(), site->returnTypes);
site->thisTypes->addSubset(cx, site->script, site->returnTypes);
else
site->returnTypes->addType(cx, site->thisType);
}
@ -5173,15 +5172,12 @@ EvaluateUCScriptForPrincipalsCommon(JSContext *cx, JSObject *obj,
LAST_FRAME_CHECKS(cx, script);
return false;
}
script->isUncachedEval = true;
JS_ASSERT(script->getVersion() == compileVersion);
bool ok = Execute(cx, obj, script, NULL, 0, Valueify(rval));
LAST_FRAME_CHECKS(cx, ok);
#ifdef JS_TYPE_INFERENCE
// Don't destroy the script yet :FIXME: bug 613221
return ok;
#endif
js_DestroyScript(cx, script);
return ok;
}

View File

@ -3113,7 +3113,7 @@ static void array_TypeSort(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite
if (site->returnTypes) {
if (site->isNew)
site->returnTypes->addType(cx, TYPE_UNKNOWN);
site->thisTypes->addSubset(cx, site->pool(), site->returnTypes);
site->thisTypes->addSubset(cx, site->script, site->returnTypes);
}
#endif
}
@ -3167,7 +3167,7 @@ static void array_TypeSplice(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsit
/* Treat the returned array the same as the 'this' array. */
if (site->isNew)
site->returnTypes->addType(cx, TYPE_UNKNOWN);
site->thisTypes->addSubset(cx, site->pool(), site->returnTypes);
site->thisTypes->addSubset(cx, site->script, site->returnTypes);
}
/* All arguments beyond the first two are new array elements. */
@ -3399,7 +3399,7 @@ static void array_TypeNew(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite *
// in the Array native itself.
if (site->argumentCount > 1) {
for (size_t ind = 0; ind < site->argumentCount; ind++)
site->argumentTypes[ind]->addSubset(cx, site->pool(), indexTypes);
site->argumentTypes[ind]->addSubset(cx, site->script, indexTypes);
}
#endif
}

View File

@ -484,6 +484,23 @@ JSCompartment::mark(JSTracer *trc)
emptyEnumeratorShape->trace(trc);
if (emptyWithShape)
emptyWithShape->trace(trc);
if (types.inferenceDepth) {
/* Mark all scripts and type objects in the compartment. */
/* :FIXME: can this list contain scriptsToGC? */
for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
JSScript *script = reinterpret_cast<JSScript *>(cursor);
js_TraceScript(trc, script);
}
types::TypeObject *obj = types.objects;
while (obj) {
if (!obj->marked)
obj->trace(trc);
obj = obj->next;
}
}
}
void
@ -505,14 +522,30 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
traceMonitor.sweep(cx);
#endif
if (!types.inferenceDepth) {
#ifdef JS_TYPE_INFERENCE
for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
JSScript *script = reinterpret_cast<JSScript *>(cursor);
if (script->types)
script->types->sweep(cx);
}
for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
JSScript *script = reinterpret_cast<JSScript *>(cursor);
script->condenseTypes(cx);
}
types::CondenseTypeObjectList(cx, types.objects);
for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
JSScript *script = reinterpret_cast<JSScript *>(cursor);
script->sweepTypes(cx);
}
#endif
types::SweepTypeObjectList(cx, types.objects);
/*
* Destroy eval'ed scripts, now that any type inference information referring
* to eval scripts has been removed.
*/
js_DestroyScriptsToGC(cx, this);
}
#if defined JS_METHODJIT && defined JS_MONOIC
/*
@ -546,7 +579,6 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
#endif
types.sweep(cx);
active = false;
}
@ -556,9 +588,6 @@ JSCompartment::purge(JSContext *cx)
freeLists.purge();
dtoaCache.purge();
/* Destroy eval'ed scripts. */
js_DestroyScriptsToGC(cx, this);
nativeIterCache.purge();
toSourceCache.clear();

View File

@ -1577,7 +1577,7 @@ JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp,
if (!script)
return false;
script->setTypeNesting(fp->script(), fp->pc(cx));
script->isUncachedEval = true;
bool ok = Execute(cx, scobj, script, fp, JSFRAME_DEBUGGER | JSFRAME_EVAL, Valueify(rval));
js_DestroyScript(cx, script);

View File

@ -1235,7 +1235,7 @@ SetCallArg(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
JSScript *script = fun->script();
if (script->types) {
jstype type = GetValueType(cx, *vp);
TypeSet *types = script->types->argTypes(i);
TypeSet *types = script->argTypes(i);
if (types && !types->hasType(type)) {
InferSpew(ISpewDynamic, "AddCallProperty: #%u arg%u: %s",
script->id(), i, TypeString(type));
@ -1329,7 +1329,7 @@ SetCallVar(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
JSScript *script = fun->script();
if (script->types) {
jstype type = GetValueType(cx, *vp);
TypeSet *types = script->types->localTypes(i);
TypeSet *types = script->localTypes(i);
if (types && !types->hasType(type)) {
InferSpew(ISpewDynamic, "AddCallProperty: #%u local%u: %s",
script->id(), i, TypeString(type));
@ -2693,11 +2693,8 @@ Function(JSContext *cx, uintN argc, Value *vp)
if (!chars)
return JS_FALSE;
JSBool res = Compiler::compileFunctionBody(cx, fun, principals, &bindings,
chars, length, filename, lineno, cx->findVersion());
if (res && fun->u.i.script->compileAndGo)
fun->u.i.script->setTypeNesting(caller->script(), caller->pc(cx));
return res;
return Compiler::compileFunctionBody(cx, fun, principals, &bindings,
chars, length, filename, lineno, cx->findVersion());
}
namespace js {
@ -2966,8 +2963,10 @@ js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen)
uintN level = fun->u.i.script->staticLevel;
JSUpvarArray *uva = fun->script()->upvars();
for (uint32 i = 0, n = uva->length; i < n; i++)
for (uint32 i = 0, n = uva->length; i < n; i++) {
upvars[i] = GetUpvar(cx, level, uva->vector[i]);
fun->script()->typeSetUpvar(cx, i, upvars[i]);
}
return closure;
}

View File

@ -1849,11 +1849,6 @@ js_DestroyScriptsToGC(JSContext *cx, JSCompartment *comp)
*listp = script->u.nextToGC;
script->u.nextToGC = NULL;
#ifdef JS_TYPE_INFERENCE
// :FIXME: bug 613221
continue;
#endif
js_DestroyCachedScript(cx, script);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -128,6 +128,31 @@ TypeIsObject(jstype type)
/* Get the type of a jsval, or zero for an unknown special value. */
inline jstype GetValueType(JSContext *cx, const Value &val);
/*
* Type inference memory management overview.
*
* Inference constructs a global web of constraints relating the contents of
* type sets particular to various scripts and type objects within a compartment.
* There are two issues at hand to manage inference memory: collecting
* the constraints, and collecting type sets (on TypeObject destruction).
*
* The constraints and types generated during analysis of a script depend entirely on
* that script's input type sets --- the types of its arguments, upvar locals,
* callee return values, object properties, and dynamic types (overflows, undefined
* reads, etc.). On a GC, we collect the analysis information for all scripts
* which have been analyzed, destroying the type constraints and intermediate
* type sets associated with stack values, and add new condensed constraints to
* the script's inputs which will trigger reanalysis and recompilation should
* that input change in the future.
*
* TypeObjects are collected when either the script they are associated with is
* destroyed or their prototype JSObject is destroyed.
*
* If a GC happens while we are in the middle of analysis or working with a TypeScript
* or TypeObject, we do not destroy/condense analysis information or collect any
* TypeObjects or JSScripts. This is controlled with AutoEnterTypeInference.
*/
/*
* A constraint which listens to additions to a type set and propagates those
* changes to other type sets.
@ -136,24 +161,27 @@ class TypeConstraint
{
public:
#ifdef DEBUG
static unsigned constraintCount;
unsigned id_;
const char *kind_;
unsigned id() const { return id_; }
const char *kind() const { return kind_; }
#else
unsigned id() const { return 0; }
const char *kind() const { return NULL; }
#endif
/* Next constraint listening to the same type set. */
TypeConstraint *next;
TypeConstraint(const char *kind) : next(NULL)
/*
* Script this constraint indicates an input for. If this constraint
* is not on an intermediate (script-local) type set, then during
* GC this will be replaced with a condensed input type constraint.
*/
JSScript *script;
TypeConstraint(const char *kind, JSScript *script)
: next(NULL), script(script)
{
JS_ASSERT(script);
#ifdef DEBUG
this->id_ = ++constraintCount;
this->kind_ = kind;
#endif
}
@ -164,8 +192,25 @@ public:
/*
* Mark the object containing the set this constraint is listening to
* as not a packed array and, possibly, not a dense array.
* This is only used for constraints attached to the index type set
* (JSID_VOID) of a TypeObject.
*/
virtual void arrayNotPacked(JSContext *cx, bool notDense) {}
/*
* Whether this is an input type constraint condensed from the original
* constraints generated during analysis of the associated script.
* If this type set changes then the script will be reanalyzed/recompiled
* should the type set change at all in the future.
*/
virtual bool condensed() { return false; }
/*
* If this is a persistent subset constraint, the object being propagated
* into. Such constraints describe relationships between TypeObject
* properties which are independent of the analysis of any script.
*/
virtual TypeObject * baseSubset() { return NULL; }
};
/*
@ -193,18 +238,6 @@ enum ObjectKind {
/* Information about the set of types associated with an lvalue. */
struct TypeSet
{
#ifdef DEBUG
static unsigned typesetCount;
unsigned id_;
/* Pool containing this type set. All constraints must also be in this pool. */
JSArenaPool *pool;
unsigned id() const { return id_; }
#else
unsigned id() const { return 0; }
#endif
/* Flags for the possible coarse types in this set. */
TypeFlags typeFlags;
@ -215,19 +248,9 @@ struct TypeSet
/* Chain of constraints which propagate changes out from this type set. */
TypeConstraint *constraintList;
TypeSet(JSArenaPool *pool)
TypeSet()
: typeFlags(0), objectSet(NULL), objectCount(0), constraintList(NULL)
{
setPool(pool);
}
void setPool(JSArenaPool *pool)
{
#if defined DEBUG && defined JS_TYPE_INFERENCE
this->id_ = ++typesetCount;
this->pool = pool;
#endif
}
{}
void print(JSContext *cx);
@ -244,7 +267,7 @@ struct TypeSet
/* Add specific kinds of constraints to this set. */
inline void add(JSContext *cx, TypeConstraint *constraint, bool callExisting = true);
void addSubset(JSContext *cx, JSArenaPool &pool, TypeSet *target);
void addSubset(JSContext *cx, JSScript *script, TypeSet *target);
void addGetProperty(JSContext *cx, JSScript *script, const jsbytecode *pc,
TypeSet *target, jsid id);
void addSetProperty(JSContext *cx, JSScript *script, const jsbytecode *pc,
@ -253,14 +276,17 @@ struct TypeSet
TypeSet *object, TypeSet *target);
void addSetElem(JSContext *cx, JSScript *script, const jsbytecode *pc,
TypeSet *object, TypeSet *target);
void addNewObject(JSContext *cx, TypeFunction *fun, TypeSet *target);
void addNewObject(JSContext *cx, JSScript *script, TypeFunction *fun, TypeSet *target);
void addCall(JSContext *cx, TypeCallsite *site);
void addArith(JSContext *cx, JSArenaPool &pool,
void addArith(JSContext *cx, JSScript *script,
TypeSet *target, TypeSet *other = NULL);
void addTransformThis(JSContext *cx, JSScript *script, TypeSet *target);
void addFilterPrimitives(JSContext *cx, JSArenaPool &pool,
void addFilterPrimitives(JSContext *cx, JSScript *script,
TypeSet *target, bool onlyNullVoid);
void addMonitorRead(JSContext *cx, JSArenaPool &pool, TypeSet *target);
void addMonitorRead(JSContext *cx, JSScript *script, TypeSet *target);
void addBaseSubset(JSContext *cx, TypeObject *object, TypeSet *target);
void addCondensed(JSContext *cx, JSScript *script);
/*
* Make an intermediate type set with the specified debugging name,
@ -296,8 +322,8 @@ struct Property
/* Types for this property resulting from direct sets on the object. */
TypeSet ownTypes;
Property(JSArenaPool *pool, jsid id)
: id(id), types(pool), ownTypes(pool)
Property(jsid id)
: id(id)
{}
static uint32 keyBits(jsid id) { return (uint32) JSID_BITS(id); }
@ -347,10 +373,9 @@ struct TypeObject
TypeObject *instanceNext;
/*
* Pool in which this object was allocated, and link in the list of objects
* for that pool.
* Link in the list of objects associated with a script or global object.
* For printing and tracking initializer objects (remove?).
*/
JSArenaPool *pool;
TypeObject *next;
/* Whether all the properties of this object are unknown. */
@ -365,7 +390,7 @@ struct TypeObject
TypeObject() {}
/* Make an object with the specified name. */
inline TypeObject(JSArenaPool *pool, jsid id, JSObject *proto);
inline TypeObject(jsid id, JSObject *proto);
/* Coerce this object to a function. */
TypeFunction* asFunction()
@ -423,12 +448,6 @@ struct TypeFunction : public TypeObject
/* If this function is interpreted, the corresponding script. */
JSScript *script;
/*
* For interpreted functions and functions with dynamic handlers, the possible
* return types of the function.
*/
TypeSet returnTypes;
/*
* Whether this is a generic native handler, and treats its first parameter
* the way it normally would its 'this' variable, e.g. Array.reverse(arr)
@ -436,7 +455,7 @@ struct TypeFunction : public TypeObject
*/
bool isGeneric;
inline TypeFunction(JSArenaPool *pool, jsid id, JSObject *proto);
inline TypeFunction(jsid id, JSObject *proto);
};
/*
@ -475,12 +494,29 @@ struct TypeCallsite
/* Get the new object at this callsite. */
inline TypeObject* getInitObject(JSContext *cx, bool isArray);
/* Pool which handlers on this call site should use. */
inline JSArenaPool & pool();
inline bool compileAndGo();
};
/*
* Type information about a dynamic value pushed by a script's opcode.
* These are associated with each JSScript and persist after the
* TypeScript is destroyed by GCs.
*/
struct TypeResult
{
/*
* Offset pushing the value. TypeResults are only generated for
* the first stack slot actually pushed by a bytecode.
*/
uint32 offset;
/* Type which was pushed. */
jstype type;
/* Next dynamic result for the script. */
TypeResult *next;
};
/* Type information for a script, result of AnalyzeTypes. */
struct TypeScript
{
@ -489,11 +525,10 @@ struct TypeScript
#endif
/*
* Pool into which type sets, constraints, and type objects associated with this
* script are allocated.
* Pool into which intermediate type sets and all type constraints are allocated
* during analysis of the script.
*/
JSArenaPool pool;
TypeObject *objects;
/*
* Stack values pushed by all bytecodes in the script. Low bit is set for
@ -501,13 +536,6 @@ struct TypeScript
*/
TypeSet **pushedArray;
/* Types of the 'this' variable, arguments and locals in this script. */
TypeSet thisTypes;
TypeSet *argTypes_;
TypeSet *localTypes_;
void nukeUpvarTypes(JSContext *cx, JSScript *script);
/* Gather statistics off this script and print it if necessary. */
void finish(JSContext *cx, JSScript *script);
@ -518,27 +546,23 @@ struct TypeScript
inline TypeSet *pushed(uint32 offset, uint32 index);
inline void addType(JSContext *cx, uint32 offset, uint32 index, jstype type);
inline TypeSet *argTypes(uint32 arg);
inline TypeSet *localTypes(uint32 local);
void trace(JSTracer *trc);
void sweep(JSContext *cx);
};
/* Analyzes all types in script, constructing its TypeScript. */
void AnalyzeTypes(JSContext *cx, JSScript *script);
void AnalyzeScriptTypes(JSContext *cx, JSScript *script);
/* Destroy the TypeScript associated with a script. */
void DestroyScriptTypes(JSContext *cx, JSScript *script);
/* Type information for a compartment. */
struct TypeCompartment
{
/*
* Pool for compartment-wide objects and their variables and constraints.
* These aren't collected until the compartment is destroyed.
*/
JSArenaPool pool;
/* List of objects not associated with a script. */
TypeObject *objects;
/* Number of active instances of AutoEnterTypeInference. */
unsigned inferenceDepth;
/* Number of scripts in this compartment. */
unsigned scriptCount;
@ -592,7 +616,6 @@ struct TypeCompartment
unsigned recompilations;
void init();
~TypeCompartment();
uint64 currentTime()
{
@ -608,7 +631,7 @@ struct TypeCompartment
/* Add a type to register with a list of constraints. */
inline void addPending(JSContext *cx, TypeConstraint *constraint, TypeSet *source, jstype type);
void growPendingArray();
void growPendingArray(JSContext *cx);
/* Resolve pending type registrations, excluding delayed ones. */
inline void resolvePending(JSContext *cx);
@ -617,7 +640,7 @@ struct TypeCompartment
void finish(JSContext *cx, JSCompartment *compartment);
/* Make a function or non-function object associated with an optional script. */
TypeObject *newTypeObject(JSContext *cx, TypeScript *script,
TypeObject *newTypeObject(JSContext *cx, JSScript *script,
const char *name, bool isFunction, JSObject *proto);
#ifdef JS_TYPE_INFERENCE
@ -631,8 +654,7 @@ struct TypeCompartment
* stemming from the change and recompile any affected scripts.
*/
void addDynamicType(JSContext *cx, TypeSet *types, jstype type);
void addDynamicPush(JSContext *cx, JSScript *script, uint32 offset,
unsigned index, jstype type);
void addDynamicPush(JSContext *cx, JSScript *script, uint32 offset, jstype type);
void dynamicAssign(JSContext *cx, JSObject *obj, jsid id, const Value &rval);
inline bool hasPendingRecompiles() { return pendingRecompiles != NULL; }
@ -641,10 +663,11 @@ struct TypeCompartment
/* Monitor future effects on a bytecode. */
void monitorBytecode(JSContext *cx, JSScript *script, uint32 offset);
void sweep(JSContext *cx);
};
void CondenseTypeObjectList(JSContext *cx, TypeObject *objects);
void SweepTypeObjectList(JSContext *cx, TypeObject *&objects);
enum SpewChannel {
ISpewDynamic, /* dynamic: Dynamic type changes and inference entry points. */
ISpewOps, /* ops: New constraints and types. */

View File

@ -207,7 +207,7 @@ JSContext::markTypeCallerUnexpected(js::types::jstype type)
JSStackFrame *caller = js_GetScriptedCaller(this, NULL);
if (!caller)
return;
caller->script()->typeMonitorResult(this, caller->pc(this), 0, type);
caller->script()->typeMonitorResult(this, caller->pc(this), type);
#endif
}
@ -314,8 +314,8 @@ JSContext::aliasTypeProperties(js::types::TypeObject *obj, jsid first, jsid seco
js::types::TypeSet *firstTypes = obj->getProperty(this, first, true);
js::types::TypeSet *secondTypes = obj->getProperty(this, second, true);
firstTypes->addSubset(this, *obj->pool, secondTypes);
secondTypes->addSubset(this, *obj->pool, firstTypes);
firstTypes->addBaseSubset(this, obj, secondTypes);
secondTypes->addBaseSubset(this, obj, firstTypes);
#endif
}
@ -382,8 +382,8 @@ JSContext::typeMonitorCall(JSScript *caller, const jsbytecode *callerpc,
JSScript *script = callee->script();
typeMonitorEntry(script);
if (!force && caller->types->monitored(callerpc - caller->code))
force = true;
if (!force)
force = !caller->types || caller->types->monitored(callerpc - caller->code);
/* Don't need to do anything if this is at a non-monitored callsite. */
if (!script->types || !force)
@ -408,10 +408,10 @@ JSContext::typeMonitorCall(JSScript *caller, const jsbytecode *callerpc,
type = js::types::GetValueType(this, args.thisv());
}
if (!script->types->thisTypes.hasType(type)) {
if (!script->thisTypes()->hasType(type)) {
js::types::InferSpew(js::types::ISpewDynamic, "AddThis: #%u: %s",
script->id(), js::types::TypeString(type));
compartment->types.addDynamicType(this, &script->types->thisTypes, type);
compartment->types.addDynamicType(this, script->thisTypes(), type);
}
/*
@ -422,7 +422,7 @@ JSContext::typeMonitorCall(JSScript *caller, const jsbytecode *callerpc,
unsigned arg = 0;
for (; arg < args.argc() && arg < callee->nargs; arg++) {
js::types::jstype type = js::types::GetValueType(this, args[arg]);
js::types::TypeSet *types = script->types->argTypes(arg);
js::types::TypeSet *types = script->argTypes(arg);
if (!types->hasType(type)) {
js::types::InferSpew(js::types::ISpewDynamic, "AddArg: #%u %u: %s",
script->id(), arg, js::types::TypeString(type));
@ -432,7 +432,7 @@ JSContext::typeMonitorCall(JSScript *caller, const jsbytecode *callerpc,
/* Watch for fewer actuals than formals to the call. */
for (; arg < callee->nargs; arg++) {
js::types::TypeSet *types = script->types->argTypes(arg);
js::types::TypeSet *types = script->argTypes(arg);
if (!types->hasType(js::types::TYPE_UNDEFINED)) {
js::types::InferSpew(js::types::ISpewDynamic,
"UndefinedArg: #%u %u:", script->id(), arg);
@ -446,13 +446,12 @@ inline void
JSContext::typeMonitorEntry(JSScript *script)
{
#ifdef JS_TYPE_INFERENCE
if (!script->types) {
if (!script->analyzed && !script->isUncachedEval) {
compartment->types.interpreting = false;
uint64_t startTime = compartment->types.currentTime();
js::types::InferSpew(js::types::ISpewDynamic, "EntryPoint: #%lu", script->id());
js::types::AnalyzeTypes(this, script);
js::types::AnalyzeScriptTypes(this, script);
uint64_t endTime = compartment->types.currentTime();
compartment->types.analysisTime += (endTime - startTime);
@ -464,19 +463,23 @@ JSContext::typeMonitorEntry(JSScript *script)
#endif
}
/* :FIXME: return success indicator. */
inline void
JSContext::typeMonitorEntry(JSScript *script, const js::Value &thisv)
{
#ifdef JS_TYPE_INFERENCE
if (!script->ensureVarTypes(this))
return;
typeMonitorEntry(script);
if (!script->types)
return;
js::types::jstype type = js::types::GetValueType(this, thisv);
if (!script->types->thisTypes.hasType(type)) {
if (!script->thisTypes()->hasType(type)) {
js::types::InferSpew(js::types::ISpewDynamic, "AddThis: #%u: %s",
script->id(), js::types::TypeString(type));
compartment->types.addDynamicType(this, &script->types->thisTypes, type);
compartment->types.addDynamicType(this, script->thisTypes(), type);
}
typeMonitorEntry(script);
@ -489,28 +492,58 @@ JSContext::typeMonitorEntry(JSScript *script, const js::Value &thisv)
#ifdef JS_TYPE_INFERENCE
inline bool
JSScript::ensureVarTypes(JSContext *cx)
{
if (varTypes)
return true;
return makeVarTypes(cx);
}
inline js::types::TypeSet *
JSScript::returnTypes()
{
JS_ASSERT(varTypes);
return &varTypes[0];
}
inline js::types::TypeSet *
JSScript::thisTypes()
{
JS_ASSERT(varTypes);
return &varTypes[1];
}
inline js::types::TypeSet *
JSScript::argTypes(unsigned i)
{
JS_ASSERT(varTypes && fun && i < fun->nargs);
return &varTypes[2 + i];
}
inline js::types::TypeSet *
JSScript::localTypes(unsigned i)
{
JS_ASSERT(varTypes && i < nfixed);
if (fun)
i += fun->nargs;
return &varTypes[2 + i];
}
inline js::types::TypeSet *
JSScript::upvarTypes(unsigned i)
{
JS_ASSERT(varTypes && i < bindings.countUpvars());
if (fun)
i += fun->nargs;
return &varTypes[2 + nfixed + i];
}
inline JSObject *
JSScript::getGlobal()
{
JS_ASSERT(compileAndGo);
if (global)
return global;
/*
* Nesting parents of this script must also be compileAndGo for the same global.
* The parser must have set the global object for the analysis at the root
* global script.
*/
JSScript *nested = parent;
while (true) {
JS_ASSERT(nested->compileAndGo);
if (nested->global) {
global = nested->global;
return global;
}
nested = nested->parent;
}
return NULL;
JS_ASSERT(compileAndGo && global);
return global;
}
inline js::types::TypeObject *
@ -530,43 +563,23 @@ JSScript::getTypeNewObject(JSContext *cx, JSProtoKey key)
#endif /* JS_TYPE_INFERENCE */
inline void
JSScript::setTypeNesting(JSScript *parent, const jsbytecode *pc)
{
#ifdef JS_TYPE_INFERENCE
this->parent = parent;
#endif
}
inline void
JSScript::nukeUpvarTypes(JSContext *cx)
{
#ifdef JS_TYPE_INFERENCE
if (this->parent) {
if (!types)
js::types::AnalyzeTypes(cx, this);
types->nukeUpvarTypes(cx, this);
}
#endif
}
inline js::types::TypeObject *
JSScript::getTypeInitObject(JSContext *cx, const jsbytecode *pc, bool isArray)
{
#ifdef JS_TYPE_INFERENCE
if (!compileAndGo || !types)
if (!compileAndGo)
return cx->getTypeNewObject(isArray ? JSProto_Array : JSProto_Object);
uint32 offset = pc - code;
js::types::TypeObject *prev = NULL, *obj = types->objects;
js::types::TypeObject *prev = NULL, *obj = typeObjects;
while (obj) {
if (isArray ? obj->initializerArray : obj->initializerObject) {
if (obj->initializerOffset == offset) {
/* Move this to the head of the objects list, maintain LRU order. */
if (prev) {
prev->next = obj->next;
obj->next = types->objects;
types->objects = obj;
obj->next = typeObjects;
typeObjects = obj;
}
return obj;
}
@ -582,40 +595,63 @@ JSScript::getTypeInitObject(JSContext *cx, const jsbytecode *pc, bool isArray)
}
inline void
JSScript::typeMonitorResult(JSContext *cx, const jsbytecode *pc, unsigned index,
JSScript::typeMonitorResult(JSContext *cx, const jsbytecode *pc,
js::types::jstype type)
{
#ifdef JS_TYPE_INFERENCE
if (!types)
return;
JS_ASSERT(index < js::analyze::GetDefCount(this, pc - code));
js::types::TypeSet *types = this->types->pushed(pc - code, index);
if (!types->hasType(type))
cx->compartment->types.addDynamicPush(cx, this, pc - code, index, type);
if (types) {
/*
* There is a TypeResult iff the type is in the pushed set.
* The latter is easier to check.
*/
js::types::TypeSet *pushed = types->pushed(pc - code, 0);
if (!pushed->hasType(type))
cx->compartment->types.addDynamicPush(cx, this, pc - code, type);
} else {
/* Scan all TypeResults on the script to check for a duplicate. */
js::types::TypeResult *result, **presult = &typeResults;
while (*presult) {
result = *presult;
if (result->offset == uint32(pc - code) && result->type == type) {
if (presult != &typeResults) {
/* Move this result to the head of the list, maintain LRU order. */
*presult = result->next;
result->next = typeResults;
typeResults = result;
}
return;
}
presult = &result->next;
}
cx->compartment->types.addDynamicPush(cx, this, pc - code, type);
}
#endif
}
inline void
JSScript::typeMonitorResult(JSContext *cx, const jsbytecode *pc, unsigned index,
const js::Value &rval)
JSScript::typeMonitorResult(JSContext *cx, const jsbytecode *pc, const js::Value &rval)
{
#ifdef JS_TYPE_INFERENCE
typeMonitorResult(cx, pc, index, js::types::GetValueType(cx, rval));
typeMonitorResult(cx, pc, js::types::GetValueType(cx, rval));
#endif
}
inline void
JSScript::typeMonitorOverflow(JSContext *cx, const jsbytecode *pc, unsigned index)
JSScript::typeMonitorOverflow(JSContext *cx, const jsbytecode *pc)
{
typeMonitorResult(cx, pc, index, js::types::TYPE_DOUBLE);
typeMonitorResult(cx, pc, js::types::TYPE_DOUBLE);
}
inline void
JSScript::typeMonitorUndefined(JSContext *cx, const jsbytecode *pc, unsigned index)
JSScript::typeMonitorUndefined(JSContext *cx, const jsbytecode *pc)
{
typeMonitorResult(cx, pc, index, js::types::TYPE_UNDEFINED);
typeMonitorResult(cx, pc, js::types::TYPE_UNDEFINED);
}
inline void
JSScript::typeMonitorUnknown(JSContext *cx, const jsbytecode *pc)
{
typeMonitorResult(cx, pc, js::types::TYPE_UNKNOWN);
}
inline void
@ -635,14 +671,28 @@ inline void
JSScript::typeSetArgument(JSContext *cx, unsigned arg, const js::Value &value)
{
#ifdef JS_TYPE_INFERENCE
if (!types)
return;
js::types::TypeSet *argTypes = types->argTypes(arg);
if (!ensureVarTypes(cx))
JS_NOT_REACHED("FIXME");
js::types::jstype type = js::types::GetValueType(cx, value);
if (!argTypes->hasType(type)) {
if (!argTypes(arg)->hasType(type)) {
js::types::InferSpew(js::types::ISpewDynamic, "SetArgument: #%u %u: %s",
id(), arg, js::types::TypeString(type));
cx->compartment->types.addDynamicType(cx, argTypes, type);
cx->compartment->types.addDynamicType(cx, argTypes(arg), type);
}
#endif
}
inline void
JSScript::typeSetUpvar(JSContext *cx, unsigned upvar, const js::Value &value)
{
#ifdef JS_TYPE_INFERENCE
if (!ensureVarTypes(cx))
JS_NOT_REACHED("FIXME");
js::types::jstype type = js::types::GetValueType(cx, value);
if (!upvarTypes(upvar)->hasType(type)) {
js::types::InferSpew(js::types::ISpewDynamic, "SetUpvar: #%u %u: %s",
id(), upvar, js::types::TypeString(type));
cx->compartment->types.addDynamicType(cx, upvarTypes(upvar), type);
}
#endif
}
@ -656,16 +706,38 @@ namespace types {
// TypeCompartment
/////////////////////////////////////////////////////////////////////
/*
* Pin inference results so that they won't be collected during GC.
* This also prevents TypeObjects and JSScripts from being collected,
* and should be used sparingly.
*/
struct AutoEnterTypeInference
{
JSContext *cx;
AutoEnterTypeInference(JSContext *cx)
: cx(cx)
{
cx->compartment->types.inferenceDepth++;
}
~AutoEnterTypeInference()
{
JS_ASSERT(cx->compartment->types.inferenceDepth);
cx->compartment->types.inferenceDepth--;
}
};
inline void
TypeCompartment::addPending(JSContext *cx, TypeConstraint *constraint, TypeSet *source, jstype type)
{
JS_ASSERT(this == &cx->compartment->types);
JS_ASSERT(type);
InferSpew(ISpewOps, "pending: C%u %s", constraint->id(), TypeString(type));
InferSpew(ISpewOps, "pending: C%p %s", constraint, TypeString(type));
if (pendingCount == pendingCapacity)
growPendingArray();
growPendingArray(cx);
PendingWork &pending = pendingArray[pendingCount++];
pending.constraint = constraint;
@ -688,8 +760,8 @@ TypeCompartment::resolvePending(JSContext *cx)
/* Handle all pending type registrations. */
while (pendingCount) {
const PendingWork &pending = pendingArray[--pendingCount];
InferSpew(ISpewOps, "resolve: C%u %s",
pending.constraint->id(), TypeString(pending.type));
InferSpew(ISpewOps, "resolve: C%p %s",
pending.constraint, TypeString(pending.type));
pending.constraint->newType(cx, pending.source, pending.type);
}
@ -874,7 +946,9 @@ TypeSet::hasType(jstype type)
if (unknown())
return true;
if (TypeIsPrimitive(type)) {
if (type == TYPE_UNKNOWN) {
return false;
} else if (TypeIsPrimitive(type)) {
return ((1 << type) & typeFlags) != 0;
} else {
return HashSetLookup<TypeObject*,TypeObject,TypeObjectKey>
@ -887,7 +961,7 @@ TypeSet::addType(JSContext *cx, jstype type)
{
JS_ASSERT(type);
JS_ASSERT_IF(unknown(), typeFlags == TYPE_FLAG_UNKNOWN);
InferSpew(ISpewOps, "addType: T%u %s", id(), TypeString(type));
InferSpew(ISpewOps, "addType: T%p %s", this, TypeString(type));
if (unknown())
return;
@ -952,8 +1026,8 @@ TypeSet::addType(JSContext *cx, jstype type)
inline TypeSet *
TypeSet::make(JSContext *cx, JSArenaPool &pool, const char *name)
{
TypeSet *res = ArenaNew<TypeSet>(pool, &pool);
InferSpew(ISpewOps, "intermediate %s T%u", name, res->id());
TypeSet *res = ArenaNew<TypeSet>(pool);
InferSpew(ISpewOps, "intermediate %s T%p", name, res);
return res;
}
@ -994,12 +1068,6 @@ TypeCallsite::getInitObject(JSContext *cx, bool isArray)
return script->getTypeInitObject(cx, pc, isArray);
}
inline JSArenaPool &
TypeCallsite::pool()
{
return script->types->pool;
}
inline bool
TypeCallsite::compileAndGo()
{
@ -1056,20 +1124,6 @@ TypeScript::pushed(uint32 offset, uint32 index)
return pushed(offset) + index;
}
inline TypeSet *
TypeScript::argTypes(uint32 arg)
{
JS_ASSERT(script->fun && arg < script->fun->nargs);
return &argTypes_[arg];
}
inline TypeSet *
TypeScript::localTypes(uint32 local)
{
JS_ASSERT(local < script->nfixed);
return &localTypes_[local];
}
inline void
TypeScript::addType(JSContext *cx, uint32 offset, uint32 index, jstype type)
{
@ -1094,11 +1148,11 @@ TypeObject::name()
#endif
}
inline TypeObject::TypeObject(JSArenaPool *pool, jsid name, JSObject *proto)
inline TypeObject::TypeObject(jsid name, JSObject *proto)
: proto(proto), emptyShapes(NULL), isFunction(false), marked(false),
initializerObject(false), initializerArray(false), initializerOffset(0),
propertySet(NULL), propertyCount(0),
instanceList(NULL), instanceNext(NULL), pool(pool), next(NULL), unknownProperties(false),
instanceList(NULL), instanceNext(NULL), next(NULL), unknownProperties(false),
isDenseArray(false), isPackedArray(false)
{
#ifdef DEBUG
@ -1130,15 +1184,10 @@ inline TypeObject::TypeObject(JSArenaPool *pool, jsid name, JSObject *proto)
}
}
inline TypeFunction::TypeFunction(JSArenaPool *pool, jsid name, JSObject *proto)
: TypeObject(pool, name, proto), handler(NULL), script(NULL),
returnTypes(pool), isGeneric(false)
inline TypeFunction::TypeFunction(jsid name, JSObject *proto)
: TypeObject(name, proto), handler(NULL), script(NULL), isGeneric(false)
{
isFunction = true;
#ifdef JS_TYPE_INFERENCE
InferSpew(ISpewOps, "newFunction: %s return T%u", this->name(), returnTypes.id());
#endif
}
} } /* namespace js::types */

View File

@ -583,7 +583,7 @@ js_OnUnknownMethod(JSContext *cx, Value *vp)
obj->setSlot(JSSLOT_SAVED_ID, vp[0]);
vp[0].setObject(*obj);
}
cx->fp()->script()->typeMonitorResult(cx, cx->regs->pc, 0, *vp);
cx->fp()->script()->typeMonitorUnknown(cx, cx->regs->pc);
return true;
}
@ -786,15 +786,15 @@ InvokeSessionGuard::start(JSContext *cx, const Value &calleev, const Value &this
/* Mark the shared 'this' type. */
jstype type = GetValueType(cx, thisv);
if (!script_->types->thisTypes.hasType(type)) {
if (!script_->thisTypes()->hasType(type)) {
InferSpew(ISpewDynamic, "AddThis: #%u: %s",
script_->id(), TypeString(type));
cx->compartment->types.addDynamicType(cx, &script_->types->thisTypes, type);
cx->compartment->types.addDynamicType(cx, script_->thisTypes(), type);
}
/* Mark all formal arguments as unknown. */
for (unsigned arg = 0; arg < fun->nargs; arg++) {
TypeSet *types = script_->types->argTypes(arg);
TypeSet *types = script_->argTypes(arg);
if (!types->unknown()) {
InferSpew(ISpewDynamic, "AddArgUnknown: #%u %u", script_->id(), arg);
cx->compartment->types.addDynamicType(cx, types, TYPE_UNKNOWN);
@ -3228,9 +3228,9 @@ BEGIN_CASE(JSOP_FORGNAME)
JS_ASSERT(regs.sp[-1].isObject());
if (!IteratorNext(cx, &regs.sp[-1].toObject(), tvr.addr()))
goto error;
script->typeMonitorAssign(cx, regs.pc, obj, id, tvr.value());
if (!obj->setProperty(cx, id, tvr.addr(), script->strictModeCode))
goto error;
script->typeMonitorAssign(cx, regs.pc, obj, id, tvr.value());
}
}
END_CASE(JSOP_FORNAME)
@ -3248,9 +3248,9 @@ BEGIN_CASE(JSOP_FORPROP)
JS_ASSERT(regs.sp[-2].isObject());
if (!IteratorNext(cx, &regs.sp[-2].toObject(), tvr.addr()))
goto error;
script->typeMonitorAssign(cx, regs.pc, obj, id, tvr.value());
if (!obj->setProperty(cx, id, tvr.addr(), script->strictModeCode))
goto error;
script->typeMonitorAssign(cx, regs.pc, obj, id, tvr.value());
}
regs.sp--;
}
@ -3374,6 +3374,9 @@ BEGIN_CASE(JSOP_SETCONST)
LOAD_ATOM(0, atom);
JSObject &obj = regs.fp->varobj(cx);
const Value &ref = regs.sp[-1];
script->typeMonitorAssign(cx, regs.pc, &obj, ATOM_TO_JSID(atom), ref);
if (!obj.defineProperty(cx, ATOM_TO_JSID(atom), ref,
PropertyStub, StrictPropertyStub,
JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY)) {
@ -3705,7 +3708,7 @@ BEGIN_CASE(JSOP_URSH)
regs.sp--;
if (!regs.sp[-1].setNumber(uint32(u)))
script->typeMonitorOverflow(cx, regs.pc, 0);
script->typeMonitorOverflow(cx, regs.pc);
}
END_CASE(JSOP_URSH)
@ -3720,7 +3723,7 @@ BEGIN_CASE(JSOP_ADD)
regs.sp--;
if (JS_UNLIKELY(bool((l ^ sum) & (r ^ sum) & 0x80000000))) {
regs.sp[-1].setDouble(double(l) + double(r));
script->typeMonitorOverflow(cx, regs.pc, 0);
script->typeMonitorOverflow(cx, regs.pc);
} else {
regs.sp[-1].setInt32(sum);
}
@ -3768,8 +3771,8 @@ BEGIN_CASE(JSOP_ADD)
goto error;
l += r;
regs.sp--;
if (!regs.sp[-1].setNumber(l))
script->typeMonitorOverflow(cx, regs.pc, 0);
if (!regs.sp[-1].setNumber(l) && !(lval.isDouble() || rval.isDouble()))
script->typeMonitorOverflow(cx, regs.pc);
}
}
}
@ -3777,15 +3780,19 @@ END_CASE(JSOP_ADD)
#define BINARY_OP(OP) \
JS_BEGIN_MACRO \
Value rval = regs.sp[-1]; \
Value lval = regs.sp[-2]; \
double d1, d2; \
if (!ValueToNumber(cx, regs.sp[-2], &d1) || \
!ValueToNumber(cx, regs.sp[-1], &d2)) { \
if (!ValueToNumber(cx, lval, &d1) || \
!ValueToNumber(cx, rval, &d2)) { \
goto error; \
} \
double d = d1 OP d2; \
regs.sp--; \
if (!regs.sp[-1].setNumber(d)) \
script->typeMonitorOverflow(cx, regs.pc, 0); \
if (!regs.sp[-1].setNumber(d) && \
!(lval.isDouble() || rval.isDouble())) { \
script->typeMonitorOverflow(cx, regs.pc); \
} \
JS_END_MACRO
BEGIN_CASE(JSOP_SUB)
@ -3800,9 +3807,11 @@ END_CASE(JSOP_MUL)
BEGIN_CASE(JSOP_DIV)
{
Value rval = regs.sp[-1];
Value lval = regs.sp[-2];
double d1, d2;
if (!ValueToNumber(cx, regs.sp[-2], &d1) ||
!ValueToNumber(cx, regs.sp[-1], &d2)) {
if (!ValueToNumber(cx, lval, &d1) ||
!ValueToNumber(cx, rval, &d2)) {
goto error;
}
regs.sp--;
@ -3821,11 +3830,11 @@ BEGIN_CASE(JSOP_DIV)
else
vp = &rt->positiveInfinityValue;
regs.sp[-1] = *vp;
script->typeMonitorOverflow(cx, regs.pc, 0);
script->typeMonitorOverflow(cx, regs.pc);
} else {
d1 /= d2;
if (!regs.sp[-1].setNumber(d1))
script->typeMonitorOverflow(cx, regs.pc, 0);
if (!regs.sp[-1].setNumber(d1) && !(lval.isDouble() || rval.isDouble()))
script->typeMonitorOverflow(cx, regs.pc);
}
}
END_CASE(JSOP_DIV)
@ -3853,7 +3862,7 @@ BEGIN_CASE(JSOP_MOD)
d1 = js_fmod(d1, d2);
regs.sp[-1].setDouble(d1);
}
script->typeMonitorOverflow(cx, regs.pc, 0);
script->typeMonitorOverflow(cx, regs.pc);
}
}
END_CASE(JSOP_MOD)
@ -3884,7 +3893,7 @@ BEGIN_CASE(JSOP_NEG)
* INT32_FITS_IN_JSVAL(-i) unless i is 0 or INT32_MIN when the
* results, -0.0 or INT32_MAX + 1, are jsdouble values.
*/
const Value &ref = regs.sp[-1];
Value ref = regs.sp[-1];
int32_t i;
if (ref.isInt32() && (i = ref.toInt32()) != 0 && i != INT32_MIN) {
i = -i;
@ -3894,8 +3903,8 @@ BEGIN_CASE(JSOP_NEG)
if (!ValueToNumber(cx, regs.sp[-1], &d))
goto error;
d = -d;
if (!regs.sp[-1].setNumber(d))
script->typeMonitorOverflow(cx, regs.pc, 0);
if (!regs.sp[-1].setNumber(d) && !ref.isDouble())
script->typeMonitorOverflow(cx, regs.pc);
}
}
END_CASE(JSOP_NEG)
@ -3904,7 +3913,7 @@ BEGIN_CASE(JSOP_POS)
if (!ValueToNumber(cx, &regs.sp[-1]))
goto error;
if (!regs.sp[-1].isInt32())
script->typeMonitorOverflow(cx, regs.pc, 0);
script->typeMonitorOverflow(cx, regs.pc);
END_CASE(JSOP_POS)
BEGIN_CASE(JSOP_DELNAME)
@ -4167,7 +4176,7 @@ BEGIN_CASE(JSOP_LOCALINC)
if (!js_DoIncDec(cx, &js_CodeSpec[op], &regs.sp[-1], vp))
goto error;
if (!vp->isInt32())
script->typeMonitorOverflow(cx, regs.pc, 0);
script->typeMonitorOverflow(cx, regs.pc);
}
len = JSOP_INCARG_LENGTH;
JS_ASSERT(len == js_CodeSpec[op].length);
@ -4278,7 +4287,7 @@ BEGIN_CASE(JSOP_GETXPROP)
} while (0);
if (rval.isUndefined())
script->typeMonitorUndefined(cx, regs.pc, 0);
script->typeMonitorUndefined(cx, regs.pc);
regs.sp[-1] = rval;
assertSameCompartment(cx, regs.sp[-1]);
@ -4401,7 +4410,7 @@ BEGIN_CASE(JSOP_CALLPROP)
}
#endif
if (rval.isUndefined())
script->typeMonitorUndefined(cx, regs.pc, 0);
script->typeMonitorUndefined(cx, regs.pc);
}
END_CASE(JSOP_CALLPROP)
@ -4637,10 +4646,11 @@ BEGIN_CASE(JSOP_GETELEM)
regs.sp--;
regs.sp[-1] = *copyFrom;
assertSameCompartment(cx, regs.sp[-1]);
if (copyFrom->isUndefined() || !rref.isInt32()) {
if (rref.isInt32())
cx->addTypeProperty(obj->getType(), NULL, TYPE_UNDEFINED);
script->typeMonitorResult(cx, regs.pc, 0, *copyFrom);
if (!rref.isInt32()) {
script->typeMonitorUnknown(cx, regs.pc);
} else if (copyFrom->isUndefined()) {
cx->addTypeProperty(obj->getType(), NULL, TYPE_UNDEFINED);
script->typeMonitorUndefined(cx, regs.pc);
}
}
END_CASE(JSOP_GETELEM)
@ -4673,8 +4683,9 @@ BEGIN_CASE(JSOP_CALLELEM)
{
regs.sp[-1] = thisv;
}
if (regs.sp[-2].isUndefined() || !JSID_IS_INT(id))
script->typeMonitorResult(cx, regs.pc, 0, regs.sp[-2]);
script->typeMonitorUnknown(cx, regs.pc);
}
END_CASE(JSOP_CALLELEM)
@ -4980,7 +4991,7 @@ BEGIN_CASE(JSOP_CALLNAME)
PUSH_COPY(rval);
if (rval.isUndefined())
script->typeMonitorUndefined(cx, regs.pc, 0);
script->typeMonitorUndefined(cx, regs.pc);
/* obj must be on the scope chain, thus not a function. */
if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME)
@ -5378,9 +5389,6 @@ BEGIN_CASE(JSOP_CALLUPVAR_DBG)
if (!obj->getProperty(cx, id, vp))
goto error;
if (vp->isUndefined())
script->typeMonitorUndefined(cx, regs.pc, 0);
if (op == JSOP_CALLUPVAR_DBG)
PUSH_UNDEFINED();
}
@ -5409,7 +5417,7 @@ BEGIN_CASE(JSOP_CALLGLOBAL)
JS_ASSERT(obj->containsSlot(slot));
PUSH_COPY(obj->getSlot(slot));
if (regs.sp[-1].isUndefined())
script->typeMonitorUndefined(cx, regs.pc, 0);
script->typeMonitorUndefined(cx, regs.pc);
if (op == JSOP_CALLGLOBAL)
PUSH_UNDEFINED();
}
@ -5529,6 +5537,8 @@ BEGIN_CASE(JSOP_DEFFUN)
Value rval = ObjectValue(*obj);
script->typeMonitorAssign(cx, regs.pc, parent, id, rval);
do {
/* Steps 5d, 5f. */
if (!prop || pobj != parent) {
@ -5567,8 +5577,6 @@ BEGIN_CASE(JSOP_DEFFUN)
/* Step 5f. */
if (!parent->setProperty(cx, id, &rval, script->strictModeCode))
goto error;
script->typeMonitorAssign(cx, regs.pc, parent, id, rval);
} while (false);
}
END_CASE(JSOP_DEFFUN)

View File

@ -400,7 +400,7 @@ GetCustomIterator(JSContext *cx, JSObject *obj, uintN flags, Value *vp)
*/
if (!(flags & JSITER_OWNONLY)) {
JS_ASSERT(JSOp(*cx->regs->pc) == JSOP_ITER);
cx->fp()->script()->typeMonitorResult(cx, cx->regs->pc, 0, (jstype) cx->getTypeGetSet());
cx->fp()->script()->typeMonitorResult(cx, cx->regs->pc, (jstype) cx->getTypeGetSet());
}
return true;
}
@ -1251,7 +1251,7 @@ SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj,
JS_ASSERT(JSOp(*yieldpc) == JSOP_YIELD);
JSScript *script = gen->floatingFrame()->script();
script->typeMonitorResult(cx, yieldpc, 0, arg);
script->typeMonitorUnknown(cx, yieldpc);
}
gen->state = JSGEN_RUNNING;
break;

View File

@ -526,7 +526,7 @@ math_pow(JSContext *cx, uintN argc, Value *vp)
z = pow(x, y);
vp->setNumber(z);
if (vp->isDouble() && (!vp[2].isDouble() || !vp[3].isDouble()))
if (vp->isDouble() && !(vp[2].isDouble() || vp[3].isDouble()))
cx->markTypeCallerOverflow();
return JS_TRUE;
@ -865,7 +865,7 @@ static void math_TypeArith(JSContext *cx, JSTypeFunction *jsfun, JSTypeCallsite
// the zero-argument case will be handled as an overflow in the actual natives.
for (size_t ind = 0; ind < site->argumentCount; ind++)
site->argumentTypes[ind]->addArith(cx, site->pool(), site->returnTypes);
site->argumentTypes[ind]->addArith(cx, site->script, site->returnTypes);
#endif
}

View File

@ -1284,12 +1284,9 @@ EvalKernel(JSContext *cx, uintN argc, Value *vp, EvalType evalType, JSStackFrame
linearStr, staticLevel);
if (!script)
return false;
script->isCachedEval = true;
}
/* set where this script is nested, if there is a caller frame. */
if (callerFrame)
script->setTypeNesting(callerFrame->script(), callerFrame->pc(cx));
assertSameCompartment(cx, scopeobj, script);
/*

View File

@ -1125,6 +1125,8 @@ Compiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *calle
script = JSScript::NewScriptFromCG(cx, &cg);
if (script && funbox)
script->savedCallerFun = true;
if (script && cg.compilingForEval())
script->isCachedEval = true;
#ifdef JS_SCOPE_DEPTH_METER
if (script) {
@ -1159,9 +1161,6 @@ Compiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *calle
bool
Compiler::defineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *script)
{
if (!globalScope.defs.length())
return true;
JSObject *globalObj = globalScope.globalObj;
/* Define and update global properties. */
@ -1183,6 +1182,7 @@ Compiler::defineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *scrip
* optimizations only take place if the property is not defined.
*/
rval.setObject(*fun);
cx->addTypePropertyId(globalObj->getType(), id, rval);
} else {
rval.setUndefined();
}
@ -1194,15 +1194,6 @@ Compiler::defineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *scrip
return false;
}
if (!rval.isUndefined()) {
cx->addTypePropertyId(globalObj->getType(), id, rval);
if (rval.isObject() && rval.toObject().isFunction()) {
JSFunction *fun = rval.toObject().getFunctionPrivate();
if (fun->isInterpreted())
fun->u.i.script->setTypeNesting(script, script->code);
}
}
JS_ASSERT(prop);
const Shape *shape = (const Shape *)prop;
def.knownSlot = shape->slot;
@ -1224,7 +1215,11 @@ Compiler::defineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *scrip
if (JSScript::isValidOffset(inner->objectsOffset)) {
JSObjectArray *arr = inner->objects();
for (size_t i = 0; i < arr->length; i++) {
/* Skip any caller function entrained in the first object. */
size_t start = inner->savedCallerFun ? 1 : 0;
for (size_t i = start; i < arr->length; i++) {
JSObject *obj = arr->vector[i];
if (!obj->isFunction())
continue;

View File

@ -1488,6 +1488,10 @@ JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
if (script->compileAndGo) {
GlobalScope *globalScope = cg->compiler()->globalScope;
script->global = globalScope->globalObj;
if (!script->global) {
JS_ASSERT(cx->globalObject);
script->global = cx->globalObject;
}
}
#endif
@ -1656,6 +1660,26 @@ DestroyScript(JSContext *cx, JSScript *script)
PurgeScriptFragments(&script->compartment->traceMonitor, script);
#endif
#ifdef JS_TYPE_INFERENCE
JS_ASSERT(!script->types);
/* Migrate any type objects associated with this script to the compartment. */
types::TypeObject *obj = script->typeObjects;
while (obj) {
types::TypeObject *next = obj->next;
obj->next = cx->compartment->types.objects;
cx->compartment->types.objects = obj;
obj = next;
}
types::TypeResult *result = script->typeResults;
while (result) {
types::TypeResult *next = result->next;
cx->free(result);
result = next;
}
#endif
#if defined(JS_METHODJIT)
mjit::ReleaseScriptCode(cx, script);
#endif
@ -1734,18 +1758,17 @@ js_TraceScript(JSTracer *trc, JSScript *script)
script->bindings.trace(trc);
#ifdef JS_TYPE_INFERENCE
if (script->types)
script->types->trace(trc);
types::TypeObject *obj = script->typeObjects;
while (obj) {
if (!obj->marked)
obj->trace(trc);
obj = obj->next;
}
if (script->fun) {
JS_SET_TRACING_NAME(trc, "script_fun");
Mark(trc, script->fun);
}
if (script->parent) {
JS_SET_TRACING_NAME(trc, "script_parent");
js_TraceScript(trc, script->parent);
}
#endif
}

View File

@ -403,9 +403,12 @@ struct JSScript {
bool warnedAboutTwoArgumentEval:1; /* have warned about use of
obsolete eval(s, o) in
this script */
bool dynamicScoping:1; /* script is dynamically scoped: uses
* 'with', 'let', DEFFUN or DEFVAR. */
bool hasSingletons:1; /* script has singleton objects */
bool isCachedEval:1; /* script came from eval() */
bool isUncachedEval:1; /* script came from EvaluateScript */
#ifdef JS_TYPE_INFERENCE
bool analyzed:1; /* script has previously been analyzed */
#endif
#ifdef JS_METHODJIT
bool debugMode:1; /* script was compiled in debug mode */
bool singleStepMode:1; /* compile script in single-step mode */
@ -467,20 +470,36 @@ struct JSScript {
/* Global object for this script, if compileAndGo. */
JSObject *global;
/*
* Location where the definition of this script occurs, representing any
* nesting for scope lookups. NULL for global scripts.
*/
JSScript *parent;
/* Lazily constructed types of rval/this/args/vars/upvars for this script. */
js::types::TypeSet *varTypes;
/* Type inference information for this script. */
/* Any type objects associated with this script, including initializer objects. */
js::types::TypeObject *typeObjects;
/* Any possibly unexpected values pushed by opcodes in this script. */
js::types::TypeResult *typeResults;
/* Results of type inference analysis for this script. Destroyed on GC. */
js::types::TypeScript *types;
inline JSObject *getGlobal();
inline js::types::TypeObject *getGlobalType();
/* Whether this code is global or is in an eval called at global scope. */
bool isGlobal() { return !parent || (!fun && !parent->parent); }
/*
* Make sure there are type sets for variables in this script.
* After it has been called or executed, the script will have such sets.
*/
inline bool ensureVarTypes(JSContext *cx);
inline js::types::TypeSet *returnTypes();
inline js::types::TypeSet *thisTypes();
inline js::types::TypeSet *argTypes(unsigned i);
inline js::types::TypeSet *localTypes(unsigned i);
inline js::types::TypeSet *upvarTypes(unsigned i);
private:
bool makeVarTypes(JSContext *cx);
public:
/* Check that correct types were inferred for the values pushed by this bytecode. */
void typeCheckBytecode(JSContext *cx, const jsbytecode *pc, const js::Value *sp);
@ -489,26 +508,19 @@ struct JSScript {
inline js::types::TypeObject *getTypeNewObject(JSContext *cx, JSProtoKey key);
#endif
/* Mark this script as having been created at the specified script/pc. */
inline void setTypeNesting(JSScript *parent, const jsbytecode *pc);
/*
* Throw away all upvar information in this script and its children, and detach from
* any parent it is nested in. For reparenting functions to arbitrary objects.
*/
inline void nukeUpvarTypes(JSContext *cx);
void condenseTypes(JSContext *cx);
void sweepTypes(JSContext *cx);
/* Get a type object for an allocation site in this script. */
inline js::types::TypeObject *
getTypeInitObject(JSContext *cx, const jsbytecode *pc, bool isArray);
/* Monitor a bytecode pushing an unexpected value. */
inline void typeMonitorResult(JSContext *cx, const jsbytecode *pc, unsigned index,
js::types::jstype type);
inline void typeMonitorResult(JSContext *cx, const jsbytecode *pc, unsigned index,
const js::Value &rval);
inline void typeMonitorOverflow(JSContext *cx, const jsbytecode *pc, unsigned index);
inline void typeMonitorUndefined(JSContext *cx, const jsbytecode *pc, unsigned index);
inline void typeMonitorResult(JSContext *cx, const jsbytecode *pc, js::types::jstype type);
inline void typeMonitorResult(JSContext *cx, const jsbytecode *pc, const js::Value &val);
inline void typeMonitorUndefined(JSContext *cx, const jsbytecode *pc);
inline void typeMonitorOverflow(JSContext *cx, const jsbytecode *pc);
inline void typeMonitorUnknown(JSContext *cx, const jsbytecode *pc);
/* Monitor a bytecode assigning to an object's property, if necessary. */
inline void typeMonitorAssign(JSContext *cx, const jsbytecode *pc,
@ -517,6 +529,9 @@ struct JSScript {
/* Override the value of an argument to this script by assigning to arguments[...]. */
inline void typeSetArgument(JSContext *cx, unsigned arg, const js::Value &value);
/* Mark the value of a flat closure upvar in this script. */
inline void typeSetUpvar(JSContext *cx, unsigned upvar, const js::Value &value);
#ifdef JS_METHODJIT
// Fast-cached pointers to make calls faster. These are also used to
// quickly test whether there is JIT code; a NULL value means no

View File

@ -194,6 +194,8 @@ mjit::Compiler::performCompilation(JITScript **jitp, const Vector<JSStackFrame*>
}
#ifdef JS_TYPE_INFERENCE
types::AutoEnterTypeInference enter(cx);
/*
* Fill in known types of arguments and locals, and patch up doubles for
* arguments and locals in existing frames. Any value assumed to be a double
@ -201,7 +203,14 @@ mjit::Compiler::performCompilation(JITScript **jitp, const Vector<JSStackFrame*>
* frames. We handle this by patching up all hold stack frames to ensure that
* arguments, locals, and stack values we treat as doubles actually are doubles.
*/
JS_ASSERT(script->types);
if (!script->types) {
/* Uncached eval scripts are never analyzed or compiled. */
if (script->isUncachedEval)
return Compile_Abort;
types::AnalyzeScriptTypes(cx, script);
if (!script->types)
return Compile_Error;
}
uint32 nargs = fun ? fun->nargs : 0;
if (!argumentTypes.reserve(nargs))
@ -209,7 +218,7 @@ mjit::Compiler::performCompilation(JITScript **jitp, const Vector<JSStackFrame*>
for (unsigned i = 0; i < nargs; i++) {
JSValueType type = JSVAL_TYPE_UNKNOWN;
if (!analysis->argEscapes(i))
type = script->types->argTypes(i)->getKnownTypeTag(cx, script);
type = script->argTypes(i)->getKnownTypeTag(cx, script);
argumentTypes.append(type);
if (type == JSVAL_TYPE_DOUBLE && frames) {
for (unsigned j = 0; j < frames->length(); j++) {
@ -225,7 +234,7 @@ mjit::Compiler::performCompilation(JITScript **jitp, const Vector<JSStackFrame*>
for (unsigned i = 0; i < script->nfixed; i++) {
JSValueType type = JSVAL_TYPE_UNKNOWN;
if (!analysis->localHasUseBeforeDef(i))
type = script->types->localTypes(i)->getKnownTypeTag(cx, script);
type = script->localTypes(i)->getKnownTypeTag(cx, script);
localTypes.append(type);
if (type == JSVAL_TYPE_DOUBLE && frames) {
for (unsigned j = 0; j < frames->length(); j++) {
@ -5480,7 +5489,7 @@ mjit::Compiler::knownThisType()
if (hasThisType)
return thisType;
hasThisType = true;
thisType = script->types->thisTypes.getKnownTypeTag(cx, script);
thisType = script->thisTypes()->getKnownTypeTag(cx, script);
return thisType;
#endif
return JSVAL_TYPE_UNKNOWN;
@ -5500,7 +5509,7 @@ void
mjit::Compiler::markArgumentOverflow(uint32 arg)
{
#ifdef JS_TYPE_INFERENCE
types::TypeSet *types = script->types->argTypes(arg);
types::TypeSet *types = script->argTypes(arg);
JS_ASSERT(!types->hasType(types::TYPE_DOUBLE));
types::InferSpew(types::ISpewDynamic, "StaticOverflow: #%u", script->id());
cx->compartment->types.addDynamicType(cx, types, types::TYPE_DOUBLE);
@ -5522,7 +5531,7 @@ void
mjit::Compiler::markLocalOverflow(uint32 local)
{
#ifdef JS_TYPE_INFERENCE
types::TypeSet *types = script->types->localTypes(local);
types::TypeSet *types = script->localTypes(local);
JS_ASSERT(!types->hasType(types::TYPE_DOUBLE));
types::InferSpew(types::ISpewDynamic, "StaticOverflow: #%u", script->id());
cx->compartment->types.addDynamicType(cx, types, types::TYPE_DOUBLE);
@ -5561,7 +5570,7 @@ types::TypeSet *
mjit::Compiler::argTypeSet(uint32 arg)
{
#ifdef JS_TYPE_INFERENCE
return script->types->argTypes(arg);
return script->argTypes(arg);
#endif
return NULL;
}
@ -5572,7 +5581,7 @@ mjit::Compiler::localTypeSet(uint32 local)
#ifdef JS_TYPE_INFERENCE
if (local >= script->nfixed)
return NULL;
return script->types->localTypes(local);
return script->localTypes(local);
#endif
return NULL;
}

View File

@ -1703,7 +1703,7 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic)
* reads of the prototype.
*/
if (v.isUndefined() && usePropCache)
f.script()->typeMonitorUndefined(f.cx, f.regs.pc, 0);
f.script()->typeMonitorUndefined(f.cx, f.regs.pc);
f.regs.sp[-1] = v;
}
@ -1858,7 +1858,7 @@ ic::CallProp(VMFrame &f, ic::PICInfo *pic)
#endif
if (regs.sp[-2].isUndefined())
f.script()->typeMonitorUndefined(cx, regs.pc, 0);
f.script()->typeMonitorUndefined(cx, regs.pc);
if (f.jit()->recompilations != recompilations)
return;
@ -1911,7 +1911,7 @@ ic::XName(VMFrame &f, ic::PICInfo *pic)
f.regs.sp[-1] = rval;
if (rval.isUndefined())
script->typeMonitorUndefined(f.cx, f.regs.pc, 0);
script->typeMonitorUndefined(f.cx, f.regs.pc);
}
void JS_FASTCALL
@ -1931,7 +1931,7 @@ ic::Name(VMFrame &f, ic::PICInfo *pic)
f.regs.sp[0] = rval;
if (rval.isUndefined())
script->typeMonitorUndefined(f.cx, f.regs.pc, 0);
script->typeMonitorUndefined(f.cx, f.regs.pc);
}
static void JS_FASTCALL
@ -2412,7 +2412,7 @@ ic::CallElement(VMFrame &f, ic::GetElementIC *ic)
f.regs.sp[-1] = thisv;
}
if (f.regs.sp[-2].isUndefined())
f.script()->typeMonitorUndefined(cx, f.regs.pc, 0);
f.script()->typeMonitorUndefined(cx, f.regs.pc);
}
void JS_FASTCALL
@ -2461,7 +2461,7 @@ ic::GetElement(VMFrame &f, ic::GetElementIC *ic)
if (f.regs.sp[-2].isUndefined()) {
if (idval.isInt32())
cx->addTypeProperty(obj->getType(), NULL, types::TYPE_UNDEFINED);
f.script()->typeMonitorUndefined(cx, f.regs.pc, 0);
f.script()->typeMonitorUndefined(cx, f.regs.pc);
}
}

View File

@ -408,7 +408,7 @@ NameOp(VMFrame &f, JSObject *obj, bool callname = false)
f.regs.sp[-1] = rval;
if (rval.isUndefined())
f.script()->typeMonitorUndefined(cx, f.regs.pc, 0);
f.script()->typeMonitorUndefined(cx, f.regs.pc);
if (callname) {
Class *clasp;
@ -516,10 +516,12 @@ stubs::GetElem(VMFrame &f)
end_getelem:
f.regs.sp[-2] = *copyFrom;
if (copyFrom->isUndefined() || !rref.isInt32()) {
if (rref.isInt32())
cx->addTypeProperty(obj->getType(), NULL, TYPE_UNDEFINED);
f.script()->typeMonitorResult(cx, regs.pc, 0, *copyFrom);
if (!rref.isInt32()) {
f.script()->typeMonitorUnknown(cx, regs.pc);
} else if (copyFrom->isUndefined()) {
cx->addTypeProperty(obj->getType(), NULL, TYPE_UNDEFINED);
f.script()->typeMonitorUndefined(cx, regs.pc);
}
}
@ -567,7 +569,7 @@ stubs::CallElem(VMFrame &f)
regs.sp[-1] = thisv;
}
if (regs.sp[-2].isUndefined() || !JSID_IS_INT(id))
f.script()->typeMonitorResult(cx, regs.pc, 0, regs.sp[-2]);
f.script()->typeMonitorUnknown(cx, regs.pc);
}
template<JSBool strict>
@ -717,7 +719,7 @@ stubs::Ursh(VMFrame &f)
u >>= (j & 31);
if (!f.regs.sp[-2].setNumber(uint32(u)))
f.script()->typeMonitorOverflow(f.cx, f.regs.pc, 0);
f.script()->typeMonitorOverflow(f.cx, f.regs.pc);
}
template<JSBool strict>
@ -1046,7 +1048,7 @@ MonitorArithmeticOverflow(VMFrame &f, const Value &v)
JSContext *cx = f.cx;
JS_ASSERT(v.isDouble());
f.script()->typeMonitorOverflow(cx, f.regs.pc, 0);
f.script()->typeMonitorOverflow(cx, f.regs.pc);
/*
* Monitoring the overflow is not enough for fused INC operations on NAME/PROP,
@ -1192,7 +1194,7 @@ stubs::Mul(VMFrame &f)
}
double d = d1 * d2;
if (!regs.sp[-2].setNumber(d))
f.script()->typeMonitorOverflow(cx, f.regs.pc, 0);
f.script()->typeMonitorOverflow(cx, f.regs.pc);
}
void JS_FASTCALL
@ -1222,11 +1224,11 @@ stubs::Div(VMFrame &f)
else
vp = &rt->positiveInfinityValue;
regs.sp[-2] = *vp;
f.script()->typeMonitorOverflow(cx, f.regs.pc, 0);
f.script()->typeMonitorOverflow(cx, f.regs.pc);
} else {
d1 /= d2;
if (!regs.sp[-2].setNumber(d1))
f.script()->typeMonitorOverflow(cx, f.regs.pc, 0);
f.script()->typeMonitorOverflow(cx, f.regs.pc);
}
}
@ -1255,7 +1257,7 @@ stubs::Mod(VMFrame &f)
d1 = js_fmod(d1, d2);
regs.sp[-2].setDouble(d1);
}
f.script()->typeMonitorOverflow(cx, f.regs.pc, 0);
f.script()->typeMonitorOverflow(cx, f.regs.pc);
}
}
@ -1368,7 +1370,7 @@ stubs::Neg(VMFrame &f)
THROW();
d = -d;
if (!f.regs.sp[-1].setNumber(d))
f.script()->typeMonitorOverflow(f.cx, f.regs.pc, 0);
f.script()->typeMonitorOverflow(f.cx, f.regs.pc);
}
JSObject * JS_FASTCALL
@ -1458,7 +1460,7 @@ stubs::GetUpvar(VMFrame &f, uint32 ck)
f.regs.sp[0] = GetUpvar(f.cx, staticLevel, cookie);
if (f.regs.sp[0].isUndefined())
f.script()->typeMonitorUndefined(f.cx, f.regs.pc, 0);
f.script()->typeMonitorUndefined(f.cx, f.regs.pc);
}
JSObject * JS_FASTCALL
@ -1672,7 +1674,7 @@ ObjIncOp(VMFrame &f, JSObject *obj, jsid id)
ref.setNumber(d);
}
if (!v.setNumber(d)) {
f.script()->typeMonitorOverflow(cx, f.regs.pc, 0);
f.script()->typeMonitorOverflow(cx, f.regs.pc);
cx->addTypePropertyId(obj->getType(), id, TYPE_DOUBLE);
}
f.script()->typeMonitorAssign(cx, f.regs.pc, obj, id, v);
@ -2005,7 +2007,7 @@ InlineGetProp(VMFrame &f)
} while(0);
if (rval.isUndefined())
f.script()->typeMonitorUndefined(cx, regs.pc, 0);
f.script()->typeMonitorUndefined(cx, regs.pc);
regs.sp[-1] = rval;
return true;
@ -2126,7 +2128,7 @@ stubs::CallProp(VMFrame &f, JSAtom *origAtom)
}
#endif
if (rval.isUndefined())
f.script()->typeMonitorUndefined(cx, regs.pc, 0);
f.script()->typeMonitorUndefined(cx, regs.pc);
}
void JS_FASTCALL
@ -2607,7 +2609,7 @@ stubs::Pos(VMFrame &f)
if (!ValueToNumber(f.cx, &f.regs.sp[-1]))
THROW();
if (!f.regs.sp[-1].isInt32())
f.script()->typeMonitorOverflow(f.cx, f.regs.pc, 0);
f.script()->typeMonitorOverflow(f.cx, f.regs.pc);
}
void JS_FASTCALL
@ -2770,14 +2772,14 @@ template void JS_FASTCALL stubs::DelElem<false>(VMFrame &f);
void JS_FASTCALL
stubs::UndefinedHelper(VMFrame &f)
{
f.script()->typeMonitorUndefined(f.cx, f.regs.pc, 0);
f.script()->typeMonitorUndefined(f.cx, f.regs.pc);
f.regs.sp[-1].setUndefined();
}
void JS_FASTCALL
stubs::NegZeroHelper(VMFrame &f)
{
f.script()->typeMonitorOverflow(f.cx, f.regs.pc, 0);
f.script()->typeMonitorOverflow(f.cx, f.regs.pc);
f.regs.sp[-1].setDouble(-0.0);
}

View File

@ -54,10 +54,6 @@ DEFINES += -DEXPORT_JS_API
LIBS = $(NSPR_LIBS) $(EDITLINE_LIBS) $(DEPTH)/$(LIB_PREFIX)js_static.$(LIB_SUFFIX)
ifdef JS_TYPE_INFERENCE
LIBS += -lz
endif
LOCAL_INCLUDES += -I$(topsrcdir) -I..
ifdef _MSC_VER

View File

@ -1313,10 +1313,6 @@ Print(JSContext *cx, uintN argc, jsval *vp)
fputc('\n', gOutFile);
fflush(gOutFile);
static int count = 0;
printf("COUNT %d\n", ++count);
fflush(stdout);
JS_SET_RVAL(cx, vp, JSVAL_VOID);
return JS_TRUE;
}