/** * lupb_msgclass_typecheck() * * Verifies that the expected msgclass matches the actual. If not, raises a Lua * error. */ static void lupb_msgclass_typecheck(lua_State *L, const lupb_msgclass *expected, const lupb_msgclass *actual) { if (expected != actual) { luaL_error(L, "Message had incorrect type, expected '%s', got '%s'", upb_msgdef_fullname(upb_msglayout_msgdef(expected->layout)), upb_msgdef_fullname(upb_msglayout_msgdef(actual->layout))); } }
static void test_freeze_free() { bool ok; /* Test that freeze frees defs that were only being kept alive by virtue of * sharing a group with other defs that are being frozen. */ upb_msgdef *m1 = upb_msgdef_newnamed("M1", &m1); upb_msgdef *m2 = upb_msgdef_newnamed("M2", &m2); upb_msgdef *m3 = upb_msgdef_newnamed("M3", &m3); upb_msgdef *m4 = upb_msgdef_newnamed("M4", &m4); upb_fielddef *f = upb_fielddef_new(&f); /* Freeze M4 and make M1 point to it. */ upb_def_freeze((upb_def*const*)&m4, 1, NULL); upb_fielddef_settype(f, UPB_TYPE_MESSAGE); ASSERT(upb_fielddef_setnumber(f, 1, NULL)); ASSERT(upb_fielddef_setname(f, "foo", NULL)); ASSERT(upb_fielddef_setmsgsubdef(f, m4, NULL)); ASSERT(upb_msgdef_addfield(m1, f, &f, NULL)); /* After this unref, M1 is the only thing keeping M4 alive. */ upb_msgdef_unref(m4, &m4); /* Force M1/M2/M3 into a single mutable refcounting group. */ f = upb_fielddef_new(&f); upb_fielddef_settype(f, UPB_TYPE_MESSAGE); ASSERT(upb_fielddef_setnumber(f, 1, NULL)); ASSERT(upb_fielddef_setname(f, "foo", NULL)); ASSERT(upb_fielddef_setmsgsubdef(f, m1, NULL)); ASSERT(upb_fielddef_setmsgsubdef(f, m2, NULL)); ASSERT(upb_fielddef_setmsgsubdef(f, m3, NULL)); /* Make M3 cyclic with itself. */ ASSERT(upb_msgdef_addfield(m3, f, &f, NULL)); /* These will be kept alive since they are in the same refcounting group as * M3, which still has a ref. Note: this behavior is not guaranteed by the * API, but true in practice with its current implementation. */ upb_msgdef_unref(m1, &m1); upb_msgdef_unref(m2, &m2); /* Test that they are still alive (NOT allowed by the API). */ ok = strcmp("M1", upb_msgdef_fullname(m1)) == 0; ASSERT(ok); ok = strcmp("M2", upb_msgdef_fullname(m2)) == 0; ASSERT(ok); /* Freeze M3. If the test leaked no memory, then freeing m1 and m2 was * successful. */ ASSERT(upb_def_freeze((upb_def*const*)&m3, 1, NULL)); upb_msgdef_unref(m3, &m3); }
static void onmreg(const void *c, upb_handlers *h) { const upb_msgdef *m = upb_handlers_msgdef(h); upb_msg_field_iter i; UPB_UNUSED(c); upb_handlers_setstartmsg(h, textprinter_startmsg, NULL); upb_handlers_setendmsg(h, textprinter_endmsg, NULL); 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); upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; upb_handlerattr_sethandlerdata(&attr, f); switch (upb_fielddef_type(f)) { case UPB_TYPE_INT32: upb_handlers_setint32(h, f, textprinter_putint32, &attr); break; case UPB_TYPE_INT64: upb_handlers_setint64(h, f, textprinter_putint64, &attr); break; case UPB_TYPE_UINT32: upb_handlers_setuint32(h, f, textprinter_putuint32, &attr); break; case UPB_TYPE_UINT64: upb_handlers_setuint64(h, f, textprinter_putuint64, &attr); break; case UPB_TYPE_FLOAT: upb_handlers_setfloat(h, f, textprinter_putfloat, &attr); break; case UPB_TYPE_DOUBLE: upb_handlers_setdouble(h, f, textprinter_putdouble, &attr); break; case UPB_TYPE_BOOL: upb_handlers_setbool(h, f, textprinter_putbool, &attr); break; case UPB_TYPE_STRING: case UPB_TYPE_BYTES: upb_handlers_setstartstr(h, f, textprinter_startstr, &attr); upb_handlers_setstring(h, f, textprinter_putstr, &attr); upb_handlers_setendstr(h, f, textprinter_endstr, &attr); break; case UPB_TYPE_MESSAGE: { const char *name = upb_fielddef_istagdelim(f) ? shortname(upb_msgdef_fullname(upb_fielddef_msgsubdef(f))) : upb_fielddef_name(f); upb_handlerattr_sethandlerdata(&attr, name); upb_handlers_setstartsubmsg(h, f, textprinter_startsubmsg, &attr); upb_handlers_setendsubmsg(h, f, textprinter_endsubmsg, &attr); break; } case UPB_TYPE_ENUM: upb_handlers_setint32(h, f, textprinter_putenum, &attr); break; } } }
VALUE build_class_from_descriptor(Descriptor* desc) { const char *name; VALUE klass; if (desc->layout == NULL) { desc->layout = create_layout(desc->msgdef); } if (desc->fill_method == NULL) { desc->fill_method = new_fillmsg_decodermethod(desc, &desc->fill_method); } name = upb_msgdef_fullname(desc->msgdef); if (name == NULL) { rb_raise(rb_eRuntimeError, "Descriptor does not have assigned name."); } klass = rb_define_class_id( // Docs say this parameter is ignored. User will assign return value to // their own toplevel constant class name. rb_intern("Message"), rb_cObject); rb_ivar_set(klass, descriptor_instancevar_interned, get_def_obj(desc->msgdef)); rb_define_alloc_func(klass, Message_alloc); rb_require("google/protobuf/message_exts"); rb_include_module(klass, rb_eval_string("::Google::Protobuf::MessageExts")); rb_extend_object( klass, rb_eval_string("::Google::Protobuf::MessageExts::ClassMethods")); rb_define_method(klass, "method_missing", Message_method_missing, -1); rb_define_method(klass, "respond_to_missing?", Message_respond_to_missing, -1); rb_define_method(klass, "initialize", Message_initialize, -1); rb_define_method(klass, "dup", Message_dup, 0); // Also define #clone so that we don't inherit Object#clone. rb_define_method(klass, "clone", Message_dup, 0); rb_define_method(klass, "==", Message_eq, 1); rb_define_method(klass, "hash", Message_hash, 0); rb_define_method(klass, "to_h", Message_to_h, 0); rb_define_method(klass, "to_hash", Message_to_h, 0); rb_define_method(klass, "inspect", Message_inspect, 0); rb_define_method(klass, "[]", Message_index, 1); rb_define_method(klass, "[]=", Message_index_set, 2); rb_define_singleton_method(klass, "decode", Message_decode, 1); rb_define_singleton_method(klass, "encode", Message_encode, 1); rb_define_singleton_method(klass, "decode_json", Message_decode_json, -1); rb_define_singleton_method(klass, "encode_json", Message_encode_json, -1); rb_define_singleton_method(klass, "descriptor", Message_descriptor, 0); return klass; }
static void test_fielddef_unref() { bool ok; upb_symtab *s = load_test_proto(&s); const upb_msgdef *md = upb_symtab_lookupmsg(s, "A"); const upb_fielddef *f = upb_msgdef_itof(md, 1); upb_fielddef_ref(f, &f); /* Unref symtab; now fielddef is the only thing keeping the msgdef alive. */ upb_symtab_unref(s, &s); /* Check that md is still alive. */ ok = strcmp(upb_msgdef_fullname(md), "A") == 0; ASSERT(ok); /* Check that unref of fielddef frees the whole remaining graph. */ upb_fielddef_unref(f, &f); }
static int32_t trygetsel(upb_handlers *h, const upb_fielddef *f, upb_handlertype_t type) { upb_selector_t sel; assert(!upb_handlers_isfrozen(h)); if (upb_handlers_msgdef(h) != upb_fielddef_containingtype(f)) { upb_status_seterrf( &h->status_, "type mismatch: field %s does not belong to message %s", upb_fielddef_name(f), upb_msgdef_fullname(upb_handlers_msgdef(h))); return -1; } if (!upb_handlers_getselector(f, type, &sel)) { upb_status_seterrf( &h->status_, "type mismatch: cannot register handler type %d for field %s", type, upb_fielddef_name(f)); return -1; } return sel; }
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; } }
bool upb_handlers_freeze(upb_handlers *const*handlers, int n, upb_status *s) { // TODO: verify we have a transitive closure. for (int i = 0; i < n; i++) { upb_handlers *h = handlers[i]; if (!upb_ok(&h->status_)) { upb_status_seterrf(s, "handlers for message %s had error status: %s", upb_msgdef_fullname(upb_handlers_msgdef(h)), upb_status_errmsg(&h->status_)); return false; } // Check that there are no closure mismatches due to missing Start* handlers // or subhandlers with different type-level types. upb_msg_iter j; for(upb_msg_begin(&j, h->msg); !upb_msg_done(&j); upb_msg_next(&j)) { const upb_fielddef *f = upb_msg_iter_field(&j); if (upb_fielddef_isseq(f)) { if (!checkstart(h, f, UPB_HANDLER_STARTSEQ, s)) return false; } if (upb_fielddef_isstring(f)) { if (!checkstart(h, f, UPB_HANDLER_STARTSTR, s)) return false; } if (upb_fielddef_issubmsg(f)) { bool hashandler = false; if (upb_handlers_gethandler(h, getsel(h, f, UPB_HANDLER_STARTSUBMSG)) || upb_handlers_gethandler(h, getsel(h, f, UPB_HANDLER_ENDSUBMSG))) { hashandler = true; } if (upb_fielddef_isseq(f) && (upb_handlers_gethandler(h, getsel(h, f, UPB_HANDLER_STARTSEQ)) || upb_handlers_gethandler(h, getsel(h, f, UPB_HANDLER_ENDSEQ)))) { hashandler = true; } if (hashandler && !upb_handlers_getsubhandlers(h, f)) { // For now we add an empty subhandlers in this case. It makes the // decoder code generator simpler, because it only has to handle two // cases (submessage has handlers or not) as opposed to three // (submessage has handlers in enclosing message but no subhandlers). // // This makes parsing less efficient in the case that we want to // notice a submessage but skip its contents (like if we're testing // for submessage presence or counting the number of repeated // submessages). In this case we will end up parsing the submessage // field by field and throwing away the results for each, instead of // skipping the whole delimited thing at once. If this is an issue we // can revisit it, but do remember that this only arises when you have // handlers (startseq/startsubmsg/endsubmsg/endseq) set for the // submessage but no subhandlers. The uses cases for this are // limited. upb_handlers *sub = upb_handlers_new(upb_fielddef_msgsubdef(f), &sub); upb_handlers_setsubhandlers(h, f, sub); upb_handlers_unref(sub, &sub); } // TODO(haberman): check type of submessage. // This is slightly tricky; also consider whether we should check that // they match at setsubhandlers time. } } } if (!upb_refcounted_freeze((upb_refcounted*const*)handlers, n, s, UPB_MAX_HANDLER_DEPTH)) { return false; } return true; }
/* * call-seq: * Descriptor.name => name * * Returns the name of this message type as a fully-qualfied string (e.g., * My.Package.MessageType). */ VALUE Descriptor_name(VALUE _self) { DEFINE_SELF(Descriptor, self, _self); return rb_str_maybe_null(upb_msgdef_fullname(self->msgdef)); }
/* 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; }