[gjs] Use std::unique_ptr instead of g_autofree/g_autoptr()



commit bcd1f395801f6928c419a6bd99a35eb004be9e54
Author: Chun-wei Fan <fanchunwei src gnome org>
Date:   Wed Apr 19 23:35:48 2017 +0800

    Use std::unique_ptr instead of g_autofree/g_autoptr()
    
    This completes the transition of the code to std::unique_ptr so that we
    can be assured that we use JS_free() on items that are acquired via
    pass-by-reference via SpiderMonkey APIs, and thus we can remove more of
    the g_free() calls on those as SpiderMonkey may switch to a different
    allocation/deallocation implementation internally (which is currently
    free()).
    
    https://bugzilla.gnome.org/show_bug.cgi?id=777597

 gi/arg.cpp                |   49 ++++++++------------
 gi/boxed.cpp              |   23 +++-------
 gi/fundamental.cpp        |   12 +----
 gi/interface.cpp          |    9 +---
 gi/ns.cpp                 |    7 +---
 gi/object.cpp             |  109 ++++++++++++++++----------------------------
 gi/param.cpp              |    4 +-
 gi/repo.cpp               |    6 +--
 gi/union.cpp              |    9 +---
 gi/value.cpp              |    4 +-
 gjs/byteArray.cpp         |   29 +++---------
 gjs/context.cpp           |    9 +---
 gjs/coverage.cpp          |   29 ++++++------
 gjs/importer.cpp          |   36 +++++----------
 gjs/jsapi-util-args.h     |    9 +++-
 gjs/jsapi-util-string.cpp |   23 +++------
 gjs/jsapi-util.cpp        |   65 ++++++++++++--------------
 gjs/jsapi-util.h          |   61 ++++++++++++++++++++++++-
 gjs/runtime.cpp           |   19 ++------
 gjs/stack.cpp             |    5 +-
 test/gjs-tests.cpp        |    6 +--
 21 files changed, 228 insertions(+), 295 deletions(-)
