/* * call-seq: * FieldDescriptor.submsg_name => submsg_name * * Returns the name of the message or enum type corresponding to this field, if * it is a message or enum field (respectively), or nil otherwise. This type * name will be resolved within the context of the pool to which the containing * message type is added. */ VALUE FieldDescriptor_submsg_name(VALUE _self) { DEFINE_SELF(FieldDescriptor, self, _self); if (!upb_fielddef_hassubdef(self->fielddef)) { return Qnil; } return rb_str_maybe_null(upb_fielddef_subdefname(self->fielddef)); }
static bool field_endmsg(void *closure, const void *hd, upb_status *status) { UPB_UNUSED(hd); upb_descreader *r = closure; upb_fielddef *f = r->f; // TODO: verify that all required fields were present. assert(upb_fielddef_number(f) != 0); assert(upb_fielddef_name(f) != NULL); assert((upb_fielddef_subdefname(f) != NULL) == upb_fielddef_hassubdef(f)); if (r->default_string) { if (upb_fielddef_issubmsg(f)) { upb_status_seterrmsg(status, "Submessages cannot have defaults."); return false; } if (upb_fielddef_isstring(f) || upb_fielddef_type(f) == UPB_TYPE_ENUM) { upb_fielddef_setdefaultcstr(f, r->default_string, NULL); } else { if (r->default_string && !parse_default(r->default_string, f)) { // We don't worry too much about giving a great error message since the // compiler should have ensured this was correct. upb_status_seterrmsg(status, "Error converting default value."); return false; } } } return true; }
static bool upb_validate_field(upb_fielddef *f, upb_status *s) { if (upb_fielddef_name(f) == NULL || upb_fielddef_number(f) == 0) { upb_status_seterrliteral(s, "fielddef must have name and number set"); return false; } if (upb_fielddef_hassubdef(f)) { if (f->subdef_is_symbolic) { upb_status_seterrf(s, "field '%s' has not been resolved", upb_fielddef_name(f)); return false; } const upb_def *subdef = upb_fielddef_subdef(f); if (subdef == NULL) { upb_status_seterrf(s, "field %s.%s is missing required subdef", msgdef_name(f->msgdef), upb_fielddef_name(f)); return false; } else if (!upb_def_isfrozen(subdef) && !subdef->came_from_user) { upb_status_seterrf(s, "subdef of field %s.%s is not frozen or being frozen", msgdef_name(f->msgdef), upb_fielddef_name(f)); return false; } else if (upb_fielddef_default_is_symbolic(f)) { upb_status_seterrf(s, "enum field %s.%s has not been resolved", msgdef_name(f->msgdef), upb_fielddef_name(f)); return false; } } return true; }
static void field_endmsg(void *_r, upb_status *status) { upb_descreader *r = _r; upb_fielddef *f = r->f; // TODO: verify that all required fields were present. assert(upb_fielddef_number(f) != 0 && upb_fielddef_name(f) != NULL); assert((upb_fielddef_subdefname(f) != NULL) == upb_fielddef_hassubdef(f)); if (r->default_string) { if (upb_fielddef_issubmsg(f)) { upb_status_seterrliteral(status, "Submessages cannot have defaults."); return; } if (upb_fielddef_isstring(f) || upb_fielddef_type(f) == UPB_TYPE(ENUM)) { upb_fielddef_setdefaultcstr(f, r->default_string); } else { upb_value val; upb_value_setptr(&val, NULL); // Silence inaccurate compiler warnings. if (!parse_default(r->default_string, &val, upb_fielddef_type(f))) { // We don't worry too much about giving a great error message since the // compiler should have ensured this was correct. upb_status_seterrliteral(status, "Error converting default value."); return; } upb_fielddef_setdefault(f, val); } } }
const upb_def *upb_fielddef_subdef(const upb_fielddef *f) { if (upb_fielddef_hassubdef(f) && upb_fielddef_isfrozen(f)) { assert(f->sub.def); return f->sub.def; } else { return f->subdef_is_symbolic ? NULL : f->sub.def; } }
bool upb_fielddef_setsubdefname(upb_fielddef *f, const char *name) { assert(!upb_fielddef_isfrozen(f)); assert(upb_fielddef_hassubdef(f)); release_subdef(f); f->sub.name = upb_strdup(name); f->subdef_is_symbolic = true; return true; }
bool upb_fielddef_setsubdef(upb_fielddef *f, const upb_def *subdef) { assert(!upb_fielddef_isfrozen(f)); assert(upb_fielddef_hassubdef(f)); if (subdef && !upb_subdef_typecheck(f, subdef)) return false; release_subdef(f); f->sub.def = subdef; f->subdef_is_symbolic = false; if (f->sub.def) upb_ref2(f->sub.def, f); return true; }
/* * call-seq: * FieldDescriptor.subtype => message_or_enum_descriptor * * Returns the message or enum descriptor corresponding to this field's type if * it is a message or enum field, respectively, or nil otherwise. Cannot be * called *until* the containing message type is added to a pool (and thus * resolved). */ VALUE FieldDescriptor_subtype(VALUE _self) { DEFINE_SELF(FieldDescriptor, self, _self); if (!upb_fielddef_hassubdef(self->fielddef)) { return Qnil; } const upb_def* def = upb_fielddef_subdef(self->fielddef); if (def == NULL) { return Qnil; } return get_def_obj(def); }
/* * call-seq: * FieldDescriptor.submsg_name = submsg_name * * Sets the name of the message or enum type corresponding to this field, if it * is a message or enum field (respectively). This type name will be resolved * within the context of the pool to which the containing message type is added. * Cannot be called on field that are not of message or enum type, or on fields * that are part of a message type already added to a pool. */ VALUE FieldDescriptor_submsg_name_set(VALUE _self, VALUE value) { DEFINE_SELF(FieldDescriptor, self, _self); upb_fielddef* mut_def = check_field_notfrozen(self->fielddef); if (!upb_fielddef_hassubdef(self->fielddef)) { rb_raise(rb_eTypeError, "FieldDescriptor does not have subdef."); } const char* str = get_str(value); CHECK_UPB(upb_fielddef_setsubdefname(mut_def, str, &status), "Error setting submessage name"); return Qnil; }
bool upb_fielddef_setsubdefname(upb_fielddef *f, const char *name, upb_status *s) { assert(!upb_fielddef_isfrozen(f)); if (!upb_fielddef_hassubdef(f)) { upb_status_seterrliteral(s, "field type does not accept a subdef"); return false; } release_subdef(f); f->sub.name = upb_strdup(name); f->subdef_is_symbolic = true; return true; }
bool upb_fielddef_setsubdefname(upb_fielddef *f, const char *name, upb_status *s) { assert(!upb_fielddef_isfrozen(f)); if (!upb_fielddef_hassubdef(f)) { upb_status_seterrmsg(s, "field type does not accept a subdef"); return false; } // TODO: validate name (upb_isident() doesn't quite work atm because this name // may have a leading "."). release_subdef(f); f->sub.name = upb_strdup(name); f->subdef_is_symbolic = true; return true; }
static bool upb_validate_field(upb_fielddef *f, upb_status *s) { if (upb_fielddef_name(f) == NULL || upb_fielddef_number(f) == 0) { upb_status_seterrmsg(s, "fielddef must have name and number set"); return false; } if (!f->type_is_set_) { upb_status_seterrmsg(s, "fielddef type was not initialized"); return false; } if (upb_fielddef_lazy(f) && upb_fielddef_descriptortype(f) != UPB_DESCRIPTOR_TYPE_MESSAGE) { upb_status_seterrmsg(s, "only length-delimited submessage fields may be lazy"); return false; } if (upb_fielddef_hassubdef(f)) { if (f->subdef_is_symbolic) { upb_status_seterrf(s, "field '%s' has not been resolved", upb_fielddef_name(f)); return false; } const upb_def *subdef = upb_fielddef_subdef(f); if (subdef == NULL) { upb_status_seterrf(s, "field %s.%s is missing required subdef", msgdef_name(f->msg.def), upb_fielddef_name(f)); return false; } else if (!upb_def_isfrozen(subdef) && !subdef->came_from_user) { upb_status_seterrf(s, "subdef of field %s.%s is not frozen or being frozen", msgdef_name(f->msg.def), upb_fielddef_name(f)); return false; } else if (upb_fielddef_default_is_symbolic(f)) { upb_status_seterrf(s, "enum field %s.%s has not been resolved", msgdef_name(f->msg.def), upb_fielddef_name(f)); return false; } } return true; }
static void test_cycles() { bool ok; upb_symtab *s = load_test_proto(&s); const upb_msgdef *m; const upb_fielddef *f; const upb_def *def; const upb_def *def2; /* Test cycle detection by making a cyclic def's main refcount go to zero * and then be incremented to one again. */ def = upb_symtab_lookup(s, "A"); upb_def_ref(def, &def); ASSERT(def); ASSERT(upb_def_isfrozen(def)); upb_symtab_unref(s, &s); /* Message A has only one subfield: "optional B b = 1". */ m = upb_downcast_msgdef(def); f = upb_msgdef_itof(m, 1); ASSERT(f); ASSERT(upb_fielddef_hassubdef(f)); ASSERT(upb_msgdef_ntofz(m, "b") == f); ASSERT(upb_msgdef_ntof(m, "b", 1) == f); def2 = upb_fielddef_subdef(f); ASSERT(upb_downcast_msgdef(def2)); ok = strcmp(upb_def_fullname(def2), "B") == 0; ASSERT(ok); upb_def_ref(def2, &def2); upb_def_unref(def, &def); /* We know "def" is still alive because it's reachable from def2. */ ok = strcmp(upb_def_fullname(def), "A") == 0; ASSERT(ok); upb_def_unref(def2, &def2); }
/* 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; }