[INFER] Overhaul inference handling of new object computation, bug 619433.

This commit is contained in:
Brian Hackett 2011-03-10 12:01:11 -08:00
parent 6279a0e820
commit a3042809bc
8 changed files with 73 additions and 38 deletions

View File

@ -2141,7 +2141,8 @@ public:
js::types::TypeObject *newTypeObject(const char *name, JSObject *proto);
/* Make a type object whose name is that of base followed by postfix. */
js::types::TypeObject *newTypeObject(const char *base, const char *postfix, JSObject *proto);
js::types::TypeObject *newTypeObject(const char *base, const char *postfix,
JSObject *proto, bool isFunction = false);
/*
* Get the default 'new' object for a given standard class, per the currently

View File

@ -2764,6 +2764,8 @@ js_InitFunctionClass(JSContext *cx, JSObject *obj)
script->owner = NULL;
#endif
fun->u.i.script = script;
fun->getType()->asFunction()->script = script;
script->fun = fun;
js_CallNewScriptHook(cx, script, fun);
if (obj->isGlobal()) {

View File

@ -974,8 +974,9 @@ TypeConstraintNewObject::newType(JSContext *cx, TypeSet *source, jstype type)
}
} else if (!fun->script) {
/*
* This constraint should only be used for native constructors with
* immutable non-primitive prototypes. Disregard primitives here.
* This constraint should only be used for scripted functions and for
* native constructors with immutable non-primitive prototypes.
* Disregard primitives here.
*/
} else if (!fun->script->compileAndGo) {
target->addType(cx, TYPE_UNKNOWN);
@ -1083,15 +1084,7 @@ TypeConstraintCall::newType(JSContext *cx, TypeSet *source, jstype type)
/* Add a binding for the receiver object of the call. */
if (callsite->isNew) {
/* The receiver object is the 'new' object for the function's prototype. */
if (function->unknownProperties) {
script->thisTypes()->addType(cx, TYPE_UNKNOWN);
} else {
TypeSet *prototypeTypes = function->getProperty(cx, id_prototype(cx), false);
if (!prototypeTypes)
return;
prototypeTypes->addNewObject(cx, script, function, callee->thisTypes());
}
callee->typeSetNewCalled(cx);
/*
* If the script does not return a value then the pushed value is the new
@ -1687,25 +1680,14 @@ TypeCompartment::dynamicCall(JSContext *cx, JSObject *callee,
unsigned nargs = callee->getFunctionPrivate()->nargs;
JSScript *script = callee->getFunctionPrivate()->script();
jstype type;
if (constructing) {
Value protov;
jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
if (!callee->getProperty(cx, id, &protov))
return false;
TypeObject *otype = protov.isObject()
? protov.toObject().getNewType(cx)
: cx->getTypeNewObject(JSProto_Object);
if (!otype)
return false;
type = (jstype) otype;
script->typeSetNewCalled(cx);
} else {
type = GetValueType(cx, args.thisv());
jstype type = GetValueType(cx, args.thisv());
if (!script->typeSetThis(cx, type))
return false;
}
if (!script->typeSetThis(cx, type))
return false;
/*
* Add constraints going up to the minimum of the actual and formal count.
* If there are more actuals than formals the later values can only be
@ -3306,6 +3288,9 @@ AnalyzeScriptTypes(JSContext *cx, JSScript *script)
cursor += sizeof(TypeScript);
types->pushedArray = (TypeSet **) cursor;
if (script->calledWithNew)
AnalyzeScriptNew(cx, script);
unsigned offset = 0;
while (offset < script->length) {
analyze::Bytecode *code = analysis.maybeCode(offset);
@ -3335,11 +3320,23 @@ AnalyzeScriptTypes(JSContext *cx, JSScript *script)
}
void
DestroyScriptTypes(JSContext *cx, JSScript *script)
AnalyzeScriptNew(JSContext *cx, JSScript *script)
{
JS_ASSERT(script->types);
cx->free(script->types);
script->types = NULL;
JS_ASSERT(script->calledWithNew && script->fun);
/*
* Compute the 'this' type when called with 'new'. We do not distinguish regular
* from 'new' calls to the function.
*/
TypeFunction *funType = script->fun->getType()->asFunction();
if (funType->unknownProperties) {
script->thisTypes()->addType(cx, TYPE_UNKNOWN);
} else {
TypeSet *prototypeTypes = funType->getProperty(cx, id_prototype(cx), false);
if (!prototypeTypes)
return;
prototypeTypes->addNewObject(cx, script, funType, script->thisTypes());
}
}
/////////////////////////////////////////////////////////////////////
@ -3576,7 +3573,7 @@ JSContext::newTypeObject(const char *name, JSObject *proto)
}
js::types::TypeObject *
JSContext::newTypeObject(const char *base, const char *postfix, JSObject *proto)
JSContext::newTypeObject(const char *base, const char *postfix, JSObject *proto, bool isFunction)
{
char *name = NULL;
#ifdef DEBUG
@ -3584,7 +3581,7 @@ JSContext::newTypeObject(const char *base, const char *postfix, JSObject *proto)
name = (char *)alloca(len);
JS_snprintf(name, len, "%s:%s", base, postfix);
#endif
return compartment->types.newTypeObject(this, NULL, name, false, proto);
return compartment->types.newTypeObject(this, NULL, name, isFunction, proto);
}
/////////////////////////////////////////////////////////////////////
@ -4117,6 +4114,8 @@ JSScript::sweepTypes(JSContext *cx)
{
SweepTypeObjectList(cx, typeObjects);
if (types && !compartment->types.inferenceDepth)
js::types::DestroyScriptTypes(cx, this);
if (types && !compartment->types.inferenceDepth) {
cx->free(types);
types = NULL;
}
}

