Home | History | Annotate | Download | only in protobuf
      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 }
    382