...
 
Commits (23)
  • Florian Müllner's avatar
    GObject: Ensure generated GType names are valid · cacdb796
    Florian Müllner authored
    JS classes accept names that aren't valid GType names. Usually this
    is something the programmer can take into account, however sometimes
    a parent class isn't under their control and can therefore change
    unexpectedly (gnome-shell extensions are a primary example).
    
    Handle this case gracefully by replacing any invalid characters with
    underscores.
    
    !229
    cacdb796
  • Philip Chimento's avatar
    GLib: Fix regression in GVariant packing · 15cf7c29
    Philip Chimento authored
    This was a use of the old ByteArray API that hadn't been updated.
    
    Closes: #202
    15cf7c29
  • Georges Basile Stavracas Neto's avatar
    profiler: Concat entry.label() and entry.dynamicString() · 3112e2aa
    Georges Basile Stavracas Neto authored
    In MozJS 60, only using entry.label() is not enough to give
    meaningful Sysprof entries, since it might be an empty string
    in various cases. This gives many empty entries in Sysprof,
    making the profiler not as useful.
    
    As per upstream MozJS, in version 60, the final string is a
    combination of entry.label() and entry.dynamicString(). In [1],
    however, the dynamic string is conditionally added depending
    on a privacy toggle - which is not really the case with GJS
    since enabling the profiler isn't possible without a large
    control over the software stack, and opting in.
    
    Thus, use entry.dynamicLabel() as an additional source of
    information when saving the profiling labels, with enough
    care to not allocate new strings while there.
    
    [1] https://hg.mozilla.org/releases/mozilla-esr60/rev/768e500ad190
    3112e2aa
  • Philip Chimento's avatar
    release: Prepare for 1.54.1 · f9fc622d
    Philip Chimento authored
    f9fc622d
  • Philip Chimento's avatar
    build: Post-release version bump · 09c4c05f
    Philip Chimento authored
    09c4c05f
  • Philip Chimento's avatar
    build: Remove apostrophe from conftest program · cf56080b
    Philip Chimento authored
    I noticed that this apostrophe causes a warning while compiling the
    conftest program. The text of this error message really doesn't matter,
    so just remove the apostrophe.
    cf56080b
  • Philip Chimento's avatar
    tools: Update clang-format scripts · a3e11762
    Philip Chimento authored
    A few bugs have been fixed upstream.
    a3e11762
  • Philip Chimento's avatar
    build: Remove useless pkgconfig check · 7e9e35ee
    Philip Chimento authored
    This was necessary because some test code used to use GUnixOutputStream,
    but it no longer does.
    7e9e35ee
  • Philip Chimento's avatar
    tests: Don't time-limit the unit tests · 358a9797
    Philip Chimento authored
    Previously there was code that would intentionally crash if more than 7
    minutes elapsed, in case the tests got stuck on autobuilders. This code
    is more harmful than helpful, see the following table:
    
      Times it has killed a stuck test on GitLab: . . . . . . . . 0
      Times it has killed a JS_GC_ZEAL test:  . . . . . . . . .  34
      Times it has killed my GDB session because I forgot
        to add the stupid environment variable: . . . . . . . . 478
    358a9797
  • Daniel van Vugt's avatar
    context: Defer and therefore batch forced GC runs · c281d9c2
    Daniel van Vugt authored
    Since commit e9e96955, forced GC runs get queued very often in some
    cases. For example, during the gnome-shell icon spring animation around
    60% of gnome-shell's CPU time was spent in `trigger_gc_if_needed`.
    That's too much.
    
    We now defer the forced GC runs by 10 seconds, which provides two
    significant performance benefits:
    
      1. Animations triggering garbage collection are unlikely to have their
         performance adversely affected by the run because the animation
         will be finished before it starts.
    
      2. The total number of garbage collection runs is much lower because
         they're more likely to have been batched into the same run.
    
    This has the observed benefit, for example, of reducing the CPU usage
    of the gnome-shell icon spring animation from 78% to 47% on an i7-7700
    (a 40% relative reduction).
    
    Closes: gnome-shell#582
    c281d9c2
  • Marco Trevisan's avatar
    context: use timeout with seconds to schedule a gc trigger · e3f6208a
    Marco Trevisan authored
    We can use lower granularity as we care about seconds anyway
    e3f6208a
  • Georges Basile Stavracas Neto's avatar
    fundamental: Check if gtype is valid before using it · e2425144
    Georges Basile Stavracas Neto authored
    Not a big deal, but found this bug while investigating some
    problems that arose from mutter!260.
    
    We should check if the GType is valid before using it, not
    after.
    e2425144
  • Philip Chimento's avatar
    console: Refactor AutoReportException for non-object exceptions · 0decd7ed
    Philip Chimento authored
    Previously, in the interactive interpreter, throwing a non-object
    exception (for example, `throw 'foo';`) would crash when trying to print
    the exception value in the console.
    
    This changes AutoReportException so that exceptions are handled better
    when they are not objects.
    0decd7ed
  • Philip Chimento's avatar
    release: Prepare for 1.54.2 · 4e817177
    Philip Chimento authored
    4e817177
  • Philip Chimento's avatar
    object: Fix write-only properties · 9f53812a
    Philip Chimento authored
    Since the property refactor, write-only properties have not been working.
    The problem was that a getter and setter function were not defined for
    them, because is_gobject_property_name() did not consider them to be
    properties. Now, the setter function works as normal while the getter
    function just pretends a write-only property has the value of undefined.
    
    The test is marked pending until a test is added to the
    gobject-introspection test suite.
    9f53812a
  • Andrea Azzarone's avatar
    engine: mozjs60 changes for GC sweeping tracking · d4fe34a2
    Andrea Azzarone authored
    With the switch to mozjs60, the sweeping happens in three phases insted of two:
    JSFINALIZE_GROUP_PREPARE, JSFINALIZE_GROUP_START, and JSFINALIZE_GROUP_END.
    Update the code to keep track of whether the runtime is currently doing GC
    sweeping, and prevent calling JS code at that time.
    
    Fixes: #212
    d4fe34a2
  • Philip Chimento's avatar
    arg: Handle case with null array and garbage length · 2190ed7d
    Philip Chimento authored
    It happens sometimes in the case of an array out argument with a
    separate length argument, that C code passess a NULL array pointer with
    garbage as the length.
    
    In the particular case that caused the crash in the associated bug
    report, gtk_selection_data_get_targets() passed NULL as the array
    pointer and -1 as the length, which later got interpreted as an unsigned
    int and so caused a failure to allocate memory.
    
    I doubt that the C code should be doing this, but in any case we should
    not crash in this case. This adds a check for this situation to
    gjs_array_from_carray_internal() as well as to all the shortcuts and
    special cases called from there.
    
    Closes: #201
    2190ed7d
  • Marco Trevisan's avatar
    jsapi-util: add ability to ref added GjsAutoParam · ecd7e786
    Marco Trevisan authored
    Add an overloaded method to initialize a GjsAutoParam with with a reffed pspec.
    This helps to ref param specs we're consuming.
    
    Fixes #213
    ecd7e786
  • Marco Trevisan's avatar
    object: add reference to the property pspec we cache · 0f77ebd6
    Marco Trevisan authored
    When caching the properties param specs we get them from the class object, which
    is currently owning them, however since we're caching them we should add a
    reference to them in order to keep the ownership and clean them up properly
    when the cache is destroyed
    
    Fixes #213
    0f77ebd6
  • Marco Trevisan's avatar
    350be193
  • Philip Chimento's avatar
    object: Fix build with --enable-dtrace · c5698eab
    Philip Chimento authored
    We don't build with this option very often, so this has been broken for a
    while.
    
    Closes: #196
    c5698eab
  • Philip Chimento's avatar
    build: Stable branch version bump · a011deca
    Philip Chimento authored
    a011deca
  • Philip Chimento's avatar
    release: Prepare for 1.54.3 · 7d5e567b
    Philip Chimento authored
    7d5e567b
