Lazily Create Singular Wrapper Message (#6833)

* Register additional handlers from wrappers

* Return zval instead of parse frame

* Use parse frame

* Update upb

* Lazily create wrapper messages

* Fix a segment fault

Need check type of field before getting submsg def

* Avoid expanding during serialization and direct access

* Fix a bug that getXXXUnwrapped returns null for string

* Implement writeWrapperUnwrapped

* Add more tests

* Fix oneof wrapper parsing

* Fix get oneof field

* Avoid expansion for oneof wrappers

* Fix bug

* Fix a bug that in php7 variable is defined out of scope

* Fix broken tests
 * Update upb to fix Timestamp conformance tests
 * Fix segmentation fault for oneof wrapper fields

* Fix encoding/decoding top level wrapper values

* Add type checking for write wrapper value in php7

* Fix zts build

* Fix the bug that readWrapperValue uses parent message's layout to access wrapper value

* Fix wrapper in map
pull/6891/head
Paul Yang 5 years ago committed by GitHub
parent f06800524f
commit 601f6963eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 393
      php/ext/google/protobuf/encode_decode.c
  2. 135
      php/ext/google/protobuf/message.c
  3. 5
      php/ext/google/protobuf/protobuf.h
  4. 113
      php/ext/google/protobuf/storage.c
  5. 170
      php/ext/google/protobuf/upb.c
  6. 13
      php/ext/google/protobuf/upb.h
  7. 36
      php/src/Google/Protobuf/Internal/Message.php
  8. 220
      php/tests/encode_decode_test.php
  9. 6
      php/tests/memory_leak_test.php
  10. 60
      php/tests/proto/test.proto
  11. 3
      php/tests/wrapper_type_setters_test.php
  12. 14
      src/google/protobuf/compiler/php/php_generator.cc

@ -122,12 +122,29 @@ static void stackenv_uninit(stackenv* se) {
// Parsing.
// -----------------------------------------------------------------------------
// TODO(teboring): This shoud be a bit in upb_msgdef
static bool is_wrapper_msg(const upb_msgdef *msg) {
return !strcmp(upb_filedef_name(upb_msgdef_file(msg)),
"google/protobuf/wrappers.proto");
bool is_wrapper_msg(const upb_msgdef* m) {
switch (upb_msgdef_wellknowntype(m)) {
case UPB_WELLKNOWN_DOUBLEVALUE:
case UPB_WELLKNOWN_FLOATVALUE:
case UPB_WELLKNOWN_INT64VALUE:
case UPB_WELLKNOWN_UINT64VALUE:
case UPB_WELLKNOWN_INT32VALUE:
case UPB_WELLKNOWN_UINT32VALUE:
case UPB_WELLKNOWN_STRINGVALUE:
case UPB_WELLKNOWN_BYTESVALUE:
case UPB_WELLKNOWN_BOOLVALUE:
return true;
default:
return false;
}
}
typedef struct {
void* closure;
void* submsg;
bool is_msg;
} wrapperfields_parseframe_t;
#define DEREF(msg, ofs, type) *(type*)(((uint8_t *)msg) + ofs)
// Creates a handlerdata that simply contains the offset for this field.
@ -432,6 +449,40 @@ static void *appendsubmsg_handler(void *closure, const void *hd) {
return submsg;
}
// Appends a wrapper submessage to a repeated field.
static void *appendwrappersubmsg_handler(void *closure, const void *hd) {
zval* array = (zval*)closure;
TSRMLS_FETCH();
RepeatedField* intern = UNBOX(RepeatedField, array);
const submsg_handlerdata_t *submsgdata = hd;
Descriptor* subdesc =
UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj((void*)submsgdata->md));
zend_class_entry* subklass = subdesc->klass;
MessageHeader* submsg;
wrapperfields_parseframe_t* frame =
(wrapperfields_parseframe_t*)malloc(sizeof(wrapperfields_parseframe_t));
#if PHP_MAJOR_VERSION < 7
zval* val = NULL;
MAKE_STD_ZVAL(val);
ZVAL_OBJ(val, subklass->create_object(subklass TSRMLS_CC));
repeated_field_push_native(intern, &val);
submsg = UNBOX(MessageHeader, val);
#else
zend_object* obj = subklass->create_object(subklass TSRMLS_CC);
repeated_field_push_native(intern, &obj);
submsg = (MessageHeader*)((char*)obj - XtOffsetOf(MessageHeader, std));
#endif
custom_data_init(subklass, submsg PHP_PROTO_TSRMLS_CC);
frame->closure = closure;
frame->submsg = submsg;
frame->is_msg = true;
return frame;
}
// Sets a non-repeated submessage field in a message.
static void *submsg_handler(void *closure, const void *hd) {
MessageHeader* msg = closure;
@ -502,6 +553,46 @@ static void *map_submsg_handler(void *closure, const void *hd) {
return submsg;
}
static void *map_wrapper_submsg_handler(void *closure, const void *hd) {
MessageHeader* msg = closure;
const submsg_handlerdata_t* submsgdata = hd;
TSRMLS_FETCH();
Descriptor* subdesc =
UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj((void*)submsgdata->md));
zend_class_entry* subklass = subdesc->klass;
zval* submsg_php;
MessageHeader* submsg;
wrapperfields_parseframe_t* frame =
(wrapperfields_parseframe_t*)malloc(sizeof(wrapperfields_parseframe_t));
CACHED_VALUE* cached =
DEREF(message_data(msg), submsgdata->ofs, CACHED_VALUE*);
if (Z_TYPE_P(CACHED_PTR_TO_ZVAL_PTR(cached)) == IS_NULL) {
#if PHP_MAJOR_VERSION < 7
zval val;
ZVAL_OBJ(&val, subklass->create_object(subklass TSRMLS_CC));
MessageHeader* intern = UNBOX(MessageHeader, &val);
custom_data_init(subklass, intern PHP_PROTO_TSRMLS_CC);
REPLACE_ZVAL_VALUE(cached, &val, 1);
zval_dtor(&val);
#else
zend_object* obj = subklass->create_object(subklass TSRMLS_CC);
ZVAL_OBJ(cached, obj);
MessageHeader* intern = UNBOX_HASHTABLE_VALUE(MessageHeader, obj);
custom_data_init(subklass, intern PHP_PROTO_TSRMLS_CC);
#endif
}
submsg_php = CACHED_PTR_TO_ZVAL_PTR(cached);
submsg = UNBOX(MessageHeader, submsg_php);
frame->closure = closure;
frame->submsg = submsg;
frame->is_msg = true;
return frame;
}
// Handler data for startmap/endmap handlers.
typedef struct {
const upb_fielddef* fd;
@ -887,6 +978,78 @@ static void* oneofsubmsg_handler(void* closure, const void* hd) {
return submsg;
}
// Sets a non-repeated wrapper submessage field in a message.
static void* wrapper_submsg_handler(void* closure, const void* hd) {
MessageHeader* msg = closure;
const submsg_handlerdata_t* submsgdata = hd;
TSRMLS_FETCH();
Descriptor* subdesc =
UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj((void*)submsgdata->md));
zend_class_entry* subklass = subdesc->klass;
zval* submsg_php;
MessageHeader* submsg;
wrapperfields_parseframe_t* frame =
(wrapperfields_parseframe_t*)malloc(sizeof(wrapperfields_parseframe_t));
CACHED_VALUE* cached = find_zval_property(msg, submsgdata->fd);
submsg_php = CACHED_PTR_TO_ZVAL_PTR(cached);
frame->closure = closure;
if (Z_TYPE_P(CACHED_PTR_TO_ZVAL_PTR(cached)) == IS_OBJECT) {
submsg = UNBOX(MessageHeader, submsg_php);
frame->submsg = submsg;
frame->is_msg = true;
} else {
// In this case, wrapper message hasn't been created and value will be
// stored in cache directly.
frame->submsg = cached;
frame->is_msg = false;
}
return frame;
}
// Handler for a wrapper submessage field in a oneof.
static void* wrapper_oneofsubmsg_handler(void* closure, const void* hd) {
MessageHeader* msg = closure;
const oneof_handlerdata_t *oneofdata = hd;
uint32_t oldcase = DEREF(message_data(msg), oneofdata->case_ofs, uint32_t);
TSRMLS_FETCH();
Descriptor* subdesc =
UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj((void*)oneofdata->md));
zend_class_entry* subklass = subdesc->klass;
wrapperfields_parseframe_t* frame =
(wrapperfields_parseframe_t*)malloc(sizeof(wrapperfields_parseframe_t));
CACHED_VALUE* cached = OBJ_PROP(&msg->std, oneofdata->property_ofs);
MessageHeader* submsg;
if (oldcase != oneofdata->oneof_case_num) {
oneof_cleanup(msg, oneofdata);
frame->submsg = cached;
frame->is_msg = false;
} else if (Z_TYPE_P(CACHED_PTR_TO_ZVAL_PTR(cached)) == IS_OBJECT) {
submsg = UNBOX(MessageHeader, CACHED_PTR_TO_ZVAL_PTR(cached));
frame->submsg = submsg;
frame->is_msg = true;
} else {
// In this case, wrapper message hasn't been created and value will be
// stored in cache directly.
frame->submsg = cached;
frame->is_msg = false;
}
DEREF(message_data(msg), oneofdata->case_ofs, uint32_t) =
oneofdata->oneof_case_num;
return frame;
}
static bool wrapper_submsg_end_handler(void *closure, const void *hd) {
wrapperfields_parseframe_t* frame = closure;
free(frame);
return true;
}
// Set up handlers for a repeated field.
static void add_handlers_for_repeated_field(upb_handlers *h,
const upb_fielddef *f,
@ -923,7 +1086,12 @@ static void add_handlers_for_repeated_field(upb_handlers *h,
case UPB_TYPE_MESSAGE: {
upb_handlerattr attr = UPB_HANDLERATTR_INIT;
attr.handler_data = newsubmsghandlerdata(h, 0, f);
upb_handlers_setstartsubmsg(h, f, appendsubmsg_handler, &attr);
if (is_wrapper_msg(upb_fielddef_msgsubdef(f))) {
upb_handlers_setstartsubmsg(h, f, appendwrappersubmsg_handler, &attr);
upb_handlers_setendsubmsg(h, f, wrapper_submsg_end_handler, &attr);
} else {
upb_handlers_setstartsubmsg(h, f, appendsubmsg_handler, &attr);
}
break;
}
}
@ -974,7 +1142,16 @@ static void add_handlers_for_singular_field(upb_handlers *h,
upb_handlerattr attr = UPB_HANDLERATTR_INIT;
if (is_map) {
attr.handler_data = newsubmsghandlerdata(h, offset, f);
upb_handlers_setstartsubmsg(h, f, map_submsg_handler, &attr);
if (is_wrapper_msg(upb_fielddef_msgsubdef(f))) {
upb_handlers_setstartsubmsg(h, f, map_wrapper_submsg_handler, &attr);
upb_handlers_setendsubmsg(h, f, wrapper_submsg_end_handler, &attr);
} else {
upb_handlers_setstartsubmsg(h, f, map_submsg_handler, &attr);
}
} else if (is_wrapper_msg(upb_fielddef_msgsubdef(f))) {
attr.handler_data = newsubmsghandlerdata(h, 0, f);
upb_handlers_setstartsubmsg(h, f, wrapper_submsg_handler, &attr);
upb_handlers_setendsubmsg(h, f, wrapper_submsg_end_handler, &attr);
} else {
attr.handler_data = newsubmsghandlerdata(h, 0, f);
upb_handlers_setstartsubmsg(h, f, submsg_handler, &attr);
@ -1056,9 +1233,106 @@ static void add_handlers_for_oneof_field(upb_handlers *h,
break;
}
case UPB_TYPE_MESSAGE: {
upb_handlers_setstartsubmsg(h, f, oneofsubmsg_handler, &attr);
if (is_wrapper_msg(upb_fielddef_msgsubdef(f))) {
upb_handlers_setstartsubmsg(h, f, wrapper_oneofsubmsg_handler, &attr);
upb_handlers_setendsubmsg(h, f, wrapper_submsg_end_handler, &attr);
} else {
upb_handlers_setstartsubmsg(h, f, oneofsubmsg_handler, &attr);
}
break;
}
}
}
#define DEFINE_WRAPPER_HANDLER(utype, type, ctype) \
static bool type##wrapper_handler( \
void* closure, const void* hd, ctype val) { \
wrapperfields_parseframe_t* frame = closure; \
if (frame->is_msg) { \
MessageHeader* msg = frame->submsg; \
const size_t *ofs = hd; \
DEREF(message_data(msg), *ofs, ctype) = val; \
} else { \
TSRMLS_FETCH(); \
native_slot_get(utype, &val, frame->submsg TSRMLS_CC); \
} \
return true; \
}
DEFINE_WRAPPER_HANDLER(UPB_TYPE_BOOL, bool, bool)
DEFINE_WRAPPER_HANDLER(UPB_TYPE_INT32, int32, int32_t)
DEFINE_WRAPPER_HANDLER(UPB_TYPE_UINT32, uint32, uint32_t)
DEFINE_WRAPPER_HANDLER(UPB_TYPE_FLOAT, float, float)
DEFINE_WRAPPER_HANDLER(UPB_TYPE_INT64, int64, int64_t)
DEFINE_WRAPPER_HANDLER(UPB_TYPE_UINT64, uint64, uint64_t)
DEFINE_WRAPPER_HANDLER(UPB_TYPE_DOUBLE, double, double)
#undef DEFINE_WRAPPER_HANDLER
static bool strwrapper_end_handler(void *closure, const void *hd) {
stringfields_parseframe_t* frame = closure;
const upb_fielddef **field = (const upb_fielddef **) hd;
wrapperfields_parseframe_t* wrapper_frame = frame->closure;
MessageHeader* msg;
CACHED_VALUE* cached;
if (wrapper_frame->is_msg) {
msg = wrapper_frame->submsg;
cached = find_zval_property(msg, *field);
} else {
cached = wrapper_frame->submsg;
}
new_php_string(cached, frame->sink.ptr, frame->sink.len);
stringsink_uninit(&frame->sink);
free(frame);
return true;
}
static void add_handlers_for_wrapper(const upb_msgdef* msgdef,
upb_handlers* h) {
const upb_fielddef* f = upb_msgdef_itof(msgdef, 1);
Descriptor* desc;
size_t offset;
TSRMLS_FETCH();
desc = UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj((void*)msgdef));
offset = desc->layout->fields[upb_fielddef_index(f)].offset;
switch (upb_msgdef_wellknowntype(msgdef)) {
#define SET_HANDLER(utype, ltype) \
case utype: { \
upb_handlerattr attr = UPB_HANDLERATTR_INIT; \
attr.handler_data = newhandlerdata(h, offset); \
upb_handlers_set##ltype(h, f, ltype##wrapper_handler, &attr); \
break; \
}
SET_HANDLER(UPB_WELLKNOWN_BOOLVALUE, bool);
SET_HANDLER(UPB_WELLKNOWN_INT32VALUE, int32);
SET_HANDLER(UPB_WELLKNOWN_UINT32VALUE, uint32);
SET_HANDLER(UPB_WELLKNOWN_FLOATVALUE, float);
SET_HANDLER(UPB_WELLKNOWN_INT64VALUE, int64);
SET_HANDLER(UPB_WELLKNOWN_UINT64VALUE, uint64);
SET_HANDLER(UPB_WELLKNOWN_DOUBLEVALUE, double);
#undef SET_HANDLER
case UPB_WELLKNOWN_STRINGVALUE:
case UPB_WELLKNOWN_BYTESVALUE: {
upb_handlerattr attr = UPB_HANDLERATTR_INIT;
attr.handler_data = newhandlerfielddata(h, f);
upb_handlers_setstartstr(h, f, str_handler, &attr);
upb_handlers_setstring(h, f, stringdata_handler, &attr);
upb_handlers_setendstr(h, f, strwrapper_end_handler, &attr);
break;
}
default:
// Cannot reach here.
break;
}
}
@ -1102,6 +1376,14 @@ void add_handlers_for_message(const void* closure, upb_handlers* h) {
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)) {
add_handlers_for_wrapper(msgdef, h);
return;
}
upb_handlerattr attr = UPB_HANDLERATTR_INIT;
attr.handler_data = newunknownfieldshandlerdata(h);
upb_handlers_setunknown(h, add_unknown_handler, &attr);
@ -1152,6 +1434,9 @@ static void putmsg(zval* msg, const Descriptor* desc, upb_sink sink,
static void putrawmsg(MessageHeader* msg, const Descriptor* desc,
upb_sink sink, int depth, bool is_json,
bool open_msg TSRMLS_DC);
static void putwrappervalue(
zval* value, const upb_fielddef* f,
upb_sink sink, int depth, bool is_json TSRMLS_DC);
static void putjsonany(MessageHeader* msg, const Descriptor* desc,
upb_sink sink, int depth TSRMLS_DC);
static void putjsonlistvalue(
@ -1299,7 +1584,7 @@ static void putmap(zval* map, const upb_fielddef* f, upb_sink sink,
value_field, depth + 1, entry_sink, is_json TSRMLS_CC);
upb_sink_endmsg(entry_sink, &status);
upb_sink_endsubmsg(subsink, getsel(f, UPB_HANDLER_ENDSUBMSG));
upb_sink_endsubmsg(subsink, entry_sink, getsel(f, UPB_HANDLER_ENDSUBMSG));
}
upb_sink_endseq(sink, getsel(f, UPB_HANDLER_ENDSEQ));
@ -1535,7 +1820,12 @@ static void putrawmsg(MessageHeader* msg, const Descriptor* desc,
}
} else if (upb_fielddef_issubmsg(f)) {
zval* submsg = CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, f));
putsubmsg(submsg, f, sink, depth, is_json TSRMLS_CC);
if (is_wrapper_msg(upb_fielddef_msgsubdef(f)) &&
Z_TYPE_P(submsg) != IS_NULL && Z_TYPE_P(submsg) != IS_OBJECT) {
putwrappervalue(submsg, f, sink, depth, is_json TSRMLS_CC);
} else {
putsubmsg(submsg, f, sink, depth, is_json TSRMLS_CC);
}
} else {
upb_selector_t sel = getsel(f, upb_handlers_getprimitivehandlertype(f));
@ -1634,6 +1924,54 @@ static void putsubmsg(zval* submsg_php, const upb_fielddef* f, upb_sink sink,
putrawsubmsg(submsg, f, sink, depth, is_json TSRMLS_CC);
}
static void putwrappervalue(
zval* value, const upb_fielddef* f,
upb_sink sink, int depth, bool is_json TSRMLS_DC) {
upb_sink subsink;
const upb_msgdef* msgdef = upb_fielddef_msgsubdef(f);
const upb_fielddef* value_field = upb_msgdef_itof(msgdef, 1);
upb_selector_t sel =
getsel(value_field, upb_handlers_getprimitivehandlertype(value_field));
upb_sink_startsubmsg(sink, getsel(f, UPB_HANDLER_STARTSUBMSG), &subsink);
#define T(upbtypeconst, upbtype, ctype, default_value) \
case upbtypeconst: { \
ctype value_raw; \
native_slot_set(upb_fielddef_type(value_field), NULL, \
&value_raw, value PHP_PROTO_TSRMLS_CC); \
if ((is_json && is_wrapper_msg(msgdef)) || \
value_raw != default_value) { \
upb_sink_put##upbtype(subsink, sel, value_raw); \
} \
} break;
switch (upb_fielddef_type(value_field)) {
T(UPB_TYPE_FLOAT, float, float, 0.0)
T(UPB_TYPE_DOUBLE, double, double, 0.0)
T(UPB_TYPE_BOOL, bool, uint8_t, 0)
T(UPB_TYPE_INT32, int32, int32_t, 0)
T(UPB_TYPE_UINT32, uint32, uint32_t, 0)
T(UPB_TYPE_INT64, int64, int64_t, 0)
T(UPB_TYPE_UINT64, uint64, uint64_t, 0)
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES: {
if ((is_json && is_wrapper_msg(msgdef)) ||
Z_STRLEN_P(value) > 0) {
putstr(value, value_field, subsink, is_json && is_wrapper_msg(msgdef));
}
break;
}
case UPB_TYPE_ENUM:
case UPB_TYPE_MESSAGE:
zend_error(E_ERROR, "Internal error.");
}
#undef T
upb_sink_endsubmsg(sink, subsink, getsel(f, UPB_HANDLER_ENDSUBMSG));
}
static void putrawsubmsg(MessageHeader* submsg, const upb_fielddef* f,
upb_sink sink, int depth, bool is_json TSRMLS_DC) {
upb_sink subsink;
@ -1643,7 +1981,7 @@ static void putrawsubmsg(MessageHeader* submsg, const upb_fielddef* f,
upb_sink_startsubmsg(sink, getsel(f, UPB_HANDLER_STARTSUBMSG), &subsink);
putrawmsg(submsg, subdesc, subsink, depth + 1, is_json, true TSRMLS_CC);
upb_sink_endsubmsg(sink, getsel(f, UPB_HANDLER_ENDSUBMSG));
upb_sink_endsubmsg(sink, subsink, getsel(f, UPB_HANDLER_ENDSUBMSG));
}
static void putarray(zval* array, const upb_fielddef* f, upb_sink sink,
@ -1769,12 +2107,28 @@ void merge_from_string(const char* data, int data_len, Descriptor* desc,
stackenv se;
upb_sink sink;
upb_pbdecoder* decoder;
void* closure;
stackenv_init(&se, "Error occurred during parsing: %s");
upb_sink_reset(&sink, h, msg);
if (is_wrapper_msg(desc->msgdef)) {
wrapperfields_parseframe_t* frame =
(wrapperfields_parseframe_t*)malloc(
sizeof(wrapperfields_parseframe_t));
frame->submsg = msg;
frame->is_msg = true;
closure = frame;
} else {
closure = msg;
}
upb_sink_reset(&sink, h, closure);
decoder = upb_pbdecoder_create(se.arena, method, sink, &se.status);
upb_bufsrc_putbuf(data, data_len, upb_pbdecoder_input(decoder));
if (is_wrapper_msg(desc->msgdef)) {
free((wrapperfields_parseframe_t*)closure);
}
stackenv_uninit(&se);
}
@ -1852,13 +2206,28 @@ PHP_METHOD(Message, mergeFromJsonString) {
stackenv se;
upb_sink sink;
upb_json_parser* parser;
void* closure;
stackenv_init(&se, "Error occurred during parsing: %s");
upb_sink_reset(&sink, get_fill_handlers(desc), msg);
if (is_wrapper_msg(desc->msgdef)) {
wrapperfields_parseframe_t* frame =
(wrapperfields_parseframe_t*)malloc(
sizeof(wrapperfields_parseframe_t));
frame->submsg = msg;
frame->is_msg = true;
closure = frame;
} else {
closure = msg;
}
upb_sink_reset(&sink, get_fill_handlers(desc), closure);
parser = upb_json_parser_create(se.arena, method, generated_pool->symtab,
sink, &se.status, ignore_json_unknown);
upb_bufsrc_putbuf(data, data_len, upb_json_parser_input(parser));
if (is_wrapper_msg(desc->msgdef)) {
free((wrapperfields_parseframe_t*)closure);
}
stackenv_uninit(&se);
}
}

@ -55,6 +55,8 @@ static zend_function_entry message_methods[] = {
PHP_ME(Message, serializeToJsonString, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Message, mergeFromJsonString, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Message, mergeFrom, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Message, readWrapperValue, NULL, ZEND_ACC_PROTECTED)
PHP_ME(Message, writeWrapperValue, NULL, ZEND_ACC_PROTECTED)
PHP_ME(Message, readOneof, NULL, ZEND_ACC_PROTECTED)
PHP_ME(Message, writeOneof, NULL, ZEND_ACC_PROTECTED)
PHP_ME(Message, whichOneof, NULL, ZEND_ACC_PROTECTED)
@ -295,12 +297,6 @@ void build_class_from_descriptor(
// PHP Methods
// -----------------------------------------------------------------------------
static bool is_wrapper_msg(const upb_msgdef* m) {
upb_wellknowntype_t type = upb_msgdef_wellknowntype(m);
return type >= UPB_WELLKNOWN_DOUBLEVALUE &&
type <= UPB_WELLKNOWN_BOOLVALUE;
}
static void append_wrapper_message(
zend_class_entry* subklass, RepeatedField* intern, zval* value TSRMLS_DC) {
MessageHeader* submsg;
@ -561,6 +557,133 @@ PHP_METHOD(Message, mergeFrom) {
layout_merge(from->descriptor->layout, from, to TSRMLS_CC);
}
PHP_METHOD(Message, readWrapperValue) {
char* member;
PHP_PROTO_SIZE length;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &member,
&length) == FAILURE) {
return;
}
MessageHeader* msg = UNBOX(MessageHeader, getThis());
const upb_fielddef* field =
upb_msgdef_ntofz(msg->descriptor->msgdef, member);
if (upb_fielddef_containingoneof(field)) {
uint32_t* oneof_case =
slot_oneof_case(msg->descriptor->layout, message_data(msg), field);
if (*oneof_case != upb_fielddef_number(field)) {
RETURN_NULL();
}
}
zval* cached_zval =
CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, field));
if (Z_TYPE_P(cached_zval) == IS_NULL) {
RETURN_NULL();
}
if (Z_TYPE_P(cached_zval) == IS_OBJECT) {
const upb_msgdef* submsgdef = upb_fielddef_msgsubdef(field);
const upb_fielddef* value_field = upb_msgdef_itof(submsgdef, 1);
MessageHeader* submsg = UNBOX(MessageHeader, cached_zval);
CACHED_VALUE* cached_value = find_zval_property(submsg, value_field);
layout_get(submsg->descriptor->layout, submsg, value_field,
cached_value TSRMLS_CC);
RETURN_ZVAL(CACHED_PTR_TO_ZVAL_PTR(cached_value), 1, 0);
} else {
RETURN_ZVAL(cached_zval, 1, 0);
}
}
PHP_METHOD(Message, writeWrapperValue) {
char* member;
PHP_PROTO_SIZE length;
zval* value;
if (zend_parse_parameters(
ZEND_NUM_ARGS() TSRMLS_CC, "sz", &member, &length, &value) ==
FAILURE) {
return;
}
MessageHeader* msg = UNBOX(MessageHeader, getThis());
const upb_fielddef* field = upb_msgdef_ntofz(msg->descriptor->msgdef, member);
zval* cached_zval =
CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, field));
if (Z_TYPE_P(value) == IS_NULL) {
MessageHeader* msg = UNBOX(MessageHeader, getThis());
layout_set(msg->descriptor->layout, msg,
field, value TSRMLS_CC);
return;
}
{
// Type Checking
const upb_msgdef* submsgdef = upb_fielddef_msgsubdef(field);
const upb_fielddef* value_field = upb_msgdef_itof(submsgdef, 1);
upb_fieldtype_t type = upb_fielddef_type(value_field);
switch(type) {
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES: {
if (!protobuf_convert_to_string(value)) {
return;
}
if (type == UPB_TYPE_STRING &&
!is_structurally_valid_utf8(Z_STRVAL_P(value), Z_STRLEN_P(value))) {
zend_error(E_USER_ERROR, "Given string is not UTF8 encoded.");
return;
}
}
break;
#define CASE_TYPE(upb_type, type, c_type) \
case UPB_TYPE_##upb_type: { \
c_type type##_value; \
if (!protobuf_convert_to_##type(value, &type##_value)) { \
return; \
} \
break; \
}
CASE_TYPE(INT32, int32, int32_t)
CASE_TYPE(UINT32, uint32, uint32_t)
CASE_TYPE(ENUM, int32, int32_t)
CASE_TYPE(INT64, int64, int64_t)
CASE_TYPE(UINT64, uint64, uint64_t)
CASE_TYPE(FLOAT, float, float)
CASE_TYPE(DOUBLE, double, double)
CASE_TYPE(BOOL, bool, int8_t)
#undef CASE_TYPE
}
}
if (upb_fielddef_containingoneof(field)) {
uint32_t* oneof_case =
slot_oneof_case(msg->descriptor->layout, message_data(msg), field);
if (*oneof_case != upb_fielddef_number(field)) {
zval null_value;
ZVAL_NULL(&null_value);
layout_set(msg->descriptor->layout, msg, field, &null_value TSRMLS_CC);
cached_zval = CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, field));
ZVAL_ZVAL(cached_zval, value, 1, 0);
return;
}
}
if (Z_TYPE_P(cached_zval) == IS_OBJECT) {
const upb_msgdef* submsgdef = upb_fielddef_msgsubdef(field);
const upb_fielddef* value_field = upb_msgdef_itof(submsgdef, 1);
MessageHeader* submsg = UNBOX(MessageHeader, cached_zval);
CACHED_VALUE* cached_value = find_zval_property(submsg, value_field);
layout_set(submsg->descriptor->layout, submsg,
value_field, value TSRMLS_CC);
} else {
ZVAL_ZVAL(cached_zval, value, 1, 0);
}
}
PHP_METHOD(Message, readOneof) {
PHP_PROTO_LONG index;

@ -965,6 +965,8 @@ void* slot_memory(MessageLayout* layout, const void* storage,
PHP_METHOD(Message, clear);
PHP_METHOD(Message, mergeFrom);
PHP_METHOD(Message, readWrapperValue);
PHP_METHOD(Message, writeWrapperValue);
PHP_METHOD(Message, readOneof);
PHP_METHOD(Message, writeOneof);
PHP_METHOD(Message, whichOneof);
@ -1525,4 +1527,7 @@ static inline zval* php_proto_message_read_property(
bool is_reserved_name(const char* name);
bool is_valid_constant_name(const char* name);
// For lazy wrapper
bool is_wrapper_msg(const upb_msgdef* m);
#endif // __GOOGLE_PROTOBUF_PHP_PROTOBUF_H__

@ -116,17 +116,17 @@ bool native_slot_set(upb_fieldtype_t type, const zend_class_entry* klass,
return false;
}
#if PHP_MAJOR_VERSION < 7
REPLACE_ZVAL_VALUE((CACHED_VALUE*)memory, value, 1);
#else
zval* property_ptr = CACHED_PTR_TO_ZVAL_PTR((CACHED_VALUE*)memory);
if (EXPECTED(property_ptr != value)) {
php_proto_zval_ptr_dtor(property_ptr);
}
#if PHP_MAJOR_VERSION < 7
DEREF(memory, zval*) = value;
Z_ADDREF_P(value);
#else
ZVAL_ZVAL(property_ptr, value, 1, 0);
#endif
break;
}
@ -797,25 +797,62 @@ zval* layout_get(MessageLayout* layout, MessageHeader* header,
if (upb_fielddef_containingoneof(field)) {
if (*oneof_case != upb_fielddef_number(field)) {
native_slot_get_default(upb_fielddef_type(field), cache TSRMLS_CC);
} else {
upb_fieldtype_t type = upb_fielddef_type(field);
CACHED_VALUE* stored_cache = find_zval_property(header, field);
native_slot_get(
type, value_memory(type, memory, stored_cache), cache TSRMLS_CC);
return CACHED_PTR_TO_ZVAL_PTR(cache);
}
return CACHED_PTR_TO_ZVAL_PTR(cache);
// Intentional fall through to be handled as a signuarl field.
} else if (is_map_field(field)) {
map_field_ensure_created(field, cache PHP_PROTO_TSRMLS_CC);
return CACHED_PTR_TO_ZVAL_PTR(cache);
} else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
repeated_field_ensure_created(field, cache PHP_PROTO_TSRMLS_CC);
return CACHED_PTR_TO_ZVAL_PTR(cache);
}
CACHED_VALUE* stored_cache = find_zval_property(header, field);
if (upb_fielddef_type(field) == UPB_TYPE_MESSAGE &&
is_wrapper_msg(upb_fielddef_msgsubdef(field))) {
zval * cached_zval = CACHED_PTR_TO_ZVAL_PTR(stored_cache);
#if PHP_MAJOR_VERSION >= 7
zend_object* obj;
#endif
if (Z_TYPE_P(cached_zval) != IS_OBJECT &&
Z_TYPE_P(cached_zval) != IS_NULL) {
// Needs to expand value to wrapper.
const upb_msgdef* submsgdef = upb_fielddef_msgsubdef(field);
const upb_fielddef* value_field = upb_msgdef_itof(submsgdef, 1);
MessageHeader* submsg;
Descriptor* subdesc =
UNBOX_HASHTABLE_VALUE(
Descriptor, get_def_obj((void*)submsgdef));
zend_class_entry* subklass = subdesc->klass;
#if PHP_MAJOR_VERSION < 7
zval* val = NULL;
MAKE_STD_ZVAL(val);
ZVAL_OBJ(val, subklass->create_object(subklass TSRMLS_CC));
submsg = UNBOX(MessageHeader, val);
#else
obj = subklass->create_object(subklass TSRMLS_CC);
submsg = (MessageHeader*)((char*)obj - XtOffsetOf(MessageHeader, std));
#endif
custom_data_init(subklass, submsg PHP_PROTO_TSRMLS_CC);
layout_set(subdesc->layout, submsg, value_field, cached_zval TSRMLS_CC);
#if PHP_MAJOR_VERSION < 7
ZVAL_ZVAL(cached_zval, val, 1, 1);
#else
ZVAL_OBJ(cached_zval, obj);
#endif
}
if (stored_cache != cache) {
ZVAL_ZVAL(CACHED_PTR_TO_ZVAL_PTR(cache), cached_zval, 1, 0);
}
} else {
upb_fieldtype_t type = upb_fielddef_type(field);
native_slot_get(type, value_memory(type, memory, cache),
native_slot_get(type, value_memory(type, memory, stored_cache),
cache TSRMLS_CC);
return CACHED_PTR_TO_ZVAL_PTR(cache);
}
return CACHED_PTR_TO_ZVAL_PTR(cache);
}
void layout_set(MessageLayout* layout, MessageHeader* header,
@ -825,33 +862,6 @@ void layout_set(MessageLayout* layout, MessageHeader* header,
uint32_t* oneof_case = slot_oneof_case(layout, storage, field);
if (upb_fielddef_containingoneof(field)) {
upb_fieldtype_t type = upb_fielddef_type(field);
zend_class_entry *ce = NULL;
// For non-singular fields, the related memory needs to point to the actual
// zval in properties table first.
switch (type) {
case UPB_TYPE_MESSAGE: {
const upb_msgdef* msg = upb_fielddef_msgsubdef(field);
Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj(msg));
ce = desc->klass;
// Intentionally fall through.
}
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES: {
int property_cache_index =
header->descriptor->layout->fields[upb_fielddef_index(field)]
.cache_index;
DEREF(memory, CACHED_VALUE*) =
OBJ_PROP(&header->std, property_cache_index);
memory = DEREF(memory, CACHED_VALUE*);
break;
}
default:
break;
}
native_slot_set(type, ce, memory, val TSRMLS_CC);
*oneof_case = upb_fielddef_number(field);
} else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
// Works for both repeated and map fields
@ -895,19 +905,20 @@ void layout_set(MessageLayout* layout, MessageHeader* header,
#endif
zval_dtor(&converted_value);
}
} else {
upb_fieldtype_t type = upb_fielddef_type(field);
zend_class_entry *ce = NULL;
if (type == UPB_TYPE_MESSAGE) {
const upb_msgdef* msg = upb_fielddef_msgsubdef(field);
Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj(msg));
ce = desc->klass;
}
CACHED_VALUE* cache = find_zval_property(header, field);
native_slot_set(
type, ce, value_memory(upb_fielddef_type(field), memory, cache),
val TSRMLS_CC);
return;
}
upb_fieldtype_t type = upb_fielddef_type(field);
zend_class_entry *ce = NULL;
if (type == UPB_TYPE_MESSAGE) {
const upb_msgdef* msg = upb_fielddef_msgsubdef(field);
Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj(msg));
ce = desc->klass;
}
CACHED_VALUE* cache = find_zval_property(header, field);
native_slot_set(
type, ce, value_memory(upb_fielddef_type(field), memory, cache),
val TSRMLS_CC);
}
static void native_slot_merge(

@ -116,7 +116,7 @@ int msvc_vsnprintf(char* s, size_t n, const char* format, va_list arg);
#ifdef __cplusplus
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) || \
(defined(_MSC_VER) && _MSC_VER >= 1900)
// C++11 is present
/* C++11 is present */
#else
#error upb requires C++11 for C++ support
#endif
@ -724,6 +724,7 @@ static bool upb_decode_field(upb_decstate *d, upb_decframe *frame) {
CHK(upb_append_unknown(d, frame));
return true;
}
UPB_UNREACHABLE();
}
static bool upb_decode_message(upb_decstate *d, char *msg, const upb_msglayout *l) {
@ -7153,7 +7154,8 @@ size_t run_decoder_vm(upb_pbdecoder *d, const mgroup *group,
CHECK_SUSPEND(upb_sink_startsubmsg(outer->sink, arg, &d->top->sink));
)
VMCASE(OP_ENDSUBMSG,
CHECK_SUSPEND(upb_sink_endsubmsg(d->top->sink, arg));
upb_sink subsink = (d->top + 1)->sink;
CHECK_SUSPEND(upb_sink_endsubmsg(d->top->sink, subsink, arg));
)
VMCASE(OP_STARTSTR,
uint32_t len = delim_remaining(d);
@ -8430,7 +8432,7 @@ done:
return r;
}
#line 1 "upb/json/parser.rl"
// #line 1 "upb/json/parser.rl"
/*
** upb::json::Parser (upb_json_parser)
**
@ -10117,28 +10119,18 @@ static void start_timestamp_zone(upb_json_parser *p, const char *ptr) {
capture_begin(p, ptr);
}
#define EPOCH_YEAR 1970
#define TM_YEAR_BASE 1900
static bool isleap(int year) {
return (year % 4) == 0 && (year % 100 != 0 || (year % 400) == 0);
static int div_round_up2(int n, int d) {
return (n + d - 1) / d;
}
const unsigned short int __mon_yday[2][13] = {
/* Normal years. */
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
/* Leap years. */
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
};
/* epoch_days(1970, 1, 1) == 1970-01-01 == 0. */
static int epoch_days(int year, int month, int day) {
static const uint16_t month_yday[12] = {0, 31, 59, 90, 120, 151,
181, 212, 243, 273, 304, 334};
int febs_since_0 = month > 2 ? year + 1 : year;
int leap_days_since_0 = div_round_up(febs_since_0, 4) -
div_round_up(febs_since_0, 100) +
div_round_up(febs_since_0, 400);
int leap_days_since_0 = div_round_up2(febs_since_0, 4) -
div_round_up2(febs_since_0, 100) +
div_round_up2(febs_since_0, 400);
int days_since_0 =
365 * year + month_yday[month - 1] + (day - 1) + leap_days_since_0;
@ -10460,7 +10452,7 @@ static void end_member(upb_json_parser *p) {
p->top--;
ok = upb_handlers_getselector(mapfield, UPB_HANDLER_ENDSUBMSG, &sel);
UPB_ASSERT(ok);
upb_sink_endsubmsg(p->top->sink, sel);
upb_sink_endsubmsg(p->top->sink, (p->top + 1)->sink, sel);
}
p->top->f = NULL;
@ -10574,7 +10566,7 @@ static void end_subobject(upb_json_parser *p) {
p->top--;
if (!is_unknown) {
sel = getsel_for_handlertype(p, UPB_HANDLER_ENDSUBMSG);
upb_sink_endsubmsg(p->top->sink, sel);
upb_sink_endsubmsg(p->top->sink, (p->top + 1)->sink, sel);
}
}
}
@ -11013,11 +11005,11 @@ static bool does_fieldmask_end(upb_json_parser *p) {
* final state once, when the closing '"' is seen. */
#line 2794 "upb/json/parser.rl"
// #line 2780 "upb/json/parser.rl"
#line 2597 "upb/json/parser.c"
// #line 2583 "upb/json/parser.c"
static const char _json_actions[] = {
0, 1, 0, 1, 1, 1, 3, 1,
4, 1, 6, 1, 7, 1, 8, 1,
@ -11272,7 +11264,7 @@ static const int json_en_value_machine = 78;
static const int json_en_main = 1;
#line 2797 "upb/json/parser.rl"
// #line 2783 "upb/json/parser.rl"
size_t parse(void *closure, const void *hd, const char *buf, size_t size,
const upb_bufhandle *handle) {
@ -11295,7 +11287,7 @@ size_t parse(void *closure, const void *hd, const char *buf, size_t size,
capture_resume(parser, buf);
#line 2875 "upb/json/parser.c"
// #line 2861 "upb/json/parser.c"
{
int _klen;
unsigned int _trans;
@ -11370,147 +11362,147 @@ _match:
switch ( *_acts++ )
{
case 1:
#line 2602 "upb/json/parser.rl"
// #line 2588 "upb/json/parser.rl"
{ p--; {cs = stack[--top]; goto _again;} }
break;
case 2:
#line 2604 "upb/json/parser.rl"
// #line 2590 "upb/json/parser.rl"
{ p--; {stack[top++] = cs; cs = 23;goto _again;} }
break;
case 3:
#line 2608 "upb/json/parser.rl"
// #line 2594 "upb/json/parser.rl"
{ start_text(parser, p); }
break;
case 4:
#line 2609 "upb/json/parser.rl"
// #line 2595 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(end_text(parser, p)); }
break;
case 5:
#line 2615 "upb/json/parser.rl"
// #line 2601 "upb/json/parser.rl"
{ start_hex(parser); }
break;
case 6:
#line 2616 "upb/json/parser.rl"
// #line 2602 "upb/json/parser.rl"
{ hexdigit(parser, p); }
break;
case 7:
#line 2617 "upb/json/parser.rl"
// #line 2603 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(end_hex(parser)); }
break;
case 8:
#line 2623 "upb/json/parser.rl"
// #line 2609 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(escape(parser, p)); }
break;
case 9:
#line 2629 "upb/json/parser.rl"
// #line 2615 "upb/json/parser.rl"
{ p--; {cs = stack[--top]; goto _again;} }
break;
case 10:
#line 2634 "upb/json/parser.rl"
// #line 2620 "upb/json/parser.rl"
{ start_year(parser, p); }
break;
case 11:
#line 2635 "upb/json/parser.rl"
// #line 2621 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(end_year(parser, p)); }
break;
case 12:
#line 2639 "upb/json/parser.rl"
// #line 2625 "upb/json/parser.rl"
{ start_month(parser, p); }
break;
case 13:
#line 2640 "upb/json/parser.rl"
// #line 2626 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(end_month(parser, p)); }
break;
case 14:
#line 2644 "upb/json/parser.rl"
// #line 2630 "upb/json/parser.rl"
{ start_day(parser, p); }
break;
case 15:
#line 2645 "upb/json/parser.rl"
// #line 2631 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(end_day(parser, p)); }
break;
case 16:
#line 2649 "upb/json/parser.rl"
// #line 2635 "upb/json/parser.rl"
{ start_hour(parser, p); }
break;
case 17:
#line 2650 "upb/json/parser.rl"
// #line 2636 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(end_hour(parser, p)); }
break;
case 18:
#line 2654 "upb/json/parser.rl"
// #line 2640 "upb/json/parser.rl"
{ start_minute(parser, p); }
break;
case 19:
#line 2655 "upb/json/parser.rl"
// #line 2641 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(end_minute(parser, p)); }
break;
case 20:
#line 2659 "upb/json/parser.rl"
// #line 2645 "upb/json/parser.rl"
{ start_second(parser, p); }
break;
case 21:
#line 2660 "upb/json/parser.rl"
// #line 2646 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(end_second(parser, p)); }
break;
case 22:
#line 2665 "upb/json/parser.rl"
// #line 2651 "upb/json/parser.rl"
{ start_duration_base(parser, p); }
break;
case 23:
#line 2666 "upb/json/parser.rl"
// #line 2652 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(end_duration_base(parser, p)); }
break;
case 24:
#line 2668 "upb/json/parser.rl"
// #line 2654 "upb/json/parser.rl"
{ p--; {cs = stack[--top]; goto _again;} }
break;
case 25:
#line 2673 "upb/json/parser.rl"
// #line 2659 "upb/json/parser.rl"
{ start_timestamp_base(parser); }
break;
case 26:
#line 2675 "upb/json/parser.rl"
// #line 2661 "upb/json/parser.rl"
{ start_timestamp_fraction(parser, p); }
break;
case 27:
#line 2676 "upb/json/parser.rl"
// #line 2662 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(end_timestamp_fraction(parser, p)); }
break;
case 28:
#line 2678 "upb/json/parser.rl"
// #line 2664 "upb/json/parser.rl"
{ start_timestamp_zone(parser, p); }
break;
case 29:
#line 2679 "upb/json/parser.rl"
// #line 2665 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(end_timestamp_zone(parser, p)); }
break;
case 30:
#line 2681 "upb/json/parser.rl"
// #line 2667 "upb/json/parser.rl"
{ p--; {cs = stack[--top]; goto _again;} }
break;
case 31:
#line 2686 "upb/json/parser.rl"
// #line 2672 "upb/json/parser.rl"
{ start_fieldmask_path_text(parser, p); }
break;
case 32:
#line 2687 "upb/json/parser.rl"
// #line 2673 "upb/json/parser.rl"
{ end_fieldmask_path_text(parser, p); }
break;
case 33:
#line 2692 "upb/json/parser.rl"
// #line 2678 "upb/json/parser.rl"
{ start_fieldmask_path(parser); }
break;
case 34:
#line 2693 "upb/json/parser.rl"
// #line 2679 "upb/json/parser.rl"
{ end_fieldmask_path(parser); }
break;
case 35:
#line 2699 "upb/json/parser.rl"
// #line 2685 "upb/json/parser.rl"
{ p--; {cs = stack[--top]; goto _again;} }
break;
case 36:
#line 2704 "upb/json/parser.rl"
// #line 2690 "upb/json/parser.rl"
{
if (is_wellknown_msg(parser, UPB_WELLKNOWN_TIMESTAMP)) {
{stack[top++] = cs; cs = 47;goto _again;}
@ -11524,11 +11516,11 @@ _match:
}
break;
case 37:
#line 2717 "upb/json/parser.rl"
// #line 2703 "upb/json/parser.rl"
{ p--; {stack[top++] = cs; cs = 78;goto _again;} }
break;
case 38:
#line 2722 "upb/json/parser.rl"
// #line 2708 "upb/json/parser.rl"
{
if (is_wellknown_msg(parser, UPB_WELLKNOWN_ANY)) {
start_any_member(parser, p);
@ -11538,11 +11530,11 @@ _match:
}
break;
case 39:
#line 2729 "upb/json/parser.rl"
// #line 2715 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(end_membername(parser)); }
break;
case 40:
#line 2732 "upb/json/parser.rl"
// #line 2718 "upb/json/parser.rl"
{
if (is_wellknown_msg(parser, UPB_WELLKNOWN_ANY)) {
end_any_member(parser, p);
@ -11552,7 +11544,7 @@ _match:
}
break;
case 41:
#line 2743 "upb/json/parser.rl"
// #line 2729 "upb/json/parser.rl"
{
if (is_wellknown_msg(parser, UPB_WELLKNOWN_ANY)) {
start_any_object(parser, p);
@ -11562,7 +11554,7 @@ _match:
}
break;
case 42:
#line 2752 "upb/json/parser.rl"
// #line 2738 "upb/json/parser.rl"
{
if (is_wellknown_msg(parser, UPB_WELLKNOWN_ANY)) {
CHECK_RETURN_TOP(end_any_object(parser, p));
@ -11572,54 +11564,54 @@ _match:
}
break;
case 43:
#line 2764 "upb/json/parser.rl"
// #line 2750 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(start_array(parser)); }
break;
case 44:
#line 2768 "upb/json/parser.rl"
// #line 2754 "upb/json/parser.rl"
{ end_array(parser); }
break;
case 45:
#line 2773 "upb/json/parser.rl"
// #line 2759 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(start_number(parser, p)); }
break;
case 46:
#line 2774 "upb/json/parser.rl"
// #line 2760 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(end_number(parser, p)); }
break;
case 47:
#line 2776 "upb/json/parser.rl"
// #line 2762 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(start_stringval(parser)); }
break;
case 48:
#line 2777 "upb/json/parser.rl"
// #line 2763 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(end_stringval(parser)); }
break;
case 49:
#line 2779 "upb/json/parser.rl"
// #line 2765 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(end_bool(parser, true)); }
break;
case 50:
#line 2781 "upb/json/parser.rl"
// #line 2767 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(end_bool(parser, false)); }
break;
case 51:
#line 2783 "upb/json/parser.rl"
// #line 2769 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(end_null(parser)); }
break;
case 52:
#line 2785 "upb/json/parser.rl"
// #line 2771 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(start_subobject_full(parser)); }
break;
case 53:
#line 2786 "upb/json/parser.rl"
// #line 2772 "upb/json/parser.rl"
{ end_subobject_full(parser); }
break;
case 54:
#line 2791 "upb/json/parser.rl"
// #line 2777 "upb/json/parser.rl"
{ p--; {cs = stack[--top]; goto _again;} }
break;
#line 3199 "upb/json/parser.c"
// #line 3185 "upb/json/parser.c"
}
}
@ -11636,32 +11628,32 @@ _again:
while ( __nacts-- > 0 ) {
switch ( *__acts++ ) {
case 0:
#line 2600 "upb/json/parser.rl"
// #line 2586 "upb/json/parser.rl"
{ p--; {cs = stack[--top]; if ( p == pe )
goto _test_eof;
goto _again;} }
break;
case 46:
#line 2774 "upb/json/parser.rl"
// #line 2760 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(end_number(parser, p)); }
break;
case 49:
#line 2779 "upb/json/parser.rl"
// #line 2765 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(end_bool(parser, true)); }
break;
case 50:
#line 2781 "upb/json/parser.rl"
// #line 2767 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(end_bool(parser, false)); }
break;
case 51:
#line 2783 "upb/json/parser.rl"
// #line 2769 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(end_null(parser)); }
break;
case 53:
#line 2786 "upb/json/parser.rl"
// #line 2772 "upb/json/parser.rl"
{ end_subobject_full(parser); }
break;
#line 3241 "upb/json/parser.c"
// #line 3227 "upb/json/parser.c"
}
}
}
@ -11669,7 +11661,7 @@ goto _again;} }
_out: {}
}
#line 2819 "upb/json/parser.rl"
// #line 2805 "upb/json/parser.rl"
if (p != pe) {
upb_status_seterrf(parser->status, "Parse error at '%.*s'\n", pe - p, p);
@ -11712,13 +11704,13 @@ static void json_parser_reset(upb_json_parser *p) {
/* Emit Ragel initialization of the parser. */
#line 3292 "upb/json/parser.c"
// #line 3278 "upb/json/parser.c"
{
cs = json_start;
top = 0;
}
#line 2861 "upb/json/parser.rl"
// #line 2847 "upb/json/parser.rl"
p->current_state = cs;
p->parser_top = top;
accumulate_clear(p);

@ -122,7 +122,7 @@ int msvc_vsnprintf(char* s, size_t n, const char* format, va_list arg);
#ifdef __cplusplus
#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) || \
(defined(_MSC_VER) && _MSC_VER >= 1900)
// C++11 is present
/* C++11 is present */
#else
#error upb requires C++11 for C++ support
#endif
@ -5700,15 +5700,16 @@ UPB_INLINE bool upb_sink_startsubmsg(upb_sink s, upb_selector_t sel,
return sub->closure ? true : false;
}
UPB_INLINE bool upb_sink_endsubmsg(upb_sink s, upb_selector_t sel) {
UPB_INLINE bool upb_sink_endsubmsg(upb_sink s, upb_sink sub,
upb_selector_t sel) {
typedef upb_endfield_handlerfunc func;
func *endsubmsg;
const void *hd;
if (!s.handlers) return true;
endsubmsg = (func*)upb_handlers_gethandler(s.handlers, sel, &hd);
if (!endsubmsg) return s.closure;
return endsubmsg(s.closure, hd);
if (!endsubmsg) return true;
return endsubmsg(sub.closure, hd);
}
#ifdef __cplusplus
@ -5874,8 +5875,8 @@ class upb::Sink {
return ret;
}
bool EndSubMessage(HandlersPtr::Selector s) {
return upb_sink_endsubmsg(sink_, s);
bool EndSubMessage(HandlersPtr::Selector s, Sink sub) {
return upb_sink_endsubmsg(sink_, sub.sink_, s);
}
/* For repeated fields of any type, the sequence of values must be wrapped in

@ -175,6 +175,42 @@ class Message
}
}
protected function readWrapperValue($member)
{
$field = $this->desc->getFieldByName($member);
$oneof_index = $field->getOneofIndex();
if ($oneof_index === -1) {
$wrapper = $this->$member;
} else {
$wrapper = $this->readOneof($field->getNumber());
}
if (is_null($wrapper)) {
return NULL;
} else {
return $wrapper->getValue();
}
}
protected function writeWrapperValue($member, $value)
{
$field = $this->desc->getFieldByName($member);
$wrapped_value = $value;
if (!is_null($value)) {
$desc = $field->getMessageType();
$klass = $desc->getClass();
$wrapped_value = new $klass;
$wrapped_value->setValue($value);
}
$oneof_index = $field->getOneofIndex();
if ($oneof_index === -1) {
$this->$member = $wrapped_value;
} else {
$this->writeOneof($field->getNumber(), $wrapped_value);
}
}
protected function readOneof($number)
{
$field = $this->desc->getFieldByNumber($number);

@ -5,7 +5,13 @@ require_once('test_util.php');
use Google\Protobuf\RepeatedField;
use Google\Protobuf\GPBType;
use Foo\TestInt32Value;
use Foo\TestInt64Value;
use Foo\TestUInt32Value;
use Foo\TestUInt64Value;
use Foo\TestBoolValue;
use Foo\TestStringValue;
use Foo\TestBytesValue;
use Foo\TestAny;
use Foo\TestEnum;
use Foo\TestMessage;
@ -98,6 +104,13 @@ class EncodeDecodeTest extends TestBase
$this->assertSame("1", $m->serializeToJsonString());
}
public function testDecodeRepeatedInt32Value()
{
$m = new TestInt32Value();
$m->mergeFromJsonString("{\"repeated_field\":[12345]}");
$this->assertSame(12345, $m->getRepeatedField()[0]->getValue());
}
public function testDecodeTopLevelUInt32Value()
{
$m = new UInt32Value();
@ -168,12 +181,18 @@ class EncodeDecodeTest extends TestBase
$this->assertSame("\"a\"", $m->serializeToJsonString());
}
public function testEncodeStringValue()
public function testDecodeRepeatedStringValue()
{
$m = new TestStringValue();
$m->mergeFromJsonString("{\"repeated_field\":[\"a\"]}");
$this->assertSame("a", $m->getRepeatedField()[0]->getValue());
}
public function testDecodeMapStringValue()
{
$m = new TestStringValue(['field' => new StringValue(['value' => ''])]);
var_dump($m->getField());
var_dump($m->serializeToJsonString());
$this->assertSame("{\"field\":\"\"}", $m->serializeToJsonString());
$m = new TestStringValue();
$m->mergeFromJsonString("{\"map_field\":{\"1\": \"a\"}}");
$this->assertSame("a", $m->getMapField()[1]->getValue());
}
public function testDecodeTopLevelBytesValue()
@ -1230,4 +1249,195 @@ class EncodeDecodeTest extends TestBase
$this->assertTrue(true);
}
/**
* @dataProvider wrappersDataProvider
*/
public function testWrapperJsonDecodeAndGet(
$class,
$nonDefaultValue,
$nonDefaultValueData,
$defaultValue,
$defaultValueData
)
{
// Singular with non-default
$m = new $class();
$m->mergeFromJsonString("{\"field\":" . $nonDefaultValueData . "}");
$wrapper = $m->getField();
$this->assertEquals($nonDefaultValue, $wrapper->getValue());
// Singular with default
$m = new $class();
$m->mergeFromJsonString("{\"field\":" . $defaultValueData . "}");
$wrapper = $m->getField();
$this->assertEquals($defaultValue, $wrapper->getValue());
// Repeated with empty
$m = new $class();
$m->mergeFromJsonString("{\"repeated_field\":[]}");
$repeatedWrapper = $m->getRepeatedField();
$this->assertSame(0, count($repeatedWrapper));
// Repeated with non-default
$m = new $class();
$m->mergeFromJsonString("{\"repeated_field\":[" . $defaultValueData . "]}");
$repeatedWrapper = $m->getRepeatedField();
$this->assertSame(1, count($repeatedWrapper));
$this->assertEquals($defaultValue, $repeatedWrapper[0]->getValue());
// Repeated with default
$m = new $class();
$m->mergeFromJsonString("{\"repeated_field\":[" . $defaultValueData . "]}");
$repeatedWrapper = $m->getRepeatedField();
$this->assertSame(1, count($repeatedWrapper));
$this->assertEquals($defaultValue, $repeatedWrapper[0]->getValue());
// Oneof with non-default
$m = new $class();
$m->mergeFromJsonString("{\"oneof_field\":" . $nonDefaultValueData . "}");
$wrapper = $m->getOneofField();
$this->assertEquals($nonDefaultValue, $wrapper->getValue());
$this->assertEquals("oneof_field", $m->getOneofFields());
$this->assertEquals(0, $m->getInt32Field());
// Oneof with default
$m = new $class();
$m->mergeFromJsonString("{\"oneof_field\":" . $defaultValueData . "}");
$wrapper = $m->getOneofField();
$this->assertEquals($defaultValue, $wrapper->getValue());
$this->assertEquals("oneof_field", $m->getOneofFields());
$this->assertEquals(0, $m->getInt32Field());
}
/**
* @dataProvider wrappersDataProvider
*/
public function testWrapperJsonDecodeAndGetUnwrapped(
$class,
$nonDefaultValue,
$nonDefaultValueData,
$defaultValue,
$defaultValueData
)
{
// Singular with non-default
$m = new $class();
$m->mergeFromJsonString("{\"field\":" . $nonDefaultValueData . "}");
$this->assertEquals($nonDefaultValue, $m->getFieldUnwrapped());
// Singular with default
$m = new $class();
$m->mergeFromJsonString("{\"field\":" . $defaultValueData . "}");
$this->assertEquals($defaultValue, $m->getFieldUnwrapped());
// Oneof with non-default
$m = new $class();
$m->mergeFromJsonString("{\"oneof_field\":" . $nonDefaultValueData . "}");
$this->assertEquals($nonDefaultValue, $m->getOneofFieldUnwrapped());
$this->assertEquals("oneof_field", $m->getOneofFields());
$this->assertEquals(0, $m->getInt32Field());
// Oneof with default
$m = new $class();
$m->mergeFromJsonString("{\"oneof_field\":" . $defaultValueData . "}");
$this->assertEquals($defaultValue, $m->getOneofFieldUnwrapped());
$this->assertEquals("oneof_field", $m->getOneofFields());
$this->assertEquals(0, $m->getInt32Field());
}
/**
* @dataProvider wrappersDataProvider
*/
public function testWrapperJsonDecodeEncode(
$class,
$nonDefaultValue,
$nonDefaultValueData,
$defaultValue,
$defaultValueData
)
{
// Singular with non-default
$from = new $class();
$to = new $class();
$from->mergeFromJsonString("{\"field\":" . $nonDefaultValueData . "}");
$data = $from->serializeToJsonString();
$to->mergeFromJsonString($data);
$this->assertEquals($nonDefaultValue, $to->getFieldUnwrapped());
// Singular with default
$from = new $class();
$to = new $class();
$from->mergeFromJsonString("{\"field\":" . $defaultValueData . "}");
$data = $from->serializeToJsonString();
$to->mergeFromJsonString($data);
$this->assertEquals($defaultValue, $to->getFieldUnwrapped());
// Oneof with non-default
$from = new $class();
$to = new $class();
$from->mergeFromJsonString("{\"oneof_field\":" . $nonDefaultValueData . "}");
$data = $from->serializeToJsonString();
$to->mergeFromJsonString($data);
$this->assertEquals($nonDefaultValue, $to->getOneofFieldUnwrapped());
// Oneof with default
$from = new $class();
$to = new $class();
$from->mergeFromJsonString("{\"oneof_field\":" . $defaultValueData . "}");
$data = $from->serializeToJsonString();
$to->mergeFromJsonString($data);
$this->assertEquals($defaultValue, $to->getOneofFieldUnwrapped());
}
/**
* @dataProvider wrappersDataProvider
*/
public function testWrapperSetUnwrappedJsonEncode(
$class,
$nonDefaultValue,
$nonDefaultValueData,
$defaultValue,
$defaultValueData
)
{
// Singular with non-default
$from = new $class();
$to = new $class();
$from->setFieldUnwrapped($nonDefaultValue);
$data = $from->serializeToJsonString();
$to->mergeFromJsonString($data);
$this->assertEquals($nonDefaultValue, $to->getFieldUnwrapped());
// Singular with default
$from = new $class();
$to = new $class();
$from->setFieldUnwrapped($defaultValue);
$data = $from->serializeToJsonString();
$to->mergeFromJsonString($data);
$this->assertEquals($defaultValue, $to->getFieldUnwrapped());
// Oneof with non-default
$from = new $class();
$to = new $class();
$from->setOneofFieldUnwrapped($nonDefaultValue);
$data = $from->serializeToJsonString();
$to->mergeFromJsonString($data);
$this->assertEquals($nonDefaultValue, $to->getOneofFieldUnwrapped());
// Oneof with default
$from = new $class();
$to = new $class();
$from->setOneofFieldUnwrapped($defaultValue);
$data = $from->serializeToJsonString();
$to->mergeFromJsonString($data);
$this->assertEquals($defaultValue, $to->getOneofFieldUnwrapped());
}
public function wrappersDataProvider()
{
return [
[TestInt32Value::class, 1, "1", 0, "0"],
[TestStringValue::class, "a", "\"a\"", "", "\"\""],
];
}
}

@ -20,9 +20,13 @@ require_once('generated/Bar/TestLegacyMessage/NestedMessage.php');
require_once('generated/Foo/PBARRAY.php');
require_once('generated/Foo/PBEmpty.php');
require_once('generated/Foo/TestAny.php');
require_once('generated/Foo/TestBoolValue.php');
require_once('generated/Foo/TestBytesValue.php');
require_once('generated/Foo/TestEnum.php');
require_once('generated/Foo/TestIncludeNamespaceMessage.php');
require_once('generated/Foo/TestIncludePrefixMessage.php');
require_once('generated/Foo/TestInt32Value.php');
require_once('generated/Foo/TestInt64Value.php');
require_once('generated/Foo/TestMessage.php');
require_once('generated/Foo/TestMessage/PBEmpty.php');
require_once('generated/Foo/TestMessage/NestedEnum.php');
@ -32,6 +36,8 @@ require_once('generated/Foo/TestPhpDoc.php');
require_once('generated/Foo/TestRandomFieldOrder.php');
require_once('generated/Foo/TestReverseFieldOrder.php');
require_once('generated/Foo/TestStringValue.php');
require_once('generated/Foo/TestUInt32Value.php');
require_once('generated/Foo/TestUInt64Value.php');
require_once('generated/Foo/TestUnpackedMessage.php');
require_once('generated/Foo/testLowerCaseMessage.php');
require_once('generated/Foo/testLowerCaseEnum.php');

@ -215,6 +215,66 @@ message TestAny {
google.protobuf.Any any = 1;
}
message TestInt32Value {
google.protobuf.Int32Value field = 1;
repeated google.protobuf.Int32Value repeated_field = 2;
oneof oneof_fields {
google.protobuf.Int32Value oneof_field = 3;
int32 int32_field = 4;
}
}
message TestInt64Value {
google.protobuf.Int64Value field = 1;
repeated google.protobuf.Int64Value repeated_field = 2;
oneof oneof_fields {
google.protobuf.Int64Value oneof_field = 3;
int32 int32_field = 4;
}
}
message TestUInt32Value {
google.protobuf.UInt32Value field = 1;
repeated google.protobuf.UInt32Value repeated_field = 2;
oneof oneof_fields {
google.protobuf.UInt32Value oneof_field = 3;
int32 int32_field = 4;
}
}
message TestUInt64Value {
google.protobuf.UInt64Value field = 1;
repeated google.protobuf.UInt64Value repeated_field = 2;
oneof oneof_fields {
google.protobuf.UInt64Value oneof_field = 3;
int32 int32_field = 4;
}
}
message TestBoolValue {
google.protobuf.BoolValue field = 1;
repeated google.protobuf.BoolValue repeated_field = 2;
oneof oneof_fields {
google.protobuf.BoolValue oneof_field = 3;
int32 int32_field = 4;
}
}
message TestStringValue {
google.protobuf.StringValue field = 1;
repeated google.protobuf.StringValue repeated_field = 2;
oneof oneof_fields {
google.protobuf.StringValue oneof_field = 3;
int32 int32_field = 4;
}
map<int32, google.protobuf.StringValue> map_field = 5;
}
message TestBytesValue {
google.protobuf.BytesValue field = 1;
repeated google.protobuf.BytesValue repeated_field = 2;
oneof oneof_fields {
google.protobuf.BytesValue oneof_field = 3;
int32 int32_field = 4;
}
}

@ -132,7 +132,8 @@ class WrapperTypeSettersTest extends TestBase
[2.2, new DoubleValue(["value" => 2.2])],
[null, null],
[0, new DoubleValue()],
]],[TestWrapperSetters::class, StringValue::class, "setStringValueOneof", "setStringValueOneofUnwrapped", "getStringValueOneof", "getStringValueOneofUnwrapped", [
]],
[TestWrapperSetters::class, StringValue::class, "setStringValueOneof", "setStringValueOneofUnwrapped", "getStringValueOneof", "getStringValueOneofUnwrapped", [
["asdf", new StringValue(["value" => "asdf"])],
["", new StringValue(["value" => ""])],
[null, null],

@ -628,7 +628,7 @@ void GenerateField(const FieldDescriptor* field, io::Printer* printer,
} else {
GenerateFieldDocComment(printer, field, is_descriptor, kFieldProperty);
printer->Print(
"private $^name^ = ^default^;\n",
"protected $^name^ = ^default^;\n",
"name", field->name(),
"default", DefaultForField(field));
}
@ -682,10 +682,10 @@ void GenerateFieldAccessor(const FieldDescriptor* field, bool is_descriptor,
printer->Print(
"public function get^camel_name^Unwrapped()\n"
"{\n"
" $wrapper = $this->get^camel_name^();\n"
" return is_null($wrapper) ? null : $wrapper->getValue();\n"
" return $this->readWrapperValue(\"^field_name^\");\n"
"}\n\n",
"camel_name", UnderscoresToCamelCase(field->name(), true));
"camel_name", UnderscoresToCamelCase(field->name(), true),
"field_name", field->name());
}
// Generate setter.
@ -795,11 +795,11 @@ void GenerateFieldAccessor(const FieldDescriptor* field, bool is_descriptor,
printer->Print(
"public function set^camel_name^Unwrapped($var)\n"
"{\n"
" $wrappedVar = is_null($var) ? null : new \\^wrapper_type^(['value' => $var]);\n"
" return $this->set^camel_name^($wrappedVar);\n"
" $this->writeWrapperValue(\"^field_name^\", $var);\n"
" return $this;"
"}\n\n",
"camel_name", UnderscoresToCamelCase(field->name(), true),
"wrapper_type", LegacyFullClassName(field->message_type(), is_descriptor));
"field_name", field->name());
}
// Generate has method for proto2 only.

Loading…
Cancel
Save