diff -Nru gjs-1.53.3/aclocal.m4 gjs-1.54.3/aclocal.m4 --- gjs-1.53.3/aclocal.m4 2018-06-18 22:55:23.000000000 +0000 +++ gjs-1.54.3/aclocal.m4 2018-11-12 17:17:12.000000000 +0000 @@ -1235,7 +1235,7 @@ m4_include([m4/ax_compiler_flags_gir.m4]) m4_include([m4/ax_compiler_flags_ldflags.m4]) m4_include([m4/ax_cxx_compile_stdcxx.m4]) -m4_include([m4/ax_cxx_compile_stdcxx_11.m4]) +m4_include([m4/ax_cxx_compile_stdcxx_14.m4]) m4_include([m4/ax_is_release.m4]) m4_include([m4/ax_pkg_check_modules.m4]) m4_include([m4/ax_require_defined.m4]) diff -Nru gjs-1.53.3/config.h.in gjs-1.54.3/config.h.in --- gjs-1.53.3/config.h.in 2018-06-18 22:55:25.000000000 +0000 +++ gjs-1.54.3/config.h.in 2018-11-12 17:17:14.000000000 +0000 @@ -15,8 +15,8 @@ /* The gjs version as an integer */ #undef GJS_VERSION -/* define if the compiler supports basic C++11 syntax */ -#undef HAVE_CXX11 +/* define if the compiler supports basic C++14 syntax */ +#undef HAVE_CXX14 /* Define to 1 if SpiderMonkey was compiled with --enable-debug */ #undef HAVE_DEBUG_SPIDERMONKEY diff -Nru gjs-1.53.3/configure gjs-1.54.3/configure --- gjs-1.53.3/configure 2018-06-18 22:55:24.000000000 +0000 +++ gjs-1.54.3/configure 2018-11-12 17:17:13.000000000 +0000 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for gjs 1.53.3. +# Generated by GNU Autoconf 2.69 for gjs 1.54.3. # # Report bugs to . # @@ -591,8 +591,8 @@ # Identity of this package. PACKAGE_NAME='gjs' PACKAGE_TARNAME='gjs' -PACKAGE_VERSION='1.53.3' -PACKAGE_STRING='gjs 1.53.3' +PACKAGE_VERSION='1.54.3' +PACKAGE_STRING='gjs 1.54.3' PACKAGE_BUGREPORT='http://bugzilla.gnome.org/enter_bug.cgi?product=gjs' PACKAGE_URL='https://wiki.gnome.org/Projects/Gjs' @@ -668,8 +668,6 @@ ENABLE_CAIRO_TRUE GJS_CAIRO_LIBS GJS_CAIRO_CFLAGS -GJSTESTS_LIBS -GJSTESTS_CFLAGS GJS_CONSOLE_LIBS GJS_CONSOLE_CFLAGS GJS_GDBUS_LIBS @@ -749,7 +747,7 @@ build_cpu build LIBTOOL -HAVE_CXX11 +HAVE_CXX14 EGREP GREP CXXCPP @@ -871,12 +869,14 @@ with_cairo with_gtk enable_profiler +enable_readline enable_installed_tests enable_dtrace enable_systemtap enable_Bsymbolic enable_asan enable_ubsan +enable_cpp_rtti with_xvfb_tests with_dbus_tests ' @@ -902,8 +902,6 @@ GJS_GDBUS_LIBS GJS_CONSOLE_CFLAGS GJS_CONSOLE_LIBS -GJSTESTS_CFLAGS -GJSTESTS_LIBS GJS_CAIRO_CFLAGS GJS_CAIRO_LIBS GJS_CAIRO_XLIB_CFLAGS @@ -1451,7 +1449,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures gjs 1.53.3 to adapt to many kinds of systems. +\`configure' configures gjs 1.54.3 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1521,7 +1519,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of gjs 1.53.3:";; + short | recursive ) echo "Configuration of gjs 1.54.3:";; esac cat <<\_ACEOF @@ -1556,6 +1554,7 @@ --enable-valgrind-sgcheck Whether to use sgcheck during the Valgrind tests --disable-profiler Don't build profiler + --disable-readline Don't build readline-related code [default=no] --enable-installed-tests Install test programs [default: no] --enable-dtrace Include dtrace trace support [default: no] @@ -1565,6 +1564,8 @@ --enable-asan Build with address sanitizer support [default: no] --enable-ubsan Build with undefined behavior sanitizer support [default: no] + --enable-cpp-rtti needs to match SpiderMonkey's config option + [default=off] Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] @@ -1614,10 +1615,6 @@ C compiler flags for GJS_CONSOLE, overriding pkg-config GJS_CONSOLE_LIBS linker flags for GJS_CONSOLE, overriding pkg-config - GJSTESTS_CFLAGS - C compiler flags for GJSTESTS, overriding pkg-config - GJSTESTS_LIBS - linker flags for GJSTESTS, overriding pkg-config GJS_CAIRO_CFLAGS C compiler flags for GJS_CAIRO, overriding pkg-config GJS_CAIRO_LIBS @@ -1700,7 +1697,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -gjs configure 1.53.3 +gjs configure 1.54.3 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2251,7 +2248,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by gjs $as_me 1.53.3, which was +It was created by gjs $as_me 1.54.3, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -3114,7 +3111,7 @@ # Define the identity of the package. PACKAGE='gjs' - VERSION='1.53.3' + VERSION='1.54.3' cat >>confdefs.h <<_ACEOF @@ -3341,10 +3338,10 @@ -GJS_VERSION=15303 +GJS_VERSION=15403 -$as_echo "#define GJS_VERSION (1 * 100 + 53) * 100 + 3" >>confdefs.h +$as_echo "#define GJS_VERSION (1 * 100 + 54) * 100 + 3" >>confdefs.h GETTEXT_PACKAGE=gjs @@ -5170,16 +5167,16 @@ fi - ax_cxx_compile_alternatives="11 0x" ax_cxx_compile_cxx11_required=true + ax_cxx_compile_alternatives="14 1y" ax_cxx_compile_cxx14_required=true ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu ac_success=no - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features by default" >&5 -$as_echo_n "checking whether $CXX supports C++11 features by default... " >&6; } -if ${ax_cv_cxx_compile_cxx11+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++14 features by default" >&5 +$as_echo_n "checking whether $CXX supports C++14 features by default... " >&6; } +if ${ax_cv_cxx_compile_cxx14+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -5470,26 +5467,146 @@ + +// If the compiler admits that it is not ready for C++14, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201402L + +#error "This is not a C++14 compiler" + +#else + +namespace cxx14 +{ + + namespace test_polymorphic_lambdas + { + + int + test() + { + const auto lambda = [](auto&&... args){ + const auto istiny = [](auto x){ + return (sizeof(x) == 1UL) ? 1 : 0; + }; + const int aretiny[] = { istiny(args)... }; + return aretiny[0]; + }; + return lambda(1, 1L, 1.0f, '1'); + } + + } + + namespace test_binary_literals + { + + constexpr auto ivii = 0b0000000000101010; + static_assert(ivii == 42, "wrong value"); + + } + + namespace test_generalized_constexpr + { + + template < typename CharT > + constexpr unsigned long + strlen_c(const CharT *const s) noexcept + { + auto length = 0UL; + for (auto p = s; *p; ++p) + ++length; + return length; + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("x") == 1UL, ""); + static_assert(strlen_c("test") == 4UL, ""); + static_assert(strlen_c("another\0test") == 7UL, ""); + + } + + namespace test_lambda_init_capture + { + + int + test() + { + auto x = 0; + const auto lambda1 = [a = x](int b){ return a + b; }; + const auto lambda2 = [a = lambda1(x)](){ return a; }; + return lambda2(); + } + + } + + namespace test_digit_separators + { + + constexpr auto ten_million = 100'000'000; + static_assert(ten_million == 100000000, ""); + + } + + namespace test_return_type_deduction + { + + auto f(int& x) { return x; } + decltype(auto) g(int& x) { return x; } + + template < typename T1, typename T2 > + struct is_same + { + static constexpr auto value = false; + }; + + template < typename T > + struct is_same + { + static constexpr auto value = true; + }; + + int + test() + { + auto x = 0; + static_assert(is_same::value, ""); + static_assert(is_same::value, ""); + return x; + } + + } + +} // namespace cxx14 + +#endif // __cplusplus >= 201402L + + + _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : - ax_cv_cxx_compile_cxx11=yes + ax_cv_cxx_compile_cxx14=yes else - ax_cv_cxx_compile_cxx11=no + ax_cv_cxx_compile_cxx14=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_cxx_compile_cxx11" >&5 -$as_echo "$ax_cv_cxx_compile_cxx11" >&6; } - if test x$ax_cv_cxx_compile_cxx11 = xyes; then +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_cxx_compile_cxx14" >&5 +$as_echo "$ax_cv_cxx_compile_cxx14" >&6; } + if test x$ax_cv_cxx_compile_cxx14 = xyes; then ac_success=yes fi if test x$ac_success = xno; then for alternative in ${ax_cxx_compile_alternatives}; do switch="-std=gnu++${alternative}" - cachevar=`$as_echo "ax_cv_cxx_compile_cxx11_$switch" | $as_tr_sh` - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features with $switch" >&5 -$as_echo_n "checking whether $CXX supports C++11 features with $switch... " >&6; } + cachevar=`$as_echo "ax_cv_cxx_compile_cxx14_$switch" | $as_tr_sh` + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++14 features with $switch" >&5 +$as_echo_n "checking whether $CXX supports C++14 features with $switch... " >&6; } if eval \${$cachevar+:} false; then : $as_echo_n "(cached) " >&6 else @@ -5783,6 +5900,126 @@ + +// If the compiler admits that it is not ready for C++14, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201402L + +#error "This is not a C++14 compiler" + +#else + +namespace cxx14 +{ + + namespace test_polymorphic_lambdas + { + + int + test() + { + const auto lambda = [](auto&&... args){ + const auto istiny = [](auto x){ + return (sizeof(x) == 1UL) ? 1 : 0; + }; + const int aretiny[] = { istiny(args)... }; + return aretiny[0]; + }; + return lambda(1, 1L, 1.0f, '1'); + } + + } + + namespace test_binary_literals + { + + constexpr auto ivii = 0b0000000000101010; + static_assert(ivii == 42, "wrong value"); + + } + + namespace test_generalized_constexpr + { + + template < typename CharT > + constexpr unsigned long + strlen_c(const CharT *const s) noexcept + { + auto length = 0UL; + for (auto p = s; *p; ++p) + ++length; + return length; + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("x") == 1UL, ""); + static_assert(strlen_c("test") == 4UL, ""); + static_assert(strlen_c("another\0test") == 7UL, ""); + + } + + namespace test_lambda_init_capture + { + + int + test() + { + auto x = 0; + const auto lambda1 = [a = x](int b){ return a + b; }; + const auto lambda2 = [a = lambda1(x)](){ return a; }; + return lambda2(); + } + + } + + namespace test_digit_separators + { + + constexpr auto ten_million = 100'000'000; + static_assert(ten_million == 100000000, ""); + + } + + namespace test_return_type_deduction + { + + auto f(int& x) { return x; } + decltype(auto) g(int& x) { return x; } + + template < typename T1, typename T2 > + struct is_same + { + static constexpr auto value = false; + }; + + template < typename T > + struct is_same + { + static constexpr auto value = true; + }; + + int + test() + { + auto x = 0; + static_assert(is_same::value, ""); + static_assert(is_same::value, ""); + return x; + } + + } + +} // namespace cxx14 + +#endif // __cplusplus >= 201402L + + + _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : eval $cachevar=yes @@ -5809,9 +6046,9 @@ if test x$ac_success = xno; then for alternative in ${ax_cxx_compile_alternatives}; do for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do - cachevar=`$as_echo "ax_cv_cxx_compile_cxx11_$switch" | $as_tr_sh` - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features with $switch" >&5 -$as_echo_n "checking whether $CXX supports C++11 features with $switch... " >&6; } + cachevar=`$as_echo "ax_cv_cxx_compile_cxx14_$switch" | $as_tr_sh` + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++14 features with $switch" >&5 +$as_echo_n "checking whether $CXX supports C++14 features with $switch... " >&6; } if eval \${$cachevar+:} false; then : $as_echo_n "(cached) " >&6 else @@ -6105,6 +6342,126 @@ + +// If the compiler admits that it is not ready for C++14, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201402L + +#error "This is not a C++14 compiler" + +#else + +namespace cxx14 +{ + + namespace test_polymorphic_lambdas + { + + int + test() + { + const auto lambda = [](auto&&... args){ + const auto istiny = [](auto x){ + return (sizeof(x) == 1UL) ? 1 : 0; + }; + const int aretiny[] = { istiny(args)... }; + return aretiny[0]; + }; + return lambda(1, 1L, 1.0f, '1'); + } + + } + + namespace test_binary_literals + { + + constexpr auto ivii = 0b0000000000101010; + static_assert(ivii == 42, "wrong value"); + + } + + namespace test_generalized_constexpr + { + + template < typename CharT > + constexpr unsigned long + strlen_c(const CharT *const s) noexcept + { + auto length = 0UL; + for (auto p = s; *p; ++p) + ++length; + return length; + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("x") == 1UL, ""); + static_assert(strlen_c("test") == 4UL, ""); + static_assert(strlen_c("another\0test") == 7UL, ""); + + } + + namespace test_lambda_init_capture + { + + int + test() + { + auto x = 0; + const auto lambda1 = [a = x](int b){ return a + b; }; + const auto lambda2 = [a = lambda1(x)](){ return a; }; + return lambda2(); + } + + } + + namespace test_digit_separators + { + + constexpr auto ten_million = 100'000'000; + static_assert(ten_million == 100000000, ""); + + } + + namespace test_return_type_deduction + { + + auto f(int& x) { return x; } + decltype(auto) g(int& x) { return x; } + + template < typename T1, typename T2 > + struct is_same + { + static constexpr auto value = false; + }; + + template < typename T > + struct is_same + { + static constexpr auto value = true; + }; + + int + test() + { + auto x = 0; + static_assert(is_same::value, ""); + static_assert(is_same::value, ""); + return x; + } + + } + +} // namespace cxx14 + +#endif // __cplusplus >= 201402L + + + _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : eval $cachevar=yes @@ -6137,19 +6494,19 @@ ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - if test x$ax_cxx_compile_cxx11_required = xtrue; then + if test x$ax_cxx_compile_cxx14_required = xtrue; then if test x$ac_success = xno; then - as_fn_error $? "*** A compiler with support for C++11 language features is required." "$LINENO" 5 + as_fn_error $? "*** A compiler with support for C++14 language features is required." "$LINENO" 5 fi fi if test x$ac_success = xno; then - HAVE_CXX11=0 - { $as_echo "$as_me:${as_lineno-$LINENO}: No compiler with C++11 support was found" >&5 -$as_echo "$as_me: No compiler with C++11 support was found" >&6;} + HAVE_CXX14=0 + { $as_echo "$as_me:${as_lineno-$LINENO}: No compiler with C++14 support was found" >&5 +$as_echo "$as_me: No compiler with C++14 support was found" >&6;} else - HAVE_CXX11=1 + HAVE_CXX14=1 -$as_echo "#define HAVE_CXX11 1" >>confdefs.h +$as_echo "#define HAVE_CXX14 1" >>confdefs.h fi @@ -21017,14 +21374,12 @@ -GOBJECT_REQUIREMENT="gobject-2.0 >= 2.50.0" +GOBJECT_REQUIREMENT="gobject-2.0 >= 2.54.0" gjs_base_packages="$GOBJECT_REQUIREMENT gio-2.0" -common_packages="gthread-2.0 gio-2.0 >= 2.50.0 mozjs-52" +common_packages="gthread-2.0 gio-2.0 >= 2.54.0 mozjs-60" gjs_packages="gobject-introspection-1.0 libffi $common_packages" gjs_cairo_packages="cairo cairo-gobject $common_packages" gjs_gtk_packages="gtk+-3.0 >= 3.20" -# gjs-tests links against everything -gjstests_packages="gio-unix-2.0 $gjs_packages" @@ -21312,97 +21667,6 @@ fi -pkg_failed=no -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $gjstests_packages" >&5 -$as_echo_n "checking for $gjstests_packages... " >&6; } - -if test -n "$GJSTESTS_CFLAGS"; then - pkg_cv_GJSTESTS_CFLAGS="$GJSTESTS_CFLAGS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$gjstests_packages\""; } >&5 - ($PKG_CONFIG --exists --print-errors "$gjstests_packages") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - pkg_cv_GJSTESTS_CFLAGS=`$PKG_CONFIG --cflags "$gjstests_packages" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes -else - pkg_failed=yes -fi - else - pkg_failed=untried -fi -if test -n "$GJSTESTS_LIBS"; then - pkg_cv_GJSTESTS_LIBS="$GJSTESTS_LIBS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ - { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$gjstests_packages\""; } >&5 - ($PKG_CONFIG --exists --print-errors "$gjstests_packages") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then - pkg_cv_GJSTESTS_LIBS=`$PKG_CONFIG --libs "$gjstests_packages" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes -else - pkg_failed=yes -fi - else - pkg_failed=untried -fi - - - -if test $pkg_failed = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - -if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then - _pkg_short_errors_supported=yes -else - _pkg_short_errors_supported=no -fi - if test $_pkg_short_errors_supported = yes; then - GJSTESTS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$gjstests_packages" 2>&1` - else - GJSTESTS_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$gjstests_packages" 2>&1` - fi - # Put the nasty error message in config.log where it belongs - echo "$GJSTESTS_PKG_ERRORS" >&5 - - as_fn_error $? "Package requirements ($gjstests_packages) were not met: - -$GJSTESTS_PKG_ERRORS - -Consider adjusting the PKG_CONFIG_PATH environment variable if you -installed software in a non-standard prefix. - -Alternatively, you may set the environment variables GJSTESTS_CFLAGS -and GJSTESTS_LIBS to avoid the need to call pkg-config. -See the pkg-config man page for more details." "$LINENO" 5 -elif test $pkg_failed = untried; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it -is in your PATH or set the PKG_CONFIG environment variable to the full -path to pkg-config. - -Alternatively, you may set the environment variables GJSTESTS_CFLAGS -and GJSTESTS_LIBS to avoid the need to call pkg-config. -See the pkg-config man page for more details. - -To get pkg-config, see . -See \`config.log' for more details" "$LINENO" 5; } -else - GJSTESTS_CFLAGS=$pkg_cv_GJSTESTS_CFLAGS - GJSTESTS_LIBS=$pkg_cv_GJSTESTS_LIBS - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - -fi - CPPFLAGS_save="$CPPFLAGS" CPPFLAGS="$GJS_CFLAGS" LIBS_save="$LIBS" @@ -21415,7 +21679,7 @@ #include #ifdef JS_DEBUG -#error debug yes, if we didn't already error out due to DEBUG not being defined +#error debug yes, if we did not already error out due to DEBUG not being defined #endif _ACEOF @@ -21806,6 +22070,7 @@ if test x$enable_profiler != xno; then : + # Requires timer_settime() - only on Linux @@ -21886,8 +22151,37 @@ LIBS=$gl_saved_libs - if test x$ac_cv_func_timer_settime = xno; then : + # Requires SIGEV_THREAD_ID - not in some stdlibs + have_sigev_thread_id=no + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Linux SIGEV_THREAD_ID" >&5 +$as_echo_n "checking for Linux SIGEV_THREAD_ID... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include +int +main () +{ +return SIGEV_THREAD_ID; + ; + return 0; +} + +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + + have_sigev_thread_id=yes + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if test x$ac_cv_func_timer_settime = xno -o x$have_sigev_thread_id = xno; then : as_fn_error $? "The profiler is currently only supported on Linux. +The standard library must support timer_settime() and SIGEV_THREAD_ID. Configure with --disable-profiler to skip it on other platforms." "$LINENO" 5 fi @@ -21904,6 +22198,13 @@ fi +# Optional redline dep (enabled by default) +# Check whether --enable-readline was given. +if test "${enable_readline+set}" = set; then : + enableval=$enable_readline; +fi + + if test -n "$GI_DATADIR"; then pkg_cv_GI_DATADIR="$GI_DATADIR" @@ -21931,19 +22232,22 @@ # readline LIBS_no_readline=$LIBS -# On some systems we need to link readline to a termcap compatible -# library. -gjs_cv_lib_readline=no -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link readline libs" >&5 +if test x$enable_readline != xno; then : + + + # On some systems we need to link readline to a termcap compatible + # library. + gjs_cv_lib_readline=no + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link readline libs" >&5 $as_echo_n "checking how to link readline libs... " >&6; } -for gjs_libtermcap in "" ncursesw ncurses curses termcap; do - if test -z "$gjs_libtermcap"; then - READLINE_LIBS="-lreadline" - else - READLINE_LIBS="-lreadline -l$gjs_libtermcap" - fi - LIBS="$READLINE_LIBS $LIBS_no_readline" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext + for gjs_libtermcap in "" ncursesw ncurses curses termcap; do + if test -z "$gjs_libtermcap"; then + READLINE_LIBS="-lreadline" + else + READLINE_LIBS="-lreadline -l$gjs_libtermcap" + fi + LIBS="$READLINE_LIBS $LIBS_no_readline" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. @@ -21966,13 +22270,19 @@ fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext - if test $gjs_cv_lib_readline = yes; then - break - fi -done + if test $gjs_cv_lib_readline = yes; then + break + fi + done + +else + gjs_cv_lib_readline=no +fi + if test $gjs_cv_lib_readline = no; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5 -$as_echo "none" >&6; } + if test x$enable_readline != xno; then : + as_fn_error $? "Readline requested but not found. Configure with --disable-readline." "$LINENO" 5 +fi READLINE_LIBS="" ac_cv_header_readline_readline_h=no else @@ -22626,6 +22936,165 @@ done +# Check whether --enable-cpp-rtti was given. +if test "${enable_cpp_rtti+set}" = set; then : + enableval=$enable_cpp_rtti; +fi + +if test "x$enable_cpp_rtti" != "xyes"; then : + + + + +for flag in -fno-rtti; do + as_CACHEVAR=`$as_echo "ax_cv_check_cxxflags__$flag" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts $flag" >&5 +$as_echo_n "checking whether C++ compiler accepts $flag... " >&6; } +if eval \${$as_CACHEVAR+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CXXFLAGS + CXXFLAGS="$CXXFLAGS $flag" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + eval "$as_CACHEVAR=yes" +else + eval "$as_CACHEVAR=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CXXFLAGS=$ax_check_save_flags +fi +eval ac_res=\$$as_CACHEVAR + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : + +if ${CXXFLAGS+:} false; then : + + case " $CXXFLAGS " in #( + *" $flag "*) : + { { $as_echo "$as_me:${as_lineno-$LINENO}: : CXXFLAGS already contains \$flag"; } >&5 + (: CXXFLAGS already contains $flag) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } ;; #( + *) : + + as_fn_append CXXFLAGS " $flag" + { { $as_echo "$as_me:${as_lineno-$LINENO}: : CXXFLAGS=\"\$CXXFLAGS\""; } >&5 + (: CXXFLAGS="$CXXFLAGS") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + ;; +esac + +else + + CXXFLAGS=$flag + { { $as_echo "$as_me:${as_lineno-$LINENO}: : CXXFLAGS=\"\$CXXFLAGS\""; } >&5 + (: CXXFLAGS="$CXXFLAGS") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + +fi + +else + : +fi + +done + +else + + + + +for flag in -GR-; do + as_CACHEVAR=`$as_echo "ax_cv_check_cxxflags__$flag" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts $flag" >&5 +$as_echo_n "checking whether C++ compiler accepts $flag... " >&6; } +if eval \${$as_CACHEVAR+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CXXFLAGS + CXXFLAGS="$CXXFLAGS $flag" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + eval "$as_CACHEVAR=yes" +else + eval "$as_CACHEVAR=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CXXFLAGS=$ax_check_save_flags +fi +eval ac_res=\$$as_CACHEVAR + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : + +if ${CXXFLAGS+:} false; then : + + case " $CXXFLAGS " in #( + *" $flag "*) : + { { $as_echo "$as_me:${as_lineno-$LINENO}: : CXXFLAGS already contains \$flag"; } >&5 + (: CXXFLAGS already contains $flag) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } ;; #( + *) : + + as_fn_append CXXFLAGS " $flag" + { { $as_echo "$as_me:${as_lineno-$LINENO}: : CXXFLAGS=\"\$CXXFLAGS\""; } >&5 + (: CXXFLAGS="$CXXFLAGS") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + ;; +esac + +else + + CXXFLAGS=$flag + { { $as_echo "$as_me:${as_lineno-$LINENO}: : CXXFLAGS=\"\$CXXFLAGS\""; } >&5 + (: CXXFLAGS="$CXXFLAGS") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + +fi + +else + : +fi + +done + +fi + # Check whether --with-xvfb-tests was given. if test "${with_xvfb_tests+set}" = set; then : @@ -23346,7 +23815,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by gjs $as_me 1.53.3, which was +This file was extended by gjs $as_me 1.54.3, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -23417,7 +23886,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -gjs config.status 1.53.3 +gjs config.status 1.54.3 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff -Nru gjs-1.53.3/configure.ac gjs-1.54.3/configure.ac --- gjs-1.53.3/configure.ac 2018-06-18 22:44:01.000000000 +0000 +++ gjs-1.54.3/configure.ac 2018-11-12 17:16:04.000000000 +0000 @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. m4_define(pkg_major_version, 1) -m4_define(pkg_minor_version, 53) +m4_define(pkg_minor_version, 54) m4_define(pkg_micro_version, 3) m4_define(pkg_version, pkg_major_version.pkg_minor_version.pkg_micro_version) m4_define(pkg_int_version, (pkg_major_version * 100 + pkg_minor_version) * 100 + pkg_micro_version) @@ -32,7 +32,7 @@ AC_LANG([C++]) AC_USE_SYSTEM_EXTENSIONS AC_PROG_CXX -AX_CXX_COMPILE_STDCXX_11 +AX_CXX_COMPILE_STDCXX_14 LT_PREREQ([2.2.0]) # no stupid static libraries @@ -59,24 +59,21 @@ AX_VALGRIND_CHECK # Checks for libraries. -m4_define(glib_required_version, 2.50.0) +m4_define(glib_required_version, 2.54.0) GOBJECT_INTROSPECTION_REQUIRE([1.41.4]) GOBJECT_REQUIREMENT="gobject-2.0 >= glib_required_version" gjs_base_packages="$GOBJECT_REQUIREMENT gio-2.0" -common_packages="gthread-2.0 gio-2.0 >= glib_required_version mozjs-52" +common_packages="gthread-2.0 gio-2.0 >= glib_required_version mozjs-60" gjs_packages="gobject-introspection-1.0 libffi $common_packages" gjs_cairo_packages="cairo cairo-gobject $common_packages" gjs_gtk_packages="gtk+-3.0 >= 3.20" -# gjs-tests links against everything -gjstests_packages="gio-unix-2.0 $gjs_packages" AX_PKG_CHECK_MODULES([GJS], [$GOBJECT_REQUIREMENT], [$gjs_packages]) dnl These don't need to be put in the .pc file so use regular PKG_CHECK_MODULES PKG_CHECK_MODULES([GJS_GDBUS], [$gjs_base_packages]) PKG_CHECK_MODULES([GJS_CONSOLE], [$gjs_base_packages]) -PKG_CHECK_MODULES([GJSTESTS], [$gjstests_packages]) CPPFLAGS_save="$CPPFLAGS" CPPFLAGS="$GJS_CFLAGS" @@ -89,7 +86,7 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ #include #ifdef JS_DEBUG -#error debug yes, if we didn't already error out due to DEBUG not being defined +#error debug yes, if we did not already error out due to DEBUG not being defined #endif ]])], [ AC_MSG_RESULT([no]) @@ -152,39 +149,59 @@ AC_ARG_ENABLE([profiler], [AS_HELP_STRING([--disable-profiler], [Don't build profiler])]) AS_IF([test x$enable_profiler != xno], [ + # Requires timer_settime() - only on Linux gl_TIMER_TIME - AS_IF([test x$ac_cv_func_timer_settime = xno], + # Requires SIGEV_THREAD_ID - not in some stdlibs + have_sigev_thread_id=no + AC_MSG_CHECKING([for Linux SIGEV_THREAD_ID]) + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([[#include ]], [return SIGEV_THREAD_ID;]) + ], [ + have_sigev_thread_id=yes + AC_MSG_RESULT([yes]) + ], [AC_MSG_RESULT([no])]) + AS_IF([test x$ac_cv_func_timer_settime = xno -o x$have_sigev_thread_id = xno], [AC_MSG_ERROR([The profiler is currently only supported on Linux. +The standard library must support timer_settime() and SIGEV_THREAD_ID. Configure with --disable-profiler to skip it on other platforms.])]) AC_DEFINE([ENABLE_PROFILER], [1], [Define if the profiler should be built.]) ]) AM_CONDITIONAL([ENABLE_PROFILER], [test x$enable_profiler != xno]) +# Optional redline dep (enabled by default) +AC_ARG_ENABLE([readline], + [AS_HELP_STRING([--disable-readline], [Don't build readline-related code @<:@default=no@:>@])]) + PKG_CHECK_VAR([GI_DATADIR], [gobject-introspection-1.0], [gidatadir]) # readline LIBS_no_readline=$LIBS -# On some systems we need to link readline to a termcap compatible -# library. -gjs_cv_lib_readline=no -AC_MSG_CHECKING([how to link readline libs]) -for gjs_libtermcap in "" ncursesw ncurses curses termcap; do - if test -z "$gjs_libtermcap"; then - READLINE_LIBS="-lreadline" - else - READLINE_LIBS="-lreadline -l$gjs_libtermcap" - fi - LIBS="$READLINE_LIBS $LIBS_no_readline" - AC_LINK_IFELSE( - [AC_LANG_CALL([],[readline])], - [gjs_cv_lib_readline=yes]) - if test $gjs_cv_lib_readline = yes; then - break - fi -done +AS_IF([test x$enable_readline != xno],[ + + # On some systems we need to link readline to a termcap compatible + # library. + gjs_cv_lib_readline=no + AC_MSG_CHECKING([how to link readline libs]) + for gjs_libtermcap in "" ncursesw ncurses curses termcap; do + if test -z "$gjs_libtermcap"; then + READLINE_LIBS="-lreadline" + else + READLINE_LIBS="-lreadline -l$gjs_libtermcap" + fi + LIBS="$READLINE_LIBS $LIBS_no_readline" + AC_LINK_IFELSE( + [AC_LANG_CALL([],[readline])], + [gjs_cv_lib_readline=yes]) + if test $gjs_cv_lib_readline = yes; then + break + fi + done +],[gjs_cv_lib_readline=no]) + if test $gjs_cv_lib_readline = no; then - AC_MSG_RESULT([none]) + AS_IF([test x$enable_readline != xno], + [AC_MSG_ERROR([Readline requested but not found. Configure with --disable-readline.])]) READLINE_LIBS="" ac_cv_header_readline_readline_h=no else @@ -287,6 +304,16 @@ AX_APPEND_COMPILE_FLAGS([$lt_prog_compiler_pic], [CFLAGS]) AX_APPEND_LINK_FLAGS([$lt_prog_compiler_pic]) +dnl If SpiderMonkey was compiled with this configure option, then GJS needs to +dnl be compiled with it as well, because we inherit from a SpiderMonkey class in +dnl context.cpp. See build/autoconf/compiler-opts.m4 in mozjs52. +AC_ARG_ENABLE([cpp-rtti], + [AS_HELP_STRING([--enable-cpp-rtti], + [needs to match SpiderMonkey's config option @<:@default=off@:>@])]) +AS_IF([test "x$enable_cpp_rtti" != "xyes"], + [AX_APPEND_COMPILE_FLAGS([-fno-rtti])], + [AX_APPEND_COMPILE_FLAGS([-GR-])]) + AC_ARG_WITH([xvfb-tests], [AS_HELP_STRING([--with-xvfb-tests], [Run all tests under an XVFB server @<:@default=no@:>@])]) diff -Nru gjs-1.53.3/debian/changelog gjs-1.54.3/debian/changelog --- gjs-1.53.3/debian/changelog 2018-09-24 15:27:41.000000000 +0000 +++ gjs-1.54.3/debian/changelog 2019-02-02 14:35:41.000000000 +0000 @@ -1,8 +1,77 @@ -gjs (1.53.3-1+zorin1) bionic; urgency=medium +gjs (1.54.3-1build1+zorin1) bionic; urgency=medium - * Uploaded to Zorin OS 15 repositories + * Published in Zorin OS 15 repositories - -- Artyom Zorin Mon, 24 Sep 2018 16:27:35 +0100 + -- Artyom Zorin Sat, 02 Feb 2019 14:35:41 +0000 + +gjs (1.54.3-1build1) disco; urgency=medium + + * No-change rebuild for readline soname change. + + -- Matthias Klose Tue, 15 Jan 2019 10:25:50 +0000 + +gjs (1.54.3-1) unstable; urgency=medium + + * Team upload + * New upstream release + * Force time zone to UTC when running tests. + This hopefully fixes FTBFS in the pathological time zone used to test + reproducible builds. + + -- Simon McVittie Thu, 15 Nov 2018 11:31:50 +0000 + +gjs (1.54.2-1) unstable; urgency=medium + + * Team upload + * Upload to unstable (starts transition: #906016) + * d/watch: Only watch for versions from a stable branch + * New upstream release + * Bump Standards-Version to 4.2.1 (no changes required) + * Use dpkg's default.mk to get upstream version number for dependencies. + This avoids relying on the differently-named variables in + gnome-get-source.mk, and also does the right thing if gjs ever gains + an epoch. + * d/rules: Remove gnome-get-source.mk (please use uscan instead) + + -- Simon McVittie Fri, 02 Nov 2018 09:33:10 +0000 + +gjs (1.54.1-1) experimental; urgency=medium + + * Team upload + * New upstream release + + -- Simon McVittie Fri, 05 Oct 2018 11:40:01 +0100 + +gjs (1.54.0-1) experimental; urgency=medium + + * Team upload + + [ Simon McVittie ] + * New upstream development release (Closes: #904994) + * Depend and build-depend on mozjs60 instead of mozjs52 + * d/copyright: Update + * Explicitly disable -Werror + * Drop build-dependency on gnome-common which does not appear to be + needed since 1.47.0 + * d/gbp.conf: Don't number patches + * Add Breaks on gnome-documents, gnome-sound-recorder and polari + versions that use conditional catch clauses, which are a + Mozilla-specific extension that is no longer supported in mozjs60 + * Add Breaks on gnome-shell (<< 3.29.90) for ByteArray changes + * dh_makeshlibs: Only generate a dependency on libgjs0g, not on the + virtual package libgjs0-libmozjs-60-0. Since the gjs-internals + module was removed in version 1.47.0, gjs does not expose mozjs + API/ABI in its own ABI, so it is unnecessary for gjs users like + gnome-shell, gnome-sushi and polari to depend on their gjs having + been compiled against a specific version of mozjs. + * Provide libgjs0-libmozjs-52-0 to indicate that our ABI is + compatible with the gjs that used libmozjs-52-0, until all + dependent packages have been rebuilt + + [ Tim Lunn ] + * New upstream release + + -- Simon McVittie Thu, 20 Sep 2018 10:32:06 +0100 gjs (1.53.3-1) experimental; urgency=medium diff -Nru gjs-1.53.3/debian/control gjs-1.54.3/debian/control --- gjs-1.53.3/debian/control 2018-09-24 15:27:47.000000000 +0000 +++ gjs-1.54.3/debian/control 2019-02-02 14:35:41.000000000 +0000 @@ -1,20 +1,19 @@ # This file is autogenerated. DO NOT EDIT! -# +# # Modifications should be made to debian/control.in instead. # This file is regenerated automatically in the clean target. Source: gjs Section: interpreters Priority: optional Maintainer: Debian GNOME Maintainers -Uploaders: Andreas Henriksson , Iain Lane , Michael Biebl +Uploaders: Iain Lane , Jeremy Bicha , Tim Lunn Build-Depends: debhelper (>= 11), - gnome-common, gnome-pkg-tools, pkg-config (>= 0.28), - libglib2.0-dev (>= 2.50.0), + libglib2.0-dev (>= 2.54.0), libgirepository1.0-dev (>= 1.53.4), gobject-introspection (>= 1.53.4), - libmozjs-52-dev, + libmozjs-60-dev, libreadline-dev, libgtk-3-dev (>= 3.20), libcairo2-dev, @@ -23,19 +22,22 @@ at-spi2-core , xvfb Rules-Requires-Root: no -Standards-Version: 4.1.3 +Standards-Version: 4.2.1 Vcs-Git: https://salsa.debian.org/gnome-team/gjs.git Vcs-Browser: https://salsa.debian.org/gnome-team/gjs -Homepage: https://wiki.gnome.org/Projects/Gjs +Homepage: https://gitlab.gnome.org/GNOME/gjs/wikis Package: gjs Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Breaks: gnome-characters (<< 3.25.92), + gnome-documents (<< 3.30.0), gnome-maps (<< 3.26.1), + gnome-shell (<< 3.29.90), + gnome-sound-recorder (<< 3.28.1-3~), gnome-weather (<< 3.25.91), - polari (<< 3.25.4), + polari (<< 3.29.91), ostree-tests (<< 2017.11-1~), Description: Mozilla-based javascript bindings for the GNOME platform Makes it possible for applications to use all of GNOME's platform @@ -82,7 +84,7 @@ libgjs0g (= ${binary:Version}), libgirepository1.0-dev (>= 1.53.4), libgtk-3-dev (>= 3.20), - libmozjs-52-dev + libmozjs-60-dev Description: Mozilla-based javascript bindings for the GNOME platform Makes it possible for applications to use all of GNOME's platform libraries using the JavaScript language. It's mainly based on the diff -Nru gjs-1.53.3/debian/control.in gjs-1.54.3/debian/control.in --- gjs-1.53.3/debian/control.in 2018-06-27 17:20:12.000000000 +0000 +++ gjs-1.54.3/debian/control.in 2018-11-15 11:31:50.000000000 +0000 @@ -4,13 +4,12 @@ Maintainer: Debian GNOME Maintainers Uploaders: @GNOME_TEAM@ Build-Depends: debhelper (>= 11), - gnome-common, gnome-pkg-tools, pkg-config (>= 0.28), - libglib2.0-dev (>= 2.50.0), + libglib2.0-dev (>= 2.54.0), libgirepository1.0-dev (>= 1.53.4), gobject-introspection (>= 1.53.4), - libmozjs-52-dev, + libmozjs-60-dev, libreadline-dev, libgtk-3-dev (>= 3.20), libcairo2-dev, @@ -19,19 +18,22 @@ at-spi2-core , xvfb Rules-Requires-Root: no -Standards-Version: 4.1.3 +Standards-Version: 4.2.1 Vcs-Git: https://salsa.debian.org/gnome-team/gjs.git Vcs-Browser: https://salsa.debian.org/gnome-team/gjs -Homepage: https://wiki.gnome.org/Projects/Gjs +Homepage: https://gitlab.gnome.org/GNOME/gjs/wikis Package: gjs Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Breaks: gnome-characters (<< 3.25.92), + gnome-documents (<< 3.30.0), gnome-maps (<< 3.26.1), + gnome-shell (<< 3.29.90), + gnome-sound-recorder (<< 3.28.1-3~), gnome-weather (<< 3.25.91), - polari (<< 3.25.4), + polari (<< 3.29.91), ostree-tests (<< 2017.11-1~), Description: Mozilla-based javascript bindings for the GNOME platform Makes it possible for applications to use all of GNOME's platform @@ -78,7 +80,7 @@ libgjs0g (= ${binary:Version}), libgirepository1.0-dev (>= 1.53.4), libgtk-3-dev (>= 3.20), - libmozjs-52-dev + libmozjs-60-dev Description: Mozilla-based javascript bindings for the GNOME platform Makes it possible for applications to use all of GNOME's platform libraries using the JavaScript language. It's mainly based on the diff -Nru gjs-1.53.3/debian/copyright gjs-1.54.3/debian/copyright --- gjs-1.53.3/debian/copyright 2018-06-27 17:20:12.000000000 +0000 +++ gjs-1.54.3/debian/copyright 2018-11-15 11:31:50.000000000 +0000 @@ -1,46 +1,33 @@ -This work was packaged for Debian by: - +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Source: https://download.gnome.org/sources/gjs/ +Comment: + This work was packaged for Debian by: + . Gustavo Noronha Silva on Mon, 12 Oct 2009 18:38:36 -0300 - -It was downloaded from https://download.gnome.org/sources/gjs/ - -Upstream Author: - + . + Upstream Author: + . litl, LLC +Files: * Copyright: + 2008 litl, LLC + 2009 Red Hat, Inc. + 2006-2007 Zeh Fernando and Nate Chatellier + 2018 Philip Chimento +License: Expat - Copyright © 2008 litl, LLC - -For the following files: - - gjs/jsapi-util.c - modules/gettext-native.c - modules/gettext.js - modules/jsUnit.js - modules/gettext-native.h - - Copyright © 2009 Red Hat, Inc. - -For the following files: - - modules/tweener/tweener.js - modules/tweener/tweenList.js - - Copyright © 2006-2007 Zeh Fernando and Nate Chatellier - -License: - +License: Expat Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + . The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -49,40 +36,35 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -The Debian packaging is: - - Copyright © 2009 Collabora Ltd. - -and is licensed under the same license as the upstream source. - -For the following files, the information bellow applies: +Files: debian/* +Copyright: + 2009 Collabora Ltd. +License: Expat +Files: modules/console.c gjs/stack.c - -License: - Version: MPL 1.1/GPL 2.0/LGPL 2.1 - +License: MPL-1.1 or GPL-2.0+ or LGPL-2.1+ The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.mozilla.org/MPL/ - + . Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. - + . The Original Code is Mozilla Communicator client code, released March 31, 1998. - + . The Initial Developer of the Original Code is Netscape Communications Corporation. Portions created by the Initial Developer are Copyright (C) 1998 the Initial Developer. All Rights Reserved. - + . Contributor(s): - + . Alternatively, the contents of this file may be used under the terms of either of the GNU General Public License Version 2 or later (the "GPL"), or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), @@ -94,20 +76,36 @@ and other provisions required by the GPL or the LGPL. If you do not delete the provisions above, a recipient may use your version of this file under the terms of any one of the MPL, the GPL or the LGPL. - +Copyright: Copyright © 2008 litl, LLC © 1998 Netscape Communications Corporation +Comment: + The GPLv2, LGPLv2.1 and MPLv1.1 files can be obtained, on Debian systems, + at /usr/share/common-licenses/GPL-2, /usr/share-common-licenses/LGPL-2.1 + and /usr/share/common-licenses/MPL-1.1, respectively. -The GPLv2 and LGPLv2.1 files can be obtained, on Debian systems, at -/usr/share/common-licenses/GPL-2, and -/usr/share-common-licenses/LGPL-2.1, respectively. - -For the following files the information bellow applies: +Files: + modules/_bootstrap/debugger.js +Copyright: + Unspecified, assumed to be: + 1998 Netscape Communications Corporation + 1998-2018 Mozilla Project + 2018 Philip Chimento +License: MPL-2.0 + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +Comment: + Based on js/examples/jorendb.js from mozjs60. + . + On Debian systems a copy of the MPLv2.0 can be found in + /usr/share/common-licenses/MPL-2.0 +Files: modules/tweener/equations.js - - Copyright © 2001 Robert Penner - License: BSD +Copyright: + 2001 Robert Penner +License: BSD-3-clause Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: . diff -Nru gjs-1.53.3/debian/gbp.conf gjs-1.54.3/debian/gbp.conf --- gjs-1.53.3/debian/gbp.conf 2018-06-27 17:20:12.000000000 +0000 +++ gjs-1.54.3/debian/gbp.conf 2018-11-15 11:31:50.000000000 +0000 @@ -3,3 +3,4 @@ debian-branch = debian/master upstream-branch = upstream/latest upstream-vcs-tag = %(version)s +patch-numbers = False diff -Nru gjs-1.53.3/debian/rules gjs-1.54.3/debian/rules --- gjs-1.53.3/debian/rules 2018-06-27 17:20:12.000000000 +0000 +++ gjs-1.54.3/debian/rules 2018-11-15 11:31:50.000000000 +0000 @@ -1,14 +1,11 @@ #!/usr/bin/make -f --include /usr/share/gnome-pkg-tools/1/rules/gnome-get-source.mk export DEB_BUILD_MAINT_OPTIONS = hardening=+all export DEB_LDFLAGS_MAINT_APPEND = -Wl,-z,defs -Wl,--as-needed -export DH_VERBOSE=1 +include /usr/share/dpkg/default.mk -# Use recursive variables since this variable must not be expanded until -# files have been installed. -LIBMOZJS = $(shell objdump -p debian/tmp/usr/lib/libgjs.so | awk '$$1=="NEEDED" && $$2~/^libmozjs/ { print $$2 }' | sed s/\\.so\\./-/ ) +export DH_VERBOSE=1 %: dh $@ --with gnome,gir @@ -17,6 +14,7 @@ dh_auto_configure -- \ --libdir=\$${prefix}/lib \ --libexecdir=\$${prefix}/lib/gjs \ + --disable-Werror \ --enable-installed-tests \ --with-xvfb-tests \ --with-dbus-tests @@ -33,12 +31,19 @@ override_dh_auto_test: ifeq (, $(filter nocheck, $(DEB_BUILD_OPTIONS))) - DEB_HOST_ARCH=$(DEB_HOST_ARCH) debian/test.sh + TZ=UTC DEB_HOST_ARCH=$(DEB_HOST_ARCH) debian/test.sh endif override_dh_makeshlibs: - dh_makeshlibs -Xusr/lib/gjs-1.0/ -V'libgjs0g (>= $(DEB_UPSTREAM_VERSION)), libgjs0-$(LIBMOZJS)' -- -c4 + dh_makeshlibs -Xusr/lib/gjs-1.0/ -V'libgjs0g (>= $(DEB_VERSION_EPOCH_UPSTREAM))' -- -c4 +# We don't actually use mozjs52 any more, but our ABI is compatible with +# the gjs that did. +# +# Yes, this really says libmozjs-52-0. Yes, this is intentional, even +# though we're now building against mozjs60. It's like this to avoid an +# unnecessary transition: programs built against gjs 1.52.x do not need +# to be rebuilt for 1.54.x. override_dh_gencontrol: - echo gjs:Provides=libgjs0-$(LIBMOZJS) >> debian/libgjs0g.substvars + echo gjs:Provides=libgjs0-libmozjs-52-0 >> debian/libgjs0g.substvars dh_gencontrol diff -Nru gjs-1.53.3/debian/watch gjs-1.54.3/debian/watch --- gjs-1.53.3/debian/watch 2018-06-27 17:20:12.000000000 +0000 +++ gjs-1.54.3/debian/watch 2018-11-15 11:31:50.000000000 +0000 @@ -1,3 +1,3 @@ version=4 -https://download.gnome.org/sources/@PACKAGE@/([\d\.]+)/ \ +https://download.gnome.org/sources/@PACKAGE@/([\d\.]+[02468])/ \ @PACKAGE@@ANY_VERSION@\.tar\.xz diff -Nru gjs-1.53.3/doc/ByteArray.md gjs-1.54.3/doc/ByteArray.md --- gjs-1.53.3/doc/ByteArray.md 2018-05-31 05:34:45.000000000 +0000 +++ gjs-1.54.3/doc/ByteArray.md 2018-11-08 04:20:06.000000000 +0000 @@ -1,16 +1,10 @@ -The ByteArray type in the `imports.byteArray` module is based on an -ECMAScript 4 proposal that was never adopted. This can be found at: -http://wiki.ecmascript.org/doku.php?id=proposals:bytearray -and the wikitext of that page is appended to this file. - -The main difference from the ECMA proposal is that gjs's ByteArray is -inside a module, and `toString()`/`fromString()` default to UTF-8 and take -optional encoding arguments. +The `imports.byteArray` module was originally based on an +ECMAScript 4 proposal that was never adopted. +Now that ES6 has typed arrays, we use `Uint8Array` to represent byte +arrays in GJS and add some extra functions for conversion to and from +strings and `GLib.Bytes`. -There are a number of more elaborate byte array proposals in the -Common JS project at http://wiki.commonjs.org/wiki/Binary - -We went with the ECMA proposal because it seems simplest, and the main +Unlike the old custom `ByteArray`, `Uint8Array` is not resizable. The main goal for most gjs users will be to shovel bytes between various C APIs, for example reading from an IO stream and then pushing the bytes into a parser. Actually manipulating bytes in JS is likely to be @@ -20,138 +14,30 @@ --- -ECMAScript proposal follows; remember it's almost but not quite like -gjs ByteArray, in particular we use UTF-8 instead of busted Latin-1 as -default encoding. - ---- - -# ByteArray # - -(Also see the [discussion page][1] for this proposal) - -## Overview ## - -In previous versions of ECMAScript, there wasn't a good way to efficiently represent a packed array of arbitrary bytes. The predefined core object Array is inefficient for this purpose; some developers have (mis)used character strings for this purpose, which may be slightly more efficient for some implementations, but still a misuse of the string type and either a less efficient use of memory (if one byte per character was stored) or cycles (if two bytes per char). - -Edition 4 will add a new predefined core object, ByteArray. A ByteArray can be thought of as similar to an Array of uint ([uint]) with each element truncated to the integer range of 0..255. - -## Creating a ByteArray ## - -To create a ByteArray object: - -```js -byteArrayObject = new ByteArray(byteArrayLength:uint) -``` - -byteArrayLength is the initial length of the ByteArray, in bytes. If omitted, the initial length is zero. - -All elements in a ByteArray are initialized to the value of zero. - -Unlike Array, there is no special form that allows you to list the initial values for the ByteArray's elements. However, the ByteArray class has an `intrinsic::to` static method that can convert an Array to a ByteArray, and implementations are free to optimize away the Array instance if it is used exclusively to initialize a ByteArray: - -```js -var values:ByteArray = [1, 2, 3, 4]; // legal by virtue of ByteArray.intrinsic::to -``` - -## Populating a ByteArray ## - -You can populate a ByteArray by assigning values to its elements. Each element can hold only an unsigned integer in the range 0..255 (inclusive). Values will be converted to unsigned integer (if necessary), then truncated to the least-significant 8 bits. - -For example, - -```js -var e = new ByteArray(3); - -e[0] = 0x12; // stores 18 -e[1] = Math.PI; // stores 3 -e[2] = "foo"; // stores 0 (generates compile error in strict mode) -e[2] = "42"; // stores 42 (generates compile error in strict mode) -e[2] = null; // stores 0 -e[2] = undefined; // stores 0 -``` - -## Referring to ByteArray Elements ## - -You refer to a ByteArray's elements by using the element's ordinal number; you refer to the first element of the array as `myArray[0]` and the second element of the array as `myArray[1]`, etc. The index of the elements begins with zero (0), but the length of array (for example, `myArray.length`) reflects the number of elements in the array. - -## ByteArray Methods ## - -The ByteArray object has the follow methods: - -### `static function fromString(s:String):ByteArray` ### - -Convert a String into newly constructed ByteArray; this creates a new ByteArray of the same length as the String, then assigns each ByteArray entry the corresponding charCodeAt() value of the String. As with other ByteArray assignments, only the lower 8 bits of the charCode value will be retained. - -```js -class ByteArray { - ... - static function fromString(s:String):ByteArray - { - var a:ByteArray = new Bytearray(s.length); - for (var i:int = 0; i < s.length; ++i) - a[i] = s.charCodeAt(i); - return a; - } - ... -} -``` - -### `static function fromArray(s:Array):ByteArray` ### - -Converts an Array into a newly constructed ByteArray; this creates a new ByteArray of the same length as the input Array, then assigns each ByteArray entry the corresponding entry value of the Array. As with other ByteArray assignments, only the lower 8 bits of the charCode value will be retained. - -```js -class ByteArray { - ... - static function fromArray(s:Array):ByteArray - { - var a:ByteArray = new Bytearray(s.length); - for (var i:int = 0; i < s.length; ++i) - a[i] = s[i]; - return a; - ... -} -``` - -### `function toString():String` ### - -Converts the ByteArray into a literal string, with each character entry set to the value of the corresponding ByteArray entry. - -The resulting string is guaranteed to round-trip back into an identical ByteArray by passing the result to `ByteArray.fromString()`, i.e., `b === ByteArray.fromString(b.toString())`. (Note that the reverse is not guaranteed to be true: `ByteArray.fromString(s).toString != s` unless all character codes in `s` are <= 255) - -```js -class ByteArray { - ... - function toString():String - { - // XXX return String.fromCharCode.apply(String, this); - var s:String = ""; - for (var i:int = 0; i < this.length; ++i) - s += String.fromCharCode(this[i]); - return s; - } - ... -} -``` +## ByteArray Functions ## -## ByteArray Properties ## +The ByteArray module has the following functions: -The ByteArray object has the following properties: +### `fromString(s:String, encoding:String):Uint8Array` ### -### `length:uint` ### +Convert a String into a newly constructed `Uint8Array`; this creates a +new `Uint8Array` of the same length as the String, then assigns each +`Uint8Array` entry the corresponding byte value of the String encoded +according to the given encoding (or UTF-8 if not given). -This property contains the number of bytes in the ByteArray. Setting the length property to a smaller value decreases the size of the ByteArray, discarding the unused bytes. Setting the length property to a larger value increases the size of the ByteArray, initializing all newly-allocated elements to zero. +### `toString(a:Uint8Array, encoding:String):String` ### -### `prototype:Object` ### +Converts the `Uint8Array` into a literal string. The bytes are +interpreted according to the given encoding (or UTF-8 if not given). -This property contains the methods of ByteArray. +The resulting string is guaranteed to round-trip back into an identical ByteArray by passing the result to `ByteArray.fromString()`, i.e., `b === ByteArray.fromString(ByteArray.toString(b, encoding), encoding)`. -## Prior Art ## +### `fromGBytes(b:GLib.Bytes):Uint8Array` ### -Adobe's ActionScript 3.0 provides [`flash.utils.ByteArray`][2], which was the primary inspiration for this proposal. Note that the ActionScript version of ByteArray includes additional functionality which has been omitted here for the sake of allowing small implementations; it is anticipated that the extra functionality can be written in ES4 itself and provided as a standard library. +Convert a `GLib.Bytes` instance into a newly constructed `Uint8Array`. +The contents are copied. -[Synopsis of ActionScript's implementation too detailed and moved to [discussion][1] page] +### `toGBytes(a:Uint8Array):GLib.Bytes` ### -[1] http://wiki.ecmascript.org/doku.php?id=discussion:bytearray -[2] http://livedocs.macromedia.com/flex/2/langref/flash/utils/ByteArray.html +Converts the `Uint8Array` into a `GLib.Bytes` instance. +The contents are copied. diff -Nru gjs-1.53.3/doc/Hacking.md gjs-1.54.3/doc/Hacking.md --- gjs-1.53.3/doc/Hacking.md 2018-05-31 05:34:45.000000000 +0000 +++ gjs-1.54.3/doc/Hacking.md 2018-11-12 16:43:50.000000000 +0000 @@ -2,6 +2,16 @@ ## Setting up ## +We use the +[Google style guide](https://google.github.io/styleguide/cppguide.html) +for C++ code, with a few exceptions, 4-space indents being the main one. +There is a handy git commit hook that will autoformat your code when you +commit it. +In your GJS checkout directory, run +`tools/git-pre-commit-format install`. +For more information, see +. + For the time being, we recommend using JHBuild to develop GJS. Follow the [instructions from GNOME](https://wiki.gnome.org/HowDoI/Jhbuild) for [JHBuild](https://git.gnome.org/browse/jhbuild/). @@ -9,10 +19,10 @@ recommend building it on JHBuild so that you can enable the debugging features. Add this to your JHBuild configuration file: ```python -module_autogenargs['mozjs52'] = '--enable-debug' +module_autogenargs['mozjs60'] = '--enable-debug' ``` -Make sure it is built first with `jhbuild build mozjs52`, otherwise +Make sure it is built first with `jhbuild build mozjs60`, otherwise `jhbuild build gjs` will skip it if you have the system package installed. @@ -36,7 +46,7 @@ After reaching a breakpoint in your program, type this to activate the pretty-printers: ``` -source ~/.cache/jhbuild/build/mozjs-52.Y.Z/js/src/shell/js-gdb.py +source ~/.cache/jhbuild/build/mozjs-60.Y.Z/js/src/shell/js-gdb.py ``` (replace `Y.Z` with mozjs's minor and micro version numbers) diff -Nru gjs-1.53.3/examples/clutter.js gjs-1.54.3/examples/clutter.js --- gjs-1.53.3/examples/clutter.js 2018-02-18 02:13:41.000000000 +0000 +++ gjs-1.54.3/examples/clutter.js 2018-11-08 04:20:06.000000000 +0000 @@ -4,14 +4,15 @@ let stage = new Clutter.Stage(); -let texture = new Clutter.Texture({ filename: 'test.jpg', - reactive: true }); +let texture = new Clutter.Texture({ + filename: 'test.jpg', + reactive: true, +}); -texture.connect('button-press-event', - function(o, event) { - log('Clicked!'); - return true; - }); +texture.connect('button-press-event', () => { + log('Clicked!'); + return Clutter.EVENT_STOP; +}); let color = new Clutter.Color(); color.from_string('Black'); diff -Nru gjs-1.53.3/examples/gio-cat.js gjs-1.54.3/examples/gio-cat.js --- gjs-1.53.3/examples/gio-cat.js 2018-02-18 02:13:41.000000000 +0000 +++ gjs-1.54.3/examples/gio-cat.js 2018-11-12 16:43:50.000000000 +0000 @@ -11,7 +11,7 @@ try { contents = f.load_contents_finish(res)[1]; } catch (e) { - log("*** ERROR: " + e.message); + logError(e); loop.quit(); return; } @@ -23,7 +23,7 @@ } if (ARGV.length != 1) { - printerr("Usage: gio-cat.js filename"); + printerr('Usage: gio-cat.js filename'); } else { cat(ARGV[0]); } diff -Nru gjs-1.53.3/examples/gtk.js gjs-1.54.3/examples/gtk.js --- gjs-1.53.3/examples/gtk.js 2018-05-11 20:04:51.000000000 +0000 +++ gjs-1.54.3/examples/gtk.js 2018-11-08 04:20:06.000000000 +0000 @@ -1,6 +1,6 @@ // Include this in case both GTK3 and GTK4 installed, otherwise an exception // will be thrown -imports.gi.versions.Gtk = "3.0"; +imports.gi.versions.Gtk = '3.0'; const Gtk = imports.gi.Gtk; // Initialize Gtk before you start calling anything from the import @@ -9,7 +9,7 @@ // Construct a top-level window let window = new Gtk.Window ({ type: Gtk.WindowType.TOPLEVEL, - title: "A default title", + title: 'A default title', default_width: 300, default_height: 250, // A decent example of how constants are mapped: @@ -20,11 +20,11 @@ // Object properties can also be set or changed after construction, unless they // are marked construct-only. -window.title = "Hello World!"; +window.title = 'Hello World!'; // This is a callback function -function onDeleteEvent(widget, event) { - log("delete-event emitted"); +function onDeleteEvent() { + log('delete-event emitted'); // If you return false in the "delete_event" signal handler, Gtk will emit // the "destroy" signal. // @@ -36,20 +36,20 @@ // When the window is given the "delete_event" signal (this is given by the // window manager, usually by the "close" option, or on the titlebar), we ask // it to call the onDeleteEvent() function as defined above. -window.connect("delete-event", onDeleteEvent); +window.connect('delete-event', onDeleteEvent); // GJS will warn when calling a C function with unexpected arguments... // // window.connect("destroy", Gtk.main_quit); // // ...so use arrow functions for inline callbacks with arguments to adjust -window.connect("destroy", () => { +window.connect('destroy', () => { Gtk.main_quit(); }); // Create a button to close the window let button = new Gtk.Button({ - label: "Close the Window", + label: 'Close the Window', // Set visible to 'true' if you don't want to call button.show() later visible: true, // Another example of constant mapping: @@ -60,7 +60,7 @@ }); // Connect to the 'clicked' signal, using another way to call an arrow function -button.connect("clicked", () => window.destroy()); +button.connect('clicked', () => window.destroy()); // Add the button to the window window.add(button); diff -Nru gjs-1.53.3/examples/http-server.js gjs-1.54.3/examples/http-server.js --- gjs-1.53.3/examples/http-server.js 2018-02-18 02:13:41.000000000 +0000 +++ gjs-1.54.3/examples/http-server.js 2018-11-08 04:20:06.000000000 +0000 @@ -4,22 +4,22 @@ function main() { let handler = function(server, msg, path, query, client) { - msg.status_code = 200; - msg.response_headers.set_content_type('text/html', {}); - msg.response_body.append('Greetings, visitor from ' + client.get_host() + '
What is your name?
\n'); + msg.status_code = 200; + msg.response_headers.set_content_type('text/html', {}); + msg.response_body.append(`Greetings, visitor from ${client.get_host()}
What is your name?
\n`); }; - let helloHandler = function(server, msg, path, query, client) { - if (!query) { + let helloHandler = function(server, msg, path, query) { + if (!query) { msg.set_redirect(302, '/'); return; - } + } - msg.status_code = 200; - msg.response_headers.set_content_type('text/html', { charset: 'UTF-8' }); - msg.response_body.append('Hello, ' + query.myname + '! \u263A
Go back'); + msg.status_code = 200; + msg.response_headers.set_content_type('text/html', {charset: 'UTF-8'}); + msg.response_body.append(`Hello, ${query.myname}! ☺
Go back`); }; - let server = new Soup.Server({ port: 1080 }); + let server = new Soup.Server({port: 1080}); server.add_handler('/', handler); server.add_handler('/hello', helloHandler); server.run(); diff -Nru gjs-1.53.3/gi/arg.cpp gjs-1.54.3/gi/arg.cpp --- gjs-1.53.3/gi/arg.cpp 2018-05-31 05:34:45.000000000 +0000 +++ gjs-1.54.3/gi/arg.cpp 2018-11-12 17:03:55.000000000 +0000 @@ -965,6 +965,16 @@ JS::MutableHandleValue value) { GValue *values = (GValue *)array; + + // a null array pointer takes precedence over whatever `length` says + if (!values) { + JSObject* jsarray = JS_NewArrayObject(context, 0); + if (!jsarray) + return false; + value.setObject(*jsarray); + return true; + } + unsigned int i; JS::AutoValueVector elems(context); if (!elems.resize(length)) @@ -1666,10 +1676,9 @@ /* Handle Struct/Union first since we don't necessarily need a GType for them */ /* We special case Closures later, so skip them here */ !g_type_is_a(gtype, G_TYPE_CLOSURE)) { - - if (g_type_is_a(gtype, G_TYPE_BYTES) - && gjs_typecheck_bytearray(context, obj, false)) { - arg->v_pointer = gjs_byte_array_get_bytes(context, obj); + if (g_type_is_a(gtype, G_TYPE_BYTES) && + JS_IsUint8Array(obj)) { + arg->v_pointer = gjs_byte_array_get_bytes(obj); } else if (g_type_is_a(gtype, G_TYPE_ERROR)) { if (!gjs_typecheck_gerror(context, obj, true)) { arg->v_pointer = NULL; @@ -1930,7 +1939,18 @@ &ghash)) { wrong = true; } else { +#if __GNUC__ >= 8 +_Pragma("GCC diagnostic push") +_Pragma("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +#endif + /* The compiler isn't smart enough to figure out that ghash + * will always be initialized if gjs_object_to_g_hash() + * returns true. + */ arg->v_pointer = ghash; +#if __GNUC__ >= 8 +_Pragma("GCC diagnostic pop") +#endif } g_base_info_unref((GIBaseInfo*) key_param_info); @@ -1944,13 +1964,13 @@ GIArrayType array_type = g_type_info_get_array_type(type_info); /* First, let's handle the case where we're passed an instance - * of our own byteArray class. + * of Uint8Array and it needs to be marshalled to GByteArray. */ - if (value.isObjectOrNull()) { + if (value.isObject()) { JS::RootedObject bytearray_obj(context, value.toObjectOrNull()); - if (gjs_typecheck_bytearray(context, bytearray_obj, false) - && array_type == GI_ARRAY_TYPE_BYTE_ARRAY) { - arg->v_pointer = gjs_byte_array_get_byte_array(context, bytearray_obj); + if (JS_IsUint8Array(bytearray_obj) && + array_type == GI_ARRAY_TYPE_BYTE_ARRAY) { + arg->v_pointer = gjs_byte_array_get_byte_array(bytearray_obj); break; } else { /* Fall through, !handled */ @@ -2262,13 +2282,7 @@ /* Special case array(guint8) */ if (element_type == GI_TYPE_TAG_UINT8) { - GByteArray gbytearray; - - gbytearray.data = (guint8 *) array; - gbytearray.len = length; - - JS::RootedObject obj(context, - gjs_byte_array_from_byte_array(context, &gbytearray)); + JSObject* obj = gjs_byte_array_from_data(context, length, array); if (!obj) return false; value_p.setObject(*obj); @@ -2279,6 +2293,15 @@ if (element_type == GI_TYPE_TAG_UNICHAR) return gjs_string_from_ucs4(context, (gunichar *) array, length, value_p); + // a null array pointer takes precedence over whatever `length` says + if (!array) { + JSObject* jsarray = JS_NewArrayObject(context, 0); + if (!jsarray) + return false; + value_p.setObject(*jsarray); + return true; + } + JS::AutoValueVector elems(context); if (!elems.resize(length)) g_error("Unable to resize vector"); @@ -2476,13 +2499,8 @@ /* Special case array(guint8) */ if (element_type == GI_TYPE_TAG_UINT8) { - GByteArray gbytearray; - - gbytearray.data = (guint8 *) c_array; - gbytearray.len = strlen((const char *) c_array); - - JS::RootedObject obj(context, - gjs_byte_array_from_byte_array(context, &gbytearray)); + size_t len = strlen(static_cast(c_array)); + JSObject* obj = gjs_byte_array_from_data(context, len, c_array); if (!obj) return false; value_p.setObject(*obj); @@ -2993,10 +3011,12 @@ return gjs_array_from_fixed_size_array(context, value_p, type_info, arg->v_pointer); } } else if (g_type_info_get_array_type(type_info) == GI_ARRAY_TYPE_BYTE_ARRAY) { - JSObject *array = gjs_byte_array_from_byte_array(context, - (GByteArray*)arg->v_pointer); + auto* byte_array = static_cast(arg->v_pointer); + JSObject* array = + gjs_byte_array_from_byte_array(context, byte_array); if (!array) { - gjs_throw(context, "Couldn't convert GByteArray to a ByteArray"); + gjs_throw(context, + "Couldn't convert GByteArray to a Uint8Array"); return false; } value_p.setObject(*array); diff -Nru gjs-1.53.3/gi/boxed.cpp gjs-1.54.3/gi/boxed.cpp --- gjs-1.53.3/gi/boxed.cpp 2018-06-18 22:44:01.000000000 +0000 +++ gjs-1.54.3/gi/boxed.cpp 2018-11-12 16:43:50.000000000 +0000 @@ -40,11 +40,6 @@ #include -/* Reserved slots of JSNative accessor wrappers */ -enum { - SLOT_PROP_NAME, -}; - struct Boxed { /* prototype info */ GIBoxedInfo *info; @@ -577,31 +572,6 @@ return true; } -static JSObject * -define_native_accessor_wrapper(JSContext *cx, - JSNative call, - unsigned nargs, - const char *func_name, - uint32_t id) -{ - JSFunction *func = js::NewFunctionWithReserved(cx, call, nargs, 0, - func_name); - if (!func) - return NULL; - - JSObject *func_obj = JS_GetFunctionObject(func); - js::SetFunctionNativeReserved(func_obj, SLOT_PROP_NAME, - JS::PrivateUint32Value(id)); - return func_obj; -} - -static uint32_t -native_accessor_slot(JSObject *func_obj) -{ - return js::GetFunctionNativeReserved(func_obj, SLOT_PROP_NAME) - .toPrivateUint32(); -} - static bool boxed_field_getter(JSContext *context, unsigned argc, @@ -613,8 +583,9 @@ GArgument arg; bool success = false; - field_info = get_field_info(context, priv, - native_accessor_slot(&args.callee())); + uint32_t field_ix = gjs_dynamic_property_private_slot(&args.callee()) + .toPrivateUint32(); + field_info = get_field_info(context, priv, field_ix); if (!field_info) return false; @@ -789,8 +760,9 @@ GIFieldInfo *field_info; bool success = false; - field_info = get_field_info(cx, priv, - native_accessor_slot(&args.callee())); + uint32_t field_ix = gjs_dynamic_property_private_slot(&args.callee()) + .toPrivateUint32(); + field_info = get_field_info(cx, priv, field_ix); if (!field_info) return false; @@ -839,31 +811,14 @@ for (i = 0; i < n_fields; i++) { GIFieldInfo *field = g_struct_info_get_field (priv->info, i); const char *field_name = g_base_info_get_name ((GIBaseInfo *)field); - GjsAutoChar getter_name = g_strconcat("boxed_field_get::", - field_name, NULL); - GjsAutoChar setter_name = g_strconcat("boxed_field_set::", - field_name, NULL); - g_base_info_unref ((GIBaseInfo *)field); - - /* In order to have one getter and setter for all the properties - * we define, we must provide the property index in a "reserved - * slot" for which we must unfortunately use the jsfriendapi. */ - JS::RootedObject getter(cx, - define_native_accessor_wrapper(cx, boxed_field_getter, 0, - getter_name, i)); - if (!getter) - return false; - JS::RootedObject setter(cx, - define_native_accessor_wrapper(cx, boxed_field_setter, 1, - setter_name, i)); - if (!setter) - return false; - - if (!JS_DefineProperty(cx, proto, field_name, JS::UndefinedHandleValue, - JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_GETTER | JSPROP_SETTER, - JS_DATA_TO_FUNC_PTR(JSNative, getter.get()), - JS_DATA_TO_FUNC_PTR(JSNative, setter.get()))) + JS::RootedValue private_id(cx, JS::PrivateUint32Value(i)); + bool ok = gjs_define_property_dynamic(cx, proto, field_name, + "boxed_field", boxed_field_getter, + boxed_field_setter, private_id, + GJS_MODULE_PROP_FLAGS); + g_base_info_unref(field); + if (!ok) return false; } @@ -900,19 +855,17 @@ * class have. */ static const struct JSClassOps gjs_boxed_class_ops = { - NULL, /* addProperty */ - NULL, /* deleteProperty */ - NULL, /* getProperty */ - NULL, /* setProperty */ - NULL, /* enumerate */ + nullptr, // addProperty + nullptr, // deleteProperty + nullptr, // enumerate + nullptr, // newEnumerate boxed_resolve, - nullptr, /* mayResolve */ + nullptr, // mayResolve boxed_finalize, - NULL, /* call */ - NULL, /* hasInstance */ - NULL, /* construct */ - boxed_trace -}; + nullptr, // call + nullptr, // hasInstance + nullptr, // construct + boxed_trace}; /* We allocate 1 reserved slot; this is typically unused, but if the * boxed is for a nested structure inside a parent structure, the @@ -931,9 +884,8 @@ }; JSFunctionSpec gjs_boxed_proto_funcs[] = { - JS_FS("toString", to_string_func, 0, 0), - JS_FS_END -}; + JS_FN("toString", to_string_func, 0, 0), + JS_FS_END}; static bool type_can_be_allocated_directly(GITypeInfo *type_info) diff -Nru gjs-1.53.3/gi/function.cpp gjs-1.54.3/gi/function.cpp --- gjs-1.53.3/gi/function.cpp 2018-05-31 06:35:20.000000000 +0000 +++ gjs-1.54.3/gi/function.cpp 2018-11-12 16:43:50.000000000 +0000 @@ -1555,16 +1555,14 @@ * class have. */ static const struct JSClassOps gjs_function_class_ops = { - NULL, /* addProperty */ - NULL, /* deleteProperty */ - NULL, /* getProperty */ - NULL, /* setProperty */ - NULL, /* enumerate */ - NULL, /* resolve */ - nullptr, /* mayResolve */ + nullptr, // addProperty + nullptr, // deleteProperty + nullptr, // enumerate + nullptr, // newEnumerate + nullptr, // resolve + nullptr, // mayResolve function_finalize, - function_call -}; + function_call}; struct JSClass gjs_function_class = { "GIRepositoryFunction", /* means "new GIRepositoryFunction()" works */ diff -Nru gjs-1.53.3/gi/fundamental.cpp gjs-1.54.3/gi/fundamental.cpp --- gjs-1.53.3/gi/fundamental.cpp 2018-06-18 22:44:01.000000000 +0000 +++ gjs-1.54.3/gi/fundamental.cpp 2018-11-12 16:43:50.000000000 +0000 @@ -545,19 +545,17 @@ * the prototype unless JSCLASS_CONSTRUCT_PROTOTYPE is in flags. */ static const struct JSClassOps gjs_fundamental_class_ops = { - NULL, /* addProperty */ - NULL, /* deleteProperty */ - NULL, /* getProperty */ - NULL, /* setProperty */ - NULL, /* enumerate */ + nullptr, // addProperty + nullptr, // deleteProperty + nullptr, // enumerate + nullptr, // newEnumerate fundamental_instance_resolve, - nullptr, /* mayResolve */ + nullptr, // mayResolve fundamental_finalize, - NULL, /* call */ - NULL, /* hasInstance */ - NULL, /* construct */ - fundamental_trace -}; + nullptr, // call + nullptr, // hasInstance + nullptr, // construct + fundamental_trace}; struct JSClass gjs_fundamental_instance_class = { "GFundamental_Object", @@ -570,9 +568,8 @@ }; static JSFunctionSpec gjs_fundamental_instance_proto_funcs[] = { - JS_FS("toString", to_string_func, 0, 0), - JS_FS_END -}; + JS_FN("toString", to_string_func, 0, 0), + JS_FS_END}; static JSObject * gjs_lookup_fundamental_prototype(JSContext *context, @@ -614,37 +611,28 @@ g_assert(constructor); - if (!gjs_object_get_property(context, constructor, - GJS_STRING_PROTOTYPE, &value)) - return NULL; - - if (G_UNLIKELY (!value.isObjectOrNull())) + JS::RootedObject prototype(context); + if (!gjs_object_require_property(context, constructor, "constructor object", + GJS_STRING_PROTOTYPE, &prototype)) return NULL; - return value.toObjectOrNull(); + return prototype; } static JSObject* gjs_lookup_fundamental_prototype_from_gtype(JSContext *context, GType gtype) { - GIObjectInfo *info; - JSObject *proto; + GjsAutoInfo info; /* A given gtype might not have any definition in the introspection * data. If that's the case, try to look for a definition of any of the * parent type. */ - while ((info = (GIObjectInfo *) - g_irepository_find_by_gtype(g_irepository_get_default(), - gtype)) == NULL && - gtype != G_TYPE_INVALID) + while (gtype != G_TYPE_INVALID && + !(info = g_irepository_find_by_gtype(nullptr, gtype))) gtype = g_type_parent(gtype); - proto = gjs_lookup_fundamental_prototype(context, info, gtype); - if (info) - g_base_info_unref((GIBaseInfo*)info); - - return proto; + return gjs_lookup_fundamental_prototype(context, info, gtype); } bool @@ -697,10 +685,8 @@ /* funcs of constructor, MyConstructor.myfunc() */ NULL, prototype, - constructor)) { - gjs_log_exception(context); - g_error("Can't init class %s", constructor_name); - } + constructor)) + return false; /* Put the info in the prototype */ priv = g_slice_new0(Fundamental); @@ -737,13 +723,13 @@ g_base_info_get_name ((GIBaseInfo *)priv->info)); } - gjs_object_define_static_methods(context, constructor, gtype, info); + if (!gjs_object_define_static_methods(context, constructor, gtype, info)) + return false; JS::RootedObject gtype_obj(context, gjs_gtype_create_gtype_wrapper(context, gtype)); - JS_DefineProperty(context, constructor, "$gtype", gtype_obj, JSPROP_PERMANENT); - - return true; + return JS_DefineProperty(context, constructor, "$gtype", gtype_obj, + JSPROP_PERMANENT); } JSObject* diff -Nru gjs-1.53.3/gi/gerror.cpp gjs-1.54.3/gi/gerror.cpp --- gjs-1.53.3/gi/gerror.cpp 2018-05-31 05:34:45.000000000 +0000 +++ gjs-1.54.3/gi/gerror.cpp 2018-11-12 16:43:50.000000000 +0000 @@ -255,15 +255,13 @@ * class have. */ static const struct JSClassOps gjs_error_class_ops = { - NULL, /* addProperty */ - NULL, /* deleteProperty */ - NULL, /* getProperty */ - NULL, /* setProperty */ - NULL, /* enumerate */ - NULL, /* resolve */ - nullptr, /* mayResolve */ - error_finalize -}; + nullptr, // addProperty + nullptr, // deleteProperty + nullptr, // enumerate + nullptr, // newEnumerate + nullptr, // resolve + nullptr, // mayResolve + error_finalize}; struct JSClass gjs_error_class = { "GLib_Error", @@ -281,14 +279,12 @@ }; JSFunctionSpec gjs_error_proto_funcs[] = { - JS_FS("toString", error_to_string, 0, GJS_MODULE_PROP_FLAGS), - JS_FS_END -}; + JS_FN("toString", error_to_string, 0, GJS_MODULE_PROP_FLAGS), + JS_FS_END}; static JSFunctionSpec gjs_error_constructor_funcs[] = { - JS_FS("valueOf", error_constructor_value_of, 0, GJS_MODULE_PROP_FLAGS), - JS_FS_END -}; + JS_FN("valueOf", error_constructor_value_of, 0, GJS_MODULE_PROP_FLAGS), + JS_FS_END}; void gjs_define_error_class(JSContext *context, @@ -427,8 +423,6 @@ return JSProto_RangeError; case GJS_JS_ERROR_REFERENCE_ERROR: return JSProto_ReferenceError; - case GJS_JS_ERROR_STOP_ITERATION: - return JSProto_StopIteration; case GJS_JS_ERROR_SYNTAX_ERROR: return JSProto_SyntaxError; case GJS_JS_ERROR_TYPE_ERROR: diff -Nru gjs-1.53.3/gi/gobject.cpp gjs-1.54.3/gi/gobject.cpp --- gjs-1.53.3/gi/gobject.cpp 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/gi/gobject.cpp 2018-11-12 16:43:50.000000000 +0000 @@ -0,0 +1,272 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ +/* + * Copyright (c) 2008 litl, LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include + +#include + +#include "gjs/context.h" +#include "gjs/jsapi-util.h" +#include "gjs/jsapi-wrapper.h" +#include "gobject.h" +#include "object.h" +#include "value.h" + +static std::unordered_map class_init_properties; + +static JSContext* current_context(void) { + GjsContext* gjs = gjs_context_get_current(); + return static_cast(gjs_context_get_native_context(gjs)); +} + +void push_class_init_properties(GType gtype, AutoParamArray* params) { + class_init_properties[gtype] = std::move(*params); +} + +bool pop_class_init_properties(GType gtype, AutoParamArray* params_out) { + auto found = class_init_properties.find(gtype); + if (found == class_init_properties.end()) + return false; + + *params_out = std::move(found->second); + class_init_properties.erase(found); + return true; +} + +static char* hyphen_to_underscore(const char* str) { + char *s = g_strdup(str); + char *retval = s; + while (*(s++) != '\0') { + if (*s == '-') + *s = '_'; + } + return retval; +} + +static void jsobj_set_gproperty(JSContext* cx, JS::HandleObject object, + const GValue* value, GParamSpec* pspec) { + JS::RootedValue jsvalue(cx); + if (!gjs_value_from_g_value(cx, &jsvalue, value)) + return; + + GjsAutoChar underscore_name = hyphen_to_underscore(pspec->name); + if (!JS_SetProperty(cx, object, underscore_name, jsvalue)) + gjs_log_exception(cx); +} + +static void gjs_object_base_init(void* klass) { + auto* priv = ObjectPrototype::for_gtype(G_OBJECT_CLASS_TYPE(klass)); + if (priv) + priv->ref_closures(); +} + +static void gjs_object_base_finalize(void* klass) { + auto* priv = ObjectPrototype::for_gtype(G_OBJECT_CLASS_TYPE(klass)); + if (priv) + priv->unref_closures(); +} + +static GObject* gjs_object_constructor( + GType type, unsigned n_construct_properties, + GObjectConstructParam* construct_properties) { + if (!ObjectInstance::object_init_list.empty()) { + GType parent_type = g_type_parent(type); + + /* The object is being constructed from JS: + * Simply chain up to the first non-gjs constructor + */ + while (G_OBJECT_CLASS(g_type_class_peek(parent_type))->constructor == + gjs_object_constructor) + parent_type = g_type_parent(parent_type); + + return G_OBJECT_CLASS(g_type_class_peek(parent_type)) + ->constructor(type, n_construct_properties, construct_properties); + } + + /* The object is being constructed from native code (e.g. GtkBuilder): + * Construct the JS object from the constructor, then use the GObject + * that was associated in gjs_object_custom_init() + */ + JSContext *cx = current_context(); + JSAutoRequest ar(cx); + JSAutoCompartment ac(cx, gjs_get_import_global(cx)); + + JS::RootedObject constructor( + cx, gjs_lookup_object_constructor_from_info(cx, nullptr, type)); + if (!constructor) + return nullptr; + + JSObject* object; + if (n_construct_properties) { + JS::RootedObject props_hash(cx, JS_NewPlainObject(cx)); + + for (unsigned i = 0; i < n_construct_properties; i++) + jsobj_set_gproperty(cx, props_hash, construct_properties[i].value, + construct_properties[i].pspec); + + JS::AutoValueArray<1> args(cx); + args[0].set(JS::ObjectValue(*props_hash)); + object = JS_New(cx, constructor, args); + } else { + object = JS_New(cx, constructor, JS::HandleValueArray::empty()); + } + + if (!object) + return nullptr; + + auto* priv = ObjectBase::for_js_nocheck(object); + /* Should have been set in init_impl() and pushed into object_init_list, + * then popped from object_init_list in gjs_object_custom_init() */ + g_assert(priv); + /* We only hold a toggle ref at this point, add back a ref that the + * native code can own. + */ + return G_OBJECT(g_object_ref(priv->to_instance()->gobj())); +} + +static void gjs_object_set_gproperty(GObject* object, unsigned property_id, + const GValue* value, GParamSpec* pspec) { + auto* priv = ObjectInstance::for_gobject(object); + JSContext *cx = current_context(); + + JS::RootedObject js_obj(cx, priv->wrapper()); + jsobj_set_gproperty(cx, js_obj, value, pspec); +} + +static void gjs_object_get_gproperty(GObject* object, unsigned property_id, + GValue* value, GParamSpec* pspec) { + auto* priv = ObjectInstance::for_gobject(object); + JSContext *cx = current_context(); + + JS::RootedObject js_obj(cx, priv->wrapper()); + JS::RootedValue jsvalue(cx); + + GjsAutoChar underscore_name = hyphen_to_underscore(pspec->name); + if (!JS_GetProperty(cx, js_obj, underscore_name, &jsvalue) || + !gjs_value_to_g_value(cx, jsvalue, value)) + gjs_log_exception(cx); +} + +static void gjs_object_class_init(void* class_pointer, void* user_data) { + GObjectClass* klass = G_OBJECT_CLASS(class_pointer); + GType gtype = G_OBJECT_CLASS_TYPE(klass); + + klass->constructor = gjs_object_constructor; + klass->set_property = gjs_object_set_gproperty; + klass->get_property = gjs_object_get_gproperty; + + AutoParamArray properties; + if (!pop_class_init_properties(gtype, &properties)) + return; + + unsigned i = 0; + for (GjsAutoParam& pspec : properties) { + g_param_spec_set_qdata(pspec, ObjectBase::custom_property_quark(), + GINT_TO_POINTER(1)); + g_object_class_install_property(klass, ++i, pspec); + } +} + +static void gjs_object_custom_init(GTypeInstance* instance, void* klass) { + if (ObjectInstance::object_init_list.empty()) + return; + + JSContext *cx = current_context(); + + JS::RootedObject object(cx, ObjectInstance::object_init_list.top()); + auto* priv_base = ObjectBase::for_js_nocheck(object); + g_assert(priv_base); // Should have been set in init_impl() + ObjectInstance* priv = priv_base->to_instance(); + + if (priv_base->gtype() != G_TYPE_FROM_INSTANCE(instance)) { + /* This is not the most derived instance_init function, + do nothing. + */ + return; + } + + ObjectInstance::object_init_list.pop(); + + priv->associate_js_gobject(cx, object, G_OBJECT(instance)); + + /* Custom JS objects will most likely have visible state, so + * just do this from the start */ + priv->ensure_uses_toggle_ref(cx); + + JS::RootedValue v(cx); + if (!gjs_object_get_property(cx, object, GJS_STRING_INSTANCE_INIT, &v)) { + gjs_log_exception(cx); + return; + } + + if (!v.isObject()) + return; + + JS::RootedValue r(cx); + if (!JS_CallFunctionValue(cx, object, v, JS::HandleValueArray::empty(), &r)) + gjs_log_exception(cx); +} + +static void gjs_interface_init(void* g_iface, void* iface_data) { + GType gtype = G_TYPE_FROM_INTERFACE(g_iface); + + AutoParamArray properties; + if (!pop_class_init_properties(gtype, &properties)) + return; + + for (GjsAutoParam& pspec : properties) { + g_param_spec_set_qdata(pspec, ObjectBase::custom_property_quark(), + GINT_TO_POINTER(1)); + g_object_interface_install_property(g_iface, pspec); + } +} + +constexpr GTypeInfo gjs_gobject_class_info = { + 0, // class_size + + gjs_object_base_init, + gjs_object_base_finalize, + + gjs_object_class_init, + GClassFinalizeFunc(nullptr), + nullptr, // class_data + + 0, // instance_size + 0, // n_preallocs + gjs_object_custom_init, +}; + +constexpr GTypeInfo gjs_gobject_interface_info = { + sizeof(GTypeInterface), // class_size + + GBaseInitFunc(nullptr), + GBaseFinalizeFunc(nullptr), + + gjs_interface_init, + GClassFinalizeFunc(nullptr), + nullptr, // class_data + + 0, // instance_size + 0, // n_preallocs + nullptr, // instance_init +}; diff -Nru gjs-1.53.3/gi/gobject.h gjs-1.54.3/gi/gobject.h --- gjs-1.53.3/gi/gobject.h 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/gi/gobject.h 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,41 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ +/* + * Copyright (c) 2018 Philip Chimento + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef GI_GOBJECT_H_ +#define GI_GOBJECT_H_ + +#include + +#include +#include + +#include "gjs/jsapi-util.h" + +using AutoParamArray = std::vector; + +extern const GTypeInfo gjs_gobject_class_info; +extern const GTypeInfo gjs_gobject_interface_info; + +void push_class_init_properties(GType gtype, AutoParamArray* params); +bool pop_class_init_properties(GType gtype, AutoParamArray* params_out); + +#endif // GI_GOBJECT_H_ diff -Nru gjs-1.53.3/gi/gtype.cpp gjs-1.54.3/gi/gtype.cpp --- gjs-1.53.3/gi/gtype.cpp 2018-05-31 05:34:45.000000000 +0000 +++ gjs-1.54.3/gi/gtype.cpp 2018-11-12 17:04:16.000000000 +0000 @@ -63,13 +63,18 @@ void *data) { for (auto iter = weak_pointer_list.begin(); iter != weak_pointer_list.end(); ) { - auto heap_wrapper = static_cast *>(g_type_get_qdata(*iter, gjs_get_gtype_wrapper_quark())); + GType gtype = *iter; + auto heap_wrapper = static_cast *>( + g_type_get_qdata(gtype, gjs_get_gtype_wrapper_quark())); JS_UpdateWeakPointerAfterGC(heap_wrapper); /* No read barriers are needed if the only thing we are doing with the * pointer is comparing it to nullptr. */ - if (heap_wrapper->unbarrieredGet() == nullptr) + if (heap_wrapper->unbarrieredGet() == nullptr) { + g_type_set_qdata(gtype, gjs_get_gtype_wrapper_quark(), nullptr); iter = weak_pointer_list.erase(iter); + delete heap_wrapper; + } else iter++; } @@ -95,8 +100,12 @@ if (G_UNLIKELY(gtype == 0)) return; - weak_pointer_list.erase(gtype); + auto heap_wrapper = static_cast*>( + g_type_get_qdata(gtype, gjs_get_gtype_wrapper_quark())); + g_type_set_qdata(gtype, gjs_get_gtype_wrapper_quark(), NULL); + weak_pointer_list.erase(gtype); + delete heap_wrapper; } static bool @@ -145,9 +154,8 @@ /* Functions */ JSFunctionSpec gjs_gtype_proto_funcs[] = { - JS_FS("toString", to_string_func, 0, 0), - JS_FS_END -}; + JS_FN("toString", to_string_func, 0, 0), + JS_FS_END}; JSFunctionSpec gjs_gtype_static_funcs[] = { JS_FS_END }; diff -Nru gjs-1.53.3/gi/interface.cpp gjs-1.54.3/gi/interface.cpp --- gjs-1.53.3/gi/interface.cpp 2018-05-31 05:34:45.000000000 +0000 +++ gjs-1.54.3/gi/interface.cpp 2018-11-12 16:43:50.000000000 +0000 @@ -187,15 +187,13 @@ } static const struct JSClassOps gjs_interface_class_ops = { - NULL, /* addProperty */ - NULL, /* deleteProperty */ - NULL, /* getProperty */ - NULL, /* setProperty */ - NULL, /* enumerate */ + nullptr, // addProperty + nullptr, // deleteProperty + nullptr, // enumerate + nullptr, // newEnumerate interface_resolve, - nullptr, /* mayResolve */ - interface_finalize -}; + nullptr, // mayResolve + interface_finalize}; struct JSClass gjs_interface_class = { "GObject_Interface", diff -Nru gjs-1.53.3/gi/ns.cpp gjs-1.54.3/gi/ns.cpp --- gjs-1.53.3/gi/ns.cpp 2018-05-31 05:34:45.000000000 +0000 +++ gjs-1.54.3/gi/ns.cpp 2018-11-12 16:43:50.000000000 +0000 @@ -155,15 +155,13 @@ * class have. */ static const struct JSClassOps gjs_ns_class_ops = { - NULL, /* addProperty */ - NULL, /* deleteProperty */ - NULL, /* getProperty */ - NULL, /* setProperty */ - NULL, /* enumerate */ + nullptr, // addProperty + nullptr, // deleteProperty + nullptr, // enumerate + nullptr, // newEnumerate ns_resolve, - nullptr, /* mayResolve */ - ns_finalize -}; + nullptr, // mayResolve + ns_finalize}; struct JSClass gjs_ns_class = { "GIRepositoryNamespace", diff -Nru gjs-1.53.3/gi/object.cpp gjs-1.54.3/gi/object.cpp --- gjs-1.53.3/gi/object.cpp 2018-06-18 22:44:01.000000000 +0000 +++ gjs-1.54.3/gi/object.cpp 2018-11-12 17:04:24.000000000 +0000 @@ -1,6 +1,7 @@ /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 litl, LLC + * Copyright (c) 2018 Philip Chimento * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -24,8 +25,6 @@ #include #include -#include -#include #include #include #include @@ -37,7 +36,6 @@ #include "gjs/jsapi-util-args.h" #include "arg.h" #include "repo.h" -#include "gtype.h" #include "function.h" #include "proxyutils.h" #include "param.h" @@ -54,291 +52,352 @@ #include #include -typedef class GjsListLink GjsListLink; -typedef struct ObjectInstance ObjectInstance; - -static GjsListLink* object_instance_get_link(ObjectInstance *priv); +#include "js/GCHashTable.h" -class GjsListLink { - private: - ObjectInstance *m_prev; - ObjectInstance *m_next; - - public: - ObjectInstance* prev() { - return m_prev; - } +/* This is a trick to print out the sizes of the structs at compile time, in + * an error message. */ +// template struct Measure; +// Measure instance_size; +// Measure prototype_size; + +#if defined(__x86_64__) && defined(__clang__) +/* This isn't meant to be comprehensive, but should trip on at least one CI job + * if sizeof(ObjectInstance) is increased. */ +static_assert(sizeof(ObjectInstance) <= 88, + "Think very hard before increasing the size of ObjectInstance. " + "There can be tens of thousands of them alive in a typical " + "gnome-shell run."); +#endif // x86-64 clang - ObjectInstance* next() { - return m_next; - } +std::stack ObjectInstance::object_init_list{}; +static bool weak_pointer_callback = false; +ObjectInstance *ObjectInstance::wrapped_gobject_list = nullptr; +JS::PersistentRootedSymbol ObjectInstance::hook_up_vfunc_root; - void prepend(ObjectInstance *this_instance, - ObjectInstance *head) { - GjsListLink *elem = object_instance_get_link(head); +extern struct JSClass gjs_object_instance_class; +GJS_DEFINE_PRIV_FROM_JS(ObjectBase, gjs_object_instance_class) - g_assert(object_instance_get_link(this_instance) == this); +// clang-format off +G_DEFINE_QUARK(gjs::custom-type, ObjectBase::custom_type) +G_DEFINE_QUARK(gjs::custom-property, ObjectBase::custom_property) +// clang-format on - if (elem->m_prev) { - GjsListLink *prev = object_instance_get_link(elem->m_prev); - prev->m_next = this_instance; - this->m_prev = elem->m_prev; - } +static GQuark +gjs_object_priv_quark (void) +{ + static GQuark val = 0; + if (G_UNLIKELY (!val)) + val = g_quark_from_static_string ("gjs::private"); - elem->m_prev = this_instance; - this->m_next = head; - } + return val; +} - void unlink() { - if (m_prev) - object_instance_get_link(m_prev)->m_next = m_next; - if (m_next) - object_instance_get_link(m_next)->m_prev = m_prev; +/* Plain g_type_query fails and leaves @query uninitialized for + dynamic types. + See https://bugzilla.gnome.org/show_bug.cgi?id=687184 and + https://bugzilla.gnome.org/show_bug.cgi?id=687211 +*/ +void ObjectBase::type_query_dynamic_safe(GTypeQuery* query) { + GType type = gtype(); + while (g_type_get_qdata(type, ObjectBase::custom_type_quark())) + type = g_type_parent(type); - m_prev = m_next = NULL; - } + g_type_query(type, query); +} - size_t size() { - GjsListLink *elem = this; - size_t count = 0; +void +GjsListLink::prepend(ObjectInstance *this_instance, + ObjectInstance *head) +{ + GjsListLink *elem = head->get_link(); - do { - count++; - if (!elem->m_next) - break; - elem = object_instance_get_link(elem->m_next); - } while (elem); + g_assert(this_instance->get_link() == this); - return count; + if (elem->m_prev) { + GjsListLink *prev = elem->m_prev->get_link(); + prev->m_next = this_instance; + this->m_prev = elem->m_prev; } -}; - -struct ObjectInstance { - GIObjectInfo *info; - GObject *gobj; /* NULL if we are the prototype and not an instance */ - GjsMaybeOwned keep_alive; - GType gtype; - - /* a list of all GClosures installed on this object (from - * signals, trampolines and explicit GClosures), used when tracing */ - std::set closures; - - /* the GObjectClass wrapped by this JS Object (only used for - prototypes) */ - GTypeClass *klass; - - GjsListLink instance_link; - - unsigned js_object_finalized : 1; - unsigned g_object_finalized : 1; - - /* True if this object has visible JS state, and thus its lifecycle is - * managed using toggle references. False if this object just keeps a - * hard ref on the underlying GObject, and may be finalized at will. */ - bool uses_toggle_ref : 1; -}; - -static std::stack object_init_list; -using ParamRef = std::unique_ptr; -using ParamRefArray = std::vector; -static std::unordered_map class_init_properties; - -static bool weak_pointer_callback = false; -ObjectInstance *wrapped_gobject_list; - -extern struct JSClass gjs_object_instance_class; -GJS_DEFINE_PRIV_FROM_JS(ObjectInstance, gjs_object_instance_class) - -static void disassociate_js_gobject(ObjectInstance *priv); -static void ensure_uses_toggle_ref(JSContext *cx, ObjectInstance *priv); - -typedef enum { - SOME_ERROR_OCCURRED = false, - NO_SUCH_G_PROPERTY, - VALUE_WAS_SET -} ValueFromPropertyResult; + elem->m_prev = this_instance; + this->m_next = head; +} -static GQuark -gjs_is_custom_type_quark (void) +void +GjsListLink::unlink(void) { - static GQuark val = 0; - if (!val) - val = g_quark_from_static_string ("gjs::custom-type"); + if (m_prev) + m_prev->get_link()->m_next = m_next; + if (m_next) + m_next->get_link()->m_prev = m_prev; - return val; + m_prev = m_next = nullptr; } -static GQuark -gjs_is_custom_property_quark (void) +size_t +GjsListLink::size(void) const { - static GQuark val = 0; - if (!val) - val = g_quark_from_static_string ("gjs::custom-property"); + const GjsListLink *elem = this; + size_t count = 0; - return val; + do { + count++; + if (!elem->m_next) + break; + elem = elem->m_next->get_link(); + } while (elem); + + return count; } -static GQuark -gjs_object_priv_quark (void) -{ - static GQuark val = 0; - if (G_UNLIKELY (!val)) - val = g_quark_from_static_string ("gjs::private"); +void ObjectInstance::link(void) { + if (wrapped_gobject_list) + m_instance_link.prepend(this, wrapped_gobject_list); + wrapped_gobject_list = this; +} - return val; +void ObjectInstance::unlink(void) { + if (wrapped_gobject_list == this) + wrapped_gobject_list = m_instance_link.next(); + m_instance_link.unlink(); } -/* Plain g_type_query fails and leaves @query uninitialized for - dynamic types. - See https://bugzilla.gnome.org/show_bug.cgi?id=687184 and - https://bugzilla.gnome.org/show_bug.cgi?id=687211 -*/ -static void -g_type_query_dynamic_safe (GType type, - GTypeQuery *query) -{ - while (g_type_get_qdata(type, gjs_is_custom_type_quark())) - type = g_type_parent(type); +GIObjectInfo* ObjectBase::info(void) const { return get_prototype()->m_info; } - g_type_query(type, query); +GType ObjectBase::gtype(void) const { return get_prototype()->m_gtype; } + +const void* ObjectBase::gobj_addr(void) const { + if (is_prototype()) + return nullptr; + return to_instance()->m_gobj; } -static void +const void* ObjectBase::jsobj_addr(void) const { + if (is_prototype()) + return nullptr; + return to_instance()->m_wrapper.get(); +} + +static bool throw_priv_is_null_error(JSContext *context) { gjs_throw(context, "This JS object wrapper isn't wrapping a GObject." " If this is a custom subclass, are you sure you chained" " up to the parent _init properly?"); + return false; +} + +bool ObjectBase::check_is_instance(JSContext* cx, const char* for_what) const { + if (!is_prototype()) + return true; + const ObjectPrototype* priv = to_prototype(); + gjs_throw(cx, "Can't %s on %s.%s.prototype; only on instances", for_what, + priv->ns(), priv->name()); + return false; +} + +bool ObjectInstance::check_gobject_disposed(const char* for_what) const { + if (!m_gobj_disposed) + return true; + + g_critical( + "Object %s.%s (%p), has been already deallocated — impossible to %s " + "it. This might be caused by the object having been destroyed from C " + "code using something such as destroy(), dispose(), or remove() " + "vfuncs.", + ns(), name(), m_gobj, for_what); + gjs_dumpstack(); + return false; +} + +/* Gets the ObjectBase belonging to a particular JS object wrapper. Checks + * that the wrapper object has the right JSClass (ObjectInstance::klass) + * and returns null if not. */ +ObjectBase* ObjectBase::for_js(JSContext* cx, JS::HandleObject wrapper) { + return priv_from_js(cx, wrapper); +} + +/* Use when you don't have a JSContext* available. This method is infallible + * and cannot trigger a GC, so it's safe to use from finalize() and trace(). + * (It can return null if no instance has been set yet.) */ +ObjectBase* ObjectBase::for_js_nocheck(JSObject* wrapper) { + return static_cast(JS_GetPrivate(wrapper)); } -static ObjectInstance * -get_object_qdata(GObject *gobj) +ObjectInstance * +ObjectInstance::for_gobject(GObject *gobj) { auto priv = static_cast(g_object_get_qdata(gobj, gjs_object_priv_quark())); - if (priv && priv->uses_toggle_ref && G_UNLIKELY(priv->js_object_finalized)) { + if (priv) + priv->check_js_object_finalized(); + + return priv; +} + +void +ObjectInstance::check_js_object_finalized(void) +{ + if (!m_uses_toggle_ref) + return; + if (G_UNLIKELY(m_wrapper_finalized)) { g_critical("Object %p (a %s) resurfaced after the JS wrapper was finalized. " "This is some library doing dubious memory management inside dispose()", - gobj, g_type_name(G_TYPE_FROM_INSTANCE(gobj))); - priv->js_object_finalized = false; - g_assert(!priv->keep_alive); /* should associate again with a new wrapper */ + m_gobj, type_name()); + m_wrapper_finalized = false; + g_assert(!m_wrapper); /* should associate again with a new wrapper */ } +} - return priv; +ObjectPrototype* ObjectPrototype::for_gtype(GType gtype) { + return static_cast( + g_type_get_qdata(gtype, gjs_object_priv_quark())); } -static void -set_object_qdata(GObject *gobj, - ObjectInstance *priv) +void ObjectPrototype::set_type_qdata(void) { + g_type_set_qdata(m_gtype, gjs_object_priv_quark(), this); +} + +void +ObjectInstance::set_object_qdata(void) { - g_object_set_qdata(gobj, gjs_object_priv_quark(), priv); + g_object_set_qdata(m_gobj, gjs_object_priv_quark(), this); } -static ValueFromPropertyResult -init_g_param_from_property(JSContext *context, - const char *js_prop_name, - JS::HandleValue value, - GType gtype, - GParameter *parameter, - bool constructing) +void +ObjectInstance::unset_object_qdata(void) { + g_object_set_qdata(m_gobj, gjs_object_priv_quark(), nullptr); +} + +GParamSpec* ObjectPrototype::find_param_spec_from_id(JSContext* cx, + JS::HandleString key) { char *gname; - GParamSpec *param_spec; - void *klass; - gname = gjs_hyphen_from_camel(js_prop_name); - gjs_debug_jsprop(GJS_DEBUG_GOBJECT, - "Hyphen name %s on %s", gname, g_type_name(gtype)); + /* First check for the ID in the cache */ + auto entry = m_property_cache.lookupForAdd(key); + if (entry) + return entry->value().get(); + + GjsAutoJSChar js_prop_name; + if (!gjs_string_to_utf8(cx, JS::StringValue(key), &js_prop_name)) + return nullptr; - klass = g_type_class_ref(gtype); - param_spec = g_object_class_find_property(G_OBJECT_CLASS(klass), - gname); - g_type_class_unref(klass); + gname = gjs_hyphen_from_camel(js_prop_name); + GObjectClass *gobj_class = G_OBJECT_CLASS(g_type_class_ref(m_gtype)); + GParamSpec* pspec = g_object_class_find_property(gobj_class, gname); + GjsAutoParam param_spec(pspec, GjsAutoParam::TakeOwnership()); + g_type_class_unref(gobj_class); g_free(gname); - if (param_spec == NULL) { - /* not a GObject prop, so nothing else to do */ - return NO_SUCH_G_PROPERTY; + if (!param_spec) { + _gjs_proxy_throw_nonexistent_field(cx, m_gtype, js_prop_name); + return nullptr; } - /* Do not set JS overridden properties through GObject, to avoid - * infinite recursion (but set them when constructing) */ - if (!constructing && - g_param_spec_get_qdata(param_spec, gjs_is_custom_property_quark())) - return NO_SUCH_G_PROPERTY; + if (!m_property_cache.add(entry, key, std::move(param_spec))) + g_error("Out of memory adding param spec to cache"); + return entry->value().get(); /* owned by property cache */ +} + +/* Gets the ObjectPrototype corresponding to obj.prototype. Cannot return null, + * and asserts so. */ +ObjectPrototype* ObjectPrototype::for_js_prototype(JSContext* context, + JS::HandleObject obj) { + JS::RootedObject proto(context); + JS_GetPrototype(context, obj, &proto); + ObjectBase* retval = ObjectBase::for_js(context, proto); + g_assert(retval); + return retval->to_prototype(); +} +/* A hook on adding a property to an object. This is called during a set + * property operation after all the resolve hooks on the prototype chain have + * failed to resolve. We use this to mark an object as needing toggle refs when + * custom state is set on it, because we need to keep the JS GObject wrapper + * alive in order not to lose custom "expando" properties. + */ +bool ObjectBase::add_property(JSContext* cx, JS::HandleObject obj, + JS::HandleId id, JS::HandleValue value) { + auto* priv = ObjectBase::for_js(cx, obj); - if ((param_spec->flags & G_PARAM_WRITABLE) == 0) { - /* prevent setting the prop even in JS */ - gjs_throw(context, "Property %s (GObject %s) is not writable", - js_prop_name, param_spec->name); - return SOME_ERROR_OCCURRED; + /* priv is null during init: property is not being added from JS */ + if (!priv) { + debug_jsprop_static("Add property hook", id, obj); + return true; } + if (priv->is_prototype()) + return true; - gjs_debug_jsprop(GJS_DEBUG_GOBJECT, - "Syncing %s to GObject prop %s", - js_prop_name, param_spec->name); + return priv->to_instance()->add_property_impl(cx, obj, id, value); +} - g_value_init(¶meter->value, G_PARAM_SPEC_VALUE_TYPE(param_spec)); - if (!gjs_value_to_g_value(context, value, ¶meter->value)) { - g_value_unset(¶meter->value); - return SOME_ERROR_OCCURRED; - } +bool +ObjectInstance::add_property_impl(JSContext *cx, + JS::HandleObject obj, + JS::HandleId id, + JS::HandleValue value) +{ + debug_jsprop("Add property hook", id, obj); - parameter->name = param_spec->name; + if (is_custom_js_class() || m_gobj_disposed) + return true; - return VALUE_WAS_SET; + ensure_uses_toggle_ref(cx); + return true; } -static inline ObjectInstance * -proto_priv_from_js(JSContext *context, - JS::HandleObject obj) -{ - JS::RootedObject proto(context); - JS_GetPrototype(context, obj, &proto); - return priv_from_js(context, proto); +bool ObjectBase::prop_getter(JSContext* cx, unsigned argc, JS::Value* vp) { + GJS_GET_PRIV(cx, argc, vp, args, obj, ObjectBase, priv); + + JS::RootedString name(cx, + gjs_dynamic_property_private_slot(&args.callee()).toString()); + + priv->debug_jsprop("Property getter", name, obj); + + if (priv->is_prototype()) + return true; + /* Ignore silently; note that this is different from what we do for + * boxed types, for historical reasons */ + + return priv->to_instance()->prop_getter_impl(cx, obj, name, args.rval()); } -static bool -get_prop_from_g_param(JSContext *context, - JS::HandleObject obj, - ObjectInstance *priv, - const char *name, - JS::MutableHandleValue value_p) +bool +ObjectInstance::prop_getter_impl(JSContext *cx, + JS::HandleObject obj, + JS::HandleString name, + JS::MutableHandleValue rval) { - char *gname; - GParamSpec *param; + if (!check_gobject_disposed("get any property from")) + return true; + GValue gvalue = { 0, }; - gname = gjs_hyphen_from_camel(name); - param = g_object_class_find_property(G_OBJECT_GET_CLASS(priv->gobj), - gname); - g_free(gname); + auto* proto_priv = ObjectBase::for_js(cx, obj)->get_prototype(); + GParamSpec *param = proto_priv->find_param_spec_from_id(cx, name); - if (param == NULL) { - /* leave value_p as it was */ - return true; - } + /* This is guaranteed because we resolved the property before */ + g_assert(param); /* Do not fetch JS overridden properties from GObject, to avoid * infinite recursion. */ - if (g_param_spec_get_qdata(param, gjs_is_custom_property_quark())) + if (g_param_spec_get_qdata(param, ObjectInstance::custom_property_quark())) return true; - if ((param->flags & G_PARAM_READABLE) == 0) + if ((param->flags & G_PARAM_READABLE) == 0) { + rval.setUndefined(); return true; + } - gjs_debug_jsprop(GJS_DEBUG_GOBJECT, - "Overriding %s with GObject prop %s", - name, param->name); + gjs_debug_jsprop(GJS_DEBUG_GOBJECT, "Accessing GObject property %s", + param->name); g_value_init(&gvalue, G_PARAM_SPEC_VALUE_TYPE(param)); - g_object_get_property(priv->gobj, param->name, - &gvalue); - if (!gjs_value_from_g_value(context, value_p, &gvalue)) { + g_object_get_property(m_gobj, param->name, &gvalue); + if (!gjs_value_from_g_value(cx, rval, &gvalue)) { g_value_unset(&gvalue); return false; } @@ -347,57 +406,87 @@ return true; } -static GIFieldInfo * +static GjsAutoInfo lookup_field_info(GIObjectInfo *info, const char *name) { int n_fields = g_object_info_get_n_fields(info); int ix; - GIFieldInfo *retval = NULL; + GjsAutoInfo retval; for (ix = 0; ix < n_fields; ix++) { retval = g_object_info_get_field(info, ix); - const char *field_name = g_base_info_get_name((GIBaseInfo *) retval); - if (strcmp(name, field_name) == 0) + if (strcmp(name, retval.name()) == 0) break; - g_clear_pointer(&retval, g_base_info_unref); + retval.reset(); } - if (!retval) + if (!retval || !(g_field_info_get_flags(retval) & GI_FIELD_IS_READABLE)) + return nullptr; + + return retval; +} + +GIFieldInfo* ObjectPrototype::find_field_info_from_id(JSContext* cx, + JS::HandleString key) { + /* First check for the ID in the cache */ + auto entry = m_field_cache.lookupForAdd(key); + if (entry) + return entry->value().get(); + + GjsAutoJSChar js_prop_name; + if (!gjs_string_to_utf8(cx, JS::StringValue(key), &js_prop_name)) return nullptr; - if (!(g_field_info_get_flags(retval) & GI_FIELD_IS_READABLE)) { - g_base_info_unref(retval); + GjsAutoInfo field = lookup_field_info(m_info, js_prop_name); + + if (!field) { + _gjs_proxy_throw_nonexistent_field(cx, m_gtype, js_prop_name); return nullptr; } - return retval; + if (!m_field_cache.add(entry, key, std::move(field))) + g_error("Out of memory adding field info to cache"); + return entry->value().get(); /* owned by field cache */ } -static bool -get_prop_from_field(JSContext *cx, - JS::HandleObject obj, - ObjectInstance *priv, - const char *name, - JS::MutableHandleValue value_p) -{ - if (priv->info == NULL) - return true; /* Not resolved, but no error; leave value_p untouched */ +bool ObjectBase::field_getter(JSContext* cx, unsigned argc, JS::Value* vp) { + GJS_GET_PRIV(cx, argc, vp, args, obj, ObjectBase, priv); - GIFieldInfo *field = lookup_field_info(priv->info, name); + JS::RootedString name(cx, + gjs_dynamic_property_private_slot(&args.callee()).toString()); - if (field == NULL) + priv->debug_jsprop("Field getter", name, obj); + + if (priv->is_prototype()) + return true; + /* Ignore silently; note that this is different from what we do for + * boxed types, for historical reasons */ + + return priv->to_instance()->field_getter_impl(cx, obj, name, args.rval()); +} + +bool +ObjectInstance::field_getter_impl(JSContext *cx, + JS::HandleObject obj, + JS::HandleString name, + JS::MutableHandleValue rval) +{ + if (!check_gobject_disposed("get any property from")) return true; - bool retval = true; - GITypeInfo *type = NULL; + auto* proto_priv = ObjectInstance::for_js(cx, obj)->get_prototype(); + GIFieldInfo *field = proto_priv->find_field_info_from_id(cx, name); + /* This is guaranteed because we resolved the property before */ + g_assert(field); + GITypeTag tag; GIArgument arg = { 0 }; gjs_debug_jsprop(GJS_DEBUG_GOBJECT, "Overriding %s with GObject field", - name); + gjs_debug_string(name).c_str()); - type = g_field_info_get_type(field); + GjsAutoInfo type = g_field_info_get_type(field); tag = g_type_info_get_tag(type); if (tag == GI_TYPE_TAG_ARRAY || tag == GI_TYPE_TAG_INTERFACE || @@ -406,216 +495,137 @@ tag == GI_TYPE_TAG_GHASH || tag == GI_TYPE_TAG_ERROR) { gjs_throw(cx, "Can't get field %s; GObject introspection supports only " - "fields with simple types, not %s", name, - g_type_tag_to_string(tag)); - retval = false; - goto out; + "fields with simple types, not %s", + gjs_debug_string(name).c_str(), g_type_tag_to_string(tag)); + return false; } - retval = g_field_info_get_field(field, priv->gobj, &arg); - if (!retval) { - gjs_throw(cx, "Error getting field %s from object", name); - goto out; + if (!g_field_info_get_field(field, m_gobj, &arg)) { + gjs_throw(cx, "Error getting field %s from object", + gjs_debug_string(name).c_str()); + return false; } - retval = gjs_value_from_g_argument(cx, value_p, type, &arg, true); + return gjs_value_from_g_argument(cx, rval, type, &arg, true); /* copy_structs is irrelevant because g_field_info_get_field() doesn't * handle boxed types */ - -out: - if (type != NULL) - g_base_info_unref((GIBaseInfo *) type); - g_base_info_unref((GIBaseInfo *) field); - return retval; } -/* a hook on getting a property; set value_p to override property's value. - * Return value is false on OOM/exception. - */ -static bool -object_instance_get_prop(JSContext *context, - JS::HandleObject obj, - JS::HandleId id, - JS::MutableHandleValue value_p) -{ - ObjectInstance *priv = priv_from_js(context, obj); - gjs_debug_jsprop(GJS_DEBUG_GOBJECT, - "Get prop '%s' hook, obj %s, priv %p", - gjs_debug_id(id).c_str(), gjs_debug_object(obj).c_str(), priv); - - if (priv == nullptr) - /* If we reach this point, either object_instance_new_resolve - * did not throw (so name == "_init"), or the property actually - * exists and it's not something we should be concerned with */ - return true; - - if (priv->gobj == NULL) /* prototype, not an instance. */ - return true; - - if (priv->g_object_finalized) { - g_critical("Object %s.%s (%p), has been already finalized. " - "Impossible to get any property from it.", - priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "", - priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype), - priv->gobj); - gjs_dumpstack(); - return true; - } +/* Dynamic setter for GObject properties. Returns false on OOM/exception. + * args.rval() becomes the "stored value" for the property. */ +bool ObjectBase::prop_setter(JSContext* cx, unsigned argc, JS::Value* vp) { + GJS_GET_PRIV(cx, argc, vp, args, obj, ObjectBase, priv); - GjsAutoJSChar name; - if (!gjs_get_string_id(context, id, &name)) - return true; /* not resolved, but no error */ + JS::RootedString name(cx, + gjs_dynamic_property_private_slot(&args.callee()).toString()); - if (!get_prop_from_g_param(context, obj, priv, name, value_p)) - return false; + priv->debug_jsprop("Property setter", name, obj); - if (!value_p.isUndefined()) + if (priv->is_prototype()) return true; + /* Ignore silently; note that this is different from what we do for + * boxed types, for historical reasons */ - /* Fall back to fields */ - return get_prop_from_field(context, obj, priv, name, value_p); -} + /* Clear the JS stored value, to avoid keeping additional references */ + args.rval().setUndefined(); -static bool -set_g_param_from_prop(JSContext *context, - ObjectInstance *priv, - const char *name, - bool& was_set, - JS::HandleValue value_p, - JS::ObjectOpResult& result) -{ - GParameter param = { NULL, { 0, }}; - was_set = false; - - switch (init_g_param_from_property(context, name, - value_p, - G_TYPE_FROM_INSTANCE(priv->gobj), - ¶m, - false /* constructing */)) { - case SOME_ERROR_OCCURRED: - return false; - case NO_SUCH_G_PROPERTY: - /* We need to keep the wrapper alive in order not to lose custom - * "expando" properties */ - ensure_uses_toggle_ref(context, priv); - return result.succeed(); - case VALUE_WAS_SET: - default: - break; - } - - g_object_set_property(priv->gobj, param.name, - ¶m.value); - - g_value_unset(¶m.value); - was_set = true; - return result.succeed(); + return priv->to_instance()->prop_setter_impl(cx, obj, name, args[0]); } -static bool -check_set_field_from_prop(JSContext *cx, - ObjectInstance *priv, - const char *name, - JS::MutableHandleValue value_p, - JS::ObjectOpResult& result) +bool +ObjectInstance::prop_setter_impl(JSContext *cx, + JS::HandleObject obj, + JS::HandleString name, + JS::HandleValue value) { - if (priv->info == NULL) - return result.succeed(); + if (!check_gobject_disposed("set any property on")) + return true; - GIFieldInfo *field = lookup_field_info(priv->info, name); - if (field == NULL) - return result.succeed(); + auto* proto_priv = ObjectInstance::for_js(cx, obj)->get_prototype(); + GParamSpec *param_spec = proto_priv->find_param_spec_from_id(cx, name); + if (!param_spec) + return false; - bool retval = true; + /* Do not set JS overridden properties through GObject, to avoid + * infinite recursion (unless constructing) */ + if (g_param_spec_get_qdata(param_spec, + ObjectInstance::custom_property_quark())) + return true; - /* As far as I know, GI never exposes GObject instance struct fields as - * writable, so no need to implement this for the time being */ - if (g_field_info_get_flags(field) & GI_FIELD_IS_WRITABLE) { - g_message("Field %s of a GObject is writable, but setting it is not " - "implemented", name); - result.succeed(); - goto out; + if (!(param_spec->flags & G_PARAM_WRITABLE)) + /* prevent setting the prop even in JS */ + return _gjs_proxy_throw_readonly_field(cx, gtype(), param_spec->name); + + gjs_debug_jsprop(GJS_DEBUG_GOBJECT, "Setting GObject prop %s", + param_spec->name); + + GValue gvalue = G_VALUE_INIT; + g_value_init(&gvalue, G_PARAM_SPEC_VALUE_TYPE(param_spec)); + if (!gjs_value_to_g_value(cx, value, &gvalue)) { + g_value_unset(&gvalue); + return false; } - result.failReadOnly(); /* still return true; error only in strict mode */ + g_object_set_property(m_gobj, param_spec->name, &gvalue); + g_value_unset(&gvalue); + + return true; +} + +bool ObjectBase::field_setter(JSContext* cx, unsigned argc, JS::Value* vp) { + GJS_GET_PRIV(cx, argc, vp, args, obj, ObjectBase, priv); + + JS::RootedString name(cx, + gjs_dynamic_property_private_slot(&args.callee()).toString()); - /* We have to update value_p because JS caches it as the property's "stored + priv->debug_jsprop("Field setter", name, obj); + + if (priv->is_prototype()) + return true; + /* Ignore silently; note that this is different from what we do for + * boxed types, for historical reasons */ + + /* We have to update args.rval(), because JS caches it as the property's "stored * value" (https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/JSAPI_reference/Stored_value) * and so subsequent gets would get the stored value instead of accessing * the field */ - value_p.setUndefined(); -out: - g_base_info_unref((GIBaseInfo *) field); - return retval; + args.rval().setUndefined(); + + return priv->to_instance()->field_setter_impl(cx, obj, name, args[0]); } -/* a hook on setting a property; set value_p to override property value to - * be set. Return value is false on OOM/exception. - */ -static bool -object_instance_set_prop(JSContext *context, - JS::HandleObject obj, - JS::HandleId id, - JS::MutableHandleValue value_p, - JS::ObjectOpResult& result) -{ - ObjectInstance *priv; - bool ret = true; - bool g_param_was_set = false; - - priv = priv_from_js(context, obj); - - gjs_debug_jsprop(GJS_DEBUG_GOBJECT, "Set prop '%s' hook, obj %s, priv %p", - gjs_debug_id(id).c_str(), gjs_debug_object(obj).c_str(), - priv); - - if (priv == nullptr) - /* see the comment in object_instance_get_prop() on this */ - return result.succeed(); - - if (priv->gobj == NULL) /* prototype, not an instance. */ - return result.succeed(); - - if (priv->g_object_finalized) { - g_critical("Object %s.%s (%p), has been already finalized. " - "Impossible to set any property to it.", - priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "", - priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype), - priv->gobj); - gjs_dumpstack(); - return result.succeed(); - } +bool +ObjectInstance::field_setter_impl(JSContext *cx, + JS::HandleObject obj, + JS::HandleString name, + JS::HandleValue value) +{ + if (!check_gobject_disposed("set GObject field on")) + return true; - GjsAutoJSChar name; - if (!gjs_get_string_id(context, id, &name)) { - /* We need to keep the wrapper alive in order not to lose custom - * "expando" properties. In this case if gjs_get_string_id() is false - * then a number or symbol property was probably set. */ - ensure_uses_toggle_ref(context, priv); - return result.succeed(); /* not resolved, but no error */ + auto* proto_priv = ObjectInstance::for_js(cx, obj)->get_prototype(); + GIFieldInfo *field = proto_priv->find_field_info_from_id(cx, name); + if (field == NULL) + return false; + + /* As far as I know, GI never exposes GObject instance struct fields as + * writable, so no need to implement this for the time being */ + if (g_field_info_get_flags(field) & GI_FIELD_IS_WRITABLE) { + g_message("Field %s of a GObject is writable, but setting it is not " + "implemented", gjs_debug_string(name).c_str()); + return true; } - ret = set_g_param_from_prop(context, priv, name, g_param_was_set, value_p, result); - if (g_param_was_set || !ret) - return ret; - - /* note that the prop will also have been set in JS, which I think - * is OK, since we hook get and set so will always override that - * value. We could also use JS_DefineProperty though and specify a - * getter/setter maybe, don't know if that is better. - */ - return check_set_field_from_prop(context, priv, name, value_p, result); + return _gjs_proxy_throw_readonly_field(cx, gtype(), + g_base_info_get_name(field)); } -static bool -is_vfunc_unchanged(GIVFuncInfo *info, - GType gtype) -{ - GType ptype = g_type_parent(gtype); +bool ObjectPrototype::is_vfunc_unchanged(GIVFuncInfo* info) { + GType ptype = g_type_parent(m_gtype); GError *error = NULL; gpointer addr1, addr2; - addr1 = g_vfunc_info_get_address(info, gtype, &error); + addr1 = g_vfunc_info_get_address(info, m_gtype, &error); if (error) { g_clear_error(&error); return false; @@ -630,91 +640,142 @@ return addr1 == addr2; } -static GIVFuncInfo * +static GjsAutoInfo find_vfunc_on_parents(GIObjectInfo *info, const char *name, bool *out_defined_by_parent) { - GIVFuncInfo *vfunc = NULL; - GIObjectInfo *parent; bool defined_by_parent = false; /* ref the first info so that we don't destroy * it when unrefing parents later */ - g_base_info_ref(info); - parent = info; + GjsAutoInfo parent = g_base_info_ref(info); /* Since it isn't possible to override a vfunc on * an interface without reimplementing it, we don't need * to search the parent types when looking for a vfunc. */ - vfunc = g_object_info_find_vfunc_using_interfaces(parent, name, NULL); + GjsAutoInfo vfunc = + g_object_info_find_vfunc_using_interfaces(parent, name, nullptr); while (!vfunc && parent) { - GIObjectInfo *tmp = parent; - parent = g_object_info_get_parent(tmp); - g_base_info_unref(tmp); + parent = g_object_info_get_parent(parent); if (parent) vfunc = g_object_info_find_vfunc(parent, name); defined_by_parent = true; } - if (parent) - g_base_info_unref(parent); - if (out_defined_by_parent) *out_defined_by_parent = defined_by_parent; return vfunc; } -static bool -object_instance_resolve_no_info(JSContext *context, - JS::HandleObject obj, - bool *resolved, - ObjectInstance *priv, - const char *name) -{ - GIFunctionInfo *method_info; - guint n_interfaces; - guint i; +/* Taken from GLib */ +static void canonicalize_key(char* key) { + for (char* p = key; *p != 0; p++) { + char c = *p; - GType *interfaces = g_type_interfaces(priv->gtype, &n_interfaces); - for (i = 0; i < n_interfaces; i++) { - GIBaseInfo *base_info; - GIInterfaceInfo *iface_info; + if (c != '-' && (c < '0' || c > '9') && (c < 'A' || c > 'Z') && + (c < 'a' || c > 'z')) + *p = '-'; + } +} - base_info = g_irepository_find_by_gtype(g_irepository_get_default(), - interfaces[i]); +/* @name must already be canonicalized */ +static bool is_ginterface_property_name(GIInterfaceInfo* info, + const char* name) { + int n_props = g_interface_info_get_n_properties(info); + GjsAutoInfo prop_info; + + for (int ix = 0; ix < n_props; ix++) { + prop_info = g_interface_info_get_property(info, ix); + if (strcmp(name, prop_info.name()) == 0) + break; + prop_info.reset(); + } - if (base_info == NULL) - continue; + return !!prop_info; +} - /* An interface GType ought to have interface introspection info */ - g_assert (g_base_info_get_type(base_info) == GI_INFO_TYPE_INTERFACE); +bool ObjectPrototype::lazy_define_gobject_property(JSContext* cx, + JS::HandleObject obj, + JS::HandleId id, + bool* resolved, + const char* name) { + bool found = false; + if (!JS_AlreadyHasOwnPropertyById(cx, obj, id, &found)) + return false; + if (found) { + /* Already defined, so *resolved = false because we didn't just + * define it */ + *resolved = false; + return true; + } + + debug_jsprop("Defining lazy GObject property", id, obj); + + JS::RootedValue private_id(cx, JS::StringValue(JSID_TO_STRING(id))); + if (!gjs_define_property_dynamic( + cx, obj, name, "gobject_prop", &ObjectBase::prop_getter, + &ObjectBase::prop_setter, private_id, + // Make property configurable so that interface properties can be + // overridden by GObject.ParamSpec.override in the class that + // implements them + GJS_MODULE_PROP_FLAGS & ~JSPROP_PERMANENT)) + return false; - iface_info = (GIInterfaceInfo*) base_info; + *resolved = true; + return true; +} + +bool ObjectPrototype::resolve_no_info(JSContext* cx, JS::HandleObject obj, + JS::HandleId id, bool* resolved, + const char* name, + ResolveWhat resolve_props) { + guint n_interfaces; + guint i; - method_info = g_interface_info_find_method(iface_info, name); + GjsAutoChar canonical_name; + if (resolve_props == ConsiderMethodsAndProperties) { + char* canonical_name_unowned = gjs_hyphen_from_camel(name); + canonicalize_key(canonical_name_unowned); + canonical_name.reset(canonical_name_unowned); + } - g_base_info_unref(base_info); + GType *interfaces = g_type_interfaces(m_gtype, &n_interfaces); + for (i = 0; i < n_interfaces; i++) { + GjsAutoInfo iface_info = + g_irepository_find_by_gtype(nullptr, interfaces[i]); + if (!iface_info) + continue; + /* An interface GType ought to have interface introspection info */ + g_assert(iface_info.type() == GI_INFO_TYPE_INTERFACE); + GjsAutoInfo method_info = + g_interface_info_find_method(iface_info, name); if (method_info != NULL) { if (g_function_info_get_flags (method_info) & GI_FUNCTION_IS_METHOD) { - if (!gjs_define_function(context, obj, priv->gtype, - (GICallableInfo *)method_info)) { - g_base_info_unref((GIBaseInfo*) method_info); + if (!gjs_define_function(cx, obj, m_gtype, method_info)) { g_free(interfaces); return false; } - g_base_info_unref((GIBaseInfo*) method_info); *resolved = true; g_free(interfaces); return true; } + } + + if (resolve_props == ConsiderOnlyMethods) + continue; - g_base_info_unref( (GIBaseInfo*) method_info); + /* If the name refers to a GObject property, lazily define the property + * in JS as we do below in the real resolve hook. We ignore fields here + * because I don't think interfaces can have fields */ + if (is_ginterface_property_name(iface_info, canonical_name)) { + g_free(interfaces); + return lazy_define_gobject_property(cx, obj, id, resolved, name); } } @@ -723,87 +784,49 @@ return true; } -/* Taken from GLib */ -static void -canonicalize_key(char *key) +static bool +is_gobject_property_name(GIObjectInfo *info, + const char *name) { - for (char *p = key; *p != 0; p++) { - char c = *p; - - if (c != '-' && - (c < '0' || c > '9') && - (c < 'A' || c > 'Z') && - (c < 'a' || c > 'z')) - *p = '-'; - } -} - -static bool -is_gobject_property_name(GIObjectInfo *info, - const char *name) -{ - int n_props = g_object_info_get_n_properties(info); - int ix; - GIPropertyInfo *prop_info = nullptr; + int n_props = g_object_info_get_n_properties(info); + int n_ifaces = g_object_info_get_n_interfaces(info); + int ix; + GjsAutoInfo prop_info; char *canonical_name = gjs_hyphen_from_camel(name); canonicalize_key(canonical_name); for (ix = 0; ix < n_props; ix++) { prop_info = g_object_info_get_property(info, ix); - const char *prop_name = g_base_info_get_name(prop_info); - if (strcmp(canonical_name, prop_name) == 0) + if (strcmp(canonical_name, prop_info.name()) == 0) break; - g_clear_pointer(&prop_info, g_base_info_unref); + prop_info.reset(); } - g_free(canonical_name); - - if (!prop_info) - return false; + if (!prop_info) { + for (ix = 0; ix < n_ifaces; ix++) { + GjsAutoInfo iface_info = + g_object_info_get_interface(info, ix); + if (is_ginterface_property_name(iface_info, canonical_name)) { + g_free(canonical_name); + return true; + } + } - if (!(g_property_info_get_flags(prop_info) & G_PARAM_READABLE)) { - g_base_info_unref(prop_info); + g_free(canonical_name); return false; } - g_base_info_unref(prop_info); - return true; -} + g_free(canonical_name); -static bool -is_gobject_field_name(GIObjectInfo *info, - const char *name) -{ - GIFieldInfo *field_info = lookup_field_info(info, name); - if (!field_info) - return false; - g_base_info_unref(field_info); return true; } -/* The *resolved out parameter, on success, should be false to indicate that id - * was not resolved; and true if id was resolved. */ -static bool -object_instance_resolve(JSContext *context, - JS::HandleObject obj, - JS::HandleId id, - bool *resolved) -{ - GIFunctionInfo *method_info; - ObjectInstance *priv = priv_from_js(context, obj); - - gjs_debug_jsprop(GJS_DEBUG_GOBJECT, - "Resolve prop '%s' hook, obj %s, priv %p (%s.%s), gobj %p %s", - gjs_debug_id(id).c_str(), - gjs_debug_object(obj).c_str(), - priv, - priv && priv->info ? g_base_info_get_namespace (priv->info) : "", - priv && priv->info ? g_base_info_get_name (priv->info) : "", - priv ? priv->gobj : NULL, - (priv && priv->gobj) ? g_type_name_from_instance((GTypeInstance*) priv->gobj) : "(type unknown)"); +bool ObjectBase::resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, + bool* resolved) { + auto* priv = ObjectBase::for_js(cx, obj); - if (priv == NULL) { + if (!priv) { /* We won't have a private until the initializer is called, so * just defer to prototype chains in this case. * @@ -812,25 +835,22 @@ * will run afterwards will fail because of the "priv == NULL" * check there. */ + debug_jsprop_static("Resolve hook", id, obj); *resolved = false; return true; } - if (priv->gobj != NULL) { + if (!priv->is_prototype()) { *resolved = false; return true; } - if (priv->g_object_finalized) { - g_critical("Object %s.%s (%p), has been already finalized. " - "Impossible to resolve it.", - priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "", - priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype), - priv->gobj); - gjs_dumpstack(); - *resolved = false; - return true; - } + return priv->to_prototype()->resolve_impl(cx, obj, id, resolved); +} + +bool ObjectPrototype::resolve_impl(JSContext* context, JS::HandleObject obj, + JS::HandleId id, bool* resolved) { + debug_jsprop("Resolve hook", id, obj); GjsAutoJSChar name; if (!gjs_get_string_id(context, id, &name)) { @@ -841,10 +861,9 @@ /* If we have no GIRepository information (we're a JS GObject subclass), * we need to look at exposing interfaces. Look up our interfaces through * GType data, and then hope that *those* are introspectable. */ - if (priv->info == NULL) { - bool status = object_instance_resolve_no_info(context, obj, resolved, priv, name); - return status; - } + if (is_custom_js_class()) + return resolve_no_info(context, obj, id, resolved, name, + ConsiderMethodsAndProperties); if (g_str_has_prefix (name, "vfunc_")) { /* The only time we find a vfunc info is when we're the base @@ -862,23 +881,21 @@ */ const char *name_without_vfunc_ = &(name[6]); /* lifetime tied to name */ - GIVFuncInfo *vfunc; bool defined_by_parent; - - vfunc = find_vfunc_on_parents(priv->info, name_without_vfunc_, &defined_by_parent); + GjsAutoInfo vfunc = find_vfunc_on_parents(m_info, + name_without_vfunc_, + &defined_by_parent); if (vfunc != NULL) { /* In the event that the vfunc is unchanged, let regular * prototypal inheritance take over. */ - if (defined_by_parent && is_vfunc_unchanged(vfunc, priv->gtype)) { - g_base_info_unref((GIBaseInfo *)vfunc); + if (defined_by_parent && is_vfunc_unchanged(vfunc)) { *resolved = false; return true; } - gjs_define_function(context, obj, priv->gtype, vfunc); + gjs_define_function(context, obj, m_gtype, vfunc); *resolved = true; - g_base_info_unref((GIBaseInfo *)vfunc); return true; } @@ -886,15 +903,33 @@ * method resolution. */ } - /* If the name refers to a GObject property or field, don't resolve. - * Instead, let the getProperty hook handle fetching the property from - * GObject. */ - if (is_gobject_property_name(priv->info, name) || - is_gobject_field_name(priv->info, name)) { - gjs_debug_jsprop(GJS_DEBUG_GOBJECT, - "Breaking out of %p resolve, '%s' is a GObject prop", - obj.get(), name.get()); - *resolved = false; + if (is_gobject_property_name(m_info, name)) + return lazy_define_gobject_property(context, obj, id, resolved, name); + + GjsAutoInfo field_info = lookup_field_info(m_info, name); + if (field_info) { + bool found = false; + if (!JS_AlreadyHasOwnPropertyById(context, obj, id, &found)) + return false; + if (found) { + *resolved = false; + return true; + } + + debug_jsprop("Defining lazy GObject field", id, obj); + + unsigned flags = GJS_MODULE_PROP_FLAGS; + if (!(g_field_info_get_flags(field_info) & GI_FIELD_IS_WRITABLE)) + flags |= JSPROP_READONLY; + + JS::RootedValue private_id(context, JS::StringValue(JSID_TO_STRING(id))); + if (!gjs_define_property_dynamic(context, obj, name, "gobject_field", + &ObjectBase::field_getter, + &ObjectBase::field_setter, private_id, + flags)) + return false; + + *resolved = true; return true; } @@ -909,20 +944,17 @@ * introduces the iface) */ - method_info = g_object_info_find_method_using_interfaces(priv->info, - name, - NULL); + GjsAutoInfo method_info = + g_object_info_find_method_using_interfaces(m_info, name, nullptr); /** * Search through any interfaces implemented by the GType; * this could be done better. See * https://bugzilla.gnome.org/show_bug.cgi?id=632922 */ - if (method_info == NULL) { - bool retval = object_instance_resolve_no_info(context, obj, resolved, priv, name); - return retval; - } - + if (!method_info) + return resolve_no_info(context, obj, id, resolved, name, + ConsiderOnlyMethods); #if GJS_VERBOSE_ENABLE_GI_USAGE _gjs_log_info_usage((GIBaseInfo*) method_info); @@ -931,37 +963,25 @@ if (g_function_info_get_flags (method_info) & GI_FUNCTION_IS_METHOD) { gjs_debug(GJS_DEBUG_GOBJECT, "Defining method %s in prototype for %s (%s.%s)", - g_base_info_get_name( (GIBaseInfo*) method_info), - g_type_name(priv->gtype), - g_base_info_get_namespace( (GIBaseInfo*) priv->info), - g_base_info_get_name( (GIBaseInfo*) priv->info)); + method_info.name(), type_name(), ns(), this->name()); - if (gjs_define_function(context, obj, priv->gtype, method_info) == NULL) { - g_base_info_unref( (GIBaseInfo*) method_info); + if (!gjs_define_function(context, obj, m_gtype, method_info)) return false; - } *resolved = true; /* we defined the prop in obj */ } else { *resolved = false; } - g_base_info_unref( (GIBaseInfo*) method_info); return true; } -/* Set properties from args to constructor (argv[0] is supposed to be - * a hash) - * The GParameter elements in the passed-in vector must be unset by the caller, - * regardless of the return value of this function. - */ -static bool -object_instance_props_to_g_parameters(JSContext *context, - JSObject *obj, - const JS::HandleValueArray& args, - GType gtype, - std::vector& gparams) -{ +/* Set properties from args to constructor (args[0] is supposed to be + * a hash) */ +bool ObjectPrototype::props_to_g_parameters(JSContext* context, + const JS::HandleValueArray& args, + std::vector* names, + AutoGValueVector* values) { size_t ix, length; if (args.length() == 0 || args[0].isUndefined()) @@ -982,112 +1002,121 @@ } for (ix = 0, length = ids.length(); ix < length; ix++) { - GjsAutoJSChar name; - GParameter gparam = { NULL, { 0, }}; + GValue gvalue = G_VALUE_INIT; /* ids[ix] is reachable because props is rooted, but require_property * doesn't know that */ prop_id = ids[ix]; - if (!gjs_object_require_property(context, props, "property list", - prop_id, &value) || - !gjs_get_string_id(context, prop_id, &name)) + if (!JSID_IS_STRING(prop_id)) + return _gjs_proxy_throw_nonexistent_field(context, m_gtype, + gjs_debug_id(prop_id).c_str()); + + JS::RootedString js_prop_name(context, JSID_TO_STRING(prop_id)); + GParamSpec *param_spec = find_param_spec_from_id(context, js_prop_name); + if (!param_spec) return false; - switch (init_g_param_from_property(context, name, - value, - gtype, - &gparam, - true /* constructing */)) { - case NO_SUCH_G_PROPERTY: - gjs_throw(context, "No property %s on this GObject %s", - name.get(), g_type_name(gtype)); - /* fallthrough */ - case SOME_ERROR_OCCURRED: + if (!JS_GetPropertyById(context, props, prop_id, &value)) + return false; + if (value.isUndefined()) { + gjs_throw(context, "Invalid value 'undefined' for property %s in " + "object initializer.", param_spec->name); return false; - case VALUE_WAS_SET: - default: - break; } - gparams.push_back(gparam); + if (!(param_spec->flags & G_PARAM_WRITABLE)) + return _gjs_proxy_throw_readonly_field(context, m_gtype, + param_spec->name); + /* prevent setting the prop even in JS */ + + g_value_init(&gvalue, G_PARAM_SPEC_VALUE_TYPE(param_spec)); + if (!gjs_value_to_g_value(context, value, &gvalue)) { + g_value_unset(&gvalue); + return false; + } + + names->push_back(param_spec->name); /* owned by GParamSpec in cache */ + values->push_back(gvalue); } return true; } -static GjsListLink * -object_instance_get_link(ObjectInstance *priv) +static void +wrapped_gobj_dispose_notify(gpointer data, + GObject *where_the_object_was) { - return &priv->instance_link; + auto *priv = static_cast(data); + priv->gobj_dispose_notify(); + gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Wrapped GObject %p disposed", + where_the_object_was); } -static void -object_instance_unlink(ObjectInstance *priv) +void +ObjectInstance::gobj_dispose_notify(void) { - if (wrapped_gobject_list == priv) - wrapped_gobject_list = priv->instance_link.next(); - priv->instance_link.unlink(); + m_gobj_disposed = true; + unlink(); } -static void -object_instance_link(ObjectInstance *priv) +void +ObjectInstance::iterate_wrapped_gobjects(ObjectInstance::Action action) { - if (wrapped_gobject_list) - priv->instance_link.prepend(priv, wrapped_gobject_list); - wrapped_gobject_list = priv; + ObjectInstance *link = ObjectInstance::wrapped_gobject_list; + while (link) { + ObjectInstance *next = link->next(); + action(link); + link = next; + } } -static void -wrapped_gobj_dispose_notify(gpointer data, - GObject *where_the_object_was) +void +ObjectInstance::remove_wrapped_gobjects_if(ObjectInstance::Predicate predicate, + ObjectInstance::Action action) { - auto *priv = static_cast(data); + std::vector removed; + iterate_wrapped_gobjects([&](ObjectInstance *link) { + if (predicate(link)) { + removed.push_back(link); + link->unlink(); + } + }); - priv->g_object_finalized = true; - object_instance_unlink(priv); - gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Wrapped GObject %p disposed", - where_the_object_was); + for (ObjectInstance *priv : removed) + action(priv); } void gjs_object_context_dispose_notify(void *data, GObject *where_the_object_was) { - ObjectInstance *priv = wrapped_gobject_list; - while (priv) { - ObjectInstance *next = priv->instance_link.next(); - - if (priv->keep_alive.rooted()) { - gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "GObject wrapper %p for GObject " - "%p (%s) was rooted but is now unrooted due to " - "GjsContext dispose", priv->keep_alive.get(), - priv->gobj, G_OBJECT_TYPE_NAME(priv->gobj)); - priv->keep_alive.reset(); - object_instance_unlink(priv); - } + ObjectInstance::iterate_wrapped_gobjects(std::mem_fn(&ObjectInstance::context_dispose_notify)); +} - priv = next; +void +ObjectInstance::context_dispose_notify(void) +{ + if (wrapper_is_rooted()) { + debug_lifecycle("Was rooted, but unrooting due to GjsContext dispose"); + discard_wrapper(); + unlink(); } } -static void -handle_toggle_down(GObject *gobj) +void +ObjectInstance::toggle_down(void) { - ObjectInstance *priv = get_object_qdata(gobj); - - gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Toggle notify DOWN for GObject " - "%p (%s), JS obj %p", gobj, G_OBJECT_TYPE_NAME(gobj), - priv->keep_alive.get()); + debug_lifecycle("Toggle notify DOWN"); /* Change to weak ref so the wrapper-wrappee pair can be * collected by the GC */ - if (priv->keep_alive.rooted()) { + if (wrapper_is_rooted()) { GjsContext *context; - gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Unrooting object"); - priv->keep_alive.switch_to_unrooted(); + debug_lifecycle("Unrooting wrapper"); + switch_to_unrooted(); /* During a GC, the collector asks each object which other * objects that it wants to hold on to so if there's an entire @@ -1111,32 +1140,28 @@ } } -static void -handle_toggle_up(GObject *gobj) +void +ObjectInstance::toggle_up(void) { - ObjectInstance *priv = get_object_qdata(gobj); - /* We need to root the JSObject associated with the passed in GObject so it * doesn't get garbage collected (and lose any associated javascript state * such as custom properties). */ - if (!priv->keep_alive) /* Object already GC'd */ + if (!has_wrapper()) /* Object already GC'd */ return; - gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Toggle notify UP for GObject " - "%p (%s), JS obj %p", gobj, G_OBJECT_TYPE_NAME(gobj), - priv->keep_alive.get()); + debug_lifecycle("Toggle notify UP"); /* Change to strong ref so the wrappee keeps the wrapper alive * in case the wrapper has data in it that the app cares about */ - if (!priv->keep_alive.rooted()) { + if (!wrapper_is_rooted()) { /* FIXME: thread the context through somehow. Maybe by looking up * the compartment that obj belongs to. */ GjsContext *context = gjs_context_get_current(); - gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Rooting object"); + debug_lifecycle("Rooting wrapper"); auto cx = static_cast(gjs_context_get_native_context(context)); - priv->keep_alive.switch_to_rooted(cx); + switch_to_rooted(cx); } } @@ -1146,10 +1171,10 @@ { switch (direction) { case ToggleQueue::UP: - handle_toggle_up(gobj); + ObjectInstance::for_gobject(gobj)->toggle_up(); break; case ToggleQueue::DOWN: - handle_toggle_down(gobj); + ObjectInstance::for_gobject(gobj)->toggle_down(); break; default: g_assert_not_reached(); @@ -1219,7 +1244,7 @@ toggle_up_queued? "up" : "down"); } - handle_toggle_down(gobj); + ObjectInstance::for_gobject(gobj)->toggle_down(); } else { toggle_queue.enqueue(gobj, ToggleQueue::DOWN, toggle_handler); } @@ -1234,22 +1259,22 @@ g_error("toggling up object %s that's already queued to toggle up\n", G_OBJECT_TYPE_NAME(gobj)); } - handle_toggle_up(gobj); + ObjectInstance::for_gobject(gobj)->toggle_up(); } else { toggle_queue.enqueue(gobj, ToggleQueue::UP, toggle_handler); } } } -static void -release_native_object (ObjectInstance *priv) +void +ObjectInstance::release_native_object(void) { - priv->keep_alive.reset(); - if (priv->uses_toggle_ref) - g_object_remove_toggle_ref(priv->gobj, wrapped_gobj_toggle_notify, nullptr); + discard_wrapper(); + if (m_uses_toggle_ref) + g_object_remove_toggle_ref(m_gobj, wrapped_gobj_toggle_notify, nullptr); else - g_object_unref(priv->gobj); - priv->gobj = NULL; + g_object_unref(m_gobj); + m_gobj = nullptr; } /* At shutdown, we need to ensure we've cleared the context of any @@ -1277,52 +1302,50 @@ * association. We avoid the potential recursion implied in: * toggle ref removal -> gobj dispose -> toggle ref notify * by emptying the toggle queue earlier in the shutdown sequence. */ - std::vector to_be_released; - ObjectInstance *link = wrapped_gobject_list; - while (link) { - ObjectInstance *next = link->instance_link.next(); - if (link->keep_alive.rooted()) { - to_be_released.push_back(link); - object_instance_unlink(link); - } - - link = next; - } - for (ObjectInstance *priv : to_be_released) - release_native_object(priv); + ObjectInstance::remove_wrapped_gobjects_if( + std::mem_fn(&ObjectInstance::wrapper_is_rooted), + std::mem_fn(&ObjectInstance::release_native_object)); } -static ObjectInstance * -init_object_private (JSContext *context, - JS::HandleObject object) +ObjectInstance * +ObjectInstance::new_for_js_object(JSContext *cx, + JS::HandleObject obj) { - ObjectInstance *proto_priv; - ObjectInstance *priv; - - JS_BeginRequest(context); + ObjectInstance *priv = g_slice_new0(ObjectInstance); + new (priv) ObjectInstance(cx, obj); + return priv; +} - priv = g_slice_new0(ObjectInstance); - new (priv) ObjectInstance(); +ObjectInstance::ObjectInstance(JSContext* cx, JS::HandleObject object) + : ObjectBase(ObjectPrototype::for_js_prototype(cx, object)) { + g_assert(!JS_GetPrivate(object)); + + GJS_INC_COUNTER(object_instance); + debug_lifecycle("Instance constructor"); +} - GJS_INC_COUNTER(object); +ObjectPrototype* ObjectPrototype::new_for_js_object(GIObjectInfo* info, + GType gtype) { + auto* priv = g_slice_new0(ObjectPrototype); + new (priv) ObjectPrototype(info, gtype); + return priv; +} - g_assert(priv_from_js(context, object) == NULL); - JS_SetPrivate(object, priv); +ObjectPrototype::ObjectPrototype(GIObjectInfo* info, GType gtype) + : ObjectBase(), m_info(info), m_gtype(gtype) { + if (info) + g_base_info_ref(info); - proto_priv = proto_priv_from_js(context, object); - g_assert(proto_priv != NULL); + g_type_class_ref(gtype); - priv->gtype = proto_priv->gtype; - priv->info = proto_priv->info; - if (priv->info) - g_base_info_ref( (GIBaseInfo*) priv->info); + if (!m_property_cache.init()) + g_error("Out of memory for property cache of %s", type_name()); - gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Instance constructor of %s, " - "JS obj %p, priv %p", g_type_name(priv->gtype), - object.get(), priv); + if (!m_field_cache.init()) + g_error("Out of memory for field cache of %s", type_name()); - JS_EndRequest(context); - return priv; + GJS_INC_COUNTER(object_prototype); + debug_lifecycle("Prototype constructor"); } static void @@ -1332,35 +1355,27 @@ { gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Weak pointer update callback, " "%zu wrapped GObject(s) to examine", - wrapped_gobject_list->instance_link.size()); - - std::vector to_be_disassociated; - ObjectInstance *priv = wrapped_gobject_list; - - while (priv) { - ObjectInstance *next = priv->instance_link.next(); + ObjectInstance::num_wrapped_gobjects()); - if (!priv->keep_alive.rooted() && - priv->keep_alive != nullptr && - priv->keep_alive.update_after_gc()) { - /* Ouch, the JS object is dead already. Disassociate the - * GObject and hope the GObject dies too. (Remove it from - * the weak pointer list first, since the disassociation - * may also cause it to be erased.) - */ - gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Found GObject weak pointer " - "whose JS object %p is about to be finalized: " - "%p (%s)", priv->keep_alive.get(), priv->gobj, - G_OBJECT_TYPE_NAME(priv->gobj)); - to_be_disassociated.push_back(priv); - object_instance_unlink(priv); - } + ObjectInstance::remove_wrapped_gobjects_if( + std::mem_fn(&ObjectInstance::weak_pointer_was_finalized), + std::mem_fn(&ObjectInstance::disassociate_js_gobject)); +} - priv = next; +bool +ObjectInstance::weak_pointer_was_finalized(void) +{ + if (has_wrapper() && !wrapper_is_rooted() && update_after_gc()) { + /* Ouch, the JS object is dead already. Disassociate the + * GObject and hope the GObject dies too. (Remove it from + * the weak pointer list first, since the disassociation + * may also cause it to be erased.) + */ + debug_lifecycle("Found GObject weak pointer whose JS wrapper is about " + "to be finalized"); + return true; } - - for (ObjectInstance *ex_object : to_be_disassociated) - disassociate_js_gobject(ex_object); + return false; } static void @@ -1374,36 +1389,33 @@ } } -static void -associate_js_gobject (JSContext *context, - JS::HandleObject object, - GObject *gobj) -{ - ObjectInstance *priv; - - priv = priv_from_js(context, object); - priv->uses_toggle_ref = false; - priv->gobj = gobj; - - g_assert(!priv->keep_alive.rooted()); - - set_object_qdata(gobj, priv); +void +ObjectInstance::associate_js_gobject(JSContext *context, + JS::HandleObject object, + GObject *gobj) +{ + g_assert(!wrapper_is_rooted()); + + m_uses_toggle_ref = false; + m_gobj = gobj; + set_object_qdata(); + m_wrapper = object; - priv->keep_alive = object; ensure_weak_pointer_callback(context); - object_instance_link(priv); + link(); - g_object_weak_ref(gobj, wrapped_gobj_dispose_notify, priv); + g_object_weak_ref(gobj, wrapped_gobj_dispose_notify, this); } -static void -ensure_uses_toggle_ref(JSContext *cx, - ObjectInstance *priv) +void +ObjectInstance::ensure_uses_toggle_ref(JSContext *cx) { - if (priv->uses_toggle_ref) + if (m_uses_toggle_ref) return; - g_assert(!priv->keep_alive.rooted()); + debug_lifecycle("Switching object instance to toggle ref"); + + g_assert(!wrapper_is_rooted()); /* OK, here is where things get complicated. We want the * wrapped gobj to keep the JSObject* wrapper alive, because @@ -1416,121 +1428,100 @@ * the wrapper to be garbage collected (and thus unref the * wrappee). */ - priv->uses_toggle_ref = true; - priv->keep_alive.switch_to_rooted(cx); - g_object_add_toggle_ref(priv->gobj, wrapped_gobj_toggle_notify, nullptr); + m_uses_toggle_ref = true; + switch_to_rooted(cx); + g_object_add_toggle_ref(m_gobj, wrapped_gobj_toggle_notify, nullptr); /* We now have both a ref and a toggle ref, we only want the toggle ref. * This may immediately remove the GC root we just added, since refcount * may drop to 1. */ - g_object_unref(priv->gobj); + g_object_unref(m_gobj); } -static void -invalidate_all_closures(ObjectInstance *priv) -{ +void ObjectBase::invalidate_all_closures(void) { /* Can't loop directly through the items, since invalidating an item's * closure might have the effect of removing the item from the set in the * invalidate notifier */ - while (!priv->closures.empty()) { + while (!m_closures.empty()) { /* This will also free cd, through the closure invalidation mechanism */ - GClosure *closure = *priv->closures.begin(); + GClosure *closure = *m_closures.begin(); g_closure_invalidate(closure); /* Erase element if not already erased */ - priv->closures.erase(closure); + m_closures.remove(closure); } } -static void -disassociate_js_gobject(ObjectInstance *priv) +void +ObjectInstance::disassociate_js_gobject(void) { bool had_toggle_down, had_toggle_up; - if (!priv->g_object_finalized) - g_object_weak_unref(priv->gobj, wrapped_gobj_dispose_notify, priv); + if (!m_gobj_disposed) + g_object_weak_unref(m_gobj, wrapped_gobj_dispose_notify, this); auto& toggle_queue = ToggleQueue::get_default(); - std::tie(had_toggle_down, had_toggle_up) = toggle_queue.cancel(priv->gobj); + std::tie(had_toggle_down, had_toggle_up) = toggle_queue.cancel(m_gobj); if (had_toggle_down != had_toggle_up) { g_error("JS object wrapper for GObject %p (%s) is being released while " - "toggle references are still pending.", - priv->gobj, G_OBJECT_TYPE_NAME(priv->gobj)); + "toggle references are still pending.", m_gobj, type_name()); } /* Fist, remove the wrapper pointer from the wrapped GObject */ - set_object_qdata(priv->gobj, nullptr); + unset_object_qdata(); /* Now release all the resources the current wrapper has */ - invalidate_all_closures(priv); - release_native_object(priv); + invalidate_all_closures(); + release_native_object(); /* Mark that a JS object once existed, but it doesn't any more */ - priv->js_object_finalized = true; - priv->keep_alive = nullptr; + m_wrapper_finalized = true; + m_wrapper = nullptr; } -static void -clear_g_params(std::vector& params) +bool +ObjectInstance::init_impl(JSContext *context, + const JS::CallArgs& args, + JS::MutableHandleObject object) { - for (GParameter param : params) - g_value_unset(¶m.value); -} - -static bool -object_instance_init (JSContext *context, - JS::MutableHandleObject object, - const JS::HandleValueArray& args) -{ - ObjectInstance *priv; - GType gtype; - std::vector params; GTypeQuery query; - GObject *gobj; - - priv = (ObjectInstance *) JS_GetPrivate(object); - gtype = priv->gtype; - g_assert(gtype != G_TYPE_NONE); + g_assert(gtype() != G_TYPE_NONE); - if (!object_instance_props_to_g_parameters(context, object, args, - gtype, params)) { - clear_g_params(params); + std::vector names; + AutoGValueVector values; + if (!m_proto->props_to_g_parameters(context, args, &names, &values)) return false; - } /* Mark this object in the construction stack, it will be popped in gjs_object_custom_init() later down. */ - if (g_type_get_qdata(gtype, gjs_is_custom_type_quark())) { + if (g_type_get_qdata(gtype(), ObjectInstance::custom_type_quark())) object_init_list.emplace(context, object); - } -G_GNUC_BEGIN_IGNORE_DEPRECATIONS - gobj = (GObject*) g_object_newv(gtype, params.size(), params.data()); -G_GNUC_END_IGNORE_DEPRECATIONS - - clear_g_params(params); - - ObjectInstance *other_priv = get_object_qdata(gobj); - if (other_priv && other_priv->keep_alive != object.get()) { - /* g_object_newv returned an object that's already tracked by a JS - * object. Let's assume this is a singleton like IBus.IBus and return - * the existing JS wrapper object. + g_assert(names.size() == values.size()); + GObject* gobj = g_object_new_with_properties(gtype(), values.size(), + names.data(), values.data()); + + ObjectInstance *other_priv = ObjectInstance::for_gobject(gobj); + if (other_priv && other_priv->m_wrapper != object.get()) { + /* g_object_new_with_properties() returned an object that's already + * tracked by a JS object. Let's assume this is a singleton like + * IBus.IBus and return the existing JS wrapper object. * * 'object' has a value that was originally created by * JS_NewObjectForConstructor in GJS_NATIVE_CONSTRUCTOR_PRELUDE, but * we're not actually using it, so just let it get collected. Avoiding * this would require a non-trivial amount of work. * */ - ensure_uses_toggle_ref(context, other_priv); - object.set(other_priv->keep_alive); + other_priv->ensure_uses_toggle_ref(context); + object.set(other_priv->m_wrapper); g_object_unref(gobj); /* We already own a reference */ gobj = NULL; - goto out; + return true; } - g_type_query_dynamic_safe(gtype, &query); + type_query_dynamic_safe(&query); if (G_LIKELY (query.type)) JS_updateMallocCounter(context, query.instance_size); @@ -1549,17 +1540,14 @@ /* we should already have a ref */ } - if (priv->gobj == NULL) + if (!m_gobj) associate_js_gobject(context, object, gobj); - gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "JSObject created with GObject %p (%s)", - priv->gobj, G_OBJECT_TYPE_NAME(priv->gobj)); + debug_lifecycle("JSObject created"); - TRACE(GJS_OBJECT_PROXY_NEW(priv, priv->gobj, - priv->info ? g_base_info_get_namespace((GIBaseInfo*) priv->info) : "_gjs_private", - priv->info ? g_base_info_get_name((GIBaseInfo*) priv->info) : g_type_name(gtype))); + TRACE(GJS_OBJECT_PROXY_NEW(this, m_gobj, ns(), name())); - out: + args.rval().setObject(*object); return true; } @@ -1574,7 +1562,7 @@ /* Init the private variable before we do anything else. If a garbage * collection happens when calling the init function then this object * might be traced and we will end up dereferencing a null pointer */ - init_object_private(context, object); + JS_SetPrivate(object, ObjectInstance::new_for_js_object(context, object)); if (!gjs_object_require_property(context, object, "GObject instance", GJS_STRING_GOBJECT_INIT, &initer)) @@ -1589,83 +1577,75 @@ return ret; } -static void -object_instance_trace(JSTracer *tracer, - JSObject *obj) -{ - ObjectInstance *priv; - - priv = (ObjectInstance *) JS_GetPrivate(obj); +void ObjectBase::trace(JSTracer* tracer, JSObject* obj) { + auto* priv = ObjectBase::for_js_nocheck(obj); if (priv == NULL) return; - if (priv->g_object_finalized) { - g_debug("Object %s.%s (%p), has been already finalized. " - "Impossible to trace it.", - priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "", - priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype), - priv->gobj); - return; - } + if (priv->is_prototype()) + priv->to_prototype()->trace_impl(tracer); + else + priv->trace_impl(tracer); +} - for (GClosure *closure : priv->closures) +void ObjectBase::trace_impl(JSTracer* tracer) { + for (GClosure *closure : m_closures) gjs_closure_trace(closure, tracer); } -static void -closure_invalidated(void *data, - GClosure *closure) -{ - auto priv = static_cast(data); - priv->closures.erase(closure); +void ObjectPrototype::trace_impl(JSTracer* tracer) { + m_property_cache.trace(tracer); + m_field_cache.trace(tracer); + ObjectBase::trace_impl(tracer); } -static void -object_instance_finalize(JSFreeOp *fop, - JSObject *obj) -{ - ObjectInstance *priv; - - priv = (ObjectInstance *) JS_GetPrivate(obj); +void ObjectBase::finalize(JSFreeOp* fop, JSObject* obj) { + auto* priv = ObjectBase::for_js_nocheck(obj); g_assert (priv != NULL); - gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, - "Finalizing %s, JS obj %p, priv %p, GObject %p", - g_type_name(priv->gtype), obj, priv, priv->gobj); - - TRACE(GJS_OBJECT_PROXY_FINALIZE(priv, priv->gobj, - priv->info ? g_base_info_get_namespace((GIBaseInfo*) priv->info) : "_gjs_private", - priv->info ? g_base_info_get_name((GIBaseInfo*) priv->info) : g_type_name(priv->gtype))); - - /* This applies only to instances, not prototypes, but it's possible that - * an instance's GObject is already freed at this point. */ - invalidate_all_closures(priv); - /* Object is instance, not prototype, AND GObject is not already freed */ - if (priv->gobj) { + if (priv->is_prototype()) { + priv->to_prototype()->~ObjectPrototype(); + g_slice_free(ObjectPrototype, priv); + } else { + priv->to_instance()->~ObjectInstance(); + g_slice_free(ObjectInstance, priv); + } + + /* Remove the pointer from the JSObject */ + JS_SetPrivate(obj, nullptr); +} + +ObjectInstance::~ObjectInstance() { + debug_lifecycle("Finalize"); + + TRACE(GJS_OBJECT_PROXY_FINALIZE(this, m_gobj, ns(), name())); + + invalidate_all_closures(); + + /* GObject is not already freed */ + if (m_gobj) { bool had_toggle_up; bool had_toggle_down; - if (G_UNLIKELY (priv->gobj->ref_count <= 0)) { + if (G_UNLIKELY (m_gobj->ref_count <= 0)) { g_error("Finalizing proxy for an already freed object of type: %s.%s\n", - priv->info ? g_base_info_get_namespace((GIBaseInfo*) priv->info) : "", - priv->info ? g_base_info_get_name((GIBaseInfo*) priv->info) : g_type_name(priv->gtype)); + ns(), name()); } auto& toggle_queue = ToggleQueue::get_default(); - std::tie(had_toggle_down, had_toggle_up) = toggle_queue.cancel(priv->gobj); + std::tie(had_toggle_down, had_toggle_up) = toggle_queue.cancel(m_gobj); if (!had_toggle_up && had_toggle_down) { g_error("Finalizing proxy for an object that's scheduled to be unrooted: %s.%s\n", - priv->info ? g_base_info_get_namespace((GIBaseInfo*) priv->info) : "", - priv->info ? g_base_info_get_name((GIBaseInfo*) priv->info) : g_type_name(priv->gtype)); + ns(), name()); } - if (!priv->g_object_finalized) - g_object_weak_unref(priv->gobj, wrapped_gobj_dispose_notify, priv); - release_native_object(priv); + if (!m_gobj_disposed) + g_object_weak_unref(m_gobj, wrapped_gobj_dispose_notify, this); + release_native_object(); } - if (priv->keep_alive.rooted()) { + if (wrapper_is_rooted()) { /* This happens when the refcount on the object is still >1, * for example with global objects GDK never frees like GdkDisplay, * when we close down the JS runtime. @@ -1673,35 +1653,27 @@ gjs_debug(GJS_DEBUG_GOBJECT, "Wrapper was finalized despite being kept alive, has refcount >1"); - gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Unrooting object"); + debug_lifecycle("Unrooting object"); - priv->keep_alive.reset(); + discard_wrapper(); } - object_instance_unlink(priv); + unlink(); - if (priv->info) { - g_base_info_unref( (GIBaseInfo*) priv->info); - priv->info = NULL; - } + GJS_DEC_COUNTER(object_instance); +} - if (priv->klass) { - g_type_class_unref (priv->klass); - priv->klass = NULL; - } +ObjectPrototype::~ObjectPrototype() { + invalidate_all_closures(); - GJS_DEC_COUNTER(object); - priv->~ObjectInstance(); - g_slice_free(ObjectInstance, priv); + g_clear_pointer(&m_info, g_base_info_unref); + g_type_class_unref(g_type_class_peek(m_gtype)); - /* Remove the ObjectInstance pointer from the JSObject */ - JS_SetPrivate(obj, nullptr); + GJS_DEC_COUNTER(object_prototype); } -static JSObject * -gjs_lookup_object_constructor_from_info(JSContext *context, - GIObjectInfo *info, - GType gtype) -{ +JSObject* gjs_lookup_object_constructor_from_info(JSContext* context, + GIObjectInfo* info, + GType gtype) { JS::RootedObject in_object(context); const char *constructor_name; @@ -1726,8 +1698,9 @@ we need to define it first. */ JS::RootedObject ignored(context); - gjs_define_object_class(context, in_object, NULL, gtype, &constructor, - &ignored); + if (!gjs_define_object_class(context, in_object, nullptr, gtype, + &constructor, &ignored)) + return nullptr; } else { if (G_UNLIKELY (!value.isObject())) return NULL; @@ -1751,138 +1724,129 @@ if (G_UNLIKELY(!constructor)) return NULL; - JS::RootedValue value(context); - if (!gjs_object_get_property(context, constructor, - GJS_STRING_PROTOTYPE, &value)) - return NULL; - - if (G_UNLIKELY (!value.isObjectOrNull())) + JS::RootedObject prototype(context); + if (!gjs_object_require_property(context, constructor, "constructor object", + GJS_STRING_PROTOTYPE, &prototype)) return NULL; - return value.toObjectOrNull(); + return prototype; } static JSObject * gjs_lookup_object_prototype(JSContext *context, GType gtype) { - GIObjectInfo *info; - JSObject *proto; - - info = (GIObjectInfo*)g_irepository_find_by_gtype(g_irepository_get_default(), gtype); - proto = gjs_lookup_object_prototype_from_info(context, info, gtype); - if (info) - g_base_info_unref((GIBaseInfo*)info); - - return proto; + GjsAutoInfo info = + g_irepository_find_by_gtype(nullptr, gtype); + return gjs_lookup_object_prototype_from_info(context, info, gtype); } -static void -do_associate_closure(ObjectInstance *priv, - GClosure *closure) -{ +void ObjectBase::associate_closure(JSContext* cx, GClosure* closure) { + if (!is_prototype()) + to_instance()->ensure_uses_toggle_ref(cx); + /* This is a weak reference, and will be cleared when the closure is * invalidated */ - priv->closures.insert(closure); - g_closure_add_invalidate_notifier(closure, priv, closure_invalidated); + auto already_has = std::find(m_closures.begin(), m_closures.end(), closure); + g_assert(already_has == m_closures.end() && + "This closure was already associated with this object"); + m_closures.push_front(closure); + g_closure_add_invalidate_notifier(closure, this, + &ObjectBase::closure_invalidated_notify); } -static bool -real_connect_func(JSContext *context, - unsigned argc, - JS::Value *vp, - bool after) +void ObjectBase::closure_invalidated_notify(void* data, GClosure* closure) { + auto* priv = static_cast(data); + priv->m_closures.remove(closure); +} + +bool ObjectBase::connect(JSContext* cx, unsigned argc, JS::Value* vp) { + GJS_GET_PRIV(cx, argc, vp, args, obj, ObjectBase, priv); + if (!priv) + return throw_priv_is_null_error(cx); /* wrong class passed in */ + + if (!priv->check_is_instance(cx, "connect to signals")) + return false; + + return priv->to_instance()->connect_impl(cx, args, false); +} + +bool ObjectBase::connect_after(JSContext* cx, unsigned argc, JS::Value* vp) { + GJS_GET_PRIV(cx, argc, vp, args, obj, ObjectBase, priv); + if (!priv) + return throw_priv_is_null_error(cx); /* wrong class passed in */ + + if (!priv->check_is_instance(cx, "connect to signals")) + return false; + + return priv->to_instance()->connect_impl(cx, args, true); +} + +bool +ObjectInstance::connect_impl(JSContext *context, + const JS::CallArgs& args, + bool after) { - GJS_GET_PRIV(context, argc, vp, argv, obj, ObjectInstance, priv); GClosure *closure; gulong id; guint signal_id; GQuark signal_detail; - gjs_debug_gsignal("connect obj %p priv %p argc %d", obj.get(), priv, argc); - if (priv == NULL) { - throw_priv_is_null_error(context); - return false; /* wrong class passed in */ - } - if (priv->gobj == NULL) { - /* prototype, not an instance. */ - gjs_throw(context, "Can't connect to signals on %s.%s.prototype; only on instances", - priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "", - priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype)); - return false; - } - if (priv->g_object_finalized) { - g_critical("Object %s.%s (%p), has been already deallocated - impossible to connect to signal. " - "This might be caused by the fact that the object has been destroyed from C " - "code using something such as destroy(), dispose(), or remove() vfuncs", - priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "", - priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype), - priv->gobj); - gjs_dumpstack(); - return true; - } + gjs_debug_gsignal("connect obj %p priv %p", m_wrapper.get(), this); - ensure_uses_toggle_ref(context, priv); + if (!check_gobject_disposed("connect to any signal on")) + return true; - if (argc != 2 || !argv[0].isString() || !JS::IsCallable(&argv[1].toObject())) { - gjs_throw(context, "connect() takes two args, the signal name and the callback"); + GjsAutoJSChar signal_name; + JS::RootedObject callback(context); + if (!gjs_parse_call_args(context, after ? "connect_after" : "connect", args, "so", + "signal name", &signal_name, + "callback", &callback)) return false; - } - JS::RootedString signal_str(context, argv[0].toString()); - GjsAutoJSChar signal_name = JS_EncodeStringToUTF8(context, signal_str); - if (!signal_name) + if (!JS::IsCallable(callback)) { + gjs_throw(context, "second arg must be a callback"); return false; + } - if (!g_signal_parse_name(signal_name, - G_OBJECT_TYPE(priv->gobj), - &signal_id, - &signal_detail, + if (!g_signal_parse_name(signal_name, gtype(), &signal_id, &signal_detail, true)) { gjs_throw(context, "No signal '%s' on object '%s'", - signal_name.get(), - g_type_name(G_OBJECT_TYPE(priv->gobj))); + signal_name.get(), type_name()); return false; } - closure = gjs_closure_new_for_signal(context, &argv[1].toObject(), "signal callback", signal_id); + closure = gjs_closure_new_for_signal(context, callback, "signal callback", signal_id); if (closure == NULL) return false; - do_associate_closure(priv, closure); + associate_closure(context, closure); - id = g_signal_connect_closure_by_id(priv->gobj, + id = g_signal_connect_closure_by_id(m_gobj, signal_id, signal_detail, closure, after); - argv.rval().setDouble(id); + args.rval().setDouble(id); return true; } -static bool -connect_after_func(JSContext *context, - unsigned argc, - JS::Value *vp) -{ - return real_connect_func(context, argc, vp, true); -} +bool ObjectBase::emit(JSContext* cx, unsigned argc, JS::Value* vp) { + GJS_GET_PRIV(cx, argc, vp, args, obj, ObjectBase, priv); + if (!priv) + return throw_priv_is_null_error(cx); /* wrong class passed in */ -static bool -connect_func(JSContext *context, - unsigned argc, - JS::Value *vp) -{ - return real_connect_func(context, argc, vp, false); + if (!priv->check_is_instance(cx, "emit signal")) + return false; + + return priv->to_instance()->emit_impl(cx, args); } -static bool -emit_func(JSContext *context, - unsigned argc, - JS::Value *vp) +bool +ObjectInstance::emit_impl(JSContext *context, + const JS::CallArgs& argv) { - GJS_GET_PRIV(context, argc, vp, argv, obj, ObjectInstance, priv); guint signal_id; GQuark signal_detail; GSignalQuery signal_query; @@ -1891,61 +1855,30 @@ unsigned int i; bool failed; - gjs_debug_gsignal("emit obj %p priv %p argc %d", obj.get(), priv, argc); - - if (priv == NULL) { - throw_priv_is_null_error(context); - return false; /* wrong class passed in */ - } - - if (priv->gobj == NULL) { - /* prototype, not an instance. */ - gjs_throw(context, "Can't emit signal on %s.%s.prototype; only on instances", - priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "", - priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype)); - return false; - } + gjs_debug_gsignal("emit obj %p priv %p argc %d", m_wrapper.get(), this, + argv.length()); - if (priv->g_object_finalized) { - g_critical("Object %s.%s (%p), has been already deallocated - impossible to emit signal. " - "This might be caused by the fact that the object has been destroyed from C " - "code using something such as destroy(), dispose(), or remove() vfuncs", - priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "", - priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype), - priv->gobj); - gjs_dumpstack(); + if (!check_gobject_disposed("emit any signal on")) return true; - } - - if (argc < 1 || !argv[0].isString()) { - gjs_throw(context, "emit() first arg is the signal name"); - return false; - } - JS::RootedString signal_str(context, argv[0].toString()); - GjsAutoJSChar signal_name = JS_EncodeStringToUTF8(context, signal_str); - if (!signal_name) + GjsAutoJSChar signal_name; + if (!gjs_parse_call_args(context, "emit", argv, "!s", + "signal name", &signal_name)) return false; - if (!g_signal_parse_name(signal_name, - G_OBJECT_TYPE(priv->gobj), - &signal_id, - &signal_detail, + if (!g_signal_parse_name(signal_name, gtype(), &signal_id, &signal_detail, false)) { gjs_throw(context, "No signal '%s' on object '%s'", - signal_name.get(), - g_type_name(G_OBJECT_TYPE(priv->gobj))); + signal_name.get(), type_name()); return false; } g_signal_query(signal_id, &signal_query); - if ((argc - 1) != signal_query.n_params) { + if ((argv.length() - 1) != signal_query.n_params) { gjs_throw(context, "Signal '%s' on %s requires %d args got %d", - signal_name.get(), - g_type_name(G_OBJECT_TYPE(priv->gobj)), - signal_query.n_params, - argc - 1); + signal_name.get(), type_name(), signal_query.n_params, + argv.length() - 1); return false; } @@ -1956,8 +1889,8 @@ instance_and_args = g_newa(GValue, signal_query.n_params + 1); memset(instance_and_args, 0, sizeof(GValue) * (signal_query.n_params + 1)); - g_value_init(&instance_and_args[0], G_TYPE_FROM_INSTANCE(priv->gobj)); - g_value_set_instance(&instance_and_args[0], priv->gobj); + g_value_init(&instance_and_args[0], G_TYPE_FROM_INSTANCE(m_gobj)); + g_value_set_instance(&instance_and_args[0], m_gobj); failed = false; for (i = 0; i < signal_query.n_params; ++i) { @@ -1995,38 +1928,42 @@ return !failed; } -static bool -to_string_func(JSContext *context, - unsigned argc, - JS::Value *vp) -{ - GJS_GET_PRIV(context, argc, vp, rec, obj, ObjectInstance, priv); +bool ObjectBase::to_string(JSContext* cx, unsigned argc, JS::Value* vp) { + GJS_GET_PRIV(cx, argc, vp, args, obj, ObjectBase, priv); + if (!priv) + return throw_priv_is_null_error(cx); /* wrong class passed in */ - if (priv == NULL) { - throw_priv_is_null_error(context); - return false; /* wrong class passed in */ - } + if (priv->is_prototype()) + return priv->to_prototype()->to_string_impl(cx, args); + return priv->to_instance()->to_string_impl(cx, args); +} + +bool +ObjectInstance::to_string_impl(JSContext *cx, + const JS::CallArgs& args) +{ + return _gjs_proxy_to_string_func( + cx, m_wrapper, m_gobj_disposed ? "object (FINALIZED)" : "object", + info(), gtype(), m_gobj, args.rval()); +} - return _gjs_proxy_to_string_func(context, obj, - (priv->g_object_finalized) ? - "object (FINALIZED)" : "object", - (GIBaseInfo*)priv->info, priv->gtype, - priv->gobj, rec.rval()); +bool ObjectPrototype::to_string_impl(JSContext* cx, const JS::CallArgs& args) { + return _gjs_proxy_to_string_func(cx, nullptr, "object prototype", info(), + gtype(), nullptr, args.rval()); } static const struct JSClassOps gjs_object_class_ops = { - NULL, /* addProperty */ - NULL, /* deleteProperty */ - object_instance_get_prop, - object_instance_set_prop, - NULL, /* enumerate */ - object_instance_resolve, - nullptr, /* mayResolve */ - object_instance_finalize, + &ObjectBase::add_property, + nullptr, // deleteProperty + nullptr, // enumerate + nullptr, // newEnumerate + &ObjectBase::resolve, + nullptr, // mayResolve + &ObjectBase::finalize, NULL, NULL, NULL, - object_instance_trace, + &ObjectBase::trace, }; struct JSClass gjs_object_instance_class = { @@ -2035,23 +1972,19 @@ &gjs_object_class_ops }; -static bool -init_func (JSContext *context, - unsigned argc, - JS::Value *vp) -{ +bool ObjectBase::init(JSContext* context, unsigned argc, JS::Value* vp) { GJS_GET_THIS(context, argc, vp, argv, obj); - bool ret; if (!do_base_typecheck(context, obj, true)) return false; - ret = object_instance_init(context, &obj, argv); + auto* priv = ObjectBase::for_js(context, obj); + g_assert(priv); /* Already checked by do_base_typecheck() */ - if (ret) - argv.rval().setObject(*obj); + if (!priv->check_is_instance(context, "initialize")) + return false; - return ret; + return priv->to_instance()->init_impl(context, argv, &obj); } JSPropertySpec gjs_object_instance_proto_props[] = { @@ -2059,31 +1992,29 @@ }; JSFunctionSpec gjs_object_instance_proto_funcs[] = { - JS_FS("_init", init_func, 0, 0), - JS_FS("connect", connect_func, 0, 0), - JS_FS("connect_after", connect_after_func, 0, 0), - JS_FS("emit", emit_func, 0, 0), - JS_FS("toString", to_string_func, 0, 0), - JS_FS_END -}; + JS_FN("_init", &ObjectBase::init, 0, 0), + JS_FN("connect", &ObjectBase::connect, 0, 0), + JS_FN("connect_after", &ObjectBase::connect_after, 0, 0), + JS_FN("emit", &ObjectBase::emit, 0, 0), + JS_FN("toString", &ObjectBase::to_string, 0, 0), + JS_FS_END}; -void +bool gjs_object_define_static_methods(JSContext *context, JS::HandleObject constructor, GType gtype, GIObjectInfo *object_info) { - GIStructInfo *gtype_struct; int i; int n_methods; n_methods = g_object_info_get_n_methods(object_info); for (i = 0; i < n_methods; i++) { - GIFunctionInfo *meth_info; GIFunctionInfoFlags flags; - meth_info = g_object_info_get_method(object_info, i); + GjsAutoInfo meth_info = + g_object_info_get_method(object_info, i); flags = g_function_info_get_flags (meth_info); /* Anything that isn't a method we put on the prototype of the @@ -2094,37 +2025,43 @@ * like in the near future. */ if (!(flags & GI_FUNCTION_IS_METHOD)) { - if (!gjs_define_function(context, constructor, gtype, - (GICallableInfo *) meth_info)) - gjs_log_exception(context); + if (!gjs_define_function(context, constructor, gtype, meth_info)) + return false; } - - g_base_info_unref((GIBaseInfo*) meth_info); } - gtype_struct = g_object_info_get_class_struct(object_info); - + GjsAutoInfo gtype_struct = + g_object_info_get_class_struct(object_info); if (gtype_struct == NULL) - return; + return true; /* not an error? */ n_methods = g_struct_info_get_n_methods(gtype_struct); for (i = 0; i < n_methods; i++) { - GIFunctionInfo *meth_info; + GjsAutoInfo meth_info = + g_struct_info_get_method(gtype_struct, i); - meth_info = g_struct_info_get_method(gtype_struct, i); + if (!gjs_define_function(context, constructor, gtype, meth_info)) + return false; + } - if (!gjs_define_function(context, constructor, gtype, - (GICallableInfo *) meth_info)) - gjs_log_exception(context); + return true; +} - g_base_info_unref((GIBaseInfo*) meth_info); +JS::Symbol* +ObjectInstance::hook_up_vfunc_symbol(JSContext *cx) +{ + if (!hook_up_vfunc_root.initialized()) { + JS::RootedString descr(cx, + JS_NewStringCopyZ(cx, "__GObject__hook_up_vfunc")); + if (!descr) + g_error("Out of memory defining internal symbol"); + hook_up_vfunc_root.init(cx, JS::NewSymbol(cx, descr)); } - - g_base_info_unref((GIBaseInfo*) gtype_struct); + return hook_up_vfunc_root; } -void +bool gjs_define_object_class(JSContext *context, JS::HandleObject in_object, GIObjectInfo *info, @@ -2135,7 +2072,6 @@ const char *constructor_name; JS::RootedObject parent_proto(context); - ObjectInstance *priv; const char *ns; GType parent_type; @@ -2184,7 +2120,7 @@ parent_type = g_type_parent(gtype); if (parent_type != G_TYPE_INVALID) - parent_proto = gjs_lookup_object_prototype(context, parent_type); + parent_proto = gjs_lookup_object_prototype(context, parent_type); ns = gjs_get_names_from_gtype_and_gi_info(gtype, (GIBaseInfo *) info, &constructor_name); @@ -2203,31 +2139,32 @@ /* funcs of constructor, MyConstructor.myfunc() */ NULL, prototype, - constructor)) { - g_error("Can't init class %s", constructor_name); - } + constructor)) + return false; - GJS_INC_COUNTER(object); - priv = g_slice_new0(ObjectInstance); - new (priv) ObjectInstance(); - priv->info = info; - if (info) - g_base_info_ref((GIBaseInfo*) info); - priv->gtype = gtype; - priv->klass = (GTypeClass*) g_type_class_ref (gtype); - JS_SetPrivate(prototype, priv); + /* Hook_up_vfunc can't be included in gjs_object_instance_proto_funcs + * because it's a custom symbol. */ + JS::RootedId hook_up_vfunc(context, + SYMBOL_TO_JSID(ObjectInstance::hook_up_vfunc_symbol(context))); + if (!JS_DefineFunctionById(context, prototype, hook_up_vfunc, + &ObjectBase::hook_up_vfunc, 3, + GJS_MODULE_PROP_FLAGS)) + return false; + + JS_SetPrivate(prototype, ObjectPrototype::new_for_js_object(info, gtype)); gjs_debug(GJS_DEBUG_GOBJECT, "Defined class for %s (%s), prototype %p, " "JSClass %p, in object %p", constructor_name, g_type_name(gtype), prototype.get(), JS_GetClass(prototype), in_object.get()); if (info) - gjs_object_define_static_methods(context, constructor, gtype, info); + if (!gjs_object_define_static_methods(context, constructor, gtype, info)) + return false; JS::RootedObject gtype_obj(context, gjs_gtype_create_gtype_wrapper(context, gtype)); - JS_DefineProperty(context, constructor, "$gtype", gtype_obj, - JSPROP_PERMANENT); + return JS_DefineProperty(context, constructor, "$gtype", gtype_obj, + JSPROP_PERMANENT); } JSObject* @@ -2237,7 +2174,7 @@ if (gobj == NULL) return NULL; - ObjectInstance *priv = get_object_qdata(gobj); + ObjectInstance *priv = ObjectInstance::for_gobject(gobj); if (!priv) { /* We have to create a wrapper */ @@ -2259,31 +2196,37 @@ if (!obj) return nullptr; - priv = init_object_private(context, obj); + priv = ObjectInstance::new_for_js_object(context, obj); + JS_SetPrivate(obj, priv); g_object_ref_sink(gobj); - associate_js_gobject(context, obj, gobj); + priv->associate_js_gobject(context, obj, gobj); - g_assert(priv->keep_alive == obj.get()); + g_assert(priv->wrapper() == obj.get()); } - return priv->keep_alive; + return priv->wrapper(); } GObject* -gjs_g_object_from_object(JSContext *context, +gjs_g_object_from_object(JSContext *cx, JS::HandleObject obj) { - ObjectInstance *priv; - if (!obj) return NULL; - priv = priv_from_js(context, obj); - return priv->gobj; -} + auto* priv = ObjectBase::for_js(cx, obj); + if (!priv || priv->is_prototype()) + return nullptr; -bool + ObjectInstance* instance = priv->to_instance(); + if (!instance->check_gobject_disposed("access")) + return nullptr; + + return instance->gobj(); +} + +bool gjs_typecheck_is_object(JSContext *context, JS::HandleObject object, bool throw_error) @@ -2291,71 +2234,53 @@ return do_base_typecheck(context, object, throw_error); } -bool -gjs_typecheck_object(JSContext *context, - JS::HandleObject object, - GType expected_type, - bool throw_error) -{ - ObjectInstance *priv; - bool result; - - if (!do_base_typecheck(context, object, throw_error)) +bool gjs_typecheck_object(JSContext* cx, JS::HandleObject object, + GType expected_type, bool throw_error) { + if (!do_base_typecheck(cx, object, throw_error)) return false; - priv = priv_from_js(context, object); + auto* priv = ObjectBase::for_js(cx, object); if (priv == NULL) { - if (throw_error) { - gjs_throw(context, - "Object instance or prototype has not been properly initialized yet. " - "Did you forget to chain-up from _init()?"); - } + if (throw_error) + return throw_priv_is_null_error(cx); return false; } - if (priv->gobj == NULL) { - if (throw_error) { - gjs_throw(context, - "Object is %s.%s.prototype, not an object instance - cannot convert to GObject*", - priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "", - priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype)); - } - + /* check_is_instance() throws, so only call if if throw_error. + * is_prototype() checks the same thing without throwing. */ + if ((throw_error && !priv->check_is_instance(cx, "convert to GObject*")) || + priv->is_prototype()) return false; - } - if (priv->g_object_finalized) { - g_critical("Object %s.%s (%p), has been already deallocated - impossible to access to it. " - "This might be caused by the fact that the object has been destroyed from C " - "code using something such as destroy(), dispose(), or remove() vfuncs", - priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "", - priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype), - priv->gobj); - gjs_dumpstack(); - - return true; - } + return priv->to_instance()->typecheck_object(cx, expected_type, + throw_error); +} - g_assert(priv->gtype == G_OBJECT_TYPE(priv->gobj)); +bool +ObjectInstance::typecheck_object(JSContext *context, + GType expected_type, + bool throw_error) +{ + g_assert(m_gobj_disposed || gtype() == G_OBJECT_TYPE(m_gobj)); + bool result; if (expected_type != G_TYPE_NONE) - result = g_type_is_a (priv->gtype, expected_type); + result = g_type_is_a(gtype(), expected_type); else result = true; if (!result && throw_error) { - if (priv->info) { + if (!is_custom_js_class()) { gjs_throw_custom(context, JSProto_TypeError, nullptr, "Object is of type %s.%s - cannot convert to %s", - g_base_info_get_namespace((GIBaseInfo*) priv->info), - g_base_info_get_name((GIBaseInfo*) priv->info), + ns(), name(), g_type_name(expected_type)); } else { gjs_throw_custom(context, JSProto_TypeError, nullptr, "Object is of type %s - cannot convert to %s", - g_type_name(priv->gtype), + type_name(), g_type_name(expected_type)); } } @@ -2364,22 +2289,22 @@ } -static void +static bool find_vfunc_info (JSContext *context, GType implementor_gtype, GIBaseInfo *vfunc_info, const char *vfunc_name, gpointer *implementor_vtable_ret, - GIFieldInfo **field_info_ret) + GjsAutoInfo *field_info_ret) { GType ancestor_gtype; int length, i; GIBaseInfo *ancestor_info; - GIStructInfo *struct_info; + GjsAutoInfo struct_info; gpointer implementor_class; bool is_interface; - *field_info_ret = NULL; + field_info_ret->reset(); *implementor_vtable_ret = NULL; ancestor_info = g_base_info_get_container(vfunc_info); @@ -2396,7 +2321,7 @@ g_type_class_unref(implementor_class); gjs_throw (context, "Couldn't find GType of implementor of interface %s.", g_type_name(ancestor_gtype)); - return; + return false; } *implementor_vtable_ret = implementor_iface_class; @@ -2411,64 +2336,50 @@ length = g_struct_info_get_n_fields(struct_info); for (i = 0; i < length; i++) { - GIFieldInfo *field_info; - GITypeInfo *type_info; - - field_info = g_struct_info_get_field(struct_info, i); - - if (strcmp(g_base_info_get_name((GIBaseInfo*)field_info), vfunc_name) != 0) { - g_base_info_unref(field_info); + GjsAutoInfo field_info = + g_struct_info_get_field(struct_info, i); + if (strcmp(field_info.name(), vfunc_name) != 0) continue; - } - type_info = g_field_info_get_type(field_info); + GjsAutoInfo type_info = g_field_info_get_type(field_info); if (g_type_info_get_tag(type_info) != GI_TYPE_TAG_INTERFACE) { /* We have a field with the same name, but it's not a callback. * There's no hope of being another field with a correct name, * so just abort early. */ - g_base_info_unref(type_info); - g_base_info_unref(field_info); - break; + return true; } else { - g_base_info_unref(type_info); - *field_info_ret = field_info; - break; + *field_info_ret = std::move(field_info); + return true; } } + return true; +} - g_base_info_unref(struct_info); +bool ObjectBase::hook_up_vfunc(JSContext* cx, unsigned argc, JS::Value* vp) { + GJS_GET_PRIV(cx, argc, vp, args, prototype, ObjectBase, priv); + if (!priv) + return throw_priv_is_null_error(cx); + + /* Normally we wouldn't assert is_prototype(), but this method can only be + * called internally so it's OK to crash if done wrongly */ + return priv->to_prototype()->hook_up_vfunc_impl(cx, args, prototype); } -static bool -gjs_hook_up_vfunc(JSContext *cx, - unsigned argc, - JS::Value *vp) -{ - JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); +bool ObjectPrototype::hook_up_vfunc_impl(JSContext* cx, + const JS::CallArgs& args, + JS::HandleObject prototype) { GjsAutoJSChar name; - JS::RootedObject object(cx), function(cx); - ObjectInstance *priv; - GType gtype, info_gtype; - GIObjectInfo *info; - GIVFuncInfo *vfunc; - gpointer implementor_vtable; - GIFieldInfo *field_info; - - if (!gjs_parse_call_args(cx, "hook_up_vfunc", argv, "oso", - "object", &object, + JS::RootedObject function(cx); + if (!gjs_parse_call_args(cx, "hook_up_vfunc", args, "so", "name", &name, "function", &function)) return false; - if (!do_base_typecheck(cx, object, true)) - return false; - - priv = priv_from_js(cx, object); - gtype = priv->gtype; - info = priv->info; + args.rval().setUndefined(); /* find the first class that actually has repository information */ - info_gtype = gtype; + GIObjectInfo *info = m_info; + GType info_gtype = m_gtype; while (!info && info_gtype != G_TYPE_OBJECT) { info_gtype = g_type_parent(info_gtype); @@ -2479,28 +2390,23 @@ * This is awful, so abort now. */ g_assert(info != NULL); - argv.rval().setUndefined(); - - vfunc = find_vfunc_on_parents(info, name, NULL); + GjsAutoInfo vfunc = find_vfunc_on_parents(info, name, nullptr); if (!vfunc) { guint i, n_interfaces; GType *interface_list; - GIInterfaceInfo *interface; - interface_list = g_type_interfaces(gtype, &n_interfaces); + interface_list = g_type_interfaces(m_gtype, &n_interfaces); for (i = 0; i < n_interfaces; i++) { - interface = (GIInterfaceInfo*)g_irepository_find_by_gtype(g_irepository_get_default(), - interface_list[i]); + GjsAutoInfo interface = + g_irepository_find_by_gtype(nullptr, interface_list[i]); /* The interface doesn't have to exist -- it could be private * or dynamic. */ if (interface) { vfunc = g_interface_info_find_vfunc(interface, name); - g_base_info_unref((GIBaseInfo*)interface); - if (vfunc) break; } @@ -2515,7 +2421,12 @@ return false; } - find_vfunc_info(cx, gtype, vfunc, name, &implementor_vtable, &field_info); + void *implementor_vtable; + GjsAutoInfo field_info; + if (!find_vfunc_info(cx, m_gtype, vfunc, name, &implementor_vtable, + &field_info)) + return false; + if (field_info != NULL) { gint offset; gpointer method_ptr; @@ -2525,719 +2436,32 @@ method_ptr = G_STRUCT_MEMBER_P(implementor_vtable, offset); JS::RootedValue v_function(cx, JS::ObjectValue(*function)); - trampoline = gjs_callback_trampoline_new(cx, v_function, vfunc, - GI_SCOPE_TYPE_NOTIFIED, - object, true); + trampoline = gjs_callback_trampoline_new( + cx, v_function, vfunc, GI_SCOPE_TYPE_NOTIFIED, prototype, true); *((ffi_closure **)method_ptr) = trampoline->closure; - - g_base_info_unref(field_info); - } - - g_base_info_unref(vfunc); - return true; -} - -static gchar * -hyphen_to_underscore (gchar *string) -{ - gchar *str, *s; - str = s = g_strdup(string); - while (*(str++) != '\0') { - if (*str == '-') - *str = '_'; - } - return s; -} - -static void -gjs_object_get_gproperty (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - GjsContext *gjs_context; - JSContext *context; - gchar *underscore_name; - ObjectInstance *priv = get_object_qdata(object); - - gjs_context = gjs_context_get_current(); - context = (JSContext*) gjs_context_get_native_context(gjs_context); - - JS::RootedObject js_obj(context, priv->keep_alive); - JS::RootedValue jsvalue(context); - - underscore_name = hyphen_to_underscore((gchar *)pspec->name); - if (!JS_GetProperty(context, js_obj, underscore_name, &jsvalue) || - !gjs_value_to_g_value(context, jsvalue, value)) - gjs_log_exception(context); - g_free (underscore_name); -} - -static void -jsobj_set_gproperty(JSContext *context, - JS::HandleObject object, - const GValue *value, - GParamSpec *pspec) -{ - JS::RootedValue jsvalue(context); - gchar *underscore_name; - - if (!gjs_value_from_g_value(context, &jsvalue, value)) - return; - - underscore_name = hyphen_to_underscore((gchar *)pspec->name); - if (!JS_SetProperty(context, object, underscore_name, jsvalue)) - gjs_log_exception(context); - g_free (underscore_name); -} - -static GObject * -gjs_object_constructor (GType type, - guint n_construct_properties, - GObjectConstructParam *construct_properties) -{ - if (!object_init_list.empty()) { - GType parent_type = g_type_parent(type); - - /* The object is being constructed from JS: - * Simply chain up to the first non-gjs constructor - */ - while (G_OBJECT_CLASS(g_type_class_peek(parent_type))->constructor == gjs_object_constructor) - parent_type = g_type_parent(parent_type); - - return G_OBJECT_CLASS(g_type_class_peek(parent_type))->constructor(type, n_construct_properties, construct_properties); - } - - GjsContext *gjs_context; - JSContext *context; - JSObject *object; - ObjectInstance *priv; - - /* The object is being constructed from native code (e.g. GtkBuilder): - * Construct the JS object from the constructor, then use the GObject - * that was associated in gjs_object_custom_init() - */ - gjs_context = gjs_context_get_current(); - context = (JSContext*) gjs_context_get_native_context(gjs_context); - - JSAutoRequest ar(context); - JSAutoCompartment ac(context, gjs_get_import_global(context)); - - JS::RootedObject constructor(context, - gjs_lookup_object_constructor_from_info(context, NULL, type)); - if (!constructor) - return NULL; - - if (n_construct_properties) { - guint i; - - JS::RootedObject props_hash(context, JS_NewPlainObject(context)); - - for (i = 0; i < n_construct_properties; i++) - jsobj_set_gproperty(context, props_hash, - construct_properties[i].value, - construct_properties[i].pspec); - - JS::AutoValueArray<1> args(context); - args[0].set(JS::ObjectValue(*props_hash)); - object = JS_New(context, constructor, args); - } else { - object = JS_New(context, constructor, JS::HandleValueArray::empty()); } - if (!object) - return NULL; - - priv = (ObjectInstance*) JS_GetPrivate(object); - /* We only hold a toggle ref at this point, add back a ref that the - * native code can own. - */ - return G_OBJECT(g_object_ref(priv->gobj)); -} - -static void -gjs_object_set_gproperty (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - GjsContext *gjs_context; - JSContext *context; - ObjectInstance *priv = get_object_qdata(object); - - gjs_context = gjs_context_get_current(); - context = (JSContext*) gjs_context_get_native_context(gjs_context); - - JS::RootedObject js_obj(context, priv->keep_alive); - jsobj_set_gproperty(context, js_obj, value, pspec); -} - -static bool -gjs_override_property(JSContext *cx, - unsigned argc, - JS::Value *vp) -{ - JS::CallArgs args = JS::CallArgsFromVp(argc, vp); - GjsAutoJSChar name; - JS::RootedObject type(cx); - GParamSpec *pspec; - GParamSpec *new_pspec; - GType gtype; - - if (!gjs_parse_call_args(cx, "override_property", args, "so", - "name", &name, - "type", &type)) - return false; - - if ((gtype = gjs_gtype_get_actual_gtype(cx, type)) == G_TYPE_INVALID) { - gjs_throw(cx, "Invalid parameter type was not a GType"); - return false; - } - - if (g_type_is_a(gtype, G_TYPE_INTERFACE)) { - GTypeInterface *interface_type = - (GTypeInterface *) g_type_default_interface_ref(gtype); - pspec = g_object_interface_find_property(interface_type, name); - g_type_default_interface_unref(interface_type); - } else { - GTypeClass *class_type = (GTypeClass *) g_type_class_ref(gtype); - pspec = g_object_class_find_property(G_OBJECT_CLASS(class_type), name); - g_type_class_unref(class_type); - } - - if (pspec == NULL) { - gjs_throw(cx, "No such property '%s' to override on type '%s'", - name.get(), g_type_name(gtype)); - return false; - } - - new_pspec = g_param_spec_override(name, pspec); - - g_param_spec_set_qdata(new_pspec, gjs_is_custom_property_quark(), GINT_TO_POINTER(1)); - - args.rval().setObject(*gjs_param_from_g_param(cx, new_pspec)); - g_param_spec_unref(new_pspec); - return true; } -static void -gjs_interface_init(GTypeInterface *g_iface, - gpointer iface_data) -{ - GType gtype = G_TYPE_FROM_INTERFACE(g_iface); - - auto found = class_init_properties.find(gtype); - if (found == class_init_properties.end()) - return; - - ParamRefArray& properties = found->second; - for (ParamRef& pspec : properties) { - g_param_spec_set_qdata(pspec.get(), gjs_is_custom_property_quark(), - GINT_TO_POINTER(1)); - g_object_interface_install_property(g_iface, pspec.get()); - } - - class_init_properties.erase(found); -} - -static void -gjs_object_class_init(GObjectClass *klass, - gpointer user_data) -{ - GType gtype = G_OBJECT_CLASS_TYPE(klass); - - klass->constructor = gjs_object_constructor; - klass->set_property = gjs_object_set_gproperty; - klass->get_property = gjs_object_get_gproperty; - - auto found = class_init_properties.find(gtype); - if (found == class_init_properties.end()) - return; - - ParamRefArray& properties = found->second; - unsigned i = 0; - for (ParamRef& pspec : properties) { - g_param_spec_set_qdata(pspec.get(), gjs_is_custom_property_quark(), - GINT_TO_POINTER(1)); - g_object_class_install_property(klass, ++i, pspec.get()); - } - - class_init_properties.erase(found); -} - -static void -gjs_object_custom_init(GTypeInstance *instance, - gpointer klass) -{ - GjsContext *gjs_context; - JSContext *context; - ObjectInstance *priv; - - if (object_init_list.empty()) - return; - - gjs_context = gjs_context_get_current(); - context = (JSContext*) gjs_context_get_native_context(gjs_context); - - JS::RootedObject object(context, object_init_list.top().get()); - priv = (ObjectInstance*) JS_GetPrivate(object); - - if (priv->gtype != G_TYPE_FROM_INSTANCE (instance)) { - /* This is not the most derived instance_init function, - do nothing. - */ - return; - } - - object_init_list.pop(); - - associate_js_gobject(context, object, G_OBJECT (instance)); - - /* Custom JS objects will most likely have visible state, so - * just do this from the start */ - ensure_uses_toggle_ref(context, priv); - - JS::RootedValue v(context); - if (!gjs_object_get_property(context, object, - GJS_STRING_INSTANCE_INIT, &v)) { - gjs_log_exception(context); - return; - } - - if (!v.isObject()) - return; - - JS::RootedValue r(context); - if (!JS_CallFunctionValue(context, object, v, - JS::HandleValueArray::empty(), &r)) - gjs_log_exception(context); -} - -static inline void -gjs_add_interface(GType instance_type, - GType interface_type) -{ - static GInterfaceInfo interface_vtable = { NULL, NULL, NULL }; - - g_type_add_interface_static(instance_type, - interface_type, - &interface_vtable); -} - -static bool -validate_interfaces_and_properties_args(JSContext *cx, - JS::HandleObject interfaces, - JS::HandleObject properties, - uint32_t *n_interfaces, - uint32_t *n_properties) -{ - guint32 n_int, n_prop; - bool is_array; - - if (!JS_IsArrayObject(cx, interfaces, &is_array)) - return false; - if (!is_array) { - gjs_throw(cx, "Invalid parameter interfaces (expected Array)"); - return false; - } - - if (!JS_GetArrayLength(cx, interfaces, &n_int)) - return false; - - if (!JS_IsArrayObject(cx, properties, &is_array)) - return false; - if (!is_array) { - gjs_throw(cx, "Invalid parameter properties (expected Array)"); - return false; - } - - if (!JS_GetArrayLength(cx, properties, &n_prop)) - return false; - - if (n_interfaces != NULL) - *n_interfaces = n_int; - if (n_properties != NULL) - *n_properties = n_prop; - return true; -} - -static bool -get_interface_gtypes(JSContext *cx, - JS::HandleObject interfaces, - uint32_t n_interfaces, - GType *iface_types) -{ - guint32 i; - - for (i = 0; i < n_interfaces; i++) { - JS::RootedValue iface_val(cx); - GType iface_type; - - if (!JS_GetElement(cx, interfaces, i, &iface_val)) - return false; - - if (!iface_val.isObject()) { - gjs_throw (cx, "Invalid parameter interfaces (element %d was not a GType)", i); - return false; - } - - JS::RootedObject iface(cx, &iface_val.toObject()); - iface_type = gjs_gtype_get_actual_gtype(cx, iface); - if (iface_type == G_TYPE_INVALID) { - gjs_throw (cx, "Invalid parameter interfaces (element %d was not a GType)", i); - return false; - } - - iface_types[i] = iface_type; - } - return true; -} - -static bool -save_properties_for_class_init(JSContext *cx, - JS::HandleObject properties, - uint32_t n_properties, - GType gtype) -{ - ParamRefArray properties_native; - JS::RootedValue prop_val(cx); - JS::RootedObject prop_obj(cx); - for (uint32_t i = 0; i < n_properties; i++) { - if (!JS_GetElement(cx, properties, i, &prop_val)) - return false; - - if (!prop_val.isObject()) { - gjs_throw(cx, "Invalid parameter, expected object"); - return false; - } - - prop_obj = &prop_val.toObject(); - if (!gjs_typecheck_param(cx, prop_obj, G_TYPE_NONE, true)) - return false; - - properties_native.emplace_back(g_param_spec_ref(gjs_g_param_from_param(cx, prop_obj)), - g_param_spec_unref); - } - class_init_properties[gtype] = std::move(properties_native); - return true; -} - -static bool -gjs_register_interface(JSContext *cx, - unsigned argc, - JS::Value *vp) -{ - JS::CallArgs args = JS::CallArgsFromVp(argc, vp); - GjsAutoJSChar name; - guint32 i, n_interfaces, n_properties; - GType *iface_types; - GType interface_type; - GTypeInfo type_info = { - sizeof (GTypeInterface), /* class_size */ - - (GBaseInitFunc) NULL, - (GBaseFinalizeFunc) NULL, - - (GClassInitFunc) gjs_interface_init, - (GClassFinalizeFunc) NULL, - NULL, /* class_data */ - - 0, /* instance_size */ - 0, /* n_preallocs */ - NULL, /* instance_init */ - }; - - JS::RootedObject interfaces(cx), properties(cx); - if (!gjs_parse_call_args(cx, "register_interface", args, "soo", - "name", &name, - "interfaces", &interfaces, - "properties", &properties)) - return false; - - if (!validate_interfaces_and_properties_args(cx, interfaces, properties, - &n_interfaces, &n_properties)) - return false; - - iface_types = (GType *) g_alloca(sizeof(GType) * n_interfaces); - - /* We do interface addition in two passes so that any failure - is caught early, before registering the GType (which we can't undo) */ - if (!get_interface_gtypes(cx, interfaces, n_interfaces, iface_types)) - return false; - - if (g_type_from_name(name) != G_TYPE_INVALID) { - gjs_throw(cx, "Type name %s is already registered", name.get()); - return false; - } - - interface_type = g_type_register_static(G_TYPE_INTERFACE, name, &type_info, - (GTypeFlags) 0); - - g_type_set_qdata(interface_type, gjs_is_custom_type_quark(), GINT_TO_POINTER(1)); - - if (!save_properties_for_class_init(cx, properties, n_properties, interface_type)) - return false; - - for (i = 0; i < n_interfaces; i++) - g_type_interface_add_prerequisite(interface_type, iface_types[i]); - - /* create a custom JSClass */ - JS::RootedObject module(cx, gjs_lookup_private_namespace(cx)); - if (!module) - return false; /* error will have been thrown already */ - - JS::RootedObject constructor(cx); - gjs_define_interface_class(cx, module, NULL, interface_type, &constructor); - - args.rval().setObject(*constructor); - return true; -} - -static void -gjs_object_base_init(void *klass) -{ - auto priv = static_cast(g_type_get_qdata(G_OBJECT_CLASS_TYPE(klass), - gjs_object_priv_quark())); - - if (priv) { - for (GClosure *closure : priv->closures) - g_closure_ref(closure); - } -} - -static void -gjs_object_base_finalize(void *klass) -{ - auto priv = static_cast(g_type_get_qdata(G_OBJECT_CLASS_TYPE(klass), - gjs_object_priv_quark())); - - if (priv) { - for (GClosure *closure : priv->closures) - g_closure_unref(closure); - } -} - -static bool -gjs_register_type(JSContext *cx, - unsigned argc, - JS::Value *vp) -{ - JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); - GjsAutoJSChar name; - GType instance_type, parent_type; - GTypeQuery query; - ObjectInstance *parent_priv; - GTypeInfo type_info = { - 0, /* class_size */ - - gjs_object_base_init, - gjs_object_base_finalize, - - (GClassInitFunc) gjs_object_class_init, - (GClassFinalizeFunc) NULL, - NULL, /* class_data */ - - 0, /* instance_size */ - 0, /* n_preallocs */ - gjs_object_custom_init, - }; - guint32 i, n_interfaces, n_properties; - GType *iface_types; - - JSAutoRequest ar(cx); - - JS::RootedObject parent(cx), interfaces(cx), properties(cx); - if (!gjs_parse_call_args(cx, "register_type", argv, "osoo", - "parent", &parent, - "name", &name, - "interfaces", &interfaces, - "properties", &properties)) - return false; - - if (!parent) - return false; - - if (!do_base_typecheck(cx, parent, true)) - return false; - - if (!validate_interfaces_and_properties_args(cx, interfaces, properties, - &n_interfaces, &n_properties)) - return false; - - iface_types = (GType*) g_alloca(sizeof(GType) * n_interfaces); - - /* We do interface addition in two passes so that any failure - is caught early, before registering the GType (which we can't undo) */ - if (!get_interface_gtypes(cx, interfaces, n_interfaces, iface_types)) - return false; - - if (g_type_from_name(name) != G_TYPE_INVALID) { - gjs_throw(cx, "Type name %s is already registered", name.get()); - return false; - } - - parent_priv = priv_from_js(cx, parent); - - /* We checked parent above, in do_base_typecheck() */ - g_assert(parent_priv != NULL); - - parent_type = parent_priv->gtype; - - g_type_query_dynamic_safe(parent_type, &query); - if (G_UNLIKELY (query.type == 0)) { - gjs_throw (cx, "Cannot inherit from a non-gjs dynamic type [bug 687184]"); - return false; - } - - type_info.class_size = query.class_size; - type_info.instance_size = query.instance_size; - - instance_type = g_type_register_static(parent_type, name, &type_info, - (GTypeFlags) 0); - - g_type_set_qdata (instance_type, gjs_is_custom_type_quark(), GINT_TO_POINTER (1)); - - if (!save_properties_for_class_init(cx, properties, n_properties, instance_type)) - return false; - - for (i = 0; i < n_interfaces; i++) - gjs_add_interface(instance_type, iface_types[i]); - - /* create a custom JSClass */ - JS::RootedObject module(cx, gjs_lookup_private_namespace(cx)); - JS::RootedObject constructor(cx), prototype(cx); - gjs_define_object_class(cx, module, nullptr, instance_type, &constructor, - &prototype); - - ObjectInstance *priv = priv_from_js(cx, prototype); - g_type_set_qdata(instance_type, gjs_object_priv_quark(), priv); - - argv.rval().setObject(*constructor); - - return true; -} - -static bool -gjs_signal_new(JSContext *cx, - unsigned argc, - JS::Value *vp) -{ - JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); - GType gtype; - GjsAutoJSChar signal_name; - GSignalAccumulator accumulator; - gint signal_id; - guint i, n_parameters; - GType *params, return_type; - - if (argc != 6) - return false; - - JSAutoRequest ar(cx); - - if (!gjs_string_to_utf8(cx, argv[1], &signal_name)) - return false; - - JS::RootedObject obj(cx, &argv[0].toObject()); - if (!gjs_typecheck_gtype(cx, obj, true)) - return false; - - /* we only support standard accumulators for now */ - switch (argv[3].toInt32()) { - case 1: - accumulator = g_signal_accumulator_first_wins; - break; - case 2: - accumulator = g_signal_accumulator_true_handled; - break; - case 0: - default: - accumulator = NULL; - } - - JS::RootedObject gtype_obj(cx, &argv[4].toObject()); - return_type = gjs_gtype_get_actual_gtype(cx, gtype_obj); - - if (accumulator == g_signal_accumulator_true_handled && return_type != G_TYPE_BOOLEAN) { - gjs_throw (cx, "GObject.SignalAccumulator.TRUE_HANDLED can only be used with boolean signals"); - return false; - } - - JS::RootedObject params_obj(cx, &argv[5].toObject()); - if (!JS_GetArrayLength(cx, params_obj, &n_parameters)) - return false; - - params = g_newa(GType, n_parameters); - JS::RootedValue gtype_val(cx); - for (i = 0; i < n_parameters; i++) { - if (!JS_GetElement(cx, params_obj, i, >ype_val) || - !gtype_val.isObject()) { - gjs_throw(cx, "Invalid signal parameter number %d", i); - return false; - } - - JS::RootedObject gjs_gtype(cx, >ype_val.toObject()); - params[i] = gjs_gtype_get_actual_gtype(cx, gjs_gtype); - } - - gtype = gjs_gtype_get_actual_gtype(cx, obj); - - signal_id = g_signal_newv(signal_name, - gtype, - (GSignalFlags) argv[2].toInt32(), /* signal_flags */ - NULL, /* class closure */ - accumulator, - NULL, /* accu_data */ - g_cclosure_marshal_generic, - return_type, /* return type */ - n_parameters, - params); - - argv.rval().setInt32(signal_id); - return true; -} - -static JSFunctionSpec module_funcs[] = { - JS_FS("override_property", gjs_override_property, 2, GJS_MODULE_PROP_FLAGS), - JS_FS("register_interface", gjs_register_interface, 3, GJS_MODULE_PROP_FLAGS), - JS_FS("register_type", gjs_register_type, 4, GJS_MODULE_PROP_FLAGS), - JS_FS("hook_up_vfunc", gjs_hook_up_vfunc, 3, GJS_MODULE_PROP_FLAGS), - JS_FS("signal_new", gjs_signal_new, 6, GJS_MODULE_PROP_FLAGS), - JS_FS_END, -}; - -bool -gjs_define_private_gi_stuff(JSContext *cx, - JS::MutableHandleObject module) -{ - module.set(JS_NewPlainObject(cx)); - return JS_DefineFunctions(cx, module, &module_funcs[0]); -} - bool gjs_lookup_object_constructor(JSContext *context, GType gtype, JS::MutableHandleValue value_p) { JSObject *constructor; - GIObjectInfo *object_info; - object_info = (GIObjectInfo*)g_irepository_find_by_gtype(NULL, gtype); + GjsAutoInfo object_info = + g_irepository_find_by_gtype(nullptr, gtype); - g_assert(object_info == NULL || - g_base_info_get_type((GIBaseInfo*)object_info) == - GI_INFO_TYPE_OBJECT); + g_assert(!object_info || object_info.type() == GI_INFO_TYPE_OBJECT); constructor = gjs_lookup_object_constructor_from_info(context, object_info, gtype); if (G_UNLIKELY (constructor == NULL)) return false; - if (object_info) - g_base_info_unref((GIBaseInfo*)object_info); - value_p.setObject(*constructor); return true; } @@ -3247,13 +2471,10 @@ JS::HandleObject object, GClosure *closure) { - ObjectInstance *priv = priv_from_js(cx, object); + auto* priv = ObjectBase::for_js(cx, object); if (!priv) return false; - if (priv->gobj) - ensure_uses_toggle_ref(cx, priv); - - do_associate_closure(priv, closure); + priv->associate_closure(cx, closure); return true; } diff -Nru gjs-1.53.3/gi/object.h gjs-1.54.3/gi/object.h --- gjs-1.53.3/gi/object.h 2018-06-18 22:44:01.000000000 +0000 +++ gjs-1.54.3/gi/object.h 2018-11-12 16:43:50.000000000 +0000 @@ -24,15 +24,441 @@ #ifndef __GJS_OBJECT_H__ #define __GJS_OBJECT_H__ -#include - #include #include + +#include +#include +#include +#include + +#include "gjs/jsapi-util-root.h" #include "gjs/jsapi-util.h" +#include "gjs/jsapi-wrapper.h" + +#include "js/GCHashTable.h" + +class ObjectInstance; + +class GjsListLink { + private: + ObjectInstance* m_prev; + ObjectInstance* m_next; + + public: + ObjectInstance* prev(void) const { return m_prev; } + ObjectInstance* next(void) const { return m_next; } + + void prepend(ObjectInstance* this_instance, ObjectInstance* head); + void unlink(void); + size_t size(void) const; +}; + +struct AutoGValueVector : public std::vector { + ~AutoGValueVector() { + for (GValue value : *this) + g_value_unset(&value); + } +}; + +class ObjectPrototype; + +/* To conserve memory, we have two different kinds of private data for GObject + * JS wrappers: ObjectInstance, and ObjectPrototype. Both inherit from + * ObjectBase for their common functionality. + * + * It's important that ObjectBase and ObjectInstance not grow in size without a + * very good reason. There can be tens, maybe hundreds of thousands of these + * objects alive in a typical gnome-shell run, so even 8 more bytes will add up. + * It's less critical that ObjectPrototype stay small, since only one of these + * is allocated per GType. + * + * Sadly, we cannot have virtual methods in ObjectBase, because SpiderMonkey can + * be compiled with or without RTTI, so we cannot count on being able to cast + * ObjectBase to ObjectInstance or ObjectPrototype with dynamic_cast<>, and the + * vtable would take up just as much space anyway. Instead, we have the + * to_prototype() and to_instance() methods which will give you a pointer if the + * ObjectBase is of the correct type (and assert if not.) + */ +class ObjectBase { + protected: + /* nullptr if this is an ObjectPrototype; points to the corresponding + * ObjectPrototype if this is an ObjectInstance */ + ObjectPrototype* m_proto; + + /* a list of all GClosures installed on this object (from + * signals, trampolines, explicit GClosures, and vfuncs on prototypes), + * used when tracing */ + std::forward_list m_closures; + + explicit ObjectBase(ObjectPrototype* proto = nullptr) : m_proto(proto) {} + + /* Methods to get an existing ObjectBase */ + + public: + static ObjectBase* for_js(JSContext* cx, JS::HandleObject obj); + static ObjectBase* for_js_nocheck(JSObject* obj); + + /* Methods for getting a pointer to the correct subclass. We don't use + * standard C++ subclasses because that would occupy another 8 bytes in + * ObjectInstance for a vtable. */ + + bool is_prototype(void) const { return !m_proto; } + + /* The to_instance() and to_prototype() methods assert that this ObjectBase + * is of the correct subclass. If you don't want an assert, then either + * check beforehand or use get_prototype(). */ + + ObjectPrototype* to_prototype(void) { + g_assert(is_prototype()); + return reinterpret_cast(this); + } + const ObjectPrototype* to_prototype(void) const { + g_assert(is_prototype()); + return reinterpret_cast(this); + } + ObjectInstance* to_instance(void) { + g_assert(!is_prototype()); + return reinterpret_cast(this); + } + const ObjectInstance* to_instance(void) const { + g_assert(!is_prototype()); + return reinterpret_cast(this); + } + + /* get_prototype() doesn't assert. If you call it on an ObjectPrototype, it + * returns you the same object cast to the correct type; if you call it on + * an ObjectInstance, it returns you the ObjectPrototype belonging to the + * corresponding JS prototype. */ + ObjectPrototype* get_prototype(void) { + return is_prototype() ? to_prototype() : m_proto; + } + const ObjectPrototype* get_prototype(void) const { + return is_prototype() ? to_prototype() : m_proto; + } + + /* Accessors */ + + /* Both ObjectInstance and ObjectPrototype have GIObjectInfo and GType, + * but for space reasons we store it only on ObjectPrototype. */ + GIObjectInfo* info(void) const; + GType gtype(void) const; + + const char* ns(void) const { + return info() ? g_base_info_get_namespace(info()) : ""; + } + const char* name(void) const { + return info() ? g_base_info_get_name(info()) : type_name(); + } + const char* type_name(void) const { return g_type_name(gtype()); } + bool is_custom_js_class(void) const { return !info(); } + + private: + /* These are used in debug methods only. */ + const void* gobj_addr(void) const; + const void* jsobj_addr(void) const; + + /* Helper methods */ + + public: + bool check_is_instance(JSContext* cx, const char* for_what) const; + + protected: + void debug_lifecycle(const char* message) const { + gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, + "[%p: GObject %p JS wrapper %p %s.%s (%s)] %s", + this, gobj_addr(), jsobj_addr(), ns(), name(), + type_name(), message); + } + void debug_jsprop_base(const char* message, const char* id, + JSObject* obj) const { + gjs_debug_jsprop(GJS_DEBUG_GOBJECT, + "[%p: GObject %p JS object %p %s.%s (%s)] %s '%s'", + this, gobj_addr(), obj, ns(), name(), type_name(), + message, id); + } + void debug_jsprop(const char* message, jsid id, JSObject* obj) const { + debug_jsprop_base(message, gjs_debug_id(id).c_str(), obj); + } + void debug_jsprop(const char* message, JSString* id, JSObject* obj) const { + debug_jsprop_base(message, gjs_debug_string(id).c_str(), obj); + } + static void debug_jsprop_static(const char* message, jsid id, + JSObject* obj) { + gjs_debug_jsprop(GJS_DEBUG_GOBJECT, + "[JS object %p] %s '%s', no instance associated", obj, + message, gjs_debug_id(id).c_str()); + } + + public: + void type_query_dynamic_safe(GTypeQuery* query); + + /* Methods to manipulate the list of closures */ + + protected: + void invalidate_all_closures(void); + + public: + void associate_closure(JSContext* cx, GClosure* closure); + static void closure_invalidated_notify(void* data, GClosure* closure); + + /* JSClass operations */ + + static bool add_property(JSContext* cx, JS::HandleObject obj, + JS::HandleId id, JS::HandleValue value); + static bool resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, + bool* resolved); + static void finalize(JSFreeOp* fop, JSObject* obj); + static void trace(JSTracer* tracer, JSObject* obj); + + protected: + void trace_impl(JSTracer* tracer); + + /* JS property getters/setters */ + + public: + static bool prop_getter(JSContext* cx, unsigned argc, JS::Value* vp); + static bool field_getter(JSContext* cx, unsigned argc, JS::Value* vp); + static bool prop_setter(JSContext* cx, unsigned argc, JS::Value* vp); + static bool field_setter(JSContext* cx, unsigned argc, JS::Value* vp); + + /* JS methods */ + + static bool connect(JSContext* cx, unsigned argc, JS::Value* vp); + static bool connect_after(JSContext* cx, unsigned argc, JS::Value* vp); + static bool emit(JSContext* cx, unsigned argc, JS::Value* vp); + static bool to_string(JSContext* cx, unsigned argc, JS::Value* vp); + static bool init(JSContext* cx, unsigned argc, JS::Value* vp); + static bool hook_up_vfunc(JSContext* cx, unsigned argc, JS::Value* vp); + + /* Quarks */ + + static GQuark custom_type_quark(void); + static GQuark custom_property_quark(void); +}; + +class ObjectPrototype : public ObjectBase { + // ObjectBase needs to call private methods (such as trace_impl) because + // of the unusual inheritance scheme + friend class ObjectBase; + + using PropertyCache = + JS::GCHashMap, GjsAutoParam, + js::DefaultHasher, js::SystemAllocPolicy>; + using FieldCache = + JS::GCHashMap, GjsAutoInfo, + js::DefaultHasher, js::SystemAllocPolicy>; + + GIObjectInfo* m_info; + GType m_gtype; + + PropertyCache m_property_cache; + FieldCache m_field_cache; + + ObjectPrototype(GIObjectInfo* info, GType gtype); + ~ObjectPrototype(); + + public: + /* Public constructor for instances (uses GSlice allocator) */ + static ObjectPrototype* new_for_js_object(GIObjectInfo* info, GType gtype); + + static ObjectPrototype* for_js(JSContext* cx, JS::HandleObject obj) { + return ObjectBase::for_js(cx, obj)->to_prototype(); + } + static ObjectPrototype* for_gtype(GType gtype); + static ObjectPrototype* for_js_prototype(JSContext* cx, + JS::HandleObject obj); + + /* Helper methods */ + private: + bool is_vfunc_unchanged(GIVFuncInfo* info); + bool lazy_define_gobject_property(JSContext* cx, JS::HandleObject obj, + JS::HandleId id, bool* resolved, + const char* name); + enum ResolveWhat { ConsiderOnlyMethods, ConsiderMethodsAndProperties }; + bool resolve_no_info(JSContext* cx, JS::HandleObject obj, JS::HandleId id, + bool* resolved, const char* name, + ResolveWhat resolve_props); + + public: + void set_type_qdata(void); + GParamSpec* find_param_spec_from_id(JSContext* cx, JS::HandleString key); + GIFieldInfo* find_field_info_from_id(JSContext* cx, JS::HandleString key); + bool props_to_g_parameters(JSContext* cx, const JS::HandleValueArray& args, + std::vector* names, + AutoGValueVector* values); + + /* These are currently only needed in the GObject base init and finalize + * functions, for prototypes, even though m_closures is in ObjectBase. */ + void ref_closures(void) { + for (GClosure* closure : m_closures) + g_closure_ref(closure); + } + void unref_closures(void) { + for (GClosure* closure : m_closures) + g_closure_unref(closure); + } + + /* JSClass operations */ + private: + bool resolve_impl(JSContext* cx, JS::HandleObject obj, JS::HandleId id, + bool* resolved); + void trace_impl(JSTracer* tracer); + + /* JS methods */ + private: + bool to_string_impl(JSContext* cx, const JS::CallArgs& args); + bool hook_up_vfunc_impl(JSContext* cx, const JS::CallArgs& args, + JS::HandleObject prototype); + + ObjectPrototype(const ObjectPrototype& other) = delete; + ObjectPrototype(ObjectPrototype&& other) = delete; + ObjectPrototype& operator=(const ObjectPrototype& other) = delete; + ObjectPrototype& operator=(ObjectPrototype&& other) = delete; +}; + +class ObjectInstance : public ObjectBase { + // ObjectBase needs to call private methods (such as trace_impl) because + // of the unusual inheritance scheme + friend class ObjectBase; + + GObject* m_gobj; // may be null + GjsMaybeOwned m_wrapper; + + GjsListLink m_instance_link; + + bool m_wrapper_finalized : 1; + bool m_gobj_disposed : 1; + + /* True if this object has visible JS state, and thus its lifecycle is + * managed using toggle references. False if this object just keeps a + * hard ref on the underlying GObject, and may be finalized at will. */ + bool m_uses_toggle_ref : 1; + + public: + static std::stack object_init_list; + + /* Constructors */ + + private: + ObjectInstance(JSContext* cx, JS::HandleObject obj); + ~ObjectInstance(); + + public: + /* Public constructor for instances (uses GSlice allocator) */ + static ObjectInstance* new_for_js_object(JSContext* cx, + JS::HandleObject obj); + + static ObjectInstance* for_gobject(GObject* gobj); + + /* Accessors */ + + private: + bool has_wrapper(void) const { return !!m_wrapper; } + + public: + GObject* gobj(void) const { return m_gobj; } + JSObject* wrapper(void) const { return m_wrapper; } + + /* Methods to manipulate the JS object wrapper */ + + private: + void discard_wrapper(void) { m_wrapper.reset(); } + void switch_to_rooted(JSContext* cx) { m_wrapper.switch_to_rooted(cx); } + void switch_to_unrooted(void) { m_wrapper.switch_to_unrooted(); } + bool update_after_gc(void) { return m_wrapper.update_after_gc(); } + + public: + bool wrapper_is_rooted(void) const { return m_wrapper.rooted(); } + void release_native_object(void); + void associate_js_gobject(JSContext* cx, JS::HandleObject obj, + GObject* gobj); + void disassociate_js_gobject(void); + bool weak_pointer_was_finalized(void); + void toggle_down(void); + void toggle_up(void); + + /* Helper methods */ + + private: + void set_object_qdata(void); + void unset_object_qdata(void); + void check_js_object_finalized(void); + + public: + void ensure_uses_toggle_ref(JSContext* cx); + bool check_gobject_disposed(const char* for_what) const; + + /* Methods to manipulate the linked list of instances */ + + private: + static ObjectInstance* wrapped_gobject_list; + ObjectInstance* next(void) const { return m_instance_link.next(); } + void link(void); + void unlink(void); + + public: + GjsListLink* get_link(void) { return &m_instance_link; } + static size_t num_wrapped_gobjects(void) { + return wrapped_gobject_list + ? wrapped_gobject_list->m_instance_link.size() + : 0; + } + using Action = std::function; + using Predicate = std::function; + static void iterate_wrapped_gobjects(Action action); + static void remove_wrapped_gobjects_if(Predicate predicate, Action action); + + /* JSClass operations */ + + private: + bool add_property_impl(JSContext* cx, JS::HandleObject obj, JS::HandleId id, + JS::HandleValue value); + void finalize_impl(JSFreeOp* fop, JSObject* obj); + + /* JS property getters/setters */ + + private: + bool prop_getter_impl(JSContext* cx, JS::HandleObject obj, + JS::HandleString name, JS::MutableHandleValue rval); + bool field_getter_impl(JSContext* cx, JS::HandleObject obj, + JS::HandleString name, JS::MutableHandleValue rval); + bool prop_setter_impl(JSContext* cx, JS::HandleObject obj, + JS::HandleString name, JS::HandleValue value); + bool field_setter_impl(JSContext* cx, JS::HandleObject obj, + JS::HandleString name, JS::HandleValue value); + + /* JS methods */ + + private: + bool connect_impl(JSContext* cx, const JS::CallArgs& args, bool after); + bool emit_impl(JSContext* cx, const JS::CallArgs& args); + bool to_string_impl(JSContext* cx, const JS::CallArgs& args); + bool init_impl(JSContext* cx, const JS::CallArgs& args, + JS::MutableHandleObject obj); + + /* Methods connected to "public" API */ + private: + static JS::PersistentRootedSymbol hook_up_vfunc_root; + + public: + bool typecheck_object(JSContext* cx, GType expected_type, bool throw_error); + static JS::Symbol* hook_up_vfunc_symbol(JSContext* cx); + + /* Notification callbacks */ + + void gobj_dispose_notify(void); + void context_dispose_notify(void); + + ObjectInstance(const ObjectInstance& other) = delete; + ObjectInstance(ObjectInstance&& other) = delete; + ObjectInstance& operator=(const ObjectInstance& other) = delete; + ObjectInstance& operator=(ObjectInstance&& other) = delete; +}; G_BEGIN_DECLS -void gjs_define_object_class(JSContext *context, +bool gjs_define_object_class(JSContext *cx, JS::HandleObject in_object, GIObjectInfo *info, GType gtype, @@ -42,6 +468,9 @@ bool gjs_lookup_object_constructor(JSContext *context, GType gtype, JS::MutableHandleValue value_p); +JSObject* gjs_lookup_object_constructor_from_info(JSContext* cx, + GIObjectInfo* info, + GType gtype); JSObject* gjs_object_from_g_object (JSContext *context, GObject *gobj); @@ -64,14 +493,11 @@ void gjs_object_context_dispose_notify(void *data, GObject *where_the_object_was); -void gjs_object_define_static_methods(JSContext *context, +bool gjs_object_define_static_methods(JSContext *context, JS::HandleObject constructor, GType gtype, GIObjectInfo *object_info); -bool gjs_define_private_gi_stuff(JSContext *cx, - JS::MutableHandleObject module); - bool gjs_object_associate_closure(JSContext *cx, JS::HandleObject obj, GClosure *closure); diff -Nru gjs-1.53.3/gi/param.cpp gjs-1.54.3/gi/param.cpp --- gjs-1.53.3/gi/param.cpp 2018-06-18 22:44:01.000000000 +0000 +++ gjs-1.54.3/gi/param.cpp 2018-11-12 16:43:50.000000000 +0000 @@ -144,15 +144,13 @@ * class have. */ static const struct JSClassOps gjs_param_class_ops = { - NULL, /* addProperty */ - NULL, /* deleteProperty */ - NULL, /* getProperty */ - NULL, /* setProperty */ - NULL, /* enumerate */ + nullptr, // addProperty + nullptr, // deleteProperty + nullptr, // enumerate + nullptr, // newEnumerate param_resolve, - nullptr, /* mayResolve */ - param_finalize -}; + nullptr, // mayResolve + param_finalize}; struct JSClass gjs_param_class = { "GObject_ParamSpec", @@ -202,13 +200,12 @@ return value.toObjectOrNull(); } -void +bool gjs_define_param_class(JSContext *context, JS::HandleObject in_object) { const char *constructor_name; JS::RootedObject prototype(context), constructor(context); - GIObjectInfo *info; constructor_name = "ParamSpec"; @@ -225,21 +222,25 @@ /* funcs of constructor, MyConstructor.myfunc() */ gjs_param_constructor_funcs, &prototype, - &constructor)) { - g_error("Can't init class %s", constructor_name); - } + &constructor)) + return false; JS::RootedObject gtype_obj(context, gjs_gtype_create_gtype_wrapper(context, G_TYPE_PARAM)); - JS_DefineProperty(context, constructor, "$gtype", gtype_obj, JSPROP_PERMANENT); - - info = (GIObjectInfo*)g_irepository_find_by_gtype(g_irepository_get_default(), G_TYPE_PARAM); - gjs_object_define_static_methods(context, constructor, G_TYPE_PARAM, info); - g_base_info_unref( (GIBaseInfo*) info); + if (!gtype_obj || + !JS_DefineProperty(context, constructor, "$gtype", gtype_obj, + JSPROP_PERMANENT)) + return false; + + GjsAutoInfo info = + g_irepository_find_by_gtype(g_irepository_get_default(), G_TYPE_PARAM); + if (!gjs_object_define_static_methods(context, constructor, G_TYPE_PARAM, info)) + return false; gjs_debug(GJS_DEBUG_GPARAM, "Defined class %s prototype is %p class %p in object %p", constructor_name, prototype.get(), JS_GetClass(prototype), in_object.get()); + return true; } JSObject* diff -Nru gjs-1.53.3/gi/param.h gjs-1.54.3/gi/param.h --- gjs-1.53.3/gi/param.h 2018-06-18 22:44:01.000000000 +0000 +++ gjs-1.54.3/gi/param.h 2018-11-12 16:43:50.000000000 +0000 @@ -31,7 +31,7 @@ G_BEGIN_DECLS -void gjs_define_param_class(JSContext *context, +bool gjs_define_param_class(JSContext *context, JS::HandleObject in_object); GParamSpec *gjs_g_param_from_param (JSContext *context, diff -Nru gjs-1.53.3/gi/private.cpp gjs-1.54.3/gi/private.cpp --- gjs-1.53.3/gi/private.cpp 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/gi/private.cpp 2018-11-12 16:43:50.000000000 +0000 @@ -0,0 +1,414 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ +/* + * Copyright (c) 2008 litl, LLC + * Copyright (c) 2018 Philip Chimento + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include + +#include + +#include "gjs/jsapi-util-args.h" +#include "gjs/jsapi-util.h" +#include "gjs/jsapi-wrapper.h" +#include "gobject.h" +#include "interface.h" +#include "object.h" +#include "param.h" +#include "private.h" +#include "repo.h" + +/* gi/private.cpp - private "imports._gi" module with operations that we need + * to use from JS in order to create GObject classes, but should not be exposed + * to client code. + */ + +static bool gjs_override_property(JSContext* cx, unsigned argc, JS::Value* vp) { + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + GjsAutoJSChar name; + JS::RootedObject type(cx); + + if (!gjs_parse_call_args(cx, "override_property", args, "so", "name", &name, + "type", &type)) + return false; + + GType gtype = gjs_gtype_get_actual_gtype(cx, type); + if (gtype == G_TYPE_INVALID) { + gjs_throw(cx, "Invalid parameter type was not a GType"); + return false; + } + + GParamSpec* pspec; + if (g_type_is_a(gtype, G_TYPE_INTERFACE)) { + auto* interface_type = + static_cast(g_type_default_interface_ref(gtype)); + pspec = g_object_interface_find_property(interface_type, name); + g_type_default_interface_unref(interface_type); + } else { + auto* class_type = static_cast(g_type_class_ref(gtype)); + pspec = g_object_class_find_property(G_OBJECT_CLASS(class_type), name); + g_type_class_unref(class_type); + } + + if (!pspec) { + gjs_throw(cx, "No such property '%s' to override on type '%s'", + name.get(), g_type_name(gtype)); + return false; + } + + GjsAutoParam new_pspec = g_param_spec_override(name, pspec); + + g_param_spec_set_qdata(new_pspec, ObjectBase::custom_property_quark(), + GINT_TO_POINTER(1)); + + args.rval().setObject(*gjs_param_from_g_param(cx, new_pspec.get())); + + return true; +} + +static bool validate_interfaces_and_properties_args(JSContext* cx, + JS::HandleObject interfaces, + JS::HandleObject properties, + uint32_t* n_interfaces, + uint32_t* n_properties) { + bool is_array; + if (!JS_IsArrayObject(cx, interfaces, &is_array)) + return false; + if (!is_array) { + gjs_throw(cx, "Invalid parameter interfaces (expected Array)"); + return false; + } + + uint32_t n_int; + if (!JS_GetArrayLength(cx, interfaces, &n_int)) + return false; + + if (!JS_IsArrayObject(cx, properties, &is_array)) + return false; + if (!is_array) { + gjs_throw(cx, "Invalid parameter properties (expected Array)"); + return false; + } + + uint32_t n_prop; + if (!JS_GetArrayLength(cx, properties, &n_prop)) + return false; + + if (n_interfaces) + *n_interfaces = n_int; + if (n_properties) + *n_properties = n_prop; + return true; +} + +static bool save_properties_for_class_init(JSContext* cx, + JS::HandleObject properties, + uint32_t n_properties, GType gtype) { + AutoParamArray properties_native; + JS::RootedValue prop_val(cx); + JS::RootedObject prop_obj(cx); + for (uint32_t i = 0; i < n_properties; i++) { + if (!JS_GetElement(cx, properties, i, &prop_val)) + return false; + + if (!prop_val.isObject()) { + gjs_throw(cx, "Invalid parameter, expected object"); + return false; + } + + prop_obj = &prop_val.toObject(); + if (!gjs_typecheck_param(cx, prop_obj, G_TYPE_NONE, true)) + return false; + + properties_native.emplace_back( + g_param_spec_ref(gjs_g_param_from_param(cx, prop_obj))); + } + push_class_init_properties(gtype, &properties_native); + return true; +} + +static bool get_interface_gtypes(JSContext* cx, JS::HandleObject interfaces, + uint32_t n_interfaces, GType* iface_types) { + for (uint32_t ix = 0; ix < n_interfaces; ix++) { + JS::RootedValue iface_val(cx); + if (!JS_GetElement(cx, interfaces, ix, &iface_val)) + return false; + + if (!iface_val.isObject()) { + gjs_throw( + cx, "Invalid parameter interfaces (element %d was not a GType)", + ix); + return false; + } + + JS::RootedObject iface(cx, &iface_val.toObject()); + GType iface_type = gjs_gtype_get_actual_gtype(cx, iface); + if (iface_type == G_TYPE_INVALID) { + gjs_throw( + cx, "Invalid parameter interfaces (element %d was not a GType)", + ix); + return false; + } + + iface_types[ix] = iface_type; + } + return true; +} + +static bool gjs_register_interface(JSContext* cx, unsigned argc, + JS::Value* vp) { + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + + GjsAutoJSChar name; + JS::RootedObject interfaces(cx), properties(cx); + if (!gjs_parse_call_args(cx, "register_interface", args, "soo", "name", + &name, "interfaces", &interfaces, "properties", + &properties)) + return false; + + uint32_t n_interfaces, n_properties; + if (!validate_interfaces_and_properties_args(cx, interfaces, properties, + &n_interfaces, &n_properties)) + return false; + + GType* iface_types = g_newa(GType, n_interfaces); + + /* We do interface addition in two passes so that any failure + is caught early, before registering the GType (which we can't undo) */ + if (!get_interface_gtypes(cx, interfaces, n_interfaces, iface_types)) + return false; + + if (g_type_from_name(name) != G_TYPE_INVALID) { + gjs_throw(cx, "Type name %s is already registered", name.get()); + return false; + } + + GTypeInfo type_info = gjs_gobject_interface_info; + GType interface_type = g_type_register_static(G_TYPE_INTERFACE, name, + &type_info, GTypeFlags(0)); + + g_type_set_qdata(interface_type, ObjectBase::custom_type_quark(), + GINT_TO_POINTER(1)); + + if (!save_properties_for_class_init(cx, properties, n_properties, + interface_type)) + return false; + + for (uint32_t ix = 0; ix < n_interfaces; ix++) + g_type_interface_add_prerequisite(interface_type, iface_types[ix]); + + /* create a custom JSClass */ + JS::RootedObject module(cx, gjs_lookup_private_namespace(cx)); + if (!module) + return false; // error will have been thrown already + + JS::RootedObject constructor(cx); + gjs_define_interface_class(cx, module, nullptr, interface_type, + &constructor); + + args.rval().setObject(*constructor); + return true; +} + +static inline void gjs_add_interface(GType instance_type, + GType interface_type) { + static GInterfaceInfo interface_vtable{nullptr, nullptr, nullptr}; + g_type_add_interface_static(instance_type, interface_type, + &interface_vtable); +} + +static bool gjs_register_type(JSContext* cx, unsigned argc, JS::Value* vp) { + JS::CallArgs argv = JS::CallArgsFromVp(argc, vp); + + JSAutoRequest ar(cx); + + GjsAutoJSChar name; + JS::RootedObject parent(cx), interfaces(cx), properties(cx); + if (!gjs_parse_call_args(cx, "register_type", argv, "osoo", "parent", + &parent, "name", &name, "interfaces", &interfaces, + "properties", &properties)) + return false; + + if (!parent) + return false; + + if (!gjs_typecheck_is_object(cx, parent, true)) + return false; + + uint32_t n_interfaces, n_properties; + if (!validate_interfaces_and_properties_args(cx, interfaces, properties, + &n_interfaces, &n_properties)) + return false; + + auto* iface_types = + static_cast(g_alloca(sizeof(GType) * n_interfaces)); + + /* We do interface addition in two passes so that any failure + is caught early, before registering the GType (which we can't undo) */ + if (!get_interface_gtypes(cx, interfaces, n_interfaces, iface_types)) + return false; + + if (g_type_from_name(name) != G_TYPE_INVALID) { + gjs_throw(cx, "Type name %s is already registered", name.get()); + return false; + } + + auto* parent_priv = ObjectPrototype::for_js(cx, parent); + /* We checked parent above, in gjs_typecheck_is_object() */ + g_assert(parent_priv); + + GTypeQuery query; + parent_priv->type_query_dynamic_safe(&query); + if (G_UNLIKELY(query.type == 0)) { + gjs_throw(cx, + "Cannot inherit from a non-gjs dynamic type [bug 687184]"); + return false; + } + + GTypeInfo type_info = gjs_gobject_class_info; + type_info.class_size = query.class_size; + type_info.instance_size = query.instance_size; + + GType instance_type = g_type_register_static(parent_priv->gtype(), name, + &type_info, GTypeFlags(0)); + + g_type_set_qdata(instance_type, ObjectBase::custom_type_quark(), + GINT_TO_POINTER(1)); + + if (!save_properties_for_class_init(cx, properties, n_properties, + instance_type)) + return false; + + for (uint32_t ix = 0; ix < n_interfaces; ix++) + gjs_add_interface(instance_type, iface_types[ix]); + + /* create a custom JSClass */ + JS::RootedObject module(cx, gjs_lookup_private_namespace(cx)); + JS::RootedObject constructor(cx), prototype(cx); + gjs_define_object_class(cx, module, nullptr, instance_type, &constructor, + &prototype); + + auto* priv = ObjectPrototype::for_js(cx, prototype); + priv->set_type_qdata(); + + argv.rval().setObject(*constructor); + + return true; +} + +static bool gjs_signal_new(JSContext* cx, unsigned argc, JS::Value* vp) { + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + + JSAutoRequest ar(cx); + + GjsAutoJSChar signal_name; + int32_t flags, accumulator_enum; + JS::RootedObject gtype_obj(cx), return_gtype_obj(cx), params_obj(cx); + if (!gjs_parse_call_args(cx, "signal_new", args, "osiioo", "gtype", + >ype_obj, "signal name", &signal_name, "flags", + &flags, "accumulator", &accumulator_enum, + "return gtype", &return_gtype_obj, "params", + ¶ms_obj)) + return false; + + if (!gjs_typecheck_gtype(cx, gtype_obj, true)) + return false; + + /* we only support standard accumulators for now */ + GSignalAccumulator accumulator; + switch (accumulator_enum) { + case 1: + accumulator = g_signal_accumulator_first_wins; + break; + case 2: + accumulator = g_signal_accumulator_true_handled; + break; + case 0: + default: + accumulator = NULL; + } + + GType return_type = gjs_gtype_get_actual_gtype(cx, return_gtype_obj); + + if (accumulator == g_signal_accumulator_true_handled && + return_type != G_TYPE_BOOLEAN) { + gjs_throw(cx, + "GObject.SignalAccumulator.TRUE_HANDLED can only be used " + "with boolean signals"); + return false; + } + + uint32_t n_parameters; + if (!JS_GetArrayLength(cx, params_obj, &n_parameters)) + return false; + + GType* params = g_newa(GType, n_parameters); + JS::RootedValue gtype_val(cx); + for (uint32_t ix = 0; ix < n_parameters; ix++) { + if (!JS_GetElement(cx, params_obj, ix, >ype_val) || + !gtype_val.isObject()) { + gjs_throw(cx, "Invalid signal parameter number %d", ix); + return false; + } + + JS::RootedObject gjs_gtype(cx, >ype_val.toObject()); + params[ix] = gjs_gtype_get_actual_gtype(cx, gjs_gtype); + } + + GType gtype = gjs_gtype_get_actual_gtype(cx, gtype_obj); + + unsigned signal_id = g_signal_newv( + signal_name, gtype, GSignalFlags(flags), nullptr, /* class closure */ + accumulator, nullptr, /* accu_data */ + g_cclosure_marshal_generic, return_type, n_parameters, params); + + // FIXME: what if ID is greater than int32 max? + args.rval().setInt32(signal_id); + return true; +} + +static bool hook_up_vfunc_symbol_getter(JSContext* cx, unsigned argc, + JS::Value* vp) { + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + args.rval().setSymbol(ObjectInstance::hook_up_vfunc_symbol(cx)); + return true; +} + +static JSFunctionSpec module_funcs[] = { + JS_FN("override_property", gjs_override_property, 2, GJS_MODULE_PROP_FLAGS), + JS_FN("register_interface", gjs_register_interface, 3, + GJS_MODULE_PROP_FLAGS), + JS_FN("register_type", gjs_register_type, 4, GJS_MODULE_PROP_FLAGS), + JS_FN("signal_new", gjs_signal_new, 6, GJS_MODULE_PROP_FLAGS), + JS_FS_END, +}; + +static JSPropertySpec module_props[] = { + JS_PSG("hook_up_vfunc_symbol", hook_up_vfunc_symbol_getter, + GJS_MODULE_PROP_FLAGS), + JS_PS_END}; + +bool gjs_define_private_gi_stuff(JSContext* cx, + JS::MutableHandleObject module) { + module.set(JS_NewPlainObject(cx)); + return JS_DefineFunctions(cx, module, module_funcs) && + JS_DefineProperties(cx, module, module_props); +} diff -Nru gjs-1.53.3/gi/private.h gjs-1.54.3/gi/private.h --- gjs-1.53.3/gi/private.h 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/gi/private.h 2018-11-12 16:43:50.000000000 +0000 @@ -0,0 +1,31 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ +/* + * Copyright (c) 2008 litl, LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef GI_PRIVATE_H_ +#define GI_PRIVATE_H_ + +#include "gjs/jsapi-wrapper.h" + +bool gjs_define_private_gi_stuff(JSContext* cx, JS::MutableHandleObject module); + +#endif // GI_PRIVATE_H_ diff -Nru gjs-1.53.3/gi/proxyutils.cpp gjs-1.54.3/gi/proxyutils.cpp --- gjs-1.53.3/gi/proxyutils.cpp 2018-06-18 22:44:01.000000000 +0000 +++ gjs-1.54.3/gi/proxyutils.cpp 2018-11-08 04:20:06.000000000 +0000 @@ -74,3 +74,22 @@ g_string_free (buf, true); return ret; } + +bool +_gjs_proxy_throw_nonexistent_field(JSContext *cx, + GType gtype, + const char *field_name) +{ + gjs_throw(cx, "No property %s on %s", field_name, g_type_name(gtype)); + return false; +} + +bool +_gjs_proxy_throw_readonly_field(JSContext *cx, + GType gtype, + const char *field_name) +{ + gjs_throw(cx, "Property %s.%s is not writable", g_type_name(gtype), + field_name); + return false; +} diff -Nru gjs-1.53.3/gi/proxyutils.h gjs-1.54.3/gi/proxyutils.h --- gjs-1.53.3/gi/proxyutils.h 2018-06-18 22:44:01.000000000 +0000 +++ gjs-1.54.3/gi/proxyutils.h 2018-11-12 16:43:50.000000000 +0000 @@ -36,6 +36,14 @@ gpointer native_address, JS::MutableHandleValue ret); +bool _gjs_proxy_throw_nonexistent_field(JSContext *cx, + GType gtype, + const char *field_name); + +bool _gjs_proxy_throw_readonly_field(JSContext *cx, + GType gtype, + const char *field_name); + G_END_DECLS #endif /* __GJS_OBJECT_H__ */ diff -Nru gjs-1.53.3/gi/repo.cpp gjs-1.54.3/gi/repo.cpp --- gjs-1.53.3/gi/repo.cpp 2018-06-18 22:44:01.000000000 +0000 +++ gjs-1.54.3/gi/repo.cpp 2018-11-12 16:43:50.000000000 +0000 @@ -222,15 +222,13 @@ * class have. */ static const struct JSClassOps gjs_repo_class_ops = { - NULL, /* addProperty */ - NULL, /* deleteProperty */ - NULL, /* getProperty */ - NULL, /* setProperty */ - NULL, /* enumerate */ + nullptr, // addProperty + nullptr, // deleteProperty + nullptr, // enumerate + nullptr, // newEnumerate repo_resolve, - nullptr, /* mayResolve */ - repo_finalize -}; + nullptr, // mayResolve + repo_finalize}; struct JSClass gjs_repo_class = { "GIRepository", /* means "new GIRepository()" works */ @@ -425,11 +423,13 @@ gtype = g_registered_type_info_get_g_type((GIRegisteredTypeInfo*)info); if (g_type_is_a (gtype, G_TYPE_PARAM)) { - gjs_define_param_class(context, in_object); + if (!gjs_define_param_class(context, in_object)) + return false; } else if (g_type_is_a (gtype, G_TYPE_OBJECT)) { JS::RootedObject ignored1(context), ignored2(context); - gjs_define_object_class(context, in_object, info, gtype, - &ignored1, &ignored2); + if (!gjs_define_object_class(context, in_object, info, gtype, + &ignored1, &ignored2)) + return false; } else if (G_TYPE_IS_INSTANTIATABLE(gtype)) { JS::RootedObject ignored1(context), ignored2(context); if (!gjs_define_fundamental_class(context, in_object, @@ -698,32 +698,6 @@ } char* -gjs_camel_from_hyphen(const char *hyphen_name) -{ - GString *s; - const char *p; - bool next_upper; - - s = g_string_sized_new(strlen(hyphen_name) + 1); - - next_upper = false; - for (p = hyphen_name; *p; ++p) { - if (*p == '-' || *p == '_') { - next_upper = true; - } else { - if (next_upper) { - g_string_append_c(s, g_ascii_toupper(*p)); - next_upper = false; - } else { - g_string_append_c(s, *p); - } - } - } - - return g_string_free(s, false); -} - -char* gjs_hyphen_from_camel(const char *camel_name) { GString *s; diff -Nru gjs-1.53.3/gi/repo.h gjs-1.54.3/gi/repo.h --- gjs-1.53.3/gi/repo.h 2018-03-29 06:04:18.000000000 +0000 +++ gjs-1.54.3/gi/repo.h 2018-11-12 16:43:50.000000000 +0000 @@ -55,7 +55,6 @@ GIBaseInfo *info, bool *defined); -char* gjs_camel_from_hyphen (const char *hyphen_name); char* gjs_hyphen_from_camel (const char *camel_name); diff -Nru gjs-1.53.3/gi/union.cpp gjs-1.54.3/gi/union.cpp --- gjs-1.53.3/gi/union.cpp 2018-05-31 05:34:45.000000000 +0000 +++ gjs-1.54.3/gi/union.cpp 2018-11-12 16:43:50.000000000 +0000 @@ -286,15 +286,13 @@ * class have. */ static const struct JSClassOps gjs_union_class_ops = { - NULL, /* addProperty */ - NULL, /* deleteProperty */ - NULL, /* getProperty */ - NULL, /* setProperty */ - NULL, /* enumerate */ + nullptr, // addProperty + nullptr, // deleteProperty + nullptr, // enumerate + nullptr, // newEnumerate union_resolve, - nullptr, /* mayResolve */ - union_finalize -}; + nullptr, // mayResolve + union_finalize}; struct JSClass gjs_union_class = { "GObject_Union", @@ -307,9 +305,8 @@ }; JSFunctionSpec gjs_union_proto_funcs[] = { - JS_FS("toString", to_string_func, 0, 0), - JS_FS_END -}; + JS_FN("toString", to_string_func, 0, 0), + JS_FS_END}; bool gjs_define_union_class(JSContext *context, diff -Nru gjs-1.53.3/gjs/byteArray.cpp gjs-1.54.3/gjs/byteArray.cpp --- gjs-1.53.3/gjs/byteArray.cpp 2018-05-31 05:34:45.000000000 +0000 +++ gjs-1.54.3/gjs/byteArray.cpp 2018-11-12 17:03:55.000000000 +0000 @@ -21,408 +21,37 @@ * IN THE SOFTWARE. */ -#include -#include #include + #include "byteArray.h" #include "gi/boxed.h" -#include "jsapi-class.h" -#include "jsapi-wrapper.h" +#include "gjs/deprecation.h" #include "jsapi-util-args.h" -#include -#include - -typedef struct { - GByteArray *array; - GBytes *bytes; -} ByteArrayInstance; - -extern struct JSClass gjs_byte_array_class; -GJS_DEFINE_PRIV_FROM_JS(ByteArrayInstance, gjs_byte_array_class) - -static bool byte_array_get_prop (JSContext *context, - JS::HandleObject obj, - JS::HandleId id, - JS::MutableHandleValue value_p); -static bool byte_array_set_prop (JSContext *context, - JS::HandleObject obj, - JS::HandleId id, - JS::MutableHandleValue value_p, - JS::ObjectOpResult& result); -GJS_NATIVE_CONSTRUCTOR_DECLARE(byte_array); -static void byte_array_finalize (JSFreeOp *fop, - JSObject *obj); - -static JSObject *gjs_byte_array_get_proto(JSContext *); - -static const struct JSClassOps gjs_byte_array_class_ops = { - NULL, /* addProperty */ - NULL, /* deleteProperty */ - byte_array_get_prop, - byte_array_set_prop, - NULL, /* enumerate */ - NULL, /* resolve */ - nullptr, /* mayResolve */ - byte_array_finalize -}; - -struct JSClass gjs_byte_array_class = { - "ByteArray", - JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE, - &gjs_byte_array_class_ops -}; - -bool -gjs_typecheck_bytearray(JSContext *context, - JS::HandleObject object, - bool throw_error) -{ - return do_base_typecheck(context, object, throw_error); -} - -static JS::Value -gjs_value_from_gsize(gsize v) -{ - if (v <= (gsize) JSVAL_INT_MAX) { - return JS::Int32Value(v); - } - return JS::NumberValue(v); -} - -static void -byte_array_ensure_array (ByteArrayInstance *priv) -{ - if (priv->bytes) { - priv->array = g_bytes_unref_to_array(priv->bytes); - priv->bytes = NULL; - } else { - g_assert(priv->array); - } -} - -static void -byte_array_ensure_gbytes (ByteArrayInstance *priv) -{ - if (priv->array) { - priv->bytes = g_byte_array_free_to_bytes(priv->array); - priv->array = NULL; - } else { - g_assert(priv->bytes); - } -} - -static bool -gjs_value_to_gsize(JSContext *context, - JS::HandleValue value, - gsize *v_p) -{ - guint32 val32; - - /* Just JS::ToUint32() would work. However, we special case ints for a nicer - * error message on negative indices. - */ - if (value.isInt32()) { - int i = value.toInt32(); - if (i < 0) { - gjs_throw(context, "Negative length or index %d is not allowed for ByteArray", - i); - return false; - } - *v_p = i; - return true; - } else { - bool ret; - /* This is pretty liberal (it converts about anything to - * a number) but it's what we use elsewhere in gjs too. - */ - - ret = JS::ToUint32(context, value, &val32); - *v_p = val32; - return ret; - } -} - -static bool -gjs_value_to_byte(JSContext *context, - JS::HandleValue value, - guint8 *v_p) -{ - gsize v; - - if (!gjs_value_to_gsize(context, value, &v)) - return false; - - if (v >= 256) { - gjs_throw(context, - "Value %" G_GSIZE_FORMAT " is not a valid byte; must be in range [0,255]", - v); - return false; - } - - *v_p = v; - return true; -} - -static bool -byte_array_get_index(JSContext *context, - JS::HandleObject obj, - ByteArrayInstance *priv, - gsize idx, - JS::MutableHandleValue value_p) -{ - gsize len; - guint8 *data; - - gjs_byte_array_peek_data(context, obj, &data, &len); - - if (idx >= len) { - gjs_throw(context, - "Index %" G_GSIZE_FORMAT " is out of range for ByteArray length %lu", - idx, - (unsigned long)len); - return false; - } - - value_p.setInt32(data[idx]); - - return true; -} - -/* a hook on getting a property; set value_p to override property's value. - * Return value is false on OOM/exception. - */ -static bool -byte_array_get_prop(JSContext *context, - JS::HandleObject obj, - JS::HandleId id, - JS::MutableHandleValue value_p) -{ - ByteArrayInstance *priv; - - priv = priv_from_js(context, obj); - - if (!priv) - return true; /* prototype, not an instance. */ - - JS::RootedValue id_value(context); - if (!JS_IdToValue(context, id, &id_value)) - return false; - - /* First handle array indexing */ - if (id_value.isNumber()) { - gsize idx; - if (!gjs_value_to_gsize(context, id_value, &idx)) - return false; - return byte_array_get_index(context, obj, priv, idx, value_p); - } - - /* We don't special-case anything else for now. Regular JS arrays - * allow string versions of ints for the index, we don't bother. - */ - - return true; -} - -static bool -byte_array_length_getter(JSContext *context, - unsigned argc, - JS::Value *vp) -{ - GJS_GET_PRIV(context, argc, vp, args, to, ByteArrayInstance, priv); - gsize len = 0; - - if (!priv) - return true; /* prototype, not an instance. */ - - if (priv->array != NULL) - len = priv->array->len; - else if (priv->bytes != NULL) - len = g_bytes_get_size (priv->bytes); - args.rval().set(gjs_value_from_gsize(len)); - return true; -} - -static bool -byte_array_length_setter(JSContext *context, - unsigned argc, - JS::Value *vp) -{ - GJS_GET_PRIV(context, argc, vp, args, to, ByteArrayInstance, priv); - gsize len = 0; - - if (!priv) - return true; /* prototype, not instance */ - - byte_array_ensure_array(priv); - - if (!gjs_value_to_gsize(context, args[0], &len)) { - gjs_throw(context, - "Can't set ByteArray length to non-integer"); - return false; - } - g_byte_array_set_size(priv->array, len); - args.rval().setUndefined(); - return true; -} - -static bool -byte_array_set_index(JSContext *context, - JS::HandleObject obj, - ByteArrayInstance *priv, - gsize idx, - JS::MutableHandleValue value_p, - JS::ObjectOpResult& result) -{ - guint8 v; - - if (!gjs_value_to_byte(context, value_p, &v)) { - return false; - } - - byte_array_ensure_array(priv); - - /* grow the array if necessary */ - if (idx >= priv->array->len) { - g_byte_array_set_size(priv->array, - idx + 1); - } - - g_array_index(priv->array, guint8, idx) = v; - - /* Stop JS from storing a copy of the value */ - value_p.setUndefined(); - - return result.succeed(); -} - -/* a hook on setting a property; set value_p to override property value to - * be set. Return value is false on OOM/exception. - */ -static bool -byte_array_set_prop(JSContext *context, - JS::HandleObject obj, - JS::HandleId id, - JS::MutableHandleValue value_p, - JS::ObjectOpResult& result) -{ - ByteArrayInstance *priv; - - priv = priv_from_js(context, obj); - - if (!priv) - return result.succeed(); /* prototype, not an instance. */ - - JS::RootedValue id_value(context); - if (!JS_IdToValue(context, id, &id_value)) - return false; - - /* First handle array indexing */ - if (id_value.isNumber()) { - gsize idx; - if (!gjs_value_to_gsize(context, id_value, &idx)) - return false; - - return byte_array_set_index(context, obj, priv, idx, value_p, result); - } - - /* We don't special-case anything else for now */ - - return result.succeed(); -} - -static GByteArray * -gjs_g_byte_array_new(int preallocated_length) -{ - GByteArray *array; +#include "jsapi-wrapper.h" - /* can't use g_byte_array_new() because we need to clear to zero. - * We nul-terminate too for ease of toString() and for security - * paranoia. - */ - array = (GByteArray*) g_array_sized_new (true, /* nul-terminated */ - true, /* clear to zero */ - 1, /* element size */ - preallocated_length); - if (preallocated_length > 0) { - /* we want to not only allocate the size, but have it - * already be the array's length. - */ - g_byte_array_set_size(array, preallocated_length); - } +/* Callbacks to use with JS_NewExternalArrayBuffer() */ - return array; +static void gfree_arraybuffer_contents(void* contents, void* unused) { + g_free(contents); } -GJS_NATIVE_CONSTRUCTOR_DECLARE(byte_array) -{ - GJS_NATIVE_CONSTRUCTOR_VARIABLES(byte_array) - ByteArrayInstance *priv; - gsize preallocated_length; - - GJS_NATIVE_CONSTRUCTOR_PRELUDE(byte_array); - - preallocated_length = 0; - if (argc >= 1) { - if (!gjs_value_to_gsize(context, argv[0], &preallocated_length)) { - gjs_throw(context, - "Argument to ByteArray constructor should be a positive number for array length"); - return false; - } - } - - priv = g_slice_new0(ByteArrayInstance); - priv->array = gjs_g_byte_array_new(preallocated_length); - g_assert(priv_from_js(context, object) == NULL); - JS_SetPrivate(object, priv); - - GJS_NATIVE_CONSTRUCTOR_FINISH(byte_array); - - return true; +static void bytes_ref_arraybuffer(void* contents, void* user_data) { + auto* gbytes = static_cast(user_data); + g_bytes_ref(gbytes); } -static void -byte_array_finalize(JSFreeOp *fop, - JSObject *obj) -{ - ByteArrayInstance *priv; - - priv = (ByteArrayInstance*) JS_GetPrivate(obj); - - if (!priv) - return; /* prototype, not instance */ - - if (priv->array) { - g_byte_array_free(priv->array, true); - priv->array = NULL; - } else if (priv->bytes) { - g_clear_pointer(&priv->bytes, g_bytes_unref); - } - - g_slice_free(ByteArrayInstance, priv); +static void bytes_unref_arraybuffer(void* contents, void* user_data) { + auto* gbytes = static_cast(user_data); + g_bytes_unref(gbytes); } /* implement toString() with an optional encoding arg */ -static bool -to_string_func(JSContext *context, - unsigned argc, - JS::Value *vp) -{ - GJS_GET_PRIV(context, argc, vp, argv, to, ByteArrayInstance, priv); - GjsAutoJSChar encoding; +static bool to_string_impl(JSContext* context, JS::HandleObject byte_array, + const char* encoding, JS::MutableHandleValue rval) { bool encoding_is_utf8; - gchar *data; - - if (!priv) - return true; /* prototype, not instance */ - - byte_array_ensure_array(priv); - - if (argc >= 1 && argv[0].isString()) { - JS::RootedString str(context, argv[0].toString()); - encoding = JS_EncodeStringToUTF8(context, str); - if (!encoding) - return false; + uint8_t* data; + if (encoding) { /* maybe we should be smarter about utf8 synonyms here. * doesn't matter much though. encoding_is_utf8 is * just an optimization anyway. @@ -432,17 +61,16 @@ encoding_is_utf8 = true; } - if (priv->array->len == 0) - /* the internal data pointer could be NULL in this case */ - data = (gchar*)""; - else - data = (gchar*)priv->array->data; + uint32_t len; + bool is_shared_memory; + js::GetUint8ArrayLengthAndData(byte_array, &len, &is_shared_memory, &data); if (encoding_is_utf8) { /* optimization, avoids iconv overhead and runs * libmozjs hardwired utf8-to-utf16 */ - return gjs_string_from_utf8_n(context, data, priv->array->len, argv.rval()); + return gjs_string_from_utf8_n(context, reinterpret_cast(data), + len, rval); } else { bool ok = false; gsize bytes_written; @@ -452,13 +80,9 @@ char16_t *u16_out; error = NULL; - u16_str = g_convert(data, - priv->array->len, - "UTF-16", - encoding, - NULL, /* bytes read */ - &bytes_written, - &error); + u16_str = g_convert(reinterpret_cast(data), len, "UTF-16", + encoding, nullptr, /* bytes read */ + &bytes_written, &error); if (u16_str == NULL) { /* frees the GError */ gjs_throw_g_error(context, error); @@ -475,7 +99,7 @@ s = JS_NewUCStringCopyN(context, u16_out, bytes_written / 2); if (s != NULL) { ok = true; - argv.rval().setString(s); + rval.setString(s); } g_free(u16_str); @@ -484,45 +108,59 @@ } } +static bool to_string_func(JSContext* cx, unsigned argc, JS::Value* vp) { + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + GjsAutoJSChar encoding; + JS::RootedObject byte_array(cx); + + if (!gjs_parse_call_args(cx, "toString", args, "o|s", "byteArray", + &byte_array, "encoding", &encoding)) + return false; + + return to_string_impl(cx, byte_array, encoding, args.rval()); +} + +/* Workaround to keep existing code compatible. This function is tacked onto + * any Uint8Array instances created in situations where previously a ByteArray + * would have been created. It logs a compatibility warning. */ +static bool instance_to_string_func(JSContext* cx, unsigned argc, + JS::Value* vp) { + GJS_GET_THIS(cx, argc, vp, args, this_obj); + GjsAutoJSChar encoding; + + _gjs_warn_deprecated_once_per_callsite( + cx, GjsDeprecationMessageId::ByteArrayInstanceToString); + + if (!gjs_parse_call_args(cx, "toString", args, "|s", "encoding", &encoding)) + return false; + + return to_string_impl(cx, this_obj, encoding, args.rval()); +} + static bool to_gbytes_func(JSContext *context, unsigned argc, JS::Value *vp) { - GJS_GET_PRIV(context, argc, vp, rec, to, ByteArrayInstance, priv); + JS::CallArgs rec = JS::CallArgsFromVp(argc, vp); JSObject *ret_bytes_obj; GIBaseInfo *gbytes_info; + JS::RootedObject byte_array(context); - if (!priv) - return true; /* prototype, not instance */ - - byte_array_ensure_gbytes(priv); + if (!gjs_parse_call_args(context, "toGBytes", rec, "o", + "byteArray", &byte_array)) + return false; + GBytes* bytes = gjs_byte_array_get_bytes(byte_array); gbytes_info = g_irepository_find_by_gtype(NULL, G_TYPE_BYTES); ret_bytes_obj = gjs_boxed_from_c_struct(context, (GIStructInfo*)gbytes_info, - priv->bytes, GJS_BOXED_CREATION_NONE); + bytes, GJS_BOXED_CREATION_NONE); + g_bytes_unref(bytes); rec.rval().setObjectOrNull(ret_bytes_obj); return true; } -static JSObject* -byte_array_new(JSContext *context) -{ - ByteArrayInstance *priv; - - JS::RootedObject proto(context, gjs_byte_array_get_proto(context)); - JS::RootedObject array(context, - JS_NewObjectWithGivenProto(context, &gjs_byte_array_class, proto)); - - priv = g_slice_new0(ByteArrayInstance); - - g_assert(priv_from_js(context, array) == NULL); - JS_SetPrivate(array, priv); - - return array; -} - /* fromString() function implementation */ static bool from_string_func(JSContext *context, @@ -530,33 +168,17 @@ JS::Value *vp) { JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); - ByteArrayInstance *priv; GjsAutoJSChar encoding; + GjsAutoJSChar utf8; bool encoding_is_utf8; - JS::RootedObject obj(context, byte_array_new(context)); + JS::RootedObject obj(context), array_buffer(context); - if (!obj) + if (!gjs_parse_call_args(context, "fromString", argv, "s|s", + "string", &utf8, + "encoding", &encoding)) return false; - priv = priv_from_js(context, obj); - g_assert (priv != NULL); - - g_assert(argc > 0); /* because we specified min args 1 */ - - priv->array = gjs_g_byte_array_new(0); - - if (!argv[0].isString()) { - gjs_throw(context, - "byteArray.fromString() called with non-string as first arg"); - return false; - } - - if (argc > 1 && argv[1].isString()) { - JS::RootedString str(context, argv[1].toString()); - encoding = JS_EncodeStringToUTF8(context, str); - if (!encoding) - return false; - + if (argc > 1) { /* maybe we should be smarter about utf8 synonyms here. * doesn't matter much though. encoding_is_utf8 is * just an optimization anyway. @@ -570,15 +192,9 @@ /* optimization? avoids iconv overhead and runs * libmozjs hardwired utf16-to-utf8. */ - JS::RootedString str(context, argv[0].toString()); - GjsAutoJSChar utf8 = JS_EncodeStringToUTF8(context, str); - if (!utf8) - return false; - - g_byte_array_set_size(priv->array, 0); - g_byte_array_append(priv->array, - reinterpret_cast(utf8.get()), - strlen(utf8)); + size_t len = strlen(utf8); + array_buffer = + JS_NewArrayBufferWithContents(context, len, utf8.release()); } else { JSString *str = argv[0].toString(); /* Rooted by argv */ GError *error = NULL; @@ -622,77 +238,15 @@ return false; } - g_byte_array_set_size(priv->array, 0); - g_byte_array_append(priv->array, (guint8*) encoded, bytes_written); - - g_free(encoded); - } - - argv.rval().setObject(*obj); - return true; -} - -/* fromArray() function implementation */ -static bool -from_array_func(JSContext *context, - unsigned argc, - JS::Value *vp) -{ - JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); - ByteArrayInstance *priv; - guint32 len; - guint32 i; - bool is_array; - JS::RootedObject obj(context, byte_array_new(context)); - - if (!obj) - return false; - - priv = priv_from_js(context, obj); - g_assert (priv != NULL); - - g_assert(argc > 0); /* because we specified min args 1 */ - - priv->array = gjs_g_byte_array_new(0); - - JS::RootedObject array_obj(context, &argv[0].toObject()); - if (!JS_IsArrayObject(context, array_obj, &is_array)) - return false; - if (!is_array) { - gjs_throw(context, - "byteArray.fromArray() called with non-array as first arg"); - return false; + array_buffer = + JS_NewExternalArrayBuffer(context, bytes_written, encoded, nullptr, + gfree_arraybuffer_contents, nullptr); } - if (!JS_GetArrayLength(context, array_obj, &len)) { - gjs_throw(context, - "byteArray.fromArray() can't get length of first array arg"); + if (!array_buffer) return false; - } - - g_byte_array_set_size(priv->array, len); - - JS::RootedValue elem(context); - for (i = 0; i < len; ++i) { - guint8 b; - - elem = JS::UndefinedValue(); - if (!JS_GetElement(context, array_obj, i, &elem)) { - /* this means there was an exception, while elem.isUndefined() - * means no element found - */ - return false; - } - - if (elem.isUndefined()) - continue; - - if (!gjs_value_to_byte(context, elem, &b)) - return false; - - g_array_index(priv->array, guint8, i) = b; - } - + obj = JS_NewUint8ArrayWithBuffer(context, array_buffer, 0, -1); + JS_DefineFunction(context, obj, "toString", instance_to_string_func, 1, 0); argv.rval().setObject(*obj); return true; } @@ -705,9 +259,8 @@ JS::CallArgs argv = JS::CallArgsFromVp (argc, vp); JS::RootedObject bytes_obj(context); GBytes *gbytes; - ByteArrayInstance *priv; - if (!gjs_parse_call_args(context, "overrides_gbytes_to_array", argv, "o", + if (!gjs_parse_call_args(context, "fromGBytes", argv, "o", "bytes", &bytes_obj)) return false; @@ -716,122 +269,71 @@ gbytes = (GBytes*) gjs_c_struct_from_boxed(context, bytes_obj); - JS::RootedObject obj(context, byte_array_new(context)); - if (!obj) + size_t len; + const void* data = g_bytes_get_data(gbytes, &len); + JS::RootedObject array_buffer( + context, + JS_NewExternalArrayBuffer( + context, len, + const_cast(data), // the ArrayBuffer won't modify the data + bytes_ref_arraybuffer, bytes_unref_arraybuffer, gbytes)); + if (!array_buffer) return false; - priv = priv_from_js(context, obj); - g_assert (priv != NULL); - priv->bytes = g_bytes_ref(gbytes); + JS::RootedObject obj( + context, JS_NewUint8ArrayWithBuffer(context, array_buffer, 0, -1)); + if (!obj) + return false; + JS_DefineFunction(context, obj, "toString", instance_to_string_func, 1, 0); argv.rval().setObject(*obj); return true; } -JSObject * -gjs_byte_array_from_byte_array (JSContext *context, - GByteArray *array) -{ - ByteArrayInstance *priv; - - g_return_val_if_fail(context != NULL, NULL); - g_return_val_if_fail(array != NULL, NULL); - - JS::RootedObject proto(context, gjs_byte_array_get_proto(context)); - JS::RootedObject object(context, - JS_NewObjectWithGivenProto(context, &gjs_byte_array_class, proto)); - - if (!object) { - gjs_throw(context, "failed to create byte array"); - return NULL; - } - - priv = g_slice_new0(ByteArrayInstance); - g_assert(priv_from_js(context, object) == NULL); - JS_SetPrivate(object, priv); - priv->array = g_byte_array_new(); - priv->array->data = (guint8*) g_memdup(array->data, array->len); - priv->array->len = array->len; - - return object; +JSObject* gjs_byte_array_from_data(JSContext* cx, size_t nbytes, void* data) { + JS::RootedObject array_buffer(cx); + // a null data pointer takes precedence over whatever `nbytes` says + if (data) + array_buffer = JS_NewArrayBufferWithContents(cx, nbytes, g_memdup(data, nbytes)); + else + array_buffer = JS_NewArrayBuffer(cx, 0); + if (!array_buffer) + return nullptr; + + JS::RootedObject array(cx, + JS_NewUint8ArrayWithBuffer(cx, array_buffer, 0, -1)); + JS_DefineFunction(cx, array, "toString", instance_to_string_func, 1, 0); + return array; } -GBytes * -gjs_byte_array_get_bytes (JSContext *context, - JS::HandleObject object) -{ - ByteArrayInstance *priv; - priv = priv_from_js(context, object); - g_assert(priv != NULL); - - byte_array_ensure_gbytes(priv); - - return g_bytes_ref (priv->bytes); +JSObject* gjs_byte_array_from_byte_array(JSContext* cx, GByteArray* array) { + return gjs_byte_array_from_data(cx, array->len, array->data); } -GByteArray * -gjs_byte_array_get_byte_array (JSContext *context, - JS::HandleObject obj) -{ - ByteArrayInstance *priv; - priv = priv_from_js(context, obj); - g_assert(priv != NULL); - - byte_array_ensure_array(priv); +GBytes* gjs_byte_array_get_bytes(JS::HandleObject obj) { + bool is_shared_memory; + uint32_t len; + uint8_t* data; - return g_byte_array_ref (priv->array); + js::GetUint8ArrayLengthAndData(obj, &len, &is_shared_memory, &data); + return g_bytes_new(data, len); } -void -gjs_byte_array_peek_data (JSContext *context, - JS::HandleObject obj, - guint8 **out_data, - gsize *out_len) -{ - ByteArrayInstance *priv; - priv = priv_from_js(context, obj); - g_assert(priv != NULL); - - if (priv->array != NULL) { - *out_data = (guint8*)priv->array->data; - *out_len = (gsize)priv->array->len; - } else if (priv->bytes != NULL) { - *out_data = (guint8*)g_bytes_get_data(priv->bytes, out_len); - } else { - g_assert_not_reached(); - } +GByteArray* gjs_byte_array_get_byte_array(JS::HandleObject obj) { + return g_bytes_unref_to_array(gjs_byte_array_get_bytes(obj)); } -static JSPropertySpec gjs_byte_array_proto_props[] = { - JS_PSGS("length", byte_array_length_getter, byte_array_length_setter, - JSPROP_PERMANENT), - JS_PS_END -}; - -static JSFunctionSpec gjs_byte_array_proto_funcs[] = { - JS_FS("toString", to_string_func, 0, 0), - JS_FS("toGBytes", to_gbytes_func, 0, 0), - JS_FS_END -}; - -static JSFunctionSpec *gjs_byte_array_static_funcs = nullptr; - static JSFunctionSpec gjs_byte_array_module_funcs[] = { - JS_FS("fromString", from_string_func, 1, 0), - JS_FS("fromArray", from_array_func, 1, 0), - JS_FS("fromGBytes", from_gbytes_func, 1, 0), - JS_FS_END -}; - -GJS_DEFINE_PROTO_FUNCS(byte_array) + JS_FN("fromString", from_string_func, 2, 0), + JS_FN("fromGBytes", from_gbytes_func, 1, 0), + JS_FN("toGBytes", to_gbytes_func, 1, 0), + JS_FN("toString", to_string_func, 2, 0), + JS_FS_END}; bool gjs_define_byte_array_stuff(JSContext *cx, JS::MutableHandleObject module) { module.set(JS_NewPlainObject(cx)); - - JS::RootedObject proto(cx); - return gjs_byte_array_define_proto(cx, module, &proto) && - JS_DefineFunctions(cx, module, gjs_byte_array_module_funcs); + return JS_DefineFunctions(cx, module, gjs_byte_array_module_funcs); } diff -Nru gjs-1.53.3/gjs/byteArray.h gjs-1.54.3/gjs/byteArray.h --- gjs-1.53.3/gjs/byteArray.h 2018-05-31 05:34:45.000000000 +0000 +++ gjs-1.54.3/gjs/byteArray.h 2018-11-12 16:43:50.000000000 +0000 @@ -30,26 +30,17 @@ G_BEGIN_DECLS -bool gjs_typecheck_bytearray(JSContext *context, - JS::HandleObject obj, - bool throw_error); - bool gjs_define_byte_array_stuff(JSContext *context, JS::MutableHandleObject module); +JSObject* gjs_byte_array_from_data(JSContext* cx, size_t nbytes, void* data); + JSObject * gjs_byte_array_from_byte_array (JSContext *context, GByteArray *array); -GByteArray *gjs_byte_array_get_byte_array(JSContext *context, - JS::HandleObject object); - -GBytes *gjs_byte_array_get_bytes(JSContext *context, - JS::HandleObject object); +GByteArray* gjs_byte_array_get_byte_array(JS::HandleObject obj); -void gjs_byte_array_peek_data(JSContext *context, - JS::HandleObject object, - guint8 **out_data, - gsize *out_len); +GBytes* gjs_byte_array_get_bytes(JS::HandleObject obj); G_END_DECLS diff -Nru gjs-1.53.3/gjs/console.cpp gjs-1.54.3/gjs/console.cpp --- gjs-1.53.3/gjs/console.cpp 2018-05-26 20:49:15.000000000 +0000 +++ gjs-1.54.3/gjs/console.cpp 2018-11-12 16:43:50.000000000 +0000 @@ -37,11 +37,13 @@ static char *command = NULL; static gboolean print_version = false; static gboolean print_js_version = false; +static gboolean debugging = false; static bool enable_profiler = false; static gboolean parse_profile_arg(const char *, const char *, void *, GError **); /* Keep in sync with entries in check_script_args_for_stray_gjs_args() */ +// clang-format off static GOptionEntry entries[] = { { "version", 0, 0, G_OPTION_ARG_NONE, &print_version, "Print GJS version and exit" }, { "jsversion", 0, 0, G_OPTION_ARG_NONE, &print_js_version, @@ -54,8 +56,10 @@ G_OPTION_ARG_CALLBACK, reinterpret_cast(&parse_profile_arg), "Enable the profiler and write output to FILE (default: gjs-$PID.syscap)", "FILE" }, + { "debugger", 'd', 0, G_OPTION_ARG_NONE, &debugging, "Start in debug mode" }, { NULL } }; +// clang-format on static char ** strndupv(int n, @@ -248,6 +252,7 @@ command = NULL; print_version = false; print_js_version = false; + debugging = false; g_option_context_set_ignore_unknown_options(context, false); g_option_context_set_help_enabled(context, true); if (!g_option_context_parse_strv(context, &gjs_argv, &error)) @@ -341,6 +346,11 @@ goto out; } + /* If we're debugging, set up the debugger. It will break on the first + * frame. */ + if (debugging) + gjs_context_setup_debugger_console(js_context); + /* evaluate the script */ if (!gjs_context_eval(js_context, script, len, filename, &code, &error)) { @@ -364,5 +374,8 @@ g_object_unref(coverage); g_object_unref(js_context); g_free(script); + + if (debugging) + g_print("Program exited with code %d\n", code); exit(code); } diff -Nru gjs-1.53.3/gjs/context.cpp gjs-1.54.3/gjs/context.cpp --- gjs-1.53.3/gjs/context.cpp 2018-06-18 22:41:13.000000000 +0000 +++ gjs-1.54.3/gjs/context.cpp 2018-11-12 16:43:50.000000000 +0000 @@ -32,8 +32,12 @@ #include +#include "byteArray.h" #include "context-private.h" #include "engine.h" +#include "gi/object.h" +#include "gi/private.h" +#include "gi/repo.h" #include "global.h" #include "importer.h" #include "jsapi-util.h" @@ -41,9 +45,6 @@ #include "mem.h" #include "native.h" #include "profiler-private.h" -#include "byteArray.h" -#include "gi/object.h" -#include "gi/repo.h" #include @@ -70,6 +71,26 @@ const GValue *value, GParamSpec *pspec); +/* Environment preparer needed for debugger, taken from SpiderMonkey's + * JS shell */ +struct GjsEnvironmentPreparer final : public js::ScriptEnvironmentPreparer { + JSContext* m_cx; + + explicit GjsEnvironmentPreparer(JSContext* cx) : m_cx(cx) { + js::SetScriptEnvironmentPreparer(m_cx, this); + } + + void invoke(JS::HandleObject scope, Closure& closure) override; +}; + +void GjsEnvironmentPreparer::invoke(JS::HandleObject scope, Closure& closure) { + g_assert(!JS_IsExceptionPending(m_cx)); + + JSAutoCompartment ac(m_cx, scope); + if (!closure(m_cx)) + gjs_log_exception(m_cx); +} + using JobQueue = JS::GCVector; struct _GjsContext { @@ -103,6 +124,8 @@ GjsProfiler *profiler; bool should_profile : 1; bool should_listen_sigusr2 : 1; + + GjsEnvironmentPreparer environment_preparer; }; /* Keep this consistent with GjsConstString */ @@ -297,7 +320,7 @@ g_free (priv_typelib_dir); } - gjs_register_native_module("byteArray", gjs_define_byte_array_stuff); + gjs_register_native_module("_byteArrayNative", gjs_define_byte_array_stuff); gjs_register_native_module("_gi", gjs_define_private_gi_stuff); gjs_register_native_module("gi", gjs_define_repo); @@ -431,6 +454,7 @@ js_context->global.~Heap(); js_context->const_strings.~array(); js_context->unhandled_rejection_stacks.~unordered_map(); + js_context->environment_preparer.~GjsEnvironmentPreparer(); G_OBJECT_CLASS(gjs_context_parent_class)->finalize(object); } @@ -466,6 +490,7 @@ new (&js_context->unhandled_rejection_stacks) std::unordered_map; new (&js_context->const_strings) std::array; + new (&js_context->environment_preparer) GjsEnvironmentPreparer(cx); for (i = 0; i < GJS_STRING_LAST; i++) { js_context->const_strings[i] = new JS::PersistentRootedId(cx, gjs_intern_string_to_id(cx, const_strings[i])); @@ -504,11 +529,6 @@ g_error("Failed to define properties on global object"); } - /* Pre-import the byteArray module. We depend on this module for some of - * our GObject introspection marshalling, so the ByteArray prototype - * defined in it needs to be always available. */ - gjs_import_native_module(cx, importer, "byteArray"); - JS_EndRequest(cx); g_mutex_lock (&contexts_lock); @@ -616,9 +636,9 @@ if (js_context->auto_gc_id > 0) return; - js_context->auto_gc_id = g_idle_add_full(G_PRIORITY_LOW, - trigger_gc_if_needed, - js_context, NULL); + js_context->auto_gc_id = g_timeout_add_seconds_full(G_PRIORITY_LOW, 10, + trigger_gc_if_needed, + js_context, NULL); } void diff -Nru gjs-1.53.3/gjs/context.h gjs-1.54.3/gjs/context.h --- gjs-1.53.3/gjs/context.h 2018-05-26 20:49:15.000000000 +0000 +++ gjs-1.54.3/gjs/context.h 2018-11-12 16:43:50.000000000 +0000 @@ -105,6 +105,9 @@ GJS_EXPORT const char *gjs_get_js_version(void); +GJS_EXPORT +void gjs_context_setup_debugger_console(GjsContext* gjs); + G_END_DECLS #endif /* __GJS_CONTEXT_H__ */ diff -Nru gjs-1.53.3/gjs/debugger.cpp gjs-1.54.3/gjs/debugger.cpp --- gjs-1.53.3/gjs/debugger.cpp 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/gjs/debugger.cpp 2018-11-12 16:43:50.000000000 +0000 @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2018 Philip Chimento + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authored By: Philip Chimento + */ + +#include + +#include + +#include "gjs/context-private.h" +#include "gjs/global.h" +#include "gjs/jsapi-util-args.h" + +#ifdef HAVE_READLINE_READLINE_H +#include +#include +#include +#endif + +static bool quit(JSContext* cx, unsigned argc, JS::Value* vp) { + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + JSAutoRequest ar(cx); + int32_t exitcode; + if (!gjs_parse_call_args(cx, "quit", args, "i", "exitcode", &exitcode)) + return false; + + auto* gjs = static_cast(JS_GetContextPrivate(cx)); + _gjs_context_exit(gjs, exitcode); + return false; // without gjs_throw() == "throw uncatchable exception" +} + +static bool do_readline(JSContext* cx, unsigned argc, JS::Value* vp) { + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + JSAutoRequest ar(cx); + + GjsAutoJSChar prompt; + if (!gjs_parse_call_args(cx, "readline", args, "|s", "prompt", &prompt)) + return false; + + GjsAutoChar line; + do { + const char* real_prompt = prompt ? prompt.get() : "db> "; +#ifdef HAVE_READLINE_READLINE_H + if (isatty(STDIN_FILENO)) { + line = readline(real_prompt); + } else { +#else + { +#endif // HAVE_READLINE_READLINE_H + char buf[256]; + g_print("%s", real_prompt); + fflush(stdout); + if (!fgets(buf, sizeof buf, stdin)) + buf[0] = '\0'; + line.reset(g_strchomp(g_strdup(buf))); + + if (!isatty(STDIN_FILENO)) { + if (feof(stdin)) { + g_print("[quit due to end of input]\n"); + line.reset(g_strdup("quit")); + } else { + g_print("%s\n", line.get()); + } + } + } + + /* EOF, return null */ + if (!line) { + args.rval().setUndefined(); + return true; + } + } while (line && line[0] == '\0'); + + /* Add line to history and convert it to a JSString so that we can pass it + * back as the return value */ +#ifdef HAVE_READLINE_READLINE_H + add_history(line); +#endif + args.rval().setString(JS_NewStringCopyZ(cx, line)); + return true; +} + +// clang-format off +static JSFunctionSpec debugger_funcs[] = { + JS_FN("quit", quit, 1, GJS_MODULE_PROP_FLAGS), + JS_FN("readline", do_readline, 1, GJS_MODULE_PROP_FLAGS), + JS_FS_END +}; +// clang-format on + +void gjs_context_setup_debugger_console(GjsContext* gjs) { + auto cx = static_cast(gjs_context_get_native_context(gjs)); + JSAutoRequest ar(cx); + + JS::RootedObject debuggee(cx, gjs_get_import_global(cx)); + JS::RootedObject debugger_compartment(cx, gjs_create_global_object(cx)); + + /* Enter compartment of the debugger and initialize it with the debuggee */ + JSAutoCompartment compartment(cx, debugger_compartment); + JS::RootedObject debuggee_wrapper(cx, debuggee); + if (!JS_WrapObject(cx, &debuggee_wrapper)) { + gjs_log_exception(cx); + return; + } + + JS::RootedValue v_wrapper(cx, JS::ObjectValue(*debuggee_wrapper)); + if (!JS_SetProperty(cx, debugger_compartment, "debuggee", v_wrapper) || + !JS_DefineFunctions(cx, debugger_compartment, debugger_funcs) || + !gjs_define_global_properties(cx, debugger_compartment, "debugger")) + gjs_log_exception(cx); +} diff -Nru gjs-1.53.3/gjs/deprecation.cpp gjs-1.54.3/gjs/deprecation.cpp --- gjs-1.53.3/gjs/deprecation.cpp 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/gjs/deprecation.cpp 2018-11-12 16:43:50.000000000 +0000 @@ -0,0 +1,100 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ +/* + * Copyright (c) 2018 Philip Chimento + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include + +#include "gjs/context-private.h" +#include "gjs/deprecation.h" +#include "gjs/jsapi-util.h" +#include "gjs/jsapi-wrapper.h" + +const char* messages[] = { + // None: + "(invalid message)", + + // ByteArrayInstanceToString: + "Some code called array.toString() on a Uint8Array instance. Previously " + "this would have interpreted the bytes of the array as a string, but that " + "is nonstandard. In the future this will return the bytes as " + "comma-separated digits. For the time being, the old behavior has been " + "preserved, but please fix your code anyway to explicitly call ByteArray" + ".toString(array).\n" + "(Note that array.toString() may have been called implicitly.)", +}; + +struct DeprecationEntry { + GjsDeprecationMessageId id; + std::string loc; + + DeprecationEntry(GjsDeprecationMessageId an_id, const char* a_loc) + : id(an_id), loc(a_loc) {} + + bool operator==(const DeprecationEntry& other) const { + return id == other.id && loc == other.loc; + } +}; + +namespace std { +template <> +struct hash { + size_t operator()(const DeprecationEntry& key) const { + return hash()(key.id) ^ hash()(key.loc); + } +}; +}; // namespace std + +static std::unordered_set logged_messages; + +static char* get_callsite(JSContext* cx) { + JS::RootedObject stack_frame(cx); + if (!JS::CaptureCurrentStack(cx, &stack_frame, + JS::StackCapture(JS::MaxFrames(1))) || + !stack_frame) + return nullptr; + + JS::RootedValue v_frame(cx, JS::ObjectValue(*stack_frame)); + JS::RootedString frame_string(cx, JS::ToString(cx, v_frame)); + if (!frame_string) + return nullptr; + + GjsAutoJSChar frame_utf8; + if (!gjs_string_to_utf8(cx, JS::StringValue(frame_string), &frame_utf8)) + return nullptr; + return frame_utf8.release(); +} + +/* Note, this can only be called from the JS thread because it uses the full + * stack dump API and not the "safe" gjs_dumpstack() which can only print to + * stdout or stderr. Do not use this function during GC, for example. */ +void _gjs_warn_deprecated_once_per_callsite(JSContext* cx, + const GjsDeprecationMessageId id) { + GjsAutoJSChar callsite = get_callsite(cx); + DeprecationEntry entry(id, callsite); + if (!logged_messages.count(entry)) { + JS::UniqueChars stack_dump = JS::FormatStackDump(cx, nullptr, false, + false, false); + g_warning("%s\n%s", messages[id], stack_dump.get()); + logged_messages.insert(std::move(entry)); + } +} diff -Nru gjs-1.53.3/gjs/deprecation.h gjs-1.54.3/gjs/deprecation.h --- gjs-1.53.3/gjs/deprecation.h 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/gjs/deprecation.h 2018-11-08 15:05:23.000000000 +0000 @@ -0,0 +1,37 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ +/* + * Copyright (c) 2018 Philip Chimento + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef GJS_DEPRECATION_H_ +#define GJS_DEPRECATION_H_ + +#include "gjs/jsapi-wrapper.h" + +enum GjsDeprecationMessageId { + None, + ByteArrayInstanceToString, +}; + +void _gjs_warn_deprecated_once_per_callsite(JSContext* cx, + GjsDeprecationMessageId message); + +#endif /* GJS_DEPRECATION_H_ */ diff -Nru gjs-1.53.3/gjs/engine.cpp gjs-1.54.3/gjs/engine.cpp --- gjs-1.53.3/gjs/engine.cpp 2018-06-18 22:44:01.000000000 +0000 +++ gjs-1.54.3/gjs/engine.cpp 2018-11-12 17:03:47.000000000 +0000 @@ -118,7 +118,6 @@ static void gjs_finalize_callback(JSFreeOp *fop, JSFinalizeStatus status, - bool isCompartment, void *data) { auto js_context = static_cast(data); @@ -163,7 +162,7 @@ code, so we can probably rely on this behavior. */ - if (status == JSFINALIZE_GROUP_START) + if (status == JSFINALIZE_GROUP_PREPARE) _gjs_context_set_sweeping(js_context, true); else if (status == JSFINALIZE_GROUP_END) _gjs_context_set_sweeping(js_context, false); @@ -193,16 +192,13 @@ return _gjs_context_enqueue_job(gjs_context, callback); } -static void -on_promise_unhandled_rejection(JSContext *cx, - JS::HandleObject promise, - PromiseRejectionHandlingState state, - void *data) -{ +static void on_promise_unhandled_rejection( + JSContext* cx, JS::HandleObject promise, + JS::PromiseRejectionHandlingState state, void* data) { auto gjs_context = static_cast(data); uint64_t id = JS::GetPromiseID(promise); - if (state == PromiseRejectionHandlingState::Handled) { + if (state == JS::PromiseRejectionHandlingState::Handled) { /* This happens when catching an exception from an await expression. */ _gjs_context_unregister_unhandled_promise_rejection(gjs_context, id); return; @@ -295,7 +291,7 @@ JS_AddFinalizeCallback(cx, gjs_finalize_callback, js_context); JS_SetGCCallback(cx, on_garbage_collect, js_context); - JS_SetLocaleCallbacks(cx, &gjs_locale_callbacks); + JS_SetLocaleCallbacks(JS_GetRuntime(cx), &gjs_locale_callbacks); JS::SetWarningReporter(cx, gjs_warning_reporter); JS::SetGetIncumbentGlobalCallback(cx, gjs_get_import_global); JS::SetEnqueuePromiseJobCallback(cx, on_enqueue_promise_job, js_context); @@ -308,13 +304,14 @@ JS::ContextOptionsRef(cx).setExtraWarnings(true); } - if (!g_getenv("GJS_DISABLE_JIT")) { + bool enable_jit = !(g_getenv("GJS_DISABLE_JIT")); + if (enable_jit) { gjs_debug(GJS_DEBUG_CONTEXT, "Enabling JIT"); - JS::ContextOptionsRef(cx) - .setIon(true) - .setBaseline(true) - .setAsmJS(true); } + JS::ContextOptionsRef(cx) + .setIon(enable_jit) + .setBaseline(enable_jit) + .setAsmJS(enable_jit); return cx; } diff -Nru gjs-1.53.3/gjs/global.cpp gjs-1.54.3/gjs/global.cpp --- gjs-1.53.3/gjs/global.cpp 2018-05-31 05:34:45.000000000 +0000 +++ gjs-1.54.3/gjs/global.cpp 2018-11-12 16:43:50.000000000 +0000 @@ -207,19 +207,17 @@ class GjsGlobal { static constexpr JSClassOps class_ops = { - nullptr, /* addProperty */ - nullptr, /* deleteProperty */ - nullptr, /* getProperty */ - nullptr, /* setProperty */ - nullptr, /* enumerate */ - nullptr, /* resolve */ - nullptr, /* mayResolve */ - nullptr, /* finalize */ - nullptr, /* call */ - nullptr, /* hasInstance */ - nullptr, /* construct */ - JS_GlobalObjectTraceHook - }; + nullptr, // addProperty + nullptr, // deleteProperty + nullptr, // enumerate + nullptr, // newEnumerate + nullptr, // resolve + nullptr, // mayResolve + nullptr, // finalize + nullptr, // call + nullptr, // hasInstance + nullptr, // construct + JS_GlobalObjectTraceHook}; static constexpr JSClass klass = { "GjsGlobal", @@ -228,12 +226,11 @@ }; static constexpr JSFunctionSpec static_funcs[] = { - JS_FS("log", gjs_log, 1, GJS_MODULE_PROP_FLAGS), - JS_FS("logError", gjs_log_error, 2, GJS_MODULE_PROP_FLAGS), - JS_FS("print", gjs_print, 0, GJS_MODULE_PROP_FLAGS), - JS_FS("printerr", gjs_printerr, 0, GJS_MODULE_PROP_FLAGS), - JS_FS_END - }; + JS_FN("log", gjs_log, 1, GJS_MODULE_PROP_FLAGS), + JS_FN("logError", gjs_log_error, 2, GJS_MODULE_PROP_FLAGS), + JS_FN("print", gjs_print, 0, GJS_MODULE_PROP_FLAGS), + JS_FN("printerr", gjs_printerr, 0, GJS_MODULE_PROP_FLAGS), + JS_FS_END}; public: @@ -241,7 +238,6 @@ create(JSContext *cx) { JS::CompartmentOptions compartment_options; - compartment_options.behaviors().setVersion(JSVERSION_LATEST); JS::RootedObject global(cx, JS_NewGlobalObject(cx, &GjsGlobal::klass, nullptr, JS::FireOnNewGlobalHook, compartment_options)); diff -Nru gjs-1.53.3/gjs/global.h gjs-1.54.3/gjs/global.h --- gjs-1.53.3/gjs/global.h 2018-05-31 05:34:45.000000000 +0000 +++ gjs-1.54.3/gjs/global.h 2018-11-12 16:43:50.000000000 +0000 @@ -61,13 +61,12 @@ JS::HandleObject global, const char *bootstrap_script); -JS::Value gjs_get_global_slot(JSContext *cx, - GjsGlobalSlot slot); - void gjs_set_global_slot(JSContext *context, GjsGlobalSlot slot, JS::Value value); G_END_DECLS +JS::Value gjs_get_global_slot(JSContext* cx, GjsGlobalSlot slot); + #endif /* GJS_GLOBAL_H */ diff -Nru gjs-1.53.3/gjs/importer.cpp gjs-1.54.3/gjs/importer.cpp --- gjs-1.53.3/gjs/importer.cpp 2018-05-31 05:34:45.000000000 +0000 +++ gjs-1.54.3/gjs/importer.cpp 2018-11-12 16:43:50.000000000 +0000 @@ -56,11 +56,7 @@ unsigned int index; } ImporterIterator; -extern const js::Class gjs_importer_real_class; - -/* Bizarrely, the API for safely casting const js::Class * to const JSClass * - * is called "js::Jsvalify" */ -static const JSClass gjs_importer_class = *js::Jsvalify(&gjs_importer_real_class); +extern const JSClass gjs_importer_class; GJS_DEFINE_PRIV_FROM_JS(Importer, gjs_importer_class) @@ -479,7 +475,7 @@ JS::RootedValue elem(context); JS::RootedString str(context); - /* First try importing an internal module like byteArray */ + /* First try importing an internal module like gi */ if (priv->is_root && gjs_is_registered_native_module(context, obj, name)) { if (!gjs_import_native_module(context, obj, name)) return false; @@ -594,12 +590,9 @@ /* Note that in a for ... in loop, this will be called first on the object, * then on its prototype. */ -static bool -importer_enumerate(JSContext *context, - JS::HandleObject object, - JS::AutoIdVector& properties, - bool enumerable_only) -{ +static bool importer_new_enumerate(JSContext* context, JS::HandleObject object, + JS::AutoIdVector& properties, + bool enumerable_only) { Importer *priv; guint32 search_path_len; guint32 i; @@ -759,10 +752,7 @@ GJS_NATIVE_CONSTRUCTOR_DEFINE_ABSTRACT(importer) -static void -importer_finalize(js::FreeOp *fop, - JSObject *obj) -{ +static void importer_finalize(JSFreeOp* fop, JSObject* obj) { Importer *priv; priv = (Importer*) JS_GetPrivate(obj); @@ -779,47 +769,28 @@ * instances of the object, and to the prototype that instances of the * class have. */ -static const js::ClassOps gjs_importer_class_ops = { - NULL, /* addProperty */ - NULL, /* deleteProperty */ - NULL, /* getProperty */ - NULL, /* setProperty */ - NULL, /* enumerate (see below) */ +static const JSClassOps gjs_importer_class_ops = { + nullptr, // addProperty + nullptr, // deleteProperty + nullptr, // enumerate + importer_new_enumerate, importer_resolve, - nullptr, /* mayResolve */ + nullptr, // mayResolve importer_finalize }; -static const js::ObjectOps gjs_importer_object_ops = { - NULL, /* lookupProperty */ - NULL, /* defineProperty */ - NULL, /* hasProperty */ - NULL, /* getProperty */ - NULL, /* setProperty */ - NULL, /* getOwnPropertyDescriptor */ - NULL, /* deleteProperty */ - NULL, /* watch */ - NULL, /* unwatch */ - NULL, /* getElements */ - importer_enumerate -}; - -const js::Class gjs_importer_real_class = { +const JSClass gjs_importer_class = { "GjsFileImporter", JSCLASS_HAS_PRIVATE | JSCLASS_FOREGROUND_FINALIZE, &gjs_importer_class_ops, - nullptr, - nullptr, - &gjs_importer_object_ops }; static JSPropertySpec *gjs_importer_proto_props = nullptr; static JSFunctionSpec *gjs_importer_static_funcs = nullptr; JSFunctionSpec gjs_importer_proto_funcs[] = { - JS_FS("toString", importer_to_string, 0, 0), - JS_FS_END -}; + JS_FN("toString", importer_to_string, 0, 0), + JS_FS_END}; GJS_DEFINE_PROTO_FUNCS(importer) diff -Nru gjs-1.53.3/gjs/jsapi-class.h gjs-1.54.3/gjs/jsapi-class.h --- gjs-1.53.3/gjs/jsapi-class.h 2018-06-18 22:44:01.000000000 +0000 +++ gjs-1.54.3/gjs/jsapi-class.h 2018-11-12 16:43:50.000000000 +0000 @@ -55,6 +55,15 @@ JS::HandleObject proto, const JS::HandleValueArray& args); +bool gjs_define_property_dynamic(JSContext *cx, + JS::HandleObject proto, + const char *prop_name, + const char *func_namespace, + JSNative getter, + JSNative setter, + JS::HandleValue private_slot, + unsigned flags); + /* * Helper methods to access private data: * @@ -168,28 +177,30 @@ #define GJS_DEFINE_PROTO_ABSTRACT_WITH_PARENT(tn, cn, parent_cn, flags) \ _GJS_DEFINE_PROTO_FULL(tn, cn, parent_cn, nullptr, G_TYPE_NONE, flags) -#define _GJS_DEFINE_PROTO_FULL(type_name, cname, parent_cname, ctor, gtype, jsclass_flags) \ -extern JSPropertySpec gjs_##cname##_proto_props[]; \ -extern JSFunctionSpec gjs_##cname##_proto_funcs[]; \ -extern JSFunctionSpec gjs_##cname##_static_funcs[]; \ -static void gjs_##cname##_finalize(JSFreeOp *fop, JSObject *obj); \ -static const struct JSClassOps gjs_##cname##_class_ops = { \ - nullptr, /* addProperty */ \ - nullptr, /* deleteProperty */ \ - nullptr, /* getProperty */ \ - nullptr, /* setProperty */ \ - nullptr, /* enumerate */ \ - nullptr, /* resolve */ \ - nullptr, /* mayResolve */ \ - gjs_##cname##_finalize \ -}; \ -static struct JSClass gjs_##cname##_class = { \ - type_name, \ - JSCLASS_HAS_PRIVATE | jsclass_flags, \ - &gjs_##cname##_class_ops \ -}; \ -_GJS_DEFINE_GET_PROTO(cname) \ -_GJS_DEFINE_DEFINE_PROTO(cname, parent_cname, ctor, gtype) +// clang-format off +#define _GJS_DEFINE_PROTO_FULL(type_name, cname, parent_cname, ctor, gtype, \ + jsclass_flags) \ + extern JSPropertySpec gjs_##cname##_proto_props[]; \ + extern JSFunctionSpec gjs_##cname##_proto_funcs[]; \ + extern JSFunctionSpec gjs_##cname##_static_funcs[]; \ + static void gjs_##cname##_finalize(JSFreeOp* fop, JSObject* obj); \ + static const struct JSClassOps gjs_##cname##_class_ops = { \ + nullptr, /* addProperty */ \ + nullptr, /* deleteProperty */ \ + nullptr, /* enumerate */ \ + nullptr, /* newEnumerate */ \ + nullptr, /* resolve */ \ + nullptr, /* mayResolve */ \ + gjs_##cname##_finalize \ + }; \ + static struct JSClass gjs_##cname##_class = { \ + type_name, \ + JSCLASS_HAS_PRIVATE | jsclass_flags, \ + &gjs_##cname##_class_ops \ + }; \ + _GJS_DEFINE_GET_PROTO(cname) \ + _GJS_DEFINE_DEFINE_PROTO(cname, parent_cname, ctor, gtype) +// clang-format on #define GJS_DEFINE_PROTO_FUNCS_WITH_PARENT(cname, parent_cname) \ G_GNUC_UNUSED static \ @@ -335,4 +346,6 @@ G_END_DECLS +JS::Value gjs_dynamic_property_private_slot(JSObject *accessor_obj); + #endif /* GJS_JSAPI_CLASS_H */ diff -Nru gjs-1.53.3/gjs/jsapi-dynamic-class.cpp gjs-1.54.3/gjs/jsapi-dynamic-class.cpp --- gjs-1.53.3/gjs/jsapi-dynamic-class.cpp 2018-06-18 22:44:01.000000000 +0000 +++ gjs-1.54.3/gjs/jsapi-dynamic-class.cpp 2018-11-12 16:43:50.000000000 +0000 @@ -35,6 +35,11 @@ #include #include +/* Reserved slots of JSNative accessor wrappers */ +enum { + DYNAMIC_PROPERTY_PRIVATE_SLOT, +}; + bool gjs_init_class_dynamic(JSContext *context, JS::HandleObject in_object, @@ -194,3 +199,90 @@ return JS_New(context, constructor, args); } + +static JSObject * +define_native_accessor_wrapper(JSContext *cx, + JSNative call, + unsigned nargs, + const char *func_name, + JS::HandleValue private_slot) +{ + JSFunction *func = js::NewFunctionWithReserved(cx, call, nargs, 0, func_name); + if (!func) + return nullptr; + + JSObject *func_obj = JS_GetFunctionObject(func); + js::SetFunctionNativeReserved(func_obj, DYNAMIC_PROPERTY_PRIVATE_SLOT, + private_slot); + return func_obj; +} + +/** + * gjs_define_property_dynamic: + * @cx: the #JSContext + * @proto: the prototype of the object, on which to define the property + * @prop_name: name of the property or field in GObject, visible to JS code + * @func_namespace: string from which the internal names for the getter and + * setter functions are built, not visible to JS code + * @getter: getter function + * @setter: setter function + * @private_slot: private data in the form of a #JS::Value that the getter and + * setter will have access to + * @flags: additional flags to define the property with (other than the ones + * required for a property with native getter/setter) + * + * When defining properties in a GBoxed or GObject, we can't have a separate + * getter and setter for each one, since the properties are defined dynamically. + * Therefore we must have one getter and setter for all the properties we define + * on all the types. In order to have that, we must provide the getter and + * setter with private data, e.g. the field index for GBoxed, in a "reserved + * slot" for which we must unfortunately use the jsfriendapi. + * + * Returns: %true on success, %false if an exception is pending on @cx. + */ +bool +gjs_define_property_dynamic(JSContext *cx, + JS::HandleObject proto, + const char *prop_name, + const char *func_namespace, + JSNative getter, + JSNative setter, + JS::HandleValue private_slot, + unsigned flags) +{ + GjsAutoChar getter_name = g_strconcat(func_namespace, "_get::", prop_name, nullptr); + GjsAutoChar setter_name = g_strconcat(func_namespace, "_set::", prop_name, nullptr); + + JS::RootedObject getter_obj(cx, + define_native_accessor_wrapper(cx, getter, 0, getter_name, private_slot)); + if (!getter_obj) + return false; + + JS::RootedObject setter_obj(cx, + define_native_accessor_wrapper(cx, setter, 1, setter_name, private_slot)); + if (!setter_obj) + return false; + + flags |= JSPROP_GETTER | JSPROP_SETTER; + + return JS_DefineProperty( + cx, proto, prop_name, JS_DATA_TO_FUNC_PTR(JSNative, getter_obj.get()), + JS_DATA_TO_FUNC_PTR(JSNative, setter_obj.get()), flags); +} + +/** + * gjs_dynamic_property_private_slot: + * @accessor_obj: the getter or setter as a function object, i.e. + * `&args.callee()` in the #JSNative function + * + * For use in dynamic property getters and setters (see + * gjs_define_property_dynamic()) to retrieve the private data passed there. + * + * Returns: the JS::Value that was passed to gjs_define_property_dynamic(). + */ +JS::Value +gjs_dynamic_property_private_slot(JSObject *accessor_obj) +{ + return js::GetFunctionNativeReserved(accessor_obj, + DYNAMIC_PROPERTY_PRIVATE_SLOT); +} diff -Nru gjs-1.53.3/gjs/jsapi-util-args.h gjs-1.54.3/gjs/jsapi-util-args.h --- gjs-1.53.3/gjs/jsapi-util-args.h 2018-06-18 22:44:01.000000000 +0000 +++ gjs-1.54.3/gjs/jsapi-util-args.h 2018-11-12 16:43:50.000000000 +0000 @@ -232,15 +232,15 @@ template static bool -parse_call_args_helper(JSContext *cx, - const char *function_name, - JS::CallArgs& args, - bool ignore_trailing_args, - const char*& fmt_required, - const char*& fmt_optional, - unsigned param_ix, - const char *param_name, - T param_ref) +parse_call_args_helper(JSContext *cx, + const char *function_name, + const JS::CallArgs& args, + bool ignore_trailing_args, + const char*& fmt_required, + const char*& fmt_optional, + unsigned param_ix, + const char *param_name, + T param_ref) { bool nullable = false; const char *fchar = fmt_required; @@ -279,16 +279,16 @@ template static bool -parse_call_args_helper(JSContext *cx, - const char *function_name, - JS::CallArgs& args, - bool ignore_trailing_args, - const char*& fmt_required, - const char*& fmt_optional, - unsigned param_ix, - const char *param_name, - T param_ref, - Args ...params) +parse_call_args_helper(JSContext *cx, + const char *function_name, + const JS::CallArgs& args, + bool ignore_trailing_args, + const char*& fmt_required, + const char*& fmt_optional, + unsigned param_ix, + const char *param_name, + T param_ref, + Args ...params) { bool retval; @@ -311,10 +311,10 @@ /* Empty-args version of the template */ G_GNUC_UNUSED static bool -gjs_parse_call_args(JSContext *cx, - const char *function_name, - JS::CallArgs& args, - const char *format) +gjs_parse_call_args(JSContext *cx, + const char *function_name, + const JS::CallArgs& args, + const char *format) { bool ignore_trailing_args = false; @@ -375,11 +375,11 @@ */ template static bool -gjs_parse_call_args(JSContext *cx, - const char *function_name, - JS::CallArgs& args, - const char *format, - Args ...params) +gjs_parse_call_args(JSContext *cx, + const char *function_name, + const JS::CallArgs& args, + const char *format, + Args ...params) { const char *fmt_iter, *fmt_required, *fmt_optional; unsigned n_required = 0, n_total = 0; diff -Nru gjs-1.53.3/gjs/jsapi-util-error.cpp gjs-1.54.3/gjs/jsapi-util-error.cpp --- gjs-1.53.3/gjs/jsapi-util-error.cpp 2018-05-31 05:34:45.000000000 +0000 +++ gjs-1.54.3/gjs/jsapi-util-error.cpp 2018-11-12 16:43:50.000000000 +0000 @@ -156,10 +156,10 @@ { va_list args; g_return_if_fail(kind == JSProto_Error || kind == JSProto_InternalError || - kind == JSProto_EvalError || kind == JSProto_RangeError || - kind == JSProto_ReferenceError || kind == JSProto_SyntaxError || - kind == JSProto_TypeError || kind == JSProto_URIError || - kind == JSProto_StopIteration); + kind == JSProto_EvalError || kind == JSProto_RangeError || + kind == JSProto_ReferenceError || + kind == JSProto_SyntaxError || kind == JSProto_TypeError || + kind == JSProto_URIError); va_start(args, format); gjs_throw_valist(cx, kind, error_name, format, args); diff -Nru gjs-1.53.3/gjs/jsapi-util.h gjs-1.54.3/gjs/jsapi-util.h --- gjs-1.53.3/gjs/jsapi-util.h 2018-06-18 22:44:01.000000000 +0000 +++ gjs-1.54.3/gjs/jsapi-util.h 2018-11-12 17:04:02.000000000 +0000 @@ -66,6 +66,43 @@ } }; +template +class GjsAutoInfo : public std::unique_ptr { +public: + GjsAutoInfo(T *ptr = nullptr) : GjsAutoInfo::unique_ptr(ptr, g_base_info_unref) {} + + operator T *() { return GjsAutoInfo::unique_ptr::get(); } + + const char *name(void) const { return g_base_info_get_name(this->get()); } + GIInfoType type(void) const { return g_base_info_get_type(this->get()); } +}; + +/* For use of GjsAutoInfo in GC hash maps */ +namespace JS { +template +struct GCPolicy> : public IgnoreGCPolicy> {}; +} + +class GjsAutoParam + : public std::unique_ptr { + public: + struct TakeOwnership {}; + + GjsAutoParam(GParamSpec* ptr = nullptr) + : unique_ptr(ptr, g_param_spec_unref) {} + + GjsAutoParam(GParamSpec* ptr, const TakeOwnership&) + : GjsAutoParam(ptr ? g_param_spec_ref(ptr) : nullptr) {} + + operator GParamSpec*() { return get(); } +}; + +/* For use of GjsAutoParam in GC hash maps */ +namespace JS { +template<> +struct GCPolicy : public IgnoreGCPolicy {}; +} // namespace JS + struct GjsJSFreeArgs { void operator() (char *str) { JS_free(nullptr, str); diff -Nru gjs-1.53.3/gjs/jsapi-util-root.h gjs-1.54.3/gjs/jsapi-util-root.h --- gjs-1.53.3/gjs/jsapi-util-root.h 2018-06-18 22:44:01.000000000 +0000 +++ gjs-1.54.3/gjs/jsapi-util-root.h 2018-11-12 16:43:50.000000000 +0000 @@ -84,10 +84,7 @@ /* If the object has been swept already, then the zone is nullptr */ if (!obj || !js::gc::detail::GetGCThingZone(uintptr_t(obj))) return; - /* COMPAT: Use JS::CurrentThreadIsHeapCollecting() in mozjs59 */ - JS::GCCellPtr ptr(obj, JS::TraceKind::Object); - JS::shadow::Runtime *rt = js::gc::detail::GetCellRuntime(ptr.asCell()); - if (!rt->isHeapCollecting()) + if (!JS::CurrentThreadIsHeapCollecting()) JS::ExposeObjectToActiveJS(obj); } }; @@ -110,8 +107,17 @@ bool m_has_weakref; /* we have a weak reference to the GjsContext */ JSContext *m_cx; - JS::Heap m_heap; /* should be untouched if in rooted mode */ - JS::PersistentRooted *m_root; /* should be null if not in rooted mode */ + + /* m_rooted controls which of these members we can access. When switching + * from one to the other, be careful to call the constructor and destructor + * of JS::Heap, since they use post barriers. */ + union RootUnion { + JS::Heap heap; + JS::PersistentRooted* root; + + RootUnion() : heap() {} + ~RootUnion() {} + } m_thing; DestroyNotify m_notify; void *m_data; @@ -138,8 +144,8 @@ debug("teardown_rooting()"); g_assert(m_rooted); - delete m_root; - m_root = nullptr; + delete m_thing.root; + new (&m_thing.heap) JS::Heap(); m_rooted = false; if (!m_has_weakref) @@ -176,7 +182,6 @@ m_rooted(false), m_has_weakref(false), m_cx(nullptr), - m_root(nullptr), m_notify(nullptr), m_data(nullptr) { @@ -188,6 +193,9 @@ debug("destroyed"); if (m_rooted) teardown_rooting(); + + /* Call in either case; teardown_rooting() constructs a new Heap */ + m_thing.heap.~Heap(); } /* To access the GC thing, call get(). In many cases you can just use the @@ -197,7 +205,7 @@ const T get(void) const { - return m_rooted ? m_root->get() : m_heap.get(); + return m_rooted ? m_thing.root->get() : m_thing.heap.get(); } operator const T(void) const { return get(); } @@ -205,8 +213,8 @@ operator==(const T& other) const { if (m_rooted) - return m_root->get() == other; - return m_heap == other; + return m_thing.root->get() == other; + return m_thing.heap == other; } inline bool operator!=(const T& other) const { return !(*this == other); } @@ -216,11 +224,14 @@ operator==(std::nullptr_t) const { if (m_rooted) - return m_root->get() == nullptr; - return m_heap.unbarrieredGet() == nullptr; + return m_thing.root->get() == nullptr; + return m_thing.heap.unbarrieredGet() == nullptr; } inline bool operator!=(std::nullptr_t) const { return !(*this == nullptr); } + /* Likewise the truth value does not require a read barrier */ + inline operator bool() const { return *this != nullptr; } + /* You can get a Handle if the thing is rooted, so that you can use this * wrapper with stack rooting. However, you must not do this if the * JSContext can be destroyed while the Handle is live. */ @@ -228,7 +239,7 @@ handle(void) { g_assert(m_rooted); - return *m_root; + return *m_thing.root; } /* Roots the GC thing. You must not use this if you're already using the @@ -241,12 +252,13 @@ { debug("root()"); g_assert(!m_rooted); - g_assert(m_heap.get() == JS::GCPolicy::initial()); + g_assert(m_thing.heap.get() == JS::GCPolicy::initial()); m_rooted = true; m_cx = cx; m_notify = notify; m_data = data; - m_root = new JS::PersistentRooted(m_cx, thing); + m_thing.heap.~Heap(); + m_thing.root = new JS::PersistentRooted(m_cx, thing); if (notify) { auto gjs_cx = static_cast(JS_GetContextPrivate(m_cx)); @@ -262,7 +274,7 @@ operator=(const T& thing) { g_assert(!m_rooted); - m_heap = thing; + m_thing.heap = thing; } /* Marks an object as reachable for one GC with ExposeObjectToActiveJS(). @@ -273,7 +285,7 @@ { debug("prevent_collection()"); g_assert(!m_rooted); - GjsHeapOperation::expose_to_js(m_heap); + GjsHeapOperation::expose_to_js(m_thing.heap); } void @@ -281,7 +293,7 @@ { debug("reset()"); if (!m_rooted) { - m_heap = JS::GCPolicy::initial(); + m_thing.heap = JS::GCPolicy::initial(); return; } @@ -300,9 +312,9 @@ g_assert(!m_rooted); /* Prevent the thing from being garbage collected while it is in neither - * m_heap nor m_root */ + * m_thing.heap nor m_thing.root */ JSAutoRequest ar(cx); - JS::Rooted thing(cx, m_heap); + JS::Rooted thing(cx, m_thing.heap); reset(); root(cx, thing, notify, data); @@ -316,12 +328,12 @@ g_assert(m_rooted); /* Prevent the thing from being garbage collected while it is in neither - * m_heap nor m_root */ + * m_thing.heap nor m_thing.root */ JSAutoRequest ar(m_cx); - JS::Rooted thing(m_cx, *m_root); + JS::Rooted thing(m_cx, *m_thing.root); reset(); - m_heap = thing; + m_thing.heap = thing; g_assert(!m_rooted); } @@ -333,7 +345,7 @@ { debug("trace()"); g_assert(!m_rooted); - JS::TraceEdge(tracer, &m_heap, name); + JS::TraceEdge(tracer, &m_thing.heap, name); } /* If not tracing, then you must call this method during GC in order to @@ -344,10 +356,10 @@ { debug("update_after_gc()"); g_assert(!m_rooted); - return GjsHeapOperation::update_after_gc(&m_heap); + return GjsHeapOperation::update_after_gc(&m_thing.heap); } - bool rooted(void) { return m_rooted; } + bool rooted(void) const { return m_rooted; } }; #endif /* GJS_JSAPI_UTIL_ROOT_H */ diff -Nru gjs-1.53.3/gjs/jsapi-util-string.cpp gjs-1.54.3/gjs/jsapi-util-string.cpp --- gjs-1.53.3/gjs/jsapi-util-string.cpp 2018-06-01 20:34:43.000000000 +0000 +++ gjs-1.54.3/gjs/jsapi-util-string.cpp 2018-11-12 17:03:55.000000000 +0000 @@ -278,6 +278,12 @@ ssize_t n_chars, JS::MutableHandleValue value_p) { + // a null array pointer takes precedence over whatever `n_chars` says + if (!ucs4_string) { + value_p.setString(JS_GetEmptyString(cx)); + return true; + } + long u16_string_length; GError *error = NULL; diff -Nru gjs-1.53.3/gjs/mem.cpp gjs-1.54.3/gjs/mem.cpp --- gjs-1.53.3/gjs/mem.cpp 2018-06-18 22:41:13.000000000 +0000 +++ gjs-1.54.3/gjs/mem.cpp 2018-11-08 04:20:06.000000000 +0000 @@ -42,13 +42,16 @@ GJS_DEFINE_COUNTER(importer) GJS_DEFINE_COUNTER(interface) GJS_DEFINE_COUNTER(ns) -GJS_DEFINE_COUNTER(object) +GJS_DEFINE_COUNTER(object_instance) +GJS_DEFINE_COUNTER(object_prototype) GJS_DEFINE_COUNTER(param) GJS_DEFINE_COUNTER(repo) #define GJS_LIST_COUNTER(name) \ & gjs_counter_ ## name +// clang-format off +// otherwise these are put into 2 columns?! static GjsMemCounter* counters[] = { GJS_LIST_COUNTER(boxed), GJS_LIST_COUNTER(closure), @@ -58,10 +61,12 @@ GJS_LIST_COUNTER(importer), GJS_LIST_COUNTER(interface), GJS_LIST_COUNTER(ns), - GJS_LIST_COUNTER(object), + GJS_LIST_COUNTER(object_instance), + GJS_LIST_COUNTER(object_prototype), GJS_LIST_COUNTER(param), GJS_LIST_COUNTER(repo), }; +// clang-format on void gjs_memory_report(const char *where, diff -Nru gjs-1.53.3/gjs/mem.h gjs-1.54.3/gjs/mem.h --- gjs-1.53.3/gjs/mem.h 2018-06-18 22:41:13.000000000 +0000 +++ gjs-1.54.3/gjs/mem.h 2018-11-12 16:43:50.000000000 +0000 @@ -48,7 +48,8 @@ GJS_DECLARE_COUNTER(importer) GJS_DECLARE_COUNTER(interface) GJS_DECLARE_COUNTER(ns) -GJS_DECLARE_COUNTER(object) +GJS_DECLARE_COUNTER(object_instance) +GJS_DECLARE_COUNTER(object_prototype) GJS_DECLARE_COUNTER(param) GJS_DECLARE_COUNTER(repo) diff -Nru gjs-1.53.3/gjs/module.cpp gjs-1.54.3/gjs/module.cpp --- gjs-1.53.3/gjs/module.cpp 2018-05-31 05:34:45.000000000 +0000 +++ gjs-1.54.3/gjs/module.cpp 2018-11-12 16:43:50.000000000 +0000 @@ -194,13 +194,12 @@ } static constexpr JSClassOps class_ops = { - nullptr, /* addProperty */ - nullptr, /* deleteProperty */ - nullptr, /* getProperty */ - nullptr, /* setProperty */ - nullptr, /* enumerate */ + nullptr, // addProperty + nullptr, // deleteProperty + nullptr, // enumerate + nullptr, // newEnumerate &GjsModule::resolve, - nullptr, /* mayResolve */ + nullptr, // mayResolve &GjsModule::finalize, }; diff -Nru gjs-1.53.3/gjs/profiler.cpp gjs-1.54.3/gjs/profiler.cpp --- gjs-1.53.3/gjs/profiler.cpp 2018-05-11 20:04:51.000000000 +0000 +++ gjs-1.54.3/gjs/profiler.cpp 2018-11-12 16:43:50.000000000 +0000 @@ -83,7 +83,7 @@ * information while executing. We will look into this during our * SIGPROF handler. */ - js::ProfileEntry stack[1024]; + PseudoStack stack; /* The context being profiled */ JSContext *cx; @@ -99,12 +99,6 @@ /* Our POSIX timer to wakeup SIGPROF */ timer_t timer; - /* The depth of @stack. This value may be larger than the - * number of elements in stack, and so you MUST ensure you - * don't walk past the end of stack[] when iterating. - */ - uint32_t stack_depth; - /* Cached copy of our pid */ GPid pid; @@ -272,21 +266,6 @@ #ifdef ENABLE_PROFILER -/* Run from a signal handler */ -static inline unsigned -gjs_profiler_get_stack_size(GjsProfiler *self) -{ - g_assert(((void) "Profiler must be set up before getting stack size", self)); - - /* - * Note that stack_depth could be larger than the number of - * items we have in our stack space. We must protect ourselves - * against overflowing by discarding anything after that depth - * of the stack. - */ - return std::min(self->stack_depth, uint32_t(G_N_ELEMENTS(self->stack))); -} - static void gjs_profiler_sigprof(int signum, siginfo_t *info, @@ -309,38 +288,71 @@ if (!self || info->si_code != SI_TIMER) return; - size_t depth = gjs_profiler_get_stack_size(self); + uint32_t depth = self->stack.stackSize(); if (depth == 0) return; - static_assert(G_N_ELEMENTS(self->stack) < G_MAXUSHORT, - "Number of elements in profiler stack should be expressible" - "in an unsigned short"); - int64_t now = g_get_monotonic_time() * 1000L; /* NOTE: cppcheck warns that alloca() is not recommended since it can * easily overflow the stack; however, dynamic allocation is not an option * here since we are in a signal handler. - * Another option would be to always allocate G_N_ELEMENTS(self->stack), - * but that is by definition at least as large of an allocation and - * therefore is more likely to overflow. */ // cppcheck-suppress allocaCalled SpCaptureAddress *addrs = static_cast(alloca(sizeof *addrs * depth)); - for (size_t ix = 0; ix < depth; ix++) { - js::ProfileEntry& entry = self->stack[ix]; + for (uint32_t ix = 0; ix < depth; ix++) { + js::ProfileEntry& entry = self->stack.entries[ix]; const char *label = entry.label(); - size_t flipped = depth - 1 - ix; + const char *dynamic_string = entry.dynamicString(); + uint32_t flipped = depth - 1 - ix; + size_t label_length = strlen(label); /* - * SPSProfiler will put "js::RunScript" on the stack, but it has + * 512 is an arbitrarily large size, very likely to be enough to + * hold the final string. + */ + char final_string[512] = { 0, }; + char *position = final_string; + size_t available_length = sizeof (final_string) - 1; + + if (label_length > 0) { + label_length = MIN(label_length, available_length); + + /* Start copying the label to the final string */ + memcpy(position, label, label_length); + available_length -= label_length; + position += label_length; + + /* + * Add a space in between the label and the dynamic string, + * if there is one. + */ + if (dynamic_string && available_length > 0) { + *position++ = ' '; + available_length--; + } + } + + /* Now append the dynamic string at the end of the final string. + * The string is cut in case it doesn't fit the remaining space. + */ + if (dynamic_string) { + size_t dynamic_string_length = strlen(dynamic_string); + + if (dynamic_string_length > 0) { + size_t remaining_length = MIN(available_length, dynamic_string_length); + memcpy(position, dynamic_string, remaining_length); + } + } + + /* + * GeckoProfiler will put "js::RunScript" on the stack, but it has * a stack address of "this", which is not terribly useful since * everything will show up as [stack] when building callgraphs. */ - if (label) - addrs[flipped] = sp_capture_writer_add_jitmap(self->capture, label); + if (final_string[0] != '\0') + addrs[flipped] = sp_capture_writer_add_jitmap(self->capture, final_string); else addrs[flipped] = SpCaptureAddress(entry.stackAddress()); } @@ -399,8 +411,6 @@ return; } - self->stack_depth = 0; - /* Setup our signal handler for SIGPROF delivery */ sa.sa_flags = SA_RESTART | SA_SIGINFO; sa.sa_sigaction = gjs_profiler_sigprof; @@ -449,8 +459,7 @@ self->running = true; /* Notify the JS runtime of where to put stack info */ - js::SetContextProfilingStack(self->cx, self->stack, &self->stack_depth, - G_N_ELEMENTS(self->stack)); + js::SetContextProfilingStack(self->cx, &self->stack); /* Start recording stack info */ js::EnableContextProfilingStack(self->cx, true); @@ -496,13 +505,12 @@ timer_delete(self->timer); js::EnableContextProfilingStack(self->cx, false); - js::SetContextProfilingStack(self->cx, nullptr, nullptr, 0); + js::SetContextProfilingStack(self->cx, nullptr); sp_capture_writer_flush(self->capture); g_clear_pointer(&self->capture, sp_capture_writer_unref); - self->stack_depth = 0; g_message("Profiler stopped"); #endif /* ENABLE_PROFILER */ diff -Nru gjs-1.53.3/gjs-srcs.mk gjs-1.54.3/gjs-srcs.mk --- gjs-1.53.3/gjs-srcs.mk 2018-06-18 22:41:13.000000000 +0000 +++ gjs-1.54.3/gjs-srcs.mk 2018-11-12 16:43:50.000000000 +0000 @@ -29,6 +29,8 @@ gi/gerror.cpp \ gi/gerror.h \ gi/gjs_gi_trace.h \ + gi/gobject.cpp \ + gi/gobject.h \ gi/gtype.cpp \ gi/gtype.h \ gi/interface.cpp \ @@ -39,6 +41,8 @@ gi/object.h \ gi/param.cpp \ gi/param.h \ + gi/private.cpp \ + gi/private.h \ gi/proxyutils.cpp \ gi/proxyutils.h \ gi/repo.cpp \ @@ -54,6 +58,9 @@ gjs/context.cpp \ gjs/context-private.h \ gjs/coverage.cpp \ + gjs/debugger.cpp \ + gjs/deprecation.cpp \ + gjs/deprecation.h \ gjs/engine.cpp \ gjs/engine.h \ gjs/global.cpp \ diff -Nru gjs-1.53.3/installed-tests/debugger/backtrace.debugger gjs-1.54.3/installed-tests/debugger/backtrace.debugger --- gjs-1.53.3/installed-tests/debugger/backtrace.debugger 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/backtrace.debugger 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,6 @@ +backtrace +c +bt +c +where +q diff -Nru gjs-1.53.3/installed-tests/debugger/backtrace.debugger.js gjs-1.54.3/installed-tests/debugger/backtrace.debugger.js --- gjs-1.53.3/installed-tests/debugger/backtrace.debugger.js 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/backtrace.debugger.js 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,8 @@ +debugger; +[[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]].forEach(array => { + debugger; + array.forEach(num => { + debugger; + print(num); + }); +}); diff -Nru gjs-1.53.3/installed-tests/debugger/backtrace.debugger.output gjs-1.54.3/installed-tests/debugger/backtrace.debugger.output --- gjs-1.53.3/installed-tests/debugger/backtrace.debugger.output 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/backtrace.debugger.output 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,14 @@ +GJS debugger. Type "help" for help +db> backtrace +#0 toplevel at backtrace.debugger.js:1:0 +db> c +Debugger statement, toplevel at backtrace.debugger.js:1:0 +db> bt +#0 toplevel at backtrace.debugger.js:1:0 +db> c +Debugger statement, ([object Array], 0, [object Array]) at backtrace.debugger.js:3:4 +db> where +#0 ([object Array], 0, [object Array]) at backtrace.debugger.js:3:4 +#1 toplevel at backtrace.debugger.js:2:0 +db> q +Program exited with code 0 diff -Nru gjs-1.53.3/installed-tests/debugger/breakpoint.debugger gjs-1.54.3/installed-tests/debugger/breakpoint.debugger --- gjs-1.53.3/installed-tests/debugger/breakpoint.debugger 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/breakpoint.debugger 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,7 @@ +breakpoint 2 +break 4 +b 6 +c +c +c +c diff -Nru gjs-1.53.3/installed-tests/debugger/breakpoint.debugger.js gjs-1.54.3/installed-tests/debugger/breakpoint.debugger.js --- gjs-1.53.3/installed-tests/debugger/breakpoint.debugger.js 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/breakpoint.debugger.js 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,7 @@ +print('1'); +print('2'); +function foo() { + print('Function foo'); +} +print('3'); +foo(); diff -Nru gjs-1.53.3/installed-tests/debugger/breakpoint.debugger.output gjs-1.54.3/installed-tests/debugger/breakpoint.debugger.output --- gjs-1.53.3/installed-tests/debugger/breakpoint.debugger.output 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/breakpoint.debugger.output 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,19 @@ +GJS debugger. Type "help" for help +db> breakpoint 2 +Breakpoint 1 at breakpoint.debugger.js:2:0 +db> break 4 +Breakpoint 2 at breakpoint.debugger.js:4:4 +db> b 6 +Breakpoint 3 at breakpoint.debugger.js:6:0 +db> c +1 +Breakpoint 1, toplevel at breakpoint.debugger.js:2:0 +db> c +2 +Breakpoint 3, toplevel at breakpoint.debugger.js:6:0 +db> c +3 +Breakpoint 2, foo() at breakpoint.debugger.js:4:4 +db> c +Function foo +Program exited with code 0 diff -Nru gjs-1.53.3/installed-tests/debugger/continue.debugger gjs-1.54.3/installed-tests/debugger/continue.debugger --- gjs-1.53.3/installed-tests/debugger/continue.debugger 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/continue.debugger 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,3 @@ +continue +cont +c diff -Nru gjs-1.53.3/installed-tests/debugger/continue.debugger.js gjs-1.54.3/installed-tests/debugger/continue.debugger.js --- gjs-1.53.3/installed-tests/debugger/continue.debugger.js 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/continue.debugger.js 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,2 @@ +debugger; +debugger; diff -Nru gjs-1.53.3/installed-tests/debugger/continue.debugger.output gjs-1.54.3/installed-tests/debugger/continue.debugger.output --- gjs-1.53.3/installed-tests/debugger/continue.debugger.output 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/continue.debugger.output 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,7 @@ +GJS debugger. Type "help" for help +db> continue +Debugger statement, toplevel at continue.debugger.js:1:0 +db> cont +Debugger statement, toplevel at continue.debugger.js:2:0 +db> c +Program exited with code 0 diff -Nru gjs-1.53.3/installed-tests/debugger/delete.debugger gjs-1.54.3/installed-tests/debugger/delete.debugger --- gjs-1.53.3/installed-tests/debugger/delete.debugger 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/delete.debugger 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,10 @@ +b 2 +b 3 +b 4 +b 5 +# Check that breakpoint 4 still remains after deleting 1-3 +delete 1 +del 2 +d 3 +c +c diff -Nru gjs-1.53.3/installed-tests/debugger/delete.debugger.js gjs-1.54.3/installed-tests/debugger/delete.debugger.js --- gjs-1.53.3/installed-tests/debugger/delete.debugger.js 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/delete.debugger.js 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,5 @@ +print('1'); +print('2'); +print('3'); +print('4'); +print('5'); diff -Nru gjs-1.53.3/installed-tests/debugger/delete.debugger.output gjs-1.54.3/installed-tests/debugger/delete.debugger.output --- gjs-1.53.3/installed-tests/debugger/delete.debugger.output 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/delete.debugger.output 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,25 @@ +GJS debugger. Type "help" for help +db> b 2 +Breakpoint 1 at delete.debugger.js:2:0 +db> b 3 +Breakpoint 2 at delete.debugger.js:3:0 +db> b 4 +Breakpoint 3 at delete.debugger.js:4:0 +db> b 5 +Breakpoint 4 at delete.debugger.js:5:0 +db> # Check that breakpoint 4 still remains after deleting 1-3 +db> delete 1 +Breakpoint 1 at delete.debugger.js:2:0 deleted +db> del 2 +Breakpoint 2 at delete.debugger.js:3:0 deleted +db> d 3 +Breakpoint 3 at delete.debugger.js:4:0 deleted +db> c +1 +2 +3 +4 +Breakpoint 4, toplevel at delete.debugger.js:5:0 +db> c +5 +Program exited with code 0 diff -Nru gjs-1.53.3/installed-tests/debugger/detach.debugger gjs-1.54.3/installed-tests/debugger/detach.debugger --- gjs-1.53.3/installed-tests/debugger/detach.debugger 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/detach.debugger 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1 @@ +detach diff -Nru gjs-1.53.3/installed-tests/debugger/detach.debugger.js gjs-1.54.3/installed-tests/debugger/detach.debugger.js --- gjs-1.53.3/installed-tests/debugger/detach.debugger.js 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/detach.debugger.js 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1 @@ +print('hi'); diff -Nru gjs-1.53.3/installed-tests/debugger/detach.debugger.output gjs-1.54.3/installed-tests/debugger/detach.debugger.output --- gjs-1.53.3/installed-tests/debugger/detach.debugger.output 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/detach.debugger.output 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,4 @@ +GJS debugger. Type "help" for help +db> detach +hi +Program exited with code 0 diff -Nru gjs-1.53.3/installed-tests/debugger/down-up.debugger gjs-1.54.3/installed-tests/debugger/down-up.debugger --- gjs-1.53.3/installed-tests/debugger/down-up.debugger 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/down-up.debugger 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,12 @@ +c +down +up +up +up +up +up +down +dn +dn +dn +c diff -Nru gjs-1.53.3/installed-tests/debugger/down-up.debugger.js gjs-1.54.3/installed-tests/debugger/down-up.debugger.js --- gjs-1.53.3/installed-tests/debugger/down-up.debugger.js 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/down-up.debugger.js 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,17 @@ +function a() { + b(); +} + +function b() { + c(); +} + +function c() { + d(); +} + +function d() { + debugger; +} + +a(); diff -Nru gjs-1.53.3/installed-tests/debugger/down-up.debugger.output gjs-1.54.3/installed-tests/debugger/down-up.debugger.output --- gjs-1.53.3/installed-tests/debugger/down-up.debugger.output 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/down-up.debugger.output 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,25 @@ +GJS debugger. Type "help" for help +db> c +Debugger statement, d() at down-up.debugger.js:14:4 +db> down +Youngest frame selected; you cannot go down. +db> up +#1 c() at down-up.debugger.js:10:4 +db> up +#2 b() at down-up.debugger.js:6:4 +db> up +#3 a() at down-up.debugger.js:2:4 +db> up +#4 toplevel at down-up.debugger.js:17:0 +db> up +Initial frame selected; you cannot go up. +db> down +#3 a() at down-up.debugger.js:2:4 +db> dn +#2 b() at down-up.debugger.js:6:4 +db> dn +#1 c() at down-up.debugger.js:10:4 +db> dn +#0 d() at down-up.debugger.js:14:4 +db> c +Program exited with code 0 diff -Nru gjs-1.53.3/installed-tests/debugger/finish.debugger gjs-1.54.3/installed-tests/debugger/finish.debugger --- gjs-1.53.3/installed-tests/debugger/finish.debugger 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/finish.debugger 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,5 @@ +c +finish +c +fin +c diff -Nru gjs-1.53.3/installed-tests/debugger/finish.debugger.js gjs-1.54.3/installed-tests/debugger/finish.debugger.js --- gjs-1.53.3/installed-tests/debugger/finish.debugger.js 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/finish.debugger.js 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,16 @@ +function foo() { + print('Print me'); + debugger; + print('Print me also'); +} + +function bar() { + print('Print me'); + debugger; + print('Print me also'); + return 5; +} + +foo(); +bar(); +print('Print me at the end'); diff -Nru gjs-1.53.3/installed-tests/debugger/finish.debugger.output gjs-1.54.3/installed-tests/debugger/finish.debugger.output --- gjs-1.53.3/installed-tests/debugger/finish.debugger.output 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/finish.debugger.output 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,21 @@ +GJS debugger. Type "help" for help +db> c +Print me +Debugger statement, foo() at finish.debugger.js:3:4 +db> finish +Run till exit from foo() at finish.debugger.js:3:4 +Print me also +No value returned. +toplevel at finish.debugger.js:14:0 +db> c +Print me +Debugger statement, bar() at finish.debugger.js:9:4 +db> fin +Run till exit from bar() at finish.debugger.js:9:4 +Print me also +Value returned is: +$1 = 5 +toplevel at finish.debugger.js:15:0 +db> c +Print me at the end +Program exited with code 0 diff -Nru gjs-1.53.3/installed-tests/debugger/frame.debugger gjs-1.54.3/installed-tests/debugger/frame.debugger --- gjs-1.53.3/installed-tests/debugger/frame.debugger 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/frame.debugger 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,4 @@ +c +frame 2 +f 1 +c diff -Nru gjs-1.53.3/installed-tests/debugger/frame.debugger.js gjs-1.54.3/installed-tests/debugger/frame.debugger.js --- gjs-1.53.3/installed-tests/debugger/frame.debugger.js 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/frame.debugger.js 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,9 @@ +function a() { + b(); +} + +function b() { + debugger; +} + +a(); diff -Nru gjs-1.53.3/installed-tests/debugger/frame.debugger.output gjs-1.54.3/installed-tests/debugger/frame.debugger.output --- gjs-1.53.3/installed-tests/debugger/frame.debugger.output 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/frame.debugger.output 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,9 @@ +GJS debugger. Type "help" for help +db> c +Debugger statement, b() at frame.debugger.js:6:4 +db> frame 2 +#2 toplevel at frame.debugger.js:9:0 +db> f 1 +#1 a() at frame.debugger.js:2:4 +db> c +Program exited with code 0 diff -Nru gjs-1.53.3/installed-tests/debugger/keys.debugger gjs-1.54.3/installed-tests/debugger/keys.debugger --- gjs-1.53.3/installed-tests/debugger/keys.debugger 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/keys.debugger 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,4 @@ +c +keys a +k a +c diff -Nru gjs-1.53.3/installed-tests/debugger/keys.debugger.js gjs-1.54.3/installed-tests/debugger/keys.debugger.js --- gjs-1.53.3/installed-tests/debugger/keys.debugger.js 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/keys.debugger.js 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,7 @@ +const a = { + foo: 1, + bar: null, + tres: undefined, +}; +debugger; +void a; \ No newline at end of file diff -Nru gjs-1.53.3/installed-tests/debugger/keys.debugger.output gjs-1.54.3/installed-tests/debugger/keys.debugger.output --- gjs-1.53.3/installed-tests/debugger/keys.debugger.output 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/keys.debugger.output 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,19 @@ +GJS debugger. Type "help" for help +db> c +Debugger statement, toplevel at keys.debugger.js:6:0 +db> keys a +$1 = [object Array] +[ + "foo", + "bar", + "tres" +] +db> k a +$2 = [object Array] +[ + "foo", + "bar", + "tres" +] +db> c +Program exited with code 0 diff -Nru gjs-1.53.3/installed-tests/debugger/next.debugger gjs-1.54.3/installed-tests/debugger/next.debugger --- gjs-1.53.3/installed-tests/debugger/next.debugger 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/next.debugger 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,9 @@ +c +next +n +n +n +n +n +n +n diff -Nru gjs-1.53.3/installed-tests/debugger/next.debugger.js gjs-1.54.3/installed-tests/debugger/next.debugger.js --- gjs-1.53.3/installed-tests/debugger/next.debugger.js 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/next.debugger.js 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,11 @@ +function a() { + debugger; + b(); + print('A line in a'); +} + +function b() { + print('A line in b'); +} + +a(); diff -Nru gjs-1.53.3/installed-tests/debugger/next.debugger.output gjs-1.54.3/installed-tests/debugger/next.debugger.output --- gjs-1.53.3/installed-tests/debugger/next.debugger.output 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/next.debugger.output 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,25 @@ +GJS debugger. Type "help" for help +db> c +Debugger statement, a() at next.debugger.js:2:4 +db> next +a() at next.debugger.js:2:4 +db> n +a() at next.debugger.js:3:4 +A line in b +db> n +a() at next.debugger.js:4:4 +A line in a +db> n +a() at next.debugger.js:5:0 +No value returned. +db> n +a() at next.debugger.js:5:0 +toplevel at next.debugger.js:11:0 +db> n +toplevel at next.debugger.js:11:0 +db> n +toplevel at next.debugger.js:12:0 +No value returned. +db> n +toplevel at next.debugger.js:12:0 +Program exited with code 0 diff -Nru gjs-1.53.3/installed-tests/debugger/print.debugger gjs-1.54.3/installed-tests/debugger/print.debugger --- gjs-1.53.3/installed-tests/debugger/print.debugger 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/print.debugger 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,21 @@ +c +# Simple types +print a +p b +p c +p d +p e +p f +p g +# Objects +print h +print/b h +print/p h +p i +p/b i +p j +p k +p/b k +p l +p m +c diff -Nru gjs-1.53.3/installed-tests/debugger/print.debugger.js gjs-1.54.3/installed-tests/debugger/print.debugger.js --- gjs-1.53.3/installed-tests/debugger/print.debugger.js 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/print.debugger.js 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,16 @@ +const {GObject} = imports.gi; +const a = undefined; +const b = null; +const c = 42; +const d = 'some string'; +const e = false; +const f = true; +const g = Symbol('foobar'); +const h = [1, 'money', 2, 'show', {three: 'to', 'get ready': 'go cat go'}]; +const i = {some: 'plain object', that: 'has keys'}; +const j = new Set([5, 6, 7]); +const k = class J {}; +const l = new GObject.Object(); +const m = new Error('message'); +debugger; +void (a, b, c, d, e, f, g, h, i, j, k, l, m); diff -Nru gjs-1.53.3/installed-tests/debugger/print.debugger.output gjs-1.54.3/installed-tests/debugger/print.debugger.output --- gjs-1.53.3/installed-tests/debugger/print.debugger.output 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/print.debugger.output 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,85 @@ +GJS debugger. Type "help" for help +db> c +Debugger statement, toplevel at print.debugger.js:15:0 +db> # Simple types +db> print a +$1 = undefined +db> p b +$2 = null +db> p c +$3 = 42 +db> p d +$4 = "some string" +db> p e +$5 = false +db> p f +$6 = true +db> p g +$7 = Symbol("foobar") +db> # Objects +db> print h +$8 = [object Array] +[ + 1, + "money", + 2, + "show", + { + "three": "to", + "get ready": "go cat go" + } +] +db> print/b h +$9 = [object Array] +{ + "0": 1, + "1": "money", + "2": 2, + "3": "show", + "4": "(...)", + "length": 5 +} +db> print/p h +$10 = [object Array] +[ + 1, + "money", + 2, + "show", + { + "three": "to", + "get ready": "go cat go" + } +] +db> p i +$11 = [object Object] +{ + "some": "plain object", + "that": "has keys" +} +db> p/b i +$12 = [object Object] +{ + "some": "plain object", + "that": "has keys" +} +db> p j +$13 = [object Set] +{} +db> p k +$14 = [object Function] +db> p/b k +$15 = [object Function] +{ + "prototype": "(...)", + "length": 0, + "name": "J" +} +db> p l +$16 = [object GObject_Object] +[object instance proxy GIName:GObject.Object jsobj@0xADDR native@0xADDR] +db> p m +$17 = [object Error] +Error: message +db> c +Program exited with code 0 diff -Nru gjs-1.53.3/installed-tests/debugger/quit.debugger gjs-1.54.3/installed-tests/debugger/quit.debugger --- gjs-1.53.3/installed-tests/debugger/quit.debugger 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/quit.debugger 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1 @@ +q diff -Nru gjs-1.53.3/installed-tests/debugger/quit.debugger.js gjs-1.54.3/installed-tests/debugger/quit.debugger.js --- gjs-1.53.3/installed-tests/debugger/quit.debugger.js 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/quit.debugger.js 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1 @@ +print('hi'); diff -Nru gjs-1.53.3/installed-tests/debugger/quit.debugger.output gjs-1.54.3/installed-tests/debugger/quit.debugger.output --- gjs-1.53.3/installed-tests/debugger/quit.debugger.output 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/quit.debugger.output 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,3 @@ +GJS debugger. Type "help" for help +db> q +Program exited with code 0 diff -Nru gjs-1.53.3/installed-tests/debugger/return.debugger gjs-1.54.3/installed-tests/debugger/return.debugger --- gjs-1.53.3/installed-tests/debugger/return.debugger 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/return.debugger 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,8 @@ +b 2 +b 6 +b 10 +c +return +ret 5 +ret `${4 * 10 + 2} is the answer` +c diff -Nru gjs-1.53.3/installed-tests/debugger/return.debugger.js gjs-1.54.3/installed-tests/debugger/return.debugger.js --- gjs-1.53.3/installed-tests/debugger/return.debugger.js 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/return.debugger.js 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,15 @@ +function func1() { + return 1; +} + +function func2() { + return 2; +} + +function func3() { + return 3; +} + +print(func1()); +print(func2()); +print(func3()); diff -Nru gjs-1.53.3/installed-tests/debugger/return.debugger.output gjs-1.54.3/installed-tests/debugger/return.debugger.output --- gjs-1.53.3/installed-tests/debugger/return.debugger.output 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/return.debugger.output 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,18 @@ +GJS debugger. Type "help" for help +db> b 2 +Breakpoint 1 at return.debugger.js:2:4 +db> b 6 +Breakpoint 2 at return.debugger.js:6:4 +db> b 10 +Breakpoint 3 at return.debugger.js:10:4 +db> c +Breakpoint 1, func1() at return.debugger.js:2:4 +db> return +undefined +Breakpoint 2, func2() at return.debugger.js:6:4 +db> ret 5 +5 +Breakpoint 3, func3() at return.debugger.js:10:4 +db> ret `${4 * 10 + 2} is the answer` +42 is the answer +Program exited with code 0 diff -Nru gjs-1.53.3/installed-tests/debugger/set.debugger gjs-1.54.3/installed-tests/debugger/set.debugger --- gjs-1.53.3/installed-tests/debugger/set.debugger 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/set.debugger 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,21 @@ +# Currently the only option is "pretty" for pretty-printing. Set doesn't yet +# allow setting variables in the program. +c +p a +set pretty 0 +p a +set pretty 1 +p a +set pretty off +p a +set pretty on +p a +set pretty false +p a +set pretty true +p a +set pretty no +p a +set pretty yes +p a +q diff -Nru gjs-1.53.3/installed-tests/debugger/set.debugger.js gjs-1.54.3/installed-tests/debugger/set.debugger.js --- gjs-1.53.3/installed-tests/debugger/set.debugger.js 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/set.debugger.js 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,3 @@ +const a = {}; +debugger; +void a; diff -Nru gjs-1.53.3/installed-tests/debugger/set.debugger.output gjs-1.54.3/installed-tests/debugger/set.debugger.output --- gjs-1.53.3/installed-tests/debugger/set.debugger.output 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/set.debugger.output 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,38 @@ +GJS debugger. Type "help" for help +db> # Currently the only option is "pretty" for pretty-printing. Set doesn't yet +db> # allow setting variables in the program. +db> c +Debugger statement, toplevel at set.debugger.js:2:0 +db> p a +$1 = [object Object] +{} +db> set pretty 0 +db> p a +$2 = [object Object] +db> set pretty 1 +db> p a +$3 = [object Object] +{} +db> set pretty off +db> p a +$4 = [object Object] +db> set pretty on +db> p a +$5 = [object Object] +{} +db> set pretty false +db> p a +$6 = [object Object] +db> set pretty true +db> p a +$7 = [object Object] +{} +db> set pretty no +db> p a +$8 = [object Object] +db> set pretty yes +db> p a +$9 = [object Object] +{} +db> q +Program exited with code 0 diff -Nru gjs-1.53.3/installed-tests/debugger/step.debugger gjs-1.54.3/installed-tests/debugger/step.debugger --- gjs-1.53.3/installed-tests/debugger/step.debugger 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/step.debugger 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,12 @@ +s +s +s +s +s +s +s +s +s +s +s +s diff -Nru gjs-1.53.3/installed-tests/debugger/step.debugger.js gjs-1.54.3/installed-tests/debugger/step.debugger.js --- gjs-1.53.3/installed-tests/debugger/step.debugger.js 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/step.debugger.js 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,10 @@ +function a() { + b(); + print('A line in a'); +} + +function b() { + print('A line in b'); +} + +a(); diff -Nru gjs-1.53.3/installed-tests/debugger/step.debugger.output gjs-1.54.3/installed-tests/debugger/step.debugger.output --- gjs-1.53.3/installed-tests/debugger/step.debugger.output 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/step.debugger.output 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,35 @@ +GJS debugger. Type "help" for help +db> s +toplevel at step.debugger.js:10:0 +entered frame: a() at step.debugger.js:2:4 +db> s +a() at step.debugger.js:2:4 +entered frame: b() at step.debugger.js:7:4 +db> s +b() at step.debugger.js:7:4 +A line in b +db> s +b() at step.debugger.js:8:0 +No value returned. +db> s +b() at step.debugger.js:8:0 +a() at step.debugger.js:2:4 +db> s +a() at step.debugger.js:2:4 +db> s +a() at step.debugger.js:3:4 +A line in a +db> s +a() at step.debugger.js:4:0 +No value returned. +db> s +a() at step.debugger.js:4:0 +toplevel at step.debugger.js:10:0 +db> s +toplevel at step.debugger.js:10:0 +db> s +toplevel at step.debugger.js:11:0 +No value returned. +db> s +toplevel at step.debugger.js:11:0 +Program exited with code 0 diff -Nru gjs-1.53.3/installed-tests/debugger/throw.debugger gjs-1.54.3/installed-tests/debugger/throw.debugger --- gjs-1.53.3/installed-tests/debugger/throw.debugger 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/throw.debugger 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,4 @@ +c +throw 'foobar' + 3.14; +fin +throw diff -Nru gjs-1.53.3/installed-tests/debugger/throw.debugger.js gjs-1.54.3/installed-tests/debugger/throw.debugger.js --- gjs-1.53.3/installed-tests/debugger/throw.debugger.js 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/throw.debugger.js 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,10 @@ +function a() { + debugger; + return 5; +} + +try { + a(); +} catch (e) { + print(`Exception: ${e}`); +} diff -Nru gjs-1.53.3/installed-tests/debugger/throw.debugger.output gjs-1.54.3/installed-tests/debugger/throw.debugger.output --- gjs-1.53.3/installed-tests/debugger/throw.debugger.output 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/throw.debugger.output 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,20 @@ +GJS debugger. Type "help" for help +db> c +Debugger statement, a() at throw.debugger.js:2:4 +db> throw 'foobar' + 3.14; +Unwinding due to exception. (Type 'c' to continue unwinding.) +#0 a() at throw.debugger.js:2:4 +Exception value is: +$1 = "foobar3.14" +db> fin +Run till exit from a() at throw.debugger.js:2:4 +Frame terminated by exception: +$2 = "foobar3.14" +(To rethrow it, type 'throw'.) +Unwinding due to exception. (Type 'c' to continue unwinding.) +#0 toplevel at throw.debugger.js:7:4 +Exception value is: +$3 = "foobar3.14" +db> throw +Exception: foobar3.14 +Program exited with code 0 diff -Nru gjs-1.53.3/installed-tests/debugger/until.debugger gjs-1.54.3/installed-tests/debugger/until.debugger --- gjs-1.53.3/installed-tests/debugger/until.debugger 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/until.debugger 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,4 @@ +until 3 +upto 5 +u 7 +c diff -Nru gjs-1.53.3/installed-tests/debugger/until.debugger.js gjs-1.54.3/installed-tests/debugger/until.debugger.js --- gjs-1.53.3/installed-tests/debugger/until.debugger.js 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/until.debugger.js 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,7 @@ +print('1'); +print('2'); +print('3'); +(function () { + print('4'); +})(); +print('5'); diff -Nru gjs-1.53.3/installed-tests/debugger/until.debugger.output gjs-1.54.3/installed-tests/debugger/until.debugger.output --- gjs-1.53.3/installed-tests/debugger/until.debugger.output 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger/until.debugger.output 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,17 @@ +GJS debugger. Type "help" for help +db> until 3 +toplevel at until.debugger.js:1:0 +1 +2 +db> upto 5 +toplevel at until.debugger.js:3:0 +3 +entered frame: () at until.debugger.js:5:4 +db> u 7 +() at until.debugger.js:5:4 +4 +No value returned. +toplevel at until.debugger.js:7:0 +db> c +5 +Program exited with code 0 diff -Nru gjs-1.53.3/installed-tests/debugger.test.in gjs-1.54.3/installed-tests/debugger.test.in --- gjs-1.53.3/installed-tests/debugger.test.in 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger.test.in 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,4 @@ +[Test] +Type=session +Exec=@pkglibexecdir@/installed-tests/debugger-test.sh @pkglibexecdir@/installed-tests/debugger/@name@ +Output=TAP diff -Nru gjs-1.53.3/installed-tests/debugger-test.sh gjs-1.54.3/installed-tests/debugger-test.sh --- gjs-1.53.3/installed-tests/debugger-test.sh 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/debugger-test.sh 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,23 @@ +#!/bin/bash + +if test "$GJS_USE_UNINSTALLED_FILES" = "1"; then + gjs="$TOP_BUILDDIR/gjs-console" +else + gjs=gjs-console +fi + +echo 1..1 + +DEBUGGER_SCRIPT="$1" +JS_SCRIPT="$1.js" +EXPECTED_OUTPUT="$1.output" +THE_DIFF=$("$gjs" -d "$JS_SCRIPT" < "$DEBUGGER_SCRIPT" | sed \ + -e "s#$1#$(basename $1)#g" \ + -e "s/0x[0-9a-f]\{4,16\}/0xADDR/g" \ + | diff -u "$EXPECTED_OUTPUT" -) +if test -n "$THE_DIFF"; then + echo "not ok 1 - $1" + echo "$THE_DIFF" | while read line; do echo "#$line"; done +else + echo "ok 1 - $1" +fi diff -Nru gjs-1.53.3/installed-tests/extra/gjs.supp gjs-1.54.3/installed-tests/extra/gjs.supp --- gjs-1.53.3/installed-tests/extra/gjs.supp 2018-05-21 16:28:05.000000000 +0000 +++ gjs-1.54.3/installed-tests/extra/gjs.supp 2018-11-12 17:04:16.000000000 +0000 @@ -1,35 +1,6 @@ # Valgrind suppressions file for GJS # This is intended to be used in addition to GLib's glib.supp file. -# We leak a small wrapper in GJS for each registered GType. - -{ - gtype-wrapper-new - Memcheck:Leak - match-leak-kinds: definite - fun:_Znwm - fun:gjs_gtype_create_gtype_wrapper -} - -{ - gtype-wrapper-qdata - Memcheck:Leak - match-leak-kinds: possible - ... - fun:type_set_qdata_W - fun:g_type_set_qdata - fun:gjs_gtype_create_gtype_wrapper -} - -{ - g_type_register_fundamental never freed - Memcheck:Leak - fun:calloc - ... - fun:g_type_register_fundamental - ... -} - # SpiderMonkey leaks { @@ -84,6 +55,27 @@ fun:clone } +# https://bugzilla.mozilla.org/show_bug.cgi?id=1478679 +{ + mozjs-bmo-1478679-1 + Memcheck:Leak + match-leak-kinds: all + ... + fun:createTable + fun:init + fun:init + fun:_ZN2js8coverage10LCovSource11writeScriptEP8JSScript +} + +{ + mozjs-bmo-1478679-2 + Memcheck:Leak + match-leak-kinds: all + ... + fun:_Z9js_strdupPKc + fun:_ZN2js8coverage15LCovCompartment11lookupOrAddEP13JSCompartmentPKc +} + # Various things that I don't believe are related to GJS { diff -Nru gjs-1.53.3/installed-tests/extra/lsan.supp gjs-1.54.3/installed-tests/extra/lsan.supp --- gjs-1.53.3/installed-tests/extra/lsan.supp 2018-05-11 20:04:51.000000000 +0000 +++ gjs-1.54.3/installed-tests/extra/lsan.supp 2018-11-12 17:04:16.000000000 +0000 @@ -1,8 +1,9 @@ # SpiderMonkey leaks a mutex for each GC helper thread. leak:js::HelperThread::threadLoop -# We leak a small wrapper in GJS for each registered GType. -leak:gjs_gtype_create_gtype_wrapper - # https://bugs.freedesktop.org/show_bug.cgi?id=105466 leak:libfontconfig.so.1 + +# https://bugzilla.mozilla.org/show_bug.cgi?id=1478679 +leak:js::coverage::LCovSource::writeScript +leak:js/src/util/Text.cpp diff -Nru gjs-1.53.3/installed-tests/js/testByteArray.js gjs-1.54.3/installed-tests/js/testByteArray.js --- gjs-1.53.3/installed-tests/js/testByteArray.js 2018-05-31 05:34:45.000000000 +0000 +++ gjs-1.54.3/installed-tests/js/testByteArray.js 2018-11-12 16:43:50.000000000 +0000 @@ -1,93 +1,7 @@ const ByteArray = imports.byteArray; +const {GIMarshallingTests, GLib} = imports.gi; describe('Byte array', function () { - it('has length 0 for empty array', function () { - let a = new ByteArray.ByteArray(); - expect(a.length).toEqual(0); - }); - - describe('initially sized to 10', function () { - let a; - beforeEach(function () { - a = new ByteArray.ByteArray(10); - }); - - it('has length 10', function () { - expect(a.length).toEqual(10); - }); - - it('is initialized to zeroes', function () { - for (let i = 0; i < a.length; ++i) { - expect(a[i]).toEqual(0); - } - }); - }); - - it('assigns values correctly', function () { - let a = new ByteArray.ByteArray(256); - - for (let i = 0; i < a.length; ++i) { - a[i] = 255 - i; - } - - for (let i = 0; i < a.length; ++i) { - expect(a[i]).toEqual(255 - i); - } - }); - - describe('assignment past end', function () { - let a; - beforeEach(function () { - a = new ByteArray.ByteArray(); - a[2] = 5; - }); - - it('implicitly lengthens the array', function () { - expect(a.length).toEqual(3); - expect(a[2]).toEqual(5); - }); - - it('implicitly creates zero bytes', function () { - expect(a[0]).toEqual(0); - expect(a[1]).toEqual(0); - }); - }); - - it('changes the length when assigning to length property', function () { - let a = new ByteArray.ByteArray(20); - expect(a.length).toEqual(20); - a.length = 5; - expect(a.length).toEqual(5); - }); - - describe('conversions', function () { - let a; - beforeEach(function () { - a = new ByteArray.ByteArray(); - a[0] = 255; - }); - - it('gives a byte 5 when assigning 5', function () { - a[0] = 5; - expect(a[0]).toEqual(5); - }); - - it('gives a byte 0 when assigning null', function () { - a[0] = null; - expect(a[0]).toEqual(0); - }); - - it('gives a byte 0 when assigning undefined', function () { - a[0] = undefined; - expect(a[0]).toEqual(0); - }); - - it('rounds off when assigning a double', function () { - a[0] = 3.14; - expect(a[0]).toEqual(3); - }); - }); - it('can be created from a string', function () { let a = ByteArray.fromString('abcd'); expect(a.length).toEqual(4); @@ -112,20 +26,37 @@ [0xe2, 0x85, 0x9c].forEach((val, ix) => expect(a[ix]).toEqual(val)); }); - it('can be created from an array', function () { - let a = ByteArray.fromArray([ 1, 2, 3, 4 ]); - expect(a.length).toEqual(4); - [1, 2, 3, 4].forEach((val, ix) => expect(a[ix]).toEqual(val)); - }); - - it('can be converted to a string of ASCII characters', function () { - let a = new ByteArray.ByteArray(); + it('can be converted to a string of ASCII characters', function() { + let a = new Uint8Array(4); a[0] = 97; a[1] = 98; a[2] = 99; a[3] = 100; - let s = a.toString(); + let s = ByteArray.toString(a); expect(s.length).toEqual(4); expect(s).toEqual('abcd'); }); + + describe('legacy toString() behavior', function () { + beforeEach(function () { + GLib.test_expect_message('Gjs', GLib.LogLevelFlags.LEVEL_WARNING, + 'Some code called array.toString()*'); + }); + + it('is preserved when created from a string', function () { + let a = ByteArray.fromString('⅜'); + expect(a.toString()).toEqual('⅜'); + }); + + it('is preserved when marshalled from GI', function () { + let a = GIMarshallingTests.bytearray_full_return(); + expect(() => a.toString()).toThrowError(TypeError, + /malformed UTF-8 character sequence/); + }); + + afterEach(function () { + GLib.test_assert_expected_messages_internal('Gjs', + 'testByteArray.js', 0, 'testToStringCompatibility'); + }); + }); }); diff -Nru gjs-1.53.3/installed-tests/js/testEverythingBasic.js gjs-1.54.3/installed-tests/js/testEverythingBasic.js --- gjs-1.53.3/installed-tests/js/testEverythingBasic.js 2018-05-26 06:51:03.000000000 +0000 +++ gjs-1.54.3/installed-tests/js/testEverythingBasic.js 2018-11-12 16:43:50.000000000 +0000 @@ -3,10 +3,12 @@ // We use Gio to have some objects that we know exist imports.gi.versions.Gdk = '3.0'; +imports.gi.versions.Gtk = '3.0'; const Gdk = imports.gi.Gdk; const GLib = imports.gi.GLib; const Gio = imports.gi.Gio; const GObject = imports.gi.GObject; +const Gtk = imports.gi.Gtk; describe('Life, the Universe and Everything', function () { it('includes booleans', function () { @@ -415,6 +417,44 @@ expect(Regress.TestEnum.param(Regress.TestEnum.VALUE4)).toEqual('value4'); }); + xit('can be answered with GObject.set()', function() { + let o = new Regress.TestObj(); + o.set({ string: 'Answer', int: 42 }); + expect(o.string).toBe('Answer'); + expect(o.int).toBe(42); + }).pend('https://gitlab.gnome.org/GNOME/gobject-introspection/issues/113'); + + describe('Object properties on GtkBuilder-constructed objects', function () { + let o1; + beforeAll(function () { + Gtk.init(null); + }); + + beforeEach(function () { + const ui = ` + + + Click me + + `; + + let builder = Gtk.Builder.new_from_string(ui, -1); + o1 = builder.get_object('button'); + }); + + it('are found on the GObject itself', function () { + expect(o1.label).toBe('Click me'); + }); + + it('are found on the GObject\'s parents', function () { + expect(o1.visible).toBeFalsy(); + }); + + it('are found on the GObject\'s interfaces', function () { + expect(o1.action_name).toBeNull(); + }); + }); + describe('Object-valued GProperty', function () { let o1, t1, t2; beforeEach(function () { diff -Nru gjs-1.53.3/installed-tests/js/testEverythingEncapsulated.js gjs-1.54.3/installed-tests/js/testEverythingEncapsulated.js --- gjs-1.53.3/installed-tests/js/testEverythingEncapsulated.js 2018-06-18 22:44:01.000000000 +0000 +++ gjs-1.54.3/installed-tests/js/testEverythingEncapsulated.js 2018-11-12 16:59:46.000000000 +0000 @@ -216,14 +216,7 @@ expect(() => obj.function_ptr).toThrow(); }); - it('silently does not set read-only fields', function () { - obj.some_int8 = 41; - expect(obj.some_int8).toEqual(42); - expect(obj.int).toEqual(42); - }); - - it('throws an error in strict mode when setting a read-only field', function () { - 'use strict'; + it('throws when setting a read-only field', function () { expect(() => obj.some_int8 = 41).toThrow(); }); @@ -241,6 +234,16 @@ expect(obj.name_conflict).toEqual(42); expect(obj.name_conflict instanceof Function).toBeFalsy(); }); + + xit('sets write-only properties', function () { + expect(obj.int).not.toEqual(0); + obj.write_only = true; + expect(obj.int).toEqual(0); + }); + + it('gives undefined for write-only properties', function () { + expect(obj.write_only).not.toBeDefined(); + }); }); describe('Introspected function length', function () { diff -Nru gjs-1.53.3/installed-tests/js/testGIMarshalling.js gjs-1.54.3/installed-tests/js/testGIMarshalling.js --- gjs-1.53.3/installed-tests/js/testGIMarshalling.js 2018-06-18 22:44:01.000000000 +0000 +++ gjs-1.54.3/installed-tests/js/testGIMarshalling.js 2018-11-08 04:20:06.000000000 +0000 @@ -214,8 +214,8 @@ }); }); -describe('GByteArray', function () { - const refByteArray = ByteArray.fromArray([0, 49, 0xFF, 51]); +describe('GByteArray', function() { + const refByteArray = Uint8Array.from([0, 49, 0xFF, 51]); it('can be passed in with transfer none', function () { expect(() => GIMarshallingTests.bytearray_none_in(refByteArray)) @@ -232,8 +232,8 @@ }); }); -describe('GBytes', function () { - const refByteArray = ByteArray.fromArray([0, 49, 0xFF, 51]); +describe('GBytes', function() { + const refByteArray = Uint8Array.from([0, 49, 0xFF, 51]); it('can be created from an array and passed in', function () { let bytes = GLib.Bytes.new([0, 49, 0xFF, 51]); @@ -265,7 +265,7 @@ expect(array[1]).toEqual(42); array[1] = 49; // Flip the value back // Now convert back to GBytes - expect(() => GIMarshallingTests.gbytes_none_in(array.toGBytes())) + expect(() => GIMarshallingTests.gbytes_none_in(ByteArray.toGBytes(array))) .not.toThrow(); }); @@ -678,4 +678,12 @@ obj.some_gvalue = 'foo'; expect(obj.some_gvalue).toEqual('foo'); }); + + xit('gets a read-only property', function () { + expect(obj.some_readonly).toEqual(42); + }).pend('https://gitlab.gnome.org/GNOME/gobject-introspection/merge_requests/32'); + + xit('throws when setting a read-only property', function () { + expect(() => obj.some_readonly = 35).toThrow(); + }).pend('https://gitlab.gnome.org/GNOME/gobject-introspection/merge_requests/32'); }); diff -Nru gjs-1.53.3/installed-tests/js/testGLib.js gjs-1.54.3/installed-tests/js/testGLib.js --- gjs-1.53.3/installed-tests/js/testGLib.js 2018-02-18 02:13:41.000000000 +0000 +++ gjs-1.54.3/installed-tests/js/testGLib.js 2018-11-12 16:43:50.000000000 +0000 @@ -1,3 +1,4 @@ +const ByteArray = imports.byteArray; const GLib = imports.gi.GLib; describe('GVariant constructor', function () { @@ -40,4 +41,11 @@ maybe_variant = new GLib.Variant('ms', 'string'); expect(maybe_variant.deep_unpack()).toEqual('string'); }); + + it('constructs a byte array variant', function () { + const byteArray = Uint8Array.from('pizza', c => c.charCodeAt(0)); + const byteArrayVariant = new GLib.Variant('ay', byteArray); + expect(ByteArray.toString(byteArrayVariant.deep_unpack())) + .toEqual('pizza'); + }); }); diff -Nru gjs-1.53.3/installed-tests/js/testGObjectClass.js gjs-1.54.3/installed-tests/js/testGObjectClass.js --- gjs-1.53.3/installed-tests/js/testGObjectClass.js 2018-02-18 02:13:41.000000000 +0000 +++ gjs-1.54.3/installed-tests/js/testGObjectClass.js 2018-11-08 04:20:06.000000000 +0000 @@ -139,6 +139,8 @@ } }); +const Cla$$ = GObject.registerClass(class Cla$$ extends MyObject {}); + const MyCustomInit = GObject.registerClass(class MyCustomInit extends GObject.Object { _instance_init() { this.foo = true; @@ -292,6 +294,13 @@ expect(derived.readwrite).toEqual('yes'); }); + it('can have any valid class name', function () { + let obj = new Cla$$(); + + expect(obj instanceof Cla$$).toBeTruthy(); + expect(obj instanceof MyObject).toBeTruthy(); + }); + it('calls its _instance_init() function while chaining up in constructor', function () { let instance = new MyCustomInit(); expect(instance.foo).toBeTruthy(); @@ -334,4 +343,29 @@ }, }, class BadOverride extends GObject.Object {})).toThrow(); }); + + it('does not pollute the wrong prototype with GObject properties', function () { + const MyCustomCharset = GObject.registerClass(class MyCustomCharset extends Gio.CharsetConverter { + _init() { + super._init(); + void this.from_charset; + } + }); + + const MySecondCustomCharset = GObject.registerClass(class MySecondCustomCharset extends GObject.Object { + _init() { + super._init(); + this.from_charset = 'another value'; + } + }); + + expect (() => new MyCustomCharset() && new MySecondCustomCharset()).not.toThrow(); + }); + + it('resolves properties from interfaces', function() { + const mon = Gio.NetworkMonitor.get_default(); + expect(mon.network_available).toBeDefined(); + expect(mon.networkAvailable).toBeDefined(); + expect(mon['network-available']).toBeDefined(); + }); }); diff -Nru gjs-1.53.3/installed-tests/js/testGObjectInterface.js gjs-1.54.3/installed-tests/js/testGObjectInterface.js --- gjs-1.53.3/installed-tests/js/testGObjectInterface.js 2018-02-18 02:13:41.000000000 +0000 +++ gjs-1.54.3/installed-tests/js/testGObjectInterface.js 2018-11-08 04:20:06.000000000 +0000 @@ -84,6 +84,22 @@ } }); +const ImplementationOfIntrospectedInterface = GObject.registerClass({ + Implements: [Gio.Action], + Properties: { + 'enabled': GObject.ParamSpec.override('enabled', Gio.Action), + 'name': GObject.ParamSpec.override('name', Gio.Action), + 'state': GObject.ParamSpec.override('state', Gio.Action), + 'state-type': GObject.ParamSpec.override('state-type', Gio.Action), + 'parameter-type': GObject.ParamSpec.override('parameter-type', + Gio.Action) + } +}, class ImplementationOfIntrospectedInterface extends GObject.Object { + get name() { + return 'inaction'; + } +}); + describe('GObject interface', function () { it('cannot be instantiated', function () { expect(() => new AGObjectInterface()).toThrow(); @@ -247,6 +263,11 @@ 253, 'testGObjectMustOverrideInterfaceProperties'); }); + it('can have introspected properties overriden', function() { + let obj = new ImplementationOfIntrospectedInterface(); + expect(obj.name).toEqual('inaction'); + }); + it('can be implemented by a class as well as its parent class', function () { const SubObject = GObject.registerClass( class SubObject extends GObjectImplementingGObjectInterface {}); diff -Nru gjs-1.53.3/installed-tests/js/testLegacyByteArray.js gjs-1.54.3/installed-tests/js/testLegacyByteArray.js --- gjs-1.53.3/installed-tests/js/testLegacyByteArray.js 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/installed-tests/js/testLegacyByteArray.js 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,113 @@ +const ByteArray = imports.byteArray; +const GIMarshallingTests = imports.gi.GIMarshallingTests; + +describe('Legacy byte array', function() { + it('has length 0 for empty array', function() { + let a = new ByteArray.ByteArray(); + expect(a.length).toEqual(0); + }); + + describe('initially sized to 10', function() { + let a; + beforeEach(function() { + a = new ByteArray.ByteArray(10); + }); + + it('has length 10', function() { + expect(a.length).toEqual(10); + }); + + it('is initialized to zeroes', function() { + for (let i = 0; i < a.length; ++i) { + expect(a[i]).toEqual(0); + } + }); + }); + + it('assigns values correctly', function() { + let a = new ByteArray.ByteArray(256); + + for (let i = 0; i < a.length; ++i) { + a[i] = 255 - i; + } + + for (let i = 0; i < a.length; ++i) { + expect(a[i]).toEqual(255 - i); + } + }); + + describe('assignment past end', function() { + let a; + beforeEach(function() { + a = new ByteArray.ByteArray(); + a[2] = 5; + }); + + it('implicitly lengthens the array', function() { + expect(a.length).toEqual(3); + expect(a[2]).toEqual(5); + }); + + it('implicitly creates zero bytes', function() { + expect(a[0]).toEqual(0); + expect(a[1]).toEqual(0); + }); + }); + + it('changes the length when assigning to length property', function() { + let a = new ByteArray.ByteArray(20); + expect(a.length).toEqual(20); + a.length = 5; + expect(a.length).toEqual(5); + }); + + describe('conversions', function() { + let a; + beforeEach(function() { + a = new ByteArray.ByteArray(); + a[0] = 255; + }); + + it('gives a byte 5 when assigning 5', function() { + a[0] = 5; + expect(a[0]).toEqual(5); + }); + + it('gives a byte 0 when assigning null', function() { + a[0] = null; + expect(a[0]).toEqual(0); + }); + + it('gives a byte 0 when assigning undefined', function() { + a[0] = undefined; + expect(a[0]).toEqual(0); + }); + + it('rounds off when assigning a double', function() { + a[0] = 3.14; + expect(a[0]).toEqual(3); + }); + }); + + it('can be created from an array', function() { + let a = ByteArray.fromArray([1, 2, 3, 4]); + expect(a.length).toEqual(4); + [1, 2, 3, 4].forEach((val, ix) => expect(a[ix]).toEqual(val)); + }); + + it('can be converted to a string of ASCII characters', function() { + let a = new ByteArray.ByteArray(4); + a[0] = 97; + a[1] = 98; + a[2] = 99; + a[3] = 100; + let s = a.toString(); + expect(s.length).toEqual(4); + expect(s).toEqual('abcd'); + }); + + it('can be passed in with transfer none', function() { + const refByteArray = ByteArray.fromArray([0, 49, 0xFF, 51]); + expect(() => GIMarshallingTests.bytearray_none_in(refByteArray)).not.toThrow(); + }); +}); diff -Nru gjs-1.53.3/installed-tests/js/testLegacyGObject.js gjs-1.54.3/installed-tests/js/testLegacyGObject.js --- gjs-1.53.3/installed-tests/js/testLegacyGObject.js 2018-04-15 05:08:50.000000000 +0000 +++ gjs-1.54.3/installed-tests/js/testLegacyGObject.js 2018-11-08 04:20:06.000000000 +0000 @@ -164,6 +164,11 @@ } }); +const OddlyNamed = new Lang.Class({ + Name: 'Legacy.OddlyNamed', + Extends: MyObject +}); + const MyCustomInit = new Lang.Class({ Name: 'MyCustomInit', Extends: GObject.Object, @@ -316,6 +321,13 @@ expect(derived.readwrite).toEqual('yes'); }); + it('can have any valid Lang.Class name', function () { + let obj = new OddlyNamed(); + + expect(obj instanceof OddlyNamed).toBeTruthy(); + expect(obj instanceof MyObject).toBeTruthy(); + }); + it('calls its _instance_init() function while chaining up in constructor', function () { let instance = new MyCustomInit(); expect(instance.foo).toBeTruthy(); diff -Nru gjs-1.53.3/libgjs-private/gjs-gdbus-wrapper.cpp gjs-1.54.3/libgjs-private/gjs-gdbus-wrapper.cpp --- gjs-1.53.3/libgjs-private/gjs-gdbus-wrapper.cpp 2018-05-11 20:04:51.000000000 +0000 +++ gjs-1.54.3/libgjs-private/gjs-gdbus-wrapper.cpp 2018-11-08 04:20:06.000000000 +0000 @@ -35,7 +35,8 @@ _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wcast-function-type\"") #endif -G_DEFINE_TYPE(GjsDBusImplementation, gjs_dbus_implementation, G_TYPE_DBUS_INTERFACE_SKELETON) +G_DEFINE_TYPE_WITH_PRIVATE(GjsDBusImplementation, gjs_dbus_implementation, + G_TYPE_DBUS_INTERFACE_SKELETON); #if __GNUC__ >= 8 _Pragma("GCC diagnostic pop") #endif @@ -217,8 +218,6 @@ GObjectClass *gobject_class = G_OBJECT_CLASS(klass); GDBusInterfaceSkeletonClass *skeleton_class = G_DBUS_INTERFACE_SKELETON_CLASS(klass); - g_type_class_add_private(klass, sizeof(GjsDBusImplementationPrivate)); - gobject_class->finalize = gjs_dbus_implementation_finalize; gobject_class->set_property = gjs_dbus_implementation_set_property; diff -Nru gjs-1.53.3/m4/ax_cxx_compile_stdcxx_11.m4 gjs-1.54.3/m4/ax_cxx_compile_stdcxx_11.m4 --- gjs-1.53.3/m4/ax_cxx_compile_stdcxx_11.m4 2017-07-06 04:35:31.000000000 +0000 +++ gjs-1.54.3/m4/ax_cxx_compile_stdcxx_11.m4 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ -# ============================================================================= -# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html -# ============================================================================= -# -# SYNOPSIS -# -# AX_CXX_COMPILE_STDCXX_11([ext|noext], [mandatory|optional]) -# -# DESCRIPTION -# -# Check for baseline language coverage in the compiler for the C++11 -# standard; if necessary, add switches to CXX and CXXCPP to enable -# support. -# -# This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX -# macro with the version set to C++11. The two optional arguments are -# forwarded literally as the second and third argument respectively. -# Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for -# more information. If you want to use this macro, you also need to -# download the ax_cxx_compile_stdcxx.m4 file. -# -# LICENSE -# -# Copyright (c) 2008 Benjamin Kosnik -# Copyright (c) 2012 Zack Weinberg -# Copyright (c) 2013 Roy Stogner -# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov -# Copyright (c) 2015 Paul Norman -# Copyright (c) 2015 Moritz Klammler -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 18 - -AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX]) -AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [AX_CXX_COMPILE_STDCXX([11], [$1], [$2])]) diff -Nru gjs-1.53.3/m4/ax_cxx_compile_stdcxx_14.m4 gjs-1.54.3/m4/ax_cxx_compile_stdcxx_14.m4 --- gjs-1.53.3/m4/ax_cxx_compile_stdcxx_14.m4 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/m4/ax_cxx_compile_stdcxx_14.m4 2018-10-08 04:46:50.000000000 +0000 @@ -0,0 +1,34 @@ +# ============================================================================= +# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_14.html +# ============================================================================= +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX_14([ext|noext], [mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the C++14 +# standard; if necessary, add switches to CXX and CXXCPP to enable +# support. +# +# This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX +# macro with the version set to C++14. The two optional arguments are +# forwarded literally as the second and third argument respectively. +# Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for +# more information. If you want to use this macro, you also need to +# download the ax_cxx_compile_stdcxx.m4 file. +# +# LICENSE +# +# Copyright (c) 2015 Moritz Klammler +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 5 + +AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX]) +AC_DEFUN([AX_CXX_COMPILE_STDCXX_14], [AX_CXX_COMPILE_STDCXX([14], [$1], [$2])]) diff -Nru gjs-1.53.3/Makefile.am gjs-1.54.3/Makefile.am --- gjs-1.53.3/Makefile.am 2018-05-11 20:04:51.000000000 +0000 +++ gjs-1.54.3/Makefile.am 2018-11-08 15:02:31.000000000 +0000 @@ -201,7 +201,7 @@ TAG_PREFIX=GJS_ COMPRESSION=.bz2 -DISTCLEANFILES=gjs-*.syscap installed-tests/scripts/*.test installed-tests/js/*.test +DISTCLEANFILES=gjs-*.syscap PACKAGE=@PACKAGE@ VERSION=@VERSION@ diff -Nru gjs-1.53.3/Makefile.in gjs-1.54.3/Makefile.in --- gjs-1.53.3/Makefile.in 2018-06-18 22:55:26.000000000 +0000 +++ gjs-1.54.3/Makefile.in 2018-11-12 17:17:15.000000000 +0000 @@ -17,6 +17,7 @@ + VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ @@ -121,18 +122,23 @@ @ENABLE_GTK_TRUE@ $(NULL) @ENABLE_CAIRO_TRUE@am__append_18 = installed-tests/js/testCairo.js -TESTS = gjs-tests.gtester$(EXEEXT) $(am__EXEEXT_2) $(am__EXEEXT_5) +TESTS = gjs-tests.gtester$(EXEEXT) $(am__EXEEXT_2) $(am__EXEEXT_5) \ + $(am__EXEEXT_6) gjsinsttest_PROGRAMS = $(am__EXEEXT_1) @BUILDOPT_INSTALL_TESTS_TRUE@am__append_19 = minijasmine -@BUILDOPT_INSTALL_TESTS_TRUE@am__append_20 = $(TEST_INTROSPECTION_TYPELIBS) -@BUILDOPT_INSTALL_TESTS_TRUE@am__append_21 = \ -@BUILDOPT_INSTALL_TESTS_TRUE@ $(jasmine_tests:.js=.test) \ -@BUILDOPT_INSTALL_TESTS_TRUE@ $(simple_tests:%=%.test) \ +@BUILDOPT_INSTALL_TESTS_TRUE@am__append_20 = installed-tests/debugger-test.sh +@BUILDOPT_INSTALL_TESTS_TRUE@am__append_21 = $(TEST_INTROSPECTION_TYPELIBS) +@BUILDOPT_INSTALL_TESTS_TRUE@am__append_22 = \ +@BUILDOPT_INSTALL_TESTS_TRUE@ $(jasmine_tests:.js=.test) \ +@BUILDOPT_INSTALL_TESTS_TRUE@ $(simple_tests:%=%.test) \ +@BUILDOPT_INSTALL_TESTS_TRUE@ $(debugger_tests:.debugger=.test) \ @BUILDOPT_INSTALL_TESTS_TRUE@ $(NULL) -@BUILDOPT_INSTALL_TESTS_TRUE@am__append_22 = $(jasmine_tests) -@BUILDOPT_INSTALL_TESTS_TRUE@am__append_23 = $(simple_tests) -@BUILDOPT_INSTALL_TESTS_TRUE@am__append_24 = libregress.la libwarnlib.la libgimarshallingtests.la +@BUILDOPT_INSTALL_TESTS_TRUE@am__append_23 = $(jasmine_tests) +@BUILDOPT_INSTALL_TESTS_TRUE@am__append_24 = $(simple_tests) +@BUILDOPT_INSTALL_TESTS_TRUE@am__append_25 = $(debugger_tests) +@BUILDOPT_INSTALL_TESTS_TRUE@am__append_26 = libregress.la libwarnlib.la libgimarshallingtests.la +@BUILDOPT_INSTALL_TESTS_TRUE@am__append_27 = $(installedtestmeta_DATA) subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_append_compile_flags.m4 \ @@ -147,7 +153,7 @@ $(top_srcdir)/m4/ax_compiler_flags_gir.m4 \ $(top_srcdir)/m4/ax_compiler_flags_ldflags.m4 \ $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ - $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_14.m4 \ $(top_srcdir)/m4/ax_is_release.m4 \ $(top_srcdir)/m4/ax_pkg_check_modules.m4 \ $(top_srcdir)/m4/ax_require_defined.m4 \ @@ -176,6 +182,7 @@ CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(gjsinsttestdir)" \ "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkglibdir)" \ + "$(DESTDIR)$(gjsinsttestdir)" "$(DESTDIR)$(debuggertestsdir)" \ "$(DESTDIR)$(gjsjsdir)" "$(DESTDIR)$(lsandir)" \ "$(DESTDIR)$(valgrinddir)" "$(DESTDIR)$(gjsinsttestdir)" \ "$(DESTDIR)$(installedtestmetadir)" \ @@ -265,14 +272,16 @@ gi/enumeration.h gi/foreign.cpp gi/foreign.h \ gi/fundamental.cpp gi/fundamental.h gi/function.cpp \ gi/function.h gi/gerror.cpp gi/gerror.h gi/gjs_gi_trace.h \ - gi/gtype.cpp gi/gtype.h gi/interface.cpp gi/interface.h \ - gi/ns.cpp gi/ns.h gi/object.cpp gi/object.h gi/param.cpp \ - gi/param.h gi/proxyutils.cpp gi/proxyutils.h gi/repo.cpp \ - gi/repo.h gi/toggle.cpp gi/toggle.h gi/union.cpp gi/union.h \ - gi/value.cpp gi/value.h gjs/byteArray.cpp gjs/byteArray.h \ - gjs/context.cpp gjs/context-private.h gjs/coverage.cpp \ - gjs/engine.cpp gjs/engine.h gjs/global.cpp gjs/global.h \ - gjs/importer.cpp gjs/importer.h gjs/jsapi-class.h \ + gi/gobject.cpp gi/gobject.h gi/gtype.cpp gi/gtype.h \ + gi/interface.cpp gi/interface.h gi/ns.cpp gi/ns.h \ + gi/object.cpp gi/object.h gi/param.cpp gi/param.h \ + gi/private.cpp gi/private.h gi/proxyutils.cpp gi/proxyutils.h \ + gi/repo.cpp gi/repo.h gi/toggle.cpp gi/toggle.h gi/union.cpp \ + gi/union.h gi/value.cpp gi/value.h gjs/byteArray.cpp \ + gjs/byteArray.h gjs/context.cpp gjs/context-private.h \ + gjs/coverage.cpp gjs/debugger.cpp gjs/deprecation.cpp \ + gjs/deprecation.h gjs/engine.cpp gjs/engine.h gjs/global.cpp \ + gjs/global.h gjs/importer.cpp gjs/importer.h gjs/jsapi-class.h \ gjs/jsapi-dynamic-class.cpp gjs/jsapi-util.cpp \ gjs/jsapi-util.h gjs/jsapi-util-args.h \ gjs/jsapi-util-error.cpp gjs/jsapi-util-root.h \ @@ -291,13 +300,15 @@ gi/libgjs_la-closure.lo gi/libgjs_la-enumeration.lo \ gi/libgjs_la-foreign.lo gi/libgjs_la-fundamental.lo \ gi/libgjs_la-function.lo gi/libgjs_la-gerror.lo \ - gi/libgjs_la-gtype.lo gi/libgjs_la-interface.lo \ - gi/libgjs_la-ns.lo gi/libgjs_la-object.lo \ - gi/libgjs_la-param.lo gi/libgjs_la-proxyutils.lo \ + gi/libgjs_la-gobject.lo gi/libgjs_la-gtype.lo \ + gi/libgjs_la-interface.lo gi/libgjs_la-ns.lo \ + gi/libgjs_la-object.lo gi/libgjs_la-param.lo \ + gi/libgjs_la-private.lo gi/libgjs_la-proxyutils.lo \ gi/libgjs_la-repo.lo gi/libgjs_la-toggle.lo \ gi/libgjs_la-union.lo gi/libgjs_la-value.lo \ gjs/libgjs_la-byteArray.lo gjs/libgjs_la-context.lo \ - gjs/libgjs_la-coverage.lo gjs/libgjs_la-engine.lo \ + gjs/libgjs_la-coverage.lo gjs/libgjs_la-debugger.lo \ + gjs/libgjs_la-deprecation.lo gjs/libgjs_la-engine.lo \ gjs/libgjs_la-global.lo gjs/libgjs_la-importer.lo \ gjs/libgjs_la-jsapi-dynamic-class.lo \ gjs/libgjs_la-jsapi-util.lo gjs/libgjs_la-jsapi-util-error.lo \ @@ -370,6 +381,7 @@ minijasmine-jsunit-resources.$(OBJEXT) minijasmine_OBJECTS = $(am_minijasmine_OBJECTS) minijasmine_DEPENDENCIES = $(am__DEPENDENCIES_1) libgjs.la +SCRIPTS = $(gjsinsttest_SCRIPTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false @@ -397,11 +409,13 @@ gi/$(DEPDIR)/libgjs_la-function.Plo \ gi/$(DEPDIR)/libgjs_la-fundamental.Plo \ gi/$(DEPDIR)/libgjs_la-gerror.Plo \ + gi/$(DEPDIR)/libgjs_la-gobject.Plo \ gi/$(DEPDIR)/libgjs_la-gtype.Plo \ gi/$(DEPDIR)/libgjs_la-interface.Plo \ gi/$(DEPDIR)/libgjs_la-ns.Plo \ gi/$(DEPDIR)/libgjs_la-object.Plo \ gi/$(DEPDIR)/libgjs_la-param.Plo \ + gi/$(DEPDIR)/libgjs_la-private.Plo \ gi/$(DEPDIR)/libgjs_la-proxyutils.Plo \ gi/$(DEPDIR)/libgjs_la-repo.Plo \ gi/$(DEPDIR)/libgjs_la-toggle.Plo \ @@ -411,6 +425,8 @@ gjs/$(DEPDIR)/libgjs_la-byteArray.Plo \ gjs/$(DEPDIR)/libgjs_la-context.Plo \ gjs/$(DEPDIR)/libgjs_la-coverage.Plo \ + gjs/$(DEPDIR)/libgjs_la-debugger.Plo \ + gjs/$(DEPDIR)/libgjs_la-deprecation.Plo \ gjs/$(DEPDIR)/libgjs_la-engine.Plo \ gjs/$(DEPDIR)/libgjs_la-global.Plo \ gjs/$(DEPDIR)/libgjs_la-importer.Plo \ @@ -510,10 +526,11 @@ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac -DATA = $(dist_gjsjs_DATA) $(dist_lsan_DATA) $(dist_valgrind_DATA) \ - $(gjsinsttest_DATA) $(installedtestmeta_DATA) \ - $(jsscripttests_DATA) $(jstests_DATA) $(noinst_DATA) \ - $(pkgconfig_DATA) $(tapset_DATA) $(typelib_DATA) +DATA = $(debuggertests_DATA) $(dist_gjsjs_DATA) $(dist_lsan_DATA) \ + $(dist_valgrind_DATA) $(gjsinsttest_DATA) \ + $(installedtestmeta_DATA) $(jsscripttests_DATA) \ + $(jstests_DATA) $(noinst_DATA) $(pkgconfig_DATA) \ + $(tapset_DATA) $(typelib_DATA) HEADERS = $(nobase_gjs_public_include_HEADERS) $(noinst_HEADERS) am__extra_recursive_targets = check-valgrind-recursive \ check-valgrind-memcheck-recursive \ @@ -715,6 +732,7 @@ installed-tests/js/testGio.js \ installed-tests/js/testImporter.js \ installed-tests/js/testLang.js \ + installed-tests/js/testLegacyByteArray.js \ installed-tests/js/testLegacyClass.js \ installed-tests/js/testLegacyGObject.js \ installed-tests/js/testLocale.js \ @@ -730,6 +748,23 @@ @ENABLE_GTK_TRUE@ installed-tests/js/testLegacyGtk.js am__EXEEXT_5 = $(am__EXEEXT_3) $(am__append_16) $(am__EXEEXT_4) \ $(am__append_18) +am__EXEEXT_6 = installed-tests/debugger/backtrace.debugger \ + installed-tests/debugger/breakpoint.debugger \ + installed-tests/debugger/continue.debugger \ + installed-tests/debugger/delete.debugger \ + installed-tests/debugger/detach.debugger \ + installed-tests/debugger/down-up.debugger \ + installed-tests/debugger/finish.debugger \ + installed-tests/debugger/frame.debugger \ + installed-tests/debugger/keys.debugger \ + installed-tests/debugger/next.debugger \ + installed-tests/debugger/print.debugger \ + installed-tests/debugger/quit.debugger \ + installed-tests/debugger/return.debugger \ + installed-tests/debugger/set.debugger \ + installed-tests/debugger/step.debugger \ + installed-tests/debugger/throw.debugger \ + installed-tests/debugger/until.debugger TEST_SUITE_LOG = test-suite.log am__test_logs1 = $(TESTS:=.log) am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) @@ -748,17 +783,19 @@ esac am__test_logs4 = $(am__test_logs3:.sh.log=.log) SH_LOG_COMPILE = $(SH_LOG_COMPILER) $(AM_SH_LOG_FLAGS) $(SH_LOG_FLAGS) -TEST_LOGS = $(am__test_logs4:.js.log=.log) +am__test_logs5 = $(am__test_logs4:.js.log=.log) JS_LOG_COMPILE = $(JS_LOG_COMPILER) $(AM_JS_LOG_FLAGS) $(JS_LOG_FLAGS) +TEST_LOGS = $(am__test_logs5:.debugger.log=.log) +DEBUGGER_LOG_COMPILE = $(DEBUGGER_LOG_COMPILER) \ + $(AM_DEBUGGER_LOG_FLAGS) $(DEBUGGER_LOG_FLAGS) am__DIST_COMMON = $(srcdir)/Makefile-examples.am \ $(srcdir)/Makefile-insttest.am $(srcdir)/Makefile-modules.am \ $(srcdir)/Makefile-test.am $(srcdir)/Makefile.in \ $(srcdir)/config.h.in $(srcdir)/gjs-1.0.pc.in \ $(srcdir)/gjs-modules-srcs.mk $(srcdir)/gjs-srcs.mk \ $(top_srcdir)/win32/config.h.win32.in AUTHORS COPYING \ - ChangeLog INSTALL NEWS README compile config.guess \ - config.rpath config.sub depcomp install-sh ltmain.sh missing \ - tap-driver.sh + ChangeLog INSTALL NEWS README compile config.guess config.sub \ + depcomp install-sh ltmain.sh missing tap-driver.sh DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) @@ -823,8 +860,6 @@ GENHTML = @GENHTML@ GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ GI_DATADIR = @GI_DATADIR@ -GJSTESTS_CFLAGS = @GJSTESTS_CFLAGS@ -GJSTESTS_LIBS = @GJSTESTS_LIBS@ GJS_CAIRO_CFLAGS = @GJS_CAIRO_CFLAGS@ GJS_CAIRO_LIBS = @GJS_CAIRO_LIBS@ GJS_CAIRO_XLIB_CFLAGS = @GJS_CAIRO_XLIB_CFLAGS@ @@ -839,7 +874,7 @@ GJS_LIBS = @GJS_LIBS@ GJS_VERSION = @GJS_VERSION@ GREP = @GREP@ -HAVE_CXX11 = @HAVE_CXX11@ +HAVE_CXX14 = @HAVE_CXX14@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ @@ -969,7 +1004,8 @@ CLEANFILES = $(nodist_libmodules_resources_la_SOURCES) \ $(INTROSPECTION_GIRS) $(typelib_DATA) mock-js-resources.c \ jsunit-resources.c jsunit-resources.h $(NULL) \ - $(TEST_INTROSPECTION_GIRS) $(TEST_INTROSPECTION_TYPELIBS) + $(TEST_INTROSPECTION_GIRS) $(TEST_INTROSPECTION_TYPELIBS) \ + $(am__append_27) EXTRA_DIST = gjs-1.0.pc.in gi/gjs_gi_probes.d $(tapset_in_files) \ $(modules_resource_files) \ $(srcdir)/modules/modules.gresource.xml examples/clutter.js \ @@ -983,7 +1019,11 @@ installed-tests/js/testGObjectDestructionAccess.js \ installed-tests/js/testLegacyGtk.js \ installed-tests/extra/gjs.supp installed-tests/extra/lsan.supp \ - $(NULL) $(simple_tests) installed-tests/minijasmine.test.in \ + $(NULL) $(simple_tests) $(debugger_tests) \ + $(debugger_tests:%=%.js) $(debugger_tests:%=%.output) $(NULL) \ + installed-tests/debugger-test.sh \ + installed-tests/debugger.test.in \ + installed-tests/minijasmine.test.in \ installed-tests/script.test.in \ installed-tests/js/jsunit.gresources.xml $(NULL) autogen.sh \ COPYING.LGPL doc/ByteArray.md doc/cairo.md doc/Hacking.md \ @@ -1043,6 +1083,8 @@ gi/gerror.cpp \ gi/gerror.h \ gi/gjs_gi_trace.h \ + gi/gobject.cpp \ + gi/gobject.h \ gi/gtype.cpp \ gi/gtype.h \ gi/interface.cpp \ @@ -1053,6 +1095,8 @@ gi/object.h \ gi/param.cpp \ gi/param.h \ + gi/private.cpp \ + gi/private.h \ gi/proxyutils.cpp \ gi/proxyutils.h \ gi/repo.cpp \ @@ -1068,6 +1112,9 @@ gjs/context.cpp \ gjs/context-private.h \ gjs/coverage.cpp \ + gjs/debugger.cpp \ + gjs/deprecation.cpp \ + gjs/deprecation.h \ gjs/engine.cpp \ gjs/engine.h \ gjs/global.cpp \ @@ -1276,13 +1323,13 @@ gjs_tests_gtester_CPPFLAGS = \ $(AM_CPPFLAGS) \ -DGJS_COMPILATION \ - $(GJSTESTS_CFLAGS) \ + $(GJS_CFLAGS) \ $(gjs_directory_defines) \ -I$(top_srcdir)/test gjs_tests_gtester_LDADD = \ libgjs.la \ - $(GJSTESTS_LIBS) + $(GJS_LIBS) gjs_tests_gtester_SOURCES = \ test/gjs-tests.cpp \ @@ -1392,6 +1439,7 @@ installed-tests/js/testGio.js \ installed-tests/js/testImporter.js \ installed-tests/js/testLang.js \ + installed-tests/js/testLegacyByteArray.js \ installed-tests/js/testLegacyClass.js \ installed-tests/js/testLegacyGObject.js \ installed-tests/js/testLocale.js \ @@ -1406,6 +1454,8 @@ jasmine_tests = $(common_jstests_files) $(am__append_16) \ $(am__append_17) $(am__append_18) +@ENABLE_GTK_FALSE@GTK_TESTS_ENVIRONMENT = +@ENABLE_GTK_TRUE@GTK_TESTS_ENVIRONMENT = export ENABLE_GTK=yes; VALGRIND_SUPPRESSIONS_FILES = \ $(datadir)/glib-2.0/valgrind/glib.supp \ $(top_srcdir)/installed-tests/extra/gjs.supp \ @@ -1431,8 +1481,9 @@ export G_FILENAME_ENCODING=latin1; \ export LSAN_OPTIONS="suppressions=$(abs_top_srcdir)/installed-tests/extra/lsan.supp"; \ export NO_AT_BRIDGE=1; \ - export LC_ALL=C.UTF-8; \ + export LC_ALL=C.UTF-8; \ $(COVERAGE_TESTS_ENVIRONMENT) \ + $(GTK_TESTS_ENVIRONMENT) \ $(XVFB_START) \ $(DBUS_SESSION_COMMAND) \ $(NULL) @@ -1442,13 +1493,35 @@ installed-tests/scripts/testWarnings.sh \ $(NULL) -TEST_EXTENSIONS = .gtester .sh .js +debugger_tests = \ + installed-tests/debugger/backtrace.debugger \ + installed-tests/debugger/breakpoint.debugger \ + installed-tests/debugger/continue.debugger \ + installed-tests/debugger/delete.debugger \ + installed-tests/debugger/detach.debugger \ + installed-tests/debugger/down-up.debugger \ + installed-tests/debugger/finish.debugger \ + installed-tests/debugger/frame.debugger \ + installed-tests/debugger/keys.debugger \ + installed-tests/debugger/next.debugger \ + installed-tests/debugger/print.debugger \ + installed-tests/debugger/quit.debugger \ + installed-tests/debugger/return.debugger \ + installed-tests/debugger/set.debugger \ + installed-tests/debugger/step.debugger \ + installed-tests/debugger/throw.debugger \ + installed-tests/debugger/until.debugger \ + $(NULL) + +TEST_EXTENSIONS = .gtester .sh .js .debugger LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/tap-driver.sh GTESTER_LOG_DRIVER = $(LOG_DRIVER) GTESTER_LOG_COMPILER = $(top_srcdir)/test/run-test SH_LOG_DRIVER = $(LOG_DRIVER) JS_LOG_DRIVER = $(LOG_DRIVER) JS_LOG_COMPILER = $$LOG_COMPILER $$LOG_FLAGS $(top_builddir)/minijasmine +DEBUGGER_LOG_DRIVER = $(LOG_DRIVER) +DEBUGGER_LOG_COMPILER = $(top_srcdir)/installed-tests/debugger-test.sh CODE_COVERAGE_IGNORE_PATTERN = */{include,mfbt,gjs/test}/* CODE_COVERAGE_GENHTML_OPTIONS = \ $(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT) \ @@ -1461,11 +1534,14 @@ installedtestmetadir = $(datadir)/installed-tests/gjs jstestsdir = $(gjsinsttestdir)/js jsscripttestsdir = $(gjsinsttestdir)/scripts -gjsinsttest_DATA = $(am__append_20) -installedtestmeta_DATA = $(am__append_21) -jstests_DATA = $(am__append_22) -jsscripttests_DATA = $(am__append_23) -pkglib_LTLIBRARIES = $(am__append_24) +debuggertestsdir = $(gjsinsttestdir)/debugger +gjsinsttest_DATA = $(am__append_21) +gjsinsttest_SCRIPTS = $(am__append_20) +installedtestmeta_DATA = $(am__append_22) +jstests_DATA = $(am__append_23) +jsscripttests_DATA = $(am__append_24) +debuggertests_DATA = $(am__append_25) +pkglib_LTLIBRARIES = $(am__append_26) # Colin's handy Makefile bits for: # 1) stuffing tarballs with pre-generated scripts from your workstation @@ -1480,7 +1556,7 @@ # Customize to taste TAG_PREFIX = GJS_ COMPRESSION = .bz2 -DISTCLEANFILES = gjs-*.syscap installed-tests/scripts/*.test installed-tests/js/*.test +DISTCLEANFILES = gjs-*.syscap DISTNAME = $(PACKAGE)-$(VERSION).tar$(COMPRESSION) TAG_VERSION := $(shell echo $(VERSION) | $(SED) s/\\\./_/g) CPPCHECK = cppcheck @@ -1488,7 +1564,7 @@ $(MAKE) $(AM_MAKEFLAGS) all-am .SUFFIXES: -.SUFFIXES: .c .cpp .gtester .gtester$(EXEEXT) .js .js$(EXEEXT) .lo .log .o .obj .sh .sh$(EXEEXT) .trs +.SUFFIXES: .c .cpp .debugger .debugger$(EXEEXT) .gtester .gtester$(EXEEXT) .js .js$(EXEEXT) .lo .log .o .obj .sh .sh$(EXEEXT) .trs am--refresh: Makefile @: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(srcdir)/gjs-srcs.mk $(srcdir)/Makefile-modules.am $(srcdir)/gjs-modules-srcs.mk $(srcdir)/Makefile-examples.am $(srcdir)/Makefile-test.am $(srcdir)/Makefile-insttest.am $(am__configure_deps) @@ -1817,6 +1893,8 @@ gi/$(DEPDIR)/$(am__dirstamp) gi/libgjs_la-gerror.lo: gi/$(am__dirstamp) \ gi/$(DEPDIR)/$(am__dirstamp) +gi/libgjs_la-gobject.lo: gi/$(am__dirstamp) \ + gi/$(DEPDIR)/$(am__dirstamp) gi/libgjs_la-gtype.lo: gi/$(am__dirstamp) gi/$(DEPDIR)/$(am__dirstamp) gi/libgjs_la-interface.lo: gi/$(am__dirstamp) \ gi/$(DEPDIR)/$(am__dirstamp) @@ -1824,6 +1902,8 @@ gi/libgjs_la-object.lo: gi/$(am__dirstamp) \ gi/$(DEPDIR)/$(am__dirstamp) gi/libgjs_la-param.lo: gi/$(am__dirstamp) gi/$(DEPDIR)/$(am__dirstamp) +gi/libgjs_la-private.lo: gi/$(am__dirstamp) \ + gi/$(DEPDIR)/$(am__dirstamp) gi/libgjs_la-proxyutils.lo: gi/$(am__dirstamp) \ gi/$(DEPDIR)/$(am__dirstamp) gi/libgjs_la-repo.lo: gi/$(am__dirstamp) gi/$(DEPDIR)/$(am__dirstamp) @@ -1843,6 +1923,10 @@ gjs/$(DEPDIR)/$(am__dirstamp) gjs/libgjs_la-coverage.lo: gjs/$(am__dirstamp) \ gjs/$(DEPDIR)/$(am__dirstamp) +gjs/libgjs_la-debugger.lo: gjs/$(am__dirstamp) \ + gjs/$(DEPDIR)/$(am__dirstamp) +gjs/libgjs_la-deprecation.lo: gjs/$(am__dirstamp) \ + gjs/$(DEPDIR)/$(am__dirstamp) gjs/libgjs_la-engine.lo: gjs/$(am__dirstamp) \ gjs/$(DEPDIR)/$(am__dirstamp) gjs/libgjs_la-global.lo: gjs/$(am__dirstamp) \ @@ -1955,6 +2039,41 @@ minijasmine$(EXEEXT): $(minijasmine_OBJECTS) $(minijasmine_DEPENDENCIES) $(EXTRA_minijasmine_DEPENDENCIES) @rm -f minijasmine$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(minijasmine_OBJECTS) $(minijasmine_LDADD) $(LIBS) +install-gjsinsttestSCRIPTS: $(gjsinsttest_SCRIPTS) + @$(NORMAL_INSTALL) + @list='$(gjsinsttest_SCRIPTS)'; test -n "$(gjsinsttestdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(gjsinsttestdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(gjsinsttestdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n' \ + -e 'h;s|.*|.|' \ + -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) { files[d] = files[d] " " $$1; \ + if (++n[d] == $(am__install_max)) { \ + print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ + else { print "f", d "/" $$4, $$1 } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(gjsinsttestdir)$$dir'"; \ + $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(gjsinsttestdir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-gjsinsttestSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(gjsinsttest_SCRIPTS)'; test -n "$(gjsinsttestdir)" || exit 0; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 's,.*/,,;$(transform)'`; \ + dir='$(DESTDIR)$(gjsinsttestdir)'; $(am__uninstall_files_from_dir) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -1986,11 +2105,13 @@ @AMDEP_TRUE@@am__include@ @am__quote@gi/$(DEPDIR)/libgjs_la-function.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gi/$(DEPDIR)/libgjs_la-fundamental.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gi/$(DEPDIR)/libgjs_la-gerror.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@gi/$(DEPDIR)/libgjs_la-gobject.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gi/$(DEPDIR)/libgjs_la-gtype.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gi/$(DEPDIR)/libgjs_la-interface.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gi/$(DEPDIR)/libgjs_la-ns.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gi/$(DEPDIR)/libgjs_la-object.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gi/$(DEPDIR)/libgjs_la-param.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@gi/$(DEPDIR)/libgjs_la-private.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gi/$(DEPDIR)/libgjs_la-proxyutils.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gi/$(DEPDIR)/libgjs_la-repo.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gi/$(DEPDIR)/libgjs_la-toggle.Plo@am__quote@ # am--include-marker @@ -2000,6 +2121,8 @@ @AMDEP_TRUE@@am__include@ @am__quote@gjs/$(DEPDIR)/libgjs_la-byteArray.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gjs/$(DEPDIR)/libgjs_la-context.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gjs/$(DEPDIR)/libgjs_la-coverage.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@gjs/$(DEPDIR)/libgjs_la-debugger.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@gjs/$(DEPDIR)/libgjs_la-deprecation.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gjs/$(DEPDIR)/libgjs_la-engine.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gjs/$(DEPDIR)/libgjs_la-global.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@gjs/$(DEPDIR)/libgjs_la-importer.Plo@am__quote@ # am--include-marker @@ -2340,6 +2463,13 @@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgjs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o gi/libgjs_la-gerror.lo `test -f 'gi/gerror.cpp' || echo '$(srcdir)/'`gi/gerror.cpp +gi/libgjs_la-gobject.lo: gi/gobject.cpp +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgjs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT gi/libgjs_la-gobject.lo -MD -MP -MF gi/$(DEPDIR)/libgjs_la-gobject.Tpo -c -o gi/libgjs_la-gobject.lo `test -f 'gi/gobject.cpp' || echo '$(srcdir)/'`gi/gobject.cpp +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) gi/$(DEPDIR)/libgjs_la-gobject.Tpo gi/$(DEPDIR)/libgjs_la-gobject.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='gi/gobject.cpp' object='gi/libgjs_la-gobject.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgjs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o gi/libgjs_la-gobject.lo `test -f 'gi/gobject.cpp' || echo '$(srcdir)/'`gi/gobject.cpp + gi/libgjs_la-gtype.lo: gi/gtype.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgjs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT gi/libgjs_la-gtype.lo -MD -MP -MF gi/$(DEPDIR)/libgjs_la-gtype.Tpo -c -o gi/libgjs_la-gtype.lo `test -f 'gi/gtype.cpp' || echo '$(srcdir)/'`gi/gtype.cpp @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) gi/$(DEPDIR)/libgjs_la-gtype.Tpo gi/$(DEPDIR)/libgjs_la-gtype.Plo @@ -2375,6 +2505,13 @@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgjs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o gi/libgjs_la-param.lo `test -f 'gi/param.cpp' || echo '$(srcdir)/'`gi/param.cpp +gi/libgjs_la-private.lo: gi/private.cpp +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgjs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT gi/libgjs_la-private.lo -MD -MP -MF gi/$(DEPDIR)/libgjs_la-private.Tpo -c -o gi/libgjs_la-private.lo `test -f 'gi/private.cpp' || echo '$(srcdir)/'`gi/private.cpp +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) gi/$(DEPDIR)/libgjs_la-private.Tpo gi/$(DEPDIR)/libgjs_la-private.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='gi/private.cpp' object='gi/libgjs_la-private.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgjs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o gi/libgjs_la-private.lo `test -f 'gi/private.cpp' || echo '$(srcdir)/'`gi/private.cpp + gi/libgjs_la-proxyutils.lo: gi/proxyutils.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgjs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT gi/libgjs_la-proxyutils.lo -MD -MP -MF gi/$(DEPDIR)/libgjs_la-proxyutils.Tpo -c -o gi/libgjs_la-proxyutils.lo `test -f 'gi/proxyutils.cpp' || echo '$(srcdir)/'`gi/proxyutils.cpp @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) gi/$(DEPDIR)/libgjs_la-proxyutils.Tpo gi/$(DEPDIR)/libgjs_la-proxyutils.Plo @@ -2431,6 +2568,20 @@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgjs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o gjs/libgjs_la-coverage.lo `test -f 'gjs/coverage.cpp' || echo '$(srcdir)/'`gjs/coverage.cpp +gjs/libgjs_la-debugger.lo: gjs/debugger.cpp +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgjs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT gjs/libgjs_la-debugger.lo -MD -MP -MF gjs/$(DEPDIR)/libgjs_la-debugger.Tpo -c -o gjs/libgjs_la-debugger.lo `test -f 'gjs/debugger.cpp' || echo '$(srcdir)/'`gjs/debugger.cpp +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) gjs/$(DEPDIR)/libgjs_la-debugger.Tpo gjs/$(DEPDIR)/libgjs_la-debugger.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='gjs/debugger.cpp' object='gjs/libgjs_la-debugger.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgjs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o gjs/libgjs_la-debugger.lo `test -f 'gjs/debugger.cpp' || echo '$(srcdir)/'`gjs/debugger.cpp + +gjs/libgjs_la-deprecation.lo: gjs/deprecation.cpp +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgjs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT gjs/libgjs_la-deprecation.lo -MD -MP -MF gjs/$(DEPDIR)/libgjs_la-deprecation.Tpo -c -o gjs/libgjs_la-deprecation.lo `test -f 'gjs/deprecation.cpp' || echo '$(srcdir)/'`gjs/deprecation.cpp +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) gjs/$(DEPDIR)/libgjs_la-deprecation.Tpo gjs/$(DEPDIR)/libgjs_la-deprecation.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='gjs/deprecation.cpp' object='gjs/libgjs_la-deprecation.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgjs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o gjs/libgjs_la-deprecation.lo `test -f 'gjs/deprecation.cpp' || echo '$(srcdir)/'`gjs/deprecation.cpp + gjs/libgjs_la-engine.lo: gjs/engine.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgjs_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT gjs/libgjs_la-engine.lo -MD -MP -MF gjs/$(DEPDIR)/libgjs_la-engine.Tpo -c -o gjs/libgjs_la-engine.lo `test -f 'gjs/engine.cpp' || echo '$(srcdir)/'`gjs/engine.cpp @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) gjs/$(DEPDIR)/libgjs_la-engine.Tpo gjs/$(DEPDIR)/libgjs_la-engine.Plo @@ -2683,6 +2834,27 @@ distclean-libtool: -rm -f libtool config.lt +install-debuggertestsDATA: $(debuggertests_DATA) + @$(NORMAL_INSTALL) + @list='$(debuggertests_DATA)'; test -n "$(debuggertestsdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(debuggertestsdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(debuggertestsdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(debuggertestsdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(debuggertestsdir)" || exit $$?; \ + done + +uninstall-debuggertestsDATA: + @$(NORMAL_UNINSTALL) + @list='$(debuggertests_DATA)'; test -n "$(debuggertestsdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(debuggertestsdir)'; $(am__uninstall_files_from_dir) install-dist_gjsjsDATA: $(dist_gjsjs_DATA) @$(NORMAL_INSTALL) @list='$(dist_gjsjs_DATA)'; test -n "$(gjsjsdir)" || list=; \ @@ -3165,6 +3337,20 @@ @am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ @am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_JS_LOG_DRIVER_FLAGS) $(JS_LOG_DRIVER_FLAGS) -- $(JS_LOG_COMPILE) \ @am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) +.debugger.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(DEBUGGER_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_DEBUGGER_LOG_DRIVER_FLAGS) $(DEBUGGER_LOG_DRIVER_FLAGS) -- $(DEBUGGER_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.debugger$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(DEBUGGER_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_DEBUGGER_LOG_DRIVER_FLAGS) $(DEBUGGER_LOG_DRIVER_FLAGS) -- $(DEBUGGER_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am @@ -3336,12 +3522,12 @@ $(MAKE) $(AM_MAKEFLAGS) check-TESTS check: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) check-am -all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) $(DATA) $(HEADERS) \ - config.h +all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) $(SCRIPTS) $(DATA) \ + $(HEADERS) config.h install-binPROGRAMS: install-libLTLIBRARIES installdirs: - for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(gjsinsttestdir)" "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkglibdir)" "$(DESTDIR)$(gjsjsdir)" "$(DESTDIR)$(lsandir)" "$(DESTDIR)$(valgrinddir)" "$(DESTDIR)$(gjsinsttestdir)" "$(DESTDIR)$(installedtestmetadir)" "$(DESTDIR)$(jsscripttestsdir)" "$(DESTDIR)$(jstestsdir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(tapsetdir)" "$(DESTDIR)$(typelibdir)" "$(DESTDIR)$(gjs_public_includedir)"; do \ + for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(gjsinsttestdir)" "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkglibdir)" "$(DESTDIR)$(gjsinsttestdir)" "$(DESTDIR)$(debuggertestsdir)" "$(DESTDIR)$(gjsjsdir)" "$(DESTDIR)$(lsandir)" "$(DESTDIR)$(valgrinddir)" "$(DESTDIR)$(gjsinsttestdir)" "$(DESTDIR)$(installedtestmetadir)" "$(DESTDIR)$(jsscripttestsdir)" "$(DESTDIR)$(jstestsdir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(tapsetdir)" "$(DESTDIR)$(typelibdir)" "$(DESTDIR)$(gjs_public_includedir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: $(BUILT_SOURCES) @@ -3436,11 +3622,13 @@ -rm -f gi/$(DEPDIR)/libgjs_la-function.Plo -rm -f gi/$(DEPDIR)/libgjs_la-fundamental.Plo -rm -f gi/$(DEPDIR)/libgjs_la-gerror.Plo + -rm -f gi/$(DEPDIR)/libgjs_la-gobject.Plo -rm -f gi/$(DEPDIR)/libgjs_la-gtype.Plo -rm -f gi/$(DEPDIR)/libgjs_la-interface.Plo -rm -f gi/$(DEPDIR)/libgjs_la-ns.Plo -rm -f gi/$(DEPDIR)/libgjs_la-object.Plo -rm -f gi/$(DEPDIR)/libgjs_la-param.Plo + -rm -f gi/$(DEPDIR)/libgjs_la-private.Plo -rm -f gi/$(DEPDIR)/libgjs_la-proxyutils.Plo -rm -f gi/$(DEPDIR)/libgjs_la-repo.Plo -rm -f gi/$(DEPDIR)/libgjs_la-toggle.Plo @@ -3450,6 +3638,8 @@ -rm -f gjs/$(DEPDIR)/libgjs_la-byteArray.Plo -rm -f gjs/$(DEPDIR)/libgjs_la-context.Plo -rm -f gjs/$(DEPDIR)/libgjs_la-coverage.Plo + -rm -f gjs/$(DEPDIR)/libgjs_la-debugger.Plo + -rm -f gjs/$(DEPDIR)/libgjs_la-deprecation.Plo -rm -f gjs/$(DEPDIR)/libgjs_la-engine.Plo -rm -f gjs/$(DEPDIR)/libgjs_la-global.Plo -rm -f gjs/$(DEPDIR)/libgjs_la-importer.Plo @@ -3513,9 +3703,10 @@ info-am: -install-data-am: install-dist_gjsjsDATA install-dist_lsanDATA \ - install-dist_valgrindDATA install-gjsinsttestDATA \ - install-gjsinsttestPROGRAMS install-installedtestmetaDATA \ +install-data-am: install-debuggertestsDATA install-dist_gjsjsDATA \ + install-dist_lsanDATA install-dist_valgrindDATA \ + install-gjsinsttestDATA install-gjsinsttestPROGRAMS \ + install-gjsinsttestSCRIPTS install-installedtestmetaDATA \ install-jsscripttestsDATA install-jstestsDATA \ install-nobase_gjs_public_includeHEADERS install-pkgconfigDATA \ install-tapsetDATA install-typelibDATA @@ -3562,11 +3753,13 @@ -rm -f gi/$(DEPDIR)/libgjs_la-function.Plo -rm -f gi/$(DEPDIR)/libgjs_la-fundamental.Plo -rm -f gi/$(DEPDIR)/libgjs_la-gerror.Plo + -rm -f gi/$(DEPDIR)/libgjs_la-gobject.Plo -rm -f gi/$(DEPDIR)/libgjs_la-gtype.Plo -rm -f gi/$(DEPDIR)/libgjs_la-interface.Plo -rm -f gi/$(DEPDIR)/libgjs_la-ns.Plo -rm -f gi/$(DEPDIR)/libgjs_la-object.Plo -rm -f gi/$(DEPDIR)/libgjs_la-param.Plo + -rm -f gi/$(DEPDIR)/libgjs_la-private.Plo -rm -f gi/$(DEPDIR)/libgjs_la-proxyutils.Plo -rm -f gi/$(DEPDIR)/libgjs_la-repo.Plo -rm -f gi/$(DEPDIR)/libgjs_la-toggle.Plo @@ -3576,6 +3769,8 @@ -rm -f gjs/$(DEPDIR)/libgjs_la-byteArray.Plo -rm -f gjs/$(DEPDIR)/libgjs_la-context.Plo -rm -f gjs/$(DEPDIR)/libgjs_la-coverage.Plo + -rm -f gjs/$(DEPDIR)/libgjs_la-debugger.Plo + -rm -f gjs/$(DEPDIR)/libgjs_la-deprecation.Plo -rm -f gjs/$(DEPDIR)/libgjs_la-engine.Plo -rm -f gjs/$(DEPDIR)/libgjs_la-global.Plo -rm -f gjs/$(DEPDIR)/libgjs_la-importer.Plo @@ -3639,9 +3834,10 @@ ps-am: -uninstall-am: uninstall-binPROGRAMS uninstall-dist_gjsjsDATA \ - uninstall-dist_lsanDATA uninstall-dist_valgrindDATA \ - uninstall-gjsinsttestDATA uninstall-gjsinsttestPROGRAMS \ +uninstall-am: uninstall-binPROGRAMS uninstall-debuggertestsDATA \ + uninstall-dist_gjsjsDATA uninstall-dist_lsanDATA \ + uninstall-dist_valgrindDATA uninstall-gjsinsttestDATA \ + uninstall-gjsinsttestPROGRAMS uninstall-gjsinsttestSCRIPTS \ uninstall-installedtestmetaDATA uninstall-jsscripttestsDATA \ uninstall-jstestsDATA uninstall-libLTLIBRARIES \ uninstall-nobase_gjs_public_includeHEADERS \ @@ -3667,13 +3863,15 @@ distclean-libtool distclean-tags distcleancheck distdir \ distuninstallcheck dvi dvi-am html html-am info info-am \ install install-am install-binPROGRAMS install-data \ - install-data-am install-dist_gjsjsDATA install-dist_lsanDATA \ + install-data-am install-debuggertestsDATA \ + install-dist_gjsjsDATA install-dist_lsanDATA \ install-dist_valgrindDATA install-dvi install-dvi-am \ install-exec install-exec-am install-exec-hook \ install-gjsinsttestDATA install-gjsinsttestPROGRAMS \ - install-html install-html-am install-info install-info-am \ - install-installedtestmetaDATA install-jsscripttestsDATA \ - install-jstestsDATA install-libLTLIBRARIES install-man \ + install-gjsinsttestSCRIPTS install-html install-html-am \ + install-info install-info-am install-installedtestmetaDATA \ + install-jsscripttestsDATA install-jstestsDATA \ + install-libLTLIBRARIES install-man \ install-nobase_gjs_public_includeHEADERS install-pdf \ install-pdf-am install-pkgconfigDATA install-pkglibLTLIBRARIES \ install-ps install-ps-am install-strip install-tapsetDATA \ @@ -3681,9 +3879,10 @@ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ pdf pdf-am ps ps-am recheck tags tags-am uninstall \ - uninstall-am uninstall-binPROGRAMS uninstall-dist_gjsjsDATA \ - uninstall-dist_lsanDATA uninstall-dist_valgrindDATA \ - uninstall-gjsinsttestDATA uninstall-gjsinsttestPROGRAMS \ + uninstall-am uninstall-binPROGRAMS uninstall-debuggertestsDATA \ + uninstall-dist_gjsjsDATA uninstall-dist_lsanDATA \ + uninstall-dist_valgrindDATA uninstall-gjsinsttestDATA \ + uninstall-gjsinsttestPROGRAMS uninstall-gjsinsttestSCRIPTS \ uninstall-installedtestmetaDATA uninstall-jsscripttestsDATA \ uninstall-jstestsDATA uninstall-libLTLIBRARIES \ uninstall-nobase_gjs_public_includeHEADERS \ @@ -3740,6 +3939,13 @@ @BUILDOPT_INSTALL_TESTS_TRUE@ < $(srcdir)/installed-tests/minijasmine.test.in > $@.tmp && \ @BUILDOPT_INSTALL_TESTS_TRUE@ mv $@.tmp $@ +@BUILDOPT_INSTALL_TESTS_TRUE@%.test: %.debugger installed-tests/debugger.test.in Makefile +@BUILDOPT_INSTALL_TESTS_TRUE@ $(AM_V_GEN)$(MKDIR_P) $(@D) && \ +@BUILDOPT_INSTALL_TESTS_TRUE@ $(SED) -e s,@pkglibexecdir\@,$(pkglibexecdir),g \ +@BUILDOPT_INSTALL_TESTS_TRUE@ -e s,@name\@,$(notdir $<), \ +@BUILDOPT_INSTALL_TESTS_TRUE@ < $(srcdir)/installed-tests/debugger.test.in > $@.tmp && \ +@BUILDOPT_INSTALL_TESTS_TRUE@ mv $@.tmp $@ + @BUILDOPT_INSTALL_TESTS_TRUE@%.test: % installed-tests/script.test.in Makefile @BUILDOPT_INSTALL_TESTS_TRUE@ $(AM_V_GEN)$(MKDIR_P) $(@D) && \ @BUILDOPT_INSTALL_TESTS_TRUE@ $(SED) -e s,@pkglibexecdir\@,$(pkglibexecdir), \ diff -Nru gjs-1.53.3/Makefile-insttest.am gjs-1.54.3/Makefile-insttest.am --- gjs-1.53.3/Makefile-insttest.am 2018-02-18 02:13:41.000000000 +0000 +++ gjs-1.54.3/Makefile-insttest.am 2018-11-08 04:20:06.000000000 +0000 @@ -1,4 +1,5 @@ EXTRA_DIST += \ + installed-tests/debugger.test.in \ installed-tests/minijasmine.test.in \ installed-tests/script.test.in \ installed-tests/js/jsunit.gresources.xml \ @@ -8,24 +9,30 @@ installedtestmetadir = $(datadir)/installed-tests/gjs jstestsdir = $(gjsinsttestdir)/js jsscripttestsdir = $(gjsinsttestdir)/scripts +debuggertestsdir = $(gjsinsttestdir)/debugger gjsinsttest_PROGRAMS = gjsinsttest_DATA = +gjsinsttest_SCRIPTS = installedtestmeta_DATA = jstests_DATA = jsscripttests_DATA = +debuggertests_DATA = pkglib_LTLIBRARIES = if BUILDOPT_INSTALL_TESTS gjsinsttest_PROGRAMS += minijasmine +gjsinsttest_SCRIPTS += installed-tests/debugger-test.sh gjsinsttest_DATA += $(TEST_INTROSPECTION_TYPELIBS) -installedtestmeta_DATA += \ - $(jasmine_tests:.js=.test) \ - $(simple_tests:%=%.test) \ +installedtestmeta_DATA += \ + $(jasmine_tests:.js=.test) \ + $(simple_tests:%=%.test) \ + $(debugger_tests:.debugger=.test) \ $(NULL) jstests_DATA += $(jasmine_tests) jsscripttests_DATA += $(simple_tests) +debuggertests_DATA += $(debugger_tests) pkglib_LTLIBRARIES += libregress.la libwarnlib.la libgimarshallingtests.la %.test: %.js installed-tests/minijasmine.test.in Makefile @@ -35,6 +42,13 @@ < $(srcdir)/installed-tests/minijasmine.test.in > $@.tmp && \ mv $@.tmp $@ +%.test: %.debugger installed-tests/debugger.test.in Makefile + $(AM_V_GEN)$(MKDIR_P) $(@D) && \ + $(SED) -e s,@pkglibexecdir\@,$(pkglibexecdir),g \ + -e s,@name\@,$(notdir $<), \ + < $(srcdir)/installed-tests/debugger.test.in > $@.tmp && \ + mv $@.tmp $@ + %.test: % installed-tests/script.test.in Makefile $(AM_V_GEN)$(MKDIR_P) $(@D) && \ $(SED) -e s,@pkglibexecdir\@,$(pkglibexecdir), \ @@ -42,4 +56,6 @@ < $(srcdir)/installed-tests/script.test.in > $@.tmp && \ mv $@.tmp $@ +CLEANFILES += $(installedtestmeta_DATA) + endif BUILDOPT_INSTALL_TESTS diff -Nru gjs-1.53.3/Makefile-test.am gjs-1.54.3/Makefile-test.am --- gjs-1.53.3/Makefile-test.am 2018-05-31 05:34:45.000000000 +0000 +++ gjs-1.54.3/Makefile-test.am 2018-11-12 16:43:50.000000000 +0000 @@ -64,13 +64,13 @@ gjs_tests_gtester_CPPFLAGS = \ $(AM_CPPFLAGS) \ -DGJS_COMPILATION \ - $(GJSTESTS_CFLAGS) \ + $(GJS_CFLAGS) \ $(gjs_directory_defines) \ -I$(top_srcdir)/test gjs_tests_gtester_LDADD = \ libgjs.la \ - $(GJSTESTS_LIBS) + $(GJS_LIBS) gjs_tests_gtester_SOURCES = \ test/gjs-tests.cpp \ @@ -204,6 +204,7 @@ installed-tests/js/testGio.js \ installed-tests/js/testImporter.js \ installed-tests/js/testLang.js \ + installed-tests/js/testLegacyByteArray.js \ installed-tests/js/testLegacyClass.js \ installed-tests/js/testLegacyGObject.js \ installed-tests/js/testLocale.js \ @@ -228,6 +229,10 @@ installed-tests/js/testGObjectDestructionAccess.js \ installed-tests/js/testLegacyGtk.js \ $(NULL) + +GTK_TESTS_ENVIRONMENT = export ENABLE_GTK=yes; +else +GTK_TESTS_ENVIRONMENT = endif if ENABLE_CAIRO @@ -279,8 +284,9 @@ export G_FILENAME_ENCODING=latin1; \ export LSAN_OPTIONS="suppressions=$(abs_top_srcdir)/installed-tests/extra/lsan.supp"; \ export NO_AT_BRIDGE=1; \ - export LC_ALL=C.UTF-8; \ + export LC_ALL=C.UTF-8; \ $(COVERAGE_TESTS_ENVIRONMENT) \ + $(GTK_TESTS_ENVIRONMENT) \ $(XVFB_START) \ $(DBUS_SESSION_COMMAND) \ $(NULL) @@ -291,13 +297,39 @@ $(NULL) EXTRA_DIST += $(simple_tests) +debugger_tests = \ + installed-tests/debugger/backtrace.debugger \ + installed-tests/debugger/breakpoint.debugger \ + installed-tests/debugger/continue.debugger \ + installed-tests/debugger/delete.debugger \ + installed-tests/debugger/detach.debugger \ + installed-tests/debugger/down-up.debugger \ + installed-tests/debugger/finish.debugger \ + installed-tests/debugger/frame.debugger \ + installed-tests/debugger/keys.debugger \ + installed-tests/debugger/next.debugger \ + installed-tests/debugger/print.debugger \ + installed-tests/debugger/quit.debugger \ + installed-tests/debugger/return.debugger \ + installed-tests/debugger/set.debugger \ + installed-tests/debugger/step.debugger \ + installed-tests/debugger/throw.debugger \ + installed-tests/debugger/until.debugger \ + $(NULL) +EXTRA_DIST += \ + $(debugger_tests) \ + $(debugger_tests:%=%.js) \ + $(debugger_tests:%=%.output) \ + $(NULL) + TESTS = \ gjs-tests.gtester \ $(simple_tests) \ $(jasmine_tests) \ + $(debugger_tests) \ $(NULL) -TEST_EXTENSIONS = .gtester .sh .js +TEST_EXTENSIONS = .gtester .sh .js .debugger LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/tap-driver.sh @@ -309,6 +341,10 @@ JS_LOG_DRIVER = $(LOG_DRIVER) JS_LOG_COMPILER = $$LOG_COMPILER $$LOG_FLAGS $(top_builddir)/minijasmine +DEBUGGER_LOG_DRIVER = $(LOG_DRIVER) +DEBUGGER_LOG_COMPILER = $(top_srcdir)/installed-tests/debugger-test.sh +EXTRA_DIST += installed-tests/debugger-test.sh + CODE_COVERAGE_IGNORE_PATTERN = */{include,mfbt,gjs/test}/* CODE_COVERAGE_GENHTML_OPTIONS = \ $(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT) \ diff -Nru gjs-1.53.3/modules/_bootstrap/debugger.js gjs-1.54.3/modules/_bootstrap/debugger.js --- gjs-1.53.3/modules/_bootstrap/debugger.js 1970-01-01 00:00:00.000000000 +0000 +++ gjs-1.54.3/modules/_bootstrap/debugger.js 2018-11-08 04:20:06.000000000 +0000 @@ -0,0 +1,738 @@ +/* global Debugger, debuggee, quit, readline, uneval */ +/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +/* + * This is a simple command-line debugger for GJS programs. It is based on + * jorendb, which is a toy debugger for shell-js programs included in the + * SpiderMonkey source. + * + * To run it: gjs -d path/to/file.js + * Execution will stop at debugger statements, and you'll get a prompt before + * the first frame is executed. + */ + +// Debugger state. +var focusedFrame = null; +var topFrame = null; +var debuggeeValues = {}; +var nextDebuggeeValueIndex = 1; +var lastExc = null; +var options = {pretty: true}; +var breakpoints = [undefined]; // Breakpoint numbers start at 1 + +// Cleanup functions to run when we next re-enter the repl. +var replCleanups = []; + +// Convert a debuggee value v to a string. +function dvToString(v) { + if (typeof v === 'undefined') + return 'undefined'; // uneval(undefined) === '(void 0)', confusing + if (v === null) + return 'null'; // typeof null === 'object', so avoid that case + return (typeof v !== 'object' || v === null) ? uneval(v) : `[object ${v.class}]`; +} + +function summarizeObject(dv) { + const obj = {}; + for (var name of dv.getOwnPropertyNames()) { + var v = dv.getOwnPropertyDescriptor(name).value; + if (v instanceof Debugger.Object) { + v = '(...)'; + } + obj[name] = v; + } + return obj; +} + +function debuggeeValueToString(dv, style = {pretty: options.pretty}) { + const dvrepr = dvToString(dv); + if (!style.pretty || dv === null || typeof dv !== 'object') + return [dvrepr, undefined]; + + if (['Error', 'GIRespositoryNamespace', 'GObject_Object'].includes(dv.class)) { + const errval = debuggeeGlobalWrapper.executeInGlobalWithBindings( + 'v.toString()', {v: dv}); + return [dvrepr, errval['return']]; + } + + if (style.brief) + return [dvrepr, JSON.stringify(summarizeObject(dv), null, 4)]; + + const str = debuggeeGlobalWrapper.executeInGlobalWithBindings( + 'JSON.stringify(v, null, 4)', {v: dv}); + if ('throw' in str) { + if (style.noerror) + return [dvrepr, undefined]; + + const substyle = {}; + Object.assign(substyle, style); + substyle.noerror = true; + return [dvrepr, debuggeeValueToString(str.throw, substyle)]; + } + + return [dvrepr, str['return']]; +} + +function showDebuggeeValue(dv, style = {pretty: options.pretty}) { + const i = nextDebuggeeValueIndex++; + debuggeeValues[`$${i}`] = dv; + const [brief, full] = debuggeeValueToString(dv, style); + print(`$${i} = ${brief}`); + if (full !== undefined) + print(full); +} + +Object.defineProperty(Debugger.Frame.prototype, 'num', { + configurable: true, + enumerable: false, + get: function() { + let i = 0; + for (var f = topFrame; f && f !== this; f = f.older) + i++; + return f === null ? undefined : i; + } +}); + +Debugger.Frame.prototype.describeFrame = function() { + if (this.type == 'call') + return `${this.callee.name || ''}(${ + this.arguments.map(dvToString).join(', ')})`; + else if (this.type == 'global') + return 'toplevel'; + else + return this.type + ' code'; +}; + +Debugger.Frame.prototype.describePosition = function() { + if (this.script) + return this.script.describeOffset(this.offset); + return null; +}; + +Debugger.Frame.prototype.describeFull = function() { + const fr = this.describeFrame(); + const pos = this.describePosition(); + if (pos) + return `${fr} at ${pos}`; + return fr; +}; + +Object.defineProperty(Debugger.Frame.prototype, 'line', { + configurable: true, + enumerable: false, + get: function() { + if (this.script) + return this.script.getOffsetLocation(this.offset).lineNumber; + else + return null; + } +}); + +Debugger.Script.prototype.describeOffset = function describeOffset(offset) { + const {lineNumber, columnNumber} = this.getOffsetLocation(offset); + const url = this.url || ''; + return `${url}:${lineNumber}:${columnNumber}`; +}; + +function showFrame(f, n) { + if (f === undefined || f === null) { + f = focusedFrame; + if (f === null) { + print('No stack.'); + return; + } + } + if (n === undefined) { + n = f.num; + if (n === undefined) + throw new Error('Internal error: frame not on stack'); + } + + print(`#${n.toString().padEnd(4)} ${f.describeFull()}`); +} + +function saveExcursion(fn) { + const tf = topFrame, ff = focusedFrame; + try { + return fn(); + } finally { + topFrame = tf; + focusedFrame = ff; + } +} + +// Accept debugger commands starting with '#' so that scripting the debugger +// can be annotated +function commentCommand(comment) { + void comment; +} + +// Evaluate an expression in the Debugger global - used for debugging the +// debugger +function evalCommand(expr) { + eval(expr); +} + +function quitCommand() { + dbg.enabled = false; + quit(0); +} + +function backtraceCommand() { + if (topFrame === null) + print('No stack.'); + for (var i = 0, f = topFrame; f; i++, f = f.older) + showFrame(f, i); +} + +function setCommand(rest) { + var space = rest.indexOf(' '); + if (space == -1) { + print('Invalid set