// narg is a lua table containing a list of defs to add. static PyObject *PyUpb_SymbolTable_add_defs(PyObject *o, PyObject *defs) { upb_symtab *s = Check_SymbolTable(o, NULL); if (!PySequence_Check(defs)) return PyUpb_Error("Must be a sequence"); Py_ssize_t n = PySequence_Length(defs); // Prevent stack overflow. if (n > 2048) return PyUpb_Error("Too many defs"); upb_def *cdefs[n]; int i = 0; for (i = 0; i < n; i++) { PyObject *pydef = PySequence_GetItem(defs, i); upb_def *def = Check_MessageDef(pydef, NULL); cdefs[i++] = def; upb_msgdef *md = upb_dyncast_msgdef(def); if (!md) continue; upb_msg_field_iter j; for(upb_msg_field_begin(&j, md); !upb_msg_field_done(&j); upb_msg_field_next(&j)) { upb_fielddef *f = upb_msg_iter_field(j); upb_fielddef_setaccessor(f, PyUpb_AccessorForField(f)); } upb_msgdef_layout(md); } upb_status status = UPB_STATUS_INIT; upb_symtab_add(s, cdefs, n, &status); PyUpb_CheckStatus(&status); return Py_None; }
static void upb_def_movetosymtab(upb_def *d, upb_symtab *s) { assert(upb_atomic_read(&d->refcount) > 0); d->symtab = s; upb_symtab_ref(s); upb_msgdef *m = upb_dyncast_msgdef(d); if (m) upb_inttable_compact(&m->itof); }
bool upb_symtab_dfs(upb_def *def, upb_def **open_defs, int n, upb_strtable *addtab) { // This linear search makes the DFS O(n^2) in the length of the paths. // Could make this O(n) with a hash table, but n is small. for (int i = 0; i < n; i++) { if (def == open_defs[i]) return false; } bool needcopy = false; upb_msgdef *m = upb_dyncast_msgdef(def); if (m) { upb_msg_iter i; open_defs[n++] = def; for(i = upb_msg_begin(m); !upb_msg_done(i); i = upb_msg_next(m, i)) { upb_fielddef *f = upb_msg_iter_field(i); if (!upb_hassubdef(f)) continue; needcopy |= upb_symtab_dfs(f->def, open_defs, n, addtab); } } bool replacing = (upb_strtable_lookup(addtab, m->base.fqname) != NULL); if (needcopy && !replacing) { upb_symtab_ent e = {upb_def_dup(def)}; upb_strtable_insert(addtab, def->fqname, &e); replacing = true; } return replacing; }
static bool upb_subdef_typecheck(upb_fielddef *f, const upb_def *subdef) { if (f->type_ == UPB_TYPE(MESSAGE) || f->type_ == UPB_TYPE(GROUP)) return upb_dyncast_msgdef(subdef) != NULL; else if (f->type_ == UPB_TYPE(ENUM)) return upb_dyncast_enumdef(subdef) != NULL; else { assert(false); return false; } }
static bool initialize() { // Initialize upb state, decode descriptor. upb_status status = UPB_STATUS_INIT; upb_symtab *s = upb_symtab_new(); upb_string *fds_str = upb_strreadfile(MESSAGE_DESCRIPTOR_FILE); if(fds_str == NULL) { fprintf(stderr, "Couldn't read " MESSAGE_DESCRIPTOR_FILE ":"), upb_printerr(&status); return false; } upb_parsedesc(s, fds_str, &status); upb_string_unref(fds_str); if(!upb_ok(&status)) { fprintf(stderr, "Error importing " MESSAGE_DESCRIPTOR_FILE ":"); upb_printerr(&status); return false; } def = upb_dyncast_msgdef(upb_symtab_lookup(s, UPB_STRLIT(MESSAGE_NAME))); if(!def) { fprintf(stderr, "Error finding symbol '" UPB_STRFMT "'.\n", UPB_STRARG(UPB_STRLIT(MESSAGE_NAME))); return false; } upb_symtab_unref(s); // Read the message data itself. input_str = upb_strreadfile(MESSAGE_FILE); if(input_str == NULL) { fprintf(stderr, "Error reading " MESSAGE_FILE "\n"); return false; } upb_status_uninit(&status); msg = upb_msg_new(def); upb_stringsrc_init(&strsrc); upb_handlers_init(&h, def); upb_msg_regdhandlers(&h); upb_decoder_init(&d, &h); if (!BYREF) { // Pretend the input string is stack-allocated, which will force its data // to be copied instead of referenced. There is no good reason to do this, // except to benchmark against proto2 more fairly, which in its open-source // release does not support referencing the input string. input_str->refcount.v = _UPB_STRING_REFCOUNT_STACK; } return true; }
static bool upb_subdef_typecheck(upb_fielddef *f, const upb_def *subdef, upb_status *s) { if (f->type_ == UPB_TYPE_MESSAGE) { if (upb_dyncast_msgdef(subdef)) return true; upb_status_seterrmsg(s, "invalid subdef type for this submessage field"); return false; } else if (f->type_ == UPB_TYPE_ENUM) { if (upb_dyncast_enumdef(subdef)) return true; upb_status_seterrmsg(s, "invalid subdef type for this enum field"); return false; } else { upb_status_seterrmsg(s, "only message and enum fields can have a subdef"); return false; } }
bool upb_symtab_add(upb_symtab *s, upb_def **defs, int n, upb_status *status) { upb_rwlock_wrlock(&s->lock); // Add all defs to a table for resolution. upb_strtable addtab; upb_strtable_init(&addtab, n, sizeof(upb_symtab_ent)); for (int i = 0; i < n; i++) { upb_def *def = defs[i]; if (upb_strtable_lookup(&addtab, def->fqname)) { upb_status_seterrf(status, "Conflicting defs named '%s'", def->fqname); upb_strtable_free(&addtab); return false; } upb_strtable_insert(&addtab, def->fqname, &def); } // All existing defs that can reach defs that are being replaced must // themselves be replaced with versions that will point to the new defs. // Do a DFS -- any path that finds a new def must replace all ancestors. upb_strtable *symtab = &s->symtab; upb_strtable_iter i; upb_strtable_begin(&i, symtab); for(; !upb_strtable_done(&i); upb_strtable_next(&i)) { upb_def *open_defs[UPB_MAX_TYPE_DEPTH]; const upb_symtab_ent *e = upb_strtable_iter_value(&i); upb_symtab_dfs(e->def, open_defs, 0, &addtab); } // Resolve all refs. upb_strtable_begin(&i, &addtab); for(; !upb_strtable_done(&i); upb_strtable_next(&i)) { const upb_symtab_ent *e = upb_strtable_iter_value(&i); upb_msgdef *m = upb_dyncast_msgdef(e->def); if(!m) continue; // Type names are resolved relative to the message in which they appear. const char *base = m->base.fqname; upb_msg_iter j; for(j = upb_msg_begin(m); !upb_msg_done(j); j = upb_msg_next(m, j)) { upb_fielddef *f = upb_msg_iter_field(j); if (f->type == 0) { upb_status_seterrf(status, "Field type was not set."); return false; } if (!upb_hassubdef(f)) continue; // No resolving necessary. upb_downcast_unresolveddef(f->def); // Type check. const char *name = f->def->fqname; // Resolve from either the addtab (pending adds) or symtab (existing // defs). If both exist, prefer the pending add, because it will be // overwriting the existing def. upb_symtab_ent *found; if(!(found = upb_resolve(&addtab, base, name)) && !(found = upb_resolve(symtab, base, name))) { upb_status_seterrf(status, "could not resolve symbol '%s' " "in context '%s'", name, base); return false; } // Check the type of the found def. upb_fieldtype_t expected = upb_issubmsg(f) ? UPB_DEF_MSG : UPB_DEF_ENUM; if(found->def->type != expected) { upb_status_seterrliteral(status, "Unexpected type"); return false; } if (!upb_fielddef_resolve(f, found->def, status)) return false; } } // The defs in the transaction have been vetted, and can be moved to the // symtab without causing errors. upb_strtable_begin(&i, &addtab); for(; !upb_strtable_done(&i); upb_strtable_next(&i)) { const upb_symtab_ent *tmptab_e = upb_strtable_iter_value(&i); upb_def_movetosymtab(tmptab_e->def, s); upb_symtab_ent *symtab_e = upb_strtable_lookup(&s->symtab, tmptab_e->def->fqname); if(symtab_e) { upb_deflist_push(&s->olddefs, symtab_e->def); symtab_e->def = tmptab_e->def; } else { upb_strtable_insert(&s->symtab, tmptab_e->def->fqname, tmptab_e); } } upb_strtable_free(&addtab); upb_rwlock_unlock(&s->lock); upb_symtab_gc(s); return true; }
const upb_msgdef *upb_fielddef_msgsubdef(const upb_fielddef *f) { const upb_def *def = upb_fielddef_subdef(f); return def ? upb_dyncast_msgdef(def) : NULL; }
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; }
/* 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; 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; }