| /* |
| * Copyright (C) 2018 Igalia S.L. |
| * |
| * 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 |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "config.h" |
| |
| // Include JSCContextPrivate.h to be able to run garbage collector for testing. |
| #define JSC_COMPILATION 1 |
| #include "jsc/JSCContextPrivate.h" |
| #undef JSC_COMPILATION |
| |
| #include <jsc/jsc.h> |
| #include <wtf/HashSet.h> |
| #include <wtf/Threading.h> |
| #include <wtf/Vector.h> |
| #include <wtf/glib/GRefPtr.h> |
| #include <wtf/glib/GUniquePtr.h> |
| |
| class LeakChecker { |
| public: |
| LeakChecker() = default; |
| |
| ~LeakChecker() |
| { |
| if (m_watchedObjects.isEmpty()) |
| return; |
| |
| g_print("Leaked objects:"); |
| for (auto* object : m_watchedObjects) |
| g_print(" [%s(%p) %u refs]", g_type_name_from_instance(reinterpret_cast<GTypeInstance*>(object)), object, G_OBJECT(object)->ref_count); |
| g_print("\n"); |
| g_assert_true(m_watchedObjects.isEmpty()); |
| } |
| |
| void watch(void* object) |
| { |
| g_assert_true(G_IS_OBJECT(object)); |
| m_watchedObjects.add(object); |
| g_object_weak_ref(G_OBJECT(object), [](gpointer userData, GObject* object) { |
| static_cast<LeakChecker*>(userData)->m_watchedObjects.remove(object); |
| }, this); |
| } |
| |
| private: |
| HashSet<void*> m_watchedObjects; |
| }; |
| |
| class ExceptionHandler { |
| public: |
| ExceptionHandler(JSCContext* context) |
| : m_context(context) |
| { |
| jsc_context_push_exception_handler(m_context, [](JSCContext*, JSCException* exception, gpointer) { |
| g_error("UNEXPECTED EXCEPTION: %s", jsc_exception_get_message(exception)); |
| }, nullptr, nullptr); |
| } |
| |
| ~ExceptionHandler() |
| { |
| pop(); |
| } |
| |
| void push(JSCExceptionHandler handler, gpointer userData, GDestroyNotify destroyFunction = nullptr) |
| { |
| jsc_context_push_exception_handler(m_context, handler, userData, destroyFunction); |
| } |
| |
| void pop() |
| { |
| jsc_context_pop_exception_handler(m_context); |
| } |
| |
| private: |
| JSCContext* m_context; |
| }; |
| |
| #define g_assert_throw_begin(handler, didThrow) \ |
| didThrow = false; \ |
| handler.push([](JSCContext*, JSCException*, gpointer userData) { \ |
| *static_cast<bool*>(userData) = true; \ |
| }, &didThrow, nullptr); |
| |
| #define g_assert_did_throw(handler, didThrow) \ |
| handler.pop(); \ |
| g_assert_true(didThrow); \ |
| didThrow = false; |
| |
| extern "C" void JSSynchronousGarbageCollectForDebugging(JSContextRef); |
| |
| static void jscContextGarbageCollect(JSCContext* context) |
| { |
| JSSynchronousGarbageCollectForDebugging(jscContextGetJSContext(context)); |
| } |
| |
| static void testJSCBasic() |
| { |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| auto* defaultVM = jsc_context_get_virtual_machine(context.get()); |
| g_assert_nonnull(defaultVM); |
| checker.watch(defaultVM); |
| |
| GRefPtr<JSCVirtualMachine> vm = adoptGRef(jsc_virtual_machine_new()); |
| checker.watch(vm.get()); |
| g_assert_false(vm.get() == defaultVM); |
| |
| GRefPtr<JSCContext> context2 = adoptGRef(jsc_context_new_with_virtual_machine(vm.get())); |
| checker.watch(context2.get()); |
| ExceptionHandler exceptionHandler2(context.get()); |
| g_assert_true(jsc_context_get_virtual_machine(context2.get()) == vm.get()); |
| |
| GRefPtr<JSCContext> context3 = adoptGRef(jsc_context_new_with_virtual_machine(defaultVM)); |
| checker.watch(context3.get()); |
| ExceptionHandler exceptionHandler3(context.get()); |
| g_assert_true(jsc_context_get_virtual_machine(context3.get()) == jsc_context_get_virtual_machine(context.get())); |
| |
| GRefPtr<JSCValue> value1 = adoptGRef(jsc_value_new_number(context.get(), 25)); |
| checker.watch(value1.get()); |
| g_assert_true(jsc_value_get_context(value1.get()) == context.get()); |
| g_assert_true(jsc_value_is_number(value1.get())); |
| g_assert_cmpint(jsc_value_to_int32(value1.get()), ==, 25); |
| jsc_context_set_value(context.get(), "value1", value1.get()); |
| |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "value1", -1)); |
| checker.watch(result.get()); |
| g_assert_true(result.get() == value1.get()); |
| |
| result = adoptGRef(jsc_context_evaluate(context2.get(), "value1", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_get_context(result.get()) == context2.get()); |
| g_assert_true(jsc_value_is_undefined(result.get())); |
| |
| result = adoptGRef(jsc_context_get_value(context3.get(), "value1")); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_get_context(result.get()) == context3.get()); |
| g_assert_true(jsc_value_is_undefined(result.get())); |
| |
| jsc_context_set_value(context3.get(), "value1", value1.get()); |
| result = adoptGRef(jsc_context_evaluate(context3.get(), "value1", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_get_context(result.get()) == context3.get()); |
| g_assert_false(result.get() == value1.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 25); |
| } |
| |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "2 + 2", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_get_context(result.get()) == context.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 4); |
| |
| GRefPtr<JSCValue> result2 = adoptGRef(jsc_context_evaluate(context.get(), "2 + 2", -1)); |
| checker.watch(result2.get()); |
| g_assert_true(jsc_value_get_context(result2.get()) == context.get()); |
| g_assert_true(result.get() == result2.get()); |
| |
| GRefPtr<JSCValue> result3 = adoptGRef(jsc_context_evaluate(context.get(), "3 + 1", -1)); |
| checker.watch(result3.get()); |
| g_assert_true(jsc_value_get_context(result3.get()) == context.get()); |
| g_assert_true(result.get() == result3.get()); |
| |
| auto* resultPtr = result.get(); |
| result = nullptr; |
| result2 = nullptr; |
| result3 = nullptr; |
| GRefPtr<JSCValue> result4 = adoptGRef(jsc_context_evaluate(context.get(), "2 + 2", -1)); |
| checker.watch(result4.get()); |
| g_assert_true(jsc_value_get_context(result4.get()) == context.get()); |
| g_assert_true(jsc_value_is_number(result4.get())); |
| g_assert_cmpint(jsc_value_to_int32(result4.get()), ==, 4); |
| g_assert_false(result4.get() == resultPtr); |
| } |
| |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "2 + 2", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_get_context(result.get()) == context.get()); |
| jsc_context_set_value(context.get(), "four", result.get()); |
| |
| GRefPtr<JSCValue> value = adoptGRef(jsc_context_get_value(context.get(), "four")); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_get_context(value.get()) == context.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 4); |
| g_assert_true(result.get() == value.get()); |
| |
| value = adoptGRef(jsc_context_evaluate(context.get(), "four", -1)); |
| checker.watch(value.get()); |
| g_assert_true(result.get() == value.get()); |
| } |
| |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "two = 2; four = two + two", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_get_context(result.get()) == context.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 4); |
| |
| GRefPtr<JSCValue> value = adoptGRef(jsc_context_get_value(context.get(), "two")); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_get_context(value.get()) == context.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 2); |
| |
| value = adoptGRef(jsc_context_get_value(context.get(), "four")); |
| checker.watch(value.get()); |
| g_assert_true(result.get() == value.get()); |
| |
| GRefPtr<JSCValue> result2 = adoptGRef(jsc_context_evaluate(context.get(), "five = 4", -1)); |
| checker.watch(result2.get()); |
| g_assert_true(result2.get() == value.get()); |
| } |
| |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| GUniquePtr<char> scriptFile(g_build_filename(WEBKIT_SRC_DIR, "Tools", "TestWebKitAPI", "Tests", "JavaScriptCore", "glib", "script.js", nullptr)); |
| GUniqueOutPtr<char> contents; |
| gsize contentsSize; |
| g_assert_true(g_file_get_contents(scriptFile.get(), &contents.outPtr(), &contentsSize, nullptr)); |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), contents.get(), contentsSize)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_undefined(result.get())); |
| |
| GString* expected = g_string_new("String"); |
| expected = g_string_append_c(expected, '\0'); |
| expected = g_string_append(expected, "With"); |
| expected = g_string_append_c(expected, '\0'); |
| expected = g_string_append(expected, "Null"); |
| GRefPtr<GBytes> expectedBytes = adoptGRef(g_string_free_to_bytes(expected)); |
| |
| GRefPtr<JSCValue> value = adoptGRef(jsc_context_evaluate(context.get(), "testStringWithNull()", -1)); |
| 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(), ==, "String"); |
| GRefPtr<GBytes> valueBytes = adoptGRef(jsc_value_to_string_as_bytes(value.get())); |
| g_assert_true(g_bytes_equal(valueBytes.get(), expectedBytes.get())); |
| |
| value = adoptGRef(jsc_value_new_string_from_bytes(context.get(), expectedBytes.get())); |
| 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(), ==, "String"); |
| valueBytes = adoptGRef(jsc_value_to_string_as_bytes(value.get())); |
| g_assert_true(g_bytes_equal(valueBytes.get(), expectedBytes.get())); |
| |
| jsc_context_set_value(context.get(), "s", value.get()); |
| result = adoptGRef(jsc_context_get_value(context.get(), "s")); |
| checker.watch(result.get()); |
| g_assert_true(result.get() == value.get()); |
| } |
| } |
| |
| static void testJSCTypes() |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| GRefPtr<JSCValue> value = adoptGRef(jsc_value_new_number(context.get(), 125)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpfloat(jsc_value_to_double(value.get()), ==, 125.); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 125); |
| g_assert_true(jsc_value_to_boolean(value.get()) == TRUE); |
| GUniquePtr<char> valueString(jsc_value_to_string(value.get())); |
| g_assert_cmpstr(valueString.get(), ==, "125"); |
| jsc_context_set_value(context.get(), "i", value.get()); |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_get_value(context.get(), "i")); |
| checker.watch(result.get()); |
| g_assert_true(result.get() == value.get()); |
| |
| value = adoptGRef(jsc_value_new_number(context.get(), 12.5)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpfloat(jsc_value_to_double(value.get()), ==, 12.5); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 12); |
| g_assert_true(jsc_value_to_boolean(value.get()) == TRUE); |
| valueString.reset(jsc_value_to_string(value.get())); |
| g_assert_cmpstr(valueString.get(), ==, "12.5"); |
| jsc_context_set_value(context.get(), "d", value.get()); |
| result = adoptGRef(jsc_context_get_value(context.get(), "d")); |
| checker.watch(result.get()); |
| g_assert_true(result.get() == value.get()); |
| |
| value = adoptGRef(jsc_value_new_boolean(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()) == TRUE); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 1); |
| valueString.reset(jsc_value_to_string(value.get())); |
| g_assert_cmpstr(valueString.get(), ==, "true"); |
| jsc_context_set_value(context.get(), "b1", value.get()); |
| result = adoptGRef(jsc_context_get_value(context.get(), "b1")); |
| checker.watch(result.get()); |
| g_assert_true(result.get() == value.get()); |
| |
| value = adoptGRef(jsc_value_new_boolean(context.get(), FALSE)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_boolean(value.get())); |
| g_assert_true(jsc_value_to_boolean(value.get()) == FALSE); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 0); |
| valueString.reset(jsc_value_to_string(value.get())); |
| g_assert_cmpstr(valueString.get(), ==, "false"); |
| jsc_context_set_value(context.get(), "b2", value.get()); |
| result = adoptGRef(jsc_context_get_value(context.get(), "b2")); |
| checker.watch(result.get()); |
| g_assert_true(result.get() == value.get()); |
| |
| value = adoptGRef(jsc_value_new_string(context.get(), "Hello World")); |
| 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(), ==, "Hello World"); |
| g_assert_true(jsc_value_to_boolean(value.get()) == TRUE); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 0); |
| jsc_context_set_value(context.get(), "s1", value.get()); |
| result = adoptGRef(jsc_context_get_value(context.get(), "s1")); |
| checker.watch(result.get()); |
| g_assert_true(result.get() == value.get()); |
| |
| value = adoptGRef(jsc_value_new_string(context.get(), nullptr)); |
| 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(), ==, ""); |
| g_assert_true(jsc_value_to_boolean(value.get()) == FALSE); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 0); |
| jsc_context_set_value(context.get(), "s2", value.get()); |
| result = adoptGRef(jsc_context_get_value(context.get(), "s2")); |
| checker.watch(result.get()); |
| g_assert_true(result.get() == value.get()); |
| |
| value = adoptGRef(jsc_value_new_string(context.get(), "12.5")); |
| 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(), ==, "12.5"); |
| g_assert_true(jsc_value_to_boolean(value.get()) == TRUE); |
| g_assert_cmpfloat(jsc_value_to_double(value.get()), ==, 12.5); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 12); |
| jsc_context_set_value(context.get(), "s3", value.get()); |
| result = adoptGRef(jsc_context_get_value(context.get(), "s3")); |
| checker.watch(result.get()); |
| g_assert_true(result.get() == value.get()); |
| |
| value = adoptGRef(jsc_value_new_array(context.get(), G_TYPE_NONE)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_array(value.get())); |
| g_assert_true(jsc_value_is_object(value.get())); |
| g_assert_true(jsc_value_to_boolean(value.get()) == TRUE); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 0); |
| valueString.reset(jsc_value_to_string(value.get())); |
| g_assert_cmpstr(valueString.get(), ==, ""); |
| jsc_context_set_value(context.get(), "a1", value.get()); |
| result = adoptGRef(jsc_context_get_value(context.get(), "a1")); |
| checker.watch(result.get()); |
| g_assert_true(result.get() == 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()), ==, 0); |
| GRefPtr<JSCValue> arrayItem = adoptGRef(jsc_value_new_number(context.get(), 1)); |
| checker.watch(arrayItem.get()); |
| jsc_value_object_set_property_at_index(value.get(), jsc_value_to_int32(arrayLength.get()), arrayItem.get()); |
| 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()), ==, 1); |
| valueString.reset(jsc_value_to_string(value.get())); |
| g_assert_cmpstr(valueString.get(), ==, "1"); |
| arrayItem = adoptGRef(jsc_value_new_number(context.get(), 5)); |
| checker.watch(arrayItem.get()); |
| jsc_value_object_set_property_at_index(value.get(), jsc_value_to_int32(arrayLength.get()), arrayItem.get()); |
| 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,5"); |
| arrayItem = adoptGRef(jsc_value_new_number(context.get(), 10)); |
| checker.watch(arrayItem.get()); |
| jsc_value_object_set_property_at_index(value.get(), 3, arrayItem.get()); |
| 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()), ==, 4); |
| arrayItem = adoptGRef(jsc_value_object_get_property_at_index(value.get(), 0)); |
| checker.watch(arrayItem.get()); |
| g_assert_true(jsc_value_is_number(arrayItem.get())); |
| g_assert_cmpint(jsc_value_to_int32(arrayItem.get()), ==, 1); |
| arrayItem = adoptGRef(jsc_value_object_get_property_at_index(value.get(), 1)); |
| checker.watch(arrayItem.get()); |
| g_assert_true(jsc_value_is_number(arrayItem.get())); |
| g_assert_cmpint(jsc_value_to_int32(arrayItem.get()), ==, 5); |
| arrayItem = adoptGRef(jsc_value_object_get_property_at_index(value.get(), 2)); |
| checker.watch(arrayItem.get()); |
| g_assert_true(jsc_value_is_undefined(arrayItem.get())); |
| arrayItem = adoptGRef(jsc_value_object_get_property_at_index(value.get(), 3)); |
| checker.watch(arrayItem.get()); |
| g_assert_true(jsc_value_is_number(arrayItem.get())); |
| g_assert_cmpint(jsc_value_to_int32(arrayItem.get()), ==, 10); |
| arrayLength = adoptGRef(jsc_value_object_get_property(value.get(), "length")); |
| checker.watch(arrayLength.get()); |
| arrayItem = adoptGRef(jsc_value_object_get_property_at_index(value.get(), jsc_value_to_int32(arrayLength.get()) + 1)); |
| checker.watch(arrayItem.get()); |
| g_assert_true(jsc_value_is_undefined(arrayItem.get())); |
| |
| GRefPtr<JSCValue> array = adoptGRef(jsc_value_new_array(context.get(), G_TYPE_INT, 1, G_TYPE_STRING, "two", G_TYPE_BOOLEAN, TRUE, JSC_TYPE_VALUE, value.get(), G_TYPE_NONE)); |
| checker.watch(array.get()); |
| g_assert_true(jsc_value_is_array(array.get())); |
| g_assert_true(jsc_value_is_object(array.get())); |
| g_assert_true(jsc_value_to_boolean(array.get()) == TRUE); |
| g_assert_cmpint(jsc_value_to_int32(array.get()), ==, 0); |
| valueString.reset(jsc_value_to_string(array.get())); |
| g_assert_cmpstr(valueString.get(), ==, "1,two,true,1,5,,10"); |
| arrayLength = adoptGRef(jsc_value_object_get_property(array.get(), "length")); |
| checker.watch(arrayLength.get()); |
| g_assert_true(jsc_value_is_number(arrayLength.get())); |
| g_assert_cmpint(jsc_value_to_int32(arrayLength.get()), ==, 4); |
| arrayItem = adoptGRef(jsc_value_object_get_property_at_index(array.get(), 0)); |
| checker.watch(arrayItem.get()); |
| g_assert_true(jsc_value_is_number(arrayItem.get())); |
| g_assert_cmpint(jsc_value_to_int32(arrayItem.get()), ==, 1); |
| arrayItem = adoptGRef(jsc_value_object_get_property_at_index(array.get(), 1)); |
| checker.watch(arrayItem.get()); |
| g_assert_true(jsc_value_is_string(arrayItem.get())); |
| valueString.reset(jsc_value_to_string(arrayItem.get())); |
| g_assert_cmpstr(valueString.get(), ==, "two"); |
| arrayItem = adoptGRef(jsc_value_object_get_property_at_index(array.get(), 2)); |
| checker.watch(arrayItem.get()); |
| g_assert_true(jsc_value_is_boolean(arrayItem.get())); |
| g_assert_true(jsc_value_to_boolean(arrayItem.get()) == TRUE); |
| arrayItem = adoptGRef(jsc_value_object_get_property_at_index(array.get(), 3)); |
| checker.watch(arrayItem.get()); |
| g_assert_true(arrayItem.get() == value.get()); |
| |
| GRefPtr<GPtrArray> gArray = adoptGRef(g_ptr_array_new_with_free_func(g_object_unref)); |
| g_ptr_array_add(gArray.get(), jsc_value_new_number(context.get(), 1)); |
| g_ptr_array_add(gArray.get(), jsc_value_new_string(context.get(), "two")); |
| g_ptr_array_add(gArray.get(), jsc_value_new_boolean(context.get(), TRUE)); |
| g_ptr_array_add(gArray.get(), g_object_ref(value.get())); |
| array = adoptGRef(jsc_value_new_array_from_garray(context.get(), gArray.get())); |
| checker.watch(array.get()); |
| g_assert_true(jsc_value_is_array(array.get())); |
| g_assert_true(jsc_value_is_object(array.get())); |
| g_assert_true(jsc_value_to_boolean(array.get()) == TRUE); |
| g_assert_cmpint(jsc_value_to_int32(array.get()), ==, 0); |
| valueString.reset(jsc_value_to_string(array.get())); |
| g_assert_cmpstr(valueString.get(), ==, "1,two,true,1,5,,10"); |
| arrayLength = adoptGRef(jsc_value_object_get_property(array.get(), "length")); |
| checker.watch(arrayLength.get()); |
| g_assert_true(jsc_value_is_number(arrayLength.get())); |
| g_assert_cmpint(jsc_value_to_int32(arrayLength.get()), ==, gArray->len); |
| |
| const char* strv[] = { "one", "two", "three", nullptr }; |
| array = adoptGRef(jsc_value_new_array_from_strv(context.get(), strv)); |
| checker.watch(array.get()); |
| g_assert_true(jsc_value_is_array(array.get())); |
| g_assert_true(jsc_value_is_object(array.get())); |
| g_assert_true(jsc_value_to_boolean(array.get()) == TRUE); |
| g_assert_cmpint(jsc_value_to_int32(array.get()), ==, 0); |
| valueString.reset(jsc_value_to_string(array.get())); |
| g_assert_cmpstr(valueString.get(), ==, "one,two,three"); |
| arrayLength = adoptGRef(jsc_value_object_get_property(array.get(), "length")); |
| checker.watch(arrayLength.get()); |
| g_assert_true(jsc_value_is_number(arrayLength.get())); |
| g_assert_cmpint(jsc_value_to_int32(arrayLength.get()), ==, 3); |
| |
| value = adoptGRef(jsc_value_new_object(context.get(), nullptr, nullptr)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_object(value.get())); |
| g_assert_true(jsc_value_to_boolean(value.get()) == TRUE); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 0); |
| valueString.reset(jsc_value_to_string(value.get())); |
| g_assert_cmpstr(valueString.get(), ==, "[object Object]"); |
| jsc_context_set_value(context.get(), "o", value.get()); |
| result = adoptGRef(jsc_context_get_value(context.get(), "o")); |
| checker.watch(result.get()); |
| g_assert_true(result.get() == value.get()); |
| |
| value = adoptGRef(jsc_value_new_undefined(context.get())); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_undefined(value.get())); |
| g_assert_true(jsc_value_to_boolean(value.get()) == FALSE); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 0); |
| valueString.reset(jsc_value_to_string(value.get())); |
| g_assert_cmpstr(valueString.get(), ==, "undefined"); |
| jsc_context_set_value(context.get(), "u", value.get()); |
| result = adoptGRef(jsc_context_get_value(context.get(), "u")); |
| checker.watch(result.get()); |
| g_assert_true(result.get() == value.get()); |
| |
| value = adoptGRef(jsc_value_new_null(context.get())); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_null(value.get())); |
| g_assert_true(jsc_value_to_boolean(value.get()) == FALSE); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 0); |
| valueString.reset(jsc_value_to_string(value.get())); |
| g_assert_cmpstr(valueString.get(), ==, "null"); |
| jsc_context_set_value(context.get(), "n", value.get()); |
| result = adoptGRef(jsc_context_get_value(context.get(), "n")); |
| checker.watch(result.get()); |
| g_assert_true(result.get() == value.get()); |
| } |
| |
| static void testJSCGlobalObject() |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| GRefPtr<JSCValue> globalObject = adoptGRef(jsc_context_get_global_object(context.get())); |
| checker.watch(globalObject.get()); |
| g_assert_true(jsc_value_is_object(globalObject.get())); |
| |
| GRefPtr<JSCValue> foo = adoptGRef(jsc_value_new_number(context.get(), 25)); |
| checker.watch(foo.get()); |
| jsc_value_object_set_property(globalObject.get(), "foo", foo.get()); |
| |
| GRefPtr<JSCValue> foo2 = adoptGRef(jsc_context_get_value(context.get(), "foo")); |
| checker.watch(foo2.get()); |
| g_assert_true(foo.get() == foo2.get()); |
| |
| GRefPtr<JSCValue> bar = adoptGRef(jsc_value_new_number(context.get(), 50)); |
| checker.watch(bar.get()); |
| jsc_context_set_value(context.get(), "bar", bar.get()); |
| |
| GRefPtr<JSCValue> bar2 = adoptGRef(jsc_value_object_get_property(globalObject.get(), "bar")); |
| checker.watch(bar2.get()); |
| g_assert_true(bar.get() == bar2.get()); |
| |
| GRefPtr<JSCValue> baz = adoptGRef(jsc_context_evaluate(context.get(), "baz = 75", -1)); |
| checker.watch(baz.get()); |
| |
| GRefPtr<JSCValue> baz2 = adoptGRef(jsc_value_object_get_property(globalObject.get(), "baz")); |
| checker.watch(baz2.get()); |
| g_assert_true(baz.get() == baz2.get()); |
| |
| jsc_context_set_value(context.get(), "window", globalObject.get()); |
| GRefPtr<JSCValue> window = adoptGRef(jsc_context_evaluate(context.get(), "window", -1)); |
| checker.watch(window.get()); |
| g_assert_true(window.get() == globalObject.get()); |
| |
| foo2 = adoptGRef(jsc_context_evaluate(context.get(), "window.foo", -1)); |
| checker.watch(foo2.get()); |
| g_assert_true(foo.get() == foo2.get()); |
| |
| GRefPtr<JSCValue> global = adoptGRef(jsc_context_evaluate(context.get(), "window.global = 100", -1)); |
| checker.watch(global.get()); |
| g_assert_true(jsc_value_is_number(global.get())); |
| g_assert_cmpint(jsc_value_to_int32(global.get()), ==, 100); |
| |
| GRefPtr<JSCValue> global2 = adoptGRef(jsc_context_get_value(context.get(), "global")); |
| checker.watch(global2.get()); |
| g_assert_true(global.get() == global2.get()); |
| |
| global2 = adoptGRef(jsc_value_object_get_property(globalObject.get(), "global")); |
| checker.watch(global2.get()); |
| g_assert_true(global.get() == global2.get()); |
| |
| jsc_value_object_define_property_data(globalObject.get(), "window2", static_cast<JSCValuePropertyFlags>(0), globalObject.get()); |
| GRefPtr<JSCValue> window2 = adoptGRef(jsc_context_evaluate(context.get(), "window2", -1)); |
| checker.watch(window2.get()); |
| g_assert_true(window2.get() == globalObject.get()); |
| } |
| |
| typedef struct { |
| const char* name; |
| bool wasDeleted; |
| } Module; |
| |
| static JSCClassVTable moduleVTable = { |
| // get_property |
| [](JSCClass* jscClass, JSCContext* context, gpointer instance, const char* name) -> JSCValue* { |
| auto* checker = static_cast<LeakChecker*>(g_object_get_data(G_OBJECT(jscClass), "leak-checker")); |
| checker->watch(context); |
| |
| if (g_strcmp0(name, "name")) |
| return nullptr; |
| |
| auto* module = static_cast<Module*>(instance); |
| auto* returnValue = jsc_value_new_string(context, module->name); |
| checker->watch(returnValue); |
| return returnValue; |
| }, |
| // set_property |
| nullptr, |
| // has_property |
| nullptr, |
| // delete_property |
| nullptr, |
| // enumerate_properties |
| nullptr, |
| // padding |
| nullptr, nullptr, nullptr, nullptr |
| }; |
| |
| static void testJSCEvaluateInObject() |
| { |
| Module moduleObject = { "ModuleWithClass", false }; |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "g = 54; function foo() { return 42; }", -1)); |
| checker.watch(result.get()); |
| |
| GRefPtr<JSCValue> globalObject = adoptGRef(jsc_context_get_global_object(context.get())); |
| checker.watch(globalObject.get()); |
| |
| GRefPtr<JSCValue> rootFoo = adoptGRef(jsc_value_object_get_property(globalObject.get(), "foo")); |
| checker.watch(rootFoo.get()); |
| g_assert_true(jsc_value_is_function(rootFoo.get())); |
| result = adoptGRef(jsc_value_function_call(rootFoo.get(), G_TYPE_NONE)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 42); |
| GRefPtr<JSCValue> value = adoptGRef(jsc_context_evaluate(context.get(), "foo()", -1)); |
| checker.watch(value.get()); |
| g_assert_true(value.get() == result.get()); |
| |
| GRefPtr<JSCValue> module; |
| result = adoptGRef(jsc_context_evaluate_in_object(context.get(), "function bar() { return g; }", -1, nullptr, nullptr, nullptr, 1, &module.outPtr())); |
| checker.watch(result.get()); |
| checker.watch(module.get()); |
| g_assert_true(JSC_IS_VALUE(module.get())); |
| g_assert_true(jsc_value_is_object(module.get())); |
| GUniquePtr<char> valueString(jsc_value_to_string(module.get())); |
| g_assert_cmpstr(valueString.get(), ==, "[object GlobalObject]"); |
| jsc_context_set_value(context.get(), "module", module.get()); |
| |
| GRefPtr<JSCValue> bar = adoptGRef(jsc_value_object_get_property(module.get(), "bar")); |
| checker.watch(bar.get()); |
| g_assert_true(jsc_value_is_function(bar.get())); |
| result = adoptGRef(jsc_value_function_call(bar.get(), G_TYPE_NONE)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 54); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "module.bar()", -1)); |
| checker.watch(value.get()); |
| g_assert_true(value.get() == result.get()); |
| |
| bar = adoptGRef(jsc_context_get_value(context.get(), "bar")); |
| checker.watch(bar.get()); |
| g_assert_true(jsc_value_is_undefined(bar.get())); |
| |
| JSCClass* jscClass = jsc_context_register_class(context.get(), "Module", nullptr, &moduleVTable, [](gpointer module) { |
| static_cast<Module*>(module)->wasDeleted = true; |
| }); |
| checker.watch(jscClass); |
| g_object_set_data(G_OBJECT(jscClass), "leak-checker", &checker); |
| GRefPtr<JSCValue> moduleWithClass; |
| result = adoptGRef(jsc_context_evaluate_in_object(context.get(), "function baz() { return foo(); }", -1, &moduleObject, jscClass, nullptr, 1, &moduleWithClass.outPtr())); |
| checker.watch(result.get()); |
| checker.watch(moduleWithClass.get()); |
| g_assert_true(JSC_IS_VALUE(moduleWithClass.get())); |
| g_assert_true(jsc_value_is_object(moduleWithClass.get())); |
| valueString.reset(jsc_value_to_string(moduleWithClass.get())); |
| g_assert_cmpstr(valueString.get(), ==, "[object Module]"); |
| jsc_context_set_value(context.get(), "moduleWithClass", moduleWithClass.get()); |
| |
| GRefPtr<JSCValue> name = adoptGRef(jsc_value_object_get_property(moduleWithClass.get(), "name")); |
| checker.watch(name.get()); |
| g_assert_true(jsc_value_is_string(name.get())); |
| valueString.reset(jsc_value_to_string(name.get())); |
| g_assert_cmpstr(valueString.get(), ==, "ModuleWithClass"); |
| |
| GRefPtr<JSCValue> baz = adoptGRef(jsc_value_object_get_property(moduleWithClass.get(), "baz")); |
| checker.watch(baz.get()); |
| g_assert_true(jsc_value_is_function(baz.get())); |
| result = adoptGRef(jsc_value_function_call(baz.get(), G_TYPE_NONE)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 42); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "moduleWithClass.baz()", -1)); |
| checker.watch(value.get()); |
| g_assert_true(value.get() == result.get()); |
| |
| bar = adoptGRef(jsc_value_object_get_property(moduleWithClass.get(), "bar")); |
| checker.watch(bar.get()); |
| g_assert_true(jsc_value_is_undefined(bar.get())); |
| |
| baz = adoptGRef(jsc_value_object_get_property(module.get(), "baz")); |
| checker.watch(baz.get()); |
| g_assert_true(jsc_value_is_undefined(baz.get())); |
| |
| baz = adoptGRef(jsc_context_get_value(context.get(), "baz")); |
| checker.watch(baz.get()); |
| g_assert_true(jsc_value_is_undefined(baz.get())); |
| |
| GRefPtr<JSCValue> jsNamespace = adoptGRef(jsc_value_new_object(context.get(), nullptr, nullptr)); |
| checker.watch(jsNamespace.get()); |
| jsc_context_set_value(context.get(), "wk", jsNamespace.get()); |
| |
| GRefPtr<JSCValue> moduleInWk; |
| result = adoptGRef(jsc_context_evaluate_in_object(context.get(), "function bar() { return g; }", -1, nullptr, nullptr, nullptr, 1, &moduleInWk.outPtr())); |
| checker.watch(result.get()); |
| checker.watch(moduleInWk.get()); |
| g_assert_true(JSC_IS_VALUE(moduleInWk.get())); |
| g_assert_true(jsc_value_is_object(moduleInWk.get())); |
| jsc_value_object_set_property(jsNamespace.get(), "moduleInWk", moduleInWk.get()); |
| |
| bar = adoptGRef(jsc_value_object_get_property(moduleInWk.get(), "bar")); |
| checker.watch(bar.get()); |
| g_assert_true(jsc_value_is_function(bar.get())); |
| result = adoptGRef(jsc_value_function_call(bar.get(), G_TYPE_NONE)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 54); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "wk.moduleInWk.bar()", -1)); |
| checker.watch(value.get()); |
| g_assert_true(value.get() == result.get()); |
| |
| moduleInWk = adoptGRef(jsc_context_get_value(context.get(), "moduleInWk")); |
| checker.watch(moduleInWk.get()); |
| g_assert_true(jsc_value_is_undefined(moduleInWk.get())); |
| } |
| g_assert_true(moduleObject.wasDeleted); |
| } |
| |
| 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_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_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")); |
| 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_name(exception.get()), ==, "SyntaxError"); |
| g_assert_cmpstr(jsc_exception_get_message(exception.get()), ==, "Unexpected token '='"); |
| 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; |
| } |
| |
| static void callback(JSCValue* function) |
| { |
| GRefPtr<JSCValue> value = adoptGRef(jsc_value_function_call(function, G_TYPE_INT, 200, G_TYPE_NONE)); |
| g_assert_true(jsc_value_is_undefined(value.get())); |
| } |
| |
| static void doubleAndSetInResult(int n) |
| { |
| GRefPtr<JSCValue> value = adoptGRef(jsc_value_new_number(jsc_context_get_current(), n * 2)); |
| jsc_context_set_value(jsc_context_get_current(), "result", value.get()); |
| } |
| |
| static int sumFunction(GPtrArray* array) |
| { |
| int retval = 0; |
| |
| g_ptr_array_foreach(array, [](gpointer data, gpointer userData) { |
| g_assert_true(JSC_IS_VALUE(data)); |
| JSCValue* item = JSC_VALUE(data); |
| g_assert_true(jsc_value_is_number(item)); |
| *static_cast<int*>(userData) += jsc_value_to_int32(item); |
| }, &retval); |
| |
| return retval; |
| } |
| |
| static char* joinFunction(const char* const* strv, const char* sep) |
| { |
| return g_strjoinv(sep, const_cast<char**>(strv)); |
| } |
| |
| static gboolean checkUserData(GFile* file) |
| { |
| return G_IS_FILE(file); |
| } |
| |
| static void testJSCFunction() |
| { |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| GRefPtr<JSCValue> function = adoptGRef(jsc_value_new_function(context.get(), "foo", G_CALLBACK(foo), nullptr, nullptr, G_TYPE_INT, 1, G_TYPE_INT)); |
| checker.watch(function.get()); |
| jsc_context_set_value(context.get(), "foo", function.get()); |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "foo(200)", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 400); |
| |
| GRefPtr<JSCValue> value = adoptGRef(jsc_value_function_call(function.get(), G_TYPE_INT, 200, G_TYPE_NONE)); |
| checker.watch(value.get()); |
| g_assert_true(value.get() == result.get()); |
| |
| GRefPtr<GPtrArray> parameters = adoptGRef(g_ptr_array_new_with_free_func(g_object_unref)); |
| auto* parameter = jsc_value_new_number(context.get(), 200); |
| checker.watch(parameter); |
| g_ptr_array_add(parameters.get(), parameter); |
| value = adoptGRef(jsc_value_function_callv(function.get(), parameters->len, reinterpret_cast<JSCValue**>(parameters->pdata))); |
| checker.watch(value.get()); |
| g_assert_true(value.get() == result.get()); |
| } |
| |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| GType parameterTypes[] = { G_TYPE_INT }; |
| GRefPtr<JSCValue> function = adoptGRef(jsc_value_new_functionv(context.get(), "foo", G_CALLBACK(foo), nullptr, nullptr, G_TYPE_INT, 1, parameterTypes)); |
| checker.watch(function.get()); |
| jsc_context_set_value(context.get(), "foo", function.get()); |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "foo(200)", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 400); |
| |
| GRefPtr<JSCValue> value = adoptGRef(jsc_value_function_call(function.get(), G_TYPE_INT, 200, G_TYPE_NONE)); |
| checker.watch(value.get()); |
| g_assert_true(value.get() == result.get()); |
| |
| GRefPtr<GPtrArray> parameters = adoptGRef(g_ptr_array_new_with_free_func(g_object_unref)); |
| auto* parameter = jsc_value_new_number(context.get(), 200); |
| checker.watch(parameter); |
| g_ptr_array_add(parameters.get(), parameter); |
| value = adoptGRef(jsc_value_function_callv(function.get(), parameters->len, reinterpret_cast<JSCValue**>(parameters->pdata))); |
| checker.watch(value.get()); |
| g_assert_true(value.get() == result.get()); |
| } |
| |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| GRefPtr<JSCValue> function = adoptGRef(jsc_context_evaluate(context.get(), "foo = function(n) { return n * 2; }", -1)); |
| checker.watch(function.get()); |
| |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "foo(200)", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 400); |
| |
| GRefPtr<JSCValue> value = adoptGRef(jsc_value_function_call(function.get(), G_TYPE_INT, 200, G_TYPE_NONE)); |
| checker.watch(value.get()); |
| g_assert_true(value.get() == result.get()); |
| |
| GRefPtr<GPtrArray> parameters = adoptGRef(g_ptr_array_new_with_free_func(g_object_unref)); |
| auto* parameter = jsc_value_new_number(context.get(), 200); |
| checker.watch(parameter); |
| g_ptr_array_add(parameters.get(), parameter); |
| value = adoptGRef(jsc_value_function_callv(function.get(), parameters->len, reinterpret_cast<JSCValue**>(parameters->pdata))); |
| checker.watch(value.get()); |
| g_assert_true(value.get() == result.get()); |
| } |
| |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| GRefPtr<JSCValue> function = adoptGRef(jsc_value_new_function(context.get(), "callback", G_CALLBACK(callback), nullptr, nullptr, G_TYPE_NONE, 1, JSC_TYPE_VALUE)); |
| checker.watch(function.get()); |
| jsc_context_set_value(context.get(), "callback", function.get()); |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "var result = 0; callback(function(n){ result = n * 2; }); result", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 400); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "result = 0", -1)); |
| checker.watch(result.get()); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "result", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 0); |
| |
| GRefPtr<JSCValue> dbl = adoptGRef(jsc_value_new_function(context.get(), "doubleAndSetInResult", G_CALLBACK(doubleAndSetInResult), nullptr, nullptr, G_TYPE_NONE, 1, G_TYPE_INT)); |
| checker.watch(dbl.get()); |
| GRefPtr<JSCValue> value = adoptGRef(jsc_value_function_call(function.get(), JSC_TYPE_VALUE, dbl.get(), G_TYPE_NONE)); |
| checker.watch(value.get()); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "result", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 400); |
| |
| GRefPtr<GPtrArray> parameters = adoptGRef(g_ptr_array_new()); |
| g_ptr_array_add(parameters.get(), dbl.get()); |
| value = adoptGRef(jsc_value_function_callv(function.get(), parameters->len, reinterpret_cast<JSCValue**>(parameters->pdata))); |
| checker.watch(value.get()); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "result", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 400); |
| } |
| |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| GRefPtr<JSCValue> function = adoptGRef(jsc_context_evaluate(context.get(), |
| "sumFunction = function(array) {\n" |
| " var result = 0;\n" |
| " for (var i in array) { result += array[i]; }\n" |
| " return result;\n" |
| "}", -1)); |
| checker.watch(function.get()); |
| g_assert_true(jsc_value_is_object(function.get())); |
| |
| GRefPtr<JSCValue> array = adoptGRef(jsc_value_new_array(context.get(), G_TYPE_INT, 2, G_TYPE_INT, 4, G_TYPE_INT, 6, G_TYPE_NONE)); |
| checker.watch(array.get()); |
| |
| GRefPtr<JSCValue> value = adoptGRef(jsc_value_function_call(function.get(), JSC_TYPE_VALUE, array.get(), G_TYPE_NONE)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 12); |
| |
| GRefPtr<GPtrArray> gArray = adoptGRef(g_ptr_array_new_with_free_func(g_object_unref)); |
| g_ptr_array_add(gArray.get(), jsc_value_new_number(context.get(), 1)); |
| g_ptr_array_add(gArray.get(), jsc_value_new_number(context.get(), 3)); |
| g_ptr_array_add(gArray.get(), jsc_value_new_number(context.get(), 5)); |
| |
| value = adoptGRef(jsc_value_function_call(function.get(), G_TYPE_PTR_ARRAY, gArray.get(), G_TYPE_NONE)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 9); |
| } |
| |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| GRefPtr<JSCValue> function = adoptGRef(jsc_value_new_function(context.get(), "sumFunction", G_CALLBACK(sumFunction), nullptr, nullptr, G_TYPE_INT, 1, G_TYPE_PTR_ARRAY)); |
| checker.watch(function.get()); |
| jsc_context_set_value(context.get(), "sumFunction", function.get()); |
| GRefPtr<JSCValue> value = adoptGRef(jsc_context_evaluate(context.get(), "sumFunction([2,4,6])", -1)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 12); |
| |
| GRefPtr<GPtrArray> gArray = adoptGRef(g_ptr_array_new_with_free_func(g_object_unref)); |
| g_ptr_array_add(gArray.get(), jsc_value_new_number(context.get(), 1)); |
| g_ptr_array_add(gArray.get(), jsc_value_new_number(context.get(), 3)); |
| g_ptr_array_add(gArray.get(), jsc_value_new_number(context.get(), 5)); |
| |
| value = adoptGRef(jsc_value_function_call(function.get(), G_TYPE_PTR_ARRAY, gArray.get(), G_TYPE_NONE)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 9); |
| } |
| |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| GRefPtr<JSCValue> function = adoptGRef(jsc_context_evaluate(context.get(), |
| "joinFunction = function(array, sep) {\n" |
| " var result = '';\n" |
| " for (var i in array) {\n" |
| " result += array[i];\n" |
| " if (i != array.length - 1) { result += sep; }\n" |
| " }\n" |
| " return result;\n" |
| "}", -1)); |
| checker.watch(function.get()); |
| g_assert_true(jsc_value_is_object(function.get())); |
| |
| const char* strv[] = { "one", "two", "three", nullptr }; |
| GRefPtr<JSCValue> value = adoptGRef(jsc_value_function_call(function.get(), G_TYPE_STRV, strv, G_TYPE_STRING, " ", G_TYPE_NONE)); |
| 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(), ==, "one two three"); |
| |
| function = adoptGRef(jsc_value_new_function(context.get(), "joinFunction2", G_CALLBACK(joinFunction), nullptr, nullptr, G_TYPE_STRING, 2, G_TYPE_STRV, G_TYPE_STRING)); |
| checker.watch(function.get()); |
| jsc_context_set_value(context.get(), "joinFunction2", function.get()); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "joinFunction2(['one','two','three'], ' ')", -1)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_string(value.get())); |
| GUniquePtr<char> valueString2(jsc_value_to_string(value.get())); |
| g_assert_cmpstr(valueString2.get(), ==, valueString.get()); |
| |
| bool didThrow = false; |
| g_assert_throw_begin(exceptionHandler, didThrow); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "joinFunction2(['one',2,'three'], ' ')", -1)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_undefined(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> function = adoptGRef(jsc_value_new_function_variadic(context.get(), "sumFunction", G_CALLBACK(sumFunction), nullptr, nullptr, G_TYPE_INT)); |
| checker.watch(function.get()); |
| jsc_context_set_value(context.get(), "sumFunction", function.get()); |
| |
| GRefPtr<JSCValue> value = adoptGRef(jsc_context_evaluate(context.get(), "sumFunction(2,4,6)", -1)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 12); |
| |
| value = adoptGRef(jsc_context_evaluate(context.get(), "sumFunction()", -1)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 0); |
| } |
| |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| GFile* file = g_file_new_for_path("."); |
| checker.watch(file); |
| GRefPtr<JSCValue> function = adoptGRef(jsc_value_new_function(context.get(), "checkUserData", G_CALLBACK(checkUserData), |
| file, g_object_unref, G_TYPE_BOOLEAN, 0, G_TYPE_NONE)); |
| checker.watch(function.get()); |
| jsc_context_set_value(context.get(), "checkUserData", function.get()); |
| |
| GRefPtr<JSCValue> value = adoptGRef(jsc_context_evaluate(context.get(), "checkUserData()", -1)); |
| 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_function_call(function.get(), G_TYPE_NONE)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_boolean(value.get())); |
| g_assert_true(jsc_value_to_boolean(value.get())); |
| } |
| } |
| |
| static void testJSCObject() |
| { |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| GRefPtr<JSCValue> foo = adoptGRef(jsc_context_evaluate(context.get(), "class Foo { foo(n) { return n * 2; } }; foo = new Foo;", -1)); |
| checker.watch(foo.get()); |
| g_assert_true(jsc_value_is_object(foo.get())); |
| g_assert_true(jsc_value_object_is_instance_of(foo.get(), "Foo")); |
| g_assert_true(jsc_value_object_has_property(foo.get(), "foo")); |
| |
| GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(foo.get())); |
| g_assert_null(properties.get()); |
| |
| GRefPtr<JSCValue> result = adoptGRef(jsc_value_object_invoke_method(foo.get(), "foo", G_TYPE_INT, 200, G_TYPE_NONE)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 400); |
| |
| GRefPtr<GPtrArray> parameters = adoptGRef(g_ptr_array_new_with_free_func(g_object_unref)); |
| auto* parameter = jsc_value_new_number(context.get(), 200); |
| checker.watch(parameter); |
| g_ptr_array_add(parameters.get(), parameter); |
| result = adoptGRef(jsc_value_object_invoke_methodv(foo.get(), "foo", parameters->len, reinterpret_cast<JSCValue**>(parameters->pdata))); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 400); |
| |
| g_assert_false(jsc_value_object_has_property(foo.get(), "bar")); |
| bool didThrow = false; |
| g_assert_throw_begin(exceptionHandler, didThrow); |
| result = adoptGRef(jsc_value_object_invoke_method(foo.get(), "bar", G_TYPE_INT, 200, G_TYPE_NONE)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_undefined(result.get())); |
| g_assert_did_throw(exceptionHandler, didThrow); |
| |
| g_assert_throw_begin(exceptionHandler, didThrow); |
| result = adoptGRef(jsc_value_object_invoke_methodv(foo.get(), "bar", parameters->len, reinterpret_cast<JSCValue**>(parameters->pdata))); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_undefined(result.get())); |
| g_assert_did_throw(exceptionHandler, didThrow); |
| |
| GRefPtr<JSCValue> constructor = adoptGRef(jsc_context_evaluate(context.get(), "Foo", -1)); |
| checker.watch(constructor.get()); |
| g_assert_true(jsc_value_is_constructor(constructor.get())); |
| |
| foo = adoptGRef(jsc_value_constructor_call(constructor.get(), G_TYPE_NONE)); |
| checker.watch(foo.get()); |
| g_assert_true(jsc_value_is_object(foo.get())); |
| g_assert_true(jsc_value_object_is_instance_of(foo.get(), "Foo")); |
| |
| result = adoptGRef(jsc_value_object_invoke_method(foo.get(), "foo", G_TYPE_INT, 300, G_TYPE_NONE)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 600); |
| |
| jsc_context_set_value(context.get(), "foo2", foo.get()); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "foo2 instanceof Foo", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "foo2.foo(500)", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 1000); |
| |
| GRefPtr<JSCValue> foo2 = adoptGRef(jsc_value_constructor_callv(constructor.get(), 0, nullptr)); |
| checker.watch(foo2.get()); |
| g_assert_true(jsc_value_is_object(foo2.get())); |
| g_assert_true(jsc_value_object_is_instance_of(foo2.get(), "Foo")); |
| g_assert_false(foo.get() == foo2.get()); |
| } |
| |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| GRefPtr<JSCValue> object = adoptGRef(jsc_value_new_object(context.get(), nullptr, nullptr)); |
| checker.watch(object.get()); |
| g_assert_true(jsc_value_is_object(object.get())); |
| g_assert_true(jsc_value_object_is_instance_of(object.get(), "Object")); |
| |
| GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(object.get())); |
| g_assert_null(properties.get()); |
| |
| GRefPtr<JSCValue> property = adoptGRef(jsc_value_new_number(context.get(), 25)); |
| checker.watch(property.get()); |
| g_assert_false(jsc_value_object_has_property(object.get(), "val")); |
| jsc_value_object_define_property_data(object.get(), "val", static_cast<JSCValuePropertyFlags>(0), property.get()); |
| g_assert_true(jsc_value_object_has_property(object.get(), "val")); |
| properties.reset(jsc_value_object_enumerate_properties(object.get())); |
| g_assert_null(properties.get()); |
| jsc_context_set_value(context.get(), "f", object.get()); |
| |
| GRefPtr<JSCValue> value = adoptGRef(jsc_context_evaluate(context.get(), "f.val;", -1)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 25); |
| |
| bool didThrow = false; |
| g_assert_throw_begin(exceptionHandler, didThrow); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "'use strict'; f.val = 32;", -1)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_undefined(value.get())); |
| g_assert_did_throw(exceptionHandler, didThrow); |
| |
| value = adoptGRef(jsc_context_evaluate(context.get(), "f.propertyIsEnumerable('val');", -1)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_boolean(value.get())); |
| g_assert_true(jsc_value_to_boolean(value.get()) == FALSE); |
| |
| value = adoptGRef(jsc_context_evaluate(context.get(), "delete f.val;", -1)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_object_has_property(object.get(), "val")); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "f.val;", -1)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 25); |
| |
| g_assert_false(jsc_value_object_delete_property(object.get(), "val")); |
| g_assert_true(jsc_value_object_has_property(object.get(), "val")); |
| |
| property = adoptGRef(jsc_value_new_number(context.get(), 52)); |
| checker.watch(property.get()); |
| g_assert_throw_begin(exceptionHandler, didThrow); |
| jsc_value_object_define_property_data(object.get(), "val", static_cast<JSCValuePropertyFlags>(0), property.get()); |
| g_assert_did_throw(exceptionHandler, didThrow); |
| |
| property = adoptGRef(jsc_value_new_number(context.get(), 32)); |
| checker.watch(property.get()); |
| g_assert_false(jsc_value_object_has_property(object.get(), "val2")); |
| jsc_value_object_define_property_data(object.get(), "val2", static_cast<JSCValuePropertyFlags>(JSC_VALUE_PROPERTY_ENUMERABLE | JSC_VALUE_PROPERTY_WRITABLE), property.get()); |
| g_assert_true(jsc_value_object_has_property(object.get(), "val2")); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "f.val2;", -1)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 32); |
| |
| properties.reset(jsc_value_object_enumerate_properties(object.get())); |
| g_assert_cmpuint(g_strv_length(properties.get()), ==, 1); |
| g_assert_cmpstr(properties.get()[0], ==, "val2"); |
| g_assert_null(properties.get()[1]); |
| |
| value = adoptGRef(jsc_context_evaluate(context.get(), "'use strict'; f.val2 = 45;", -1)); |
| checker.watch(value.get()); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "f.val2;", -1)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 45); |
| |
| value = adoptGRef(jsc_context_evaluate(context.get(), "f.propertyIsEnumerable('val2');", -1)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_boolean(value.get())); |
| g_assert_true(jsc_value_to_boolean(value.get()) == TRUE); |
| |
| g_assert_false(jsc_value_object_delete_property(object.get(), "val2")); |
| g_assert_true(jsc_value_object_has_property(object.get(), "val2")); |
| |
| property = adoptGRef(jsc_value_new_number(context.get(), 125)); |
| checker.watch(property.get()); |
| g_assert_false(jsc_value_object_has_property(object.get(), "val3")); |
| jsc_value_object_define_property_data(object.get(), "val3", static_cast<JSCValuePropertyFlags>(JSC_VALUE_PROPERTY_CONFIGURABLE | JSC_VALUE_PROPERTY_WRITABLE), property.get()); |
| g_assert_true(jsc_value_object_has_property(object.get(), "val3")); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "f.val3;", -1)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 125); |
| |
| properties.reset(jsc_value_object_enumerate_properties(object.get())); |
| g_assert_cmpuint(g_strv_length(properties.get()), ==, 1); |
| g_assert_cmpstr(properties.get()[0], ==, "val2"); |
| g_assert_null(properties.get()[1]); |
| |
| property = adoptGRef(jsc_value_new_number(context.get(), 150)); |
| checker.watch(property.get()); |
| jsc_value_object_define_property_data(object.get(), "val3", static_cast<JSCValuePropertyFlags>(JSC_VALUE_PROPERTY_CONFIGURABLE), property.get()); |
| g_assert_true(jsc_value_object_has_property(object.get(), "val3")); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "f.val3;", -1)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 150); |
| |
| properties.reset(jsc_value_object_enumerate_properties(object.get())); |
| g_assert_cmpuint(g_strv_length(properties.get()), ==, 1); |
| g_assert_cmpstr(properties.get()[0], ==, "val2"); |
| g_assert_null(properties.get()[1]); |
| |
| value = adoptGRef(jsc_context_evaluate(context.get(), "delete f.val3;", -1)); |
| checker.watch(value.get()); |
| g_assert_false(jsc_value_object_has_property(object.get(), "val3")); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "f.val3;", -1)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_undefined(value.get())); |
| |
| property = adoptGRef(jsc_value_new_number(context.get(), 250)); |
| checker.watch(property.get()); |
| g_assert_false(jsc_value_object_has_property(object.get(), "val4")); |
| jsc_value_object_define_property_data(object.get(), "val4", static_cast<JSCValuePropertyFlags>(JSC_VALUE_PROPERTY_CONFIGURABLE | JSC_VALUE_PROPERTY_ENUMERABLE), property.get()); |
| g_assert_true(jsc_value_object_has_property(object.get(), "val4")); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "f.val4;", -1)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 250); |
| |
| properties.reset(jsc_value_object_enumerate_properties(object.get())); |
| g_assert_cmpuint(g_strv_length(properties.get()), ==, 2); |
| g_assert_cmpstr(properties.get()[0], ==, "val2"); |
| g_assert_cmpstr(properties.get()[1], ==, "val4"); |
| g_assert_null(properties.get()[2]); |
| |
| g_assert_true(jsc_value_object_delete_property(object.get(), "val4")); |
| g_assert_false(jsc_value_object_has_property(object.get(), "val4")); |
| |
| properties.reset(jsc_value_object_enumerate_properties(object.get())); |
| g_assert_cmpuint(g_strv_length(properties.get()), ==, 1); |
| g_assert_cmpstr(properties.get()[0], ==, "val2"); |
| g_assert_null(properties.get()[1]); |
| |
| GRefPtr<JSCValue> function = adoptGRef(jsc_value_new_function(context.get(), "foo", G_CALLBACK(foo), nullptr, nullptr, G_TYPE_INT, 1, G_TYPE_INT)); |
| checker.watch(function.get()); |
| |
| g_assert_false(jsc_value_object_has_property(object.get(), "foo")); |
| jsc_value_object_define_property_data(object.get(), "foo", static_cast<JSCValuePropertyFlags>(0), function.get()); |
| g_assert_true(jsc_value_object_has_property(object.get(), "foo")); |
| |
| properties.reset(jsc_value_object_enumerate_properties(object.get())); |
| g_assert_cmpuint(g_strv_length(properties.get()), ==, 1); |
| g_assert_cmpstr(properties.get()[0], ==, "val2"); |
| g_assert_null(properties.get()[1]); |
| |
| GRefPtr<JSCValue> result = adoptGRef(jsc_value_object_invoke_method(object.get(), "foo", G_TYPE_INT, 200, G_TYPE_NONE)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 400); |
| |
| g_assert_throw_begin(exceptionHandler, didThrow); |
| result = adoptGRef(jsc_value_object_invoke_method(object.get(), "foo", G_TYPE_POINTER, "200", G_TYPE_NONE)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_undefined(result.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_number(context.get(), 125)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_false(jsc_value_is_object(value.get())); |
| g_assert_false(jsc_value_object_is_instance_of(value.get(), "Object")); |
| |
| bool didThrow = false; |
| g_assert_throw_begin(exceptionHandler, didThrow); |
| GRefPtr<JSCValue> result = adoptGRef(jsc_value_object_invoke_method(value.get(), "foo", G_TYPE_INT, 200, G_TYPE_NONE)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_undefined(result.get())); |
| g_assert_throw_begin(exceptionHandler, didThrow); |
| } |
| } |
| |
| typedef struct _Foo Foo; |
| struct _Foo { |
| int foo; |
| HashMap<CString, int> properties; |
| Foo* sibling; |
| }; |
| |
| static Foo* fooCreate() |
| { |
| Foo* foo = g_new0(Foo, 1); |
| new (foo) Foo(); |
| return foo; |
| } |
| |
| static Foo* fooCreateWithFoo(int value) |
| { |
| auto* f = fooCreate(); |
| f->foo = value; |
| return f; |
| } |
| |
| static Foo* fooCreateWithFooV(GPtrArray* values) |
| { |
| auto* f = fooCreate(); |
| g_ptr_array_foreach(values, [](gpointer data, gpointer userData) { |
| g_assert_true(JSC_IS_VALUE(data)); |
| JSCValue* item = JSC_VALUE(data); |
| g_assert_true(jsc_value_is_number(item)); |
| auto* foo = static_cast<Foo*>(userData); |
| foo->foo += jsc_value_to_int32(item); |
| }, f); |
| return f; |
| } |
| |
| static Foo* fooCreateWithUserData(GFile* file) |
| { |
| g_assert_true(G_IS_FILE(file)); |
| return fooCreate(); |
| } |
| |
| static void fooFree(Foo* foo) |
| { |
| foo->~Foo(); |
| g_free(foo); |
| } |
| |
| static void setFoo(Foo* foo, int value) |
| { |
| foo->foo = value; |
| } |
| |
| static int getFoo(Foo* foo) |
| { |
| return foo->foo; |
| } |
| |
| static void setSibling(Foo* foo, Foo* sibling) |
| { |
| foo->sibling = sibling; |
| } |
| |
| static Foo* getSibling(Foo* foo) |
| { |
| return foo->sibling; |
| } |
| |
| static void multiplyFoo(Foo* foo, int multiplier) |
| { |
| foo->foo *= multiplier; |
| } |
| |
| static void multiplyFooV(Foo* foo, GPtrArray* multipliers) |
| { |
| g_ptr_array_foreach(multipliers, [](gpointer data, gpointer userData) { |
| g_assert_true(JSC_IS_VALUE(data)); |
| JSCValue* item = JSC_VALUE(data); |
| g_assert_true(jsc_value_is_number(item)); |
| auto* foo = static_cast<Foo*>(userData); |
| foo->foo *= jsc_value_to_int32(item); |
| }, foo); |
| } |
| |
| static int fooGetProperty(Foo* foo, const char* name) |
| { |
| auto addResult = foo->properties.add(name, 0); |
| return addResult.iterator->value; |
| } |
| |
| static void fooSetProperty(Foo* foo, const char* name, int value) |
| { |
| auto addResult = foo->properties.add(name, value); |
| if (!addResult.isNewEntry) |
| addResult.iterator->value = value; |
| } |
| |
| struct PromiseData { |
| Foo* foo; |
| int multiplier; |
| LeakChecker* checker; |
| }; |
| |
| static void getMultiplyFoo(JSCValue* resolve, JSCValue* reject, PromiseData* data) |
| { |
| data->checker->watch(resolve); |
| g_assert_true(JSC_IS_VALUE(resolve)); |
| g_assert_true(jsc_value_is_function(resolve)); |
| data->checker->watch(reject); |
| g_assert_true(JSC_IS_VALUE(reject)); |
| g_assert_true(jsc_value_is_function(reject)); |
| |
| GRefPtr<JSCValue> result; |
| if (data->multiplier > 0) |
| result = adoptGRef(jsc_value_function_call(resolve, G_TYPE_INT, data->foo->foo * data->multiplier, G_TYPE_NONE)); |
| else { |
| GRefPtr<JSCException> exception = adoptGRef(jsc_exception_new(jsc_context_get_current(), "Multiplier must be positive greater than 0")); |
| result = adoptGRef(jsc_value_function_call(reject, JSC_TYPE_EXCEPTION, exception.get(), G_TYPE_NONE)); |
| } |
| data->checker->watch(result.get()); |
| g_assert_true(jsc_value_is_undefined(result.get())); |
| } |
| |
| static JSCValue* getMultiplyFooAsync(Foo* foo, int multiplier, LeakChecker* checker) |
| { |
| auto* jsContext = jsc_context_get_current(); |
| g_assert_true(JSC_IS_CONTEXT(jsContext)); |
| |
| GRefPtr<JSCValue> function = adoptGRef(jsc_value_new_function(jsContext, nullptr, G_CALLBACK(getMultiplyFoo), new PromiseData { foo, multiplier, checker}, |
| [](gpointer data) { delete static_cast<PromiseData*>(data); }, G_TYPE_NONE, 2, JSC_TYPE_VALUE, JSC_TYPE_VALUE)); |
| checker->watch(function.get()); |
| GRefPtr<JSCValue> promise = adoptGRef(jsc_context_get_value(jsContext, "Promise")); |
| checker->watch(promise.get()); |
| g_assert_true(jsc_value_is_constructor(promise.get())); |
| auto* returnValue = jsc_value_constructor_call(promise.get(), JSC_TYPE_VALUE, function.get(), G_TYPE_NONE); |
| checker->watch(returnValue); |
| return returnValue; |
| } |
| |
| typedef struct { |
| int baz; |
| } Baz; |
| |
| static Baz* bazCreate() |
| { |
| return g_new0(Baz, 1); |
| } |
| |
| static JSCClassVTable fooVTable = { |
| // get_property |
| [](JSCClass* jscClass, JSCContext* context, gpointer instance, const char* name) -> JSCValue* { |
| auto* checker = static_cast<LeakChecker*>(g_object_get_data(G_OBJECT(jscClass), "leak-checker")); |
| checker->watch(context); |
| |
| if (!g_str_has_prefix(name, "prop_")) |
| return nullptr; |
| |
| if (!g_strcmp0(name, "prop_throw_on_get")) { |
| jsc_context_throw(context, "Invalid property"); |
| return jsc_value_new_undefined(context); |
| } |
| |
| auto* foo = static_cast<Foo*>(instance); |
| auto* returnValue = jsc_value_new_number(context, fooGetProperty(foo, name)); |
| checker->watch(returnValue); |
| return returnValue; |
| }, |
| // set_property |
| [](JSCClass* jscClass, JSCContext* context, gpointer instance, const char* name, JSCValue* value) -> gboolean { |
| auto* checker = static_cast<LeakChecker*>(g_object_get_data(G_OBJECT(jscClass), "leak-checker")); |
| checker->watch(context); |
| checker->watch(value); |
| |
| if (!g_str_has_prefix(name, "prop_")) |
| return FALSE; |
| |
| if (!jsc_value_is_number(value)) { |
| jsc_context_throw(context, "Invalid value set: only numbers are allowed"); |
| return TRUE; |
| } |
| |
| auto* foo = static_cast<Foo*>(instance); |
| fooSetProperty(foo, name, jsc_value_to_int32(value)); |
| return true; |
| }, |
| // has_property |
| [](JSCClass* jscClass, JSCContext* context, gpointer instance, const char* name) -> gboolean { |
| auto* checker = static_cast<LeakChecker*>(g_object_get_data(G_OBJECT(jscClass), "leak-checker")); |
| checker->watch(context); |
| return g_str_has_prefix(name, "prop_"); |
| }, |
| // delete_property |
| [](JSCClass* jscClass, JSCContext* context, gpointer instance, const char* name) -> gboolean { |
| auto* checker = static_cast<LeakChecker*>(g_object_get_data(G_OBJECT(jscClass), "leak-checker")); |
| checker->watch(context); |
| |
| if (!g_strcmp0(name, "prop_cant_delete")) |
| return FALSE; |
| |
| if (!g_strcmp0(name, "prop_throw_on_delete")) { |
| jsc_context_throw(context, "Invalid property"); |
| return TRUE; |
| } |
| |
| auto* foo = static_cast<Foo*>(instance); |
| if (!foo->properties.contains(name)) |
| return FALSE; |
| |
| foo->properties.remove(name); |
| return TRUE; |
| }, |
| // enumerate_properties |
| [](JSCClass* jscClass, JSCContext* context, gpointer instance) -> char** { |
| auto* checker = static_cast<LeakChecker*>(g_object_get_data(G_OBJECT(jscClass), "leak-checker")); |
| checker->watch(context); |
| |
| auto* foo = static_cast<Foo*>(instance); |
| GRefPtr<GPtrArray> properties = adoptGRef(g_ptr_array_new_with_free_func(g_free)); |
| Vector<CString> names = copyToVector(foo->properties.keys()); |
| std::sort(names.begin(), names.end()); |
| for (const auto& name : names) { |
| if (g_str_has_prefix(name.data(), "prop_enum_")) |
| g_ptr_array_add(properties.get(), g_strdup(name.data())); |
| } |
| if (!properties->len) |
| return nullptr; |
| |
| g_ptr_array_add(properties.get(), nullptr); |
| return reinterpret_cast<char**>(g_ptr_array_free(properties.leakRef(), FALSE)); |
| }, |
| // padding |
| nullptr, nullptr, nullptr, nullptr |
| }; |
| |
| static GFile* createGFile(const char* path) |
| { |
| GFile* file = g_file_new_for_path(path); |
| auto* checker = static_cast<LeakChecker*>(g_object_get_data(G_OBJECT(jsc_context_get_current()), "leak-checker")); |
| checker->watch(file); |
| return file; |
| } |
| |
| static GFile* getGFile(GFile* file) |
| { |
| return G_FILE(g_object_ref(file)); |
| } |
| |
| static JSCValue* getParent(GFile* file, JSCClass* jscClass) |
| { |
| auto* checker = static_cast<LeakChecker*>(g_object_get_data(G_OBJECT(jsc_context_get_current()), "leak-checker")); |
| GFile* parent = g_file_get_parent(file); |
| checker->watch(parent); |
| auto* value = jsc_value_new_object(jsc_context_get_current(), parent, jscClass); |
| checker->watch(value); |
| return value; |
| } |
| |
| static GString* createGString(const char* str) |
| { |
| return g_string_new(str); |
| } |
| |
| static GString* getGString(GString* str) |
| { |
| return str; |
| } |
| |
| static GString* getGStringCopyWillRaise(GString* str) |
| { |
| return static_cast<GString*>(g_boxed_copy(G_TYPE_GSTRING, str)); |
| } |
| |
| static JSCValue* getGStringCopy(GString *str, JSCClass* jscClass) |
| { |
| auto* checker = static_cast<LeakChecker*>(g_object_get_data(G_OBJECT(jsc_context_get_current()), "leak-checker")); |
| auto* copy = getGStringCopyWillRaise(str); |
| auto* value = jsc_value_new_object(jsc_context_get_current(), copy, jscClass); |
| checker->watch(value); |
| return value; |
| } |
| |
| static char* getGStringStr(GString* str) |
| { |
| return g_strdup(str->str); |
| } |
| |
| static guint64 getGStringLen(GString* str) |
| { |
| return str->len; |
| } |
| |
| static void freeGString(GString* str) |
| { |
| g_string_free(str, TRUE); |
| } |
| |
| static void testJSCClass() |
| { |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFree)); |
| checker.watch(jscClass); |
| g_assert_false(jsc_class_get_parent(jscClass)); |
| |
| GRefPtr<JSCValue> constructor = adoptGRef(jsc_class_add_constructor(jscClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE)); |
| checker.watch(constructor.get()); |
| g_assert_true(jsc_value_is_constructor(constructor.get())); |
| jsc_context_set_value(context.get(), jsc_class_get_name(jscClass), constructor.get()); |
| |
| GRefPtr<JSCValue> foo = adoptGRef(jsc_context_evaluate(context.get(), "f = new Foo();", -1)); |
| checker.watch(foo.get()); |
| g_assert_true(jsc_value_is_object(foo.get())); |
| g_assert_true(jsc_value_object_is_instance_of(foo.get(), jsc_class_get_name(jscClass))); |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "f instanceof Foo;", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| |
| GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(foo.get())); |
| g_assert_null(properties.get()); |
| |
| g_assert_false(jsc_value_object_has_property(foo.get(), "getFoo")); |
| jsc_class_add_method(jscClass, "getFoo", G_CALLBACK(getFoo), nullptr, nullptr, G_TYPE_INT, 0, G_TYPE_NONE); |
| g_assert_true(jsc_value_object_has_property(foo.get(), "getFoo")); |
| properties.reset(jsc_value_object_enumerate_properties(foo.get())); |
| g_assert_null(properties.get()); |
| GRefPtr<JSCValue> value = adoptGRef(jsc_value_object_invoke_method(foo.get(), "getFoo", G_TYPE_NONE)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 0); |
| |
| value = adoptGRef(jsc_value_object_invoke_methodv(foo.get(), "getFoo", 0, nullptr)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 0); |
| |
| GRefPtr<JSCValue> value2 = adoptGRef(jsc_context_evaluate(context.get(), "f.getFoo()", -1)); |
| checker.watch(value2.get()); |
| g_assert_true(value.get() == value2.get()); |
| |
| g_assert_false(jsc_value_object_has_property(foo.get(), "setFoo")); |
| GType parameterTypes[] = { G_TYPE_INT }; |
| jsc_class_add_methodv(jscClass, "setFoo", G_CALLBACK(setFoo), nullptr, nullptr, G_TYPE_NONE, 1, parameterTypes); |
| g_assert_true(jsc_value_object_has_property(foo.get(), "setFoo")); |
| properties.reset(jsc_value_object_enumerate_properties(foo.get())); |
| g_assert_null(properties.get()); |
| result = adoptGRef(jsc_value_object_invoke_method(foo.get(), "setFoo", G_TYPE_INT, 25, G_TYPE_NONE)); |
| checker.watch(result.get()); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "f.getFoo()", -1)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 25); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f.setFoo(45)", -1)); |
| checker.watch(result.get()); |
| value = adoptGRef(jsc_value_object_invoke_method(foo.get(), "getFoo", G_TYPE_NONE)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 45); |
| |
| value = adoptGRef(jsc_value_object_invoke_methodv(foo.get(), "getFoo", 0, nullptr)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 45); |
| |
| jsc_class_add_method_variadic(jscClass, "multiply", G_CALLBACK(multiplyFooV), nullptr, nullptr, G_TYPE_NONE); |
| g_assert_true(jsc_value_object_has_property(foo.get(), "multiply")); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "f.setFoo(1); f.multiply(1,2,3);", -1)); |
| checker.watch(value.get()); |
| value = adoptGRef(jsc_value_object_invoke_method(foo.get(), "getFoo", G_TYPE_NONE)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 6); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "f.multiply()", -1)); |
| checker.watch(value.get()); |
| value = adoptGRef(jsc_value_object_invoke_method(foo.get(), "getFoo", G_TYPE_NONE)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 6); |
| |
| GRefPtr<JSCValue> constructor2 = adoptGRef(jsc_class_add_constructorv(jscClass, "CreateWithFoo", G_CALLBACK(fooCreateWithFoo), nullptr, nullptr, G_TYPE_POINTER, 1, parameterTypes)); |
| checker.watch(constructor2.get()); |
| g_assert_true(jsc_value_is_constructor(constructor2.get())); |
| jsc_value_object_set_property(constructor.get(), "CreateWithFoo", constructor2.get()); |
| |
| GRefPtr<JSCValue> foo2 = adoptGRef(jsc_context_evaluate(context.get(), "f2 = new Foo.CreateWithFoo(42);", -1)); |
| checker.watch(foo2.get()); |
| g_assert_true(jsc_value_is_object(foo2.get())); |
| g_assert_true(jsc_value_object_is_instance_of(foo2.get(), jsc_class_get_name(jscClass))); |
| g_assert_false(foo.get() == foo2.get()); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f2 instanceof Foo;", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f2 instanceof Foo.CreateWithFoo;", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| |
| g_assert_false(jsc_value_object_has_property(foo.get(), "foo")); |
| g_assert_false(jsc_value_object_has_property(foo2.get(), "foo")); |
| jsc_class_add_property(jscClass, "foo", G_TYPE_INT, G_CALLBACK(getFoo), G_CALLBACK(setFoo), nullptr, nullptr); |
| g_assert_true(jsc_value_object_has_property(foo.get(), "foo")); |
| g_assert_true(jsc_value_object_has_property(foo2.get(), "foo")); |
| properties.reset(jsc_value_object_enumerate_properties(foo.get())); |
| g_assert_null(properties.get()); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "f2.foo", -1)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 42); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f2.foo = 52", -1)); |
| checker.watch(result.get()); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "f2.foo", -1)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 52); |
| |
| GRefPtr<JSCValue> constructorV = adoptGRef(jsc_class_add_constructor_variadic(jscClass, "CreateWithFoo", G_CALLBACK(fooCreateWithFooV), nullptr, nullptr, G_TYPE_POINTER)); |
| checker.watch(constructorV.get()); |
| g_assert_true(jsc_value_is_constructor(constructorV.get())); |
| jsc_value_object_set_property(constructor.get(), "CreateWithFooV", constructorV.get()); |
| |
| GRefPtr<JSCValue> foo3 = adoptGRef(jsc_context_evaluate(context.get(), "f3 = new Foo.CreateWithFooV(10,20,30,40);", -1)); |
| checker.watch(foo3.get()); |
| g_assert_true(jsc_value_is_object(foo3.get())); |
| g_assert_true(jsc_value_object_is_instance_of(foo3.get(), jsc_class_get_name(jscClass))); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "f3.foo", -1)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 100); |
| |
| GRefPtr<JSCValue> foo4 = adoptGRef(jsc_context_evaluate(context.get(), "f4 = new Foo.CreateWithFooV();", -1)); |
| checker.watch(foo4.get()); |
| g_assert_true(jsc_value_is_object(foo4.get())); |
| g_assert_true(jsc_value_object_is_instance_of(foo3.get(), jsc_class_get_name(jscClass))); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "f4.foo", -1)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 0); |
| |
| GFile* file = g_file_new_for_path("."); |
| checker.watch(file); |
| GRefPtr<JSCValue> constructorUserData = adoptGRef(jsc_class_add_constructor(jscClass, "CreateWithUserData", G_CALLBACK(fooCreateWithUserData), |
| file, g_object_unref, G_TYPE_POINTER, 0, G_TYPE_NONE)); |
| checker.watch(constructorUserData.get()); |
| g_assert_true(jsc_value_is_constructor(constructorUserData.get())); |
| jsc_value_object_set_property(constructor.get(), "CreateWithUserData", constructorUserData.get()); |
| |
| GRefPtr<JSCValue> foo5 = adoptGRef(jsc_context_evaluate(context.get(), "f5 = new Foo.CreateWithUserData();", -1)); |
| checker.watch(foo5.get()); |
| g_assert_true(jsc_value_is_object(foo5.get())); |
| g_assert_true(jsc_value_object_is_instance_of(foo5.get(), jsc_class_get_name(jscClass))); |
| |
| JSCClass* otherClass = jsc_context_register_class(context.get(), "Baz", nullptr, nullptr, g_free); |
| checker.watch(otherClass); |
| g_assert_false(jsc_class_get_parent(otherClass)); |
| |
| GRefPtr<JSCValue> constructor3 = adoptGRef(jsc_class_add_constructor(otherClass, nullptr, G_CALLBACK(bazCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE)); |
| checker.watch(constructor3.get()); |
| g_assert_true(jsc_value_is_constructor(constructor3.get())); |
| jsc_context_set_value(context.get(), jsc_class_get_name(otherClass), constructor3.get()); |
| |
| g_assert_false(jsc_value_object_is_instance_of(foo.get(), jsc_class_get_name(otherClass))); |
| g_assert_false(jsc_value_object_is_instance_of(foo2.get(), jsc_class_get_name(otherClass))); |
| |
| GRefPtr<JSCValue> baz = adoptGRef(jsc_value_constructor_call(constructor3.get(), G_TYPE_NONE)); |
| checker.watch(baz.get()); |
| g_assert_true(jsc_value_is_object(baz.get())); |
| g_assert_true(jsc_value_object_is_instance_of(baz.get(), jsc_class_get_name(otherClass))); |
| g_assert_false(jsc_value_object_is_instance_of(baz.get(), jsc_class_get_name(jscClass))); |
| } |
| |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFree)); |
| checker.watch(jscClass); |
| |
| GRefPtr<JSCValue> constructor = adoptGRef(jsc_class_add_constructor(jscClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE)); |
| checker.watch(constructor.get()); |
| g_assert_true(jsc_value_is_constructor(constructor.get())); |
| jsc_context_set_value(context.get(), jsc_class_get_name(jscClass), constructor.get()); |
| jsc_class_add_property(jscClass, "sibling", G_TYPE_POINTER, G_CALLBACK(getSibling), G_CALLBACK(setSibling), nullptr, nullptr); |
| |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "f1 = new Foo(); f2 = new Foo(); f2.sibling = f1", -1)); |
| checker.watch(result.get()); |
| |
| GRefPtr<JSCValue> f1 = adoptGRef(jsc_context_get_value(context.get(), "f1")); |
| checker.watch(f1.get()); |
| g_assert_true(jsc_value_is_object(f1.get())); |
| g_assert_true(jsc_value_object_has_property(f1.get(), "sibling")); |
| GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(f1.get())); |
| g_assert_null(properties.get()); |
| GRefPtr<JSCValue> f2 = adoptGRef(jsc_context_get_value(context.get(), "f2")); |
| checker.watch(f2.get()); |
| g_assert_true(jsc_value_is_object(f2.get())); |
| g_assert_true(jsc_value_object_has_property(f2.get(), "sibling")); |
| properties.reset(jsc_value_object_enumerate_properties(f2.get())); |
| g_assert_null(properties.get()); |
| |
| GRefPtr<JSCValue> value = adoptGRef(jsc_context_evaluate(context.get(), "f2.sibling", -1)); |
| checker.watch(value.get()); |
| g_assert_true(value.get() == f1.get()); |
| |
| jsc_value_object_set_property(f1.get(), "sibling", f2.get()); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "f1.sibling", -1)); |
| checker.watch(value.get()); |
| g_assert_true(value.get() == f2.get()); |
| } |
| |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFree)); |
| checker.watch(jscClass); |
| |
| GRefPtr<JSCValue> constructor = adoptGRef(jsc_class_add_constructorv(jscClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_POINTER, 0, nullptr)); |
| checker.watch(constructor.get()); |
| g_assert_true(jsc_value_is_constructor(constructor.get())); |
| jsc_context_set_value(context.get(), jsc_class_get_name(jscClass), constructor.get()); |
| jsc_class_add_property(jscClass, "foo", G_TYPE_INT, G_CALLBACK(getFoo), G_CALLBACK(setFoo), nullptr, nullptr); |
| |
| Foo* f = fooCreateWithFoo(25); |
| GRefPtr<JSCValue> foo = adoptGRef(jsc_value_new_object(context.get(), f, jscClass)); |
| checker.watch(foo.get()); |
| g_assert_true(jsc_value_is_object(foo.get())); |
| g_assert_true(jsc_value_object_is_instance_of(foo.get(), jsc_class_get_name(jscClass))); |
| g_assert_true(jsc_value_object_has_property(foo.get(), "foo")); |
| GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(foo.get())); |
| g_assert_null(properties.get()); |
| |
| jsc_context_set_value(context.get(), "f", foo.get()); |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "f instanceof Foo;", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f.foo", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 25); |
| |
| setFoo(f, 42); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f.foo", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 42); |
| |
| JSCClass* bazClass = jsc_context_register_class(context.get(), "Baz", nullptr, nullptr, g_free); |
| checker.watch(bazClass); |
| |
| GRefPtr<JSCValue> constructor2 = adoptGRef(jsc_class_add_constructor(bazClass, nullptr, G_CALLBACK(bazCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE)); |
| checker.watch(constructor2.get()); |
| g_assert_true(jsc_value_is_constructor(constructor2.get())); |
| jsc_context_set_value(context.get(), jsc_class_get_name(bazClass), constructor2.get()); |
| |
| Baz* bz = bazCreate(); |
| GRefPtr<JSCValue> baz = adoptGRef(jsc_value_new_object(context.get(), bz, bazClass)); |
| checker.watch(baz.get()); |
| g_assert_true(jsc_value_is_object(baz.get())); |
| g_assert_true(jsc_value_object_is_instance_of(baz.get(), jsc_class_get_name(bazClass))); |
| g_assert_false(jsc_value_object_is_instance_of(baz.get(), jsc_class_get_name(jscClass))); |
| g_assert_false(jsc_value_object_is_instance_of(foo.get(), jsc_class_get_name(bazClass))); |
| g_assert_false(jsc_value_object_has_property(baz.get(), "foo")); |
| |
| jsc_context_set_value(context.get(), "bz", baz.get()); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "bz instanceof Baz;", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "bz instanceof Foo;", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_false(jsc_value_to_boolean(result.get())); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f instanceof Baz;", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_false(jsc_value_to_boolean(result.get())); |
| |
| jsc_value_object_set_property(baz.get(), "foo", foo.get()); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "bz.foo", -1)); |
| checker.watch(result.get()); |
| g_assert_true(result.get() == foo.get()); |
| } |
| |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFree)); |
| checker.watch(jscClass); |
| |
| GRefPtr<JSCValue> constructor = adoptGRef(jsc_class_add_constructor(jscClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE)); |
| checker.watch(constructor.get()); |
| g_assert_true(jsc_value_is_constructor(constructor.get())); |
| jsc_context_set_value(context.get(), jsc_class_get_name(jscClass), constructor.get()); |
| jsc_class_add_property(jscClass, "foo", G_TYPE_INT, G_CALLBACK(getFoo), G_CALLBACK(setFoo), nullptr, nullptr); |
| |
| Foo* f = fooCreateWithFoo(25); |
| GRefPtr<JSCValue> foo = adoptGRef(jsc_value_new_object(context.get(), f, jscClass)); |
| checker.watch(foo.get()); |
| g_assert_true(jsc_value_is_object(foo.get())); |
| g_assert_true(jsc_value_object_is_instance_of(foo.get(), jsc_class_get_name(jscClass))); |
| g_assert_true(jsc_value_object_has_property(foo.get(), "foo")); |
| GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(foo.get())); |
| g_assert_null(properties.get()); |
| |
| jsc_context_set_value(context.get(), "f1", foo.get()); |
| jsc_context_set_value(context.get(), "f2", foo.get()); |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "f1 === f2;", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| |
| GRefPtr<JSCValue> property = adoptGRef(jsc_value_new_number(context.get(), 50)); |
| checker.watch(property.get()); |
| g_assert_false(jsc_value_object_has_property(foo.get(), "n")); |
| jsc_value_object_set_property(foo.get(), "n", property.get()); |
| g_assert_true(jsc_value_object_has_property(foo.get(), "n")); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f1.n", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 50); |
| } |
| |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFree)); |
| checker.watch(jscClass); |
| g_assert_false(jsc_class_get_parent(jscClass)); |
| |
| GRefPtr<JSCValue> constructor = adoptGRef(jsc_class_add_constructor(jscClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE)); |
| checker.watch(constructor.get()); |
| g_assert_true(jsc_value_is_constructor(constructor.get())); |
| jsc_context_set_value(context.get(), jsc_class_get_name(jscClass), constructor.get()); |
| jsc_class_add_property(jscClass, "foo", G_TYPE_INT, G_CALLBACK(getFoo), G_CALLBACK(setFoo), nullptr, nullptr); |
| GRefPtr<JSCValue> foo = adoptGRef(jsc_context_evaluate(context.get(), "f = new Foo();", -1)); |
| checker.watch(foo.get()); |
| g_assert_true(jsc_value_is_object(foo.get())); |
| g_assert_true(jsc_value_object_is_instance_of(foo.get(), jsc_class_get_name(jscClass))); |
| g_assert_true(jsc_value_object_has_property(foo.get(), "foo")); |
| GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(foo.get())); |
| g_assert_null(properties.get()); |
| |
| GRefPtr<JSCValue> value = adoptGRef(jsc_value_new_number(context.get(), 125)); |
| checker.watch(value.get()); |
| jsc_value_object_set_property(foo.get(), "foo", value.get()); |
| |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "f.__lookupGetter__('foo').call(f)", -1)); |
| checker.watch(result.get()); |
| g_assert_true(value.get() == result.get()); |
| |
| bool didThrow = false; |
| g_assert_throw_begin(exceptionHandler, didThrow); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f.__lookupGetter__('foo').call({})", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_undefined(result.get())); |
| g_assert_did_throw(exceptionHandler, didThrow); |
| } |
| |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFree)); |
| checker.watch(jscClass); |
| g_assert_false(jsc_class_get_parent(jscClass)); |
| |
| GRefPtr<JSCValue> constructor = adoptGRef(jsc_class_add_constructor(jscClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_NONE, 0, G_TYPE_NONE)); |
| checker.watch(constructor.get()); |
| g_assert_true(jsc_value_is_constructor(constructor.get())); |
| jsc_context_set_value(context.get(), jsc_class_get_name(jscClass), constructor.get()); |
| bool didThrow = false; |
| g_assert_throw_begin(exceptionHandler, didThrow); |
| GRefPtr<JSCValue> foo = adoptGRef(jsc_context_evaluate(context.get(), "f = new Foo();", -1)); |
| checker.watch(foo.get()); |
| g_assert_true(jsc_value_is_undefined(foo.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> jsNamespace = adoptGRef(jsc_value_new_object(context.get(), nullptr, nullptr)); |
| checker.watch(jsNamespace.get()); |
| g_assert_true(jsc_value_is_object(jsNamespace.get())); |
| |
| jsc_context_set_value(context.get(), "wk", jsNamespace.get()); |
| |
| JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFree)); |
| checker.watch(jscClass); |
| |
| GRefPtr<JSCValue> constructor = adoptGRef(jsc_class_add_constructor(jscClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE)); |
| checker.watch(constructor.get()); |
| g_assert_true(jsc_value_is_constructor(constructor.get())); |
| jsc_value_object_set_property(jsNamespace.get(), jsc_class_get_name(jscClass), constructor.get()); |
| jsc_class_add_property(jscClass, "foo", G_TYPE_INT, G_CALLBACK(getFoo), G_CALLBACK(setFoo), nullptr, nullptr); |
| |
| bool didThrow = false; |
| g_assert_throw_begin(exceptionHandler, didThrow); |
| GRefPtr<JSCValue> foo = adoptGRef(jsc_context_evaluate(context.get(), "f = new Foo();", -1)); |
| checker.watch(foo.get()); |
| g_assert_true(jsc_value_is_undefined(foo.get())); |
| g_assert_did_throw(exceptionHandler, didThrow); |
| |
| foo = adoptGRef(jsc_context_evaluate(context.get(), "f = new wk.Foo();", -1)); |
| g_assert_true(jsc_value_is_object(foo.get())); |
| g_assert_true(jsc_value_object_is_instance_of(foo.get(), "wk.Foo")); |
| g_assert_true(jsc_value_object_has_property(foo.get(), "foo")); |
| GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(foo.get())); |
| g_assert_null(properties.get()); |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "f instanceof wk.Foo;", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| |
| GRefPtr<JSCValue> constructor2 = adoptGRef(jsc_class_add_constructor(jscClass, "CreateWithFoo", G_CALLBACK(fooCreateWithFoo), nullptr, nullptr, G_TYPE_POINTER, 1, G_TYPE_INT)); |
| checker.watch(constructor2.get()); |
| g_assert_true(jsc_value_is_constructor(constructor2.get())); |
| jsc_value_object_set_property(constructor.get(), "CreateWithFoo", constructor2.get()); |
| |
| g_assert_throw_begin(exceptionHandler, didThrow); |
| GRefPtr<JSCValue> foo2 = adoptGRef(jsc_context_evaluate(context.get(), "f2 = new Foo.CreateWithFoo(42);", -1)); |
| checker.watch(foo2.get()); |
| g_assert_true(jsc_value_is_undefined(foo2.get())); |
| g_assert_did_throw(exceptionHandler, didThrow); |
| |
| foo2 = adoptGRef(jsc_context_evaluate(context.get(), "f2 = new wk.Foo.CreateWithFoo(42);", -1)); |
| checker.watch(foo2.get()); |
| g_assert_true(jsc_value_is_object(foo2.get())); |
| g_assert_true(jsc_value_object_is_instance_of(foo2.get(), "wk.Foo")); |
| g_assert_true(jsc_value_object_is_instance_of(foo2.get(), "wk.Foo.CreateWithFoo")); |
| g_assert_false(foo.get() == foo2.get()); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f2 instanceof wk.Foo;", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f2 instanceof wk.Foo.CreateWithFoo;", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f2.foo", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 42); |
| |
| GRefPtr<GPtrArray> parameters = adoptGRef(g_ptr_array_new_with_free_func(g_object_unref)); |
| auto* parameter = jsc_value_new_number(context.get(), 62); |
| checker.watch(parameter); |
| g_ptr_array_add(parameters.get(), parameter); |
| |
| GRefPtr<JSCValue> foo3 = adoptGRef(jsc_value_constructor_callv(constructor2.get(), parameters->len, reinterpret_cast<JSCValue**>(parameters->pdata))); |
| checker.watch(foo3.get()); |
| g_assert_true(jsc_value_is_object(foo3.get())); |
| g_assert_true(jsc_value_object_is_instance_of(foo3.get(), "wk.Foo")); |
| g_assert_true(jsc_value_object_is_instance_of(foo3.get(), "wk.Foo.CreateWithFoo")); |
| g_assert_false(foo2.get() == foo3.get()); |
| jsc_context_set_value(context.get(), "f3", foo3.get()); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f3 instanceof wk.Foo;", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f3 instanceof wk.Foo.CreateWithFoo;", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f3.foo", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 62); |
| } |
| |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, &fooVTable, reinterpret_cast<GDestroyNotify>(fooFree)); |
| checker.watch(jscClass); |
| g_object_set_data(G_OBJECT(jscClass), "leak-checker", &checker); |
| |
| GRefPtr<JSCValue> constructor = adoptGRef(jsc_class_add_constructor(jscClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE)); |
| checker.watch(constructor.get()); |
| g_assert_true(jsc_value_is_constructor(constructor.get())); |
| jsc_context_set_value(context.get(), jsc_class_get_name(jscClass), constructor.get()); |
| jsc_class_add_property(jscClass, "foo", G_TYPE_INT, G_CALLBACK(getFoo), G_CALLBACK(setFoo), nullptr, nullptr); |
| |
| GRefPtr<JSCValue> foo = adoptGRef(jsc_context_evaluate(context.get(), "f = new Foo();", -1)); |
| checker.watch(foo.get()); |
| g_assert_true(jsc_value_is_object(foo.get())); |
| g_assert_true(jsc_value_object_has_property(foo.get(), "foo")); |
| |
| g_assert_true(jsc_value_object_has_property(foo.get(), "prop_whatever")); |
| g_assert_false(jsc_value_object_has_property(foo.get(), "whatever_prop")); |
| |
| GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(foo.get())); |
| g_assert_null(properties.get()); |
| |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_1", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 0); |
| |
| GRefPtr<JSCValue> value = adoptGRef(jsc_value_object_get_property(foo.get(), "prop_1")); |
| checker.watch(value.get()); |
| g_assert_true(value.get() == result.get()); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f.foo", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 0); |
| |
| value = adoptGRef(jsc_value_object_get_property(foo.get(), "foo")); |
| checker.watch(value.get()); |
| g_assert_true(value.get() == result.get()); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "'foo' in f.__proto__", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "'foo' in f", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "'prop_1' in f.__proto__", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_false(jsc_value_to_boolean(result.get())); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "'prop_1' in f", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_1 = 25", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 25); |
| |
| g_assert_true(jsc_value_object_delete_property(foo.get(), "prop_1")); |
| g_assert_true(jsc_value_object_has_property(foo.get(), "prop_1")); |
| value = adoptGRef(jsc_value_object_get_property(foo.get(), "prop_1")); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpuint(jsc_value_to_int32(value.get()), ==, 0); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_cant_delete = 125", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 125); |
| jsc_value_object_delete_property(foo.get(), "prop_cant_delete"); |
| g_assert_true(jsc_value_object_has_property(foo.get(), "prop_cant_delete")); |
| value = adoptGRef(jsc_value_object_get_property(foo.get(), "prop_cant_delete")); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpuint(jsc_value_to_int32(value.get()), ==, 125); |
| |
| value = adoptGRef(jsc_value_new_number(context.get(), 42)); |
| checker.watch(value.get()); |
| jsc_value_object_set_property(foo.get(), "prop_1", value.get()); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_1", -1)); |
| checker.watch(result.get()); |
| g_assert_true(value.get() == result.get()); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_2 = 35", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 35); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "'prop_2' in f.__proto__", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_false(jsc_value_to_boolean(result.get())); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "'prop_2' in f", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_enum_1 = 250", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 250); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_enum_2 = 450", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 450); |
| |
| properties.reset(jsc_value_object_enumerate_properties(foo.get())); |
| g_assert_cmpuint(g_strv_length(properties.get()), ==, 2); |
| g_assert_cmpstr(properties.get()[0], ==, "prop_enum_1"); |
| g_assert_cmpstr(properties.get()[1], ==, "prop_enum_2"); |
| g_assert_null(properties.get()[2]); |
| |
| g_assert_null(jsc_context_get_exception(context.get())); |
| bool didThrow = false; |
| g_assert_throw_begin(exceptionHandler, didThrow); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_throw_on_get", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_undefined(result.get())); |
| g_assert_did_throw(exceptionHandler, didThrow); |
| g_assert_null(jsc_context_get_exception(context.get())); |
| |
| didThrow = false; |
| g_assert_throw_begin(exceptionHandler, didThrow); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_3 = 'not a number'", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_undefined(result.get())); |
| g_assert_did_throw(exceptionHandler, didThrow); |
| g_assert_null(jsc_context_get_exception(context.get())); |
| |
| didThrow = false; |
| g_assert_throw_begin(exceptionHandler, didThrow); |
| jsc_value_object_delete_property(foo.get(), "prop_throw_on_delete"); |
| g_assert_did_throw(exceptionHandler, didThrow); |
| g_assert_null(jsc_context_get_exception(context.get())); |
| |
| jsc_context_throw(context.get(), "Fake exception"); |
| GRefPtr<JSCException> previousException = jsc_context_get_exception(context.get()); |
| checker.watch(previousException.get()); |
| didThrow = false; |
| g_assert_throw_begin(exceptionHandler, didThrow); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_throw_on_get", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_undefined(result.get())); |
| g_assert_did_throw(exceptionHandler, didThrow); |
| g_assert_true(jsc_context_get_exception(context.get()) == previousException.get()); |
| |
| didThrow = false; |
| g_assert_throw_begin(exceptionHandler, didThrow); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f.prop_3 = 'not a number'", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_undefined(result.get())); |
| g_assert_did_throw(exceptionHandler, didThrow); |
| g_assert_true(jsc_context_get_exception(context.get()) == previousException.get()); |
| |
| didThrow = false; |
| g_assert_throw_begin(exceptionHandler, didThrow); |
| jsc_value_object_delete_property(foo.get(), "prop_throw_on_delete"); |
| g_assert_did_throw(exceptionHandler, didThrow); |
| g_assert_true(jsc_context_get_exception(context.get()) == previousException.get()); |
| } |
| |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| g_object_set_data(G_OBJECT(context.get()), "leak-checker", &checker); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| JSCClass* jscClass = jsc_context_register_class(context.get(), "GFile", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(g_object_unref)); |
| checker.watch(jscClass); |
| |
| GRefPtr<JSCValue> constructor = adoptGRef(jsc_class_add_constructor(jscClass, nullptr, G_CALLBACK(createGFile), nullptr, nullptr, G_TYPE_OBJECT, 1, G_TYPE_STRING)); |
| checker.watch(constructor.get()); |
| g_assert_true(jsc_value_is_constructor(constructor.get())); |
| jsc_class_add_method(jscClass, "getPath", G_CALLBACK(g_file_get_path), nullptr, nullptr, G_TYPE_STRING, 0, G_TYPE_NONE); |
| |
| jsc_context_set_value(context.get(), jsc_class_get_name(jscClass), constructor.get()); |
| |
| GRefPtr<JSCValue> file = adoptGRef(jsc_context_evaluate(context.get(), "f = new GFile('.');", -1)); |
| checker.watch(file.get()); |
| g_assert_true(jsc_value_is_object(file.get())); |
| g_assert_true(jsc_value_object_is_instance_of(file.get(), jsc_class_get_name(jscClass))); |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "f instanceof GFile;", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| |
| g_assert_true(jsc_value_object_has_property(file.get(), "getPath")); |
| GRefPtr<JSCValue> value = adoptGRef(jsc_value_object_invoke_method(file.get(), "getPath", G_TYPE_NONE)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_string(value.get())); |
| GUniquePtr<char> resultString(jsc_value_to_string(value.get())); |
| GUniquePtr<char> currentDirectory(g_get_current_dir()); |
| g_assert_cmpstr(resultString.get(), ==, currentDirectory.get()); |
| |
| GRefPtr<JSCValue> value2 = adoptGRef(jsc_context_evaluate(context.get(), "f.getPath('.');", -1)); |
| checker.watch(value2.get()); |
| g_assert_true(jsc_value_is_string(value2.get())); |
| resultString.reset(jsc_value_to_string(value2.get())); |
| g_assert_cmpstr(resultString.get(), ==, currentDirectory.get()); |
| |
| jsc_class_add_method(jscClass, "getGFile", G_CALLBACK(getGFile), nullptr, nullptr, G_TYPE_OBJECT, 0, G_TYPE_NONE); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f = new GFile('.'); f2 = f.getGFile(); f2.getPath()", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_string(result.get())); |
| resultString.reset(jsc_value_to_string(result.get())); |
| g_assert_cmpstr(resultString.get(), ==, currentDirectory.get()); |
| |
| value = adoptGRef(jsc_value_object_invoke_method(file.get(), "getGFile", G_TYPE_NONE)); |
| checker.watch(value.get()); |
| g_assert_true(value.get() == file.get()); |
| |
| jsc_class_add_method(jscClass, "getParent", G_CALLBACK(getParent), jscClass, nullptr, JSC_TYPE_VALUE, 0, G_TYPE_NONE); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f = new GFile('.'); p = f.getParent(); p.getPath()", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_string(result.get())); |
| resultString.reset(jsc_value_to_string(result.get())); |
| GUniquePtr<char> parentDirectory(g_path_get_dirname(currentDirectory.get())); |
| g_assert_cmpstr(resultString.get(), ==, parentDirectory.get()); |
| |
| jsc_class_add_method(jscClass, "equal", G_CALLBACK(g_file_equal), nullptr, nullptr, G_TYPE_BOOLEAN, 1, G_TYPE_OBJECT); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f1 = new GFile('.'); f2 = new GFile('.'); f1.equal(f2);", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| |
| GFile* fileObject = g_file_new_for_path("."); |
| checker.watch(fileObject); |
| GRefPtr<JSCValue> fileValue = adoptGRef(jsc_value_new_object(context.get(), fileObject, jscClass)); |
| checker.watch(fileValue.get()); |
| |
| result = adoptGRef(jsc_value_object_invoke_method(file.get(), "equal", G_TYPE_OBJECT, fileObject, G_TYPE_NONE)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| |
| result = adoptGRef(jsc_value_object_invoke_method(file.get(), "equal", JSC_TYPE_VALUE, fileValue.get(), G_TYPE_NONE)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| } |
| |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| g_object_set_data(G_OBJECT(context.get()), "leak-checker", &checker); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| JSCClass* jscClass = jsc_context_register_class(context.get(), "GString", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(freeGString)); |
| checker.watch(jscClass); |
| |
| GRefPtr<JSCValue> constructor = adoptGRef(jsc_class_add_constructor(jscClass, nullptr, G_CALLBACK(createGString), nullptr, nullptr, G_TYPE_GSTRING, 1, G_TYPE_STRING)); |
| checker.watch(constructor.get()); |
| g_assert_true(jsc_value_is_constructor(constructor.get())); |
| |
| jsc_class_add_property(jscClass, "str", G_TYPE_STRING, G_CALLBACK(getGStringStr), nullptr, nullptr, nullptr); |
| jsc_class_add_property(jscClass, "len", G_TYPE_UINT64, G_CALLBACK(getGStringLen), nullptr, nullptr, nullptr); |
| |
| jsc_context_set_value(context.get(), jsc_class_get_name(jscClass), constructor.get()); |
| |
| GRefPtr<JSCValue> str = adoptGRef(jsc_context_evaluate(context.get(), "s = new GString('Foo');", -1)); |
| checker.watch(str.get()); |
| g_assert_true(jsc_value_is_object(str.get())); |
| g_assert_true(jsc_value_object_is_instance_of(str.get(), jsc_class_get_name(jscClass))); |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "s instanceof GString;", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| |
| g_assert_true(jsc_value_object_has_property(str.get(), "str")); |
| GRefPtr<JSCValue> value = adoptGRef(jsc_value_object_get_property(str.get(), "str")); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_string(value.get())); |
| GUniquePtr<char> resultString(jsc_value_to_string(value.get())); |
| g_assert_cmpstr(resultString.get(), ==, "Foo"); |
| |
| GRefPtr<JSCValue> value2 = adoptGRef(jsc_context_evaluate(context.get(), "s.str", -1)); |
| checker.watch(value2.get()); |
| g_assert_true(jsc_value_is_string(value2.get())); |
| resultString.reset(jsc_value_to_string(value2.get())); |
| g_assert_cmpstr(resultString.get(), ==, "Foo"); |
| |
| GRefPtr<JSCValue> value3 = adoptGRef(jsc_context_evaluate(context.get(), "s.len", -1)); |
| checker.watch(value3.get()); |
| g_assert_true(jsc_value_is_number(value3.get())); |
| g_assert_cmpint(jsc_value_to_int32(value3.get()), ==, 3); |
| |
| jsc_class_add_method(jscClass, "getGString", G_CALLBACK(getGString), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "s = new GString('Self'); s2 = s.getGString(); s2.str;", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_string(result.get())); |
| resultString.reset(jsc_value_to_string(result.get())); |
| g_assert_cmpstr(resultString.get(), ==, "Self"); |
| |
| jsc_class_add_method(jscClass, "getGStringCopy", G_CALLBACK(getGStringCopy), jscClass, nullptr, JSC_TYPE_VALUE, 0, G_TYPE_NONE); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "s = new GString('Copy'); s2 = s.getGStringCopy(); s2.str;", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_string(result.get())); |
| resultString.reset(jsc_value_to_string(result.get())); |
| g_assert_cmpstr(resultString.get(), ==, "Copy"); |
| |
| jsc_class_add_method(jscClass, "getGStringCopyWillRaise", G_CALLBACK(getGStringCopyWillRaise), nullptr, nullptr, G_TYPE_GSTRING, 0, G_TYPE_NONE); |
| bool didThrow = false; |
| g_assert_throw_begin(exceptionHandler, didThrow); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "s = new GString('Copy'); s2 = s.getGStringCopyWillRaise(); s2.str;", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_undefined(result.get())); |
| g_assert_did_throw(exceptionHandler, didThrow); |
| |
| jsc_class_add_method(jscClass, "equal", G_CALLBACK(g_string_equal), nullptr, nullptr, G_TYPE_BOOLEAN, 1, G_TYPE_GSTRING); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "s1 = new GString('Bar'); s2 = new GString('Bar'); s1.equal(s2);", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| |
| GString* strBoxed = g_string_new("Foo"); |
| GRefPtr<JSCValue> strValue = adoptGRef(jsc_value_new_object(context.get(), strBoxed, jscClass)); |
| checker.watch(strValue.get()); |
| |
| result = adoptGRef(jsc_value_object_invoke_method(str.get(), "equal", G_TYPE_GSTRING, strBoxed, G_TYPE_NONE)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| |
| result = adoptGRef(jsc_value_object_invoke_method(str.get(), "equal", JSC_TYPE_VALUE, strValue.get(), G_TYPE_NONE)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| } |
| } |
| |
| typedef struct { |
| Foo parent; |
| int bar; |
| } Bar; |
| |
| static Bar* barCreate() |
| { |
| Bar* bar = g_new0(Bar, 1); |
| new (bar) Bar(); |
| return bar; |
| } |
| |
| static void barFree(Bar* bar) |
| { |
| bar->~Bar(); |
| g_free(bar); |
| } |
| |
| static void setBar(Bar* bar, int value) |
| { |
| bar->bar = value; |
| } |
| |
| static int getBar(Bar* bar) |
| { |
| return bar->bar; |
| } |
| |
| static JSCClassVTable barVTable = { |
| // get_property |
| nullptr, |
| // set_property |
| nullptr, |
| // has_property |
| nullptr, |
| // delete_property |
| nullptr, |
| // enumerate_properties |
| [](JSCClass* jscClass, JSCContext* context, gpointer instance) -> char** { |
| auto* checker = static_cast<LeakChecker*>(g_object_get_data(G_OBJECT(jscClass), "leak-checker")); |
| checker->watch(context); |
| |
| auto* properties = static_cast<char**>(g_malloc0(2 * sizeof(char*))); |
| properties[0] = g_strdup("bar"); |
| return properties; |
| }, |
| // padding |
| nullptr, nullptr, nullptr, nullptr |
| }; |
| |
| static void testJSCPrototypes() |
| { |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| JSCClass* fooClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFree)); |
| checker.watch(fooClass); |
| g_assert_false(jsc_class_get_parent(fooClass)); |
| GRefPtr<JSCValue> fooConstructor = adoptGRef(jsc_class_add_constructor(fooClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE)); |
| checker.watch(fooConstructor.get()); |
| g_assert_true(jsc_value_is_constructor(fooConstructor.get())); |
| jsc_context_set_value(context.get(), jsc_class_get_name(fooClass), fooConstructor.get()); |
| jsc_class_add_method(fooClass, "multiply", G_CALLBACK(multiplyFoo), nullptr, nullptr, G_TYPE_NONE, 1, G_TYPE_INT); |
| jsc_class_add_property(fooClass, "foo", G_TYPE_INT, G_CALLBACK(getFoo), G_CALLBACK(setFoo), nullptr, nullptr); |
| |
| JSCClass* barClass = jsc_context_register_class(context.get(), "Bar", fooClass, nullptr, reinterpret_cast<GDestroyNotify>(barFree)); |
| checker.watch(barClass); |
| g_assert_true(jsc_class_get_parent(barClass) == fooClass); |
| GRefPtr<JSCValue> barConstructor = adoptGRef(jsc_class_add_constructor(barClass, nullptr, G_CALLBACK(barCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE)); |
| checker.watch(barConstructor.get()); |
| g_assert_true(jsc_value_is_constructor(barConstructor.get())); |
| jsc_context_set_value(context.get(), jsc_class_get_name(barClass), barConstructor.get()); |
| jsc_class_add_property(barClass, "bar", G_TYPE_INT, G_CALLBACK(getBar), G_CALLBACK(setBar), nullptr, nullptr); |
| |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "f = new Foo(); b = new Bar();", -1)); |
| checker.watch(result.get()); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f.__proto__ == Foo.prototype", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "b.__proto__ == Bar.prototype", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "b.__proto__.__proto__ == Foo.prototype", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| |
| GRefPtr<JSCValue> foo = adoptGRef(jsc_context_get_value(context.get(), "f")); |
| checker.watch(foo.get()); |
| g_assert_true(jsc_value_is_object(foo.get())); |
| g_assert_true(jsc_value_object_is_instance_of(foo.get(), jsc_class_get_name(fooClass))); |
| g_assert_false(jsc_value_object_is_instance_of(foo.get(), jsc_class_get_name(barClass))); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f instanceof Foo", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f instanceof Bar", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_false(jsc_value_to_boolean(result.get())); |
| g_assert_true(jsc_value_object_has_property(foo.get(), "foo")); |
| g_assert_false(jsc_value_object_has_property(foo.get(), "bar")); |
| GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(foo.get())); |
| g_assert_null(properties.get()); |
| |
| GRefPtr<JSCValue> bar = adoptGRef(jsc_context_get_value(context.get(), "b")); |
| checker.watch(bar.get()); |
| g_assert_true(jsc_value_is_object(bar.get())); |
| g_assert_true(jsc_value_object_is_instance_of(bar.get(), jsc_class_get_name(barClass))); |
| g_assert_true(jsc_value_object_is_instance_of(bar.get(), jsc_class_get_name(fooClass))); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "b instanceof Bar", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "b instanceof Foo", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| g_assert_true(jsc_value_object_has_property(bar.get(), "bar")); |
| g_assert_true(jsc_value_object_has_property(bar.get(), "foo")); |
| properties.reset(jsc_value_object_enumerate_properties(bar.get())); |
| g_assert_null(properties.get()); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "b.bar = 25; b.foo = 42;", -1)); |
| checker.watch(result.get()); |
| |
| GRefPtr<JSCValue> value = adoptGRef(jsc_context_evaluate(context.get(), "b.bar", -1)); |
| 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_context_evaluate(context.get(), "b.foo", -1)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 42); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "b.multiply(2)", -1)); |
| checker.watch(result.get()); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "b.foo", -1)); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 84); |
| } |
| |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| JSCClass* fooClass = jsc_context_register_class(context.get(), "Foo", nullptr, &fooVTable, reinterpret_cast<GDestroyNotify>(fooFree)); |
| checker.watch(fooClass); |
| g_object_set_data(G_OBJECT(fooClass), "leak-checker", &checker); |
| |
| GRefPtr<JSCValue> fooConstructor = adoptGRef(jsc_class_add_constructor(fooClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE)); |
| checker.watch(fooConstructor.get()); |
| g_assert_true(jsc_value_is_constructor(fooConstructor.get())); |
| jsc_context_set_value(context.get(), jsc_class_get_name(fooClass), fooConstructor.get()); |
| jsc_class_add_property(fooClass, "foo", G_TYPE_INT, G_CALLBACK(getFoo), G_CALLBACK(setFoo), nullptr, nullptr); |
| |
| JSCClass* barClass = jsc_context_register_class(context.get(), "Bar", fooClass, &barVTable, reinterpret_cast<GDestroyNotify>(barFree)); |
| checker.watch(barClass); |
| g_object_set_data(G_OBJECT(barClass), "leak-checker", &checker); |
| g_assert_true(jsc_class_get_parent(barClass) == fooClass); |
| GRefPtr<JSCValue> barConstructor = adoptGRef(jsc_class_add_constructor(barClass, nullptr, G_CALLBACK(barCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE)); |
| checker.watch(barConstructor.get()); |
| g_assert_true(jsc_value_is_constructor(barConstructor.get())); |
| jsc_context_set_value(context.get(), jsc_class_get_name(barClass), barConstructor.get()); |
| jsc_class_add_property(barClass, "bar", G_TYPE_INT, G_CALLBACK(getBar), G_CALLBACK(setBar), nullptr, nullptr); |
| |
| GRefPtr<JSCValue> bar = adoptGRef(jsc_context_evaluate(context.get(), "b = new Bar();", -1)); |
| checker.watch(bar.get()); |
| g_assert_true(jsc_value_is_object(bar.get())); |
| g_assert_true(jsc_value_object_has_property(bar.get(), "bar")); |
| g_assert_true(jsc_value_object_has_property(bar.get(), "foo")); |
| |
| g_assert_true(jsc_value_object_has_property(bar.get(), "prop_whatever")); |
| g_assert_false(jsc_value_object_has_property(bar.get(), "whatever_prop")); |
| |
| GUniquePtr<char*> properties(jsc_value_object_enumerate_properties(bar.get())); |
| g_assert_cmpuint(g_strv_length(properties.get()), ==, 1); |
| g_assert_cmpstr(properties.get()[0], ==, "bar"); |
| g_assert_null(properties.get()[1]); |
| |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "b.prop_1", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 0); |
| |
| GRefPtr<JSCValue> value = adoptGRef(jsc_value_object_get_property(bar.get(), "prop_1")); |
| checker.watch(value.get()); |
| g_assert_true(value.get() == result.get()); |
| |
| g_assert_true(jsc_value_object_delete_property(bar.get(), "prop_1")); |
| g_assert_true(jsc_value_object_has_property(bar.get(), "prop_1")); |
| value = adoptGRef(jsc_value_object_get_property(bar.get(), "prop_1")); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpuint(jsc_value_to_int32(value.get()), ==, 0); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "b.prop_cant_delete = 125", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 125); |
| jsc_value_object_delete_property(bar.get(), "prop_cant_delete"); |
| g_assert_true(jsc_value_object_has_property(bar.get(), "prop_cant_delete")); |
| value = adoptGRef(jsc_value_object_get_property(bar.get(), "prop_cant_delete")); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpuint(jsc_value_to_int32(value.get()), ==, 125); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "b.prop_enum_1 = 250", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 250); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "b.prop_enum_2 = 450", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpuint(jsc_value_to_int32(result.get()), ==, 450); |
| |
| properties.reset(jsc_value_object_enumerate_properties(bar.get())); |
| g_assert_cmpuint(g_strv_length(properties.get()), ==, 3); |
| g_assert_cmpstr(properties.get()[0], ==, "bar"); |
| g_assert_cmpstr(properties.get()[1], ==, "prop_enum_1"); |
| g_assert_cmpstr(properties.get()[2], ==, "prop_enum_2"); |
| g_assert_null(properties.get()[3]); |
| } |
| } |
| |
| static void createError() |
| { |
| 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 createFormattedError(const char* details) |
| { |
| jsc_context_throw_printf(jsc_context_get_current(), "API exception: %s", details); |
| } |
| |
| static void createCustomFormattedError(const char* details) |
| { |
| jsc_context_throw_with_name_printf(jsc_context_get_current(), "CustomFormattedAPIError", "API custom exception: %s", details); |
| } |
| |
| static void testJSCExceptions() |
| { |
| { |
| 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(context.get(), "foo", -1)); |
| checker.watch(result.get()); |
| // By default exceptions are not caught. |
| 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), ==, "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_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())); |
| } |
| |
| { |
| 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(context.get(), "f = 25;\nfoo;", -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_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())); |
| } |
| |
| { |
| 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(), |
| "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())); |
| 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())); |
| } |
| |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| g_assert_false(jsc_context_get_exception(context.get())); |
| |
| GRefPtr<JSCValue> function = adoptGRef(jsc_value_new_function(context.get(), "createError", G_CALLBACK(createError), nullptr, nullptr, G_TYPE_NONE, 0, G_TYPE_NONE)); |
| checker.watch(function.get()); |
| jsc_context_set_value(context.get(), "createError", function.get()); |
| |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "var result; try { createError(); } catch (e) { result = 'Caught exception'; }", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_string(result.get())); |
| GUniquePtr<char> resultString(jsc_value_to_string(result.get())); |
| g_assert_cmpstr(resultString.get(), ==, "Caught exception"); |
| g_assert_false(jsc_context_get_exception(context.get())); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "var result; createError(); 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), ==, "Error"); |
| g_assert_cmpstr(jsc_exception_get_message(exception), ==, "API exception"); |
| g_assert_cmpuint(jsc_exception_get_line_number(exception), ==, 1); |
| 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())); |
| } |
| |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| 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> function = adoptGRef(jsc_value_new_function(context.get(), "createFormattedError", G_CALLBACK(createFormattedError), nullptr, nullptr, G_TYPE_NONE, 1, G_TYPE_STRING)); |
| checker.watch(function.get()); |
| jsc_context_set_value(context.get(), "createFormattedError", function.get()); |
| |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "var result; createFormattedError('error details'); 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), ==, "Error"); |
| g_assert_cmpstr(jsc_exception_get_message(exception), ==, "API exception: error details"); |
| g_assert_cmpuint(jsc_exception_get_line_number(exception), ==, 1); |
| g_assert_cmpuint(jsc_exception_get_column_number(exception), ==, 33); |
| g_assert_null(jsc_exception_get_source_uri(exception)); |
| g_assert_cmpstr(jsc_exception_get_backtrace_string(exception), ==, "createFormattedError@[native code]\nglobal code"); |
| GUniquePtr<char> errorString(jsc_exception_to_string(exception)); |
| g_assert_cmpstr(errorString.get(), ==, "Error: API exception: error details"); |
| GUniquePtr<char> reportString(jsc_exception_report(exception)); |
| g_assert_cmpstr(reportString.get(), ==, ":1:33 Error: API exception: error details\n createFormattedError@[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> function = adoptGRef(jsc_value_new_function(context.get(), "createCustomFormattedError", G_CALLBACK(createCustomFormattedError), nullptr, nullptr, G_TYPE_NONE, 1, G_TYPE_STRING)); |
| checker.watch(function.get()); |
| jsc_context_set_value(context.get(), "createCustomFormattedError", function.get()); |
| |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "var result; createCustomFormattedError('error details'); 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), ==, "CustomFormattedAPIError"); |
| g_assert_cmpstr(jsc_exception_get_message(exception), ==, "API custom exception: error details"); |
| g_assert_cmpuint(jsc_exception_get_line_number(exception), ==, 1); |
| g_assert_cmpuint(jsc_exception_get_column_number(exception), ==, 39); |
| g_assert_null(jsc_exception_get_source_uri(exception)); |
| g_assert_cmpstr(jsc_exception_get_backtrace_string(exception), ==, "createCustomFormattedError@[native code]\nglobal code"); |
| GUniquePtr<char> errorString(jsc_exception_to_string(exception)); |
| g_assert_cmpstr(errorString.get(), ==, "CustomFormattedAPIError: API custom exception: error details"); |
| GUniquePtr<char> reportString(jsc_exception_report(exception)); |
| g_assert_cmpstr(reportString.get(), ==, ":1:39 CustomFormattedAPIError: API custom exception: error details\n createCustomFormattedError@[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; |
| bool wasDeleted; |
| }; |
| |
| Test test = { context.get(), nullptr, false }; |
| jsc_context_push_exception_handler(context.get(), [](JSCContext* context, JSCException* exception, gpointer userData) { |
| auto* test = static_cast<Test*>(userData); |
| g_assert_true(context == test->context); |
| g_assert_false(test->exception); |
| g_assert_false(test->wasDeleted); |
| test->exception = exception; |
| }, &test, [](gpointer userData) { |
| static_cast<Test*>(userData)->wasDeleted = true; |
| }); |
| |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "foo", -1)); |
| checker.watch(result.get()); |
| // Exception was caught by the user handler. |
| 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()); |
| g_assert_true(test.wasDeleted); |
| |
| test.exception = nullptr; |
| test.wasDeleted = false; |
| jsc_context_push_exception_handler(context.get(), [](JSCContext* context, JSCException* exception, gpointer userData) { |
| auto* test = static_cast<Test*>(userData); |
| g_assert_true(context == test->context); |
| g_assert_false(test->exception); |
| g_assert_false(test->wasDeleted); |
| test->exception = exception; |
| jsc_context_throw_exception(context, exception); |
| }, &test, [](gpointer userData) { |
| static_cast<Test*>(userData)->wasDeleted = true; |
| }); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "foo", -1)); |
| checker.watch(result.get()); |
| // Exception was handled by the user handler, but not caught. |
| auto* exception = jsc_context_get_exception(context.get()); |
| g_assert_true(JSC_IS_EXCEPTION(exception)); |
| checker.watch(exception); |
| g_assert_true(exception == test.exception.get()); |
| |
| g_assert_false(test.wasDeleted); |
| jsc_context_pop_exception_handler(context.get()); |
| g_assert_true(test.wasDeleted); |
| |
| jsc_context_clear_exception(context.get()); |
| g_assert_null(jsc_context_get_exception(context.get())); |
| } |
| } |
| |
| static void testJSCPromises() |
| { |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| GRefPtr<JSCValue> promise = adoptGRef(jsc_context_get_value(context.get(), "Promise")); |
| checker.watch(promise.get()); |
| g_assert_true(jsc_value_is_function(promise.get())); |
| |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "typeof Promise", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_string(result.get())); |
| GUniquePtr<char> resultString(jsc_value_to_string(result.get())); |
| g_assert_cmpstr(resultString.get(), ==, "function"); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "result = 0; Promise.resolve(42).then(function (value) { result = value; });", -1)); |
| checker.watch(result.get()); |
| |
| GRefPtr<JSCValue> value = adoptGRef(jsc_context_get_value(context.get(), "result")); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 42); |
| } |
| |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| JSCClass* fooClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFree)); |
| checker.watch(fooClass); |
| g_assert_false(jsc_class_get_parent(fooClass)); |
| GRefPtr<JSCValue> fooConstructor = adoptGRef(jsc_class_add_constructor(fooClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE)); |
| checker.watch(fooConstructor.get()); |
| g_assert_true(jsc_value_is_constructor(fooConstructor.get())); |
| jsc_context_set_value(context.get(), jsc_class_get_name(fooClass), fooConstructor.get()); |
| jsc_class_add_method(fooClass, "getMultiplyFooAsync", G_CALLBACK(getMultiplyFooAsync), &checker, nullptr, JSC_TYPE_VALUE, 1, G_TYPE_INT); |
| jsc_class_add_property(fooClass, "foo", G_TYPE_INT, G_CALLBACK(getFoo), G_CALLBACK(setFoo), nullptr, nullptr); |
| |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "result = 0; f = new Foo(); f.foo = 5; f.getMultiplyFooAsync(2).then(function (value) { result = value; }, function (error) { result = -1; });", -1)); |
| checker.watch(result.get()); |
| |
| GRefPtr<JSCValue> value = adoptGRef(jsc_context_get_value(context.get(), "result")); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 10); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "result = 0; f.getMultiplyFooAsync(0).then(function (value) { result = value; }, function (error) { result = -1; });", -1)); |
| checker.watch(result.get()); |
| value = adoptGRef(jsc_context_get_value(context.get(), "result")); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, -1); |
| } |
| } |
| |
| static bool s_fooWasFreed; |
| |
| static void fooFreeAndLog(Foo* foo) |
| { |
| fooFree(foo); |
| s_fooWasFreed = true; |
| } |
| |
| static void testJSCGarbageCollector() |
| { |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| GRefPtr<JSCValue> object = adoptGRef(jsc_value_new_object(context.get(), nullptr, nullptr)); |
| checker.watch(object.get()); |
| |
| GRefPtr<JSCValue> foo = adoptGRef(jsc_value_new_number(context.get(), 25)); |
| checker.watch(foo.get()); |
| g_assert_true(jsc_value_is_number(foo.get())); |
| g_assert_cmpint(jsc_value_to_int32(foo.get()), ==, 25); |
| jsc_value_object_set_property(object.get(), "foo", foo.get()); |
| |
| jsc_context_set_value(context.get(), "f", object.get()); |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "f", -1)); |
| checker.watch(result.get()); |
| g_assert_true(object.get() == result.get()); |
| |
| object = nullptr; |
| foo = nullptr; |
| result = nullptr; |
| jscContextGarbageCollect(context.get()); |
| object = adoptGRef(jsc_context_get_value(context.get(), "f")); |
| checker.watch(object.get()); |
| g_assert_true(jsc_value_is_object(object.get())); |
| foo = adoptGRef(jsc_context_evaluate(context.get(), "f.foo", -1)); |
| checker.watch(foo.get()); |
| g_assert_true(jsc_value_is_number(foo.get())); |
| g_assert_cmpint(jsc_value_to_int32(foo.get()), ==, 25); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f = undefined", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_undefined(result.get())); |
| |
| jscContextGarbageCollect(context.get()); |
| |
| g_assert_true(jsc_value_is_object(object.get())); |
| jsc_context_set_value(context.get(), "f", object.get()); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f", -1)); |
| checker.watch(result.get()); |
| g_assert_true(object.get() == result.get()); |
| foo = adoptGRef(jsc_context_evaluate(context.get(), "f.foo", -1)); |
| checker.watch(foo.get()); |
| g_assert_true(jsc_value_is_number(foo.get())); |
| g_assert_cmpint(jsc_value_to_int32(foo.get()), ==, 25); |
| } |
| |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| s_fooWasFreed = false; |
| |
| JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFreeAndLog)); |
| checker.watch(jscClass); |
| g_assert_false(jsc_class_get_parent(jscClass)); |
| |
| GRefPtr<JSCValue> constructor = adoptGRef(jsc_class_add_constructor(jscClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE)); |
| checker.watch(constructor.get()); |
| g_assert_true(jsc_value_is_constructor(constructor.get())); |
| jsc_context_set_value(context.get(), jsc_class_get_name(jscClass), constructor.get()); |
| GRefPtr<JSCValue> object = adoptGRef(jsc_context_evaluate(context.get(), "f = new Foo();", -1)); |
| checker.watch(object.get()); |
| g_assert_true(jsc_value_is_object(object.get())); |
| g_assert_true(jsc_value_object_is_instance_of(object.get(), jsc_class_get_name(jscClass))); |
| |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "f", -1)); |
| checker.watch(result.get()); |
| g_assert_true(object.get() == result.get()); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f = undefined", -1)); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_undefined(result.get())); |
| |
| g_assert_false(s_fooWasFreed); |
| jscContextGarbageCollect(context.get()); |
| g_assert_false(s_fooWasFreed); |
| |
| object = nullptr; |
| g_assert_false(s_fooWasFreed); |
| jscContextGarbageCollect(context.get()); |
| g_assert_true(s_fooWasFreed); |
| } |
| } |
| |
| static void weakValueClearedCallback(JSCWeakValue* weakValue, bool* weakValueCleared) |
| { |
| *weakValueCleared = true; |
| g_assert_null(jsc_weak_value_get_value(weakValue)); |
| } |
| |
| static void testJSCWeakValue() |
| { |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| GRefPtr<JSCValue> object = adoptGRef(jsc_value_new_object(context.get(), nullptr, nullptr)); |
| checker.watch(object.get()); |
| |
| GRefPtr<JSCWeakValue> weak = adoptGRef(jsc_weak_value_new(object.get())); |
| checker.watch(weak.get()); |
| bool weakValueCleared = false; |
| g_signal_connect(weak.get(), "cleared", G_CALLBACK(weakValueClearedCallback), &weakValueCleared); |
| |
| jsc_context_set_value(context.get(), "foo", object.get()); |
| jscContextGarbageCollect(context.get()); |
| g_assert_false(weakValueCleared); |
| |
| GRefPtr<JSCValue> foo = adoptGRef(jsc_context_get_value(context.get(), "foo")); |
| checker.watch(foo.get()); |
| g_assert_true(object.get() == foo.get()); |
| |
| GRefPtr<JSCValue> weakFoo = adoptGRef(jsc_weak_value_get_value(weak.get())); |
| checker.watch(weakFoo.get()); |
| g_assert_true(foo.get() == weakFoo.get()); |
| |
| GRefPtr<JSCValue> undefinedValue = adoptGRef(jsc_value_new_undefined(context.get())); |
| checker.watch(undefinedValue.get()); |
| jsc_context_set_value(context.get(), "foo", undefinedValue.get()); |
| weakFoo = nullptr; |
| foo = nullptr; |
| object = nullptr; |
| |
| // The value is still reachable, but unprotected. |
| g_assert_false(weakValueCleared); |
| weakFoo = adoptGRef(jsc_weak_value_get_value(weak.get())); |
| checker.watch(weakFoo.get()); |
| g_assert_true(jsc_value_is_object(weakFoo.get())); |
| weakFoo = nullptr; |
| |
| jscContextGarbageCollect(context.get()); |
| g_assert_true(weakValueCleared); |
| g_assert_null(jsc_weak_value_get_value(weak.get())); |
| } |
| |
| { |
| LeakChecker checker; |
| GRefPtr<JSCWeakValue> weakObject; |
| bool weakValueCleared = false; |
| { |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| GRefPtr<JSCValue> object = adoptGRef(jsc_context_evaluate(context.get(), "obj = {};", -1)); |
| checker.watch(object.get()); |
| g_assert_true(JSC_IS_VALUE(object.get())); |
| g_assert_true(jsc_value_is_object(object.get())); |
| |
| weakObject = adoptGRef(jsc_weak_value_new(object.get())); |
| checker.watch(weakObject.get()); |
| g_signal_connect(weakObject.get(), "cleared", G_CALLBACK(weakValueClearedCallback), &weakValueCleared); |
| |
| object = adoptGRef(jsc_context_evaluate(context.get(), "obj = null", -1)); |
| checker.watch(object.get()); |
| g_assert_false(weakValueCleared); |
| } |
| |
| g_assert_true(weakValueCleared); |
| g_assert_null(jsc_weak_value_get_value(weakObject.get())); |
| } |
| |
| { |
| LeakChecker checker; |
| GRefPtr<JSCWeakValue> weakObj; |
| bool weakObjValueCleared = false; |
| GRefPtr<JSCWeakValue> weakStr; |
| bool weakStrValueCleared = false; |
| GRefPtr<JSCWeakValue> weakPrimitive; |
| bool weakPrimitiveValueCleared = false; |
| { |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "obj = { 'foo' : 'bar' }; str = 'Hello World'; primitive = 25;", -1)); |
| checker.watch(result.get()); |
| |
| GRefPtr<JSCValue> value = adoptGRef(jsc_context_get_value(context.get(), "obj")); |
| checker.watch(value.get()); |
| weakObj = adoptGRef(jsc_weak_value_new(value.get())); |
| checker.watch(weakObj.get()); |
| g_signal_connect(weakObj.get(), "cleared", G_CALLBACK(weakValueClearedCallback), &weakObjValueCleared); |
| |
| value = adoptGRef(jsc_context_get_value(context.get(), "str")); |
| checker.watch(value.get()); |
| weakStr = adoptGRef(jsc_weak_value_new(value.get())); |
| checker.watch(weakStr.get()); |
| g_signal_connect(weakStr.get(), "cleared", G_CALLBACK(weakValueClearedCallback), &weakStrValueCleared); |
| |
| value = adoptGRef(jsc_context_get_value(context.get(), "primitive")); |
| checker.watch(value.get()); |
| weakPrimitive = adoptGRef(jsc_weak_value_new(value.get())); |
| checker.watch(weakPrimitive.get()); |
| g_signal_connect(weakPrimitive.get(), "cleared", G_CALLBACK(weakValueClearedCallback), &weakPrimitiveValueCleared); |
| |
| value = nullptr; |
| jscContextGarbageCollect(context.get()); |
| g_assert_false(weakObjValueCleared); |
| g_assert_false(weakStrValueCleared); |
| g_assert_false(weakPrimitiveValueCleared); |
| |
| value = adoptGRef(jsc_weak_value_get_value(weakObj.get())); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_object(value.get())); |
| |
| value = adoptGRef(jsc_weak_value_get_value(weakStr.get())); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_string(value.get())); |
| |
| value = adoptGRef(jsc_weak_value_get_value(weakPrimitive.get())); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| value = nullptr; |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "str = undefined", -1)); |
| checker.watch(result.get()); |
| jscContextGarbageCollect(context.get()); |
| |
| g_assert_true(weakStrValueCleared); |
| g_assert_false(weakObjValueCleared); |
| g_assert_false(weakPrimitiveValueCleared); |
| g_assert_null(jsc_weak_value_get_value(weakStr.get())); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f = undefined", -1)); |
| checker.watch(result.get()); |
| jscContextGarbageCollect(context.get()); |
| |
| // Non-string primitve values are not garbage collected, the weak value |
| // will be cleared when the global object is destroyed. |
| g_assert_false(weakPrimitiveValueCleared); |
| g_assert_false(weakObjValueCleared); |
| g_assert_true(weakStrValueCleared); |
| |
| value = adoptGRef(jsc_weak_value_get_value(weakPrimitive.get())); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| value = nullptr; |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "obj = undefined", -1)); |
| checker.watch(result.get()); |
| jscContextGarbageCollect(context.get()); |
| |
| g_assert_true(weakObjValueCleared); |
| g_assert_true(weakStrValueCleared); |
| g_assert_false(weakPrimitiveValueCleared); |
| g_assert_null(jsc_weak_value_get_value(weakObj.get())); |
| weakObjValueCleared = false; |
| weakStrValueCleared = false; |
| } |
| |
| // Context is now destroyed, only the primitive value should be notified. |
| g_assert_true(weakPrimitiveValueCleared); |
| g_assert_false(weakObjValueCleared); |
| g_assert_false(weakStrValueCleared); |
| g_assert_null(jsc_weak_value_get_value(weakPrimitive.get())); |
| } |
| } |
| |
| static void testsJSCVirtualMachine() |
| { |
| { |
| LeakChecker checker; |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new()); |
| checker.watch(context.get()); |
| ExceptionHandler exceptionHandler(context.get()); |
| |
| auto thread = Thread::create("JSCVirtualMachineTest", [&] { |
| JSCClass* jscClass = jsc_context_register_class(context.get(), "Foo", nullptr, nullptr, reinterpret_cast<GDestroyNotify>(fooFreeAndLog)); |
| checker.watch(jscClass); |
| g_assert_false(jsc_class_get_parent(jscClass)); |
| |
| GRefPtr<JSCValue> constructor = adoptGRef(jsc_class_add_constructor(jscClass, nullptr, G_CALLBACK(fooCreate), nullptr, nullptr, G_TYPE_POINTER, 0, G_TYPE_NONE)); |
| checker.watch(constructor.get()); |
| g_assert_true(jsc_value_is_constructor(constructor.get())); |
| jsc_context_set_value(context.get(), jsc_class_get_name(jscClass), constructor.get()); |
| |
| GRefPtr<JSCValue> object = adoptGRef(jsc_context_evaluate(context.get(), "f = new Foo();", -1)); |
| checker.watch(object.get()); |
| g_assert_true(jsc_value_get_context(object.get()) == context.get()); |
| g_assert_true(jsc_value_is_object(object.get())); |
| g_assert_true(jsc_value_object_is_instance_of(object.get(), jsc_class_get_name(jscClass))); |
| }); |
| thread->waitForCompletion(); |
| |
| GRefPtr<JSCValue> object = adoptGRef(jsc_context_get_value(context.get(), "f")); |
| checker.watch(object.get()); |
| g_assert_true(jsc_value_is_object(object.get())); |
| |
| jscContextGarbageCollect(context.get()); |
| } |
| |
| { |
| Vector<Ref<Thread>, 5> threads; |
| bool ok = true; |
| for (unsigned i = 0; i < 5; ++i) { |
| threads.append(Thread::create("JSCVirtualMachineTest", [&ok] { |
| GRefPtr<JSCVirtualMachine> vm = adoptGRef(jsc_virtual_machine_new()); |
| GRefPtr<JSCContext> context = adoptGRef(jsc_context_new_with_virtual_machine(vm.get())); |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), |
| "var array = [{}];\n" |
| "for (var i = 0; i < 20; ++i) {\n" |
| " var newArray = new Array(array.length * 2);\n" |
| " for (var j = 0; j < newArray.length; ++j)\n" |
| " newArray[j] = {parent: array[j / 2]};\n" |
| " array = newArray;\n" |
| "}\n", -1 |
| )); |
| g_assert_true(jsc_value_get_context(result.get()) == context.get()); |
| if (auto* exception = jsc_context_get_exception(context.get())) { |
| g_message("Uncaught exception: %s", jsc_exception_get_message(exception)); |
| ok = false; |
| } |
| GRefPtr<JSCValue> value = adoptGRef(jsc_context_get_value(context.get(), "array")); |
| g_assert_true(jsc_value_get_context(value.get()) == context.get()); |
| if (!jsc_value_is_object(value.get())) { |
| g_message("Did not find \"array\" variable"); |
| ok = false; |
| } |
| jscContextGarbageCollect(context.get()); |
| })); |
| } |
| |
| for (auto& thread : threads) |
| thread->waitForCompletion(); |
| |
| g_assert_true(ok); |
| } |
| } |
| |
| static void testsJSCOptions() |
| { |
| gboolean useJIT; |
| g_assert_true(jsc_options_get_boolean(JSC_OPTIONS_USE_JIT, &useJIT)); |
| g_assert_true(useJIT); |
| g_assert_true(jsc_options_set_boolean(JSC_OPTIONS_USE_JIT, FALSE)); |
| g_assert_true(jsc_options_get_boolean(JSC_OPTIONS_USE_JIT, &useJIT)); |
| g_assert_false(useJIT); |
| g_assert_true(jsc_options_set_boolean(JSC_OPTIONS_USE_JIT, TRUE)); |
| |
| gint thresholdForJITAfterWarmUp; |
| g_assert_true(jsc_options_get_int("thresholdForJITAfterWarmUp", &thresholdForJITAfterWarmUp)); |
| g_assert_cmpint(thresholdForJITAfterWarmUp, ==, 500); |
| g_assert_true(jsc_options_set_int("thresholdForJITAfterWarmUp", 1000)); |
| g_assert_true(jsc_options_get_int("thresholdForJITAfterWarmUp", &thresholdForJITAfterWarmUp)); |
| g_assert_cmpint(thresholdForJITAfterWarmUp, ==, 1000); |
| g_assert_true(jsc_options_set_int("thresholdForJITAfterWarmUp", 500)); |
| |
| guint maxPerThreadStackUsage; |
| g_assert_true(jsc_options_get_uint("maxPerThreadStackUsage", &maxPerThreadStackUsage)); |
| g_assert_cmpuint(maxPerThreadStackUsage, ==, 4194304); |
| g_assert_true(jsc_options_set_uint("maxPerThreadStackUsage", 4096)); |
| g_assert_true(jsc_options_get_uint("maxPerThreadStackUsage", &maxPerThreadStackUsage)); |
| g_assert_cmpuint(maxPerThreadStackUsage, ==, 4096); |
| g_assert_true(jsc_options_set_uint("maxPerThreadStackUsage", 4194304)); |
| |
| gsize webAssemblyPartialCompileLimit; |
| g_assert_true(jsc_options_get_size("webAssemblyPartialCompileLimit", &webAssemblyPartialCompileLimit)); |
| g_assert_cmpuint(webAssemblyPartialCompileLimit, ==, 5000); |
| g_assert_true(jsc_options_set_size("webAssemblyPartialCompileLimit", 6000)); |
| g_assert_true(jsc_options_get_size("webAssemblyPartialCompileLimit", &webAssemblyPartialCompileLimit)); |
| g_assert_cmpuint(webAssemblyPartialCompileLimit, ==, 6000); |
| g_assert_true(jsc_options_set_size("webAssemblyPartialCompileLimit", 5000)); |
| |
| gdouble smallHeapRAMFraction; |
| g_assert_true(jsc_options_get_double("smallHeapRAMFraction", &smallHeapRAMFraction)); |
| g_assert_cmpfloat(smallHeapRAMFraction, ==, 0.25); |
| g_assert_true(jsc_options_set_double("smallHeapRAMFraction", 0.50)); |
| g_assert_true(jsc_options_get_double("smallHeapRAMFraction", &smallHeapRAMFraction)); |
| g_assert_cmpfloat(smallHeapRAMFraction, ==, 0.50); |
| g_assert_true(jsc_options_set_double("smallHeapRAMFraction", 0.25)); |
| |
| GUniqueOutPtr<char> configFile; |
| g_assert_true(jsc_options_get_string("configFile", &configFile.outPtr())); |
| g_assert_null(configFile.get()); |
| g_assert_true(jsc_options_set_string("configFile", "/tmp/foo")); |
| g_assert_true(jsc_options_get_string("configFile", &configFile.outPtr())); |
| g_assert_cmpstr(configFile.get(), ==, "/tmp/foo"); |
| g_assert_true(jsc_options_set_string("configFile", nullptr)); |
| g_assert_true(jsc_options_get_string("configFile", &configFile.outPtr())); |
| g_assert_null(configFile.get()); |
| |
| GUniqueOutPtr<char> bytecodeRangeToJITCompile; |
| g_assert_true(jsc_options_get_range_string("bytecodeRangeToJITCompile", &bytecodeRangeToJITCompile.outPtr())); |
| g_assert_null(bytecodeRangeToJITCompile.get()); |
| g_assert_true(jsc_options_set_range_string("bytecodeRangeToJITCompile", "100")); |
| g_assert_true(jsc_options_get_range_string("bytecodeRangeToJITCompile", &bytecodeRangeToJITCompile.outPtr())); |
| g_assert_cmpstr(bytecodeRangeToJITCompile.get(), ==, "100"); |
| g_assert_true(jsc_options_set_range_string("bytecodeRangeToJITCompile", "100:200")); |
| g_assert_true(jsc_options_get_range_string("bytecodeRangeToJITCompile", &bytecodeRangeToJITCompile.outPtr())); |
| g_assert_cmpstr(bytecodeRangeToJITCompile.get(), ==, "100:200"); |
| g_assert_true(jsc_options_set_range_string("bytecodeRangeToJITCompile", "!100:200")); |
| g_assert_true(jsc_options_get_range_string("bytecodeRangeToJITCompile", &bytecodeRangeToJITCompile.outPtr())); |
| g_assert_cmpstr(bytecodeRangeToJITCompile.get(), ==, "!100:200"); |
| g_assert_false(jsc_options_set_range_string("bytecodeRangeToJITCompile", "200:100")); |
| g_assert_true(jsc_options_get_range_string("bytecodeRangeToJITCompile", &bytecodeRangeToJITCompile.outPtr())); |
| g_assert_cmpstr(bytecodeRangeToJITCompile.get(), ==, "!100:200"); |
| g_assert_true(jsc_options_set_range_string("bytecodeRangeToJITCompile", nullptr)); |
| g_assert_true(jsc_options_get_range_string("bytecodeRangeToJITCompile", &bytecodeRangeToJITCompile.outPtr())); |
| g_assert_null(bytecodeRangeToJITCompile.get()); |
| |
| guint logGC; |
| g_assert_true(jsc_options_get_uint("logGC", &logGC)); |
| g_assert_cmpuint(logGC, ==, 0); |
| g_assert_true(jsc_options_set_uint("logGC", 1)); |
| g_assert_true(jsc_options_get_uint("logGC", &logGC)); |
| g_assert_cmpuint(logGC, ==, 1); |
| g_assert_true(jsc_options_set_uint("logGC", 2)); |
| g_assert_true(jsc_options_get_uint("logGC", &logGC)); |
| g_assert_cmpuint(logGC, ==, 2); |
| g_assert_false(jsc_options_set_uint("logGC", 3)); |
| g_assert_true(jsc_options_get_uint("logGC", &logGC)); |
| g_assert_cmpuint(logGC, ==, 2); |
| g_assert_true(jsc_options_set_uint("logGC", 0)); |
| g_assert_true(jsc_options_get_uint("logGC", &logGC)); |
| g_assert_cmpuint(logGC, ==, 0); |
| |
| gboolean value = TRUE; |
| g_assert_false(jsc_options_get_boolean("InvalidOption", &value)); |
| g_assert_true(value); |
| g_assert_false(jsc_options_set_boolean("InvalidOption", TRUE)); |
| g_assert_false(jsc_options_get_boolean("InvalidOption", &value)); |
| g_assert_true(value); |
| |
| // Find a particular option. |
| bool found = false; |
| jsc_options_foreach([](const char* option, JSCOptionType type, const char* description, gpointer userData) -> gboolean { |
| if (!g_strcmp0(option, "useJIT")) { |
| *static_cast<bool*>(userData) = true; |
| return TRUE; |
| } |
| return FALSE; |
| }, &found); |
| g_assert_true(found); |
| |
| unsigned optionsCount = 0; |
| jsc_options_foreach([](const char* option, JSCOptionType type, const char* description, gpointer userData) -> gboolean { |
| (*static_cast<unsigned*>(userData))++; |
| return FALSE; |
| }, &optionsCount); |
| g_assert_cmpuint(optionsCount, >, 100); |
| g_assert_cmpuint(optionsCount, <, 500); |
| |
| optionsCount = 0; |
| jsc_options_foreach([](const char* option, JSCOptionType type, const char* description, gpointer userData) -> gboolean { |
| if (!g_strcmp0(option, "useJIT")) |
| g_assert_true(type == JSC_OPTION_BOOLEAN); |
| else if (!g_strcmp0(option, "thresholdForJITAfterWarmUp")) |
| g_assert_true(type == JSC_OPTION_INT); |
| else if (!g_strcmp0(option, "maxPerThreadStackUsage")) |
| g_assert_true(type == JSC_OPTION_UINT); |
| else if (!g_strcmp0(option, "webAssemblyPartialCompileLimit")) |
| g_assert_true(type == JSC_OPTION_SIZE); |
| else if (!g_strcmp0(option, "smallHeapRAMFraction")) |
| g_assert_true(type == JSC_OPTION_DOUBLE); |
| else if (!g_strcmp0(option, "configFile")) |
| g_assert_true(type == JSC_OPTION_STRING); |
| else if (!g_strcmp0(option, "bytecodeRangeToJITCompile")) |
| g_assert_true(type == JSC_OPTION_RANGE_STRING); |
| else |
| return FALSE; |
| |
| (*static_cast<unsigned*>(userData))++; |
| return FALSE; |
| }, &optionsCount); |
| g_assert_cmpuint(optionsCount, ==, 7); |
| |
| GOptionContext* context = g_option_context_new(nullptr); |
| g_option_context_add_group(context, jsc_options_get_option_group()); |
| static const char* argv[] = { |
| __FILE__, |
| "--jsc-useJIT=false", |
| "--jsc-thresholdForJITAfterWarmUp=2000", |
| "--jsc-maxPerThreadStackUsage=1024", |
| "--jsc-webAssemblyPartialCompileLimit=4000", |
| "--jsc-smallHeapRAMFraction=0.75", |
| "--jsc-configFile=/tmp/bar", |
| "--jsc-bytecodeRangeToJITCompile=100:300", |
| "--jsc-logGC=1", |
| nullptr |
| }; |
| GUniquePtr<char*> copy(g_strdupv(const_cast<char**>(argv))); |
| int argc = g_strv_length(copy.get()); |
| auto* copyPtr = copy.get(); |
| g_assert_true(g_option_context_parse(context, &argc, ©Ptr, nullptr)); |
| g_option_context_free(context); |
| |
| g_assert_true(jsc_options_get_boolean(JSC_OPTIONS_USE_JIT, &useJIT)); |
| g_assert_false(useJIT); |
| g_assert_true(jsc_options_get_int("thresholdForJITAfterWarmUp", &thresholdForJITAfterWarmUp)); |
| g_assert_cmpint(thresholdForJITAfterWarmUp, ==, 2000); |
| g_assert_true(jsc_options_get_uint("maxPerThreadStackUsage", &maxPerThreadStackUsage)); |
| g_assert_cmpuint(maxPerThreadStackUsage, ==, 1024); |
| g_assert_true(jsc_options_get_size("webAssemblyPartialCompileLimit", &webAssemblyPartialCompileLimit)); |
| g_assert_cmpuint(webAssemblyPartialCompileLimit, ==, 4000); |
| g_assert_true(jsc_options_get_double("smallHeapRAMFraction", &smallHeapRAMFraction)); |
| g_assert_cmpfloat(smallHeapRAMFraction, ==, 0.75); |
| g_assert_true(jsc_options_get_string("configFile", &configFile.outPtr())); |
| g_assert_cmpstr(configFile.get(), ==, "/tmp/bar"); |
| g_assert_true(jsc_options_get_range_string("bytecodeRangeToJITCompile", &bytecodeRangeToJITCompile.outPtr())); |
| g_assert_cmpstr(bytecodeRangeToJITCompile.get(), ==, "100:300"); |
| g_assert_true(jsc_options_get_uint("logGC", &logGC)); |
| g_assert_cmpuint(logGC, ==, 1); |
| |
| // Restore options their default values. |
| g_assert_true(jsc_options_set_boolean(JSC_OPTIONS_USE_JIT, TRUE)); |
| g_assert_true(jsc_options_set_int("thresholdForJITAfterWarmUp", 500)); |
| g_assert_true(jsc_options_set_uint("maxPerThreadStackUsage", 4194304)); |
| g_assert_true(jsc_options_set_size("webAssemblyPartialCompileLimit", 5000)); |
| g_assert_true(jsc_options_set_double("smallHeapRAMFraction", 0.25)); |
| g_assert_true(jsc_options_set_string("configFile", nullptr)); |
| g_assert_true(jsc_options_set_range_string("bytecodeRangeToJITCompile", nullptr)); |
| g_assert_true(jsc_options_set_uint("logGC", 0)); |
| |
| context = g_option_context_new(nullptr); |
| g_option_context_add_group(context, jsc_options_get_option_group()); |
| static const char* argv2[] = { __FILE__, "--jsc-InvalidOption=true", nullptr }; |
| copy.reset(g_strdupv(const_cast<char**>(argv2))); |
| argc = g_strv_length(copy.get()); |
| copyPtr = copy.get(); |
| g_assert_false(g_option_context_parse(context, &argc, ©Ptr, nullptr)); |
| g_option_context_free(context); |
| |
| context = g_option_context_new(nullptr); |
| g_option_context_add_group(context, jsc_options_get_option_group()); |
| static const char* argv3[] = { __FILE__, "--jsc-useJIT=nein", nullptr }; |
| copy.reset(g_strdupv(const_cast<char**>(argv3))); |
| argc = g_strv_length(copy.get()); |
| copyPtr = copy.get(); |
| g_assert_false(g_option_context_parse(context, &argc, ©Ptr, nullptr)); |
| g_option_context_free(context); |
| g_assert_true(jsc_options_get_boolean(JSC_OPTIONS_USE_JIT, &useJIT)); |
| g_assert_true(useJIT); |
| } |
| |
| #ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC |
| static void testsJSCAutocleanups() |
| { |
| LeakChecker checker; |
| g_autoptr(JSCVirtualMachine) vm = jsc_virtual_machine_new(); |
| checker.watch(vm); |
| g_assert_true(JSC_IS_VIRTUAL_MACHINE(vm)); |
| |
| g_autoptr(JSCContext) context = jsc_context_new_with_virtual_machine(vm); |
| checker.watch(context); |
| g_assert_true(JSC_IS_CONTEXT(context)); |
| |
| g_autoptr(JSCValue) value = jsc_context_evaluate(context, "v = 25", -1); |
| checker.watch(value); |
| g_assert_true(JSC_IS_VALUE(value)); |
| g_assert_true(jsc_value_is_number(value)); |
| g_assert_cmpint(jsc_value_to_int32(value), ==, 25); |
| |
| g_autoptr(JSCException) exception = jsc_exception_new(context, "API error"); |
| checker.watch(exception); |
| g_assert_true(JSC_IS_EXCEPTION(exception)); |
| jsc_context_throw_exception(context, exception); |
| } |
| #endif |
| |
| int main(int argc, char** argv) |
| { |
| g_test_init(&argc, &argv, nullptr); |
| |
| // options should always be the first test, since changing options |
| // is not allowed after the first VM instance is created. |
| g_test_add_func("/jsc/options", testsJSCOptions); |
| g_test_add_func("/jsc/basic", testJSCBasic); |
| 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); |
| g_test_add_func("/jsc/prototypes", testJSCPrototypes); |
| g_test_add_func("/jsc/exceptions", testJSCExceptions); |
| g_test_add_func("/jsc/promises", testJSCPromises); |
| g_test_add_func("/jsc/garbage-collector", testJSCGarbageCollector); |
| g_test_add_func("/jsc/weak-value", testJSCWeakValue); |
| g_test_add_func("/jsc/vm", testsJSCVirtualMachine); |
| #ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC |
| g_test_add_func("/jsc/autocleanups", testsJSCAutocleanups); |
| #endif |
| |
| return g_test_run(); |
| } |