Modified: trunk/Source/_javascript_Core/API/glib/JSCException.cpp (235022 => 235023)
--- trunk/Source/_javascript_Core/API/glib/JSCException.cpp 2018-08-20 00:53:34 UTC (rev 235022)
+++ trunk/Source/_javascript_Core/API/glib/JSCException.cpp 2018-08-20 06:57:12 UTC (rev 235023)
@@ -42,9 +42,12 @@
JSCContext* context;
JSC::Strong<JSC::JSObject> jsException;
bool cached;
+ GUniquePtr<char> errorName;
GUniquePtr<char> message;
unsigned lineNumber;
+ unsigned columnNumber;
GUniquePtr<char> sourceURI;
+ GUniquePtr<char> backtrace;
};
WEBKIT_DEFINE_TYPE(JSCException, jsc_exception, G_TYPE_OBJECT)
@@ -95,15 +98,24 @@
priv->cached = true;
auto value = jscContextGetOrCreateValue(priv->context, toRef(priv->jsException.get()));
- auto propertyValue = adoptGRef(jsc_value_object_get_property(value.get(), "message"));
+ auto propertyValue = adoptGRef(jsc_value_object_get_property(value.get(), "name"));
if (!jsc_value_is_undefined(propertyValue.get()))
+ priv->errorName.reset(jsc_value_to_string(propertyValue.get()));
+ propertyValue = adoptGRef(jsc_value_object_get_property(value.get(), "message"));
+ if (!jsc_value_is_undefined(propertyValue.get()))
priv->message.reset(jsc_value_to_string(propertyValue.get()));
propertyValue = adoptGRef(jsc_value_object_get_property(value.get(), "line"));
if (!jsc_value_is_undefined(propertyValue.get()))
priv->lineNumber = jsc_value_to_int32(propertyValue.get());
+ propertyValue = adoptGRef(jsc_value_object_get_property(value.get(), "column"));
+ if (!jsc_value_is_undefined(propertyValue.get()))
+ priv->columnNumber = jsc_value_to_int32(propertyValue.get());
propertyValue = adoptGRef(jsc_value_object_get_property(value.get(), "sourceURL"));
if (!jsc_value_is_undefined(propertyValue.get()))
priv->sourceURI.reset(jsc_value_to_string(propertyValue.get()));
+ propertyValue = adoptGRef(jsc_value_object_get_property(value.get(), "stack"));
+ if (!jsc_value_is_undefined(propertyValue.get()))
+ priv->backtrace.reset(jsc_value_to_string(propertyValue.get()));
}
/**
@@ -113,10 +125,25 @@
*
* Create a new #JSCException in @context with @message.
*
- * Returns: (transfer full): a new JSCException.
+ * Returns: (transfer full): a new #JSCException.
*/
JSCException* jsc_exception_new(JSCContext* context, const char* message)
{
+ return jsc_exception_new_with_name(context, nullptr, message);
+}
+
+/**
+ * jsc_exception_new_with_name:
+ * @context: a #JSCContext
+ * @name: the error name
+ * @message: the error message
+ *
+ * Create a new #JSCException in @context with @name and @message.
+ *
+ * Returns: (transfer full): a new #JSCException.
+ */
+JSCException* jsc_exception_new_with_name(JSCContext* context, const char* name, const char* message)
+{
g_return_val_if_fail(JSC_IS_CONTEXT(context), nullptr);
auto* jsContext = jscContextGetJSContext(context);
@@ -125,10 +152,37 @@
JSRetainPtr<JSStringRef> jsMessageString(Adopt, JSStringCreateWithUTF8CString(message));
jsMessage = JSValueMakeString(jsContext, jsMessageString.get());
}
- return jscExceptionCreate(context, JSObjectMakeError(jsContext, jsMessage ? 1 : 0, &jsMessage, nullptr)).leakRef();
+
+ auto exception = jscExceptionCreate(context, JSObjectMakeError(jsContext, jsMessage ? 1 : 0, &jsMessage, nullptr));
+ if (name) {
+ auto value = jscContextGetOrCreateValue(context, toRef(exception->priv->jsException.get()));
+ GRefPtr<JSCValue> nameValue = adoptGRef(jsc_value_new_string(context, name));
+ jsc_value_object_set_property(value.get(), "name", nameValue.get());
+ }
+
+ return exception.leakRef();
}
/**
+ * jsc_exception_get_name:
+ * @exception: a #JSCException
+ *
+ * Get the error name of @exception
+ *
+ * Returns: the @exception error name.
+ */
+const char* jsc_exception_get_name(JSCException* exception)
+{
+ g_return_val_if_fail(JSC_IS_EXCEPTION(exception), nullptr);
+
+ JSCExceptionPrivate* priv = exception->priv;
+ g_return_val_if_fail(priv->context, nullptr);
+
+ jscExceptionEnsureProperties(exception);
+ return priv->errorName.get();
+}
+
+/**
* jsc_exception_get_message:
* @exception: a #JSCException
*
@@ -167,6 +221,25 @@
}
/**
+ * jsc_exception_get_column_number:
+ * @exception: a #JSCException
+ *
+ * Get the column number at which @exception happened.
+ *
+ * Returns: the column number of @exception.
+ */
+guint jsc_exception_get_column_number(JSCException* exception)
+{
+ g_return_val_if_fail(JSC_IS_EXCEPTION(exception), 0);
+
+ JSCExceptionPrivate* priv = exception->priv;
+ g_return_val_if_fail(priv->context, 0);
+
+ jscExceptionEnsureProperties(exception);
+ return priv->columnNumber;
+}
+
+/**
* jsc_exception_get_source_uri:
* @exception: a #JSCException
*
@@ -184,3 +257,80 @@
jscExceptionEnsureProperties(exception);
return priv->sourceURI.get();
}
+
+/**
+ * jsc_exception_get_backtrace_string:
+ * @exception: a #JSCException
+ *
+ * Get a string with the exception backtrace.
+ *
+ * Returns: (nullable): the exception backtrace string or %NULL.
+ */
+const char* jsc_exception_get_backtrace_string(JSCException* exception)
+{
+ g_return_val_if_fail(JSC_IS_EXCEPTION(exception), nullptr);
+
+ JSCExceptionPrivate* priv = exception->priv;
+ g_return_val_if_fail(priv->context, nullptr);
+
+ jscExceptionEnsureProperties(exception);
+ return priv->backtrace.get();
+}
+
+/**
+ * jsc_exception_to_string:
+ * @exception: a #JSCException
+ *
+ * Get the string representation of @exception error.
+ *
+ * Returns: (transfer full): the string representation of @exception.
+ */
+char* jsc_exception_to_string(JSCException* exception)
+{
+ g_return_val_if_fail(JSC_IS_EXCEPTION(exception), nullptr);
+
+ JSCExceptionPrivate* priv = exception->priv;
+ g_return_val_if_fail(priv->context, nullptr);
+
+ auto value = jscContextGetOrCreateValue(priv->context, toRef(priv->jsException.get()));
+ return jsc_value_to_string(value.get());
+}
+
+/**
+ * jsc_exception_report:
+ * @exception: a #JSCException
+ *
+ * Return a report message of @exception, containing all the possible details such us
+ * source URI, line, column and backtrace, and formatted to be printed.
+ *
+ * Returns: (transfer full): a new string with the exception report
+ */
+char* jsc_exception_report(JSCException* exception)
+{
+ g_return_val_if_fail(JSC_IS_EXCEPTION(exception), nullptr);
+
+ JSCExceptionPrivate* priv = exception->priv;
+ g_return_val_if_fail(priv->context, nullptr);
+
+ jscExceptionEnsureProperties(exception);
+ GString* report = g_string_new(nullptr);
+ if (priv->sourceURI)
+ report = g_string_append(report, priv->sourceURI.get());
+ if (priv->lineNumber)
+ g_string_append_printf(report, ":%d", priv->lineNumber);
+ if (priv->columnNumber)
+ g_string_append_printf(report, ":%d", priv->columnNumber);
+ report = g_string_append_c(report, ' ');
+ GUniquePtr<char> errorMessage(jsc_exception_to_string(exception));
+ if (errorMessage)
+ report = g_string_append(report, errorMessage.get());
+ report = g_string_append_c(report, '\n');
+
+ if (priv->backtrace) {
+ GUniquePtr<char*> lines(g_strsplit(priv->backtrace.get(), "\n", 0));
+ for (unsigned i = 0; lines.get()[i]; ++i)
+ g_string_append_printf(report, " %s\n", lines.get()[i]);
+ }
+
+ return g_string_free(report, FALSE);
+}
Modified: trunk/Tools/TestWebKitAPI/Tests/_javascript_Core/glib/TestJSC.cpp (235022 => 235023)
--- trunk/Tools/TestWebKitAPI/Tests/_javascript_Core/glib/TestJSC.cpp 2018-08-20 00:53:34 UTC (rev 235022)
+++ trunk/Tools/TestWebKitAPI/Tests/_javascript_Core/glib/TestJSC.cpp 2018-08-20 06:57:12 UTC (rev 235023)
@@ -777,9 +777,11 @@
g_assert_cmpuint(jsc_context_check_syntax(context.get(), "f = 42; b =", -1, JSC_CHECK_SYNTAX_MODE_SCRIPT, nullptr, 0, &exception.outPtr()), ==, JSC_CHECK_SYNTAX_RESULT_RECOVERABLE_ERROR);
checker.watch(exception.get());
g_assert_true(JSC_IS_EXCEPTION(exception.get()));
+ g_assert_cmpstr(jsc_exception_get_name(exception.get()), ==, "SyntaxError");
g_assert_cmpstr(jsc_exception_get_message(exception.get()), ==, "Unexpected end of script");
g_assert_cmpuint(jsc_exception_get_line_number(exception.get()), ==, 1);
- g_assert_false(jsc_exception_get_source_uri(exception.get()));
+ g_assert_null(jsc_exception_get_source_uri(exception.get()));
+ g_assert_null(jsc_exception_get_backtrace_string(exception.get()));
GRefPtr<JSCValue> globalObject = adoptGRef(jsc_context_get_global_object(context.get()));
checker.watch(globalObject.get());
g_assert_false(jsc_value_object_has_property(globalObject.get(), "f"));
@@ -798,8 +800,8 @@
g_assert_cmpuint(jsc_context_check_syntax(context.get(), "f ==== 42", -1, JSC_CHECK_SYNTAX_MODE_SCRIPT, "file:///foo/script.js", 2, &exception.outPtr()), ==, JSC_CHECK_SYNTAX_RESULT_IRRECOVERABLE_ERROR);
checker.watch(exception.get());
g_assert_true(JSC_IS_EXCEPTION(exception.get()));
+ g_assert_cmpstr(jsc_exception_get_name(exception.get()), ==, "SyntaxError");
g_assert_cmpstr(jsc_exception_get_message(exception.get()), ==, "Unexpected token '='");
- g_assert_cmpuint(jsc_exception_get_line_number(exception.get()), ==, 2);
g_assert_cmpstr(jsc_exception_get_source_uri(exception.get()), ==, "file:///foo/script.js");
g_assert_cmpuint(jsc_context_check_syntax(context.get(), "f := 42", -1, JSC_CHECK_SYNTAX_MODE_SCRIPT, nullptr, 0, nullptr), ==, JSC_CHECK_SYNTAX_RESULT_IRRECOVERABLE_ERROR);
@@ -2488,6 +2490,11 @@
jsc_context_throw(jsc_context_get_current(), "API exception");
}
+static void createCustomError()
+{
+ jsc_context_throw_with_name(jsc_context_get_current(), "CustomAPIError", "API custom exception");
+}
+
static void testJSCExceptions()
{
{
@@ -2503,9 +2510,16 @@
auto* exception = jsc_context_get_exception(context.get());
g_assert_true(JSC_IS_EXCEPTION(exception));
checker.watch(exception);
+ g_assert_cmpstr(jsc_exception_get_name(exception), ==, "ReferenceError");
g_assert_cmpstr(jsc_exception_get_message(exception), ==, "Can't find variable: foo");
g_assert_cmpuint(jsc_exception_get_line_number(exception), ==, 1);
- g_assert_false(jsc_exception_get_source_uri(exception));
+ g_assert_cmpuint(jsc_exception_get_column_number(exception), ==, 4);
+ g_assert_null(jsc_exception_get_source_uri(exception));
+ g_assert_cmpstr(jsc_exception_get_backtrace_string(exception), ==, "global code");
+ GUniquePtr<char> errorString(jsc_exception_to_string(exception));
+ g_assert_cmpstr(errorString.get(), ==, "ReferenceError: Can't find variable: foo");
+ GUniquePtr<char> reportString(jsc_exception_report(exception));
+ g_assert_cmpstr(reportString.get(), ==, ":1:4 ReferenceError: Can't find variable: foo\n global code\n");
jsc_context_clear_exception(context.get());
g_assert_null(jsc_context_get_exception(context.get()));
@@ -2525,6 +2539,7 @@
g_assert_true(JSC_IS_EXCEPTION(exception));
checker.watch(exception);
g_assert_cmpuint(jsc_exception_get_line_number(exception), ==, 2);
+ g_assert_cmpuint(jsc_exception_get_column_number(exception), ==, 4);
jsc_context_clear_exception(context.get());
g_assert_null(jsc_context_get_exception(context.get()));
@@ -2536,7 +2551,18 @@
checker.watch(context.get());
g_assert_false(jsc_context_get_exception(context.get()));
- GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate_with_source_uri(context.get(), "foo", -1, "file:///foo/script.js", 3));
+ GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate_with_source_uri(context.get(),
+ "let a = 25;\n"
+ "function foo() {\n"
+ " let b = baz();\n"
+ " return b;\n"
+ "}\n"
+ "function bar() {\n"
+ " let c = 75;\n"
+ " return foo();\n"
+ "}\n"
+ "let d = bar();\n",
+ -1, "file:///foo/script.js", 1));
checker.watch(result.get());
g_assert_true(jsc_value_is_undefined(result.get()));
@@ -2543,8 +2569,16 @@
auto* exception = jsc_context_get_exception(context.get());
g_assert_true(JSC_IS_EXCEPTION(exception));
checker.watch(exception);
+ g_assert_cmpstr(jsc_exception_get_name(exception), ==, "ReferenceError");
+ g_assert_cmpstr(jsc_exception_get_message(exception), ==, "Can't find variable: baz");
g_assert_cmpstr(jsc_exception_get_source_uri(exception), ==, "file:///foo/script.js");
g_assert_cmpuint(jsc_exception_get_line_number(exception), ==, 3);
+ g_assert_cmpuint(jsc_exception_get_column_number(exception), ==, 16);
+ g_assert_cmpstr(jsc_exception_get_backtrace_string(exception), ==, "foo@file:///foo/script.js:3:16\nbar@file:///foo/script.js:8:15\nglobal code@file:///foo/script.js:10:12");
+ GUniquePtr<char> errorString(jsc_exception_to_string(exception));
+ g_assert_cmpstr(errorString.get(), ==, "ReferenceError: Can't find variable: baz");
+ GUniquePtr<char> reportString(jsc_exception_report(exception));
+ g_assert_cmpstr(reportString.get(), ==, "file:///foo/script.js:3:16 ReferenceError: Can't find variable: baz\n foo@file:///foo/script.js:3:16\n bar@file:///foo/script.js:8:15\n global code@file:///foo/script.js:10:12\n");
jsc_context_clear_exception(context.get());
g_assert_null(jsc_context_get_exception(context.get()));
@@ -2573,9 +2607,16 @@
auto* exception = jsc_context_get_exception(context.get());
g_assert_true(JSC_IS_EXCEPTION(exception));
checker.watch(exception);
+ g_assert_cmpstr(jsc_exception_get_name(exception), ==, "Error");
g_assert_cmpstr(jsc_exception_get_message(exception), ==, "API exception");
g_assert_cmpuint(jsc_exception_get_line_number(exception), ==, 1);
- g_assert_false(jsc_exception_get_source_uri(exception));
+ g_assert_cmpuint(jsc_exception_get_column_number(exception), ==, 24);
+ g_assert_null(jsc_exception_get_source_uri(exception));
+ g_assert_cmpstr(jsc_exception_get_backtrace_string(exception), ==, "createError@[native code]\nglobal code");
+ GUniquePtr<char> errorString(jsc_exception_to_string(exception));
+ g_assert_cmpstr(errorString.get(), ==, "Error: API exception");
+ GUniquePtr<char> reportString(jsc_exception_report(exception));
+ g_assert_cmpstr(reportString.get(), ==, ":1:24 Error: API exception\n createError@[native code]\n global code\n");
jsc_context_clear_exception(context.get());
g_assert_null(jsc_context_get_exception(context.get()));
@@ -2587,6 +2628,61 @@
checker.watch(context.get());
g_assert_false(jsc_context_get_exception(context.get()));
+ GRefPtr<JSCValue> function = adoptGRef(jsc_value_new_function(context.get(), "createCustomError", G_CALLBACK(createCustomError), nullptr, nullptr, G_TYPE_NONE, 0, G_TYPE_NONE));
+ checker.watch(function.get());
+ jsc_context_set_value(context.get(), "createCustomError", function.get());
+
+ GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "var result; createCustomError(); result = 'No exception';", -1));
+ checker.watch(result.get());
+ g_assert_true(jsc_value_is_undefined(result.get()));
+ auto* exception = jsc_context_get_exception(context.get());
+ g_assert_true(JSC_IS_EXCEPTION(exception));
+ checker.watch(exception);
+ g_assert_cmpstr(jsc_exception_get_name(exception), ==, "CustomAPIError");
+ g_assert_cmpstr(jsc_exception_get_message(exception), ==, "API custom exception");
+ g_assert_cmpuint(jsc_exception_get_line_number(exception), ==, 1);
+ g_assert_cmpuint(jsc_exception_get_column_number(exception), ==, 30);
+ g_assert_null(jsc_exception_get_source_uri(exception));
+ g_assert_cmpstr(jsc_exception_get_backtrace_string(exception), ==, "createCustomError@[native code]\nglobal code");
+ GUniquePtr<char> errorString(jsc_exception_to_string(exception));
+ g_assert_cmpstr(errorString.get(), ==, "CustomAPIError: API custom exception");
+ GUniquePtr<char> reportString(jsc_exception_report(exception));
+ g_assert_cmpstr(reportString.get(), ==, ":1:30 CustomAPIError: API custom exception\n createCustomError@[native code]\n global code\n");
+
+ jsc_context_clear_exception(context.get());
+ g_assert_null(jsc_context_get_exception(context.get()));
+ }
+
+ {
+ LeakChecker checker;
+ GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
+ checker.watch(context.get());
+ g_assert_false(jsc_context_get_exception(context.get()));
+
+ GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate_with_source_uri(context.get(), "foo", -1, "file:///foo/script.js", 3));
+ checker.watch(result.get());
+
+ g_assert_true(jsc_value_is_undefined(result.get()));
+ auto* exception = jsc_context_get_exception(context.get());
+ g_assert_true(JSC_IS_EXCEPTION(exception));
+ checker.watch(exception);
+ g_assert_cmpstr(jsc_exception_get_source_uri(exception), ==, "file:///foo/script.js");
+ g_assert_cmpuint(jsc_exception_get_line_number(exception), ==, 3);
+ g_assert_cmpuint(jsc_exception_get_column_number(exception), ==, 4);
+ g_assert_cmpstr(jsc_exception_get_backtrace_string(exception), ==, "global code@file:///foo/script.js:3:4");
+ GUniquePtr<char> reportString(jsc_exception_report(exception));
+ g_assert_cmpstr(reportString.get(), ==, "file:///foo/script.js:3:4 ReferenceError: Can't find variable: foo\n global code@file:///foo/script.js:3:4\n");
+
+ jsc_context_clear_exception(context.get());
+ g_assert_null(jsc_context_get_exception(context.get()));
+ }
+
+ {
+ LeakChecker checker;
+ GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
+ checker.watch(context.get());
+ g_assert_false(jsc_context_get_exception(context.get()));
+
struct Test {
JSCContext* context;
GRefPtr<JSCException> exception;
@@ -2610,9 +2706,13 @@
g_assert_false(jsc_context_get_exception(context.get()));
g_assert_true(JSC_IS_EXCEPTION(test.exception.get()));
checker.watch(test.exception.get());
+ g_assert_cmpstr(jsc_exception_get_name(test.exception.get()), ==, "ReferenceError");
g_assert_cmpstr(jsc_exception_get_message(test.exception.get()), ==, "Can't find variable: foo");
g_assert_cmpuint(jsc_exception_get_line_number(test.exception.get()), ==, 1);
+ g_assert_cmpuint(jsc_exception_get_column_number(test.exception.get()), ==, 4);
g_assert_false(jsc_exception_get_source_uri(test.exception.get()));
+ GUniquePtr<char> errorString(jsc_exception_to_string(test.exception.get()));
+ g_assert_cmpstr(errorString.get(), ==, "ReferenceError: Can't find variable: foo");
g_assert_false(test.wasDeleted);
jsc_context_pop_exception_handler(context.get());