......@@ -64,13 +64,13 @@ check_PROGRAMS += gjs-tests.gtester minijasmine
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 \
......
Version 1.54.3
--------------
- Closed bugs and merge requests:
* object: Fix write-only properties [!246, Philip Chimento]
* SIGSEGV when exiting gnome-shell [#212, !247, Andrea Azzarone]
* SelectionData.get_targets crashes with "Unable to resize vector" [#201,
!241, Philip Chimento]
* Gnome-shell crashes on destroying cached param specs [#213, !240, Marco
Trevisan]
* GType memleak fixes [!244, Marco Trevisan]
* Fix build with --enable-dtrace and create CI job to ensure it doesn't break
in the future [#196, !253, Philip Chimento]
Version 1.54.2
--------------
- Closed bugs and merge requests:
* context: Defer and therefore batch forced GC runs [performance] [!236,
Daniel van Vugt]
* context: use timeout with seconds to schedule a gc trigger [!239, Marco
Trevisan]
* fundamental: Check if gtype is valid before using it [!242, Georges Basile
Stavracas Neto]
- Backported a fix for a crash in the interactive interpreter when executing
something like `throw "foo"` [Philip Chimento]
- Backported various maintenance from 3.31 [Philip Chimento]
Version 1.54.1
--------------
- Closed bugs and merge requests:
* legacy: Ensure generated GType names are valid [!229, Florian Müllner]
* Fix GJS profiler with MozJS 60 [!230, Georges Basile Stavracas Neto]
* Regression with DBus proxies [#202, !231, Philip Chimento]
Version 1.54.0
--------------
......
......@@ -3,7 +3,7 @@
m4_define(pkg_major_version, 1)
m4_define(pkg_minor_version, 54)
m4_define(pkg_micro_version, 1)
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)
......@@ -69,14 +69,11 @@ 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_MSG_CHECKING([whether SpiderMonkey was configured with --enable-debug])
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
#include <js-config.h>
#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])
......
......@@ -965,6 +965,16 @@ gjs_array_from_flat_gvalue_array(JSContext *context,
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))
......@@ -2283,6 +2293,15 @@ gjs_array_from_carray_internal (JSContext *context,
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");
......
......@@ -623,23 +623,16 @@ static JSObject*
gjs_lookup_fundamental_prototype_from_gtype(JSContext *context,
GType gtype)
{
GIObjectInfo *info;
JSObject *proto;
GjsAutoInfo<GIObjectInfo> 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
......
......@@ -63,13 +63,18 @@ update_gtype_weak_pointers(JSContext *cx,
void *data)
{
for (auto iter = weak_pointer_list.begin(); iter != weak_pointer_list.end(); ) {
auto heap_wrapper = static_cast<JS::Heap<JSObject *> *>(g_type_get_qdata(*iter, gjs_get_gtype_wrapper_quark()));
GType gtype = *iter;
auto heap_wrapper = static_cast<JS::Heap<JSObject *> *>(
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 @@ gjs_gtype_finalize(JSFreeOp *fop,
if (G_UNLIKELY(gtype == 0))
return;
weak_pointer_list.erase(gtype);
auto heap_wrapper = static_cast<JS::Heap<JSObject*>*>(
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
......
......@@ -287,7 +287,8 @@ GParamSpec* ObjectPrototype::find_param_spec_from_id(JSContext* cx,
gname = gjs_hyphen_from_camel(js_prop_name);
GObjectClass *gobj_class = G_OBJECT_CLASS(g_type_class_ref(m_gtype));
GjsAutoParam param_spec = g_object_class_find_property(gobj_class, gname);
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);
......@@ -386,8 +387,10 @@ ObjectInstance::prop_getter_impl(JSContext *cx,
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, "Accessing GObject property %s",
param->name);
......@@ -691,10 +694,7 @@ static bool is_ginterface_property_name(GIInterfaceInfo* info,
prop_info.reset();
}
if (!prop_info)
return false;
return g_property_info_get_flags(prop_info) & G_PARAM_READABLE;
return !!prop_info;
}
bool ObjectPrototype::lazy_define_gobject_property(JSContext* cx,
......@@ -812,14 +812,14 @@ is_gobject_property_name(GIObjectInfo *info,
return true;
}
}
g_free(canonical_name);
return false;
}
g_free(canonical_name);
if (!prop_info)
return false;
return g_property_info_get_flags(prop_info) & G_PARAM_READABLE;
return true;
}
bool ObjectBase::resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
......@@ -1618,7 +1618,7 @@ void ObjectBase::finalize(JSFreeOp* fop, JSObject* obj) {
ObjectInstance::~ObjectInstance() {
debug_lifecycle("Finalize");
TRACE(GJS_OBJECT_PROXY_FINALIZE(priv, m_gobj, ns(), name()));
TRACE(GJS_OBJECT_PROXY_FINALIZE(this, m_gobj, ns(), name()));
invalidate_all_closures();
......
......@@ -291,8 +291,12 @@ from_gbytes_func(JSContext *context,
}
JSObject* gjs_byte_array_from_data(JSContext* cx, size_t nbytes, void* data) {
JS::RootedObject array_buffer(
cx, JS_NewArrayBufferWithContents(cx, nbytes, g_memdup(data, nbytes)));
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;
......
......@@ -636,9 +636,9 @@ _gjs_context_schedule_gc_internal(GjsContext *js_context,
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
......
......@@ -162,7 +162,7 @@ gjs_finalize_callback(JSFreeOp *fop,
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);
......
......@@ -278,6 +278,12 @@ gjs_string_from_ucs4(JSContext *cx,
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;
......
......@@ -86,10 +86,13 @@ struct GCPolicy<GjsAutoInfo<T>> : public IgnoreGCPolicy<GjsAutoInfo<T>> {};
class GjsAutoParam
: public std::unique_ptr<GParamSpec, decltype(&g_param_spec_unref)> {
public:
struct TakeOwnership {};
GjsAutoParam(GParamSpec* ptr = nullptr)
: unique_ptr(ptr, g_param_spec_unref)
{
}
: unique_ptr(ptr, g_param_spec_unref) {}
GjsAutoParam(GParamSpec* ptr, const TakeOwnership&)
: GjsAutoParam(ptr ? g_param_spec_ref(ptr) : nullptr) {}
operator GParamSpec*() { return get(); }
};
......
......@@ -304,15 +304,55 @@ gjs_profiler_sigprof(int signum,
for (uint32_t ix = 0; ix < depth; ix++) {
js::ProfileEntry& entry = self->stack.entries[ix];
const char *label = entry.label();
const char *dynamic_string = entry.dynamicString();
uint32_t flipped = depth - 1 - ix;
size_t label_length = strlen(label);
/*
* 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());
}
......
# 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
{
......
# 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
......
......@@ -234,6 +234,16 @@ describe('Introspected GObject', function () {
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 () {
......
const ByteArray = imports.byteArray;
const GLib = imports.gi.GLib;
describe('GVariant constructor', function () {
......@@ -40,4 +41,11 @@ describe('GVariant constructor', function () {
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');
});
});
......@@ -139,6 +139,8 @@ const Derived = GObject.registerClass(class Derived extends MyObject {
}
});
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 @@ describe('GObject class with decorator', function () {
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();
......
......@@ -164,6 +164,11 @@ const Derived = new Lang.Class({
}
});
const OddlyNamed = new Lang.Class({
Name: 'Legacy.OddlyNamed',
Extends: MyObject
});
const MyCustomInit = new Lang.Class({
Name: 'MyCustomInit',
Extends: GObject.Object,
......@@ -316,6 +321,13 @@ describe('GObject class', function () {
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();
......
......@@ -434,7 +434,7 @@ function defineGObjectLegacyObjects(GObject) {
if (params.GTypeName)
return params.GTypeName;
else
return 'Gjs_' + params.Name;
return 'Gjs_' + params.Name.replace(/[^a-z0-9_+-]/gi, '_');
}
function _getGObjectInterfaces(interfaces) {
......
......@@ -52,6 +52,8 @@
#include <glib.h>
#include <glib/gprintf.h>
#include <string>
#include "console.h"
#include "gjs/context.h"
#include "gjs/context-private.h"
......@@ -142,6 +144,20 @@ gjs_console_warning_reporter(JSContext *cx, JSErrorReport *report)
class AutoReportException {
JSContext *m_cx;
JSErrorReport* error_from_exception_value(JS::HandleValue v_exn) const {
if (!v_exn.isObject())
return nullptr;
JS::RootedObject exn(m_cx, &v_exn.toObject());
return JS_ErrorFromException(m_cx, exn);
}
JSObject* stack_from_exception_value(JS::HandleValue v_exn) const {
if (!v_exn.isObject())
return nullptr;
JS::RootedObject exn(m_cx, &v_exn.toObject());
return ExceptionStackOrNull(exn);
}
public:
explicit AutoReportException(JSContext *cx) : m_cx(cx) {}
......@@ -153,22 +169,17 @@ public:
JS::RootedValue v_exn(m_cx);
(void) JS_GetPendingException(m_cx, &v_exn);
JS::RootedObject exn(m_cx, &v_exn.toObject());
JSErrorReport *report = JS_ErrorFromException(m_cx, exn);
JSErrorReport* report = error_from_exception_value(v_exn);
if (report) {
g_assert(!JSREPORT_IS_WARNING(report->flags));
gjs_console_print_error(report);
} else {
JS::RootedString message(m_cx, JS::ToString(m_cx, v_exn));
if (!message) {
g_printerr("(could not convert thrown exception to string)\n");
} else {
GjsAutoJSChar message_utf8 = JS_EncodeStringToUTF8(m_cx, message);
g_printerr("%s\n", message_utf8.get());
}
GjsAutoChar string = gjs_value_debug_string(m_cx, v_exn);
g_printerr("error: %s\n", string.get());
return;
}
JS::RootedObject stack(m_cx, ExceptionStackOrNull(exn));
JS::RootedObject stack(m_cx, stack_from_exception_value(v_exn));
if (stack) {
GjsAutoChar stack_str = gjs_format_stack_trace(m_cx, stack);
if (!stack_str)
......
......@@ -68,7 +68,7 @@ function _read_single_type(signature, forceSimple) {
function _makeBytes(byteArray) {
if (byteArray instanceof Uint8Array || byteArray instanceof ByteArray.ByteArray)
return byteArray.toGBytes();
return ByteArray.toGBytes(byteArray);
else
return new GLib.Bytes(byteArray);
}
......
......@@ -110,7 +110,7 @@ function _createSignals(gtype, signals) {
function _createGTypeName(klass) {
if (klass.hasOwnProperty(GTypeName))
return klass[GTypeName];
return `Gjs_${klass.name}`;
return `Gjs_${klass.name.replace(/[^a-z0-9+_-]/gi, '_')}`;
}
function _propertiesAsArray(klass) {
......
......@@ -35,7 +35,6 @@
#include <glib.h>
#include <gio/gio.h>
#include <gio/gunixoutputstream.h>
#include <gjs/gjs.h>
#include "gjs/coverage.h"
......
......@@ -88,91 +88,3 @@ gjs_unit_test_exception_message(GjsUnitTestFixture *fx)
JS_ClearPendingException(fx->cx);
return retval;
}
/* Fork a process that waits the given time then
* sends us ABRT
*/
void
gjs_crash_after_timeout(int seconds)
{
pid_t parent_pid;
int pipe_fds[2];
fd_set read_fds;
struct timeval term_time;
struct timeval remaining;
struct timeval now;
int old_flags;
/* We use a pipe to know in the child when the parent exited */
if (pipe(pipe_fds) != 0) {
fprintf(stderr, "Failed to create pipe to crash-in-timeout process: %s\n",
strerror(errno));
return;
}
/* We want pipe_fds[1] to only be open in the parent process; when it closes
* the child will see an EOF. Setting FD_CLOEXEC is protection in case the
* parent spawns off some process without properly closing fds.
*/
old_flags = fcntl(pipe_fds[1], F_GETFD);
if (old_flags == -1 ||
fcntl(pipe_fds[1], F_SETFD, old_flags | FD_CLOEXEC) != 0) {
fprintf(stderr, "Couldn't make crash-timeout pipe FD_CLOEXEC: %s\n",
strerror(errno));
return;
}
parent_pid = getpid();
switch (fork()) {
case -1:
fprintf(stderr, "Failed to fork crash-in-timeout process: %s\n",
strerror(errno));
return;
case 0:
/* child */
break;
default:
/* parent */
close(pipe_fds[0]);
return;
}
close (pipe_fds[1]);
gettimeofday (&now, NULL);
term_time = now;
term_time.tv_sec += seconds;
FD_ZERO(&read_fds);
FD_SET(pipe_fds[0], &read_fds);
while (true) {
remaining.tv_sec = term_time.tv_sec - now.tv_sec;
remaining.tv_usec = term_time.tv_usec - now.tv_usec;
if (remaining.tv_usec < 0) {
remaining.tv_usec += 1000;
remaining.tv_sec -= 1;
}
if (remaining.tv_sec < 0) /* expired */
break;
select(pipe_fds[0] + 1, &read_fds, NULL, NULL, &remaining);
if (FD_ISSET(pipe_fds[0], &read_fds)) {
/* The parent exited */
_exit(0);
}
gettimeofday(&now, NULL);
}
if (kill(parent_pid, 0) == 0) {
fprintf(stderr, "Timeout of %d seconds expired; aborting process %d\n",
seconds, (int) parent_pid);
kill(parent_pid, SIGABRT);
}
_exit(1);
}
......@@ -46,8 +46,6 @@ void gjs_unit_test_fixture_teardown(GjsUnitTestFixture *fx,
char *gjs_unit_test_exception_message(GjsUnitTestFixture *fx);
void gjs_crash_after_timeout(int seconds);
void gjs_test_add_tests_for_coverage ();
void gjs_test_add_tests_for_parse_call_args(void);
......
......@@ -406,11 +406,6 @@ int
main(int argc,
char **argv)
{
/* give the unit tests 7 minutes to complete, unless an environment variable
* is set; use this when running under GDB, for example */
if (!g_getenv("GJS_TEST_SKIP_TIMEOUT"))
gjs_crash_after_timeout(60 * 7);
/* Avoid interference in the tests from stray environment variable */
g_unsetenv("GJS_ENABLE_PROFILER");
......
......@@ -162,7 +162,7 @@ if [ ${#positionals[@]} -gt 0 ]; then
fi
[ -n "$style" ] || \
error_exit "If you use --style you need to speficy a valid style."
error_exit "If you use --style you need to specify a valid style."
#######################################
# Detection of clang-format & friends #
......@@ -181,7 +181,7 @@ if [ -z "$format" ]; then
$'On Ubuntu/Debian this is available in the clang-format package or, in\n' \
$'older distro versions, clang-format-VERSION.\n' \
$'On Fedora it\'s available in the clang package.\n' \
$'You can also speficy your own path for clang-format by setting the\n' \
$'You can also specify your own path for clang-format by setting the\n' \
$'$CLANG_FORMAT environment variable.'
fi
......@@ -249,7 +249,7 @@ if [ "$whole_file" = false ]; then
$'at https://github.com/barisione/clang-format-hooks/issues with details about\n' \
$'your operating system and setup.\n' \
$'\n' \
$'You can also speficy your own path for clang-format-diff by setting the\n' \
$'You can also specify your own path for clang-format-diff by setting the\n' \
$'$CLANG_FORMAT_DIFF environment variable, for instance:\n' \
$'\n' \
$' CLANG_FORMAT_DIFF="python /.../clang-format-diff.py" \\\n' \
......
......@@ -80,6 +80,7 @@ function rel_realpath() {
# Find the top-level git directory (taking into account we could be in a submodule).
declare git_test_dir=.
declare top_dir
while true; do
top_dir=$(cd "$git_test_dir" && git rev-parse --show-toplevel) || \
error_exit "You need to be in the git repository to run this script."
......@@ -90,8 +91,30 @@ while true; do
if [ -d "$top_dir/.git" ]; then
# We are done! top_dir is the root git directory.
break
else
# We are in a submodule if .git is a file!
elif [ -f "$top_dir/.git" ]; then
# We are in a submodule or git work-tree if .git is a file!
if [ -z "$(git rev-parse --show-superproject-working-tree)" ]; then
# The --show-superproject-working-tree option is available and we
# are in a work tree.
gitdir=$(<"$top_dir/.git")
gitdir=${gitdir#gitdir: }
topdir_basename=${gitdir##*/}
git_test_dir=${gitdir%/worktrees/$topdir_basename}
break
fi
# If show-superproject-working-tree returns non-empty string, either:
#
# 1) --show-superproject-working-tree is not defined for this version of git
#
# 2) --show-superproject-working-tree is defined and we are in a submodule
#
# In the first case we will assume it is not a work tree because people
# using that advanced technology will be using a recent version of git.
#
# In second case, we could use the value returned by
# --show-superproject-working-tree directly but we do not here because
# that would require extra work.
#
git_test_dir="$git_test_dir/.."
fi
done
......