upb_fielddef *upb_fielddef_dup(const upb_fielddef *f, const void *owner) { upb_fielddef *newf = upb_fielddef_new(owner); if (!newf) return NULL; upb_fielddef_settype(newf, upb_fielddef_type(f)); upb_fielddef_setlabel(newf, upb_fielddef_label(f)); upb_fielddef_setnumber(newf, upb_fielddef_number(f), NULL); upb_fielddef_setname(newf, upb_fielddef_name(f), NULL); if (f->default_is_string) { str_t *s = upb_value_getptr(upb_fielddef_default(f)); upb_fielddef_setdefaultstr(newf, s->str, s->len, NULL); } else { upb_fielddef_setdefault(newf, upb_fielddef_default(f)); } const char *srcname; if (f->subdef_is_symbolic) { srcname = f->sub.name; // Might be NULL. } else { srcname = f->sub.def ? upb_def_fullname(f->sub.def) : NULL; } if (srcname) { char *newname = malloc(strlen(f->sub.def->fullname) + 2); if (!newname) { upb_fielddef_unref(newf, owner); return NULL; } strcpy(newname, "."); strcat(newname, f->sub.def->fullname); upb_fielddef_setsubdefname(newf, newname, NULL); free(newname); } return newf; }
static void track(const upb_refcounted *r, const void *owner, bool ref2) { assert(owner); if (owner == UPB_UNTRACKED_REF) return; upb_lock(); upb_value v; if (upb_inttable_lookupptr(r->refs, owner, &v)) { trackedref *ref = upb_value_getptr(v); // Since we allow multiple ref2's for the same to/from pair without // allocating separate memory for each one, we lose the fine-grained // tracking behavior we get with regular refs. Since ref2s only happen // inside upb, we'll accept this limitation until/unless there is a really // difficult upb-internal bug that can't be figured out without it. assert(ref2); assert(ref->is_ref2); ref->count++; } else { trackedref *ref = trackedref_new(ref2); bool ok = upb_inttable_insertptr(r->refs, owner, upb_value_ptr(ref)); CHECK_OOM(ok); if (ref2) { // We know this cast is safe when it is a ref2, because it's coming from // another refcounted object. const upb_refcounted *from = owner; assert(!upb_inttable_lookupptr(from->ref2s, r, NULL)); ok = upb_inttable_insertptr(from->ref2s, r, upb_value_ptr(NULL)); CHECK_OOM(ok); } } upb_unlock(); }
// Rewrites the dispatch tables into machine code offsets. static void patchdispatch(jitcompiler *jc) { upb_inttable_iter i; upb_inttable_begin(&i, &jc->group->methods); for (; !upb_inttable_done(&i); upb_inttable_next(&i)) { upb_pbdecodermethod *method = upb_value_getptr(upb_inttable_iter_value(&i)); method->is_native_ = true; upb_inttable *dispatch = &method->dispatch; upb_inttable_iter i2; upb_inttable_begin(&i2, dispatch); for (; !upb_inttable_done(&i2); upb_inttable_next(&i2)) { uintptr_t key = upb_inttable_iter_key(&i2); if (key == 0) continue; uint64_t val = upb_value_getuint64(upb_inttable_iter_value(&i2)); uint64_t newval; if (key <= UPB_MAX_FIELDNUMBER) { // Primary slot. uint64_t oldofs = val >> 16; uint64_t newofs = dispatchofs(jc, method, oldofs); newval = (val & 0xffff) | (newofs << 16); assert((int64_t)newval > 0); } else { // Secondary slot. Since we have 64 bits for the value, we use an // absolute offset. newval = (uint64_t)(jc->group->jit_code + nativeofs(jc, method, val)); } bool ok = upb_inttable_replace(dispatch, key, upb_value_uint64(newval)); UPB_ASSERT_VAR(ok, ok); }
// TODO(haberman): discard upb_handlers* objects that do not actually have any // handlers set and cannot reach any upb_handlers* object that does. This is // slightly tricky to do correctly. static upb_handlers *newformsg(const upb_msgdef *m, const void *owner, dfs_state *s) { upb_handlers *h = upb_handlers_new(m, owner); if (!h) return NULL; if (!upb_inttable_insertptr(&s->tab, m, upb_value_ptr(h))) goto oom; s->callback(s->closure, h); // For each submessage field, get or create a handlers object and set it as // the subhandlers. upb_msg_iter i; for(upb_msg_begin(&i, m); !upb_msg_done(&i); upb_msg_next(&i)) { upb_fielddef *f = upb_msg_iter_field(&i); if (!upb_fielddef_issubmsg(f)) continue; const upb_msgdef *subdef = upb_downcast_msgdef(upb_fielddef_subdef(f)); upb_value subm_ent; if (upb_inttable_lookupptr(&s->tab, subdef, &subm_ent)) { upb_handlers_setsubhandlers(h, f, upb_value_getptr(subm_ent)); } else { upb_handlers *sub_mh = newformsg(subdef, &sub_mh, s); if (!sub_mh) goto oom; upb_handlers_setsubhandlers(h, f, sub_mh); upb_handlers_unref(sub_mh, &sub_mh); } } return h; oom: upb_handlers_unref(h, owner); return NULL; }
// Pops an obj from the Tarjan stack and sets it to WHITE, with a ptr to its // SCC group. static upb_refcounted *pop(tarjan *t) { upb_refcounted *r = upb_value_getptr(upb_inttable_pop(&t->stack)); assert(color(t, r) == GREEN); // This defines the attr layout for nodes in the WHITE state. // Top of group stack is [group, NULL]; we point at group. setattr(t, r, WHITE | (upb_inttable_count(&t->groups) - 2) << 8); return r; }
const char *upb_fielddef_defaultstr(const upb_fielddef *f, size_t *len) { assert(f->type_is_set_); if (f->default_is_string) { str_t *str = upb_value_getptr(f->defaultval); if (len) *len = str->len; return str->str; } return NULL; }
static void checkref(const upb_refcounted *r, const void *owner, bool ref2) { upb_lock(); upb_value v; bool found = upb_inttable_lookupptr(r->refs, owner, &v); UPB_ASSERT_VAR(found, found); trackedref *ref = upb_value_getptr(v); assert(ref->is_ref2 == ref2); upb_unlock(); }
static void visitgroup(const upb_refcounted *r, upb_refcounted_visit *visit, void *closure) { const mgroup *g = (const mgroup*)r; upb_inttable_iter i; upb_inttable_begin(&i, &g->methods); for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { upb_pbdecodermethod *method = upb_value_getptr(upb_inttable_iter_value(&i)); visit(r, UPB_UPCAST(method), closure); } }
void upb_msgfactory_free(upb_msgfactory *f) { upb_inttable_iter i; upb_inttable_begin(&i, &f->layouts); for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { upb_msglayout *l = upb_value_getptr(upb_inttable_iter_value(&i)); upb_msglayout_free(l); } upb_inttable_uninit(&f->layouts); upb_gfree(f); }
static void upb_symtab_free(upb_refcounted *r) { upb_symtab *s = (upb_symtab*)r; upb_strtable_iter i; upb_strtable_begin(&i, &s->symtab); for (; !upb_strtable_done(&i); upb_strtable_next(&i)) { const upb_def *def = upb_value_getptr(upb_strtable_iter_value(&i)); upb_def_unref(def, s); } upb_strtable_uninit(&s->symtab); free(s); }
const upb_msglayout *upb_msgfactory_getlayout(upb_msgfactory *f, const upb_msgdef *m) { upb_value v; UPB_ASSERT(upb_symtab_lookupmsg(f->symtab, upb_msgdef_fullname(m)) == m); UPB_ASSERT(!upb_msgdef_mapentry(m)); if (upb_inttable_lookupptr(&f->layouts, m, &v)) { UPB_ASSERT(upb_value_getptr(v)); return upb_value_getptr(v); } else { /* In case of circular dependency, layout has to be inserted first. */ upb_msglayout *l = upb_gmalloc(sizeof(*l)); upb_msgfactory *mutable_f = (void*)f; upb_inttable_insertptr(&mutable_f->layouts, m, upb_value_ptr(l)); UPB_ASSERT(l); if (!upb_msglayout_init(m, l, f)) { upb_msglayout_free(l); } return l; } }
void upb_fielddef_setdefault(upb_fielddef *f, upb_value value) { assert(f->type_is_set_); assert(!upb_fielddef_isfrozen(f)); assert(!upb_fielddef_isstring(f) && !upb_fielddef_issubmsg(f)); if (f->default_is_string) { str_t *s = upb_value_getptr(f->defaultval); assert(s); freestr(s); } f->defaultval = value; f->default_is_string = false; }
static void freejitcompiler(jitcompiler *jc) { upb_inttable_iter i; upb_inttable_begin(&i, &jc->asmlabels); for (; !upb_inttable_done(&i); upb_inttable_next(&i)) { free(upb_value_getptr(upb_inttable_iter_value(&i))); } upb_inttable_uninit(&jc->asmlabels); upb_inttable_uninit(&jc->pclabels); upb_inttable_uninit(&jc->pcdefined); dasm_free(jc); free(jc->globals); free(jc); }
/* Given a symbol and the base symbol inside which it is defined, find the * symbol's definition in t. */ static upb_def *upb_resolvename(const upb_strtable *t, const char *base, const char *sym) { if(strlen(sym) == 0) return NULL; if(sym[0] == '.') { /* Symbols starting with '.' are absolute, so we do a single lookup. * Slice to omit the leading '.' */ upb_value v; return upb_strtable_lookup(t, sym + 1, &v) ? upb_value_getptr(v) : NULL; } else { /* Remove components from base until we find an entry or run out. * TODO: This branch is totally broken, but currently not used. */ (void)base; assert(false); return NULL; } }
bool upb_fielddef_setdefaultstr(upb_fielddef *f, const void *str, size_t len, upb_status *s) { assert(upb_fielddef_isstring(f) || f->type_ == UPB_TYPE_ENUM); if (f->type_ == UPB_TYPE_ENUM && !upb_isident(str, len, false, s)) return false; if (f->default_is_string) { str_t *s = upb_value_getptr(f->defaultval); assert(s); freestr(s); } else { assert(f->type_ == UPB_TYPE_ENUM); } str_t *str2 = newstr(str, len); upb_value_setptr(&f->defaultval, str2); f->default_is_string = true; return true; }
// Populates the given UPB_CTYPE_INT32 inttable with counts of ref2's that // originate from the given owner. static void getref2s(const upb_refcounted *owner, upb_inttable *tab) { upb_lock(); upb_inttable_iter i; upb_inttable_begin(&i, owner->ref2s); for(; !upb_inttable_done(&i); upb_inttable_next(&i)) { upb_refcounted *to = (upb_refcounted*)upb_inttable_iter_key(&i); // To get the count we need to look in the target's table. upb_value v; bool found = upb_inttable_lookupptr(to->refs, owner, &v); assert(found); trackedref *ref = upb_value_getptr(v); upb_value count = upb_value_int32(ref->count); bool ok = upb_inttable_insertptr(tab, to, count); CHECK_OOM(ok); } upb_unlock(); }
void upb_stdmsg_free(void *m, const upb_msgdef *md) { if (m == NULL) return; upb_msg_iter i; for(i = upb_msg_begin(md); !upb_msg_done(i); i = upb_msg_next(md, i)) { upb_fielddef *f = upb_msg_iter_field(i); if (!upb_isseq(f) && !upb_issubmsg(f) && !upb_isstring(f)) continue; void *subp = upb_value_getptr(upb_stdmsg_getptr(m, f->fval)); if (subp == NULL) continue; if (upb_isseq(f)) { upb_stdseq_free(subp, f); } else if (upb_issubmsg(f)) { upb_stdmsg_free(subp, upb_downcast_msgdef(f->def)); } else { upb_stdarray *str = subp; free(str->ptr); free(str); } } free(m); }
bool upb_fielddef_resolveenumdefault(upb_fielddef *f, upb_status *s) { if (!upb_fielddef_default_is_symbolic(f)) return true; str_t *str = upb_value_getptr(f->defaultval); const upb_enumdef *e = upb_downcast_enumdef(upb_fielddef_subdef(f)); assert(str); // Points to either a real default or the empty string. assert(e); if (str->len == 0) { // The "default default" for an enum is the first defined value. upb_value_setint32(&f->defaultval, e->defaultval); } else { int32_t val = 0; if (!upb_enumdef_ntoi(e, str->str, &val)) { upb_status_seterrf(s, "enum default not found in enum (%s)", str->str); return false; } upb_value_setint32(&f->defaultval, val); } f->default_is_string = false; freestr(str); return true; }
static void untrack(const upb_refcounted *r, const void *owner, bool ref2) { assert(owner); if (owner == UPB_UNTRACKED_REF) return; upb_lock(); upb_value v; bool found = upb_inttable_lookupptr(r->refs, owner, &v); // This assert will fail if an owner attempts to release a ref it didn't have. UPB_ASSERT_VAR(found, found); trackedref *ref = upb_value_getptr(v); assert(ref->is_ref2 == ref2); if (--ref->count == 0) { free(ref); upb_inttable_removeptr(r->refs, owner, NULL); if (ref2) { // We know this cast is safe when it is a ref2, because it's coming from // another refcounted object. const upb_refcounted *from = owner; bool removed = upb_inttable_removeptr(from->ref2s, r, NULL); assert(removed); } } upb_unlock(); }
const upb_def *upb_symtab_iter_def(const upb_symtab_iter *iter) { return upb_value_getptr(upb_strtable_iter_value(&iter->iter)); }
const upb_fielddef *upb_msgdef_ntof(const upb_msgdef *m, const char *name) { upb_value val; return upb_strtable_lookup(&m->ntof, name, &val) ? upb_value_getptr(val) : NULL; }
const upb_fielddef *upb_msgdef_itof(const upb_msgdef *m, uint32_t i) { upb_value val; return upb_inttable_lookup32(&m->itof, i, &val) ? upb_value_getptr(val) : NULL; }
static void upb_fielddef_uninit_default(upb_fielddef *f) { if (f->type_is_set_ && f->default_is_string) freestr(upb_value_getptr(f->defaultval)); }
upb_fielddef *upb_msg_iter_field(upb_msg_iter *iter) { return (upb_fielddef*)upb_value_getptr(upb_inttable_iter_value(iter)); }
const upb_fielddef *upb_msgdef_itof(const upb_msgdef *m, uint32_t i) { const upb_value *val = upb_inttable_lookup32(&m->itof, i); return val ? (const upb_fielddef*)upb_value_getptr(*val) : NULL; }
const upb_def *upb_symtab_lookup(const upb_symtab *s, const char *sym) { upb_value v; upb_def *ret = upb_strtable_lookup(&s->symtab, sym, &v) ? upb_value_getptr(v) : NULL; return ret; }
const upb_fielddef *upb_msgdef_ntof(const upb_msgdef *m, const char *name) { const upb_value *val = upb_strtable_lookup(&m->ntof, name); return val ? (upb_fielddef*)upb_value_getptr(*val) : NULL; }
/* 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; }
const upb_enumdef *upb_symtab_lookupenum(const upb_symtab *s, const char *sym) { upb_value v; upb_def *def = upb_strtable_lookup(&s->symtab, sym, &v) ? upb_value_getptr(v) : NULL; return def ? upb_dyncast_enumdef(def) : NULL; }