Modified: trunk/Source/_javascript_Core/API/glib/JSCContext.cpp (233910 => 233911)
--- trunk/Source/_javascript_Core/API/glib/JSCContext.cpp 2018-07-18 10:22:15 UTC (rev 233910)
+++ trunk/Source/_javascript_Core/API/glib/JSCContext.cpp 2018-07-18 12:54:38 UTC (rev 233911)
@@ -30,6 +30,7 @@
#include "JSRetainPtr.h"
#include "JSWithScope.h"
#include "OpaqueJSString.h"
+#include "Parser.h"
#include <wtf/glib/GUniquePtr.h>
#include <wtf/glib/WTFGType.h>
@@ -828,6 +829,114 @@
}
/**
+ * JSCCheckSyntaxMode:
+ * @JSC_CHECK_SYNTAX_MODE_SCRIPT: mode to check syntax of a script
+ * @JSC_CHECK_SYNTAX_MODE_MODULE: mode to check syntax of a module
+ *
+ * Enum values to specify a mode to check for syntax errors in jsc_context_check_syntax().
+ */
+
+/**
+ * JSCCheckSyntaxResult:
+ * @JSC_CHECK_SYNTAX_RESULT_SUCCESS: no errors
+ * @JSC_CHECK_SYNTAX_RESULT_RECOVERABLE_ERROR: recoverable syntax error
+ * @JSC_CHECK_SYNTAX_RESULT_IRRECOVERABLE_ERROR: irrecoverable syntax error
+ * @JSC_CHECK_SYNTAX_RESULT_UNTERMINATED_LITERAL_ERROR: unterminated literal error
+ * @JSC_CHECK_SYNTAX_RESULT_OUT_OF_MEMORY_ERROR: out of memory error
+ * @JSC_CHECK_SYNTAX_RESULT_STACK_OVERFLOW_ERROR: stack overflow error
+ *
+ * Enum values to specify the result of jsc_context_check_syntax().
+ */
+
+/**
+ * jsc_context_check_syntax:
+ * @context: a #JSCContext
+ * @code: a _javascript_ script to check
+ * @length: length of @code, or -1 if @code is a nul-terminated string
+ * @mode: a #JSCCheckSyntaxMode
+ * @uri: the source URI
+ * @line_number: the starting line number
+ * @exception: (out) (optional) (transfer full): return location for a #JSCException, or %NULL to ignore
+ *
+ * Check the given @code in @context for syntax errors. The @line_number is the starting line number in @uri;
+ * the value is one-based so the first line is 1. @uri and @line_number are only used to fill the @exception.
+ * In case of errors @exception will be set to a new #JSCException with the details. You can pass %NULL to
+ * @exception to ignore the error details.
+ *
+ * Returns: a #JSCCheckSyntaxResult
+ */
+JSCCheckSyntaxResult jsc_context_check_syntax(JSCContext* context, const char* code, gssize length, JSCCheckSyntaxMode mode, const char* uri, unsigned lineNumber, JSCException **exception)
+{
+ g_return_val_if_fail(JSC_IS_CONTEXT(context), JSC_CHECK_SYNTAX_RESULT_IRRECOVERABLE_ERROR);
+ g_return_val_if_fail(code, JSC_CHECK_SYNTAX_RESULT_IRRECOVERABLE_ERROR);
+ g_return_val_if_fail(!exception || !*exception, JSC_CHECK_SYNTAX_RESULT_IRRECOVERABLE_ERROR);
+
+ lineNumber = std::max<unsigned>(1, lineNumber);
+
+ auto* jsContext = context->priv->jsContext.get();
+ JSC::ExecState* exec = toJS(jsContext);
+ JSC::VM& vm = exec->vm();
+ JSC::JSLockHolder locker(vm);
+
+ String sourceURLString = uri ? String::fromUTF8(uri) : String();
+ JSC::SourceCode source = JSC::makeSource(String::fromUTF8(code, length < 0 ? strlen(code) : length), JSC::SourceOrigin { sourceURLString },
+ sourceURLString, TextPosition(OrdinalNumber::fromOneBasedInt(lineNumber), OrdinalNumber()));
+ bool success = false;
+ JSC::ParserError error;
+ switch (mode) {
+ case JSC_CHECK_SYNTAX_MODE_SCRIPT:
+ success = !!JSC::parse<JSC::ProgramNode>(&vm, source, JSC::Identifier(), JSC::JSParserBuiltinMode::NotBuiltin,
+ JSC::JSParserStrictMode::NotStrict, JSC::JSParserScriptMode::Classic, JSC::SourceParseMode::ProgramMode, JSC::SuperBinding::NotNeeded, error);
+ break;
+ case JSC_CHECK_SYNTAX_MODE_MODULE:
+ success = !!JSC::parse<JSC::ModuleProgramNode>(&vm, source, JSC::Identifier(), JSC::JSParserBuiltinMode::NotBuiltin,
+ JSC::JSParserStrictMode::Strict, JSC::JSParserScriptMode::Module, JSC::SourceParseMode::ModuleAnalyzeMode, JSC::SuperBinding::NotNeeded, error);
+ break;
+ }
+
+ JSCCheckSyntaxResult result = JSC_CHECK_SYNTAX_RESULT_SUCCESS;
+ if (success)
+ return result;
+
+ switch (error.type()) {
+ case JSC::ParserError::ErrorType::SyntaxError: {
+ switch (error.syntaxErrorType()) {
+ case JSC::ParserError::SyntaxErrorType::SyntaxErrorIrrecoverable:
+ result = JSC_CHECK_SYNTAX_RESULT_IRRECOVERABLE_ERROR;
+ break;
+ case JSC::ParserError::SyntaxErrorType::SyntaxErrorUnterminatedLiteral:
+ result = JSC_CHECK_SYNTAX_RESULT_UNTERMINATED_LITERAL_ERROR;
+ break;
+ case JSC::ParserError::SyntaxErrorType::SyntaxErrorRecoverable:
+ result = JSC_CHECK_SYNTAX_RESULT_RECOVERABLE_ERROR;
+ break;
+ case JSC::ParserError::SyntaxErrorType::SyntaxErrorNone:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+ break;
+ }
+ case JSC::ParserError::ErrorType::StackOverflow:
+ result = JSC_CHECK_SYNTAX_RESULT_STACK_OVERFLOW_ERROR;
+ break;
+ case JSC::ParserError::ErrorType::OutOfMemory:
+ result = JSC_CHECK_SYNTAX_RESULT_OUT_OF_MEMORY_ERROR;
+ break;
+ case JSC::ParserError::ErrorType::EvalError:
+ case JSC::ParserError::ErrorType::ErrorNone:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+
+ if (exception) {
+ auto* jsError = error.toErrorObject(exec->lexicalGlobalObject(), source);
+ *exception = jscExceptionCreate(context, toRef(exec, jsError)).leakRef();
+ }
+
+ return result;
+}
+
+/**
* jsc_context_get_global_object:
* @context: a #JSCContext
*
Modified: trunk/Source/_javascript_Core/API/glib/JSCContext.h (233910 => 233911)
--- trunk/Source/_javascript_Core/API/glib/JSCContext.h 2018-07-18 10:22:15 UTC (rev 233910)
+++ trunk/Source/_javascript_Core/API/glib/JSCContext.h 2018-07-18 12:54:38 UTC (rev 233911)
@@ -48,6 +48,20 @@
JSCException *exception,
gpointer user_data);
+typedef enum {
+ JSC_CHECK_SYNTAX_MODE_SCRIPT,
+ JSC_CHECK_SYNTAX_MODE_MODULE
+} JSCCheckSyntaxMode;
+
+typedef enum {
+ JSC_CHECK_SYNTAX_RESULT_SUCCESS,
+ JSC_CHECK_SYNTAX_RESULT_RECOVERABLE_ERROR,
+ JSC_CHECK_SYNTAX_RESULT_IRRECOVERABLE_ERROR,
+ JSC_CHECK_SYNTAX_RESULT_UNTERMINATED_LITERAL_ERROR,
+ JSC_CHECK_SYNTAX_RESULT_OUT_OF_MEMORY_ERROR,
+ JSC_CHECK_SYNTAX_RESULT_STACK_OVERFLOW_ERROR,
+} JSCCheckSyntaxResult;
+
struct _JSCContext {
GObject parent;
@@ -123,6 +137,15 @@
guint line_number,
JSCValue **object);
+JSC_API JSCCheckSyntaxResult
+jsc_context_check_syntax (JSCContext *context,
+ const char *code,
+ gssize length,
+ JSCCheckSyntaxMode mode,
+ const char *uri,
+ unsigned line_number,
+ JSCException **exception);
+
JSC_API JSCValue *
jsc_context_get_global_object (JSCContext *context);
Modified: trunk/Tools/TestWebKitAPI/Tests/_javascript_Core/glib/TestJSC.cpp (233910 => 233911)
--- trunk/Tools/TestWebKitAPI/Tests/_javascript_Core/glib/TestJSC.cpp 2018-07-18 10:22:15 UTC (rev 233910)
+++ trunk/Tools/TestWebKitAPI/Tests/_javascript_Core/glib/TestJSC.cpp 2018-07-18 12:54:38 UTC (rev 233911)
@@ -719,6 +719,52 @@
g_assert_true(jsc_value_is_undefined(moduleInWk.get()));
}
+static void testJSCCheckSyntax()
+{
+ LeakChecker checker;
+ GRefPtr<JSCContext> context = adoptGRef(jsc_context_new());
+ checker.watch(context.get());
+ ExceptionHandler exceptionHandler(context.get());
+
+ GRefPtr<JSCException> exception;
+ g_assert_cmpuint(jsc_context_check_syntax(context.get(), "f = 42", -1, JSC_CHECK_SYNTAX_MODE_SCRIPT, nullptr, 0, &exception.outPtr()), ==, JSC_CHECK_SYNTAX_RESULT_SUCCESS);
+ g_assert_null(exception.get());
+
+ 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_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()));
+ 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"));
+ exception = nullptr;
+
+ // Only syntax errors are checked.
+ bool didThrow = false;
+ g_assert_throw_begin(exceptionHandler, didThrow);
+ GRefPtr<JSCValue> value = adoptGRef(jsc_context_evaluate(context.get(), "f", -1));
+ checker.watch(value.get());
+ g_assert_true(jsc_value_is_undefined(value.get()));
+ g_assert_did_throw(exceptionHandler, didThrow);
+ g_assert_cmpuint(jsc_context_check_syntax(context.get(), "f", -1, JSC_CHECK_SYNTAX_MODE_SCRIPT, nullptr, 0, &exception.outPtr()), ==, JSC_CHECK_SYNTAX_RESULT_SUCCESS);
+ g_assert_null(exception.get());
+
+ 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_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);
+ g_assert_cmpuint(jsc_context_check_syntax(context.get(), "f '42;", -1, JSC_CHECK_SYNTAX_MODE_SCRIPT, nullptr, 0, nullptr), ==, JSC_CHECK_SYNTAX_RESULT_UNTERMINATED_LITERAL_ERROR);
+
+ g_assert_cmpuint(jsc_context_check_syntax(context.get(), "import foo from '/foo.js'", -1, JSC_CHECK_SYNTAX_MODE_SCRIPT, nullptr, 0, nullptr), ==, JSC_CHECK_SYNTAX_RESULT_IRRECOVERABLE_ERROR);
+ g_assert_cmpuint(jsc_context_check_syntax(context.get(), "import foo from '/foo.js'", -1, JSC_CHECK_SYNTAX_MODE_MODULE, nullptr, 0, nullptr), ==, JSC_CHECK_SYNTAX_RESULT_SUCCESS);
+}
+
static int foo(int n)
{
return n * 2;
@@ -2998,6 +3044,7 @@
g_test_add_func("/jsc/types", testJSCTypes);
g_test_add_func("/jsc/global-object", testJSCGlobalObject);
g_test_add_func("/jsc/evaluate-in-object", testJSCEvaluateInObject);
+ g_test_add_func("/jsc/check-syntax", testJSCCheckSyntax);
g_test_add_func("/jsc/function", testJSCFunction);
g_test_add_func("/jsc/object", testJSCObject);
g_test_add_func("/jsc/class", testJSCClass);