---
diff --git a/gi/arg.cpp b/gi/arg.cpp
index 81ebf4d..0457673 100644
--- a/gi/arg.cpp
+++ b/gi/arg.cpp
@@ -416,7 +416,7 @@ value_to_ghashtable_key(JSContext      *cx,
 
 #define HANDLE_STRING(type, lctype)                                   \
     case GI_TYPE_TAG_##type: {                                        \
-        char *cstr;                                                   \
+        GjsAutoJSChar cstr(cx);                                       \
         JS::RootedValue str_val(cx, value);                           \
         if (!str_val.isString()) {                                    \
             JS::RootedString str(cx, JS::ToString(cx, str_val));      \
@@ -424,7 +424,7 @@ value_to_ghashtable_key(JSContext      *cx,
         }                                                             \
         if (!gjs_string_to_##lctype(cx, str_val, &cstr))              \
             return false;                                             \
-        *pointer_out = cstr;                                          \
+        *pointer_out = cstr.copy();                                   \
         break;                                                        \
     }
 
@@ -610,6 +610,8 @@ gjs_array_to_strv(JSContext   *context,
     result = g_new0(char *, length+1);
 
     for (i = 0; i < length; ++i) {
+        GjsAutoJSChar tmp_result(context);
+
         elem = JS::UndefinedValue();
         if (!JS_GetElement(context, array, i, &elem)) {
             g_free(result);
@@ -625,10 +627,11 @@ gjs_array_to_strv(JSContext   *context,
             g_strfreev(result);
             return false;
         }
-        if (!gjs_string_to_utf8(context, elem, (char **)&(result[i]))) {
+        if (!gjs_string_to_utf8(context, elem, &tmp_result)) {
             g_strfreev(result);
             return false;
         }
+        result[i] = tmp_result.copy();
     }
 
     *arr_p = result;
@@ -644,16 +647,17 @@ gjs_string_to_intarray(JSContext   *context,
                        gsize       *length)
 {
     GITypeTag element_type;
-    char *result;
     char16_t *result16;
 
     element_type = g_type_info_get_tag(param_info);
 
     if (element_type == GI_TYPE_TAG_INT8 || element_type == GI_TYPE_TAG_UINT8) {
+        GjsAutoJSChar result(context);
+
         if (!gjs_string_to_utf8(context, string_val, &result))
             return false;
-        *arr_p = result;
         *length = strlen(result);
+        *arr_p = result.copy();
         return true;
     }
 
@@ -1463,10 +1467,9 @@ gjs_value_to_g_argument(JSContext      *context,
         if (value.isNull()) {
             arg->v_pointer = NULL;
         } else if (value.isString()) {
-            char *filename_str;
+            GjsAutoJSChar filename_str(context);
             if (gjs_string_to_filename(context, value, &filename_str))
-                // doing this as a separate step to avoid type-punning
-                arg->v_pointer = filename_str;
+                arg->v_pointer = filename_str.copy();
             else
                 wrong = true;
         } else {
@@ -1479,10 +1482,9 @@ gjs_value_to_g_argument(JSContext      *context,
         if (value.isNull()) {
             arg->v_pointer = NULL;
         } else if (value.isString()) {
-            char *utf8_str;
+            GjsAutoJSChar utf8_str(context);
             if (gjs_string_to_utf8(context, value, &utf8_str))
-                // doing this as a separate step to avoid type-punning
-                arg->v_pointer = utf8_str;
+                arg->v_pointer = utf8_str.copy();
             else
                 wrong = true;
         } else {
@@ -2526,9 +2528,7 @@ gjs_object_from_g_hash (JSContext             *context,
                         GHashTable            *hash)
 {
     GHashTableIter iter;
-    char     *keyutf8 = NULL;
     GArgument keyarg, valarg;
-    bool result;
 
     // a NULL hash table becomes a null JS value
     if (hash==NULL) {
@@ -2545,41 +2545,32 @@ gjs_object_from_g_hash (JSContext             *context,
     JS::RootedValue keyjs(context), valjs(context);
     JS::RootedString keystr(context);
 
-    result = false;
-
     g_hash_table_iter_init(&iter, hash);
     while (g_hash_table_iter_next
            (&iter, &keyarg.v_pointer, &valarg.v_pointer)) {
         if (!gjs_value_from_g_argument(context, &keyjs,
                                        key_param_info, &keyarg,
                                        true))
-            goto out;
+            return false;
 
         keystr = JS::ToString(context, keyjs);
         if (!keystr)
-            goto out;
+            return false;
 
+        GjsAutoJSChar keyutf8(context);
         if (!gjs_string_to_utf8(context, JS::StringValue(keystr), &keyutf8))
-            goto out;
+            return false;
 
         if (!gjs_value_from_g_argument(context, &valjs,
                                        val_param_info, &valarg,
                                        true))
-            goto out;
+            return false;
 
         if (!JS_DefineProperty(context, obj, keyutf8, valjs, JSPROP_ENUMERATE))
-            goto out;
-
-        g_free(keyutf8);
-        keyutf8 = NULL;
+            return false;
     }
 
-    result = true;
-
- out:
-    if (keyutf8) g_free(keyutf8);
-
-    return result;
+    return true;
 }
 
 static const int64_t MAX_SAFE_INT64 =
diff --git a/gi/boxed.cpp b/gi/boxed.cpp
index e6500d3..3fe037d 100644
--- a/gi/boxed.cpp
+++ b/gi/boxed.cpp
@@ -122,7 +122,7 @@ boxed_resolve(JSContext       *context,
               bool            *resolved)
 {
     Boxed *priv;
-    char *name = NULL;
+    GjsAutoJSChar name(context);
 
     if (!gjs_get_string_id(context, id, &name)) {
         *resolved = false;
@@ -133,10 +133,8 @@ boxed_resolve(JSContext       *context,
     gjs_debug_jsprop(GJS_DEBUG_GBOXED, "Resolve prop '%s' hook obj %p priv %p",
                      name, obj.get(), priv);
 
-    if (priv == NULL) {
-        g_free(name);
+    if (priv == nullptr)
         return false; /* wrong class */
-    }
 
     if (priv->gboxed == NULL) {
         /* We are the prototype, so look for methods and other class properties */
@@ -164,7 +162,6 @@ boxed_resolve(JSContext       *context,
                 if (gjs_define_function(context, obj, priv->gtype,
                                         (GICallableInfo *)method_info) == NULL) {
                     g_base_info_unref( (GIBaseInfo*) method_info);
-                    g_free(name);
                     return false;
                 }
 
@@ -185,7 +182,6 @@ boxed_resolve(JSContext       *context,
          */
         *resolved = false;
     }
-    g_free(name);
     return true;
 }
 
@@ -283,7 +279,7 @@ boxed_init_from_props(JSContext   *context,
     JS::RootedId prop_id(context);
     for (ix = 0, length = ids.length(); ix < length; ix++) {
         GIFieldInfo *field_info;
-        char *name = NULL;
+        GjsAutoJSChar name(context);
 
         if (!gjs_get_string_id(context, ids[ix], &name))
             return false;
@@ -291,8 +287,7 @@ boxed_init_from_props(JSContext   *context,
         field_info = (GIFieldInfo *) g_hash_table_lookup(priv->field_map, name);
         if (field_info == NULL) {
             gjs_throw(context, "No field %s on boxed type %s",
-                      name, g_base_info_get_name((GIBaseInfo *)priv->info));
-            g_free(name);
+                      name.get(), g_base_info_get_name((GIBaseInfo *)priv->info));
             return false;
         }
 
@@ -300,17 +295,11 @@ boxed_init_from_props(JSContext   *context,
          * doesn't know that */
         prop_id = ids[ix];
         if (!gjs_object_require_property(context, props, "property list",
-                                         prop_id, &value)) {
-            g_free(name);
+                                         prop_id, &value))
             return false;
-        }
 
-        if (!boxed_set_field_from_value(context, priv, field_info, value)) {
-            g_free(name);
+        if (!boxed_set_field_from_value(context, priv, field_info, value))
             return false;
-        }
-
-        g_free(name);
     }
 
     return true;
diff --git a/gi/fundamental.cpp b/gi/fundamental.cpp
index 2242bc8..4ff6702 100644
--- a/gi/fundamental.cpp
+++ b/gi/fundamental.cpp
@@ -241,7 +241,7 @@ fundamental_instance_resolve_interface(JSContext       *context,
                                        JS::HandleObject obj,
                                        bool            *resolved,
                                        Fundamental     *proto_priv,
-                                       char            *name)
+                                       const char      *name)
 {
     GIFunctionInfo *method_info;
     bool ret;
@@ -301,7 +301,7 @@ fundamental_instance_resolve(JSContext       *context,
                              bool            *resolved)
 {
     FundamentalInstance *priv;
-    char *name = NULL;
+    GjsAutoJSChar name(context);
 
     if (!gjs_get_string_id(context, id, &name)) {
         *resolved = false;
@@ -313,10 +313,8 @@ fundamental_instance_resolve(JSContext       *context,
                      "Resolve prop '%s' hook obj %p priv %p",
                      name, obj.get(), priv);
 
-    if (priv == NULL) {
-        g_free(name);
+    if (priv == nullptr)
         return false; /* wrong class */
-    }
 
     if (!fundamental_is_prototype(priv)) {
         /* We are an instance, not a prototype, so look for
@@ -327,7 +325,6 @@ fundamental_instance_resolve(JSContext       *context,
          * hooks, not this resolve hook.
          */
         *resolved = false;
-        g_free(name);
         return true;
     }
 
@@ -356,7 +353,6 @@ fundamental_instance_resolve(JSContext       *context,
                           g_base_info_get_name((GIBaseInfo *) proto_priv->info));
                 g_base_info_unref((GIBaseInfo *) method_info);
                 *resolved = false;
-                g_free(name);
                 return true;
             }
 
@@ -369,7 +365,6 @@ fundamental_instance_resolve(JSContext       *context,
             if (gjs_define_function(context, obj, proto_priv->gtype,
                                     method_info) == NULL) {
                 g_base_info_unref((GIBaseInfo *) method_info);
-                g_free(name);
                 return false;
             }
 
@@ -384,7 +379,6 @@ fundamental_instance_resolve(JSContext       *context,
     bool status =
         fundamental_instance_resolve_interface(context, obj, resolved,
                                                proto_priv, name);
-    g_free(name);
     return status;
 }
 
diff --git a/gi/interface.cpp b/gi/interface.cpp
index 712702b..c9c8573 100644
--- a/gi/interface.cpp
+++ b/gi/interface.cpp
@@ -112,7 +112,7 @@ interface_resolve(JSContext       *context,
                   bool            *resolved)
 {
     Interface *priv;
-    char *name = NULL;
+    GjsAutoJSChar name(context);
     GIFunctionInfo *method_info;
 
     if (!gjs_get_string_id(context, id, &name)) {
@@ -122,17 +122,14 @@ interface_resolve(JSContext       *context,
 
     priv = priv_from_js(context, obj);
 
-    if (priv == NULL) {
-        g_free(name);
+    if (priv == nullptr)
         return false;
-    }
 
     /* If we have no GIRepository information then this interface was defined
      * from within GJS. In that case, it has no properties that need to be
      * resolved from within C code, as interfaces cannot inherit. */
     if (priv->info == NULL) {
         *resolved = false;
-        g_free(name);
         return true;
     }
 
@@ -144,7 +141,6 @@ interface_resolve(JSContext       *context,
                                     priv->gtype,
                                     (GICallableInfo*)method_info) == NULL) {
                 g_base_info_unref((GIBaseInfo*)method_info);
-                g_free(name);
                 return false;
             }
 
@@ -158,7 +154,6 @@ interface_resolve(JSContext       *context,
         *resolved = false;
     }
 
-    g_free(name);
     return true;
 }
 
diff --git a/gi/ns.cpp b/gi/ns.cpp
index 9406cd6..830e4e9 100644
--- a/gi/ns.cpp
+++ b/gi/ns.cpp
@@ -55,7 +55,7 @@ ns_resolve(JSContext       *context,
            bool            *resolved)
 {
     Ns *priv;
-    char *name = NULL;
+    GjsAutoJSChar name(context);
     GIRepository *repo;
     GIBaseInfo *info;
     bool defined;
@@ -69,7 +69,6 @@ ns_resolve(JSContext       *context,
     if (strcmp(name, "valueOf") == 0 ||
         strcmp(name, "toString") == 0) {
         *resolved = false;
-        g_free(name);
         return true;
     }
 
@@ -80,7 +79,6 @@ ns_resolve(JSContext       *context,
 
     if (priv == NULL) {
         *resolved = false;  /* we are the prototype, or have the wrong class */
-        g_free(name);
         return true;
     }
 
@@ -89,7 +87,6 @@ ns_resolve(JSContext       *context,
     info = g_irepository_find_by_name(repo, priv->gi_namespace, name);
     if (info == NULL) {
         *resolved = false; /* No property defined, but no error either */
-        g_free(name);
         return true;
     }
 
@@ -107,14 +104,12 @@ ns_resolve(JSContext       *context,
                   g_base_info_get_name(info));
 
         g_base_info_unref(info);
-        g_free(name);
         return false;
     }
 
     /* we defined the property in this object? */
     g_base_info_unref(info);
     *resolved = defined;
-    g_free(name);
     return true;
 }
 
diff --git a/gi/object.cpp b/gi/object.cpp
index d98c126..fab3225 100644
--- a/gi/object.cpp
+++ b/gi/object.cpp
@@ -390,8 +390,7 @@ object_instance_get_prop(JSContext              *context,
                          JS::MutableHandleValue  value_p)
 {
     ObjectInstance *priv;
-    char *name;
-    bool ret = true;
+    GjsAutoJSChar name(context);
 
     if (!gjs_get_string_id(context, id, &name))
         return true; /* not resolved, but no error */
@@ -401,28 +400,23 @@ object_instance_get_prop(JSContext              *context,
                      "Get prop '%s' hook obj %p priv %p",
                      name, obj.get(), priv);
 
-    if (priv == NULL) {
+    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 */
-        goto out;
-    }
+        return true;
+
     if (priv->gobj == NULL) /* prototype, not an instance. */
-        goto out;
+        return true;
 
-    ret = get_prop_from_g_param(context, obj, priv, name, value_p);
-    if (!ret)
-        goto out;
+    if (!get_prop_from_g_param(context, obj, priv, name, value_p))
+        return false;
 
     if (!value_p.isUndefined())
-        goto out;
+        return true;
 
     /* Fall back to fields */
-    ret = get_prop_from_field(context, obj, priv, name, value_p);
-
- out:
-    g_free(name);
-    return ret;
+    return get_prop_from_field(context, obj, priv, name, value_p);
 }
 
 static bool
@@ -509,7 +503,7 @@ object_instance_set_prop(JSContext              *context,
                          JS::MutableHandleValue  value_p)
 {
     ObjectInstance *priv;
-    char *name;
+    GjsAutoJSChar name(context);
     bool ret = true;
     bool g_param_was_set = false;
 
@@ -521,28 +515,23 @@ object_instance_set_prop(JSContext              *context,
                      "Set prop '%s' hook obj %p priv %p",
                      name, obj.get(), priv);
 
-    if (priv == NULL) {
+    if (priv == nullptr)
         /* see the comment in object_instance_get_prop() on this */
-        goto out;
-    }
+        return true;
+
     if (priv->gobj == NULL) /* prototype, not an instance. */
-        goto out;
+        return true;
 
     ret = set_g_param_from_prop(context, priv, name, g_param_was_set, value_p);
     if (g_param_was_set || !ret)
-        goto out;
-
-    ret = check_set_field_from_prop(context, priv, name, strict, value_p);
+        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.
      */
-
- out:
-    g_free(name);
-    return ret;
+    return check_set_field_from_prop(context, priv, name, strict, value_p);
 }
 
 static bool
@@ -610,7 +599,7 @@ object_instance_resolve_no_info(JSContext       *context,
                                 JS::HandleObject obj,
                                 bool            *resolved,
                                 ObjectInstance  *priv,
-                                char            *name)
+                                const char      *name)
 {
     GIFunctionInfo *method_info;
     guint n_interfaces;
@@ -674,7 +663,7 @@ object_instance_resolve(JSContext       *context,
 {
     GIFunctionInfo *method_info;
     ObjectInstance *priv;
-    char *name = NULL;
+    GjsAutoJSChar name(context);
 
     if (!gjs_get_string_id(context, id, &name)) {
         *resolved = false;
@@ -703,13 +692,11 @@ object_instance_resolve(JSContext       *context,
          * check there.
          */
         *resolved = false;
-        g_free(name);
         return true;
     }
 
     if (priv->gobj != NULL) {
         *resolved = false;
-        g_free(name);
         return true;
     }
 
@@ -718,7 +705,6 @@ object_instance_resolve(JSContext       *context,
      * 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);
-        g_free(name);
         return status;
     }
 
@@ -737,13 +723,12 @@ object_instance_resolve(JSContext       *context,
          * rest.
          */
 
-        const char *name_without_vfunc_ = &name[6];  /* lifetime tied to name */
+        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);
         if (vfunc != NULL) {
-            g_free(name);
 
             /* In the event that the vfunc is unchanged, let regular
              * prototypal inheritance take over. */
@@ -785,11 +770,9 @@ object_instance_resolve(JSContext       *context,
      */
     if (method_info == NULL) {
         bool retval = object_instance_resolve_no_info(context, obj, resolved, priv, name);
-        g_free(name);
         return retval;
     }
 
-    g_free(name);
 
 #if GJS_VERBOSE_ENABLE_GI_USAGE
     _gjs_log_info_usage((GIBaseInfo*) method_info);
@@ -859,7 +842,7 @@ object_instance_props_to_g_parameters(JSContext                  *context,
     }
 
     for (ix = 0, length = ids.length(); ix < length; ix++) {
-        char *name = NULL;
+        GjsAutoJSChar name(context);
         GParameter gparam = { NULL, { 0, }};
 
         /* ids[ix] is reachable because props is rooted, but require_property
@@ -880,18 +863,15 @@ object_instance_props_to_g_parameters(JSContext                  *context,
                                            true /* constructing */)) {
         case NO_SUCH_G_PROPERTY:
             gjs_throw(context, "No property %s on this GObject %s",
-                         name, g_type_name(gtype));
+                      name.get(), g_type_name(gtype));
             /* fallthrough */
         case SOME_ERROR_OCCURRED:
-            g_free(name);
             goto free_array_and_fail;
         case VALUE_WAS_SET:
         default:
             break;
         }
 
-        g_free(name);
-
         gparams.push_back(gparam);
     }
 
@@ -1596,10 +1576,9 @@ real_connect_func(JSContext *context,
     GClosure *closure;
     gulong id;
     guint signal_id;
-    char *signal_name;
+    GjsAutoJSChar signal_name(context);
     GQuark signal_detail;
     ConnectData *connect_data;
-    bool ret = false;
 
     gjs_debug_gsignal("connect obj %p priv %p argc %d", obj.get(), priv, argc);
     if (priv == NULL) {
@@ -1629,14 +1608,14 @@ real_connect_func(JSContext *context,
                              &signal_detail,
                              true)) {
         gjs_throw(context, "No signal '%s' on object '%s'",
-                     signal_name,
-                     g_type_name(G_OBJECT_TYPE(priv->gobj)));
-        goto out;
+                  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)
-        goto out;
+        return false;
 
     connect_data = g_slice_new(ConnectData);
     priv->signals = g_list_prepend(priv->signals, connect_data);
@@ -1654,10 +1633,7 @@ real_connect_func(JSContext *context,
 
     argv.rval().setDouble(id);
 
-    ret = true;
- out:
-    g_free(signal_name);
-    return ret;
+    return true;
 }
 
 static bool
@@ -1685,12 +1661,11 @@ emit_func(JSContext *context,
     guint signal_id;
     GQuark signal_detail;
     GSignalQuery signal_query;
-    char *signal_name;
+    GjsAutoJSChar signal_name(context);
     GValue *instance_and_args;
     GValue rvalue = G_VALUE_INIT;
     unsigned int i;
     bool failed;
-    bool ret = false;
 
     gjs_debug_gsignal("emit obj %p priv %p argc %d", obj.get(), priv, argc);
 
@@ -1721,20 +1696,20 @@ emit_func(JSContext *context,
                              &signal_detail,
                              false)) {
         gjs_throw(context, "No signal '%s' on object '%s'",
-                     signal_name,
-                     g_type_name(G_OBJECT_TYPE(priv->gobj)));
-        goto out;
+                  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,
-                     g_type_name(G_OBJECT_TYPE(priv->gobj)),
-                     signal_query.n_params,
-                     argc - 1);
-        goto out;
+                  signal_name.get(),
+                  g_type_name(G_OBJECT_TYPE(priv->gobj)),
+                  signal_query.n_params,
+                  argc - 1);
+        return false;
     }
 
     if (signal_query.return_type != G_TYPE_NONE) {
@@ -1780,10 +1755,7 @@ emit_func(JSContext *context,
         g_value_unset(&instance_and_args[i]);
     }
 
-    ret = !failed;
- out:
-    g_free(signal_name);
-    return ret;
+    return !failed;
 }
 
 static bool
@@ -2902,7 +2874,7 @@ gjs_signal_new(JSContext *cx,
 {
     JS::CallArgs argv = JS::CallArgsFromVp (argc, vp);
     GType gtype;
-    char *signal_name_tmp = NULL;
+    GjsAutoJSChar signal_name(cx);
     GSignalAccumulator accumulator;
     gint signal_id;
     guint i, n_parameters;
@@ -2913,9 +2885,8 @@ gjs_signal_new(JSContext *cx,
 
     JSAutoRequest ar(cx);
 
-    if (!gjs_string_to_utf8(cx, argv[1], &signal_name_tmp))
+    if (!gjs_string_to_utf8(cx, argv[1], &signal_name))
         return false;
-    std::unique_ptr<char, decltype(&g_free)> signal_name(signal_name_tmp, g_free);
 
     JS::RootedObject obj(cx, &argv[0].toObject());
     if (!gjs_typecheck_gtype(cx, obj, true))
@@ -2961,7 +2932,7 @@ gjs_signal_new(JSContext *cx,
 
     gtype = gjs_gtype_get_actual_gtype(cx, obj);
 
-    signal_id = g_signal_newv(signal_name.get(),
+    signal_id = g_signal_newv(signal_name,
                               gtype,
                               (GSignalFlags) argv[2].toInt32(), /* signal_flags */
                               NULL, /* class closure */
diff --git a/gi/param.cpp b/gi/param.cpp
index c38eebb..6fe1c06 100644
--- a/gi/param.cpp
+++ b/gi/param.cpp
@@ -58,7 +58,7 @@ param_resolve(JSContext       *context,
     GIObjectInfo *info = NULL;
     GIFunctionInfo *method_info;
     Param *priv;
-    char *name = NULL;
+    GjsAutoJSChar name(context);
     bool ret = false;
 
     if (!gjs_get_string_id(context, id, &name))
@@ -69,7 +69,6 @@ param_resolve(JSContext       *context,
     if (priv != NULL) {
         /* instance, not prototype */
         *resolved = false;
-        g_free(name);
         return true;
     }
 
@@ -102,7 +101,6 @@ param_resolve(JSContext       *context,
 
     ret = true;
  out:
-    g_free(name); 
     if (info != NULL)
         g_base_info_unref( (GIBaseInfo*)info);
 
diff --git a/gi/repo.cpp b/gi/repo.cpp
index 10fd465..6102c58 100644
--- a/gi/repo.cpp
+++ b/gi/repo.cpp
@@ -167,7 +167,7 @@ repo_resolve(JSContext       *context,
              bool            *resolved)
 {
     Repo *priv;
-    char *name = NULL;
+    GjsAutoJSChar name(context);
 
     if (!gjs_get_string_id(context, id, &name)) {
         *resolved = false;
@@ -178,7 +178,6 @@ repo_resolve(JSContext       *context,
     if (strcmp(name, "valueOf") == 0 ||
         strcmp(name, "toString") == 0) {
         *resolved = false;
-        g_free(name);
         return true;
     }
 
@@ -189,17 +188,14 @@ repo_resolve(JSContext       *context,
     if (priv == NULL) {
         /* we are the prototype, or have the wrong class */
         *resolved = false;
-        g_free(name);
         return true;
     }
 
     if (!resolve_namespace_object(context, obj, id, name)) {
-        g_free(name);
         return false;
     }
 
     *resolved = true;
-    g_free(name);
     return true;
 }
 
diff --git a/gi/union.cpp b/gi/union.cpp
index c4ef103..c18e367 100644
--- a/gi/union.cpp
+++ b/gi/union.cpp
@@ -61,7 +61,7 @@ union_resolve(JSContext       *context,
               bool            *resolved)
 {
     Union *priv;
-    char *name = NULL;
+    GjsAutoJSChar name(context);
 
     if (!gjs_get_string_id(context, id, &name)) {
         *resolved = false;
@@ -72,10 +72,8 @@ union_resolve(JSContext       *context,
     gjs_debug_jsprop(GJS_DEBUG_GBOXED, "Resolve prop '%s' hook obj %p priv %p",
                      name, obj.get(), priv);
 
-    if (priv == NULL) {
-        g_free(name);
+    if (priv == nullptr)
         return false; /* wrong class */
-    }
 
     if (priv->gboxed != NULL) {
         /* We are an instance, not a prototype, so look for
@@ -86,7 +84,6 @@ union_resolve(JSContext       *context,
          * hooks, not this resolve hook.
          */
         *resolved = false;
-        g_free(name);
         return true;
     }
 
@@ -116,7 +113,6 @@ union_resolve(JSContext       *context,
                                     g_registered_type_info_get_g_type(priv->info),
                                     method_info) == NULL) {
                 g_base_info_unref( (GIBaseInfo*) method_info);
-                g_free(name);
                 return false;
             }
 
@@ -130,7 +126,6 @@ union_resolve(JSContext       *context,
         *resolved = false;
     }
 
-    g_free(name);
     return true;
 }
 
diff --git a/gi/value.cpp b/gi/value.cpp
index 2ad9592..9c95ead 100644
--- a/gi/value.cpp
+++ b/gi/value.cpp
@@ -380,12 +380,12 @@ gjs_value_to_g_value_internal(JSContext      *context,
         if (value.isNull()) {
             g_value_set_string(gvalue, NULL);
         } else if (value.isString()) {
-            gchar *utf8_string;
+            GjsAutoJSChar utf8_string(context);
 
             if (!gjs_string_to_utf8(context, value, &utf8_string))
                 return false;
 
-            g_value_take_string(gvalue, utf8_string);
+            g_value_take_string(gvalue, utf8_string.copy());
         } else {
             gjs_throw(context,
                       "Wrong type %s; string expected",
diff --git a/gjs/byteArray.cpp b/gjs/byteArray.cpp
index 3f55150..89211e0 100644
--- a/gjs/byteArray.cpp
+++ b/gjs/byteArray.cpp
@@ -405,7 +405,7 @@ to_string_func(JSContext *context,
                JS::Value *vp)
 {
     GJS_GET_PRIV(context, argc, vp, argv, to, ByteArrayInstance, priv);
-    char *encoding;
+    GjsAutoJSChar encoding(context);
     bool encoding_is_utf8;
     gchar *data;
 
@@ -422,13 +422,7 @@ to_string_func(JSContext *context,
          * doesn't matter much though. encoding_is_utf8 is
          * just an optimization anyway.
          */
-        if (strcmp(encoding, "UTF-8") == 0) {
-            encoding_is_utf8 = true;
-            g_free(encoding);
-            encoding = NULL;
-        } else {
-            encoding_is_utf8 = false;
-        }
+        encoding_is_utf8 = (strcmp(encoding, "UTF-8") == 0);
     } else {
         encoding_is_utf8 = true;
     }
@@ -461,7 +455,6 @@ to_string_func(JSContext *context,
                            NULL, /* bytes read */
                            &bytes_written,
                            &error);
-        g_free(encoding);
         if (u16_str == NULL) {
             /* frees the GError */
             gjs_throw_g_error(context, error);
@@ -534,7 +527,7 @@ from_string_func(JSContext *context,
 {
     JS::CallArgs argv = JS::CallArgsFromVp (argc, vp);
     ByteArrayInstance *priv;
-    char *encoding;
+    GjsAutoJSChar encoding(context);
     bool encoding_is_utf8;
     JS::RootedObject obj(context, byte_array_new(context));
 
@@ -562,13 +555,7 @@ from_string_func(JSContext *context,
          * doesn't matter much though. encoding_is_utf8 is
          * just an optimization anyway.
          */
-        if (strcmp(encoding, "UTF-8") == 0) {
-            encoding_is_utf8 = true;
-            g_free(encoding);
-            encoding = NULL;
-        } else {
-            encoding_is_utf8 = false;
-        }
+        encoding_is_utf8 = (strcmp(encoding, "UTF-8") == 0);
     } else {
         encoding_is_utf8 = true;
     }
@@ -577,15 +564,16 @@ from_string_func(JSContext *context,
         /* optimization? avoids iconv overhead and runs
          * libmozjs hardwired utf16-to-utf8.
          */
-        char *utf8 = NULL;
+        GjsAutoJSChar utf8(context);
         if (!gjs_string_to_utf8(context,
                                 argv[0],
                                 &utf8))
             return false;
 
         g_byte_array_set_size(priv->array, 0);
-        g_byte_array_append(priv->array, (guint8*) utf8, strlen(utf8));
-        g_free(utf8);
+        g_byte_array_append(priv->array,
+                            reinterpret_cast<const guint8*>(utf8.get()),
+                            strlen(utf8));
     } else {
         JSString *str = argv[0].toString();  /* Rooted by argv */
         GError *error = NULL;
@@ -623,7 +611,6 @@ from_string_func(JSContext *context,
             }
         }
 
-        g_free(encoding);
         if (encoded == NULL) {
             /* frees the GError */
             gjs_throw_g_error(context, error);
diff --git a/gjs/context.cpp b/gjs/context.cpp
index 2b506c2..ca8d43d 100644
--- a/gjs/context.cpp
+++ b/gjs/context.cpp
@@ -119,7 +119,6 @@ gjs_log(JSContext *context,
         JS::Value *vp)
 {
     JS::CallArgs argv = JS::CallArgsFromVp (argc, vp);
-    char *s;
 
     if (argc != 1) {
         gjs_throw(context, "Must pass a single argument to log()");
@@ -140,13 +139,12 @@ gjs_log(JSContext *context,
         return true;
     }
 
+    GjsAutoJSChar s(context);
     if (!gjs_string_to_utf8(context, JS::StringValue(jstr), &s)) {
         JS_EndRequest(context);
         return false;
     }
-
-    g_message("JS LOG: %s", s);
-    g_free(s);
+    g_message("JS LOG: %s", s.get());
 
     JS_EndRequest(context);
     argv.rval().setUndefined();
@@ -190,7 +188,6 @@ gjs_print_parse_args(JSContext *context,
                      char     **buffer)
 {
     GString *str;
-    gchar *s;
     guint n;
 
     JS_BeginRequest(context);
@@ -204,6 +201,7 @@ gjs_print_parse_args(JSContext *context,
         exc_state.restore();
 
         if (jstr != NULL) {
+            GjsAutoJSChar s(context);
             if (!gjs_string_to_utf8(context, JS::StringValue(jstr), &s)) {
                 JS_EndRequest(context);
                 g_string_free(str, true);
@@ -211,7 +209,6 @@ gjs_print_parse_args(JSContext *context,
             }
 
             g_string_append(str, s);
-            g_free(s);
             if (n < (argv.length()-1))
                 g_string_append_c(str, ' ');
         } else {
diff --git a/gjs/coverage.cpp b/gjs/coverage.cpp
index 579b141..52a39df 100644
--- a/gjs/coverage.cpp
+++ b/gjs/coverage.cpp
@@ -493,11 +493,11 @@ get_executed_lines_for(JSContext        *context,
 
 static void
 init_covered_function(GjsCoverageFunction *function,
-                      char                *key,
+                      const char          *key,
                       unsigned int        line_number,
                       unsigned int        hit_count)
 {
-    function->key = key;
+    function->key = g_strdup(key);
     function->line_number = line_number;
     function->hit_count = hit_count;
 }
@@ -543,7 +543,7 @@ convert_and_insert_function_decl(GArray         *array,
                                      &function_name_property_value))
         return false;
 
-    char *utf8_string;
+    GjsAutoJSChar utf8_string(context);
 
     if (function_name_property_value.isString()) {
         if (!gjs_string_to_utf8(context,
@@ -552,9 +552,7 @@ convert_and_insert_function_decl(GArray         *array,
             gjs_throw(context, "Failed to convert function_name to string");
             return false;
         }
-    } else if (function_name_property_value.isNull()) {
-        utf8_string = NULL;
-    } else {
+    } else if (!function_name_property_value.isNull()) {
         gjs_throw(context, "Unexpected type for function_name");
         return false;
     }
@@ -869,14 +867,14 @@ get_covered_files(GjsCoverage *coverage)
     files = g_new0(char *, n_files + 1);
     JS::RootedValue element(context);
     for (uint32_t i = 0; i < n_files; i++) {
-        char *file;
+        GjsAutoJSChar file(context);
         if (!JS_GetElement(context, files_obj, i, &element))
             goto error;
 
         if (!gjs_string_to_utf8(context, element, &file))
             goto error;
 
-        files[i] = file;
+        files[i] = file.copy();
     }
 
     files[n_files] = NULL;
@@ -1012,7 +1010,7 @@ gjs_serialize_statistics(GjsCoverage *coverage)
         return NULL;
 
     /* Free'd by g_bytes_new_take */
-    char *statistics_as_json_string = NULL;
+    GjsAutoJSChar statistics_as_json_string(js_context);
 
     if (!gjs_string_to_utf8(js_context,
                             string_value_return.get(),
@@ -1021,8 +1019,12 @@ gjs_serialize_statistics(GjsCoverage *coverage)
         return NULL;
     }
 
-    return g_bytes_new_take((guint8 *) statistics_as_json_string,
-                            strlen(statistics_as_json_string));
+    int json_string_len = strlen(statistics_as_json_string);
+    auto json_bytes =
+        reinterpret_cast<uint8_t*>(statistics_as_json_string.copy());
+
+    return g_bytes_new_take(json_bytes,
+                            json_string_len);
 }
 
 static JSString *
@@ -1333,7 +1335,7 @@ coverage_log(JSContext *context,
              JS::Value *vp)
 {
     JS::CallArgs argv = JS::CallArgsFromVp (argc, vp);
-    char *s;
+    GjsAutoJSChar s(context);
     JSExceptionState *exc_state;
 
     if (argc != 1) {
@@ -1366,8 +1368,7 @@ coverage_log(JSContext *context,
         return false;
     }
 
-    g_message("JS COVERAGE MESSAGE: %s", s);
-    g_free(s);
+    g_message("JS COVERAGE MESSAGE: %s", s.get());
 
     argv.rval().setUndefined();
     return true;
diff --git a/gjs/importer.cpp b/gjs/importer.cpp
index 5ee128e..ff592c1 100644
--- a/gjs/importer.cpp
+++ b/gjs/importer.cpp
@@ -75,7 +75,7 @@ importer_to_string(JSContext *cx,
                                  &module_path))
         return false;
 
-    char *path = NULL;
+    GjsAutoJSChar path(cx);
     GjsAutoChar output;
 
     if (module_path.isNull()) {
@@ -83,11 +83,10 @@ importer_to_string(JSContext *cx,
     } else {
         if (!gjs_string_to_utf8(cx, module_path, &path))
             return false;
-        output = g_strdup_printf("[%s %s]", klass->name, path);
+        output = g_strdup_printf("[%s %s]", klass->name, path.get());
     }
 
     args.rval().setString(JS_NewStringCopyZ(cx, output));
-    g_free(path);
     return true;
 }
 
@@ -139,11 +138,10 @@ define_meta_properties(JSContext       *context,
         if (parent_module_path.isNull()) {
             module_path_buf = g_strdup(module_name);
         } else {
-            char *parent_path = NULL;
+            GjsAutoJSChar parent_path(context);
             if (!gjs_string_to_utf8(context, parent_module_path, &parent_path))
                 return false;
-            module_path_buf = g_strdup_printf("%s.%s", parent_path, module_name);
-            g_free(parent_path);
+            module_path_buf = g_strdup_printf("%s.%s", parent_path.get(), module_name);
         }
         module_path.setString(JS_NewStringCopyZ(context, module_path_buf));
     }
@@ -286,13 +284,12 @@ module_to_string(JSContext *cx,
 
     g_assert(!module_path.isNull());
 
-    char *path = NULL;
+    GjsAutoJSChar path(cx);
     if (!gjs_string_to_utf8(cx, module_path, &path))
         return false;
-    GjsAutoChar output = g_strdup_printf("[GjsModule %s]", path);
+    GjsAutoChar output = g_strdup_printf("[GjsModule %s]", path.get());
 
     args.rval().setString(JS_NewStringCopyZ(cx, output));
-    g_free(path);
     return true;
 }
 
@@ -502,7 +499,6 @@ do_import(JSContext       *context,
 {
     char *filename;
     char *full_path;
-    char *dirname = NULL;
     JS::RootedObject search_path(context);
     guint32 search_path_len;
     guint32 i;
@@ -544,6 +540,8 @@ do_import(JSContext       *context,
     }
 
     for (i = 0; i < search_path_len; ++i) {
+        GjsAutoJSChar dirname(context);
+
         elem.setUndefined();
         if (!JS_GetElement(context, search_path, i, &elem)) {
             /* this means there was an exception, while elem.isUndefined()
@@ -560,9 +558,6 @@ do_import(JSContext       *context,
             goto out;
         }
 
-        g_free(dirname);
-        dirname = NULL;
-
         if (!gjs_string_to_utf8(context, elem, &dirname))
             goto out; /* Error message already set */
 
@@ -616,7 +611,7 @@ do_import(JSContext       *context,
         if (!exists) {
             gjs_debug(GJS_DEBUG_IMPORTER,
                       "JS import '%s' not found in %s",
-                      name, dirname);
+                      name, dirname.get());
 
             g_object_unref(gfile);
             continue;
@@ -665,7 +660,6 @@ do_import(JSContext       *context,
 
     g_free(full_path);
     g_free(filename);
-    g_free(dirname);
 
     if (!result &&
         !JS_IsExceptionPending(context)) {
@@ -715,7 +709,7 @@ importer_enumerate(JSContext        *context,
 
     JS::RootedValue elem(context);
     for (i = 0; i < search_path_len; ++i) {
-        char *dirname = NULL;
+        GjsAutoJSChar dirname(context);
         char *init_path;
 
         elem.setUndefined();
@@ -745,7 +739,6 @@ importer_enumerate(JSContext        *context,
 
         /* new_for_commandline_arg handles resource:/// paths */
         GjsAutoUnref<GFile> dir = g_file_new_for_commandline_arg(dirname);
-        g_free(dirname);
         GjsAutoUnref<GFileEnumerator> direnum =
             g_file_enumerate_children(dir, G_FILE_ATTRIBUTE_STANDARD_TYPE,
                                       G_FILE_QUERY_INFO_NONE, NULL, NULL);
@@ -793,8 +786,8 @@ importer_resolve(JSContext        *context,
                  bool             *resolved)
 {
     Importer *priv;
-    char *name = NULL;
     jsid module_init_name;
+    GjsAutoJSChar name(context);
 
     module_init_name = gjs_context_get_const_string(context, GJS_STRING_MODULE_INIT);
     if (id == module_init_name) {
@@ -810,7 +803,6 @@ importer_resolve(JSContext        *context,
         strcmp(name, "toString") == 0 ||
         strcmp(name, "__iterator__") == 0) {
         *resolved = false;
-        g_free(name);
         return true;
     }
     priv = priv_from_js(context, obj);
@@ -821,18 +813,14 @@ importer_resolve(JSContext        *context,
     if (priv == NULL) {
         /* we are the prototype, or have the wrong class */
         *resolved = false;
-        g_free(name);
         return true;
     }
 
     JSAutoRequest ar(context);
-    if (!do_import(context, obj, priv, name)) {
-        g_free(name);
+    if (!do_import(context, obj, priv, name))
         return false;
-    }
 
     *resolved = true;
-    g_free(name);
     return true;
 }
 
diff --git a/gjs/jsapi-util-args.h b/gjs/jsapi-util-args.h
index c572fab..1fb3ec7 100644
--- a/gjs/jsapi-util-args.h
+++ b/gjs/jsapi-util-args.h
@@ -103,15 +103,20 @@ assign(JSContext      *cx,
         *ref = NULL;
         return;
     }
+
+    GjsAutoJSChar tmp_ref(cx);
+
     if (c == 's') {
-        if (!gjs_string_to_utf8(cx, value, ref))
+        if (!gjs_string_to_utf8(cx, value, &tmp_ref))
             throw g_strdup("Couldn't convert to string");
     } else if (c == 'F') {
-        if (!gjs_string_to_filename(cx, value, ref))
+        if (!gjs_string_to_filename(cx, value, &tmp_ref))
             throw g_strdup("Couldn't convert to filename");
     } else {
         throw g_strdup_printf("Wrong type for %c, got char**", c);
     }
+
+    *ref = tmp_ref.copy();
 }
 GNUC_ALWAYS_INLINE
 static inline void
diff --git a/gjs/jsapi-util-string.cpp b/gjs/jsapi-util-string.cpp
index a0be7b8..e01d595 100644
--- a/gjs/jsapi-util-string.cpp
+++ b/gjs/jsapi-util-string.cpp
@@ -32,10 +32,9 @@
 bool
 gjs_string_to_utf8 (JSContext      *context,
                     const JS::Value value,
-                    char          **utf8_string_p)
+                    GjsAutoJSChar  *utf8_string_p)
 {
     gsize len;
-    char *bytes;
 
     JS_BeginRequest(context);
 
@@ -54,10 +53,7 @@ gjs_string_to_utf8 (JSContext      *context,
         return false;
     }
 
-    if (utf8_string_p) {
-        bytes = JS_EncodeStringToUTF8(context, str);
-        *utf8_string_p = bytes;
-    }
+    utf8_string_p->reset(context, JS_EncodeStringToUTF8(context, str));
 
     JS_EndRequest(context);
 
@@ -106,10 +102,11 @@ gjs_string_from_utf8(JSContext             *context,
 bool
 gjs_string_to_filename(JSContext      *context,
                        const JS::Value filename_val,
-                       char          **filename_string_p)
+                       GjsAutoJSChar  *filename_string_p)
 {
     GError *error;
-    gchar *tmp, *filename_string;
+    GjsAutoJSChar tmp(context);
+    char *filename_string;
 
     /* gjs_string_to_filename verifies that filename_val is a string */
 
@@ -122,12 +119,10 @@ gjs_string_to_filename(JSContext      *context,
     filename_string = g_filename_from_utf8(tmp, -1, NULL, NULL, &error);
     if (!filename_string) {
         gjs_throw_g_error(context, error);
-        g_free(tmp);
         return false;
     }
 
-    *filename_string_p = filename_string;
-    g_free(tmp);
+    filename_string_p->reset(context, filename_string);
     return true;
 }
 
@@ -352,7 +347,7 @@ gjs_string_from_ucs4(JSContext             *cx,
 bool
 gjs_get_string_id (JSContext       *context,
                    jsid             id,
-                   char           **name_p)
+                   GjsAutoJSChar   *name_p)
 {
     JS::RootedValue id_val(context);
 
@@ -362,7 +357,6 @@ gjs_get_string_id (JSContext       *context,
     if (id_val.isString()) {
         return gjs_string_to_utf8(context, id_val, name_p);
     } else {
-        *name_p = NULL;
         return false;
     }
 }
@@ -384,10 +378,9 @@ gjs_unichar_from_string (JSContext *context,
                          JS::Value  value,
                          gunichar  *result)
 {
-    char *utf8_str;
+    GjsAutoJSChar utf8_str(context);
     if (gjs_string_to_utf8(context, value, &utf8_str)) {
         *result = g_utf8_get_char(utf8_str);
-        g_free(utf8_str);
         return true;
     }
     return false;
diff --git a/gjs/jsapi-util.cpp b/gjs/jsapi-util.cpp
index 900b1b4..7c7de73 100644
--- a/gjs/jsapi-util.cpp
+++ b/gjs/jsapi-util.cpp
@@ -211,17 +211,15 @@ throw_property_lookup_error(JSContext       *cx,
     /* remember gjs_throw() is a no-op if JS_GetProperty()
      * already set an exception
      */
-    char *name;
+    GjsAutoJSChar name(cx);
     gjs_get_string_id(cx, property_name, &name);
 
     if (description)
-        gjs_throw(cx, "No property '%s' in %s (or %s)", name, description,
+        gjs_throw(cx, "No property '%s' in %s (or %s)", name.get(), description,
                   reason);
     else
-        gjs_throw(cx, "No property '%s' in object %p (or %s)", name,
+        gjs_throw(cx, "No property '%s' in object %p (or %s)", name.get(),
                   obj.address(), reason);
-
-    g_free(name);
 }
 
 /* Returns whether the object had the property; if the object did
@@ -302,8 +300,10 @@ gjs_object_require_property(JSContext       *cx,
                             char           **value)
 {
     JS::RootedValue prop_value(cx);
+    GjsAutoJSChar value_tmp(cx);
     if (JS_GetPropertyById(cx, obj, property_name, &prop_value) &&
-        gjs_string_to_utf8(cx, prop_value, value)) {
+        gjs_string_to_utf8(cx, prop_value, &value_tmp)) {
+        *value = value_tmp.copy();
         return true;
     }
 
@@ -432,7 +432,7 @@ gjs_string_readable (JSContext   *context,
                      JSString    *string)
 {
     GString *buf = g_string_new("");
-    char *chars;
+    GjsAutoJSChar chars(context);
 
     JS_BeginRequest(context);
 
@@ -454,7 +454,6 @@ gjs_string_readable (JSContext   *context,
         g_free(escaped);
     } else {
         g_string_append(buf, chars);
-        g_free(chars);
     }
 
     g_string_append_c(buf, '"');
@@ -565,11 +564,11 @@ static char *
 utf8_exception_from_non_gerror_value(JSContext      *cx,
                                      JS::HandleValue exc)
 {
-    char *utf8_exception = NULL;
+    GjsAutoJSChar utf8_exception(cx);
     JS::RootedString exc_str(cx, JS::ToString(cx, exc));
     if (exc_str != NULL)
         gjs_string_to_utf8(cx, JS::StringValue(exc_str), &utf8_exception);
-    return utf8_exception;
+    return utf8_exception.copy();
 }
 
 bool
@@ -577,7 +576,8 @@ gjs_log_exception_full(JSContext       *context,
                        JS::HandleValue  exc,
                        JS::HandleString message)
 {
-    char *utf8_exception, *utf8_message;
+    char *utf8_exception;
+    GjsAutoJSChar utf8_message(context);
     bool is_syntax;
 
     JS_BeginRequest(context);
@@ -596,7 +596,7 @@ gjs_log_exception_full(JSContext       *context,
                                              gerror->message);
         } else {
             JS::RootedValue js_name(context);
-            char *utf8_name;
+            GjsAutoJSChar utf8_name(context);
 
             if (gjs_object_get_property(context, exc_obj,
                                         GJS_STRING_NAME, &js_name) &&
@@ -611,8 +611,6 @@ gjs_log_exception_full(JSContext       *context,
 
     if (message != NULL)
         gjs_string_to_utf8(context, JS::StringValue(message), &utf8_message);
-    else
-        utf8_message = NULL;
 
     /* We log syntax errors differently, because the stack for those includes
        only the referencing module, but we want to print out the filename and
@@ -622,7 +620,7 @@ gjs_log_exception_full(JSContext       *context,
     if (is_syntax) {
         JS::RootedValue js_lineNumber(context), js_fileName(context);
         unsigned lineNumber;
-        char *utf8_fileName;
+        GjsAutoJSChar utf8_filename(context);
 
         gjs_object_get_property(context, exc_obj, GJS_STRING_LINE_NUMBER,
                                 &js_lineNumber);
@@ -630,50 +628,47 @@ gjs_log_exception_full(JSContext       *context,
                                 &js_fileName);
 
         if (js_fileName.isString())
-            gjs_string_to_utf8(context, js_fileName, &utf8_fileName);
+            gjs_string_to_utf8(context, js_fileName, &utf8_filename);
         else
-            utf8_fileName = g_strdup("unknown");
+            utf8_filename.reset(context, JS_strdup(context, "unknown"));
 
         lineNumber = js_lineNumber.toInt32();
 
-        if (utf8_message) {
-            g_critical("JS ERROR: %s: %s @ %s:%u", utf8_message, utf8_exception,
-                       utf8_fileName, lineNumber);
+        if (message != NULL) {
+            g_critical("JS ERROR: %s: %s @ %s:%u", utf8_message.get(), utf8_exception,
+                       utf8_filename.get(), lineNumber);
         } else {
             g_critical("JS ERROR: %s @ %s:%u", utf8_exception,
-                       utf8_fileName, lineNumber);
+                       utf8_filename.get(), lineNumber);
         }
 
-        g_free(utf8_fileName);
     } else {
-        char *utf8_stack;
+        GjsAutoJSChar utf8_stack(context);
         JS::RootedValue stack(context);
+        bool have_utf8_stack = false;
 
         if (exc.isObject() &&
             gjs_object_get_property(context, exc_obj, GJS_STRING_STACK,
                                     &stack) &&
-            stack.isString())
+            stack.isString()) {
             gjs_string_to_utf8(context, stack, &utf8_stack);
-        else
-            utf8_stack = NULL;
+            have_utf8_stack = true;
+        }
 
-        if (utf8_message) {
-            if (utf8_stack)
-                g_warning("JS ERROR: %s: %s\n%s", utf8_message, utf8_exception, utf8_stack);
+        if (message != nullptr) {
+            if (have_utf8_stack)
+                g_warning("JS ERROR: %s: %s\n%s", utf8_message.get(), utf8_exception, utf8_stack.get());
             else
-                g_warning("JS ERROR: %s: %s", utf8_message, utf8_exception);
+                g_warning("JS ERROR: %s: %s", utf8_message.get(), utf8_exception);
         } else {
-            if (utf8_stack)
-                g_warning("JS ERROR: %s\n%s", utf8_exception, utf8_stack);
+            if (have_utf8_stack)
+                g_warning("JS ERROR: %s\n%s", utf8_exception, utf8_stack.get());
             else
                 g_warning("JS ERROR: %s", utf8_exception);
         }
-
-        g_free(utf8_stack);
     }
 
     g_free(utf8_exception);
-    g_free(utf8_message);
 
     JS_EndRequest(context);
 
diff --git a/gjs/jsapi-util.h b/gjs/jsapi-util.h
index e05e12b..44bc372 100644
--- a/gjs/jsapi-util.h
+++ b/gjs/jsapi-util.h
@@ -60,6 +60,61 @@ public:
     }
 };
 
+class GjsJSFreeArgs {
+private:
+    JSContext *m_cx;
+
+public:
+    explicit GjsJSFreeArgs(JSContext *cx) : m_cx(cx)
+    {}
+
+    void operator() (char *str) {
+        JS_free(m_cx, str);
+    }
+
+    JSContext* get_context() {
+        return m_cx;
+    }
+
+    void set_context(JSContext *cx) {
+        m_cx = cx;
+    }
+};
+
+class GjsAutoJSChar {
+private:
+    std::unique_ptr<char, GjsJSFreeArgs> m_ptr;
+
+public:
+    GjsAutoJSChar(JSContext *cx, char *str = nullptr)
+    : m_ptr (str, GjsJSFreeArgs(cx)) {
+        g_assert(cx != nullptr);
+    }
+
+    operator const char*() {
+        return m_ptr.get();
+    }
+
+    const char* get() {
+        return m_ptr.get();
+    }
+
+    char* copy() {
+        /* Strings acquired by this should be g_free()'ed */
+        return g_strdup(m_ptr.get());
+    }
+
+    char* js_copy() {
+        /* Strings acquired by this should be JS_free()'ed */
+        return JS_strdup(m_ptr.get_deleter().get_context(), m_ptr.get());
+    }
+
+    void reset(JSContext *cx, char *str) {
+        m_ptr.get_deleter().set_context(cx);
+        m_ptr.reset(str);
+    }
+};
+
 G_BEGIN_DECLS
 
 #define GJS_UTIL_ERROR gjs_util_error_quark ()
@@ -184,7 +239,7 @@ void        gjs_error_reporter               (JSContext       *context,
 
 bool        gjs_string_to_utf8               (JSContext       *context,
                                               const JS::Value  string_val,
-                                              char           **utf8_string_p);
+                                              GjsAutoJSChar   *utf8_string_p);
 bool gjs_string_from_utf8(JSContext             *context,
                           const char            *utf8_string,
                           ssize_t                n_bytes,
@@ -192,7 +247,7 @@ bool gjs_string_from_utf8(JSContext             *context,
 
 bool        gjs_string_to_filename           (JSContext       *context,
                                               const JS::Value  string_val,
-                                              char           **filename_string_p);
+                                              GjsAutoJSChar   *filename_string_p);
 bool gjs_string_from_filename(JSContext             *context,
                               const char            *filename_string,
                               ssize_t                n_bytes,
@@ -214,7 +269,7 @@ bool gjs_string_from_ucs4(JSContext             *cx,
 
 bool        gjs_get_string_id                (JSContext       *context,
                                               jsid             id,
-                                              char           **name_p);
+                                              GjsAutoJSChar   *name_p);
 jsid        gjs_intern_string_to_id          (JSContext       *context,
                                               const char      *string);
 
diff --git a/gjs/runtime.cpp b/gjs/runtime.cpp
index 5a81638..eb9677b 100644
--- a/gjs/runtime.cpp
+++ b/gjs/runtime.cpp
@@ -57,7 +57,7 @@ gjs_locale_to_upper_case (JSContext *context,
                           JS::MutableHandleValue retval)
 {
     bool success = false;
-    char *utf8 = NULL;
+    GjsAutoJSChar utf8(context);
     char *upper_case_utf8 = NULL;
 
     if (!gjs_string_to_utf8(context, JS::StringValue(src), &utf8))
@@ -71,7 +71,6 @@ gjs_locale_to_upper_case (JSContext *context,
     success = true;
 
 out:
-    g_free(utf8);
     g_free(upper_case_utf8);
 
     return success;
@@ -83,7 +82,7 @@ gjs_locale_to_lower_case (JSContext *context,
                           JS::MutableHandleValue retval)
 {
     bool success = false;
-    char *utf8 = NULL;
+    GjsAutoJSChar utf8(context);
     char *lower_case_utf8 = NULL;
 
     if (!gjs_string_to_utf8(context, JS::StringValue(src), &utf8))
@@ -97,7 +96,6 @@ gjs_locale_to_lower_case (JSContext *context,
     success = true;
 
 out:
-    g_free(utf8);
     g_free(lower_case_utf8);
 
     return success;
@@ -109,22 +107,15 @@ gjs_locale_compare (JSContext *context,
                     JS::HandleString src_2,
                     JS::MutableHandleValue retval)
 {
-    bool success = false;
-    char *utf8_1 = NULL, *utf8_2 = NULL;
+    GjsAutoJSChar utf8_1(context), utf8_2(context);
 
     if (!gjs_string_to_utf8(context, JS::StringValue(src_1), &utf8_1) ||
         !gjs_string_to_utf8(context, JS::StringValue(src_2), &utf8_2))
-        goto out;
+        return false;
 
     retval.setInt32(g_utf8_collate(utf8_1, utf8_2));
 
-    success = true;
-
-out:
-    g_free(utf8_1);
-    g_free(utf8_2);
-
-    return success;
+    return true;
 }
 
 static bool
diff --git a/gjs/stack.cpp b/gjs/stack.cpp
index e49a77f..0096393 100644
--- a/gjs/stack.cpp
+++ b/gjs/stack.cpp
@@ -89,7 +89,7 @@ gjs_context_print_stack_stderr(GjsContext *context)
 {
     JSContext *cx = (JSContext*) gjs_context_get_native_context(context);
     JS::RootedValue v_stack(cx);
-    char *stack;
+    GjsAutoJSChar stack(cx);
 
     g_printerr("== Stack trace for context %p ==\n", context);
 
@@ -101,8 +101,7 @@ gjs_context_print_stack_stderr(GjsContext *context)
         return;
     }
 
-    g_printerr("%s\n", stack);
-    g_free(stack);
+    g_printerr("%s\n", stack.get());
 }
 
 void
diff --git a/test/gjs-tests.cpp b/test/gjs-tests.cpp
index f5119e5..fefc2ef 100644
--- a/test/gjs-tests.cpp
+++ b/test/gjs-tests.cpp
@@ -124,14 +124,13 @@ static void
 gjstest_test_func_gjs_jsapi_util_string_js_string_utf8(GjsUnitTestFixture *fx,
                                                        gconstpointer       unused)
 {
-    char *utf8_result;
+    GjsAutoJSChar utf8_result(fx->cx);
     JS::RootedValue js_string(fx->cx);
 
     g_assert_true(gjs_string_from_utf8(fx->cx, VALID_UTF8_STRING, -1, &js_string));
     g_assert(js_string.isString());
     g_assert(gjs_string_to_utf8(fx->cx, js_string, &utf8_result));
     g_assert_cmpstr(VALID_UTF8_STRING, ==, utf8_result);
-    g_free(utf8_result);
 }
 
 static void
@@ -139,7 +138,7 @@ gjstest_test_func_gjs_jsapi_util_error_throw(GjsUnitTestFixture *fx,
                                              gconstpointer       unused)
 {
     JS::RootedValue exc(fx->cx), value(fx->cx);
-    char *s = NULL;
+    GjsAutoJSChar s(fx->cx);
 
     /* Test that we can throw */
 
@@ -158,7 +157,6 @@ gjstest_test_func_gjs_jsapi_util_error_throw(GjsUnitTestFixture *fx,
     gjs_string_to_utf8(fx->cx, value, &s);
     g_assert_nonnull(s);
     g_assert_cmpstr(s, ==, "This is an exception 42");
-    JS_free(fx->cx, s);
 
     /* keep this around before we clear it */
     JS::RootedValue previous(fx->cx, exc);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]