Jisi Liu | 3b3c8ab | 2016-03-30 11:39:59 -0700 | [diff] [blame] | 1 | #include "protobuf.h" |
| 2 | |
| 3 | // ----------------------------------------------------------------------------- |
| 4 | // Common Utilities |
| 5 | // ----------------------------------------------------------------------------- |
| 6 | |
| 7 | void check_upb_status(const upb_status* status, const char* msg) { |
| 8 | if (!upb_ok(status)) { |
| 9 | zend_error("%s: %s\n", msg, upb_status_errmsg(status)); |
| 10 | } |
| 11 | } |
| 12 | |
| 13 | |
| 14 | static upb_def *check_notfrozen(const upb_def *def) { |
| 15 | if (upb_def_isfrozen(def)) { |
| 16 | zend_error(E_ERROR, |
| 17 | "Attempt to modify a frozen descriptor. Once descriptors are " |
| 18 | "added to the descriptor pool, they may not be modified."); |
| 19 | } |
| 20 | return (upb_def *)def; |
| 21 | } |
| 22 | |
| 23 | static upb_msgdef *check_msgdef_notfrozen(const upb_msgdef *def) { |
| 24 | return upb_downcast_msgdef_mutable(check_notfrozen((const upb_def *)def)); |
| 25 | } |
| 26 | |
| 27 | static upb_fielddef *check_fielddef_notfrozen(const upb_fielddef *def) { |
| 28 | return upb_downcast_fielddef_mutable(check_notfrozen((const upb_def *)def)); |
| 29 | } |
| 30 | |
| 31 | #define PROTOBUF_WRAP_INTERN(wrapper, intern, intern_dtor) \ |
| 32 | Z_TYPE_P(wrapper) = IS_OBJECT; \ |
| 33 | Z_OBJVAL_P(wrapper) \ |
| 34 | .handle = zend_objects_store_put( \ |
| 35 | intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, \ |
| 36 | intern_dtor, NULL TSRMLS_CC); \ |
| 37 | Z_OBJVAL_P(wrapper).handlers = zend_get_std_object_handlers(); |
| 38 | |
| 39 | #define PROTOBUF_SETUP_ZEND_WRAPPER(class_name, class_name_lower, wrapper, \ |
| 40 | intern) \ |
| 41 | Z_TYPE_P(wrapper) = IS_OBJECT; \ |
| 42 | class_name *intern = ALLOC(class_name); \ |
| 43 | memset(intern, 0, sizeof(class_name)); \ |
| 44 | class_name_lower##_init_c_instance(intern TSRMLS_CC); \ |
| 45 | Z_OBJVAL_P(wrapper) \ |
| 46 | .handle = zend_objects_store_put(intern, NULL, class_name_lower##_free, \ |
| 47 | NULL TSRMLS_CC); \ |
| 48 | Z_OBJVAL_P(wrapper).handlers = zend_get_std_object_handlers(); |
| 49 | |
| 50 | #define PROTOBUF_CREATE_ZEND_WRAPPER(class_name, class_name_lower, wrapper, \ |
| 51 | intern) \ |
| 52 | MAKE_STD_ZVAL(wrapper); \ |
| 53 | PROTOBUF_SETUP_ZEND_WRAPPER(class_name, class_name_lower, wrapper, intern); |
| 54 | |
| 55 | #define DEFINE_CLASS(name, name_lower, string_name) \ |
| 56 | zend_class_entry *name_lower##_type; \ |
| 57 | void name_lower##_init(TSRMLS_D) { \ |
| 58 | zend_class_entry class_type; \ |
| 59 | INIT_CLASS_ENTRY(class_type, string_name, name_lower##_methods); \ |
| 60 | name_lower##_type = zend_register_internal_class(&class_type TSRMLS_CC); \ |
| 61 | name_lower##_type->create_object = name_lower##_create; \ |
| 62 | } \ |
| 63 | name *php_to_##name_lower(zval *val TSRMLS_DC) { \ |
| 64 | return (name *)zend_object_store_get_object(val TSRMLS_CC); \ |
| 65 | } \ |
| 66 | void name_lower##_free(void *object TSRMLS_DC) { \ |
| 67 | name *intern = (name *)object; \ |
| 68 | name_lower##_free_c(intern TSRMLS_CC); \ |
| 69 | efree(object); \ |
| 70 | } \ |
| 71 | zend_object_value name_lower##_create(zend_class_entry *ce TSRMLS_DC) { \ |
| 72 | zend_object_value return_value; \ |
| 73 | name *intern = (name *)emalloc(sizeof(name)); \ |
| 74 | memset(intern, 0, sizeof(name)); \ |
| 75 | name_lower##_init_c_instance(intern TSRMLS_CC); \ |
| 76 | return_value.handle = zend_objects_store_put( \ |
| 77 | intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, \ |
| 78 | name_lower##_free, NULL TSRMLS_CC); \ |
| 79 | return_value.handlers = zend_get_std_object_handlers(); \ |
| 80 | return return_value; \ |
| 81 | } |
| 82 | |
| 83 | // ----------------------------------------------------------------------------- |
| 84 | // DescriptorPool |
| 85 | // ----------------------------------------------------------------------------- |
| 86 | |
| 87 | static zend_function_entry descriptor_pool_methods[] = { |
| 88 | PHP_ME(DescriptorPool, addMessage, NULL, ZEND_ACC_PUBLIC) |
| 89 | PHP_ME(DescriptorPool, finalize, NULL, ZEND_ACC_PUBLIC) |
| 90 | ZEND_FE_END |
| 91 | }; |
| 92 | |
| 93 | DEFINE_CLASS(DescriptorPool, descriptor_pool, |
| 94 | "Google\\Protobuf\\DescriptorPool"); |
| 95 | |
| 96 | DescriptorPool *generated_pool; // The actual generated pool |
| 97 | |
| 98 | ZEND_FUNCTION(get_generated_pool) { |
| 99 | if (PROTOBUF_G(generated_pool) == NULL) { |
| 100 | MAKE_STD_ZVAL(PROTOBUF_G(generated_pool)); |
| 101 | Z_TYPE_P(PROTOBUF_G(generated_pool)) = IS_OBJECT; |
| 102 | generated_pool = ALLOC(DescriptorPool); |
| 103 | descriptor_pool_init_c_instance(generated_pool TSRMLS_CC); |
| 104 | Z_OBJ_HANDLE_P(PROTOBUF_G(generated_pool)) = zend_objects_store_put( |
| 105 | generated_pool, NULL, |
| 106 | (zend_objects_free_object_storage_t)descriptor_pool_free, NULL TSRMLS_CC); |
| 107 | Z_OBJ_HT_P(PROTOBUF_G(generated_pool)) = zend_get_std_object_handlers(); |
| 108 | } |
| 109 | RETURN_ZVAL(PROTOBUF_G(generated_pool), 1, 0); |
| 110 | } |
| 111 | |
| 112 | void descriptor_pool_init_c_instance(DescriptorPool* pool TSRMLS_DC) { |
| 113 | zend_object_std_init(&pool->std, descriptor_pool_type TSRMLS_CC); |
| 114 | pool->symtab = upb_symtab_new(&pool->symtab); |
| 115 | |
| 116 | ALLOC_HASHTABLE(pool->pending_list); |
| 117 | zend_hash_init(pool->pending_list, 1, NULL, ZVAL_PTR_DTOR, 0); |
| 118 | } |
| 119 | |
| 120 | void descriptor_pool_free_c(DescriptorPool *pool TSRMLS_DC) { |
| 121 | upb_symtab_unref(pool->symtab, &pool->symtab); |
| 122 | zend_hash_destroy(pool->pending_list); |
| 123 | FREE_HASHTABLE(pool->pending_list); |
| 124 | } |
| 125 | |
| 126 | PHP_METHOD(DescriptorPool, addMessage) { |
| 127 | char *name = NULL; |
| 128 | int str_len; |
| 129 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &str_len) == |
| 130 | FAILURE) { |
| 131 | return; |
| 132 | } |
| 133 | |
| 134 | zval* retval = NULL; |
| 135 | PROTOBUF_CREATE_ZEND_WRAPPER(MessageBuilderContext, message_builder_context, |
| 136 | retval, context); |
| 137 | |
| 138 | MAKE_STD_ZVAL(context->pool); |
| 139 | ZVAL_ZVAL(context->pool, getThis(), 1, 0); |
| 140 | |
| 141 | Descriptor *desc = php_to_descriptor(context->descriptor TSRMLS_CC); |
| 142 | Descriptor_name_set(desc, name); |
| 143 | |
| 144 | RETURN_ZVAL(retval, 0, 1); |
| 145 | } |
| 146 | |
| 147 | static void validate_msgdef(const upb_msgdef* msgdef) { |
| 148 | // Verify that no required fields exist. proto3 does not support these. |
| 149 | upb_msg_field_iter it; |
| 150 | for (upb_msg_field_begin(&it, msgdef); |
| 151 | !upb_msg_field_done(&it); |
| 152 | upb_msg_field_next(&it)) { |
| 153 | const upb_fielddef* field = upb_msg_iter_field(&it); |
| 154 | if (upb_fielddef_label(field) == UPB_LABEL_REQUIRED) { |
| 155 | zend_error(E_ERROR, "Required fields are unsupported in proto3."); |
| 156 | } |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | PHP_METHOD(DescriptorPool, finalize) { |
| 161 | DescriptorPool *self = php_to_descriptor_pool(getThis() TSRMLS_CC); |
| 162 | Bucket *temp; |
| 163 | int i, num; |
| 164 | |
| 165 | num = zend_hash_num_elements(self->pending_list); |
| 166 | upb_def **defs = emalloc(sizeof(upb_def *) * num); |
| 167 | |
| 168 | for (i = 0, temp = self->pending_list->pListHead; temp != NULL; |
| 169 | temp = temp->pListNext) { |
| 170 | zval *def_php = *(zval **)temp->pData; |
| 171 | Descriptor* desc = php_to_descriptor(def_php TSRMLS_CC); |
| 172 | defs[i] = (upb_def *)desc->msgdef; |
| 173 | validate_msgdef((const upb_msgdef *)defs[i++]); |
| 174 | } |
| 175 | |
| 176 | CHECK_UPB(upb_symtab_add(self->symtab, (upb_def **)defs, num, NULL, &status), |
| 177 | "Unable to add defs to DescriptorPool"); |
| 178 | |
| 179 | for (temp = self->pending_list->pListHead; temp != NULL; |
| 180 | temp = temp->pListNext) { |
| 181 | // zval *def_php = *(zval **)temp->pData; |
| 182 | // Descriptor* desc = php_to_descriptor(def_php TSRMLS_CC); |
| 183 | build_class_from_descriptor((zval *)temp->pDataPtr TSRMLS_CC); |
| 184 | } |
| 185 | |
| 186 | FREE(defs); |
| 187 | zend_hash_destroy(self->pending_list); |
| 188 | zend_hash_init(self->pending_list, 1, NULL, ZVAL_PTR_DTOR, 0); |
| 189 | } |
| 190 | |
| 191 | // ----------------------------------------------------------------------------- |
| 192 | // Descriptor |
| 193 | // ----------------------------------------------------------------------------- |
| 194 | |
| 195 | static zend_function_entry descriptor_methods[] = { |
| 196 | ZEND_FE_END |
| 197 | }; |
| 198 | |
| 199 | DEFINE_CLASS(Descriptor, descriptor, "Google\\Protobuf\\Descriptor"); |
| 200 | |
| 201 | void descriptor_free_c(Descriptor *self TSRMLS_DC) { |
| 202 | upb_msg_field_iter iter; |
| 203 | upb_msg_field_begin(&iter, self->msgdef); |
| 204 | while (!upb_msg_field_done(&iter)) { |
| 205 | upb_fielddef *fielddef = upb_msg_iter_field(&iter); |
| 206 | upb_fielddef_unref(fielddef, &fielddef); |
| 207 | upb_msg_field_next(&iter); |
| 208 | } |
| 209 | upb_msgdef_unref(self->msgdef, &self->msgdef); |
| 210 | if (self->layout) { |
| 211 | free_layout(self->layout); |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | static void descriptor_add_field(Descriptor *desc, |
| 216 | const upb_fielddef *fielddef) { |
| 217 | upb_msgdef *mut_def = check_msgdef_notfrozen(desc->msgdef); |
| 218 | upb_fielddef *mut_field_def = check_fielddef_notfrozen(fielddef); |
| 219 | CHECK_UPB(upb_msgdef_addfield(mut_def, mut_field_def, NULL, &status), |
| 220 | "Adding field to Descriptor failed"); |
| 221 | // add_def_obj(fielddef, obj); |
| 222 | } |
| 223 | |
| 224 | void descriptor_init_c_instance(Descriptor* desc TSRMLS_DC) { |
| 225 | zend_object_std_init(&desc->std, descriptor_type TSRMLS_CC); |
| 226 | desc->msgdef = upb_msgdef_new(&desc->msgdef); |
| 227 | desc->layout = NULL; |
| 228 | // MAKE_STD_ZVAL(intern->klass); |
| 229 | // ZVAL_NULL(intern->klass); |
| 230 | desc->pb_serialize_handlers = NULL; |
| 231 | } |
| 232 | |
| 233 | void Descriptor_name_set(Descriptor *desc, const char *name) { |
| 234 | upb_msgdef *mut_def = check_msgdef_notfrozen(desc->msgdef); |
| 235 | CHECK_UPB(upb_msgdef_setfullname(mut_def, name, &status), |
| 236 | "Error setting Descriptor name"); |
| 237 | } |
| 238 | |
| 239 | // ----------------------------------------------------------------------------- |
| 240 | // FieldDescriptor |
| 241 | // ----------------------------------------------------------------------------- |
| 242 | |
| 243 | static void field_descriptor_name_set(const upb_fielddef* fielddef, |
| 244 | const char *name) { |
| 245 | upb_fielddef *mut_def = check_fielddef_notfrozen(fielddef); |
| 246 | CHECK_UPB(upb_fielddef_setname(mut_def, name, &status), |
| 247 | "Error setting FieldDescriptor name"); |
| 248 | } |
| 249 | |
| 250 | static void field_descriptor_label_set(const upb_fielddef* fielddef, |
| 251 | upb_label_t upb_label) { |
| 252 | upb_fielddef *mut_def = check_fielddef_notfrozen(fielddef); |
| 253 | upb_fielddef_setlabel(mut_def, upb_label); |
| 254 | } |
| 255 | |
| 256 | upb_fieldtype_t string_to_descriptortype(const char *type) { |
| 257 | #define CONVERT(upb, str) \ |
| 258 | if (!strcmp(type, str)) { \ |
| 259 | return UPB_DESCRIPTOR_TYPE_##upb; \ |
| 260 | } |
| 261 | |
| 262 | CONVERT(FLOAT, "float"); |
| 263 | CONVERT(DOUBLE, "double"); |
| 264 | CONVERT(BOOL, "bool"); |
| 265 | CONVERT(STRING, "string"); |
| 266 | CONVERT(BYTES, "bytes"); |
| 267 | CONVERT(MESSAGE, "message"); |
| 268 | CONVERT(GROUP, "group"); |
| 269 | CONVERT(ENUM, "enum"); |
| 270 | CONVERT(INT32, "int32"); |
| 271 | CONVERT(INT64, "int64"); |
| 272 | CONVERT(UINT32, "uint32"); |
| 273 | CONVERT(UINT64, "uint64"); |
| 274 | CONVERT(SINT32, "sint32"); |
| 275 | CONVERT(SINT64, "sint64"); |
| 276 | CONVERT(FIXED32, "fixed32"); |
| 277 | CONVERT(FIXED64, "fixed64"); |
| 278 | CONVERT(SFIXED32, "sfixed32"); |
| 279 | CONVERT(SFIXED64, "sfixed64"); |
| 280 | |
| 281 | #undef CONVERT |
| 282 | |
| 283 | zend_error(E_ERROR, "Unknown field type."); |
| 284 | return 0; |
| 285 | } |
| 286 | |
| 287 | static void field_descriptor_type_set(const upb_fielddef* fielddef, |
| 288 | const char *type) { |
| 289 | upb_fielddef *mut_def = check_fielddef_notfrozen(fielddef); |
| 290 | upb_fielddef_setdescriptortype(mut_def, string_to_descriptortype(type)); |
| 291 | } |
| 292 | |
| 293 | static void field_descriptor_number_set(const upb_fielddef* fielddef, |
| 294 | int number) { |
| 295 | upb_fielddef *mut_def = check_fielddef_notfrozen(fielddef); |
| 296 | CHECK_UPB(upb_fielddef_setnumber(mut_def, number, &status), |
| 297 | "Error setting field number"); |
| 298 | } |
| 299 | |
| 300 | // ----------------------------------------------------------------------------- |
| 301 | // MessageBuilderContext |
| 302 | // ----------------------------------------------------------------------------- |
| 303 | |
| 304 | static zend_function_entry message_builder_context_methods[] = { |
| 305 | PHP_ME(MessageBuilderContext, finalizeToPool, NULL, ZEND_ACC_PUBLIC) |
| 306 | PHP_ME(MessageBuilderContext, optional, NULL, ZEND_ACC_PUBLIC) |
| 307 | {NULL, NULL, NULL} |
| 308 | }; |
| 309 | |
| 310 | DEFINE_CLASS(MessageBuilderContext, message_builder_context, |
| 311 | "Google\\Protobuf\\Internal\\MessageBuilderContext"); |
| 312 | |
| 313 | void message_builder_context_free_c(MessageBuilderContext *context TSRMLS_DC) { |
| 314 | zval_ptr_dtor(&context->descriptor); |
| 315 | zval_ptr_dtor(&context->pool); |
| 316 | } |
| 317 | |
| 318 | void message_builder_context_init_c_instance( |
| 319 | MessageBuilderContext *context TSRMLS_DC) { |
| 320 | zend_object_std_init(&context->std, message_builder_context_type TSRMLS_CC); |
| 321 | PROTOBUF_CREATE_ZEND_WRAPPER(Descriptor, descriptor, context->descriptor, |
| 322 | desc); |
| 323 | } |
| 324 | |
| 325 | static void msgdef_add_field(Descriptor *desc, upb_label_t upb_label, |
| 326 | const char *name, const char *type, int number, |
| 327 | const char *type_class) { |
| 328 | upb_fielddef *fielddef = upb_fielddef_new(&fielddef); |
| 329 | upb_fielddef_setpacked(fielddef, false); |
| 330 | |
| 331 | field_descriptor_label_set(fielddef, upb_label); |
| 332 | field_descriptor_name_set(fielddef, name); |
| 333 | field_descriptor_type_set(fielddef, type); |
| 334 | field_descriptor_number_set(fielddef, number); |
| 335 | |
| 336 | // // if (type_class != Qnil) { |
| 337 | // // if (TYPE(type_class) != T_STRING) { |
| 338 | // // rb_raise(rb_eArgError, "Expected string for type class"); |
| 339 | // // } |
| 340 | // // // Make it an absolute type name by prepending a dot. |
| 341 | // // type_class = rb_str_append(rb_str_new2("."), type_class); |
| 342 | // // rb_funcall(fielddef, rb_intern("submsg_name="), 1, type_class); |
| 343 | // // } |
| 344 | descriptor_add_field(desc, fielddef); |
| 345 | } |
| 346 | |
| 347 | PHP_METHOD(MessageBuilderContext, optional) { |
| 348 | MessageBuilderContext *self = php_to_message_builder_context(getThis() TSRMLS_CC); |
| 349 | Descriptor *desc = php_to_descriptor(self->descriptor TSRMLS_CC); |
| 350 | // VALUE name, type, number, type_class; |
| 351 | const char *name, *type, *type_class; |
| 352 | int number, name_str_len, type_str_len, type_class_str_len; |
| 353 | if (ZEND_NUM_ARGS() == 3) { |
| 354 | if (zend_parse_parameters(3 TSRMLS_CC, "ssl", &name, |
| 355 | &name_str_len, &type, &type_str_len, &number) == FAILURE) { |
| 356 | return; |
| 357 | } |
| 358 | } else { |
| 359 | if (zend_parse_parameters(4 TSRMLS_CC, "ssls", &name, |
| 360 | &name_str_len, &type, &type_str_len, &number, &type_class, |
| 361 | &type_class_str_len) == FAILURE) { |
| 362 | return; |
| 363 | } |
| 364 | } |
| 365 | |
| 366 | msgdef_add_field(desc, UPB_LABEL_OPTIONAL, name, type, number, type_class); |
| 367 | |
| 368 | zval_copy_ctor(getThis()); |
| 369 | RETURN_ZVAL(getThis(), 1, 0); |
| 370 | } |
| 371 | |
| 372 | PHP_METHOD(MessageBuilderContext, finalizeToPool) { |
| 373 | MessageBuilderContext *self = php_to_message_builder_context(getThis() TSRMLS_CC); |
| 374 | DescriptorPool *pool = php_to_descriptor_pool(self->pool TSRMLS_CC); |
| 375 | Descriptor* desc = php_to_descriptor(self->descriptor TSRMLS_CC); |
| 376 | |
| 377 | Z_ADDREF_P(self->descriptor); |
| 378 | zend_hash_next_index_insert(pool->pending_list, &self->descriptor, |
| 379 | sizeof(zval *), NULL); |
| 380 | RETURN_ZVAL(self->pool, 1, 0); |
| 381 | } |