| /* |
| * 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")); |
| checker.watch(result.get()); |
| g_assert_true(result.get() == value1.get()); |
| |
| result = adoptGRef(jsc_context_evaluate(context2.get(), "value1")); |
| 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")); |
| 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")); |
| 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")); |
| 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")); |
| 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")); |
| 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")); |
| 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")); |
| 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")); |
| 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")); |
| checker.watch(result2.get()); |
| g_assert_true(result2.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); |
| |
| 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 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 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)")); |
| 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()); |
| } |
| |
| { |
| 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; }")); |
| checker.watch(function.get()); |
| |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "foo(200)")); |
| 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()); |
| } |
| |
| { |
| 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")); |
| 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")); |
| checker.watch(result.get()); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "result")); |
| 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")); |
| 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" |
| "}")); |
| 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])")); |
| 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); |
| } |
| } |
| |
| 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;")); |
| 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")); |
| |
| 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); |
| |
| 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); |
| |
| GRefPtr<JSCValue> constructor = adoptGRef(jsc_context_evaluate(context.get(), "Foo")); |
| 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")); |
| 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)")); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 1000); |
| } |
| |
| { |
| 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")); |
| |
| GRefPtr<JSCValue> property = adoptGRef(jsc_value_new_number(context.get(), 25)); |
| checker.watch(property.get()); |
| |
| jsc_value_object_define_property_data(object.get(), "val", static_cast<JSCValuePropertyFlags>(0), property.get()); |
| jsc_context_set_value(context.get(), "f", object.get()); |
| |
| GRefPtr<JSCValue> value = adoptGRef(jsc_context_evaluate(context.get(), "f.val;")); |
| 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;")); |
| 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');")); |
| 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;")); |
| checker.watch(value.get()); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "f.val;")); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 25); |
| |
| 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()); |
| jsc_value_object_define_property_data(object.get(), "val2", static_cast<JSCValuePropertyFlags>(JSC_VALUE_PROPERTY_ENUMERABLE | JSC_VALUE_PROPERTY_WRITABLE), property.get()); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "f.val2;")); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 32); |
| |
| value = adoptGRef(jsc_context_evaluate(context.get(), "'use strict'; f.val2 = 45;")); |
| checker.watch(value.get()); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "f.val2;")); |
| 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');")); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_boolean(value.get())); |
| g_assert_true(jsc_value_to_boolean(value.get()) == TRUE); |
| |
| property = adoptGRef(jsc_value_new_number(context.get(), 125)); |
| checker.watch(property.get()); |
| jsc_value_object_define_property_data(object.get(), "val3", static_cast<JSCValuePropertyFlags>(JSC_VALUE_PROPERTY_CONFIGURABLE | JSC_VALUE_PROPERTY_WRITABLE), property.get()); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "f.val3;")); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 125); |
| |
| 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()); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "f.val3;")); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 150); |
| |
| value = adoptGRef(jsc_context_evaluate(context.get(), "delete f.val3;")); |
| checker.watch(value.get()); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "f.val3;")); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_undefined(value.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_value_object_define_property_data(object.get(), "foo", static_cast<JSCValuePropertyFlags>(0), function.get()); |
| |
| 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; |
| Foo* sibling; |
| }; |
| |
| static Foo* fooCreate() |
| { |
| return g_new0(Foo, 1); |
| } |
| |
| static Foo* fooCreateWithFoo(int value) |
| { |
| auto* f = fooCreate(); |
| f->foo = value; |
| return f; |
| } |
| |
| static void fooFree(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; |
| } |
| |
| 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 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, 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();")); |
| 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;")); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| |
| jsc_class_add_method(jscClass, "getFoo", G_CALLBACK(getFoo), nullptr, nullptr, G_TYPE_INT, 0, G_TYPE_NONE); |
| 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); |
| |
| GRefPtr<JSCValue> value2 = adoptGRef(jsc_context_evaluate(context.get(), "f.getFoo()")); |
| checker.watch(value2.get()); |
| g_assert_true(value.get() == value2.get()); |
| |
| jsc_class_add_method(jscClass, "setFoo", G_CALLBACK(setFoo), nullptr, nullptr, G_TYPE_NONE, 1, G_TYPE_INT); |
| 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()")); |
| 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)")); |
| 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); |
| |
| 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()); |
| |
| GRefPtr<JSCValue> foo2 = adoptGRef(jsc_context_evaluate(context.get(), "f2 = new Foo.CreateWithFoo(42);")); |
| 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;")); |
| 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;")); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_true(jsc_value_to_boolean(result.get())); |
| |
| jsc_class_add_property(jscClass, "foo", G_TYPE_INT, G_CALLBACK(getFoo), G_CALLBACK(setFoo), nullptr, nullptr); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "f2.foo")); |
| 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")); |
| checker.watch(result.get()); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "f2.foo")); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 52); |
| |
| JSCClass* otherClass = jsc_context_register_class(context.get(), "Baz", 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, 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")); |
| 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())); |
| GRefPtr<JSCValue> f2 = adoptGRef(jsc_context_get_value(context.get(), "f2")); |
| checker.watch(f2.get()); |
| g_assert_true(jsc_value_is_object(f2.get())); |
| |
| GRefPtr<JSCValue> value = adoptGRef(jsc_context_evaluate(context.get(), "f2.sibling")); |
| 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")); |
| 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, 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))); |
| |
| jsc_context_set_value(context.get(), "f", foo.get()); |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "f instanceof Foo;")); |
| 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")); |
| 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")); |
| 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, 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))); |
| |
| jsc_context_set_value(context.get(), "bz", baz.get()); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "bz instanceof Baz;")); |
| 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;")); |
| 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;")); |
| 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")); |
| 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, 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))); |
| |
| 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;")); |
| 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()); |
| jsc_value_object_set_property(foo.get(), "n", property.get()); |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f1.n")); |
| 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, 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();")); |
| 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> 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)")); |
| 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({})")); |
| 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, 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();")); |
| 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, 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();")); |
| 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();")); |
| g_assert_true(jsc_value_is_object(foo.get())); |
| g_assert_true(jsc_value_object_is_instance_of(foo.get(), "wk.Foo")); |
| GRefPtr<JSCValue> result = adoptGRef(jsc_context_evaluate(context.get(), "f instanceof wk.Foo;")); |
| 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);")); |
| 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);")); |
| 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;")); |
| 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;")); |
| 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")); |
| 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> foo3 = adoptGRef(jsc_value_constructor_call(constructor2.get(), G_TYPE_INT, 62, G_TYPE_NONE)); |
| 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;")); |
| 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;")); |
| 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")); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_number(result.get())); |
| g_assert_cmpint(jsc_value_to_int32(result.get()), ==, 62); |
| } |
| } |
| |
| typedef struct { |
| Foo parent; |
| int bar; |
| } Bar; |
| |
| static Bar* barCreate() |
| { |
| return g_new0(Bar, 1); |
| } |
| |
| static void barFree(Bar* bar) |
| { |
| g_free(bar); |
| } |
| |
| static void setBar(Bar* bar, int value) |
| { |
| bar->bar = value; |
| } |
| |
| static int getBar(Bar* bar) |
| { |
| return bar->bar; |
| } |
| |
| 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, 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, 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();")); |
| checker.watch(result.get()); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f.__proto__ == Foo.prototype")); |
| 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")); |
| 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")); |
| 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")); |
| 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")); |
| checker.watch(result.get()); |
| g_assert_true(jsc_value_is_boolean(result.get())); |
| g_assert_false(jsc_value_to_boolean(result.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")); |
| 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")); |
| 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.bar = 25; b.foo = 42;")); |
| checker.watch(result.get()); |
| |
| GRefPtr<JSCValue> value = adoptGRef(jsc_context_evaluate(context.get(), "b.bar")); |
| 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")); |
| 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)")); |
| checker.watch(result.get()); |
| value = adoptGRef(jsc_context_evaluate(context.get(), "b.foo")); |
| checker.watch(value.get()); |
| g_assert_true(jsc_value_is_number(value.get())); |
| g_assert_cmpint(jsc_value_to_int32(value.get()), ==, 84); |
| } |
| } |
| |
| static void createError() |
| { |
| jsc_context_throw(jsc_context_get_current(), "API exception"); |
| } |
| |
| 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")); |
| 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_message(exception), ==, "Can't find variable: foo"); |
| g_assert_cmpuint(jsc_exception_get_line_number(exception), ==, 1); |
| g_assert_false(jsc_exception_get_source_uri(exception)); |
| } |
| |
| { |
| 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;")); |
| 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); |
| } |
| |
| { |
| 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", "file:///foo/script.js")); |
| 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"); |
| } |
| |
| { |
| 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'; }")); |
| 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';")); |
| 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_message(exception), ==, "API exception"); |
| g_assert_cmpuint(jsc_exception_get_line_number(exception), ==, 1); |
| g_assert_false(jsc_exception_get_source_uri(exception)); |
| } |
| |
| { |
| 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")); |
| 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_message(test.exception.get()), ==, "Can't find variable: foo"); |
| g_assert_cmpuint(jsc_exception_get_line_number(test.exception.get()), ==, 1); |
| g_assert_false(jsc_exception_get_source_uri(test.exception.get())); |
| |
| 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")); |
| 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); |
| } |
| } |
| |
| 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")); |
| 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; });")); |
| 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, 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; });")); |
| 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; });")); |
| 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")); |
| 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")); |
| 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")); |
| 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")); |
| checker.watch(result.get()); |
| g_assert_true(object.get() == result.get()); |
| foo = adoptGRef(jsc_context_evaluate(context.get(), "f.foo")); |
| 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, 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();")); |
| 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")); |
| checker.watch(result.get()); |
| g_assert_true(object.get() == result.get()); |
| |
| result = adoptGRef(jsc_context_evaluate(context.get(), "f = undefined")); |
| 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 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, 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();")); |
| 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(); |
| thread->detach(); |
| |
| 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" |
| )); |
| 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(); |
| thread->detach(); |
| } |
| |
| g_assert_true(ok); |
| } |
| } |
| |
| #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"); |
| 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); |
| |
| g_test_add_func("/jsc/basic", testJSCBasic); |
| g_test_add_func("/jsc/types", testJSCTypes); |
| 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/vm", testsJSCVirtualMachine); |
| #ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC |
| g_test_add_func("/jsc/autocleanups", testsJSCAutocleanups); |
| #endif |
| |
| return g_test_run(); |
| } |