OLD | NEW |
1 // Protocol Buffers - Google's data interchange format | 1 // Protocol Buffers - Google's data interchange format |
2 // Copyright 2014 Google Inc. All rights reserved. | 2 // Copyright 2014 Google Inc. All rights reserved. |
3 // https://developers.google.com/protocol-buffers/ | 3 // https://developers.google.com/protocol-buffers/ |
4 // | 4 // |
5 // Redistribution and use in source and binary forms, with or without | 5 // Redistribution and use in source and binary forms, with or without |
6 // modification, are permitted provided that the following conditions are | 6 // modification, are permitted provided that the following conditions are |
7 // met: | 7 // met: |
8 // | 8 // |
9 // * Redistributions of source code must retain the above copyright | 9 // * Redistributions of source code must retain the above copyright |
10 // notice, this list of conditions and the following disclaimer. | 10 // notice, this list of conditions and the following disclaimer. |
(...skipping 11 matching lines...) Expand all Loading... |
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
30 | 30 |
31 #include <php.h> | 31 #include <php.h> |
| 32 #include <stdlib.h> |
32 | 33 |
33 #include "protobuf.h" | 34 #include "protobuf.h" |
34 | 35 |
35 // ----------------------------------------------------------------------------- | 36 static zend_class_entry* message_type; |
36 // Class/module creation from msgdefs and enumdefs, respectively. | 37 zend_object_handlers* message_handlers; |
37 // ----------------------------------------------------------------------------- | 38 |
38 | 39 static zend_function_entry message_methods[] = { |
39 void* message_data(void* msg) { | 40 PHP_ME(Message, encode, NULL, ZEND_ACC_PUBLIC) |
40 return ((uint8_t *)msg) + sizeof(MessageHeader); | 41 PHP_ME(Message, decode, NULL, ZEND_ACC_PUBLIC) |
41 } | 42 PHP_ME(Message, readOneof, NULL, ZEND_ACC_PROTECTED) |
42 | 43 PHP_ME(Message, writeOneof, NULL, ZEND_ACC_PROTECTED) |
43 void message_set_property(zval* object, zval* field_name, zval* value, | 44 PHP_ME(Message, __construct, NULL, ZEND_ACC_PROTECTED) |
44 const zend_literal* key TSRMLS_DC) { | 45 {NULL, NULL, NULL} |
| 46 }; |
| 47 |
| 48 // Forward declare static functions. |
| 49 |
| 50 static void message_set_property(zval* object, zval* member, zval* value, |
| 51 const zend_literal* key TSRMLS_DC); |
| 52 static zval* message_get_property(zval* object, zval* member, int type, |
| 53 const zend_literal* key TSRMLS_DC); |
| 54 static zval** message_get_property_ptr_ptr(zval* object, zval* member, int type, |
| 55 const zend_literal* key TSRMLS_DC); |
| 56 |
| 57 static zend_object_value message_create(zend_class_entry* ce TSRMLS_DC); |
| 58 static void message_free(void* object TSRMLS_DC); |
| 59 |
| 60 // ----------------------------------------------------------------------------- |
| 61 // PHP Message Handlers |
| 62 // ----------------------------------------------------------------------------- |
| 63 |
| 64 void message_init(TSRMLS_D) { |
| 65 zend_class_entry class_type; |
| 66 INIT_CLASS_ENTRY(class_type, "Google\\Protobuf\\Internal\\Message", |
| 67 message_methods); |
| 68 message_type = zend_register_internal_class(&class_type TSRMLS_CC); |
| 69 |
| 70 message_handlers = PEMALLOC(zend_object_handlers); |
| 71 memcpy(message_handlers, zend_get_std_object_handlers(), |
| 72 sizeof(zend_object_handlers)); |
| 73 message_handlers->write_property = message_set_property; |
| 74 message_handlers->read_property = message_get_property; |
| 75 message_handlers->get_property_ptr_ptr = message_get_property_ptr_ptr; |
| 76 } |
| 77 |
| 78 static void message_set_property(zval* object, zval* member, zval* value, |
| 79 const zend_literal* key TSRMLS_DC) { |
| 80 if (Z_TYPE_P(member) != IS_STRING) { |
| 81 zend_error(E_USER_ERROR, "Unexpected type for field name"); |
| 82 return; |
| 83 } |
| 84 |
| 85 if (Z_OBJCE_P(object) != EG(scope)) { |
| 86 // User cannot set property directly (e.g., $m->a = 1) |
| 87 zend_error(E_USER_ERROR, "Cannot access private property."); |
| 88 return; |
| 89 } |
| 90 |
45 const upb_fielddef* field; | 91 const upb_fielddef* field; |
46 | 92 |
47 MessageHeader* self = zend_object_store_get_object(object TSRMLS_CC); | 93 MessageHeader* self = zend_object_store_get_object(object TSRMLS_CC); |
48 | 94 |
49 CHECK_TYPE(field_name, IS_STRING); | 95 field = upb_msgdef_ntofz(self->descriptor->msgdef, Z_STRVAL_P(member)); |
50 field = upb_msgdef_ntofz(self->descriptor->msgdef, Z_STRVAL_P(field_name)); | |
51 if (field == NULL) { | 96 if (field == NULL) { |
52 zend_error(E_ERROR, "Unknown field: %s", Z_STRVAL_P(field_name)); | 97 zend_error(E_USER_ERROR, "Unknown field: %s", Z_STRVAL_P(member)); |
53 } | 98 } |
54 layout_set(self->descriptor->layout, message_data(self), field, value); | 99 |
55 } | 100 layout_set(self->descriptor->layout, self, field, value TSRMLS_CC); |
56 | 101 } |
57 zval* message_get_property(zval* object, zval* member, int type, | 102 |
58 const zend_literal* key TSRMLS_DC) { | 103 static zval* message_get_property(zval* object, zval* member, int type, |
| 104 const zend_literal* key TSRMLS_DC) { |
| 105 if (Z_TYPE_P(member) != IS_STRING) { |
| 106 zend_error(E_USER_ERROR, "Property name has to be a string."); |
| 107 return EG(uninitialized_zval_ptr); |
| 108 } |
| 109 |
| 110 if (Z_OBJCE_P(object) != EG(scope)) { |
| 111 // User cannot get property directly (e.g., $a = $m->a) |
| 112 zend_error(E_USER_ERROR, "Cannot access private property."); |
| 113 return EG(uninitialized_zval_ptr); |
| 114 } |
| 115 |
| 116 zend_property_info* property_info = NULL; |
| 117 |
| 118 // All properties should have been declared in the generated code and have |
| 119 // corresponding zvals in properties_table. |
| 120 ulong h = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member) + 1); |
| 121 if (zend_hash_quick_find(&Z_OBJCE_P(object)->properties_info, |
| 122 Z_STRVAL_P(member), Z_STRLEN_P(member) + 1, h, |
| 123 (void**)&property_info) != SUCCESS) { |
| 124 zend_error(E_USER_ERROR, "Property does not exist."); |
| 125 return EG(uninitialized_zval_ptr); |
| 126 } |
| 127 |
59 MessageHeader* self = | 128 MessageHeader* self = |
60 (MessageHeader*)zend_object_store_get_object(object TSRMLS_CC); | 129 (MessageHeader*)zend_object_store_get_object(object TSRMLS_CC); |
61 CHECK_TYPE(member, IS_STRING); | |
62 | 130 |
63 const upb_fielddef* field; | 131 const upb_fielddef* field; |
64 field = upb_msgdef_ntofz(self->descriptor->msgdef, Z_STRVAL_P(member)); | 132 field = upb_msgdef_ntofz(self->descriptor->msgdef, Z_STRVAL_P(member)); |
65 if (field == NULL) { | 133 if (field == NULL) { |
66 return EG(uninitialized_zval_ptr); | 134 return EG(uninitialized_zval_ptr); |
67 } | 135 } |
68 zval* retval = layout_get(self->descriptor->layout, message_data(self), field
TSRMLS_CC); | 136 return layout_get( |
69 return retval; | 137 self->descriptor->layout, message_data(self), field, |
70 } | 138 &Z_OBJ_P(object)->properties_table[property_info->offset] TSRMLS_CC); |
71 | 139 } |
72 static zend_function_entry message_methods[] = { | 140 |
73 PHP_ME(Message, encode, NULL, ZEND_ACC_PUBLIC) | 141 static zval** message_get_property_ptr_ptr(zval* object, zval* member, int type, |
74 {NULL, NULL, NULL} | 142 const zend_literal* key TSRMLS_DC) { |
75 }; | 143 return NULL; |
76 | 144 } |
77 /* stringsink *****************************************************************/ | 145 |
78 | 146 // ----------------------------------------------------------------------------- |
79 // This should probably be factored into a common upb component. | 147 // C Message Utilities |
80 | 148 // ----------------------------------------------------------------------------- |
81 typedef struct { | 149 |
82 upb_byteshandler handler; | 150 void* message_data(void* msg) { |
83 upb_bytessink sink; | 151 return ((uint8_t*)msg) + sizeof(MessageHeader); |
84 char *ptr; | 152 } |
85 size_t len, size; | 153 |
86 } stringsink; | 154 static void message_free(void* object TSRMLS_DC) { |
87 | 155 MessageHeader* msg = (MessageHeader*)object; |
88 static void *stringsink_start(void *_sink, const void *hd, size_t size_hint) { | 156 int i; |
89 stringsink *sink = _sink; | 157 |
90 sink->len = 0; | 158 for (i = 0; i < msg->std.ce->default_properties_count; i++) { |
91 return sink; | 159 zval_ptr_dtor(&msg->std.properties_table[i]); |
92 } | 160 } |
93 | 161 efree(msg->std.properties_table); |
94 static size_t stringsink_string(void *_sink, const void *hd, const char *ptr, | 162 efree(msg); |
95 size_t len, const upb_bufhandle *handle) { | 163 } |
96 stringsink *sink = _sink; | 164 |
97 size_t new_size = sink->size; | 165 static zend_object_value message_create(zend_class_entry* ce TSRMLS_DC) { |
98 | |
99 UPB_UNUSED(hd); | |
100 UPB_UNUSED(handle); | |
101 | |
102 while (sink->len + len > new_size) { | |
103 new_size *= 2; | |
104 } | |
105 | |
106 if (new_size != sink->size) { | |
107 sink->ptr = realloc(sink->ptr, new_size); | |
108 sink->size = new_size; | |
109 } | |
110 | |
111 memcpy(sink->ptr + sink->len, ptr, len); | |
112 sink->len += len; | |
113 | |
114 return len; | |
115 } | |
116 | |
117 void stringsink_init(stringsink *sink) { | |
118 upb_byteshandler_init(&sink->handler); | |
119 upb_byteshandler_setstartstr(&sink->handler, stringsink_start, NULL); | |
120 upb_byteshandler_setstring(&sink->handler, stringsink_string, NULL); | |
121 | |
122 upb_bytessink_reset(&sink->sink, &sink->handler, sink); | |
123 | |
124 sink->size = 32; | |
125 sink->ptr = malloc(sink->size); | |
126 sink->len = 0; | |
127 } | |
128 | |
129 void stringsink_uninit(stringsink *sink) { free(sink->ptr); } | |
130 | |
131 // Stack-allocated context during an encode/decode operation. Contains the upb | |
132 // environment and its stack-based allocator, an initial buffer for allocations | |
133 // to avoid malloc() when possible, and a template for PHP exception messages | |
134 // if any error occurs. | |
135 #define STACK_ENV_STACKBYTES 4096 | |
136 typedef struct { | |
137 upb_env env; | |
138 upb_seededalloc alloc; | |
139 const char *php_error_template; | |
140 char allocbuf[STACK_ENV_STACKBYTES]; | |
141 } stackenv; | |
142 | |
143 static void stackenv_init(stackenv* se, const char* errmsg); | |
144 static void stackenv_uninit(stackenv* se); | |
145 | |
146 // Callback invoked by upb if any error occurs during parsing or serialization. | |
147 static bool env_error_func(void* ud, const upb_status* status) { | |
148 stackenv* se = ud; | |
149 // Free the env -- rb_raise will longjmp up the stack past the encode/decode | |
150 // function so it would not otherwise have been freed. | |
151 stackenv_uninit(se); | |
152 | |
153 // TODO(teboring): have a way to verify that this is actually a parse error, | |
154 // instead of just throwing "parse error" unconditionally. | |
155 zend_error(E_ERROR, se->php_error_template); | |
156 // Never reached. | |
157 return false; | |
158 } | |
159 | |
160 static void stackenv_init(stackenv* se, const char* errmsg) { | |
161 se->php_error_template = errmsg; | |
162 upb_env_init(&se->env); | |
163 upb_seededalloc_init(&se->alloc, &se->allocbuf, STACK_ENV_STACKBYTES); | |
164 upb_env_setallocfunc(&se->env, upb_seededalloc_getallocfunc(&se->alloc), | |
165 &se->alloc); | |
166 upb_env_seterrorfunc(&se->env, env_error_func, se); | |
167 } | |
168 | |
169 static void stackenv_uninit(stackenv* se) { | |
170 upb_env_uninit(&se->env); | |
171 upb_seededalloc_uninit(&se->alloc); | |
172 } | |
173 | |
174 // ----------------------------------------------------------------------------- | |
175 // Message | |
176 // ----------------------------------------------------------------------------- | |
177 | |
178 static const upb_handlers* msgdef_pb_serialize_handlers(Descriptor* desc) { | |
179 if (desc->pb_serialize_handlers == NULL) { | |
180 desc->pb_serialize_handlers = | |
181 upb_pb_encoder_newhandlers(desc->msgdef, &desc->pb_serialize_handlers); | |
182 } | |
183 return desc->pb_serialize_handlers; | |
184 } | |
185 | |
186 PHP_METHOD(Message, encode) { | |
187 Descriptor* desc = (Descriptor*)zend_object_store_get_object( | |
188 CE_STATIC_MEMBERS(Z_OBJCE_P(getThis()))[0] TSRMLS_CC); | |
189 | |
190 stringsink sink; | |
191 stringsink_init(&sink); | |
192 | |
193 { | |
194 const upb_handlers* serialize_handlers = msgdef_pb_serialize_handlers(desc); | |
195 | |
196 stackenv se; | |
197 upb_pb_encoder* encoder; | |
198 | |
199 stackenv_init(&se, "Error occurred during encoding: %s"); | |
200 encoder = upb_pb_encoder_create(&se.env, serialize_handlers, &sink.sink); | |
201 | |
202 putmsg(getThis(), desc, upb_pb_encoder_input(encoder), 0); | |
203 | |
204 RETVAL_STRINGL(sink.ptr, sink.len, 1); | |
205 | |
206 stackenv_uninit(&se); | |
207 stringsink_uninit(&sink); | |
208 } | |
209 } | |
210 | |
211 void message_free(void * object TSRMLS_DC) { | |
212 FREE(object); | |
213 } | |
214 | |
215 zend_object_value message_create(zend_class_entry* ce TSRMLS_DC) { | |
216 zend_object_value return_value; | 166 zend_object_value return_value; |
217 | 167 |
218 zval* php_descriptor = get_def_obj(ce); | 168 zval* php_descriptor = get_ce_obj(ce); |
219 | 169 |
220 Descriptor* desc = zend_object_store_get_object(php_descriptor TSRMLS_CC); | 170 Descriptor* desc = zend_object_store_get_object(php_descriptor TSRMLS_CC); |
221 MessageHeader* msg = (MessageHeader*)ALLOC_N( | 171 MessageHeader* msg = (MessageHeader*)ALLOC_N( |
222 uint8_t, sizeof(MessageHeader) + desc->layout->size); | 172 uint8_t, sizeof(MessageHeader) + desc->layout->size); |
223 memset(message_data(msg), 0, desc->layout->size); | 173 memset(message_data(msg), 0, desc->layout->size); |
224 | 174 |
225 // We wrap first so that everything in the message object is GC-rooted in case | 175 // We wrap first so that everything in the message object is GC-rooted in |
226 // a collection happens during object creation in layout_init(). | 176 // case a collection happens during object creation in layout_init(). |
227 msg->descriptor = desc; | 177 msg->descriptor = desc; |
228 | 178 |
229 layout_init(desc->layout, message_data(msg)); | |
230 zend_object_std_init(&msg->std, ce TSRMLS_CC); | 179 zend_object_std_init(&msg->std, ce TSRMLS_CC); |
| 180 object_properties_init(&msg->std, ce); |
| 181 layout_init(desc->layout, message_data(msg), |
| 182 msg->std.properties_table TSRMLS_CC); |
231 | 183 |
232 return_value.handle = zend_objects_store_put( | 184 return_value.handle = zend_objects_store_put( |
233 msg, (zend_objects_store_dtor_t)zend_objects_destroy_object, | 185 msg, (zend_objects_store_dtor_t)zend_objects_destroy_object, message_free, |
234 message_free, NULL TSRMLS_CC); | 186 NULL TSRMLS_CC); |
235 | 187 |
236 return_value.handlers = PROTOBUF_G(message_handlers); | 188 return_value.handlers = message_handlers; |
237 return return_value; | 189 return return_value; |
238 } | 190 } |
239 | 191 |
240 const zend_class_entry* build_class_from_descriptor( | 192 void build_class_from_descriptor(zval* php_descriptor TSRMLS_DC) { |
241 zval* php_descriptor TSRMLS_DC) { | 193 Descriptor* desc = UNBOX(Descriptor, php_descriptor); |
242 Descriptor* desc = php_to_descriptor(php_descriptor); | 194 |
| 195 // Map entries don't have existing php class. |
| 196 if (upb_msgdef_mapentry(desc->msgdef)) { |
| 197 return; |
| 198 } |
| 199 |
| 200 zend_class_entry* registered_ce = desc->klass; |
| 201 |
243 if (desc->layout == NULL) { | 202 if (desc->layout == NULL) { |
244 MessageLayout* layout = create_layout(desc->msgdef); | 203 MessageLayout* layout = create_layout(desc->msgdef); |
245 desc->layout = layout; | 204 desc->layout = layout; |
246 } | 205 } |
247 // TODO(teboring): Add it back. | |
248 // if (desc->fill_method == NULL) { | |
249 // desc->fill_method = new_fillmsg_decodermethod(desc, &desc->fill_method); | |
250 // } | |
251 | |
252 const char* name = upb_msgdef_fullname(desc->msgdef); | |
253 if (name == NULL) { | |
254 zend_error(E_ERROR, "Descriptor does not have assigned name."); | |
255 } | |
256 | |
257 zend_class_entry class_entry; | |
258 INIT_CLASS_ENTRY_EX(class_entry, name, strlen(name), message_methods); | |
259 zend_class_entry* registered_ce = | |
260 zend_register_internal_class(&class_entry TSRMLS_CC); | |
261 | |
262 add_def_obj(registered_ce, php_descriptor); | |
263 | |
264 if (PROTOBUF_G(message_handlers) == NULL) { | |
265 PROTOBUF_G(message_handlers) = ALLOC(zend_object_handlers); | |
266 memcpy(PROTOBUF_G(message_handlers), zend_get_std_object_handlers(), | |
267 sizeof(zend_object_handlers)); | |
268 PROTOBUF_G(message_handlers)->write_property = message_set_property; | |
269 PROTOBUF_G(message_handlers)->read_property = message_get_property; | |
270 } | |
271 | 206 |
272 registered_ce->create_object = message_create; | 207 registered_ce->create_object = message_create; |
273 } | 208 } |
| 209 |
| 210 // ----------------------------------------------------------------------------- |
| 211 // PHP Methods |
| 212 // ----------------------------------------------------------------------------- |
| 213 |
| 214 // At the first time the message is created, the class entry hasn't been |
| 215 // modified. As a result, the first created instance will be a normal zend |
| 216 // object. Here, we manually modify it to our message in such a case. |
| 217 PHP_METHOD(Message, __construct) { |
| 218 if (Z_OBJVAL_P(getThis()).handlers != message_handlers) { |
| 219 zend_class_entry* ce = Z_OBJCE_P(getThis()); |
| 220 zval_dtor(getThis()); |
| 221 Z_OBJVAL_P(getThis()) = message_create(ce TSRMLS_CC); |
| 222 } |
| 223 } |
| 224 |
| 225 PHP_METHOD(Message, readOneof) { |
| 226 long index; |
| 227 |
| 228 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) == |
| 229 FAILURE) { |
| 230 return; |
| 231 } |
| 232 |
| 233 MessageHeader* msg = |
| 234 (MessageHeader*)zend_object_store_get_object(getThis() TSRMLS_CC); |
| 235 |
| 236 const upb_fielddef* field = upb_msgdef_itof(msg->descriptor->msgdef, index); |
| 237 |
| 238 int property_cache_index = |
| 239 msg->descriptor->layout->fields[upb_fielddef_index(field)].cache_index; |
| 240 zval** cache_ptr = &(msg->std.properties_table)[property_cache_index]; |
| 241 |
| 242 layout_get(msg->descriptor->layout, message_data(msg), field, |
| 243 &return_value TSRMLS_CC); |
| 244 } |
| 245 |
| 246 PHP_METHOD(Message, writeOneof) { |
| 247 long index; |
| 248 zval* value; |
| 249 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lz", &index, &value) == |
| 250 FAILURE) { |
| 251 return; |
| 252 } |
| 253 |
| 254 MessageHeader* msg = |
| 255 (MessageHeader*)zend_object_store_get_object(getThis() TSRMLS_CC); |
| 256 |
| 257 const upb_fielddef* field = upb_msgdef_itof(msg->descriptor->msgdef, index); |
| 258 |
| 259 layout_set(msg->descriptor->layout, msg, field, value TSRMLS_CC); |
| 260 } |
OLD | NEW |