upb_def *upb_def_dup(const upb_def *def) { switch (def->type) { case UPB_DEF_MSG: return UPB_UPCAST(upb_msgdef_dup(upb_downcast_msgdef_const(def))); case UPB_DEF_ENUM: return UPB_UPCAST(upb_enumdef_dup(upb_downcast_enumdef_const(def))); default: assert(false); return NULL; } }
upb_def *upb_def_dup(const upb_def *def, const void *o) { switch (def->type) { case UPB_DEF_MSG: return upb_upcast(upb_msgdef_dup(upb_downcast_msgdef(def), o)); case UPB_DEF_FIELD: return upb_upcast(upb_fielddef_dup(upb_downcast_fielddef(def), o)); case UPB_DEF_ENUM: return upb_upcast(upb_enumdef_dup(upb_downcast_enumdef(def), o)); default: assert(false); return NULL; } }
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); }
/* 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; }