Commit 091b7dbc authored by Cosimo Cecchi's avatar Cosimo Cecchi

Merge branch 'january-maintenance' into 'master'

January maintenance

See merge request GNOME/gjs!267
parents df8542a0 643bd164
......@@ -286,7 +286,7 @@ AM_TESTS_ENVIRONMENT = \
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=$(TESTS_LOCALE); \
$(COVERAGE_TESTS_ENVIRONMENT) \
$(GTK_TESTS_ENVIRONMENT) \
$(XVFB_START) \
......
......@@ -337,6 +337,26 @@ AS_IF([test "x$with_dbus_tests" != "xno"], [
])
AM_CONDITIONAL([DBUS_TESTS], [test "x$with_dbus_tests" != "xno"])
AC_MSG_CHECKING([for a suitable UTF-8 locale to run the tests in])
LOCALES=$(locale -a)
AS_CASE([$LOCALES],
dnl Prefer C.UTF-8 although it is only available with newer libc
[*C.UTF-8*], [TESTS_LOCALE=C.UTF-8],
dnl Most systems will probably have this
[*en_US.UTF-8*], [TESTS_LOCALE=en_US.UTF-8],
[*en_US.utf8*], [TESTS_LOCALE=en_US.utf8],
dnl If not, fall back to any English UTF-8 locale or any UTF-8 locale at all
[*en_*.UTF-8*], [TESTS_LOCALE=$(echo $LOCALES | grep -m1 en_.\*\\.UTF-8)],
[*en_*.utf8*], [TESTS_LOCALE=$(echo $LOCALES | grep -m1 en_.\*\\.utf8)],
[*.UTF-8*], [TESTS_LOCALE=$(echo $LOCALES | grep -m1 \\.UTF-8)],
[*.utf8*], [TESTS_LOCALE=$(echo $LOCALES | grep -m1 \\.utf8)],
[TESTS_LOCALE=C])
AC_MSG_RESULT([$TESTS_LOCALE])
AC_SUBST([TESTS_LOCALE])
AC_SUBST([gjsjsdir], [\${datadir}/gjs-1.0])
dnl automake 1.11/1.12 defines this but does not substitute it
......@@ -362,6 +382,12 @@ PKG_CHECK_EXISTS([gobject-introspection-1.0 >= 1.57.2], [], [
gobject-introspection to run the tests. You can still build GJS, but some
tests will fail.])])
AS_IF([test "$TESTS_LOCALE" = "C"], [
AC_MSG_WARN([Your libc does not have the C.UTF-8 locale and no other
suitable UTF-8 fallback locale could be found. You can still build GJS, but
some tests will fail.])
])
TEST_MSG=
AM_COND_IF([XVFB_TESTS], [TEST_MSG="xvfb "])
AM_COND_IF([DBUS_TESTS], [TEST_MSG="${TEST_MSG}dbus"])
......
......@@ -447,17 +447,10 @@ bool BoxedInstance::get_nested_interface_object(
return false;
}
JS::RootedObject proto(
context, gjs_lookup_generic_prototype(context, interface_info));
if (!proto)
return false;
offset = g_field_info_get_offset (field_info);
JS::RootedObject obj(context, JS_NewObjectWithGivenProto(
context, JS_GetClass(proto), proto));
JS::RootedObject obj(context, gjs_new_object_with_generic_prototype(
context, interface_info));
if (!obj)
return false;
......@@ -711,8 +704,6 @@ bool BoxedPrototype::define_boxed_class_fields(JSContext* cx,
// Overrides GIWrapperPrototype::trace_impl().
void BoxedPrototype::trace_impl(JSTracer* trc) {
JS::TraceEdge<jsid>(trc, &m_zero_args_constructor_name,
"Boxed::zero_args_constructor_name");
JS::TraceEdge<jsid>(trc, &m_default_constructor_name,
"Boxed::default_constructor_name");
if (m_field_map)
......@@ -868,7 +859,6 @@ struct_is_simple(GIStructInfo *info)
BoxedPrototype::BoxedPrototype(GIStructInfo* info, GType gtype)
: GIWrapperPrototype(info, gtype),
m_zero_args_constructor(-1),
m_zero_args_constructor_name(JSID_VOID),
m_default_constructor(-1),
m_default_constructor_name(JSID_VOID),
m_can_allocate_directly(struct_is_simple(info)) {
......@@ -880,6 +870,7 @@ bool BoxedPrototype::init(JSContext* context) {
int i, n_methods;
int first_constructor = -1;
jsid first_constructor_name = JSID_VOID;
jsid zero_args_constructor_name = JSID_VOID;
if (m_gtype != G_TYPE_NONE) {
/* If the structure is registered as a boxed, we can create a new instance by
......@@ -907,9 +898,9 @@ bool BoxedPrototype::init(JSContext* context) {
if (m_zero_args_constructor < 0 &&
g_callable_info_get_n_args(func_info) == 0) {
m_zero_args_constructor = i;
m_zero_args_constructor_name =
zero_args_constructor_name =
gjs_intern_string_to_id(context, func_info.name());
if (m_zero_args_constructor_name == JSID_VOID)
if (zero_args_constructor_name == JSID_VOID)
return false;
}
......@@ -924,7 +915,7 @@ bool BoxedPrototype::init(JSContext* context) {
if (m_default_constructor < 0) {
m_default_constructor = m_zero_args_constructor;
m_default_constructor_name = m_zero_args_constructor_name;
m_default_constructor_name = zero_args_constructor_name;
}
if (m_default_constructor < 0) {
m_default_constructor = first_constructor;
......@@ -963,12 +954,7 @@ JSObject* gjs_boxed_from_c_struct(JSContext* cx, GIStructInfo* info,
"Wrapping struct %s %p with JSObject",
g_base_info_get_name((GIBaseInfo *)info), gboxed);
JS::RootedObject proto(cx, gjs_lookup_generic_prototype(cx, info));
if (!proto)
return nullptr;
JS::RootedObject obj(
cx, JS_NewObjectWithGivenProto(cx, JS_GetClass(proto), proto));
JS::RootedObject obj(cx, gjs_new_object_with_generic_prototype(cx, info));
if (!obj)
return nullptr;
......
......@@ -97,7 +97,6 @@ class BoxedPrototype : public GIWrapperPrototype<BoxedBase, BoxedPrototype,
js::DefaultHasher<JSString*>, js::SystemAllocPolicy>;
int m_zero_args_constructor; // -1 if none
JS::Heap<jsid> m_zero_args_constructor_name;
int m_default_constructor; // -1 if none
JS::Heap<jsid> m_default_constructor_name;
FieldMap* m_field_map;
......
......@@ -105,7 +105,7 @@ static void global_context_finalized(JS::HandleFunction func, void* data) {
gjs_debug_closure(
"Context global object destroy notifier on closure %p which calls "
"object %p",
c, c->func.get());
c, c->func.debug_addr());
if (c->func) {
g_assert(c->func == func.get());
......@@ -135,7 +135,7 @@ closure_invalidated(gpointer data,
GJS_DEC_COUNTER(closure);
gjs_debug_closure("Invalidating closure %p which calls function %p",
closure, c->func.get());
closure, c->func.debug_addr());
if (!c->func) {
gjs_debug_closure(" (closure %p already dead, nothing to do)",
......@@ -165,7 +165,7 @@ closure_set_invalid(gpointer data,
Closure *self = &((GjsClosure*) closure)->priv;
gjs_debug_closure("Invalidating signal closure %p which calls function %p",
closure, self->func.get());
closure, self->func.debug_addr());
self->func.prevent_collection();
self->func.reset();
......@@ -217,7 +217,7 @@ gjs_closure_invoke(GClosure *closure,
gjs_debug_closure(
"Closure invocation failed (exception should have been thrown) "
"closure %p function %p",
closure, c->func.get());
closure, c->func.debug_addr());
/* If an exception has been thrown, log it, unless the caller
* explicitly wants to handle it manually (for example to turn it
* into a GError), in which case it replaces the return value
......@@ -320,7 +320,7 @@ GClosure* gjs_closure_new(JSContext* context, JSFunction* callable,
g_closure_add_finalize_notifier(&gc->base, NULL, closure_finalize);
gjs_debug_closure("Create closure %p which calls function %p '%s'", gc,
c->func.get(), description);
c->func.debug_addr(), description);
JS_EndRequest(context);
......
......@@ -33,6 +33,7 @@
#include "gi/object.h"
#include "gi/repo.h"
#include "gi/wrapperutils.h"
#include "gjs/context-private.h"
#include "gjs/jsapi-class.h"
#include "gjs/jsapi-wrapper.h"
#include "gjs/mem-private.h"
......@@ -41,63 +42,6 @@
#include <util/log.h>
#include <girepository.h>
GJS_USE
static GQuark
gjs_fundamental_table_quark (void)
{
static GQuark val = 0;
if (!val)
val = g_quark_from_static_string("gjs::fundamental-table");
return val;
}
GJS_USE
static GHashTable *
_ensure_mapping_table(GjsContext *context)
{
GHashTable *table =
(GHashTable *) g_object_get_qdata ((GObject *) context,
gjs_fundamental_table_quark());
if (G_UNLIKELY(table == NULL)) {
table = g_hash_table_new(NULL, NULL);
g_object_set_qdata_full((GObject *) context,
gjs_fundamental_table_quark(),
table,
(GDestroyNotify) g_hash_table_unref);
}
return table;
}
static void
_fundamental_add_object(void *native_object, JSObject *js_object)
{
GHashTable *table = _ensure_mapping_table(gjs_context_get_current());
g_hash_table_insert(table, native_object, js_object);
}
static void
_fundamental_remove_object(void *native_object)
{
GHashTable *table = _ensure_mapping_table(gjs_context_get_current());
g_hash_table_remove(table, native_object);
}
GJS_USE
static JSObject *
_fundamental_lookup_object(void *native_object)
{
GHashTable *table = _ensure_mapping_table(gjs_context_get_current());
return (JSObject *) g_hash_table_lookup(table, native_object);
}
/**/
FundamentalInstance::FundamentalInstance(JSContext* cx, JS::HandleObject obj)
: GIWrapperInstance(cx, obj) {
GJS_INC_COUNTER(fundamental_instance);
......@@ -110,16 +54,20 @@ FundamentalInstance::FundamentalInstance(JSContext* cx, JS::HandleObject obj)
* future if you have a pointer to @gfundamental. (Assuming @object has not been
* garbage collected in the meantime.)
*/
void FundamentalInstance::associate_js_instance(JSObject* object,
bool FundamentalInstance::associate_js_instance(JSContext* cx, JSObject* object,
void* gfundamental) {
m_ptr = gfundamental;
g_assert(_fundamental_lookup_object(gfundamental) == NULL);
_fundamental_add_object(gfundamental, object);
GjsContextPrivate* gjs = GjsContextPrivate::from_cx(cx);
if (!gjs->fundamental_table().putNew(gfundamental, object)) {
JS_ReportOutOfMemory(cx);
return false;
}
debug_lifecycle(object, "associated JSObject with fundamental");
ref();
return true;
}
/**/
......@@ -275,11 +223,10 @@ bool FundamentalInstance::constructor_impl(JSContext* cx,
GArgument ret_value;
GITypeInfo return_info;
if (!invoke_constructor(cx, object, argv, &ret_value))
if (!invoke_constructor(cx, object, argv, &ret_value) ||
!associate_js_instance(cx, object, ret_value.v_pointer))
return false;
associate_js_instance(object, ret_value.v_pointer);
GICallableInfo* constructor_info = get_prototype()->constructor_info();
g_callable_info_load_return_type(constructor_info, &return_info);
......@@ -290,7 +237,6 @@ bool FundamentalInstance::constructor_impl(JSContext* cx,
FundamentalInstance::~FundamentalInstance(void) {
if (m_ptr) {
_fundamental_remove_object(m_ptr);
unref();
m_ptr = nullptr;
}
......@@ -480,9 +426,10 @@ gjs_object_from_g_fundamental(JSContext *context,
if (gfundamental == NULL)
return NULL;
JS::RootedObject object(context, _fundamental_lookup_object(gfundamental));
if (object)
return object;
GjsContextPrivate* gjs = GjsContextPrivate::from_cx(context);
auto p = gjs->fundamental_table().lookup(gfundamental);
if (p)
return p->value();
gjs_debug_marshal(GJS_DEBUG_GFUNDAMENTAL,
"Wrapping fundamental %s.%s %p with JSObject",
......@@ -496,14 +443,16 @@ gjs_object_from_g_fundamental(JSContext *context,
if (!proto)
return NULL;
object = JS_NewObjectWithGivenProto(context, JS_GetClass(proto), proto);
JS::RootedObject object(context, JS_NewObjectWithGivenProto(
context, JS_GetClass(proto), proto));
if (!object)
return nullptr;
auto* priv = FundamentalInstance::new_for_js_object(context, object);
priv->associate_js_instance(object, gfundamental);
if (!priv->associate_js_instance(context, object, gfundamental))
return nullptr;
return object;
}
......
......@@ -145,7 +145,9 @@ class FundamentalInstance
const JS::CallArgs& args);
public:
void associate_js_instance(JSObject* object, void* gfundamental);
GJS_JSAPI_RETURN_CONVENTION
bool associate_js_instance(JSContext* cx, JSObject* object,
void* gfundamental);
};
G_BEGIN_DECLS
......
......@@ -382,10 +382,8 @@ gjs_error_from_gerror(JSContext *context,
"Wrapping struct %s with JSObject",
g_base_info_get_name((GIBaseInfo *)info));
JS::RootedObject proto(context, gjs_lookup_generic_prototype(context, info));
JS::RootedObject obj(context,
JS_NewObjectWithGivenProto(context, JS_GetClass(proto), proto));
gjs_new_object_with_generic_prototype(context, info));
if (!obj)
return nullptr;
......
......@@ -935,7 +935,7 @@ bool ObjectPrototype::new_enumerate_impl(JSContext* cx, JS::HandleObject obj,
g_free(interfaces);
if (info()) {
if (!is_custom_js_class()) {
// Methods
int n_methods = g_object_info_get_n_methods(info());
for (int i = 0; i < n_methods; i++) {
......
......@@ -780,3 +780,12 @@ gjs_lookup_generic_prototype(JSContext *context,
return &value.toObject();
}
JSObject* gjs_new_object_with_generic_prototype(JSContext* cx,
GIBaseInfo* info) {
JS::RootedObject proto(cx, gjs_lookup_generic_prototype(cx, info));
if (!proto)
return nullptr;
return JS_NewObjectWithGivenProto(cx, JS_GetClass(proto), proto);
}
......@@ -57,6 +57,9 @@ JSObject * gjs_lookup_generic_constructor (JSContext *context,
GJS_JSAPI_RETURN_CONVENTION
JSObject * gjs_lookup_generic_prototype (JSContext *context,
GIBaseInfo *info);
GJS_JSAPI_RETURN_CONVENTION
JSObject* gjs_new_object_with_generic_prototype(JSContext* cx,
GIBaseInfo* info);
GJS_JSAPI_RETURN_CONVENTION
bool gjs_define_info(JSContext *context,
......
......@@ -233,13 +233,8 @@ gjs_union_from_c_union(JSContext *context,
"Wrapping union %s %p with JSObject",
g_base_info_get_name((GIBaseInfo *)info), gboxed);
JS::RootedObject proto(context,
gjs_lookup_generic_prototype(context, (GIUnionInfo*) info));
if (!proto)
return nullptr;
JS::RootedObject obj(
context, JS_NewObjectWithGivenProto(context, JS_GetClass(proto), proto));
JS::RootedObject obj(context,
gjs_new_object_with_generic_prototype(context, info));
if (!obj)
return nullptr;
......
......@@ -143,7 +143,9 @@ bool gjs_define_static_methods(JSContext* cx, JS::HandleObject constructor,
}
}
if (!InfoMethodsPolicy<TAG>::type_struct)
// Casting to void* avoids warning that the function pointer will never be
// null in template instantiations where it is not null
if (!reinterpret_cast<void*>(InfoMethodsPolicy<TAG>::type_struct))
return true;
// Also define class/interface methods if there is a gtype struct
......
......@@ -101,6 +101,7 @@ struct GjsSymbolAtom : GjsAtom {
class GjsAtoms {
public:
explicit GjsAtoms(JSContext* cx) {}
~GjsAtoms(void) {} // prevents giant destructor from being inlined
GJS_JSAPI_RETURN_CONVENTION bool init_atoms(JSContext* cx);
void trace(JSTracer* trc);
......
......@@ -33,7 +33,19 @@
#include "jsapi-util.h"
#include "jsapi-wrapper.h"
#include "js/GCHashTable.h"
#include "js/SweepingAPI.h"
using JobQueue = JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>;
using FundamentalTable =
JS::GCHashMap<void*, JS::Heap<JSObject*>, js::DefaultHasher<void*>,
js::SystemAllocPolicy>;
// FundamentalTable's key type is void*, the GC sweep method should ignore it
namespace JS {
template <>
struct GCPolicy<void*> : public IgnoreGCPolicy<void*> {};
} // namespace JS
class GjsContextPrivate {
GjsContext* m_public_context;
......@@ -69,6 +81,9 @@ class GjsContextPrivate {
};
EnvironmentPreparer m_environment_preparer;
// Weak pointer mapping from fundamental native pointer to JSObject
JS::WeakCache<FundamentalTable>* m_fundamental_table;
uint8_t m_exit_code;
/* flags */
......@@ -122,6 +137,9 @@ class GjsContextPrivate {
GJS_USE bool is_owner_thread(void) const {
return m_owner_thread == g_thread_self();
}
GJS_USE JS::WeakCache<FundamentalTable>& fundamental_table(void) {
return *m_fundamental_table;
}
GJS_USE
static const GjsAtoms& atoms(JSContext* cx) { return from_cx(cx)->m_atoms; }
......
......@@ -342,6 +342,8 @@ void GjsContextPrivate::dispose(void) {
JS_BeginRequest(m_cx);
gjs_debug(GJS_DEBUG_CONTEXT, "Releasing cached JS wrappers");
m_fundamental_table->clear();
/* Do a full GC here before tearing down, since once we do
* that we may not have the JS_GetPrivate() to access the
* context
......@@ -372,6 +374,7 @@ void GjsContextPrivate::dispose(void) {
gjs_debug(GJS_DEBUG_CONTEXT, "Freeing allocated resources");
delete m_job_queue;
delete m_fundamental_table;
/* Tear down JS */
JS_DestroyContext(m_cx);
......@@ -451,6 +454,11 @@ GjsContextPrivate::GjsContextPrivate(JSContext* cx, GjsContext* public_context)
if (!m_job_queue)
g_error("Failed to initialize promise job queue");
JSRuntime* rt = JS_GetRuntime(m_cx);
m_fundamental_table = new JS::WeakCache<FundamentalTable>(rt);
if (!m_fundamental_table->init())
g_error("Failed to initialize fundamental objects table");
JS_BeginRequest(m_cx);
JS::RootedObject global(m_cx, gjs_create_global_object(m_cx));
......
......@@ -215,6 +215,13 @@ public:
}
operator const T(void) const { return get(); }
/* Use debug_addr() only for debug logging, because it is unbarriered. */
template <typename U = T>
GJS_USE const T
debug_addr(std::enable_if_t<std::is_pointer<U>::value>* = nullptr) const {
return m_rooted ? m_thing.root->get() : m_thing.heap.unbarrieredGet();
}
bool
operator==(const T& other) const
{
......
......@@ -601,7 +601,8 @@ function helpCommand() {
group.push(cmd);
} else {
// Don't print commands for debugging the debugger
if (['comment', 'eval'].includes(group[0]))
if ([commentCommand, evalCommand].includes(cmd) ||
['comment', 'eval'].includes(group[0]))
continue;
if (group.length)
printcmd(group);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment