def.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. // Protocol Buffers - Google's data interchange format
  2. // Copyright 2008 Google Inc. All rights reserved.
  3. // https://developers.google.com/protocol-buffers/
  4. //
  5. // Redistribution and use in source and binary forms, with or without
  6. // modification, are permitted provided that the following conditions are
  7. // met:
  8. //
  9. // * Redistributions of source code must retain the above copyright
  10. // notice, this list of conditions and the following disclaimer.
  11. // * Redistributions in binary form must reproduce the above
  12. // copyright notice, this list of conditions and the following disclaimer
  13. // in the documentation and/or other materials provided with the
  14. // distribution.
  15. // * Neither the name of Google Inc. nor the names of its
  16. // contributors may be used to endorse or promote products derived from
  17. // this software without specific prior written permission.
  18. //
  19. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  20. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  21. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  22. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  23. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  24. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  25. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  26. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  27. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  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.
  30. #include "protobuf.h"
  31. // Forward declare.
  32. static void descriptor_init_c_instance(Descriptor* intern TSRMLS_DC);
  33. static void descriptor_free_c(Descriptor* object TSRMLS_DC);
  34. static void enum_descriptor_init_c_instance(EnumDescriptor* intern TSRMLS_DC);
  35. static void enum_descriptor_free_c(EnumDescriptor* object TSRMLS_DC);
  36. static void descriptor_pool_free_c(DescriptorPool* object TSRMLS_DC);
  37. static void descriptor_pool_init_c_instance(DescriptorPool* pool TSRMLS_DC);
  38. // -----------------------------------------------------------------------------
  39. // Common Utilities
  40. // -----------------------------------------------------------------------------
  41. static void check_upb_status(const upb_status* status, const char* msg) {
  42. if (!upb_ok(status)) {
  43. zend_error(E_ERROR, "%s: %s\n", msg, upb_status_errmsg(status));
  44. }
  45. }
  46. static void upb_filedef_free(void *r) {
  47. upb_filedef *f = *(upb_filedef **)r;
  48. size_t i;
  49. for (i = 0; i < upb_filedef_depcount(f); i++) {
  50. upb_filedef_unref(upb_filedef_dep(f, i), f);
  51. }
  52. upb_inttable_uninit(&f->defs);
  53. upb_inttable_uninit(&f->deps);
  54. upb_gfree((void *)f->name);
  55. upb_gfree((void *)f->package);
  56. upb_gfree(f);
  57. }
  58. // Camel-case the field name and append "Entry" for generated map entry name.
  59. // e.g. map<KeyType, ValueType> foo_map => FooMapEntry
  60. static void append_map_entry_name(char *result, const char *field_name,
  61. int pos) {
  62. bool cap_next = true;
  63. int i;
  64. for (i = 0; i < strlen(field_name); ++i) {
  65. if (field_name[i] == '_') {
  66. cap_next = true;
  67. } else if (cap_next) {
  68. // Note: Do not use ctype.h due to locales.
  69. if ('a' <= field_name[i] && field_name[i] <= 'z') {
  70. result[pos++] = field_name[i] - 'a' + 'A';
  71. } else {
  72. result[pos++] = field_name[i];
  73. }
  74. cap_next = false;
  75. } else {
  76. result[pos++] = field_name[i];
  77. }
  78. }
  79. strcat(result, "Entry");
  80. }
  81. #define CHECK_UPB(code, msg) \
  82. do { \
  83. upb_status status = UPB_STATUS_INIT; \
  84. code; \
  85. check_upb_status(&status, msg); \
  86. } while (0)
  87. // Define PHP class
  88. #define DEFINE_PROTOBUF_INIT_CLASS(CLASSNAME, CAMELNAME, LOWERNAME) \
  89. PHP_PROTO_INIT_CLASS_START(CLASSNAME, CAMELNAME, LOWERNAME) \
  90. PHP_PROTO_INIT_CLASS_END
  91. #define DEFINE_PROTOBUF_CREATE(NAME, LOWERNAME) \
  92. PHP_PROTO_OBJECT_CREATE_START(NAME, LOWERNAME) \
  93. LOWERNAME##_init_c_instance(intern TSRMLS_CC); \
  94. PHP_PROTO_OBJECT_CREATE_END(NAME, LOWERNAME)
  95. #define DEFINE_PROTOBUF_FREE(CAMELNAME, LOWERNAME) \
  96. PHP_PROTO_OBJECT_FREE_START(CAMELNAME, LOWERNAME) \
  97. LOWERNAME##_free_c(intern TSRMLS_CC); \
  98. PHP_PROTO_OBJECT_FREE_END
  99. #define DEFINE_PROTOBUF_DTOR(CAMELNAME, LOWERNAME) \
  100. PHP_PROTO_OBJECT_DTOR_START(CAMELNAME, LOWERNAME) \
  101. PHP_PROTO_OBJECT_DTOR_END
  102. #define DEFINE_CLASS(NAME, LOWERNAME, string_name) \
  103. zend_class_entry *LOWERNAME##_type; \
  104. zend_object_handlers *LOWERNAME##_handlers; \
  105. DEFINE_PROTOBUF_FREE(NAME, LOWERNAME) \
  106. DEFINE_PROTOBUF_DTOR(NAME, LOWERNAME) \
  107. DEFINE_PROTOBUF_CREATE(NAME, LOWERNAME) \
  108. DEFINE_PROTOBUF_INIT_CLASS(string_name, NAME, LOWERNAME)
  109. // -----------------------------------------------------------------------------
  110. // GPBType
  111. // -----------------------------------------------------------------------------
  112. zend_class_entry* gpb_type_type;
  113. static zend_function_entry gpb_type_methods[] = {
  114. ZEND_FE_END
  115. };
  116. void gpb_type_init(TSRMLS_D) {
  117. zend_class_entry class_type;
  118. INIT_CLASS_ENTRY(class_type, "Google\\Protobuf\\Internal\\GPBType",
  119. gpb_type_methods);
  120. gpb_type_type = zend_register_internal_class(&class_type TSRMLS_CC);
  121. zend_declare_class_constant_long(gpb_type_type, STR("DOUBLE"), 1 TSRMLS_CC);
  122. zend_declare_class_constant_long(gpb_type_type, STR("FLOAT"), 2 TSRMLS_CC);
  123. zend_declare_class_constant_long(gpb_type_type, STR("INT64"), 3 TSRMLS_CC);
  124. zend_declare_class_constant_long(gpb_type_type, STR("UINT64"), 4 TSRMLS_CC);
  125. zend_declare_class_constant_long(gpb_type_type, STR("INT32"), 5 TSRMLS_CC);
  126. zend_declare_class_constant_long(gpb_type_type, STR("FIXED64"), 6 TSRMLS_CC);
  127. zend_declare_class_constant_long(gpb_type_type, STR("FIXED32"), 7 TSRMLS_CC);
  128. zend_declare_class_constant_long(gpb_type_type, STR("BOOL"), 8 TSRMLS_CC);
  129. zend_declare_class_constant_long(gpb_type_type, STR("STRING"), 9 TSRMLS_CC);
  130. zend_declare_class_constant_long(gpb_type_type, STR("GROUP"), 10 TSRMLS_CC);
  131. zend_declare_class_constant_long(gpb_type_type, STR("MESSAGE"), 11 TSRMLS_CC);
  132. zend_declare_class_constant_long(gpb_type_type, STR("BYTES"), 12 TSRMLS_CC);
  133. zend_declare_class_constant_long(gpb_type_type, STR("UINT32"), 13 TSRMLS_CC);
  134. zend_declare_class_constant_long(gpb_type_type, STR("ENUM"), 14 TSRMLS_CC);
  135. zend_declare_class_constant_long(gpb_type_type, STR("SFIXED32"),
  136. 15 TSRMLS_CC);
  137. zend_declare_class_constant_long(gpb_type_type, STR("SFIXED64"),
  138. 16 TSRMLS_CC);
  139. zend_declare_class_constant_long(gpb_type_type, STR("SINT32"), 17 TSRMLS_CC);
  140. zend_declare_class_constant_long(gpb_type_type, STR("SINT64"), 18 TSRMLS_CC);
  141. }
  142. // -----------------------------------------------------------------------------
  143. // Descriptor
  144. // -----------------------------------------------------------------------------
  145. static zend_function_entry descriptor_methods[] = {
  146. ZEND_FE_END
  147. };
  148. DEFINE_CLASS(Descriptor, descriptor, "Google\\Protobuf\\Internal\\Descriptor");
  149. static void descriptor_free_c(Descriptor *self TSRMLS_DC) {
  150. if (self->layout) {
  151. free_layout(self->layout);
  152. }
  153. if (self->fill_handlers) {
  154. upb_handlers_unref(self->fill_handlers, &self->fill_handlers);
  155. }
  156. if (self->fill_method) {
  157. upb_pbdecodermethod_unref(self->fill_method, &self->fill_method);
  158. }
  159. if (self->json_fill_method) {
  160. upb_json_parsermethod_unref(self->json_fill_method,
  161. &self->json_fill_method);
  162. }
  163. if (self->pb_serialize_handlers) {
  164. upb_handlers_unref(self->pb_serialize_handlers,
  165. &self->pb_serialize_handlers);
  166. }
  167. if (self->json_serialize_handlers) {
  168. upb_handlers_unref(self->json_serialize_handlers,
  169. &self->json_serialize_handlers);
  170. }
  171. if (self->json_serialize_handlers_preserve) {
  172. upb_handlers_unref(self->json_serialize_handlers_preserve,
  173. &self->json_serialize_handlers_preserve);
  174. }
  175. }
  176. static void descriptor_init_c_instance(Descriptor *desc TSRMLS_DC) {
  177. // zend_object_std_init(&desc->std, descriptor_type TSRMLS_CC);
  178. desc->msgdef = NULL;
  179. desc->layout = NULL;
  180. desc->klass = NULL;
  181. desc->fill_handlers = NULL;
  182. desc->fill_method = NULL;
  183. desc->json_fill_method = NULL;
  184. desc->pb_serialize_handlers = NULL;
  185. desc->json_serialize_handlers = NULL;
  186. desc->json_serialize_handlers_preserve = NULL;
  187. }
  188. // -----------------------------------------------------------------------------
  189. // EnumDescriptor
  190. // -----------------------------------------------------------------------------
  191. static zend_function_entry enum_descriptor_methods[] = {
  192. ZEND_FE_END
  193. };
  194. DEFINE_CLASS(EnumDescriptor, enum_descriptor,
  195. "Google\\Protobuf\\Internal\\EnumDescriptor");
  196. static void enum_descriptor_free_c(EnumDescriptor *self TSRMLS_DC) {
  197. }
  198. static void enum_descriptor_init_c_instance(EnumDescriptor *self TSRMLS_DC) {
  199. // zend_object_std_init(&self->std, enum_descriptor_type TSRMLS_CC);
  200. self->enumdef = NULL;
  201. self->klass = NULL;
  202. }
  203. // -----------------------------------------------------------------------------
  204. // FieldDescriptor
  205. // -----------------------------------------------------------------------------
  206. upb_fieldtype_t to_fieldtype(upb_descriptortype_t type) {
  207. switch (type) {
  208. #define CASE(descriptor_type, type) \
  209. case UPB_DESCRIPTOR_TYPE_##descriptor_type: \
  210. return UPB_TYPE_##type;
  211. CASE(FLOAT, FLOAT);
  212. CASE(DOUBLE, DOUBLE);
  213. CASE(BOOL, BOOL);
  214. CASE(STRING, STRING);
  215. CASE(BYTES, BYTES);
  216. CASE(MESSAGE, MESSAGE);
  217. CASE(GROUP, MESSAGE);
  218. CASE(ENUM, ENUM);
  219. CASE(INT32, INT32);
  220. CASE(INT64, INT64);
  221. CASE(UINT32, UINT32);
  222. CASE(UINT64, UINT64);
  223. CASE(SINT32, INT32);
  224. CASE(SINT64, INT64);
  225. CASE(FIXED32, UINT32);
  226. CASE(FIXED64, UINT64);
  227. CASE(SFIXED32, INT32);
  228. CASE(SFIXED64, INT64);
  229. #undef CONVERT
  230. }
  231. zend_error(E_ERROR, "Unknown field type.");
  232. return 0;
  233. }
  234. // -----------------------------------------------------------------------------
  235. // DescriptorPool
  236. // -----------------------------------------------------------------------------
  237. static zend_function_entry descriptor_pool_methods[] = {
  238. PHP_ME(DescriptorPool, getGeneratedPool, NULL,
  239. ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
  240. PHP_ME(DescriptorPool, internalAddGeneratedFile, NULL, ZEND_ACC_PUBLIC)
  241. ZEND_FE_END
  242. };
  243. DEFINE_CLASS(DescriptorPool, descriptor_pool,
  244. "Google\\Protobuf\\Internal\\DescriptorPool");
  245. // wrapper of generated pool
  246. #if PHP_MAJOR_VERSION < 7
  247. zval* generated_pool_php;
  248. #else
  249. zend_object *generated_pool_php;
  250. #endif
  251. DescriptorPool *generated_pool; // The actual generated pool
  252. static void init_generated_pool_once(TSRMLS_D) {
  253. if (generated_pool_php == NULL) {
  254. #if PHP_MAJOR_VERSION < 7
  255. MAKE_STD_ZVAL(generated_pool_php);
  256. ZVAL_OBJ(generated_pool_php, descriptor_pool_type->create_object(
  257. descriptor_pool_type TSRMLS_CC));
  258. generated_pool = UNBOX(DescriptorPool, generated_pool_php);
  259. #else
  260. generated_pool_php =
  261. descriptor_pool_type->create_object(descriptor_pool_type TSRMLS_CC);
  262. generated_pool = (DescriptorPool *)((char *)generated_pool_php -
  263. XtOffsetOf(DescriptorPool, std));
  264. #endif
  265. }
  266. }
  267. static void descriptor_pool_init_c_instance(DescriptorPool *pool TSRMLS_DC) {
  268. // zend_object_std_init(&pool->std, descriptor_pool_type TSRMLS_CC);
  269. pool->symtab = upb_symtab_new();
  270. ALLOC_HASHTABLE(pool->pending_list);
  271. zend_hash_init(pool->pending_list, 1, NULL, ZVAL_PTR_DTOR, 0);
  272. }
  273. static void descriptor_pool_free_c(DescriptorPool *pool TSRMLS_DC) {
  274. upb_symtab_free(pool->symtab);
  275. zend_hash_destroy(pool->pending_list);
  276. FREE_HASHTABLE(pool->pending_list);
  277. }
  278. static void validate_enumdef(const upb_enumdef *enumdef) {
  279. // Verify that an entry exists with integer value 0. (This is the default
  280. // value.)
  281. const char *lookup = upb_enumdef_iton(enumdef, 0);
  282. if (lookup == NULL) {
  283. zend_error(E_USER_ERROR,
  284. "Enum definition does not contain a value for '0'.");
  285. }
  286. }
  287. static void validate_msgdef(const upb_msgdef* msgdef) {
  288. // Verify that no required fields exist. proto3 does not support these.
  289. upb_msg_field_iter it;
  290. for (upb_msg_field_begin(&it, msgdef);
  291. !upb_msg_field_done(&it);
  292. upb_msg_field_next(&it)) {
  293. const upb_fielddef* field = upb_msg_iter_field(&it);
  294. if (upb_fielddef_label(field) == UPB_LABEL_REQUIRED) {
  295. zend_error(E_ERROR, "Required fields are unsupported in proto3.");
  296. }
  297. }
  298. }
  299. PHP_METHOD(DescriptorPool, getGeneratedPool) {
  300. init_generated_pool_once(TSRMLS_C);
  301. #if PHP_MAJOR_VERSION < 7
  302. RETURN_ZVAL(generated_pool_php, 1, 0);
  303. #else
  304. ++GC_REFCOUNT(generated_pool_php);
  305. RETURN_OBJ(generated_pool_php);
  306. #endif
  307. }
  308. static void convert_to_class_name_inplace(char *class_name,
  309. const char* fullname,
  310. const char* prefix,
  311. const char* package_name) {
  312. size_t i = 0, j;
  313. bool first_char = true;
  314. size_t pkg_name_len = package_name == NULL ? 0 : strlen(package_name);
  315. size_t prefix_len = prefix == NULL ? 0 : strlen(prefix);
  316. size_t message_name_start = package_name == NULL ? 0 : pkg_name_len + 1;
  317. size_t message_len = (strlen(fullname) - message_name_start);
  318. // In php, class name cannot be Empty.
  319. if (strcmp("google.protobuf.Empty", fullname) == 0) {
  320. strcpy(class_name, "\\Google\\Protobuf\\GPBEmpty");
  321. return;
  322. }
  323. if (pkg_name_len != 0) {
  324. class_name[i++] = '\\';
  325. for (j = 0; j < pkg_name_len; j++) {
  326. // php packages are divided by '\'.
  327. if (package_name[j] == '.') {
  328. class_name[i++] = '\\';
  329. first_char = true;
  330. } else if (first_char) {
  331. // PHP package uses camel case.
  332. if (package_name[j] < 'A' || package_name[j] > 'Z') {
  333. class_name[i++] = package_name[j] + 'A' - 'a';
  334. } else {
  335. class_name[i++] = package_name[j];
  336. }
  337. first_char = false;
  338. } else {
  339. class_name[i++] = package_name[j];
  340. }
  341. }
  342. class_name[i++] = '\\';
  343. }
  344. if (prefix_len > 0) {
  345. strcpy(class_name + i, prefix);
  346. i += prefix_len;
  347. }
  348. // Submessage is concatenated with its containing messages by '_'.
  349. for (j = message_name_start; j < message_name_start + message_len; j++) {
  350. if (fullname[j] == '.') {
  351. class_name[i++] = '_';
  352. } else {
  353. class_name[i++] = fullname[j];
  354. }
  355. }
  356. }
  357. PHP_METHOD(DescriptorPool, internalAddGeneratedFile) {
  358. char *data = NULL;
  359. PHP_PROTO_SIZE data_len;
  360. upb_filedef **files;
  361. size_t i;
  362. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data, &data_len) ==
  363. FAILURE) {
  364. return;
  365. }
  366. DescriptorPool *pool = UNBOX(DescriptorPool, getThis());
  367. CHECK_UPB(files = upb_loaddescriptor(data, data_len, &pool, &status),
  368. "Parse binary descriptors to internal descriptors failed");
  369. // This method is called only once in each file.
  370. assert(files[0] != NULL);
  371. assert(files[1] == NULL);
  372. CHECK_UPB(upb_symtab_addfile(pool->symtab, files[0], &status),
  373. "Unable to add file to DescriptorPool");
  374. // For each enum/message, we need its PHP class, upb descriptor and its PHP
  375. // wrapper. These information are needed later for encoding, decoding and type
  376. // checking. However, sometimes we just have one of them. In order to find
  377. // them quickly, here, we store the mapping for them.
  378. for (i = 0; i < upb_filedef_defcount(files[0]); i++) {
  379. const upb_def *def = upb_filedef_def(files[0], i);
  380. switch (upb_def_type(def)) {
  381. #define CASE_TYPE(def_type, def_type_lower, desc_type, desc_type_lower) \
  382. case UPB_DEF_##def_type: { \
  383. CREATE_HASHTABLE_VALUE(desc, desc_php, desc_type, desc_type_lower##_type); \
  384. const upb_##def_type_lower *def_type_lower = \
  385. upb_downcast_##def_type_lower(def); \
  386. desc->def_type_lower = def_type_lower; \
  387. add_def_obj(desc->def_type_lower, desc_php); \
  388. /* Unlike other messages, MapEntry is shared by all map fields and doesn't \
  389. * have generated PHP class.*/ \
  390. if (upb_def_type(def) == UPB_DEF_MSG && \
  391. upb_msgdef_mapentry(upb_downcast_msgdef(def))) { \
  392. break; \
  393. } \
  394. /* Prepend '.' to package name to make it absolute. In the 5 additional \
  395. * bytes allocated, one for '.', one for trailing 0, and 3 for 'GPB' if \
  396. * given message is google.protobuf.Empty.*/ \
  397. const char *fullname = upb_##def_type_lower##_fullname(def_type_lower); \
  398. const char *prefix = upb_filedef_phpprefix(files[0]); \
  399. size_t klass_name_len = strlen(fullname) + 5; \
  400. if (prefix != NULL) { \
  401. klass_name_len += strlen(prefix); \
  402. } \
  403. char *klass_name = ecalloc(sizeof(char), klass_name_len); \
  404. convert_to_class_name_inplace(klass_name, fullname, prefix, \
  405. upb_filedef_package(files[0])); \
  406. PHP_PROTO_CE_DECLARE pce; \
  407. if (php_proto_zend_lookup_class(klass_name, strlen(klass_name), &pce) == \
  408. FAILURE) { \
  409. zend_error(E_ERROR, "Generated message class %s hasn't been defined", \
  410. klass_name); \
  411. return; \
  412. } else { \
  413. desc->klass = PHP_PROTO_CE_UNREF(pce); \
  414. } \
  415. add_ce_obj(desc->klass, desc_php); \
  416. efree(klass_name); \
  417. break; \
  418. }
  419. CASE_TYPE(MSG, msgdef, Descriptor, descriptor)
  420. CASE_TYPE(ENUM, enumdef, EnumDescriptor, enum_descriptor)
  421. #undef CASE_TYPE
  422. default:
  423. break;
  424. }
  425. }
  426. for (i = 0; i < upb_filedef_defcount(files[0]); i++) {
  427. const upb_def *def = upb_filedef_def(files[0], i);
  428. if (upb_def_type(def) == UPB_DEF_MSG) {
  429. const upb_msgdef *msgdef = upb_downcast_msgdef(def);
  430. PHP_PROTO_HASHTABLE_VALUE desc_php = get_def_obj(msgdef);
  431. build_class_from_descriptor(desc_php TSRMLS_CC);
  432. }
  433. }
  434. upb_filedef_unref(files[0], &pool);
  435. upb_gfree(files);
  436. }