mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-08 04:27:37 +00:00
[INFER] Condense type information during GC, bug 613221.
This commit is contained in:
parent
ac00635e1e
commit
6010fa207c
@ -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)))
|
||||
|
12
js/src/jit-test/tests/jaeger/recompile/memory-01.js
Normal file
12
js/src/jit-test/tests/jaeger/recompile/memory-01.js
Normal 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)");
|
||||
|
19
js/src/jit-test/tests/jaeger/recompile/memory-02.js
Normal file
19
js/src/jit-test/tests/jaeger/recompile/memory-02.js
Normal 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);
|
6
js/src/jit-test/tests/jaeger/recompile/memory-03.js
Normal file
6
js/src/jit-test/tests/jaeger/recompile/memory-03.js
Normal file
@ -0,0 +1,6 @@
|
||||
|
||||
eval("var x = 10; function foo() { return x; }");
|
||||
|
||||
assertEq(foo(), 10);
|
||||
gc();
|
||||
assertEq(foo(), 10);
|
8
js/src/jit-test/tests/jaeger/recompile/memory-04.js
Normal file
8
js/src/jit-test/tests/jaeger/recompile/memory-04.js
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
function foo(x, y) {
|
||||
gc();
|
||||
var z = x + y;
|
||||
print(z);
|
||||
}
|
||||
|
||||
foo(0x7ffffff0, 100);
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
185
js/src/jsinfer.h
185
js/src/jsinfer.h
@ -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. */
|
||||
|
@ -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 */
|
||||
|
@ -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, ®s.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, ®s.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, ®s.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], ®s.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)
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
/*
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user