Merge pull request #414 from haberman/python-review

Beginning of Python+upb: DescriptorPool and a few Descriptor types
pull/13171/head
Joshua Haberman 4 years ago committed by GitHub
commit 62c61ecc2c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 11
      .bazelrc
  2. 9
      .github/workflows/bazel_tests.yml
  3. 6
      WORKSPACE
  4. 93
      bazel/workspace_defs.bzl
  5. 1
      cmake/build_defs.bzl
  6. 3
      cmake/make_cmakelists.py
  7. 80
      python/BUILD
  8. 247
      python/descriptor.c
  9. 45
      python/descriptor.h
  10. 229
      python/descriptor_pool.c
  11. 37
      python/descriptor_pool.h
  12. 55
      python/minimal_test.py
  13. 128
      python/protobuf.c
  14. 98
      python/protobuf.h
  15. 6
      python/version_script.lds
  16. 6
      upb/def.c
  17. 1
      upb/def.h

@ -1,11 +1,20 @@
# temporary fix for https://github.com/bazelbuild/bazel/issues/12905 on macOS
build --features=-debug_prefix_map_pwd_is_dot
build --extra_toolchains=@system_python//:python_toolchain
# Use our custom-configured c++ toolchain.
build:m32 --copt=-m32 --linkopt=-m32
build:asan --copt=-fsanitize=address --linkopt=-fsanitize=address
build:valgrind --run_under='valgrind --leak-check=full --error-exitcode=1'
# For Valgrind, we have to disable checks of "possible" leaks because the Python
# interpreter does the sorts of things that flag Valgrind "possible" leak checks.
# Ideally we could enforce a stricter check for the non-Python tests, but I don't
# know of an easy way to do that.
#
# We also have to disable pymalloc to avoid triggering Valgrind.
build:valgrind --run_under='valgrind --leak-check=full --trace-children=yes --show-possibly-lost=no --errors-for-leak-kinds=definite --error-exitcode=1' --action_env=PYTHONMALLOC=malloc
build:ubsan --copt=-fsanitize=undefined --linkopt=-fsanitize=undefined --action_env=UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1
# Workaround for the fact that Bazel links with $CC, not $CXX

@ -21,10 +21,15 @@ jobs:
- { CC: clang, os: ubuntu-20.04, flags: "-c opt" } # Some warnings only fire with -c opt
- { CC: gcc, os: ubuntu-20.04, flags: "-c opt" }
- { CC: clang, os: ubuntu-20.04, flags: "--//:fasttable_enabled=true -- -cmake:test_generated_files" }
- { CC: clang, os: ubuntu-20.04, flags: "--config=asan -- -benchmarks:benchmark" }
- { CC: clang, os: ubuntu-20.04, flags: "--config=asan -- -benchmarks:benchmark -python:all" }
- { CC: clang, os: macos-11, flags: "" }
steps:
- uses: actions/checkout@v2
- name: Setup Python venv
run: rm -rf /tmp/venv && python3 -m venv /tmp/venv
- name: Install dependencies
run: sudo apt install -y ${{ matrix.install }}
if: matrix.install != ''
- name: Run tests
run: cd ${{ github.workspace }} && CC=${{ matrix.CC }} bazel test ... ${{ matrix.flags }}
run: cd ${{ github.workspace }} && PATH=/tmp/venv/bin:$PATH CC=${{ matrix.CC }} bazel test --test_output=errors ... ${{ matrix.flags }}

