commit
9a9a1097a7
9 changed files with 664 additions and 695 deletions
@ -1,495 +0,0 @@ |
|||||||
|
|
||||||
#include "upb/structdefs.int.h" |
|
||||||
#include "upb/symtab.h" |
|
||||||
|
|
||||||
#include <string.h> |
|
||||||
|
|
||||||
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); |
|
||||||
upb_gfree(s); |
|
||||||
} |
|
||||||
|
|
||||||
upb_symtab *upb_symtab_new(const void *owner) { |
|
||||||
static const struct upb_refcounted_vtbl vtbl = {NULL, &upb_symtab_free}; |
|
||||||
|
|
||||||
upb_symtab *s = upb_gmalloc(sizeof(*s)); |
|
||||||
if (!s) { |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
upb_refcounted_init(upb_symtab_upcast_mutable(s), &vtbl, owner); |
|
||||||
upb_strtable_init(&s->symtab, UPB_CTYPE_PTR); |
|
||||||
return s; |
|
||||||
} |
|
||||||
|
|
||||||
void upb_symtab_freeze(upb_symtab *s) { |
|
||||||
upb_refcounted *r; |
|
||||||
bool ok; |
|
||||||
|
|
||||||
UPB_ASSERT(!upb_symtab_isfrozen(s)); |
|
||||||
r = upb_symtab_upcast_mutable(s); |
|
||||||
/* The symtab does not take ref2's (see refcounted.h) on the defs, because
|
|
||||||
* defs cannot refer back to the table and therefore cannot create cycles. So |
|
||||||
* 0 will suffice for maxdepth here. */ |
|
||||||
ok = upb_refcounted_freeze(&r, 1, NULL, 0); |
|
||||||
UPB_ASSERT(ok); |
|
||||||
} |
|
||||||
|
|
||||||
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_msgdef *upb_symtab_lookupmsg(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_msgdef(def) : NULL; |
|
||||||
} |
|
||||||
|
|
||||||
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; |
|
||||||
} |
|
||||||
|
|
||||||
/* 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; |
|
||||||
UPB_ASSERT(false); |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
const upb_def *upb_symtab_resolve(const upb_symtab *s, const char *base, |
|
||||||
const char *sym) { |
|
||||||
upb_def *ret = upb_resolvename(&s->symtab, base, sym); |
|
||||||
return ret; |
|
||||||
} |
|
||||||
|
|
||||||
/* Starts a depth-first traversal at "def", recursing into any subdefs
|
|
||||||
* (ie. submessage types). Adds duplicates of existing defs to addtab |
|
||||||
* wherever necessary, so that the resulting symtab will be consistent once |
|
||||||
* addtab is added. |
|
||||||
* |
|
||||||
* More specifically, if any def D is found in the DFS that: |
|
||||||
* |
|
||||||
* 1. can reach a def that is being replaced by something in addtab, AND |
|
||||||
* |
|
||||||
* 2. is not itself being replaced already (ie. this name doesn't already |
|
||||||
* exist in addtab) |
|
||||||
* |
|
||||||
* ...then a duplicate (new copy) of D will be added to addtab. |
|
||||||
* |
|
||||||
* Returns true if this happened for any def reachable from "def." |
|
||||||
* |
|
||||||
* It is slightly tricky to do this correctly in the presence of cycles. If we |
|
||||||
* detect that our DFS has hit a cycle, we might not yet know if any SCCs on |
|
||||||
* our stack can reach a def in addtab or not. Once we figure this out, that |
|
||||||
* answer needs to apply to *all* defs in these SCCs, even if we visited them |
|
||||||
* already. So a straight up one-pass cycle-detecting DFS won't work. |
|
||||||
* |
|
||||||
* To work around this problem, we traverse each SCC (which we already |
|
||||||
* computed, since these defs are frozen) as a single node. We first compute |
|
||||||
* whether the SCC as a whole can reach any def in addtab, then we dup (or not) |
|
||||||
* the entire SCC. This requires breaking the encapsulation of upb_refcounted, |
|
||||||
* since that is where we get the data about what SCC we are in. */ |
|
||||||
static bool upb_resolve_dfs(const upb_def *def, upb_strtable *addtab, |
|
||||||
const void *new_owner, upb_inttable *seen, |
|
||||||
upb_status *s) { |
|
||||||
upb_value v; |
|
||||||
bool need_dup; |
|
||||||
const upb_def *base; |
|
||||||
const void* memoize_key; |
|
||||||
|
|
||||||
/* Memoize results of this function for efficiency (since we're traversing a
|
|
||||||
* DAG this is not needed to limit the depth of the search). |
|
||||||
* |
|
||||||
* We memoize by SCC instead of by individual def. */ |
|
||||||
memoize_key = def->base.group; |
|
||||||
|
|
||||||
if (upb_inttable_lookupptr(seen, memoize_key, &v)) |
|
||||||
return upb_value_getbool(v); |
|
||||||
|
|
||||||
/* Visit submessages for all messages in the SCC. */ |
|
||||||
need_dup = false; |
|
||||||
base = def; |
|
||||||
do { |
|
||||||
upb_value v; |
|
||||||
const upb_msgdef *m; |
|
||||||
|
|
||||||
UPB_ASSERT(upb_def_isfrozen(def)); |
|
||||||
if (def->type == UPB_DEF_FIELD) continue; |
|
||||||
if (upb_strtable_lookup(addtab, upb_def_fullname(def), &v)) { |
|
||||||
need_dup = true; |
|
||||||
} |
|
||||||
|
|
||||||
/* For messages, continue the recursion by visiting all subdefs, but only
|
|
||||||
* ones in different SCCs. */ |
|
||||||
m = upb_dyncast_msgdef(def); |
|
||||||
if (m) { |
|
||||||
upb_msg_field_iter i; |
|
||||||
for(upb_msg_field_begin(&i, m); |
|
||||||
!upb_msg_field_done(&i); |
|
||||||
upb_msg_field_next(&i)) { |
|
||||||
upb_fielddef *f = upb_msg_iter_field(&i); |
|
||||||
const upb_def *subdef; |
|
||||||
|
|
||||||
if (!upb_fielddef_hassubdef(f)) continue; |
|
||||||
subdef = upb_fielddef_subdef(f); |
|
||||||
|
|
||||||
/* Skip subdefs in this SCC. */ |
|
||||||
if (def->base.group == subdef->base.group) continue; |
|
||||||
|
|
||||||
/* |= to avoid short-circuit; we need its side-effects. */ |
|
||||||
need_dup |= upb_resolve_dfs(subdef, addtab, new_owner, seen, s); |
|
||||||
if (!upb_ok(s)) return false; |
|
||||||
} |
|
||||||
} |
|
||||||
} while ((def = (upb_def*)def->base.next) != base); |
|
||||||
|
|
||||||
if (need_dup) { |
|
||||||
/* Dup all defs in this SCC that don't already have entries in addtab. */ |
|
||||||
def = base; |
|
||||||
do { |
|
||||||
const char *name; |
|
||||||
|
|
||||||
if (def->type == UPB_DEF_FIELD) continue; |
|
||||||
name = upb_def_fullname(def); |
|
||||||
if (!upb_strtable_lookup(addtab, name, NULL)) { |
|
||||||
upb_def *newdef = upb_def_dup(def, new_owner); |
|
||||||
if (!newdef) goto oom; |
|
||||||
newdef->came_from_user = false; |
|
||||||
if (!upb_strtable_insert(addtab, name, upb_value_ptr(newdef))) |
|
||||||
goto oom; |
|
||||||
} |
|
||||||
} while ((def = (upb_def*)def->base.next) != base); |
|
||||||
} |
|
||||||
|
|
||||||
upb_inttable_insertptr(seen, memoize_key, upb_value_bool(need_dup)); |
|
||||||
return need_dup; |
|
||||||
|
|
||||||
oom: |
|
||||||
upb_status_seterrmsg(s, "out of memory"); |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
/* TODO(haberman): we need a lot more testing of error conditions.
|
|
||||||
* The came_from_user stuff in particular is not tested. */ |
|
||||||
static bool symtab_add(upb_symtab *s, upb_def *const*defs, size_t n, |
|
||||||
void *ref_donor, upb_refcounted *freeze_also, |
|
||||||
upb_status *status) { |
|
||||||
size_t i; |
|
||||||
size_t add_n; |
|
||||||
size_t freeze_n; |
|
||||||
upb_strtable_iter iter; |
|
||||||
upb_refcounted **add_objs = NULL; |
|
||||||
upb_def **add_defs = NULL; |
|
||||||
size_t add_objs_size; |
|
||||||
upb_strtable addtab; |
|
||||||
upb_inttable seen; |
|
||||||
|
|
||||||
if (n == 0 && !freeze_also) { |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
UPB_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; |
|
||||||
} |
|
||||||
UPB_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. */ |
|
||||||
UPB_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_refcounted_freeze(). */ |
|
||||||
add_objs_size = upb_strtable_count(&addtab); |
|
||||||
if (freeze_also) { |
|
||||||
add_objs_size++; |
|
||||||
} |
|
||||||
|
|
||||||
add_defs = upb_gmalloc(sizeof(void*) * add_objs_size); |
|
||||||
if (add_defs == NULL) goto oom_err; |
|
||||||
upb_strtable_begin(&iter, &addtab); |
|
||||||
for (add_n = 0; !upb_strtable_done(&iter); upb_strtable_next(&iter)) { |
|
||||||
add_defs[add_n++] = upb_value_getptr(upb_strtable_iter_value(&iter)); |
|
||||||
} |
|
||||||
|
|
||||||
/* Validate defs. */ |
|
||||||
if (!_upb_def_validate(add_defs, add_n, status)) { |
|
||||||
goto err; |
|
||||||
} |
|
||||||
|
|
||||||
/* Cheat a little and give the array a new type.
|
|
||||||
* This is probably undefined behavior, but this code will be deleted soon. */ |
|
||||||
add_objs = (upb_refcounted**)add_defs; |
|
||||||
|
|
||||||
freeze_n = add_n; |
|
||||||
if (freeze_also) { |
|
||||||
add_objs[freeze_n++] = freeze_also; |
|
||||||
} |
|
||||||
|
|
||||||
if (!upb_refcounted_freeze(add_objs, freeze_n, status, |
|
||||||
UPB_MAX_MESSAGE_DEPTH * 2)) { |
|
||||||
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 < add_n; i++) { |
|
||||||
upb_def *def = (upb_def*)add_objs[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(success == true); |
|
||||||
} |
|
||||||
upb_gfree(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); |
|
||||||
upb_gfree(add_defs); |
|
||||||
UPB_ASSERT(!upb_ok(status)); |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
bool upb_symtab_add(upb_symtab *s, upb_def *const*defs, size_t n, |
|
||||||
void *ref_donor, upb_status *status) { |
|
||||||
return symtab_add(s, defs, n, ref_donor, NULL, status); |
|
||||||
} |
|
||||||
|
|
||||||
bool upb_symtab_addfile(upb_symtab *s, upb_filedef *file, upb_status *status) { |
|
||||||
size_t n; |
|
||||||
size_t i; |
|
||||||
upb_def **defs; |
|
||||||
bool ret; |
|
||||||
|
|
||||||
n = upb_filedef_defcount(file); |
|
||||||
defs = upb_gmalloc(sizeof(*defs) * n); |
|
||||||
|
|
||||||
if (defs == NULL) { |
|
||||||
upb_status_seterrmsg(status, "Out of memory"); |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
for (i = 0; i < n; i++) { |
|
||||||
defs[i] = upb_filedef_mutabledef(file, i); |
|
||||||
} |
|
||||||
|
|
||||||
ret = symtab_add(s, defs, n, NULL, upb_filedef_upcast_mutable(file), status); |
|
||||||
|
|
||||||
upb_gfree(defs); |
|
||||||
return ret; |
|
||||||
} |
|
||||||
|
|
||||||
/* Iteration. */ |
|
||||||
|
|
||||||
static void advance_to_matching(upb_symtab_iter *iter) { |
|
||||||
if (iter->type == UPB_DEF_ANY) |
|
||||||
return; |
|
||||||
|
|
||||||
while (!upb_strtable_done(&iter->iter) && |
|
||||||
iter->type != upb_symtab_iter_def(iter)->type) { |
|
||||||
upb_strtable_next(&iter->iter); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void upb_symtab_begin(upb_symtab_iter *iter, const upb_symtab *s, |
|
||||||
upb_deftype_t type) { |
|
||||||
upb_strtable_begin(&iter->iter, &s->symtab); |
|
||||||
iter->type = type; |
|
||||||
advance_to_matching(iter); |
|
||||||
} |
|
||||||
|
|
||||||
void upb_symtab_next(upb_symtab_iter *iter) { |
|
||||||
upb_strtable_next(&iter->iter); |
|
||||||
advance_to_matching(iter); |
|
||||||
} |
|
||||||
|
|
||||||
bool upb_symtab_done(const upb_symtab_iter *iter) { |
|
||||||
return upb_strtable_done(&iter->iter); |
|
||||||
} |
|
||||||
|
|
||||||
const upb_def *upb_symtab_iter_def(const upb_symtab_iter *iter) { |
|
||||||
return upb_value_getptr(upb_strtable_iter_value(&iter->iter)); |
|
||||||
} |
|
@ -1,195 +0,0 @@ |
|||||||
/*
|
|
||||||
** upb::SymbolTable (upb_symtab) |
|
||||||
** |
|
||||||
** A symtab (symbol table) stores a name->def map of upb_defs. Clients could |
|
||||||
** always create such tables themselves, but upb_symtab has logic for resolving |
|
||||||
** symbolic references, and in particular, for keeping a whole set of consistent |
|
||||||
** defs when replacing some subset of those defs. This logic is nontrivial. |
|
||||||
** |
|
||||||
** This is a mixed C/C++ interface that offers a full API to both languages. |
|
||||||
** See the top-level README for more information. |
|
||||||
*/ |
|
||||||
|
|
||||||
#ifndef UPB_SYMTAB_H_ |
|
||||||
#define UPB_SYMTAB_H_ |
|
||||||
|
|
||||||
#include "upb/def.h" |
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
#include <vector> |
|
||||||
namespace upb { class SymbolTable; } |
|
||||||
#endif |
|
||||||
|
|
||||||
UPB_DECLARE_DERIVED_TYPE(upb::SymbolTable, upb::RefCounted, |
|
||||||
upb_symtab, upb_refcounted) |
|
||||||
|
|
||||||
typedef struct { |
|
||||||
UPB_PRIVATE_FOR_CPP |
|
||||||
upb_strtable_iter iter; |
|
||||||
upb_deftype_t type; |
|
||||||
} upb_symtab_iter; |
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
|
|
||||||
/* Non-const methods in upb::SymbolTable are NOT thread-safe. */ |
|
||||||
class upb::SymbolTable { |
|
||||||
public: |
|
||||||
/* Returns a new symbol table with a single ref owned by "owner."
|
|
||||||
* Returns NULL if memory allocation failed. */ |
|
||||||
static reffed_ptr<SymbolTable> New(); |
|
||||||
|
|
||||||
/* Include RefCounted base methods. */ |
|
||||||
UPB_REFCOUNTED_CPPMETHODS |
|
||||||
|
|
||||||
/* For all lookup functions, the returned pointer is not owned by the
|
|
||||||
* caller; it may be invalidated by any non-const call or unref of the |
|
||||||
* SymbolTable! To protect against this, take a ref if desired. */ |
|
||||||
|
|
||||||
/* Freezes the symbol table: prevents further modification of it.
|
|
||||||
* After the Freeze() operation is successful, the SymbolTable must only be |
|
||||||
* accessed via a const pointer. |
|
||||||
* |
|
||||||
* Unlike with upb::MessageDef/upb::EnumDef/etc, freezing a SymbolTable is not |
|
||||||
* a necessary step in using a SymbolTable. If you have no need for it to be |
|
||||||
* immutable, there is no need to freeze it ever. However sometimes it is |
|
||||||
* useful, and SymbolTables that are statically compiled into the binary are |
|
||||||
* always frozen by nature. */ |
|
||||||
void Freeze(); |
|
||||||
|
|
||||||
/* Resolves the given symbol using the rules described in descriptor.proto,
|
|
||||||
* namely: |
|
||||||
* |
|
||||||
* If the name starts with a '.', it is fully-qualified. Otherwise, |
|
||||||
* C++-like scoping rules are used to find the type (i.e. first the nested |
|
||||||
* types within this message are searched, then within the parent, on up |
|
||||||
* to the root namespace). |
|
||||||
* |
|
||||||
* If not found, returns NULL. */ |
|
||||||
const Def* Resolve(const char* base, const char* sym) const; |
|
||||||
|
|
||||||
/* Finds an entry in the symbol table with this exact name. If not found,
|
|
||||||
* returns NULL. */ |
|
||||||
const Def* Lookup(const char *sym) const; |
|
||||||
const MessageDef* LookupMessage(const char *sym) const; |
|
||||||
const EnumDef* LookupEnum(const char *sym) const; |
|
||||||
|
|
||||||
/* TODO: introduce a C++ iterator, but make it nice and templated so that if
|
|
||||||
* you ask for an iterator of MessageDef the iterated elements are strongly |
|
||||||
* typed as MessageDef*. */ |
|
||||||
|
|
||||||
/* Adds the given mutable defs to the symtab, resolving all symbols
|
|
||||||
* (including enum default values) and finalizing the defs. Only one def per |
|
||||||
* name may be in the list, but defs can replace existing defs in the symtab. |
|
||||||
* All defs must have a name -- anonymous defs are not allowed. Anonymous |
|
||||||
* defs can still be frozen by calling upb_def_freeze() directly. |
|
||||||
* |
|
||||||
* Any existing defs that can reach defs that are being replaced will |
|
||||||
* themselves be replaced also, so that the resulting set of defs is fully |
|
||||||
* consistent. |
|
||||||
* |
|
||||||
* This logic implemented in this method is a convenience; ultimately it |
|
||||||
* calls some combination of upb_fielddef_setsubdef(), upb_def_dup(), and |
|
||||||
* upb_freeze(), any of which the client could call themself. However, since |
|
||||||
* the logic for doing so is nontrivial, we provide it here. |
|
||||||
* |
|
||||||
* The entire operation either succeeds or fails. If the operation fails, |
|
||||||
* the symtab is unchanged, false is returned, and status indicates the |
|
||||||
* error. The caller passes a ref on all defs to the symtab (even if the |
|
||||||
* operation fails). |
|
||||||
* |
|
||||||
* TODO(haberman): currently failure will leave the symtab unchanged, but may |
|
||||||
* leave the defs themselves partially resolved. Does this matter? If so we |
|
||||||
* could do a prepass that ensures that all symbols are resolvable and bail |
|
||||||
* if not, so we don't mutate anything until we know the operation will |
|
||||||
* succeed. |
|
||||||
* |
|
||||||
* TODO(haberman): since the defs must be mutable, refining a frozen def |
|
||||||
* requires making mutable copies of the entire tree. This is wasteful if |
|
||||||
* only a few messages are changing. We may want to add a way of adding a |
|
||||||
* tree of frozen defs to the symtab (perhaps an alternate constructor where |
|
||||||
* you pass the root of the tree?) */ |
|
||||||
bool Add(Def*const* defs, size_t n, void* ref_donor, Status* status); |
|
||||||
|
|
||||||
bool Add(const std::vector<Def*>& defs, void *owner, Status* status) { |
|
||||||
return Add((Def*const*)&defs[0], defs.size(), owner, status); |
|
||||||
} |
|
||||||
|
|
||||||
/* Resolves all subdefs for messages in this file and attempts to freeze the
|
|
||||||
* file. If this succeeds, adds all the symbols to this SymbolTable |
|
||||||
* (replacing any existing ones with the same names). */ |
|
||||||
bool AddFile(FileDef* file, Status* s); |
|
||||||
|
|
||||||
private: |
|
||||||
UPB_DISALLOW_POD_OPS(SymbolTable, upb::SymbolTable) |
|
||||||
}; |
|
||||||
|
|
||||||
#endif /* __cplusplus */ |
|
||||||
|
|
||||||
UPB_BEGIN_EXTERN_C |
|
||||||
|
|
||||||
/* Native C API. */ |
|
||||||
|
|
||||||
/* Include refcounted methods like upb_symtab_ref(). */ |
|
||||||
UPB_REFCOUNTED_CMETHODS(upb_symtab, upb_symtab_upcast) |
|
||||||
|
|
||||||
upb_symtab *upb_symtab_new(const void *owner); |
|
||||||
void upb_symtab_freeze(upb_symtab *s); |
|
||||||
const upb_def *upb_symtab_resolve(const upb_symtab *s, const char *base, |
|
||||||
const char *sym); |
|
||||||
const upb_def *upb_symtab_lookup(const upb_symtab *s, const char *sym); |
|
||||||
const upb_msgdef *upb_symtab_lookupmsg(const upb_symtab *s, const char *sym); |
|
||||||
const upb_enumdef *upb_symtab_lookupenum(const upb_symtab *s, const char *sym); |
|
||||||
bool upb_symtab_add(upb_symtab *s, upb_def *const*defs, size_t n, |
|
||||||
void *ref_donor, upb_status *status); |
|
||||||
bool upb_symtab_addfile(upb_symtab *s, upb_filedef *file, upb_status* status); |
|
||||||
|
|
||||||
/* upb_symtab_iter i;
|
|
||||||
* for(upb_symtab_begin(&i, s, type); !upb_symtab_done(&i); |
|
||||||
* upb_symtab_next(&i)) { |
|
||||||
* const upb_def *def = upb_symtab_iter_def(&i); |
|
||||||
* // ...
|
|
||||||
* } |
|
||||||
* |
|
||||||
* For C we don't have separate iterators for const and non-const. |
|
||||||
* It is the caller's responsibility to cast the upb_fielddef* to |
|
||||||
* const if the upb_msgdef* is const. */ |
|
||||||
void upb_symtab_begin(upb_symtab_iter *iter, const upb_symtab *s, |
|
||||||
upb_deftype_t type); |
|
||||||
void upb_symtab_next(upb_symtab_iter *iter); |
|
||||||
bool upb_symtab_done(const upb_symtab_iter *iter); |
|
||||||
const upb_def *upb_symtab_iter_def(const upb_symtab_iter *iter); |
|
||||||
|
|
||||||
UPB_END_EXTERN_C |
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
/* C++ inline wrappers. */ |
|
||||||
namespace upb { |
|
||||||
inline reffed_ptr<SymbolTable> SymbolTable::New() { |
|
||||||
upb_symtab *s = upb_symtab_new(&s); |
|
||||||
return reffed_ptr<SymbolTable>(s, &s); |
|
||||||
} |
|
||||||
|
|
||||||
inline void SymbolTable::Freeze() { |
|
||||||
return upb_symtab_freeze(this); |
|
||||||
} |
|
||||||
inline const Def *SymbolTable::Resolve(const char *base, |
|
||||||
const char *sym) const { |
|
||||||
return upb_symtab_resolve(this, base, sym); |
|
||||||
} |
|
||||||
inline const Def* SymbolTable::Lookup(const char *sym) const { |
|
||||||
return upb_symtab_lookup(this, sym); |
|
||||||
} |
|
||||||
inline const MessageDef *SymbolTable::LookupMessage(const char *sym) const { |
|
||||||
return upb_symtab_lookupmsg(this, sym); |
|
||||||
} |
|
||||||
inline bool SymbolTable::Add( |
|
||||||
Def*const* defs, size_t n, void* ref_donor, Status* status) { |
|
||||||
return upb_symtab_add(this, (upb_def*const*)defs, n, ref_donor, status); |
|
||||||
} |
|
||||||
inline bool SymbolTable::AddFile(FileDef* file, Status* s) { |
|
||||||
return upb_symtab_addfile(this, file, s); |
|
||||||
} |
|
||||||
} /* namespace upb */ |
|
||||||
#endif |
|
||||||
|
|
||||||
#endif /* UPB_SYMTAB_H_ */ |
|
Loading…
Reference in new issue