From d36f51c13b54a872cdaf08a1765a23afab26ae51 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sun, 13 Jul 2014 02:41:55 -0400 Subject: move setting of object->type to alloc_* functions The "struct object" type implements basic object polymorphism. Individual instances are allocated as concrete types (or as a union type that can store any object), and a "struct object *" can be cast into its real type after examining its "type" enum. This means it is dangerous to have a type field that does not match the allocation (e.g., setting the type field of a "struct blob" to "OBJ_COMMIT" would mean that a reader might read past the allocated memory). In most of the current code this is not a problem; the first thing we do after allocating an object is usually to set its type field by passing it to create_object. However, the virtual commits we create in merge-recursive.c do not ever get their type set. This does not seem to have caused problems in practice, though (presumably because we always pass around a "struct commit" pointer and never even look at the type). We can fix this oversight and also make it harder for future code to get it wrong by setting the type directly in the object allocation functions. This will also make it easier to fix problems with commit index allocation, as we know that any object allocated by alloc_commit_node will meet the invariant that an object with an OBJ_COMMIT type field will have a unique index number. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- object.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'object.c') diff --git a/object.c b/object.c index 9c31e9a5e0..a950b85146 100644 --- a/object.c +++ b/object.c @@ -141,13 +141,12 @@ static void grow_object_hash(void) obj_hash_size = new_hash_size; } -void *create_object(const unsigned char *sha1, int type, void *o) +void *create_object(const unsigned char *sha1, void *o) { struct object *obj = o; obj->parsed = 0; obj->used = 0; - obj->type = type; obj->flags = 0; hashcpy(obj->sha1, sha1); @@ -163,7 +162,7 @@ struct object *lookup_unknown_object(const unsigned char *sha1) { struct object *obj = lookup_object(sha1); if (!obj) - obj = create_object(sha1, OBJ_NONE, alloc_object_node()); + obj = create_object(sha1, alloc_object_node()); return obj; } -- cgit v1.2.3 From 5af01caa08700e389d49a81be15c7413abd4aa69 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sun, 13 Jul 2014 02:42:00 -0400 Subject: parse_object_buffer: do not set object type The only way that "obj" can be non-NULL is if it came from one of the lookup_* functions. These functions always ensure that the object has the expected type (and return NULL otherwise), so there is no need for us to set the type. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- object.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'object.c') diff --git a/object.c b/object.c index a950b85146..472aa8d5be 100644 --- a/object.c +++ b/object.c @@ -213,8 +213,6 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t warning("object %s has unknown type id %d", sha1_to_hex(sha1), type); obj = NULL; } - if (obj && obj->type == OBJ_NONE) - obj->type = type; return obj; } -- cgit v1.2.3 From 8ff226a9d5ee065fe52752e6032f63cb6e4beccb Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sun, 13 Jul 2014 02:42:03 -0400 Subject: add object_as_type helper for casting objects When we call lookup_commit, lookup_tree, etc, the logic goes something like: 1. Look for an existing object struct. If we don't have one, allocate and return a new one. 2. Double check that any object we have is the expected type (and complain and return NULL otherwise). 3. Convert an object with type OBJ_NONE (from a prior call to lookup_unknown_object) to the expected type. We can encapsulate steps 2 and 3 in a helper function which checks whether we have the expected object type, converts OBJ_NONE as appropriate, and returns the object. Not only does this shorten the code, but it also provides one central location for converting OBJ_NONE objects into objects of other types. Future patches will use that to enforce type-specific invariants. Since this is a refactoring, we would want it to behave exactly as the current code. It takes a little reasoning to see that this is the case: - for lookup_{commit,tree,etc} functions, we are just pulling steps 2 and 3 into a function that does the same thing. - for the call in peel_object, we currently only do step 3 (but we want to consolidate it with the others, as mentioned above). However, step 2 is a noop here, as the surrounding conditional makes sure we have OBJ_NONE (which we want to keep to avoid an extraneous call to sha1_object_info). - for the call in lookup_commit_reference_gently, we are currently doing step 2 but not step 3. However, step 3 is a noop here. The object we got will have just come from deref_tag, which must have figured out the type for each object in order to know when to stop peeling. Therefore the type will never be OBJ_NONE. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- object.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'object.c') diff --git a/object.c b/object.c index 472aa8d5be..b2319f6246 100644 --- a/object.c +++ b/object.c @@ -158,6 +158,23 @@ void *create_object(const unsigned char *sha1, void *o) return obj; } +void *object_as_type(struct object *obj, enum object_type type, int quiet) +{ + if (obj->type == type) + return obj; + else if (obj->type == OBJ_NONE) { + obj->type = type; + return obj; + } + else { + if (!quiet) + error("object %s is a %s, not a %s", + sha1_to_hex(obj->sha1), + typename(obj->type), typename(type)); + return NULL; + } +} + struct object *lookup_unknown_object(const unsigned char *sha1) { struct object *obj = lookup_object(sha1); -- cgit v1.2.3 From d66bebcbcfa46d72bb5008e1d211c0ea87200d86 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sun, 13 Jul 2014 02:42:12 -0400 Subject: object_as_type: set commit index The point of the "index" field of struct commit is that every allocated commit would have one. It is supposed to be an invariant that whenever object->type is set to OBJ_COMMIT, we have a unique index. Commit 969eba6 (commit: push commit_index update into alloc_commit_node, 2014-06-10) covered this case for newly-allocated commits. However, we may also allocate an "unknown" object via lookup_unknown_object, and only later convert it to a commit. We must make sure that we set the commit index when we switch the type field. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- object.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'object.c') diff --git a/object.c b/object.c index b2319f6246..69fbbbf504 100644 --- a/object.c +++ b/object.c @@ -163,6 +163,8 @@ void *object_as_type(struct object *obj, enum object_type type, int quiet) if (obj->type == type) return obj; else if (obj->type == OBJ_NONE) { + if (type == OBJ_COMMIT) + ((struct commit *)obj)->index = alloc_commit_index(); obj->type = type; return obj; } -- cgit v1.2.3