View File

@ -570,8 +570,8 @@ struct TypeScript
/* Analyzes all types in script, constructing its TypeScript. */
void AnalyzeScriptTypes(JSContext *cx, JSScript *script);
/* Destroy the TypeScript associated with a script. */
void DestroyScriptTypes(JSContext *cx, JSScript *script);
/* Analyze the effect of invoking 'new' on script. */
void AnalyzeScriptNew(JSContext *cx, JSScript *script);
/* Type information for a compartment. */
struct TypeCompartment

View File

@ -530,6 +530,30 @@ JSScript::typeSetThis(JSContext *cx, js::types::jstype type)
return true;
}
inline bool
JSScript::typeSetNewCalled(JSContext *cx)
{
if (!cx->typeInferenceEnabled() || calledWithNew)
return true;
calledWithNew = true;
/*
* Determining the 'this' type used when the script is invoked with 'new'
* happens during the script's prologue, so we don't try to pick it up from
* dynamic calls. Instead, generate constraints modeling the construction
* of 'this' when the script is analyzed or reanalyzed after an invoke with 'new',
* and if 'new' is first invoked after the script has already been analyzed.
*/
if (analyzed) {
/* Regenerate types for the function. */
js::types::AutoEnterTypeInference enter(cx);
js::types::AnalyzeScriptNew(cx, this);
if (!cx->compartment->types.checkPendingRecompiles(cx))
return false;
}
return true;
}
inline bool
JSScript::typeSetLocal(JSContext *cx, unsigned local, const js::Value &value)
{

View File

@ -3940,7 +3940,8 @@ DefineConstructorAndPrototype(JSContext *cx, JSObject *obj, JSProtoKey key, JSAt
if (!proto)
return NULL;
TypeObject *protoType = cx->newTypeObject(clasp->name, "prototype", proto->getProto());
TypeObject *protoType = cx->newTypeObject(clasp->name, "prototype", proto->getProto(),
clasp == &js_FunctionClass);
if (!protoType)
return NULL;
proto->setType(protoType);

View File

@ -406,6 +406,7 @@ struct JSScript {
bool hasSingletons:1; /* script has singleton objects */
bool isCachedEval:1; /* script came from eval() */
bool isUncachedEval:1; /* script came from EvaluateScript */
bool calledWithNew:1; /* script has been called using 'new' */
bool analyzed:1; /* script has been analyzed by type inference */
#ifdef JS_METHODJIT
bool debugMode:1; /* script was compiled in debug mode */
@ -522,6 +523,7 @@ struct JSScript {
/* Add a type for a variable in this script. */
inline bool typeSetThis(JSContext *cx, js::types::jstype type);
inline bool typeSetNewCalled(JSContext *cx);
inline bool typeSetLocal(JSContext *cx, unsigned local, const js::Value &value);
inline bool typeSetArgument(JSContext *cx, unsigned arg, js::types::jstype type);
inline bool typeSetArgument(JSContext *cx, unsigned arg, const js::Value &value);

View File

@ -7114,6 +7114,8 @@ js_InitQNameClass(JSContext *cx, JSObject *obj)
{
JSObject *proto = js_InitClass(cx, obj, NULL, &js_QNameClass, QName, 2, JS_TypeHandlerDynamic,
qname_props, qname_methods, NULL, NULL);
if (!proto)
return NULL;
/* Properties of QName objects are not modeled by type inference. */
TypeObject *type = proto->getNewType(cx);
@ -7305,6 +7307,10 @@ js_SetDefaultXMLNamespace(JSContext *cx, const Value &v)
JSStackFrame *fp = js_GetTopStackFrame(cx);
JSObject &varobj = fp->varobj(cx);
if (!cx->addTypePropertyId(varobj.getType(), JS_DEFAULT_XML_NAMESPACE_ID, types::TYPE_UNKNOWN))
return JS_FALSE;
if (!varobj.defineProperty(cx, JS_DEFAULT_XML_NAMESPACE_ID, ObjectValue(*ns),
PropertyStub, StrictPropertyStub, JSPROP_PERMANENT)) {
return JS_FALSE;