[INFER] Stronger type-correctness assertions when setting or getting properties, bug 619693.

This commit is contained in:
Brian Hackett 2011-03-06 15:35:09 -08:00
Родитель efe50d913e
Коммит df6ca9d613
9 изменённых файлов: 134 добавлений и 48 удалений

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

@ -2614,7 +2614,7 @@ array_concat(JSContext *cx, uintN argc, Value *vp)
nobj->setType(ntype);
if (!nobj->setArrayLength(cx, length))
return JS_FALSE;
if (!InitArrayTypes(cx, ntype, vector, length))
if (!InitArrayTypes(cx, ntype, vector, initlen))
return JS_FALSE;
if (!aobj->isPackedDenseArray() && !nobj->setDenseArrayNotPacked(cx))
return JS_FALSE;

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

@ -912,6 +912,9 @@ UpdateWatchpointShape(JSContext *cx, JSWatchPoint *wp, const Shape *newShape)
*/
StrictPropertyOp originalSetter = newShape->setter();
if (!cx->addTypePropertyId(wp->object->getType(), newShape->id, types::TYPE_UNKNOWN))
return NULL;
/*
* Drop the watching setter into the object, in place of newShape. Note that a single
* watchpoint-wrapped shape may correspond to more than one non-watchpoint shape: we

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

@ -203,6 +203,88 @@ void InferSpew(SpewChannel channel, const char *fmt, ...)
va_end(ap);
}
/* Whether types can be considered to contain type or an equivalent, for checking results. */
static inline bool
TypeSetMatches(JSContext *cx, TypeSet *types, jstype type)
{
if (types->hasType(type))
return true;
/*
* If this is a type for an object with unknown properties, match any object
* in the type set which also has unknown properties. This avoids failure
* on objects whose prototype (and thus type) changes dynamically, which will
* mark the old and new type objects as unknown.
*
* Similarly, when checking the correctness of property accesses let the
* GetSet property match against any type. All accesses on getters/setters
* go through Invoke, which will record the property's behavior.
*/
bool unknown =
(js::types::TypeIsObject(type) && ((js::types::TypeObject*)type)->unknownProperties);
if (types->objectCount >= 2) {
unsigned objectCapacity = HashSetCapacity(types->objectCount);
for (unsigned i = 0; i < objectCapacity; i++) {
TypeObject *object = types->objectSet[i];
if (object) {
if (object == cx->compartment->types.typeGetSet)
return true;
if (unknown && object->unknownProperties)
return true;
}
}
} else if (types->objectCount == 1) {
TypeObject *object = (TypeObject *) types->objectSet;
if (object == cx->compartment->types.typeGetSet)
return true;
if (unknown && object->unknownProperties)
return true;
}
return false;
}
bool
TypeHasProperty(JSContext *cx, TypeObject *obj, jsid id, const Value &value)
{
/*
* Check the correctness of the type information in the object's property
* against an actual value. Note that we are only checking the .types set,
* not the .ownTypes set, and could miss cases where a type set is missing
* entries from its ownTypes set when they are shadowed by a prototype property.
*/
if (cx->typeInferenceEnabled() && !obj->unknownProperties && !value.isUndefined() &&
!JSID_IS_DEFAULT_XML_NAMESPACE(id)) {
id = MakeTypeId(cx, id);
/* Watch for properties which inference does not monitor. */
if (id == id___proto__(cx) || id == id_constructor(cx) || id == id_caller(cx))
return true;
/*
* If we called in here while resolving a type constraint, we may be in the
* middle of resolving a standard class and the type sets will not be updated
* until the outer TypeSet::add finishes.
*/
if (cx->compartment->types.pendingCount)
return true;
jstype type = GetValueType(cx, value);
AutoEnterTypeInference enter(cx);
TypeSet *types = obj->getProperty(cx, id, false);
if (types && !TypeSetMatches(cx, types, type)) {
TypeFailure(cx, "Missing type in object %s %s: %s",
obj->name(), TypeIdString(id), TypeString(type));
}
cx->compartment->types.checkPendingRecompiles(cx);
}
return true;
}
#endif
void TypeFailure(JSContext *cx, const char *fmt, ...)
@ -1479,22 +1561,6 @@ TypeSet::knownNonEmpty(JSContext *cx, JSScript *script)
return false;
}
static bool
HasUnknownObject(TypeSet *types)
{
if (types->objectCount >= 2) {
unsigned objectCapacity = HashSetCapacity(types->objectCount);
for (unsigned i = 0; i < objectCapacity; i++) {
TypeObject *object = types->objectSet[i];
if (object && object->unknownProperties)
return true;
}
} else if (types->objectCount == 1 && ((TypeObject*)types->objectSet)->unknownProperties) {
return true;
}
return false;
}
/////////////////////////////////////////////////////////////////////
// TypeCompartment
/////////////////////////////////////////////////////////////////////
@ -1979,6 +2045,11 @@ TypeCompartment::finish(JSContext *cx, JSCompartment *compartment)
script = (JSScript *)script->links.next) {
if (script->types)
script->types->finish(cx, script);
TypeObject *object = script->typeObjects;
while (object) {
object->print(cx);
object = object->next;
}
}
#ifdef DEBUG
@ -3517,12 +3588,6 @@ TypeScript::finish(JSContext *cx, JSScript *script)
printf("\n");
TypeObject *object = script->typeObjects;
while (object) {
object->print(cx);
object = object->next;
}
#endif /* DEBUG */
}
@ -3671,18 +3736,7 @@ JSScript::typeCheckBytecode(JSContext *cx, const jsbytecode *pc, const js::Value
js::types::jstype type = js::types::GetValueType(cx, val);
/*
* If this is a type for an object with unknown properties, match any object
* in the type set which also has unknown properties. This avoids failure
* on objects whose prototype (and thus type) changes dynamically, which will
* mark the old and new type objects as unknown.
*/
if (js::types::TypeIsObject(type) && ((js::types::TypeObject*)type)->unknownProperties &&
js::types::HasUnknownObject(types)) {
continue;
}
if (!types->hasType(type)) {
if (!js::types::TypeSetMatches(cx, types, type)) {
js::types::TypeFailure(cx, "Missing type at #%u:%05u pushed %u: %s",
id(), pc - code, i, js::types::TypeString(type));
}

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

@ -707,6 +707,9 @@ enum SpewChannel {
void InferSpew(SpewChannel which, const char *fmt, ...);
const char * TypeString(jstype type);
/* Check that the type property for id in obj contains value. */
bool TypeHasProperty(JSContext *cx, TypeObject *obj, jsid id, const Value &value);
#else
inline void InferSpew(SpewChannel which, const char *fmt, ...) {}

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

@ -5986,6 +5986,10 @@ BEGIN_CASE(JSOP_SETTER)
if (!CheckRedeclaration(cx, obj, id, attrs))
goto error;
TypeObject *type = cx->getTypeGetSet();
if (!type || !cx->addTypePropertyId(obj->getType(), id, (jstype) type))
goto error;
if (!obj->defineProperty(cx, id, UndefinedValue(), getter, setter, attrs))
goto error;

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

@ -2466,6 +2466,11 @@ DefineProperty(JSContext *cx, JSObject *obj, const PropDesc &desc, bool throwErr
{
if (!cx->addTypePropertyId(obj->getType(), desc.id, desc.value))
return false;
if (!desc.get.isUndefined() || !desc.set.isUndefined()) {
TypeObject *type = cx->getTypeGetSet();
if (!type || !cx->addTypePropertyId(obj->getType(), desc.id, (jstype) type))
return JS_FALSE;
}
if (obj->isArray())
return DefinePropertyOnArray(cx, obj, desc, throwError, rval);
@ -4793,6 +4798,8 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &valu
JS_ASSERT((defineHow & ~(JSDNP_CACHE_RESULT | JSDNP_DONT_PURGE | JSDNP_SET_METHOD)) == 0);
LeaveTraceIfGlobalObject(cx, obj);
JS_ASSERT(TypeHasProperty(cx, obj->getType(), id, value));
/* Convert string indices to integers if appropriate. */
id = js_CheckForStringIndex(id);
@ -5395,6 +5402,7 @@ js_NativeGetInline(JSContext *cx, JSObject *receiver, JSObject *obj, JSObject *p
pobj->nativeSetSlot(slot, *vp);
}
JS_ASSERT(TypeHasProperty(cx, obj->getType(), shape->id, *vp));
return true;
}
@ -5410,6 +5418,8 @@ js_NativeSet(JSContext *cx, JSObject *obj, const Shape *shape, bool added, bool
{
LeaveTraceIfGlobalObject(cx, obj);
JS_ASSERT(TypeHasProperty(cx, obj->getType(), shape->id, *vp));
uint32 slot;
int32 sample;

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

@ -72,7 +72,6 @@ class JSProxyHandler;
class AutoPropDescArrayRooter;
namespace mjit { class Compiler; }
namespace types { struct TypeObject; }
static inline PropertyOp
CastAsPropertyOp(JSObject *object)
@ -1216,13 +1215,17 @@ struct JSObject : js::gc::Cell {
js::PropertyOp getter = js::PropertyStub,
js::StrictPropertyOp setter = js::StrictPropertyStub,
uintN attrs = JSPROP_ENUMERATE) {
JS_ASSERT_IF(getter == js::PropertyStub && setter == js::StrictPropertyStub,
js::types::TypeHasProperty(cx, getType(), id, value));
js::DefinePropOp op = getOps()->defineProperty;
return (op ? op : js_DefineProperty)(cx, this, id, &value, getter, setter, attrs);
}
JSBool getProperty(JSContext *cx, JSObject *receiver, jsid id, js::Value *vp) {
js::PropertyIdOp op = getOps()->getProperty;
return (op ? op : (js::PropertyIdOp)js_GetProperty)(cx, this, receiver, id, vp);
JSBool res = (op ? op : (js::PropertyIdOp)js_GetProperty)(cx, this, receiver, id, vp);
JS_ASSERT_IF(res, js::types::TypeHasProperty(cx, getType(), id, *vp));
return res;
}
JSBool getProperty(JSContext *cx, jsid id, js::Value *vp) {
@ -1230,6 +1233,7 @@ struct JSObject : js::gc::Cell {
}
JSBool setProperty(JSContext *cx, jsid id, js::Value *vp, JSBool strict) {
JS_ASSERT(js::types::TypeHasProperty(cx, getType(), id, *vp));
js::StrictPropertyIdOp op = getOps()->setProperty;
return (op ? op : js_SetProperty)(cx, this, id, vp, strict);
}

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

@ -928,16 +928,14 @@ js_InitRegExpClass(JSContext *cx, JSObject *obj)
return NULL;
}
TypeObject *regexpType = proto->getNewType(cx);
if (!regexpType)
return NULL;
TypeObject *protoType = proto->getType();
if (!cx->addTypeProperty(regexpType, "source", TYPE_STRING) ||
!cx->addTypeProperty(regexpType, "global", TYPE_BOOLEAN) ||
!cx->addTypeProperty(regexpType, "ignoreCase", TYPE_BOOLEAN) ||
!cx->addTypeProperty(regexpType, "multiline", TYPE_BOOLEAN) ||
!cx->addTypeProperty(regexpType, "sticky", TYPE_BOOLEAN) ||
!cx->addTypeProperty(regexpType, "lastIndex", TYPE_INT32)) {
if (!cx->addTypeProperty(protoType, "source", TYPE_STRING) ||
!cx->addTypeProperty(protoType, "global", TYPE_BOOLEAN) ||
!cx->addTypeProperty(protoType, "ignoreCase", TYPE_BOOLEAN) ||
!cx->addTypeProperty(protoType, "multiline", TYPE_BOOLEAN) ||
!cx->addTypeProperty(protoType, "sticky", TYPE_BOOLEAN) ||
!cx->addTypeProperty(protoType, "lastIndex", TYPE_INT32)) {
return NULL;
}

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

@ -7112,8 +7112,18 @@ js_InitNamespaceClass(JSContext *cx, JSObject *obj)
JSObject *
js_InitQNameClass(JSContext *cx, JSObject *obj)
{
return js_InitClass(cx, obj, NULL, &js_QNameClass, QName, 2, JS_TypeHandlerDynamic,
qname_props, qname_methods, NULL, NULL);
JSObject *proto = js_InitClass(cx, obj, NULL, &js_QNameClass, QName, 2, JS_TypeHandlerDynamic,
qname_props, qname_methods, NULL, NULL);
/* Properties of QName objects are not modeled by type inference. */
TypeObject *type = proto->getNewType(cx);
if (!type ||
!cx->markTypeObjectUnknownProperties(type) ||
!cx->markTypeObjectUnknownProperties(proto->getType())) {
return NULL;
}
return proto;
}
JSObject *