@ -2,7 +2,7 @@ workspace(name = "upb")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
load("//bazel:workspace_deps.bzl", "upb_deps")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
load("//bazel:workspace_defs.bzl", "system_python")
upb_deps()
@ -39,6 +39,10 @@ http_archive(
patch_cmds = ["find google -type f -name BUILD.bazel -delete"],
)
system_python(
name = "system_python"
)
http_archive(
name = "rules_fuzzing",
sha256 = "127d7c45e9b7520b3c42145b3cb1b8c26477cdaed0521b02a0298907339fefa1",

@ -0,0 +1,93 @@
# Copyright (c) 2009-2021, Google LLC
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google LLC nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Repository rule for using Python 3.x headers from the system."""
_build_file = """
load("@bazel_tools//tools/python:toolchain.bzl", "py_runtime_pair")
cc_library(
name = "python_headers",
hdrs = glob(["python/**/*.h"]),
includes = ["python"],
visibility = ["//visibility:public"],
)
py_runtime(
name = "py3_runtime",
interpreter_path = "%s",
python_version = "PY3",
)
py_runtime_pair(
name = "runtime_pair",
py3_runtime = ":py3_runtime",
)
toolchain(
name = "python_toolchain",
toolchain = ":runtime_pair",
toolchain_type = "@rules_python//python:toolchain_type",
)
"""
_build_defs_file = """
EXT_SUFFIX = "%s"
"""
def _get_config_var(repository_ctx, name):
py_program = "import sysconfig; print(sysconfig.get_config_var('%s'), end='')"
result = repository_ctx.execute(["python3", "-c", py_program % (name)])
if result.return_code != 0:
fail("No python3 executable available on the system")
return result.stdout
def _python_headers_impl(repository_ctx):
path = _get_config_var(repository_ctx, "INCLUDEPY")
ext_suffix = _get_config_var(repository_ctx, "EXT_SUFFIX")
repository_ctx.symlink(path, "python")
python3 = repository_ctx.which("python3")
repository_ctx.file("BUILD.bazel", _build_file % python3)
repository_ctx.file("build_defs.bzl", _build_defs_file % ext_suffix)
# The system_python() repository rule exposes Python headers from the system.
#
# In WORKSPACE:
# system_python(
# name = "system_python_repo",
# )
#
# This repository exposes a single rule that you can depend on from BUILD:
# cc_library(
# name = "foobar",
# srcs = ["foobar.cc"],
# deps = ["@system_python_repo//:python_headers"],
# )
#
# The headers should correspond to the version of python obtained by running
# the `python3` command on the system.
system_python = repository_rule(
implementation = _python_headers_impl,
local = True,
)

@ -62,6 +62,7 @@ def generated_file_staleness_test(name, outs, generated_pattern):
name = name,
srcs = [script_name],
data = existing_outs + [generated_pattern % file for file in outs],
python_version = "PY3",
deps = [
":staleness_test_lib",
],

@ -220,6 +220,9 @@ class WorkspaceFileFunctions(object):
def rules_fuzzing_init(self):
pass
def system_python(self, **kwargs):
pass
class Converter(object):
def __init__(self):

@ -0,0 +1,80 @@
# Copyright (c) 2009-2021, Google LLC
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google LLC nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
load(
"//bazel:build_defs.bzl",
"UPB_DEFAULT_COPTS",
)
load(
"@system_python//:build_defs.bzl",
"EXT_SUFFIX",
)
cc_binary(
name = "message",
srcs = [
"descriptor.c",
"descriptor.h",
"descriptor_pool.c",
"descriptor_pool.h",
"protobuf.c",
"protobuf.h",
],
copts = UPB_DEFAULT_COPTS + [
# The Python API requires patterns that are ISO C incompatible, like
# casts between function pointers and object pointers.
"-Wno-pedantic",
],
# We use a linker script to hide all symbols except the entry point for
# the module.
linkopts = select({
"@platforms//os:linux": ["-Wl,--version-script,$(location :version_script.lds)"],
"@platforms//os:macos": ["-Wl,-exported_symbol", "-Wl,_PyInit__message"],
}),
linkshared = True,
linkstatic = True,
deps = [
":version_script.lds",
"//:reflection",
"//:upb",
"@system_python//:python_headers",
],
)
# Copy the extension into the location recognized by Python.
genrule(
name = "message_ext",
srcs = [":message"],
outs = ["google/protobuf/pyext/_message" + EXT_SUFFIX],
cmd = "cp $< $@",
)
py_test(
name = "minimal_test",
srcs = ["minimal_test.py"],
data = [":message_ext"],
imports = ["."],
legacy_create_init = False,
)

@ -0,0 +1,247 @@
/*
* Copyright (c) 2009-2021, Google LLC
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Google LLC nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "python/descriptor.h"
#include "python/protobuf.h"
#include "upb/def.h"
// -----------------------------------------------------------------------------
// DescriptorBase
// -----------------------------------------------------------------------------
// This representation is used by all concrete descriptors.
typedef struct {
PyObject_HEAD
PyObject *pool; // We own a ref.
const void *def; // Type depends on the class. Kept alive by "pool".
} PyUpb_DescriptorBase;
PyObject *PyUpb_AnyDescriptor_GetPool(PyObject *desc) {
PyUpb_DescriptorBase *base = (void *)desc;
return base->pool;
}
static PyObject *PyUpb_DescriptorBase_New(PyTypeObject *subtype, PyObject *args,
PyObject *kwds) {
return PyErr_Format(PyExc_RuntimeError,
"Creating descriptors directly is not allowed.");
}
static PyObject *PyUpb_DescriptorBase_NewInternal(PyTypeObject *type,
const void *def,
PyObject *pool) {
PyUpb_DescriptorBase *base = (PyUpb_DescriptorBase *)PyUpb_ObjCache_Get(def);
if (!base) {
base = PyObject_New(PyUpb_DescriptorBase, type);
base->pool = pool;
base->def = def;
Py_INCREF(pool);
PyUpb_ObjCache_Add(def, &base->ob_base);
}
return &base->ob_base;
}
static void PyUpb_DescriptorBase_Dealloc(PyUpb_DescriptorBase *self) {
PyUpb_DescriptorBase *base = (PyUpb_DescriptorBase *)self;
PyUpb_ObjCache_Delete(base->def);
Py_DECREF(base->pool);
PyObject_Del(self);
}
#define DESCRIPTOR_BASE_SLOTS \
{Py_tp_new, (void *)&PyUpb_DescriptorBase_New}, \
{Py_tp_dealloc, (void *)&PyUpb_DescriptorBase_Dealloc}
// -----------------------------------------------------------------------------
// FieldDescriptor
// -----------------------------------------------------------------------------
static PyObject *PyUpb_FieldDescriptor_GetType(PyUpb_DescriptorBase *self,
void *closure) {
return PyLong_FromLong(upb_fielddef_descriptortype(self->def));
}
static PyObject *PyUpb_FieldDescriptor_GetLabel(PyUpb_DescriptorBase *self,
void *closure) {
return PyLong_FromLong(upb_fielddef_label(self->def));
}
static PyObject *PyUpb_FieldDescriptor_GetNumber(PyUpb_DescriptorBase *self,
void *closure) {
return PyLong_FromLong(upb_fielddef_number(self->def));
}
static PyGetSetDef PyUpb_FieldDescriptor_Getters[] = {
/*
{ "full_name", (getter)GetFullName, NULL, "Full name"},
{ "name", (getter)GetName, NULL, "Unqualified name"},
{ "camelcase_name", (getter)GetCamelcaseName, NULL, "Camelcase name"},
{ "json_name", (getter)GetJsonName, NULL, "Json name"},
{ "file", (getter)GetFile, NULL, "File Descriptor"},
*/
{"type", (getter)PyUpb_FieldDescriptor_GetType, NULL, "Type"},
/*
{ "cpp_type", (getter)PyUpb_FieldDescriptor_GetCppType, NULL, "C++ Type"},
*/
{"label", (getter)PyUpb_FieldDescriptor_GetLabel, NULL, "Label"},
{"number", (getter)PyUpb_FieldDescriptor_GetNumber, NULL, "Number"},
/*
{ "index", (getter)GetIndex, NULL, "Index"},
{ "default_value", (getter)GetDefaultValue, NULL, "Default Value"},
{ "has_default_value", (getter)HasDefaultValue},
{ "is_extension", (getter)IsExtension, NULL, "ID"},
{ "id", (getter)GetID, NULL, "ID"},
{ "_cdescriptor", (getter)GetCDescriptor, NULL, "HAACK REMOVE ME"},
{ "message_type", (getter)GetMessageType, (setter)SetMessageType,
"Message type"},
{ "enum_type", (getter)GetEnumType, (setter)SetEnumType, "Enum type"},
{ "containing_type", (getter)GetContainingType, (setter)SetContainingType,
"Containing type"},
{ "extension_scope", (getter)GetExtensionScope, (setter)NULL,
"Extension scope"},
{ "containing_oneof", (getter)GetContainingOneof,
(setter)SetContainingOneof, "Containing oneof"}, { "has_options",
(getter)GetHasOptions, (setter)SetHasOptions, "Has Options"}, { "_options",
(getter)NULL, (setter)SetOptions, "Options"}, { "_serialized_options",
(getter)NULL, (setter)SetSerializedOptions, "Serialized Options"},
*/
{NULL}};
static PyMethodDef PyUpb_FieldDescriptor_Methods[] = {
/*
{ "GetOptions", (PyCFunction)GetOptions, METH_NOARGS, },
*/
{NULL}};
static PyType_Slot PyUpb_FieldDescriptor_Slots[] = {
DESCRIPTOR_BASE_SLOTS,
{Py_tp_methods, PyUpb_FieldDescriptor_Methods},
{Py_tp_getset, PyUpb_FieldDescriptor_Getters},
{0, NULL}};
static PyType_Spec PyUpb_FieldDescriptor_Spec = {
PYUPB_MODULE_NAME ".FieldDescriptor",
sizeof(PyUpb_DescriptorBase),
0, // tp_itemsize
Py_TPFLAGS_DEFAULT,
PyUpb_FieldDescriptor_Slots,
};
PyObject *PyUpb_FieldDescriptor_GetOrCreateWrapper(const upb_fielddef *field,
PyObject *pool) {
PyUpb_ModuleState *state = PyUpb_ModuleState_Get();
return PyUpb_DescriptorBase_NewInternal(state->field_descriptor_type, field,
pool);
}
// -----------------------------------------------------------------------------
// FileDescriptor
// -----------------------------------------------------------------------------
//
static PyObject *PyUpb_FileDescriptor_GetName(PyUpb_DescriptorBase *self,
void *closure) {
return PyUnicode_FromString(upb_filedef_name(self->def));
}
static PyGetSetDef PyUpb_FileDescriptor_Getters[] =
{
/*
{ "pool", (getter)GetPool, NULL, "pool"},
*/
{"name", (getter)PyUpb_FileDescriptor_GetName, NULL, "name"},
/*
{ "package", (getter)GetPackage, NULL, "package"},
{ "serialized_pb", (getter)GetSerializedPb},
{ "message_types_by_name", PyUpb_FileDescriptor_GetMessageTypesByName,
NULL, "Messages by name"}, { "enum_types_by_name",
PyUpb_FileDescriptor_GetEnumTypesByName, NULL, "Enums by name"}, {
"extensions_by_name", (getter)GetExtensionsByName, NULL, "Extensions by
name"}, { "services_by_name", (getter)GetServicesByName, NULL, "Services
by name"}, { "dependencies", (getter)GetDependencies, NULL,
"Dependencies"}, { "public_dependencies", (getter)GetPublicDependencies,
NULL, "Dependencies"},
{ "has_options", (getter)GetHasOptions, (setter)SetHasOptions, "Has
Options"}, { "_options", (getter)NULL, (setter)SetOptions, "Options"},
{ "_serialized_options", (getter)NULL, (setter)SetSerializedOptions,
"Serialized Options"},
{ "syntax", (getter)GetSyntax, (setter)NULL, "Syntax"},
*/
{NULL}};
static PyMethodDef PyUpb_FileDescriptor_Methods[] = {
/*
{ "GetOptions", (PyCFunction)GetOptions, METH_NOARGS, },
{ "CopyToProto", (PyCFunction)CopyToProto, METH_O, },
*/
{NULL}};
static PyType_Slot PyUpb_FileDescriptor_Slots[] = {
DESCRIPTOR_BASE_SLOTS,
{Py_tp_methods, PyUpb_FileDescriptor_Methods},
{Py_tp_getset, PyUpb_FileDescriptor_Getters},
{0, NULL}};
static PyType_Spec PyUpb_FileDescriptor_Spec = {
PYUPB_MODULE_NAME ".FileDescriptor", // tp_name
sizeof(PyUpb_DescriptorBase), // tp_basicsize
0, // tp_itemsize
Py_TPFLAGS_DEFAULT, // tp_flags
PyUpb_FileDescriptor_Slots,
};
PyObject *PyUpb_FileDescriptor_GetOrCreateWrapper(const upb_filedef *file,
PyObject *pool) {
PyUpb_ModuleState *state = PyUpb_ModuleState_Get();
return PyUpb_DescriptorBase_NewInternal(state->file_descriptor_type, file,
pool);
}
const upb_filedef *PyUpb_FileDescriptor_GetDef(PyObject *_self) {
PyUpb_DescriptorBase *self = (void *)_self;
return self->def;
}
// -----------------------------------------------------------------------------
// Top Level
// -----------------------------------------------------------------------------
bool PyUpb_InitDescriptor(PyObject *m) {
PyUpb_ModuleState *s = PyUpb_ModuleState_Get();
s->field_descriptor_type =
AddObject(m, "FieldDescriptor", &PyUpb_FieldDescriptor_Spec);
s->file_descriptor_type =
AddObject(m, "FileDescriptor", &PyUpb_FileDescriptor_Spec);
return s->field_descriptor_type && s->file_descriptor_type;
}

@ -0,0 +1,45 @@
/*
* Copyright (c) 2009-2021, Google LLC
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Google LLC nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef PYUPB_DESCRIPTOR_H__
#define PYUPB_DESCRIPTOR_H__
#include <stdbool.h>
#include "protobuf.h"
#include "upb/def.h"
PyObject *PyUpb_FieldDescriptor_GetOrCreateWrapper(const upb_fielddef *field,
PyObject *pool);
PyObject *PyUpb_FileDescriptor_GetOrCreateWrapper(const upb_filedef *file,
PyObject *pool);
const upb_filedef *PyUpb_FileDescriptor_GetDef(PyObject *file);
bool PyUpb_InitDescriptor(PyObject *m);
#endif // PYUPB_DESCRIPTOR_H__

@ -0,0 +1,229 @@
/*
* Copyright (c) 2009-2021, Google LLC
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Google LLC nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "python/descriptor_pool.h"
#include "python/descriptor.h"
#include "python/protobuf.h"
#include "upb/def.h"
// -----------------------------------------------------------------------------
// DescriptorPool
// -----------------------------------------------------------------------------
typedef struct {
PyObject_HEAD
upb_symtab* symtab;
PyObject* db;
} PyUpb_DescriptorPool;
static PyObject* PyUpb_DescriptorPool_DoCreate(PyTypeObject* type,
PyObject* db) {
PyUpb_DescriptorPool* pool = PyObject_GC_New(PyUpb_DescriptorPool, type);
pool->symtab = upb_symtab_new();
pool->db = db;
Py_XINCREF(pool->db);
PyObject_GC_Track(&pool->ob_base);
return &pool->ob_base;
}
static int PyUpb_DescriptorPool_Traverse(PyUpb_DescriptorPool* self,
visitproc visit, void* arg) {
Py_VISIT(self->db);
return 0;
}
static int PyUpb_DescriptorPool_Clear(PyUpb_DescriptorPool* self) {
Py_CLEAR(self->db);
return 0;
}
static void PyUpb_DescriptorPool_Dealloc(PyUpb_DescriptorPool* self) {
upb_symtab_free(self->symtab);
PyUpb_DescriptorPool_Clear(self);
PyObject_GC_Del(self);
}
/*
* DescriptorPool.__new__()
*
* Implements:
* DescriptorPool(descriptor_db=None)
*/
static PyObject* PyUpb_DescriptorPool_New(PyTypeObject* type, PyObject* args,
PyObject* kwargs) {
char* kwlist[] = {"descriptor_db", 0};
PyObject* db = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O", kwlist, &db)) {
return NULL;
}
return PyUpb_DescriptorPool_DoCreate(type, db);
}
/*
* PyUpb_DescriptorPool_AddSerializedFile()
*
* Implements:
* DescriptorPool.AddSerializedFile(self, serialized_file_descriptor)
*
* Adds the given serialized FileDescriptorProto to the pool.
*/
static PyObject* PyUpb_DescriptorPool_AddSerializedFile(
PyObject* _self, PyObject* serialized_pb) {
PyUpb_DescriptorPool* self = (PyUpb_DescriptorPool*)_self;
char* buf;
Py_ssize_t size;
upb_arena* arena = upb_arena_new();
PyObject* result = NULL;
if (self->db) {
PyErr_SetString(
PyExc_ValueError,
"Cannot call Add on a DescriptorPool that uses a DescriptorDatabase. "
"Add your file to the underlying database.");
return NULL;
}
if (PyBytes_AsStringAndSize(serialized_pb, &buf, &size) < 0) {
goto done;
}
google_protobuf_FileDescriptorProto* proto =
google_protobuf_FileDescriptorProto_parse(buf, size, arena);
if (!proto) {
PyErr_SetString(PyExc_TypeError, "Couldn't parse file content!");
goto done;
}
upb_status status;
upb_status_clear(&status);
const upb_filedef* filedef = upb_symtab_addfile(self->symtab, proto, &status);
if (!filedef) {
PyErr_Format(PyExc_TypeError,
"Couldn't build proto file into descriptor pool: %s",
upb_status_errmsg(&status));
goto done;
}
result = PyUpb_FileDescriptor_GetOrCreateWrapper(filedef, _self);
done:
upb_arena_free(arena);
return result;
}
/*
* PyUpb_DescriptorPool_FindExtensionByName()
*
* Implements:
* DescriptorPool.FindExtensionByName(self, name)
*/
static PyObject* PyUpb_DescriptorPool_FindExtensionByName(PyObject* _self,
PyObject* arg) {
PyUpb_DescriptorPool* self = (PyUpb_DescriptorPool*)_self;
const char* name = PyUpb_GetStrData(arg);
if (!name) return NULL;
const upb_fielddef* field = upb_symtab_lookupext(self->symtab, name);
if (field == NULL) {
return PyErr_Format(PyExc_KeyError, "Couldn't find extension %.200s", name);
}
return PyUpb_FieldDescriptor_GetOrCreateWrapper(field, _self);
}
static PyMethodDef PyUpb_DescriptorPool_Methods[] = {
/*
TODO: implement remaining methods.
{ "Add", Add, METH_O,
"Adds the FileDescriptorProto and its types to this pool." },
*/
{"AddSerializedFile", PyUpb_DescriptorPool_AddSerializedFile, METH_O,
"Adds a serialized FileDescriptorProto to this pool."},
/*
{ "FindFileByName", FindFileByName, METH_O,
"Searches for a file descriptor by its .proto name." },
{ "FindMessageTypeByName", FindMessageByName, METH_O,
"Searches for a message descriptor by full name." },
{ "FindFieldByName", FindFieldByNameMethod, METH_O,
"Searches for a field descriptor by full name." },
*/
{"FindExtensionByName", PyUpb_DescriptorPool_FindExtensionByName, METH_O,
"Searches for extension descriptor by full name."},
/*
{ "FindEnumTypeByName", FindEnumTypeByNameMethod, METH_O,
"Searches for enum type descriptor by full name." },
{ "FindOneofByName", FindOneofByNameMethod, METH_O,
"Searches for oneof descriptor by full name." },
{ "FindServiceByName", FindServiceByName, METH_O,
"Searches for service descriptor by full name." },
{ "FindMethodByName", FindMethodByName, METH_O,
"Searches for method descriptor by full name." },
{ "FindFileContainingSymbol", FindFileContainingSymbol, METH_O,
"Gets the FileDescriptor containing the specified symbol." },
{ "FindExtensionByNumber", FindExtensionByNumber, METH_VARARGS,
"Gets the extension descriptor for the given number." },
{ "FindAllExtensions", FindAllExtensions, METH_O,
"Gets all known extensions of the given message descriptor." },
*/
{NULL}};
static PyType_Slot PyUpb_DescriptorPool_Slots[] = {
{Py_tp_clear, PyUpb_DescriptorPool_Clear},
{Py_tp_dealloc, PyUpb_DescriptorPool_Dealloc},
{Py_tp_methods, PyUpb_DescriptorPool_Methods},
{Py_tp_new, PyUpb_DescriptorPool_New},
{Py_tp_traverse, PyUpb_DescriptorPool_Traverse},
{0, NULL}};
static PyType_Spec PyUpb_DescriptorPool_Spec = {
PYUPB_MODULE_NAME ".DescriptorPool",
sizeof(PyUpb_DescriptorPool),
0, // tp_itemsize
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
PyUpb_DescriptorPool_Slots,
};
// -----------------------------------------------------------------------------
// Top Level
// -----------------------------------------------------------------------------
bool PyUpb_InitDescriptorPool(PyObject* m) {
PyTypeObject* descriptor_pool_type =
AddObject(m, "DescriptorPool", &PyUpb_DescriptorPool_Spec);
if (!descriptor_pool_type) return false;
PyObject* default_pool =
PyUpb_DescriptorPool_DoCreate(descriptor_pool_type, NULL);
return default_pool &&
PyModule_AddObject(m, "default_pool", default_pool) == 0;
}

@ -0,0 +1,37 @@
/*
* Copyright (c) 2009-2021, Google LLC
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Google LLC nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef PYUPB_DESCRIPTOR_POOL_H__
#define PYUPB_DESCRIPTOR_POOL_H__
#include <stdbool.h>
#include "protobuf.h"
bool PyUpb_InitDescriptorPool(PyObject* m);
#endif // PYUPB_DESCRIPTOR_POOL_H__

@ -0,0 +1,55 @@
# Copyright (c) 2009-2021, Google LLC
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Google LLC nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""A bare-bones unit test that doesn't load any generated code."""
import unittest
from google.protobuf.pyext import _message
class TestMessageExtension(unittest.TestCase):
def test_descriptor_pool(self):
serialized_desc = b'\n\ntest.proto\"\x0e\n\x02M1*\x08\x08\x01\x10\x80\x80\x80\x80\x02:\x15\n\x08test_ext\x12\x03.M1\x18\x01 \x01(\x05'
pool = _message.DescriptorPool()
file_desc = pool.AddSerializedFile(serialized_desc)
self.assertEqual("test.proto", file_desc.name)
ext_desc = pool.FindExtensionByName("test_ext")
self.assertEqual(1, ext_desc.number)
# Test object cache: repeatedly retrieving the same descriptor
# should result in the same object
self.assertIs(ext_desc, pool.FindExtensionByName("test_ext"))
def test_lib_is_upb(self):
# Ensure we are not pulling in a different protobuf library on the
# system.
self.assertTrue(_message._IS_UPB)
if __name__ == '__main__':
unittest.main()

@ -0,0 +1,128 @@
/*
* Copyright (c) 2009-2021, Google LLC
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Google LLC nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "protobuf.h"
#include "descriptor.h"
#include "descriptor_pool.h"
static void PyUpb_ModuleDealloc(void *module) {
PyUpb_ModuleState *s = PyModule_GetState(module);
upb_arena_free(s->obj_cache_arena);
}
static struct PyModuleDef module_def = {PyModuleDef_HEAD_INIT,
PYUPB_MODULE_NAME,
"Protobuf Module",
sizeof(PyUpb_ModuleState),
NULL, // m_methods
NULL, // m_slots
NULL, // m_traverse
NULL, // m_clear
PyUpb_ModuleDealloc};
// -----------------------------------------------------------------------------
// ModuleState
// -----------------------------------------------------------------------------
PyUpb_ModuleState *PyUpb_ModuleState_Get() {
PyObject *module = PyState_FindModule(&module_def);
return PyModule_GetState(module);
}
// -----------------------------------------------------------------------------
// ObjectCache
// -----------------------------------------------------------------------------
void PyUpb_ObjCache_Add(const void *key, PyObject *py_obj) {
PyUpb_ModuleState *s = PyUpb_ModuleState_Get();
upb_inttable_insert(&s->obj_cache, (uintptr_t)key, upb_value_ptr(py_obj),
s->obj_cache_arena);
}
void PyUpb_ObjCache_Delete(const void *key) {
PyUpb_ModuleState *s = PyUpb_ModuleState_Get();
upb_value val;
upb_inttable_remove(&s->obj_cache, (uintptr_t)key, &val);
assert(upb_value_getptr(val));
}
PyObject *PyUpb_ObjCache_Get(const void *key) {
PyUpb_ModuleState *s = PyUpb_ModuleState_Get();
upb_value val;
if (upb_inttable_lookup(&s->obj_cache, (uintptr_t)key, &val)) {
PyObject *ret = upb_value_getptr(val);
Py_INCREF(ret);
return ret;
} else {
return NULL;
}
}
// -----------------------------------------------------------------------------
// Utilities
// -----------------------------------------------------------------------------
PyTypeObject *AddObject(PyObject *m, const char *name, PyType_Spec *spec) {
PyObject *type = PyType_FromSpec(spec);
return type && PyModule_AddObject(m, name, type) == 0 ? (PyTypeObject *)type
: NULL;
}
const char *PyUpb_GetStrData(PyObject *obj) {
if (PyUnicode_Check(obj)) {
return PyUnicode_AsUTF8AndSize(obj, NULL);
} else if (PyBytes_Check(obj)) {
return PyBytes_AsString(obj);
} else {
return NULL;
}
}
// -----------------------------------------------------------------------------
// Module Entry Point
// -----------------------------------------------------------------------------
PyMODINIT_FUNC PyInit__message(void) {
PyObject *m = PyModule_Create(&module_def);
PyState_AddModule(m, &module_def);
PyUpb_ModuleState *state = PyUpb_ModuleState_Get();
state->obj_cache_arena = upb_arena_new();
upb_inttable_init(&state->obj_cache, state->obj_cache_arena);
if (!PyUpb_InitDescriptorPool(m) || !PyUpb_InitDescriptor(m)) {
Py_DECREF(m);
return NULL;
}
// Temporary: an cookie we can use in the tests to ensure we are testing upb
// and not another protobuf library on the system.
PyModule_AddObject(m, "_IS_UPB", Py_True);
return m;
}

@ -0,0 +1,98 @@
/*
* Copyright (c) 2009-2021, Google LLC
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Google LLC nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef PYUPB_PROTOBUF_H__
#define PYUPB_PROTOBUF_H__
#include <stdbool.h>
#define Py_LIMITED_API 0x03060000
#include <Python.h>
// This function was not officially added to the limited API until Python 3.10.
// But in practice it has been stable since Python 3.1. See:
// https://bugs.python.org/issue41784
PyAPI_FUNC(const char *)
PyUnicode_AsUTF8AndSize(PyObject *unicode, Py_ssize_t *size);
#include "upb/table_internal.h"
#define PYUPB_MODULE_NAME "google.protobuf.pyext._message"
// -----------------------------------------------------------------------------
// ModuleState
// -----------------------------------------------------------------------------
// We store all "global" state in this struct instead of using (C) global
// variables. This makes this extension compatible with sub-interpreters.
typedef struct {
// From descriptor.c
PyTypeObject *field_descriptor_type;
PyTypeObject *file_descriptor_type;
// From descriptor_pool.c
PyTypeObject *descriptor_pool_type;
// From protobuf.c
upb_arena *obj_cache_arena;
upb_inttable obj_cache;
} PyUpb_ModuleState;
// Returns the global state object from the current interpreter. The current
// interpreter is looked up from thread-local state.
PyUpb_ModuleState *PyUpb_ModuleState_Get(void);
// -----------------------------------------------------------------------------
// ObjectCache
// -----------------------------------------------------------------------------
// The ObjectCache is a weak map that maps C pointers to the corresponding
// Python wrapper object. We want a consistent Python wrapper object for each
// C object, both to save memory and to provide object stability (ie. x is x).
//
// Each wrapped object should add itself to the map when it is constructed and
// remove itself from the map when it is destroyed. The map is weak so it does
// not take references to the cached objects.
// Adds the given object to the cache, indexed by the given key.
void PyUpb_ObjCache_Add(const void *key, PyObject *py_obj);
// Removes the given key from the cache. It must exist in the cache currently.
void PyUpb_ObjCache_Delete(const void *key);
// Returns a new reference to an object if it exists, otherwise returns NULL.
PyObject *PyUpb_ObjCache_Get(const void *key);
// -----------------------------------------------------------------------------
// Utilities
// -----------------------------------------------------------------------------
PyTypeObject *AddObject(PyObject *m, const char *name, PyType_Spec *spec);
const char *PyUpb_GetStrData(PyObject *obj);
#endif // PYUPB_PROTOBUF_H__

@ -0,0 +1,6 @@
message {
global:
PyInit__message;
local:
*;
};

@ -877,6 +877,12 @@ const upb_enumdef *upb_symtab_lookupenum(const upb_symtab *s, const char *sym) {
unpack_def(v, UPB_DEFTYPE_ENUM) : NULL;
}
const upb_fielddef *upb_symtab_lookupext(const upb_symtab *s, const char *sym) {
upb_value v;
return upb_strtable_lookup(&s->syms, sym, &v) ?
unpack_def(v, UPB_DEFTYPE_FIELD) : NULL;
}
const upb_filedef *upb_symtab_lookupfile(const upb_symtab *s, const char *name) {
upb_value v;
return upb_strtable_lookup(&s->files, name, &v) ? upb_value_getconstptr(v)

@ -308,6 +308,7 @@ const upb_msgdef *upb_symtab_lookupmsg(const upb_symtab *s, const char *sym);
const upb_msgdef *upb_symtab_lookupmsg2(
const upb_symtab *s, const char *sym, size_t len);
const upb_enumdef *upb_symtab_lookupenum(const upb_symtab *s, const char *sym);
const upb_fielddef *upb_symtab_lookupext(const upb_symtab *s, const char *sym);
const upb_filedef *upb_symtab_lookupfile(const upb_symtab *s, const char *name);
const upb_filedef *upb_symtab_lookupfile2(
const upb_symtab *s, const char *name, size_t len);

Loading…
Cancel
Save