зеркало из https://github.com/mozilla/gecko-dev.git
[INFER] Stronger type-correctness assertions when setting or getting properties, bug 619693.
This commit is contained in:
Родитель
efe50d913e
Коммит
df6ca9d613
|
@ -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,
|
||||
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 *
|
||||
|
|
Загрузка…
Ссылка в новой задаче