Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement lazy loading of php class for proto messages (#6911) #6925

Merged
merged 1 commit into from Nov 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
136 changes: 95 additions & 41 deletions php/ext/google/protobuf/def.c
Expand Up @@ -828,9 +828,11 @@ static void fill_segment(const char *segment, int length,
static void fill_namespace(const char *package, const char *php_namespace,
stringsink *classname) {
if (php_namespace != NULL) {
stringsink_string(classname, NULL, php_namespace, strlen(php_namespace),
NULL);
stringsink_string(classname, NULL, "\\", 1, NULL);
if (strlen(php_namespace) != 0) {
stringsink_string(classname, NULL, php_namespace, strlen(php_namespace),
NULL);
stringsink_string(classname, NULL, "\\", 1, NULL);
}
} else if (package != NULL) {
int i = 0, j, offset = 0;
size_t package_len = strlen(package);
Expand Down Expand Up @@ -882,11 +884,23 @@ static void fill_classname(const char *fullname,
}
}

static zend_class_entry *register_class(const upb_filedef *file,
const char *fullname,
PHP_PROTO_HASHTABLE_VALUE desc_php,
bool use_nested_submsg,
bool is_enum TSRMLS_DC) {
static void fill_classname_for_desc(void *desc, bool is_enum) {
const upb_filedef *file;
const char *fullname;
bool use_nested_submsg;

if (is_enum) {
EnumDescriptorInternal* enumdesc = desc;
file = upb_enumdef_file(enumdesc->enumdef);
fullname = upb_enumdef_fullname(enumdesc->enumdef);
use_nested_submsg = enumdesc->use_nested_submsg;
} else {
DescriptorInternal* msgdesc = desc;
file = upb_msgdef_file(msgdesc->msgdef);
fullname = upb_msgdef_fullname(msgdesc->msgdef);
use_nested_submsg = msgdesc->use_nested_submsg;
}

// Prepend '.' to package name to make it absolute. In the 5 additional
// bytes allocated, one for '.', one for trailing 0, and 3 for 'GPB' if
// given message is google.protobuf.Empty.
Expand All @@ -896,36 +910,75 @@ static zend_class_entry *register_class(const upb_filedef *file,
size_t classname_len =
classname_len_max(fullname, package, php_namespace, prefix);
char* after_package;
zend_class_entry* ret;
stringsink namesink;
stringsink_init(&namesink);

fill_namespace(package, php_namespace, &namesink);
fill_classname(fullname, package, prefix, &namesink, use_nested_submsg);
stringsink_string(&namesink, NULL, "\0", 1, NULL);

if (is_enum) {
EnumDescriptorInternal* enumdesc = desc;
enumdesc->classname = strdup(namesink.ptr);
} else {
DescriptorInternal* msgdesc = desc;
msgdesc->classname = strdup(namesink.ptr);
}

stringsink_uninit(&namesink);
}

void register_class(void *desc, bool is_enum TSRMLS_DC) {
const char *classname;
const char *fullname;
zend_class_entry* ret;

if (is_enum) {
EnumDescriptorInternal* enumdesc = desc;
if (enumdesc->klass) {
return;
}
classname = enumdesc->classname;
fullname = upb_enumdef_fullname(enumdesc->enumdef);
} else {
DescriptorInternal* msgdesc = desc;
if (msgdesc->klass) {
return;
}
if (!msgdesc->classname) {
return;
}
classname = msgdesc->classname;
fullname = upb_msgdef_fullname(msgdesc->msgdef);
}

PHP_PROTO_CE_DECLARE pce;
if (php_proto_zend_lookup_class(namesink.ptr, namesink.len - 1, &pce) ==
if (php_proto_zend_lookup_class(classname, strlen(classname), &pce) ==
FAILURE) {
zend_error(
E_ERROR,
"Generated message class %s hasn't been defined (%s, %s, %s, %s)",
namesink.ptr, fullname, package, php_namespace, prefix);
return NULL;
"Generated message class %s hasn't been defined (%s)",
classname);
return;
}
ret = PHP_PROTO_CE_UNREF(pce);
add_ce_obj(ret, desc_php);
if (is_enum) {
EnumDescriptor* desc = UNBOX_HASHTABLE_VALUE(EnumDescriptor, desc_php);
add_ce_enumdesc(ret, desc->intern);
add_proto_enumdesc(fullname, desc->intern);
EnumDescriptorInternal* enumdesc = desc;
add_ce_enumdesc(ret, desc);
add_proto_enumdesc(fullname, desc);
enumdesc->klass = ret;
} else {
Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, desc_php);
add_ce_desc(ret, desc->intern);
add_proto_desc(fullname, desc->intern);
DescriptorInternal* msgdesc = desc;
add_ce_desc(ret, desc);
msgdesc->klass = ret;
// Map entries don't have existing php class.
if (!upb_msgdef_mapentry(msgdesc->msgdef)) {
if (msgdesc->layout == NULL) {
MessageLayout* layout = create_layout(msgdesc->msgdef);
msgdesc->layout = layout;
}
}
}
stringsink_uninit(&namesink);
return ret;
}

bool depends_on_descriptor(const google_protobuf_FileDescriptorProto* file) {
Expand Down Expand Up @@ -1019,6 +1072,8 @@ void internal_add_generated_file(const char *data, PHP_PROTO_SIZE data_len,
desc->intern->pool = pool;
desc->intern->layout = NULL;
desc->intern->klass = NULL;
desc->intern->use_nested_submsg = use_nested_submsg;
desc->intern->classname = NULL;

add_def_obj(desc->intern->msgdef, desc_php);
add_msgdef_desc(desc->intern->msgdef, desc->intern);
Expand All @@ -1029,15 +1084,9 @@ void internal_add_generated_file(const char *data, PHP_PROTO_SIZE data_len,
continue;
}

desc->intern->klass =
register_class(file, upb_msgdef_fullname(msgdef), desc_php,
use_nested_submsg, false TSRMLS_CC);

if (desc->intern->klass == NULL) {
return;
}

build_class_from_descriptor(desc_php TSRMLS_CC);
fill_classname_for_desc(desc->intern, false);
add_class_desc(desc->intern->classname, desc->intern);
add_proto_desc(upb_msgdef_fullname(desc->intern->msgdef), desc->intern);
}

for (i = 0; i < upb_filedef_enumcount(file); i++) {
Expand All @@ -1046,16 +1095,13 @@ void internal_add_generated_file(const char *data, PHP_PROTO_SIZE data_len,
desc->intern = SYS_MALLOC(EnumDescriptorInternal);
desc->intern->enumdef = enumdef;
desc->intern->klass = NULL;
desc->intern->use_nested_submsg = use_nested_submsg;
desc->intern->classname = NULL;

add_def_obj(desc->intern->enumdef, desc_php);
add_enumdef_enumdesc(desc->intern->enumdef, desc->intern);
desc->intern->klass =
register_class(file, upb_enumdef_fullname(enumdef), desc_php,
use_nested_submsg, true TSRMLS_CC);

if (desc->intern->klass == NULL) {
return;
}
fill_classname_for_desc(desc->intern, true);
add_class_enumdesc(desc->intern->classname, desc->intern);
}
}

Expand Down Expand Up @@ -1144,9 +1190,17 @@ PHP_METHOD(DescriptorPool, getEnumDescriptorByClassName) {
RETURN_NULL();
}

PHP_PROTO_HASHTABLE_VALUE desc_php = get_ce_obj(PHP_PROTO_CE_UNREF(pce));
zend_class_entry* ce = PHP_PROTO_CE_UNREF(pce);

PHP_PROTO_HASHTABLE_VALUE desc_php = get_ce_obj(ce);
if (desc_php == NULL) {
EnumDescriptorInternal* intern = get_ce_enumdesc(PHP_PROTO_CE_UNREF(pce));
#if PHP_MAJOR_VERSION < 7
EnumDescriptorInternal* intern = get_class_enumdesc(ce->name);
#else
EnumDescriptorInternal* intern = get_class_enumdesc(ZSTR_VAL(ce->name));
#endif
register_class(intern, true TSRMLS_CC);

if (intern == NULL) {
RETURN_NULL();
}
Expand All @@ -1164,7 +1218,7 @@ PHP_METHOD(DescriptorPool, getEnumDescriptorByClassName) {
EnumDescriptor* desc = UNBOX_HASHTABLE_VALUE(EnumDescriptor, desc_php);
desc->intern = intern;
add_def_obj(intern->enumdef, desc_php);
add_ce_obj(PHP_PROTO_CE_UNREF(pce), desc_php);
add_ce_obj(ce, desc_php);
}

zend_class_entry* instance_ce = HASHTABLE_VALUE_CE(desc_php);
Expand Down
20 changes: 11 additions & 9 deletions php/ext/google/protobuf/encode_decode.c
Expand Up @@ -429,6 +429,7 @@ static void *appendsubmsg_handler(void *closure, const void *hd) {

const submsg_handlerdata_t *submsgdata = hd;
DescriptorInternal* subdesc = get_msgdef_desc(submsgdata->md);
register_class(subdesc, false TSRMLS_CC);
zend_class_entry* subklass = subdesc->klass;
MessageHeader* submsg;

Expand Down Expand Up @@ -456,6 +457,7 @@ static void *appendwrappersubmsg_handler(void *closure, const void *hd) {

const submsg_handlerdata_t *submsgdata = hd;
DescriptorInternal* subdesc = get_msgdef_desc(submsgdata->md);
register_class(subdesc, false TSRMLS_CC);
zend_class_entry* subklass = subdesc->klass;
MessageHeader* submsg;
wrapperfields_parseframe_t* frame =
Expand Down Expand Up @@ -487,6 +489,7 @@ static void *submsg_handler(void *closure, const void *hd) {
const submsg_handlerdata_t* submsgdata = hd;
TSRMLS_FETCH();
DescriptorInternal* subdesc = get_msgdef_desc(submsgdata->md);
register_class(subdesc, false TSRMLS_CC);
zend_class_entry* subklass = subdesc->klass;
zval* submsg_php;
MessageHeader* submsg;
Expand Down Expand Up @@ -520,6 +523,7 @@ static void *map_submsg_handler(void *closure, const void *hd) {
const submsg_handlerdata_t* submsgdata = hd;
TSRMLS_FETCH();
DescriptorInternal* subdesc = get_msgdef_desc(submsgdata->md);
register_class(subdesc, false TSRMLS_CC);
zend_class_entry* subklass = subdesc->klass;
zval* submsg_php;
MessageHeader* submsg;
Expand Down Expand Up @@ -554,6 +558,7 @@ static void *map_wrapper_submsg_handler(void *closure, const void *hd) {
const submsg_handlerdata_t* submsgdata = hd;
TSRMLS_FETCH();
DescriptorInternal* subdesc = get_msgdef_desc(submsgdata->md);
register_class(subdesc, false TSRMLS_CC);
zend_class_entry* subklass = subdesc->klass;
zval* submsg_php;
MessageHeader* submsg;
Expand Down Expand Up @@ -641,6 +646,7 @@ static void map_slot_init(
}
case UPB_TYPE_MESSAGE: {
DescriptorInternal* subdesc = get_msgdef_desc(value_msg);
register_class(subdesc, false TSRMLS_CC);
zend_class_entry* subklass = subdesc->klass;
MessageHeader* submsg;
#if PHP_MAJOR_VERSION < 7
Expand Down Expand Up @@ -938,6 +944,7 @@ static void* oneofsubmsg_handler(void* closure, const void* hd) {
uint32_t oldcase = DEREF(message_data(msg), oneofdata->case_ofs, uint32_t);
TSRMLS_FETCH();
DescriptorInternal* subdesc = get_msgdef_desc(oneofdata->md);
register_class(subdesc, false TSRMLS_CC);
zend_class_entry* subklass = subdesc->klass;
zval* submsg_php;
MessageHeader* submsg;
Expand Down Expand Up @@ -976,6 +983,7 @@ static void* wrapper_submsg_handler(void* closure, const void* hd) {
const submsg_handlerdata_t* submsgdata = hd;
TSRMLS_FETCH();
DescriptorInternal* subdesc = get_msgdef_desc(submsgdata->md);
register_class(subdesc, false TSRMLS_CC);
zend_class_entry* subklass = subdesc->klass;
zval* submsg_php;
MessageHeader* submsg;
Expand Down Expand Up @@ -1007,6 +1015,7 @@ static void* wrapper_oneofsubmsg_handler(void* closure, const void* hd) {
uint32_t oldcase = DEREF(message_data(msg), oneofdata->case_ofs, uint32_t);
TSRMLS_FETCH();
DescriptorInternal* subdesc = get_msgdef_desc(oneofdata->md);
register_class(subdesc, false TSRMLS_CC);
zend_class_entry* subklass = subdesc->klass;
wrapperfields_parseframe_t* frame =
(wrapperfields_parseframe_t*)malloc(sizeof(wrapperfields_parseframe_t));
Expand Down Expand Up @@ -1347,6 +1356,7 @@ void add_handlers_for_message(const void* closure, upb_handlers* h) {
const upb_msgdef* msgdef = upb_handlers_msgdef(h);
TSRMLS_FETCH();
DescriptorInternal* desc = get_msgdef_desc(msgdef);
register_class(desc, false TSRMLS_CC);
upb_msg_field_iter i;

// If this is a mapentry message type, set up a special set of handlers and
Expand All @@ -1356,15 +1366,6 @@ void add_handlers_for_message(const void* closure, upb_handlers* h) {
return;
}

// Ensure layout exists. We may be invoked to create handlers for a given
// message if we are included as a submsg of another message type before our
// class is actually built, so to work around this, we just create the layout
// (and handlers, in the class-building function) on-demand.
if (desc->layout == NULL) {
desc->layout = create_layout(desc->msgdef);
}


// If this is a wrapper message type, set up a special set of handlers and
// bail out of the normal (user-defined) message type handling.
if (is_wrapper_msg(msgdef)) {
Expand Down Expand Up @@ -1635,6 +1636,7 @@ static void putjsonany(MessageHeader* msg, const DescriptorInternal* desc,

if (value_len > 0) {
DescriptorInternal* payload_desc = get_msgdef_desc(payload_type);
register_class(payload_desc, false TSRMLS_CC);
zend_class_entry* payload_klass = payload_desc->klass;
zval val;
upb_sink subsink;
Expand Down