From 0e1430719aca1c2678140fcbfa6b626428e8fe6d Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Wed, 22 Dec 2021 16:50:22 +0100 Subject: [PATCH 1/1] ensure proper default initialization --- ion.c | 25 +++++++--- ion.stub.php | 2 +- ion_arginfo.h | 4 +- ion_private.h | 90 ++++++++++++++++++++++++---------- tests/Serializer/PHP.phpt | 64 ++++++++++++++++++++++++ tests/Symbol/Table/Shared.phpt | 4 +- tests/Unserializer/PHP.phpt | 59 ++++++++++++++++++++++ tests/serialize/custom.phpt | 43 ++++++++++++++++ 8 files changed, 254 insertions(+), 37 deletions(-) create mode 100644 tests/Serializer/PHP.phpt create mode 100644 tests/Unserializer/PHP.phpt create mode 100644 tests/serialize/custom.phpt diff --git a/ion.c b/ion.c index 762ea91..f6fb4d3 100644 --- a/ion.c +++ b/ion.c @@ -630,17 +630,26 @@ static ZEND_METHOD(ion_Reader_Options, __construct) Z_PARAM_BOOL(skip_validation) ZEND_PARSE_PARAMETERS_END(); - opt->opt.context_change_notifier = EMPTY_READER_CHANGE_NOTIFIER; if (opt->cb) { + zval zcb; + ZVAL_OBJ(&zcb, opt->cb); + zend_fcall_info_init(&zcb, 0, &opt->ccn.fci, &opt->ccn.fcc, NULL, NULL); + opt->opt.context_change_notifier.context = &opt->ccn; update_property_obj(&opt->std, ZEND_STRL("onContextChange"), opt->cb); + } else { + zend_update_property_null(NULL, &opt->std, ZEND_STRL("onContextChange")); } if (opt->cat) { update_property_obj(&opt->std, ZEND_STRL("catalog"), opt->cat); opt->opt.pcatalog = php_ion_obj(catalog, opt->cat)->cat; + } else { + zend_update_property_null(NULL, &opt->std, ZEND_STRL("catalog")); } if (opt->dec_ctx) { update_property_obj(&opt->std, ZEND_STRL("decimalContext"), opt->dec_ctx); opt->opt.decimal_context = &php_ion_obj(decimal_ctx, opt->dec_ctx)->ctx; + } else { + zend_update_property_null(NULL, &opt->std, ZEND_STRL("decimalContext")); } zend_update_property_bool(opt->std.ce, &opt->std, ZEND_STRL("returnSystemValues"), opt->opt.return_system_values = ret_sys_val); @@ -648,7 +657,7 @@ static ZEND_METHOD(ion_Reader_Options, __construct) opt->opt.new_line_char = ch_nl); zend_update_property_long(opt->std.ce, &opt->std, ZEND_STRL("maxContainerDepth"), opt->opt.max_container_depth = max_depth); - zend_update_property_long(opt->std.ce, &opt->std, ZEND_STRL("maxAnnotationCount"), + zend_update_property_long(opt->std.ce, &opt->std, ZEND_STRL("maxAnnotations"), opt->opt.max_annotation_count = max_ann); zend_update_property_long(opt->std.ce, &opt->std, ZEND_STRL("maxAnnotationBuffered"), opt->opt.max_annotation_buffered = max_ann_buf); @@ -1246,16 +1255,20 @@ static ZEND_METHOD(ion_Writer_Options, __construct) if (obj->cat) { update_property_obj(&obj->std, ZEND_STRL("catalog"), obj->cat); obj->opt.pcatalog = php_ion_obj(catalog, obj->cat)->cat; + } else { + zend_update_property_null(NULL, &obj->std, ZEND_STRL("catalog")); } if (obj->dec_ctx) { update_property_obj(&obj->std, ZEND_STRL("decimalContext"), obj->dec_ctx); obj->opt.decimal_context = &php_ion_obj(decimal_ctx, obj->dec_ctx)->ctx; + } else { + zend_update_property_null(NULL, &obj->std, ZEND_STRL("decimalContext")); } zend_update_property_bool(obj->std.ce, &obj->std, ZEND_STRL("outputBinary"), obj->opt.output_as_binary = binary); - zend_update_property_bool(obj->std.ce, &obj->std, ZEND_STRL("comactFloats"), + zend_update_property_bool(obj->std.ce, &obj->std, ZEND_STRL("compactFloats"), obj->opt.compact_floats = compact_floats); - zend_update_property_bool(obj->std.ce, &obj->std, ZEND_STRL("excapeNonAscii"), + zend_update_property_bool(obj->std.ce, &obj->std, ZEND_STRL("escapeNonAscii"), obj->opt.escape_all_non_ascii = escape); zend_update_property_bool(obj->std.ce, &obj->std, ZEND_STRL("prettyPrint"), obj->opt.pretty_print = pretty); @@ -1659,7 +1672,7 @@ static ZEND_METHOD(ion_Serializer_PHP, __construct) obj->serializer.call_magic = true; - ZEND_PARSE_PARAMETERS_START(0, 3) + ZEND_PARSE_PARAMETERS_START(0, 4) Z_PARAM_OPTIONAL Z_PARAM_OBJ_OF_CLASS_OR_NULL(obj->opt, ce_Writer_Options) Z_PARAM_BOOL(obj->serializer.multi_seq) @@ -1723,7 +1736,7 @@ static ZEND_METHOD(ion_Unserializer_PHP, __construct) obj->unserializer.call_magic = true; - ZEND_PARSE_PARAMETERS_START(0, 3) + ZEND_PARSE_PARAMETERS_START(0, 4) Z_PARAM_OPTIONAL Z_PARAM_OBJ_OF_CLASS_OR_NULL(obj->opt, ce_Reader_Options) Z_PARAM_BOOL(obj->unserializer.multi_seq) diff --git a/ion.stub.php b/ion.stub.php index 8f4be35..f317abc 100644 --- a/ion.stub.php +++ b/ion.stub.php @@ -557,7 +557,7 @@ namespace ion\Unserializer; class PHP implements \ion\Unserializer { public function __construct( public readonly ?\ion\Reader\Options $readerOptions = null, - public readonly bool $multiSequence = true, + public readonly bool $multiSequence = false, public readonly bool $callMagicUnserialize = true, public readonly ?string $callCustomUnserialize = null, ){} diff --git a/ion_arginfo.h b/ion_arginfo.h index 0f38dcc..2f02cec 100644 --- a/ion_arginfo.h +++ b/ion_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: c45868fbad8a734e7511f0c4e4991d10b5cc4aa7 */ + * Stub hash: 7322502dd0c48787538abe7e2fa1894b20a736ba */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_ion_Symbol_Table_PHP, 0, 0, ion\\Symbol\\Table, 0) ZEND_END_ARG_INFO() @@ -533,7 +533,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ion_Unserializer_PHP___construct, 0, 0, 0) ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, readerOptions, ion\\Reader\\Options, 1, "null") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, multiSequence, _IS_BOOL, 0, "true") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, multiSequence, _IS_BOOL, 0, "false") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, callMagicUnserialize, _IS_BOOL, 0, "true") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, callCustomUnserialize, IS_STRING, 1, "null") ZEND_END_ARG_INFO() diff --git a/ion_private.h b/ion_private.h index 1bc1c15..0831df3 100644 --- a/ion_private.h +++ b/ion_private.h @@ -379,18 +379,20 @@ static inline void php_ion_globals_unserializer_dtor(void) } \ } while (0) -#define PTR_CHECK(ptr, ...) do { \ +#define PTR_CHECK_RETURN(ret, ptr, ...) do { \ if (!(ptr)) { \ zend_throw_error(NULL, "Uninitialized object"); \ __VA_ARGS__; \ - return; \ + return ret; \ } \ } while (0) +#define PTR_CHECK(ptr, ...) PTR_CHECK_RETURN(, ptr, __VA_ARGS__) -#define OBJ_CHECK(obj, ...) do { \ - PTR_CHECK(obj, __VA_ARGS__); \ - PTR_CHECK(*((void **)obj), __VA_ARGS__); \ +#define OBJ_CHECK_RETURN(ret, obj, ...) do { \ + PTR_CHECK_RETURN(ret, obj, __VA_ARGS__); \ + PTR_CHECK_RETURN(ret, *((void **)obj), __VA_ARGS__); \ } while (0) +#define OBJ_CHECK(obj, ...) OBJ_CHECK_RETURN(, obj, __VA_ARGS__) static inline ION_STRING *ion_string_from_zend(ION_STRING *is, const zend_string *zs) { @@ -987,12 +989,33 @@ static inline void php_ion_catalog_symbol_table_zval(php_ion_catalog *obj, ION_S php_ion_decl(catalog, Catalog, php_ion_catalog_dtor(obj)); +typedef struct php_ion_reader_options_ccn_ctx { + zend_object *obj; + zend_fcall_info fci; + zend_fcall_info_cache fcc; +} php_ion_reader_options_ccn_ctx; + typedef struct php_ion_reader_options { ION_READER_OPTIONS opt; + php_ion_reader_options_ccn_ctx ccn; zend_object *cat, *dec_ctx, *cb, std; } php_ion_reader_options; -php_ion_decl(reader_options, Reader_Options); +static inline void php_ion_reader_options_dtor(php_ion_reader_options *obj) +{ + if (obj->cb) { + zend_fcall_info_args_clear(&obj->ccn.fci, true); + } +} + +php_ion_decl(reader_options, Reader_Options, php_ion_reader_options_dtor(obj)); + +static inline zend_object *php_ion_reader_options_new(void) +{ + zend_object *obj = create_ion_Reader_Options(NULL); + zend_call_known_instance_method_with_0_params(obj->ce->constructor, obj, NULL); + return obj; +} typedef struct php_ion_reader { ION_READER *reader; @@ -1039,26 +1062,31 @@ static inline iERR php_ion_reader_stream_handler(struct _ion_user_stream *user) static inline iERR on_context_change(void *context, ION_COLLECTION *imports) { + iERR e = IERR_OK; + if (context) { - php_ion_reader *obj = php_ion_obj(reader, context); - (void) obj; + php_ion_reader_options_ccn_ctx *ctx = context; + + zval zobj; + ZVAL_OBJ(&zobj, ctx->obj); + zend_fcall_info_argn(&ctx->fci, 1, &zobj); + if (SUCCESS != zend_fcall_info_call(&ctx->fci, &ctx->fcc, NULL, NULL)) { + e = IERR_INTERNAL_ERROR; + } + zend_fcall_info_args_clear(&ctx->fci, false); } - fprintf(stderr, "%s\n", __FUNCTION__); - return IERR_OK; + return e; } -static ION_READER_CONTEXT_CHANGE_NOTIFIER EMPTY_READER_CHANGE_NOTIFIER = { - on_context_change, - NULL -}; - static inline void php_ion_reader_ctor(php_ion_reader *obj) { iERR err; php_ion_reader_options *opt = php_ion_obj(reader_options, obj->opt); - if (opt) { - opt->opt.context_change_notifier.context = obj; + if (opt && opt->opt.context_change_notifier.context) { + php_ion_reader_options_ccn_ctx *ctx = opt->opt.context_change_notifier.context; + ctx->obj = &obj->std; + opt->opt.context_change_notifier.notify = on_context_change; } if (obj->type == STREAM_READER) { PTR_CHECK(obj->stream.ptr); @@ -1073,9 +1101,6 @@ static inline void php_ion_reader_ctor(php_ion_reader *obj) (BYTE *) obj->buffer->val, obj->buffer->len, opt ? &opt->opt : NULL); } - if (opt) { - opt->opt.context_change_notifier.context = NULL; - } ION_CHECK(err); OBJ_CHECK(obj); @@ -1108,6 +1133,13 @@ typedef struct php_ion_writer_options { php_ion_decl(writer_options, Writer_Options); +static inline zend_object *php_ion_writer_options_new(void) +{ + zend_object *obj = create_ion_Writer_Options(NULL); + zend_call_known_instance_method_with_0_params(obj->ce->constructor, obj, NULL); + return obj; +} + typedef struct php_ion_writer { ION_WRITER *writer; enum { @@ -1326,6 +1358,8 @@ static inline void php_ion_serializer_php_ctor(php_ion_serializer_php *ser_obj) ser_obj->serializer.ids = global_ser->ids; ser_obj->serializer.tmp = global_ser->tmp; + zend_update_property_bool(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("multiSequence"), + ser_obj->serializer.multi_seq); zend_update_property_bool(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("callMagicSerialize"), ser_obj->serializer.call_magic); if (ser_obj->serializer.call_custom) { @@ -1336,7 +1370,7 @@ static inline void php_ion_serializer_php_ctor(php_ion_serializer_php *ser_obj) zend_update_property_null(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("callCustomSerialize")); } if (!ser_obj->opt) { - ser_obj->opt = create_ion_Writer_Options(NULL); + ser_obj->opt = php_ion_writer_options_new(); } else { GC_ADDREF(ser_obj->opt); } @@ -1706,7 +1740,7 @@ void php_ion_serialize(php_ion_serializer *ser, zval *zv, zval *return_value) writer->type = BUFFER_WRITER; if (ser->options) { - zo_opt = writer->opt = create_ion_Writer_Options(NULL); + zo_opt = writer->opt = php_ion_writer_options_new(); php_ion_obj(writer_options, writer->opt)->opt = *ser->options; } @@ -1743,17 +1777,19 @@ static inline void php_ion_unserializer_php_ctor(php_ion_unserializer_php *ser_o ser_obj->unserializer.tmp = global_ser->tmp; ser_obj->unserializer.addref = global_ser->addref; - zend_update_property_bool(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("callMagicSerialize"), + zend_update_property_bool(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("multiSequence"), + ser_obj->unserializer.multi_seq); + zend_update_property_bool(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("callMagicUnserialize"), ser_obj->unserializer.call_magic); if (ser_obj->unserializer.call_custom) { - zend_update_property_str(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("callCustomSerialize"), + zend_update_property_str(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("callCustomUnserialize"), ser_obj->unserializer.call_custom); ser_obj->unserializer.call_custom = zend_string_tolower(ser_obj->unserializer.call_custom); } else { - zend_update_property_null(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("callCustomSerialize")); + zend_update_property_null(ser_obj->std.ce, &ser_obj->std, ZEND_STRL("callCustomUnserialize")); } if (!ser_obj->opt) { - ser_obj->opt = create_ion_Reader_Options(NULL); + ser_obj->opt = php_ion_reader_options_new(); } else { GC_ADDREF(ser_obj->opt); } @@ -2424,7 +2460,7 @@ void php_ion_unserialize(php_ion_unserializer *ser, zval *zdata, zval *return_va } if (ser->options) { - zo_opt = reader->opt = create_ion_Reader_Options(NULL); + zo_opt = reader->opt = php_ion_reader_options_new(); php_ion_obj(reader_options, reader->opt)->opt = *ser->options; } diff --git a/tests/Serializer/PHP.phpt b/tests/Serializer/PHP.phpt new file mode 100644 index 0000000..6fad20b --- /dev/null +++ b/tests/Serializer/PHP.phpt @@ -0,0 +1,64 @@ +--TEST-- +ion\Serializer\PHP +--EXTENSIONS-- +ion +--FILE-- +TEST + 1]]); +$o2 = ion\serialize(["foo", ["p" => 1]], $s1 = new ion\Serializer\PHP); +$o3 = ion\serialize(["foo", ["p" => 1]], $s2 = new ion\Serializer\PHP(new ion\Writer\Options)); +if ($o1 !== $o2) { + var_dump($o1, $o2); +} +var_dump($s1); +if ($s1 != $s2) { + var_dump($s2); +} +?> +DONE +--EXPECTF-- +TEST +object(ion\Serializer\PHP)#%d (4) { + ["writerOptions"]=> + object(ion\Writer\Options)#%d (15) { + ["catalog"]=> + NULL + ["decimalContext"]=> + NULL + ["outputBinary"]=> + bool(false) + ["compactFloats"]=> + bool(false) + ["escapeNonAscii"]=> + bool(false) + ["prettyPrint"]=> + bool(false) + ["indentTabs"]=> + bool(true) + ["indentSize"]=> + int(2) + ["smallContainersInline"]=> + bool(true) + ["suppressSystemValues"]=> + bool(false) + ["flushEveryValue"]=> + bool(false) + ["maxContainerDepth"]=> + int(10) + ["maxAnnotations"]=> + int(10) + ["tempBufferSize"]=> + int(16384) + ["allocationPageSize"]=> + int(65536) + } + ["multiSequence"]=> + bool(false) + ["callMagicSerialize"]=> + bool(true) + ["callCustomSerialize"]=> + NULL +} +DONE + diff --git a/tests/Symbol/Table/Shared.phpt b/tests/Symbol/Table/Shared.phpt index 2d51c48..25df25c 100644 --- a/tests/Symbol/Table/Shared.phpt +++ b/tests/Symbol/Table/Shared.phpt @@ -31,7 +31,9 @@ $u = new ion\Unserializer\PHP(multiSequence: true); var_dump($u($buf)); $u = new ion\Unserializer\PHP(multiSequence: true, - readerOptions: new ion\Reader\Options(catalog: $c)); + readerOptions: new ion\Reader\Options( + catalog: $c, + onContextChange: fn(ion\Reader $r) => print("on_context_change\n"))); var_dump($u($buf)); ?> diff --git a/tests/Unserializer/PHP.phpt b/tests/Unserializer/PHP.phpt new file mode 100644 index 0000000..3783d1d --- /dev/null +++ b/tests/Unserializer/PHP.phpt @@ -0,0 +1,59 @@ +--TEST-- +ion\Unserializer\PHP +--EXTENSIONS-- +ion +--FILE-- +TEST + +DONE +--EXPECTF-- +TEST +object(ion\Unserializer\PHP)#%d (4) { + ["readerOptions"]=> + object(ion\Reader\Options)#%d (13) { + ["catalog"]=> + NULL + ["decimalContext"]=> + NULL + ["onContextChange"]=> + NULL + ["returnSystemValues"]=> + bool(false) + ["newLine"]=> + int(10) + ["maxContainerDepth"]=> + int(10) + ["maxAnnotations"]=> + int(10) + ["maxAnnotationBuffered"]=> + int(512) + ["symbolThreshold"]=> + int(16384) + ["userValueThreshold"]=> + int(16384) + ["chunkThreshold"]=> + int(16384) + ["allocationPageSize"]=> + int(65536) + ["skipCharacterValidation"]=> + bool(false) + } + ["multiSequence"]=> + bool(false) + ["callMagicUnserialize"]=> + bool(true) + ["callCustomUnserialize"]=> + NULL +} +DONE diff --git a/tests/serialize/custom.phpt b/tests/serialize/custom.phpt new file mode 100644 index 0000000..7fdd6f0 --- /dev/null +++ b/tests/serialize/custom.phpt @@ -0,0 +1,43 @@ +--TEST-- +ion\serialize/custom +--EXTENSIONS-- +ion +--FILE-- +TEST + 1, + "prot" => 2, + "pub" => 3 + ]; + } + + function unserialize(array $data) : void { + foreach ($data as $k => $v) { + $this->$k = $v; + } + } +} +$s = ion\serialize(new custom, new ion\Serializer\PHP(callCustomSerialize: "serialize")); +echo $s,"\n"; +var_dump(ion\unserialize($s, new ion\Unserializer\PHP(callCustomUnserialize: "unserialize"))); +?> +DONE +--EXPECTF-- +TEST +C::custom::{priv:1,prot:2,pub:3} +object(custom)#%d (3) { + ["priv":"custom":private]=> + int(1) + ["prot":protected]=> + int(2) + ["pub"]=> + int(3) +} +DONE -- 2.30.2