From a44c356190f44f1532208f1a0612a878bbafce27 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 16 Feb 2018 10:51:37 -0500
Subject: [PATCH] object: only print stacktraces when debugging enabled
We have a bunch of corruption right now spamming the
log.
This commit gets rid of the spam unless G_MESSAGES_DEBUG
is set.
---
gi/object.cpp | 86 +++++++++++++++++++++++++++++++--------------------
1 file changed, 52 insertions(+), 34 deletions(-)
diff --git a/gi/object.cpp b/gi/object.cpp
index fe381ec3..bd09a75c 100644
--- a/gi/object.cpp
+++ b/gi/object.cpp
@@ -127,60 +127,83 @@ struct ObjectInstance {
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<JS::PersistentRootedObject> object_init_list;
using ParamRef = std::unique_ptr<GParamSpec, decltype(&g_param_spec_unref)>;
using ParamRefArray = std::vector<ParamRef>;
static std::unordered_map<GType, ParamRefArray> class_init_properties;
static bool context_weak_pointer_callback = false;
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 (GObject *gobj);
static void ensure_uses_toggle_ref(JSContext *cx, ObjectInstance *priv);
+static void
+gjs_log_stacktrace(const char *format,
+ ...)
+{
+ const char *domain;
+ va_list args;
+
+ domain = g_getenv("G_MESSAGES_DEBUG");
+
+ if (!domain)
+ return;
+
+ if (!g_str_equal(domain, "all") &&
+ !strstr(domain, G_LOG_DOMAIN))
+ return;
+
+ va_start(args, format);
+ g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format, args);
+ va_end(args);
+
+ gjs_dumpstack();
+}
+
typedef enum {
SOME_ERROR_OCCURRED = false,
NO_SUCH_G_PROPERTY,
VALUE_WAS_SET
} ValueFromPropertyResult;
static GQuark
gjs_is_custom_type_quark (void)
{
static GQuark val = 0;
if (!val)
val = g_quark_from_static_string ("gjs::custom-type");
return val;
}
static GQuark
gjs_is_custom_property_quark (void)
{
static GQuark val = 0;
if (!val)
val = g_quark_from_static_string ("gjs::custom-property");
return val;
}
static GQuark
gjs_object_priv_quark (void)
{
static GQuark val = 0;
@@ -433,66 +456,65 @@ out:
/* 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;
GjsAutoJSChar name;
if (!gjs_get_string_id(context, id, &name))
return true; /* not resolved, but no error */
priv = priv_from_js(context, obj);
gjs_debug_jsprop(GJS_DEBUG_GOBJECT,
"Get prop '%s' hook obj %p priv %p",
name.get(), obj.get(), 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();
+ gjs_log_stacktrace("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);
return true;
}
if (!get_prop_from_g_param(context, obj, priv, name, value_p))
return false;
if (!value_p.isUndefined())
return true;
/* Fall back to fields */
return get_prop_from_field(context, obj, priv, name, value_p);
}
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:
@@ -549,66 +571,65 @@ check_set_field_from_prop(JSContext *cx,
value_p.setUndefined();
out:
g_base_info_unref((GIBaseInfo *) field);
return retval;
}
/* 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;
GjsAutoJSChar name;
bool ret = true;
bool g_param_was_set = false;
priv = priv_from_js(context, obj);
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();
+ gjs_log_stacktrace("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);
return result.succeed();
}
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 */
}
gjs_debug_jsprop(GJS_DEBUG_GOBJECT,
"Set prop '%s' hook obj %p priv %p",
name.get(), obj.get(), priv);
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);
}
static bool
is_vfunc_unchanged(GIVFuncInfo *info,
GType gtype)
@@ -1780,67 +1801,66 @@ do_associate_closure(ObjectInstance *priv,
* invalidated */
priv->closures.insert(closure);
g_closure_add_invalidate_notifier(closure, priv, closure_invalidated);
}
static bool
real_connect_func(JSContext *context,
unsigned argc,
JS::Value *vp,
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();
+ gjs_log_stacktrace("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);
return true;
}
ensure_uses_toggle_ref(context, priv);
if (argc != 2 || !argv[0].isString() || !JS::IsCallable(&argv[1].toObject())) {
gjs_throw(context, "connect() takes two args, the signal name and the callback");
return false;
}
JS::RootedString signal_str(context, argv[0].toString());
GjsAutoJSChar signal_name = JS_EncodeStringToUTF8(context, signal_str);
if (!signal_name)
return false;
if (!g_signal_parse_name(signal_name,
G_OBJECT_TYPE(priv->gobj),
&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)));
return false;
}
closure = gjs_closure_new_for_signal(context, &argv[1].toObject(), "signal callback", signal_id);
if (closure == NULL)
return false;
do_associate_closure(priv, closure);
@@ -1875,67 +1895,66 @@ connect_func(JSContext *context,
static bool
emit_func(JSContext *context,
unsigned argc,
JS::Value *vp)
{
GJS_GET_PRIV(context, argc, vp, argv, obj, ObjectInstance, priv);
guint signal_id;
GQuark signal_detail;
GSignalQuery signal_query;
GValue *instance_and_args;
GValue rvalue = G_VALUE_INIT;
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;
}
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();
+ gjs_log_stacktrace("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);
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)
return false;
if (!g_signal_parse_name(signal_name,
G_OBJECT_TYPE(priv->gobj),
&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)));
return false;
}
g_signal_query(signal_id, &signal_query);
if ((argc - 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)),
@@ -2250,68 +2269,67 @@ gjs_object_from_g_object(JSContext *context,
return nullptr;
JS::RootedObject obj(context,
JS_NewObjectWithGivenProto(context, JS_GetClass(proto), proto));
if (!obj)
return nullptr;
priv = init_object_private(context, obj);
g_object_ref_sink(gobj);
associate_js_gobject(context, obj, gobj);
g_assert(priv->keep_alive == obj.get());
}
return priv->keep_alive;
}
GObject*
gjs_g_object_from_object(JSContext *context,
JS::HandleObject obj)
{
ObjectInstance *priv;
if (!obj)
return NULL;
priv = priv_from_js(context, obj);
if (priv->g_object_finalized) {
- g_critical("Object %s.%s (%p), has been already deallocated - "
- "impossible to access it. This might be caused by the "
- "object having been destroyed from C code using something "
- "such as destroy(), dispose(), or remove() vfuncs",
- priv->info ? g_base_info_get_namespace(priv->info) : "",
- priv->info ? g_base_info_get_name(priv->info) : g_type_name(priv->gtype),
- priv->gobj);
- gjs_dumpstack();
+ gjs_log_stacktrace ("Object %s.%s (%p), has been already deallocated - "
+ "impossible to access it. This might be caused by the "
+ "object having been destroyed from C code using something "
+ "such as destroy(), dispose(), or remove() vfuncs",
+ priv->info ? g_base_info_get_namespace(priv->info) : "",
+ priv->info ? g_base_info_get_name(priv->info) : g_type_name(priv->gtype),
+ priv->gobj);
return nullptr;
}
return priv->gobj;
}
bool
gjs_typecheck_is_object(JSContext *context,
JS::HandleObject object,
bool throw_error)
{
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))
return false;
priv = priv_from_js(context, object);
if (priv == NULL) {
if (throw_error) {
--
2.17.1