Modified: trunk/Source/_javascript_Core/API/glib/JSCValue.cpp (254330 => 254331)
--- trunk/Source/_javascript_Core/API/glib/JSCValue.cpp 2020-01-10 08:45:56 UTC (rev 254330)
+++ trunk/Source/_javascript_Core/API/glib/JSCValue.cpp 2020-01-10 10:53:29 UTC (rev 254331)
@@ -28,6 +28,7 @@
#include "JSCInlines.h"
#include "JSCValuePrivate.h"
#include "JSRetainPtr.h"
+#include "LiteralParser.h"
#include "OpaqueJSString.h"
#include <gobject/gvaluecollector.h>
#include <wtf/glib/GRefPtr.h>
@@ -1446,3 +1447,83 @@
return jscContextGetOrCreateValue(priv->context.get(), result).leakRef();
}
+
+/**
+ * jsc_value_new_from_json:
+ * @context: a #JSCContext
+ * @json: the JSON string to be parsed
+ *
+ * Create a new #JSCValue referencing a new value created by parsing @json.
+ *
+ * Returns: (transfer full): a #JSCValue.
+ *
+ * Since: 2.28
+ */
+JSCValue* jsc_value_new_from_json(JSCContext* context, const char* json)
+{
+ g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
+
+ if (!json)
+ return jsc_value_new_null(context);
+
+ auto* jsContext = jscContextGetJSContext(context);
+ JSC::JSGlobalObject* globalObject = toJS(jsContext);
+ JSC::JSLockHolder locker(globalObject);
+
+ JSValueRef exception = nullptr;
+ JSC::JSValue jsValue;
+ String jsonString = String::fromUTF8(json);
+ if (jsonString.is8Bit()) {
+ JSC::LiteralParser<LChar> jsonParser(globalObject, jsonString.characters8(), jsonString.length(), JSC::StrictJSON);
+ jsValue = jsonParser.tryLiteralParse();
+ if (!jsValue)
+ exception = toRef(JSC::createSyntaxError(globalObject, jsonParser.getErrorMessage()));
+ } else {
+ JSC::LiteralParser<UChar> jsonParser(globalObject, jsonString.characters16(), jsonString.length(), JSC::StrictJSON);
+ jsValue = jsonParser.tryLiteralParse();
+ if (!jsValue)
+ exception = toRef(JSC::createSyntaxError(globalObject, jsonParser.getErrorMessage()));
+ }
+
+ if (exception) {
+ jscContextHandleExceptionIfNeeded(context, exception);
+ return nullptr;
+ }
+
+ return jsValue ? jscContextGetOrCreateValue(context, toRef(globalObject, jsValue)).leakRef() : nullptr;
+}
+
+/**
+ * jsc_value_to_json:
+ * @value: a #JSCValue
+ * @indent: The number of spaces to indent when nesting.
+ *
+ * Create a JSON string of @value serialization. If @indent is 0, the resulting JSON will
+ * not contain newlines. The size of the indent is clamped to 10 spaces.
+ *
+ * Returns: (transfer full): a null-terminated JSON string with serialization of @value
+ *
+ * Since: 2.28
+ */
+char* jsc_value_to_json(JSCValue* value, unsigned indent)
+{
+ g_return_val_if_fail(JSC_IS_VALUE(value), nullptr);
+
+ JSCValuePrivate* priv = value->priv;
+ JSValueRef exception = nullptr;
+ JSRetainPtr<JSStringRef> jsJSON(Adopt, JSValueCreateJSONString(jscContextGetJSContext(priv->context.get()), priv->jsValue, indent, &exception));
+ if (jscContextHandleExceptionIfNeeded(priv->context.get(), exception))
+ return nullptr;
+
+ if (!jsJSON)
+ return nullptr;
+
+ size_t maxSize = JSStringGetMaximumUTF8CStringSize(jsJSON.get());
+ auto* json = static_cast<char*>(g_malloc(maxSize));
+ if (!JSStringGetUTF8CString(jsJSON.get(), json, maxSize)) {
+ g_free(json);
+ return nullptr;
+ }
+
+ return json;
+}
Modified: trunk/Tools/TestWebKitAPI/Tests/_javascript_Core/glib/TestJSC.cpp (254330 => 254331)
--- trunk/Tools/TestWebKitAPI/Tests/_javascript_Core/glib/TestJSC.cpp 2020-01-10 08:45:56 UTC (rev 254330)
+++ trunk/Tools/TestWebKitAPI/Tests/_javascript_Core/glib/TestJSC.cpp 2020-01-10 10:53:29 UTC (rev 254331)
@@ -3737,6 +3737,144 @@
}
#endif
+static void testJSCJSON()
+{
+ {
+ LeakChecker checker;
+ GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
+ checker.watch(context.get());
+ ExceptionHandler exceptionHandler(context.get());
+
+ GRefPtr<JSCValue> value = adoptGRef(jsc_value_new_from_json(context.get(), nullptr));
+ checker.watch(value.get());
+ g_assert_true(jsc_value_is_null(value.get()));
+
+ value = adoptGRef(jsc_value_new_from_json(context.get(), "25"));
+ checker.watch(value.get());
+ g_assert_true(jsc_value_is_number(value.get()));
+ g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 25);
+
+ value = adoptGRef(jsc_value_new_from_json(context.get(), "2.5"));
+ checker.watch(value.get());
+ g_assert_true(jsc_value_is_number(value.get()));
+ g_assert_cmpfloat(jsc_value_to_double(value.get()), ==, 2.5);
+
+ value = adoptGRef(jsc_value_new_from_json(context.get(), "true"));
+ checker.watch(value.get());
+ g_assert_true(jsc_value_is_boolean(value.get()));
+ g_assert_true(jsc_value_to_boolean(value.get()));
+
+ value = adoptGRef(jsc_value_new_from_json(context.get(), "false"));
+ checker.watch(value.get());
+ g_assert_true(jsc_value_is_boolean(value.get()));
+ g_assert_false(jsc_value_to_boolean(value.get()));
+
+ value = adoptGRef(jsc_value_new_from_json(context.get(), "\"\""));
+ checker.watch(value.get());
+ g_assert_true(jsc_value_is_string(value.get()));
+ GUniquePtr<char> valueString(jsc_value_to_string(value.get()));
+ g_assert_cmpstr(valueString.get(), ==, "");
+
+ value = adoptGRef(jsc_value_new_from_json(context.get(), "\"Foo\""));
+ checker.watch(value.get());
+ g_assert_true(jsc_value_is_string(value.get()));
+ valueString.reset(jsc_value_to_string(value.get()));
+ g_assert_cmpstr(valueString.get(), ==, "Foo");
+
+ value = adoptGRef(jsc_value_new_from_json(context.get(), "[1,2]"));
+ checker.watch(value.get());
+ g_assert_true(jsc_value_is_array(value.get()));
+ GRefPtr<JSCValue> arrayLength = adoptGRef(jsc_value_object_get_property(value.get(), "length"));
+ checker.watch(arrayLength.get());
+ g_assert_true(jsc_value_is_number(arrayLength.get()));
+ g_assert_cmpint(jsc_value_to_int32(arrayLength.get()), ==, 2);
+ valueString.reset(jsc_value_to_string(value.get()));
+ g_assert_cmpstr(valueString.get(), ==, "1,2");
+
+ value = adoptGRef(jsc_value_new_from_json(context.get(), "{\"foo\":42}"));
+ checker.watch(value.get());
+ g_assert_true(jsc_value_is_object(value.get()));
+ GRefPtr<JSCValue> property = adoptGRef(jsc_value_object_get_property(value.get(), "foo"));
+ checker.watch(property.get());
+ g_assert_true(jsc_value_is_number(property.get()));
+ g_assert_cmpint(jsc_value_to_int32(property.get()), ==, 42);
+
+ bool didThrow = false;
+ g_assert_throw_begin(exceptionHandler, didThrow);
+ value = adoptGRef(jsc_value_new_from_json(context.get(), "[1,"));
+ g_assert_null(value.get());
+ g_assert_did_throw(exceptionHandler, didThrow);
+ }
+
+ {
+ LeakChecker checker;
+ GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
+ checker.watch(context.get());
+ ExceptionHandler exceptionHandler(context.get());
+
+ GRefPtr<JSCValue> value = adoptGRef(jsc_value_new_null(context.get()));
+ checker.watch(value.get());
+ GUniquePtr<char> jsonString(jsc_value_to_json(value.get(), 0));
+ g_assert_cmpstr(jsonString.get(), ==, "null");
+
+ value = adoptGRef(jsc_value_new_undefined(context.get()));
+ checker.watch(value.get());
+ g_assert_null(jsc_value_to_json(value.get(), 0));
+
+ value = adoptGRef(jsc_value_new_number(context.get(), 25));
+ checker.watch(value.get());
+ jsonString.reset(jsc_value_to_json(value.get(), 0));
+ g_assert_cmpstr(jsonString.get(), ==, "25");
+
+ value = adoptGRef(jsc_value_new_number(context.get(), 2.5));
+ checker.watch(value.get());
+ jsonString.reset(jsc_value_to_json(value.get(), 0));
+ g_assert_cmpstr(jsonString.get(), ==, "2.5");
+
+ value = adoptGRef(jsc_value_new_boolean(context.get(), TRUE));
+ checker.watch(value.get());
+ jsonString.reset(jsc_value_to_json(value.get(), 0));
+ g_assert_cmpstr(jsonString.get(), ==, "true");
+
+ value = adoptGRef(jsc_value_new_boolean(context.get(), FALSE));
+ checker.watch(value.get());
+ jsonString.reset(jsc_value_to_json(value.get(), 0));
+ g_assert_cmpstr(jsonString.get(), ==, "false");
+
+ value = adoptGRef(jsc_value_new_string(context.get(), nullptr));
+ checker.watch(value.get());
+ jsonString.reset(jsc_value_to_json(value.get(), 0));
+ g_assert_cmpstr(jsonString.get(), ==, "\"\"");
+
+ value = adoptGRef(jsc_value_new_string(context.get(), "Foo"));
+ checker.watch(value.get());
+ jsonString.reset(jsc_value_to_json(value.get(), 0));
+ g_assert_cmpstr(jsonString.get(), ==, "\"Foo\"");
+
+ value = adoptGRef(jsc_value_new_array(context.get(), G_TYPE_INT, 1, G_TYPE_INT, 2, G_TYPE_NONE));
+ checker.watch(value.get());
+ jsonString.reset(jsc_value_to_json(value.get(), 0));
+ g_assert_cmpstr(jsonString.get(), ==, "[1,2]");
+
+ value = adoptGRef(jsc_value_new_object(context.get(), nullptr, nullptr));
+ checker.watch(value.get());
+ GRefPtr<JSCValue> property = adoptGRef(jsc_value_new_number(context.get(), 42));
+ jsc_value_object_set_property(value.get(), "foo", property.get());
+ jsonString.reset(jsc_value_to_json(value.get(), 0));
+ g_assert_cmpstr(jsonString.get(), ==, "{\"foo\":42}");
+
+ jsonString.reset(jsc_value_to_json(value.get(), 4));
+ g_assert_cmpstr(jsonString.get(), ==, "{\n \"foo\": 42\n}");
+
+ value = adoptGRef(jsc_context_evaluate(context.get(), "({get a(){ throw '';}})", -1));
+ checker.watch(value.get());
+ bool didThrow = false;
+ g_assert_throw_begin(exceptionHandler, didThrow);
+ g_assert_null(jsc_value_to_json(value.get(), 0));
+ g_assert_did_throw(exceptionHandler, didThrow);
+ }
+}
+
int main(int argc, char** argv)
{
g_test_init(&argc, &argv, nullptr);
@@ -3761,6 +3899,7 @@
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
g_test_add_func("/jsc/autocleanups", testsJSCAutocleanups);
#endif
+ g_test_add_func("/jsc/json", testJSCJSON);
return g_test_run();
}