static void test_mapentry_check() { upb_status s = UPB_STATUS_INIT; upb_msgdef *m = upb_msgdef_new(&m); upb_fielddef *f = upb_fielddef_new(&f); upb_symtab *symtab = upb_symtab_new(&symtab); upb_msgdef *subm = upb_msgdef_new(&subm); upb_def *defs[2]; upb_msgdef_setfullname(m, "TestMessage", &s); upb_fielddef_setname(f, "field1", &s); upb_fielddef_setnumber(f, 1, &s); upb_fielddef_setlabel(f, UPB_LABEL_OPTIONAL); upb_fielddef_settype(f, UPB_TYPE_MESSAGE); upb_fielddef_setsubdefname(f, ".MapEntry", &s); upb_msgdef_addfield(m, f, &f, &s); ASSERT(upb_ok(&s)); upb_msgdef_setfullname(subm, "MapEntry", &s); upb_msgdef_setmapentry(subm, true); defs[0] = upb_msgdef_upcast_mutable(m); defs[1] = upb_msgdef_upcast_mutable(subm); upb_symtab_add(symtab, defs, 2, NULL, &s); /* Should not have succeeded: non-repeated field pointing to a MapEntry. */ ASSERT(!upb_ok(&s)); upb_fielddef_setlabel(f, UPB_LABEL_REPEATED); upb_symtab_add(symtab, defs, 2, NULL, &s); ASSERT(upb_ok(&s)); upb_symtab_unref(symtab, &symtab); upb_msgdef_unref(subm, &subm); upb_msgdef_unref(m, &m); }
static void test_symbol_resolution() { upb_status s = UPB_STATUS_INIT; upb_def *defs[2]; upb_msgdef *m1; upb_msgdef *m2; upb_msgdef *m3; upb_fielddef *m3_field1; upb_fielddef *m3_field2; upb_symtab *symtab = upb_symtab_new(&symtab); ASSERT(symtab); /* m1 has name "A.B.C" and no fields. We'll add it to the symtab now. */ m1 = upb_msgdef_new(&m1); ASSERT(m1); ASSERT_STATUS(upb_msgdef_setfullname(m1, "A.B.C", &s), &s); ASSERT_STATUS(upb_symtab_add(symtab, (upb_def**)&m1, 1, NULL, &s), &s); /* m2 has name "D.E" and no fields. We'll add it in the same batch as m3 * below. */ m2 = upb_msgdef_new(&m2); ASSERT(m2); ASSERT_STATUS(upb_msgdef_setfullname(m2, "D.E", &s), &s); /* m3 has name "F.G" and two fields, of type A.B.C and D.E respectively. We'll * add it in the same batch as m2 above. */ m3 = upb_msgdef_new(&m3); ASSERT(m3); ASSERT_STATUS(upb_msgdef_setfullname(m3, "F.G", &s), &s); m3_field1 = upb_fielddef_new(&m3_field1); ASSERT_STATUS(upb_fielddef_setname(m3_field1, "field1", &s), &s); ASSERT_STATUS(upb_fielddef_setnumber(m3_field1, 1, &s), &s); upb_fielddef_setlabel(m3_field1, UPB_LABEL_OPTIONAL); upb_fielddef_settype(m3_field1, UPB_TYPE_MESSAGE); ASSERT_STATUS(upb_fielddef_setsubdefname(m3_field1, ".A.B.C", &s), &s); ASSERT_STATUS(upb_msgdef_addfield(m3, m3_field1, NULL, &s), &s); m3_field2 = upb_fielddef_new(&m3_field2); ASSERT_STATUS(upb_fielddef_setname(m3_field2, "field2", &s), &s); ASSERT_STATUS(upb_fielddef_setnumber(m3_field2, 2, &s), &s); upb_fielddef_setlabel(m3_field2, UPB_LABEL_OPTIONAL); upb_fielddef_settype(m3_field2, UPB_TYPE_MESSAGE); ASSERT_STATUS(upb_fielddef_setsubdefname(m3_field2, ".D.E", &s), &s); ASSERT_STATUS(upb_msgdef_addfield(m3, m3_field2, NULL, &s), &s); defs[0] = upb_msgdef_upcast_mutable(m2); defs[1] = upb_msgdef_upcast_mutable(m3); ASSERT_STATUS(upb_symtab_add(symtab, defs, 2, NULL, &s), &s); upb_fielddef_unref(m3_field2, &m3_field2); upb_fielddef_unref(m3_field1, &m3_field1); upb_msgdef_unref(m3, &m3); upb_msgdef_unref(m2, &m2); upb_msgdef_unref(m1, &m1); upb_symtab_unref(symtab, &symtab); }
static void test_freeze_free() { bool ok; /* Test that freeze frees defs that were only being kept alive by virtue of * sharing a group with other defs that are being frozen. */ upb_msgdef *m1 = upb_msgdef_newnamed("M1", &m1); upb_msgdef *m2 = upb_msgdef_newnamed("M2", &m2); upb_msgdef *m3 = upb_msgdef_newnamed("M3", &m3); upb_msgdef *m4 = upb_msgdef_newnamed("M4", &m4); upb_fielddef *f = upb_fielddef_new(&f); /* Freeze M4 and make M1 point to it. */ upb_def_freeze((upb_def*const*)&m4, 1, NULL); upb_fielddef_settype(f, UPB_TYPE_MESSAGE); ASSERT(upb_fielddef_setnumber(f, 1, NULL)); ASSERT(upb_fielddef_setname(f, "foo", NULL)); ASSERT(upb_fielddef_setmsgsubdef(f, m4, NULL)); ASSERT(upb_msgdef_addfield(m1, f, &f, NULL)); /* After this unref, M1 is the only thing keeping M4 alive. */ upb_msgdef_unref(m4, &m4); /* Force M1/M2/M3 into a single mutable refcounting group. */ f = upb_fielddef_new(&f); upb_fielddef_settype(f, UPB_TYPE_MESSAGE); ASSERT(upb_fielddef_setnumber(f, 1, NULL)); ASSERT(upb_fielddef_setname(f, "foo", NULL)); ASSERT(upb_fielddef_setmsgsubdef(f, m1, NULL)); ASSERT(upb_fielddef_setmsgsubdef(f, m2, NULL)); ASSERT(upb_fielddef_setmsgsubdef(f, m3, NULL)); /* Make M3 cyclic with itself. */ ASSERT(upb_msgdef_addfield(m3, f, &f, NULL)); /* These will be kept alive since they are in the same refcounting group as * M3, which still has a ref. Note: this behavior is not guaranteed by the * API, but true in practice with its current implementation. */ upb_msgdef_unref(m1, &m1); upb_msgdef_unref(m2, &m2); /* Test that they are still alive (NOT allowed by the API). */ ok = strcmp("M1", upb_msgdef_fullname(m1)) == 0; ASSERT(ok); ok = strcmp("M2", upb_msgdef_fullname(m2)) == 0; ASSERT(ok); /* Freeze M3. If the test leaked no memory, then freeing m1 and m2 was * successful. */ ASSERT(upb_def_freeze((upb_def*const*)&m3, 1, NULL)); upb_msgdef_unref(m3, &m3); }
static void test_descriptor_flags() { upb_msgdef *m = upb_msgdef_new(&m); upb_msgdef *m2; upb_status s = UPB_STATUS_INIT; ASSERT(upb_msgdef_mapentry(m) == false); upb_msgdef_setfullname(m, "TestMessage", &s); ASSERT(upb_ok(&s)); upb_msgdef_setmapentry(m, true); ASSERT(upb_msgdef_mapentry(m) == true); m2 = upb_msgdef_dup(m, &m2); ASSERT(upb_msgdef_mapentry(m2) == true); upb_msgdef_unref(m, &m); upb_msgdef_unref(m2, &m2); }
static void test_noreftracking() { /* Reftracking is not required; clients can pass UPB_UNTRACKED_REF for owner. */ upb_msgdef *md = upb_msgdef_new(UPB_UNTRACKED_REF); upb_msgdef_ref(md, UPB_UNTRACKED_REF); /* Clients can mix tracked and untracked refs. */ upb_msgdef_ref(md, &md); upb_msgdef_unref(md, UPB_UNTRACKED_REF); upb_msgdef_unref(md, UPB_UNTRACKED_REF); /* Call some random function on the messagedef to test that it is alive. */ ASSERT(!upb_msgdef_isfrozen(md)); upb_msgdef_unref(md, &md); }
static void freehandlers(upb_refcounted *r) { upb_handlers *h = (upb_handlers*)r; for (int i = 0; i < h->msg->selector_count; i++) { upb_handlerfree *cleanup = h->table[i].attr.cleanup; if (cleanup) { cleanup(h->table[i].attr.handler_data_); } } upb_msgdef_unref(h->msg, h); free(h->sub); free(h); }
void upb_fielddef_unref(upb_fielddef *f) { // TODO. (void)f; if (!f) return; if (upb_atomic_unref(&f->refcount)) { if (f->msgdef) { upb_msgdef_unref(f->msgdef); // fielddef might be deleted now. } else { upb_fielddef_free(f); } } }
upb_msgdef *upb_msgdef_dup(const upb_msgdef *m, const void *owner) { upb_msgdef *newm = upb_msgdef_new(owner); if (!newm) return NULL; upb_def_setfullname(upb_upcast(newm), upb_def_fullname(upb_upcast(m))); upb_msg_iter i; for(upb_msg_begin(&i, m); !upb_msg_done(&i); upb_msg_next(&i)) { upb_fielddef *f = upb_fielddef_dup(upb_msg_iter_field(&i), &f); if (!f || !upb_msgdef_addfield(newm, f, &f)) { upb_msgdef_unref(newm, owner); return NULL; } } return newm; }
static void test_partial_freeze() { /* Test that freeze of only part of the graph correctly adjusts objects that * point to the newly-frozen objects. */ upb_msgdef *m1 = upb_msgdef_newnamed("M1", &m1); upb_msgdef *m2 = upb_msgdef_newnamed("M2", &m2); upb_msgdef *m3 = upb_msgdef_newnamed("M3", &m3); upb_fielddef *f1 = upb_fielddef_new(&f1); upb_fielddef *f2 = upb_fielddef_new(&f2); upb_def *defs[2]; defs[0] = upb_msgdef_upcast_mutable(m1); defs[1] = upb_msgdef_upcast_mutable(m2); upb_fielddef_settype(f1, UPB_TYPE_MESSAGE); ASSERT(upb_fielddef_setnumber(f1, 1, NULL)); ASSERT(upb_fielddef_setname(f1, "f1", NULL)); ASSERT(upb_fielddef_setmsgsubdef(f1, m1, NULL)); upb_fielddef_settype(f2, UPB_TYPE_MESSAGE); ASSERT(upb_fielddef_setnumber(f2, 2, NULL)); ASSERT(upb_fielddef_setname(f2, "f2", NULL)); ASSERT(upb_fielddef_setmsgsubdef(f2, m2, NULL)); ASSERT(upb_msgdef_addfield(m3, f1, &f1, NULL)); ASSERT(upb_msgdef_addfield(m3, f2, &f2, NULL)); /* Freeze M1 and M2, which should cause the group to be split * and m3 (left mutable) to take references on m1 and m2. */ ASSERT(upb_def_freeze(defs, 2, NULL)); ASSERT(upb_msgdef_isfrozen(m1)); ASSERT(upb_msgdef_isfrozen(m2)); ASSERT(!upb_msgdef_isfrozen(m3)); upb_msgdef_unref(m1, &m1); upb_msgdef_unref(m2, &m2); upb_msgdef_unref(m3, &m3); }
upb_msgdef *upb_msgdef_dup(const upb_msgdef *m, const void *owner) { upb_msgdef *newm = upb_msgdef_new(owner); if (!newm) return NULL; bool ok = upb_def_setfullname(UPB_UPCAST(newm), upb_def_fullname(UPB_UPCAST(m)), NULL); UPB_ASSERT_VAR(ok, ok); upb_msg_iter i; for(upb_msg_begin(&i, m); !upb_msg_done(&i); upb_msg_next(&i)) { upb_fielddef *f = upb_fielddef_dup(upb_msg_iter_field(&i), &f); if (!f || !upb_msgdef_addfield(newm, f, &f, NULL)) { upb_msgdef_unref(newm, owner); return NULL; } } return newm; }
void Descriptor_free(void* _self) { Descriptor* self = _self; upb_msgdef_unref(self->msgdef, &self->msgdef); if (self->layout) { free_layout(self->layout); } if (self->fill_handlers) { upb_handlers_unref(self->fill_handlers, &self->fill_handlers); } if (self->fill_method) { upb_pbdecodermethod_unref(self->fill_method, &self->fill_method); } if (self->pb_serialize_handlers) { upb_handlers_unref(self->pb_serialize_handlers, &self->pb_serialize_handlers); } if (self->json_serialize_handlers) { upb_handlers_unref(self->pb_serialize_handlers, &self->json_serialize_handlers); } xfree(self); }
void free_layout(MessageLayout* layout) { xfree(layout->fields); upb_msgdef_unref(layout->msgdef, &layout->msgdef); xfree(layout); }
/* TODO(haberman): we need a lot more testing of error conditions. * The came_from_user stuff in particular is not tested. */ bool upb_symtab_add(upb_symtab *s, upb_def *const*defs, int n, void *ref_donor, upb_status *status) { int i; upb_strtable_iter iter; upb_def **add_defs = NULL; upb_strtable addtab; upb_inttable seen; assert(!upb_symtab_isfrozen(s)); if (!upb_strtable_init(&addtab, UPB_CTYPE_PTR)) { upb_status_seterrmsg(status, "out of memory"); return false; } /* Add new defs to our "add" set. */ for (i = 0; i < n; i++) { upb_def *def = defs[i]; const char *fullname; upb_fielddef *f; if (upb_def_isfrozen(def)) { upb_status_seterrmsg(status, "added defs must be mutable"); goto err; } assert(!upb_def_isfrozen(def)); fullname = upb_def_fullname(def); if (!fullname) { upb_status_seterrmsg( status, "Anonymous defs cannot be added to a symtab"); goto err; } f = upb_dyncast_fielddef_mutable(def); if (f) { if (!upb_fielddef_containingtypename(f)) { upb_status_seterrmsg(status, "Standalone fielddefs must have a containing type " "(extendee) name set"); goto err; } } else { if (upb_strtable_lookup(&addtab, fullname, NULL)) { upb_status_seterrf(status, "Conflicting defs named '%s'", fullname); goto err; } /* We need this to back out properly, because if there is a failure we * need to donate the ref back to the caller. */ def->came_from_user = true; upb_def_donateref(def, ref_donor, s); if (!upb_strtable_insert(&addtab, fullname, upb_value_ptr(def))) goto oom_err; } } /* Add standalone fielddefs (ie. extensions) to the appropriate messages. * If the appropriate message only exists in the existing symtab, duplicate * it so we have a mutable copy we can add the fields to. */ for (i = 0; i < n; i++) { upb_def *def = defs[i]; upb_fielddef *f = upb_dyncast_fielddef_mutable(def); const char *msgname; upb_value v; upb_msgdef *m; if (!f) continue; msgname = upb_fielddef_containingtypename(f); /* We validated this earlier in this function. */ assert(msgname); /* If the extendee name is absolutely qualified, move past the initial ".". * TODO(haberman): it is not obvious what it would mean if this was not * absolutely qualified. */ if (msgname[0] == '.') { msgname++; } if (upb_strtable_lookup(&addtab, msgname, &v)) { /* Extendee is in the set of defs the user asked us to add. */ m = upb_value_getptr(v); } else { /* Need to find and dup the extendee from the existing symtab. */ const upb_msgdef *frozen_m = upb_symtab_lookupmsg(s, msgname); if (!frozen_m) { upb_status_seterrf(status, "Tried to extend message %s that does not exist " "in this SymbolTable.", msgname); goto err; } m = upb_msgdef_dup(frozen_m, s); if (!m) goto oom_err; if (!upb_strtable_insert(&addtab, msgname, upb_value_ptr(m))) { upb_msgdef_unref(m, s); goto oom_err; } } if (!upb_msgdef_addfield(m, f, ref_donor, status)) { goto err; } } /* Add dups of any existing def that can reach a def with the same name as * anything in our "add" set. */ if (!upb_inttable_init(&seen, UPB_CTYPE_BOOL)) goto oom_err; upb_strtable_begin(&iter, &s->symtab); for (; !upb_strtable_done(&iter); upb_strtable_next(&iter)) { upb_def *def = upb_value_getptr(upb_strtable_iter_value(&iter)); upb_resolve_dfs(def, &addtab, s, &seen, status); if (!upb_ok(status)) goto err; } upb_inttable_uninit(&seen); /* Now using the table, resolve symbolic references for subdefs. */ upb_strtable_begin(&iter, &addtab); for (; !upb_strtable_done(&iter); upb_strtable_next(&iter)) { const char *base; upb_def *def = upb_value_getptr(upb_strtable_iter_value(&iter)); upb_msgdef *m = upb_dyncast_msgdef_mutable(def); upb_msg_field_iter j; if (!m) continue; /* Type names are resolved relative to the message in which they appear. */ base = upb_msgdef_fullname(m); for(upb_msg_field_begin(&j, m); !upb_msg_field_done(&j); upb_msg_field_next(&j)) { upb_fielddef *f = upb_msg_iter_field(&j); const char *name = upb_fielddef_subdefname(f); if (name && !upb_fielddef_subdef(f)) { /* Try the lookup in the current set of to-be-added defs first. If not * there, try existing defs. */ upb_def *subdef = upb_resolvename(&addtab, base, name); if (subdef == NULL) { subdef = upb_resolvename(&s->symtab, base, name); } if (subdef == NULL) { upb_status_seterrf( status, "couldn't resolve name '%s' in message '%s'", name, base); goto err; } else if (!upb_fielddef_setsubdef(f, subdef, status)) { goto err; } } } } /* We need an array of the defs in addtab, for passing to upb_def_freeze. */ add_defs = malloc(sizeof(void*) * upb_strtable_count(&addtab)); if (add_defs == NULL) goto oom_err; upb_strtable_begin(&iter, &addtab); for (n = 0; !upb_strtable_done(&iter); upb_strtable_next(&iter)) { add_defs[n++] = upb_value_getptr(upb_strtable_iter_value(&iter)); } if (!upb_def_freeze(add_defs, n, status)) goto err; /* This must be delayed until all errors have been detected, since error * recovery code uses this table to cleanup defs. */ upb_strtable_uninit(&addtab); /* TODO(haberman) we don't properly handle errors after this point (like * OOM in upb_strtable_insert() below). */ for (i = 0; i < n; i++) { upb_def *def = add_defs[i]; const char *name = upb_def_fullname(def); upb_value v; bool success; if (upb_strtable_remove(&s->symtab, name, &v)) { const upb_def *def = upb_value_getptr(v); upb_def_unref(def, s); } success = upb_strtable_insert(&s->symtab, name, upb_value_ptr(def)); UPB_ASSERT_VAR(success, success == true); } free(add_defs); return true; oom_err: upb_status_seterrmsg(status, "out of memory"); err: { /* For defs the user passed in, we need to donate the refs back. For defs * we dup'd, we need to just unref them. */ upb_strtable_begin(&iter, &addtab); for (; !upb_strtable_done(&iter); upb_strtable_next(&iter)) { upb_def *def = upb_value_getptr(upb_strtable_iter_value(&iter)); bool came_from_user = def->came_from_user; def->came_from_user = false; if (came_from_user) { upb_def_donateref(def, s, ref_donor); } else { upb_def_unref(def, s); } } } upb_strtable_uninit(&addtab); free(add_defs); assert(!upb_ok(status)); return false; }