[PATCH v15 0/7] MR10263: windows.web: Support JSON array and object parsing
Implements JsonArray, JsonObject value getters and extends JsonValue.Parse to support arrays and objects. -- v15: windows.web: Support boolean & number value creation windows.web: Support object parsing windows.web: JsonObject typed getters windows.web: Add object mapping windows.web: Support array parsing windows.web: JsonArray element getters windows.web: Stub IJsonArray https://gitlab.winehq.org/wine/wine/-/merge_requests/10263
From: Olivia Ryan <olivia.r.dev@gmail.com> --- dlls/windows.web/Makefile.in | 1 + dlls/windows.web/classes.idl | 1 + dlls/windows.web/json_array.c | 239 ++++++++++++++++++++++++++++++++++ dlls/windows.web/main.c | 2 + dlls/windows.web/private.h | 1 + dlls/windows.web/tests/web.c | 46 +++++++ 6 files changed, 290 insertions(+) create mode 100644 dlls/windows.web/json_array.c diff --git a/dlls/windows.web/Makefile.in b/dlls/windows.web/Makefile.in index ebcf7bbc2f3..aaaacd6b298 100644 --- a/dlls/windows.web/Makefile.in +++ b/dlls/windows.web/Makefile.in @@ -3,6 +3,7 @@ IMPORTS = combase SOURCES = \ classes.idl \ + json_array.c \ json_object.c \ json_value.c \ main.c diff --git a/dlls/windows.web/classes.idl b/dlls/windows.web/classes.idl index 37537cfeb26..266f560af01 100644 --- a/dlls/windows.web/classes.idl +++ b/dlls/windows.web/classes.idl @@ -24,6 +24,7 @@ import "windows.data.json.idl"; namespace Windows.Data.Json { + runtimeclass JsonArray; runtimeclass JsonObject; runtimeclass JsonValue; } diff --git a/dlls/windows.web/json_array.c b/dlls/windows.web/json_array.c new file mode 100644 index 00000000000..d62427d92d1 --- /dev/null +++ b/dlls/windows.web/json_array.c @@ -0,0 +1,239 @@ +/* WinRT Windows.Data.Json.JsonArray Implementation + * + * Copyright (C) 2026 Olivia Ryan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "private.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(web); + +struct json_array +{ + IJsonArray IJsonArray_iface; + LONG ref; +}; + +static inline struct json_array *impl_from_IJsonArray( IJsonArray *iface ) +{ + return CONTAINING_RECORD( iface, struct json_array, IJsonArray_iface ); +} + +static HRESULT WINAPI json_array_statics_QueryInterface( IJsonArray *iface, REFIID iid, void **out ) +{ + struct json_array *impl = impl_from_IJsonArray( iface ); + + TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IJsonArray )) + { + *out = &impl->IJsonArray_iface; + IInspectable_AddRef( *out ); + return S_OK; + } + + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI json_array_statics_AddRef( IJsonArray *iface ) +{ + struct json_array *impl = impl_from_IJsonArray( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p, ref %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI json_array_statics_Release( IJsonArray *iface ) +{ + struct json_array *impl = impl_from_IJsonArray( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + TRACE( "iface %p, ref %lu.\n", iface, ref ); + return ref; +} + +static HRESULT WINAPI json_array_statics_GetIids( IJsonArray *iface, ULONG *iid_count, IID **iids ) +{ + FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids ); + return E_NOTIMPL; +} + +static HRESULT WINAPI json_array_statics_GetRuntimeClassName( IJsonArray *iface, HSTRING *class_name ) +{ + FIXME( "iface %p, class_name %p stub!\n", iface, class_name ); + return E_NOTIMPL; +} + +static HRESULT WINAPI json_array_statics_GetTrustLevel( IJsonArray *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI json_array_statics_GetObjectAt( IJsonArray *iface, UINT32 index, IJsonObject **value ) +{ + FIXME( "iface %p, index %u, value %p\n", iface, index, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI json_array_statics_GetArrayAt( IJsonArray *iface, UINT32 index, IJsonArray **value ) +{ + FIXME( "iface %p, index %u, value %p\n", iface, index, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI json_array_statics_GetStringAt( IJsonArray *iface, UINT32 index, HSTRING *value ) +{ + FIXME( "iface %p, index %u, value %p\n", iface, index, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI json_array_statics_GetNumberAt( IJsonArray *iface, UINT32 index, double *value ) +{ + FIXME( "iface %p, index %u, value %p\n", iface, index, value ); + return E_NOTIMPL; +} + +static HRESULT WINAPI json_array_statics_GetBooleanAt( IJsonArray *iface, UINT32 index, boolean *value ) +{ + FIXME( "iface %p, index %u, value %p\n", iface, index, value ); + return E_NOTIMPL; +} + +static const struct IJsonArrayVtbl json_array_statics_vtbl = +{ + json_array_statics_QueryInterface, + json_array_statics_AddRef, + json_array_statics_Release, + /* IInspectable methods */ + json_array_statics_GetIids, + json_array_statics_GetRuntimeClassName, + json_array_statics_GetTrustLevel, + /* IJsonArray methods */ + json_array_statics_GetObjectAt, + json_array_statics_GetArrayAt, + json_array_statics_GetStringAt, + json_array_statics_GetNumberAt, + json_array_statics_GetBooleanAt, +}; + +struct json_array_statics +{ + IActivationFactory IActivationFactory_iface; + LONG ref; +}; + +static inline struct json_array_statics *impl_from_IActivationFactory( IActivationFactory *iface ) +{ + return CONTAINING_RECORD( iface, struct json_array_statics, IActivationFactory_iface ); +} + +static HRESULT WINAPI factory_QueryInterface( IActivationFactory *iface, REFIID iid, void **out ) +{ + struct json_array_statics *impl = impl_from_IActivationFactory( iface ); + + TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IActivationFactory )) + { + *out = &impl->IActivationFactory_iface; + IInspectable_AddRef( *out ); + return S_OK; + } + + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI factory_AddRef( IActivationFactory *iface ) +{ + struct json_array_statics *impl = impl_from_IActivationFactory( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p, ref %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI factory_Release( IActivationFactory *iface ) +{ + struct json_array_statics *impl = impl_from_IActivationFactory( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + TRACE( "iface %p, ref %lu.\n", iface, ref ); + return ref; +} + +static HRESULT WINAPI factory_GetIids( IActivationFactory *iface, ULONG *iid_count, IID **iids ) +{ + FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids ); + return E_NOTIMPL; +} + +static HRESULT WINAPI factory_GetRuntimeClassName( IActivationFactory *iface, HSTRING *class_name ) +{ + FIXME( "iface %p, class_name %p stub!\n", iface, class_name ); + return E_NOTIMPL; +} + +static HRESULT WINAPI factory_GetTrustLevel( IActivationFactory *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI factory_ActivateInstance( IActivationFactory *iface, IInspectable **instance ) +{ + struct json_array *impl; + + TRACE( "iface %p, instance %p.\n", iface, instance ); + + *instance = NULL; + if (!(impl = calloc( 1, sizeof(*impl) ))) return E_OUTOFMEMORY; + + impl->IJsonArray_iface.lpVtbl = &json_array_statics_vtbl; + impl->ref = 1; + + *instance = (IInspectable*)&impl->IJsonArray_iface; + return S_OK; +} + +static const struct IActivationFactoryVtbl factory_vtbl = +{ + factory_QueryInterface, + factory_AddRef, + factory_Release, + /* IInspectable methods */ + factory_GetIids, + factory_GetRuntimeClassName, + factory_GetTrustLevel, + /* IActivationFactory methods */ + factory_ActivateInstance, +}; + +static struct json_array_statics json_array_statics = +{ + {&factory_vtbl}, + 1, +}; + +IActivationFactory *json_array_factory = &json_array_statics.IActivationFactory_iface; diff --git a/dlls/windows.web/main.c b/dlls/windows.web/main.c index 60e95fdba0c..bec92a81775 100644 --- a/dlls/windows.web/main.c +++ b/dlls/windows.web/main.c @@ -38,6 +38,8 @@ HRESULT WINAPI DllGetActivationFactory( HSTRING classid, IActivationFactory **fa *factory = NULL; + if (!wcscmp( buffer, RuntimeClass_Windows_Data_Json_JsonArray )) + IActivationFactory_QueryInterface( json_array_factory, &IID_IActivationFactory, (void **)factory ); if (!wcscmp( buffer, RuntimeClass_Windows_Data_Json_JsonObject )) IActivationFactory_QueryInterface( json_object_factory, &IID_IActivationFactory, (void **)factory ); if (!wcscmp( buffer, RuntimeClass_Windows_Data_Json_JsonValue )) diff --git a/dlls/windows.web/private.h b/dlls/windows.web/private.h index d5f0e76d641..9dda1e095f1 100644 --- a/dlls/windows.web/private.h +++ b/dlls/windows.web/private.h @@ -36,6 +36,7 @@ #define WIDL_using_Windows_Data_Json #include "windows.data.json.h" +extern IActivationFactory *json_array_factory; extern IActivationFactory *json_object_factory; extern IActivationFactory *json_value_factory; diff --git a/dlls/windows.web/tests/web.c b/dlls/windows.web/tests/web.c index b0a4fc6cd92..84a1f3e850a 100644 --- a/dlls/windows.web/tests/web.c +++ b/dlls/windows.web/tests/web.c @@ -45,6 +45,51 @@ static void check_interface_( unsigned int line, void *obj, const IID *iid ) IUnknown_Release( unk ); } +static void test_JsonArrayStatics(void) +{ + static const WCHAR *json_array_name = L"Windows.Data.Json.JsonArray"; + IActivationFactory *factory = (void *)0xdeadbeef; + IInspectable *inspectable = (void *)0xdeadbeef; + IJsonArray *json_array = (void *)0xdeadbeef; + HSTRING str = NULL; + HRESULT hr; + LONG ref; + + hr = WindowsCreateString( json_array_name, wcslen( json_array_name ), &str ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = RoGetActivationFactory( str, &IID_IActivationFactory, (void **)&factory ); + WindowsDeleteString( str ); + ok( hr == S_OK || broken( hr == REGDB_E_CLASSNOTREG ), "got hr %#lx.\n", hr ); + if (hr == REGDB_E_CLASSNOTREG) + { + win_skip( "%s runtimeclass not registered, skipping tests.\n", wine_dbgstr_w( json_array_name ) ); + return; + } + + check_interface( factory, &IID_IUnknown ); + check_interface( factory, &IID_IInspectable ); + check_interface( factory, &IID_IAgileObject ); + + hr = IActivationFactory_QueryInterface( factory, &IID_IJsonArray, (void **)&json_array ); + ok( hr == E_NOINTERFACE, "got hr %#lx.\n", hr ); + + hr = WindowsCreateString( json_array_name, wcslen( json_array_name ), &str ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = RoActivateInstance( str, &inspectable ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + WindowsDeleteString( str ); + + hr = IInspectable_QueryInterface( inspectable, &IID_IJsonArray, (void **)&json_array ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + + check_interface( inspectable, &IID_IAgileObject ); + + IJsonArray_Release( json_array ); + IInspectable_Release( inspectable ); + ref = IActivationFactory_Release( factory ); + ok( ref == 1, "got ref %ld.\n", ref ); +} + static void test_JsonObjectStatics(void) { static const WCHAR *json_object_name = L"Windows.Data.Json.JsonObject"; @@ -434,6 +479,7 @@ START_TEST(web) hr = RoInitialize( RO_INIT_MULTITHREADED ); ok( hr == S_OK, "RoInitialize failed, hr %#lx\n", hr ); + test_JsonArrayStatics(); test_JsonObjectStatics(); test_JsonValueStatics(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10263
From: Olivia Ryan <olivia.r.dev@gmail.com> --- dlls/windows.web/json_array.c | 64 ++++++++++--- dlls/windows.web/tests/web.c | 164 ++++++++++++++++++++++++++++++++++ 2 files changed, 218 insertions(+), 10 deletions(-) diff --git a/dlls/windows.web/json_array.c b/dlls/windows.web/json_array.c index d62427d92d1..c48b868e062 100644 --- a/dlls/windows.web/json_array.c +++ b/dlls/windows.web/json_array.c @@ -26,6 +26,8 @@ struct json_array { IJsonArray IJsonArray_iface; LONG ref; + IJsonValue **elements; + ULONG length; }; static inline struct json_array *impl_from_IJsonArray( IJsonArray *iface ) @@ -66,7 +68,17 @@ static ULONG WINAPI json_array_statics_Release( IJsonArray *iface ) { struct json_array *impl = impl_from_IJsonArray( iface ); ULONG ref = InterlockedDecrement( &impl->ref ); + TRACE( "iface %p, ref %lu.\n", iface, ref ); + + if (!ref) + { + for (UINT32 i = 0; i < impl->length; i++) + IJsonValue_Release( impl->elements[i] ); + + if (impl->elements) free( impl->elements ); + free( impl ); + } return ref; } @@ -90,32 +102,62 @@ static HRESULT WINAPI json_array_statics_GetTrustLevel( IJsonArray *iface, Trust static HRESULT WINAPI json_array_statics_GetObjectAt( IJsonArray *iface, UINT32 index, IJsonObject **value ) { - FIXME( "iface %p, index %u, value %p\n", iface, index, value ); - return E_NOTIMPL; + struct json_array *impl = impl_from_IJsonArray( iface ); + + TRACE( "iface %p, index %u, value %p\n", iface, index, value ); + + if (!value) return E_INVALIDARG; + if (index >= impl->length) return E_BOUNDS; + + return IJsonValue_GetObject( impl->elements[index], value ); } static HRESULT WINAPI json_array_statics_GetArrayAt( IJsonArray *iface, UINT32 index, IJsonArray **value ) { - FIXME( "iface %p, index %u, value %p\n", iface, index, value ); - return E_NOTIMPL; + struct json_array *impl = impl_from_IJsonArray( iface ); + + TRACE( "iface %p, index %u, value %p\n", iface, index, value ); + + if (!value) return E_INVALIDARG; + if (index >= impl->length) return E_BOUNDS; + + return IJsonValue_GetArray( impl->elements[index], value ); } static HRESULT WINAPI json_array_statics_GetStringAt( IJsonArray *iface, UINT32 index, HSTRING *value ) { - FIXME( "iface %p, index %u, value %p\n", iface, index, value ); - return E_NOTIMPL; + struct json_array *impl = impl_from_IJsonArray( iface ); + + TRACE( "iface %p, index %u, value %p\n", iface, index, value ); + + if (!value) return E_INVALIDARG; + if (index >= impl->length) return E_BOUNDS; + + return IJsonValue_GetString( impl->elements[index], value ); } static HRESULT WINAPI json_array_statics_GetNumberAt( IJsonArray *iface, UINT32 index, double *value ) { - FIXME( "iface %p, index %u, value %p\n", iface, index, value ); - return E_NOTIMPL; + struct json_array *impl = impl_from_IJsonArray( iface ); + + TRACE( "iface %p, index %u, value %p\n", iface, index, value ); + + if (!value) return E_INVALIDARG; + if (index >= impl->length) return E_BOUNDS; + + return IJsonValue_GetNumber( impl->elements[index], value ); } static HRESULT WINAPI json_array_statics_GetBooleanAt( IJsonArray *iface, UINT32 index, boolean *value ) { - FIXME( "iface %p, index %u, value %p\n", iface, index, value ); - return E_NOTIMPL; + struct json_array *impl = impl_from_IJsonArray( iface ); + + TRACE( "iface %p, index %u, value %p\n", iface, index, value ); + + if (!value) return E_INVALIDARG; + if (index >= impl->length) return E_BOUNDS; + + return IJsonValue_GetBoolean( impl->elements[index], value ); } static const struct IJsonArrayVtbl json_array_statics_vtbl = @@ -212,6 +254,8 @@ static HRESULT WINAPI factory_ActivateInstance( IActivationFactory *iface, IInsp impl->IJsonArray_iface.lpVtbl = &json_array_statics_vtbl; impl->ref = 1; + impl->elements = NULL; + impl->length = 0; *instance = (IInspectable*)&impl->IJsonArray_iface; return S_OK; diff --git a/dlls/windows.web/tests/web.c b/dlls/windows.web/tests/web.c index 84a1f3e850a..87c7f7fb66a 100644 --- a/dlls/windows.web/tests/web.c +++ b/dlls/windows.web/tests/web.c @@ -47,14 +47,37 @@ static void check_interface_( unsigned int line, void *obj, const IID *iid ) static void test_JsonArrayStatics(void) { + static const WCHAR *json_value_statics_name = L"Windows.Data.Json.JsonValue"; static const WCHAR *json_array_name = L"Windows.Data.Json.JsonArray"; + IJsonValueStatics *json_value_statics = (void *)0xdeadbeef; IActivationFactory *factory = (void *)0xdeadbeef; IInspectable *inspectable = (void *)0xdeadbeef; + IJsonObject *child_object = (void *)0xdeadbeef; + IJsonArray *child_array = (void *)0xdeadbeef; IJsonArray *json_array = (void *)0xdeadbeef; + IJsonValue *json_value = (void *)0xdeadbeef; + BOOLEAN child_boolean; + HSTRING child_string; + DOUBLE child_number; HSTRING str = NULL; HRESULT hr; LONG ref; + hr = WindowsCreateString( json_value_statics_name, wcslen( json_value_statics_name ), &str ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = RoGetActivationFactory( str, &IID_IActivationFactory, (void **)&factory ); + WindowsDeleteString( str ); + ok( hr == S_OK || broken( hr == REGDB_E_CLASSNOTREG ), "got hr %#lx.\n", hr ); + if (hr == REGDB_E_CLASSNOTREG) + { + win_skip( "%s runtimeclass not registered, skipping tests.\n", wine_dbgstr_w( json_value_statics_name ) ); + return; + } + + hr = IActivationFactory_QueryInterface( factory, &IID_IJsonValueStatics, (void **)&json_value_statics ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + ref = IActivationFactory_Release( factory ); + hr = WindowsCreateString( json_array_name, wcslen( json_array_name ), &str ); ok( hr == S_OK, "got hr %#lx.\n", hr ); hr = RoGetActivationFactory( str, &IID_IActivationFactory, (void **)&factory ); @@ -84,10 +107,151 @@ static void test_JsonArrayStatics(void) check_interface( inspectable, &IID_IAgileObject ); + hr = IJsonArray_GetObjectAt( json_array, 0, NULL ); + ok( hr == E_INVALIDARG, "got hr %#lx.\n", hr ); + hr = IJsonArray_GetObjectAt( json_array, 0, &child_object ); + ok( hr == E_BOUNDS, "got hr %#lx.\n", hr ); + hr = IJsonArray_GetArrayAt( json_array, 0, NULL ); + ok( hr == E_INVALIDARG, "got hr %#lx.\n", hr ); + hr = IJsonArray_GetArrayAt( json_array, 0, &child_array ); + ok( hr == E_BOUNDS, "got hr %#lx.\n", hr ); + hr = IJsonArray_GetStringAt( json_array, 0, NULL ); + ok( hr == E_INVALIDARG, "got hr %#lx.\n", hr ); + hr = IJsonArray_GetStringAt( json_array, 0, &child_string ); + ok( hr == E_BOUNDS, "got hr %#lx.\n", hr ); + hr = IJsonArray_GetNumberAt( json_array, 0, NULL ); + ok( hr == E_INVALIDARG, "got hr %#lx.\n", hr ); + hr = IJsonArray_GetNumberAt( json_array, 0, &child_number ); + ok( hr == E_BOUNDS, "got hr %#lx.\n", hr ); + hr = IJsonArray_GetBooleanAt( json_array, 0, NULL ); + ok( hr == E_INVALIDARG, "got hr %#lx.\n", hr ); + hr = IJsonArray_GetBooleanAt( json_array, 0, &child_boolean ); + ok( hr == E_BOUNDS, "got hr %#lx.\n", hr ); + IJsonArray_Release( json_array ); IInspectable_Release( inspectable ); ref = IActivationFactory_Release( factory ); ok( ref == 1, "got ref %ld.\n", ref ); + + hr = WindowsCreateString( L"[{}]", wcslen( L"[{}]" ), &str ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = IJsonValueStatics_Parse( json_value_statics, str, &json_value ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + WindowsDeleteString( str ); + if (SUCCEEDED(hr)) + { + hr = IJsonValue_GetArray( json_value, &json_array ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + IJsonValue_Release( json_value ); + hr = IJsonArray_GetObjectAt( json_array, 0, &child_object ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + IJsonObject_Release( child_object ); + hr = IJsonArray_GetArrayAt( json_array, 0, &child_array ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonArray_GetStringAt( json_array, 0, &child_string ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonArray_GetNumberAt( json_array, 0, &child_number ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonArray_GetBooleanAt( json_array, 0, &child_boolean ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + IJsonArray_Release( json_array ); + } + + hr = WindowsCreateString( L"[[]]", wcslen( L"[[]]" ), &str ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = IJsonValueStatics_Parse( json_value_statics, str, &json_value ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + WindowsDeleteString( str ); + if (SUCCEEDED(hr)) + { + hr = IJsonValue_GetArray( json_value, &json_array ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + IJsonValue_Release( json_value ); + hr = IJsonArray_GetObjectAt( json_array, 0, &child_object ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonArray_GetArrayAt( json_array, 0, &child_array ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + IJsonArray_Release( child_array ); + hr = IJsonArray_GetStringAt( json_array, 0, &child_string ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonArray_GetNumberAt( json_array, 0, &child_number ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonArray_GetBooleanAt( json_array, 0, &child_boolean ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + IJsonArray_Release( json_array ); + } + + hr = WindowsCreateString( L"[\"Hello, World!\"]", wcslen( L"[\"Hello, World!\"]" ), &str ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = IJsonValueStatics_Parse( json_value_statics, str, &json_value ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + WindowsDeleteString( str ); + if (SUCCEEDED(hr)) + { + hr = IJsonValue_GetArray( json_value, &json_array ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + IJsonValue_Release( json_value ); + hr = IJsonArray_GetObjectAt( json_array, 0, &child_object ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonArray_GetArrayAt( json_array, 0, &child_array ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonArray_GetStringAt( json_array, 0, &child_string ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + WindowsDeleteString( child_string ); + hr = IJsonArray_GetNumberAt( json_array, 0, &child_number ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonArray_GetBooleanAt( json_array, 0, &child_boolean ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + IJsonArray_Release( json_array ); + } + + hr = WindowsCreateString( L"[12.6]", wcslen( L"[12.6]" ), &str ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = IJsonValueStatics_Parse( json_value_statics, str, &json_value ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + WindowsDeleteString( str ); + if (SUCCEEDED(hr)) + { + hr = IJsonValue_GetArray( json_value, &json_array ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + IJsonValue_Release( json_value ); + hr = IJsonArray_GetObjectAt( json_array, 0, &child_object ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonArray_GetArrayAt( json_array, 0, &child_array ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonArray_GetStringAt( json_array, 0, &child_string ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonArray_GetNumberAt( json_array, 0, &child_number ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = IJsonArray_GetBooleanAt( json_array, 0, &child_boolean ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + IJsonArray_Release( json_array ); + } + + hr = WindowsCreateString( L"[true]", wcslen( L"[true]" ), &str ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = IJsonValueStatics_Parse( json_value_statics, str, &json_value ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + WindowsDeleteString( str ); + if (SUCCEEDED(hr)) + { + hr = IJsonValue_GetArray( json_value, &json_array ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + IJsonValue_Release( json_value ); + hr = IJsonArray_GetObjectAt( json_array, 0, &child_object ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonArray_GetArrayAt( json_array, 0, &child_array ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonArray_GetStringAt( json_array, 0, &child_string ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonArray_GetNumberAt( json_array, 0, &child_number ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonArray_GetBooleanAt( json_array, 0, &child_boolean ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + IJsonArray_Release( json_array ); + } + + IJsonValueStatics_Release( json_value_statics ); } static void test_JsonObjectStatics(void) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10263
From: Olivia Ryan <olivia.r.dev@gmail.com> --- dlls/windows.web/json_array.c | 19 ++++++++++++ dlls/windows.web/json_value.c | 56 ++++++++++++++++++++++++++++++++--- dlls/windows.web/private.h | 2 ++ dlls/windows.web/tests/web.c | 3 +- 4 files changed, 74 insertions(+), 6 deletions(-) diff --git a/dlls/windows.web/json_array.c b/dlls/windows.web/json_array.c index c48b868e062..1bb7bef6b84 100644 --- a/dlls/windows.web/json_array.c +++ b/dlls/windows.web/json_array.c @@ -35,6 +35,25 @@ static inline struct json_array *impl_from_IJsonArray( IJsonArray *iface ) return CONTAINING_RECORD( iface, struct json_array, IJsonArray_iface ); } +HRESULT json_array_push( IJsonArray *iface, IJsonValue *value ) +{ + struct json_array *impl = impl_from_IJsonArray( iface ); + IJsonValue **new = impl->elements; + + TRACE( "iface %p, value %p.\n", iface, value ); + + if (!(new = realloc( new, ++impl->length * sizeof(*new) ))) + { + impl->length--; + return E_OUTOFMEMORY; + } + + impl->elements = new; + IJsonValue_AddRef( value ); + impl->elements[impl->length - 1] = value; + return S_OK; +} + static HRESULT WINAPI json_array_statics_QueryInterface( IJsonArray *iface, REFIID iid, void **out ) { struct json_array *impl = impl_from_IJsonArray( iface ); diff --git a/dlls/windows.web/json_value.c b/dlls/windows.web/json_value.c index f34ce5b73d9..ed539219ef4 100644 --- a/dlls/windows.web/json_value.c +++ b/dlls/windows.web/json_value.c @@ -127,6 +127,7 @@ struct json_value boolean boolean_value; HSTRING string_value; double number_value; + IJsonArray *array_value; }; }; @@ -175,6 +176,8 @@ static ULONG WINAPI json_value_Release( IJsonValue *iface ) { if (impl->json_value_type == JsonValueType_String) WindowsDeleteString( impl->string_value ); + else if (impl->json_value_type == JsonValueType_Array) + IJsonArray_Release( impl->array_value ); free( impl ); } @@ -259,12 +262,14 @@ static HRESULT WINAPI json_value_GetArray( IJsonValue *iface, IJsonArray **value { struct json_value *impl = impl_from_IJsonValue( iface ); - FIXME( "iface %p, value %p stub!\n", iface, value ); + TRACE( "iface %p, value %p\n", iface, value ); if (!value) return E_POINTER; if (impl->json_value_type != JsonValueType_Array) return E_ILLEGAL_METHOD_CALL; - return E_NOTIMPL; + IJsonArray_AddRef( impl->array_value ); + *value = impl->array_value; + return S_OK; } static HRESULT WINAPI json_value_GetObject( IJsonValue *iface, IJsonObject **value ) @@ -341,6 +346,50 @@ static WCHAR json_buffer_next( struct json_buffer *json, const WCHAR *valid ) return chr; } +static HRESULT parse_json_value( struct json_buffer *json, IJsonValue **value ); + +static HRESULT parse_json_array( struct json_buffer *json, IJsonArray **value ) +{ + IJsonArray *array; + IJsonValue *child; + HRESULT hr; + + if (!json_buffer_take( json, L"[" )) return WEB_E_INVALID_JSON_STRING; + if (FAILED(hr = IActivationFactory_ActivateInstance( + json_array_factory, (IInspectable**)&array ))) return hr; + + json_buffer_trim( json ); + while (json->len && *json->str != ']') + { + if (FAILED(hr = parse_json_value( json, &child ))) + { + IJsonArray_Release( array ); + return hr; + } + + hr = json_array_push( array, child ); + IJsonValue_Release( child ); + if (FAILED(hr)) + { + IJsonArray_Release( array ); + return hr; + } + + json_buffer_trim( json ); + if (!json_buffer_take( json, L"," )) break; + json_buffer_trim( json ); + } + + if (!json_buffer_take( json, L"]" )) + { + IJsonArray_Release( array ); + return WEB_E_INVALID_JSON_STRING; + } + + *value = array; + return S_OK; +} + static HRESULT parse_json_string( struct json_buffer *json, HSTRING *output ) { const WCHAR valid_hex_chars[] = L"abcdefABCDEF0123456789"; @@ -437,9 +486,8 @@ static HRESULT parse_json_value( struct json_buffer *json, IJsonValue **value ) } else if (*json->str == '[') { - FIXME( "Array parsing not implemented!\n" ); + hr = parse_json_array( json, &impl->array_value ); impl->json_value_type = JsonValueType_Array; - hr = WEB_E_INVALID_JSON_STRING; } else if (*json->str == '{') { diff --git a/dlls/windows.web/private.h b/dlls/windows.web/private.h index 9dda1e095f1..b8cc30cda42 100644 --- a/dlls/windows.web/private.h +++ b/dlls/windows.web/private.h @@ -40,6 +40,8 @@ extern IActivationFactory *json_array_factory; extern IActivationFactory *json_object_factory; extern IActivationFactory *json_value_factory; +HRESULT json_array_push( IJsonArray *iface, IJsonValue *value ); + #define DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from, iface_mem, expr ) \ static inline impl_type *impl_from( iface_type *iface ) \ { \ diff --git a/dlls/windows.web/tests/web.c b/dlls/windows.web/tests/web.c index 87c7f7fb66a..1b29447dfd2 100644 --- a/dlls/windows.web/tests/web.c +++ b/dlls/windows.web/tests/web.c @@ -327,7 +327,7 @@ static void check_json_( unsigned int line, IJsonValueStatics *json_value_static return; } - todo_wine_if(expected_json_value_type == JsonValueType_Array || expected_json_value_type == JsonValueType_Object) + todo_wine_if(expected_json_value_type == JsonValueType_Object) ok_(__FILE__, line)( hr == S_OK, "got hr %#lx.\n", hr ); if (FAILED(hr)) return; hr = IJsonValue_get_ValueType( json_value, &json_value_type ); @@ -398,7 +398,6 @@ static void check_json_( unsigned int line, IJsonValueStatics *json_value_static break; case JsonValueType_Array: hr = IJsonValue_GetArray( json_value, &json_array ); - todo_wine ok_(__FILE__, line)( hr == S_OK, "got hr %#lx.\n", hr ); if (hr == S_OK) IJsonArray_Release( json_array ); break; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10263
From: Olivia Ryan <olivia.r.dev@gmail.com> --- dlls/windows.web/json_object.c | 50 ++++++++++++++++++++++++++++++---- dlls/windows.web/private.h | 1 + 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/dlls/windows.web/json_object.c b/dlls/windows.web/json_object.c index 38c899288b7..81f12e2b72d 100644 --- a/dlls/windows.web/json_object.c +++ b/dlls/windows.web/json_object.c @@ -1,6 +1,7 @@ /* WinRT Windows.Data.Json.JsonObject Implementation * * Copyright (C) 2024 Mohamad Al-Jaf + * Copyright (C) 2026 Olivia Ryan * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -26,6 +27,7 @@ struct json_object { IJsonObject IJsonObject_iface; LONG ref; + IMap_HSTRING_IInspectable *members; }; static inline struct json_object *impl_from_IJsonObject( IJsonObject *iface ) @@ -69,7 +71,10 @@ static ULONG WINAPI json_object_statics_Release( IJsonObject *iface ) TRACE( "iface %p, ref %lu.\n", iface, ref ); - if (!ref) free( impl ); + if (!ref) { + IMap_HSTRING_IInspectable_Release( impl->members ); + free( impl ); + } return ref; } @@ -93,14 +98,25 @@ static HRESULT WINAPI json_object_statics_GetTrustLevel( IJsonObject *iface, Tru static HRESULT WINAPI json_object_statics_GetNamedValue( IJsonObject *iface, HSTRING name, IJsonValue **value ) { - FIXME( "iface %p, name %s, value %p stub!\n", iface, debugstr_hstring( name ), value ); - return E_NOTIMPL; + struct json_object *impl = impl_from_IJsonObject( iface ); + boolean exists; + HRESULT hr; + + TRACE( "iface %p, name %s, value %p.\n", iface, debugstr_hstring( name ), value ); + + if (!value) return E_POINTER; + + hr = IMap_HSTRING_IInspectable_HasKey( impl->members, name, &exists ); + if ( FAILED(hr) || !exists ) return WEB_E_JSON_VALUE_NOT_FOUND; + return IMap_HSTRING_IInspectable_Lookup( impl->members, name, (IInspectable**)value ); } static HRESULT WINAPI json_object_statics_SetNamedValue( IJsonObject *iface, HSTRING name, IJsonValue *value ) { - FIXME( "iface %p, name %s, value %p stub!\n", iface, debugstr_hstring( name ), value ); - return E_NOTIMPL; + boolean dummy; + struct json_object *impl = impl_from_IJsonObject( iface ); + TRACE( "iface %p, name %s, value %p.\n", iface, debugstr_hstring( name ), value ); + return IMap_HSTRING_IInspectable_Insert( impl->members, name, (IInspectable*)value, &dummy ); } static HRESULT WINAPI json_object_statics_GetNamedObject( IJsonObject *iface, HSTRING name, IJsonObject **value ) @@ -220,7 +236,12 @@ static HRESULT WINAPI factory_GetTrustLevel( IActivationFactory *iface, TrustLev static HRESULT WINAPI factory_ActivateInstance( IActivationFactory *iface, IInspectable **instance ) { + const WCHAR *property_set_str = RuntimeClass_Windows_Foundation_Collections_PropertySet; + IPropertySet *property_set; + HSTRING property_set_class; struct json_object *impl; + HSTRING_HEADER header; + HRESULT hr; TRACE( "iface %p, instance %p.\n", iface, instance ); @@ -230,6 +251,25 @@ static HRESULT WINAPI factory_ActivateInstance( IActivationFactory *iface, IInsp impl->IJsonObject_iface.lpVtbl = &json_object_statics_vtbl; impl->ref = 1; + WindowsCreateStringReference( + property_set_str, wcslen( property_set_str ), &header, &property_set_class ); + + if (FAILED(hr = RoActivateInstance( property_set_class, (IInspectable**)&property_set ))) + { + free( impl ); + return hr; + } + + hr = IPropertySet_QueryInterface( + property_set, &IID_IMap_HSTRING_IInspectable, (void**)&impl->members ); + + IPropertySet_Release( property_set ); + if (FAILED(hr)) + { + free( impl ); + return hr; + } + *instance = (IInspectable *)&impl->IJsonObject_iface; return S_OK; } diff --git a/dlls/windows.web/private.h b/dlls/windows.web/private.h index b8cc30cda42..6722d69bc57 100644 --- a/dlls/windows.web/private.h +++ b/dlls/windows.web/private.h @@ -29,6 +29,7 @@ #include "winstring.h" #include "activation.h" +#include "roapi.h" #define WIDL_using_Windows_Foundation #define WIDL_using_Windows_Foundation_Collections -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10263
From: Olivia Ryan <olivia.r.dev@gmail.com> --- dlls/windows.web/json_object.c | 85 ++++++++++++++++++++++++++++++---- 1 file changed, 75 insertions(+), 10 deletions(-) diff --git a/dlls/windows.web/json_object.c b/dlls/windows.web/json_object.c index 81f12e2b72d..afb0fc6e1f4 100644 --- a/dlls/windows.web/json_object.c +++ b/dlls/windows.web/json_object.c @@ -121,32 +121,97 @@ static HRESULT WINAPI json_object_statics_SetNamedValue( IJsonObject *iface, HST static HRESULT WINAPI json_object_statics_GetNamedObject( IJsonObject *iface, HSTRING name, IJsonObject **value ) { - FIXME( "iface %p, name %s, value %p stub!\n", iface, debugstr_hstring( name ), value ); - return E_NOTIMPL; + IJsonValue *internal_value; + JsonValueType value_type; + HRESULT hr; + + TRACE( "iface %p, name %s, value %p.\n", iface, debugstr_hstring( name ), value ); + + if (!value) return E_POINTER; + if (FAILED(hr = IJsonObject_GetNamedValue( iface, name, &internal_value ))) + return hr; + + IJsonValue_Release( internal_value ); + IJsonValue_get_ValueType( internal_value, &value_type ); + if (value_type != JsonValueType_Object) return E_ILLEGAL_METHOD_CALL; + + return IJsonValue_GetObject( internal_value, value ); } static HRESULT WINAPI json_object_statics_GetNamedArray( IJsonObject *iface, HSTRING name, IJsonArray **value ) { - FIXME( "iface %p, name %s, value %p stub!\n", iface, debugstr_hstring( name ), value ); - return E_NOTIMPL; + IJsonValue *internal_value; + JsonValueType value_type; + HRESULT hr; + + TRACE( "iface %p, name %s, value %p.\n", iface, debugstr_hstring( name ), value ); + + if (!value) return E_POINTER; + if (FAILED(hr = IJsonObject_GetNamedValue( iface, name, &internal_value ))) + return hr; + + IJsonValue_Release( internal_value ); + IJsonValue_get_ValueType( internal_value, &value_type ); + if (value_type != JsonValueType_Array) return E_ILLEGAL_METHOD_CALL; + + return IJsonValue_GetArray( internal_value, value ); } static HRESULT WINAPI json_object_statics_GetNamedString( IJsonObject *iface, HSTRING name, HSTRING *value ) { - FIXME( "iface %p, name %s, value %p stub!\n", iface, debugstr_hstring( name ), value ); - return E_NOTIMPL; + IJsonValue *internal_value; + JsonValueType value_type; + HRESULT hr; + + TRACE( "iface %p, name %s, value %p.\n", iface, debugstr_hstring( name ), value ); + + if (!value) return E_POINTER; + if (FAILED(hr = IJsonObject_GetNamedValue( iface, name, &internal_value ))) + return hr; + + IJsonValue_Release( internal_value ); + IJsonValue_get_ValueType( internal_value, &value_type ); + if (value_type != JsonValueType_String) return E_ILLEGAL_METHOD_CALL; + + return IJsonValue_GetString( internal_value, value ); } static HRESULT WINAPI json_object_statics_GetNamedNumber( IJsonObject *iface, HSTRING name, DOUBLE *value ) { - FIXME( "iface %p, name %s, value %p stub!\n", iface, debugstr_hstring( name ), value ); - return E_NOTIMPL; + IJsonValue *internal_value; + JsonValueType value_type; + HRESULT hr; + + TRACE( "iface %p, name %s, value %p.\n", iface, debugstr_hstring( name ), value ); + + if (!value) return E_POINTER; + if (FAILED(hr = IJsonObject_GetNamedValue( iface, name, &internal_value ))) + return hr; + + IJsonValue_Release( internal_value ); + IJsonValue_get_ValueType( internal_value, &value_type ); + if (value_type != JsonValueType_Number) return E_ILLEGAL_METHOD_CALL; + + return IJsonValue_GetNumber( internal_value, value ); } static HRESULT WINAPI json_object_statics_GetNamedBoolean( IJsonObject *iface, HSTRING name, boolean *value ) { - FIXME( "iface %p, name %s, value %p stub!\n", iface, debugstr_hstring( name ), value ); - return E_NOTIMPL; + IJsonValue *internal_value; + JsonValueType value_type; + HRESULT hr; + + TRACE( "iface %p, name %s, value %p.\n", iface, debugstr_hstring( name ), value ); + + if (!value) return E_POINTER; + if (FAILED(hr = IJsonObject_GetNamedValue( iface, name, &internal_value ))) + return hr; + + IJsonValue_Release( internal_value ); + IJsonValue_get_ValueType( internal_value, &value_type ); + if (value_type != JsonValueType_Boolean) return E_ILLEGAL_METHOD_CALL; + + return IJsonValue_GetBoolean( internal_value, value ); } static const struct IJsonObjectVtbl json_object_statics_vtbl = -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10263
From: Olivia Ryan <olivia.r.dev@gmail.com> --- dlls/windows.web/json_value.c | 76 +++++++++++-- dlls/windows.web/tests/web.c | 197 +++++++++++++++++++++++++++++++++- 2 files changed, 263 insertions(+), 10 deletions(-) diff --git a/dlls/windows.web/json_value.c b/dlls/windows.web/json_value.c index ed539219ef4..648b8a5d764 100644 --- a/dlls/windows.web/json_value.c +++ b/dlls/windows.web/json_value.c @@ -128,6 +128,7 @@ struct json_value HSTRING string_value; double number_value; IJsonArray *array_value; + IJsonObject *object_value; }; }; @@ -178,6 +179,8 @@ static ULONG WINAPI json_value_Release( IJsonValue *iface ) WindowsDeleteString( impl->string_value ); else if (impl->json_value_type == JsonValueType_Array) IJsonArray_Release( impl->array_value ); + else if (impl->json_value_type == JsonValueType_Object) + IJsonObject_Release( impl->object_value ); free( impl ); } @@ -276,12 +279,14 @@ static HRESULT WINAPI json_value_GetObject( IJsonValue *iface, IJsonObject **val { struct json_value *impl = impl_from_IJsonValue( iface ); - FIXME( "iface %p, value %p stub!\n", iface, value ); + TRACE( "iface %p, value %p\n", iface, value ); if (!value) return E_POINTER; if (impl->json_value_type != JsonValueType_Object) return E_ILLEGAL_METHOD_CALL; - return E_NOTIMPL; + IJsonObject_AddRef( impl->object_value ); + *value = impl->object_value; + return S_OK; } static const struct IJsonValueVtbl json_value_vtbl = @@ -453,13 +458,71 @@ static HRESULT parse_json_string( struct json_buffer *json, HSTRING *output ) return hr; } +static HRESULT parse_json_object( struct json_buffer *json, IJsonObject **value ) +{ + IJsonObject *object; + IJsonValue *child; + HSTRING name; + HRESULT hr; + + if (!json_buffer_take( json, L"{" )) return WEB_E_INVALID_JSON_STRING; + if (FAILED(hr = IActivationFactory_ActivateInstance( + json_object_factory, (IInspectable**)&object ))) return hr; + + json_buffer_trim( json ); + while (json->len && *json->str != '}') + { + if (FAILED(hr = parse_json_string( json, &name ))) + { + IJsonObject_Release( object ); + return hr; + } + + json_buffer_trim( json ); + if (!json_buffer_take( json, L":" )) + { + IJsonObject_Release( object ); + WindowsDeleteString( name ); + return WEB_E_INVALID_JSON_STRING; + } + + json_buffer_trim( json ); + if (FAILED(hr = parse_json_value( json, &child ))) + { + IJsonObject_Release( object ); + WindowsDeleteString( name ); + return hr; + } + + hr = IJsonObject_SetNamedValue( object, name, child ); + IJsonValue_Release( child ); + WindowsDeleteString( name ); + if (FAILED(hr)) + { + IJsonObject_Release( object ); + return hr; + } + + json_buffer_trim( json ); + if (!json_buffer_take( json, L"," )) break; + json_buffer_trim( json ); + } + + if (!json_buffer_take( json, L"}" )) + { + IJsonObject_Release( object ); + return WEB_E_INVALID_JSON_STRING; + } + + *value = object; + return S_OK; +} + static HRESULT parse_json_value( struct json_buffer *json, IJsonValue **value ) { struct json_value *impl; HRESULT hr = S_OK; - /* FIXME: Handle all JSON edge cases */ - if (!json->len) return WEB_E_INVALID_JSON_STRING; if (!(impl = calloc( 1, sizeof( *impl ) ))) return E_OUTOFMEMORY; impl->IJsonValue_iface.lpVtbl = &json_value_vtbl; @@ -491,9 +554,8 @@ static HRESULT parse_json_value( struct json_buffer *json, IJsonValue **value ) } else if (*json->str == '{') { - FIXME( "Object parsing not implemented!\n" ); + hr = parse_json_object( json, &impl->object_value ); impl->json_value_type = JsonValueType_Object; - hr = WEB_E_INVALID_JSON_STRING; } else { @@ -534,7 +596,7 @@ static HRESULT WINAPI json_value_statics_Parse( IJsonValueStatics *iface, HSTRIN { HRESULT hr; - FIXME( "iface %p, input %s, value %p semi-stub\n", iface, debugstr_hstring( input ), value ); + TRACE( "iface %p, input %s, value %p\n", iface, debugstr_hstring( input ), value ); if (!value) return E_POINTER; if (!input) return WEB_E_INVALID_JSON_STRING; diff --git a/dlls/windows.web/tests/web.c b/dlls/windows.web/tests/web.c index 1b29447dfd2..8b91ddfe530 100644 --- a/dlls/windows.web/tests/web.c +++ b/dlls/windows.web/tests/web.c @@ -256,14 +256,37 @@ static void test_JsonArrayStatics(void) static void test_JsonObjectStatics(void) { + static const WCHAR *json_value_statics_name = L"Windows.Data.Json.JsonValue"; static const WCHAR *json_object_name = L"Windows.Data.Json.JsonObject"; + IJsonValueStatics *json_value_statics = (void *)0xdeadbeef; IActivationFactory *factory = (void *)0xdeadbeef; IInspectable *inspectable = (void *)0xdeadbeef; + IJsonObject *child_object = (void *)0xdeadbeef; IJsonObject *json_object = (void *)0xdeadbeef; + IJsonArray *child_array = (void *)0xdeadbeef; + IJsonValue *child_value = (void *)0xdeadbeef; + BOOLEAN child_boolean; + HSTRING child_string; + DOUBLE child_number; HSTRING str = NULL; HRESULT hr; LONG ref; + hr = WindowsCreateString( json_value_statics_name, wcslen( json_value_statics_name ), &str ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = RoGetActivationFactory( str, &IID_IActivationFactory, (void **)&factory ); + WindowsDeleteString( str ); + ok( hr == S_OK || broken( hr == REGDB_E_CLASSNOTREG ), "got hr %#lx.\n", hr ); + if (hr == REGDB_E_CLASSNOTREG) + { + win_skip( "%s runtimeclass not registered, skipping tests.\n", wine_dbgstr_w( json_value_statics_name ) ); + return; + } + + hr = IActivationFactory_QueryInterface( factory, &IID_IJsonValueStatics, (void **)&json_value_statics ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + ref = IActivationFactory_Release( factory ); + hr = WindowsCreateString( json_object_name, wcslen( json_object_name ), &str ); ok( hr == S_OK, "got hr %#lx.\n", hr ); hr = RoGetActivationFactory( str, &IID_IActivationFactory, (void **)&factory ); @@ -292,9 +315,179 @@ static void test_JsonObjectStatics(void) ok( hr == S_OK, "got hr %#lx.\n", hr ); check_interface( inspectable, &IID_IAgileObject ); + IInspectable_Release( inspectable ); + + hr = WindowsCreateString( L"key", wcslen( L"key" ), &str ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + + /* key pair does not exist */ + + hr = IJsonObject_GetNamedValue( json_object, NULL, NULL ); + ok( hr == E_POINTER, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedValue( json_object, str, NULL ); + ok( hr == E_POINTER, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedValue( json_object, NULL, &child_value ); + ok( hr == WEB_E_JSON_VALUE_NOT_FOUND, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedValue( json_object, str, &child_value ); + ok( hr == WEB_E_JSON_VALUE_NOT_FOUND, "got hr %#lx.\n", hr ); + + hr = IJsonObject_GetNamedObject( json_object, NULL, NULL ); + ok( hr == E_POINTER, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedObject( json_object, str, NULL ); + ok( hr == E_POINTER, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedObject( json_object, NULL, &child_object ); + ok( hr == WEB_E_JSON_VALUE_NOT_FOUND, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedObject( json_object, str, &child_object ); + ok( hr == WEB_E_JSON_VALUE_NOT_FOUND, "got hr %#lx.\n", hr ); + + hr = IJsonObject_GetNamedArray( json_object, NULL, NULL ); + ok( hr == E_POINTER, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedArray( json_object, str, NULL ); + ok( hr == E_POINTER, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedArray( json_object, NULL, &child_array ); + ok( hr == WEB_E_JSON_VALUE_NOT_FOUND, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedArray( json_object, str, &child_array ); + ok( hr == WEB_E_JSON_VALUE_NOT_FOUND, "got hr %#lx.\n", hr ); + + hr = IJsonObject_GetNamedString( json_object, NULL, NULL ); + ok( hr == E_POINTER, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedString( json_object, str, NULL ); + ok( hr == E_POINTER, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedString( json_object, NULL, &child_string ); + ok( hr == WEB_E_JSON_VALUE_NOT_FOUND, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedString( json_object, str, &child_string ); + ok( hr == WEB_E_JSON_VALUE_NOT_FOUND, "got hr %#lx.\n", hr ); + + hr = IJsonObject_GetNamedNumber( json_object, NULL, NULL ); + ok( hr == E_POINTER, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedNumber( json_object, str, NULL ); + ok( hr == E_POINTER, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedNumber( json_object, NULL, &child_number ); + ok( hr == WEB_E_JSON_VALUE_NOT_FOUND, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedNumber( json_object, str, &child_number ); + ok( hr == WEB_E_JSON_VALUE_NOT_FOUND, "got hr %#lx.\n", hr ); + + hr = IJsonObject_GetNamedBoolean( json_object, NULL, NULL ); + ok( hr == E_POINTER, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedBoolean( json_object, str, NULL ); + ok( hr == E_POINTER, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedBoolean( json_object, NULL, &child_boolean ); + ok( hr == WEB_E_JSON_VALUE_NOT_FOUND, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedBoolean( json_object, str, &child_boolean ); + ok( hr == WEB_E_JSON_VALUE_NOT_FOUND, "got hr %#lx.\n", hr ); + + /* key pair exists */ + + WindowsDeleteString( str ); + hr = WindowsCreateString( L"{}", wcslen( L"{}" ), &str ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = IJsonValueStatics_Parse( json_value_statics, str, &child_value ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + WindowsDeleteString( str ); + hr = WindowsCreateString( L"key", wcslen( L"key" ), &str ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = IJsonObject_SetNamedValue( json_object, str, child_value ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedValue( json_object, str, &child_value ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + IJsonValue_Release( child_value ); + hr = IJsonObject_GetNamedObject( json_object, str, &child_object ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + IJsonObject_Release( child_object ); + hr = IJsonObject_GetNamedArray( json_object, str, &child_array ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedString( json_object, str, &child_string ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedNumber( json_object, str, &child_number ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedBoolean( json_object, str, &child_boolean ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + + WindowsDeleteString( str ); + hr = WindowsCreateString( L"[]", wcslen( L"[]" ), &str ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = IJsonValueStatics_Parse( json_value_statics, str, &child_value ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + WindowsDeleteString( str ); + hr = WindowsCreateString( L"key", wcslen( L"key" ), &str ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = IJsonObject_SetNamedValue( json_object, str, child_value ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedValue( json_object, str, &child_value ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + IJsonValue_Release( child_value ); + hr = IJsonObject_GetNamedObject( json_object, str, &child_object ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedArray( json_object, str, &child_array ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + IJsonArray_Release( child_array ); + hr = IJsonObject_GetNamedString( json_object, str, &child_string ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedNumber( json_object, str, &child_number ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedBoolean( json_object, str, &child_boolean ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + + hr = IJsonValueStatics_CreateStringValue( json_value_statics, str, &child_value ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = IJsonObject_SetNamedValue( json_object, str, child_value ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + IJsonValue_Release( child_value ); + hr = IJsonObject_GetNamedValue( json_object, str, &child_value ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + IJsonValue_Release( child_value ); + hr = IJsonObject_GetNamedObject( json_object, str, &child_object ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedArray( json_object, str, &child_array ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedString( json_object, str, &child_string ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + WindowsDeleteString( child_string ); + hr = IJsonObject_GetNamedNumber( json_object, str, &child_number ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedBoolean( json_object, str, &child_boolean ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + + hr = IJsonValueStatics_CreateNumberValue( json_value_statics, 10, &child_value ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = IJsonObject_SetNamedValue( json_object, str, child_value ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + IJsonValue_Release( child_value ); + hr = IJsonObject_GetNamedValue( json_object, str, &child_value ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + IJsonValue_Release( child_value ); + hr = IJsonObject_GetNamedObject( json_object, str, &child_object ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedArray( json_object, str, &child_array ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedString( json_object, str, &child_string ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedNumber( json_object, str, &child_number ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedBoolean( json_object, str, &child_boolean ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + + hr = IJsonValueStatics_CreateBooleanValue( json_value_statics, FALSE, &child_value ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = IJsonObject_SetNamedValue( json_object, str, child_value ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + IJsonValue_Release( child_value ); + hr = IJsonObject_GetNamedValue( json_object, str, &child_value ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + IJsonValue_Release( child_value ); + hr = IJsonObject_GetNamedObject( json_object, str, &child_object ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedArray( json_object, str, &child_array ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedString( json_object, str, &child_string ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedNumber( json_object, str, &child_number ); + ok( hr == E_ILLEGAL_METHOD_CALL, "got hr %#lx.\n", hr ); + hr = IJsonObject_GetNamedBoolean( json_object, str, &child_boolean ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); IJsonObject_Release( json_object ); - IInspectable_Release( inspectable ); + IJsonValueStatics_Release( json_value_statics ); ref = IActivationFactory_Release( factory ); ok( ref == 1, "got ref %ld.\n", ref ); } @@ -327,7 +520,6 @@ static void check_json_( unsigned int line, IJsonValueStatics *json_value_static return; } - todo_wine_if(expected_json_value_type == JsonValueType_Object) ok_(__FILE__, line)( hr == S_OK, "got hr %#lx.\n", hr ); if (FAILED(hr)) return; hr = IJsonValue_get_ValueType( json_value, &json_value_type ); @@ -403,7 +595,6 @@ static void check_json_( unsigned int line, IJsonValueStatics *json_value_static break; case JsonValueType_Object: hr = IJsonValue_GetObject( json_value, &json_object ); - todo_wine ok_(__FILE__, line)( hr == S_OK, "got hr %#lx.\n", hr ); if (hr == S_OK) IJsonObject_Release( json_object ); break; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10263
From: Olivia Ryan <olivia.r.dev@gmail.com> --- dlls/windows.web/json_value.c | 34 ++++++++++++++++++++++++++++++---- dlls/windows.web/tests/web.c | 24 ++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/dlls/windows.web/json_value.c b/dlls/windows.web/json_value.c index 648b8a5d764..2c0714c67cc 100644 --- a/dlls/windows.web/json_value.c +++ b/dlls/windows.web/json_value.c @@ -615,14 +615,40 @@ static HRESULT WINAPI json_value_statics_TryParse( IJsonValueStatics *iface, HST static HRESULT WINAPI json_value_statics_CreateBooleanValue( IJsonValueStatics *iface, boolean input, IJsonValue **value ) { - FIXME( "iface %p, input %d, value %p stub!\n", iface, input, value ); - return E_NOTIMPL; + struct json_value *impl; + + TRACE( "iface %p, input %d, value %p\n", iface, input, value ); + + if (!value) return E_POINTER; + if (!(impl = calloc( 1, sizeof(*impl) ))) return E_OUTOFMEMORY; + + impl->IJsonValue_iface.lpVtbl = &json_value_vtbl; + impl->ref = 1; + impl->json_value_type = JsonValueType_Boolean; + impl->boolean_value = input != FALSE; + + *value = &impl->IJsonValue_iface; + TRACE( "created IJsonValue %p.\n", *value ); + return S_OK; } static HRESULT WINAPI json_value_statics_CreateNumberValue( IJsonValueStatics *iface, DOUBLE input, IJsonValue **value ) { - FIXME( "iface %p, input %f, value %p stub!\n", iface, input, value ); - return E_NOTIMPL; + struct json_value *impl; + + TRACE( "iface %p, input %f, value %p\n", iface, input, value ); + + if (!value) return E_POINTER; + if (!(impl = calloc( 1, sizeof(*impl) ))) return E_OUTOFMEMORY; + + impl->IJsonValue_iface.lpVtbl = &json_value_vtbl; + impl->ref = 1; + impl->json_value_type = JsonValueType_Number; + impl->number_value = input; + + *value = &impl->IJsonValue_iface; + TRACE( "created IJsonValue %p.\n", *value ); + return S_OK; } static HRESULT WINAPI json_value_statics_CreateStringValue( IJsonValueStatics *iface, HSTRING input, IJsonValue **value ) diff --git a/dlls/windows.web/tests/web.c b/dlls/windows.web/tests/web.c index 8b91ddfe530..67e82dbe63e 100644 --- a/dlls/windows.web/tests/web.c +++ b/dlls/windows.web/tests/web.c @@ -661,6 +661,30 @@ static void test_JsonValueStatics(void) hr = IActivationFactory_QueryInterface( factory, &IID_IJsonValueStatics, (void **)&json_value_statics ); ok( hr == S_OK, "got hr %#lx.\n", hr ); + hr = IJsonValueStatics_CreateBooleanValue( json_value_statics, FALSE, NULL ); + ok( hr == E_POINTER, "got hr %#lx.\n", hr ); + hr = IJsonValueStatics_CreateBooleanValue( json_value_statics, FALSE, &json_value ); + ok( hr == S_OK, "got hr %#lx,\n", hr ); + hr = IJsonValue_get_ValueType( json_value, NULL ); + ok( hr == E_POINTER, "got hr %#lx.\n", hr ); + hr = IJsonValue_get_ValueType( json_value, &json_value_type ); + ok( json_value_type == JsonValueType_Boolean, "got JsonValueType %d.\n", json_value_type ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + ref = IJsonValue_Release( json_value ); + ok( ref == 0, "got ref %ld.\n", ref ); + + hr = IJsonValueStatics_CreateNumberValue( json_value_statics, 0, NULL ); + ok( hr == E_POINTER, "got hr %#lx.\n", hr ); + hr = IJsonValueStatics_CreateNumberValue( json_value_statics, 0, &json_value ); + ok( hr == S_OK, "got hr %#lx,\n", hr ); + hr = IJsonValue_get_ValueType( json_value, NULL ); + ok( hr == E_POINTER, "got hr %#lx.\n", hr ); + hr = IJsonValue_get_ValueType( json_value, &json_value_type ); + ok( json_value_type == JsonValueType_Number, "got JsonValueType %d.\n", json_value_type ); + ok( hr == S_OK, "got hr %#lx.\n", hr ); + ref = IJsonValue_Release( json_value ); + ok( ref == 0, "got ref %ld.\n", ref ); + hr = IJsonValueStatics_CreateStringValue( json_value_statics, NULL, &json_value ); ok( hr == S_OK, "got hr %#lx.\n", hr ); hr = IJsonValue_get_ValueType( json_value, NULL ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10263
This looked good but I still thought we could tweak a bit the parser to simplify error case handling. I've made some editions and created !10457. Note as well that we usually try to make sure tests pass on every commit, and avoid code that isn't called, so I also reordered the commits to support parsing earlier, then the array/object element getters. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10263#note_133855
This merge request was closed by Olivia Ryan. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10263
participants (3)
-
Olivia Ryan -
Olivia Ryan (@olivi-r) -
Rémi Bernon (@rbernon)